@better-webhook/cli 3.4.3 → 3.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.
Files changed (41) hide show
  1. package/README.md +35 -0
  2. package/dist/dashboard/assets/index-CxrRCNTh.css +1 -0
  3. package/dist/dashboard/assets/index-Dlqdzwyc.js +23 -0
  4. package/dist/dashboard/index.html +2 -2
  5. package/dist/index.cjs +341 -24
  6. package/dist/index.js +341 -24
  7. package/package.json +5 -4
  8. package/dist/commands/capture.d.ts +0 -2
  9. package/dist/commands/capture.js +0 -30
  10. package/dist/commands/captures.d.ts +0 -2
  11. package/dist/commands/captures.js +0 -217
  12. package/dist/commands/dashboard.d.ts +0 -2
  13. package/dist/commands/dashboard.js +0 -65
  14. package/dist/commands/index.d.ts +0 -6
  15. package/dist/commands/index.js +0 -6
  16. package/dist/commands/replay.d.ts +0 -2
  17. package/dist/commands/replay.js +0 -140
  18. package/dist/commands/run.d.ts +0 -2
  19. package/dist/commands/run.js +0 -181
  20. package/dist/commands/templates.d.ts +0 -2
  21. package/dist/commands/templates.js +0 -285
  22. package/dist/core/capture-server.d.ts +0 -31
  23. package/dist/core/capture-server.js +0 -298
  24. package/dist/core/dashboard-api.d.ts +0 -8
  25. package/dist/core/dashboard-api.js +0 -271
  26. package/dist/core/dashboard-server.d.ts +0 -20
  27. package/dist/core/dashboard-server.js +0 -124
  28. package/dist/core/executor.d.ts +0 -11
  29. package/dist/core/executor.js +0 -130
  30. package/dist/core/index.d.ts +0 -5
  31. package/dist/core/index.js +0 -5
  32. package/dist/core/replay-engine.d.ts +0 -18
  33. package/dist/core/replay-engine.js +0 -208
  34. package/dist/core/signature.d.ts +0 -24
  35. package/dist/core/signature.js +0 -199
  36. package/dist/core/template-manager.d.ts +0 -24
  37. package/dist/core/template-manager.js +0 -246
  38. package/dist/dashboard/assets/index-BSfTbn4Y.js +0 -23
  39. package/dist/dashboard/assets/index-zDTVdss_.css +0 -1
  40. package/dist/types/index.d.ts +0 -299
  41. package/dist/types/index.js +0 -86
