httptmux 1.1.2 → 1.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.
package/CHANGELOG.md CHANGED
@@ -31,3 +31,5 @@ v1.1.0 (15/1/2026): Added JWT expiry awareness, better error handling and better
31
31
  v1.1.1 (16/1/2026): Added more badges to README.md
32
32
 
33
33
  v1.1.2 (16/1/2026): Changed README.md badge layout
34
+
35
+ v1.2.0 (19/1/2026): Added non-interactive mode and PATCH, HEAD and OPTIONS support
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![install size](https://packagephobia.com/badge?p=httptmux)](https://packagephobia.com/result?p=httptmux)
7
7
  ![GitHub repo size](https://img.shields.io/github/repo-size/somerandomdevig/httptmux)
8
8
 
9
- An interactive CLI tool originally meant for termux but now extended to desktops with support for JWT requests (now with better error handling, JWT expiry awareness and improved history management!)
9
+ An interactive CLI tool originally meant for termux but now extended to desktops with support for JWT requests (now with a non-interactive mode and support fot PATCH, HEAD and OPTIONS requests)
10
10
 
11
11
  ## Installation
12
12
 
package/index.js CHANGED
@@ -5,14 +5,16 @@ import chalk from 'chalk';
5
5
  import fs from 'fs';
6
6
  import path from 'path';
7
7
  import os from 'os';
8
+ import minimist from 'minimist';
8
9
 
9
10
  // Load package.json for version info
10
11
  import pkg from "./package.json" with { type: "json" };
11
12
 
12
13
  // CLI flags
13
- const verbose = process.argv.includes("--verbose");
14
- const showHelp = process.argv.includes("--help");
15
- const showVersion = process.argv.includes("--version");
14
+ const args = minimist(process.argv.slice(2));
15
+ const verbose = args.verbose || args.v;
16
+ const showHelp = args.help || args.h === true;
17
+ const showVersion = args.version || args.V === true;
16
18
 
17
19
  // Config paths
18
20
  const historyFile = path.join(os.homedir(), ".api-cli-history.json");
@@ -26,25 +28,25 @@ function logVerbose(message) {
26
28
  function printHelp() {
27
29
  console.log(chalk.cyan("\nhttptmux CLI Help"));
28
30
  console.log(`
29
- Usage: httptmux [options]
31
+ Usage:
32
+ httptmux METHOD -u <url> [-h <headers>] [-b <body>]
33
+ httptmux -c
34
+ httptmux -e <file>
35
+ httptmux -f "status=200 since=YYYY-MM-DD"
30
36
 
31
- Options:
32
- --help Show this help message
33
- --version Show version number
34
- --verbose Enable verbose logging
37
+ Methods:
38
+ GET | POST | PUT | DELETE | PATCH | HEAD | OPTIONS
35
39
 
36
- Interactive Menu Options:
37
- Make new request
38
- View history
39
- • Re-run from history
40
- Search history
41
- Clear history
42
- Export history
43
- Filter history
44
- Set JWT token
45
- Help
46
- • Version
47
- • Exit
40
+ Flags:
41
+ -u, --url API URL
42
+ -h, --headers Headers as JSON string
43
+ -b, --body Body as JSON string
44
+ -c, --clear-history Clear request history
45
+ -e, --export-history Export history to file
46
+ -f, --filter-history Filter history (status=XXX since=YYYY-MM-DD)
47
+ -v, --verbose Enable verbose logging
48
+ -V, --version Show version
49
+ --help Show help
48
50
  `);
49
51
  }
50
52
 
@@ -75,14 +77,8 @@ function exportHistory(filePath = path.join(os.homedir(), "httptmux-history-expo
75
77
  fs.writeFileSync(filePath, JSON.stringify(history, null, 2));
76
78
  console.log(chalk.green(`History exported to ${filePath}`));
77
79
  }
78
- async function filterHistoryPrompt() {
79
- const history = loadHistory();
80
- if (history.length === 0) return console.log(chalk.yellow("No history found."));
81
- const { status, since } = await inquirer.prompt([
82
- { type: "input", name: "status", message: chalk.blue("Filter by status code (or leave empty):") },
83
- { type: "input", name: "since", message: chalk.blue("Filter by date (YYYY-MM-DD or leave empty):") }
84
- ]);
85
- let results = history;
80
+ function filterHistory(status, since) {
81
+ let results = loadHistory();
86
82
  if (status) results = results.filter(e => String(e.status) === status);
87
83
  if (since) results = results.filter(e => new Date(e.timestamp) >= new Date(since));
88
84
  if (results.length === 0) return console.log(chalk.yellow("No matching entries."));
@@ -150,8 +146,16 @@ async function executeRequest({ method, url, headers, body }) {
150
146
  const response = await axios({ method, url, headers, data: body });
151
147
  const duration = Date.now() - start;
152
148
 
153
- console.log(chalk.green("\nResponse received:"));
154
- console.log(chalk.gray(JSON.stringify(response.data, null, 2)));
149
+ if (method === "HEAD") {
150
+ console.log(chalk.green("\nResponse headers:"));
151
+ console.log(chalk.gray(JSON.stringify(response.headers, null, 2)));
152
+ } else if (method === "OPTIONS") {
153
+ console.log(chalk.green("\nAllowed methods:"));
154
+ console.log(chalk.gray(response.headers.allow || JSON.stringify(response.headers, null, 2)));
155
+ } else {
156
+ console.log(chalk.green("\nResponse received:"));
157
+ console.log(chalk.gray(JSON.stringify(response.data, null, 2)));
158
+ }
155
159
 
156
160
  logVerbose(`Request completed in ${duration} ms`);
157
161
  logVerbose(`Status code: ${response.status}`);
@@ -165,13 +169,15 @@ async function executeRequest({ method, url, headers, body }) {
165
169
  }
166
170
  }
167
171
 
168
- // Prompt user for new request
172
+ // Interactive request
169
173
  async function runRequest() {
170
- const { method } = await inquirer.prompt([{ type: "list", name: "method", message: chalk.blue("Select HTTP method:"), choices: ["GET", "POST", "PUT", "DELETE"] }]);
174
+ const { method } = await inquirer.prompt([
175
+ { type: "list", name: "method", message: chalk.blue("Select HTTP method:"), choices: ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"] }
176
+ ]);
171
177
  const { url } = await inquirer.prompt([{ type: "input", name: "url", message: chalk.blue("Enter API URL:") }]);
172
178
  const { headersInput } = await inquirer.prompt([{ type: "input", name: "headersInput", message: chalk.yellow("Enter headers as JSON (or leave empty):") }]);
173
179
  let bodyInput = "";
174
- if (method === "POST" || method === "PUT") {
180
+ if (["POST", "PUT", "PATCH"].includes(method)) {
175
181
  const bodyAnswer = await inquirer.prompt([{ type: "input", name: "bodyInput", message: chalk.yellow("Enter request body as JSON (or leave empty):") }]);
176
182
  bodyInput = bodyAnswer.bodyInput;
177
183
  }
@@ -184,61 +190,31 @@ async function runRequest() {
184
190
  await executeRequest({ method, url, headers, body });
185
191
  }
186
192
 
187
- // Re-run from history
188
- async function rerunHistory() {
189
- const history = loadHistory();
190
- if (history.length === 0) return console.log(chalk.yellow("No history found."));
191
- const choices = history.map((entry, i) => ({ name: `${i + 1}. [${entry.timestamp}] ${entry.method} ${entry.url} (status: ${entry.status})`, value: i }));
192
- const { index } = await inquirer.prompt([{ type: "list", name: "index", message: chalk.blue("Select a request to re-run:"), choices }]);
193
- const entry = history[index];
194
- console.log(chalk.cyan(`\nRe-running: ${entry.method} ${entry.url}`));
195
- await executeRequest(entry);
196
- }
197
-
198
- // Search/filter history
199
- async function searchHistory() {
200
- const history = loadHistory();
201
- if (history.length === 0) return console.log(chalk.yellow("No history found."));
202
- const { keyword } = await inquirer.prompt([{ type: "input", name: "keyword", message: chalk.blue("Enter keyword to search (method, URL, status):") }]);
203
- const results = history.filter(entry =>
204
- entry.method.includes(keyword.toUpperCase()) ||
205
- entry.url.includes(keyword) ||
206
- String(entry.status).includes(keyword)
207
- );
208
- if (results.length === 0) return console.log(chalk.yellow("No matching entries."));
209
- console.log(chalk.cyan("\nSearch Results:"));
210
- results.forEach((entry, i) => console.log(chalk.gray(`${i + 1}. [${entry.timestamp}] ${entry.method} ${entry.url} (status: ${entry.status})`)));
211
- }
193
+ // Non-interactive mode
194
+ async function runNonInteractive() {
195
+ const cliMethod = args._[0];
196
+ const cliUrl = args.u || args.url;
197
+ const cliHeaders = args.h || args.headers ? JSON.parse(args.h || args.headers) : {};
198
+ const cliBody = args.b || args.body ? JSON.parse(args.b || args.body) : {};
212
199
 
213
- // JWT mode
214
- async function jwtMode() {
215
- const current = loadJWT();
216
- if (current) {
217
- console.log(chalk.cyan("Current JWT:"));
218
- const payload = decodeJWT(current);
219
- console.log(chalk.gray(JSON.stringify({ tokenPreview: `${current.slice(0, 12)}...`, payload }, null, 2)));
220
- checkJWTExpiry(current);
200
+ if (args.c || args["clear-history"]) {
201
+ clearHistory();
202
+ return;
221
203
  }
222
-
223
- const { action } = await inquirer.prompt([{
224
- type: "list",
225
- name: "action",
226
- message: chalk.blue("JWT actions:"),
227
- choices: ["Set new token", "Remove token", "Back"]
228
- }]);
229
-
230
- if (action === "Set new token") {
231
- const { token } = await inquirer.prompt([{ type: "input", name: "token", message: chalk.blue("Enter JWT token:") }]);
232
- if (!token || !token.includes(".")) {
233
- console.log(chalk.red("Invalid JWT format."));
234
- return;
235
- }
236
- saveJWT(token);
237
- } else if (action === "Remove token") {
238
- if (fs.existsSync(jwtFile)) fs.unlinkSync(jwtFile);
239
- console.log(chalk.green("JWT removed."));
240
- } else {
241
- // Back
204
+ if (args.e || args["export-history"]) {
205
+ exportHistory(args.e || args["export-history"]);
206
+ return;
207
+ }
208
+ if (args.f || args["filter-history"]) {
209
+ const filters = (args.f || args["filter-history"]).split(" ");
210
+ const status = filters.find(f => f.startsWith("status="))?.split("=")[1];
211
+ const since = filters.find(f => f.startsWith("since="))?.split("=")[1];
212
+ filterHistory(status, since);
213
+ return;
214
+ }
215
+ if (cliMethod && cliUrl) {
216
+ await executeRequest({ method: cliMethod, url: cliUrl, headers: cliHeaders, body: cliBody });
217
+ return;
242
218
  }
243
219
  }
244
220
 
@@ -247,6 +223,13 @@ async function main() {
247
223
  if (showHelp) return printHelp();
248
224
  if (showVersion) return printVersion();
249
225
 
226
+ // Non-interactive mode
227
+ if (args._.length > 0 || args.c || args.e || args.f) {
228
+ await runNonInteractive();
229
+ return;
230
+ }
231
+
232
+ // Interactive fallback
250
233
  console.log(chalk.cyan("httptmux"));
251
234
 
252
235
  let keepGoing = true;
@@ -276,17 +259,40 @@ async function main() {
276
259
  else if (action === "View history") {
277
260
  const history = loadHistory();
278
261
  if (history.length === 0) console.log(chalk.yellow("No history found."));
279
- else history.forEach((entry, i) => console.log(chalk.gray(`${i + 1}. [${entry.timestamp}] ${entry.method} ${entry.url} (status: ${entry.status})`)));
262
+ else history.forEach((entry, i) =>
263
+ console.log(chalk.gray(`${i + 1}. [${entry.timestamp}] ${entry.method} ${entry.url} (status: ${entry.status})`))
264
+ );
280
265
  }
281
266
  else if (action === "Re-run from history") await rerunHistory();
282
- else if (action === "Search history") await searchHistory();
267
+ else if (action === "Search history") {
268
+ const { keyword } = await inquirer.prompt([{ type: "input", name: "keyword", message: chalk.blue("Enter keyword to search:") }]);
269
+ const results = loadHistory().filter(entry =>
270
+ entry.method.includes(keyword.toUpperCase()) ||
271
+ entry.url.includes(keyword) ||
272
+ String(entry.status).includes(keyword)
273
+ );
274
+ if (results.length === 0) console.log(chalk.yellow("No matching entries."));
275
+ else results.forEach((entry, i) =>
276
+ console.log(chalk.gray(`${i + 1}. [${entry.timestamp}] ${entry.method} ${entry.url} (status: ${entry.status})`))
277
+ );
278
+ }
283
279
  else if (action === "Clear history") clearHistory();
284
280
  else if (action === "Export history") {
285
281
  const { filePath } = await inquirer.prompt([{ type: "input", name: "filePath", message: chalk.blue("Export file path (default in HOME):") }]);
286
282
  exportHistory(filePath && filePath.trim() ? filePath.trim() : undefined);
287
283
  }
288
- else if (action === "Filter history") await filterHistoryPrompt();
289
- else if (action === "Set JWT token") await jwtMode();
284
+ else if (action === "Filter history") {
285
+ const { status, since } = await inquirer.prompt([
286
+ { type: "input", name: "status", message: chalk.blue("Filter by status code (or leave empty):") },
287
+ { type: "input", name: "since", message: chalk.blue("Filter by date (YYYY-MM-DD or leave empty):") }
288
+ ]);
289
+ filterHistory(status, since);
290
+ }
291
+ else if (action === "Set JWT token") {
292
+ const { token } = await inquirer.prompt([{ type: "input", name: "token", message: chalk.blue("Enter JWT token:") }]);
293
+ if (!token || !token.includes(".")) console.log(chalk.red("Invalid JWT format."));
294
+ else saveJWT(token);
295
+ }
290
296
  else if (action === "Help") printHelp();
291
297
  else if (action === "Version") printVersion();
292
298
  else keepGoing = false;
@@ -296,6 +302,7 @@ async function main() {
296
302
  console.log(chalk.cyan("\nExiting httptmux. Goodbye!"));
297
303
  }
298
304
 
305
+ // Entry point
299
306
  main().catch(err => {
300
307
  console.error(chalk.red("Fatal error:"), err);
301
308
  process.exit(1);
package/index_desktop.js CHANGED
@@ -5,14 +5,16 @@ import chalk from 'chalk';
5
5
  import fs from 'fs';
6
6
  import path from 'path';
7
7
  import os from 'os';
8
+ import minimist from 'minimist';
8
9
 
9
10
  // Load package.json for version info
10
11
  import pkg from "./package.json" with { type: "json" };
11
12
 
12
13
  // CLI flags
13
- const verbose = process.argv.includes("--verbose");
14
- const showHelp = process.argv.includes("--help");
15
- const showVersion = process.argv.includes("--version");
14
+ const args = minimist(process.argv.slice(2));
15
+ const verbose = args.verbose || args.v;
16
+ const showHelp = args.help || args.h === true;
17
+ const showVersion = args.version || args.V === true;
16
18
 
17
19
  // Config paths
18
20
  const historyFile = path.join(os.homedir(), ".api-cli-history.json");
@@ -26,25 +28,25 @@ function logVerbose(message) {
26
28
  function printHelp() {
27
29
  console.log(chalk.cyan("\nhttptmux CLI Help"));
28
30
  console.log(`
29
- Usage: httptmux [options]
31
+ Usage:
32
+ httptmux METHOD -u <url> [-h <headers>] [-b <body>]
33
+ httptmux -c
34
+ httptmux -e <file>
35
+ httptmux -f "status=200 since=YYYY-MM-DD"
30
36
 
31
- Options:
32
- --help Show this help message
33
- --version Show version number
34
- --verbose Enable verbose logging
37
+ Methods:
38
+ GET | POST | PUT | DELETE | PATCH | HEAD | OPTIONS
35
39
 
36
- Interactive Menu Options:
37
- Make new request
38
- View history
39
- • Re-run from history
40
- Search history
41
- Clear history
42
- Export history
43
- Filter history
44
- Set JWT token
45
- Help
46
- • Version
47
- • Exit
40
+ Flags:
41
+ -u, --url API URL
42
+ -h, --headers Headers as JSON string
43
+ -b, --body Body as JSON string
44
+ -c, --clear-history Clear request history
45
+ -e, --export-history Export history to file
46
+ -f, --filter-history Filter history (status=XXX since=YYYY-MM-DD)
47
+ -v, --verbose Enable verbose logging
48
+ -V, --version Show version
49
+ --help Show help
48
50
  `);
49
51
  }
50
52
 
@@ -75,14 +77,8 @@ function exportHistory(filePath = path.join(os.homedir(), "httptmux-history-expo
75
77
  fs.writeFileSync(filePath, JSON.stringify(history, null, 2));
76
78
  console.log(chalk.green(`History exported to ${filePath}`));
77
79
  }
78
- async function filterHistoryPrompt() {
79
- const history = loadHistory();
80
- if (history.length === 0) return console.log(chalk.yellow("No history found."));
81
- const { status, since } = await inquirer.prompt([
82
- { type: "input", name: "status", message: chalk.blue("Filter by status code (or leave empty):") },
83
- { type: "input", name: "since", message: chalk.blue("Filter by date (YYYY-MM-DD or leave empty):") }
84
- ]);
85
- let results = history;
80
+ function filterHistory(status, since) {
81
+ let results = loadHistory();
86
82
  if (status) results = results.filter(e => String(e.status) === status);
87
83
  if (since) results = results.filter(e => new Date(e.timestamp) >= new Date(since));
88
84
  if (results.length === 0) return console.log(chalk.yellow("No matching entries."));
@@ -150,8 +146,16 @@ async function executeRequest({ method, url, headers, body }) {
150
146
  const response = await axios({ method, url, headers, data: body });
151
147
  const duration = Date.now() - start;
152
148
 
153
- console.log(chalk.green("\nResponse received:"));
154
- console.log(chalk.gray(JSON.stringify(response.data, null, 2)));
149
+ if (method === "HEAD") {
150
+ console.log(chalk.green("\nResponse headers:"));
151
+ console.log(chalk.gray(JSON.stringify(response.headers, null, 2)));
152
+ } else if (method === "OPTIONS") {
153
+ console.log(chalk.green("\nAllowed methods:"));
154
+ console.log(chalk.gray(response.headers.allow || JSON.stringify(response.headers, null, 2)));
155
+ } else {
156
+ console.log(chalk.green("\nResponse received:"));
157
+ console.log(chalk.gray(JSON.stringify(response.data, null, 2)));
158
+ }
155
159
 
156
160
  logVerbose(`Request completed in ${duration} ms`);
157
161
  logVerbose(`Status code: ${response.status}`);
@@ -165,13 +169,15 @@ async function executeRequest({ method, url, headers, body }) {
165
169
  }
166
170
  }
167
171
 
168
- // Prompt user for new request
172
+ // Interactive request
169
173
  async function runRequest() {
170
- const { method } = await inquirer.prompt([{ type: "list", name: "method", message: chalk.blue("Select HTTP method:"), choices: ["GET", "POST", "PUT", "DELETE"] }]);
174
+ const { method } = await inquirer.prompt([
175
+ { type: "list", name: "method", message: chalk.blue("Select HTTP method:"), choices: ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"] }
176
+ ]);
171
177
  const { url } = await inquirer.prompt([{ type: "input", name: "url", message: chalk.blue("Enter API URL:") }]);
172
178
  const { headersInput } = await inquirer.prompt([{ type: "input", name: "headersInput", message: chalk.yellow("Enter headers as JSON (or leave empty):") }]);
173
179
  let bodyInput = "";
174
- if (method === "POST" || method === "PUT") {
180
+ if (["POST", "PUT", "PATCH"].includes(method)) {
175
181
  const bodyAnswer = await inquirer.prompt([{ type: "input", name: "bodyInput", message: chalk.yellow("Enter request body as JSON (or leave empty):") }]);
176
182
  bodyInput = bodyAnswer.bodyInput;
177
183
  }
@@ -184,61 +190,31 @@ async function runRequest() {
184
190
  await executeRequest({ method, url, headers, body });
185
191
  }
186
192
 
187
- // Re-run from history
188
- async function rerunHistory() {
189
- const history = loadHistory();
190
- if (history.length === 0) return console.log(chalk.yellow("No history found."));
191
- const choices = history.map((entry, i) => ({ name: `${i + 1}. [${entry.timestamp}] ${entry.method} ${entry.url} (status: ${entry.status})`, value: i }));
192
- const { index } = await inquirer.prompt([{ type: "list", name: "index", message: chalk.blue("Select a request to re-run:"), choices }]);
193
- const entry = history[index];
194
- console.log(chalk.cyan(`\nRe-running: ${entry.method} ${entry.url}`));
195
- await executeRequest(entry);
196
- }
197
-
198
- // Search/filter history
199
- async function searchHistory() {
200
- const history = loadHistory();
201
- if (history.length === 0) return console.log(chalk.yellow("No history found."));
202
- const { keyword } = await inquirer.prompt([{ type: "input", name: "keyword", message: chalk.blue("Enter keyword to search (method, URL, status):") }]);
203
- const results = history.filter(entry =>
204
- entry.method.includes(keyword.toUpperCase()) ||
205
- entry.url.includes(keyword) ||
206
- String(entry.status).includes(keyword)
207
- );
208
- if (results.length === 0) return console.log(chalk.yellow("No matching entries."));
209
- console.log(chalk.cyan("\nSearch Results:"));
210
- results.forEach((entry, i) => console.log(chalk.gray(`${i + 1}. [${entry.timestamp}] ${entry.method} ${entry.url} (status: ${entry.status})`)));
211
- }
193
+ // Non-interactive mode
194
+ async function runNonInteractive() {
195
+ const cliMethod = args._[0];
196
+ const cliUrl = args.u || args.url;
197
+ const cliHeaders = args.h || args.headers ? JSON.parse(args.h || args.headers) : {};
198
+ const cliBody = args.b || args.body ? JSON.parse(args.b || args.body) : {};
212
199
 
213
- // JWT mode
214
- async function jwtMode() {
215
- const current = loadJWT();
216
- if (current) {
217
- console.log(chalk.cyan("Current JWT:"));
218
- const payload = decodeJWT(current);
219
- console.log(chalk.gray(JSON.stringify({ tokenPreview: `${current.slice(0, 12)}...`, payload }, null, 2)));
220
- checkJWTExpiry(current);
200
+ if (args.c || args["clear-history"]) {
201
+ clearHistory();
202
+ return;
221
203
  }
222
-
223
- const { action } = await inquirer.prompt([{
224
- type: "list",
225
- name: "action",
226
- message: chalk.blue("JWT actions:"),
227
- choices: ["Set new token", "Remove token", "Back"]
228
- }]);
229
-
230
- if (action === "Set new token") {
231
- const { token } = await inquirer.prompt([{ type: "input", name: "token", message: chalk.blue("Enter JWT token:") }]);
232
- if (!token || !token.includes(".")) {
233
- console.log(chalk.red("Invalid JWT format."));
234
- return;
235
- }
236
- saveJWT(token);
237
- } else if (action === "Remove token") {
238
- if (fs.existsSync(jwtFile)) fs.unlinkSync(jwtFile);
239
- console.log(chalk.green("JWT removed."));
240
- } else {
241
- // Back
204
+ if (args.e || args["export-history"]) {
205
+ exportHistory(args.e || args["export-history"]);
206
+ return;
207
+ }
208
+ if (args.f || args["filter-history"]) {
209
+ const filters = (args.f || args["filter-history"]).split(" ");
210
+ const status = filters.find(f => f.startsWith("status="))?.split("=")[1];
211
+ const since = filters.find(f => f.startsWith("since="))?.split("=")[1];
212
+ filterHistory(status, since);
213
+ return;
214
+ }
215
+ if (cliMethod && cliUrl) {
216
+ await executeRequest({ method: cliMethod, url: cliUrl, headers: cliHeaders, body: cliBody });
217
+ return;
242
218
  }
243
219
  }
244
220
 
@@ -247,6 +223,13 @@ async function main() {
247
223
  if (showHelp) return printHelp();
248
224
  if (showVersion) return printVersion();
249
225
 
226
+ // Non-interactive mode
227
+ if (args._.length > 0 || args.c || args.e || args.f) {
228
+ await runNonInteractive();
229
+ return;
230
+ }
231
+
232
+ // Interactive fallback
250
233
  console.log(chalk.cyan("httptmux"));
251
234
 
252
235
  let keepGoing = true;
@@ -276,17 +259,40 @@ async function main() {
276
259
  else if (action === "View history") {
277
260
  const history = loadHistory();
278
261
  if (history.length === 0) console.log(chalk.yellow("No history found."));
279
- else history.forEach((entry, i) => console.log(chalk.gray(`${i + 1}. [${entry.timestamp}] ${entry.method} ${entry.url} (status: ${entry.status})`)));
262
+ else history.forEach((entry, i) =>
263
+ console.log(chalk.gray(`${i + 1}. [${entry.timestamp}] ${entry.method} ${entry.url} (status: ${entry.status})`))
264
+ );
280
265
  }
281
266
  else if (action === "Re-run from history") await rerunHistory();
282
- else if (action === "Search history") await searchHistory();
267
+ else if (action === "Search history") {
268
+ const { keyword } = await inquirer.prompt([{ type: "input", name: "keyword", message: chalk.blue("Enter keyword to search:") }]);
269
+ const results = loadHistory().filter(entry =>
270
+ entry.method.includes(keyword.toUpperCase()) ||
271
+ entry.url.includes(keyword) ||
272
+ String(entry.status).includes(keyword)
273
+ );
274
+ if (results.length === 0) console.log(chalk.yellow("No matching entries."));
275
+ else results.forEach((entry, i) =>
276
+ console.log(chalk.gray(`${i + 1}. [${entry.timestamp}] ${entry.method} ${entry.url} (status: ${entry.status})`))
277
+ );
278
+ }
283
279
  else if (action === "Clear history") clearHistory();
284
280
  else if (action === "Export history") {
285
281
  const { filePath } = await inquirer.prompt([{ type: "input", name: "filePath", message: chalk.blue("Export file path (default in HOME):") }]);
286
282
  exportHistory(filePath && filePath.trim() ? filePath.trim() : undefined);
287
283
  }
288
- else if (action === "Filter history") await filterHistoryPrompt();
289
- else if (action === "Set JWT token") await jwtMode();
284
+ else if (action === "Filter history") {
285
+ const { status, since } = await inquirer.prompt([
286
+ { type: "input", name: "status", message: chalk.blue("Filter by status code (or leave empty):") },
287
+ { type: "input", name: "since", message: chalk.blue("Filter by date (YYYY-MM-DD or leave empty):") }
288
+ ]);
289
+ filterHistory(status, since);
290
+ }
291
+ else if (action === "Set JWT token") {
292
+ const { token } = await inquirer.prompt([{ type: "input", name: "token", message: chalk.blue("Enter JWT token:") }]);
293
+ if (!token || !token.includes(".")) console.log(chalk.red("Invalid JWT format."));
294
+ else saveJWT(token);
295
+ }
290
296
  else if (action === "Help") printHelp();
291
297
  else if (action === "Version") printVersion();
292
298
  else keepGoing = false;
@@ -296,6 +302,7 @@ async function main() {
296
302
  console.log(chalk.cyan("\nExiting httptmux. Goodbye!"));
297
303
  }
298
304
 
305
+ // Entry point
299
306
  main().catch(err => {
300
307
  console.error(chalk.red("Fatal error:"), err);
301
308
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "httptmux",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "httptmux is an interactive CLI tool for API requests. Originally made for Termux (hence the name), but decided to make a second index.js file for desktops",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,23 +18,24 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "axios": "^1.6.0",
21
+ "chalk": "^5.3.0",
21
22
  "inquirer": "^9.0.0",
22
- "chalk": "^5.3.0"
23
+ "minimist": "^1.2.8"
23
24
  },
24
25
  "license": "MIT",
25
26
  "keywords": [
26
- "http",
27
- "cli",
28
- "termux",
29
- "desktop",
30
- "api",
31
- "requests",
32
- "axios",
33
- "interactive",
34
- "jwt",
35
- "developer-tools",
36
- "nodejs",
37
- "command-line",
38
- "automation"
39
- ]
27
+ "http",
28
+ "cli",
29
+ "termux",
30
+ "desktop",
31
+ "api",
32
+ "requests",
33
+ "axios",
34
+ "interactive",
35
+ "jwt",
36
+ "developer-tools",
37
+ "nodejs",
38
+ "command-line",
39
+ "automation"
40
+ ]
40
41
  }