recker 1.0.29-next.3524ab6 → 1.0.29-next.72485b7
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/dist/ai/memory.d.ts +35 -0
- package/dist/ai/memory.js +136 -0
- package/dist/browser/types/ai-client.d.ts +29 -0
- package/dist/browser/types/ai-client.js +1 -0
- package/dist/cli/tui/shell.js +4 -4
- package/dist/mcp/server.js +10 -0
- package/dist/mcp/tools/scrape.d.ts +3 -0
- package/dist/mcp/tools/scrape.js +156 -0
- package/dist/mcp/tools/security.d.ts +3 -0
- package/dist/mcp/tools/security.js +471 -0
- package/dist/mcp/tools/seo.js +4 -4
- package/dist/scrape/spider.js +1 -1
- package/dist/types/ai-client.d.ts +29 -0
- package/dist/types/ai-client.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ChatMessage } from '../types/ai.js';
|
|
2
|
+
import type { AIMemoryConfig } from '../types/ai-client.js';
|
|
3
|
+
export declare class ConversationMemory {
|
|
4
|
+
private config;
|
|
5
|
+
private systemMessage;
|
|
6
|
+
private messages;
|
|
7
|
+
constructor(config?: AIMemoryConfig);
|
|
8
|
+
setSystemPrompt(prompt: string): void;
|
|
9
|
+
getSystemPrompt(): string;
|
|
10
|
+
addUserMessage(content: string): void;
|
|
11
|
+
addAssistantMessage(content: string): void;
|
|
12
|
+
addMessage(message: ChatMessage): void;
|
|
13
|
+
buildMessages(userPrompt: string): ChatMessage[];
|
|
14
|
+
recordResponse(content: string): void;
|
|
15
|
+
getMessages(): ChatMessage[];
|
|
16
|
+
getConversation(): readonly ChatMessage[];
|
|
17
|
+
getPairCount(): number;
|
|
18
|
+
clear(): void;
|
|
19
|
+
reset(): void;
|
|
20
|
+
setConfig(config: Partial<AIMemoryConfig>): void;
|
|
21
|
+
getConfig(): AIMemoryConfig;
|
|
22
|
+
private prune;
|
|
23
|
+
isEmpty(): boolean;
|
|
24
|
+
getMessageCount(): number;
|
|
25
|
+
toJSON(): {
|
|
26
|
+
config: AIMemoryConfig;
|
|
27
|
+
systemPrompt: string | null;
|
|
28
|
+
messages: ChatMessage[];
|
|
29
|
+
};
|
|
30
|
+
static fromJSON(data: {
|
|
31
|
+
config?: AIMemoryConfig;
|
|
32
|
+
systemPrompt?: string | null;
|
|
33
|
+
messages?: ChatMessage[];
|
|
34
|
+
}): ConversationMemory;
|
|
35
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
const DEFAULT_MAX_PAIRS = 12;
|
|
2
|
+
export class ConversationMemory {
|
|
3
|
+
config;
|
|
4
|
+
systemMessage = null;
|
|
5
|
+
messages = [];
|
|
6
|
+
constructor(config = {}) {
|
|
7
|
+
this.config = {
|
|
8
|
+
maxPairs: config.maxPairs ?? DEFAULT_MAX_PAIRS,
|
|
9
|
+
systemPrompt: config.systemPrompt ?? '',
|
|
10
|
+
};
|
|
11
|
+
if (this.config.systemPrompt) {
|
|
12
|
+
this.systemMessage = {
|
|
13
|
+
role: 'system',
|
|
14
|
+
content: this.config.systemPrompt,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
setSystemPrompt(prompt) {
|
|
19
|
+
this.config.systemPrompt = prompt;
|
|
20
|
+
if (prompt) {
|
|
21
|
+
this.systemMessage = {
|
|
22
|
+
role: 'system',
|
|
23
|
+
content: prompt,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
this.systemMessage = null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
getSystemPrompt() {
|
|
31
|
+
return this.config.systemPrompt;
|
|
32
|
+
}
|
|
33
|
+
addUserMessage(content) {
|
|
34
|
+
this.messages.push({
|
|
35
|
+
role: 'user',
|
|
36
|
+
content,
|
|
37
|
+
});
|
|
38
|
+
this.prune();
|
|
39
|
+
}
|
|
40
|
+
addAssistantMessage(content) {
|
|
41
|
+
this.messages.push({
|
|
42
|
+
role: 'assistant',
|
|
43
|
+
content,
|
|
44
|
+
});
|
|
45
|
+
this.prune();
|
|
46
|
+
}
|
|
47
|
+
addMessage(message) {
|
|
48
|
+
if (message.role === 'system') {
|
|
49
|
+
this.setSystemPrompt(typeof message.content === 'string' ? message.content : '');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this.messages.push(message);
|
|
53
|
+
this.prune();
|
|
54
|
+
}
|
|
55
|
+
buildMessages(userPrompt) {
|
|
56
|
+
this.addUserMessage(userPrompt);
|
|
57
|
+
return this.getMessages();
|
|
58
|
+
}
|
|
59
|
+
recordResponse(content) {
|
|
60
|
+
this.addAssistantMessage(content);
|
|
61
|
+
}
|
|
62
|
+
getMessages() {
|
|
63
|
+
const result = [];
|
|
64
|
+
if (this.systemMessage) {
|
|
65
|
+
result.push(this.systemMessage);
|
|
66
|
+
}
|
|
67
|
+
result.push(...this.messages);
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
getConversation() {
|
|
71
|
+
return this.messages;
|
|
72
|
+
}
|
|
73
|
+
getPairCount() {
|
|
74
|
+
let pairs = 0;
|
|
75
|
+
for (let i = 0; i < this.messages.length - 1; i += 2) {
|
|
76
|
+
if (this.messages[i].role === 'user' &&
|
|
77
|
+
this.messages[i + 1]?.role === 'assistant') {
|
|
78
|
+
pairs++;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return pairs;
|
|
82
|
+
}
|
|
83
|
+
clear() {
|
|
84
|
+
this.messages = [];
|
|
85
|
+
}
|
|
86
|
+
reset() {
|
|
87
|
+
this.messages = [];
|
|
88
|
+
this.systemMessage = null;
|
|
89
|
+
this.config.systemPrompt = '';
|
|
90
|
+
}
|
|
91
|
+
setConfig(config) {
|
|
92
|
+
if (config.maxPairs !== undefined) {
|
|
93
|
+
this.config.maxPairs = config.maxPairs;
|
|
94
|
+
this.prune();
|
|
95
|
+
}
|
|
96
|
+
if (config.systemPrompt !== undefined) {
|
|
97
|
+
this.setSystemPrompt(config.systemPrompt);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
getConfig() {
|
|
101
|
+
return { ...this.config };
|
|
102
|
+
}
|
|
103
|
+
prune() {
|
|
104
|
+
const maxMessages = this.config.maxPairs * 2;
|
|
105
|
+
if (this.messages.length > maxMessages) {
|
|
106
|
+
const excess = this.messages.length - maxMessages;
|
|
107
|
+
const toRemove = Math.ceil(excess / 2) * 2;
|
|
108
|
+
this.messages = this.messages.slice(toRemove);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
isEmpty() {
|
|
112
|
+
return this.messages.length === 0;
|
|
113
|
+
}
|
|
114
|
+
getMessageCount() {
|
|
115
|
+
return this.messages.length;
|
|
116
|
+
}
|
|
117
|
+
toJSON() {
|
|
118
|
+
return {
|
|
119
|
+
config: this.config,
|
|
120
|
+
systemPrompt: this.systemMessage?.content,
|
|
121
|
+
messages: [...this.messages],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
static fromJSON(data) {
|
|
125
|
+
const memory = new ConversationMemory(data.config);
|
|
126
|
+
if (data.systemPrompt) {
|
|
127
|
+
memory.setSystemPrompt(data.systemPrompt);
|
|
128
|
+
}
|
|
129
|
+
if (data.messages) {
|
|
130
|
+
for (const msg of data.messages) {
|
|
131
|
+
memory.addMessage(msg);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return memory;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AIResponse, AIStream, ChatMessage, AIProvider } from './ai.js';
|
|
2
|
+
export interface AIMemoryConfig {
|
|
3
|
+
maxPairs?: number;
|
|
4
|
+
systemPrompt?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface PresetAIConfig {
|
|
7
|
+
provider: AIProvider;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
model: string;
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
memory?: AIMemoryConfig;
|
|
12
|
+
organization?: string;
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
export interface ClientAI {
|
|
16
|
+
chat(prompt: string): Promise<AIResponse>;
|
|
17
|
+
chatStream(prompt: string): Promise<AIStream>;
|
|
18
|
+
prompt(prompt: string): Promise<AIResponse>;
|
|
19
|
+
promptStream(prompt: string): Promise<AIStream>;
|
|
20
|
+
clearMemory(): void;
|
|
21
|
+
getMemory(): readonly ChatMessage[];
|
|
22
|
+
setMemoryConfig(config: Partial<AIMemoryConfig>): void;
|
|
23
|
+
getMemoryConfig(): AIMemoryConfig;
|
|
24
|
+
readonly provider: AIProvider;
|
|
25
|
+
readonly model: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ClientOptionsWithAI {
|
|
28
|
+
_aiConfig?: PresetAIConfig;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/cli/tui/shell.js
CHANGED
|
@@ -1517,7 +1517,7 @@ ${colors.bold('Network:')}
|
|
|
1517
1517
|
}
|
|
1518
1518
|
async runSpider(args) {
|
|
1519
1519
|
let url = '';
|
|
1520
|
-
let maxDepth =
|
|
1520
|
+
let maxDepth = 5;
|
|
1521
1521
|
let maxPages = 100;
|
|
1522
1522
|
let concurrency = 5;
|
|
1523
1523
|
let seoEnabled = false;
|
|
@@ -1525,7 +1525,7 @@ ${colors.bold('Network:')}
|
|
|
1525
1525
|
for (let i = 0; i < args.length; i++) {
|
|
1526
1526
|
const arg = args[i];
|
|
1527
1527
|
if (arg.startsWith('depth=')) {
|
|
1528
|
-
maxDepth = parseInt(arg.split('=')[1]) ||
|
|
1528
|
+
maxDepth = parseInt(arg.split('=')[1]) || 5;
|
|
1529
1529
|
}
|
|
1530
1530
|
else if (arg.startsWith('limit=')) {
|
|
1531
1531
|
maxPages = parseInt(arg.split('=')[1]) || 100;
|
|
@@ -1547,7 +1547,7 @@ ${colors.bold('Network:')}
|
|
|
1547
1547
|
if (!this.baseUrl) {
|
|
1548
1548
|
console.log(colors.yellow('Usage: spider <url> [options]'));
|
|
1549
1549
|
console.log(colors.gray(' Options:'));
|
|
1550
|
-
console.log(colors.gray(' depth=
|
|
1550
|
+
console.log(colors.gray(' depth=5 Max crawl depth'));
|
|
1551
1551
|
console.log(colors.gray(' limit=100 Max pages to crawl'));
|
|
1552
1552
|
console.log(colors.gray(' concurrency=5 Concurrent requests'));
|
|
1553
1553
|
console.log(colors.gray(' seo Enable SEO analysis'));
|
|
@@ -2710,7 +2710,7 @@ ${colors.bold('Network:')}
|
|
|
2710
2710
|
${colors.bold('Web Crawler:')}
|
|
2711
2711
|
${colors.green('spider <url>')} Crawl website following internal links.
|
|
2712
2712
|
${colors.gray('Options:')}
|
|
2713
|
-
${colors.white('--depth=
|
|
2713
|
+
${colors.white('--depth=5')} ${colors.gray('Maximum depth to crawl')}
|
|
2714
2714
|
${colors.white('--limit=100')} ${colors.gray('Maximum pages to crawl')}
|
|
2715
2715
|
${colors.white('--concurrency=5')} ${colors.gray('Parallel requests')}
|
|
2716
2716
|
|
package/dist/mcp/server.js
CHANGED
|
@@ -9,6 +9,8 @@ import { UnsupportedError } from '../core/errors.js';
|
|
|
9
9
|
import { getIpInfo, isValidIP, isGeoIPAvailable, isBogon, isIPv6 } from './ip-intel.js';
|
|
10
10
|
import { networkTools, networkToolHandlers } from './tools/network.js';
|
|
11
11
|
import { seoTools, seoToolHandlers } from './tools/seo.js';
|
|
12
|
+
import { scrapeTools, scrapeToolHandlers } from './tools/scrape.js';
|
|
13
|
+
import { securityTools, securityToolHandlers } from './tools/security.js';
|
|
12
14
|
import { ToolRegistry } from './tools/registry.js';
|
|
13
15
|
import { loadToolModules } from './tools/loader.js';
|
|
14
16
|
export class MCPServer {
|
|
@@ -50,6 +52,14 @@ export class MCPServer {
|
|
|
50
52
|
tools: seoTools,
|
|
51
53
|
handlers: seoToolHandlers
|
|
52
54
|
});
|
|
55
|
+
this.toolRegistry.registerModule({
|
|
56
|
+
tools: scrapeTools,
|
|
57
|
+
handlers: scrapeToolHandlers
|
|
58
|
+
});
|
|
59
|
+
this.toolRegistry.registerModule({
|
|
60
|
+
tools: securityTools,
|
|
61
|
+
handlers: securityToolHandlers
|
|
62
|
+
});
|
|
53
63
|
}
|
|
54
64
|
indexReady = null;
|
|
55
65
|
async ensureIndexReady() {
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { createClient } from '../../core/client.js';
|
|
2
|
+
import { ScrapeDocument } from '../../scrape/document.js';
|
|
3
|
+
async function scrapeUrl(args) {
|
|
4
|
+
const url = String(args.url || '');
|
|
5
|
+
const selectors = args.selectors;
|
|
6
|
+
const extract = args.extract;
|
|
7
|
+
const selector = args.selector;
|
|
8
|
+
if (!url) {
|
|
9
|
+
return {
|
|
10
|
+
content: [{ type: 'text', text: 'Error: url is required' }],
|
|
11
|
+
isError: true,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const client = createClient({ timeout: 30000 });
|
|
16
|
+
const response = await client.get(url);
|
|
17
|
+
const html = await response.text();
|
|
18
|
+
const doc = await ScrapeDocument.create(html, { baseUrl: url });
|
|
19
|
+
const output = {
|
|
20
|
+
url,
|
|
21
|
+
title: doc.title(),
|
|
22
|
+
};
|
|
23
|
+
if (selector) {
|
|
24
|
+
const elements = doc.selectAll(selector);
|
|
25
|
+
output.results = elements.map(el => ({
|
|
26
|
+
text: el.text(),
|
|
27
|
+
html: el.html(),
|
|
28
|
+
tag: el.tagName(),
|
|
29
|
+
attrs: el.attrs(),
|
|
30
|
+
}));
|
|
31
|
+
output.count = elements.length;
|
|
32
|
+
}
|
|
33
|
+
if (selectors && Object.keys(selectors).length > 0) {
|
|
34
|
+
const extracted = {};
|
|
35
|
+
for (const [key, sel] of Object.entries(selectors)) {
|
|
36
|
+
const isMultiple = sel.endsWith('[]');
|
|
37
|
+
const actualSel = isMultiple ? sel.slice(0, -2) : sel;
|
|
38
|
+
if (isMultiple) {
|
|
39
|
+
extracted[key] = doc.texts(actualSel);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
extracted[key] = doc.text(actualSel);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
output.data = extracted;
|
|
46
|
+
}
|
|
47
|
+
const extractSet = new Set(extract || []);
|
|
48
|
+
if (extractSet.has('links') || extractSet.has('all')) {
|
|
49
|
+
const links = doc.links({ absolute: true });
|
|
50
|
+
output.links = links.slice(0, 100).map(l => ({
|
|
51
|
+
href: l.href,
|
|
52
|
+
text: l.text?.slice(0, 100),
|
|
53
|
+
rel: l.rel,
|
|
54
|
+
}));
|
|
55
|
+
output.linkCount = links.length;
|
|
56
|
+
}
|
|
57
|
+
if (extractSet.has('images') || extractSet.has('all')) {
|
|
58
|
+
const images = doc.images({ absolute: true });
|
|
59
|
+
output.images = images.slice(0, 50).map(img => ({
|
|
60
|
+
src: img.src,
|
|
61
|
+
alt: img.alt,
|
|
62
|
+
width: img.width,
|
|
63
|
+
height: img.height,
|
|
64
|
+
}));
|
|
65
|
+
output.imageCount = images.length;
|
|
66
|
+
}
|
|
67
|
+
if (extractSet.has('meta') || extractSet.has('all')) {
|
|
68
|
+
output.meta = doc.meta();
|
|
69
|
+
}
|
|
70
|
+
if (extractSet.has('og') || extractSet.has('all')) {
|
|
71
|
+
output.openGraph = doc.openGraph();
|
|
72
|
+
}
|
|
73
|
+
if (extractSet.has('twitter') || extractSet.has('all')) {
|
|
74
|
+
output.twitterCard = doc.twitterCard();
|
|
75
|
+
}
|
|
76
|
+
if (extractSet.has('jsonld') || extractSet.has('all')) {
|
|
77
|
+
output.jsonLd = doc.jsonLd();
|
|
78
|
+
}
|
|
79
|
+
if (extractSet.has('tables') || extractSet.has('all')) {
|
|
80
|
+
const tables = doc.tables();
|
|
81
|
+
output.tables = tables.slice(0, 10).map(t => ({
|
|
82
|
+
headers: t.headers,
|
|
83
|
+
rows: t.rows.slice(0, 50),
|
|
84
|
+
}));
|
|
85
|
+
output.tableCount = tables.length;
|
|
86
|
+
}
|
|
87
|
+
if (extractSet.has('forms') || extractSet.has('all')) {
|
|
88
|
+
output.forms = doc.forms();
|
|
89
|
+
}
|
|
90
|
+
if (extractSet.has('headings')) {
|
|
91
|
+
output.headings = {
|
|
92
|
+
h1: doc.texts('h1'),
|
|
93
|
+
h2: doc.texts('h2'),
|
|
94
|
+
h3: doc.texts('h3'),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
content: [{
|
|
99
|
+
type: 'text',
|
|
100
|
+
text: JSON.stringify(output, null, 2),
|
|
101
|
+
}],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
return {
|
|
106
|
+
content: [{
|
|
107
|
+
type: 'text',
|
|
108
|
+
text: `Scrape failed: ${error.message}`,
|
|
109
|
+
}],
|
|
110
|
+
isError: true,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export const scrapeTools = [
|
|
115
|
+
{
|
|
116
|
+
name: 'rek_scrape',
|
|
117
|
+
description: `Scrape a web page and extract data using CSS selectors.
|
|
118
|
+
|
|
119
|
+
Supports multiple extraction modes:
|
|
120
|
+
- Single selector: Extract elements matching one CSS selector
|
|
121
|
+
- Selector map: Extract multiple fields at once
|
|
122
|
+
- Built-in extractors: links, images, meta, og, twitter, jsonld, tables, forms, headings
|
|
123
|
+
|
|
124
|
+
Examples:
|
|
125
|
+
- Get all product titles: selector=".product-title"
|
|
126
|
+
- Extract multiple fields: selectors={"title":"h1","price":".price","desc":".description"}
|
|
127
|
+
- Get all links and images: extract=["links","images"]
|
|
128
|
+
- Full extraction: extract=["all"]`,
|
|
129
|
+
inputSchema: {
|
|
130
|
+
type: 'object',
|
|
131
|
+
properties: {
|
|
132
|
+
url: {
|
|
133
|
+
type: 'string',
|
|
134
|
+
description: 'URL to scrape',
|
|
135
|
+
},
|
|
136
|
+
selector: {
|
|
137
|
+
type: 'string',
|
|
138
|
+
description: 'Single CSS selector to extract elements (e.g., ".product-card", "article h2")',
|
|
139
|
+
},
|
|
140
|
+
selectors: {
|
|
141
|
+
type: 'object',
|
|
142
|
+
description: 'Map of field names to CSS selectors. Add [] suffix for multiple values (e.g., {"title":"h1","links[]":"a"})',
|
|
143
|
+
},
|
|
144
|
+
extract: {
|
|
145
|
+
type: 'array',
|
|
146
|
+
items: { type: 'string' },
|
|
147
|
+
description: 'Built-in extractors to run: links, images, meta, og, twitter, jsonld, tables, forms, headings, all',
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
required: ['url'],
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
];
|
|
154
|
+
export const scrapeToolHandlers = {
|
|
155
|
+
rek_scrape: scrapeUrl,
|
|
156
|
+
};
|
|
@@ -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
|
+
};
|
package/dist/mcp/tools/seo.js
CHANGED
|
@@ -160,8 +160,8 @@ async function seoAnalyze(args) {
|
|
|
160
160
|
}
|
|
161
161
|
async function seoSpiderCrawl(args) {
|
|
162
162
|
const url = String(args.url || '');
|
|
163
|
-
const maxPages = Number(args.maxPages) ||
|
|
164
|
-
const maxDepth = Number(args.maxDepth) ||
|
|
163
|
+
const maxPages = Number(args.maxPages) || 100;
|
|
164
|
+
const maxDepth = Number(args.maxDepth) || 5;
|
|
165
165
|
const concurrency = Number(args.concurrency) || 3;
|
|
166
166
|
if (!url) {
|
|
167
167
|
return {
|
|
@@ -377,12 +377,12 @@ Returns per-page scores and prioritized recommendations for improving overall si
|
|
|
377
377
|
maxPages: {
|
|
378
378
|
type: 'number',
|
|
379
379
|
description: 'Maximum pages to crawl',
|
|
380
|
-
default:
|
|
380
|
+
default: 100,
|
|
381
381
|
},
|
|
382
382
|
maxDepth: {
|
|
383
383
|
type: 'number',
|
|
384
384
|
description: 'Maximum link depth to follow',
|
|
385
|
-
default:
|
|
385
|
+
default: 5,
|
|
386
386
|
},
|
|
387
387
|
concurrency: {
|
|
388
388
|
type: 'number',
|
package/dist/scrape/spider.js
CHANGED
|
@@ -86,7 +86,7 @@ export class Spider {
|
|
|
86
86
|
pendingCount = 0;
|
|
87
87
|
constructor(options = {}) {
|
|
88
88
|
this.options = {
|
|
89
|
-
maxDepth: options.maxDepth ??
|
|
89
|
+
maxDepth: options.maxDepth ?? 5,
|
|
90
90
|
maxPages: options.maxPages ?? 100,
|
|
91
91
|
sameDomain: options.sameDomain ?? true,
|
|
92
92
|
concurrency: options.concurrency ?? 5,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AIResponse, AIStream, ChatMessage, AIProvider } from './ai.js';
|
|
2
|
+
export interface AIMemoryConfig {
|
|
3
|
+
maxPairs?: number;
|
|
4
|
+
systemPrompt?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface PresetAIConfig {
|
|
7
|
+
provider: AIProvider;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
model: string;
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
memory?: AIMemoryConfig;
|
|
12
|
+
organization?: string;
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
export interface ClientAI {
|
|
16
|
+
chat(prompt: string): Promise<AIResponse>;
|
|
17
|
+
chatStream(prompt: string): Promise<AIStream>;
|
|
18
|
+
prompt(prompt: string): Promise<AIResponse>;
|
|
19
|
+
promptStream(prompt: string): Promise<AIStream>;
|
|
20
|
+
clearMemory(): void;
|
|
21
|
+
getMemory(): readonly ChatMessage[];
|
|
22
|
+
setMemoryConfig(config: Partial<AIMemoryConfig>): void;
|
|
23
|
+
getMemoryConfig(): AIMemoryConfig;
|
|
24
|
+
readonly provider: AIProvider;
|
|
25
|
+
readonly model: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ClientOptionsWithAI {
|
|
28
|
+
_aiConfig?: PresetAIConfig;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|