@stripe/link-cli 0.5.0 → 0.6.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 +31 -1
  2. package/dist/cli.js +855 -394
  3. package/package.json +3 -3
package/dist/cli.js CHANGED
@@ -402,11 +402,11 @@ var require_codegen = __commonJS({
402
402
  const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
403
403
  return `${varKind} ${this.name}${rhs};` + _n;
404
404
  }
405
- optimizeNames(names, constants) {
405
+ optimizeNames(names, constants2) {
406
406
  if (!names[this.name.str])
407
407
  return;
408
408
  if (this.rhs)
409
- this.rhs = optimizeExpr(this.rhs, names, constants);
409
+ this.rhs = optimizeExpr(this.rhs, names, constants2);
410
410
  return this;
411
411
  }
412
412
  get names() {
@@ -423,10 +423,10 @@ var require_codegen = __commonJS({
423
423
  render({ _n }) {
424
424
  return `${this.lhs} = ${this.rhs};` + _n;
425
425
  }
426
- optimizeNames(names, constants) {
426
+ optimizeNames(names, constants2) {
427
427
  if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
428
428
  return;
429
- this.rhs = optimizeExpr(this.rhs, names, constants);
429
+ this.rhs = optimizeExpr(this.rhs, names, constants2);
430
430
  return this;
431
431
  }
432
432
  get names() {
@@ -487,8 +487,8 @@ var require_codegen = __commonJS({
487
487
  optimizeNodes() {
488
488
  return `${this.code}` ? this : void 0;
489
489
  }
490
- optimizeNames(names, constants) {
491
- this.code = optimizeExpr(this.code, names, constants);
490
+ optimizeNames(names, constants2) {
491
+ this.code = optimizeExpr(this.code, names, constants2);
492
492
  return this;
493
493
  }
494
494
  get names() {
@@ -517,12 +517,12 @@ var require_codegen = __commonJS({
517
517
  }
518
518
  return nodes.length > 0 ? this : void 0;
519
519
  }
520
- optimizeNames(names, constants) {
520
+ optimizeNames(names, constants2) {
521
521
  const { nodes } = this;
522
522
  let i = nodes.length;
523
523
  while (i--) {
524
524
  const n = nodes[i];
525
- if (n.optimizeNames(names, constants))
525
+ if (n.optimizeNames(names, constants2))
526
526
  continue;
527
527
  subtractNames(names, n.names);
528
528
  nodes.splice(i, 1);
@@ -575,12 +575,12 @@ var require_codegen = __commonJS({
575
575
  return void 0;
576
576
  return this;
577
577
  }
578
- optimizeNames(names, constants) {
578
+ optimizeNames(names, constants2) {
579
579
  var _a;
580
- this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants);
581
- if (!(super.optimizeNames(names, constants) || this.else))
580
+ this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants2);
581
+ if (!(super.optimizeNames(names, constants2) || this.else))
582
582
  return;
583
- this.condition = optimizeExpr(this.condition, names, constants);
583
+ this.condition = optimizeExpr(this.condition, names, constants2);
584
584
  return this;
585
585
  }
586
586
  get names() {
@@ -603,10 +603,10 @@ var require_codegen = __commonJS({
603
603
  render(opts) {
604
604
  return `for(${this.iteration})` + super.render(opts);
605
605
  }
606
- optimizeNames(names, constants) {
607
- if (!super.optimizeNames(names, constants))
606
+ optimizeNames(names, constants2) {
607
+ if (!super.optimizeNames(names, constants2))
608
608
  return;
609
- this.iteration = optimizeExpr(this.iteration, names, constants);
609
+ this.iteration = optimizeExpr(this.iteration, names, constants2);
610
610
  return this;
611
611
  }
612
612
  get names() {
@@ -642,10 +642,10 @@ var require_codegen = __commonJS({
642
642
  render(opts) {
643
643
  return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
644
644
  }
645
- optimizeNames(names, constants) {
646
- if (!super.optimizeNames(names, constants))
645
+ optimizeNames(names, constants2) {
646
+ if (!super.optimizeNames(names, constants2))
647
647
  return;
648
- this.iterable = optimizeExpr(this.iterable, names, constants);
648
+ this.iterable = optimizeExpr(this.iterable, names, constants2);
649
649
  return this;
650
650
  }
651
651
  get names() {
@@ -687,11 +687,11 @@ var require_codegen = __commonJS({
687
687
  (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes();
688
688
  return this;
689
689
  }
690
- optimizeNames(names, constants) {
690
+ optimizeNames(names, constants2) {
691
691
  var _a, _b;
692
- super.optimizeNames(names, constants);
693
- (_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants);
694
- (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants);
692
+ super.optimizeNames(names, constants2);
693
+ (_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants2);
694
+ (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants2);
695
695
  return this;
696
696
  }
697
697
  get names() {
@@ -992,7 +992,7 @@ var require_codegen = __commonJS({
992
992
  function addExprNames(names, from) {
993
993
  return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
994
994
  }
995
- function optimizeExpr(expr, names, constants) {
995
+ function optimizeExpr(expr, names, constants2) {
996
996
  if (expr instanceof code_1.Name)
997
997
  return replaceName(expr);
998
998
  if (!canOptimize(expr))
@@ -1007,14 +1007,14 @@ var require_codegen = __commonJS({
1007
1007
  return items;
1008
1008
  }, []));
1009
1009
  function replaceName(n) {
1010
- const c = constants[n.str];
1010
+ const c = constants2[n.str];
1011
1011
  if (c === void 0 || names[n.str] !== 1)
1012
1012
  return n;
1013
1013
  delete names[n.str];
1014
1014
  return c;
1015
1015
  }
1016
1016
  function canOptimize(e) {
1017
- return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants[c.str] !== void 0);
1017
+ return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants2[c.str] !== void 0);
1018
1018
  }
1019
1019
  }
1020
1020
  function subtractNames(names, from) {
@@ -8704,19 +8704,19 @@ var require_range = __commonJS({
8704
8704
  var replaceCaret = (comp, options) => {
8705
8705
  debug("caret", comp, options);
8706
8706
  const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET];
8707
- const z8 = options.includePrerelease ? "-0" : "";
8707
+ const z9 = options.includePrerelease ? "-0" : "";
8708
8708
  return comp.replace(r, (_, M, m, p, pr) => {
8709
8709
  debug("caret", comp, _, M, m, p, pr);
8710
8710
  let ret;
8711
8711
  if (isX(M)) {
8712
8712
  ret = "";
8713
8713
  } else if (isX(m)) {
8714
- ret = `>=${M}.0.0${z8} <${+M + 1}.0.0-0`;
8714
+ ret = `>=${M}.0.0${z9} <${+M + 1}.0.0-0`;
8715
8715
  } else if (isX(p)) {
8716
8716
  if (M === "0") {
8717
- ret = `>=${M}.${m}.0${z8} <${M}.${+m + 1}.0-0`;
8717
+ ret = `>=${M}.${m}.0${z9} <${M}.${+m + 1}.0-0`;
8718
8718
  } else {
8719
- ret = `>=${M}.${m}.0${z8} <${+M + 1}.0.0-0`;
8719
+ ret = `>=${M}.${m}.0${z9} <${+M + 1}.0.0-0`;
8720
8720
  }
8721
8721
  } else if (pr) {
8722
8722
  debug("replaceCaret pr", pr);
@@ -8733,9 +8733,9 @@ var require_range = __commonJS({
8733
8733
  debug("no pr");
8734
8734
  if (M === "0") {
8735
8735
  if (m === "0") {
8736
- ret = `>=${M}.${m}.${p}${z8} <${M}.${m}.${+p + 1}-0`;
8736
+ ret = `>=${M}.${m}.${p}${z9} <${M}.${m}.${+p + 1}-0`;
8737
8737
  } else {
8738
- ret = `>=${M}.${m}.${p}${z8} <${M}.${+m + 1}.0-0`;
8738
+ ret = `>=${M}.${m}.${p}${z9} <${M}.${+m + 1}.0-0`;
8739
8739
  }
8740
8740
  } else {
8741
8741
  ret = `>=${M}.${m}.${p} <${+M + 1}.0.0-0`;
@@ -9458,7 +9458,7 @@ var require_semver2 = __commonJS({
9458
9458
  "../../node_modules/.pnpm/semver@7.7.4/node_modules/semver/index.js"(exports, module) {
9459
9459
  "use strict";
9460
9460
  var internalRe = require_re();
9461
- var constants = require_constants();
9461
+ var constants2 = require_constants();
9462
9462
  var SemVer = require_semver();
9463
9463
  var identifiers = require_identifiers();
9464
9464
  var parse = require_parse();
@@ -9540,8 +9540,8 @@ var require_semver2 = __commonJS({
9540
9540
  re: internalRe.re,
9541
9541
  src: internalRe.src,
9542
9542
  tokens: internalRe.t,
9543
- SEMVER_SPEC_VERSION: constants.SEMVER_SPEC_VERSION,
9544
- RELEASE_TYPES: constants.RELEASE_TYPES,
9543
+ SEMVER_SPEC_VERSION: constants2.SEMVER_SPEC_VERSION,
9544
+ RELEASE_TYPES: constants2.RELEASE_TYPES,
9545
9545
  compareIdentifiers: identifiers.compareIdentifiers,
9546
9546
  rcompareIdentifiers: identifiers.rcompareIdentifiers
9547
9547
  };
@@ -11269,6 +11269,24 @@ var SpendRequestResource = class {
11269
11269
  }
11270
11270
  return res;
11271
11271
  }
11272
+ list() {
11273
+ return this.listSpendRequests();
11274
+ }
11275
+ async listSpendRequests() {
11276
+ const { status, data, rawBody } = await this.apiFetch({
11277
+ method: "GET",
11278
+ url: this.spendRequestsEndpoint
11279
+ });
11280
+ if (status < 200 || status >= 300) {
11281
+ throw new LinkApiError(
11282
+ `Failed to list spend requests (${status}): ${extractApiError(data, rawBody)}`,
11283
+ { status, rawBody, details: data }
11284
+ );
11285
+ }
11286
+ const body = data;
11287
+ const items = body?.data ?? [];
11288
+ return items.map(normalizeSpendRequest);
11289
+ }
11272
11290
  create(params) {
11273
11291
  return this.createSpendRequest(params);
11274
11292
  }
@@ -11450,9 +11468,138 @@ var UserInfoResource = class {
11450
11468
  };
11451
11469
  }
11452
11470
  };
11471
+ var EXPIRY_BUFFER_MS = 3e4;
11472
+ var WebBotAuthResource = class {
11473
+ verbose;
11474
+ getAccessToken;
11475
+ fetchImpl;
11476
+ credentialsEndpoint;
11477
+ logger;
11478
+ cache = /* @__PURE__ */ new Map();
11479
+ constructor(options) {
11480
+ const config = resolveLinkSdkConfig(options);
11481
+ this.verbose = config.verbose;
11482
+ this.getAccessToken = config.getAccessToken;
11483
+ this.fetchImpl = requireFetchImplementation(config);
11484
+ this.credentialsEndpoint = `${config.apiBaseUrl}/v1/agent_identity/credentials`;
11485
+ this.logger = config.logger;
11486
+ }
11487
+ async rawFetch(opts) {
11488
+ if (this.verbose) {
11489
+ const redactedHeaders = { ...opts.headers };
11490
+ if (redactedHeaders.Authorization)
11491
+ redactedHeaders.Authorization = "Bearer <redacted>";
11492
+ this.logger.debug(`> ${opts.method} ${opts.url}`);
11493
+ this.logger.debug(` Headers: ${JSON.stringify(redactedHeaders)}`);
11494
+ if (opts.body) this.logger.debug(opts.body);
11495
+ }
11496
+ const fetchOpts = {
11497
+ method: opts.method,
11498
+ headers: opts.headers
11499
+ };
11500
+ if (opts.body) {
11501
+ fetchOpts.body = opts.body;
11502
+ }
11503
+ let response;
11504
+ try {
11505
+ response = await this.fetchImpl(opts.url, fetchOpts);
11506
+ } catch (error) {
11507
+ throw new LinkTransportError(
11508
+ `Request failed: ${opts.method} ${opts.url}`,
11509
+ { cause: error }
11510
+ );
11511
+ }
11512
+ const rawBody = await response.text();
11513
+ let data = null;
11514
+ try {
11515
+ data = JSON.parse(rawBody);
11516
+ } catch {
11517
+ }
11518
+ if (this.verbose) {
11519
+ this.logger.debug(`< ${response.status} ${response.statusText}`);
11520
+ response.headers.forEach((value, key) => {
11521
+ this.logger.debug(` ${key}: ${value}`);
11522
+ });
11523
+ this.logger.debug(JSON.stringify(data, null, 2) ?? rawBody);
11524
+ }
11525
+ return { status: response.status, data, rawBody };
11526
+ }
11527
+ async apiFetch(opts) {
11528
+ const token = await this.getAccessToken();
11529
+ const authedOpts = {
11530
+ ...opts,
11531
+ headers: { ...opts.headers, Authorization: `Bearer ${token}` }
11532
+ };
11533
+ const res = await this.rawFetch(authedOpts);
11534
+ if (res.status === 401) {
11535
+ const refreshedToken = await this.getAccessToken({ forceRefresh: true });
11536
+ authedOpts.headers.Authorization = `Bearer ${refreshedToken}`;
11537
+ return this.rawFetch(authedOpts);
11538
+ }
11539
+ return res;
11540
+ }
11541
+ /**
11542
+ * Returns Web Bot Auth signature headers for the given URL's authority.
11543
+ *
11544
+ * Pass the full merchant URL (e.g. `https://merchant.com/checkout`). The
11545
+ * authority (hostname) is extracted and used as the cache key — repeated
11546
+ * calls for the same domain within the 10-minute signature window are served
11547
+ * from cache without a network round-trip.
11548
+ *
11549
+ * Attach the returned `signature` and `signature_input` values as the
11550
+ * `Signature` and `Signature-Input` HTTP headers on outbound requests to
11551
+ * the merchant site.
11552
+ *
11553
+ * @throws {LinkSdkError} if `url` is not a valid URL
11554
+ * @throws {LinkSdkError} if the credentials response is missing the web_bot_auth block
11555
+ * @throws {LinkApiError} if the credentials endpoint returns a non-2xx status
11556
+ * @throws {LinkTransportError} if the network request fails
11557
+ */
11558
+ async getHeaders(url) {
11559
+ let authority;
11560
+ try {
11561
+ authority = new URL(url).hostname;
11562
+ } catch {
11563
+ throw new LinkSdkError(`Invalid URL: ${url}`);
11564
+ }
11565
+ const cached = this.cache.get(authority);
11566
+ if (cached && Date.now() < cached.expiresAt - EXPIRY_BUFFER_MS) {
11567
+ return cached.block;
11568
+ }
11569
+ const { status, data, rawBody } = await this.apiFetch({
11570
+ method: "POST",
11571
+ url: this.credentialsEndpoint,
11572
+ headers: { "Content-Type": "application/json" },
11573
+ body: JSON.stringify({ url })
11574
+ });
11575
+ if (status < 200 || status >= 300) {
11576
+ const body2 = data;
11577
+ const msg = body2?.error ?? body2?.message ?? (rawBody || "unknown error");
11578
+ throw new LinkApiError(
11579
+ `Failed to get web bot auth headers (${status}): ${msg}`,
11580
+ { status, rawBody, details: data }
11581
+ );
11582
+ }
11583
+ const body = data;
11584
+ const webBotAuth = body?.web_bot_auth;
11585
+ if (!webBotAuth) {
11586
+ throw new LinkSdkError(
11587
+ `Credentials response missing web_bot_auth block (status ${status})`
11588
+ );
11589
+ }
11590
+ const expiresAt = Date.parse(webBotAuth.expires_at);
11591
+ if (Number.isNaN(expiresAt)) {
11592
+ throw new LinkSdkError(
11593
+ `Credentials response has invalid expires_at: ${webBotAuth.expires_at}`
11594
+ );
11595
+ }
11596
+ this.cache.set(authority, { block: webBotAuth, expiresAt });
11597
+ return webBotAuth;
11598
+ }
11599
+ };
11453
11600
 
11454
11601
  // src/cli.tsx
11455
- import { Cli as Cli9 } from "incur";
11602
+ import { Cli as Cli10 } from "incur";
11456
11603
 
11457
11604
  // src/commands/auth/index.tsx
11458
11605
  import { Cli } from "incur";
@@ -11499,6 +11646,41 @@ async function renderInteractive(element, getResult) {
11499
11646
  }
11500
11647
  }
11501
11648
 
11649
+ // src/utils/sanitize-text.ts
11650
+ import stripAnsi from "strip-ansi";
11651
+ var CONTROL_CHAR_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f\r]/g;
11652
+ var NEEDS_SANITIZE_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f\r\x1b]/;
11653
+ function sanitizeText(input) {
11654
+ if (!input) {
11655
+ return "";
11656
+ }
11657
+ if (!NEEDS_SANITIZE_RE.test(input)) {
11658
+ return input;
11659
+ }
11660
+ return stripAnsi(input).replace(CONTROL_CHAR_RE, "");
11661
+ }
11662
+ function sanitizeDeep(value) {
11663
+ if (value === null || value === void 0) {
11664
+ return value;
11665
+ }
11666
+ if (typeof value === "string") {
11667
+ return sanitizeText(value);
11668
+ }
11669
+ if (Array.isArray(value)) {
11670
+ return value.map(sanitizeDeep);
11671
+ }
11672
+ if (typeof value === "object") {
11673
+ const result = {};
11674
+ const keys = Object.keys(value);
11675
+ for (let i = 0; i < keys.length; i++) {
11676
+ const k = keys[i];
11677
+ result[k] = sanitizeDeep(value[k]);
11678
+ }
11679
+ return result;
11680
+ }
11681
+ return value;
11682
+ }
11683
+
11502
11684
  // src/commands/auth/login.tsx
11503
11685
  import { Box, Text, useInput } from "ink";
11504
11686
  import Spinner from "ink-spinner";
@@ -11723,7 +11905,12 @@ import { z } from "incur";
11723
11905
  var loginOptions = z.object({
11724
11906
  clientName: z.string().default("Link CLI").describe(
11725
11907
  "Agent or app name shown in the Link app when approving the device connection"
11726
- )
11908
+ ),
11909
+ interval: z.coerce.number().default(0).describe(
11910
+ "Poll interval in seconds. When > 0, polls until authenticated or timeout is reached, yielding status on each attempt."
11911
+ ),
11912
+ maxAttempts: z.coerce.number().default(0).describe("Max poll attempts. 0 = unlimited (use timeout instead)."),
11913
+ timeout: z.coerce.number().default(300).describe("Polling timeout in seconds.")
11727
11914
  });
11728
11915
  var statusOptions = z.object({
11729
11916
  interval: z.coerce.number().default(0).describe(
@@ -11736,47 +11923,65 @@ var statusOptions = z.object({
11736
11923
  // src/commands/auth/status.tsx
11737
11924
  import { Box as Box3, Text as Text3 } from "ink";
11738
11925
  import { useEffect as useEffect3, useState as useState3 } from "react";
11926
+
11927
+ // src/commands/auth/utils.ts
11928
+ function resolveAuthInfo(envAccessToken2, authStorage2) {
11929
+ if (envAccessToken2) {
11930
+ return {
11931
+ authenticated: true,
11932
+ source: "env",
11933
+ tokenPreview: `${envAccessToken2.substring(0, 20)}...`,
11934
+ tokenType: "Bearer"
11935
+ };
11936
+ }
11937
+ const auth = authStorage2.getAuth();
11938
+ const credentialsPath = authStorage2.getPath();
11939
+ if (auth) {
11940
+ return {
11941
+ authenticated: true,
11942
+ source: "storage",
11943
+ tokenPreview: `${auth.access_token.substring(0, 20)}...`,
11944
+ tokenType: auth.token_type,
11945
+ credentialsPath
11946
+ };
11947
+ }
11948
+ return { authenticated: false, source: "storage", credentialsPath };
11949
+ }
11950
+
11951
+ // src/commands/auth/status.tsx
11739
11952
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
11740
11953
  var AuthStatus = ({
11741
11954
  authStorage: authStorage2 = storage,
11955
+ envAccessToken: envAccessToken2,
11742
11956
  onComplete
11743
11957
  }) => {
11744
- const storage2 = authStorage2;
11745
11958
  const [checked, setChecked] = useState3(false);
11746
- const [authenticated, setAuthenticated] = useState3(false);
11747
- const [tokenPreview, setTokenPreview] = useState3("");
11748
- const [tokenType, setTokenType] = useState3("");
11749
- const [credentialsPath, setCredentialsPath] = useState3("");
11750
11959
  useEffect3(() => {
11751
- const auth = storage2.getAuth();
11752
- const credentialsPath2 = storage2.getPath();
11753
- if (auth) {
11754
- setAuthenticated(true);
11755
- setTokenPreview(`${auth.access_token.substring(0, 20)}...`);
11756
- setTokenType(auth.token_type);
11757
- }
11758
- setCredentialsPath(credentialsPath2);
11759
11960
  setChecked(true);
11760
11961
  setTimeout(onComplete, DISPLAY_DELAY_MS);
11761
- }, [onComplete, storage2]);
11962
+ }, [onComplete]);
11762
11963
  if (!checked) {
11763
11964
  return null;
11764
11965
  }
11765
- if (authenticated) {
11966
+ const info = resolveAuthInfo(envAccessToken2, authStorage2);
11967
+ if (info.authenticated) {
11766
11968
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
11767
11969
  /* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u2713 Authenticated" }),
11768
11970
  /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
11769
11971
  /* @__PURE__ */ jsxs3(Text3, { children: [
11770
11972
  "Access token: ",
11771
- /* @__PURE__ */ jsx3(Text3, { bold: true, children: tokenPreview })
11973
+ /* @__PURE__ */ jsx3(Text3, { bold: true, children: info.tokenPreview })
11772
11974
  ] }),
11773
11975
  /* @__PURE__ */ jsxs3(Text3, { children: [
11774
11976
  "Token type: ",
11775
- /* @__PURE__ */ jsx3(Text3, { bold: true, children: tokenType })
11977
+ /* @__PURE__ */ jsx3(Text3, { bold: true, children: info.tokenType })
11776
11978
  ] }),
11777
- /* @__PURE__ */ jsxs3(Text3, { children: [
11979
+ info.source === "env" ? /* @__PURE__ */ jsxs3(Text3, { children: [
11980
+ "Source: ",
11981
+ /* @__PURE__ */ jsx3(Text3, { bold: true, children: "LINK_ACCESS_TOKEN" })
11982
+ ] }) : /* @__PURE__ */ jsxs3(Text3, { children: [
11778
11983
  "Credentials: ",
11779
- /* @__PURE__ */ jsx3(Text3, { bold: true, children: credentialsPath })
11984
+ /* @__PURE__ */ jsx3(Text3, { bold: true, children: info.credentialsPath })
11780
11985
  ] })
11781
11986
  ] })
11782
11987
  ] });
@@ -11786,14 +11991,55 @@ var AuthStatus = ({
11786
11991
  /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: 'Run "link-cli auth login" to authenticate' }),
11787
11992
  /* @__PURE__ */ jsx3(Box3, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsxs3(Text3, { children: [
11788
11993
  "Credentials: ",
11789
- /* @__PURE__ */ jsx3(Text3, { bold: true, children: credentialsPath })
11994
+ /* @__PURE__ */ jsx3(Text3, { bold: true, children: info.credentialsPath })
11790
11995
  ] }) })
11791
11996
  ] });
11792
11997
  };
11793
11998
 
11794
11999
  // src/commands/auth/index.tsx
11795
12000
  import { jsx as jsx4 } from "react/jsx-runtime";
11796
- function createAuthCli(authResource, getUpdateInfo2, authStorage2) {
12001
+ async function* pollAuthStatus(authResource, storage2, opts, update) {
12002
+ for await (const result of pollUntil({
12003
+ fn: async () => {
12004
+ const pending = storage2.getPendingDeviceAuth();
12005
+ if (pending && !storage2.isAuthenticated()) {
12006
+ const tokens = await authResource.pollDeviceAuth(pending.device_code);
12007
+ if (tokens) {
12008
+ storage2.setAuth(tokens);
12009
+ storage2.clearPendingDeviceAuth();
12010
+ }
12011
+ }
12012
+ const auth = storage2.getAuth();
12013
+ if (auth) {
12014
+ return {
12015
+ authenticated: true,
12016
+ access_token: `${auth.access_token.substring(0, 20)}...`,
12017
+ token_type: auth.token_type,
12018
+ credentials_path: storage2.getPath(),
12019
+ ...update && { update }
12020
+ };
12021
+ }
12022
+ const currentPending = storage2.getPendingDeviceAuth();
12023
+ return {
12024
+ authenticated: false,
12025
+ credentials_path: storage2.getPath(),
12026
+ ...update && { update },
12027
+ ...currentPending ? {
12028
+ pending: true,
12029
+ verification_url: currentPending.verification_url,
12030
+ phrase: currentPending.phrase
12031
+ } : {}
12032
+ };
12033
+ },
12034
+ isTerminal: (status) => status.authenticated,
12035
+ interval: opts.interval,
12036
+ maxAttempts: opts.maxAttempts,
12037
+ timeout: opts.timeout
12038
+ })) {
12039
+ yield result.value;
12040
+ }
12041
+ }
12042
+ function createAuthCli(authResource, getUpdateInfo2, authStorage2, envAccessToken2) {
11797
12043
  const storage2 = authStorage2 ?? storage;
11798
12044
  const cli2 = Cli.create("auth", {
11799
12045
  description: "Authentication commands"
@@ -11833,16 +12079,30 @@ function createAuthCli(authResource, getUpdateInfo2, authStorage2) {
11833
12079
  verification_url: authRequest.verification_url_complete,
11834
12080
  phrase: authRequest.user_code
11835
12081
  });
11836
- yield {
12082
+ const interval = c.options.interval;
12083
+ if (interval <= 0) {
12084
+ yield sanitizeDeep({
12085
+ verification_url: authRequest.verification_url_complete,
12086
+ phrase: authRequest.user_code,
12087
+ instruction: "Present the verification_url to the user and ask them to approve in the Link app. Then call `auth status --interval 5 --max-attempts 60` to poll until authenticated. Do not wait for the user to reply \u2014 start polling immediately.",
12088
+ _next: {
12089
+ command: "auth status --interval 5 --max-attempts 60",
12090
+ poll_interval_seconds: authRequest.interval,
12091
+ until: "authenticated is true"
12092
+ }
12093
+ });
12094
+ return;
12095
+ }
12096
+ yield sanitizeDeep({
11837
12097
  verification_url: authRequest.verification_url_complete,
11838
12098
  phrase: authRequest.user_code,
11839
- instruction: "Present the verification_url to the user and ask them to approve in the Link app. Then call `auth status --interval 5 --max-attempts 60` to poll until authenticated. Do not wait for the user to reply \u2014 start polling immediately.",
11840
- _next: {
11841
- command: "auth status --interval 5 --max-attempts 60",
11842
- poll_interval_seconds: authRequest.interval,
11843
- until: "authenticated is true"
11844
- }
11845
- };
12099
+ instruction: "Present the verification_url to the user and ask them to approve in the Link app. Polling has started automatically \u2014 no further action needed."
12100
+ });
12101
+ yield* pollAuthStatus(authResource, storage2, {
12102
+ interval,
12103
+ maxAttempts: c.options.maxAttempts,
12104
+ timeout: c.options.timeout
12105
+ });
11846
12106
  }
11847
12107
  });
11848
12108
  cli2.command("logout", {
@@ -11895,63 +12155,55 @@ function createAuthCli(authResource, getUpdateInfo2, authStorage2) {
11895
12155
  } : void 0;
11896
12156
  if (!c.agent && !c.formatExplicit) {
11897
12157
  return renderInteractive(
11898
- /* @__PURE__ */ jsx4(AuthStatus, { authStorage: storage2, onComplete: () => {
11899
- } }),
12158
+ /* @__PURE__ */ jsx4(
12159
+ AuthStatus,
12160
+ {
12161
+ authStorage: storage2,
12162
+ envAccessToken: envAccessToken2,
12163
+ onComplete: () => {
12164
+ }
12165
+ }
12166
+ ),
11900
12167
  () => {
11901
- const auth = storage2.getAuth();
12168
+ const info = resolveAuthInfo(envAccessToken2, storage2);
12169
+ if (info.authenticated) {
12170
+ return {
12171
+ authenticated: true,
12172
+ access_token: info.tokenPreview,
12173
+ token_type: info.tokenType,
12174
+ ...info.source === "storage" && {
12175
+ credentials_path: info.credentialsPath
12176
+ },
12177
+ ...update && { update }
12178
+ };
12179
+ }
11902
12180
  return {
11903
- authenticated: !!auth,
11904
- ...auth ? {
11905
- access_token: `${auth.access_token.substring(0, 20)}...`,
11906
- token_type: auth.token_type
11907
- } : {},
11908
- credentials_path: storage2.getPath(),
12181
+ authenticated: false,
12182
+ credentials_path: info.credentialsPath,
11909
12183
  ...update && { update }
11910
12184
  };
11911
12185
  }
11912
12186
  );
11913
12187
  }
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
- } : {}
11946
- };
11947
- },
11948
- isTerminal: (status) => status.authenticated,
11949
- interval,
11950
- maxAttempts,
11951
- timeout: opts.timeout
11952
- })) {
11953
- yield result.value;
12188
+ if (envAccessToken2) {
12189
+ yield {
12190
+ authenticated: true,
12191
+ access_token: `${envAccessToken2.substring(0, 20)}...`,
12192
+ token_type: "Bearer",
12193
+ ...update && { update }
12194
+ };
12195
+ return;
11954
12196
  }
12197
+ yield* pollAuthStatus(
12198
+ authResource,
12199
+ storage2,
12200
+ {
12201
+ interval,
12202
+ maxAttempts,
12203
+ timeout: opts.timeout
12204
+ },
12205
+ update
12206
+ );
11955
12207
  }
11956
12208
  });
11957
12209
  return cli2;
@@ -13849,8 +14101,85 @@ function createPaymentMethodsCli(createResource, authStorage2) {
13849
14101
  return cli2;
13850
14102
  }
13851
14103
 
14104
+ // src/commands/serve/index.ts
14105
+ import {
14106
+ createServer
14107
+ } from "http";
14108
+ import { Cli as Cli6, z as z5 } from "incur";
14109
+ async function nodeRequestToWebRequest(req, port) {
14110
+ const body = await new Promise((resolve) => {
14111
+ const chunks = [];
14112
+ req.on("data", (chunk) => chunks.push(chunk));
14113
+ req.on("end", () => resolve(Buffer.concat(chunks)));
14114
+ });
14115
+ const headers = new Headers();
14116
+ for (const [key, val] of Object.entries(req.headers)) {
14117
+ if (val == null) continue;
14118
+ headers.set(key, Array.isArray(val) ? val.join(", ") : val);
14119
+ }
14120
+ const method = req.method ?? "GET";
14121
+ return new Request(`http://localhost:${port}${req.url ?? "/"}`, {
14122
+ method,
14123
+ headers,
14124
+ body: ["GET", "HEAD"].includes(method) ? void 0 : body.length > 0 ? new Uint8Array(body) : void 0
14125
+ });
14126
+ }
14127
+ async function sendWebResponse(webRes, res) {
14128
+ webRes.headers.forEach((val, key) => res.setHeader(key, val));
14129
+ const buffer = await webRes.arrayBuffer();
14130
+ res.writeHead(webRes.status);
14131
+ res.end(Buffer.from(buffer));
14132
+ }
14133
+ function createServeCli(rootCli) {
14134
+ return Cli6.create("serve", {
14135
+ description: "Start an HTTP server exposing link-cli as an MCP endpoint at /mcp",
14136
+ options: z5.object({
14137
+ port: z5.coerce.number().default(54321).describe("Port to listen on")
14138
+ }),
14139
+ async run(c) {
14140
+ const { port } = c.options;
14141
+ const server = createServer(
14142
+ async (req, res) => {
14143
+ res.setHeader("Access-Control-Allow-Origin", "*");
14144
+ res.setHeader(
14145
+ "Access-Control-Allow-Methods",
14146
+ "GET, POST, DELETE, OPTIONS"
14147
+ );
14148
+ res.setHeader(
14149
+ "Access-Control-Allow-Headers",
14150
+ "Content-Type, Accept, Mcp-Session-Id"
14151
+ );
14152
+ if (req.method === "OPTIONS") {
14153
+ res.writeHead(204);
14154
+ res.end();
14155
+ return;
14156
+ }
14157
+ try {
14158
+ const webReq = await nodeRequestToWebRequest(req, port);
14159
+ const webRes = await rootCli.fetch(webReq);
14160
+ await sendWebResponse(webRes, res);
14161
+ } catch (err) {
14162
+ console.error("Request handling failed:", err);
14163
+ res.writeHead(500, { "Content-Type": "application/json" });
14164
+ res.end(JSON.stringify({ error: "Internal server error" }));
14165
+ }
14166
+ }
14167
+ );
14168
+ await new Promise((resolve, reject) => {
14169
+ server.on("error", reject);
14170
+ server.listen(port, () => {
14171
+ process.stderr.write(
14172
+ `link-cli MCP server listening on http://localhost:${port}/mcp
14173
+ `
14174
+ );
14175
+ });
14176
+ });
14177
+ }
14178
+ });
14179
+ }
14180
+
13852
14181
  // src/commands/shipping-address/index.tsx
13853
- import { Cli as Cli6 } from "incur";
14182
+ import { Cli as Cli7 } from "incur";
13854
14183
 
13855
14184
  // src/commands/shipping-address/list.tsx
13856
14185
  import { Box as Box13, Text as Text14 } from "ink";
@@ -13944,7 +14273,7 @@ var ShippingAddressList = ({
13944
14273
  // src/commands/shipping-address/index.tsx
13945
14274
  import { jsx as jsx20 } from "react/jsx-runtime";
13946
14275
  function createShippingAddressCli(createResource, authStorage2) {
13947
- const cli2 = Cli6.create("shipping-address", {
14276
+ const cli2 = Cli7.create("shipping-address", {
13948
14277
  description: "Shipping address management commands"
13949
14278
  });
13950
14279
  cli2.command("list", {
@@ -13967,46 +14296,79 @@ function createShippingAddressCli(createResource, authStorage2) {
13967
14296
  }
13968
14297
 
13969
14298
  // src/commands/spend-request/index.tsx
13970
- import { Cli as Cli7, z as z7 } from "incur";
14299
+ import { Cli as Cli8, z as z8 } from "incur";
13971
14300
 
13972
14301
  // src/utils/credential-output.ts
14302
+ import { constants } from "fs";
13973
14303
  import fs4 from "fs/promises";
13974
14304
  import path6 from "path";
13975
14305
  async function writeCredentialFile(filePath, data, force) {
13976
14306
  const resolved = path6.resolve(filePath);
13977
- if (!force) {
13978
- try {
13979
- await fs4.access(resolved);
14307
+ let existing;
14308
+ try {
14309
+ existing = await fs4.lstat(resolved);
14310
+ } catch (err) {
14311
+ if (err.code !== "ENOENT") throw err;
14312
+ }
14313
+ if (existing?.isSymbolicLink()) {
14314
+ throw new Error(
14315
+ `OUTPUT_FILE_SYMLINK: ${resolved} is a symbolic link. Refusing to write credentials through it.`
14316
+ );
14317
+ }
14318
+ if (existing) {
14319
+ if (!force) {
13980
14320
  throw new Error(
13981
14321
  `OUTPUT_FILE_EXISTS: ${resolved} already exists. Use --force to overwrite.`
13982
14322
  );
13983
- } catch (err) {
13984
- if (err.code !== "ENOENT") throw err;
13985
14323
  }
14324
+ await fs4.unlink(resolved);
14325
+ }
14326
+ const noFollowFlag = constants.O_NOFOLLOW ?? 0;
14327
+ let handle;
14328
+ try {
14329
+ handle = await fs4.open(
14330
+ resolved,
14331
+ constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | noFollowFlag,
14332
+ 384
14333
+ );
14334
+ } catch (err) {
14335
+ const code = err.code;
14336
+ if (code === "EEXIST") {
14337
+ throw new Error(
14338
+ `OUTPUT_FILE_EXISTS: ${resolved} already exists. Use --force to overwrite.`
14339
+ );
14340
+ }
14341
+ if (code === "ELOOP") {
14342
+ throw new Error(
14343
+ `OUTPUT_FILE_SYMLINK: ${resolved} is a symbolic link. Refusing to write credentials through it.`
14344
+ );
14345
+ }
14346
+ throw err;
14347
+ }
14348
+ try {
14349
+ await handle.write(JSON.stringify(data, null, 2));
14350
+ } finally {
14351
+ await handle.close();
13986
14352
  }
13987
- await fs4.writeFile(resolved, JSON.stringify(data, null, 2), {
13988
- mode: 384
13989
- });
13990
- await fs4.chmod(resolved, 384);
13991
14353
  return resolved;
13992
14354
  }
13993
14355
 
13994
14356
  // src/utils/line-item-parser.ts
13995
- import { z as z5 } from "zod";
13996
- var LineItemSchema = z5.object({
13997
- name: z5.string(),
13998
- url: z5.string().optional(),
13999
- image_url: z5.string().optional(),
14000
- description: z5.string().optional(),
14001
- sku: z5.string().optional(),
14002
- quantity: z5.coerce.number().optional(),
14003
- unit_amount: z5.coerce.number().optional(),
14004
- product_url: z5.string().optional()
14357
+ import { z as z6 } from "zod";
14358
+ var LineItemSchema = z6.object({
14359
+ name: z6.string(),
14360
+ url: z6.string().optional(),
14361
+ image_url: z6.string().optional(),
14362
+ description: z6.string().optional(),
14363
+ sku: z6.string().optional(),
14364
+ quantity: z6.coerce.number().optional(),
14365
+ unit_amount: z6.coerce.number().optional(),
14366
+ product_url: z6.string().optional()
14005
14367
  }).strict();
14006
- var TotalSchema = z5.object({
14007
- type: z5.string(),
14008
- display_text: z5.string(),
14009
- amount: z5.coerce.number()
14368
+ var TotalSchema = z6.object({
14369
+ type: z6.string(),
14370
+ display_text: z6.string(),
14371
+ amount: z6.coerce.number()
14010
14372
  }).strict();
14011
14373
  function parseKvString(raw) {
14012
14374
  const result = {};
@@ -14036,7 +14398,7 @@ function parseLineItemFlag(raw) {
14036
14398
  try {
14037
14399
  return LineItemSchema.parse(obj);
14038
14400
  } catch (err) {
14039
- if (err instanceof z5.ZodError)
14401
+ if (err instanceof z6.ZodError)
14040
14402
  throw formatZodError(err, "Line item", LineItemSchema);
14041
14403
  throw err;
14042
14404
  }
@@ -14046,7 +14408,7 @@ function parseTotalFlag(raw) {
14046
14408
  try {
14047
14409
  return TotalSchema.parse(obj);
14048
14410
  } catch (err) {
14049
- if (err instanceof z5.ZodError)
14411
+ if (err instanceof z6.ZodError)
14050
14412
  throw formatZodError(err, "Total", TotalSchema);
14051
14413
  throw err;
14052
14414
  }
@@ -14360,11 +14722,68 @@ var CreateSpendRequest = ({
14360
14722
  ] });
14361
14723
  };
14362
14724
 
14363
- // src/commands/spend-request/request-approval.tsx
14364
- import { Box as Box17, Text as Text18 } from "ink";
14725
+ // src/commands/spend-request/list.tsx
14726
+ import { Box as Box17, Text as Text18, useApp as useApp4 } from "ink";
14365
14727
  import Spinner9 from "ink-spinner";
14366
- import { useCallback as useCallback7, useEffect as useEffect10, useState as useState10 } from "react";
14728
+ import { useCallback as useCallback7 } from "react";
14367
14729
  import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
14730
+ var SpendRequestList = ({
14731
+ repository,
14732
+ onComplete
14733
+ }) => {
14734
+ const { exit } = useApp4();
14735
+ const action = useCallback7(
14736
+ () => repository.listSpendRequests(),
14737
+ [repository]
14738
+ );
14739
+ const wrappedOnComplete = useCallback7(
14740
+ (result) => {
14741
+ onComplete(result);
14742
+ exit();
14743
+ },
14744
+ [onComplete, exit]
14745
+ );
14746
+ const {
14747
+ status,
14748
+ data: requests,
14749
+ error
14750
+ } = useAsyncAction(action, wrappedOnComplete);
14751
+ if (status === "loading") {
14752
+ return /* @__PURE__ */ jsx24(Box17, { children: /* @__PURE__ */ jsxs17(Text18, { color: "cyan", children: [
14753
+ /* @__PURE__ */ jsx24(Spinner9, { type: "dots" }),
14754
+ " Loading spend requests..."
14755
+ ] }) });
14756
+ }
14757
+ if (status === "error") {
14758
+ return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
14759
+ /* @__PURE__ */ jsx24(Text18, { color: "red", children: "\u2717 Failed to load spend requests" }),
14760
+ /* @__PURE__ */ jsx24(Text18, { color: "red", children: error })
14761
+ ] });
14762
+ }
14763
+ if (!requests || requests.length === 0) {
14764
+ return /* @__PURE__ */ jsx24(Box17, { children: /* @__PURE__ */ jsx24(Text18, { dimColor: true, children: "No active spend requests found" }) });
14765
+ }
14766
+ return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
14767
+ /* @__PURE__ */ jsx24(Text18, { bold: true, children: "Active Spend Requests" }),
14768
+ /* @__PURE__ */ jsx24(Box17, { flexDirection: "column", marginTop: 1, children: requests.map((sr) => {
14769
+ const statusColor = sr.status === "approved" ? "green" : sr.status === "pending_approval" ? "yellow" : "white";
14770
+ const amount = sr.amount != null ? `$${(sr.amount / 100).toFixed(2)} ${(sr.currency ?? "usd").toUpperCase()}` : "";
14771
+ return /* @__PURE__ */ jsx24(Box17, { paddingX: 2, children: /* @__PURE__ */ jsxs17(Text18, { children: [
14772
+ /* @__PURE__ */ jsx24(Text18, { dimColor: true, children: sr.id }),
14773
+ " ",
14774
+ /* @__PURE__ */ jsx24(Text18, { color: statusColor, children: sr.status }),
14775
+ sr.merchant_name ? ` ${sr.merchant_name}` : "",
14776
+ amount ? ` ${amount}` : ""
14777
+ ] }) }, sr.id);
14778
+ }) })
14779
+ ] });
14780
+ };
14781
+
14782
+ // src/commands/spend-request/request-approval.tsx
14783
+ import { Box as Box18, Text as Text19 } from "ink";
14784
+ import Spinner10 from "ink-spinner";
14785
+ import { useCallback as useCallback8, useEffect as useEffect10, useState as useState10 } from "react";
14786
+ import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
14368
14787
  var RequestApproval = ({
14369
14788
  repository,
14370
14789
  id,
@@ -14374,8 +14793,8 @@ var RequestApproval = ({
14374
14793
  const [approvalUrl, setApprovalUrl] = useState10("");
14375
14794
  const [result, setResult] = useState10(null);
14376
14795
  const [error, setError] = useState10("");
14377
- const onSuccess = useCallback7((r) => setResult(r), []);
14378
- const onError = useCallback7((msg) => setError(msg), []);
14796
+ const onSuccess = useCallback8((r) => setResult(r), []);
14797
+ const onError = useCallback8((msg) => setError(msg), []);
14379
14798
  useApprovalPolling({
14380
14799
  status,
14381
14800
  setStatus,
@@ -14400,46 +14819,46 @@ var RequestApproval = ({
14400
14819
  request();
14401
14820
  }, [repository, id]);
14402
14821
  if (status === "requesting") {
14403
- return /* @__PURE__ */ jsx24(Box17, { children: /* @__PURE__ */ jsxs17(Text18, { color: "cyan", children: [
14404
- /* @__PURE__ */ jsx24(Spinner9, { type: "dots" }),
14822
+ return /* @__PURE__ */ jsx25(Box18, { children: /* @__PURE__ */ jsxs18(Text19, { color: "cyan", children: [
14823
+ /* @__PURE__ */ jsx25(Spinner10, { type: "dots" }),
14405
14824
  " Requesting approval..."
14406
14825
  ] }) });
14407
14826
  }
14408
14827
  if (status === "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 })
14828
+ return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
14829
+ /* @__PURE__ */ jsx25(Text19, { color: "red", children: "\u2717 Failed to request approval" }),
14830
+ /* @__PURE__ */ jsx25(Text19, { color: "red", children: error })
14412
14831
  ] });
14413
14832
  }
14414
14833
  if (status === "success") {
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: [
14834
+ return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
14835
+ /* @__PURE__ */ jsx25(Text19, { color: "green", children: "\u2713 Approval completed" }),
14836
+ /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14837
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14419
14838
  "ID: ",
14420
- /* @__PURE__ */ jsx24(Text18, { bold: true, children: result?.id })
14839
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: result?.id })
14421
14840
  ] }),
14422
- /* @__PURE__ */ jsxs17(Text18, { children: [
14841
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14423
14842
  "Status: ",
14424
- /* @__PURE__ */ jsx24(Text18, { bold: true, children: result?.status })
14843
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: result?.status })
14425
14844
  ] }),
14426
- /* @__PURE__ */ jsxs17(Text18, { children: [
14845
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14427
14846
  "Amount:",
14428
14847
  " ",
14429
- /* @__PURE__ */ jsx24(Text18, { bold: true, children: result?.amount != null ? `${result.amount} ${result.currency?.toUpperCase() ?? ""}`.trim() : "N/A" })
14848
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: result?.amount != null ? `${result.amount} ${result.currency?.toUpperCase() ?? ""}`.trim() : "N/A" })
14430
14849
  ] }),
14431
- /* @__PURE__ */ jsxs17(Text18, { children: [
14850
+ /* @__PURE__ */ jsxs18(Text19, { children: [
14432
14851
  "Merchant: ",
14433
- /* @__PURE__ */ jsx24(Text18, { bold: true, children: result?.merchant_name })
14852
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: result?.merchant_name })
14434
14853
  ] }),
14435
- result?.credential_type === "shared_payment_token" && result.shared_payment_token && /* @__PURE__ */ jsxs17(Text18, { children: [
14854
+ result?.credential_type === "shared_payment_token" && result.shared_payment_token && /* @__PURE__ */ jsxs18(Text19, { children: [
14436
14855
  "Token: ",
14437
- /* @__PURE__ */ jsx24(Text18, { bold: true, children: result.shared_payment_token.id })
14856
+ /* @__PURE__ */ jsx25(Text19, { bold: true, children: result.shared_payment_token.id })
14438
14857
  ] })
14439
14858
  ] })
14440
14859
  ] });
14441
14860
  }
14442
- return /* @__PURE__ */ jsx24(
14861
+ return /* @__PURE__ */ jsx25(
14443
14862
  ApprovalWaitingView,
14444
14863
  {
14445
14864
  status,
@@ -14449,10 +14868,10 @@ var RequestApproval = ({
14449
14868
  };
14450
14869
 
14451
14870
  // src/commands/spend-request/retrieve.tsx
14452
- import { Box as Box18, Text as Text19 } from "ink";
14453
- import Spinner10 from "ink-spinner";
14871
+ import { Box as Box19, Text as Text20 } from "ink";
14872
+ import Spinner11 from "ink-spinner";
14454
14873
  import { useEffect as useEffect11, useRef as useRef5, useState as useState11 } from "react";
14455
- import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
14874
+ import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
14456
14875
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
14457
14876
  "approved",
14458
14877
  "denied",
@@ -14576,181 +14995,181 @@ var RetrieveSpendRequest = ({
14576
14995
  };
14577
14996
  }, [phase, repository, id, include, timeout, onComplete]);
14578
14997
  if (phase === "fetching") {
14579
- return /* @__PURE__ */ jsx25(Box18, { children: /* @__PURE__ */ jsxs18(Text19, { color: "cyan", children: [
14580
- /* @__PURE__ */ jsx25(Spinner10, { type: "dots" }),
14998
+ return /* @__PURE__ */ jsx26(Box19, { children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
14999
+ /* @__PURE__ */ jsx26(Spinner11, { type: "dots" }),
14581
15000
  " Retrieving spend request ",
14582
15001
  id,
14583
15002
  "..."
14584
15003
  ] }) });
14585
15004
  }
14586
15005
  if (phase === "error") {
14587
- return /* @__PURE__ */ jsx25(Box18, { flexDirection: "column", children: /* @__PURE__ */ jsxs18(Text19, { color: "red", children: [
15006
+ return /* @__PURE__ */ jsx26(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsxs19(Text20, { color: "red", children: [
14588
15007
  "\u2717 ",
14589
15008
  error
14590
15009
  ] }) });
14591
15010
  }
14592
15011
  if (phase === "timeout") {
14593
- return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
14594
- /* @__PURE__ */ jsxs18(Text19, { color: "yellow", children: [
15012
+ return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
15013
+ /* @__PURE__ */ jsxs19(Text20, { color: "yellow", children: [
14595
15014
  "\u2717 Timed out waiting for approval after ",
14596
15015
  timeout,
14597
15016
  "s"
14598
15017
  ] }),
14599
- request && /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14600
- /* @__PURE__ */ jsxs18(Text19, { children: [
15018
+ request && /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
15019
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14601
15020
  "ID: ",
14602
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request.id })
15021
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request.id })
14603
15022
  ] }),
14604
- /* @__PURE__ */ jsxs18(Text19, { children: [
15023
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14605
15024
  "Status: ",
14606
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request.status })
15025
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request.status })
14607
15026
  ] })
14608
15027
  ] })
14609
15028
  ] });
14610
15029
  }
14611
15030
  if (phase === "polling") {
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" }),
15031
+ return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
15032
+ /* @__PURE__ */ jsx26(Box19, { children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
15033
+ /* @__PURE__ */ jsx26(Spinner11, { type: "dots" }),
14615
15034
  " Awaiting approval... (",
14616
15035
  elapsed,
14617
15036
  "s elapsed)"
14618
15037
  ] }) }),
14619
- request?.approval_url && /* @__PURE__ */ jsx25(Box18, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsxs18(Text19, { dimColor: true, children: [
15038
+ request?.approval_url && /* @__PURE__ */ jsx26(Box19, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsxs19(Text20, { dimColor: true, children: [
14620
15039
  "Approval URL: ",
14621
- /* @__PURE__ */ jsx25(Text19, { color: "cyan", children: request.approval_url })
15040
+ /* @__PURE__ */ jsx26(Text20, { color: "cyan", children: request.approval_url })
14622
15041
  ] }) })
14623
15042
  ] });
14624
15043
  }
14625
15044
  if (phase === "finalized") {
14626
- return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
14627
- /* @__PURE__ */ jsxs18(Text19, { color: "yellow", children: [
15045
+ return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
15046
+ /* @__PURE__ */ jsxs19(Text20, { color: "yellow", children: [
14628
15047
  "Spend request reached terminal status: ",
14629
15048
  request?.status
14630
15049
  ] }),
14631
- /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
14632
- /* @__PURE__ */ jsxs18(Text19, { children: [
15050
+ /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
15051
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14633
15052
  "ID: ",
14634
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.id })
15053
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.id })
14635
15054
  ] }),
14636
- /* @__PURE__ */ jsxs18(Text19, { children: [
15055
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14637
15056
  "Status: ",
14638
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.status })
15057
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.status })
14639
15058
  ] })
14640
15059
  ] })
14641
15060
  ] });
14642
15061
  }
14643
15062
  if (phase === "declined") {
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: [
15063
+ return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
15064
+ /* @__PURE__ */ jsx26(Text20, { color: "red", children: "\u2717 Spend request declined" }),
15065
+ /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
15066
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14648
15067
  "ID: ",
14649
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.id })
15068
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.id })
14650
15069
  ] }),
14651
- /* @__PURE__ */ jsxs18(Text19, { children: [
15070
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14652
15071
  "Status:",
14653
15072
  " ",
14654
- /* @__PURE__ */ jsx25(Text19, { bold: true, color: "red", children: request?.status })
15073
+ /* @__PURE__ */ jsx26(Text20, { bold: true, color: "red", children: request?.status })
14655
15074
  ] }),
14656
- /* @__PURE__ */ jsxs18(Text19, { children: [
15075
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14657
15076
  "Amount:",
14658
15077
  " ",
14659
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.amount != null ? String(request.amount) : "N/A" })
15078
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.amount != null ? String(request.amount) : "N/A" })
14660
15079
  ] }),
14661
- /* @__PURE__ */ jsxs18(Text19, { children: [
15080
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14662
15081
  "Merchant: ",
14663
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.merchant_name })
15082
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.merchant_name })
14664
15083
  ] })
14665
15084
  ] })
