htpx-cli 0.1.2 → 0.2.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 (252) hide show
  1. package/LICENSE +665 -21
  2. package/README.md +422 -116
  3. package/dist/cli/commands/clear.d.ts.map +1 -1
  4. package/dist/cli/commands/clear.js +11 -11
  5. package/dist/cli/commands/clear.js.map +1 -1
  6. package/dist/cli/commands/daemon.d.ts +3 -0
  7. package/dist/cli/commands/daemon.d.ts.map +1 -0
  8. package/dist/cli/commands/daemon.js +59 -0
  9. package/dist/cli/commands/daemon.js.map +1 -0
  10. package/dist/cli/commands/debug-dump.d.ts.map +1 -1
  11. package/dist/cli/commands/debug-dump.js +8 -10
  12. package/dist/cli/commands/debug-dump.js.map +1 -1
  13. package/dist/cli/commands/helpers.d.ts +18 -0
  14. package/dist/cli/commands/helpers.d.ts.map +1 -0
  15. package/dist/cli/commands/helpers.js +34 -0
  16. package/dist/cli/commands/helpers.js.map +1 -0
  17. package/dist/cli/commands/init.d.ts +1 -1
  18. package/dist/cli/commands/init.d.ts.map +1 -1
  19. package/dist/cli/commands/init.js +3 -4
  20. package/dist/cli/commands/init.js.map +1 -1
  21. package/dist/cli/commands/intercept.d.ts +2 -1
  22. package/dist/cli/commands/intercept.d.ts.map +1 -1
  23. package/dist/cli/commands/intercept.js +74 -30
  24. package/dist/cli/commands/intercept.js.map +1 -1
  25. package/dist/cli/commands/interceptors.d.ts +3 -0
  26. package/dist/cli/commands/interceptors.d.ts.map +1 -0
  27. package/dist/cli/commands/interceptors.js +163 -0
  28. package/dist/cli/commands/interceptors.js.map +1 -0
  29. package/dist/cli/commands/mcp.d.ts +3 -0
  30. package/dist/cli/commands/mcp.d.ts.map +1 -0
  31. package/dist/cli/commands/mcp.js +24 -0
  32. package/dist/cli/commands/mcp.js.map +1 -0
  33. package/dist/cli/commands/off.d.ts +8 -0
  34. package/dist/cli/commands/off.d.ts.map +1 -0
  35. package/dist/cli/commands/off.js +34 -0
  36. package/dist/cli/commands/off.js.map +1 -0
  37. package/dist/cli/commands/on.d.ts +9 -0
  38. package/dist/cli/commands/on.d.ts.map +1 -0
  39. package/dist/cli/commands/on.js +121 -0
  40. package/dist/cli/commands/on.js.map +1 -0
  41. package/dist/cli/commands/project.d.ts.map +1 -1
  42. package/dist/cli/commands/project.js +5 -3
  43. package/dist/cli/commands/project.js.map +1 -1
  44. package/dist/cli/commands/restart.d.ts.map +1 -1
  45. package/dist/cli/commands/restart.js +5 -10
  46. package/dist/cli/commands/restart.js.map +1 -1
  47. package/dist/cli/commands/status.d.ts.map +1 -1
  48. package/dist/cli/commands/status.js +50 -20
  49. package/dist/cli/commands/status.js.map +1 -1
  50. package/dist/cli/commands/stop.d.ts.map +1 -1
  51. package/dist/cli/commands/stop.js +7 -10
  52. package/dist/cli/commands/stop.js.map +1 -1
  53. package/dist/cli/commands/tui.d.ts.map +1 -1
  54. package/dist/cli/commands/tui.js +6 -13
  55. package/dist/cli/commands/tui.js.map +1 -1
  56. package/dist/cli/index.js +12 -7
  57. package/dist/cli/index.js.map +1 -1
  58. package/dist/cli/tui/App.d.ts +7 -2
  59. package/dist/cli/tui/App.d.ts.map +1 -1
  60. package/dist/cli/tui/App.js +490 -33
  61. package/dist/cli/tui/App.js.map +1 -1
  62. package/dist/cli/tui/components/AccordionContent.d.ts +28 -0
  63. package/dist/cli/tui/components/AccordionContent.d.ts.map +1 -0
  64. package/dist/cli/tui/components/AccordionContent.js +87 -0
  65. package/dist/cli/tui/components/AccordionContent.js.map +1 -0
  66. package/dist/cli/tui/components/AccordionPanel.d.ts +38 -0
  67. package/dist/cli/tui/components/AccordionPanel.d.ts.map +1 -0
  68. package/dist/cli/tui/components/AccordionPanel.js +110 -0
  69. package/dist/cli/tui/components/AccordionPanel.js.map +1 -0
  70. package/dist/cli/tui/components/AccordionSection.d.ts +32 -0
  71. package/dist/cli/tui/components/AccordionSection.d.ts.map +1 -0
  72. package/dist/cli/tui/components/AccordionSection.js +41 -0
  73. package/dist/cli/tui/components/AccordionSection.js.map +1 -0
  74. package/dist/cli/tui/components/ExportModal.d.ts +34 -0
  75. package/dist/cli/tui/components/ExportModal.d.ts.map +1 -0
  76. package/dist/cli/tui/components/ExportModal.js +109 -0
  77. package/dist/cli/tui/components/ExportModal.js.map +1 -0
  78. package/dist/cli/tui/components/FilterBar.d.ts +21 -0
  79. package/dist/cli/tui/components/FilterBar.d.ts.map +1 -0
  80. package/dist/cli/tui/components/FilterBar.js +155 -0
  81. package/dist/cli/tui/components/FilterBar.js.map +1 -0
  82. package/dist/cli/tui/components/HelpModal.d.ts +13 -0
  83. package/dist/cli/tui/components/HelpModal.d.ts.map +1 -0
  84. package/dist/cli/tui/components/HelpModal.js +78 -0
  85. package/dist/cli/tui/components/HelpModal.js.map +1 -0
  86. package/dist/cli/tui/components/HintContent.d.ts +25 -0
  87. package/dist/cli/tui/components/HintContent.d.ts.map +1 -0
  88. package/dist/cli/tui/components/HintContent.js +44 -0
  89. package/dist/cli/tui/components/HintContent.js.map +1 -0
  90. package/dist/cli/tui/components/InfoModal.d.ts +15 -0
  91. package/dist/cli/tui/components/InfoModal.d.ts.map +1 -0
  92. package/dist/cli/tui/components/InfoModal.js +17 -0
  93. package/dist/cli/tui/components/InfoModal.js.map +1 -0
  94. package/dist/cli/tui/components/JsonExplorerModal.d.ts +24 -0
  95. package/dist/cli/tui/components/JsonExplorerModal.d.ts.map +1 -0
  96. package/dist/cli/tui/components/JsonExplorerModal.js +311 -0
  97. package/dist/cli/tui/components/JsonExplorerModal.js.map +1 -0
  98. package/dist/cli/tui/components/Modal.d.ts +26 -0
  99. package/dist/cli/tui/components/Modal.d.ts.map +1 -0
  100. package/dist/cli/tui/components/Modal.js +15 -0
  101. package/dist/cli/tui/components/Modal.js.map +1 -0
  102. package/dist/cli/tui/components/Panel.d.ts +19 -0
  103. package/dist/cli/tui/components/Panel.d.ts.map +1 -0
  104. package/dist/cli/tui/components/Panel.js +37 -0
  105. package/dist/cli/tui/components/Panel.js.map +1 -0
  106. package/dist/cli/tui/components/RequestDetails.d.ts +4 -1
  107. package/dist/cli/tui/components/RequestDetails.d.ts.map +1 -1
  108. package/dist/cli/tui/components/RequestDetails.js +9 -5
  109. package/dist/cli/tui/components/RequestDetails.js.map +1 -1
  110. package/dist/cli/tui/components/RequestList.d.ts +9 -3
  111. package/dist/cli/tui/components/RequestList.d.ts.map +1 -1
  112. package/dist/cli/tui/components/RequestList.js +24 -11
  113. package/dist/cli/tui/components/RequestList.js.map +1 -1
  114. package/dist/cli/tui/components/RequestListItem.d.ts +26 -3
  115. package/dist/cli/tui/components/RequestListItem.d.ts.map +1 -1
  116. package/dist/cli/tui/components/RequestListItem.js +86 -9
  117. package/dist/cli/tui/components/RequestListItem.js.map +1 -1
  118. package/dist/cli/tui/components/SaveModal.d.ts +30 -0
  119. package/dist/cli/tui/components/SaveModal.d.ts.map +1 -0
  120. package/dist/cli/tui/components/SaveModal.js +95 -0
  121. package/dist/cli/tui/components/SaveModal.js.map +1 -0
  122. package/dist/cli/tui/components/StatusBar.d.ts +31 -2
  123. package/dist/cli/tui/components/StatusBar.d.ts.map +1 -1
  124. package/dist/cli/tui/components/StatusBar.js +44 -9
  125. package/dist/cli/tui/components/StatusBar.js.map +1 -1
  126. package/dist/cli/tui/components/TextViewerModal.d.ts +19 -0
  127. package/dist/cli/tui/components/TextViewerModal.d.ts.map +1 -0
  128. package/dist/cli/tui/components/TextViewerModal.js +227 -0
  129. package/dist/cli/tui/components/TextViewerModal.js.map +1 -0
  130. package/dist/cli/tui/hooks/useBodyExport.d.ts +26 -0
  131. package/dist/cli/tui/hooks/useBodyExport.d.ts.map +1 -0
  132. package/dist/cli/tui/hooks/useBodyExport.js +173 -0
  133. package/dist/cli/tui/hooks/useBodyExport.js.map +1 -0
  134. package/dist/cli/tui/hooks/useExport.d.ts +13 -2
  135. package/dist/cli/tui/hooks/useExport.d.ts.map +1 -1
  136. package/dist/cli/tui/hooks/useExport.js +46 -40
  137. package/dist/cli/tui/hooks/useExport.js.map +1 -1
  138. package/dist/cli/tui/hooks/useRequests.d.ts +9 -3
  139. package/dist/cli/tui/hooks/useRequests.d.ts.map +1 -1
  140. package/dist/cli/tui/hooks/useRequests.js +61 -15
  141. package/dist/cli/tui/hooks/useRequests.js.map +1 -1
  142. package/dist/cli/tui/hooks/useSaveBinary.d.ts +26 -0
  143. package/dist/cli/tui/hooks/useSaveBinary.d.ts.map +1 -0
  144. package/dist/cli/tui/hooks/useSaveBinary.js +165 -0
  145. package/dist/cli/tui/hooks/useSaveBinary.js.map +1 -0
  146. package/dist/cli/tui/hooks/useSpinner.d.ts +5 -0
  147. package/dist/cli/tui/hooks/useSpinner.d.ts.map +1 -0
  148. package/dist/cli/tui/hooks/useSpinner.js +25 -0
  149. package/dist/cli/tui/hooks/useSpinner.js.map +1 -0
  150. package/dist/cli/tui/utils/binary.d.ts +24 -0
  151. package/dist/cli/tui/utils/binary.d.ts.map +1 -0
  152. package/dist/cli/tui/utils/binary.js +152 -0
  153. package/dist/cli/tui/utils/binary.js.map +1 -0
  154. package/dist/cli/tui/utils/clipboard.d.ts +9 -0
  155. package/dist/cli/tui/utils/clipboard.d.ts.map +1 -0
  156. package/dist/cli/tui/utils/clipboard.js +58 -0
  157. package/dist/cli/tui/utils/clipboard.js.map +1 -0
  158. package/dist/cli/tui/utils/content-type.d.ts +8 -0
  159. package/dist/cli/tui/utils/content-type.d.ts.map +1 -0
  160. package/dist/cli/tui/utils/content-type.js +10 -0
  161. package/dist/cli/tui/utils/content-type.js.map +1 -0
  162. package/dist/cli/tui/utils/curl.d.ts.map +1 -1
  163. package/dist/cli/tui/utils/curl.js +9 -2
  164. package/dist/cli/tui/utils/curl.js.map +1 -1
  165. package/dist/cli/tui/utils/filters.d.ts +6 -0
  166. package/dist/cli/tui/utils/filters.d.ts.map +1 -0
  167. package/dist/cli/tui/utils/filters.js +13 -0
  168. package/dist/cli/tui/utils/filters.js.map +1 -0
  169. package/dist/cli/tui/utils/formatters.d.ts +8 -0
  170. package/dist/cli/tui/utils/formatters.d.ts.map +1 -1
  171. package/dist/cli/tui/utils/formatters.js +85 -0
  172. package/dist/cli/tui/utils/formatters.js.map +1 -1
  173. package/dist/cli/tui/utils/har.d.ts.map +1 -1
  174. package/dist/cli/tui/utils/har.js +3 -25
  175. package/dist/cli/tui/utils/har.js.map +1 -1
  176. package/dist/cli/tui/utils/json-tree.d.ts +69 -0
  177. package/dist/cli/tui/utils/json-tree.d.ts.map +1 -0
  178. package/dist/cli/tui/utils/json-tree.js +339 -0
  179. package/dist/cli/tui/utils/json-tree.js.map +1 -0
  180. package/dist/cli/tui/utils/open-external.d.ts +17 -0
  181. package/dist/cli/tui/utils/open-external.d.ts.map +1 -0
  182. package/dist/cli/tui/utils/open-external.js +57 -0
  183. package/dist/cli/tui/utils/open-external.js.map +1 -0
  184. package/dist/cli/tui/utils/syntax-highlight.d.ts +16 -0
  185. package/dist/cli/tui/utils/syntax-highlight.d.ts.map +1 -0
  186. package/dist/cli/tui/utils/syntax-highlight.js +64 -0
  187. package/dist/cli/tui/utils/syntax-highlight.js.map +1 -0
  188. package/dist/daemon/control.d.ts +3 -49
  189. package/dist/daemon/control.d.ts.map +1 -1
  190. package/dist/daemon/control.js +183 -141
  191. package/dist/daemon/control.js.map +1 -1
  192. package/dist/daemon/htpx-client.d.ts +8 -0
  193. package/dist/daemon/htpx-client.d.ts.map +1 -0
  194. package/dist/daemon/htpx-client.js +25 -0
  195. package/dist/daemon/htpx-client.js.map +1 -0
  196. package/dist/daemon/index.js +50 -2
  197. package/dist/daemon/index.js.map +1 -1
  198. package/dist/daemon/interceptor-loader.d.ts +30 -0
  199. package/dist/daemon/interceptor-loader.d.ts.map +1 -0
  200. package/dist/daemon/interceptor-loader.js +249 -0
  201. package/dist/daemon/interceptor-loader.js.map +1 -0
  202. package/dist/daemon/interceptor-runner.d.ts +39 -0
  203. package/dist/daemon/interceptor-runner.d.ts.map +1 -0
  204. package/dist/daemon/interceptor-runner.js +312 -0
  205. package/dist/daemon/interceptor-runner.js.map +1 -0
  206. package/dist/daemon/proxy.d.ts +12 -0
  207. package/dist/daemon/proxy.d.ts.map +1 -1
  208. package/dist/daemon/proxy.js +121 -10
  209. package/dist/daemon/proxy.js.map +1 -1
  210. package/dist/daemon/storage.d.ts +64 -2
  211. package/dist/daemon/storage.d.ts.map +1 -1
  212. package/dist/daemon/storage.js +527 -12
  213. package/dist/daemon/storage.js.map +1 -1
  214. package/dist/interceptors.d.ts +2 -0
  215. package/dist/interceptors.d.ts.map +1 -0
  216. package/dist/interceptors.js +2 -0
  217. package/dist/interceptors.js.map +1 -0
  218. package/dist/mcp/server.d.ts +110 -0
  219. package/dist/mcp/server.d.ts.map +1 -0
  220. package/dist/mcp/server.js +806 -0
  221. package/dist/mcp/server.js.map +1 -0
  222. package/dist/shared/config.d.ts +21 -0
  223. package/dist/shared/config.d.ts.map +1 -0
  224. package/dist/shared/config.js +83 -0
  225. package/dist/shared/config.js.map +1 -0
  226. package/dist/shared/content-type.d.ts +64 -0
  227. package/dist/shared/content-type.d.ts.map +1 -0
  228. package/dist/shared/content-type.js +145 -0
  229. package/dist/shared/content-type.js.map +1 -0
  230. package/dist/shared/control-client.d.ts +144 -0
  231. package/dist/shared/control-client.d.ts.map +1 -0
  232. package/dist/shared/control-client.js +272 -0
  233. package/dist/shared/control-client.js.map +1 -0
  234. package/dist/shared/daemon.d.ts.map +1 -1
  235. package/dist/shared/daemon.js +17 -4
  236. package/dist/shared/daemon.js.map +1 -1
  237. package/dist/shared/logger.d.ts +21 -5
  238. package/dist/shared/logger.d.ts.map +1 -1
  239. package/dist/shared/logger.js +100 -21
  240. package/dist/shared/logger.js.map +1 -1
  241. package/dist/shared/project.d.ts +16 -3
  242. package/dist/shared/project.d.ts.map +1 -1
  243. package/dist/shared/project.js +45 -5
  244. package/dist/shared/project.js.map +1 -1
  245. package/dist/shared/proxy-info.d.ts +10 -0
  246. package/dist/shared/proxy-info.d.ts.map +1 -0
  247. package/dist/shared/proxy-info.js +15 -0
  248. package/dist/shared/proxy-info.js.map +1 -0
  249. package/dist/shared/types.d.ts +95 -0
  250. package/dist/shared/types.d.ts.map +1 -1
  251. package/package.json +24 -5
  252. package/skills/htpx/SKILL.md +228 -0
