httptmux 1.0.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 (3) hide show
  1. package/index.js +228 -0
  2. package/index_desktop.js +228 -0
  3. package/package.json +16 -0
package/index.js ADDED
@@ -0,0 +1,228 @@
1
+ #!/data/data/com.termux/files/usr/bin/node
2
+ import inquirer from 'inquirer';
3
+ import axios from 'axios';
4
+ import chalk from 'chalk';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import os from 'os';
8
+
9
+ // Load package.json for version info
10
+ import pkg from "./package.json" with { type: "json" };
11
+
12
+ // Check CLI flags
13
+ const verbose = process.argv.includes("--verbose");
14
+ const showHelp = process.argv.includes("--help");
15
+ const showVersion = process.argv.includes("--version");
16
+
17
+ // History & JWT file paths
18
+ const historyFile = path.join(os.homedir(), ".api-cli-history.json");
19
+ const jwtFile = path.join(os.homedir(), ".api-cli-jwt.json");
20
+
21
+ function logVerbose(message) {
22
+ if (verbose) {
23
+ console.log(chalk.dim(`Verbose: ${message}`));
24
+ }
25
+ }
26
+
27
+ // HELP text
28
+ function printHelp() {
29
+ console.log(chalk.cyan("\nhttptmux CLI Help"));
30
+ console.log(`
31
+ Usage: nreq [options]
32
+
33
+ Options:
34
+ --help Show this help message
35
+ --version Show version number
36
+ --verbose Enable verbose logging
37
+
38
+ Interactive Menu Options:
39
+ • Make new request
40
+ • View history
41
+ • Re-run from history
42
+ • Search history
43
+ • Set JWT token
44
+ • Help
45
+ • Version
46
+ • Exit
47
+ `);
48
+ }
49
+
50
+ // VERSION text
51
+ function printVersion() {
52
+ console.log(chalk.cyan(`\nhttptmux v${pkg.version}`));
53
+ }
54
+
55
+ // Load history
56
+ function loadHistory() {
57
+ if (fs.existsSync(historyFile)) {
58
+ try {
59
+ return JSON.parse(fs.readFileSync(historyFile, "utf8"));
60
+ } catch {
61
+ return [];
62
+ }
63
+ }
64
+ return [];
65
+ }
66
+
67
+ // Save history
68
+ function saveHistory(entry) {
69
+ const history = loadHistory();
70
+ history.push(entry);
71
+ fs.writeFileSync(historyFile, JSON.stringify(history, null, 2));
72
+ }
73
+
74
+ // Load JWT
75
+ function loadJWT() {
76
+ if (fs.existsSync(jwtFile)) {
77
+ try {
78
+ const data = JSON.parse(fs.readFileSync(jwtFile, "utf8"));
79
+ return data.token || null;
80
+ } catch {
81
+ return null;
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+
87
+ // Save JWT
88
+ function saveJWT(token) {
89
+ fs.writeFileSync(jwtFile, JSON.stringify({ token }, null, 2));
90
+ console.log(chalk.green("JWT saved successfully."));
91
+ }
92
+
93
+ // Execute a request object
94
+ async function executeRequest({ method, url, headers, body }) {
95
+ try {
96
+ const jwt = loadJWT();
97
+ if (jwt) {
98
+ headers = { ...headers, Authorization: `Bearer ${jwt}` };
99
+ logVerbose("JWT attached to request headers.");
100
+ }
101
+
102
+ const start = Date.now();
103
+ const response = await axios({ method, url, headers, data: body });
104
+ const duration = Date.now() - start;
105
+
106
+ console.log(chalk.green("\nResponse received:"));
107
+ console.log(chalk.gray(JSON.stringify(response.data, null, 2)));
108
+
109
+ logVerbose(`Request completed in ${duration} ms`);
110
+ logVerbose(`Status code: ${response.status}`);
111
+
112
+ saveHistory({
113
+ timestamp: new Date().toISOString(),
114
+ method,
115
+ url,
116
+ headers,
117
+ body,
118
+ status: response.status,
119
+ duration
120
+ });
121
+ } catch (error) {
122
+ console.error(chalk.red("\nError making request:"), chalk.red(error.message));
123
+ if (verbose && error.response) {
124
+ console.error(chalk.dim("Verbose: Error details →"), error.response.data);
125
+ }
126
+ saveHistory({
127
+ timestamp: new Date().toISOString(),
128
+ method,
129
+ url,
130
+ headers,
131
+ body,
132
+ status: error.response ? error.response.status : "ERROR",
133
+ error: error.message
134
+ });
135
+ }
136
+ }
137
+
138
+ // Prompt user for new request
139
+ async function runRequest() {
140
+ const { method } = await inquirer.prompt([
141
+ { type: "list", name: "method", message: chalk.blue("Select HTTP method:"), choices: ["GET", "POST", "PUT", "DELETE"] }
142
+ ]);
143
+ const { url } = await inquirer.prompt([{ type: "input", name: "url", message: chalk.blue("Enter API URL:") }]);
144
+ const { headersInput } = await inquirer.prompt([{ type: "input", name: "headersInput", message: chalk.yellow("Enter headers as JSON (or leave empty):") }]);
145
+ let bodyInput = "";
146
+ if (method === "POST" || method === "PUT") {
147
+ const bodyAnswer = await inquirer.prompt([{ type: "input", name: "bodyInput", message: chalk.yellow("Enter request body as JSON (or leave empty):") }]);
148
+ bodyInput = bodyAnswer.bodyInput;
149
+ }
150
+
151
+ let headers = {};
152
+ let body = {};
153
+ try { if (headersInput) headers = JSON.parse(headersInput); } catch { console.log(chalk.yellow("Invalid JSON for headers.")); }
154
+ try { if (bodyInput) body = JSON.parse(bodyInput); } catch { console.log(chalk.yellow("Invalid JSON for body.")); }
155
+
156
+ await executeRequest({ method, url, headers, body });
157
+ }
158
+
159
+ // Re-run from history
160
+ async function rerunHistory() {
161
+ const history = loadHistory();
162
+ if (history.length === 0) return console.log(chalk.yellow("No history found."));
163
+ const choices = history.map((entry, i) => ({ name: `${i + 1}. [${entry.timestamp}] ${entry.method} ${entry.url} (status: ${entry.status})`, value: i }));
164
+ const { index } = await inquirer.prompt([{ type: "list", name: "index", message: chalk.blue("Select a request to re-run:"), choices }]);
165
+ const entry = history[index];
166
+ console.log(chalk.cyan(`\nRe-running: ${entry.method} ${entry.url}`));
167
+ await executeRequest(entry);
168
+ }
169
+
170
+ // Search/filter history
171
+ async function searchHistory() {
172
+ const history = loadHistory();
173
+ if (history.length === 0) return console.log(chalk.yellow("No history found."));
174
+ const { keyword } = await inquirer.prompt([{ type: "input", name: "keyword", message: chalk.blue("Enter keyword to search (method, URL, status):") }]);
175
+ const results = history.filter(entry => entry.method.includes(keyword.toUpperCase()) || entry.url.includes(keyword) || String(entry.status).includes(keyword));
176
+ if (results.length === 0) return console.log(chalk.yellow("No matching entries."));
177
+ console.log(chalk.cyan("\nSearch Results:"));
178
+ results.forEach((entry, i) => console.log(chalk.gray(`${i + 1}. [${entry.timestamp}] ${entry.method} ${entry.url} (status: ${entry.status})`)));
179
+ }
180
+
181
+ // JWT mode
182
+ async function jwtMode() {
183
+ const { token } = await inquirer.prompt([{ type: "input", name: "token", message: chalk.blue("Enter JWT token:") }]);
184
+ saveJWT(token);
185
+ }
186
+
187
+ // Main loop
188
+ async function main() {
189
+ if (showHelp) return printHelp();
190
+ if (showVersion) return printVersion();
191
+
192
+ console.log(chalk.cyan("httpnode"));
193
+
194
+ let keepGoing = true;
195
+ while (keepGoing) {
196
+ const { action } = await inquirer.prompt([
197
+ {
198
+ type: "list",
199
+ name: "action",
200
+ message: chalk.blue("Choose an action:"),
201
+ choices: [
202
+ "Make new request",
203
+ "View history",
204
+ "Re-run from history",
205
+ "Search history",
206
+ "Set JWT token",
207
+ "Help",
208
+ "Version",
209
+ "Exit"
210
+ ]
211
+ }
212
+ ]);
213
+
214
+ if (action === "Make new request") await runRequest();
215
+ else if (action === "View history") console.log(chalk.cyan("\nRequest History:"), loadHistory());
216
+ else if (action === "Re-run from history") await rerunHistory();
217
+ else if (action === "Search history") await searchHistory();
218
+ else if (action === "Set JWT token") await jwtMode();
219
+ else if (action === "Help") printHelp();
220
+ else if (action === "Version") printVersion();
221
+ else keepGoing = false;
222
+ }
223
+
224
+ logVerbose("Program finished.");
225
+ console.log(chalk.cyan("\nExiting httptmux. Goodbye!"));
226
+ }
227
+
228
+ main();
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env node
2
+ import inquirer from 'inquirer';
3
+ import axios from 'axios';
4
+ import chalk from 'chalk';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import os from 'os';
8
+
9
+ // Load package.json for version info
10
+ import pkg from "./package.json" with { type: "json" };
11
+
12
+ // Check CLI flags
13
+ const verbose = process.argv.includes("--verbose");
14
+ const showHelp = process.argv.includes("--help");
15
+ const showVersion = process.argv.includes("--version");
16
+
17
+ // History & JWT file paths
18
+ const historyFile = path.join(os.homedir(), ".api-cli-history.json");
19
+ const jwtFile = path.join(os.homedir(), ".api-cli-jwt.json");
20
+
21
+ function logVerbose(message) {
22
+ if (verbose) {
23
+ console.log(chalk.dim(`Verbose: ${message}`));
24
+ }
25
+ }
26
+
27
+ // HELP text
28
+ function printHelp() {
29
+ console.log(chalk.cyan("\nhttptmux (desktop) CLI Help"));
30
+ console.log(`
31
+ Usage: nreq [options]
32
+
33
+ Options:
34
+ --help Show this help message
35
+ --version Show version number
36
+ --verbose Enable verbose logging
37
+
38
+ Interactive Menu Options:
39
+ • Make new request
40
+ • View history
41
+ • Re-run from history
42
+ • Search history
43
+ • Set JWT token
44
+ • Help
45
+ • Version
46
+ • Exit
47
+ `);
48
+ }
49
+
50
+ // VERSION text
51
+ function printVersion() {
52
+ console.log(chalk.cyan(`\nhttpntmux v${pkg.version}`));
53
+ }
54
+
55
+ // Load history
56
+ function loadHistory() {
57
+ if (fs.existsSync(historyFile)) {
58
+ try {
59
+ return JSON.parse(fs.readFileSync(historyFile, "utf8"));
60
+ } catch {
61
+ return [];
62
+ }
63
+ }
64
+ return [];
65
+ }
66
+
67
+ // Save history
68
+ function saveHistory(entry) {
69
+ const history = loadHistory();
70
+ history.push(entry);
71
+ fs.writeFileSync(historyFile, JSON.stringify(history, null, 2));
72
+ }
73
+
74
+ // Load JWT
75
+ function loadJWT() {
76
+ if (fs.existsSync(jwtFile)) {
77
+ try {
78
+ const data = JSON.parse(fs.readFileSync(jwtFile, "utf8"));
79
+ return data.token || null;
80
+ } catch {
81
+ return null;
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+
87
+ // Save JWT
88
+ function saveJWT(token) {
89
+ fs.writeFileSync(jwtFile, JSON.stringify({ token }, null, 2));
90
+ console.log(chalk.green("JWT saved successfully."));
91
+ }
92
+
93
+ // Execute a request object
94
+ async function executeRequest({ method, url, headers, body }) {
95
+ try {
96
+ const jwt = loadJWT();
97
+ if (jwt) {
98
+ headers = { ...headers, Authorization: `Bearer ${jwt}` };
99
+ logVerbose("JWT attached to request headers.");
100
+ }
101
+
102
+ const start = Date.now();
103
+ const response = await axios({ method, url, headers, data: body });
104
+ const duration = Date.now() - start;
105
+
106
+ console.log(chalk.green("\nResponse received:"));
107
+ console.log(chalk.gray(JSON.stringify(response.data, null, 2)));
108
+
109
+ logVerbose(`Request completed in ${duration} ms`);
110
+ logVerbose(`Status code: ${response.status}`);
111
+
112
+ saveHistory({
113
+ timestamp: new Date().toISOString(),
114
+ method,
115
+ url,
116
+ headers,
117
+ body,
118
+ status: response.status,
119
+ duration
120
+ });
121
+ } catch (error) {
122
+ console.error(chalk.red("\nError making request:"), chalk.red(error.message));
123
+ if (verbose && error.response) {
124
+ console.error(chalk.dim("Verbose: Error details →"), error.response.data);
125
+ }
126
+ saveHistory({
127
+ timestamp: new Date().toISOString(),
128
+ method,
129
+ url,
130
+ headers,
131
+ body,
132
+ status: error.response ? error.response.status : "ERROR",
133
+ error: error.message
134
+ });
135
+ }
136
+ }
137
+
138
+ // Prompt user for new request
139
+ async function runRequest() {
140
+ const { method } = await inquirer.prompt([
141
+ { type: "list", name: "method", message: chalk.blue("Select HTTP method:"), choices: ["GET", "POST", "PUT", "DELETE"] }
142
+ ]);
143
+ const { url } = await inquirer.prompt([{ type: "input", name: "url", message: chalk.blue("Enter API URL:") }]);
144
+ const { headersInput } = await inquirer.prompt([{ type: "input", name: "headersInput", message: chalk.yellow("Enter headers as JSON (or leave empty):") }]);
145
+ let bodyInput = "";
146
+ if (method === "POST" || method === "PUT") {
147
+ const bodyAnswer = await inquirer.prompt([{ type: "input", name: "bodyInput", message: chalk.yellow("Enter request body as JSON (or leave empty):") }]);
148
+ bodyInput = bodyAnswer.bodyInput;
149
+ }
150
+
151
+ let headers = {};
152
+ let body = {};
153
+ try { if (headersInput) headers = JSON.parse(headersInput); } catch { console.log(chalk.yellow("Invalid JSON for headers.")); }
154
+ try { if (bodyInput) body = JSON.parse(bodyInput); } catch { console.log(chalk.yellow("Invalid JSON for body.")); }
155
+
156
+ await executeRequest({ method, url, headers, body });
157
+ }
158
+
159
+ // Re-run from history
160
+ async function rerunHistory() {
161
+ const history = loadHistory();
162
+ if (history.length === 0) return console.log(chalk.yellow("No history found."));
163
+ const choices = history.map((entry, i) => ({ name: `${i + 1}. [${entry.timestamp}] ${entry.method} ${entry.url} (status: ${entry.status})`, value: i }));
164
+ const { index } = await inquirer.prompt([{ type: "list", name: "index", message: chalk.blue("Select a request to re-run:"), choices }]);
165
+ const entry = history[index];
166
+ console.log(chalk.cyan(`\nRe-running: ${entry.method} ${entry.url}`));
167
+ await executeRequest(entry);
168
+ }
169
+
170
+ // Search/filter history
171
+ async function searchHistory() {
172
+ const history = loadHistory();
173
+ if (history.length === 0) return console.log(chalk.yellow("No history found."));
174
+ const { keyword } = await inquirer.prompt([{ type: "input", name: "keyword", message: chalk.blue("Enter keyword to search (method, URL, status):") }]);
175
+ const results = history.filter(entry => entry.method.includes(keyword.toUpperCase()) || entry.url.includes(keyword) || String(entry.status).includes(keyword));
176
+ if (results.length === 0) return console.log(chalk.yellow("No matching entries."));
177
+ console.log(chalk.cyan("\nSearch Results:"));
178
+ results.forEach((entry, i) => console.log(chalk.gray(`${i + 1}. [${entry.timestamp}] ${entry.method} ${entry.url} (status: ${entry.status})`)));
179
+ }
180
+
181
+ // JWT mode
182
+ async function jwtMode() {
183
+ const { token } = await inquirer.prompt([{ type: "input", name: "token", message: chalk.blue("Enter JWT token:") }]);
184
+ saveJWT(token);
185
+ }
186
+
187
+ // Main loop
188
+ async function main() {
189
+ if (showHelp) return printHelp();
190
+ if (showVersion) return printVersion();
191
+
192
+ console.log(chalk.cyan("httptmux"));
193
+
194
+ let keepGoing = true;
195
+ while (keepGoing) {
196
+ const { action } = await inquirer.prompt([
197
+ {
198
+ type: "list",
199
+ name: "action",
200
+ message: chalk.blue("Choose an action:"),
201
+ choices: [
202
+ "Make new request",
203
+ "View history",
204
+ "Re-run from history",
205
+ "Search history",
206
+ "Set JWT token",
207
+ "Help",
208
+ "Version",
209
+ "Exit"
210
+ ]
211
+ }
212
+ ]);
213
+
214
+ if (action === "Make new request") await runRequest();
215
+ else if (action === "View history") console.log(chalk.cyan("\nRequest History:"), loadHistory());
216
+ else if (action === "Re-run from history") await rerunHistory();
217
+ else if (action === "Search history") await searchHistory();
218
+ else if (action === "Set JWT token") await jwtMode();
219
+ else if (action === "Help") printHelp();
220
+ else if (action === "Version") printVersion();
221
+ else keepGoing = false;
222
+ }
223
+
224
+ logVerbose("Program finished.");
225
+ console.log(chalk.cyan("\nExiting httptmux. Goodbye!"));
226
+ }
227
+
228
+ main();
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "httptmux",
3
+ "version": "1.0.0",
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
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "httptmux": "./index.js",
9
+ "httptmuxd": "./index_desktop.js"
10
+ },
11
+ "dependencies": {
12
+ "axios": "^1.6.0",
13
+ "inquirer": "^9.0.0",
14
+ "chalk": "^5.3.0"
15
+ }
16
+ }