appium-ios-remotexpc 0.6.2 → 0.8.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/CHANGELOG.md +12 -0
- package/build/src/index.d.ts +1 -1
- package/build/src/index.d.ts.map +1 -1
- package/build/src/lib/types.d.ts +184 -0
- package/build/src/lib/types.d.ts.map +1 -1
- package/build/src/service-connection.d.ts +6 -0
- package/build/src/service-connection.d.ts.map +1 -1
- package/build/src/service-connection.js +8 -0
- package/build/src/services/index.d.ts +2 -1
- package/build/src/services/index.d.ts.map +1 -1
- package/build/src/services/index.js +2 -1
- package/build/src/services/ios/springboard-service/index.d.ts +39 -0
- package/build/src/services/ios/springboard-service/index.d.ts.map +1 -0
- package/build/src/services/ios/springboard-service/index.js +174 -0
- package/build/src/services/ios/webinspector/index.d.ts +114 -0
- package/build/src/services/ios/webinspector/index.d.ts.map +1 -0
- package/build/src/services/ios/webinspector/index.js +286 -0
- package/build/src/services.d.ts +3 -1
- package/build/src/services.d.ts.map +1 -1
- package/build/src/services.js +24 -0
- package/package.json +3 -1
- package/src/index.ts +4 -0
- package/src/lib/types.ts +226 -0
- package/src/service-connection.ts +9 -0
- package/src/services/index.ts +2 -0
- package/src/services/ios/springboard-service/index.ts +197 -0
- package/src/services/ios/webinspector/index.ts +372 -0
- package/src/services.ts +36 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/services/ios/webinspector/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE3E,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAIjD;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,eAAe;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,eAAe,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,qBAAa,mBAAoB,SAAQ,WAAW;IAClD,MAAM,CAAC,QAAQ,CAAC,gBAAgB,wCAAwC;IAGxE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAA4B;IACzE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,8BAA8B,CACnB;IACnC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,8BAA8B,CACnB;IACnC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAA6B;IAC5E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sCAAsC,CACpB;IAC1C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,wBAAwB,CAA8B;IAC9E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAA6B;IAC5E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,6BAA6B,CACpB;IAEjC,OAAO,CAAC,UAAU,CAAkC;IACpD,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,cAAc,CAA8B;gBAExC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;IAKrC;;;;;OAKG;IACG,WAAW,CACf,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,eAAoB,GACzB,OAAO,CAAC,IAAI,CAAC;IAiBhB;;;OAGG;IACI,aAAa,IAAI,cAAc,CAAC,YAAY,EAAE,IAAI,EAAE,OAAO,CAAC;IA6DnE;;OAEG;IACH,aAAa,IAAI,IAAI;IAKrB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAc5B;;;OAGG;IACH,eAAe,IAAI,MAAM;IAIzB;;;OAGG;IACG,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/D;;OAEG;IACG,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;IAO/C;;;OAGG;IACG,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrD;;;;;OAKG;IACG,+BAA+B,CACnC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,eAAe,GAC7B,OAAO,CAAC,IAAI,CAAC;IAmBhB;;;;;;OAMG;IACG,kBAAkB,CACtB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,kBAAkB,GAAE,OAAc,GACjC,OAAO,CAAC,IAAI,CAAC;IAkBhB;;;;;;OAMG;IACG,iBAAiB,CACrB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,GAAG,GACR,OAAO,CAAC,IAAI,CAAC;IAYhB;;;;;OAKG;IACG,sBAAsB,CAC1B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,IAAI,CAAC;IAQhB;;;OAGG;YACW,4BAA4B;IA2B1C;;OAEG;IACH,OAAO,CAAC,oBAAoB;CA0B7B;AAED,eAAe,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { logger } from '@appium/support';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
import { ServiceConnection } from '../../../service-connection.js';
|
|
5
|
+
import { BaseService } from '../base-service.js';
|
|
6
|
+
const log = logger.getLogger('WebInspectorService');
|
|
7
|
+
/**
|
|
8
|
+
* WebInspectorService provides an API to:
|
|
9
|
+
* - Send messages to webinspectord
|
|
10
|
+
* - Listen to messages from webinspectord
|
|
11
|
+
* - Communicate with web views and Safari on iOS devices
|
|
12
|
+
*
|
|
13
|
+
* This service is used for web automation, inspection, and debugging.
|
|
14
|
+
*/
|
|
15
|
+
export class WebInspectorService extends BaseService {
|
|
16
|
+
static RSD_SERVICE_NAME = 'com.apple.webinspector.shim.remote';
|
|
17
|
+
// RPC method selectors
|
|
18
|
+
static RPC_REPORT_IDENTIFIER = '_rpc_reportIdentifier:';
|
|
19
|
+
static RPC_REQUEST_APPLICATION_LAUNCH = '_rpc_requestApplicationLaunch:';
|
|
20
|
+
static RPC_GET_CONNECTED_APPLICATIONS = '_rpc_getConnectedApplications:';
|
|
21
|
+
static RPC_FORWARD_GET_LISTING = '_rpc_forwardGetListing:';
|
|
22
|
+
static RPC_FORWARD_AUTOMATION_SESSION_REQUEST = '_rpc_forwardAutomationSessionRequest:';
|
|
23
|
+
static RPC_FORWARD_SOCKET_SETUP = '_rpc_forwardSocketSetup:';
|
|
24
|
+
static RPC_FORWARD_SOCKET_DATA = '_rpc_forwardSocketData:';
|
|
25
|
+
static RPC_FORWARD_INDICATE_WEB_VIEW = '_rpc_forwardIndicateWebView:';
|
|
26
|
+
connection = null;
|
|
27
|
+
messageEmitter = new EventEmitter();
|
|
28
|
+
isReceiving = false;
|
|
29
|
+
connectionId;
|
|
30
|
+
receivePromise = null;
|
|
31
|
+
constructor(address) {
|
|
32
|
+
super(address);
|
|
33
|
+
this.connectionId = randomUUID().toUpperCase();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Send a message to the WebInspector service
|
|
37
|
+
* @param selector The RPC selector (e.g., '_rpc_reportIdentifier:')
|
|
38
|
+
* @param args The arguments dictionary for the message
|
|
39
|
+
* @returns Promise that resolves when the message is sent
|
|
40
|
+
*/
|
|
41
|
+
async sendMessage(selector, args = {}) {
|
|
42
|
+
const connection = await this.connectToWebInspectorService();
|
|
43
|
+
// Add connection identifier to all messages
|
|
44
|
+
const message = {
|
|
45
|
+
__selector: selector,
|
|
46
|
+
__argument: {
|
|
47
|
+
...args,
|
|
48
|
+
WIRConnectionIdentifierKey: this.connectionId,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
log.debug(`Sending WebInspector message: ${selector}`);
|
|
52
|
+
connection.sendPlist(message);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Listen to messages from the WebInspector service using async generator
|
|
56
|
+
* @yields PlistMessage - Messages received from the WebInspector service
|
|
57
|
+
*/
|
|
58
|
+
async *listenMessage() {
|
|
59
|
+
await this.connectToWebInspectorService();
|
|
60
|
+
// Start receiving messages in background if not already started
|
|
61
|
+
if (!this.isReceiving) {
|
|
62
|
+
this.startMessageReceiver();
|
|
63
|
+
}
|
|
64
|
+
const queue = [];
|
|
65
|
+
let resolveNext = null;
|
|
66
|
+
let stopped = false;
|
|
67
|
+
const messageHandler = (message) => {
|
|
68
|
+
if (resolveNext) {
|
|
69
|
+
resolveNext({ value: message, done: false });
|
|
70
|
+
resolveNext = null;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
queue.push(message);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const stopHandler = () => {
|
|
77
|
+
stopped = true;
|
|
78
|
+
if (resolveNext) {
|
|
79
|
+
resolveNext({ value: undefined, done: true });
|
|
80
|
+
resolveNext = null;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
this.messageEmitter.on('message', messageHandler);
|
|
84
|
+
this.messageEmitter.once('stop', stopHandler);
|
|
85
|
+
try {
|
|
86
|
+
while (!stopped) {
|
|
87
|
+
if (queue.length > 0) {
|
|
88
|
+
yield queue.shift();
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const message = await new Promise((resolve) => {
|
|
92
|
+
if (stopped) {
|
|
93
|
+
resolve(null);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
resolveNext = (result) => {
|
|
97
|
+
resolve(result.done ? null : result.value);
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
if (message === null) {
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
yield message;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
this.messageEmitter.off('message', messageHandler);
|
|
109
|
+
this.messageEmitter.off('stop', stopHandler);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Stop listening to messages
|
|
114
|
+
*/
|
|
115
|
+
stopListening() {
|
|
116
|
+
this.isReceiving = false;
|
|
117
|
+
this.messageEmitter.emit('stop');
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Close the connection and clean up resources
|
|
121
|
+
*/
|
|
122
|
+
async close() {
|
|
123
|
+
this.stopListening();
|
|
124
|
+
if (this.connection) {
|
|
125
|
+
await this.connection.close();
|
|
126
|
+
this.connection = null;
|
|
127
|
+
log.debug('WebInspector connection closed');
|
|
128
|
+
}
|
|
129
|
+
if (this.receivePromise) {
|
|
130
|
+
await this.receivePromise;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get the connection ID being used for this service
|
|
135
|
+
* @returns The connection identifier
|
|
136
|
+
*/
|
|
137
|
+
getConnectionId() {
|
|
138
|
+
return this.connectionId;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Request application launch
|
|
142
|
+
* @param bundleId The bundle identifier of the application to launch
|
|
143
|
+
*/
|
|
144
|
+
async requestApplicationLaunch(bundleId) {
|
|
145
|
+
await this.sendMessage(WebInspectorService.RPC_REQUEST_APPLICATION_LAUNCH, {
|
|
146
|
+
WIRApplicationBundleIdentifierKey: bundleId,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get connected applications
|
|
151
|
+
*/
|
|
152
|
+
async getConnectedApplications() {
|
|
153
|
+
await this.sendMessage(WebInspectorService.RPC_GET_CONNECTED_APPLICATIONS, {});
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Forward get listing for an application
|
|
157
|
+
* @param appId The application identifier
|
|
158
|
+
*/
|
|
159
|
+
async forwardGetListing(appId) {
|
|
160
|
+
await this.sendMessage(WebInspectorService.RPC_FORWARD_GET_LISTING, {
|
|
161
|
+
WIRApplicationIdentifierKey: appId,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Forward automation session request
|
|
166
|
+
* @param sessionId The session identifier
|
|
167
|
+
* @param appId The application identifier
|
|
168
|
+
* @param capabilities Optional session capabilities
|
|
169
|
+
*/
|
|
170
|
+
async forwardAutomationSessionRequest(sessionId, appId, capabilities) {
|
|
171
|
+
const defaultCapabilities = {
|
|
172
|
+
'org.webkit.webdriver.webrtc.allow-insecure-media-capture': true,
|
|
173
|
+
'org.webkit.webdriver.webrtc.suppress-ice-candidate-filtering': false,
|
|
174
|
+
};
|
|
175
|
+
await this.sendMessage(WebInspectorService.RPC_FORWARD_AUTOMATION_SESSION_REQUEST, {
|
|
176
|
+
WIRApplicationIdentifierKey: appId,
|
|
177
|
+
WIRSessionIdentifierKey: sessionId,
|
|
178
|
+
WIRSessionCapabilitiesKey: {
|
|
179
|
+
...defaultCapabilities,
|
|
180
|
+
...(capabilities ?? {}),
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Forward socket setup for inspector connection
|
|
186
|
+
* @param sessionId The session identifier
|
|
187
|
+
* @param appId The application identifier
|
|
188
|
+
* @param pageId The page identifier
|
|
189
|
+
* @param automaticallyPause Whether to automatically pause (defaults to true)
|
|
190
|
+
*/
|
|
191
|
+
async forwardSocketSetup(sessionId, appId, pageId, automaticallyPause = true) {
|
|
192
|
+
const message = {
|
|
193
|
+
WIRApplicationIdentifierKey: appId,
|
|
194
|
+
WIRPageIdentifierKey: pageId,
|
|
195
|
+
WIRSenderKey: sessionId,
|
|
196
|
+
WIRMessageDataTypeChunkSupportedKey: 0,
|
|
197
|
+
};
|
|
198
|
+
if (!automaticallyPause) {
|
|
199
|
+
message.WIRAutomaticallyPause = false;
|
|
200
|
+
}
|
|
201
|
+
await this.sendMessage(WebInspectorService.RPC_FORWARD_SOCKET_SETUP, message);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Forward socket data to a page
|
|
205
|
+
* @param sessionId The session identifier
|
|
206
|
+
* @param appId The application identifier
|
|
207
|
+
* @param pageId The page identifier
|
|
208
|
+
* @param data The data to send (will be JSON stringified)
|
|
209
|
+
*/
|
|
210
|
+
async forwardSocketData(sessionId, appId, pageId, data) {
|
|
211
|
+
const socketData = typeof data === 'string' ? data : JSON.stringify(data);
|
|
212
|
+
await this.sendMessage(WebInspectorService.RPC_FORWARD_SOCKET_DATA, {
|
|
213
|
+
WIRApplicationIdentifierKey: appId,
|
|
214
|
+
WIRPageIdentifierKey: pageId,
|
|
215
|
+
WIRSessionIdentifierKey: sessionId,
|
|
216
|
+
WIRSenderKey: sessionId,
|
|
217
|
+
WIRSocketDataKey: Buffer.from(socketData, 'utf-8'),
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Forward indicate web view
|
|
222
|
+
* @param appId The application identifier
|
|
223
|
+
* @param pageId The page identifier
|
|
224
|
+
* @param enable Whether to enable indication
|
|
225
|
+
*/
|
|
226
|
+
async forwardIndicateWebView(appId, pageId, enable) {
|
|
227
|
+
await this.sendMessage(WebInspectorService.RPC_FORWARD_INDICATE_WEB_VIEW, {
|
|
228
|
+
WIRApplicationIdentifierKey: appId,
|
|
229
|
+
WIRPageIdentifierKey: pageId,
|
|
230
|
+
WIRIndicateEnabledKey: enable,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Connect to the WebInspector service
|
|
235
|
+
* @returns Promise resolving to the ServiceConnection instance
|
|
236
|
+
*/
|
|
237
|
+
async connectToWebInspectorService() {
|
|
238
|
+
if (this.connection) {
|
|
239
|
+
return this.connection;
|
|
240
|
+
}
|
|
241
|
+
const service = {
|
|
242
|
+
serviceName: WebInspectorService.RSD_SERVICE_NAME,
|
|
243
|
+
port: this.address[1].toString(),
|
|
244
|
+
};
|
|
245
|
+
this.connection = await this.startLockdownService(service);
|
|
246
|
+
// Consume the StartService response from RSDCheckin
|
|
247
|
+
const startServiceResponse = await this.connection.receive();
|
|
248
|
+
if (startServiceResponse?.Request !== 'StartService') {
|
|
249
|
+
log.warn(`Expected StartService response, got: ${JSON.stringify(startServiceResponse)}`);
|
|
250
|
+
}
|
|
251
|
+
// Send initial identifier report
|
|
252
|
+
await this.sendMessage(WebInspectorService.RPC_REPORT_IDENTIFIER, {});
|
|
253
|
+
log.debug('Connected to WebInspector service');
|
|
254
|
+
return this.connection;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Start receiving messages from the WebInspector service in the background
|
|
258
|
+
*/
|
|
259
|
+
startMessageReceiver() {
|
|
260
|
+
if (this.isReceiving || !this.connection) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
this.isReceiving = true;
|
|
264
|
+
this.receivePromise = (async () => {
|
|
265
|
+
try {
|
|
266
|
+
while (this.isReceiving && this.connection) {
|
|
267
|
+
try {
|
|
268
|
+
const message = await this.connection.receive();
|
|
269
|
+
this.messageEmitter.emit('message', message);
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
if (this.isReceiving) {
|
|
273
|
+
log.error('Error receiving message:', error);
|
|
274
|
+
this.messageEmitter.emit('error', error);
|
|
275
|
+
}
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
finally {
|
|
281
|
+
this.isReceiving = false;
|
|
282
|
+
}
|
|
283
|
+
})();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
export default WebInspectorService;
|
package/build/src/services.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { RemoteXpcConnection } from './lib/remote-xpc/remote-xpc-connection.js';
|
|
2
|
-
import type { DiagnosticsServiceWithConnection, MobileConfigServiceWithConnection, MobileImageMounterServiceWithConnection, NotificationProxyServiceWithConnection, SyslogService as SyslogServiceType } from './lib/types.js';
|
|
2
|
+
import type { DiagnosticsServiceWithConnection, MobileConfigServiceWithConnection, MobileImageMounterServiceWithConnection, NotificationProxyServiceWithConnection, SpringboardServiceWithConnection, SyslogService as SyslogServiceType, WebInspectorServiceWithConnection } from './lib/types.js';
|
|
3
3
|
export declare function startDiagnosticsService(udid: string): Promise<DiagnosticsServiceWithConnection>;
|
|
4
4
|
export declare function startNotificationProxyService(udid: string): Promise<NotificationProxyServiceWithConnection>;
|
|
5
5
|
export declare function startMobileConfigService(udid: string): Promise<MobileConfigServiceWithConnection>;
|
|
6
6
|
export declare function startMobileImageMounterService(udid: string): Promise<MobileImageMounterServiceWithConnection>;
|
|
7
|
+
export declare function startSpringboardService(udid: string): Promise<SpringboardServiceWithConnection>;
|
|
7
8
|
export declare function startSyslogService(udid: string): Promise<SyslogServiceType>;
|
|
9
|
+
export declare function startWebInspectorService(udid: string): Promise<WebInspectorServiceWithConnection>;
|
|
8
10
|
export declare function createRemoteXPCConnection(udid: string): Promise<{
|
|
9
11
|
remoteXPC: RemoteXpcConnection;
|
|
10
12
|
tunnelConnection: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/services.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAGhF,OAAO,KAAK,EACV,gCAAgC,EAChC,iCAAiC,EACjC,uCAAuC,EACvC,sCAAsC,EACtC,aAAa,IAAI,iBAAiB,
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/services.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAGhF,OAAO,KAAK,EACV,gCAAgC,EAChC,iCAAiC,EACjC,uCAAuC,EACvC,sCAAsC,EACtC,gCAAgC,EAChC,aAAa,IAAI,iBAAiB,EAClC,iCAAiC,EAClC,MAAM,gBAAgB,CAAC;AAYxB,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gCAAgC,CAAC,CAY3C;AAED,wBAAsB,6BAA6B,CACjD,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,sCAAsC,CAAC,CAYjD;AAED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iCAAiC,CAAC,CAY5C;AACD,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,uCAAuC,CAAC,CAYlD;AAED,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gCAAgC,CAAC,CAY3C;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,CAAC,CAG5B;AAED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iCAAiC,CAAC,CAY5C;AAED,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,MAAM;;;;;;;;GAO3D"}
|
package/build/src/services.js
CHANGED
|
@@ -6,7 +6,9 @@ import DiagnosticsService from './services/ios/diagnostic-service/index.js';
|
|
|
6
6
|
import { MobileConfigService } from './services/ios/mobile-config/index.js';
|
|
7
7
|
import MobileImageMounterService from './services/ios/mobile-image-mounter/index.js';
|
|
8
8
|
import { NotificationProxyService } from './services/ios/notification-proxy/index.js';
|
|
9
|
+
import { SpringBoardService } from './services/ios/springboard-service/index.js';
|
|
9
10
|
import SyslogService from './services/ios/syslog-service/index.js';
|
|
11
|
+
import { WebInspectorService } from './services/ios/webinspector/index.js';
|
|
10
12
|
const APPIUM_XCUITEST_DRIVER_NAME = 'appium-xcuitest-driver';
|
|
11
13
|
const TUNNEL_REGISTRY_PORT = 'tunnelRegistryPort';
|
|
12
14
|
export async function startDiagnosticsService(udid) {
|
|
@@ -53,10 +55,32 @@ export async function startMobileImageMounterService(udid) {
|
|
|
53
55
|
]),
|
|
54
56
|
};
|
|
55
57
|
}
|
|
58
|
+
export async function startSpringboardService(udid) {
|
|
59
|
+
const { remoteXPC, tunnelConnection } = await createRemoteXPCConnection(udid);
|
|
60
|
+
const springboardService = remoteXPC.findService(SpringBoardService.RSD_SERVICE_NAME);
|
|
61
|
+
return {
|
|
62
|
+
remoteXPC: remoteXPC,
|
|
63
|
+
springboardService: new SpringBoardService([
|
|
64
|
+
tunnelConnection.host,
|
|
65
|
+
parseInt(springboardService.port, 10),
|
|
66
|
+
]),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
56
69
|
export async function startSyslogService(udid) {
|
|
57
70
|
const { tunnelConnection } = await createRemoteXPCConnection(udid);
|
|
58
71
|
return new SyslogService([tunnelConnection.host, tunnelConnection.port]);
|
|
59
72
|
}
|
|
73
|
+
export async function startWebInspectorService(udid) {
|
|
74
|
+
const { remoteXPC, tunnelConnection } = await createRemoteXPCConnection(udid);
|
|
75
|
+
const webInspectorService = remoteXPC.findService(WebInspectorService.RSD_SERVICE_NAME);
|
|
76
|
+
return {
|
|
77
|
+
remoteXPC: remoteXPC,
|
|
78
|
+
webInspectorService: new WebInspectorService([
|
|
79
|
+
tunnelConnection.host,
|
|
80
|
+
parseInt(webInspectorService.port, 10),
|
|
81
|
+
]),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
60
84
|
export async function createRemoteXPCConnection(udid) {
|
|
61
85
|
const tunnelConnection = await getTunnelInformation(udid);
|
|
62
86
|
const remoteXPC = await startService(tunnelConnection.host, tunnelConnection.port);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appium-ios-remotexpc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"main": "build/src/index.js",
|
|
5
5
|
"types": "build/src/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
"test:notification": "mocha test/integration/notification-proxy-test.ts --exit --timeout 1m",
|
|
32
32
|
"test:image-mounter": "mocha test/integration/mobile-image-mounter-test.ts --exit --timeout 1m",
|
|
33
33
|
"test:mobile-config": "mocha test/integration/mobile-config-test.ts --exit --timeout 1m",
|
|
34
|
+
"test:springboard": "mocha test/integration/springboard-service-test.ts --exit --timeout 1m",
|
|
35
|
+
"test:webinspector": "mocha test/integration/webinspector-test.ts --exit --timeout 1m",
|
|
34
36
|
"test:unit": "mocha 'test/unit/**/*.ts' --exit --timeout 2m",
|
|
35
37
|
"test:tunnel-creation": "sudo tsx scripts/test-tunnel-creation.ts",
|
|
36
38
|
"test:tunnel-creation:lsof": "sudo tsx scripts/test-tunnel-creation.ts --keep-open"
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,8 @@ export type {
|
|
|
17
17
|
MobileImageMounterService,
|
|
18
18
|
NotificationProxyService,
|
|
19
19
|
MobileConfigService,
|
|
20
|
+
SpringboardService,
|
|
21
|
+
WebInspectorService,
|
|
20
22
|
SyslogService,
|
|
21
23
|
SocketInfo,
|
|
22
24
|
TunnelResult,
|
|
@@ -26,6 +28,8 @@ export type {
|
|
|
26
28
|
MobileImageMounterServiceWithConnection,
|
|
27
29
|
NotificationProxyServiceWithConnection,
|
|
28
30
|
MobileConfigServiceWithConnection,
|
|
31
|
+
SpringboardServiceWithConnection,
|
|
32
|
+
WebInspectorServiceWithConnection,
|
|
29
33
|
} from './lib/types.js';
|
|
30
34
|
export {
|
|
31
35
|
createUsbmux,
|
package/src/lib/types.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { EventEmitter } from 'events';
|
|
|
6
6
|
|
|
7
7
|
import type { ServiceConnection } from '../service-connection.js';
|
|
8
8
|
import type { BaseService, Service } from '../services/ios/base-service.js';
|
|
9
|
+
import type { InterfaceOrientation } from '../services/ios/springboard-service/index.js';
|
|
9
10
|
import type { RemoteXpcConnection } from './remote-xpc/remote-xpc-connection.js';
|
|
10
11
|
import type { Device } from './usbmux/index.js';
|
|
11
12
|
|
|
@@ -311,6 +312,121 @@ export interface MobileConfigServiceWithConnection {
|
|
|
311
312
|
remoteXPC: RemoteXpcConnection;
|
|
312
313
|
}
|
|
313
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Represents the WebInspectorService
|
|
317
|
+
*/
|
|
318
|
+
export interface WebInspectorService extends BaseService {
|
|
319
|
+
/**
|
|
320
|
+
* Send a message to the WebInspector service
|
|
321
|
+
* @param selector The RPC selector (e.g., '_rpc_reportIdentifier:')
|
|
322
|
+
* @param args The arguments dictionary for the message
|
|
323
|
+
* @returns Promise that resolves when the message is sent
|
|
324
|
+
*/
|
|
325
|
+
sendMessage(selector: string, args?: PlistDictionary): Promise<void>;
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Listen to messages from the WebInspector service using async generator
|
|
329
|
+
* @yields PlistMessage - Messages received from the WebInspector service
|
|
330
|
+
*/
|
|
331
|
+
listenMessage(): AsyncGenerator<PlistMessage, void, unknown>;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Stop listening to messages
|
|
335
|
+
*/
|
|
336
|
+
stopListening(): void;
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Close the connection and clean up resources
|
|
340
|
+
*/
|
|
341
|
+
close(): Promise<void>;
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Get the connection ID being used for this service
|
|
345
|
+
* @returns The connection identifier
|
|
346
|
+
*/
|
|
347
|
+
getConnectionId(): string;
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Request application launch
|
|
351
|
+
* @param bundleId The bundle identifier of the application to launch
|
|
352
|
+
*/
|
|
353
|
+
requestApplicationLaunch(bundleId: string): Promise<void>;
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Get connected applications
|
|
357
|
+
*/
|
|
358
|
+
getConnectedApplications(): Promise<void>;
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Forward get listing for an application
|
|
362
|
+
* @param appId The application identifier
|
|
363
|
+
*/
|
|
364
|
+
forwardGetListing(appId: string): Promise<void>;
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Forward automation session request
|
|
368
|
+
* @param sessionId The session identifier
|
|
369
|
+
* @param appId The application identifier
|
|
370
|
+
* @param capabilities Optional session capabilities
|
|
371
|
+
*/
|
|
372
|
+
forwardAutomationSessionRequest(
|
|
373
|
+
sessionId: string,
|
|
374
|
+
appId: string,
|
|
375
|
+
capabilities?: PlistDictionary,
|
|
376
|
+
): Promise<void>;
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Forward socket setup for inspector connection
|
|
380
|
+
* @param sessionId The session identifier
|
|
381
|
+
* @param appId The application identifier
|
|
382
|
+
* @param pageId The page identifier
|
|
383
|
+
* @param automaticallyPause Whether to automatically pause (defaults to true)
|
|
384
|
+
*/
|
|
385
|
+
forwardSocketSetup(
|
|
386
|
+
sessionId: string,
|
|
387
|
+
appId: string,
|
|
388
|
+
pageId: number,
|
|
389
|
+
automaticallyPause?: boolean,
|
|
390
|
+
): Promise<void>;
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Forward socket data to a page
|
|
394
|
+
* @param sessionId The session identifier
|
|
395
|
+
* @param appId The application identifier
|
|
396
|
+
* @param pageId The page identifier
|
|
397
|
+
* @param data The data to send (will be JSON stringified)
|
|
398
|
+
*/
|
|
399
|
+
forwardSocketData(
|
|
400
|
+
sessionId: string,
|
|
401
|
+
appId: string,
|
|
402
|
+
pageId: number,
|
|
403
|
+
data: any,
|
|
404
|
+
): Promise<void>;
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Forward indicate web view
|
|
408
|
+
* @param appId The application identifier
|
|
409
|
+
* @param pageId The page identifier
|
|
410
|
+
* @param enable Whether to enable indication
|
|
411
|
+
*/
|
|
412
|
+
forwardIndicateWebView(
|
|
413
|
+
appId: string,
|
|
414
|
+
pageId: number,
|
|
415
|
+
enable: boolean,
|
|
416
|
+
): Promise<void>;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Represents a WebInspectorService instance with its associated RemoteXPC connection
|
|
421
|
+
* This allows callers to properly manage the connection lifecycle
|
|
422
|
+
*/
|
|
423
|
+
export interface WebInspectorServiceWithConnection {
|
|
424
|
+
/** The WebInspectorService instance */
|
|
425
|
+
webInspectorService: WebInspectorService;
|
|
426
|
+
/** The RemoteXPC connection that can be used to close the connection */
|
|
427
|
+
remoteXPC: RemoteXpcConnection;
|
|
428
|
+
}
|
|
429
|
+
|
|
314
430
|
/**
|
|
315
431
|
* Options for configuring syslog capture
|
|
316
432
|
*/
|
|
@@ -508,3 +624,113 @@ export interface MobileImageMounterServiceWithConnection {
|
|
|
508
624
|
/** The RemoteXPC connection for service management */
|
|
509
625
|
remoteXPC: RemoteXpcConnection;
|
|
510
626
|
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Represents the instance side of SpringboardService
|
|
630
|
+
*/
|
|
631
|
+
export interface SpringboardService extends BaseService {
|
|
632
|
+
/**
|
|
633
|
+
* Gets the icon state
|
|
634
|
+
* @returns Promise resolving to the icon state
|
|
635
|
+
* e.g.
|
|
636
|
+
* [
|
|
637
|
+
* {
|
|
638
|
+
* displayIdentifier: 'com.apple.MobileSMS',
|
|
639
|
+
* displayName: 'Messages',
|
|
640
|
+
* iconModDate: 2025-09-03T12:55:46.400Z,
|
|
641
|
+
* bundleVersion: '1402.700.63.2.1',
|
|
642
|
+
* bundleIdentifier: 'com.apple.MobileSMS'
|
|
643
|
+
* },
|
|
644
|
+
* {
|
|
645
|
+
* displayIdentifier: 'com.apple.measure',
|
|
646
|
+
* displayName: 'Measure',
|
|
647
|
+
* iconModDate: 2025-09-03T12:55:49.522Z,
|
|
648
|
+
* bundleVersion: '175.100.3.0.1',
|
|
649
|
+
* bundleIdentifier: 'com.apple.measure'
|
|
650
|
+
* },
|
|
651
|
+
* ...
|
|
652
|
+
* ]
|
|
653
|
+
*/
|
|
654
|
+
getIconState(): Promise<PlistDictionary>;
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* TODO: This does not work currently due to a bug in Apple protocol implementation (maybe?)
|
|
658
|
+
* Sets the icon state
|
|
659
|
+
* @param newState where is the payload from getIconState
|
|
660
|
+
*/
|
|
661
|
+
setIconState(newState: PlistDictionary[]): Promise<void>;
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Gets the icon PNG data for a given bundle ID
|
|
665
|
+
* @param bundleID The bundle ID of the app
|
|
666
|
+
* @returns {Promise<Buffer>} which is the PNG data of the app icon
|
|
667
|
+
*/
|
|
668
|
+
getIconPNGData(bundleID: string): Promise<Buffer>;
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* TODO: This does not work currently due to a bug in Apple protocol implementation
|
|
672
|
+
* Add payload structure when it is fixed
|
|
673
|
+
* Gets wallpaper info
|
|
674
|
+
* @param wallpaperName The name of the wallpaper
|
|
675
|
+
* @returns Promise resolving to the wallpaper info
|
|
676
|
+
*/
|
|
677
|
+
getWallpaperInfo(wallpaperName: string): Promise<PlistDictionary>;
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Gets homescreen icon metrics
|
|
681
|
+
* @returns {Promise<PlistDictionary>}
|
|
682
|
+
* e.g.
|
|
683
|
+
* {
|
|
684
|
+
* homeScreenIconHeight: 64,
|
|
685
|
+
* homeScreenIconMaxPages: 15,
|
|
686
|
+
* homeScreenWidth: 414,
|
|
687
|
+
* homeScreenHeight: 896,
|
|
688
|
+
* homeScreenIconDockMaxCount: 4,
|
|
689
|
+
* homeScreenIconFolderMaxPages: 15,
|
|
690
|
+
* homeScreenIconWidth: 64,
|
|
691
|
+
* homeScreenIconRows: 6,
|
|
692
|
+
* homeScreenIconColumns: 4,
|
|
693
|
+
* homeScreenIconFolderColumns: 3,
|
|
694
|
+
* homeScreenIconFolderRows: 3
|
|
695
|
+
* }
|
|
696
|
+
*/
|
|
697
|
+
getHomescreenIconMetrics(): Promise<PlistDictionary>;
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Gets the current interface orientation
|
|
701
|
+
* @returns {Promise<InterfaceOrientation>}
|
|
702
|
+
* 1 = Portrait
|
|
703
|
+
* 2 = PortraitUpsideDown
|
|
704
|
+
* 3 = Landscape
|
|
705
|
+
* 4 = LandscapeHomeToLeft
|
|
706
|
+
*/
|
|
707
|
+
getInterfaceOrientation(): Promise<InterfaceOrientation>;
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Gets wallpaper preview image for homescreen and lockscreen
|
|
711
|
+
* @param wallpaperName
|
|
712
|
+
* @returns {Promise<Buffer>} which is a wallpaper preview image
|
|
713
|
+
*/
|
|
714
|
+
getWallpaperPreviewImage(
|
|
715
|
+
wallpaperName: 'homescreen' | 'lockscreen',
|
|
716
|
+
): Promise<Buffer>;
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* TODO: This does not work currently due to a bug in Apple protocol implementation
|
|
720
|
+
* Use getWallpaperPreviewImage('homescreen') instead
|
|
721
|
+
* Gets wallpaper PNG data
|
|
722
|
+
* @param wallpaperName
|
|
723
|
+
* @returns {Promise<Buffer>}
|
|
724
|
+
*/
|
|
725
|
+
getWallpaperPNGData(wallpaperName: string): Promise<Buffer>;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Represents a SpringboardService instance with its associated RemoteXPC connection
|
|
730
|
+
*/
|
|
731
|
+
export interface SpringboardServiceWithConnection {
|
|
732
|
+
/** The SpringboardService instance */
|
|
733
|
+
springboardService: SpringboardService;
|
|
734
|
+
/** The RemoteXPC connection for service management */
|
|
735
|
+
remoteXPC: RemoteXpcConnection;
|
|
736
|
+
}
|
|
@@ -60,6 +60,15 @@ export class ServiceConnection extends BasePlistService {
|
|
|
60
60
|
return this.sendAndReceive(requestObj, timeout);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Sends a plist message without waiting for a response
|
|
65
|
+
* This is useful for fire-and-forget style communication
|
|
66
|
+
* @param message The message to send
|
|
67
|
+
*/
|
|
68
|
+
sendPlist(message: PlistDictionary): void {
|
|
69
|
+
this.send(message);
|
|
70
|
+
}
|
|
71
|
+
|
|
63
72
|
/**
|
|
64
73
|
* Gets the underlying socket
|
|
65
74
|
* @returns The socket used by this service
|
package/src/services/index.ts
CHANGED
|
@@ -6,12 +6,14 @@ import * as diagnostics from './ios/diagnostic-service/index.js';
|
|
|
6
6
|
import * as mobileImageMounter from './ios/mobile-image-mounter/index.js';
|
|
7
7
|
import * as syslog from './ios/syslog-service/index.js';
|
|
8
8
|
import * as tunnel from './ios/tunnel-service/index.js';
|
|
9
|
+
import * as webinspector from './ios/webinspector/index.js';
|
|
9
10
|
|
|
10
11
|
export {
|
|
11
12
|
diagnostics,
|
|
12
13
|
mobileImageMounter,
|
|
13
14
|
syslog,
|
|
14
15
|
tunnel,
|
|
16
|
+
webinspector,
|
|
15
17
|
TunnelRegistryServer,
|
|
16
18
|
startTunnelRegistryServer,
|
|
17
19
|
};
|