@midscene/ios 1.13.3 → 1.13.4-beta-20260924033639.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.
package/dist/es/bin.mjs CHANGED
@@ -2,6 +2,7 @@ import node_path from "node:path";
2
2
  import { createMjpegPreviewDescriptor, definePlaygroundPlatform, launchPreparedPlaygroundPlatform } from "@midscene/playground";
3
3
  import { DEFAULT_WDA_PORT, PLAYGROUND_SERVER_PORT } from "@midscene/shared/constants";
4
4
  import { findAvailablePort } from "@midscene/shared/node";
5
+ import { WDAManager, WebDriverClient, normalizeWebDriverBaseUrl } from "@midscene/webdriver";
5
6
  import { Agent } from "@midscene/core/agent";
6
7
  import { MIDSCENE_IOS_DEVICE_CLASS_OVERRIDE } from "@midscene/shared/env";
7
8
  import { getDebug } from "@midscene/shared/logger";
@@ -11,7 +12,6 @@ import { z } from "@midscene/core";
11
12
  import { createDefaultMobileActions, defineAction, resolveTextInputOptions, sendTextSequentially, shouldInputSequentially } from "@midscene/core/device";
12
13
  import { sleep } from "@midscene/core/utils";
13
14
  import { createImgBase64ByFormat } from "@midscene/shared/img";
14
- import { WDAManager, WebDriverClient } from "@midscene/webdriver";
15
15
  import { createAgentTestRunnerNodeDefinition } from "@midscene/core/agent/test";
16
16
  import { z as v4_z } from "zod/v4";
17
17
  const defaultAppNameMapping = {
@@ -976,6 +976,9 @@ class MjpegFrameSource {
976
976
  this.nowFn = nowFn;
977
977
  }
978
978
  }
979
+ function assertWdaConnectionOptions(options) {
980
+ if (options?.wdaBaseUrl !== void 0 && (void 0 !== options.wdaHost || void 0 !== options.wdaPort)) throw new Error('wdaBaseUrl cannot be used with wdaHost or wdaPort. Choose one WDA connection method.');
981
+ }
979
982
  function device_define_property(obj, key, value) {
980
983
  if (key in obj) Object.defineProperty(obj, key, {
981
984
  value: value,
@@ -1644,6 +1647,7 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
1644
1647
  scroll: (param)=>this.performActionScroll(param)
1645
1648
  }
1646
1649
  });
1650
+ assertWdaConnectionOptions(options);
1647
1651
  this.deviceId = 'pending-connection';
1648
1652
  this.options = options;
1649
1653
  this.customActions = options?.customActions;
@@ -1653,11 +1657,14 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
1653
1657
  this.wdaBackend = new IOSWebDriverClient({
1654
1658
  port: wdaPort,
1655
1659
  host: wdaHost,
1660
+ ...options?.wdaBaseUrl !== void 0 ? {
1661
+ baseUrl: options.wdaBaseUrl
1662
+ } : {},
1656
1663
  ...options?.sessionId ? {
1657
1664
  sessionId: options.sessionId
1658
1665
  } : {}
1659
1666
  });
1660
- this.wdaManager = WDAManager.getInstance(wdaPort, wdaHost);
1667
+ this.wdaManager = WDAManager.getInstance(wdaPort, wdaHost, options?.wdaBaseUrl);
1661
1668
  this.mjpegStreamUrl = `http://${wdaHost}:${mjpegPort}`;
1662
1669
  if (options?.wdaMjpegFrameSource?.enabled) this.openFrameSource = ()=>this.openMjpegFrameSource();
1663
1670
  }
@@ -1866,6 +1873,7 @@ class IOSAgent extends Agent {
1866
1873
  }
1867
1874
  }
