@better-webhook/cli 3.9.0 → 3.10.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 (56) hide show
  1. package/dist/_binary_entry.js +29 -0
  2. package/dist/commands/capture.d.ts +2 -0
  3. package/dist/commands/capture.js +33 -0
  4. package/dist/commands/captures.d.ts +2 -0
  5. package/dist/commands/captures.js +316 -0
  6. package/dist/commands/dashboard.d.ts +2 -0
  7. package/dist/commands/dashboard.js +70 -0
  8. package/dist/commands/index.d.ts +6 -0
  9. package/dist/commands/index.js +6 -0
  10. package/dist/commands/replay.d.ts +2 -0
  11. package/dist/commands/replay.js +140 -0
  12. package/dist/commands/run.d.ts +2 -0
  13. package/dist/commands/run.js +182 -0
  14. package/dist/commands/templates.d.ts +2 -0
  15. package/dist/commands/templates.js +285 -0
  16. package/dist/core/capture-server.d.ts +37 -0
  17. package/dist/core/capture-server.js +400 -0
  18. package/dist/core/capture-server.test.d.ts +1 -0
  19. package/dist/core/capture-server.test.js +86 -0
  20. package/dist/core/cli-version.d.ts +1 -0
  21. package/dist/core/cli-version.js +30 -0
  22. package/dist/core/cli-version.test.d.ts +1 -0
  23. package/dist/core/cli-version.test.js +42 -0
  24. package/dist/core/dashboard-api.d.ts +8 -0
  25. package/dist/core/dashboard-api.js +333 -0
  26. package/dist/core/dashboard-server.d.ts +24 -0
  27. package/dist/core/dashboard-server.js +224 -0
  28. package/dist/core/debug-output.d.ts +3 -0
  29. package/dist/core/debug-output.js +69 -0
  30. package/dist/core/debug-verify.d.ts +25 -0
  31. package/dist/core/debug-verify.js +253 -0
  32. package/dist/core/executor.d.ts +11 -0
  33. package/dist/core/executor.js +152 -0
  34. package/dist/core/index.d.ts +5 -0
  35. package/dist/core/index.js +5 -0
  36. package/dist/core/replay-engine.d.ts +20 -0
  37. package/dist/core/replay-engine.js +293 -0
  38. package/dist/core/replay-engine.test.d.ts +1 -0
  39. package/dist/core/replay-engine.test.js +482 -0
  40. package/dist/core/runtime-paths.d.ts +2 -0
  41. package/dist/core/runtime-paths.js +65 -0
  42. package/dist/core/runtime-paths.test.d.ts +1 -0
  43. package/dist/core/runtime-paths.test.js +50 -0
  44. package/dist/core/signature.d.ts +25 -0
  45. package/dist/core/signature.js +224 -0
  46. package/dist/core/signature.test.d.ts +1 -0
  47. package/dist/core/signature.test.js +38 -0
  48. package/dist/core/template-manager.d.ts +33 -0
  49. package/dist/core/template-manager.js +313 -0
  50. package/dist/core/template-manager.test.d.ts +1 -0
  51. package/dist/core/template-manager.test.js +236 -0
  52. package/dist/index.cjs +135 -20
  53. package/dist/index.js +123 -8
  54. package/dist/types/index.d.ts +312 -0
  55. package/dist/types/index.js +87 -0
  56. package/package.json +1 -1
