verybot 0.1.3
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.
Potentially problematic release.
This version of verybot might be problematic. Click here for more details.
- package/dist/brain/agent-registry.d.ts +75 -0
- package/dist/brain/agent-registry.js +124 -0
- package/dist/brain/agent.d.ts +146 -0
- package/dist/brain/agent.js +680 -0
- package/dist/brain/channel-store.d.ts +27 -0
- package/dist/brain/channel-store.js +78 -0
- package/dist/brain/compaction.d.ts +37 -0
- package/dist/brain/compaction.js +214 -0
- package/dist/brain/context.d.ts +33 -0
- package/dist/brain/context.js +77 -0
- package/dist/brain/delegation-store.d.ts +33 -0
- package/dist/brain/delegation-store.js +106 -0
- package/dist/brain/loop.d.ts +21 -0
- package/dist/brain/loop.js +161 -0
- package/dist/brain/mcp-adapter.d.ts +39 -0
- package/dist/brain/mcp-adapter.js +227 -0
- package/dist/brain/memory-extractor.d.ts +26 -0
- package/dist/brain/memory-extractor.js +82 -0
- package/dist/brain/providers.d.ts +10 -0
- package/dist/brain/providers.js +69 -0
- package/dist/brain/queue.d.ts +18 -0
- package/dist/brain/queue.js +84 -0
- package/dist/brain/run-tools.d.ts +47 -0
- package/dist/brain/run-tools.js +84 -0
- package/dist/brain/session-key.d.ts +23 -0
- package/dist/brain/session-key.js +41 -0
- package/dist/brain/session-state.d.ts +36 -0
- package/dist/brain/session-state.js +51 -0
- package/dist/brain/session-store.d.ts +50 -0
- package/dist/brain/session-store.js +207 -0
- package/dist/brain/session.d.ts +32 -0
- package/dist/brain/session.js +75 -0
- package/dist/brain/utils.d.ts +4 -0
- package/dist/brain/utils.js +26 -0
- package/dist/brain/worker-coordinator.d.ts +25 -0
- package/dist/brain/worker-coordinator.js +83 -0
- package/dist/channels/commands.d.ts +35 -0
- package/dist/channels/commands.js +65 -0
- package/dist/channels/discord/channel.d.ts +18 -0
- package/dist/channels/discord/channel.js +154 -0
- package/dist/channels/discord/markdown.d.ts +19 -0
- package/dist/channels/discord/markdown.js +62 -0
- package/dist/channels/manager.d.ts +29 -0
- package/dist/channels/manager.js +100 -0
- package/dist/channels/slack/channel.d.ts +26 -0
- package/dist/channels/slack/channel.js +207 -0
- package/dist/channels/slack/markdown.d.ts +19 -0
- package/dist/channels/slack/markdown.js +62 -0
- package/dist/channels/specs.d.ts +21 -0
- package/dist/channels/specs.js +96 -0
- package/dist/channels/telegram/channel.d.ts +18 -0
- package/dist/channels/telegram/channel.js +156 -0
- package/dist/channels/telegram/markdown.d.ts +17 -0
- package/dist/channels/telegram/markdown.js +66 -0
- package/dist/channels/types.d.ts +26 -0
- package/dist/channels/types.js +1 -0
- package/dist/channels/whatsapp/channel.d.ts +23 -0
- package/dist/channels/whatsapp/channel.js +242 -0
- package/dist/channels/whatsapp/markdown.d.ts +20 -0
- package/dist/channels/whatsapp/markdown.js +51 -0
- package/dist/cli/config.d.ts +5 -0
- package/dist/cli/config.js +78 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +13 -0
- package/dist/computer/browser/actions.d.ts +31 -0
- package/dist/computer/browser/actions.js +148 -0
- package/dist/computer/browser/manager.d.ts +55 -0
- package/dist/computer/browser/manager.js +496 -0
- package/dist/computer/browser/profile-badge.d.ts +13 -0
- package/dist/computer/browser/profile-badge.js +67 -0
- package/dist/computer/browser/screenshot.d.ts +5 -0
- package/dist/computer/browser/screenshot.js +21 -0
- package/dist/computer/browser/snapshot.d.ts +30 -0
- package/dist/computer/browser/snapshot.js +242 -0
- package/dist/computer/browser/tools.d.ts +5 -0
- package/dist/computer/browser/tools.js +167 -0
- package/dist/computer/desktop/adapter.d.ts +25 -0
- package/dist/computer/desktop/adapter.js +11 -0
- package/dist/computer/desktop/macos.d.ts +24 -0
- package/dist/computer/desktop/macos.js +223 -0
- package/dist/computer/desktop/tools.d.ts +25 -0
- package/dist/computer/desktop/tools.js +114 -0
- package/dist/config/agent-config.d.ts +41 -0
- package/dist/config/agent-config.js +14 -0
- package/dist/config/model-catalog.d.ts +22 -0
- package/dist/config/model-catalog.js +99 -0
- package/dist/config/store.d.ts +25 -0
- package/dist/config/store.js +143 -0
- package/dist/config.d.ts +103 -0
- package/dist/config.js +224 -0
- package/dist/control-ui/assets/index-BANXNUyt.js +143 -0
- package/dist/control-ui/assets/index-BSUFrP9R.css +1 -0
- package/dist/control-ui/assets/noto-sans-cyrillic-ext-wght-normal-DSNfmdVt.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-cyrillic-wght-normal-B2hlT84T.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-devanagari-wght-normal-Cv-Vwajv.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-greek-ext-wght-normal-12T8GTDR.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-greek-wght-normal-Ymb6dZNd.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-latin-ext-wght-normal-W1qJv59z.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-latin-wght-normal-BYSzYMf3.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-vietnamese-wght-normal-DLTJy58D.woff2 +0 -0
- package/dist/control-ui/index.html +14 -0
- package/dist/control-ui/vite.svg +1 -0
- package/dist/events.d.ts +2 -0
- package/dist/events.js +11 -0
- package/dist/gateway/broadcast.d.ts +5 -0
- package/dist/gateway/broadcast.js +33 -0
- package/dist/gateway/methods/chat.d.ts +24 -0
- package/dist/gateway/methods/chat.js +19 -0
- package/dist/gateway/methods/config.d.ts +13 -0
- package/dist/gateway/methods/config.js +14 -0
- package/dist/gateway/methods/models.d.ts +10 -0
- package/dist/gateway/methods/models.js +14 -0
- package/dist/gateway/methods/prompt-templates.d.ts +23 -0
- package/dist/gateway/methods/prompt-templates.js +82 -0
- package/dist/gateway/methods/scheduler.d.ts +62 -0
- package/dist/gateway/methods/scheduler.js +129 -0
- package/dist/gateway/methods/sessions.d.ts +26 -0
- package/dist/gateway/methods/sessions.js +54 -0
- package/dist/gateway/methods/skills.d.ts +35 -0
- package/dist/gateway/methods/skills.js +202 -0
- package/dist/gateway/methods/system.d.ts +12 -0
- package/dist/gateway/methods/system.js +39 -0
- package/dist/gateway/methods/tasks.d.ts +21 -0
- package/dist/gateway/methods/tasks.js +46 -0
- package/dist/gateway/methods/teams.d.ts +70 -0
- package/dist/gateway/methods/teams.js +374 -0
- package/dist/gateway/methods/tools.d.ts +6 -0
- package/dist/gateway/methods/tools.js +7 -0
- package/dist/gateway/methods/whatsapp.d.ts +19 -0
- package/dist/gateway/methods/whatsapp.js +35 -0
- package/dist/gateway/rpc.d.ts +38 -0
- package/dist/gateway/rpc.js +75 -0
- package/dist/gateway/server.d.ts +4 -0
- package/dist/gateway/server.js +133 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +212 -0
- package/dist/integrations/github.d.ts +7 -0
- package/dist/integrations/github.js +133 -0
- package/dist/integrations/mcp.d.ts +7 -0
- package/dist/integrations/mcp.js +106 -0
- package/dist/integrations/registry.d.ts +43 -0
- package/dist/integrations/registry.js +258 -0
- package/dist/integrations/scanner.d.ts +10 -0
- package/dist/integrations/scanner.js +122 -0
- package/dist/integrations/twitter.d.ts +10 -0
- package/dist/integrations/twitter.js +120 -0
- package/dist/integrations/types.d.ts +72 -0
- package/dist/integrations/types.js +1 -0
- package/dist/logger.d.ts +16 -0
- package/dist/logger.js +104 -0
- package/dist/markdown/chunk.d.ts +9 -0
- package/dist/markdown/chunk.js +52 -0
- package/dist/markdown/ir.d.ts +37 -0
- package/dist/markdown/ir.js +529 -0
- package/dist/markdown/render.d.ts +22 -0
- package/dist/markdown/render.js +148 -0
- package/dist/markdown/table-render.d.ts +43 -0
- package/dist/markdown/table-render.js +219 -0
- package/dist/markdown/tables.d.ts +17 -0
- package/dist/markdown/tables.js +27 -0
- package/dist/memory/embedding.d.ts +16 -0
- package/dist/memory/embedding.js +66 -0
- package/dist/memory/extractor.d.ts +6 -0
- package/dist/memory/extractor.js +72 -0
- package/dist/memory/search.d.ts +15 -0
- package/dist/memory/search.js +57 -0
- package/dist/memory/store.d.ts +34 -0
- package/dist/memory/store.js +328 -0
- package/dist/memory/types.d.ts +9 -0
- package/dist/memory/types.js +2 -0
- package/dist/paths.d.ts +20 -0
- package/dist/paths.js +29 -0
- package/dist/prompt-templates/builtins.d.ts +2 -0
- package/dist/prompt-templates/builtins.js +72 -0
- package/dist/prompt-templates/store.d.ts +39 -0
- package/dist/prompt-templates/store.js +174 -0
- package/dist/prompt-templates/types.d.ts +10 -0
- package/dist/prompt-templates/types.js +1 -0
- package/dist/scheduler/connected-channels.d.ts +24 -0
- package/dist/scheduler/connected-channels.js +57 -0
- package/dist/scheduler/scheduler.d.ts +22 -0
- package/dist/scheduler/scheduler.js +132 -0
- package/dist/scheduler/store.d.ts +27 -0
- package/dist/scheduler/store.js +205 -0
- package/dist/scheduler/types.d.ts +29 -0
- package/dist/scheduler/types.js +1 -0
- package/dist/security/command-validator.d.ts +22 -0
- package/dist/security/command-validator.js +160 -0
- package/dist/security/docker-sandbox.d.ts +48 -0
- package/dist/security/docker-sandbox.js +218 -0
- package/dist/security/env-filter.d.ts +8 -0
- package/dist/security/env-filter.js +41 -0
- package/dist/skills/loader.d.ts +33 -0
- package/dist/skills/loader.js +132 -0
- package/dist/skills/prompt.d.ts +6 -0
- package/dist/skills/prompt.js +17 -0
- package/dist/skills/read-tool.d.ts +7 -0
- package/dist/skills/read-tool.js +24 -0
- package/dist/skills/scanner.d.ts +6 -0
- package/dist/skills/scanner.js +73 -0
- package/dist/skills/types.d.ts +15 -0
- package/dist/skills/types.js +1 -0
- package/dist/tasks/store.d.ts +47 -0
- package/dist/tasks/store.js +193 -0
- package/dist/tasks/types.d.ts +75 -0
- package/dist/tasks/types.js +32 -0
- package/dist/teams/store.d.ts +78 -0
- package/dist/teams/store.js +420 -0
- package/dist/teams/types.d.ts +23 -0
- package/dist/teams/types.js +1 -0
- package/dist/tools/bash.d.ts +16 -0
- package/dist/tools/bash.js +62 -0
- package/dist/tools/channel-history.d.ts +10 -0
- package/dist/tools/channel-history.js +43 -0
- package/dist/tools/delegate.d.ts +16 -0
- package/dist/tools/delegate.js +216 -0
- package/dist/tools/fs.d.ts +4 -0
- package/dist/tools/fs.js +335 -0
- package/dist/tools/integration-toggle.d.ts +14 -0
- package/dist/tools/integration-toggle.js +47 -0
- package/dist/tools/memory.d.ts +13 -0
- package/dist/tools/memory.js +65 -0
- package/dist/tools/registry.d.ts +6 -0
- package/dist/tools/registry.js +9 -0
- package/dist/tools/schedule.d.ts +8 -0
- package/dist/tools/schedule.js +219 -0
- package/dist/tools/speak.d.ts +10 -0
- package/dist/tools/speak.js +56 -0
- package/dist/tools/tasks.d.ts +29 -0
- package/dist/tools/tasks.js +92 -0
- package/dist/tools/teams.d.ts +7 -0
- package/dist/tools/teams.js +180 -0
- package/dist/tools/web-fetch.d.ts +3 -0
- package/dist/tools/web-fetch.js +22 -0
- package/dist/tts/edge.d.ts +10 -0
- package/dist/tts/edge.js +60 -0
- package/dist/tts/speak.d.ts +12 -0
- package/dist/tts/speak.js +81 -0
- package/dist/tts/transcribe.d.ts +5 -0
- package/dist/tts/transcribe.js +40 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +22 -0
- package/package.json +90 -0
- package/verybot.js +2 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ConfigStore } from "../config/store.js";
|
|
2
|
+
import { seedConfigStore, generateGatewayToken } from "../config.js";
|
|
3
|
+
import { BASE_DIR } from "../paths.js";
|
|
4
|
+
/** Resolve a dot-separated path on a nested object. */
|
|
5
|
+
function getByPath(obj, path) {
|
|
6
|
+
return path.split(".").reduce((o, k) => (o && typeof o === "object" ? o[k] : undefined), obj);
|
|
7
|
+
}
|
|
8
|
+
/** Build a nested partial object from a dot-separated path and a value. */
|
|
9
|
+
function setByPath(path, value) {
|
|
10
|
+
const keys = path.split(".");
|
|
11
|
+
if (keys.length === 1)
|
|
12
|
+
return { [path]: value };
|
|
13
|
+
const root = {};
|
|
14
|
+
let current = root;
|
|
15
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
16
|
+
current[keys[i]] = {};
|
|
17
|
+
current = current[keys[i]];
|
|
18
|
+
}
|
|
19
|
+
current[keys[keys.length - 1]] = value;
|
|
20
|
+
return root;
|
|
21
|
+
}
|
|
22
|
+
/** Auto-coerce CLI string values to native types. */
|
|
23
|
+
function coerce(raw) {
|
|
24
|
+
if (raw === "true")
|
|
25
|
+
return true;
|
|
26
|
+
if (raw === "false")
|
|
27
|
+
return false;
|
|
28
|
+
if (raw === "null")
|
|
29
|
+
return null;
|
|
30
|
+
const num = Number(raw);
|
|
31
|
+
if (!Number.isNaN(num) && raw.trim() !== "")
|
|
32
|
+
return num;
|
|
33
|
+
return raw;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Handle `config get [KEY]` and `config set KEY VALUE` CLI subcommands.
|
|
37
|
+
* Operates directly on config.json — no gateway boot needed.
|
|
38
|
+
*/
|
|
39
|
+
export function handleConfigCommand(sub, rest) {
|
|
40
|
+
const store = new ConfigStore(BASE_DIR);
|
|
41
|
+
seedConfigStore(store);
|
|
42
|
+
if (sub === "get") {
|
|
43
|
+
const key = rest[0];
|
|
44
|
+
if (!key) {
|
|
45
|
+
console.log(JSON.stringify(store.getRedacted(), null, 2));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const data = store.load();
|
|
49
|
+
const value = getByPath(data, key);
|
|
50
|
+
if (value === undefined) {
|
|
51
|
+
console.error(`Key "${key}" not found in config`);
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
console.log(typeof value === "string" ? value : JSON.stringify(value, null, 2));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (sub === "set") {
|
|
59
|
+
const [key, ...valueParts] = rest;
|
|
60
|
+
if (!key || valueParts.length === 0) {
|
|
61
|
+
console.error("Usage: config set KEY VALUE");
|
|
62
|
+
process.exitCode = 1;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const value = coerce(valueParts.join(" "));
|
|
66
|
+
store.patch(setByPath(key, value));
|
|
67
|
+
console.log(`Set ${key}`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (sub === "regenerate_gateway_token") {
|
|
71
|
+
const token = generateGatewayToken();
|
|
72
|
+
store.patch({ GATEWAY_TOKEN: token });
|
|
73
|
+
console.log(token);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
console.error("Usage: config get [KEY] | config set KEY VALUE | config regenerate_gateway_token");
|
|
77
|
+
process.exitCode = 1;
|
|
78
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { handleConfigCommand } from "./config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Route lightweight CLI subcommands without booting the gateway.
|
|
4
|
+
* Returns true if a subcommand was handled.
|
|
5
|
+
*/
|
|
6
|
+
export function handleCli() {
|
|
7
|
+
const [cmd, sub, ...rest] = process.argv.slice(2);
|
|
8
|
+
if (cmd === "config") {
|
|
9
|
+
handleConfigCommand(sub, rest);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin wrappers around Playwright APIs that use BrowserManager for state.
|
|
3
|
+
* Each action returns a serializable result suitable for tool output.
|
|
4
|
+
*/
|
|
5
|
+
import type { BrowserManager } from "./manager.js";
|
|
6
|
+
/** Prefix a tool output string with the active profile name. */
|
|
7
|
+
export declare function withProfilePrefix(browser: BrowserManager, text: string): string;
|
|
8
|
+
/** Navigate to a URL. Returns the page URL + title + snapshot. */
|
|
9
|
+
export declare function navigate(browser: BrowserManager, url: string): Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Take an a11y snapshot of the current page, assign refs, store them in manager.
|
|
12
|
+
* Returns raw snapshot text without a profile prefix — callers add the prefix.
|
|
13
|
+
*/
|
|
14
|
+
export declare function takeSnapshot(browser: BrowserManager): Promise<string>;
|
|
15
|
+
/** Take a screenshot, compress it, return base64 + mediaType. */
|
|
16
|
+
export declare function takeScreenshot(browser: BrowserManager, fullPage?: boolean): Promise<{
|
|
17
|
+
base64: string;
|
|
18
|
+
mediaType: string;
|
|
19
|
+
}>;
|
|
20
|
+
/** Click an element by ref. Returns confirmation + auto-snapshot. */
|
|
21
|
+
export declare function click(browser: BrowserManager, ref: string): Promise<string>;
|
|
22
|
+
/** Type text into an input by ref. Optionally submit with Enter. */
|
|
23
|
+
export declare function type(browser: BrowserManager, ref: string, text: string, submit?: boolean): Promise<string>;
|
|
24
|
+
/** Press a keyboard key or combo (e.g. "Enter", "Control+A"). */
|
|
25
|
+
export declare function pressKey(browser: BrowserManager, key: string): Promise<string>;
|
|
26
|
+
/** Switch the browser to a different named profile. Closes the current browser. */
|
|
27
|
+
export declare function switchProfile(browser: BrowserManager, name: string): Promise<string>;
|
|
28
|
+
/** List existing named profiles by reading BROWSER_PROFILES_DIR. */
|
|
29
|
+
export declare function listProfiles(): string[];
|
|
30
|
+
/** Translate Playwright errors into helpful messages for the agent. */
|
|
31
|
+
export declare function toAIFriendlyError(err: unknown): string;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin wrappers around Playwright APIs that use BrowserManager for state.
|
|
3
|
+
* Each action returns a serializable result suitable for tool output.
|
|
4
|
+
*/
|
|
5
|
+
import { readdirSync } from "node:fs";
|
|
6
|
+
import { buildRoleSnapshotFromAriaSnapshot } from "./snapshot.js";
|
|
7
|
+
import { compressScreenshot } from "./screenshot.js";
|
|
8
|
+
import { BROWSER_PROFILES_DIR } from "../../paths.js";
|
|
9
|
+
import { logger } from "../../logger.js";
|
|
10
|
+
const NAVIGATION_TIMEOUT_MS = 30_000;
|
|
11
|
+
const ACTION_TIMEOUT_MS = 10_000;
|
|
12
|
+
const POST_ACTION_SETTLE_MS = 500;
|
|
13
|
+
const POST_KEY_SETTLE_MS = 300;
|
|
14
|
+
const TEXT_PREVIEW_LENGTH = 50;
|
|
15
|
+
const ALLOWED_SCHEMES = new Set(["http:", "https:"]);
|
|
16
|
+
/** Allowed keyboard keys for browser_press_key. */
|
|
17
|
+
const ALLOWED_KEYS = new Set([
|
|
18
|
+
"Enter", "Escape", "Tab", "Backspace", "Delete", "Space",
|
|
19
|
+
"ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight",
|
|
20
|
+
"Home", "End", "PageUp", "PageDown",
|
|
21
|
+
"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
|
|
22
|
+
]);
|
|
23
|
+
/** Modifier+key pattern (e.g. "Control+A", "Meta+Shift+V"). */
|
|
24
|
+
const MODIFIER_KEY_PATTERN = /^(Control|Meta|Alt|Shift)(\+(Control|Meta|Alt|Shift))*\+[A-Za-z0-9]$/;
|
|
25
|
+
/** Prefix a tool output string with the active profile name. */
|
|
26
|
+
export function withProfilePrefix(browser, text) {
|
|
27
|
+
return `[profile: ${browser.getActiveProfile()}] ${text}`;
|
|
28
|
+
}
|
|
29
|
+
/** Navigate to a URL. Returns the page URL + title + snapshot. */
|
|
30
|
+
export async function navigate(browser, url) {
|
|
31
|
+
// Validate URL scheme to prevent SSRF (file://, javascript:, etc.)
|
|
32
|
+
const parsed = new URL(url);
|
|
33
|
+
if (!ALLOWED_SCHEMES.has(parsed.protocol)) {
|
|
34
|
+
throw new Error(`Unsupported URL scheme: ${parsed.protocol}. Only http and https are allowed.`);
|
|
35
|
+
}
|
|
36
|
+
const page = await browser.launch();
|
|
37
|
+
await page.goto(url, { waitUntil: "domcontentloaded", timeout: NAVIGATION_TIMEOUT_MS });
|
|
38
|
+
const title = await page.title();
|
|
39
|
+
// Auto-snapshot after navigation
|
|
40
|
+
const snapshot = await takeSnapshot(browser);
|
|
41
|
+
return withProfilePrefix(browser, `Navigated to: ${page.url()}\nTitle: ${title}\n\n${snapshot}`);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Take an a11y snapshot of the current page, assign refs, store them in manager.
|
|
45
|
+
* Returns raw snapshot text without a profile prefix — callers add the prefix.
|
|
46
|
+
*/
|
|
47
|
+
export async function takeSnapshot(browser) {
|
|
48
|
+
const page = browser.getPage();
|
|
49
|
+
if (!page)
|
|
50
|
+
throw new Error("Browser not open. Use browser_navigate first.");
|
|
51
|
+
const ariaText = await page.locator(":root").ariaSnapshot();
|
|
52
|
+
const { snapshot, refs } = buildRoleSnapshotFromAriaSnapshot(ariaText);
|
|
53
|
+
browser.setRoleRefs(refs);
|
|
54
|
+
const refCount = Object.keys(refs).length;
|
|
55
|
+
return `[${page.url()}]\n${snapshot}\n\n(${refCount} refs assigned — use ref like "e1" to interact)`;
|
|
56
|
+
}
|
|
57
|
+
/** Take a screenshot, compress it, return base64 + mediaType. */
|
|
58
|
+
export async function takeScreenshot(browser, fullPage) {
|
|
59
|
+
const page = browser.getPage();
|
|
60
|
+
if (!page)
|
|
61
|
+
throw new Error("Browser not open. Use browser_navigate first.");
|
|
62
|
+
const png = await page.screenshot({ fullPage: fullPage ?? false });
|
|
63
|
+
return compressScreenshot(Buffer.from(png));
|
|
64
|
+
}
|
|
65
|
+
/** Click an element by ref. Returns confirmation + auto-snapshot. */
|
|
66
|
+
export async function click(browser, ref) {
|
|
67
|
+
const locator = browser.refLocator(ref);
|
|
68
|
+
await locator.click({ timeout: ACTION_TIMEOUT_MS });
|
|
69
|
+
// Short wait for navigation/rendering to settle
|
|
70
|
+
const pageAfterClick = browser.getPage();
|
|
71
|
+
if (pageAfterClick)
|
|
72
|
+
await pageAfterClick.waitForTimeout(POST_ACTION_SETTLE_MS);
|
|
73
|
+
// Auto-snapshot after click
|
|
74
|
+
const snapshot = await takeSnapshot(browser);
|
|
75
|
+
return withProfilePrefix(browser, `Clicked ref=${ref}\n\n${snapshot}`);
|
|
76
|
+
}
|
|
77
|
+
/** Type text into an input by ref. Optionally submit with Enter. */
|
|
78
|
+
export async function type(browser, ref, text, submit) {
|
|
79
|
+
const locator = browser.refLocator(ref);
|
|
80
|
+
await locator.fill(text, { timeout: ACTION_TIMEOUT_MS });
|
|
81
|
+
if (submit) {
|
|
82
|
+
await locator.press("Enter");
|
|
83
|
+
const pageAfterSubmit = browser.getPage();
|
|
84
|
+
if (pageAfterSubmit)
|
|
85
|
+
await pageAfterSubmit.waitForTimeout(POST_ACTION_SETTLE_MS);
|
|
86
|
+
}
|
|
87
|
+
// Auto-snapshot after typing
|
|
88
|
+
const snapshot = await takeSnapshot(browser);
|
|
89
|
+
const preview = text.length > TEXT_PREVIEW_LENGTH
|
|
90
|
+
? `${text.slice(0, TEXT_PREVIEW_LENGTH)}...`
|
|
91
|
+
: text;
|
|
92
|
+
return withProfilePrefix(browser, `Typed "${preview}" into ref=${ref}${submit ? " (submitted)" : ""}\n\n${snapshot}`);
|
|
93
|
+
}
|
|
94
|
+
/** Press a keyboard key or combo (e.g. "Enter", "Control+A"). */
|
|
95
|
+
export async function pressKey(browser, key) {
|
|
96
|
+
const page = browser.getPage();
|
|
97
|
+
if (!page)
|
|
98
|
+
throw new Error("Browser not open. Use browser_navigate first.");
|
|
99
|
+
// Validate key to prevent arbitrary keyboard sequences
|
|
100
|
+
if (!ALLOWED_KEYS.has(key) && !MODIFIER_KEY_PATTERN.test(key)) {
|
|
101
|
+
throw new Error(`Key "${key}" is not allowed. Use single keys (Enter, Escape, ArrowDown, Tab, etc.) or modifier combos (Control+A, Meta+C, etc.).`);
|
|
102
|
+
}
|
|
103
|
+
await page.keyboard.press(key);
|
|
104
|
+
await page.waitForTimeout(POST_KEY_SETTLE_MS);
|
|
105
|
+
return withProfilePrefix(browser, `Pressed key: ${key}`);
|
|
106
|
+
}
|
|
107
|
+
/** Switch the browser to a different named profile. Closes the current browser. */
|
|
108
|
+
export async function switchProfile(browser, name) {
|
|
109
|
+
const previous = browser.getActiveProfile();
|
|
110
|
+
await browser.switchProfile(name);
|
|
111
|
+
const profiles = listProfiles();
|
|
112
|
+
return withProfilePrefix(browser, `Switched from "${previous}" to "${name}". Browser closed — it will relaunch with the new profile on next navigation.\n\nAvailable profiles: ${profiles.join(", ")}`);
|
|
113
|
+
}
|
|
114
|
+
/** List existing named profiles by reading BROWSER_PROFILES_DIR. */
|
|
115
|
+
export function listProfiles() {
|
|
116
|
+
try {
|
|
117
|
+
const entries = readdirSync(BROWSER_PROFILES_DIR, { withFileTypes: true });
|
|
118
|
+
const profiles = entries
|
|
119
|
+
.filter((e) => e.isDirectory())
|
|
120
|
+
.map((e) => e.name);
|
|
121
|
+
// Always include "default" even if dir doesn't exist under profiles/
|
|
122
|
+
if (!profiles.includes("default"))
|
|
123
|
+
profiles.unshift("default");
|
|
124
|
+
return profiles;
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
logger.warn(`Failed to list browser profiles: ${err instanceof Error ? err.message : err}`);
|
|
128
|
+
return ["default"];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/** Translate Playwright errors into helpful messages for the agent. */
|
|
132
|
+
export function toAIFriendlyError(err) {
|
|
133
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
134
|
+
if (msg.includes("Timeout")) {
|
|
135
|
+
return `Action timed out -- the element may not be visible or the page may still be loading. Try taking a fresh snapshot.`;
|
|
136
|
+
}
|
|
137
|
+
if (msg.includes("not attached") || msg.includes("detached")) {
|
|
138
|
+
return `Element is no longer on the page (DOM changed). Take a fresh snapshot and use updated refs.`;
|
|
139
|
+
}
|
|
140
|
+
if (msg.includes("intercepts pointer events") || msg.includes("is not clickable")) {
|
|
141
|
+
return `Element is hidden behind another element. Try scrolling or closing overlays first.`;
|
|
142
|
+
}
|
|
143
|
+
if (msg.includes("Unknown ref") || msg.includes("not allowed") || msg.includes("Unsupported URL") || msg.includes("Profile name")) {
|
|
144
|
+
return msg;
|
|
145
|
+
}
|
|
146
|
+
logger.error(`Browser action error: ${msg}`);
|
|
147
|
+
return `Browser error: ${msg}`;
|
|
148
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type Page } from "playwright";
|
|
2
|
+
import type { RoleRefMap } from "./snapshot.js";
|
|
3
|
+
/** Validate a profile name. Throws on invalid input. */
|
|
4
|
+
export declare function validateProfileName(name: string): void;
|
|
5
|
+
export interface BrowserConfig {
|
|
6
|
+
headless?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Chrome user-data-dir.
|
|
9
|
+
* - `undefined` (default): uses the shared persistent profile (`BROWSER_PROFILE_DIR`).
|
|
10
|
+
* - `"temp"`: creates a fresh temp dir per launch (used for worker isolation).
|
|
11
|
+
* - any other string: uses that path as-is.
|
|
12
|
+
*/
|
|
13
|
+
profileDir?: string | "temp";
|
|
14
|
+
/** Named profile (e.g. "work", "personal"). Default: "default". */
|
|
15
|
+
profile?: string;
|
|
16
|
+
/** Custom User-Agent string. When unset, auto-detects a clean UA from the real browser. */
|
|
17
|
+
userAgent?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare class BrowserManager {
|
|
20
|
+
private context;
|
|
21
|
+
private config;
|
|
22
|
+
private roleRefs;
|
|
23
|
+
private activeProfile;
|
|
24
|
+
constructor(config: BrowserConfig);
|
|
25
|
+
launch(): Promise<Page>;
|
|
26
|
+
/**
|
|
27
|
+
* Probe-launch the browser to read its real User-Agent, then close it.
|
|
28
|
+
* Returns a clean UA with "HeadlessChrome" replaced by "Chrome".
|
|
29
|
+
* The result is cached on the class so we only probe once per process.
|
|
30
|
+
*/
|
|
31
|
+
private static _cachedCleanUA;
|
|
32
|
+
private detectCleanUA;
|
|
33
|
+
/** Resolve the user-data directory for the active named profile. */
|
|
34
|
+
private resolveProfileDir;
|
|
35
|
+
/** Close current browser and switch to a different named profile. */
|
|
36
|
+
switchProfile(name: string): Promise<void>;
|
|
37
|
+
/** Get the currently active profile name. */
|
|
38
|
+
getActiveProfile(): string;
|
|
39
|
+
/** Update config for next browser launch. Closes existing browser if headless mode changed. */
|
|
40
|
+
updateConfig(config: BrowserConfig): Promise<void>;
|
|
41
|
+
/** Get the active page, or null if browser is not launched. */
|
|
42
|
+
getPage(): Page | null;
|
|
43
|
+
/** Check if the browser is currently launched. */
|
|
44
|
+
isLaunched(): boolean;
|
|
45
|
+
/** Store role refs from the latest snapshot. */
|
|
46
|
+
setRoleRefs(refs: RoleRefMap): void;
|
|
47
|
+
/** Get stored role refs from the latest snapshot. */
|
|
48
|
+
getRoleRefs(): RoleRefMap;
|
|
49
|
+
/**
|
|
50
|
+
* Resolve a ref string (e.g. "e5") to a Playwright Locator.
|
|
51
|
+
* Ported from main project's pw-session.ts:refLocator.
|
|
52
|
+
*/
|
|
53
|
+
refLocator(ref: string): import("playwright-core").Locator;
|
|
54
|
+
close(): Promise<void>;
|
|
55
|
+
}
|