serve-sim 0.1.49 → 0.1.50-beta.144.1
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/Sources/SimCameraInjector/SimCamFakes.h +1 -0
- package/Sources/SimCameraInjector/SimCamFakes.m +15 -0
- package/Sources/SimCameraInjector/SimCamSwizzles.m +17 -13
- package/dist/middleware.js +2 -2
- package/dist/serve-sim.js +3 -3
- package/dist/simcam/libSimCameraInjector.dylib +0 -0
- package/package.json +1 -1
|
@@ -69,6 +69,7 @@ void SimCamLogSwallowedRuntimeError(NSString *via, id object, NSDictionary *user
|
|
|
69
69
|
|
|
70
70
|
AVCaptureDeviceFormat *SimCamSharedFakeFormat(void);
|
|
71
71
|
AVCaptureDevice *SimCamFakeDeviceForPosition(AVCaptureDevicePosition p);
|
|
72
|
+
NSArray<AVCaptureDevice *> *SimCamFakeDevices(void);
|
|
72
73
|
AVCaptureDeviceInput *SimCamFakeInputForPosition(AVCaptureDevicePosition p);
|
|
73
74
|
NSArray<AVCaptureInputPort *> *SimCamFakePortsForInput(AVCaptureInput *input);
|
|
74
75
|
|
|
@@ -143,6 +143,14 @@ void SimCamLogSwallowedRuntimeError(NSString *via, id object, NSDictionary *user
|
|
|
143
143
|
- (BOOL)isVideoBinned { return NO; }
|
|
144
144
|
- (BOOL)isVideoHDRSupported { return NO; }
|
|
145
145
|
- (BOOL)isVideoStabilizationModeSupported:(AVCaptureVideoStabilizationMode)m { return NO; }
|
|
146
|
+
// AVFoundation's description reads private state this instance doesn't have.
|
|
147
|
+
- (NSString *)description {
|
|
148
|
+
CMFormatDescriptionRef format = self.formatDescription;
|
|
149
|
+
CMVideoDimensions size = CMVideoFormatDescriptionGetDimensions(format);
|
|
150
|
+
FourCharCode subtype = CMFormatDescriptionGetMediaSubType(format);
|
|
151
|
+
return [NSString stringWithFormat:@"<SimCamFakeFormat %dx%d %c%c%c%c>", size.width, size.height,
|
|
152
|
+
(char)(subtype >> 24), (char)(subtype >> 16), (char)(subtype >> 8), (char)subtype];
|
|
153
|
+
}
|
|
146
154
|
- (CGFloat)videoMaxZoomFactor { return 16.0; }
|
|
147
155
|
- (CGFloat)videoZoomFactorUpscaleThreshold { return 1.0; }
|
|
148
156
|
- (AVCaptureAutoFocusSystem)autoFocusSystem { return AVCaptureAutoFocusSystemNone; }
|
|
@@ -293,6 +301,13 @@ AVCaptureDevice *SimCamFakeDeviceForPosition(AVCaptureDevicePosition p) {
|
|
|
293
301
|
return p == AVCaptureDevicePositionBack ? back : front;
|
|
294
302
|
}
|
|
295
303
|
|
|
304
|
+
NSArray<AVCaptureDevice *> *SimCamFakeDevices(void) {
|
|
305
|
+
return @[
|
|
306
|
+
SimCamFakeDeviceForPosition(AVCaptureDevicePositionFront),
|
|
307
|
+
SimCamFakeDeviceForPosition(AVCaptureDevicePositionBack),
|
|
308
|
+
];
|
|
309
|
+
}
|
|
310
|
+
|
|
296
311
|
#pragma mark - SimCamFakeConnection
|
|
297
312
|
|
|
298
313
|
@interface AVCaptureConnection (SimCamPrivate)
|
|
@@ -129,22 +129,24 @@ BOOL SwizzleInstanceMethod(Class cls, SEL orig, SEL swiz) {
|
|
|
129
129
|
}
|
|
130
130
|
return [self simcam_defaultDeviceWithMediaType:m];
|
|
131
131
|
}
|
|
132
|
+
+ (AVCaptureDevice *)simcam_deviceWithUniqueID:(NSString *)uniqueID {
|
|
133
|
+
for (AVCaptureDevice *fake in SimCamFakeDevices()) {
|
|
134
|
+
if ([fake.uniqueID isEqualToString:uniqueID]) {
|
|
135
|
+
simcam_log(@"deviceWithUniqueID: %@ → fake", uniqueID);
|
|
136
|
+
return fake;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return [self simcam_deviceWithUniqueID:uniqueID];
|
|
140
|
+
}
|
|
132
141
|
+ (NSArray<AVCaptureDevice *> *)simcam_devicesWithMediaType:(AVMediaType)m {
|
|
133
142
|
if ([m isEqualToString:AVMediaTypeVideo]) {
|
|
134
|
-
return
|
|
135
|
-
SimCamFakeDeviceForPosition(AVCaptureDevicePositionFront),
|
|
136
|
-
SimCamFakeDeviceForPosition(AVCaptureDevicePositionBack),
|
|
137
|
-
];
|
|
143
|
+
return SimCamFakeDevices();
|
|
138
144
|
}
|
|
139
145
|
return [self simcam_devicesWithMediaType:m];
|
|
140
146
|
}
|
|
141
147
|
+ (NSArray<AVCaptureDevice *> *)simcam_devices {
|
|
142
148
|
NSArray *real = [self simcam_devices];
|
|
143
|
-
|
|
144
|
-
SimCamFakeDeviceForPosition(AVCaptureDevicePositionFront),
|
|
145
|
-
SimCamFakeDeviceForPosition(AVCaptureDevicePositionBack),
|
|
146
|
-
];
|
|
147
|
-
return [fakes arrayByAddingObjectsFromArray:real ?: @[]];
|
|
149
|
+
return [SimCamFakeDevices() arrayByAddingObjectsFromArray:real ?: @[]];
|
|
148
150
|
}
|
|
149
151
|
@end
|
|
150
152
|
|
|
@@ -160,10 +162,9 @@ BOOL SwizzleInstanceMethod(Class cls, SEL orig, SEL swiz) {
|
|
|
160
162
|
[self simcam_discoverySessionWithDeviceTypes:types mediaType:m position:p];
|
|
161
163
|
if ([m isEqualToString:AVMediaTypeVideo] || m == nil) {
|
|
162
164
|
NSMutableArray *list = [NSMutableArray new];
|
|
163
|
-
|
|
164
|
-
[list addObject:
|
|
165
|
-
|
|
166
|
-
[list addObject:SimCamFakeDeviceForPosition(AVCaptureDevicePositionBack)];
|
|
165
|
+
for (AVCaptureDevice *fake in SimCamFakeDevices()) {
|
|
166
|
+
if (p == AVCaptureDevicePositionUnspecified || p == fake.position) [list addObject:fake];
|
|
167
|
+
}
|
|
167
168
|
@try {
|
|
168
169
|
[real setValue:list forKey:@"devices"];
|
|
169
170
|
} @catch (__unused id e) {
|
|
@@ -1221,6 +1222,9 @@ void SimCamInstallSwizzles(void) {
|
|
|
1221
1222
|
@selector(devicesWithMediaType:),
|
|
1222
1223
|
@selector(simcam_devicesWithMediaType:));
|
|
1223
1224
|
SwizzleClassMethod(dev, @selector(devices), @selector(simcam_devices));
|
|
1225
|
+
SwizzleClassMethod(dev,
|
|
1226
|
+
@selector(deviceWithUniqueID:),
|
|
1227
|
+
@selector(simcam_deviceWithUniqueID:));
|
|
1224
1228
|
|
|
1225
1229
|
Class disc = [AVCaptureDeviceDiscoverySession class];
|
|
1226
1230
|
SwizzleClassMethod(disc,
|
package/dist/middleware.js
CHANGED
|
@@ -57,7 +57,7 @@ Content-Length: ${Q.length}\r
|
|
|
57
57
|
Content-Type: image/jpeg\r
|
|
58
58
|
Content-Length: ${$}\r
|
|
59
59
|
\r
|
|
60
|
-
`,"ascii")}function OZ($){let Q=Buffer.allocUnsafe(5+$.length);return Q.writeUInt32BE($.length+1,0),Q[4]=MZ,Q.set($,5),Q}var RZ={portrait:M6.portrait,portrait_upside_down:M6.portraitUpsideDown,landscape_left:M6.landscapeLeft,landscape_right:M6.landscapeRight};function a7($){if($.writableEnded||$.destroyed||!$.writableNeedDrain)return Promise.resolve();return new Promise((Q)=>{let Z=()=>{J(),Q()},J=()=>{$.off("drain",Z),$.off("close",Z),$.off("error",Z)};$.once("drain",Z),$.once("close",Z),$.once("error",Z)})}async function r7($,Q){if(await a7($),$.writableEnded||$.destroyed)return!1;if(!Q()||$.writableNeedDrain)await a7($);return!$.writableEnded&&!$.destroyed}function m9(){return{open:0,frames:0,last_frame_at:null,last_error:null}}function _Z($){return{error:"duo_renderer_unavailable",reason:L0($.reason),stage:$.stage,details:L0($.details)}}class t7{udid;dependencies;capture;hid;unsubscribeMjpeg;unsubscribeSize;phase="unstarted";startPromise;width=0;height=0;orientation="portrait";latestJpegBuffer=null;latestJpegLength=0;hidSockets=new Set;duo;duoMonitor;followedCover;primaryDuoPanel;poseQueue=Promise.resolve();duoStateQueue=Promise.resolve();streamResponses=new Set;touchGestureLog;streamHealth={mjpeg:m9(),avcc:m9(),"3d":m9()};deviceHubShadowed;recording;rendererFailureEvent;constructor($,Q){this.udid=$;this.dependencies=Q;this.hid=Q?.hid??new T9($),this.capture=Q?.capture??new P9($),this.duo=new f9({createRenderer:(Z)=>Q?.createDuoRenderer?Q.createDuoRenderer(Z):new k9(Z),onProjection:(Z)=>{for(let J of this.hidSockets)J.send(Z)},onStreamError:(Z,J)=>this.handleStreamError(Z,J,"3d"),onRendererEvent:(Z)=>this.recordRendererEvent(Z),onFrameDelivered:()=>this.noteStreamFrame("3d")})}start(){if(this.phase==="stopped")return Promise.reject(Error(`Device session ${this.udid} is closed`));if(this.startPromise)return this.startPromise;this.phase="starting";let $=(async()=>{if(await this.capture.start(),this.isStopped())throw Error(`Device session ${this.udid} closed while starting`);let Q=await this.capture.subscribeSize((Z)=>this.onScreenSize(Z.width,Z.height));if(this.isStopped())throw await Q(),Error(`Device session ${this.udid} closed while starting`);if(this.unsubscribeSize=Q,this.phase="running",await this.hid.isFoldable?.()&&!this.isStopped()){let Z=await this.capture.subscribeMjpeg((Y)=>this.onSharedMjpegFrame(Y));if(this.isStopped()){await Z();return}if(this.unsubscribeMjpeg=Z,!this.dependencies?.createDuoMonitor)U7(this.udid).then((Y)=>{this.deviceHubShadowed=Y});let J=this.dependencies?.createDuoMonitor??((Y,X)=>new v9(Y,X));this.duoMonitor=J(this.udid,(Y)=>{this.followDuoState(Y).catch((X)=>{console.error("[duo] State update failed:",X)})})}})().catch((Q)=>{if(this.phase==="starting")this.phase="failed";throw Q});return $.catch(()=>{}),this.startPromise=$,$}isStopped(){return this.phase==="stopped"}close(){if(this.phase==="stopped")return;this.phase="stopped",this.duo.close(),this.duoMonitor?.close();for(let $ of this.hidSockets)this.runCleanup("HID socket close",()=>$.close());for(let $ of this.streamResponses)this.runCleanup("stream response close",()=>{$.destroy()});if(this.streamResponses.clear(),this.unsubscribeMjpeg)this.runCleanup("shared MJPEG unsubscribe",this.unsubscribeMjpeg);if(this.unsubscribeSize)this.runCleanup("size unsubscribe",this.unsubscribeSize);if(this.hidSockets.clear(),this.recording){let $=this.recording;if(this.recording=void 0,$.limitTimer)clearTimeout($.limitTimer);if($.holdTimer)clearTimeout($.holdTimer);if($.phase!=="stopping"){let Q=$.finalizing?.catch(()=>{})??Promise.resolve();this.runCleanup("recording cleanup",()=>Q.then(()=>v0($.path,{force:!0})))}}this.runCleanup("HID close",()=>this.hid.close?.()),this.runCleanup("capture stop",()=>this.capture.stop())}followDuoState($){let Q=this.duoStateQueue.then(()=>this.applyDuoState($));return this.duoStateQueue=Q.catch(()=>{}),Q}activePanel(){return I7({primaryPanel:this.primaryDuoPanel,hingeDegrees:this.duo.hinge})}async applyDuoState($){if(this.isStopped())return;if($.hingeDegrees!=null)this.duo.noteHinge($.hingeDegrees);if($.primaryPanel)this.primaryDuoPanel=$.primaryPanel;let Q=this.activePanel(),Z=x7(Q);if(($.hingeDegrees!=null||$.primaryPanel)&&Q==="cover"!==this.followedCover)await this.capture.setPreferredScreenSize(Z.width,Z.height),this.followedCover=Q==="cover";let J=$.orientations[Q];if(J)this.orientation=J;this.duo.setPanel(Q),this.broadcastConfig(),this.duo.requestFrame()}onScreenSize($,Q){if($===this.width&&Q===this.height)return;this.width=$,this.height=Q,this.broadcastConfig()}async onSharedMjpegFrame($){let{width:Q,height:Z,data:J}=$;if(this.onScreenSize(Q,Z),!this.latestJpegBuffer||this.latestJpegBuffer.length<J.length){let X=this.latestJpegBuffer?.length??0;this.latestJpegBuffer=Buffer.allocUnsafe(Math.max(J.length,X*2))}this.latestJpegBuffer.set(J,0),this.latestJpegLength=J.length;let Y=this.latestJpeg();if(Y)this.duo.onCapturedFrame(Y)}latestJpeg(){if(!this.latestJpegBuffer)return null;return this.latestJpegBuffer.subarray(0,this.latestJpegLength)}sharedJpegCopy(){let $=this.unsubscribeMjpeg?this.latestJpeg():null;return $?Buffer.from($):null}async seedJpeg(){let $=this.sharedJpegCopy();if($)return $;try{return await this.capture.snapshotJpeg()}catch{return null}}writeMjpegFrame($,Q){let Z=$.write(LZ(Q.length)),J=$.write(Q),Y=$.write(jZ);return Z&&J&&Y}handleDuoMjpeg($,Q){(async()=>{if(await this.start(),Q.destroyed||Q.writableEnded)return;if(!await this.hid.isFoldable?.()){this.sendJson(Q,400,{error:"This device has no Duo model"});return}let Z=new URL($.url??"","http://x").searchParams.get("raw")==="1";if(!this.trackStreamResponse(Q,"3d"))return;this.duo.setPanel(this.activePanel()),Q.once("close",()=>this.duo.detach(Q)),this.duo.attach(Q,{onFirstFrame:()=>{Q.writeHead(200,{"Content-Type":Z?"application/octet-stream":"multipart/x-mixed-replace; boundary=frame","Cache-Control":"no-store"})}})})().catch((Z)=>this.handleStreamError(Q,Z,"3d"))}handleMjpeg($,Q){this.serveMjpeg($,Q).catch((Z)=>this.handleStreamError(Q,Z,"mjpeg"))}async serveMjpeg($,Q){if(await this.start(),Q.writableEnded||Q.destroyed)return;let Z=new URL($.url??"","http://x").searchParams.get("raw")==="1";if(Q.writeHead(200,{"Content-Type":Z?"application/octet-stream":"multipart/x-mixed-replace; boundary=frame","Cache-Control":"no-cache, no-store",Connection:"keep-alive"}),!this.trackStreamResponse(Q,"mjpeg"))return;let J=!1,Y=0,X=null,K=0,z=(N)=>{if(!X||X.length<N.length)X=Buffer.allocUnsafe(Math.max(N.length,(X?.length??0)*2));X.set(N,0),K=N.length},G=async(N)=>{if(J||Q.writableEnded||Q.destroyed)return;J=!0;try{if(await r7(Q,()=>this.writeMjpegFrame(Q,N)))Y=Date.now(),this.noteStreamFrame("mjpeg")}catch(j){if(!Q.destroyed)Q.destroy(j instanceof Error?j:Error(String(j)))}finally{J=!1}},V=this.sharedJpegCopy();if(V)z(V),await G(V);let W=await this.capture.subscribeMjpeg(async(N)=>{z(N.data),await G(N.data)}),H=setInterval(()=>{if(Q.writableEnded||Q.destroyed)return;if(Date.now()-Y<n7)return;if(X)G(Buffer.from(X.subarray(0,K)))},n7);this.bindSubscription(Q,async()=>{clearInterval(H),await W()})}handleAvcc($,Q){this.serveAvcc(Q).catch((Z)=>this.handleStreamError(Q,Z,"avcc"))}async serveAvcc($){if(await this.start(),$.writableEnded||$.destroyed)return;if($.writeHead(200,{"Content-Type":"application/octet-stream","Cache-Control":"no-cache, no-store",Connection:"keep-alive"}),!this.trackStreamResponse($,"avcc"))return;let Q=!1;this.seedJpeg().then((J)=>{if(J&&!Q&&!$.writableEnded&&!$.destroyed)$.write(OZ(J))});let Z=await this.capture.subscribeAvcc(async(J)=>{Q=!0,await r7($,()=>$.write(J.data)),this.noteStreamFrame("avcc")});this.bindSubscription($,Z)}handleConfig($,Q){this.sendJson(Q,200,this.screenConfig())}handleHealth($,Q){this.sendJson(Q,200,{status:"ok"})}handleAx($,Q){return this.serveAxJson(Q,()=>m6(this.udid),"ax_unavailable")}handleForeground($,Q){return this.serveAxJson(Q,()=>l6(this.udid),"foreground_unavailable")}async serveAxJson($,Q,Z){try{let J=await Q();if($.writableEnded)return;this.sendJsonString($,200,J)}catch(J){if($.writableEnded)return;this.sendJson($,503,{error:Z,message:J instanceof Error?J.message:String(J)})}}recordingStatus(){let $=this.recording;if(!$)return{recording:!1};let Q=$.finalizing&&$.phase==="recording";return{recording:!0,startedAt:$.startedAt,...Q?{limitReached:!0}:{},...Q&&$.heldAt!=null?{holdExpiresAt:new Date($.heldAt+this.recordingHoldMs()).toISOString()}:{}}}recordingHoldMs(){return this.dependencies?.recordingHoldMs??y7}async discardExpiredHold(){let $=this.recording;if(!$||!$.holdExpired||$.phase!=="recording")return;this.recording=void 0,console.error(`[record] ${this.udid}: discarding the uncollected recording held at the duration limit`),await $.finalizing?.catch(()=>{}),await v0($.path,{force:!0}).catch(()=>{})}async handleRecording($,Q,Z){let J=($.method??"GET").toUpperCase();if(Z==="status"){this.sendJson(Q,200,this.recordingStatus());return}if(J!=="POST"){Q.setHeader("Allow","POST"),this.sendJson(Q,405,{error:"method_not_allowed"});return}try{if(Z==="start")await this.startRecording($,Q);else await this.stopRecording($,Q)}catch(Y){if(Q.headersSent||Q.writableEnded){Q.destroy();return}this.sendJson(Q,500,{error:"recording_failed",message:L0(Y instanceof Error?Y.message:String(Y))})}}async startRecording($,Q){if(!this.capture.startRecording||!this.capture.stopRecording||this.capture.supportsRecording===!1){this.sendJson(Q,501,{error:"recording_unsupported",message:"This native build cannot record"});return}let Z=f7(new URL($.url??"","http://x").searchParams);if("error"in Z){this.sendJson(Q,400,{error:"invalid_recording_options",message:Z.error});return}if(await this.discardExpiredHold(),this.recording){this.sendJson(Q,409,{error:"already_recording",...this.recordingStatus()});return}let J=c7(FZ(),"serve-sim-recordings"),Y=c7(J,`${this.udid}-${d7()}.mp4`),X=new Date().toISOString(),K=d7(),z={path:Y,startedAt:X,token:K,phase:"starting"};this.recording=z;let G=()=>{this.runCleanup("recording cleanup",()=>v0(Y,{force:!0})),this.sendJson(Q,409,{error:"session_closed",message:"The device session closed while the recording was starting"})};try{await this.start(),await BZ(J,{recursive:!0}),await this.capture.startRecording(Y,{...Z,rotation:b7(this.screenConfig())})}catch(W){if(this.recording!==z)return G();throw this.recording=void 0,await v0(Y,{force:!0}).catch(()=>{}),W}if(this.recording!==z)return G();z.phase="recording";let V=this.dependencies?.maxRecordingMs??k7;z.limitTimer=setTimeout(()=>{if(this.recording!==z||z.phase!=="recording")return;console.error(`[record] ${this.udid}: reached the ${Math.round(V/60000)} min limit; finalizing`),this.finalizeRecording(z).catch((W)=>{console.error(`[record] ${this.udid}: finalize at the duration limit failed:`,W instanceof Error?W.message:W)}).finally(()=>this.holdForOwner(z))},V),z.limitTimer.unref?.(),this.sendJson(Q,200,{recording:!0,startedAt:X,token:K})}holdForOwner($){if(this.recording!==$||$.phase!=="recording")return;$.heldAt=Date.now(),$.holdTimer=setTimeout(()=>{if($.holdTimer=void 0,this.recording!==$||$.phase!=="recording")return;$.holdExpired=!0,console.error(`[record] ${this.udid}: the recording held at the duration limit is now available to any client`)},this.recordingHoldMs()),$.holdTimer.unref?.()}finalizeRecording($){if($.limitTimer)clearTimeout($.limitTimer);return $.limitTimer=void 0,$.finalizing??=this.capture.stopRecording(),$.finalizing}async stopRecording($,Q){let Z=this.recording;if(!Z||Z.phase!=="recording"||!this.capture.stopRecording){this.sendJson(Q,409,{error:"not_recording",...this.recordingStatus()});return}if(Z.finalizing&&!Z.holdExpired&&new URL($.url??"","http://x").searchParams.get("token")!==Z.token){this.sendJson(Q,409,{error:"not_owner",message:"This recording hit the time limit and is held for the client that started it"+(Z.heldAt!=null?` until ${new Date(Z.heldAt+this.recordingHoldMs()).toLocaleTimeString()}`:""),...this.recordingStatus()});return}if(Z.holdTimer)clearTimeout(Z.holdTimer);Z.holdTimer=void 0,Z.phase="stopping";let J=$.aborted||Q.destroyed,Y=()=>{J=!0};$.once("aborted",Y),Q.once("close",Y);let X=()=>{$.off("aborted",Y),Q.off("close",Y)},K,z;try{K=await this.finalizeRecording(Z),{size:z}=await NZ(Z.path)}catch(G){throw await v0(Z.path,{force:!0}).catch(()=>{}),G}finally{if(X(),this.recording===Z)this.recording=void 0}if(J||$.aborted||Q.destroyed||$.socket?.destroyed){await v0(Z.path,{force:!0}).catch(()=>{});return}Q.writeHead(200,{"Content-Type":"video/mp4","Content-Length":String(z),"Content-Disposition":`attachment; filename="${v7()}"`,"Cache-Control":"no-store",[S7]:AZ(K)}),await new Promise((G)=>{let V=HZ(Z.path),W=()=>{V.destroy(),v0(Z.path,{force:!0}).finally(G)};V.once("error",()=>{Q.destroy(),W()}),Q.once("close",W),V.pipe(Q)})}attachHidSocket($){this.hidSockets.add($);let Q=this.configFrame(),Z=this.duo.projectionFrame();if(Z)$.send(Z);if(Q)$.send(Q);$.on("message",(J)=>{let Y=Buffer.isBuffer(J)?J:Buffer.from(J);if(Y[0]===14)this.poseQueue=this.poseQueue.then(async()=>{if(this.isStopped())return;let X=!1;try{X=await this.handleHidMessage(Y)===!0}catch(K){console.error("[hid] Pose failed:",K)}if(this.hidSockets.has($))$.send(Buffer.concat([Buffer.from([14]),Buffer.from(JSON.stringify({ok:X}))]))}).catch((X)=>{console.error("[hid] Pose reply failed:",X)});else this.handleHidMessage(Y).catch((X)=>{console.error("[hid] Input failed:",X)})}),$.on("close",()=>this.hidSockets.delete($)),$.on("error",()=>this.hidSockets.delete($))}async handleHidMessage($){if($.length<1)return;let Q=$[0],Z=$.length>1?$.subarray(1):null,J=()=>{if(!Z)return null;try{return JSON.parse(Z.toString("utf8"))}catch{return null}},Y=this.width,X=this.height;switch(Q){case 3:{let K=J();if(K)this.recordTouchEvent(K),this.hid.touch(K.type,K.x,K.y,Y,X,K.edge??0);break}case 4:{let K=J();if(!K)break;if(this.recordHidEvent(Q,K),K.page!=null&&K.usage!=null)this.hid.buttonHid(K.page,K.usage,K.phase??"press");else this.hid.button(K.button);break}case 5:{let K=J();if(K)this.recordHidEvent(Q,K),this.hid.multiTouch(K.type,K.x1,K.y1,K.x2,K.y2,Y,X);break}case 6:{let K=J();if(K)this.recordHidEvent(Q,K),this.hid.key(K.type,K.usage);break}case 7:{let K=J();if(!K)break;let z=RZ[K.orientation];if(z!=null&&await this.hid.orientation(z)){if(this.recordHidEvent(Q,K),K.orientation!==this.orientation||K.orientation!==this.duo.viewOrientationName){if(this.duo.hinge==null)this.orientation=K.orientation;this.duo.animateRotation(K.orientation),this.duoMonitor?.refreshOrientation(),this.broadcastConfig(),this.duo.requestFrame()}}break}case 8:{let K=J();if(K)this.recordHidEvent(Q,K),this.hid.caDebug(K.option,K.enabled);break}case 9:this.recordHidEvent(Q,{}),this.hid.memoryWarning();break;case 10:{let K=J();if(K)this.recordHidEvent(Q,K),this.hid.digitalCrown(K.delta);break}case 11:{let K=J();if(K)this.recordHidEvent(Q,K),this.hid.scroll(K.dx*Y,K.dy*X,Y,X,K.x,K.y);break}case 12:this.recordHidEvent(Q,{}),this.hid.softwareKeyboard();break;case 13:{let K=J();if(!K)break;let z=Number(K.width),G=Number(K.height);if(!Number.isFinite(z)||!Number.isFinite(G))break;this.recordHidEvent(Q,K),await this.capture.setPreferredScreenSize(Math.round(z),Math.round(G));break}case 14:{let K=J();if(!K)break;if(this.recordHidEvent(Q,K),typeof K.pose==="string"&&K.pose.trim()){let z=d6(K.pose);if(!z)break;let G=this.duo.displayAngle();if(this.duo.beginPose(G,z.hingeDegrees),!await this.hid.pose(K.pose,G)){this.duo.cancelPose();break}return this.duo.finishPose(z.hingeDegrees),await this.followDuoState({hingeDegrees:z.hingeDegrees,orientations:{}}),this.duoMonitor?.refreshOrientation(),this.broadcastConfig(),this.duo.requestFrame(),!0}else if(typeof K.hinge==="number"&&Number.isFinite(K.hinge)&&K.hinge>=0&&K.hinge<=180){if(!await this.hid.hinge(K.hinge))break;return this.duo.cancelPose(),await this.followDuoState({hingeDegrees:K.hinge,orientations:{}}),this.duoMonitor?.refreshOrientation(),this.broadcastConfig(),this.duo.requestFrame(),!0}break}}}recordTouchEvent($){if($.type==="begin"){this.touchGestureLog=o7($);return}if($.type==="move"){let Q=this.touchGestureLog;if(!Q)Q=o7($),this.touchGestureLog=Q;if(Q.lastX=$.x,Q.lastY=$.y,Q.moveCount++,$.edge!=null)Q.edge=$.edge;if(i7(Q))if(Q.eventId==null){let Z=_0({device:this.udid,source:"hid",kind:"drag",action:"drag",summary:o6(Q),details:this.touchGestureDetails(Q,"drag","move")});Q.eventId=Z.id}else n6(Q.eventId,{kind:"drag",action:"drag",summary:o6(Q),details:this.touchGestureDetails(Q,"drag","move")},{notify:!1});return}if($.type==="end"){let Q=this.touchGestureLog;if(Q){if(Q.lastX=$.x,Q.lastY=$.y,$.edge!=null)Q.edge=$.edge;if(Q.moveCount>0&&i7(Q))if(Q.eventId==null)_0({device:this.udid,source:"hid",kind:"drag",action:"drag",summary:o6(Q),details:this.touchGestureDetails(Q,"drag","end")});else n6(Q.eventId,{kind:"drag",action:"drag",summary:o6(Q),details:this.touchGestureDetails(Q,"drag","end")});else _0({device:this.udid,source:"hid",kind:"tap",action:"tap",summary:`Tap ${e0($.x,$.y)}`,details:this.touchGestureDetails(Q,"tap")});this.touchGestureLog=void 0;return}}this.recordHidEvent(3,$)}eventLogScreen(){return this.width>0&&this.height>0?{width:this.width,height:this.height}:void 0}touchGestureDetails($,Q,Z){return{type:Q,...Z?{phase:Z}:{},start:{x:$.startX,y:$.startY},current:{x:$.lastX,y:$.lastY},moveCount:$.moveCount,...$.edge!=null?{edge:$.edge}:{},...this.eventLogScreen()?{screen:this.eventLogScreen()}:{}}}recordHidEvent($,Q){let Z=m7(this.udid,$,Q,this.eventLogScreen());if(Z)_0(Z)}screenConfig(){let $=this.duo.hinge;return{width:this.width,height:this.height,orientation:this.orientation,...$==null?{}:{hingeDegrees:$,duoViewOrientation:this.duo.viewOrientationName}}}configFrame(){if(this.width===0&&this.height===0)return null;return Buffer.concat([Buffer.from([UZ]),Buffer.from(JSON.stringify(this.screenConfig()))])}broadcastConfig(){let $=this.configFrame();if(!$)return;for(let Q of this.hidSockets)Q.send($)}bindSubscription($,Q){let Z=!1,J=()=>{if(Z)return;Z=!0,this.streamResponses.delete($),this.runCleanup("stream unsubscribe",Q)};if($.writableEnded||$.destroyed){J();return}$.once("close",J),$.once("error",J)}trackStreamResponse($,Q){if(this.isStopped()||$.writableEnded||$.destroyed){if(!$.writableEnded&&!$.destroyed)$.destroy();return!1}this.streamResponses.add($),this.streamHealth[Q].open++;let Z=()=>{if($.off("close",Z),$.off("error",Z),this.streamResponses.delete($))this.streamHealth[Q].open--};return $.once("close",Z),$.once("error",Z),!0}runCleanup($,Q){try{Promise.resolve(Q()).catch((Z)=>this.logCleanupError($,Z))}catch(Z){this.logCleanupError($,Z)}}logCleanupError($,Q){console.error(`[capture] ${$} failed for ${this.udid}:`,Q instanceof Error?Q.message:Q)}noteStreamFrame($){let Q=this.streamHealth[$];Q.frames++,Q.last_frame_at=new Date().toISOString()}recordRendererEvent($){if($.phase==="ready"){this.rendererFailureEvent=void 0,_0({device:this.udid,source:"capture",kind:"duo.renderer",action:"ready",status:"ok",summary:`Duo renderer ready (${$.firstFrameMs} ms to first frame)`,details:{phase:"ready","duo.first_frame_ms":$.firstFrameMs}});return}let{error:Q}=$;_7(Q);let Z=`${Q.stage}\x00${Q.reason}`,J=L0(Q.reason),Y={phase:"failed",stage:Q.stage,reason:J,...L0(Q.details)},X=this.rendererFailureEvent;if(X?.key===Z&&n6(X.id,{details:{...Y,count:X.count+1}})){X.count++;return}let K=_0({device:this.udid,source:"capture",kind:"duo.renderer",action:"failed",status:"error",summary:`Duo renderer ${Q.stage} failed: ${J}`,details:{...Y,count:1}});this.rendererFailureEvent={id:K.id,key:Z,count:1}}async diagnostics(){let $=null;if(this.capture.diagnostics)try{$=JSON.parse(await this.capture.diagnostics())}catch(Q){$={error:Q instanceof Error?Q.message:String(Q)}}else $={error:"native capture diagnostics unavailable in this build"};return{udid:this.udid,phase:this.phase,screen:this.screenConfig(),streams:structuredClone(this.streamHealth),native:$,duo:{...this.duo.diagnostics(),primary_panel:this.primaryDuoPanel??null,active_panel:this.activePanel(),followed_cover:this.followedCover??null},hid:{device_hub_input_shadowed:this.deviceHubShadowed??null,sockets:this.hidSockets.size}}}handleStreamError($,Q,Z){let J=L0(Q instanceof Error?Q.message:String(Q));if(Z)this.streamHealth[Z].last_error={at:new Date().toISOString(),message:J,...Q instanceof z0?{stage:Q.stage}:{}};if($.writableEnded||$.destroyed)return;if(Q instanceof z0&&!$.headersSent){this.sendJson($,503,_Z(Q));return}if($.headersSent){$.destroy(Q instanceof Error?Q:Error(String(Q)));return}this.sendJson($,503,{error:"capture_unavailable",message:J})}sendJson($,Q,Z){this.sendJsonString($,Q,JSON.stringify(Z))}sendJsonString($,Q,Z){let J=Buffer.from(Z,"utf8");$.writeHead(Q,{"Content-Type":"application/json","Cache-Control":"no-cache, no-store","Content-Length":String(J.length)}),$.end(J)}}var q0=new Map;function s7($){if($){let Q=q0.get($);return Q?[Q]:[]}return[...q0.values()]}function l9($){let Q=q0.get($);if(!Q){let Z=new t7($);Q=Z,q0.set($,Z),Z.start().catch(()=>{if(q0.get($)!==Z)return;q0.delete($),Z.close()})}return Q}function p9($){let Q=q0.get($);if(Q)Q.close(),q0.delete($)}function AZ($){let Q;try{Q=JSON.parse($)}catch{return"{}"}return delete Q.path,JSON.stringify(Q).replace(/[^\x20-\x7e]/g,"")}import{execFile as TZ}from"node:child_process";import{closeSync as AK,existsSync as PZ,openSync as TK,readdirSync as DZ,readFileSync as qZ,readSync as PK,statSync as DK}from"node:fs";import{dirname as EZ,join as $6}from"node:path";import{fileURLToPath as IZ}from"node:url";var xZ=1;var e7=($,Q)=>new Promise((Z)=>{TZ($,Q,{timeout:5000,maxBuffer:4194304},(J,Y)=>{Z(J?null:String(Y).trim())})});function wZ(){return"0.1.49"}function SZ($){let Q=/^(.*?\.app)\/Contents\/Developer\/?$/.exec($);return Q?Q[1]:null}async function kZ($){let Q=new Set,Z=await $("/usr/bin/mdfind",["kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode'"]);for(let J of Z?.split(`
|
|
60
|
+
`,"ascii")}function OZ($){let Q=Buffer.allocUnsafe(5+$.length);return Q.writeUInt32BE($.length+1,0),Q[4]=MZ,Q.set($,5),Q}var RZ={portrait:M6.portrait,portrait_upside_down:M6.portraitUpsideDown,landscape_left:M6.landscapeLeft,landscape_right:M6.landscapeRight};function a7($){if($.writableEnded||$.destroyed||!$.writableNeedDrain)return Promise.resolve();return new Promise((Q)=>{let Z=()=>{J(),Q()},J=()=>{$.off("drain",Z),$.off("close",Z),$.off("error",Z)};$.once("drain",Z),$.once("close",Z),$.once("error",Z)})}async function r7($,Q){if(await a7($),$.writableEnded||$.destroyed)return!1;if(!Q()||$.writableNeedDrain)await a7($);return!$.writableEnded&&!$.destroyed}function m9(){return{open:0,frames:0,last_frame_at:null,last_error:null}}function _Z($){return{error:"duo_renderer_unavailable",reason:L0($.reason),stage:$.stage,details:L0($.details)}}class t7{udid;dependencies;capture;hid;unsubscribeMjpeg;unsubscribeSize;phase="unstarted";startPromise;width=0;height=0;orientation="portrait";latestJpegBuffer=null;latestJpegLength=0;hidSockets=new Set;duo;duoMonitor;followedCover;primaryDuoPanel;poseQueue=Promise.resolve();duoStateQueue=Promise.resolve();streamResponses=new Set;touchGestureLog;streamHealth={mjpeg:m9(),avcc:m9(),"3d":m9()};deviceHubShadowed;recording;rendererFailureEvent;constructor($,Q){this.udid=$;this.dependencies=Q;this.hid=Q?.hid??new T9($),this.capture=Q?.capture??new P9($),this.duo=new f9({createRenderer:(Z)=>Q?.createDuoRenderer?Q.createDuoRenderer(Z):new k9(Z),onProjection:(Z)=>{for(let J of this.hidSockets)J.send(Z)},onStreamError:(Z,J)=>this.handleStreamError(Z,J,"3d"),onRendererEvent:(Z)=>this.recordRendererEvent(Z),onFrameDelivered:()=>this.noteStreamFrame("3d")})}start(){if(this.phase==="stopped")return Promise.reject(Error(`Device session ${this.udid} is closed`));if(this.startPromise)return this.startPromise;this.phase="starting";let $=(async()=>{if(await this.capture.start(),this.isStopped())throw Error(`Device session ${this.udid} closed while starting`);let Q=await this.capture.subscribeSize((Z)=>this.onScreenSize(Z.width,Z.height));if(this.isStopped())throw await Q(),Error(`Device session ${this.udid} closed while starting`);if(this.unsubscribeSize=Q,this.phase="running",await this.hid.isFoldable?.()&&!this.isStopped()){let Z=await this.capture.subscribeMjpeg((Y)=>this.onSharedMjpegFrame(Y));if(this.isStopped()){await Z();return}if(this.unsubscribeMjpeg=Z,!this.dependencies?.createDuoMonitor)U7(this.udid).then((Y)=>{this.deviceHubShadowed=Y});let J=this.dependencies?.createDuoMonitor??((Y,X)=>new v9(Y,X));this.duoMonitor=J(this.udid,(Y)=>{this.followDuoState(Y).catch((X)=>{console.error("[duo] State update failed:",X)})})}})().catch((Q)=>{if(this.phase==="starting")this.phase="failed";throw Q});return $.catch(()=>{}),this.startPromise=$,$}isStopped(){return this.phase==="stopped"}close(){if(this.phase==="stopped")return;this.phase="stopped",this.duo.close(),this.duoMonitor?.close();for(let $ of this.hidSockets)this.runCleanup("HID socket close",()=>$.close());for(let $ of this.streamResponses)this.runCleanup("stream response close",()=>{$.destroy()});if(this.streamResponses.clear(),this.unsubscribeMjpeg)this.runCleanup("shared MJPEG unsubscribe",this.unsubscribeMjpeg);if(this.unsubscribeSize)this.runCleanup("size unsubscribe",this.unsubscribeSize);if(this.hidSockets.clear(),this.recording){let $=this.recording;if(this.recording=void 0,$.limitTimer)clearTimeout($.limitTimer);if($.holdTimer)clearTimeout($.holdTimer);if($.phase!=="stopping"){let Q=$.finalizing?.catch(()=>{})??Promise.resolve();this.runCleanup("recording cleanup",()=>Q.then(()=>v0($.path,{force:!0})))}}this.runCleanup("HID close",()=>this.hid.close?.()),this.runCleanup("capture stop",()=>this.capture.stop())}followDuoState($){let Q=this.duoStateQueue.then(()=>this.applyDuoState($));return this.duoStateQueue=Q.catch(()=>{}),Q}activePanel(){return I7({primaryPanel:this.primaryDuoPanel,hingeDegrees:this.duo.hinge})}async applyDuoState($){if(this.isStopped())return;if($.hingeDegrees!=null)this.duo.noteHinge($.hingeDegrees);if($.primaryPanel)this.primaryDuoPanel=$.primaryPanel;let Q=this.activePanel(),Z=x7(Q);if(($.hingeDegrees!=null||$.primaryPanel)&&Q==="cover"!==this.followedCover)await this.capture.setPreferredScreenSize(Z.width,Z.height),this.followedCover=Q==="cover";let J=$.orientations[Q];if(J)this.orientation=J;this.duo.setPanel(Q),this.broadcastConfig(),this.duo.requestFrame()}onScreenSize($,Q){if($===this.width&&Q===this.height)return;this.width=$,this.height=Q,this.broadcastConfig()}async onSharedMjpegFrame($){let{width:Q,height:Z,data:J}=$;if(this.onScreenSize(Q,Z),!this.latestJpegBuffer||this.latestJpegBuffer.length<J.length){let X=this.latestJpegBuffer?.length??0;this.latestJpegBuffer=Buffer.allocUnsafe(Math.max(J.length,X*2))}this.latestJpegBuffer.set(J,0),this.latestJpegLength=J.length;let Y=this.latestJpeg();if(Y)this.duo.onCapturedFrame(Y)}latestJpeg(){if(!this.latestJpegBuffer)return null;return this.latestJpegBuffer.subarray(0,this.latestJpegLength)}sharedJpegCopy(){let $=this.unsubscribeMjpeg?this.latestJpeg():null;return $?Buffer.from($):null}async seedJpeg(){let $=this.sharedJpegCopy();if($)return $;try{return await this.capture.snapshotJpeg()}catch{return null}}writeMjpegFrame($,Q){let Z=$.write(LZ(Q.length)),J=$.write(Q),Y=$.write(jZ);return Z&&J&&Y}handleDuoMjpeg($,Q){(async()=>{if(await this.start(),Q.destroyed||Q.writableEnded)return;if(!await this.hid.isFoldable?.()){this.sendJson(Q,400,{error:"This device has no Duo model"});return}let Z=new URL($.url??"","http://x").searchParams.get("raw")==="1";if(!this.trackStreamResponse(Q,"3d"))return;this.duo.setPanel(this.activePanel()),Q.once("close",()=>this.duo.detach(Q)),this.duo.attach(Q,{onFirstFrame:()=>{Q.writeHead(200,{"Content-Type":Z?"application/octet-stream":"multipart/x-mixed-replace; boundary=frame","Cache-Control":"no-store"})}})})().catch((Z)=>this.handleStreamError(Q,Z,"3d"))}handleMjpeg($,Q){this.serveMjpeg($,Q).catch((Z)=>this.handleStreamError(Q,Z,"mjpeg"))}async serveMjpeg($,Q){if(await this.start(),Q.writableEnded||Q.destroyed)return;let Z=new URL($.url??"","http://x").searchParams.get("raw")==="1";if(Q.writeHead(200,{"Content-Type":Z?"application/octet-stream":"multipart/x-mixed-replace; boundary=frame","Cache-Control":"no-cache, no-store",Connection:"keep-alive"}),!this.trackStreamResponse(Q,"mjpeg"))return;let J=!1,Y=0,X=null,K=0,z=(N)=>{if(!X||X.length<N.length)X=Buffer.allocUnsafe(Math.max(N.length,(X?.length??0)*2));X.set(N,0),K=N.length},G=async(N)=>{if(J||Q.writableEnded||Q.destroyed)return;J=!0;try{if(await r7(Q,()=>this.writeMjpegFrame(Q,N)))Y=Date.now(),this.noteStreamFrame("mjpeg")}catch(j){if(!Q.destroyed)Q.destroy(j instanceof Error?j:Error(String(j)))}finally{J=!1}},V=this.sharedJpegCopy();if(V)z(V),await G(V);let W=await this.capture.subscribeMjpeg(async(N)=>{z(N.data),await G(N.data)}),H=setInterval(()=>{if(Q.writableEnded||Q.destroyed)return;if(Date.now()-Y<n7)return;if(X)G(Buffer.from(X.subarray(0,K)))},n7);this.bindSubscription(Q,async()=>{clearInterval(H),await W()})}handleAvcc($,Q){this.serveAvcc(Q).catch((Z)=>this.handleStreamError(Q,Z,"avcc"))}async serveAvcc($){if(await this.start(),$.writableEnded||$.destroyed)return;if($.writeHead(200,{"Content-Type":"application/octet-stream","Cache-Control":"no-cache, no-store",Connection:"keep-alive"}),!this.trackStreamResponse($,"avcc"))return;let Q=!1;this.seedJpeg().then((J)=>{if(J&&!Q&&!$.writableEnded&&!$.destroyed)$.write(OZ(J))});let Z=await this.capture.subscribeAvcc(async(J)=>{Q=!0,await r7($,()=>$.write(J.data)),this.noteStreamFrame("avcc")});this.bindSubscription($,Z)}handleConfig($,Q){this.sendJson(Q,200,this.screenConfig())}handleHealth($,Q){this.sendJson(Q,200,{status:"ok"})}handleAx($,Q){return this.serveAxJson(Q,()=>m6(this.udid),"ax_unavailable")}handleForeground($,Q){return this.serveAxJson(Q,()=>l6(this.udid),"foreground_unavailable")}async serveAxJson($,Q,Z){try{let J=await Q();if($.writableEnded)return;this.sendJsonString($,200,J)}catch(J){if($.writableEnded)return;this.sendJson($,503,{error:Z,message:J instanceof Error?J.message:String(J)})}}recordingStatus(){let $=this.recording;if(!$)return{recording:!1};let Q=$.finalizing&&$.phase==="recording";return{recording:!0,startedAt:$.startedAt,...Q?{limitReached:!0}:{},...Q&&$.heldAt!=null?{holdExpiresAt:new Date($.heldAt+this.recordingHoldMs()).toISOString()}:{}}}recordingHoldMs(){return this.dependencies?.recordingHoldMs??y7}async discardExpiredHold(){let $=this.recording;if(!$||!$.holdExpired||$.phase!=="recording")return;this.recording=void 0,console.error(`[record] ${this.udid}: discarding the uncollected recording held at the duration limit`),await $.finalizing?.catch(()=>{}),await v0($.path,{force:!0}).catch(()=>{})}async handleRecording($,Q,Z){let J=($.method??"GET").toUpperCase();if(Z==="status"){this.sendJson(Q,200,this.recordingStatus());return}if(J!=="POST"){Q.setHeader("Allow","POST"),this.sendJson(Q,405,{error:"method_not_allowed"});return}try{if(Z==="start")await this.startRecording($,Q);else await this.stopRecording($,Q)}catch(Y){if(Q.headersSent||Q.writableEnded){Q.destroy();return}this.sendJson(Q,500,{error:"recording_failed",message:L0(Y instanceof Error?Y.message:String(Y))})}}async startRecording($,Q){if(!this.capture.startRecording||!this.capture.stopRecording||this.capture.supportsRecording===!1){this.sendJson(Q,501,{error:"recording_unsupported",message:"This native build cannot record"});return}let Z=f7(new URL($.url??"","http://x").searchParams);if("error"in Z){this.sendJson(Q,400,{error:"invalid_recording_options",message:Z.error});return}if(await this.discardExpiredHold(),this.recording){this.sendJson(Q,409,{error:"already_recording",...this.recordingStatus()});return}let J=c7(FZ(),"serve-sim-recordings"),Y=c7(J,`${this.udid}-${d7()}.mp4`),X=new Date().toISOString(),K=d7(),z={path:Y,startedAt:X,token:K,phase:"starting"};this.recording=z;let G=()=>{this.runCleanup("recording cleanup",()=>v0(Y,{force:!0})),this.sendJson(Q,409,{error:"session_closed",message:"The device session closed while the recording was starting"})};try{await this.start(),await BZ(J,{recursive:!0}),await this.capture.startRecording(Y,{...Z,rotation:b7(this.screenConfig())})}catch(W){if(this.recording!==z)return G();throw this.recording=void 0,await v0(Y,{force:!0}).catch(()=>{}),W}if(this.recording!==z)return G();z.phase="recording";let V=this.dependencies?.maxRecordingMs??k7;z.limitTimer=setTimeout(()=>{if(this.recording!==z||z.phase!=="recording")return;console.error(`[record] ${this.udid}: reached the ${Math.round(V/60000)} min limit; finalizing`),this.finalizeRecording(z).catch((W)=>{console.error(`[record] ${this.udid}: finalize at the duration limit failed:`,W instanceof Error?W.message:W)}).finally(()=>this.holdForOwner(z))},V),z.limitTimer.unref?.(),this.sendJson(Q,200,{recording:!0,startedAt:X,token:K})}holdForOwner($){if(this.recording!==$||$.phase!=="recording")return;$.heldAt=Date.now(),$.holdTimer=setTimeout(()=>{if($.holdTimer=void 0,this.recording!==$||$.phase!=="recording")return;$.holdExpired=!0,console.error(`[record] ${this.udid}: the recording held at the duration limit is now available to any client`)},this.recordingHoldMs()),$.holdTimer.unref?.()}finalizeRecording($){if($.limitTimer)clearTimeout($.limitTimer);return $.limitTimer=void 0,$.finalizing??=this.capture.stopRecording(),$.finalizing}async stopRecording($,Q){let Z=this.recording;if(!Z||Z.phase!=="recording"||!this.capture.stopRecording){this.sendJson(Q,409,{error:"not_recording",...this.recordingStatus()});return}if(Z.finalizing&&!Z.holdExpired&&new URL($.url??"","http://x").searchParams.get("token")!==Z.token){this.sendJson(Q,409,{error:"not_owner",message:"This recording hit the time limit and is held for the client that started it"+(Z.heldAt!=null?` until ${new Date(Z.heldAt+this.recordingHoldMs()).toLocaleTimeString()}`:""),...this.recordingStatus()});return}if(Z.holdTimer)clearTimeout(Z.holdTimer);Z.holdTimer=void 0,Z.phase="stopping";let J=$.aborted||Q.destroyed,Y=()=>{J=!0};$.once("aborted",Y),Q.once("close",Y);let X=()=>{$.off("aborted",Y),Q.off("close",Y)},K,z;try{K=await this.finalizeRecording(Z),{size:z}=await NZ(Z.path)}catch(G){throw await v0(Z.path,{force:!0}).catch(()=>{}),G}finally{if(X(),this.recording===Z)this.recording=void 0}if(J||$.aborted||Q.destroyed||$.socket?.destroyed){await v0(Z.path,{force:!0}).catch(()=>{});return}Q.writeHead(200,{"Content-Type":"video/mp4","Content-Length":String(z),"Content-Disposition":`attachment; filename="${v7()}"`,"Cache-Control":"no-store",[S7]:AZ(K)}),await new Promise((G)=>{let V=HZ(Z.path),W=()=>{V.destroy(),v0(Z.path,{force:!0}).finally(G)};V.once("error",()=>{Q.destroy(),W()}),Q.once("close",W),V.pipe(Q)})}attachHidSocket($){this.hidSockets.add($);let Q=this.configFrame(),Z=this.duo.projectionFrame();if(Z)$.send(Z);if(Q)$.send(Q);$.on("message",(J)=>{let Y=Buffer.isBuffer(J)?J:Buffer.from(J);if(Y[0]===14)this.poseQueue=this.poseQueue.then(async()=>{if(this.isStopped())return;let X=!1;try{X=await this.handleHidMessage(Y)===!0}catch(K){console.error("[hid] Pose failed:",K)}if(this.hidSockets.has($))$.send(Buffer.concat([Buffer.from([14]),Buffer.from(JSON.stringify({ok:X}))]))}).catch((X)=>{console.error("[hid] Pose reply failed:",X)});else this.handleHidMessage(Y).catch((X)=>{console.error("[hid] Input failed:",X)})}),$.on("close",()=>this.hidSockets.delete($)),$.on("error",()=>this.hidSockets.delete($))}async handleHidMessage($){if($.length<1)return;let Q=$[0],Z=$.length>1?$.subarray(1):null,J=()=>{if(!Z)return null;try{return JSON.parse(Z.toString("utf8"))}catch{return null}},Y=this.width,X=this.height;switch(Q){case 3:{let K=J();if(K)this.recordTouchEvent(K),this.hid.touch(K.type,K.x,K.y,Y,X,K.edge??0);break}case 4:{let K=J();if(!K)break;if(this.recordHidEvent(Q,K),K.page!=null&&K.usage!=null)this.hid.buttonHid(K.page,K.usage,K.phase??"press");else this.hid.button(K.button);break}case 5:{let K=J();if(K)this.recordHidEvent(Q,K),this.hid.multiTouch(K.type,K.x1,K.y1,K.x2,K.y2,Y,X);break}case 6:{let K=J();if(K)this.recordHidEvent(Q,K),this.hid.key(K.type,K.usage);break}case 7:{let K=J();if(!K)break;let z=RZ[K.orientation];if(z!=null&&await this.hid.orientation(z)){if(this.recordHidEvent(Q,K),K.orientation!==this.orientation||K.orientation!==this.duo.viewOrientationName){if(this.duo.hinge==null)this.orientation=K.orientation;this.duo.animateRotation(K.orientation),this.duoMonitor?.refreshOrientation(),this.broadcastConfig(),this.duo.requestFrame()}}break}case 8:{let K=J();if(K)this.recordHidEvent(Q,K),this.hid.caDebug(K.option,K.enabled);break}case 9:this.recordHidEvent(Q,{}),this.hid.memoryWarning();break;case 10:{let K=J();if(K)this.recordHidEvent(Q,K),this.hid.digitalCrown(K.delta);break}case 11:{let K=J();if(K)this.recordHidEvent(Q,K),this.hid.scroll(K.dx*Y,K.dy*X,Y,X,K.x,K.y);break}case 12:this.recordHidEvent(Q,{}),this.hid.softwareKeyboard();break;case 13:{let K=J();if(!K)break;let z=Number(K.width),G=Number(K.height);if(!Number.isFinite(z)||!Number.isFinite(G))break;this.recordHidEvent(Q,K),await this.capture.setPreferredScreenSize(Math.round(z),Math.round(G));break}case 14:{let K=J();if(!K)break;if(this.recordHidEvent(Q,K),typeof K.pose==="string"&&K.pose.trim()){let z=d6(K.pose);if(!z)break;let G=this.duo.displayAngle();if(this.duo.beginPose(G,z.hingeDegrees),!await this.hid.pose(K.pose,G)){this.duo.cancelPose();break}return this.duo.finishPose(z.hingeDegrees),await this.followDuoState({hingeDegrees:z.hingeDegrees,orientations:{}}),this.duoMonitor?.refreshOrientation(),this.broadcastConfig(),this.duo.requestFrame(),!0}else if(typeof K.hinge==="number"&&Number.isFinite(K.hinge)&&K.hinge>=0&&K.hinge<=180){if(!await this.hid.hinge(K.hinge))break;return this.duo.cancelPose(),await this.followDuoState({hingeDegrees:K.hinge,orientations:{}}),this.duoMonitor?.refreshOrientation(),this.broadcastConfig(),this.duo.requestFrame(),!0}break}}}recordTouchEvent($){if($.type==="begin"){this.touchGestureLog=o7($);return}if($.type==="move"){let Q=this.touchGestureLog;if(!Q)Q=o7($),this.touchGestureLog=Q;if(Q.lastX=$.x,Q.lastY=$.y,Q.moveCount++,$.edge!=null)Q.edge=$.edge;if(i7(Q))if(Q.eventId==null){let Z=_0({device:this.udid,source:"hid",kind:"drag",action:"drag",summary:o6(Q),details:this.touchGestureDetails(Q,"drag","move")});Q.eventId=Z.id}else n6(Q.eventId,{kind:"drag",action:"drag",summary:o6(Q),details:this.touchGestureDetails(Q,"drag","move")},{notify:!1});return}if($.type==="end"){let Q=this.touchGestureLog;if(Q){if(Q.lastX=$.x,Q.lastY=$.y,$.edge!=null)Q.edge=$.edge;if(Q.moveCount>0&&i7(Q))if(Q.eventId==null)_0({device:this.udid,source:"hid",kind:"drag",action:"drag",summary:o6(Q),details:this.touchGestureDetails(Q,"drag","end")});else n6(Q.eventId,{kind:"drag",action:"drag",summary:o6(Q),details:this.touchGestureDetails(Q,"drag","end")});else _0({device:this.udid,source:"hid",kind:"tap",action:"tap",summary:`Tap ${e0($.x,$.y)}`,details:this.touchGestureDetails(Q,"tap")});this.touchGestureLog=void 0;return}}this.recordHidEvent(3,$)}eventLogScreen(){return this.width>0&&this.height>0?{width:this.width,height:this.height}:void 0}touchGestureDetails($,Q,Z){return{type:Q,...Z?{phase:Z}:{},start:{x:$.startX,y:$.startY},current:{x:$.lastX,y:$.lastY},moveCount:$.moveCount,...$.edge!=null?{edge:$.edge}:{},...this.eventLogScreen()?{screen:this.eventLogScreen()}:{}}}recordHidEvent($,Q){let Z=m7(this.udid,$,Q,this.eventLogScreen());if(Z)_0(Z)}screenConfig(){let $=this.duo.hinge;return{width:this.width,height:this.height,orientation:this.orientation,...$==null?{}:{hingeDegrees:$,duoViewOrientation:this.duo.viewOrientationName}}}configFrame(){if(this.width===0&&this.height===0)return null;return Buffer.concat([Buffer.from([UZ]),Buffer.from(JSON.stringify(this.screenConfig()))])}broadcastConfig(){let $=this.configFrame();if(!$)return;for(let Q of this.hidSockets)Q.send($)}bindSubscription($,Q){let Z=!1,J=()=>{if(Z)return;Z=!0,this.streamResponses.delete($),this.runCleanup("stream unsubscribe",Q)};if($.writableEnded||$.destroyed){J();return}$.once("close",J),$.once("error",J)}trackStreamResponse($,Q){if(this.isStopped()||$.writableEnded||$.destroyed){if(!$.writableEnded&&!$.destroyed)$.destroy();return!1}this.streamResponses.add($),this.streamHealth[Q].open++;let Z=()=>{if($.off("close",Z),$.off("error",Z),this.streamResponses.delete($))this.streamHealth[Q].open--};return $.once("close",Z),$.once("error",Z),!0}runCleanup($,Q){try{Promise.resolve(Q()).catch((Z)=>this.logCleanupError($,Z))}catch(Z){this.logCleanupError($,Z)}}logCleanupError($,Q){console.error(`[capture] ${$} failed for ${this.udid}:`,Q instanceof Error?Q.message:Q)}noteStreamFrame($){let Q=this.streamHealth[$];Q.frames++,Q.last_frame_at=new Date().toISOString()}recordRendererEvent($){if($.phase==="ready"){this.rendererFailureEvent=void 0,_0({device:this.udid,source:"capture",kind:"duo.renderer",action:"ready",status:"ok",summary:`Duo renderer ready (${$.firstFrameMs} ms to first frame)`,details:{phase:"ready","duo.first_frame_ms":$.firstFrameMs}});return}let{error:Q}=$;_7(Q);let Z=`${Q.stage}\x00${Q.reason}`,J=L0(Q.reason),Y={phase:"failed",stage:Q.stage,reason:J,...L0(Q.details)},X=this.rendererFailureEvent;if(X?.key===Z&&n6(X.id,{details:{...Y,count:X.count+1}})){X.count++;return}let K=_0({device:this.udid,source:"capture",kind:"duo.renderer",action:"failed",status:"error",summary:`Duo renderer ${Q.stage} failed: ${J}`,details:{...Y,count:1}});this.rendererFailureEvent={id:K.id,key:Z,count:1}}async diagnostics(){let $=null;if(this.capture.diagnostics)try{$=JSON.parse(await this.capture.diagnostics())}catch(Q){$={error:Q instanceof Error?Q.message:String(Q)}}else $={error:"native capture diagnostics unavailable in this build"};return{udid:this.udid,phase:this.phase,screen:this.screenConfig(),streams:structuredClone(this.streamHealth),native:$,duo:{...this.duo.diagnostics(),primary_panel:this.primaryDuoPanel??null,active_panel:this.activePanel(),followed_cover:this.followedCover??null},hid:{device_hub_input_shadowed:this.deviceHubShadowed??null,sockets:this.hidSockets.size}}}handleStreamError($,Q,Z){let J=L0(Q instanceof Error?Q.message:String(Q));if(Z)this.streamHealth[Z].last_error={at:new Date().toISOString(),message:J,...Q instanceof z0?{stage:Q.stage}:{}};if($.writableEnded||$.destroyed)return;if(Q instanceof z0&&!$.headersSent){this.sendJson($,503,_Z(Q));return}if($.headersSent){$.destroy(Q instanceof Error?Q:Error(String(Q)));return}this.sendJson($,503,{error:"capture_unavailable",message:J})}sendJson($,Q,Z){this.sendJsonString($,Q,JSON.stringify(Z))}sendJsonString($,Q,Z){let J=Buffer.from(Z,"utf8");$.writeHead(Q,{"Content-Type":"application/json","Cache-Control":"no-cache, no-store","Content-Length":String(J.length)}),$.end(J)}}var q0=new Map;function s7($){if($){let Q=q0.get($);return Q?[Q]:[]}return[...q0.values()]}function l9($){let Q=q0.get($);if(!Q){let Z=new t7($);Q=Z,q0.set($,Z),Z.start().catch(()=>{if(q0.get($)!==Z)return;q0.delete($),Z.close()})}return Q}function p9($){let Q=q0.get($);if(Q)Q.close(),q0.delete($)}function AZ($){let Q;try{Q=JSON.parse($)}catch{return"{}"}return delete Q.path,JSON.stringify(Q).replace(/[^\x20-\x7e]/g,"")}import{execFile as TZ}from"node:child_process";import{closeSync as AK,existsSync as PZ,openSync as TK,readdirSync as DZ,readFileSync as qZ,readSync as PK,statSync as DK}from"node:fs";import{dirname as EZ,join as $6}from"node:path";import{fileURLToPath as IZ}from"node:url";var xZ=1;var e7=($,Q)=>new Promise((Z)=>{TZ($,Q,{timeout:5000,maxBuffer:4194304},(J,Y)=>{Z(J?null:String(Y).trim())})});function wZ(){return"0.1.50-beta.144.1"}function SZ($){let Q=/^(.*?\.app)\/Contents\/Developer\/?$/.exec($);return Q?Q[1]:null}async function kZ($){let Q=new Set,Z=await $("/usr/bin/mdfind",["kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode'"]);for(let J of Z?.split(`
|
|
61
61
|
`)??[])if(J.trim().endsWith(".app"))Q.add(J.trim());try{for(let J of DZ("/Applications"))if(/^Xcode.*\.app$/.test(J))Q.add($6("/Applications",J))}catch{}return[...Q].sort()}async function $8($={}){let Q=$.probe??e7,Z=$.env??process.env,J=$.exists??PZ,[Y,X,K,z,G,V]=await Promise.all([Q("/usr/bin/sw_vers",["-productVersion"]),Q("/usr/bin/sw_vers",["-buildVersion"]),Q("/usr/sbin/sysctl",["-n","machdep.cpu.brand_string"]),Q("/usr/bin/xcode-select",["-p"]),Q("/usr/bin/xcodebuild",["-version"]),Q("/bin/ps",["-axo","pid=,command="])]),W=Z.DEVELOPER_DIR??z,H=W?SZ(W):null,N=await($.listXcodes?$.listXcodes():kZ(Q)),j=await Promise.all(N.map(async(q)=>{let A=await Q("/usr/bin/defaults",["read",$6(q,"Contents","Info"),"CFBundleShortVersionString"]),R=await Q("/usr/bin/defaults",["read",$6(q,"Contents","version"),"ProductBuildVersion"]);return{path:q,version:A,build:R,selected:q===H,has_device_hub:J($6(q,"Contents","Applications","DeviceHub.app")),has_duo_model:J(x9($6(q,"Contents","Developer")))}})),O=(V??"").split(`
|
|
62
62
|
`).map((q)=>/^\s*(\d+)\s+(.*\/DeviceHub\.app\/Contents\/MacOS\/\S+)/.exec(q)).filter((q)=>q!=null).map((q)=>{let A=q[2],R=/^(.*?\.app)\/Contents\/Applications\/DeviceHub\.app\//.exec(A)?.[1]??null;return{pid:Number(q[1]),executable:A,xcode:R,matches_selected:R!=null&&R===H}}),T=w9(Z,()=>{if(!z)throw Error("xcode-select -p failed");return z},J);return{macos:{version:Y,build:X},chip:K,arch:process.arch,node:process.version,xcode:{xcode_select:z,developer_dir_env:Z.DEVELOPER_DIR??null,effective_developer_dir:W,xcodebuild_version:G,installed:j},device_hub:{processes:O},duo_renderer:{executable:S9(),model_path:T.modelPath,model_source:T.source,model_found:T.exists,...T.error?{model_error:T.error}:{}}}}async function Q8($,Q=e7){let Z=await Q("/usr/bin/xcrun",a(["list","devices","-j"])),J=null;try{let Y=JSON.parse(Z??"{}");for(let[X,K]of Object.entries(Y.devices??{})){let z=K.find((G)=>G.udid===$);if(z){J={name:z.name,state:z.state,runtime:X,device_type:z.deviceTypeIdentifier??null};break}}}catch{}return{udid:$,...J??{error:"not found in simctl list"},device_hub_input_shadowed:await j7($,async(Y)=>{let X=await Q("/usr/bin/xcrun",Y);if(X==null)throw Error("probe failed");return X})}}function Z8($){return!!$&&typeof $==="object"&&!Array.isArray($)}function yZ($){if(!Z8($))return $;let Q={...$};if(Array.isArray($.errors)){let Z=new Map;for(let J of $.errors){let Y=Z.get(J.codec);if(!Y){Z.set(J.codec,{...J});continue}let X=J.last_at>Y.last_at?J:Y;Z.set(J.codec,{...X,count:Y.count+J.count,first_at:J.first_at<Y.first_at?J.first_at:Y.first_at,last_at:X.last_at,variants:(Y.variants??1)+1})}Q.errors=[...Z.values()].sort((J,Y)=>J.last_at<Y.last_at?1:J.last_at>Y.last_at?-1:0)}if(Array.isArray($.sessions)){let Z=new Map;for(let J of $.sessions){let Y=Z.get(J.codec),X=(Y?.session_count??0)+1;Z.set(J.codec,{...!Y||J.at>=Y.at?J:Y,session_count:X})}Q.sessions=[...Z.values()].sort((J,Y)=>J.at<Y.at?-1:J.at>Y.at?1:0)}return Q}function fZ($){let Q=$.native;if(!Z8(Q)||!("encoders"in Q))return $;return{...$,native:{...Q,encoders:yZ(Q.encoders)}}}function J8($){let Q={schema_version:xZ,generated_at:($.now??new Date).toISOString(),serve_sim:{version:wZ(),pid:process.pid},host:$.host,devices:$.devices??[],sessions:($.sessions??[]).map(fZ),event_log:$.event_log??[],...$.server?{server:$.server}:{},...$.crash_reports?{crash_reports:$.crash_reports}:{}};return L0(Q,$.home)}function Y8($){let Q=$.now??Date.now,Z=$.burst,J=Q();return{take(){let Y=Q();if(Z=Math.min($.burst,Z+(Y-J)/$.refillMs),J=Y,Z>=1)return Z-=1,{ok:!0};return{ok:!1,retryAfterMs:Math.ceil((1-Z)*$.refillMs)}}}}function X8($,Q=Date.now){let Z=new Map;return{get(J,Y){let X=Q(),K=Z.get(J);if(K&&X-K.at<$)return K.value;for(let[V,W]of Z)if(X-W.at>=$)Z.delete(V);let z=Y(),G={at:X,value:z};return Z.set(J,G),z.catch(()=>{if(Z.get(J)===G)Z.delete(J)}),z}}}import{WebSocket as bZ}from"ws";var vZ=Array.from({length:10},($,Q)=>8081+Q),hZ=new Set(["host.exp.Exponent"]);function c9($){return!hZ.has($)}var W8=1000,G8=1200,fK=W8+G8,K8=2;function gZ($){let Q=new Map;for(let Z of $){if(!Z.appId)continue;let J=Z.reactNative?.logicalDeviceId??Z.deviceName??Z.appId;Q.set(J,Z.deviceName)}return Q}function V8($,Q,Z){return $.some((J)=>J.appId===Q&&J.deviceName===Z)}function uZ($,Q,Z){if(!V8($,Q,Z))return!1;let J=gZ($);return J.size===1&&[...J.values()][0]===Z}function z8($,Q){let Z=[...$],J=Z.find((X)=>X.udid===Q)?.name;if(!J)return;return Z.some((X)=>X.udid!==Q&&X.name===J&&X.state==="Booted")?void 0:J}async function mZ($){let Q=await fetch($,{signal:AbortSignal.timeout(800)});if(!Q.ok)throw Error(`HTTP ${Q.status}`);return Q.json()}function lZ($){if(!$||typeof $!=="object")return[];return Object.entries($).filter(([,Q])=>pZ(Q)).map(([Q])=>Q)}function pZ($){if(typeof $==="string")return new URLSearchParams($).has("role");if($&&typeof $==="object")return $.role!=null;return!1}function cZ($,{peersTimeoutMs:Q=500,timeoutMs:Z=G8}={}){return new Promise((J)=>{let Y=new bZ(`ws://127.0.0.1:${$}/message`),X=`serve-sim-peers-${Date.now()}`,K,z=!1,G=null,V=(N)=>{if(z)return;z=!0,clearTimeout(W),clearTimeout(K),J(N);try{Y.close()}catch{}},W=setTimeout(()=>V(G??"failed"),Z),H=(N,j)=>{if(z||G)return;clearTimeout(K),G=j,Y.send(JSON.stringify({version:K8,...N}),(O)=>V(O?"failed":j))};Y.on("open",()=>{Y.send(JSON.stringify({version:K8,id:X,method:"getpeers",target:"server"})),K=setTimeout(()=>H({method:"reload"},"broadcast"),Q)}),Y.on("message",(N)=>{let j;try{j=JSON.parse(N.toString())}catch{return}if(j.id!==X||G)return;if(j.error!==void 0&&j.result===void 0){H({method:"reload"},"broadcast");return}let O=lZ(j.result);if(O.length===1)H({method:"reload",target:O[0]},"targeted");else V(O.length===0?"failed":"ambiguous")}),Y.on("error",()=>V(G??"failed"))})}async function dZ($,Q){let Z=await Promise.all($.map(async(Y)=>{try{let X=await Q(`http://127.0.0.1:${Y}/json/list`);return Array.isArray(X)?X:null}catch{return null}})),J=new Map;return $.forEach((Y,X)=>{if(Z[X])J.set(Y,Z[X])}),J}async function H8($,Q,Z=vZ,{fetchJson:J=mZ,sendReload:Y=(K)=>cZ(K),lookupBudgetMs:X=W8}={}){if(!c9($))return{sent:!1,reason:"unsupported-app"};let K,z=await Promise.race([Promise.all([Promise.resolve(Q).catch(()=>null),dZ(Z,J)]),new Promise((O)=>{K=setTimeout(()=>O(null),X)})]).finally(()=>clearTimeout(K));if(!z)return{sent:!1,reason:"timeout"};let[G,V]=z;if(!G)return{sent:!1,reason:"unknown-device"};let W=[...V].filter(([,O])=>V8(O,$,G));if(W.length===0)return{sent:!1,reason:"no-metro"};let H=W.map(([O])=>O);if(W.length>1||!uZ(W[0][1],$,G))return{sent:!1,reason:"ambiguous",ports:H};let N=H[0],j=await Y(N);if(j==="targeted"||j==="broadcast")return{sent:!0,port:N,targeted:j==="targeted"};return{sent:!1,reason:j==="ambiguous"?"ambiguous":"send-failed",ports:H}}import{execFileSync as u1}from"child_process";import{createHash as m1}from"crypto";import{readFileSync as l1,unlinkSync as JW}from"fs";import{fileURLToPath as Q1}from"url";import{dirname as Z1}from"path";import{createHash as N8,timingSafeEqual as nZ}from"crypto";var F8="SERVE_SIM_AUTH_TOKEN",d9="serve_sim_auth",M8="x-serve-sim-auth",iZ=new Set(["forwarded","via","x-real-ip","x-client-ip","x-cluster-client-ip","true-client-ip","fastly-client-ip","cf-connecting-ip","cf-ray"]),oZ=["x-forwarded-","tailscale-","ngrok-","x-original-forwarded"],aZ=new Map;function E0($){return Array.isArray($)?$[0]:$}function n9($){let Q=$.socket;if(!Q)return;let Z=Q.remoteAddress;if(Z&&i9(Z)&&typeof Q.remotePort==="number"){let J=aZ.get(Q.remotePort);if(J)return J}return Z}function i9($){if(!$)return!1;let Q=$.toLowerCase();if(Q==="::1"||Q==="0:0:0:0:0:0:0:1")return!0;let Z=Q.startsWith("::ffff:")?Q.slice(7):Q;return/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(Z)}function rZ($){if(!$)return;try{return new URL(`http://${$}`).hostname.toLowerCase()}catch{return}}function U8($){if(!$)return!1;if($==="localhost"||$==="[::1]")return!0;return i9($)}function tZ($){for(let Q of Object.keys($)){let Z=Q.toLowerCase();if(iZ.has(Z))return!0;if(oZ.some((J)=>Z.startsWith(J)))return!0}return!1}function sZ($){return i9(n9($))&&!tZ($.headers)}function I0($){if(!sZ($))return!1;return U8(rZ(E0($.headers.host)))}function Q6($){let Q=E0($.headers.origin);if(!Q)return"absent";try{let Z=new URL(Q);if(Z.protocol!=="http:"&&Z.protocol!=="https:")return"cross";let J=Z.host.toLowerCase();return[E0($.headers.host),E0($.headers["x-forwarded-host"])?.split(",")[0]?.trim()].some((X)=>!!X&&X.toLowerCase()===J)?"same":"cross"}catch{return"cross"}}function Z6($){let Q=E0($.headers.origin);if(!Q)return!1;try{let Z=new URL(Q);if(Z.protocol!=="http:"&&Z.protocol!=="https:")return!1;if(Z.host.toLowerCase()!==(E0($.headers.host)??"").toLowerCase())return!1;return U8(Z.hostname.toLowerCase())}catch{return!1}}function B8($){return N8("sha256").update($).digest()}function a6($,Q){return nZ(B8($),B8(Q))}function o9($){return N8("sha256").update(`serve-sim-auth-cookie:${$}`).digest("base64url")}function eZ($,Q){if(!$)return;for(let Z of $.split(";")){let J=Z.indexOf("=");if(J===-1)continue;if(Z.slice(0,J).trim()===Q)return Z.slice(J+1).trim()}return}function $1($,Q){let Z=E0($.headers.authorization)??"",J=/^Bearer\s+(.+)$/i.exec(Z)?.[1]?.trim();if(J&&a6(J,Q))return!0;let Y=eZ(E0($.headers.cookie),d9);return!!Y&&a6(Y,o9(Q))}function a9($,Q){if(!Q)return{ok:!1,status:403,message:`Remote access is disabled. Start serve-sim with --auth-token <token> (or ${F8}) and send it as "Authorization: Bearer <token>".`};if($1($,Q))return{ok:!0};return{ok:!1,status:401,message:"Missing or invalid serve-sim auth token."}}function j8($){let Q=($??process.env[F8]??"").trim();return Q===""?void 0:Q}function C8($){return Z1(Q1($))}import{execSync as Y1}from"child_process";function L8($){try{let Q=Y1(`lsof -ti tcp:${$} -sTCP:LISTEN`,{encoding:"utf-8",stdio:"pipe"}).trim();if(!Q)return[];let Z=process.pid;return Q.split(`
|
|
63
63
|
`).map((J)=>parseInt(J,10)).filter((J)=>Number.isFinite(J)&&J!==Z)}catch{return[]}}var L6=b5(I8(),1),sK=L6.default("serve-sim:cli"),g1=L6.default("serve-sim:helper"),s9=L6.default("serve-sim:state"),O6=L6.default("serve-sim:mw");var p1=/^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i;function c1($){return p1.test($)}function d1($){if(!Number.isInteger($)||$<=0)return!1;try{return process.kill($,0),!0}catch{return!1}}function n1($){return/serve-sim(?:-bin)?/.test($)}function i1($){try{return l1(`/proc/${$}/cmdline`,"utf8").split(String.fromCharCode(0)).join(" ").trim()}catch{}try{return u1("ps",["-p",String($),"-o","args="],{encoding:"utf-8",stdio:["ignore","pipe","pipe"],timeout:1000}).trim()}catch{return null}}function x8($,Q={}){let Z=Q.isAlive??d1,J=Q.portHolders??L8,Y=Q.commandLine??i1;if(Z($.pid))return $;for(let X of J($.port)){if(X===process.pid||!Z(X))continue;let K=Y(X);if(K==null||!n1(K))continue;return s9("recovered helper for %s via port %d pid %d (recorded pid %d was dead)",$.device,$.port,X,$.pid),{...$,pid:X}}return null}function w8($){try{c6($)}catch(Q){s9("failed to rewrite recovered state for %s: %o",$.device,Q)}}function S8($){return $?m1("sha256").update($).digest("hex"):void 0}function k8($,Q,Z,J){if(!b6($.deviceSet,J))return"keep";if(Q&&c1($.device)&&!Q.has($.device))return $.pid===Z?"recycle-self":"recycle-helper";return"keep"}import{execFileSync as $9}from"child_process";import{createHash as c8}from"crypto";import{existsSync as N0,mkdirSync as d8,readdirSync as o1,readFileSync as P0,renameSync as n8,statSync as i8,unlinkSync as h8}from"fs";import{tmpdir as o8}from"os";import{basename as a8,dirname as Q9,join as J0}from"path";import{inflateSync as a1}from"zlib";function y8($){if($.length===0)return null;return $.reduce((Q,Z)=>Z.width*Z.height<Q.width*Q.height?Z:Q)}function f8($){if($.length!==2)return $.map(()=>"display");return $[0]<=$[1]?["cover","inner"]:["inner","cover"]}function b8($,Q="Display"){switch($){case"cover":return"Cover";case"inner":return"Inner";default:return Q}}function v8($){let Q=$$($$($).capabilities??$),Z=Array.isArray(Q.displays)?Q.displays:[],J=[];for(let Y of Z){let X=$$(Y);if(R6(X.displayType)!=="integrated")continue;if(X.hasDigitizer!==!0)continue;let K=R6(X.chromeIdentifier);if(!K)continue;let z=e9(X.width),G=e9(X.height);if(!z||!G||z<=0||G<=0)continue;let V=e9(X.scale),W=R6(X.deviceName)??(typeof X.screenID==="number"?`screen-${X.screenID}`:`display-${J.length}`);J.push({id:W,label:R6(X.displayName)??W,chromeIdentifier:K,mask:R6(X.framebufferMaskIdentifier),width:z,height:G,logicalScreenSize:V&&V>0?{width:z/V,height:G/V}:null})}return J}function e9($){if(typeof $==="number"&&Number.isFinite($))return $;if(typeof $==="string"&&$.trim()){let Q=Number($);return Number.isFinite(Q)?Q:null}return null}function R6($){return typeof $==="string"&&$?$:null}function $$($){return $&&typeof $==="object"&&!Array.isArray($)?$:{}}var V$="/Library/Developer/CoreSimulator/Profiles/DeviceTypes",r8="/Library/Developer/DeviceKit/Chrome",g8="com.apple.dt.devicekit.chrome.",u8=J0(o8(),"serve-sim-devicekit-chrome"),m8=J0(o8(),"serve-sim-device-placeholder-assets"),x0="/System/Library/CoreServices/CoreTypes.bundle/Contents/Library/MobileDevices.bundle/Contents/Resources",r1="/System/Library/CoreServices/CoreTypes.bundle/Contents/Library/CoreTypes-0006.bundle/Contents/Resources",l8={"vision-pro":{paths:[J0(x0,"com.apple.vision-pro.icns"),J0(r1,"com.apple.visionpro.icns")]},"iphone-17e":{paths:[J0(x0,"com.apple.iphone-16-e-1.icns")]},"ipad-air-11-inch-m4":{paths:[J0(x0,"com.apple.ipad-air-m3-1.icns")]},"ipad-air-13-inch-m4":{paths:[J0(x0,"com.apple.ipad-air-m3-1.icns")]}},Q$=null,Z$=new Map,J$=new Map,Y$=new Map,X$=new Map,K$=null,W$=new Map;function A6($){return $.startsWith(g8)?$.slice(g8.length):$}function z$($){let Q=typeof $==="string"?$:$.toString("latin1"),Z=/\/CropBox\s*\[\s*(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s*\]/.exec(Q)??/\/MediaBox\s*\[\s*(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s*\]/.exec(Q);if(!Z)return null;let J=Number(Z[1]),Y=Number(Z[2]),X=Number(Z[3]),K=Number(Z[4]),z=Math.abs(X-J),G=Math.abs(K-Y);return z>0&&G>0?{width:z,height:G}:null}function t1($,Q){let Z=N2($);if(Z)return Z;let J=typeof $.framebufferMask==="string"?$.framebufferMask:null;if(!J)return null;let Y=typeof $.__profileDir==="string"?$.__profileDir:null;if(!Y)return null;let X=J0(Y,`${J}.pdf`);if(!N0(X))return null;let K=z$(P0(X));if(!K)return null;let z=F2(Q);return{width:K.width/z,height:K.height/z}}function H$($){let Q=J9($);if(Y$.has(Q))return Y$.get(Q)??[];let Z=Q2(Q);return Y$.set(Q,Z),Z}function t8($){let Q=H$($);if(Q.length>0)return y8(Q)?.chrome??null;let Z=J9($),J=Z;if(J$.has(J))return J$.get(J)??null;let Y=Z2(Z);return J$.set(J,Y),Y}function s8($){let Q=J9($);if(X$.has(Q))return X$.get(Q)??null;let Z=s1($);return X$.set(Q,Z),Z}function s1($){let Q=Y2($),Z=Q?J4(Q):null,J=X2(Z)??K2($.name);if(!J)return null;let Y=Q4(J);return Y?{name:J,width:Y.width,height:Y.height}:null}function e8($,Q){let Z=A6($.searchParams.get("chrome")??""),J=$.searchParams.get("image")??"";if(!/^[A-Za-z0-9_-]+$/.test(Z)||!J){K6(Q,400,"Invalid chrome asset request");return}let Y=K4(Z);if(!Y||!Y.allowedImages.has(J)||J.includes("/")){K6(Q,404,"Chrome asset not found");return}let X=N$(Z,J);if(!N0(X)){K6(Q,404,"Chrome asset not found");return}try{let K=W4(Z,J,X),z=P0(K);Q.writeHead(200,{"Content-Type":"image/png","Cache-Control":"public, max-age=604800, immutable","Content-Length":String(z.byteLength)}),Q.end(z)}catch(K){K6(Q,500,K instanceof Error?K.message:"Failed to render chrome asset")}}function $4($,Q){let Z=$.searchParams.get("name")??"";try{let J=Q4(Z);if(!J){K6(Q,404,"Placeholder asset not found");return}let Y=P0(J.pngPath);Q.writeHead(200,{"Content-Type":"image/png","Cache-Control":"public, max-age=604800, immutable","Content-Length":String(Y.byteLength)}),Q.end(Y)}catch(J){K6(Q,500,J instanceof Error?J.message:"Failed to render placeholder asset")}}function Q4($){if(!/^[A-Za-z0-9._-]+$/.test($))return null;if(W$.has($))return W$.get($)??null;let Q=e1($),Z=Q?M2($,Q):null;return W$.set($,Z),Z}function e1($){let Z=$2($)?.paths.find((Y)=>N0(Y));if(Z)return Z;if(!X4().some((Y)=>Y.iconName===$))return null;let J=J0(x0,`${$}.icns`);return N0(J)?J:null}function $2($){return Object.prototype.hasOwnProperty.call(l8,$)?l8[$]:null}function Q2($){let Q=B$($);if(!N0(Q))return[];let Z=Q9(Q),J=_6(J0(Z,"capabilities.plist")),Y=v8(J);if(Y.length===0)return[];let X=f8(Y.map((z)=>z.width*z.height)),K=[];for(let z=0;z<Y.length;z++){let G=Y[z],V=Z4(A6(G.chromeIdentifier),Y4(Z,G.mask),G.logicalScreenSize);if(!V)continue;let W=X[z]??"display";K.push({id:G.id,role:W,name:b8(W,G.label),width:G.width,height:G.height,chrome:V})}return K}function Z2($){let Q=B$($);if(!N0(Q))return null;let Z=J4(Q);if(!Z?.chromeIdentifier)return null;return Z4(Z.chromeIdentifier,Z.framebufferMaskSize,Z.screenSize)}function Z4($,Q,Z){let J=K4($);if(!J)return null;let Y=null;if(J.compositeImage)Y=G$(J.identifier,J.compositeImage);let X=J.compositeImage?C2(J.identifier,J.compositeImage):null,K=(X&&Q?j2(Q,X):null)??Z??(Y?{width:Y.width-J.insets.left-J.insets.right,height:Y.height-J.insets.top-J.insets.bottom}:null);if(!K||K.width<=0||K.height<=0)return null;let z=Y??{width:K.width+J.insets.left+J.insets.right,height:K.height+J.insets.top+J.insets.bottom},G={x:J.devicePadding.left,y:J.devicePadding.top,width:z.width,height:z.height},V={width:z.width+J.devicePadding.left+J.devicePadding.right,height:z.height+J.devicePadding.top+J.devicePadding.bottom},W={x:G.x+(z.width-K.width)/2,y:G.y+(z.height-K.height)/2,width:K.width,height:K.height},H=X?Math.max(0,X.radius-(X.width-K.width)/2):J.innerCornerRadius,N=J.slice?G$(J.identifier,J.slice.topLeft):null,j=J.identifier.startsWith("watch"),O=J.buttons.flatMap((T)=>{let q=G$(J.identifier,T.image);if(!q)return[];let A=z2(T,q,z,J.devicePadding),R={x:q.width>0?(T.rolloverOffset.x-T.normalOffset.x)/q.width:0,y:q.height>0?(T.rolloverOffset.y-T.normalOffset.y)/q.height:0};return[{name:T.name,image:T.image,imageDown:T.imageDown,onTop:T.onTop||j,frame:{...A,...q},hover:R,usagePage:T.usagePage,usage:T.usage}]});return{identifier:J.identifier,frame:V,body:G,screen:W,insets:J.insets,outerCornerRadius:J.outerCornerRadius,innerCornerRadius:J.innerCornerRadius,screenRadius:H,compositeImage:J.compositeImage,slice:J.slice,corner:N,buttons:O}}function J4($){let Q=_6($);if(!Q)return null;let Z=typeof Q.chromeIdentifier==="string"?Q.chromeIdentifier:null,J=Z?A6(Z):null;return{chromeIdentifier:J,modelIdentifier:typeof Q.modelIdentifier==="string"?Q.modelIdentifier:null,productClass:typeof Q.productClass==="string"?Q.productClass:null,screenSize:J?t1({...Q,__profileDir:Q9($)},J):null,framebufferMaskSize:J2({...Q,__profileDir:Q9($)})}}function J2($){let Q=typeof $.framebufferMask==="string"?$.framebufferMask:null,Z=typeof $.__profileDir==="string"?$.__profileDir:null;if(!Q||!Z)return null;return Y4(Z,Q)}function Y4($,Q){if(!Q)return null;let Z=J0($,`${Q}.pdf`);if(!N0(Z))return null;return z$(P0(Z))}function J9($){return H2($.deviceTypeIdentifier)??$.name}function Y2($){let Q=B$(J9($));return N0(Q)?Q:null}function B$($){return J0(V$,`${$}.simdevicetype`,"Contents","Resources","profile.plist")}function X2($){if(!$)return null;let Q=new Set;if($.modelIdentifier)Q.add($.modelIdentifier);if($.productClass)Q.add($.productClass),Q.add(`${$.productClass}AP`);if(Q.size===0)return null;for(let Z of X4())if(Z.modelCodes.some((J)=>Q.has(J)))return Z.iconName;return null}function K2($){let Q=$.toLowerCase();if(Q.includes("vision"))return"vision-pro";if(/iphone\s+17e\b/.test(Q))return"iphone-17e";if(/ipad\s+air\s+11-inch\s+\(m4\)/.test(Q))return"ipad-air-11-inch-m4";if(/ipad\s+air\s+13-inch\s+\(m4\)/.test(Q))return"ipad-air-13-inch-m4";return null}function X4(){if(K$)return K$;let $=_6(J0(x0,"MobileDevices-Info.plist"))??_6(J0(Q9(x0),"Info.plist")),Q=Array.isArray($?.UTExportedTypeDeclarations)?$.UTExportedTypeDeclarations:[],Z=[];for(let J of Q){let Y=O0(J),X=B0(Y.UTTypeIconFile);if(!X||!X.endsWith(".icns"))continue;let K=J0(x0,X);if(!N0(K))continue;let G=O0(Y.UTTypeTagSpecification)["com.apple.device-model-code"],V=Array.isArray(G)?G.filter((W)=>typeof W==="string"&&W.length>0):[];if(V.length===0)continue;Z.push({description:B0(Y.UTTypeDescription)??"",iconFile:X,iconName:a8(X,".icns"),modelCodes:V})}return K$=Z,Z}function K4($){let Q=A6($);if(!/^[A-Za-z0-9_-]+$/.test(Q))return null;if(Z$.has(Q))return Z$.get(Q)??null;let Z=J0(r8,`${Q}.devicechrome`,"Contents","Resources","chrome.json"),J=null;try{let Y=JSON.parse(P0(Z,"utf-8"));J=W2(Y)}catch{J=null}return Z$.set(Q,J),J}function W2($){let Q=typeof $.identifier==="string"?$.identifier:null;if(!Q)return null;let Z=A6(Q),J=O0($.images),Y=O0(J.sizing),X=O0($.paths),K=T0(O0(X.simpleOutsideBorder).cornerRadiusX),z={top:T0(Y.topHeight),left:T0(Y.leftWidth),bottom:T0(Y.bottomHeight),right:T0(Y.rightWidth)},G=O0(J.devicePadding),V={top:T0(G.top),left:T0(G.left),bottom:T0(G.bottom),right:T0(G.right)},W=typeof J.composite==="string"?J.composite:null,H=G2(J),N=Array.isArray($.inputs)?$.inputs.flatMap((O)=>{let T=V2(O0(O));return T?[T]:[]}):[],j=new Set;for(let O of Object.values(J))if(typeof O==="string")j.add(O);for(let O of N)j.add(O.image);if(Array.isArray($.inputs))for(let O of $.inputs){let T=O0(O);if(typeof T.imageDown==="string")j.add(T.imageDown)}return{identifier:Z,insets:z,devicePadding:V,outerCornerRadius:K,innerCornerRadius:Math.max(K-Math.max(z.left,z.top),0),compositeImage:W,slice:H,buttons:N,allowedImages:j}}function G2($){let Q=B0($.topLeft),Z=B0($.top),J=B0($.topRight),Y=B0($.right),X=B0($.bottomRight),K=B0($.bottom),z=B0($.bottomLeft),G=B0($.left);if(!Q||!Z||!J||!Y||!X||!K||!z||!G)return null;return{topLeft:Q,top:Z,topRight:J,right:Y,bottomRight:X,bottom:K,bottomLeft:z,left:G}}function V2($){let Q=B0($.name),Z=B0($.image);if(!Q||!Z)return null;let J=B0($.anchor),Y=B0($.align),X=O0($.offsets),K=p8(O0(X.normal)),z=p8(O0(X.rollover));return{name:Q,image:Z,imageDown:B0($.imageDown),onTop:$.onTop===!0,anchor:J==="right"||J==="top"||J==="bottom"?J:"left",align:Y==="trailing"?"trailing":"leading",normalOffset:K??z??{x:0,y:0},rolloverOffset:z??K??{x:0,y:0},usagePage:w0($.usagePage),usage:w0($.usage)}}function z2($,Q,Z,J){let{left:Y,top:X}=J,K=2*$.normalOffset.x-$.rolloverOffset.x,z=2*$.normalOffset.y-$.rolloverOffset.y,G=$.align==="trailing"?Y+Z.width+K-Q.width:Y+K;switch($.anchor){case"left":return{x:Y+$.rolloverOffset.x-Q.width/2,y:X+$.rolloverOffset.y};case"right":return{x:Y+Z.width+K,y:X+z};case"top":return{x:G,y:X+z-Q.height};case"bottom":return{x:G,y:X+Z.height+z}}}function H2($){if(!$)return null;if(!Q$)Q$=B2();return Q$.get($)??null}function B2(){let $=new Map;try{for(let Q of o1(V$)){if(!Q.endsWith(".simdevicetype"))continue;let Z=J0(V$,Q),J=_6(J0(Z,"Contents","Info.plist")),Y=typeof J?.CFBundleIdentifier==="string"?J.CFBundleIdentifier:null,X=typeof J?.CFBundleName==="string"?J.CFBundleName:a8(Q,".simdevicetype");if(Y)$.set(Y,X)}}catch{}return $}function _6($){try{let Q=$9("plutil",["-convert","json","-o","-",$],{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:2000});return JSON.parse(Q)}catch{return null}}function N2($){let Q=w0($.mainScreenWidth),Z=w0($.mainScreenHeight),J=w0($.mainScreenScale);if(!Q||!Z||!J||J<=0)return null;return{width:Q/J,height:Z/J}}function F2($){if($.startsWith("phone"))return 3;if($.startsWith("tablet"))return 2;if($.startsWith("watch"))return 2;return 1}function G$($,Q){let Z=N$($,Q);if(!N0(Z))return null;return z$(P0(Z))}function N$($,Q){return J0(r8,`${$}.devicechrome`,"Contents","Resources",`${Q}.pdf`)}function W4($,Q,Z){d8(u8,{recursive:!0});let J=i8(Z),Y=c8("sha1").update($).update("\x00").update(Q).update("\x00").update(String(J.mtimeMs)).update("\x00").update(String(J.size)).digest("hex"),X=J0(u8,`${$}-${Y}.png`);if(N0(X))return X;let K=`${X}.${process.pid}.tmp`;return $9("sips",["-s","format","png",Z,"--out",K],{stdio:["ignore","ignore","ignore"],timeout:1e4}),n8(K,X),X}function M2($,Q){d8(m8,{recursive:!0});let Z=i8(Q),J=c8("sha1").update($).update("\x00").update(Q).update("\x00").update(String(Z.mtimeMs)).update("\x00").update(String(Z.size)).digest("hex"),Y=J0(m8,`${$}-${J}.png`);if(N0(Y)){let z=Z9(P0(Y));return{sourcePath:Q,pngPath:Y,width:z.width,height:z.height}}let X=`${Y}.${process.pid}.tmp`,K=`${Y}.${process.pid}.raw.png`;try{$9("sips",["-s","format","png",Q,"--out",K],{stdio:["ignore","ignore","ignore"],timeout:1e4});let z=P0(K),G=U2(z)??{x:0,y:0,...Z9(z)};return $9("sips",["-c",String(G.height),String(G.width),"--cropOffset",String(G.y),String(G.x),"-s","format","png",Q,"--out",X],{stdio:["ignore","ignore","ignore"],timeout:1e4}),n8(X,Y),{sourcePath:Q,pngPath:Y,width:G.width,height:G.height}}finally{try{if(N0(K))h8(K)}catch{}try{if(N0(X))h8(X)}catch{}}}function Z9($){if($.byteLength<24||$[0]!==137||$.toString("ascii",1,4)!=="PNG")throw Error("Invalid PNG placeholder asset");return{width:$.readUInt32BE(16),height:$.readUInt32BE(20)}}function G4($,Q){let{width:Z,height:J}=Z9($),Y=8,X=0,K=0,z=[];while(Y<$.byteLength){let T=$.readUInt32BE(Y);Y+=4;let q=$.toString("ascii",Y,Y+4);Y+=4;let A=$.subarray(Y,Y+T);if(Y+=T+4,q==="IHDR")X=A[8]??0,K=A[9]??0;else if(q==="IDAT")z.push(A);else if(q==="IEND")break}let G=K===6?4:K===4?2:0;if(X!==8||G===0||z.length===0)return null;let V=K===6,W=a1(Buffer.concat(z)),H=Z*G,N=new Uint8Array(Z*J),j=Buffer.alloc(H),O=0;for(let T=0;T<J;T++){let q=W[O++]??0,A=Buffer.from(W.subarray(O,O+H));O+=H,O2(A,j,G,q);for(let R=0;R<Z;R++){let _=R*G,D=A[_]??0,E=V?A[_+1]??0:D,F=V?A[_+2]??0:D,B=A[_+G-1]??0;if(Q(D,E,F,B))N[T*Z+R]=1}j=A}return{width:Z,height:J,mask:N}}function U2($){let{width:Q,height:Z}=Z9($),J=G4($,(G,V,W,H)=>H>0);if(!J)return{x:0,y:0,width:Q,height:Z};let Y=Q,X=Z,K=-1,z=-1;for(let G=0;G<Z;G++)for(let V=0;V<Q;V++){if(!J.mask[G*Q+V])continue;if(V<Y)Y=V;if(G<X)X=G;if(V>K)K=V;if(G>z)z=G}if(K<Y||z<X)return{x:0,y:0,width:Q,height:Z};return{x:Y,y:X,width:K-Y+1,height:z-X+1}}function j2($,Q){if(Q.width<=0)return null;let Z=Math.max(1,Math.round($.width/Q.width));return{width:$.width/Z,height:$.height/Z}}function C2($,Q){let Z=L2($,Q);if(!Z)return null;let J=G4(Z,(R,_,D,E)=>E>200&&R<30&&_<30&&D<30);if(!J)return null;let{width:Y,height:X,mask:K}=J,z=Math.floor(Y/2),G=Math.floor(X/2),V=(R,_)=>R>=0&&_>=0&&R<Y&&_<X&&K[_*Y+R]===1;if(!V(z,G))return null;let W=z;while(W>0&&V(W-1,G))W--;let H=z;while(H<Y-1&&V(H+1,G))H++;let N=G;while(N>0&&V(z,N-1))N--;let j=G;while(j<X-1&&V(z,j+1))j++;let O=j-N,T=(R,_)=>{let D=0;while(D<O&&!V(R,_?N+D:j-D))D++;return D},q=[T(W,!0),T(H,!0),T(W,!1),T(H,!1)],A=q.reduce((R,_)=>R+_,0)/q.length;return{x:W,y:N,width:H-W+1,height:j-N+1,radius:A}}function L2($,Q){let Z=N$($,Q);if(!N0(Z))return null;try{return P0(W4($,Q,Z))}catch{return null}}function O2($,Q,Z,J){for(let Y=0;Y<$.byteLength;Y++){let X=Y>=Z?$[Y-Z]:0,K=Q[Y]??0,z=Y>=Z?Q[Y-Z]:0;switch(J){case 0:break;case 1:$[Y]=$[Y]+X&255;break;case 2:$[Y]=$[Y]+K&255;break;case 3:$[Y]=$[Y]+Math.floor((X+K)/2)&255;break;case 4:$[Y]=$[Y]+R2(X,K,z)&255;break;default:throw Error(`Unsupported PNG filter ${J}`)}}}function R2($,Q,Z){let J=$+Q-Z,Y=Math.abs(J-$),X=Math.abs(J-Q),K=Math.abs(J-Z);if(Y<=X&&Y<=K)return $;return X<=K?Q:Z}function K6($,Q,Z){$.writeHead(Q,{"Content-Type":"application/json","Cache-Control":"no-store"}),$.end(JSON.stringify({ok:!1,error:Z}))}function T0($){return w0($)??0}function w0($){if(typeof $==="number"&&Number.isFinite($))return $;if(typeof $==="string"&&$.trim()){let Q=Number($);return Number.isFinite(Q)?Q:null}return null}function B0($){return typeof $==="string"&&$?$:null}function O0($){return $&&typeof $==="object"&&!Array.isArray($)?$:{}}function p8($){let Q=w0($.x),Z=w0($.y);return Q===null||Z===null?null:{x:Q,y:Z}}import{exec as _2}from"child_process";import{timingSafeEqual as A2}from"crypto";import{request as T2}from"http";import{WebSocketServer as P2}from"ws";var D2=1e4,q2=4194304,Y9="exec disabled; start with --allow-exec",V4="exec requires the exec token",E2="host actions are only available to loopback clients";function I2($){for(let Q=0;Q<$.length;Q++){let Z=$.charCodeAt(Q);if(Z<=31||Z===127)return!0}return/\s/.test($)}function x2($,Q){if(!Q||typeof $!=="string")return!1;let Z=Buffer.from($),J=Buffer.from(Q);return Z.length===J.length&&A2(Z,J)}function z4($){return I0($)&&Z6($)}function w2($,Q,Z,J,Y){let X=!1,K=!1,z=new Map,G=Z.ssePrefixes??[],V=(N)=>{if($.readyState===$.OPEN)$.send(JSON.stringify(N))},W=setTimeout(()=>{if(!X)$.close()},D2);W.unref?.();let H=(N,j)=>{if(z.has(N))return;let O=j.split("?")[0];if(!j.startsWith("/")||I2(j)||!G.some((q)=>O===q)){V({sub:N,end:!0,error:"path not allowed"});return}if(!Q){V({sub:N,end:!0,error:"no local port"});return}let T;try{T=T2({host:"127.0.0.1",port:Q,path:j,headers:{accept:"text/event-stream"}},(q)=>{q.on("data",(A)=>V({sub:N,data:A.toString("utf-8")})),q.on("end",()=>{z.delete(N),V({sub:N,end:!0})})})}catch{V({sub:N,end:!0,error:"bad path"});return}T.on("error",()=>{z.delete(N),V({sub:N,end:!0})}),T.end(),z.set(N,{destroy:()=>T.destroy()})};$.on("message",(N)=>{let j;try{j=JSON.parse(N.toString())}catch{return}if(!X){X=!0,K=x2(j.token,Z.execToken),clearTimeout(W),V({ready:!0});return}if(typeof j.unsub==="number"){z.get(j.unsub)?.destroy(),z.delete(j.unsub);return}if(typeof j.sub==="number"&&typeof j.path==="string"){H(j.sub,j.path);return}if(typeof j.id==="number"&&j.rpc!==void 0){let{id:q}=j;if(!Z.onRpcRequest){V({id:q,error:"rpc requests not supported"});return}if(!Y){V({id:q,error:E2});return}Z.onRpcRequest(j.rpc).then((A)=>V({...A,id:q})).catch((A)=>V({id:q,error:A instanceof Error?A.message:String(A)}));return}if(typeof j.id==="number"&&j.ui!==void 0){let{id:q}=j;if(!Z.onUiRequest){V({id:q,error:"ui requests not supported"});return}Z.onUiRequest(j.ui).then((A)=>V({id:q,...A})).catch((A)=>V({id:q,error:A instanceof Error?A.message:String(A)}));return}if(typeof j.id!=="number"||typeof j.command!=="string"||!j.command)return;let{id:O,command:T}=j;if(!Z.allowExec){V({id:O,stdout:"",stderr:Y9,exitCode:126,error:Y9});return}if(!J){V({id:O,stdout:"",stderr:"exec is only available to loopback clients",exitCode:126});return}if(!K){V({id:O,stdout:"",stderr:V4,exitCode:126,error:V4});return}_2(T,{maxBuffer:16777216},(q,A,R)=>{let _={id:O,stdout:A.toString(),stderr:R.toString(),exitCode:q?q.code??1:0};try{Z.onCommandResult?.(T,_)}catch{}V(_)})}),$.on("error",()=>$.close()),$.on("close",()=>{clearTimeout(W);for(let N of z.values())N.destroy();z.clear()})}function H4($){let Q=new P2({noServer:!0,maxPayload:q2});return function(J,Y,X){let K=J.url??"",z=K.indexOf("?"),G=z===-1?K:K.slice(0,z);if(G!==$.path&&G!==`${$.path}/`)return!1;if(Q6(J)!=="same")return Y.destroy(),!0;let V=($.allowShell??z4)(J),W=($.allowRpc??z4)(J),H=$.serverPort?.()||Y.localPort||void 0;return Q.handleUpgrade(J,Y,X,(N)=>{w2(N,H,$,V,W)}),!0}}import{execFile as l2}from"child_process";import{randomUUID as p2}from"crypto";import{closeSync as c2,constants as d2,fstatSync as n2,lstatSync as O4,openSync as i2,readFileSync as R4,unlinkSync as _4,writeSync as o2}from"fs";import{homedir as a2}from"os";import{basename as r2,dirname as t2,join as W9}from"path";var B4=[{key:"camera",label:"Camera"},{key:"microphone",label:"Microphone"},{key:"photos",label:"Photos"},{key:"photos-add",label:"Add to Photos"},{key:"contacts",label:"Contacts"},{key:"calendar",label:"Calendar"},{key:"reminders",label:"Reminders"},{key:"location",label:"Location"},{key:"location-always",label:"Location (Always)"},{key:"notifications",label:"Notifications"},{key:"motion",label:"Motion"},{key:"media-library",label:"Media Library"},{key:"siri",label:"Siri"}];function S2($){return`'${$.replace(/'/g,"'\\''")}'`}function N4($){return $.map(S2).join(" ")}import{execFile as k2}from"child_process";import{existsSync as M4}from"fs";import{join as F$,resolve as y2}from"path";var M$=C8(import.meta.url),U4=["extra-small","small","medium","large","extra-large","extra-extra-large","extra-extra-extra-large","accessibility-medium","accessibility-large","accessibility-extra-large","accessibility-extra-extra-large","accessibility-extra-extra-extra-large"],f2=["none","grayscale","red-green","green-red","blue-yellow"],b2={protanopia:"red-green",deuteranopia:"green-red",tritanopia:"blue-yellow"},T6=["on","off"],v2=new Set(["on","true","enabled","1","yes"]),h2=new Set(["off","false","disabled","0","no"]),u0={appearance:{via:"appearance",values:["light","dark"]},"liquid-glass":{via:"ax",values:["clear","tinted"]},"color-filter":{via:"ax",values:f2,aliases:b2},"text-size":{via:"content_size",values:U4,extraValues:["increment","decrement"]},"reduce-motion":{via:"ax",values:T6,toggle:!0},"increase-contrast":{via:"increase_contrast",values:T6,toggle:!0},"show-borders":{via:"ax",values:T6,toggle:!0},"reduce-transparency":{via:"ax",values:T6,toggle:!0},voiceover:{via:"ax",values:T6,toggle:!0}};function j4($,Q){let Z=u0[$];if(!Z)return null;let J=Q.toLowerCase();if(Z.toggle){if(v2.has(J))return"on";if(h2.has(J))return"off";return null}let Y=Z.aliases?.[J]??J;if(Z.values.includes(Y))return Y;if(Z.extraValues?.includes(Y))return Y;return null}function F4(){let $=[F$(M$,"..","dist","simax","serve-sim-ax-settings"),F$(M$,"simax","serve-sim-ax-settings")];for(let Q of $)if(M4(Q))return y2(Q);return null}var X9=null;function g2(){return X9??=(async()=>{let $=F4();if($)return $;let Q=F$(M$,"..","Sources","SimAXSettings","build.sh");if(!M4(Q))throw Error("sim-ax-settings binary not found — this build of serve-sim does not "+"include the simulator settings helper. Reinstall from a recent release.");console.error("[serve-sim] building sim-ax-settings (one-time)…"),await U$("bash",[Q]);let Z=F4();if(!Z)throw Error("Build succeeded but sim-ax-settings not found.");return Z})(),X9.catch(()=>{X9=null}),X9}function U$($,Q){return new Promise((Z,J)=>{k2($,Q,{encoding:"utf-8",maxBuffer:4194304},(Y,X,K)=>{if(Y)J(Error(String(K).trim()||Y.message));else Z(String(X).trim())})})}function C4($,Q,Z){let J=a(["ui",$,Q]);if(Z!==void 0)J.push(Z);return U$("xcrun",J)}async function j$($,...Q){let Z=await g2();return U$("xcrun",a(["spawn",$,Z,...Q]))}function u2($){return $==="enabled"?"on":"off"}function m2($){return $==="on"?"enabled":"disabled"}async function C$($,Q){let Z=u0[Q];if(!Z)throw Error(`unknown option: ${Q}`);if(Z.via==="ax")return j$($,"get",Q);let J=(await C4($,Z.via)).toLowerCase();return Z.toggle?u2(J):J}async function K9($,Q,Z){let J=u0[Q];if(!J)throw Error(`unknown option: ${Q}`);if(J.via==="ax"){await j$($,"set",Q,Z);return}await C4($,J.via,J.toggle?m2(Z):Z)}async function L4($){let Q=Object.entries(u0).filter(([,X])=>X.via!=="ax"),[Z,...J]=await Promise.all([j$($,"status").then((X)=>JSON.parse(X)).catch(()=>({})),...Q.map(([X])=>C$($,X).catch(()=>"unsupported"))]),Y={};for(let[X,K]of Object.entries(u0))if(K.via==="ax")Y[X]=Z[X]??"unsupported";return Q.forEach(([X],K)=>{Y[X]=J[K]}),Y}var vW=`Usage: serve-sim ui [status] [--json] [-d udid]
|
|
@@ -145,5 +145,5 @@ ${r}`)}})();return}if(!s){M.destroy();return}let Z0=s.device??n;if(s.upstreamPat
|
|
|
145
145
|
\r
|
|
146
146
|
`)};let I,x=H4({path:`${Q}/exec-ws`,serverPort:()=>I,allowExec:z,execToken:K,allowShell:(P)=>I0(P)&&Z6(P),allowRpc:(P)=>I0(P)&&Z6(P),onRpcRequest:B,ssePrefixes:[`${Q}/api/events`,`${Q}/api/event-log/events`,`${Q}/appstate`,`${Q}/ax`],onUiRequest:U,onCommandResult:(P,M)=>v$(P,M)}),y=C.handleUpgrade;return C.handleUpgrade=(P,M,f)=>{let v=P.url??"",n=g$(v);if(H(n)){if(!I0(P)){let S=a9(P,G);if(!S.ok){O5(M,S.status,S.message);return}}if(Q6(P)==="cross"){O5(M,403,"Cross-origin WebSocket blocked");return}}if(x(P,M,f))return;y(P,M,f)},C.setServerPort=(P)=>{I=P},C}export{EY as startDeviceInProcess,hY as simulatorName,wG as simMiddleware,c0 as selectServeSimState,M9 as rewriteStateForRequestHost,bY as resolveServeSimBin,y0 as readServeSimStates,A5 as publicState,u$ as previewConfigForState,CY as parseGridPaging,UY as parseForegroundAppLogMessage,xG as matchInstalledAppByDisplayName};
|
|
147
147
|
|
|
148
|
-
//# debugId=
|
|
148
|
+
//# debugId=F46012660EB4547364756E2164756E21
|
|
149
149
|
//# sourceMappingURL=middleware.js.map
|
package/dist/serve-sim.js
CHANGED
|
@@ -49,7 +49,7 @@ Simulator-wide UI options:
|
|
|
49
49
|
reduce-transparency on | off
|
|
50
50
|
voiceover on | off`});function b4($){let Z=$.trim(),Q=/^\d+(\.\d+)?$/.test(Z)?Number(Z):NaN;if(!Number.isFinite(Q)||Q<=0||Q>1000)throw Error(`Invalid max bitrate '${$}': expected Mbps between 0 and 1000 (e.g. 8 or 2.5).`);return Q}function c7($=process.env,Z=console.warn){let Q=$.SERVE_SIM_MAX_BITRATE;if(Q===void 0||Q.trim()==="")return;try{return Math.round(b4(Q)*1e6)}catch(J){Z(`[serve-sim] Ignoring SERVE_SIM_MAX_BITRATE: ${J instanceof Error?J.message:String(J)}`);return}}function l7($,Z=process.env){if($!==void 0)Z.SERVE_SIM_MAX_BITRATE=String($)}var m7="SERVE_SIM_MAX_BITRATE";function SY($){return $==="landscape_left"||$==="landscape_right"}function yY($){if(!$||$.width<=0||$.height<=0)return null;let Z=SY($.orientation)||$.width>$.height,Q=Z?Math.max($.width,$.height):Math.min($.width,$.height),J=Z?Math.min($.width,$.height):Math.max($.width,$.height);if(Q===$.width&&J===$.height)return $;return{...$,width:Q,height:J}}function K9($){let Z=yY($),Q=fY($?.orientation),J=Math.abs(Q)===90,Y=!!$&&$.width>$.height,X=Q===180||J&&!Y;return{displayConfig:Z,rotationDegrees:X?Q:0,needsCssRotation:X,inputOrientation:X?$?.orientation:void 0}}function fY($){switch($){case"landscape_left":return 90;case"landscape_right":return-90;case"portrait_upside_down":return 180;default:return 0}}function n7($,Z,Q){switch($){case"landscape_left":return{x:Q,y:1-Z};case"landscape_right":return{x:1-Q,y:Z};case"portrait_upside_down":return{x:1-Z,y:1-Q};default:return{x:Z,y:Q}}}function i7($,Z){switch($){case"landscape_left":switch(Z){case 1:return 3;case 4:return 2;case 2:return 1;case 3:return 4;default:return Z}case"landscape_right":switch(Z){case 1:return 2;case 4:return 3;case 2:return 4;case 3:return 1;default:return Z}case"portrait_upside_down":switch(Z){case 1:return 4;case 4:return 1;case 2:return 3;case 3:return 2;default:return Z}default:return Z}}var v4=()=>{};function a7($){let Z=($.get("codec")??"h264").toLowerCase();if(!vY.has(Z))return{error:`unsupported codec: ${Z} (h264|hevc)`};let Q=$.get("fps"),J=Q==null?bY:Number(Q);if(!Number.isInteger(J)||J<1||J>120)return{error:"fps must be an integer 1..120"};let Y=$.get("bitrate"),X;if(Y!=null){if(X=Number(Y),!Number.isInteger(X)||X<1e5||X>500000000)return{error:"bitrate must be an integer in bits/s (100000..500000000)"}}return{codec:Z,fps:J,...X!=null?{bitrate:X}:{}}}function r7($){return K9({width:$.width,height:$.height,orientation:$.orientation??"portrait"}).rotationDegrees}function W9($=new Date){let Z=(J)=>String(J).padStart(2,"0");return`serve-sim-recording-${[$.getFullYear(),Z($.getMonth()+1),Z($.getDate()),Z($.getHours()),Z($.getMinutes()),Z($.getSeconds())].join("-")}.mp4`}function s7($,Z){if(!Z($))return $;let Q=$.lastIndexOf("."),[J,Y]=Q>0?[$.slice(0,Q),$.slice(Q)]:[$,""];for(let X=2;;X++){let G=`${J}-${X}${Y}`;if(!Z(G))return G}}function V9($,Z){let Q;try{Q=new URL($)}catch{return null}if(!Q.pathname.endsWith("/ws"))return null;return Q.protocol=Q.protocol==="wss:"?"https:":"http:",Q.pathname=Q.pathname.slice(0,-2)+(Z?`recording/${Z}`:"recording"),Q.search="",Q.toString()}function t7($,Z){if(!$)return"recording";if(!$.recording)return"stopped-elsewhere";if(Z&&$.startedAt&&$.startedAt!==Z)return"stopped-elsewhere";return $.limitReached?"limit":"recording"}function e7($){let Z=/^\s*(\d+(?:\.\d+)?)\s*(ms|s|m)?\s*$/.exec($);if(!Z)return null;let Q=Number(Z[1]),J=Z[2]??"s",Y=J==="ms"?Q:J==="m"?Q*60000:Q*1000;return Y>0&&Number.isFinite(Y)?Math.round(Y):null}function $Z($){return Math.min($,g0)}var z9="X-Serve-Sim-Recording",bY=60,g0=1800000,o7=300000,vY;var h4=n(()=>{v4();vY=new Set(["h264","hevc"])});import{createHash as lY}from"crypto";import{existsSync as zZ,readFileSync as WZ}from"fs";import{join as u4}from"path";function V8($){return u4(e$,`${$}.pid`)}function B9($){return u4(e$,`${$}.bundles.json`)}function H9($){return`/tmp/serve-sim-cam-${lY("sha1").update($).digest("hex").slice(0,12)}.sock`}function dY($){try{return process.kill($,0),!0}catch{return!1}}function m4($){try{let Z=Number(WZ(V8($),"utf-8").trim());return Number.isInteger(Z)&&Z>0?Z:null}catch{return null}}function nY($){if(!$||typeof $!=="object"||Array.isArray($))throw Error("invalid camera helper reply");return $}async function u0($,Z){let Q=H9($);if(!zZ(Q))throw Error("camera helper socket not found");let J=await import("net");return new Promise((Y,X)=>{let G=J.createConnection(Q);G.setEncoding("utf8");let z="",V=!1,W,K=(B,M)=>{if(V)return;if(V=!0,W)clearTimeout(W);if(B)X(B);else Y(M??{})};G.on("data",(B)=>{z+=B;let M=z.indexOf(`
|
|
51
51
|
`);if(M<0)return;try{K(void 0,nY(JSON.parse(z.slice(0,M))))}catch(U){K(U)}G.end()}),G.on("error",K),G.on("close",()=>K(Error("socket closed"))),W=setTimeout(()=>{G.destroy(),K(Error("helper timeout"))},pY),G.write(JSON.stringify(Z)+`
|
|
52
|
-
`)})}function B8($){let Z=m4($);return Z!==null&&dY(Z)&&zZ(H9($))}function M9($){let Z;try{let J=JSON.parse(WZ(B9($),"utf-8"));if(!J||typeof J!=="object"||Array.isArray(J))return[];let Y=J;if(typeof Y.helperPid!=="number"||!Array.isArray(Y.bundleIds))return[];Z={helperPid:Y.helperPid,bundleIds:Y.bundleIds}}catch{return[]}let Q=m4($);if(Q===null||Z.helperPid!==Q)return[];return Z.bundleIds.filter((J)=>typeof J==="string")}async function L9($){if(!B8($))return{udid:$,alive:!1};let Z=m4($),Q=M9($);try{return{...await u0($,{action:"status"}),udid:$,alive:!0,helperPid:Z,bundleIds:Q}}catch(J){return{udid:$,alive:!0,helperPid:Z,bundleIds:Q,error:J instanceof Error?J.message:String(J)}}}var e$,pY=3000;var c4=n(()=>{e0();e$=u4(x$,"simcam")});function iY($){return`'${$.replace(/'/g,"'\\''")}'`}function l4($){return $.map(iY).join(" ")}function VZ($){return $.length===1?$[0]:l4($)}function LZ($){let Z="prefix",Q=Buffer.allocUnsafe(4),J=Q,Y=0,X;return{push(G){let z=0;while(z<G.length){let V=Math.min(G.length-z,J.length-Y);if(G.copy(J,Y,z,z+V),z+=V,Y+=V,Y!==J.length)continue;if(Y=0,Z==="prefix"){let W=Q.readUInt32BE(0);if(W<1||W>65536)throw Error("Invalid Duo render header");J=Buffer.allocUnsafe(W),Z="header"}else if(Z==="header"){let{jpegLength:W,...K}=JSON.parse(J.toString());if(!Number.isInteger(W)||W<1||W>32000000)throw Error("Invalid Duo render frame");X=K,J=Buffer.allocUnsafe(W),Z="frame"}else{let W=J;J=Q,Z="prefix",$({jpeg:W,projection:X})}}}}}import{homedir as aY}from"node:os";function C$($,Z=aY()){if(!Z||Z==="/")return $;let Q=Z.replace(/\/+$/,"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),J=new RegExp(`${Q}(?![A-Za-z0-9_-])(?!\\.[A-Za-z0-9_-])`,"g"),Y=(X)=>{if(typeof X==="string")return X.replace(J,"~");if(Array.isArray(X))return X.map(Y);if(X&&typeof X==="object")return Object.fromEntries(Object.entries(X).map(([G,z])=>[G,Y(z)]));return X};return Y($)}var H8=()=>{};import{spawn as rY,execFileSync as sY}from"node:child_process";import{existsSync as jZ}from"node:fs";import{dirname as NZ,join as M8}from"node:path";import{fileURLToPath as tY}from"node:url";function p4($){return M8($,"..",eY)}function d4($=process.env,Z=()=>sY("/usr/bin/xcode-select",["-p"],{encoding:"utf8",timeout:5000}).trim(),Q=jZ){let J=$.DEVELOPER_DIR?"DEVELOPER_DIR":"xcode-select",Y;try{Y=$.DEVELOPER_DIR??Z()}catch(G){return{developerDir:null,source:J,modelPath:null,exists:!1,error:G instanceof Error?G.message:String(G)}}let X=p4(Y);return{developerDir:Y,source:J,modelPath:X,exists:Q(X)}}function n4(){let $=M8("simduo","serve-sim-duo-render"),Z=NZ(tY(import.meta.url));return[M8(NZ(process.execPath),$),M8(Z,$),M8(Z,"..","dist",$)].find(jZ)??null}function RZ($,Z=console.error){let Q=`${$.stage}\x00${$.reason}`;if(UZ.has(Q))return!1;UZ.add(Q);let J=C$($.details),Y=Object.entries(J).filter(([,X])=>X!=null&&X!=="").map(([X,G])=>`${X}=${JSON.stringify(G)}`).join(" ");return Z(`[duo-renderer] ${$.stage} failed: ${C$($.reason)}${Y?` (${Y})`:""}`),!0}class i4{onFailure;child;pending;closed=!1;stderrTail="";modelPath;constructor($){this.onFailure=$;let Z=n4();if(!Z)throw new M$("worker_missing","Duo renderer is missing. Rebuild serve-sim.");let Q=d4();if(!Q.developerDir||!Q.modelPath)throw new M$("developer_dir","Could not resolve the selected Xcode (xcode-select -p failed).",{source:Q.source,error:Q.error});if(!Q.exists)throw new M$("model_lookup","The selected Xcode does not include the iPhone Duo 3D model.",{model_path:Q.modelPath,developer_dir:Q.developerDir,source:Q.source});this.modelPath=Q.modelPath,this.child=rY(Z,[Q.modelPath],{stdio:"pipe"}),this.child.stderr.on("data",(Y)=>{let X=Y.toString();this.stderrTail=(this.stderrTail+X).slice(-$X),console.error("[duo-renderer]",X.trim())}),this.child.on("error",(Y)=>this.fail(new M$("worker_spawn",`Duo renderer could not start: ${Y.message}`,{executable:Z,code:Y.code,model_path:this.modelPath}))),this.child.stdin.on("error",(Y)=>this.fail(new M$("worker_exit",`Duo renderer input closed: ${Y.message}`,this.exitDetails()))),this.child.on("exit",(Y,X)=>this.fail(new M$("worker_exit","Duo renderer exited",this.exitDetails(Y,X))));let J=LZ((Y)=>{let X=this.pending;if(this.pending=void 0,X)clearTimeout(X.timer),X.resolve(Y)});this.child.stdout.on("data",(Y)=>{try{J.push(Y)}catch(X){this.fail(new M$("protocol",`Duo renderer sent a malformed frame: ${X instanceof Error?X.message:String(X)}`,this.exitDetails()))}})}render($,Z,Q,J,Y=!1){if(this.closed||this.pending)return Promise.reject(Error("Duo renderer is unavailable or busy"));return new Promise((X,G)=>{let z=setTimeout(()=>this.fail(new M$("timeout","Duo renderer timed out after 10s",{...this.exitDetails(),jpeg_bytes:$.length,panel:Z,hinge_degrees:Q})),1e4);this.pending={resolve:X,reject:G,timer:z};let V=Buffer.from(JSON.stringify({jpegLength:$.length,panel:Z,hingeDegrees:Q,rollDegrees:J,fullResolution:Y})),W=Buffer.alloc(4);W.writeUInt32BE(V.length),this.child.stdin.write(Buffer.concat([W,V,$]))})}exitDetails($,Z){return{model_path:this.modelPath,...$!==void 0?{exit_code:$}:{},...Z!==void 0?{signal:Z}:{},...this.stderrTail?{stderr_tail:this.stderrTail.trim()}:{}}}fail($){if(this.closed)return;let Z=this.pending;if(this.pending=void 0,Z)clearTimeout(Z.timer),Z.reject($);this.close(),this.onFailure?.($)}close(){if(this.closed)return;this.closed=!0,this.child.stdin.end(),this.child.kill();let $=this.pending;if(this.pending=void 0,$)clearTimeout($.timer),$.reject(Error("Duo renderer closed"))}}var M$,eY="SharedFrameworks/DeviceKit.framework/Versions/A/PlugIns/CoreDevicePopDeviceKitExtension.devicekitplugin/Contents/Resources/V68.usdz",$X=2048,UZ;var N9=n(()=>{H8();M$=class M$ extends Error{stage;reason;details;constructor($,Z,Q={}){super(Z);this.stage=$;this.reason=Z;this.details=Q;this.name="DuoRendererError"}};UZ=new Set});var FZ={};t9(FZ,{warnDeviceHubInput:()=>a4,repairDeviceHubInput:()=>YX,readDeviceHubInputShadowed:()=>r4,isDeviceHubInputShadowed:()=>U9});import{execFile as ZX}from"node:child_process";import{promisify as QX}from"node:util";function U9($){return $.trim()===`${L8} 1`}async function a4($,Z=o4){try{let Q=U9(await Z(c(["spawn",$,"notifyutil","-g",L8])));if(Q)console.warn(`[hid] Device Hub has disconnected legacy input. Run serve-sim repair-input -d ${$} before using touch or keyboard. Repair restarts SpringBoard and closes running apps.`);return Q}catch{return}}async function r4($,Z=o4){try{return U9(await Z(c(["spawn",$,"notifyutil","-g",L8])))}catch{return null}}async function YX($,Z=o4){let Q=c(["spawn",$]);if(!U9(await Z([...Q,"notifyutil","-g",L8])))return!1;return await Z([...Q,"notifyutil","-s",L8,"0"]),await Z([...Q,"launchctl","kickstart","-k","system/com.apple.backboardd"]),!0}var JX,o4=async($)=>(await JX("/usr/bin/xcrun",$,{timeout:5000})).stdout,L8="com.apple.coredevice.dtuhidd.active";var j9=n(()=>{T$();JX=QX(ZX)});import{execFile as XX}from"node:child_process";import{closeSync as GX,existsSync as KX,openSync as zX,readdirSync as _Z,readFileSync as WX,readSync as VX,statSync as BX}from"node:fs";import{homedir as HX}from"node:os";import{dirname as MX,join as $0}from"node:path";import{fileURLToPath as LX}from"node:url";function CZ($){let Z="";try{let Q=new URL($.streamUrl).pathname,J=Q.lastIndexOf("/helper/");if(J>0)Z=Q.slice(0,J)}catch{}return new URL(`${Z}/api/diagnostics`,new URL($.url).origin)}function UX(){return"0.1.
|
|
52
|
+
`)})}function B8($){let Z=m4($);return Z!==null&&dY(Z)&&zZ(H9($))}function M9($){let Z;try{let J=JSON.parse(WZ(B9($),"utf-8"));if(!J||typeof J!=="object"||Array.isArray(J))return[];let Y=J;if(typeof Y.helperPid!=="number"||!Array.isArray(Y.bundleIds))return[];Z={helperPid:Y.helperPid,bundleIds:Y.bundleIds}}catch{return[]}let Q=m4($);if(Q===null||Z.helperPid!==Q)return[];return Z.bundleIds.filter((J)=>typeof J==="string")}async function L9($){if(!B8($))return{udid:$,alive:!1};let Z=m4($),Q=M9($);try{return{...await u0($,{action:"status"}),udid:$,alive:!0,helperPid:Z,bundleIds:Q}}catch(J){return{udid:$,alive:!0,helperPid:Z,bundleIds:Q,error:J instanceof Error?J.message:String(J)}}}var e$,pY=3000;var c4=n(()=>{e0();e$=u4(x$,"simcam")});function iY($){return`'${$.replace(/'/g,"'\\''")}'`}function l4($){return $.map(iY).join(" ")}function VZ($){return $.length===1?$[0]:l4($)}function LZ($){let Z="prefix",Q=Buffer.allocUnsafe(4),J=Q,Y=0,X;return{push(G){let z=0;while(z<G.length){let V=Math.min(G.length-z,J.length-Y);if(G.copy(J,Y,z,z+V),z+=V,Y+=V,Y!==J.length)continue;if(Y=0,Z==="prefix"){let W=Q.readUInt32BE(0);if(W<1||W>65536)throw Error("Invalid Duo render header");J=Buffer.allocUnsafe(W),Z="header"}else if(Z==="header"){let{jpegLength:W,...K}=JSON.parse(J.toString());if(!Number.isInteger(W)||W<1||W>32000000)throw Error("Invalid Duo render frame");X=K,J=Buffer.allocUnsafe(W),Z="frame"}else{let W=J;J=Q,Z="prefix",$({jpeg:W,projection:X})}}}}}import{homedir as aY}from"node:os";function C$($,Z=aY()){if(!Z||Z==="/")return $;let Q=Z.replace(/\/+$/,"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),J=new RegExp(`${Q}(?![A-Za-z0-9_-])(?!\\.[A-Za-z0-9_-])`,"g"),Y=(X)=>{if(typeof X==="string")return X.replace(J,"~");if(Array.isArray(X))return X.map(Y);if(X&&typeof X==="object")return Object.fromEntries(Object.entries(X).map(([G,z])=>[G,Y(z)]));return X};return Y($)}var H8=()=>{};import{spawn as rY,execFileSync as sY}from"node:child_process";import{existsSync as jZ}from"node:fs";import{dirname as NZ,join as M8}from"node:path";import{fileURLToPath as tY}from"node:url";function p4($){return M8($,"..",eY)}function d4($=process.env,Z=()=>sY("/usr/bin/xcode-select",["-p"],{encoding:"utf8",timeout:5000}).trim(),Q=jZ){let J=$.DEVELOPER_DIR?"DEVELOPER_DIR":"xcode-select",Y;try{Y=$.DEVELOPER_DIR??Z()}catch(G){return{developerDir:null,source:J,modelPath:null,exists:!1,error:G instanceof Error?G.message:String(G)}}let X=p4(Y);return{developerDir:Y,source:J,modelPath:X,exists:Q(X)}}function n4(){let $=M8("simduo","serve-sim-duo-render"),Z=NZ(tY(import.meta.url));return[M8(NZ(process.execPath),$),M8(Z,$),M8(Z,"..","dist",$)].find(jZ)??null}function RZ($,Z=console.error){let Q=`${$.stage}\x00${$.reason}`;if(UZ.has(Q))return!1;UZ.add(Q);let J=C$($.details),Y=Object.entries(J).filter(([,X])=>X!=null&&X!=="").map(([X,G])=>`${X}=${JSON.stringify(G)}`).join(" ");return Z(`[duo-renderer] ${$.stage} failed: ${C$($.reason)}${Y?` (${Y})`:""}`),!0}class i4{onFailure;child;pending;closed=!1;stderrTail="";modelPath;constructor($){this.onFailure=$;let Z=n4();if(!Z)throw new M$("worker_missing","Duo renderer is missing. Rebuild serve-sim.");let Q=d4();if(!Q.developerDir||!Q.modelPath)throw new M$("developer_dir","Could not resolve the selected Xcode (xcode-select -p failed).",{source:Q.source,error:Q.error});if(!Q.exists)throw new M$("model_lookup","The selected Xcode does not include the iPhone Duo 3D model.",{model_path:Q.modelPath,developer_dir:Q.developerDir,source:Q.source});this.modelPath=Q.modelPath,this.child=rY(Z,[Q.modelPath],{stdio:"pipe"}),this.child.stderr.on("data",(Y)=>{let X=Y.toString();this.stderrTail=(this.stderrTail+X).slice(-$X),console.error("[duo-renderer]",X.trim())}),this.child.on("error",(Y)=>this.fail(new M$("worker_spawn",`Duo renderer could not start: ${Y.message}`,{executable:Z,code:Y.code,model_path:this.modelPath}))),this.child.stdin.on("error",(Y)=>this.fail(new M$("worker_exit",`Duo renderer input closed: ${Y.message}`,this.exitDetails()))),this.child.on("exit",(Y,X)=>this.fail(new M$("worker_exit","Duo renderer exited",this.exitDetails(Y,X))));let J=LZ((Y)=>{let X=this.pending;if(this.pending=void 0,X)clearTimeout(X.timer),X.resolve(Y)});this.child.stdout.on("data",(Y)=>{try{J.push(Y)}catch(X){this.fail(new M$("protocol",`Duo renderer sent a malformed frame: ${X instanceof Error?X.message:String(X)}`,this.exitDetails()))}})}render($,Z,Q,J,Y=!1){if(this.closed||this.pending)return Promise.reject(Error("Duo renderer is unavailable or busy"));return new Promise((X,G)=>{let z=setTimeout(()=>this.fail(new M$("timeout","Duo renderer timed out after 10s",{...this.exitDetails(),jpeg_bytes:$.length,panel:Z,hinge_degrees:Q})),1e4);this.pending={resolve:X,reject:G,timer:z};let V=Buffer.from(JSON.stringify({jpegLength:$.length,panel:Z,hingeDegrees:Q,rollDegrees:J,fullResolution:Y})),W=Buffer.alloc(4);W.writeUInt32BE(V.length),this.child.stdin.write(Buffer.concat([W,V,$]))})}exitDetails($,Z){return{model_path:this.modelPath,...$!==void 0?{exit_code:$}:{},...Z!==void 0?{signal:Z}:{},...this.stderrTail?{stderr_tail:this.stderrTail.trim()}:{}}}fail($){if(this.closed)return;let Z=this.pending;if(this.pending=void 0,Z)clearTimeout(Z.timer),Z.reject($);this.close(),this.onFailure?.($)}close(){if(this.closed)return;this.closed=!0,this.child.stdin.end(),this.child.kill();let $=this.pending;if(this.pending=void 0,$)clearTimeout($.timer),$.reject(Error("Duo renderer closed"))}}var M$,eY="SharedFrameworks/DeviceKit.framework/Versions/A/PlugIns/CoreDevicePopDeviceKitExtension.devicekitplugin/Contents/Resources/V68.usdz",$X=2048,UZ;var N9=n(()=>{H8();M$=class M$ extends Error{stage;reason;details;constructor($,Z,Q={}){super(Z);this.stage=$;this.reason=Z;this.details=Q;this.name="DuoRendererError"}};UZ=new Set});var FZ={};t9(FZ,{warnDeviceHubInput:()=>a4,repairDeviceHubInput:()=>YX,readDeviceHubInputShadowed:()=>r4,isDeviceHubInputShadowed:()=>U9});import{execFile as ZX}from"node:child_process";import{promisify as QX}from"node:util";function U9($){return $.trim()===`${L8} 1`}async function a4($,Z=o4){try{let Q=U9(await Z(c(["spawn",$,"notifyutil","-g",L8])));if(Q)console.warn(`[hid] Device Hub has disconnected legacy input. Run serve-sim repair-input -d ${$} before using touch or keyboard. Repair restarts SpringBoard and closes running apps.`);return Q}catch{return}}async function r4($,Z=o4){try{return U9(await Z(c(["spawn",$,"notifyutil","-g",L8])))}catch{return null}}async function YX($,Z=o4){let Q=c(["spawn",$]);if(!U9(await Z([...Q,"notifyutil","-g",L8])))return!1;return await Z([...Q,"notifyutil","-s",L8,"0"]),await Z([...Q,"launchctl","kickstart","-k","system/com.apple.backboardd"]),!0}var JX,o4=async($)=>(await JX("/usr/bin/xcrun",$,{timeout:5000})).stdout,L8="com.apple.coredevice.dtuhidd.active";var j9=n(()=>{T$();JX=QX(ZX)});import{execFile as XX}from"node:child_process";import{closeSync as GX,existsSync as KX,openSync as zX,readdirSync as _Z,readFileSync as WX,readSync as VX,statSync as BX}from"node:fs";import{homedir as HX}from"node:os";import{dirname as MX,join as $0}from"node:path";import{fileURLToPath as LX}from"node:url";function CZ($){let Z="";try{let Q=new URL($.streamUrl).pathname,J=Q.lastIndexOf("/helper/");if(J>0)Z=Q.slice(0,J)}catch{}return new URL(`${Z}/api/diagnostics`,new URL($.url).origin)}function UX(){return"0.1.50-beta.144.1"}function jX($){let Z=/^(.*?\.app)\/Contents\/Developer\/?$/.exec($);return Z?Z[1]:null}async function RX($){let Z=new Set,Q=await $("/usr/bin/mdfind",["kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode'"]);for(let J of Q?.split(`
|
|
53
53
|
`)??[])if(J.trim().endsWith(".app"))Z.add(J.trim());try{for(let J of _Z("/Applications"))if(/^Xcode.*\.app$/.test(J))Z.add($0("/Applications",J))}catch{}return[...Z].sort()}async function R9($={}){let Z=$.probe??TZ,Q=$.env??process.env,J=$.exists??KX,[Y,X,G,z,V,W]=await Promise.all([Z("/usr/bin/sw_vers",["-productVersion"]),Z("/usr/bin/sw_vers",["-buildVersion"]),Z("/usr/sbin/sysctl",["-n","machdep.cpu.brand_string"]),Z("/usr/bin/xcode-select",["-p"]),Z("/usr/bin/xcodebuild",["-version"]),Z("/bin/ps",["-axo","pid=,command="])]),K=Q.DEVELOPER_DIR??z,B=K?jX(K):null,M=await($.listXcodes?$.listXcodes():RX(Z)),U=await Promise.all(M.map(async(P)=>{let O=await Z("/usr/bin/defaults",["read",$0(P,"Contents","Info"),"CFBundleShortVersionString"]),_=await Z("/usr/bin/defaults",["read",$0(P,"Contents","version"),"ProductBuildVersion"]);return{path:P,version:O,build:_,selected:P===B,has_device_hub:J($0(P,"Contents","Applications","DeviceHub.app")),has_duo_model:J(p4($0(P,"Contents","Developer")))}})),F=(W??"").split(`
|
|
54
54
|
`).map((P)=>/^\s*(\d+)\s+(.*\/DeviceHub\.app\/Contents\/MacOS\/\S+)/.exec(P)).filter((P)=>P!=null).map((P)=>{let O=P[2],_=/^(.*?\.app)\/Contents\/Applications\/DeviceHub\.app\//.exec(O)?.[1]??null;return{pid:Number(P[1]),executable:O,xcode:_,matches_selected:_!=null&&_===B}}),T=d4(Q,()=>{if(!z)throw Error("xcode-select -p failed");return z},J);return{macos:{version:Y,build:X},chip:G,arch:process.arch,node:process.version,xcode:{xcode_select:z,developer_dir_env:Q.DEVELOPER_DIR??null,effective_developer_dir:K,xcodebuild_version:V,installed:U},device_hub:{processes:F},duo_renderer:{executable:n4(),model_path:T.modelPath,model_source:T.source,model_found:T.exists,...T.error?{model_error:T.error}:{}}}}async function F9($,Z=TZ){let Q=await Z("/usr/bin/xcrun",c(["list","devices","-j"])),J=null;try{let Y=JSON.parse(Q??"{}");for(let[X,G]of Object.entries(Y.devices??{})){let z=G.find((V)=>V.udid===$);if(z){J={name:z.name,state:z.state,runtime:X,device_type:z.deviceTypeIdentifier??null};break}}}catch{}return{udid:$,...J??{error:"not found in simctl list"},device_hub_input_shadowed:await r4($,async(Y)=>{let X=await Z("/usr/bin/xcrun",Y);if(X==null)throw Error("probe failed");return X})}}function TX($,Z){let Q=zX($,"r");try{let J=Buffer.alloc(Z);return J.toString("utf8",0,VX(Q,J,0,Z,0))}finally{GX(Q)}}function OZ($={}){let Z=$.dir??$0(HX(),"Library","Logs","DiagnosticReports"),Q=($.now??Date.now)()-($.maxAgeMs??FX),J;try{J=_Z(Z).filter((G)=>G.endsWith(".ips"))}catch{return{directory:Z,recent:[]}}let Y=J.flatMap((G)=>{let z=$0(Z,G);try{let V=BX(z).mtimeMs;return V>=Q?[{file:G,path:z,mtime:V}]:[]}catch{return[]}}).sort((G,z)=>z.mtime-G.mtime),X=[];for(let G of Y){if(X.length>=($.limit??5))break;let z;try{z=TX(G.path,65536)}catch{continue}z=z.replace(/\\\//g,"/");let V=Number(/"platform"\s*:\s*(\d+)/.exec(z)?.[1]);if(!CX.has(V)&&!_X.test(z))continue;X.push({file:G.file,path:G.path,modified_at:new Date(G.mtime).toISOString(),process:/"app_name"\s*:\s*"([^"]+)"/.exec(z)?.[1]??/"procName"\s*:\s*"([^"]+)"/.exec(z)?.[1]??null,udid:/CoreSimulator\/Devices\/([0-9A-Fa-f-]{36})\//.exec(z)?.[1]??null})}return{directory:Z,recent:X}}function qZ($){return!!$&&typeof $==="object"&&!Array.isArray($)}function OX($){if(!qZ($))return $;let Z={...$};if(Array.isArray($.errors)){let Q=new Map;for(let J of $.errors){let Y=Q.get(J.codec);if(!Y){Q.set(J.codec,{...J});continue}let X=J.last_at>Y.last_at?J:Y;Q.set(J.codec,{...X,count:Y.count+J.count,first_at:J.first_at<Y.first_at?J.first_at:Y.first_at,last_at:X.last_at,variants:(Y.variants??1)+1})}Z.errors=[...Q.values()].sort((J,Y)=>J.last_at<Y.last_at?1:J.last_at>Y.last_at?-1:0)}if(Array.isArray($.sessions)){let Q=new Map;for(let J of $.sessions){let Y=Q.get(J.codec),X=(Y?.session_count??0)+1;Q.set(J.codec,{...!Y||J.at>=Y.at?J:Y,session_count:X})}Z.sessions=[...Q.values()].sort((J,Y)=>J.at<Y.at?-1:J.at>Y.at?1:0)}return Z}function qX($){let Z=$.native;if(!qZ(Z)||!("encoders"in Z))return $;return{...$,native:{...Z,encoders:OX(Z.encoders)}}}function _9($){let Z={schema_version:NX,generated_at:($.now??new Date).toISOString(),serve_sim:{version:UX(),pid:process.pid},host:$.host,devices:$.devices??[],sessions:($.sessions??[]).map(qX),event_log:$.event_log??[],...$.server?{server:$.server}:{},...$.crash_reports?{crash_reports:$.crash_reports}:{}};return C$(Z,$.home)}function AX($){let Z=[],Q=$.host,J=Q.device_hub?.processes??[];if(J.some((X)=>!X.matches_selected))Z.push(`Device Hub runs from ${J.map((X)=>X.xcode??"?").join(", ")}, not the selected Xcode (${Q.xcode?.effective_developer_dir??"unknown"}).`);let Y=$.sessions.filter((X)=>X.duo!=null);if(Q.duo_renderer?.model_found===!1&&Y.length>0)Z.push(`Selected Xcode has no iPhone Duo 3D model (${Q.duo_renderer.model_path??"no path"}).`);for(let X of $.sessions){let G=X.streams;for(let[V,W]of Object.entries(G??{}))if(W.last_error)Z.push(`${V} stream for ${String(X.udid).slice(0,8)}: ${W.last_error.stage?`${W.last_error.stage}: `:""}${W.last_error.message}`);let z=X.native?.encoders?.errors??[];for(let V of z)Z.push(`${V.codec} encoder: ${V.error} at ${V.width}x${V.height} (${V.count}x)`)}return Z}function AZ($){let Z=$.host,Q=[`serve-sim ${$.serve_sim.version} macOS ${Z.macos?.version??"?"} ${Z.chip??Z.arch??""}`.trim(),`xcode-select -p: ${Z.xcode?.xcode_select??"(failed)"}${Z.xcode?.developer_dir_env?` DEVELOPER_DIR=${Z.xcode.developer_dir_env}`:""}`,`xcodebuild: ${(Z.xcode?.xcodebuild_version??"(failed)").replace(/\n/g," ")}`,"Xcodes:",...(Z.xcode?.installed??[]).map((Y)=>` ${Y.selected?"*":" "} ${Y.path} ${Y.version??""} Duo model: ${Y.has_duo_model?"yes":"no"}`),`Device Hub: ${(Z.device_hub?.processes??[]).map((Y)=>`${Y.executable}${Y.matches_selected?"":" (not selected Xcode)"}`).join(", ")||"not running"}`,`Duo model: ${Z.duo_renderer?.model_found?"found":"missing"} at ${Z.duo_renderer?.model_path??"?"}`];for(let Y of $.sessions){let X=Y.streams;Q.push(`Session ${Y.udid}:`);for(let[V,W]of Object.entries(X??{}))Q.push(` ${V.padEnd(5)} open=${W.open} frames=${W.frames} last=${W.last_frame_at??"-"}${W.last_error?` error=${W.last_error.message}`:""}`);let G=Y.native?.capture;if(G)Q.push(` displays ${(G.native_displays??[]).map((V)=>`${V.width}x${V.height}`).join(", ")}; capturing ${G.captured?.width}x${G.captured?.height} (${G.selection_reason})`);let z=Y.duo;if(z)Q.push(` duo renderer=${z.renderer} hinge=${z.hinge_degrees??"unknown"} panel=${z.active_panel}${z.last_error?` last_error=${z.last_error.stage}: ${z.last_error.reason}`:""}`)}if($.crash_reports){if(Q.push(`Simulator crash reports (.ips): ${$.crash_reports.directory}`),$.crash_reports.recent.length===0)Q.push(" none in the last 7 days");for(let Y of $.crash_reports.recent)Q.push(` ${Y.modified_at} ${Y.process??"?"}${Y.udid?` (${Y.udid.slice(0,8)})`:""} ${Y.path}`)}if($.server&&!$.server.reachable)Q.push(`Server: not reachable (${$.server.error??"not running"}); live stream state omitted.`);let J=AX($);if(J.length)Q.push("Findings:",...J.map((Y)=>` - ${Y}`));return Q.join(`
|
|
55
55
|
`)}function PZ($){let Z=$.now??Date.now,Q=$.burst,J=Z();return{take(){let Y=Z();if(Q=Math.min($.burst,Q+(Y-J)/$.refillMs),J=Y,Q>=1)return Q-=1,{ok:!0};return{ok:!1,retryAfterMs:Math.ceil((1-Q)*$.refillMs)}}}}function DZ($,Z=Date.now){let Q=new Map;return{get(J,Y){let X=Z(),G=Q.get(J);if(G&&X-G.at<$)return G.value;for(let[W,K]of Q)if(X-K.at>=$)Q.delete(W);let z=Y(),V={at:X,value:z};return Q.set(J,V),z.catch(()=>{if(Q.get(J)===V)Q.delete(J)}),z}}}var NX=1,TZ=($,Z)=>new Promise((Q)=>{XX($,Z,{timeout:5000,maxBuffer:4194304},(J,Y)=>{Q(J?null:String(Y).trim())})}),FX=604800000,_X,CX;var s4=n(()=>{N9();T$();j9();H8();_X=/CoreSimulator\/Devices\/|\.simruntime\/|iPhoneSimulator\.platform\//,CX=new Set([7,8,9,12])});var t4="Accessibility unavailable on this simulator.";function EZ($){return Number.isInteger($)&&$>=224&&$<=231}function e4($,Z,Q){return new Promise((J,Y)=>{let X=setTimeout(()=>Y(Error(`${Q} timed out`)),Z);$.then(()=>{clearTimeout(X),J()},(G)=>{clearTimeout(X),Y(G)})})}class $6{sink;timeoutMs;queue=Promise.resolve();heldModifiers=new Set;closing=!1;discardQueued=!1;closeAttempt;constructor($,Z=250){this.sink=$;this.timeoutMs=Z}key($,Z){if(this.closing)return Promise.resolve();let Q=this.queue.then(async()=>{if(this.discardQueued&&$==="down")return;let J=EZ(Z);if(J&&$==="down")this.heldModifiers.add(Z);try{if(await this.sink.key($,Z),J&&$==="up")this.heldModifiers.delete(Z)}finally{if(this.discardQueued&&J&&$==="down")this.heldModifiers.add(Z),await this.releaseModifier(Z)}});return this.queue=Q.catch(()=>{}),Q}drainAndRelease(){if(this.closeAttempt)return this.closeAttempt;this.closing=!0;let $=this.finishClose();return this.closeAttempt=$,$.catch(()=>{if(this.closeAttempt===$)this.closeAttempt=void 0}),$}async releaseModifier($){let Z;for(let Q=0;Q<2;Q++)try{await e4(Promise.resolve().then(()=>this.sink.key("up",$)),this.timeoutMs,"Modifier release"),this.heldModifiers.delete($);return}catch(J){Z=J}throw Z}async finishClose(){let $=[];try{await e4(this.queue,this.timeoutMs,"Keyboard drain")}catch(Q){this.discardQueued=!0,$.push(Q)}let Z=await Promise.allSettled([...this.heldModifiers].map((Q)=>this.releaseModifier(Q)));for(let Q of Z)if(Q.status==="rejected")$.push(Q.reason);try{await e4(Promise.resolve().then(()=>this.sink.close()),this.timeoutMs,"Native keyboard close")}catch(Q){$.push(Q)}if($.length)throw AggregateError($,"Keyboard teardown incomplete")}}var IZ=()=>{};import{createRequire as PX}from"module";import{dirname as T9,join as O9}from"path";import{existsSync as DX}from"fs";import{fileURLToPath as xZ}from"url";function wZ(){let $=[O9(T9(process.execPath),"native","serve-sim-native.node"),O9(T9(xZ(import.meta.url)),"native","serve-sim-native.node"),O9(T9(xZ(import.meta.url)),"..","dist","native","serve-sim-native.node")];for(let Z of $)if(DX(Z))return Z;throw Error(`serve-sim-native.node not found. Looked in:
|
|
@@ -185,7 +185,7 @@ Usage:
|
|
|
185
185
|
serve-sim permissions reset <permission|all> <bundle-id> [-d <udid|name>]
|
|
186
186
|
serve-sim permissions list [bundle-id] [-d <udid|name>]
|
|
187
187
|
|
|
188
|
-
Permissions: ${q4().join(", ")}`),process.exit(1);let Y=J.device?U$(J.device):y$();if(!Y)console.error("No booted simulator. Boot one or pass -d <udid|name>."),process.exit(1);if(J.verb==="list"){let G={udid:Y,bundleId:J.bundleId??null,tcc:VY(Y,J.bundleId),location:HY(Y,J.bundleId),notifications:UY(Y,J.bundleId)};console.log(JSON.stringify(G,null,Z?0:2)),process.exit(0)}let X=J.bundleId;try{if(J.permission==="all")for(let G of q4())O7(Y,"reset",G,void 0,X);else O7(Y,J.verb,J.permission,J.value,X)}catch(G){console.error(G?.message??String(G)),process.exit(1)}if(Z)console.log(JSON.stringify({udid:Y,verb:J.verb,permission:J.permission,value:J.value??null,bundleId:X}));else{let G=J.value?` (${J.value})`:"";console.log(`\uD83D\uDD10 ${J.verb} ${J.permission}${G} for ${X} on ${Y}`)}process.exit(0)}X9();e8();function g7($,Z={}){let Q=new Date($.timestamp),J=Number.isNaN(Q.getTime())?$.timestamp:Q.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"}),Y=Z.deviceLabel??($.device?$.device.slice(0,8):null),X=$.status==="error"?" failed":"";return[J,Y,`${DY($)}${X?` (${X.trim()})`:""}`].filter(Boolean).join(" ")}function DY($){if($.kind==="tap"){let Z=G9($.details,"current")??G9($.details,"start");return Z?`Tap at ${f4(Z)}`:"Tap"}if($.kind==="drag"){let Z=G9($.details,"start"),Q=G9($.details,"current");if(Z&&Q)return`Drag from ${f4(Z)} to ${f4(Q)}`;return"Drag"}if($.kind==="key"){let Z=EY($.details,"key")??$.action??"key",Q=$.action==="down"?"down":$.action==="up"?"up":$.action;return Q?`Key ${Q} ${h7(Z)}`:`Key ${h7(Z)}`}if($.kind==="rotate"){let Z=$.action?u7($.action):"";return Z?`Rotate ${Z}`:"Rotate"}return xY($.msg??$.summary)}function G9($,Z){let Q=$?.[Z];if(!Q||typeof Q!=="object"||Array.isArray(Q))return null;let J=Q;return typeof J.x==="number"&&typeof J.y==="number"?{x:J.x,y:J.y}:null}function EY($,Z){let Q=$?.[Z];return typeof Q==="string"&&Q.length>0?Q:null}function f4($){return`${v7($.x)}, ${v7($.y)}`}function v7($){if(!Number.isFinite($))return"?";return(Math.round((IY($,0,1)+Number.EPSILON)*100)/100).toFixed(2)}function IY($,Z,Q){return Math.min(Q,Math.max(Z,$))}function xY($){return $.replace(/\b[a-z]+(?:_[a-z]+)+\b/g,u7)}function u7($){return $.replace(/[-_]+/g," ")}function h7($){return{MetaLeft:"Left Command",MetaRight:"Right Command",AltLeft:"Left Option",AltRight:"Right Option",ControlLeft:"Left Control",ControlRight:"Right Control",ShiftLeft:"Left Shift",ShiftRight:"Right Shift",ArrowLeft:"Left Arrow",ArrowRight:"Right Arrow",ArrowUp:"Up Arrow",ArrowDown:"Down Arrow"}[$]??$}$8();var wY=new Set(["","0.0.0.0","::","::0"]);function kY($){return $.startsWith("[")&&$.endsWith("]")?$.slice(1,-1):$}function p7($){return $.includes(":")?`[${$}]`:$}function d7($){if(!$.exposedToLan)return"<host>";let Z=kY($.host.trim());if(wY.has(Z))return $.networkIP?p7($.networkIP):"<host>";return p7(Z)}h4();import{once as ZZ}from"events";import{createWriteStream as hY}from"fs";import{rm as gY}from"fs/promises";import{pipeline as uY}from"stream/promises";async function QZ($,Z,{exclusive:Q,onOpen:J}){let Y=hY(Z,{flags:Q?"wx":"w"});try{await ZZ(Y,"open")}catch(X){throw await $.cancel().catch(()=>{}),X}try{J?.(),await uY(({signal:X}={})=>mY($,X),Y)}catch(X){if(!Y.closed){let G=ZZ(Y,"close");Y.destroy(),await G.catch(()=>{})}throw await $.cancel().catch(()=>{}),await gY(Z,{force:!0}).catch(()=>{}),X}return Y.bytesWritten}async function*mY($,Z){let Q=$.getReader(),J=!1,Y=()=>{if(J)return;J=!0,Q.cancel(Z?.reason).catch(()=>{})};Z?.addEventListener("abort",Y,{once:!0});try{if(Z?.aborted)Y();while(!0){let X=await Q.read();if(Z?.throwIfAborted(),X.done){J=!0;return}yield X.value}}catch(X){throw J=!0,X}finally{Z?.removeEventListener("abort",Y),Y(),Q.releaseLock()}}v4();var cY=new Set(["portrait","portrait_upside_down","landscape_left","landscape_right"]);function JZ($){if(!$||typeof $!=="object")return;let Z=$;if(typeof Z.width!=="number"||typeof Z.height!=="number")return;if(typeof Z.hingeDegrees==="number")return;let Q=cY.has(Z.orientation)?Z.orientation:void 0;return K9({width:Z.width,height:Z.height,orientation:Q}).inputOrientation}function g4($,Z){let Q=n7(Z,$.x,$.y),J={...$,x:Q.x,y:Q.y};if(typeof $.edge==="number"&&$.edge!==0)J.edge=i7(Z,$.edge);return J}function YZ($){let Z;try{Z=new URL($)}catch{return null}if(!Z.pathname.endsWith("/ws"))return null;return Z.protocol=Z.protocol==="wss:"?"https:":"http:",Z.pathname=Z.pathname.slice(0,-2)+"config",Z.toString()}var XZ=["devices","tools","devtools"];function GZ($){let Z=$.split(",").map((J)=>J.trim().toLowerCase()).filter(Boolean);if(Z.length===1&&Z[0]==="none")return[];if(Z.length===0||Z.includes("none"))throw Error("Expected 'none' or a comma-separated list of: devices, tools, devtools.");let Q=Z.filter((J)=>!XZ.includes(J));if(Q.length>0)throw Error(`Unknown pane${Q.length===1?"":"s"}: ${Q.join(", ")}. Expected: ${XZ.join(", ")}.`);return[...new Set(Z)]}function KZ($){let Z=$.trim().toLowerCase();if(Z==="light"||Z==="dark")return Z;throw Error("Expected simulator theme: light or dark.")}c4();T$();var oY=/(^|\/)serve-sim$/;function BZ($){let[Z,Q]=$.argv;if(Z&&oY.test(Z))return Z;if(Q?.startsWith("/$bunfs/"))return $.execPath;return null}function HZ($,Z=process){let Q=BZ(Z);if(Q)return{command:Q,args:$};return{command:Z.argv[0],args:[Z.argv[1],...$]}}function MZ($=process){return BZ($)??$.argv[1]??"serve-sim"}s4();H8();var z5="SERVE_SIM_URL",K0=p8(import.meta.url);function SV(){return"0.1.
|
|
188
|
+
Permissions: ${q4().join(", ")}`),process.exit(1);let Y=J.device?U$(J.device):y$();if(!Y)console.error("No booted simulator. Boot one or pass -d <udid|name>."),process.exit(1);if(J.verb==="list"){let G={udid:Y,bundleId:J.bundleId??null,tcc:VY(Y,J.bundleId),location:HY(Y,J.bundleId),notifications:UY(Y,J.bundleId)};console.log(JSON.stringify(G,null,Z?0:2)),process.exit(0)}let X=J.bundleId;try{if(J.permission==="all")for(let G of q4())O7(Y,"reset",G,void 0,X);else O7(Y,J.verb,J.permission,J.value,X)}catch(G){console.error(G?.message??String(G)),process.exit(1)}if(Z)console.log(JSON.stringify({udid:Y,verb:J.verb,permission:J.permission,value:J.value??null,bundleId:X}));else{let G=J.value?` (${J.value})`:"";console.log(`\uD83D\uDD10 ${J.verb} ${J.permission}${G} for ${X} on ${Y}`)}process.exit(0)}X9();e8();function g7($,Z={}){let Q=new Date($.timestamp),J=Number.isNaN(Q.getTime())?$.timestamp:Q.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"}),Y=Z.deviceLabel??($.device?$.device.slice(0,8):null),X=$.status==="error"?" failed":"";return[J,Y,`${DY($)}${X?` (${X.trim()})`:""}`].filter(Boolean).join(" ")}function DY($){if($.kind==="tap"){let Z=G9($.details,"current")??G9($.details,"start");return Z?`Tap at ${f4(Z)}`:"Tap"}if($.kind==="drag"){let Z=G9($.details,"start"),Q=G9($.details,"current");if(Z&&Q)return`Drag from ${f4(Z)} to ${f4(Q)}`;return"Drag"}if($.kind==="key"){let Z=EY($.details,"key")??$.action??"key",Q=$.action==="down"?"down":$.action==="up"?"up":$.action;return Q?`Key ${Q} ${h7(Z)}`:`Key ${h7(Z)}`}if($.kind==="rotate"){let Z=$.action?u7($.action):"";return Z?`Rotate ${Z}`:"Rotate"}return xY($.msg??$.summary)}function G9($,Z){let Q=$?.[Z];if(!Q||typeof Q!=="object"||Array.isArray(Q))return null;let J=Q;return typeof J.x==="number"&&typeof J.y==="number"?{x:J.x,y:J.y}:null}function EY($,Z){let Q=$?.[Z];return typeof Q==="string"&&Q.length>0?Q:null}function f4($){return`${v7($.x)}, ${v7($.y)}`}function v7($){if(!Number.isFinite($))return"?";return(Math.round((IY($,0,1)+Number.EPSILON)*100)/100).toFixed(2)}function IY($,Z,Q){return Math.min(Q,Math.max(Z,$))}function xY($){return $.replace(/\b[a-z]+(?:_[a-z]+)+\b/g,u7)}function u7($){return $.replace(/[-_]+/g," ")}function h7($){return{MetaLeft:"Left Command",MetaRight:"Right Command",AltLeft:"Left Option",AltRight:"Right Option",ControlLeft:"Left Control",ControlRight:"Right Control",ShiftLeft:"Left Shift",ShiftRight:"Right Shift",ArrowLeft:"Left Arrow",ArrowRight:"Right Arrow",ArrowUp:"Up Arrow",ArrowDown:"Down Arrow"}[$]??$}$8();var wY=new Set(["","0.0.0.0","::","::0"]);function kY($){return $.startsWith("[")&&$.endsWith("]")?$.slice(1,-1):$}function p7($){return $.includes(":")?`[${$}]`:$}function d7($){if(!$.exposedToLan)return"<host>";let Z=kY($.host.trim());if(wY.has(Z))return $.networkIP?p7($.networkIP):"<host>";return p7(Z)}h4();import{once as ZZ}from"events";import{createWriteStream as hY}from"fs";import{rm as gY}from"fs/promises";import{pipeline as uY}from"stream/promises";async function QZ($,Z,{exclusive:Q,onOpen:J}){let Y=hY(Z,{flags:Q?"wx":"w"});try{await ZZ(Y,"open")}catch(X){throw await $.cancel().catch(()=>{}),X}try{J?.(),await uY(({signal:X}={})=>mY($,X),Y)}catch(X){if(!Y.closed){let G=ZZ(Y,"close");Y.destroy(),await G.catch(()=>{})}throw await $.cancel().catch(()=>{}),await gY(Z,{force:!0}).catch(()=>{}),X}return Y.bytesWritten}async function*mY($,Z){let Q=$.getReader(),J=!1,Y=()=>{if(J)return;J=!0,Q.cancel(Z?.reason).catch(()=>{})};Z?.addEventListener("abort",Y,{once:!0});try{if(Z?.aborted)Y();while(!0){let X=await Q.read();if(Z?.throwIfAborted(),X.done){J=!0;return}yield X.value}}catch(X){throw J=!0,X}finally{Z?.removeEventListener("abort",Y),Y(),Q.releaseLock()}}v4();var cY=new Set(["portrait","portrait_upside_down","landscape_left","landscape_right"]);function JZ($){if(!$||typeof $!=="object")return;let Z=$;if(typeof Z.width!=="number"||typeof Z.height!=="number")return;if(typeof Z.hingeDegrees==="number")return;let Q=cY.has(Z.orientation)?Z.orientation:void 0;return K9({width:Z.width,height:Z.height,orientation:Q}).inputOrientation}function g4($,Z){let Q=n7(Z,$.x,$.y),J={...$,x:Q.x,y:Q.y};if(typeof $.edge==="number"&&$.edge!==0)J.edge=i7(Z,$.edge);return J}function YZ($){let Z;try{Z=new URL($)}catch{return null}if(!Z.pathname.endsWith("/ws"))return null;return Z.protocol=Z.protocol==="wss:"?"https:":"http:",Z.pathname=Z.pathname.slice(0,-2)+"config",Z.toString()}var XZ=["devices","tools","devtools"];function GZ($){let Z=$.split(",").map((J)=>J.trim().toLowerCase()).filter(Boolean);if(Z.length===1&&Z[0]==="none")return[];if(Z.length===0||Z.includes("none"))throw Error("Expected 'none' or a comma-separated list of: devices, tools, devtools.");let Q=Z.filter((J)=>!XZ.includes(J));if(Q.length>0)throw Error(`Unknown pane${Q.length===1?"":"s"}: ${Q.join(", ")}. Expected: ${XZ.join(", ")}.`);return[...new Set(Z)]}function KZ($){let Z=$.trim().toLowerCase();if(Z==="light"||Z==="dark")return Z;throw Error("Expected simulator theme: light or dark.")}c4();T$();var oY=/(^|\/)serve-sim$/;function BZ($){let[Z,Q]=$.argv;if(Z&&oY.test(Z))return Z;if(Q?.startsWith("/$bunfs/"))return $.execPath;return null}function HZ($,Z=process){let Q=BZ(Z);if(Q)return{command:Q,args:$};return{command:Z.argv[0],args:[Z.argv[1],...$]}}function MZ($=process){return BZ($)??$.argv[1]??"serve-sim"}s4();H8();var z5="SERVE_SIM_URL",K0=p8(import.meta.url);function SV(){return"0.1.50-beta.144.1"}function yV(){if(!P$(x$))Y5(x$,{recursive:!0})}function i$($){if($){let Z=Q5(h8($));return Z&&i9(Z)?Z:null}for(let Z of g8()){let Q=Q5(Z);if(Q&&i9(Q))return Q}return null}function i9($){return W0($.deviceSet,N$())}var w8={at:0,booted:null};function U2(){let $=Date.now();if(w8.booted&&$-w8.at<1000)return w8.booted;try{let Z=b$("xcrun",c(["list","devices","booted","-j"]),{encoding:"utf-8",stdio:["ignore","pipe","pipe"],timeout:3000}),Q=JSON.parse(Z),J=new Set;for(let Y of Object.values(Q.devices))for(let X of Y)if(X.state==="Booted")J.add(X.udid);return w8={at:$,booted:J},J}catch{return null}}function Q5($){try{if(!P$($))return O$("state file missing %s",$),null;let Z=R7(q0($,"utf-8"));if(!Z)return O$("malformed state %s",$),_4($),null;let Q=$9(Z);if(!Q)return O$("helper pid %d dead, removing stale state %s",Z.pid,$),_4($),null;if(Q.pid!==Z.pid)Z9(Q);return O$("state ok pid=%d device=%s port=%d",Q.pid,Q.device,Q.port),Q}catch(Z){return O$("readStateFile threw for %s: %o",$,Z),null}}function j2($,Z){let Q=i$($);if(!Q)return null;let J=Q9(Q,U2(),process.pid,N$());if(J==="keep"){let Y=F7(Q,V0(void 0))??_7(Q,Z);if(Y)console.error(Y),process.exit(1);return Q}if(O$("recycling %s helper pid=%d for unbooted device %s",J,Q.pid,Q.device),J==="recycle-helper"){for(let Y of C4(Q))G8(Y);n8(Q.port)}return O0($),null}function S8(){let $=[];for(let Z of g8()){let Q=Q5(Z);if(Q)$.push(Q)}return $}function fV($){k0($),O$("wrote state pid=%d device=%s port=%d",$.pid,$.device,$.port)}function O0($){if($){O$("clearState device=%s",$);try{k8(h8($))}catch{}}else{O$("clearState (all)");for(let Z of g8())try{k8(Z)}catch{}}}function W5(){try{let $=b$("xcrun",c(["list","devices","-j"]),{encoding:"utf-8"}),Z=JSON.parse($),Q=Object.keys(Z.devices).filter((J)=>/SimRuntime\.iOS-/i.test(J)).sort((J,Y)=>{let X=(J.match(/iOS-(\d+)-(\d+)/)??[]).slice(1).map(Number),G=(Y.match(/iOS-(\d+)-(\d+)/)??[]).slice(1).map(Number);return(G[0]??0)-(X[0]??0)||(G[1]??0)-(X[1]??0)});for(let J of Q){let X=(Z.devices[J]??[]).find((G)=>G.isAvailable!==!1&&/^iPhone\b/i.test(G.name));if(X)return{udid:X.udid,name:X.name}}}catch{}return null}function M2($){return R2().get($)??null}function R2(){let $=new Map;try{let Z=b$("xcrun",c(["list","devices","-j"]),{encoding:"utf-8"}),Q=JSON.parse(Z);for(let J of Object.values(Q.devices))for(let Y of J)$.set(Y.udid,Y.name)}catch{}return $}function J5($){try{let Z=b$("xcrun",c(["list","devices","-j"]),{encoding:"utf-8"}),Q=JSON.parse(Z);for(let J of Object.values(Q.devices))for(let Y of J)if(Y.udid===$)return Y.state==="Booted"}catch{}return!1}function bV($){if(!J5($))try{b$("xcrun",c(["boot",$]),{encoding:"utf-8",stdio:"pipe"})}catch(Z){let Q=(Z.stderr??Z.message??"").toLowerCase();if(!Q.includes("booted")&&!Q.includes("current state"))throw Error(`Failed to boot device ${$}: ${Z.stderr||Z.message}`)}if(!W0(N$(),void 0))return;try{o9("open -ga Simulator",{encoding:"utf-8",stdio:"pipe",timeout:3000})}catch{}}function vV(){let $=wV();for(let Z of Object.values($))for(let Q of Z??[])if(Q.family==="IPv4"&&!Q.internal)return Q.address;return null}async function F2($){let Z=new Set(S8().map((Q)=>Q.port));for(let Q=$;Q<$+100;Q++){if(Z.has(Q))continue;if(await J7(Q))return Q}throw Error(`No available port found in range ${$}-${$+99}`)}async function hV($){let Z=J5($);if(bV($),Z)return;try{b$("xcrun",v8($),{encoding:"utf-8",stdio:"pipe",timeout:60000})}catch(Q){if(!J5($))console.error(`Device ${$} failed to reach booted state: ${Q.stderr||Q.message}`),process.exit(1)}}async function gV($,Z=150000){let Q=Date.now();while(Date.now()-Q<Z){let J=i$($);if(J)return J;await new Promise((Y)=>setTimeout(Y,200))}return null}async function _2($,Z,Q){M0("startHelper udid=%s port=%d detach=%s",$,Z,Q.detach);let J="127.0.0.1";yV(),O0($),n8(Z);let Y=h$(x$,`server-${$}.log`),X=X5(Y,"w"),{command:G,args:z}=HZ([$,"--port",String(Z),"--host",J,"--exit-on-simulator-shutdown",...Q.allowExec?["--allow-exec"]:[]]),V=N2(G,z,{detached:Q.detach,stdio:["ignore",X,X]});if(G5(X),Q.detach)V.unref();let W=await gV($);if(!W){if(V.pid)G8(V.pid);let K="";try{K=q0(Y,"utf-8").trim()}catch{}console.error(K?`Preview server failed:
|
|
189
189
|
${K}`:"Preview server failed to start"),process.exit(1)}return Q.detach?{pid:W.pid}:{pid:W.pid,child:V}}async function uV($,Z,Q,J){t8("follow devices=%o startPort=%d",$,Z);let Y=$.length>0?$.map(U$):(()=>{let K=y$();if(K)return[K];let B=W5();if(!B)console.error("No device specified and no available iOS simulator found."),process.exit(1);if(!Q)console.log(`No booted simulator — booting ${B.name}...`);return[B.udid]})(),X=new Map,G=[],z=Z;for(let K of Y){let B=j2(K,J);if(B){if(!Q){let F=M2(K)??K;if(Y.length>1)console.log(`
|
|
190
190
|
==> ${F} (${K}) <==`);console.log(` Already running on port ${B.port}`),console.log(` Stream: ${B.streamUrl}`),console.log(` WebSocket: ${B.wsUrl}`)}G.push(B);continue}z=await F2(z);let{child:M}=await _2(K,z,{detach:!1,allowExec:J});if(M)X.set(K,M);let U=i$(K)??w0(K,z,"/","127.0.0.1");if(G.push(U),!Q){let F=M2(K)??K;if(Y.length>1)console.log(`
|
|
191
191
|
==> ${F} (${K}) <==`);console.log(` Stream: ${U.streamUrl}`),console.log(` WebSocket: ${U.wsUrl}`),console.log(` Port: ${z}`)}z++}if(G.length===1){let K=G[0];console.log(JSON.stringify({url:K.url,streamUrl:K.streamUrl,wsUrl:K.wsUrl,port:K.port,device:K.device}))}else console.log(JSON.stringify({devices:G.map((K)=>({url:K.url,streamUrl:K.streamUrl,wsUrl:K.wsUrl,port:K.port,device:K.device}))}));if(X.size===0)return;let V=!1,W=(K)=>{if(V)return;if(V=!0,!Q)console.log(`
|
|
@@ -245,5 +245,5 @@ Examples:
|
|
|
245
245
|
serve-sim --list Show all running streams
|
|
246
246
|
serve-sim --kill Stop all streams`).action(async($,Z)=>{if(typeof Z.authToken==="string"&&Z.authToken.trim()!=="")process.env[a$]=Z.authToken.trim();if(l7(Z.maxBitrate),Z.list!==void 0){lV(typeof Z.list==="string"?Z.list:void 0);return}if(Z.kill!==void 0){pV(typeof Z.kill==="string"?Z.kill:void 0);return}let Q=Z.port;if(Z.detach){let J=await mV($,Q??3100,!!Z.allowExec);cV(J)}else if(Z.preview===!1)await uV($,Q??3100,!!Z.quiet,!!Z.allowExec);else{let J=Z.panes!==void 0||Z.fit?{...Z.panes!==void 0?{panes:Z.panes}:{},...Z.fit?{fit:!0}:{}}:void 0;await UB(Q??3200,$,Q!==void 0,Z.host,Z.codec,J,Z.theme,!!Z.exitOnSimulatorShutdown,!!Z.allowExec)}});var D$=["-d, --device <udid>","Target a specific simulator (udid or name)"],x2=["--raw","Send coords in the device's native portrait frame (skip landscape remapping)"];G$.command("gesture").description("Send a touch gesture").argument("<json>",`Gesture JSON, e.g. '{"type":"begin","x":0.5,"y":0.5}'`).option(...D$).option(...x2).action(($,Z)=>rV($,Z.device,Z.raw));G$.command("tap").description("Tap at normalized 0..1 coords").argument("<x>","X coord, normalized 0..1").argument("<y>","Y coord, normalized 0..1").option(...D$).option(...x2).action(($,Z,Q)=>sV($,Z,Q.device,Q.raw));G$.command("record").description("Record the simulator screen to an MP4 (native resolution, 60 fps H.264); Ctrl+C stops").option(...D$).option("-o, --output <file>","Output file (default: ./serve-sim-recording-<timestamp>.mp4)").option("-t, --duration <time>","Stop automatically after this long, e.g. 10s, 1.5m, 500ms").option("--codec <codec>","h264 (default) or hevc").option("--fps <fps>","Constant output frame rate (default 60)").option("--bitrate <bps>","Average bitrate in bits/s (default scales with resolution)").action(($)=>tV($.device,$));G$.command("button").description("Send a hardware button press").argument("[name]","Button name","home").option(...D$).action(($,Z)=>JB($,Z.device));G$.command("type").description("Type text (US keyboard only)").argument("[text...]","Text to type").option(...D$).option("--stdin","Read text from stdin").option("--file <path>","Read text from a file").action(($,Z)=>eV($,{device:Z.device,stdin:Z.stdin,file:Z.file}));G$.command("rotate").description("Set device orientation (portrait|portrait_upside_down|landscape_left|landscape_right)").argument("<orientation>").option(...D$).action(($,Z)=>ZB($,Z.device));G$.command("repair-input").description("Repair Device Hub input; restarts SpringBoard and closes running apps").option(...D$).action(async($)=>{let Z=$.device?U$($.device):y$();if(!Z)throw Error("No booted simulator found.");let{repairDeviceHubInput:Q}=await Promise.resolve().then(() => (j9(),FZ)),J=await Q(Z);console.log(J?"Input repaired. Restart serve-sim to reconnect its guest services, then reopen your app.":"Input is not shadowed by Device Hub; no restart needed.")});G$.command("fold").description("Set the iPhone Duo hinge angle in degrees (0 closed, 180 fully open)").argument("<deg>","Hinge angle from 0 to 180",($)=>{let Z=Number($);if(!$.trim()||!Number.isFinite(Z)||Z<0||Z>180)throw new I0("Hinge angle must be between 0 and 180 degrees.");return Z}).option(...D$).action(($,Z)=>$B($,Z.device));G$.command("ca-debug").description("Toggle a CoreAnimation debug render flag (blended|copies|misaligned|offscreen|slow-animations)").argument("<option>").argument("<state>","on|off").option(...D$).action(($,Z,Q)=>YB($,Z,Q.device));G$.command("memory-warning").description("Simulate a memory warning on the device").option(...D$).action(($)=>XB($.device));G$.command("exec").description('Run a shell command through a local server started with --allow-exec. One argument is a shell command line (`exec "xcrun simctl list | head"`); several are quoted as one argv (`exec ls "my dir"`).').argument("<command...>","Shell command line, or argv words to quote").option(...D$).action(($,Z)=>dV(VZ($),Z.device));G$.command("event-log").description("Show recent simulator events").option(...D$).option("-j, --json","Print JSON").option("-n, --limit <count>","Maximum number of events").action(($)=>nV($.device,{json:$.json,limit:$.limit}));var RB=new Set(["gesture","tap","button","type","rotate","fold","ca-debug","memory-warning","event-log","record"]);for(let $ of G$.commands){if(!RB.has($.name()))continue;$.option("--url <url>",`Remote serve-sim preview URL (or ${z5}); pass a UDID with -d`).option("--auth-token <token>",`Auth token for the remote preview (or ${a$})`).hook("preAction",(Z,Q)=>{let J=Q.optsWithGlobals();V5={url:J.url,authToken:J.authToken}})}G$.command("doctor").description("Collect redacted diagnostics for bug reports (Xcode, displays, encoders, streams, Duo renderer)").option(...D$).option("-j, --json","Print the full JSON bundle").option("-o, --out <file>","Write the JSON bundle to a file").action(($)=>iV($.device,{json:$.json,out:$.out}));G$.command("camera").description("Inject a synthetic camera feed and launch an app (see `camera --help`)").allowUnknownOption(!0).helpOption(!1).argument("[args...]").action(($)=>LB($));G$.command("permissions").description("Manage app permissions (see `permissions` with no args for usage)").allowUnknownOption(!0).helpOption(!1).argument("[args...]").action(($)=>x7($));G$.command("ui").description("Get or set simulator-wide UI options (see `ui --help`)").allowUnknownOption(!0).helpOption(!1).argument("[args...]").action(($)=>b7($));G$.hook("preAction",()=>{try{N$(G$.opts().deviceSet)}catch($){console.error($ instanceof Error?$.message:String($)),process.exit(1)}});await G$.parseAsync(process.argv);
|
|
247
247
|
|
|
248
|
-
//# debugId=
|
|
248
|
+
//# debugId=D461A85C6606513164756E2164756E21
|
|
249
249
|
//# sourceMappingURL=serve-sim.js.map
|
|
Binary file
|