feat: Geutebruck GeViScope/GeViSoft Action Mapping System - MVP

This MVP release provides a complete full-stack solution for managing action mappings
in Geutebruck's GeViScope and GeViSoft video surveillance systems.

## Features

### Flutter Web Application (Port 8081)
- Modern, responsive UI for managing action mappings
- Action picker dialog with full parameter configuration
- Support for both GSC (GeViScope) and G-Core server actions
- Consistent UI for input and output actions with edit/delete capabilities
- Real-time action mapping creation, editing, and deletion
- Server categorization (GSC: prefix for GeViScope, G-Core: prefix for G-Core servers)

### FastAPI REST Backend (Port 8000)
- RESTful API for action mapping CRUD operations
- Action template service with comprehensive action catalog (247 actions)
- Server management (G-Core and GeViScope servers)
- Configuration tree reading and writing
- JWT authentication with role-based access control
- PostgreSQL database integration

### C# SDK Bridge (gRPC, Port 50051)
- Native integration with GeViSoft SDK (GeViProcAPINET_4_0.dll)
- Action mapping creation with correct binary format
- Support for GSC and G-Core action types
- Proper Camera parameter inclusion in action strings (fixes CrossSwitch bug)
- Action ID lookup table with server-specific action IDs
- Configuration reading/writing via SetupClient

## Bug Fixes
- **CrossSwitch Bug**: GSC and G-Core actions now correctly display camera/PTZ head parameters in GeViSet
- Action strings now include Camera parameter: `@ PanLeft (Comment: "", Camera: 101028)`
- Proper filter flags and VideoInput=0 for action mappings
- Correct action ID assignment (4198 for GSC, 9294 for G-Core PanLeft)

## Technical Stack
- **Frontend**: Flutter Web, Dart, Dio HTTP client
- **Backend**: Python FastAPI, PostgreSQL, Redis
- **SDK Bridge**: C# .NET 8.0, gRPC, GeViSoft SDK
- **Authentication**: JWT tokens
- **Configuration**: GeViSoft .set files (binary format)

## Credentials
- GeViSoft/GeViScope: username=sysadmin, password=masterkey
- Default admin: username=admin, password=admin123

## Deployment
All services run on localhost:
- Flutter Web: http://localhost:8081
- FastAPI: http://localhost:8000
- SDK Bridge gRPC: localhost:50051
- GeViServer: localhost (default port)

Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Administrator
2025-12-31 18:10:54 +01:00
commit 14893e62a5
4189 changed files with 1395076 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var actions_d_exports = {};
module.exports = __toCommonJS(actions_d_exports);

View File

@@ -0,0 +1,296 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var browserContextFactory_exports = {};
__export(browserContextFactory_exports, {
SharedContextFactory: () => SharedContextFactory,
contextFactory: () => contextFactory
});
module.exports = __toCommonJS(browserContextFactory_exports);
var import_crypto = __toESM(require("crypto"));
var import_fs = __toESM(require("fs"));
var import_net = __toESM(require("net"));
var import_path = __toESM(require("path"));
var playwright = __toESM(require("playwright-core"));
var import_registry = require("playwright-core/lib/server/registry/index");
var import_server = require("playwright-core/lib/server");
var import_log = require("../log");
var import_config = require("./config");
var import_server2 = require("../sdk/server");
function contextFactory(config) {
if (config.sharedBrowserContext)
return SharedContextFactory.create(config);
if (config.browser.remoteEndpoint)
return new RemoteContextFactory(config);
if (config.browser.cdpEndpoint)
return new CdpContextFactory(config);
if (config.browser.isolated)
return new IsolatedContextFactory(config);
return new PersistentContextFactory(config);
}
class BaseContextFactory {
constructor(name, config) {
this._logName = name;
this.config = config;
}
async _obtainBrowser(clientInfo) {
if (this._browserPromise)
return this._browserPromise;
(0, import_log.testDebug)(`obtain browser (${this._logName})`);
this._browserPromise = this._doObtainBrowser(clientInfo);
void this._browserPromise.then((browser) => {
browser.on("disconnected", () => {
this._browserPromise = void 0;
});
}).catch(() => {
this._browserPromise = void 0;
});
return this._browserPromise;
}
async _doObtainBrowser(clientInfo) {
throw new Error("Not implemented");
}
async createContext(clientInfo) {
(0, import_log.testDebug)(`create browser context (${this._logName})`);
const browser = await this._obtainBrowser(clientInfo);
const browserContext = await this._doCreateContext(browser);
await addInitScript(browserContext, this.config.browser.initScript);
return {
browserContext,
close: (afterClose) => this._closeBrowserContext(browserContext, browser, afterClose)
};
}
async _doCreateContext(browser) {
throw new Error("Not implemented");
}
async _closeBrowserContext(browserContext, browser, afterClose) {
(0, import_log.testDebug)(`close browser context (${this._logName})`);
if (browser.contexts().length === 1)
this._browserPromise = void 0;
await browserContext.close().catch(import_log.logUnhandledError);
await afterClose();
if (browser.contexts().length === 0) {
(0, import_log.testDebug)(`close browser (${this._logName})`);
await browser.close().catch(import_log.logUnhandledError);
}
}
}
class IsolatedContextFactory extends BaseContextFactory {
constructor(config) {
super("isolated", config);
}
async _doObtainBrowser(clientInfo) {
await injectCdpPort(this.config.browser);
const browserType = playwright[this.config.browser.browserName];
const tracesDir = await computeTracesDir(this.config, clientInfo);
if (tracesDir && this.config.saveTrace)
await startTraceServer(this.config, tracesDir);
return browserType.launch({
tracesDir,
...this.config.browser.launchOptions,
handleSIGINT: false,
handleSIGTERM: false
}).catch((error) => {
if (error.message.includes("Executable doesn't exist"))
throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);
throw error;
});
}
async _doCreateContext(browser) {
return browser.newContext(this.config.browser.contextOptions);
}
}
class CdpContextFactory extends BaseContextFactory {
constructor(config) {
super("cdp", config);
}
async _doObtainBrowser() {
return playwright.chromium.connectOverCDP(this.config.browser.cdpEndpoint, { headers: this.config.browser.cdpHeaders });
}
async _doCreateContext(browser) {
return this.config.browser.isolated ? await browser.newContext() : browser.contexts()[0];
}
}
class RemoteContextFactory extends BaseContextFactory {
constructor(config) {
super("remote", config);
}
async _doObtainBrowser() {
const url = new URL(this.config.browser.remoteEndpoint);
url.searchParams.set("browser", this.config.browser.browserName);
if (this.config.browser.launchOptions)
url.searchParams.set("launch-options", JSON.stringify(this.config.browser.launchOptions));
return playwright[this.config.browser.browserName].connect(String(url));
}
async _doCreateContext(browser) {
return browser.newContext();
}
}
class PersistentContextFactory {
constructor(config) {
this.name = "persistent";
this.description = "Create a new persistent browser context";
this._userDataDirs = /* @__PURE__ */ new Set();
this.config = config;
}
async createContext(clientInfo) {
await injectCdpPort(this.config.browser);
(0, import_log.testDebug)("create browser context (persistent)");
const userDataDir = this.config.browser.userDataDir ?? await this._createUserDataDir(clientInfo);
const tracesDir = await computeTracesDir(this.config, clientInfo);
if (tracesDir && this.config.saveTrace)
await startTraceServer(this.config, tracesDir);
this._userDataDirs.add(userDataDir);
(0, import_log.testDebug)("lock user data dir", userDataDir);
const browserType = playwright[this.config.browser.browserName];
for (let i = 0; i < 5; i++) {
const launchOptions = {
tracesDir,
...this.config.browser.launchOptions,
...this.config.browser.contextOptions,
handleSIGINT: false,
handleSIGTERM: false,
ignoreDefaultArgs: [
"--disable-extensions"
],
assistantMode: true
};
try {
const browserContext = await browserType.launchPersistentContext(userDataDir, launchOptions);
await addInitScript(browserContext, this.config.browser.initScript);
const close = (afterClose) => this._closeBrowserContext(browserContext, userDataDir, afterClose);
return { browserContext, close };
} catch (error) {
if (error.message.includes("Executable doesn't exist"))
throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);
if (error.message.includes("ProcessSingleton") || error.message.includes("Invalid URL")) {
await new Promise((resolve) => setTimeout(resolve, 1e3));
continue;
}
throw error;
}
}
throw new Error(`Browser is already in use for ${userDataDir}, use --isolated to run multiple instances of the same browser`);
}
async _closeBrowserContext(browserContext, userDataDir, afterClose) {
(0, import_log.testDebug)("close browser context (persistent)");
(0, import_log.testDebug)("release user data dir", userDataDir);
await browserContext.close().catch(() => {
});
await afterClose();
this._userDataDirs.delete(userDataDir);
(0, import_log.testDebug)("close browser context complete (persistent)");
}
async _createUserDataDir(clientInfo) {
const dir = process.env.PWMCP_PROFILES_DIR_FOR_TEST ?? import_registry.registryDirectory;
const browserToken = this.config.browser.launchOptions?.channel ?? this.config.browser?.browserName;
const rootPath = (0, import_server2.firstRootPath)(clientInfo);
const rootPathToken = rootPath ? `-${createHash(rootPath)}` : "";
const result = import_path.default.join(dir, `mcp-${browserToken}${rootPathToken}`);
await import_fs.default.promises.mkdir(result, { recursive: true });
return result;
}
}
async function injectCdpPort(browserConfig) {
if (browserConfig.browserName === "chromium")
browserConfig.launchOptions.cdpPort = await findFreePort();
}
async function findFreePort() {
return new Promise((resolve, reject) => {
const server = import_net.default.createServer();
server.listen(0, () => {
const { port } = server.address();
server.close(() => resolve(port));
});
server.on("error", reject);
});
}
async function startTraceServer(config, tracesDir) {
if (!config.saveTrace)
return;
const server = await (0, import_server.startTraceViewerServer)();
const urlPrefix = server.urlPrefix("human-readable");
const url = urlPrefix + "/trace/index.html?trace=" + tracesDir + "/trace.json";
console.error("\nTrace viewer listening on " + url);
}
function createHash(data) {
return import_crypto.default.createHash("sha256").update(data).digest("hex").slice(0, 7);
}
async function addInitScript(browserContext, initScript) {
for (const scriptPath of initScript ?? [])
await browserContext.addInitScript({ path: import_path.default.resolve(scriptPath) });
}
class SharedContextFactory {
static create(config) {
if (SharedContextFactory._instance)
throw new Error("SharedContextFactory already exists");
const baseConfig = { ...config, sharedBrowserContext: false };
const baseFactory = contextFactory(baseConfig);
SharedContextFactory._instance = new SharedContextFactory(baseFactory);
return SharedContextFactory._instance;
}
constructor(baseFactory) {
this._baseFactory = baseFactory;
}
async createContext(clientInfo, abortSignal, toolName) {
if (!this._contextPromise) {
(0, import_log.testDebug)("create shared browser context");
this._contextPromise = this._baseFactory.createContext(clientInfo, abortSignal, toolName);
}
const { browserContext } = await this._contextPromise;
(0, import_log.testDebug)(`shared context client connected`);
return {
browserContext,
close: async () => {
(0, import_log.testDebug)(`shared context client disconnected`);
}
};
}
static async dispose() {
await SharedContextFactory._instance?._dispose();
}
async _dispose() {
const contextPromise = this._contextPromise;
this._contextPromise = void 0;
if (!contextPromise)
return;
const { close } = await contextPromise;
await close(async () => {
});
}
}
async function computeTracesDir(config, clientInfo) {
if (!config.saveTrace && !config.capabilities?.includes("tracing"))
return;
return await (0, import_config.outputFile)(config, clientInfo, `traces`, { origin: "code", reason: "Collecting trace" });
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
SharedContextFactory,
contextFactory
});

