@garuhq/cli 0.3.0 → 0.4.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.4.0] — 2026-05-19
7
+
8
+ ### Added
9
+
10
+ - `garu webhooks events` command tree — inspect and replay webhook
11
+ deliveries from the CLI. The dashboard "Reenviar" button is now
12
+ available from the API key auth path as well, which means support
13
+ + on-call workflows can resend events from a terminal instead of
14
+ having to log into the dashboard.
15
+ - `garu webhooks events list [--status <s>] [--event-type <t>] [--endpoint-id <n>] [--page <n>] [--limit <n>]`
16
+ — paginated listing with status badges (green `success`, yellow
17
+ `pending`, red `failed`) in TTY mode.
18
+ - `garu webhooks events get <id>` — fetch one webhook event with
19
+ the full endpoint snapshot, response status, and (truncated)
20
+ response body.
21
+ - `garu webhooks events retry <id>` — re-deliver a webhook event
22
+ (resets to `pending` and triggers an immediate attempt). Works on
23
+ any status; use this when a customer reports a missed event.
24
+
25
+ ### Changed
26
+
27
+ - `@garuhq/node` SDK bumped to 0.11.0 for the new `webhookEvents`
28
+ resource and its response-shape normalization fix.
29
+
6
30
  ## [0.3.0] — 2026-04-28
7
31
 
