recker 1.0.29 → 1.0.31

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 (68) hide show
  1. package/README.md +28 -1
  2. package/dist/ai/client-ai.d.ts +41 -0
  3. package/dist/ai/client-ai.js +391 -0
  4. package/dist/ai/index.d.ts +2 -0
  5. package/dist/ai/index.js +2 -0
  6. package/dist/ai/memory.d.ts +35 -0
  7. package/dist/ai/memory.js +136 -0
  8. package/dist/browser/ai/client-ai.d.ts +41 -0
  9. package/dist/browser/ai/client-ai.js +391 -0
  10. package/dist/browser/ai/memory.d.ts +35 -0
  11. package/dist/browser/ai/memory.js +136 -0
  12. package/dist/browser/core/client.d.ts +6 -1
  13. package/dist/browser/core/client.js +18 -0
  14. package/dist/browser/transport/undici.js +11 -2
  15. package/dist/browser/types/ai-client.d.ts +32 -0
  16. package/dist/browser/types/ai-client.js +1 -0
  17. package/dist/browser/types/ai.d.ts +1 -1
  18. package/dist/cli/index.js +402 -1
  19. package/dist/cli/tui/scroll-buffer.js +4 -4
  20. package/dist/cli/tui/shell.d.ts +3 -0
  21. package/dist/cli/tui/shell.js +166 -19
  22. package/dist/core/client.d.ts +6 -1
  23. package/dist/core/client.js +18 -0
  24. package/dist/mcp/server.js +15 -0
  25. package/dist/mcp/tools/scrape.d.ts +3 -0
  26. package/dist/mcp/tools/scrape.js +156 -0
  27. package/dist/mcp/tools/security.d.ts +3 -0
  28. package/dist/mcp/tools/security.js +471 -0
  29. package/dist/mcp/tools/seo.d.ts +3 -0
  30. package/dist/mcp/tools/seo.js +427 -0
  31. package/dist/presets/anthropic.d.ts +3 -1
  32. package/dist/presets/anthropic.js +11 -1
  33. package/dist/presets/azure-openai.d.ts +3 -1
  34. package/dist/presets/azure-openai.js +11 -1
  35. package/dist/presets/cohere.d.ts +3 -1
  36. package/dist/presets/cohere.js +8 -2
  37. package/dist/presets/deepseek.d.ts +3 -1
  38. package/dist/presets/deepseek.js +8 -2
  39. package/dist/presets/fireworks.d.ts +3 -1
  40. package/dist/presets/fireworks.js +8 -2
  41. package/dist/presets/gemini.d.ts +3 -1
  42. package/dist/presets/gemini.js +8 -1
  43. package/dist/presets/groq.d.ts +3 -1
  44. package/dist/presets/groq.js +8 -2
  45. package/dist/presets/huggingface.d.ts +3 -1
  46. package/dist/presets/huggingface.js +8 -1
  47. package/dist/presets/mistral.d.ts +3 -1
  48. package/dist/presets/mistral.js +8 -2
  49. package/dist/presets/openai.d.ts +3 -1
  50. package/dist/presets/openai.js +9 -2
  51. package/dist/presets/perplexity.d.ts +3 -1
  52. package/dist/presets/perplexity.js +8 -2
  53. package/dist/presets/registry.d.ts +4 -0
  54. package/dist/presets/registry.js +48 -0
  55. package/dist/presets/replicate.d.ts +3 -1
  56. package/dist/presets/replicate.js +8 -1
  57. package/dist/presets/together.d.ts +3 -1
  58. package/dist/presets/together.js +8 -2
  59. package/dist/presets/xai.d.ts +3 -1
  60. package/dist/presets/xai.js +8 -2
  61. package/dist/scrape/spider.js +1 -1
  62. package/dist/transport/undici.js +11 -2
  63. package/dist/types/ai-client.d.ts +32 -0
  64. package/dist/types/ai-client.js +1 -0
  65. package/dist/types/ai.d.ts +1 -1
  66. package/dist/utils/colors.d.ts +2 -0
  67. package/dist/utils/colors.js +4 -0
  68. package/package.json +1 -1