View File

@@ -0,0 +1,76 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var browserServerBackend_exports = {};
__export(browserServerBackend_exports, {
BrowserServerBackend: () => BrowserServerBackend
});
module.exports = __toCommonJS(browserServerBackend_exports);
var import_context = require("./context");
var import_log = require("../log");
var import_response = require("./response");
var import_sessionLog = require("./sessionLog");
var import_tools = require("./tools");
var import_tool = require("../sdk/tool");
class BrowserServerBackend {
constructor(config, factory) {
this._config = config;
this._browserContextFactory = factory;
this._tools = (0, import_tools.filteredTools)(config);
}
async initialize(clientInfo) {
this._sessionLog = this._config.saveSession ? await import_sessionLog.SessionLog.create(this._config, clientInfo) : void 0;
this._context = new import_context.Context({
config: this._config,
browserContextFactory: this._browserContextFactory,
sessionLog: this._sessionLog,
clientInfo
});
}
async listTools() {
return this._tools.map((tool) => (0, import_tool.toMcpTool)(tool.schema));
}
async callTool(name, rawArguments) {
const tool = this._tools.find((tool2) => tool2.schema.name === name);
if (!tool)
throw new Error(`Tool "${name}" not found`);
const parsedArguments = tool.schema.inputSchema.parse(rawArguments || {});
const context = this._context;
const response = new import_response.Response(context, name, parsedArguments);
response.logBegin();
context.setRunningTool(name);
try {
await tool.handle(context, parsedArguments, response);
await response.finish();
this._sessionLog?.logResponse(response);
} catch (error) {
response.addError(String(error));
} finally {
context.setRunningTool(void 0);
}
response.logEnd();
return response.serialize();
}
serverClosed() {
void this._context?.dispose().catch(import_log.logUnhandledError);
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BrowserServerBackend
});

View File

@@ -0,0 +1,66 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var codegen_exports = {};
__export(codegen_exports, {
escapeWithQuotes: () => escapeWithQuotes,
formatObject: () => formatObject,
quote: () => quote
});
module.exports = __toCommonJS(codegen_exports);
function escapeWithQuotes(text, char = "'") {
const stringified = JSON.stringify(text);
const escapedText = stringified.substring(1, stringified.length - 1).replace(/\\"/g, '"');
if (char === "'")
return char + escapedText.replace(/[']/g, "\\'") + char;
if (char === '"')
return char + escapedText.replace(/["]/g, '\\"') + char;
if (char === "`")
return char + escapedText.replace(/[`]/g, "\\`") + char;
throw new Error("Invalid escape char");
}
function quote(text) {
return escapeWithQuotes(text, "'");
}
function formatObject(value, indent = " ", mode = "multiline") {
if (typeof value === "string")
return quote(value);
if (Array.isArray(value))
return `[${value.map((o) => formatObject(o)).join(", ")}]`;
if (typeof value === "object") {
const keys = Object.keys(value).filter((key) => value[key] !== void 0).sort();
if (!keys.length)
return "{}";
const tokens = [];
for (const key of keys)
tokens.push(`${key}: ${formatObject(value[key])}`);
if (mode === "multiline")
return `{
${tokens.join(`,
${indent}`)}
}`;
return `{ ${tokens.join(", ")} }`;
}
return String(value);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
escapeWithQuotes,
formatObject,
quote
});

View File

@@ -0,0 +1,368 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var config_exports = {};
__export(config_exports, {
commaSeparatedList: () => commaSeparatedList,
configFromCLIOptions: () => configFromCLIOptions,
defaultConfig: () => defaultConfig,
dotenvFileLoader: () => dotenvFileLoader,
headerParser: () => headerParser,
numberParser: () => numberParser,
outputDir: () => outputDir,
outputFile: () => outputFile,
resolutionParser: () => resolutionParser,
resolveCLIConfig: () => resolveCLIConfig,
resolveConfig: () => resolveConfig
});
module.exports = __toCommonJS(config_exports);
var import_fs = __toESM(require("fs"));
var import_os = __toESM(require("os"));
var import_path = __toESM(require("path"));
var import_playwright_core = require("playwright-core");
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
var import_util = require("../../util");
var import_server = require("../sdk/server");
const defaultConfig = {
browser: {
browserName: "chromium",
launchOptions: {
channel: "chrome",
headless: import_os.default.platform() === "linux" && !process.env.DISPLAY,
chromiumSandbox: true
},
contextOptions: {
viewport: null
}
},
server: {},
saveTrace: false,
timeouts: {
action: 5e3,
navigation: 6e4
}
};
async function resolveConfig(config) {
return mergeConfig(defaultConfig, config);
}
async function resolveCLIConfig(cliOptions) {
const configInFile = await loadConfig(cliOptions.config);
const envOverrides = configFromEnv();
const cliOverrides = configFromCLIOptions(cliOptions);
let result = defaultConfig;
result = mergeConfig(result, configInFile);
result = mergeConfig(result, envOverrides);
result = mergeConfig(result, cliOverrides);
await validateConfig(result);
return result;
}
async function validateConfig(config) {
if (config.browser.initScript) {
for (const script of config.browser.initScript) {
if (!await (0, import_util.fileExistsAsync)(script))
throw new Error(`Init script file does not exist: ${script}`);
}
}
if (config.sharedBrowserContext && config.saveVideo)
throw new Error("saveVideo is not supported when sharedBrowserContext is true");
}
function configFromCLIOptions(cliOptions) {
let browserName;
let channel;
switch (cliOptions.browser) {
case "chrome":
case "chrome-beta":
case "chrome-canary":
case "chrome-dev":
case "chromium":
case "msedge":
case "msedge-beta":
case "msedge-canary":
case "msedge-dev":
browserName = "chromium";
channel = cliOptions.browser;
break;
case "firefox":
browserName = "firefox";
break;
case "webkit":
browserName = "webkit";
break;
}
const launchOptions = {
channel,
executablePath: cliOptions.executablePath,
headless: cliOptions.headless
};
if (cliOptions.sandbox === false)
launchOptions.chromiumSandbox = false;
if (cliOptions.proxyServer) {
launchOptions.proxy = {
server: cliOptions.proxyServer
};
if (cliOptions.proxyBypass)
launchOptions.proxy.bypass = cliOptions.proxyBypass;
}
if (cliOptions.device && cliOptions.cdpEndpoint)
throw new Error("Device emulation is not supported with cdpEndpoint.");
const contextOptions = cliOptions.device ? import_playwright_core.devices[cliOptions.device] : {};
if (cliOptions.storageState)
contextOptions.storageState = cliOptions.storageState;
if (cliOptions.userAgent)
contextOptions.userAgent = cliOptions.userAgent;
if (cliOptions.viewportSize)
contextOptions.viewport = cliOptions.viewportSize;
if (cliOptions.ignoreHttpsErrors)
contextOptions.ignoreHTTPSErrors = true;
if (cliOptions.blockServiceWorkers)
contextOptions.serviceWorkers = "block";
if (cliOptions.grantPermissions)
contextOptions.permissions = cliOptions.grantPermissions;
if (cliOptions.saveVideo) {
contextOptions.recordVideo = {
// Videos are moved to output directory on saveAs.
dir: tmpDir(),
size: cliOptions.saveVideo
};
}
const result = {
browser: {
browserName,
isolated: cliOptions.isolated,
userDataDir: cliOptions.userDataDir,
launchOptions,
contextOptions,
cdpEndpoint: cliOptions.cdpEndpoint,
cdpHeaders: cliOptions.cdpHeader,
initPage: cliOptions.initPage,
initScript: cliOptions.initScript
},
server: {
port: cliOptions.port,
host: cliOptions.host,
allowedHosts: cliOptions.allowedHosts
},
capabilities: cliOptions.caps,
saveSession: cliOptions.saveSession,
saveTrace: cliOptions.saveTrace,
saveVideo: cliOptions.saveVideo,
secrets: cliOptions.secrets,
sharedBrowserContext: cliOptions.sharedBrowserContext,
outputDir: cliOptions.outputDir,
imageResponses: cliOptions.imageResponses,
testIdAttribute: cliOptions.testIdAttribute,
timeouts: {
action: cliOptions.timeoutAction,
navigation: cliOptions.timeoutNavigation
}
};
return result;
}
function configFromEnv() {
const options = {};
options.allowedHosts = commaSeparatedList(process.env.PLAYWRIGHT_MCP_ALLOWED_HOSTNAMES);
options.blockServiceWorkers = envToBoolean(process.env.PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS);
options.browser = envToString(process.env.PLAYWRIGHT_MCP_BROWSER);
options.caps = commaSeparatedList(process.env.PLAYWRIGHT_MCP_CAPS);
options.cdpEndpoint = envToString(process.env.PLAYWRIGHT_MCP_CDP_ENDPOINT);
options.cdpHeader = headerParser(process.env.PLAYWRIGHT_MCP_CDP_HEADERS, {});
options.config = envToString(process.env.PLAYWRIGHT_MCP_CONFIG);
options.device = envToString(process.env.PLAYWRIGHT_MCP_DEVICE);
options.executablePath = envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH);
options.grantPermissions = commaSeparatedList(process.env.PLAYWRIGHT_MCP_GRANT_PERMISSIONS);
options.headless = envToBoolean(process.env.PLAYWRIGHT_MCP_HEADLESS);
options.host = envToString(process.env.PLAYWRIGHT_MCP_HOST);
options.ignoreHttpsErrors = envToBoolean(process.env.PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS);
const initPage = envToString(process.env.PLAYWRIGHT_MCP_INIT_PAGE);
if (initPage)
options.initPage = [initPage];
const initScript = envToString(process.env.PLAYWRIGHT_MCP_INIT_SCRIPT);
if (initScript)
options.initScript = [initScript];
options.isolated = envToBoolean(process.env.PLAYWRIGHT_MCP_ISOLATED);
if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES === "omit")
options.imageResponses = "omit";
options.sandbox = envToBoolean(process.env.PLAYWRIGHT_MCP_SANDBOX);
options.outputDir = envToString(process.env.PLAYWRIGHT_MCP_OUTPUT_DIR);
options.port = numberParser(process.env.PLAYWRIGHT_MCP_PORT);
options.proxyBypass = envToString(process.env.PLAYWRIGHT_MCP_PROXY_BYPASS);
options.proxyServer = envToString(process.env.PLAYWRIGHT_MCP_PROXY_SERVER);
options.saveTrace = envToBoolean(process.env.PLAYWRIGHT_MCP_SAVE_TRACE);
options.saveVideo = resolutionParser("--save-video", process.env.PLAYWRIGHT_MCP_SAVE_VIDEO);
options.secrets = dotenvFileLoader(process.env.PLAYWRIGHT_MCP_SECRETS_FILE);
options.storageState = envToString(process.env.PLAYWRIGHT_MCP_STORAGE_STATE);
options.testIdAttribute = envToString(process.env.PLAYWRIGHT_MCP_TEST_ID_ATTRIBUTE);
options.timeoutAction = numberParser(process.env.PLAYWRIGHT_MCP_TIMEOUT_ACTION);
options.timeoutNavigation = numberParser(process.env.PLAYWRIGHT_MCP_TIMEOUT_NAVIGATION);
options.userAgent = envToString(process.env.PLAYWRIGHT_MCP_USER_AGENT);
options.userDataDir = envToString(process.env.PLAYWRIGHT_MCP_USER_DATA_DIR);
options.viewportSize = resolutionParser("--viewport-size", process.env.PLAYWRIGHT_MCP_VIEWPORT_SIZE);
return configFromCLIOptions(options);
}
async function loadConfig(configFile) {
if (!configFile)
return {};
try {
return JSON.parse(await import_fs.default.promises.readFile(configFile, "utf8"));
} catch (error) {
throw new Error(`Failed to load config file: ${configFile}, ${error}`);
}
}
function tmpDir() {
return import_path.default.join(process.env.PW_TMPDIR_FOR_TEST ?? import_os.default.tmpdir(), "playwright-mcp-output");
}
function outputDir(config, clientInfo) {
const rootPath = (0, import_server.firstRootPath)(clientInfo);
return config.outputDir ?? (rootPath ? import_path.default.join(rootPath, ".playwright-mcp") : void 0) ?? import_path.default.join(tmpDir(), String(clientInfo.timestamp));
}
async function outputFile(config, clientInfo, fileName, options) {
const file = await resolveFile(config, clientInfo, fileName, options);
(0, import_utilsBundle.debug)("pw:mcp:file")(options.reason, file);
return file;
}
async function resolveFile(config, clientInfo, fileName, options) {
const dir = outputDir(config, clientInfo);
if (options.origin === "code")
return import_path.default.resolve(dir, fileName);
if (options.origin === "llm") {
fileName = fileName.split("\\").join("/");
const resolvedFile = import_path.default.resolve(dir, fileName);
if (!resolvedFile.startsWith(import_path.default.resolve(dir) + import_path.default.sep))
throw new Error(`Resolved file path ${resolvedFile} is outside of the output directory ${dir}. Use relative file names to stay within the output directory.`);
return resolvedFile;
}
return import_path.default.join(dir, sanitizeForFilePath(fileName));
}
function pickDefined(obj) {
return Object.fromEntries(
Object.entries(obj ?? {}).filter(([_, v]) => v !== void 0)
);
}
function mergeConfig(base, overrides) {
const browser = {
...pickDefined(base.browser),
...pickDefined(overrides.browser),
browserName: overrides.browser?.browserName ?? base.browser?.browserName ?? "chromium",
isolated: overrides.browser?.isolated ?? base.browser?.isolated ?? false,
launchOptions: {
...pickDefined(base.browser?.launchOptions),
...pickDefined(overrides.browser?.launchOptions),
...{ assistantMode: true }
},
contextOptions: {
...pickDefined(base.browser?.contextOptions),
...pickDefined(overrides.browser?.contextOptions)
}
};
if (browser.browserName !== "chromium" && browser.launchOptions)
delete browser.launchOptions.channel;
return {
...pickDefined(base),
...pickDefined(overrides),
browser,
server: {
...pickDefined(base.server),
...pickDefined(overrides.server)
},
timeouts: {
...pickDefined(base.timeouts),
...pickDefined(overrides.timeouts)
}
};
}
function commaSeparatedList(value) {
if (!value)
return void 0;
return value.split(",").map((v) => v.trim());
}
function dotenvFileLoader(value) {
if (!value)
return void 0;
return import_utilsBundle.dotenv.parse(import_fs.default.readFileSync(value, "utf8"));
}
function numberParser(value) {
if (!value)
return void 0;
return +value;
}
function resolutionParser(name, value) {
if (!value)
return void 0;
if (value.includes("x")) {
const [width, height] = value.split("x").map((v) => +v);
if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0)
throw new Error(`Invalid resolution format: use ${name}="800x600"`);
return { width, height };
}
if (value.includes(",")) {
const [width, height] = value.split(",").map((v) => +v);
if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0)
throw new Error(`Invalid resolution format: use ${name}="800x600"`);
return { width, height };
}
throw new Error(`Invalid resolution format: use ${name}="800x600"`);
}
function headerParser(arg, previous) {
if (!arg)
return previous || {};
const result = previous || {};
const [name, value] = arg.split(":").map((v) => v.trim());
result[name] = value;
return result;
}
function envToBoolean(value) {
if (value === "true" || value === "1")
return true;
if (value === "false" || value === "0")
return false;
return void 0;
}
function envToString(value) {
return value ? value.trim() : void 0;
}
function sanitizeForFilePath(s) {
const sanitize = (s2) => s2.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, "-");
const separator = s.lastIndexOf(".");
if (separator === -1)
return sanitize(s);
return sanitize(s.substring(0, separator)) + "." + sanitize(s.substring(separator + 1));
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
commaSeparatedList,
configFromCLIOptions,
defaultConfig,
dotenvFileLoader,
headerParser,
numberParser,
outputDir,
outputFile,
resolutionParser,
resolveCLIConfig,
resolveConfig
});

