verybot 0.1.8
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 +167 -0
- package/dist/aliases/store.d.ts +21 -0
- package/dist/aliases/store.js +148 -0
- package/dist/aliases/types.d.ts +6 -0
- package/dist/aliases/types.js +1 -0
- package/dist/brain/agent-registry.d.ts +96 -0
- package/dist/brain/agent-registry.js +141 -0
- package/dist/brain/agent.d.ts +167 -0
- package/dist/brain/agent.js +932 -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 +43 -0
- package/dist/brain/context.js +139 -0
- package/dist/brain/delegation-store.d.ts +33 -0
- package/dist/brain/delegation-store.js +106 -0
- package/dist/brain/loop.d.ts +24 -0
- package/dist/brain/loop.js +318 -0
- package/dist/brain/mcp-adapter.d.ts +43 -0
- package/dist/brain/mcp-adapter.js +244 -0
- package/dist/brain/memory-extractor.d.ts +26 -0
- package/dist/brain/memory-extractor.js +82 -0
- package/dist/brain/providers.d.ts +14 -0
- package/dist/brain/providers.js +85 -0
- package/dist/brain/queue.d.ts +18 -0
- package/dist/brain/queue.js +111 -0
- package/dist/brain/run-tools.d.ts +50 -0
- package/dist/brain/run-tools.js +136 -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/task-subscriber.d.ts +56 -0
- package/dist/brain/task-subscriber.js +317 -0
- package/dist/brain/user-content.d.ts +16 -0
- package/dist/brain/user-content.js +32 -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 +50 -0
- package/dist/channels/commands.js +132 -0
- package/dist/channels/discord/channel.d.ts +29 -0
- package/dist/channels/discord/channel.js +159 -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 +37 -0
- package/dist/channels/slack/channel.js +227 -0
- package/dist/channels/slack/markdown.d.ts +19 -0
- package/dist/channels/slack/markdown.js +62 -0
- package/dist/channels/specs.d.ts +32 -0
- package/dist/channels/specs.js +99 -0
- package/dist/channels/telegram/channel.d.ts +29 -0
- package/dist/channels/telegram/channel.js +182 -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 +34 -0
- package/dist/channels/whatsapp/channel.js +276 -0
- package/dist/channels/whatsapp/markdown.d.ts +20 -0
- package/dist/channels/whatsapp/markdown.js +51 -0
- package/dist/cli/claude-login.d.ts +5 -0
- package/dist/cli/claude-login.js +47 -0
- package/dist/cli/config.d.ts +5 -0
- package/dist/cli/config.js +78 -0
- package/dist/cli/index.d.ts +11 -0
- package/dist/cli/index.js +96 -0
- package/dist/computer/browser/actions.d.ts +31 -0
- package/dist/computer/browser/actions.js +148 -0
- package/dist/computer/browser/context-manager.d.ts +28 -0
- package/dist/computer/browser/context-manager.js +78 -0
- package/dist/computer/browser/manager.d.ts +91 -0
- package/dist/computer/browser/manager.js +344 -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/browser/types.d.ts +26 -0
- package/dist/computer/browser/types.js +1 -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 +55 -0
- package/dist/config/agent-config.js +16 -0
- package/dist/config/model-catalog.d.ts +22 -0
- package/dist/config/model-catalog.js +112 -0
- package/dist/config/model-spec.d.ts +8 -0
- package/dist/config/model-spec.js +66 -0
- package/dist/config/store.d.ts +25 -0
- package/dist/config/store.js +143 -0
- package/dist/config.d.ts +110 -0
- package/dist/config.js +259 -0
- package/dist/control-ui/assets/index-Cbl7G5Sc.css +1 -0
- package/dist/control-ui/assets/index-Cu1P4C62.js +266 -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/aliases.d.ts +17 -0
- package/dist/gateway/methods/aliases.js +22 -0
- package/dist/gateway/methods/chat.d.ts +33 -0
- package/dist/gateway/methods/chat.js +37 -0
- package/dist/gateway/methods/config.d.ts +14 -0
- package/dist/gateway/methods/config.js +24 -0
- package/dist/gateway/methods/models.d.ts +10 -0
- package/dist/gateway/methods/models.js +14 -0
- package/dist/gateway/methods/playbooks.d.ts +45 -0
- package/dist/gateway/methods/playbooks.js +488 -0
- package/dist/gateway/methods/prompt-templates.d.ts +27 -0
- package/dist/gateway/methods/prompt-templates.js +106 -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 +44 -0
- package/dist/gateway/methods/sessions.js +111 -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 +40 -0
- package/dist/gateway/methods/tasks.js +151 -0
- package/dist/gateway/methods/teams.d.ts +69 -0
- package/dist/gateway/methods/teams.js +376 -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 +79 -0
- package/dist/gateway/server.d.ts +9 -0
- package/dist/gateway/server.js +137 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +254 -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 +47 -0
- package/dist/integrations/registry.js +332 -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/explicit.d.ts +16 -0
- package/dist/memory/explicit.js +29 -0
- package/dist/memory/extractor.d.ts +13 -0
- package/dist/memory/extractor.js +82 -0
- package/dist/memory/search.d.ts +15 -0
- package/dist/memory/search.js +57 -0
- package/dist/memory/session-learning.d.ts +23 -0
- package/dist/memory/session-learning.js +55 -0
- package/dist/memory/store.d.ts +36 -0
- package/dist/memory/store.js +334 -0
- package/dist/memory/types.d.ts +9 -0
- package/dist/memory/types.js +2 -0
- package/dist/paths.d.ts +28 -0
- package/dist/paths.js +48 -0
- package/dist/prompt-templates/builtins/index.d.ts +4 -0
- package/dist/prompt-templates/builtins/index.js +5 -0
- package/dist/prompt-templates/builtins/planner.d.ts +4 -0
- package/dist/prompt-templates/builtins/planner.js +77 -0
- package/dist/prompt-templates/store.d.ts +45 -0
- package/dist/prompt-templates/store.js +224 -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/inline-attachment-content.d.ts +9 -0
- package/dist/tasks/inline-attachment-content.js +64 -0
- package/dist/tasks/store.d.ts +112 -0
- package/dist/tasks/store.js +519 -0
- package/dist/tasks/types.d.ts +129 -0
- package/dist/tasks/types.js +80 -0
- package/dist/teams/status-config.d.ts +8 -0
- package/dist/teams/status-config.js +40 -0
- package/dist/teams/store.d.ts +111 -0
- package/dist/teams/store.js +671 -0
- package/dist/teams/types.d.ts +30 -0
- package/dist/teams/types.js +1 -0
- package/dist/tools/bash.d.ts +18 -0
- package/dist/tools/bash.js +64 -0
- package/dist/tools/channel-history.d.ts +10 -0
- package/dist/tools/channel-history.js +43 -0
- package/dist/tools/delegate.d.ts +20 -0
- package/dist/tools/delegate.js +299 -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 +59 -0
- package/dist/tools/prompt-templates.d.ts +7 -0
- package/dist/tools/prompt-templates.js +133 -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 +67 -0
- package/dist/tools/tasks.js +288 -0
- package/dist/tools/teams.d.ts +22 -0
- package/dist/tools/teams.js +470 -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/dist/version.d.ts +1 -0
- package/dist/version.js +13 -0
- package/package.json +102 -0
- package/verybot.js +2 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { handleConfigCommand } from "./config.js";
|
|
2
|
+
import { handleClaudeLogin } from "./claude-login.js";
|
|
3
|
+
import { getVersion } from "../version.js";
|
|
4
|
+
const VERSION_FLAGS = new Set(["--version", "-v"]);
|
|
5
|
+
const HELP_FLAGS = new Set(["--help", "-h", "help"]);
|
|
6
|
+
const HANDLED_SUBCOMMANDS = new Set(["config", "claude-login"]);
|
|
7
|
+
const HOST_FLAG = "--host";
|
|
8
|
+
const PORT_FLAG = "--port";
|
|
9
|
+
const HOST_FLAG_PREFIX = `${HOST_FLAG}=`;
|
|
10
|
+
const PORT_FLAG_PREFIX = `${PORT_FLAG}=`;
|
|
11
|
+
const PORT_MIN = 1;
|
|
12
|
+
const PORT_MAX = 65_535;
|
|
13
|
+
function printHelp() {
|
|
14
|
+
console.log(`Usage:
|
|
15
|
+
verybot
|
|
16
|
+
verybot --host <host> [--port <port>]
|
|
17
|
+
verybot config get [key]
|
|
18
|
+
verybot config set <key> <value>
|
|
19
|
+
verybot config regenerate_gateway_token
|
|
20
|
+
verybot claude-login
|
|
21
|
+
`);
|
|
22
|
+
}
|
|
23
|
+
function parsePort(raw) {
|
|
24
|
+
const port = Number(raw);
|
|
25
|
+
const isValid = Number.isInteger(port) && port >= PORT_MIN && port <= PORT_MAX;
|
|
26
|
+
if (!isValid) {
|
|
27
|
+
throw new Error(`Invalid --port value "${raw}". Expected an integer between ${PORT_MIN} and ${PORT_MAX}.`);
|
|
28
|
+
}
|
|
29
|
+
return port;
|
|
30
|
+
}
|
|
31
|
+
function parseHost(raw) {
|
|
32
|
+
const host = raw.trim();
|
|
33
|
+
if (!host)
|
|
34
|
+
throw new Error("Missing value for --host");
|
|
35
|
+
return host;
|
|
36
|
+
}
|
|
37
|
+
function requireFlagValue(argv, idx, flag) {
|
|
38
|
+
const value = argv[idx + 1];
|
|
39
|
+
if (!value || value.startsWith("-")) {
|
|
40
|
+
throw new Error(`Missing value for ${flag}`);
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
/** Parse runtime flags used when launching the main gateway process. */
|
|
45
|
+
export function parseRuntimeCliOptions(argv = process.argv.slice(2)) {
|
|
46
|
+
const first = argv[0];
|
|
47
|
+
if (!first)
|
|
48
|
+
return {};
|
|
49
|
+
if (VERSION_FLAGS.has(first) || HELP_FLAGS.has(first) || HANDLED_SUBCOMMANDS.has(first))
|
|
50
|
+
return {};
|
|
51
|
+
const options = {};
|
|
52
|
+
for (let i = 0; i < argv.length; i++) {
|
|
53
|
+
const arg = argv[i];
|
|
54
|
+
if (arg === HOST_FLAG) {
|
|
55
|
+
options.gatewayHost = parseHost(requireFlagValue(argv, i, HOST_FLAG));
|
|
56
|
+
i += 1;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (arg.startsWith(HOST_FLAG_PREFIX)) {
|
|
60
|
+
options.gatewayHost = parseHost(arg.slice(HOST_FLAG_PREFIX.length));
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (arg === PORT_FLAG) {
|
|
64
|
+
options.gatewayPort = parsePort(requireFlagValue(argv, i, PORT_FLAG));
|
|
65
|
+
i += 1;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (arg.startsWith(PORT_FLAG_PREFIX)) {
|
|
69
|
+
options.gatewayPort = parsePort(arg.slice(PORT_FLAG_PREFIX.length));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return options;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Route lightweight CLI subcommands without booting the gateway.
|
|
76
|
+
* Returns a promise (or null) so the caller can await async commands.
|
|
77
|
+
*/
|
|
78
|
+
export function handleCli() {
|
|
79
|
+
const [cmd, sub, ...rest] = process.argv.slice(2);
|
|
80
|
+
if (cmd && VERSION_FLAGS.has(cmd)) {
|
|
81
|
+
console.log(`verybot v${getVersion()}`);
|
|
82
|
+
return Promise.resolve();
|
|
83
|
+
}
|
|
84
|
+
if (cmd && HELP_FLAGS.has(cmd)) {
|
|
85
|
+
printHelp();
|
|
86
|
+
return Promise.resolve();
|
|
87
|
+
}
|
|
88
|
+
if (cmd === "config") {
|
|
89
|
+
handleConfigCommand(sub, rest);
|
|
90
|
+
return Promise.resolve();
|
|
91
|
+
}
|
|
92
|
+
if (cmd === "claude-login") {
|
|
93
|
+
return handleClaudeLogin();
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
@@ -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, sessionKey?: 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, sessionKey?: string): Promise<string>;
|
|
15
|
+
/** Take a screenshot, compress it, return base64 + mediaType. */
|
|
16
|
+
export declare function takeScreenshot(browser: BrowserManager, fullPage?: boolean, sessionKey?: string): 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, sessionKey?: 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, sessionKey?: string): Promise<string>;
|
|
24
|
+
/** Press a keyboard key or combo (e.g. "Enter", "Control+A"). */
|
|
25
|
+
export declare function pressKey(browser: BrowserManager, key: string, sessionKey?: 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, sessionKey) {
|
|
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(sessionKey);
|
|
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, sessionKey);
|
|
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, sessionKey) {
|
|
48
|
+
const page = browser.getPage(sessionKey);
|
|
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, sessionKey);
|
|
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, sessionKey) {
|
|
59
|
+
const page = browser.getPage(sessionKey);
|
|
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, sessionKey) {
|
|
67
|
+
const locator = browser.refLocator(ref, sessionKey);
|
|
68
|
+
await locator.click({ timeout: ACTION_TIMEOUT_MS });
|
|
69
|
+
// Short wait for navigation/rendering to settle
|
|
70
|
+
const pageAfterClick = browser.getPage(sessionKey);
|
|
71
|
+
if (pageAfterClick)
|
|
72
|
+
await pageAfterClick.waitForTimeout(POST_ACTION_SETTLE_MS);
|
|
73
|
+
// Auto-snapshot after click
|
|
74
|
+
const snapshot = await takeSnapshot(browser, sessionKey);
|
|
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, sessionKey) {
|
|
79
|
+
const locator = browser.refLocator(ref, sessionKey);
|
|
80
|
+
await locator.fill(text, { timeout: ACTION_TIMEOUT_MS });
|
|
81
|
+
if (submit) {
|
|
82
|
+
await locator.press("Enter");
|
|
83
|
+
const pageAfterSubmit = browser.getPage(sessionKey);
|
|
84
|
+
if (pageAfterSubmit)
|
|
85
|
+
await pageAfterSubmit.waitForTimeout(POST_ACTION_SETTLE_MS);
|
|
86
|
+
}
|
|
87
|
+
// Auto-snapshot after typing
|
|
88
|
+
const snapshot = await takeSnapshot(browser, sessionKey);
|
|
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, sessionKey) {
|
|
96
|
+
const page = browser.getPage(sessionKey);
|
|
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,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser Context Manager - manages browser instances across multiple sessions.
|
|
3
|
+
*/
|
|
4
|
+
import type { BrowserManager } from "./manager.js";
|
|
5
|
+
import type { BrowserConfig } from "./manager.js";
|
|
6
|
+
/**
|
|
7
|
+
* Get or create a BrowserManager for the given session.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getBrowserManager(sessionId: string, config?: BrowserConfig): BrowserManager;
|
|
10
|
+
/**
|
|
11
|
+
* Release a session's browser manager and clean up resources.
|
|
12
|
+
*/
|
|
13
|
+
export declare function releaseBrowserManager(sessionId: string): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Close all browser managers and clean up resources.
|
|
16
|
+
*/
|
|
17
|
+
export declare function closeAllBrowsers(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Get all active sessions.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getActiveSessions(): string[];
|
|
22
|
+
/**
|
|
23
|
+
* Get browser statistics.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getBrowserStats(): {
|
|
26
|
+
totalSessions: number;
|
|
27
|
+
activeSessions: string[];
|
|
28
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser Context Manager - manages browser instances across multiple sessions.
|
|
3
|
+
*/
|
|
4
|
+
import { BrowserManager as BM } from "./manager.js";
|
|
5
|
+
import { logger } from "../../logger.js";
|
|
6
|
+
/**
|
|
7
|
+
* Global registry of browser managers per session.
|
|
8
|
+
*/
|
|
9
|
+
const SESSION_MANAGERS = new Map();
|
|
10
|
+
/**
|
|
11
|
+
* Get or create a BrowserManager for the given session.
|
|
12
|
+
*/
|
|
13
|
+
export function getBrowserManager(sessionId, config) {
|
|
14
|
+
if (SESSION_MANAGERS.has(sessionId)) {
|
|
15
|
+
return SESSION_MANAGERS.get(sessionId);
|
|
16
|
+
}
|
|
17
|
+
const finalConfig = config || {};
|
|
18
|
+
const manager = new BM(finalConfig);
|
|
19
|
+
manager.setSessionKey(sessionId);
|
|
20
|
+
SESSION_MANAGERS.set(sessionId, manager);
|
|
21
|
+
logger.info(`Created BrowserManager for session: ${sessionId}`);
|
|
22
|
+
return manager;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Release a session's browser manager and clean up resources.
|
|
26
|
+
*/
|
|
27
|
+
export async function releaseBrowserManager(sessionId) {
|
|
28
|
+
const manager = SESSION_MANAGERS.get(sessionId);
|
|
29
|
+
if (!manager)
|
|
30
|
+
return;
|
|
31
|
+
logger.info(`Releasing BrowserManager for session: ${sessionId}`);
|
|
32
|
+
const pages = manager.getSessionPages(sessionId);
|
|
33
|
+
for (const page of pages) {
|
|
34
|
+
try {
|
|
35
|
+
await page.close();
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
logger.warn(`Failed to close page for session ${sessionId}: ${err}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
SESSION_MANAGERS.delete(sessionId);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Close all browser managers and clean up resources.
|
|
45
|
+
*/
|
|
46
|
+
export async function closeAllBrowsers() {
|
|
47
|
+
const managers = Array.from(SESSION_MANAGERS.values());
|
|
48
|
+
const seenContexts = new Set();
|
|
49
|
+
for (const manager of managers) {
|
|
50
|
+
const contextId = manager['context']?.toString();
|
|
51
|
+
if (contextId && !seenContexts.has(contextId)) {
|
|
52
|
+
try {
|
|
53
|
+
await manager.close();
|
|
54
|
+
seenContexts.add(contextId);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
logger.warn(`Failed to close browser: ${err}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
SESSION_MANAGERS.clear();
|
|
62
|
+
logger.info("All browsers closed");
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get all active sessions.
|
|
66
|
+
*/
|
|
67
|
+
export function getActiveSessions() {
|
|
68
|
+
return Array.from(SESSION_MANAGERS.keys());
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get browser statistics.
|
|
72
|
+
*/
|
|
73
|
+
export function getBrowserStats() {
|
|
74
|
+
return {
|
|
75
|
+
totalSessions: SESSION_MANAGERS.size,
|
|
76
|
+
activeSessions: getActiveSessions(),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Page } from "playwright";
|
|
2
|
+
import type { RoleRefMap } from "./snapshot.js";
|
|
3
|
+
import type { BrowserModeConfig } from "./types.js";
|
|
4
|
+
/** Validate a profile name. Throws on invalid input. */
|
|
5
|
+
export declare function validateProfileName(name: string): void;
|
|
6
|
+
export interface BrowserConfig extends BrowserModeConfig {
|
|
7
|
+
headless?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Chrome user-data-dir.
|
|
10
|
+
* - `undefined` (default): uses the shared persistent profile (`BROWSER_PROFILE_DIR`).
|
|
11
|
+
* - `"temp"`: creates a fresh temp dir per launch (used for worker isolation).
|
|
12
|
+
* - any other string: uses that path as-is.
|
|
13
|
+
*/
|
|
14
|
+
profileDir?: string | "temp";
|
|
15
|
+
/** Named profile (e.g. "work", "personal"). Default: "default". */
|
|
16
|
+
profile?: string;
|
|
17
|
+
/** Custom User-Agent string. When unset, auto-detects a clean UA from the real browser. */
|
|
18
|
+
userAgent?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare class BrowserManager {
|
|
21
|
+
private static cachedCleanUAByMode;
|
|
22
|
+
private context;
|
|
23
|
+
private config;
|
|
24
|
+
private roleRefs;
|
|
25
|
+
private sessionRoleRefs;
|
|
26
|
+
private activeProfile;
|
|
27
|
+
private tempProfileDir;
|
|
28
|
+
private sessionPages;
|
|
29
|
+
private currentSessionKey;
|
|
30
|
+
constructor(config: BrowserConfig);
|
|
31
|
+
private isPerTabMode;
|
|
32
|
+
private resolveSessionKey;
|
|
33
|
+
private ensureSessionPages;
|
|
34
|
+
private trackPageForSession;
|
|
35
|
+
private pruneClosedPages;
|
|
36
|
+
/**
|
|
37
|
+
* Set the current session key for page tracking (per-tab-per-session mode).
|
|
38
|
+
* Called by buildRunTools() to bind tools to a specific session.
|
|
39
|
+
*/
|
|
40
|
+
setSessionKey(key: string | null): void;
|
|
41
|
+
/**
|
|
42
|
+
* Get the current session key (if set).
|
|
43
|
+
*/
|
|
44
|
+
getSessionKey(): string | null;
|
|
45
|
+
/**
|
|
46
|
+
* MODIFIED: launch() now session-aware
|
|
47
|
+
* If mode is "per-tab-per-session" and sessionKey is set,
|
|
48
|
+
* pages are tracked per session.
|
|
49
|
+
*/
|
|
50
|
+
launch(sessionKey?: string): Promise<Page>;
|
|
51
|
+
/**
|
|
52
|
+
* Probe-launch Chromium once per mode to derive a stable UA without "HeadlessChrome".
|
|
53
|
+
* This protects the initial page in persistent contexts where plugin hooks may not run yet.
|
|
54
|
+
*/
|
|
55
|
+
private detectCleanUA;
|
|
56
|
+
/** Resolve the user-data directory for the active named profile. */
|
|
57
|
+
private resolveProfileDir;
|
|
58
|
+
/** Close current browser and switch to a different named profile. */
|
|
59
|
+
switchProfile(name: string): Promise<void>;
|
|
60
|
+
/** Get the currently active profile name. */
|
|
61
|
+
getActiveProfile(): string;
|
|
62
|
+
/** Update config for next browser launch. Closes existing browser if headless mode changed. */
|
|
63
|
+
updateConfig(config: BrowserConfig): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* MODIFIED: getPage() respects session boundary in per-tab mode
|
|
66
|
+
*/
|
|
67
|
+
getPage(sessionKey?: string): Page | null;
|
|
68
|
+
/** Check if the browser is currently launched. */
|
|
69
|
+
isLaunched(): boolean;
|
|
70
|
+
/** Store role refs from the latest snapshot. */
|
|
71
|
+
setRoleRefs(refs: RoleRefMap, sessionKey?: string): void;
|
|
72
|
+
/** Get stored role refs from the latest snapshot. */
|
|
73
|
+
getRoleRefs(sessionKey?: string): RoleRefMap;
|
|
74
|
+
/**
|
|
75
|
+
* Resolve a ref string (e.g. "e5") to a Playwright Locator.
|
|
76
|
+
* Ported from main project's pw-session.ts:refLocator.
|
|
77
|
+
*/
|
|
78
|
+
refLocator(ref: string, sessionKey?: string): import("playwright-core").Locator;
|
|
79
|
+
/**
|
|
80
|
+
* NEW: Get all pages for a session (useful for debugging/cleanup).
|
|
81
|
+
*/
|
|
82
|
+
getSessionPages(sessionKey: string): Page[];
|
|
83
|
+
/**
|
|
84
|
+
* MODIFIED: close() now clears session page tracking
|
|
85
|
+
*/
|
|
86
|
+
close(): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* NEW: Clear pages for a specific session (called on session cleanup)
|
|
89
|
+
*/
|
|
90
|
+
clearSessionPages(sessionKey: string): Promise<void>;
|
|
91
|
+
}
|