@garuhq/cli 0.4.1 → 0.5.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 +61 -0
- package/dist/index.cjs +39 -4
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,67 @@
|
|
|
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.5.0] — 2026-05-19
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- `garu webhooks events resend <id>` — audit-trail-preserving replay
|
|
11
|
+
of a webhook event. Unlike `retry`, this does **not** mutate the
|
|
12
|
+
original row: the gateway inserts a fresh event with its own
|
|
13
|
+
numeric id that points back at the source via `manualResendOf`,
|
|
14
|
+
then dispatches that clone. The original failure record (response
|
|
15
|
+
status, response body, attempts, timestamps) stays intact.
|
|
16
|
+
- In pretty mode the CLI prints `✓ Resent event <src> → new event
|
|
17
|
+
<clone>` to stderr so the new id is impossible to miss; the
|
|
18
|
+
cloned event itself is rendered on stdout with a new `resendOf:`
|
|
19
|
+
line.
|
|
20
|
+
- In JSON mode the cloned event is the full stdout payload —
|
|
21
|
+
`.id` is the new event, `.manualResendOf` is the source.
|
|
22
|
+
- Recipient handlers will see this as a distinct delivery: the
|
|
23
|
+
gateway POSTs the clone with `Idempotency-Key:
|
|
24
|
+
resend_<originalId>`.
|
|
25
|
+
|
|
26
|
+
### Deprecated
|
|
27
|
+
|
|
28
|
+
- `garu webhooks events retry <id>` — kept for backwards
|
|
29
|
+
compatibility but now marked `[deprecated: prefer resend]` in
|
|
30
|
+
`--help`. `retry` resets the original row in place, which means
|
|
31
|
+
once the replay succeeds the historical record of the prior
|
|
32
|
+
failure is gone. For incident response, support workflows, and
|
|
33
|
+
any backfill where the audit trail matters, use `resend` instead.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- `@garuhq/node` SDK bumped to 0.12.0 for the new
|
|
38
|
+
`webhookEvents.resend()` method and the `manualResendOf` field on
|
|
39
|
+
`WebhookEvent`.
|
|
40
|
+
|
|
41
|
+
## [0.4.2] — 2026-05-19
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- **Error codes**: HTTP 403 responses (valid key, not your resource —
|
|
46
|
+
e.g. retrying a webhook event that belongs to another seller) were
|
|
47
|
+
previously surfaced as `auth_error`, which read like "your key is
|
|
48
|
+
bad". They now surface as `permission_error`. `auth_error` is now
|
|
49
|
+
reserved for HTTP 401 (bad/missing key).
|
|
50
|
+
- **`garu --version`** correctly reports `0.4.2`. The `0.4.1` release
|
|
51
|
+
shipped with `src/version.ts:CLI_VERSION` still pinned to `'0.4.0'`
|
|
52
|
+
because that file was missed during the bump. A new `scripts/
|
|
53
|
+
check-version-sync.mjs` runs as part of `prepublishOnly` and fails
|
|
54
|
+
the build if `package.json:version` and `src/version.ts:CLI_VERSION`
|
|
55
|
+
drift apart.
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- **Error output**: `printErrorAndExit` now includes `status` and
|
|
60
|
+
`body` from `GaruAPIError`-derived failures.
|
|
61
|
+
- In `--json` mode, the payload gains optional `status` and `body`
|
|
62
|
+
fields: `{"error":{"code","message","status","body"}}`. Allows
|
|
63
|
+
self-diagnosis of 4xx responses without dropping to `curl`.
|
|
64
|
+
- In pretty mode, the HTTP status is shown as a dimmed line below
|
|
65
|
+
the existing `code:` line.
|
|
66
|
+
|
|
6
67
|
## [0.4.1] — 2026-05-19
|
|
7
68
|
|
|
8
69
|
### Fixed
|
package/dist/index.cjs
CHANGED
|
@@ -15,19 +15,28 @@ var pc__default = /*#__PURE__*/_interopDefault(pc);
|
|
|
15
15
|
// src/index.ts
|
|
16
16
|
|
|
17
17
|
// src/version.ts
|
|
18
|
-
var CLI_VERSION = "0.
|
|
18
|
+
var CLI_VERSION = "0.5.0";
|
|
19
19
|
var CliError = class extends Error {
|
|
20
20
|
code;
|
|
21
21
|
exitCode;
|
|
22
|
-
|
|
22
|
+
/** HTTP status code when the error came from a `GaruAPIError`, else `null`. */
|
|
23
|
+
status;
|
|
24
|
+
/** Raw backend response body when the error came from a `GaruAPIError`, else `undefined`. */
|
|
25
|
+
body;
|
|
26
|
+
constructor(code, message, exitCode = 1, status = null, body = void 0) {
|
|
23
27
|
super(message);
|
|
24
28
|
this.name = "CliError";
|
|
25
29
|
this.code = code;
|
|
26
30
|
this.exitCode = exitCode;
|
|
31
|
+
this.status = status;
|
|
32
|
+
this.body = body;
|
|
27
33
|
}
|
|
28
34
|
};
|
|
29
35
|
function toCliError(err) {
|
|
30
36
|
if (err instanceof CliError) return err;
|
|
37
|
+
if (err instanceof node.GaruAPIError) {
|
|
38
|
+
return new CliError(mapSdkCodeToCliCode(err.code), err.message, 1, err.status, err.body);
|
|
39
|
+
}
|
|
31
40
|
if (err instanceof node.GaruError) {
|
|
32
41
|
return new CliError(mapSdkCodeToCliCode(err.code), err.message, 1);
|
|
33
42
|
}
|
|
@@ -37,8 +46,9 @@ function toCliError(err) {
|
|
|
37
46
|
function mapSdkCodeToCliCode(sdkCode) {
|
|
38
47
|
switch (sdkCode) {
|
|
39
48
|
case "authentication_error":
|
|
40
|
-
case "permission_error":
|
|
41
49
|
return "auth_error";
|
|
50
|
+
case "permission_error":
|
|
51
|
+
return "permission_error";
|
|
42
52
|
case "not_found":
|
|
43
53
|
return "not_found";
|
|
44
54
|
case "validation_error":
|
|
@@ -154,6 +164,8 @@ function printSuccess(message, opts = {}) {
|
|
|
154
164
|
function printErrorAndExit(err, opts = {}) {
|
|
155
165
|
const cliErr = toCliError(err);
|
|
156
166
|
const payload = { error: { code: cliErr.code, message: cliErr.message } };
|
|
167
|
+
if (cliErr.status !== null) payload.error.status = cliErr.status;
|
|
168
|
+
if (cliErr.body !== void 0) payload.error.body = cliErr.body;
|
|
157
169
|
if (resolveMode(opts) === "json") {
|
|
158
170
|
process.stdout.write(`${JSON.stringify(payload)}
|
|
159
171
|
`);
|
|
@@ -162,6 +174,10 @@ function printErrorAndExit(err, opts = {}) {
|
|
|
162
174
|
`);
|
|
163
175
|
if (cliErr.code !== "unknown_error") {
|
|
164
176
|
process.stderr.write(`${pc__default.default.dim(` code: ${cliErr.code}`)}
|
|
177
|
+
`);
|
|
178
|
+
}
|
|
179
|
+
if (cliErr.status !== null) {
|
|
180
|
+
process.stderr.write(`${pc__default.default.dim(` status: ${cliErr.status}`)}
|
|
165
181
|
`);
|
|
166
182
|
}
|
|
167
183
|
}
|
|
@@ -494,6 +510,13 @@ async function webhooksEventsRetryCommand(opts) {
|
|
|
494
510
|
printResult(event, { ...opts, prettyPrint: prettyWebhookEvent });
|
|
495
511
|
return event;
|
|
496
512
|
}
|
|
513
|
+
async function webhooksEventsResendCommand(opts) {
|
|
514
|
+
const garu = await getClient2(opts);
|
|
515
|
+
const clone = await garu.webhookEvents.resend(opts.id);
|
|
516
|
+
printSuccess(`Resent event ${opts.id} \u2192 new event ${clone.id}`, opts);
|
|
517
|
+
printResult(clone, { ...opts, prettyPrint: prettyWebhookEvent });
|
|
518
|
+
return clone;
|
|
519
|
+
}
|
|
497
520
|
function statusBadge(status) {
|
|
498
521
|
const padded = status.padEnd(7);
|
|
499
522
|
if (status === "success") return pc__default.default.green(padded);
|
|
@@ -519,6 +542,7 @@ function prettyWebhookEvent(event) {
|
|
|
519
542
|
` endpoint: [${event.webhookEndpoint.id}] ${event.webhookEndpoint.url}`,
|
|
520
543
|
` createdAt: ${event.createdAt}`
|
|
521
544
|
];
|
|
545
|
+
if (event.manualResendOf !== null) lines.push(` resendOf: ${event.manualResendOf}`);
|
|
522
546
|
if (event.lastAttemptAt) lines.push(` lastAttemptAt: ${event.lastAttemptAt}`);
|
|
523
547
|
if (event.nextRetryAt) lines.push(` nextRetryAt: ${event.nextRetryAt}`);
|
|
524
548
|
if (event.responseStatus !== null) lines.push(` responseStatus: ${event.responseStatus}`);
|
|
@@ -656,13 +680,24 @@ function buildCli() {
|
|
|
656
680
|
id: parsePositiveIntId(id, "Webhook event ID")
|
|
657
681
|
}).catch((err) => printErrorAndExit(err, base));
|
|
658
682
|
});
|
|
659
|
-
events.command("retry <id>").description(
|
|
683
|
+
events.command("retry <id>").description(
|
|
684
|
+
"[deprecated: prefer `resend`] Re-deliver a webhook event in place (resets the original row to pending and triggers an immediate attempt; destroys the prior failure record)"
|
|
685
|
+
).action(async (id) => {
|
|
660
686
|
const base = toCommandOptions(program);
|
|
661
687
|
await webhooksEventsRetryCommand({
|
|
662
688
|
...base,
|
|
663
689
|
id: parsePositiveIntId(id, "Webhook event ID")
|
|
664
690
|
}).catch((err) => printErrorAndExit(err, base));
|
|
665
691
|
});
|
|
692
|
+
events.command("resend <id>").description(
|
|
693
|
+
"Re-deliver a webhook event by cloning it (audit-trail preserving: original row is untouched, clone gets a new id and points back via manualResendOf)"
|
|
694
|
+
).action(async (id) => {
|
|
695
|
+
const base = toCommandOptions(program);
|
|
696
|
+
await webhooksEventsResendCommand({
|
|
697
|
+
...base,
|
|
698
|
+
id: parsePositiveIntId(id, "Webhook event ID")
|
|
699
|
+
}).catch((err) => printErrorAndExit(err, base));
|
|
700
|
+
});
|
|
666
701
|
program.command("doctor").description("Environment diagnostic").action(async () => {
|
|
667
702
|
const base = toCommandOptions(program);
|
|
668
703
|
await doctorCommand(base).catch((err) => printErrorAndExit(err, base));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@garuhq/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Official command-line interface for the Garu payment gateway.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://garu.com.br",
|
|
@@ -42,10 +42,11 @@
|
|
|
42
42
|
"typecheck": "tsc --noEmit",
|
|
43
43
|
"test": "vitest run",
|
|
44
44
|
"test:watch": "vitest",
|
|
45
|
-
"
|
|
45
|
+
"check:version-sync": "node scripts/check-version-sync.mjs",
|
|
46
|
+
"prepublishOnly": "npm run check:version-sync && npm run typecheck && npm test && npm run build"
|
|
46
47
|
},
|
|
47
48
|
"dependencies": {
|
|
48
|
-
"@garuhq/node": "0.
|
|
49
|
+
"@garuhq/node": "0.12.0",
|
|
49
50
|
"@inquirer/prompts": "8.4.1",
|
|
50
51
|
"commander": "12.0.0",
|
|
51
52
|
"picocolors": "1.0.0",
|