@projectservan8n/cnapse 0.9.0 → 0.10.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.
@@ -0,0 +1,377 @@
1
+ import {
2
+ getApiKey,
3
+ getConfig
4
+ } from "./chunk-COKO6V5J.js";
5
+
6
+ // src/lib/system.ts
7
+ import os from "os";
8
+ import { exec } from "child_process";
9
+ import { promisify } from "util";
10
+ var execAsync = promisify(exec);
11
+ var cachedSystemInfo = null;
12
+ async function getSystemInfo() {
13
+ if (cachedSystemInfo) return cachedSystemInfo;
14
+ const platform = os.platform();
15
+ const cpus = os.cpus();
16
+ let osName = platform;
17
+ const osVersion = os.release();
18
+ if (platform === "win32") {
19
+ try {
20
+ const { stdout } = await execAsync("wmic os get Caption /value", { timeout: 5e3 });
21
+ const match = stdout.match(/Caption=(.+)/);
22
+ if (match) osName = match[1].trim();
23
+ } catch {
24
+ osName = `Windows ${osVersion}`;
25
+ }
26
+ } else if (platform === "darwin") {
27
+ try {
28
+ const { stdout } = await execAsync("sw_vers -productName && sw_vers -productVersion", { timeout: 5e3 });
29
+ const lines = stdout.trim().split("\n");
30
+ osName = `${lines[0]} ${lines[1]}`;
31
+ } catch {
32
+ osName = `macOS ${osVersion}`;
33
+ }
34
+ } else if (platform === "linux") {
35
+ try {
36
+ const { stdout } = await execAsync("cat /etc/os-release | grep PRETTY_NAME", { timeout: 5e3 });
37
+ const match = stdout.match(/PRETTY_NAME="(.+)"/);
38
+ if (match) osName = match[1];
39
+ } catch {
40
+ osName = `Linux ${osVersion}`;
41
+ }
42
+ }
43
+ cachedSystemInfo = {
44
+ platform,
45
+ osName,
46
+ osVersion,
47
+ arch: os.arch(),
48
+ cpuModel: cpus[0]?.model || "Unknown CPU",
49
+ cpuCores: cpus.length,
50
+ totalMemoryGB: Math.round(os.totalmem() / 1024 ** 3 * 10) / 10,
51
+ freeMemoryGB: Math.round(os.freemem() / 1024 ** 3 * 10) / 10,
52
+ username: os.userInfo().username,
53
+ hostname: os.hostname(),
54
+ homeDir: os.homedir(),
55
+ shell: process.env.SHELL || process.env.COMSPEC || "unknown"
56
+ };
57
+ return cachedSystemInfo;
58
+ }
59
+ async function getSystemContext() {
60
+ const info = await getSystemInfo();
61
+ return `SYSTEM INFO:
62
+ - OS: ${info.osName} (${info.arch})
63
+ - CPU: ${info.cpuModel} (${info.cpuCores} cores)
64
+ - RAM: ${info.totalMemoryGB}GB total, ${info.freeMemoryGB}GB free
65
+ - User: ${info.username}@${info.hostname}
66
+ - Home: ${info.homeDir}
67
+ - Shell: ${info.shell}`;
68
+ }
69
+ function getCwd() {
70
+ return process.cwd();
71
+ }
72
+
73
+ // src/lib/api.ts
74
+ var BASE_PROMPT = `You are C-napse, an AI assistant for PC automation running on the user's desktop.
75
+ You have access to their system and can help with coding, file management, shell commands, and more.
76
+
77
+ When responding:
78
+ - Be direct and practical
79
+ - Use markdown formatting for code blocks
80
+ - If asked to do something, explain what you'll do first
81
+ - Give commands specific to the user's OS (use the system info below)
82
+ - Be aware of the user's current working directory`;
83
+ var systemContextCache = null;
84
+ async function getSystemPrompt() {
85
+ if (!systemContextCache) {
86
+ systemContextCache = await getSystemContext();
87
+ }
88
+ const cwd = getCwd();
89
+ return `${BASE_PROMPT}
90
+
91
+ ${systemContextCache}
92
+ - Current directory: ${cwd}`;
93
+ }
94
+ async function chat(messages, systemPrompt) {
95
+ const config = getConfig();
96
+ const finalPrompt = systemPrompt || await getSystemPrompt();
97
+ const allMessages = [
98
+ { role: "system", content: finalPrompt },
99
+ ...messages
100
+ ];
101
+ switch (config.provider) {
102
+ case "openrouter":
103
+ return chatOpenRouter(allMessages, config.model);
104
+ case "ollama":
105
+ return chatOllama(allMessages, config.model);
106
+ case "anthropic":
107
+ return chatAnthropic(allMessages, config.model);
108
+ case "openai":
109
+ return chatOpenAI(allMessages, config.model);
110
+ default:
111
+ throw new Error(`Unknown provider: ${config.provider}`);
112
+ }
113
+ }
114
+ async function chatOpenRouter(messages, model) {
115
+ const apiKey = getApiKey("openrouter");
116
+ if (!apiKey) {
117
+ throw new Error("OpenRouter API key not configured. Run: cnapse auth openrouter <key>");
118
+ }
119
+ const config = getConfig();
120
+ const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
121
+ method: "POST",
122
+ headers: {
123
+ "Authorization": `Bearer ${apiKey}`,
124
+ "Content-Type": "application/json",
125
+ "HTTP-Referer": config.openrouter.siteUrl,
126
+ "X-Title": config.openrouter.appName
127
+ },
128
+ body: JSON.stringify({
129
+ model,
130
+ messages,
131
+ max_tokens: 2048,
132
+ temperature: 0.7
133
+ })
134
+ });
135
+ if (!response.ok) {
136
+ const error = await response.text();
137
+ throw new Error(`OpenRouter error: ${response.status} - ${error}`);
138
+ }
139
+ const data = await response.json();
140
+ const content = data.choices?.[0]?.message?.content || "";
141
+ return { content, model };
142
+ }
143
+ async function chatOllama(messages, model) {
144
+ const config = getConfig();
145
+ const response = await fetch(`${config.ollamaHost}/api/chat`, {
146
+ method: "POST",
147
+ headers: { "Content-Type": "application/json" },
148
+ body: JSON.stringify({
149
+ model,
150
+ messages,
151
+ stream: false
152
+ })
153
+ });
154
+ if (!response.ok) {
155
+ const error = await response.text();
156
+ throw new Error(`Ollama error: ${response.status} - ${error}`);
157
+ }
158
+ const data = await response.json();
159
+ const content = data.message?.content || "";
160
+ return { content, model };
161
+ }
162
+ async function chatAnthropic(messages, model) {
163
+ const apiKey = getApiKey("anthropic");
164
+ if (!apiKey) {
165
+ throw new Error("Anthropic API key not configured. Run: cnapse auth anthropic <key>");
166
+ }
167
+ const systemMsg = messages.find((m) => m.role === "system");
168
+ const chatMessages = messages.filter((m) => m.role !== "system");
169
+ const response = await fetch("https://api.anthropic.com/v1/messages", {
170
+ method: "POST",
171
+ headers: {
172
+ "x-api-key": apiKey,
173
+ "Content-Type": "application/json",
174
+ "anthropic-version": "2023-06-01"
175
+ },
176
+ body: JSON.stringify({
177
+ model,
178
+ max_tokens: 2048,
179
+ system: systemMsg?.content || "",
180
+ messages: chatMessages
181
+ })
182
+ });
183
+ if (!response.ok) {
184
+ const error = await response.text();
185
+ throw new Error(`Anthropic error: ${response.status} - ${error}`);
186
+ }
187
+ const data = await response.json();
188
+ const content = data.content?.[0]?.text || "";
189
+ return { content, model };
190
+ }
191
+ async function chatOpenAI(messages, model) {
192
+ const apiKey = getApiKey("openai");
193
+ if (!apiKey) {
194
+ throw new Error("OpenAI API key not configured. Run: cnapse auth openai <key>");
195
+ }
196
+ const response = await fetch("https://api.openai.com/v1/chat/completions", {
197
+ method: "POST",
198
+ headers: {
199
+ "Authorization": `Bearer ${apiKey}`,
200
+ "Content-Type": "application/json"
201
+ },
202
+ body: JSON.stringify({
203
+ model,
204
+ messages,
205
+ max_tokens: 2048,
206
+ temperature: 0.7
207
+ })
208
+ });
209
+ if (!response.ok) {
210
+ const error = await response.text();
211
+ throw new Error(`OpenAI error: ${response.status} - ${error}`);
212
+ }
213
+ const data = await response.json();
214
+ const content = data.choices?.[0]?.message?.content || "";
215
+ return { content, model };
216
+ }
217
+ async function chatWithVision(messages, screenshotBase64) {
218
+ const config = getConfig();
219
+ const systemPrompt = await getSystemPrompt();
220
+ const visionPrompt = systemPrompt + "\n\nYou can see the user's screen. Describe what you see and help them with their request.";
221
+ switch (config.provider) {
222
+ case "openrouter":
223
+ return chatWithVisionOpenRouter(messages, screenshotBase64, visionPrompt);
224
+ case "ollama":
225
+ return chatWithVisionOllama(messages, screenshotBase64, visionPrompt);
226
+ case "anthropic":
227
+ return chatWithVisionAnthropic(messages, screenshotBase64, visionPrompt);
228
+ case "openai":
229
+ return chatWithVisionOpenAI(messages, screenshotBase64, visionPrompt);
230
+ default:
231
+ throw new Error(`Vision not supported for provider: ${config.provider}`);
232
+ }
233
+ }
234
+ async function chatWithVisionOpenRouter(messages, screenshot, systemPrompt) {
235
+ const apiKey = getApiKey("openrouter");
236
+ if (!apiKey) throw new Error("OpenRouter API key not configured");
237
+ const config = getConfig();
238
+ let model = config.model;
239
+ if (!model.includes("gpt-5") && !model.includes("claude") && !model.includes("gemini")) {
240
+ model = "openai/gpt-5-nano";
241
+ }
242
+ const lastUserIdx = messages.length - 1;
243
+ const visionMessages = messages.map((m, i) => {
244
+ if (i === lastUserIdx && m.role === "user") {
245
+ return {
246
+ role: "user",
247
+ content: [
248
+ { type: "text", text: m.content },
249
+ { type: "image_url", image_url: { url: `data:image/png;base64,${screenshot}` } }
250
+ ]
251
+ };
252
+ }
253
+ return m;
254
+ });
255
+ const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
256
+ method: "POST",
257
+ headers: {
258
+ "Authorization": `Bearer ${apiKey}`,
259
+ "Content-Type": "application/json",
260
+ "HTTP-Referer": config.openrouter.siteUrl,
261
+ "X-Title": config.openrouter.appName
262
+ },
263
+ body: JSON.stringify({
264
+ model,
265
+ messages: [{ role: "system", content: systemPrompt }, ...visionMessages],
266
+ max_tokens: 2048
267
+ })
268
+ });
269
+ if (!response.ok) {
270
+ const error = await response.text();
271
+ throw new Error(`OpenRouter vision error: ${response.status} - ${error}`);
272
+ }
273
+ const data = await response.json();
274
+ return { content: data.choices?.[0]?.message?.content || "", model };
275
+ }
276
+ async function chatWithVisionOllama(messages, screenshot, systemPrompt) {
277
+ const config = getConfig();
278
+ const visionModels = ["llava", "llama3.2-vision", "bakllava"];
279
+ const model = visionModels.find((m) => config.model.includes(m)) || "llava";
280
+ const lastUserMsg = messages.filter((m) => m.role === "user").pop();
281
+ const response = await fetch(`${config.ollamaHost}/api/generate`, {
282
+ method: "POST",
283
+ headers: { "Content-Type": "application/json" },
284
+ body: JSON.stringify({
285
+ model,
286
+ prompt: `${systemPrompt}
287
+
288
+ User: ${lastUserMsg?.content || "What do you see?"}`,
289
+ images: [screenshot],
290
+ stream: false
291
+ })
292
+ });
293
+ if (!response.ok) {
294
+ const error = await response.text();
295
+ throw new Error(`Ollama vision error: ${error}`);
296
+ }
297
+ const data = await response.json();
298
+ return { content: data.response || "", model };
299
+ }
300
+ async function chatWithVisionAnthropic(messages, screenshot, systemPrompt) {
301
+ const apiKey = getApiKey("anthropic");
302
+ if (!apiKey) throw new Error("Anthropic API key not configured");
303
+ const chatMessages = messages.filter((m) => m.role !== "system");
304
+ const lastUserIdx = chatMessages.length - 1;
305
+ const visionMessages = chatMessages.map((m, i) => {
306
+ if (i === lastUserIdx && m.role === "user") {
307
+ return {
308
+ role: "user",
309
+ content: [
310
+ { type: "image", source: { type: "base64", media_type: "image/png", data: screenshot } },
311
+ { type: "text", text: m.content }
312
+ ]
313
+ };
314
+ }
315
+ return { role: m.role, content: m.content };
316
+ });
317
+ const response = await fetch("https://api.anthropic.com/v1/messages", {
318
+ method: "POST",
319
+ headers: {
320
+ "x-api-key": apiKey,
321
+ "anthropic-version": "2023-06-01",
322
+ "Content-Type": "application/json"
323
+ },
324
+ body: JSON.stringify({
325
+ model: "claude-3-5-sonnet-20241022",
326
+ max_tokens: 2048,
327
+ system: systemPrompt,
328
+ messages: visionMessages
329
+ })
330
+ });
331
+ if (!response.ok) {
332
+ const error = await response.text();
333
+ throw new Error(`Anthropic vision error: ${error}`);
334
+ }
335
+ const data = await response.json();
336
+ return { content: data.content?.[0]?.text || "", model: "claude-3-5-sonnet-20241022" };
337
+ }
338
+ async function chatWithVisionOpenAI(messages, screenshot, systemPrompt) {
339
+ const apiKey = getApiKey("openai");
340
+ if (!apiKey) throw new Error("OpenAI API key not configured");
341
+ const lastUserIdx = messages.length - 1;
342
+ const visionMessages = messages.map((m, i) => {
343
+ if (i === lastUserIdx && m.role === "user") {
344
+ return {
345
+ role: "user",
346
+ content: [
347
+ { type: "text", text: m.content },
348
+ { type: "image_url", image_url: { url: `data:image/png;base64,${screenshot}` } }
349
+ ]
350
+ };
351
+ }
352
+ return m;
353
+ });
354
+ const response = await fetch("https://api.openai.com/v1/chat/completions", {
355
+ method: "POST",
356
+ headers: {
357
+ "Authorization": `Bearer ${apiKey}`,
358
+ "Content-Type": "application/json"
359
+ },
360
+ body: JSON.stringify({
361
+ model: "gpt-4o",
362
+ messages: [{ role: "system", content: systemPrompt }, ...visionMessages],
363
+ max_tokens: 2048
364
+ })
365
+ });
366
+ if (!response.ok) {
367
+ const error = await response.text();
368
+ throw new Error(`OpenAI vision error: ${error}`);
369
+ }
370
+ const data = await response.json();
371
+ return { content: data.choices?.[0]?.message?.content || "", model: "gpt-4o" };
372
+ }
373
+
374
+ export {
375
+ chat,
376
+ chatWithVision
377
+ };
@@ -0,0 +1,344 @@
1
+ import {
2
+ clickMouse,
3
+ keyCombo,
4
+ pressKey,
5
+ runCommand,
6
+ typeText
7
+ } from "./chunk-TFHK5CYF.js";
8
+ import {
9
+ captureScreenshot,
10
+ describeScreen
11
+ } from "./chunk-OIVTPXE4.js";
12
+
13
+ // src/services/browser.ts
14
+ function sleep(ms) {
15
+ return new Promise((resolve) => setTimeout(resolve, ms));
16
+ }
17
+ async function openUrl(url) {
18
+ const fullUrl = url.startsWith("http") ? url : `https://${url}`;
19
+ try {
20
+ if (process.platform === "win32") {
21
+ await runCommand(`start "" "${fullUrl}"`, 5e3);
22
+ } else if (process.platform === "darwin") {
23
+ await runCommand(`open "${fullUrl}"`, 5e3);
24
+ } else {
25
+ await runCommand(`xdg-open "${fullUrl}"`, 5e3);
26
+ }
27
+ return { success: true };
28
+ } catch (error) {
29
+ return {
30
+ success: false,
31
+ error: error instanceof Error ? error.message : "Failed to open URL"
32
+ };
33
+ }
34
+ }
35
+ async function navigateTo(url) {
36
+ await openUrl(url);
37
+ }
38
+ async function searchGoogle(query) {
39
+ const searchUrl = `https://www.google.com/search?q=${encodeURIComponent(query)}`;
40
+ return openUrl(searchUrl);
41
+ }
42
+ async function webSearch(query, engine = "google") {
43
+ const urls = {
44
+ google: `https://www.google.com/search?q=${encodeURIComponent(query)}`,
45
+ bing: `https://www.bing.com/search?q=${encodeURIComponent(query)}`,
46
+ duckduckgo: `https://duckduckgo.com/?q=${encodeURIComponent(query)}`
47
+ };
48
+ await openUrl(urls[engine]);
49
+ await sleep(3e3);
50
+ const vision = await describeScreen();
51
+ return `\u{1F50D} Search results for "${query}":
52
+
53
+ ${vision.description}`;
54
+ }
55
+ async function askAI(site, question) {
56
+ const urls = {
57
+ perplexity: "https://www.perplexity.ai",
58
+ chatgpt: "https://chat.openai.com",
59
+ claude: "https://claude.ai",
60
+ copilot: "https://copilot.microsoft.com",
61
+ google: "https://www.google.com"
62
+ };
63
+ await openUrl(urls[site]);
64
+ await sleep(4e3);
65
+ await typeText(question);
66
+ await sleep(500);
67
+ await pressKey("Return");
68
+ await sleep(site === "google" ? 3e3 : 1e4);
69
+ const vision = await describeScreen();
70
+ return {
71
+ response: vision.description,
72
+ screenshot: vision.screenshot
73
+ };
74
+ }
75
+ async function openGmailCompose(to, subject, body) {
76
+ let url = "https://mail.google.com/mail/u/0/?fs=1&tf=cm";
77
+ if (to) url += `&to=${encodeURIComponent(to)}`;
78
+ if (subject) url += `&su=${encodeURIComponent(subject)}`;
79
+ if (body) url += `&body=${encodeURIComponent(body)}`;
80
+ const result = await openUrl(url);
81
+ return result.success;
82
+ }
83
+ async function sendGmail(email) {
84
+ try {
85
+ await openGmailCompose(email.to, email.subject, email.body);
86
+ await sleep(5e3);
87
+ await keyCombo(["control", "Return"]);
88
+ await sleep(2e3);
89
+ return true;
90
+ } catch {
91
+ return false;
92
+ }
93
+ }
94
+ async function openOutlookCompose(to, subject, body) {
95
+ let url = "https://outlook.office.com/mail/deeplink/compose?";
96
+ if (to) url += `to=${encodeURIComponent(to)}&`;
97
+ if (subject) url += `subject=${encodeURIComponent(subject)}&`;
98
+ if (body) url += `body=${encodeURIComponent(body)}&`;
99
+ const result = await openUrl(url);
100
+ return result.success;
101
+ }
102
+ async function sendOutlook(email) {
103
+ try {
104
+ await openOutlookCompose(email.to, email.subject, email.body);
105
+ await sleep(5e3);
106
+ await keyCombo(["control", "Return"]);
107
+ await sleep(2e3);
108
+ return true;
109
+ } catch {
110
+ return false;
111
+ }
112
+ }
113
+ async function openGoogleSheet() {
114
+ const result = await openUrl("https://docs.google.com/spreadsheets/create");
115
+ return result.success;
116
+ }
117
+ async function openGoogleDoc() {
118
+ const result = await openUrl("https://docs.google.com/document/create");
119
+ return result.success;
120
+ }
121
+ async function typeInBrowser(text) {
122
+ await typeText(text);
123
+ }
124
+ async function pressKey2(key) {
125
+ await pressKey(key);
126
+ }
127
+ async function click(button = "left") {
128
+ await clickMouse(button);
129
+ }
130
+ async function scroll(direction, amount = 3) {
131
+ const key = direction === "down" ? "pagedown" : "pageup";
132
+ for (let i = 0; i < amount; i++) {
133
+ await pressKey(key);
134
+ await sleep(200);
135
+ }
136
+ }
137
+ async function takeScreenshot() {
138
+ return await captureScreenshot();
139
+ }
140
+ async function getPageText() {
141
+ const vision = await describeScreen();
142
+ return vision.description;
143
+ }
144
+ async function research(topic, maxSources = 3) {
145
+ await searchGoogle(topic);
146
+ await sleep(3e3);
147
+ const searchResults = await describeScreen();
148
+ return {
149
+ query: topic,
150
+ sources: [{
151
+ title: `Google search: ${topic}`,
152
+ url: `https://www.google.com/search?q=${encodeURIComponent(topic)}`,
153
+ content: searchResults.description
154
+ }],
155
+ summary: searchResults.description
156
+ };
157
+ }
158
+ async function closeTab() {
159
+ await keyCombo(["control", "w"]);
160
+ }
161
+ async function newTab() {
162
+ await keyCombo(["control", "t"]);
163
+ }
164
+ async function nextTab() {
165
+ await keyCombo(["control", "Tab"]);
166
+ }
167
+ async function goBack() {
168
+ await keyCombo(["alt", "Left"]);
169
+ }
170
+ async function goForward() {
171
+ await keyCombo(["alt", "Right"]);
172
+ }
173
+ async function refresh() {
174
+ await pressKey("F5");
175
+ }
176
+ async function focusAddressBar() {
177
+ await keyCombo(["control", "l"]);
178
+ }
179
+ async function typeUrl(url) {
180
+ await focusAddressBar();
181
+ await sleep(300);
182
+ await typeText(url);
183
+ await sleep(200);
184
+ await pressKey("Return");
185
+ }
186
+ async function initBrowser() {
187
+ return null;
188
+ }
189
+ async function getPage() {
190
+ return null;
191
+ }
192
+ async function closeBrowser() {
193
+ await keyCombo(["alt", "F4"]);
194
+ }
195
+ async function elementExists(selector) {
196
+ return true;
197
+ }
198
+ async function clickElement(selector) {
199
+ await click();
200
+ return true;
201
+ }
202
+ async function typeInElement(selector, text) {
203
+ await typeInBrowser(text);
204
+ return true;
205
+ }
206
+ async function typeSlowly(selector, text) {
207
+ for (const char of text) {
208
+ await typeText(char);
209
+ await sleep(50);
210
+ }
211
+ return true;
212
+ }
213
+ async function waitForText(text) {
214
+ await sleep(3e3);
215
+ return true;
216
+ }
217
+ async function getTextContent(selector) {
218
+ const vision = await describeScreen();
219
+ return vision.description;
220
+ }
221
+ async function waitForNavigation() {
222
+ await sleep(3e3);
223
+ }
224
+ async function getFullAIResponse(site, maxScrolls = 5) {
225
+ const responses = [];
226
+ for (let i = 0; i < maxScrolls; i++) {
227
+ const vision = await describeScreen();
228
+ responses.push(vision.description);
229
+ await scroll("down", 1);
230
+ await sleep(1e3);
231
+ }
232
+ return responses;
233
+ }
234
+ async function googleSheetsType(cells) {
235
+ try {
236
+ for (const { cell, value } of cells) {
237
+ await keyCombo(["control", "g"]);
238
+ await sleep(500);
239
+ await typeText(cell);
240
+ await pressKey("Return");
241
+ await sleep(300);
242
+ await typeText(value);
243
+ await pressKey("Return");
244
+ await sleep(200);
245
+ }
246
+ return true;
247
+ } catch {
248
+ return false;
249
+ }
250
+ }
251
+ async function googleDocsType(text) {
252
+ try {
253
+ await sleep(1e3);
254
+ await typeText(text);
255
+ return true;
256
+ } catch {
257
+ return false;
258
+ }
259
+ }
260
+ var browser_default = {
261
+ openUrl,
262
+ navigateTo,
263
+ searchGoogle,
264
+ webSearch,
265
+ askAI,
266
+ openGmailCompose,
267
+ sendGmail,
268
+ openOutlookCompose,
269
+ sendOutlook,
270
+ openGoogleSheet,
271
+ openGoogleDoc,
272
+ typeInBrowser,
273
+ pressKey: pressKey2,
274
+ click,
275
+ scroll,
276
+ takeScreenshot,
277
+ getPageText,
278
+ research,
279
+ closeTab,
280
+ newTab,
281
+ nextTab,
282
+ goBack,
283
+ goForward,
284
+ refresh,
285
+ focusAddressBar,
286
+ typeUrl,
287
+ // Legacy compatibility
288
+ initBrowser,
289
+ getPage,
290
+ closeBrowser,
291
+ elementExists,
292
+ clickElement,
293
+ typeInElement,
294
+ typeSlowly,
295
+ waitForText,
296
+ getTextContent,
297
+ waitForNavigation,
298
+ getFullAIResponse,
299
+ googleSheetsType,
300
+ googleDocsType
301
+ };
302
+
303
+ export {
304
+ openUrl,
305
+ navigateTo,
306
+ searchGoogle,
307
+ webSearch,
308
+ askAI,
309
+ openGmailCompose,
310
+ sendGmail,
311
+ openOutlookCompose,
312
+ sendOutlook,
313
+ openGoogleSheet,
314
+ openGoogleDoc,
315
+ typeInBrowser,
316
+ pressKey2 as pressKey,
317
+ click,
318
+ scroll,
319
+ takeScreenshot,
320
+ getPageText,
321
+ research,
322
+ closeTab,
323
+ newTab,
324
+ nextTab,
325
+ goBack,
326
+ goForward,
327
+ refresh,
328
+ focusAddressBar,
329
+ typeUrl,
330
+ initBrowser,
331
+ getPage,
332
+ closeBrowser,
333
+ elementExists,
334
+ clickElement,
335
+ typeInElement,
336
+ typeSlowly,
337
+ waitForText,
338
+ getTextContent,
339
+ waitForNavigation,
340
+ getFullAIResponse,
341
+ googleSheetsType,
342
+ googleDocsType,
343
+ browser_default
344
+ };