@midscene/ios 1.8.4-beta-20260521070317.0 → 1.8.4
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 +45 -3
- package/dist/es/cli.mjs +25 -4
- package/dist/es/index.mjs +57 -4
- package/dist/es/mcp-server.mjs +25 -4
- package/dist/lib/bin.js +45 -3
- package/dist/lib/cli.js +25 -4
- package/dist/lib/index.js +57 -4
- package/dist/lib/mcp-server.js +25 -4
- package/dist/types/index.d.ts +2 -1
- package/dist/types/mcp-server.d.ts +1 -1
- package/package.json +5 -5
- package/static/index.html +1 -1
- package/static/static/js/{index.0e39e81b.js → index.c3c091dd.js} +58 -46
- package/static/static/js/index.c3c091dd.js.map +1 -0
- package/static/static/js/index.0e39e81b.js.map +0 -1
- /package/static/static/js/{index.0e39e81b.js.LICENSE.txt → index.c3c091dd.js.LICENSE.txt} +0 -0
package/dist/es/bin.mjs
CHANGED
|
@@ -635,6 +635,10 @@ class IOSWebDriverClient extends WebDriverClient {
|
|
|
635
635
|
debugIOS(`Failed to apply iOS session configuration: ${error}`);
|
|
636
636
|
}
|
|
637
637
|
}
|
|
638
|
+
async setupExistingSession() {
|
|
639
|
+
this.ensureSession();
|
|
640
|
+
await this.setupIOSSession();
|
|
641
|
+
}
|
|
638
642
|
async executeRequest(method, endpoint, data) {
|
|
639
643
|
return this.makeRequest(method, this.buildSessionEndpoint(endpoint), data);
|
|
640
644
|
}
|
|
@@ -732,7 +736,10 @@ class IOSDevice {
|
|
|
732
736
|
debugDevice(`Connecting to iOS device: ${this.deviceId}`);
|
|
733
737
|
try {
|
|
734
738
|
await this.wdaManager.start();
|
|
735
|
-
|
|
739
|
+
if (this.options?.sessionId) {
|
|
740
|
+
debugDevice(`Using existing WDA session: ${this.options.sessionId}`);
|
|
741
|
+
await this.wdaBackend.setupExistingSession();
|
|
742
|
+
} else await this.wdaBackend.createSession();
|
|
736
743
|
const deviceInfo = await this.wdaBackend.getDeviceInfo();
|
|
737
744
|
if (deviceInfo?.udid) {
|
|
738
745
|
this.deviceId = deviceInfo.udid;
|
|
@@ -1207,7 +1214,10 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
1207
1214
|
const mjpegPort = options?.wdaMjpegPort ?? DEFAULT_WDA_MJPEG_PORT;
|
|
1208
1215
|
this.wdaBackend = new IOSWebDriverClient({
|
|
1209
1216
|
port: wdaPort,
|
|
1210
|
-
host: wdaHost
|
|
1217
|
+
host: wdaHost,
|
|
1218
|
+
...options?.sessionId ? {
|
|
1219
|
+
sessionId: options.sessionId
|
|
1220
|
+
} : {}
|
|
1211
1221
|
});
|
|
1212
1222
|
this.wdaManager = WDAManager.getInstance(wdaPort, wdaHost);
|
|
1213
1223
|
this.mjpegStreamUrl = `http://${wdaHost}:${mjpegPort}`;
|
|
@@ -1334,6 +1344,22 @@ async function agentFromWebDriverAgent(opts) {
|
|
|
1334
1344
|
await device.connect();
|
|
1335
1345
|
return new IOSAgent(device, opts);
|
|
1336
1346
|
}
|
|
1347
|
+
async function probeWdaReady(host, port, timeoutMs = 800) {
|
|
1348
|
+
const controller = new AbortController();
|
|
1349
|
+
const timer = setTimeout(()=>controller.abort(), timeoutMs);
|
|
1350
|
+
try {
|
|
1351
|
+
const res = await fetch(`http://${host}:${port}/status`, {
|
|
1352
|
+
signal: controller.signal
|
|
1353
|
+
});
|
|
1354
|
+
if (!res.ok) return false;
|
|
1355
|
+
const body = await res.json().catch(()=>null);
|
|
1356
|
+
return body?.value?.ready === true;
|
|
1357
|
+
} catch {
|
|
1358
|
+
return false;
|
|
1359
|
+
} finally{
|
|
1360
|
+
clearTimeout(timer);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1337
1363
|
const iosPlaygroundPlatform = definePlaygroundPlatform({
|
|
1338
1364
|
id: 'ios',
|
|
1339
1365
|
title: 'Midscene iOS Playground',
|
|
@@ -1344,10 +1370,12 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
1344
1370
|
if (availablePlaygroundPort !== PLAYGROUND_SERVER_PORT) console.log(`⚠️ Port ${PLAYGROUND_SERVER_PORT} is busy, using port ${availablePlaygroundPort} instead`);
|
|
1345
1371
|
const sessionManager = {
|
|
1346
1372
|
async getSetupSchema () {
|
|
1373
|
+
const wdaReady = await probeWdaReady('localhost', DEFAULT_WDA_PORT);
|
|
1347
1374
|
return {
|
|
1348
1375
|
title: 'Connect WebDriverAgent',
|
|
1349
1376
|
description: 'Provide the WebDriverAgent host and port that are already running for your selected iPhone or simulator.',
|
|
1350
1377
|
primaryActionLabel: 'Create Agent',
|
|
1378
|
+
autoSubmitWhenReady: wdaReady,
|
|
1351
1379
|
fields: [
|
|
1352
1380
|
{
|
|
1353
1381
|
key: 'host',
|
|
@@ -1364,6 +1392,13 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
1364
1392
|
required: true,
|
|
1365
1393
|
defaultValue: DEFAULT_WDA_PORT,
|
|
1366
1394
|
placeholder: DEFAULT_WDA_PORT.toString()
|
|
1395
|
+
},
|
|
1396
|
+
{
|
|
1397
|
+
key: 'sessionId',
|
|
1398
|
+
label: 'WebDriverAgent session ID',
|
|
1399
|
+
type: 'text',
|
|
1400
|
+
required: false,
|
|
1401
|
+
placeholder: 'Existing session ID'
|
|
1367
1402
|
}
|
|
1368
1403
|
]
|
|
1369
1404
|
};
|
|
@@ -1372,9 +1407,13 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
1372
1407
|
const host = 'string' == typeof input?.host && input.host.trim() ? input.host.trim().replace(/^https?:\/\//, '') : 'localhost';
|
|
1373
1408
|
const port = 'number' == typeof input?.port ? input.port : Number.parseInt(String(input?.port ?? DEFAULT_WDA_PORT), 10);
|
|
1374
1409
|
if (Number.isNaN(port) || port < 1 || port > 65535) throw new Error(`Invalid WebDriverAgent port: ${String(input?.port)}`);
|
|
1410
|
+
const sessionId = 'string' == typeof input?.sessionId && input.sessionId.trim() ? input.sessionId.trim() : void 0;
|
|
1375
1411
|
const connectAgent = async ()=>agentFromWebDriverAgent({
|
|
1376
1412
|
wdaHost: host,
|
|
1377
|
-
wdaPort: port
|
|
1413
|
+
wdaPort: port,
|
|
1414
|
+
...sessionId ? {
|
|
1415
|
+
sessionId
|
|
1416
|
+
} : {}
|
|
1378
1417
|
});
|
|
1379
1418
|
const agent = await connectAgent();
|
|
1380
1419
|
const deviceInfo = await agent.interface.getConnectedDeviceInfo?.();
|
|
@@ -1389,6 +1428,9 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
1389
1428
|
metadata: {
|
|
1390
1429
|
wdaHost: host,
|
|
1391
1430
|
wdaPort: port,
|
|
1431
|
+
...sessionId ? {
|
|
1432
|
+
sessionId
|
|
1433
|
+
} : {},
|
|
1392
1434
|
...deviceInfo ? {
|
|
1393
1435
|
deviceInfo
|
|
1394
1436
|
} : {}
|
package/dist/es/cli.mjs
CHANGED
|
@@ -634,6 +634,10 @@ class IOSWebDriverClient extends WebDriverClient {
|
|
|
634
634
|
debugIOS(`Failed to apply iOS session configuration: ${error}`);
|
|
635
635
|
}
|
|
636
636
|
}
|
|
637
|
+
async setupExistingSession() {
|
|
638
|
+
this.ensureSession();
|
|
639
|
+
await this.setupIOSSession();
|
|
640
|
+
}
|
|
637
641
|
async executeRequest(method, endpoint, data) {
|
|
638
642
|
return this.makeRequest(method, this.buildSessionEndpoint(endpoint), data);
|
|
639
643
|
}
|
|
@@ -731,7 +735,10 @@ class IOSDevice {
|
|
|
731
735
|
debugDevice(`Connecting to iOS device: ${this.deviceId}`);
|
|
732
736
|
try {
|
|
733
737
|
await this.wdaManager.start();
|
|
734
|
-
|
|
738
|
+
if (this.options?.sessionId) {
|
|
739
|
+
debugDevice(`Using existing WDA session: ${this.options.sessionId}`);
|
|
740
|
+
await this.wdaBackend.setupExistingSession();
|
|
741
|
+
} else await this.wdaBackend.createSession();
|
|
735
742
|
const deviceInfo = await this.wdaBackend.getDeviceInfo();
|
|
736
743
|
if (deviceInfo?.udid) {
|
|
737
744
|
this.deviceId = deviceInfo.udid;
|
|
@@ -1206,7 +1213,10 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
1206
1213
|
const mjpegPort = options?.wdaMjpegPort ?? DEFAULT_WDA_MJPEG_PORT;
|
|
1207
1214
|
this.wdaBackend = new IOSWebDriverClient({
|
|
1208
1215
|
port: wdaPort,
|
|
1209
|
-
host: wdaHost
|
|
1216
|
+
host: wdaHost,
|
|
1217
|
+
...options?.sessionId ? {
|
|
1218
|
+
sessionId: options.sessionId
|
|
1219
|
+
} : {}
|
|
1210
1220
|
});
|
|
1211
1221
|
this.wdaManager = WDAManager.getInstance(wdaPort, wdaHost);
|
|
1212
1222
|
this.mjpegStreamUrl = `http://${wdaHost}:${mjpegPort}`;
|
|
@@ -1348,9 +1358,20 @@ const iosInitArgShape = {
|
|
|
1348
1358
|
deviceId: z.string().optional().describe('iOS device UDID (optional when WDA auto-detect is sufficient)'),
|
|
1349
1359
|
wdaHost: z.string().optional().describe('WebDriverAgent host, defaults to localhost'),
|
|
1350
1360
|
wdaPort: z.number().optional().describe('WebDriverAgent port'),
|
|
1361
|
+
sessionId: z.string().optional().describe('Existing WebDriverAgent session ID to reuse'),
|
|
1351
1362
|
useWDA: z.boolean().optional().describe('Whether to reuse an existing WebDriverAgent session'),
|
|
1352
1363
|
wdaMjpegPort: z.number().optional().describe('WebDriverAgent MJPEG streaming port')
|
|
1353
1364
|
};
|
|
1365
|
+
function getTargetIdentity(initArgs) {
|
|
1366
|
+
if (initArgs?.deviceId) return initArgs.sessionId ? `${initArgs.deviceId}-session-${initArgs.sessionId}` : initArgs.deviceId;
|
|
1367
|
+
if (initArgs?.wdaHost || initArgs?.wdaPort || initArgs?.sessionId) {
|
|
1368
|
+
const wdaHost = initArgs.wdaHost ?? 'localhost';
|
|
1369
|
+
const wdaPort = initArgs.wdaPort ?? 'default';
|
|
1370
|
+
const sessionSegment = initArgs.sessionId ? `-session-${initArgs.sessionId}` : '';
|
|
1371
|
+
return `wda-${wdaHost}-${wdaPort}${sessionSegment}`;
|
|
1372
|
+
}
|
|
1373
|
+
return 'auto';
|
|
1374
|
+
}
|
|
1354
1375
|
class IOSMidsceneTools extends BaseMidsceneTools {
|
|
1355
1376
|
getCliReportSessionName() {
|
|
1356
1377
|
return 'midscene-ios';
|
|
@@ -1389,7 +1410,7 @@ class IOSMidsceneTools extends BaseMidsceneTools {
|
|
|
1389
1410
|
cli: this.getAgentInitArgCliMetadata(),
|
|
1390
1411
|
handler: async (args)=>{
|
|
1391
1412
|
const initArgs = this.extractAgentInitParam(args);
|
|
1392
|
-
const identity =
|
|
1413
|
+
const identity = getTargetIdentity(initArgs);
|
|
1393
1414
|
const reportSession = this.createNewCliReportSession(identity);
|
|
1394
1415
|
this.commitCliReportSession(reportSession);
|
|
1395
1416
|
if (this.agent) {
|
|
@@ -1436,7 +1457,7 @@ class IOSMidsceneTools extends BaseMidsceneTools {
|
|
|
1436
1457
|
const tools = new IOSMidsceneTools();
|
|
1437
1458
|
runToolsCLI(tools, 'midscene-ios', {
|
|
1438
1459
|
stripPrefix: 'ios_',
|
|
1439
|
-
version: "1.8.4
|
|
1460
|
+
version: "1.8.4",
|
|
1440
1461
|
extraCommands: createReportCliCommands()
|
|
1441
1462
|
}).catch((e)=>{
|
|
1442
1463
|
process.exit(reportCLIError(e));
|
package/dist/es/index.mjs
CHANGED
|
@@ -455,6 +455,10 @@ class IOSWebDriverClient extends WebDriverClient {
|
|
|
455
455
|
debugIOS(`Failed to apply iOS session configuration: ${error}`);
|
|
456
456
|
}
|
|
457
457
|
}
|
|
458
|
+
async setupExistingSession() {
|
|
459
|
+
this.ensureSession();
|
|
460
|
+
await this.setupIOSSession();
|
|
461
|
+
}
|
|
458
462
|
async executeRequest(method, endpoint, data) {
|
|
459
463
|
return this.makeRequest(method, this.buildSessionEndpoint(endpoint), data);
|
|
460
464
|
}
|
|
@@ -552,7 +556,10 @@ class IOSDevice {
|
|
|
552
556
|
debugDevice(`Connecting to iOS device: ${this.deviceId}`);
|
|
553
557
|
try {
|
|
554
558
|
await this.wdaManager.start();
|
|
555
|
-
|
|
559
|
+
if (this.options?.sessionId) {
|
|
560
|
+
debugDevice(`Using existing WDA session: ${this.options.sessionId}`);
|
|
561
|
+
await this.wdaBackend.setupExistingSession();
|
|
562
|
+
} else await this.wdaBackend.createSession();
|
|
556
563
|
const deviceInfo = await this.wdaBackend.getDeviceInfo();
|
|
557
564
|
if (deviceInfo?.udid) {
|
|
558
565
|
this.deviceId = deviceInfo.udid;
|
|
@@ -1027,7 +1034,10 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
1027
1034
|
const mjpegPort = options?.wdaMjpegPort ?? DEFAULT_WDA_MJPEG_PORT;
|
|
1028
1035
|
this.wdaBackend = new IOSWebDriverClient({
|
|
1029
1036
|
port: wdaPort,
|
|
1030
|
-
host: wdaHost
|
|
1037
|
+
host: wdaHost,
|
|
1038
|
+
...options?.sessionId ? {
|
|
1039
|
+
sessionId: options.sessionId
|
|
1040
|
+
} : {}
|
|
1031
1041
|
});
|
|
1032
1042
|
this.wdaManager = WDAManager.getInstance(wdaPort, wdaHost);
|
|
1033
1043
|
this.mjpegStreamUrl = `http://${wdaHost}:${mjpegPort}`;
|
|
@@ -1353,9 +1363,20 @@ const iosInitArgShape = {
|
|
|
1353
1363
|
deviceId: z.string().optional().describe('iOS device UDID (optional when WDA auto-detect is sufficient)'),
|
|
1354
1364
|
wdaHost: z.string().optional().describe('WebDriverAgent host, defaults to localhost'),
|
|
1355
1365
|
wdaPort: z.number().optional().describe('WebDriverAgent port'),
|
|
1366
|
+
sessionId: z.string().optional().describe('Existing WebDriverAgent session ID to reuse'),
|
|
1356
1367
|
useWDA: z.boolean().optional().describe('Whether to reuse an existing WebDriverAgent session'),
|
|
1357
1368
|
wdaMjpegPort: z.number().optional().describe('WebDriverAgent MJPEG streaming port')
|
|
1358
1369
|
};
|
|
1370
|
+
function getTargetIdentity(initArgs) {
|
|
1371
|
+
if (initArgs?.deviceId) return initArgs.sessionId ? `${initArgs.deviceId}-session-${initArgs.sessionId}` : initArgs.deviceId;
|
|
1372
|
+
if (initArgs?.wdaHost || initArgs?.wdaPort || initArgs?.sessionId) {
|
|
1373
|
+
const wdaHost = initArgs.wdaHost ?? 'localhost';
|
|
1374
|
+
const wdaPort = initArgs.wdaPort ?? 'default';
|
|
1375
|
+
const sessionSegment = initArgs.sessionId ? `-session-${initArgs.sessionId}` : '';
|
|
1376
|
+
return `wda-${wdaHost}-${wdaPort}${sessionSegment}`;
|
|
1377
|
+
}
|
|
1378
|
+
return 'auto';
|
|
1379
|
+
}
|
|
1359
1380
|
class IOSMidsceneTools extends BaseMidsceneTools {
|
|
1360
1381
|
getCliReportSessionName() {
|
|
1361
1382
|
return 'midscene-ios';
|
|
@@ -1394,7 +1415,7 @@ class IOSMidsceneTools extends BaseMidsceneTools {
|
|
|
1394
1415
|
cli: this.getAgentInitArgCliMetadata(),
|
|
1395
1416
|
handler: async (args)=>{
|
|
1396
1417
|
const initArgs = this.extractAgentInitParam(args);
|
|
1397
|
-
const identity =
|
|
1418
|
+
const identity = getTargetIdentity(initArgs);
|
|
1398
1419
|
const reportSession = this.createNewCliReportSession(identity);
|
|
1399
1420
|
this.commitCliReportSession(reportSession);
|
|
1400
1421
|
if (this.agent) {
|
|
@@ -1484,6 +1505,22 @@ async function checkIOSEnvironment() {
|
|
|
1484
1505
|
};
|
|
1485
1506
|
}
|
|
1486
1507
|
}
|
|
1508
|
+
async function probeWdaReady(host, port, timeoutMs = 800) {
|
|
1509
|
+
const controller = new AbortController();
|
|
1510
|
+
const timer = setTimeout(()=>controller.abort(), timeoutMs);
|
|
1511
|
+
try {
|
|
1512
|
+
const res = await fetch(`http://${host}:${port}/status`, {
|
|
1513
|
+
signal: controller.signal
|
|
1514
|
+
});
|
|
1515
|
+
if (!res.ok) return false;
|
|
1516
|
+
const body = await res.json().catch(()=>null);
|
|
1517
|
+
return body?.value?.ready === true;
|
|
1518
|
+
} catch {
|
|
1519
|
+
return false;
|
|
1520
|
+
} finally{
|
|
1521
|
+
clearTimeout(timer);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1487
1524
|
const iosPlaygroundPlatform = definePlaygroundPlatform({
|
|
1488
1525
|
id: 'ios',
|
|
1489
1526
|
title: 'Midscene iOS Playground',
|
|
@@ -1494,10 +1531,12 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
1494
1531
|
if (availablePlaygroundPort !== PLAYGROUND_SERVER_PORT) console.log(`⚠️ Port ${PLAYGROUND_SERVER_PORT} is busy, using port ${availablePlaygroundPort} instead`);
|
|
1495
1532
|
const sessionManager = {
|
|
1496
1533
|
async getSetupSchema () {
|
|
1534
|
+
const wdaReady = await probeWdaReady('localhost', DEFAULT_WDA_PORT);
|
|
1497
1535
|
return {
|
|
1498
1536
|
title: 'Connect WebDriverAgent',
|
|
1499
1537
|
description: 'Provide the WebDriverAgent host and port that are already running for your selected iPhone or simulator.',
|
|
1500
1538
|
primaryActionLabel: 'Create Agent',
|
|
1539
|
+
autoSubmitWhenReady: wdaReady,
|
|
1501
1540
|
fields: [
|
|
1502
1541
|
{
|
|
1503
1542
|
key: 'host',
|
|
@@ -1514,6 +1553,13 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
1514
1553
|
required: true,
|
|
1515
1554
|
defaultValue: DEFAULT_WDA_PORT,
|
|
1516
1555
|
placeholder: DEFAULT_WDA_PORT.toString()
|
|
1556
|
+
},
|
|
1557
|
+
{
|
|
1558
|
+
key: 'sessionId',
|
|
1559
|
+
label: 'WebDriverAgent session ID',
|
|
1560
|
+
type: 'text',
|
|
1561
|
+
required: false,
|
|
1562
|
+
placeholder: 'Existing session ID'
|
|
1517
1563
|
}
|
|
1518
1564
|
]
|
|
1519
1565
|
};
|
|
@@ -1522,9 +1568,13 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
1522
1568
|
const host = 'string' == typeof input?.host && input.host.trim() ? input.host.trim().replace(/^https?:\/\//, '') : 'localhost';
|
|
1523
1569
|
const port = 'number' == typeof input?.port ? input.port : Number.parseInt(String(input?.port ?? DEFAULT_WDA_PORT), 10);
|
|
1524
1570
|
if (Number.isNaN(port) || port < 1 || port > 65535) throw new Error(`Invalid WebDriverAgent port: ${String(input?.port)}`);
|
|
1571
|
+
const sessionId = 'string' == typeof input?.sessionId && input.sessionId.trim() ? input.sessionId.trim() : void 0;
|
|
1525
1572
|
const connectAgent = async ()=>agentFromWebDriverAgent({
|
|
1526
1573
|
wdaHost: host,
|
|
1527
|
-
wdaPort: port
|
|
1574
|
+
wdaPort: port,
|
|
1575
|
+
...sessionId ? {
|
|
1576
|
+
sessionId
|
|
1577
|
+
} : {}
|
|
1528
1578
|
});
|
|
1529
1579
|
const agent = await connectAgent();
|
|
1530
1580
|
const deviceInfo = await agent.interface.getConnectedDeviceInfo?.();
|
|
@@ -1539,6 +1589,9 @@ const iosPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
1539
1589
|
metadata: {
|
|
1540
1590
|
wdaHost: host,
|
|
1541
1591
|
wdaPort: port,
|
|
1592
|
+
...sessionId ? {
|
|
1593
|
+
sessionId
|
|
1594
|
+
} : {},
|
|
1542
1595
|
...deviceInfo ? {
|
|
1543
1596
|
deviceInfo
|
|
1544
1597
|
} : {}
|
package/dist/es/mcp-server.mjs
CHANGED
|
@@ -634,6 +634,10 @@ class IOSWebDriverClient extends WebDriverClient {
|
|
|
634
634
|
debugIOS(`Failed to apply iOS session configuration: ${error}`);
|
|
635
635
|
}
|
|
636
636
|
}
|
|
637
|
+
async setupExistingSession() {
|
|
638
|
+
this.ensureSession();
|
|
639
|
+
await this.setupIOSSession();
|
|
640
|
+
}
|
|
637
641
|
async executeRequest(method, endpoint, data) {
|
|
638
642
|
return this.makeRequest(method, this.buildSessionEndpoint(endpoint), data);
|
|
639
643
|
}
|
|
@@ -731,7 +735,10 @@ class IOSDevice {
|
|
|
731
735
|
debugDevice(`Connecting to iOS device: ${this.deviceId}`);
|
|
732
736
|
try {
|
|
733
737
|
await this.wdaManager.start();
|
|
734
|
-
|
|
738
|
+
if (this.options?.sessionId) {
|
|
739
|
+
debugDevice(`Using existing WDA session: ${this.options.sessionId}`);
|
|
740
|
+
await this.wdaBackend.setupExistingSession();
|
|
741
|
+
} else await this.wdaBackend.createSession();
|
|
735
742
|
const deviceInfo = await this.wdaBackend.getDeviceInfo();
|
|
736
743
|
if (deviceInfo?.udid) {
|
|
737
744
|
this.deviceId = deviceInfo.udid;
|
|
@@ -1206,7 +1213,10 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
1206
1213
|
const mjpegPort = options?.wdaMjpegPort ?? DEFAULT_WDA_MJPEG_PORT;
|
|
1207
1214
|
this.wdaBackend = new IOSWebDriverClient({
|
|
1208
1215
|
port: wdaPort,
|
|
1209
|
-
host: wdaHost
|
|
1216
|
+
host: wdaHost,
|
|
1217
|
+
...options?.sessionId ? {
|
|
1218
|
+
sessionId: options.sessionId
|
|
1219
|
+
} : {}
|
|
1210
1220
|
});
|
|
1211
1221
|
this.wdaManager = WDAManager.getInstance(wdaPort, wdaHost);
|
|
1212
1222
|
this.mjpegStreamUrl = `http://${wdaHost}:${mjpegPort}`;
|
|
@@ -1348,9 +1358,20 @@ const iosInitArgShape = {
|
|
|
1348
1358
|
deviceId: z.string().optional().describe('iOS device UDID (optional when WDA auto-detect is sufficient)'),
|
|
1349
1359
|
wdaHost: z.string().optional().describe('WebDriverAgent host, defaults to localhost'),
|
|
1350
1360
|
wdaPort: z.number().optional().describe('WebDriverAgent port'),
|
|
1361
|
+
sessionId: z.string().optional().describe('Existing WebDriverAgent session ID to reuse'),
|
|
1351
1362
|
useWDA: z.boolean().optional().describe('Whether to reuse an existing WebDriverAgent session'),
|
|
1352
1363
|
wdaMjpegPort: z.number().optional().describe('WebDriverAgent MJPEG streaming port')
|
|
1353
1364
|
};
|
|
1365
|
+
function getTargetIdentity(initArgs) {
|
|
1366
|
+
if (initArgs?.deviceId) return initArgs.sessionId ? `${initArgs.deviceId}-session-${initArgs.sessionId}` : initArgs.deviceId;
|
|
1367
|
+
if (initArgs?.wdaHost || initArgs?.wdaPort || initArgs?.sessionId) {
|
|
1368
|
+
const wdaHost = initArgs.wdaHost ?? 'localhost';
|
|
1369
|
+
const wdaPort = initArgs.wdaPort ?? 'default';
|
|
1370
|
+
const sessionSegment = initArgs.sessionId ? `-session-${initArgs.sessionId}` : '';
|
|
1371
|
+
return `wda-${wdaHost}-${wdaPort}${sessionSegment}`;
|
|
1372
|
+
}
|
|
1373
|
+
return 'auto';
|
|
1374
|
+
}
|
|
1354
1375
|
class IOSMidsceneTools extends BaseMidsceneTools {
|
|
1355
1376
|
getCliReportSessionName() {
|
|
1356
1377
|
return 'midscene-ios';
|
|
@@ -1389,7 +1410,7 @@ class IOSMidsceneTools extends BaseMidsceneTools {
|
|
|
1389
1410
|
cli: this.getAgentInitArgCliMetadata(),
|
|
1390
1411
|
handler: async (args)=>{
|
|
1391
1412
|
const initArgs = this.extractAgentInitParam(args);
|
|
1392
|
-
const identity =
|
|
1413
|
+
const identity = getTargetIdentity(initArgs);
|
|
1393
1414
|
const reportSession = this.createNewCliReportSession(identity);
|
|
1394
1415
|
this.commitCliReportSession(reportSession);
|
|
1395
1416
|
if (this.agent) {
|
|
@@ -1440,7 +1461,7 @@ class IOSMCPServer extends BaseMCPServer {
|
|
|
1440
1461
|
constructor(toolsManager){
|
|
1441
1462
|
super({
|
|
1442
1463
|
name: '@midscene/ios-mcp',
|
|
1443
|
-
version: "1.8.4
|
|
1464
|
+
version: "1.8.4",
|
|
1444
1465
|
description: 'Control the iOS device using natural language commands'
|
|
1445
1466
|
}, toolsManager);
|
|
1446
1467
|
}
|
package/dist/lib/bin.js
CHANGED
|
@@ -660,6 +660,10 @@ class IOSWebDriverClient extends webdriver_namespaceObject.WebDriverClient {
|
|
|
660
660
|
debugIOS(`Failed to apply iOS session configuration: ${error}`);
|
|
661
661
|
}
|
|
662
662
|
}
|
|
663
|
+
async setupExistingSession() {
|
|
664
|
+
this.ensureSession();
|
|
665
|
+
await this.setupIOSSession();
|
|
666
|
+
}
|
|
663
667
|
async executeRequest(method, endpoint, data) {
|
|
664
668
|
return this.makeRequest(method, this.buildSessionEndpoint(endpoint), data);
|
|
665
669
|
}
|
|
@@ -757,7 +761,10 @@ class IOSDevice {
|
|
|
757
761
|
debugDevice(`Connecting to iOS device: ${this.deviceId}`);
|
|
758
762
|
try {
|
|
759
763
|
await this.wdaManager.start();
|
|
760
|
-
|
|
764
|
+
if (this.options?.sessionId) {
|
|
765
|
+
debugDevice(`Using existing WDA session: ${this.options.sessionId}`);
|
|
766
|
+
await this.wdaBackend.setupExistingSession();
|
|
767
|
+
} else await this.wdaBackend.createSession();
|
|
761
768
|
const deviceInfo = await this.wdaBackend.getDeviceInfo();
|
|
762
769
|
if (deviceInfo?.udid) {
|
|
763
770
|
this.deviceId = deviceInfo.udid;
|
|
@@ -1232,7 +1239,10 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
1232
1239
|
const mjpegPort = options?.wdaMjpegPort ?? DEFAULT_WDA_MJPEG_PORT;
|
|
1233
1240
|
this.wdaBackend = new IOSWebDriverClient({
|
|
1234
1241
|
port: wdaPort,
|
|
1235
|
-
host: wdaHost
|
|
1242
|
+
host: wdaHost,
|
|
1243
|
+
...options?.sessionId ? {
|
|
1244
|
+
sessionId: options.sessionId
|
|
1245
|
+
} : {}
|
|
1236
1246
|
});
|
|
1237
1247
|
this.wdaManager = webdriver_namespaceObject.WDAManager.getInstance(wdaPort, wdaHost);
|
|
1238
1248
|
this.mjpegStreamUrl = `http://${wdaHost}:${mjpegPort}`;
|
|
@@ -1359,6 +1369,22 @@ async function agentFromWebDriverAgent(opts) {
|
|
|
1359
1369
|
await device.connect();
|
|
1360
1370
|
return new IOSAgent(device, opts);
|
|
1361
1371
|
}
|
|
1372
|
+
async function probeWdaReady(host, port, timeoutMs = 800) {
|
|
1373
|
+
const controller = new AbortController();
|
|
1374
|
+
const timer = setTimeout(()=>controller.abort(), timeoutMs);
|
|
1375
|
+
try {
|
|
1376
|
+
const res = await fetch(`http://${host}:${port}/status`, {
|
|
1377
|
+
signal: controller.signal
|
|
1378
|
+
});
|
|
1379
|
+
if (!res.ok) return false;
|
|
1380
|
+
const body = await res.json().catch(()=>null);
|
|
1381
|
+
return body?.value?.ready === true;
|
|
1382
|
+
} catch {
|
|
1383
|
+
return false;
|
|
1384
|
+
} finally{
|
|
1385
|
+
clearTimeout(timer);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1362
1388
|
const iosPlaygroundPlatform = (0, playground_namespaceObject.definePlaygroundPlatform)({
|
|
1363
1389
|
id: 'ios',
|
|
1364
1390
|
title: 'Midscene iOS Playground',
|
|
@@ -1369,10 +1395,12 @@ const iosPlaygroundPlatform = (0, playground_namespaceObject.definePlaygroundPla
|
|
|
1369
1395
|
if (availablePlaygroundPort !== constants_namespaceObject.PLAYGROUND_SERVER_PORT) console.log(`⚠️ Port ${constants_namespaceObject.PLAYGROUND_SERVER_PORT} is busy, using port ${availablePlaygroundPort} instead`);
|
|
1370
1396
|
const sessionManager = {
|
|
1371
1397
|
async getSetupSchema () {
|
|
1398
|
+
const wdaReady = await probeWdaReady('localhost', constants_namespaceObject.DEFAULT_WDA_PORT);
|
|
1372
1399
|
return {
|
|
1373
1400
|
title: 'Connect WebDriverAgent',
|
|
1374
1401
|
description: 'Provide the WebDriverAgent host and port that are already running for your selected iPhone or simulator.',
|
|
1375
1402
|
primaryActionLabel: 'Create Agent',
|
|
1403
|
+
autoSubmitWhenReady: wdaReady,
|
|
1376
1404
|
fields: [
|
|
1377
1405
|
{
|
|
1378
1406
|
key: 'host',
|
|
@@ -1389,6 +1417,13 @@ const iosPlaygroundPlatform = (0, playground_namespaceObject.definePlaygroundPla
|
|
|
1389
1417
|
required: true,
|
|
1390
1418
|
defaultValue: constants_namespaceObject.DEFAULT_WDA_PORT,
|
|
1391
1419
|
placeholder: constants_namespaceObject.DEFAULT_WDA_PORT.toString()
|
|
1420
|
+
},
|
|
1421
|
+
{
|
|
1422
|
+
key: 'sessionId',
|
|
1423
|
+
label: 'WebDriverAgent session ID',
|
|
1424
|
+
type: 'text',
|
|
1425
|
+
required: false,
|
|
1426
|
+
placeholder: 'Existing session ID'
|
|
1392
1427
|
}
|
|
1393
1428
|
]
|
|
1394
1429
|
};
|
|
@@ -1397,9 +1432,13 @@ const iosPlaygroundPlatform = (0, playground_namespaceObject.definePlaygroundPla
|
|
|
1397
1432
|
const host = 'string' == typeof input?.host && input.host.trim() ? input.host.trim().replace(/^https?:\/\//, '') : 'localhost';
|
|
1398
1433
|
const port = 'number' == typeof input?.port ? input.port : Number.parseInt(String(input?.port ?? constants_namespaceObject.DEFAULT_WDA_PORT), 10);
|
|
1399
1434
|
if (Number.isNaN(port) || port < 1 || port > 65535) throw new Error(`Invalid WebDriverAgent port: ${String(input?.port)}`);
|
|
1435
|
+
const sessionId = 'string' == typeof input?.sessionId && input.sessionId.trim() ? input.sessionId.trim() : void 0;
|
|
1400
1436
|
const connectAgent = async ()=>agentFromWebDriverAgent({
|
|
1401
1437
|
wdaHost: host,
|
|
1402
|
-
wdaPort: port
|
|
1438
|
+
wdaPort: port,
|
|
1439
|
+
...sessionId ? {
|
|
1440
|
+
sessionId
|
|
1441
|
+
} : {}
|
|
1403
1442
|
});
|
|
1404
1443
|
const agent = await connectAgent();
|
|
1405
1444
|
const deviceInfo = await agent.interface.getConnectedDeviceInfo?.();
|
|
@@ -1414,6 +1453,9 @@ const iosPlaygroundPlatform = (0, playground_namespaceObject.definePlaygroundPla
|
|
|
1414
1453
|
metadata: {
|
|
1415
1454
|
wdaHost: host,
|
|
1416
1455
|
wdaPort: port,
|
|
1456
|
+
...sessionId ? {
|
|
1457
|
+
sessionId
|
|
1458
|
+
} : {},
|
|
1417
1459
|
...deviceInfo ? {
|
|
1418
1460
|
deviceInfo
|
|
1419
1461
|
} : {}
|
package/dist/lib/cli.js
CHANGED
|
@@ -658,6 +658,10 @@ class IOSWebDriverClient extends webdriver_namespaceObject.WebDriverClient {
|
|
|
658
658
|
debugIOS(`Failed to apply iOS session configuration: ${error}`);
|
|
659
659
|
}
|
|
660
660
|
}
|
|
661
|
+
async setupExistingSession() {
|
|
662
|
+
this.ensureSession();
|
|
663
|
+
await this.setupIOSSession();
|
|
664
|
+
}
|
|
661
665
|
async executeRequest(method, endpoint, data) {
|
|
662
666
|
return this.makeRequest(method, this.buildSessionEndpoint(endpoint), data);
|
|
663
667
|
}
|
|
@@ -755,7 +759,10 @@ class IOSDevice {
|
|
|
755
759
|
debugDevice(`Connecting to iOS device: ${this.deviceId}`);
|
|
756
760
|
try {
|
|
757
761
|
await this.wdaManager.start();
|
|
758
|
-
|
|
762
|
+
if (this.options?.sessionId) {
|
|
763
|
+
debugDevice(`Using existing WDA session: ${this.options.sessionId}`);
|
|
764
|
+
await this.wdaBackend.setupExistingSession();
|
|
765
|
+
} else await this.wdaBackend.createSession();
|
|
759
766
|
const deviceInfo = await this.wdaBackend.getDeviceInfo();
|
|
760
767
|
if (deviceInfo?.udid) {
|
|
761
768
|
this.deviceId = deviceInfo.udid;
|
|
@@ -1230,7 +1237,10 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
1230
1237
|
const mjpegPort = options?.wdaMjpegPort ?? DEFAULT_WDA_MJPEG_PORT;
|
|
1231
1238
|
this.wdaBackend = new IOSWebDriverClient({
|
|
1232
1239
|
port: wdaPort,
|
|
1233
|
-
host: wdaHost
|
|
1240
|
+
host: wdaHost,
|
|
1241
|
+
...options?.sessionId ? {
|
|
1242
|
+
sessionId: options.sessionId
|
|
1243
|
+
} : {}
|
|
1234
1244
|
});
|
|
1235
1245
|
this.wdaManager = webdriver_namespaceObject.WDAManager.getInstance(wdaPort, wdaHost);
|
|
1236
1246
|
this.mjpegStreamUrl = `http://${wdaHost}:${mjpegPort}`;
|
|
@@ -1372,9 +1382,20 @@ const iosInitArgShape = {
|
|
|
1372
1382
|
deviceId: core_namespaceObject.z.string().optional().describe('iOS device UDID (optional when WDA auto-detect is sufficient)'),
|
|
1373
1383
|
wdaHost: core_namespaceObject.z.string().optional().describe('WebDriverAgent host, defaults to localhost'),
|
|
1374
1384
|
wdaPort: core_namespaceObject.z.number().optional().describe('WebDriverAgent port'),
|
|
1385
|
+
sessionId: core_namespaceObject.z.string().optional().describe('Existing WebDriverAgent session ID to reuse'),
|
|
1375
1386
|
useWDA: core_namespaceObject.z.boolean().optional().describe('Whether to reuse an existing WebDriverAgent session'),
|
|
1376
1387
|
wdaMjpegPort: core_namespaceObject.z.number().optional().describe('WebDriverAgent MJPEG streaming port')
|
|
1377
1388
|
};
|
|
1389
|
+
function getTargetIdentity(initArgs) {
|
|
1390
|
+
if (initArgs?.deviceId) return initArgs.sessionId ? `${initArgs.deviceId}-session-${initArgs.sessionId}` : initArgs.deviceId;
|
|
1391
|
+
if (initArgs?.wdaHost || initArgs?.wdaPort || initArgs?.sessionId) {
|
|
1392
|
+
const wdaHost = initArgs.wdaHost ?? 'localhost';
|
|
1393
|
+
const wdaPort = initArgs.wdaPort ?? 'default';
|
|
1394
|
+
const sessionSegment = initArgs.sessionId ? `-session-${initArgs.sessionId}` : '';
|
|
1395
|
+
return `wda-${wdaHost}-${wdaPort}${sessionSegment}`;
|
|
1396
|
+
}
|
|
1397
|
+
return 'auto';
|
|
1398
|
+
}
|
|
1378
1399
|
class IOSMidsceneTools extends base_tools_namespaceObject.BaseMidsceneTools {
|
|
1379
1400
|
getCliReportSessionName() {
|
|
1380
1401
|
return 'midscene-ios';
|
|
@@ -1413,7 +1434,7 @@ class IOSMidsceneTools extends base_tools_namespaceObject.BaseMidsceneTools {
|
|
|
1413
1434
|
cli: this.getAgentInitArgCliMetadata(),
|
|
1414
1435
|
handler: async (args)=>{
|
|
1415
1436
|
const initArgs = this.extractAgentInitParam(args);
|
|
1416
|
-
const identity =
|
|
1437
|
+
const identity = getTargetIdentity(initArgs);
|
|
1417
1438
|
const reportSession = this.createNewCliReportSession(identity);
|
|
1418
1439
|
this.commitCliReportSession(reportSession);
|
|
1419
1440
|
if (this.agent) {
|
|
@@ -1460,7 +1481,7 @@ class IOSMidsceneTools extends base_tools_namespaceObject.BaseMidsceneTools {
|
|
|
1460
1481
|
const tools = new IOSMidsceneTools();
|
|
1461
1482
|
(0, cli_namespaceObject.runToolsCLI)(tools, 'midscene-ios', {
|
|
1462
1483
|
stripPrefix: 'ios_',
|
|
1463
|
-
version: "1.8.4
|
|
1484
|
+
version: "1.8.4",
|
|
1464
1485
|
extraCommands: (0, core_namespaceObject.createReportCliCommands)()
|
|
1465
1486
|
}).catch((e)=>{
|
|
1466
1487
|
process.exit((0, cli_namespaceObject.reportCLIError)(e));
|