1868
1875
  async function agentFromWebDriverAgent(opts) {
1876
+ assertWdaConnectionOptions(opts);
1869
1877
  debugAgent('Creating iOS agent with WebDriverAgent');
1870
1878
  const overrideModule = opts?.iOSDeviceClassOverride?.trim() || process.env[MIDSCENE_IOS_DEVICE_CLASS_OVERRIDE]?.trim();
1871
1879
  let DeviceClass = IOSDevice;
@@ -1910,7 +1918,7 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
1910
1918
  const wdaReady = await probeWdaReady('localhost', DEFAULT_WDA_PORT);
1911
1919
  return {
1912
1920
  title: 'Connect WebDriverAgent',
1913
- description: 'Provide the WebDriverAgent host and port that are already running for your selected iPhone or simulator.',
1921
+ description: 'Provide a WebDriverAgent base URL, or the host and port for your selected iPhone or simulator.',
1914
1922
  primaryActionLabel: 'Create Agent',
1915
1923
  autoSubmitWhenReady: wdaReady,
1916
1924
  fields: [
@@ -1918,18 +1926,23 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
1918
1926
  key: 'host',
1919
1927
  label: 'WebDriverAgent host',
1920
1928
  type: 'text',
1921
- required: true,
1922
- defaultValue: 'localhost',
1929
+ required: false,
1923
1930
  placeholder: 'localhost'
1924
1931
  },
1925
1932
  {
1926
1933
  key: 'port',
1927
1934
  label: 'WebDriverAgent port',
1928
1935
  type: 'number',
1929
- required: true,
1930
- defaultValue: DEFAULT_WDA_PORT,
1936
+ required: false,
1931
1937
  placeholder: DEFAULT_WDA_PORT.toString()
1932
1938
  },
1939
+ {
1940
+ key: 'baseUrl',
1941
+ label: 'WebDriverAgent base URL (optional)',
1942
+ type: 'text',
1943
+ required: false,
1944
+ placeholder: 'https://gateway.example/device/wda'
1945
+ },
1933
1946
  {
1934
1947
  key: 'sessionId',
1935
1948
  label: 'WebDriverAgent session ID',
@@ -1941,21 +1954,44 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
1941
1954
  };
1942
1955
  },
1943
1956
  async createSession (input) {
1957
+ const baseUrl = 'string' == typeof input?.baseUrl && input.baseUrl.trim() ? normalizeWebDriverBaseUrl(input.baseUrl.trim()) : void 0;
1958
+ const hasHostInput = input?.host !== void 0 && '' !== String(input.host).trim();
1959
+ const hasPortInput = input?.port !== void 0 && '' !== String(input.port).trim();
1960
+ assertWdaConnectionOptions({
1961
+ ...baseUrl ? {
1962
+ wdaBaseUrl: baseUrl
1963
+ } : {},
1964
+ ...hasHostInput ? {
1965
+ wdaHost: String(input?.host)
1966
+ } : {},
1967
+ ...hasPortInput ? {
1968
+ wdaPort: Number(input?.port)
1969
+ } : {}
1970
+ });
1944
1971
  const host = 'string' == typeof input?.host && input.host.trim() ? input.host.trim().replace(/^https?:\/\//, '') : 'localhost';
1945
- const port = 'number' == typeof input?.port ? input.port : Number.parseInt(String(input?.port ?? DEFAULT_WDA_PORT), 10);
1972
+ const port = hasPortInput ? 'number' == typeof input?.port ? input.port : Number.parseInt(String(input?.port), 10) : DEFAULT_WDA_PORT;
1946
1973
  if (Number.isNaN(port) || port < 1 || port > 65535) throw new Error(`Invalid WebDriverAgent port: ${String(input?.port)}`);
1947
1974
  const sessionId = 'string' == typeof input?.sessionId && input.sessionId.trim() ? input.sessionId.trim() : void 0;
1948
- const connectAgent = async ()=>agentFromWebDriverAgent({
1975
+ const connectAgent = async ()=>{
1976
+ const agentOptions = {
1949
1977
  ...options?.getAgentOptions?.(),
1950
- wdaHost: host,
1951
- wdaPort: port,
1978
+ ...baseUrl ? {
1979
+ wdaBaseUrl: baseUrl
1980
+ } : {
1981
+ wdaHost: host,
1982
+ wdaPort: port
1983
+ },
1952
1984
  ...sessionId ? {
1953
1985
  sessionId
1954
1986
  } : {}
1955
- });
1987
+ };
1988
+ assertWdaConnectionOptions(agentOptions);
1989
+ return agentFromWebDriverAgent(agentOptions);
1990
+ };
1956
1991
  const agent = await connectAgent();
1957
1992
  const deviceInfo = await agent.interface.getConnectedDeviceInfo?.();
1958
- const displayName = deviceInfo ? `${deviceInfo.name} (${deviceInfo.model})` : `${host}:${port}`;
1993
+ const gatewayUrl = baseUrl ? new URL(baseUrl) : void 0;
1994
+ const displayName = deviceInfo ? `${deviceInfo.name} (${deviceInfo.model})` : gatewayUrl ? `${gatewayUrl.host} (WDA gateway)` : `${host}:${port}`;
1959
1995
  return {
1960
1996
  agent,
1961
1997
  agentFactory: connectAgent,
@@ -1964,8 +2000,8 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
1964
2000
  }),
1965
2001
  displayName,
1966
2002
  metadata: {
1967
- wdaHost: host,
1968
- wdaPort: port,
2003
+ wdaHost: gatewayUrl?.hostname ?? host,
2004
+ wdaPort: gatewayUrl ? Number(gatewayUrl.port || ('https:' === gatewayUrl.protocol ? 443 : 80)) : port,
1969
2005
  ...sessionId ? {
1970
2006
  sessionId
1971
2007
  } : {},
package/dist/es/cli.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createReportCliCommands, z } from "@midscene/core";
2
2
  import { reportCLIError, runToolsCLI } from "@midscene/shared/cli";
3
+ import { createHash } from "node:crypto";
3
4
  import { agentBehaviorInitArgShape, getAgentInitArgsSignature, shouldRebuildAgentForInitArgs } from "@midscene/shared/agent-tools/agent-behavior-init-args";
4
5
  import { BaseMidsceneTools } from "@midscene/shared/agent-tools/base-tools";
5
6
  import { getDebug } from "@midscene/shared/logger";
@@ -976,6 +977,9 @@ class MjpegFrameSource {
976
977
  this.nowFn = nowFn;
977
978
  }
978
979
  }
980
+ function assertWdaConnectionOptions(options) {
981
+ if (options?.wdaBaseUrl !== void 0 && (void 0 !== options.wdaHost || void 0 !== options.wdaPort)) throw new Error('wdaBaseUrl cannot be used with wdaHost or wdaPort. Choose one WDA connection method.');
982
+ }
979
983
  function device_define_property(obj, key, value) {
980
984
  if (key in obj) Object.defineProperty(obj, key, {
981
985
  value: value,
@@ -1644,6 +1648,7 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
1644
1648
  scroll: (param)=>this.performActionScroll(param)
1645
1649
  }
1646
1650
  });
1651
+ assertWdaConnectionOptions(options);
1647
1652
  this.deviceId = 'pending-connection';
1648
1653
  this.options = options;
1649
1654
  this.customActions = options?.customActions;
@@ -1653,11 +1658,14 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
1653
1658
  this.wdaBackend = new IOSWebDriverClient({
1654
1659
  port: wdaPort,
1655
1660
  host: wdaHost,
1661
+ ...options?.wdaBaseUrl !== void 0 ? {
1662
+ baseUrl: options.wdaBaseUrl
1663
+ } : {},
1656
1664
  ...options?.sessionId ? {
1657
1665
  sessionId: options.sessionId
1658
1666
  } : {}
1659
1667
  });
1660
- this.wdaManager = WDAManager.getInstance(wdaPort, wdaHost);
1668
+ this.wdaManager = WDAManager.getInstance(wdaPort, wdaHost, options?.wdaBaseUrl);
1661
1669
  this.mjpegStreamUrl = `http://${wdaHost}:${mjpegPort}`;
1662
1670
  if (options?.wdaMjpegFrameSource?.enabled) this.openFrameSource = ()=>this.openMjpegFrameSource();
1663
1671
  }
