@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.
- package/README.md +31 -1
- package/dist/cli.js +855 -394
- 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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
491
|
-
this.code = optimizeExpr(this.code, names,
|
|
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,
|
|
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,
|
|
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,
|
|
578
|
+
optimizeNames(names, constants2) {
|
|
579
579
|
var _a;
|
|
580
|
-
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
581
|
-
if (!(super.optimizeNames(names,
|
|
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,
|
|
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,
|
|
607
|
-
if (!super.optimizeNames(names,
|
|
606
|
+
optimizeNames(names, constants2) {
|
|
607
|
+
if (!super.optimizeNames(names, constants2))
|
|
608
608
|
return;
|
|
609
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
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,
|
|
646
|
-
if (!super.optimizeNames(names,
|
|
645
|
+
optimizeNames(names, constants2) {
|
|
646
|
+
if (!super.optimizeNames(names, constants2))
|
|
647
647
|
return;
|
|
648
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
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,
|
|
690
|
+
optimizeNames(names, constants2) {
|
|
691
691
|
var _a, _b;
|
|
692
|
-
super.optimizeNames(names,
|
|
693
|
-
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
694
|
-
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names,
|
|
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,
|
|
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 =
|
|
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 &&
|
|
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
|
|
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${
|
|
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${
|
|
8717
|
+
ret = `>=${M}.${m}.0${z9} <${M}.${+m + 1}.0-0`;
|
|
8718
8718
|
} else {
|
|
8719
|
-
ret = `>=${M}.${m}.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}${
|
|
8736
|
+
ret = `>=${M}.${m}.${p}${z9} <${M}.${m}.${+p + 1}-0`;
|
|
8737
8737
|
} else {
|
|
8738
|
-
ret = `>=${M}.${m}.${p}${
|
|
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
|
|
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:
|
|
9544
|
-
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
|
|
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
|
|
11962
|
+
}, [onComplete]);
|
|
11762
11963
|
if (!checked) {
|
|
11763
11964
|
return null;
|
|
11764
11965
|
}
|
|
11765
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
|
|
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(
|
|
11899
|
-
|
|
12158
|
+
/* @__PURE__ */ jsx4(
|
|
12159
|
+
AuthStatus,
|
|
12160
|
+
{
|
|
12161
|
+
authStorage: storage2,
|
|
12162
|
+
envAccessToken: envAccessToken2,
|
|
12163
|
+
onComplete: () => {
|
|
12164
|
+
}
|
|
12165
|
+
}
|
|
12166
|
+
),
|
|
11900
12167
|
() => {
|
|
11901
|
-
const
|
|
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:
|
|
11904
|
-
|
|
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
|
-
|
|
11915
|
-
|
|
11916
|
-
|
|
11917
|
-
|
|
11918
|
-
|
|
11919
|
-
|
|
11920
|
-
|
|
11921
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
13978
|
-
|
|
13979
|
-
|
|
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
|
|
13996
|
-
var LineItemSchema =
|
|
13997
|
-
name:
|
|
13998
|
-
url:
|
|
13999
|
-
image_url:
|
|
14000
|
-
description:
|
|
14001
|
-
sku:
|
|
14002
|
-
quantity:
|
|
14003
|
-
unit_amount:
|
|
14004
|
-
product_url:
|
|
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 =
|
|
14007
|
-
type:
|
|
14008
|
-
display_text:
|
|
14009
|
-
amount:
|
|
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
|
|
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
|
|
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/
|
|
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
|
|
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 =
|
|
14378
|
-
const onError =
|
|
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__ */
|
|
14404
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14410
|
-
/* @__PURE__ */
|
|
14411
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14416
|
-
/* @__PURE__ */
|
|
14417
|
-
/* @__PURE__ */
|
|
14418
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14839
|
+
/* @__PURE__ */ jsx25(Text19, { bold: true, children: result?.id })
|
|
14421
14840
|
] }),
|
|
14422
|
-
/* @__PURE__ */
|
|
14841
|
+
/* @__PURE__ */ jsxs18(Text19, { children: [
|
|
14423
14842
|
"Status: ",
|
|
14424
|
-
/* @__PURE__ */
|
|
14843
|
+
/* @__PURE__ */ jsx25(Text19, { bold: true, children: result?.status })
|
|
14425
14844
|
] }),
|
|
14426
|
-
/* @__PURE__ */
|
|
14845
|
+
/* @__PURE__ */ jsxs18(Text19, { children: [
|
|
14427
14846
|
"Amount:",
|
|
14428
14847
|
" ",
|
|
14429
|
-
/* @__PURE__ */
|
|
14848
|
+
/* @__PURE__ */ jsx25(Text19, { bold: true, children: result?.amount != null ? `${result.amount} ${result.currency?.toUpperCase() ?? ""}`.trim() : "N/A" })
|
|
14430
14849
|
] }),
|
|
14431
|
-
/* @__PURE__ */
|
|
14850
|
+
/* @__PURE__ */ jsxs18(Text19, { children: [
|
|
14432
14851
|
"Merchant: ",
|
|
14433
|
-
/* @__PURE__ */
|
|
14852
|
+
/* @__PURE__ */ jsx25(Text19, { bold: true, children: result?.merchant_name })
|
|
14434
14853
|
] }),
|
|
14435
|
-
result?.credential_type === "shared_payment_token" && result.shared_payment_token && /* @__PURE__ */
|
|
14854
|
+
result?.credential_type === "shared_payment_token" && result.shared_payment_token && /* @__PURE__ */ jsxs18(Text19, { children: [
|
|
14436
14855
|
"Token: ",
|
|
14437
|
-
/* @__PURE__ */
|
|
14856
|
+
/* @__PURE__ */ jsx25(Text19, { bold: true, children: result.shared_payment_token.id })
|
|
14438
14857
|
] })
|
|
14439
14858
|
] })
|
|
14440
14859
|
] });
|
|
14441
14860
|
}
|
|
14442
|
-
return /* @__PURE__ */
|
|
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
|
|
14453
|
-
import
|
|
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
|
|
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__ */
|
|
14580
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
14594
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14600
|
-
/* @__PURE__ */
|
|
15018
|
+
request && /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
|
|
15019
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14601
15020
|
"ID: ",
|
|
14602
|
-
/* @__PURE__ */
|
|
15021
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: request.id })
|
|
14603
15022
|
] }),
|
|
14604
|
-
/* @__PURE__ */
|
|
15023
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14605
15024
|
"Status: ",
|
|
14606
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14613
|
-
/* @__PURE__ */
|
|
14614
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15038
|
+
request?.approval_url && /* @__PURE__ */ jsx26(Box19, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsxs19(Text20, { dimColor: true, children: [
|
|
14620
15039
|
"Approval URL: ",
|
|
14621
|
-
/* @__PURE__ */
|
|
15040
|
+
/* @__PURE__ */ jsx26(Text20, { color: "cyan", children: request.approval_url })
|
|
14622
15041
|
] }) })
|
|
14623
15042
|
] });
|
|
14624
15043
|
}
|
|
14625
15044
|
if (phase === "finalized") {
|
|
14626
|
-
return /* @__PURE__ */
|
|
14627
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14632
|
-
/* @__PURE__ */
|
|
15050
|
+
/* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
|
|
15051
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14633
15052
|
"ID: ",
|
|
14634
|
-
/* @__PURE__ */
|
|
15053
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.id })
|
|
14635
15054
|
] }),
|
|
14636
|
-
/* @__PURE__ */
|
|
15055
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14637
15056
|
"Status: ",
|
|
14638
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14645
|
-
/* @__PURE__ */
|
|
14646
|
-
/* @__PURE__ */
|
|
14647
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15068
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.id })
|
|
14650
15069
|
] }),
|
|
14651
|
-
/* @__PURE__ */
|
|
15070
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14652
15071
|
"Status:",
|
|
14653
15072
|
" ",
|
|
14654
|
-
/* @__PURE__ */
|
|
15073
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, color: "red", children: request?.status })
|
|
14655
15074
|
] }),
|
|
14656
|
-
/* @__PURE__ */
|
|
15075
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14657
15076
|
"Amount:",
|
|
14658
15077
|
" ",
|
|
14659
|
-
/* @__PURE__ */
|
|
15078
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.amount != null ? String(request.amount) : "N/A" })
|
|
14660
15079
|
] }),
|
|
14661
|
-
/* @__PURE__ */
|
|
15080
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14662
15081
|
"Merchant: ",
|
|
14663
|
-
/* @__PURE__ */
|
|
15082
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.merchant_name })
|
|
14664
15083
|
] })
|
|
14665
15084
|
] })
|
|
14666
15085
|
] });
|
|
14667
15086
|
}
|
|
14668
|
-
return /* @__PURE__ */
|
|
14669
|
-
/* @__PURE__ */
|
|
14670
|
-
/* @__PURE__ */
|
|
14671
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15092
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.id })
|
|
14674
15093
|
] }),
|
|
14675
|
-
/* @__PURE__ */
|
|
15094
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14676
15095
|
"Status:",
|
|
14677
15096
|
" ",
|
|
14678
|
-
/* @__PURE__ */
|
|
15097
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, color: "green", children: request?.status })
|
|
14679
15098
|
] }),
|
|
14680
|
-
/* @__PURE__ */
|
|
15099
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14681
15100
|
"Amount:",
|
|
14682
15101
|
" ",
|
|
14683
|
-
/* @__PURE__ */
|
|
15102
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.amount != null ? String(request.amount) : "N/A" })
|
|
14684
15103
|
] }),
|
|
14685
|
-
/* @__PURE__ */
|
|
15104
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14686
15105
|
"Merchant: ",
|
|
14687
|
-
/* @__PURE__ */
|
|
15106
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.merchant_name })
|
|
14688
15107
|
] }),
|
|
14689
|
-
/* @__PURE__ */
|
|
15108
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14690
15109
|
"Line Items:",
|
|
14691
15110
|
" ",
|
|
14692
|
-
/* @__PURE__ */
|
|
15111
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.line_items.map((li) => li.name).join(", ") })
|
|
14693
15112
|
] }),
|
|
14694
|
-
request?.shared_payment_token && /* @__PURE__ */
|
|
14695
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15120
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14702
15121
|
" ",
|
|
14703
15122
|
"Token: ",
|
|
14704
|
-
/* @__PURE__ */
|
|
15123
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: request.shared_payment_token.id })
|
|
14705
15124
|
] })
|
|
14706
15125
|
] }),
|
|
14707
|
-
request?.card && !outputFile && /* @__PURE__ */
|
|
14708
|
-
/* @__PURE__ */
|
|
14709
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15131
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.card.number })
|
|
14713
15132
|
] }),
|
|
14714
|
-
/* @__PURE__ */
|
|
15133
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14715
15134
|
" ",
|
|
14716
15135
|
"Brand: ",
|
|
14717
|
-
/* @__PURE__ */
|
|
15136
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: request?.card.brand })
|
|
14718
15137
|
] }),
|
|
14719
|
-
/* @__PURE__ */
|
|
15138
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14720
15139
|
" ",
|
|
14721
15140
|
"Expiry:",
|
|
14722
15141
|
" ",
|
|
14723
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15148
|
+
request?.card.cvc && /* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14730
15149
|
" ",
|
|
14731
15150
|
"CVC: ",
|
|
14732
|
-
/* @__PURE__ */
|
|
15151
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: request.card.cvc })
|
|
14733
15152
|
] }),
|
|
14734
|
-
request?.card.valid_until && /* @__PURE__ */
|
|
15153
|
+
request?.card.valid_until && /* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14735
15154
|
" ",
|
|
14736
15155
|
"Valid Until: ",
|
|
14737
|
-
/* @__PURE__ */
|
|
15156
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: request.card.valid_until })
|
|
14738
15157
|
] }),
|
|
14739
|
-
request?.card.billing_address && /* @__PURE__ */
|
|
14740
|
-
/* @__PURE__ */
|
|
14741
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15164
|
+
/* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14746
15165
|
" ",
|
|
14747
15166
|
request.card.billing_address.line1
|
|
14748
15167
|
] }),
|
|
14749
|
-
request.card.billing_address.line2 && /* @__PURE__ */
|
|
15168
|
+
request.card.billing_address.line2 && /* @__PURE__ */ jsxs19(Text20, { children: [
|
|
14750
15169
|
" ",
|
|
14751
15170
|
request.card.billing_address.line2
|
|
14752
15171
|
] }),
|
|
14753
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
14768
|
-
outputFilePath && /* @__PURE__ */
|
|
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__ */
|
|
15189
|
+
/* @__PURE__ */ jsx26(Text20, { bold: true, children: outputFilePath })
|
|
14771
15190
|
] }),
|
|
14772
|
-
fileError && /* @__PURE__ */
|
|
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
|
|
14783
|
-
var createOptions =
|
|
14784
|
-
paymentMethodId:
|
|
14785
|
-
credentialType:
|
|
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:
|
|
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:
|
|
14792
|
-
currency:
|
|
14793
|
-
merchantName:
|
|
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:
|
|
15215
|
+
merchantUrl: z7.string().optional().describe(
|
|
14797
15216
|
"Merchant URL (required for card; forbidden for shared_payment_token)"
|
|
14798
15217
|
),
|
|
14799
|
-
context:
|
|
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:
|
|
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:
|
|
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:
|
|
14809
|
-
test:
|
|
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:
|
|
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:
|
|
15234
|
+
force: z7.boolean().default(false).describe("Overwrite output file if it already exists")
|
|
14816
15235
|
});
|
|
14817
|
-
var retrieveOptions =
|
|
14818
|
-
timeout:
|
|
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:
|
|
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:
|
|
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:
|
|
14828
|
-
outputFile:
|
|
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:
|
|
14832
|
-
});
|
|
14833
|
-
var updateOptions =
|
|
14834
|
-
paymentMethodId:
|
|
14835
|
-
amount:
|
|
14836
|
-
merchantUrl:
|
|
14837
|
-
profileId:
|
|
14838
|
-
merchantId:
|
|
14839
|
-
currency:
|
|
14840
|
-
lineItem:
|
|
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:
|
|
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
|
|
14850
|
-
import
|
|
14851
|
-
import { useCallback as
|
|
14852
|
-
import { jsx as
|
|
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 =
|
|
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__ */
|
|
14866
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14874
|
-
/* @__PURE__ */
|
|
14875
|
-
/* @__PURE__ */
|
|
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__ */
|
|
14879
|
-
/* @__PURE__ */
|
|
14880
|
-
/* @__PURE__ */
|
|
14881
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15302
|
+
/* @__PURE__ */ jsx27(Text21, { bold: true, children: request?.id })
|
|
14884
15303
|
] }),
|
|
14885
|
-
/* @__PURE__ */
|
|
15304
|
+
/* @__PURE__ */ jsxs20(Text21, { children: [
|
|
14886
15305
|
"Status: ",
|
|
14887
|
-
/* @__PURE__ */
|
|
15306
|
+
/* @__PURE__ */ jsx27(Text21, { bold: true, children: request?.status })
|
|
14888
15307
|
] }),
|
|
14889
|
-
/* @__PURE__ */
|
|
15308
|
+
/* @__PURE__ */ jsxs20(Text21, { children: [
|
|
14890
15309
|
"Amount:",
|
|
14891
15310
|
" ",
|
|
14892
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15316
|
+
/* @__PURE__ */ jsxs20(Text21, { children: [
|
|
14898
15317
|
"Merchant: ",
|
|
14899
|
-
/* @__PURE__ */
|
|
15318
|
+
/* @__PURE__ */ jsx27(Text21, { bold: true, children: request?.merchant_name })
|
|
14900
15319
|
] }),
|
|
14901
|
-
/* @__PURE__ */
|
|
15320
|
+
/* @__PURE__ */ jsxs20(Text21, { children: [
|
|
14902
15321
|
"Line Items:",
|
|
14903
15322
|
" ",
|
|
14904
|
-
/* @__PURE__ */
|
|
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
|
|
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 =
|
|
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__ */
|
|
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:
|
|
15047
|
-
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__ */
|
|
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:
|
|
15099
|
-
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__ */
|
|
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:
|
|
15139
|
-
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__ */
|
|
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:
|
|
15230
|
-
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__ */
|
|
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
|
|
15697
|
+
import { Cli as Cli9 } from "incur";
|
|
15264
15698
|
|
|
15265
15699
|
// src/commands/user-info/retrieve.tsx
|
|
15266
|
-
import { Box as
|
|
15267
|
-
import
|
|
15268
|
-
import { useCallback as
|
|
15269
|
-
import { jsx as
|
|
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 =
|
|
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__ */
|
|
15278
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15284
|
-
/* @__PURE__ */
|
|
15285
|
-
/* @__PURE__ */
|
|
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__ */
|
|
15289
|
-
/* @__PURE__ */
|
|
15290
|
-
/* @__PURE__ */
|
|
15291
|
-
/* @__PURE__ */
|
|
15292
|
-
/* @__PURE__ */
|
|
15293
|
-
userInfo?.email ?? /* @__PURE__ */
|
|
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__ */
|
|
15296
|
-
/* @__PURE__ */
|
|
15297
|
-
userInfo?.name ?? /* @__PURE__ */
|
|
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__ */
|
|
15300
|
-
/* @__PURE__ */
|
|
15301
|
-
userInfo?.phone ?? /* @__PURE__ */
|
|
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
|
|
15742
|
+
import { jsx as jsx30 } from "react/jsx-runtime";
|
|
15309
15743
|
function createUserInfoCli(createResource, authStorage2) {
|
|
15310
|
-
const cli2 =
|
|
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__ */
|
|
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
|
|
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 -
|
|
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
|
-
|
|
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.
|
|
15644
|
-
return this.
|
|
16055
|
+
if (this._authResource) {
|
|
16056
|
+
return this._authResource;
|
|
15645
16057
|
}
|
|
15646
|
-
this.
|
|
15647
|
-
|
|
15648
|
-
|
|
15649
|
-
|
|
15650
|
-
|
|
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.
|
|
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}
|
|
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
|
|
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 =
|
|
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:
|
|
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(
|
|
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 {
|