@stripe/link-cli 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +17 -3
  2. package/dist/cli.js +1503 -870
  3. package/package.json +6 -4
package/dist/cli.js CHANGED
@@ -3218,8 +3218,8 @@ var require_utils = __commonJS({
3218
3218
  }
3219
3219
  return ind;
3220
3220
  }
3221
- function removeDotSegments(path6) {
3222
- let input = path6;
3221
+ function removeDotSegments(path7) {
3222
+ let input = path7;
3223
3223
  const output = [];
3224
3224
  let nextSlash = -1;
3225
3225
  let len = 0;
@@ -3418,8 +3418,8 @@ var require_schemes = __commonJS({
3418
3418
  wsComponent.secure = void 0;
3419
3419
  }
3420
3420
  if (wsComponent.resourceName) {
3421
- const [path6, query] = wsComponent.resourceName.split("?");
3422
- wsComponent.path = path6 && path6 !== "/" ? path6 : void 0;
3421
+ const [path7, query] = wsComponent.resourceName.split("?");
3422
+ wsComponent.path = path7 && path7 !== "/" ? path7 : void 0;
3423
3423
  wsComponent.query = query;
3424
3424
  wsComponent.resourceName = void 0;
3425
3425
  }
@@ -9548,11 +9548,9 @@ var require_semver2 = __commonJS({
9548
9548
  }
9549
9549
  });
9550
9550
 
9551
- // src/cli.tsx
9552
- import { Cli as Cli7 } from "incur";
9553
-
9554
9551
  // ../sdk/dist/index.js
9555
9552
  import fs3 from "fs";
9553
+ import path5 from "path";
9556
9554
 
9557
9555
  // ../../node_modules/.pnpm/conf@13.1.0/node_modules/conf/dist/source/index.js
9558
9556
  import { isDeepStrictEqual } from "util";
@@ -9573,12 +9571,12 @@ var disallowedKeys = /* @__PURE__ */ new Set([
9573
9571
  "constructor"
9574
9572
  ]);
9575
9573
  var digits = new Set("0123456789");
9576
- function getPathSegments(path6) {
9574
+ function getPathSegments(path7) {
9577
9575
  const parts = [];
9578
9576
  let currentSegment = "";
9579
9577
  let currentPart = "start";
9580
9578
  let isIgnoring = false;
9581
- for (const character of path6) {
9579
+ for (const character of path7) {
9582
9580
  switch (character) {
9583
9581
  case "\\": {
9584
9582
  if (currentPart === "index") {
@@ -9700,11 +9698,11 @@ function assertNotStringIndex(object, key) {
9700
9698
  throw new Error("Cannot use string index");
9701
9699
  }
9702
9700
  }
9703
- function getProperty(object, path6, value) {
9704
- if (!isObject(object) || typeof path6 !== "string") {
9701
+ function getProperty(object, path7, value) {
9702
+ if (!isObject(object) || typeof path7 !== "string") {
9705
9703
  return value === void 0 ? object : value;
9706
9704
  }
9707
- const pathArray = getPathSegments(path6);
9705
+ const pathArray = getPathSegments(path7);
9708
9706
  if (pathArray.length === 0) {
9709
9707
  return value;
9710
9708
  }
@@ -9724,12 +9722,12 @@ function getProperty(object, path6, value) {
9724
9722
  }
9725
9723
  return object === void 0 ? value : object;
9726
9724
  }
9727
- function setProperty(object, path6, value) {
9728
- if (!isObject(object) || typeof path6 !== "string") {
9725
+ function setProperty(object, path7, value) {
9726
+ if (!isObject(object) || typeof path7 !== "string") {
9729
9727
  return object;
9730
9728
  }
9731
9729
  const root = object;
9732
- const pathArray = getPathSegments(path6);
9730
+ const pathArray = getPathSegments(path7);
9733
9731
  for (let index = 0; index < pathArray.length; index++) {
9734
9732
  const key = pathArray[index];
9735
9733
  assertNotStringIndex(object, key);
@@ -9742,11 +9740,11 @@ function setProperty(object, path6, value) {
9742
9740
  }
9743
9741
  return root;
9744
9742
  }
9745
- function deleteProperty(object, path6) {
9746
- if (!isObject(object) || typeof path6 !== "string") {
9743
+ function deleteProperty(object, path7) {
9744
+ if (!isObject(object) || typeof path7 !== "string") {
9747
9745
  return false;
9748
9746
  }
9749
- const pathArray = getPathSegments(path6);
9747
+ const pathArray = getPathSegments(path7);
9750
9748
  for (let index = 0; index < pathArray.length; index++) {
9751
9749
  const key = pathArray[index];
9752
9750
  assertNotStringIndex(object, key);
@@ -9760,11 +9758,11 @@ function deleteProperty(object, path6) {
9760
9758
  }
9761
9759
  }
9762
9760
  }
9763
- function hasProperty(object, path6) {
9764
- if (!isObject(object) || typeof path6 !== "string") {
9761
+ function hasProperty(object, path7) {
9762
+ if (!isObject(object) || typeof path7 !== "string") {
9765
9763
  return false;
9766
9764
  }
9767
- const pathArray = getPathSegments(path6);
9765
+ const pathArray = getPathSegments(path7);
9768
9766
  if (pathArray.length === 0) {
9769
9767
  return false;
9770
9768
  }
@@ -10869,10 +10867,18 @@ var Storage = class {
10869
10867
  }
10870
10868
  getConfig() {
10871
10869
  if (!this.config) {
10870
+ let locationOverride;
10871
+ if (this.options.configPath) {
10872
+ const parsed = path5.parse(path5.resolve(this.options.configPath));
10873
+ const configName = parsed.ext === ".json" ? parsed.name : parsed.base;
10874
+ locationOverride = { cwd: parsed.dir, configName };
10875
+ } else if (this.options.cwd) {
10876
+ locationOverride = { cwd: this.options.cwd };
10877
+ }
10872
10878
  this.config = new Conf({
10873
10879
  projectName: "link-cli",
10874
10880
  configFileMode: CONFIG_FILE_MODE,
10875
- ...this.options.cwd ? { cwd: this.options.cwd } : {},
10881
+ ...locationOverride,
10876
10882
  defaults: {
10877
10883
  auth: null,
10878
10884
  pendingDeviceAuth: null
@@ -11082,6 +11088,91 @@ var PaymentMethodsResource = class {
11082
11088
  return body?.payment_details ?? [];
11083
11089
  }
11084
11090
  };
11091
+ var ShippingAddressResource = class {
11092
+ verbose;
11093
+ getAccessToken;
11094
+ fetchImpl;
11095
+ shippingAddressesEndpoint;
11096
+ logger;
11097
+ constructor(options) {
11098
+ const config = resolveLinkSdkConfig(options);
11099
+ this.verbose = config.verbose;
11100
+ this.getAccessToken = config.getAccessToken;
11101
+ this.fetchImpl = requireFetchImplementation(config);
11102
+ this.shippingAddressesEndpoint = `${config.apiBaseUrl}/shipping_addresses`;
11103
+ this.logger = config.logger;
11104
+ }
11105
+ async rawFetch(opts) {
11106
+ if (this.verbose) {
11107
+ const redactedHeaders = { ...opts.headers };
11108
+ if (redactedHeaders.Authorization)
11109
+ redactedHeaders.Authorization = "Bearer <redacted>";
11110
+ this.logger.debug(`> ${opts.method} ${opts.url}`);
11111
+ this.logger.debug(` Headers: ${JSON.stringify(redactedHeaders)}`);
11112
+ }
11113
+ let response;
11114
+ try {
11115
+ response = await this.fetchImpl(opts.url, {
11116
+ method: opts.method,
11117
+ headers: opts.headers
11118
+ });
11119
+ } catch (error) {
11120
+ throw new LinkTransportError(
11121
+ `Request failed: ${opts.method} ${opts.url}`,
11122
+ {
11123
+ cause: error
11124
+ }
11125
+ );
11126
+ }
11127
+ const rawBody = await response.text();
11128
+ let data = null;
11129
+ try {
11130
+ data = JSON.parse(rawBody);
11131
+ } catch {
11132
+ }
11133
+ if (this.verbose) {
11134
+ this.logger.debug(`< ${response.status} ${response.statusText}`);
11135
+ response.headers.forEach((value, key) => {
11136
+ this.logger.debug(` ${key}: ${value}`);
11137
+ });
11138
+ this.logger.debug(rawBody);
11139
+ }
11140
+ return { status: response.status, data, rawBody };
11141
+ }
11142
+ async apiFetch(opts) {
11143
+ const token = await this.getAccessToken();
11144
+ const authedOpts = {
11145
+ ...opts,
11146
+ headers: { ...opts.headers, Authorization: `Bearer ${token}` }
11147
+ };
11148
+ const res = await this.rawFetch(authedOpts);
11149
+ if (res.status === 401) {
11150
+ const refreshedToken = await this.getAccessToken({ forceRefresh: true });
11151
+ authedOpts.headers.Authorization = `Bearer ${refreshedToken}`;
11152
+ return this.rawFetch(authedOpts);
11153
+ }
11154
+ return res;
11155
+ }
11156
+ list() {
11157
+ return this.listShippingAddresses();
11158
+ }
11159
+ async listShippingAddresses() {
11160
+ const { status, data, rawBody } = await this.apiFetch({
11161
+ method: "GET",
11162
+ url: this.shippingAddressesEndpoint
11163
+ });
11164
+ if (status < 200 || status >= 300) {
11165
+ const body2 = data;
11166
+ const msg = body2?.error ?? body2?.message ?? (rawBody || "unknown error");
11167
+ throw new LinkApiError(
11168
+ `Failed to list shipping addresses (${status}): ${msg}`,
11169
+ { status, rawBody, details: data }
11170
+ );
11171
+ }
11172
+ const body = data;
11173
+ return body?.shipping_addresses ?? [];
11174
+ }
11175
+ };
11085
11176
  function normalizeSpendRequest(data) {
11086
11177
  const sr = data;
11087
11178
  if (typeof sr.shared_payment_token === "string") {
@@ -11214,6 +11305,22 @@ var SpendRequestResource = class {
11214
11305
  }
11215
11306
  return normalizeSpendRequest(data);
11216
11307
  }
11308
+ cancel(id) {
11309
+ return this.cancelSpendRequest(id);
11310
+ }
11311
+ async cancelSpendRequest(id) {
11312
+ const { status, data, rawBody } = await this.apiFetch({
11313
+ method: "POST",
11314
+ url: `${this.spendRequestsEndpoint}/${id}/cancel`
11315
+ });
11316
+ if (status < 200 || status >= 300) {
11317
+ throw new LinkApiError(
11318
+ `Failed to cancel spend request (${status}): ${extractApiError(data, rawBody)}`,
11319
+ { status, rawBody, details: data }
11320
+ );
11321
+ }
11322
+ return normalizeSpendRequest(data);
11323
+ }
11217
11324
  async requestApproval(id) {
11218
11325
  const { status, data, rawBody } = await this.apiFetch({
11219
11326
  method: "POST",
@@ -11251,16 +11358,155 @@ var SpendRequestResource = class {
11251
11358
  return normalizeSpendRequest(data);
11252
11359
  }
11253
11360
  };
11361
+ var UserInfoResource = class {
11362
+ verbose;
11363
+ getAccessToken;
11364
+ fetchImpl;
11365
+ userInfoEndpoint;
11366
+ logger;
11367
+ constructor(options) {
11368
+ const config = resolveLinkSdkConfig(options);
11369
+ this.verbose = config.verbose;
11370
+ this.getAccessToken = config.getAccessToken;
11371
+ this.fetchImpl = requireFetchImplementation(config);
11372
+ this.userInfoEndpoint = `${config.apiBaseUrl}/userinfo`;
11373
+ this.logger = config.logger;
11374
+ }
11375
+ async rawFetch(opts) {
11376
+ if (this.verbose) {
11377
+ const redactedHeaders = { ...opts.headers };
11378
+ if (redactedHeaders.Authorization)
11379
+ redactedHeaders.Authorization = "Bearer <redacted>";
11380
+ this.logger.debug(`> ${opts.method} ${opts.url}`);
11381
+ this.logger.debug(` Headers: ${JSON.stringify(redactedHeaders)}`);
11382
+ }
11383
+ let response;
11384
+ try {
11385
+ response = await this.fetchImpl(opts.url, {
11386
+ method: opts.method,
11387
+ headers: opts.headers
11388
+ });
11389
+ } catch (error) {
11390
+ throw new LinkTransportError(
11391
+ `Request failed: ${opts.method} ${opts.url}`,
11392
+ {
11393
+ cause: error
11394
+ }
11395
+ );
11396
+ }
11397
+ const rawBody = await response.text();
11398
+ let data = null;
11399
+ try {
11400
+ data = JSON.parse(rawBody);
11401
+ } catch {
11402
+ }
11403
+ if (this.verbose) {
11404
+ this.logger.debug(`< ${response.status} ${response.statusText}`);
11405
+ response.headers.forEach((value, key) => {
11406
+ this.logger.debug(` ${key}: ${value}`);
11407
+ });
11408
+ this.logger.debug(rawBody);
11409
+ }
11410
+ return { status: response.status, data, rawBody };
11411
+ }
11412
+ async apiFetch(opts) {
11413
+ const token = await this.getAccessToken();
11414
+ const authedOpts = {
11415
+ ...opts,
11416
+ headers: { ...opts.headers, Authorization: `Bearer ${token}` }
11417
+ };
11418
+ const res = await this.rawFetch(authedOpts);
11419
+ if (res.status === 401) {
11420
+ const refreshedToken = await this.getAccessToken({ forceRefresh: true });
11421
+ authedOpts.headers.Authorization = `Bearer ${refreshedToken}`;
11422
+ return this.rawFetch(authedOpts);
11423
+ }
11424
+ return res;
11425
+ }
11426
+ async retrieve() {
11427
+ const { status, data, rawBody } = await this.apiFetch({
11428
+ method: "GET",
11429
+ url: this.userInfoEndpoint
11430
+ });
11431
+ if (status < 200 || status >= 300) {
11432
+ const body2 = data;
11433
+ const msg = body2?.error ?? body2?.message ?? (rawBody || "unknown error");
11434
+ throw new LinkApiError(
11435
+ `Failed to retrieve user info (${status}): ${msg}`,
11436
+ {
11437
+ status,
11438
+ rawBody,
11439
+ details: data
11440
+ }
11441
+ );
11442
+ }
11443
+ const body = data;
11444
+ return {
11445
+ email: body?.email ?? null,
11446
+ name: body?.name ?? null,
11447
+ first_name: body?.first_name ?? null,
11448
+ last_name: body?.last_name ?? null,
11449
+ phone: body?.phone ?? null
11450
+ };
11451
+ }
11452
+ };
11453
+
11454
+ // src/cli.tsx
11455
+ import { Cli as Cli9 } from "incur";
11254
11456
 
11255
11457
  // src/commands/auth/index.tsx
11256
11458
  import { Cli } from "incur";
11459
+
11460
+ // src/utils/poll-until.ts
11461
+ async function* pollUntil(options) {
11462
+ const { fn, isTerminal, interval, timeout, maxAttempts } = options;
11463
+ const deadline = timeout !== void 0 && timeout >= 0 ? Date.now() + timeout * 1e3 : void 0;
11464
+ let attempts = 0;
11465
+ let previousSnapshot;
11466
+ while (true) {
11467
+ const value = await fn();
11468
+ if (isTerminal(value) || interval <= 0) {
11469
+ yield { value, terminal: true };
11470
+ return;
11471
+ }
11472
+ attempts++;
11473
+ const maxAttemptsExhausted = maxAttempts > 0 && attempts >= maxAttempts;
11474
+ const timeoutReached = deadline !== void 0 && Date.now() >= deadline;
11475
+ if (maxAttemptsExhausted) {
11476
+ yield { value, terminal: true, reason: "max_attempts" };
11477
+ return;
11478
+ }
11479
+ if (timeoutReached) {
11480
+ yield { value, terminal: true, reason: "timeout" };
11481
+ return;
11482
+ }
11483
+ const snapshot = JSON.stringify(value);
11484
+ if (snapshot !== previousSnapshot) {
11485
+ previousSnapshot = snapshot;
11486
+ yield { value, terminal: false };
11487
+ }
11488
+ await new Promise((resolve) => setTimeout(resolve, interval * 1e3));
11489
+ }
11490
+ }
11491
+
11492
+ // src/utils/render-interactive.tsx
11257
11493
  import { render } from "ink";
11494
+ async function renderInteractive(element, getResult) {
11495
+ const { waitUntilExit } = render(element);
11496
+ await waitUntilExit();
11497
+ if (getResult) {
11498
+ return getResult();
11499
+ }
11500
+ }
11258
11501
 
11259
11502
  // src/commands/auth/login.tsx
11260
11503
  import { Box, Text, useInput } from "ink";
11261
11504
  import Spinner from "ink-spinner";
11262
11505
  import { useEffect, useState } from "react";
11263
11506
 
11507
+ // src/utils/constants.ts
11508
+ var DISPLAY_DELAY_MS = 1500;
11509
+
11264
11510
  // src/utils/open-url.ts
11265
11511
  import { spawn } from "child_process";
11266
11512
  function openUrl(url) {
@@ -11292,8 +11538,10 @@ import { jsx, jsxs } from "react/jsx-runtime";
11292
11538
  var Login = ({
11293
11539
  authResource,
11294
11540
  clientName,
11541
+ authStorage: authStorage2 = storage,
11295
11542
  onComplete
11296
11543
  }) => {
11544
+ const storage2 = authStorage2;
11297
11545
  const [status, setStatus] = useState("initiating");
11298
11546
  const [userCode, setUserCode] = useState("");
11299
11547
  const [verificationUrl, setVerificationUrl] = useState("");
@@ -11330,9 +11578,9 @@ var Login = ({
11330
11578
  const tokens = await authResource.pollDeviceAuth(deviceCode);
11331
11579
  if (tokens) {
11332
11580
  clearInterval(pollInterval);
11333
- storage.setAuth(tokens);
11581
+ storage2.setAuth(tokens);
11334
11582
  setStatus("success");
11335
- setTimeout(onComplete, 1e3);
11583
+ setTimeout(onComplete, DISPLAY_DELAY_MS);
11336
11584
  }
11337
11585
  } catch (err) {
11338
11586
  clearInterval(pollInterval);
@@ -11344,7 +11592,7 @@ var Login = ({
11344
11592
  };
11345
11593
  const timeout = setTimeout(startPolling, 1e3);
11346
11594
  return () => clearTimeout(timeout);
11347
- }, [status, deviceCode, authResource, onComplete]);
11595
+ }, [status, deviceCode, authResource, onComplete, storage2]);
11348
11596
  if (status === "initiating") {
11349
11597
  return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
11350
11598
  /* @__PURE__ */ jsx(Spinner, { type: "dots" }),
@@ -11397,28 +11645,75 @@ var Login = ({
11397
11645
 
11398
11646
  // src/commands/auth/logout.tsx
11399
11647
  import { Box as Box2, Text as Text2 } from "ink";
11400
- import { useEffect as useEffect2, useState as useState2 } from "react";
11401
- import { jsx as jsx2 } from "react/jsx-runtime";
11402
- var Logout = ({ authResource, onComplete }) => {
11403
- const [done, setDone] = useState2(false);
11648
+ import Spinner2 from "ink-spinner";
11649
+ import { useCallback } from "react";
11650
+
11651
+ // src/hooks/use-async-action.ts
11652
+ import { useEffect as useEffect2, useRef, useState as useState2 } from "react";
11653
+ function useAsyncAction(action, onComplete) {
11654
+ const [status, setStatus] = useState2("loading");
11655
+ const [data, setData] = useState2(null);
11656
+ const [error, setError] = useState2("");
11657
+ const onCompleteRef = useRef(onComplete);
11658
+ onCompleteRef.current = onComplete;
11404
11659
  useEffect2(() => {
11660
+ let timeoutId = null;
11661
+ let cancelled = false;
11405
11662
  const run = async () => {
11406
- const auth = storage.getAuth();
11407
- if (auth?.refresh_token) {
11408
- try {
11409
- await authResource.revokeToken(auth.refresh_token);
11410
- } catch {
11411
- }
11663
+ try {
11664
+ const result = await action();
11665
+ if (cancelled) return;
11666
+ setData(result);
11667
+ setStatus("success");
11668
+ timeoutId = setTimeout(
11669
+ () => onCompleteRef.current(result),
11670
+ DISPLAY_DELAY_MS
11671
+ );
11672
+ } catch (err) {
11673
+ if (cancelled) return;
11674
+ const message = err instanceof Error ? err.message : String(err);
11675
+ setError(message);
11676
+ setStatus("error");
11677
+ timeoutId = setTimeout(
11678
+ () => onCompleteRef.current(null),
11679
+ DISPLAY_DELAY_MS
11680
+ );
11412
11681
  }
11413
- storage.clearAuth();
11414
- storage.deleteConfig();
11415
- setDone(true);
11416
- setTimeout(onComplete, 1e3);
11417
11682
  };
11418
11683
  run();
11419
- }, [authResource, onComplete]);
11420
- if (!done) {
11421
- return null;
11684
+ return () => {
11685
+ cancelled = true;
11686
+ if (timeoutId) clearTimeout(timeoutId);
11687
+ };
11688
+ }, [action]);
11689
+ return { status, data, error };
11690
+ }
11691
+
11692
+ // src/commands/auth/logout.tsx
11693
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
11694
+ var Logout = ({
11695
+ authResource,
11696
+ authStorage: authStorage2 = storage,
11697
+ onComplete
11698
+ }) => {
11699
+ const storage2 = authStorage2;
11700
+ const action = useCallback(async () => {
11701
+ const auth = storage2.getAuth();
11702
+ if (auth?.refresh_token) {
11703
+ try {
11704
+ await authResource.revokeToken(auth.refresh_token);
11705
+ } catch {
11706
+ }
11707
+ }
11708
+ storage2.clearAuth();
11709
+ storage2.deleteConfig();
11710
+ }, [authResource, storage2]);
11711
+ const { status } = useAsyncAction(action, onComplete);
11712
+ if (status === "loading") {
11713
+ return /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: "cyan", children: [
11714
+ /* @__PURE__ */ jsx2(Spinner2, { type: "dots" }),
11715
+ " Logging out..."
11716
+ ] }) });
11422
11717
  }
11423
11718
  return /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsx2(Text2, { color: "green", children: "\u2713 Logged out successfully" }) });
11424
11719
  };
@@ -11441,16 +11736,20 @@ var statusOptions = z.object({
11441
11736
  // src/commands/auth/status.tsx
11442
11737
  import { Box as Box3, Text as Text3 } from "ink";
11443
11738
  import { useEffect as useEffect3, useState as useState3 } from "react";
11444
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
11445
- var AuthStatus = ({ onComplete }) => {
11739
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
11740
+ var AuthStatus = ({
11741
+ authStorage: authStorage2 = storage,
11742
+ onComplete
11743
+ }) => {
11744
+ const storage2 = authStorage2;
11446
11745
  const [checked, setChecked] = useState3(false);
11447
11746
  const [authenticated, setAuthenticated] = useState3(false);
11448
11747
  const [tokenPreview, setTokenPreview] = useState3("");
11449
11748
  const [tokenType, setTokenType] = useState3("");
11450
11749
  const [credentialsPath, setCredentialsPath] = useState3("");
11451
11750
  useEffect3(() => {
11452
- const auth = storage.getAuth();
11453
- const credentialsPath2 = storage.getPath();
11751
+ const auth = storage2.getAuth();
11752
+ const credentialsPath2 = storage2.getPath();
11454
11753
  if (auth) {
11455
11754
  setAuthenticated(true);
11456
11755
  setTokenPreview(`${auth.access_token.substring(0, 20)}...`);
@@ -11458,34 +11757,34 @@ var AuthStatus = ({ onComplete }) => {
11458
11757
  }
11459
11758
  setCredentialsPath(credentialsPath2);
11460
11759
  setChecked(true);
11461
- setTimeout(onComplete, 1e3);
11462
- }, [onComplete]);
11760
+ setTimeout(onComplete, DISPLAY_DELAY_MS);
11761
+ }, [onComplete, storage2]);
11463
11762
  if (!checked) {
11464
11763
  return null;
11465
11764
  }
11466
11765
  if (authenticated) {
11467
- return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", children: [
11766
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
11468
11767
  /* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u2713 Authenticated" }),
11469
- /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
11470
- /* @__PURE__ */ jsxs2(Text3, { children: [
11768
+ /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
11769
+ /* @__PURE__ */ jsxs3(Text3, { children: [
11471
11770
  "Access token: ",
11472
11771
  /* @__PURE__ */ jsx3(Text3, { bold: true, children: tokenPreview })
11473
11772
  ] }),
11474
- /* @__PURE__ */ jsxs2(Text3, { children: [
11773
+ /* @__PURE__ */ jsxs3(Text3, { children: [
11475
11774
  "Token type: ",
11476
11775
  /* @__PURE__ */ jsx3(Text3, { bold: true, children: tokenType })
11477
11776
  ] }),
11478
- /* @__PURE__ */ jsxs2(Text3, { children: [
11777
+ /* @__PURE__ */ jsxs3(Text3, { children: [
11479
11778
  "Credentials: ",
11480
11779
  /* @__PURE__ */ jsx3(Text3, { bold: true, children: credentialsPath })
11481
11780
  ] })
11482
11781
  ] })
11483
11782
  ] });
11484
11783
  }
11485
- return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", children: [
11784
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
11486
11785
  /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "\u2717 Not authenticated" }),
11487
11786
  /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: 'Run "link-cli auth login" to authenticate' }),
11488
- /* @__PURE__ */ jsx3(Box3, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsxs2(Text3, { children: [
11787
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsxs3(Text3, { children: [
11489
11788
  "Credentials: ",
11490
11789
  /* @__PURE__ */ jsx3(Text3, { bold: true, children: credentialsPath })
11491
11790
  ] }) })
@@ -11494,7 +11793,8 @@ var AuthStatus = ({ onComplete }) => {
11494
11793
 
11495
11794
  // src/commands/auth/index.tsx
11496
11795
  import { jsx as jsx4 } from "react/jsx-runtime";
11497
- function createAuthCli(authResource, getUpdateInfo2) {
11796
+ function createAuthCli(authResource, getUpdateInfo2, authStorage2) {
11797
+ const storage2 = authStorage2 ?? storage;
11498
11798
  const cli2 = Cli.create("auth", {
11499
11799
  description: "Authentication commands"
11500
11800
  });
@@ -11511,25 +11811,22 @@ function createAuthCli(authResource, getUpdateInfo2) {
11511
11811
  });
11512
11812
  }
11513
11813
  if (!c.agent && !c.formatExplicit) {
11514
- return new Promise((resolve) => {
11515
- const { waitUntilExit } = render(
11516
- /* @__PURE__ */ jsx4(
11517
- Login,
11518
- {
11519
- authResource,
11520
- clientName,
11521
- onComplete: () => {
11522
- }
11814
+ return renderInteractive(
11815
+ /* @__PURE__ */ jsx4(
11816
+ Login,
11817
+ {
11818
+ authResource,
11819
+ clientName,
11820
+ authStorage: storage2,
11821
+ onComplete: () => {
11523
11822
  }
11524
- )
11525
- );
11526
- waitUntilExit().then(
11527
- () => resolve({ authenticated: true, token_type: "Bearer" })
11528
- );
11529
- });
11823
+ }
11824
+ ),
11825
+ () => ({ authenticated: true, token_type: "Bearer" })
11826
+ );
11530
11827
  }
11531
11828
  const authRequest = await authResource.initiateDeviceAuth(clientName);
11532
- storage.setPendingDeviceAuth({
11829
+ storage2.setPendingDeviceAuth({
11533
11830
  device_code: authRequest.device_code,
11534
11831
  interval: authRequest.interval,
11535
11832
  expires_at: Date.now() + authRequest.expires_in * 1e3,
@@ -11552,25 +11849,30 @@ function createAuthCli(authResource, getUpdateInfo2) {
11552
11849
  description: "Log out from Link",
11553
11850
  outputPolicy: "agent-only",
11554
11851
  async run(c) {
11555
- const auth = storage.getAuth();
11852
+ const auth = storage2.getAuth();
11556
11853
  if (auth?.refresh_token) {
11557
11854
  try {
11558
11855
  await authResource.revokeToken(auth.refresh_token);
11559
11856
  } catch {
11560
11857
  }
11561
11858
  }
11562
- storage.clearAuth();
11563
- storage.clearPendingDeviceAuth();
11564
- storage.deleteConfig();
11859
+ storage2.clearAuth();
11860
+ storage2.clearPendingDeviceAuth();
11861
+ storage2.deleteConfig();
11565
11862
  const result = { authenticated: false };
11566
11863
  if (!c.agent && !c.formatExplicit) {
11567
- return new Promise((resolve) => {
11568
- const { waitUntilExit } = render(
11569
- /* @__PURE__ */ jsx4(Logout, { authResource, onComplete: () => {
11570
- } })
11571
- );
11572
- waitUntilExit().then(() => resolve(result));
11573
- });
11864
+ return renderInteractive(
11865
+ /* @__PURE__ */ jsx4(
11866
+ Logout,
11867
+ {
11868
+ authResource,
11869
+ authStorage: storage2,
11870
+ onComplete: () => {
11871
+ }
11872
+ }
11873
+ ),
11874
+ () => result
11875
+ );
11574
11876
  }
11575
11877
  return result;
11576
11878
  }
@@ -11583,7 +11885,6 @@ function createAuthCli(authResource, getUpdateInfo2) {
11583
11885
  const opts = c.options;
11584
11886
  const interval = opts.interval;
11585
11887
  const maxAttempts = opts.maxAttempts;
11586
- const deadline = Date.now() + opts.timeout * 1e3;
11587
11888
  const updateInfo = await getUpdateInfo2?.({
11588
11889
  polling: interval > 0
11589
11890
  });
@@ -11593,70 +11894,63 @@ function createAuthCli(authResource, getUpdateInfo2) {
11593
11894
  update_command: "npm install -g @stripe/link-cli"
11594
11895
  } : void 0;
11595
11896
  if (!c.agent && !c.formatExplicit) {
11596
- return new Promise((resolve) => {
11597
- const { waitUntilExit } = render(
11598
- /* @__PURE__ */ jsx4(AuthStatus, { onComplete: () => {
11599
- } })
11600
- );
11601
- waitUntilExit().then(() => {
11602
- const auth = storage.getAuth();
11603
- resolve({
11897
+ return renderInteractive(
11898
+ /* @__PURE__ */ jsx4(AuthStatus, { authStorage: storage2, onComplete: () => {
11899
+ } }),
11900
+ () => {
11901
+ const auth = storage2.getAuth();
11902
+ return {
11604
11903
  authenticated: !!auth,
11605
11904
  ...auth ? {
11606
11905
  access_token: `${auth.access_token.substring(0, 20)}...`,
11607
11906
  token_type: auth.token_type
11608
11907
  } : {},
11609
- credentials_path: storage.getPath(),
11908
+ credentials_path: storage2.getPath(),
11610
11909
  ...update && { update }
11611
- });
11612
- });
11613
- });
11910
+ };
11911
+ }
11912
+ );
11614
11913
  }
11615
- let attempts = 0;
11616
- let previousAttemptData;
11617
- while (true) {
11618
- const pending = storage.getPendingDeviceAuth();
11619
- if (pending && !storage.isAuthenticated()) {
11620
- const tokens = await authResource.pollDeviceAuth(pending.device_code);
11621
- if (tokens) {
11622
- storage.setAuth(tokens);
11623
- storage.clearPendingDeviceAuth();
11624
- }
11625
- }
11626
- const auth = storage.getAuth();
11627
- if (auth) {
11628
- yield {
11629
- authenticated: true,
11630
- access_token: `${auth.access_token.substring(0, 20)}...`,
11631
- token_type: auth.token_type,
11632
- credentials_path: storage.getPath(),
11633
- ...update && { update }
11914
+ for await (const result of pollUntil({
11915
+ fn: async () => {
11916
+ const pending = storage2.getPendingDeviceAuth();
11917
+ if (pending && !storage2.isAuthenticated()) {
11918
+ const tokens = await authResource.pollDeviceAuth(
11919
+ pending.device_code
11920
+ );
11921
+ if (tokens) {
11922
+ storage2.setAuth(tokens);
11923
+ storage2.clearPendingDeviceAuth();
11924
+ }
11925
+ }
11926
+ const auth = storage2.getAuth();
11927
+ if (auth) {
11928
+ return {
11929
+ authenticated: true,
11930
+ access_token: `${auth.access_token.substring(0, 20)}...`,
11931
+ token_type: auth.token_type,
11932
+ credentials_path: storage2.getPath(),
11933
+ ...update && { update }
11934
+ };
11935
+ }
11936
+ const currentPending = storage2.getPendingDeviceAuth();
11937
+ return {
11938
+ authenticated: false,
11939
+ credentials_path: storage2.getPath(),
11940
+ ...update && { update },
11941
+ ...currentPending ? {
11942
+ pending: true,
11943
+ verification_url: currentPending.verification_url,
11944
+ phrase: currentPending.phrase
11945
+ } : {}
11634
11946
  };
11635
- return;
11636
- }
11637
- const currentPending = storage.getPendingDeviceAuth();
11638
- const status = {
11639
- authenticated: false,
11640
- credentials_path: storage.getPath(),
11641
- ...update && { update },
11642
- ...currentPending ? {
11643
- pending: true,
11644
- verification_url: currentPending.verification_url,
11645
- phrase: currentPending.phrase
11646
- } : {}
11647
- };
11648
- attempts++;
11649
- const shouldStop = interval <= 0 || maxAttempts > 0 && attempts >= maxAttempts || Date.now() >= deadline;
11650
- if (shouldStop) {
11651
- yield status;
11652
- return;
11653
- }
11654
- const snapshot = JSON.stringify(status);
11655
- if (snapshot !== previousAttemptData) {
11656
- previousAttemptData = snapshot;
11657
- yield status;
11658
- }
11659
- await new Promise((resolve) => setTimeout(resolve, interval * 1e3));
11947
+ },
11948
+ isTerminal: (status) => status.authenticated,
11949
+ interval,
11950
+ maxAttempts,
11951
+ timeout: opts.timeout
11952
+ })) {
11953
+ yield result.value;
11660
11954
  }
11661
11955
  }
11662
11956
  });
@@ -11665,11 +11959,10 @@ function createAuthCli(authResource, getUpdateInfo2) {
11665
11959
 
11666
11960
  // src/commands/demo/index.tsx
11667
11961
  import { Cli as Cli2, z as z2 } from "incur";
11668
- import { render as render2 } from "ink";
11669
11962
 
11670
11963
  // src/commands/demo/demo-runner.tsx
11671
- import { Box as Box8, Text as Text9, useInput as useInput4 } from "ink";
11672
- import { useCallback, useState as useState7 } from "react";
11964
+ import { Box as Box8, Text as Text9, useApp, useInput as useInput4 } from "ink";
11965
+ import { useCallback as useCallback2, useState as useState7 } from "react";
11673
11966
 
11674
11967
  // src/utils/markdown-text.tsx
11675
11968
  import { Text as Text4 } from "ink";
@@ -11758,13 +12051,13 @@ function renderQrMatrix(url) {
11758
12051
  }
11759
12052
 
11760
12053
  // src/commands/spend-request/app-download-qr-codes.tsx
11761
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
12054
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
11762
12055
  var DOWNLOAD_URL = "https://link.com/download";
11763
12056
  var AppDownloadQrCodes = () => {
11764
12057
  const qrLines = useMemo(() => renderQrMatrix(DOWNLOAD_URL), []);
11765
- return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", marginTop: 1, children: [
12058
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: 1, children: [
11766
12059
  /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "Get the Link app to approve spend requests from your phone" }),
11767
- /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", alignItems: "flex-start", marginTop: 1, children: [
12060
+ /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", alignItems: "flex-start", marginTop: 1, children: [
11768
12061
  qrLines.map((line, i) => (
11769
12062
  // biome-ignore lint/suspicious/noArrayIndexKey: stable static array
11770
12063
  /* @__PURE__ */ jsx6(Text5, { children: line }, i)
@@ -11776,7 +12069,7 @@ var AppDownloadQrCodes = () => {
11776
12069
 
11777
12070
  // src/commands/demo/card-flow.tsx
11778
12071
  import { Box as Box5, Text as Text6, useInput as useInput2 } from "ink";
11779
- import { useEffect as useEffect4, useRef, useState as useState4 } from "react";
12072
+ import { useEffect as useEffect4, useRef as useRef2, useState as useState4 } from "react";
11780
12073
 
11781
12074
  // src/utils/poll-until-approved.ts
11782
12075
  function pollUntilApproved(repository, id, options = {}) {
@@ -11940,7 +12233,7 @@ var ONBOARD = {
11940
12233
  };
11941
12234
 
11942
12235
  // src/commands/demo/card-flow.tsx
11943
- import { Fragment, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
12236
+ import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
11944
12237
  function formatPmLabel(pm) {
11945
12238
  return `${pm.card_details?.brand ?? pm.type} ****${pm.card_details?.last4 ?? ""}`;
11946
12239
  }
@@ -11965,11 +12258,11 @@ var CardFlow = ({
11965
12258
  const [spendRequest, setSpendRequest] = useState4(null);
11966
12259
  const [error, setError] = useState4("");
11967
12260
  const approvalUrl = spendRequest?.approval_url ?? "";
11968
- const enterResolver = useRef(null);
11969
- const pmResolver = useRef(null);
11970
- const retryChoiceResolver = useRef(null);
11971
- function waitForEnter() {
11972
- return new Promise((resolve) => {
12261
+ const enterResolver = useRef2(null);
12262
+ const pmResolver = useRef2(null);
12263
+ const retryChoiceResolver = useRef2(null);
12264
+ function waitForEnter() {
12265
+ return new Promise((resolve) => {
11973
12266
  enterResolver.current = resolve;
11974
12267
  });
11975
12268
  }
@@ -12015,7 +12308,7 @@ var CardFlow = ({
12015
12308
  }
12016
12309
  }
12017
12310
  });
12018
- const started = useRef(false);
12311
+ const started = useRef2(false);
12019
12312
  useEffect4(() => {
12020
12313
  if (started.current) return;
12021
12314
  started.current = true;
@@ -12066,7 +12359,12 @@ var CardFlow = ({
12066
12359
  setStep("await-approval");
12067
12360
  for (; ; ) {
12068
12361
  try {
12069
- await pollUntilApproved(spendRequestRepo2, result.id);
12362
+ const final = await pollUntilApproved(spendRequestRepo2, result.id);
12363
+ if (final.status !== "approved") {
12364
+ throw new Error(
12365
+ `Spend request did not reach approved (status: ${final.status})`
12366
+ );
12367
+ }
12070
12368
  break;
12071
12369
  } catch (err) {
12072
12370
  if (err.message === "Approval polling timed out") {
@@ -12119,21 +12417,21 @@ var CardFlow = ({
12119
12417
  ];
12120
12418
  return order.indexOf(step) > order.indexOf(target);
12121
12419
  };
12122
- const prompt = (label = "Press [Enter] to continue") => /* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
12420
+ const prompt = (label = "Press [Enter] to continue") => /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
12123
12421
  "\n",
12124
12422
  ">",
12125
12423
  " ",
12126
12424
  label
12127
12425
  ] });
12128
- return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, children: [
12426
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", gap: 1, children: [
12129
12427
  /* @__PURE__ */ jsx7(Text6, { bold: true, color: "cyan", children: CARD_FLOW.title }),
12130
- /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
12131
- /* @__PURE__ */ jsxs4(Box5, { flexDirection: "row", gap: 1, children: [
12428
+ /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
12429
+ /* @__PURE__ */ jsxs5(Box5, { flexDirection: "row", gap: 1, children: [
12132
12430
  /* @__PURE__ */ jsx7(Text6, { color: "yellow", children: "[testmode]" }),
12133
12431
  /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: DEMO_MERCHANT_URL })
12134
12432
  ] }),
12135
12433
  /* @__PURE__ */ jsx7(MarkdownText, { children: CARD_FLOW.intro.description }),
12136
- /* @__PURE__ */ jsxs4(Box5, { marginTop: 1, flexDirection: "column", children: [
12434
+ /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, flexDirection: "column", children: [
12137
12435
  /* @__PURE__ */ jsx7(Text6, { children: "What happens:" }),
12138
12436
  CARD_FLOW.intro.steps.map((s, i) => {
12139
12437
  const doneAfter = [
@@ -12150,17 +12448,17 @@ var CardFlow = ({
12150
12448
  ];
12151
12449
  const done = pastStep(doneAfter[i]);
12152
12450
  const active = !done && (step === activeFrom[i] || pastStep(activeFrom[i]));
12153
- return done ? /* @__PURE__ */ jsxs4(Text6, { dimColor: true, strikethrough: true, children: [
12451
+ return done ? /* @__PURE__ */ jsxs5(Text6, { dimColor: true, strikethrough: true, children: [
12154
12452
  " ",
12155
12453
  i + 1,
12156
12454
  ". ",
12157
12455
  s
12158
- ] }, s) : active ? /* @__PURE__ */ jsxs4(Text6, { bold: true, color: "cyan", children: [
12456
+ ] }, s) : active ? /* @__PURE__ */ jsxs5(Text6, { bold: true, color: "cyan", children: [
12159
12457
  " ",
12160
12458
  i + 1,
12161
12459
  ". ",
12162
12460
  s
12163
- ] }, s) : /* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
12461
+ ] }, s) : /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
12164
12462
  " ",
12165
12463
  i + 1,
12166
12464
  ". ",
@@ -12171,23 +12469,23 @@ var CardFlow = ({
12171
12469
  step === "intro" && prompt(CARD_FLOW.intro.prompt)
12172
12470
  ] }),
12173
12471
  step === "fetch-pm" && /* @__PURE__ */ jsx7(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "Fetching payment methods from your Link wallet..." }) }),
12174
- (step === "pick-pm" || step === "explain-pm") && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
12175
- step === "pick-pm" && paymentMethods.length > 1 && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
12472
+ (step === "pick-pm" || step === "explain-pm") && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
12473
+ step === "pick-pm" && paymentMethods.length > 1 && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
12176
12474
  /* @__PURE__ */ jsx7(Text6, { children: "Which payment method should we use for the demo?" }),
12177
- /* @__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: [
12475
+ /* @__PURE__ */ jsx7(Box5, { flexDirection: "column", marginTop: 1, children: paymentMethods.map((pm, i) => /* @__PURE__ */ jsx7(Text6, { children: i === selectedPmIndex ? /* @__PURE__ */ jsxs5(Text6, { color: "cyan", bold: true, children: [
12178
12476
  ">",
12179
12477
  " ",
12180
12478
  formatPmLabel(pm),
12181
12479
  pm.is_default ? " (default)" : ""
12182
- ] }) : /* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
12480
+ ] }) : /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
12183
12481
  " ",
12184
12482
  formatPmLabel(pm),
12185
12483
  pm.is_default ? " (default)" : ""
12186
12484
  ] }) }, pm.id)) }),
12187
12485
  /* @__PURE__ */ jsx7(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "Use \u2191\u2193 to select, [Enter] to confirm" }) })
12188
12486
  ] }),
12189
- paymentMethod && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
12190
- /* @__PURE__ */ jsxs4(Text6, { color: "green", children: [
12487
+ paymentMethod && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
12488
+ /* @__PURE__ */ jsxs5(Text6, { color: "green", children: [
12191
12489
  "\u2713 Using ",
12192
12490
  /* @__PURE__ */ jsx7(Text6, { bold: true, children: pmLabel }),
12193
12491
  paymentMethod.is_default ? " (default)" : ""
@@ -12195,19 +12493,19 @@ var CardFlow = ({
12195
12493
  step === "explain-pm" && prompt(CARD_FLOW.explainPm.prompt)
12196
12494
  ] })
12197
12495
  ] }),
12198
- step === "create-spend" && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
12496
+ step === "create-spend" && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
12199
12497
  /* @__PURE__ */ jsx7(MarkdownText, { children: CARD_FLOW.createSpend.description }),
12200
12498
  /* @__PURE__ */ jsx7(Box5, { marginY: 1, children: /* @__PURE__ */ jsx7(Text6, { color: "cyan", children: CARD_FLOW.createSpend.loading }) })
12201
12499
  ] }),
12202
- (step === "await-approval" || step === "approval-timeout") && spendRequest && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
12203
- /* @__PURE__ */ jsxs4(Text6, { color: "green", children: [
12500
+ (step === "await-approval" || step === "approval-timeout") && spendRequest && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
12501
+ /* @__PURE__ */ jsxs5(Text6, { color: "green", children: [
12204
12502
  "\u2713 Spend request created (",
12205
12503
  spendRequest.id,
12206
12504
  ")"
12207
12505
  ] }),
12208
- step === "await-approval" && /* @__PURE__ */ jsxs4(Fragment, { children: [
12506
+ step === "await-approval" && /* @__PURE__ */ jsxs5(Fragment, { children: [
12209
12507
  /* @__PURE__ */ jsx7(Text6, { children: CARD_FLOW.approval.description }),
12210
- /* @__PURE__ */ jsxs4(
12508
+ /* @__PURE__ */ jsxs5(
12211
12509
  Box5,
12212
12510
  {
12213
12511
  flexDirection: "column",
@@ -12217,7 +12515,7 @@ var CardFlow = ({
12217
12515
  paddingY: 1,
12218
12516
  marginTop: 1,
12219
12517
  children: [
12220
- /* @__PURE__ */ jsxs4(Text6, { children: [
12518
+ /* @__PURE__ */ jsxs5(Text6, { children: [
12221
12519
  "Approve at:",
12222
12520
  " ",
12223
12521
  /* @__PURE__ */ jsx7(Text6, { bold: true, color: "cyan", children: approvalUrl })
@@ -12228,9 +12526,9 @@ var CardFlow = ({
12228
12526
  ),
12229
12527
  /* @__PURE__ */ jsx7(Box5, { marginY: 1, children: /* @__PURE__ */ jsx7(Text6, { color: "cyan", children: CARD_FLOW.approval.loading }) })
12230
12528
  ] }),
12231
- step === "approval-timeout" && /* @__PURE__ */ jsxs4(Fragment, { children: [
12529
+ step === "approval-timeout" && /* @__PURE__ */ jsxs5(Fragment, { children: [
12232
12530
  /* @__PURE__ */ jsx7(Text6, { color: "yellow", children: "\u26A0 Approval timed out (5 min). Still pending \u2014 you can still approve." }),
12233
- /* @__PURE__ */ jsxs4(
12531
+ /* @__PURE__ */ jsxs5(
12234
12532
  Box5,
12235
12533
  {
12236
12534
  flexDirection: "column",
@@ -12240,7 +12538,7 @@ var CardFlow = ({
12240
12538
  paddingY: 1,
12241
12539
  marginTop: 1,
12242
12540
  children: [
12243
- /* @__PURE__ */ jsxs4(Text6, { children: [
12541
+ /* @__PURE__ */ jsxs5(Text6, { children: [
12244
12542
  "Approve at:",
12245
12543
  " ",
12246
12544
  /* @__PURE__ */ jsx7(Text6, { bold: true, color: "cyan", children: approvalUrl })
@@ -12252,10 +12550,10 @@ var CardFlow = ({
12252
12550
  /* @__PURE__ */ jsx7(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "r Retry polling q Quit demo" }) })
12253
12551
  ] })
12254
12552
  ] }),
12255
- (step === "show-card" || step === "open-url" || step === "done") && card && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
12553
+ (step === "show-card" || step === "open-url" || step === "done") && card && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
12256
12554
  /* @__PURE__ */ jsx7(Text6, { color: "green", children: "\u2713 Approved!" }),
12257
12555
  /* @__PURE__ */ jsx7(MarkdownText, { children: CARD_FLOW.showCard.description }),
12258
- /* @__PURE__ */ jsx7(Box5, { flexDirection: "column", paddingX: 2, marginTop: 1, children: /* @__PURE__ */ jsxs4(Text6, { children: [
12556
+ /* @__PURE__ */ jsx7(Box5, { flexDirection: "column", paddingX: 2, marginTop: 1, children: /* @__PURE__ */ jsxs5(Text6, { children: [
12259
12557
  /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "Number " }),
12260
12558
  /* @__PURE__ */ jsx7(Text6, { bold: true, children: formatCardNumber(card.number) }),
12261
12559
  " ",
@@ -12264,14 +12562,14 @@ var CardFlow = ({
12264
12562
  " ",
12265
12563
  /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "CVC " }),
12266
12564
  /* @__PURE__ */ jsx7(Text6, { bold: true, children: card.cvc }),
12267
- card.billing_address?.postal_code && /* @__PURE__ */ jsxs4(Fragment, { children: [
12565
+ card.billing_address?.postal_code && /* @__PURE__ */ jsxs5(Fragment, { children: [
12268
12566
  " ",
12269
12567
  /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "Zip " }),
12270
12568
  /* @__PURE__ */ jsx7(Text6, { bold: true, children: card.billing_address.postal_code })
12271
12569
  ] }),
12272
- card.valid_until && /* @__PURE__ */ jsxs4(Fragment, { children: [
12570
+ card.valid_until && /* @__PURE__ */ jsxs5(Fragment, { children: [
12273
12571
  " ",
12274
- /* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
12572
+ /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
12275
12573
  "expires",
12276
12574
  " ",
12277
12575
  new Date(card.valid_until).toLocaleTimeString([], {
@@ -12283,14 +12581,14 @@ var CardFlow = ({
12283
12581
  ] }) }),
12284
12582
  step === "show-card" && prompt(CARD_FLOW.showCard.prompt)
12285
12583
  ] }),
12286
- step === "done" && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
12287
- /* @__PURE__ */ jsxs4(Text6, { color: "green", children: [
12584
+ step === "done" && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
12585
+ /* @__PURE__ */ jsxs5(Text6, { color: "green", children: [
12288
12586
  "\u2713 ",
12289
12587
  CARD_FLOW.done.success
12290
12588
  ] }),
12291
12589
  /* @__PURE__ */ jsx7(Text6, { children: CARD_FLOW.done.detail })
12292
12590
  ] }),
12293
- step === "error" && /* @__PURE__ */ jsxs4(Text6, { color: "red", children: [
12591
+ step === "error" && /* @__PURE__ */ jsxs5(Text6, { color: "red", children: [
12294
12592
  "Error: ",
12295
12593
  error
12296
12594
  ] })
@@ -12299,20 +12597,20 @@ var CardFlow = ({
12299
12597
 
12300
12598
  // src/commands/demo/spt-flow.tsx
12301
12599
  import { Box as Box7, Text as Text8, useInput as useInput3 } from "ink";
12302
- import { useEffect as useEffect6, useRef as useRef2, useState as useState6 } from "react";
12600
+ import { useEffect as useEffect6, useRef as useRef3, useState as useState6 } from "react";
12303
12601
 
12304
12602
  // src/commands/mpp/decode.ts
12305
12603
  import { Challenge } from "mppx";
12306
- function getString(value, path6, required = true) {
12604
+ function getString(value, path7, required = true) {
12307
12605
  if (value == null) {
12308
12606
  if (required) {
12309
- throw new Error(`Invalid stripe challenge request: ${path6}: missing`);
12607
+ throw new Error(`Invalid stripe challenge request: ${path7}: missing`);
12310
12608
  }
12311
12609
  return void 0;
12312
12610
  }
12313
12611
  if (typeof value !== "string") {
12314
12612
  throw new Error(
12315
- `Invalid stripe challenge request: ${path6}: expected string, received ${typeof value}`
12613
+ `Invalid stripe challenge request: ${path7}: expected string, received ${typeof value}`
12316
12614
  );
12317
12615
  }
12318
12616
  return value;
@@ -12381,12 +12679,12 @@ function decodeStripeChallenge(challengeHeader) {
12381
12679
 
12382
12680
  // src/commands/mpp/pay.tsx
12383
12681
  import { Box as Box6, Text as Text7 } from "ink";
12384
- import Spinner2 from "ink-spinner";
12682
+ import Spinner3 from "ink-spinner";
12385
12683
  import { Credential, Method } from "mppx";
12386
12684
  import { Mppx, Transport } from "mppx/client";
12387
12685
  import { Methods as StripeMethods } from "mppx/stripe";
12388
12686
  import { useEffect as useEffect5, useState as useState5 } from "react";
12389
- import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
12687
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
12390
12688
  function buildHeaders(data, headers) {
12391
12689
  const result = {};
12392
12690
  if (data !== void 0) {
@@ -12401,14 +12699,9 @@ function buildHeaders(data, headers) {
12401
12699
  }
12402
12700
  return result;
12403
12701
  }
12404
- async function readPayResult(response, options) {
12702
+ async function readPayResult(response) {
12405
12703
  const responseHeaders = Object.fromEntries(response.headers.entries());
12406
12704
  const body = await response.text();
12407
- if (options?.failOnError && !response.ok) {
12408
- throw new Error(
12409
- `Payment submission failed with status ${response.status}: ${body}`
12410
- );
12411
- }
12412
12705
  return { status: response.status, headers: responseHeaders, body };
12413
12706
  }
12414
12707
  function createStripePaymentClient(spt) {
@@ -12480,7 +12773,7 @@ async function runMppPay(url, spendRequestId, method, data, headers, repository)
12480
12773
  Authorization: authHeader
12481
12774
  }
12482
12775
  });
12483
- return readPayResult(retryResponse, { failOnError: true });
12776
+ return readPayResult(retryResponse);
12484
12777
  }
12485
12778
  function MppPay({
12486
12779
  url,
@@ -12528,9 +12821,10 @@ function MppPay({
12528
12821
  headers: requestHeaders
12529
12822
  });
12530
12823
  if (initialResponse.status !== 402) {
12531
- setResult(await readPayResult(initialResponse));
12824
+ const payResult2 = await readPayResult(initialResponse);
12825
+ setResult(payResult2);
12532
12826
  setStep("done");
12533
- onComplete();
12827
+ onComplete(payResult2);
12534
12828
  return;
12535
12829
  }
12536
12830
  setStep("signing");
@@ -12546,12 +12840,13 @@ function MppPay({
12546
12840
  Authorization: authHeader
12547
12841
  }
12548
12842
  });
12549
- setResult(await readPayResult(retryResponse, { failOnError: true }));
12843
+ const payResult = await readPayResult(retryResponse);
12844
+ setResult(payResult);
12550
12845
  setStep("done");
12551
- onComplete();
12846
+ onComplete(payResult);
12552
12847
  } catch (err) {
12553
12848
  setError(err.message);
12554
- onComplete();
12849
+ onComplete(null);
12555
12850
  }
12556
12851
  })();
12557
12852
  }, [url, spendRequestId, method, data, headers, repository, onComplete]);
@@ -12563,30 +12858,36 @@ function MppPay({
12563
12858
  done: "Done"
12564
12859
  };
12565
12860
  if (error) {
12566
- return /* @__PURE__ */ jsxs5(Text7, { color: "red", children: [
12861
+ return /* @__PURE__ */ jsxs6(Text7, { color: "red", children: [
12567
12862
  "Error: ",
12568
12863
  error
12569
12864
  ] });
12570
12865
  }
12571
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
12572
- step !== "done" && /* @__PURE__ */ jsx8(Box6, { children: /* @__PURE__ */ jsxs5(Text7, { color: "cyan", children: [
12573
- /* @__PURE__ */ jsx8(Spinner2, { type: "dots" }),
12866
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
12867
+ step !== "done" && /* @__PURE__ */ jsx8(Box6, { children: /* @__PURE__ */ jsxs6(Text7, { color: "cyan", children: [
12868
+ /* @__PURE__ */ jsx8(Spinner3, { type: "dots" }),
12574
12869
  " ",
12575
12870
  stepLabels[step],
12576
12871
  "..."
12577
12872
  ] }) }),
12578
- result && /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
12579
- /* @__PURE__ */ jsxs5(Text7, { color: "green", children: [
12580
- "HTTP ",
12581
- result.status
12582
- ] }),
12873
+ result && /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
12874
+ /* @__PURE__ */ jsxs6(
12875
+ Text7,
12876
+ {
12877
+ color: result.status >= 400 ? "red" : result.status >= 300 ? "yellow" : "green",
12878
+ children: [
12879
+ "HTTP ",
12880
+ result.status
12881
+ ]
12882
+ }
12883
+ ),
12583
12884
  /* @__PURE__ */ jsx8(Text7, { children: result.body })
12584
12885
  ] })
12585
12886
  ] });
12586
12887
  }
12587
12888
 
12588
12889
  // src/commands/demo/spt-flow.tsx
12589
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
12890
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
12590
12891
  var SptFlow = ({
12591
12892
  spendRequestRepo: spendRequestRepo2,
12592
12893
  paymentMethodsResource,
@@ -12601,9 +12902,9 @@ var SptFlow = ({
12601
12902
  const [payResult, setPayResult] = useState6(null);
12602
12903
  const [error, setError] = useState6("");
12603
12904
  const approvalUrl = spendRequest?.approval_url ?? "";
12604
- const enterResolver = useRef2(null);
12605
- const pmResolver = useRef2(null);
12606
- const retryChoiceResolver = useRef2(null);
12905
+ const enterResolver = useRef3(null);
12906
+ const pmResolver = useRef3(null);
12907
+ const retryChoiceResolver = useRef3(null);
12607
12908
  function waitForEnter() {
12608
12909
  return new Promise((resolve) => {
12609
12910
  enterResolver.current = resolve;
@@ -12651,7 +12952,7 @@ var SptFlow = ({
12651
12952
  }
12652
12953
  }
12653
12954
  });
12654
- const started = useRef2(false);
12955
+ const started = useRef3(false);
12655
12956
  useEffect6(() => {
12656
12957
  if (started.current) return;
12657
12958
  started.current = true;
@@ -12711,6 +13012,11 @@ var SptFlow = ({
12711
13012
  spendRequestRepo2,
12712
13013
  result.id
12713
13014
  );
13015
+ if (approved.status !== "approved") {
13016
+ throw new Error(
13017
+ `Spend request did not reach approved (status: ${approved.status})`
13018
+ );
13019
+ }
12714
13020
  setSpendRequest(approved);
12715
13021
  break;
12716
13022
  } catch (err) {
@@ -12764,25 +13070,25 @@ var SptFlow = ({
12764
13070
  ];
12765
13071
  return order.indexOf(step) > order.indexOf(target);
12766
13072
  };
12767
- const prompt = (label = "Press [Enter] to continue") => /* @__PURE__ */ jsxs6(Text8, { dimColor: true, children: [
13073
+ const prompt = (label = "Press [Enter] to continue") => /* @__PURE__ */ jsxs7(Text8, { dimColor: true, children: [
12768
13074
  "\n",
12769
13075
  ">",
12770
13076
  " ",
12771
13077
  label
12772
13078
  ] });
12773
- return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 1, children: [
13079
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", gap: 1, children: [
12774
13080
  /* @__PURE__ */ jsx9(Text8, { bold: true, color: "cyan", children: SPT_FLOW.title }),
12775
- /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
12776
- /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 1, children: [
13081
+ /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
13082
+ /* @__PURE__ */ jsxs7(Box7, { flexDirection: "row", gap: 1, children: [
12777
13083
  /* @__PURE__ */ jsx9(Text8, { color: "yellow", children: "[testmode]" }),
12778
- /* @__PURE__ */ jsxs6(Text8, { dimColor: true, children: [
13084
+ /* @__PURE__ */ jsxs7(Text8, { dimColor: true, children: [
12779
13085
  DEMO_CLIMATE_API_URL,
12780
13086
  " ",
12781
13087
  DEMO_MPP_DEV_URL
12782
13088
  ] })
12783
13089
  ] }),
12784
13090
  /* @__PURE__ */ jsx9(MarkdownText, { children: SPT_FLOW.intro.description }),
12785
- /* @__PURE__ */ jsxs6(Box7, { marginTop: 1, flexDirection: "column", children: [
13091
+ /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
12786
13092
  /* @__PURE__ */ jsx9(Text8, { children: SPT_FLOW.intro.preamble }),
12787
13093
  SPT_FLOW.intro.steps.map((s, i) => {
12788
13094
  const doneAfter = [
@@ -12802,17 +13108,17 @@ var SptFlow = ({
12802
13108
  const done = pastStep(doneAfter[i]);
12803
13109
  const active = !done && (step === activeFrom[i] || pastStep(activeFrom[i]));
12804
13110
  const label = s.replace(/`/g, "");
12805
- return done ? /* @__PURE__ */ jsxs6(Text8, { dimColor: true, strikethrough: true, children: [
13111
+ return done ? /* @__PURE__ */ jsxs7(Text8, { dimColor: true, strikethrough: true, children: [
12806
13112
  " ",
12807
13113
  i + 1,
12808
13114
  ". ",
12809
13115
  label
12810
- ] }, s) : active ? /* @__PURE__ */ jsxs6(Text8, { bold: true, color: "cyan", children: [
13116
+ ] }, s) : active ? /* @__PURE__ */ jsxs7(Text8, { bold: true, color: "cyan", children: [
12811
13117
  " ",
12812
13118
  i + 1,
12813
13119
  ". ",
12814
13120
  label
12815
- ] }, s) : /* @__PURE__ */ jsxs6(Text8, { dimColor: true, children: [
13121
+ ] }, s) : /* @__PURE__ */ jsxs7(Text8, { dimColor: true, children: [
12816
13122
  " ",
12817
13123
  i + 1,
12818
13124
  ". ",
@@ -12823,45 +13129,45 @@ var SptFlow = ({
12823
13129
  step === "intro" && prompt(SPT_FLOW.intro.prompt)
12824
13130
  ] }),
12825
13131
  step === "fetch-pm" && /* @__PURE__ */ jsx9(Box7, { marginY: 1, children: /* @__PURE__ */ jsx9(Text8, { color: "cyan", children: "Fetching payment methods..." }) }),
12826
- step === "pick-pm" && paymentMethods.length > 1 && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
13132
+ step === "pick-pm" && paymentMethods.length > 1 && /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
12827
13133
  /* @__PURE__ */ jsx9(Text8, { children: "Which payment method should we use for the demo?" }),
12828
- /* @__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: [
13134
+ /* @__PURE__ */ jsx9(Box7, { flexDirection: "column", marginTop: 1, children: paymentMethods.map((pm, i) => /* @__PURE__ */ jsx9(Text8, { children: i === selectedPmIndex ? /* @__PURE__ */ jsxs7(Text8, { color: "cyan", bold: true, children: [
12829
13135
  ">",
12830
13136
  " ",
12831
13137
  pm.card_details ? `${pm.card_details.brand} ****${pm.card_details.last4}` : pm.type,
12832
13138
  pm.is_default ? " (default)" : ""
12833
- ] }) : /* @__PURE__ */ jsxs6(Text8, { dimColor: true, children: [
13139
+ ] }) : /* @__PURE__ */ jsxs7(Text8, { dimColor: true, children: [
12834
13140
  " ",
12835
13141
  pm.card_details ? `${pm.card_details.brand} ****${pm.card_details.last4}` : pm.type,
12836
13142
  pm.is_default ? " (default)" : ""
12837
13143
  ] }) }, pm.id)) }),
12838
13144
  /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text8, { dimColor: true, children: "Use \u2191\u2193 to select, [Enter] to confirm" }) })
12839
13145
  ] }),
12840
- (step === "probe" || step === "explain-402") && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
13146
+ (step === "probe" || step === "explain-402") && /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
12841
13147
  /* @__PURE__ */ jsx9(MarkdownText, { children: SPT_FLOW.probe.description }),
12842
13148
  step === "probe" && /* @__PURE__ */ jsx9(Box7, { marginY: 1, children: /* @__PURE__ */ jsx9(Text8, { color: "cyan", children: SPT_FLOW.probe.loading }) }),
12843
- step === "explain-402" && networkId && /* @__PURE__ */ jsxs6(Fragment2, { children: [
13149
+ step === "explain-402" && networkId && /* @__PURE__ */ jsxs7(Fragment2, { children: [
12844
13150
  /* @__PURE__ */ jsx9(MarkdownText, { children: SPT_FLOW.probe.detail }),
12845
- /* @__PURE__ */ jsxs6(Text8, { color: "green", children: [
13151
+ /* @__PURE__ */ jsxs7(Text8, { color: "green", children: [
12846
13152
  "\u2713 Got HTTP 402 \u2014 network_id: ",
12847
13153
  /* @__PURE__ */ jsx9(Text8, { bold: true, children: networkId })
12848
13154
  ] }),
12849
13155
  prompt(SPT_FLOW.probe.prompt)
12850
13156
  ] })
12851
13157
  ] }),
12852
- step === "create-spend" && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
13158
+ step === "create-spend" && /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
12853
13159
  /* @__PURE__ */ jsx9(MarkdownText, { children: SPT_FLOW.createSpend.description }),
12854
13160
  /* @__PURE__ */ jsx9(Box7, { marginY: 1, children: /* @__PURE__ */ jsx9(Text8, { color: "cyan", children: SPT_FLOW.createSpend.loading }) })
12855
13161
  ] }),
12856
- (step === "await-approval" || step === "approval-timeout") && spendRequest && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
12857
- /* @__PURE__ */ jsxs6(Text8, { color: "green", children: [
13162
+ (step === "await-approval" || step === "approval-timeout") && spendRequest && /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
13163
+ /* @__PURE__ */ jsxs7(Text8, { color: "green", children: [
12858
13164
  "\u2713 Spend request created (",
12859
13165
  spendRequest.id,
12860
13166
  ")"
12861
13167
  ] }),
12862
- step === "await-approval" && /* @__PURE__ */ jsxs6(Fragment2, { children: [
13168
+ step === "await-approval" && /* @__PURE__ */ jsxs7(Fragment2, { children: [
12863
13169
  /* @__PURE__ */ jsx9(Text8, { children: SPT_FLOW.approval.description }),
12864
- /* @__PURE__ */ jsxs6(
13170
+ /* @__PURE__ */ jsxs7(
12865
13171
  Box7,
12866
13172
  {
12867
13173
  flexDirection: "column",
@@ -12871,7 +13177,7 @@ var SptFlow = ({
12871
13177
  paddingY: 1,
12872
13178
  marginTop: 1,
12873
13179
  children: [
12874
- /* @__PURE__ */ jsxs6(Text8, { children: [
13180
+ /* @__PURE__ */ jsxs7(Text8, { children: [
12875
13181
  "Approve at:",
12876
13182
  " ",
12877
13183
  /* @__PURE__ */ jsx9(Text8, { bold: true, color: "cyan", children: approvalUrl })
@@ -12882,9 +13188,9 @@ var SptFlow = ({
12882
13188
  ),
12883
13189
  /* @__PURE__ */ jsx9(Box7, { marginY: 1, children: /* @__PURE__ */ jsx9(Text8, { color: "cyan", children: SPT_FLOW.approval.loading }) })
12884
13190
  ] }),
12885
- step === "approval-timeout" && /* @__PURE__ */ jsxs6(Fragment2, { children: [
13191
+ step === "approval-timeout" && /* @__PURE__ */ jsxs7(Fragment2, { children: [
12886
13192
  /* @__PURE__ */ jsx9(Text8, { color: "yellow", children: "\u26A0 Approval timed out (5 min). Still pending \u2014 you can still approve." }),
12887
- /* @__PURE__ */ jsxs6(
13193
+ /* @__PURE__ */ jsxs7(
12888
13194
  Box7,
12889
13195
  {
12890
13196
  flexDirection: "column",
@@ -12894,7 +13200,7 @@ var SptFlow = ({
12894
13200
  paddingY: 1,
12895
13201
  marginTop: 1,
12896
13202
  children: [
12897
- /* @__PURE__ */ jsxs6(Text8, { children: [
13203
+ /* @__PURE__ */ jsxs7(Text8, { children: [
12898
13204
  "Approve at:",
12899
13205
  " ",
12900
13206
  /* @__PURE__ */ jsx9(Text8, { bold: true, color: "cyan", children: approvalUrl })
@@ -12906,14 +13212,14 @@ var SptFlow = ({
12906
13212
  /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text8, { dimColor: true, children: "r Retry polling q Quit demo" }) })
12907
13213
  ] })
12908
13214
  ] }),
12909
- (step === "mpp-pay-gate" || step === "mpp-pay") && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
13215
+ (step === "mpp-pay-gate" || step === "mpp-pay") && /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
12910
13216
  /* @__PURE__ */ jsx9(Text8, { color: "green", children: "\u2713 Approved!" }),
12911
13217
  /* @__PURE__ */ jsx9(MarkdownText, { children: SPT_FLOW.mppPay.description }),
12912
13218
  step === "mpp-pay-gate" && prompt(SPT_FLOW.mppPay.prompt),
12913
13219
  step === "mpp-pay" && /* @__PURE__ */ jsx9(Box7, { marginY: 1, children: /* @__PURE__ */ jsx9(Text8, { color: "cyan", children: SPT_FLOW.mppPay.loading }) })
12914
13220
  ] }),
12915
- step === "done" && payResult && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
12916
- /* @__PURE__ */ jsxs6(Text8, { bold: true, color: "green", children: [
13221
+ step === "done" && payResult && /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
13222
+ /* @__PURE__ */ jsxs7(Text8, { bold: true, color: "green", children: [
12917
13223
  "\u2713 ",
12918
13224
  SPT_FLOW.done.success,
12919
13225
  " (HTTP ",
@@ -12922,7 +13228,7 @@ var SptFlow = ({
12922
13228
  ] }),
12923
13229
  /* @__PURE__ */ jsx9(MarkdownText, { children: SPT_FLOW.done.detail })
12924
13230
  ] }),
12925
- step === "error" && /* @__PURE__ */ jsxs6(Text8, { color: "red", children: [
13231
+ step === "error" && /* @__PURE__ */ jsxs7(Text8, { color: "red", children: [
12926
13232
  "Error: ",
12927
13233
  error
12928
13234
  ] })
@@ -12930,22 +13236,25 @@ var SptFlow = ({
12930
13236
  };
12931
13237
 
12932
13238
  // src/commands/demo/demo-runner.tsx
12933
- import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
13239
+ import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
12934
13240
  var DemoRunner = ({
12935
13241
  authRepo: authRepo2,
12936
13242
  spendRequestRepo: spendRequestRepo2,
12937
13243
  paymentMethodsResource,
13244
+ authStorage: authStorage2 = storage,
12938
13245
  paymentMethodId: preselectedPmId,
12939
13246
  onlyCard,
12940
13247
  onlySpt,
12941
13248
  onComplete
12942
13249
  }) => {
13250
+ const { exit } = useApp();
13251
+ const storage2 = authStorage2;
12943
13252
  const preselected = onlyCard ? "card" : onlySpt ? "spt" : null;
12944
13253
  const [choice, setChoice] = useState7(preselected);
12945
13254
  const [menuIndex, setMenuIndex] = useState7(0);
12946
13255
  const postAuthPhase = preselected === "spt" ? "spt-flow" : preselected ? "card-flow" : "menu";
12947
13256
  const [phase, setPhase] = useState7(
12948
- storage.isAuthenticated() ? postAuthPhase : "auth"
13257
+ storage2.isAuthenticated() ? postAuthPhase : "auth"
12949
13258
  );
12950
13259
  const [paymentMethodId, setPaymentMethodId] = useState7(
12951
13260
  preselectedPmId ?? ""
@@ -12969,29 +13278,35 @@ var DemoRunner = ({
12969
13278
  setPhase("spt-flow");
12970
13279
  }
12971
13280
  });
12972
- const onCardComplete = useCallback(
13281
+ const onCardComplete = useCallback2(
12973
13282
  (result) => {
12974
13283
  setPaymentMethodId(result.paymentMethodId);
12975
13284
  setCardSuccess(result.success);
12976
13285
  if (!runSpt) {
12977
13286
  setPhase("summary");
12978
- setTimeout(onComplete, 1500);
13287
+ setTimeout(() => {
13288
+ onComplete();
13289
+ exit();
13290
+ }, DISPLAY_DELAY_MS);
12979
13291
  } else {
12980
13292
  setPhase("card-done");
12981
13293
  }
12982
13294
  },
12983
- [runSpt, onComplete]
13295
+ [runSpt, onComplete, exit]
12984
13296
  );
12985
- const onSptComplete = useCallback(
13297
+ const onSptComplete = useCallback2(
12986
13298
  (success) => {
12987
13299
  setSptSuccess(success);
12988
13300
  setPhase("summary");
12989
- setTimeout(onComplete, 1500);
13301
+ setTimeout(() => {
13302
+ onComplete();
13303
+ exit();
13304
+ }, DISPLAY_DELAY_MS);
12990
13305
  },
12991
- [onComplete]
13306
+ [onComplete, exit]
12992
13307
  );
12993
- return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 1, children: [
12994
- /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
13308
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", gap: 1, children: [
13309
+ /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
12995
13310
  /* @__PURE__ */ jsx10(Text9, { bold: true, children: DEMO_MENU.title }),
12996
13311
  /* @__PURE__ */ jsx10(Text9, { children: DEMO_MENU.subtitle })
12997
13312
  ] }),
@@ -13000,22 +13315,23 @@ var DemoRunner = ({
13000
13315
  {
13001
13316
  authResource: authRepo2,
13002
13317
  clientName: ONBOARD.auth.clientName,
13318
+ authStorage: storage2,
13003
13319
  onComplete: () => setPhase(postAuthPhase)
13004
13320
  }
13005
13321
  ),
13006
- phase === "menu" && /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
13322
+ phase === "menu" && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
13007
13323
  /* @__PURE__ */ jsx10(Text9, { children: DEMO_MENU.question }),
13008
- /* @__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: [
13009
- /* @__PURE__ */ jsxs7(Text9, { color: "cyan", bold: true, children: [
13324
+ /* @__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__ */ jsxs8(Fragment3, { children: [
13325
+ /* @__PURE__ */ jsxs8(Text9, { color: "cyan", bold: true, children: [
13010
13326
  ">",
13011
13327
  " ",
13012
13328
  opt.label
13013
13329
  ] }),
13014
- /* @__PURE__ */ jsxs7(Text9, { color: "cyan", children: [
13330
+ /* @__PURE__ */ jsxs8(Text9, { color: "cyan", children: [
13015
13331
  " ",
13016
13332
  opt.description
13017
13333
  ] })
13018
- ] }) : /* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
13334
+ ] }) : /* @__PURE__ */ jsxs8(Text9, { dimColor: true, children: [
13019
13335
  " ",
13020
13336
  opt.label
13021
13337
  ] }) }, opt.key)) }),
@@ -13030,17 +13346,17 @@ var DemoRunner = ({
13030
13346
  onComplete: onCardComplete
13031
13347
  }
13032
13348
  ),
13033
- phase === "card-done" && /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
13349
+ phase === "card-done" && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
13034
13350
  /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u2500\u2500\u2500" }),
13035
13351
  /* @__PURE__ */ jsx10(MarkdownText, { children: DEMO_MENU.transition }),
13036
- /* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
13352
+ /* @__PURE__ */ jsxs8(Text9, { dimColor: true, children: [
13037
13353
  "\n",
13038
13354
  ">",
13039
13355
  " ",
13040
13356
  DEMO_MENU.transitionPrompt
13041
13357
  ] })
13042
13358
  ] }),
13043
- runSpt && (phase === "spt-flow" || phase === "summary") && /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
13359
+ runSpt && (phase === "spt-flow" || phase === "summary") && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
13044
13360
  runCard && /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u2500\u2500\u2500" }),
13045
13361
  /* @__PURE__ */ jsx10(
13046
13362
  SptFlow,
@@ -13052,14 +13368,14 @@ var DemoRunner = ({
13052
13368
  }
13053
13369
  )
13054
13370
  ] }),
13055
- phase === "summary" && /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
13371
+ phase === "summary" && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
13056
13372
  /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u2500\u2500\u2500" }),
13057
13373
  /* @__PURE__ */ jsx10(Text9, { bold: true, children: "Done!" }),
13058
- cardSuccess !== null && /* @__PURE__ */ jsxs7(Text9, { color: cardSuccess ? "green" : "red", children: [
13374
+ cardSuccess !== null && /* @__PURE__ */ jsxs8(Text9, { color: cardSuccess ? "green" : "red", children: [
13059
13375
  cardSuccess ? "\u2713" : "\u2717",
13060
13376
  " Virtual card flow"
13061
13377
  ] }),
13062
- sptSuccess !== null && /* @__PURE__ */ jsxs7(Text9, { color: sptSuccess ? "green" : "red", children: [
13378
+ sptSuccess !== null && /* @__PURE__ */ jsxs8(Text9, { color: sptSuccess ? "green" : "red", children: [
13063
13379
  sptSuccess ? "\u2713" : "\u2717",
13064
13380
  " Machine payment flow"
13065
13381
  ] }),
@@ -13074,7 +13390,7 @@ var demoOptions = z2.object({
13074
13390
  onlyCard: z2.boolean().default(false).describe("Run only the virtual card flow"),
13075
13391
  onlySpt: z2.boolean().default(false).describe("Run only the machine payment (SPT) flow")
13076
13392
  });
13077
- function createDemoCli(authRepo2, spendRequestRepo2, createPaymentMethodsResource) {
13393
+ function createDemoCli(authRepo2, spendRequestRepo2, createPaymentMethodsResource, authStorage2) {
13078
13394
  return Cli2.create("demo", {
13079
13395
  description: "Run an interactive demo of both Link payment flows (virtual card + machine payment)",
13080
13396
  options: demoOptions,
@@ -13087,48 +13403,71 @@ function createDemoCli(authRepo2, spendRequestRepo2, createPaymentMethodsResourc
13087
13403
  });
13088
13404
  }
13089
13405
  const paymentMethodsResource = createPaymentMethodsResource();
13090
- return new Promise((resolve) => {
13091
- const { waitUntilExit, unmount } = render2(
13092
- /* @__PURE__ */ jsx11(
13093
- DemoRunner,
13094
- {
13095
- authRepo: authRepo2,
13096
- spendRequestRepo: spendRequestRepo2,
13097
- paymentMethodsResource,
13098
- onlyCard: c.options.onlyCard,
13099
- onlySpt: c.options.onlySpt,
13100
- onComplete: () => unmount()
13406
+ return renderInteractive(
13407
+ /* @__PURE__ */ jsx11(
13408
+ DemoRunner,
13409
+ {
13410
+ authRepo: authRepo2,
13411
+ spendRequestRepo: spendRequestRepo2,
13412
+ paymentMethodsResource,
13413
+ authStorage: authStorage2,
13414
+ onlyCard: c.options.onlyCard,
13415
+ onlySpt: c.options.onlySpt,
13416
+ onComplete: () => {
13101
13417
  }
13102
- )
13103
- );
13104
- waitUntilExit().then(() => resolve({}));
13105
- });
13418
+ }
13419
+ ),
13420
+ () => ({})
13421
+ );
13106
13422
  }
13107
13423
  });
13108
13424
  }
13109
13425
 
13110
13426
  // src/commands/mpp/index.tsx
13111
13427
  import { Cli as Cli3, z as z4 } from "incur";
13112
- import { render as render3 } from "ink";
13428
+
13429
+ // src/utils/require-auth.ts
13430
+ var NOT_AUTHENTICATED_ERROR = {
13431
+ code: "NOT_AUTHENTICATED",
13432
+ message: 'Not authenticated. Run "link-cli auth login" first.',
13433
+ cta: {
13434
+ commands: [{ command: "auth login", description: "Log in to Link" }]
13435
+ }
13436
+ };
13437
+ function requireAuth(authStorage2) {
13438
+ const store = authStorage2 ?? storage;
13439
+ return (c, next) => {
13440
+ if (!store.isAuthenticated()) {
13441
+ return c.error(NOT_AUTHENTICATED_ERROR);
13442
+ }
13443
+ return next();
13444
+ };
13445
+ }
13446
+ function requireAuthGuard(c, authStorage2) {
13447
+ const store = authStorage2 ?? storage;
13448
+ if (!store.isAuthenticated()) {
13449
+ c.error(NOT_AUTHENTICATED_ERROR);
13450
+ }
13451
+ }
13113
13452
 
13114
13453
  // src/commands/mpp/decode-view.tsx
13115
13454
  import { Box as Box9, Text as Text10 } from "ink";
13116
- import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
13455
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
13117
13456
  function DecodeChallengeView({
13118
13457
  decoded
13119
13458
  }) {
13120
- return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", children: [
13459
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
13121
13460
  /* @__PURE__ */ jsx12(Text10, { color: "green", children: "\u2713 Stripe challenge decoded" }),
13122
- /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
13123
- /* @__PURE__ */ jsxs8(Text10, { children: [
13461
+ /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
13462
+ /* @__PURE__ */ jsxs9(Text10, { children: [
13124
13463
  "ID: ",
13125
13464
  /* @__PURE__ */ jsx12(Text10, { bold: true, children: decoded.id })
13126
13465
  ] }),
13127
- /* @__PURE__ */ jsxs8(Text10, { children: [
13466
+ /* @__PURE__ */ jsxs9(Text10, { children: [
13128
13467
  "Realm: ",
13129
13468
  /* @__PURE__ */ jsx12(Text10, { bold: true, children: decoded.realm })
13130
13469
  ] }),
13131
- /* @__PURE__ */ jsxs8(Text10, { children: [
13470
+ /* @__PURE__ */ jsxs9(Text10, { children: [
13132
13471
  "Network ID: ",
13133
13472
  /* @__PURE__ */ jsx12(Text10, { bold: true, children: decoded.network_id })
13134
13473
  ] }),
@@ -13156,7 +13495,7 @@ var decodeOptions = z3.object({
13156
13495
 
13157
13496
  // src/commands/mpp/index.tsx
13158
13497
  import { jsx as jsx13 } from "react/jsx-runtime";
13159
- function createMppCli(repository) {
13498
+ function createMppCli(repository, authStorage2) {
13160
13499
  const cli2 = Cli3.create("mpp", {
13161
13500
  description: "Machine payment protocol (MPP) commands"
13162
13501
  });
@@ -13168,53 +13507,36 @@ function createMppCli(repository) {
13168
13507
  options: payOptions,
13169
13508
  alias: { method: "X", data: "d", header: "H" },
13170
13509
  outputPolicy: "agent-only",
13510
+ middleware: [requireAuth(authStorage2)],
13171
13511
  async run(c) {
13172
- if (!storage.isAuthenticated()) {
13173
- return c.error({
13174
- code: "NOT_AUTHENTICATED",
13175
- message: 'Not authenticated. Run "link-cli auth login" first.',
13176
- cta: {
13177
- commands: [
13178
- { command: "auth login", description: "Log in to Link" }
13179
- ]
13180
- }
13181
- });
13182
- }
13183
13512
  const url = c.args.url;
13184
13513
  const opts = c.options;
13185
13514
  const method = opts.method;
13186
13515
  const data = opts.data;
13187
13516
  const headers = opts.header?.length ? opts.header : void 0;
13188
13517
  if (!c.agent && !c.formatExplicit) {
13189
- return new Promise((resolve) => {
13190
- const { waitUntilExit } = render3(
13191
- /* @__PURE__ */ jsx13(
13192
- MppPay,
13193
- {
13194
- url,
13195
- spendRequestId: opts.spendRequestId,
13196
- method,
13197
- data,
13198
- headers,
13199
- repository,
13200
- onComplete: () => {
13201
- }
13518
+ let capturedResult = null;
13519
+ return renderInteractive(
13520
+ /* @__PURE__ */ jsx13(
13521
+ MppPay,
13522
+ {
13523
+ url,
13524
+ spendRequestId: opts.spendRequestId,
13525
+ method,
13526
+ data,
13527
+ headers,
13528
+ repository,
13529
+ onComplete: (result) => {
13530
+ capturedResult = result;
13202
13531
  }
13203
- )
13204
- );
13205
- waitUntilExit().then(async () => {
13206
- resolve(
13207
- await runMppPay(
13208
- url,
13209
- opts.spendRequestId,
13210
- method,
13211
- data,
13212
- headers,
13213
- repository
13214
- )
13215
- );
13216
- });
13217
- });
13532
+ }
13533
+ ),
13534
+ () => {
13535
+ if (!capturedResult)
13536
+ throw new Error("Component exited without producing a result");
13537
+ return capturedResult;
13538
+ }
13539
+ );
13218
13540
  }
13219
13541
  return runMppPay(
13220
13542
  url,
@@ -13233,12 +13555,10 @@ function createMppCli(repository) {
13233
13555
  async run(c) {
13234
13556
  const decoded = decodeStripeChallenge(c.options.challenge);
13235
13557
  if (!c.agent && !c.formatExplicit) {
13236
- return new Promise((resolve) => {
13237
- const { waitUntilExit } = render3(
13238
- /* @__PURE__ */ jsx13(DecodeChallengeView, { decoded })
13239
- );
13240
- waitUntilExit().then(() => resolve(decoded));
13241
- });
13558
+ return renderInteractive(
13559
+ /* @__PURE__ */ jsx13(DecodeChallengeView, { decoded }),
13560
+ () => decoded
13561
+ );
13242
13562
  }
13243
13563
  return decoded;
13244
13564
  }
@@ -13248,29 +13568,31 @@ function createMppCli(repository) {
13248
13568
 
13249
13569
  // src/commands/onboard/index.tsx
13250
13570
  import { Cli as Cli4 } from "incur";
13251
- import { render as render4 } from "ink";
13252
13571
 
13253
13572
  // src/commands/onboard/onboard-runner.tsx
13254
- import { Box as Box10, Text as Text11, useInput as useInput5 } from "ink";
13255
- import { useEffect as useEffect7, useRef as useRef3, useState as useState8 } from "react";
13256
- import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
13573
+ import { Box as Box10, Text as Text11, useApp as useApp2, useInput as useInput5 } from "ink";
13574
+ import { useEffect as useEffect7, useRef as useRef4, useState as useState8 } from "react";
13575
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
13257
13576
  var OnboardRunner = ({
13258
13577
  authRepo: authRepo2,
13259
13578
  spendRequestRepo: spendRequestRepo2,
13260
13579
  paymentMethodsResource,
13580
+ authStorage: authStorage2 = storage,
13261
13581
  onComplete
13262
13582
  }) => {
13583
+ const { exit } = useApp2();
13584
+ const storage2 = authStorage2;
13263
13585
  const [phase, setPhase] = useState8("welcome");
13264
13586
  const [authSkipped, setAuthSkipped] = useState8(false);
13265
13587
  const [pmMissing, setPmMissing] = useState8(false);
13266
13588
  const [error, setError] = useState8("");
13267
- const enterResolver = useRef3(null);
13589
+ const enterResolver = useRef4(null);
13268
13590
  function waitForEnter() {
13269
13591
  return new Promise((resolve) => {
13270
13592
  enterResolver.current = resolve;
13271
13593
  });
13272
13594
  }
13273
- const authResolver = useRef3(null);
13595
+ const authResolver = useRef4(null);
13274
13596
  function waitForAuth() {
13275
13597
  return new Promise((resolve) => {
13276
13598
  authResolver.current = resolve;
@@ -13283,14 +13605,14 @@ var OnboardRunner = ({
13283
13605
  resolve();
13284
13606
  }
13285
13607
  });
13286
- const started = useRef3(false);
13608
+ const started = useRef4(false);
13287
13609
  useEffect7(() => {
13288
13610
  if (started.current) return;
13289
13611
  started.current = true;
13290
13612
  const run = async () => {
13291
13613
  try {
13292
13614
  setPhase("auth");
13293
- if (storage.isAuthenticated()) {
13615
+ if (storage2.isAuthenticated()) {
13294
13616
  setAuthSkipped(true);
13295
13617
  } else {
13296
13618
  setAuthSkipped(false);
@@ -13307,6 +13629,7 @@ var OnboardRunner = ({
13307
13629
  setPhase("demo");
13308
13630
  } catch (err) {
13309
13631
  setError(err.message);
13632
+ setTimeout(exit, 2e3);
13310
13633
  }
13311
13634
  };
13312
13635
  run();
@@ -13315,33 +13638,34 @@ var OnboardRunner = ({
13315
13638
  const order = ["welcome", "auth", "payment-methods", "demo"];
13316
13639
  return order.indexOf(phase) > order.indexOf(target);
13317
13640
  };
13318
- const prompt = (label = "Press [Enter] to continue") => /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
13641
+ const prompt = (label = "Press [Enter] to continue") => /* @__PURE__ */ jsxs10(Text11, { dimColor: true, children: [
13319
13642
  "\n",
13320
13643
  ">",
13321
13644
  " ",
13322
13645
  label
13323
13646
  ] });
13324
- return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", gap: 1, children: [
13325
- /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
13647
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", gap: 1, children: [
13648
+ /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
13326
13649
  /* @__PURE__ */ jsx14(Text11, { bold: true, children: ONBOARD.title }),
13327
13650
  /* @__PURE__ */ jsx14(Text11, { children: ONBOARD.subtitle })
13328
13651
  ] }),
13329
- /* @__PURE__ */ jsx14(Box10, { flexDirection: "column", children: authSkipped || pastPhase("auth") ? /* @__PURE__ */ jsxs9(Text11, { color: "green", children: [
13652
+ /* @__PURE__ */ jsx14(Box10, { flexDirection: "column", children: authSkipped || pastPhase("auth") ? /* @__PURE__ */ jsxs10(Text11, { color: "green", children: [
13330
13653
  "\u2713 ",
13331
13654
  authSkipped ? ONBOARD.auth.alreadyLoggedIn : ONBOARD.auth.authenticated
13332
- ] }) : phase === "auth" && !storage.isAuthenticated() ? /* @__PURE__ */ jsx14(
13655
+ ] }) : phase === "auth" && !storage2.isAuthenticated() ? /* @__PURE__ */ jsx14(
13333
13656
  Login,
13334
13657
  {
13335
13658
  authResource: authRepo2,
13336
13659
  clientName: ONBOARD.auth.clientName,
13660
+ authStorage: storage2,
13337
13661
  onComplete: () => authResolver.current?.()
13338
13662
  }
13339
13663
  ) : null }),
13340
- pastPhase("auth") && /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
13664
+ pastPhase("auth") && /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
13341
13665
  phase === "payment-methods" && !pmMissing && /* @__PURE__ */ jsx14(Text11, { color: "cyan", children: ONBOARD.paymentMethods.loading }),
13342
- pmMissing && /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
13666
+ pmMissing && /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
13343
13667
  /* @__PURE__ */ jsx14(Text11, { color: "yellow", children: ONBOARD.paymentMethods.missing }),
13344
- /* @__PURE__ */ jsx14(Box10, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text11, { children: [
13668
+ /* @__PURE__ */ jsx14(Box10, { marginTop: 1, children: /* @__PURE__ */ jsxs10(Text11, { children: [
13345
13669
  "Visit",
13346
13670
  " ",
13347
13671
  /* @__PURE__ */ jsx14(Text11, { bold: true, color: "cyan", children: "app.link.com/wallet" }),
@@ -13352,7 +13676,7 @@ var OnboardRunner = ({
13352
13676
  ] }),
13353
13677
  pastPhase("payment-methods") && /* @__PURE__ */ jsx14(Text11, { color: "green", children: "\u2713 Payment method found" })
13354
13678
  ] }),
13355
- phase === "demo" && /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
13679
+ phase === "demo" && /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
13356
13680
  /* @__PURE__ */ jsx14(Text11, { dimColor: true, children: "\u2500\u2500\u2500" }),
13357
13681
  /* @__PURE__ */ jsx14(
13358
13682
  DemoRunner,
@@ -13360,11 +13684,12 @@ var OnboardRunner = ({
13360
13684
  authRepo: authRepo2,
13361
13685
  spendRequestRepo: spendRequestRepo2,
13362
13686
  paymentMethodsResource,
13687
+ authStorage: storage2,
13363
13688
  onComplete
13364
13689
  }
13365
13690
  )
13366
13691
  ] }),
13367
- error && /* @__PURE__ */ jsxs9(Text11, { color: "red", children: [
13692
+ error && /* @__PURE__ */ jsxs10(Text11, { color: "red", children: [
13368
13693
  "Error: ",
13369
13694
  error
13370
13695
  ] })
@@ -13373,7 +13698,7 @@ var OnboardRunner = ({
13373
13698
 
13374
13699
  // src/commands/onboard/index.tsx
13375
13700
  import { jsx as jsx15 } from "react/jsx-runtime";
13376
- function createOnboardCli(authRepo2, spendRequestRepo2, createPaymentMethodsResource) {
13701
+ function createOnboardCli(authRepo2, spendRequestRepo2, createPaymentMethodsResource, authStorage2) {
13377
13702
  return Cli4.create("onboard", {
13378
13703
  description: "Guided setup: authenticate, verify payment methods, and demo both payment flows",
13379
13704
  outputPolicy: "agent-only",
@@ -13385,43 +13710,42 @@ function createOnboardCli(authRepo2, spendRequestRepo2, createPaymentMethodsReso
13385
13710
  });
13386
13711
  }
13387
13712
  const paymentMethodsResource = createPaymentMethodsResource();
13388
- return new Promise((resolve) => {
13389
- const { waitUntilExit, unmount } = render4(
13390
- /* @__PURE__ */ jsx15(
13391
- OnboardRunner,
13392
- {
13393
- authRepo: authRepo2,
13394
- spendRequestRepo: spendRequestRepo2,
13395
- paymentMethodsResource,
13396
- onComplete: () => unmount()
13713
+ return renderInteractive(
13714
+ /* @__PURE__ */ jsx15(
13715
+ OnboardRunner,
13716
+ {
13717
+ authRepo: authRepo2,
13718
+ spendRequestRepo: spendRequestRepo2,
13719
+ paymentMethodsResource,
13720
+ authStorage: authStorage2,
13721
+ onComplete: () => {
13397
13722
  }
13398
- )
13399
- );
13400
- waitUntilExit().then(() => resolve({}));
13401
- });
13723
+ }
13724
+ ),
13725
+ () => ({})
13726
+ );
13402
13727
  }
13403
13728
  });
13404
13729
  }
13405
13730
 
13406
13731
  // src/commands/payment-methods/index.tsx
13407
13732
  import { Cli as Cli5 } from "incur";
13408
- import { render as render5 } from "ink";
13409
13733
 
13410
13734
  // src/commands/payment-methods/add.tsx
13411
- import { Box as Box11, Text as Text12, useApp, useInput as useInput6 } from "ink";
13412
- import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
13735
+ import { Box as Box11, Text as Text12, useApp as useApp3, useInput as useInput6 } from "ink";
13736
+ import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
13413
13737
  var WALLET_URL = "https://app.link.com/wallet";
13414
13738
  var AddPaymentMethod = () => {
13415
- const { exit } = useApp();
13739
+ const { exit } = useApp3();
13416
13740
  useInput6((_input, key) => {
13417
13741
  if (key.return) {
13418
13742
  openUrl(WALLET_URL);
13419
13743
  exit();
13420
13744
  }
13421
13745
  });
13422
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingY: 1, children: [
13746
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingY: 1, children: [
13423
13747
  /* @__PURE__ */ jsx16(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text12, { bold: true, children: "Add Payment Method" }) }),
13424
- /* @__PURE__ */ jsxs10(
13748
+ /* @__PURE__ */ jsxs11(
13425
13749
  Box11,
13426
13750
  {
13427
13751
  flexDirection: "column",
@@ -13430,7 +13754,7 @@ var AddPaymentMethod = () => {
13430
13754
  paddingX: 2,
13431
13755
  paddingY: 1,
13432
13756
  children: [
13433
- /* @__PURE__ */ jsxs10(Text12, { children: [
13757
+ /* @__PURE__ */ jsxs11(Text12, { children: [
13434
13758
  "Open:",
13435
13759
  " ",
13436
13760
  /* @__PURE__ */ jsx16(Text12, { bold: true, color: "cyan", children: WALLET_URL })
@@ -13444,55 +13768,37 @@ var AddPaymentMethod = () => {
13444
13768
 
13445
13769
  // src/commands/payment-methods/list.tsx
13446
13770
  import { Box as Box12, Text as Text13 } from "ink";
13447
- import Spinner3 from "ink-spinner";
13448
- import { useEffect as useEffect8, useState as useState9 } from "react";
13449
- import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
13771
+ import Spinner4 from "ink-spinner";
13772
+ import { useCallback as useCallback3 } from "react";
13773
+ import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
13450
13774
  var PaymentMethodsList = ({
13451
13775
  resource,
13452
13776
  onComplete
13453
13777
  }) => {
13454
- const [status, setStatus] = useState9(
13455
- "loading"
13456
- );
13457
- const [methods, setMethods] = useState9([]);
13458
- const [error, setError] = useState9("");
13459
- useEffect8(() => {
13460
- const fetch2 = async () => {
13461
- try {
13462
- const result = await resource.listPaymentMethods();
13463
- setMethods(result);
13464
- setStatus("success");
13465
- setTimeout(onComplete, 1500);
13466
- } catch (err) {
13467
- setError(err.message);
13468
- setStatus("error");
13469
- setTimeout(onComplete, 1500);
13470
- }
13471
- };
13472
- fetch2();
13473
- }, [resource, onComplete]);
13778
+ const action = useCallback3(() => resource.listPaymentMethods(), [resource]);
13779
+ const { status, data: methods, error } = useAsyncAction(action, onComplete);
13474
13780
  if (status === "loading") {
13475
- return /* @__PURE__ */ jsx17(Box12, { children: /* @__PURE__ */ jsxs11(Text13, { color: "cyan", children: [
13476
- /* @__PURE__ */ jsx17(Spinner3, { type: "dots" }),
13781
+ return /* @__PURE__ */ jsx17(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
13782
+ /* @__PURE__ */ jsx17(Spinner4, { type: "dots" }),
13477
13783
  " Loading payment methods..."
13478
13784
  ] }) });
13479
13785
  }
13480
13786
  if (status === "error") {
13481
- return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
13787
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
13482
13788
  /* @__PURE__ */ jsx17(Text13, { color: "red", children: "\u2717 Failed to load payment methods" }),
13483
13789
  /* @__PURE__ */ jsx17(Text13, { color: "red", children: error })
13484
13790
  ] });
13485
13791
  }
13486
- if (methods.length === 0) {
13792
+ if (!methods || methods.length === 0) {
13487
13793
  return /* @__PURE__ */ jsx17(Box12, { children: /* @__PURE__ */ jsx17(Text13, { dimColor: true, children: "No payment methods found" }) });
13488
13794
  }
13489
- return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
13795
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
13490
13796
  /* @__PURE__ */ jsx17(Text13, { bold: true, children: "Payment Methods" }),
13491
13797
  /* @__PURE__ */ jsx17(Box12, { flexDirection: "column", marginTop: 1, children: methods.map((pm) => {
13492
13798
  const label = pm.card_details?.brand ?? pm.bank_account_details?.bank_name ?? "Bank account";
13493
13799
  const last4 = pm.card_details?.last4 ?? pm.bank_account_details?.last4;
13494
- const suffix = [pm.nickname ? `(${pm.nickname})` : ""].filter(Boolean).join(" ");
13495
- return /* @__PURE__ */ jsx17(Box12, { paddingX: 2, children: /* @__PURE__ */ jsxs11(Text13, { children: [
13800
+ const suffix = pm.nickname ? `(${pm.nickname})` : "";
13801
+ return /* @__PURE__ */ jsx17(Box12, { paddingX: 2, children: /* @__PURE__ */ jsxs12(Text13, { children: [
13496
13802
  /* @__PURE__ */ jsx17(Text13, { dimColor: true, children: pm.id }),
13497
13803
  " ",
13498
13804
  label,
@@ -13507,36 +13813,22 @@ var PaymentMethodsList = ({
13507
13813
 
13508
13814
  // src/commands/payment-methods/index.tsx
13509
13815
  import { jsx as jsx18 } from "react/jsx-runtime";
13510
- function createPaymentMethodsCli(createResource) {
13816
+ function createPaymentMethodsCli(createResource, authStorage2) {
13511
13817
  const cli2 = Cli5.create("payment-methods", {
13512
13818
  description: "Payment methods management commands"
13513
13819
  });
13514
13820
  cli2.command("list", {
13515
13821
  description: "List all payment methods on your account",
13516
13822
  outputPolicy: "agent-only",
13823
+ middleware: [requireAuth(authStorage2)],
13517
13824
  async run(c) {
13518
- if (!storage.isAuthenticated()) {
13519
- return c.error({
13520
- code: "NOT_AUTHENTICATED",
13521
- message: 'Not authenticated. Run "link-cli auth login" first.',
13522
- cta: {
13523
- commands: [
13524
- { command: "auth login", description: "Log in to Link" }
13525
- ]
13526
- }
13527
- });
13528
- }
13529
13825
  const resource = createResource();
13530
13826
  if (!c.agent && !c.formatExplicit) {
13531
- return new Promise((resolve) => {
13532
- const { waitUntilExit } = render5(
13533
- /* @__PURE__ */ jsx18(PaymentMethodsList, { resource, onComplete: () => {
13534
- } })
13535
- );
13536
- waitUntilExit().then(async () => {
13537
- resolve(await resource.listPaymentMethods());
13538
- });
13539
- });
13827
+ return renderInteractive(
13828
+ /* @__PURE__ */ jsx18(PaymentMethodsList, { resource, onComplete: () => {
13829
+ } }),
13830
+ () => resource.listPaymentMethods()
13831
+ );
13540
13832
  }
13541
13833
  return resource.listPaymentMethods();
13542
13834
  }
@@ -13544,23 +13836,12 @@ function createPaymentMethodsCli(createResource) {
13544
13836
  cli2.command("add", {
13545
13837
  description: "Open the Link wallet to add a new payment method",
13546
13838
  outputPolicy: "agent-only",
13839
+ middleware: [requireAuth(authStorage2)],
13547
13840
  async run(c) {
13548
- if (!storage.isAuthenticated()) {
13549
- return c.error({
13550
- code: "NOT_AUTHENTICATED",
13551
- message: 'Not authenticated. Run "link-cli auth login" first.',
13552
- cta: {
13553
- commands: [
13554
- { command: "auth login", description: "Log in to Link" }
13555
- ]
13556
- }
13557
- });
13558
- }
13559
13841
  if (!c.agent && !c.formatExplicit) {
13560
- return new Promise((resolve) => {
13561
- const { waitUntilExit } = render5(/* @__PURE__ */ jsx18(AddPaymentMethod, {}));
13562
- waitUntilExit().then(() => resolve({ url: WALLET_URL }));
13563
- });
13842
+ return renderInteractive(/* @__PURE__ */ jsx18(AddPaymentMethod, {}), () => ({
13843
+ url: WALLET_URL
13844
+ }));
13564
13845
  }
13565
13846
  return { url: WALLET_URL };
13566
13847
  }
@@ -13568,15 +13849,131 @@ function createPaymentMethodsCli(createResource) {
13568
13849
  return cli2;
13569
13850
  }
13570
13851
 
13852
+ // src/commands/shipping-address/index.tsx
13853
+ import { Cli as Cli6 } from "incur";
13854
+
13855
+ // src/commands/shipping-address/list.tsx
13856
+ import { Box as Box13, Text as Text14 } from "ink";
13857
+ import Spinner5 from "ink-spinner";
13858
+ import { useCallback as useCallback4 } from "react";
13859
+ import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
13860
+ function formatStreetLine(address) {
13861
+ const parts = [address.line_1, address.line_2].filter(Boolean);
13862
+ return parts.length > 0 ? parts.join(", ") : null;
13863
+ }
13864
+ function formatLocalityLine(address) {
13865
+ const locality = [
13866
+ address.dependent_locality,
13867
+ address.locality,
13868
+ address.administrative_area
13869
+ ].filter(Boolean).join(", ");
13870
+ const postal = [address.postal_code, address.sorting_code].filter(Boolean).join(" ");
13871
+ const localityPostal = locality && postal ? `${locality} ${postal}` : locality || postal || null;
13872
+ if (localityPostal && address.country_code) {
13873
+ return `${localityPostal}, ${address.country_code}`;
13874
+ }
13875
+ return localityPostal || address.country_code || null;
13876
+ }
13877
+ function formatAddressLines(addressRecord) {
13878
+ if (!addressRecord.address) {
13879
+ return ["Address details unavailable"];
13880
+ }
13881
+ const address = addressRecord.address;
13882
+ const lines = [formatStreetLine(address), formatLocalityLine(address)].filter(
13883
+ (line) => Boolean(line)
13884
+ );
13885
+ return lines.length > 0 ? lines : ["Address details unavailable"];
13886
+ }
13887
+ var ShippingAddressList = ({
13888
+ resource,
13889
+ onComplete
13890
+ }) => {
13891
+ const action = useCallback4(
13892
+ () => resource.listShippingAddresses(),
13893
+ [resource]
13894
+ );
13895
+ const {
13896
+ status,
13897
+ data: shippingAddresses,
13898
+ error
13899
+ } = useAsyncAction(action, onComplete);
13900
+ if (status === "loading") {
13901
+ return /* @__PURE__ */ jsx19(Box13, { children: /* @__PURE__ */ jsxs13(Text14, { color: "cyan", children: [
13902
+ /* @__PURE__ */ jsx19(Spinner5, { type: "dots" }),
13903
+ " Loading shipping addresses..."
13904
+ ] }) });
13905
+ }
13906
+ if (status === "error") {
13907
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
13908
+ /* @__PURE__ */ jsx19(Text14, { color: "red", children: "\u2717 Failed to load shipping addresses" }),
13909
+ /* @__PURE__ */ jsx19(Text14, { color: "red", children: error })
13910
+ ] });
13911
+ }
13912
+ if (!shippingAddresses || shippingAddresses.length === 0) {
13913
+ return /* @__PURE__ */ jsx19(Box13, { children: /* @__PURE__ */ jsx19(Text14, { dimColor: true, children: "No shipping addresses found" }) });
13914
+ }
13915
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
13916
+ /* @__PURE__ */ jsx19(Text14, { bold: true, children: "Shipping Addresses" }),
13917
+ /* @__PURE__ */ jsx19(Box13, { flexDirection: "column", marginTop: 1, children: shippingAddresses.map((shippingAddress) => {
13918
+ const addressName = shippingAddress.address?.name;
13919
+ const nickname = shippingAddress.nickname ? ` (${shippingAddress.nickname})` : "";
13920
+ return /* @__PURE__ */ jsxs13(
13921
+ Box13,
13922
+ {
13923
+ flexDirection: "column",
13924
+ paddingX: 2,
13925
+ marginBottom: 1,
13926
+ children: [
13927
+ /* @__PURE__ */ jsxs13(Text14, { children: [
13928
+ /* @__PURE__ */ jsx19(Text14, { dimColor: true, children: shippingAddress.id }),
13929
+ nickname,
13930
+ shippingAddress.is_default ? /* @__PURE__ */ jsx19(Text14, { color: "green", children: " (default)" }) : null
13931
+ ] }),
13932
+ /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginTop: 1, children: [
13933
+ addressName ? /* @__PURE__ */ jsx19(Text14, { bold: true, children: addressName }) : null,
13934
+ formatAddressLines(shippingAddress).map((line) => /* @__PURE__ */ jsx19(Text14, { children: line }, `${shippingAddress.id}:${line}`))
13935
+ ] })
13936
+ ]
13937
+ },
13938
+ shippingAddress.id
13939
+ );
13940
+ }) })
13941
+ ] });
13942
+ };
13943
+
13944
+ // src/commands/shipping-address/index.tsx
13945
+ import { jsx as jsx20 } from "react/jsx-runtime";
13946
+ function createShippingAddressCli(createResource, authStorage2) {
13947
+ const cli2 = Cli6.create("shipping-address", {
13948
+ description: "Shipping address management commands"
13949
+ });
13950
+ cli2.command("list", {
13951
+ description: "List all shipping addresses on your account",
13952
+ outputPolicy: "agent-only",
13953
+ middleware: [requireAuth(authStorage2)],
13954
+ async run(c) {
13955
+ const resource = createResource();
13956
+ if (!c.agent && !c.formatExplicit) {
13957
+ return renderInteractive(
13958
+ /* @__PURE__ */ jsx20(ShippingAddressList, { resource, onComplete: () => {
13959
+ } }),
13960
+ () => resource.listShippingAddresses()
13961
+ );
13962
+ }
13963
+ return resource.listShippingAddresses();
13964
+ }
13965
+ });
13966
+ return cli2;
13967
+ }
13968
+
13571
13969
  // src/commands/spend-request/index.tsx
13572
- import { Cli as Cli6, z as z7 } from "incur";
13573
- import { render as render6 } from "ink";
13970
+ import { Cli as Cli7, z as z7 } from "incur";
13574
13971
 
13575
13972
  // src/utils/credential-output.ts
13576
13973
  import fs4 from "fs/promises";
13577
- import path5 from "path";
13974
+ import path6 from "path";
13578
13975
  async function writeCredentialFile(filePath, data, force) {
13579
- const resolved = path5.resolve(filePath);
13976
+ const resolved = path6.resolve(filePath);
13580
13977
  if (!force) {
13581
13978
  try {
13582
13979
  await fs4.access(resolved);
@@ -13655,21 +14052,59 @@ function parseTotalFlag(raw) {
13655
14052
  }
13656
14053
  }
13657
14054
 
13658
- // src/commands/spend-request/create.tsx
14055
+ // src/commands/spend-request/cancel.tsx
13659
14056
  import { Box as Box14, Text as Text15 } from "ink";
13660
- import Spinner5 from "ink-spinner";
13661
- import { useCallback as useCallback2, useEffect as useEffect10, useState as useState10 } from "react";
14057
+ import Spinner6 from "ink-spinner";
14058
+ import { useCallback as useCallback5 } from "react";
14059
+ import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
14060
+ var CancelSpendRequest = ({
14061
+ repository,
14062
+ id,
14063
+ onComplete
14064
+ }) => {
14065
+ const action = useCallback5(
14066
+ () => repository.cancelSpendRequest(id),
14067
+ [repository, id]
14068
+ );
14069
+ const { status, data: request, error } = useAsyncAction(action, onComplete);
14070
+ if (status === "loading") {
14071
+ return /* @__PURE__ */ jsx21(Box14, { children: /* @__PURE__ */ jsxs14(Text15, { color: "cyan", children: [
14072
+ /* @__PURE__ */ jsx21(Spinner6, { type: "dots" }),
14073
+ " Canceling spend request ",
14074
+ id,
14075
+ "..."
14076
+ ] }) });
14077
+ }
14078
+ if (status === "error") {
14079
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
14080
+ /* @__PURE__ */ jsx21(Text15, { color: "red", children: "\u2717 Failed to cancel spend request" }),
14081
+ /* @__PURE__ */ jsx21(Text15, { color: "red", children: error })
14082
+ ] });
14083
+ }
14084
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
14085
+ /* @__PURE__ */ jsx21(Text15, { color: "green", children: "\u2713 Spend request canceled" }),
14086
+ /* @__PURE__ */ jsx21(Box14, { flexDirection: "column", marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsxs14(Text15, { children: [
14087
+ "ID: ",
14088
+ /* @__PURE__ */ jsx21(Text15, { bold: true, children: request?.id })
14089
+ ] }) })
14090
+ ] });
14091
+ };
14092
+
14093
+ // src/commands/spend-request/create.tsx
14094
+ import { Box as Box16, Text as Text17 } from "ink";
14095
+ import Spinner8 from "ink-spinner";
14096
+ import { useCallback as useCallback6, useEffect as useEffect9, useState as useState9 } from "react";
13662
14097
 
13663
14098
  // src/commands/spend-request/approval-waiting-view.tsx
13664
- import { Box as Box13, Text as Text14 } from "ink";
13665
- import Spinner4 from "ink-spinner";
13666
- import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
14099
+ import { Box as Box15, Text as Text16 } from "ink";
14100
+ import Spinner7 from "ink-spinner";
14101
+ import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
13667
14102
  var ApprovalWaitingView = ({
13668
14103
  status,
13669
14104
  approvalUrl
13670
- }) => /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingY: 1, children: [
13671
- /* @__PURE__ */ jsxs12(
13672
- Box13,
14105
+ }) => /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", paddingY: 1, children: [
14106
+ /* @__PURE__ */ jsxs15(
14107
+ Box15,
13673
14108
  {
13674
14109
  flexDirection: "column",
13675
14110
  borderStyle: "round",
@@ -13677,25 +14112,25 @@ var ApprovalWaitingView = ({
13677
14112
  paddingX: 2,
13678
14113
  paddingY: 1,
13679
14114
  children: [
13680
- /* @__PURE__ */ jsxs12(Text14, { children: [
14115
+ /* @__PURE__ */ jsxs15(Text16, { children: [
13681
14116
  "Approve at:",
13682
14117
  " ",
13683
- /* @__PURE__ */ jsx19(Text14, { bold: true, color: "cyan", children: approvalUrl })
14118
+ /* @__PURE__ */ jsx22(Text16, { bold: true, color: "cyan", children: approvalUrl })
13684
14119
  ] }),
13685
- /* @__PURE__ */ jsx19(Text14, { dimColor: true, children: "Press Enter to open in browser" })
14120
+ /* @__PURE__ */ jsx22(Text16, { dimColor: true, children: "Press Enter to open in browser" })
13686
14121
  ]
13687
14122
  }
13688
14123
  ),
13689
- /* @__PURE__ */ jsx19(AppDownloadQrCodes, {}),
13690
- /* @__PURE__ */ jsx19(Box13, { marginTop: 1, children: status === "polling" ? /* @__PURE__ */ jsxs12(Text14, { color: "cyan", children: [
13691
- /* @__PURE__ */ jsx19(Spinner4, { type: "dots" }),
14124
+ /* @__PURE__ */ jsx22(AppDownloadQrCodes, {}),
14125
+ /* @__PURE__ */ jsx22(Box15, { marginTop: 1, children: status === "polling" ? /* @__PURE__ */ jsxs15(Text16, { color: "cyan", children: [
14126
+ /* @__PURE__ */ jsx22(Spinner7, { type: "dots" }),
13692
14127
  " Waiting for approval..."
13693
- ] }) : /* @__PURE__ */ jsx19(Text14, { dimColor: true, children: "Waiting..." }) })
14128
+ ] }) : /* @__PURE__ */ jsx22(Text16, { dimColor: true, children: "Waiting..." }) })
13694
14129
  ] });
13695
14130
 
13696
14131
  // src/commands/spend-request/use-approval-polling.ts
13697
14132
  import { useInput as useInput7 } from "ink";
13698
- import { useEffect as useEffect9 } from "react";
14133
+ import { useEffect as useEffect8 } from "react";
13699
14134
  function useApprovalPolling({
13700
14135
  status,
13701
14136
  setStatus,
@@ -13713,22 +14148,29 @@ function useApprovalPolling({
13713
14148
  },
13714
14149
  { isActive: isWaiting }
13715
14150
  );
13716
- useEffect9(() => {
14151
+ useEffect8(() => {
13717
14152
  if (status !== "waiting") return;
13718
14153
  const timeout = setTimeout(() => setStatus("polling"), 1e3);
13719
14154
  return () => clearTimeout(timeout);
13720
14155
  }, [status, setStatus]);
13721
- useEffect9(() => {
14156
+ useEffect8(() => {
13722
14157
  if (status !== "polling" || !requestId) return;
13723
14158
  let cancelled = false;
13724
14159
  const poll = async () => {
13725
14160
  try {
13726
14161
  const final = await pollUntilApproved(repository, requestId);
13727
- if (!cancelled) {
13728
- onSuccess(final);
13729
- setStatus("success");
13730
- setTimeout(() => onComplete(final), 1e3);
14162
+ if (cancelled) return;
14163
+ if (final.status !== "approved") {
14164
+ onError(
14165
+ `Spend request did not reach approved (status: ${final.status})`
14166
+ );
14167
+ setStatus("error");
14168
+ setTimeout(() => onComplete(final), DISPLAY_DELAY_MS);
14169
+ return;
13731
14170
  }
14171
+ onSuccess(final);
14172
+ setStatus("success");
14173
+ setTimeout(() => onComplete(final), DISPLAY_DELAY_MS);
13732
14174
  } catch (err) {
13733
14175
  if (!cancelled) {
13734
14176
  onError(err.message);
@@ -13752,7 +14194,7 @@ function useApprovalPolling({
13752
14194
  }
13753
14195
 
13754
14196
  // src/commands/spend-request/create.tsx
13755
- import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
14197
+ import { Fragment as Fragment4, jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
13756
14198
  var CreateSpendRequest = ({
13757
14199
  repository,
13758
14200
  params,
@@ -13761,17 +14203,17 @@ var CreateSpendRequest = ({
13761
14203
  force,
13762
14204
  onComplete
13763
14205
  }) => {
13764
- const [status, setStatus] = useState10("creating");
13765
- const [request, setRequest] = useState10(null);
13766
- const [error, setError] = useState10("");
13767
- const [outputFilePath, setOutputFilePath] = useState10(null);
13768
- const [fileError, setFileError] = useState10("");
14206
+ const [status, setStatus] = useState9("creating");
14207
+ const [request, setRequest] = useState9(null);
14208
+ const [error, setError] = useState9("");
14209
+ const [outputFilePath, setOutputFilePath] = useState9(null);
14210
+ const [fileError, setFileError] = useState9("");
13769
14211
  const approvalUrl = request?.approval_url ?? "";
13770
- const onSuccess = useCallback2(
14212
+ const onSuccess = useCallback6(
13771
14213
  (result) => setRequest(result),
13772
14214
  []
13773
14215
  );
13774
- const onError = useCallback2((msg) => setError(msg), []);
14216
+ const onError = useCallback6((msg) => setError(msg), []);
13775
14217
  useApprovalPolling({
13776
14218
  status,
13777
14219
  setStatus,
@@ -13782,7 +14224,7 @@ var CreateSpendRequest = ({
13782
14224
  onSuccess,
13783
14225
  onError
13784
14226
  });
13785
- useEffect10(() => {
14227
+ useEffect9(() => {
13786
14228
  const create = async () => {
13787
14229
  try {
13788
14230
  const result = await repository.createSpendRequest(params);
@@ -13791,17 +14233,17 @@ var CreateSpendRequest = ({
13791
14233
  setStatus("waiting");
13792
14234
  } else {
13793
14235
  setStatus("success");
13794
- setTimeout(() => onComplete(result), 1500);
14236
+ setTimeout(() => onComplete(result), DISPLAY_DELAY_MS);
13795
14237
  }
13796
14238
  } catch (err) {
13797
14239
  setError(err.message);
13798
14240
  setStatus("error");
13799
- setTimeout(() => onComplete(null), 1500);
14241
+ setTimeout(() => onComplete(null), DISPLAY_DELAY_MS);
13800
14242
  }
13801
14243
  };
13802
14244
  create();
13803
14245
  }, [repository, params, requestApproval, onComplete]);
13804
- useEffect10(() => {
14246
+ useEffect9(() => {
13805
14247
  if (status !== "success" || !outputFile || !request?.card) return;
13806
14248
  const fileData = {
13807
14249
  spend_request_id: request.id,
@@ -13811,104 +14253,104 @@ var CreateSpendRequest = ({
13811
14253
  created_at: request.created_at,
13812
14254
  card: request.card
13813
14255
  };
13814
- writeCredentialFile(outputFile, fileData, force ?? false).then((path6) => setOutputFilePath(path6)).catch((err) => setFileError(err.message));
14256
+ writeCredentialFile(outputFile, fileData, force ?? false).then((path7) => setOutputFilePath(path7)).catch((err) => setFileError(err.message));
13815
14257
  }, [status, outputFile, force, request]);
13816
14258
  if (status === "creating") {
13817
- return /* @__PURE__ */ jsx20(Box14, { children: /* @__PURE__ */ jsxs13(Text15, { color: "cyan", children: [
13818
- /* @__PURE__ */ jsx20(Spinner5, { type: "dots" }),
14259
+ return /* @__PURE__ */ jsx23(Box16, { children: /* @__PURE__ */ jsxs16(Text17, { color: "cyan", children: [
14260
+ /* @__PURE__ */ jsx23(Spinner8, { type: "dots" }),
13819
14261
  " Creating spend request..."
13820
14262
  ] }) });
13821
14263
  }
13822
14264
  if (status === "error") {
13823
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
13824
- /* @__PURE__ */ jsx20(Text15, { color: "red", children: "\u2717 Failed to create spend request" }),
13825
- /* @__PURE__ */ jsx20(Text15, { color: "red", children: error })
14265
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
14266
+ /* @__PURE__ */ jsx23(Text17, { color: "red", children: "\u2717 Failed to create spend request" }),
14267
+ /* @__PURE__ */ jsx23(Text17, { color: "red", children: error })
13826
14268
  ] });
13827
14269
  }
13828
14270
  if (status === "success") {
13829
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
13830
- /* @__PURE__ */ jsx20(Text15, { color: "green", children: "\u2713 Spend request created" }),
13831
- /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
13832
- /* @__PURE__ */ jsxs13(Text15, { children: [
14271
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
14272
+ /* @__PURE__ */ jsx23(Text17, { color: "green", children: "\u2713 Spend request created" }),
14273
+ /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14274
+ /* @__PURE__ */ jsxs16(Text17, { children: [
13833
14275
  "ID: ",
13834
- /* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.id })
14276
+ /* @__PURE__ */ jsx23(Text17, { bold: true, children: request?.id })
13835
14277
  ] }),
13836
- /* @__PURE__ */ jsxs13(Text15, { children: [
14278
+ /* @__PURE__ */ jsxs16(Text17, { children: [
13837
14279
  "Status: ",
13838
- /* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.status })
14280
+ /* @__PURE__ */ jsx23(Text17, { bold: true, children: request?.status })
13839
14281
  ] }),
13840
- /* @__PURE__ */ jsxs13(Text15, { children: [
14282
+ /* @__PURE__ */ jsxs16(Text17, { children: [
13841
14283
  "Amount:",
13842
14284
  " ",
13843
- /* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.amount != null ? `${request.amount} ${request.currency?.toUpperCase() ?? ""}`.trim() : "N/A" })
14285
+ /* @__PURE__ */ jsx23(Text17, { bold: true, children: request?.amount != null ? `${request.amount} ${request.currency?.toUpperCase() ?? ""}`.trim() : "N/A" })
13844
14286
  ] }),
13845
- /* @__PURE__ */ jsxs13(Text15, { children: [
14287
+ /* @__PURE__ */ jsxs16(Text17, { children: [
13846
14288
  "Merchant: ",
13847
- /* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.merchant_name })
14289
+ /* @__PURE__ */ jsx23(Text17, { bold: true, children: request?.merchant_name })
13848
14290
  ] }),
13849
- /* @__PURE__ */ jsxs13(Text15, { children: [
14291
+ /* @__PURE__ */ jsxs16(Text17, { children: [
13850
14292
  "Line Items:",
13851
14293
  " ",
13852
- /* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") || "N/A" })
14294
+ /* @__PURE__ */ jsx23(Text17, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") || "N/A" })
13853
14295
  ] }),
13854
- request?.credential_type === "shared_payment_token" && request.shared_payment_token && /* @__PURE__ */ jsxs13(Text15, { children: [
14296
+ request?.credential_type === "shared_payment_token" && request.shared_payment_token && /* @__PURE__ */ jsxs16(Text17, { children: [
13855
14297
  "Token: ",
13856
- /* @__PURE__ */ jsx20(Text15, { bold: true, children: request.shared_payment_token.id })
14298
+ /* @__PURE__ */ jsx23(Text17, { bold: true, children: request.shared_payment_token.id })
13857
14299
  ] })
13858
14300
  ] }),
13859
- request?.card && !outputFile && /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginTop: 1, children: [
13860
- /* @__PURE__ */ jsx20(Text15, { bold: true, children: "Card Details:" }),
13861
- /* @__PURE__ */ jsxs13(Text15, { children: [
14301
+ request?.card && !outputFile && /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", marginTop: 1, children: [
14302
+ /* @__PURE__ */ jsx23(Text17, { bold: true, children: "Card Details:" }),
14303
+ /* @__PURE__ */ jsxs16(Text17, { children: [
13862
14304
  " ",
13863
14305
  "Number: ",
13864
- /* @__PURE__ */ jsx20(Text15, { bold: true, children: request.card.number })
14306
+ /* @__PURE__ */ jsx23(Text17, { bold: true, children: request.card.number })
13865
14307
  ] }),
13866
- /* @__PURE__ */ jsxs13(Text15, { children: [
14308
+ /* @__PURE__ */ jsxs16(Text17, { children: [
13867
14309
  " ",
13868
14310
  "Brand: ",
13869
- /* @__PURE__ */ jsx20(Text15, { bold: true, children: request.card.brand })
14311
+ /* @__PURE__ */ jsx23(Text17, { bold: true, children: request.card.brand })
13870
14312
  ] }),
13871
- /* @__PURE__ */ jsxs13(Text15, { children: [
14313
+ /* @__PURE__ */ jsxs16(Text17, { children: [
13872
14314
  " ",
13873
14315
  "Expiry:",
13874
14316
  " ",
13875
- /* @__PURE__ */ jsxs13(Text15, { bold: true, children: [
14317
+ /* @__PURE__ */ jsxs16(Text17, { bold: true, children: [
13876
14318
  String(request.card.exp_month).padStart(2, "0"),
13877
14319
  "/",
13878
14320
  request.card.exp_year
13879
14321
  ] })
13880
14322
  ] }),
13881
- request.card.cvc && /* @__PURE__ */ jsxs13(Text15, { children: [
14323
+ request.card.cvc && /* @__PURE__ */ jsxs16(Text17, { children: [
13882
14324
  " ",
13883
14325
  "CVC: ",
13884
- /* @__PURE__ */ jsx20(Text15, { bold: true, children: request.card.cvc })
14326
+ /* @__PURE__ */ jsx23(Text17, { bold: true, children: request.card.cvc })
13885
14327
  ] }),
13886
- request.card.valid_until && /* @__PURE__ */ jsxs13(Text15, { children: [
14328
+ request.card.valid_until && /* @__PURE__ */ jsxs16(Text17, { children: [
13887
14329
  " ",
13888
14330
  "Valid Until: ",
13889
- /* @__PURE__ */ jsx20(Text15, { bold: true, children: request.card.valid_until })
14331
+ /* @__PURE__ */ jsx23(Text17, { bold: true, children: request.card.valid_until })
13890
14332
  ] })
13891
14333
  ] }),
13892
- request?.card && outputFile && /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginTop: 1, children: [
13893
- outputFilePath && /* @__PURE__ */ jsxs13(Text15, { color: "green", children: [
14334
+ request?.card && outputFile && /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", marginTop: 1, children: [
14335
+ outputFilePath && /* @__PURE__ */ jsxs16(Text17, { color: "green", children: [
13894
14336
  "Card credentials written to ",
13895
- /* @__PURE__ */ jsx20(Text15, { bold: true, children: outputFilePath })
14337
+ /* @__PURE__ */ jsx23(Text17, { bold: true, children: outputFilePath })
13896
14338
  ] }),
13897
- fileError && /* @__PURE__ */ jsxs13(Text15, { color: "red", children: [
14339
+ fileError && /* @__PURE__ */ jsxs16(Text17, { color: "red", children: [
13898
14340
  "Failed to write card file: ",
13899
14341
  fileError
13900
14342
  ] })
13901
14343
  ] }),
13902
- /* @__PURE__ */ jsx20(AppDownloadQrCodes, {})
14344
+ /* @__PURE__ */ jsx23(AppDownloadQrCodes, {})
13903
14345
  ] });
13904
14346
  }
13905
- return /* @__PURE__ */ jsxs13(Fragment4, { children: [
13906
- /* @__PURE__ */ jsx20(Box14, { children: /* @__PURE__ */ jsxs13(Text15, { color: "green", children: [
14347
+ return /* @__PURE__ */ jsxs16(Fragment4, { children: [
14348
+ /* @__PURE__ */ jsx23(Box16, { children: /* @__PURE__ */ jsxs16(Text17, { color: "green", children: [
13907
14349
  "\u2713 Spend request created (ID: ",
13908
- /* @__PURE__ */ jsx20(Text15, { bold: true, children: request?.id }),
14350
+ /* @__PURE__ */ jsx23(Text17, { bold: true, children: request?.id }),
13909
14351
  ")"
13910
14352
  ] }) }),
13911
- /* @__PURE__ */ jsx20(
14353
+ /* @__PURE__ */ jsx23(
13912
14354
  ApprovalWaitingView,
13913
14355
  {
13914
14356
  status,
@@ -13919,21 +14361,21 @@ var CreateSpendRequest = ({
13919
14361
  };
13920
14362
 
13921
14363
  // src/commands/spend-request/request-approval.tsx
13922
- import { Box as Box15, Text as Text16 } from "ink";
13923
- import Spinner6 from "ink-spinner";
13924
- import { useCallback as useCallback3, useEffect as useEffect11, useState as useState11 } from "react";
13925
- import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
14364
+ import { Box as Box17, Text as Text18 } from "ink";
14365
+ import Spinner9 from "ink-spinner";
14366
+ import { useCallback as useCallback7, useEffect as useEffect10, useState as useState10 } from "react";
14367
+ import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
13926
14368
  var RequestApproval = ({
13927
14369
  repository,
13928
14370
  id,
13929
14371
  onComplete
13930
14372
  }) => {
13931
- const [status, setStatus] = useState11("requesting");
13932
- const [approvalUrl, setApprovalUrl] = useState11("");
13933
- const [result, setResult] = useState11(null);
13934
- const [error, setError] = useState11("");
13935
- const onSuccess = useCallback3((r) => setResult(r), []);
13936
- const onError = useCallback3((msg) => setError(msg), []);
14373
+ const [status, setStatus] = useState10("requesting");
14374
+ const [approvalUrl, setApprovalUrl] = useState10("");
14375
+ const [result, setResult] = useState10(null);
14376
+ const [error, setError] = useState10("");
14377
+ const onSuccess = useCallback7((r) => setResult(r), []);
14378
+ const onError = useCallback7((msg) => setError(msg), []);
13937
14379
  useApprovalPolling({
13938
14380
  status,
13939
14381
  setStatus,
@@ -13944,7 +14386,7 @@ var RequestApproval = ({
13944
14386
  onSuccess,
13945
14387
  onError
13946
14388
  });
13947
- useEffect11(() => {
14389
+ useEffect10(() => {
13948
14390
  const request = async () => {
13949
14391
  try {
13950
14392
  const res = await repository.requestApproval(id);
@@ -13958,46 +14400,46 @@ var RequestApproval = ({
13958
14400
  request();
13959
14401
  }, [repository, id]);
13960
14402
  if (status === "requesting") {
13961
- return /* @__PURE__ */ jsx21(Box15, { children: /* @__PURE__ */ jsxs14(Text16, { color: "cyan", children: [
13962
- /* @__PURE__ */ jsx21(Spinner6, { type: "dots" }),
14403
+ return /* @__PURE__ */ jsx24(Box17, { children: /* @__PURE__ */ jsxs17(Text18, { color: "cyan", children: [
14404
+ /* @__PURE__ */ jsx24(Spinner9, { type: "dots" }),
13963
14405
  " Requesting approval..."
13964
14406
  ] }) });
13965
14407
  }
13966
14408
  if (status === "error") {
13967
- return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
13968
- /* @__PURE__ */ jsx21(Text16, { color: "red", children: "\u2717 Failed to request approval" }),
13969
- /* @__PURE__ */ jsx21(Text16, { color: "red", children: error })
14409
+ return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
14410
+ /* @__PURE__ */ jsx24(Text18, { color: "red", children: "\u2717 Failed to request approval" }),
14411
+ /* @__PURE__ */ jsx24(Text18, { color: "red", children: error })
13970
14412
  ] });
13971
14413
  }
13972
14414
  if (status === "success") {
13973
- return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
13974
- /* @__PURE__ */ jsx21(Text16, { color: "green", children: "\u2713 Approval completed" }),
13975
- /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
13976
- /* @__PURE__ */ jsxs14(Text16, { children: [
14415
+ return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
14416
+ /* @__PURE__ */ jsx24(Text18, { color: "green", children: "\u2713 Approval completed" }),
14417
+ /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14418
+ /* @__PURE__ */ jsxs17(Text18, { children: [
13977
14419
  "ID: ",
13978
- /* @__PURE__ */ jsx21(Text16, { bold: true, children: result?.id })
14420
+ /* @__PURE__ */ jsx24(Text18, { bold: true, children: result?.id })
13979
14421
  ] }),
13980
- /* @__PURE__ */ jsxs14(Text16, { children: [
14422
+ /* @__PURE__ */ jsxs17(Text18, { children: [
13981
14423
  "Status: ",
13982
- /* @__PURE__ */ jsx21(Text16, { bold: true, children: result?.status })
14424
+ /* @__PURE__ */ jsx24(Text18, { bold: true, children: result?.status })
13983
14425
  ] }),
13984
- /* @__PURE__ */ jsxs14(Text16, { children: [
14426
+ /* @__PURE__ */ jsxs17(Text18, { children: [
13985
14427
  "Amount:",
13986
14428
  " ",
13987
- /* @__PURE__ */ jsx21(Text16, { bold: true, children: result?.amount != null ? `${result.amount} ${result.currency?.toUpperCase() ?? ""}`.trim() : "N/A" })
14429
+ /* @__PURE__ */ jsx24(Text18, { bold: true, children: result?.amount != null ? `${result.amount} ${result.currency?.toUpperCase() ?? ""}`.trim() : "N/A" })
13988
14430
  ] }),
13989
- /* @__PURE__ */ jsxs14(Text16, { children: [
14431
+ /* @__PURE__ */ jsxs17(Text18, { children: [
13990
14432
  "Merchant: ",
13991
- /* @__PURE__ */ jsx21(Text16, { bold: true, children: result?.merchant_name })
14433
+ /* @__PURE__ */ jsx24(Text18, { bold: true, children: result?.merchant_name })
13992
14434
  ] }),
13993
- result?.credential_type === "shared_payment_token" && result.shared_payment_token && /* @__PURE__ */ jsxs14(Text16, { children: [
14435
+ result?.credential_type === "shared_payment_token" && result.shared_payment_token && /* @__PURE__ */ jsxs17(Text18, { children: [
13994
14436
  "Token: ",
13995
- /* @__PURE__ */ jsx21(Text16, { bold: true, children: result.shared_payment_token.id })
14437
+ /* @__PURE__ */ jsx24(Text18, { bold: true, children: result.shared_payment_token.id })
13996
14438
  ] })
13997
14439
  ] })
13998
14440
  ] });
13999
14441
  }
14000
- return /* @__PURE__ */ jsx21(
14442
+ return /* @__PURE__ */ jsx24(
14001
14443
  ApprovalWaitingView,
14002
14444
  {
14003
14445
  status,
@@ -14007,36 +14449,44 @@ var RequestApproval = ({
14007
14449
  };
14008
14450
 
14009
14451
  // src/commands/spend-request/retrieve.tsx
14010
- import { Box as Box16, Text as Text17 } from "ink";
14011
- import Spinner7 from "ink-spinner";
14012
- import { useEffect as useEffect12, useRef as useRef4, useState as useState12 } from "react";
14013
- import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
14452
+ import { Box as Box18, Text as Text19 } from "ink";
14453
+ import Spinner10 from "ink-spinner";
14454
+ import { useEffect as useEffect11, useRef as useRef5, useState as useState11 } from "react";
14455
+ import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
14456
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
14457
+ "approved",
14458
+ "denied",
14459
+ "expired",
14460
+ "succeeded",
14461
+ "failed",
14462
+ "canceled"
14463
+ ]);
14014
14464
  var RetrieveSpendRequest = ({
14015
14465
  repository,
14016
14466
  id,
14017
- timeout = 300,
14467
+ timeout = 600,
14018
14468
  include,
14019
14469
  outputFile,
14020
14470
  force,
14021
14471
  onComplete
14022
14472
  }) => {
14023
- const [phase, setPhase] = useState12("fetching");
14024
- const [request, setRequest] = useState12(null);
14025
- const [error, setError] = useState12("");
14026
- const [elapsed, setElapsed] = useState12(0);
14027
- const [outputFilePath, setOutputFilePath] = useState12(null);
14028
- const [fileError, setFileError] = useState12("");
14029
- const startTimeRef = useRef4(Date.now());
14030
- const pollRef = useRef4(null);
14031
- const timerRef = useRef4(null);
14032
- const requestRef = useRef4(null);
14033
- useEffect12(() => {
14473
+ const [phase, setPhase] = useState11("fetching");
14474
+ const [request, setRequest] = useState11(null);
14475
+ const [error, setError] = useState11("");
14476
+ const [elapsed, setElapsed] = useState11(0);
14477
+ const [outputFilePath, setOutputFilePath] = useState11(null);
14478
+ const [fileError, setFileError] = useState11("");
14479
+ const startTimeRef = useRef5(Date.now());
14480
+ const pollRef = useRef5(null);
14481
+ const timerRef = useRef5(null);
14482
+ const requestRef = useRef5(null);
14483
+ useEffect11(() => {
14034
14484
  return () => {
14035
14485
  if (pollRef.current) clearInterval(pollRef.current);
14036
14486
  if (timerRef.current) clearInterval(timerRef.current);
14037
14487
  };
14038
14488
  }, []);
14039
- useEffect12(() => {
14489
+ useEffect11(() => {
14040
14490
  if (phase !== "success" || !outputFile || !request?.card) return;
14041
14491
  const fileData = {
14042
14492
  spend_request_id: request.id,
@@ -14046,26 +14496,29 @@ var RetrieveSpendRequest = ({
14046
14496
  created_at: request.created_at,
14047
14497
  card: request.card
14048
14498
  };
14049
- writeCredentialFile(outputFile, fileData, force ?? false).then((path6) => setOutputFilePath(path6)).catch((err) => setFileError(err.message));
14499
+ writeCredentialFile(outputFile, fileData, force ?? false).then((path7) => setOutputFilePath(path7)).catch((err) => setFileError(err.message));
14050
14500
  }, [phase, outputFile, force, request]);
14051
- useEffect12(() => {
14501
+ useEffect11(() => {
14052
14502
  const fetch2 = async () => {
14053
14503
  try {
14054
14504
  const result = await repository.getSpendRequest(id, { include });
14055
14505
  if (!result) {
14056
14506
  setError(`Spend request ${id} not found`);
14057
14507
  setPhase("error");
14058
- setTimeout(() => onComplete(null), 1500);
14508
+ setTimeout(() => onComplete(null), DISPLAY_DELAY_MS);
14059
14509
  return;
14060
14510
  }
14061
14511
  requestRef.current = result;
14062
14512
  setRequest(result);
14063
14513
  if (result.status === "approved") {
14064
14514
  setPhase("success");
14065
- setTimeout(() => onComplete(result), 1500);
14515
+ setTimeout(() => onComplete(result), DISPLAY_DELAY_MS);
14066
14516
  } else if (result.status === "denied") {
14067
14517
  setPhase("declined");
14068
- setTimeout(() => onComplete(result), 1500);
14518
+ setTimeout(() => onComplete(result), DISPLAY_DELAY_MS);
14519
+ } else if (TERMINAL_STATUSES.has(result.status)) {
14520
+ setPhase("finalized");
14521
+ setTimeout(() => onComplete(result), DISPLAY_DELAY_MS);
14069
14522
  } else {
14070
14523
  startTimeRef.current = Date.now();
14071
14524
  setPhase("polling");
@@ -14073,12 +14526,12 @@ var RetrieveSpendRequest = ({
14073
14526
  } catch (err) {
14074
14527
  setError(err.message);
14075
14528
  setPhase("error");
14076
- setTimeout(() => onComplete(null), 1500);
14529
+ setTimeout(() => onComplete(null), DISPLAY_DELAY_MS);
14077
14530
  }
14078
14531
  };
14079
14532
  fetch2();
14080
14533
  }, [repository, id, include, onComplete]);
14081
- useEffect12(() => {
14534
+ useEffect11(() => {
14082
14535
  if (phase !== "polling") return;
14083
14536
  timerRef.current = setInterval(() => {
14084
14537
  const secs = Math.floor((Date.now() - startTimeRef.current) / 1e3);
@@ -14090,7 +14543,7 @@ var RetrieveSpendRequest = ({
14090
14543
  if (pollRef.current) clearInterval(pollRef.current);
14091
14544
  if (timerRef.current) clearInterval(timerRef.current);
14092
14545
  setPhase("timeout");
14093
- setTimeout(() => onComplete(requestRef.current), 1500);
14546
+ setTimeout(() => onComplete(requestRef.current), DISPLAY_DELAY_MS);
14094
14547
  return;
14095
14548
  }
14096
14549
  try {
@@ -14102,12 +14555,17 @@ var RetrieveSpendRequest = ({
14102
14555
  if (pollRef.current) clearInterval(pollRef.current);
14103
14556
  if (timerRef.current) clearInterval(timerRef.current);
14104
14557
  setPhase("success");
14105
- setTimeout(() => onComplete(result), 1500);
14558
+ setTimeout(() => onComplete(result), DISPLAY_DELAY_MS);
14106
14559
  } else if (result.status === "denied") {
14107
14560
  if (pollRef.current) clearInterval(pollRef.current);
14108
14561
  if (timerRef.current) clearInterval(timerRef.current);
14109
14562
  setPhase("declined");
14110
- setTimeout(() => onComplete(result), 1500);
14563
+ setTimeout(() => onComplete(result), DISPLAY_DELAY_MS);
14564
+ } else if (TERMINAL_STATUSES.has(result.status)) {
14565
+ if (pollRef.current) clearInterval(pollRef.current);
14566
+ if (timerRef.current) clearInterval(timerRef.current);
14567
+ setPhase("finalized");
14568
+ setTimeout(() => onComplete(result), DISPLAY_DELAY_MS);
14111
14569
  }
14112
14570
  } catch {
14113
14571
  }
@@ -14118,163 +14576,181 @@ var RetrieveSpendRequest = ({
14118
14576
  };
14119
14577
  }, [phase, repository, id, include, timeout, onComplete]);
14120
14578
  if (phase === "fetching") {
14121
- return /* @__PURE__ */ jsx22(Box16, { children: /* @__PURE__ */ jsxs15(Text17, { color: "cyan", children: [
14122
- /* @__PURE__ */ jsx22(Spinner7, { type: "dots" }),
14579
+ return /* @__PURE__ */ jsx25(Box18, { children: /* @__PURE__ */ jsxs18(Text19, { color: "cyan", children: [
14580
+ /* @__PURE__ */ jsx25(Spinner10, { type: "dots" }),
14123
14581
  " Retrieving spend request ",
14124
14582
  id,
14125
14583
  "..."
14126
14584
  ] }) });
14127
14585
  }
14128
14586
  if (phase === "error") {
14129
- return /* @__PURE__ */ jsx22(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsxs15(Text17, { color: "red", children: [
14587
+ return /* @__PURE__ */ jsx25(Box18, { flexDirection: "column", children: /* @__PURE__ */ jsxs18(Text19, { color: "red", children: [
14130
14588
  "\u2717 ",
14131
14589
  error
14132
14590
  ] }) });
14133
14591
  }
14134
14592
  if (phase === "timeout") {
14135
- return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
14136
- /* @__PURE__ */ jsxs15(Text17, { color: "yellow", children: [
14593
+ return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
14594
+ /* @__PURE__ */ jsxs18(Text19, { color: "yellow", children: [
14137
14595
  "\u2717 Timed out waiting for approval after ",
14138
14596
  timeout,
14139
14597
  "s"
14140
14598
  ] }),
14141
- request && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14142
- /* @__PURE__ */ jsxs15(Text17, { children: [
14599
+ request && /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14600
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14143
14601
  "ID: ",
14144
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: request.id })
14602
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request.id })
14145
14603
  ] }),
14146
- /* @__PURE__ */ jsxs15(Text17, { children: [
14604
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14147
14605
  "Status: ",
14148
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: request.status })
14606
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request.status })
14149
14607
  ] })
14150
14608
  ] })
14151
14609
  ] });
14152
14610
  }
14153
14611
  if (phase === "polling") {
14154
- return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
14155
- /* @__PURE__ */ jsx22(Box16, { children: /* @__PURE__ */ jsxs15(Text17, { color: "cyan", children: [
14156
- /* @__PURE__ */ jsx22(Spinner7, { type: "dots" }),
14612
+ return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
14613
+ /* @__PURE__ */ jsx25(Box18, { children: /* @__PURE__ */ jsxs18(Text19, { color: "cyan", children: [
14614
+ /* @__PURE__ */ jsx25(Spinner10, { type: "dots" }),
14157
14615
  " Awaiting approval... (",
14158
14616
  elapsed,
14159
14617
  "s elapsed)"
14160
14618
  ] }) }),
14161
- request?.approval_url && /* @__PURE__ */ jsx22(Box16, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsxs15(Text17, { dimColor: true, children: [
14619
+ request?.approval_url && /* @__PURE__ */ jsx25(Box18, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsxs18(Text19, { dimColor: true, children: [
14162
14620
  "Approval URL: ",
14163
- /* @__PURE__ */ jsx22(Text17, { color: "cyan", children: request.approval_url })
14621
+ /* @__PURE__ */ jsx25(Text19, { color: "cyan", children: request.approval_url })
14164
14622
  ] }) })
14165
14623
  ] });
14166
14624
  }
14625
+ if (phase === "finalized") {
14626
+ return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
14627
+ /* @__PURE__ */ jsxs18(Text19, { color: "yellow", children: [
14628
+ "Spend request reached terminal status: ",
14629
+ request?.status
14630
+ ] }),
14631
+ /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14632
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14633
+ "ID: ",
14634
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.id })
14635
+ ] }),
14636
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14637
+ "Status: ",
14638
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.status })
14639
+ ] })
14640
+ ] })
14641
+ ] });
14642
+ }
14167
14643
  if (phase === "declined") {
14168
- return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
14169
- /* @__PURE__ */ jsx22(Text17, { color: "red", children: "\u2717 Spend request declined" }),
14170
- /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14171
- /* @__PURE__ */ jsxs15(Text17, { children: [
14644
+ return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
14645
+ /* @__PURE__ */ jsx25(Text19, { color: "red", children: "\u2717 Spend request declined" }),
14646
+ /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14647
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14172
14648
  "ID: ",
14173
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.id })
14649
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.id })
14174
14650
  ] }),
14175
- /* @__PURE__ */ jsxs15(Text17, { children: [
14651
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14176
14652
  "Status:",
14177
14653
  " ",
14178
- /* @__PURE__ */ jsx22(Text17, { bold: true, color: "red", children: request?.status })
14654
+ /* @__PURE__ */ jsx25(Text19, { bold: true, color: "red", children: request?.status })
14179
14655
  ] }),
14180
- /* @__PURE__ */ jsxs15(Text17, { children: [
14656
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14181
14657
  "Amount:",
14182
14658
  " ",
14183
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.amount != null ? String(request.amount) : "N/A" })
14659
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.amount != null ? String(request.amount) : "N/A" })
14184
14660
  ] }),
14185
- /* @__PURE__ */ jsxs15(Text17, { children: [
14661
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14186
14662
  "Merchant: ",
14187
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.merchant_name })
14663
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.merchant_name })
14188
14664
  ] })
14189
14665
  ] })
14190
14666
  ] });
14191
14667
  }
14192
- return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
14193
- /* @__PURE__ */ jsx22(Text17, { color: "green", children: "\u2713 Spend request approved" }),
14194
- /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14195
- /* @__PURE__ */ jsxs15(Text17, { children: [
14668
+ return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
14669
+ /* @__PURE__ */ jsx25(Text19, { color: "green", children: "\u2713 Spend request approved" }),
14670
+ /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14671
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14196
14672
  "ID: ",
14197
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.id })
14673
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.id })
14198
14674
  ] }),
14199
- /* @__PURE__ */ jsxs15(Text17, { children: [
14675
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14200
14676
  "Status:",
14201
14677
  " ",
14202
- /* @__PURE__ */ jsx22(Text17, { bold: true, color: "green", children: request?.status })
14678
+ /* @__PURE__ */ jsx25(Text19, { bold: true, color: "green", children: request?.status })
14203
14679
  ] }),
14204
- /* @__PURE__ */ jsxs15(Text17, { children: [
14680
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14205
14681
  "Amount:",
14206
14682
  " ",
14207
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.amount != null ? String(request.amount) : "N/A" })
14683
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.amount != null ? String(request.amount) : "N/A" })
14208
14684
  ] }),
14209
- /* @__PURE__ */ jsxs15(Text17, { children: [
14685
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14210
14686
  "Merchant: ",
14211
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.merchant_name })
14687
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.merchant_name })
14212
14688
  ] }),
14213
- /* @__PURE__ */ jsxs15(Text17, { children: [
14689
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14214
14690
  "Line Items:",
14215
14691
  " ",
14216
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
14692
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
14217
14693
  ] }),
14218
- request?.shared_payment_token && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, children: [
14219
- /* @__PURE__ */ jsxs15(Text17, { bold: true, children: [
14694
+ request?.shared_payment_token && /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, children: [
14695
+ /* @__PURE__ */ jsxs18(Text19, { bold: true, children: [
14220
14696
  "\x1B]8;;https://docs.stripe.com/agentic-commerce/concepts/shared-payment-tokens\x07",
14221
14697
  "Shared Payment Token",
14222
14698
  "\x1B]8;;\x07",
14223
14699
  ":"
14224
14700
  ] }),
14225
- /* @__PURE__ */ jsxs15(Text17, { children: [
14701
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14226
14702
  " ",
14227
14703
  "Token: ",
14228
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: request.shared_payment_token.id })
14704
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request.shared_payment_token.id })
14229
14705
  ] })
14230
14706
  ] }),
14231
- request?.card && !outputFile && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, children: [
14232
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: "Card Details:" }),
14233
- /* @__PURE__ */ jsxs15(Text17, { children: [
14707
+ request?.card && !outputFile && /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, children: [
14708
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: "Card Details:" }),
14709
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14234
14710
  " ",
14235
14711
  "Number: ",
14236
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.card.number })
14712
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.card.number })
14237
14713
  ] }),
14238
- /* @__PURE__ */ jsxs15(Text17, { children: [
14714
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14239
14715
  " ",
14240
14716
  "Brand: ",
14241
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: request?.card.brand })
14717
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.card.brand })
14242
14718
  ] }),
14243
- /* @__PURE__ */ jsxs15(Text17, { children: [
14719
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14244
14720
  " ",
14245
14721
  "Expiry:",
14246
14722
  " ",
14247
- /* @__PURE__ */ jsxs15(Text17, { bold: true, children: [
14723
+ /* @__PURE__ */ jsxs18(Text19, { bold: true, children: [
14248
14724
  String(request?.card.exp_month).padStart(2, "0"),
14249
14725
  "/",
14250
14726
  request?.card.exp_year
14251
14727
  ] })
14252
14728
  ] }),
14253
- request?.card.cvc && /* @__PURE__ */ jsxs15(Text17, { children: [
14729
+ request?.card.cvc && /* @__PURE__ */ jsxs18(Text19, { children: [
14254
14730
  " ",
14255
14731
  "CVC: ",
14256
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: request.card.cvc })
14732
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request.card.cvc })
14257
14733
  ] }),
14258
- request?.card.valid_until && /* @__PURE__ */ jsxs15(Text17, { children: [
14734
+ request?.card.valid_until && /* @__PURE__ */ jsxs18(Text19, { children: [
14259
14735
  " ",
14260
14736
  "Valid Until: ",
14261
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: request.card.valid_until })
14737
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: request.card.valid_until })
14262
14738
  ] }),
14263
- request?.card.billing_address && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, children: [
14264
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: " Billing Address:" }),
14265
- /* @__PURE__ */ jsxs15(Text17, { children: [
14739
+ request?.card.billing_address && /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, children: [
14740
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: " Billing Address:" }),
14741
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14266
14742
  " ",
14267
14743
  request.card.billing_address.name
14268
14744
  ] }),
14269
- /* @__PURE__ */ jsxs15(Text17, { children: [
14745
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14270
14746
  " ",
14271
14747
  request.card.billing_address.line1
14272
14748
  ] }),
14273
- request.card.billing_address.line2 && /* @__PURE__ */ jsxs15(Text17, { children: [
14749
+ request.card.billing_address.line2 && /* @__PURE__ */ jsxs18(Text19, { children: [
14274
14750
  " ",
14275
14751
  request.card.billing_address.line2
14276
14752
  ] }),
14277
- /* @__PURE__ */ jsxs15(Text17, { children: [
14753
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14278
14754
  " ",
14279
14755
  [
14280
14756
  request.card.billing_address.city,
@@ -14282,18 +14758,18 @@ var RetrieveSpendRequest = ({
14282
14758
  request.card.billing_address.postal_code
14283
14759
  ].filter(Boolean).join(", ")
14284
14760
  ] }),
14285
- /* @__PURE__ */ jsxs15(Text17, { children: [
14761
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14286
14762
  " ",
14287
14763
  request.card.billing_address.country
14288
14764
  ] })
14289
14765
  ] })
14290
14766
  ] }),
14291
- request?.card && outputFile && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, children: [
14292
- outputFilePath && /* @__PURE__ */ jsxs15(Text17, { color: "green", children: [
14767
+ request?.card && outputFile && /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, children: [
14768
+ outputFilePath && /* @__PURE__ */ jsxs18(Text19, { color: "green", children: [
14293
14769
  "Card credentials written to ",
14294
- /* @__PURE__ */ jsx22(Text17, { bold: true, children: outputFilePath })
14770
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: outputFilePath })
14295
14771
  ] }),
14296
- fileError && /* @__PURE__ */ jsxs15(Text17, { color: "red", children: [
14772
+ fileError && /* @__PURE__ */ jsxs18(Text19, { color: "red", children: [
14297
14773
  "Failed to write card file: ",
14298
14774
  fileError
14299
14775
  ] })
@@ -14327,7 +14803,7 @@ var createOptions = z6.object({
14327
14803
  'Line item (repeatable, key:value format). Keys: name (required), quantity, unit_amount, description, sku, url, image_url, product_url. Example: "name:Shoes,unit_amount:5000,quantity:2"'
14328
14804
  ),
14329
14805
  total: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe(
14330
- 'Total (repeatable, key:value format). Keys: type (required; one of: subtotal, tax, total), display_text (required), amount (required). Example: "type:total,display_text:Total,amount:5000"'
14806
+ 'Total (repeatable, key:value format). Keys: type (required; one of: subtotal, tax, total, items_base_amount, items_discount, discount, fulfillment, shipping, fee, gift_wrap, tip, store_credit), display_text (required), amount (required). Example: "type:total,display_text:Total,amount:5000"'
14331
14807
  ),
14332
14808
  requestApproval: z6.boolean().default(true).describe("Request approval and poll until approved/denied/expired"),
14333
14809
  test: z6.boolean().default(false).describe(
@@ -14339,8 +14815,8 @@ var createOptions = z6.object({
14339
14815
  force: z6.boolean().default(false).describe("Overwrite output file if it already exists")
14340
14816
  });
14341
14817
  var retrieveOptions = z6.object({
14342
- timeout: z6.coerce.number().default(300).describe(
14343
- "Polling timeout in seconds. When reached during active polling, exits non-zero with POLLING_TIMEOUT."
14818
+ timeout: z6.coerce.number().default(600).describe(
14819
+ "Polling timeout in seconds. When reached during active polling, exits non-zero with POLLING_TIMEOUT. Default exceeds the server-side spend-request expiry so polling outlives the request itself."
14344
14820
  ),
14345
14821
  interval: z6.coerce.number().default(0).describe(
14346
14822
  "Poll interval in seconds. When > 0, polls until status is terminal, timeout is reached, or max attempts are exhausted."
@@ -14365,89 +14841,74 @@ var updateOptions = z6.object({
14365
14841
  'Line item (repeatable, key:value format). Keys: name (required), quantity, unit_amount, description, sku, url, image_url, product_url. Example: "name:Shoes,unit_amount:5000,quantity:2"'
14366
14842
  ),
14367
14843
  total: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe(
14368
- 'Total (repeatable, key:value format). Keys: type (required; one of: subtotal, tax, total), display_text (required), amount (required). Example: "type:total,display_text:Total,amount:5000"'
14844
+ 'Total (repeatable, key:value format). Keys: type (required; one of: subtotal, tax, total, items_base_amount, items_discount, discount, fulfillment, shipping, fee, gift_wrap, tip, store_credit), display_text (required), amount (required). Example: "type:total,display_text:Total,amount:5000"'
14369
14845
  )
14370
14846
  });
14371
14847
 
14372
14848
  // src/commands/spend-request/update.tsx
14373
- import { Box as Box17, Text as Text18 } from "ink";
14374
- import Spinner8 from "ink-spinner";
14375
- import { useEffect as useEffect13, useState as useState13 } from "react";
14376
- import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
14849
+ import { Box as Box19, Text as Text20 } from "ink";
14850
+ import Spinner11 from "ink-spinner";
14851
+ import { useCallback as useCallback8 } from "react";
14852
+ import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
14377
14853
  var UpdateSpendRequest = ({
14378
14854
  repository,
14379
14855
  id,
14380
14856
  params,
14381
14857
  onComplete
14382
14858
  }) => {
14383
- const [status, setStatus] = useState13(
14384
- "loading"
14859
+ const action = useCallback8(
14860
+ () => repository.updateSpendRequest(id, params),
14861
+ [repository, id, params]
14385
14862
  );
14386
- const [request, setRequest] = useState13(null);
14387
- const [error, setError] = useState13("");
14388
- useEffect13(() => {
14389
- const update = async () => {
14390
- try {
14391
- const result = await repository.updateSpendRequest(id, params);
14392
- setRequest(result);
14393
- setStatus("success");
14394
- setTimeout(() => onComplete(result), 1500);
14395
- } catch (err) {
14396
- setError(err.message);
14397
- setStatus("error");
14398
- setTimeout(() => onComplete(null), 1500);
14399
- }
14400
- };
14401
- update();
14402
- }, [repository, id, params, onComplete]);
14863
+ const { status, data: request, error } = useAsyncAction(action, onComplete);
14403
14864
  if (status === "loading") {
14404
- return /* @__PURE__ */ jsx23(Box17, { children: /* @__PURE__ */ jsxs16(Text18, { color: "cyan", children: [
14405
- /* @__PURE__ */ jsx23(Spinner8, { type: "dots" }),
14865
+ return /* @__PURE__ */ jsx26(Box19, { children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
14866
+ /* @__PURE__ */ jsx26(Spinner11, { type: "dots" }),
14406
14867
  " Updating spend request ",
14407
14868
  id,
14408
14869
  "..."
14409
14870
  ] }) });
14410
14871
  }
14411
14872
  if (status === "error") {
14412
- return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", children: [
14413
- /* @__PURE__ */ jsx23(Text18, { color: "red", children: "\u2717 Failed to update spend request" }),
14414
- /* @__PURE__ */ jsx23(Text18, { color: "red", children: error })
14873
+ return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
14874
+ /* @__PURE__ */ jsx26(Text20, { color: "red", children: "\u2717 Failed to update spend request" }),
14875
+ /* @__PURE__ */ jsx26(Text20, { color: "red", children: error })
14415
14876
  ] });
14416
14877
  }
14417
- return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", children: [
14418
- /* @__PURE__ */ jsx23(Text18, { color: "green", children: "\u2713 Spend request updated" }),
14419
- /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14420
- /* @__PURE__ */ jsxs16(Text18, { children: [
14878
+ return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
14879
+ /* @__PURE__ */ jsx26(Text20, { color: "green", children: "\u2713 Spend request updated" }),
14880
+ /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14881
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14421
14882
  "ID: ",
14422
- /* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.id })
14883
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.id })
14423
14884
  ] }),
14424
- /* @__PURE__ */ jsxs16(Text18, { children: [
14885
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14425
14886
  "Status: ",
14426
- /* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.status })
14887
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.status })
14427
14888
  ] }),
14428
- /* @__PURE__ */ jsxs16(Text18, { children: [
14889
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14429
14890
  "Amount:",
14430
14891
  " ",
14431
- /* @__PURE__ */ jsx23(Text18, { bold: true, children: (() => {
14892
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: (() => {
14432
14893
  const t = request?.totals.find((t2) => t2.type === "total");
14433
14894
  return t ? String(t.amount) : "N/A";
14434
14895
  })() })
14435
14896
  ] }),
14436
- /* @__PURE__ */ jsxs16(Text18, { children: [
14897
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14437
14898
  "Merchant: ",
14438
- /* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.merchant_name })
14899
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.merchant_name })
14439
14900
  ] }),
14440
- /* @__PURE__ */ jsxs16(Text18, { children: [
14901
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14441
14902
  "Line Items:",
14442
14903
  " ",
14443
- /* @__PURE__ */ jsx23(Text18, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
14904
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
14444
14905
  ] })
14445
14906
  ] })
14446
14907
  ] });
14447
14908
  };
14448
14909
 
14449
14910
  // src/commands/spend-request/index.tsx
14450
- import { jsx as jsx24 } from "react/jsx-runtime";
14911
+ import { jsx as jsx27 } from "react/jsx-runtime";
14451
14912
  async function applyOutputFile(request, outputFile, force) {
14452
14913
  if (!outputFile || !request.card) return request;
14453
14914
  const fileData = {
@@ -14465,8 +14926,8 @@ async function applyOutputFile(request, outputFile, force) {
14465
14926
  card_output_file: resolvedPath
14466
14927
  };
14467
14928
  }
14468
- function createSpendRequestCli(repository) {
14469
- const cli2 = Cli6.create("spend-request", {
14929
+ function createSpendRequestCli(repository, authStorage2) {
14930
+ const cli2 = Cli7.create("spend-request", {
14470
14931
  description: "Spend request management commands"
14471
14932
  });
14472
14933
  cli2.command("create", {
@@ -14475,17 +14936,7 @@ function createSpendRequestCli(repository) {
14475
14936
  alias: { merchantName: "m" },
14476
14937
  outputPolicy: "agent-only",
14477
14938
  async *run(c) {
14478
- if (!storage.isAuthenticated()) {
14479
- return c.error({
14480
- code: "NOT_AUTHENTICATED",
14481
- message: 'Not authenticated. Run "link-cli auth login" first.',
14482
- cta: {
14483
- commands: [
14484
- { command: "auth login", description: "Log in to Link" }
14485
- ]
14486
- }
14487
- });
14488
- }
14939
+ requireAuthGuard(c, authStorage2);
14489
14940
  const opts = c.options;
14490
14941
  const requestApproval = !!opts.requestApproval;
14491
14942
  const credentialType = opts.credentialType;
@@ -14545,27 +14996,27 @@ function createSpendRequestCli(repository) {
14545
14996
  const outputFile = opts.outputFile;
14546
14997
  const forceOverwrite = opts.force;
14547
14998
  if (!c.agent && !c.formatExplicit) {
14548
- return new Promise((resolve) => {
14549
- let capturedResult = null;
14550
- const { waitUntilExit } = render6(
14551
- /* @__PURE__ */ jsx24(
14552
- CreateSpendRequest,
14553
- {
14554
- repository,
14555
- params: createParams,
14556
- requestApproval,
14557
- outputFile,
14558
- force: forceOverwrite,
14559
- onComplete: (result) => {
14560
- capturedResult = result;
14561
- }
14999
+ let capturedResult = null;
15000
+ return renderInteractive(
15001
+ /* @__PURE__ */ jsx27(
15002
+ CreateSpendRequest,
15003
+ {
15004
+ repository,
15005
+ params: createParams,
15006
+ requestApproval,
15007
+ outputFile,
15008
+ force: forceOverwrite,
15009
+ onComplete: (result) => {
15010
+ capturedResult = result;
14562
15011
  }
14563
- )
14564
- );
14565
- waitUntilExit().then(() => {
14566
- resolve(capturedResult);
14567
- });
14568
- });
15012
+ }
15013
+ ),
15014
+ () => {
15015
+ if (!capturedResult)
15016
+ throw new Error("Component exited without producing a result");
15017
+ return capturedResult;
15018
+ }
15019
+ );
14569
15020
  }
14570
15021
  const created = await repository.createSpendRequest(createParams);
14571
15022
  if (!requestApproval) {
@@ -14582,9 +15033,9 @@ function createSpendRequestCli(repository) {
14582
15033
  }
14583
15034
  yield {
14584
15035
  ...created,
14585
- instruction: `Present the approval_url to the user and ask them to approve in the Link app. Then call \`spend-request retrieve ${created.id} --interval 2 --max-attempts 150\` to poll until approved. Do not wait for the user to reply \u2014 start polling immediately.`,
15036
+ instruction: `Present the approval_url to the user and ask them to approve in the Link app. Then call \`spend-request retrieve ${created.id} --interval 2 --max-attempts 300\` to poll until approved. Do not wait for the user to reply \u2014 start polling immediately.`,
14586
15037
  _next: {
14587
- command: `spend-request retrieve ${created.id} --interval 2 --max-attempts 150`,
15038
+ command: `spend-request retrieve ${created.id} --interval 2 --max-attempts 300`,
14588
15039
  until: "status changes from pending_approval"
14589
15040
  }
14590
15041
  };
@@ -14597,18 +15048,8 @@ function createSpendRequestCli(repository) {
14597
15048
  }),
14598
15049
  options: updateOptions,
14599
15050
  outputPolicy: "agent-only",
15051
+ middleware: [requireAuth(authStorage2)],
14600
15052
  async run(c) {
14601
- if (!storage.isAuthenticated()) {
14602
- return c.error({
14603
- code: "NOT_AUTHENTICATED",
14604
- message: 'Not authenticated. Run "link-cli auth login" first.',
14605
- cta: {
14606
- commands: [
14607
- { command: "auth login", description: "Log in to Link" }
14608
- ]
14609
- }
14610
- });
14611
- }
14612
15053
  const id = c.args.id;
14613
15054
  const opts = c.options;
14614
15055
  const params = {};
@@ -14629,25 +15070,25 @@ function createSpendRequestCli(repository) {
14629
15070
  (item) => typeof item === "string" ? parseTotalFlag(item) : item
14630
15071
  );
14631
15072
  if (!c.agent && !c.formatExplicit) {
14632
- return new Promise((resolve) => {
14633
- let capturedResult = null;
14634
- const { waitUntilExit } = render6(
14635
- /* @__PURE__ */ jsx24(
14636
- UpdateSpendRequest,
14637
- {
14638
- repository,
14639
- id,
14640
- params,
14641
- onComplete: (result) => {
14642
- capturedResult = result;
14643
- }
15073
+ let capturedResult = null;
15074
+ return renderInteractive(
15075
+ /* @__PURE__ */ jsx27(
15076
+ UpdateSpendRequest,
15077
+ {
15078
+ repository,
15079
+ id,
15080
+ params,
15081
+ onComplete: (result) => {
15082
+ capturedResult = result;
14644
15083
  }
14645
- )
14646
- );
14647
- waitUntilExit().then(() => {
14648
- resolve(capturedResult);
14649
- });
14650
- });
15084
+ }
15085
+ ),
15086
+ () => {
15087
+ if (!capturedResult)
15088
+ throw new Error("Component exited without producing a result");
15089
+ return capturedResult;
15090
+ }
15091
+ );
14651
15092
  }
14652
15093
  return repository.updateSpendRequest(id, params);
14653
15094
  }
@@ -14659,44 +15100,34 @@ function createSpendRequestCli(repository) {
14659
15100
  }),
14660
15101
  outputPolicy: "agent-only",
14661
15102
  async *run(c) {
14662
- if (!storage.isAuthenticated()) {
14663
- return c.error({
14664
- code: "NOT_AUTHENTICATED",
14665
- message: 'Not authenticated. Run "link-cli auth login" first.',
14666
- cta: {
14667
- commands: [
14668
- { command: "auth login", description: "Log in to Link" }
14669
- ]
14670
- }
14671
- });
14672
- }
15103
+ requireAuthGuard(c, authStorage2);
14673
15104
  const id = c.args.id;
14674
15105
  if (!c.agent && !c.formatExplicit) {
14675
- return new Promise((resolve) => {
14676
- let capturedResult = null;
14677
- const { waitUntilExit } = render6(
14678
- /* @__PURE__ */ jsx24(
14679
- RequestApproval,
14680
- {
14681
- repository,
14682
- id,
14683
- onComplete: (result) => {
14684
- capturedResult = result;
14685
- }
15106
+ let capturedResult = null;
15107
+ return renderInteractive(
15108
+ /* @__PURE__ */ jsx27(
15109
+ RequestApproval,
15110
+ {
15111
+ repository,
15112
+ id,
15113
+ onComplete: (result) => {
15114
+ capturedResult = result;
14686
15115
  }
14687
- )
14688
- );
14689
- waitUntilExit().then(() => {
14690
- resolve(capturedResult);
14691
- });
14692
- });
15116
+ }
15117
+ ),
15118
+ () => {
15119
+ if (!capturedResult)
15120
+ throw new Error("Component exited without producing a result");
15121
+ return capturedResult;
15122
+ }
15123
+ );
14693
15124
  }
14694
15125
  const approval = await repository.requestApproval(id);
14695
15126
  yield {
14696
15127
  ...approval,
14697
- instruction: `Present the approval_url to the user and ask them to approve in the Link app. Then call \`spend-request retrieve ${id} --interval 2 --max-attempts 150\` to poll until approved. Do not wait for the user to reply \u2014 start polling immediately.`,
15128
+ instruction: `Present the approval_url to the user and ask them to approve in the Link app. Then call \`spend-request retrieve ${id} --interval 2 --max-attempts 300\` to poll until approved. Do not wait for the user to reply \u2014 start polling immediately.`,
14698
15129
  _next: {
14699
- command: `spend-request retrieve ${id} --interval 2 --max-attempts 150`,
15130
+ command: `spend-request retrieve ${id} --interval 2 --max-attempts 300`,
14700
15131
  until: "status changes from pending_approval"
14701
15132
  }
14702
15133
  };
@@ -14710,17 +15141,7 @@ function createSpendRequestCli(repository) {
14710
15141
  options: retrieveOptions,
14711
15142
  outputPolicy: "agent-only",
14712
15143
  async *run(c) {
14713
- if (!storage.isAuthenticated()) {
14714
- return c.error({
14715
- code: "NOT_AUTHENTICATED",
14716
- message: 'Not authenticated. Run "link-cli auth login" first.',
14717
- cta: {
14718
- commands: [
14719
- { command: "auth login", description: "Log in to Link" }
14720
- ]
14721
- }
14722
- });
14723
- }
15144
+ requireAuthGuard(c, authStorage2);
14724
15145
  const id = c.args.id;
14725
15146
  const opts = c.options;
14726
15147
  const timeout = opts.timeout;
@@ -14731,78 +15152,178 @@ function createSpendRequestCli(repository) {
14731
15152
  const outputFile = opts.outputFile;
14732
15153
  const forceOverwrite = opts.force;
14733
15154
  if (!c.agent && !c.formatExplicit) {
14734
- return new Promise((resolve) => {
14735
- let capturedResult = null;
14736
- const { waitUntilExit } = render6(
14737
- /* @__PURE__ */ jsx24(
14738
- RetrieveSpendRequest,
14739
- {
14740
- repository,
14741
- id,
14742
- timeout,
14743
- include,
14744
- outputFile,
14745
- force: forceOverwrite,
14746
- onComplete: (result) => {
14747
- capturedResult = result;
14748
- }
15155
+ let capturedResult = null;
15156
+ return renderInteractive(
15157
+ /* @__PURE__ */ jsx27(
15158
+ RetrieveSpendRequest,
15159
+ {
15160
+ repository,
15161
+ id,
15162
+ timeout,
15163
+ include,
15164
+ outputFile,
15165
+ force: forceOverwrite,
15166
+ onComplete: (result) => {
15167
+ capturedResult = result;
14749
15168
  }
14750
- )
14751
- );
14752
- waitUntilExit().then(() => {
14753
- resolve(capturedResult);
14754
- });
14755
- });
15169
+ }
15170
+ ),
15171
+ () => {
15172
+ if (!capturedResult)
15173
+ throw new Error("Component exited without producing a result");
15174
+ return capturedResult;
15175
+ }
15176
+ );
14756
15177
  }
14757
15178
  const terminalStatuses = /* @__PURE__ */ new Set([
14758
15179
  "approved",
14759
15180
  "denied",
14760
15181
  "expired",
14761
15182
  "succeeded",
14762
- "failed"
15183
+ "failed",
15184
+ "canceled"
14763
15185
  ]);
14764
- const deadline = Date.now() + timeout * 1e3;
14765
- let attempts = 0;
14766
- let previousAttemptData;
14767
- while (true) {
14768
- const request = await repository.getSpendRequest(id, { include });
14769
- if (!request) {
15186
+ for await (const result of pollUntil({
15187
+ fn: () => repository.getSpendRequest(id, { include }),
15188
+ isTerminal: (req) => req === null || terminalStatuses.has(req.status),
15189
+ interval,
15190
+ maxAttempts,
15191
+ timeout
15192
+ })) {
15193
+ if (result.value === null) {
14770
15194
  return c.error({
14771
15195
  code: "NOT_FOUND",
14772
15196
  message: `Spend request ${id} not found`
14773
15197
  });
14774
15198
  }
14775
- const shouldEmitFinal = terminalStatuses.has(request.status) || interval <= 0;
14776
- if (shouldEmitFinal) {
14777
- try {
14778
- yield await applyOutputFile(request, outputFile, forceOverwrite);
14779
- } catch (err) {
14780
- const message = err.message;
14781
- if (message.startsWith("OUTPUT_FILE_EXISTS")) {
14782
- return c.error({ code: "OUTPUT_FILE_EXISTS", message });
15199
+ if (result.terminal) {
15200
+ if (terminalStatuses.has(result.value.status) || !result.reason) {
15201
+ try {
15202
+ yield await applyOutputFile(
15203
+ result.value,
15204
+ outputFile,
15205
+ forceOverwrite
15206
+ );
15207
+ } catch (err) {
15208
+ const message = err.message;
15209
+ if (message.startsWith("OUTPUT_FILE_EXISTS")) {
15210
+ return c.error({ code: "OUTPUT_FILE_EXISTS", message });
15211
+ }
15212
+ return c.error({ code: "OUTPUT_FILE_WRITE_ERROR", message });
14783
15213
  }
14784
- return c.error({ code: "OUTPUT_FILE_WRITE_ERROR", message });
15214
+ return;
14785
15215
  }
14786
- return;
14787
- }
14788
- attempts++;
14789
- const maxAttemptsExhausted = maxAttempts > 0 && attempts >= maxAttempts;
14790
- const timeoutReached = Date.now() >= deadline;
14791
- if (maxAttemptsExhausted || timeoutReached) {
14792
- const reason = maxAttemptsExhausted ? `max attempts (${maxAttempts}) exhausted` : `timeout (${timeout}s) reached`;
15216
+ const reason = result.reason === "max_attempts" ? `max attempts (${maxAttempts}) exhausted` : `timeout (${timeout}s) reached`;
14793
15217
  return c.error({
14794
15218
  code: "POLLING_TIMEOUT",
14795
- message: `Polling stopped before spend request ${id} reached a terminal status: ${reason}; current status is ${request.status}.`,
15219
+ message: `Polling stopped before spend request ${id} reached a terminal status: ${reason}; current status is ${result.value.status}.`,
14796
15220
  retryable: true
14797
15221
  });
14798
15222
  }
14799
- const snapshot = JSON.stringify(request);
14800
- if (snapshot !== previousAttemptData) {
14801
- previousAttemptData = snapshot;
14802
- yield request;
14803
- }
14804
- await new Promise((resolve) => setTimeout(resolve, interval * 1e3));
15223
+ yield result.value;
15224
+ }
15225
+ }
15226
+ });
15227
+ cli2.command("cancel", {
15228
+ description: "Cancel a spend request",
15229
+ args: z7.object({
15230
+ id: z7.string().describe("Spend request ID")
15231
+ }),
15232
+ outputPolicy: "agent-only",
15233
+ middleware: [requireAuth(authStorage2)],
15234
+ async run(c) {
15235
+ const id = c.args.id;
15236
+ if (!c.agent && !c.formatExplicit) {
15237
+ let capturedResult = null;
15238
+ return renderInteractive(
15239
+ /* @__PURE__ */ jsx27(
15240
+ CancelSpendRequest,
15241
+ {
15242
+ repository,
15243
+ id,
15244
+ onComplete: (result) => {
15245
+ capturedResult = result;
15246
+ }
15247
+ }
15248
+ ),
15249
+ () => {
15250
+ if (!capturedResult)
15251
+ throw new Error("Component exited without producing a result");
15252
+ return capturedResult;
15253
+ }
15254
+ );
15255
+ }
15256
+ return repository.cancelSpendRequest(id);
15257
+ }
15258
+ });
15259
+ return cli2;
15260
+ }
15261
+
15262
+ // src/commands/user-info/index.tsx
15263
+ import { Cli as Cli8 } from "incur";
15264
+
15265
+ // src/commands/user-info/retrieve.tsx
15266
+ import { Box as Box20, Text as Text21 } from "ink";
15267
+ import Spinner12 from "ink-spinner";
15268
+ import { useCallback as useCallback9 } from "react";
15269
+ import { jsx as jsx28, jsxs as jsxs20 } from "react/jsx-runtime";
15270
+ var UserInfoRetrieve = ({
15271
+ resource,
15272
+ onComplete
15273
+ }) => {
15274
+ const action = useCallback9(() => resource.retrieve(), [resource]);
15275
+ const { status, data: userInfo, error } = useAsyncAction(action, onComplete);
15276
+ if (status === "loading") {
15277
+ return /* @__PURE__ */ jsx28(Box20, { children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
15278
+ /* @__PURE__ */ jsx28(Spinner12, { type: "dots" }),
15279
+ " Loading user info..."
15280
+ ] }) });
15281
+ }
15282
+ if (status === "error") {
15283
+ return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
15284
+ /* @__PURE__ */ jsx28(Text21, { color: "red", children: "\u2717 Failed to load user info" }),
15285
+ /* @__PURE__ */ jsx28(Text21, { color: "red", children: error })
15286
+ ] });
15287
+ }
15288
+ return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
15289
+ /* @__PURE__ */ jsx28(Text21, { bold: true, children: "User Info" }),
15290
+ /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
15291
+ /* @__PURE__ */ jsxs20(Text21, { children: [
15292
+ /* @__PURE__ */ jsx28(Text21, { dimColor: true, children: "Email: " }),
15293
+ userInfo?.email ?? /* @__PURE__ */ jsx28(Text21, { dimColor: true, children: "Not set" })
15294
+ ] }),
15295
+ /* @__PURE__ */ jsxs20(Text21, { children: [
15296
+ /* @__PURE__ */ jsx28(Text21, { dimColor: true, children: "Name: " }),
15297
+ userInfo?.name ?? /* @__PURE__ */ jsx28(Text21, { dimColor: true, children: "Not set" })
15298
+ ] }),
15299
+ /* @__PURE__ */ jsxs20(Text21, { children: [
15300
+ /* @__PURE__ */ jsx28(Text21, { dimColor: true, children: "Phone: " }),
15301
+ userInfo?.phone ?? /* @__PURE__ */ jsx28(Text21, { dimColor: true, children: "Not set" })
15302
+ ] })
15303
+ ] })
15304
+ ] });
15305
+ };
15306
+
15307
+ // src/commands/user-info/index.tsx
15308
+ import { jsx as jsx29 } from "react/jsx-runtime";
15309
+ function createUserInfoCli(createResource, authStorage2) {
15310
+ const cli2 = Cli8.create("user-info", {
15311
+ description: "User information commands"
15312
+ });
15313
+ cli2.command("retrieve", {
15314
+ description: "Retrieve user info (email, name, phone)",
15315
+ outputPolicy: "agent-only",
15316
+ middleware: [requireAuth(authStorage2)],
15317
+ async run(c) {
15318
+ const resource = createResource();
15319
+ if (!c.agent && !c.formatExplicit) {
15320
+ return renderInteractive(
15321
+ /* @__PURE__ */ jsx29(UserInfoRetrieve, { resource, onComplete: () => {
15322
+ } }),
15323
+ () => resource.retrieve()
15324
+ );
14805
15325
  }
15326
+ return resource.retrieve();
14806
15327
  }
14807
15328
  });
14808
15329
  return cli2;
@@ -15032,9 +15553,9 @@ ${JSON.stringify(redacted, null, 2)}`
15032
15553
 
15033
15554
  // src/auth/session.ts
15034
15555
  var EXPIRY_BUFFER_MS = 6e4;
15035
- function createAccessTokenProvider(authResource, authStorage = storage) {
15556
+ function createAccessTokenProvider(authResource, authStorage2 = storage) {
15036
15557
  return async ({ forceRefresh } = {}) => {
15037
- const auth = authStorage.getAuth();
15558
+ const auth = authStorage2.getAuth();
15038
15559
  if (!auth) {
15039
15560
  throw new LinkAuthenticationError(
15040
15561
  'Not authenticated. Run "link-cli auth login" first.'
@@ -15045,22 +15566,78 @@ function createAccessTokenProvider(authResource, authStorage = storage) {
15045
15566
  return auth.access_token;
15046
15567
  }
15047
15568
  const refreshed = await authResource.refreshToken(auth.refresh_token);
15048
- authStorage.setAuth(refreshed);
15569
+ authStorage2.setAuth(refreshed);
15049
15570
  return refreshed.access_token;
15050
15571
  };
15051
15572
  }
15052
15573
 
15574
+ // src/utils/sanitize-text.ts
15575
+ import stripAnsi from "strip-ansi";
15576
+ var CONTROL_CHAR_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f\r]/g;
15577
+ var NEEDS_SANITIZE_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f\r\x1b]/;
15578
+ function sanitizeText(input) {
15579
+ if (!input) {
15580
+ return "";
15581
+ }
15582
+ if (!NEEDS_SANITIZE_RE.test(input)) {
15583
+ return input;
15584
+ }
15585
+ return stripAnsi(input).replace(CONTROL_CHAR_RE, "");
15586
+ }
15587
+ function sanitizeDeep(value) {
15588
+ if (value === null || value === void 0) {
15589
+ return value;
15590
+ }
15591
+ if (typeof value === "string") {
15592
+ return sanitizeText(value);
15593
+ }
15594
+ if (Array.isArray(value)) {
15595
+ return value.map(sanitizeDeep);
15596
+ }
15597
+ if (typeof value === "object") {
15598
+ const result = {};
15599
+ const keys = Object.keys(value);
15600
+ for (let i = 0; i < keys.length; i++) {
15601
+ const k = keys[i];
15602
+ result[k] = sanitizeDeep(value[k]);
15603
+ }
15604
+ return result;
15605
+ }
15606
+ return value;
15607
+ }
15608
+
15053
15609
  // src/utils/resource-factory.ts
15610
+ function sanitizeResource(resource) {
15611
+ return new Proxy(resource, {
15612
+ get(target, prop, receiver) {
15613
+ const value = Reflect.get(target, prop, receiver);
15614
+ if (typeof value !== "function") {
15615
+ return value;
15616
+ }
15617
+ return (...args) => {
15618
+ const result = value.apply(target, args);
15619
+ if (result && typeof result.then === "function") {
15620
+ return result.then(sanitizeDeep);
15621
+ }
15622
+ return result;
15623
+ };
15624
+ }
15625
+ });
15626
+ }
15054
15627
  var ResourceFactory = class {
15055
15628
  verbose;
15056
15629
  defaultHeaders;
15630
+ authStorage;
15057
15631
  authResource;
15058
15632
  accessTokenProvider;
15059
15633
  spendRequestResource;
15060
15634
  paymentMethodsResource;
15635
+ shippingAddressResource;
15636
+ userInfoResource;
15061
15637
  constructor(options = {}) {
15062
15638
  this.verbose = options.verbose ?? false;
15063
15639
  this.defaultHeaders = options.defaultHeaders;
15640
+ this.authStorage = options.authStorage;
15064
15641
  }
15065
15642
  createAuthResource() {
15066
15643
  if (this.authResource) {
@@ -15072,12 +15649,16 @@ var ResourceFactory = class {
15072
15649
  });
15073
15650
  return this.authResource;
15074
15651
  }
15652
+ getAuthStorage() {
15653
+ return this.authStorage;
15654
+ }
15075
15655
  createSdkAccessTokenProvider() {
15076
15656
  if (this.accessTokenProvider) {
15077
15657
  return this.accessTokenProvider;
15078
15658
  }
15079
15659
  this.accessTokenProvider = createAccessTokenProvider(
15080
- this.createAuthResource()
15660
+ this.createAuthResource(),
15661
+ this.authStorage
15081
15662
  );
15082
15663
  return this.accessTokenProvider;
15083
15664
  }
@@ -15086,11 +15667,13 @@ var ResourceFactory = class {
15086
15667
  return this.spendRequestResource;
15087
15668
  }
15088
15669
  const getAccessToken = this.createSdkAccessTokenProvider();
15089
- this.spendRequestResource = new SpendRequestResource({
15090
- verbose: this.verbose,
15091
- defaultHeaders: this.defaultHeaders,
15092
- getAccessToken
15093
- });
15670
+ this.spendRequestResource = sanitizeResource(
15671
+ new SpendRequestResource({
15672
+ verbose: this.verbose,
15673
+ defaultHeaders: this.defaultHeaders,
15674
+ getAccessToken
15675
+ })
15676
+ );
15094
15677
  return this.spendRequestResource;
15095
15678
  }
15096
15679
  createPaymentMethodsResource() {
@@ -15098,13 +15681,43 @@ var ResourceFactory = class {
15098
15681
  return this.paymentMethodsResource;
15099
15682
  }
15100
15683
  const getAccessToken = this.createSdkAccessTokenProvider();
15101
- this.paymentMethodsResource = new PaymentMethodsResource({
15102
- verbose: this.verbose,
15103
- defaultHeaders: this.defaultHeaders,
15104
- getAccessToken
15105
- });
15684
+ this.paymentMethodsResource = sanitizeResource(
15685
+ new PaymentMethodsResource({
15686
+ verbose: this.verbose,
15687
+ defaultHeaders: this.defaultHeaders,
15688
+ getAccessToken
15689
+ })
15690
+ );
15106
15691
  return this.paymentMethodsResource;
15107
15692
  }
15693
+ createShippingAddressResource() {
15694
+ if (this.shippingAddressResource) {
15695
+ return this.shippingAddressResource;
15696
+ }
15697
+ const getAccessToken = this.createSdkAccessTokenProvider();
15698
+ this.shippingAddressResource = sanitizeResource(
15699
+ new ShippingAddressResource({
15700
+ verbose: this.verbose,
15701
+ defaultHeaders: this.defaultHeaders,
15702
+ getAccessToken
15703
+ })
15704
+ );
15705
+ return this.shippingAddressResource;
15706
+ }
15707
+ createUserInfoResource() {
15708
+ if (this.userInfoResource) {
15709
+ return this.userInfoResource;
15710
+ }
15711
+ const getAccessToken = this.createSdkAccessTokenProvider();
15712
+ this.userInfoResource = sanitizeResource(
15713
+ new UserInfoResource({
15714
+ verbose: this.verbose,
15715
+ defaultHeaders: this.defaultHeaders,
15716
+ getAccessToken
15717
+ })
15718
+ );
15719
+ return this.userInfoResource;
15720
+ }
15108
15721
  };
15109
15722
 
15110
15723
  // src/utils/update-info.ts
@@ -15178,7 +15791,7 @@ function cacheUpdateInfo(value, ttlMs = UPDATE_CACHE_TTL_MS) {
15178
15791
  }
15179
15792
 
15180
15793
  // src/cli.tsx
15181
- var cliVersion = "0.4.3";
15794
+ var cliVersion = "0.5.0";
15182
15795
  var buildNumber = "1";
15183
15796
  var cliName = "@stripe/link-cli";
15184
15797
  var defaultHeaders = {
@@ -15186,10 +15799,16 @@ var defaultHeaders = {
15186
15799
  "X-Build-Number": buildNumber
15187
15800
  };
15188
15801
  var verbose = process.argv.includes("--verbose");
15189
- var factory = new ResourceFactory({ verbose, defaultHeaders });
15802
+ var authFileIndex = process.argv.indexOf("--auth");
15803
+ var credentialFilePath = authFileIndex !== -1 ? process.argv[authFileIndex + 1] : process.env.LINK_AUTH_FILE;
15804
+ if (authFileIndex !== -1) {
15805
+ process.argv.splice(authFileIndex, 2);
15806
+ }
15807
+ var authStorage = credentialFilePath ? new Storage({ configPath: credentialFilePath }) : storage;
15808
+ var factory = new ResourceFactory({ verbose, defaultHeaders, authStorage });
15190
15809
  var authRepo = factory.createAuthResource();
15191
15810
  var spendRequestRepo = factory.createSpendRequestResource();
15192
- var cli = Cli7.create("link-cli", {
15811
+ var cli = Cli9.create("link-cli", {
15193
15812
  description: "Create a secure, one-time payment credential from a Link wallet to let agents complete purchases on behalf of users.",
15194
15813
  version: `${cliVersion} (build ${buildNumber})`
15195
15814
  });
@@ -15206,24 +15825,38 @@ if (!isAgent && process.stdout.isTTY) {
15206
15825
  process.stderr.write(renderInteractiveUpdateNotice(updateInfo));
15207
15826
  }
15208
15827
  }
15209
- cli.command(createAuthCli(authRepo, getUpdateInfo));
15210
- cli.command(createSpendRequestCli(spendRequestRepo));
15828
+ cli.command(createAuthCli(authRepo, getUpdateInfo, authStorage));
15829
+ cli.command(createSpendRequestCli(spendRequestRepo, authStorage));
15830
+ cli.command(
15831
+ createPaymentMethodsCli(
15832
+ () => factory.createPaymentMethodsResource(),
15833
+ authStorage
15834
+ )
15835
+ );
15836
+ cli.command(
15837
+ createShippingAddressCli(
15838
+ () => factory.createShippingAddressResource(),
15839
+ authStorage
15840
+ )
15841
+ );
15211
15842
  cli.command(
15212
- createPaymentMethodsCli(() => factory.createPaymentMethodsResource())
15843
+ createUserInfoCli(() => factory.createUserInfoResource(), authStorage)
15213
15844
  );
15214
- cli.command(createMppCli(spendRequestRepo));
15845
+ cli.command(createMppCli(spendRequestRepo, authStorage));
15215
15846
  cli.command(
15216
15847
  createDemoCli(
15217
15848
  authRepo,
15218
15849
  spendRequestRepo,
15219
- () => factory.createPaymentMethodsResource()
15850
+ () => factory.createPaymentMethodsResource(),
15851
+ authStorage
15220
15852
  )
15221
15853
  );
15222
15854
  cli.command(
15223
15855
  createOnboardCli(
15224
15856
  authRepo,
15225
15857
  spendRequestRepo,
15226
- () => factory.createPaymentMethodsResource()
15858
+ () => factory.createPaymentMethodsResource(),
15859
+ authStorage
15227
15860
  )
15228
15861
  );
15229
15862
  cli.serve();