@darbotlabs/darbot-browser-mcp 0.1.1 → 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.
Files changed (80) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +249 -158
  3. package/cli.js +1 -1
  4. package/config.d.ts +77 -1
  5. package/index.d.ts +1 -1
  6. package/index.js +1 -1
  7. package/lib/ai/context.js +150 -0
  8. package/lib/ai/guardrails.js +382 -0
  9. package/lib/ai/integration.js +397 -0
  10. package/lib/ai/intent.js +237 -0
  11. package/lib/ai/manualPromise.js +111 -0
  12. package/lib/ai/memory.js +273 -0
  13. package/lib/ai/ml-scorer.js +265 -0
  14. package/lib/ai/orchestrator-tools.js +292 -0
  15. package/lib/ai/orchestrator.js +473 -0
  16. package/lib/ai/planner.js +300 -0
  17. package/lib/ai/reporter.js +493 -0
  18. package/lib/ai/workflow.js +407 -0
  19. package/lib/auth/apiKeyAuth.js +46 -0
  20. package/lib/auth/entraAuth.js +110 -0
  21. package/lib/auth/entraJwtVerifier.js +117 -0
  22. package/lib/auth/index.js +210 -0
  23. package/lib/auth/managedIdentityAuth.js +175 -0
  24. package/lib/auth/mcpOAuthProvider.js +186 -0
  25. package/lib/auth/tunnelAuth.js +120 -0
  26. package/lib/browserContextFactory.js +1 -1
  27. package/lib/browserServer.js +1 -1
  28. package/lib/cdpRelay.js +2 -2
  29. package/lib/common.js +68 -0
  30. package/lib/config.js +62 -3
  31. package/lib/connection.js +1 -1
  32. package/lib/context.js +1 -1
  33. package/lib/fileUtils.js +1 -1
  34. package/lib/guardrails.js +382 -0
  35. package/lib/health.js +178 -0
  36. package/lib/httpServer.js +1 -1
  37. package/lib/index.js +1 -1
  38. package/lib/javascript.js +1 -1
  39. package/lib/manualPromise.js +1 -1
  40. package/lib/memory.js +273 -0
  41. package/lib/openapi.js +373 -0
  42. package/lib/orchestrator.js +473 -0
  43. package/lib/package.js +1 -1
  44. package/lib/pageSnapshot.js +17 -2
  45. package/lib/planner.js +302 -0
  46. package/lib/program.js +17 -5
  47. package/lib/reporter.js +493 -0
  48. package/lib/resources/resource.js +1 -1
  49. package/lib/server.js +5 -3
  50. package/lib/tab.js +1 -1
  51. package/lib/tools/ai-native.js +298 -0
  52. package/lib/tools/autonomous.js +147 -0
  53. package/lib/tools/clock.js +183 -0
  54. package/lib/tools/common.js +1 -1
  55. package/lib/tools/console.js +1 -1
  56. package/lib/tools/diagnostics.js +132 -0
  57. package/lib/tools/dialogs.js +1 -1
  58. package/lib/tools/emulation.js +155 -0
  59. package/lib/tools/files.js +1 -1
  60. package/lib/tools/install.js +1 -1
  61. package/lib/tools/keyboard.js +1 -1
  62. package/lib/tools/navigate.js +1 -1
  63. package/lib/tools/network.js +1 -1
  64. package/lib/tools/pageSnapshot.js +58 -0
  65. package/lib/tools/pdf.js +1 -1
  66. package/lib/tools/profiles.js +76 -25
  67. package/lib/tools/screenshot.js +1 -1
  68. package/lib/tools/scroll.js +93 -0
  69. package/lib/tools/snapshot.js +1 -1
  70. package/lib/tools/storage.js +328 -0
  71. package/lib/tools/tab.js +16 -0
  72. package/lib/tools/tabs.js +1 -1
  73. package/lib/tools/testing.js +1 -1
  74. package/lib/tools/tool.js +1 -1
  75. package/lib/tools/utils.js +1 -1
  76. package/lib/tools/vision.js +1 -1
  77. package/lib/tools/wait.js +1 -1
  78. package/lib/tools.js +22 -1
  79. package/lib/transport.js +251 -31
  80. package/package.json +54 -21
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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Microsoft Corporation.
2
+ * Copyright (c) DarbotLabs.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Microsoft Corporation.
2
+ * Copyright (c) DarbotLabs.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
package/lib/javascript.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Microsoft Corporation.
2
+ * Copyright (c) DarbotLabs.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Microsoft Corporation.
2
+ * Copyright (c) DarbotLabs.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
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
+ }