@@ -0,0 +1,182 @@
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
+ recall: "RECALL_WEBHOOK_SECRET",
15
+ slack: "SLACK_WEBHOOK_SECRET",
16
+ linear: "LINEAR_WEBHOOK_SECRET",
17
+ clerk: "CLERK_WEBHOOK_SECRET",
18
+ sendgrid: "SENDGRID_WEBHOOK_SECRET",
19
+ discord: "DISCORD_WEBHOOK_SECRET",
20
+ custom: "WEBHOOK_SECRET",
21
+ };
22
+ return envVarMap[provider] || "WEBHOOK_SECRET";
23
+ }
24
+ export const run = new Command()
25
+ .name("run")
26
+ .argument("[templateId]", "Template ID to run")
27
+ .description("Run a webhook template against a target URL")
28
+ .requiredOption("-u, --url <url>", "Target URL to send the webhook to")
29
+ .option("-s, --secret <secret>", "Secret for signature generation")
30
+ .option("-H, --header <header>", "Add custom header (format: key:value)", (value, previous) => {
31
+ const [key, ...valueParts] = value.split(":");
32
+ const headerValue = valueParts.join(":");
33
+ if (!key || !headerValue) {
34
+ throw new Error("Header format should be key:value");
35
+ }
36
+ return (previous || []).concat([
37
+ { key: key.trim(), value: headerValue.trim() },
38
+ ]);
39
+ }, [])
40
+ .option("-v, --verbose", "Show detailed request/response information")
41
+ .action(async (templateId, options) => {
42
+ const manager = getTemplateManager();
43
+ if (!templateId) {
44
+ const spinner = ora("Loading templates...").start();
45
+ try {
46
+ const local = manager.listLocalTemplates();
47
+ const remote = await manager.listRemoteTemplates();
48
+ spinner.stop();
49
+ if (local.length === 0 && remote.length === 0) {
50
+ console.log(chalk.yellow("\nšŸ“­ No templates available."));
51
+ console.log(chalk.gray(" Download templates with: better-webhook templates download\n"));
52
+ return;
53
+ }
54
+ const choices = [];
55
+ if (local.length > 0) {
56
+ for (const t of local) {
57
+ choices.push({
58
+ title: `${t.id} ${chalk.green("(local)")}`,
59
+ description: `${t.metadata.provider} - ${t.metadata.event}`,
60
+ value: t.id,
61
+ });
62
+ }
63
+ }
64
+ const remoteOnly = remote.filter((t) => !t.isDownloaded);
65
+ for (const t of remoteOnly) {
66
+ choices.push({
67
+ title: `${t.metadata.id} ${chalk.gray("(remote)")}`,
68
+ description: `${t.metadata.provider} - ${t.metadata.event}`,
69
+ value: `remote:${t.metadata.id}`,
70
+ });
71
+ }
72
+ const response = await prompts({
73
+ type: "select",
74
+ name: "templateId",
75
+ message: "Select a template to run:",
76
+ choices,
77
+ });
78
+ if (!response.templateId) {
79
+ console.log(chalk.yellow("Cancelled"));
80
+ return;
81
+ }
82
+ templateId = response.templateId;
83
+ }
84
+ catch (error) {
85
+ spinner.fail("Failed to load templates");
86
+ console.error(chalk.red(error.message));
87
+ process.exitCode = 1;
88
+ return;
89
+ }
90
+ }
91
+ if (templateId?.startsWith("remote:")) {
92
+ const remoteId = templateId.replace("remote:", "");
93
+ const downloadSpinner = ora(`Downloading ${remoteId}...`).start();
94
+ try {
95
+ await manager.downloadTemplate(remoteId);
96
+ downloadSpinner.succeed(`Downloaded ${remoteId}`);
97
+ templateId = remoteId;
98
+ }
99
+ catch (error) {
100
+ downloadSpinner.fail(`Failed to download: ${error.message}`);
101
+ process.exitCode = 1;
102
+ return;
103
+ }
104
+ }
105
+ const localTemplate = manager.getLocalTemplate(templateId);
106
+ if (!localTemplate) {
107
+ console.log(chalk.red(`\nāŒ Template not found: ${templateId}`));
108
+ console.log(chalk.gray(" Download it with: better-webhook templates download " +
109
+ templateId +
110
+ "\n"));
111
+ process.exitCode = 1;
112
+ return;
113
+ }
114
+ const targetUrl = options.url;
115
+ let secret = options?.secret;
116
+ if (!secret && localTemplate.metadata.provider) {
117
+ const envVarName = getSecretEnvVarName(localTemplate.metadata.provider);
118
+ secret = process.env[envVarName];
119
+ }
120
+ console.log(chalk.bold("\nšŸš€ Executing Webhook\n"));
121
+ console.log(chalk.gray(` Template: ${templateId}`));
122
+ console.log(chalk.gray(` Provider: ${localTemplate.metadata.provider}`));
123
+ console.log(chalk.gray(` Event: ${localTemplate.metadata.event}`));
124
+ console.log(chalk.gray(` Target: ${targetUrl}`));
125
+ if (secret) {
126
+ console.log(chalk.gray(` Signature: Will be generated`));
127
+ }
128
+ else {
129
+ console.log(chalk.yellow(` āš ļø No secret provided - signature will not be generated`));
130
+ }
131
+ console.log();
132
+ const spinner = ora("Sending webhook...").start();
133
+ try {
134
+ const result = await executeTemplate(localTemplate.template, {
135
+ url: targetUrl,
136
+ secret,
137
+ headers: options?.header,
138
+ });
139
+ spinner.stop();
140
+ const statusColor = result.status >= 200 && result.status < 300
141
+ ? chalk.green
142
+ : result.status >= 400
143
+ ? chalk.red
144
+ : chalk.yellow;
145
+ console.log(chalk.bold("šŸ“„ Response\n"));
146
+ console.log(` Status: ${statusColor(`${result.status} ${result.statusText}`)}`);
147
+ console.log(` Duration: ${chalk.cyan(`${result.duration}ms`)}`);
148
+ if (options?.verbose) {
149
+ console.log(chalk.bold("\n Headers:"));
150
+ for (const [key, value] of Object.entries(result.headers)) {
151
+ const headerValue = Array.isArray(value) ? value.join(", ") : value;
152
+ console.log(chalk.gray(` ${key}: ${headerValue}`));
153
+ }
154
+ }
155
+ if (result.json !== undefined) {
156
+ console.log(chalk.bold("\n Body:"));
157
+ console.log(chalk.gray(JSON.stringify(result.json, null, 2)
158
+ .split("\n")
159
+ .map((l) => ` ${l}`)
160
+ .join("\n")));
161
+ }
162
+ else if (result.bodyText) {
163
+ console.log(chalk.bold("\n Body:"));
164
+ const preview = result.bodyText.length > 500
165
+ ? result.bodyText.slice(0, 500) + "..."
166
+ : result.bodyText;
167
+ console.log(chalk.gray(` ${preview}`));
168
+ }
169
+ console.log();
170
+ if (result.status >= 200 && result.status < 300) {
171
+ console.log(chalk.green("āœ“ Webhook delivered successfully\n"));
172
+ }
173
+ else {
174
+ console.log(chalk.yellow(`⚠ Webhook delivered with status ${result.status}\n`));
175
+ }
176
+ }
177
+ catch (error) {
178
+ spinner.fail("Request failed");
179
+ console.error(chalk.red(`\nāŒ ${error.message}\n`));
180
+ process.exitCode = 1;
181
+ }
182
+ });
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare const templates: Command;
@@ -0,0 +1,285 @@
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
+ const listCommand = new Command()
7
+ .name("list")
8
+ .alias("ls")
9
+ .description("List available remote templates from the repository")
10
+ .option("-p, --provider <provider>", "Filter by provider (stripe, github, etc.)")
11
+ .option("-r, --refresh", "Force refresh the template index cache")
12
+ .action(async (options) => {
13
+ const spinner = ora("Fetching remote templates...").start();
14
+ try {
15
+ const manager = getTemplateManager();
16
+ const templates = await manager.listRemoteTemplates({
17
+ forceRefresh: !!options.refresh,
18
+ });
19
+ spinner.stop();
20
+ if (templates.length === 0) {
21
+ console.log(chalk.yellow("šŸ“­ No remote templates found."));
22
+ return;
23
+ }
24
+ let filtered = templates;
25
+ if (options.provider) {
26
+ filtered = templates.filter((t) => t.metadata.provider.toLowerCase() ===
27
+ options.provider?.toLowerCase());
28
+ }
29
+ if (filtered.length === 0) {
30
+ console.log(chalk.yellow(`šŸ“­ No templates found for provider: ${options.provider}`));
31
+ return;
32
+ }
33
+ console.log(chalk.bold("\nšŸ“¦ Available Templates\n"));
34
+ const byProvider = new Map();
35
+ for (const t of filtered) {
36
+ const provider = t.metadata.provider;
37
+ if (!byProvider.has(provider)) {
38
+ byProvider.set(provider, []);
39
+ }
40
+ byProvider.get(provider).push(t);
41
+ }
42
+ for (const [provider, providerTemplates] of byProvider) {
43
+ console.log(chalk.cyan.bold(` ${provider.toUpperCase()}`));
44
+ for (const t of providerTemplates) {
45
+ const status = t.isDownloaded
46
+ ? chalk.green("āœ“ downloaded")
47
+ : chalk.gray("ā—‹ remote");
48
+ console.log(` ${chalk.white(t.metadata.id)} ${status}`);
49
+ if (t.metadata.description) {
50
+ console.log(chalk.gray(` ${t.metadata.description}`));
51
+ }
52
+ }
53
+ console.log();
54
+ }
55
+ console.log(chalk.gray(` Total: ${filtered.length} templates`));
56
+ console.log(chalk.gray(` Download: better-webhook templates download <id>\n`));
57
+ }
58
+ catch (error) {
59
+ spinner.fail("Failed to fetch templates");
60
+ console.error(chalk.red(error.message));
61
+ process.exitCode = 1;
62
+ }
63
+ });
64
+ const downloadCommand = new Command()
65
+ .name("download")
66
+ .alias("get")
67
+ .argument("[templateId]", "Template ID to download")
68
+ .description("Download a template to local storage")
69
+ .option("-a, --all", "Download all available templates")
70
+ .option("-r, --refresh", "Force refresh the template index cache")
71
+ .action(async (templateId, options) => {
72
+ const manager = getTemplateManager();
73
+ if (options?.all) {
74
+ const spinner = ora("Fetching template list...").start();
75
+ try {
76
+ const templates = await manager.listRemoteTemplates({
77
+ forceRefresh: true,
78
+ });
79
+ const toDownload = templates.filter((t) => !t.isDownloaded);
80
+ spinner.stop();
81
+ if (toDownload.length === 0) {
82
+ console.log(chalk.green("āœ“ All templates already downloaded"));
83
+ return;
84
+ }
85
+ console.log(chalk.bold(`\nDownloading ${toDownload.length} templates...\n`));
86
+ for (const t of toDownload) {
87
+ const downloadSpinner = ora(`Downloading ${t.metadata.id}...`).start();
88
+ try {
89
+ await manager.downloadTemplate(t.metadata.id);
90
+ downloadSpinner.succeed(`Downloaded ${t.metadata.id}`);
91
+ }
92
+ catch (error) {
93
+ downloadSpinner.fail(`Failed: ${t.metadata.id} - ${error.message}`);
94
+ }
95
+ }
96
+ console.log(chalk.green("\nāœ“ Download complete\n"));
97
+ }
98
+ catch (error) {
99
+ spinner.fail("Failed to fetch templates");
100
+ console.error(chalk.red(error.message));
101
+ process.exitCode = 1;
102
+ }
103
+ return;
104
+ }
105
+ if (!templateId) {
106
+ const spinner = ora("Fetching templates...").start();
107
+ try {
108
+ const templates = await manager.listRemoteTemplates({
109
+ forceRefresh: !!options?.refresh,
110
+ });
111
+ spinner.stop();
112
+ const notDownloaded = templates.filter((t) => !t.isDownloaded);
113
+ if (notDownloaded.length === 0) {
114
+ console.log(chalk.green("āœ“ All templates already downloaded"));
115
+ return;
116
+ }
117
+ const choices = notDownloaded.map((t) => ({
118
+ title: t.metadata.id,
119
+ description: `${t.metadata.provider} - ${t.metadata.event}`,
120
+ value: t.metadata.id,
121
+ }));
122
+ const response = await prompts({
123
+ type: "select",
124
+ name: "templateId",
125
+ message: "Select a template to download:",
126
+ choices,
127
+ });
128
+ if (!response.templateId) {
129
+ console.log(chalk.yellow("Cancelled"));
130
+ return;
131
+ }
132
+ templateId = response.templateId;
133
+ }
134
+ catch (error) {
135
+ spinner.fail("Failed to fetch templates");
136
+ console.error(chalk.red(error.message));
137
+ process.exitCode = 1;
138
+ return;
139
+ }
140
+ }
141
+ const spinner = ora(`Downloading ${templateId}...`).start();
142
+ try {
143
+ const template = await manager.downloadTemplate(templateId);
144
+ spinner.succeed(`Downloaded ${templateId}`);
145
+ console.log(chalk.gray(` Saved to: ${template.filePath}`));
146
+ console.log(chalk.gray(` Run with: better-webhook run ${templateId}\n`));
147
+ }
148
+ catch (error) {
149
+ spinner.fail(`Failed to download ${templateId}`);
150
+ console.error(chalk.red(error.message));
151
+ process.exitCode = 1;
152
+ }
153
+ });
154
+ const localCommand = new Command()
155
+ .name("local")
156
+ .description("List downloaded local templates")
157
+ .option("-p, --provider <provider>", "Filter by provider")
158
+ .action((options) => {
159
+ const manager = getTemplateManager();
160
+ let templates = manager.listLocalTemplates();
161
+ if (options.provider) {
162
+ templates = templates.filter((t) => t.metadata.provider.toLowerCase() === options.provider?.toLowerCase());
163
+ }
164
+ if (templates.length === 0) {
165
+ console.log(chalk.yellow("\nšŸ“­ No local templates found."));
166
+ console.log(chalk.gray(" Download templates with: better-webhook templates download\n"));
167
+ return;
168
+ }
169
+ console.log(chalk.bold("\nšŸ“ Local Templates\n"));
170
+ const byProvider = new Map();
171
+ for (const t of templates) {
172
+ const provider = t.metadata.provider;
173
+ if (!byProvider.has(provider)) {
174
+ byProvider.set(provider, []);
175
+ }
176
+ byProvider.get(provider).push(t);
177
+ }
178
+ for (const [provider, providerTemplates] of byProvider) {
179
+ console.log(chalk.cyan.bold(` ${provider.toUpperCase()}`));
180
+ for (const t of providerTemplates) {
181
+ console.log(` ${chalk.white(t.id)}`);
182
+ console.log(chalk.gray(` Event: ${t.metadata.event}`));
183
+ console.log(chalk.gray(` Downloaded: ${new Date(t.downloadedAt).toLocaleDateString()}`));
184
+ }
185
+ console.log();
186
+ }
187
+ console.log(chalk.gray(` Total: ${templates.length} templates`));
188
+ console.log(chalk.gray(` Storage: ${manager.getTemplatesDir()}\n`));
189
+ });
190
+ const searchCommand = new Command()
191
+ .name("search")
192
+ .argument("<query>", "Search query")
193
+ .description("Search templates by name, provider, or event")
194
+ .action(async (query) => {
195
+ const spinner = ora("Searching...").start();
196
+ try {
197
+ const manager = getTemplateManager();
198
+ const results = await manager.searchTemplates(query);
199
+ spinner.stop();
200
+ const totalCount = results.remote.length + results.local.length;
201
+ if (totalCount === 0) {
202
+ console.log(chalk.yellow(`\nšŸ“­ No templates found for: "${query}"\n`));
203
+ return;
204
+ }
205
+ console.log(chalk.bold(`\nšŸ” Search Results for "${query}"\n`));
206
+ if (results.local.length > 0) {
207
+ console.log(chalk.cyan.bold(" LOCAL TEMPLATES"));
208
+ for (const t of results.local) {
209
+ console.log(` ${chalk.green("āœ“")} ${t.id} (${t.metadata.provider})`);
210
+ }
211
+ console.log();
212
+ }
213
+ if (results.remote.length > 0) {
214
+ console.log(chalk.cyan.bold(" REMOTE TEMPLATES"));
215
+ for (const t of results.remote) {
216
+ const status = t.isDownloaded ? chalk.green("āœ“") : chalk.gray("ā—‹");
217
+ console.log(` ${status} ${t.metadata.id} (${t.metadata.provider})`);
218
+ }
219
+ console.log();
220
+ }
221
+ console.log(chalk.gray(` Found: ${totalCount} templates\n`));
222
+ }
223
+ catch (error) {
224
+ spinner.fail("Search failed");
225
+ console.error(chalk.red(error.message));
226
+ process.exitCode = 1;
227
+ }
228
+ });
229
+ const cacheCommand = new Command()
230
+ .name("cache")
231
+ .description("Manage template cache")
232
+ .option("-c, --clear", "Clear the template cache")
233
+ .action((options) => {
234
+ if (options.clear) {
235
+ const manager = getTemplateManager();
236
+ manager.clearCache();
237
+ console.log(chalk.green("āœ“ Template cache cleared"));
238
+ }
239
+ else {
240
+ console.log("Use --clear to clear the template cache");
241
+ }
242
+ });
243
+ const cleanCommand = new Command()
244
+ .name("clean")
245
+ .alias("remove-all")
246
+ .description("Remove all downloaded templates")
247
+ .option("-f, --force", "Skip confirmation prompt")
248
+ .action(async (options) => {
249
+ const manager = getTemplateManager();
250
+ const templates = manager.listLocalTemplates();
251
+ if (templates.length === 0) {
252
+ console.log(chalk.yellow("\nšŸ“­ No local templates to remove.\n"));
253
+ return;
254
+ }
255
+ console.log(chalk.bold(`\nšŸ—‘ļø Found ${templates.length} downloaded template(s)\n`));
256
+ for (const t of templates) {
257
+ console.log(chalk.gray(` ${t.id} (${t.metadata.provider})`));
258
+ }
259
+ console.log();
260
+ if (!options.force) {
261
+ const response = await prompts({
262
+ type: "confirm",
263
+ name: "confirm",
264
+ message: `Delete all ${templates.length} template(s)?`,
265
+ initial: false,
266
+ });
267
+ if (!response.confirm) {
268
+ console.log(chalk.yellow("Cancelled"));
269
+ return;
270
+ }
271
+ }
272
+ const deleted = manager.deleteAllLocalTemplates();
273
+ console.log(chalk.green(`\nāœ“ Removed ${deleted} template(s)`));
274
+ console.log(chalk.gray(` Storage: ${manager.getTemplatesDir()}\n`));
275
+ });
276
+ export const templates = new Command()
277
+ .name("templates")
278
+ .alias("t")
279
+ .description("Manage webhook templates")
280
+ .addCommand(listCommand)
281
+ .addCommand(downloadCommand)
282
+ .addCommand(localCommand)
283
+ .addCommand(searchCommand)
284
+ .addCommand(cacheCommand)
285
+ .addCommand(cleanCommand);
@@ -0,0 +1,37 @@
1
+ import type { CapturedWebhook, CaptureFile } from "../types/index.js";
2
+ export interface CaptureServerOptions {
3
+ capturesDir?: string;
4
+ verbose?: boolean;
5
+ enableWebSocket?: boolean;
6
+ onCapture?: (args: {
7
+ file: string;
8
+ capture: CapturedWebhook;
9
+ }) => void;
10
+ }
11
+ export declare class CaptureServer {
12
+ private server;
13
+ private wss;
14
+ private capturesDir;
15
+ private clients;
16
+ private captureCount;
17
+ private enableWebSocket;
18
+ private onCapture?;
19
+ private verbose;
20
+ constructor(options?: string | CaptureServerOptions);
21
+ getCapturesDir(): string;
22
+ start(port?: number, host?: string): Promise<number>;
23
+ stop(): Promise<void>;
24
+ private handleRequest;
25
+ private detectProvider;
26
+ private broadcast;
27
+ private hasStandardWebhookHeaders;
28
+ private hasRecallStandardWebhookShape;
29
+ private hasRecallResourceKeys;
30
+ private headerIncludes;
31
+ private sendToClient;
32
+ listCaptures(limit?: number): CaptureFile[];
33
+ getCapture(captureId: string): CaptureFile | null;
34
+ deleteCapture(captureId: string): boolean;
35
+ getClientCount(): number;
36
+ }
37
+ export declare function getCaptureServer(capturesDir?: string): CaptureServer;