@stripe/link-cli 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +25 -0
  2. package/dist/cli.js +41 -13
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -71,6 +71,23 @@ The `--request-approval` flag triggers a push notification (or email) to the use
71
71
 
72
72
  Users can easily approve requests with the [Link app](https://link.com/download).
73
73
 
74
+ #### Line items and totals
75
+
76
+ `--line-item` and `--total` use repeatable `key:value` format.
77
+
78
+ **`--line-item` keys:** `name` (required), `quantity`, `unit_amount`, `description`, `sku`, `url`, `image_url`, `product_url`
79
+
80
+ ```bash
81
+ --line-item "name:Running Shoes,unit_amount:12000,quantity:1,description:Trail runners"
82
+ ```
83
+
84
+ **`--total` keys:** `type` (required), `display_text` (required), `amount` (required)
85
+
86
+ ```bash
87
+ --total "type:subtotal,display_text:Subtotal,amount:12000" \
88
+ --total "type:total,display_text:Total,amount:12000"
89
+ ```
90
+
74
91
  #### Credential types
75
92
 
76
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, you can instead include `--credential-type "shared_payment_token"`.
@@ -84,6 +101,14 @@ link-cli spend-request retrieve lsrq_001 --format json
84
101
  ```
85
102
  By default, retrieving a spend request will not include card details. Use the `--include=card` to see unmasked card details.
86
103
 
104
+ For agent polling, pass `--interval` and optionally `--max-attempts`:
105
+
106
+ ```bash
107
+ link-cli spend-request retrieve lsrq_001 --interval 2 --max-attempts 150 --format json
108
+ ```
109
+
110
+ 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.
111
+
87
112
  If the merchant supports MPP, use `link-cli mpp pay` instead:
88
113
 
89
114
  ```bash
package/dist/cli.js CHANGED
@@ -13590,8 +13590,13 @@ function parseKvString(raw) {
13590
13590
  }
13591
13591
  return result;
13592
13592
  }
