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.
Files changed (114) hide show
  1. package/README.md +274 -0
  2. package/dist/cli/commands/clear.d.ts +3 -0
  3. package/dist/cli/commands/clear.d.ts.map +1 -0
  4. package/dist/cli/commands/clear.js +30 -0
  5. package/dist/cli/commands/clear.js.map +1 -0
  6. package/dist/cli/commands/init.d.ts +9 -0
  7. package/dist/cli/commands/init.d.ts.map +1 -0
  8. package/dist/cli/commands/init.js +25 -0
  9. package/dist/cli/commands/init.js.map +1 -0
  10. package/dist/cli/commands/intercept.d.ts +8 -0
  11. package/dist/cli/commands/intercept.d.ts.map +1 -0
  12. package/dist/cli/commands/intercept.js +59 -0
  13. package/dist/cli/commands/intercept.js.map +1 -0
  14. package/dist/cli/commands/project.d.ts +3 -0
  15. package/dist/cli/commands/project.d.ts.map +1 -0
  16. package/dist/cli/commands/project.js +13 -0
  17. package/dist/cli/commands/project.js.map +1 -0
  18. package/dist/cli/commands/status.d.ts +3 -0
  19. package/dist/cli/commands/status.d.ts.map +1 -0
  20. package/dist/cli/commands/status.js +36 -0
  21. package/dist/cli/commands/status.js.map +1 -0
  22. package/dist/cli/commands/stop.d.ts +3 -0
  23. package/dist/cli/commands/stop.d.ts.map +1 -0
  24. package/dist/cli/commands/stop.js +27 -0
  25. package/dist/cli/commands/stop.js.map +1 -0
  26. package/dist/cli/commands/tui.d.ts +3 -0
  27. package/dist/cli/commands/tui.d.ts.map +1 -0
  28. package/dist/cli/commands/tui.js +27 -0
  29. package/dist/cli/commands/tui.js.map +1 -0
  30. package/dist/cli/index.d.ts +3 -0
  31. package/dist/cli/index.d.ts.map +1 -0
  32. package/dist/cli/index.js +20 -0
  33. package/dist/cli/index.js.map +1 -0
  34. package/dist/cli/tui/App.d.ts +10 -0
  35. package/dist/cli/tui/App.d.ts.map +1 -0
  36. package/dist/cli/tui/App.js +96 -0
  37. package/dist/cli/tui/App.js.map +1 -0
  38. package/dist/cli/tui/components/BodyView.d.ts +14 -0
  39. package/dist/cli/tui/components/BodyView.d.ts.map +1 -0
  40. package/dist/cli/tui/components/BodyView.js +39 -0
  41. package/dist/cli/tui/components/BodyView.js.map +1 -0
  42. package/dist/cli/tui/components/HeadersView.d.ts +13 -0
  43. package/dist/cli/tui/components/HeadersView.d.ts.map +1 -0
  44. package/dist/cli/tui/components/HeadersView.js +8 -0
  45. package/dist/cli/tui/components/HeadersView.js.map +1 -0
  46. package/dist/cli/tui/components/RequestDetails.d.ts +13 -0
  47. package/dist/cli/tui/components/RequestDetails.d.ts.map +1 -0
  48. package/dist/cli/tui/components/RequestDetails.js +19 -0
  49. package/dist/cli/tui/components/RequestDetails.js.map +1 -0
  50. package/dist/cli/tui/components/RequestList.d.ts +15 -0
  51. package/dist/cli/tui/components/RequestList.d.ts.map +1 -0
  52. package/dist/cli/tui/components/RequestList.js +17 -0
  53. package/dist/cli/tui/components/RequestList.js.map +1 -0
  54. package/dist/cli/tui/components/RequestListItem.d.ts +13 -0
  55. package/dist/cli/tui/components/RequestListItem.d.ts.map +1 -0
  56. package/dist/cli/tui/components/RequestListItem.js +53 -0
  57. package/dist/cli/tui/components/RequestListItem.js.map +1 -0
  58. package/dist/cli/tui/components/StatusBar.d.ts +10 -0
  59. package/dist/cli/tui/components/StatusBar.d.ts.map +1 -0
  60. package/dist/cli/tui/components/StatusBar.js +18 -0
  61. package/dist/cli/tui/components/StatusBar.js.map +1 -0
  62. package/dist/cli/tui/hooks/useExport.d.ts +18 -0
  63. package/dist/cli/tui/hooks/useExport.d.ts.map +1 -0
  64. package/dist/cli/tui/hooks/useExport.js +58 -0
  65. package/dist/cli/tui/hooks/useExport.js.map +1 -0
  66. package/dist/cli/tui/hooks/useRequests.d.ts +20 -0
  67. package/dist/cli/tui/hooks/useRequests.d.ts.map +1 -0
  68. package/dist/cli/tui/hooks/useRequests.js +85 -0
  69. package/dist/cli/tui/hooks/useRequests.js.map +1 -0
  70. package/dist/cli/tui/hooks/useStdoutDimensions.d.ts +11 -0
  71. package/dist/cli/tui/hooks/useStdoutDimensions.d.ts.map +1 -0
  72. package/dist/cli/tui/hooks/useStdoutDimensions.js +29 -0
  73. package/dist/cli/tui/hooks/useStdoutDimensions.js.map +1 -0
  74. package/dist/cli/tui/utils/curl.d.ts +9 -0
  75. package/dist/cli/tui/utils/curl.d.ts.map +1 -0
  76. package/dist/cli/tui/utils/curl.js +47 -0
  77. package/dist/cli/tui/utils/curl.js.map +1 -0
  78. package/dist/cli/tui/utils/formatters.d.ts +36 -0
  79. package/dist/cli/tui/utils/formatters.d.ts.map +1 -0
  80. package/dist/cli/tui/utils/formatters.js +108 -0
  81. package/dist/cli/tui/utils/formatters.js.map +1 -0
  82. package/dist/cli/tui/utils/har.d.ts +75 -0
  83. package/dist/cli/tui/utils/har.d.ts.map +1 -0
  84. package/dist/cli/tui/utils/har.js +139 -0
  85. package/dist/cli/tui/utils/har.js.map +1 -0
  86. package/dist/daemon/control.d.ts +63 -0
  87. package/dist/daemon/control.d.ts.map +1 -0
  88. package/dist/daemon/control.js +260 -0
  89. package/dist/daemon/control.js.map +1 -0
  90. package/dist/daemon/index.d.ts +3 -0
  91. package/dist/daemon/index.d.ts.map +1 -0
  92. package/dist/daemon/index.js +107 -0
  93. package/dist/daemon/index.js.map +1 -0
  94. package/dist/daemon/proxy.d.ts +19 -0
  95. package/dist/daemon/proxy.d.ts.map +1 -0
  96. package/dist/daemon/proxy.js +89 -0
  97. package/dist/daemon/proxy.js.map +1 -0
  98. package/dist/daemon/storage.d.ts +60 -0
  99. package/dist/daemon/storage.d.ts.map +1 -0
  100. package/dist/daemon/storage.js +215 -0
  101. package/dist/daemon/storage.js.map +1 -0
  102. package/dist/shared/daemon.d.ts +14 -0
  103. package/dist/shared/daemon.d.ts.map +1 -0
  104. package/dist/shared/daemon.js +161 -0
  105. package/dist/shared/daemon.js.map +1 -0
  106. package/dist/shared/project.d.ts +61 -0
  107. package/dist/shared/project.d.ts.map +1 -0
  108. package/dist/shared/project.js +143 -0
  109. package/dist/shared/project.js.map +1 -0
  110. package/dist/shared/types.d.ts +32 -0
  111. package/dist/shared/types.d.ts.map +1 -0
  112. package/dist/shared/types.js +5 -0
  113. package/dist/shared/types.js.map +1 -0
  114. package/package.json +89 -0
