@darbotlabs/darbot-browser-mcp 0.2.0 → 1.3.0
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/LICENSE +1 -1
- package/README.md +222 -161
- package/cli.js +1 -1
- package/config.d.ts +77 -1
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/lib/ai/context.js +150 -0
- package/lib/ai/guardrails.js +382 -0
- package/lib/ai/integration.js +397 -0
- package/lib/ai/intent.js +237 -0
- package/lib/ai/manualPromise.js +111 -0
- package/lib/ai/memory.js +273 -0
- package/lib/ai/ml-scorer.js +265 -0
- package/lib/ai/orchestrator-tools.js +292 -0
- package/lib/ai/orchestrator.js +473 -0
- package/lib/ai/planner.js +300 -0
- package/lib/ai/reporter.js +493 -0
- package/lib/ai/workflow.js +407 -0
- package/lib/auth/apiKeyAuth.js +46 -0
- package/lib/auth/entraAuth.js +110 -0
- package/lib/auth/entraJwtVerifier.js +117 -0
- package/lib/auth/index.js +210 -0
- package/lib/auth/managedIdentityAuth.js +175 -0
- package/lib/auth/mcpOAuthProvider.js +186 -0
- package/lib/auth/tunnelAuth.js +120 -0
- package/lib/browserContextFactory.js +1 -1
- package/lib/browserServer.js +1 -1
- package/lib/cdpRelay.js +2 -2
- package/lib/common.js +68 -0
- package/lib/config.js +62 -3
- package/lib/connection.js +1 -1
- package/lib/context.js +1 -1
- package/lib/fileUtils.js +1 -1
- package/lib/guardrails.js +382 -0
- package/lib/health.js +178 -0
- package/lib/httpServer.js +1 -1
- package/lib/index.js +1 -1
- package/lib/javascript.js +1 -1
- package/lib/manualPromise.js +1 -1
- package/lib/memory.js +273 -0
- package/lib/openapi.js +373 -0
- package/lib/orchestrator.js +473 -0
- package/lib/package.js +1 -1
- package/lib/pageSnapshot.js +17 -2
- package/lib/planner.js +302 -0
- package/lib/program.js +17 -5
- package/lib/reporter.js +493 -0
- package/lib/resources/resource.js +1 -1
- package/lib/server.js +5 -3
- package/lib/tab.js +1 -1
- package/lib/tools/ai-native.js +298 -0
- package/lib/tools/autonomous.js +147 -0
- package/lib/tools/clock.js +183 -0
- package/lib/tools/common.js +1 -1
- package/lib/tools/console.js +1 -1
- package/lib/tools/diagnostics.js +132 -0
- package/lib/tools/dialogs.js +1 -1
- package/lib/tools/emulation.js +155 -0
- package/lib/tools/files.js +1 -1
- package/lib/tools/install.js +1 -1
- package/lib/tools/keyboard.js +1 -1
- package/lib/tools/navigate.js +1 -1
- package/lib/tools/network.js +1 -1
- package/lib/tools/pageSnapshot.js +58 -0
- package/lib/tools/pdf.js +1 -1
- package/lib/tools/profiles.js +76 -25
- package/lib/tools/screenshot.js +1 -1
- package/lib/tools/scroll.js +93 -0
- package/lib/tools/snapshot.js +1 -1
- package/lib/tools/storage.js +328 -0
- package/lib/tools/tab.js +16 -0
- package/lib/tools/tabs.js +1 -1
- package/lib/tools/testing.js +1 -1
- package/lib/tools/tool.js +1 -1
- package/lib/tools/utils.js +1 -1
- package/lib/tools/vision.js +1 -1
- package/lib/tools/wait.js +1 -1
- package/lib/tools.js +22 -1
- package/lib/transport.js +251 -31
- package/package.json +28 -22
package/lib/health.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) DarbotLabs.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { packageJSON } from './package.js';
|
|
17
|
+
/**
|
|
18
|
+
* Health check service for monitoring system status
|
|
19
|
+
*/
|
|
20
|
+
export class HealthCheckService {
|
|
21
|
+
checks = new Map();
|
|
22
|
+
constructor() {
|
|
23
|
+
this.registerDefaultChecks();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Registers a health check function
|
|
27
|
+
*/
|
|
28
|
+
registerCheck(name, checkFn) {
|
|
29
|
+
this.checks.set(name, checkFn);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Runs all registered health checks
|
|
33
|
+
*/
|
|
34
|
+
async runChecks() {
|
|
35
|
+
const timestamp = new Date().toISOString();
|
|
36
|
+
const checks = [];
|
|
37
|
+
for (const [name, checkFn] of this.checks) {
|
|
38
|
+
try {
|
|
39
|
+
const startTime = Date.now();
|
|
40
|
+
const result = await checkFn();
|
|
41
|
+
result.duration = Date.now() - startTime;
|
|
42
|
+
checks.push(result);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
checks.push({
|
|
46
|
+
name,
|
|
47
|
+
status: 'fail',
|
|
48
|
+
duration: 0,
|
|
49
|
+
details: { error: error instanceof Error ? error.message : 'Unknown error' }
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const overallStatus = this.determineOverallStatus(checks);
|
|
54
|
+
return {
|
|
55
|
+
status: overallStatus,
|
|
56
|
+
timestamp,
|
|
57
|
+
version: packageJSON.version,
|
|
58
|
+
checks
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* HTTP handler for health check endpoint
|
|
63
|
+
*/
|
|
64
|
+
async handleHealthCheck(req, res) {
|
|
65
|
+
try {
|
|
66
|
+
const health = await this.runChecks();
|
|
67
|
+
const statusCode = health.status === 'healthy' ? 200 :
|
|
68
|
+
health.status === 'degraded' ? 200 : 503;
|
|
69
|
+
res.statusCode = statusCode;
|
|
70
|
+
res.setHeader('Content-Type', 'application/json');
|
|
71
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
72
|
+
res.end(JSON.stringify(health, null, 2));
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
res.statusCode = 500;
|
|
76
|
+
res.setHeader('Content-Type', 'application/json');
|
|
77
|
+
res.end(JSON.stringify({
|
|
78
|
+
status: 'unhealthy',
|
|
79
|
+
timestamp: new Date().toISOString(),
|
|
80
|
+
error: error instanceof Error ? error.message : 'Health check failed'
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Lightweight readiness probe for Kubernetes/Azure
|
|
86
|
+
*/
|
|
87
|
+
async handleReadinessCheck(req, res) {
|
|
88
|
+
try {
|
|
89
|
+
// Quick check - just verify service is responding
|
|
90
|
+
res.statusCode = 200;
|
|
91
|
+
res.setHeader('Content-Type', 'text/plain');
|
|
92
|
+
res.end('OK');
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
res.statusCode = 503;
|
|
96
|
+
res.setHeader('Content-Type', 'text/plain');
|
|
97
|
+
res.end('Service Unavailable');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Liveness probe for Kubernetes/Azure
|
|
102
|
+
*/
|
|
103
|
+
async handleLivenessCheck(req, res) {
|
|
104
|
+
try {
|
|
105
|
+
// Basic liveness check
|
|
106
|
+
res.statusCode = 200;
|
|
107
|
+
res.setHeader('Content-Type', 'text/plain');
|
|
108
|
+
res.end('Alive');
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
res.statusCode = 503;
|
|
112
|
+
res.setHeader('Content-Type', 'text/plain');
|
|
113
|
+
res.end('Dead');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
registerDefaultChecks() {
|
|
117
|
+
// Memory usage check
|
|
118
|
+
this.registerCheck('memory', async () => {
|
|
119
|
+
const memUsage = process.memoryUsage();
|
|
120
|
+
const heapUsedMB = Math.round(memUsage.heapUsed / 1024 / 1024);
|
|
121
|
+
const heapTotalMB = Math.round(memUsage.heapTotal / 1024 / 1024);
|
|
122
|
+
// Warn if heap usage > 95%, fail if > 98% (relaxed for Playwright workloads)
|
|
123
|
+
const usagePercent = (heapUsedMB / heapTotalMB) * 100;
|
|
124
|
+
const status = usagePercent > 98 ? 'fail' : usagePercent > 95 ? 'warn' : 'pass';
|
|
125
|
+
return {
|
|
126
|
+
name: 'memory',
|
|
127
|
+
status,
|
|
128
|
+
duration: 0,
|
|
129
|
+
details: {
|
|
130
|
+
heapUsedMB,
|
|
131
|
+
heapTotalMB,
|
|
132
|
+
usagePercent: Math.round(usagePercent)
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
// Process uptime check
|
|
137
|
+
this.registerCheck('uptime', async () => {
|
|
138
|
+
const uptimeSeconds = process.uptime();
|
|
139
|
+
return {
|
|
140
|
+
name: 'uptime',
|
|
141
|
+
status: 'pass',
|
|
142
|
+
duration: 0,
|
|
143
|
+
details: {
|
|
144
|
+
uptimeSeconds: Math.round(uptimeSeconds),
|
|
145
|
+
uptimeHours: Math.round(uptimeSeconds / 3600 * 100) / 100
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
// Node.js version check
|
|
150
|
+
this.registerCheck('runtime', async () => {
|
|
151
|
+
return {
|
|
152
|
+
name: 'runtime',
|
|
153
|
+
status: 'pass',
|
|
154
|
+
duration: 0,
|
|
155
|
+
details: {
|
|
156
|
+
nodeVersion: process.version,
|
|
157
|
+
platform: process.platform,
|
|
158
|
+
arch: process.arch
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
determineOverallStatus(checks) {
|
|
164
|
+
const hasFailures = checks.some(check => check.status === 'fail');
|
|
165
|
+
const hasWarnings = checks.some(check => check.status === 'warn');
|
|
166
|
+
if (hasFailures)
|
|
167
|
+
return 'unhealthy';
|
|
168
|
+
if (hasWarnings)
|
|
169
|
+
return 'degraded';
|
|
170
|
+
return 'healthy';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Creates a health check service with default checks
|
|
175
|
+
*/
|
|
176
|
+
export function createHealthCheckService() {
|
|
177
|
+
return new HealthCheckService();
|
|
178
|
+
}
|
package/lib/httpServer.js
CHANGED
package/lib/index.js
CHANGED
package/lib/javascript.js
CHANGED
package/lib/manualPromise.js
CHANGED
package/lib/memory.js
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) DarbotLabs.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import crypto from 'node:crypto';
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import debug from 'debug';
|
|
20
|
+
const log = debug('darbot:memory');
|
|
21
|
+
/**
|
|
22
|
+
* Local file-based memory storage implementation
|
|
23
|
+
*/
|
|
24
|
+
export class LocalMemoryStorage {
|
|
25
|
+
storagePath;
|
|
26
|
+
maxStates;
|
|
27
|
+
constructor(config = {}) {
|
|
28
|
+
this.storagePath = config.storagePath || path.join(process.cwd(), '.darbot', 'memory');
|
|
29
|
+
this.maxStates = config.maxStates || 1000;
|
|
30
|
+
this.ensureStorageDirectory();
|
|
31
|
+
}
|
|
32
|
+
ensureStorageDirectory() {
|
|
33
|
+
if (!fs.existsSync(this.storagePath))
|
|
34
|
+
fs.mkdirSync(this.storagePath, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
getStatePath(stateHash) {
|
|
37
|
+
return path.join(this.storagePath, `${stateHash}.json`);
|
|
38
|
+
}
|
|
39
|
+
async storeState(state) {
|
|
40
|
+
try {
|
|
41
|
+
const statePath = this.getStatePath(state.stateHash);
|
|
42
|
+
await fs.promises.writeFile(statePath, JSON.stringify(state, null, 2));
|
|
43
|
+
log('Stored state:', state.stateHash, state.url);
|
|
44
|
+
// Clean up old states if we exceed the limit
|
|
45
|
+
await this.cleanupOldStates();
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
log('Error storing state:', error);
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async getState(stateHash) {
|
|
53
|
+
try {
|
|
54
|
+
const statePath = this.getStatePath(stateHash);
|
|
55
|
+
if (!fs.existsSync(statePath))
|
|
56
|
+
return null;
|
|
57
|
+
const data = await fs.promises.readFile(statePath, 'utf-8');
|
|
58
|
+
return JSON.parse(data);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
log('Error reading state:', error);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async hasState(stateHash) {
|
|
66
|
+
const statePath = this.getStatePath(stateHash);
|
|
67
|
+
return fs.existsSync(statePath);
|
|
68
|
+
}
|
|
69
|
+
async getAllStates() {
|
|
70
|
+
try {
|
|
71
|
+
const files = await fs.promises.readdir(this.storagePath);
|
|
72
|
+
const states = [];
|
|
73
|
+
for (const file of files) {
|
|
74
|
+
if (file.endsWith('.json')) {
|
|
75
|
+
const filePath = path.join(this.storagePath, file);
|
|
76
|
+
try {
|
|
77
|
+
const data = await fs.promises.readFile(filePath, 'utf-8');
|
|
78
|
+
states.push(JSON.parse(data));
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
log('Error reading state file:', file, error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return states.sort((a, b) => a.timestamp - b.timestamp);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
log('Error reading states:', error);
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async getUnvisitedLinks() {
|
|
93
|
+
const states = await this.getAllStates();
|
|
94
|
+
const visited = new Set(states.filter(s => s.visited).map(s => s.url));
|
|
95
|
+
const allLinks = new Set();
|
|
96
|
+
states.forEach(state => {
|
|
97
|
+
state.links.forEach(link => {
|
|
98
|
+
if (!visited.has(link))
|
|
99
|
+
allLinks.add(link);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
return Array.from(allLinks);
|
|
103
|
+
}
|
|
104
|
+
async clear() {
|
|
105
|
+
try {
|
|
106
|
+
const files = await fs.promises.readdir(this.storagePath);
|
|
107
|
+
await Promise.all(files.map(file => fs.promises.unlink(path.join(this.storagePath, file))));
|
|
108
|
+
log('Cleared memory storage');
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
log('Error clearing storage:', error);
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async cleanupOldStates() {
|
|
116
|
+
const states = await this.getAllStates();
|
|
117
|
+
if (states.length <= this.maxStates)
|
|
118
|
+
return;
|
|
119
|
+
// Remove oldest states
|
|
120
|
+
const toRemove = states.slice(0, states.length - this.maxStates);
|
|
121
|
+
await Promise.all(toRemove.map(state => fs.promises.unlink(this.getStatePath(state.stateHash)).catch(() => { })));
|
|
122
|
+
log(`Cleaned up ${toRemove.length} old states`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Darbot Memory MCP connector (placeholder for future implementation)
|
|
127
|
+
*/
|
|
128
|
+
export class DarbotMemoryStorage {
|
|
129
|
+
constructor(config = {}) {
|
|
130
|
+
// TODO: Implement darbot-memory-mcp integration
|
|
131
|
+
log('Darbot Memory MCP connector not yet implemented, falling back to local storage');
|
|
132
|
+
}
|
|
133
|
+
async storeState(state) {
|
|
134
|
+
// TODO: Send to darbot-memory-mcp server
|
|
135
|
+
throw new Error('Darbot Memory MCP connector not yet implemented');
|
|
136
|
+
}
|
|
137
|
+
async getState(stateHash) {
|
|
138
|
+
// TODO: Query darbot-memory-mcp server
|
|
139
|
+
throw new Error('Darbot Memory MCP connector not yet implemented');
|
|
140
|
+
}
|
|
141
|
+
async hasState(stateHash) {
|
|
142
|
+
// TODO: Check darbot-memory-mcp server
|
|
143
|
+
throw new Error('Darbot Memory MCP connector not yet implemented');
|
|
144
|
+
}
|
|
145
|
+
async getAllStates() {
|
|
146
|
+
// TODO: Fetch from darbot-memory-mcp server
|
|
147
|
+
throw new Error('Darbot Memory MCP connector not yet implemented');
|
|
148
|
+
}
|
|
149
|
+
async getUnvisitedLinks() {
|
|
150
|
+
// TODO: Query darbot-memory-mcp server
|
|
151
|
+
throw new Error('Darbot Memory MCP connector not yet implemented');
|
|
152
|
+
}
|
|
153
|
+
async clear() {
|
|
154
|
+
// TODO: Clear darbot-memory-mcp storage
|
|
155
|
+
throw new Error('Darbot Memory MCP connector not yet implemented');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Memory manager with optional darbot-memory-mcp integration
|
|
160
|
+
*/
|
|
161
|
+
export class MemoryManager {
|
|
162
|
+
storage;
|
|
163
|
+
config;
|
|
164
|
+
constructor(config = { enabled: true }) {
|
|
165
|
+
this.config = config;
|
|
166
|
+
if (!config.enabled) {
|
|
167
|
+
this.storage = new LocalMemoryStorage(); // Dummy storage that won't be used
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
switch (config.connector) {
|
|
171
|
+
case 'darbot-memory-mcp':
|
|
172
|
+
try {
|
|
173
|
+
this.storage = new DarbotMemoryStorage();
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
log('Failed to initialize darbot-memory-mcp connector, falling back to local storage:', error);
|
|
177
|
+
this.storage = new LocalMemoryStorage({
|
|
178
|
+
storagePath: config.storagePath,
|
|
179
|
+
maxStates: config.maxStates
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
case 'local':
|
|
184
|
+
default:
|
|
185
|
+
this.storage = new LocalMemoryStorage({
|
|
186
|
+
storagePath: config.storagePath,
|
|
187
|
+
maxStates: config.maxStates
|
|
188
|
+
});
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Generate a hash for the current page state
|
|
194
|
+
*/
|
|
195
|
+
static stateHash(domSnapshot) {
|
|
196
|
+
return crypto.createHash('sha256').update(domSnapshot).digest('hex').substring(0, 16);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Store a crawl state with screenshot
|
|
200
|
+
*/
|
|
201
|
+
async storeState(url, title, domSnapshot, screenshot, links = []) {
|
|
202
|
+
if (!this.config.enabled)
|
|
203
|
+
return '';
|
|
204
|
+
const stateHash = MemoryManager.stateHash(domSnapshot);
|
|
205
|
+
let screenshotPath;
|
|
206
|
+
// Save screenshot if provided
|
|
207
|
+
if (screenshot) {
|
|
208
|
+
const screenshotDir = path.join(process.cwd(), '.darbot', 'screenshots');
|
|
209
|
+
if (!fs.existsSync(screenshotDir))
|
|
210
|
+
fs.mkdirSync(screenshotDir, { recursive: true });
|
|
211
|
+
screenshotPath = path.join(screenshotDir, `${stateHash}.png`);
|
|
212
|
+
await fs.promises.writeFile(screenshotPath, screenshot);
|
|
213
|
+
}
|
|
214
|
+
const state = {
|
|
215
|
+
url,
|
|
216
|
+
title,
|
|
217
|
+
stateHash,
|
|
218
|
+
timestamp: Date.now(),
|
|
219
|
+
screenshot: screenshotPath,
|
|
220
|
+
links,
|
|
221
|
+
visited: true
|
|
222
|
+
};
|
|
223
|
+
await this.storage.storeState(state);
|
|
224
|
+
return stateHash;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Check if we've seen this state before
|
|
228
|
+
*/
|
|
229
|
+
async hasState(domSnapshot) {
|
|
230
|
+
if (!this.config.enabled)
|
|
231
|
+
return false;
|
|
232
|
+
const stateHash = MemoryManager.stateHash(domSnapshot);
|
|
233
|
+
return await this.storage.hasState(stateHash);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Get a stored state by hash
|
|
237
|
+
*/
|
|
238
|
+
async getState(stateHash) {
|
|
239
|
+
if (!this.config.enabled)
|
|
240
|
+
return null;
|
|
241
|
+
return await this.storage.getState(stateHash);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Get all stored states
|
|
245
|
+
*/
|
|
246
|
+
async getAllStates() {
|
|
247
|
+
if (!this.config.enabled)
|
|
248
|
+
return [];
|
|
249
|
+
return await this.storage.getAllStates();
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Get unvisited links for BFS crawling
|
|
253
|
+
*/
|
|
254
|
+
async getUnvisitedLinks() {
|
|
255
|
+
if (!this.config.enabled)
|
|
256
|
+
return [];
|
|
257
|
+
return await this.storage.getUnvisitedLinks();
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Clear all stored states
|
|
261
|
+
*/
|
|
262
|
+
async clear() {
|
|
263
|
+
if (!this.config.enabled)
|
|
264
|
+
return;
|
|
265
|
+
await this.storage.clear();
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Check if memory is enabled
|
|
269
|
+
*/
|
|
270
|
+
get enabled() {
|
|
271
|
+
return this.config.enabled;
|
|
272
|
+
}
|
|
273
|
+
}
|