View File

@@ -0,0 +1,267 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var context_exports = {};
__export(context_exports, {
Context: () => Context,
InputRecorder: () => InputRecorder
});
module.exports = __toCommonJS(context_exports);
var import_fs = __toESM(require("fs"));
var import_path = __toESM(require("path"));
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
var import_playwright_core = require("playwright-core");
var import_log = require("../log");
var import_tab = require("./tab");
var import_config = require("./config");
var codegen = __toESM(require("./codegen"));
var import_utils = require("./tools/utils");
const testDebug = (0, import_utilsBundle.debug)("pw:mcp:test");
class Context {
constructor(options) {
this._tabs = [];
this._abortController = new AbortController();
this.config = options.config;
this.sessionLog = options.sessionLog;
this.options = options;
this._browserContextFactory = options.browserContextFactory;
this._clientInfo = options.clientInfo;
testDebug("create context");
Context._allContexts.add(this);
}
static {
this._allContexts = /* @__PURE__ */ new Set();
}
static async disposeAll() {
await Promise.all([...Context._allContexts].map((context) => context.dispose()));
}
tabs() {
return this._tabs;
}
currentTab() {
return this._currentTab;
}
currentTabOrDie() {
if (!this._currentTab)
throw new Error('No open pages available. Use the "browser_navigate" tool to navigate to a page first.');
return this._currentTab;
}
async newTab() {
const { browserContext } = await this._ensureBrowserContext();
const page = await browserContext.newPage();
this._currentTab = this._tabs.find((t) => t.page === page);
await this._currentTab.initializedPromise;
return this._currentTab;
}
async selectTab(index) {
const tab = this._tabs[index];
if (!tab)
throw new Error(`Tab ${index} not found`);
await tab.page.bringToFront();
this._currentTab = tab;
return tab;
}
async ensureTab() {
const { browserContext } = await this._ensureBrowserContext();
if (!this._currentTab)
await browserContext.newPage();
return this._currentTab;
}
async closeTab(index) {
const tab = index === void 0 ? this._currentTab : this._tabs[index];
if (!tab)
throw new Error(`Tab ${index} not found`);
const url = tab.page.url();
await tab.page.close();
return url;
}
async outputFile(fileName, options) {
return (0, import_config.outputFile)(this.config, this._clientInfo, fileName, options);
}
_onPageCreated(page) {
const tab = new import_tab.Tab(this, page, (tab2) => this._onPageClosed(tab2));
this._tabs.push(tab);
if (!this._currentTab)
this._currentTab = tab;
}
_onPageClosed(tab) {
const index = this._tabs.indexOf(tab);
if (index === -1)
return;
this._tabs.splice(index, 1);
if (this._currentTab === tab)
this._currentTab = this._tabs[Math.min(index, this._tabs.length - 1)];
if (!this._tabs.length)
void this.closeBrowserContext();
}
async closeBrowserContext() {
if (!this._closeBrowserContextPromise)
this._closeBrowserContextPromise = this._closeBrowserContextImpl().catch(import_log.logUnhandledError);
await this._closeBrowserContextPromise;
this._closeBrowserContextPromise = void 0;
}
isRunningTool() {
return this._runningToolName !== void 0;
}
setRunningTool(name) {
this._runningToolName = name;
}
async _closeBrowserContextImpl() {
if (!this._browserContextPromise)
return;
testDebug("close context");
const promise = this._browserContextPromise;
this._browserContextPromise = void 0;
await promise.then(async ({ browserContext, close }) => {
if (this.config.saveTrace)
await browserContext.tracing.stop();
const videos = this.config.saveVideo ? browserContext.pages().map((page) => page.video()).filter((video) => !!video) : [];
await close(async () => {
for (const video of videos) {
const name = await this.outputFile((0, import_utils.dateAsFileName)("webm"), { origin: "code", reason: "Saving video" });
await import_fs.default.promises.mkdir(import_path.default.dirname(name), { recursive: true });
const p = await video.path();
if (import_fs.default.existsSync(p)) {
try {
await import_fs.default.promises.rename(p, name);
} catch (e) {
if (e.code !== "EXDEV")
(0, import_log.logUnhandledError)(e);
try {
await import_fs.default.promises.copyFile(p, name);
await import_fs.default.promises.unlink(p);
} catch (e2) {
(0, import_log.logUnhandledError)(e2);
}
}
}
}
});
});
}
async dispose() {
this._abortController.abort("MCP context disposed");
await this.closeBrowserContext();
Context._allContexts.delete(this);
}
async ensureBrowserContext() {
const { browserContext } = await this._ensureBrowserContext();
return browserContext;
}
_ensureBrowserContext() {
if (!this._browserContextPromise) {
this._browserContextPromise = this._setupBrowserContext();
this._browserContextPromise.catch(() => {
this._browserContextPromise = void 0;
});
}
return this._browserContextPromise;
}
async _setupBrowserContext() {
if (this._closeBrowserContextPromise)
throw new Error("Another browser context is being closed.");
if (this.config.testIdAttribute)
import_playwright_core.selectors.setTestIdAttribute(this.config.testIdAttribute);
const result = await this._browserContextFactory.createContext(this._clientInfo, this._abortController.signal, this._runningToolName);
const { browserContext } = result;
if (this.sessionLog)
await InputRecorder.create(this, browserContext);
for (const page of browserContext.pages())
this._onPageCreated(page);
browserContext.on("page", (page) => this._onPageCreated(page));
if (this.config.saveTrace) {
await browserContext.tracing.start({
name: "trace-" + Date.now(),
screenshots: true,
snapshots: true,
_live: true
});
}
return result;
}
lookupSecret(secretName) {
if (!this.config.secrets?.[secretName])
return { value: secretName, code: codegen.quote(secretName) };
return {
value: this.config.secrets[secretName],
code: `process.env['${secretName}']`
};
}
}
class InputRecorder {
constructor(context, browserContext) {
this._context = context;
this._browserContext = browserContext;
}
static async create(context, browserContext) {
const recorder = new InputRecorder(context, browserContext);
await recorder._initialize();
return recorder;
}
async _initialize() {
const sessionLog = this._context.sessionLog;
await this._browserContext._enableRecorder({
mode: "recording",
recorderMode: "api"
}, {
actionAdded: (page, data, code) => {
if (this._context.isRunningTool())
return;
const tab = import_tab.Tab.forPage(page);
if (tab)
sessionLog.logUserAction(data.action, tab, code, false);
},
actionUpdated: (page, data, code) => {
if (this._context.isRunningTool())
return;
const tab = import_tab.Tab.forPage(page);
if (tab)
sessionLog.logUserAction(data.action, tab, code, true);
},
signalAdded: (page, data) => {
if (this._context.isRunningTool())
return;
if (data.signal.name !== "navigation")
return;
const tab = import_tab.Tab.forPage(page);
const navigateAction = {
name: "navigate",
url: data.signal.url,
signals: []
};
if (tab)
sessionLog.logUserAction(navigateAction, tab, `await page.goto('${data.signal.url}');`, false);
}
});
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Context,
InputRecorder
});

View File

