@garuhq/cli 0.11.0 → 0.13.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/CHANGELOG.md CHANGED
@@ -3,6 +3,30 @@
3
3
  All notable changes to `@garuhq/cli` are documented in this file. Format:
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [SemVer](https://semver.org/).
5
5
 
6
+ ## [0.13.0] — 2026-08-22
7
+
8
+ ### Added
9
+
10
+ - `--idempotency-key <key>` on `garu customers create`, `garu charges refund`,
11
+ and `garu installment-plans request-refund` (auto-generated if omitted).
12
+ The gateway now dedupes all three against the header (bumped
13
+ `@garuhq/node` to 4.1.0), so a retried command can no longer register a
14
+ duplicate customer or open a second refund request.
15
+
16
+ ## [0.12.0] — 2026-08-22
17
+
18
+ ### Changed
19
+
20
+ - **`garu scheduled-charges` now targets `/api/v1/scheduled-charges`.**
21
+ `list`/`attempts` pretty-print output reports `count`/`totalCount`/
22
+ `totalPages` instead of a `page`/`total` counter. No command flags or
23
+ arguments changed — scheduled-charge ids were already opaque strings
24
+ (`sch_...`), so this is a wire-level move, not an identifier change.
25
+ - **`@garuhq/node` bumped to `4.0.0`.** Breaking for
26
+ `garu.scheduledCharges.list()`/`.listAttempts()` (see that package's
27
+ changelog); `garu-cli`'s own commands are unaffected beyond the
28
+ pretty-print header above.
29
+
6
30
  ## [0.11.0] — 2026-08-22
7
31
 
8
32
  ### Changed
package/dist/index.cjs CHANGED
@@ -15,7 +15,7 @@ var pc__default = /*#__PURE__*/_interopDefault(pc);
15
15
  // src/index.ts
16
16
 
17
17
  // src/version.ts
18
- var CLI_VERSION = "0.11.0";
18
+ var CLI_VERSION = "0.13.0";
19
19
  var CliError = class extends Error {
20
20
  code;
21
21
  exitCode;
@@ -490,6 +490,7 @@ async function chargesRefundCommand(opts) {
490
490
  const params = {};
491
491
  if (opts.amount !== void 0) params.amount = opts.amount;
492
492
  if (opts.reason !== void 0) params.reason = opts.reason;
493
+ if (opts.idempotencyKey !== void 0) params.idempotencyKey = opts.idempotencyKey;
493
494
  const charge = await garu.charges.refund(opts.id, params);
494
495
  printResult(charge, { ...opts, prettyPrint: prettyCharge });
495
496
  return charge;
@@ -559,6 +560,7 @@ async function customersCreateCommand(opts) {
559
560
  if (opts.neighborhood !== void 0) params.neighborhood = opts.neighborhood;
560
561
  if (opts.city !== void 0) params.city = opts.city;
561
562
  if (opts.state !== void 0) params.state = opts.state;
563
+ if (opts.idempotencyKey !== void 0) params.idempotencyKey = opts.idempotencyKey;
562
564
  const customer = await garu.customers.create(params);
563
565
  printResult(customer, { ...opts, prettyPrint: prettyCustomer });
564
566
  return customer;
@@ -809,6 +811,7 @@ async function installmentPlansRequestRefundCommand(opts) {
809
811
  const params = {};
810
812
  if (opts.amount !== void 0) params.amount = opts.amount;
811
813
  if (opts.reason !== void 0) params.reason = opts.reason;
814
+ if (opts.idempotencyKey !== void 0) params.idempotencyKey = opts.idempotencyKey;
812
815
  const request = await garu.installmentPlans.requestRefund(opts.uuid, params);
813
816
  printResult(request, { ...opts, prettyPrint: prettyRefundRequest });
814
817
  return request;
@@ -1226,10 +1229,8 @@ function prettyScheduledCharge(c) {
1226
1229
  return lines.join("\n");
1227
1230
  }
1228
1231
  function prettyScheduledChargeList(list) {
1229
- if (list.data.length === 0) {
1230
- return `No scheduled charges found (page ${list.meta.page}/${list.meta.totalPages || 1})`;
1231
- }
1232
- const header = `Scheduled charges (page ${list.meta.page}/${list.meta.totalPages || "?"}, ${list.meta.total} total)`;
1232
+ if (list.data.length === 0) return "No scheduled charges found";
1233
+ const header = `Scheduled charges (${list.count} of ${list.totalCount} total, ${list.totalPages} page(s))`;
1233
1234
  const rows = list.data.map(
1234
1235
  (c) => ` ${c.id.padEnd(20)} ${String(c.status).padEnd(18)} ${String(c.type).padEnd(9)} ${String(c.amount).padStart(10)} ${c.dueDate}`
1235
1236
  );
@@ -1252,10 +1253,8 @@ function prettyScheduledChargeDetail(detail) {
1252
1253
  return lines.join("\n");
1253
1254
  }
1254
1255
  function prettyAttemptList(list) {
1255
- if (list.data.length === 0) {
1256
- return `No billing attempts found (page ${list.meta.page}/${list.meta.totalPages || 1})`;
1257
- }
1258
- const header = `Billing attempts (page ${list.meta.page}/${list.meta.totalPages || "?"}, ${list.meta.total} total)`;
1256
+ if (list.data.length === 0) return "No billing attempts found";
1257
+ const header = `Billing attempts (${list.count} of ${list.totalCount} total, ${list.totalPages} page(s))`;
1259
1258
  const rows = list.data.map(
1260
1259
  (a) => ` cycle ${String(a.cycleNumber).padStart(3)} #${a.attemptNumber} ${a.status.padEnd(10)} ${a.paymentMethod.padEnd(7)} ${a.failureCode ?? ""} ${a.attemptedAt}`
1261
1260
  );
@@ -1439,13 +1438,14 @@ Recipes:
1439
1438
  "--amount <reais>",
1440
1439
  "partial refund amount in decimal BRL, e.g. 10.00",
1441
1440
  (v) => parseNonNegativeBrl(v, "--amount")
1442
- ).option("--reason <text>", "optional refund reason").action(async (id, cmdOpts) => {
1441
+ ).option("--reason <text>", "optional refund reason").option("--idempotency-key <key>", "idempotency key (auto-generated if omitted)").action(async (id, cmdOpts) => {
1443
1442
  const base = toCommandOptions(program);
1444
1443
  await chargesRefundCommand({
1445
1444
  ...base,
1446
1445
  id,
1447
1446
  amount: cmdOpts.amount,
1448
- reason: cmdOpts.reason
1447
+ reason: cmdOpts.reason,
1448
+ idempotencyKey: cmdOpts.idempotencyKey
1449
1449
  }).catch((err) => printErrorAndExit(err, base));
1450
1450
  });
1451
1451
  const scheduled = program.command("scheduled-charges").description("Schedule, inspect, and act on future-dated charges");
@@ -1660,7 +1660,7 @@ Recipes:
1660
1660
  );
1661
1661
  });
1662
1662
  const customers = program.command("customers").description("Manage your customer base");
1663
- customers.command("create").description("Register a customer for the current seller (dedup by document)").requiredOption("--name <name>", "customer name").requiredOption("--email <email>", "customer email").requiredOption("--document <document>", "CPF (11 digits) or CNPJ (14 digits), digits only").requiredOption("--phone <phone>", "phone with area code, 10-11 digits").requiredOption("--person-type <type>", "'fisica' or 'juridica'").option("--zip-code <cep>", "ZIP code, 8 digits").option("--street <street>", "street address").option("--number <number>", "address number").option("--complement <text>", "address complement").option("--neighborhood <text>", "neighborhood").option("--city <city>", "city").option("--state <uf>", "2-letter state code, e.g. SP").action(async (cmdOpts) => {
1663
+ customers.command("create").description("Register a customer for the current seller (dedup by document)").requiredOption("--name <name>", "customer name").requiredOption("--email <email>", "customer email").requiredOption("--document <document>", "CPF (11 digits) or CNPJ (14 digits), digits only").requiredOption("--phone <phone>", "phone with area code, 10-11 digits").requiredOption("--person-type <type>", "'fisica' or 'juridica'").option("--zip-code <cep>", "ZIP code, 8 digits").option("--street <street>", "street address").option("--number <number>", "address number").option("--complement <text>", "address complement").option("--neighborhood <text>", "neighborhood").option("--city <city>", "city").option("--state <uf>", "2-letter state code, e.g. SP").option("--idempotency-key <key>", "idempotency key (auto-generated if omitted)").action(async (cmdOpts) => {
1664
1664
  const base = toCommandOptions(program);
1665
1665
  await customersCreateCommand({
1666
1666
  ...base,
@@ -1675,7 +1675,8 @@ Recipes:
1675
1675
  complement: cmdOpts.complement,
1676
1676
  neighborhood: cmdOpts.neighborhood,
1677
1677
  city: cmdOpts.city,
1678
- state: cmdOpts.state
1678
+ state: cmdOpts.state,
1679
+ idempotencyKey: cmdOpts.idempotencyKey
1679
1680
  }).catch((err) => printErrorAndExit(err, base));
1680
1681
  });
1681
1682
  customers.command("list").description("List customers with pagination and search").option("--page <n>", "page number (1-based)", (v) => parseInt(v, 10)).option("--limit <n>", "items per page (1-100)", (v) => parseInt(v, 10)).option("--search <query>", "search by name, email, or document").option(
@@ -1828,15 +1829,18 @@ Recipes:
1828
1829
  "--amount <reais>",
1829
1830
  "defaults to everything the carn\xEA has collected",
1830
1831
  (v) => parseNonNegativeBrl(v, "--amount")
1831
- ).option("--reason <text>", "optional reason").action(async (uuid, cmdOpts) => {
1832
- const base = toCommandOptions(program);
1833
- await installmentPlansRequestRefundCommand({
1834
- ...base,
1835
- uuid,
1836
- amount: cmdOpts.amount,
1837
- reason: cmdOpts.reason
1838
- }).catch((err) => printErrorAndExit(err, base));
1839
- });
1832
+ ).option("--reason <text>", "optional reason").option("--idempotency-key <key>", "idempotency key (auto-generated if omitted)").action(
1833
+ async (uuid, cmdOpts) => {
1834
+ const base = toCommandOptions(program);
1835
+ await installmentPlansRequestRefundCommand({
1836
+ ...base,
1837
+ uuid,
1838
+ amount: cmdOpts.amount,
1839
+ reason: cmdOpts.reason,
1840
+ idempotencyKey: cmdOpts.idempotencyKey
1841
+ }).catch((err) => printErrorAndExit(err, base));
1842
+ }
1843
+ );
1840
1844
  const refundRequests = program.command("refund-requests").description("Inspect and resolve refund requests (carn\xEA and Pix/boleto)");
1841
1845
  refundRequests.command("list").description("List refund requests with pagination and filters").option("--page <n>", "page number (1-based)", (v) => parseInt(v, 10)).option("--limit <n>", "items per page (1-100)", (v) => parseInt(v, 10)).option("--plan-id <uuid>", "filter by carn\xEA uuid").option("--charge-id <uuid>", "filter by charge uuid (Pix/boleto)").option(
1842
1846
  "--status <status>",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@garuhq/cli",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "Official command-line interface for the Garu payment gateway.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://garu.com.br",
@@ -46,7 +46,7 @@
46
46
  "prepublishOnly": "npm run check:version-sync && npm run typecheck && npm test && npm run build"
47
47
  },
48
48
  "dependencies": {
49
- "@garuhq/node": "3.0.0",
49
+ "@garuhq/node": "4.1.0",
50
50
  "@inquirer/prompts": "8.4.1",
51
51
  "commander": "12.0.0",
52
52
  "picocolors": "1.0.0",