hooktunnel-cli 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.
- package/README.md +404 -0
- package/dist/commands/connect.d.ts +11 -0
- package/dist/commands/connect.d.ts.map +1 -0
- package/dist/commands/connect.js +59 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/hooks.d.ts +6 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +51 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/login.d.ts +10 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +36 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +6 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +16 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/logs.d.ts +10 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +64 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/replay.d.ts +10 -0
- package/dist/commands/replay.d.ts.map +1 -0
- package/dist/commands/replay.js +43 -0
- package/dist/commands/replay.js.map +1 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +56 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +120 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api.d.ts +12 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/api.js +75 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/config.d.ts +24 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +44 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/errors.d.ts +14 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +97 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/tunnel.d.ts +30 -0
- package/dist/lib/tunnel.d.ts.map +1 -0
- package/dist/lib/tunnel.js +237 -0
- package/dist/lib/tunnel.js.map +1 -0
- package/dist/types/api.d.ts +47 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +5 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/tunnel.d.ts +82 -0
- package/dist/types/tunnel.d.ts.map +1 -0
- package/dist/types/tunnel.js +6 -0
- package/dist/types/tunnel.js.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
# HookTunnel CLI
|
|
2
|
+
|
|
3
|
+
**Webhook infrastructure that never drops a request.**
|
|
4
|
+
|
|
5
|
+
Webhooks are the weakest link in every platform integration. HookTunnel fixes that — reliable ingress, full history, instant replay. For development and production.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx hooktunnel-cli connect dev 3000
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## The Problem
|
|
14
|
+
|
|
15
|
+
Webhooks are fire-and-forget. When they fail, you're blind.
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Provider ──────────────────────────────────▶ Your Server
|
|
19
|
+
If down, webhook lost forever 💀
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Every team hits these:**
|
|
23
|
+
|
|
24
|
+
| Failure Mode | What Happens |
|
|
25
|
+
|--------------|--------------|
|
|
26
|
+
| **Server down** | Webhook gone. Stripe retries 8x, GitHub gives you 1 shot. |
|
|
27
|
+
| **Handler bug** | You need the exact payload to debug. It's gone. |
|
|
28
|
+
| **URL changed** | Redeployed? New infra? Webhooks silently fail. |
|
|
29
|
+
| **No visibility** | Something broke. What was in that payload? Who knows. |
|
|
30
|
+
| **Can't replay** | Fixed the bug, but need to wait for a real event to test. |
|
|
31
|
+
|
|
32
|
+
**Traditional tools don't solve this:**
|
|
33
|
+
|
|
34
|
+
| Tool | Problem |
|
|
35
|
+
|------|---------|
|
|
36
|
+
| **ngrok** | URL changes on restart. Dev only. No history. |
|
|
37
|
+
| **localtunnel** | Unstable. No persistence. Dev only. |
|
|
38
|
+
| **RequestBin** | View-only. Can't forward or replay. |
|
|
39
|
+
| **Custom logging** | You built it. You maintain it. It's missing features. |
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## The Solution
|
|
44
|
+
|
|
45
|
+
HookTunnel is infrastructure between providers and your servers.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
WITHOUT WITH HOOKTUNNEL
|
|
49
|
+
|
|
50
|
+
Provider ────────▶ Your Server Provider ────▶ HookTunnel ────▶ Your Server
|
|
51
|
+
(if down, lost) (always on) (can be down)
|
|
52
|
+
✓ Captured ✓ Process later
|
|
53
|
+
✓ Logged ✓ Debug anytime
|
|
54
|
+
✓ Replayable ✓ Test fixes
|
|
55
|
+
✓ Stable URL ✓ Redeploy freely
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**What you get:**
|
|
59
|
+
|
|
60
|
+
- **Stable URLs** — Configure once in Stripe/Twilio/GitHub. Never change again.
|
|
61
|
+
- **Never lose webhooks** — Captured even when your server is down.
|
|
62
|
+
- **Full history** — See every request. Search by content. Debug with context.
|
|
63
|
+
- **Instant replay** — Re-send any webhook to test your fix. No waiting.
|
|
64
|
+
- **Local forwarding** — Forward to localhost for development.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Use Cases
|
|
69
|
+
|
|
70
|
+
### Development: Forward to Localhost
|
|
71
|
+
|
|
72
|
+
Test against real webhooks while building:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Terminal 1: Your server
|
|
76
|
+
npm run dev
|
|
77
|
+
|
|
78
|
+
# Terminal 2: Forward webhooks to localhost
|
|
79
|
+
hooktunnel connect dev 3000
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Every Stripe/Twilio webhook instantly hits your local machine.
|
|
83
|
+
|
|
84
|
+
### Production: Debug Incidents
|
|
85
|
+
|
|
86
|
+
2am. Payments are failing. What's in those webhooks?
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# See what's hitting your webhook endpoint
|
|
90
|
+
hooktunnel logs abc123 --limit 50
|
|
91
|
+
|
|
92
|
+
# Find the problematic request
|
|
93
|
+
# Note the log ID of the 500 error
|
|
94
|
+
|
|
95
|
+
# After fixing, replay to verify
|
|
96
|
+
hooktunnel replay log_xyz123 --to https://your-server.com/webhook
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Recovery: Server Was Down
|
|
100
|
+
|
|
101
|
+
Your server crashed for 10 minutes. Providers sent webhooks. They're not lost.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# See what came in while you were down
|
|
105
|
+
hooktunnel logs abc123 --limit 100
|
|
106
|
+
|
|
107
|
+
# Everything is there
|
|
108
|
+
# Process them now, or replay to your recovered server
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### AI-Assisted Debugging
|
|
112
|
+
|
|
113
|
+
Use with Claude Code for intelligent troubleshooting:
|
|
114
|
+
|
|
115
|
+
**Prompt:** *"Use hooktunnel to find recent webhook errors and explain what's wrong"*
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Claude runs:
|
|
119
|
+
hooktunnel logs abc123 --limit 20
|
|
120
|
+
|
|
121
|
+
# Analyzes the 500 errors
|
|
122
|
+
# Explains what payload caused failure
|
|
123
|
+
# Suggests fixes
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Prompt:** *"Replay the failed payment webhook and debug my handler"*
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Claude runs:
|
|
130
|
+
hooktunnel replay log_abc123 --to http://localhost:3000
|
|
131
|
+
|
|
132
|
+
# Shows the response
|
|
133
|
+
# Explains the error
|
|
134
|
+
# Helps you fix it
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Quick Start
|
|
140
|
+
|
|
141
|
+
### 1. Get Your Free Account
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Go to hooktunnel.com
|
|
145
|
+
# Sign up (or sign in with GitHub)
|
|
146
|
+
# Generate a webhook URL
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 2. Get Your API Key
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# Go to hooktunnel.com/app/settings
|
|
153
|
+
# Generate an API key
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 3. Connect
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Install and login
|
|
160
|
+
npx hooktunnel-cli login --key ht_your_api_key
|
|
161
|
+
|
|
162
|
+
# Start forwarding to localhost:3000
|
|
163
|
+
npx hooktunnel-cli connect dev 3000
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### 4. Configure Your Provider
|
|
167
|
+
|
|
168
|
+
Add your HookTunnel URL to Stripe/Twilio/GitHub webhook settings:
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
https://hooks.hooktunnel.com/h/your-hook-id
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Done.** Webhooks flow through HookTunnel to your server.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Installation
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# Use directly with npx (recommended)
|
|
182
|
+
npx hooktunnel-cli <command>
|
|
183
|
+
|
|
184
|
+
# Or install globally
|
|
185
|
+
npm install -g hooktunnel-cli
|
|
186
|
+
hooktunnel <command>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Requirements:** Node.js 18+
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Commands
|
|
194
|
+
|
|
195
|
+
### `hooktunnel login`
|
|
196
|
+
|
|
197
|
+
Authenticate with your API key.
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
hooktunnel login --key ht_abc123...
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Get your API key from [hooktunnel.com/app/settings](https://hooktunnel.com/app/settings)
|
|
204
|
+
|
|
205
|
+
### `hooktunnel connect <env> <port>`
|
|
206
|
+
|
|
207
|
+
Forward webhooks to your local server.
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# Basic usage
|
|
211
|
+
hooktunnel connect dev 3000
|
|
212
|
+
|
|
213
|
+
# Custom host
|
|
214
|
+
hooktunnel connect dev 3000 --host 127.0.0.1
|
|
215
|
+
|
|
216
|
+
# Verbose mode (shows all request details)
|
|
217
|
+
hooktunnel connect dev 3000 --verbose
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Output:**
|
|
221
|
+
```
|
|
222
|
+
🔗 HookTunnel
|
|
223
|
+
Environment: dev
|
|
224
|
+
Local port: 3000
|
|
225
|
+
|
|
226
|
+
✓ Connected to HookTunnel
|
|
227
|
+
Session: abc12345...
|
|
228
|
+
Forwarding to: http://localhost:3000
|
|
229
|
+
|
|
230
|
+
Waiting for webhooks... (Ctrl+C to stop)
|
|
231
|
+
|
|
232
|
+
[12:00:01] POST /webhook 200 (45ms)
|
|
233
|
+
[12:00:05] POST /webhook 500 (12ms)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Environments:**
|
|
237
|
+
- `dev` - Development
|
|
238
|
+
- `staging` - Staging
|
|
239
|
+
- `prod` - Production
|
|
240
|
+
|
|
241
|
+
### `hooktunnel hooks`
|
|
242
|
+
|
|
243
|
+
List your webhook endpoints.
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
hooktunnel hooks
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**Output:**
|
|
250
|
+
```
|
|
251
|
+
📌 Your Hooks (2)
|
|
252
|
+
|
|
253
|
+
ID Provider Status Requests
|
|
254
|
+
------------------------------------------------------------
|
|
255
|
+
abc123def456ghi789 stripe active 142
|
|
256
|
+
xyz789abc123def456 twilio active 57
|
|
257
|
+
|
|
258
|
+
Webhook URL: https://hooks.hooktunnel.com/h/<hook_id>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### `hooktunnel logs <hookId>`
|
|
262
|
+
|
|
263
|
+
View request history for a hook.
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
# Last 20 requests
|
|
267
|
+
hooktunnel logs abc123def456
|
|
268
|
+
|
|
269
|
+
# Last 100 requests
|
|
270
|
+
hooktunnel logs abc123def456 --limit 100
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Output:**
|
|
274
|
+
```
|
|
275
|
+
📋 Request Logs for abc123def456... (20)
|
|
276
|
+
|
|
277
|
+
Time Method Path Status Size
|
|
278
|
+
---------------------------------------------------------------------------
|
|
279
|
+
1/11/2026 12:00:05 POST /webhook 200 1.2KB
|
|
280
|
+
1/11/2026 11:58:32 POST /webhook 500 0.8KB
|
|
281
|
+
|
|
282
|
+
Log ID (for replay): log_abc123...
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### `hooktunnel replay <logId>` (Pro)
|
|
286
|
+
|
|
287
|
+
Re-send a captured webhook.
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
# Replay to connected tunnel
|
|
291
|
+
hooktunnel replay log_abc123
|
|
292
|
+
|
|
293
|
+
# Replay to specific URL (including production)
|
|
294
|
+
hooktunnel replay log_abc123 --to https://your-server.com/webhook
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### `hooktunnel status`
|
|
298
|
+
|
|
299
|
+
Check your connection and account status.
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
hooktunnel status
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### `hooktunnel logout`
|
|
306
|
+
|
|
307
|
+
Clear stored credentials.
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
hooktunnel logout
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## How It Works
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
|
319
|
+
│ │ │ │ │ │
|
|
320
|
+
│ Stripe/Twilio │────▶│ HookTunnel │────▶│ Your Server │
|
|
321
|
+
│ GitHub/etc │ │ (always on) │ │ (or CLI) │
|
|
322
|
+
│ │ │ │ │ │
|
|
323
|
+
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
|
324
|
+
│
|
|
325
|
+
▼
|
|
326
|
+
┌──────────────────┐
|
|
327
|
+
│ Request History │
|
|
328
|
+
│ • Full payloads │
|
|
329
|
+
│ • Searchable │
|
|
330
|
+
│ • Replayable │
|
|
331
|
+
└──────────────────┘
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
1. **Provider sends webhook** to your stable HookTunnel URL
|
|
335
|
+
2. **HookTunnel captures and logs** — stored even if your server is down
|
|
336
|
+
3. **Request forwarded** to your server (or CLI for local dev)
|
|
337
|
+
4. **Full history available** — debug, search, replay anytime
|
|
338
|
+
|
|
339
|
+
The URL never changes. No webhook is ever lost.
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Pricing
|
|
344
|
+
|
|
345
|
+
| Feature | Free | Pro ($19/mo) |
|
|
346
|
+
|---------|:----:|:------------:|
|
|
347
|
+
| Webhook URLs | 1 | 10 |
|
|
348
|
+
| Requests/day | 100 | Unlimited |
|
|
349
|
+
| History | 24 hours | 30 days |
|
|
350
|
+
| CLI Access | ✓ | ✓ |
|
|
351
|
+
| Request Replay | - | ✓ |
|
|
352
|
+
| Payload Storage | - | ✓ |
|
|
353
|
+
|
|
354
|
+
**Start free:** [hooktunnel.com](https://hooktunnel.com)
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Configuration
|
|
359
|
+
|
|
360
|
+
Credentials stored in:
|
|
361
|
+
- **Linux/macOS:** `~/.config/hooktunnel-cli/config.json`
|
|
362
|
+
- **Windows:** `%APPDATA%/hooktunnel-cli/config.json`
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Troubleshooting
|
|
367
|
+
|
|
368
|
+
### "Authentication required"
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
hooktunnel login --key <your-api-key>
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### "Connection failed"
|
|
375
|
+
|
|
376
|
+
1. Check internet connection
|
|
377
|
+
2. Verify API key: `hooktunnel status`
|
|
378
|
+
3. Try verbose mode: `hooktunnel connect dev 3000 --verbose`
|
|
379
|
+
|
|
380
|
+
### 502 errors in terminal
|
|
381
|
+
|
|
382
|
+
Your local server isn't responding:
|
|
383
|
+
1. Make sure it's running
|
|
384
|
+
2. Check the port number
|
|
385
|
+
3. Verify it's listening on localhost
|
|
386
|
+
|
|
387
|
+
### "Pro tier required"
|
|
388
|
+
|
|
389
|
+
Replay requires Pro. Upgrade at [hooktunnel.com/#pricing](https://hooktunnel.com/#pricing)
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## Links
|
|
394
|
+
|
|
395
|
+
- **Website:** [hooktunnel.com](https://hooktunnel.com)
|
|
396
|
+
- **Dashboard:** [hooktunnel.com/app](https://hooktunnel.com/app)
|
|
397
|
+
- **Documentation:** [hulupeep.github.io/hooktunnel-help](https://hulupeep.github.io/hooktunnel-help)
|
|
398
|
+
- **Issues:** [github.com/Hulupeep/hooktunnel-cli/issues](https://github.com/Hulupeep/hooktunnel-cli/issues)
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## License
|
|
403
|
+
|
|
404
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connect Command
|
|
3
|
+
* CLI-CMD-001: Connect command
|
|
4
|
+
*/
|
|
5
|
+
interface ConnectOptions {
|
|
6
|
+
verbose?: boolean;
|
|
7
|
+
host?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function connectCommand(env: string, port: string, options: ConnectOptions): Promise<void>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=connect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/commands/connect.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,UAAU,cAAc;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CAyDf"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connect Command
|
|
3
|
+
* CLI-CMD-001: Connect command
|
|
4
|
+
*/
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { getApiKey, isAuthenticated } from '../lib/config.js';
|
|
7
|
+
import { TunnelClient } from '../lib/tunnel.js';
|
|
8
|
+
import { CLIError } from '../lib/errors.js';
|
|
9
|
+
export async function connectCommand(env, port, options) {
|
|
10
|
+
// Validate auth
|
|
11
|
+
if (!isAuthenticated()) {
|
|
12
|
+
throw new CLIError('AUTH_REQUIRED');
|
|
13
|
+
}
|
|
14
|
+
// Validate environment
|
|
15
|
+
const validEnvs = ['dev', 'staging', 'prod'];
|
|
16
|
+
if (!validEnvs.includes(env)) {
|
|
17
|
+
console.log(chalk.red(`Invalid environment: ${env}`));
|
|
18
|
+
console.log(chalk.gray(`Valid environments: ${validEnvs.join(', ')}`));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
// Validate port
|
|
22
|
+
const portNum = parseInt(port, 10);
|
|
23
|
+
if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
|
24
|
+
throw new CLIError('INVALID_PORT');
|
|
25
|
+
}
|
|
26
|
+
const apiKey = getApiKey();
|
|
27
|
+
console.log(chalk.cyan('\n🔗 HookTunnel'));
|
|
28
|
+
console.log(chalk.gray(` Environment: ${env}`));
|
|
29
|
+
console.log(chalk.gray(` Local port: ${portNum}`));
|
|
30
|
+
if (options.host) {
|
|
31
|
+
console.log(chalk.gray(` Local host: ${options.host}`));
|
|
32
|
+
}
|
|
33
|
+
console.log();
|
|
34
|
+
const client = new TunnelClient({
|
|
35
|
+
env: env,
|
|
36
|
+
port: portNum,
|
|
37
|
+
host: options.host,
|
|
38
|
+
verbose: options.verbose,
|
|
39
|
+
apiKey,
|
|
40
|
+
});
|
|
41
|
+
// Handle graceful shutdown
|
|
42
|
+
const shutdown = () => {
|
|
43
|
+
console.log(chalk.yellow('\n\nDisconnecting...'));
|
|
44
|
+
client.disconnect();
|
|
45
|
+
process.exit(0);
|
|
46
|
+
};
|
|
47
|
+
process.on('SIGINT', shutdown);
|
|
48
|
+
process.on('SIGTERM', shutdown);
|
|
49
|
+
try {
|
|
50
|
+
await client.connect();
|
|
51
|
+
console.log(chalk.gray('Waiting for webhooks... (Ctrl+C to stop)\n'));
|
|
52
|
+
// Keep the process alive
|
|
53
|
+
await new Promise(() => { });
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=connect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/commands/connect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/commands/hooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAoDlD"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks Command
|
|
3
|
+
* CLI-CMD-003: Hooks command
|
|
4
|
+
*/
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import ora from 'ora';
|
|
7
|
+
import { isAuthenticated } from '../lib/config.js';
|
|
8
|
+
import { fetchHooks } from '../lib/api.js';
|
|
9
|
+
import { CLIError } from '../lib/errors.js';
|
|
10
|
+
export async function hooksCommand() {
|
|
11
|
+
if (!isAuthenticated()) {
|
|
12
|
+
throw new CLIError('AUTH_REQUIRED');
|
|
13
|
+
}
|
|
14
|
+
const spinner = ora('Fetching hooks...').start();
|
|
15
|
+
try {
|
|
16
|
+
const hooks = await fetchHooks();
|
|
17
|
+
spinner.stop();
|
|
18
|
+
if (hooks.length === 0) {
|
|
19
|
+
console.log(chalk.yellow('\nNo hooks found.'));
|
|
20
|
+
console.log(chalk.gray('Create one at https://hooktunnel.com/app\n'));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
console.log(chalk.cyan(`\n📌 Your Hooks (${hooks.length})\n`));
|
|
24
|
+
// Table header
|
|
25
|
+
console.log(chalk.gray(' ') +
|
|
26
|
+
chalk.gray('ID'.padEnd(24)) +
|
|
27
|
+
chalk.gray('Provider'.padEnd(12)) +
|
|
28
|
+
chalk.gray('Status'.padEnd(10)) +
|
|
29
|
+
chalk.gray('Requests'));
|
|
30
|
+
console.log(chalk.gray(' ' + '-'.repeat(60)));
|
|
31
|
+
for (const hook of hooks) {
|
|
32
|
+
const statusColor = hook.status === 'active' ? chalk.green : chalk.yellow;
|
|
33
|
+
const providerColor = hook.provider === 'stripe' ? chalk.magenta :
|
|
34
|
+
hook.provider === 'twilio' ? chalk.red :
|
|
35
|
+
chalk.blue;
|
|
36
|
+
console.log(' ' +
|
|
37
|
+
chalk.white(hook.hook_id.padEnd(24)) +
|
|
38
|
+
providerColor(hook.provider.padEnd(12)) +
|
|
39
|
+
statusColor(hook.status.padEnd(10)) +
|
|
40
|
+
chalk.gray(hook.request_count.toString()));
|
|
41
|
+
}
|
|
42
|
+
console.log();
|
|
43
|
+
console.log(chalk.gray(' Webhook URL: https://hooks.hooktunnel.com/h/<hook_id>'));
|
|
44
|
+
console.log(chalk.gray(' To forward: hooktunnel connect dev <port>\n'));
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
spinner.fail('Failed to fetch hooks');
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/commands/hooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,UAAU,YAAY;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BvE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Login Command
|
|
3
|
+
* CLI-CMD-002: Login command
|
|
4
|
+
*/
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import ora from 'ora';
|
|
7
|
+
import { setApiKey, getConfigPath } from '../lib/config.js';
|
|
8
|
+
import { validateApiKey } from '../lib/api.js';
|
|
9
|
+
import { CLIError } from '../lib/errors.js';
|
|
10
|
+
export async function loginCommand(options) {
|
|
11
|
+
const apiKey = options.key;
|
|
12
|
+
if (!apiKey) {
|
|
13
|
+
console.log(chalk.yellow('\nTo get an API key:'));
|
|
14
|
+
console.log(chalk.gray(' 1. Go to https://hooktunnel.com/app/settings'));
|
|
15
|
+
console.log(chalk.gray(' 2. Generate an API key'));
|
|
16
|
+
console.log(chalk.gray(' 3. Run: hooktunnel login --key <your-api-key>\n'));
|
|
17
|
+
throw new CLIError('AUTH_REQUIRED', 'API key is required');
|
|
18
|
+
}
|
|
19
|
+
const spinner = ora('Validating API key...').start();
|
|
20
|
+
try {
|
|
21
|
+
const isValid = await validateApiKey(apiKey);
|
|
22
|
+
if (!isValid) {
|
|
23
|
+
spinner.fail('Invalid API key');
|
|
24
|
+
throw new CLIError('AUTH_INVALID');
|
|
25
|
+
}
|
|
26
|
+
setApiKey(apiKey);
|
|
27
|
+
spinner.succeed('Logged in successfully');
|
|
28
|
+
console.log(chalk.gray(`\nCredentials saved to: ${getConfigPath()}`));
|
|
29
|
+
console.log(chalk.gray('You can now run: hooktunnel connect dev <port>\n'));
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
spinner.fail('Login failed');
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAM5C,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAqB;IACtD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAE3B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC;QAClD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC,CAAC;QAC7E,MAAM,IAAI,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,uBAAuB,CAAC,CAAC,KAAK,EAAE,CAAC;IAErD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;QAE7C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YAChC,MAAM,IAAI,QAAQ,CAAC,cAAc,CAAC,CAAC;QACrC,CAAC;QAED,SAAS,CAAC,MAAM,CAAC,CAAC;QAClB,OAAO,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;QAE1C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,2BAA2B,aAAa,EAAE,EAAE,CAAC,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC,CAAC;IAC9E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC7B,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CASnD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logout Command
|
|
3
|
+
* CLI-CMD-007: Logout command
|
|
4
|
+
*/
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { clearApiKey, isAuthenticated } from '../lib/config.js';
|
|
7
|
+
export async function logoutCommand() {
|
|
8
|
+
if (!isAuthenticated()) {
|
|
9
|
+
console.log(chalk.yellow('Not logged in'));
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
clearApiKey();
|
|
13
|
+
console.log(chalk.green('✓ Logged out successfully'));
|
|
14
|
+
console.log(chalk.gray(' Your API key has been removed from this device\n'));
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=logout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEhE,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC;QAC3C,OAAO;IACT,CAAC;IAED,WAAW,EAAE,CAAC;IACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAC;IACtD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC,CAAC;AAChF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAqErF"}
|