@@ -0,0 +1,237 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var response_exports = {};
__export(response_exports, {
Response: () => Response,
parseResponse: () => parseResponse,
requestDebug: () => requestDebug
});
module.exports = __toCommonJS(response_exports);
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
var import_tab = require("./tab");
const requestDebug = (0, import_utilsBundle.debug)("pw:mcp:request");
class Response {
constructor(context, toolName, toolArgs) {
this._result = [];
this._code = [];
this._images = [];
this._includeSnapshot = "none";
this._includeTabs = false;
this._context = context;
this.toolName = toolName;
this.toolArgs = toolArgs;
}
addResult(result) {
this._result.push(result);
}
addError(error) {
this._result.push(error);
this._isError = true;
}
isError() {
return this._isError;
}
result() {
return this._result.join("\n");
}
addCode(code) {
this._code.push(code);
}
code() {
return this._code.join("\n");
}
addImage(image) {
this._images.push(image);
}
images() {
return this._images;
}
setIncludeSnapshot(full) {
this._includeSnapshot = full ?? "incremental";
}
setIncludeTabs() {
this._includeTabs = true;
}
async finish() {
if (this._includeSnapshot !== "none" && this._context.currentTab())
this._tabSnapshot = await this._context.currentTabOrDie().captureSnapshot();
for (const tab of this._context.tabs())
await tab.updateTitle();
}
tabSnapshot() {
return this._tabSnapshot;
}
logBegin() {
if (requestDebug.enabled)
requestDebug(this.toolName, this.toolArgs);
}
logEnd() {
if (requestDebug.enabled)
requestDebug(this.serialize({ omitSnapshot: true, omitBlobs: true }));
}
serialize(options = {}) {
const response = [];
if (this._result.length) {
response.push("### Result");
response.push(this._result.join("\n"));
response.push("");
}
if (this._code.length) {
response.push(`### Ran Playwright code
\`\`\`js
${this._code.join("\n")}
\`\`\``);
response.push("");
}
if (this._includeSnapshot !== "none" || this._includeTabs)
response.push(...renderTabsMarkdown(this._context.tabs(), this._includeTabs));
if (this._tabSnapshot?.modalStates.length) {
response.push(...(0, import_tab.renderModalStates)(this._context, this._tabSnapshot.modalStates));
response.push("");
} else if (this._tabSnapshot) {
const includeSnapshot = options.omitSnapshot ? "none" : this._includeSnapshot;
response.push(renderTabSnapshot(this._tabSnapshot, includeSnapshot));
response.push("");
}
const content = [
{ type: "text", text: response.join("\n") }
];
if (this._context.config.imageResponses !== "omit") {
for (const image of this._images)
content.push({ type: "image", data: options.omitBlobs ? "<blob>" : image.data.toString("base64"), mimeType: image.contentType });
}
this._redactSecrets(content);
return { content, isError: this._isError };
}
_redactSecrets(content) {
if (!this._context.config.secrets)
return;
for (const item of content) {
if (item.type !== "text")
continue;
for (const [secretName, secretValue] of Object.entries(this._context.config.secrets))
item.text = item.text.replaceAll(secretValue, `<secret>${secretName}</secret>`);
}
}
}
function renderTabSnapshot(tabSnapshot, includeSnapshot) {
const lines = [];
if (tabSnapshot.consoleMessages.length) {
lines.push(`### New console messages`);
for (const message of tabSnapshot.consoleMessages)
lines.push(`- ${trim(message.toString(), 100)}`);
lines.push("");
}
if (tabSnapshot.downloads.length) {
lines.push(`### Downloads`);
for (const entry of tabSnapshot.downloads) {
if (entry.finished)
lines.push(`- Downloaded file ${entry.download.suggestedFilename()} to ${entry.outputFile}`);
else
lines.push(`- Downloading file ${entry.download.suggestedFilename()} ...`);
}
lines.push("");
}
if (includeSnapshot === "incremental" && tabSnapshot.ariaSnapshotDiff === "") {
return lines.join("\n");
}
lines.push(`### Page state`);
lines.push(`- Page URL: ${tabSnapshot.url}`);
lines.push(`- Page Title: ${tabSnapshot.title}`);
if (includeSnapshot !== "none") {
lines.push(`- Page Snapshot:`);
lines.push("```yaml");
if (includeSnapshot === "incremental" && tabSnapshot.ariaSnapshotDiff !== void 0)
lines.push(tabSnapshot.ariaSnapshotDiff);
else
lines.push(tabSnapshot.ariaSnapshot);
lines.push("```");
}
return lines.join("\n");
}
function renderTabsMarkdown(tabs, force = false) {
if (tabs.length === 1 && !force)
return [];
if (!tabs.length) {
return [
"### Open tabs",
'No open tabs. Use the "browser_navigate" tool to navigate to a page first.',
""
];
}
const lines = ["### Open tabs"];
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
const current = tab.isCurrentTab() ? " (current)" : "";
lines.push(`- ${i}:${current} [${tab.lastTitle()}] (${tab.page.url()})`);
}
lines.push("");
return lines;
}
function trim(text, maxLength) {
if (text.length <= maxLength)
return text;
return text.slice(0, maxLength) + "...";
}
function parseSections(text) {
const sections = /* @__PURE__ */ new Map();
const sectionHeaders = text.split(/^### /m).slice(1);
for (const section of sectionHeaders) {
const firstNewlineIndex = section.indexOf("\n");
if (firstNewlineIndex === -1)
continue;
const sectionName = section.substring(0, firstNewlineIndex);
const sectionContent = section.substring(firstNewlineIndex + 1).trim();
sections.set(sectionName, sectionContent);
}
return sections;
}
function parseResponse(response) {
if (response.content?.[0].type !== "text")
return void 0;
const text = response.content[0].text;
const sections = parseSections(text);
const result = sections.get("Result");
const code = sections.get("Ran Playwright code");
const tabs = sections.get("Open tabs");
const pageState = sections.get("Page state");
const consoleMessages = sections.get("New console messages");
const modalState = sections.get("Modal state");
const downloads = sections.get("Downloads");
const codeNoFrame = code?.replace(/^```js\n/, "").replace(/\n```$/, "");
const isError = response.isError;
const attachments = response.content.slice(1);
return {
result,
code: codeNoFrame,
tabs,
pageState,
consoleMessages,
modalState,
downloads,
isError,
attachments
};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Response,
parseResponse,
requestDebug
});

View File

@@ -0,0 +1,160 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var sessionLog_exports = {};
__export(sessionLog_exports, {
SessionLog: () => SessionLog
});
module.exports = __toCommonJS(sessionLog_exports);
var import_fs = __toESM(require("fs"));
var import_path = __toESM(require("path"));
var import_log = require("../log");
var import_config = require("./config");
class SessionLog {
constructor(sessionFolder) {
this._ordinal = 0;
this._pendingEntries = [];
this._sessionFileQueue = Promise.resolve();
this._folder = sessionFolder;
this._file = import_path.default.join(this._folder, "session.md");
}
static async create(config, clientInfo) {
const sessionFolder = await (0, import_config.outputFile)(config, clientInfo, `session-${Date.now()}`, { origin: "code", reason: "Saving session" });
await import_fs.default.promises.mkdir(sessionFolder, { recursive: true });
console.error(`Session: ${sessionFolder}`);
return new SessionLog(sessionFolder);
}
logResponse(response) {
const entry = {
timestamp: performance.now(),
toolCall: {
toolName: response.toolName,
toolArgs: response.toolArgs,
result: response.result(),
isError: response.isError()
},
code: response.code(),
tabSnapshot: response.tabSnapshot()
};
this._appendEntry(entry);
}
logUserAction(action, tab, code, isUpdate) {
code = code.trim();
if (isUpdate) {
const lastEntry = this._pendingEntries[this._pendingEntries.length - 1];
if (lastEntry?.userAction?.name === action.name) {
lastEntry.userAction = action;
lastEntry.code = code;
return;
}
}
if (action.name === "navigate") {
const lastEntry = this._pendingEntries[this._pendingEntries.length - 1];
if (lastEntry?.tabSnapshot?.url === action.url)
return;
}
const entry = {
timestamp: performance.now(),
userAction: action,
code,
tabSnapshot: {
url: tab.page.url(),
title: "",
ariaSnapshot: action.ariaSnapshot || "",
modalStates: [],
consoleMessages: [],
downloads: []
}
};
this._appendEntry(entry);
}
_appendEntry(entry) {
this._pendingEntries.push(entry);
if (this._flushEntriesTimeout)
clearTimeout(this._flushEntriesTimeout);
this._flushEntriesTimeout = setTimeout(() => this._flushEntries(), 1e3);
}
async _flushEntries() {
clearTimeout(this._flushEntriesTimeout);
const entries = this._pendingEntries;
this._pendingEntries = [];
const lines = [""];
for (const entry of entries) {
const ordinal = (++this._ordinal).toString().padStart(3, "0");
if (entry.toolCall) {
lines.push(
`### Tool call: ${entry.toolCall.toolName}`,
`- Args`,
"```json",
JSON.stringify(entry.toolCall.toolArgs, null, 2),
"```"
);
if (entry.toolCall.result) {
lines.push(
entry.toolCall.isError ? `- Error` : `- Result`,
"```",
entry.toolCall.result,
"```"
);
}
}
if (entry.userAction) {
const actionData = { ...entry.userAction };
delete actionData.ariaSnapshot;
delete actionData.selector;
delete actionData.signals;
lines.push(
`### User action: ${entry.userAction.name}`,
`- Args`,
"```json",
JSON.stringify(actionData, null, 2),
"```"
);
}
if (entry.code) {
lines.push(
`- Code`,
"```js",
entry.code,
"```"
);
}
if (entry.tabSnapshot) {
const fileName = `${ordinal}.snapshot.yml`;
import_fs.default.promises.writeFile(import_path.default.join(this._folder, fileName), entry.tabSnapshot.ariaSnapshot).catch(import_log.logUnhandledError);
lines.push(`- Snapshot: ${fileName}`);
}
lines.push("", "");
}
this._sessionFileQueue = this._sessionFileQueue.then(() => import_fs.default.promises.appendFile(this._file, lines.join("\n")));
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
SessionLog
});

View File

