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.
@@ -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":"AAIA;;;;;;;;;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;IAkFpB;;OAEG;IACH,OAAO,CAAC,WAAW;IAenB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAkBzB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAiBpD"}
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
- return this.decodeObject(obj.CF$UID);
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
- const referenced = this.objects[value];
116
- if (referenced &&
117
- typeof referenced === 'object' &&
118
- referenced !== '$null') {
119
- result[key] = this.decodeObject(value);
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;AAmBrD,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,CA6BnC;AAED,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,MAAM;;;;;;;;GAO3D"}
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"}
@@ -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.16.1",
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
@@ -29,6 +29,8 @@ export type {
29
29
  ConditionInducerService,
30
30
  ScreenshotService,
31
31
  GraphicsService,
32
+ DeviceInfoService,
33
+ ProcessInfo,
32
34
  ConditionProfile,
33
35
  ConditionGroup,
34
36
  SocketInfo,
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
  }