@midscene/web 1.7.7-beta-20260430031647.0 → 1.7.9

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.
Files changed (38) hide show
  1. package/dist/es/bridge-mode/io-client.mjs +1 -1
  2. package/dist/es/bridge-mode/io-server.mjs +2 -2
  3. package/dist/es/bridge-mode/io-server.mjs.map +1 -1
  4. package/dist/es/bridge-mode/page-browser-side.mjs +1 -1
  5. package/dist/es/bridge-mode/page-browser-side.mjs.map +1 -1
  6. package/dist/es/cli-options.mjs +4 -3
  7. package/dist/es/cli-options.mjs.map +1 -1
  8. package/dist/es/cli.mjs +1 -1
  9. package/dist/es/common/viewport.mjs +10 -2
  10. package/dist/es/common/viewport.mjs.map +1 -1
  11. package/dist/es/mcp-server.mjs +1 -1
  12. package/dist/es/mcp-tools-cdp.mjs +2 -2
  13. package/dist/es/mcp-tools-cdp.mjs.map +1 -1
  14. package/dist/es/mcp-tools-puppeteer.mjs +9 -8
  15. package/dist/es/mcp-tools-puppeteer.mjs.map +1 -1
  16. package/dist/es/mcp-tools.mjs +2 -2
  17. package/dist/es/mcp-tools.mjs.map +1 -1
  18. package/dist/lib/bridge-mode/io-client.js +1 -1
  19. package/dist/lib/bridge-mode/io-server.js +2 -2
  20. package/dist/lib/bridge-mode/io-server.js.map +1 -1
  21. package/dist/lib/bridge-mode/page-browser-side.js +1 -1
  22. package/dist/lib/bridge-mode/page-browser-side.js.map +1 -1
  23. package/dist/lib/cli-options.js +3 -2
  24. package/dist/lib/cli-options.js.map +1 -1
  25. package/dist/lib/cli.js +1 -1
  26. package/dist/lib/common/viewport.js +16 -2
  27. package/dist/lib/common/viewport.js.map +1 -1
  28. package/dist/lib/mcp-server.js +1 -1
  29. package/dist/lib/mcp-tools-cdp.js +1 -1
  30. package/dist/lib/mcp-tools-cdp.js.map +1 -1
  31. package/dist/lib/mcp-tools-puppeteer.js +8 -7
  32. package/dist/lib/mcp-tools-puppeteer.js.map +1 -1
  33. package/dist/lib/mcp-tools.js +1 -1
  34. package/dist/lib/mcp-tools.js.map +1 -1
  35. package/dist/types/cli-options.d.ts +1 -1
  36. package/dist/types/common/viewport.d.ts +3 -1
  37. package/dist/types/mcp-tools-puppeteer.d.ts +2 -2
  38. package/package.json +4 -4
