@anonymilyhq/cli 1.0.0 ā 1.0.2
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/README.md +54 -0
- package/bin/cli.js +7 -55
- package/lib/forwarder.js +48 -0
- package/lib/logger.js +11 -0
- package/lib/utils.js +3 -0
- package/package.json +6 -4
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# @anonymilyhq/cli
|
|
2
|
+
|
|
3
|
+
The official CLI for [Anonymily](https://anonymily.com), a simple and powerful webhook relayer.
|
|
4
|
+
|
|
5
|
+
With the Anonymily CLI, you can forward webhooks directly from Anonymily to your local machine, allowing you to easily test, inspect, and debug incoming webhooks in real-time.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
You can install the CLI globally using npm:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @anonymilyhq/cli
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Alternatively, you can run it directly using `npx`:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx @anonymilyhq/cli listen 8080
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
Start listening for incoming webhooks and forward them to a specific port on your localhost:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
anonymily listen <port>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Example
|
|
30
|
+
|
|
31
|
+
To forward webhooks to your local development server running on port `3000` with a randomly generated endpoint ID:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
anonymily listen 3000
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
To specify a custom endpoint ID (e.g., `stripe-test`) so that your URL is consistently `api.anonymily.com/h/stripe-test`:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
anonymily listen 3000 --id stripe-test
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
1. The CLI will output a webhook connection URL and automatically connect via Server-Sent Events.
|
|
44
|
+
2. Send your webhooks to the provided URL.
|
|
45
|
+
3. The CLI will receive the requests natively and forward them precisely to `http://localhost:3000`, preserving exactly the same method, body, queries, and headers.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## Issues
|
|
49
|
+
|
|
50
|
+
If you encounter a bug, please create an issue in the main repository or contact our support.
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
|
|
54
|
+
MIT
|
package/bin/cli.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { EventSource as EventSourceConstructor } from 'eventsource';
|
|
4
|
-
|
|
5
3
|
import { program } from 'commander';
|
|
6
|
-
import
|
|
4
|
+
import { generateHookId } from '../lib/utils.js';
|
|
5
|
+
import { startForwarding } from '../lib/forwarder.js';
|
|
7
6
|
|
|
8
7
|
// Point this to your live production backend URL
|
|
9
8
|
const API_URL = process.env.ANONYMILY_API_URL || 'https://api.anonymily.com';
|
|
@@ -17,57 +16,10 @@ program
|
|
|
17
16
|
.command('listen')
|
|
18
17
|
.description('Listen for incoming webhooks and forward them to a local port')
|
|
19
18
|
.argument('<port>', 'Local port to forward to (e.g., 8080)')
|
|
20
|
-
.
|
|
21
|
-
|
|
19
|
+
.option('-i, --id <id>', 'Provide a custom endpoint ID to listen to')
|
|
20
|
+
.action((port, options) => {
|
|
21
|
+
const hookId = options.id || generateHookId();
|
|
22
|
+
startForwarding(API_URL, hookId, parseInt(port, 10));
|
|
22
23
|
});
|
|
23
24
|
|
|
24
|
-
program.parse();
|
|
25
|
-
|
|
26
|
-
async function startListening(port) {
|
|
27
|
-
// 1. Generate a random Hook ID
|
|
28
|
-
const hookId = Math.random().toString(36).substring(2, 10);
|
|
29
|
-
const webhookUrl = `${API_URL}/h/${hookId}`;
|
|
30
|
-
const localUrl = `http://localhost:${port}`;
|
|
31
|
-
|
|
32
|
-
console.log(pc.green(pc.bold(`\nš Anonymily CLI is running!`)));
|
|
33
|
-
console.log(`\nForwarding: ${pc.cyan(webhookUrl)} ā ${pc.cyan(localUrl)}`);
|
|
34
|
-
console.log(pc.dim(`Waiting for requests to arrive...\n`));
|
|
35
|
-
|
|
36
|
-
// 2. Connect to the Server-Sent Events stream using the safe constructor
|
|
37
|
-
const es = new EventSourceConstructor(`${API_URL}/stream/${hookId}`);
|
|
38
|
-
|
|
39
|
-
es.onmessage = async (event) => {
|
|
40
|
-
const reqData = JSON.parse(event.data);
|
|
41
|
-
const { method, headers, query, body } = reqData;
|
|
42
|
-
|
|
43
|
-
// 3. Clean up headers before forwarding
|
|
44
|
-
const forwardHeaders = { ...headers };
|
|
45
|
-
delete forwardHeaders.host;
|
|
46
|
-
delete forwardHeaders.connection;
|
|
47
|
-
|
|
48
|
-
// Reconstruct query parameters
|
|
49
|
-
const queryString = new URLSearchParams(query).toString();
|
|
50
|
-
const fullLocalUrl = queryString ? `${localUrl}?${queryString}` : localUrl;
|
|
51
|
-
|
|
52
|
-
console.log(pc.yellow(`[${new Date().toLocaleTimeString()}] ā” Incoming ${method} request...`));
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
// 4. Forward the exact payload to localhost
|
|
56
|
-
const response = await fetch(fullLocalUrl, {
|
|
57
|
-
method: method,
|
|
58
|
-
headers: forwardHeaders,
|
|
59
|
-
body: ['GET', 'HEAD'].includes(method) ? undefined : JSON.stringify(body)
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const statusColor = response.ok ? pc.green : pc.red;
|
|
63
|
-
console.log(` āā Forwarded to localhost | Status: ${statusColor(response.status)}\n`);
|
|
64
|
-
} catch (err) {
|
|
65
|
-
console.log(pc.red(` āā Error forwarding: ${err.message}`));
|
|
66
|
-
console.log(pc.dim(` Make sure your local server is actually running on port ${port}\n`));
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
es.onerror = () => {
|
|
71
|
-
console.error(pc.red(`\nConnection error. Attempting to reconnect...`));
|
|
72
|
-
};
|
|
73
|
-
}
|
|
25
|
+
program.parse();
|
package/lib/forwarder.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { EventSource as EventSourceConstructor } from 'eventsource';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
import { logger } from './logger.js';
|
|
4
|
+
|
|
5
|
+
export function startForwarding(apiUrl, hookId, port) {
|
|
6
|
+
const webhookUrl = `${apiUrl}/h/${hookId}`;
|
|
7
|
+
const localUrl = `http://localhost:${port}`;
|
|
8
|
+
|
|
9
|
+
logger.success(pc.bold(`\nš Anonymily CLI is running!`));
|
|
10
|
+
logger.raw(`\nForwarding: ${pc.cyan(webhookUrl)} ā ${pc.cyan(localUrl)}`);
|
|
11
|
+
logger.dim(`Waiting for requests to arrive...\n`);
|
|
12
|
+
|
|
13
|
+
const es = new EventSourceConstructor(`${apiUrl}/stream/${hookId}`);
|
|
14
|
+
|
|
15
|
+
es.onmessage = async (event) => {
|
|
16
|
+
try {
|
|
17
|
+
const reqData = JSON.parse(event.data);
|
|
18
|
+
const { method, headers, query, body } = reqData;
|
|
19
|
+
|
|
20
|
+
const forwardHeaders = { ...headers };
|
|
21
|
+
delete forwardHeaders.host;
|
|
22
|
+
delete forwardHeaders.connection;
|
|
23
|
+
|
|
24
|
+
const queryString = new URLSearchParams(query || {}).toString();
|
|
25
|
+
const fullLocalUrl = queryString ? `${localUrl}?${queryString}` : localUrl;
|
|
26
|
+
|
|
27
|
+
logger.warn(`[${new Date().toLocaleTimeString()}] ā” Incoming ${method} request...`);
|
|
28
|
+
|
|
29
|
+
const response = await fetch(fullLocalUrl, {
|
|
30
|
+
method: method,
|
|
31
|
+
headers: forwardHeaders,
|
|
32
|
+
body: ['GET', 'HEAD'].includes(method) ? undefined : JSON.stringify(body)
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const statusColor = response.ok ? pc.green : pc.red;
|
|
36
|
+
logger.raw(` āā Forwarded to localhost | Status: ${statusColor(response.status)}\n`);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
logger.error(` āā Error forwarding: ${err.message}`);
|
|
39
|
+
logger.dim(` Make sure your local server is actually running on port ${port}\n`);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
es.onerror = () => {
|
|
44
|
+
logger.dim(`\n[Network] Connection dropped. Automatically reconnecting...`);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return es;
|
|
48
|
+
}
|
package/lib/logger.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
|
|
3
|
+
export const logger = {
|
|
4
|
+
info: (msg) => console.log(pc.cyan(msg)),
|
|
5
|
+
success: (msg) => console.log(pc.green(msg)),
|
|
6
|
+
warn: (msg) => console.log(pc.yellow(msg)),
|
|
7
|
+
error: (msg) => console.log(pc.red(msg)),
|
|
8
|
+
dim: (msg) => console.log(pc.dim(msg)),
|
|
9
|
+
bold: (msg) => console.log(pc.bold(msg)),
|
|
10
|
+
raw: (msg) => console.log(msg),
|
|
11
|
+
};
|
package/lib/utils.js
ADDED
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anonymilyhq/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "CLI for Anonymily platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"anonymily": "
|
|
7
|
+
"anonymily": "bin/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node ./bin/cli.js"
|
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
"devtools",
|
|
17
17
|
"api-client"
|
|
18
18
|
],
|
|
19
|
+
"author": "Anonymily",
|
|
20
|
+
"license": "MIT",
|
|
19
21
|
"files": [
|
|
20
22
|
"bin",
|
|
21
|
-
"
|
|
23
|
+
"lib",
|
|
22
24
|
"README.md"
|
|
23
25
|
],
|
|
24
26
|
"dependencies": {
|
|
@@ -26,4 +28,4 @@
|
|
|
26
28
|
"eventsource": "^4.1.0",
|
|
27
29
|
"picocolors": "^1.1.1"
|
|
28
30
|
}
|
|
29
|
-
}
|
|
31
|
+
}
|