8
32
  Rolls up the unpublished 0.2.0 work plus a new update notifier into a single
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.3.0";
18
+ var CLI_VERSION = "0.4.0";
19
19
  var CliError = class extends Error {
20
20
  code;
21
21
  exitCode;
@@ -459,6 +459,98 @@ async function logoutCommand(opts = {}) {
459
459
  printSuccess(`Profile '${opts.profile}' removed.`, opts);
460
460
  return { cleared: opts.profile };
461
461
  }
462
+ async function getClient2(opts) {
463
+ if (opts.garu) return opts.garu;
464
+ const auth = await resolveAuth({
465
+ ...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {},
466
+ ...opts.profile !== void 0 ? { profile: opts.profile } : {}
467
+ });
468
+ return createGaruClient({
469
+ auth,
470
+ ...opts.baseUrl !== void 0 ? { baseUrl: opts.baseUrl } : {}
471
+ });
472
+ }
473
+ async function webhooksEventsListCommand(opts) {
474
+ const garu = await getClient2(opts);
475
+ const params = {};
476
+ if (opts.page !== void 0) params.page = opts.page;
477
+ if (opts.limit !== void 0) params.limit = opts.limit;
478
+ if (opts.status !== void 0) params.status = opts.status;
479
+ if (opts.eventType !== void 0) params.eventType = opts.eventType;
480
+ if (opts.endpointId !== void 0) params.endpointId = opts.endpointId;
481
+ const result = await garu.webhookEvents.list(params);
482
+ printResult(result, { ...opts, prettyPrint: prettyWebhookEventList });
483
+ return result;
484
+ }
485
+ async function webhooksEventsGetCommand(opts) {
486
+ const garu = await getClient2(opts);
487
+ const event = await garu.webhookEvents.get(opts.id);
488
+ printResult(event, { ...opts, prettyPrint: prettyWebhookEvent });
489
+ return event;
490
+ }
491
+ async function webhooksEventsRetryCommand(opts) {
492
+ const garu = await getClient2(opts);
493
+ const event = await garu.webhookEvents.retry(opts.id);
494
+ printResult(event, { ...opts, prettyPrint: prettyWebhookEvent });
495
+ return event;
496
+ }
497
+ function statusBadge(status) {
498
+ const padded = status.padEnd(7);
499
+ if (status === "success") return pc__default.default.green(padded);
500
+ if (status === "failed") return pc__default.default.red(padded);
501
+ return pc__default.default.yellow(padded);
502
+ }
503
+ function prettyWebhookEventList(list) {
504
+ if (list.data.length === 0) {
505
+ return `No webhook events found (page ${list.meta.page}/${list.meta.totalPages || 1})`;
506
+ }
507
+ const header = `Webhook events (page ${list.meta.page}/${list.meta.totalPages || "?"}, ${list.meta.total} total)`;
508
+ const rows = list.data.map(
509
+ (e) => ` ${String(e.id).padStart(8)} ${statusBadge(e.status)} attempts=${String(e.attempts).padStart(2)} ${e.eventType.padEnd(38)} ${e.createdAt}`
510
+ );
511
+ return [header, ...rows].join("\n");
512
+ }
513
+ function prettyWebhookEvent(event) {
514
+ const lines = [
515
+ `Webhook event ${event.id}`,
516
+ ` status: ${statusBadge(event.status)}`,
517
+ ` eventType: ${event.eventType}`,
518
+ ` attempts: ${event.attempts}`,
519
+ ` endpoint: [${event.webhookEndpoint.id}] ${event.webhookEndpoint.url}`,
520
+ ` createdAt: ${event.createdAt}`
521
+ ];
522
+ if (event.lastAttemptAt) lines.push(` lastAttemptAt: ${event.lastAttemptAt}`);
523
+ if (event.nextRetryAt) lines.push(` nextRetryAt: ${event.nextRetryAt}`);
524
+ if (event.responseStatus !== null) lines.push(` responseStatus: ${event.responseStatus}`);
525
+ if (event.responseBody) {
526
+ const body = event.responseBody.length > 200 ? `${event.responseBody.slice(0, 200)}\u2026` : event.responseBody;
527
+ lines.push(` responseBody: ${body}`);
528
+ }
529
+ return lines.join("\n");
530
+ }
531
+
532
+ // src/lib/parse.ts
533
+ function parsePositiveIntId(raw, label) {
534
+ const id = Number.parseInt(raw, 10);
535
+ if (!Number.isFinite(id) || id <= 0) {
536
+ throw new CliError("invalid_input", `${label} must be a positive integer (got '${raw}')`);
537
+ }
538
+ return id;
539
+ }
540
+ function parsePaymentMethod(raw) {
541
+ if (raw === "pix" || raw === "credit_card" || raw === "boleto") return raw;
542
+ throw new CliError(
543
+ "invalid_input",
544
+ `--type must be 'pix', 'credit_card', or 'boleto' (got '${raw}')`
545
+ );
546
+ }
547
+ function parseWebhookEventStatus(raw) {
548
+ if (raw === "pending" || raw === "success" || raw === "failed") return raw;
549
+ throw new CliError(
550
+ "invalid_input",
551
+ `--status must be 'pending', 'success', or 'failed' (got '${raw}')`
552
+ );
553
+ }
462
554
 
463
555
  // src/index.ts
464
556
  function buildCli() {
@@ -524,7 +616,7 @@ function buildCli() {
524
616
  });
525
617
  charges.command("get <id>").description("Fetch a single charge by ID").action(async (id) => {
526
618
  const base = toCommandOptions(program);
527
- await chargesGetCommand({ ...base, id: parseId(id) }).catch(
619
+ await chargesGetCommand({ ...base, id: parsePositiveIntId(id, "Charge ID") }).catch(
528
620
  (err) => printErrorAndExit(err, base)
529
621
  );
530
622
  });
@@ -532,12 +624,45 @@ function buildCli() {
532
624
  const base = toCommandOptions(program);
533
625
  await chargesRefundCommand({
534
626
  ...base,
535
- id: parseId(id),
627
+ id: parsePositiveIntId(id, "Charge ID"),
536
628
  amount: cmdOpts.amount,
537
629
  reason: cmdOpts.reason,
538
630
  idempotencyKey: cmdOpts.idempotencyKey
539
631
  }).catch((err) => printErrorAndExit(err, base));
540
632
  });
633
+ const webhooks = program.command("webhooks").description("Inspect and replay webhook deliveries");
634
+ const events = webhooks.command("events").description("List, inspect, and resend webhook events");
635
+ events.command("list").description("List webhook events with 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("--status <status>", "filter: pending, success, or failed").option("--event-type <type>", "filter by Garu event type, e.g. transaction.payment.paid").option(
636
+ "--endpoint-id <n>",
637
+ "filter by destination endpoint id",
638
+ (v) => parseInt(v, 10)
639
+ ).action(
640
+ async (cmdOpts) => {
641
+ const base = toCommandOptions(program);
642
+ await webhooksEventsListCommand({
643
+ ...base,
644
+ page: cmdOpts.page,
645
+ limit: cmdOpts.limit,
646
+ status: cmdOpts.status ? parseWebhookEventStatus(cmdOpts.status) : void 0,
647
+ eventType: cmdOpts.eventType,
648
+ endpointId: cmdOpts.endpointId
649
+ }).catch((err) => printErrorAndExit(err, base));
650
+ }
651
+ );
652
+ events.command("get <id>").description("Fetch a single webhook event by ID").action(async (id) => {
653
+ const base = toCommandOptions(program);
654
+ await webhooksEventsGetCommand({
655
+ ...base,
656
+ id: parsePositiveIntId(id, "Webhook event ID")
657
+ }).catch((err) => printErrorAndExit(err, base));
658
+ });
659
+ events.command("retry <id>").description("Re-deliver a webhook event (resets to pending and triggers an immediate attempt)").action(async (id) => {
660
+ const base = toCommandOptions(program);
661
+ await webhooksEventsRetryCommand({
662
+ ...base,
663
+ id: parsePositiveIntId(id, "Webhook event ID")
664
+ }).catch((err) => printErrorAndExit(err, base));
665
+ });
541
666
  program.command("doctor").description("Environment diagnostic").action(async () => {
542
667
  const base = toCommandOptions(program);
543
668
  await doctorCommand(base).catch((err) => printErrorAndExit(err, base));
@@ -553,20 +678,6 @@ function toCommandOptions(program) {
553
678
  if (g.quiet) out.quiet = true;
554
679
  return out;
555
680
  }
556
- function parsePaymentMethod(raw) {
557
- if (raw === "pix" || raw === "credit_card" || raw === "boleto") return raw;
558
- throw new CliError(
559
- "invalid_input",
560
- `--type must be 'pix', 'credit_card', or 'boleto' (got '${raw}')`
561
- );
562
- }
563
- function parseId(raw) {
564
- const id = Number.parseInt(raw, 10);
565
- if (!Number.isFinite(id) || id <= 0) {
566
- throw new CliError("invalid_input", `Charge ID must be a positive integer (got '${raw}')`);
567
- }
568
- return id;
569
- }
570
681
  async function setupUpdateNotifier() {
571
682
  try {
572
683
  const { default: updateNotifier } = await import('update-notifier');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@garuhq/cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Official command-line interface for the Garu payment gateway.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://garu.com.br",
@@ -45,7 +45,7 @@
45
45
  "prepublishOnly": "npm run typecheck && npm test && npm run build"
46
46
  },
47
47
  "dependencies": {
48
- "@garuhq/node": "0.2.0",
48
+ "@garuhq/node": "0.11.0",
49
49
  "@inquirer/prompts": "8.4.1",
50
50
  "commander": "12.0.0",
51
51
  "picocolors": "1.0.0",