14666
15085
  ] });
14667
15086
  }
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: [
15087
+ return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
15088
+ /* @__PURE__ */ jsx26(Text20, { color: "green", children: "\u2713 Spend request approved" }),
15089
+ /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
15090
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14672
15091
  "ID: ",
14673
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.id })
15092
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.id })
14674
15093
  ] }),
14675
- /* @__PURE__ */ jsxs18(Text19, { children: [
15094
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14676
15095
  "Status:",
14677
15096
  " ",
14678
- /* @__PURE__ */ jsx25(Text19, { bold: true, color: "green", children: request?.status })
15097
+ /* @__PURE__ */ jsx26(Text20, { bold: true, color: "green", children: request?.status })
14679
15098
  ] }),
14680
- /* @__PURE__ */ jsxs18(Text19, { children: [
15099
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14681
15100
  "Amount:",
14682
15101
  " ",
14683
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.amount != null ? String(request.amount) : "N/A" })
15102
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.amount != null ? String(request.amount) : "N/A" })
14684
15103
  ] }),
14685
- /* @__PURE__ */ jsxs18(Text19, { children: [
15104
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14686
15105
  "Merchant: ",
14687
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.merchant_name })
15106
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.merchant_name })
14688
15107
  ] }),
14689
- /* @__PURE__ */ jsxs18(Text19, { children: [
15108
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14690
15109
  "Line Items:",
14691
15110
  " ",
14692
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
15111
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
14693
15112
  ] }),
