recker 1.0.4 → 1.0.6

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 (115) hide show
  1. package/README.md +1 -1
  2. package/dist/ai/adaptive-timeout.d.ts +51 -0
  3. package/dist/ai/adaptive-timeout.d.ts.map +1 -0
  4. package/dist/ai/adaptive-timeout.js +208 -0
  5. package/dist/ai/client.d.ts +24 -0
  6. package/dist/ai/client.d.ts.map +1 -0
  7. package/dist/ai/client.js +289 -0
  8. package/dist/ai/index.d.ts +10 -0
  9. package/dist/ai/index.d.ts.map +1 -0
  10. package/dist/ai/index.js +6 -0
  11. package/dist/ai/providers/anthropic.d.ts +64 -0
  12. package/dist/ai/providers/anthropic.d.ts.map +1 -0
  13. package/dist/ai/providers/anthropic.js +367 -0
  14. package/dist/ai/providers/base.d.ts +49 -0
  15. package/dist/ai/providers/base.d.ts.map +1 -0
  16. package/dist/ai/providers/base.js +145 -0
  17. package/dist/ai/providers/index.d.ts +7 -0
  18. package/dist/ai/providers/index.d.ts.map +1 -0
  19. package/dist/ai/providers/index.js +3 -0
  20. package/dist/ai/providers/openai.d.ts +65 -0
  21. package/dist/ai/providers/openai.d.ts.map +1 -0
  22. package/dist/ai/providers/openai.js +298 -0
  23. package/dist/ai/rate-limiter.d.ts +44 -0
  24. package/dist/ai/rate-limiter.d.ts.map +1 -0
  25. package/dist/ai/rate-limiter.js +212 -0
  26. package/dist/bench/generator.d.ts +19 -0
  27. package/dist/bench/generator.d.ts.map +1 -0
  28. package/dist/bench/generator.js +86 -0
  29. package/dist/bench/stats.d.ts +35 -0
  30. package/dist/bench/stats.d.ts.map +1 -0
  31. package/dist/bench/stats.js +60 -0
  32. package/dist/cache/memory-storage.d.ts.map +1 -1
  33. package/dist/cache/memory-storage.js +0 -5
  34. package/dist/cli/handler.d.ts +11 -0
  35. package/dist/cli/handler.d.ts.map +1 -0
  36. package/dist/cli/handler.js +92 -0
  37. package/dist/cli/index.d.ts +3 -0
  38. package/dist/cli/index.d.ts.map +1 -0
  39. package/dist/cli/index.js +255 -0
  40. package/dist/cli/presets.d.ts +2 -0
  41. package/dist/cli/presets.d.ts.map +1 -0
  42. package/dist/cli/presets.js +67 -0
  43. package/dist/cli/tui/ai-chat.d.ts +3 -0
  44. package/dist/cli/tui/ai-chat.d.ts.map +1 -0
  45. package/dist/cli/tui/ai-chat.js +100 -0
  46. package/dist/cli/tui/load-dashboard.d.ts +3 -0
  47. package/dist/cli/tui/load-dashboard.d.ts.map +1 -0
  48. package/dist/cli/tui/load-dashboard.js +117 -0
  49. package/dist/cli/tui/shell.d.ts +27 -0
  50. package/dist/cli/tui/shell.d.ts.map +1 -0
  51. package/dist/cli/tui/shell.js +386 -0
  52. package/dist/cli/tui/websocket.d.ts +2 -0
  53. package/dist/cli/tui/websocket.d.ts.map +1 -0
  54. package/dist/cli/tui/websocket.js +87 -0
  55. package/dist/contract/index.d.ts +2 -2
  56. package/dist/contract/index.d.ts.map +1 -1
  57. package/dist/core/client.d.ts +1 -0
  58. package/dist/core/client.d.ts.map +1 -1
  59. package/dist/core/client.js +4 -2
  60. package/dist/core/request-promise.d.ts +1 -1
  61. package/dist/core/request-promise.d.ts.map +1 -1
  62. package/dist/mcp/contract.d.ts +1 -1
  63. package/dist/mcp/contract.d.ts.map +1 -1
  64. package/dist/plugins/cache.d.ts.map +1 -1
  65. package/dist/plugins/cache.js +0 -7
  66. package/dist/plugins/scrape.d.ts.map +1 -1
  67. package/dist/plugins/scrape.js +9 -14
  68. package/dist/protocols/ftp.d.ts +28 -5
  69. package/dist/protocols/ftp.d.ts.map +1 -1
  70. package/dist/protocols/ftp.js +549 -136
  71. package/dist/protocols/sftp.d.ts +4 -2
  72. package/dist/protocols/sftp.d.ts.map +1 -1
  73. package/dist/protocols/sftp.js +16 -2
  74. package/dist/protocols/telnet.d.ts +37 -5
  75. package/dist/protocols/telnet.d.ts.map +1 -1
  76. package/dist/protocols/telnet.js +434 -58
  77. package/dist/runner/request-runner.d.ts.map +1 -1
  78. package/dist/runner/request-runner.js +0 -1
  79. package/dist/scrape/document.d.ts +3 -2
  80. package/dist/scrape/document.d.ts.map +1 -1
  81. package/dist/scrape/document.js +15 -3
  82. package/dist/testing/index.d.ts +2 -0
  83. package/dist/testing/index.d.ts.map +1 -1
  84. package/dist/testing/index.js +1 -0
  85. package/dist/testing/mock-udp-server.d.ts +44 -0
  86. package/dist/testing/mock-udp-server.d.ts.map +1 -0
  87. package/dist/testing/mock-udp-server.js +188 -0
  88. package/dist/transport/base-udp.d.ts +36 -0
  89. package/dist/transport/base-udp.d.ts.map +1 -0
  90. package/dist/transport/base-udp.js +188 -0
  91. package/dist/transport/udp-response.d.ts +65 -0
  92. package/dist/transport/udp-response.d.ts.map +1 -0
  93. package/dist/transport/udp-response.js +269 -0
  94. package/dist/transport/udp.d.ts +22 -0
  95. package/dist/transport/udp.d.ts.map +1 -0
  96. package/dist/transport/udp.js +260 -0
  97. package/dist/types/ai.d.ts +268 -0
  98. package/dist/types/ai.d.ts.map +1 -0
  99. package/dist/types/ai.js +1 -0
  100. package/dist/types/udp.d.ts +138 -0
  101. package/dist/types/udp.d.ts.map +1 -0
  102. package/dist/types/udp.js +1 -0
  103. package/dist/udp/index.d.ts +6 -0
  104. package/dist/udp/index.d.ts.map +1 -0
  105. package/dist/udp/index.js +3 -0
  106. package/dist/utils/chart.d.ts +15 -0
  107. package/dist/utils/chart.d.ts.map +1 -0
  108. package/dist/utils/chart.js +94 -0
  109. package/dist/utils/colors.d.ts +27 -0
  110. package/dist/utils/colors.d.ts.map +1 -0
  111. package/dist/utils/colors.js +50 -0
  112. package/dist/utils/optional-require.d.ts +20 -0
  113. package/dist/utils/optional-require.d.ts.map +1 -0
  114. package/dist/utils/optional-require.js +105 -0
  115. package/package.json +53 -12
