recker 1.0.15-next.50d74b2 ā 1.0.15-next.64a2dc9
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 +10 -1
- package/dist/ai/providers/anthropic.d.ts.map +1 -1
- package/dist/ai/providers/anthropic.js +4 -1
- package/dist/ai/providers/base.d.ts.map +1 -1
- package/dist/ai/providers/base.js +7 -2
- package/dist/ai/rate-limiter.d.ts.map +1 -1
- package/dist/ai/rate-limiter.js +4 -1
- package/dist/bench/generator.d.ts.map +1 -1
- package/dist/bench/generator.js +7 -3
- package/dist/bench/stats.d.ts.map +1 -1
- package/dist/bench/stats.js +43 -10
- package/dist/cache/memory-storage.d.ts.map +1 -1
- package/dist/cache/memory-storage.js +3 -2
- package/dist/cli/handler.js +14 -14
- package/dist/cli/index.js +582 -49
- package/dist/cli/presets.js +5 -5
- package/dist/cli/tui/ai-chat.js +10 -10
- package/dist/cli/tui/load-dashboard.d.ts.map +1 -1
- package/dist/cli/tui/load-dashboard.js +96 -55
- package/dist/cli/tui/scroll-buffer.d.ts +1 -1
- package/dist/cli/tui/scroll-buffer.d.ts.map +1 -1
- package/dist/cli/tui/scroll-buffer.js +2 -2
- package/dist/cli/tui/shell.d.ts +3 -0
- package/dist/cli/tui/shell.d.ts.map +1 -1
- package/dist/cli/tui/shell.js +173 -10
- package/dist/cli/tui/websocket.js +17 -17
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +18 -26
- package/dist/core/errors.d.ts +109 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +214 -1
- package/dist/core/request-promise.d.ts.map +1 -1
- package/dist/core/request-promise.js +5 -6
- package/dist/core/response.d.ts.map +1 -1
- package/dist/core/response.js +5 -6
- package/dist/dns/propagation.d.ts +3 -1
- package/dist/dns/propagation.d.ts.map +1 -1
- package/dist/dns/propagation.js +99 -59
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +10 -11
- package/dist/mcp/embeddings-loader.d.ts.map +1 -1
- package/dist/mcp/embeddings-loader.js +12 -2
- package/dist/mcp/geoip-loader.d.ts +11 -0
- package/dist/mcp/geoip-loader.d.ts.map +1 -0
- package/dist/mcp/geoip-loader.js +107 -0
- package/dist/mcp/ip-intel.d.ts +28 -0
- package/dist/mcp/ip-intel.d.ts.map +1 -0
- package/dist/mcp/ip-intel.js +209 -0
- package/dist/mcp/search/hybrid-search.d.ts.map +1 -1
- package/dist/mcp/search/hybrid-search.js +5 -1
- package/dist/mcp/search/math.d.ts.map +1 -1
- package/dist/mcp/search/math.js +5 -1
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +114 -1
- package/dist/plugins/compression.js +4 -2
- package/dist/plugins/har-player.d.ts.map +1 -1
- package/dist/plugins/har-player.js +8 -11
- package/dist/plugins/odata.d.ts.map +1 -1
- package/dist/plugins/odata.js +5 -2
- package/dist/protocols/ftp.d.ts.map +1 -1
- package/dist/protocols/ftp.js +69 -16
- package/dist/protocols/sftp.d.ts.map +1 -1
- package/dist/protocols/sftp.js +13 -3
- package/dist/protocols/telnet.d.ts.map +1 -1
- package/dist/protocols/telnet.js +25 -6
- package/dist/transport/base-udp.d.ts.map +1 -1
- package/dist/transport/base-udp.js +7 -4
- package/dist/transport/udp-response.d.ts.map +1 -1
- package/dist/transport/udp-response.js +10 -3
- package/dist/transport/udp.d.ts.map +1 -1
- package/dist/transport/udp.js +5 -1
- package/dist/transport/undici.d.ts.map +1 -1
- package/dist/transport/undici.js +75 -63
- package/dist/utils/agent-manager.d.ts +1 -0
- package/dist/utils/agent-manager.d.ts.map +1 -1
- package/dist/utils/agent-manager.js +11 -0
- package/dist/utils/client-pool.d.ts.map +1 -1
- package/dist/utils/client-pool.js +4 -1
- package/dist/utils/dns-toolkit.d.ts +88 -1
- package/dist/utils/dns-toolkit.d.ts.map +1 -1
- package/dist/utils/dns-toolkit.js +704 -6
- package/dist/utils/doh.d.ts.map +1 -1
- package/dist/utils/doh.js +13 -16
- package/dist/utils/download.d.ts.map +1 -1
- package/dist/utils/download.js +10 -11
- package/dist/utils/rdap.d.ts +9 -0
- package/dist/utils/rdap.d.ts.map +1 -1
- package/dist/utils/rdap.js +78 -9
- package/dist/utils/security-grader.d.ts +47 -0
- package/dist/utils/security-grader.d.ts.map +1 -0
- package/dist/utils/security-grader.js +637 -0
- package/dist/utils/sparkline.d.ts +18 -0
- package/dist/utils/sparkline.d.ts.map +1 -0
- package/dist/utils/sparkline.js +55 -0
- package/dist/utils/sse.d.ts.map +1 -1
- package/dist/utils/sse.js +5 -6
- package/dist/utils/system-metrics.d.ts +26 -0
- package/dist/utils/system-metrics.d.ts.map +1 -0
- package/dist/utils/system-metrics.js +81 -0
- package/dist/webrtc/index.d.ts.map +1 -1
- package/dist/webrtc/index.js +21 -7
- package/dist/websocket/client.d.ts.map +1 -1
- package/dist/websocket/client.js +13 -16
- package/package.json +2 -1
package/dist/cli/presets.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as presets from '../presets/index.js';
|
|
2
|
-
import
|
|
2
|
+
import colors from '../utils/colors.js';
|
|
3
3
|
const ENV_MAPPING = {
|
|
4
4
|
openai: ['OPENAI_API_KEY'],
|
|
5
5
|
anthropic: ['ANTHROPIC_API_KEY'],
|
|
@@ -14,7 +14,7 @@ const ENV_MAPPING = {
|
|
|
14
14
|
export function resolvePreset(name) {
|
|
15
15
|
const presetFn = presets[name];
|
|
16
16
|
if (!presetFn) {
|
|
17
|
-
console.error(
|
|
17
|
+
console.error(colors.red(`Error: Unknown preset '@${name}'`));
|
|
18
18
|
process.exit(1);
|
|
19
19
|
}
|
|
20
20
|
const requiredEnvs = ENV_MAPPING[name];
|
|
@@ -24,7 +24,7 @@ export function resolvePreset(name) {
|
|
|
24
24
|
for (const envVar of requiredEnvs) {
|
|
25
25
|
const value = process.env[envVar];
|
|
26
26
|
if (!value) {
|
|
27
|
-
console.error(
|
|
27
|
+
console.error(colors.yellow(`Warning: Missing env variable ${envVar} for preset @${name}`));
|
|
28
28
|
missing = true;
|
|
29
29
|
}
|
|
30
30
|
else {
|
|
@@ -33,14 +33,14 @@ export function resolvePreset(name) {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
if (missing) {
|
|
36
|
-
console.log(
|
|
36
|
+
console.log(colors.gray(`Tip: export ${requiredEnvs.join('=... ')}=...`));
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
try {
|
|
40
40
|
return presetFn(options);
|
|
41
41
|
}
|
|
42
42
|
catch (error) {
|
|
43
|
-
console.error(
|
|
43
|
+
console.error(colors.red(`Error initializing preset @${name}: ${error.message}`));
|
|
44
44
|
process.exit(1);
|
|
45
45
|
}
|
|
46
46
|
}
|
package/dist/cli/tui/ai-chat.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import readline from 'node:readline';
|
|
2
|
-
import
|
|
2
|
+
import colors from '../../utils/colors.js';
|
|
3
3
|
import { createAI } from '../../ai/client.js';
|
|
4
4
|
export async function startAIChat(rl, provider = 'openai', apiKey, model) {
|
|
5
5
|
console.clear();
|
|
6
|
-
console.log(
|
|
7
|
-
console.log(
|
|
6
|
+
console.log(colors.bold(colors.magenta(`š¤ Rek AI Chat (${provider})`)));
|
|
7
|
+
console.log(colors.gray('Type your message. Ctrl+C to exit.'));
|
|
8
8
|
const envKey = provider === 'openai' ? 'OPENAI_API_KEY' : 'ANTHROPIC_API_KEY';
|
|
9
9
|
const key = apiKey || process.env[envKey];
|
|
10
10
|
if (!key) {
|
|
11
|
-
console.log(
|
|
11
|
+
console.log(colors.yellow(`
|
|
12
12
|
Warning: No API Key found for ${provider}.`));
|
|
13
|
-
console.log(`Please set it via environment variable ${
|
|
13
|
+
console.log(`Please set it via environment variable ${colors.bold(envKey)} or passing it to the command.`);
|
|
14
14
|
console.log(`Example: set ${envKey}=sk-... inside the shell.`);
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
@@ -24,7 +24,7 @@ Warning: No API Key found for ${provider}.`));
|
|
|
24
24
|
const history = [
|
|
25
25
|
{ role: 'system', content: 'You are Recker AI, a helpful and concise assistant in a terminal environment.' }
|
|
26
26
|
];
|
|
27
|
-
rl.setPrompt(
|
|
27
|
+
rl.setPrompt(colors.magenta('You āŗ '));
|
|
28
28
|
rl.prompt();
|
|
29
29
|
return new Promise((resolve) => {
|
|
30
30
|
const onLine = async (line) => {
|
|
@@ -36,7 +36,7 @@ Warning: No API Key found for ${provider}.`));
|
|
|
36
36
|
if (input.toLowerCase() === '/clear') {
|
|
37
37
|
history.length = 1;
|
|
38
38
|
console.clear();
|
|
39
|
-
console.log(
|
|
39
|
+
console.log(colors.gray('Context cleared.'));
|
|
40
40
|
rl.prompt();
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
@@ -47,7 +47,7 @@ Warning: No API Key found for ${provider}.`));
|
|
|
47
47
|
}
|
|
48
48
|
history.push({ role: 'user', content: input });
|
|
49
49
|
rl.pause();
|
|
50
|
-
process.stdout.write(
|
|
50
|
+
process.stdout.write(colors.cyan('AI āŗ '));
|
|
51
51
|
let fullResponse = '';
|
|
52
52
|
try {
|
|
53
53
|
const stream = await client.stream({
|
|
@@ -66,10 +66,10 @@ Warning: No API Key found for ${provider}.`));
|
|
|
66
66
|
history.push({ role: 'assistant', content: fullResponse });
|
|
67
67
|
}
|
|
68
68
|
catch (error) {
|
|
69
|
-
console.log(
|
|
69
|
+
console.log(colors.red(`
|
|
70
70
|
Error: ${error.message}`));
|
|
71
71
|
if (error.cause)
|
|
72
|
-
console.log(
|
|
72
|
+
console.log(colors.gray(error.cause));
|
|
73
73
|
}
|
|
74
74
|
finally {
|
|
75
75
|
rl.resume();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-dashboard.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/load-dashboard.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiB,UAAU,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"load-dashboard.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/load-dashboard.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiB,UAAU,EAAE,MAAM,0BAA0B,CAAC;AASrE,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,UAAU,iBAuG1D"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import colors from '../../utils/colors.js';
|
|
2
2
|
import { plot } from '../../utils/chart.js';
|
|
3
3
|
import readline from 'node:readline';
|
|
4
4
|
import { LoadGenerator } from '../../bench/generator.js';
|
|
5
|
+
import { SparklineBuffer } from '../../utils/sparkline.js';
|
|
6
|
+
import { SystemMetrics } from '../../utils/system-metrics.js';
|
|
5
7
|
const ALTERNATE_SCREEN_ENTER = '\x1b[?1049h';
|
|
6
8
|
const ALTERNATE_SCREEN_EXIT = '\x1b[?1049l';
|
|
9
|
+
const SPARKLINE_WIDTH = 40;
|
|
7
10
|
export async function startLoadDashboard(config) {
|
|
8
11
|
process.stdout.write(ALTERNATE_SCREEN_ENTER);
|
|
9
12
|
readline.emitKeypressEvents(process.stdin);
|
|
@@ -12,6 +15,22 @@ export async function startLoadDashboard(config) {
|
|
|
12
15
|
process.stdin.resume();
|
|
13
16
|
}
|
|
14
17
|
const generator = new LoadGenerator(config);
|
|
18
|
+
const sysMetrics = new SystemMetrics();
|
|
19
|
+
const cpuBuffer = new SparklineBuffer(SPARKLINE_WIDTH);
|
|
20
|
+
const memBuffer = new SparklineBuffer(SPARKLINE_WIDTH);
|
|
21
|
+
let currentCpu = 0;
|
|
22
|
+
let currentMem = { percent: 0, used: 0, total: 0 };
|
|
23
|
+
sysMetrics.onSnapshot((snap) => {
|
|
24
|
+
cpuBuffer.push(snap.cpu);
|
|
25
|
+
memBuffer.push(snap.memory);
|
|
26
|
+
currentCpu = snap.cpu;
|
|
27
|
+
currentMem = {
|
|
28
|
+
percent: snap.memory,
|
|
29
|
+
used: snap.memoryUsed,
|
|
30
|
+
total: snap.memoryTotal
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
sysMetrics.startPolling(1000);
|
|
15
34
|
let abortReject;
|
|
16
35
|
const abortPromise = new Promise((_, reject) => {
|
|
17
36
|
abortReject = reject;
|
|
@@ -19,6 +38,7 @@ export async function startLoadDashboard(config) {
|
|
|
19
38
|
const onKeypress = (_str, key) => {
|
|
20
39
|
if (key && (key.name === 'escape' || (key.ctrl && key.name === 'c'))) {
|
|
21
40
|
generator.stop();
|
|
41
|
+
sysMetrics.stopPolling();
|
|
22
42
|
if (abortReject)
|
|
23
43
|
abortReject(new Error('User aborted'));
|
|
24
44
|
}
|
|
@@ -43,7 +63,7 @@ export async function startLoadDashboard(config) {
|
|
|
43
63
|
latencyHistory.push(snapshot.p95);
|
|
44
64
|
usersHistory.shift();
|
|
45
65
|
usersHistory.push(snapshot.activeUsers);
|
|
46
|
-
render(config, elapsed, remaining, snapshot, rpsHistory, latencyHistory, usersHistory, generator.stats);
|
|
66
|
+
render(config, elapsed, remaining, snapshot, rpsHistory, latencyHistory, usersHistory, generator.stats, cpuBuffer, memBuffer, currentCpu, currentMem);
|
|
47
67
|
}, 1000);
|
|
48
68
|
try {
|
|
49
69
|
await Promise.race([runPromise, abortPromise]);
|
|
@@ -54,6 +74,7 @@ export async function startLoadDashboard(config) {
|
|
|
54
74
|
}
|
|
55
75
|
finally {
|
|
56
76
|
clearInterval(interval);
|
|
77
|
+
sysMetrics.stopPolling();
|
|
57
78
|
process.stdin.off('keypress', onKeypress);
|
|
58
79
|
if (process.stdin.isTTY)
|
|
59
80
|
process.stdin.setRawMode(false);
|
|
@@ -61,55 +82,63 @@ export async function startLoadDashboard(config) {
|
|
|
61
82
|
}
|
|
62
83
|
renderFinalReport(generator.stats, config);
|
|
63
84
|
}
|
|
64
|
-
function render(config, elapsed, remaining, snapshot, rpsHistory, latencyHistory, usersHistory, stats) {
|
|
85
|
+
function render(config, elapsed, remaining, snapshot, rpsHistory, latencyHistory, usersHistory, stats, cpuBuffer, memBuffer, currentCpu, currentMem) {
|
|
65
86
|
readline.cursorTo(process.stdout, 0, 0);
|
|
66
87
|
readline.clearScreenDown(process.stdout);
|
|
67
|
-
console.log(
|
|
68
|
-
console.log(
|
|
69
|
-
console.log(
|
|
70
|
-
console.log(
|
|
88
|
+
console.log(colors.bold(colors.cyan('š„ Rek Load Generator')));
|
|
89
|
+
console.log(colors.gray(`Target: ${config.url}`));
|
|
90
|
+
console.log(colors.gray(`Mode: ${config.mode.toUpperCase()} ${config.http2 ? '(HTTP/2)' : ''}`));
|
|
91
|
+
console.log(colors.gray('Press ESC to stop'));
|
|
92
|
+
console.log('');
|
|
93
|
+
console.log(`${colors.white('Time:')} ${colors.green(elapsed + 's')} ` +
|
|
94
|
+
`${colors.gray('/')} ${config.duration}s ` +
|
|
95
|
+
`${colors.gray('(')}${colors.yellow(remaining + 's left')}${colors.gray(')')} ` +
|
|
96
|
+
`${colors.white('Reqs:')} ${colors.bold(String(stats.totalRequests))}`);
|
|
97
|
+
console.log(`${colors.blue('Users:')} ${colors.bold(String(snapshot.activeUsers))} ` +
|
|
98
|
+
`${colors.green('RPS:')} ${colors.bold(snapshot.rps.toFixed(0))} ` +
|
|
99
|
+
`${colors.magenta('Latency (P95):')} ${colors.bold(snapshot.p95.toFixed(0) + 'ms')} ` +
|
|
100
|
+
`${colors.white('Errors:')} ${stats.failed > 0 ? colors.red(String(stats.failed)) : colors.green('0')}`);
|
|
101
|
+
console.log(colors.gray('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
102
|
+
console.log(colors.bold(colors.blue('š„ Active Users')));
|
|
103
|
+
console.log(colors.blue(plot(usersHistory, { height: 4 })));
|
|
71
104
|
console.log('');
|
|
72
|
-
console.log(
|
|
73
|
-
|
|
74
|
-
`${pc.gray('(')}${pc.yellow(remaining + 's left')}${pc.gray(')')} ` +
|
|
75
|
-
`${pc.white('Reqs:')} ${pc.bold(String(stats.totalRequests))}`);
|
|
76
|
-
console.log(`${pc.blue('Users:')} ${pc.bold(String(snapshot.activeUsers))} ` +
|
|
77
|
-
`${pc.green('RPS:')} ${pc.bold(snapshot.rps.toFixed(0))} ` +
|
|
78
|
-
`${pc.magenta('Latency (P95):')} ${pc.bold(snapshot.p95.toFixed(0) + 'ms')} ` +
|
|
79
|
-
`${pc.white('Errors:')} ${stats.failed > 0 ? pc.red(String(stats.failed)) : pc.green('0')}`);
|
|
80
|
-
console.log(pc.gray('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
81
|
-
console.log(pc.bold(pc.blue('š„ Active Users')));
|
|
82
|
-
console.log(pc.blue(plot(usersHistory, { height: 4 })));
|
|
105
|
+
console.log(colors.bold(colors.green('ā” Requests per Second')));
|
|
106
|
+
console.log(colors.green(plot(rpsHistory, { height: 6 })));
|
|
83
107
|
console.log('');
|
|
84
|
-
console.log(
|
|
85
|
-
console.log(
|
|
108
|
+
console.log(colors.bold(colors.magenta('ā±ļø Latency P95 (ms)')));
|
|
109
|
+
console.log(colors.magenta(plot(latencyHistory, { height: 4 })));
|
|
86
110
|
console.log('');
|
|
87
|
-
console.log(
|
|
88
|
-
|
|
111
|
+
console.log(colors.bold(colors.yellow('š» System Resources')));
|
|
112
|
+
const cpuSparkline = cpuBuffer.render({ min: 0, max: 100 });
|
|
113
|
+
const memSparkline = memBuffer.render({ min: 0, max: 100 });
|
|
114
|
+
const memUsed = SystemMetrics.formatBytes(currentMem.used);
|
|
115
|
+
const memTotal = SystemMetrics.formatBytes(currentMem.total);
|
|
116
|
+
console.log(` ${colors.yellow('CPU')} ${colors.gray(cpuSparkline)} ${colors.bold(currentCpu.toFixed(0) + '%')}`);
|
|
117
|
+
console.log(` ${colors.yellow('RAM')} ${colors.gray(memSparkline)} ${colors.bold(currentMem.percent.toFixed(0) + '%')} ${colors.gray(`(${memUsed}/${memTotal})`)}`);
|
|
89
118
|
const recentErrors = stats.getRecentErrors();
|
|
90
119
|
if (recentErrors.length > 0) {
|
|
91
120
|
console.log('');
|
|
92
|
-
console.log(
|
|
121
|
+
console.log(colors.bold(colors.red('ā ļø Recent Errors')));
|
|
93
122
|
renderErrorList(recentErrors, 5);
|
|
94
123
|
}
|
|
95
124
|
}
|
|
96
125
|
function formatErrorEntry(entry) {
|
|
97
|
-
const count =
|
|
126
|
+
const count = colors.white(`${entry.count}x`);
|
|
98
127
|
if (entry.status === 0) {
|
|
99
|
-
return ` ${count} ${
|
|
128
|
+
return ` ${count} ${colors.red('ERR')} ${colors.gray(entry.message)}`;
|
|
100
129
|
}
|
|
101
|
-
const
|
|
102
|
-
return ` ${count} ${
|
|
130
|
+
const statusColor = entry.status >= 500 ? colors.red : colors.yellow;
|
|
131
|
+
return ` ${count} ${statusColor(String(entry.status))} ${colors.gray(entry.message)}`;
|
|
103
132
|
}
|
|
104
133
|
function formatStatusBadge(status) {
|
|
105
134
|
const code = String(status);
|
|
106
135
|
if (status >= 500)
|
|
107
|
-
return
|
|
136
|
+
return colors.bgRed(colors.white(` ${code} `));
|
|
108
137
|
if (status >= 400)
|
|
109
|
-
return
|
|
138
|
+
return colors.bgYellow(colors.black(` ${code} `));
|
|
110
139
|
if (status >= 300)
|
|
111
|
-
return
|
|
112
|
-
return
|
|
140
|
+
return colors.bgCyan(colors.black(` ${code} `));
|
|
141
|
+
return colors.bgGreen(colors.black(` ${code} `));
|
|
113
142
|
}
|
|
114
143
|
function renderErrorList(errors, maxItems = 10) {
|
|
115
144
|
const toShow = errors.slice(-maxItems);
|
|
@@ -117,53 +146,65 @@ function renderErrorList(errors, maxItems = 10) {
|
|
|
117
146
|
console.log(formatErrorEntry(entry));
|
|
118
147
|
}
|
|
119
148
|
if (errors.length > maxItems) {
|
|
120
|
-
console.log(
|
|
149
|
+
console.log(colors.gray(` ... and ${errors.length - maxItems} more`));
|
|
121
150
|
}
|
|
122
151
|
}
|
|
123
152
|
function renderFinalReport(stats, config) {
|
|
124
153
|
const summary = stats.getSummary();
|
|
125
|
-
console.log(
|
|
126
|
-
console.log(
|
|
154
|
+
console.log(colors.bold(colors.green('\nā
Load Test Complete')));
|
|
155
|
+
console.log(colors.bold('Configuration:'));
|
|
127
156
|
console.log(` URL: ${config.url}`);
|
|
128
157
|
console.log(` Mode: ${config.mode}`);
|
|
129
158
|
console.log(` Users: ${config.users}`);
|
|
130
159
|
console.log(` Duration: ${config.duration}s`);
|
|
131
|
-
console.log('\n' +
|
|
160
|
+
console.log('\n' + colors.bold('Traffic:'));
|
|
132
161
|
console.log(` Total Requests: ${summary.total}`);
|
|
133
|
-
console.log(` Successful: ${
|
|
134
|
-
console.log(` Failed: ${summary.failed > 0 ?
|
|
162
|
+
console.log(` Successful: ${colors.green(String(summary.success))}`);
|
|
163
|
+
console.log(` Failed: ${summary.failed > 0 ? colors.red(String(summary.failed)) : colors.gray('0')}`);
|
|
135
164
|
console.log(` Total Bytes: ${(summary.bytes / 1024 / 1024).toFixed(2)} MB`);
|
|
136
|
-
console.log('\n' +
|
|
165
|
+
console.log('\n' + colors.bold('Latency (ms):'));
|
|
137
166
|
console.log(` Avg: ${summary.latency.avg.toFixed(2)}`);
|
|
138
167
|
console.log(` P50: ${summary.latency.p50.toFixed(0)}`);
|
|
139
168
|
console.log(` P95: ${summary.latency.p95.toFixed(0)}`);
|
|
140
169
|
console.log(` P99: ${summary.latency.p99.toFixed(0)}`);
|
|
141
170
|
console.log(` Max: ${summary.latency.max.toFixed(0)}`);
|
|
142
171
|
if (Object.keys(summary.codes).length > 0) {
|
|
143
|
-
console.log('\n' +
|
|
144
|
-
Object.entries(summary.codes)
|
|
172
|
+
console.log('\n' + colors.bold('Status Codes:'));
|
|
173
|
+
const codeEntries = Object.entries(summary.codes)
|
|
145
174
|
.sort(([a], [b]) => Number(a) - Number(b))
|
|
146
|
-
.
|
|
147
|
-
const
|
|
148
|
-
|
|
175
|
+
.map(([code, count]) => {
|
|
176
|
+
const c = Number(code);
|
|
177
|
+
const color = c >= 500 ? colors.red : c >= 400 ? colors.yellow : colors.green;
|
|
178
|
+
return `${color(code)}: ${count}`;
|
|
149
179
|
});
|
|
180
|
+
console.log(` ${codeEntries.join(' ')}`);
|
|
150
181
|
}
|
|
151
182
|
const allErrors = stats.getErrors();
|
|
152
183
|
if (allErrors.length > 0) {
|
|
153
|
-
console.log('\n' +
|
|
154
|
-
renderErrorList(allErrors, 15);
|
|
155
|
-
const networkErrors = allErrors.filter(e => e.status === 0);
|
|
184
|
+
console.log('\n' + colors.bold(colors.red('Errors:')));
|
|
156
185
|
const httpErrors = allErrors.filter(e => e.status > 0);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
186
|
+
const netErrors = allErrors.filter(e => e.status === 0);
|
|
187
|
+
if (httpErrors.length > 0) {
|
|
188
|
+
const httpLine = httpErrors
|
|
189
|
+
.slice(0, 5)
|
|
190
|
+
.map(e => {
|
|
191
|
+
const color = e.status >= 500 ? colors.red : colors.yellow;
|
|
192
|
+
return `${e.count}x ${color(String(e.status))} ${colors.gray(e.message)}`;
|
|
193
|
+
})
|
|
194
|
+
.join(colors.gray(', '));
|
|
195
|
+
console.log(` ${httpLine}`);
|
|
196
|
+
if (httpErrors.length > 5) {
|
|
197
|
+
console.log(colors.gray(` ... +${httpErrors.length - 5} more HTTP errors`));
|
|
163
198
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
199
|
+
}
|
|
200
|
+
if (netErrors.length > 0) {
|
|
201
|
+
const netLine = netErrors
|
|
202
|
+
.slice(0, 5)
|
|
203
|
+
.map(e => `${e.count}x ${colors.red('ERR')} ${colors.gray(e.message)}`)
|
|
204
|
+
.join(colors.gray(', '));
|
|
205
|
+
console.log(` ${netLine}`);
|
|
206
|
+
if (netErrors.length > 5) {
|
|
207
|
+
console.log(colors.gray(` ... +${netErrors.length - 5} more network errors`));
|
|
167
208
|
}
|
|
168
209
|
}
|
|
169
210
|
}
|
|
@@ -36,7 +36,7 @@ export declare class ScrollBuffer extends EventEmitter {
|
|
|
36
36
|
percent: number;
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
export declare function parseScrollKey(data: Buffer): 'pageUp' | 'pageDown' | 'scrollUp' | 'scrollDown' | 'home' | 'end' | '
|
|
39
|
+
export declare function parseScrollKey(data: Buffer): 'pageUp' | 'pageDown' | 'scrollUp' | 'scrollDown' | 'home' | 'end' | 'quit' | null;
|
|
40
40
|
export declare function parseMouseScroll(data: Buffer): 'scrollUp' | 'scrollDown' | null;
|
|
41
41
|
export declare function enableMouseReporting(): void;
|
|
42
42
|
export declare function disableMouseReporting(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scroll-buffer.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/scroll-buffer.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,aAAa,CAA4C;IACjE,OAAO,CAAC,aAAa,CAAc;gBAEvB,OAAO,GAAE,mBAAwB;IAS7C,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAuB5B,KAAK,IAAI,IAAI;IAUb,IAAI,SAAS,IAAI,MAAM,CAEtB;IAKD,IAAI,QAAQ,IAAI,MAAM,CAErB;IAKD,IAAI,YAAY,IAAI,OAAO,CAE1B;IAKD,QAAQ,CAAC,KAAK,GAAE,MAAU,GAAG,OAAO;IAcpC,UAAU,CAAC,KAAK,GAAE,MAAU,GAAG,OAAO;IAatC,MAAM,IAAI,OAAO;IAOjB,QAAQ,IAAI,OAAO;IAOnB,WAAW,IAAI,IAAI;IAOnB,cAAc,IAAI,IAAI;IAOtB,eAAe,IAAI,MAAM,EAAE;IAS3B,MAAM,IAAI,MAAM;IAsBhB,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAOrC,eAAe,IAAI,IAAI;IAcvB,cAAc,IAAI,IAAI;IAWtB,IAAI,YAAY,IAAI,OAAO,CAE1B;IAKD,KAAK,IAAI,IAAI;IASb,aAAa,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;CAOrE;AAKD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,YAAY,GAAG,MAAM,GAAG,KAAK,GAAG,
|
|
1
|
+
{"version":3,"file":"scroll-buffer.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/scroll-buffer.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,aAAa,CAA4C;IACjE,OAAO,CAAC,aAAa,CAAc;gBAEvB,OAAO,GAAE,mBAAwB;IAS7C,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAuB5B,KAAK,IAAI,IAAI;IAUb,IAAI,SAAS,IAAI,MAAM,CAEtB;IAKD,IAAI,QAAQ,IAAI,MAAM,CAErB;IAKD,IAAI,YAAY,IAAI,OAAO,CAE1B;IAKD,QAAQ,CAAC,KAAK,GAAE,MAAU,GAAG,OAAO;IAcpC,UAAU,CAAC,KAAK,GAAE,MAAU,GAAG,OAAO;IAatC,MAAM,IAAI,OAAO;IAOjB,QAAQ,IAAI,OAAO;IAOnB,WAAW,IAAI,IAAI;IAOnB,cAAc,IAAI,IAAI;IAOtB,eAAe,IAAI,MAAM,EAAE;IAS3B,MAAM,IAAI,MAAM;IAsBhB,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAOrC,eAAe,IAAI,IAAI;IAcvB,cAAc,IAAI,IAAI;IAWtB,IAAI,YAAY,IAAI,OAAO,CAE1B;IAKD,KAAK,IAAI,IAAI;IASb,aAAa,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;CAOrE;AAKD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,YAAY,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,IAAI,CAyB/H;AAOD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,YAAY,GAAG,IAAI,CAqB/E;AAKD,wBAAgB,oBAAoB,IAAI,IAAI,CAK3C;AAKD,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C"}
|
|
@@ -131,8 +131,8 @@ export function parseScrollKey(data) {
|
|
|
131
131
|
return 'home';
|
|
132
132
|
if (str === '\x1b[F' || str === '\x1b[4~' || str === '\x1bOF')
|
|
133
133
|
return 'end';
|
|
134
|
-
if (str === '
|
|
135
|
-
return '
|
|
134
|
+
if (str === 'q' || str === 'Q')
|
|
135
|
+
return 'quit';
|
|
136
136
|
return null;
|
|
137
137
|
}
|
|
138
138
|
export function parseMouseScroll(data) {
|
package/dist/cli/tui/shell.d.ts
CHANGED
|
@@ -42,8 +42,11 @@ export declare class RekShell {
|
|
|
42
42
|
private executeRequest;
|
|
43
43
|
private runWhois;
|
|
44
44
|
private runTLS;
|
|
45
|
+
private runSecurityGrader;
|
|
46
|
+
private runIpIntelligence;
|
|
45
47
|
private runDNS;
|
|
46
48
|
private runDNSPropagation;
|
|
49
|
+
private runDnsEmailCheck;
|
|
47
50
|
private runRDAP;
|
|
48
51
|
private runPing;
|
|
49
52
|
private runScrap;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/shell.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/shell.ts"],"names":[],"mappings":"AAyCA,qBAAa,QAAQ;IACnB,OAAO,CAAC,EAAE,CAAsB;IAChC,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,SAAS,CAA2B;IAC5C,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,mBAAmB,CAA4C;IACvE,OAAO,CAAC,YAAY,CAAkB;;YAgBxB,iBAAiB;IAe/B,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,SAAS;IAcJ,KAAK;IA8ClB,OAAO,CAAC,kBAAkB;IA2B1B,OAAO,CAAC,oBAAoB;IAW5B,OAAO,CAAC,qBAAqB;IAqE7B,OAAO,CAAC,eAAe;IAmEvB,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,gBAAgB;IA8BxB,OAAO,CAAC,MAAM;YAKA,aAAa;YA6Mb,kBAAkB;YAkBlB,SAAS;YAkBT,WAAW;IA0DzB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,QAAQ;IAoCV,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM;IA6CnC,OAAO,CAAC,gBAAgB;IAqBxB,OAAO,CAAC,UAAU;YAeJ,cAAc;YAyEd,QAAQ;YA8GR,MAAM;YA4FN,iBAAiB;YAqDjB,iBAAiB;YAkDjB,MAAM;YA2EN,iBAAiB;YAuBjB,gBAAgB;YAwEhB,OAAO;YA+DP,OAAO;YA0CP,QAAQ;YAoER,SAAS;YAsCT,aAAa;YA8Bb,aAAa;YA+Bb,aAAa;YA6Bb,cAAc;YAkCd,eAAe;YA+Ef,gBAAgB;YAmEhB,YAAY;YAiEZ,mBAAmB;YAsFnB,QAAQ;YA0FR,YAAY;YAoCZ,YAAY;YA6CZ,WAAW;IA6CzB,OAAO,CAAC,UAAU;IA4GlB,OAAO,CAAC,WAAW;YAgFL,eAAe;YAkBf,cAAc;YAgDd,SAAS;YAgBT,UAAU;YAuBV,UAAU;IAwBxB,OAAO,CAAC,aAAa;IAuCrB,OAAO,CAAC,SAAS;CA8ElB"}
|
package/dist/cli/tui/shell.js
CHANGED
|
@@ -92,7 +92,7 @@ export class RekShell {
|
|
|
92
92
|
const commands = [
|
|
93
93
|
'get', 'post', 'put', 'delete', 'patch', 'head', 'options',
|
|
94
94
|
'ws', 'udp', 'load', 'chat', 'ai',
|
|
95
|
-
'whois', 'tls', 'ssl', 'dns', 'dns:propagate', 'rdap', 'ping',
|
|
95
|
+
'whois', 'tls', 'ssl', 'security', 'ip', 'dns', 'dns:propagate', 'dns:email', 'rdap', 'ping',
|
|
96
96
|
'scrap', '$', '$text', '$attr', '$html', '$links', '$images', '$scripts', '$css', '$sourcemaps', '$unmap', '$unmap:view', '$unmap:save', '$beautify', '$beautify:save', '$table',
|
|
97
97
|
'?', 'search', 'suggest', 'example',
|
|
98
98
|
'help', 'clear', 'exit', 'set', 'url', 'vars', 'env'
|
|
@@ -172,20 +172,21 @@ export class RekShell {
|
|
|
172
172
|
}
|
|
173
173
|
return true;
|
|
174
174
|
}
|
|
175
|
-
if (self.inScrollMode) {
|
|
176
|
-
if (str === '\x1b' || str === '\x1b\x1b') {
|
|
177
|
-
self.exitScrollMode();
|
|
178
|
-
}
|
|
179
|
-
return true;
|
|
180
|
-
}
|
|
181
175
|
const scrollKey = parseScrollKey(data);
|
|
182
176
|
if (scrollKey) {
|
|
183
|
-
if (scrollKey === '
|
|
177
|
+
if (scrollKey === 'quit') {
|
|
178
|
+
if (self.inScrollMode) {
|
|
179
|
+
self.exitScrollMode();
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
184
182
|
return originalEmit(event, ...args);
|
|
185
183
|
}
|
|
186
184
|
self.handleScrollKey(scrollKey);
|
|
187
185
|
return true;
|
|
188
186
|
}
|
|
187
|
+
if (self.inScrollMode) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
189
190
|
}
|
|
190
191
|
return originalEmit(event, ...args);
|
|
191
192
|
};
|
|
@@ -234,7 +235,7 @@ export class RekShell {
|
|
|
234
235
|
return;
|
|
235
236
|
}
|
|
236
237
|
break;
|
|
237
|
-
case '
|
|
238
|
+
case 'quit':
|
|
238
239
|
if (this.inScrollMode) {
|
|
239
240
|
this.exitScrollMode();
|
|
240
241
|
return;
|
|
@@ -286,7 +287,7 @@ export class RekShell {
|
|
|
286
287
|
const scrollInfo = this.scrollBuffer.isScrolledUp
|
|
287
288
|
? colors.yellow(`ā ${this.scrollBuffer.position} lines | ${info.percent}% | `)
|
|
288
289
|
: '';
|
|
289
|
-
const helpText = colors.gray('Page Up/Down ⢠Home/End ā¢
|
|
290
|
+
const helpText = colors.gray('Page Up/Down ⢠Home/End ⢠Q to exit');
|
|
290
291
|
const statusBar = `\x1b[${rows};1H\x1b[7m ${scrollInfo}${helpText} \x1b[0m`;
|
|
291
292
|
this.originalStdoutWrite(statusBar);
|
|
292
293
|
}
|
|
@@ -340,12 +341,21 @@ export class RekShell {
|
|
|
340
341
|
case 'ssl':
|
|
341
342
|
await this.runTLS(parts[1], parts[2] ? parseInt(parts[2]) : 443);
|
|
342
343
|
return;
|
|
344
|
+
case 'security':
|
|
345
|
+
await this.runSecurityGrader(parts[1]);
|
|
346
|
+
return;
|
|
347
|
+
case 'ip':
|
|
348
|
+
await this.runIpIntelligence(parts[1]);
|
|
349
|
+
return;
|
|
343
350
|
case 'dns':
|
|
344
351
|
await this.runDNS(parts[1]);
|
|
345
352
|
return;
|
|
346
353
|
case 'dns:propagate':
|
|
347
354
|
await this.runDNSPropagation(parts[1], parts[2]);
|
|
348
355
|
return;
|
|
356
|
+
case 'dns:email':
|
|
357
|
+
await this.runDnsEmailCheck(parts[1], parts[2]);
|
|
358
|
+
return;
|
|
349
359
|
case 'rdap':
|
|
350
360
|
await this.runRDAP(parts[1]);
|
|
351
361
|
return;
|
|
@@ -888,6 +898,96 @@ export class RekShell {
|
|
|
888
898
|
}
|
|
889
899
|
console.log('');
|
|
890
900
|
}
|
|
901
|
+
async runSecurityGrader(url) {
|
|
902
|
+
if (!url) {
|
|
903
|
+
url = this.baseUrl || '';
|
|
904
|
+
if (!url) {
|
|
905
|
+
console.log(colors.yellow('Usage: security <url>'));
|
|
906
|
+
console.log(colors.gray(' Examples: security google.com | security https://example.com'));
|
|
907
|
+
console.log(colors.gray(' Or set a base URL first: url https://example.com'));
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
else if (!url.startsWith('http')) {
|
|
912
|
+
url = `https://${url}`;
|
|
913
|
+
}
|
|
914
|
+
console.log(colors.gray(`Analyzing security headers for ${url}...`));
|
|
915
|
+
try {
|
|
916
|
+
const { analyzeSecurityHeaders } = await import('../../utils/security-grader.js');
|
|
917
|
+
const res = await this.client.get(url);
|
|
918
|
+
const report = analyzeSecurityHeaders(res.headers);
|
|
919
|
+
let gradeColor = colors.red;
|
|
920
|
+
if (report.grade.startsWith('A'))
|
|
921
|
+
gradeColor = colors.green;
|
|
922
|
+
else if (report.grade.startsWith('B'))
|
|
923
|
+
gradeColor = colors.blue;
|
|
924
|
+
else if (report.grade.startsWith('C'))
|
|
925
|
+
gradeColor = colors.yellow;
|
|
926
|
+
console.log(`
|
|
927
|
+
${colors.bold(colors.cyan('š”ļø Security Headers Report'))}
|
|
928
|
+
Grade: ${gradeColor(colors.bold(report.grade))} (${report.score}/100)
|
|
929
|
+
|
|
930
|
+
${colors.bold('Details:')}`);
|
|
931
|
+
report.details.forEach(item => {
|
|
932
|
+
const icon = item.status === 'pass' ? colors.green('ā') : item.status === 'warn' ? colors.yellow('ā ') : colors.red('ā');
|
|
933
|
+
const headerName = colors.bold(item.header);
|
|
934
|
+
const value = item.value ? colors.gray(`= ${item.value.length > 50 ? item.value.slice(0, 47) + '...' : item.value}`) : colors.gray('(missing)');
|
|
935
|
+
console.log(` ${icon} ${headerName} ${value}`);
|
|
936
|
+
if (item.status !== 'pass') {
|
|
937
|
+
console.log(` ${colors.red('ā')} ${item.message}`);
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
console.log('');
|
|
941
|
+
this.lastResponse = report;
|
|
942
|
+
}
|
|
943
|
+
catch (error) {
|
|
944
|
+
console.error(colors.red(`Analysis failed: ${error.message}`));
|
|
945
|
+
}
|
|
946
|
+
console.log('');
|
|
947
|
+
}
|
|
948
|
+
async runIpIntelligence(address) {
|
|
949
|
+
if (!address) {
|
|
950
|
+
console.log(colors.yellow('Usage: ip <address>'));
|
|
951
|
+
console.log(colors.gray(' Examples: ip 8.8.8.8 | ip 192.168.1.1'));
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
console.log(colors.gray(`Looking up ${address} using local GeoLite2 database...`));
|
|
955
|
+
try {
|
|
956
|
+
const { getIpInfo, isGeoIPAvailable } = await import('../../mcp/ip-intel.js');
|
|
957
|
+
if (!isGeoIPAvailable()) {
|
|
958
|
+
console.log(colors.gray(`Downloading GeoLite2 database...`));
|
|
959
|
+
}
|
|
960
|
+
const info = await getIpInfo(address);
|
|
961
|
+
if (info.bogon) {
|
|
962
|
+
console.log(colors.yellow(`\nā ${address} is a Bogon/Private IP.`));
|
|
963
|
+
console.log(colors.gray(` Type: ${info.bogonType}`));
|
|
964
|
+
this.lastResponse = info;
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
console.log(`
|
|
968
|
+
${colors.bold(colors.cyan('š IP Intelligence Report'))}
|
|
969
|
+
|
|
970
|
+
${colors.bold('Location:')}
|
|
971
|
+
${colors.gray('City:')} ${info.city || 'N/A'}
|
|
972
|
+
${colors.gray('Region:')} ${info.region || 'N/A'}
|
|
973
|
+
${colors.gray('Country:')} ${info.country || 'N/A'} ${info.countryCode ? `(${info.countryCode})` : ''}
|
|
974
|
+
${colors.gray('Continent:')} ${info.continent || 'N/A'}
|
|
975
|
+
${colors.gray('Timezone:')} ${info.timezone || 'N/A'}
|
|
976
|
+
${colors.gray('Coords:')} ${info.loc ? colors.cyan(info.loc) : 'N/A'}
|
|
977
|
+
${colors.gray('Accuracy:')} ${info.accuracy ? `~${info.accuracy} km` : 'N/A'}
|
|
978
|
+
|
|
979
|
+
${colors.bold('Network:')}
|
|
980
|
+
${colors.gray('IP:')} ${info.ip}
|
|
981
|
+
${colors.gray('Type:')} ${info.isIPv6 ? 'IPv6' : 'IPv4'}
|
|
982
|
+
${colors.gray('Postal:')} ${info.postal || 'N/A'}
|
|
983
|
+
`);
|
|
984
|
+
this.lastResponse = info;
|
|
985
|
+
}
|
|
986
|
+
catch (error) {
|
|
987
|
+
console.error(colors.red(`IP Lookup Failed: ${error.message}`));
|
|
988
|
+
}
|
|
989
|
+
console.log('');
|
|
990
|
+
}
|
|
891
991
|
async runDNS(domain) {
|
|
892
992
|
if (!domain) {
|
|
893
993
|
domain = this.getBaseDomain() || '';
|
|
@@ -969,6 +1069,69 @@ export class RekShell {
|
|
|
969
1069
|
console.error(colors.red(`Propagation check failed: ${error.message}`));
|
|
970
1070
|
}
|
|
971
1071
|
}
|
|
1072
|
+
async runDnsEmailCheck(domain, selector) {
|
|
1073
|
+
if (!domain) {
|
|
1074
|
+
domain = this.getBaseDomain() || '';
|
|
1075
|
+
if (!domain) {
|
|
1076
|
+
console.log(colors.yellow('Usage: dns:email <domain> [dkim-selector]'));
|
|
1077
|
+
console.log(colors.gray(' Examples: dns:email google.com | dns:email github.com google'));
|
|
1078
|
+
console.log(colors.gray(' Or set a base URL first: url https://example.com'));
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
console.log(colors.gray(`Checking email security for ${domain}...`));
|
|
1083
|
+
const startTime = performance.now();
|
|
1084
|
+
try {
|
|
1085
|
+
const { validateSpf, validateDmarc, checkDkim } = await import('../../utils/dns-toolkit.js');
|
|
1086
|
+
const [spf, dmarc, dkim] = await Promise.all([
|
|
1087
|
+
validateSpf(domain),
|
|
1088
|
+
validateDmarc(domain),
|
|
1089
|
+
checkDkim(domain, selector || 'default')
|
|
1090
|
+
]);
|
|
1091
|
+
const duration = Math.round(performance.now() - startTime);
|
|
1092
|
+
console.log(colors.green(`ā Email security check completed`) + colors.gray(` (${duration}ms)\n`));
|
|
1093
|
+
console.log(colors.bold('SPF:'));
|
|
1094
|
+
if (spf.valid) {
|
|
1095
|
+
console.log(` ${colors.green('ā')} ${spf.record || 'No record'}`);
|
|
1096
|
+
}
|
|
1097
|
+
else {
|
|
1098
|
+
console.log(` ${colors.red('ā')} ${spf.errors?.join(', ') || 'Invalid'}`);
|
|
1099
|
+
}
|
|
1100
|
+
if (spf.warnings?.length) {
|
|
1101
|
+
spf.warnings.forEach((w) => console.log(` ${colors.yellow('ā ')} ${w}`));
|
|
1102
|
+
}
|
|
1103
|
+
console.log(colors.bold('\nDMARC:'));
|
|
1104
|
+
if (dmarc.valid) {
|
|
1105
|
+
console.log(` ${colors.green('ā')} Policy: ${dmarc.policy || 'none'}`);
|
|
1106
|
+
if (dmarc.percentage !== undefined && dmarc.percentage < 100) {
|
|
1107
|
+
console.log(` ${colors.yellow('ā ')} Only ${dmarc.percentage}% of emails affected`);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
else {
|
|
1111
|
+
console.log(` ${colors.red('ā')} No DMARC record found`);
|
|
1112
|
+
}
|
|
1113
|
+
if (dmarc.warnings?.length) {
|
|
1114
|
+
dmarc.warnings.forEach((w) => console.log(` ${colors.yellow('ā ')} ${w}`));
|
|
1115
|
+
}
|
|
1116
|
+
console.log(colors.bold(`\nDKIM (${selector || 'default'}):`));
|
|
1117
|
+
if (dkim.found) {
|
|
1118
|
+
console.log(` ${colors.green('ā')} Record found`);
|
|
1119
|
+
if (dkim.publicKey) {
|
|
1120
|
+
const keyPreview = dkim.publicKey.substring(0, 40) + '...';
|
|
1121
|
+
console.log(` ${colors.gray('Key:')} ${keyPreview}`);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
else {
|
|
1125
|
+
console.log(` ${colors.yellow('ā ')} No DKIM record for selector "${selector || 'default'}"`);
|
|
1126
|
+
console.log(` ${colors.gray('Try: dns:email ' + domain + ' <selector>')}`);
|
|
1127
|
+
}
|
|
1128
|
+
console.log('');
|
|
1129
|
+
this.lastResponse = { spf, dmarc, dkim };
|
|
1130
|
+
}
|
|
1131
|
+
catch (error) {
|
|
1132
|
+
console.error(colors.red(`Email security check failed: ${error.message}`));
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
972
1135
|
async runRDAP(domain) {
|
|
973
1136
|
if (!domain) {
|
|
974
1137
|
domain = this.getRootDomain() || '';
|