@@ -0,0 +1,292 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var tab_exports = {};
__export(tab_exports, {
Tab: () => Tab,
TabEvents: () => TabEvents,
renderModalStates: () => renderModalStates
});
module.exports = __toCommonJS(tab_exports);
var import_events = require("events");
var import_utils = require("playwright-core/lib/utils");
var import_utils2 = require("./tools/utils");
var import_log = require("../log");
var import_dialogs = require("./tools/dialogs");
var import_files = require("./tools/files");
var import_transform = require("../../transform/transform");
const TabEvents = {
modalState: "modalState"
};
class Tab extends import_events.EventEmitter {
constructor(context, page, onPageClose) {
super();
this._lastTitle = "about:blank";
this._consoleMessages = [];
this._recentConsoleMessages = [];
this._requests = /* @__PURE__ */ new Set();
this._modalStates = [];
this._downloads = [];
this._needsFullSnapshot = false;
this.context = context;
this.page = page;
this._onPageClose = onPageClose;
page.on("console", (event) => this._handleConsoleMessage(messageToConsoleMessage(event)));
page.on("pageerror", (error) => this._handleConsoleMessage(pageErrorToConsoleMessage(error)));
page.on("request", (request) => this._requests.add(request));
page.on("close", () => this._onClose());
page.on("filechooser", (chooser) => {
this.setModalState({
type: "fileChooser",
description: "File chooser",
fileChooser: chooser,
clearedBy: import_files.uploadFile.schema.name
});
});
page.on("dialog", (dialog) => this._dialogShown(dialog));
page.on("download", (download) => {
void this._downloadStarted(download);
});
page.setDefaultNavigationTimeout(this.context.config.timeouts.navigation);
page.setDefaultTimeout(this.context.config.timeouts.action);
page[tabSymbol] = this;
this.initializedPromise = this._initialize();
}
static forPage(page) {
return page[tabSymbol];
}
static async collectConsoleMessages(page) {
const result = [];
const messages = await page.consoleMessages().catch(() => []);
for (const message of messages)
result.push(messageToConsoleMessage(message));
const errors = await page.pageErrors().catch(() => []);
for (const error of errors)
result.push(pageErrorToConsoleMessage(error));
return result;
}
async _initialize() {
for (const message of await Tab.collectConsoleMessages(this.page))
this._handleConsoleMessage(message);
const requests = await this.page.requests().catch(() => []);
for (const request of requests)
this._requests.add(request);
for (const initPage of this.context.config.browser.initPage || []) {
try {
const { default: func } = await (0, import_transform.requireOrImport)(initPage);
await func({ page: this.page });
} catch (e) {
(0, import_log.logUnhandledError)(e);
}
}
}
modalStates() {
return this._modalStates;
}
setModalState(modalState) {
this._modalStates.push(modalState);
this.emit(TabEvents.modalState, modalState);
}
clearModalState(modalState) {
this._modalStates = this._modalStates.filter((state) => state !== modalState);
}
modalStatesMarkdown() {
return renderModalStates(this.context, this.modalStates());
}
_dialogShown(dialog) {
this.setModalState({
type: "dialog",
description: `"${dialog.type()}" dialog with message "${dialog.message()}"`,
dialog,
clearedBy: import_dialogs.handleDialog.schema.name
});
}
async _downloadStarted(download) {
const entry = {
download,
finished: false,
outputFile: await this.context.outputFile(download.suggestedFilename(), { origin: "web", reason: "Saving download" })
};
this._downloads.push(entry);
await download.saveAs(entry.outputFile);
entry.finished = true;
}
_clearCollectedArtifacts() {
this._consoleMessages.length = 0;
this._recentConsoleMessages.length = 0;
this._requests.clear();
}
_handleConsoleMessage(message) {
this._consoleMessages.push(message);
this._recentConsoleMessages.push(message);
}
_onClose() {
this._clearCollectedArtifacts();
this._onPageClose(this);
}
async updateTitle() {
await this._raceAgainstModalStates(async () => {
this._lastTitle = await (0, import_utils2.callOnPageNoTrace)(this.page, (page) => page.title());
});
}
lastTitle() {
return this._lastTitle;
}
isCurrentTab() {
return this === this.context.currentTab();
}
async waitForLoadState(state, options) {
await (0, import_utils2.callOnPageNoTrace)(this.page, (page) => page.waitForLoadState(state, options).catch(import_log.logUnhandledError));
}
async navigate(url) {
this._clearCollectedArtifacts();
const downloadEvent = (0, import_utils2.callOnPageNoTrace)(this.page, (page) => page.waitForEvent("download").catch(import_log.logUnhandledError));
try {
await this.page.goto(url, { waitUntil: "domcontentloaded" });
} catch (_e) {
const e = _e;
const mightBeDownload = e.message.includes("net::ERR_ABORTED") || e.message.includes("Download is starting");
if (!mightBeDownload)
throw e;
const download = await Promise.race([
downloadEvent,
new Promise((resolve) => setTimeout(resolve, 3e3))
]);
if (!download)
throw e;
await new Promise((resolve) => setTimeout(resolve, 500));
return;
}
await this.waitForLoadState("load", { timeout: 5e3 });
}
async consoleMessages(type) {
await this.initializedPromise;
return this._consoleMessages.filter((message) => type ? message.type === type : true);
}
async requests() {
await this.initializedPromise;
return this._requests;
}
async captureSnapshot() {
let tabSnapshot;
const modalStates = await this._raceAgainstModalStates(async () => {
const snapshot = await this.page._snapshotForAI({ track: "response" });
tabSnapshot = {
url: this.page.url(),
title: await this.page.title(),
ariaSnapshot: snapshot.full,
ariaSnapshotDiff: this._needsFullSnapshot ? void 0 : snapshot.incremental,
modalStates: [],
consoleMessages: [],
downloads: this._downloads
};
});
if (tabSnapshot) {
tabSnapshot.consoleMessages = this._recentConsoleMessages;
this._recentConsoleMessages = [];
}
this._needsFullSnapshot = !tabSnapshot;
return tabSnapshot ?? {
url: this.page.url(),
title: "",
ariaSnapshot: "",
modalStates,
consoleMessages: [],
downloads: []
};
}
_javaScriptBlocked() {
return this._modalStates.some((state) => state.type === "dialog");
}
async _raceAgainstModalStates(action) {
if (this.modalStates().length)
return this.modalStates();
const promise = new import_utils.ManualPromise();
const listener = (modalState) => promise.resolve([modalState]);
this.once(TabEvents.modalState, listener);
return await Promise.race([
action().then(() => {
this.off(TabEvents.modalState, listener);
return [];
}),
promise
]);
}
async waitForCompletion(callback) {
await this._raceAgainstModalStates(() => (0, import_utils2.waitForCompletion)(this, callback));
}
async refLocator(params) {
return (await this.refLocators([params]))[0];
}
async refLocators(params) {
return Promise.all(params.map(async (param) => {
try {
const locator = this.page.locator(`aria-ref=${param.ref}`).describe(param.element);
const { resolvedSelector } = await locator._resolveSelector();
return { locator, resolved: (0, import_utils.asLocator)("javascript", resolvedSelector) };
} catch (e) {
throw new Error(`Ref ${param.ref} not found in the current page snapshot. Try capturing new snapshot.`);
}
}));
}
async waitForTimeout(time) {
if (this._javaScriptBlocked()) {
await new Promise((f) => setTimeout(f, time));
return;
}
await (0, import_utils2.callOnPageNoTrace)(this.page, (page) => {
return page.evaluate(() => new Promise((f) => setTimeout(f, 1e3)));
});
}
}
function messageToConsoleMessage(message) {
return {
type: message.type(),
text: message.text(),
toString: () => `[${message.type().toUpperCase()}] ${message.text()} @ ${message.location().url}:${message.location().lineNumber}`
};
}
function pageErrorToConsoleMessage(errorOrValue) {
if (errorOrValue instanceof Error) {
return {
type: "error",
text: errorOrValue.message,
toString: () => errorOrValue.stack || errorOrValue.message
};
}
return {
type: "error",
text: String(errorOrValue),
toString: () => String(errorOrValue)
};
}
function renderModalStates(context, modalStates) {
const result = ["### Modal state"];
if (modalStates.length === 0)
result.push("- There is no modal state present");
for (const state of modalStates)
result.push(`- [${state.description}]: can be handled by the "${state.clearedBy}" tool`);
return result;
}
const tabSymbol = Symbol("tabSymbol");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Tab,
TabEvents,
renderModalStates
});

View File

@@ -0,0 +1,82 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var tools_exports = {};
__export(tools_exports, {
browserTools: () => browserTools,
filteredTools: () => filteredTools
});
module.exports = __toCommonJS(tools_exports);
var import_common = __toESM(require("./tools/common"));
var import_console = __toESM(require("./tools/console"));
var import_dialogs = __toESM(require("./tools/dialogs"));
var import_evaluate = __toESM(require("./tools/evaluate"));
var import_files = __toESM(require("./tools/files"));
var import_form = __toESM(require("./tools/form"));
var import_install = __toESM(require("./tools/install"));
var import_keyboard = __toESM(require("./tools/keyboard"));
var import_mouse = __toESM(require("./tools/mouse"));
var import_navigate = __toESM(require("./tools/navigate"));
var import_network = __toESM(require("./tools/network"));
var import_pdf = __toESM(require("./tools/pdf"));
var import_runCode = __toESM(require("./tools/runCode"));
var import_snapshot = __toESM(require("./tools/snapshot"));
var import_screenshot = __toESM(require("./tools/screenshot"));
var import_tabs = __toESM(require("./tools/tabs"));
var import_tracing = __toESM(require("./tools/tracing"));
var import_wait = __toESM(require("./tools/wait"));
var import_verify = __toESM(require("./tools/verify"));
const browserTools = [
...import_common.default,
...import_console.default,
...import_dialogs.default,
...import_evaluate.default,
...import_files.default,
...import_form.default,
...import_install.default,
...import_keyboard.default,
...import_navigate.default,
...import_network.default,
...import_mouse.default,
...import_pdf.default,
...import_runCode.default,
...import_screenshot.default,
...import_snapshot.default,
...import_tabs.default,
...import_tracing.default,
...import_wait.default,
...import_verify.default
];
function filteredTools(config) {
return browserTools.filter((tool) => tool.capability.startsWith("core") || config.capabilities?.includes(tool.capability));
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
browserTools,
filteredTools
});

View File

@@ -0,0 +1,63 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var common_exports = {};
__export(common_exports, {
default: () => common_default
});
module.exports = __toCommonJS(common_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
const close = (0, import_tool.defineTool)({
capability: "core",
schema: {
name: "browser_close",
title: "Close browser",
description: "Close the page",
inputSchema: import_bundle.z.object({}),
type: "action"
},
handle: async (context, params, response) => {
await context.closeBrowserContext();
response.setIncludeTabs();
response.addCode(`await page.close()`);
}
});
const resize = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_resize",
title: "Resize browser window",
description: "Resize the browser window",
inputSchema: import_bundle.z.object({
width: import_bundle.z.number().describe("Width of the browser window"),
height: import_bundle.z.number().describe("Height of the browser window")
}),
type: "action"
},
handle: async (tab, params, response) => {
response.addCode(`await page.setViewportSize({ width: ${params.width}, height: ${params.height} });`);
await tab.waitForCompletion(async () => {
await tab.page.setViewportSize({ width: params.width, height: params.height });
});
}
});
var common_default = [
close,
resize
];

View File

@@ -0,0 +1,44 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var console_exports = {};
__export(console_exports, {
default: () => console_default
});
module.exports = __toCommonJS(console_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
const console = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_console_messages",
title: "Get console messages",
description: "Returns all console messages",
inputSchema: import_bundle.z.object({
onlyErrors: import_bundle.z.boolean().optional().describe("Only return error messages")
}),
type: "readOnly"
},
handle: async (tab, params, response) => {
const messages = await tab.consoleMessages(params.onlyErrors ? "error" : void 0);
messages.map((message) => response.addResult(message.toString()));
}
});
var console_default = [
console
];

View File

@@ -0,0 +1,60 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var dialogs_exports = {};
__export(dialogs_exports, {
default: () => dialogs_default,
handleDialog: () => handleDialog
});
module.exports = __toCommonJS(dialogs_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
const handleDialog = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_handle_dialog",
title: "Handle a dialog",
description: "Handle a dialog",
inputSchema: import_bundle.z.object({
accept: import_bundle.z.boolean().describe("Whether to accept the dialog."),
promptText: import_bundle.z.string().optional().describe("The text of the prompt in case of a prompt dialog.")
}),
type: "action"
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
const dialogState = tab.modalStates().find((state) => state.type === "dialog");
if (!dialogState)
throw new Error("No dialog visible");
tab.clearModalState(dialogState);
await tab.waitForCompletion(async () => {
if (params.accept)
await dialogState.dialog.accept(params.promptText);
else
await dialogState.dialog.dismiss();
});
},
clearsModalState: "dialog"
});
var dialogs_default = [
handleDialog
];
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
handleDialog
});

View File