14694
- request?.shared_payment_token && /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, children: [
14695
- /* @__PURE__ */ jsxs18(Text19, { bold: true, children: [
15113
+ request?.shared_payment_token && /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", marginTop: 1, children: [
15114
+ /* @__PURE__ */ jsxs19(Text20, { bold: true, children: [
14696
15115
  "\x1B]8;;https://docs.stripe.com/agentic-commerce/concepts/shared-payment-tokens\x07",
14697
15116
  "Shared Payment Token",
14698
15117
  "\x1B]8;;\x07",
14699
15118
  ":"
14700
15119
  ] }),
14701
- /* @__PURE__ */ jsxs18(Text19, { children: [
15120
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14702
15121
  " ",
14703
15122
  "Token: ",
14704
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request.shared_payment_token.id })
15123
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request.shared_payment_token.id })
14705
15124
  ] })
14706
15125
  ] }),
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: [
15126
+ request?.card && !outputFile && /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", marginTop: 1, children: [
15127
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: "Card Details:" }),
15128
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14710
15129
  " ",
14711
15130
  "Number: ",
14712
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.card.number })
15131
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.card.number })
14713
15132
  ] }),
14714
- /* @__PURE__ */ jsxs18(Text19, { children: [
15133
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14715
15134
  " ",
14716
15135
  "Brand: ",
14717
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request?.card.brand })
15136
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.card.brand })
14718
15137
  ] }),