@@ -0,0 +1,471 @@
1
+ import { createClient } from '../../core/client.js';
2
+ import { inspectTLS } from '../../utils/tls-inspector.js';
3
+ import { rdap, supportsRDAP } from '../../utils/rdap.js';
4
+ import { getIpInfo, isValidIP } from '../ip-intel.js';
5
+ import { analyzeSecurityHeaders, quickSecurityCheck } from '../../utils/security-grader.js';
6
+ import { validateSpf, validateDmarc, checkDkim, checkDnsHealth, getSecurityRecords, } from '../../utils/dns-toolkit.js';
7
+ async function tlsInspect(args) {
8
+ const host = String(args.host || '');
9
+ const port = Number(args.port) || 443;
10
+ if (!host) {
11
+ return {
12
+ content: [{ type: 'text', text: 'Error: host is required' }],
13
+ isError: true,
14
+ };
15
+ }
16
+ try {
17
+ const info = await inspectTLS(host, port);
18
+ const output = {
19
+ host,
20
+ port,
21
+ valid: info.valid,
22
+ authorized: info.authorized,
23
+ certificate: {
24
+ subject: info.subject,
25
+ issuer: info.issuer,
26
+ validFrom: info.validFrom.toISOString(),
27
+ validTo: info.validTo.toISOString(),
28
+ daysRemaining: info.daysRemaining,
29
+ serialNumber: info.serialNumber,
30
+ fingerprint256: info.fingerprint256,
31
+ },
32
+ connection: {
33
+ protocol: info.protocol,
34
+ cipher: info.cipher,
35
+ },
36
+ subjectAltNames: info.altNames,
37
+ publicKey: info.pubkey,
38
+ extendedKeyUsage: info.extKeyUsage,
39
+ warnings: [],
40
+ };
41
+ if (info.daysRemaining <= 30) {
42
+ output.warnings.push(`Certificate expires in ${info.daysRemaining} days!`);
43
+ }
44
+ if (!info.authorized) {
45
+ output.warnings.push(`Certificate not trusted: ${info.authorizationError?.message || 'unknown reason'}`);
46
+ }
47
+ if (info.pubkey && info.pubkey.algo === 'rsa' && info.pubkey.size < 2048) {
48
+ output.warnings.push(`RSA key size ${info.pubkey.size} bits is considered weak`);
49
+ }
50
+ return {
51
+ content: [{ type: 'text', text: JSON.stringify(output, null, 2) }],
52
+ };
53
+ }
54
+ catch (error) {
55
+ return {
56
+ content: [{ type: 'text', text: `TLS inspection failed: ${error.message}` }],
57
+ isError: true,
58
+ };
59
+ }
60
+ }
61
+ async function rdapLookup(args) {
62
+ const query = String(args.query || '');
63
+ if (!query) {
64
+ return {
65
+ content: [{ type: 'text', text: 'Error: query (domain or IP) is required' }],
66
+ isError: true,
67
+ };
68
+ }
69
+ if (!query.includes(':') && !/^\d+\.\d+\.\d+\.\d+$/.test(query)) {
70
+ const tld = query.split('.').pop()?.toLowerCase() || '';
71
+ if (!supportsRDAP(tld)) {
72
+ return {
73
+ content: [{
74
+ type: 'text',
75
+ text: `RDAP is not available for .${tld} domains. Use rek_whois_lookup instead for traditional WHOIS data.`,
76
+ }],
77
+ isError: true,
78
+ };
79
+ }
80
+ }
81
+ try {
82
+ const client = createClient({ timeout: 15000 });
83
+ const result = await rdap(client, query);
84
+ const output = {
85
+ query,
86
+ ldhName: result.ldhName,
87
+ handle: result.handle,
88
+ status: result.status,
89
+ };
90
+ if (result.events) {
91
+ output.events = {};
92
+ for (const event of result.events) {
93
+ output.events[event.eventAction] = event.eventDate;
94
+ }
95
+ }
96
+ if (result.nameservers) {
97
+ output.nameservers = result.nameservers.map(ns => ns.ldhName);
98
+ }
99
+ if (result.entities) {
100
+ output.entities = result.entities.map(entity => ({
101
+ handle: entity.handle,
102
+ roles: entity.roles,
103
+ }));
104
+ }
105
+ if (result.secureDNS) {
106
+ output.dnssec = result.secureDNS.delegationSigned ? 'signed' : 'unsigned';
107
+ }
108
+ return {
109
+ content: [{ type: 'text', text: JSON.stringify(output, null, 2) }],
110
+ };
111
+ }
112
+ catch (error) {
113
+ return {
114
+ content: [{ type: 'text', text: `RDAP lookup failed: ${error.message}` }],
115
+ isError: true,
116
+ };
117
+ }
118
+ }
119
+ async function geoipLookup(args) {
120
+ const ip = String(args.ip || '');
121
+ if (!ip) {
122
+ return {
123
+ content: [{ type: 'text', text: 'Error: ip address is required' }],
124
+ isError: true,
125
+ };
126
+ }
127
+ if (!isValidIP(ip)) {
128
+ return {
129
+ content: [{ type: 'text', text: `Invalid IP address: ${ip}` }],
130
+ isError: true,
131
+ };
132
+ }
133
+ try {
134
+ const info = await getIpInfo(ip);
135
+ const output = {
136
+ ip: info.ip,
137
+ isIPv6: info.isIPv6,
138
+ };
139
+ if (info.bogon) {
140
+ output.bogon = true;
141
+ output.bogonType = info.bogonType;
142
+ output.note = 'This is a private/reserved IP address. No geolocation available.';
143
+ }
144
+ else {
145
+ output.location = {
146
+ city: info.city,
147
+ region: info.region,
148
+ country: info.country,
149
+ countryCode: info.countryCode,
150
+ continent: info.continent,
151
+ coordinates: info.loc,
152
+ timezone: info.timezone,
153
+ postalCode: info.postal,
154
+ accuracyRadius: info.accuracy ? `${info.accuracy} km` : undefined,
155
+ };
156
+ }
157
+ return {
158
+ content: [{ type: 'text', text: JSON.stringify(output, null, 2) }],
159
+ };
160
+ }
161
+ catch (error) {
162
+ return {
163
+ content: [{ type: 'text', text: `GeoIP lookup failed: ${error.message}` }],
164
+ isError: true,
165
+ };
166
+ }
167
+ }
168
+ async function securityHeadersAnalyze(args) {
169
+ const url = String(args.url || '');
170
+ const quick = Boolean(args.quick);
171
+ if (!url) {
172
+ return {
173
+ content: [{ type: 'text', text: 'Error: url is required' }],
174
+ isError: true,
175
+ };
176
+ }
177
+ try {
178
+ const client = createClient({ timeout: 15000 });
179
+ const response = await client.head(url);
180
+ if (quick) {
181
+ const result = quickSecurityCheck(response.headers);
182
+ return {
183
+ content: [{
184
+ type: 'text',
185
+ text: JSON.stringify({
186
+ url,
187
+ secure: result.secure,
188
+ criticalIssues: result.criticalIssues,
189
+ }, null, 2),
190
+ }],
191
+ };
192
+ }
193
+ const report = analyzeSecurityHeaders(response.headers);
194
+ const output = {
195
+ url,
196
+ grade: report.grade,
197
+ score: report.score,
198
+ summary: report.summary,
199
+ };
200
+ const failed = report.details.filter(d => d.status === 'fail');
201
+ const warnings = report.details.filter(d => d.status === 'warn');
202
+ const passed = report.details.filter(d => d.status === 'pass');
203
+ if (failed.length > 0) {
204
+ output.failed = failed.map(d => ({
205
+ header: d.header,
206
+ message: d.message,
207
+ recommendation: d.recommendation,
208
+ }));
209
+ }
210
+ if (warnings.length > 0) {
211
+ output.warnings = warnings.map(d => ({
212
+ header: d.header,
213
+ message: d.message,
214
+ recommendation: d.recommendation,
215
+ }));
216
+ }
217
+ output.passed = passed.map(d => d.header);
218
+ if (report.csp) {
219
+ output.csp = {
220
+ score: report.csp.score,
221
+ issues: report.csp.issues,
222
+ missingDirectives: report.csp.missingDirectives,
223
+ hasUnsafeInline: report.csp.hasUnsafeInline,
224
+ hasUnsafeEval: report.csp.hasUnsafeEval,
225
+ };
226
+ }
227
+ return {
228
+ content: [{ type: 'text', text: JSON.stringify(output, null, 2) }],
229
+ };
230
+ }
231
+ catch (error) {
232
+ return {
233
+ content: [{ type: 'text', text: `Security headers analysis failed: ${error.message}` }],
234
+ isError: true,
235
+ };
236
+ }
237
+ }
238
+ async function dnsToolkit(args) {
239
+ const domain = String(args.domain || '');
240
+ const check = String(args.check || 'all');
241
+ const dkimSelector = args.dkimSelector;
242
+ if (!domain) {
243
+ return {
244
+ content: [{ type: 'text', text: 'Error: domain is required' }],
245
+ isError: true,
246
+ };
247
+ }
248
+ try {
249
+ const output = { domain };
250
+ if (check === 'all' || check === 'health') {
251
+ const healthReport = await checkDnsHealth(domain);
252
+ output.health = {
253
+ score: healthReport.score,
254
+ grade: healthReport.grade,
255
+ checks: healthReport.checks,
256
+ };
257
+ }
258
+ if (check === 'all' || check === 'spf') {
259
+ const spfResult = await validateSpf(domain);
260
+ output.spf = {
261
+ valid: spfResult.valid,
262
+ record: spfResult.record,
263
+ mechanisms: spfResult.mechanisms,
264
+ lookupCount: spfResult.lookupCount,
265
+ warnings: spfResult.warnings,
266
+ errors: spfResult.errors,
267
+ };
268
+ }
269
+ if (check === 'all' || check === 'dmarc') {
270
+ const dmarcResult = await validateDmarc(domain);
271
+ output.dmarc = {
272
+ valid: dmarcResult.valid,
273
+ record: dmarcResult.record,
274
+ policy: dmarcResult.policy,
275
+ subdomainPolicy: dmarcResult.subdomainPolicy,
276
+ percentage: dmarcResult.percentage,
277
+ reportingUris: dmarcResult.rua,
278
+ warnings: dmarcResult.warnings,
279
+ };
280
+ }
281
+ if (check === 'all' || check === 'dkim') {
282
+ const selectors = dkimSelector
283
+ ? [dkimSelector]
284
+ : ['default', 'google', 'selector1', 'selector2', 'k1', 's1', 'dkim'];
285
+ const dkimResults = [];
286
+ for (const sel of selectors) {
287
+ const result = await checkDkim(domain, sel);
288
+ if (result.found) {
289
+ dkimResults.push({
290
+ selector: sel,
291
+ found: true,
292
+ publicKey: result.publicKey?.slice(0, 50) + '...',
293
+ });
294
+ }
295
+ }
296
+ output.dkim = {
297
+ found: dkimResults.length > 0,
298
+ selectors: dkimResults,
299
+ note: dkimResults.length === 0
300
+ ? 'No DKIM records found with common selectors. Specify dkimSelector parameter if you know the selector.'
301
+ : undefined,
302
+ };
303
+ }
304
+ if (check === 'all' || check === 'records') {
305
+ const records = await getSecurityRecords(domain);
306
+ output.securityRecords = {
307
+ spf: records.spf,
308
+ dmarc: records.dmarc,
309
+ caa: records.caa,
310
+ mx: records.mx,
311
+ };
312
+ }
313
+ return {
314
+ content: [{ type: 'text', text: JSON.stringify(output, null, 2) }],
315
+ };
316
+ }
317
+ catch (error) {
318
+ return {
319
+ content: [{ type: 'text', text: `DNS toolkit failed: ${error.message}` }],
320
+ isError: true,
321
+ };
322
+ }
323
+ }
324
+ export const securityTools = [
325
+ {
326
+ name: 'rek_tls_inspect',
327
+ description: `Inspect SSL/TLS certificate and connection details for a host.
328
+
329
+ Returns:
330
+ - Certificate validity and expiration
331
+ - Subject and issuer details
332
+ - Subject Alternative Names (SANs)
333
+ - TLS protocol version and cipher suite
334
+ - Public key algorithm and size
335
+ - Warnings for expiring certs, weak keys, or trust issues
336
+
337
+ Use this to check certificate health, debug TLS issues, or audit HTTPS configuration.`,
338
+ inputSchema: {
339
+ type: 'object',
340
+ properties: {
341
+ host: {
342
+ type: 'string',
343
+ description: 'Hostname to inspect (e.g., "example.com")',
344
+ },
345
+ port: {
346
+ type: 'number',
347
+ description: 'Port number (default: 443)',
348
+ default: 443,
349
+ },
350
+ },
351
+ required: ['host'],
352
+ },
353
+ },
354
+ {
355
+ name: 'rek_rdap_lookup',
356
+ description: `Perform RDAP lookup (modern WHOIS) for a domain or IP address.
357
+
358
+ RDAP provides structured JSON data including:
359
+ - Registration dates (created, updated, expires)
360
+ - Status (active, locked, etc.)
361
+ - Nameservers
362
+ - DNSSEC status
363
+ - Registrar/Registrant info
364
+
365
+ Note: Some TLDs (.io, .ai, etc.) don't support RDAP yet - use rek_whois_lookup for those.`,
366
+ inputSchema: {
367
+ type: 'object',
368
+ properties: {
369
+ query: {
370
+ type: 'string',
371
+ description: 'Domain name or IP address to lookup',
372
+ },
373
+ },
374
+ required: ['query'],
375
+ },
376
+ },
377
+ {
378
+ name: 'rek_geoip_lookup',
379
+ description: `Get geolocation data for an IP address using MaxMind GeoLite2 database.
380
+
381
+ Returns:
382
+ - City, region, country, continent
383
+ - Coordinates (latitude, longitude)
384
+ - Timezone
385
+ - Accuracy radius
386
+ - Bogon detection (identifies private/reserved IPs)
387
+
388
+ Database is cached locally and updated automatically.`,
389
+ inputSchema: {
390
+ type: 'object',
391
+ properties: {
392
+ ip: {
393
+ type: 'string',
394
+ description: 'IPv4 or IPv6 address to lookup',
395
+ },
396
+ },
397
+ required: ['ip'],
398
+ },
399
+ },
400
+ {
401
+ name: 'rek_security_headers',
402
+ description: `Analyze HTTP security headers for a URL.
403
+
404
+ Grades (A+ to F) based on:
405
+ - HSTS (Strict-Transport-Security)
406
+ - CSP (Content-Security-Policy) with detailed analysis
407
+ - X-Frame-Options / frame-ancestors
408
+ - X-Content-Type-Options
409
+ - Referrer-Policy
410
+ - Permissions-Policy
411
+ - Cross-Origin policies (COOP, COEP, CORP)
412
+ - Information leakage (Server, X-Powered-By)
413
+
414
+ Use quick=true for a fast critical issues check.`,
415
+ inputSchema: {
416
+ type: 'object',
417
+ properties: {
418
+ url: {
419
+ type: 'string',
420
+ description: 'URL to analyze',
421
+ },
422
+ quick: {
423
+ type: 'boolean',
424
+ description: 'Quick mode - only check critical security issues',
425
+ default: false,
426
+ },
427
+ },
428
+ required: ['url'],
429
+ },
430
+ },
431
+ {
432
+ name: 'rek_dns_toolkit',
433
+ description: `Advanced DNS security analysis for email authentication and DNS health.
434
+
435
+ Checks:
436
+ - SPF validation (syntax, lookup count, mechanisms)
437
+ - DMARC validation (policy, reporting, alignment)
438
+ - DKIM discovery (tries common selectors)
439
+ - CAA records (certificate authority authorization)
440
+ - MX records
441
+ - Overall DNS health score
442
+
443
+ Use check parameter to run specific checks: "all", "health", "spf", "dmarc", "dkim", "records"`,
444
+ inputSchema: {
445
+ type: 'object',
446
+ properties: {
447
+ domain: {
448
+ type: 'string',
449
+ description: 'Domain to analyze',
450
+ },
451
+ check: {
452
+ type: 'string',
453
+ description: 'Which check to run: all, health, spf, dmarc, dkim, records',
454
+ default: 'all',
455
+ },
456
+ dkimSelector: {
457
+ type: 'string',
458
+ description: 'Specific DKIM selector to check (if known)',
459
+ },
460
+ },
461
+ required: ['domain'],
462
+ },
463
+ },
464
+ ];
465
+ export const securityToolHandlers = {
466
+ rek_tls_inspect: tlsInspect,
467
+ rek_rdap_lookup: rdapLookup,
468
+ rek_geoip_lookup: geoipLookup,
469
+ rek_security_headers: securityHeadersAnalyze,
470
+ rek_dns_toolkit: dnsToolkit,
471
+ };
@@ -0,0 +1,3 @@
1
+ import type { MCPTool, MCPToolResult } from '../types.js';
2
+ export declare const seoTools: MCPTool[];
3
+ export declare const seoToolHandlers: Record<string, (args: Record<string, unknown>) => Promise<MCPToolResult>>;