package/README.md CHANGED
@@ -4,98 +4,295 @@
4
4
  [![CI](https://github.com/mtford90/htpx/actions/workflows/ci.yml/badge.svg)](https://github.com/mtford90/htpx/actions/workflows/ci.yml)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- A terminal-based HTTP interception toolkit with project-scoped isolation and a lazygit-style TUI.
7
+ HTTP interception for the terminal. Each project gets its own proxy, its own traffic database, its own mocks — all in a `.htpx/` directory that lives alongside your code.
8
8
 
9
- Capture HTTP/HTTPS traffic through a MITM proxy and inspect it in an interactive terminal interface. No browser extensions, no separate apps—just your terminal.
9
+ ![htpx demo](demo.gif)
10
10
 
11
- ## Features
12
-
13
- - **Project-scoped isolation** — Each project gets its own `.htpx/` directory, keeping traffic separate
14
- - **Interactive TUI** — Browse, inspect, and export requests with vim-style keybindings
15
- - **Full HTTPS support** — Automatic CA certificate generation and trust
16
- - **Session labelling** — Tag and filter traffic by session for organised debugging
17
- - **Export anywhere** — Generate curl commands or HAR files from captured requests
18
- - **Zero config** — Works out of the box with curl, wget, Node.js, Python, and more
11
+ No browser extensions, no global system proxy, no separate apps. A MITM proxy captures your traffic, a lazygit-style TUI lets you browse it, TypeScript files let you mock it, and AI agents can query and manipulate all of it via MCP.
19
12
 
20
13
  ## Quick Start
21
14
 
22
15
  ```bash
23
- # Install globally
24
16
  npm install -g htpx-cli
25
17
 
26
- # One-time shell setup (add to ~/.zshrc or ~/.bashrc)
18
+ # One-time shell setup
27
19
  eval "$(htpx init)"
28
20
 
29
- # Start intercepting in your project directory
30
- htpx intercept
31
-
32
- # Make some requests...
21
+ # In your project directory
22
+ htpx on
33
23
  curl https://api.example.com/users
34
-
35
- # Open the TUI to inspect
36
24
  htpx tui
37
25
  ```
38
26
 
39
- ## Installation
27
+ Requires Node.js 20+.
28
+
29
+ ### Shell Setup
30
+
31
+ Add this to your `~/.zshrc` or `~/.bashrc`:
40
32
 
41
33
  ```bash
42
- npm install -g htpx-cli
34
+ eval "$(htpx init)"
43
35
  ```
44
36
 
45
- **Requirements:** Node.js 20 or later
37
+ This creates a shell function that sets proxy environment variables in your current session.
46
38
 
47
- ### Shell Setup
39
+ ## Features
48
40
 
49
- Add the following to your `~/.zshrc` or `~/.bashrc`:
41
+ - **Project-scoped** each project gets its own `.htpx/` directory with a separate daemon, database, CA cert and interceptors. No cross-project bleed.
42
+ - **TypeScript interceptors** — mock, modify or observe traffic with `.ts` files. Match on anything, query past traffic from within handlers, compose complex scenarios.
43
+ - **MCP server** — AI agents get full access to your captured traffic and can write interceptor files for you. Search, filter, inspect, mock — all via tool calls.
44
+ - **Terminal TUI** — vim-style keybindings, mouse support, JSON explorer, filtering. Stays in your terminal where you're already working.
45
+ - **HTTPS** — automatic CA certificate generation and trust
46
+ - **Export** — copy as curl, export as HAR, save bodies to disk
47
+ - **Zero config** — works with curl, wget, Node.js, Python, Go, Rust and anything else that respects `HTTP_PROXY`
50
48
 
51
- ```bash
52
- eval "$(htpx init)"
49
+ ## Project Isolation
50
+
51
+ htpx doesn't use a global system proxy. Each project gets its own `.htpx/` directory in the project root (detected by `.git` or an existing `.htpx/`):
52
+
53
+ ```
54
+ your-project/
55
+ ├── .htpx/
56
+ │ ├── interceptors/ # TypeScript interceptor files
57
+ │ ├── config.json # Optional project config
58
+ │ ├── proxy.port # Proxy TCP port
59
+ │ ├── control.sock # IPC socket
60
+ │ ├── requests.db # Captured traffic
61
+ │ ├── ca.pem # CA certificate
62
+ │ └── daemon.pid # Process ID
63
+ └── src/...
53
64
  ```
54
65
 
55
- This creates a shell function that properly sets up proxy environment variables in your current session.
66
+ Separate daemon, separate database, separate certificates. You can run htpx in multiple projects at the same time without them interfering with each other.
56
67
 
57
- ## Usage
68
+ ## Interceptors
58
69
 
59
- ### Start Intercepting
70
+ TypeScript files in `.htpx/interceptors/` that intercept HTTP traffic as it passes through the proxy. They can return mock responses, modify upstream responses, or just observe.
60
71
 
61
72
  ```bash
62
- # Basic interception
63
- htpx intercept
73
+ htpx interceptors init # scaffold an example
74
+ htpx interceptors reload # reload after editing
75
+ ```
76
+
77
+ ### Mock
64
78
 
65
- # With a session label for filtering
66
- htpx intercept --label api-tests
79
+ Return a response without hitting upstream:
80
+
81
+ ```typescript
82
+ import type { Interceptor } from "htpx-cli/interceptors";
83
+
84
+ export default {
85
+ name: "mock-users",
86
+ match: (req) => req.path === "/api/users",
87
+ handler: async () => ({
88
+ status: 200,
89
+ headers: { "content-type": "application/json" },
90
+ body: JSON.stringify([{ id: 1, name: "Alice" }]),
91
+ }),
92
+ } satisfies Interceptor;
67
93
  ```
68
94
 
69
- This auto-starts the daemon, sets up the proxy, and configures your shell to route HTTP traffic through htpx.
95
+ ### Modify
70
96
 
71
- ### Browse Captured Traffic
97
+ Forward to upstream, then alter the response:
72
98
 
73
- ```bash
74
- # View all requests
75
- htpx tui
99
+ ```typescript
100
+ import type { Interceptor } from "htpx-cli/interceptors";
76
101
 
77
- # Filter by session label
78
- htpx tui --label api-tests
102
+ export default {
103
+ name: "inject-header",
104
+ match: (req) => req.host.includes("example.com"),
105
+ handler: async (ctx) => {
106
+ const response = await ctx.forward();
107
+ return { ...response, headers: { ...response.headers, "x-debug": "htpx" } };
108
+ },
109
+ } satisfies Interceptor;
79
110
  ```
80
111
 
81
- ### TUI Keybindings
112
+ ### Observe
82
113
 
83
- | Key | Action |
84
- |-----|--------|
85
- | `j` / `↓` | Navigate down |
86
- | `k` / `↑` | Navigate up |
87
- | `Tab` | Switch between list and details panes |
88
- | `c` | Export selected request as curl command |
89
- | `h` | Export all requests as HAR file |
90
- | `r` | Refresh request list |
91
- | `q` | Quit |
114
+ Log traffic without altering it:
92
115
 
93
- ### Other Commands
116
+ ```typescript
117
+ import type { Interceptor } from "htpx-cli/interceptors";
118
+
119
+ export default {
120
+ name: "log-api",
121
+ match: (req) => req.path.startsWith("/api/"),
122
+ handler: async (ctx) => {
123
+ ctx.log(`${ctx.request.method} ${ctx.request.url}`);
124
+ const response = await ctx.forward();
125
+ ctx.log(` -> ${response.status}`);
126
+ return response;
127
+ },
128
+ } satisfies Interceptor;
129
+ ```
130
+
131
+ ### Query Past Traffic
132
+
133
+ Interceptors can query the traffic database via `ctx.htpx`. This lets you build mocks that react to what's already happened — rate limiting, conditional failures, responses based on prior requests:
134
+
135
+ ```typescript
136
+ import type { Interceptor } from "htpx-cli/interceptors";
137
+
138
+ export default {
139
+ name: "rate-limit",
140
+ match: (req) => req.path.startsWith("/api/"),
141
+ handler: async (ctx) => {
142
+ // Count how many requests this endpoint has seen in the last minute
143
+ const since = new Date(Date.now() - 60_000).toISOString();
144
+ const count = await ctx.htpx.countRequests({
145
+ path: ctx.request.path,
146
+ since,
147
+ });
148
+
149
+ if (count >= 10) {
150
+ return {
151
+ status: 429,
152
+ headers: { "retry-after": "60" },
153
+ body: JSON.stringify({ error: "rate_limited" }),
154
+ };
155
+ }
156
+
157
+ return ctx.forward();
158
+ },
159
+ } satisfies Interceptor;
160
+ ```
161
+
162
+ ### Handler Context
163
+
164
+ | Property | Description |
165
+ |----------|-------------|
166
+ | `ctx.request` | The incoming request (frozen, read-only) |
167
+ | `ctx.forward()` | Forward to upstream, returns the response |
168
+ | `ctx.htpx` | Query captured traffic (see below) |
169
+ | `ctx.log(msg)` | Write to `.htpx/htpx.log` |
170
+
171
+ #### `ctx.htpx`
172
+
173
+ | Method | Description |
174
+ |--------|-------------|
175
+ | `countRequests(filter?)` | Count matching requests |
176
+ | `listRequests({ filter?, limit?, offset? })` | List request summaries |
177
+ | `getRequest(id)` | Full request details by ID |
178
+ | `searchBodies({ query, ...filter? })` | Full-text search through bodies |
179
+ | `queryJsonBodies({ json_path, ...filter? })` | Extract values from JSON bodies via JSONPath |
180
+
181
+ ### How Interceptors Work
182
+
183
+ - Any `.ts` file in `.htpx/interceptors/` is loaded automatically
184
+ - Files load alphabetically; first match wins
185
+ - `match` is optional — omit it to match everything
186
+ - Hot-reloads on file changes, or run `htpx interceptors reload`
187
+ - 30s handler timeout, 5s match timeout
188
+ - Errors fall through gracefully (never crashes the proxy)
189
+ - `ctx.log()` writes to `.htpx/htpx.log` since `console.log` goes nowhere in the daemon
190
+ - Use `satisfies Interceptor` for full intellisense
191
+
192
+ ## MCP Integration
193
+
194
+ htpx has a built-in [MCP](https://modelcontextprotocol.io/) server that gives AI agents full access to your captured traffic and interceptor system. Agents can search through requests, inspect headers and bodies, and write interceptor files directly into `.htpx/interceptors/`.
195
+
196
+ This means you can ask things like:
197
+
198
+ - "Find all failing requests to the payments API and write mocks that return valid responses"
199
+ - "Make every 5th request to /api/users return a 429 so I can test rate limiting"
200
+ - "What's the average response time for requests to the auth service in the last hour?"
201
+ - "Write an interceptor that logs all requests with missing auth headers"
202
+
203
+ The agent reads your traffic, writes the TypeScript, and htpx hot-reloads it.
204
+
205
+ ### Setup
206
+
207
+ Add htpx to your MCP client config:
208
+
209
+ ```json
210
+ {
211
+ "mcpServers": {
212
+ "htpx": {
213
+ "command": "htpx",
214
+ "args": ["mcp"]
215
+ }
216
+ }
217
+ }
218
+ ```
219
+
220
+ `htpx on` must be running — the MCP server connects to the same daemon as the TUI.
221
+
222
+ ### Agent Skill
223
+
224
+ htpx also ships an agent skill that teaches AI assistants how to use the MCP tools properly. Gets you better results out of the box.
225
+
226
+ **Claude Code:**
227
+
228
+ ```bash
229
+ /plugin marketplace add mtford90/htpx
230
+ /plugin install htpx
231
+ ```
232
+
233
+ **npm-agentskills** (works with Cursor, Copilot, Codex, etc.):
94
234
 
95
235
  ```bash
96
- htpx status # Check daemon status
97
- htpx stop # Stop the daemon
98
- htpx clear # Clear captured requests
236
+ npx agents export --target claude
237
+ ```
238
+
239
+ ### Available Tools
240
+
241
+ | Tool | Description |
242
+ |------|-------------|
243
+ | `htpx_get_status` | Daemon status, proxy port, request count |
244
+ | `htpx_list_requests` | Search and filter captured requests |
245
+ | `htpx_get_request` | Full request details by ID (headers, bodies, timing) |
246
+ | `htpx_search_bodies` | Full-text search through body content |
247
+ | `htpx_query_json` | Extract values from JSON bodies via JSONPath |
248
+ | `htpx_count_requests` | Count matching requests |
249
+ | `htpx_clear_requests` | Delete all captured requests |
250
+ | `htpx_list_sessions` | List active proxy sessions |
251
+ | `htpx_list_interceptors` | List loaded interceptors with status and errors |
252
+ | `htpx_reload_interceptors` | Reload interceptors from disk |
253
+
254
+ ### Filtering
255
+
256
+ Most tools accept these filters:
257
+
258
+ | Parameter | Description | Example |
259
+ |-----------|-------------|---------|
260
+ | `method` | HTTP method(s), comma-separated | `"GET,POST"` |
261
+ | `status_range` | Status code, Nxx pattern, or range | `"4xx"`, `"401"`, `"500-503"` |
262
+ | `search` | Substring match on URL/path | `"api/users"` |
263
+ | `host` | Exact or suffix match (prefix with `.`) | `"api.example.com"`, `".example.com"` |
264
+ | `path` | Path prefix match | `"/api/v2"` |
265
+ | `since` / `before` | Time window (ISO 8601) | `"2024-01-15T10:30:00Z"` |
266
+ | `header_name` | Filter by header existence or value | `"content-type"` |
267
+ | `header_value` | Exact header value (requires `header_name`) | `"application/json"` |
268
+ | `header_target` | Which headers to search | `"request"`, `"response"`, `"both"` |
269
+ | `intercepted_by` | Filter by interceptor name | `"mock-users"` |
270
+ | `offset` | Pagination offset (0-based) | `0` |
271
+ | `limit` | Max results (default 50, max 500) | `100` |
272
+
273
+ `htpx_get_request` accepts comma-separated IDs for batch fetching (e.g. `"id1,id2,id3"`).
274
+
275
+ `htpx_query_json` also takes:
276
+
277
+ | Parameter | Description | Example |
278
+ |-----------|-------------|---------|
279
+ | `target` | Which body to query: `"request"`, `"response"`, or `"both"` (default) | `"response"` |
280
+ | `value` | Exact value match after JSONPath extraction | `"active"` |
281
+
282
+ ### Output Formats
283
+
284
+ All query tools accept a `format` parameter:
285
+
286
+ - `text` (default) — markdown summaries, readable by humans and AI
287
+ - `json` — structured JSON for programmatic use
288
+
289
+ ### Examples
290
+
291
+ ```
292
+ htpx_list_requests({ status_range: "5xx", path: "/api" })
293
+ htpx_search_bodies({ query: "error_code", method: "POST" })
294
+ htpx_query_json({ json_path: "$.user.id", target: "response" })
295
+ htpx_list_requests({ header_name: "authorization", header_target: "request" })
99
296
  ```
100
297
 
101
298
  ## How It Works
@@ -133,49 +330,60 @@ htpx clear # Clear captured requests
133
330
  └─────────────────────────────────────────────────────────────┘
134
331
  ```
135
332
 
136
- When you run `htpx intercept`:
333
+ `htpx on` starts a daemon, sets `HTTP_PROXY`/`HTTPS_PROXY` in your shell, and captures everything that flows through. `htpx off` unsets them. The TUI connects to the daemon via Unix socket.
137
334
 
138
- 1. A daemon starts in the background with a MITM proxy
139
- 2. Environment variables (`HTTP_PROXY`, `HTTPS_PROXY`, etc.) are set in your shell
140
- 3. HTTP clients that respect these variables route traffic through the proxy
141
- 4. Requests are captured and stored in a local SQLite database
142
- 5. The TUI connects via Unix socket to display captured traffic
335
+ ### Environment Variables
143
336
 
144
- ### Project Isolation
337
+ `htpx on` sets these in your shell (`htpx off` unsets them):
145
338
 
146
- htpx creates a `.htpx/` directory in your project root (detected by `.git` or existing `.htpx`):
339
+ | Variable | Purpose |
340
+ |----------|---------|
341
+ | `HTTP_PROXY` | Proxy URL for HTTP clients |
342
+ | `HTTPS_PROXY` | Proxy URL for HTTPS clients |
343
+ | `SSL_CERT_FILE` | CA cert path (curl, git, etc.) |
344
+ | `REQUESTS_CA_BUNDLE` | CA cert path (Python requests) |
345
+ | `NODE_EXTRA_CA_CERTS` | CA cert path (Node.js) |
346
+ | `HTPX_SESSION_ID` | UUID identifying the current session |
347
+ | `HTPX_LABEL` | Session label (when `-l` flag used) |
147
348
 
148
- ```
149
- your-project/
150
- ├── .htpx/
151
- │ ├── proxy.port # Proxy TCP port
152
- │ ├── control.sock # IPC socket
153
- │ ├── requests.db # Captured traffic
154
- │ ├── ca.pem # CA certificate
155
- │ └── daemon.pid # Process ID
156
- └── src/...
349
+ ## Configuration
350
+
351
+ Create `.htpx/config.json` to override defaults. All fields are optional:
352
+
353
+ ```json
354
+ {
355
+ "maxStoredRequests": 5000,
356
+ "maxBodySize": 10485760,
357
+ "maxLogSize": 10485760,
358
+ "pollInterval": 2000
359
+ }
157
360
  ```
158
361
 
159
- Different projects have completely separate daemons, databases, and certificates.
362
+ | Setting | Default | Description |
363
+ |---------|---------|-------------|
364
+ | `maxStoredRequests` | `5000` | Max requests in the database. Oldest evicted automatically. |
365
+ | `maxBodySize` | `10485760` (10 MB) | Max body size to capture. Larger bodies are proxied but not stored. |
366
+ | `maxLogSize` | `10485760` (10 MB) | Max log file size before rotation. |
367
+ | `pollInterval` | `2000` | TUI polling interval in ms. Lower = faster updates, more IPC traffic. |
368
+
369
+ Missing or invalid values fall back to defaults.
160
370
 
161
371
  ## Supported HTTP Clients
162
372
 
163
- htpx works with any client that respects the `HTTP_PROXY` environment variable:
373
+ Anything that respects `HTTP_PROXY` works:
164
374
 
165
375
  | Client | Support |
166
376
  |--------|---------|
167
- | curl | Automatic |
168
- | wget | Automatic |
169
- | Node.js (fetch, axios, etc.) | With `NODE_EXTRA_CA_CERTS` |
170
- | Python (requests, httpx) | With `REQUESTS_CA_BUNDLE` |
171
- | Go | Automatic |
172
- | Rust (reqwest) | Automatic |
173
-
174
- ## Export Formats
377
+ | curl | Automatic |
378
+ | wget | Automatic |
379
+ | Node.js (fetch, axios, etc.) | With `NODE_EXTRA_CA_CERTS` |
380
+ | Python (requests, httpx) | With `REQUESTS_CA_BUNDLE` |
381
+ | Go | Automatic |
382
+ | Rust (reqwest) | Automatic |
175
383
 
176
- ### curl
384
+ ## Export
177
385
 
178
- Press `c` in the TUI to copy a request as a curl command:
386
+ Press `c` to copy a request as curl:
179
387
 
180
388
  ```bash
181
389
  curl -X POST 'https://api.example.com/users' \
@@ -184,94 +392,192 @@ curl -X POST 'https://api.example.com/users' \
184
392
  -d '{"name": "test"}'
185
393
  ```
186
394
 
187
- ### HAR
395
+ Press `H` to export all requests as a HAR file. Compatible with browser dev tools.
396
+
397
+ Press `s` on a body to open the export modal — clipboard, `.htpx/exports/`, `~/Downloads/`, custom path, or open in default application.
398
+
399
+ ## TUI Keybindings
400
+
401
+ `j`/`k` to navigate, `Tab` to switch panels, `/` to filter, `c` to copy as curl, `Enter` to inspect bodies, `q` to quit.
402
+
403
+ Mouse support: click to select, scroll to navigate, click panels to focus.
188
404
 
189
- Press `h` to export all captured requests as a HAR file, compatible with browser dev tools and HTTP analysis tools.
405
+ <details>
406
+ <summary>Full keybinding reference</summary>
407
+
408
+ ### Main View
409
+
410
+ | Key | Action |
411
+ |-----|--------|
412
+ | `j`/`k` or `↑`/`↓` | Navigate up/down |
413
+ | `g` / `G` | Jump to first / last item |
414
+ | `Ctrl+u` / `Ctrl+d` | Half-page up / down |
415
+ | `Ctrl+f` / `Ctrl+b` | Full-page down / up |
416
+ | `Tab` / `Shift+Tab` | Next / previous panel |
417
+ | `1`-`5` | Jump to section (list / request / request body / response / response body) |
418
+ | `Enter` | Open body in full-screen viewer |
419
+ | `/` | Open filter bar |
420
+ | `u` | Toggle full URL display |
421
+ | `c` | Copy request as curl |
422
+ | `y` | Copy body to clipboard |
423
+ | `s` | Export body (opens export modal) |
424
+ | `H` | Export all as HAR |
425
+ | `r` | Refresh |
426
+ | `?` | Help |
427
+ | `i` | Proxy connection info |
428
+ | `q` | Quit |
429
+
430
+ ### Filter Bar (`/`)
431
+
432
+ | Key | Action |
433
+ |-----|--------|
434
+ | `Tab` / `Shift+Tab` | Cycle between search, method, status fields |
435
+ | `←` / `→` | Cycle method (ALL/GET/POST/PUT/PATCH/DELETE) or status (ALL/2xx-5xx) |
436
+ | `Return` | Apply filter |
437
+ | `Esc` | Cancel and revert |
438
+
439
+ ### JSON Explorer (Enter on a JSON body)
440
+
441
+ | Key | Action |
442
+ |-----|--------|
443
+ | `j`/`k` | Navigate nodes |
444
+ | `Enter`/`l` | Expand/collapse node |
445
+ | `h` | Collapse node |
446
+ | `e` / `c` | Expand / collapse all |
447
+ | `/` | Filter by path |
448
+ | `n` / `N` | Next / previous match |
449
+ | `y` | Copy value |
450
+ | `q` / `Esc` | Close |
451
+
452
+ ### Text Viewer (Enter on a non-JSON body)
453
+
454
+ | Key | Action |
455
+ |-----|--------|
456
+ | `j`/`k` | Scroll line by line |
457
+ | `Space` | Page down |
458
+ | `g` / `G` | Top / bottom |
459
+ | `/` | Search text |
460
+ | `n` / `N` | Next / previous match |
461
+ | `y` | Copy to clipboard |
462
+ | `q` / `Esc` | Close |
463
+
464
+ </details>
190
465
 
191
466
  ## CLI Reference
192
467
 
468
+ ### Global Options
469
+
470
+ | Flag | Description |
471
+ |------|-------------|
472
+ | `-v, --verbose` | Increase log verbosity (stackable: `-vv`, `-vvv`) |
473
+ | `-d, --dir <path>` | Override project root directory |
474
+
193
475
  ### `htpx init`
194
476
 
195
477
  Output shell configuration for your `.zshrc`/`.bashrc`.
196
478
 
197
- ### `htpx intercept [options]`
479
+ ### `htpx on`
198
480
 
199
- Start intercepting HTTP traffic.
481
+ Start intercepting HTTP traffic. Sets proxy environment variables in your shell.
200
482
 
201
- | Option | Description |
202
- |--------|-------------|
203
- | `-l, --label <label>` | Label this session for filtering |
483
+ | Flag | Description |
484
+ |------|-------------|
485
+ | `-l, --label <label>` | Label this session (visible in TUI and MCP) |
486
+ | `--no-restart` | Don't auto-restart daemon on version mismatch |
204
487
 
205
- ### `htpx tui [options]`
488
+ ### `htpx off`
489
+
490
+ Stop intercepting HTTP traffic. Unsets proxy environment variables.
491
+
492
+ ### `htpx tui`
206
493
 
207
494
  Open the interactive TUI.
208
495
 
209
- | Option | Description |
210
- |--------|-------------|
211
- | `-l, --label <label>` | Filter to requests from labelled sessions |
496
+ | Flag | Description |
497
+ |------|-------------|
498
+ | `--ci` | CI mode: render once and exit (for testing) |
212
499
 
213
500
  ### `htpx status`
214
501
 
215
- Show daemon status, including proxy port, active sessions, and request count.
502
+ Show comprehensive status: daemon state, interception state, sessions, request count, loaded interceptors.
503
+
504
+ ### `htpx daemon stop`
216
505
 
217
- ### `htpx stop`
506
+ Stop the daemon.
218
507
 
219
- Stop the daemon gracefully.
508
+ ### `htpx daemon restart`
509
+
510
+ Restart the daemon (or start it if not running).
220
511
 
221
512
  ### `htpx clear`
222
513
 
223
- Clear all captured requests from the database.
514
+ Clear all captured requests.
515
+
516
+ ### `htpx debug-dump`
517
+
518
+ Collect diagnostics (system info, daemon status, recent logs) into `.htpx/debug-dump-<timestamp>.json`.
519
+
520
+ ### `htpx mcp`
521
+
522
+ Start the MCP server (stdio transport). See [MCP Integration](#mcp-integration).
224
523
 
225
524
  ### `htpx project init`
226
525
 
227
526
  Manually initialise a `.htpx` directory in the current location.
228
527
 
528
+ ### `htpx interceptors`
529
+
530
+ List loaded interceptors, or manage them with subcommands.
531
+
532
+ ### `htpx interceptors init`
533
+
534
+ Scaffold an example interceptor in `.htpx/interceptors/`.
535
+
536
+ ### `htpx interceptors reload`
537
+
538
+ Reload interceptors from disk without restarting the daemon.
539
+
229
540
  ## Development
230
541
 
231
542
  ```bash
232
- # Clone and install
233
543
  git clone https://github.com/mtford90/htpx.git
234
544
  cd htpx
235
545
  npm install
236
546
 
237
- # Build
238
- npm run build
239
-
240
- # Run tests
241
- npm test
242
-
243
- # Type check and lint
244
- npm run typecheck
245
- npm run lint
246
-
247
- # Development mode (watch)
248
- npm run dev
547
+ npm run build # Compile TypeScript
548
+ npm test # Run all tests
549
+ npm run typecheck # Type checking only
550
+ npm run lint # ESLint
551
+ npm run dev # Watch mode
249
552
  ```
250
553
 
251
554
  ## Troubleshooting
252
555
 
253
556
  ### Certificate errors
254
557
 
255
- Some tools need explicit CA certificate configuration. htpx sets common environment variables automatically, but you may need to configure your specific tool:
558
+ htpx sets common CA environment variables automatically, but some tools need manual configuration:
256
559
 
257
560
  ```bash
258
- # The CA certificate is at:
259
561
  cat .htpx/ca.pem
260
562
  ```
261
563
 
262
564
  ### Daemon won't start
263
565
 
264
- Check if another process is using the socket:
566
+ Check if something else is using the socket:
265
567
 
266
568
  ```bash
267
569
  htpx status
268
- htpx stop
269
- htpx intercept
570
+ htpx daemon stop
571
+ htpx on
270
572
  ```
271
573
 
574
+ ### Terminal too small
575
+
576
+ The TUI needs at least 60 columns by 10 rows.
577
+
272
578
  ### Requests not appearing
273
579
 
274
- Ensure your HTTP client respects proxy environment variables. Some clients (like browsers) ignore `HTTP_PROXY` by default.
580
+ Your HTTP client needs to respect proxy environment variables. Browsers typically don't use curl, wget, or language-level HTTP clients instead.
275
581
 
276
582
  ## Licence
277
583
 
@@ -1 +1 @@
1
- {"version":3,"file":"clear.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/clear.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,eAAO,MAAM,YAAY,SA0BrB,CAAC"}
1
+ {"version":3,"file":"clear.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/clear.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,YAAY,SAuBrB,CAAC"}