http-mitm-proxy-ui 0.1.1

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 ADDED
@@ -0,0 +1,359 @@
1
+ # http-mitm-proxy-ui
2
+
3
+ A self-contained Node.js package that bundles `http-mitm-proxy` with a modern web-based UI for inspecting HTTP/HTTPS traffic in real time.
4
+
5
+ ![http-mitm-proxy-ui screenshot](docs/images/screenshot.png)
6
+
7
+ ## Features
8
+
9
+ - **Real-time Traffic Inspection**: Live stream of HTTP/HTTPS requests and responses with WebSocket updates
10
+ - **Complete Request/Response Details**: Headers, bodies, cookies, query parameters with syntax highlighting
11
+ - **Advanced Filtering & Search**: Filter by domain, method, status code, content type with full-text search
12
+ - **Export Data**: Export traffic as JSON or CSV for offline analysis and sharing
13
+ - **HTTPS Interception**: Auto-generates and manages SSL certificates with easy CA download
14
+ - **Standalone CLI**: Single command to start both proxy and UI with configurable options
15
+ - **REST API**: Programmatic access to traffic data and configuration
16
+ - **Material Design UI**: Clean, responsive interface with red/black on white theme
17
+
18
+ ## Guides
19
+
20
+ - 📖 **[User Guide](docs/guides/USER-GUIDE.md)** — Complete documentation with screenshots
21
+ - 🔍 **[Qwen Code Use Cases](docs/guides/use-cases-qwen-code.md)** — Inspect Qwen Code API traffic with examples
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install http-mitm-proxy-ui
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### CLI Usage
32
+
33
+ Start with default ports (proxy: 8080, UI: 3000):
34
+
35
+ ```bash
36
+ npx http-mitm-proxy-ui
37
+ ```
38
+
39
+ Custom configuration:
40
+
41
+ ```bash
42
+ http-mitm-proxy-ui --proxy-port 9090 --ui-port 4000 --ssl-ca-dir ./my-certs
43
+ ```
44
+
45
+ Available options:
46
+ - `-p, --proxy-port <port>`: MITM proxy server port (default: 8080)
47
+ - `-u, --ui-port <port>`: Web UI server port (default: 3000)
48
+ - `-H, --headless`: Run in proxy-only mode (no UI)
49
+ - `-c, --config <path>`: Path to JSON config file
50
+ - `--ssl-ca-dir <path>`: Directory for SSL CA certificates
51
+ - `--ca-cert <path>`: Path to custom CA certificate file (.pem)
52
+ - `--ca-key <path>`: Path to custom CA private key file (.pem)
53
+ - `--max-requests <count>`: Max requests to keep in memory (default: 1000)
54
+ - `-h, --help`: Display help
55
+
56
+ ### Using Custom CA Certificates
57
+
58
+ If you already have a CA certificate and key (e.g., from a corporate PKI or a previous proxy setup), you can use them instead of auto-generating:
59
+
60
+ ```bash
61
+ http-mitm-proxy-ui --ca-cert /path/to/ca.pem --ca-key /path/to/ca.key
62
+ ```
63
+
64
+ Both `--ca-cert` and `--ca-key` must be provided together. The files are copied into the `--ssl-ca-dir` (default: `~/.http-mitm-proxy-ui/ca`) in the format expected by the proxy. When omitted, a new CA is auto-generated on first run.
65
+
66
+ You can also set these in a JSON config file:
67
+
68
+ ```json
69
+ {
70
+ "caCertPath": "/path/to/ca.pem",
71
+ "caKeyPath": "/path/to/ca.key"
72
+ }
73
+ ```
74
+
75
+ ### Programmatic Usage
76
+
77
+ ```typescript
78
+ import { createProxyUI } from 'http-mitm-proxy-ui';
79
+
80
+ const proxy = createProxyUI({
81
+ proxyPort: 8080,
82
+ uiPort: 3000,
83
+ });
84
+
85
+ proxy.on('request', (req) => {
86
+ console.log('Request:', req.method, req.url);
87
+ });
88
+
89
+ proxy.on('response', (req) => {
90
+ console.log('Response:', req.response?.statusCode);
91
+ });
92
+
93
+ await proxy.start();
94
+ ```
95
+
96
+ ## Architecture
97
+
98
+ ```
99
+ ┌─────────────────────────────────────────────┐
100
+ │ http-mitm-proxy-ui │
101
+ ├──────────────────┬──────────────────────────┤
102
+ │ MITM Proxy │ Web UI Server │
103
+ │ (http-mitm- │ (Express + Vue 3) │
104
+ │ proxy core) │ │
105
+ │ - Intercept │ - WebSocket for │
106
+ │ HTTP/HTTPS │ real-time updates │
107
+ │ - Capture & │ - REST API for │
108
+ │ emit events │ history/query │
109
+ │ │ - Static file serving │
110
+ └────────┬─────────┴────────────┬─────────────┘
111
+ │ │
112
+ ▼ ▼
113
+ Network Traffic Browser (localhost:3000)
114
+ (localhost:8080)
115
+ ```
116
+
117
+ ## API Endpoints
118
+
119
+ When the UI server is running (not headless):
120
+
121
+ - `GET /api/health` - Health check
122
+ - `GET /api/requests` - List requests with filtering/pagination
123
+ - `GET /api/requests/:id` - Get specific request details
124
+ - `DELETE /api/requests` - Clear request history
125
+ - `GET /api/config` - Get current configuration
126
+ - `GET /api/ca-cert` - Download CA certificate for trust installation
127
+ - `GET /*` - Serves the Vue SPA (all other routes)
128
+
129
+ ## WebSocket Events
130
+
131
+ Connect to `ws://localhost:3000/ws` for real-time updates:
132
+
133
+ - `{ type: 'init', data: [...] }` - Initial state on connection
134
+ - `{ type: 'request', data: RequestRecord }` - New request captured
135
+ - `{ type: 'response', data: RequestRecord }` - Response received
136
+ - `{ type: 'error', data: { message: string } }` - Error occurred
137
+ - `{ type: 'clear', data: {} }` - History cleared
138
+
139
+ ## Configuration
140
+
141
+ Create a `proxy-config.json` file:
142
+
143
+ ```json
144
+ {
145
+ "proxyPort": 8080,
146
+ "uiPort": 3000,
147
+ "sslCaDir": "./certs",
148
+ "maxRequests": 1000,
149
+ "headless": false
150
+ }
151
+ ```
152
+
153
+ Then start with: `http-mitm-proxy-ui --config ./proxy-config.json`
154
+
155
+ ## Development
156
+
157
+ ```bash
158
+ # Install dependencies
159
+ npm install
160
+
161
+ # Build the package
162
+ npm run build
163
+
164
+ # Start in development mode (watches for changes)
165
+ npm run dev
166
+
167
+ # Run tests
168
+ npm test
169
+ ```
170
+
171
+ ## Use Cases
172
+
173
+ ### Debugging API Calls
174
+ 1. Start the proxy: `http-mitm-proxy-ui`
175
+ 2. Configure your application to use `localhost:8080` as HTTP/HTTPS proxy
176
+ 3. Visit `http://localhost:3000` to see live traffic
177
+ 4. Use filtering to isolate specific endpoints
178
+ 5. Inspect full request/response details including headers, bodies, and timing
179
+
180
+ ### Mobile App Testing
181
+ 1. Start the proxy on your development machine
182
+ 2. Configure your mobile device to use your machine's IP:8080 as proxy
183
+ 3. Install the CA certificate on your device (download from `http://YOUR_IP:3000/api/ca-cert`)
184
+ 4. Inspect mobile app network calls with full visibility
185
+
186
+ ### Security Testing
187
+ 1. Capture and inspect all traffic for unexpected requests or data leaks
188
+ 2. Verify authorization headers and sensitive data in transit
189
+ 3. Export captured traffic as JSON or CSV for team analysis
190
+
191
+ ## Configuring Existing Applications to Use the Proxy
192
+
193
+ The primary purpose of this tool is to **intercept and inspect traffic from existing applications without modifying their code**. You do this by setting proxy environment variables or command-line flags.
194
+
195
+ ### Step 1: Install the CA Certificate (for HTTPS)
196
+
197
+ Before intercepting HTTPS traffic, you must trust the proxy's CA certificate:
198
+
199
+ ```bash
200
+ # Start the proxy once to generate the CA
201
+ http-mitm-proxy-ui
202
+
203
+ # Download the CA certificate
204
+ curl http://localhost:3000/api/ca-cert -o http-mitm-proxy-ca.pem
205
+
206
+ # On macOS, add to Keychain and trust it:
207
+ security add-trusted-cert -d -r trustRoot -k ~/Library/Keychains/login.keychain http-mitm-proxy-ca.pem
208
+
209
+ # On Linux (Debian/Ubuntu):
210
+ sudo cp http-mitm-proxy-ca.pem /usr/local/share/ca-certificates/
211
+ sudo update-ca-certificates
212
+ ```
213
+
214
+ ### Step 2: Configure Your Application
215
+
216
+ #### Node.js Applications
217
+
218
+ Node.js respects standard proxy environment variables. Start your app with:
219
+
220
+ ```bash
221
+ # For HTTP and HTTPS traffic
222
+ HTTP_PROXY=http://localhost:8080 HTTPS_PROXY=http://localhost:8080 node your-app.js
223
+
224
+ # Or if using npm scripts
225
+ HTTP_PROXY=http://localhost:8080 HTTPS_PROXY=http://localhost:8080 npm start
226
+
227
+ # For apps that use the `proxy` env var (some libraries)
228
+ proxy=http://localhost:8080 node your-app.js
229
+ ```
230
+
231
+ **Note**: Some HTTP clients in Node.js (like `axios`, `node-fetch`) don't automatically respect env vars. For those, you may need to pass an `agent` with proxy support, or use `global-agent`:
232
+
233
+ ```bash
234
+ # Force all Node.js HTTP clients through the proxy
235
+ npx global-agent bootstrap -- node your-app.js
236
+ ```
237
+
238
+ #### curl / wget
239
+
240
+ ```bash
241
+ # curl with proxy
242
+ curl --proxy http://localhost:8080 https://api.example.com/data
243
+
244
+ # curl with explicit HTTPS proxy (for HTTPS targets)
245
+ curl --proxy http://localhost:8080 --proxy-insecure https://api.example.com/data
246
+
247
+ # wget with proxy
248
+ export http_proxy=http://localhost:8080
249
+ export https_proxy=http://localhost:8080
250
+ wget https://api.example.com/data
251
+ ```
252
+
253
+ #### Java Applications
254
+
255
+ Java uses system properties for proxy configuration. Pass them via `-D` flags:
256
+
257
+ ```bash
258
+ # Basic HTTP proxy
259
+ java -Dhttp.proxyHost=localhost -Dhttp.proxyPort=8080 \
260
+ -Dhttps.proxyHost=localhost -Dhttps.proxyPort=8080 \
261
+ -jar your-app.jar
262
+
263
+ # For Spring Boot apps
264
+ java -Dhttp.proxyHost=localhost -Dhttp.proxyPort=8080 \
265
+ -Dhttps.proxyHost=localhost -Dhttps.proxyPort=8080 \
266
+ -Dhttp.nonProxyHosts="localhost|127.0.0.1" \
267
+ -jar your-app.jar
268
+
269
+ # For Maven-based apps
270
+ mvn -Dhttp.proxyHost=localhost -Dhttp.proxyPort=8080 \
271
+ -Dhttps.proxyHost=localhost -Dhttps.proxyPort=8080 \
272
+ spring-boot:run
273
+
274
+ # For Gradle-based apps
275
+ gradle -Dorg.gradle.jvmargs="-Dhttp.proxyHost=localhost -Dhttp.proxyPort=8080 -Dhttps.proxyHost=localhost -Dhttps.proxyPort=8080" bootRun
276
+ ```
277
+
278
+ **Note**: Java's default HTTP client may not intercept all traffic (e.g., OkHttp, Apache HttpClient may need separate proxy config). See [Java-specific notes below](#java-specific-http-clients).
279
+
280
+ #### Python Applications
281
+
282
+ ```bash
283
+ # Standard library (urllib, requests with env var support)
284
+ HTTP_PROXY=http://localhost:8080 HTTPS_PROXY=http://localhost:8080 python your-app.py
285
+
286
+ # For pip itself (debugging package installs)
287
+ HTTP_PROXY=http://localhost:8080 HTTPS_PROXY=http://localhost:8080 pip install requests
288
+ ```
289
+
290
+ #### Docker Containers
291
+
292
+ ```bash
293
+ # Pass proxy env vars to container
294
+ docker run -e HTTP_PROXY=http://host.docker.internal:8080 \
295
+ -e HTTPS_PROXY=http://host.docker.internal:8080 \
296
+ your-image
297
+
298
+ # Or in docker-compose.yml:
299
+ # services:
300
+ # app:
301
+ # environment:
302
+ # - HTTP_PROXY=http://host.docker.internal:8080
303
+ # - HTTPS_PROXY=http://host.docker.internal:8080
304
+ ```
305
+
306
+ **Note**: On macOS/Windows, use `host.docker.internal` instead of `localhost` to reach the host machine from inside a container.
307
+
308
+ #### React / Angular / Vue Dev Servers
309
+
310
+ ```bash
311
+ # React (Create React App)
312
+ HTTP_PROXY=http://localhost:8080 npm start
313
+
314
+ # Angular
315
+ HTTP_PROXY=http://localhost:8080 ng serve
316
+
317
+ # Vite
318
+ HTTP_PROXY=http://localhost:8080 npm run dev
319
+ ```
320
+
321
+ ### Java-Specific HTTP Clients
322
+
323
+ Some Java HTTP libraries don't respect the `-D` system properties above. Configure them individually:
324
+
325
+ **OkHttp:**
326
+ ```java
327
+ Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", 8080));
328
+ OkHttpClient client = new OkHttpClient.Builder()
329
+ .proxy(proxy)
330
+ .build();
331
+ ```
332
+
333
+ **Apache HttpClient:**
334
+ ```java
335
+ HttpHost proxy = new HttpHost("localhost", 8080, "http");
336
+ RequestConfig config = RequestConfig.custom()
337
+ .setProxy(proxy)
338
+ .build();
339
+ CloseableHttpClient client = HttpClients.custom()
340
+ .setDefaultRequestConfig(config)
341
+ .build();
342
+ ```
343
+
344
+ **Java 11+ HttpClient:**
345
+ ```java
346
+ HttpClient client = HttpClient.newBuilder()
347
+ .proxy(ProxySelector.of(new InetSocketAddress("localhost", 8080)))
348
+ .build();
349
+ ```
350
+
351
+ ## License
352
+
353
+ ISC
354
+
355
+ ## Author & Credits
356
+
357
+ - **Dung Tran** ([dungviettran89](https://github.com/dungviettran89))
358
+ - Built with [OpenClaw](https://github.com/dungviettran89/openclaw) — an AI agent framework for autonomous development
359
+ - Code written with assistance from [Qwen Coder](https://qwenlm.github.io/) — an AI coding assistant
@@ -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,213 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const commander_1 = require("commander");
38
+ const path = __importStar(require("path"));
39
+ const os = __importStar(require("os"));
40
+ const fs = __importStar(require("fs"));
41
+ const proxy_1 = require("./proxy");
42
+ const server_1 = require("./ui/server");
43
+ // node-forge has no @types, but it's already a dependency and we only use it for public key extraction
44
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-explicit-any
45
+ const nodeForge = require('node-forge');
46
+ const DEFAULT_PROXY_PORT = 8080;
47
+ const DEFAULT_UI_PORT = 3000;
48
+ const DEFAULT_SSL_CA_DIR = path.join(os.homedir(), '.http-mitm-proxy-ui', 'ca');
49
+ const DEFAULT_MAX_REQUESTS = 1000;
50
+ async function loadConfigFile(configPath) {
51
+ if (!configPath)
52
+ return {};
53
+ try {
54
+ const { default: fs } = await Promise.resolve().then(() => __importStar(require('fs')));
55
+ const raw = fs.readFileSync(configPath, 'utf-8');
56
+ return JSON.parse(raw);
57
+ }
58
+ catch (err) {
59
+ console.error(`Warning: Could not load config file "${configPath}": ${err.message}`);
60
+ return {};
61
+ }
62
+ }
63
+ /**
64
+ * If custom CA cert/key are provided, copy them to the expected locations
65
+ * in sslCaDir so http-mitm-proxy's CA class loads them instead of generating.
66
+ * The CA class expects three files: certs/ca.pem, keys/ca.private.key, keys/ca.public.key
67
+ */
68
+ async function setupCaFiles(caCertPath, caKeyPath, sslCaDir) {
69
+ const certsDir = path.join(sslCaDir, 'certs');
70
+ const keysDir = path.join(sslCaDir, 'keys');
71
+ // Ensure directories exist
72
+ fs.mkdirSync(certsDir, { recursive: true });
73
+ fs.mkdirSync(keysDir, { recursive: true });
74
+ // Copy cert and private key to expected locations
75
+ const destCert = path.join(certsDir, 'ca.pem');
76
+ const destPrivateKey = path.join(keysDir, 'ca.private.key');
77
+ const destPublicKey = path.join(keysDir, 'ca.public.key');
78
+ fs.copyFileSync(caCertPath, destCert);
79
+ fs.copyFileSync(caKeyPath, destPrivateKey);
80
+ // Extract public key from the private key using node-forge (already a dependency)
81
+ const pki = nodeForge.pki;
82
+ const privateKey = pki.privateKeyFromPem(fs.readFileSync(caKeyPath, 'utf-8'));
83
+ const publicKey = pki.setRsaPublicKey(privateKey.n, privateKey.e);
84
+ fs.writeFileSync(destPublicKey, pki.publicKeyToPem(publicKey));
85
+ console.log(` CA cert: ${caCertPath} → ${destCert}`);
86
+ console.log(` CA key: ${caKeyPath} → ${destPrivateKey}`);
87
+ console.log(` CA pub: (derived) → ${destPublicKey}`);
88
+ }
89
+ async function main() {
90
+ const program = new commander_1.Command();
91
+ program
92
+ .name('http-mitm-proxy-ui')
93
+ .description('HTTP MITM Proxy with a web-based UI for inspecting and modifying traffic')
94
+ .version('1.0.0')
95
+ .option('-p, --proxy-port <port>', 'Port for the MITM proxy server', String(DEFAULT_PROXY_PORT))
96
+ .option('-u, --ui-port <port>', 'Port for the web UI server', String(DEFAULT_UI_PORT))
97
+ .option('-H, --headless', 'Run in headless mode (proxy only, no UI)', false)
98
+ .option('-c, --config <path>', 'Path to a JSON config file')
99
+ .option('--ssl-ca-dir <path>', 'Directory for storing CA certificates')
100
+ .option('--max-requests <count>', 'Maximum number of requests to keep in memory', String(DEFAULT_MAX_REQUESTS))
101
+ .option('--no-modification', 'Disable request/response modification features')
102
+ .option('--ca-cert <path>', 'Path to custom CA certificate file (.pem)')
103
+ .option('--ca-key <path>', 'Path to custom CA private key file (.pem)')
104
+ .parse(process.argv);
105
+ const opts = program.opts();
106
+ // Load config file if provided
107
+ const fileConfig = await loadConfigFile(opts.config);
108
+ // Merge: defaults < file config < CLI flags
109
+ const config = {
110
+ proxyPort: parseInt(fileConfig.proxyPort != null ? String(fileConfig.proxyPort) : opts.proxyPort, 10),
111
+ uiPort: parseInt(fileConfig.uiPort != null ? String(fileConfig.uiPort) : opts.uiPort, 10),
112
+ headless: fileConfig.headless != null ? Boolean(fileConfig.headless) : Boolean(opts.headless),
113
+ sslCaDir: fileConfig.sslCaDir || opts.sslCaDir || DEFAULT_SSL_CA_DIR,
114
+ maxRequests: parseInt(fileConfig.maxRequests != null ? String(fileConfig.maxRequests) : opts.maxRequests, 10),
115
+ enableModification: fileConfig.enableModification != null
116
+ ? Boolean(fileConfig.enableModification)
117
+ : opts.enableModification !== false,
118
+ caCertPath: fileConfig.caCertPath || opts.caCert,
119
+ caKeyPath: fileConfig.caKeyPath || opts.caKey,
120
+ };
121
+ // Validate CA cert/key: both or neither must be provided
122
+ if ((config.caCertPath && !config.caKeyPath) || (!config.caCertPath && config.caKeyPath)) {
123
+ console.error('Error: --ca-cert and --ca-key must be provided together.');
124
+ process.exit(1);
125
+ }
126
+ // Validate CA cert/key files exist
127
+ if (config.caCertPath && !fs.existsSync(config.caCertPath)) {
128
+ console.error(`Error: CA certificate file not found: ${config.caCertPath}`);
129
+ process.exit(1);
130
+ }
131
+ if (config.caKeyPath && !fs.existsSync(config.caKeyPath)) {
132
+ console.error(`Error: CA private key file not found: ${config.caKeyPath}`);
133
+ process.exit(1);
134
+ }
135
+ // Validate ports
136
+ if (config.proxyPort < 1 || config.proxyPort > 65535) {
137
+ console.error(`Error: Invalid proxy port ${config.proxyPort}. Must be between 1 and 65535.`);
138
+ process.exit(1);
139
+ }
140
+ if (config.uiPort < 1 || config.uiPort > 65535) {
141
+ console.error(`Error: Invalid UI port ${config.uiPort}. Must be between 1 and 65535.`);
142
+ process.exit(1);
143
+ }
144
+ if (config.proxyPort === config.uiPort) {
145
+ console.error('Error: Proxy port and UI port must be different.');
146
+ process.exit(1);
147
+ }
148
+ const proxyConfig = {
149
+ proxyPort: config.proxyPort,
150
+ uiPort: config.uiPort,
151
+ sslCaDir: config.sslCaDir,
152
+ maxRequests: config.maxRequests,
153
+ enableModification: config.enableModification,
154
+ headless: config.headless,
155
+ caCertPath: config.caCertPath,
156
+ caKeyPath: config.caKeyPath,
157
+ };
158
+ console.log('Starting http-mitm-proxy-ui...');
159
+ console.log(` Proxy: localhost:${config.proxyPort}`);
160
+ if (!config.headless) {
161
+ console.log(` UI: http://localhost:${config.uiPort}`);
162
+ }
163
+ console.log(` Headless: ${config.headless}`);
164
+ if (config.caCertPath && config.caKeyPath) {
165
+ console.log(` Custom CA: yes`);
166
+ await setupCaFiles(config.caCertPath, config.caKeyPath, config.sslCaDir);
167
+ }
168
+ else {
169
+ console.log(` Custom CA: no (auto-generate)`);
170
+ }
171
+ console.log('');
172
+ // Start the proxy
173
+ const proxy = new proxy_1.MitmProxy(proxyConfig);
174
+ proxy.on('error', (err) => {
175
+ console.error('Proxy error:', err.message);
176
+ });
177
+ try {
178
+ await proxy.start();
179
+ }
180
+ catch (err) {
181
+ console.error(`Failed to start proxy: ${err.message}`);
182
+ process.exit(1);
183
+ }
184
+ // Start the UI (unless in headless mode)
185
+ let uiServer = null;
186
+ if (!config.headless) {
187
+ uiServer = new server_1.UIServer(proxy, proxyConfig);
188
+ try {
189
+ await uiServer.start();
190
+ }
191
+ catch (err) {
192
+ console.error(`Failed to start UI server: ${err.message}`);
193
+ proxy.stop();
194
+ process.exit(1);
195
+ }
196
+ }
197
+ // Graceful shutdown
198
+ const shutdown = (signal) => {
199
+ console.log(`\nReceived ${signal}. Shutting down...`);
200
+ if (uiServer) {
201
+ uiServer.stop();
202
+ }
203
+ proxy.stop();
204
+ process.exit(0);
205
+ };
206
+ process.on('SIGINT', () => shutdown('SIGINT'));
207
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
208
+ }
209
+ main().catch((err) => {
210
+ console.error('Fatal error:', err);
211
+ process.exit(1);
212
+ });
213
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { RequestRecord } from '.';
2
+ /**
3
+ * Persistent request store backed by a local JSON file via lowdb.
4
+ */
5
+ export declare class RequestStore {
6
+ private db;
7
+ private filePath;
8
+ constructor(dbPath?: string);
9
+ init(): Promise<this>;
10
+ /**
11
+ * Add a new request record to the store.
12
+ */
13
+ add(req: RequestRecord): Promise<void>;
14
+ /**
15
+ * Update an existing request record (merge by id).
16
+ */
17
+ update(req: RequestRecord): Promise<void>;
18
+ /**
19
+ * Get all requests, converted back to live form.
20
+ */
21
+ getAll(): RequestRecord[];
22
+ /**
23
+ * Get a single request by id.
24
+ */
25
+ getById(id: string): RequestRecord | undefined;
26
+ /**
27
+ * Clear all stored requests.
28
+ */
29
+ clear(): Promise<void>;
30
+ }
31
+ //# sourceMappingURL=RequestStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RequestStore.d.ts","sourceRoot":"","sources":["../../src/proxy/RequestStore.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,GAAG,CAAA;AA+GtD;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,EAAE,CAAuD;IACjE,OAAO,CAAC,QAAQ,CAAQ;gBAEZ,MAAM,CAAC,EAAE,MAAM;IAIrB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAO5C;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAU/C;;OAEG;IACH,MAAM,IAAI,aAAa,EAAE;IAIzB;;OAEG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAK9C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B"}