recker 1.0.15 → 1.0.17-next.9570ed5
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 +86 -97
- 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 +8 -3
- package/dist/bench/stats.d.ts +15 -1
- package/dist/bench/stats.d.ts.map +1 -1
- package/dist/bench/stats.js +117 -5
- 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 +602 -48
- 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 +127 -32
- package/dist/cli/tui/scroll-buffer.d.ts +43 -0
- package/dist/cli/tui/scroll-buffer.d.ts.map +1 -0
- package/dist/cli/tui/scroll-buffer.js +162 -0
- package/dist/cli/tui/search-panel.d.ts +41 -0
- package/dist/cli/tui/search-panel.d.ts.map +1 -0
- package/dist/cli/tui/search-panel.js +420 -0
- package/dist/cli/tui/shell.d.ts +14 -0
- package/dist/cli/tui/shell.d.ts.map +1 -1
- package/dist/cli/tui/shell.js +424 -46
- package/dist/cli/tui/websocket.js +17 -17
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +3 -2
- 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/index.d.ts +1 -0
- package/dist/dns/index.d.ts.map +1 -1
- package/dist/dns/index.js +1 -0
- package/dist/dns/propagation.d.ts +21 -0
- package/dist/dns/propagation.d.ts.map +1 -0
- package/dist/dns/propagation.js +169 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +10 -11
- package/dist/mcp/embeddings-loader.d.ts +18 -0
- package/dist/mcp/embeddings-loader.d.ts.map +1 -0
- package/dist/mcp/embeddings-loader.js +162 -0
- 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/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -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 +59 -38
- package/dist/mcp/search/math.d.ts.map +1 -1
- package/dist/mcp/search/math.js +5 -1
- package/dist/mcp/server.d.ts +6 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +122 -2
- 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/presets/index.d.ts +2 -0
- package/dist/presets/index.d.ts.map +1 -1
- package/dist/presets/index.js +2 -0
- package/dist/presets/mailgun.d.ts +8 -0
- package/dist/presets/mailgun.d.ts.map +1 -0
- package/dist/presets/mailgun.js +20 -0
- package/dist/presets/registry.d.ts.map +1 -1
- package/dist/presets/registry.js +20 -0
- package/dist/presets/sinch.d.ts +10 -0
- package/dist/presets/sinch.d.ts.map +1 -0
- package/dist/presets/sinch.js +39 -0
- 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/recker.d.ts +47 -0
- package/dist/recker.d.ts.map +1 -0
- package/dist/recker.js +99 -0
- 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/colors.d.ts +16 -0
- package/dist/utils/colors.d.ts.map +1 -1
- package/dist/utils/colors.js +16 -0
- 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/utils/tls-inspector.d.ts +6 -0
- package/dist/utils/tls-inspector.d.ts.map +1 -1
- package/dist/utils/tls-inspector.js +35 -1
- 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 +4 -3
- package/dist/mcp/data/embeddings.json +0 -1
package/dist/cli/tui/shell.js
CHANGED
|
@@ -12,6 +12,8 @@ import { rdap } from '../../utils/rdap.js';
|
|
|
12
12
|
import { ScrapeDocument } from '../../scrape/document.js';
|
|
13
13
|
import colors from '../../utils/colors.js';
|
|
14
14
|
import { getShellSearch } from './shell-search.js';
|
|
15
|
+
import { openSearchPanel } from './search-panel.js';
|
|
16
|
+
import { ScrollBuffer, parseScrollKey, parseMouseScroll, enableMouseReporting, disableMouseReporting } from './scroll-buffer.js';
|
|
15
17
|
let highlight;
|
|
16
18
|
async function initDependencies() {
|
|
17
19
|
if (!highlight) {
|
|
@@ -36,11 +38,16 @@ export class RekShell {
|
|
|
36
38
|
initialized = false;
|
|
37
39
|
currentDoc = null;
|
|
38
40
|
currentDocUrl = '';
|
|
41
|
+
scrollBuffer;
|
|
42
|
+
originalStdoutWrite = null;
|
|
43
|
+
inScrollMode = false;
|
|
39
44
|
constructor() {
|
|
40
45
|
this.client = createClient({
|
|
41
46
|
baseUrl: 'http://localhost',
|
|
42
|
-
checkHooks: false
|
|
47
|
+
checkHooks: false,
|
|
48
|
+
http2: true
|
|
43
49
|
});
|
|
50
|
+
this.scrollBuffer = new ScrollBuffer({ maxLines: 10000 });
|
|
44
51
|
}
|
|
45
52
|
async ensureInitialized() {
|
|
46
53
|
if (this.initialized)
|
|
@@ -85,7 +92,7 @@ export class RekShell {
|
|
|
85
92
|
const commands = [
|
|
86
93
|
'get', 'post', 'put', 'delete', 'patch', 'head', 'options',
|
|
87
94
|
'ws', 'udp', 'load', 'chat', 'ai',
|
|
88
|
-
'whois', 'tls', 'ssl', 'dns', 'rdap', 'ping',
|
|
95
|
+
'whois', 'tls', 'ssl', 'security', 'ip', 'dns', 'dns:propagate', 'dns:email', 'rdap', 'ping',
|
|
89
96
|
'scrap', '$', '$text', '$attr', '$html', '$links', '$images', '$scripts', '$css', '$sourcemaps', '$unmap', '$unmap:view', '$unmap:save', '$beautify', '$beautify:save', '$table',
|
|
90
97
|
'?', 'search', 'suggest', 'example',
|
|
91
98
|
'help', 'clear', 'exit', 'set', 'url', 'vars', 'env'
|
|
@@ -95,9 +102,11 @@ export class RekShell {
|
|
|
95
102
|
}
|
|
96
103
|
async start() {
|
|
97
104
|
await this.ensureInitialized();
|
|
105
|
+
this.setupScrollCapture();
|
|
98
106
|
console.clear();
|
|
99
107
|
console.log(colors.bold(colors.cyan('Rek Console')));
|
|
100
108
|
console.log(colors.gray('Chat with your APIs. Type "help" for magic.'));
|
|
109
|
+
console.log(colors.gray('Page Up/Down or mouse scroll to view history.'));
|
|
101
110
|
console.log(colors.gray('--------------------------------------------\n'));
|
|
102
111
|
this.prompt();
|
|
103
112
|
this.rl.on('line', async (line) => {
|
|
@@ -112,15 +121,185 @@ export class RekShell {
|
|
|
112
121
|
this.rl.close();
|
|
113
122
|
});
|
|
114
123
|
this.rl.on('close', () => {
|
|
124
|
+
this.cleanupScrollCapture();
|
|
115
125
|
console.log(colors.gray('\nSee ya.'));
|
|
116
126
|
process.exit(0);
|
|
117
127
|
});
|
|
128
|
+
process.stdout.on('resize', () => {
|
|
129
|
+
this.scrollBuffer.updateViewport();
|
|
130
|
+
});
|
|
131
|
+
this.setupScrollKeyHandler();
|
|
132
|
+
}
|
|
133
|
+
setupScrollCapture() {
|
|
134
|
+
this.originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
135
|
+
const self = this;
|
|
136
|
+
process.stdout.write = function (chunk, encodingOrCallback, callback) {
|
|
137
|
+
const content = typeof chunk === 'string' ? chunk : chunk.toString();
|
|
138
|
+
self.scrollBuffer.write(content);
|
|
139
|
+
if (!self.inScrollMode && self.originalStdoutWrite) {
|
|
140
|
+
return self.originalStdoutWrite(chunk, encodingOrCallback, callback);
|
|
141
|
+
}
|
|
142
|
+
return true;
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
cleanupScrollCapture() {
|
|
146
|
+
if (this.originalStdoutWrite) {
|
|
147
|
+
process.stdout.write = this.originalStdoutWrite;
|
|
148
|
+
this.originalStdoutWrite = null;
|
|
149
|
+
}
|
|
150
|
+
disableMouseReporting();
|
|
151
|
+
}
|
|
152
|
+
setupScrollKeyHandler() {
|
|
153
|
+
enableMouseReporting();
|
|
154
|
+
if (process.stdin.isTTY) {
|
|
155
|
+
const originalEmit = process.stdin.emit.bind(process.stdin);
|
|
156
|
+
const self = this;
|
|
157
|
+
process.stdin.emit = function (event, ...args) {
|
|
158
|
+
if (event === 'data') {
|
|
159
|
+
const data = args[0];
|
|
160
|
+
const str = data.toString();
|
|
161
|
+
if (str.includes('\x1b[<')) {
|
|
162
|
+
const mouseScroll = parseMouseScroll(data);
|
|
163
|
+
if (mouseScroll) {
|
|
164
|
+
self.handleScrollKey(mouseScroll);
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
if (data.length >= 6 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x4d) {
|
|
169
|
+
const mouseScroll = parseMouseScroll(data);
|
|
170
|
+
if (mouseScroll) {
|
|
171
|
+
self.handleScrollKey(mouseScroll);
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
const scrollKey = parseScrollKey(data);
|
|
176
|
+
if (scrollKey) {
|
|
177
|
+
if (scrollKey === 'quit') {
|
|
178
|
+
if (self.inScrollMode) {
|
|
179
|
+
self.exitScrollMode();
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
return originalEmit(event, ...args);
|
|
183
|
+
}
|
|
184
|
+
self.handleScrollKey(scrollKey);
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
if (self.inScrollMode) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return originalEmit(event, ...args);
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
handleScrollKey(key) {
|
|
196
|
+
let needsRedraw = false;
|
|
197
|
+
switch (key) {
|
|
198
|
+
case 'pageUp':
|
|
199
|
+
if (!this.inScrollMode) {
|
|
200
|
+
this.enterScrollMode();
|
|
201
|
+
}
|
|
202
|
+
needsRedraw = this.scrollBuffer.pageUp();
|
|
203
|
+
break;
|
|
204
|
+
case 'pageDown':
|
|
205
|
+
needsRedraw = this.scrollBuffer.pageDown();
|
|
206
|
+
if (!this.scrollBuffer.isScrolledUp && this.inScrollMode) {
|
|
207
|
+
this.exitScrollMode();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
case 'scrollUp':
|
|
212
|
+
if (!this.inScrollMode) {
|
|
213
|
+
this.enterScrollMode();
|
|
214
|
+
}
|
|
215
|
+
needsRedraw = this.scrollBuffer.scrollUp(3);
|
|
216
|
+
break;
|
|
217
|
+
case 'scrollDown':
|
|
218
|
+
needsRedraw = this.scrollBuffer.scrollDown(3);
|
|
219
|
+
if (!this.scrollBuffer.isScrolledUp && this.inScrollMode) {
|
|
220
|
+
this.exitScrollMode();
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
break;
|
|
224
|
+
case 'home':
|
|
225
|
+
if (!this.inScrollMode) {
|
|
226
|
+
this.enterScrollMode();
|
|
227
|
+
}
|
|
228
|
+
this.scrollBuffer.scrollToTop();
|
|
229
|
+
needsRedraw = true;
|
|
230
|
+
break;
|
|
231
|
+
case 'end':
|
|
232
|
+
this.scrollBuffer.scrollToBottom();
|
|
233
|
+
if (this.inScrollMode) {
|
|
234
|
+
this.exitScrollMode();
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
case 'quit':
|
|
239
|
+
if (this.inScrollMode) {
|
|
240
|
+
this.exitScrollMode();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
if (needsRedraw && this.inScrollMode) {
|
|
246
|
+
this.renderScrollView();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
enterScrollMode() {
|
|
250
|
+
if (this.inScrollMode)
|
|
251
|
+
return;
|
|
252
|
+
this.inScrollMode = true;
|
|
253
|
+
this.rl.pause();
|
|
254
|
+
if (this.originalStdoutWrite) {
|
|
255
|
+
this.originalStdoutWrite('\x1b[?25l');
|
|
256
|
+
}
|
|
257
|
+
this.renderScrollView();
|
|
258
|
+
}
|
|
259
|
+
exitScrollMode() {
|
|
260
|
+
if (!this.inScrollMode)
|
|
261
|
+
return;
|
|
262
|
+
this.inScrollMode = false;
|
|
263
|
+
if (this.originalStdoutWrite) {
|
|
264
|
+
this.originalStdoutWrite('\x1b[?25h');
|
|
265
|
+
this.originalStdoutWrite('\x1b[2J\x1b[H');
|
|
266
|
+
}
|
|
267
|
+
const recentLines = this.scrollBuffer.getVisibleLines();
|
|
268
|
+
if (this.originalStdoutWrite) {
|
|
269
|
+
this.originalStdoutWrite(recentLines.join('\n') + '\n');
|
|
270
|
+
}
|
|
271
|
+
this.rl.resume();
|
|
272
|
+
this.prompt();
|
|
273
|
+
}
|
|
274
|
+
renderScrollView() {
|
|
275
|
+
if (!this.originalStdoutWrite)
|
|
276
|
+
return;
|
|
277
|
+
const rows = process.stdout.rows || 24;
|
|
278
|
+
const cols = process.stdout.columns || 80;
|
|
279
|
+
const visibleLines = this.scrollBuffer.getVisibleLines();
|
|
280
|
+
const info = this.scrollBuffer.getScrollInfo();
|
|
281
|
+
this.originalStdoutWrite('\x1b[2J\x1b[H');
|
|
282
|
+
for (let i = 0; i < visibleLines.length && i < rows - 1; i++) {
|
|
283
|
+
const line = visibleLines[i] || '';
|
|
284
|
+
const truncated = line.length > cols ? line.slice(0, cols - 1) + '…' : line;
|
|
285
|
+
this.originalStdoutWrite(truncated + '\n');
|
|
286
|
+
}
|
|
287
|
+
const scrollInfo = this.scrollBuffer.isScrolledUp
|
|
288
|
+
? colors.yellow(`↑ ${this.scrollBuffer.position} lines | ${info.percent}% | `)
|
|
289
|
+
: '';
|
|
290
|
+
const helpText = colors.gray('Page Up/Down • Home/End • Q to exit');
|
|
291
|
+
const statusBar = `\x1b[${rows};1H\x1b[7m ${scrollInfo}${helpText} \x1b[0m`;
|
|
292
|
+
this.originalStdoutWrite(statusBar);
|
|
118
293
|
}
|
|
119
294
|
prompt() {
|
|
120
295
|
this.rl.setPrompt(this.getPrompt());
|
|
121
296
|
this.rl.prompt();
|
|
122
297
|
}
|
|
123
298
|
async handleCommand(input) {
|
|
299
|
+
if (input.endsWith('?') && !input.startsWith('?') && input.length > 1) {
|
|
300
|
+
await this.runSearch(input.slice(0, -1).trim());
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
124
303
|
if (input.includes('=') && !input.includes(' ') && !input.startsWith('http')) {
|
|
125
304
|
}
|
|
126
305
|
const parts = this.parseLine(input);
|
|
@@ -162,9 +341,21 @@ export class RekShell {
|
|
|
162
341
|
case 'ssl':
|
|
163
342
|
await this.runTLS(parts[1], parts[2] ? parseInt(parts[2]) : 443);
|
|
164
343
|
return;
|
|
344
|
+
case 'security':
|
|
345
|
+
await this.runSecurityGrader(parts[1]);
|
|
346
|
+
return;
|
|
347
|
+
case 'ip':
|
|
348
|
+
await this.runIpIntelligence(parts[1]);
|
|
349
|
+
return;
|
|
165
350
|
case 'dns':
|
|
166
351
|
await this.runDNS(parts[1]);
|
|
167
352
|
return;
|
|
353
|
+
case 'dns:propagate':
|
|
354
|
+
await this.runDNSPropagation(parts[1], parts[2]);
|
|
355
|
+
return;
|
|
356
|
+
case 'dns:email':
|
|
357
|
+
await this.runDnsEmailCheck(parts[1], parts[2]);
|
|
358
|
+
return;
|
|
168
359
|
case 'rdap':
|
|
169
360
|
await this.runRDAP(parts[1]);
|
|
170
361
|
return;
|
|
@@ -650,25 +841,56 @@ export class RekShell {
|
|
|
650
841
|
const duration = Math.round(performance.now() - startTime);
|
|
651
842
|
const statusIcon = info.valid ? colors.green('✔') : colors.red('✖');
|
|
652
843
|
const statusText = info.valid ? colors.green('Valid') : colors.red('Invalid/Expired');
|
|
653
|
-
console.log(`${statusIcon} Certificate ${statusText}` + colors.gray(` (${duration}ms)\n`));
|
|
654
|
-
console.log(colors.bold(' Certificate:'));
|
|
655
|
-
console.log(` ${colors.cyan('Subject')}: ${info.subject?.CN || info.subject?.O || 'N/A'}`);
|
|
656
|
-
console.log(` ${colors.cyan('Issuer')}: ${info.issuer?.CN || info.issuer?.O || 'N/A'}`);
|
|
657
|
-
console.log(` ${colors.cyan('Valid From')}: ${info.validFrom.toISOString()}`);
|
|
658
|
-
console.log(` ${colors.cyan('Valid To')}: ${info.validTo.toISOString()}`);
|
|
659
844
|
const daysColor = info.daysRemaining < 30 ? colors.red : info.daysRemaining < 90 ? colors.yellow : colors.green;
|
|
660
|
-
console.log(
|
|
661
|
-
console.log(colors.
|
|
662
|
-
console.log(
|
|
663
|
-
console.log(`
|
|
664
|
-
|
|
845
|
+
console.log(`\n${colors.bold(colors.cyan('🔒 TLS/SSL Report'))}`);
|
|
846
|
+
console.log(`${statusIcon} Certificate ${statusText}` + colors.gray(` (${duration}ms)\n`));
|
|
847
|
+
console.log(colors.bold('Certificate:'));
|
|
848
|
+
console.log(` ${colors.gray('Subject:')}`);
|
|
849
|
+
for (const key of Object.keys(info.subject || {})) {
|
|
850
|
+
console.log(` ${colors.gray(key.padEnd(10))}: ${info.subject[key]}`);
|
|
851
|
+
}
|
|
852
|
+
console.log(` ${colors.gray('Issuer:')}`);
|
|
853
|
+
for (const key of Object.keys(info.issuer || {})) {
|
|
854
|
+
console.log(` ${colors.gray(key.padEnd(10))}: ${info.issuer[key]}`);
|
|
855
|
+
}
|
|
856
|
+
console.log(` ${colors.gray('Expires:')} ${daysColor(info.daysRemaining + ' days')} (${info.validTo.toISOString().split('T')[0]})`);
|
|
857
|
+
console.log(` ${colors.gray('Valid From:')} ${info.validFrom.toISOString().split('T')[0]}`);
|
|
858
|
+
console.log(` ${colors.gray('Valid To:')} ${info.validTo.toISOString().split('T')[0]}`);
|
|
859
|
+
console.log(` ${colors.gray('Valid:')} ${info.valid ? colors.green('Yes') : colors.red('No')}`);
|
|
860
|
+
if (info.altNames && info.altNames.length > 0) {
|
|
861
|
+
console.log(colors.bold('\nSubject Alternative Names (SANs):'));
|
|
862
|
+
for (const name of info.altNames) {
|
|
863
|
+
console.log(` ${colors.cyan('→')} ${name}`);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
console.log(colors.bold('\nPublic Key:'));
|
|
867
|
+
if (info.pubkey) {
|
|
868
|
+
console.log(` ${colors.gray('Algorithm:')} ${info.pubkey.algo}`);
|
|
869
|
+
console.log(` ${colors.gray('Size:')} ${info.pubkey.size} bits`);
|
|
870
|
+
}
|
|
871
|
+
else {
|
|
872
|
+
console.log(' Not available');
|
|
873
|
+
}
|
|
874
|
+
console.log(colors.bold('\nExtended Key Usage:'));
|
|
875
|
+
if (info.extKeyUsage && info.extKeyUsage.length > 0) {
|
|
876
|
+
for (const oid of info.extKeyUsage) {
|
|
877
|
+
console.log(` ${colors.cyan('→')} ${oid}`);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
console.log(' None');
|
|
882
|
+
}
|
|
883
|
+
console.log(colors.bold('\nConnection:'));
|
|
884
|
+
console.log(` ${colors.gray('Protocol:')} ${info.protocol || 'N/A'}`);
|
|
885
|
+
console.log(` ${colors.gray('Cipher:')} ${info.cipher?.name || 'N/A'}`);
|
|
886
|
+
console.log(` ${colors.gray('Auth:')} ${info.authorized ? colors.green('Trusted') : colors.red('Untrusted')}`);
|
|
665
887
|
if (info.authorizationError) {
|
|
666
|
-
console.log(`
|
|
888
|
+
console.log(` ${colors.gray('Auth Error:')} ${colors.red(String(info.authorizationError))}`);
|
|
667
889
|
}
|
|
668
|
-
console.log(colors.bold('\
|
|
669
|
-
console.log(`
|
|
670
|
-
console.log(`
|
|
671
|
-
console.log(`
|
|
890
|
+
console.log(colors.bold('\nFingerprints:'));
|
|
891
|
+
console.log(` ${colors.gray('SHA1:')} ${info.fingerprint}`);
|
|
892
|
+
console.log(` ${colors.gray('SHA256:')} ${info.fingerprint256}`);
|
|
893
|
+
console.log(` ${colors.gray('Serial:')} ${info.serialNumber}`);
|
|
672
894
|
this.lastResponse = info;
|
|
673
895
|
}
|
|
674
896
|
catch (error) {
|
|
@@ -676,6 +898,96 @@ export class RekShell {
|
|
|
676
898
|
}
|
|
677
899
|
console.log('');
|
|
678
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
|
+
}
|
|
679
991
|
async runDNS(domain) {
|
|
680
992
|
if (!domain) {
|
|
681
993
|
domain = this.getBaseDomain() || '';
|
|
@@ -736,6 +1048,90 @@ export class RekShell {
|
|
|
736
1048
|
}
|
|
737
1049
|
console.log('');
|
|
738
1050
|
}
|
|
1051
|
+
async runDNSPropagation(domain, type = 'A') {
|
|
1052
|
+
if (!domain) {
|
|
1053
|
+
domain = this.getBaseDomain() || '';
|
|
1054
|
+
if (!domain) {
|
|
1055
|
+
console.log(colors.yellow('Usage: dns:propagate <domain> [type]'));
|
|
1056
|
+
console.log(colors.gray(' Examples: dns:propagate google.com | dns:propagate github.com TXT'));
|
|
1057
|
+
console.log(colors.gray(' Or set a base URL first: url https://example.com'));
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
console.log(colors.gray(`Checking DNS propagation for ${domain} (${type})...`));
|
|
1062
|
+
try {
|
|
1063
|
+
const { checkPropagation, formatPropagationReport } = await import('../../dns/propagation.js');
|
|
1064
|
+
const results = await checkPropagation(domain, type);
|
|
1065
|
+
console.log(formatPropagationReport(results, domain, type));
|
|
1066
|
+
this.lastResponse = results;
|
|
1067
|
+
}
|
|
1068
|
+
catch (error) {
|
|
1069
|
+
console.error(colors.red(`Propagation check failed: ${error.message}`));
|
|
1070
|
+
}
|
|
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
|
+
}
|
|
739
1135
|
async runRDAP(domain) {
|
|
740
1136
|
if (!domain) {
|
|
741
1137
|
domain = this.getRootDomain() || '';
|
|
@@ -1660,38 +2056,14 @@ export class RekShell {
|
|
|
1660
2056
|
console.log('');
|
|
1661
2057
|
}
|
|
1662
2058
|
async runSearch(query) {
|
|
1663
|
-
if (!query.trim()) {
|
|
1664
|
-
console.log(colors.yellow('Usage: ? <query> or search <query>'));
|
|
1665
|
-
console.log(colors.gray(' Examples:'));
|
|
1666
|
-
console.log(colors.gray(' ? retry exponential backoff'));
|
|
1667
|
-
console.log(colors.gray(' search cache configuration'));
|
|
1668
|
-
return;
|
|
1669
|
-
}
|
|
1670
|
-
console.log(colors.gray('Searching documentation...'));
|
|
1671
2059
|
try {
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
console.log(colors.yellow(`No results for: "${query}"`));
|
|
1676
|
-
console.log(colors.gray('Try different keywords like: retry, cache, streaming, websocket, pagination'));
|
|
1677
|
-
return;
|
|
1678
|
-
}
|
|
1679
|
-
console.log(colors.bold(colors.cyan(`\nFound ${results.length} results for "${query}":\n`)));
|
|
1680
|
-
for (let i = 0; i < results.length; i++) {
|
|
1681
|
-
const result = results[i];
|
|
1682
|
-
const score = Math.round(result.score * 100);
|
|
1683
|
-
console.log(` ${colors.green(`${i + 1}.`)} ${colors.bold(result.title)} ${colors.gray(`(${score}%)`)}`);
|
|
1684
|
-
console.log(` ${colors.gray(result.path)}`);
|
|
1685
|
-
if (result.snippet) {
|
|
1686
|
-
const snippet = result.snippet.slice(0, 120).replace(/\n/g, ' ');
|
|
1687
|
-
console.log(` ${colors.gray(snippet)}${result.snippet.length > 120 ? '...' : ''}`);
|
|
1688
|
-
}
|
|
1689
|
-
console.log('');
|
|
1690
|
-
}
|
|
1691
|
-
console.log(colors.gray(' Tip: Use "get_doc <path>" to read full documentation'));
|
|
2060
|
+
this.rl.pause();
|
|
2061
|
+
await openSearchPanel(query.trim() || undefined);
|
|
2062
|
+
this.rl.resume();
|
|
1692
2063
|
}
|
|
1693
2064
|
catch (error) {
|
|
1694
2065
|
console.error(colors.red(`Search failed: ${error.message}`));
|
|
2066
|
+
this.rl.resume();
|
|
1695
2067
|
}
|
|
1696
2068
|
}
|
|
1697
2069
|
async runSuggest(useCase) {
|
|
@@ -1834,6 +2206,12 @@ export class RekShell {
|
|
|
1834
2206
|
${colors.green('suggest <use-case>')} Get implementation suggestions.
|
|
1835
2207
|
${colors.green('example <feature>')} Get code examples for a feature.
|
|
1836
2208
|
|
|
2209
|
+
${colors.bold('Navigation:')}
|
|
2210
|
+
${colors.green('Page Up/Down')} Scroll through command history.
|
|
2211
|
+
${colors.green('Home/End')} Jump to top/bottom of history.
|
|
2212
|
+
${colors.green('Mouse Scroll')} Scroll with mouse wheel.
|
|
2213
|
+
${colors.green('Escape')} Exit scroll mode.
|
|
2214
|
+
|
|
1837
2215
|
${colors.bold('Examples:')}
|
|
1838
2216
|
› url httpbin.org
|
|
1839
2217
|
› get /json
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import readline from 'node:readline';
|
|
2
2
|
import { createClient } from '../../core/client.js';
|
|
3
|
-
import
|
|
3
|
+
import colors from '../../utils/colors.js';
|
|
4
4
|
export async function startInteractiveWebSocket(url, headers) {
|
|
5
|
-
console.log(
|
|
6
|
-
console.log(
|
|
7
|
-
console.log(
|
|
8
|
-
console.log(
|
|
5
|
+
console.log(colors.gray('--------------------------------------------------'));
|
|
6
|
+
console.log(colors.cyan(`Connecting to ${colors.bold(url)}...`));
|
|
7
|
+
console.log(colors.gray('Commands: /quit to exit, /ping to send heartbeat'));
|
|
8
|
+
console.log(colors.gray('--------------------------------------------------\n'));
|
|
9
9
|
const client = createClient();
|
|
10
10
|
let ws;
|
|
11
11
|
try {
|
|
12
12
|
ws = client.websocket(url, { headers });
|
|
13
13
|
}
|
|
14
14
|
catch (error) {
|
|
15
|
-
console.error(
|
|
15
|
+
console.error(colors.red(`Error creating WebSocket: ${error.message}`));
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
const rl = readline.createInterface({
|
|
19
19
|
input: process.stdin,
|
|
20
20
|
output: process.stdout,
|
|
21
|
-
prompt:
|
|
21
|
+
prompt: colors.green('>> '),
|
|
22
22
|
});
|
|
23
23
|
const printIncoming = (text) => {
|
|
24
24
|
readline.cursorTo(process.stdout, 0);
|
|
@@ -27,37 +27,37 @@ export async function startInteractiveWebSocket(url, headers) {
|
|
|
27
27
|
rl.prompt(true);
|
|
28
28
|
};
|
|
29
29
|
ws.on('open', () => {
|
|
30
|
-
printIncoming(
|
|
30
|
+
printIncoming(colors.green('✔ Connected!'));
|
|
31
31
|
rl.prompt();
|
|
32
32
|
});
|
|
33
33
|
ws.on('close', (code, reason) => {
|
|
34
|
-
printIncoming(
|
|
34
|
+
printIncoming(colors.red(`✖ Disconnected (Code: ${code}${reason ? `, Reason: ${reason}` : ''})`));
|
|
35
35
|
rl.close();
|
|
36
36
|
process.exit(0);
|
|
37
37
|
});
|
|
38
38
|
ws.on('error', (err) => {
|
|
39
|
-
printIncoming(
|
|
39
|
+
printIncoming(colors.red(`⚠ Error: ${err.message}`));
|
|
40
40
|
});
|
|
41
41
|
ws.on('message', (msg) => {
|
|
42
42
|
const content = msg.isBinary
|
|
43
|
-
?
|
|
43
|
+
? colors.yellow(`<Binary ${msg.data.length} bytes>`)
|
|
44
44
|
: msg.data.toString();
|
|
45
|
-
printIncoming(`${
|
|
45
|
+
printIncoming(`${colors.cyan('<<')} ${content}`);
|
|
46
46
|
});
|
|
47
47
|
rl.on('line', (line) => {
|
|
48
48
|
const input = line.trim();
|
|
49
49
|
readline.moveCursor(process.stdout, 0, -1);
|
|
50
50
|
readline.clearLine(process.stdout, 0);
|
|
51
|
-
console.log(`${
|
|
51
|
+
console.log(`${colors.green('>>')} ${input}`);
|
|
52
52
|
if (input === '/quit' || input === '/exit') {
|
|
53
|
-
console.log(
|
|
53
|
+
console.log(colors.gray('Closing connection...'));
|
|
54
54
|
ws.close();
|
|
55
55
|
rl.close();
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
if (input === '/ping') {
|
|
59
59
|
ws.ping();
|
|
60
|
-
console.log(
|
|
60
|
+
console.log(colors.gray('(ping sent)'));
|
|
61
61
|
rl.prompt();
|
|
62
62
|
return;
|
|
63
63
|
}
|
|
@@ -66,11 +66,11 @@ export async function startInteractiveWebSocket(url, headers) {
|
|
|
66
66
|
ws.send(input);
|
|
67
67
|
}
|
|
68
68
|
catch (err) {
|
|
69
|
-
console.error(
|
|
69
|
+
console.error(colors.red(`Failed to send: ${err.message}`));
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
else if (input && !ws.isConnected) {
|
|
73
|
-
console.log(
|
|
73
|
+
console.log(colors.yellow('Not connected.'));
|
|
74
74
|
}
|
|
75
75
|
rl.prompt();
|
|
76
76
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CACpC;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAMlE,KAAK,gBAAgB,CAAC,CAAC,SAAS,gBAAgB,IAAI,CAClD,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,SAAS,GAAG;IAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;CAAE,GAAG,EAAE,CAAC,GACvE,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,SAAS,GAAG;IAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;CAAE,GAAG,EAAE,CAAC,GACjE;IAAE,OAAO,CAAC,EAAE,WAAW,CAAA;CAAE,KAC5B,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC;AAGjF,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,kBAAkB,IAAI;KACxD,CAAC,IAAI,MAAM,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACvC,CAAC;AAEF,qBAAa,aAAc,SAAQ,KAAK;IACnB,MAAM,EAAE,MAAM;IAAS,IAAI,EAAE,GAAG;IAAS,aAAa,EAAE,SAAS;gBAAjE,MAAM,EAAE,MAAM,EAAS,IAAI,EAAE,GAAG,EAAS,aAAa,EAAE,SAAS;CAIrF;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,kBAAkB,EACzD,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,GACV,cAAc,CAAC,CAAC,CAAC,CAqEnB"}
|