@@ -0,0 +1,69 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var evaluate_exports = {};
__export(evaluate_exports, {
default: () => evaluate_default
});
module.exports = __toCommonJS(evaluate_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
var javascript = __toESM(require("../codegen"));
const evaluateSchema = import_bundle.z.object({
function: import_bundle.z.string().describe("() => { /* code */ } or (element) => { /* code */ } when element is provided"),
element: import_bundle.z.string().optional().describe("Human-readable element description used to obtain permission to interact with the element"),
ref: import_bundle.z.string().optional().describe("Exact target element reference from the page snapshot")
});
const evaluate = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_evaluate",
title: "Evaluate JavaScript",
description: "Evaluate JavaScript expression on page or element",
inputSchema: evaluateSchema,
type: "action"
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
let locator;
if (params.ref && params.element) {
locator = await tab.refLocator({ ref: params.ref, element: params.element });
response.addCode(`await page.${locator.resolved}.evaluate(${javascript.quote(params.function)});`);
} else {
response.addCode(`await page.evaluate(${javascript.quote(params.function)});`);
}
await tab.waitForCompletion(async () => {
const receiver = locator?.locator ?? tab.page;
const result = await receiver._evaluateFunction(params.function);
response.addResult(JSON.stringify(result, null, 2) || "undefined");
});
}
});
var evaluate_default = [
evaluate
];

View File

@@ -0,0 +1,58 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var files_exports = {};
__export(files_exports, {
default: () => files_default,
uploadFile: () => uploadFile
});
module.exports = __toCommonJS(files_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
const uploadFile = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_file_upload",
title: "Upload files",
description: "Upload one or multiple files",
inputSchema: import_bundle.z.object({
paths: import_bundle.z.array(import_bundle.z.string()).optional().describe("The absolute paths to the files to upload. Can be single file or multiple files. If omitted, file chooser is cancelled.")
}),
type: "action"
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
const modalState = tab.modalStates().find((state) => state.type === "fileChooser");
if (!modalState)
throw new Error("No file chooser visible");
response.addCode(`await fileChooser.setFiles(${JSON.stringify(params.paths)})`);
tab.clearModalState(modalState);
await tab.waitForCompletion(async () => {
if (params.paths)
await modalState.fileChooser.setFiles(params.paths);
});
},
clearsModalState: "fileChooser"
});
var files_default = [
uploadFile
];
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
uploadFile
});

View File

@@ -0,0 +1,73 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var form_exports = {};
__export(form_exports, {
default: () => form_default
});
module.exports = __toCommonJS(form_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
var codegen = __toESM(require("../codegen"));
const fillForm = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_fill_form",
title: "Fill form",
description: "Fill multiple form fields",
inputSchema: import_bundle.z.object({
fields: import_bundle.z.array(import_bundle.z.object({
name: import_bundle.z.string().describe("Human-readable field name"),
type: import_bundle.z.enum(["textbox", "checkbox", "radio", "combobox", "slider"]).describe("Type of the field"),
ref: import_bundle.z.string().describe("Exact target field reference from the page snapshot"),
value: import_bundle.z.string().describe("Value to fill in the field. If the field is a checkbox, the value should be `true` or `false`. If the field is a combobox, the value should be the text of the option.")
})).describe("Fields to fill in")
}),
type: "input"
},
handle: async (tab, params, response) => {
for (const field of params.fields) {
const { locator, resolved } = await tab.refLocator({ element: field.name, ref: field.ref });
const locatorSource = `await page.${resolved}`;
if (field.type === "textbox" || field.type === "slider") {
const secret = tab.context.lookupSecret(field.value);
await locator.fill(secret.value);
response.addCode(`${locatorSource}.fill(${secret.code});`);
} else if (field.type === "checkbox" || field.type === "radio") {
await locator.setChecked(field.value === "true");
response.addCode(`${locatorSource}.setChecked(${field.value});`);
} else if (field.type === "combobox") {
await locator.selectOption({ label: field.value });
response.addCode(`${locatorSource}.selectOption(${codegen.quote(field.value)});`);
}
}
}
});
var form_default = [
fillForm
];

View File

@@ -0,0 +1,69 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var install_exports = {};
__export(install_exports, {
default: () => install_default
});
module.exports = __toCommonJS(install_exports);
var import_child_process = require("child_process");
var import_path = __toESM(require("path"));
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
const install = (0, import_tool.defineTool)({
capability: "core-install",
schema: {
name: "browser_install",
title: "Install the browser specified in the config",
description: "Install the browser specified in the config. Call this if you get an error about the browser not being installed.",
inputSchema: import_bundle.z.object({}),
type: "action"
},
handle: async (context, params, response) => {
const channel = context.config.browser?.launchOptions?.channel ?? context.config.browser?.browserName ?? "chrome";
const cliPath = import_path.default.join(require.resolve("playwright/package.json"), "../cli.js");
const child = (0, import_child_process.fork)(cliPath, ["install", channel], {
stdio: "pipe"
});
const output = [];
child.stdout?.on("data", (data) => output.push(data.toString()));
child.stderr?.on("data", (data) => output.push(data.toString()));
await new Promise((resolve, reject) => {
child.on("close", (code) => {
if (code === 0)
resolve();
else
reject(new Error(`Failed to install browser: ${output.join("")}`));
});
});
response.setIncludeTabs();
}
});
var install_default = [
install
];

View File

@@ -0,0 +1,84 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var keyboard_exports = {};
__export(keyboard_exports, {
default: () => keyboard_default
});
module.exports = __toCommonJS(keyboard_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
var import_snapshot = require("./snapshot");
const pressKey = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_press_key",
title: "Press a key",
description: "Press a key on the keyboard",
inputSchema: import_bundle.z.object({
key: import_bundle.z.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")
}),
type: "input"
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
response.addCode(`// Press ${params.key}`);
response.addCode(`await page.keyboard.press('${params.key}');`);
await tab.waitForCompletion(async () => {
await tab.page.keyboard.press(params.key);
});
}
});
const typeSchema = import_snapshot.elementSchema.extend({
text: import_bundle.z.string().describe("Text to type into the element"),
submit: import_bundle.z.boolean().optional().describe("Whether to submit entered text (press Enter after)"),
slowly: import_bundle.z.boolean().optional().describe("Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.")
});
const type = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_type",
title: "Type text",
description: "Type text into editable element",
inputSchema: typeSchema,
type: "input"
},
handle: async (tab, params, response) => {
const { locator, resolved } = await tab.refLocator(params);
const secret = tab.context.lookupSecret(params.text);
await tab.waitForCompletion(async () => {
if (params.slowly) {
response.setIncludeSnapshot();
response.addCode(`await page.${resolved}.pressSequentially(${secret.code});`);
await locator.pressSequentially(secret.value);
} else {
response.addCode(`await page.${resolved}.fill(${secret.code});`);
await locator.fill(secret.value);
}
if (params.submit) {
response.setIncludeSnapshot();
response.addCode(`await page.${resolved}.press('Enter');`);
await locator.press("Enter");
}
});
}
});
var keyboard_default = [
pressKey,
type
];

View File

@@ -0,0 +1,107 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var mouse_exports = {};
__export(mouse_exports, {
default: () => mouse_default
});
module.exports = __toCommonJS(mouse_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
const elementSchema = import_bundle.z.object({
element: import_bundle.z.string().describe("Human-readable element description used to obtain permission to interact with the element")
});
const mouseMove = (0, import_tool.defineTabTool)({
capability: "vision",
schema: {
name: "browser_mouse_move_xy",
title: "Move mouse",
description: "Move mouse to a given position",
inputSchema: elementSchema.extend({
x: import_bundle.z.number().describe("X coordinate"),
y: import_bundle.z.number().describe("Y coordinate")
}),
type: "input"
},
handle: async (tab, params, response) => {
response.addCode(`// Move mouse to (${params.x}, ${params.y})`);
response.addCode(`await page.mouse.move(${params.x}, ${params.y});`);
await tab.waitForCompletion(async () => {
await tab.page.mouse.move(params.x, params.y);
});
}
});
const mouseClick = (0, import_tool.defineTabTool)({
capability: "vision",
schema: {
name: "browser_mouse_click_xy",
title: "Click",
description: "Click left mouse button at a given position",
inputSchema: elementSchema.extend({
x: import_bundle.z.number().describe("X coordinate"),
y: import_bundle.z.number().describe("Y coordinate")
}),
type: "input"
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
response.addCode(`// Click mouse at coordinates (${params.x}, ${params.y})`);
response.addCode(`await page.mouse.move(${params.x}, ${params.y});`);
response.addCode(`await page.mouse.down();`);
response.addCode(`await page.mouse.up();`);
await tab.waitForCompletion(async () => {
await tab.page.mouse.move(params.x, params.y);
await tab.page.mouse.down();
await tab.page.mouse.up();
});
}
});
const mouseDrag = (0, import_tool.defineTabTool)({
capability: "vision",
schema: {
name: "browser_mouse_drag_xy",
title: "Drag mouse",
description: "Drag left mouse button to a given position",
inputSchema: elementSchema.extend({
startX: import_bundle.z.number().describe("Start X coordinate"),
startY: import_bundle.z.number().describe("Start Y coordinate"),
endX: import_bundle.z.number().describe("End X coordinate"),
endY: import_bundle.z.number().describe("End Y coordinate")
}),
type: "input"
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
response.addCode(`// Drag mouse from (${params.startX}, ${params.startY}) to (${params.endX}, ${params.endY})`);
response.addCode(`await page.mouse.move(${params.startX}, ${params.startY});`);
response.addCode(`await page.mouse.down();`);
response.addCode(`await page.mouse.move(${params.endX}, ${params.endY});`);
response.addCode(`await page.mouse.up();`);
await tab.waitForCompletion(async () => {
await tab.page.mouse.move(params.startX, params.startY);
await tab.page.mouse.down();
await tab.page.mouse.move(params.endX, params.endY);
await tab.page.mouse.up();
});
}
});
var mouse_default = [
mouseMove,
mouseClick,
mouseDrag
];

View File

@@ -0,0 +1,62 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var navigate_exports = {};
__export(navigate_exports, {
default: () => navigate_default
});
module.exports = __toCommonJS(navigate_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
const navigate = (0, import_tool.defineTool)({
capability: "core",
schema: {
name: "browser_navigate",
title: "Navigate to a URL",
description: "Navigate to a URL",
inputSchema: import_bundle.z.object({
url: import_bundle.z.string().describe("The URL to navigate to")
}),
type: "action"
},
handle: async (context, params, response) => {
const tab = await context.ensureTab();
await tab.navigate(params.url);
response.setIncludeSnapshot();
response.addCode(`await page.goto('${params.url}');`);
}
});
const goBack = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_navigate_back",
title: "Go back",
description: "Go back to the previous page",
inputSchema: import_bundle.z.object({}),
type: "action"
},
handle: async (tab, params, response) => {
await tab.page.goBack();
response.setIncludeSnapshot();
response.addCode(`await page.goBack();`);
}
});
var navigate_default = [
navigate,
goBack
];

View File

@@ -0,0 +1,54 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var network_exports = {};
__export(network_exports, {
default: () => network_default
});
module.exports = __toCommonJS(network_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
const requests = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_network_requests",
title: "List network requests",
description: "Returns all network requests since loading the page",
inputSchema: import_bundle.z.object({}),
type: "readOnly"
},
handle: async (tab, params, response) => {
const requests2 = await tab.requests();
for (const request of requests2)
response.addResult(await renderRequest(request));
}
});
async function renderRequest(request) {
const result = [];
result.push(`[${request.method().toUpperCase()}] ${request.url()}`);
const hasResponse = request._hasResponse;
if (hasResponse) {
const response = await request.response();
if (response)
result.push(`=> [${response.status()}] ${response.statusText()}`);
}
return result.join(" ");
}
var network_default = [
requests
];

View File

