browser-use 0.0.1 → 0.1.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 +21 -0
- package/README.md +761 -0
- package/dist/agent/cloud-events.d.ts +264 -0
- package/dist/agent/cloud-events.js +318 -0
- package/dist/agent/gif.d.ts +15 -0
- package/dist/agent/gif.js +215 -0
- package/dist/agent/index.d.ts +8 -0
- package/dist/agent/index.js +8 -0
- package/dist/agent/message-manager/service.d.ts +30 -0
- package/dist/agent/message-manager/service.js +208 -0
- package/dist/agent/message-manager/utils.d.ts +2 -0
- package/dist/agent/message-manager/utils.js +41 -0
- package/dist/agent/message-manager/views.d.ts +26 -0
- package/dist/agent/message-manager/views.js +73 -0
- package/dist/agent/prompts.d.ts +52 -0
- package/dist/agent/prompts.js +259 -0
- package/dist/agent/service.d.ts +290 -0
- package/dist/agent/service.js +2200 -0
- package/dist/agent/views.d.ts +741 -0
- package/dist/agent/views.js +537 -0
- package/dist/browser/browser.d.ts +7 -0
- package/dist/browser/browser.js +5 -0
- package/dist/browser/context.d.ts +8 -0
- package/dist/browser/context.js +4 -0
- package/dist/browser/dvd-screensaver.d.ts +101 -0
- package/dist/browser/dvd-screensaver.js +270 -0
- package/dist/browser/extensions.d.ts +63 -0
- package/dist/browser/extensions.js +359 -0
- package/dist/browser/index.d.ts +10 -0
- package/dist/browser/index.js +9 -0
- package/dist/browser/playwright-manager.d.ts +47 -0
- package/dist/browser/playwright-manager.js +146 -0
- package/dist/browser/profile.d.ts +196 -0
- package/dist/browser/profile.js +815 -0
- package/dist/browser/session.d.ts +505 -0
- package/dist/browser/session.js +3409 -0
- package/dist/browser/types.d.ts +1184 -0
- package/dist/browser/types.js +1 -0
- package/dist/browser/utils.d.ts +1 -0
- package/dist/browser/utils.js +19 -0
- package/dist/browser/views.d.ts +78 -0
- package/dist/browser/views.js +72 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +44 -0
- package/dist/config.d.ts +108 -0
- package/dist/config.js +430 -0
- package/dist/controller/index.d.ts +3 -0
- package/dist/controller/index.js +3 -0
- package/dist/controller/registry/index.d.ts +2 -0
- package/dist/controller/registry/index.js +2 -0
- package/dist/controller/registry/service.d.ts +45 -0
- package/dist/controller/registry/service.js +184 -0
- package/dist/controller/registry/views.d.ts +55 -0
- package/dist/controller/registry/views.js +174 -0
- package/dist/controller/service.d.ts +49 -0
- package/dist/controller/service.js +1176 -0
- package/dist/controller/views.d.ts +241 -0
- package/dist/controller/views.js +88 -0
- package/dist/dom/clickable-element-processor/service.d.ts +11 -0
- package/dist/dom/clickable-element-processor/service.js +60 -0
- package/dist/dom/dom_tree/index.js +1400 -0
- package/dist/dom/history-tree-processor/service.d.ts +14 -0
- package/dist/dom/history-tree-processor/service.js +75 -0
- package/dist/dom/history-tree-processor/view.d.ts +54 -0
- package/dist/dom/history-tree-processor/view.js +56 -0
- package/dist/dom/playground/extraction.d.ts +19 -0
- package/dist/dom/playground/extraction.js +187 -0
- package/dist/dom/playground/process-dom.d.ts +1 -0
- package/dist/dom/playground/process-dom.js +5 -0
- package/dist/dom/playground/test-accessibility.d.ts +44 -0
- package/dist/dom/playground/test-accessibility.js +111 -0
- package/dist/dom/service.d.ts +19 -0
- package/dist/dom/service.js +227 -0
- package/dist/dom/utils.d.ts +1 -0
- package/dist/dom/utils.js +6 -0
- package/dist/dom/views.d.ts +61 -0
- package/dist/dom/views.js +247 -0
- package/dist/event-bus.d.ts +11 -0
- package/dist/event-bus.js +19 -0
- package/dist/exceptions.d.ts +10 -0
- package/dist/exceptions.js +22 -0
- package/dist/filesystem/file-system.d.ts +68 -0
- package/dist/filesystem/file-system.js +412 -0
- package/dist/filesystem/index.d.ts +1 -0
- package/dist/filesystem/index.js +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +33 -0
- package/dist/integrations/gmail/actions.d.ts +12 -0
- package/dist/integrations/gmail/actions.js +113 -0
- package/dist/integrations/gmail/index.d.ts +2 -0
- package/dist/integrations/gmail/index.js +2 -0
- package/dist/integrations/gmail/service.d.ts +61 -0
- package/dist/integrations/gmail/service.js +260 -0
- package/dist/llm/anthropic/chat.d.ts +28 -0
- package/dist/llm/anthropic/chat.js +126 -0
- package/dist/llm/anthropic/index.d.ts +2 -0
- package/dist/llm/anthropic/index.js +2 -0
- package/dist/llm/anthropic/serializer.d.ts +68 -0
- package/dist/llm/anthropic/serializer.js +285 -0
- package/dist/llm/aws/chat-anthropic.d.ts +61 -0
- package/dist/llm/aws/chat-anthropic.js +176 -0
- package/dist/llm/aws/chat-bedrock.d.ts +15 -0
- package/dist/llm/aws/chat-bedrock.js +80 -0
- package/dist/llm/aws/index.d.ts +3 -0
- package/dist/llm/aws/index.js +3 -0
- package/dist/llm/aws/serializer.d.ts +5 -0
- package/dist/llm/aws/serializer.js +68 -0
- package/dist/llm/azure/chat.d.ts +15 -0
- package/dist/llm/azure/chat.js +83 -0
- package/dist/llm/azure/index.d.ts +1 -0
- package/dist/llm/azure/index.js +1 -0
- package/dist/llm/base.d.ts +16 -0
- package/dist/llm/base.js +1 -0
- package/dist/llm/deepseek/chat.d.ts +15 -0
- package/dist/llm/deepseek/chat.js +51 -0
- package/dist/llm/deepseek/index.d.ts +2 -0
- package/dist/llm/deepseek/index.js +2 -0
- package/dist/llm/deepseek/serializer.d.ts +6 -0
- package/dist/llm/deepseek/serializer.js +57 -0
- package/dist/llm/exceptions.d.ts +10 -0
- package/dist/llm/exceptions.js +18 -0
- package/dist/llm/google/chat.d.ts +20 -0
- package/dist/llm/google/chat.js +144 -0
- package/dist/llm/google/index.d.ts +2 -0
- package/dist/llm/google/index.js +2 -0
- package/dist/llm/google/serializer.d.ts +6 -0
- package/dist/llm/google/serializer.js +64 -0
- package/dist/llm/groq/chat.d.ts +15 -0
- package/dist/llm/groq/chat.js +52 -0
- package/dist/llm/groq/index.d.ts +3 -0
- package/dist/llm/groq/index.js +3 -0
- package/dist/llm/groq/parser.d.ts +32 -0
- package/dist/llm/groq/parser.js +189 -0
- package/dist/llm/groq/serializer.d.ts +6 -0
- package/dist/llm/groq/serializer.js +56 -0
- package/dist/llm/messages.d.ts +77 -0
- package/dist/llm/messages.js +157 -0
- package/dist/llm/ollama/chat.d.ts +15 -0
- package/dist/llm/ollama/chat.js +77 -0
- package/dist/llm/ollama/index.d.ts +2 -0
- package/dist/llm/ollama/index.js +2 -0
- package/dist/llm/ollama/serializer.d.ts +6 -0
- package/dist/llm/ollama/serializer.js +53 -0
- package/dist/llm/openai/chat.d.ts +38 -0
- package/dist/llm/openai/chat.js +174 -0
- package/dist/llm/openai/index.d.ts +3 -0
- package/dist/llm/openai/index.js +3 -0
- package/dist/llm/openai/like.d.ts +17 -0
- package/dist/llm/openai/like.js +19 -0
- package/dist/llm/openai/serializer.d.ts +6 -0
- package/dist/llm/openai/serializer.js +57 -0
- package/dist/llm/openrouter/chat.d.ts +15 -0
- package/dist/llm/openrouter/chat.js +74 -0
- package/dist/llm/openrouter/index.d.ts +2 -0
- package/dist/llm/openrouter/index.js +2 -0
- package/dist/llm/openrouter/serializer.d.ts +3 -0
- package/dist/llm/openrouter/serializer.js +3 -0
- package/dist/llm/schema.d.ts +6 -0
- package/dist/llm/schema.js +77 -0
- package/dist/llm/views.d.ts +15 -0
- package/dist/llm/views.js +12 -0
- package/dist/logging-config.d.ts +25 -0
- package/dist/logging-config.js +89 -0
- package/dist/mcp/client.d.ts +142 -0
- package/dist/mcp/client.js +638 -0
- package/dist/mcp/controller.d.ts +6 -0
- package/dist/mcp/controller.js +38 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.js +3 -0
- package/dist/mcp/server.d.ts +134 -0
- package/dist/mcp/server.js +759 -0
- package/dist/observability-decorators.d.ts +158 -0
- package/dist/observability-decorators.js +286 -0
- package/dist/observability.d.ts +23 -0
- package/dist/observability.js +58 -0
- package/dist/screenshots/index.d.ts +1 -0
- package/dist/screenshots/index.js +1 -0
- package/dist/screenshots/service.d.ts +6 -0
- package/dist/screenshots/service.js +28 -0
- package/dist/sync/auth.d.ts +27 -0
- package/dist/sync/auth.js +205 -0
- package/dist/sync/index.d.ts +2 -0
- package/dist/sync/index.js +2 -0
- package/dist/sync/service.d.ts +21 -0
- package/dist/sync/service.js +146 -0
- package/dist/telemetry/index.d.ts +2 -0
- package/dist/telemetry/index.js +2 -0
- package/dist/telemetry/service.d.ts +12 -0
- package/dist/telemetry/service.js +85 -0
- package/dist/telemetry/views.d.ts +112 -0
- package/dist/telemetry/views.js +112 -0
- package/dist/tokens/index.d.ts +2 -0
- package/dist/tokens/index.js +2 -0
- package/dist/tokens/service.d.ts +35 -0
- package/dist/tokens/service.js +423 -0
- package/dist/tokens/views.d.ts +58 -0
- package/dist/tokens/views.js +1 -0
- package/dist/utils.d.ts +128 -0
- package/dist/utils.js +529 -0
- package/package.json +94 -5
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import fsSync from 'node:fs';
|
|
2
|
+
import { promises as fsp } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import PDFDocument from 'pdfkit';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
import { spawnSync } from 'node:child_process';
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
const pdfParse = require('pdf-parse');
|
|
9
|
+
export const INVALID_FILENAME_ERROR_MESSAGE = 'Error: Invalid filename format. Must be alphanumeric with supported extension.';
|
|
10
|
+
export const DEFAULT_FILE_SYSTEM_PATH = 'browseruse_agent_data';
|
|
11
|
+
const DEFAULT_EXTENSIONS = ['md', 'txt', 'json', 'csv', 'pdf'];
|
|
12
|
+
const filenameRegex = new RegExp(`^[a-zA-Z0-9_\\-]+\\.(${DEFAULT_EXTENSIONS.join('|')})$`);
|
|
13
|
+
export class FileSystemError extends Error {
|
|
14
|
+
}
|
|
15
|
+
class BaseFile {
|
|
16
|
+
name;
|
|
17
|
+
content;
|
|
18
|
+
constructor(name, content = '') {
|
|
19
|
+
this.name = name;
|
|
20
|
+
this.content = content;
|
|
21
|
+
}
|
|
22
|
+
get fullName() {
|
|
23
|
+
return `${this.name}.${this.extension}`;
|
|
24
|
+
}
|
|
25
|
+
get size() {
|
|
26
|
+
return this.content.length;
|
|
27
|
+
}
|
|
28
|
+
get lineCount() {
|
|
29
|
+
return this.content ? this.content.split(/\r?\n/).length : 0;
|
|
30
|
+
}
|
|
31
|
+
writeFileContent(content) {
|
|
32
|
+
this.content = content;
|
|
33
|
+
}
|
|
34
|
+
appendFileContent(content) {
|
|
35
|
+
this.content = `${this.content}${content}`;
|
|
36
|
+
}
|
|
37
|
+
read() {
|
|
38
|
+
return this.content;
|
|
39
|
+
}
|
|
40
|
+
async syncToDisk(dir) {
|
|
41
|
+
await fsp.writeFile(path.join(dir, this.fullName), this.content, 'utf-8');
|
|
42
|
+
}
|
|
43
|
+
syncToDiskSync(dir) {
|
|
44
|
+
fsSync.writeFileSync(path.join(dir, this.fullName), this.content, 'utf-8');
|
|
45
|
+
}
|
|
46
|
+
async write(content, dir) {
|
|
47
|
+
this.writeFileContent(content);
|
|
48
|
+
await this.syncToDisk(dir);
|
|
49
|
+
}
|
|
50
|
+
writeSync(content, dir) {
|
|
51
|
+
this.writeFileContent(content);
|
|
52
|
+
this.syncToDiskSync(dir);
|
|
53
|
+
}
|
|
54
|
+
async append(content, dir) {
|
|
55
|
+
this.appendFileContent(content);
|
|
56
|
+
await this.syncToDisk(dir);
|
|
57
|
+
}
|
|
58
|
+
appendSync(content, dir) {
|
|
59
|
+
this.appendFileContent(content);
|
|
60
|
+
this.syncToDiskSync(dir);
|
|
61
|
+
}
|
|
62
|
+
toJSON() {
|
|
63
|
+
return { name: this.name, content: this.content };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
class MarkdownFile extends BaseFile {
|
|
67
|
+
get extension() {
|
|
68
|
+
return 'md';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
class TxtFile extends BaseFile {
|
|
72
|
+
get extension() {
|
|
73
|
+
return 'txt';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
class JsonFile extends BaseFile {
|
|
77
|
+
get extension() {
|
|
78
|
+
return 'json';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
class CsvFile extends BaseFile {
|
|
82
|
+
get extension() {
|
|
83
|
+
return 'csv';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
class PdfFile extends BaseFile {
|
|
87
|
+
get extension() {
|
|
88
|
+
return 'pdf';
|
|
89
|
+
}
|
|
90
|
+
async syncToDisk(dir) {
|
|
91
|
+
const filePath = path.join(dir, this.fullName);
|
|
92
|
+
await new Promise((resolve, reject) => {
|
|
93
|
+
const doc = new PDFDocument({ autoFirstPage: true });
|
|
94
|
+
const stream = fsSync.createWriteStream(filePath);
|
|
95
|
+
doc.pipe(stream);
|
|
96
|
+
doc.fontSize(12).text(this.content || '', { width: 500, align: 'left' });
|
|
97
|
+
doc.end();
|
|
98
|
+
stream.on('finish', resolve);
|
|
99
|
+
stream.on('error', reject);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
syncToDiskSync(dir) {
|
|
103
|
+
const filePath = path.join(dir, this.fullName);
|
|
104
|
+
const script = `
|
|
105
|
+
const { createWriteStream } = require('fs');
|
|
106
|
+
const PDFDocument = require(${JSON.stringify(require.resolve('pdfkit'))});
|
|
107
|
+
const filePath = ${JSON.stringify(filePath)};
|
|
108
|
+
const content = ${JSON.stringify(this.content ?? '')};
|
|
109
|
+
const doc = new PDFDocument({ autoFirstPage: true });
|
|
110
|
+
const stream = createWriteStream(filePath);
|
|
111
|
+
doc.pipe(stream);
|
|
112
|
+
doc.fontSize(12).text(content || '', { width: 500, align: 'left' });
|
|
113
|
+
doc.end();
|
|
114
|
+
stream.on('finish', () => process.exit(0));
|
|
115
|
+
stream.on('error', (err) => {
|
|
116
|
+
console.error(err);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
});
|
|
119
|
+
`;
|
|
120
|
+
const result = spawnSync(process.execPath, ['-e', script], {
|
|
121
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
122
|
+
});
|
|
123
|
+
if (result.status !== 0) {
|
|
124
|
+
const errorMsg = result.stderr?.toString() ||
|
|
125
|
+
`Could not write to file '${this.fullName}'.`;
|
|
126
|
+
throw new FileSystemError(`Error: ${errorMsg.trim()}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const FILE_TYPES = {
|
|
131
|
+
md: MarkdownFile,
|
|
132
|
+
txt: TxtFile,
|
|
133
|
+
json: JsonFile,
|
|
134
|
+
csv: CsvFile,
|
|
135
|
+
pdf: PdfFile,
|
|
136
|
+
};
|
|
137
|
+
const TYPE_NAME_MAP = {
|
|
138
|
+
MarkdownFile,
|
|
139
|
+
TxtFile,
|
|
140
|
+
JsonFile,
|
|
141
|
+
CsvFile,
|
|
142
|
+
PdfFile,
|
|
143
|
+
};
|
|
144
|
+
export class FileSystem {
|
|
145
|
+
files = new Map();
|
|
146
|
+
defaultFiles = ['todo.md'];
|
|
147
|
+
baseDir;
|
|
148
|
+
dataDir;
|
|
149
|
+
extractedContentCount = 0;
|
|
150
|
+
constructor(baseDir, createDefaultFiles = true) {
|
|
151
|
+
this.baseDir = path.resolve(baseDir);
|
|
152
|
+
fsSync.mkdirSync(this.baseDir, { recursive: true });
|
|
153
|
+
this.dataDir = path.join(this.baseDir, DEFAULT_FILE_SYSTEM_PATH);
|
|
154
|
+
if (fsSync.existsSync(this.dataDir)) {
|
|
155
|
+
fsSync.rmSync(this.dataDir, { recursive: true, force: true });
|
|
156
|
+
}
|
|
157
|
+
fsSync.mkdirSync(this.dataDir, { recursive: true });
|
|
158
|
+
if (createDefaultFiles) {
|
|
159
|
+
this.createDefaultFiles();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
createDefaultFiles() {
|
|
163
|
+
for (const filename of this.defaultFiles) {
|
|
164
|
+
const file = this.instantiateFile(filename);
|
|
165
|
+
this.files.set(filename, file);
|
|
166
|
+
fsSync.writeFileSync(path.join(this.dataDir, filename), file.read(), 'utf-8');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
isValidFilename(filename) {
|
|
170
|
+
return filenameRegex.test(filename);
|
|
171
|
+
}
|
|
172
|
+
parseFilename(filename) {
|
|
173
|
+
const idx = filename.lastIndexOf('.');
|
|
174
|
+
if (idx === -1) {
|
|
175
|
+
throw new FileSystemError(INVALID_FILENAME_ERROR_MESSAGE);
|
|
176
|
+
}
|
|
177
|
+
const name = filename.slice(0, idx);
|
|
178
|
+
const extension = filename.slice(idx + 1).toLowerCase();
|
|
179
|
+
return [name, extension];
|
|
180
|
+
}
|
|
181
|
+
getFileClass(extension) {
|
|
182
|
+
return FILE_TYPES[extension];
|
|
183
|
+
}
|
|
184
|
+
instantiateFile(fullFilename, content = '') {
|
|
185
|
+
const [name, extension] = this.parseFilename(fullFilename);
|
|
186
|
+
const FileCtor = this.getFileClass(extension);
|
|
187
|
+
if (!FileCtor) {
|
|
188
|
+
throw new FileSystemError(INVALID_FILENAME_ERROR_MESSAGE);
|
|
189
|
+
}
|
|
190
|
+
return new FileCtor(name, content);
|
|
191
|
+
}
|
|
192
|
+
get_allowed_extensions() {
|
|
193
|
+
return Object.keys(FILE_TYPES);
|
|
194
|
+
}
|
|
195
|
+
get_dir() {
|
|
196
|
+
return this.dataDir;
|
|
197
|
+
}
|
|
198
|
+
get_file(filename) {
|
|
199
|
+
return this.files.get(filename) ?? null;
|
|
200
|
+
}
|
|
201
|
+
list_files() {
|
|
202
|
+
return Array.from(this.files.values()).map((file) => file.fullName);
|
|
203
|
+
}
|
|
204
|
+
display_file(filename) {
|
|
205
|
+
if (!this.isValidFilename(filename)) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
const file = this.get_file(filename);
|
|
209
|
+
return file ? file.read() : null;
|
|
210
|
+
}
|
|
211
|
+
async read_file(filename, externalFile = false) {
|
|
212
|
+
if (externalFile) {
|
|
213
|
+
try {
|
|
214
|
+
const [, extension] = this.parseFilename(filename);
|
|
215
|
+
if (['md', 'txt', 'json', 'csv'].includes(extension)) {
|
|
216
|
+
const content = await fsp.readFile(filename, 'utf-8');
|
|
217
|
+
return `Read from file ${filename}.\n<content>\n${content}\n</content>`;
|
|
218
|
+
}
|
|
219
|
+
if (extension === 'pdf') {
|
|
220
|
+
const buffer = await fsp.readFile(filename);
|
|
221
|
+
const parsed = await pdfParse(buffer);
|
|
222
|
+
const totalPages = parsed.numpages ?? 0;
|
|
223
|
+
const extraPages = Math.max(0, totalPages - 10);
|
|
224
|
+
const snippet = parsed.text?.trim() || '';
|
|
225
|
+
const preview = snippet
|
|
226
|
+
.split(/\n{2,}/)
|
|
227
|
+
.slice(0, 10)
|
|
228
|
+
.join('\n\n');
|
|
229
|
+
const suffix = extraPages > 0 ? `\n${extraPages} more pages...` : '';
|
|
230
|
+
return `Read from file ${filename}.\n<content>\n${preview}${suffix}\n</content>`;
|
|
231
|
+
}
|
|
232
|
+
return `Error: Cannot read file ${filename} as ${extension} extension is not supported.`;
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
if (error?.code === 'ENOENT') {
|
|
236
|
+
return `Error: File '${filename}' not found.`;
|
|
237
|
+
}
|
|
238
|
+
if (error?.code === 'EACCES') {
|
|
239
|
+
return `Error: Permission denied to read file '${filename}'.`;
|
|
240
|
+
}
|
|
241
|
+
return `Error: Could not read file '${filename}'.`;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (!this.isValidFilename(filename)) {
|
|
245
|
+
return INVALID_FILENAME_ERROR_MESSAGE;
|
|
246
|
+
}
|
|
247
|
+
const file = this.get_file(filename);
|
|
248
|
+
if (!file) {
|
|
249
|
+
return `File '${filename}' not found.`;
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
const content = file.read();
|
|
253
|
+
return `Read from file ${filename}.\n<content>\n${content}\n</content>`;
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
return error instanceof FileSystemError
|
|
257
|
+
? error.message
|
|
258
|
+
: `Error: Could not read file '${filename}'.`;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async write_file(filename, content) {
|
|
262
|
+
if (!this.isValidFilename(filename)) {
|
|
263
|
+
return INVALID_FILENAME_ERROR_MESSAGE;
|
|
264
|
+
}
|
|
265
|
+
const file = this.files.get(filename) ?? this.instantiateFile(filename);
|
|
266
|
+
this.files.set(filename, file);
|
|
267
|
+
try {
|
|
268
|
+
await file.write(content, this.dataDir);
|
|
269
|
+
return `Data written to file ${filename} successfully.`;
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
return `Error: Could not write to file '${filename}'. ${error.message}`;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
async append_file(filename, content) {
|
|
276
|
+
if (!this.isValidFilename(filename)) {
|
|
277
|
+
return INVALID_FILENAME_ERROR_MESSAGE;
|
|
278
|
+
}
|
|
279
|
+
const file = this.get_file(filename);
|
|
280
|
+
if (!file) {
|
|
281
|
+
return `File '${filename}' not found.`;
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
await file.append(content, this.dataDir);
|
|
285
|
+
return `Data appended to file ${filename} successfully.`;
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
return `Error: Could not append to file '${filename}'. ${error.message}`;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async replace_file_str(filename, oldStr, newStr) {
|
|
292
|
+
if (!this.isValidFilename(filename)) {
|
|
293
|
+
return INVALID_FILENAME_ERROR_MESSAGE;
|
|
294
|
+
}
|
|
295
|
+
if (!oldStr) {
|
|
296
|
+
return 'Error: Cannot replace empty string. Please provide a non-empty string to replace.';
|
|
297
|
+
}
|
|
298
|
+
const file = this.get_file(filename);
|
|
299
|
+
if (!file) {
|
|
300
|
+
return `File '${filename}' not found.`;
|
|
301
|
+
}
|
|
302
|
+
try {
|
|
303
|
+
const content = file.read().replaceAll(oldStr, newStr);
|
|
304
|
+
await file.write(content, this.dataDir);
|
|
305
|
+
return `Successfully replaced all occurrences of "${oldStr}" with "${newStr}" in file ${filename}`;
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
return `Error: Could not replace string in file '${filename}'. ${error.message}`;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
async save_extracted_content(content) {
|
|
312
|
+
const filename = `extracted_content_${this.extractedContentCount}.md`;
|
|
313
|
+
const file = new MarkdownFile(`extracted_content_${this.extractedContentCount}`);
|
|
314
|
+
await file.write(content, this.dataDir);
|
|
315
|
+
this.files.set(filename, file);
|
|
316
|
+
this.extractedContentCount += 1;
|
|
317
|
+
return `Extracted content saved to file ${filename} successfully.`;
|
|
318
|
+
}
|
|
319
|
+
describe() {
|
|
320
|
+
const DISPLAY_CHARS = 400;
|
|
321
|
+
let description = '';
|
|
322
|
+
for (const file of this.files.values()) {
|
|
323
|
+
if (file.fullName === 'todo.md') {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const content = file.read();
|
|
327
|
+
if (!content) {
|
|
328
|
+
description += `<file>\n${file.fullName} - [empty file]\n</file>\n`;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
const lines = content.split(/\r?\n/);
|
|
332
|
+
const lineCount = lines.length;
|
|
333
|
+
if (content.length < DISPLAY_CHARS * 1.5) {
|
|
334
|
+
description += `<file>\n${file.fullName} - ${lineCount} lines\n<content>\n${content}\n</content>\n</file>\n`;
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
const halfChars = Math.floor(DISPLAY_CHARS / 2);
|
|
338
|
+
let startPreview = '';
|
|
339
|
+
let startLines = 0;
|
|
340
|
+
let accumulated = 0;
|
|
341
|
+
for (const line of lines) {
|
|
342
|
+
if (accumulated + line.length + 1 > halfChars) {
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
startPreview += `${line}\n`;
|
|
346
|
+
accumulated += line.length + 1;
|
|
347
|
+
startLines += 1;
|
|
348
|
+
}
|
|
349
|
+
let endPreview = '';
|
|
350
|
+
let endLines = 0;
|
|
351
|
+
accumulated = 0;
|
|
352
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
353
|
+
const line = lines[i];
|
|
354
|
+
if (accumulated + line.length + 1 > halfChars) {
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
endPreview = `${line}\n${endPreview}`;
|
|
358
|
+
accumulated += line.length + 1;
|
|
359
|
+
endLines += 1;
|
|
360
|
+
}
|
|
361
|
+
const middleLines = lineCount - startLines - endLines;
|
|
362
|
+
if (middleLines <= 0) {
|
|
363
|
+
description += `<file>\n${file.fullName} - ${lineCount} lines\n<content>\n${content}\n</content>\n</file>\n`;
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
description += `<file>\n${file.fullName} - ${lineCount} lines\n<content>\n${startPreview.trim()}\n`;
|
|
367
|
+
description += `... ${middleLines} more lines ...\n`;
|
|
368
|
+
description += `${endPreview.trim()}\n</content>\n</file>\n`;
|
|
369
|
+
}
|
|
370
|
+
return description.trim();
|
|
371
|
+
}
|
|
372
|
+
get_todo_contents() {
|
|
373
|
+
const todo = this.get_file('todo.md');
|
|
374
|
+
return todo?.read() ?? '';
|
|
375
|
+
}
|
|
376
|
+
get_state() {
|
|
377
|
+
const files = {};
|
|
378
|
+
for (const [filename, file] of this.files.entries()) {
|
|
379
|
+
files[filename] = { type: file.constructor.name, data: file.toJSON() };
|
|
380
|
+
}
|
|
381
|
+
return {
|
|
382
|
+
files,
|
|
383
|
+
base_dir: this.baseDir,
|
|
384
|
+
extracted_content_count: this.extractedContentCount,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
async nuke() {
|
|
388
|
+
await fsp.rm(this.dataDir, { recursive: true, force: true });
|
|
389
|
+
}
|
|
390
|
+
static from_state_sync(state) {
|
|
391
|
+
const fsInstance = new FileSystem(state.base_dir, false);
|
|
392
|
+
fsInstance.extractedContentCount = state.extracted_content_count;
|
|
393
|
+
for (const [filename, fileState] of Object.entries(state.files)) {
|
|
394
|
+
const FileCtor = TYPE_NAME_MAP[fileState.type];
|
|
395
|
+
if (!FileCtor) {
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
const file = new FileCtor(fileState.data.name, fileState.data.content);
|
|
399
|
+
fsInstance.files.set(filename, file);
|
|
400
|
+
try {
|
|
401
|
+
file.writeSync(fileState.data.content, fsInstance.dataDir);
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
throw new FileSystemError(`Error restoring file '${filename}': ${error.message}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return fsInstance;
|
|
408
|
+
}
|
|
409
|
+
static async from_state(state) {
|
|
410
|
+
return FileSystem.from_state_sync(state);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './file-system.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './file-system.js';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export * from './config.js';
|
|
2
|
+
export * from './logging-config.js';
|
|
3
|
+
export { observe, observe_debug, isLmnrAvailable, isDebugMode, getObservabilityStatus, } from './observability.js';
|
|
4
|
+
export { observeDebug, observeDebugMethod, OperationTrace, trackPerformance, withObservability, PerformanceCounter, } from './observability-decorators.js';
|
|
5
|
+
export { URLNotAllowedError as BaseURLNotAllowedError } from './exceptions.js';
|
|
6
|
+
export * from './utils.js';
|
|
7
|
+
export * from './browser/index.js';
|
|
8
|
+
export * from './dom/views.js';
|
|
9
|
+
export * from './dom/history-tree-processor/view.js';
|
|
10
|
+
export * from './dom/history-tree-processor/service.js';
|
|
11
|
+
export * from './dom/service.js';
|
|
12
|
+
export * from './dom/clickable-element-processor/service.js';
|
|
13
|
+
export * from './screenshots/service.js';
|
|
14
|
+
export * from './controller/views.js';
|
|
15
|
+
export * from './controller/registry/service.js';
|
|
16
|
+
export * from './controller/service.js';
|
|
17
|
+
export * from './filesystem/file-system.js';
|
|
18
|
+
export * from './agent/views.js';
|
|
19
|
+
export * from './telemetry/views.js';
|
|
20
|
+
export * from './telemetry/service.js';
|
|
21
|
+
export * from './llm/messages.js';
|
|
22
|
+
export * from './llm/views.js';
|
|
23
|
+
export * from './llm/base.js';
|
|
24
|
+
export * from './llm/exceptions.js';
|
|
25
|
+
export * from './llm/schema.js';
|
|
26
|
+
export * from './tokens/views.js';
|
|
27
|
+
export * from './agent/message-manager/views.js';
|
|
28
|
+
export * from './agent/prompts.js';
|
|
29
|
+
export * from './agent/message-manager/service.js';
|
|
30
|
+
export * from './agent/service.js';
|
|
31
|
+
export * from './agent/message-manager/utils.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export * from './config.js';
|
|
2
|
+
export * from './logging-config.js';
|
|
3
|
+
// Export observability - note observeDebug exists in both files
|
|
4
|
+
export { observe, observe_debug, isLmnrAvailable, isDebugMode, getObservabilityStatus, } from './observability.js';
|
|
5
|
+
export { observeDebug, observeDebugMethod, OperationTrace, trackPerformance, withObservability, PerformanceCounter, } from './observability-decorators.js';
|
|
6
|
+
// Export exceptions - note URLNotAllowedError defined in both exceptions.js and browser/views.js
|
|
7
|
+
export { URLNotAllowedError as BaseURLNotAllowedError } from './exceptions.js';
|
|
8
|
+
export * from './utils.js';
|
|
9
|
+
export * from './browser/index.js';
|
|
10
|
+
export * from './dom/views.js';
|
|
11
|
+
export * from './dom/history-tree-processor/view.js';
|
|
12
|
+
export * from './dom/history-tree-processor/service.js';
|
|
13
|
+
export * from './dom/service.js';
|
|
14
|
+
export * from './dom/clickable-element-processor/service.js';
|
|
15
|
+
export * from './screenshots/service.js';
|
|
16
|
+
export * from './controller/views.js';
|
|
17
|
+
export * from './controller/registry/service.js';
|
|
18
|
+
export * from './controller/service.js';
|
|
19
|
+
export * from './filesystem/file-system.js';
|
|
20
|
+
export * from './agent/views.js';
|
|
21
|
+
export * from './telemetry/views.js';
|
|
22
|
+
export * from './telemetry/service.js';
|
|
23
|
+
export * from './llm/messages.js';
|
|
24
|
+
export * from './llm/views.js';
|
|
25
|
+
export * from './llm/base.js';
|
|
26
|
+
export * from './llm/exceptions.js';
|
|
27
|
+
export * from './llm/schema.js';
|
|
28
|
+
export * from './tokens/views.js';
|
|
29
|
+
export * from './agent/message-manager/views.js';
|
|
30
|
+
export * from './agent/prompts.js';
|
|
31
|
+
export * from './agent/message-manager/service.js';
|
|
32
|
+
export * from './agent/service.js';
|
|
33
|
+
export * from './agent/message-manager/utils.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gmail Actions for Browser Use
|
|
3
|
+
* Defines agent actions for Gmail integration including 2FA code retrieval,
|
|
4
|
+
* email reading, and authentication management.
|
|
5
|
+
*/
|
|
6
|
+
import type { Controller } from '../../controller/service.js';
|
|
7
|
+
import { GmailService } from './service.js';
|
|
8
|
+
/**
|
|
9
|
+
* Register Gmail actions with the provided controller
|
|
10
|
+
*/
|
|
11
|
+
export declare function registerGmailActions(controller: Controller, gmailService?: GmailService | null, accessToken?: string | null): Controller;
|
|
12
|
+
export declare const register_gmail_actions: typeof registerGmailActions;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gmail Actions for Browser Use
|
|
3
|
+
* Defines agent actions for Gmail integration including 2FA code retrieval,
|
|
4
|
+
* email reading, and authentication management.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { createLogger } from '../../logging-config.js';
|
|
8
|
+
import { ActionResult } from '../../agent/views.js';
|
|
9
|
+
import { GmailService } from './service.js';
|
|
10
|
+
const logger = createLogger('browser_use.gmail.actions');
|
|
11
|
+
// Global Gmail service instance - initialized when actions are registered
|
|
12
|
+
let _gmailService = null;
|
|
13
|
+
// Schema for get_recent_emails action
|
|
14
|
+
const GetRecentEmailsParams = z.object({
|
|
15
|
+
keyword: z
|
|
16
|
+
.string()
|
|
17
|
+
.default('')
|
|
18
|
+
.describe('A single keyword for search, e.g. github, airbnb, etc.'),
|
|
19
|
+
max_results: z
|
|
20
|
+
.number()
|
|
21
|
+
.int()
|
|
22
|
+
.min(1)
|
|
23
|
+
.max(50)
|
|
24
|
+
.default(3)
|
|
25
|
+
.describe('Maximum number of emails to retrieve (1-50, default: 3)'),
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Register Gmail actions with the provided controller
|
|
29
|
+
*/
|
|
30
|
+
export function registerGmailActions(controller, gmailService, accessToken) {
|
|
31
|
+
// Use provided service or create a new one with access token if provided
|
|
32
|
+
if (gmailService) {
|
|
33
|
+
_gmailService = gmailService;
|
|
34
|
+
}
|
|
35
|
+
else if (accessToken) {
|
|
36
|
+
_gmailService = new GmailService({ access_token: accessToken });
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
_gmailService = new GmailService();
|
|
40
|
+
}
|
|
41
|
+
// Register get_recent_emails action
|
|
42
|
+
controller.registry.action('Get recent emails from the mailbox with a keyword to retrieve verification codes, OTP, 2FA tokens, magic links, or any recent email content. Keep your query a single keyword.', GetRecentEmailsParams)(async (params) => {
|
|
43
|
+
try {
|
|
44
|
+
if (!_gmailService) {
|
|
45
|
+
throw new Error('Gmail service not initialized');
|
|
46
|
+
}
|
|
47
|
+
// Ensure authentication
|
|
48
|
+
if (!_gmailService.isAuthenticated()) {
|
|
49
|
+
logger.info('📧 Gmail not authenticated, attempting authentication...');
|
|
50
|
+
const authenticated = await _gmailService.authenticate();
|
|
51
|
+
if (!authenticated) {
|
|
52
|
+
return new ActionResult({
|
|
53
|
+
extracted_content: 'Failed to authenticate with Gmail. Please ensure Gmail credentials are set up properly.',
|
|
54
|
+
long_term_memory: 'Gmail authentication failed',
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Use specified max_results (1-50, default 3), last 5 minutes
|
|
59
|
+
const maxResults = params.max_results;
|
|
60
|
+
const timeFilter = '5m';
|
|
61
|
+
// Build query with time filter and optional user query
|
|
62
|
+
const queryParts = [`newer_than:${timeFilter}`];
|
|
63
|
+
if (params.keyword.trim()) {
|
|
64
|
+
queryParts.push(params.keyword.trim());
|
|
65
|
+
}
|
|
66
|
+
const query = queryParts.join(' ');
|
|
67
|
+
logger.info(`🔍 Gmail search query: ${query}`);
|
|
68
|
+
// Get emails
|
|
69
|
+
const emails = await _gmailService.getRecentEmails({
|
|
70
|
+
max_results: maxResults,
|
|
71
|
+
query: query,
|
|
72
|
+
time_filter: timeFilter,
|
|
73
|
+
});
|
|
74
|
+
if (!emails.length) {
|
|
75
|
+
const queryInfo = params.keyword.trim()
|
|
76
|
+
? ` matching '${params.keyword}'`
|
|
77
|
+
: '';
|
|
78
|
+
const memory = `No recent emails found from last ${timeFilter}${queryInfo}`;
|
|
79
|
+
return new ActionResult({
|
|
80
|
+
extracted_content: memory,
|
|
81
|
+
long_term_memory: memory,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
// Format with full email content for large display
|
|
85
|
+
let content = `Found ${emails.length} recent email${emails.length > 1 ? 's' : ''} from the last ${timeFilter}:\n\n`;
|
|
86
|
+
for (let i = 0; i < emails.length; i++) {
|
|
87
|
+
const email = emails[i];
|
|
88
|
+
content += `Email ${i + 1}:\n`;
|
|
89
|
+
content += `From: ${email.from}\n`;
|
|
90
|
+
content += `Subject: ${email.subject}\n`;
|
|
91
|
+
content += `Date: ${email.date}\n`;
|
|
92
|
+
content += `Content:\n${email.body}\n`;
|
|
93
|
+
content += '-'.repeat(50) + '\n\n';
|
|
94
|
+
}
|
|
95
|
+
logger.info(`📧 Retrieved ${emails.length} recent emails`);
|
|
96
|
+
return new ActionResult({
|
|
97
|
+
extracted_content: content,
|
|
98
|
+
include_extracted_content_only_once: true,
|
|
99
|
+
long_term_memory: `Retrieved ${emails.length} recent emails from last ${timeFilter} for query ${query}.`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
logger.error(`Error getting recent emails: ${error.message || error}`);
|
|
104
|
+
return new ActionResult({
|
|
105
|
+
error: `Error getting recent emails: ${error.message || error}`,
|
|
106
|
+
long_term_memory: 'Failed to get recent emails due to error',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
return controller;
|
|
111
|
+
}
|
|
112
|
+
// Backward compatibility export
|
|
113
|
+
export const register_gmail_actions = registerGmailActions;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gmail API Service for Browser Use
|
|
3
|
+
* Handles Gmail API authentication, email reading, and 2FA code extraction.
|
|
4
|
+
* This service provides a clean interface for agents to interact with Gmail.
|
|
5
|
+
*/
|
|
6
|
+
import type { gmail_v1 } from 'googleapis';
|
|
7
|
+
export interface EmailData {
|
|
8
|
+
id: string;
|
|
9
|
+
thread_id: string;
|
|
10
|
+
subject: string;
|
|
11
|
+
from: string;
|
|
12
|
+
to: string;
|
|
13
|
+
date: string;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
body: string;
|
|
16
|
+
raw_message: gmail_v1.Schema$Message;
|
|
17
|
+
}
|
|
18
|
+
export declare class GmailService {
|
|
19
|
+
private static readonly SCOPES;
|
|
20
|
+
private configDir;
|
|
21
|
+
private credentialsFile;
|
|
22
|
+
private tokenFile;
|
|
23
|
+
private accessToken;
|
|
24
|
+
private service;
|
|
25
|
+
private creds;
|
|
26
|
+
private _authenticated;
|
|
27
|
+
constructor(options?: {
|
|
28
|
+
credentials_file?: string;
|
|
29
|
+
token_file?: string;
|
|
30
|
+
config_dir?: string;
|
|
31
|
+
access_token?: string;
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* Check if Gmail service is authenticated
|
|
35
|
+
*/
|
|
36
|
+
isAuthenticated(): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Handle OAuth authentication and token management
|
|
39
|
+
*/
|
|
40
|
+
authenticate(): Promise<boolean>;
|
|
41
|
+
/**
|
|
42
|
+
* Get recent emails with optional query filter
|
|
43
|
+
*/
|
|
44
|
+
getRecentEmails(options?: {
|
|
45
|
+
max_results?: number;
|
|
46
|
+
query?: string;
|
|
47
|
+
time_filter?: string;
|
|
48
|
+
}): Promise<EmailData[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Parse Gmail message into readable format
|
|
51
|
+
*/
|
|
52
|
+
private _parseEmail;
|
|
53
|
+
/**
|
|
54
|
+
* Extract email body from payload
|
|
55
|
+
*/
|
|
56
|
+
private _extractBody;
|
|
57
|
+
/**
|
|
58
|
+
* Send an email
|
|
59
|
+
*/
|
|
60
|
+
sendMessage(to: string, subject: string, body: string): Promise<gmail_v1.Schema$Message | null>;
|
|
61
|
+
}
|