@stripe/link-cli 0.4.2 → 0.4.3
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 +71 -38
- package/dist/cli.js +295 -56
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# Link CLI
|
|
2
2
|
|
|
3
|
-
Link CLI lets agents get secure, one-time-use payment credentials from a Link wallet
|
|
3
|
+
Link CLI lets agents get secure, one-time-use payment credentials from a Link wallet to complete purchases on your behalf — without storing your real card details.
|
|
4
|
+
|
|
5
|
+
The CLI can produce one of two credential types:
|
|
6
|
+
|
|
7
|
+
- A virtual card (PAN) for use with a standard web checkout form. The issued card works anywhere, and is not restricted to Link-enabled sellers or sellers that use Stripe.
|
|
8
|
+
- A [Shared Payment Token](https://docs.stripe.com/agentic-commerce/concepts/shared-payment-tokens) (SPT) when the seller accepts programmatic payments through [Machine Payment Protocols](https://mpp.dev) (MPP)
|
|
9
|
+
|
|
10
|
+
For now, this is only available to US Link accounts.
|
|
4
11
|
|
|
5
12
|
## Installation
|
|
6
13
|
|
|
@@ -14,11 +21,31 @@ Or run directly with `npx`:
|
|
|
14
21
|
npx @stripe/link-cli
|
|
15
22
|
```
|
|
16
23
|
|
|
17
|
-
|
|
24
|
+
### Use with agents
|
|
25
|
+
|
|
26
|
+
Install the skill:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx skills add stripe/link-cli
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
By default when called from an agent (non-TTY), all commands use `toon` output — a compact, LLM-friendly text format. All commands accept `--format [format]` for structured output. Other formats: `json`, `yaml`, `md`, `jsonl`.
|
|
18
33
|
|
|
19
|
-
|
|
34
|
+
List available commands:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
link-cli --llms-full
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Get a command's full schema with `--schema`. Example:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
link-cli spend-request create --schema
|
|
44
|
+
```
|
|
20
45
|
|
|
21
|
-
|
|
46
|
+
#### MCP Server
|
|
47
|
+
|
|
48
|
+
Link CLI can run as a local MCP server. Add the following to your MCP client config (`.mcp.json`, etc.)
|
|
22
49
|
|
|
23
50
|
```json
|
|
24
51
|
{
|
|
@@ -33,6 +60,12 @@ Link CLI can also run as a local MCP server. Add the following to your MCP clien
|
|
|
33
60
|
|
|
34
61
|
## Quickstart
|
|
35
62
|
|
|
63
|
+
Run a guided onboarding and demo flow:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
link-cli onboard
|
|
67
|
+
```
|
|
68
|
+
|
|
36
69
|
### Login
|
|
37
70
|
|
|
38
71
|
The `link-cli` requires a Link account. You can log in to your existing one or [register online](https://app.link.com).
|
|
@@ -41,7 +74,7 @@ The `link-cli` requires a Link account. You can log in to your existing one or [
|
|
|
41
74
|
link-cli auth login
|
|
42
75
|
```
|
|
43
76
|
|
|
44
|
-
You
|
|
77
|
+
You receive a verification URL and a short phrase. Visit the URL, log in to your Link account, and enter the phrase to approve the connection.
|
|
45
78
|
|
|
46
79
|
### List payment methods
|
|
47
80
|
|
|
@@ -49,11 +82,11 @@ You'll receive a verification URL and a short phrase. Visit the URL, log in to y
|
|
|
49
82
|
link-cli payment-methods list
|
|
50
83
|
```
|
|
51
84
|
|
|
52
|
-
Returns the cards and bank accounts saved to your Link account. Use the `id` field as `payment_method_id` in the next step. If you have no payment methods,
|
|
85
|
+
Returns the cards and bank accounts saved to your Link account. Use the `id` field as `payment_method_id` in the next step. If you have no payment methods, [add new ones in Link](https://app.link.com/wallet).
|
|
53
86
|
|
|
54
87
|
### Create a spend request
|
|
55
88
|
|
|
56
|
-
|
|
89
|
+
Create a spend request with a payment method, merchant details, line items, and amounts:
|
|
57
90
|
|
|
58
91
|
```bash
|
|
59
92
|
link-cli spend-request create \
|
|
@@ -67,9 +100,9 @@ link-cli spend-request create \
|
|
|
67
100
|
--request-approval
|
|
68
101
|
```
|
|
69
102
|
|
|
70
|
-
The `--request-approval` flag triggers a push notification
|
|
103
|
+
The `--request-approval` flag triggers a push notification to the user for approval, then polls until the request is approved or denied.
|
|
71
104
|
|
|
72
|
-
|
|
105
|
+
Easily approve requests with the [Link app](https://link.com/download).
|
|
73
106
|
|
|
74
107
|
#### Line items and totals
|
|
75
108
|
|
|
@@ -81,7 +114,7 @@ Users can easily approve requests with the [Link app](https://link.com/download)
|
|
|
81
114
|
--line-item "name:Running Shoes,unit_amount:12000,quantity:1,description:Trail runners"
|
|
82
115
|
```
|
|
83
116
|
|
|
84
|
-
**`--total` keys:** `type` (required), `display_text` (required), `amount` (required)
|
|
117
|
+
**`--total` keys:** `type` (required; one of: `subtotal`, `tax`, `total`), `display_text` (required), `amount` (required)
|
|
85
118
|
|
|
86
119
|
```bash
|
|
87
120
|
--total "type:subtotal,display_text:Subtotal,amount:12000" \
|
|
@@ -90,21 +123,29 @@ Users can easily approve requests with the [Link app](https://link.com/download)
|
|
|
90
123
|
|
|
91
124
|
#### Credential types
|
|
92
125
|
|
|
93
|
-
By default, a spend request provisions a virtual card. For merchants that support the [Machine Payments Protocol](https://mpp.dev) (HTTP 402) and the Stripe payment method,
|
|
126
|
+
By default, a spend request provisions a virtual card. For merchants that support the [Machine Payments Protocol](https://mpp.dev) (HTTP 402) and the Stripe payment method, instead pass `--credential-type "shared_payment_token"`.
|
|
94
127
|
|
|
95
128
|
### Execute payment
|
|
96
129
|
|
|
97
130
|
The approved spend request includes a `card` object with `number`, `cvc`, `exp_month`, `exp_year`, `billing_address`, and `valid_until`. Enter these into the merchant's checkout form.
|
|
98
131
|
|
|
99
132
|
```bash
|
|
100
|
-
link-cli spend-request retrieve lsrq_001
|
|
133
|
+
link-cli spend-request retrieve lsrq_001
|
|
134
|
+
```
|
|
135
|
+
By default, retrieving a spend request doesn't include card details. Pass `--include card` to see unmasked card details.
|
|
136
|
+
|
|
137
|
+
To avoid leaking card credentials into agent transcripts or logs, use `--output-file` to write the full card to a secure local file while stdout shows only redacted data (brand, last4, expiry):
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
link-cli spend-request retrieve lsrq_001 --include card --output-file /tmp/link-card.json --format json
|
|
101
141
|
```
|
|
102
|
-
|
|
142
|
+
|
|
143
|
+
The file is created with `0600` permissions. If the file already exists, the command fails unless `--force` is passed. When `--output-file` is set, the JSON output replaces the `card` object with redacted fields and adds a `card_output_file` path.
|
|
103
144
|
|
|
104
145
|
For agent polling, pass `--interval` and optionally `--max-attempts`:
|
|
105
146
|
|
|
106
147
|
```bash
|
|
107
|
-
link-cli spend-request retrieve lsrq_001 --interval 2 --max-attempts 150
|
|
148
|
+
link-cli spend-request retrieve lsrq_001 --interval 2 --max-attempts 150
|
|
108
149
|
```
|
|
109
150
|
|
|
110
151
|
Polling exits successfully only after the request reaches a terminal status such as `approved`, `denied`, or `expired`. If polling reaches `--timeout` or exhausts `--max-attempts` while the request is still non-terminal, the command exits non-zero with `code: "POLLING_TIMEOUT"` so callers do not treat a still-pending request as complete.
|
|
@@ -115,8 +156,7 @@ If the merchant supports MPP, use `link-cli mpp pay` instead:
|
|
|
115
156
|
link-cli mpp pay https://climate.stripe.dev/api/contribute \
|
|
116
157
|
--spend-request-id lsrq_001 \
|
|
117
158
|
--method POST \
|
|
118
|
-
--data '{"amount":100}'
|
|
119
|
-
--format json
|
|
159
|
+
--data '{"amount":100}'
|
|
120
160
|
```
|
|
121
161
|
|
|
122
162
|
## Advanced
|
|
@@ -124,14 +164,14 @@ link-cli mpp pay https://climate.stripe.dev/api/contribute \
|
|
|
124
164
|
### Authentication
|
|
125
165
|
|
|
126
166
|
```bash
|
|
127
|
-
link-cli auth login --client-name "Claude Code"
|
|
128
|
-
link-cli auth status
|
|
129
|
-
link-cli auth logout
|
|
167
|
+
link-cli auth login --client-name "Claude Code" # identify the connecting agent
|
|
168
|
+
link-cli auth status # check auth status
|
|
169
|
+
link-cli auth logout # disconnect
|
|
130
170
|
```
|
|
131
171
|
|
|
132
|
-
When `--client-name
|
|
172
|
+
When you provide `--client-name`, the Link app displays it when you approve the connection — for example, `Claude Code on my-macbook` instead of `link-cli on my-macbook`.
|
|
133
173
|
|
|
134
|
-
`auth status
|
|
174
|
+
`auth status` includes an `update` field when a newer version is available:
|
|
135
175
|
|
|
136
176
|
```json
|
|
137
177
|
{
|
|
@@ -144,7 +184,7 @@ When `--client-name` is provided, the name is shown in the Link app when the use
|
|
|
144
184
|
}
|
|
145
185
|
```
|
|
146
186
|
|
|
147
|
-
Set `NO_UPDATE_NOTIFIER=1` to suppress update checks (
|
|
187
|
+
Set `NO_UPDATE_NOTIFIER=1` to suppress update checks (for example, in CI).
|
|
148
188
|
|
|
149
189
|
### Spend request lifecycle
|
|
150
190
|
|
|
@@ -153,44 +193,37 @@ A spend request moves through: **create** → **request approval** → **approve
|
|
|
153
193
|
**Required fields for create:** `payment_method_id`, `merchant_name`, `merchant_url`, `context`, `amount`
|
|
154
194
|
|
|
155
195
|
**Constraints:** `context` must be at least 100 characters; `amount` must not exceed 50000 (cents); `currency` must be a 3-letter ISO code.
|
|
156
|
-
**Test mode:** Pass `--test` to create testmode credentials (uses test card `4242424242424242`)
|
|
196
|
+
**Test mode:** Pass `--test` to create testmode credentials (uses test card `4242424242424242`), useful for development and integration testing without real payment methods.
|
|
157
197
|
|
|
158
198
|
```bash
|
|
159
199
|
# Update before approval
|
|
160
200
|
link-cli spend-request update lsrq_001 \
|
|
161
|
-
--merchant-url https://press.stripe.com/working-in-public
|
|
162
|
-
--format json
|
|
201
|
+
--merchant-url https://press.stripe.com/working-in-public
|
|
163
202
|
|
|
164
203
|
# Request approval separately (alternative to create --request-approval)
|
|
165
|
-
link-cli spend-request request-approval lsrq_001
|
|
204
|
+
link-cli spend-request request-approval lsrq_001
|
|
166
205
|
|
|
167
|
-
# Retrieve at any time (includes card credentials
|
|
168
|
-
link-cli spend-request retrieve lsrq_001
|
|
206
|
+
# Retrieve at any time (includes card credentials after approval)
|
|
207
|
+
link-cli spend-request retrieve lsrq_001
|
|
169
208
|
```
|
|
170
209
|
|
|
171
|
-
### Output formats
|
|
172
|
-
|
|
173
|
-
All commands accept `--format json` for structured JSON output. Other formats: `yaml`, `md`, `jsonl`, `toon` (default). Errors are returned as JSON with `code` and `message` fields, with exit code 1.
|
|
174
|
-
|
|
175
210
|
### MPP
|
|
176
211
|
|
|
177
|
-
Use `mpp pay` to complete purchases on merchants that use the [Machine Payments Protocol](https://mpp.dev). The spend request must use `credential_type: "shared_payment_token"` and
|
|
212
|
+
Use `mpp pay` to complete purchases on merchants that use the [Machine Payments Protocol](https://mpp.dev). The spend request must use `credential_type: "shared_payment_token"` and you must approve it before paying. The SPT is one-time-use — if payment fails, create a new spend request.
|
|
178
213
|
|
|
179
214
|
```bash
|
|
180
215
|
link-cli mpp pay https://climate.stripe.dev/api/contribute \
|
|
181
216
|
--spend-request-id lsrq_001 \
|
|
182
217
|
--method POST \
|
|
183
218
|
--data '{"amount":100}' \
|
|
184
|
-
--header "X-Custom: value"
|
|
185
|
-
--format json
|
|
219
|
+
--header "X-Custom: value"
|
|
186
220
|
```
|
|
187
221
|
|
|
188
222
|
Use `mpp decode` to validate a raw `WWW-Authenticate` header and extract the `network_id` needed for `shared_payment_token` spend requests:
|
|
189
223
|
|
|
190
224
|
```bash
|
|
191
225
|
link-cli mpp decode \
|
|
192
|
-
--challenge 'Payment id="ch_001", realm="merchant.example", method="stripe", intent="charge", request="..."'
|
|
193
|
-
--format json
|
|
226
|
+
--challenge 'Payment id="ch_001", realm="merchant.example", method="stripe", intent="charge", request="..."'
|
|
194
227
|
```
|
|
195
228
|
|
|
196
229
|
### Environment variables
|
|
@@ -203,7 +236,7 @@ link-cli mpp decode \
|
|
|
203
236
|
|
|
204
237
|
## Onboard
|
|
205
238
|
|
|
206
|
-
Run the guided setup flow — authenticates, checks payment methods, shows the app download QR, and
|
|
239
|
+
Run the guided setup flow — authenticates, checks payment methods, shows the app download QR, and runs both demo flows:
|
|
207
240
|
|
|
208
241
|
```bash
|
|
209
242
|
link-cli onboard
|
package/dist/cli.js
CHANGED
|
@@ -3218,8 +3218,8 @@ var require_utils = __commonJS({
|
|
|
3218
3218
|
}
|
|
3219
3219
|
return ind;
|
|
3220
3220
|
}
|
|
3221
|
-
function removeDotSegments(
|
|
3222
|
-
let input =
|
|
3221
|
+
function removeDotSegments(path6) {
|
|
3222
|
+
let input = path6;
|
|
3223
3223
|
const output = [];
|
|
3224
3224
|
let nextSlash = -1;
|
|
3225
3225
|
let len = 0;
|
|
@@ -3418,8 +3418,8 @@ var require_schemes = __commonJS({
|
|
|
3418
3418
|
wsComponent.secure = void 0;
|
|
3419
3419
|
}
|
|
3420
3420
|
if (wsComponent.resourceName) {
|
|
3421
|
-
const [
|
|
3422
|
-
wsComponent.path =
|
|
3421
|
+
const [path6, query] = wsComponent.resourceName.split("?");
|
|
3422
|
+
wsComponent.path = path6 && path6 !== "/" ? path6 : void 0;
|
|
3423
3423
|
wsComponent.query = query;
|
|
3424
3424
|
wsComponent.resourceName = void 0;
|
|
3425
3425
|
}
|
|
@@ -7605,12 +7605,12 @@ var require_dist = __commonJS({
|
|
|
7605
7605
|
throw new Error(`Unknown format "${name}"`);
|
|
7606
7606
|
return f;
|
|
7607
7607
|
};
|
|
7608
|
-
function addFormats(ajv, list,
|
|
7608
|
+
function addFormats(ajv, list, fs5, exportName) {
|
|
7609
7609
|
var _a;
|
|
7610
7610
|
var _b;
|
|
7611
7611
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
7612
7612
|
for (const f of list)
|
|
7613
|
-
ajv.addFormat(f,
|
|
7613
|
+
ajv.addFormat(f, fs5[f]);
|
|
7614
7614
|
}
|
|
7615
7615
|
module.exports = exports = formatsPlugin;
|
|
7616
7616
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -9550,7 +9550,6 @@ var require_semver2 = __commonJS({
|
|
|
9550
9550
|
|
|
9551
9551
|
// src/cli.tsx
|
|
9552
9552
|
import { Cli as Cli7 } from "incur";
|
|
9553
|
-
import updateNotifier from "update-notifier";
|
|
9554
9553
|
|
|
9555
9554
|
// ../sdk/dist/index.js
|
|
9556
9555
|
import fs3 from "fs";
|
|
@@ -9574,12 +9573,12 @@ var disallowedKeys = /* @__PURE__ */ new Set([
|
|
|
9574
9573
|
"constructor"
|
|
9575
9574
|
]);
|
|
9576
9575
|
var digits = new Set("0123456789");
|
|
9577
|
-
function getPathSegments(
|
|
9576
|
+
function getPathSegments(path6) {
|
|
9578
9577
|
const parts = [];
|
|
9579
9578
|
let currentSegment = "";
|
|
9580
9579
|
let currentPart = "start";
|
|
9581
9580
|
let isIgnoring = false;
|
|
9582
|
-
for (const character of
|
|
9581
|
+
for (const character of path6) {
|
|
9583
9582
|
switch (character) {
|
|
9584
9583
|
case "\\": {
|
|
9585
9584
|
if (currentPart === "index") {
|
|
@@ -9701,11 +9700,11 @@ function assertNotStringIndex(object, key) {
|
|
|
9701
9700
|
throw new Error("Cannot use string index");
|
|
9702
9701
|
}
|
|
9703
9702
|
}
|
|
9704
|
-
function getProperty(object,
|
|
9705
|
-
if (!isObject(object) || typeof
|
|
9703
|
+
function getProperty(object, path6, value) {
|
|
9704
|
+
if (!isObject(object) || typeof path6 !== "string") {
|
|
9706
9705
|
return value === void 0 ? object : value;
|
|
9707
9706
|
}
|
|
9708
|
-
const pathArray = getPathSegments(
|
|
9707
|
+
const pathArray = getPathSegments(path6);
|
|
9709
9708
|
if (pathArray.length === 0) {
|
|
9710
9709
|
return value;
|
|
9711
9710
|
}
|
|
@@ -9725,12 +9724,12 @@ function getProperty(object, path5, value) {
|
|
|
9725
9724
|
}
|
|
9726
9725
|
return object === void 0 ? value : object;
|
|
9727
9726
|
}
|
|
9728
|
-
function setProperty(object,
|
|
9729
|
-
if (!isObject(object) || typeof
|
|
9727
|
+
function setProperty(object, path6, value) {
|
|
9728
|
+
if (!isObject(object) || typeof path6 !== "string") {
|
|
9730
9729
|
return object;
|
|
9731
9730
|
}
|
|
9732
9731
|
const root = object;
|
|
9733
|
-
const pathArray = getPathSegments(
|
|
9732
|
+
const pathArray = getPathSegments(path6);
|
|
9734
9733
|
for (let index = 0; index < pathArray.length; index++) {
|
|
9735
9734
|
const key = pathArray[index];
|
|
9736
9735
|
assertNotStringIndex(object, key);
|
|
@@ -9743,11 +9742,11 @@ function setProperty(object, path5, value) {
|
|
|
9743
9742
|
}
|
|
9744
9743
|
return root;
|
|
9745
9744
|
}
|
|
9746
|
-
function deleteProperty(object,
|
|
9747
|
-
if (!isObject(object) || typeof
|
|
9745
|
+
function deleteProperty(object, path6) {
|
|
9746
|
+
if (!isObject(object) || typeof path6 !== "string") {
|
|
9748
9747
|
return false;
|
|
9749
9748
|
}
|
|
9750
|
-
const pathArray = getPathSegments(
|
|
9749
|
+
const pathArray = getPathSegments(path6);
|
|
9751
9750
|
for (let index = 0; index < pathArray.length; index++) {
|
|
9752
9751
|
const key = pathArray[index];
|
|
9753
9752
|
assertNotStringIndex(object, key);
|
|
@@ -9761,11 +9760,11 @@ function deleteProperty(object, path5) {
|
|
|
9761
9760
|
}
|
|
9762
9761
|
}
|
|
9763
9762
|
}
|
|
9764
|
-
function hasProperty(object,
|
|
9765
|
-
if (!isObject(object) || typeof
|
|
9763
|
+
function hasProperty(object, path6) {
|
|
9764
|
+
if (!isObject(object) || typeof path6 !== "string") {
|
|
9766
9765
|
return false;
|
|
9767
9766
|
}
|
|
9768
|
-
const pathArray = getPathSegments(
|
|
9767
|
+
const pathArray = getPathSegments(path6);
|
|
9769
9768
|
if (pathArray.length === 0) {
|
|
9770
9769
|
return false;
|
|
9771
9770
|
}
|
|
@@ -10861,12 +10860,19 @@ function withComputedExpiry(auth) {
|
|
|
10861
10860
|
expires_at: auth.expires_at ?? Date.now() + auth.expires_in * 1e3
|
|
10862
10861
|
};
|
|
10863
10862
|
}
|
|
10863
|
+
var CONFIG_FILE_MODE = 384;
|
|
10864
10864
|
var Storage = class {
|
|
10865
10865
|
config;
|
|
10866
|
+
options;
|
|
10867
|
+
constructor(options = {}) {
|
|
10868
|
+
this.options = options;
|
|
10869
|
+
}
|
|
10866
10870
|
getConfig() {
|
|
10867
10871
|
if (!this.config) {
|
|
10868
10872
|
this.config = new Conf({
|
|
10869
10873
|
projectName: "link-cli",
|
|
10874
|
+
configFileMode: CONFIG_FILE_MODE,
|
|
10875
|
+
...this.options.cwd ? { cwd: this.options.cwd } : {},
|
|
10870
10876
|
defaults: {
|
|
10871
10877
|
auth: null,
|
|
10872
10878
|
pendingDeviceAuth: null
|
|
@@ -11488,7 +11494,7 @@ var AuthStatus = ({ onComplete }) => {
|
|
|
11488
11494
|
|
|
11489
11495
|
// src/commands/auth/index.tsx
|
|
11490
11496
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
11491
|
-
function createAuthCli(authResource,
|
|
11497
|
+
function createAuthCli(authResource, getUpdateInfo2) {
|
|
11492
11498
|
const cli2 = Cli.create("auth", {
|
|
11493
11499
|
description: "Authentication commands"
|
|
11494
11500
|
});
|
|
@@ -11574,6 +11580,18 @@ function createAuthCli(authResource, updateInfo) {
|
|
|
11574
11580
|
options: statusOptions,
|
|
11575
11581
|
outputPolicy: "agent-only",
|
|
11576
11582
|
async *run(c) {
|
|
11583
|
+
const opts = c.options;
|
|
11584
|
+
const interval = opts.interval;
|
|
11585
|
+
const maxAttempts = opts.maxAttempts;
|
|
11586
|
+
const deadline = Date.now() + opts.timeout * 1e3;
|
|
11587
|
+
const updateInfo = await getUpdateInfo2?.({
|
|
11588
|
+
polling: interval > 0
|
|
11589
|
+
});
|
|
11590
|
+
const update = updateInfo ? {
|
|
11591
|
+
current_version: updateInfo.current,
|
|
11592
|
+
latest_version: updateInfo.latest,
|
|
11593
|
+
update_command: "npm install -g @stripe/link-cli"
|
|
11594
|
+
} : void 0;
|
|
11577
11595
|
if (!c.agent && !c.formatExplicit) {
|
|
11578
11596
|
return new Promise((resolve) => {
|
|
11579
11597
|
const { waitUntilExit } = render(
|
|
@@ -11588,15 +11606,12 @@ function createAuthCli(authResource, updateInfo) {
|
|
|
11588
11606
|
access_token: `${auth.access_token.substring(0, 20)}...`,
|
|
11589
11607
|
token_type: auth.token_type
|
|
11590
11608
|
} : {},
|
|
11591
|
-
credentials_path: storage.getPath()
|
|
11609
|
+
credentials_path: storage.getPath(),
|
|
11610
|
+
...update && { update }
|
|
11592
11611
|
});
|
|
11593
11612
|
});
|
|
11594
11613
|
});
|
|
11595
11614
|
}
|
|
11596
|
-
const opts = c.options;
|
|
11597
|
-
const interval = opts.interval;
|
|
11598
|
-
const maxAttempts = opts.maxAttempts;
|
|
11599
|
-
const deadline = Date.now() + opts.timeout * 1e3;
|
|
11600
11615
|
let attempts = 0;
|
|
11601
11616
|
let previousAttemptData;
|
|
11602
11617
|
while (true) {
|
|
@@ -11609,11 +11624,6 @@ function createAuthCli(authResource, updateInfo) {
|
|
|
11609
11624
|
}
|
|
11610
11625
|
}
|
|
11611
11626
|
const auth = storage.getAuth();
|
|
11612
|
-
const update = updateInfo ? {
|
|
11613
|
-
current_version: updateInfo.current,
|
|
11614
|
-
latest_version: updateInfo.latest,
|
|
11615
|
-
update_command: "npm install -g @stripe/link-cli"
|
|
11616
|
-
} : void 0;
|
|
11617
11627
|
if (auth) {
|
|
11618
11628
|
yield {
|
|
11619
11629
|
authenticated: true,
|
|
@@ -12293,16 +12303,16 @@ import { useEffect as useEffect6, useRef as useRef2, useState as useState6 } fro
|
|
|
12293
12303
|
|
|
12294
12304
|
// src/commands/mpp/decode.ts
|
|
12295
12305
|
import { Challenge } from "mppx";
|
|
12296
|
-
function getString(value,
|
|
12306
|
+
function getString(value, path6, required = true) {
|
|
12297
12307
|
if (value == null) {
|
|
12298
12308
|
if (required) {
|
|
12299
|
-
throw new Error(`Invalid stripe challenge request: ${
|
|
12309
|
+
throw new Error(`Invalid stripe challenge request: ${path6}: missing`);
|
|
12300
12310
|
}
|
|
12301
12311
|
return void 0;
|
|
12302
12312
|
}
|
|
12303
12313
|
if (typeof value !== "string") {
|
|
12304
12314
|
throw new Error(
|
|
12305
|
-
`Invalid stripe challenge request: ${
|
|
12315
|
+
`Invalid stripe challenge request: ${path6}: expected string, received ${typeof value}`
|
|
12306
12316
|
);
|
|
12307
12317
|
}
|
|
12308
12318
|
return value;
|
|
@@ -13562,6 +13572,28 @@ function createPaymentMethodsCli(createResource) {
|
|
|
13562
13572
|
import { Cli as Cli6, z as z7 } from "incur";
|
|
13563
13573
|
import { render as render6 } from "ink";
|
|
13564
13574
|
|
|
13575
|
+
// src/utils/credential-output.ts
|
|
13576
|
+
import fs4 from "fs/promises";
|
|
13577
|
+
import path5 from "path";
|
|
13578
|
+
async function writeCredentialFile(filePath, data, force) {
|
|
13579
|
+
const resolved = path5.resolve(filePath);
|
|
13580
|
+
if (!force) {
|
|
13581
|
+
try {
|
|
13582
|
+
await fs4.access(resolved);
|
|
13583
|
+
throw new Error(
|
|
13584
|
+
`OUTPUT_FILE_EXISTS: ${resolved} already exists. Use --force to overwrite.`
|
|
13585
|
+
);
|
|
13586
|
+
} catch (err) {
|
|
13587
|
+
if (err.code !== "ENOENT") throw err;
|
|
13588
|
+
}
|
|
13589
|
+
}
|
|
13590
|
+
await fs4.writeFile(resolved, JSON.stringify(data, null, 2), {
|
|
13591
|
+
mode: 384
|
|
13592
|
+
});
|
|
13593
|
+
await fs4.chmod(resolved, 384);
|
|
13594
|
+
return resolved;
|
|
13595
|
+
}
|
|
13596
|
+
|
|
13565
13597
|
// src/utils/line-item-parser.ts
|
|
13566
13598
|
import { z as z5 } from "zod";
|
|
13567
13599
|
var LineItemSchema = z5.object({
|
|
@@ -13725,11 +13757,15 @@ var CreateSpendRequest = ({
|
|
|
13725
13757
|
repository,
|
|
13726
13758
|
params,
|
|
13727
13759
|
requestApproval = false,
|
|
13760
|
+
outputFile,
|
|
13761
|
+
force,
|
|
13728
13762
|
onComplete
|
|
13729
13763
|
}) => {
|
|
13730
13764
|
const [status, setStatus] = useState10("creating");
|
|
13731
13765
|
const [request, setRequest] = useState10(null);
|
|
13732
13766
|
const [error, setError] = useState10("");
|
|
13767
|
+
const [outputFilePath, setOutputFilePath] = useState10(null);
|
|
13768
|
+
const [fileError, setFileError] = useState10("");
|
|
13733
13769
|
const approvalUrl = request?.approval_url ?? "";
|
|
13734
13770
|
const onSuccess = useCallback2(
|
|
13735
13771
|
(result) => setRequest(result),
|
|
@@ -13765,6 +13801,18 @@ var CreateSpendRequest = ({
|
|
|
13765
13801
|
};
|
|
13766
13802
|
create();
|
|
13767
13803
|
}, [repository, params, requestApproval, onComplete]);
|
|
13804
|
+
useEffect10(() => {
|
|
13805
|
+
if (status !== "success" || !outputFile || !request?.card) return;
|
|
13806
|
+
const fileData = {
|
|
13807
|
+
spend_request_id: request.id,
|
|
13808
|
+
merchant_name: request.merchant_name,
|
|
13809
|
+
merchant_url: request.merchant_url,
|
|
13810
|
+
context: request.context,
|
|
13811
|
+
created_at: request.created_at,
|
|
13812
|
+
card: request.card
|
|
13813
|
+
};
|
|
13814
|
+
writeCredentialFile(outputFile, fileData, force ?? false).then((path6) => setOutputFilePath(path6)).catch((err) => setFileError(err.message));
|
|
13815
|
+
}, [status, outputFile, force, request]);
|
|
13768
13816
|
if (status === "creating") {
|
|
13769
13817
|
return /* @__PURE__ */ jsx20(Box14, { children: /* @__PURE__ */ jsxs13(Text15, { color: "cyan", children: [
|
|
13770
13818
|
/* @__PURE__ */ jsx20(Spinner5, { type: "dots" }),
|
|
@@ -13808,6 +13856,49 @@ var CreateSpendRequest = ({
|
|
|
13808
13856
|
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request.shared_payment_token.id })
|
|
13809
13857
|
] })
|
|
13810
13858
|
] }),
|
|
13859
|
+
request?.card && !outputFile && /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginTop: 1, children: [
|
|
13860
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: "Card Details:" }),
|
|
13861
|
+
/* @__PURE__ */ jsxs13(Text15, { children: [
|
|
13862
|
+
" ",
|
|
13863
|
+
"Number: ",
|
|
13864
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request.card.number })
|
|
13865
|
+
] }),
|
|
13866
|
+
/* @__PURE__ */ jsxs13(Text15, { children: [
|
|
13867
|
+
" ",
|
|
13868
|
+
"Brand: ",
|
|
13869
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request.card.brand })
|
|
13870
|
+
] }),
|
|
13871
|
+
/* @__PURE__ */ jsxs13(Text15, { children: [
|
|
13872
|
+
" ",
|
|
13873
|
+
"Expiry:",
|
|
13874
|
+
" ",
|
|
13875
|
+
/* @__PURE__ */ jsxs13(Text15, { bold: true, children: [
|
|
13876
|
+
String(request.card.exp_month).padStart(2, "0"),
|
|
13877
|
+
"/",
|
|
13878
|
+
request.card.exp_year
|
|
13879
|
+
] })
|
|
13880
|
+
] }),
|
|
13881
|
+
request.card.cvc && /* @__PURE__ */ jsxs13(Text15, { children: [
|
|
13882
|
+
" ",
|
|
13883
|
+
"CVC: ",
|
|
13884
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request.card.cvc })
|
|
13885
|
+
] }),
|
|
13886
|
+
request.card.valid_until && /* @__PURE__ */ jsxs13(Text15, { children: [
|
|
13887
|
+
" ",
|
|
13888
|
+
"Valid Until: ",
|
|
13889
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: request.card.valid_until })
|
|
13890
|
+
] })
|
|
13891
|
+
] }),
|
|
13892
|
+
request?.card && outputFile && /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginTop: 1, children: [
|
|
13893
|
+
outputFilePath && /* @__PURE__ */ jsxs13(Text15, { color: "green", children: [
|
|
13894
|
+
"Card credentials written to ",
|
|
13895
|
+
/* @__PURE__ */ jsx20(Text15, { bold: true, children: outputFilePath })
|
|
13896
|
+
] }),
|
|
13897
|
+
fileError && /* @__PURE__ */ jsxs13(Text15, { color: "red", children: [
|
|
13898
|
+
"Failed to write card file: ",
|
|
13899
|
+
fileError
|
|
13900
|
+
] })
|
|
13901
|
+
] }),
|
|
13811
13902
|
/* @__PURE__ */ jsx20(AppDownloadQrCodes, {})
|
|
13812
13903
|
] });
|
|
13813
13904
|
}
|
|
@@ -13925,12 +14016,16 @@ var RetrieveSpendRequest = ({
|
|
|
13925
14016
|
id,
|
|
13926
14017
|
timeout = 300,
|
|
13927
14018
|
include,
|
|
14019
|
+
outputFile,
|
|
14020
|
+
force,
|
|
13928
14021
|
onComplete
|
|
13929
14022
|
}) => {
|
|
13930
14023
|
const [phase, setPhase] = useState12("fetching");
|
|
13931
14024
|
const [request, setRequest] = useState12(null);
|
|
13932
14025
|
const [error, setError] = useState12("");
|
|
13933
14026
|
const [elapsed, setElapsed] = useState12(0);
|
|
14027
|
+
const [outputFilePath, setOutputFilePath] = useState12(null);
|
|
14028
|
+
const [fileError, setFileError] = useState12("");
|
|
13934
14029
|
const startTimeRef = useRef4(Date.now());
|
|
13935
14030
|
const pollRef = useRef4(null);
|
|
13936
14031
|
const timerRef = useRef4(null);
|
|
@@ -13941,6 +14036,18 @@ var RetrieveSpendRequest = ({
|
|
|
13941
14036
|
if (timerRef.current) clearInterval(timerRef.current);
|
|
13942
14037
|
};
|
|
13943
14038
|
}, []);
|
|
14039
|
+
useEffect12(() => {
|
|
14040
|
+
if (phase !== "success" || !outputFile || !request?.card) return;
|
|
14041
|
+
const fileData = {
|
|
14042
|
+
spend_request_id: request.id,
|
|
14043
|
+
merchant_name: request.merchant_name,
|
|
14044
|
+
merchant_url: request.merchant_url,
|
|
14045
|
+
context: request.context,
|
|
14046
|
+
created_at: request.created_at,
|
|
14047
|
+
card: request.card
|
|
14048
|
+
};
|
|
14049
|
+
writeCredentialFile(outputFile, fileData, force ?? false).then((path6) => setOutputFilePath(path6)).catch((err) => setFileError(err.message));
|
|
14050
|
+
}, [phase, outputFile, force, request]);
|
|
13944
14051
|
useEffect12(() => {
|
|
13945
14052
|
const fetch2 = async () => {
|
|
13946
14053
|
try {
|
|
@@ -14121,7 +14228,7 @@ var RetrieveSpendRequest = ({
|
|
|
14121
14228
|
/* @__PURE__ */ jsx22(Text17, { bold: true, children: request.shared_payment_token.id })
|
|
14122
14229
|
] })
|
|
14123
14230
|
] }),
|
|
14124
|
-
request?.card && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, children: [
|
|
14231
|
+
request?.card && !outputFile && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, children: [
|
|
14125
14232
|
/* @__PURE__ */ jsx22(Text17, { bold: true, children: "Card Details:" }),
|
|
14126
14233
|
/* @__PURE__ */ jsxs15(Text17, { children: [
|
|
14127
14234
|
" ",
|
|
@@ -14180,6 +14287,16 @@ var RetrieveSpendRequest = ({
|
|
|
14180
14287
|
request.card.billing_address.country
|
|
14181
14288
|
] })
|
|
14182
14289
|
] })
|
|
14290
|
+
] }),
|
|
14291
|
+
request?.card && outputFile && /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginTop: 1, children: [
|
|
14292
|
+
outputFilePath && /* @__PURE__ */ jsxs15(Text17, { color: "green", children: [
|
|
14293
|
+
"Card credentials written to ",
|
|
14294
|
+
/* @__PURE__ */ jsx22(Text17, { bold: true, children: outputFilePath })
|
|
14295
|
+
] }),
|
|
14296
|
+
fileError && /* @__PURE__ */ jsxs15(Text17, { color: "red", children: [
|
|
14297
|
+
"Failed to write card file: ",
|
|
14298
|
+
fileError
|
|
14299
|
+
] })
|
|
14183
14300
|
] })
|
|
14184
14301
|
] })
|
|
14185
14302
|
] });
|
|
@@ -14210,12 +14327,16 @@ var createOptions = z6.object({
|
|
|
14210
14327
|
'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"'
|
|
14211
14328
|
),
|
|
14212
14329
|
total: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe(
|
|
14213
|
-
'Total (repeatable, key:value format). Keys: type (required), display_text (required), amount (required). Example: "type:total,display_text:Total,amount:5000"'
|
|
14330
|
+
'Total (repeatable, key:value format). Keys: type (required; one of: subtotal, tax, total), display_text (required), amount (required). Example: "type:total,display_text:Total,amount:5000"'
|
|
14214
14331
|
),
|
|
14215
14332
|
requestApproval: z6.boolean().default(true).describe("Request approval and poll until approved/denied/expired"),
|
|
14216
14333
|
test: z6.boolean().default(false).describe(
|
|
14217
14334
|
"Use test mode (creates testmode credentials from test card data)"
|
|
14218
|
-
)
|
|
14335
|
+
),
|
|
14336
|
+
outputFile: z6.string().optional().describe(
|
|
14337
|
+
"Write full card credentials to this file path; stdout shows redacted card data only"
|
|
14338
|
+
),
|
|
14339
|
+
force: z6.boolean().default(false).describe("Overwrite output file if it already exists")
|
|
14219
14340
|
});
|
|
14220
14341
|
var retrieveOptions = z6.object({
|
|
14221
14342
|
timeout: z6.coerce.number().default(300).describe(
|
|
@@ -14227,7 +14348,11 @@ var retrieveOptions = z6.object({
|
|
|
14227
14348
|
maxAttempts: z6.coerce.number().default(0).describe(
|
|
14228
14349
|
"Max poll attempts. 0 = unlimited. Exhaustion during active polling exits non-zero with POLLING_TIMEOUT."
|
|
14229
14350
|
),
|
|
14230
|
-
include: z6.array(z6.string()).default([]).describe("Include extra data (repeatable, e.g. --include card)")
|
|
14351
|
+
include: z6.array(z6.string()).default([]).describe("Include extra data (repeatable, e.g. --include card)"),
|
|
14352
|
+
outputFile: z6.string().optional().describe(
|
|
14353
|
+
"Write full card credentials to this file path; stdout shows redacted card data only"
|
|
14354
|
+
),
|
|
14355
|
+
force: z6.boolean().default(false).describe("Overwrite output file if it already exists")
|
|
14231
14356
|
});
|
|
14232
14357
|
var updateOptions = z6.object({
|
|
14233
14358
|
paymentMethodId: z6.string().optional().describe("Payment method ID"),
|
|
@@ -14240,7 +14365,7 @@ var updateOptions = z6.object({
|
|
|
14240
14365
|
'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"'
|
|
14241
14366
|
),
|
|
14242
14367
|
total: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe(
|
|
14243
|
-
'Total (repeatable, key:value format). Keys: type (required), display_text (required), amount (required). Example: "type:total,display_text:Total,amount:5000"'
|
|
14368
|
+
'Total (repeatable, key:value format). Keys: type (required; one of: subtotal, tax, total), display_text (required), amount (required). Example: "type:total,display_text:Total,amount:5000"'
|
|
14244
14369
|
)
|
|
14245
14370
|
});
|
|
14246
14371
|
|
|
@@ -14323,6 +14448,23 @@ var UpdateSpendRequest = ({
|
|
|
14323
14448
|
|
|
14324
14449
|
// src/commands/spend-request/index.tsx
|
|
14325
14450
|
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
14451
|
+
async function applyOutputFile(request, outputFile, force) {
|
|
14452
|
+
if (!outputFile || !request.card) return request;
|
|
14453
|
+
const fileData = {
|
|
14454
|
+
spend_request_id: request.id,
|
|
14455
|
+
merchant_name: request.merchant_name,
|
|
14456
|
+
merchant_url: request.merchant_url,
|
|
14457
|
+
context: request.context,
|
|
14458
|
+
created_at: request.created_at,
|
|
14459
|
+
card: request.card
|
|
14460
|
+
};
|
|
14461
|
+
const resolvedPath = await writeCredentialFile(outputFile, fileData, force);
|
|
14462
|
+
const { card: _, ...withoutCard } = request;
|
|
14463
|
+
return {
|
|
14464
|
+
...withoutCard,
|
|
14465
|
+
card_output_file: resolvedPath
|
|
14466
|
+
};
|
|
14467
|
+
}
|
|
14326
14468
|
function createSpendRequestCli(repository) {
|
|
14327
14469
|
const cli2 = Cli6.create("spend-request", {
|
|
14328
14470
|
description: "Spend request management commands"
|
|
@@ -14400,6 +14542,8 @@ function createSpendRequestCli(repository) {
|
|
|
14400
14542
|
request_approval: requestApproval || void 0,
|
|
14401
14543
|
test: opts.test ? true : void 0
|
|
14402
14544
|
};
|
|
14545
|
+
const outputFile = opts.outputFile;
|
|
14546
|
+
const forceOverwrite = opts.force;
|
|
14403
14547
|
if (!c.agent && !c.formatExplicit) {
|
|
14404
14548
|
return new Promise((resolve) => {
|
|
14405
14549
|
let capturedResult = null;
|
|
@@ -14410,6 +14554,8 @@ function createSpendRequestCli(repository) {
|
|
|
14410
14554
|
repository,
|
|
14411
14555
|
params: createParams,
|
|
14412
14556
|
requestApproval,
|
|
14557
|
+
outputFile,
|
|
14558
|
+
force: forceOverwrite,
|
|
14413
14559
|
onComplete: (result) => {
|
|
14414
14560
|
capturedResult = result;
|
|
14415
14561
|
}
|
|
@@ -14423,7 +14569,15 @@ function createSpendRequestCli(repository) {
|
|
|
14423
14569
|
}
|
|
14424
14570
|
const created = await repository.createSpendRequest(createParams);
|
|
14425
14571
|
if (!requestApproval) {
|
|
14426
|
-
|
|
14572
|
+
try {
|
|
14573
|
+
yield await applyOutputFile(created, outputFile, forceOverwrite);
|
|
14574
|
+
} catch (err) {
|
|
14575
|
+
const message = err.message;
|
|
14576
|
+
if (message.startsWith("OUTPUT_FILE_EXISTS")) {
|
|
14577
|
+
return c.error({ code: "OUTPUT_FILE_EXISTS", message });
|
|
14578
|
+
}
|
|
14579
|
+
return c.error({ code: "OUTPUT_FILE_WRITE_ERROR", message });
|
|
14580
|
+
}
|
|
14427
14581
|
return;
|
|
14428
14582
|
}
|
|
14429
14583
|
yield {
|
|
@@ -14574,6 +14728,8 @@ function createSpendRequestCli(repository) {
|
|
|
14574
14728
|
const maxAttempts = opts.maxAttempts;
|
|
14575
14729
|
const includeArr = opts.include;
|
|
14576
14730
|
const include = includeArr?.length ? includeArr : void 0;
|
|
14731
|
+
const outputFile = opts.outputFile;
|
|
14732
|
+
const forceOverwrite = opts.force;
|
|
14577
14733
|
if (!c.agent && !c.formatExplicit) {
|
|
14578
14734
|
return new Promise((resolve) => {
|
|
14579
14735
|
let capturedResult = null;
|
|
@@ -14585,6 +14741,8 @@ function createSpendRequestCli(repository) {
|
|
|
14585
14741
|
id,
|
|
14586
14742
|
timeout,
|
|
14587
14743
|
include,
|
|
14744
|
+
outputFile,
|
|
14745
|
+
force: forceOverwrite,
|
|
14588
14746
|
onComplete: (result) => {
|
|
14589
14747
|
capturedResult = result;
|
|
14590
14748
|
}
|
|
@@ -14614,15 +14772,20 @@ function createSpendRequestCli(repository) {
|
|
|
14614
14772
|
message: `Spend request ${id} not found`
|
|
14615
14773
|
});
|
|
14616
14774
|
}
|
|
14617
|
-
|
|
14618
|
-
|
|
14775
|
+
const shouldEmitFinal = terminalStatuses.has(request.status) || interval <= 0;
|
|
14776
|
+
if (shouldEmitFinal) {
|
|
14777
|
+
try {
|
|
14778
|
+
yield await applyOutputFile(request, outputFile, forceOverwrite);
|
|
14779
|
+
} catch (err) {
|
|
14780
|
+
const message = err.message;
|
|
14781
|
+
if (message.startsWith("OUTPUT_FILE_EXISTS")) {
|
|
14782
|
+
return c.error({ code: "OUTPUT_FILE_EXISTS", message });
|
|
14783
|
+
}
|
|
14784
|
+
return c.error({ code: "OUTPUT_FILE_WRITE_ERROR", message });
|
|
14785
|
+
}
|
|
14619
14786
|
return;
|
|
14620
14787
|
}
|
|
14621
14788
|
attempts++;
|
|
14622
|
-
if (interval <= 0) {
|
|
14623
|
-
yield request;
|
|
14624
|
-
return;
|
|
14625
|
-
}
|
|
14626
14789
|
const maxAttemptsExhausted = maxAttempts > 0 && attempts >= maxAttempts;
|
|
14627
14790
|
const timeoutReached = Date.now() >= deadline;
|
|
14628
14791
|
if (maxAttemptsExhausted || timeoutReached) {
|
|
@@ -14944,8 +15107,78 @@ var ResourceFactory = class {
|
|
|
14944
15107
|
}
|
|
14945
15108
|
};
|
|
14946
15109
|
|
|
15110
|
+
// src/utils/update-info.ts
|
|
15111
|
+
import process8 from "process";
|
|
15112
|
+
import updateNotifier from "update-notifier";
|
|
15113
|
+
var UPDATE_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
15114
|
+
var FAILURE_CACHE_TTL_MS = 60 * 1e3;
|
|
15115
|
+
var cachedUpdateInfo;
|
|
15116
|
+
var inflightUpdateInfo;
|
|
15117
|
+
function createAgentUpdateInfoProvider(packageName, cliVersion2) {
|
|
15118
|
+
return async ({ polling }) => {
|
|
15119
|
+
if (polling) {
|
|
15120
|
+
return cachedUpdateInfo?.value;
|
|
15121
|
+
}
|
|
15122
|
+
const now = Date.now();
|
|
15123
|
+
if (cachedUpdateInfo && cachedUpdateInfo.expiresAt > now) {
|
|
15124
|
+
return cachedUpdateInfo.value;
|
|
15125
|
+
}
|
|
15126
|
+
if (!inflightUpdateInfo) {
|
|
15127
|
+
inflightUpdateInfo = fetchLatestVersion(packageName, cliVersion2).finally(
|
|
15128
|
+
() => {
|
|
15129
|
+
inflightUpdateInfo = void 0;
|
|
15130
|
+
}
|
|
15131
|
+
);
|
|
15132
|
+
}
|
|
15133
|
+
return inflightUpdateInfo;
|
|
15134
|
+
};
|
|
15135
|
+
}
|
|
15136
|
+
function createInteractiveUpdateInfoProvider(updateInfo) {
|
|
15137
|
+
return async () => updateInfo;
|
|
15138
|
+
}
|
|
15139
|
+
function renderInteractiveUpdateNotice(updateInfo) {
|
|
15140
|
+
return [
|
|
15141
|
+
"",
|
|
15142
|
+
`Update available for @stripe/link-cli: ${updateInfo.current} -> ${updateInfo.latest}`,
|
|
15143
|
+
"Run: npm install -g @stripe/link-cli",
|
|
15144
|
+
""
|
|
15145
|
+
].join("\n");
|
|
15146
|
+
}
|
|
15147
|
+
async function fetchLatestVersion(packageName, cliVersion2) {
|
|
15148
|
+
const previousDisableFlag = process8.env.NO_UPDATE_NOTIFIER;
|
|
15149
|
+
try {
|
|
15150
|
+
process8.env.NO_UPDATE_NOTIFIER = "1";
|
|
15151
|
+
const notifier = updateNotifier({
|
|
15152
|
+
pkg: {
|
|
15153
|
+
name: packageName,
|
|
15154
|
+
version: cliVersion2
|
|
15155
|
+
}
|
|
15156
|
+
});
|
|
15157
|
+
const payload = await notifier.fetchInfo();
|
|
15158
|
+
const latest = payload.latest?.trim();
|
|
15159
|
+
const value = latest && latest.length > 0 && latest !== cliVersion2 ? { current: cliVersion2, latest } : void 0;
|
|
15160
|
+
cacheUpdateInfo(value);
|
|
15161
|
+
return value;
|
|
15162
|
+
} catch {
|
|
15163
|
+
cacheUpdateInfo(void 0, FAILURE_CACHE_TTL_MS);
|
|
15164
|
+
return void 0;
|
|
15165
|
+
} finally {
|
|
15166
|
+
if (previousDisableFlag === void 0) {
|
|
15167
|
+
process8.env.NO_UPDATE_NOTIFIER = void 0;
|
|
15168
|
+
} else {
|
|
15169
|
+
process8.env.NO_UPDATE_NOTIFIER = previousDisableFlag;
|
|
15170
|
+
}
|
|
15171
|
+
}
|
|
15172
|
+
}
|
|
15173
|
+
function cacheUpdateInfo(value, ttlMs = UPDATE_CACHE_TTL_MS) {
|
|
15174
|
+
cachedUpdateInfo = {
|
|
15175
|
+
expiresAt: Date.now() + ttlMs,
|
|
15176
|
+
value
|
|
15177
|
+
};
|
|
15178
|
+
}
|
|
15179
|
+
|
|
14947
15180
|
// src/cli.tsx
|
|
14948
|
-
var cliVersion = "0.4.
|
|
15181
|
+
var cliVersion = "0.4.3";
|
|
14949
15182
|
var buildNumber = "1";
|
|
14950
15183
|
var cliName = "@stripe/link-cli";
|
|
14951
15184
|
var defaultHeaders = {
|
|
@@ -14956,14 +15189,24 @@ var verbose = process.argv.includes("--verbose");
|
|
|
14956
15189
|
var factory = new ResourceFactory({ verbose, defaultHeaders });
|
|
14957
15190
|
var authRepo = factory.createAuthResource();
|
|
14958
15191
|
var spendRequestRepo = factory.createSpendRequestResource();
|
|
14959
|
-
var notifier = updateNotifier({
|
|
14960
|
-
pkg: { name: cliName, version: cliVersion }
|
|
14961
|
-
});
|
|
14962
15192
|
var cli = Cli7.create("link-cli", {
|
|
14963
15193
|
description: "Create a secure, one-time payment credential from a Link wallet to let agents complete purchases on behalf of users.",
|
|
14964
15194
|
version: `${cliVersion} (build ${buildNumber})`
|
|
14965
15195
|
});
|
|
14966
|
-
|
|
15196
|
+
var isAgent = process.argv.includes("--format") || process.argv.includes("--mcp");
|
|
15197
|
+
var agentUpdateInfoProvider = createAgentUpdateInfoProvider(
|
|
15198
|
+
cliName,
|
|
15199
|
+
cliVersion
|
|
15200
|
+
);
|
|
15201
|
+
var getUpdateInfo = agentUpdateInfoProvider;
|
|
15202
|
+
if (!isAgent && process.stdout.isTTY) {
|
|
15203
|
+
const updateInfo = await agentUpdateInfoProvider({ polling: false });
|
|
15204
|
+
getUpdateInfo = createInteractiveUpdateInfoProvider(updateInfo);
|
|
15205
|
+
if (updateInfo) {
|
|
15206
|
+
process.stderr.write(renderInteractiveUpdateNotice(updateInfo));
|
|
15207
|
+
}
|
|
15208
|
+
}
|
|
15209
|
+
cli.command(createAuthCli(authRepo, getUpdateInfo));
|
|
14967
15210
|
cli.command(createSpendRequestCli(spendRequestRepo));
|
|
14968
15211
|
cli.command(
|
|
14969
15212
|
createPaymentMethodsCli(() => factory.createPaymentMethodsResource())
|
|
@@ -14983,10 +15226,6 @@ cli.command(
|
|
|
14983
15226
|
() => factory.createPaymentMethodsResource()
|
|
14984
15227
|
)
|
|
14985
15228
|
);
|
|
14986
|
-
var isAgent = process.argv.includes("--format") || process.argv.includes("--mcp");
|
|
14987
|
-
if (!isAgent) {
|
|
14988
|
-
notifier.notify({ defer: false });
|
|
14989
|
-
}
|
|
14990
15229
|
cli.serve();
|
|
14991
15230
|
var cli_default = cli;
|
|
14992
15231
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stripe/link-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"link-cli": "./dist/cli.js"
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"tsx": "^4.21.0",
|
|
41
41
|
"typescript": "^5.9.3",
|
|
42
42
|
"vitest": "^4.1.2",
|
|
43
|
-
"@stripe/link-
|
|
44
|
-
"@stripe/link-
|
|
43
|
+
"@stripe/link-typescript-config": "0.0.0",
|
|
44
|
+
"@stripe/link-sdk": "1.0.0"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsup",
|