@@ -0,0 +1,59 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var pdf_exports = {};
__export(pdf_exports, {
default: () => pdf_default
});
module.exports = __toCommonJS(pdf_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
var javascript = __toESM(require("../codegen"));
var import_utils = require("./utils");
const pdfSchema = import_bundle.z.object({
filename: import_bundle.z.string().optional().describe("File name to save the pdf to. Defaults to `page-{timestamp}.pdf` if not specified. Prefer relative file names to stay within the output directory.")
});
const pdf = (0, import_tool.defineTabTool)({
capability: "pdf",
schema: {
name: "browser_pdf_save",
title: "Save as PDF",
description: "Save page as PDF",
inputSchema: pdfSchema,
type: "readOnly"
},
handle: async (tab, params, response) => {
const fileName = await tab.context.outputFile(params.filename ?? (0, import_utils.dateAsFileName)("pdf"), { origin: "llm", reason: "Saving PDF" });
response.addCode(`await page.pdf(${javascript.formatObject({ path: fileName })});`);
response.addResult(`Saved page as ${fileName}`);
await tab.page.pdf({ path: fileName });
}
});
var pdf_default = [
pdf
];

View File

@@ -0,0 +1,75 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var runCode_exports = {};
__export(runCode_exports, {
default: () => runCode_default
});
module.exports = __toCommonJS(runCode_exports);
var import_vm = __toESM(require("vm"));
var import_utils = require("playwright-core/lib/utils");
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
const codeSchema = import_bundle.z.object({
code: import_bundle.z.string().describe(`Playwright code snippet to run. The snippet should access the \`page\` object to interact with the page. Can make multiple statements. For example: \`await page.getByRole('button', { name: 'Submit' }).click();\``)
});
const runCode = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_run_code",
title: "Run Playwright code",
description: "Run Playwright code snippet",
inputSchema: codeSchema,
type: "action"
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
response.addCode(params.code);
const __end__ = new import_utils.ManualPromise();
const context = {
page: tab.page,
__end__
};
import_vm.default.createContext(context);
await tab.waitForCompletion(async () => {
const snippet = `(async () => {
try {
${params.code};
__end__.resolve();
} catch (e) {
__end__.reject(e);
}
})()`;
import_vm.default.runInContext(snippet, context);
await __end__;
});
}
});
var runCode_default = [
runCode
];

View File

@@ -0,0 +1,106 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var screenshot_exports = {};
__export(screenshot_exports, {
default: () => screenshot_default,
scaleImageToFitMessage: () => scaleImageToFitMessage
});
module.exports = __toCommonJS(screenshot_exports);
var import_fs = __toESM(require("fs"));
var import_utils = require("playwright-core/lib/utils");
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
var javascript = __toESM(require("../codegen"));
var import_utils2 = require("./utils");
const screenshotSchema = import_bundle.z.object({
type: import_bundle.z.enum(["png", "jpeg"]).default("png").describe("Image format for the screenshot. Default is png."),
filename: import_bundle.z.string().optional().describe("File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg}` if not specified. Prefer relative file names to stay within the output directory."),
element: import_bundle.z.string().optional().describe("Human-readable element description used to obtain permission to screenshot the element. If not provided, the screenshot will be taken of viewport. If element is provided, ref must be provided too."),
ref: import_bundle.z.string().optional().describe("Exact target element reference from the page snapshot. If not provided, the screenshot will be taken of viewport. If ref is provided, element must be provided too."),
fullPage: import_bundle.z.boolean().optional().describe("When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Cannot be used with element screenshots.")
});
const screenshot = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_take_screenshot",
title: "Take a screenshot",
description: `Take a screenshot of the current page. You can't perform actions based on the screenshot, use browser_snapshot for actions.`,
inputSchema: screenshotSchema,
type: "readOnly"
},
handle: async (tab, params, response) => {
if (!!params.element !== !!params.ref)
throw new Error("Both element and ref must be provided or neither.");
if (params.fullPage && params.ref)
throw new Error("fullPage cannot be used with element screenshots.");
const fileType = params.type || "png";
const fileName = await tab.context.outputFile(params.filename || (0, import_utils2.dateAsFileName)(fileType), { origin: "llm", reason: "Saving screenshot" });
const options = {
type: fileType,
quality: fileType === "png" ? void 0 : 90,
scale: "css",
...params.fullPage !== void 0 && { fullPage: params.fullPage }
};
const isElementScreenshot = params.element && params.ref;
const screenshotTarget = isElementScreenshot ? params.element : params.fullPage ? "full page" : "viewport";
response.addCode(`// Screenshot ${screenshotTarget} and save it as ${fileName}`);
const ref = params.ref ? await tab.refLocator({ element: params.element || "", ref: params.ref }) : null;
if (ref)
response.addCode(`await page.${ref.resolved}.screenshot(${javascript.formatObject(options)});`);
else
response.addCode(`await page.screenshot(${javascript.formatObject(options)});`);
const buffer = ref ? await ref.locator.screenshot(options) : await tab.page.screenshot(options);
await (0, import_utils.mkdirIfNeeded)(fileName);
await import_fs.default.promises.writeFile(fileName, buffer);
response.addResult(`Took the ${screenshotTarget} screenshot and saved it as ${fileName}`);
response.addImage({
contentType: fileType === "png" ? "image/png" : "image/jpeg",
data: scaleImageToFitMessage(buffer, fileType)
});
}
});
function scaleImageToFitMessage(buffer, imageType) {
const image = imageType === "png" ? import_utilsBundle.PNG.sync.read(buffer) : import_utilsBundle.jpegjs.decode(buffer, { maxMemoryUsageInMB: 512 });
const pixels = image.width * image.height;
const shrink = Math.min(1568 / image.width, 1568 / image.height, Math.sqrt(1.15 * 1024 * 1024 / pixels));
if (shrink > 1)
return buffer;
const width = image.width * shrink | 0;
const height = image.height * shrink | 0;
const scaledImage = (0, import_utils.scaleImageToSize)(image, { width, height });
return imageType === "png" ? import_utilsBundle.PNG.sync.write(scaledImage) : import_utilsBundle.jpegjs.encode(scaledImage, 80).data;
}
var screenshot_default = [
screenshot
];
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
scaleImageToFitMessage
});

View File

@@ -0,0 +1,181 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var snapshot_exports = {};
__export(snapshot_exports, {
default: () => snapshot_default,
elementSchema: () => elementSchema
});
module.exports = __toCommonJS(snapshot_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
var javascript = __toESM(require("../codegen"));
const snapshot = (0, import_tool.defineTool)({
capability: "core",
schema: {
name: "browser_snapshot",
title: "Page snapshot",
description: "Capture accessibility snapshot of the current page, this is better than screenshot",
inputSchema: import_bundle.z.object({}),
type: "readOnly"
},
handle: async (context, params, response) => {
await context.ensureTab();
response.setIncludeSnapshot("full");
}
});
const elementSchema = import_bundle.z.object({
element: import_bundle.z.string().describe("Human-readable element description used to obtain permission to interact with the element"),
ref: import_bundle.z.string().describe("Exact target element reference from the page snapshot")
});
const clickSchema = elementSchema.extend({
doubleClick: import_bundle.z.boolean().optional().describe("Whether to perform a double click instead of a single click"),
button: import_bundle.z.enum(["left", "right", "middle"]).optional().describe("Button to click, defaults to left"),
modifiers: import_bundle.z.array(import_bundle.z.enum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"])).optional().describe("Modifier keys to press")
});
const click = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_click",
title: "Click",
description: "Perform click on a web page",
inputSchema: clickSchema,
type: "input"
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
const { locator, resolved } = await tab.refLocator(params);
const options = {
button: params.button,
modifiers: params.modifiers
};
const formatted = javascript.formatObject(options, " ", "oneline");
const optionsAttr = formatted !== "{}" ? formatted : "";
if (params.doubleClick)
response.addCode(`await page.${resolved}.dblclick(${optionsAttr});`);
else
response.addCode(`await page.${resolved}.click(${optionsAttr});`);
await tab.waitForCompletion(async () => {
if (params.doubleClick)
await locator.dblclick(options);
else
await locator.click(options);
});
}
});
const drag = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_drag",
title: "Drag mouse",
description: "Perform drag and drop between two elements",
inputSchema: import_bundle.z.object({
startElement: import_bundle.z.string().describe("Human-readable source element description used to obtain the permission to interact with the element"),
startRef: import_bundle.z.string().describe("Exact source element reference from the page snapshot"),
endElement: import_bundle.z.string().describe("Human-readable target element description used to obtain the permission to interact with the element"),
endRef: import_bundle.z.string().describe("Exact target element reference from the page snapshot")
}),
type: "input"
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
const [start, end] = await tab.refLocators([
{ ref: params.startRef, element: params.startElement },
{ ref: params.endRef, element: params.endElement }
]);
await tab.waitForCompletion(async () => {
await start.locator.dragTo(end.locator);
});
response.addCode(`await page.${start.resolved}.dragTo(page.${end.resolved});`);
}
});
const hover = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_hover",
title: "Hover mouse",
description: "Hover over element on page",
inputSchema: elementSchema,
type: "input"
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
const { locator, resolved } = await tab.refLocator(params);
response.addCode(`await page.${resolved}.hover();`);
await tab.waitForCompletion(async () => {
await locator.hover();
});
}
});
const selectOptionSchema = elementSchema.extend({
values: import_bundle.z.array(import_bundle.z.string()).describe("Array of values to select in the dropdown. This can be a single value or multiple values.")
});
const selectOption = (0, import_tool.defineTabTool)({
capability: "core",
schema: {
name: "browser_select_option",
title: "Select option",
description: "Select an option in a dropdown",
inputSchema: selectOptionSchema,
type: "input"
},
handle: async (tab, params, response) => {
response.setIncludeSnapshot();
const { locator, resolved } = await tab.refLocator(params);
response.addCode(`await page.${resolved}.selectOption(${javascript.formatObject(params.values)});`);
await tab.waitForCompletion(async () => {
await locator.selectOption(params.values);
});
}
});
const pickLocator = (0, import_tool.defineTabTool)({
capability: "testing",
schema: {
name: "browser_generate_locator",
title: "Create locator for element",
description: "Generate locator for the given element to use in tests",
inputSchema: elementSchema,
type: "readOnly"
},
handle: async (tab, params, response) => {
const { resolved } = await tab.refLocator(params);
response.addResult(resolved);
}
});
var snapshot_default = [
snapshot,
click,
drag,
hover,
selectOption,
pickLocator
];
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
elementSchema
});

View File

@@ -0,0 +1,67 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var tabs_exports = {};
__export(tabs_exports, {
default: () => tabs_default
});
module.exports = __toCommonJS(tabs_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
const browserTabs = (0, import_tool.defineTool)({
capability: "core-tabs",
schema: {
name: "browser_tabs",
title: "Manage tabs",
description: "List, create, close, or select a browser tab.",
inputSchema: import_bundle.z.object({
action: import_bundle.z.enum(["list", "new", "close", "select"]).describe("Operation to perform"),
index: import_bundle.z.number().optional().describe("Tab index, used for close/select. If omitted for close, current tab is closed.")
}),
type: "action"
},
handle: async (context, params, response) => {
switch (params.action) {
case "list": {
await context.ensureTab();
response.setIncludeTabs();
return;
}
case "new": {
await context.newTab();
response.setIncludeTabs();
return;
}
case "close": {
await context.closeTab(params.index);
response.setIncludeSnapshot("full");
return;
}
case "select": {
if (params.index === void 0)
throw new Error("Tab index is required");
await context.selectTab(params.index);
response.setIncludeSnapshot("full");
return;
}
}
}
});
var tabs_default = [
browserTabs
];

View File

@@ -0,0 +1,49 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var tool_exports = {};
__export(tool_exports, {
defineTabTool: () => defineTabTool,
defineTool: () => defineTool
});
module.exports = __toCommonJS(tool_exports);
function defineTool(tool) {
return tool;
}
function defineTabTool(tool) {
return {
...tool,
handle: async (context, params, response) => {
const tab = await context.ensureTab();
const modalStates = tab.modalStates().map((state) => state.type);
if (tool.clearsModalState && !modalStates.includes(tool.clearsModalState))
response.addError(`Error: The tool "${tool.schema.name}" can only be used when there is related modal state present.
` + tab.modalStatesMarkdown().join("\n"));
else if (!tool.clearsModalState && modalStates.length)
response.addError(`Error: Tool "${tool.schema.name}" does not handle the modal state.
` + tab.modalStatesMarkdown().join("\n"));
else
return tool.handle(tab, params, response);
}
};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
defineTabTool,
defineTool
});

