hookcatch 0.1.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 (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +170 -0
  3. package/bin/hookcatch.js +2 -0
  4. package/dist/commands/list.d.ts +3 -0
  5. package/dist/commands/list.d.ts.map +1 -0
  6. package/dist/commands/list.js +55 -0
  7. package/dist/commands/list.js.map +1 -0
  8. package/dist/commands/login.d.ts +3 -0
  9. package/dist/commands/login.d.ts.map +1 -0
  10. package/dist/commands/login.js +83 -0
  11. package/dist/commands/login.js.map +1 -0
  12. package/dist/commands/logout.d.ts +3 -0
  13. package/dist/commands/logout.d.ts.map +1 -0
  14. package/dist/commands/logout.js +11 -0
  15. package/dist/commands/logout.js.map +1 -0
  16. package/dist/commands/stop.d.ts +3 -0
  17. package/dist/commands/stop.d.ts.map +1 -0
  18. package/dist/commands/stop.js +31 -0
  19. package/dist/commands/stop.js.map +1 -0
  20. package/dist/commands/tunnel.d.ts +3 -0
  21. package/dist/commands/tunnel.d.ts.map +1 -0
  22. package/dist/commands/tunnel.js +197 -0
  23. package/dist/commands/tunnel.js.map +1 -0
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +34 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/lib/api.d.ts +23 -0
  29. package/dist/lib/api.d.ts.map +1 -0
  30. package/dist/lib/api.js +114 -0
  31. package/dist/lib/api.js.map +1 -0
  32. package/dist/lib/config.d.ts +7 -0
  33. package/dist/lib/config.d.ts.map +1 -0
  34. package/dist/lib/config.js +26 -0
  35. package/dist/lib/config.js.map +1 -0
  36. package/dist/lib/websocket.d.ts +15 -0
  37. package/dist/lib/websocket.d.ts.map +1 -0
  38. package/dist/lib/websocket.js +164 -0
  39. package/dist/lib/websocket.js.map +1 -0
  40. package/package.json +66 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 HookCatch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # HookCatch CLI
2
+
3
+ Official CLI tool for creating localhost tunnels with HookCatch.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # Using npx (no installation)
9
+ npx hookcatch tunnel 3000
10
+
11
+ # Or install globally
12
+ npm install -g hookcatch
13
+ hookcatch tunnel 3000
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ ### 1. Login
19
+
20
+ First, generate an API token from your [HookCatch Dashboard](https://hookcatch.dev/dashboard), then:
21
+
22
+ ```bash
23
+ npx hookcatch login
24
+ # Enter your API token when prompted
25
+ ```
26
+
27
+ ### 2. Start Tunnel (Inbound - Receive webhooks)
28
+
29
+ Forward external webhooks to your localhost:
30
+
31
+ ```bash
32
+ npx hookcatch tunnel <port>
33
+
34
+ # Example: Forward to localhost:3000
35
+ npx hookcatch tunnel 3000
36
+
37
+ # With custom options
38
+ npx hookcatch tunnel 8080 --password secret123
39
+ ```
40
+
41
+ Your tunnel URL will be displayed. Send webhooks to this URL and they'll be forwarded to your localhost.
42
+
43
+ ### 3. Start Tunnel with Capture (Outbound - Capture localhost requests)
44
+
45
+ Capture outbound requests from your localhost application to a bin:
46
+
47
+ ```bash
48
+ npx hookcatch tunnel <port> --capture <binId>
49
+
50
+ # Example: Capture requests to bin abc123
51
+ npx hookcatch tunnel 3000 --capture abc123
52
+
53
+ # Custom proxy port (default: 8081)
54
+ npx hookcatch tunnel 3000 --capture abc123 --proxy-port 9000
55
+ ```
56
+
57
+ Configure your application to use the proxy:
58
+
59
+ ```bash
60
+ # Set HTTP_PROXY environment variable
61
+ HTTP_PROXY=http://localhost:8081 node app.js
62
+
63
+ # Or in your code (Node.js example):
64
+ const axios = require('axios');
65
+ axios.get('https://api.example.com', {
66
+ proxy: {
67
+ host: 'localhost',
68
+ port: 8081
69
+ }
70
+ });
71
+ ```
72
+
73
+ All requests through the proxy will be captured in your bin and visible in the dashboard.
74
+
75
+ ### 4. Bidirectional Mode (Both directions)
76
+
77
+ Run both inbound and outbound capture simultaneously:
78
+
79
+ ```bash
80
+ npx hookcatch tunnel 3000 --capture my-bin-id
81
+ ```
82
+
83
+ - **INBOUND**: External webhooks → tunnel URL → your localhost:3000
84
+ - **OUTBOUND**: Your app → proxy (localhost:8081) → captured in bin
85
+
86
+ ### 5. List Active Tunnels
87
+
88
+ ```bash
89
+ npx hookcatch list
90
+ ```
91
+
92
+ ### 6. Stop Tunnel
93
+
94
+ ```bash
95
+ npx hookcatch stop <tunnelId>
96
+ ```
97
+
98
+ ## Commands
99
+
100
+ ### `login`
101
+ Authenticate with your HookCatch API token.
102
+
103
+ ```bash
104
+ npx hookcatch login
105
+ ```
106
+
107
+ ### `tunnel <port>`
108
+ Create a tunnel to your localhost.
109
+
110
+ **Options:**
111
+ - `--password <password>` - Password-protect the tunnel (PRO+ tier)
112
+ - `--subdomain <name>` - Custom subdomain (ENTERPRISE tier)
113
+
114
+ **Example:**
115
+ ```bash
116
+ npx hookcatch tunnel 3000
117
+ # ✓ Tunnel established
118
+ # → http://localhost:3001/tunnel/abc123xyz
119
+ #
120
+ # Forwarding to http://localhost:3000
121
+ # Press Ctrl+C to stop
122
+ ```
123
+
124
+ ### `list`
125
+ Show all your active tunnels.
126
+
127
+ ```bash
128
+ npx hookcatch list
129
+ ```
130
+
131
+ ### `stop <tunnelId>`
132
+ Stop a specific tunnel.
133
+
134
+ ```bash
135
+ npx hookcatch stop abc123xyz
136
+ ```
137
+
138
+ ### `logout`
139
+ Remove stored API token.
140
+
141
+ ```bash
142
+ npx hookcatch logout
143
+ ```
144
+
145
+ ## Features
146
+
147
+ - ✅ Zero-config tunnel creation
148
+ - ✅ Real-time request forwarding
149
+ - ✅ Automatic reconnection on network issues (PRO+)
150
+ - ✅ Password protection (PRO+ tier)
151
+ - ✅ Custom subdomains (ENTERPRISE tier)
152
+ - ✅ Usage tracking and limits
153
+ - ✅ Cross-platform (Windows, Mac, Linux)
154
+
155
+ ## Tiers
156
+
157
+ - **FREE**: No tunneling (upgrade required)
158
+ - **PLUS**: 1 tunnel, 1-hour sessions, 1GB/month
159
+ - **PRO**: 5 tunnels, unlimited time, 10GB/month
160
+ - **ENTERPRISE**: 20 tunnels, custom subdomains, 100GB/month
161
+
162
+ ## Support
163
+
164
+ - Documentation: https://hookcatch.dev/docs
165
+ - Issues: https://github.com/yourusername/hookcatch-cli/issues
166
+ - Email: support@hookcatch.dev
167
+
168
+ ## License
169
+
170
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import('../dist/index.js');
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const listCommand: Command;
3
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,WAAW,SAwDpB,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { api } from '../lib/api.js';
5
+ import { hasApiToken } from '../lib/config.js';
6
+ export const listCommand = new Command('list')
7
+ .description('List all your active tunnels')
8
+ .action(async () => {
9
+ if (!hasApiToken()) {
10
+ console.log(chalk.red('✗ Not authenticated. Run "hookcatch login" first.\n'));
11
+ process.exit(1);
12
+ }
13
+ const spinner = ora('Fetching tunnels...').start();
14
+ try {
15
+ const tunnels = await api.listTunnels();
16
+ spinner.stop();
17
+ if (tunnels.length === 0) {
18
+ console.log(chalk.yellow('\nNo active tunnels found.\n'));
19
+ console.log(chalk.gray('Create one with: hookcatch tunnel <port>\n'));
20
+ return;
21
+ }
22
+ console.log(chalk.blue.bold('\n📡 Active Tunnels\n'));
23
+ tunnels.forEach((tunnel) => {
24
+ const statusColor = tunnel.isConnected ? 'green' : 'gray';
25
+ const statusText = tunnel.isConnected ? '● ACTIVE' : '○ INACTIVE';
26
+ console.log(chalk.white.bold(`Tunnel: ${tunnel.tunnelId}`));
27
+ console.log(chalk[statusColor](` ${statusText}`));
28
+ console.log(chalk.cyan(` URL: ${tunnel.url}`));
29
+ console.log(chalk.gray(` Port: ${tunnel.localPort}`));
30
+ console.log(chalk.gray(` Created: ${new Date(tunnel.createdAt).toLocaleString()}`));
31
+ if (tunnel.bytesTransferred > 0) {
32
+ const mb = (tunnel.bytesTransferred / 1024 / 1024).toFixed(2);
33
+ console.log(chalk.gray(` Data: ${mb} MB (${tunnel.requestCount} requests)`));
34
+ }
35
+ if (tunnel.expiresAt) {
36
+ const expiresIn = new Date(tunnel.expiresAt).getTime() - Date.now();
37
+ if (expiresIn > 0) {
38
+ const minutes = Math.floor(expiresIn / 60000);
39
+ console.log(chalk.yellow(` ⏱️ Expires in ${minutes} minutes`));
40
+ }
41
+ else {
42
+ console.log(chalk.red(' ⏱️ EXPIRED'));
43
+ }
44
+ }
45
+ console.log();
46
+ });
47
+ console.log(chalk.gray(`Total: ${tunnels.length} tunnel(s)\n`));
48
+ }
49
+ catch (error) {
50
+ spinner.fail('Failed to fetch tunnels');
51
+ console.log(chalk.red(`\n✗ ${error.message}\n`));
52
+ process.exit(1);
53
+ }
54
+ });
55
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const loginCommand: Command;
3
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,eAAO,MAAM,YAAY,SA8ErB,CAAC"}
@@ -0,0 +1,83 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import * as readline from 'readline';
5
+ import { setApiToken, getApiUrl, setApiUrl } from '../lib/config.js';
6
+ import axios from 'axios';
7
+ export const loginCommand = new Command('login')
8
+ .description('Authenticate with your HookCatch API token')
9
+ .action(async () => {
10
+ console.log(chalk.blue.bold('\n🔐 HookCatch Login\n'));
11
+ console.log('Get your API token from: ' + chalk.cyan(`${getApiUrl()}/dashboard\n`));
12
+ const rl = readline.createInterface({
13
+ input: process.stdin,
14
+ output: process.stdout,
15
+ });
16
+ rl.question('Enter your API token: ', async (token) => {
17
+ token = token.trim();
18
+ if (!token) {
19
+ console.log(chalk.red('\n✗ API token is required'));
20
+ rl.close();
21
+ process.exit(1);
22
+ }
23
+ if (!token.startsWith('hc_')) {
24
+ console.log(chalk.red('\n✗ Invalid API token format (should start with "hc_")'));
25
+ rl.close();
26
+ process.exit(1);
27
+ }
28
+ // Validate token with server
29
+ const spinner = ora('Validating token...').start();
30
+ try {
31
+ const apiUrl = getApiUrl();
32
+ const fullUrl = `${apiUrl}/api/user/api-token`;
33
+ const requestToken = (url) => axios.get(url, {
34
+ headers: {
35
+ Authorization: `Bearer ${token}`,
36
+ },
37
+ timeout: 10000,
38
+ });
39
+ let response;
40
+ try {
41
+ response = await requestToken(fullUrl);
42
+ }
43
+ catch (error) {
44
+ if (error.response?.status === 404 && apiUrl === 'http://localhost:3001') {
45
+ const fallbackUrl = 'http://localhost:3002';
46
+ spinner.text = 'Validating token (retrying on port 3002)...';
47
+ response = await requestToken(`${fallbackUrl}/api/user/api-token`);
48
+ setApiUrl(fallbackUrl);
49
+ }
50
+ else {
51
+ throw error;
52
+ }
53
+ }
54
+ if (response.status === 200 && response.data.hasToken) {
55
+ spinner.succeed('Token validated');
56
+ setApiToken(token);
57
+ console.log(chalk.green('\n✓ Successfully authenticated!'));
58
+ console.log(chalk.gray('API token saved to ~/.hookcatch/config.json\n'));
59
+ }
60
+ else {
61
+ spinner.fail('Token validation failed');
62
+ console.log(chalk.red('\n✗ Invalid API token'));
63
+ process.exit(1);
64
+ }
65
+ }
66
+ catch (error) {
67
+ spinner.fail('Token validation failed');
68
+ if (error.response?.status === 401) {
69
+ console.log(chalk.red('\n✗ Invalid API token - authentication failed'));
70
+ }
71
+ else if (error.code === 'ECONNREFUSED') {
72
+ console.log(chalk.red('\n✗ Cannot connect to HookCatch API'));
73
+ console.log(chalk.gray(` Make sure the API is running at ${getApiUrl()}`));
74
+ }
75
+ else {
76
+ console.log(chalk.red('\n✗ Failed to validate token: ' + error.message));
77
+ }
78
+ process.exit(1);
79
+ }
80
+ rl.close();
81
+ });
82
+ });
83
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const logoutCommand: Command;
3
+ //# sourceMappingURL=logout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,eAAO,MAAM,aAAa,SAMtB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { clearApiToken } from '../lib/config.js';
4
+ export const logoutCommand = new Command('logout')
5
+ .description('Remove stored API token')
6
+ .action(() => {
7
+ clearApiToken();
8
+ console.log(chalk.green('\n✓ Logged out successfully'));
9
+ console.log(chalk.gray('API token removed from ~/.hookcatch/config.json\n'));
10
+ });
11
+ //# sourceMappingURL=logout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,yBAAyB,CAAC;KACtC,MAAM,CAAC,GAAG,EAAE;IACX,aAAa,EAAE,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC,CAAC;AAC/E,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const stopCommand: Command;
3
+ //# sourceMappingURL=stop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.d.ts","sourceRoot":"","sources":["../../src/commands/stop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,WAAW,SA0BpB,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { api } from '../lib/api.js';
5
+ import { hasApiToken } from '../lib/config.js';
6
+ export const stopCommand = new Command('stop')
7
+ .description('Stop a tunnel')
8
+ .argument('<tunnelId>', 'Tunnel ID to stop')
9
+ .action(async (tunnelId) => {
10
+ if (!hasApiToken()) {
11
+ console.log(chalk.red('✗ Not authenticated. Run "hookcatch login" first.\n'));
12
+ process.exit(1);
13
+ }
14
+ const spinner = ora(`Stopping tunnel ${tunnelId}...`).start();
15
+ try {
16
+ await api.deleteTunnel(tunnelId);
17
+ spinner.succeed('Tunnel stopped');
18
+ console.log(chalk.green(`\n✓ Tunnel ${tunnelId} has been stopped\n`));
19
+ }
20
+ catch (error) {
21
+ spinner.fail('Failed to stop tunnel');
22
+ if (error.response?.data?.error) {
23
+ console.log(chalk.red(`\n✗ ${error.response.data.error}\n`));
24
+ }
25
+ else {
26
+ console.log(chalk.red(`\n✗ ${error.message}\n`));
27
+ }
28
+ process.exit(1);
29
+ }
30
+ });
31
+ //# sourceMappingURL=stop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.js","sourceRoot":"","sources":["../../src/commands/stop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CAAC,eAAe,CAAC;KAC5B,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;KAC3C,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,EAAE;IACjC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC,CAAC;QAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,mBAAmB,QAAQ,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;IAE9D,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QACjC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,cAAc,QAAQ,qBAAqB,CAAC,CAAC,CAAC;IACxE,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAEtC,IAAI,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;YAChC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;QAC/D,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;QACnD,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const tunnelCommand: Command;
3
+ //# sourceMappingURL=tunnel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel.d.ts","sourceRoot":"","sources":["../../src/commands/tunnel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,eAAO,MAAM,aAAa,SAyJtB,CAAC"}
@@ -0,0 +1,197 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { api } from '../lib/api.js';
5
+ import { TunnelClient } from '../lib/websocket.js';
6
+ import { hasApiToken } from '../lib/config.js';
7
+ import * as http from 'http';
8
+ export const tunnelCommand = new Command('tunnel')
9
+ .description('Create a tunnel to your localhost')
10
+ .argument('<port>', 'Local port to forward (e.g., 3000)')
11
+ .option('--password <password>', 'Password-protect the tunnel (PRO+ tier)')
12
+ .option('--subdomain <name>', 'Custom subdomain (ENTERPRISE tier)')
13
+ .option('--capture <binId>', 'Capture outbound requests to a bin')
14
+ .option('--proxy-port <port>', 'Local proxy port for capture (default: 8081)', '8081')
15
+ .action(async (portArg, options) => {
16
+ if (!hasApiToken()) {
17
+ console.log(chalk.red('✗ Not authenticated. Run "hookcatch login" first.\n'));
18
+ process.exit(1);
19
+ }
20
+ const port = parseInt(portArg, 10);
21
+ if (isNaN(port) || port < 1 || port > 65535) {
22
+ console.log(chalk.red('✗ Invalid port number. Must be between 1 and 65535.\n'));
23
+ process.exit(1);
24
+ }
25
+ const proxyPort = parseInt(options.proxyPort, 10);
26
+ if (isNaN(proxyPort) || proxyPort < 1 || proxyPort > 65535) {
27
+ console.log(chalk.red('✗ Invalid proxy port number. Must be between 1 and 65535.\n'));
28
+ process.exit(1);
29
+ }
30
+ const spinner = ora('Creating tunnel...').start();
31
+ try {
32
+ // Get user stats to determine tier and limits
33
+ const stats = await api.getTunnelStats();
34
+ // Create tunnel via API
35
+ const tunnel = await api.createTunnel(port, {
36
+ subdomain: options.subdomain,
37
+ password: options.password,
38
+ });
39
+ spinner.succeed('Tunnel created');
40
+ console.log(chalk.green.bold('\n✓ Tunnel established'));
41
+ console.log(chalk.cyan(`→ ${tunnel.url}`));
42
+ console.log(chalk.gray(`\nForwarding to http://localhost:${port}`));
43
+ if (options.password) {
44
+ console.log(chalk.yellow('🔒 Password protected'));
45
+ }
46
+ // Start capture proxy if --capture is provided
47
+ let proxyServer = null;
48
+ if (options.capture) {
49
+ console.log(chalk.blue(`\n📦 Capture Mode Enabled`));
50
+ console.log(chalk.gray(`Capturing requests to bin: ${options.capture}`));
51
+ console.log(chalk.gray(`Proxy listening on http://localhost:${proxyPort}`));
52
+ console.log(chalk.gray(`\nConfigure your app to use this proxy:`));
53
+ console.log(chalk.white(` HTTP_PROXY=http://localhost:${proxyPort} node app.js\n`));
54
+ proxyServer = createCaptureProxy(proxyPort, options.capture);
55
+ }
56
+ console.log(chalk.gray('Press Ctrl+C to stop\n'));
57
+ // Session timer - updates at top right of terminal (on ASCII art line)
58
+ const sessionStartTime = Date.now();
59
+ const getTimerString = () => {
60
+ const elapsed = Math.floor((Date.now() - sessionStartTime) / 1000);
61
+ const hours = Math.floor(elapsed / 3600);
62
+ const minutes = Math.floor((elapsed % 3600) / 60);
63
+ const seconds = elapsed % 60;
64
+ const currentTime = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
65
+ if (stats.tier === 'FREE') {
66
+ const totalMinutes = Math.floor((stats.limits.totalTunnelTime || 1800) / 60);
67
+ const totalTime = `${String(totalMinutes).padStart(2, '0')}:00`;
68
+ return `${currentTime} / ${totalTime}`;
69
+ }
70
+ else if (stats.tier === 'PLUS' && tunnel.expiresAt) {
71
+ const limitSeconds = stats.limits.sessionLength || 3600;
72
+ const limitHours = Math.floor(limitSeconds / 3600);
73
+ const limitMinutes = Math.floor((limitSeconds % 3600) / 60);
74
+ const limitTime = `${String(limitHours).padStart(2, '0')}:${String(limitMinutes).padStart(2, '0')}:00`;
75
+ return `${currentTime} / ${limitTime}`;
76
+ }
77
+ else {
78
+ return `${currentTime}`;
79
+ }
80
+ };
81
+ // Count lines printed so far (banner = 5, tunnel info = ~8, capture = 5, total ~18 lines up)
82
+ const timerLine = 5; // Position on last line of ASCII banner
83
+ // Update timer every second at fixed position
84
+ const timerInterval = setInterval(() => {
85
+ const timerStr = getTimerString();
86
+ const timerLength = timerStr.length;
87
+ const cols = process.stdout.columns || 80;
88
+ const col = Math.max(1, cols - timerLength - 2);
89
+ // ANSI escape: save cursor, move to line 5 column X, write timer, restore cursor
90
+ process.stdout.write(`\x1b7\x1b[${timerLine};${col}H${chalk.yellow(timerStr)}\x1b8`);
91
+ }, 1000);
92
+ // Write initial timer
93
+ const timerStr = getTimerString();
94
+ const timerLength = timerStr.length;
95
+ const cols = process.stdout.columns || 80;
96
+ const col = Math.max(1, cols - timerLength - 2);
97
+ process.stdout.write(`\x1b7\x1b[${timerLine};${col}H${chalk.yellow(timerStr)}\x1b8`);
98
+ // Connect WebSocket
99
+ const client = new TunnelClient(tunnel.tunnelId, port, (method, path, statusCode, duration) => {
100
+ const statusColor = statusCode >= 500 ? 'red' : statusCode >= 400 ? 'yellow' : 'green';
101
+ const timestamp = new Date().toLocaleTimeString();
102
+ console.log(chalk.gray(`[${timestamp}]`) +
103
+ ' ' +
104
+ chalk.blue('INBOUND ') +
105
+ ' ' +
106
+ chalk.white(method.padEnd(6)) +
107
+ ' ' +
108
+ chalk.gray(path.padEnd(30)) +
109
+ ' ' +
110
+ chalk[statusColor](String(statusCode).padEnd(3)) +
111
+ ' ' +
112
+ chalk.gray(`(${duration}ms)`));
113
+ });
114
+ await client.connect();
115
+ // Handle graceful shutdown
116
+ const cleanup = () => {
117
+ console.log(chalk.yellow('\n\nShutting down tunnel...'));
118
+ clearInterval(timerInterval);
119
+ client.disconnect();
120
+ if (proxyServer) {
121
+ proxyServer.close();
122
+ }
123
+ process.exit(0);
124
+ };
125
+ process.on('SIGINT', cleanup);
126
+ process.on('SIGTERM', cleanup);
127
+ }
128
+ catch (error) {
129
+ spinner.fail('Failed to create tunnel');
130
+ if (error.response?.data?.error) {
131
+ console.log(chalk.red(`\n✗ ${error.response.data.error}\n`));
132
+ }
133
+ else {
134
+ console.log(chalk.red(`\n✗ ${error.message}\n`));
135
+ }
136
+ process.exit(1);
137
+ }
138
+ });
139
+ function createCaptureProxy(proxyPort, binId) {
140
+ const server = http.createServer(async (req, res) => {
141
+ const startTime = Date.now();
142
+ try {
143
+ // Capture request data
144
+ const chunks = [];
145
+ req.on('data', (chunk) => chunks.push(chunk));
146
+ req.on('end', async () => {
147
+ let body = Buffer.concat(chunks).toString();
148
+ // Windows curl --json flag wraps JSON in single quotes - strip them
149
+ if (body.startsWith("'") && body.endsWith("'")) {
150
+ body = body.slice(1, -1);
151
+ }
152
+ console.log(chalk.gray('DEBUG - Captured body:'), body);
153
+ console.log(chalk.gray('DEBUG - Content-Type:'), req.headers['content-type']);
154
+ // Send to bin endpoint
155
+ try {
156
+ await api.sendToBin(binId, {
157
+ method: req.method || 'GET',
158
+ url: req.url || '/',
159
+ headers: req.headers,
160
+ body: body || undefined,
161
+ });
162
+ const duration = Date.now() - startTime;
163
+ const timestamp = new Date().toLocaleTimeString();
164
+ console.log(chalk.gray(`[${timestamp}]`) +
165
+ ' ' +
166
+ chalk.magenta('OUTBOUND') +
167
+ ' ' +
168
+ chalk.white((req.method || 'GET').padEnd(6)) +
169
+ ' ' +
170
+ chalk.gray((req.url || '/').padEnd(30)) +
171
+ ' ' +
172
+ chalk.green('200') +
173
+ ' ' +
174
+ chalk.gray(`(${duration}ms)`));
175
+ res.writeHead(200, { 'Content-Type': 'application/json' });
176
+ res.end(JSON.stringify({
177
+ success: true,
178
+ message: 'Request captured',
179
+ binUrl: `${process.env.API_URL || 'http://localhost:3001'}/bin/${binId}/view`
180
+ }));
181
+ }
182
+ catch (error) {
183
+ console.error(chalk.red('Failed to capture request:'), error.message);
184
+ res.writeHead(500, { 'Content-Type': 'application/json' });
185
+ res.end(JSON.stringify({ error: 'Failed to capture request' }));
186
+ }
187
+ });
188
+ }
189
+ catch (error) {
190
+ res.writeHead(500);
191
+ res.end('Proxy error');
192
+ }
193
+ });
194
+ server.listen(proxyPort);
195
+ return server;
196
+ }
197
+ //# sourceMappingURL=tunnel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel.js","sourceRoot":"","sources":["../../src/commands/tunnel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,mCAAmC,CAAC;KAChD,QAAQ,CAAC,QAAQ,EAAE,oCAAoC,CAAC;KACxD,MAAM,CAAC,uBAAuB,EAAE,yCAAyC,CAAC;KAC1E,MAAM,CAAC,oBAAoB,EAAE,oCAAoC,CAAC;KAClE,MAAM,CAAC,mBAAmB,EAAE,oCAAoC,CAAC;KACjE,MAAM,CAAC,qBAAqB,EAAE,8CAA8C,EAAE,MAAM,CAAC;KACrF,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,OAAO,EAAE,EAAE;IACzC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC,CAAC;QAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACnC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC,CAAC;QAChF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAClD,IAAI,KAAK,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,IAAI,SAAS,GAAG,KAAK,EAAE,CAAC;QAC3D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC,CAAC;QACtF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,oBAAoB,CAAC,CAAC,KAAK,EAAE,CAAC;IAElD,IAAI,CAAC;QACH,8CAA8C;QAC9C,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,cAAc,EAAE,CAAC;QAEzC,wBAAwB;QACxB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE;YAC1C,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B,CAAC,CAAC;QAEH,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAElC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,oCAAoC,IAAI,EAAE,CAAC,CAAC,CAAC;QAEpE,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC;QACrD,CAAC;QAED,+CAA+C;QAC/C,IAAI,WAAW,GAAuB,IAAI,CAAC;QAC3C,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC,CAAC;YACrD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,8BAA8B,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACzE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,uCAAuC,SAAS,EAAE,CAAC,CAAC,CAAC;YAC5E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC,CAAC;YACnE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,iCAAiC,SAAS,gBAAgB,CAAC,CAAC,CAAC;YAErF,WAAW,GAAG,kBAAkB,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/D,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC;QAElD,uEAAuE;QACvE,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEpC,MAAM,cAAc,GAAG,GAAG,EAAE;YAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB,CAAC,GAAG,IAAI,CAAC,CAAC;YACnE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;YACzC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YAClD,MAAM,OAAO,GAAG,OAAO,GAAG,EAAE,CAAC;YAC7B,MAAM,WAAW,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;YAEhI,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC1B,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,eAAe,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;gBAC7E,MAAM,SAAS,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC;gBAChE,OAAO,GAAG,WAAW,MAAM,SAAS,EAAE,CAAC;YACzC,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBACrD,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,aAAa,IAAI,IAAI,CAAC;gBACxD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;gBACnD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;gBAC5D,MAAM,SAAS,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC;gBACvG,OAAO,GAAG,WAAW,MAAM,SAAS,EAAE,CAAC;YACzC,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,WAAW,EAAE,CAAC;YAC1B,CAAC;QACH,CAAC,CAAC;QAEF,6FAA6F;QAC7F,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC,wCAAwC;QAE7D,8CAA8C;QAC9C,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE;YACrC,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;YAClC,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC;YACpC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;YAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC;YAEhD,iFAAiF;YACjF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,SAAS,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACvF,CAAC,EAAE,IAAI,CAAC,CAAC;QAET,sBAAsB;QACtB,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;QAClC,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC;QACpC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC;QAChD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,SAAS,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAErF,oBAAoB;QACpB,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,MAAc,EAAE,IAAY,EAAE,UAAkB,EAAE,QAAgB,EAAE,EAAE;YAC5H,MAAM,WAAW,GAAG,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;YACvF,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,kBAAkB,EAAE,CAAC;YAClD,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,GAAG,CAAC;gBAC1B,GAAG;gBACH,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;gBACtB,GAAG;gBACH,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBAC7B,GAAG;gBACH,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC3B,GAAG;gBACH,KAAK,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBAChD,GAAG;gBACH,KAAK,CAAC,IAAI,CAAC,IAAI,QAAQ,KAAK,CAAC,CAChC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QAEvB,2BAA2B;QAC3B,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,6BAA6B,CAAC,CAAC,CAAC;YACzD,aAAa,CAAC,aAAa,CAAC,CAAC;YAC7B,MAAM,CAAC,UAAU,EAAE,CAAC;YACpB,IAAI,WAAW,EAAE,CAAC;gBAChB,WAAW,CAAC,KAAK,EAAE,CAAC;YACtB,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC;QAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC9B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAEjC,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QAExC,IAAI,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;YAChC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;QAC/D,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;QACnD,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,SAAS,kBAAkB,CAAC,SAAiB,EAAE,KAAa;IAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAClD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,IAAI,CAAC;YACH,uBAAuB;YACvB,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YAC9C,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;gBACvB,IAAI,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAE5C,oEAAoE;gBACpE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC/C,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC3B,CAAC;gBAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,EAAE,IAAI,CAAC,CAAC;gBACxD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;gBAE9E,uBAAuB;gBACvB,IAAI,CAAC;oBACH,MAAM,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE;wBACzB,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,KAAK;wBAC3B,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG;wBACnB,OAAO,EAAE,GAAG,CAAC,OAAiC;wBAC9C,IAAI,EAAE,IAAI,IAAI,SAAS;qBACxB,CAAC,CAAC;oBAEH,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;oBACxC,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,kBAAkB,EAAE,CAAC;oBAClD,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,IAAI,CAAC,IAAI,SAAS,GAAG,CAAC;wBAC1B,GAAG;wBACH,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;wBACzB,GAAG;wBACH,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;wBAC5C,GAAG;wBACH,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;wBACvC,GAAG;wBACH,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC;wBAClB,GAAG;wBACH,KAAK,CAAC,IAAI,CAAC,IAAI,QAAQ,KAAK,CAAC,CAChC,CAAC;oBAEF,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;wBACrB,OAAO,EAAE,IAAI;wBACb,OAAO,EAAE,kBAAkB;wBAC3B,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,uBAAuB,QAAQ,KAAK,OAAO;qBAC9E,CAAC,CAAC,CAAC;gBACN,CAAC;gBAAC,OAAO,KAAU,EAAE,CAAC;oBACpB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,4BAA4B,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;oBACtE,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,2BAA2B,EAAE,CAAC,CAAC,CAAC;gBAClE,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACnB,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACzB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACzB,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import { loginCommand } from './commands/login.js';
5
+ import { tunnelCommand } from './commands/tunnel.js';
6
+ import { listCommand } from './commands/list.js';
7
+ import { stopCommand } from './commands/stop.js';
8
+ import { logoutCommand } from './commands/logout.js';
9
+ const program = new Command();
10
+ program
11
+ .name('hookcatch')
12
+ .description('CLI tool for creating localhost tunnels with HookCatch')
13
+ .version('0.1.0');
14
+ // ASCII art banner
15
+ console.log(chalk.cyan(`
16
+ _ _ _ ____ _ _
17
+ | | | | ___ ___ | | __/ ___|__ _| |_ ___| |__
18
+ | |_| |/ _ \\ / _ \\| |/ / | / _\` | __/ __| '_ \\
19
+ | _ | (_) | (_) | <| |__| (_| | || (__| | | |
20
+ |_| |_|\\___/ \\___/|_|\\_\\\\____\\__,_|\\__\\___|_| |_|
21
+ `));
22
+ // Add commands
23
+ program.addCommand(loginCommand);
24
+ program.addCommand(tunnelCommand);
25
+ program.addCommand(listCommand);
26
+ program.addCommand(stopCommand);
27
+ program.addCommand(logoutCommand);
28
+ // Parse arguments
29
+ program.parse(process.argv);
30
+ // Show help if no command provided
31
+ if (!process.argv.slice(2).length) {
32
+ program.outputHelp();
33
+ }
34
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,wDAAwD,CAAC;KACrE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,mBAAmB;AACnB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;;;;;;CAMtB,CAAC,CAAC,CAAC;AAEJ,eAAe;AACf,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAElC,kBAAkB;AAClB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAE5B,mCAAmC;AACnC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAClC,OAAO,CAAC,UAAU,EAAE,CAAC;AACvB,CAAC"}
@@ -0,0 +1,23 @@
1
+ declare class HookCatchAPI {
2
+ private client;
3
+ constructor();
4
+ private getHeaders;
5
+ createTunnel(localPort: number, options?: {
6
+ subdomain?: string;
7
+ password?: string;
8
+ }): Promise<any>;
9
+ listTunnels(): Promise<any>;
10
+ getTunnel(tunnelId: string): Promise<any>;
11
+ getTunnelStats(): Promise<any>;
12
+ deleteTunnel(tunnelId: string): Promise<any>;
13
+ getUsageStats(): Promise<any>;
14
+ sendToBin(binId: string, data: {
15
+ method: string;
16
+ url: string;
17
+ headers: Record<string, string>;
18
+ body?: string;
19
+ }): Promise<any>;
20
+ }
21
+ export declare const api: HookCatchAPI;
22
+ export {};
23
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AAGA,cAAM,YAAY;IAChB,OAAO,CAAC,MAAM,CAAgB;;IAS9B,OAAO,CAAC,UAAU;IAUZ,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE;IAanF,WAAW;IAOX,SAAS,CAAC,QAAQ,EAAE,MAAM;IAO1B,cAAc;IAOd,YAAY,CAAC,QAAQ,EAAE,MAAM;IAO7B,aAAa;IAOb,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;CA6DrH;AAED,eAAO,MAAM,GAAG,cAAqB,CAAC"}
@@ -0,0 +1,114 @@
1
+ import axios from 'axios';
2
+ import { getApiToken, getApiUrl } from './config.js';
3
+ class HookCatchAPI {
4
+ client;
5
+ constructor() {
6
+ this.client = axios.create({
7
+ baseURL: getApiUrl(),
8
+ timeout: 30000,
9
+ });
10
+ }
11
+ getHeaders() {
12
+ const token = getApiToken();
13
+ if (!token) {
14
+ throw new Error('Not authenticated. Run "hookcatch login" first.');
15
+ }
16
+ return {
17
+ Authorization: `Bearer ${token}`,
18
+ };
19
+ }
20
+ async createTunnel(localPort, options) {
21
+ const response = await this.client.post('/api/tunnels', {
22
+ localPort,
23
+ subdomain: options?.subdomain,
24
+ password: options?.password,
25
+ }, { headers: this.getHeaders() });
26
+ return response.data.tunnel;
27
+ }
28
+ async listTunnels() {
29
+ const response = await this.client.get('/api/tunnels', {
30
+ headers: this.getHeaders(),
31
+ });
32
+ return response.data.tunnels;
33
+ }
34
+ async getTunnel(tunnelId) {
35
+ const response = await this.client.get(`/api/tunnels/${tunnelId}`, {
36
+ headers: this.getHeaders(),
37
+ });
38
+ return response.data.tunnel;
39
+ }
40
+ async getTunnelStats() {
41
+ const response = await this.client.get('/api/tunnels/stats/usage', {
42
+ headers: this.getHeaders(),
43
+ });
44
+ return response.data.stats;
45
+ }
46
+ async deleteTunnel(tunnelId) {
47
+ const response = await this.client.delete(`/api/tunnels/${tunnelId}`, {
48
+ headers: this.getHeaders(),
49
+ });
50
+ return response.data;
51
+ }
52
+ async getUsageStats() {
53
+ const response = await this.client.get('/api/tunnels/stats/usage', {
54
+ headers: this.getHeaders(),
55
+ });
56
+ return response.data.stats;
57
+ }
58
+ async sendToBin(binId, data) {
59
+ // Clean headers - remove problematic ones
60
+ const cleanHeaders = {};
61
+ for (const [key, value] of Object.entries(data.headers)) {
62
+ const lowerKey = key.toLowerCase();
63
+ // Skip connection-specific, host, and content-type headers (we'll set content-type ourselves)
64
+ if (!['host', 'connection', 'content-length', 'transfer-encoding', 'content-type'].includes(lowerKey)) {
65
+ cleanHeaders[key] = String(value);
66
+ }
67
+ }
68
+ // Get original content type for reference
69
+ const originalContentType = data.headers['content-type'] || data.headers['Content-Type'] || '';
70
+ // Handle body based on content type
71
+ let requestBody = {};
72
+ let finalContentType = 'application/json';
73
+ console.log('DEBUG sendToBin - raw body:', data.body);
74
+ console.log('DEBUG sendToBin - originalContentType:', originalContentType);
75
+ if (data.body && data.body.trim()) {
76
+ if (originalContentType.includes('application/json')) {
77
+ try {
78
+ // Parse JSON so axios sends it as object (axios will stringify)
79
+ requestBody = JSON.parse(data.body);
80
+ finalContentType = 'application/json';
81
+ console.log('DEBUG sendToBin - parsed JSON:', requestBody);
82
+ }
83
+ catch (e) {
84
+ // Invalid JSON, send as raw text
85
+ requestBody = data.body;
86
+ finalContentType = 'text/plain';
87
+ console.log('DEBUG sendToBin - JSON parse failed, sending as text:', e);
88
+ }
89
+ }
90
+ else {
91
+ // Non-JSON content, send raw
92
+ requestBody = data.body;
93
+ finalContentType = originalContentType || 'text/plain';
94
+ }
95
+ }
96
+ else {
97
+ // Empty body - send empty object for JSON
98
+ requestBody = {};
99
+ finalContentType = 'application/json';
100
+ }
101
+ console.log('DEBUG sendToBin - final body:', requestBody);
102
+ console.log('DEBUG sendToBin - final content-type:', finalContentType);
103
+ const response = await this.client.post(`/b/${binId}${data.url}`, requestBody, {
104
+ headers: {
105
+ ...cleanHeaders,
106
+ 'Content-Type': finalContentType,
107
+ 'User-Agent': 'HookCatch-CLI-Proxy/1.0',
108
+ }
109
+ });
110
+ return response.data;
111
+ }
112
+ }
113
+ export const api = new HookCatchAPI();
114
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare function setApiToken(token: string): void;
2
+ export declare function getApiToken(): string | undefined;
3
+ export declare function clearApiToken(): void;
4
+ export declare function getApiUrl(): string;
5
+ export declare function setApiUrl(url: string): void;
6
+ export declare function hasApiToken(): boolean;
7
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAcA,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,wBAAgB,WAAW,IAAI,MAAM,GAAG,SAAS,CAEhD;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,wBAAgB,WAAW,IAAI,OAAO,CAErC"}
@@ -0,0 +1,26 @@
1
+ import Conf from 'conf';
2
+ const config = new Conf({
3
+ projectName: 'hookcatch',
4
+ defaults: {
5
+ apiUrl: 'http://localhost:3002', // Default for development (backend port)
6
+ },
7
+ });
8
+ export function setApiToken(token) {
9
+ config.set('apiToken', token);
10
+ }
11
+ export function getApiToken() {
12
+ return config.get('apiToken');
13
+ }
14
+ export function clearApiToken() {
15
+ config.delete('apiToken');
16
+ }
17
+ export function getApiUrl() {
18
+ return config.get('apiUrl') || 'http://localhost:3002';
19
+ }
20
+ export function setApiUrl(url) {
21
+ config.set('apiUrl', url);
22
+ }
23
+ export function hasApiToken() {
24
+ return !!config.get('apiToken');
25
+ }
26
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAOxB,MAAM,MAAM,GAAG,IAAI,IAAI,CAAS;IAC9B,WAAW,EAAE,WAAW;IACxB,QAAQ,EAAE;QACR,MAAM,EAAE,uBAAuB,EAAE,yCAAyC;KAC3E;CACF,CAAC,CAAC;AAEH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,uBAAuB,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,15 @@
1
+ export declare class TunnelClient {
2
+ private ws;
3
+ private tunnelId;
4
+ private localPort;
5
+ private onRequest?;
6
+ private reconnectAttempts;
7
+ private maxReconnectAttempts;
8
+ private reconnectDelay;
9
+ constructor(tunnelId: string, localPort: number, onRequest?: (method: string, path: string, statusCode: number, duration: number) => void);
10
+ connect(): Promise<void>;
11
+ private handleRequest;
12
+ private attemptReconnect;
13
+ disconnect(): void;
14
+ }
15
+ //# sourceMappingURL=websocket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../src/lib/websocket.ts"],"names":[],"mappings":"AAgCA,qBAAa,YAAY;IACvB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAC,CAA+E;IACjG,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,oBAAoB,CAAK;IACjC,OAAO,CAAC,cAAc,CAAQ;gBAG5B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI;IAOpF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAiEhB,aAAa;IA+E3B,OAAO,CAAC,gBAAgB;IAqBxB,UAAU,IAAI,IAAI;CAMnB"}
@@ -0,0 +1,164 @@
1
+ import WebSocket from 'ws';
2
+ import axios from 'axios';
3
+ import chalk from 'chalk';
4
+ import { getApiToken, getApiUrl } from './config.js';
5
+ export class TunnelClient {
6
+ ws = null;
7
+ tunnelId;
8
+ localPort;
9
+ onRequest;
10
+ reconnectAttempts = 0;
11
+ maxReconnectAttempts = 5;
12
+ reconnectDelay = 2000;
13
+ constructor(tunnelId, localPort, onRequest) {
14
+ this.tunnelId = tunnelId;
15
+ this.localPort = localPort;
16
+ this.onRequest = onRequest;
17
+ }
18
+ async connect() {
19
+ return new Promise((resolve, reject) => {
20
+ const apiToken = getApiToken();
21
+ if (!apiToken) {
22
+ return reject(new Error('Not authenticated. Run "hookcatch login" first.'));
23
+ }
24
+ const apiUrl = getApiUrl();
25
+ const wsUrl = apiUrl.replace('http://', 'ws://').replace('https://', 'wss://');
26
+ // Warn if using insecure WebSocket
27
+ if (wsUrl.startsWith('ws://') && !wsUrl.includes('localhost')) {
28
+ console.log(chalk.yellow('⚠️ WARNING: Using insecure WebSocket connection (ws://)'));
29
+ console.log(chalk.yellow(' All tunnel traffic is transmitted in plaintext.'));
30
+ console.log(chalk.yellow(' Use HTTPS endpoint for secure connections (wss://).\n'));
31
+ }
32
+ this.ws = new WebSocket(`${wsUrl}/ws/tunnel`);
33
+ this.ws.on('open', () => {
34
+ // Send CONNECT message
35
+ this.ws.send(JSON.stringify({
36
+ type: 'CONNECT',
37
+ token: apiToken,
38
+ tunnelId: this.tunnelId,
39
+ localPort: this.localPort,
40
+ }));
41
+ });
42
+ this.ws.on('message', async (data) => {
43
+ try {
44
+ const message = JSON.parse(data.toString());
45
+ if (message.type === 'CONNECTED') {
46
+ this.reconnectAttempts = 0;
47
+ resolve();
48
+ }
49
+ else if (message.type === 'ERROR') {
50
+ console.error(chalk.red(`Error: ${message.message}`));
51
+ this.ws?.close();
52
+ reject(new Error(message.message));
53
+ }
54
+ else if (message.type === 'REQUEST') {
55
+ await this.handleRequest(message);
56
+ }
57
+ else if (message.type === 'PING') {
58
+ // Respond to keepalive ping
59
+ this.ws.send(JSON.stringify({ type: 'PONG' }));
60
+ }
61
+ }
62
+ catch (error) {
63
+ console.error(chalk.red('Error processing message:'), error);
64
+ }
65
+ });
66
+ this.ws.on('close', () => {
67
+ console.log(chalk.yellow('\nTunnel disconnected'));
68
+ this.attemptReconnect();
69
+ });
70
+ this.ws.on('error', (error) => {
71
+ console.error(chalk.red('WebSocket error:'), error.message);
72
+ reject(error);
73
+ });
74
+ });
75
+ }
76
+ async handleRequest(message) {
77
+ const { requestId, method, path, headers, body, query } = message;
78
+ const startTime = Date.now();
79
+ try {
80
+ // Build URL with query params
81
+ const url = new URL(`http://localhost:${this.localPort}${path}`);
82
+ Object.entries(query).forEach(([key, value]) => {
83
+ url.searchParams.append(key, String(value));
84
+ });
85
+ const sanitizedHeaders = Object.fromEntries(Object.entries(headers)
86
+ .filter(([, value]) => typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean')
87
+ .map(([key, value]) => [key, String(value)]));
88
+ delete sanitizedHeaders['content-length'];
89
+ delete sanitizedHeaders['transfer-encoding'];
90
+ delete sanitizedHeaders['connection'];
91
+ delete sanitizedHeaders['host'];
92
+ // Forward request to localhost
93
+ const response = await axios({
94
+ method: method.toLowerCase(),
95
+ url: url.toString(),
96
+ headers: {
97
+ ...sanitizedHeaders,
98
+ host: `localhost:${this.localPort}`, // Override host header
99
+ },
100
+ data: body,
101
+ validateStatus: () => true, // Accept any status code
102
+ maxRedirects: 0, // Don't follow redirects
103
+ timeout: 30000,
104
+ });
105
+ const duration = Date.now() - startTime;
106
+ // Send response back
107
+ this.ws.send(JSON.stringify({
108
+ type: 'RESPONSE',
109
+ requestId,
110
+ statusCode: response.status,
111
+ headers: response.headers,
112
+ body: response.data,
113
+ }));
114
+ // Log request
115
+ if (this.onRequest) {
116
+ this.onRequest(method, path, response.status, duration);
117
+ }
118
+ }
119
+ catch (error) {
120
+ const duration = Date.now() - startTime;
121
+ const errorMessage = error?.message || (typeof error === 'string' ? error : 'Internal server error');
122
+ const errorCode = error?.code ? ` (${error.code})` : '';
123
+ const errorBody = `${errorMessage}${errorCode}`;
124
+ // Send error response
125
+ this.ws.send(JSON.stringify({
126
+ type: 'RESPONSE',
127
+ requestId,
128
+ statusCode: error.response?.status || 500,
129
+ headers: error.response?.headers || {},
130
+ body: errorBody,
131
+ }));
132
+ // Log error
133
+ if (this.onRequest) {
134
+ this.onRequest(method, path, error.response?.status || 500, duration);
135
+ }
136
+ console.error(chalk.red(`Error forwarding request: ${errorBody}`));
137
+ }
138
+ }
139
+ attemptReconnect() {
140
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
141
+ console.log(chalk.red('Max reconnection attempts reached. Exiting.'));
142
+ process.exit(1);
143
+ }
144
+ this.reconnectAttempts++;
145
+ const delay = this.reconnectDelay * this.reconnectAttempts;
146
+ console.log(chalk.yellow(`Attempting to reconnect in ${delay / 1000}s... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`));
147
+ setTimeout(async () => {
148
+ try {
149
+ await this.connect();
150
+ console.log(chalk.green('✓ Reconnected successfully'));
151
+ }
152
+ catch (error) {
153
+ console.error(chalk.red(`Reconnection failed: ${error.message}`));
154
+ }
155
+ }, delay);
156
+ }
157
+ disconnect() {
158
+ if (this.ws) {
159
+ this.ws.close();
160
+ this.ws = null;
161
+ }
162
+ }
163
+ }
164
+ //# sourceMappingURL=websocket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.js","sourceRoot":"","sources":["../../src/lib/websocket.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "hookcatch",
3
+ "version": "0.1.0",
4
+ "description": "CLI tool for creating localhost tunnels and testing webhooks with HookCatch",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "hookcatch": "./bin/hookcatch.js"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "dist",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "dev": "tsx src/index.ts",
19
+ "prepublishOnly": "npm run build",
20
+ "test": "echo \"No tests yet\" && exit 0"
21
+ },
22
+ "keywords": [
23
+ "webhook",
24
+ "tunnel",
25
+ "localhost",
26
+ "ngrok",
27
+ "development",
28
+ "testing",
29
+ "http",
30
+ "webhooks",
31
+ "debugging",
32
+ "request-bin",
33
+ "hookcatch"
34
+ ],
35
+ "author": {
36
+ "name": "HookCatch",
37
+ "email": "support@hookcatch.dev",
38
+ "url": "https://hookcatch.dev"
39
+ },
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/hookcatch/cli.git"
44
+ },
45
+ "bugs": {
46
+ "url": "https://github.com/hookcatch/cli/issues"
47
+ },
48
+ "homepage": "https://hookcatch.dev",
49
+ "engines": {
50
+ "node": ">=18.0.0"
51
+ },
52
+ "dependencies": {
53
+ "axios": "^1.6.7",
54
+ "chalk": "^5.3.0",
55
+ "commander": "^12.0.0",
56
+ "conf": "^12.0.0",
57
+ "ora": "^8.0.1",
58
+ "ws": "^8.16.0"
59
+ },
60
+ "devDependencies": {
61
+ "@types/node": "^20.11.16",
62
+ "@types/ws": "^8.5.10",
63
+ "tsx": "^4.7.0",
64
+ "typescript": "^5.3.3"
65
+ }
66
+ }