browser-use 0.6.0 → 0.7.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/README.md +29 -18
- package/dist/actor/element.js +24 -3
- package/dist/actor/mouse.js +21 -3
- package/dist/actor/page.js +33 -11
- package/dist/agent/gif.js +28 -3
- package/dist/agent/message-manager/service.js +2 -22
- package/dist/agent/message-manager/utils.js +15 -2
- package/dist/agent/message-manager/views.d.ts +7 -7
- package/dist/agent/message-manager/views.js +1 -0
- package/dist/agent/prompts.d.ts +3 -0
- package/dist/agent/prompts.js +22 -12
- package/dist/agent/service.d.ts +9 -1
- package/dist/agent/service.js +215 -81
- package/dist/agent/system_prompt.md +12 -11
- package/dist/agent/system_prompt_anthropic_flash.md +6 -5
- package/dist/agent/system_prompt_no_thinking.md +12 -11
- package/dist/agent/views.d.ts +2 -0
- package/dist/agent/views.js +48 -36
- package/dist/browser/extensions.js +20 -10
- package/dist/browser/profile.d.ts +4 -0
- package/dist/browser/profile.js +107 -4
- package/dist/browser/session.d.ts +28 -1
- package/dist/browser/session.js +1436 -528
- package/dist/browser/watchdogs/default-action-watchdog.js +32 -3
- package/dist/browser/watchdogs/downloads-watchdog.d.ts +4 -0
- package/dist/browser/watchdogs/downloads-watchdog.js +105 -9
- package/dist/browser/watchdogs/har-recording-watchdog.d.ts +1 -0
- package/dist/browser/watchdogs/har-recording-watchdog.js +54 -2
- package/dist/browser/watchdogs/permissions-watchdog.d.ts +5 -0
- package/dist/browser/watchdogs/permissions-watchdog.js +106 -3
- package/dist/browser/watchdogs/recording-watchdog.d.ts +2 -0
- package/dist/browser/watchdogs/recording-watchdog.js +54 -2
- package/dist/browser/watchdogs/security-watchdog.d.ts +1 -0
- package/dist/browser/watchdogs/security-watchdog.js +47 -7
- package/dist/browser/watchdogs/storage-state-watchdog.d.ts +6 -0
- package/dist/browser/watchdogs/storage-state-watchdog.js +206 -14
- package/dist/cli.d.ts +13 -2
- package/dist/cli.js +188 -8
- package/dist/code-use/namespace.js +52 -7
- package/dist/code-use/notebook-export.js +18 -2
- package/dist/code-use/service.js +1 -0
- package/dist/config.js +27 -5
- package/dist/controller/action-timeout.d.ts +9 -0
- package/dist/controller/action-timeout.js +95 -0
- package/dist/controller/registry/service.d.ts +1 -0
- package/dist/controller/registry/service.js +28 -1
- package/dist/controller/registry/views.d.ts +2 -0
- package/dist/controller/registry/views.js +44 -17
- package/dist/controller/service.d.ts +2 -1
- package/dist/controller/service.js +494 -329
- package/dist/filesystem/file-system.js +38 -8
- package/dist/integrations/gmail/service.js +30 -6
- package/dist/llm/browser-use/chat.js +2 -2
- package/dist/llm/codex/auth.d.ts +118 -0
- package/dist/llm/codex/auth.js +599 -0
- package/dist/llm/codex/chat.d.ts +70 -0
- package/dist/llm/codex/chat.js +392 -0
- package/dist/llm/codex/index.d.ts +2 -0
- package/dist/llm/codex/index.js +2 -0
- package/dist/llm/google/chat.js +18 -1
- package/dist/logging-config.js +22 -11
- package/dist/mcp/client.d.ts +1 -0
- package/dist/mcp/client.js +12 -10
- package/dist/mcp/redaction.d.ts +3 -0
- package/dist/mcp/redaction.js +132 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +64 -22
- package/dist/observability.js +1 -1
- package/dist/screenshots/service.js +25 -2
- package/dist/skill-cli/direct.d.ts +4 -1
- package/dist/skill-cli/direct.js +260 -64
- package/dist/skill-cli/server.d.ts +1 -0
- package/dist/skill-cli/server.js +115 -25
- package/dist/skill-cli/tunnel.d.ts +1 -0
- package/dist/skill-cli/tunnel.js +16 -4
- package/dist/sync/auth.js +22 -9
- package/dist/telemetry/service.js +21 -2
- package/dist/telemetry/views.js +31 -8
- package/dist/tokens/custom-pricing.js +2 -2
- package/dist/tokens/openrouter-pricing.d.ts +11 -0
- package/dist/tokens/openrouter-pricing.js +102 -0
- package/dist/tokens/service.js +20 -16
- package/dist/utils.d.ts +3 -1
- package/dist/utils.js +4 -2
- package/package.json +75 -33
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
const REDACTED_VALUE = '<redacted>';
|
|
2
|
+
const isSensitiveMcpArgumentKey = (key) => {
|
|
3
|
+
const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
4
|
+
return [
|
|
5
|
+
'password',
|
|
6
|
+
'passwd',
|
|
7
|
+
'pwd',
|
|
8
|
+
'secret',
|
|
9
|
+
'token',
|
|
10
|
+
'apikey',
|
|
11
|
+
'accesskey',
|
|
12
|
+
'secretkey',
|
|
13
|
+
'credential',
|
|
14
|
+
'credentials',
|
|
15
|
+
'authorization',
|
|
16
|
+
'cookie',
|
|
17
|
+
'session',
|
|
18
|
+
].some((candidate) => normalized.includes(candidate));
|
|
19
|
+
};
|
|
20
|
+
const redactStringForMcpLog = (value) => {
|
|
21
|
+
const trimmed = value.trim();
|
|
22
|
+
if (/^(bearer|basic)\s+\S+/i.test(trimmed)) {
|
|
23
|
+
return REDACTED_VALUE;
|
|
24
|
+
}
|
|
25
|
+
if (trimmed.startsWith('data:')) {
|
|
26
|
+
return 'data:<redacted>';
|
|
27
|
+
}
|
|
28
|
+
if (trimmed.startsWith('blob:')) {
|
|
29
|
+
try {
|
|
30
|
+
const parsed = new URL(trimmed.slice('blob:'.length));
|
|
31
|
+
return parsed.origin && parsed.origin !== 'null'
|
|
32
|
+
? `blob:${parsed.origin}/<redacted>`
|
|
33
|
+
: 'blob:<redacted>';
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return 'blob:<redacted>';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const parsed = new URL(trimmed);
|
|
41
|
+
if (parsed.search || parsed.hash) {
|
|
42
|
+
return `${parsed.origin}${parsed.pathname}${parsed.search ? '?<redacted>' : ''}${parsed.hash ? '#<redacted>' : ''}`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Not a URL; keep regular non-sensitive strings unchanged.
|
|
47
|
+
}
|
|
48
|
+
const assignmentMatch = trimmed.match(/^([^:=\s][^:=]*?)([:=])\s*(.+)$/);
|
|
49
|
+
if (assignmentMatch) {
|
|
50
|
+
const [, key, separator, assignedValue] = assignmentMatch;
|
|
51
|
+
if (isSensitiveMcpArgumentKey(key)) {
|
|
52
|
+
return `${key}${separator}${REDACTED_VALUE}`;
|
|
53
|
+
}
|
|
54
|
+
const redactedAssignedValue = redactStringForMcpLog(assignedValue);
|
|
55
|
+
if (redactedAssignedValue !== assignedValue) {
|
|
56
|
+
return `${key}${separator}${redactedAssignedValue}`;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
60
|
+
};
|
|
61
|
+
const redactMcpProcessArgs = (args) => {
|
|
62
|
+
const redactedArgs = [];
|
|
63
|
+
let redactNext = false;
|
|
64
|
+
for (const arg of args) {
|
|
65
|
+
if (redactNext) {
|
|
66
|
+
redactedArgs.push(REDACTED_VALUE);
|
|
67
|
+
redactNext = false;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const assignmentIndex = arg.indexOf('=');
|
|
71
|
+
const argName = assignmentIndex >= 0 ? arg.slice(0, assignmentIndex) : arg;
|
|
72
|
+
if (argName.startsWith('-') && isSensitiveMcpArgumentKey(argName)) {
|
|
73
|
+
if (assignmentIndex >= 0) {
|
|
74
|
+
redactedArgs.push(`${argName}=${REDACTED_VALUE}`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
redactedArgs.push(arg);
|
|
78
|
+
redactNext = true;
|
|
79
|
+
}
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
redactedArgs.push(redactStringForMcpLog(arg));
|
|
83
|
+
}
|
|
84
|
+
return redactedArgs;
|
|
85
|
+
};
|
|
86
|
+
export const formatMcpCommandForLog = (command, args) => [command, ...redactMcpProcessArgs(args)].filter(Boolean).join(' ');
|
|
87
|
+
export const redactMcpLogMessage = (value) => {
|
|
88
|
+
let message = value instanceof Error
|
|
89
|
+
? value.message
|
|
90
|
+
: typeof value === 'string'
|
|
91
|
+
? value
|
|
92
|
+
: String(value);
|
|
93
|
+
message = message.replace(/https?:\/\/[^\s"'<>]+/gi, (match) => redactStringForMcpLog(match));
|
|
94
|
+
message = message.replace(/\bdata:\S+/gi, () => 'data:<redacted>');
|
|
95
|
+
message = message.replace(/\bblob:[^\s"'<>]+/gi, (match) => redactStringForMcpLog(match));
|
|
96
|
+
message = message.replace(/\b(Bearer|Basic)\s+[^\s,;]+/gi, (_match, scheme) => `${scheme} ${REDACTED_VALUE}`);
|
|
97
|
+
message = message.replace(/\b([A-Za-z0-9_.-]*(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|secret[_-]?key|credential|authorization|cookie|session)[A-Za-z0-9_.-]*)\s*([:=])\s*([^\s,;]+)/gi, (_match, key, separator) => `${key}${separator}${REDACTED_VALUE}`);
|
|
98
|
+
return message;
|
|
99
|
+
};
|
|
100
|
+
const redactMcpToolArgs = (value, seen = new WeakSet()) => {
|
|
101
|
+
if (value == null) {
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
if (typeof value === 'string') {
|
|
105
|
+
return redactStringForMcpLog(value);
|
|
106
|
+
}
|
|
107
|
+
if (typeof value !== 'object') {
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
if (seen.has(value)) {
|
|
111
|
+
return '[Circular]';
|
|
112
|
+
}
|
|
113
|
+
seen.add(value);
|
|
114
|
+
if (Array.isArray(value)) {
|
|
115
|
+
return value.map((item) => redactMcpToolArgs(item, seen));
|
|
116
|
+
}
|
|
117
|
+
const result = {};
|
|
118
|
+
for (const [key, item] of Object.entries(value)) {
|
|
119
|
+
result[key] = isSensitiveMcpArgumentKey(key)
|
|
120
|
+
? REDACTED_VALUE
|
|
121
|
+
: redactMcpToolArgs(item, seen);
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
};
|
|
125
|
+
export const formatMcpToolArgsForLog = (args) => {
|
|
126
|
+
try {
|
|
127
|
+
return JSON.stringify(redactMcpToolArgs(args));
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return '[unserializable]';
|
|
131
|
+
}
|
|
132
|
+
};
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -53,6 +53,7 @@ export declare class MCPServer {
|
|
|
53
53
|
private activeSessions;
|
|
54
54
|
private sessionTimeoutMinutes;
|
|
55
55
|
private sessionCleanupInterval;
|
|
56
|
+
private restoreConsoleRedirect;
|
|
56
57
|
constructor(name: string, version: string);
|
|
57
58
|
private resolvePath;
|
|
58
59
|
private getDefaultProfileConfig;
|
|
@@ -61,6 +62,7 @@ export declare class MCPServer {
|
|
|
61
62
|
private seedOpenAiApiKeyFromConfig;
|
|
62
63
|
private createLlmFromModelName;
|
|
63
64
|
private sanitizeProfileConfig;
|
|
65
|
+
private normalizeDomainList;
|
|
64
66
|
private buildDirectSessionProfile;
|
|
65
67
|
private buildRetryProfile;
|
|
66
68
|
private initializeLlmForDirectTools;
|
package/dist/mcp/server.js
CHANGED
|
@@ -42,15 +42,22 @@ import { zodSchemaToJsonSchema } from '../llm/schema.js';
|
|
|
42
42
|
import { productTelemetry } from '../telemetry/service.js';
|
|
43
43
|
import { MCPServerTelemetryEvent } from '../telemetry/views.js';
|
|
44
44
|
import { get_browser_use_version } from '../utils.js';
|
|
45
|
-
|
|
46
|
-
const originalLog = console.log;
|
|
47
|
-
const originalInfo = console.info;
|
|
48
|
-
const originalWarn = console.warn;
|
|
49
|
-
const originalError = console.error;
|
|
50
|
-
console.log = (...args) => console.error(...args);
|
|
51
|
-
console.info = (...args) => console.error(...args);
|
|
52
|
-
console.warn = (...args) => console.error(...args);
|
|
45
|
+
import { redactMcpLogMessage } from './redaction.js';
|
|
53
46
|
const logger = createLogger('browser_use.mcp.server');
|
|
47
|
+
const redirectConsoleToStderr = () => {
|
|
48
|
+
const originalLog = console.log;
|
|
49
|
+
const originalInfo = console.info;
|
|
50
|
+
const originalWarn = console.warn;
|
|
51
|
+
const writeToStderr = (...args) => console.error(...args);
|
|
52
|
+
console.log = writeToStderr;
|
|
53
|
+
console.info = writeToStderr;
|
|
54
|
+
console.warn = writeToStderr;
|
|
55
|
+
return () => {
|
|
56
|
+
console.log = originalLog;
|
|
57
|
+
console.info = originalInfo;
|
|
58
|
+
console.warn = originalWarn;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
54
61
|
export class MCPServer {
|
|
55
62
|
server;
|
|
56
63
|
tools = {};
|
|
@@ -68,6 +75,7 @@ export class MCPServer {
|
|
|
68
75
|
activeSessions = new Map();
|
|
69
76
|
sessionTimeoutMinutes = 10;
|
|
70
77
|
sessionCleanupInterval = null;
|
|
78
|
+
restoreConsoleRedirect = null;
|
|
71
79
|
constructor(name, version) {
|
|
72
80
|
this.server = new Server({
|
|
73
81
|
name,
|
|
@@ -130,6 +138,20 @@ export class MCPServer {
|
|
|
130
138
|
delete sanitized.created_at;
|
|
131
139
|
return sanitized;
|
|
132
140
|
}
|
|
141
|
+
normalizeDomainList(value) {
|
|
142
|
+
const entries = value instanceof Set
|
|
143
|
+
? Array.from(value)
|
|
144
|
+
: Array.isArray(value)
|
|
145
|
+
? value
|
|
146
|
+
: null;
|
|
147
|
+
if (!entries) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
const normalized = entries
|
|
151
|
+
.map((entry) => String(entry).trim())
|
|
152
|
+
.filter(Boolean);
|
|
153
|
+
return normalized.length > 0 ? normalized : null;
|
|
154
|
+
}
|
|
133
155
|
buildDirectSessionProfile(profileConfig) {
|
|
134
156
|
const merged = {
|
|
135
157
|
downloads_path: '~/Downloads/browser-use-mcp',
|
|
@@ -164,7 +186,11 @@ export class MCPServer {
|
|
|
164
186
|
const merged = {
|
|
165
187
|
...this.sanitizeProfileConfig(profileConfig),
|
|
166
188
|
};
|
|
167
|
-
|
|
189
|
+
const configuredAllowedDomains = this.normalizeDomainList(merged.allowed_domains);
|
|
190
|
+
if (configuredAllowedDomains) {
|
|
191
|
+
merged.allowed_domains = configuredAllowedDomains;
|
|
192
|
+
}
|
|
193
|
+
else if (allowedDomains !== undefined) {
|
|
168
194
|
merged.allowed_domains = allowedDomains;
|
|
169
195
|
}
|
|
170
196
|
if (merged.keep_alive == null) {
|
|
@@ -200,7 +226,7 @@ export class MCPServer {
|
|
|
200
226
|
this.llm = this.createLlmFromModelName(model, llmConfig);
|
|
201
227
|
}
|
|
202
228
|
catch (error) {
|
|
203
|
-
logger.debug(`Skipping MCP direct-tools LLM initialization for model "${model}": ${
|
|
229
|
+
logger.debug(`Skipping MCP direct-tools LLM initialization for model "${model}": ${redactMcpLogMessage(error)}`);
|
|
204
230
|
}
|
|
205
231
|
}
|
|
206
232
|
initializeFileSystem(profileConfig) {
|
|
@@ -314,7 +340,7 @@ export class MCPServer {
|
|
|
314
340
|
return {
|
|
315
341
|
session_id: sessionId,
|
|
316
342
|
closed: false,
|
|
317
|
-
message: `Failed to close session ${sessionId}: ${
|
|
343
|
+
message: `Failed to close session ${sessionId}: ${redactMcpLogMessage(error)}`,
|
|
318
344
|
};
|
|
319
345
|
}
|
|
320
346
|
}
|
|
@@ -350,7 +376,7 @@ export class MCPServer {
|
|
|
350
376
|
}
|
|
351
377
|
this.sessionCleanupInterval = setInterval(() => {
|
|
352
378
|
this.cleanupExpiredSessions().catch((error) => {
|
|
353
|
-
logger.warning(`MCP session cleanup failed: ${
|
|
379
|
+
logger.warning(`MCP session cleanup failed: ${redactMcpLogMessage(error)}`);
|
|
354
380
|
});
|
|
355
381
|
}, 120_000);
|
|
356
382
|
// Do not keep the Node process alive solely because of the cleanup loop.
|
|
@@ -448,7 +474,7 @@ export class MCPServer {
|
|
|
448
474
|
}
|
|
449
475
|
catch (error) {
|
|
450
476
|
this.errorCount++;
|
|
451
|
-
errorMsg =
|
|
477
|
+
errorMsg = redactMcpLogMessage(error);
|
|
452
478
|
logger.error(`Tool execution failed: ${errorMsg}`);
|
|
453
479
|
return {
|
|
454
480
|
content: [
|
|
@@ -594,8 +620,15 @@ export class MCPServer {
|
|
|
594
620
|
: null;
|
|
595
621
|
if (locator && typeof locator.click === 'function') {
|
|
596
622
|
const modifier = process.platform === 'darwin' ? 'Meta' : 'Control';
|
|
597
|
-
await
|
|
598
|
-
|
|
623
|
+
const pageBeforeClick = await browserSession.get_current_page();
|
|
624
|
+
try {
|
|
625
|
+
await locator.click({ modifiers: [modifier] });
|
|
626
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
627
|
+
}
|
|
628
|
+
finally {
|
|
629
|
+
const pageAfterClick = (await browserSession.get_current_page()) ?? pageBeforeClick;
|
|
630
|
+
await browserSession.validate_page_after_action(pageAfterClick);
|
|
631
|
+
}
|
|
599
632
|
return `Clicked element ${index} with ${modifier} key (new tab if supported)`;
|
|
600
633
|
}
|
|
601
634
|
// Fallback: if no href exists, perform a normal click.
|
|
@@ -726,7 +759,7 @@ export class MCPServer {
|
|
|
726
759
|
task: z.string(),
|
|
727
760
|
max_steps: z.number().int().optional().default(100),
|
|
728
761
|
model: z.string().optional(),
|
|
729
|
-
allowed_domains: z.array(z.string()).optional()
|
|
762
|
+
allowed_domains: z.array(z.string()).optional(),
|
|
730
763
|
use_vision: z.boolean().optional().default(true),
|
|
731
764
|
}), async (args) => {
|
|
732
765
|
const task = String(args?.task ?? '').trim();
|
|
@@ -736,11 +769,12 @@ export class MCPServer {
|
|
|
736
769
|
const requestedModel = typeof args?.model === 'string' ? args.model.trim() : '';
|
|
737
770
|
const maxSteps = Number(args?.max_steps ?? 100);
|
|
738
771
|
const useVision = Boolean(args?.use_vision ?? true);
|
|
739
|
-
const
|
|
772
|
+
const allowedDomainValues = Array.isArray(args?.allowed_domains)
|
|
740
773
|
? args.allowed_domains
|
|
741
774
|
.map((entry) => String(entry).trim())
|
|
742
775
|
.filter(Boolean)
|
|
743
776
|
: [];
|
|
777
|
+
const allowedDomains = allowedDomainValues.length > 0 ? allowedDomainValues : undefined;
|
|
744
778
|
const llmConfig = this.getDefaultLlmConfig();
|
|
745
779
|
const configuredModel = typeof llmConfig.model === 'string' && llmConfig.model.trim()
|
|
746
780
|
? llmConfig.model.trim()
|
|
@@ -751,7 +785,7 @@ export class MCPServer {
|
|
|
751
785
|
llm = this.createLlmFromModelName(llmModel, llmConfig);
|
|
752
786
|
}
|
|
753
787
|
catch (error) {
|
|
754
|
-
return `Error: Failed to initialize LLM "${llmModel}": ${
|
|
788
|
+
return `Error: Failed to initialize LLM "${llmModel}": ${redactMcpLogMessage(error)}`;
|
|
755
789
|
}
|
|
756
790
|
const profileConfig = this.getDefaultProfileConfig();
|
|
757
791
|
const profile = this.buildRetryProfile(profileConfig, allowedDomains);
|
|
@@ -769,8 +803,7 @@ export class MCPServer {
|
|
|
769
803
|
return this.formatRetryResult(history);
|
|
770
804
|
}
|
|
771
805
|
catch (error) {
|
|
772
|
-
|
|
773
|
-
return `Agent task failed: ${message}`;
|
|
806
|
+
return `Agent task failed: ${redactMcpLogMessage(error)}`;
|
|
774
807
|
}
|
|
775
808
|
finally {
|
|
776
809
|
await agent.close();
|
|
@@ -891,6 +924,7 @@ export class MCPServer {
|
|
|
891
924
|
logger.warning('MCP Server is already running');
|
|
892
925
|
return;
|
|
893
926
|
}
|
|
927
|
+
this.restoreConsoleRedirect ??= redirectConsoleToStderr();
|
|
894
928
|
// Capture telemetry for server start
|
|
895
929
|
productTelemetry.capture(new MCPServerTelemetryEvent({
|
|
896
930
|
version: get_browser_use_version(),
|
|
@@ -905,7 +939,9 @@ export class MCPServer {
|
|
|
905
939
|
}
|
|
906
940
|
catch (error) {
|
|
907
941
|
this.isRunning = false;
|
|
908
|
-
|
|
942
|
+
this.restoreConsoleRedirect?.();
|
|
943
|
+
this.restoreConsoleRedirect = null;
|
|
944
|
+
logger.error(`Failed to start MCP server: ${redactMcpLogMessage(error)}`);
|
|
909
945
|
throw error;
|
|
910
946
|
}
|
|
911
947
|
}
|
|
@@ -915,6 +951,8 @@ export class MCPServer {
|
|
|
915
951
|
async stop() {
|
|
916
952
|
if (!this.isRunning) {
|
|
917
953
|
logger.warning('MCP Server is not running');
|
|
954
|
+
this.restoreConsoleRedirect?.();
|
|
955
|
+
this.restoreConsoleRedirect = null;
|
|
918
956
|
return;
|
|
919
957
|
}
|
|
920
958
|
try {
|
|
@@ -943,7 +981,11 @@ export class MCPServer {
|
|
|
943
981
|
logger.info(`🔌 MCP Server stopped (uptime: ${Math.floor(stats.uptime)}s, executions: ${stats.executionCount}, success rate: ${(stats.successRate * 100).toFixed(1)}%)`);
|
|
944
982
|
}
|
|
945
983
|
catch (error) {
|
|
946
|
-
logger.error(`Error stopping MCP server: ${error}`);
|
|
984
|
+
logger.error(`Error stopping MCP server: ${redactMcpLogMessage(error)}`);
|
|
985
|
+
}
|
|
986
|
+
finally {
|
|
987
|
+
this.restoreConsoleRedirect?.();
|
|
988
|
+
this.restoreConsoleRedirect = null;
|
|
947
989
|
}
|
|
948
990
|
}
|
|
949
991
|
/**
|
package/dist/observability.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { config as loadEnv } from 'dotenv';
|
|
3
3
|
import { createLogger } from './logging-config.js';
|
|
4
|
-
loadEnv();
|
|
4
|
+
loadEnv({ quiet: true });
|
|
5
5
|
const require = createRequire(import.meta.url);
|
|
6
6
|
const logger = createLogger('browser_use.observability');
|
|
7
7
|
let lmnrObserve = null;
|
|
@@ -1,16 +1,39 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
const decodeBase64 = (data) => Buffer.from(data, 'base64');
|
|
4
|
+
const chmodPrivatePath = (targetPath, mode) => {
|
|
5
|
+
if (process.platform === 'win32') {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
fs.chmodSync(targetPath, mode);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
/* best effort */
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const createPrivateDirectory = (dirPath) => {
|
|
16
|
+
fs.mkdirSync(dirPath, { recursive: true, mode: 0o700 });
|
|
17
|
+
chmodPrivatePath(dirPath, 0o700);
|
|
18
|
+
};
|
|
19
|
+
const chmodPrivateFile = async (filePath) => {
|
|
20
|
+
if (process.platform !== 'win32') {
|
|
21
|
+
await fs.promises.chmod(filePath, 0o600);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
4
24
|
export class ScreenshotService {
|
|
5
25
|
screenshotsDir;
|
|
6
26
|
constructor(agentDirectory) {
|
|
7
27
|
this.screenshotsDir = path.join(agentDirectory, 'screenshots');
|
|
8
|
-
|
|
28
|
+
createPrivateDirectory(this.screenshotsDir);
|
|
9
29
|
}
|
|
10
30
|
async store_screenshot(screenshot_b64, step_number) {
|
|
11
31
|
const filename = `step_${step_number}.png`;
|
|
12
32
|
const filepath = path.join(this.screenshotsDir, filename);
|
|
13
|
-
await fs.promises.writeFile(filepath, decodeBase64(screenshot_b64)
|
|
33
|
+
await fs.promises.writeFile(filepath, decodeBase64(screenshot_b64), {
|
|
34
|
+
mode: 0o600,
|
|
35
|
+
});
|
|
36
|
+
await chmodPrivateFile(filepath);
|
|
14
37
|
return filepath;
|
|
15
38
|
}
|
|
16
39
|
async get_screenshot(screenshot_path) {
|
|
@@ -59,11 +59,14 @@ interface DirectSessionLike {
|
|
|
59
59
|
go_forward?: () => Promise<unknown>;
|
|
60
60
|
get_page_html?: () => Promise<string>;
|
|
61
61
|
execute_javascript?: (script: string) => Promise<unknown>;
|
|
62
|
+
validate_page_after_action?: (page: any) => Promise<unknown>;
|
|
62
63
|
switch_to_tab?: (identifier: number | string) => Promise<unknown>;
|
|
63
64
|
close_tab?: (identifier: number | string) => Promise<unknown>;
|
|
64
65
|
select_dropdown_option?: (node: any, value: string) => Promise<unknown>;
|
|
65
66
|
wait_for_element?: (selector: string, timeout: number) => Promise<unknown>;
|
|
66
|
-
get_cookies?: (
|
|
67
|
+
get_cookies?: (options?: {
|
|
68
|
+
include_blocked?: boolean;
|
|
69
|
+
}) => Promise<any[]>;
|
|
67
70
|
}
|
|
68
71
|
export interface DirectCliEnvironment {
|
|
69
72
|
state_file?: string;
|