@rvanbaalen/roxyproxy 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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +658 -0
  3. package/dist/cli/banner.d.ts +6 -0
  4. package/dist/cli/banner.js +30 -0
  5. package/dist/cli/banner.js.map +1 -0
  6. package/dist/cli/commands/clear.d.ts +2 -0
  7. package/dist/cli/commands/clear.js +35 -0
  8. package/dist/cli/commands/clear.js.map +1 -0
  9. package/dist/cli/commands/proxy-off.d.ts +2 -0
  10. package/dist/cli/commands/proxy-off.js +19 -0
  11. package/dist/cli/commands/proxy-off.js.map +1 -0
  12. package/dist/cli/commands/proxy-on.d.ts +2 -0
  13. package/dist/cli/commands/proxy-on.js +20 -0
  14. package/dist/cli/commands/proxy-on.js.map +1 -0
  15. package/dist/cli/commands/request.d.ts +2 -0
  16. package/dist/cli/commands/request.js +23 -0
  17. package/dist/cli/commands/request.js.map +1 -0
  18. package/dist/cli/commands/requests.d.ts +2 -0
  19. package/dist/cli/commands/requests.js +46 -0
  20. package/dist/cli/commands/requests.js.map +1 -0
  21. package/dist/cli/commands/start.d.ts +2 -0
  22. package/dist/cli/commands/start.js +41 -0
  23. package/dist/cli/commands/start.js.map +1 -0
  24. package/dist/cli/commands/status.d.ts +2 -0
  25. package/dist/cli/commands/status.js +44 -0
  26. package/dist/cli/commands/status.js.map +1 -0
  27. package/dist/cli/commands/stop.d.ts +2 -0
  28. package/dist/cli/commands/stop.js +52 -0
  29. package/dist/cli/commands/stop.js.map +1 -0
  30. package/dist/cli/commands/trust-ca.d.ts +2 -0
  31. package/dist/cli/commands/trust-ca.js +134 -0
  32. package/dist/cli/commands/trust-ca.js.map +1 -0
  33. package/dist/cli/format.d.ts +3 -0
  34. package/dist/cli/format.js +112 -0
  35. package/dist/cli/format.js.map +1 -0
  36. package/dist/cli/index.d.ts +2 -0
  37. package/dist/cli/index.js +35 -0
  38. package/dist/cli/index.js.map +1 -0
  39. package/dist/cli/interactive.d.ts +1 -0
  40. package/dist/cli/interactive.js +408 -0
  41. package/dist/cli/interactive.js.map +1 -0
  42. package/dist/cli/system-proxy.d.ts +14 -0
  43. package/dist/cli/system-proxy.js +115 -0
  44. package/dist/cli/system-proxy.js.map +1 -0
  45. package/dist/server/api.d.ts +10 -0
  46. package/dist/server/api.js +109 -0
  47. package/dist/server/api.js.map +1 -0
  48. package/dist/server/config.d.ts +6 -0
  49. package/dist/server/config.js +66 -0
  50. package/dist/server/config.js.map +1 -0
  51. package/dist/server/events.d.ts +20 -0
  52. package/dist/server/events.js +49 -0
  53. package/dist/server/events.js.map +1 -0
  54. package/dist/server/index.d.ts +20 -0
  55. package/dist/server/index.js +91 -0
  56. package/dist/server/index.js.map +1 -0
  57. package/dist/server/proxy.d.ts +27 -0
  58. package/dist/server/proxy.js +245 -0
  59. package/dist/server/proxy.js.map +1 -0
  60. package/dist/server/ssl.d.ts +17 -0
  61. package/dist/server/ssl.js +88 -0
  62. package/dist/server/ssl.js.map +1 -0
  63. package/dist/shared/types.d.ts +53 -0
  64. package/dist/shared/types.js +10 -0
  65. package/dist/shared/types.js.map +1 -0
  66. package/dist/storage/cleanup.d.ts +11 -0
  67. package/dist/storage/cleanup.js +32 -0
  68. package/dist/storage/cleanup.js.map +1 -0
  69. package/dist/storage/db.d.ts +17 -0
  70. package/dist/storage/db.js +152 -0
  71. package/dist/storage/db.js.map +1 -0
  72. package/dist/ui/assets/index-C8qts74N.js +9 -0
  73. package/dist/ui/assets/index-CrfXQK5U.css +2 -0
  74. package/dist/ui/index.html +13 -0
  75. package/package.json +59 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"banner.js","sourceRoot":"","sources":["../../src/cli/banner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,UAAU,WAAW;IACzB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC,CAAC;IACrE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC,CAAC;IACtE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC,CAAC;IAC3E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC,CAAC;IAC7E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC,CAAC;IACrE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,MAAc;IAC9D,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,oBAAoB,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;IACvF,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,oBAAoB,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;IACpF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerClear(program: Command): void;
@@ -0,0 +1,35 @@
1
+ import http from 'node:http';
2
+ import { printSuccess, printError } from '../banner.js';
3
+ export function registerClear(program) {
4
+ program
5
+ .command('clear')
6
+ .description('Clear all captured traffic')
7
+ .option('--ui-port <number>', 'UI/API port', '8081')
8
+ .action(async (opts) => {
9
+ const port = parseInt(opts.uiPort, 10);
10
+ try {
11
+ await apiDelete(port, '/api/requests');
12
+ printSuccess('All traffic cleared.');
13
+ }
14
+ catch {
15
+ printError('Could not clear. Is the proxy running?');
16
+ process.exit(1);
17
+ }
18
+ });
19
+ }
20
+ function apiDelete(port, urlPath) {
21
+ return new Promise((resolve, reject) => {
22
+ const req = http.request({ host: '127.0.0.1', port, path: urlPath, method: 'DELETE' }, (res) => {
23
+ res.resume();
24
+ res.on('end', () => {
25
+ if (res.statusCode === 200)
26
+ resolve();
27
+ else
28
+ reject(new Error(`HTTP ${res.statusCode}`));
29
+ });
30
+ });
31
+ req.on('error', reject);
32
+ req.end();
33
+ });
34
+ }
35
+ //# sourceMappingURL=clear.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clear.js","sourceRoot":"","sources":["../../../src/cli/commands/clear.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAExD,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC5C,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,4BAA4B,CAAC;SACzC,MAAM,CAAC,oBAAoB,EAAE,aAAa,EAAE,MAAM,CAAC;SACnD,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACvC,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;YACvC,YAAY,CAAC,sBAAsB,CAAC,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,UAAU,CAAC,wCAAwC,CAAC,CAAC;YACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,SAAS,CAAC,IAAY,EAAE,OAAe;IAC9C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE;YAC7F,GAAG,CAAC,MAAM,EAAE,CAAC;YACb,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACjB,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG;oBAAE,OAAO,EAAE,CAAC;;oBACjC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;YACnD,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACxB,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerProxyOff(program: Command): void;
@@ -0,0 +1,19 @@
1
+ import { disableSystemProxy } from '../system-proxy.js';
2
+ import { printSuccess, printError } from '../banner.js';
3
+ export function registerProxyOff(program) {
4
+ program
5
+ .command('proxy-off')
6
+ .description('Remove RoxyProxy as system-wide proxy (macOS)')
7
+ .option('--service <name>', 'Network service name (default: auto-detect)')
8
+ .action(async (opts) => {
9
+ const result = await disableSystemProxy(opts.service);
10
+ if (result.ok) {
11
+ printSuccess(result.message);
12
+ }
13
+ else {
14
+ printError(result.message);
15
+ process.exit(1);
16
+ }
17
+ });
18
+ }
19
+ //# sourceMappingURL=proxy-off.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy-off.js","sourceRoot":"","sources":["../../../src/cli/commands/proxy-off.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAExD,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,OAAO;SACJ,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,+CAA+C,CAAC;SAC5D,MAAM,CAAC,kBAAkB,EAAE,6CAA6C,CAAC;SACzE,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtD,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC3B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerProxyOn(program: Command): void;
@@ -0,0 +1,20 @@
1
+ import { enableSystemProxy } from '../system-proxy.js';
2
+ import { printSuccess, printError } from '../banner.js';
3
+ export function registerProxyOn(program) {
4
+ program
5
+ .command('proxy-on')
6
+ .description('Set RoxyProxy as system-wide proxy (macOS)')
7
+ .option('--port <number>', 'Proxy port', '8080')
8
+ .option('--service <name>', 'Network service name (default: auto-detect)')
9
+ .action(async (opts) => {
10
+ const result = await enableSystemProxy(opts.port, opts.service);
11
+ if (result.ok) {
12
+ printSuccess(result.message);
13
+ }
14
+ else {
15
+ printError(result.message);
16
+ process.exit(1);
17
+ }
18
+ });
19
+ }
20
+ //# sourceMappingURL=proxy-on.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy-on.js","sourceRoot":"","sources":["../../../src/cli/commands/proxy-on.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAExD,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO;SACJ,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,4CAA4C,CAAC;SACzD,MAAM,CAAC,iBAAiB,EAAE,YAAY,EAAE,MAAM,CAAC;SAC/C,MAAM,CAAC,kBAAkB,EAAE,6CAA6C,CAAC;SACzE,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAChE,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC3B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerRequest(program: Command): void;
@@ -0,0 +1,23 @@
1
+ import { Database } from '../../storage/db.js';
2
+ import { loadConfig } from '../../server/config.js';
3
+ import { formatRequest } from '../format.js';
4
+ export function registerRequest(program) {
5
+ program
6
+ .command('request <id>')
7
+ .description('Show details of a single request')
8
+ .option('--format <format>', 'Output format (json|table)', 'json')
9
+ .option('--db-path <path>', 'Database path')
10
+ .action((id, opts) => {
11
+ const config = loadConfig(opts.dbPath ? { dbPath: opts.dbPath } : {});
12
+ const db = new Database(config.dbPath);
13
+ const record = db.getById(id);
14
+ if (!record) {
15
+ console.error(`Request ${id} not found.`);
16
+ db.close();
17
+ process.exit(1);
18
+ }
19
+ console.log(formatRequest(record, opts.format));
20
+ db.close();
21
+ });
22
+ }
23
+ //# sourceMappingURL=request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../../../src/cli/commands/request.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO;SACJ,OAAO,CAAC,cAAc,CAAC;SACvB,WAAW,CAAC,kCAAkC,CAAC;SAC/C,MAAM,CAAC,mBAAmB,EAAE,4BAA4B,EAAE,MAAM,CAAC;SACjE,MAAM,CAAC,kBAAkB,EAAE,eAAe,CAAC;SAC3C,MAAM,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE;QACnB,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACtE,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEvC,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC9B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;YAC1C,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QAChD,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerRequests(program: Command): void;
@@ -0,0 +1,46 @@
1
+ import { Database } from '../../storage/db.js';
2
+ import { loadConfig } from '../../server/config.js';
3
+ import { formatRequests } from '../format.js';
4
+ export function registerRequests(program) {
5
+ program
6
+ .command('requests')
7
+ .description('Query captured requests')
8
+ .option('--host <pattern>', 'Filter by hostname')
9
+ .option('--status <code>', 'Filter by status code')
10
+ .option('--method <method>', 'Filter by HTTP method')
11
+ .option('--search <pattern>', 'Search URL')
12
+ .option('--since <time>', 'Requests after this time')
13
+ .option('--until <time>', 'Requests before this time')
14
+ .option('--limit <n>', 'Max results', '100')
15
+ .option('--format <format>', 'Output format (json|table)', 'json')
16
+ .option('--db-path <path>', 'Database path')
17
+ .action((opts) => {
18
+ const config = loadConfig(opts.dbPath ? { dbPath: opts.dbPath } : {});
19
+ const db = new Database(config.dbPath);
20
+ const filter = {
21
+ limit: parseInt(opts.limit, 10),
22
+ };
23
+ if (opts.host)
24
+ filter.host = opts.host;
25
+ if (opts.status)
26
+ filter.status = parseInt(opts.status, 10);
27
+ if (opts.method)
28
+ filter.method = opts.method;
29
+ if (opts.search)
30
+ filter.search = opts.search;
31
+ if (opts.since)
32
+ filter.since = parseTime(opts.since);
33
+ if (opts.until)
34
+ filter.until = parseTime(opts.until);
35
+ const result = db.query(filter);
36
+ console.log(formatRequests(result, opts.format));
37
+ db.close();
38
+ });
39
+ }
40
+ function parseTime(value) {
41
+ const num = Number(value);
42
+ if (!isNaN(num))
43
+ return num;
44
+ return new Date(value).getTime();
45
+ }
46
+ //# sourceMappingURL=requests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requests.js","sourceRoot":"","sources":["../../../src/cli/commands/requests.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9C,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,OAAO;SACJ,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,yBAAyB,CAAC;SACtC,MAAM,CAAC,kBAAkB,EAAE,oBAAoB,CAAC;SAChD,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,CAAC;SAClD,MAAM,CAAC,mBAAmB,EAAE,uBAAuB,CAAC;SACpD,MAAM,CAAC,oBAAoB,EAAE,YAAY,CAAC;SAC1C,MAAM,CAAC,gBAAgB,EAAE,0BAA0B,CAAC;SACpD,MAAM,CAAC,gBAAgB,EAAE,2BAA2B,CAAC;SACrD,MAAM,CAAC,aAAa,EAAE,aAAa,EAAE,KAAK,CAAC;SAC3C,MAAM,CAAC,mBAAmB,EAAE,4BAA4B,EAAE,MAAM,CAAC;SACjE,MAAM,CAAC,kBAAkB,EAAE,eAAe,CAAC;SAC3C,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;QACf,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACtE,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEvC,MAAM,MAAM,GAAkB;YAC5B,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;SAChC,CAAC;QACF,IAAI,IAAI,CAAC,IAAI;YAAE,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvC,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC3D,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC7C,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC7C,IAAI,IAAI,CAAC,KAAK;YAAE,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrD,IAAI,IAAI,CAAC,KAAK;YAAE,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAErD,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QACjD,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IAC5B,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;AACnC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerStart(program: Command): void;
@@ -0,0 +1,41 @@
1
+ import pc from 'picocolors';
2
+ import { loadConfig } from '../../server/config.js';
3
+ import { RoxyProxyServer } from '../../server/index.js';
4
+ import { printBanner, printStartInfo } from '../banner.js';
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import os from 'node:os';
8
+ export function registerStart(program) {
9
+ program
10
+ .command('start')
11
+ .description('Start the proxy server')
12
+ .option('--port <number>', 'Proxy port', '8080')
13
+ .option('--ui-port <number>', 'UI/API port', '8081')
14
+ .option('--db-path <path>', 'Database path')
15
+ .action(async (opts) => {
16
+ printBanner();
17
+ const config = loadConfig({
18
+ proxyPort: parseInt(opts.port, 10),
19
+ uiPort: parseInt(opts.uiPort, 10),
20
+ ...(opts.dbPath ? { dbPath: opts.dbPath } : {}),
21
+ });
22
+ const pidPath = path.join(os.homedir(), '.roxyproxy', 'pid');
23
+ fs.mkdirSync(path.dirname(pidPath), { recursive: true });
24
+ fs.writeFileSync(pidPath, process.pid.toString());
25
+ const server = new RoxyProxyServer(config);
26
+ const { proxyPort, uiPort } = await server.start();
27
+ printStartInfo(proxyPort, uiPort);
28
+ const shutdown = async () => {
29
+ console.log(`\n ${pc.yellow('⏻')} ${pc.dim('Shutting down...')}`);
30
+ await server.stop();
31
+ try {
32
+ fs.unlinkSync(pidPath);
33
+ }
34
+ catch { }
35
+ process.exit(0);
36
+ };
37
+ process.on('SIGINT', shutdown);
38
+ process.on('SIGTERM', shutdown);
39
+ });
40
+ }
41
+ //# sourceMappingURL=start.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../../../src/cli/commands/start.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC5C,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,wBAAwB,CAAC;SACrC,MAAM,CAAC,iBAAiB,EAAE,YAAY,EAAE,MAAM,CAAC;SAC/C,MAAM,CAAC,oBAAoB,EAAE,aAAa,EAAE,MAAM,CAAC;SACnD,MAAM,CAAC,kBAAkB,EAAE,eAAe,CAAC;SAC3C,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,WAAW,EAAE,CAAC;QAEd,MAAM,MAAM,GAAG,UAAU,CAAC;YACxB,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAClC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YACjC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChD,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;QAC7D,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;QAElD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QAEnD,cAAc,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAElC,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;YAC1B,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;YACnE,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,IAAI,CAAC;gBAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC;QAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerStatus(program: Command): void;
@@ -0,0 +1,44 @@
1
+ import pc from 'picocolors';
2
+ import http from 'node:http';
3
+ import { printError } from '../banner.js';
4
+ export function registerStatus(program) {
5
+ program
6
+ .command('status')
7
+ .description('Show proxy status')
8
+ .option('--ui-port <number>', 'UI/API port', '8081')
9
+ .action(async (opts) => {
10
+ const port = parseInt(opts.uiPort, 10);
11
+ try {
12
+ const body = await apiGet(port, '/api/status');
13
+ const s = JSON.parse(body);
14
+ const dot = s.running ? pc.green('●') : pc.red('●');
15
+ const state = s.running ? pc.green('Running') : pc.red('Stopped');
16
+ const bytes = s.dbSizeBytes;
17
+ const size = bytes < 1024 * 1024
18
+ ? `${(bytes / 1024).toFixed(1)}KB`
19
+ : `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
20
+ console.log('');
21
+ console.log(` ${dot} ${pc.bold('Status')} ${state}`);
22
+ console.log(` ${pc.dim('Proxy')} port ${pc.cyan(String(s.proxyPort))}`);
23
+ console.log(` ${pc.dim('Requests')} ${pc.bold(String(s.requestCount))}`);
24
+ console.log(` ${pc.dim('DB Size')} ${size}`);
25
+ console.log('');
26
+ }
27
+ catch {
28
+ printError('Proxy is not running.');
29
+ process.exit(1);
30
+ }
31
+ });
32
+ }
33
+ function apiGet(port, urlPath) {
34
+ return new Promise((resolve, reject) => {
35
+ const req = http.request({ host: '127.0.0.1', port, path: urlPath, method: 'GET' }, (res) => {
36
+ let body = '';
37
+ res.on('data', (chunk) => { body += chunk; });
38
+ res.on('end', () => resolve(body));
39
+ });
40
+ req.on('error', reject);
41
+ req.end();
42
+ });
43
+ }
44
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerStop(program: Command): void;
@@ -0,0 +1,52 @@
1
+ import http from 'node:http';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import os from 'node:os';
5
+ import { printSuccess, printError } from '../banner.js';
6
+ export function registerStop(program) {
7
+ program
8
+ .command('stop')
9
+ .description('Stop the running proxy server')
10
+ .option('--ui-port <number>', 'UI/API port', '8081')
11
+ .action(async (opts) => {
12
+ const port = parseInt(opts.uiPort, 10);
13
+ try {
14
+ await apiPost(port, '/api/shutdown');
15
+ printSuccess('Server shutting down.');
16
+ return;
17
+ }
18
+ catch { }
19
+ const pidPath = path.join(os.homedir(), '.roxyproxy', 'pid');
20
+ if (fs.existsSync(pidPath)) {
21
+ const pid = parseInt(fs.readFileSync(pidPath, 'utf-8').trim(), 10);
22
+ try {
23
+ process.kill(pid, 'SIGTERM');
24
+ fs.unlinkSync(pidPath);
25
+ printSuccess(`Sent SIGTERM to process ${pid}.`);
26
+ return;
27
+ }
28
+ catch { }
29
+ }
30
+ printError('Could not stop proxy. Is it running?');
31
+ process.exit(1);
32
+ });
33
+ }
34
+ function apiPost(port, urlPath) {
35
+ return new Promise((resolve, reject) => {
36
+ const req = http.request({
37
+ host: '127.0.0.1', port, path: urlPath, method: 'POST',
38
+ headers: { 'Content-Type': 'application/json' },
39
+ }, (res) => {
40
+ res.resume();
41
+ res.on('end', () => {
42
+ if (res.statusCode === 200)
43
+ resolve();
44
+ else
45
+ reject(new Error(`HTTP ${res.statusCode}`));
46
+ });
47
+ });
48
+ req.on('error', reject);
49
+ req.end();
50
+ });
51
+ }
52
+ //# sourceMappingURL=stop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.js","sourceRoot":"","sources":["../../../src/cli/commands/stop.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAExD,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC3C,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,+BAA+B,CAAC;SAC5C,MAAM,CAAC,oBAAoB,EAAE,aAAa,EAAE,MAAM,CAAC;SACnD,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAEvC,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;YACrC,YAAY,CAAC,uBAAuB,CAAC,CAAC;YACtC,OAAO;QACT,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;QAEV,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;QAC7D,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YACnE,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;gBAC7B,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;gBACvB,YAAY,CAAC,2BAA2B,GAAG,GAAG,CAAC,CAAC;gBAChD,OAAO;YACT,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACZ,CAAC;QAED,UAAU,CAAC,sCAAsC,CAAC,CAAC;QACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,OAAO,CAAC,IAAY,EAAE,OAAe;IAC5C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC;YACvB,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM;YACtD,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAChD,EAAE,CAAC,GAAG,EAAE,EAAE;YACT,GAAG,CAAC,MAAM,EAAE,CAAC;YACb,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACjB,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG;oBAAE,OAAO,EAAE,CAAC;;oBACjC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;YACnD,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACxB,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerTrustCa(program: Command): void;
@@ -0,0 +1,134 @@
1
+ import pc from 'picocolors';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ import fs from 'node:fs';
5
+ import readline from 'node:readline';
6
+ import { execFile } from 'node:child_process';
7
+ import { printSuccess, printError, printInfo, printWarn } from '../banner.js';
8
+ function ask(question) {
9
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
10
+ return new Promise((resolve) => {
11
+ rl.question(question, (answer) => {
12
+ rl.close();
13
+ resolve(answer.trim().toLowerCase());
14
+ });
15
+ });
16
+ }
17
+ function run(cmd, args) {
18
+ return new Promise((resolve) => {
19
+ execFile(cmd, args, (error, stdout, stderr) => {
20
+ resolve({ code: error ? 1 : 0, stdout, stderr });
21
+ });
22
+ });
23
+ }
24
+ export function registerTrustCa(program) {
25
+ program
26
+ .command('trust-ca')
27
+ .description('Install and trust the CA certificate')
28
+ .option('--no-interactive', 'Just print the cert path and instructions')
29
+ .action(async (opts) => {
30
+ const certPath = path.join(os.homedir(), '.roxyproxy', 'ca', 'ca.crt');
31
+ if (!fs.existsSync(certPath)) {
32
+ printError('CA certificate not found. Start the proxy first to generate it.');
33
+ process.exit(1);
34
+ }
35
+ console.log('');
36
+ printInfo(`CA Certificate: ${pc.cyan(certPath)}`);
37
+ console.log('');
38
+ const platform = os.platform();
39
+ if (!opts.interactive) {
40
+ printManualInstructions(certPath, platform);
41
+ return;
42
+ }
43
+ if (platform === 'darwin') {
44
+ await installMacOS(certPath);
45
+ }
46
+ else if (platform === 'linux') {
47
+ await installLinux(certPath);
48
+ }
49
+ else {
50
+ printWarn(`Automatic installation not supported on ${platform}.`);
51
+ printManualInstructions(certPath, platform);
52
+ }
53
+ });
54
+ }
55
+ async function installMacOS(certPath) {
56
+ console.log(` This will add the RoxyProxy CA to your macOS system keychain.`);
57
+ console.log(` You'll be prompted for your ${pc.bold('sudo password')}.`);
58
+ console.log('');
59
+ const answer = await ask(` ${pc.yellow('?')} Install certificate to system keychain? ${pc.dim('[Y/n]')} `);
60
+ if (answer && answer !== 'y' && answer !== 'yes') {
61
+ printInfo('Skipped. You can install manually:');
62
+ printManualInstructions(certPath, 'darwin');
63
+ return;
64
+ }
65
+ console.log('');
66
+ printInfo('Installing certificate...');
67
+ const result = await run('sudo', [
68
+ 'security', 'add-trusted-cert',
69
+ '-d', '-r', 'trustRoot',
70
+ '-k', '/Library/Keychains/System.keychain',
71
+ certPath,
72
+ ]);
73
+ if (result.code === 0) {
74
+ printSuccess('Certificate installed and trusted!');
75
+ console.log('');
76
+ printInfo('HTTPS interception is now active.');
77
+ printInfo(`Test it: ${pc.cyan('curl -x http://127.0.0.1:8080 https://httpbin.org/get')}`);
78
+ }
79
+ else {
80
+ printError('Failed to install certificate.');
81
+ if (result.stderr)
82
+ console.log(pc.dim(` ${result.stderr.trim()}`));
83
+ console.log('');
84
+ printInfo('You can install manually:');
85
+ printManualInstructions(certPath, 'darwin');
86
+ }
87
+ console.log('');
88
+ }
89
+ async function installLinux(certPath) {
90
+ console.log(` This will copy the RoxyProxy CA to your system certificate store.`);
91
+ console.log(` You'll be prompted for your ${pc.bold('sudo password')}.`);
92
+ console.log('');
93
+ const answer = await ask(` ${pc.yellow('?')} Install certificate to system store? ${pc.dim('[Y/n]')} `);
94
+ if (answer && answer !== 'y' && answer !== 'yes') {
95
+ printInfo('Skipped. You can install manually:');
96
+ printManualInstructions(certPath, 'linux');
97
+ return;
98
+ }
99
+ console.log('');
100
+ printInfo('Installing certificate...');
101
+ const copyResult = await run('sudo', [
102
+ 'cp', certPath, '/usr/local/share/ca-certificates/roxyproxy.crt',
103
+ ]);
104
+ if (copyResult.code !== 0) {
105
+ printError('Failed to copy certificate.');
106
+ return;
107
+ }
108
+ const updateResult = await run('sudo', ['update-ca-certificates']);
109
+ if (updateResult.code === 0) {
110
+ printSuccess('Certificate installed and trusted!');
111
+ }
112
+ else {
113
+ printError('Failed to update certificate store.');
114
+ if (updateResult.stderr)
115
+ console.log(pc.dim(` ${updateResult.stderr.trim()}`));
116
+ }
117
+ console.log('');
118
+ }
119
+ function printManualInstructions(certPath, platform) {
120
+ if (platform === 'darwin') {
121
+ console.log(` ${pc.bold('macOS:')}`);
122
+ console.log(pc.cyan(` sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${certPath}"`));
123
+ }
124
+ else {
125
+ console.log(` ${pc.bold('Linux (Debian/Ubuntu):')}`);
126
+ console.log(pc.cyan(` sudo cp "${certPath}" /usr/local/share/ca-certificates/roxyproxy.crt`));
127
+ console.log(pc.cyan(` sudo update-ca-certificates`));
128
+ }
129
+ console.log('');
130
+ console.log(` ${pc.bold('Firefox')} ${pc.dim('(uses its own cert store):')}`);
131
+ console.log(` Settings > Privacy & Security > Certificates > Import`);
132
+ console.log('');
133
+ }
134
+ //# sourceMappingURL=trust-ca.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust-ca.js","sourceRoot":"","sources":["../../../src/cli/commands/trust-ca.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { RequestRecord, PaginatedResponse } from '../shared/types.js';
2
+ export declare function formatRequests(result: PaginatedResponse<RequestRecord>, format: string): string;
3
+ export declare function formatRequest(record: RequestRecord, format: string): string;
@@ -0,0 +1,112 @@
1
+ import pc from 'picocolors';
2
+ const methodColor = (method) => {
3
+ switch (method) {
4
+ case 'GET': return pc.blue(method);
5
+ case 'POST': return pc.green(method);
6
+ case 'PUT': return pc.yellow(method);
7
+ case 'PATCH': return pc.magenta(method);
8
+ case 'DELETE': return pc.red(method);
9
+ default: return pc.dim(method);
10
+ }
11
+ };
12
+ const statusColor = (status) => {
13
+ const s = String(status ?? '-');
14
+ if (!status)
15
+ return pc.dim(s);
16
+ if (status < 300)
17
+ return pc.green(s);
18
+ if (status < 400)
19
+ return pc.yellow(s);
20
+ if (status < 500)
21
+ return pc.magenta(s);
22
+ return pc.red(s);
23
+ };
24
+ export function formatRequests(result, format) {
25
+ if (format === 'json') {
26
+ return JSON.stringify(result, null, 2);
27
+ }
28
+ if (result.data.length === 0) {
29
+ return `\n ${pc.dim('No requests found.')}\n`;
30
+ }
31
+ const header = pc.dim(' ' +
32
+ 'METHOD'.padEnd(10) +
33
+ 'STATUS'.padEnd(8) +
34
+ 'HOST'.padEnd(32) +
35
+ 'PATH'.padEnd(32) +
36
+ 'TIME'.padEnd(10) +
37
+ 'SIZE'.padEnd(10));
38
+ const divider = pc.dim(' ' + '─'.repeat(100));
39
+ const rows = result.data.map((r) => {
40
+ return ' ' +
41
+ methodColor(r.method || '').padEnd(10 + 10) + // extra for ANSI codes
42
+ statusColor(r.status).padEnd(8 + 10) +
43
+ (r.host || '').slice(0, 30).padEnd(32) +
44
+ pc.dim((r.path || '').slice(0, 30)).padEnd(32 + 10) +
45
+ pc.dim(r.duration ? `${r.duration}ms` : '-').padEnd(10 + 10) +
46
+ pc.dim(formatBytes(r.response_size || 0));
47
+ });
48
+ const footer = `\n ${pc.dim(`${result.total} total (showing ${result.data.length}, offset ${result.offset})`)}`;
49
+ return ['', header, divider, ...rows, footer, ''].join('\n');
50
+ }
51
+ export function formatRequest(record, format) {
52
+ if (format === 'json') {
53
+ return JSON.stringify(record, null, 2);
54
+ }
55
+ const lines = [
56
+ '',
57
+ ` ${pc.dim('ID')} ${record.id}`,
58
+ ` ${pc.dim('URL')} ${pc.cyan(record.url)}`,
59
+ ` ${pc.dim('Method')} ${methodColor(record.method)}`,
60
+ ` ${pc.dim('Status')} ${statusColor(record.status)}`,
61
+ ` ${pc.dim('Duration')} ${record.duration}ms`,
62
+ ` ${pc.dim('Protocol')} ${record.protocol}`,
63
+ ` ${pc.dim('Time')} ${new Date(record.timestamp).toISOString()}`,
64
+ '',
65
+ ` ${pc.bold('Request Headers')}`,
66
+ formatHeaders(record.request_headers),
67
+ '',
68
+ ` ${pc.bold('Response Headers')}`,
69
+ formatHeaders(record.response_headers),
70
+ ];
71
+ if (record.request_body) {
72
+ lines.push('', ` ${pc.bold('Request Body')}`, formatBody(record.request_body, record.content_type));
73
+ }
74
+ if (record.response_body) {
75
+ lines.push('', ` ${pc.bold('Response Body')}`, formatBody(record.response_body, record.content_type));
76
+ }
77
+ lines.push('');
78
+ return lines.join('\n');
79
+ }
80
+ function formatHeaders(headersJson) {
81
+ if (!headersJson)
82
+ return ` ${pc.dim('(none)')}`;
83
+ try {
84
+ const headers = JSON.parse(headersJson);
85
+ return Object.entries(headers)
86
+ .map(([k, v]) => ` ${pc.magenta(k)}${pc.dim(':')} ${v}`)
87
+ .join('\n');
88
+ }
89
+ catch {
90
+ return ` ${headersJson}`;
91
+ }
92
+ }
93
+ function formatBody(body, contentType) {
94
+ if (!body)
95
+ return ` ${pc.dim('(empty)')}`;
96
+ const str = Buffer.isBuffer(body) ? body.toString('utf-8') : String(body);
97
+ if (contentType?.includes('json')) {
98
+ try {
99
+ return str.split('\n').map(line => ` ${line}`).join('\n');
100
+ }
101
+ catch { }
102
+ }
103
+ return ` ${str}`;
104
+ }
105
+ function formatBytes(bytes) {
106
+ if (bytes < 1024)
107
+ return `${bytes}B`;
108
+ if (bytes < 1024 * 1024)
109
+ return `${(bytes / 1024).toFixed(1)}KB`;
110
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
111
+ }
112
+ //# sourceMappingURL=format.js.map