@appium/coresim 1.1.0 → 1.2.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/lib/src/commands/permissions.d.ts +4 -2
- package/lib/src/commands/permissions.d.ts.map +1 -1
- package/lib/src/commands/permissions.js +9 -2
- package/lib/src/commands/permissions.js.map +1 -1
- package/lib/src/types.d.ts +15 -8
- package/lib/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/@appium+coresim.node +0 -0
- package/src/commands/permissions.ts +15 -2
- package/src/coresim.mm +30 -11
- package/src/native/tcc_privacy.h +17 -14
- package/src/native/tcc_privacy.mm +8 -5
- package/src/types.ts +17 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [1.2.0](https://github.com/appium/coresim/compare/v1.1.1...v1.2.0) (2026-09-17)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* Support more permission types ([#12](https://github.com/appium/coresim/issues/12)) ([164cb7d](https://github.com/appium/coresim/commit/164cb7d4d4bbc9029f18ad137f1215d82fa3ab79))
|
|
6
|
+
|
|
7
|
+
## [1.1.1](https://github.com/appium/coresim/compare/v1.1.0...v1.1.1) (2026-09-16)
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* Pass correct stream handle ([#11](https://github.com/appium/coresim/issues/11)) ([4584776](https://github.com/appium/coresim/commit/4584776f1d143bc709233cdfa2a557eb3bb99bd7))
|
|
12
|
+
|
|
1
13
|
## [1.1.0](https://github.com/appium/coresim/compare/v1.0.1...v1.1.0) (2026-09-16)
|
|
2
14
|
|
|
3
15
|
### Features
|
|
@@ -2,7 +2,7 @@ import type { NativeSimctl } from '../native-simctl.js';
|
|
|
2
2
|
import type { SimPermissionService, SimPermissionStatus } from '../types.js';
|
|
3
3
|
declare module '../native-simctl.js' {
|
|
4
4
|
interface NativeSimctl {
|
|
5
|
-
grantPermission(udid: string, service: SimPermissionService, bundleId: string): Promise<void>;
|
|
5
|
+
grantPermission(udid: string, service: SimPermissionService, bundleId: string, status?: 'granted' | 'limited'): Promise<void>;
|
|
6
6
|
revokePermission(udid: string, service: SimPermissionService, bundleId: string): Promise<void>;
|
|
7
7
|
resetPermission(udid: string, service: SimPermissionService, bundleId: string): Promise<void>;
|
|
8
8
|
getPermission(udid: string, service: SimPermissionService, bundleId: string): Promise<SimPermissionStatus>;
|
|
@@ -16,8 +16,10 @@ declare module '../native-simctl.js' {
|
|
|
16
16
|
* @param udid — UDID of the target device
|
|
17
17
|
* @param service — permission to grant, e.g. `"camera"`, `"contacts"`, `"photos"`
|
|
18
18
|
* @param bundleId — bundle identifier of the app the permission applies to
|
|
19
|
+
* @param status — defaults to `'granted'`. `'limited'` ("selected photos" access) is only valid
|
|
20
|
+
* for the `photos` service.
|
|
19
21
|
*/
|
|
20
|
-
export declare function grantPermission(this: NativeSimctl, udid: string, service: SimPermissionService, bundleId: string): Promise<void>;
|
|
22
|
+
export declare function grantPermission(this: NativeSimctl, udid: string, service: SimPermissionService, bundleId: string, status?: 'granted' | 'limited'): Promise<void>;
|
|
21
23
|
/**
|
|
22
24
|
* Revokes a previously granted privacy permission from the given app.
|
|
23
25
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../../src/commands/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAC,oBAAoB,EAAE,mBAAmB,EAAC,MAAM,aAAa,CAAC;AAG3E,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,YAAY;QACpB,eAAe,
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../../src/commands/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAC,oBAAoB,EAAE,mBAAmB,EAAC,MAAM,aAAa,CAAC;AAG3E,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,YAAY;QACpB,eAAe,CACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,GAC7B,OAAO,CAAC,IAAI,CAAC,CAAC;QACjB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/F,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC9F,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;KAC5G;CACF;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,SAAS,GAAG,SAAqB,GACxC,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAKf;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAKf;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,mBAAmB,CAAC,CAK9B"}
|
|
@@ -7,11 +7,16 @@ import { runCatchingAsync } from '../utils/index.js';
|
|
|
7
7
|
* @param udid — UDID of the target device
|
|
8
8
|
* @param service — permission to grant, e.g. `"camera"`, `"contacts"`, `"photos"`
|
|
9
9
|
* @param bundleId — bundle identifier of the app the permission applies to
|
|
10
|
+
* @param status — defaults to `'granted'`. `'limited'` ("selected photos" access) is only valid
|
|
11
|
+
* for the `photos` service.
|
|
10
12
|
*/
|
|
11
|
-
export async function grantPermission(udid, service, bundleId) {
|
|
13
|
+
export async function grantPermission(udid, service, bundleId, status = 'granted') {
|
|
12
14
|
return runCatchingAsync(async () => {
|
|
15
|
+
if (status === 'limited' && service !== 'photos') {
|
|
16
|
+
throw new Error(`'limited' is only a valid status for the 'photos' service, not '${service}'`);
|
|
17
|
+
}
|
|
13
18
|
const tccIdentifier = toTCCIdentifier(service);
|
|
14
|
-
return (await this._findDevice(udid)).grantPermission(tccIdentifier, bundleId);
|
|
19
|
+
return (await this._findDevice(udid)).grantPermission(tccIdentifier, bundleId, status);
|
|
15
20
|
});
|
|
16
21
|
}
|
|
17
22
|
/**
|
|
@@ -64,6 +69,7 @@ const SERVICE_TO_TCC_IDENTIFIER = {
|
|
|
64
69
|
calendar: 'kTCCServiceCalendar',
|
|
65
70
|
camera: 'kTCCServiceCamera',
|
|
66
71
|
contacts: 'kTCCServiceAddressBook',
|
|
72
|
+
faceid: 'kTCCServiceFaceID',
|
|
67
73
|
health: 'kTCCServiceMSO',
|
|
68
74
|
homekit: 'kTCCServiceWillow',
|
|
69
75
|
medialibrary: 'kTCCServiceMediaLibrary',
|
|
@@ -73,6 +79,7 @@ const SERVICE_TO_TCC_IDENTIFIER = {
|
|
|
73
79
|
reminders: 'kTCCServiceReminders',
|
|
74
80
|
siri: 'kTCCServiceSiri',
|
|
75
81
|
speech: 'kTCCServiceSpeechRecognition',
|
|
82
|
+
usertracking: 'kTCCServiceUserTracking',
|
|
76
83
|
};
|
|
77
84
|
function toTCCIdentifier(service) {
|
|
78
85
|
const identifier = SERVICE_TO_TCC_IDENTIFIER[service];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../../src/commands/permissions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../../src/commands/permissions.ts"],"names":[],"mappings":"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"}
|
package/lib/src/types.d.ts
CHANGED
|
@@ -67,8 +67,7 @@ export interface SimBootInfo {
|
|
|
67
67
|
* Result of `NativeSimctl.getPermission` — mirrors the TCC database's own auth states rather than
|
|
68
68
|
* a plain boolean, since `'unset'` (never prompted/decided) and `'denied'` (explicitly refused)
|
|
69
69
|
* are different states with different UI implications. `'limited'` only applies to `photos`
|
|
70
|
-
* ("selected photos" access)
|
|
71
|
-
* present if something else set it.
|
|
70
|
+
* ("selected photos" access).
|
|
72
71
|
*/
|
|
73
72
|
export type SimPermissionStatus = 'unset' | 'denied' | 'granted' | 'limited';
|
|
74
73
|
/**
|
|
@@ -77,7 +76,7 @@ export type SimPermissionStatus = 'unset' | 'denied' | 'granted' | 'limited';
|
|
|
77
76
|
* `location` isn't included since CoreLocation simulation has its own subsystem, not a plain TCC
|
|
78
77
|
* row (see CLAUDE.md).
|
|
79
78
|
*/
|
|
80
|
-
export type SimPermissionService = 'calendar' | 'camera' | 'contacts' | 'health' | 'homekit' | 'medialibrary' | 'microphone' | 'motion' | 'photos' | 'reminders' | 'siri' | 'speech';
|
|
79
|
+
export type SimPermissionService = 'calendar' | 'camera' | 'contacts' | 'faceid' | 'health' | 'homekit' | 'medialibrary' | 'microphone' | 'motion' | 'photos' | 'reminders' | 'siri' | 'speech' | 'usertracking';
|
|
81
80
|
/**
|
|
82
81
|
* A device's renderable display, as returned by `NativeSimctl.getDisplays()`.
|
|
83
82
|
*/
|
|
@@ -109,16 +108,24 @@ export interface ScreenshotOptions {
|
|
|
109
108
|
* Options for `NativeSimctl.spawnProcess`, passed through to CoreSimulator's
|
|
110
109
|
* `spawnWithPath:options:terminationQueue:terminationHandler:error:`. Only keys confirmed
|
|
111
110
|
* empirically (see CLAUDE.md) are typed here. CoreSimulator also recognizes
|
|
112
|
-
* `binpref`/`
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
111
|
+
* `binpref`/`wait_for_debugger`/`enableCheckedAllocations`/`stdin`/`stdout`/`stderr`, but the
|
|
112
|
+
* latter three's expected value shape (`NSFileHandle`/XPC file descriptor/`NSNumber`, never a path
|
|
113
|
+
* string) isn't yet confirmed safe — passing the wrong type crashes the whole process instead of
|
|
114
|
+
* throwing a catchable error (see CLAUDE.md) — so they're deliberately omitted here.
|
|
116
115
|
*/
|
|
117
116
|
export interface SpawnOptions {
|
|
118
117
|
/** Fully replaces argv, including argv[0] — `path` only selects the executable. */
|
|
119
118
|
arguments?: string[];
|
|
120
119
|
/** Merged additively into the spawned process's environment. */
|
|
121
120
|
environment?: Record<string, string>;
|
|
121
|
+
/**
|
|
122
|
+
* Defaults to `true` — required for CoreSimulator from Xcode 26.4+ to wire up the child's dyld
|
|
123
|
+
* shared-cache environment (see CLAUDE.md); without it, that CoreSimulator aborts the child with
|
|
124
|
+
* SIGABRT trying to load even `libSystem.B.dylib`. Defaults to `false` when `path` is `launchctl`
|
|
125
|
+
* itself, which needs to stay attached to the guest's own launchd bootstrap namespace to function
|
|
126
|
+
* at all — a standalone spawn is detached from it. Set explicitly to override either default.
|
|
127
|
+
*/
|
|
128
|
+
standalone?: boolean;
|
|
122
129
|
}
|
|
123
130
|
/** One running process/launchd job inside a device, as reported by `NativeSimctl.listProcesses`. */
|
|
124
131
|
export interface SimProcessInfo {
|
|
@@ -227,7 +234,7 @@ export interface NativeDeviceHandle {
|
|
|
227
234
|
setIncreaseContrast(enabled: boolean): Promise<void>;
|
|
228
235
|
getContentSize(): Promise<number>;
|
|
229
236
|
setContentSize(category: number): Promise<void>;
|
|
230
|
-
grantPermission(service: string, bundleId: string): Promise<void>;
|
|
237
|
+
grantPermission(service: string, bundleId: string, status?: 'granted' | 'limited'): Promise<void>;
|
|
231
238
|
revokePermission(service: string, bundleId: string): Promise<void>;
|
|
232
239
|
resetPermission(service: string, bundleId: string): Promise<void>;
|
|
233
240
|
getPermission(service: string, bundleId: string): Promise<SimPermissionStatus>;
|
package/lib/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,oBAAY,cAAc;IACxB,QAAQ,IAAI;IACZ,QAAQ,IAAI;IACZ,OAAO,IAAI;IACX,MAAM,IAAI;IACV,YAAY,IAAI;CACjB;AAED,uGAAuG;AACvG,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,cAAc,CAAC;IACtB,gHAAgH;IAChH,oBAAoB,EAAE,MAAM,CAAC;IAC7B,wGAAwG;IACxG,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,+GAA+G;AAC/G,MAAM,WAAW,iBAAiB;IAChC,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wGAAwG;AACxG,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;GAUG;AACH,oBAAY,aAAa;IACvB,OAAO,IAAI;IACX,sBAAsB,IAAI;IAC1B,kBAAkB,IAAI;IACtB,uDAAuD;IACvD,QAAQ,aAAa;CACtB;AAED,uGAAuG;AACvG,MAAM,WAAW,WAAW;IAC1B,8FAA8F;IAC9F,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,UAAU,EAAE,OAAO,CAAC;CACrB;AAED
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
Binary file
|
|
@@ -4,7 +4,12 @@ import {runCatchingAsync} from '../utils/index.js';
|
|
|
4
4
|
|
|
5
5
|
declare module '../native-simctl.js' {
|
|
6
6
|
interface NativeSimctl {
|
|
7
|
-
grantPermission(
|
|
7
|
+
grantPermission(
|
|
8
|
+
udid: string,
|
|
9
|
+
service: SimPermissionService,
|
|
10
|
+
bundleId: string,
|
|
11
|
+
status?: 'granted' | 'limited',
|
|
12
|
+
): Promise<void>;
|
|
8
13
|
revokePermission(udid: string, service: SimPermissionService, bundleId: string): Promise<void>;
|
|
9
14
|
resetPermission(udid: string, service: SimPermissionService, bundleId: string): Promise<void>;
|
|
10
15
|
getPermission(udid: string, service: SimPermissionService, bundleId: string): Promise<SimPermissionStatus>;
|
|
@@ -19,16 +24,22 @@ declare module '../native-simctl.js' {
|
|
|
19
24
|
* @param udid — UDID of the target device
|
|
20
25
|
* @param service — permission to grant, e.g. `"camera"`, `"contacts"`, `"photos"`
|
|
21
26
|
* @param bundleId — bundle identifier of the app the permission applies to
|
|
27
|
+
* @param status — defaults to `'granted'`. `'limited'` ("selected photos" access) is only valid
|
|
28
|
+
* for the `photos` service.
|
|
22
29
|
*/
|
|
23
30
|
export async function grantPermission(
|
|
24
31
|
this: NativeSimctl,
|
|
25
32
|
udid: string,
|
|
26
33
|
service: SimPermissionService,
|
|
27
34
|
bundleId: string,
|
|
35
|
+
status: 'granted' | 'limited' = 'granted',
|
|
28
36
|
): Promise<void> {
|
|
29
37
|
return runCatchingAsync(async () => {
|
|
38
|
+
if (status === 'limited' && service !== 'photos') {
|
|
39
|
+
throw new Error(`'limited' is only a valid status for the 'photos' service, not '${service}'`);
|
|
40
|
+
}
|
|
30
41
|
const tccIdentifier = toTCCIdentifier(service);
|
|
31
|
-
return (await this._findDevice(udid)).grantPermission(tccIdentifier, bundleId);
|
|
42
|
+
return (await this._findDevice(udid)).grantPermission(tccIdentifier, bundleId, status);
|
|
32
43
|
});
|
|
33
44
|
}
|
|
34
45
|
|
|
@@ -100,6 +111,7 @@ const SERVICE_TO_TCC_IDENTIFIER: Record<SimPermissionService, string> = {
|
|
|
100
111
|
calendar: 'kTCCServiceCalendar',
|
|
101
112
|
camera: 'kTCCServiceCamera',
|
|
102
113
|
contacts: 'kTCCServiceAddressBook',
|
|
114
|
+
faceid: 'kTCCServiceFaceID',
|
|
103
115
|
health: 'kTCCServiceMSO',
|
|
104
116
|
homekit: 'kTCCServiceWillow',
|
|
105
117
|
medialibrary: 'kTCCServiceMediaLibrary',
|
|
@@ -109,6 +121,7 @@ const SERVICE_TO_TCC_IDENTIFIER: Record<SimPermissionService, string> = {
|
|
|
109
121
|
reminders: 'kTCCServiceReminders',
|
|
110
122
|
siri: 'kTCCServiceSiri',
|
|
111
123
|
speech: 'kTCCServiceSpeechRecognition',
|
|
124
|
+
usertracking: 'kTCCServiceUserTracking',
|
|
112
125
|
};
|
|
113
126
|
|
|
114
127
|
function toTCCIdentifier(service: SimPermissionService): string {
|
package/src/coresim.mm
CHANGED
|
@@ -454,20 +454,28 @@ class NativeDevice : public Napi::ObjectWrap<NativeDevice> {
|
|
|
454
454
|
});
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
-
|
|
458
|
-
|
|
457
|
+
// `grantPermission`'s optional 3rd JS argument is a status override ("limited"), used only for
|
|
458
|
+
// kTCCServicePhotos's "selected photos" access — see commands/permissions.ts.
|
|
459
|
+
Napi::Value GrantPermission(const Napi::CallbackInfo& info) {
|
|
460
|
+
TCCAuthStatus desiredStatus = kTCCAuthGranted;
|
|
461
|
+
if (info.Length() > 2 && info[2].IsString() && info[2].As<Napi::String>().Utf8Value() == "limited") {
|
|
462
|
+
desiredStatus = kTCCAuthLimited;
|
|
463
|
+
}
|
|
464
|
+
return SetPermission(info, desiredStatus);
|
|
465
|
+
}
|
|
466
|
+
Napi::Value RevokePermission(const Napi::CallbackInfo& info) { return SetPermission(info, kTCCAuthDenied); }
|
|
459
467
|
|
|
460
468
|
// Writes directly to the simulator's own TCC.db instead of calling CoreSimulator's
|
|
461
469
|
// setPrivacyAccessForService:bundleID:granted:error: — that private method requires the calling
|
|
462
470
|
// process to hold an entitlement no ordinary npm package can obtain (see CLAUDE.md).
|
|
463
|
-
Napi::Value SetPermission(const Napi::CallbackInfo& info,
|
|
471
|
+
Napi::Value SetPermission(const Napi::CallbackInfo& info, TCCAuthStatus desiredStatus) {
|
|
464
472
|
id device = device_;
|
|
465
473
|
NSString* service = @(info[0].As<Napi::String>().Utf8Value().c_str());
|
|
466
474
|
NSString* bundleId = @(info[1].As<Napi::String>().Utf8Value().c_str());
|
|
467
|
-
return RunAsyncVoid(info.Env(), [device, service, bundleId,
|
|
475
|
+
return RunAsyncVoid(info.Env(), [device, service, bundleId, desiredStatus]() {
|
|
468
476
|
NSError* error = nil;
|
|
469
477
|
NSString* dataPath = coresim::DeviceDataPath(device);
|
|
470
|
-
ThrowIfFailed(coresim::SetTCCAccess(dataPath, service, bundleId,
|
|
478
|
+
ThrowIfFailed(coresim::SetTCCAccess(dataPath, service, bundleId, desiredStatus, &error), error);
|
|
471
479
|
});
|
|
472
480
|
}
|
|
473
481
|
|
|
@@ -688,10 +696,9 @@ class NativeDevice : public Napi::ObjectWrap<NativeDevice> {
|
|
|
688
696
|
return RunAsync<SpawnResult>(
|
|
689
697
|
env,
|
|
690
698
|
[device, path, userOptions, exitTsfn]() -> SpawnResult {
|
|
691
|
-
//
|
|
692
|
-
//
|
|
693
|
-
//
|
|
694
|
-
// reach the read end once the child exits.
|
|
699
|
+
// stdout/stderr must be raw fd numbers (NSNumber), not NSFileHandle objects — see
|
|
700
|
+
// CLAUDE.md. Our own copy of the write end still closes right after spawning (below) so
|
|
701
|
+
// EOF reaches the read end once the child exits.
|
|
695
702
|
NSPipe* stdoutPipe = [NSPipe pipe];
|
|
696
703
|
NSPipe* stderrPipe = [NSPipe pipe];
|
|
697
704
|
|
|
@@ -721,8 +728,20 @@ class NativeDevice : public Napi::ObjectWrap<NativeDevice> {
|
|
|
721
728
|
}
|
|
722
729
|
|
|
723
730
|
NSMutableDictionary* options = [userOptions mutableCopy];
|
|
724
|
-
options[@"stdout"] = stdoutPipe.fileHandleForWriting;
|
|
725
|
-
options[@"stderr"] = stderrPipe.fileHandleForWriting;
|
|
731
|
+
options[@"stdout"] = @(stdoutPipe.fileHandleForWriting.fileDescriptor);
|
|
732
|
+
options[@"stderr"] = @(stderrPipe.fileHandleForWriting.fileDescriptor);
|
|
733
|
+
// kSimDeviceSpawnStandalone (literal key "standalone" — confirmed via `strings` on the
|
|
734
|
+
// framework binary) — see CLAUDE.md/SpawnOptions. Defaults to YES: without it,
|
|
735
|
+
// CoreSimulator from Xcode 26.4+ never wires up the child's dyld shared-cache
|
|
736
|
+
// environment, aborting it with SIGABRT trying to load even libSystem.B.dylib. Except for
|
|
737
|
+
// launchctl itself, which needs to stay attached to the guest's launchd bootstrap
|
|
738
|
+
// namespace to function at all — checked here (by executable name) rather than left to
|
|
739
|
+
// each caller, so any spawnProcess(..., ".../launchctl", ...) call gets this right, not
|
|
740
|
+
// just listProcesses' own internal one.
|
|
741
|
+
if (!options[@"standalone"]) {
|
|
742
|
+
BOOL isLaunchctl = [path.lastPathComponent isEqualToString:@"launchctl"];
|
|
743
|
+
options[@"standalone"] = @(!isLaunchctl);
|
|
744
|
+
}
|
|
726
745
|
|
|
727
746
|
void (^terminationHandler)(int) = ^(int status) {
|
|
728
747
|
// Confirmed empirically (see CLAUDE.md): `status` is a raw wait(2)-style status, not a
|
package/src/native/tcc_privacy.h
CHANGED
|
@@ -4,22 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
namespace coresim {
|
|
6
6
|
|
|
7
|
-
// Grants or revokes a privacy permission for `bundleId` in the simulator's own TCC (privacy)
|
|
8
|
-
// database, located under the device's data directory (see DeviceDataPath, sim_device.h) at
|
|
9
|
-
// `Library/TCC/TCC.db`. `service` is the raw TCC service identifier (e.g. "kTCCServiceCamera"),
|
|
10
|
-
// not a friendly name — callers map friendly names to these before calling in (see
|
|
11
|
-
// commands/permissions.ts). Fails with a descriptive NSError if the device has never been booted
|
|
12
|
-
// (TCC.db doesn't exist yet) or the database can't be written to.
|
|
13
|
-
BOOL SetTCCAccess(NSString* dataPath, NSString* service, NSString* bundleId, BOOL granted, NSError** error);
|
|
14
|
-
|
|
15
|
-
// Resets a previously granted/revoked permission back to its default (unprompted, "unset") state
|
|
16
|
-
// by deleting its row from TCC.db, if one exists.
|
|
17
|
-
BOOL ResetTCCAccess(NSString* dataPath, NSString* service, NSString* bundleId, NSError** error);
|
|
18
|
-
|
|
19
7
|
// Own status codes GetTCCAccess reports through `outStatus` — deliberately not TCC's own raw wire
|
|
20
8
|
// values (which reuse 0 for "denied", not "no row"), so a missing row and an explicit denial are
|
|
21
|
-
// never confusable. `kTCCAuthLimited`
|
|
22
|
-
//
|
|
9
|
+
// never confusable. `kTCCAuthLimited` ("selected photos" access) is only meaningful for
|
|
10
|
+
// kTCCServicePhotos — SetTCCAccess itself doesn't enforce that (see commands/permissions.ts).
|
|
23
11
|
enum TCCAuthStatus {
|
|
24
12
|
kTCCAuthNotDetermined = 0,
|
|
25
13
|
kTCCAuthDenied,
|
|
@@ -27,6 +15,21 @@ enum TCCAuthStatus {
|
|
|
27
15
|
kTCCAuthLimited,
|
|
28
16
|
};
|
|
29
17
|
|
|
18
|
+
// Grants, revokes, or limits a privacy permission for `bundleId` in the simulator's own TCC
|
|
19
|
+
// (privacy) database, located under the device's data directory (see DeviceDataPath, sim_device.h)
|
|
20
|
+
// at `Library/TCC/TCC.db`. `service` is the raw TCC service identifier (e.g. "kTCCServiceCamera"),
|
|
21
|
+
// not a friendly name — callers map friendly names to these before calling in (see
|
|
22
|
+
// commands/permissions.ts). `desiredStatus` must be kTCCAuthGranted, kTCCAuthDenied, or
|
|
23
|
+
// kTCCAuthLimited — kTCCAuthNotDetermined isn't a writable state (see ResetTCCAccess). Fails with a
|
|
24
|
+
// descriptive NSError if the device has never been booted (TCC.db doesn't exist yet) or the
|
|
25
|
+
// database can't be written to.
|
|
26
|
+
BOOL SetTCCAccess(NSString* dataPath, NSString* service, NSString* bundleId, TCCAuthStatus desiredStatus,
|
|
27
|
+
NSError** error);
|
|
28
|
+
|
|
29
|
+
// Resets a previously granted/revoked permission back to its default (unprompted, "unset") state
|
|
30
|
+
// by deleting its row from TCC.db, if one exists.
|
|
31
|
+
BOOL ResetTCCAccess(NSString* dataPath, NSString* service, NSString* bundleId, NSError** error);
|
|
32
|
+
|
|
30
33
|
// Reads TCC.db for the current authorization status of (service, bundleId), translating either
|
|
31
34
|
// schema (see HasAuthValueColumn in the .mm) into the TCCAuthStatus values above. Fails with the
|
|
32
35
|
// same descriptive NSError as SetTCCAccess if the device has never been booted (no TCC.db yet).
|
|
@@ -136,7 +136,8 @@ bool CommitOrRollback(sqlite3* db, bool commit, NSError** error) {
|
|
|
136
136
|
|
|
137
137
|
} // namespace
|
|
138
138
|
|
|
139
|
-
BOOL SetTCCAccess(NSString* dataPath, NSString* service, NSString* bundleId,
|
|
139
|
+
BOOL SetTCCAccess(NSString* dataPath, NSString* service, NSString* bundleId, TCCAuthStatus desiredStatus,
|
|
140
|
+
NSError** error) {
|
|
140
141
|
sqlite3* db = OpenDatabase(dataPath, error);
|
|
141
142
|
if (!db) {
|
|
142
143
|
return NO;
|
|
@@ -149,9 +150,9 @@ BOOL SetTCCAccess(NSString* dataPath, NSString* service, NSString* bundleId, BOO
|
|
|
149
150
|
if (success) {
|
|
150
151
|
if (HasAuthValueColumn(db)) {
|
|
151
152
|
// `kTCCServicePhotos` rows use auth_version 2 (supports the "limited" library access
|
|
152
|
-
// introduced alongside it); every other service uses version 1.
|
|
153
|
-
// as a grantable status here — only plain granted/denied, matching grantPermission's BOOL.
|
|
153
|
+
// introduced alongside it); every other service uses version 1.
|
|
154
154
|
BOOL isPhotos = [service isEqualToString:@"kTCCServicePhotos"];
|
|
155
|
+
int authValue = desiredStatus == kTCCAuthGranted ? 2 : desiredStatus == kTCCAuthLimited ? 3 : 0;
|
|
155
156
|
success = ExecuteStatement(
|
|
156
157
|
db,
|
|
157
158
|
"INSERT INTO access (service, client, client_type, auth_value, auth_reason, auth_version, flags) "
|
|
@@ -159,17 +160,19 @@ BOOL SetTCCAccess(NSString* dataPath, NSString* service, NSString* bundleId, BOO
|
|
|
159
160
|
[&](sqlite3_stmt* stmt) {
|
|
160
161
|
BindText(stmt, 1, service);
|
|
161
162
|
BindText(stmt, 2, bundleId);
|
|
162
|
-
sqlite3_bind_int(stmt, 3,
|
|
163
|
+
sqlite3_bind_int(stmt, 3, authValue);
|
|
163
164
|
sqlite3_bind_int(stmt, 4, isPhotos ? 2 : 1);
|
|
164
165
|
},
|
|
165
166
|
error);
|
|
166
167
|
} else {
|
|
168
|
+
// Pre-iOS 14 schema has no auth_value column, so "limited" collapses to a plain grant here.
|
|
169
|
+
BOOL allowed = desiredStatus != kTCCAuthDenied;
|
|
167
170
|
success = ExecuteStatement(
|
|
168
171
|
db, "REPLACE INTO access (service, client, client_type, allowed, prompt_count) VALUES (?, ?, 0, ?, 1)",
|
|
169
172
|
[&](sqlite3_stmt* stmt) {
|
|
170
173
|
BindText(stmt, 1, service);
|
|
171
174
|
BindText(stmt, 2, bundleId);
|
|
172
|
-
sqlite3_bind_int(stmt, 3,
|
|
175
|
+
sqlite3_bind_int(stmt, 3, allowed ? 1 : 0);
|
|
173
176
|
},
|
|
174
177
|
error);
|
|
175
178
|
}
|
package/src/types.ts
CHANGED
|
@@ -73,8 +73,7 @@ export interface SimBootInfo {
|
|
|
73
73
|
* Result of `NativeSimctl.getPermission` — mirrors the TCC database's own auth states rather than
|
|
74
74
|
* a plain boolean, since `'unset'` (never prompted/decided) and `'denied'` (explicitly refused)
|
|
75
75
|
* are different states with different UI implications. `'limited'` only applies to `photos`
|
|
76
|
-
* ("selected photos" access)
|
|
77
|
-
* present if something else set it.
|
|
76
|
+
* ("selected photos" access).
|
|
78
77
|
*/
|
|
79
78
|
export type SimPermissionStatus = 'unset' | 'denied' | 'granted' | 'limited';
|
|
80
79
|
|
|
@@ -88,6 +87,7 @@ export type SimPermissionService =
|
|
|
88
87
|
| 'calendar'
|
|
89
88
|
| 'camera'
|
|
90
89
|
| 'contacts'
|
|
90
|
+
| 'faceid'
|
|
91
91
|
| 'health'
|
|
92
92
|
| 'homekit'
|
|
93
93
|
| 'medialibrary'
|
|
@@ -96,7 +96,8 @@ export type SimPermissionService =
|
|
|
96
96
|
| 'photos'
|
|
97
97
|
| 'reminders'
|
|
98
98
|
| 'siri'
|
|
99
|
-
| 'speech'
|
|
99
|
+
| 'speech'
|
|
100
|
+
| 'usertracking';
|
|
100
101
|
|
|
101
102
|
/**
|
|
102
103
|
* A device's renderable display, as returned by `NativeSimctl.getDisplays()`.
|
|
@@ -131,16 +132,24 @@ export interface ScreenshotOptions {
|
|
|
131
132
|
* Options for `NativeSimctl.spawnProcess`, passed through to CoreSimulator's
|
|
132
133
|
* `spawnWithPath:options:terminationQueue:terminationHandler:error:`. Only keys confirmed
|
|
133
134
|
* empirically (see CLAUDE.md) are typed here. CoreSimulator also recognizes
|
|
134
|
-
* `binpref`/`
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
135
|
+
* `binpref`/`wait_for_debugger`/`enableCheckedAllocations`/`stdin`/`stdout`/`stderr`, but the
|
|
136
|
+
* latter three's expected value shape (`NSFileHandle`/XPC file descriptor/`NSNumber`, never a path
|
|
137
|
+
* string) isn't yet confirmed safe — passing the wrong type crashes the whole process instead of
|
|
138
|
+
* throwing a catchable error (see CLAUDE.md) — so they're deliberately omitted here.
|
|
138
139
|
*/
|
|
139
140
|
export interface SpawnOptions {
|
|
140
141
|
/** Fully replaces argv, including argv[0] — `path` only selects the executable. */
|
|
141
142
|
arguments?: string[];
|
|
142
143
|
/** Merged additively into the spawned process's environment. */
|
|
143
144
|
environment?: Record<string, string>;
|
|
145
|
+
/**
|
|
146
|
+
* Defaults to `true` — required for CoreSimulator from Xcode 26.4+ to wire up the child's dyld
|
|
147
|
+
* shared-cache environment (see CLAUDE.md); without it, that CoreSimulator aborts the child with
|
|
148
|
+
* SIGABRT trying to load even `libSystem.B.dylib`. Defaults to `false` when `path` is `launchctl`
|
|
149
|
+
* itself, which needs to stay attached to the guest's own launchd bootstrap namespace to function
|
|
150
|
+
* at all — a standalone spawn is detached from it. Set explicitly to override either default.
|
|
151
|
+
*/
|
|
152
|
+
standalone?: boolean;
|
|
144
153
|
}
|
|
145
154
|
|
|
146
155
|
/** One running process/launchd job inside a device, as reported by `NativeSimctl.listProcesses`. */
|
|
@@ -262,7 +271,7 @@ export interface NativeDeviceHandle {
|
|
|
262
271
|
setIncreaseContrast(enabled: boolean): Promise<void>;
|
|
263
272
|
getContentSize(): Promise<number>;
|
|
264
273
|
setContentSize(category: number): Promise<void>;
|
|
265
|
-
grantPermission(service: string, bundleId: string): Promise<void>;
|
|
274
|
+
grantPermission(service: string, bundleId: string, status?: 'granted' | 'limited'): Promise<void>;
|
|
266
275
|
revokePermission(service: string, bundleId: string): Promise<void>;
|
|
267
276
|
resetPermission(service: string, bundleId: string): Promise<void>;
|
|
268
277
|
getPermission(service: string, bundleId: string): Promise<SimPermissionStatus>;
|