@@ -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 { ScreenshotItem, z } from '@midscene/core';\nimport { BaseMidsceneTools } from '@midscene/shared/mcp/base-tools';\nimport type { ToolDefinition } from '@midscene/shared/mcp/types';\nimport { AgentOverChromeBridge } from './bridge-mode';\nimport { defaultViewportSize } from './common/viewport';\nimport { StaticPage } from './static';\n\n/**\n * Tools manager for Web bridge-mode MCP\n */\nexport class WebMidsceneTools extends BaseMidsceneTools<AgentOverChromeBridge> {\n protected getCliReportSessionName() {\n return 'midscene-web';\n }\n\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 // Use screenshotBase64 field to avoid async ScreenshotItem.create()\n return new StaticPage({\n screenshot: ScreenshotItem.create('', Date.now()),\n shotSize: defaultViewportSize,\n shrunkShotToLogicalRatio: 1,\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 // Connect to current tab when no URL provided (handles CLI stateless calls)\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 reportOptions = this.readCliReportAgentOptions();\n const agent = new AgentOverChromeBridge({\n closeConflictServer: true,\n ...(reportOptions ?? {}),\n });\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:\n 'Connect to web page. If URL provided, opens new tab; otherwise connects to current tab.',\n schema: {\n url: z\n .string()\n .url()\n .optional()\n .describe('URL to open in new tab (omit to connect current tab)'),\n },\n handler: async (args) => {\n const { url } = args as { url?: string };\n\n // Bypass ensureAgent's URL check — directly init bridge agent\n if (this.agent) {\n try {\n await this.agent.destroy?.();\n } catch {}\n this.agent = undefined;\n }\n const reportSession = this.createNewCliReportSession(\n url ?? 'current-tab',\n );\n this.commitCliReportSession(reportSession);\n this.agent = await this.initBridgeModeAgent(url);\n\n const screenshot = await this.agent.page?.screenshotBase64();\n const label = url ?? 'current tab';\n\n return {\n content: [\n { type: 'text', text: `Connected to: ${label}` },\n ...(screenshot ? 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","ScreenshotItem","Date","defaultViewportSize","openNewTabWithUrl","error","console","undefined","url","reportOptions","agent","AgentOverChromeBridge","z","args","reportSession","screenshot","label"],"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 { ScreenshotItem, z } from '@midscene/core';\nimport { BaseMidsceneTools } from '@midscene/shared/mcp/base-tools';\nimport type { ToolDefinition } from '@midscene/shared/mcp/types';\nimport { AgentOverChromeBridge } from './bridge-mode';\nimport { defaultStaticPageViewportSize } from './common/viewport';\nimport { StaticPage } from './static';\n\n/**\n * Tools manager for Web bridge-mode MCP\n */\nexport class WebMidsceneTools extends BaseMidsceneTools<AgentOverChromeBridge> {\n protected getCliReportSessionName() {\n return 'midscene-web';\n }\n\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 // Use screenshotBase64 field to avoid async ScreenshotItem.create()\n return new StaticPage({\n screenshot: ScreenshotItem.create('', Date.now()),\n shotSize: defaultStaticPageViewportSize,\n shrunkShotToLogicalRatio: 1,\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 // Connect to current tab when no URL provided (handles CLI stateless calls)\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 reportOptions = this.readCliReportAgentOptions();\n const agent = new AgentOverChromeBridge({\n closeConflictServer: true,\n ...(reportOptions ?? {}),\n });\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:\n 'Connect to web page. If URL provided, opens new tab; otherwise connects to current tab.',\n schema: {\n url: z\n .string()\n .url()\n .optional()\n .describe('URL to open in new tab (omit to connect current tab)'),\n },\n handler: async (args) => {\n const { url } = args as { url?: string };\n\n // Bypass ensureAgent's URL check — directly init bridge agent\n if (this.agent) {\n try {\n await this.agent.destroy?.();\n } catch {}\n this.agent = undefined;\n }\n const reportSession = this.createNewCliReportSession(\n url ?? 'current-tab',\n );\n this.commitCliReportSession(reportSession);\n this.agent = await this.initBridgeModeAgent(url);\n\n const screenshot = await this.agent.page?.screenshotBase64();\n const label = url ?? 'current tab';\n\n return {\n content: [\n { type: 'text', text: `Connected to: ${label}` },\n ...(screenshot ? 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","ScreenshotItem","Date","defaultStaticPageViewportSize","openNewTabWithUrl","error","console","undefined","url","reportOptions","agent","AgentOverChromeBridge","z","args","reportSession","screenshot","label"],"mappings":"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"}
@@ -3,6 +3,6 @@ export interface ParsedWebCliOptions {
3
3
  argv: string[];
4
4
  mode: 'bridge' | 'cdp' | 'puppeteer';
5
5
  cdpEndpoint?: string;
6
- viewport: ViewportSize;
6
+ viewport?: ViewportSize;
7
7
  }
8
8
  export declare function parseWebCliOptions(rawArgs: string[], env?: NodeJS.ProcessEnv): ParsedWebCliOptions;
@@ -3,8 +3,10 @@ export interface ViewportSize {
3
3
  height: number;
4
4
  }
5
5
  export declare const defaultViewportWidth = 1440;
6
- export declare const defaultViewportHeight = 768;
6
+ export declare const defaultViewportHeight = 800;
7
7
  export declare const defaultViewportSize: ViewportSize;
8
+ export declare const defaultPuppeteerWindowViewportSize: ViewportSize;
9
+ export declare const defaultStaticPageViewportSize: ViewportSize;
8
10
  export declare function resolveViewportSize(viewport?: {
9
11
  width?: number | string | null;
10
12
  height?: number | string | null;
@@ -6,14 +6,14 @@ import { StaticPage } from './static';
6
6
  export declare const PUPPETEER_ENDPOINT_FILE: string;
7
7
  export declare function buildDetachedChromeArgs(options: {
8
8
  userDataDir: string;
9
- viewport: ViewportSize;
9
+ viewport?: ViewportSize;
10
10
  }): string[];
11
11
  /**
12
12
  * Tools manager for Web Puppeteer-mode MCP.
13
13
  * Uses a persistent headless Chrome browser that survives across CLI calls.
14
14
  */
15
15
  export declare class WebPuppeteerMidsceneTools extends BaseMidsceneTools<PuppeteerAgent> {
16
- private readonly viewport;
16
+ private readonly viewport?;
17
17
  constructor(viewport?: ViewportSize);
18
18
  protected getCliReportSessionName(): string;
19
19
  protected createTemporaryDevice(): StaticPage;
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "Browser use",
9
9
  "Android use"
10
10
  ],
11
- "version": "1.7.7-beta-20260430031647.0",
11
+ "version": "1.7.9",
12
12
  "repository": "https://github.com/web-infra-dev/midscene",
13
13
  "homepage": "https://midscenejs.com/",
14
14
  "main": "./dist/lib/index.js",
@@ -111,9 +111,9 @@
111
111
  "socket.io": "^4.8.1",
112
112
  "socket.io-client": "4.8.1",
113
113
  "ws": "^8.18.1",
114
- "@midscene/core": "1.7.7-beta-20260430031647.0",
115
- "@midscene/shared": "1.7.7-beta-20260430031647.0",
116
- "@midscene/playground": "1.7.7-beta-20260430031647.0"
114
+ "@midscene/core": "1.7.9",
115
+ "@midscene/playground": "1.7.9",
116
+ "@midscene/shared": "1.7.9"
117
117
  },
118
118
  "devDependencies": {
119
119
  "@playwright/test": "^1.45.0",