appium-ios-remotexpc 0.16.1 → 0.18.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 +234 -0
- package/build/src/lib/types.d.ts.map +1 -1
- package/build/src/services/ios/dvt/index.d.ts.map +1 -1
- package/build/src/services/ios/dvt/index.js +5 -2
- package/build/src/services/ios/dvt/instruments/application-listing.d.ts +46 -0
- package/build/src/services/ios/dvt/instruments/application-listing.d.ts.map +1 -0
- package/build/src/services/ios/dvt/instruments/application-listing.js +41 -0
- package/build/src/services/ios/dvt/instruments/device-info.d.ts +105 -0
- package/build/src/services/ios/dvt/instruments/device-info.d.ts.map +1 -0
- package/build/src/services/ios/dvt/instruments/device-info.js +179 -0
- package/build/src/services/ios/dvt/nskeyedarchiver-decoder.d.ts.map +1 -1
- package/build/src/services/ios/dvt/nskeyedarchiver-decoder.js +42 -17
- package/build/src/services.d.ts.map +1 -1
- package/build/src/services.js +6 -0
- package/package.json +3 -1
- package/src/index.ts +2 -0
- package/src/lib/types.ts +253 -0
- package/src/services/ios/dvt/index.ts +6 -2
- package/src/services/ios/dvt/instruments/application-listing.ts +97 -0
- package/src/services/ios/dvt/instruments/device-info.ts +217 -0
- package/src/services/ios/dvt/nskeyedarchiver-decoder.ts +62 -19
- package/src/services.ts +6 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { getLogger } from '../../../../lib/logger.js';
|
|
2
|
+
import { parseBinaryPlist } from '../../../../lib/plist/index.js';
|
|
3
|
+
import { MessageAux } from '../dtx-message.js';
|
|
4
|
+
const log = getLogger('DeviceInfo');
|
|
5
|
+
/**
|
|
6
|
+
* DeviceInfo service provides access to device information, file system,
|
|
7
|
+
* and process management through the DTX protocol.
|
|
8
|
+
*
|
|
9
|
+
* Available methods:
|
|
10
|
+
* - ls(path): List directory contents
|
|
11
|
+
* - execnameForPid(pid): Get executable path for a process ID
|
|
12
|
+
* - proclist(): Get list of running processes
|
|
13
|
+
* - isRunningPid(pid): Check if a process is running
|
|
14
|
+
* - hardwareInformation(): Get hardware details
|
|
15
|
+
* - networkInformation(): Get network configuration
|
|
16
|
+
* - machTimeInfo(): Get mach time information
|
|
17
|
+
* - machKernelName(): Get kernel name
|
|
18
|
+
* - kpepDatabase(): Get kernel performance event database
|
|
19
|
+
* - traceCodes(): Get trace code mappings
|
|
20
|
+
* - nameForUid(uid): Get username for UID
|
|
21
|
+
* - nameForGid(gid): Get group name for GID
|
|
22
|
+
*/
|
|
23
|
+
export class DeviceInfo {
|
|
24
|
+
dvt;
|
|
25
|
+
static IDENTIFIER = 'com.apple.instruments.server.services.deviceinfo';
|
|
26
|
+
channel = null;
|
|
27
|
+
constructor(dvt) {
|
|
28
|
+
this.dvt = dvt;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Initialize the device info channel
|
|
32
|
+
*/
|
|
33
|
+
async initialize() {
|
|
34
|
+
if (!this.channel) {
|
|
35
|
+
this.channel = await this.dvt.makeChannel(DeviceInfo.IDENTIFIER);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* List directory contents at the specified path.
|
|
40
|
+
* @param path - The directory path to list
|
|
41
|
+
* @returns Array of filenames
|
|
42
|
+
* @throws {Error} If the directory doesn't exist or cannot be accessed
|
|
43
|
+
*/
|
|
44
|
+
async ls(path) {
|
|
45
|
+
const result = await this.requestInformation('directoryListingForPath_', path);
|
|
46
|
+
if (result === null || result === undefined) {
|
|
47
|
+
throw new Error(`Failed to list directory: ${path}`);
|
|
48
|
+
}
|
|
49
|
+
log.debug(`Listed directory ${path}: ${result.length} entries`);
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get the full executable path for a given process ID.
|
|
54
|
+
* @param pid - The process identifier
|
|
55
|
+
* @returns The full path to the executable
|
|
56
|
+
*/
|
|
57
|
+
async execnameForPid(pid) {
|
|
58
|
+
return this.requestInformation('execnameForPid_', pid);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get the list of all running processes on the device.
|
|
62
|
+
* @returns Array of process information objects
|
|
63
|
+
*/
|
|
64
|
+
async proclist() {
|
|
65
|
+
const result = await this.requestInformation('runningProcesses');
|
|
66
|
+
if (!Array.isArray(result)) {
|
|
67
|
+
throw new Error(`proclist returned invalid data: expected an array, got ${typeof result} (${JSON.stringify(result)})`);
|
|
68
|
+
}
|
|
69
|
+
log.debug(`Retrieved ${result.length} running processes`);
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Check if a process with the given PID is currently running.
|
|
74
|
+
* @param pid - The process identifier to check
|
|
75
|
+
* @returns true if the process is running, false otherwise
|
|
76
|
+
*/
|
|
77
|
+
async isRunningPid(pid) {
|
|
78
|
+
return this.requestInformation('isRunningPid_', pid);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get hardware information about the device.
|
|
82
|
+
* @returns Object containing hardware information
|
|
83
|
+
*/
|
|
84
|
+
async hardwareInformation() {
|
|
85
|
+
return this.requestInformation('hardwareInformation');
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get network configuration information.
|
|
89
|
+
* @returns Object containing network information
|
|
90
|
+
*/
|
|
91
|
+
async networkInformation() {
|
|
92
|
+
return this.requestInformation('networkInformation');
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get mach kernel time information.
|
|
96
|
+
* @returns Object containing mach time info
|
|
97
|
+
*/
|
|
98
|
+
async machTimeInfo() {
|
|
99
|
+
return this.requestInformation('machTimeInfo');
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get the mach kernel name.
|
|
103
|
+
* @returns The kernel name string
|
|
104
|
+
*/
|
|
105
|
+
async machKernelName() {
|
|
106
|
+
return this.requestInformation('machKernelName');
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get the kernel performance event (kpep) database.
|
|
110
|
+
* @returns Object containing kpep database or null if not available
|
|
111
|
+
*/
|
|
112
|
+
async kpepDatabase() {
|
|
113
|
+
const kpepData = await this.requestInformation('kpepDatabase');
|
|
114
|
+
if (kpepData === null || kpepData === undefined) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
// The kpepDatabase is returned as binary plist data
|
|
118
|
+
if (Buffer.isBuffer(kpepData)) {
|
|
119
|
+
try {
|
|
120
|
+
return parseBinaryPlist(kpepData);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
log.warn('Failed to parse kpep database:', error);
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return kpepData;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get trace code mappings.
|
|
131
|
+
* @returns Object mapping trace codes (as hex strings) to descriptions
|
|
132
|
+
*/
|
|
133
|
+
async traceCodes() {
|
|
134
|
+
const codesFile = await this.requestInformation('traceCodesFile');
|
|
135
|
+
if (typeof codesFile !== 'string') {
|
|
136
|
+
return {};
|
|
137
|
+
}
|
|
138
|
+
const codes = {};
|
|
139
|
+
for (const line of codesFile.split('\n')) {
|
|
140
|
+
const match = line.trim().match(/^(\S+)\s+(.+)$/);
|
|
141
|
+
if (match) {
|
|
142
|
+
const [, hex, description] = match;
|
|
143
|
+
codes[hex] = description;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
log.debug(`Retrieved ${Object.keys(codes).length} trace codes`);
|
|
147
|
+
return codes;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get the username for a given user ID (UID).
|
|
151
|
+
* @param uid - The user identifier
|
|
152
|
+
* @returns The username string
|
|
153
|
+
*/
|
|
154
|
+
async nameForUid(uid) {
|
|
155
|
+
return this.requestInformation('nameForUID_', uid);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get the group name for a given group ID (GID).
|
|
159
|
+
* @param gid - The group identifier
|
|
160
|
+
* @returns The group name string
|
|
161
|
+
*/
|
|
162
|
+
async nameForGid(gid) {
|
|
163
|
+
return this.requestInformation('nameForGID_', gid);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Generic method to request information from the device.
|
|
167
|
+
* @param selectorName - The selector name to call
|
|
168
|
+
* @param arg - Optional argument to pass to the selector
|
|
169
|
+
* @returns The information object or value returned by the selector
|
|
170
|
+
* @private
|
|
171
|
+
*/
|
|
172
|
+
async requestInformation(selectorName, arg) {
|
|
173
|
+
await this.initialize();
|
|
174
|
+
const call = this.channel.call(selectorName);
|
|
175
|
+
const args = arg !== undefined ? new MessageAux().appendObj(arg) : undefined;
|
|
176
|
+
await call(args);
|
|
177
|
+
return this.channel.receivePlist();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nskeyedarchiver-decoder.d.ts","sourceRoot":"","sources":["../../../../../src/services/ios/dvt/nskeyedarchiver-decoder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"nskeyedarchiver-decoder.d.ts","sourceRoot":"","sources":["../../../../../src/services/ios/dvt/nskeyedarchiver-decoder.ts"],"names":[],"mappings":"AAKA;;;;;;;;;GASG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAM;gBAElB,IAAI,EAAE,GAAG;IAUrB;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO;IAa3C;;OAEG;IACH,MAAM,IAAI,GAAG;IA8Bb;;OAEG;IACH,OAAO,CAAC,YAAY;IAmHpB;;OAEG;IACH,OAAO,CAAC,WAAW;IAmBnB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAuBzB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAiBpD"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getLogger } from '../../../lib/logger.js';
|
|
2
2
|
const log = getLogger('NSKeyedArchiverDecoder');
|
|
3
|
+
const MAX_DECODE_DEPTH = 1000;
|
|
3
4
|
/**
|
|
4
5
|
* Decode NSKeyedArchiver formatted data into native JavaScript objects
|
|
5
6
|
*
|
|
@@ -66,42 +67,59 @@ export class NSKeyedArchiverDecoder {
|
|
|
66
67
|
/**
|
|
67
68
|
* Decode an object at a specific index
|
|
68
69
|
*/
|
|
69
|
-
decodeObject(index) {
|
|
70
|
+
decodeObject(index, visited = new Set(), depth = 0) {
|
|
71
|
+
// Prevent stack overflow with depth limit
|
|
72
|
+
if (depth > MAX_DECODE_DEPTH) {
|
|
73
|
+
log.warn(`Maximum decode depth exceeded at index ${index}`);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
70
76
|
if (index < 0 || index >= this.objects.length) {
|
|
71
77
|
return null;
|
|
72
78
|
}
|
|
79
|
+
// Prevent infinite recursion
|
|
80
|
+
if (visited.has(index)) {
|
|
81
|
+
return null; // Return null for circular references
|
|
82
|
+
}
|
|
73
83
|
// Check cache
|
|
74
84
|
if (this.decoded.has(index)) {
|
|
75
85
|
return this.decoded.get(index);
|
|
76
86
|
}
|
|
87
|
+
visited.add(index);
|
|
77
88
|
const obj = this.objects[index];
|
|
78
89
|
// Handle null marker
|
|
79
90
|
if (obj === '$null' || obj === null) {
|
|
91
|
+
visited.delete(index);
|
|
80
92
|
return null;
|
|
81
93
|
}
|
|
82
94
|
// Handle primitive types
|
|
83
95
|
if (typeof obj !== 'object') {
|
|
96
|
+
visited.delete(index);
|
|
84
97
|
return obj;
|
|
85
98
|
}
|
|
86
99
|
// Handle Buffer/binary data (eg. screenshots)
|
|
87
100
|
if (Buffer.isBuffer(obj)) {
|
|
88
101
|
this.decoded.set(index, obj);
|
|
102
|
+
visited.delete(index);
|
|
89
103
|
return obj;
|
|
90
104
|
}
|
|
91
105
|
// Handle UID references
|
|
92
106
|
if ('CF$UID' in obj) {
|
|
93
|
-
|
|
107
|
+
const result = this.decodeObject(obj.CF$UID, visited, depth + 1);
|
|
108
|
+
visited.delete(index);
|
|
109
|
+
return result;
|
|
94
110
|
}
|
|
95
111
|
// Handle NSDictionary (NS.keys + NS.objects) - check this FIRST before NSArray
|
|
96
112
|
if ('NS.keys' in obj && 'NS.objects' in obj) {
|
|
97
|
-
const result = this.decodeDictionary(obj['NS.keys'], obj['NS.objects']);
|
|
113
|
+
const result = this.decodeDictionary(obj['NS.keys'], obj['NS.objects'], visited, depth);
|
|
98
114
|
this.decoded.set(index, result);
|
|
115
|
+
visited.delete(index);
|
|
99
116
|
return result;
|
|
100
117
|
}
|
|
101
118
|
// Handle NSArray (NS.objects only, without NS.keys)
|
|
102
119
|
if ('NS.objects' in obj) {
|
|
103
|
-
const result = this.decodeArray(obj['NS.objects']);
|
|
120
|
+
const result = this.decodeArray(obj['NS.objects'], visited, depth);
|
|
104
121
|
this.decoded.set(index, result);
|
|
122
|
+
visited.delete(index);
|
|
105
123
|
return result;
|
|
106
124
|
}
|
|
107
125
|
// Handle regular objects - just return as-is but resolve references
|
|
@@ -112,11 +130,17 @@ export class NSKeyedArchiverDecoder {
|
|
|
112
130
|
}
|
|
113
131
|
if (typeof value === 'number') {
|
|
114
132
|
// Could be a reference or primitive
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
133
|
+
if (value < this.objects.length && value >= 0) {
|
|
134
|
+
const referenced = this.objects[value];
|
|
135
|
+
if (referenced &&
|
|
136
|
+
typeof referenced === 'object' &&
|
|
137
|
+
referenced !== '$null' &&
|
|
138
|
+
!visited.has(value)) {
|
|
139
|
+
result[key] = this.decodeObject(value, visited, depth + 1);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
result[key] = value;
|
|
143
|
+
}
|
|
120
144
|
}
|
|
121
145
|
else {
|
|
122
146
|
result[key] = value;
|
|
@@ -124,8 +148,8 @@ export class NSKeyedArchiverDecoder {
|
|
|
124
148
|
}
|
|
125
149
|
else if (typeof value === 'object' && value && 'CF$UID' in value) {
|
|
126
150
|
const uid = value.CF$UID;
|
|
127
|
-
if (typeof uid === 'number') {
|
|
128
|
-
result[key] = this.decodeObject(uid);
|
|
151
|
+
if (typeof uid === 'number' && !visited.has(uid)) {
|
|
152
|
+
result[key] = this.decodeObject(uid, visited, depth + 1);
|
|
129
153
|
}
|
|
130
154
|
else {
|
|
131
155
|
result[key] = value;
|
|
@@ -136,21 +160,22 @@ export class NSKeyedArchiverDecoder {
|
|
|
136
160
|
}
|
|
137
161
|
}
|
|
138
162
|
this.decoded.set(index, result);
|
|
163
|
+
visited.delete(index);
|
|
139
164
|
return result;
|
|
140
165
|
}
|
|
141
166
|
/**
|
|
142
167
|
* Decode an NSArray
|
|
143
168
|
*/
|
|
144
|
-
decodeArray(refs) {
|
|
169
|
+
decodeArray(refs, visited = new Set(), depth = 0) {
|
|
145
170
|
if (!Array.isArray(refs)) {
|
|
146
171
|
return [];
|
|
147
172
|
}
|
|
148
173
|
return refs.map((ref) => {
|
|
149
174
|
if (typeof ref === 'number') {
|
|
150
|
-
return this.decodeObject(ref);
|
|
175
|
+
return this.decodeObject(ref, visited, depth + 1);
|
|
151
176
|
}
|
|
152
177
|
else if (typeof ref === 'object' && ref && 'CF$UID' in ref) {
|
|
153
|
-
return this.decodeObject(ref.CF$UID);
|
|
178
|
+
return this.decodeObject(ref.CF$UID, visited, depth + 1);
|
|
154
179
|
}
|
|
155
180
|
return ref;
|
|
156
181
|
});
|
|
@@ -158,14 +183,14 @@ export class NSKeyedArchiverDecoder {
|
|
|
158
183
|
/**
|
|
159
184
|
* Decode an NSDictionary
|
|
160
185
|
*/
|
|
161
|
-
decodeDictionary(keyRefs, valueRefs) {
|
|
186
|
+
decodeDictionary(keyRefs, valueRefs, visited = new Set(), depth = 0) {
|
|
162
187
|
if (!Array.isArray(keyRefs) || !Array.isArray(valueRefs)) {
|
|
163
188
|
return {};
|
|
164
189
|
}
|
|
165
190
|
const result = {};
|
|
166
191
|
for (let i = 0; i < keyRefs.length && i < valueRefs.length; i++) {
|
|
167
|
-
const key = this.decodeObject(keyRefs[i]);
|
|
168
|
-
const value = this.decodeObject(valueRefs[i]);
|
|
192
|
+
const key = this.decodeObject(keyRefs[i], visited, depth + 1);
|
|
193
|
+
const value = this.decodeObject(valueRefs[i], visited, depth + 1);
|
|
169
194
|
if (typeof key === 'string') {
|
|
170
195
|
result[key] = value;
|
|
171
196
|
}
|
|
@@ -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,wBAAwB,EACxB,gCAAgC,EAChC,6BAA6B,EAC7B,iCAAiC,EACjC,uCAAuC,EACvC,sCAAsC,EACtC,mCAAmC,EACnC,gCAAgC,EAChC,aAAa,IAAI,iBAAiB,EAClC,iCAAiC,EAClC,MAAM,gBAAgB,CAAC;AACxB,OAAO,UAAU,MAAM,6BAA6B,CAAC;
|
|
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,wBAAwB,EACxB,gCAAgC,EAChC,6BAA6B,EAC7B,iCAAiC,EACjC,uCAAuC,EACvC,sCAAsC,EACtC,mCAAmC,EACnC,gCAAgC,EAChC,aAAa,IAAI,iBAAiB,EAClC,iCAAiC,EAClC,MAAM,gBAAgB,CAAC;AACxB,OAAO,UAAU,MAAM,6BAA6B,CAAC;AAqBrD,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;AAED,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,oBAAoB,CACxC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,6BAA6B,CAAC,CAYxC;AAED,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,mCAAmC,CAAC,CAY9C;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,CAAC,CAG5B;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAOvE;AAED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iCAAiC,CAAC,CAY5C;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,wBAAwB,CAAC,CAiCnC;AAED,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,MAAM;;;;;;;;GAO3D"}
|
package/build/src/services.js
CHANGED
|
@@ -5,7 +5,9 @@ import { TunnelApiClient } from './lib/tunnel/tunnel-api-client.js';
|
|
|
5
5
|
import AfcService from './services/ios/afc/index.js';
|
|
6
6
|
import DiagnosticsService from './services/ios/diagnostic-service/index.js';
|
|
7
7
|
import { DVTSecureSocketProxyService } from './services/ios/dvt/index.js';
|
|
8
|
+
import { ApplicationListing } from './services/ios/dvt/instruments/application-listing.js';
|
|
8
9
|
import { ConditionInducer } from './services/ios/dvt/instruments/condition-inducer.js';
|
|
10
|
+
import { DeviceInfo } from './services/ios/dvt/instruments/device-info.js';
|
|
9
11
|
import { Graphics } from './services/ios/dvt/instruments/graphics.js';
|
|
10
12
|
import { LocationSimulation } from './services/ios/dvt/instruments/location-simulation.js';
|
|
11
13
|
import { Screenshot } from './services/ios/dvt/instruments/screenshot.js';
|
|
@@ -137,14 +139,18 @@ export async function startDVTService(udid) {
|
|
|
137
139
|
const locationSimulation = new LocationSimulation(dvtService);
|
|
138
140
|
const conditionInducer = new ConditionInducer(dvtService);
|
|
139
141
|
const screenshot = new Screenshot(dvtService);
|
|
142
|
+
const appListing = new ApplicationListing(dvtService);
|
|
140
143
|
const graphics = new Graphics(dvtService);
|
|
144
|
+
const deviceInfo = new DeviceInfo(dvtService);
|
|
141
145
|
return {
|
|
142
146
|
remoteXPC: remoteXPC,
|
|
143
147
|
dvtService,
|
|
144
148
|
locationSimulation,
|
|
145
149
|
conditionInducer,
|
|
146
150
|
screenshot,
|
|
151
|
+
appListing,
|
|
147
152
|
graphics,
|
|
153
|
+
deviceInfo,
|
|
148
154
|
};
|
|
149
155
|
}
|
|
150
156
|
export async function createRemoteXPCConnection(udid) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appium-ios-remotexpc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"main": "build/src/index.js",
|
|
5
5
|
"types": "build/src/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
"test:dvt:location-simulation": "mocha test/integration/dvt_instruments/location-simulation-test.ts --exit --timeout 1m",
|
|
45
45
|
"test:dvt:condition-inducer": "mocha test/integration/dvt_instruments/condition-inducer-test.ts --exit --timeout 1m",
|
|
46
46
|
"test:dvt:screenshot": "mocha test/integration/dvt_instruments/screenshot-test.ts --exit --timeout 1m",
|
|
47
|
+
"test:dvt:device-info": "mocha test/integration/dvt_instruments/device-info-test.ts --exit --timeout 1m",
|
|
48
|
+
"test:dvt:applist": "mocha test/integration/dvt_instruments/app-listing-test.ts --exit --timeout 1m",
|
|
47
49
|
"test:tunnel-creation": "sudo tsx scripts/test-tunnel-creation.ts",
|
|
48
50
|
"test:tunnel-creation:lsof": "sudo tsx scripts/test-tunnel-creation.ts --keep-open"
|
|
49
51
|
},
|
package/src/index.ts
CHANGED
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 { iOSApplication } from '../services/ios/dvt/instruments/application-listing.js';
|
|
9
10
|
import type { LocationCoordinates } from '../services/ios/dvt/instruments/location-simulation.js';
|
|
10
11
|
import { ProvisioningProfile } from '../services/ios/misagent/provisioning-profile.js';
|
|
11
12
|
import type { PowerAssertionOptions } from '../services/ios/power-assertion/index.js';
|
|
@@ -521,6 +522,32 @@ export interface ScreenshotService {
|
|
|
521
522
|
getScreenshot(): Promise<Buffer>;
|
|
522
523
|
}
|
|
523
524
|
|
|
525
|
+
/**
|
|
526
|
+
* Application listing service interface
|
|
527
|
+
*/
|
|
528
|
+
export interface AppListService {
|
|
529
|
+
/**
|
|
530
|
+
* Get the list of iOS applications on the device
|
|
531
|
+
* @returns {Promise<iOSApplication>}
|
|
532
|
+
* e.g.
|
|
533
|
+
* [
|
|
534
|
+
* {
|
|
535
|
+
* ExtensionDictionary: { NSExtensionPointIdentifier: 'com.apple.mlhost.worker', ... },
|
|
536
|
+
* Version: '1.0',
|
|
537
|
+
* DisplayName: 'ModelMonitoringLighthousePlugin',
|
|
538
|
+
* CFBundleIdentifier: 'com.apple.aeroml.ModelMonitoringLighthouse.ModelMonitoringLighthousePlugin',
|
|
539
|
+
* BundlePath: '/System/Library/ExtensionKit/Extensions/ModelMonitoringLighthousePlugin.appex',
|
|
540
|
+
* ExecutableName: 'ModelMonitoringLighthousePlugin',
|
|
541
|
+
* Restricted: 1,
|
|
542
|
+
* Type: 'PluginKit',
|
|
543
|
+
* PluginIdentifier: 'com.apple.aeroml.ModelMonitoringLighthouse.ModelMonitoringLighthousePlugin',
|
|
544
|
+
* PluginUUID: 'AF17A1FE-E454-57C8-B963-0832FD71AB08'
|
|
545
|
+
* },
|
|
546
|
+
* ...
|
|
547
|
+
* ]
|
|
548
|
+
*/
|
|
549
|
+
list(): Promise<iOSApplication[]>;
|
|
550
|
+
}
|
|
524
551
|
/**
|
|
525
552
|
* Graphics service interface for OpenGL/graphics monitoring
|
|
526
553
|
*/
|
|
@@ -547,6 +574,228 @@ export interface GraphicsService {
|
|
|
547
574
|
messages(): AsyncGenerator<unknown, void, unknown>;
|
|
548
575
|
}
|
|
549
576
|
|
|
577
|
+
/**
|
|
578
|
+
* Process information
|
|
579
|
+
*/
|
|
580
|
+
export interface ProcessInfo {
|
|
581
|
+
/** Process identifier (may be negative for system services) */
|
|
582
|
+
pid: number;
|
|
583
|
+
|
|
584
|
+
/** Process name */
|
|
585
|
+
name?: string;
|
|
586
|
+
|
|
587
|
+
/** Indicates whether the process is an application */
|
|
588
|
+
isApplication: boolean;
|
|
589
|
+
|
|
590
|
+
/** Bundle identifier for application processes */
|
|
591
|
+
bundleIdentifier?: string;
|
|
592
|
+
|
|
593
|
+
/** Full path to the executable */
|
|
594
|
+
realAppName?: string;
|
|
595
|
+
|
|
596
|
+
/** Raw device start timestamp */
|
|
597
|
+
startDate?: {
|
|
598
|
+
/** Mach-based timestamp value */
|
|
599
|
+
'NS.time': number;
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
/** Whether crash analysis should include corpse sampling */
|
|
603
|
+
shouldAnalyzeWithCorpse?: boolean;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* DeviceInfo service interface for accessing device information,
|
|
608
|
+
* file system, and process management
|
|
609
|
+
*/
|
|
610
|
+
export interface DeviceInfoService {
|
|
611
|
+
/**
|
|
612
|
+
* List directory contents
|
|
613
|
+
* @param path The directory path to list
|
|
614
|
+
* @returns Array of filenames
|
|
615
|
+
*/
|
|
616
|
+
ls(path: string): Promise<string[]>;
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Get executable path for a process
|
|
620
|
+
* @param pid The process identifier
|
|
621
|
+
* @returns The full path to the executable
|
|
622
|
+
*/
|
|
623
|
+
execnameForPid(pid: number): Promise<string>;
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Get list of running processes
|
|
627
|
+
* @returns Array of process information
|
|
628
|
+
* @example
|
|
629
|
+
* ```typescript
|
|
630
|
+
* const processes = await deviceInfo.proclist();
|
|
631
|
+
* // Example response:
|
|
632
|
+
* // [
|
|
633
|
+
* // {
|
|
634
|
+
* // name: 'audioaccessoryd',
|
|
635
|
+
* // startDate: { 'NS.time': 786563887.8186979 },
|
|
636
|
+
* // isApplication: false,
|
|
637
|
+
* // pid: 77,
|
|
638
|
+
* // realAppName: '/usr/libexec/audioaccessoryd'
|
|
639
|
+
* // },
|
|
640
|
+
* // {
|
|
641
|
+
* // name: 'dmd',
|
|
642
|
+
* // startDate: { 'NS.time': 786563890.2724509 },
|
|
643
|
+
* // isApplication: false,
|
|
644
|
+
* // pid: -102,
|
|
645
|
+
* // realAppName: '/usr/libexec/dmd'
|
|
646
|
+
* // },
|
|
647
|
+
* // ...
|
|
648
|
+
* // ]
|
|
649
|
+
* ```
|
|
650
|
+
*/
|
|
651
|
+
proclist(): Promise<ProcessInfo[]>;
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Check if a process is running
|
|
655
|
+
* @param pid The process identifier
|
|
656
|
+
* @returns true if running, false otherwise
|
|
657
|
+
*/
|
|
658
|
+
isRunningPid(pid: number): Promise<boolean>;
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Get hardware information
|
|
662
|
+
* @returns Hardware information object
|
|
663
|
+
* @example
|
|
664
|
+
* ```typescript
|
|
665
|
+
* const hwInfo = await deviceInfo.hardwareInformation();
|
|
666
|
+
* // Example response:
|
|
667
|
+
* // {
|
|
668
|
+
* // numberOfPhysicalCpus: 6,
|
|
669
|
+
* // hwCPUsubtype: 2,
|
|
670
|
+
* // numberOfCpus: 6,
|
|
671
|
+
* // hwCPUtype: 16777228,
|
|
672
|
+
* // hwCPU64BitCapable: 1,
|
|
673
|
+
* // ProcessorTraceState: {
|
|
674
|
+
* // HWTraceVersion: '{\n "lib_ver": "libhwtrace @ tag libhwtrace-118.1",\n "api_ver": 21,\n ...\n}',
|
|
675
|
+
* // Streaming: false,
|
|
676
|
+
* // ProdTraceSupported: false,
|
|
677
|
+
* // AllocatedBufferSize: 0,
|
|
678
|
+
* // HWSupported: false,
|
|
679
|
+
* // HWConfigured: false,
|
|
680
|
+
* // RequestedBufferSize: 0,
|
|
681
|
+
* // DevTraceSupported: false
|
|
682
|
+
* // }
|
|
683
|
+
* // }
|
|
684
|
+
* ```
|
|
685
|
+
*/
|
|
686
|
+
hardwareInformation(): Promise<any>;
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Get network information
|
|
690
|
+
* @returns Network information object
|
|
691
|
+
* @example
|
|
692
|
+
* ```typescript
|
|
693
|
+
* const networkInfo = await deviceInfo.networkInformation();
|
|
694
|
+
* // Example response:
|
|
695
|
+
* // {
|
|
696
|
+
* // en2: 'Ethernet Adapter (en2)',
|
|
697
|
+
* // en0: 'Wi-Fi',
|
|
698
|
+
* // en1: 'Ethernet Adapter (en1)',
|
|
699
|
+
* // lo0: 'Loopback'
|
|
700
|
+
* // }
|
|
701
|
+
* ```
|
|
702
|
+
*/
|
|
703
|
+
networkInformation(): Promise<any>;
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Get mach time information
|
|
707
|
+
* @returns Mach time info array containing [machAbsoluteTime, numer, denom, machContinuousTime, systemTime, timezone]
|
|
708
|
+
* @example
|
|
709
|
+
* ```typescript
|
|
710
|
+
* const machTime = await deviceInfo.machTimeInfo();
|
|
711
|
+
* // Example response:
|
|
712
|
+
* // [
|
|
713
|
+
* // 1536005260807, // machAbsoluteTime
|
|
714
|
+
* // 125, // numer
|
|
715
|
+
* // 3, // denom
|
|
716
|
+
* // 1713684132688, // machContinuousTime
|
|
717
|
+
* // 1764942215.065243, // systemTime
|
|
718
|
+
* // 'Asia/Kolkata' // timezone
|
|
719
|
+
* // ]
|
|
720
|
+
* ```
|
|
721
|
+
*/
|
|
722
|
+
machTimeInfo(): Promise<any>;
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Get mach kernel name
|
|
726
|
+
* @returns Kernel name string
|
|
727
|
+
* @example
|
|
728
|
+
* ```typescript
|
|
729
|
+
* const kernelName = await deviceInfo.machKernelName();
|
|
730
|
+
* // Example response:
|
|
731
|
+
* // '/mach.release.t8030'
|
|
732
|
+
* ```
|
|
733
|
+
*/
|
|
734
|
+
machKernelName(): Promise<string>;
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Get kernel performance event database
|
|
738
|
+
* @returns KPEP database object or null
|
|
739
|
+
* @example
|
|
740
|
+
* ```typescript
|
|
741
|
+
* const kpep = await deviceInfo.kpepDatabase();
|
|
742
|
+
* // Example response:
|
|
743
|
+
* // {
|
|
744
|
+
* // system: {
|
|
745
|
+
* // cpu: {
|
|
746
|
+
* // config_counters: 1020,
|
|
747
|
+
* // marketing_name: 'Apple A13',
|
|
748
|
+
* // fixed_counters: 3,
|
|
749
|
+
* // aliases: { ... },
|
|
750
|
+
* // events: { ... },
|
|
751
|
+
* // architecture: 'arm64',
|
|
752
|
+
* // power_counters: -32
|
|
753
|
+
* // }
|
|
754
|
+
* // },
|
|
755
|
+
* // internal: false,
|
|
756
|
+
* // id: 'cpu_100000c_2_462504d2',
|
|
757
|
+
* // name: 'a13',
|
|
758
|
+
* // version: [1, 0]
|
|
759
|
+
* // }
|
|
760
|
+
* ```
|
|
761
|
+
*/
|
|
762
|
+
kpepDatabase(): Promise<any | null>;
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Get trace code mappings
|
|
766
|
+
* @returns Object mapping trace codes (as hex strings) to descriptions
|
|
767
|
+
* @example
|
|
768
|
+
* ```typescript
|
|
769
|
+
* const codes = await deviceInfo.traceCodes();
|
|
770
|
+
* // Example response:
|
|
771
|
+
* // {
|
|
772
|
+
* // '0x1020000': 'KTrap_DivideError',
|
|
773
|
+
* // '0x1020004': 'KTrap_Debug',
|
|
774
|
+
* // '0x1020008': 'KTrap_NMI',
|
|
775
|
+
* // '0x102000c': 'KTrap_Int3',
|
|
776
|
+
* // '0x1020010': 'KTrap_Overflow',
|
|
777
|
+
* // '0x1020014': 'KTrap_BoundRange',
|
|
778
|
+
* // ...
|
|
779
|
+
* // }
|
|
780
|
+
* ```
|
|
781
|
+
*/
|
|
782
|
+
traceCodes(): Promise<Record<string, string>>;
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Get username for UID
|
|
786
|
+
* @param uid The user identifier
|
|
787
|
+
* @returns Username string
|
|
788
|
+
*/
|
|
789
|
+
nameForUid(uid: number): Promise<string>;
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Get group name for GID
|
|
793
|
+
* @param gid The group identifier
|
|
794
|
+
* @returns Group name string
|
|
795
|
+
*/
|
|
796
|
+
nameForGid(gid: number): Promise<string>;
|
|
797
|
+
}
|
|
798
|
+
|
|
550
799
|
/**
|
|
551
800
|
* DVT service with connection
|
|
552
801
|
* This allows callers to properly manage the connection lifecycle
|
|
@@ -560,8 +809,12 @@ export interface DVTServiceWithConnection {
|
|
|
560
809
|
conditionInducer: ConditionInducerService;
|
|
561
810
|
/** The Screenshot service instance */
|
|
562
811
|
screenshot: ScreenshotService;
|
|
812
|
+
/** The Application Listing service instance */
|
|
813
|
+
appListing: AppListService;
|
|
563
814
|
/** The Graphics service instance */
|
|
564
815
|
graphics: GraphicsService;
|
|
816
|
+
/** The DeviceInfo service instance */
|
|
817
|
+
deviceInfo: DeviceInfoService;
|
|
565
818
|
/** The RemoteXPC connection that can be used to close the connection */
|
|
566
819
|
remoteXPC: RemoteXpcConnection;
|
|
567
820
|
}
|