14719
- /* @__PURE__ */ jsxs18(Text19, { children: [
15138
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14720
15139
  " ",
14721
15140
  "Expiry:",
14722
15141
  " ",
14723
- /* @__PURE__ */ jsxs18(Text19, { bold: true, children: [
15142
+ /* @__PURE__ */ jsxs19(Text20, { bold: true, children: [
14724
15143
  String(request?.card.exp_month).padStart(2, "0"),
14725
15144
  "/",
14726
15145
  request?.card.exp_year
14727
15146
  ] })
14728
15147
  ] }),
14729
- request?.card.cvc && /* @__PURE__ */ jsxs18(Text19, { children: [
15148
+ request?.card.cvc && /* @__PURE__ */ jsxs19(Text20, { children: [
14730
15149
  " ",
14731
15150
  "CVC: ",
14732
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request.card.cvc })
15151
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request.card.cvc })
14733
15152
  ] }),
14734
- request?.card.valid_until && /* @__PURE__ */ jsxs18(Text19, { children: [
15153
+ request?.card.valid_until && /* @__PURE__ */ jsxs19(Text20, { children: [
14735
15154
  " ",
14736
15155
  "Valid Until: ",
14737
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: request.card.valid_until })
15156
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: request.card.valid_until })
14738
15157
  ] }),
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: [
15158
+ request?.card.billing_address && /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", marginTop: 1, children: [
15159
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: " Billing Address:" }),
15160
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14742
15161
  " ",
