browser-use 0.6.1 → 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 +24 -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 +204 -79
- 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 +187 -7
- 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 +26 -4
- 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/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/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 +3 -1
- package/package.json +68 -27
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
import { ModelProviderError, ModelRateLimitError } from '../exceptions.js';
|
|
3
|
+
import { ResponsesAPIMessageSerializer, } from '../openai/responses-serializer.js';
|
|
4
|
+
import { SchemaOptimizer, zodSchemaToJsonSchema } from '../schema.js';
|
|
5
|
+
import { ChatInvokeCompletion } from '../views.js';
|
|
6
|
+
import { CodexAuthError, DEFAULT_CODEX_BASE_URL, getCodexCloudflareHeaders, resolveCodexRuntimeCredentials, } from './auth.js';
|
|
7
|
+
const DEFAULT_CODEX_INSTRUCTIONS = 'You are a helpful assistant.';
|
|
8
|
+
const isCodexBackendURL = (baseURL) => {
|
|
9
|
+
try {
|
|
10
|
+
const parsed = new URL(baseURL);
|
|
11
|
+
return (parsed.hostname === 'chatgpt.com' &&
|
|
12
|
+
parsed.pathname.replace(/\/+$/, '') === '/backend-api/codex');
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
export class ChatCodex {
|
|
19
|
+
model;
|
|
20
|
+
provider = 'codex';
|
|
21
|
+
apiKey;
|
|
22
|
+
baseURL;
|
|
23
|
+
timeout;
|
|
24
|
+
maxRetries;
|
|
25
|
+
defaultHeaders;
|
|
26
|
+
defaultQuery;
|
|
27
|
+
fetchImplementation;
|
|
28
|
+
fetchOptions;
|
|
29
|
+
reasoningEffort;
|
|
30
|
+
maxCompletionTokens;
|
|
31
|
+
topP;
|
|
32
|
+
seed;
|
|
33
|
+
serviceTier;
|
|
34
|
+
include;
|
|
35
|
+
addSchemaToSystemPrompt;
|
|
36
|
+
dontForceStructuredOutput;
|
|
37
|
+
removeMinItemsFromSchema;
|
|
38
|
+
removeDefaultsFromSchema;
|
|
39
|
+
configDir;
|
|
40
|
+
authStorePath;
|
|
41
|
+
refreshSkewSeconds;
|
|
42
|
+
constructor(options = {}) {
|
|
43
|
+
const { model = process.env.BROWSER_USE_CODEX_MODEL ?? 'gpt-5.5', apiKey = process.env.BROWSER_USE_CODEX_ACCESS_TOKEN ?? null, baseURL = process.env.BROWSER_USE_CODEX_BASE_URL ??
|
|
44
|
+
DEFAULT_CODEX_BASE_URL, timeout = null, maxRetries = 2, defaultHeaders = null, defaultQuery = null, fetchImplementation, fetchOptions = null, reasoningEffort = 'low', maxCompletionTokens = 4096, topP = null, seed = null, serviceTier = null, include = null, addSchemaToSystemPrompt = false, dontForceStructuredOutput = false, removeMinItemsFromSchema = false, removeDefaultsFromSchema = false, configDir = null, authStorePath = null, refreshSkewSeconds, } = options;
|
|
45
|
+
this.model = model;
|
|
46
|
+
this.apiKey = apiKey?.trim() || null;
|
|
47
|
+
this.baseURL =
|
|
48
|
+
(baseURL ?? '').trim().replace(/\/+$/, '') || DEFAULT_CODEX_BASE_URL;
|
|
49
|
+
this.timeout = timeout;
|
|
50
|
+
this.maxRetries = maxRetries;
|
|
51
|
+
this.defaultHeaders = defaultHeaders;
|
|
52
|
+
this.defaultQuery = defaultQuery;
|
|
53
|
+
this.fetchImplementation = fetchImplementation;
|
|
54
|
+
this.fetchOptions = fetchOptions;
|
|
55
|
+
this.reasoningEffort = reasoningEffort;
|
|
56
|
+
this.maxCompletionTokens = maxCompletionTokens;
|
|
57
|
+
this.topP = topP;
|
|
58
|
+
this.seed = seed;
|
|
59
|
+
this.serviceTier = serviceTier;
|
|
60
|
+
this.include = include ? [...include] : null;
|
|
61
|
+
this.addSchemaToSystemPrompt = addSchemaToSystemPrompt;
|
|
62
|
+
this.dontForceStructuredOutput = dontForceStructuredOutput;
|
|
63
|
+
this.removeMinItemsFromSchema = removeMinItemsFromSchema;
|
|
64
|
+
this.removeDefaultsFromSchema = removeDefaultsFromSchema;
|
|
65
|
+
this.configDir = configDir;
|
|
66
|
+
this.authStorePath = authStorePath;
|
|
67
|
+
this.refreshSkewSeconds = refreshSkewSeconds;
|
|
68
|
+
}
|
|
69
|
+
get name() {
|
|
70
|
+
return this.model;
|
|
71
|
+
}
|
|
72
|
+
get model_name() {
|
|
73
|
+
return this.model;
|
|
74
|
+
}
|
|
75
|
+
async resolveClientConfig(forceRefresh = false) {
|
|
76
|
+
if (this.apiKey) {
|
|
77
|
+
return {
|
|
78
|
+
apiKey: this.apiKey,
|
|
79
|
+
baseURL: this.baseURL,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const credentials = await resolveCodexRuntimeCredentials({
|
|
83
|
+
configDir: this.configDir,
|
|
84
|
+
authStorePath: this.authStorePath,
|
|
85
|
+
baseURL: this.baseURL,
|
|
86
|
+
forceRefresh,
|
|
87
|
+
refreshSkewSeconds: this.refreshSkewSeconds,
|
|
88
|
+
fetchImplementation: this.fetchImplementation,
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
apiKey: credentials.api_key,
|
|
92
|
+
baseURL: credentials.base_url,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
createClient(config) {
|
|
96
|
+
const codexHeaders = isCodexBackendURL(config.baseURL)
|
|
97
|
+
? getCodexCloudflareHeaders(config.apiKey)
|
|
98
|
+
: {};
|
|
99
|
+
return new OpenAI({
|
|
100
|
+
apiKey: config.apiKey,
|
|
101
|
+
baseURL: config.baseURL,
|
|
102
|
+
timeout: this.timeout ?? undefined,
|
|
103
|
+
maxRetries: this.maxRetries,
|
|
104
|
+
defaultHeaders: {
|
|
105
|
+
...codexHeaders,
|
|
106
|
+
...(this.defaultHeaders ?? {}),
|
|
107
|
+
},
|
|
108
|
+
defaultQuery: this.defaultQuery ?? undefined,
|
|
109
|
+
fetch: this.fetchImplementation,
|
|
110
|
+
fetchOptions: (this.fetchOptions ?? undefined),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
getResponsesUsage(response) {
|
|
114
|
+
if (!response?.usage) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
prompt_tokens: response.usage.input_tokens ?? 0,
|
|
119
|
+
prompt_cached_tokens: response.usage.input_tokens_details?.cached_tokens ?? null,
|
|
120
|
+
prompt_cache_creation_tokens: null,
|
|
121
|
+
prompt_image_tokens: null,
|
|
122
|
+
completion_tokens: response.usage.output_tokens ?? 0,
|
|
123
|
+
total_tokens: response.usage.total_tokens ?? 0,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
getResponseOutputText(response) {
|
|
127
|
+
if (typeof response?.output_text === 'string') {
|
|
128
|
+
return response.output_text;
|
|
129
|
+
}
|
|
130
|
+
const outputs = Array.isArray(response?.output) ? response.output : [];
|
|
131
|
+
for (const item of outputs) {
|
|
132
|
+
if (Array.isArray(item?.content)) {
|
|
133
|
+
for (const part of item.content) {
|
|
134
|
+
if (typeof part?.text === 'string') {
|
|
135
|
+
return part.text;
|
|
136
|
+
}
|
|
137
|
+
if (typeof part?.output_text === 'string') {
|
|
138
|
+
return part.output_text;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return '';
|
|
144
|
+
}
|
|
145
|
+
async collectResponse(responseOrStream) {
|
|
146
|
+
if (!responseOrStream ||
|
|
147
|
+
typeof responseOrStream[Symbol.asyncIterator] !== 'function') {
|
|
148
|
+
return responseOrStream;
|
|
149
|
+
}
|
|
150
|
+
let terminalResponse = null;
|
|
151
|
+
const outputItems = [];
|
|
152
|
+
const textDeltas = [];
|
|
153
|
+
for await (const event of responseOrStream) {
|
|
154
|
+
const eventType = event?.type;
|
|
155
|
+
if (eventType === 'response.output_text.delta' &&
|
|
156
|
+
typeof event?.delta === 'string') {
|
|
157
|
+
textDeltas.push(event.delta);
|
|
158
|
+
}
|
|
159
|
+
if (eventType === 'response.output_item.done' && event?.item) {
|
|
160
|
+
outputItems.push(event.item);
|
|
161
|
+
}
|
|
162
|
+
if (eventType === 'response.completed' ||
|
|
163
|
+
eventType === 'response.incomplete' ||
|
|
164
|
+
eventType === 'response.failed') {
|
|
165
|
+
terminalResponse = event.response ?? null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const fallbackOutputText = textDeltas.join('');
|
|
169
|
+
if (!terminalResponse) {
|
|
170
|
+
return {
|
|
171
|
+
output_text: fallbackOutputText,
|
|
172
|
+
output: outputItems,
|
|
173
|
+
status: fallbackOutputText ? 'completed' : 'incomplete',
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
if (typeof terminalResponse.output_text !== 'string' &&
|
|
177
|
+
fallbackOutputText) {
|
|
178
|
+
terminalResponse.output_text = fallbackOutputText;
|
|
179
|
+
}
|
|
180
|
+
if ((!Array.isArray(terminalResponse.output) ||
|
|
181
|
+
terminalResponse.output.length === 0) &&
|
|
182
|
+
outputItems.length > 0) {
|
|
183
|
+
terminalResponse.output = outputItems;
|
|
184
|
+
}
|
|
185
|
+
return terminalResponse;
|
|
186
|
+
}
|
|
187
|
+
getInputText(content) {
|
|
188
|
+
if (typeof content === 'string') {
|
|
189
|
+
return content;
|
|
190
|
+
}
|
|
191
|
+
if (!Array.isArray(content)) {
|
|
192
|
+
return '';
|
|
193
|
+
}
|
|
194
|
+
return content
|
|
195
|
+
.map((part) => {
|
|
196
|
+
if (part &&
|
|
197
|
+
typeof part === 'object' &&
|
|
198
|
+
'text' in part &&
|
|
199
|
+
typeof part.text === 'string') {
|
|
200
|
+
return part.text;
|
|
201
|
+
}
|
|
202
|
+
return '';
|
|
203
|
+
})
|
|
204
|
+
.filter(Boolean)
|
|
205
|
+
.join('\n');
|
|
206
|
+
}
|
|
207
|
+
getModelParamsForResponses(baseURL) {
|
|
208
|
+
const codexBackend = isCodexBackendURL(baseURL);
|
|
209
|
+
const modelParams = {
|
|
210
|
+
store: false,
|
|
211
|
+
reasoning: codexBackend
|
|
212
|
+
? { effort: this.reasoningEffort, summary: 'auto' }
|
|
213
|
+
: { effort: this.reasoningEffort },
|
|
214
|
+
};
|
|
215
|
+
if (this.maxCompletionTokens !== null && !codexBackend) {
|
|
216
|
+
modelParams.max_output_tokens = this.maxCompletionTokens;
|
|
217
|
+
}
|
|
218
|
+
if (this.topP !== null && !codexBackend) {
|
|
219
|
+
modelParams.top_p = this.topP;
|
|
220
|
+
}
|
|
221
|
+
if (this.seed !== null && !codexBackend) {
|
|
222
|
+
modelParams.seed = this.seed;
|
|
223
|
+
}
|
|
224
|
+
if (this.serviceTier !== null) {
|
|
225
|
+
modelParams.service_tier = this.serviceTier;
|
|
226
|
+
}
|
|
227
|
+
if (codexBackend) {
|
|
228
|
+
modelParams.stream = true;
|
|
229
|
+
modelParams.include = this.include ?? ['reasoning.encrypted_content'];
|
|
230
|
+
modelParams.tools = [];
|
|
231
|
+
modelParams.tool_choice = 'auto';
|
|
232
|
+
modelParams.parallel_tool_calls = true;
|
|
233
|
+
}
|
|
234
|
+
else if (this.include !== null) {
|
|
235
|
+
modelParams.include = this.include;
|
|
236
|
+
}
|
|
237
|
+
return modelParams;
|
|
238
|
+
}
|
|
239
|
+
buildCodexBackendInput(inputMessages) {
|
|
240
|
+
const instructionParts = [];
|
|
241
|
+
const input = [];
|
|
242
|
+
for (const message of inputMessages) {
|
|
243
|
+
if (message.role === 'system') {
|
|
244
|
+
const text = this.getInputText(message.content).trim();
|
|
245
|
+
if (text) {
|
|
246
|
+
instructionParts.push(text);
|
|
247
|
+
}
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
input.push(message);
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
input: input.length > 0 ? input : [{ role: 'user', content: '' }],
|
|
254
|
+
instructions: instructionParts.join('\n\n').trim() || DEFAULT_CODEX_INSTRUCTIONS,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
getZodSchemaCandidate(output_format) {
|
|
258
|
+
const output = output_format;
|
|
259
|
+
if (output &&
|
|
260
|
+
typeof output === 'object' &&
|
|
261
|
+
typeof output.safeParse === 'function' &&
|
|
262
|
+
typeof output.parse === 'function') {
|
|
263
|
+
return output;
|
|
264
|
+
}
|
|
265
|
+
if (output &&
|
|
266
|
+
typeof output === 'object' &&
|
|
267
|
+
output.schema &&
|
|
268
|
+
typeof output.schema.safeParse === 'function' &&
|
|
269
|
+
typeof output.schema.parse === 'function') {
|
|
270
|
+
return output.schema;
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
buildRequest(messages, zodSchemaCandidate, baseURL) {
|
|
275
|
+
const serializer = new ResponsesAPIMessageSerializer();
|
|
276
|
+
const inputMessages = serializer.serialize(messages);
|
|
277
|
+
const codexBackend = isCodexBackendURL(baseURL);
|
|
278
|
+
const codexInput = codexBackend
|
|
279
|
+
? this.buildCodexBackendInput(inputMessages)
|
|
280
|
+
: null;
|
|
281
|
+
const request = {
|
|
282
|
+
model: this.model,
|
|
283
|
+
input: codexInput?.input ?? inputMessages,
|
|
284
|
+
...this.getModelParamsForResponses(baseURL),
|
|
285
|
+
};
|
|
286
|
+
if (codexInput) {
|
|
287
|
+
request.instructions = codexInput.instructions;
|
|
288
|
+
}
|
|
289
|
+
if (!zodSchemaCandidate) {
|
|
290
|
+
return request;
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
const rawJsonSchema = zodSchemaToJsonSchema(zodSchemaCandidate, {
|
|
294
|
+
name: 'agent_output',
|
|
295
|
+
target: 'jsonSchema7',
|
|
296
|
+
});
|
|
297
|
+
const optimizedJsonSchema = SchemaOptimizer.createOptimizedJsonSchema(rawJsonSchema, {
|
|
298
|
+
removeMinItems: this.removeMinItemsFromSchema,
|
|
299
|
+
removeDefaults: this.removeDefaultsFromSchema,
|
|
300
|
+
});
|
|
301
|
+
if (this.addSchemaToSystemPrompt &&
|
|
302
|
+
(codexBackend ||
|
|
303
|
+
(inputMessages.length > 0 && inputMessages[0]?.role === 'system'))) {
|
|
304
|
+
const schemaText = `\n<json_schema>\n${JSON.stringify(optimizedJsonSchema)}\n</json_schema>`;
|
|
305
|
+
if (codexBackend) {
|
|
306
|
+
request.instructions = `${String(request.instructions ?? DEFAULT_CODEX_INSTRUCTIONS)}${schemaText}`;
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
const firstInput = inputMessages[0];
|
|
310
|
+
const firstContent = firstInput?.content;
|
|
311
|
+
let patchedContent = firstContent ?? '';
|
|
312
|
+
if (typeof firstContent === 'string') {
|
|
313
|
+
patchedContent = firstContent + schemaText;
|
|
314
|
+
}
|
|
315
|
+
else if (Array.isArray(firstContent)) {
|
|
316
|
+
patchedContent = [
|
|
317
|
+
...firstContent,
|
|
318
|
+
{ type: 'input_text', text: schemaText },
|
|
319
|
+
];
|
|
320
|
+
}
|
|
321
|
+
inputMessages[0] = {
|
|
322
|
+
...inputMessages[0],
|
|
323
|
+
content: patchedContent,
|
|
324
|
+
};
|
|
325
|
+
request.input = inputMessages;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (!this.dontForceStructuredOutput) {
|
|
329
|
+
request.text = {
|
|
330
|
+
format: {
|
|
331
|
+
type: 'json_schema',
|
|
332
|
+
name: 'agent_output',
|
|
333
|
+
strict: true,
|
|
334
|
+
schema: optimizedJsonSchema,
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
// Skip structured output forcing when schema conversion fails.
|
|
341
|
+
}
|
|
342
|
+
return request;
|
|
343
|
+
}
|
|
344
|
+
async ainvoke(messages, output_format, options = {}) {
|
|
345
|
+
return this.invokeResponses(messages, output_format, options, false);
|
|
346
|
+
}
|
|
347
|
+
async invokeResponses(messages, output_format, options, forceRefresh) {
|
|
348
|
+
const zodSchemaCandidate = this.getZodSchemaCandidate(output_format);
|
|
349
|
+
try {
|
|
350
|
+
const clientConfig = await this.resolveClientConfig(forceRefresh);
|
|
351
|
+
const request = this.buildRequest(messages, zodSchemaCandidate, clientConfig.baseURL);
|
|
352
|
+
const client = this.createClient(clientConfig);
|
|
353
|
+
const responseOrStream = await client.responses.create(request, options.signal ? { signal: options.signal } : undefined);
|
|
354
|
+
const response = await this.collectResponse(responseOrStream);
|
|
355
|
+
const content = this.getResponseOutputText(response);
|
|
356
|
+
const usage = this.getResponsesUsage(response);
|
|
357
|
+
const stopReason = response?.status ?? null;
|
|
358
|
+
let completion = content;
|
|
359
|
+
if (output_format) {
|
|
360
|
+
if (zodSchemaCandidate) {
|
|
361
|
+
const parsedJson = JSON.parse(content);
|
|
362
|
+
const output = output_format;
|
|
363
|
+
if (output &&
|
|
364
|
+
typeof output === 'object' &&
|
|
365
|
+
output.schema &&
|
|
366
|
+
typeof output.schema.parse === 'function') {
|
|
367
|
+
completion = output.schema.parse(parsedJson);
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
completion = output.parse(parsedJson);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
completion = output_format.parse(content);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return new ChatInvokeCompletion(completion, usage, null, null, stopReason);
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
if (!this.apiKey && !forceRefresh && error?.status === 401) {
|
|
381
|
+
return this.invokeResponses(messages, output_format, options, true);
|
|
382
|
+
}
|
|
383
|
+
if (error?.status === 429) {
|
|
384
|
+
throw new ModelRateLimitError(error?.message ?? 'Rate limit exceeded', 429, this.model);
|
|
385
|
+
}
|
|
386
|
+
if (error instanceof CodexAuthError) {
|
|
387
|
+
throw error;
|
|
388
|
+
}
|
|
389
|
+
throw new ModelProviderError(error?.message ?? String(error), error?.status ?? 500, this.model);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
package/dist/llm/google/chat.js
CHANGED
|
@@ -3,6 +3,22 @@ import { ModelProviderError } from '../exceptions.js';
|
|
|
3
3
|
import { ChatInvokeCompletion } from '../views.js';
|
|
4
4
|
import { SchemaOptimizer, zodSchemaToJsonSchema } from '../schema.js';
|
|
5
5
|
import { GoogleMessageSerializer } from './serializer.js';
|
|
6
|
+
import { get_browser_use_version } from '../../utils.js';
|
|
7
|
+
const buildGoogleHttpOptions = (httpOptions) => {
|
|
8
|
+
const resolvedHttpOptions = {
|
|
9
|
+
...(httpOptions ?? {}),
|
|
10
|
+
};
|
|
11
|
+
const existingHeaders = resolvedHttpOptions.headers;
|
|
12
|
+
const headers = existingHeaders &&
|
|
13
|
+
typeof existingHeaders === 'object' &&
|
|
14
|
+
!Array.isArray(existingHeaders)
|
|
15
|
+
? Object.fromEntries(Object.entries(existingHeaders).map(([key, value]) => [String(key), String(value)]))
|
|
16
|
+
: {};
|
|
17
|
+
headers['x-goog-api-client'] =
|
|
18
|
+
`browser-use/${get_browser_use_version() || 'unknown'}`;
|
|
19
|
+
resolvedHttpOptions.headers = headers;
|
|
20
|
+
return resolvedHttpOptions;
|
|
21
|
+
};
|
|
6
22
|
export class ChatGoogle {
|
|
7
23
|
model;
|
|
8
24
|
provider = 'google';
|
|
@@ -44,6 +60,7 @@ export class ChatGoogle {
|
|
|
44
60
|
credentials,
|
|
45
61
|
};
|
|
46
62
|
const resolvedVertexAi = vertexai ?? vertexAi;
|
|
63
|
+
const resolvedHttpOptions = buildGoogleHttpOptions(httpOptions);
|
|
47
64
|
const clientOptions = {
|
|
48
65
|
...(apiKey != null ? { apiKey } : {}),
|
|
49
66
|
...(baseUrl ? { baseUrl } : {}),
|
|
@@ -51,7 +68,7 @@ export class ChatGoogle {
|
|
|
51
68
|
...(resolvedVertexAi != null ? { vertexai: resolvedVertexAi } : {}),
|
|
52
69
|
...(project ? { project } : {}),
|
|
53
70
|
...(location ? { location } : {}),
|
|
54
|
-
|
|
71
|
+
httpOptions: resolvedHttpOptions,
|
|
55
72
|
...(resolvedGoogleAuthOptions
|
|
56
73
|
? { googleAuthOptions: resolvedGoogleAuthOptions }
|
|
57
74
|
: {}),
|
package/dist/logging-config.js
CHANGED
|
@@ -45,7 +45,26 @@ const writePayload = (stream, level, payload) => {
|
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
47
|
const ensureFilePathReady = (filePath) => {
|
|
48
|
-
|
|
48
|
+
const dirPath = path.dirname(path.resolve(filePath));
|
|
49
|
+
const existed = fs.existsSync(dirPath);
|
|
50
|
+
fs.mkdirSync(dirPath, { recursive: true, mode: 0o700 });
|
|
51
|
+
if (!existed && process.platform !== 'win32') {
|
|
52
|
+
fs.chmodSync(dirPath, 0o700);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const createPrivateLogStream = (filePath) => {
|
|
56
|
+
const resolved = path.resolve(filePath);
|
|
57
|
+
ensureFilePathReady(resolved);
|
|
58
|
+
const fd = fs.openSync(resolved, 'a', 0o600);
|
|
59
|
+
if (process.platform !== 'win32') {
|
|
60
|
+
fs.chmodSync(resolved, 0o600);
|
|
61
|
+
}
|
|
62
|
+
return fs.createWriteStream(resolved, {
|
|
63
|
+
fd,
|
|
64
|
+
flags: 'a',
|
|
65
|
+
encoding: 'utf-8',
|
|
66
|
+
autoClose: true,
|
|
67
|
+
});
|
|
49
68
|
};
|
|
50
69
|
const closeFileStreams = () => {
|
|
51
70
|
if (debugLogStream) {
|
|
@@ -121,19 +140,11 @@ export const setupLogging = (options = {}) => {
|
|
|
121
140
|
consoleStream = options.stream || process.stderr;
|
|
122
141
|
const debugLogFile = options.debugLogFile ?? process.env.BROWSER_USE_DEBUG_LOG_FILE ?? null;
|
|
123
142
|
if (debugLogFile && debugLogFile.trim().length > 0) {
|
|
124
|
-
|
|
125
|
-
debugLogStream = fs.createWriteStream(path.resolve(debugLogFile), {
|
|
126
|
-
flags: 'a',
|
|
127
|
-
encoding: 'utf-8',
|
|
128
|
-
});
|
|
143
|
+
debugLogStream = createPrivateLogStream(debugLogFile);
|
|
129
144
|
}
|
|
130
145
|
const infoLogFile = options.infoLogFile ?? process.env.BROWSER_USE_INFO_LOG_FILE ?? null;
|
|
131
146
|
if (infoLogFile && infoLogFile.trim().length > 0) {
|
|
132
|
-
|
|
133
|
-
infoLogStream = fs.createWriteStream(path.resolve(infoLogFile), {
|
|
134
|
-
flags: 'a',
|
|
135
|
-
encoding: 'utf-8',
|
|
136
|
-
});
|
|
147
|
+
infoLogStream = createPrivateLogStream(infoLogFile);
|
|
137
148
|
}
|
|
138
149
|
configured = true;
|
|
139
150
|
return createLogger('browser_use');
|
package/dist/mcp/client.d.ts
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
import { type Tool, type Prompt } from '@modelcontextprotocol/sdk/types.js';
|
|
26
26
|
import type { Controller } from '../controller/service.js';
|
|
27
27
|
import type { Tools } from '../tools/service.js';
|
|
28
|
+
export { formatMcpCommandForLog, formatMcpToolArgsForLog, redactMcpLogMessage, } from './redaction.js';
|
|
28
29
|
export interface MCPClientOptions {
|
|
29
30
|
/** Maximum number of connection retry attempts (default: 3) */
|
|
30
31
|
maxRetries?: number;
|
package/dist/mcp/client.js
CHANGED
|
@@ -31,7 +31,9 @@ import { ActionResult } from '../agent/views.js';
|
|
|
31
31
|
import { productTelemetry } from '../telemetry/service.js';
|
|
32
32
|
import { MCPClientTelemetryEvent } from '../telemetry/views.js';
|
|
33
33
|
import { get_browser_use_version, retryAsync } from '../utils.js';
|
|
34
|
+
import { formatMcpCommandForLog, formatMcpToolArgsForLog, redactMcpLogMessage, } from './redaction.js';
|
|
34
35
|
const logger = createLogger('browser_use.mcp.client');
|
|
36
|
+
export { formatMcpCommandForLog, formatMcpToolArgsForLog, redactMcpLogMessage, } from './redaction.js';
|
|
35
37
|
export class MCPClient {
|
|
36
38
|
client;
|
|
37
39
|
command;
|
|
@@ -89,7 +91,7 @@ export class MCPClient {
|
|
|
89
91
|
const startTime = Date.now() / 1000;
|
|
90
92
|
let errorMsg = null;
|
|
91
93
|
try {
|
|
92
|
-
logger.info(`🔌 Connecting to MCP server '${this.serverName}': ${this.command
|
|
94
|
+
logger.info(`🔌 Connecting to MCP server '${this.serverName}': ${formatMcpCommandForLog(this.command, this.args)}`);
|
|
93
95
|
// Use retry logic for connection
|
|
94
96
|
await retryAsync(async () => {
|
|
95
97
|
// Create transport with environment variables
|
|
@@ -105,7 +107,7 @@ export class MCPClient {
|
|
|
105
107
|
delayMs: 1000,
|
|
106
108
|
backoffMultiplier: 2,
|
|
107
109
|
onRetry: (error, attempt, delay) => {
|
|
108
|
-
logger.warning(`Connection attempt ${attempt} failed for '${this.serverName}': ${error
|
|
110
|
+
logger.warning(`Connection attempt ${attempt} failed for '${this.serverName}': ${redactMcpLogMessage(error)}. Retrying in ${delay}ms...`);
|
|
109
111
|
},
|
|
110
112
|
});
|
|
111
113
|
this._connected = true;
|
|
@@ -125,7 +127,7 @@ export class MCPClient {
|
|
|
125
127
|
this._startHealthCheck();
|
|
126
128
|
}
|
|
127
129
|
catch (error) {
|
|
128
|
-
errorMsg =
|
|
130
|
+
errorMsg = redactMcpLogMessage(error);
|
|
129
131
|
this._connected = false;
|
|
130
132
|
throw error;
|
|
131
133
|
}
|
|
@@ -183,7 +185,7 @@ export class MCPClient {
|
|
|
183
185
|
logger.info(`Disconnected from '${this.serverName}' (${stats.toolCallCount} tool calls, ${(stats.successRate * 100).toFixed(1)}% success rate)`);
|
|
184
186
|
}
|
|
185
187
|
catch (error) {
|
|
186
|
-
errorMsg =
|
|
188
|
+
errorMsg = redactMcpLogMessage(error);
|
|
187
189
|
logger.error(`Error disconnecting from MCP server: ${errorMsg}`);
|
|
188
190
|
}
|
|
189
191
|
finally {
|
|
@@ -220,7 +222,7 @@ export class MCPClient {
|
|
|
220
222
|
const startTime = Date.now() / 1000;
|
|
221
223
|
let errorMsg = null;
|
|
222
224
|
try {
|
|
223
|
-
logger.debug(`🔧 Calling MCP tool '${name}' with params: ${
|
|
225
|
+
logger.debug(`🔧 Calling MCP tool '${name}' with params: ${formatMcpToolArgsForLog(args)}`);
|
|
224
226
|
this._toolCallCount++;
|
|
225
227
|
const result = await this.client.request(CallToolRequestSchema, {
|
|
226
228
|
name,
|
|
@@ -230,7 +232,7 @@ export class MCPClient {
|
|
|
230
232
|
}
|
|
231
233
|
catch (error) {
|
|
232
234
|
this._errorCount++;
|
|
233
|
-
errorMsg =
|
|
235
|
+
errorMsg = redactMcpLogMessage(error);
|
|
234
236
|
logger.error(`MCP tool '${name}' failed: ${errorMsg}`);
|
|
235
237
|
throw error;
|
|
236
238
|
}
|
|
@@ -308,7 +310,7 @@ export class MCPClient {
|
|
|
308
310
|
});
|
|
309
311
|
}
|
|
310
312
|
catch (error) {
|
|
311
|
-
const errorMsg =
|
|
313
|
+
const errorMsg = redactMcpLogMessage(error);
|
|
312
314
|
logger.error(`MCP tool '${tool.name}' failed: ${errorMsg}`);
|
|
313
315
|
return new ActionResult({
|
|
314
316
|
error: `MCP tool '${tool.name}' failed: ${errorMsg}`,
|
|
@@ -541,7 +543,7 @@ export class MCPClient {
|
|
|
541
543
|
return result;
|
|
542
544
|
}
|
|
543
545
|
catch (error) {
|
|
544
|
-
logger.error(`Failed to get prompt '${name}': ${error}`);
|
|
546
|
+
logger.error(`Failed to get prompt '${name}': ${redactMcpLogMessage(error)}`);
|
|
545
547
|
throw error;
|
|
546
548
|
}
|
|
547
549
|
}
|
|
@@ -557,7 +559,7 @@ export class MCPClient {
|
|
|
557
559
|
await this._performHealthCheck();
|
|
558
560
|
}
|
|
559
561
|
catch (error) {
|
|
560
|
-
logger.warning(`Health check failed for '${this.serverName}': ${error}`);
|
|
562
|
+
logger.warning(`Health check failed for '${this.serverName}': ${redactMcpLogMessage(error)}`);
|
|
561
563
|
if (this.autoReconnect) {
|
|
562
564
|
await this._attemptReconnect();
|
|
563
565
|
}
|
|
@@ -600,7 +602,7 @@ export class MCPClient {
|
|
|
600
602
|
logger.info(`✅ Reconnected to '${this.serverName}'`);
|
|
601
603
|
}
|
|
602
604
|
catch (error) {
|
|
603
|
-
logger.error(`Failed to reconnect to '${this.serverName}': ${error}`);
|
|
605
|
+
logger.error(`Failed to reconnect to '${this.serverName}': ${redactMcpLogMessage(error)}`);
|
|
604
606
|
}
|
|
605
607
|
}
|
|
606
608
|
/**
|