@stripe/link-cli 0.16.0 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -1
- package/dist/cli.js +187 -27
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -111,6 +111,29 @@ link-cli auth login
|
|
|
111
111
|
|
|
112
112
|
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.
|
|
113
113
|
|
|
114
|
+
### Retrieve user info
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
link-cli user-info retrieve --format json
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
In addition to identity fields, the response can include Agent Wallet spend limits and step-up status:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"email": "jane@example.com",
|
|
125
|
+
"name": "Jane Doe",
|
|
126
|
+
"agent_wallet_spend_limits": {
|
|
127
|
+
"per_transaction": { "limit": 50000 },
|
|
128
|
+
"daily": { "limit": 500000, "used": 120000, "remaining": 380000 },
|
|
129
|
+
"thirty_day": { "limit": null, "used": 600000, "remaining": null }
|
|
130
|
+
},
|
|
131
|
+
"agent_wallet_step_up": { "status": "not_required" }
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Finite spend-limit values are cents because this response does not include a currency. A `null` limit or remaining amount means unlimited. Either Agent Wallet object can be absent when backend enrichment is unavailable; absence does not imply a limit or step-up status.
|
|
136
|
+
|
|
114
137
|
### List payment methods
|
|
115
138
|
|
|
116
139
|
```bash
|
|
@@ -496,4 +519,4 @@ pnpm turbo run build
|
|
|
496
519
|
pnpm --filter @stripe/link-cli --filter @stripe/link-sdk publish --dry-run --no-git-checks
|
|
497
520
|
```
|
|
498
521
|
|
|
499
|
-
CI runs the same publish dry-run for every pull request.
|
|
522
|
+
CI runs the same publish dry-run for every pull request.
|
package/dist/cli.js
CHANGED
|
@@ -802,11 +802,13 @@ var SpendRequestResource = class extends BaseResource {
|
|
|
802
802
|
);
|
|
803
803
|
}
|
|
804
804
|
async update(id, params) {
|
|
805
|
+
const { approve, ...body } = params;
|
|
806
|
+
const url = approve ? `${this.endpoint}/${id}/update_delegated` : `${this.endpoint}/${id}`;
|
|
805
807
|
const { status, data, rawBody } = await this.apiFetch({
|
|
806
808
|
method: "POST",
|
|
807
|
-
url
|
|
809
|
+
url,
|
|
808
810
|
headers: { "Content-Type": "application/json" },
|
|
809
|
-
body: JSON.stringify(
|
|
811
|
+
body: JSON.stringify(body)
|
|
810
812
|
});
|
|
811
813
|
if (status < 200 || status >= 300) {
|
|
812
814
|
this.throwApiError("update spend request", status, data, rawBody);
|
|
@@ -932,19 +934,54 @@ var TransactionsResource = class extends BaseResource {
|
|
|
932
934
|
);
|
|
933
935
|
}
|
|
934
936
|
};
|
|
937
|
+
var rollingSpendLimitSchema = z8.object({
|
|
938
|
+
limit: z8.number().int().nullable(),
|
|
939
|
+
used: z8.number().int(),
|
|
940
|
+
remaining: z8.number().int().nullable()
|
|
941
|
+
});
|
|
942
|
+
var agentWalletSpendLimitsSchema = z8.object({
|
|
943
|
+
per_transaction: z8.object({
|
|
944
|
+
limit: z8.number().int().nullable()
|
|
945
|
+
}),
|
|
946
|
+
daily: rollingSpendLimitSchema,
|
|
947
|
+
thirty_day: rollingSpendLimitSchema
|
|
948
|
+
});
|
|
949
|
+
var agentWalletStepUpSchema = z8.object({
|
|
950
|
+
status: z8.enum([
|
|
951
|
+
"not_required",
|
|
952
|
+
"ssn_verification",
|
|
953
|
+
"identity_verification",
|
|
954
|
+
"contact_support",
|
|
955
|
+
"complete"
|
|
956
|
+
])
|
|
957
|
+
});
|
|
935
958
|
var userInfoSchema = z8.looseObject({
|
|
936
959
|
email: z8.string().nullable().optional(),
|
|
937
960
|
name: z8.string().nullable().optional(),
|
|
938
961
|
first_name: z8.string().nullable().optional(),
|
|
939
962
|
last_name: z8.string().nullable().optional(),
|
|
940
|
-
phone: z8.string().nullable().optional()
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
963
|
+
phone: z8.string().nullable().optional(),
|
|
964
|
+
agent_wallet_spend_limits: agentWalletSpendLimitsSchema.optional(),
|
|
965
|
+
agent_wallet_step_up: agentWalletStepUpSchema.optional()
|
|
966
|
+
}).transform(
|
|
967
|
+
({
|
|
968
|
+
email,
|
|
969
|
+
name,
|
|
970
|
+
first_name,
|
|
971
|
+
last_name,
|
|
972
|
+
phone,
|
|
973
|
+
agent_wallet_spend_limits,
|
|
974
|
+
agent_wallet_step_up
|
|
975
|
+
}) => ({
|
|
976
|
+
email: email ?? null,
|
|
977
|
+
name: name ?? null,
|
|
978
|
+
first_name: first_name ?? null,
|
|
979
|
+
last_name: last_name ?? null,
|
|
980
|
+
phone: phone ?? null,
|
|
981
|
+
...agent_wallet_spend_limits === void 0 ? {} : { agent_wallet_spend_limits },
|
|
982
|
+
...agent_wallet_step_up === void 0 ? {} : { agent_wallet_step_up }
|
|
983
|
+
})
|
|
984
|
+
);
|
|
948
985
|
var UserInfoResource = class extends BaseResource {
|
|
949
986
|
constructor(options) {
|
|
950
987
|
super(options, "/userinfo");
|
|
@@ -2801,7 +2838,7 @@ function buildHeaders(data, headers) {
|
|
|
2801
2838
|
if (key) result[key] = value;
|
|
2802
2839
|
}
|
|
2803
2840
|
if (!Object.keys(result).some((key) => key.toLowerCase() === "user-agent")) {
|
|
2804
|
-
result["User-Agent"] = `link-cli/${"0.
|
|
2841
|
+
result["User-Agent"] = `link-cli/${"0.17.1"}`;
|
|
2805
2842
|
}
|
|
2806
2843
|
return result;
|
|
2807
2844
|
}
|
|
@@ -4939,6 +4976,7 @@ var CreateSpendRequest = ({
|
|
|
4939
4976
|
repository,
|
|
4940
4977
|
params,
|
|
4941
4978
|
requestApproval = false,
|
|
4979
|
+
approve = false,
|
|
4942
4980
|
outputFile,
|
|
4943
4981
|
force,
|
|
4944
4982
|
onComplete
|
|
@@ -5077,7 +5115,7 @@ var CreateSpendRequest = ({
|
|
|
5077
5115
|
result.status_details?.requires_action?.next_action ?? null
|
|
5078
5116
|
);
|
|
5079
5117
|
setStatus("requires_action");
|
|
5080
|
-
} else if (requestApproval) {
|
|
5118
|
+
} else if (requestApproval && result.status !== "approved") {
|
|
5081
5119
|
setStatus("waiting");
|
|
5082
5120
|
} else {
|
|
5083
5121
|
setStatus("success");
|
|
@@ -5352,7 +5390,7 @@ var CreateSpendRequest = ({
|
|
|
5352
5390
|
fileError
|
|
5353
5391
|
] })
|
|
5354
5392
|
] }),
|
|
5355
|
-
/* @__PURE__ */ jsx27(AppDownloadQrCodes, {})
|
|
5393
|
+
!approve && /* @__PURE__ */ jsx27(AppDownloadQrCodes, {})
|
|
5356
5394
|
] });
|
|
5357
5395
|
}
|
|
5358
5396
|
return /* @__PURE__ */ jsxs18(Fragment4, { children: [
|
|
@@ -6190,13 +6228,39 @@ var updateOptions = z19.object({
|
|
|
6190
6228
|
),
|
|
6191
6229
|
total: z19.array(z19.union([z19.string(), z19.record(z19.string(), z19.unknown())])).default([]).describe(
|
|
6192
6230
|
'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"'
|
|
6193
|
-
)
|
|
6231
|
+
),
|
|
6232
|
+
approve: z19.boolean().default(false).describe("Use the delegated approval flow for this update")
|
|
6194
6233
|
});
|
|
6195
6234
|
|
|
6196
6235
|
// src/commands/spend-request/update.tsx
|
|
6197
|
-
import { Box as Box22, Text as Text24 } from "ink";
|
|
6236
|
+
import { Box as Box22, Text as Text24, useInput as useInput10 } from "ink";
|
|
6198
6237
|
import Spinner14 from "ink-spinner";
|
|
6199
|
-
import { useCallback as useCallback11 } from "react";
|
|
6238
|
+
import { useCallback as useCallback11, useState as useState12 } from "react";
|
|
6239
|
+
|
|
6240
|
+
// src/utils/poll-until-spend-request-update.ts
|
|
6241
|
+
async function pollUntilSpendRequestUpdate(repository, id, pendingAmount, options = {}) {
|
|
6242
|
+
const pollIntervalMs = options.pollIntervalMs ?? 2e3;
|
|
6243
|
+
const timeoutMs = options.timeoutMs ?? 3e5;
|
|
6244
|
+
const startTime = Date.now();
|
|
6245
|
+
while (true) {
|
|
6246
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
6247
|
+
throw new Error("Spend request update polling timed out");
|
|
6248
|
+
}
|
|
6249
|
+
const request = await repository.retrieve(id);
|
|
6250
|
+
if (!request) {
|
|
6251
|
+
throw new Error(`Spend request ${id} not found`);
|
|
6252
|
+
}
|
|
6253
|
+
if (request.amount === pendingAmount) {
|
|
6254
|
+
return { request, outcome: "approved" };
|
|
6255
|
+
}
|
|
6256
|
+
if (!request.approval_url) {
|
|
6257
|
+
return { request, outcome: "denied" };
|
|
6258
|
+
}
|
|
6259
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
6260
|
+
}
|
|
6261
|
+
}
|
|
6262
|
+
|
|
6263
|
+
// src/commands/spend-request/update.tsx
|
|
6200
6264
|
import { jsx as jsx31, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
6201
6265
|
var UpdateSpendRequest = ({
|
|
6202
6266
|
repository,
|
|
@@ -6204,12 +6268,45 @@ var UpdateSpendRequest = ({
|
|
|
6204
6268
|
params,
|
|
6205
6269
|
onComplete
|
|
6206
6270
|
}) => {
|
|
6207
|
-
const
|
|
6208
|
-
|
|
6209
|
-
|
|
6271
|
+
const [pendingApproval, setPendingApproval] = useState12(
|
|
6272
|
+
null
|
|
6273
|
+
);
|
|
6274
|
+
const action = useCallback11(async () => {
|
|
6275
|
+
const request2 = await repository.update(id, params);
|
|
6276
|
+
if (params.amount !== void 0 && request2.status === "approved" && request2.approval_url) {
|
|
6277
|
+
setPendingApproval(request2);
|
|
6278
|
+
return pollUntilSpendRequestUpdate(repository, id, params.amount);
|
|
6279
|
+
}
|
|
6280
|
+
return { request: request2, outcome: "updated" };
|
|
6281
|
+
}, [repository, id, params]);
|
|
6282
|
+
const handleComplete = useCallback11(
|
|
6283
|
+
(result2) => onComplete(result2?.request ?? null),
|
|
6284
|
+
[onComplete]
|
|
6285
|
+
);
|
|
6286
|
+
const {
|
|
6287
|
+
status,
|
|
6288
|
+
data: result,
|
|
6289
|
+
error
|
|
6290
|
+
} = useAsyncAction(action, handleComplete);
|
|
6291
|
+
const request = result?.request ?? pendingApproval;
|
|
6292
|
+
useInput10(
|
|
6293
|
+
(_input, key) => {
|
|
6294
|
+
if (key.return && pendingApproval?.approval_url) {
|
|
6295
|
+
openUrl(pendingApproval.approval_url);
|
|
6296
|
+
}
|
|
6297
|
+
},
|
|
6298
|
+
{ isActive: status === "loading" && pendingApproval !== null }
|
|
6210
6299
|
);
|
|
6211
|
-
const { status, data: request, error } = useAsyncAction(action, onComplete);
|
|
6212
6300
|
if (status === "loading") {
|
|
6301
|
+
if (pendingApproval?.approval_url) {
|
|
6302
|
+
return /* @__PURE__ */ jsx31(
|
|
6303
|
+
ApprovalWaitingView,
|
|
6304
|
+
{
|
|
6305
|
+
status: "polling",
|
|
6306
|
+
approvalUrl: pendingApproval.approval_url
|
|
6307
|
+
}
|
|
6308
|
+
);
|
|
6309
|
+
}
|
|
6213
6310
|
return /* @__PURE__ */ jsx31(Box22, { children: /* @__PURE__ */ jsxs22(Text24, { color: "cyan", children: [
|
|
6214
6311
|
/* @__PURE__ */ jsx31(Spinner14, { type: "dots" }),
|
|
6215
6312
|
" Updating spend request ",
|
|
@@ -6223,6 +6320,21 @@ var UpdateSpendRequest = ({
|
|
|
6223
6320
|
/* @__PURE__ */ jsx31(Text24, { color: "red", children: error })
|
|
6224
6321
|
] });
|
|
6225
6322
|
}
|
|
6323
|
+
if (result?.outcome === "denied") {
|
|
6324
|
+
return /* @__PURE__ */ jsxs22(Box22, { flexDirection: "column", children: [
|
|
6325
|
+
/* @__PURE__ */ jsx31(Text24, { color: "yellow", children: "\u2717 Spend request update denied" }),
|
|
6326
|
+
/* @__PURE__ */ jsxs22(Box22, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
|
|
6327
|
+
/* @__PURE__ */ jsxs22(Text24, { children: [
|
|
6328
|
+
"ID: ",
|
|
6329
|
+
/* @__PURE__ */ jsx31(Text24, { bold: true, children: request?.id })
|
|
6330
|
+
] }),
|
|
6331
|
+
/* @__PURE__ */ jsxs22(Text24, { children: [
|
|
6332
|
+
"Amount: ",
|
|
6333
|
+
/* @__PURE__ */ jsx31(Text24, { bold: true, children: request?.amount ?? "N/A" })
|
|
6334
|
+
] })
|
|
6335
|
+
] })
|
|
6336
|
+
] });
|
|
6337
|
+
}
|
|
6226
6338
|
return /* @__PURE__ */ jsxs22(Box22, { flexDirection: "column", children: [
|
|
6227
6339
|
/* @__PURE__ */ jsx31(Text24, { color: "green", children: "\u2713 Spend request updated" }),
|
|
6228
6340
|
/* @__PURE__ */ jsxs22(Box22, { flexDirection: "column", marginTop: 1, paddingX: 2, children: [
|
|
@@ -6237,10 +6349,7 @@ var UpdateSpendRequest = ({
|
|
|
6237
6349
|
/* @__PURE__ */ jsxs22(Text24, { children: [
|
|
6238
6350
|
"Amount:",
|
|
6239
6351
|
" ",
|
|
6240
|
-
/* @__PURE__ */ jsx31(Text24, { bold: true, children: (
|
|
6241
|
-
const t = request?.totals?.find((t2) => t2.type === "total");
|
|
6242
|
-
return t ? String(t.amount) : "N/A";
|
|
6243
|
-
})() })
|
|
6352
|
+
/* @__PURE__ */ jsx31(Text24, { bold: true, children: request?.amount !== void 0 ? String(request.amount) : "N/A" })
|
|
6244
6353
|
] }),
|
|
6245
6354
|
/* @__PURE__ */ jsxs22(Text24, { children: [
|
|
6246
6355
|
"Merchant: ",
|
|
@@ -6452,6 +6561,7 @@ function createSpendRequestCli(repository, authStorage2, envAccessToken2) {
|
|
|
6452
6561
|
repository,
|
|
6453
6562
|
params: createParams,
|
|
6454
6563
|
requestApproval,
|
|
6564
|
+
approve: opts.approve ? true : void 0,
|
|
6455
6565
|
outputFile,
|
|
6456
6566
|
force: forceOverwrite,
|
|
6457
6567
|
onComplete: (result) => {
|
|
@@ -6558,6 +6668,9 @@ function createSpendRequestCli(repository, authStorage2, envAccessToken2) {
|
|
|
6558
6668
|
params.totals = opts.total.map(
|
|
6559
6669
|
(item) => typeof item === "string" ? parseTotalFlag(item) : item
|
|
6560
6670
|
);
|
|
6671
|
+
if (opts.approve !== void 0) {
|
|
6672
|
+
params.approve = opts.approve;
|
|
6673
|
+
}
|
|
6561
6674
|
if (!c.agent && !c.formatExplicit) {
|
|
6562
6675
|
let capturedResult = null;
|
|
6563
6676
|
return renderInteractive(
|
|
@@ -6938,6 +7051,9 @@ import { Box as Box24, Text as Text26 } from "ink";
|
|
|
6938
7051
|
import Spinner16 from "ink-spinner";
|
|
6939
7052
|
import { useCallback as useCallback13 } from "react";
|
|
6940
7053
|
import { jsx as jsx35, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
7054
|
+
function formatLimit(value) {
|
|
7055
|
+
return value === null ? "Unlimited" : `${value} cents`;
|
|
7056
|
+
}
|
|
6941
7057
|
var UserInfoRetrieve = ({
|
|
6942
7058
|
resource,
|
|
6943
7059
|
onComplete
|
|
@@ -6970,7 +7086,51 @@ var UserInfoRetrieve = ({
|
|
|
6970
7086
|
/* @__PURE__ */ jsxs24(Text26, { children: [
|
|
6971
7087
|
/* @__PURE__ */ jsx35(Text26, { dimColor: true, children: "Phone: " }),
|
|
6972
7088
|
userInfo?.phone ?? /* @__PURE__ */ jsx35(Text26, { dimColor: true, children: "Not set" })
|
|
6973
|
-
] })
|
|
7089
|
+
] }),
|
|
7090
|
+
userInfo?.agent_wallet_spend_limits && /* @__PURE__ */ jsxs24(Box24, { flexDirection: "column", marginTop: 1, children: [
|
|
7091
|
+
/* @__PURE__ */ jsx35(Text26, { bold: true, children: "Agent Wallet Spend Limits" }),
|
|
7092
|
+
/* @__PURE__ */ jsxs24(Box24, { flexDirection: "column", paddingLeft: 2, children: [
|
|
7093
|
+
/* @__PURE__ */ jsxs24(Text26, { children: [
|
|
7094
|
+
/* @__PURE__ */ jsx35(Text26, { dimColor: true, children: "Per-transaction: " }),
|
|
7095
|
+
formatLimit(
|
|
7096
|
+
userInfo.agent_wallet_spend_limits.per_transaction.limit
|
|
7097
|
+
)
|
|
7098
|
+
] }),
|
|
7099
|
+
/* @__PURE__ */ jsxs24(Text26, { children: [
|
|
7100
|
+
/* @__PURE__ */ jsx35(Text26, { dimColor: true, children: "Daily: " }),
|
|
7101
|
+
"limit",
|
|
7102
|
+
" ",
|
|
7103
|
+
formatLimit(userInfo.agent_wallet_spend_limits.daily.limit),
|
|
7104
|
+
", used ",
|
|
7105
|
+
userInfo.agent_wallet_spend_limits.daily.used,
|
|
7106
|
+
" cents, remaining",
|
|
7107
|
+
" ",
|
|
7108
|
+
formatLimit(
|
|
7109
|
+
userInfo.agent_wallet_spend_limits.daily.remaining
|
|
7110
|
+
)
|
|
7111
|
+
] }),
|
|
7112
|
+
/* @__PURE__ */ jsxs24(Text26, { children: [
|
|
7113
|
+
/* @__PURE__ */ jsx35(Text26, { dimColor: true, children: "30-day: " }),
|
|
7114
|
+
"limit",
|
|
7115
|
+
" ",
|
|
7116
|
+
formatLimit(
|
|
7117
|
+
userInfo.agent_wallet_spend_limits.thirty_day.limit
|
|
7118
|
+
),
|
|
7119
|
+
", used ",
|
|
7120
|
+
userInfo.agent_wallet_spend_limits.thirty_day.used,
|
|
7121
|
+
" ",
|
|
7122
|
+
"cents, remaining",
|
|
7123
|
+
" ",
|
|
7124
|
+
formatLimit(
|
|
7125
|
+
userInfo.agent_wallet_spend_limits.thirty_day.remaining
|
|
7126
|
+
)
|
|
7127
|
+
] })
|
|
7128
|
+
] })
|
|
7129
|
+
] }),
|
|
7130
|
+
userInfo?.agent_wallet_step_up && /* @__PURE__ */ jsx35(Box24, { marginTop: 1, children: /* @__PURE__ */ jsxs24(Text26, { children: [
|
|
7131
|
+
/* @__PURE__ */ jsx35(Text26, { dimColor: true, children: "Agent Wallet step-up status: " }),
|
|
7132
|
+
userInfo.agent_wallet_step_up.status
|
|
7133
|
+
] }) })
|
|
6974
7134
|
] })
|
|
6975
7135
|
] });
|
|
6976
7136
|
};
|
|
@@ -6982,7 +7142,7 @@ function createUserInfoCli(createResource, authStorage2, envAccessToken2) {
|
|
|
6982
7142
|
description: "User information commands"
|
|
6983
7143
|
});
|
|
6984
7144
|
cli2.command("retrieve", {
|
|
6985
|
-
description: "Retrieve user info
|
|
7145
|
+
description: "Retrieve user info, including optional Agent Wallet spend limits and step-up status",
|
|
6986
7146
|
outputPolicy: "agent-only",
|
|
6987
7147
|
middleware: [requireAuth(authStorage2, envAccessToken2)],
|
|
6988
7148
|
async run(c) {
|
|
@@ -7599,7 +7759,7 @@ function cacheUpdateInfo(value, ttlMs = UPDATE_CACHE_TTL_MS) {
|
|
|
7599
7759
|
}
|
|
7600
7760
|
|
|
7601
7761
|
// src/cli.tsx
|
|
7602
|
-
var cliVersion = "0.
|
|
7762
|
+
var cliVersion = "0.17.1";
|
|
7603
7763
|
var cliName = "@stripe/link-cli";
|
|
7604
7764
|
var defaultHeaders = {
|
|
7605
7765
|
"User-Agent": `link-cli/${cliVersion}`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stripe/link-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"link-cli": "./dist/cli.js"
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"tsx": "^4.23.5",
|
|
46
46
|
"typescript": "^5.9.3",
|
|
47
47
|
"vitest": "^4.1.10",
|
|
48
|
-
"@stripe/link-sdk": "0.
|
|
48
|
+
"@stripe/link-sdk": "0.3.1",
|
|
49
49
|
"@stripe/link-typescript-config": "0.0.0"
|
|
50
50
|
},
|
|
51
51
|
"scripts": {
|