@@ -0,0 +1,215 @@
1
+ import Database from "better-sqlite3";
2
+ import { v4 as uuidv4 } from "uuid";
3
+ const SCHEMA = `
4
+ CREATE TABLE IF NOT EXISTS sessions (
5
+ id TEXT PRIMARY KEY,
6
+ label TEXT,
7
+ pid INTEGER NOT NULL,
8
+ started_at INTEGER NOT NULL
9
+ );
10
+
11
+ CREATE TABLE IF NOT EXISTS requests (
12
+ id TEXT PRIMARY KEY,
13
+ session_id TEXT NOT NULL,
14
+ label TEXT,
15
+ timestamp INTEGER NOT NULL,
16
+ method TEXT NOT NULL,
17
+ url TEXT NOT NULL,
18
+ host TEXT NOT NULL,
19
+ path TEXT NOT NULL,
20
+ request_headers TEXT,
21
+ request_body BLOB,
22
+ response_status INTEGER,
23
+ response_headers TEXT,
24
+ response_body BLOB,
25
+ duration_ms INTEGER,
26
+ created_at INTEGER DEFAULT (unixepoch()),
27
+ FOREIGN KEY (session_id) REFERENCES sessions(id)
28
+ );
29
+
30
+ CREATE INDEX IF NOT EXISTS idx_requests_timestamp ON requests(timestamp DESC);
31
+ CREATE INDEX IF NOT EXISTS idx_requests_session ON requests(session_id);
32
+ CREATE INDEX IF NOT EXISTS idx_requests_label ON requests(label);
33
+ `;
34
+ export class RequestRepository {
35
+ db;
36
+ constructor(dbPath) {
37
+ this.db = new Database(dbPath);
38
+ this.db.pragma("journal_mode = WAL");
39
+ this.db.exec(SCHEMA);
40
+ }
41
+ /**
42
+ * Register a new session.
43
+ */
44
+ registerSession(label, pid = process.pid) {
45
+ const session = {
46
+ id: uuidv4(),
47
+ label,
48
+ pid,
49
+ startedAt: Date.now(),
50
+ };
51
+ const stmt = this.db.prepare(`
52
+ INSERT INTO sessions (id, label, pid, started_at)
53
+ VALUES (?, ?, ?, ?)
54
+ `);
55
+ stmt.run(session.id, session.label ?? null, session.pid, session.startedAt);
56
+ return session;
57
+ }
58
+ /**
59
+ * Get a session by ID.
60
+ */
61
+ getSession(id) {
62
+ const stmt = this.db.prepare(`
63
+ SELECT id, label, pid, started_at as startedAt
64
+ FROM sessions
65
+ WHERE id = ?
66
+ `);
67
+ const row = stmt.get(id);
68
+ if (!row) {
69
+ return undefined;
70
+ }
71
+ return {
72
+ id: row.id,
73
+ label: row.label ?? undefined,
74
+ pid: row.pid,
75
+ startedAt: row.startedAt,
76
+ };
77
+ }
78
+ /**
79
+ * List all sessions.
80
+ */
81
+ listSessions() {
82
+ const stmt = this.db.prepare(`
83
+ SELECT id, label, pid, started_at as startedAt
84
+ FROM sessions
85
+ ORDER BY started_at DESC
86
+ `);
87
+ const rows = stmt.all();
88
+ return rows.map((row) => ({
89
+ id: row.id,
90
+ label: row.label ?? undefined,
91
+ pid: row.pid,
92
+ startedAt: row.startedAt,
93
+ }));
94
+ }
95
+ /**
96
+ * Save a captured request. Returns the generated ID.
97
+ */
98
+ saveRequest(request) {
99
+ const id = uuidv4();
100
+ const stmt = this.db.prepare(`
101
+ INSERT INTO requests (
102
+ id, session_id, label, timestamp, method, url, host, path,
103
+ request_headers, request_body, response_status, response_headers,
104
+ response_body, duration_ms
105
+ )
106
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
107
+ `);
108
+ stmt.run(id, request.sessionId, request.label ?? null, request.timestamp, request.method, request.url, request.host, request.path, request.requestHeaders ? JSON.stringify(request.requestHeaders) : null, request.requestBody ?? null, request.responseStatus ?? null, request.responseHeaders ? JSON.stringify(request.responseHeaders) : null, request.responseBody ?? null, request.durationMs ?? null);
109
+ return id;
110
+ }
111
+ /**
112
+ * Update a request with response data.
113
+ */
114
+ updateRequestResponse(id, response) {
115
+ const stmt = this.db.prepare(`
116
+ UPDATE requests
117
+ SET response_status = ?, response_headers = ?, response_body = ?, duration_ms = ?
118
+ WHERE id = ?
119
+ `);
120
+ stmt.run(response.status, JSON.stringify(response.headers), response.body ?? null, response.durationMs, id);
121
+ }
122
+ /**
123
+ * Get a request by ID.
124
+ */
125
+ getRequest(id) {
126
+ const stmt = this.db.prepare(`
127
+ SELECT * FROM requests WHERE id = ?
128
+ `);
129
+ const row = stmt.get(id);
130
+ return row ? this.rowToRequest(row) : undefined;
131
+ }
132
+ /**
133
+ * List requests, optionally filtered by session or label.
134
+ */
135
+ listRequests(options = {}) {
136
+ const conditions = [];
137
+ const params = [];
138
+ if (options.sessionId) {
139
+ conditions.push("session_id = ?");
140
+ params.push(options.sessionId);
141
+ }
142
+ if (options.label) {
143
+ conditions.push("label = ?");
144
+ params.push(options.label);
145
+ }
146
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
147
+ const limit = options.limit ?? 1000;
148
+ const offset = options.offset ?? 0;
149
+ const stmt = this.db.prepare(`
150
+ SELECT * FROM requests
151
+ ${whereClause}
152
+ ORDER BY timestamp DESC
153
+ LIMIT ? OFFSET ?
154
+ `);
155
+ params.push(limit, offset);
156
+ const rows = stmt.all(...params);
157
+ return rows.map((row) => this.rowToRequest(row));
158
+ }
159
+ /**
160
+ * Count requests, optionally filtered by session or label.
161
+ */
162
+ countRequests(options = {}) {
163
+ const conditions = [];
164
+ const params = [];
165
+ if (options.sessionId) {
166
+ conditions.push("session_id = ?");
167
+ params.push(options.sessionId);
168
+ }
169
+ if (options.label) {
170
+ conditions.push("label = ?");
171
+ params.push(options.label);
172
+ }
173
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
174
+ const stmt = this.db.prepare(`
175
+ SELECT COUNT(*) as count FROM requests ${whereClause}
176
+ `);
177
+ const result = stmt.get(...params);
178
+ return result.count;
179
+ }
180
+ /**
181
+ * Delete all requests (useful for cleanup).
182
+ */
183
+ clearRequests() {
184
+ this.db.exec("DELETE FROM requests");
185
+ }
186
+ /**
187
+ * Close the database connection.
188
+ */
189
+ close() {
190
+ this.db.close();
191
+ }
192
+ rowToRequest(row) {
193
+ return {
194
+ id: row.id,
195
+ sessionId: row.session_id,
196
+ label: row.label ?? undefined,
197
+ timestamp: row.timestamp,
198
+ method: row.method,
199
+ url: row.url,
200
+ host: row.host,
201
+ path: row.path,
202
+ requestHeaders: row.request_headers
203
+ ? JSON.parse(row.request_headers)
204
+ : {},
205
+ requestBody: row.request_body ?? undefined,
206
+ responseStatus: row.response_status ?? undefined,
207
+ responseHeaders: row.response_headers
208
+ ? JSON.parse(row.response_headers)
209
+ : undefined,
210
+ responseBody: row.response_body ?? undefined,
211
+ durationMs: row.duration_ms ?? undefined,
212
+ };
213
+ }
214
+ }
215
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/daemon/storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Check if the daemon is running for a project.
3
+ */
4
+ export declare function isDaemonRunning(projectRoot: string): Promise<boolean>;
5
+ /**
6
+ * Start the daemon for a project.
7
+ * Returns the proxy port.
8
+ */
9
+ export declare function startDaemon(projectRoot: string): Promise<number>;
10
+ /**
11
+ * Stop the daemon for a project.
12
+ */
13
+ export declare function stopDaemon(projectRoot: string): Promise<void>;
14
+ //# sourceMappingURL=daemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/shared/daemon.ts"],"names":[],"mappings":"AAmBA;;GAEG;AACH,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAY3E;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAqCtE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBnE"}
@@ -0,0 +1,161 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { spawn } from "node:child_process";
4
+ import { fileURLToPath } from "node:url";
5
+ import { getHtpxPaths, readDaemonPid, isProcessRunning, ensureHtpxDir } from "./project.js";
6
+ import { ControlClient } from "../daemon/control.js";
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ /**
10
+ * Get the path to the daemon entry point.
11
+ */
12
+ function getDaemonPath() {
13
+ // This is relative to dist/shared/daemon.js
14
+ // The daemon entry point is at dist/daemon/index.js
15
+ return path.resolve(__dirname, "..", "daemon", "index.js");
16
+ }
17
+ /**
18
+ * Check if the daemon is running for a project.
19
+ */
20
+ export async function isDaemonRunning(projectRoot) {
21
+ const paths = getHtpxPaths(projectRoot);
22
+ // Check PID file first
23
+ const pid = readDaemonPid(projectRoot);
24
+ if (!pid || !isProcessRunning(pid)) {
25
+ return false;
26
+ }
27
+ // Verify the daemon is actually responding
28
+ const client = new ControlClient(paths.controlSocketFile);
29
+ return client.ping();
30
+ }
31
+ /**
32
+ * Start the daemon for a project.
33
+ * Returns the proxy port.
34
+ */
35
+ export async function startDaemon(projectRoot) {
36
+ // Check if already running
37
+ if (await isDaemonRunning(projectRoot)) {
38
+ const paths = getHtpxPaths(projectRoot);
39
+ const portContent = fs.readFileSync(paths.proxyPortFile, "utf-8").trim();
40
+ return parseInt(portContent, 10);
41
+ }
42
+ // Ensure .htpx directory exists
43
+ ensureHtpxDir(projectRoot);
44
+ const daemonPath = getDaemonPath();
45
+ const paths = getHtpxPaths(projectRoot);
46
+ // Spawn daemon as detached background process
47
+ // Clear the log file first so we only see errors from this attempt
48
+ const logFile = path.join(paths.htpxDir, "daemon.log");
49
+ fs.writeFileSync(logFile, "");
50
+ const out = fs.openSync(logFile, "a");
51
+ const err = fs.openSync(logFile, "a");
52
+ const child = spawn("node", [daemonPath], {
53
+ env: {
54
+ ...process.env,
55
+ PROJECT_ROOT: projectRoot,
56
+ },
57
+ detached: true,
58
+ stdio: ["ignore", out, err],
59
+ });
60
+ // Detach from parent process
61
+ child.unref();
62
+ // Wait for daemon to start and write port file
63
+ const port = await waitForDaemon(projectRoot, 10000);
64
+ return port;
65
+ }
66
+ /**
67
+ * Stop the daemon for a project.
68
+ */
69
+ export async function stopDaemon(projectRoot) {
70
+ const pid = readDaemonPid(projectRoot);
71
+ if (!pid) {
72
+ return; // Already stopped
73
+ }
74
+ if (!isProcessRunning(pid)) {
75
+ // PID file exists but process is dead - clean up
76
+ const paths = getHtpxPaths(projectRoot);
77
+ cleanupDaemonFiles(paths);
78
+ return;
79
+ }
80
+ // Send SIGTERM to daemon
81
+ process.kill(pid, "SIGTERM");
82
+ // Wait for daemon to stop
83
+ await waitForDaemonStop(pid, 5000);
84
+ // Clean up any remaining files
85
+ const paths = getHtpxPaths(projectRoot);
86
+ cleanupDaemonFiles(paths);
87
+ }
88
+ /**
89
+ * Wait for the daemon to start and return the proxy port.
90
+ */
91
+ async function waitForDaemon(projectRoot, timeoutMs) {
92
+ const paths = getHtpxPaths(projectRoot);
93
+ const startTime = Date.now();
94
+ while (Date.now() - startTime < timeoutMs) {
95
+ // Check if port file exists
96
+ if (fs.existsSync(paths.proxyPortFile)) {
97
+ const portContent = fs.readFileSync(paths.proxyPortFile, "utf-8").trim();
98
+ const port = parseInt(portContent, 10);
99
+ if (!isNaN(port)) {
100
+ // Verify daemon is responding
101
+ const client = new ControlClient(paths.controlSocketFile);
102
+ if (await client.ping()) {
103
+ return port;
104
+ }
105
+ }
106
+ }
107
+ // Wait a bit before checking again
108
+ await sleep(100);
109
+ }
110
+ // On timeout, check daemon.log for errors to surface to the user
111
+ const logPath = path.join(paths.htpxDir, "daemon.log");
112
+ let logTail = "";
113
+ if (fs.existsSync(logPath)) {
114
+ const content = fs.readFileSync(logPath, "utf-8");
115
+ const lines = content.trim().split("\n");
116
+ logTail = lines.slice(-20).join("\n"); // Last 20 lines
117
+ }
118
+ if (logTail) {
119
+ throw new Error(`Daemon failed to start. Log output:\n${logTail}`);
120
+ }
121
+ throw new Error("Daemon failed to start within timeout (no log output)");
122
+ }
123
+ /**
124
+ * Wait for daemon process to stop.
125
+ */
126
+ async function waitForDaemonStop(pid, timeoutMs) {
127
+ const startTime = Date.now();
128
+ while (Date.now() - startTime < timeoutMs) {
129
+ if (!isProcessRunning(pid)) {
130
+ return;
131
+ }
132
+ await sleep(100);
133
+ }
134
+ // Force kill if still running
135
+ try {
136
+ process.kill(pid, "SIGKILL");
137
+ }
138
+ catch {
139
+ // Process may have already exited
140
+ }
141
+ }
142
+ /**
143
+ * Clean up daemon files (socket, port file, pid file).
144
+ */
145
+ function cleanupDaemonFiles(paths) {
146
+ const files = [paths.controlSocketFile, paths.proxyPortFile, paths.pidFile];
147
+ for (const file of files) {
148
+ if (fs.existsSync(file)) {
149
+ try {
150
+ fs.unlinkSync(file);
151
+ }
152
+ catch {
153
+ // Ignore errors during cleanup
154
+ }
155
+ }
156
+ }
157
+ }
158
+ function sleep(ms) {
159
+ return new Promise((resolve) => setTimeout(resolve, ms));
160
+ }
161
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/shared/daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Find the project root by looking for .htpx directory or .git directory.
3
+ * Walks up the directory tree from the current working directory.
4
+ * Returns undefined if no project root is found.
5
+ */
6
+ export declare function findProjectRoot(startDir?: string): string | undefined;
7
+ /**
8
+ * Determine the project root, creating .htpx if needed.
9
+ * - If .htpx exists anywhere in the tree, use that directory
10
+ * - If .git exists, use the git root
11
+ * - Otherwise, use the current working directory
12
+ */
13
+ export declare function findOrCreateProjectRoot(startDir?: string): string;
14
+ /**
15
+ * Get the .htpx directory path for a project root.
16
+ */
17
+ export declare function getHtpxDir(projectRoot: string): string;
18
+ /**
19
+ * Ensure the .htpx directory exists, creating it if necessary.
20
+ * Returns the path to the .htpx directory.
21
+ */
22
+ export declare function ensureHtpxDir(projectRoot: string): string;
23
+ /**
24
+ * Get paths to various files within the .htpx directory.
25
+ */
26
+ export declare function getHtpxPaths(projectRoot: string): {
27
+ htpxDir: string;
28
+ proxyPortFile: string;
29
+ controlSocketFile: string;
30
+ databaseFile: string;
31
+ caKeyFile: string;
32
+ caCertFile: string;
33
+ pidFile: string;
34
+ };
35
+ /**
36
+ * Read the proxy port from the .htpx directory.
37
+ * Returns undefined if the file doesn't exist.
38
+ */
39
+ export declare function readProxyPort(projectRoot: string): number | undefined;
40
+ /**
41
+ * Write the proxy port to the .htpx directory.
42
+ */
43
+ export declare function writeProxyPort(projectRoot: string, port: number): void;
44
+ /**
45
+ * Read the daemon PID from the .htpx directory.
46
+ * Returns undefined if the file doesn't exist.
47
+ */
48
+ export declare function readDaemonPid(projectRoot: string): number | undefined;
49
+ /**
50
+ * Write the daemon PID to the .htpx directory.
51
+ */
52
+ export declare function writeDaemonPid(projectRoot: string, pid: number): void;
53
+ /**
54
+ * Remove the daemon PID file.
55
+ */
56
+ export declare function removeDaemonPid(projectRoot: string): void;
57
+ /**
58
+ * Check if a process with the given PID is running.
59
+ */
60
+ export declare function isProcessRunning(pid: number): boolean;
61
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/shared/project.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,GAAG,SAAS,CAmBpF;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAqBhF;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAQzD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM;;;;;;;;EAY/C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWrE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAGtE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWrE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAGrE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAMzD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQrD"}
@@ -0,0 +1,143 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ const HTPX_DIR = ".htpx";
4
+ /**
5
+ * Find the project root by looking for .htpx directory or .git directory.
6
+ * Walks up the directory tree from the current working directory.
7
+ * Returns undefined if no project root is found.
8
+ */
9
+ export function findProjectRoot(startDir = process.cwd()) {
10
+ let currentDir = path.resolve(startDir);
11
+ const root = path.parse(currentDir).root;
12
+ while (currentDir !== root) {
13
+ // Check for .htpx directory first
14
+ if (fs.existsSync(path.join(currentDir, HTPX_DIR))) {
15
+ return currentDir;
16
+ }
17
+ // Check for .git directory as fallback
18
+ if (fs.existsSync(path.join(currentDir, ".git"))) {
19
+ return currentDir;
20
+ }
21
+ currentDir = path.dirname(currentDir);
22
+ }
23
+ return undefined;
24
+ }
25
+ /**
26
+ * Determine the project root, creating .htpx if needed.
27
+ * - If .htpx exists anywhere in the tree, use that directory
28
+ * - If .git exists, use the git root
29
+ * - Otherwise, use the current working directory
30
+ */
31
+ export function findOrCreateProjectRoot(startDir = process.cwd()) {
32
+ let currentDir = path.resolve(startDir);
33
+ const root = path.parse(currentDir).root;
34
+ let gitRoot;
35
+ while (currentDir !== root) {
36
+ // Check for .htpx directory first - this takes priority
37
+ if (fs.existsSync(path.join(currentDir, HTPX_DIR))) {
38
+ return currentDir;
39
+ }
40
+ // Remember the git root if we find one
41
+ if (!gitRoot && fs.existsSync(path.join(currentDir, ".git"))) {
42
+ gitRoot = currentDir;
43
+ }
44
+ currentDir = path.dirname(currentDir);
45
+ }
46
+ // If we found a git root, use that; otherwise use cwd
47
+ return gitRoot ?? process.cwd();
48
+ }
49
+ /**
50
+ * Get the .htpx directory path for a project root.
51
+ */
52
+ export function getHtpxDir(projectRoot) {
53
+ return path.join(projectRoot, HTPX_DIR);
54
+ }
55
+ /**
56
+ * Ensure the .htpx directory exists, creating it if necessary.
57
+ * Returns the path to the .htpx directory.
58
+ */
59
+ export function ensureHtpxDir(projectRoot) {
60
+ const htpxDir = getHtpxDir(projectRoot);
61
+ if (!fs.existsSync(htpxDir)) {
62
+ fs.mkdirSync(htpxDir, { recursive: true });
63
+ }
64
+ return htpxDir;
65
+ }
66
+ /**
67
+ * Get paths to various files within the .htpx directory.
68
+ */
69
+ export function getHtpxPaths(projectRoot) {
70
+ const htpxDir = getHtpxDir(projectRoot);
71
+ return {
72
+ htpxDir,
73
+ proxyPortFile: path.join(htpxDir, "proxy.port"),
74
+ controlSocketFile: path.join(htpxDir, "control.sock"),
75
+ databaseFile: path.join(htpxDir, "requests.db"),
76
+ caKeyFile: path.join(htpxDir, "ca-key.pem"),
77
+ caCertFile: path.join(htpxDir, "ca.pem"),
78
+ pidFile: path.join(htpxDir, "daemon.pid"),
79
+ };
80
+ }
81
+ /**
82
+ * Read the proxy port from the .htpx directory.
83
+ * Returns undefined if the file doesn't exist.
84
+ */
85
+ export function readProxyPort(projectRoot) {
86
+ const { proxyPortFile } = getHtpxPaths(projectRoot);
87
+ if (!fs.existsSync(proxyPortFile)) {
88
+ return undefined;
89
+ }
90
+ const content = fs.readFileSync(proxyPortFile, "utf-8").trim();
91
+ const port = parseInt(content, 10);
92
+ return isNaN(port) ? undefined : port;
93
+ }
94
+ /**
95
+ * Write the proxy port to the .htpx directory.
96
+ */
97
+ export function writeProxyPort(projectRoot, port) {
98
+ const { proxyPortFile } = getHtpxPaths(projectRoot);
99
+ fs.writeFileSync(proxyPortFile, port.toString(), "utf-8");
100
+ }
101
+ /**
102
+ * Read the daemon PID from the .htpx directory.
103
+ * Returns undefined if the file doesn't exist.
104
+ */
105
+ export function readDaemonPid(projectRoot) {
106
+ const { pidFile } = getHtpxPaths(projectRoot);
107
+ if (!fs.existsSync(pidFile)) {
108
+ return undefined;
109
+ }
110
+ const content = fs.readFileSync(pidFile, "utf-8").trim();
111
+ const pid = parseInt(content, 10);
112
+ return isNaN(pid) ? undefined : pid;
113
+ }
114
+ /**
115
+ * Write the daemon PID to the .htpx directory.
116
+ */
117
+ export function writeDaemonPid(projectRoot, pid) {
118
+ const { pidFile } = getHtpxPaths(projectRoot);
119
+ fs.writeFileSync(pidFile, pid.toString(), "utf-8");
120
+ }
121
+ /**
122
+ * Remove the daemon PID file.
123
+ */
124
+ export function removeDaemonPid(projectRoot) {
125
+ const { pidFile } = getHtpxPaths(projectRoot);
126
+ if (fs.existsSync(pidFile)) {
127
+ fs.unlinkSync(pidFile);
128
+ }
129
+ }
130
+ /**
131
+ * Check if a process with the given PID is running.
132
+ */
133
+ export function isProcessRunning(pid) {
134
+ try {
135
+ // Sending signal 0 checks if the process exists without actually sending a signal
136
+ process.kill(pid, 0);
137
+ return true;
138
+ }
139
+ catch {
140
+ return false;
141
+ }
142
+ }
143
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/shared/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Core types for htpx
3
+ */
4
+ export interface CapturedRequest {
5
+ id: string;
6
+ sessionId: string;
7
+ label?: string;
8
+ timestamp: number;
9
+ method: string;
10
+ url: string;
11
+ host: string;
12
+ path: string;
13
+ requestHeaders: Record<string, string>;
14
+ requestBody?: Buffer;
15
+ responseStatus?: number;
16
+ responseHeaders?: Record<string, string>;
17
+ responseBody?: Buffer;
18
+ durationMs?: number;
19
+ }
20
+ export interface Session {
21
+ id: string;
22
+ label?: string;
23
+ pid: number;
24
+ startedAt: number;
25
+ }
26
+ export interface DaemonStatus {
27
+ running: boolean;
28
+ proxyPort?: number;
29
+ sessionCount: number;
30
+ requestCount: number;
31
+ }
32
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Core types for htpx
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map