diff --git a/.claude/skills/playwright-dev/SKILL.md b/.claude/skills/playwright-dev/SKILL.md index a6baca90575fd..a15b2b2bf25a1 100644 --- a/.claude/skills/playwright-dev/SKILL.md +++ b/.claude/skills/playwright-dev/SKILL.md @@ -11,5 +11,5 @@ See [CLAUDE.md](../../../CLAUDE.md) for monorepo structure, build/test/lint comm - [Library Architecture](library.md) — client/server/dispatcher structure, protocol layer, DEPS rules - [Adding and Modifying APIs](api.md) — define API docs, implement client/server, add tests -- [MCP Tools and CLI Commands](mcp-dev.md) — add MCP tools, CLI commands, config options +- [MCP Tools and CLI Commands](tools.md) — add MCP tools, CLI commands, config options - [Vendoring Dependencies](vendor.md) — bundle third-party npm packages into playwright-core or playwright diff --git a/.claude/skills/playwright-dev/mcp-dev.md b/.claude/skills/playwright-dev/tools.md similarity index 73% rename from .claude/skills/playwright-dev/mcp-dev.md rename to .claude/skills/playwright-dev/tools.md index dffe0ba4e9614..62c0e3a34e03c 100644 --- a/.claude/skills/playwright-dev/mcp-dev.md +++ b/.claude/skills/playwright-dev/tools.md @@ -4,12 +4,12 @@ ### Step 1: Create the Tool File -Create `packages/playwright/src/tools/.ts`. +Create `packages/playwright-core/src/tools/backend/.ts`. Import zod from the MCP bundle and use `defineTool` or `defineTabTool`: ```typescript -import { z } from 'playwright-core/lib/mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTool, defineTabTool } from './tool'; ``` @@ -90,7 +90,7 @@ const myContextTool = defineTool({ ### Step 2: Add ToolCapability (if needed) -If your tool doesn't fit an existing capability, add a new one to `packages/playwright/src/mcp/config.d.ts`: +If your tool doesn't fit an existing capability, add a new one to `packages/playwright-core/src/tools/mcp/config.d.ts`: ```typescript export type ToolCapability = @@ -115,10 +115,10 @@ export type ToolCapability = ### Step 3: Register the Tool -In `packages/playwright/src/tools/tools.ts`: +In `packages/playwright-core/src/tools/backend/tools.ts`: ```typescript -import myTool from './tools/myTool'; +import myTool from './myTool'; export const browserTools: Tool[] = [ // ... existing tools ... @@ -198,10 +198,10 @@ Implement the corresponding MCP tool first (see section above). CLI commands cal ### Step 2: Add the Command Declaration -In `packages/playwright/src/cli/daemon/commands.ts`, use `declareCommand()`: +In `packages/playwright-core/src/tools/cli-daemon/commands.ts`, use `declareCommand()`: ```typescript -import { z } from 'playwright-core/lib/mcpBundle'; +import { z } from '../../mcpBundle'; import { declareCommand } from './command'; const myCommand = declareCommand({ @@ -249,7 +249,7 @@ const commandsArray: AnyCommandSchema[] = [ ]; ``` -**Categories** (defined in `packages/playwright/src/cli/daemon/command.ts`): +**Categories** (defined in `packages/playwright-core/src/tools/cli-daemon/command.ts`): ```typescript type Category = 'core' | 'navigation' | 'keyboard' | 'mouse' | 'export' | @@ -258,8 +258,8 @@ type Category = 'core' | 'navigation' | 'keyboard' | 'mouse' | 'export' | ``` To add a new category: -1. Add it to `Category` type in `packages/playwright/src/cli/daemon/command.ts` -2. Add it to the `categories` array in `packages/playwright/src/cli/daemon/helpGenerator.ts`: +1. Add it to `Category` type in `packages/playwright-core/src/tools/cli-daemon/command.ts` +2. Add it to the `categories` array in `packages/playwright-core/src/tools/cli-daemon/helpGenerator.ts`: ```typescript const categories: { name: Category, title: string }[] = [ // ... existing ... @@ -316,7 +316,7 @@ test('my-command', async ({ cli, server }) => { When you need to add a new config option, update these files in order: -### 1. Type definition: `packages/playwright/src/mcp/config.d.ts` +### 1. Type definition: `packages/playwright-core/src/tools/mcp/config.d.ts` Add the option to the `Config` type with JSDoc: @@ -331,7 +331,7 @@ export type Config = { }; ``` -### 2. CLI options type: `packages/playwright/src/mcp/config.ts` +### 2. CLI options type: `packages/playwright-core/src/tools/mcp/config.ts` Add to `CLIOptions` type: @@ -379,7 +379,7 @@ options.myOption = envToString(process.env.PLAYWRIGHT_MCP_MY_OPTION); // For semicolon lists: semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_MY_OPTION) ``` -### 5. MCP server CLI: `packages/playwright/src/mcp/program.ts` +### 5. MCP server CLI: `packages/playwright-core/src/tools/mcp/program.ts` Add CLI flag: @@ -418,58 +418,72 @@ Run `npm run playwright-cli -- --help` to see the latest available commands and ### Directory Structure ``` +packages/playwright-core/src/tools/ +├── backend/ # All MCP tool implementations +│ ├── tool.ts # Tool/TabTool types, defineTool(), defineTabTool() +│ ├── tools.ts # Tool registry (browserTools array, filteredTools) +│ ├── browserBackend.ts # Browser backend +│ ├── context.ts # Browser context management +│ ├── tab.ts # Tab management +│ ├── response.ts # Response class, parseResponse() +│ ├── common.ts # close, resize +│ ├── navigate.ts # navigate, goBack, goForward, reload +│ ├── snapshot.ts # page snapshot +│ ├── form.ts # click, type, fill, select, check +│ ├── keyboard.ts # press, keydown, keyup +│ ├── mouse.ts # mouse move, click, wheel +│ ├── tabs.ts # tab management +│ ├── cookies.ts # cookie CRUD +│ ├── webstorage.ts # localStorage, sessionStorage +│ ├── storage.ts # storage state save/load +│ ├── network.ts # network requests listing +│ ├── route.ts # request mocking/routing +│ ├── console.ts # console messages +│ ├── evaluate.ts # JS evaluation +│ ├── screenshot.ts # screenshots +│ ├── pdf.ts # PDF generation +│ ├── files.ts # file upload +│ ├── dialogs.ts # dialog handling +│ ├── verify.ts # assertions +│ ├── wait.ts # wait operations +│ ├── tracing.ts # trace recording +│ ├── video.ts # video recording +│ ├── runCode.ts # run Playwright code +│ ├── devtools.ts # DevTools integration +│ ├── config.ts # config tool +│ └── utils.ts # shared utilities +├── mcp/ # MCP server +│ ├── config.d.ts # Config type, ToolCapability type +│ ├── config.ts # Config resolution, CLIOptions, FullConfig +│ ├── program.ts # MCP server CLI setup +│ ├── index.ts # MCP server entry +│ ├── browserFactory.ts # Browser factory +│ ├── extensionContextFactory.ts +│ ├── cdpRelay.ts # CDP relay +│ ├── watchdog.ts # Watchdog +│ └── log.ts # Logging +├── cli-client/ # CLI client +│ ├── program.ts # CLI client entry (argument parsing) +│ ├── session.ts # Session management +│ └── registry.ts # Session registry +├── cli-daemon/ # CLI daemon +│ ├── command.ts # Category type, CommandSchema, declareCommand(), parseCommand() +│ ├── commands.ts # All CLI command declarations +│ ├── helpGenerator.ts # Help text generation (generateHelp, generateHelpJSON) +│ ├── daemon.ts # Daemon server +│ └── program.ts # Daemon program entry +├── dashboard/ # Dashboard UI +│ ├── dashboardApp.ts # Dashboard app +│ └── dashboardController.ts +├── utils/ +│ ├── socketConnection.ts # Socket connection utilities +│ └── mcp/ # MCP SDK utilities +│ ├── server.ts # MCP server wrapper +│ ├── tool.ts # ToolSchema type, toMcpTool() +│ └── http.ts # HTTP utilities +└── exports.ts # Public exports + packages/playwright/src/ -├── mcp/ -│ ├── browser/ -│ │ ├── tools/ # All MCP tool implementations -│ │ │ ├── tool.ts # Tool/TabTool types, defineTool(), defineTabTool() -│ │ │ ├── common.ts # close, resize -│ │ │ ├── navigate.ts # navigate, goBack, goForward, reload -│ │ │ ├── snapshot.ts # page snapshot -│ │ │ ├── form.ts # click, type, fill, select, check -│ │ │ ├── keyboard.ts # press, keydown, keyup -│ │ │ ├── mouse.ts # mouse move, click, wheel -│ │ │ ├── tabs.ts # tab management -│ │ │ ├── cookies.ts # cookie CRUD -│ │ │ ├── webstorage.ts # localStorage, sessionStorage -│ │ │ ├── storage.ts # storage state save/load -│ │ │ ├── network.ts # network requests listing -│ │ │ ├── route.ts # request mocking/routing -│ │ │ ├── console.ts # console messages -│ │ │ ├── evaluate.ts # JS evaluation -│ │ │ ├── screenshot.ts # screenshots -│ │ │ ├── pdf.ts # PDF generation -│ │ │ ├── files.ts # file upload -│ │ │ ├── dialogs.ts # dialog handling -│ │ │ ├── verify.ts # assertions -│ │ │ ├── wait.ts # wait operations -│ │ │ ├── tracing.ts # trace recording -│ │ │ ├── video.ts # video recording -│ │ │ ├── runCode.ts # run Playwright code -│ │ │ ├── devtools.ts # DevTools integration -│ │ │ ├── config.ts # config tool -│ │ │ ├── install.ts # browser install -│ │ │ └── utils.ts # shared utilities -│ │ ├── tools.ts # Tool registry (browserTools array, filteredTools) -│ │ ├── config.ts # Config resolution, CLIOptions, FullConfig -│ │ ├── context.ts # Browser context management -│ │ ├── response.ts # Response class, parseResponse() -│ │ └── tab.ts # Tab management -│ ├── sdk/ -│ │ ├── server.ts # MCP server -│ │ └── tool.ts # ToolSchema type, toMcpTool() -│ ├── config.d.ts # Config type, ToolCapability type -│ └── program.ts # MCP server CLI setup -├── cli/ -│ ├── client/ -│ │ ├── program.ts # CLI client entry (argument parsing) -│ │ ├── session.ts # Session management -│ │ └── registry.ts # Session registry -│ └── daemon/ -│ ├── command.ts # Category type, CommandSchema, declareCommand(), parseCommand() -│ ├── commands.ts # All CLI command declarations -│ ├── helpGenerator.ts # Help text generation (generateHelp, generateHelpJSON) -│ └── daemon.ts # Daemon server └── skill/ ├── SKILL.md # Skill documentation └── references/ # Reference docs diff --git a/.github/workflows/tests_components.yml b/.github/workflows/tests_components.yml index 6daa301a2a6aa..8fed8ae75b27c 100644 --- a/.github/workflows/tests_components.yml +++ b/.github/workflows/tests_components.yml @@ -9,10 +9,7 @@ on: paths-ignore: - 'browser_patches/**' - 'docs/**' - - 'packages/playwright-core/src/cli/client/**' - - 'packages/playwright-core/src/cli/daemon/**' - - 'packages/playwright-core/src/mcp/**' - - 'packages/playwright/src/mcp/**' + - 'packages/playwright-core/src/tools/**' - 'tests/mcp/**' branches: - main diff --git a/.github/workflows/tests_primary.yml b/.github/workflows/tests_primary.yml index 2f95772f99d3a..bc5413bf21927 100644 --- a/.github/workflows/tests_primary.yml +++ b/.github/workflows/tests_primary.yml @@ -9,10 +9,7 @@ on: paths-ignore: - 'browser_patches/**' - 'docs/**' - - 'packages/playwright-core/src/cli/client/**' - - 'packages/playwright-core/src/cli/daemon/**' - - 'packages/playwright-core/src/mcp/**' - - 'packages/playwright/src/mcp/**' + - 'packages/playwright-core/src/tools/**' - 'tests/mcp/**' branches: - main diff --git a/CLAUDE.md b/CLAUDE.md index 858166243e0f6..ed556a57ea537 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -131,5 +131,5 @@ Detailed guides for common development tasks: - **[Architecture: Client, Server, and Dispatchers](.claude/skills/playwright-dev/library.md)** — package layout, protocol layer, ChannelOwner/SdkObject/Dispatcher base classes, DEPS rules, end-to-end RPC flow, object lifecycle - **[Adding and Modifying APIs](.claude/skills/playwright-dev/api.md)** — 6-step process: define docs → implement client → define protocol → implement dispatcher → implement server → write tests -- **[MCP Tools and CLI Commands](.claude/skills/playwright-dev/mcp-dev.md)** — `defineTool()`/`defineTabTool()`, tool capabilities, CLI `declareCommand()`, config options, testing with MCP fixtures +- **[MCP Tools and CLI Commands](.claude/skills/playwright-dev/tools.md)** — `defineTool()`/`defineTabTool()`, tool capabilities, CLI `declareCommand()`, config options, testing with MCP fixtures - **[Vendoring Dependencies](.claude/skills/playwright-dev/vendor.md)** — bundle architecture, esbuild setup, typed wrappers, adding deps to existing bundles diff --git a/package-lock.json b/package-lock.json index a5c240aca7630..94f09f6995ab9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1339,8 +1339,8 @@ "resolved": "packages/playwright-client", "link": true }, - "node_modules/@playwright/devtools": { - "resolved": "packages/devtools", + "node_modules/@playwright/dashboard": { + "resolved": "packages/dashboard", "link": true }, "node_modules/@playwright/experimental-ct-core": { @@ -8185,9 +8185,14 @@ "zod": "^3.25 || ^4" } }, + "packages/dashboard": { + "name": "@playwright/dashboard", + "version": "0.0.0" + }, "packages/devtools": { "name": "@playwright/devtools", - "version": "0.0.0" + "version": "0.0.0", + "extraneous": true }, "packages/html-reporter": { "version": "0.0.0" diff --git a/package.json b/package.json index 73bd6076d5568..43053cd703fe0 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "build-android-driver": "./utils/build_android_driver.sh", "innerloop": "playwright run-server --reuse-browser", "test-playwright-cli": "playwright test --config=tests/mcp/playwright.config.ts --project=chrome cli-", - "playwright-cli": "node packages/playwright-core/lib/cli/client/cli.js", + "playwright-cli": "node packages/playwright-core/lib/tools/cli-client/cli.js", "playwright-cli-readme": "node utils/generate_cli_help.js --readme" }, "workspaces": [ diff --git a/packages/devtools/.gitignore b/packages/dashboard/.gitignore similarity index 100% rename from packages/devtools/.gitignore rename to packages/dashboard/.gitignore diff --git a/packages/devtools/index.html b/packages/dashboard/index.html similarity index 100% rename from packages/devtools/index.html rename to packages/dashboard/index.html diff --git a/packages/devtools/package.json b/packages/dashboard/package.json similarity index 64% rename from packages/devtools/package.json rename to packages/dashboard/package.json index 3acc8eba44626..0654986212789 100644 --- a/packages/devtools/package.json +++ b/packages/dashboard/package.json @@ -1,5 +1,5 @@ { - "name": "@playwright/devtools", + "name": "@playwright/dashboard", "private": true, "version": "0.0.0", "type": "module" diff --git a/packages/devtools/src/colors.css b/packages/dashboard/src/colors.css similarity index 100% rename from packages/devtools/src/colors.css rename to packages/dashboard/src/colors.css diff --git a/packages/devtools/src/common.css b/packages/dashboard/src/common.css similarity index 100% rename from packages/devtools/src/common.css rename to packages/dashboard/src/common.css diff --git a/packages/devtools/src/devtools.css b/packages/dashboard/src/dashboard.css similarity index 88% rename from packages/devtools/src/devtools.css rename to packages/dashboard/src/dashboard.css index eddd9f4cee97c..de8bdcf9a5f67 100644 --- a/packages/devtools/src/devtools.css +++ b/packages/dashboard/src/dashboard.css @@ -14,7 +14,7 @@ * limitations under the License. */ -.devtools-view { +.dashboard-view { --interactive-orange: 238 134 12; display: flex; flex-direction: column; @@ -38,12 +38,12 @@ background: var(--color-canvas-overlay); } -.devtools-view.interactive .tab.active { +.dashboard-view.interactive .tab.active { background: rgb(var(--interactive-orange)); color: var(--color-fg-on-emphasis); } -.devtools-view.interactive .toolbar::after { +.dashboard-view.interactive .toolbar::after { content: ''; position: absolute; top: 0; @@ -60,7 +60,7 @@ z-index: -1; } -.devtools-view.interactive .segmented-control::after { +.dashboard-view.interactive .segmented-control::after { content: ''; position: absolute; left: 25%; @@ -75,7 +75,7 @@ animation: interactive-track-radial 420ms cubic-bezier(0.12, 0.72, 0.22, 1); } -.devtools-view.interactive .segmented-control.interactive::after { +.dashboard-view.interactive .segmented-control.interactive::after { left: 75%; } @@ -319,43 +319,43 @@ cursor: default; } -.devtools-view.interactive .segmented-control { +.dashboard-view.interactive .segmented-control { background: rgb(255 255 255 / 0.2); } -.devtools-view.interactive .segmented-control::before { +.dashboard-view.interactive .segmented-control::before { background: rgb(255 255 255 / 0.32); } -:root.light-mode .devtools-view.interactive .segmented-control { +:root.light-mode .dashboard-view.interactive .segmented-control { background: rgb(0 0 0 / 0.1); } -:root.light-mode .devtools-view.interactive .segmented-control::before { +:root.light-mode .dashboard-view.interactive .segmented-control::before { background: rgb(0 0 0 / 0.12); } -:root.light-mode .devtools-view.interactive .toolbar .nav-btn:hover { +:root.light-mode .dashboard-view.interactive .toolbar .nav-btn:hover { background: rgba(255, 255, 255, 0.18); } -:root.light-mode .devtools-view.interactive .toolbar .nav-btn:active { +:root.light-mode .dashboard-view.interactive .toolbar .nav-btn:active { background: rgba(255, 255, 255, 0.25); } -.devtools-view.interactive .segmented-control.interactive::before { +.dashboard-view.interactive .segmented-control.interactive::before { background: rgb(var(--interactive-orange) / 0.95); } -.devtools-view.interactive .segmented-control-option { +.dashboard-view.interactive .segmented-control-option { color: rgb(255 255 255 / 0.74); } -.devtools-view.interactive .segmented-control-option.active { +.dashboard-view.interactive .segmented-control-option.active { color: var(--color-fg-on-emphasis); } -:root.light-mode .devtools-view.interactive .segmented-control-option { +:root.light-mode .dashboard-view.interactive .segmented-control-option { color: var(--color-fg-muted); } -:root.light-mode .devtools-view.interactive .segmented-control-option.active { +:root.light-mode .dashboard-view.interactive .segmented-control-option.active { color: var(--color-fg-on-emphasis); } @@ -376,24 +376,24 @@ background: var(--color-canvas-subtle); } -.devtools-view.interactive .toolbar { +.dashboard-view.interactive .toolbar { background: rgb(var(--interactive-orange)); color: var(--color-fg-on-emphasis); } -.devtools-view.interactive .toolbar .nav-btn, -.devtools-view.interactive .toolbar .omnibox { +.dashboard-view.interactive .toolbar .nav-btn, +.dashboard-view.interactive .toolbar .omnibox { color: var(--color-fg-on-emphasis); } -:root.light-mode .devtools-view.interactive .toolbar { +:root.light-mode .dashboard-view.interactive .toolbar { color: var(--color-fg-on-emphasis); } -:root.light-mode .devtools-view.interactive .toolbar .omnibox { +:root.light-mode .dashboard-view.interactive .toolbar .omnibox { background: rgb(0 0 0 / 0.15); color: var(--color-fg-on-emphasis); } -:root.light-mode .devtools-view.interactive .toolbar .omnibox::placeholder { +:root.light-mode .dashboard-view.interactive .toolbar .omnibox::placeholder { color: rgb(255 255 255 / 0.6); } @@ -495,7 +495,7 @@ object-fit: contain; } -.devtools-view.interactive .screen::after { +.dashboard-view.interactive .screen::after { opacity: 1; } diff --git a/packages/devtools/src/devtools.tsx b/packages/dashboard/src/dashboard.tsx similarity index 95% rename from packages/devtools/src/devtools.tsx rename to packages/dashboard/src/dashboard.tsx index 96e5c2f4eed80..db3b02ac005e5 100644 --- a/packages/devtools/src/devtools.tsx +++ b/packages/dashboard/src/dashboard.tsx @@ -15,16 +15,16 @@ */ import React from 'react'; -import './devtools.css'; +import './dashboard.css'; import { navigate } from './index'; -import { DevToolsClient } from './devtoolsClient'; +import { DashboardClient } from './dashboardClient'; import { asLocator } from '@isomorphic/locatorGenerators'; import { SplitView } from '@web/components/splitView'; import { ChevronLeftIcon, ChevronRightIcon, CloseIcon, PlusIcon, ReloadIcon, PickLocatorIcon, InspectorPanelIcon } from './icons'; import { SettingsButton } from './settingsView'; -import type { DevToolsClientChannel } from './devtoolsClient'; -import type { Tab, DevToolsChannelEvents } from './devtoolsChannel'; +import type { DashboardClientChannel } from './dashboardClient'; +import type { Tab, DashboardChannelEvents } from './dashboardChannel'; function tabFavicon(url: string): string { try { @@ -38,16 +38,16 @@ function tabFavicon(url: string): string { const BUTTONS = ['left', 'middle', 'right'] as const; -export const DevTools: React.FC<{ wsUrl?: string }> = ({ wsUrl }) => { +export const Dashboard: React.FC<{ wsUrl?: string }> = ({ wsUrl }) => { const [interactive, setInteractive] = React.useState(false); const [tabs, setTabs] = React.useState(null); const [url, setUrl] = React.useState(''); - const [frame, setFrame] = React.useState(); + const [frame, setFrame] = React.useState(); const [showInspector, setShowInspector] = React.useState(false); const [picking, setPicking] = React.useState(false); const [locatorToast, setLocatorToast] = React.useState<{ text: string; timer: ReturnType }>(); - const [channel, setChannel] = React.useState(); + const [channel, setChannel] = React.useState(); const displayRef = React.useRef(null); const screenRef = React.useRef(null); const tabbarRef = React.useRef(null); @@ -57,7 +57,7 @@ export const DevTools: React.FC<{ wsUrl?: string }> = ({ wsUrl }) => { React.useEffect(() => { if (!wsUrl) return; - const channel = DevToolsClient.create(wsUrl); + const channel = DashboardClient.create(wsUrl); channel.onopen = () => { setChannel(channel); @@ -225,7 +225,7 @@ export const DevTools: React.FC<{ wsUrl?: string }> = ({ wsUrl }) => { else if (tabs.length === 0) overlayText = 'No tabs open'; - return (
{/* Tab bar */}
diff --git a/packages/devtools/src/devtoolsChannel.ts b/packages/dashboard/src/dashboardChannel.ts similarity index 84% rename from packages/devtools/src/devtoolsChannel.ts rename to packages/dashboard/src/dashboardChannel.ts index 2ee00d36658e0..094d61664ab70 100644 --- a/packages/devtools/src/devtoolsChannel.ts +++ b/packages/dashboard/src/dashboardChannel.ts @@ -16,13 +16,13 @@ export type Tab = { pageId: string; title: string; url: string; selected: boolean; inspectorUrl?: string }; -export type DevToolsChannelEvents = { +export type DashboardChannelEvents = { frame: { data: string; viewportWidth: number; viewportHeight: number }; tabs: { tabs: Tab[] }; elementPicked: { selector: string }; }; -export interface DevToolsChannel { +export interface DashboardChannel { version: 1; tabs(): Promise<{ tabs: Tab[] }>; selectTab(params: { pageId: string }): Promise; @@ -41,6 +41,6 @@ export interface DevToolsChannel { pickLocator(): Promise; cancelPickLocator(): Promise; - on(event: K, listener: (params: DevToolsChannelEvents[K]) => void): void; - off(event: K, listener: (params: DevToolsChannelEvents[K]) => void): void; + on(event: K, listener: (params: DashboardChannelEvents[K]) => void): void; + off(event: K, listener: (params: DashboardChannelEvents[K]) => void): void; } diff --git a/packages/devtools/src/devtoolsClient.ts b/packages/dashboard/src/dashboardClient.ts similarity index 85% rename from packages/devtools/src/devtoolsClient.ts rename to packages/dashboard/src/dashboardClient.ts index 34c66e4d4b515..3a69446e84475 100644 --- a/packages/devtools/src/devtoolsClient.ts +++ b/packages/dashboard/src/dashboardClient.ts @@ -16,15 +16,15 @@ import { Transport } from './transport'; -import type { DevToolsChannel } from './devtoolsChannel'; +import type { DashboardChannel } from './dashboardChannel'; -export type DevToolsClientChannel = DevToolsChannel & { +export type DashboardClientChannel = DashboardChannel & { onopen?: () => void; onclose?: (reason?: string) => void; close(): void; }; -export class DevToolsClient { +export class DashboardClient { private _transport: Transport; private _listeners = new Map>(); @@ -44,11 +44,11 @@ export class DevToolsClient { }; } - static create(url: string): DevToolsClientChannel { + static create(url: string): DashboardClientChannel { const transport = new Transport(url); - const client = new DevToolsClient(transport); + const client = new DashboardClient(transport); return new Proxy(client, { - get(target: DevToolsClient, prop: string | symbol, receiver: any): any { + get(target: DashboardClient, prop: string | symbol, receiver: any): any { if (typeof prop === 'symbol' || prop in target) return Reflect.get(target, prop, receiver); // Prevent the proxy from being treated as a thenable. @@ -56,7 +56,7 @@ export class DevToolsClient { return undefined; return (params?: any) => target._transport.send(prop, params); } - }) as unknown as DevToolsClientChannel; + }) as unknown as DashboardClientChannel; } private _fireEvent(event: string, params: any) { diff --git a/packages/devtools/src/grid.css b/packages/dashboard/src/grid.css similarity index 100% rename from packages/devtools/src/grid.css rename to packages/dashboard/src/grid.css diff --git a/packages/devtools/src/grid.tsx b/packages/dashboard/src/grid.tsx similarity index 98% rename from packages/devtools/src/grid.tsx rename to packages/dashboard/src/grid.tsx index fc5d4a7346e0b..f3ea91090b49b 100644 --- a/packages/devtools/src/grid.tsx +++ b/packages/dashboard/src/grid.tsx @@ -16,13 +16,13 @@ import React from 'react'; import './grid.css'; -import { DevToolsClient } from './devtoolsClient'; +import { DashboardClient } from './dashboardClient'; import { navigate } from './index'; import { Screencast } from './screencast'; import { SettingsButton } from './settingsView'; import type { BrowserDescriptor } from '../../playwright-core/src/serverRegistry'; -import type { Tab } from './devtoolsChannel'; +import type { Tab } from './dashboardChannel'; import type { SessionModel, SessionStatus } from './sessionModel'; export const Grid: React.FC<{ model: SessionModel }> = ({ model }) => { @@ -108,7 +108,7 @@ const SessionChip: React.FC<{ descriptor: BrowserDescriptor; wsUrl: string | und const channel = React.useMemo(() => { if (!wsUrl || !visible) return undefined; - return DevToolsClient.create(wsUrl); + return DashboardClient.create(wsUrl); }, [wsUrl, visible]); const [selectedTab, setSelectedTab] = React.useState(); diff --git a/packages/devtools/src/icons.tsx b/packages/dashboard/src/icons.tsx similarity index 100% rename from packages/devtools/src/icons.tsx rename to packages/dashboard/src/icons.tsx diff --git a/packages/devtools/src/index.tsx b/packages/dashboard/src/index.tsx similarity index 95% rename from packages/devtools/src/index.tsx rename to packages/dashboard/src/index.tsx index 8bebaaba90864..ed3077110193b 100644 --- a/packages/devtools/src/index.tsx +++ b/packages/dashboard/src/index.tsx @@ -19,7 +19,7 @@ import * as ReactDOM from 'react-dom/client'; import './colors.css'; import './common.css'; import { applyTheme } from '@web/theme'; -import { DevTools } from './devtools'; +import { Dashboard } from './dashboard'; import { Grid } from './grid'; import { SessionModel } from './sessionModel'; @@ -61,7 +61,7 @@ const App: React.FC = () => { if (socketPath) { const wsUrl = model.sessionByGuid(socketPath)?.wsUrl; - return ; + return ; } return ; }; diff --git a/packages/devtools/src/screencast.tsx b/packages/dashboard/src/screencast.tsx similarity index 87% rename from packages/devtools/src/screencast.tsx rename to packages/dashboard/src/screencast.tsx index f48dd16c7b778..232dc391ea3df 100644 --- a/packages/devtools/src/screencast.tsx +++ b/packages/dashboard/src/screencast.tsx @@ -16,9 +16,9 @@ import React from 'react'; -import type { DevToolsClientChannel } from './devtoolsClient'; +import type { DashboardClientChannel } from './dashboardClient'; -export const Screencast: React.FC<{ channel: DevToolsClientChannel }> = ({ channel }) => { +export const Screencast: React.FC<{ channel: DashboardClientChannel }> = ({ channel }) => { const [frameSrc, setFrameSrc] = React.useState(''); React.useEffect(() => { diff --git a/packages/devtools/src/sessionModel.ts b/packages/dashboard/src/sessionModel.ts similarity index 97% rename from packages/devtools/src/sessionModel.ts rename to packages/dashboard/src/sessionModel.ts index 74526f9e58cc8..644f49440b8e7 100644 --- a/packages/devtools/src/sessionModel.ts +++ b/packages/dashboard/src/sessionModel.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { ClientInfo } from '../../playwright-core/src/cli/client/registry'; +import type { ClientInfo } from '../../playwright-core/src/tools/cli-client/registry'; import type { BrowserDescriptor } from '../../playwright-core/src/serverRegistry'; export type SessionStatus = BrowserDescriptor & { diff --git a/packages/devtools/src/settingsView.css b/packages/dashboard/src/settingsView.css similarity index 100% rename from packages/devtools/src/settingsView.css rename to packages/dashboard/src/settingsView.css diff --git a/packages/devtools/src/settingsView.tsx b/packages/dashboard/src/settingsView.tsx similarity index 100% rename from packages/devtools/src/settingsView.tsx rename to packages/dashboard/src/settingsView.tsx diff --git a/packages/devtools/src/transport.ts b/packages/dashboard/src/transport.ts similarity index 100% rename from packages/devtools/src/transport.ts rename to packages/dashboard/src/transport.ts diff --git a/packages/devtools/src/vite-env.d.ts b/packages/dashboard/src/vite-env.d.ts similarity index 100% rename from packages/devtools/src/vite-env.d.ts rename to packages/dashboard/src/vite-env.d.ts diff --git a/packages/devtools/vite.config.ts b/packages/dashboard/vite.config.ts similarity index 98% rename from packages/devtools/vite.config.ts rename to packages/dashboard/vite.config.ts index a10d62d865897..3bf1c2bea6166 100644 --- a/packages/devtools/vite.config.ts +++ b/packages/dashboard/vite.config.ts @@ -32,7 +32,7 @@ export default defineConfig({ }, }, build: { - outDir: path.resolve(__dirname, '../playwright-core/lib/vite/devtools'), + outDir: path.resolve(__dirname, '../playwright-core/lib/vite/dashboard'), emptyOutDir: true, rollupOptions: { output: { diff --git a/packages/playwright-cli-stub/playwright-cli-stub.js b/packages/playwright-cli-stub/playwright-cli-stub.js index 6feb86e802b39..5afbcfa30cf4b 100755 --- a/packages/playwright-cli-stub/playwright-cli-stub.js +++ b/packages/playwright-cli-stub/playwright-cli-stub.js @@ -15,7 +15,7 @@ * limitations under the License. */ -const { program } = require('playwright-core/lib/cli/client/program'); +const { program } = require('playwright-core/lib/tools/cli-client/program'); program().catch(e => { console.error(e.message); diff --git a/packages/playwright-core/package.json b/packages/playwright-core/package.json index 560121b58200e..6107faab366af 100644 --- a/packages/playwright-core/package.json +++ b/packages/playwright-core/package.json @@ -24,12 +24,10 @@ "./package.json": "./package.json", "./lib/outofprocess": "./lib/outofprocess.js", "./lib/cli/program": "./lib/cli/program.js", - "./lib/cli/client/program": "./lib/cli/client/program.js", - "./lib/client/connect": "./lib/client/connect.js", + "./lib/tools/cli-client/program": "./lib/tools/cli-client/program.js", + "./lib/tools/mcp/program": "./lib/tools/mcp/program.js", "./lib/mcpBundle": "./lib/mcpBundle.js", - "./lib/mcp/exports": "./lib/mcp/exports.js", "./lib/tools/exports": "./lib/tools/exports.js", - "./lib/mcp/index": "./lib/mcp/index.js", "./lib/remote/playwrightServer": "./lib/remote/playwrightServer.js", "./lib/server": "./lib/server/index.js", "./lib/server/utils/image_tools/stats": "./lib/server/utils/image_tools/stats.js", diff --git a/packages/playwright-core/src/cli/DEPS.list b/packages/playwright-core/src/cli/DEPS.list index f4709da17348b..fb73f81dd5381 100644 --- a/packages/playwright-core/src/cli/DEPS.list +++ b/packages/playwright-core/src/cli/DEPS.list @@ -1,20 +1,9 @@ [*] -../../ -../generated/ +../.. ../server/ -../server/trace -../server/utils -../utils +../server/utils/ +../remote/ +../utils/ ../utilsBundle.ts -./client/ -./daemon/ - -[program.ts] -../server/trace/viewer/traceViewer.ts -../server/ -../mcp/ -./daemon/ - -[driver.ts] -../** -../../ +../client/ +../server/trace/viewer/ diff --git a/packages/playwright-core/src/cli/program.ts b/packages/playwright-core/src/cli/program.ts index 62c165e40121c..cbd18e242e371 100644 --- a/packages/playwright-core/src/cli/program.ts +++ b/packages/playwright-core/src/cli/program.ts @@ -28,8 +28,6 @@ import { runTraceInBrowser, runTraceViewerApp } from '../server/trace/viewer/tra import { assert, getPackageManagerExecCommand } from '../utils'; import { wrapInASCIIBox } from '../server/utils/ascii'; import { dotenv, program } from '../utilsBundle'; -import { decorateMCPCommand } from '../mcp/program'; -import { decorateCLICommand } from './daemon/program'; import type { Browser } from '../client/browser'; import type { BrowserContext } from '../client/browserContext'; @@ -732,14 +730,6 @@ function commandWithOpenOptions(command: string, description: string, options: a .option('--viewport-size ', 'specify browser viewport size in pixels, for example "1280, 720"'); } -const mcpCommand = program.command('run-mcp-server', { hidden: true }); -mcpCommand.description('Interact with the browser over MCP'); -decorateMCPCommand(mcpCommand, packageJSON.version); - -const cliCommand = program.command('run-cli-server', { hidden: true }); -cliCommand.description('Interact with the browser over CLI'); -decorateCLICommand(cliCommand, packageJSON.version); - function buildBasePlaywrightCLICommand(cliTargetLang: string | undefined): string { switch (cliTargetLang) { case 'python': diff --git a/packages/playwright-core/src/devtools/DEPS.list b/packages/playwright-core/src/devtools/DEPS.list deleted file mode 100644 index 3423c19bba5c9..0000000000000 --- a/packages/playwright-core/src/devtools/DEPS.list +++ /dev/null @@ -1,8 +0,0 @@ -[*] -../../ -../server/registry/index.ts -../server/utils/ -../serverRegistry.ts -../utils/ -../client/connect.ts -../client/eventEmitter.ts diff --git a/packages/playwright-core/src/mcp/DEPS.list b/packages/playwright-core/src/mcp/DEPS.list deleted file mode 100644 index 03d40d26e9173..0000000000000 --- a/packages/playwright-core/src/mcp/DEPS.list +++ /dev/null @@ -1,14 +0,0 @@ -[*] -../../ -./sdk/ -../tools/ -../utils/ -../utils/isomorphic/ -../utilsBundle.ts -../mcpBundle.ts -../server/ -../server/registry/ -../server/utils/ -../client/ -../cli/ -../serverRegistry.ts diff --git a/packages/playwright-core/src/mcp/sdk/DEPS.list b/packages/playwright-core/src/mcp/sdk/DEPS.list deleted file mode 100644 index 184ea09429034..0000000000000 --- a/packages/playwright-core/src/mcp/sdk/DEPS.list +++ /dev/null @@ -1,4 +0,0 @@ -[*] -../../mcpBundle.ts -../../utilsBundle.ts -../../server/utils/ diff --git a/packages/playwright-core/src/mcp/sdk/README.md b/packages/playwright-core/src/mcp/sdk/README.md deleted file mode 100644 index b8b280e5a0d05..0000000000000 --- a/packages/playwright-core/src/mcp/sdk/README.md +++ /dev/null @@ -1 +0,0 @@ -- Generic MCP utils, no dependencies on anything. diff --git a/packages/playwright-core/src/server/codegen/csharp.ts b/packages/playwright-core/src/server/codegen/csharp.ts index 32fb598cff64c..070b117aacc73 100644 --- a/packages/playwright-core/src/server/codegen/csharp.ts +++ b/packages/playwright-core/src/server/codegen/csharp.ts @@ -19,7 +19,7 @@ import { asLocator, escapeWithQuotes } from '../../utils'; import { deviceDescriptors } from '../deviceDescriptors'; import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types'; -import type { BrowserContextOptions } from '../../../types/types'; +import type { BrowserContextOptions } from '../../..'; import type * as actions from '@recorder/actions'; type CSharpLanguageMode = 'library' | 'mstest' | 'nunit'; diff --git a/packages/playwright-core/src/server/codegen/java.ts b/packages/playwright-core/src/server/codegen/java.ts index 787bb86e50bad..2c6856b67dfe4 100644 --- a/packages/playwright-core/src/server/codegen/java.ts +++ b/packages/playwright-core/src/server/codegen/java.ts @@ -19,7 +19,7 @@ import { deviceDescriptors } from '../deviceDescriptors'; import { JavaScriptFormatter } from './javascript'; import { asLocator, escapeWithQuotes } from '../../utils'; -import type { BrowserContextOptions } from '../../../types/types'; +import type { BrowserContextOptions } from '../../..'; import type * as types from '../types'; import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types'; import type * as actions from '@recorder/actions'; diff --git a/packages/playwright-core/src/server/codegen/javascript.ts b/packages/playwright-core/src/server/codegen/javascript.ts index c6683e1543b29..6487f2efbde75 100644 --- a/packages/playwright-core/src/server/codegen/javascript.ts +++ b/packages/playwright-core/src/server/codegen/javascript.ts @@ -19,7 +19,7 @@ import { asLocator, escapeWithQuotes, formatObject, formatObjectOrVoid } from '. import { deviceDescriptors } from '../deviceDescriptors'; import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types'; -import type { BrowserContextOptions } from '../../../types/types'; +import type { BrowserContextOptions } from '../../..'; import type * as actions from '@recorder/actions'; export class JavaScriptLanguageGenerator implements LanguageGenerator { diff --git a/packages/playwright-core/src/server/codegen/python.ts b/packages/playwright-core/src/server/codegen/python.ts index 53204013848e2..67d87c89e5389 100644 --- a/packages/playwright-core/src/server/codegen/python.ts +++ b/packages/playwright-core/src/server/codegen/python.ts @@ -19,7 +19,7 @@ import { asLocator, escapeWithQuotes, toSnakeCase } from '../../utils'; import { deviceDescriptors } from '../deviceDescriptors'; import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types'; -import type { BrowserContextOptions } from '../../../types/types'; +import type { BrowserContextOptions } from '../../..'; import type * as actions from '@recorder/actions'; export class PythonLanguageGenerator implements LanguageGenerator { diff --git a/packages/playwright-core/src/server/codegen/types.ts b/packages/playwright-core/src/server/codegen/types.ts index c4d3c827bf379..896e6a3a49dff 100644 --- a/packages/playwright-core/src/server/codegen/types.ts +++ b/packages/playwright-core/src/server/codegen/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { BrowserContextOptions, LaunchOptions } from '../../../types/types'; +import type { BrowserContextOptions, LaunchOptions } from '../../..'; import type { Language } from '../../utils'; import type * as actions from '@recorder/actions'; export type { Language } from '../../utils'; diff --git a/packages/playwright-core/src/tools/DEPS.list b/packages/playwright-core/src/tools/DEPS.list deleted file mode 100644 index 300b3fcbcd84e..0000000000000 --- a/packages/playwright-core/src/tools/DEPS.list +++ /dev/null @@ -1,7 +0,0 @@ -[*] -../.. -../mcpBundle.ts -../utils/ -../utils/isomorphic/ -../utilsBundle.ts -../client/ diff --git a/packages/playwright-core/src/tools/backend/DEPS.list b/packages/playwright-core/src/tools/backend/DEPS.list new file mode 100644 index 0000000000000..6a31e10942716 --- /dev/null +++ b/packages/playwright-core/src/tools/backend/DEPS.list @@ -0,0 +1,8 @@ +[*] +../../.. +../.. +../../mcpBundle.ts +../../utils/ +../../utils/isomorphic/ +../../utilsBundle.ts +../../client/ diff --git a/packages/playwright-core/src/tools/browserServerBackend.ts b/packages/playwright-core/src/tools/backend/browserBackend.ts similarity index 91% rename from packages/playwright-core/src/tools/browserServerBackend.ts rename to packages/playwright-core/src/tools/backend/browserBackend.ts index 529493674e777..6ab579763ad66 100644 --- a/packages/playwright-core/src/tools/browserServerBackend.ts +++ b/packages/playwright-core/src/tools/backend/browserBackend.ts @@ -17,15 +17,15 @@ import { Context } from './context'; import { Response } from './response'; import { SessionLog } from './sessionLog'; -import { debug } from '../utilsBundle'; +import { debug } from '../../utilsBundle'; import type { ContextConfig } from './context'; -import type * as playwright from '../../types/types'; +import type * as playwright from '../../..'; import type { Tool } from './tool'; -import type * as mcpServer from '../mcp/sdk/server'; -import type { ClientInfo, ServerBackend } from '../mcp/sdk/server'; +import type * as mcpServer from '../utils/mcp/server'; +import type { ClientInfo, ServerBackend } from '../utils/mcp/server'; -export class BrowserServerBackend implements ServerBackend { +export class BrowserBackend implements ServerBackend { private _tools: Tool[]; private _context: Context | undefined; private _sessionLog: SessionLog | undefined; diff --git a/packages/playwright-core/src/tools/common.ts b/packages/playwright-core/src/tools/backend/common.ts similarity index 97% rename from packages/playwright-core/src/tools/common.ts rename to packages/playwright-core/src/tools/backend/common.ts index 58cb58e03f68a..553c072becc4a 100644 --- a/packages/playwright-core/src/tools/common.ts +++ b/packages/playwright-core/src/tools/backend/common.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTabTool, defineTool } from './tool'; import { renderTabsMarkdown } from './response'; diff --git a/packages/playwright-core/src/tools/config.ts b/packages/playwright-core/src/tools/backend/config.ts similarity index 96% rename from packages/playwright-core/src/tools/config.ts rename to packages/playwright-core/src/tools/backend/config.ts index 78ddea14a1fbf..5dd862f55c62e 100644 --- a/packages/playwright-core/src/tools/config.ts +++ b/packages/playwright-core/src/tools/backend/config.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTool } from './tool'; const configShow = defineTool({ diff --git a/packages/playwright-core/src/tools/console.ts b/packages/playwright-core/src/tools/backend/console.ts similarity index 98% rename from packages/playwright-core/src/tools/console.ts rename to packages/playwright-core/src/tools/backend/console.ts index be2471ae77442..c2a072b13090c 100644 --- a/packages/playwright-core/src/tools/console.ts +++ b/packages/playwright-core/src/tools/backend/console.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTabTool } from './tool'; const console = defineTabTool({ diff --git a/packages/playwright-core/src/tools/context.ts b/packages/playwright-core/src/tools/backend/context.ts similarity index 96% rename from packages/playwright-core/src/tools/context.ts rename to packages/playwright-core/src/tools/backend/context.ts index 730d252b658ec..4f6144215bf91 100644 --- a/packages/playwright-core/src/tools/context.ts +++ b/packages/playwright-core/src/tools/backend/context.ts @@ -17,19 +17,19 @@ import fs from 'fs'; import path from 'path'; -import { disposeAll } from '../client/disposable'; -import { eventsHelper } from '../client/eventEmitter'; -import { debug } from '../utilsBundle'; -import { escapeWithQuotes } from '../utils/isomorphic/stringUtils'; -import { selectors } from '../..'; +import { disposeAll } from '../../client/disposable'; +import { eventsHelper } from '../../client/eventEmitter'; +import { debug } from '../../utilsBundle'; +import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils'; +import { selectors } from '../../..'; import { Tab } from './tab'; -import type * as playwright from '../..'; +import type * as playwright from '../../..'; import type { SessionLog } from './sessionLog'; -import type { Tracing } from '../client/tracing'; -import type { Disposable } from '../client/disposable'; -import type { BrowserContext } from '../client/browserContext'; +import type { Tracing } from '../../client/tracing'; +import type { Disposable } from '../../client/disposable'; +import type { BrowserContext } from '../../client/browserContext'; import type { ToolCapability } from './tool'; const testDebug = debug('pw:mcp:test'); diff --git a/packages/playwright-core/src/tools/cookies.ts b/packages/playwright-core/src/tools/backend/cookies.ts similarity index 99% rename from packages/playwright-core/src/tools/cookies.ts rename to packages/playwright-core/src/tools/backend/cookies.ts index 2a123fb94a7bb..d0a56f97ce348 100644 --- a/packages/playwright-core/src/tools/cookies.ts +++ b/packages/playwright-core/src/tools/backend/cookies.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTool } from './tool'; const cookieList = defineTool({ diff --git a/packages/playwright-core/src/tools/devtools.ts b/packages/playwright-core/src/tools/backend/devtools.ts similarity index 97% rename from packages/playwright-core/src/tools/devtools.ts rename to packages/playwright-core/src/tools/backend/devtools.ts index 7aabe63d19df5..2721fe3d45e0d 100644 --- a/packages/playwright-core/src/tools/devtools.ts +++ b/packages/playwright-core/src/tools/backend/devtools.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTool } from './tool'; const devtoolsConnect = defineTool({ diff --git a/packages/playwright-core/src/tools/dialogs.ts b/packages/playwright-core/src/tools/backend/dialogs.ts similarity index 97% rename from packages/playwright-core/src/tools/dialogs.ts rename to packages/playwright-core/src/tools/backend/dialogs.ts index cdfde818ecac1..beca6dac82985 100644 --- a/packages/playwright-core/src/tools/dialogs.ts +++ b/packages/playwright-core/src/tools/backend/dialogs.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTabTool } from './tool'; export const handleDialog = defineTabTool({ diff --git a/packages/playwright-core/src/tools/evaluate.ts b/packages/playwright-core/src/tools/backend/evaluate.ts similarity index 95% rename from packages/playwright-core/src/tools/evaluate.ts rename to packages/playwright-core/src/tools/backend/evaluate.ts index b90a5a9316c1a..c33e6eb2bfb16 100644 --- a/packages/playwright-core/src/tools/evaluate.ts +++ b/packages/playwright-core/src/tools/backend/evaluate.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; -import { escapeWithQuotes } from '../utils/isomorphic/stringUtils'; +import { z } from '../../mcpBundle'; +import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils'; import { defineTabTool } from './tool'; diff --git a/packages/playwright-core/src/tools/files.ts b/packages/playwright-core/src/tools/backend/files.ts similarity index 97% rename from packages/playwright-core/src/tools/files.ts rename to packages/playwright-core/src/tools/backend/files.ts index e9265fb5ced0b..d872d35bdbafe 100644 --- a/packages/playwright-core/src/tools/files.ts +++ b/packages/playwright-core/src/tools/backend/files.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTabTool } from './tool'; export const uploadFile = defineTabTool({ diff --git a/packages/playwright-core/src/tools/form.ts b/packages/playwright-core/src/tools/backend/form.ts similarity index 96% rename from packages/playwright-core/src/tools/form.ts rename to packages/playwright-core/src/tools/backend/form.ts index 88999d5c1a391..f9cc042bdf5ac 100644 --- a/packages/playwright-core/src/tools/form.ts +++ b/packages/playwright-core/src/tools/backend/form.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; -import { escapeWithQuotes } from '../utils/isomorphic/stringUtils'; +import { z } from '../../mcpBundle'; +import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils'; import { defineTabTool } from './tool'; diff --git a/packages/playwright-core/src/tools/keyboard.ts b/packages/playwright-core/src/tools/backend/keyboard.ts similarity index 99% rename from packages/playwright-core/src/tools/keyboard.ts rename to packages/playwright-core/src/tools/backend/keyboard.ts index 4c1cbf44e583a..91b21999d749b 100644 --- a/packages/playwright-core/src/tools/keyboard.ts +++ b/packages/playwright-core/src/tools/backend/keyboard.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTabTool } from './tool'; import { elementSchema } from './snapshot'; diff --git a/packages/playwright-core/src/tools/logFile.ts b/packages/playwright-core/src/tools/backend/logFile.ts similarity index 98% rename from packages/playwright-core/src/tools/logFile.ts rename to packages/playwright-core/src/tools/backend/logFile.ts index 89e8d4670ac8a..6bdd0e3870f35 100644 --- a/packages/playwright-core/src/tools/logFile.ts +++ b/packages/playwright-core/src/tools/backend/logFile.ts @@ -17,7 +17,7 @@ import fs from 'fs'; import path from 'path'; -import { debug } from '../utilsBundle'; +import { debug } from '../../utilsBundle'; import type { Context } from './context'; diff --git a/packages/playwright-core/src/tools/mouse.ts b/packages/playwright-core/src/tools/backend/mouse.ts similarity index 98% rename from packages/playwright-core/src/tools/mouse.ts rename to packages/playwright-core/src/tools/backend/mouse.ts index eb597edde76d6..f0cdc67c5baa3 100644 --- a/packages/playwright-core/src/tools/mouse.ts +++ b/packages/playwright-core/src/tools/backend/mouse.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; -import { formatObjectOrVoid } from '../utils/isomorphic/stringUtils'; +import { z } from '../../mcpBundle'; +import { formatObjectOrVoid } from '../../utils/isomorphic/stringUtils'; import { defineTabTool } from './tool'; const mouseMove = defineTabTool({ diff --git a/packages/playwright-core/src/tools/navigate.ts b/packages/playwright-core/src/tools/backend/navigate.ts similarity index 98% rename from packages/playwright-core/src/tools/navigate.ts rename to packages/playwright-core/src/tools/backend/navigate.ts index 773b1b0467760..8a5f37a856aed 100644 --- a/packages/playwright-core/src/tools/navigate.ts +++ b/packages/playwright-core/src/tools/backend/navigate.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTool, defineTabTool } from './tool'; const navigate = defineTool({ diff --git a/packages/playwright-core/src/tools/network.ts b/packages/playwright-core/src/tools/backend/network.ts similarity index 97% rename from packages/playwright-core/src/tools/network.ts rename to packages/playwright-core/src/tools/backend/network.ts index 785cc5847b587..2e3040528678e 100644 --- a/packages/playwright-core/src/tools/network.ts +++ b/packages/playwright-core/src/tools/backend/network.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTool, defineTabTool } from './tool'; -import type * as playwright from '../../types/types'; +import type * as playwright from '../../..'; const requests = defineTabTool({ capability: 'core', diff --git a/packages/playwright-core/src/tools/pdf.ts b/packages/playwright-core/src/tools/backend/pdf.ts similarity index 93% rename from packages/playwright-core/src/tools/pdf.ts rename to packages/playwright-core/src/tools/backend/pdf.ts index 182e66d829d0b..b6e8a37d8f50f 100644 --- a/packages/playwright-core/src/tools/pdf.ts +++ b/packages/playwright-core/src/tools/backend/pdf.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; -import { formatObject } from '../utils/isomorphic/stringUtils'; +import { z } from '../../mcpBundle'; +import { formatObject } from '../../utils/isomorphic/stringUtils'; import { defineTabTool } from './tool'; diff --git a/packages/playwright-core/src/tools/response.ts b/packages/playwright-core/src/tools/backend/response.ts similarity index 99% rename from packages/playwright-core/src/tools/response.ts rename to packages/playwright-core/src/tools/backend/response.ts index 2bf41e8d453c0..6d6593303bdc3 100644 --- a/packages/playwright-core/src/tools/response.ts +++ b/packages/playwright-core/src/tools/backend/response.ts @@ -17,7 +17,7 @@ import fs from 'fs'; import path from 'path'; -import { debug } from '../utilsBundle'; +import { debug } from '../../utilsBundle'; import { renderModalStates, shouldIncludeMessage } from './tab'; import { scaleImageToFitMessage } from './screenshot'; diff --git a/packages/playwright-core/src/tools/route.ts b/packages/playwright-core/src/tools/backend/route.ts similarity index 98% rename from packages/playwright-core/src/tools/route.ts rename to packages/playwright-core/src/tools/backend/route.ts index 2e722e39b4906..3b2dfb29744d8 100644 --- a/packages/playwright-core/src/tools/route.ts +++ b/packages/playwright-core/src/tools/backend/route.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTool } from './tool'; -import type * as playwright from '../../types/types'; +import type * as playwright from '../../..'; import type { RouteEntry } from './context'; const route = defineTool({ diff --git a/packages/playwright-core/src/tools/runCode.ts b/packages/playwright-core/src/tools/backend/runCode.ts similarity index 94% rename from packages/playwright-core/src/tools/runCode.ts rename to packages/playwright-core/src/tools/backend/runCode.ts index 82fc6d810c9c0..7602aa86d69c0 100644 --- a/packages/playwright-core/src/tools/runCode.ts +++ b/packages/playwright-core/src/tools/backend/runCode.ts @@ -16,9 +16,9 @@ import vm from 'vm'; -import { ManualPromise } from '../utils/isomorphic/manualPromise'; +import { ManualPromise } from '../../utils/isomorphic/manualPromise'; -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTabTool } from './tool'; const codeSchema = z.object({ diff --git a/packages/playwright-core/src/tools/screenshot.ts b/packages/playwright-core/src/tools/backend/screenshot.ts similarity index 94% rename from packages/playwright-core/src/tools/screenshot.ts rename to packages/playwright-core/src/tools/backend/screenshot.ts index 05a073eb8a669..b2273965a1ac9 100644 --- a/packages/playwright-core/src/tools/screenshot.ts +++ b/packages/playwright-core/src/tools/backend/screenshot.ts @@ -14,14 +14,14 @@ * limitations under the License. */ -import { scaleImageToSize } from '../utils/isomorphic/imageUtils'; -import { jpegjs, PNG } from '../utilsBundle'; -import { formatObject } from '../utils/isomorphic/stringUtils'; +import { scaleImageToSize } from '../../utils/isomorphic/imageUtils'; +import { jpegjs, PNG } from '../../utilsBundle'; +import { formatObject } from '../../utils/isomorphic/stringUtils'; -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTabTool } from './tool'; -import type * as playwright from '../../types/types'; +import type * as playwright from '../../..'; const screenshotSchema = z.object({ type: z.enum(['png', 'jpeg']).default('png').describe('Image format for the screenshot. Default is png.'), diff --git a/packages/playwright-core/src/tools/sessionLog.ts b/packages/playwright-core/src/tools/backend/sessionLog.ts similarity index 100% rename from packages/playwright-core/src/tools/sessionLog.ts rename to packages/playwright-core/src/tools/backend/sessionLog.ts diff --git a/packages/playwright-core/src/tools/snapshot.ts b/packages/playwright-core/src/tools/backend/snapshot.ts similarity index 98% rename from packages/playwright-core/src/tools/snapshot.ts rename to packages/playwright-core/src/tools/backend/snapshot.ts index 39dc76b10f163..2988dff0bbf1c 100644 --- a/packages/playwright-core/src/tools/snapshot.ts +++ b/packages/playwright-core/src/tools/backend/snapshot.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; -import { formatObject, formatObjectOrVoid } from '../utils/isomorphic/stringUtils'; +import { z } from '../../mcpBundle'; +import { formatObject, formatObjectOrVoid } from '../../utils/isomorphic/stringUtils'; import { defineTabTool, defineTool } from './tool'; diff --git a/packages/playwright-core/src/tools/storage.ts b/packages/playwright-core/src/tools/backend/storage.ts similarity index 98% rename from packages/playwright-core/src/tools/storage.ts rename to packages/playwright-core/src/tools/backend/storage.ts index d7668daebefc1..152ba453b17a7 100644 --- a/packages/playwright-core/src/tools/storage.ts +++ b/packages/playwright-core/src/tools/backend/storage.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTool } from './tool'; const storageState = defineTool({ diff --git a/packages/playwright-core/src/tools/tab.ts b/packages/playwright-core/src/tools/backend/tab.ts similarity index 97% rename from packages/playwright-core/src/tools/tab.ts rename to packages/playwright-core/src/tools/backend/tab.ts index 93c936e72a1f7..1b350fcac8c14 100644 --- a/packages/playwright-core/src/tools/tab.ts +++ b/packages/playwright-core/src/tools/backend/tab.ts @@ -17,23 +17,23 @@ import url from 'url'; import { EventEmitter } from 'events'; -import { asLocator } from '../utils/isomorphic/locatorGenerators'; -import { ManualPromise } from '../utils/isomorphic/manualPromise'; -import { debug } from '../utilsBundle'; +import { asLocator } from '../../utils/isomorphic/locatorGenerators'; +import { ManualPromise } from '../../utils/isomorphic/manualPromise'; +import { debug } from '../../utilsBundle'; -import { eventsHelper } from '../client/eventEmitter'; +import { eventsHelper } from '../../client/eventEmitter'; import { callOnPageNoTrace, waitForCompletion, eventWaiter } from './utils'; import { LogFile } from './logFile'; import { ModalState } from './tool'; import { handleDialog } from './dialogs'; import { uploadFile } from './files'; -import { disposeAll } from '../client/disposable'; +import { disposeAll } from '../../client/disposable'; -import type { Disposable } from '../client/disposable'; +import type { Disposable } from '../../client/disposable'; import type { Context, ContextConfig } from './context'; -import type { Page } from '../client/page'; -import type { Locator } from '../client/locator'; -import type * as playwright from '../../types/types'; +import type { Page } from '../../client/page'; +import type { Locator } from '../../client/locator'; +import type * as playwright from '../../..'; const TabEvents = { modalState: 'modalState' diff --git a/packages/playwright-core/src/tools/tabs.ts b/packages/playwright-core/src/tools/backend/tabs.ts similarity index 98% rename from packages/playwright-core/src/tools/tabs.ts rename to packages/playwright-core/src/tools/backend/tabs.ts index b308ae119a4e6..679421be3f9a1 100644 --- a/packages/playwright-core/src/tools/tabs.ts +++ b/packages/playwright-core/src/tools/backend/tabs.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTool } from './tool'; import { renderTabsMarkdown } from './response'; diff --git a/packages/playwright-core/src/tools/tool.ts b/packages/playwright-core/src/tools/backend/tool.ts similarity index 98% rename from packages/playwright-core/src/tools/tool.ts rename to packages/playwright-core/src/tools/backend/tool.ts index b6a7e208cabe5..37de63c1c8383 100644 --- a/packages/playwright-core/src/tools/tool.ts +++ b/packages/playwright-core/src/tools/backend/tool.ts @@ -16,7 +16,7 @@ import type { z } from 'zod'; import type { Context } from './context'; -import type * as playwright from '../../types/types'; +import type * as playwright from '../../..'; import type { Tab } from './tab'; import type { Response } from './response'; diff --git a/packages/playwright-core/src/tools/tools.ts b/packages/playwright-core/src/tools/backend/tools.ts similarity index 98% rename from packages/playwright-core/src/tools/tools.ts rename to packages/playwright-core/src/tools/backend/tools.ts index 9ad0aebef7bb3..de8d3eda86ac9 100644 --- a/packages/playwright-core/src/tools/tools.ts +++ b/packages/playwright-core/src/tools/backend/tools.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import common from './common'; import config from './config'; diff --git a/packages/playwright-core/src/tools/tracing.ts b/packages/playwright-core/src/tools/backend/tracing.ts similarity index 96% rename from packages/playwright-core/src/tools/tracing.ts rename to packages/playwright-core/src/tools/backend/tracing.ts index 606ab0ee33773..b141131be620a 100644 --- a/packages/playwright-core/src/tools/tracing.ts +++ b/packages/playwright-core/src/tools/backend/tracing.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTool } from './tool'; -import type { Tracing } from '../client/tracing'; +import type { Tracing } from '../../client/tracing'; const tracingStart = defineTool({ capability: 'devtools', diff --git a/packages/playwright-core/src/tools/utils.ts b/packages/playwright-core/src/tools/backend/utils.ts similarity index 98% rename from packages/playwright-core/src/tools/utils.ts rename to packages/playwright-core/src/tools/backend/utils.ts index cd25bd1649b12..918dc0e7d1c21 100644 --- a/packages/playwright-core/src/tools/utils.ts +++ b/packages/playwright-core/src/tools/backend/utils.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type * as playwright from '../../types/types'; +import type * as playwright from '../../..'; import type { Tab } from './tab'; export async function waitForCompletion(tab: Tab, callback: () => Promise): Promise { diff --git a/packages/playwright-core/src/tools/verify.ts b/packages/playwright-core/src/tools/backend/verify.ts similarity index 98% rename from packages/playwright-core/src/tools/verify.ts rename to packages/playwright-core/src/tools/backend/verify.ts index bb5ff8e8255df..09d33f5209ec3 100644 --- a/packages/playwright-core/src/tools/verify.ts +++ b/packages/playwright-core/src/tools/backend/verify.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; -import { escapeWithQuotes } from '../utils/isomorphic/stringUtils'; +import { z } from '../../mcpBundle'; +import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils'; import { defineTabTool } from './tool'; diff --git a/packages/playwright-core/src/tools/video.ts b/packages/playwright-core/src/tools/backend/video.ts similarity index 98% rename from packages/playwright-core/src/tools/video.ts rename to packages/playwright-core/src/tools/backend/video.ts index 698ccc9a83993..8b1d1f93c0fee 100644 --- a/packages/playwright-core/src/tools/video.ts +++ b/packages/playwright-core/src/tools/backend/video.ts @@ -15,7 +15,7 @@ */ import path from 'path'; -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTool } from './tool'; const startVideo = defineTool({ diff --git a/packages/playwright-core/src/tools/wait.ts b/packages/playwright-core/src/tools/backend/wait.ts similarity index 98% rename from packages/playwright-core/src/tools/wait.ts rename to packages/playwright-core/src/tools/backend/wait.ts index 97bbe9876838b..6de520941badc 100644 --- a/packages/playwright-core/src/tools/wait.ts +++ b/packages/playwright-core/src/tools/backend/wait.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTool } from './tool'; const wait = defineTool({ diff --git a/packages/playwright-core/src/tools/webstorage.ts b/packages/playwright-core/src/tools/backend/webstorage.ts similarity index 99% rename from packages/playwright-core/src/tools/webstorage.ts rename to packages/playwright-core/src/tools/backend/webstorage.ts index 5beacb1d62a93..8c2598a444f44 100644 --- a/packages/playwright-core/src/tools/webstorage.ts +++ b/packages/playwright-core/src/tools/backend/webstorage.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z } from '../mcpBundle'; +import { z } from '../../mcpBundle'; import { defineTabTool } from './tool'; const localStorageList = defineTabTool({ diff --git a/packages/playwright-core/src/cli/client/DEPS.list b/packages/playwright-core/src/tools/cli-client/DEPS.list similarity index 85% rename from packages/playwright-core/src/cli/client/DEPS.list rename to packages/playwright-core/src/tools/cli-client/DEPS.list index 670e75657986b..a780566006ba7 100644 --- a/packages/playwright-core/src/cli/client/DEPS.list +++ b/packages/playwright-core/src/tools/cli-client/DEPS.list @@ -6,7 +6,7 @@ [session.ts] "strict" -./socketConnection.ts +../utils/socketConnection.ts ./registry.ts [socketConnection.ts] diff --git a/packages/playwright-core/src/cli/client/cli.ts b/packages/playwright-core/src/tools/cli-client/cli.ts similarity index 100% rename from packages/playwright-core/src/cli/client/cli.ts rename to packages/playwright-core/src/tools/cli-client/cli.ts diff --git a/packages/playwright-core/src/cli/client/program.ts b/packages/playwright-core/src/tools/cli-client/program.ts similarity index 98% rename from packages/playwright-core/src/cli/client/program.ts rename to packages/playwright-core/src/tools/cli-client/program.ts index 63705f9714e9f..346ef1999b4fb 100644 --- a/packages/playwright-core/src/cli/client/program.ts +++ b/packages/playwright-core/src/tools/cli-client/program.ts @@ -27,7 +27,7 @@ import { createClientInfo, Registry, resolveSessionName } from './registry'; import { Session, renderResolvedConfig } from './session'; import { serverRegistry } from '../../serverRegistry'; -import type { Config } from '../../mcp/config.d'; +import type { Config } from '../mcp/config.d'; import type { ClientInfo, SessionFile } from './registry'; import type { BrowserDescriptor } from '../../serverRegistry'; @@ -296,7 +296,7 @@ async function killAllDaemons(): Promise { const result = execSync( `powershell -NoProfile -NonInteractive -Command ` + `"Get-CimInstance Win32_Process ` - + `| Where-Object { $_.CommandLine -like '*run-mcp-server*' -or $_.CommandLine -like '*run-cli-server*' } ` + + `| Where-Object { $_.CommandLine -like '*run-mcp-server*' -or $_.CommandLine -like '*run-cli-server*' -or $_.CommandLine -like '*cli-daemon*' } ` + `| ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue; $_.ProcessId }"`, { encoding: 'utf-8' } ); @@ -310,7 +310,7 @@ async function killAllDaemons(): Promise { const result = execSync('ps aux', { encoding: 'utf-8' }); const lines = result.split('\n'); for (const line of lines) { - if (line.includes('run-mcp-server') || line.includes('run-cli-server')) { + if (line.includes('run-mcp-server') || line.includes('run-cli-server') || line.includes('cli-daemon')) { const parts = line.trim().split(/\s+/); const pid = parts[1]; if (pid && /^\d+$/.test(pid)) { diff --git a/packages/playwright-core/src/cli/client/registry.ts b/packages/playwright-core/src/tools/cli-client/registry.ts similarity index 100% rename from packages/playwright-core/src/cli/client/registry.ts rename to packages/playwright-core/src/tools/cli-client/registry.ts diff --git a/packages/playwright-core/src/cli/client/session.ts b/packages/playwright-core/src/tools/cli-client/session.ts similarity index 95% rename from packages/playwright-core/src/cli/client/session.ts rename to packages/playwright-core/src/tools/cli-client/session.ts index 029a059664e67..b18fb9a9b5ef3 100644 --- a/packages/playwright-core/src/cli/client/session.ts +++ b/packages/playwright-core/src/tools/cli-client/session.ts @@ -22,7 +22,7 @@ import fs from 'fs'; import net from 'net'; import os from 'os'; import path from 'path'; -import { compareSemver, SocketConnection } from './socketConnection'; +import { compareSemver, SocketConnection } from '../utils/socketConnection'; import { resolveSessionName } from './registry'; import type { SessionConfig, ClientInfo, SessionFile } from './registry'; @@ -126,15 +126,13 @@ export class Session { static async startDaemon(clientInfo: ClientInfo, cliArgs: MinimistArgs) { await fs.promises.mkdir(clientInfo.daemonProfilesDir, { recursive: true }); - const cliPath = path.join(__dirname, '../../../cli.js'); - + const cliPath = require.resolve('../cli-daemon/program.js'); const sessionName = resolveSessionName(cliArgs.session); const errLog = path.join(clientInfo.daemonProfilesDir, sessionName + '.err'); const err = fs.openSync(errLog, 'w'); const args = [ cliPath, - 'run-cli-server', sessionName, ]; if (cliArgs.headed) @@ -189,8 +187,11 @@ export class Session { resolve(); }); child.on('close', code => { - if (!signalled) - reject(new Error(`Daemon process exited with code ${code}`)); + if (!signalled) { + const errLogContent = fs.readFileSync(errLog, 'utf-8'); + const message = `Daemon process exited with code ${code}` + (errLogContent ? '\n' + errLogContent : ''); + reject(new Error(message)); + } }); }); diff --git a/packages/playwright-core/src/cli/daemon/DEPS.list b/packages/playwright-core/src/tools/cli-daemon/DEPS.list similarity index 57% rename from packages/playwright-core/src/cli/daemon/DEPS.list rename to packages/playwright-core/src/tools/cli-daemon/DEPS.list index 8f52042492225..da410b83b2998 100644 --- a/packages/playwright-core/src/cli/daemon/DEPS.list +++ b/packages/playwright-core/src/tools/cli-daemon/DEPS.list @@ -1,10 +1,10 @@ [*] -../client/socketConnection.ts -../client/registry.ts -../../tools/ +../utils/socketConnection.ts +../cli-client/registry.ts +../backend/ +../mcp/ ../../utilsBundle.ts ../../utils/ ../../mcpBundle.ts -../../mcp/ ../../server/utils/ ../../serverRegistry.ts diff --git a/packages/playwright-core/src/cli/daemon/command.ts b/packages/playwright-core/src/tools/cli-daemon/command.ts similarity index 100% rename from packages/playwright-core/src/cli/daemon/command.ts rename to packages/playwright-core/src/tools/cli-daemon/command.ts diff --git a/packages/playwright-core/src/cli/daemon/commands.ts b/packages/playwright-core/src/tools/cli-daemon/commands.ts similarity index 100% rename from packages/playwright-core/src/cli/daemon/commands.ts rename to packages/playwright-core/src/tools/cli-daemon/commands.ts diff --git a/packages/playwright-core/src/cli/daemon/daemon.ts b/packages/playwright-core/src/tools/cli-daemon/daemon.ts similarity index 90% rename from packages/playwright-core/src/cli/daemon/daemon.ts rename to packages/playwright-core/src/tools/cli-daemon/daemon.ts index 8bc57e97b2ca8..cf106166da286 100644 --- a/packages/playwright-core/src/cli/daemon/daemon.ts +++ b/packages/playwright-core/src/tools/cli-daemon/daemon.ts @@ -25,18 +25,19 @@ import { debug } from '../../utilsBundle'; import { decorateServer } from '../../server/utils/network'; import { gracefullyProcessExitDoNotHang } from '../../server/utils/processLauncher'; -import { BrowserServerBackend } from '../../tools/browserServerBackend'; -import { browserTools } from '../../tools/tools'; +import { BrowserBackend } from '../backend/browserBackend'; +import { browserTools } from '../backend/tools'; import { parseCommand } from './command'; import { commands } from './commands'; -import { SocketConnection } from '../client/socketConnection'; -import { createClientInfo } from '../client/registry'; +import { SocketConnection } from '../utils/socketConnection'; +import { createClientInfo } from '../cli-client/registry'; import type * as playwright from '../../..'; -import type * as tools from '../../tools/exports'; -import type { SessionConfig, ClientInfo } from '../client/registry'; +import type { SessionConfig, ClientInfo } from '../cli-client/registry'; import type { BrowserContext } from '../../client/browserContext'; +import type { CallToolRequest, CallToolResult } from '../backend/tool'; +import type { ContextConfig } from '../backend/context'; const daemonDebug = debug('pw:daemon'); @@ -53,7 +54,7 @@ async function socketExists(socketPath: string): Promise { export async function startCliDaemonServer( sessionName: string, browserContext: playwright.BrowserContext, - contextConfig: tools.ContextConfig = {}, + contextConfig: ContextConfig = {}, clientInfo = createClientInfo(), options?: { persistent?: boolean, @@ -74,7 +75,7 @@ export async function startCliDaemonServer( } } - const backend = new BrowserServerBackend(contextConfig, browserContext, browserTools); + const backend = new BrowserBackend(contextConfig, browserContext, browserTools); await backend.initialize({ cwd: process.cwd() }); await fs.promises.mkdir(path.dirname(socketPath), { recursive: true }); @@ -150,13 +151,13 @@ async function deleteSessionFile(clientInfo: ClientInfo, sessionConfig: SessionC } } -function formatResult(result: tools.CallToolResult) { +function formatResult(result: CallToolResult) { const isError = result.isError; const text = result.content[0].type === 'text' ? result.content[0].text : undefined; return { isError, text }; } -function parseCliCommand(args: Record & { _: string[] }): { toolName: string, toolParams: NonNullable } { +function parseCliCommand(args: Record & { _: string[] }): { toolName: string, toolParams: NonNullable } { const command = commands[args._[0]]; if (!command) throw new Error('Command is required'); diff --git a/packages/playwright-core/src/cli/daemon/helpGenerator.ts b/packages/playwright-core/src/tools/cli-daemon/helpGenerator.ts similarity index 98% rename from packages/playwright-core/src/cli/daemon/helpGenerator.ts rename to packages/playwright-core/src/tools/cli-daemon/helpGenerator.ts index 33c70c9b2830c..2a1bed430eade 100644 --- a/packages/playwright-core/src/cli/daemon/helpGenerator.ts +++ b/packages/playwright-core/src/tools/cli-daemon/helpGenerator.ts @@ -16,10 +16,10 @@ import { z } from '../../mcpBundle'; -import { commands } from '../daemon/commands'; +import { commands } from './commands'; import type zodType from 'zod'; -import type { AnyCommandSchema, Category } from '../daemon/command'; +import type { AnyCommandSchema, Category } from './command'; type CommandArg = { name: string, description: string, optional: boolean }; diff --git a/packages/playwright-core/src/cli/daemon/program.ts b/packages/playwright-core/src/tools/cli-daemon/program.ts similarity index 53% rename from packages/playwright-core/src/cli/daemon/program.ts rename to packages/playwright-core/src/tools/cli-daemon/program.ts index eb814b2d9f03a..4ba6be398cce8 100644 --- a/packages/playwright-core/src/cli/daemon/program.ts +++ b/packages/playwright-core/src/tools/cli-daemon/program.ts @@ -20,51 +20,49 @@ import fs from 'fs'; import path from 'path'; import { startCliDaemonServer } from './daemon'; -import { setupExitWatchdog } from '../../mcp/watchdog'; -import { createBrowser } from '../../mcp/browserFactory'; -import * as configUtils from '../../mcp/config'; -import { ClientInfo, createClientInfo } from '../client/registry'; - -import type { Command } from '../../utilsBundle'; -import type { FullConfig } from '../../mcp/config'; - -export function decorateCLICommand(command: Command, version: string) { - command - .version(version) - .argument('[session-name]', 'name of the session to create or connect to', 'default') - .option('--headed', 'run in headed mode (non-headless)') - .option('--extension', 'run with the extension') - .option('--browser ', 'browser to use (chromium, chrome, firefox, webkit)') - .option('--persistent', 'use a persistent browser context') - .option('--profile ', 'path to the user data dir') - .option('--config ', 'path to the config file') - .option('--attach ', 'attach to a running Playwright browser by name or endpoint') - - .action(async (sessionName: string, options: any) => { - setupExitWatchdog(); - const clientInfo = createClientInfo(); - const mcpConfig = await resolveCLIConfig(clientInfo, sessionName, options); - const mcpClientInfo = { cwd: process.cwd() }; - - try { - const browser = await createBrowser(mcpConfig, mcpClientInfo); - const browserContext = mcpConfig.browser.isolated ? await browser.newContext(mcpConfig.browser.contextOptions) : browser.contexts()[0]; - const persistent = options.persistent || options.profile || mcpConfig.browser.userDataDir ? true : undefined; - const socketPath = await startCliDaemonServer(sessionName, browserContext, mcpConfig, clientInfo, { persistent, exitOnClose: true }); - console.log(`### Success\nDaemon listening on ${socketPath}`); - console.log(''); - - if (!(browser as any)._connection.isRemote()) { - await (browser as any)._startServer(sessionName, { workspaceDir: clientInfo.workspaceDir }); - browserContext.on('close', () => (browser as any)._stopServer().catch(() => {})); - } - } catch (error) { - const message = process.env.PWDEBUGIMPL ? (error as Error).stack || (error as Error).message : (error as Error).message; - console.log(`### Error\n${message}`); - console.log(''); +import { setupExitWatchdog } from '../mcp/watchdog'; +import { createBrowser } from '../mcp/browserFactory'; +import * as configUtils from '../mcp/config'; +import { ClientInfo, createClientInfo } from '../cli-client/registry'; +import { program } from '../../utilsBundle'; + +import type { FullConfig } from '../mcp/config'; + +program.argument('[session-name]', 'name of the session to create or connect to', 'default') + .option('--headed', 'run in headed mode (non-headless)') + .option('--extension', 'run with the extension') + .option('--browser ', 'browser to use (chromium, chrome, firefox, webkit)') + .option('--persistent', 'use a persistent browser context') + .option('--profile ', 'path to the user data dir') + .option('--config ', 'path to the config file') + .option('--attach ', 'attach to a running Playwright browser by name or endpoint') + + .action(async (sessionName: string, options: any) => { + setupExitWatchdog(); + const clientInfo = createClientInfo(); + const mcpConfig = await resolveCLIConfig(clientInfo, sessionName, options); + const mcpClientInfo = { cwd: process.cwd() }; + + try { + const browser = await createBrowser(mcpConfig, mcpClientInfo); + const browserContext = mcpConfig.browser.isolated ? await browser.newContext(mcpConfig.browser.contextOptions) : browser.contexts()[0]; + const persistent = options.persistent || options.profile || mcpConfig.browser.userDataDir ? true : undefined; + const socketPath = await startCliDaemonServer(sessionName, browserContext, mcpConfig, clientInfo, { persistent, exitOnClose: true }); + console.log(`### Success\nDaemon listening on ${socketPath}`); + console.log(''); + + if (!(browser as any)._connection.isRemote()) { + await (browser as any)._startServer(sessionName, { workspaceDir: clientInfo.workspaceDir }); + browserContext.on('close', () => (browser as any)._stopServer().catch(() => {})); } - }); -} + } catch (error) { + const message = process.env.PWDEBUGIMPL ? (error as Error).stack || (error as Error).message : (error as Error).message; + console.log(`### Error\n${message}`); + console.log(''); + } + }); + +void program.parseAsync(); function defaultConfigFile(): string { return path.resolve('.playwright', 'cli.config.json'); diff --git a/packages/playwright-core/src/tools/dashboard/DEPS.list b/packages/playwright-core/src/tools/dashboard/DEPS.list new file mode 100644 index 0000000000000..97630bfa6a62d --- /dev/null +++ b/packages/playwright-core/src/tools/dashboard/DEPS.list @@ -0,0 +1,9 @@ +[*] +../../.. +../../ +../../server/registry/index.ts +../../server/utils/ +../../serverRegistry.ts +../../utils/ +../../client/connect.ts +../../client/eventEmitter.ts diff --git a/packages/playwright-core/src/devtools/appIcon.png b/packages/playwright-core/src/tools/dashboard/appIcon.png similarity index 100% rename from packages/playwright-core/src/devtools/appIcon.png rename to packages/playwright-core/src/tools/dashboard/appIcon.png diff --git a/packages/playwright-core/src/devtools/devtoolsApp.ts b/packages/playwright-core/src/tools/dashboard/dashboardApp.ts similarity index 86% rename from packages/playwright-core/src/devtools/devtoolsApp.ts rename to packages/playwright-core/src/tools/dashboard/dashboardApp.ts index 174bddf44314e..97122d4934244 100644 --- a/packages/playwright-core/src/devtools/devtoolsApp.ts +++ b/packages/playwright-core/src/tools/dashboard/dashboardApp.ts @@ -20,17 +20,17 @@ import os from 'os'; import net from 'net'; import http from 'http'; -import { chromium } from '../..'; -import { HttpServer } from '../server/utils/httpServer'; -import { gracefullyProcessExitDoNotHang } from '../server/utils/processLauncher'; -import { findChromiumChannelBestEffort, registryDirectory } from '../server/registry/index'; -import { calculateSha1 } from '../utils'; -import { CDPConnection, DevToolsConnection } from './devtoolsController'; -import { serverRegistry } from '../serverRegistry'; -import { connectToBrowserAcrossVersions } from '../client/connect'; - -import type * as api from '../..'; -import type { SessionStatus } from '@devtools/sessionModel'; +import { chromium } from '../../..'; +import { HttpServer } from '../../server/utils/httpServer'; +import { gracefullyProcessExitDoNotHang } from '../../server/utils/processLauncher'; +import { findChromiumChannelBestEffort, registryDirectory } from '../../server/registry/index'; +import { calculateSha1 } from '../../utils'; +import { CDPConnection, DashboardConnection } from './dashboardController'; +import { serverRegistry } from '../../serverRegistry'; +import { connectToBrowserAcrossVersions } from '../../client/connect'; + +import type * as api from '../../..'; +import type { SessionStatus } from '../../../../dashboard/src/sessionModel'; function readBody(request: http.IncomingMessage): Promise { return new Promise((resolve, reject) => { @@ -78,7 +78,7 @@ async function loadBrowserDescriptorSessions(wsPath: string): Promise(); +const browserGuidToDashboardConnection = new Map(); async function handleApiRequest(httpServer: HttpServer, request: http.IncomingMessage, response: http.ServerResponse) { const url = new URL(request.url!, httpServer.urlPrefix('human-readable')); @@ -127,10 +127,10 @@ async function handleApiRequest(httpServer: HttpServer, request: http.IncomingMe response.end(JSON.stringify({ error: 'Not found' })); } -async function openDevToolsApp(): Promise { +async function openDashboardApp(): Promise { const httpServer = new HttpServer(); const libDir = require.resolve('playwright-core/package.json'); - const devtoolsDir = path.join(path.dirname(libDir), 'lib/vite/devtools'); + const dashboardDir = path.join(path.dirname(libDir), 'lib/vite/dashboard'); httpServer.routePrefix('/api/', (request: http.IncomingMessage, response: http.ServerResponse) => { handleApiRequest(httpServer, request, response).catch(e => { @@ -148,7 +148,7 @@ async function openDevToolsApp(): Promise { const cdpPageId = url.searchParams.get('cdpPageId'); if (cdpPageId) { - const connection = browserGuidToDevToolsConnection.get(guid); + const connection = browserGuidToDashboardConnection.get(guid); if (!connection) throw new Error('CDP connection not found for session: ' + guid); const page = connection.pageForId(cdpPageId); @@ -160,23 +160,23 @@ async function openDevToolsApp(): Promise { const cdpUrl = new URL(httpServer.urlPrefix('human-readable')); cdpUrl.pathname = httpServer.wsGuid()!; cdpUrl.searchParams.set('guid', guid); - const connection = new DevToolsConnection(browserDescriptor, cdpUrl, () => browserGuidToDevToolsConnection.delete(guid)); - browserGuidToDevToolsConnection.set(guid, connection); + const connection = new DashboardConnection(browserDescriptor, cdpUrl, () => browserGuidToDashboardConnection.delete(guid)); + browserGuidToDashboardConnection.set(guid, connection); return connection; }); httpServer.routePrefix('/', (request: http.IncomingMessage, response: http.ServerResponse) => { const pathname = new URL(request.url!, `http://${request.headers.host}`).pathname; const filePath = pathname === '/' ? 'index.html' : pathname.substring(1); - const resolved = path.join(devtoolsDir, filePath); - if (!resolved.startsWith(devtoolsDir)) + const resolved = path.join(dashboardDir, filePath); + if (!resolved.startsWith(dashboardDir)) return false; return httpServer.serveFile(request, response, resolved); }); await httpServer.start(); const url = httpServer.urlPrefix('human-readable'); - const { page } = await launchApp('devtools'); + const { page } = await launchApp('dashboard'); await page.goto(url); return page; } @@ -246,15 +246,15 @@ function socketsDirectory() { return process.env.PLAYWRIGHT_DAEMON_SOCKETS_DIR || path.join(os.tmpdir(), 'playwright-cli'); } -function devtoolsSocketPath() { +function dashboardSocketPath() { const userNameHash = calculateSha1(process.env.USERNAME || 'default').slice(0, 8); return process.platform === 'win32' - ? `\\\\.\\pipe\\playwright-devtools-${userNameHash}` - : path.join(socketsDirectory(), `devtools-${userNameHash}.sock`); + ? `\\\\.\\pipe\\playwright-dashboard-${userNameHash}` + : path.join(socketsDirectory(), `dashboard-${userNameHash}.sock`); } async function acquireSingleton(): Promise { - const socketPath = devtoolsSocketPath(); + const socketPath = dashboardSocketPath(); if (process.platform !== 'win32') await fs.promises.mkdir(path.dirname(socketPath), { recursive: true }); @@ -286,7 +286,7 @@ async function main() { } catch { return; } - const page = await openDevToolsApp(); + const page = await openDashboardApp(); server.on('connection', socket => { socket.on('data', data => { if (data.toString() === 'bringToFront') diff --git a/packages/playwright-core/src/devtools/devtoolsController.ts b/packages/playwright-core/src/tools/dashboard/dashboardController.ts similarity index 87% rename from packages/playwright-core/src/devtools/devtoolsController.ts rename to packages/playwright-core/src/tools/dashboard/dashboardController.ts index 31bbdfcb76bfe..b275204db6d7a 100644 --- a/packages/playwright-core/src/devtools/devtoolsController.ts +++ b/packages/playwright-core/src/tools/dashboard/dashboardController.ts @@ -14,15 +14,15 @@ * limitations under the License. */ -import { eventsHelper } from '../client/eventEmitter'; -import { connectToBrowserAcrossVersions } from '../client/connect'; +import { eventsHelper } from '../../client/eventEmitter'; +import { connectToBrowserAcrossVersions } from '../../client/connect'; -import type * as api from '../../types/types'; -import type { Transport } from '../server/utils/httpServer'; -import type { DevToolsChannel, DevToolsChannelEvents, Tab } from '@devtools/devtoolsChannel'; -import type { BrowserDescriptor } from '../serverRegistry'; +import type * as api from '../../..'; +import type { Transport } from '../../server/utils/httpServer'; +import type { DashboardChannel, DashboardChannelEvents, Tab } from '../../../../dashboard/src/dashboardChannel'; +import type { BrowserDescriptor } from '../../serverRegistry'; -export class DevToolsConnection implements Transport, DevToolsChannel { +export class DashboardConnection implements Transport, DashboardChannel { readonly version = 1; sendEvent?: (method: string, params: any) => void; @@ -49,7 +49,7 @@ export class DevToolsConnection implements Transport, DevToolsChannel { this._onclose = onclose; } - on(event: K, listener: (params: DevToolsChannelEvents[K]) => void): void { + on(event: K, listener: (params: DashboardChannelEvents[K]) => void): void { let set = this._eventListeners.get(event); if (!set) { set = new Set(); @@ -58,11 +58,11 @@ export class DevToolsConnection implements Transport, DevToolsChannel { set.add(listener); } - off(event: K, listener: (params: DevToolsChannelEvents[K]) => void): void { + off(event: K, listener: (params: DashboardChannelEvents[K]) => void): void { this._eventListeners.get(event)?.delete(listener); } - private _emit(event: K, params: DevToolsChannelEvents[K]): void { + private _emit(event: K, params: DashboardChannelEvents[K]): void { this.sendEvent?.(event, params); const set = this._eventListeners.get(event); if (set) { @@ -118,7 +118,7 @@ export class DevToolsConnection implements Transport, DevToolsChannel { async closeTab(params: { pageId: string }) { const page = this._context.pages().find(p => this._pageId(p) === params.pageId); if (page) - await page.close({ reason: 'Closed from devtools' }); + await page.close({ reason: 'Closed in Dashboard' }); } async newTab() { @@ -241,13 +241,13 @@ export class DevToolsConnection implements Transport, DevToolsChannel { const pages = this._context.pages(); if (pages.length === 0) return []; - const devtoolsUrl = await this._devtoolsUrl(pages[0]); + const dashboardUrl = await this._dashboardUrl(pages[0]); return await Promise.all(pages.map(async page => ({ pageId: this._pageId(page), title: await page.title() || page.url(), url: page.url(), selected: page === this.selectedPage, - inspectorUrl: devtoolsUrl ? await this._pageInspectorUrl(page, devtoolsUrl) : 'data:text/plain,DevTools only supported in Chromium based browsers', + inspectorUrl: dashboardUrl ? await this._pageInspectorUrl(page, dashboardUrl) : 'data:text/plain,Dashboard only supported in Chromium based browsers', }))); } @@ -259,10 +259,10 @@ export class DevToolsConnection implements Transport, DevToolsChannel { return (p as any)._guid; } - private async _devtoolsUrl(page: api.Page) { + private async _dashboardUrl(page: api.Page) { const cdpPort = (this._browserDescriptor.browser.launchOptions as any).cdpPort; if (cdpPort) - return new URL(`http://localhost:${cdpPort}/devtools/`); + return new URL(`http://localhost:${cdpPort}/dashboard/`); const browserRevision = await getBrowserRevision(page); if (!browserRevision) @@ -270,8 +270,8 @@ export class DevToolsConnection implements Transport, DevToolsChannel { return new URL(`https://chrome-devtools-frontend.appspot.com/serve_rev/${browserRevision}/`); } - private async _pageInspectorUrl(page: api.Page, devtoolsUrl: URL): Promise { - const inspector = new URL('./devtools_app.html', devtoolsUrl); + private async _pageInspectorUrl(page: api.Page, dashboardUrl: URL): Promise { + const inspector = new URL('./devtools_app.html', dashboardUrl); const cdp = new URL(this._cdpUrl); cdp.searchParams.set('cdpPageId', this._pageId(page)); inspector.searchParams.set('ws', `${cdp.host}${cdp.pathname}${cdp.search}`); diff --git a/packages/playwright-core/src/tools/exports.ts b/packages/playwright-core/src/tools/exports.ts index 40f38559e2266..543441ba65fc0 100644 --- a/packages/playwright-core/src/tools/exports.ts +++ b/packages/playwright-core/src/tools/exports.ts @@ -14,11 +14,23 @@ * limitations under the License. */ -export { BrowserServerBackend } from './browserServerBackend'; -export { browserTools } from './tools'; -export { filteredTools } from './tools'; -export { parseResponse } from './response'; -export { Tab } from './tab'; +export { createClientInfo } from './cli-client/registry'; +export { startCliDaemonServer } from './cli-daemon/daemon'; +export { logUnhandledError } from './mcp/log'; +export { setupExitWatchdog } from './mcp/watchdog'; +export { toMcpTool } from './utils/mcp/tool'; -export type { ContextConfig } from './context'; -export type { Tool, CallToolResult, CallToolRequest } from './tool'; +export { BrowserBackend } from './backend/browserBackend'; +export { parseResponse } from './backend/response'; +export { Tab } from './backend/tab'; +export { browserTools, filteredTools } from './backend/tools'; +export { start } from './utils/mcp/server'; + +export type { ContextConfig } from './backend/context'; +export type { CallToolRequest, CallToolResult, Tool } from './backend/tool'; +export type { ClientInfo } from './utils/mcp/server'; +export type { FullConfig } from './mcp/config'; +export type { ServerBackend } from './utils/mcp/server'; +export type { ToolSchema } from './utils/mcp/tool'; +export type { ServerBackendFactory } from './utils/mcp/server'; +export { createConnection } from './mcp/index'; diff --git a/packages/playwright-core/src/tools/mcp/DEPS.list b/packages/playwright-core/src/tools/mcp/DEPS.list new file mode 100644 index 0000000000000..cf8eca0e5ae82 --- /dev/null +++ b/packages/playwright-core/src/tools/mcp/DEPS.list @@ -0,0 +1,14 @@ +[*] +../../.. +../../ +../utils/mcp/ +../backend/ +../../utils/ +../../utils/isomorphic/ +../../utilsBundle.ts +../../mcpBundle.ts +../../server/ +../../server/registry/ +../../server/utils/ +../../client/ +../../serverRegistry.ts diff --git a/packages/playwright-core/src/mcp/browserFactory.ts b/packages/playwright-core/src/tools/mcp/browserFactory.ts similarity index 95% rename from packages/playwright-core/src/mcp/browserFactory.ts rename to packages/playwright-core/src/tools/mcp/browserFactory.ts index df9ad6f531cd7..a56575ddec099 100644 --- a/packages/playwright-core/src/mcp/browserFactory.ts +++ b/packages/playwright-core/src/tools/mcp/browserFactory.ts @@ -19,18 +19,18 @@ import fs from 'fs'; import net from 'net'; import path from 'path'; -import * as playwright from '../..'; -import { registryDirectory } from '../server/registry/index'; +import * as playwright from '../../..'; +import { registryDirectory } from '../../server/registry/index'; import { testDebug } from './log'; -import { outputDir } from '../tools/context'; +import { outputDir } from '../backend/context'; import { createExtensionBrowser } from './extensionContextFactory'; -import { connectToBrowser, connectToBrowserAcrossVersions } from '../client/connect'; -import { serverRegistry } from '../serverRegistry'; +import { connectToBrowser, connectToBrowserAcrossVersions } from '../../client/connect'; +import { serverRegistry } from '../../serverRegistry'; import type { FullConfig } from './config'; -import type { LaunchOptions, BrowserContextOptions, ConnectOptions } from '../client/types'; -import type { ClientInfo } from './sdk/server'; -import type { Playwright } from '../client/playwright'; +import type { LaunchOptions, BrowserContextOptions, ConnectOptions } from '../../client/types'; +import type { ClientInfo } from '../utils/mcp/server'; +import type { Playwright } from '../../client/playwright'; export async function createBrowser(config: FullConfig, clientInfo: ClientInfo): Promise { if (config.browser.remoteEndpoint) diff --git a/packages/playwright-core/src/mcp/cdpRelay.ts b/packages/playwright-core/src/tools/mcp/cdpRelay.ts similarity index 97% rename from packages/playwright-core/src/mcp/cdpRelay.ts rename to packages/playwright-core/src/tools/mcp/cdpRelay.ts index 1e78ed6d2b2ee..9a50b18aaa854 100644 --- a/packages/playwright-core/src/mcp/cdpRelay.ts +++ b/packages/playwright-core/src/tools/mcp/cdpRelay.ts @@ -26,18 +26,18 @@ import { spawn } from 'child_process'; import http from 'http'; import os from 'os'; -import { debug, ws, wsServer } from '../utilsBundle'; -import { registry } from '../server/registry/index'; -import { ManualPromise } from '../utils/isomorphic/manualPromise'; +import { debug, ws, wsServer } from '../../utilsBundle'; +import { registry } from '../../server/registry/index'; +import { ManualPromise } from '../../utils/isomorphic/manualPromise'; -import { addressToString } from './sdk/http'; +import { addressToString } from '../utils/mcp/http'; import { logUnhandledError } from './log'; import * as protocol from './protocol'; import type websocket from 'ws'; -import type { ClientInfo } from './sdk/server'; +import type { ClientInfo } from '../utils/mcp/server'; import type { ExtensionCommand, ExtensionEvents } from './protocol'; -import type { WebSocket, WebSocketServer } from '../utilsBundle'; +import type { WebSocket, WebSocketServer } from '../../utilsBundle'; const debugLogger = debug('pw:mcp:relay'); diff --git a/packages/playwright-core/src/mcp/exports.ts b/packages/playwright-core/src/tools/mcp/cli-stub.ts similarity index 53% rename from packages/playwright-core/src/mcp/exports.ts rename to packages/playwright-core/src/tools/mcp/cli-stub.ts index dd5244b9a0a00..36de64d4eb949 100644 --- a/packages/playwright-core/src/mcp/exports.ts +++ b/packages/playwright-core/src/tools/mcp/cli-stub.ts @@ -14,11 +14,15 @@ * limitations under the License. */ -export { createClientInfo } from '../cli/client/registry'; -export { logUnhandledError } from './log'; -export { setupExitWatchdog } from './watchdog'; -export { startCliDaemonServer } from '../cli/daemon/daemon'; -export * from './sdk/server'; -export * from './sdk/tool'; +import { program } from '../../utilsBundle'; +import { decorateMCPCommand, decorateMCPInstallBrowserCommand } from './program'; -export type { FullConfig } from './config'; +const packageJSON = require('../../../package.json'); +const p = program.version('Version ' + packageJSON.version).name('Playwright MCP'); +decorateMCPCommand(p); + +const installBrowserCommand = p.command('install-browser'); +installBrowserCommand.description('Install browser for Playwright MCP'); +decorateMCPInstallBrowserCommand(installBrowserCommand); + +void program.parseAsync(process.argv); diff --git a/packages/playwright-core/src/mcp/config.d.ts b/packages/playwright-core/src/tools/mcp/config.d.ts similarity index 99% rename from packages/playwright-core/src/mcp/config.d.ts rename to packages/playwright-core/src/tools/mcp/config.d.ts index 35c5ca61fb225..1c03f11dcf9ae 100644 --- a/packages/playwright-core/src/mcp/config.d.ts +++ b/packages/playwright-core/src/tools/mcp/config.d.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type * as playwright from '../..'; +import type * as playwright from '../../..'; export type ToolCapability = 'config' | diff --git a/packages/playwright-core/src/mcp/config.ts b/packages/playwright-core/src/tools/mcp/config.ts similarity index 99% rename from packages/playwright-core/src/mcp/config.ts rename to packages/playwright-core/src/tools/mcp/config.ts index 577da213a873e..3f87148bd5d76 100644 --- a/packages/playwright-core/src/mcp/config.ts +++ b/packages/playwright-core/src/tools/mcp/config.ts @@ -17,12 +17,12 @@ import fs from 'fs'; import os from 'os'; -import { devices } from '../..'; -import { dotenv } from '../utilsBundle'; +import { devices } from '../../..'; +import { dotenv } from '../../utilsBundle'; import { configFromIniFile } from './configIni'; -import type * as playwright from '../..'; +import type * as playwright from '../../..'; import type { Config, ToolCapability } from './config.d'; async function fileExistsAsync(resolved: string) { diff --git a/packages/playwright-core/src/mcp/configIni.ts b/packages/playwright-core/src/tools/mcp/configIni.ts similarity index 97% rename from packages/playwright-core/src/mcp/configIni.ts rename to packages/playwright-core/src/tools/mcp/configIni.ts index 966e4ab617c9b..ceb0b7d95ec0c 100644 --- a/packages/playwright-core/src/mcp/configIni.ts +++ b/packages/playwright-core/src/tools/mcp/configIni.ts @@ -16,7 +16,7 @@ import fs from 'fs'; -import { ini } from '../utilsBundle'; +import { ini } from '../../utilsBundle'; import type { Config } from './config.d'; @@ -97,7 +97,7 @@ function setNestedValue(obj: Record, dotPath: string, value: any) { current[parts[parts.length - 1]] = value; } -// Regenerate this based on packages/playwright/src/mcp/config.d.ts when config changes. +// Regenerate this based on packages/playwright/src/tools/mcp/config.d.ts when config changes. type LonghandType = 'string' | 'number' | 'boolean' | 'string[]' | 'size'; diff --git a/packages/playwright-core/src/mcp/extensionContextFactory.ts b/packages/playwright-core/src/tools/mcp/extensionContextFactory.ts similarity index 86% rename from packages/playwright-core/src/mcp/extensionContextFactory.ts rename to packages/playwright-core/src/tools/mcp/extensionContextFactory.ts index a2715130ddc09..07865abc83d1c 100644 --- a/packages/playwright-core/src/mcp/extensionContextFactory.ts +++ b/packages/playwright-core/src/tools/mcp/extensionContextFactory.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import * as playwright from '../..'; -import { debug } from '../utilsBundle'; -import { createHttpServer, startHttpServer } from '../server/utils/network'; +import * as playwright from '../../..'; +import { debug } from '../../utilsBundle'; +import { createHttpServer, startHttpServer } from '../../server/utils/network'; import { CDPRelayServer } from './cdpRelay'; -import type { ClientInfo } from './sdk/server'; +import type { ClientInfo } from '../utils/mcp/server'; import type { FullConfig } from './config'; const debugLogger = debug('pw:mcp:relay'); diff --git a/packages/playwright-core/src/mcp/index.ts b/packages/playwright-core/src/tools/mcp/index.ts similarity index 85% rename from packages/playwright-core/src/mcp/index.ts rename to packages/playwright-core/src/tools/mcp/index.ts index 750bd8d315832..606861905a895 100644 --- a/packages/playwright-core/src/mcp/index.ts +++ b/packages/playwright-core/src/tools/mcp/index.ts @@ -15,17 +15,17 @@ */ import { resolveConfig } from './config'; -import { filteredTools } from '../tools/tools'; +import { filteredTools } from '../backend/tools'; import { createBrowser } from './browserFactory'; -import { BrowserServerBackend } from '../tools/browserServerBackend'; -import { createServer } from './sdk/server'; +import { BrowserBackend } from '../backend/browserBackend'; +import { createServer } from '../utils/mcp/server'; import type { BrowserContext } from 'playwright'; import type { Server } from '@modelcontextprotocol/sdk/server/index.js'; -import type { ClientInfo, ServerBackendFactory } from './sdk/server'; +import type { ClientInfo, ServerBackendFactory } from '../utils/mcp/server'; import type { Config } from './config.d'; -const packageJSON = require('../../package.json'); +const packageJSON = require('../../../package.json'); export async function createConnection(userConfig: Config = {}, contextGetter?: () => Promise): Promise { const config = await resolveConfig(userConfig); @@ -38,7 +38,7 @@ export async function createConnection(userConfig: Config = {}, contextGetter?: create: async (clientInfo: ClientInfo) => { const browser = contextGetter ? new SimpleBrowser(await contextGetter()) : await createBrowser(config, clientInfo); const context = config.browser.isolated ? await browser.newContext(config.browser.contextOptions) : browser.contexts()[0]; - return new BrowserServerBackend(config, context, tools); + return new BrowserBackend(config, context, tools); }, disposed: async () => { } }; diff --git a/packages/playwright-core/src/mcp/log.ts b/packages/playwright-core/src/tools/mcp/log.ts similarity index 94% rename from packages/playwright-core/src/mcp/log.ts rename to packages/playwright-core/src/tools/mcp/log.ts index e17e1f84e0c77..f06c311b31f6a 100644 --- a/packages/playwright-core/src/mcp/log.ts +++ b/packages/playwright-core/src/tools/mcp/log.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { debug } from '../utilsBundle'; +import { debug } from '../../utilsBundle'; const errorDebug = debug('pw:mcp:error'); diff --git a/packages/playwright-core/src/mcp/program.ts b/packages/playwright-core/src/tools/mcp/program.ts similarity index 93% rename from packages/playwright-core/src/mcp/program.ts rename to packages/playwright-core/src/tools/mcp/program.ts index 414db8e0a96aa..64a4b01b2339e 100644 --- a/packages/playwright-core/src/mcp/program.ts +++ b/packages/playwright-core/src/tools/mcp/program.ts @@ -14,21 +14,23 @@ * limitations under the License. */ -import { ProgramOption } from '../utilsBundle'; +import { ProgramOption } from '../../utilsBundle'; -import * as mcpServer from './sdk/server'; +import * as mcpServer from '../utils/mcp/server'; import { commaSeparatedList, dotenvFileLoader, enumParser, headerParser, numberParser, resolutionParser, resolveCLIConfig, semicolonSeparatedList } from './config'; import { setupExitWatchdog } from './watchdog'; import { createBrowser } from './browserFactory'; -import { BrowserServerBackend } from '../tools/browserServerBackend'; -import { filteredTools } from '../tools/tools'; +import { BrowserBackend } from '../backend/browserBackend'; +import { filteredTools } from '../backend/tools'; import { testDebug } from './log'; -import type { Command } from '../utilsBundle'; -import type { ClientInfo } from './sdk/server'; -import type * as playwright from '../..'; +import type { Command } from '../../utilsBundle'; +import type { ClientInfo } from '../utils/mcp/server'; +import type * as playwright from '../../..'; -export function decorateMCPCommand(command: Command, version: string) { +const version = require('../../../package.json').version; + +export function decorateMCPCommand(command: Command) { command .option('--allowed-hosts ', 'comma-separated list of hosts this server is allowed to serve from. Defaults to the host the server is bound to. Pass \'*\' to disable the host check.', commaSeparatedList) .option('--allowed-origins ', 'semicolon-separated list of TRUSTED origins to allow the browser to request. Default is to allow all.\nImportant: *does not* serve as a security boundary and *does not* affect redirects. ', semicolonSeparatedList) @@ -100,7 +102,7 @@ export function decorateMCPCommand(command: Command, version: string) { create: async (clientInfo: ClientInfo) => { const browser = await createBrowser(config, clientInfo); const browserContext = browser.contexts()[0]; - return new BrowserServerBackend(config, browserContext, tools); + return new BrowserBackend(config, browserContext, tools); }, disposed: async () => { } }; @@ -123,7 +125,7 @@ export function decorateMCPCommand(command: Command, version: string) { clientCount++; const browser = sharedBrowser || await createBrowser(config, clientInfo); const browserContext = config.browser.isolated ? await browser.newContext(config.browser.contextOptions) : browser.contexts()[0]; - return new BrowserServerBackend(config, browserContext, tools); + return new BrowserBackend(config, browserContext, tools); }, disposed: async backend => { clientCount--; @@ -132,7 +134,7 @@ export function decorateMCPCommand(command: Command, version: string) { testDebug('close browser'); sharedBrowser = undefined; - const browserContext = (backend as BrowserServerBackend).browserContext; + const browserContext = (backend as BrowserBackend).browserContext; await browserContext.close().catch(() => { }); await browserContext.browser()!.close().catch(() => { }); } @@ -141,7 +143,7 @@ export function decorateMCPCommand(command: Command, version: string) { }); } -export function decorateMCPInstallBrowserCommand(command: Command, version: string) { +export function decorateMCPInstallBrowserCommand(command: Command) { command .description('ensure browsers necessary for this version of Playwright are installed') .option('--with-deps', 'install system dependencies for browsers') diff --git a/packages/playwright-core/src/mcp/protocol.ts b/packages/playwright-core/src/tools/mcp/protocol.ts similarity index 100% rename from packages/playwright-core/src/mcp/protocol.ts rename to packages/playwright-core/src/tools/mcp/protocol.ts diff --git a/packages/playwright-core/src/mcp/watchdog.ts b/packages/playwright-core/src/tools/mcp/watchdog.ts similarity index 94% rename from packages/playwright-core/src/mcp/watchdog.ts rename to packages/playwright-core/src/tools/mcp/watchdog.ts index dc5860f8ae887..b9d13812309d9 100644 --- a/packages/playwright-core/src/mcp/watchdog.ts +++ b/packages/playwright-core/src/tools/mcp/watchdog.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { gracefullyCloseAll, gracefullyCloseSet } from '../utils'; +import { gracefullyCloseAll, gracefullyCloseSet } from '../../utils'; import { testDebug } from './log'; export function setupExitWatchdog() { diff --git a/packages/playwright-core/src/tools/utils/DEPS.list b/packages/playwright-core/src/tools/utils/DEPS.list new file mode 100644 index 0000000000000..844f0c46677d5 --- /dev/null +++ b/packages/playwright-core/src/tools/utils/DEPS.list @@ -0,0 +1,2 @@ +[socketConnection.ts] +"strict" diff --git a/packages/playwright-core/src/tools/utils/mcp/DEPS.list b/packages/playwright-core/src/tools/utils/mcp/DEPS.list new file mode 100644 index 0000000000000..9f67c501dd00a --- /dev/null +++ b/packages/playwright-core/src/tools/utils/mcp/DEPS.list @@ -0,0 +1,4 @@ +[*] +../../../mcpBundle.ts +../../../utilsBundle.ts +../../../server/utils/network.ts diff --git a/packages/playwright-core/src/mcp/sdk/http.ts b/packages/playwright-core/src/tools/utils/mcp/http.ts similarity index 96% rename from packages/playwright-core/src/mcp/sdk/http.ts rename to packages/playwright-core/src/tools/utils/mcp/http.ts index 8c94a8a594c7a..e124c330e2ba8 100644 --- a/packages/playwright-core/src/mcp/sdk/http.ts +++ b/packages/playwright-core/src/tools/utils/mcp/http.ts @@ -19,9 +19,9 @@ import net from 'net'; import http from 'http'; import crypto from 'crypto'; -import { debug } from '../../utilsBundle'; -import * as mcpBundle from '../../mcpBundle'; -import { createHttpServer, startHttpServer } from '../../server/utils/network'; +import { debug } from '../../../utilsBundle'; +import * as mcpBundle from '../../../mcpBundle'; +import { createHttpServer, startHttpServer } from '../../../server/utils/network'; import * as mcpServer from './server'; diff --git a/packages/playwright-core/src/mcp/sdk/server.ts b/packages/playwright-core/src/tools/utils/mcp/server.ts similarity index 98% rename from packages/playwright-core/src/mcp/sdk/server.ts rename to packages/playwright-core/src/tools/utils/mcp/server.ts index 97f34cbf04869..948de9ac1de8e 100644 --- a/packages/playwright-core/src/mcp/sdk/server.ts +++ b/packages/playwright-core/src/tools/utils/mcp/server.ts @@ -16,8 +16,8 @@ import { fileURLToPath } from 'url'; -import { debug } from '../../utilsBundle'; -import * as mcpBundle from '../../mcpBundle'; +import { debug } from '../../../utilsBundle'; +import * as mcpBundle from '../../../mcpBundle'; import { startMcpHttpServer } from './http'; import { toMcpTool } from './tool'; diff --git a/packages/playwright-core/src/mcp/sdk/tool.ts b/packages/playwright-core/src/tools/utils/mcp/tool.ts similarity index 96% rename from packages/playwright-core/src/mcp/sdk/tool.ts rename to packages/playwright-core/src/tools/utils/mcp/tool.ts index f260334d9b7bd..465ad8ddeaf72 100644 --- a/packages/playwright-core/src/mcp/sdk/tool.ts +++ b/packages/playwright-core/src/tools/utils/mcp/tool.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { z as zod } from '../../mcpBundle'; +import { z as zod } from '../../../mcpBundle'; import type { z } from 'zod'; import type * as mcpServer from './server'; diff --git a/packages/playwright-core/src/cli/client/socketConnection.ts b/packages/playwright-core/src/tools/utils/socketConnection.ts similarity index 100% rename from packages/playwright-core/src/cli/client/socketConnection.ts rename to packages/playwright-core/src/tools/utils/socketConnection.ts diff --git a/packages/playwright/src/mcp/test/browserBackend.ts b/packages/playwright/src/mcp/test/browserBackend.ts index 6d9ef5267855d..196360cf8e09c 100644 --- a/packages/playwright/src/mcp/test/browserBackend.ts +++ b/packages/playwright/src/mcp/test/browserBackend.ts @@ -16,7 +16,6 @@ import path from 'path'; import { createGuid } from 'playwright-core/lib/utils'; -import * as mcp from 'playwright-core/lib/mcp/exports'; import * as tools from 'playwright-core/lib/tools/exports'; import { stripAnsiEscapes } from '../../util'; @@ -26,21 +25,20 @@ import type { Page } from '../../../../playwright-core/src/client/page'; import type { TestInfoImpl } from '../../worker/testInfo'; export type BrowserMCPRequest = { - initialize?: { clientInfo: mcp.ClientInfo }, + initialize?: { clientInfo: tools.ClientInfo }, listTools?: {}, - callTool?: { name: string, arguments: mcp.CallToolRequest['params']['arguments'] }, + callTool?: { name: string, arguments: tools.CallToolRequest['params']['arguments'] }, close?: {}, }; export type BrowserMCPResponse = { initialize?: { pausedMessage: string }, - listTools?: mcp.Tool[], - callTool?: mcp.CallToolResult, + callTool?: tools.CallToolResult, close?: {}, }; export function createCustomMessageHandler(testInfo: TestInfoImpl, context: playwright.BrowserContext) { - let backend: tools.BrowserServerBackend | undefined; + let backend: tools.BrowserBackend | undefined; const config: tools.ContextConfig = { capabilities: ['testing'] }; const toolList = tools.filteredTools(config); @@ -48,18 +46,12 @@ export function createCustomMessageHandler(testInfo: TestInfoImpl, context: play if (data.initialize) { if (backend) throw new Error('MCP backend is already initialized'); - backend = new tools.BrowserServerBackend(config, context, toolList); + backend = new tools.BrowserBackend(config, context, toolList); await backend.initialize(data.initialize.clientInfo); const pausedMessage = await generatePausedMessage(testInfo, context); return { initialize: { pausedMessage } }; } - if (data.listTools) { - if (!backend) - throw new Error('MCP backend is not initialized'); - return { listTools: toolList.map(t => mcp.toMcpTool(t.schema)) }; - } - if (data.callTool) { if (!backend) throw new Error('MCP backend is not initialized'); @@ -125,7 +117,7 @@ export async function runDaemonForContext(testInfo: TestInfoImpl, context: playw const outputDir = path.join(testInfo.artifactsDir(), '.playwright-mcp'); const sessionName = `test-worker-${createGuid().slice(0, 6)}`; - await mcp.startCliDaemonServer(sessionName, context, { + await tools.startCliDaemonServer(sessionName, context, { outputMode: 'file', snapshot: { mode: 'full' }, outputDir, diff --git a/packages/playwright/src/mcp/test/testBackend.ts b/packages/playwright/src/mcp/test/testBackend.ts index 8339a0facbbbd..f371ee4ed50c3 100644 --- a/packages/playwright/src/mcp/test/testBackend.ts +++ b/packages/playwright/src/mcp/test/testBackend.ts @@ -17,7 +17,7 @@ import EventEmitter from 'events'; import { z as zod } from 'playwright-core/lib/mcpBundle'; -import * as mcp from 'playwright-core/lib/mcp/exports'; +import * as mcp from 'playwright-core/lib/tools/exports'; import { browserTools } from 'playwright-core/lib/tools/exports'; import { TestContext } from './testContext'; diff --git a/packages/playwright/src/mcp/test/testContext.ts b/packages/playwright/src/mcp/test/testContext.ts index 99f3423c558d4..a886e77d921f9 100644 --- a/packages/playwright/src/mcp/test/testContext.ts +++ b/packages/playwright/src/mcp/test/testContext.ts @@ -33,7 +33,7 @@ import { resolveConfigLocation } from '../../common/configLoader'; import type { TerminalScreen } from '../../reporters/base'; import type { FullResultStatus, RunTestsParams } from '../../runner/testRunner'; import type { ConfigLocation } from '../../common/config'; -import type { ClientInfo } from 'playwright-core/lib/mcp/exports'; +import type { ClientInfo } from 'playwright-core/lib/tools/exports'; import type { BrowserMCPRequest, BrowserMCPResponse } from './browserBackend'; export type SeedFile = { diff --git a/packages/playwright/src/mcp/test/testTool.ts b/packages/playwright/src/mcp/test/testTool.ts index 7da087fa21b26..cbffb5ab0754a 100644 --- a/packages/playwright/src/mcp/test/testTool.ts +++ b/packages/playwright/src/mcp/test/testTool.ts @@ -17,7 +17,7 @@ import type { z } from 'zod'; import type { TestContext } from './testContext.js'; import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; -import type { ToolSchema } from 'playwright-core/lib/mcp/exports'; +import type { ToolSchema } from 'playwright-core/lib/tools/exports.js'; export type TestTool = { schema: ToolSchema; diff --git a/packages/playwright/src/program.ts b/packages/playwright/src/program.ts index e885a235dd5bb..edeb679ed4d26 100644 --- a/packages/playwright/src/program.ts +++ b/packages/playwright/src/program.ts @@ -21,8 +21,8 @@ import path from 'path'; import { program } from 'playwright-core/lib/cli/program'; import { gracefullyProcessExitDoNotHang, startProfiling, stopProfiling } from 'playwright-core/lib/utils'; -import * as mcp from 'playwright-core/lib/mcp/exports'; -import { setupExitWatchdog } from 'playwright-core/lib/mcp/exports'; +import * as tools from 'playwright-core/lib/tools/exports'; +import { setupExitWatchdog } from 'playwright-core/lib/tools/exports'; import { builtInReporters, defaultReporter, defaultTimeout } from './common/config'; import { loadConfigFromFile, loadEmptyConfigForMergeReports, resolveConfigLocation } from './common/configLoader'; @@ -155,7 +155,7 @@ function addTestMCPServerCommand(program: Command) { command.option('--port ', 'port to listen on for SSE transport.'); command.action(async options => { setupExitWatchdog(); - const factory: mcp.ServerBackendFactory = { + const factory: tools.ServerBackendFactory = { name: 'Playwright Test Runner', nameInConfig: 'playwright-test-runner', version: packageJSON.version, @@ -164,7 +164,7 @@ function addTestMCPServerCommand(program: Command) { disposed: async () => { } }; // TODO: add all options from mcp.startHttpServer. - await mcp.start(factory, { port: options.port === undefined ? undefined : +options.port, host: options.host }); + await tools.start(factory, { port: options.port === undefined ? undefined : +options.port, host: options.host }); }); } diff --git a/tests/library/perform-task.spec.ts-cache.json b/tests/library/perform-task.spec.ts-cache.json deleted file mode 100644 index 2f884f4624567..0000000000000 --- a/tests/library/perform-task.spec.ts-cache.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "Enter x-secret-email into the email field": { - "actions": [ - { - "method": "fill", - "selector": "internal:role=textbox[name=\"Email Address\"i]", - "text": "x-secret-email", - "code": "await page.getByRole('textbox', { name: 'Email Address' }).fill('x-secret-email');" - } - ] - }, - "Fill out the form with the following details:\nName: John Smith\nAddress: 1045 La Avenida St, Mountain View, CA 94043\nEmail: john.smith@at-microsoft.com": { - "actions": [ - { - "method": "fill", - "selector": "internal:role=textbox[name=\"Full Name *\"i]", - "text": "John Smith", - "code": "await page.getByRole('textbox', { name: 'Full Name *' }).fill('John Smith');" - }, - { - "method": "fill", - "selector": "internal:role=textbox[name=\"Email Address *\"i]", - "text": "john.smith@at-microsoft.com", - "code": "await page.getByRole('textbox', { name: 'Email Address *' }).fill('john.smith@at-microsoft.com');" - }, - { - "method": "fill", - "selector": "internal:role=textbox[name=\"Street Address *\"i]", - "text": "1045 La Avenida St", - "code": "await page.getByRole('textbox', { name: 'Street Address *' }).fill('1045 La Avenida St');" - }, - { - "method": "fill", - "selector": "internal:role=textbox[name=\"City *\"i]", - "text": "Mountain View", - "code": "await page.getByRole('textbox', { name: 'City *' }).fill('Mountain View');" - }, - { - "method": "fill", - "selector": "internal:role=textbox[name=\"State/Province *\"i]", - "text": "CA", - "code": "await page.getByRole('textbox', { name: 'State/Province *' }).fill('CA');" - }, - { - "method": "fill", - "selector": "internal:role=textbox[name=\"ZIP/Postal Code *\"i]", - "text": "94043", - "code": "await page.getByRole('textbox', { name: 'ZIP/Postal Code *' }).fill('94043');" - } - ] - }, - "- Enter \"bogus\" into the email field\n - Check that the value is in fact \"bogus\"\n - Check that the error message is displayed": { - "actions": [ - { - "method": "fill", - "selector": "internal:role=textbox[name=\"Email Address\"i]", - "text": "bogus", - "code": "await page.getByRole('textbox', { name: 'Email Address' }).fill('bogus');" - } - ] - }, - "click the Fox button": { - "actions": [ - { - "method": "click", - "selector": "internal:role=button[name=\"Fox\"i]", - "code": "await page.getByRole('button', { name: 'Fox' }).click();" - } - ] - }, - "click the Fox button again": { - "actions": [ - { - "method": "click", - "selector": "internal:role=button[name=\"Fox\"i]", - "code": "await page.getByRole('button', { name: 'Fox' }).click();" - } - ] - } -} \ No newline at end of file diff --git a/tests/mcp/cli-fixtures.ts b/tests/mcp/cli-fixtures.ts index 300fe379590b0..51ed349802e6f 100644 --- a/tests/mcp/cli-fixtures.ts +++ b/tests/mcp/cli-fixtures.ts @@ -75,7 +75,7 @@ async function runCli(childProcess: CommonFixtures['childProcess'], args: string return await test.step(stepTitle, async () => { const testInfo = test.info(); const cli = childProcess({ - command: [process.execPath, require.resolve('../../packages/playwright-core/lib/cli/client/cli.js'), ...args], + command: [process.execPath, require.resolve('../../packages/playwright-core/lib/tools/cli-client/cli.js'), ...args], cwd: cliOptions.cwd ?? testInfo.outputPath(), env: { ...process.env, diff --git a/tests/mcp/config.spec.ts b/tests/mcp/config.spec.ts index f0b6b4388cf82..273d81b605ae4 100644 --- a/tests/mcp/config.spec.ts +++ b/tests/mcp/config.spec.ts @@ -17,8 +17,8 @@ import fs from 'node:fs'; import { test, expect, parseResponse } from './fixtures'; -import { resolveCLIConfig } from '../../packages/playwright-core/lib/mcp/config'; -import type { Config } from '../../packages/playwright-core/src/mcp/config.d'; +import { resolveCLIConfig } from '../../packages/playwright-core/lib/tools/mcp/config'; +import type { Config } from '../../packages/playwright-core/src/tools/mcp/config.d'; test('config user data dir', async ({ startClient, server }, testInfo) => { server.setContent('/', ` diff --git a/tests/mcp/fixtures.ts b/tests/mcp/fixtures.ts index 06d8b06b163d9..728bba77f0862 100644 --- a/tests/mcp/fixtures.ts +++ b/tests/mcp/fixtures.ts @@ -24,11 +24,11 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { ListRootsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; import { TestServer } from '../config/testserver'; import { serverFixtures } from '../config/serverFixtures'; -import { parseResponse } from '../../packages/playwright-core/lib/tools/response'; +import { parseResponse } from '../../packages/playwright-core/lib/tools/backend/response'; import { commonFixtures } from '../config/commonFixtures'; import type { CommonFixtures, CommonWorkerFixtures } from '../config/commonFixtures'; -import type { Config } from '../../packages/playwright-core/src/mcp/config.d'; +import type { Config } from '../../packages/playwright-core/src/tools/mcp/config.d'; import type { BrowserContext } from 'playwright'; import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; import type { Stream } from 'stream'; @@ -290,8 +290,8 @@ export const expect = baseExpect.extend({ }, }); -export const mcpServerPath = [path.join(__dirname, '../../packages/playwright-core/cli.js'), 'run-mcp-server']; -export const testMcpServerPath = [path.join(__dirname, '../../packages/playwright-test/cli.js'), 'run-test-mcp-server']; +export const mcpServerPath = [require.resolve('../../packages/playwright-core/lib/tools/mcp/cli-stub.js')]; +export const testMcpServerPath = [require.resolve('../../packages/playwright-test/cli.js'), 'run-test-mcp-server']; type Files = { [key: string]: string | Buffer }; diff --git a/tests/mcp/http.spec.ts b/tests/mcp/http.spec.ts index af82e2477651b..1d54a605327e8 100644 --- a/tests/mcp/http.spec.ts +++ b/tests/mcp/http.spec.ts @@ -23,7 +23,7 @@ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/ import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { test as baseTest, expect, mcpServerPath, formatLog } from './fixtures'; -import type { Config } from '../../packages/playwright-core/src/mcp/config.d'; +import type { Config } from '../../packages/playwright-core/src/tools/mcp/config.d'; import { ListRootsRequestSchema } from 'playwright-core/lib/mcpBundle'; const test = baseTest.extend<{ serverEndpoint: (options?: { args?: string[], noPort?: boolean }) => Promise<{ url: URL, stderr: () => string }> }>({ diff --git a/tests/mcp/library.spec.ts b/tests/mcp/library.spec.ts index 7265043a1083f..bc5dfc5cd0780 100644 --- a/tests/mcp/library.spec.ts +++ b/tests/mcp/library.spec.ts @@ -20,7 +20,7 @@ import { test, expect } from './fixtures'; test('library can be used from CommonJS', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright-mcp/issues/456' } }, async ({}, testInfo) => { const file = testInfo.outputPath('main.cjs'); await fs.writeFile(file, ` - import('playwright-core/lib/mcp/index') + import('playwright-core/lib/tools/exports') .then(playwrightMCP => playwrightMCP.createConnection()) .then(() => console.log('OK')); `); diff --git a/tests/mcp/profile-lock.spec.ts b/tests/mcp/profile-lock.spec.ts index ddb95e35c4bc6..d47a1adafdaaf 100644 --- a/tests/mcp/profile-lock.spec.ts +++ b/tests/mcp/profile-lock.spec.ts @@ -17,7 +17,7 @@ import { chromium } from 'playwright'; import { test, expect } from './fixtures'; -import { isProfileLocked } from '../../packages/playwright-core/lib/mcp/browserFactory'; +import { isProfileLocked } from '../../packages/playwright-core/lib/tools/mcp/browserFactory'; test('isProfileLocked returns false for empty directory', async ({ mcpBrowser }, testInfo) => { test.skip(!['chromium', 'chrome', 'msedge'].includes(mcpBrowser!), 'Chromium-only'); diff --git a/tests/mcp/semver.spec.ts b/tests/mcp/semver.spec.ts index 19becae5ecc15..e2df550c58053 100644 --- a/tests/mcp/semver.spec.ts +++ b/tests/mcp/semver.spec.ts @@ -15,7 +15,7 @@ */ import { test, expect } from '@playwright/test'; -import { compareSemver } from '../../packages/playwright-core/lib/cli/client/socketConnection'; +import { compareSemver } from '../../packages/playwright-core/lib/tools/utils/socketConnection'; test('compareSemver', () => { // Stable versions. diff --git a/tests/mcp/sse.spec.ts b/tests/mcp/sse.spec.ts index f9f4cbb4ab279..6c971c3510c81 100644 --- a/tests/mcp/sse.spec.ts +++ b/tests/mcp/sse.spec.ts @@ -21,7 +21,7 @@ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { test as baseTest, expect, mcpServerPath, formatLog } from './fixtures'; -import type { Config } from '../../packages/playwright-core/src/mcp/config.d'; +import type { Config } from '../../packages/playwright-core/src/tools/mcp/config.d'; const test = baseTest.extend<{ serverEndpoint: (options?: { args?: string[], noPort?: boolean }) => Promise<{ url: URL, stderr: () => string }> }>({ serverEndpoint: async ({ mcpHeadless }, use, testInfo) => { diff --git a/tsconfig.json b/tsconfig.json index 7a7eab952d09d..2b187e40997a8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ - @foo/* is for importing types only, - .../lib/* means require dependency */ - "@devtools/*": ["./packages/devtools/src/*"], + "@dashboard/*": ["./packages/dashboard/src/*"], "@html-reporter/*": ["./packages/html-reporter/src/*"], "@injected/*": ["./packages/injected/src/*"], "@isomorphic/*": ["./packages/playwright-core/src/utils/isomorphic/*"], diff --git a/utils/build/build.js b/utils/build/build.js index 7d4ff162e5930..5ce3f6bd36403 100644 --- a/utils/build/build.js +++ b/utils/build/build.js @@ -535,7 +535,7 @@ steps.push(new ProgramStep({ })); // Build/watch web packages. -for (const webPackage of ['html-reporter', 'recorder', 'trace-viewer', 'devtools']) { +for (const webPackage of ['html-reporter', 'recorder', 'trace-viewer', 'dashboard']) { steps.push(new ProgramStep({ command: 'npx', args: [ @@ -554,8 +554,8 @@ for (const webPackage of ['html-reporter', 'recorder', 'trace-viewer', 'devtools // Generate CLI help. onChanges.push({ inputs: [ - 'packages/playwright-core/src/cli/daemon/commands.ts', - 'packages/playwright-core/src/cli/daemon/helpGenerator.ts', + 'packages/playwright-core/src/tools/cli-daemon/commands.ts', + 'packages/playwright-core/src/tools/cli-daemon/helpGenerator.ts', 'utils/generate_cli_help.js', ], script: 'utils/generate_cli_help.js', @@ -654,7 +654,7 @@ copyFiles.push({ }); copyFiles.push({ - files: 'packages/playwright-core/src/devtools/*.{png,ico}', + files: 'packages/playwright-core/src/tools/dashboard/*.{png,ico}', from: 'packages/playwright-core/src', to: 'packages/playwright-core/lib', }); diff --git a/utils/generate_cli_help.js b/utils/generate_cli_help.js index 379957ccb5e1e..af15daa7758f6 100644 --- a/utils/generate_cli_help.js +++ b/utils/generate_cli_help.js @@ -19,7 +19,7 @@ const fs = require('fs') const path = require('path') -const { generateHelp, generateReadme, generateHelpJSON } = require('../packages/playwright-core/lib/cli/daemon/helpGenerator.js'); +const { generateHelp, generateReadme, generateHelpJSON } = require('../packages/playwright-core/lib/tools/cli-daemon/helpGenerator.js'); if (process.argv[2] === '--readme') { console.log(generateReadme()); @@ -31,6 +31,6 @@ if (process.argv[2] === '--print') { process.exit(0); } -const fileName = path.resolve(__dirname, '../packages/playwright-core/lib/cli/client/help.json'); +const fileName = path.resolve(__dirname, '../packages/playwright-core/lib/tools/cli-client/help.json'); console.log('Writing ', path.relative(process.cwd(), fileName)); fs.writeFileSync(fileName, JSON.stringify(generateHelpJSON(), null, 2));