htpx-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +274 -0
- package/dist/cli/commands/clear.d.ts +3 -0
- package/dist/cli/commands/clear.d.ts.map +1 -0
- package/dist/cli/commands/clear.js +30 -0
- package/dist/cli/commands/clear.js.map +1 -0
- package/dist/cli/commands/init.d.ts +9 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +25 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/intercept.d.ts +8 -0
- package/dist/cli/commands/intercept.d.ts.map +1 -0
- package/dist/cli/commands/intercept.js +59 -0
- package/dist/cli/commands/intercept.js.map +1 -0
- package/dist/cli/commands/project.d.ts +3 -0
- package/dist/cli/commands/project.d.ts.map +1 -0
- package/dist/cli/commands/project.js +13 -0
- package/dist/cli/commands/project.js.map +1 -0
- package/dist/cli/commands/status.d.ts +3 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +36 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +3 -0
- package/dist/cli/commands/stop.d.ts.map +1 -0
- package/dist/cli/commands/stop.js +27 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/commands/tui.d.ts +3 -0
- package/dist/cli/commands/tui.d.ts.map +1 -0
- package/dist/cli/commands/tui.js +27 -0
- package/dist/cli/commands/tui.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +20 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/tui/App.d.ts +10 -0
- package/dist/cli/tui/App.d.ts.map +1 -0
- package/dist/cli/tui/App.js +96 -0
- package/dist/cli/tui/App.js.map +1 -0
- package/dist/cli/tui/components/BodyView.d.ts +14 -0
- package/dist/cli/tui/components/BodyView.d.ts.map +1 -0
- package/dist/cli/tui/components/BodyView.js +39 -0
- package/dist/cli/tui/components/BodyView.js.map +1 -0
- package/dist/cli/tui/components/HeadersView.d.ts +13 -0
- package/dist/cli/tui/components/HeadersView.d.ts.map +1 -0
- package/dist/cli/tui/components/HeadersView.js +8 -0
- package/dist/cli/tui/components/HeadersView.js.map +1 -0
- package/dist/cli/tui/components/RequestDetails.d.ts +13 -0
- package/dist/cli/tui/components/RequestDetails.d.ts.map +1 -0
- package/dist/cli/tui/components/RequestDetails.js +19 -0
- package/dist/cli/tui/components/RequestDetails.js.map +1 -0
- package/dist/cli/tui/components/RequestList.d.ts +15 -0
- package/dist/cli/tui/components/RequestList.d.ts.map +1 -0
- package/dist/cli/tui/components/RequestList.js +17 -0
- package/dist/cli/tui/components/RequestList.js.map +1 -0
- package/dist/cli/tui/components/RequestListItem.d.ts +13 -0
- package/dist/cli/tui/components/RequestListItem.d.ts.map +1 -0
- package/dist/cli/tui/components/RequestListItem.js +53 -0
- package/dist/cli/tui/components/RequestListItem.js.map +1 -0
- package/dist/cli/tui/components/StatusBar.d.ts +10 -0
- package/dist/cli/tui/components/StatusBar.d.ts.map +1 -0
- package/dist/cli/tui/components/StatusBar.js +18 -0
- package/dist/cli/tui/components/StatusBar.js.map +1 -0
- package/dist/cli/tui/hooks/useExport.d.ts +18 -0
- package/dist/cli/tui/hooks/useExport.d.ts.map +1 -0
- package/dist/cli/tui/hooks/useExport.js +58 -0
- package/dist/cli/tui/hooks/useExport.js.map +1 -0
- package/dist/cli/tui/hooks/useRequests.d.ts +20 -0
- package/dist/cli/tui/hooks/useRequests.d.ts.map +1 -0
- package/dist/cli/tui/hooks/useRequests.js +85 -0
- package/dist/cli/tui/hooks/useRequests.js.map +1 -0
- package/dist/cli/tui/hooks/useStdoutDimensions.d.ts +11 -0
- package/dist/cli/tui/hooks/useStdoutDimensions.d.ts.map +1 -0
- package/dist/cli/tui/hooks/useStdoutDimensions.js +29 -0
- package/dist/cli/tui/hooks/useStdoutDimensions.js.map +1 -0
- package/dist/cli/tui/utils/curl.d.ts +9 -0
- package/dist/cli/tui/utils/curl.d.ts.map +1 -0
- package/dist/cli/tui/utils/curl.js +47 -0
- package/dist/cli/tui/utils/curl.js.map +1 -0
- package/dist/cli/tui/utils/formatters.d.ts +36 -0
- package/dist/cli/tui/utils/formatters.d.ts.map +1 -0
- package/dist/cli/tui/utils/formatters.js +108 -0
- package/dist/cli/tui/utils/formatters.js.map +1 -0
- package/dist/cli/tui/utils/har.d.ts +75 -0
- package/dist/cli/tui/utils/har.d.ts.map +1 -0
- package/dist/cli/tui/utils/har.js +139 -0
- package/dist/cli/tui/utils/har.js.map +1 -0
- package/dist/daemon/control.d.ts +63 -0
- package/dist/daemon/control.d.ts.map +1 -0
- package/dist/daemon/control.js +260 -0
- package/dist/daemon/control.js.map +1 -0
- package/dist/daemon/index.d.ts +3 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +107 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/proxy.d.ts +19 -0
- package/dist/daemon/proxy.d.ts.map +1 -0
- package/dist/daemon/proxy.js +89 -0
- package/dist/daemon/proxy.js.map +1 -0
- package/dist/daemon/storage.d.ts +60 -0
- package/dist/daemon/storage.d.ts.map +1 -0
- package/dist/daemon/storage.js +215 -0
- package/dist/daemon/storage.js.map +1 -0
- package/dist/shared/daemon.d.ts +14 -0
- package/dist/shared/daemon.d.ts.map +1 -0
- package/dist/shared/daemon.js +161 -0
- package/dist/shared/daemon.js.map +1 -0
- package/dist/shared/project.d.ts +61 -0
- package/dist/shared/project.d.ts.map +1 -0
- package/dist/shared/project.js +143 -0
- package/dist/shared/project.js.map +1 -0
- package/dist/shared/types.d.ts +32 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +5 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import * as net from "node:net";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
/**
|
|
4
|
+
* Create a Unix socket control server for daemon communication.
|
|
5
|
+
*/
|
|
6
|
+
export function createControlServer(options) {
|
|
7
|
+
const { socketPath, storage, proxyPort } = options;
|
|
8
|
+
// Remove existing socket file if it exists
|
|
9
|
+
if (fs.existsSync(socketPath)) {
|
|
10
|
+
fs.unlinkSync(socketPath);
|
|
11
|
+
}
|
|
12
|
+
const handlers = {
|
|
13
|
+
/**
|
|
14
|
+
* Get daemon status.
|
|
15
|
+
*/
|
|
16
|
+
status: () => {
|
|
17
|
+
const sessions = storage.listSessions();
|
|
18
|
+
const requestCount = storage.countRequests();
|
|
19
|
+
return {
|
|
20
|
+
running: true,
|
|
21
|
+
proxyPort,
|
|
22
|
+
sessionCount: sessions.length,
|
|
23
|
+
requestCount,
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
/**
|
|
27
|
+
* Register a new session.
|
|
28
|
+
*/
|
|
29
|
+
registerSession: (params) => {
|
|
30
|
+
const label = params["label"];
|
|
31
|
+
const pid = params["pid"];
|
|
32
|
+
return storage.registerSession(label, pid);
|
|
33
|
+
},
|
|
34
|
+
/**
|
|
35
|
+
* List all sessions.
|
|
36
|
+
*/
|
|
37
|
+
listSessions: () => {
|
|
38
|
+
return storage.listSessions();
|
|
39
|
+
},
|
|
40
|
+
/**
|
|
41
|
+
* List captured requests.
|
|
42
|
+
*/
|
|
43
|
+
listRequests: (params) => {
|
|
44
|
+
return storage.listRequests({
|
|
45
|
+
sessionId: params["sessionId"],
|
|
46
|
+
label: params["label"],
|
|
47
|
+
limit: params["limit"],
|
|
48
|
+
offset: params["offset"],
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
/**
|
|
52
|
+
* Get a specific request by ID.
|
|
53
|
+
*/
|
|
54
|
+
getRequest: (params) => {
|
|
55
|
+
const id = params["id"];
|
|
56
|
+
return storage.getRequest(id) ?? null;
|
|
57
|
+
},
|
|
58
|
+
/**
|
|
59
|
+
* Count requests.
|
|
60
|
+
*/
|
|
61
|
+
countRequests: (params) => {
|
|
62
|
+
return storage.countRequests({
|
|
63
|
+
sessionId: params["sessionId"],
|
|
64
|
+
label: params["label"],
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
/**
|
|
68
|
+
* Clear all requests.
|
|
69
|
+
*/
|
|
70
|
+
clearRequests: () => {
|
|
71
|
+
storage.clearRequests();
|
|
72
|
+
return { success: true };
|
|
73
|
+
},
|
|
74
|
+
/**
|
|
75
|
+
* Ping - used for health checks.
|
|
76
|
+
*/
|
|
77
|
+
ping: () => {
|
|
78
|
+
return { pong: true };
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
const server = net.createServer((socket) => {
|
|
82
|
+
let buffer = "";
|
|
83
|
+
socket.on("data", (data) => {
|
|
84
|
+
buffer += data.toString();
|
|
85
|
+
// Process complete messages (newline-delimited JSON)
|
|
86
|
+
let newlineIndex;
|
|
87
|
+
while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
|
|
88
|
+
const messageStr = buffer.slice(0, newlineIndex);
|
|
89
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
90
|
+
try {
|
|
91
|
+
const message = JSON.parse(messageStr);
|
|
92
|
+
const response = handleMessage(message, handlers);
|
|
93
|
+
socket.write(JSON.stringify(response) + "\n");
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
const errorResponse = {
|
|
97
|
+
id: "unknown",
|
|
98
|
+
error: {
|
|
99
|
+
code: -32700,
|
|
100
|
+
message: `Parse error: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
socket.write(JSON.stringify(errorResponse) + "\n");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
socket.on("error", (err) => {
|
|
108
|
+
// Log but don't crash on socket errors
|
|
109
|
+
console.error("Control socket error:", err.message);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
server.listen(socketPath);
|
|
113
|
+
// Set socket permissions to be accessible only by owner
|
|
114
|
+
fs.chmodSync(socketPath, 0o600);
|
|
115
|
+
return {
|
|
116
|
+
close: () => {
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
server.close((err) => {
|
|
119
|
+
if (err) {
|
|
120
|
+
reject(err);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Clean up socket file
|
|
124
|
+
if (fs.existsSync(socketPath)) {
|
|
125
|
+
fs.unlinkSync(socketPath);
|
|
126
|
+
}
|
|
127
|
+
resolve();
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function handleMessage(message, handlers) {
|
|
135
|
+
const { id, method, params } = message;
|
|
136
|
+
const handler = handlers[method];
|
|
137
|
+
if (!handler) {
|
|
138
|
+
return {
|
|
139
|
+
id,
|
|
140
|
+
error: {
|
|
141
|
+
code: -32601,
|
|
142
|
+
message: `Method not found: ${method}`,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const result = handler(params ?? {});
|
|
148
|
+
return { id, result };
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
return {
|
|
152
|
+
id,
|
|
153
|
+
error: {
|
|
154
|
+
code: -32000,
|
|
155
|
+
message: err instanceof Error ? err.message : "Unknown error",
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Client for communicating with the control server.
|
|
162
|
+
*/
|
|
163
|
+
export class ControlClient {
|
|
164
|
+
socketPath;
|
|
165
|
+
requestId = 0;
|
|
166
|
+
constructor(socketPath) {
|
|
167
|
+
this.socketPath = socketPath;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Send a request to the control server and wait for response.
|
|
171
|
+
*/
|
|
172
|
+
async request(method, params) {
|
|
173
|
+
return new Promise((resolve, reject) => {
|
|
174
|
+
const socket = net.createConnection(this.socketPath);
|
|
175
|
+
const id = String(++this.requestId);
|
|
176
|
+
let buffer = "";
|
|
177
|
+
socket.on("connect", () => {
|
|
178
|
+
const message = { id, method, params };
|
|
179
|
+
socket.write(JSON.stringify(message) + "\n");
|
|
180
|
+
});
|
|
181
|
+
socket.on("data", (data) => {
|
|
182
|
+
buffer += data.toString();
|
|
183
|
+
const newlineIndex = buffer.indexOf("\n");
|
|
184
|
+
if (newlineIndex !== -1) {
|
|
185
|
+
const responseStr = buffer.slice(0, newlineIndex);
|
|
186
|
+
try {
|
|
187
|
+
const response = JSON.parse(responseStr);
|
|
188
|
+
socket.end();
|
|
189
|
+
if (response.error) {
|
|
190
|
+
reject(new Error(response.error.message));
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
resolve(response.result);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
socket.end();
|
|
198
|
+
reject(err);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
socket.on("error", (err) => {
|
|
203
|
+
reject(err);
|
|
204
|
+
});
|
|
205
|
+
socket.setTimeout(5000, () => {
|
|
206
|
+
socket.destroy();
|
|
207
|
+
reject(new Error("Control request timed out"));
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Check if the daemon is running by sending a ping.
|
|
213
|
+
*/
|
|
214
|
+
async ping() {
|
|
215
|
+
try {
|
|
216
|
+
await this.request("ping");
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Get daemon status.
|
|
225
|
+
*/
|
|
226
|
+
async status() {
|
|
227
|
+
return this.request("status");
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Register a new session.
|
|
231
|
+
*/
|
|
232
|
+
async registerSession(label, pid) {
|
|
233
|
+
return this.request("registerSession", { label, pid });
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* List captured requests.
|
|
237
|
+
*/
|
|
238
|
+
async listRequests(options) {
|
|
239
|
+
return this.request("listRequests", options);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Get a specific request by ID.
|
|
243
|
+
*/
|
|
244
|
+
async getRequest(id) {
|
|
245
|
+
return this.request("getRequest", { id });
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Count requests.
|
|
249
|
+
*/
|
|
250
|
+
async countRequests(options) {
|
|
251
|
+
return this.request("countRequests", options);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Clear all requests.
|
|
255
|
+
*/
|
|
256
|
+
async clearRequests() {
|
|
257
|
+
await this.request("clearRequests");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control.js","sourceRoot":"","sources":["../../src/daemon/control.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AA+B9B;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA6B;IAC/D,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IAEnD,2CAA2C;IAC3C,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,QAAQ,GAAmC;QAC/C;;WAEG;QACH,MAAM,EAAE,GAAiB,EAAE;YACzB,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;YACxC,MAAM,YAAY,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;YAE7C,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,SAAS;gBACT,YAAY,EAAE,QAAQ,CAAC,MAAM;gBAC7B,YAAY;aACb,CAAC;QACJ,CAAC;QAED;;WAEG;QACH,eAAe,EAAE,CAAC,MAAM,EAAW,EAAE;YACnC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAuB,CAAC;YACpD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAuB,CAAC;YAChD,OAAO,OAAO,CAAC,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC7C,CAAC;QAED;;WAEG;QACH,YAAY,EAAE,GAAc,EAAE;YAC5B,OAAO,OAAO,CAAC,YAAY,EAAE,CAAC;QAChC,CAAC;QAED;;WAEG;QACH,YAAY,EAAE,CAAC,MAAM,EAAqB,EAAE;YAC1C,OAAO,OAAO,CAAC,YAAY,CAAC;gBAC1B,SAAS,EAAE,MAAM,CAAC,WAAW,CAAuB;gBACpD,KAAK,EAAE,MAAM,CAAC,OAAO,CAAuB;gBAC5C,KAAK,EAAE,MAAM,CAAC,OAAO,CAAuB;gBAC5C,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAuB;aAC/C,CAAC,CAAC;QACL,CAAC;QAED;;WAEG;QACH,UAAU,EAAE,CAAC,MAAM,EAA0B,EAAE;YAC7C,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAW,CAAC;YAClC,OAAO,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;QACxC,CAAC;QAED;;WAEG;QACH,aAAa,EAAE,CAAC,MAAM,EAAU,EAAE;YAChC,OAAO,OAAO,CAAC,aAAa,CAAC;gBAC3B,SAAS,EAAE,MAAM,CAAC,WAAW,CAAuB;gBACpD,KAAK,EAAE,MAAM,CAAC,OAAO,CAAuB;aAC7C,CAAC,CAAC;QACL,CAAC;QAED;;WAEG;QACH,aAAa,EAAE,GAAyB,EAAE;YACxC,OAAO,CAAC,aAAa,EAAE,CAAC;YACxB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC3B,CAAC;QAED;;WAEG;QACH,IAAI,EAAE,GAAsB,EAAE;YAC5B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACxB,CAAC;KACF,CAAC;IAEF,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;QACzC,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAE1B,qDAAqD;YACrD,IAAI,YAAoB,CAAC;YACzB,OAAO,CAAC,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;gBACpD,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;gBACjD,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;gBAExC,IAAI,CAAC;oBACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAmB,CAAC;oBACzD,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;oBAClD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;gBAChD,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,aAAa,GAAoB;wBACrC,EAAE,EAAE,SAAS;wBACb,KAAK,EAAE;4BACL,IAAI,EAAE,CAAC,KAAK;4BACZ,OAAO,EAAE,gBAAgB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE;yBAChF;qBACF,CAAC;oBACF,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,CAAC;gBACrD,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACzB,uCAAuC;YACvC,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAE1B,wDAAwD;IACxD,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAEhC,OAAO;QACL,KAAK,EAAE,GAAG,EAAE;YACV,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACrC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;oBACnB,IAAI,GAAG,EAAE,CAAC;wBACR,MAAM,CAAC,GAAG,CAAC,CAAC;oBACd,CAAC;yBAAM,CAAC;wBACN,uBAAuB;wBACvB,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;4BAC9B,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;wBAC5B,CAAC;wBACD,OAAO,EAAE,CAAC;oBACZ,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CACpB,OAAuB,EACvB,QAAwC;IAExC,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAEvC,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;YACL,EAAE;YACF,KAAK,EAAE;gBACL,IAAI,EAAE,CAAC,KAAK;gBACZ,OAAO,EAAE,qBAAqB,MAAM,EAAE;aACvC;SACF,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;QACrC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;IACxB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE;YACF,KAAK,EAAE;gBACL,IAAI,EAAE,CAAC,KAAK;gBACZ,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;aAC9D;SACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,OAAO,aAAa;IAChB,UAAU,CAAS;IACnB,SAAS,GAAG,CAAC,CAAC;IAEtB,YAAY,UAAkB;QAC5B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,CAAI,MAAc,EAAE,MAAgC;QAC/D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACrD,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YACpC,IAAI,MAAM,GAAG,EAAE,CAAC;YAEhB,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;gBACxB,MAAM,OAAO,GAAmB,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;gBACvD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;YAC/C,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;gBACzB,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAE1B,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC1C,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;oBACxB,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;oBAElD,IAAI,CAAC;wBACH,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAoB,CAAC;wBAC5D,MAAM,CAAC,GAAG,EAAE,CAAC;wBAEb,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;4BACnB,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;wBAC5C,CAAC;6BAAM,CAAC;4BACN,OAAO,CAAC,QAAQ,CAAC,MAAW,CAAC,CAAC;wBAChC,CAAC;oBACH,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,MAAM,CAAC,GAAG,EAAE,CAAC;wBACb,MAAM,CAAC,GAAG,CAAC,CAAC;oBACd,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACzB,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE;gBAC3B,MAAM,CAAC,OAAO,EAAE,CAAC;gBACjB,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAC;YACjD,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,CAAoB,MAAM,CAAC,CAAC;YAC9C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM;QACV,OAAO,IAAI,CAAC,OAAO,CAAe,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,KAAc,EAAE,GAAY;QAChD,OAAO,IAAI,CAAC,OAAO,CAAU,iBAAiB,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAClE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAAC,OAKlB;QACC,OAAO,IAAI,CAAC,OAAO,CAAoB,cAAc,EAAE,OAAO,CAAC,CAAC;IAClE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,EAAU;QACzB,OAAO,IAAI,CAAC,OAAO,CAAyB,YAAY,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IACpE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,OAAgD;QAClE,OAAO,IAAI,CAAC,OAAO,CAAS,eAAe,EAAE,OAAO,CAAC,CAAC;IACxD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa;QACjB,MAAM,IAAI,CAAC,OAAO,CAAuB,eAAe,CAAC,CAAC;IAC5D,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as net from "node:net";
|
|
4
|
+
import { generateCACertificate } from "mockttp";
|
|
5
|
+
import { RequestRepository } from "./storage.js";
|
|
6
|
+
import { createProxy } from "./proxy.js";
|
|
7
|
+
import { createControlServer } from "./control.js";
|
|
8
|
+
import { getHtpxPaths, ensureHtpxDir, writeProxyPort, writeDaemonPid, removeDaemonPid, } from "../shared/project.js";
|
|
9
|
+
/**
|
|
10
|
+
* Daemon entry point.
|
|
11
|
+
* Expected to be spawned as a background process with PROJECT_ROOT env var set.
|
|
12
|
+
*/
|
|
13
|
+
async function main() {
|
|
14
|
+
const projectRoot = process.env["PROJECT_ROOT"];
|
|
15
|
+
if (!projectRoot) {
|
|
16
|
+
console.error("PROJECT_ROOT environment variable is required");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
// Ensure .htpx directory exists
|
|
20
|
+
ensureHtpxDir(projectRoot);
|
|
21
|
+
const paths = getHtpxPaths(projectRoot);
|
|
22
|
+
// Generate CA certificate if it doesn't exist
|
|
23
|
+
if (!fs.existsSync(paths.caCertFile) || !fs.existsSync(paths.caKeyFile)) {
|
|
24
|
+
console.log("Generating CA certificate...");
|
|
25
|
+
const ca = await generateCACertificate({
|
|
26
|
+
subject: { commonName: "htpx Local CA - DO NOT TRUST" },
|
|
27
|
+
});
|
|
28
|
+
fs.writeFileSync(paths.caKeyFile, ca.key);
|
|
29
|
+
fs.writeFileSync(paths.caCertFile, ca.cert);
|
|
30
|
+
// Restrict permissions on key file
|
|
31
|
+
fs.chmodSync(paths.caKeyFile, 0o600);
|
|
32
|
+
}
|
|
33
|
+
// Initialise storage
|
|
34
|
+
const storage = new RequestRepository(paths.databaseFile);
|
|
35
|
+
// Find a free port for the proxy
|
|
36
|
+
const proxyPort = await findFreePort();
|
|
37
|
+
// Start the proxy server
|
|
38
|
+
console.log(`Starting proxy on port ${proxyPort}...`);
|
|
39
|
+
const proxy = await createProxy({
|
|
40
|
+
port: proxyPort,
|
|
41
|
+
caKeyPath: paths.caKeyFile,
|
|
42
|
+
caCertPath: paths.caCertFile,
|
|
43
|
+
storage,
|
|
44
|
+
sessionId: "daemon", // Default session for unattributed requests
|
|
45
|
+
});
|
|
46
|
+
// Write proxy port to file
|
|
47
|
+
writeProxyPort(projectRoot, proxy.port);
|
|
48
|
+
// Start control server
|
|
49
|
+
console.log("Starting control server...");
|
|
50
|
+
const controlServer = createControlServer({
|
|
51
|
+
socketPath: paths.controlSocketFile,
|
|
52
|
+
storage,
|
|
53
|
+
proxyPort: proxy.port,
|
|
54
|
+
});
|
|
55
|
+
// Write PID file
|
|
56
|
+
writeDaemonPid(projectRoot, process.pid);
|
|
57
|
+
console.log(`htpx daemon started (PID: ${process.pid})`);
|
|
58
|
+
console.log(` Proxy: http://127.0.0.1:${proxy.port}`);
|
|
59
|
+
console.log(` Control: ${paths.controlSocketFile}`);
|
|
60
|
+
console.log(` CA cert: ${paths.caCertFile}`);
|
|
61
|
+
// Handle graceful shutdown
|
|
62
|
+
const shutdown = async (signal) => {
|
|
63
|
+
console.log(`\nReceived ${signal}, shutting down...`);
|
|
64
|
+
try {
|
|
65
|
+
await controlServer.close();
|
|
66
|
+
await proxy.stop();
|
|
67
|
+
storage.close();
|
|
68
|
+
removeDaemonPid(projectRoot);
|
|
69
|
+
// Clean up port file
|
|
70
|
+
if (fs.existsSync(paths.proxyPortFile)) {
|
|
71
|
+
fs.unlinkSync(paths.proxyPortFile);
|
|
72
|
+
}
|
|
73
|
+
console.log("Shutdown complete");
|
|
74
|
+
process.exit(0);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
console.error("Error during shutdown:", err);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
82
|
+
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Find a free port to use for the proxy.
|
|
86
|
+
*/
|
|
87
|
+
async function findFreePort() {
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const server = net.createServer();
|
|
90
|
+
server.listen(0, "127.0.0.1", () => {
|
|
91
|
+
const address = server.address();
|
|
92
|
+
if (address && typeof address === "object") {
|
|
93
|
+
const port = address.port;
|
|
94
|
+
server.close(() => resolve(port));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
reject(new Error("Failed to get server address"));
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
server.on("error", reject);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
main().catch((err) => {
|
|
104
|
+
console.error("Daemon error:", err);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
});
|
|
107
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RequestRepository } from "./storage.js";
|
|
2
|
+
export interface ProxyOptions {
|
|
3
|
+
port?: number;
|
|
4
|
+
caKeyPath: string;
|
|
5
|
+
caCertPath: string;
|
|
6
|
+
storage: RequestRepository;
|
|
7
|
+
sessionId: string;
|
|
8
|
+
label?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ProxyServer {
|
|
11
|
+
port: number;
|
|
12
|
+
url: string;
|
|
13
|
+
stop: () => Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create and start a MITM proxy server that captures all HTTP/HTTPS traffic.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createProxy(options: ProxyOptions): Promise<ProxyServer>;
|
|
19
|
+
//# sourceMappingURL=proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/daemon/proxy.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAetD,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAqF7E"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as mockttp from "mockttp";
|
|
2
|
+
/**
|
|
3
|
+
* Create and start a MITM proxy server that captures all HTTP/HTTPS traffic.
|
|
4
|
+
*/
|
|
5
|
+
export async function createProxy(options) {
|
|
6
|
+
const { storage, sessionId, label } = options;
|
|
7
|
+
// Map to track request info for response correlation
|
|
8
|
+
const requestInfo = new Map();
|
|
9
|
+
const server = mockttp.getLocal({
|
|
10
|
+
https: {
|
|
11
|
+
keyPath: options.caKeyPath,
|
|
12
|
+
certPath: options.caCertPath,
|
|
13
|
+
},
|
|
14
|
+
// Record traffic for potential debugging
|
|
15
|
+
recordTraffic: false,
|
|
16
|
+
});
|
|
17
|
+
await server.start(options.port);
|
|
18
|
+
// Set up passthrough rule that captures all traffic
|
|
19
|
+
await server.forAnyRequest().thenPassThrough({
|
|
20
|
+
// Ignore certificate errors when connecting to upstream servers
|
|
21
|
+
ignoreHostHttpsErrors: true,
|
|
22
|
+
beforeRequest: (request) => {
|
|
23
|
+
const timestamp = Date.now();
|
|
24
|
+
// Parse URL to extract host and path
|
|
25
|
+
const url = new URL(request.url);
|
|
26
|
+
// Convert headers to simple object
|
|
27
|
+
const headers = flattenHeaders(request.headers);
|
|
28
|
+
// Save request to storage and track the ID
|
|
29
|
+
const ourId = storage.saveRequest({
|
|
30
|
+
sessionId,
|
|
31
|
+
label,
|
|
32
|
+
timestamp,
|
|
33
|
+
method: request.method,
|
|
34
|
+
url: request.url,
|
|
35
|
+
host: url.host,
|
|
36
|
+
path: url.pathname + url.search,
|
|
37
|
+
requestHeaders: headers,
|
|
38
|
+
requestBody: request.body.buffer.length > 0 ? request.body.buffer : undefined,
|
|
39
|
+
});
|
|
40
|
+
// Store mapping from mockttp ID to our ID and timestamp
|
|
41
|
+
requestInfo.set(request.id, { ourId, timestamp });
|
|
42
|
+
// Return undefined to pass through without modification
|
|
43
|
+
return undefined;
|
|
44
|
+
},
|
|
45
|
+
beforeResponse: (response, request) => {
|
|
46
|
+
const info = requestInfo.get(request.id);
|
|
47
|
+
requestInfo.delete(request.id);
|
|
48
|
+
if (!info) {
|
|
49
|
+
// Request wasn't tracked (shouldn't happen, but handle gracefully)
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const durationMs = Date.now() - info.timestamp;
|
|
53
|
+
// Convert headers to simple object
|
|
54
|
+
const headers = flattenHeaders(response.headers);
|
|
55
|
+
// Update request with response data using our ID
|
|
56
|
+
storage.updateRequestResponse(info.ourId, {
|
|
57
|
+
status: response.statusCode,
|
|
58
|
+
headers,
|
|
59
|
+
body: response.body.buffer.length > 0 ? response.body.buffer : undefined,
|
|
60
|
+
durationMs,
|
|
61
|
+
});
|
|
62
|
+
// Return undefined to pass through without modification
|
|
63
|
+
return undefined;
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
port: server.port,
|
|
68
|
+
url: server.url,
|
|
69
|
+
stop: async () => {
|
|
70
|
+
await server.stop();
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Flatten mockttp headers (which may have array values) to simple string values.
|
|
76
|
+
*/
|
|
77
|
+
function flattenHeaders(headers) {
|
|
78
|
+
const result = {};
|
|
79
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
80
|
+
if (Array.isArray(value)) {
|
|
81
|
+
result[key] = value.join(", ");
|
|
82
|
+
}
|
|
83
|
+
else if (value !== undefined) {
|
|
84
|
+
result[key] = value;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/daemon/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAgCnC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAqB;IACrD,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAE9C,qDAAqD;IACrD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAgD,CAAC;IAE5E,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;QAC9B,KAAK,EAAE;YACL,OAAO,EAAE,OAAO,CAAC,SAAS;YAC1B,QAAQ,EAAE,OAAO,CAAC,UAAU;SAC7B;QACD,yCAAyC;QACzC,aAAa,EAAE,KAAK;KACrB,CAAC,CAAC;IAEH,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjC,oDAAoD;IACpD,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC,eAAe,CAAC;QAC3C,gEAAgE;QAChE,qBAAqB,EAAE,IAAI;QAE3B,aAAa,EAAE,CAAC,OAAyB,EAAE,EAAE;YAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAE7B,qCAAqC;YACrC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAEjC,mCAAmC;YACnC,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAEhD,2CAA2C;YAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,CAAC;gBAChC,SAAS;gBACT,KAAK;gBACL,SAAS;gBACT,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,IAAI,EAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM;gBAC/B,cAAc,EAAE,OAAO;gBACvB,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;aAC9E,CAAC,CAAC;YAEH,wDAAwD;YACxD,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;YAElD,wDAAwD;YACxD,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,cAAc,EAAE,CAAC,QAA6B,EAAE,OAAyB,EAAE,EAAE;YAC3E,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACzC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAE/B,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,mEAAmE;gBACnE,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YAE/C,mCAAmC;YACnC,MAAM,OAAO,GAAG,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAEjD,iDAAiD;YACjD,OAAO,CAAC,qBAAqB,CAAC,IAAI,CAAC,KAAK,EAAE;gBACxC,MAAM,EAAE,QAAQ,CAAC,UAAU;gBAC3B,OAAO;gBACP,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;gBACxE,UAAU;aACX,CAAC,CAAC;YAEH,wDAAwD;YACxD,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC,CAAC;IAEH,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,IAAI,EAAE,KAAK,IAAI,EAAE;YACf,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACtB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,OAAgB;IACtC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAE1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;aAAM,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { CapturedRequest, Session } from "../shared/types.js";
|
|
2
|
+
export declare class RequestRepository {
|
|
3
|
+
private db;
|
|
4
|
+
constructor(dbPath: string);
|
|
5
|
+
/**
|
|
6
|
+
* Register a new session.
|
|
7
|
+
*/
|
|
8
|
+
registerSession(label?: string, pid?: number): Session;
|
|
9
|
+
/**
|
|
10
|
+
* Get a session by ID.
|
|
11
|
+
*/
|
|
12
|
+
getSession(id: string): Session | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* List all sessions.
|
|
15
|
+
*/
|
|
16
|
+
listSessions(): Session[];
|
|
17
|
+
/**
|
|
18
|
+
* Save a captured request. Returns the generated ID.
|
|
19
|
+
*/
|
|
20
|
+
saveRequest(request: Omit<CapturedRequest, "id">): string;
|
|
21
|
+
/**
|
|
22
|
+
* Update a request with response data.
|
|
23
|
+
*/
|
|
24
|
+
updateRequestResponse(id: string, response: {
|
|
25
|
+
status: number;
|
|
26
|
+
headers: Record<string, string>;
|
|
27
|
+
body?: Buffer;
|
|
28
|
+
durationMs: number;
|
|
29
|
+
}): void;
|
|
30
|
+
/**
|
|
31
|
+
* Get a request by ID.
|
|
32
|
+
*/
|
|
33
|
+
getRequest(id: string): CapturedRequest | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* List requests, optionally filtered by session or label.
|
|
36
|
+
*/
|
|
37
|
+
listRequests(options?: {
|
|
38
|
+
sessionId?: string;
|
|
39
|
+
label?: string;
|
|
40
|
+
limit?: number;
|
|
41
|
+
offset?: number;
|
|
42
|
+
}): CapturedRequest[];
|
|
43
|
+
/**
|
|
44
|
+
* Count requests, optionally filtered by session or label.
|
|
45
|
+
*/
|
|
46
|
+
countRequests(options?: {
|
|
47
|
+
sessionId?: string;
|
|
48
|
+
label?: string;
|
|
49
|
+
}): number;
|
|
50
|
+
/**
|
|
51
|
+
* Delete all requests (useful for cleanup).
|
|
52
|
+
*/
|
|
53
|
+
clearRequests(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Close the database connection.
|
|
56
|
+
*/
|
|
57
|
+
close(): void;
|
|
58
|
+
private rowToRequest;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/daemon/storage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAkCnE,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,EAAE,CAAoB;gBAElB,MAAM,EAAE,MAAM;IAM1B;;OAEG;IACH,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,GAAE,MAAoB,GAAG,OAAO;IAkBnE;;OAEG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAuB3C;;OAEG;IACH,YAAY,IAAI,OAAO,EAAE;IAsBzB;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,GAAG,MAAM;IAgCzD;;OAEG;IACH,qBAAqB,CACnB,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE;QACR,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,GACA,IAAI;IAgBP;;OAEG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAUnD;;OAEG;IACH,YAAY,CACV,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GACpF,eAAe,EAAE;IAgCpB;;OAEG;IACH,aAAa,CAAC,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,MAAM;IAyB3E;;OAEG;IACH,aAAa,IAAI,IAAI;IAIrB;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,YAAY;CAsBrB"}
|