@midscene/web 1.2.1-beta-20260109060244.0 → 1.2.1-beta-20260109075435.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,7 +17,7 @@ class BridgeClient {
17
17
  this.socket = io(this.endpoint, {
18
18
  reconnection: false,
19
19
  query: {
20
- version: "1.2.1-beta-20260109060244.0"
20
+ version: "1.2.1-beta-20260109075435.0"
21
21
  }
22
22
  });
23
23
  const timeout = setTimeout(()=>{
@@ -75,7 +75,7 @@ class BridgeServer {
75
75
  logMsg('one client connected');
76
76
  this.socket = socket;
77
77
  const clientVersion = socket.handshake.query.version;
78
- logMsg(`Bridge connected, cli-side version v1.2.1-beta-20260109060244.0, browser-side version v${clientVersion}`);
78
+ logMsg(`Bridge connected, cli-side version v1.2.1-beta-20260109075435.0, browser-side version v${clientVersion}`);
79
79
  socket.on(BridgeEvent.CallResponse, (params)=>{
80
80
  const id = params.id;
81
81
  const response = params.response;
@@ -100,7 +100,7 @@ class BridgeServer {
100
100
  setTimeout(()=>{
101
101
  this.onConnect?.();
102
102
  const payload = {
103
- version: "1.2.1-beta-20260109060244.0"
103
+ version: "1.2.1-beta-20260109075435.0"
104
104
  };
105
105
  socket.emit(BridgeEvent.Connected, payload);
106
106
  Promise.resolve().then(()=>{
@@ -46,7 +46,7 @@ class ExtensionBridgePageBrowserSide extends page {
46
46
  }
47
47
  }, ()=>this.destroy());
48
48
  await this.bridgeClient.connect();
49
- this.onLogMessage(`Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v1.2.1-beta-20260109060244.0`, 'log');
49
+ this.onLogMessage(`Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v1.2.1-beta-20260109075435.0`, 'log');
50
50
  }
51
51
  async connect() {
52
52
  return await this.setupBridgeClient();
@@ -7,7 +7,7 @@ class WebMCPServer extends BaseMCPServer {
7
7
  constructor(toolsManager){
8
8
  super({
9
9
  name: '@midscene/web-bridge-mcp',
10
- version: "1.2.1-beta-20260109060244.0",
10
+ version: "1.2.1-beta-20260109075435.0",
11
11
  description: 'Control the browser using natural language commands'
12
12
  }, toolsManager);
13
13
  }
@@ -69,30 +69,7 @@ class WebMidsceneTools extends BaseMidsceneTools {
69
69
  name: 'web_disconnect',
70
70
  description: 'Disconnect from current web page and release browser resources',
71
71
  schema: {},
72
- handler: async ()=>{
73
- if (!this.agent) return {
74
- content: [
75
- {
76
- type: 'text',
77
- text: 'No active connection to disconnect'
78
- }
79
- ]
80
- };
81
- try {
82
- await this.agent.destroy?.();
83
- } catch (error) {
84
- console.debug('Failed to destroy agent during disconnect:', error);
85
- }
86
- this.agent = void 0;
87
- return {
88
- content: [
89
- {
90
- type: 'text',
91
- text: 'Disconnected from web page'
92
- }
93
- ]
94
- };
95
- }
72
+ handler: this.createDisconnectHandler('web page')
96
73
  }
97
74
  ];
98
75
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-tools.mjs","sources":["../../src/mcp-tools.ts"],"sourcesContent":["import { z } from '@midscene/core';\nimport { BaseMidsceneTools, type ToolDefinition } from '@midscene/shared/mcp';\nimport { AgentOverChromeBridge } from './bridge-mode';\nimport { StaticPage } from './static';\n\n/**\n * Tools manager for Web bridge-mode MCP\n */\nexport class WebMidsceneTools extends BaseMidsceneTools<AgentOverChromeBridge> {\n protected createTemporaryDevice() {\n // Use require to avoid type incompatibility with DeviceAction vs ActionSpaceItem\n // StaticPage.actionSpace() returns DeviceAction[] which is compatible at runtime\n return new StaticPage({\n screenshotBase64: '',\n size: { width: 1920, height: 1080 },\n });\n }\n\n protected async ensureAgent(\n openNewTabWithUrl?: string,\n ): Promise<AgentOverChromeBridge> {\n // Re-init if URL provided\n if (this.agent && openNewTabWithUrl) {\n try {\n await this.agent?.destroy?.();\n } catch (error) {\n console.debug('Failed to destroy agent during re-init:', error);\n }\n this.agent = undefined;\n }\n\n if (this.agent) return this.agent;\n\n // Bridge mode requires a URL to connect to browser\n if (!openNewTabWithUrl) {\n throw new Error(\n 'Bridge mode requires a URL. Use web_connect tool to connect to a page first.',\n );\n }\n\n this.agent = await this.initBridgeModeAgent(openNewTabWithUrl);\n\n return this.agent;\n }\n\n private async initBridgeModeAgent(\n url?: string,\n ): Promise<AgentOverChromeBridge> {\n const agent = new AgentOverChromeBridge({ closeConflictServer: true });\n\n if (!url) {\n await agent.connectCurrentTab();\n } else {\n await agent.connectNewTabWithUrl(url);\n }\n\n return agent;\n }\n\n protected preparePlatformTools(): ToolDefinition[] {\n return [\n {\n name: 'web_connect',\n description: 'Connect to web page by opening new tab with URL',\n schema: {\n url: z.string().url().describe('URL to connect to'),\n },\n handler: async (args) => {\n const { url } = args as { url: string };\n const agent = await this.ensureAgent(url);\n const screenshot = await agent.page?.screenshotBase64();\n if (!screenshot) {\n return {\n content: [\n {\n type: 'text',\n text: `Connected to: ${url}`,\n },\n ],\n };\n }\n\n return {\n content: [\n {\n type: 'text',\n text: `Connected to: ${url}`,\n },\n ...this.buildScreenshotContent(screenshot),\n ],\n };\n },\n },\n {\n name: 'web_disconnect',\n description:\n 'Disconnect from current web page and release browser resources',\n schema: {},\n handler: async () => {\n if (!this.agent) {\n return {\n content: [\n {\n type: 'text',\n text: 'No active connection to disconnect',\n },\n ],\n };\n }\n\n try {\n await this.agent.destroy?.();\n } catch (error) {\n console.debug('Failed to destroy agent during disconnect:', error);\n }\n this.agent = undefined;\n\n return {\n content: [\n {\n type: 'text',\n text: 'Disconnected from web page',\n },\n ],\n };\n },\n },\n ];\n }\n}\n"],"names":["WebMidsceneTools","BaseMidsceneTools","StaticPage","openNewTabWithUrl","error","console","undefined","Error","url","agent","AgentOverChromeBridge","z","args","screenshot"],"mappings":";;;;AAQO,MAAMA,yBAAyBC;IAC1B,wBAAwB;QAGhC,OAAO,IAAIC,WAAW;YACpB,kBAAkB;YAClB,MAAM;gBAAE,OAAO;gBAAM,QAAQ;YAAK;QACpC;IACF;IAEA,MAAgB,YACdC,iBAA0B,EACM;QAEhC,IAAI,IAAI,CAAC,KAAK,IAAIA,mBAAmB;YACnC,IAAI;gBACF,MAAM,IAAI,CAAC,KAAK,EAAE;YACpB,EAAE,OAAOC,OAAO;gBACdC,QAAQ,KAAK,CAAC,2CAA2CD;YAC3D;YACA,IAAI,CAAC,KAAK,GAAGE;QACf;QAEA,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,IAAI,CAAC,KAAK;QAGjC,IAAI,CAACH,mBACH,MAAM,IAAII,MACR;QAIJ,IAAI,CAAC,KAAK,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAACJ;QAE5C,OAAO,IAAI,CAAC,KAAK;IACnB;IAEA,MAAc,oBACZK,GAAY,EACoB;QAChC,MAAMC,QAAQ,IAAIC,sBAAsB;YAAE,qBAAqB;QAAK;QAEpE,IAAKF,KAGH,MAAMC,MAAM,oBAAoB,CAACD;aAFjC,MAAMC,MAAM,iBAAiB;QAK/B,OAAOA;IACT;IAEU,uBAAyC;QACjD,OAAO;YACL;gBACE,MAAM;gBACN,aAAa;gBACb,QAAQ;oBACN,KAAKE,EAAE,MAAM,GAAG,GAAG,GAAG,QAAQ,CAAC;gBACjC;gBACA,SAAS,OAAOC;oBACd,MAAM,EAAEJ,GAAG,EAAE,GAAGI;oBAChB,MAAMH,QAAQ,MAAM,IAAI,CAAC,WAAW,CAACD;oBACrC,MAAMK,aAAa,MAAMJ,MAAM,IAAI,EAAE;oBACrC,IAAI,CAACI,YACH,OAAO;wBACL,SAAS;4BACP;gCACE,MAAM;gCACN,MAAM,CAAC,cAAc,EAAEL,KAAK;4BAC9B;yBACD;oBACH;oBAGF,OAAO;wBACL,SAAS;4BACP;gCACE,MAAM;gCACN,MAAM,CAAC,cAAc,EAAEA,KAAK;4BAC9B;+BACG,IAAI,CAAC,sBAAsB,CAACK;yBAChC;oBACH;gBACF;YACF;YACA;gBACE,MAAM;gBACN,aACE;gBACF,QAAQ,CAAC;gBACT,SAAS;oBACP,IAAI,CAAC,IAAI,CAAC,KAAK,EACb,OAAO;wBACL,SAAS;4BACP;gCACE,MAAM;gCACN,MAAM;4BACR;yBACD;oBACH;oBAGF,IAAI;wBACF,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO;oBAC1B,EAAE,OAAOT,OAAO;wBACdC,QAAQ,KAAK,CAAC,8CAA8CD;oBAC9D;oBACA,IAAI,CAAC,KAAK,GAAGE;oBAEb,OAAO;wBACL,SAAS;4BACP;gCACE,MAAM;gCACN,MAAM;4BACR;yBACD;oBACH;gBACF;YACF;SACD;IACH;AACF"}
1
+ {"version":3,"file":"mcp-tools.mjs","sources":["../../src/mcp-tools.ts"],"sourcesContent":["import { z } from '@midscene/core';\nimport { BaseMidsceneTools, type ToolDefinition } from '@midscene/shared/mcp';\nimport { AgentOverChromeBridge } from './bridge-mode';\nimport { StaticPage } from './static';\n\n/**\n * Tools manager for Web bridge-mode MCP\n */\nexport class WebMidsceneTools extends BaseMidsceneTools<AgentOverChromeBridge> {\n protected createTemporaryDevice() {\n // Use require to avoid type incompatibility with DeviceAction vs ActionSpaceItem\n // StaticPage.actionSpace() returns DeviceAction[] which is compatible at runtime\n return new StaticPage({\n screenshotBase64: '',\n size: { width: 1920, height: 1080 },\n });\n }\n\n protected async ensureAgent(\n openNewTabWithUrl?: string,\n ): Promise<AgentOverChromeBridge> {\n // Re-init if URL provided\n if (this.agent && openNewTabWithUrl) {\n try {\n await this.agent?.destroy?.();\n } catch (error) {\n console.debug('Failed to destroy agent during re-init:', error);\n }\n this.agent = undefined;\n }\n\n if (this.agent) return this.agent;\n\n // Bridge mode requires a URL to connect to browser\n if (!openNewTabWithUrl) {\n throw new Error(\n 'Bridge mode requires a URL. Use web_connect tool to connect to a page first.',\n );\n }\n\n this.agent = await this.initBridgeModeAgent(openNewTabWithUrl);\n\n return this.agent;\n }\n\n private async initBridgeModeAgent(\n url?: string,\n ): Promise<AgentOverChromeBridge> {\n const agent = new AgentOverChromeBridge({ closeConflictServer: true });\n\n if (!url) {\n await agent.connectCurrentTab();\n } else {\n await agent.connectNewTabWithUrl(url);\n }\n\n return agent;\n }\n\n protected preparePlatformTools(): ToolDefinition[] {\n return [\n {\n name: 'web_connect',\n description: 'Connect to web page by opening new tab with URL',\n schema: {\n url: z.string().url().describe('URL to connect to'),\n },\n handler: async (args) => {\n const { url } = args as { url: string };\n const agent = await this.ensureAgent(url);\n const screenshot = await agent.page?.screenshotBase64();\n if (!screenshot) {\n return {\n content: [\n {\n type: 'text',\n text: `Connected to: ${url}`,\n },\n ],\n };\n }\n\n return {\n content: [\n {\n type: 'text',\n text: `Connected to: ${url}`,\n },\n ...this.buildScreenshotContent(screenshot),\n ],\n };\n },\n },\n {\n name: 'web_disconnect',\n description:\n 'Disconnect from current web page and release browser resources',\n schema: {},\n handler: this.createDisconnectHandler('web page'),\n },\n ];\n }\n}\n"],"names":["WebMidsceneTools","BaseMidsceneTools","StaticPage","openNewTabWithUrl","error","console","undefined","Error","url","agent","AgentOverChromeBridge","z","args","screenshot"],"mappings":";;;;AAQO,MAAMA,yBAAyBC;IAC1B,wBAAwB;QAGhC,OAAO,IAAIC,WAAW;YACpB,kBAAkB;YAClB,MAAM;gBAAE,OAAO;gBAAM,QAAQ;YAAK;QACpC;IACF;IAEA,MAAgB,YACdC,iBAA0B,EACM;QAEhC,IAAI,IAAI,CAAC,KAAK,IAAIA,mBAAmB;YACnC,IAAI;gBACF,MAAM,IAAI,CAAC,KAAK,EAAE;YACpB,EAAE,OAAOC,OAAO;gBACdC,QAAQ,KAAK,CAAC,2CAA2CD;YAC3D;YACA,IAAI,CAAC,KAAK,GAAGE;QACf;QAEA,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,IAAI,CAAC,KAAK;QAGjC,IAAI,CAACH,mBACH,MAAM,IAAII,MACR;QAIJ,IAAI,CAAC,KAAK,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAACJ;QAE5C,OAAO,IAAI,CAAC,KAAK;IACnB;IAEA,MAAc,oBACZK,GAAY,EACoB;QAChC,MAAMC,QAAQ,IAAIC,sBAAsB;YAAE,qBAAqB;QAAK;QAEpE,IAAKF,KAGH,MAAMC,MAAM,oBAAoB,CAACD;aAFjC,MAAMC,MAAM,iBAAiB;QAK/B,OAAOA;IACT;IAEU,uBAAyC;QACjD,OAAO;YACL;gBACE,MAAM;gBACN,aAAa;gBACb,QAAQ;oBACN,KAAKE,EAAE,MAAM,GAAG,GAAG,GAAG,QAAQ,CAAC;gBACjC;gBACA,SAAS,OAAOC;oBACd,MAAM,EAAEJ,GAAG,EAAE,GAAGI;oBAChB,MAAMH,QAAQ,MAAM,IAAI,CAAC,WAAW,CAACD;oBACrC,MAAMK,aAAa,MAAMJ,MAAM,IAAI,EAAE;oBACrC,IAAI,CAACI,YACH,OAAO;wBACL,SAAS;4BACP;gCACE,MAAM;gCACN,MAAM,CAAC,cAAc,EAAEL,KAAK;4BAC9B;yBACD;oBACH;oBAGF,OAAO;wBACL,SAAS;4BACP;gCACE,MAAM;gCACN,MAAM,CAAC,cAAc,EAAEA,KAAK;4BAC9B;+BACG,IAAI,CAAC,sBAAsB,CAACK;yBAChC;oBACH;gBACF;YACF;YACA;gBACE,MAAM;gBACN,aACE;gBACF,QAAQ,CAAC;gBACT,SAAS,IAAI,CAAC,uBAAuB,CAAC;YACxC;SACD;IACH;AACF"}
@@ -32,6 +32,38 @@ function validateChromeArgs(args, baseArgs) {
32
32
  if (dangerousArgs.length > 0) console.warn(`Warning: Dangerous Chrome arguments detected: ${dangerousArgs.join(', ')}.\nThese arguments may reduce browser security. Use only in controlled testing environments.`);
33
33
  }
34
34
  const launcherDebug = getDebug('puppeteer:launcher');
35
+ function buildChromeArgs(options) {
36
+ const isWindows = 'win32' === process.platform;
37
+ const sandboxArgs = isWindows ? [] : [
38
+ '--no-sandbox',
39
+ '--disable-setuid-sandbox'
40
+ ];
41
+ const featureArgs = [
42
+ '--disable-features=HttpsFirstBalancedModeAutoEnable',
43
+ '--disable-features=PasswordLeakDetection',
44
+ '--disable-save-password-bubble'
45
+ ];
46
+ const userAgentArg = options?.userAgent ? [
47
+ `--user-agent="${options.userAgent}"`
48
+ ] : [];
49
+ const windowSizeArg = options?.windowSize ? [
50
+ `--window-size=${options.windowSize.width},${options.windowSize.height}`
51
+ ] : [];
52
+ const baseArgs = [
53
+ ...sandboxArgs,
54
+ ...featureArgs,
55
+ ...userAgentArg,
56
+ ...windowSizeArg
57
+ ];
58
+ if (options?.chromeArgs?.length) {
59
+ validateChromeArgs(options.chromeArgs, baseArgs);
60
+ return [
61
+ ...baseArgs,
62
+ ...options.chromeArgs
63
+ ];
64
+ }
65
+ return baseArgs;
66
+ }
35
67
  async function launchPuppeteerPage(target, preference, browser) {
36
68
  assert(target.url, 'url is required');
37
69
  const freeFn = [];
@@ -60,30 +92,16 @@ async function launchPuppeteerPage(target, preference, browser) {
60
92
  deviceScaleFactor: dpr
61
93
  };
62
94
  const headed = preference?.headed || preference?.keepWindow;
63
- const windowSizeArg = `--window-size=${width},${height + (headed ? 100 : 0)}`;
64
95
  const defaultViewportConfig = headed ? null : viewportConfig;
65
96
  if (headed && '1' === process.env.CI) console.warn('you are probably running headed mode in CI, this will usually fail.');
66
- const isWindows = 'win32' === process.platform;
67
- const baseArgs = [
68
- ...isWindows ? [] : [
69
- '--no-sandbox',
70
- '--disable-setuid-sandbox'
71
- ],
72
- '--disable-features=HttpsFirstBalancedModeAutoEnable',
73
- '--disable-features=PasswordLeakDetection',
74
- '--disable-save-password-bubble',
75
- `--user-agent="${ua}"`,
76
- windowSizeArg
77
- ];
78
- let args = baseArgs;
79
- if (target.chromeArgs && target.chromeArgs.length > 0) {
80
- validateChromeArgs(target.chromeArgs, baseArgs);
81
- args = [
82
- ...baseArgs,
83
- ...target.chromeArgs
84
- ];
85
- launcherDebug('Merging custom Chrome arguments', target.chromeArgs, 'Final args', args);
86
- }
97
+ const args = buildChromeArgs({
98
+ userAgent: ua,
99
+ windowSize: {
100
+ width,
101
+ height: height + (headed ? 100 : 0)
102
+ },
103
+ chromeArgs: target.chromeArgs
104
+ });
87
105
  launcherDebug('launching browser with viewport, headed', headed, 'viewport', viewportConfig, 'args', args, 'preference', preference);
88
106
  let browserInstance = browser;
89
107
  if (!browserInstance) {
@@ -96,7 +114,7 @@ async function launchPuppeteerPage(target, preference, browser) {
96
114
  freeFn.push({
97
115
  name: 'puppeteer_browser',
98
116
  fn: ()=>{
99
- if (!preference?.keepWindow) if (isWindows) setTimeout(()=>{
117
+ if (!preference?.keepWindow) if ('win32' === process.platform) setTimeout(()=>{
100
118
  browserInstance?.close();
101
119
  }, 800);
102
120
  else browserInstance?.close();
@@ -153,6 +171,6 @@ async function puppeteerAgentForTarget(target, preference, browser) {
153
171
  freeFn
154
172
  };
155
173
  }
156
- export { defaultUA, defaultViewportHeight, defaultViewportScale, defaultViewportWidth, defaultWaitForNetworkIdleTimeout, launchPuppeteerPage, puppeteerAgentForTarget, resolveAiActionContext };
174
+ export { buildChromeArgs, defaultUA, defaultViewportHeight, defaultViewportScale, defaultViewportWidth, defaultWaitForNetworkIdleTimeout, launchPuppeteerPage, puppeteerAgentForTarget, resolveAiActionContext };
157
175
 
158
176
  //# sourceMappingURL=agent-launcher.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"puppeteer/agent-launcher.mjs","sources":["../../../src/puppeteer/agent-launcher.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\n\nimport { PuppeteerAgent } from '@/puppeteer/index';\nimport type { AgentOpt, Cache, MidsceneYamlScriptWebEnv } from '@midscene/core';\nimport { DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT } from '@midscene/shared/constants';\nimport puppeteer, { type Browser } from 'puppeteer';\n\nexport const defaultUA =\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36';\nexport const defaultViewportWidth = 1440;\nexport const defaultViewportHeight = 768;\nexport const defaultViewportScale = process.platform === 'darwin' ? 2 : 1;\nexport const defaultWaitForNetworkIdleTimeout =\n DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT;\n\nexport function resolveAiActionContext(\n target: MidsceneYamlScriptWebEnv,\n preference?: Partial<Pick<AgentOpt, 'aiActionContext' | 'aiActContext'>>,\n): AgentOpt['aiActionContext'] | undefined {\n // Prefer agent-level preference if provided; otherwise fall back to target-level context.\n // Priority: preference.aiActContext > preference.aiActionContext (deprecated) > target.aiActionContext\n const data =\n preference?.aiActContext ??\n preference?.aiActionContext ??\n target.aiActionContext;\n return data;\n}\n\n/**\n * Chrome arguments that may reduce browser security.\n * These should only be used in controlled testing environments.\n *\n * Security implications:\n * - `--no-sandbox`: Disables Chrome's sandbox security model\n * - `--disable-setuid-sandbox`: Disables setuid sandbox on Linux\n * - `--disable-web-security`: Allows cross-origin requests without CORS\n * - `--ignore-certificate-errors`: Ignores SSL/TLS certificate errors\n * - `--disable-features=IsolateOrigins`: Disables origin isolation\n * - `--disable-site-isolation-trials`: Disables site isolation\n * - `--allow-running-insecure-content`: Allows mixed HTTP/HTTPS content\n */\nconst DANGEROUS_ARGS = [\n '--no-sandbox',\n '--disable-setuid-sandbox',\n '--disable-web-security',\n '--ignore-certificate-errors',\n '--disable-features=IsolateOrigins',\n '--disable-site-isolation-trials',\n '--allow-running-insecure-content',\n] as const;\n\n/**\n * Validates Chrome launch arguments for security concerns.\n * Emits a warning if dangerous arguments are detected.\n *\n * This function filters out arguments that are already present in baseArgs\n * to avoid warning about platform-specific defaults (e.g., --no-sandbox on non-Windows).\n *\n * @param args - Chrome launch arguments to validate\n * @param baseArgs - Base Chrome arguments already configured\n *\n * @example\n * ```typescript\n * // Will show warning for --disable-web-security\n * validateChromeArgs(['--disable-web-security', '--headless'], ['--no-sandbox']);\n *\n * // Will NOT show warning for --no-sandbox (already in baseArgs)\n * validateChromeArgs(['--no-sandbox'], ['--no-sandbox', '--headless']);\n * ```\n */\nfunction validateChromeArgs(args: string[], baseArgs: string[]): void {\n // Filter out arguments that are already in baseArgs\n const newArgs = args.filter(\n (arg) =>\n !baseArgs.some((baseArg) => {\n // Check if arg starts with the same flag as baseArg (before '=' if present)\n const argFlag = arg.split('=')[0];\n const baseFlag = baseArg.split('=')[0];\n return argFlag === baseFlag;\n }),\n );\n\n const dangerousArgs = newArgs.filter((arg) =>\n DANGEROUS_ARGS.some((dangerous) => arg.startsWith(dangerous)),\n );\n\n if (dangerousArgs.length > 0) {\n console.warn(\n `Warning: Dangerous Chrome arguments detected: ${dangerousArgs.join(', ')}.\\nThese arguments may reduce browser security. Use only in controlled testing environments.`,\n );\n }\n}\n\ninterface FreeFn {\n name: string;\n fn: () => void;\n}\n\nconst launcherDebug = getDebug('puppeteer:launcher');\n\nexport async function launchPuppeteerPage(\n target: MidsceneYamlScriptWebEnv,\n preference?: {\n headed?: boolean;\n keepWindow?: boolean;\n },\n browser?: Browser,\n) {\n assert(target.url, 'url is required');\n const freeFn: FreeFn[] = [];\n\n // prepare the environment\n const ua = target.userAgent || defaultUA;\n let width = defaultViewportWidth;\n if (target.viewportWidth) {\n assert(\n typeof target.viewportWidth === 'number',\n 'viewportWidth must be a number',\n );\n width = Number.parseInt(target.viewportWidth as unknown as string, 10);\n assert(width > 0, `viewportWidth must be greater than 0, but got ${width}`);\n }\n let height = defaultViewportHeight;\n if (target.viewportHeight) {\n assert(\n typeof target.viewportHeight === 'number',\n 'viewportHeight must be a number',\n );\n height = Number.parseInt(target.viewportHeight as unknown as string, 10);\n assert(\n height > 0,\n `viewportHeight must be greater than 0, but got ${height}`,\n );\n }\n let dpr = defaultViewportScale;\n if (target.viewportScale) {\n assert(\n typeof target.viewportScale === 'number',\n 'viewportScale must be a number',\n );\n dpr = Number.parseInt(target.viewportScale as unknown as string, 10);\n assert(dpr > 0, `viewportScale must be greater than 0, but got ${dpr}`);\n }\n const viewportConfig = {\n width,\n height,\n deviceScaleFactor: dpr,\n };\n\n const headed = preference?.headed || preference?.keepWindow;\n const windowSizeArg = `--window-size=${width},${height + (headed ? 100 : 0)}`; // add 100px for the address bar in headed mode\n const defaultViewportConfig = headed ? null : viewportConfig;\n\n // launch the browser\n if (headed && process.env.CI === '1') {\n console.warn(\n 'you are probably running headed mode in CI, this will usually fail.',\n );\n }\n // do not use 'no-sandbox' on windows https://www.perplexity.ai/search/how-to-solve-this-with-nodejs-dMHpdCypRa..JA8TkQzbeQ\n const isWindows = process.platform === 'win32';\n\n const baseArgs = [\n ...(isWindows ? [] : ['--no-sandbox', '--disable-setuid-sandbox']),\n '--disable-features=HttpsFirstBalancedModeAutoEnable',\n '--disable-features=PasswordLeakDetection',\n '--disable-save-password-bubble',\n `--user-agent=\"${ua}\"`,\n windowSizeArg,\n ];\n\n // Merge custom Chrome arguments\n let args = baseArgs;\n if (target.chromeArgs && target.chromeArgs.length > 0) {\n validateChromeArgs(target.chromeArgs, baseArgs);\n\n // Custom args come after base args, allowing them to override defaults\n args = [...baseArgs, ...target.chromeArgs];\n launcherDebug(\n 'Merging custom Chrome arguments',\n target.chromeArgs,\n 'Final args',\n args,\n );\n }\n\n launcherDebug(\n 'launching browser with viewport, headed',\n headed,\n 'viewport',\n viewportConfig,\n 'args',\n args,\n 'preference',\n preference,\n );\n let browserInstance = browser;\n if (!browserInstance) {\n browserInstance = await puppeteer.launch({\n headless: !preference?.headed,\n defaultViewport: defaultViewportConfig,\n args,\n acceptInsecureCerts: target.acceptInsecureCerts,\n });\n freeFn.push({\n name: 'puppeteer_browser',\n fn: () => {\n if (!preference?.keepWindow) {\n if (isWindows) {\n setTimeout(() => {\n browserInstance?.close();\n }, 800);\n } else {\n browserInstance?.close();\n }\n }\n },\n });\n }\n const page = await browserInstance.newPage();\n\n if (target.cookie) {\n const cookieFileContent = readFileSync(target.cookie, 'utf-8');\n await browserInstance.setCookie(...JSON.parse(cookieFileContent));\n }\n\n if (ua) {\n await page.setUserAgent(ua);\n }\n\n if (viewportConfig) {\n await page.setViewport(viewportConfig);\n }\n\n const waitForNetworkIdleTimeout =\n typeof target.waitForNetworkIdle?.timeout === 'number'\n ? target.waitForNetworkIdle.timeout\n : defaultWaitForNetworkIdleTimeout;\n\n try {\n launcherDebug('goto', target.url);\n await page.goto(target.url);\n if (waitForNetworkIdleTimeout > 0) {\n launcherDebug('waitForNetworkIdle', waitForNetworkIdleTimeout);\n await page.waitForNetworkIdle({\n timeout: waitForNetworkIdleTimeout,\n });\n }\n } catch (e) {\n if (\n typeof target.waitForNetworkIdle?.continueOnNetworkIdleError ===\n 'boolean' &&\n !target.waitForNetworkIdle?.continueOnNetworkIdleError\n ) {\n const newError = new Error(`failed to wait for network idle: ${e}`, {\n cause: e,\n });\n throw newError;\n }\n const newMessage = `failed to wait for network idle after ${waitForNetworkIdleTimeout}ms, but the script will continue.`;\n console.warn(newMessage);\n }\n\n return { page, freeFn };\n}\n\nexport async function puppeteerAgentForTarget(\n target: MidsceneYamlScriptWebEnv,\n preference?: {\n headed?: boolean;\n keepWindow?: boolean;\n } & Partial<\n Pick<\n AgentOpt,\n | 'testId'\n | 'groupName'\n | 'groupDescription'\n | 'generateReport'\n | 'autoPrintReportMsg'\n | 'reportFileName'\n | 'replanningCycleLimit'\n | 'cache'\n | 'aiActionContext'\n >\n >,\n browser?: Browser,\n) {\n const { page, freeFn } = await launchPuppeteerPage(\n target,\n preference,\n browser,\n );\n const aiActContext = resolveAiActionContext(target, preference);\n\n const { aiActionContext, ...preferenceToUse } = preference ?? {};\n\n // prepare Midscene agent\n const agent = new PuppeteerAgent(page, {\n ...preferenceToUse,\n aiActContext,\n forceSameTabNavigation:\n typeof target.forceSameTabNavigation !== 'undefined'\n ? target.forceSameTabNavigation\n : true, // true for default in yaml script\n });\n\n freeFn.push({\n name: 'midscene_puppeteer_agent',\n fn: () => agent.destroy(),\n });\n\n return { agent, freeFn };\n}\n"],"names":["defaultUA","defaultViewportWidth","defaultViewportHeight","defaultViewportScale","process","defaultWaitForNetworkIdleTimeout","DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT","resolveAiActionContext","target","preference","data","DANGEROUS_ARGS","validateChromeArgs","args","baseArgs","newArgs","arg","baseArg","argFlag","baseFlag","dangerousArgs","dangerous","console","launcherDebug","getDebug","launchPuppeteerPage","browser","assert","freeFn","ua","width","Number","height","dpr","viewportConfig","headed","windowSizeArg","defaultViewportConfig","isWindows","browserInstance","puppeteer","setTimeout","page","cookieFileContent","readFileSync","JSON","waitForNetworkIdleTimeout","e","newError","Error","newMessage","puppeteerAgentForTarget","aiActContext","aiActionContext","preferenceToUse","agent","PuppeteerAgent"],"mappings":"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"}
1
+ {"version":3,"file":"puppeteer/agent-launcher.mjs","sources":["../../../src/puppeteer/agent-launcher.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\n\nimport { PuppeteerAgent } from '@/puppeteer/index';\nimport type { AgentOpt, Cache, MidsceneYamlScriptWebEnv } from '@midscene/core';\nimport { DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT } from '@midscene/shared/constants';\nimport puppeteer, { type Browser } from 'puppeteer';\n\nexport const defaultUA =\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36';\nexport const defaultViewportWidth = 1440;\nexport const defaultViewportHeight = 768;\nexport const defaultViewportScale = process.platform === 'darwin' ? 2 : 1;\nexport const defaultWaitForNetworkIdleTimeout =\n DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT;\n\nexport function resolveAiActionContext(\n target: MidsceneYamlScriptWebEnv,\n preference?: Partial<Pick<AgentOpt, 'aiActionContext' | 'aiActContext'>>,\n): AgentOpt['aiActionContext'] | undefined {\n // Prefer agent-level preference if provided; otherwise fall back to target-level context.\n // Priority: preference.aiActContext > preference.aiActionContext (deprecated) > target.aiActionContext\n const data =\n preference?.aiActContext ??\n preference?.aiActionContext ??\n target.aiActionContext;\n return data;\n}\n\n/**\n * Chrome arguments that may reduce browser security.\n * These should only be used in controlled testing environments.\n *\n * Security implications:\n * - `--no-sandbox`: Disables Chrome's sandbox security model\n * - `--disable-setuid-sandbox`: Disables setuid sandbox on Linux\n * - `--disable-web-security`: Allows cross-origin requests without CORS\n * - `--ignore-certificate-errors`: Ignores SSL/TLS certificate errors\n * - `--disable-features=IsolateOrigins`: Disables origin isolation\n * - `--disable-site-isolation-trials`: Disables site isolation\n * - `--allow-running-insecure-content`: Allows mixed HTTP/HTTPS content\n */\nconst DANGEROUS_ARGS = [\n '--no-sandbox',\n '--disable-setuid-sandbox',\n '--disable-web-security',\n '--ignore-certificate-errors',\n '--disable-features=IsolateOrigins',\n '--disable-site-isolation-trials',\n '--allow-running-insecure-content',\n] as const;\n\n/**\n * Validates Chrome launch arguments for security concerns.\n * Emits a warning if dangerous arguments are detected.\n *\n * This function filters out arguments that are already present in baseArgs\n * to avoid warning about platform-specific defaults (e.g., --no-sandbox on non-Windows).\n *\n * @param args - Chrome launch arguments to validate\n * @param baseArgs - Base Chrome arguments already configured\n *\n * @example\n * ```typescript\n * // Will show warning for --disable-web-security\n * validateChromeArgs(['--disable-web-security', '--headless'], ['--no-sandbox']);\n *\n * // Will NOT show warning for --no-sandbox (already in baseArgs)\n * validateChromeArgs(['--no-sandbox'], ['--no-sandbox', '--headless']);\n * ```\n */\nfunction validateChromeArgs(args: string[], baseArgs: string[]): void {\n // Filter out arguments that are already in baseArgs\n const newArgs = args.filter(\n (arg) =>\n !baseArgs.some((baseArg) => {\n // Check if arg starts with the same flag as baseArg (before '=' if present)\n const argFlag = arg.split('=')[0];\n const baseFlag = baseArg.split('=')[0];\n return argFlag === baseFlag;\n }),\n );\n\n const dangerousArgs = newArgs.filter((arg) =>\n DANGEROUS_ARGS.some((dangerous) => arg.startsWith(dangerous)),\n );\n\n if (dangerousArgs.length > 0) {\n console.warn(\n `Warning: Dangerous Chrome arguments detected: ${dangerousArgs.join(', ')}.\\nThese arguments may reduce browser security. Use only in controlled testing environments.`,\n );\n }\n}\n\ninterface FreeFn {\n name: string;\n fn: () => void;\n}\n\nconst launcherDebug = getDebug('puppeteer:launcher');\n\nexport interface BuildChromeArgsOptions {\n userAgent?: string;\n windowSize?: { width: number; height: number };\n chromeArgs?: string[];\n}\n\n/**\n * Builds Chrome launch arguments with sensible defaults.\n *\n * Platform-specific behavior:\n * - On non-Windows systems, automatically adds --no-sandbox and --disable-setuid-sandbox\n * for compatibility with containerized/CI environments\n *\n * @param options - Configuration options for Chrome arguments\n * @returns Array of Chrome launch arguments\n *\n * @example\n * ```typescript\n * // Basic usage\n * const args = buildChromeArgs();\n *\n * // With custom arguments\n * const args = buildChromeArgs({\n * chromeArgs: ['--disable-gpu', '--disable-dev-shm-usage'],\n * userAgent: 'CustomUA/1.0',\n * windowSize: { width: 1920, height: 1080 },\n * });\n * ```\n */\nexport function buildChromeArgs(options?: BuildChromeArgsOptions): string[] {\n const isWindows = process.platform === 'win32';\n\n const sandboxArgs = isWindows\n ? []\n : ['--no-sandbox', '--disable-setuid-sandbox'];\n const featureArgs = [\n '--disable-features=HttpsFirstBalancedModeAutoEnable',\n '--disable-features=PasswordLeakDetection',\n '--disable-save-password-bubble',\n ];\n const userAgentArg = options?.userAgent\n ? [`--user-agent=\"${options.userAgent}\"`]\n : [];\n const windowSizeArg = options?.windowSize\n ? [`--window-size=${options.windowSize.width},${options.windowSize.height}`]\n : [];\n\n const baseArgs = [\n ...sandboxArgs,\n ...featureArgs,\n ...userAgentArg,\n ...windowSizeArg,\n ];\n\n if (options?.chromeArgs?.length) {\n validateChromeArgs(options.chromeArgs, baseArgs);\n return [...baseArgs, ...options.chromeArgs];\n }\n\n return baseArgs;\n}\n\nexport async function launchPuppeteerPage(\n target: MidsceneYamlScriptWebEnv,\n preference?: {\n headed?: boolean;\n keepWindow?: boolean;\n },\n browser?: Browser,\n) {\n assert(target.url, 'url is required');\n const freeFn: FreeFn[] = [];\n\n // prepare the environment\n const ua = target.userAgent || defaultUA;\n let width = defaultViewportWidth;\n if (target.viewportWidth) {\n assert(\n typeof target.viewportWidth === 'number',\n 'viewportWidth must be a number',\n );\n width = Number.parseInt(target.viewportWidth as unknown as string, 10);\n assert(width > 0, `viewportWidth must be greater than 0, but got ${width}`);\n }\n let height = defaultViewportHeight;\n if (target.viewportHeight) {\n assert(\n typeof target.viewportHeight === 'number',\n 'viewportHeight must be a number',\n );\n height = Number.parseInt(target.viewportHeight as unknown as string, 10);\n assert(\n height > 0,\n `viewportHeight must be greater than 0, but got ${height}`,\n );\n }\n let dpr = defaultViewportScale;\n if (target.viewportScale) {\n assert(\n typeof target.viewportScale === 'number',\n 'viewportScale must be a number',\n );\n dpr = Number.parseInt(target.viewportScale as unknown as string, 10);\n assert(dpr > 0, `viewportScale must be greater than 0, but got ${dpr}`);\n }\n const viewportConfig = {\n width,\n height,\n deviceScaleFactor: dpr,\n };\n\n const headed = preference?.headed || preference?.keepWindow;\n const defaultViewportConfig = headed ? null : viewportConfig;\n\n // launch the browser\n if (headed && process.env.CI === '1') {\n console.warn(\n 'you are probably running headed mode in CI, this will usually fail.',\n );\n }\n\n // Build Chrome arguments using the shared helper\n const args = buildChromeArgs({\n userAgent: ua,\n windowSize: {\n width,\n height: height + (headed ? 100 : 0), // add 100px for the address bar in headed mode\n },\n chromeArgs: target.chromeArgs,\n });\n\n launcherDebug(\n 'launching browser with viewport, headed',\n headed,\n 'viewport',\n viewportConfig,\n 'args',\n args,\n 'preference',\n preference,\n );\n let browserInstance = browser;\n if (!browserInstance) {\n browserInstance = await puppeteer.launch({\n headless: !preference?.headed,\n defaultViewport: defaultViewportConfig,\n args,\n acceptInsecureCerts: target.acceptInsecureCerts,\n });\n freeFn.push({\n name: 'puppeteer_browser',\n fn: () => {\n if (!preference?.keepWindow) {\n if (process.platform === 'win32') {\n setTimeout(() => {\n browserInstance?.close();\n }, 800);\n } else {\n browserInstance?.close();\n }\n }\n },\n });\n }\n const page = await browserInstance.newPage();\n\n if (target.cookie) {\n const cookieFileContent = readFileSync(target.cookie, 'utf-8');\n await browserInstance.setCookie(...JSON.parse(cookieFileContent));\n }\n\n if (ua) {\n await page.setUserAgent(ua);\n }\n\n if (viewportConfig) {\n await page.setViewport(viewportConfig);\n }\n\n const waitForNetworkIdleTimeout =\n typeof target.waitForNetworkIdle?.timeout === 'number'\n ? target.waitForNetworkIdle.timeout\n : defaultWaitForNetworkIdleTimeout;\n\n try {\n launcherDebug('goto', target.url);\n await page.goto(target.url);\n if (waitForNetworkIdleTimeout > 0) {\n launcherDebug('waitForNetworkIdle', waitForNetworkIdleTimeout);\n await page.waitForNetworkIdle({\n timeout: waitForNetworkIdleTimeout,\n });\n }\n } catch (e) {\n if (\n typeof target.waitForNetworkIdle?.continueOnNetworkIdleError ===\n 'boolean' &&\n !target.waitForNetworkIdle?.continueOnNetworkIdleError\n ) {\n const newError = new Error(`failed to wait for network idle: ${e}`, {\n cause: e,\n });\n throw newError;\n }\n const newMessage = `failed to wait for network idle after ${waitForNetworkIdleTimeout}ms, but the script will continue.`;\n console.warn(newMessage);\n }\n\n return { page, freeFn };\n}\n\nexport async function puppeteerAgentForTarget(\n target: MidsceneYamlScriptWebEnv,\n preference?: {\n headed?: boolean;\n keepWindow?: boolean;\n } & Partial<\n Pick<\n AgentOpt,\n | 'testId'\n | 'groupName'\n | 'groupDescription'\n | 'generateReport'\n | 'autoPrintReportMsg'\n | 'reportFileName'\n | 'replanningCycleLimit'\n | 'cache'\n | 'aiActionContext'\n >\n >,\n browser?: Browser,\n) {\n const { page, freeFn } = await launchPuppeteerPage(\n target,\n preference,\n browser,\n );\n const aiActContext = resolveAiActionContext(target, preference);\n\n const { aiActionContext, ...preferenceToUse } = preference ?? {};\n\n // prepare Midscene agent\n const agent = new PuppeteerAgent(page, {\n ...preferenceToUse,\n aiActContext,\n forceSameTabNavigation:\n typeof target.forceSameTabNavigation !== 'undefined'\n ? target.forceSameTabNavigation\n : true, // true for default in yaml script\n });\n\n freeFn.push({\n name: 'midscene_puppeteer_agent',\n fn: () => agent.destroy(),\n });\n\n return { agent, freeFn };\n}\n"],"names":["defaultUA","defaultViewportWidth","defaultViewportHeight","defaultViewportScale","process","defaultWaitForNetworkIdleTimeout","DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT","resolveAiActionContext","target","preference","data","DANGEROUS_ARGS","validateChromeArgs","args","baseArgs","newArgs","arg","baseArg","argFlag","baseFlag","dangerousArgs","dangerous","console","launcherDebug","getDebug","buildChromeArgs","options","isWindows","sandboxArgs","featureArgs","userAgentArg","windowSizeArg","launchPuppeteerPage","browser","assert","freeFn","ua","width","Number","height","dpr","viewportConfig","headed","defaultViewportConfig","browserInstance","puppeteer","setTimeout","page","cookieFileContent","readFileSync","JSON","waitForNetworkIdleTimeout","e","newError","Error","newMessage","puppeteerAgentForTarget","aiActContext","aiActionContext","preferenceToUse","agent","PuppeteerAgent"],"mappings":"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"}
@@ -45,7 +45,7 @@ class BridgeClient {
45
45
  this.socket = (0, external_socket_io_client_namespaceObject.io)(this.endpoint, {
46
46
  reconnection: false,
47
47
  query: {
48
- version: "1.2.1-beta-20260109060244.0"
48
+ version: "1.2.1-beta-20260109075435.0"
49
49
  }
50
50
  });
51
51
  const timeout = setTimeout(()=>{
@@ -104,7 +104,7 @@ class BridgeServer {
104
104
  (0, shared_utils_namespaceObject.logMsg)('one client connected');
105
105
  this.socket = socket;
106
106
  const clientVersion = socket.handshake.query.version;
107
- (0, shared_utils_namespaceObject.logMsg)(`Bridge connected, cli-side version v1.2.1-beta-20260109060244.0, browser-side version v${clientVersion}`);
107
+ (0, shared_utils_namespaceObject.logMsg)(`Bridge connected, cli-side version v1.2.1-beta-20260109075435.0, browser-side version v${clientVersion}`);
108
108
  socket.on(external_common_js_namespaceObject.BridgeEvent.CallResponse, (params)=>{
109
109
  const id = params.id;
110
110
  const response = params.response;
@@ -129,7 +129,7 @@ class BridgeServer {
129
129
  setTimeout(()=>{
130
130
  this.onConnect?.();
131
131
  const payload = {
132
- version: "1.2.1-beta-20260109060244.0"
132
+ version: "1.2.1-beta-20260109075435.0"
133
133
  };
134
134
  socket.emit(external_common_js_namespaceObject.BridgeEvent.Connected, payload);
135
135
  Promise.resolve().then(()=>{
@@ -84,7 +84,7 @@ class ExtensionBridgePageBrowserSide extends page_js_default() {
84
84
  }
85
85
  }, ()=>this.destroy());
86
86
  await this.bridgeClient.connect();
87
- this.onLogMessage(`Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v1.2.1-beta-20260109060244.0`, 'log');
87
+ this.onLogMessage(`Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v1.2.1-beta-20260109075435.0`, 'log');
88
88
  }
89
89
  async connect() {
90
90
  return await this.setupBridgeClient();
@@ -37,7 +37,7 @@ class WebMCPServer extends mcp_namespaceObject.BaseMCPServer {
37
37
  constructor(toolsManager){
38
38
  super({
39
39
  name: '@midscene/web-bridge-mcp',
40
- version: "1.2.1-beta-20260109060244.0",
40
+ version: "1.2.1-beta-20260109075435.0",
41
41
  description: 'Control the browser using natural language commands'
42
42
  }, toolsManager);
43
43
  }
@@ -97,30 +97,7 @@ class WebMidsceneTools extends mcp_namespaceObject.BaseMidsceneTools {
97
97
  name: 'web_disconnect',
98
98
  description: 'Disconnect from current web page and release browser resources',
99
99
  schema: {},
100
- handler: async ()=>{
101
- if (!this.agent) return {
102
- content: [
103
- {
104
- type: 'text',
105
- text: 'No active connection to disconnect'
106
- }
107
- ]
108
- };
109
- try {
110
- await this.agent.destroy?.();
111
- } catch (error) {
112
- console.debug('Failed to destroy agent during disconnect:', error);
113
- }
114
- this.agent = void 0;
115
- return {
116
- content: [
117
- {
118
- type: 'text',
119
- text: 'Disconnected from web page'
120
- }
121
- ]
122
- };
123
- }
100
+ handler: this.createDisconnectHandler('web page')
124
101
  }
125
102
  ];
126
103
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-tools.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../src/mcp-tools.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { z } from '@midscene/core';\nimport { BaseMidsceneTools, type ToolDefinition } from '@midscene/shared/mcp';\nimport { AgentOverChromeBridge } from './bridge-mode';\nimport { StaticPage } from './static';\n\n/**\n * Tools manager for Web bridge-mode MCP\n */\nexport class WebMidsceneTools extends BaseMidsceneTools<AgentOverChromeBridge> {\n protected createTemporaryDevice() {\n // Use require to avoid type incompatibility with DeviceAction vs ActionSpaceItem\n // StaticPage.actionSpace() returns DeviceAction[] which is compatible at runtime\n return new StaticPage({\n screenshotBase64: '',\n size: { width: 1920, height: 1080 },\n });\n }\n\n protected async ensureAgent(\n openNewTabWithUrl?: string,\n ): Promise<AgentOverChromeBridge> {\n // Re-init if URL provided\n if (this.agent && openNewTabWithUrl) {\n try {\n await this.agent?.destroy?.();\n } catch (error) {\n console.debug('Failed to destroy agent during re-init:', error);\n }\n this.agent = undefined;\n }\n\n if (this.agent) return this.agent;\n\n // Bridge mode requires a URL to connect to browser\n if (!openNewTabWithUrl) {\n throw new Error(\n 'Bridge mode requires a URL. Use web_connect tool to connect to a page first.',\n );\n }\n\n this.agent = await this.initBridgeModeAgent(openNewTabWithUrl);\n\n return this.agent;\n }\n\n private async initBridgeModeAgent(\n url?: string,\n ): Promise<AgentOverChromeBridge> {\n const agent = new AgentOverChromeBridge({ closeConflictServer: true });\n\n if (!url) {\n await agent.connectCurrentTab();\n } else {\n await agent.connectNewTabWithUrl(url);\n }\n\n return agent;\n }\n\n protected preparePlatformTools(): ToolDefinition[] {\n return [\n {\n name: 'web_connect',\n description: 'Connect to web page by opening new tab with URL',\n schema: {\n url: z.string().url().describe('URL to connect to'),\n },\n handler: async (args) => {\n const { url } = args as { url: string };\n const agent = await this.ensureAgent(url);\n const screenshot = await agent.page?.screenshotBase64();\n if (!screenshot) {\n return {\n content: [\n {\n type: 'text',\n text: `Connected to: ${url}`,\n },\n ],\n };\n }\n\n return {\n content: [\n {\n type: 'text',\n text: `Connected to: ${url}`,\n },\n ...this.buildScreenshotContent(screenshot),\n ],\n };\n },\n },\n {\n name: 'web_disconnect',\n description:\n 'Disconnect from current web page and release browser resources',\n schema: {},\n handler: async () => {\n if (!this.agent) {\n return {\n content: [\n {\n type: 'text',\n text: 'No active connection to disconnect',\n },\n ],\n };\n }\n\n try {\n await this.agent.destroy?.();\n } catch (error) {\n console.debug('Failed to destroy agent during disconnect:', error);\n }\n this.agent = undefined;\n\n return {\n content: [\n {\n type: 'text',\n text: 'Disconnected from web page',\n },\n ],\n };\n },\n },\n ];\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","WebMidsceneTools","BaseMidsceneTools","StaticPage","openNewTabWithUrl","error","console","undefined","Error","url","agent","AgentOverChromeBridge","z","args","screenshot"],"mappings":"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"}
1
+ {"version":3,"file":"mcp-tools.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../src/mcp-tools.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { z } from '@midscene/core';\nimport { BaseMidsceneTools, type ToolDefinition } from '@midscene/shared/mcp';\nimport { AgentOverChromeBridge } from './bridge-mode';\nimport { StaticPage } from './static';\n\n/**\n * Tools manager for Web bridge-mode MCP\n */\nexport class WebMidsceneTools extends BaseMidsceneTools<AgentOverChromeBridge> {\n protected createTemporaryDevice() {\n // Use require to avoid type incompatibility with DeviceAction vs ActionSpaceItem\n // StaticPage.actionSpace() returns DeviceAction[] which is compatible at runtime\n return new StaticPage({\n screenshotBase64: '',\n size: { width: 1920, height: 1080 },\n });\n }\n\n protected async ensureAgent(\n openNewTabWithUrl?: string,\n ): Promise<AgentOverChromeBridge> {\n // Re-init if URL provided\n if (this.agent && openNewTabWithUrl) {\n try {\n await this.agent?.destroy?.();\n } catch (error) {\n console.debug('Failed to destroy agent during re-init:', error);\n }\n this.agent = undefined;\n }\n\n if (this.agent) return this.agent;\n\n // Bridge mode requires a URL to connect to browser\n if (!openNewTabWithUrl) {\n throw new Error(\n 'Bridge mode requires a URL. Use web_connect tool to connect to a page first.',\n );\n }\n\n this.agent = await this.initBridgeModeAgent(openNewTabWithUrl);\n\n return this.agent;\n }\n\n private async initBridgeModeAgent(\n url?: string,\n ): Promise<AgentOverChromeBridge> {\n const agent = new AgentOverChromeBridge({ closeConflictServer: true });\n\n if (!url) {\n await agent.connectCurrentTab();\n } else {\n await agent.connectNewTabWithUrl(url);\n }\n\n return agent;\n }\n\n protected preparePlatformTools(): ToolDefinition[] {\n return [\n {\n name: 'web_connect',\n description: 'Connect to web page by opening new tab with URL',\n schema: {\n url: z.string().url().describe('URL to connect to'),\n },\n handler: async (args) => {\n const { url } = args as { url: string };\n const agent = await this.ensureAgent(url);\n const screenshot = await agent.page?.screenshotBase64();\n if (!screenshot) {\n return {\n content: [\n {\n type: 'text',\n text: `Connected to: ${url}`,\n },\n ],\n };\n }\n\n return {\n content: [\n {\n type: 'text',\n text: `Connected to: ${url}`,\n },\n ...this.buildScreenshotContent(screenshot),\n ],\n };\n },\n },\n {\n name: 'web_disconnect',\n description:\n 'Disconnect from current web page and release browser resources',\n schema: {},\n handler: this.createDisconnectHandler('web page'),\n },\n ];\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","WebMidsceneTools","BaseMidsceneTools","StaticPage","openNewTabWithUrl","error","console","undefined","Error","url","agent","AgentOverChromeBridge","z","args","screenshot"],"mappings":";;;IAAAA,oBAAoB,CAAC,GAAG,CAAC,UAASC;QACjC,IAAI,IAAIC,OAAOD,WACR,IAAGD,oBAAoB,CAAC,CAACC,YAAYC,QAAQ,CAACF,oBAAoB,CAAC,CAAC,UAASE,MACzEC,OAAO,cAAc,CAAC,UAASD,KAAK;YAAE,YAAY;YAAM,KAAKD,UAAU,CAACC,IAAI;QAAC;IAGzF;;;ICNAF,oBAAoB,CAAC,GAAG,CAACI,KAAKC,OAAUF,OAAO,SAAS,CAAC,cAAc,CAAC,IAAI,CAACC,KAAKC;;;ICClFL,oBAAoB,CAAC,GAAG,CAAC;QACxB,IAAG,AAAkB,eAAlB,OAAOM,UAA0BA,OAAO,WAAW,EACrDH,OAAO,cAAc,CAAC,UAASG,OAAO,WAAW,EAAE;YAAE,OAAO;QAAS;QAEtEH,OAAO,cAAc,CAAC,UAAS,cAAc;YAAE,OAAO;QAAK;IAC5D;;;;;;;;;;;ACEO,MAAMI,yBAAyBC,oBAAAA,iBAAiBA;IAC3C,wBAAwB;QAGhC,OAAO,IAAIC,yCAAAA,UAAUA,CAAC;YACpB,kBAAkB;YAClB,MAAM;gBAAE,OAAO;gBAAM,QAAQ;YAAK;QACpC;IACF;IAEA,MAAgB,YACdC,iBAA0B,EACM;QAEhC,IAAI,IAAI,CAAC,KAAK,IAAIA,mBAAmB;YACnC,IAAI;gBACF,MAAM,IAAI,CAAC,KAAK,EAAE;YACpB,EAAE,OAAOC,OAAO;gBACdC,QAAQ,KAAK,CAAC,2CAA2CD;YAC3D;YACA,IAAI,CAAC,KAAK,GAAGE;QACf;QAEA,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,IAAI,CAAC,KAAK;QAGjC,IAAI,CAACH,mBACH,MAAM,IAAII,MACR;QAIJ,IAAI,CAAC,KAAK,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAACJ;QAE5C,OAAO,IAAI,CAAC,KAAK;IACnB;IAEA,MAAc,oBACZK,GAAY,EACoB;QAChC,MAAMC,QAAQ,IAAIC,yBAAAA,qBAAqBA,CAAC;YAAE,qBAAqB;QAAK;QAEpE,IAAKF,KAGH,MAAMC,MAAM,oBAAoB,CAACD;aAFjC,MAAMC,MAAM,iBAAiB;QAK/B,OAAOA;IACT;IAEU,uBAAyC;QACjD,OAAO;YACL;gBACE,MAAM;gBACN,aAAa;gBACb,QAAQ;oBACN,KAAKE,qBAAAA,CAAAA,CAAAA,MAAQ,GAAG,GAAG,GAAG,QAAQ,CAAC;gBACjC;gBACA,SAAS,OAAOC;oBACd,MAAM,EAAEJ,GAAG,EAAE,GAAGI;oBAChB,MAAMH,QAAQ,MAAM,IAAI,CAAC,WAAW,CAACD;oBACrC,MAAMK,aAAa,MAAMJ,MAAM,IAAI,EAAE;oBACrC,IAAI,CAACI,YACH,OAAO;wBACL,SAAS;4BACP;gCACE,MAAM;gCACN,MAAM,CAAC,cAAc,EAAEL,KAAK;4BAC9B;yBACD;oBACH;oBAGF,OAAO;wBACL,SAAS;4BACP;gCACE,MAAM;gCACN,MAAM,CAAC,cAAc,EAAEA,KAAK;4BAC9B;+BACG,IAAI,CAAC,sBAAsB,CAACK;yBAChC;oBACH;gBACF;YACF;YACA;gBACE,MAAM;gBACN,aACE;gBACF,QAAQ,CAAC;gBACT,SAAS,IAAI,CAAC,uBAAuB,CAAC;YACxC;SACD;IACH;AACF"}
@@ -36,6 +36,7 @@ __webpack_require__.d(__webpack_exports__, {
36
36
  defaultViewportScale: ()=>defaultViewportScale,
37
37
  launchPuppeteerPage: ()=>launchPuppeteerPage,
38
38
  defaultUA: ()=>defaultUA,
39
+ buildChromeArgs: ()=>buildChromeArgs,
39
40
  defaultViewportHeight: ()=>defaultViewportHeight,
40
41
  defaultWaitForNetworkIdleTimeout: ()=>defaultWaitForNetworkIdleTimeout,
41
42
  resolveAiActionContext: ()=>resolveAiActionContext,
@@ -77,6 +78,38 @@ function validateChromeArgs(args, baseArgs) {
77
78
  if (dangerousArgs.length > 0) console.warn(`Warning: Dangerous Chrome arguments detected: ${dangerousArgs.join(', ')}.\nThese arguments may reduce browser security. Use only in controlled testing environments.`);
78
79
  }
79
80
  const launcherDebug = (0, logger_namespaceObject.getDebug)('puppeteer:launcher');
81
+ function buildChromeArgs(options) {
82
+ const isWindows = 'win32' === process.platform;
83
+ const sandboxArgs = isWindows ? [] : [
84
+ '--no-sandbox',
85
+ '--disable-setuid-sandbox'
86
+ ];
87
+ const featureArgs = [
88
+ '--disable-features=HttpsFirstBalancedModeAutoEnable',
89
+ '--disable-features=PasswordLeakDetection',
90
+ '--disable-save-password-bubble'
91
+ ];
92
+ const userAgentArg = options?.userAgent ? [
93
+ `--user-agent="${options.userAgent}"`
94
+ ] : [];
95
+ const windowSizeArg = options?.windowSize ? [
96
+ `--window-size=${options.windowSize.width},${options.windowSize.height}`
97
+ ] : [];
98
+ const baseArgs = [
99
+ ...sandboxArgs,
100
+ ...featureArgs,
101
+ ...userAgentArg,
102
+ ...windowSizeArg
103
+ ];
104
+ if (options?.chromeArgs?.length) {
105
+ validateChromeArgs(options.chromeArgs, baseArgs);
106
+ return [
107
+ ...baseArgs,
108
+ ...options.chromeArgs
109
+ ];
110
+ }
111
+ return baseArgs;
112
+ }
80
113
  async function launchPuppeteerPage(target, preference, browser) {
81
114
  (0, utils_namespaceObject.assert)(target.url, 'url is required');
82
115
  const freeFn = [];
@@ -105,30 +138,16 @@ async function launchPuppeteerPage(target, preference, browser) {
105
138
  deviceScaleFactor: dpr
106
139
  };
107
140
  const headed = preference?.headed || preference?.keepWindow;
108
- const windowSizeArg = `--window-size=${width},${height + (headed ? 100 : 0)}`;
109
141
  const defaultViewportConfig = headed ? null : viewportConfig;
110
142
  if (headed && '1' === process.env.CI) console.warn('you are probably running headed mode in CI, this will usually fail.');
111
- const isWindows = 'win32' === process.platform;
112
- const baseArgs = [
113
- ...isWindows ? [] : [
114
- '--no-sandbox',
115
- '--disable-setuid-sandbox'
116
- ],
117
- '--disable-features=HttpsFirstBalancedModeAutoEnable',
118
- '--disable-features=PasswordLeakDetection',
119
- '--disable-save-password-bubble',
120
- `--user-agent="${ua}"`,
121
- windowSizeArg
122
- ];
123
- let args = baseArgs;
124
- if (target.chromeArgs && target.chromeArgs.length > 0) {
125
- validateChromeArgs(target.chromeArgs, baseArgs);
126
- args = [
127
- ...baseArgs,
128
- ...target.chromeArgs
129
- ];
130
- launcherDebug('Merging custom Chrome arguments', target.chromeArgs, 'Final args', args);
131
- }
143
+ const args = buildChromeArgs({
144
+ userAgent: ua,
145
+ windowSize: {
146
+ width,
147
+ height: height + (headed ? 100 : 0)
148
+ },
149
+ chromeArgs: target.chromeArgs
150
+ });
132
151
  launcherDebug('launching browser with viewport, headed', headed, 'viewport', viewportConfig, 'args', args, 'preference', preference);
133
152
  let browserInstance = browser;
134
153
  if (!browserInstance) {
@@ -141,7 +160,7 @@ async function launchPuppeteerPage(target, preference, browser) {
141
160
  freeFn.push({
142
161
  name: 'puppeteer_browser',
143
162
  fn: ()=>{
144
- if (!preference?.keepWindow) if (isWindows) setTimeout(()=>{
163
+ if (!preference?.keepWindow) if ('win32' === process.platform) setTimeout(()=>{
145
164
  browserInstance?.close();
146
165
  }, 800);
147
166
  else browserInstance?.close();
@@ -198,6 +217,7 @@ async function puppeteerAgentForTarget(target, preference, browser) {
198
217
  freeFn
199
218
  };
200
219
  }
220
+ exports.buildChromeArgs = __webpack_exports__.buildChromeArgs;
201
221
  exports.defaultUA = __webpack_exports__.defaultUA;
202
222
  exports.defaultViewportHeight = __webpack_exports__.defaultViewportHeight;
203
223
  exports.defaultViewportScale = __webpack_exports__.defaultViewportScale;
@@ -207,6 +227,7 @@ exports.launchPuppeteerPage = __webpack_exports__.launchPuppeteerPage;
207
227
  exports.puppeteerAgentForTarget = __webpack_exports__.puppeteerAgentForTarget;
208
228
  exports.resolveAiActionContext = __webpack_exports__.resolveAiActionContext;
209
229
  for(var __rspack_i in __webpack_exports__)if (-1 === [
230
+ "buildChromeArgs",
210
231
  "defaultUA",
211
232
  "defaultViewportHeight",
212
233
  "defaultViewportScale",
@@ -1 +1 @@
1
- {"version":3,"file":"puppeteer/agent-launcher.js","sources":["webpack/runtime/compat_get_default_export","webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/puppeteer/agent-launcher.ts"],"sourcesContent":["// getDefaultExport function for compatibility with non-ESM modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};\n","__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { readFileSync } from 'node:fs';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\n\nimport { PuppeteerAgent } from '@/puppeteer/index';\nimport type { AgentOpt, Cache, MidsceneYamlScriptWebEnv } from '@midscene/core';\nimport { DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT } from '@midscene/shared/constants';\nimport puppeteer, { type Browser } from 'puppeteer';\n\nexport const defaultUA =\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36';\nexport const defaultViewportWidth = 1440;\nexport const defaultViewportHeight = 768;\nexport const defaultViewportScale = process.platform === 'darwin' ? 2 : 1;\nexport const defaultWaitForNetworkIdleTimeout =\n DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT;\n\nexport function resolveAiActionContext(\n target: MidsceneYamlScriptWebEnv,\n preference?: Partial<Pick<AgentOpt, 'aiActionContext' | 'aiActContext'>>,\n): AgentOpt['aiActionContext'] | undefined {\n // Prefer agent-level preference if provided; otherwise fall back to target-level context.\n // Priority: preference.aiActContext > preference.aiActionContext (deprecated) > target.aiActionContext\n const data =\n preference?.aiActContext ??\n preference?.aiActionContext ??\n target.aiActionContext;\n return data;\n}\n\n/**\n * Chrome arguments that may reduce browser security.\n * These should only be used in controlled testing environments.\n *\n * Security implications:\n * - `--no-sandbox`: Disables Chrome's sandbox security model\n * - `--disable-setuid-sandbox`: Disables setuid sandbox on Linux\n * - `--disable-web-security`: Allows cross-origin requests without CORS\n * - `--ignore-certificate-errors`: Ignores SSL/TLS certificate errors\n * - `--disable-features=IsolateOrigins`: Disables origin isolation\n * - `--disable-site-isolation-trials`: Disables site isolation\n * - `--allow-running-insecure-content`: Allows mixed HTTP/HTTPS content\n */\nconst DANGEROUS_ARGS = [\n '--no-sandbox',\n '--disable-setuid-sandbox',\n '--disable-web-security',\n '--ignore-certificate-errors',\n '--disable-features=IsolateOrigins',\n '--disable-site-isolation-trials',\n '--allow-running-insecure-content',\n] as const;\n\n/**\n * Validates Chrome launch arguments for security concerns.\n * Emits a warning if dangerous arguments are detected.\n *\n * This function filters out arguments that are already present in baseArgs\n * to avoid warning about platform-specific defaults (e.g., --no-sandbox on non-Windows).\n *\n * @param args - Chrome launch arguments to validate\n * @param baseArgs - Base Chrome arguments already configured\n *\n * @example\n * ```typescript\n * // Will show warning for --disable-web-security\n * validateChromeArgs(['--disable-web-security', '--headless'], ['--no-sandbox']);\n *\n * // Will NOT show warning for --no-sandbox (already in baseArgs)\n * validateChromeArgs(['--no-sandbox'], ['--no-sandbox', '--headless']);\n * ```\n */\nfunction validateChromeArgs(args: string[], baseArgs: string[]): void {\n // Filter out arguments that are already in baseArgs\n const newArgs = args.filter(\n (arg) =>\n !baseArgs.some((baseArg) => {\n // Check if arg starts with the same flag as baseArg (before '=' if present)\n const argFlag = arg.split('=')[0];\n const baseFlag = baseArg.split('=')[0];\n return argFlag === baseFlag;\n }),\n );\n\n const dangerousArgs = newArgs.filter((arg) =>\n DANGEROUS_ARGS.some((dangerous) => arg.startsWith(dangerous)),\n );\n\n if (dangerousArgs.length > 0) {\n console.warn(\n `Warning: Dangerous Chrome arguments detected: ${dangerousArgs.join(', ')}.\\nThese arguments may reduce browser security. Use only in controlled testing environments.`,\n );\n }\n}\n\ninterface FreeFn {\n name: string;\n fn: () => void;\n}\n\nconst launcherDebug = getDebug('puppeteer:launcher');\n\nexport async function launchPuppeteerPage(\n target: MidsceneYamlScriptWebEnv,\n preference?: {\n headed?: boolean;\n keepWindow?: boolean;\n },\n browser?: Browser,\n) {\n assert(target.url, 'url is required');\n const freeFn: FreeFn[] = [];\n\n // prepare the environment\n const ua = target.userAgent || defaultUA;\n let width = defaultViewportWidth;\n if (target.viewportWidth) {\n assert(\n typeof target.viewportWidth === 'number',\n 'viewportWidth must be a number',\n );\n width = Number.parseInt(target.viewportWidth as unknown as string, 10);\n assert(width > 0, `viewportWidth must be greater than 0, but got ${width}`);\n }\n let height = defaultViewportHeight;\n if (target.viewportHeight) {\n assert(\n typeof target.viewportHeight === 'number',\n 'viewportHeight must be a number',\n );\n height = Number.parseInt(target.viewportHeight as unknown as string, 10);\n assert(\n height > 0,\n `viewportHeight must be greater than 0, but got ${height}`,\n );\n }\n let dpr = defaultViewportScale;\n if (target.viewportScale) {\n assert(\n typeof target.viewportScale === 'number',\n 'viewportScale must be a number',\n );\n dpr = Number.parseInt(target.viewportScale as unknown as string, 10);\n assert(dpr > 0, `viewportScale must be greater than 0, but got ${dpr}`);\n }\n const viewportConfig = {\n width,\n height,\n deviceScaleFactor: dpr,\n };\n\n const headed = preference?.headed || preference?.keepWindow;\n const windowSizeArg = `--window-size=${width},${height + (headed ? 100 : 0)}`; // add 100px for the address bar in headed mode\n const defaultViewportConfig = headed ? null : viewportConfig;\n\n // launch the browser\n if (headed && process.env.CI === '1') {\n console.warn(\n 'you are probably running headed mode in CI, this will usually fail.',\n );\n }\n // do not use 'no-sandbox' on windows https://www.perplexity.ai/search/how-to-solve-this-with-nodejs-dMHpdCypRa..JA8TkQzbeQ\n const isWindows = process.platform === 'win32';\n\n const baseArgs = [\n ...(isWindows ? [] : ['--no-sandbox', '--disable-setuid-sandbox']),\n '--disable-features=HttpsFirstBalancedModeAutoEnable',\n '--disable-features=PasswordLeakDetection',\n '--disable-save-password-bubble',\n `--user-agent=\"${ua}\"`,\n windowSizeArg,\n ];\n\n // Merge custom Chrome arguments\n let args = baseArgs;\n if (target.chromeArgs && target.chromeArgs.length > 0) {\n validateChromeArgs(target.chromeArgs, baseArgs);\n\n // Custom args come after base args, allowing them to override defaults\n args = [...baseArgs, ...target.chromeArgs];\n launcherDebug(\n 'Merging custom Chrome arguments',\n target.chromeArgs,\n 'Final args',\n args,\n );\n }\n\n launcherDebug(\n 'launching browser with viewport, headed',\n headed,\n 'viewport',\n viewportConfig,\n 'args',\n args,\n 'preference',\n preference,\n );\n let browserInstance = browser;\n if (!browserInstance) {\n browserInstance = await puppeteer.launch({\n headless: !preference?.headed,\n defaultViewport: defaultViewportConfig,\n args,\n acceptInsecureCerts: target.acceptInsecureCerts,\n });\n freeFn.push({\n name: 'puppeteer_browser',\n fn: () => {\n if (!preference?.keepWindow) {\n if (isWindows) {\n setTimeout(() => {\n browserInstance?.close();\n }, 800);\n } else {\n browserInstance?.close();\n }\n }\n },\n });\n }\n const page = await browserInstance.newPage();\n\n if (target.cookie) {\n const cookieFileContent = readFileSync(target.cookie, 'utf-8');\n await browserInstance.setCookie(...JSON.parse(cookieFileContent));\n }\n\n if (ua) {\n await page.setUserAgent(ua);\n }\n\n if (viewportConfig) {\n await page.setViewport(viewportConfig);\n }\n\n const waitForNetworkIdleTimeout =\n typeof target.waitForNetworkIdle?.timeout === 'number'\n ? target.waitForNetworkIdle.timeout\n : defaultWaitForNetworkIdleTimeout;\n\n try {\n launcherDebug('goto', target.url);\n await page.goto(target.url);\n if (waitForNetworkIdleTimeout > 0) {\n launcherDebug('waitForNetworkIdle', waitForNetworkIdleTimeout);\n await page.waitForNetworkIdle({\n timeout: waitForNetworkIdleTimeout,\n });\n }\n } catch (e) {\n if (\n typeof target.waitForNetworkIdle?.continueOnNetworkIdleError ===\n 'boolean' &&\n !target.waitForNetworkIdle?.continueOnNetworkIdleError\n ) {\n const newError = new Error(`failed to wait for network idle: ${e}`, {\n cause: e,\n });\n throw newError;\n }\n const newMessage = `failed to wait for network idle after ${waitForNetworkIdleTimeout}ms, but the script will continue.`;\n console.warn(newMessage);\n }\n\n return { page, freeFn };\n}\n\nexport async function puppeteerAgentForTarget(\n target: MidsceneYamlScriptWebEnv,\n preference?: {\n headed?: boolean;\n keepWindow?: boolean;\n } & Partial<\n Pick<\n AgentOpt,\n | 'testId'\n | 'groupName'\n | 'groupDescription'\n | 'generateReport'\n | 'autoPrintReportMsg'\n | 'reportFileName'\n | 'replanningCycleLimit'\n | 'cache'\n | 'aiActionContext'\n >\n >,\n browser?: Browser,\n) {\n const { page, freeFn } = await launchPuppeteerPage(\n target,\n preference,\n browser,\n );\n const aiActContext = resolveAiActionContext(target, preference);\n\n const { aiActionContext, ...preferenceToUse } = preference ?? {};\n\n // prepare Midscene agent\n const agent = new PuppeteerAgent(page, {\n ...preferenceToUse,\n aiActContext,\n forceSameTabNavigation:\n typeof target.forceSameTabNavigation !== 'undefined'\n ? target.forceSameTabNavigation\n : true, // true for default in yaml script\n });\n\n freeFn.push({\n name: 'midscene_puppeteer_agent',\n fn: () => agent.destroy(),\n });\n\n return { agent, freeFn };\n}\n"],"names":["__webpack_require__","module","getter","definition","key","Object","obj","prop","Symbol","defaultUA","defaultViewportWidth","defaultViewportHeight","defaultViewportScale","process","defaultWaitForNetworkIdleTimeout","DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT","resolveAiActionContext","target","preference","data","DANGEROUS_ARGS","validateChromeArgs","args","baseArgs","newArgs","arg","baseArg","argFlag","baseFlag","dangerousArgs","dangerous","console","launcherDebug","getDebug","launchPuppeteerPage","browser","assert","freeFn","ua","width","Number","height","dpr","viewportConfig","headed","windowSizeArg","defaultViewportConfig","isWindows","browserInstance","puppeteer","setTimeout","page","cookieFileContent","readFileSync","JSON","waitForNetworkIdleTimeout","e","newError","Error","newMessage","puppeteerAgentForTarget","aiActContext","aiActionContext","preferenceToUse","agent","PuppeteerAgent"],"mappings":"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"}
1
+ {"version":3,"file":"puppeteer/agent-launcher.js","sources":["webpack/runtime/compat_get_default_export","webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/puppeteer/agent-launcher.ts"],"sourcesContent":["// getDefaultExport function for compatibility with non-ESM modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};\n","__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { readFileSync } from 'node:fs';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\n\nimport { PuppeteerAgent } from '@/puppeteer/index';\nimport type { AgentOpt, Cache, MidsceneYamlScriptWebEnv } from '@midscene/core';\nimport { DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT } from '@midscene/shared/constants';\nimport puppeteer, { type Browser } from 'puppeteer';\n\nexport const defaultUA =\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36';\nexport const defaultViewportWidth = 1440;\nexport const defaultViewportHeight = 768;\nexport const defaultViewportScale = process.platform === 'darwin' ? 2 : 1;\nexport const defaultWaitForNetworkIdleTimeout =\n DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT;\n\nexport function resolveAiActionContext(\n target: MidsceneYamlScriptWebEnv,\n preference?: Partial<Pick<AgentOpt, 'aiActionContext' | 'aiActContext'>>,\n): AgentOpt['aiActionContext'] | undefined {\n // Prefer agent-level preference if provided; otherwise fall back to target-level context.\n // Priority: preference.aiActContext > preference.aiActionContext (deprecated) > target.aiActionContext\n const data =\n preference?.aiActContext ??\n preference?.aiActionContext ??\n target.aiActionContext;\n return data;\n}\n\n/**\n * Chrome arguments that may reduce browser security.\n * These should only be used in controlled testing environments.\n *\n * Security implications:\n * - `--no-sandbox`: Disables Chrome's sandbox security model\n * - `--disable-setuid-sandbox`: Disables setuid sandbox on Linux\n * - `--disable-web-security`: Allows cross-origin requests without CORS\n * - `--ignore-certificate-errors`: Ignores SSL/TLS certificate errors\n * - `--disable-features=IsolateOrigins`: Disables origin isolation\n * - `--disable-site-isolation-trials`: Disables site isolation\n * - `--allow-running-insecure-content`: Allows mixed HTTP/HTTPS content\n */\nconst DANGEROUS_ARGS = [\n '--no-sandbox',\n '--disable-setuid-sandbox',\n '--disable-web-security',\n '--ignore-certificate-errors',\n '--disable-features=IsolateOrigins',\n '--disable-site-isolation-trials',\n '--allow-running-insecure-content',\n] as const;\n\n/**\n * Validates Chrome launch arguments for security concerns.\n * Emits a warning if dangerous arguments are detected.\n *\n * This function filters out arguments that are already present in baseArgs\n * to avoid warning about platform-specific defaults (e.g., --no-sandbox on non-Windows).\n *\n * @param args - Chrome launch arguments to validate\n * @param baseArgs - Base Chrome arguments already configured\n *\n * @example\n * ```typescript\n * // Will show warning for --disable-web-security\n * validateChromeArgs(['--disable-web-security', '--headless'], ['--no-sandbox']);\n *\n * // Will NOT show warning for --no-sandbox (already in baseArgs)\n * validateChromeArgs(['--no-sandbox'], ['--no-sandbox', '--headless']);\n * ```\n */\nfunction validateChromeArgs(args: string[], baseArgs: string[]): void {\n // Filter out arguments that are already in baseArgs\n const newArgs = args.filter(\n (arg) =>\n !baseArgs.some((baseArg) => {\n // Check if arg starts with the same flag as baseArg (before '=' if present)\n const argFlag = arg.split('=')[0];\n const baseFlag = baseArg.split('=')[0];\n return argFlag === baseFlag;\n }),\n );\n\n const dangerousArgs = newArgs.filter((arg) =>\n DANGEROUS_ARGS.some((dangerous) => arg.startsWith(dangerous)),\n );\n\n if (dangerousArgs.length > 0) {\n console.warn(\n `Warning: Dangerous Chrome arguments detected: ${dangerousArgs.join(', ')}.\\nThese arguments may reduce browser security. Use only in controlled testing environments.`,\n );\n }\n}\n\ninterface FreeFn {\n name: string;\n fn: () => void;\n}\n\nconst launcherDebug = getDebug('puppeteer:launcher');\n\nexport interface BuildChromeArgsOptions {\n userAgent?: string;\n windowSize?: { width: number; height: number };\n chromeArgs?: string[];\n}\n\n/**\n * Builds Chrome launch arguments with sensible defaults.\n *\n * Platform-specific behavior:\n * - On non-Windows systems, automatically adds --no-sandbox and --disable-setuid-sandbox\n * for compatibility with containerized/CI environments\n *\n * @param options - Configuration options for Chrome arguments\n * @returns Array of Chrome launch arguments\n *\n * @example\n * ```typescript\n * // Basic usage\n * const args = buildChromeArgs();\n *\n * // With custom arguments\n * const args = buildChromeArgs({\n * chromeArgs: ['--disable-gpu', '--disable-dev-shm-usage'],\n * userAgent: 'CustomUA/1.0',\n * windowSize: { width: 1920, height: 1080 },\n * });\n * ```\n */\nexport function buildChromeArgs(options?: BuildChromeArgsOptions): string[] {\n const isWindows = process.platform === 'win32';\n\n const sandboxArgs = isWindows\n ? []\n : ['--no-sandbox', '--disable-setuid-sandbox'];\n const featureArgs = [\n '--disable-features=HttpsFirstBalancedModeAutoEnable',\n '--disable-features=PasswordLeakDetection',\n '--disable-save-password-bubble',\n ];\n const userAgentArg = options?.userAgent\n ? [`--user-agent=\"${options.userAgent}\"`]\n : [];\n const windowSizeArg = options?.windowSize\n ? [`--window-size=${options.windowSize.width},${options.windowSize.height}`]\n : [];\n\n const baseArgs = [\n ...sandboxArgs,\n ...featureArgs,\n ...userAgentArg,\n ...windowSizeArg,\n ];\n\n if (options?.chromeArgs?.length) {\n validateChromeArgs(options.chromeArgs, baseArgs);\n return [...baseArgs, ...options.chromeArgs];\n }\n\n return baseArgs;\n}\n\nexport async function launchPuppeteerPage(\n target: MidsceneYamlScriptWebEnv,\n preference?: {\n headed?: boolean;\n keepWindow?: boolean;\n },\n browser?: Browser,\n) {\n assert(target.url, 'url is required');\n const freeFn: FreeFn[] = [];\n\n // prepare the environment\n const ua = target.userAgent || defaultUA;\n let width = defaultViewportWidth;\n if (target.viewportWidth) {\n assert(\n typeof target.viewportWidth === 'number',\n 'viewportWidth must be a number',\n );\n width = Number.parseInt(target.viewportWidth as unknown as string, 10);\n assert(width > 0, `viewportWidth must be greater than 0, but got ${width}`);\n }\n let height = defaultViewportHeight;\n if (target.viewportHeight) {\n assert(\n typeof target.viewportHeight === 'number',\n 'viewportHeight must be a number',\n );\n height = Number.parseInt(target.viewportHeight as unknown as string, 10);\n assert(\n height > 0,\n `viewportHeight must be greater than 0, but got ${height}`,\n );\n }\n let dpr = defaultViewportScale;\n if (target.viewportScale) {\n assert(\n typeof target.viewportScale === 'number',\n 'viewportScale must be a number',\n );\n dpr = Number.parseInt(target.viewportScale as unknown as string, 10);\n assert(dpr > 0, `viewportScale must be greater than 0, but got ${dpr}`);\n }\n const viewportConfig = {\n width,\n height,\n deviceScaleFactor: dpr,\n };\n\n const headed = preference?.headed || preference?.keepWindow;\n const defaultViewportConfig = headed ? null : viewportConfig;\n\n // launch the browser\n if (headed && process.env.CI === '1') {\n console.warn(\n 'you are probably running headed mode in CI, this will usually fail.',\n );\n }\n\n // Build Chrome arguments using the shared helper\n const args = buildChromeArgs({\n userAgent: ua,\n windowSize: {\n width,\n height: height + (headed ? 100 : 0), // add 100px for the address bar in headed mode\n },\n chromeArgs: target.chromeArgs,\n });\n\n launcherDebug(\n 'launching browser with viewport, headed',\n headed,\n 'viewport',\n viewportConfig,\n 'args',\n args,\n 'preference',\n preference,\n );\n let browserInstance = browser;\n if (!browserInstance) {\n browserInstance = await puppeteer.launch({\n headless: !preference?.headed,\n defaultViewport: defaultViewportConfig,\n args,\n acceptInsecureCerts: target.acceptInsecureCerts,\n });\n freeFn.push({\n name: 'puppeteer_browser',\n fn: () => {\n if (!preference?.keepWindow) {\n if (process.platform === 'win32') {\n setTimeout(() => {\n browserInstance?.close();\n }, 800);\n } else {\n browserInstance?.close();\n }\n }\n },\n });\n }\n const page = await browserInstance.newPage();\n\n if (target.cookie) {\n const cookieFileContent = readFileSync(target.cookie, 'utf-8');\n await browserInstance.setCookie(...JSON.parse(cookieFileContent));\n }\n\n if (ua) {\n await page.setUserAgent(ua);\n }\n\n if (viewportConfig) {\n await page.setViewport(viewportConfig);\n }\n\n const waitForNetworkIdleTimeout =\n typeof target.waitForNetworkIdle?.timeout === 'number'\n ? target.waitForNetworkIdle.timeout\n : defaultWaitForNetworkIdleTimeout;\n\n try {\n launcherDebug('goto', target.url);\n await page.goto(target.url);\n if (waitForNetworkIdleTimeout > 0) {\n launcherDebug('waitForNetworkIdle', waitForNetworkIdleTimeout);\n await page.waitForNetworkIdle({\n timeout: waitForNetworkIdleTimeout,\n });\n }\n } catch (e) {\n if (\n typeof target.waitForNetworkIdle?.continueOnNetworkIdleError ===\n 'boolean' &&\n !target.waitForNetworkIdle?.continueOnNetworkIdleError\n ) {\n const newError = new Error(`failed to wait for network idle: ${e}`, {\n cause: e,\n });\n throw newError;\n }\n const newMessage = `failed to wait for network idle after ${waitForNetworkIdleTimeout}ms, but the script will continue.`;\n console.warn(newMessage);\n }\n\n return { page, freeFn };\n}\n\nexport async function puppeteerAgentForTarget(\n target: MidsceneYamlScriptWebEnv,\n preference?: {\n headed?: boolean;\n keepWindow?: boolean;\n } & Partial<\n Pick<\n AgentOpt,\n | 'testId'\n | 'groupName'\n | 'groupDescription'\n | 'generateReport'\n | 'autoPrintReportMsg'\n | 'reportFileName'\n | 'replanningCycleLimit'\n | 'cache'\n | 'aiActionContext'\n >\n >,\n browser?: Browser,\n) {\n const { page, freeFn } = await launchPuppeteerPage(\n target,\n preference,\n browser,\n );\n const aiActContext = resolveAiActionContext(target, preference);\n\n const { aiActionContext, ...preferenceToUse } = preference ?? {};\n\n // prepare Midscene agent\n const agent = new PuppeteerAgent(page, {\n ...preferenceToUse,\n aiActContext,\n forceSameTabNavigation:\n typeof target.forceSameTabNavigation !== 'undefined'\n ? target.forceSameTabNavigation\n : true, // true for default in yaml script\n });\n\n freeFn.push({\n name: 'midscene_puppeteer_agent',\n fn: () => agent.destroy(),\n });\n\n return { agent, freeFn };\n}\n"],"names":["__webpack_require__","module","getter","definition","key","Object","obj","prop","Symbol","defaultUA","defaultViewportWidth","defaultViewportHeight","defaultViewportScale","process","defaultWaitForNetworkIdleTimeout","DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT","resolveAiActionContext","target","preference","data","DANGEROUS_ARGS","validateChromeArgs","args","baseArgs","newArgs","arg","baseArg","argFlag","baseFlag","dangerousArgs","dangerous","console","launcherDebug","getDebug","buildChromeArgs","options","isWindows","sandboxArgs","featureArgs","userAgentArg","windowSizeArg","launchPuppeteerPage","browser","assert","freeFn","ua","width","Number","height","dpr","viewportConfig","headed","defaultViewportConfig","browserInstance","puppeteer","setTimeout","page","cookieFileContent","readFileSync","JSON","waitForNetworkIdleTimeout","e","newError","Error","newMessage","puppeteerAgentForTarget","aiActContext","aiActionContext","preferenceToUse","agent","PuppeteerAgent"],"mappings":"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"}
@@ -11,6 +11,38 @@ interface FreeFn {
11
11
  name: string;
12
12
  fn: () => void;
13
13
  }
14
+ export interface BuildChromeArgsOptions {
15
+ userAgent?: string;
16
+ windowSize?: {
17
+ width: number;
18
+ height: number;
19
+ };
20
+ chromeArgs?: string[];
21
+ }
22
+ /**
23
+ * Builds Chrome launch arguments with sensible defaults.
24
+ *
25
+ * Platform-specific behavior:
26
+ * - On non-Windows systems, automatically adds --no-sandbox and --disable-setuid-sandbox
27
+ * for compatibility with containerized/CI environments
28
+ *
29
+ * @param options - Configuration options for Chrome arguments
30
+ * @returns Array of Chrome launch arguments
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * // Basic usage
35
+ * const args = buildChromeArgs();
36
+ *
37
+ * // With custom arguments
38
+ * const args = buildChromeArgs({
39
+ * chromeArgs: ['--disable-gpu', '--disable-dev-shm-usage'],
40
+ * userAgent: 'CustomUA/1.0',
41
+ * windowSize: { width: 1920, height: 1080 },
42
+ * });
43
+ * ```
44
+ */
45
+ export declare function buildChromeArgs(options?: BuildChromeArgsOptions): string[];
14
46
  export declare function launchPuppeteerPage(target: MidsceneYamlScriptWebEnv, preference?: {
15
47
  headed?: boolean;
16
48
  keepWindow?: boolean;
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "Browser use",
9
9
  "Android use"
10
10
  ],
11
- "version": "1.2.1-beta-20260109060244.0",
11
+ "version": "1.2.1-beta-20260109075435.0",
12
12
  "repository": "https://github.com/web-infra-dev/midscene",
13
13
  "homepage": "https://midscenejs.com/",
14
14
  "main": "./dist/lib/index.js",
@@ -108,9 +108,9 @@
108
108
  "http-server": "14.1.1",
109
109
  "socket.io": "^4.8.1",
110
110
  "socket.io-client": "4.8.1",
111
- "@midscene/playground": "1.2.1-beta-20260109060244.0",
112
- "@midscene/core": "1.2.1-beta-20260109060244.0",
113
- "@midscene/shared": "1.2.1-beta-20260109060244.0"
111
+ "@midscene/playground": "1.2.1-beta-20260109075435.0",
112
+ "@midscene/shared": "1.2.1-beta-20260109075435.0",
113
+ "@midscene/core": "1.2.1-beta-20260109075435.0"
114
114
  },
115
115
  "devDependencies": {
116
116
  "@playwright/test": "^1.44.1",