14743
15162
  request.card.billing_address.name
14744
15163
  ] }),
14745
- /* @__PURE__ */ jsxs18(Text19, { children: [
15164
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14746
15165
  " ",
14747
15166
  request.card.billing_address.line1
14748
15167
  ] }),
14749
- request.card.billing_address.line2 && /* @__PURE__ */ jsxs18(Text19, { children: [
15168
+ request.card.billing_address.line2 && /* @__PURE__ */ jsxs19(Text20, { children: [
14750
15169
  " ",
14751
15170
  request.card.billing_address.line2
14752
15171
  ] }),
14753
- /* @__PURE__ */ jsxs18(Text19, { children: [
15172
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14754
15173
  " ",
14755
15174
  [
14756
15175
  request.card.billing_address.city,
@@ -14758,18 +15177,18 @@ var RetrieveSpendRequest = ({
14758
15177
  request.card.billing_address.postal_code
14759
15178
  ].filter(Boolean).join(", ")
14760
15179
  ] }),
14761
- /* @__PURE__ */ jsxs18(Text19, { children: [
15180
+ /* @__PURE__ */ jsxs19(Text20, { children: [
14762
15181
  " ",
14763
15182
  request.card.billing_address.country
14764
15183
  ] })
14765
15184
  ] })
14766
15185
  ] }),
14767
- request?.card && outputFile && /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, children: [
14768
- outputFilePath && /* @__PURE__ */ jsxs18(Text19, { color: "green", children: [
15186
+ request?.card && outputFile && /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", marginTop: 1, children: [
15187
+ outputFilePath && /* @__PURE__ */ jsxs19(Text20, { color: "green", children: [
14769
15188
  "Card credentials written to ",
14770
- /* @__PURE__ */ jsx25(Text19, { bold: true, children: outputFilePath })
15189
+ /* @__PURE__ */ jsx26(Text20, { bold: true, children: outputFilePath })
14771
15190
  ] }),
14772
- fileError && /* @__PURE__ */ jsxs18(Text19, { color: "red", children: [
15191
+ fileError && /* @__PURE__ */ jsxs19(Text20, { color: "red", children: [
14773
15192
  "Failed to write card file: ",
14774
15193
  fileError
14775
15194
  ] })
@@ -14779,136 +15198,136 @@ var RetrieveSpendRequest = ({
14779
15198
  };
14780
15199
 
14781
15200
  // src/commands/spend-request/schema.ts
14782
- import { z as z6 } from "incur";
14783
- var createOptions = z6.object({
14784
- paymentMethodId: z6.string().describe("Payment method ID"),
14785
- credentialType: z6.enum(["shared_payment_token", "card"]).default("card").describe(
15201
+ import { z as z7 } from "incur";
15202
+ var createOptions = z7.object({
15203
+ paymentMethodId: z7.string().describe("Payment method ID"),
15204
+ credentialType: z7.enum(["shared_payment_token", "card"]).default("card").describe(
14786
15205
  '"card" for checkout forms/Stripe Elements; "shared_payment_token" for HTTP 402/machine payment flows'
14787
15206
  ),
14788
- networkId: z6.string().optional().describe(
15207
+ networkId: z7.string().optional().describe(
14789
15208
  "Network ID (required for shared_payment_token) \u2014 use `link-cli mpp decode` to extract"
14790
15209
  ),
14791
- amount: z6.coerce.number().int().positive().max(5e4).describe("Amount in cents, max 50000 ($500.00)"),
14792
- currency: z6.string().length(3).default("usd").describe("Currency code"),
14793
- merchantName: z6.string().optional().describe(
15210
+ amount: z7.coerce.number().int().positive().max(5e4).describe("Amount in cents, max 50000 ($500.00)"),
15211
+ currency: z7.string().length(3).default("usd").describe("Currency code"),
15212
+ merchantName: z7.string().optional().describe(
14794
15213
  "Merchant name (required for card; forbidden for shared_payment_token)"
14795
15214
  ),
14796
- merchantUrl: z6.string().optional().describe(
15215
+ merchantUrl: z7.string().optional().describe(
14797
15216
  "Merchant URL (required for card; forbidden for shared_payment_token)"
14798
15217
  ),
14799
- context: z6.string().min(100).describe(
15218
+ context: z7.string().min(100).describe(
14800
15219
  "Min 100 chars \u2014 describe the purchase and rationale; the user reads this when approving"
14801
15220
  ),
14802
- lineItem: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe(
15221
+ lineItem: z7.array(z7.union([z7.string(), z7.record(z7.string(), z7.unknown())])).default([]).describe(
14803
15222
  '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"'
14804
15223
  ),
14805
- total: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe(
15224
+ total: z7.array(z7.union([z7.string(), z7.record(z7.string(), z7.unknown())])).default([]).describe(
14806
15225
  '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"'
14807
15226
  ),
14808
- requestApproval: z6.boolean().default(true).describe("Request approval and poll until approved/denied/expired"),
14809
- test: z6.boolean().default(false).describe(
15227
+ requestApproval: z7.boolean().default(true).describe("Request approval and poll until approved/denied/expired"),
15228
+ test: z7.boolean().default(false).describe(
14810
15229
  "Use test mode (creates testmode credentials from test card data)"
14811
15230
  ),
14812
- outputFile: z6.string().optional().describe(
15231
+ outputFile: z7.string().optional().describe(
14813
15232
  "Write full card credentials to this file path; stdout shows redacted card data only"
14814
15233
  ),
14815
- force: z6.boolean().default(false).describe("Overwrite output file if it already exists")
15234
+ force: z7.boolean().default(false).describe("Overwrite output file if it already exists")
14816
15235
  });
14817
- var retrieveOptions = z6.object({
14818
- timeout: z6.coerce.number().default(600).describe(
15236
+ var retrieveOptions = z7.object({
15237
+ timeout: z7.coerce.number().default(600).describe(
14819
15238
  "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."
14820
15239
  ),
14821
- interval: z6.coerce.number().default(0).describe(
15240
+ interval: z7.coerce.number().default(0).describe(
14822
15241
  "Poll interval in seconds. When > 0, polls until status is terminal, timeout is reached, or max attempts are exhausted."
14823
15242
  ),
14824
- maxAttempts: z6.coerce.number().default(0).describe(
15243
+ maxAttempts: z7.coerce.number().default(0).describe(
14825
15244
  "Max poll attempts. 0 = unlimited. Exhaustion during active polling exits non-zero with POLLING_TIMEOUT."
14826
15245
  ),
14827
- include: z6.array(z6.string()).default([]).describe("Include extra data (repeatable, e.g. --include card)"),
14828
- outputFile: z6.string().optional().describe(
15246
+ include: z7.array(z7.string()).default([]).describe("Include extra data (repeatable, e.g. --include card)"),
15247
+ outputFile: z7.string().optional().describe(
14829
15248
  "Write full card credentials to this file path; stdout shows redacted card data only"
14830
15249
  ),
14831
- force: z6.boolean().default(false).describe("Overwrite output file if it already exists")
14832
- });
14833
- var updateOptions = z6.object({
14834
- paymentMethodId: z6.string().optional().describe("Payment method ID"),
14835
- amount: z6.coerce.number().optional().describe("Amount in cents"),
14836
- merchantUrl: z6.string().optional().describe("Merchant URL"),
14837
- profileId: z6.string().optional().describe("Profile ID"),
14838
- merchantId: z6.string().optional().describe("Merchant ID"),
14839
- currency: z6.string().optional().describe("Currency code"),
14840
- lineItem: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe(
15250
+ force: z7.boolean().default(false).describe("Overwrite output file if it already exists")
15251
+ });
15252
+ var updateOptions = z7.object({
15253
+ paymentMethodId: z7.string().optional().describe("Payment method ID"),
15254
+ amount: z7.coerce.number().optional().describe("Amount in cents"),
15255
+ merchantUrl: z7.string().optional().describe("Merchant URL"),
15256
+ profileId: z7.string().optional().describe("Profile ID"),
15257
+ merchantId: z7.string().optional().describe("Merchant ID"),
15258
+ currency: z7.string().optional().describe("Currency code"),
15259
+ lineItem: z7.array(z7.union([z7.string(), z7.record(z7.string(), z7.unknown())])).default([]).describe(
14841
15260
  '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"'
14842
15261
  ),
14843
- total: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe(
15262
+ total: z7.array(z7.union([z7.string(), z7.record(z7.string(), z7.unknown())])).default([]).describe(
14844
15263
  '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"'
14845
15264
  )
14846
15265
  });
14847
15266
 
14848
15267
  // src/commands/spend-request/update.tsx
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";
15268
+ import { Box as Box20, Text as Text21 } from "ink";
15269
+ import Spinner12 from "ink-spinner";
15270
+ import { useCallback as useCallback9 } from "react";
15271
+ import { jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
14853
15272
  var UpdateSpendRequest = ({
14854
15273
  repository,
14855
15274
  id,
14856
15275
  params,
14857
15276
  onComplete
14858
15277
  }) => {
14859
- const action = useCallback8(
15278
+ const action = useCallback9(
14860
15279
  () => repository.updateSpendRequest(id, params),
14861
15280
  [repository, id, params]
14862
15281
  );
14863
15282
  const { status, data: request, error } = useAsyncAction(action, onComplete);
14864
15283
  if (status === "loading") {
14865
- return /* @__PURE__ */ jsx26(Box19, { children: /* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
14866
- /* @__PURE__ */ jsx26(Spinner11, { type: "dots" }),
15284
+ return /* @__PURE__ */ jsx27(Box20, { children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
15285
+ /* @__PURE__ */ jsx27(Spinner12, { type: "dots" }),
14867
15286
  " Updating spend request ",
14868
15287
  id,
14869
15288
  "..."
14870
15289
  ] }) });
14871
15290
  }
14872
15291
  if (status === "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 })
15292
+ return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
15293
+ /* @__PURE__ */ jsx27(Text21, { color: "red", children: "\u2717 Failed to update spend request" }),
15294
+ /* @__PURE__ */ jsx27(Text21, { color: "red", children: error })
14876
15295
  ] });
14877
15296
  }
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: [
15297
+ return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
15298
+ /* @__PURE__ */ jsx27(Text21, { color: "green", children: "\u2713 Spend request updated" }),
15299
+ /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
15300
+ /* @__PURE__ */ jsxs20(Text21, { children: [
14882
15301
  "ID: ",
14883
- /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.id })
15302
+ /* @__PURE__ */ jsx27(Text21, { bold: true, children: request?.id })
14884
15303
  ] }),
14885
- /* @__PURE__ */ jsxs19(Text20, { children: [
15304
+ /* @__PURE__ */ jsxs20(Text21, { children: [
14886
15305
  "Status: ",
14887
- /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.status })
15306
+ /* @__PURE__ */ jsx27(Text21, { bold: true, children: request?.status })
14888
15307
  ] }),
14889
- /* @__PURE__ */ jsxs19(Text20, { children: [
15308
+ /* @__PURE__ */ jsxs20(Text21, { children: [
14890
15309
  "Amount:",
14891
15310
  " ",
14892
- /* @__PURE__ */ jsx26(Text20, { bold: true, children: (() => {
15311
+ /* @__PURE__ */ jsx27(Text21, { bold: true, children: (() => {
14893
15312
  const t = request?.totals.find((t2) => t2.type === "total");
14894
15313
  return t ? String(t.amount) : "N/A";
14895
15314
  })() })
14896
15315
  ] }),
14897
- /* @__PURE__ */ jsxs19(Text20, { children: [
15316
+ /* @__PURE__ */ jsxs20(Text21, { children: [
14898
15317
  "Merchant: ",
14899
- /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.merchant_name })
15318
+ /* @__PURE__ */ jsx27(Text21, { bold: true, children: request?.merchant_name })
14900
15319
  ] }),
14901
- /* @__PURE__ */ jsxs19(Text20, { children: [
15320
+ /* @__PURE__ */ jsxs20(Text21, { children: [
14902
15321
  "Line Items:",
14903
15322
  " ",
14904
- /* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
15323
+ /* @__PURE__ */ jsx27(Text21, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
14905
15324
  ] })
14906
15325
  ] })
14907
15326
  ] });
14908
15327
  };
14909
15328
 
14910
15329
  // src/commands/spend-request/index.tsx
14911
- import { jsx as jsx27 } from "react/jsx-runtime";
15330
+ import { jsx as jsx28 } from "react/jsx-runtime";
14912
15331
  async function applyOutputFile(request, outputFile, force) {
14913
15332
  if (!outputFile || !request.card) return request;
14914
15333
  const fileData = {
@@ -14927,9 +15346,24 @@ async function applyOutputFile(request, outputFile, force) {
14927
15346
  };
14928
15347
  }
14929
15348
  function createSpendRequestCli(repository, authStorage2) {
14930
- const cli2 = Cli7.create("spend-request", {
15349
+ const cli2 = Cli8.create("spend-request", {
14931
15350
  description: "Spend request management commands"
14932
15351
  });
15352
+ cli2.command("list", {
15353
+ description: "List active spend requests (created, pending_approval, approved)",
15354
+ outputPolicy: "agent-only",
15355
+ middleware: [requireAuth(authStorage2)],
15356
+ async run(c) {
15357
+ if (!c.agent && !c.formatExplicit) {
15358
+ return renderInteractive(
15359
+ /* @__PURE__ */ jsx28(SpendRequestList, { repository, onComplete: () => {
15360
+ } }),
15361
+ () => repository.listSpendRequests()
15362
+ );
15363
+ }
15364
+ return repository.listSpendRequests();
15365
+ }
15366
+ });
14933
15367
  cli2.command("create", {
14934
15368
  description: "Create a new spend request",
14935
15369
  options: createOptions,
@@ -14998,7 +15432,7 @@ function createSpendRequestCli(repository, authStorage2) {
14998
15432
  if (!c.agent && !c.formatExplicit) {
14999
15433
  let capturedResult = null;
15000
15434
  return renderInteractive(
15001
- /* @__PURE__ */ jsx27(
15435
+ /* @__PURE__ */ jsx28(
15002
15436
  CreateSpendRequest,
15003
15437
  {
15004
15438
  repository,
@@ -15043,8 +15477,8 @@ function createSpendRequestCli(repository, authStorage2) {
15043
15477
  });
15044
15478
  cli2.command("update", {
15045
15479
  description: "Update a spend request",
15046
- args: z7.object({
15047
- id: z7.string().describe("Spend request ID")
15480
+ args: z8.object({
15481
+ id: z8.string().describe("Spend request ID")
15048
15482
  }),
15049
15483
  options: updateOptions,
15050
15484
  outputPolicy: "agent-only",
@@ -15072,7 +15506,7 @@ function createSpendRequestCli(repository, authStorage2) {
15072
15506
  if (!c.agent && !c.formatExplicit) {
15073
15507
  let capturedResult = null;
15074
15508
  return renderInteractive(
15075
- /* @__PURE__ */ jsx27(
15509
+ /* @__PURE__ */ jsx28(
15076
15510
  UpdateSpendRequest,
15077
15511
  {
15078
15512
  repository,
@@ -15095,8 +15529,8 @@ function createSpendRequestCli(repository, authStorage2) {
15095
15529
  });
15096
15530
  cli2.command("request-approval", {
15097
15531
  description: "Request approval for a spend request",
15098
- args: z7.object({
15099
- id: z7.string().describe("Spend request ID")
15532
+ args: z8.object({
15533
+ id: z8.string().describe("Spend request ID")
15100
15534
  }),
15101
15535
  outputPolicy: "agent-only",
15102
15536
  async *run(c) {
@@ -15105,7 +15539,7 @@ function createSpendRequestCli(repository, authStorage2) {
15105
15539
  if (!c.agent && !c.formatExplicit) {
15106
15540
  let capturedResult = null;
15107
15541
  return renderInteractive(
15108
- /* @__PURE__ */ jsx27(
15542
+ /* @__PURE__ */ jsx28(
15109
15543
  RequestApproval,
15110
15544
  {
15111
15545
  repository,
@@ -15135,8 +15569,8 @@ function createSpendRequestCli(repository, authStorage2) {
15135
15569
  });
15136
15570
  cli2.command("retrieve", {
15137
15571
  description: "Retrieve a spend request",
15138
- args: z7.object({
15139
- id: z7.string().describe("Spend request ID")
15572
+ args: z8.object({
15573
+ id: z8.string().describe("Spend request ID")
15140
15574
  }),
15141
15575
  options: retrieveOptions,
15142
15576
  outputPolicy: "agent-only",
@@ -15154,7 +15588,7 @@ function createSpendRequestCli(repository, authStorage2) {
15154
15588
  if (!c.agent && !c.formatExplicit) {
15155
15589
  let capturedResult = null;
15156
15590
  return renderInteractive(
15157
- /* @__PURE__ */ jsx27(
15591
+ /* @__PURE__ */ jsx28(
15158
15592
  RetrieveSpendRequest,
15159
15593
  {
15160
15594
  repository,
@@ -15226,8 +15660,8 @@ function createSpendRequestCli(repository, authStorage2) {
15226
15660
  });
15227
15661
  cli2.command("cancel", {
15228
15662
  description: "Cancel a spend request",
15229
- args: z7.object({
15230
- id: z7.string().describe("Spend request ID")
15663
+ args: z8.object({
15664
+ id: z8.string().describe("Spend request ID")
15231
15665
  }),
15232
15666
  outputPolicy: "agent-only",
15233
15667
  middleware: [requireAuth(authStorage2)],
@@ -15236,7 +15670,7 @@ function createSpendRequestCli(repository, authStorage2) {
15236
15670
  if (!c.agent && !c.formatExplicit) {
15237
15671
  let capturedResult = null;
15238
15672
  return renderInteractive(
15239
- /* @__PURE__ */ jsx27(
15673
+ /* @__PURE__ */ jsx28(
15240
15674
  CancelSpendRequest,
15241
15675
  {
15242
15676
  repository,
@@ -15260,54 +15694,54 @@ function createSpendRequestCli(repository, authStorage2) {
15260
15694
  }
15261
15695
 
15262
15696
  // src/commands/user-info/index.tsx
15263
- import { Cli as Cli8 } from "incur";
15697
+ import { Cli as Cli9 } from "incur";
15264
15698
 
15265
15699
  // 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";
15700
+ import { Box as Box21, Text as Text22 } from "ink";
15701
+ import Spinner13 from "ink-spinner";
15702
+ import { useCallback as useCallback10 } from "react";
15703
+ import { jsx as jsx29, jsxs as jsxs21 } from "react/jsx-runtime";
15270
15704
  var UserInfoRetrieve = ({
15271
15705
  resource,
15272
15706
  onComplete
15273
15707
  }) => {
15274
- const action = useCallback9(() => resource.retrieve(), [resource]);
15708
+ const action = useCallback10(() => resource.retrieve(), [resource]);
15275
15709
  const { status, data: userInfo, error } = useAsyncAction(action, onComplete);
15276
15710
  if (status === "loading") {
15277
- return /* @__PURE__ */ jsx28(Box20, { children: /* @__PURE__ */ jsxs20(Text21, { color: "cyan", children: [
15278
- /* @__PURE__ */ jsx28(Spinner12, { type: "dots" }),
15711
+ return /* @__PURE__ */ jsx29(Box21, { children: /* @__PURE__ */ jsxs21(Text22, { color: "cyan", children: [
15712
+ /* @__PURE__ */ jsx29(Spinner13, { type: "dots" }),
15279
15713
  " Loading user info..."
15280
15714
  ] }) });
15281
15715
  }
15282
15716
  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 })
15717
+ return /* @__PURE__ */ jsxs21(Box21, { flexDirection: "column", children: [
15718
+ /* @__PURE__ */ jsx29(Text22, { color: "red", children: "\u2717 Failed to load user info" }),
15719
+ /* @__PURE__ */ jsx29(Text22, { color: "red", children: error })
15286
15720
  ] });
15287
15721
  }
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" })
15722
+ return /* @__PURE__ */ jsxs21(Box21, { flexDirection: "column", children: [
15723
+ /* @__PURE__ */ jsx29(Text22, { bold: true, children: "User Info" }),
15724
+ /* @__PURE__ */ jsxs21(Box21, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
15725
+ /* @__PURE__ */ jsxs21(Text22, { children: [
15726
+ /* @__PURE__ */ jsx29(Text22, { dimColor: true, children: "Email: " }),
15727
+ userInfo?.email ?? /* @__PURE__ */ jsx29(Text22, { dimColor: true, children: "Not set" })
15294
15728
  ] }),
15295
- /* @__PURE__ */ jsxs20(Text21, { children: [
15296
- /* @__PURE__ */ jsx28(Text21, { dimColor: true, children: "Name: " }),
15297
- userInfo?.name ?? /* @__PURE__ */ jsx28(Text21, { dimColor: true, children: "Not set" })
15729
+ /* @__PURE__ */ jsxs21(Text22, { children: [
15730
+ /* @__PURE__ */ jsx29(Text22, { dimColor: true, children: "Name: " }),
15731
+ userInfo?.name ?? /* @__PURE__ */ jsx29(Text22, { dimColor: true, children: "Not set" })
15298
15732
  ] }),
15299
- /* @__PURE__ */ jsxs20(Text21, { children: [
15300
- /* @__PURE__ */ jsx28(Text21, { dimColor: true, children: "Phone: " }),
15301
- userInfo?.phone ?? /* @__PURE__ */ jsx28(Text21, { dimColor: true, children: "Not set" })
15733
+ /* @__PURE__ */ jsxs21(Text22, { children: [
15734
+ /* @__PURE__ */ jsx29(Text22, { dimColor: true, children: "Phone: " }),
15735
+ userInfo?.phone ?? /* @__PURE__ */ jsx29(Text22, { dimColor: true, children: "Not set" })
15302
15736
  ] })
15303
15737
  ] })
15304
15738
  ] });
15305
15739
  };
15306
15740
 
15307
15741
  // src/commands/user-info/index.tsx
15308
- import { jsx as jsx29 } from "react/jsx-runtime";
15742
+ import { jsx as jsx30 } from "react/jsx-runtime";
15309
15743
  function createUserInfoCli(createResource, authStorage2) {
15310
- const cli2 = Cli8.create("user-info", {
15744
+ const cli2 = Cli9.create("user-info", {
15311
15745
  description: "User information commands"
15312
15746
  });
15313
15747
  cli2.command("retrieve", {
@@ -15318,7 +15752,7 @@ function createUserInfoCli(createResource, authStorage2) {
15318
15752
  const resource = createResource();
15319
15753
  if (!c.agent && !c.formatExplicit) {
15320
15754
  return renderInteractive(
15321
- /* @__PURE__ */ jsx29(UserInfoRetrieve, { resource, onComplete: () => {
15755
+ /* @__PURE__ */ jsx30(UserInfoRetrieve, { resource, onComplete: () => {
15322
15756
  } }),
15323
15757
  () => resource.retrieve()
15324
15758
  );
@@ -15552,8 +15986,8 @@ ${JSON.stringify(redacted, null, 2)}`
15552
15986
  };
15553
15987
 
15554
15988
  // src/auth/session.ts
15555
- var EXPIRY_BUFFER_MS = 6e4;
15556
- function createAccessTokenProvider(authResource, authStorage2 = storage) {
15989
+ var EXPIRY_BUFFER_MS2 = 6e4;
15990
+ function createAccessTokenProvider(authResource, authStorage2 = storage, options = {}) {
15557
15991
  return async ({ forceRefresh } = {}) => {
15558
15992
  const auth = authStorage2.getAuth();
15559
15993
  if (!auth) {
@@ -15561,51 +15995,21 @@ function createAccessTokenProvider(authResource, authStorage2 = storage) {
15561
15995
  'Not authenticated. Run "link-cli auth login" first.'
15562
15996
  );
15563
15997
  }
15564
- const isExpired = auth.expires_at != null && Date.now() >= auth.expires_at - EXPIRY_BUFFER_MS;
15998
+ const isExpired = auth.expires_at != null && Date.now() >= auth.expires_at - EXPIRY_BUFFER_MS2;
15565
15999
  if (!forceRefresh && !isExpired) {
15566
16000
  return auth.access_token;
15567
16001
  }
16002
+ if (options.noRefresh) {
16003
+ throw new LinkAuthenticationError(
16004
+ 'Access token expired. Re-authenticate with "link-cli auth login".'
16005
+ );
16006
+ }
15568
16007
  const refreshed = await authResource.refreshToken(auth.refresh_token);
15569
16008
  authStorage2.setAuth(refreshed);
15570
16009
  return refreshed.access_token;
15571
16010
  };
15572
16011
  }
15573
16012
 
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
-
15609
16013
  // src/utils/resource-factory.ts
15610
16014
  function sanitizeResource(resource) {
15611
16015
  return new Proxy(resource, {
@@ -15628,37 +16032,69 @@ var ResourceFactory = class {
15628
16032
  verbose;
15629
16033
  defaultHeaders;
15630
16034
  authStorage;
15631
- authResource;
16035
+ envAccessToken;
16036
+ envRefreshToken;
16037
+ noRefresh;
16038
+ _authResource;
15632
16039
  accessTokenProvider;
15633
16040
  spendRequestResource;
15634
16041
  paymentMethodsResource;
15635
16042
  shippingAddressResource;
15636
16043
  userInfoResource;
16044
+ webBotAuthResource;
15637
16045
  constructor(options = {}) {
15638
16046
  this.verbose = options.verbose ?? false;
15639
16047
  this.defaultHeaders = options.defaultHeaders;
15640
16048
  this.authStorage = options.authStorage;
16049
+ this.envAccessToken = options.envAccessToken;
16050
+ this.envRefreshToken = options.envRefreshToken;
16051
+ this.noRefresh = options.noRefresh ?? false;
16052
+ this._authResource = options.authResource;
15641
16053
  }
15642
16054
  createAuthResource() {
15643
- if (this.authResource) {
15644
- return this.authResource;
16055
+ if (this._authResource) {
16056
+ return this._authResource;
15645
16057
  }
15646
- this.authResource = new LinkAuthResource({
15647
- verbose: this.verbose,
15648
- defaultHeaders: this.defaultHeaders
15649
- });
15650
- return this.authResource;
16058
+ this._authResource = sanitizeResource(
16059
+ new LinkAuthResource({
16060
+ verbose: this.verbose,
16061
+ defaultHeaders: this.defaultHeaders
16062
+ })
16063
+ );
16064
+ return this._authResource;
15651
16065
  }
15652
16066
  getAuthStorage() {
15653
16067
  return this.authStorage;
15654
16068
  }
16069
+ getAccessTokenProvider() {
16070
+ return this.createSdkAccessTokenProvider();
16071
+ }
15655
16072
  createSdkAccessTokenProvider() {
15656
16073
  if (this.accessTokenProvider) {
15657
16074
  return this.accessTokenProvider;
15658
16075
  }
16076
+ if (this.envAccessToken) {
16077
+ const envAccessToken2 = this.envAccessToken;
16078
+ const envRefreshToken2 = this.envRefreshToken;
16079
+ const noRefresh2 = this.noRefresh;
16080
+ this.accessTokenProvider = async ({ forceRefresh } = {}) => {
16081
+ if (forceRefresh) {
16082
+ if (noRefresh2 || !envRefreshToken2) {
16083
+ throw new LinkAuthenticationError(
16084
+ "Access token expired. Update LINK_ACCESS_TOKEN and retry."
16085
+ );
16086
+ }
16087
+ const refreshed = await this.createAuthResource().refreshToken(envRefreshToken2);
16088
+ return refreshed.access_token;
16089
+ }
16090
+ return envAccessToken2;
16091
+ };
16092
+ return this.accessTokenProvider;
16093
+ }
15659
16094
  this.accessTokenProvider = createAccessTokenProvider(
15660
16095
  this.createAuthResource(),
15661
- this.authStorage
16096
+ this.authStorage,
16097
+ { noRefresh: this.noRefresh }
15662
16098
  );
15663
16099
  return this.accessTokenProvider;
15664
16100
  }
@@ -15718,6 +16154,20 @@ var ResourceFactory = class {
15718
16154
  );
15719
16155
  return this.userInfoResource;
15720
16156
  }
16157
+ createWebBotAuthResource() {
16158
+ if (this.webBotAuthResource) {
16159
+ return this.webBotAuthResource;
16160
+ }
16161
+ const getAccessToken = this.createSdkAccessTokenProvider();
16162
+ this.webBotAuthResource = sanitizeResource(
16163
+ new WebBotAuthResource({
16164
+ verbose: this.verbose,
16165
+ defaultHeaders: this.defaultHeaders,
16166
+ getAccessToken
16167
+ })
16168
+ );
16169
+ return this.webBotAuthResource;
16170
+ }
15721
16171
  };
15722
16172
 
15723
16173
  // src/utils/update-info.ts
@@ -15791,12 +16241,10 @@ function cacheUpdateInfo(value, ttlMs = UPDATE_CACHE_TTL_MS) {
15791
16241
  }
15792
16242
 
15793
16243
  // src/cli.tsx
15794
- var cliVersion = "0.5.0";
15795
- var buildNumber = "1";
16244
+ var cliVersion = "0.6.0";
15796
16245
  var cliName = "@stripe/link-cli";
15797
16246
  var defaultHeaders = {
15798
- "User-Agent": `link-cli/${cliVersion} (build ${buildNumber})`,
15799
- "X-Build-Number": buildNumber
16247
+ "User-Agent": `link-cli/${cliVersion}`
15800
16248
  };
15801
16249
  var verbose = process.argv.includes("--verbose");
15802
16250
  var authFileIndex = process.argv.indexOf("--auth");
@@ -15805,12 +16253,22 @@ if (authFileIndex !== -1) {
15805
16253
  process.argv.splice(authFileIndex, 2);
15806
16254
  }
15807
16255
  var authStorage = credentialFilePath ? new Storage({ configPath: credentialFilePath }) : storage;
15808
- var factory = new ResourceFactory({ verbose, defaultHeaders, authStorage });
16256
+ var envAccessToken = process.env.LINK_ACCESS_TOKEN;
16257
+ var envRefreshToken = process.env.LINK_REFRESH_TOKEN;
16258
+ var noRefresh = Boolean(process.env.LINK_NO_REFRESH);
16259
+ var factory = new ResourceFactory({
16260
+ verbose,
16261
+ defaultHeaders,
16262
+ authStorage,
16263
+ envAccessToken,
16264
+ envRefreshToken,
16265
+ noRefresh
16266
+ });
15809
16267
  var authRepo = factory.createAuthResource();
15810
16268
  var spendRequestRepo = factory.createSpendRequestResource();
15811
- var cli = Cli9.create("link-cli", {
16269
+ var cli = Cli10.create("link-cli", {
15812
16270
  description: "Create a secure, one-time payment credential from a Link wallet to let agents complete purchases on behalf of users.",
15813
- version: `${cliVersion} (build ${buildNumber})`
16271
+ version: cliVersion
15814
16272
  });
15815
16273
  var isAgent = process.argv.includes("--format") || process.argv.includes("--mcp");
15816
16274
  var agentUpdateInfoProvider = createAgentUpdateInfoProvider(
@@ -15825,7 +16283,9 @@ if (!isAgent && process.stdout.isTTY) {
15825
16283
  process.stderr.write(renderInteractiveUpdateNotice(updateInfo));
15826
16284
  }
15827
16285
  }
15828
- cli.command(createAuthCli(authRepo, getUpdateInfo, authStorage));
16286
+ cli.command(
16287
+ createAuthCli(authRepo, getUpdateInfo, authStorage, envAccessToken)
16288
+ );
15829
16289
  cli.command(createSpendRequestCli(spendRequestRepo, authStorage));
15830
16290
  cli.command(
15831
16291
  createPaymentMethodsCli(
@@ -15859,6 +16319,7 @@ cli.command(
15859
16319
  authStorage
15860
16320
  )
15861
16321
  );
16322
+ cli.command(createServeCli(cli));
15862
16323
  cli.serve();
15863
16324
  var cli_default = cli;
15864
16325
  export {