@@ -1866,6 +1874,7 @@ class IOSAgent extends Agent {
1866
1874
  }
1867
1875
  }
1868
1876
  async function agentFromWebDriverAgent(opts) {
1877
+ assertWdaConnectionOptions(opts);
1869
1878
  debugAgent('Creating iOS agent with WebDriverAgent');
1870
1879
  const overrideModule = opts?.iOSDeviceClassOverride?.trim() || process.env[MIDSCENE_IOS_DEVICE_CLASS_OVERRIDE]?.trim();
1871
1880
  let DeviceClass = IOSDevice;
@@ -1893,6 +1902,7 @@ function agent_tools_define_property(obj, key, value) {
1893
1902
  }
1894
1903
  const agent_tools_debug = getDebug('agent-tools:ios');
1895
1904
  const iosInitArgShape = {
1905
+ wdaBaseUrl: z.string().optional().describe('Full WebDriverAgent API base URL, including gateway path prefix'),
1896
1906
  wdaHost: z.string().optional().describe('WebDriverAgent host, defaults to localhost'),
1897
1907
  wdaPort: z.number().optional().describe('WebDriverAgent port'),
1898
1908
  sessionId: z.string().optional().describe('Existing WebDriverAgent session ID to reuse'),
@@ -1903,6 +1913,10 @@ const iosInitArgShape = {
1903
1913
  ...agentBehaviorInitArgShape
1904
1914
  };
1905
1915
  function getTargetIdentity(initArgs) {
1916
+ if (initArgs?.wdaBaseUrl) {
1917
+ const fingerprint = createHash('sha256').update(`${initArgs.wdaBaseUrl}\0${initArgs.sessionId ?? ''}`).digest('hex').slice(0, 12);
1918
+ return `wda-${fingerprint}`;
1919
+ }
1906
1920
  if (initArgs?.wdaHost || initArgs?.wdaPort || initArgs?.sessionId) {
1907
1921
  const wdaHost = initArgs.wdaHost ?? 'localhost';
1908
1922
  const wdaPort = initArgs.wdaPort ?? 'default';
@@ -1919,6 +1933,7 @@ class IOSMidsceneTools extends BaseMidsceneTools {
1919
1933
  return new IOSDevice({});
1920
1934
  }
1921
1935
  async ensureAgent(opts) {
1936
+ assertWdaConnectionOptions(opts);
1922
1937
  const nextSignature = getAgentInitArgsSignature(opts);
1923
1938
  if (this.agent && shouldRebuildAgentForInitArgs(this.lastOptsSignature, nextSignature)) {
1924
1939
  try {
@@ -1929,7 +1944,7 @@ class IOSMidsceneTools extends BaseMidsceneTools {
1929
1944
  this.agent = void 0;
1930
1945
  }
1931
1946
  if (this.agent) return this.agent;
1932
- agent_tools_debug('Creating iOS agent with WebDriverAgent options:', opts || {});
1947
+ agent_tools_debug('Creating iOS agent with WebDriverAgent options');
1933
1948
  const reportOptions = this.readCliReportAgentOptions();
1934
1949
  this.agent = await agentFromWebDriverAgent({
1935
1950
  autoDismissKeyboard: false,
@@ -1948,6 +1963,7 @@ class IOSMidsceneTools extends BaseMidsceneTools {
1948
1963
  cli: this.getAgentInitArgCliMetadata(),
1949
1964
  handler: async (args)=>{
1950
1965
  const initArgs = this.extractAgentInitParam(args);
1966
+ assertWdaConnectionOptions(initArgs);
1951
1967
  const identity = getTargetIdentity(initArgs);
1952
1968
  const reportSession = this.createNewCliReportSession(identity);
1953
1969
  this.commitCliReportSession(reportSession);
@@ -1996,7 +2012,7 @@ class IOSMidsceneTools extends BaseMidsceneTools {
1996
2012
  const tools = new IOSMidsceneTools();
1997
2013
  runToolsCLI(tools, 'midscene-ios', {
1998
2014
  stripPrefix: 'ios_',
1999
- version: "1.13.3",
2015
+ version: "1.13.4-beta-20260924033639.0",
2000
2016
  extraCommands: createReportCliCommands()
2001
2017
  }).catch((e)=>{
2002
2018
  process.exit(reportCLIError(e));
package/dist/es/index.mjs CHANGED
@@ -6,11 +6,12 @@ import { DEFAULT_WDA_PORT, PLAYGROUND_SERVER_PORT } from "@midscene/shared/const
6
6
  import { createImgBase64ByFormat } from "@midscene/shared/img";
7
7
  import { getDebug } from "@midscene/shared/logger";
8
8
  import { mergeAndNormalizeAppNameMapping, normalizeForComparison } from "@midscene/shared/utils";
9
- import { WDAManager, WebDriverClient } from "@midscene/webdriver";
9
+ import { WDAManager, WebDriverClient, normalizeWebDriverBaseUrl } from "@midscene/webdriver";
10
10
  import { Agent } from "@midscene/core/agent";
11
11
  import { MIDSCENE_IOS_DEVICE_CLASS_OVERRIDE, overrideAIConfig } from "@midscene/shared/env";
12
12
  import { createAgentTestRunnerNodeDefinition } from "@midscene/core/agent/test";
13
13
  import { z as v4_z } from "zod/v4";
14
+ import { createHash } from "node:crypto";
14
15
  import { agentBehaviorInitArgShape, getAgentInitArgsSignature, shouldRebuildAgentForInitArgs } from "@midscene/shared/agent-tools/agent-behavior-init-args";
15
16
  import { BaseMidsceneTools } from "@midscene/shared/agent-tools/base-tools";
16
17
  import { exec } from "node:child_process";
@@ -797,6 +798,9 @@ class MjpegFrameSource {
797
798
  this.nowFn = nowFn;
798
799
  }
799
800
  }
801
+ function assertWdaConnectionOptions(options) {
802
+ if (options?.wdaBaseUrl !== void 0 && (void 0 !== options.wdaHost || void 0 !== options.wdaPort)) throw new Error('wdaBaseUrl cannot be used with wdaHost or wdaPort. Choose one WDA connection method.');
803
+ }
800
804
  function device_define_property(obj, key, value) {
801
805
  if (key in obj) Object.defineProperty(obj, key, {
802
806
  value: value,
@@ -1465,6 +1469,7 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
1465
1469
  scroll: (param)=>this.performActionScroll(param)
1466
1470
  }
1467
1471
  });
1472
+ assertWdaConnectionOptions(options);
1468
1473
  this.deviceId = 'pending-connection';
1469
1474
  this.options = options;
1470
1475
  this.customActions = options?.customActions;
@@ -1474,11 +1479,14 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
1474
1479
  this.wdaBackend = new IOSWebDriverClient({
1475
1480
  port: wdaPort,
1476
1481
  host: wdaHost,
1482
+ ...options?.wdaBaseUrl !== void 0 ? {
1483
+ baseUrl: options.wdaBaseUrl
1484
+ } : {},
1477
1485
  ...options?.sessionId ? {
1478
1486
  sessionId: options.sessionId
1479
1487
  } : {}
1480
1488
  });
1481
- this.wdaManager = WDAManager.getInstance(wdaPort, wdaHost);
1489
+ this.wdaManager = WDAManager.getInstance(wdaPort, wdaHost, options?.wdaBaseUrl);
1482
1490
  this.mjpegStreamUrl = `http://${wdaHost}:${mjpegPort}`;
1483
1491
  if (options?.wdaMjpegFrameSource?.enabled) this.openFrameSource = ()=>this.openMjpegFrameSource();
1484
1492
  }
@@ -1871,6 +1879,7 @@ class IOSAgent extends Agent {
1871
1879
  }
1872
1880
  }
1873
1881
  async function agentFromWebDriverAgent(opts) {
1882
+ assertWdaConnectionOptions(opts);
1874
1883
  debugAgent('Creating iOS agent with WebDriverAgent');
1875
1884
  const overrideModule = opts?.iOSDeviceClassOverride?.trim() || process.env[MIDSCENE_IOS_DEVICE_CLASS_OVERRIDE]?.trim();
1876
1885
  let DeviceClass = IOSDevice;
@@ -1898,6 +1907,7 @@ function agent_tools_define_property(obj, key, value) {
1898
1907
  }
1899
1908
  const agent_tools_debug = getDebug('agent-tools:ios');
1900
1909
  const iosInitArgShape = {
1910
+ wdaBaseUrl: z.string().optional().describe('Full WebDriverAgent API base URL, including gateway path prefix'),
1901
1911
  wdaHost: z.string().optional().describe('WebDriverAgent host, defaults to localhost'),
1902
1912
  wdaPort: z.number().optional().describe('WebDriverAgent port'),
1903
1913
  sessionId: z.string().optional().describe('Existing WebDriverAgent session ID to reuse'),
@@ -1908,6 +1918,10 @@ const iosInitArgShape = {
1908
1918
  ...agentBehaviorInitArgShape
1909
1919
  };
1910
1920
  function getTargetIdentity(initArgs) {
1921
+ if (initArgs?.wdaBaseUrl) {
1922
+ const fingerprint = createHash('sha256').update(`${initArgs.wdaBaseUrl}\0${initArgs.sessionId ?? ''}`).digest('hex').slice(0, 12);
1923
+ return `wda-${fingerprint}`;
1924
+ }
1911
1925
  if (initArgs?.wdaHost || initArgs?.wdaPort || initArgs?.sessionId) {
1912
1926
  const wdaHost = initArgs.wdaHost ?? 'localhost';
1913
1927
  const wdaPort = initArgs.wdaPort ?? 'default';
@@ -1924,6 +1938,7 @@ class IOSMidsceneTools extends BaseMidsceneTools {
1924
1938
  return new IOSDevice({});
1925
1939
  }
1926
1940
  async ensureAgent(opts) {
1941
+ assertWdaConnectionOptions(opts);
1927
1942
  const nextSignature = getAgentInitArgsSignature(opts);
1928
1943
  if (this.agent && shouldRebuildAgentForInitArgs(this.lastOptsSignature, nextSignature)) {
1929
1944
  try {
@@ -1934,7 +1949,7 @@ class IOSMidsceneTools extends BaseMidsceneTools {
1934
1949
  this.agent = void 0;
1935
1950
  }
1936
1951
  if (this.agent) return this.agent;
1937
- agent_tools_debug('Creating iOS agent with WebDriverAgent options:', opts || {});
1952
+ agent_tools_debug('Creating iOS agent with WebDriverAgent options');
1938
1953
  const reportOptions = this.readCliReportAgentOptions();
1939
1954
  this.agent = await agentFromWebDriverAgent({
1940
1955
  autoDismissKeyboard: false,
@@ -1953,6 +1968,7 @@ class IOSMidsceneTools extends BaseMidsceneTools {
1953
1968
  cli: this.getAgentInitArgCliMetadata(),
1954
1969
  handler: async (args)=>{
1955
1970
  const initArgs = this.extractAgentInitParam(args);
1971
+ assertWdaConnectionOptions(initArgs);
1956
1972
  const identity = getTargetIdentity(initArgs);
1957
1973
  const reportSession = this.createNewCliReportSession(identity);
1958
1974
  this.commitCliReportSession(reportSession);
@@ -2073,7 +2089,7 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
2073
2089
  const wdaReady = await probeWdaReady('localhost', DEFAULT_WDA_PORT);
2074
2090
  return {
2075
2091
  title: 'Connect WebDriverAgent',
2076
- description: 'Provide the WebDriverAgent host and port that are already running for your selected iPhone or simulator.',
2092
+ description: 'Provide a WebDriverAgent base URL, or the host and port for your selected iPhone or simulator.',
2077
2093
  primaryActionLabel: 'Create Agent',
2078
2094
  autoSubmitWhenReady: wdaReady,
2079
2095
  fields: [
@@ -2081,18 +2097,23 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
2081
2097
  key: 'host',
2082
2098
  label: 'WebDriverAgent host',
2083
2099
  type: 'text',
2084
- required: true,
2085
- defaultValue: 'localhost',
2100
+ required: false,
2086
2101
  placeholder: 'localhost'
2087
2102
  },
2088
2103
  {
2089
2104
  key: 'port',
2090
2105
  label: 'WebDriverAgent port',
2091
2106
  type: 'number',
2092
- required: true,
2093
- defaultValue: DEFAULT_WDA_PORT,
2107
+ required: false,
2094
2108
  placeholder: DEFAULT_WDA_PORT.toString()
2095
2109
  },
2110
+ {
2111
+ key: 'baseUrl',
2112
+ label: 'WebDriverAgent base URL (optional)',
2113
+ type: 'text',
2114
+ required: false,
2115
+ placeholder: 'https://gateway.example/device/wda'
2116
+ },
2096
2117
  {
2097
2118
  key: 'sessionId',
2098
2119
  label: 'WebDriverAgent session ID',
@@ -2104,21 +2125,44 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
2104
2125
  };
2105
2126
  },
2106
2127
  async createSession (input) {
2128
+ const baseUrl = 'string' == typeof input?.baseUrl && input.baseUrl.trim() ? normalizeWebDriverBaseUrl(input.baseUrl.trim()) : void 0;
2129
+ const hasHostInput = input?.host !== void 0 && '' !== String(input.host).trim();
2130
+ const hasPortInput = input?.port !== void 0 && '' !== String(input.port).trim();
2131
+ assertWdaConnectionOptions({
2132
+ ...baseUrl ? {
2133
+ wdaBaseUrl: baseUrl
2134
+ } : {},
2135
+ ...hasHostInput ? {
2136
+ wdaHost: String(input?.host)
2137
+ } : {},
2138
+ ...hasPortInput ? {
2139
+ wdaPort: Number(input?.port)
2140
+ } : {}
2141
+ });
2107
2142
  const host = 'string' == typeof input?.host && input.host.trim() ? input.host.trim().replace(/^https?:\/\//, '') : 'localhost';
2108
- const port = 'number' == typeof input?.port ? input.port : Number.parseInt(String(input?.port ?? DEFAULT_WDA_PORT), 10);
2143
+ const port = hasPortInput ? 'number' == typeof input?.port ? input.port : Number.parseInt(String(input?.port), 10) : DEFAULT_WDA_PORT;
2109
2144
  if (Number.isNaN(port) || port < 1 || port > 65535) throw new Error(`Invalid WebDriverAgent port: ${String(input?.port)}`);
2110
2145
  const sessionId = 'string' == typeof input?.sessionId && input.sessionId.trim() ? input.sessionId.trim() : void 0;
2111
- const connectAgent = async ()=>agentFromWebDriverAgent({
2146
+ const connectAgent = async ()=>{
2147
+ const agentOptions = {
2112
2148
  ...options?.getAgentOptions?.(),
2113
- wdaHost: host,
2114
- wdaPort: port,
2149
+ ...baseUrl ? {
2150
+ wdaBaseUrl: baseUrl
2151
+ } : {
2152
+ wdaHost: host,
2153
+ wdaPort: port
2154
+ },
2115
2155
  ...sessionId ? {
2116
2156
  sessionId
2117
2157
  } : {}
2118
- });
2158
+ };
2159
+ assertWdaConnectionOptions(agentOptions);
2160
+ return agentFromWebDriverAgent(agentOptions);
2161
+ };
2119
2162
  const agent = await connectAgent();
2120
2163
  const deviceInfo = await agent.interface.getConnectedDeviceInfo?.();
2121
- const displayName = deviceInfo ? `${deviceInfo.name} (${deviceInfo.model})` : `${host}:${port}`;
2164
+ const gatewayUrl = baseUrl ? new URL(baseUrl) : void 0;
2165
+ const displayName = deviceInfo ? `${deviceInfo.name} (${deviceInfo.model})` : gatewayUrl ? `${gatewayUrl.host} (WDA gateway)` : `${host}:${port}`;
2122
2166
  return {
2123
2167
  agent,
2124
2168
  agentFactory: connectAgent,
@@ -2127,8 +2171,8 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
2127
2171
  }),
2128
2172
  displayName,
2129
2173
  metadata: {
2130
- wdaHost: host,
2131
- wdaPort: port,
2174
+ wdaHost: gatewayUrl?.hostname ?? host,
2175
+ wdaPort: gatewayUrl ? Number(gatewayUrl.port || ('https:' === gatewayUrl.protocol ? 443 : 80)) : port,
2132
2176
  ...sessionId ? {
2133
2177
  sessionId
2134
2178
  } : {},
package/dist/lib/bin.js CHANGED
@@ -141,6 +141,7 @@ var __webpack_exports__ = {};
141
141
  const playground_namespaceObject = require("@midscene/playground");
142
142
  const constants_namespaceObject = require("@midscene/shared/constants");
143
143
  const node_namespaceObject = require("@midscene/shared/node");
144
+ const webdriver_namespaceObject = require("@midscene/webdriver");
144
145
  const agent_namespaceObject = require("@midscene/core/agent");
145
146
  const env_namespaceObject = require("@midscene/shared/env");
146
147
  const logger_namespaceObject = require("@midscene/shared/logger");
@@ -335,7 +336,6 @@ var __webpack_exports__ = {};
335
336
  const device_namespaceObject = require("@midscene/core/device");
336
337
  const core_utils_namespaceObject = require("@midscene/core/utils");
337
338
  const img_namespaceObject = require("@midscene/shared/img");
338
- const webdriver_namespaceObject = require("@midscene/webdriver");
339
339
  const keyboardAccessoryToolbarGeometry = {
340
340
  minHeight: 24,
341
341
  maxHeight: 80,
@@ -1114,6 +1114,9 @@ var __webpack_exports__ = {};
1114
1114
  this.nowFn = nowFn;
1115
1115
  }
1116
1116
  }
1117
+ function assertWdaConnectionOptions(options) {
1118
+ if (options?.wdaBaseUrl !== void 0 && (void 0 !== options.wdaHost || void 0 !== options.wdaPort)) throw new Error('wdaBaseUrl cannot be used with wdaHost or wdaPort. Choose one WDA connection method.');
1119
+ }
1117
1120
  function device_define_property(obj, key, value) {
1118
1121
  if (key in obj) Object.defineProperty(obj, key, {
1119
1122
  value: value,
@@ -1782,6 +1785,7 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
1782
1785
  scroll: (param)=>this.performActionScroll(param)
1783
1786
  }
1784
1787
  });
1788
+ assertWdaConnectionOptions(options);
1785
1789
  this.deviceId = 'pending-connection';
1786
1790
  this.options = options;
1787
1791
  this.customActions = options?.customActions;
@@ -1791,11 +1795,14 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
1791
1795
  this.wdaBackend = new IOSWebDriverClient({
1792
1796
  port: wdaPort,
1793
1797
  host: wdaHost,
1798
+ ...options?.wdaBaseUrl !== void 0 ? {
1799
+ baseUrl: options.wdaBaseUrl
1800
+ } : {},
1794
1801
  ...options?.sessionId ? {
1795
1802
  sessionId: options.sessionId
1796
1803
  } : {}
1797
1804
  });
1798
- this.wdaManager = webdriver_namespaceObject.WDAManager.getInstance(wdaPort, wdaHost);
1805
+ this.wdaManager = webdriver_namespaceObject.WDAManager.getInstance(wdaPort, wdaHost, options?.wdaBaseUrl);
1799
1806
  this.mjpegStreamUrl = `http://${wdaHost}:${mjpegPort}`;
1800
1807
  if (options?.wdaMjpegFrameSource?.enabled) this.openFrameSource = ()=>this.openMjpegFrameSource();
1801
1808
  }
@@ -1909,6 +1916,7 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
1909
1916
  }
1910
1917
  }
1911
1918
  async function agentFromWebDriverAgent(opts) {
1919
+ assertWdaConnectionOptions(opts);
1912
1920
  debugAgent('Creating iOS agent with WebDriverAgent');
1913
1921
  const overrideModule = opts?.iOSDeviceClassOverride?.trim() || process.env[env_namespaceObject.MIDSCENE_IOS_DEVICE_CLASS_OVERRIDE]?.trim();
1914
1922
  let DeviceClass = IOSDevice;
@@ -1953,7 +1961,7 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
1953
1961
  const wdaReady = await probeWdaReady('localhost', constants_namespaceObject.DEFAULT_WDA_PORT);
1954
1962
  return {
1955
1963
  title: 'Connect WebDriverAgent',
1956
- description: 'Provide the WebDriverAgent host and port that are already running for your selected iPhone or simulator.',
1964
+ description: 'Provide a WebDriverAgent base URL, or the host and port for your selected iPhone or simulator.',
1957
1965
  primaryActionLabel: 'Create Agent',
1958
1966
  autoSubmitWhenReady: wdaReady,
1959
1967
  fields: [
@@ -1961,18 +1969,23 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
1961
1969
  key: 'host',
1962
1970
  label: 'WebDriverAgent host',
1963
1971
  type: 'text',
1964
- required: true,
1965
- defaultValue: 'localhost',
1972
+ required: false,
1966
1973
  placeholder: 'localhost'
1967
1974
  },
1968
1975
  {
1969
1976
  key: 'port',
1970
1977
  label: 'WebDriverAgent port',
1971
1978
  type: 'number',
1972
- required: true,
1973
- defaultValue: constants_namespaceObject.DEFAULT_WDA_PORT,
1979
+ required: false,
1974
1980
  placeholder: constants_namespaceObject.DEFAULT_WDA_PORT.toString()
1975
1981
  },
1982
+ {
1983
+ key: 'baseUrl',
1984
+ label: 'WebDriverAgent base URL (optional)',
1985
+ type: 'text',
1986
+ required: false,
1987
+ placeholder: 'https://gateway.example/device/wda'
1988
+ },
1976
1989
  {
1977
1990
  key: 'sessionId',
1978
1991
  label: 'WebDriverAgent session ID',
@@ -1984,21 +1997,44 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
1984
1997
  };
1985
1998
  },
1986
1999
  async createSession (input) {
2000
+ const baseUrl = 'string' == typeof input?.baseUrl && input.baseUrl.trim() ? (0, webdriver_namespaceObject.normalizeWebDriverBaseUrl)(input.baseUrl.trim()) : void 0;
2001
+ const hasHostInput = input?.host !== void 0 && '' !== String(input.host).trim();
2002
+ const hasPortInput = input?.port !== void 0 && '' !== String(input.port).trim();
2003
+ assertWdaConnectionOptions({
2004
+ ...baseUrl ? {
2005
+ wdaBaseUrl: baseUrl
2006
+ } : {},
2007
+ ...hasHostInput ? {
2008
+ wdaHost: String(input?.host)
2009
+ } : {},
2010
+ ...hasPortInput ? {
2011
+ wdaPort: Number(input?.port)
2012
+ } : {}
2013
+ });
1987
2014
  const host = 'string' == typeof input?.host && input.host.trim() ? input.host.trim().replace(/^https?:\/\//, '') : 'localhost';
1988
- const port = 'number' == typeof input?.port ? input.port : Number.parseInt(String(input?.port ?? constants_namespaceObject.DEFAULT_WDA_PORT), 10);
2015
+ const port = hasPortInput ? 'number' == typeof input?.port ? input.port : Number.parseInt(String(input?.port), 10) : constants_namespaceObject.DEFAULT_WDA_PORT;
1989
2016
  if (Number.isNaN(port) || port < 1 || port > 65535) throw new Error(`Invalid WebDriverAgent port: ${String(input?.port)}`);
1990
2017
  const sessionId = 'string' == typeof input?.sessionId && input.sessionId.trim() ? input.sessionId.trim() : void 0;
1991
- const connectAgent = async ()=>agentFromWebDriverAgent({
2018
+ const connectAgent = async ()=>{
2019
+ const agentOptions = {
1992
2020
  ...options?.getAgentOptions?.(),
1993
- wdaHost: host,
1994
- wdaPort: port,
2021
+ ...baseUrl ? {
2022
+ wdaBaseUrl: baseUrl
2023
+ } : {
2024
+ wdaHost: host,
2025
+ wdaPort: port
2026
+ },
1995
2027
  ...sessionId ? {
1996
2028
  sessionId
1997
2029
  } : {}
1998
- });
2030
+ };
2031
+ assertWdaConnectionOptions(agentOptions);
2032
+ return agentFromWebDriverAgent(agentOptions);
2033
+ };
1999
2034
  const agent = await connectAgent();
2000
2035
  const deviceInfo = await agent.interface.getConnectedDeviceInfo?.();
2001
- const displayName = deviceInfo ? `${deviceInfo.name} (${deviceInfo.model})` : `${host}:${port}`;
2036
+ const gatewayUrl = baseUrl ? new URL(baseUrl) : void 0;
2037
+ const displayName = deviceInfo ? `${deviceInfo.name} (${deviceInfo.model})` : gatewayUrl ? `${gatewayUrl.host} (WDA gateway)` : `${host}:${port}`;
2002
2038
  return {
2003
2039
  agent,
2004
2040
  agentFactory: connectAgent,
@@ -2007,8 +2043,8 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
2007
2043
  }),
2008
2044
  displayName,
2009
2045
  metadata: {
2010
- wdaHost: host,
2011
- wdaPort: port,
2046
+ wdaHost: gatewayUrl?.hostname ?? host,
2047
+ wdaPort: gatewayUrl ? Number(gatewayUrl.port || ('https:' === gatewayUrl.protocol ? 443 : 80)) : port,
2012
2048
  ...sessionId ? {
2013
2049
  sessionId
2014
2050
  } : {},