View File

@@ -0,0 +1,74 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var tracing_exports = {};
__export(tracing_exports, {
default: () => tracing_default
});
module.exports = __toCommonJS(tracing_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
const tracingStart = (0, import_tool.defineTool)({
capability: "tracing",
schema: {
name: "browser_start_tracing",
title: "Start tracing",
description: "Start trace recording",
inputSchema: import_bundle.z.object({}),
type: "readOnly"
},
handle: async (context, params, response) => {
const browserContext = await context.ensureBrowserContext();
const tracesDir = await context.outputFile(`traces`, { origin: "code", reason: "Collecting trace" });
const name = "trace-" + Date.now();
await browserContext.tracing.start({
name,
screenshots: true,
snapshots: true,
_live: true
});
const traceLegend = `- Action log: ${tracesDir}/${name}.trace
- Network log: ${tracesDir}/${name}.network
- Resources with content by sha1: ${tracesDir}/resources`;
response.addResult(`Tracing started, saving to ${tracesDir}.
${traceLegend}`);
browserContext.tracing[traceLegendSymbol] = traceLegend;
}
});
const tracingStop = (0, import_tool.defineTool)({
capability: "tracing",
schema: {
name: "browser_stop_tracing",
title: "Stop tracing",
description: "Stop trace recording",
inputSchema: import_bundle.z.object({}),
type: "readOnly"
},
handle: async (context, params, response) => {
const browserContext = await context.ensureBrowserContext();
await browserContext.tracing.stop();
const traceLegend = browserContext.tracing[traceLegendSymbol];
response.addResult(`Tracing stopped.
${traceLegend}`);
}
});
var tracing_default = [
tracingStart,
tracingStop
];
const traceLegendSymbol = Symbol("tracesDir");

View File

@@ -0,0 +1,89 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var utils_exports = {};
__export(utils_exports, {
callOnPageNoTrace: () => callOnPageNoTrace,
dateAsFileName: () => dateAsFileName,
waitForCompletion: () => waitForCompletion
});
module.exports = __toCommonJS(utils_exports);
async function waitForCompletion(tab, callback) {
const requests = /* @__PURE__ */ new Set();
let frameNavigated = false;
let waitCallback = () => {
};
const waitBarrier = new Promise((f) => {
waitCallback = f;
});
const responseListener = (request) => {
requests.delete(request);
if (!requests.size)
waitCallback();
};
const requestListener = (request) => {
requests.add(request);
void request.response().then(() => responseListener(request)).catch(() => {
});
};
const frameNavigateListener = (frame) => {
if (frame.parentFrame())
return;
frameNavigated = true;
dispose();
clearTimeout(timeout);
void tab.waitForLoadState("load").then(waitCallback);
};
const onTimeout = () => {
dispose();
waitCallback();
};
tab.page.on("request", requestListener);
tab.page.on("requestfailed", responseListener);
tab.page.on("framenavigated", frameNavigateListener);
const timeout = setTimeout(onTimeout, 1e4);
const dispose = () => {
tab.page.off("request", requestListener);
tab.page.off("requestfailed", responseListener);
tab.page.off("framenavigated", frameNavigateListener);
clearTimeout(timeout);
};
try {
const result = await callback();
if (!requests.size && !frameNavigated)
waitCallback();
await waitBarrier;
await tab.waitForTimeout(1e3);
return result;
} finally {
dispose();
}
}
async function callOnPageNoTrace(page, callback) {
return await page._wrapApiCall(() => callback(page), { internal: true });
}
function dateAsFileName(extension) {
const date = /* @__PURE__ */ new Date();
return `page-${date.toISOString().replace(/[:.]/g, "-")}.${extension}`;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
callOnPageNoTrace,
dateAsFileName,
waitForCompletion
});

View File

@@ -0,0 +1,153 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var verify_exports = {};
__export(verify_exports, {
default: () => verify_default
});
module.exports = __toCommonJS(verify_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
var javascript = __toESM(require("../codegen"));
const verifyElement = (0, import_tool.defineTabTool)({
capability: "testing",
schema: {
name: "browser_verify_element_visible",
title: "Verify element visible",
description: "Verify element is visible on the page",
inputSchema: import_bundle.z.object({
role: import_bundle.z.string().describe('ROLE of the element. Can be found in the snapshot like this: `- {ROLE} "Accessible Name":`'),
accessibleName: import_bundle.z.string().describe('ACCESSIBLE_NAME of the element. Can be found in the snapshot like this: `- role "{ACCESSIBLE_NAME}"`')
}),
type: "assertion"
},
handle: async (tab, params, response) => {
const locator = tab.page.getByRole(params.role, { name: params.accessibleName });
if (await locator.count() === 0) {
response.addError(`Element with role "${params.role}" and accessible name "${params.accessibleName}" not found`);
return;
}
response.addCode(`await expect(page.getByRole(${javascript.escapeWithQuotes(params.role)}, { name: ${javascript.escapeWithQuotes(params.accessibleName)} })).toBeVisible();`);
response.addResult("Done");
}
});
const verifyText = (0, import_tool.defineTabTool)({
capability: "testing",
schema: {
name: "browser_verify_text_visible",
title: "Verify text visible",
description: `Verify text is visible on the page. Prefer ${verifyElement.schema.name} if possible.`,
inputSchema: import_bundle.z.object({
text: import_bundle.z.string().describe('TEXT to verify. Can be found in the snapshot like this: `- role "Accessible Name": {TEXT}` or like this: `- text: {TEXT}`')
}),
type: "assertion"
},
handle: async (tab, params, response) => {
const locator = tab.page.getByText(params.text).filter({ visible: true });
if (await locator.count() === 0) {
response.addError("Text not found");
return;
}
response.addCode(`await expect(page.getByText(${javascript.escapeWithQuotes(params.text)})).toBeVisible();`);
response.addResult("Done");
}
});
const verifyList = (0, import_tool.defineTabTool)({
capability: "testing",
schema: {
name: "browser_verify_list_visible",
title: "Verify list visible",
description: "Verify list is visible on the page",
inputSchema: import_bundle.z.object({
element: import_bundle.z.string().describe("Human-readable list description"),
ref: import_bundle.z.string().describe("Exact target element reference that points to the list"),
items: import_bundle.z.array(import_bundle.z.string()).describe("Items to verify")
}),
type: "assertion"
},
handle: async (tab, params, response) => {
const { locator } = await tab.refLocator({ ref: params.ref, element: params.element });
const itemTexts = [];
for (const item of params.items) {
const itemLocator = locator.getByText(item);
if (await itemLocator.count() === 0) {
response.addError(`Item "${item}" not found`);
return;
}
itemTexts.push(await itemLocator.textContent());
}
const ariaSnapshot = `\`
- list:
${itemTexts.map((t) => ` - listitem: ${javascript.escapeWithQuotes(t, '"')}`).join("\n")}
\``;
response.addCode(`await expect(page.locator('body')).toMatchAriaSnapshot(${ariaSnapshot});`);
response.addResult("Done");
}
});
const verifyValue = (0, import_tool.defineTabTool)({
capability: "testing",
schema: {
name: "browser_verify_value",
title: "Verify value",
description: "Verify element value",
inputSchema: import_bundle.z.object({
type: import_bundle.z.enum(["textbox", "checkbox", "radio", "combobox", "slider"]).describe("Type of the element"),
element: import_bundle.z.string().describe("Human-readable element description"),
ref: import_bundle.z.string().describe("Exact target element reference that points to the element"),
value: import_bundle.z.string().describe('Value to verify. For checkbox, use "true" or "false".')
}),
type: "assertion"
},
handle: async (tab, params, response) => {
const { locator, resolved } = await tab.refLocator({ ref: params.ref, element: params.element });
const locatorSource = `page.${resolved}`;
if (params.type === "textbox" || params.type === "slider" || params.type === "combobox") {
const value = await locator.inputValue();
if (value !== params.value) {
response.addError(`Expected value "${params.value}", but got "${value}"`);
return;
}
response.addCode(`await expect(${locatorSource}).toHaveValue(${javascript.quote(params.value)});`);
} else if (params.type === "checkbox" || params.type === "radio") {
const value = await locator.isChecked();
if (value !== (params.value === "true")) {
response.addError(`Expected value "${params.value}", but got "${value}"`);
return;
}
const matcher = value ? "toBeChecked" : "not.toBeChecked";
response.addCode(`await expect(${locatorSource}).${matcher}();`);
}
response.addResult("Done");
}
});
var verify_default = [
verifyElement,
verifyText,
verifyList,
verifyValue
];

View File

@@ -0,0 +1,63 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var wait_exports = {};
__export(wait_exports, {
default: () => wait_default
});
module.exports = __toCommonJS(wait_exports);
var import_bundle = require("../../sdk/bundle");
var import_tool = require("./tool");
const wait = (0, import_tool.defineTool)({
capability: "core",
schema: {
name: "browser_wait_for",
title: "Wait for",
description: "Wait for text to appear or disappear or a specified time to pass",
inputSchema: import_bundle.z.object({
time: import_bundle.z.number().optional().describe("The time to wait in seconds"),
text: import_bundle.z.string().optional().describe("The text to wait for"),
textGone: import_bundle.z.string().optional().describe("The text to wait for to disappear")
}),
type: "assertion"
},
handle: async (context, params, response) => {
if (!params.text && !params.textGone && !params.time)
throw new Error("Either time, text or textGone must be provided");
if (params.time) {
response.addCode(`await new Promise(f => setTimeout(f, ${params.time} * 1000));`);
await new Promise((f) => setTimeout(f, Math.min(3e4, params.time * 1e3)));
}
const tab = context.currentTabOrDie();
const locator = params.text ? tab.page.getByText(params.text).first() : void 0;
const goneLocator = params.textGone ? tab.page.getByText(params.textGone).first() : void 0;
if (goneLocator) {
response.addCode(`await page.getByText(${JSON.stringify(params.textGone)}).first().waitFor({ state: 'hidden' });`);
await goneLocator.waitFor({ state: "hidden" });
}
if (locator) {
response.addCode(`await page.getByText(${JSON.stringify(params.text)}).first().waitFor({ state: 'visible' });`);
await locator.waitFor({ state: "visible" });
}
response.addResult(`Waited for ${params.text || params.textGone || params.time}`);
response.setIncludeSnapshot();
}
});
var wait_default = [
wait
];

View File

@@ -0,0 +1,44 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var watchdog_exports = {};
__export(watchdog_exports, {
setupExitWatchdog: () => setupExitWatchdog
});
module.exports = __toCommonJS(watchdog_exports);
var import_browserContextFactory = require("./browserContextFactory");
var import_context = require("./context");
function setupExitWatchdog() {
let isExiting = false;
const handleExit = async () => {
if (isExiting)
return;
isExiting = true;
setTimeout(() => process.exit(0), 15e3);
await import_context.Context.disposeAll();
await import_browserContextFactory.SharedContextFactory.dispose();
process.exit(0);
};
process.stdin.on("close", handleExit);
process.on("SIGINT", handleExit);
process.on("SIGTERM", handleExit);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
setupExitWatchdog
});