@@ -1,217 +0,0 @@
1
- import { Command } from "commander";
2
- import chalk from "chalk";
3
- import prompts from "prompts";
4
- import { getReplayEngine } from "../core/replay-engine.js";
5
- const listCommand = new Command()
6
- .name("list")
7
- .alias("ls")
8
- .description("List captured webhooks")
9
- .option("-l, --limit <limit>", "Maximum number of captures to show", "20")
10
- .option("-p, --provider <provider>", "Filter by provider")
11
- .action((options) => {
12
- const limit = parseInt(options.limit, 10);
13
- if (isNaN(limit) || limit <= 0) {
14
- console.error(chalk.red("Invalid limit value"));
15
- process.exitCode = 1;
16
- return;
17
- }
18
- const engine = getReplayEngine();
19
- let captures = engine.listCaptures(limit);
20
- if (options.provider) {
21
- captures = captures.filter((c) => c.capture.provider?.toLowerCase() === options.provider?.toLowerCase());
22
- }
23
- if (captures.length === 0) {
24
- console.log(chalk.yellow("\nšŸ“­ No captured webhooks found."));
25
- console.log(chalk.gray(" Start capturing with: better-webhook capture\n"));
26
- return;
27
- }
28
- console.log(chalk.bold("\nšŸ“¦ Captured Webhooks\n"));
29
- for (const { file, capture } of captures) {
30
- const date = new Date(capture.timestamp).toLocaleString();
31
- const provider = capture.provider
32
- ? chalk.cyan(`[${capture.provider}]`)
33
- : chalk.gray("[unknown]");
34
- const size = capture.contentLength || capture.rawBody?.length || 0;
35
- console.log(` ${chalk.white(capture.id.slice(0, 8))} ${provider}`);
36
- console.log(chalk.gray(` ${capture.method} ${capture.path}`));
37
- console.log(chalk.gray(` ${date} | ${size} bytes`));
38
- console.log(chalk.gray(` File: ${file}`));
39
- console.log();
40
- }
41
- console.log(chalk.gray(` Showing ${captures.length} captures`));
42
- console.log(chalk.gray(` Storage: ${engine.getCapturesDir()}\n`));
43
- });
44
- const showCommand = new Command()
45
- .name("show")
46
- .argument("<captureId>", "Capture ID or partial ID")
47
- .description("Show detailed information about a capture")
48
- .option("-b, --body", "Show full body content")
49
- .action((captureId, options) => {
50
- const engine = getReplayEngine();
51
- const captureFile = engine.getCapture(captureId);
52
- if (!captureFile) {
53
- console.log(chalk.red(`\nāŒ Capture not found: ${captureId}\n`));
54
- process.exitCode = 1;
55
- return;
56
- }
57
- const { capture } = captureFile;
58
- console.log(chalk.bold("\nšŸ“‹ Capture Details\n"));
59
- console.log(` ${chalk.gray("ID:")} ${capture.id}`);
60
- console.log(` ${chalk.gray("File:")} ${captureFile.file}`);
61
- console.log(` ${chalk.gray("Timestamp:")} ${new Date(capture.timestamp).toLocaleString()}`);
62
- console.log(` ${chalk.gray("Method:")} ${capture.method}`);
63
- console.log(` ${chalk.gray("Path:")} ${capture.path}`);
64
- console.log(` ${chalk.gray("URL:")} ${capture.url}`);
65
- if (capture.provider) {
66
- console.log(` ${chalk.gray("Provider:")} ${chalk.cyan(capture.provider)}`);
67
- }
68
- console.log(` ${chalk.gray("Content-Type:")} ${capture.contentType || "unknown"}`);
69
- console.log(` ${chalk.gray("Content-Length:")} ${capture.contentLength || 0} bytes`);
70
- const queryKeys = Object.keys(capture.query);
71
- if (queryKeys.length > 0) {
72
- console.log(chalk.bold("\n Query Parameters:"));
73
- for (const [key, value] of Object.entries(capture.query)) {
74
- const queryValue = Array.isArray(value) ? value.join(", ") : value;
75
- console.log(chalk.gray(` ${key}: ${queryValue}`));
76
- }
77
- }
78
- console.log(chalk.bold("\n Headers:"));
79
- for (const [key, value] of Object.entries(capture.headers)) {
80
- const headerValue = Array.isArray(value) ? value.join(", ") : value;
81
- const display = headerValue.length > 80
82
- ? headerValue.slice(0, 80) + "..."
83
- : headerValue;
84
- console.log(chalk.gray(` ${key}: ${display}`));
85
- }
86
- if (options.body && capture.body) {
87
- console.log(chalk.bold("\n Body:"));
88
- if (typeof capture.body === "object") {
89
- console.log(chalk.gray(JSON.stringify(capture.body, null, 2)
90
- .split("\n")
91
- .map((l) => ` ${l}`)
92
- .join("\n")));
93
- }
94
- else {
95
- console.log(chalk.gray(` ${capture.body}`));
96
- }
97
- }
98
- else if (capture.body) {
99
- console.log(chalk.bold("\n Body:"));
100
- const preview = JSON.stringify(capture.body).slice(0, 200);
101
- console.log(chalk.gray(` ${preview}${preview.length >= 200 ? "..." : ""}`));
102
- console.log(chalk.gray(" Use --body to see full content"));
103
- }
104
- console.log();
105
- });
106
- const searchCommand = new Command()
107
- .name("search")
108
- .argument("<query>", "Search query")
109
- .description("Search captures by path, method, or provider")
110
- .action((query) => {
111
- const engine = getReplayEngine();
112
- const results = engine.searchCaptures(query);
113
- if (results.length === 0) {
114
- console.log(chalk.yellow(`\nšŸ“­ No captures found for: "${query}"\n`));
115
- return;
116
- }
117
- console.log(chalk.bold(`\nšŸ” Search Results for "${query}"\n`));
118
- for (const { file, capture } of results) {
119
- const date = new Date(capture.timestamp).toLocaleString();
120
- const provider = capture.provider
121
- ? chalk.cyan(`[${capture.provider}]`)
122
- : "";
123
- console.log(` ${chalk.white(capture.id.slice(0, 8))} ${provider}`);
124
- console.log(chalk.gray(` ${capture.method} ${capture.path}`));
125
- console.log(chalk.gray(` ${date}`));
126
- console.log();
127
- }
128
- console.log(chalk.gray(` Found: ${results.length} captures\n`));
129
- });
130
- const deleteCommand = new Command()
131
- .name("delete")
132
- .alias("rm")
133
- .argument("<captureId>", "Capture ID or partial ID to delete")
134
- .description("Delete a specific captured webhook")
135
- .option("-f, --force", "Skip confirmation prompt")
136
- .action(async (captureId, options) => {
137
- const engine = getReplayEngine();
138
- const captureFile = engine.getCapture(captureId);
139
- if (!captureFile) {
140
- console.log(chalk.red(`\nāŒ Capture not found: ${captureId}\n`));
141
- process.exitCode = 1;
142
- return;
143
- }
144
- const { capture } = captureFile;
145
- if (!options.force) {
146
- console.log(chalk.bold("\nšŸ—‘ļø Capture to delete:\n"));
147
- console.log(` ${chalk.white(capture.id.slice(0, 8))}`);
148
- console.log(chalk.gray(` ${capture.method} ${capture.path}`));
149
- console.log(chalk.gray(` ${new Date(capture.timestamp).toLocaleString()}`));
150
- console.log();
151
- const response = await prompts({
152
- type: "confirm",
153
- name: "confirm",
154
- message: "Delete this capture?",
155
- initial: false,
156
- });
157
- if (!response.confirm) {
158
- console.log(chalk.yellow("Cancelled"));
159
- return;
160
- }
161
- }
162
- const deleted = engine.deleteCapture(captureId);
163
- if (deleted) {
164
- console.log(chalk.green(`\nāœ“ Deleted capture: ${capture.id.slice(0, 8)}\n`));
165
- }
166
- else {
167
- console.log(chalk.red(`\nāŒ Failed to delete capture\n`));
168
- process.exitCode = 1;
169
- }
170
- });
171
- const cleanCommand = new Command()
172
- .name("clean")
173
- .alias("remove-all")
174
- .description("Remove all captured webhooks")
175
- .option("-f, --force", "Skip confirmation prompt")
176
- .action(async (options) => {
177
- const engine = getReplayEngine();
178
- const captures = engine.listCaptures(10000);
179
- if (captures.length === 0) {
180
- console.log(chalk.yellow("\nšŸ“­ No captures to remove.\n"));
181
- return;
182
- }
183
- console.log(chalk.bold(`\nšŸ—‘ļø Found ${captures.length} captured webhook(s)\n`));
184
- const byProvider = new Map();
185
- for (const c of captures) {
186
- const provider = c.capture.provider || "unknown";
187
- byProvider.set(provider, (byProvider.get(provider) || 0) + 1);
188
- }
189
- for (const [provider, count] of byProvider) {
190
- console.log(chalk.gray(` ${provider}: ${count}`));
191
- }
192
- console.log();
193
- if (!options.force) {
194
- const response = await prompts({
195
- type: "confirm",
196
- name: "confirm",
197
- message: `Delete all ${captures.length} capture(s)?`,
198
- initial: false,
199
- });
200
- if (!response.confirm) {
201
- console.log(chalk.yellow("Cancelled"));
202
- return;
203
- }
204
- }
205
- const deleted = engine.deleteAllCaptures();
206
- console.log(chalk.green(`\nāœ“ Removed ${deleted} capture(s)`));
207
- console.log(chalk.gray(` Storage: ${engine.getCapturesDir()}\n`));
208
- });
209
- export const captures = new Command()
210
- .name("captures")
211
- .alias("c")
212
- .description("Manage captured webhooks")
213
- .addCommand(listCommand)
214
- .addCommand(showCommand)
215
- .addCommand(searchCommand)
216
- .addCommand(deleteCommand)
217
- .addCommand(cleanCommand);
@@ -1,2 +0,0 @@
1
- import { Command } from "commander";
2
- export declare const dashboard: Command;
@@ -1,65 +0,0 @@
1
- import { Command } from "commander";
2
- import chalk from "chalk";
3
- import { startDashboardServer } from "../core/dashboard-server.js";
4
- export const dashboard = new Command()
5
- .name("dashboard")
6
- .description("Start the local dashboard (UI + API + WebSocket) server")
7
- .option("-p, --port <port>", "Port to listen on", "4000")
8
- .option("-h, --host <host>", "Host to bind to", "localhost")
9
- .option("--capture-port <port>", "Capture server port", "3001")
10
- .option("--capture-host <host>", "Capture server host", "0.0.0.0")
11
- .option("--no-capture", "Do not start the capture server")
12
- .option("--captures-dir <dir>", "Override captures directory")
13
- .option("--templates-dir <dir>", "Override templates base directory")
14
- .action(async (options) => {
15
- const port = Number.parseInt(String(options.port), 10);
16
- if (!Number.isFinite(port) || port < 0 || port > 65535) {
17
- console.error(chalk.red("Invalid port number"));
18
- process.exitCode = 1;
19
- return;
20
- }
21
- try {
22
- const capturePort = Number.parseInt(String(options.capturePort), 10);
23
- if (!Number.isFinite(capturePort) ||
24
- capturePort < 0 ||
25
- capturePort > 65535) {
26
- console.error(chalk.red("Invalid capture port number"));
27
- process.exitCode = 1;
28
- return;
29
- }
30
- const { url, server, capture } = await startDashboardServer({
31
- host: options.host,
32
- port,
33
- captureHost: options.captureHost,
34
- capturePort,
35
- startCapture: options.capture !== false,
36
- capturesDir: options.capturesDir,
37
- templatesBaseDir: options.templatesDir,
38
- });
39
- console.log(chalk.bold("\n🧭 Dashboard Server\n"));
40
- console.log(chalk.gray(` Dashboard: ${url}/`));
41
- console.log(chalk.gray(` Health: ${url}/health`));
42
- console.log(chalk.gray(` API Base: ${url}/api`));
43
- console.log(chalk.gray(` WebSocket: ${url.replace("http://", "ws://")}/ws`));
44
- if (capture) {
45
- console.log();
46
- console.log(chalk.bold("šŸŽ£ Capture Server"));
47
- console.log(chalk.gray(` Capture: ${capture.url}`));
48
- console.log(chalk.gray(` Tip: Send webhooks to any path, e.g. ${capture.url}/webhooks/github`));
49
- }
50
- console.log();
51
- const shutdown = async () => {
52
- if (capture) {
53
- await capture.server.stop();
54
- }
55
- await new Promise((resolve) => server.close(() => resolve()));
56
- process.exit(0);
57
- };
58
- process.on("SIGINT", shutdown);
59
- process.on("SIGTERM", shutdown);
60
- }
61
- catch (error) {
62
- console.error(chalk.red(`Failed to start dashboard server: ${error?.message || error}`));
63
- process.exitCode = 1;
64
- }
65
- });
@@ -1,6 +0,0 @@
1
- export { templates } from "./templates.js";
2
- export { run } from "./run.js";
3
- export { capture } from "./capture.js";
4
- export { captures } from "./captures.js";
5
- export { replay } from "./replay.js";
6
- export { dashboard } from "./dashboard.js";
@@ -1,6 +0,0 @@
1
- export { templates } from "./templates.js";
2
- export { run } from "./run.js";
3
- export { capture } from "./capture.js";
4
- export { captures } from "./captures.js";
5
- export { replay } from "./replay.js";
6
- export { dashboard } from "./dashboard.js";
@@ -1,2 +0,0 @@
1
- import { Command } from "commander";
2
- export declare const replay: Command;
@@ -1,140 +0,0 @@
1
- import { Command } from "commander";
2
- import ora from "ora";
3
- import prompts from "prompts";
4
- import chalk from "chalk";
5
- import { getReplayEngine } from "../core/replay-engine.js";
6
- export const replay = new Command()
7
- .name("replay")
8
- .argument("[captureId]", "Capture ID to replay")
9
- .argument("[targetUrl]", "Target URL to replay to")
10
- .description("Replay a captured webhook to a target URL")
11
- .option("-m, --method <method>", "Override HTTP method")
12
- .option("-H, --header <header>", "Add or override header (format: key:value)", (value, previous) => {
13
- const [key, ...valueParts] = value.split(":");
14
- const headerValue = valueParts.join(":");
15
- if (!key || !headerValue) {
16
- throw new Error("Header format should be key:value");
17
- }
18
- return (previous || []).concat([
19
- { key: key.trim(), value: headerValue.trim() },
20
- ]);
21
- }, [])
22
- .option("-v, --verbose", "Show detailed request/response information")
23
- .action(async (captureId, targetUrl, options) => {
24
- const engine = getReplayEngine();
25
- if (!captureId) {
26
- const captures = engine.listCaptures(50);
27
- if (captures.length === 0) {
28
- console.log(chalk.yellow("\nšŸ“­ No captured webhooks found."));
29
- console.log(chalk.gray(" Start capturing with: better-webhook capture\n"));
30
- return;
31
- }
32
- const choices = captures.map((c) => {
33
- const date = new Date(c.capture.timestamp).toLocaleString();
34
- const provider = c.capture.provider ? `[${c.capture.provider}]` : "";
35
- return {
36
- title: `${c.capture.id.slice(0, 8)} ${provider} ${c.capture.method} ${c.capture.path}`,
37
- description: date,
38
- value: c.capture.id,
39
- };
40
- });
41
- const response = await prompts({
42
- type: "select",
43
- name: "captureId",
44
- message: "Select a capture to replay:",
45
- choices,
46
- });
47
- if (!response.captureId) {
48
- console.log(chalk.yellow("Cancelled"));
49
- return;
50
- }
51
- captureId = response.captureId;
52
- }
53
- const captureFile = engine.getCapture(captureId);
54
- if (!captureFile) {
55
- console.log(chalk.red(`\nāŒ Capture not found: ${captureId}\n`));
56
- process.exitCode = 1;
57
- return;
58
- }
59
- if (!targetUrl) {
60
- const response = await prompts({
61
- type: "text",
62
- name: "url",
63
- message: "Enter target URL:",
64
- initial: `http://localhost:3000${captureFile.capture.path}`,
65
- validate: (value) => {
66
- try {
67
- new URL(value);
68
- return true;
69
- }
70
- catch {
71
- return "Please enter a valid URL";
72
- }
73
- },
74
- });
75
- if (!response.url) {
76
- console.log(chalk.yellow("Cancelled"));
77
- return;
78
- }
79
- targetUrl = response.url;
80
- }
81
- const { capture } = captureFile;
82
- console.log(chalk.bold("\nšŸ”„ Replaying Webhook\n"));
83
- console.log(chalk.gray(` Capture ID: ${capture.id.slice(0, 8)}`));
84
- console.log(chalk.gray(` Original: ${capture.method} ${capture.path}`));
85
- if (capture.provider) {
86
- console.log(chalk.gray(` Provider: ${capture.provider}`));
87
- }
88
- console.log(chalk.gray(` Target: ${targetUrl}`));
89
- console.log();
90
- const spinner = ora("Replaying webhook...").start();
91
- try {
92
- const result = await engine.replay(captureId, {
93
- targetUrl: targetUrl,
94
- method: options?.method,
95
- headers: options?.header,
96
- });
97
- spinner.stop();
98
- const statusColor = result.status >= 200 && result.status < 300
99
- ? chalk.green
100
- : result.status >= 400
101
- ? chalk.red
102
- : chalk.yellow;
103
- console.log(chalk.bold("šŸ“„ Response\n"));
104
- console.log(` Status: ${statusColor(`${result.status} ${result.statusText}`)}`);
105
- console.log(` Duration: ${chalk.cyan(`${result.duration}ms`)}`);
106
- if (options?.verbose) {
107
- console.log(chalk.bold("\n Headers:"));
108
- for (const [key, value] of Object.entries(result.headers)) {
109
- const headerValue = Array.isArray(value) ? value.join(", ") : value;
110
- console.log(chalk.gray(` ${key}: ${headerValue}`));
111
- }
112
- }
113
- if (result.json !== undefined) {
114
- console.log(chalk.bold("\n Body:"));
115
- console.log(chalk.gray(JSON.stringify(result.json, null, 2)
116
- .split("\n")
117
- .map((l) => ` ${l}`)
118
- .join("\n")));
119
- }
120
- else if (result.bodyText) {
121
- console.log(chalk.bold("\n Body:"));
122
- const preview = result.bodyText.length > 500
123
- ? result.bodyText.slice(0, 500) + "..."
124
- : result.bodyText;
125
- console.log(chalk.gray(` ${preview}`));
126
- }
127
- console.log();
128
- if (result.status >= 200 && result.status < 300) {
129
- console.log(chalk.green("āœ“ Replay completed successfully\n"));
130
- }
131
- else {
132
- console.log(chalk.yellow(`⚠ Replay completed with status ${result.status}\n`));
133
- }
134
- }
135
- catch (error) {
136
- spinner.fail("Replay failed");
137
- console.error(chalk.red(`\nāŒ ${error.message}\n`));
138
- process.exitCode = 1;
139
- }
140
- });
@@ -1,2 +0,0 @@
1
- import { Command } from "commander";
2
- export declare const run: Command;
@@ -1,181 +0,0 @@
1
- import { Command } from "commander";
2
- import ora from "ora";
3
- import prompts from "prompts";
4
- import chalk from "chalk";
5
- import { getTemplateManager } from "../core/template-manager.js";
6
- import { executeTemplate } from "../core/executor.js";
7
- function getSecretEnvVarName(provider) {
8
- const envVarMap = {
9
- github: "GITHUB_WEBHOOK_SECRET",
10
- stripe: "STRIPE_WEBHOOK_SECRET",
11
- shopify: "SHOPIFY_WEBHOOK_SECRET",
12
- twilio: "TWILIO_WEBHOOK_SECRET",
13
- ragie: "RAGIE_WEBHOOK_SECRET",
14
- slack: "SLACK_WEBHOOK_SECRET",
15
- linear: "LINEAR_WEBHOOK_SECRET",
16
- clerk: "CLERK_WEBHOOK_SECRET",
17
- sendgrid: "SENDGRID_WEBHOOK_SECRET",
18
- discord: "DISCORD_WEBHOOK_SECRET",
19
- custom: "WEBHOOK_SECRET",
20
- };
21
- return envVarMap[provider] || "WEBHOOK_SECRET";
22
- }
23
- export const run = new Command()
24
- .name("run")
25
- .argument("[templateId]", "Template ID to run")
26
- .description("Run a webhook template against a target URL")
27
- .requiredOption("-u, --url <url>", "Target URL to send the webhook to")
28
- .option("-s, --secret <secret>", "Secret for signature generation")
29
- .option("-H, --header <header>", "Add custom header (format: key:value)", (value, previous) => {
30
- const [key, ...valueParts] = value.split(":");
31
- const headerValue = valueParts.join(":");
32
- if (!key || !headerValue) {
33
- throw new Error("Header format should be key:value");
34
- }
35
- return (previous || []).concat([
36
- { key: key.trim(), value: headerValue.trim() },
37
- ]);
38
- }, [])
39
- .option("-v, --verbose", "Show detailed request/response information")
40
- .action(async (templateId, options) => {
41
- const manager = getTemplateManager();
42
- if (!templateId) {
43
- const spinner = ora("Loading templates...").start();
44
- try {
45
- const local = manager.listLocalTemplates();
46
- const remote = await manager.listRemoteTemplates();
47
- spinner.stop();
48
- if (local.length === 0 && remote.length === 0) {
49
- console.log(chalk.yellow("\nšŸ“­ No templates available."));
50
- console.log(chalk.gray(" Download templates with: better-webhook templates download\n"));
51
- return;
52
- }
53
- const choices = [];
54
- if (local.length > 0) {
55
- for (const t of local) {
56
- choices.push({
57
- title: `${t.id} ${chalk.green("(local)")}`,
58
- description: `${t.metadata.provider} - ${t.metadata.event}`,
59
- value: t.id,
60
- });
61
- }
62
- }
63
- const remoteOnly = remote.filter((t) => !t.isDownloaded);
64
- for (const t of remoteOnly) {
65
- choices.push({
66
- title: `${t.metadata.id} ${chalk.gray("(remote)")}`,
67
- description: `${t.metadata.provider} - ${t.metadata.event}`,
68
- value: `remote:${t.metadata.id}`,
69
- });
70
- }
71
- const response = await prompts({
72
- type: "select",
73
- name: "templateId",
74
- message: "Select a template to run:",
75
- choices,
76
- });
77
- if (!response.templateId) {
78
- console.log(chalk.yellow("Cancelled"));
79
- return;
80
- }
81
- templateId = response.templateId;
82
- }
83
- catch (error) {
84
- spinner.fail("Failed to load templates");
85
- console.error(chalk.red(error.message));
86
- process.exitCode = 1;
87
- return;
88
- }
89
- }
90
- if (templateId?.startsWith("remote:")) {
91
- const remoteId = templateId.replace("remote:", "");
92
- const downloadSpinner = ora(`Downloading ${remoteId}...`).start();
93
- try {
94
- await manager.downloadTemplate(remoteId);
95
- downloadSpinner.succeed(`Downloaded ${remoteId}`);
96
- templateId = remoteId;
97
- }
98
- catch (error) {
99
- downloadSpinner.fail(`Failed to download: ${error.message}`);
100
- process.exitCode = 1;
101
- return;
102
- }
103
- }
104
- const localTemplate = manager.getLocalTemplate(templateId);
105
- if (!localTemplate) {
106
- console.log(chalk.red(`\nāŒ Template not found: ${templateId}`));
107
- console.log(chalk.gray(" Download it with: better-webhook templates download " +
108
- templateId +
109
- "\n"));
110
- process.exitCode = 1;
111
- return;
112
- }
113
- const targetUrl = options.url;
114
- let secret = options?.secret;
115
- if (!secret && localTemplate.metadata.provider) {
116
- const envVarName = getSecretEnvVarName(localTemplate.metadata.provider);
117
- secret = process.env[envVarName];
118
- }
119
- console.log(chalk.bold("\nšŸš€ Executing Webhook\n"));
120
- console.log(chalk.gray(` Template: ${templateId}`));
121
- console.log(chalk.gray(` Provider: ${localTemplate.metadata.provider}`));
122
- console.log(chalk.gray(` Event: ${localTemplate.metadata.event}`));
123
- console.log(chalk.gray(` Target: ${targetUrl}`));
124
- if (secret) {
125
- console.log(chalk.gray(` Signature: Will be generated`));
126
- }
127
- else {
128
- console.log(chalk.yellow(` āš ļø No secret provided - signature will not be generated`));
129
- }
130
- console.log();
131
- const spinner = ora("Sending webhook...").start();
132
- try {
133
- const result = await executeTemplate(localTemplate.template, {
134
- url: targetUrl,
135
- secret,
136
- headers: options?.header,
137
- });
138
- spinner.stop();
139
- const statusColor = result.status >= 200 && result.status < 300
140
- ? chalk.green
141
- : result.status >= 400
142
- ? chalk.red
143
- : chalk.yellow;
144
- console.log(chalk.bold("šŸ“„ Response\n"));
145
- console.log(` Status: ${statusColor(`${result.status} ${result.statusText}`)}`);
146
- console.log(` Duration: ${chalk.cyan(`${result.duration}ms`)}`);
147
- if (options?.verbose) {
148
- console.log(chalk.bold("\n Headers:"));
149
- for (const [key, value] of Object.entries(result.headers)) {
150
- const headerValue = Array.isArray(value) ? value.join(", ") : value;
151
- console.log(chalk.gray(` ${key}: ${headerValue}`));
152
- }
153
- }
154
- if (result.json !== undefined) {
155
- console.log(chalk.bold("\n Body:"));
156
- console.log(chalk.gray(JSON.stringify(result.json, null, 2)
157
- .split("\n")
158
- .map((l) => ` ${l}`)
159
- .join("\n")));
160
- }
161
- else if (result.bodyText) {
162
- console.log(chalk.bold("\n Body:"));
163
- const preview = result.bodyText.length > 500
164
- ? result.bodyText.slice(0, 500) + "..."
165
- : result.bodyText;
166
- console.log(chalk.gray(` ${preview}`));
167
- }
168
- console.log();
169
- if (result.status >= 200 && result.status < 300) {
170
- console.log(chalk.green("āœ“ Webhook delivered successfully\n"));
171
- }
172
- else {
173
- console.log(chalk.yellow(`⚠ Webhook delivered with status ${result.status}\n`));
174
- }
175
- }
176
- catch (error) {
177
- spinner.fail("Request failed");
178
- console.error(chalk.red(`\nāŒ ${error.message}\n`));
179
- process.exitCode = 1;
180
- }
181
- });
@@ -1,2 +0,0 @@
1
- import { Command } from "commander";
2
- export declare const templates: Command;