@@ -0,0 +1,100 @@
1
+ import readline from 'node:readline';
2
+ import pc from '../../utils/colors.js';
3
+ import { createAIClient } from '../../ai/client.js';
4
+ export async function startAIChat(rl, provider = 'openai', apiKey, model) {
5
+ console.clear();
6
+ console.log(pc.bold(pc.magenta(`🤖 Rek AI Chat (${provider})`)));
7
+ console.log(pc.gray('Type your message. Ctrl+C to exit.'));
8
+ const envKey = provider === 'openai' ? 'OPENAI_API_KEY' : 'ANTHROPIC_API_KEY';
9
+ const key = apiKey || process.env[envKey];
10
+ if (!key) {
11
+ console.log(pc.yellow(`
12
+ Warning: No API Key found for ${provider}.`));
13
+ console.log(`Please set it via environment variable ${pc.bold(envKey)} or passing it to the command.`);
14
+ console.log(`Example: set ${envKey}=sk-... inside the shell.`);
15
+ return;
16
+ }
17
+ const client = createAIClient({
18
+ defaultProvider: provider,
19
+ providers: {
20
+ [provider]: { apiKey: key }
21
+ },
22
+ observability: false
23
+ });
24
+ const history = [
25
+ { role: 'system', content: 'You are Recker AI, a helpful and concise assistant in a terminal environment.' }
26
+ ];
27
+ rl.setPrompt(pc.magenta('You › '));
28
+ rl.prompt();
29
+ return new Promise((resolve) => {
30
+ const onLine = async (line) => {
31
+ const input = line.trim();
32
+ if (!input) {
33
+ rl.prompt();
34
+ return;
35
+ }
36
+ if (input.toLowerCase() === '/clear') {
37
+ history.length = 1;
38
+ console.clear();
39
+ console.log(pc.gray('Context cleared.'));
40
+ rl.prompt();
41
+ return;
42
+ }
43
+ if (input.toLowerCase() === '/exit') {
44
+ cleanup();
45
+ resolve();
46
+ return;
47
+ }
48
+ history.push({ role: 'user', content: input });
49
+ rl.pause();
50
+ process.stdout.write(pc.cyan('AI › '));
51
+ let fullResponse = '';
52
+ try {
53
+ const stream = await client.stream({
54
+ provider: provider,
55
+ model: model,
56
+ messages: history
57
+ });
58
+ for await (const chunk of stream) {
59
+ const content = typeof chunk === 'string' ? chunk : chunk.content || chunk.delta?.content || '';
60
+ if (content) {
61
+ process.stdout.write(content);
62
+ fullResponse += content;
63
+ }
64
+ }
65
+ process.stdout.write('\n');
66
+ history.push({ role: 'assistant', content: fullResponse });
67
+ }
68
+ catch (error) {
69
+ console.log(pc.red(`
70
+ Error: ${error.message}`));
71
+ if (error.cause)
72
+ console.log(pc.gray(error.cause));
73
+ }
74
+ finally {
75
+ rl.resume();
76
+ rl.prompt();
77
+ }
78
+ };
79
+ const cleanup = () => {
80
+ rl.off('line', onLine);
81
+ rl.off('SIGINT', onSigInt);
82
+ process.stdin.off('keypress', onKeypress);
83
+ };
84
+ if (process.stdin.isTTY)
85
+ readline.emitKeypressEvents(process.stdin);
86
+ const onKeypress = (_str, key) => {
87
+ if (key && key.name === 'escape') {
88
+ cleanup();
89
+ resolve();
90
+ }
91
+ };
92
+ process.stdin.on('keypress', onKeypress);
93
+ rl.on('line', onLine);
94
+ const onSigInt = () => {
95
+ cleanup();
96
+ resolve();
97
+ };
98
+ rl.once('SIGINT', onSigInt);
99
+ });
100
+ }
@@ -0,0 +1,3 @@
1
+ import { LoadConfig } from '../../bench/generator.js';
2
+ export declare function startLoadDashboard(config: LoadConfig): Promise<void>;
3
+ //# sourceMappingURL=load-dashboard.d.ts.map
@@ -0,0 +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;AAKrE,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,UAAU,iBA4E1D"}
@@ -0,0 +1,117 @@
1
+ import pc from '../../utils/colors.js';
2
+ import { plot } from '../../utils/chart.js';
3
+ import readline from 'node:readline';
4
+ import { LoadGenerator } from '../../bench/generator.js';
5
+ const ALTERNATE_SCREEN_ENTER = '\x1b[?1049h';
6
+ const ALTERNATE_SCREEN_EXIT = '\x1b[?1049l';
7
+ export async function startLoadDashboard(config) {
8
+ process.stdout.write(ALTERNATE_SCREEN_ENTER);
9
+ readline.emitKeypressEvents(process.stdin);
10
+ if (process.stdin.isTTY) {
11
+ process.stdin.setRawMode(true);
12
+ process.stdin.resume();
13
+ }
14
+ const generator = new LoadGenerator(config);
15
+ let abortReject;
16
+ const abortPromise = new Promise((_, reject) => {
17
+ abortReject = reject;
18
+ });
19
+ const onKeypress = (_str, key) => {
20
+ if (key && (key.name === 'escape' || (key.ctrl && key.name === 'c'))) {
21
+ generator.stop();
22
+ if (abortReject)
23
+ abortReject(new Error('User aborted'));
24
+ }
25
+ };
26
+ process.stdin.on('keypress', onKeypress);
27
+ const rpsHistory = new Array(60).fill(0);
28
+ const latencyHistory = new Array(60).fill(0);
29
+ const usersHistory = new Array(60).fill(0);
30
+ const runPromise = generator.start();
31
+ const startTime = Date.now();
32
+ const interval = setInterval(() => {
33
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
34
+ const remaining = Math.max(0, config.duration - elapsed);
35
+ if (remaining <= 0) {
36
+ clearInterval(interval);
37
+ return;
38
+ }
39
+ const snapshot = generator.stats.getSnapshot();
40
+ rpsHistory.shift();
41
+ rpsHistory.push(snapshot.rps);
42
+ latencyHistory.shift();
43
+ latencyHistory.push(snapshot.p95);
44
+ usersHistory.shift();
45
+ usersHistory.push(snapshot.activeUsers);
46
+ render(config, elapsed, remaining, snapshot, rpsHistory, latencyHistory, usersHistory, generator.stats);
47
+ }, 1000);
48
+ try {
49
+ await Promise.race([runPromise, abortPromise]);
50
+ }
51
+ catch (e) {
52
+ if (e.message !== 'User aborted')
53
+ throw e;
54
+ }
55
+ finally {
56
+ clearInterval(interval);
57
+ process.stdin.off('keypress', onKeypress);
58
+ if (process.stdin.isTTY)
59
+ process.stdin.setRawMode(false);
60
+ process.stdout.write(ALTERNATE_SCREEN_EXIT);
61
+ }
62
+ renderFinalReport(generator.stats, config);
63
+ }
64
+ function render(config, elapsed, remaining, snapshot, rpsHistory, latencyHistory, usersHistory, stats) {
65
+ readline.cursorTo(process.stdout, 0, 0);
66
+ readline.clearScreenDown(process.stdout);
67
+ console.log(pc.bold(pc.cyan('🔥 Rek Load Generator')));
68
+ console.log(pc.gray(`Target: ${config.url}`));
69
+ console.log(pc.gray(`Mode: ${config.mode.toUpperCase()} ${config.http2 ? '(HTTP/2)' : ''}`));
70
+ console.log(pc.gray('Press ESC to stop'));
71
+ console.log('');
72
+ console.log(`${pc.white('Time:')} ${pc.green(elapsed + 's')} ` +
73
+ `${pc.gray('/')} ${config.duration}s ` +
74
+ `${pc.gray('(')}${pc.yellow(remaining + 's left')}${pc.gray(')')} ` +
75
+ `${pc.white('Reqs:')} ${pc.bold(stats.totalRequests)}`);
76
+ console.log(`${pc.blue('Users:')} ${pc.bold(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(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 })));
83
+ console.log('');
84
+ console.log(pc.bold(pc.green('⚡ Requests per Second')));
85
+ console.log(pc.green(plot(rpsHistory, { height: 6 })));
86
+ console.log('');
87
+ console.log(pc.bold(pc.magenta('⏱️ Latency P95 (ms)')));
88
+ console.log(pc.magenta(plot(latencyHistory, { height: 4 })));
89
+ }
90
+ function renderFinalReport(stats, config) {
91
+ const summary = stats.getSummary();
92
+ console.log(pc.bold(pc.green('\n✅ Load Test Complete')));
93
+ console.log(pc.bold('Configuration:'));
94
+ console.log(` URL: ${config.url}`);
95
+ console.log(` Mode: ${config.mode}`);
96
+ console.log(` Users: ${config.users}`);
97
+ console.log(` Duration: ${config.duration}s`);
98
+ console.log('\n' + pc.bold('Traffic:'));
99
+ console.log(` Total Requests: ${summary.total}`);
100
+ console.log(` Successful: ${pc.green(summary.success)}`);
101
+ console.log(` Failed: ${summary.failed > 0 ? pc.red(summary.failed) : pc.gray(0)}`);
102
+ console.log(` Total Bytes: ${(summary.bytes / 1024 / 1024).toFixed(2)} MB`);
103
+ console.log('\n' + pc.bold('Latency (ms):'));
104
+ console.log(` Avg: ${summary.latency.avg.toFixed(2)}`);
105
+ console.log(` P50: ${summary.latency.p50.toFixed(0)}`);
106
+ console.log(` P95: ${summary.latency.p95.toFixed(0)}`);
107
+ console.log(` P99: ${summary.latency.p99.toFixed(0)}`);
108
+ console.log(` Max: ${summary.latency.max.toFixed(0)}`);
109
+ if (Object.keys(summary.codes).length > 0) {
110
+ console.log('\n' + pc.bold('Status Codes:'));
111
+ Object.entries(summary.codes).forEach(([code, count]) => {
112
+ const color = code.startsWith('2') ? pc.green : code.startsWith('5') ? pc.red : pc.yellow;
113
+ console.log(` ${color(code)}: ${count}`);
114
+ });
115
+ }
116
+ console.log('');
117
+ }
@@ -0,0 +1,27 @@
1
+ export declare class RekShell {
2
+ private rl;
3
+ private client;
4
+ private history;
5
+ private baseUrl;
6
+ private lastResponse;
7
+ private variables;
8
+ private initialized;
9
+ constructor();
10
+ private ensureInitialized;
11
+ private getPrompt;
12
+ private completer;
13
+ start(): Promise<void>;
14
+ private prompt;
15
+ private handleCommand;
16
+ private runInteractiveMode;
17
+ private runAIChat;
18
+ private runLoadTest;
19
+ private parseLine;
20
+ private setBaseUrl;
21
+ private setVariable;
22
+ private resolveVariables;
23
+ private resolveUrl;
24
+ private executeRequest;
25
+ private printHelp;
26
+ }
27
+ //# sourceMappingURL=shell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/shell.ts"],"names":[],"mappings":"AAsBA,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,WAAW,CAAS;;YAWd,iBAAiB;IAe/B,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,SAAS;IAMJ,KAAK;IA8BlB,OAAO,CAAC,MAAM;YAKA,aAAa;YAgGb,kBAAkB;YAkBlB,SAAS;YAkBT,WAAW;IA0DzB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,UAAU;YAeJ,cAAc;IAyE5B,OAAO,CAAC,SAAS;CAwClB"}
@@ -0,0 +1,386 @@
1
+ import readline from 'node:readline';
2
+ import { requireOptional } from '../../utils/optional-require.js';
3
+ import { createClient } from '../../core/client.js';
4
+ import { startInteractiveWebSocket } from './websocket.js';
5
+ import pc from '../../utils/colors.js';
6
+ let highlight;
7
+ async function initDependencies() {
8
+ if (!highlight) {
9
+ const cardinal = await requireOptional('cardinal', 'recker/cli');
10
+ highlight = cardinal.highlight;
11
+ }
12
+ }
13
+ export class RekShell {
14
+ rl;
15
+ client;
16
+ history = [];
17
+ baseUrl = '';
18
+ lastResponse = null;
19
+ variables = {};
20
+ initialized = false;
21
+ constructor() {
22
+ this.client = createClient({
23
+ baseUrl: 'http://localhost',
24
+ checkHooks: false
25
+ });
26
+ }
27
+ async ensureInitialized() {
28
+ if (this.initialized)
29
+ return;
30
+ await initDependencies();
31
+ this.rl = readline.createInterface({
32
+ input: process.stdin,
33
+ output: process.stdout,
34
+ completer: this.completer.bind(this),
35
+ prompt: ''
36
+ });
37
+ this.initialized = true;
38
+ }
39
+ getPrompt() {
40
+ const base = this.baseUrl ? pc.cyan(new URL(this.baseUrl).hostname) : pc.gray('rek');
41
+ return `${base} ${pc.magenta('›')} `;
42
+ }
43
+ completer(line) {
44
+ const commands = ['get', 'post', 'put', 'delete', 'patch', 'head', 'options', 'ws', 'udp', 'load', 'chat', 'ai', 'help', 'clear', 'exit', 'set', 'url'];
45
+ const hits = commands.filter((c) => c.startsWith(line));
46
+ return [hits.length ? hits : commands, line];
47
+ }
48
+ async start() {
49
+ await this.ensureInitialized();
50
+ console.clear();
51
+ console.log(pc.bold(pc.cyan('Rek Console')));
52
+ console.log(pc.gray('Chat with your APIs. Type "help" for magic.'));
53
+ console.log(pc.gray('--------------------------------------------\n'));
54
+ this.prompt();
55
+ this.rl.on('line', async (line) => {
56
+ const input = line.trim();
57
+ if (input) {
58
+ await this.handleCommand(input);
59
+ }
60
+ this.prompt();
61
+ });
62
+ this.rl.on('SIGINT', () => {
63
+ readline.clearLine(process.stdout, 0);
64
+ this.prompt();
65
+ });
66
+ this.rl.on('close', () => {
67
+ console.log(pc.gray('\nSee ya.'));
68
+ process.exit(0);
69
+ });
70
+ }
71
+ prompt() {
72
+ this.rl.setPrompt(this.getPrompt());
73
+ this.rl.prompt();
74
+ }
75
+ async handleCommand(input) {
76
+ if (input.includes('=') && !input.includes(' ') && !input.startsWith('http')) {
77
+ }
78
+ const parts = this.parseLine(input);
79
+ const cmd = parts[0].toLowerCase();
80
+ switch (cmd) {
81
+ case 'help':
82
+ this.printHelp();
83
+ return;
84
+ case 'clear':
85
+ console.clear();
86
+ return;
87
+ case 'exit':
88
+ case 'quit':
89
+ this.rl.close();
90
+ return;
91
+ case 'url':
92
+ this.setBaseUrl(parts[1]);
93
+ return;
94
+ case 'set':
95
+ this.setVariable(parts.slice(1));
96
+ return;
97
+ case 'vars':
98
+ console.log(this.variables);
99
+ return;
100
+ case 'load':
101
+ await this.runLoadTest(parts.slice(1));
102
+ return;
103
+ case 'ai':
104
+ case 'chat':
105
+ await this.runAIChat(parts.slice(1));
106
+ return;
107
+ }
108
+ const methods = ['get', 'post', 'put', 'delete', 'patch', 'head', 'options'];
109
+ let method = 'GET';
110
+ let url = '';
111
+ let bodyParts = [];
112
+ let headers = {};
113
+ if (methods.includes(cmd)) {
114
+ method = cmd.toUpperCase();
115
+ url = parts[1];
116
+ bodyParts = parts.slice(2);
117
+ }
118
+ else if (cmd.startsWith('http') || cmd.startsWith('/') || cmd.includes('.')) {
119
+ method = 'GET';
120
+ url = cmd;
121
+ bodyParts = parts.slice(1);
122
+ }
123
+ else {
124
+ console.log(pc.red(`Unknown command: ${cmd}`));
125
+ return;
126
+ }
127
+ url = this.resolveUrl(url);
128
+ if (!url) {
129
+ console.log(pc.yellow('No URL provided and no Base URL set. Use "url <url>" or provide full URL.'));
130
+ return;
131
+ }
132
+ const body = {};
133
+ for (const part of bodyParts) {
134
+ if (part.includes(':')) {
135
+ const [k, v] = part.split(':');
136
+ headers[k] = this.resolveVariables(v);
137
+ }
138
+ else if (part.includes('=')) {
139
+ const isTyped = part.includes(':=');
140
+ const sep = isTyped ? ':=' : '=';
141
+ const [k, v] = part.split(sep);
142
+ let val = this.resolveVariables(v);
143
+ if (isTyped) {
144
+ if (val === 'true')
145
+ val = true;
146
+ else if (val === 'false')
147
+ val = false;
148
+ else if (!isNaN(Number(val)))
149
+ val = Number(val);
150
+ }
151
+ if (method === 'GET')
152
+ method = 'POST';
153
+ body[k] = val;
154
+ }
155
+ }
156
+ await this.executeRequest(method, url, headers, body);
157
+ }
158
+ async runInteractiveMode(runner) {
159
+ const shellListeners = this.rl.listeners('line');
160
+ this.rl.removeAllListeners('line');
161
+ try {
162
+ await runner(this.rl);
163
+ }
164
+ finally {
165
+ this.rl.removeAllListeners('line');
166
+ shellListeners.forEach(listener => this.rl.on('line', listener));
167
+ this.prompt();
168
+ }
169
+ }
170
+ async runAIChat(args) {
171
+ const provider = args[0] || 'openai';
172
+ const model = args[1];
173
+ const envKeyName = provider === 'openai' ? 'OPENAI_API_KEY' : 'ANTHROPIC_API_KEY';
174
+ const apiKey = this.variables[envKeyName] || process.env[envKeyName];
175
+ const { startAIChat } = await import('./ai-chat.js');
176
+ await this.runInteractiveMode(async (rl) => {
177
+ await startAIChat(rl, provider, apiKey, model);
178
+ });
179
+ }
180
+ async runLoadTest(args) {
181
+ let targetUrl = '';
182
+ let users = 50;
183
+ let duration = 300;
184
+ let mode = 'throughput';
185
+ let http2 = false;
186
+ let rampUp = 5;
187
+ for (const arg of args) {
188
+ if (arg.includes('=')) {
189
+ const [key, val] = arg.split('=');
190
+ const k = key.toLowerCase();
191
+ if (k === 'users' || k === 'u')
192
+ users = parseInt(val);
193
+ else if (k === 'duration' || k === 'd' || k === 'time')
194
+ duration = parseInt(val);
195
+ else if (k === 'mode' || k === 'm')
196
+ mode = val;
197
+ else if (k === 'http2')
198
+ http2 = val === 'true';
199
+ else if (k === 'ramp' || k === 'rampup')
200
+ rampUp = parseInt(val);
201
+ }
202
+ else if (arg.toLowerCase() === 'http2') {
203
+ http2 = true;
204
+ }
205
+ else if (!targetUrl) {
206
+ targetUrl = arg;
207
+ }
208
+ }
209
+ targetUrl = this.resolveUrl(targetUrl);
210
+ if (!targetUrl) {
211
+ console.log(pc.yellow('Target URL required. usage: load <url> users=10 duration=10s ramp=5'));
212
+ return;
213
+ }
214
+ const { startLoadDashboard } = await import('./load-dashboard.js');
215
+ this.rl.pause();
216
+ process.stdout.write('\x1B[?25l');
217
+ try {
218
+ await startLoadDashboard({
219
+ url: targetUrl,
220
+ users,
221
+ duration,
222
+ mode,
223
+ http2,
224
+ rampUp
225
+ });
226
+ }
227
+ catch (e) {
228
+ console.error(pc.red('Load Test Failed: ' + e.message));
229
+ }
230
+ finally {
231
+ process.stdout.write('\x1B[?25h');
232
+ this.rl.resume();
233
+ this.prompt();
234
+ }
235
+ }
236
+ parseLine(input) {
237
+ return input.match(/(?:[^\s"]+|"[^"]*")+/g)?.map(s => s.replace(/"/g, '')) || [];
238
+ }
239
+ setBaseUrl(url) {
240
+ if (!url.startsWith('http'))
241
+ url = `https://${url}`;
242
+ this.baseUrl = url;
243
+ console.log(pc.gray(`Base URL set to: ${pc.cyan(this.baseUrl)}`));
244
+ }
245
+ setVariable(args) {
246
+ const [expr] = args;
247
+ if (!expr || !expr.includes('='))
248
+ return;
249
+ const [key, val] = expr.split('=');
250
+ this.variables[key] = val;
251
+ console.log(pc.gray(`Variable $${key} set.`));
252
+ }
253
+ resolveVariables(value) {
254
+ if (value.startsWith('$')) {
255
+ const key = value.slice(1);
256
+ if (key.startsWith('response.') || key.startsWith('res.')) {
257
+ const path = key.split('.').slice(1);
258
+ let current = this.lastResponse;
259
+ for (const p of path) {
260
+ if (current && typeof current === 'object')
261
+ current = current[p];
262
+ else
263
+ return '';
264
+ }
265
+ return String(current);
266
+ }
267
+ return this.variables[key] || value;
268
+ }
269
+ return value;
270
+ }
271
+ resolveUrl(inputUrl) {
272
+ if (!inputUrl)
273
+ return this.baseUrl;
274
+ if (inputUrl.startsWith('http') || inputUrl.startsWith('ws') || inputUrl.startsWith('udp'))
275
+ return inputUrl;
276
+ if (this.baseUrl) {
277
+ const cleanBase = this.baseUrl.endsWith('/') ? this.baseUrl.slice(0, -1) : this.baseUrl;
278
+ const cleanPath = inputUrl.startsWith('/') ? inputUrl : `/${inputUrl}`;
279
+ return `${cleanBase}${cleanPath}`;
280
+ }
281
+ return `https://${inputUrl}`;
282
+ }
283
+ async executeRequest(method, url, headers, body) {
284
+ const startTime = performance.now();
285
+ if (url.startsWith('ws')) {
286
+ this.rl.pause();
287
+ try {
288
+ await startInteractiveWebSocket(url, headers);
289
+ }
290
+ finally {
291
+ this.rl.resume();
292
+ this.prompt();
293
+ }
294
+ return;
295
+ }
296
+ if (url.startsWith('udp')) {
297
+ const { UDPTransport } = await import('../../transport/udp.js');
298
+ const transport = new UDPTransport(url);
299
+ const msg = Object.keys(body).length ? JSON.stringify(body) : 'ping';
300
+ console.log(pc.gray(`UDP packet -> ${url}`));
301
+ const res = await transport.dispatch({
302
+ url, method: 'GET', headers: new Headers(),
303
+ body: msg, withHeader: () => ({}), withBody: () => ({})
304
+ });
305
+ const text = await res.text();
306
+ console.log(pc.green('✔ Sent/Received'));
307
+ if (text)
308
+ console.log(text);
309
+ return;
310
+ }
311
+ console.log(pc.gray(`${method} ${url}...`));
312
+ try {
313
+ const hasBody = Object.keys(body).length > 0;
314
+ const res = await this.client.request(url, {
315
+ method: method,
316
+ headers,
317
+ json: hasBody ? body : undefined
318
+ });
319
+ const duration = Math.round(performance.now() - startTime);
320
+ const statusColor = res.ok ? pc.green : pc.red;
321
+ console.log(`${statusColor(pc.bold(res.status))} ${statusColor(res.statusText)} ` +
322
+ `${pc.gray(`(${duration}ms)`)}`);
323
+ const text = await res.text();
324
+ const isJson = res.headers.get('content-type')?.includes('json');
325
+ if (isJson) {
326
+ try {
327
+ const data = JSON.parse(text);
328
+ console.log(highlight(JSON.stringify(data, null, 2)));
329
+ this.lastResponse = data;
330
+ }
331
+ catch {
332
+ console.log(text);
333
+ this.lastResponse = text;
334
+ }
335
+ }
336
+ else {
337
+ console.log(text.slice(0, 500) + (text.length > 500 ? '...' : ''));
338
+ this.lastResponse = text;
339
+ }
340
+ }
341
+ catch (error) {
342
+ console.error(pc.red(`Error: ${error.message}`));
343
+ }
344
+ console.log('');
345
+ }
346
+ printHelp() {
347
+ console.log(`
348
+ ${pc.bold(pc.cyan('Rek Console Help'))}
349
+
350
+ ${pc.bold('Core Commands:')}
351
+ ${pc.green('url <url>')} Set persistent Base URL.
352
+ ${pc.green('set <key>=<val>')} Set a session variable.
353
+ ${pc.green('vars')} List all session variables.
354
+ ${pc.green('clear')} Clear the screen.
355
+ ${pc.green('exit')} Exit the console.
356
+
357
+ ${pc.bold('HTTP Requests:')}
358
+ ${pc.green('<method> <path>')} Execute HTTP request (GET, POST, PUT, DELETE, etc).
359
+ ${pc.gray('Params:')} ${pc.white('key=value')} (string) or ${pc.white('key:=value')} (typed).
360
+ ${pc.gray('Headers:')} ${pc.white('Key:Value')}
361
+
362
+ ${pc.bold('Advanced Tools:')}
363
+ ${pc.green('load <url>')} Run Load Test.
364
+ ${pc.gray('Options:')}
365
+ ${pc.white('users=50')} ${pc.gray('Concurrent users')}
366
+ ${pc.white('duration=300')} ${pc.gray('Duration in seconds')}
367
+ ${pc.white('ramp=5')} ${pc.gray('Ramp-up time in seconds')}
368
+ ${pc.white('mode=throughput')}${pc.gray('throughput | stress | realistic')}
369
+ ${pc.white('http2=false')} ${pc.gray('Force HTTP/2')}
370
+
371
+ ${pc.green('chat <provider>')} Start AI Chat.
372
+ ${pc.gray('Providers:')} ${pc.white('openai')}, ${pc.white('anthropic')}
373
+ ${pc.gray('Arg:')} ${pc.white('model=...')} (optional)
374
+
375
+ ${pc.green('ws <url>')} Start interactive WebSocket session.
376
+ ${pc.green('udp <url>')} Send UDP packet.
377
+
378
+ ${pc.bold('Examples:')}
379
+ › url httpbin.org
380
+ › get /json
381
+ › post /post name="Neo" active:=true role:Admin
382
+ › load /heavy-endpoint users=100 mode=stress
383
+ › chat openai gpt-5.1
384
+ `);
385
+ }
386
+ }
@@ -0,0 +1,2 @@
1
+ export declare function startInteractiveWebSocket(url: string, headers: Record<string, string>): Promise<void>;
2
+ //# sourceMappingURL=websocket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/websocket.ts"],"names":[],"mappings":"AAKA,wBAAsB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,iBAuG3F"}