13593
- function formatZodError(err, prefix) {
13593
+ function formatZodError(err, prefix, schema) {
13594
+ const allowed = Object.keys(schema.shape);
13594
13595
  const messages = err.issues.map((issue) => {
13596
+ if (issue.code === "unrecognized_keys") {
13597
+ const keys = issue.keys.map((k) => `"${k}"`).join(", ");
13598
+ return `${prefix}: unrecognized key ${keys}. Allowed keys: ${allowed.join(", ")}`;
13599
+ }
13595
13600
  const key = issue.path[0]?.toString();
13596
13601
  return key ? `${prefix} ${key}: ${issue.message}` : `${prefix}: ${issue.message}`;
13597
13602
  });
@@ -13602,7 +13607,8 @@ function parseLineItemFlag(raw) {
13602
13607
  try {
13603
13608
  return LineItemSchema.parse(obj);
13604
13609
  } catch (err) {
13605
- if (err instanceof z5.ZodError) throw formatZodError(err, "Line item");
13610
+ if (err instanceof z5.ZodError)
13611
+ throw formatZodError(err, "Line item", LineItemSchema);
13606
13612
  throw err;
13607
13613
  }
13608
13614
  }
@@ -13611,7 +13617,8 @@ function parseTotalFlag(raw) {
13611
13617
  try {
13612
13618
  return TotalSchema.parse(obj);
13613
13619
  } catch (err) {
13614
- if (err instanceof z5.ZodError) throw formatZodError(err, "Total");
13620
+ if (err instanceof z5.ZodError)
13621
+ throw formatZodError(err, "Total", TotalSchema);
13615
13622
  throw err;
13616
13623
  }
13617
13624
  }
@@ -14199,19 +14206,27 @@ var createOptions = z6.object({
14199
14206
  context: z6.string().min(100).describe(
14200
14207
  "Min 100 chars \u2014 describe the purchase and rationale; the user reads this when approving"
14201
14208
  ),
14202
- lineItem: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe("Line item (repeatable, key:value format)"),
14203
- total: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe("Total (repeatable, key:value format)"),
14209
+ lineItem: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe(
14210
+ '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
+ ),
14212
+ 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"'
14214
+ ),
14204
14215
  requestApproval: z6.boolean().default(true).describe("Request approval and poll until approved/denied/expired"),
14205
14216
  test: z6.boolean().default(false).describe(
14206
14217
  "Use test mode (creates testmode credentials from test card data)"
14207
14218
  )
14208
14219
  });
14209
14220
  var retrieveOptions = z6.object({
14210
- timeout: z6.coerce.number().default(300).describe("Polling timeout in seconds"),
14221
+ timeout: z6.coerce.number().default(300).describe(
14222
+ "Polling timeout in seconds. When reached during active polling, exits non-zero with POLLING_TIMEOUT."
14223
+ ),
14211
14224
  interval: z6.coerce.number().default(0).describe(
14212
- "Poll interval in seconds. When > 0, polls until status is terminal or timeout is reached, yielding status on each attempt."
14225
+ "Poll interval in seconds. When > 0, polls until status is terminal, timeout is reached, or max attempts are exhausted."
14226
+ ),
14227
+ maxAttempts: z6.coerce.number().default(0).describe(
14228
+ "Max poll attempts. 0 = unlimited. Exhaustion during active polling exits non-zero with POLLING_TIMEOUT."
14213
14229
  ),
14214
- maxAttempts: z6.coerce.number().default(0).describe("Max poll attempts. 0 = unlimited (use timeout instead)."),
14215
14230
  include: z6.array(z6.string()).default([]).describe("Include extra data (repeatable, e.g. --include card)")
14216
14231
  });
14217
14232
  var updateOptions = z6.object({
@@ -14221,8 +14236,12 @@ var updateOptions = z6.object({
14221
14236
  profileId: z6.string().optional().describe("Profile ID"),
14222
14237
  merchantId: z6.string().optional().describe("Merchant ID"),
14223
14238
  currency: z6.string().optional().describe("Currency code"),
14224
- lineItem: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe("Line item (repeatable, key:value format)"),
14225
- total: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe("Total (repeatable, key:value format)")
14239
+ lineItem: z6.array(z6.union([z6.string(), z6.record(z6.string(), z6.unknown())])).default([]).describe(
14240
+ '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
+ ),
14242
+ 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"'
14244
+ )
14226
14245
  });
14227
14246
 
14228
14247
  // src/commands/spend-request/update.tsx
@@ -14600,11 +14619,20 @@ function createSpendRequestCli(repository) {
14600
14619
  return;
14601
14620
  }
14602
14621
  attempts++;
14603
- const shouldStop = interval <= 0 || maxAttempts > 0 && attempts >= maxAttempts || Date.now() >= deadline;
14604
- if (shouldStop) {
14622
+ if (interval <= 0) {
14605
14623
  yield request;
14606
14624
  return;
14607
14625
  }
14626
+ const maxAttemptsExhausted = maxAttempts > 0 && attempts >= maxAttempts;
14627
+ const timeoutReached = Date.now() >= deadline;
14628
+ if (maxAttemptsExhausted || timeoutReached) {
14629
+ const reason = maxAttemptsExhausted ? `max attempts (${maxAttempts}) exhausted` : `timeout (${timeout}s) reached`;
14630
+ return c.error({
14631
+ code: "POLLING_TIMEOUT",
14632
+ message: `Polling stopped before spend request ${id} reached a terminal status: ${reason}; current status is ${request.status}.`,
14633
+ retryable: true
14634
+ });
14635
+ }
14608
14636
  const snapshot = JSON.stringify(request);
14609
14637
  if (snapshot !== previousAttemptData) {
14610
14638
  previousAttemptData = snapshot;
@@ -14917,7 +14945,7 @@ var ResourceFactory = class {
14917
14945
  };
14918
14946
 
14919
14947
  // src/cli.tsx
14920
- var cliVersion = "0.4.0";
14948
+ var cliVersion = "0.4.2";
14921
14949
  var buildNumber = "1";
14922
14950
  var cliName = "@stripe/link-cli";
14923
14951
  var defaultHeaders = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stripe/link-cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "link-cli": "./dist/cli.js"
@@ -25,6 +25,7 @@
25
25
  "ink": "^5.2.1",
26
26
  "ink-spinner": "^5.0.0",
27
27
  "mppx": "^0.5.7",
28
+ "viem": "^2.47.5",
28
29
  "qrcode": "^1.5.4",
29
30
  "react": "^18.3.1",
30
31
  "update-notifier": "^7.3.1",