tail-sim 0.1.1 → 0.1.3

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/bin/tail-sim-bin CHANGED
Binary file
package/dist/tail-sim.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- import{execSync as H,spawn as v}from"child_process";import{existsSync as m,mkdirSync as Gz,openSync as g,closeSync as n,readFileSync as I,unlinkSync as j,writeFileSync as Jz}from"fs";import{join as Kz,resolve as Qz}from"path";import{tmpdir as zz}from"os";import{join as _}from"path";import{readdirSync as Cz}from"fs";var U=_(zz(),"tail-sim"),kz=_(U,"server.json");function k(z){return _(U,`server-${z}.json`)}function q(){try{return Cz(U).filter((z)=>z.startsWith("server-")&&z.endsWith(".json")).map((z)=>_(U,z))}catch{return[]}}function F(){if(!m(U))Gz(U,{recursive:!0})}function R(z){if(z)return T(k(z));for(let G of q()){let C=T(G);if(C)return C}return null}function T(z){try{if(!m(z))return null;let G=JSON.parse(I(z,"utf-8"));try{return process.kill(G.pid,0),G}catch{return j(z),null}}catch{return null}}function y(){let z=[];for(let G of q()){let C=T(G);if(C)z.push(C)}return z}function A(z){F(),Jz(k(z.device),JSON.stringify(z,null,2))}function b(z){if(z)try{j(k(z))}catch{}else for(let G of q())try{j(G)}catch{}}function Vz(){let z=Qz(import.meta.dir,"../bin/tail-sim-bin");if(m(z))return z;throw Error(`tail-sim-bin binary not found. Run 'bun run build:swift' first.
4
- Checked: ${z}`)}function p(){try{let z=H("xcrun simctl list devices booted -j",{encoding:"utf-8"}),G=JSON.parse(z);for(let C of Object.values(G.devices))for(let K of C)if(K.state==="Booted")return K.udid}catch{}return null}function D(z){try{let G=H("xcrun simctl list devices -j",{encoding:"utf-8"}),C=JSON.parse(G);for(let K of Object.values(C.devices))for(let V of K)if(V.udid===z)return V.name}catch{}return null}function E(z){if(/^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i.test(z))return z;try{let G=H("xcrun simctl list devices -j",{encoding:"utf-8"}),C=JSON.parse(G);for(let K of Object.values(C.devices))for(let V of K)if(V.name.toLowerCase()===z.toLowerCase())return V.udid}catch{}console.error(`Could not resolve device: ${z}`),process.exit(1)}function O(z){try{let G=H("xcrun simctl list devices -j",{encoding:"utf-8"}),C=JSON.parse(G);for(let K of Object.values(C.devices))for(let V of K)if(V.udid===z)return V.state==="Booted"}catch{}return!1}function w(z){try{return process.kill(z,0),!0}catch{return!1}}function h(z){try{process.kill(z,"SIGTERM")}catch{return}let G=Date.now()+500;while(Date.now()<G)try{process.kill(z,0),Bun.sleepSync(25)}catch{return}try{process.kill(z,"SIGKILL")}catch{}let C=Date.now()+500;while(Date.now()<C)try{process.kill(z,0),Bun.sleepSync(25)}catch{return}}function Wz(z){try{let G=H(`lsof -ti tcp:${z}`,{encoding:"utf-8",stdio:"pipe"}).trim();if(G){let C=process.pid;for(let K of G.split(`
5
- `)){let V=parseInt(K,10);if(V!==C)try{process.kill(V,"SIGKILL")}catch{}}Bun.sleepSync(100)}}catch{}}function u(z){if(O(z))return;try{H(`xcrun simctl boot ${z}`,{encoding:"utf-8",stdio:"pipe"})}catch(G){let C=(G.stderr??G.message??"").toLowerCase();if(C.includes("booted")||C.includes("current state"))return;throw Error(`Failed to boot device ${z}: ${G.stderr||G.message}`)}}function Yz(z){try{H(`xcrun simctl shutdown ${z}`,{encoding:"utf-8",stdio:"pipe"})}catch{}u(z)}async function c(z){let G=new Set(y().map((C)=>C.port));for(let C=z;C<z+100;C++){if(G.has(C))continue;try{return Bun.serve({port:C,fetch:()=>new Response("ok")}).stop(!0),C}catch{continue}}throw Error(`No available port found in range ${z}-${z+99}`)}async function S(z){if(!O(z)){u(z);let G=Date.now()+15000;while(!O(z)&&Date.now()<G)await new Promise((C)=>setTimeout(C,200));if(!O(z))console.error(`Device ${z} failed to boot within 15 seconds.`),process.exit(1)}}async function l(z,G,C,K){let V=!1;for(let Q=0;Q<30;Q++){if(!K())break;try{if((await fetch(`${G}/health`)).ok){V=!0;break}}catch{}await new Promise((Y)=>setTimeout(Y,100))}if(V){let Q=Date.now()+8000;while(Date.now()<Q){if(await new Promise((Y)=>setTimeout(Y,200)),!K()){V=!1;break}try{if(I(C,"utf-8").includes("Capture started"))break}catch{}}}let W="";try{W=I(C,"utf-8").trim()}catch{}return{ready:V,log:W}}async function Zz(z){let{helperPath:G,udid:C,port:K,host:V,logFile:W}=z,Q=`http://${V}:${K}`;F();let Y=g(W,"w"),J=v(G,[C,"--port",String(K)],{detached:!0,stdio:["ignore",Y,Y]});J.unref(),n(Y);let Z=J.pid,X=!1;J.once("exit",()=>{X=!0});let{ready:L,log:M}=await l(Z,Q,W,()=>!X&&w(Z));return{ready:L,pid:Z,exited:X||!w(Z),log:M}}async function $z(z){let{helperPath:G,udid:C,port:K,host:V,logFile:W}=z,Q=`http://${V}:${K}`;F();let Y=g(W,"w"),J=v(G,[C,"--port",String(K)],{detached:!1,stdio:["ignore",Y,Y]});n(Y);let Z=J.pid,X=!1;J.once("exit",()=>{X=!0});let{ready:L,log:M}=await l(Z,Q,W,()=>!X&&w(Z));return{ready:L,child:J,log:M}}async function o(z,G,C){await S(z);let K="127.0.0.1",V=Vz(),W=Kz(U,`server-${z}.log`),Q={helperPath:V,udid:z,port:G,host:K,logFile:W},Y="",J=!1,Z=3;for(let L=1;L<=Z;L++){if(Wz(G),C.detach){let $=await Zz(Q);if($.ready){let B={pid:$.pid,port:G,device:z,url:`http://${K}:${G}`,streamUrl:`http://${K}:${G}/stream.mjpeg`,wsUrl:`ws://${K}:${G}/ws`};return A(B),{pid:$.pid}}h($.pid),Y=$.log}else{let $=await $z(Q);if($.ready){let B={pid:$.child.pid,port:G,device:z,url:`http://${K}:${G}`,streamUrl:`http://${K}:${G}/stream.mjpeg`,wsUrl:`ws://${K}:${G}/ws`};return A(B),{pid:$.child.pid,child:$.child}}h($.child.pid),Y=$.log}if(Y.includes("framebufferSurface not available")&&!J)console.error("[tail-sim] Framebuffer unavailable \u2014 restarting simulator..."),J=!0,Yz(z),await S(z);else if(L<Z)await new Promise(($)=>setTimeout($,500))}let X=Y?`Helper failed:
6
- ${Y}`:"Helper process failed to start";console.error(X),process.exit(1)}async function Xz(z,G,C){let K=z.length>0?z.map(E):(()=>{let J=p();if(!J)console.error("No device specified and no booted simulator found."),process.exit(1);return[J]})(),V=new Map,W=[],Q=G;for(let J of K){let Z=R(J);if(Z){if(!C){let B=D(J)??J;if(K.length>1)console.error(`
7
- ==> ${B} (${J}) <==`);console.error(` Already running on port ${Z.port}`),console.error(` Stream: ${Z.streamUrl}`),console.error(` WebSocket: ${Z.wsUrl}`)}W.push(Z);continue}Q=await c(Q);let{pid:X,child:L}=await o(J,Q,{detach:!1});if(L)V.set(J,L);let M="127.0.0.1",$={pid:X,port:Q,device:J,url:`http://${M}:${Q}`,streamUrl:`http://${M}:${Q}/stream.mjpeg`,wsUrl:`ws://${M}:${Q}/ws`};if(W.push($),!C){let B=D(J)??J;if(K.length>1)console.error(`
8
- ==> ${B} (${J}) <==`);console.error(` Stream: ${$.streamUrl}`),console.error(` WebSocket: ${$.wsUrl}`),console.error(` Port: ${Q}`)}Q++}if(W.length===1){let J=W[0];console.log(JSON.stringify({url:J.url,streamUrl:J.streamUrl,wsUrl:J.wsUrl,port:J.port,device:J.device}))}else console.log(JSON.stringify({devices:W.map((J)=>({url:J.url,streamUrl:J.streamUrl,wsUrl:J.wsUrl,port:J.port,device:J.device}))}));if(V.size===0)return;for(let[J,Z]of V)Z.on("exit",(X)=>{if(!C)console.error(`[${J}] Helper exited (code ${X})`);if(b(J),V.delete(J),V.size===0)process.exit(X??1)});let Y=()=>{if(!C)console.error(`
9
- [tail-sim] Shutting down...`);for(let[J,Z]of V){try{process.kill(Z.pid,"SIGTERM")}catch{}b(J)}process.exit(0)};process.on("SIGINT",Y),process.on("SIGTERM",Y),await new Promise(()=>{})}async function Nz(z,G,C){let K=z.length>0?z.map(E):(()=>{let Q=p();if(!Q)console.error("No device specified and no booted simulator found."),process.exit(1);return[Q]})(),V=[],W=G;for(let Q of K){let Y=R(Q);if(Y){V.push(Y);continue}W=await c(W),await o(Q,W,{detach:!0});let J="127.0.0.1";V.push({pid:R(Q).pid,port:W,device:Q,url:`http://${J}:${W}`,streamUrl:`http://${J}:${W}/stream.mjpeg`,wsUrl:`ws://${J}:${W}/ws`}),W++}if(V.length===1){let Q=V[0];console.log(JSON.stringify({url:Q.url,streamUrl:Q.streamUrl,wsUrl:Q.wsUrl,port:Q.port,device:Q.device}))}else console.log(JSON.stringify({devices:V.map((Q)=>({url:Q.url,streamUrl:Q.streamUrl,wsUrl:Q.wsUrl,port:Q.port,device:Q.device}))}))}function Lz(z){if(z){let C=E(z),K=R(C);if(!K)console.log(JSON.stringify({running:!1,device:C}));else console.log(JSON.stringify({running:!0,url:K.url,streamUrl:K.streamUrl,wsUrl:K.wsUrl,port:K.port,device:K.device,pid:K.pid}));return}let G=y();if(G.length===0)console.log(JSON.stringify({running:!1}));else if(G.length===1){let C=G[0];console.log(JSON.stringify({running:!0,url:C.url,streamUrl:C.streamUrl,wsUrl:C.wsUrl,port:C.port,device:C.device,pid:C.pid}))}else console.log(JSON.stringify({running:!0,streams:G.map((C)=>({url:C.url,streamUrl:C.streamUrl,wsUrl:C.wsUrl,port:C.port,device:C.device,pid:C.pid}))}))}function Mz(z){if(z){let G=E(z),C=R(G);if(!C){console.log(JSON.stringify({disconnected:!0,device:G}));return}try{process.kill(C.pid,"SIGTERM")}catch{}b(G),console.log(JSON.stringify({disconnected:!0,device:C.device}))}else{let G=y();if(G.length===0){console.log(JSON.stringify({disconnected:!0,devices:[]}));return}let C=[];for(let K of G){try{process.kill(K.pid,"SIGTERM")}catch{}C.push(K.device)}b(),console.log(JSON.stringify({disconnected:!0,devices:C}))}}async function Uz(z){let G,C=[];for(let Q=0;Q<z.length;Q++)if(z[Q]==="--device"||z[Q]==="-d")G=z[++Q];else C.push(z[Q]);let K=R(G);if(!K)console.error("No tail-sim server running. Run `tail-sim` first."),process.exit(1);let V=C[0];if(!V)console.error("Usage: tail-sim gesture '<json>'"),console.error(`Example: tail-sim gesture '{"type":"begin","x":0.5,"y":0.5}'`),process.exit(1);let W;try{W=JSON.parse(V)}catch{console.error("Invalid JSON:",V),process.exit(1)}return new Promise((Q,Y)=>{let J=new WebSocket(K.wsUrl);J.binaryType="arraybuffer",J.onopen=()=>{let Z=new TextEncoder().encode(JSON.stringify(W)),X=new Uint8Array(1+Z.length);X[0]=3,X.set(Z,1),J.send(X),setTimeout(()=>{J.close(),Q()},50)},J.onerror=()=>{console.error("Failed to connect to tail-sim server at",K.wsUrl),Y(Error("WebSocket connection failed"))}})}async function Bz(z){let G,C=[];for(let W=0;W<z.length;W++)if(z[W]==="--device"||z[W]==="-d")G=z[++W];else C.push(z[W]);let K=R(G);if(!K)console.error("No tail-sim server running. Run `tail-sim` first."),process.exit(1);let V=C[0]??"home";return new Promise((W,Q)=>{let Y=new WebSocket(K.wsUrl);Y.binaryType="arraybuffer",Y.onopen=()=>{let J=new TextEncoder().encode(JSON.stringify({button:V})),Z=new Uint8Array(1+J.length);Z[0]=4,Z.set(J,1),Y.send(Z),setTimeout(()=>{Y.close(),W()},50)},Y.onerror=()=>{console.error("Failed to connect to tail-sim server at",K.wsUrl),Q(Error("WebSocket connection failed"))}})}function a(){console.log(`
3
+ import{execSync as M,spawn as h}from"child_process";import{existsSync as P,mkdirSync as zz,openSync as S,closeSync as v,readFileSync as E,unlinkSync as I,writeFileSync as Cz}from"fs";import{join as Gz,resolve as Jz}from"path";import{tmpdir as t}from"os";import{join as R}from"path";import{readdirSync as e}from"fs";var B=R(t(),"tail-sim"),_z=R(B,"server.json");function H(z){return R(B,`server-${z}.json`)}function _(){try{return e(B).filter((z)=>z.startsWith("server-")&&z.endsWith(".json")).map((z)=>R(B,z))}catch{return[]}}function m(){if(!P(B))zz(B,{recursive:!0})}function U(z){if(z)return j(H(z));for(let C of _()){let G=j(C);if(G)return G}return null}function j(z){try{if(!P(z))return null;let C=JSON.parse(E(z,"utf-8"));try{return process.kill(C.pid,0),C}catch{return I(z),null}}catch{return null}}function F(){let z=[];for(let C of _()){let G=j(C);if(G)z.push(G)}return z}function y(z){m(),Cz(H(z.device),JSON.stringify(z,null,2))}function k(z){if(z)try{I(H(z))}catch{}else for(let C of _())try{I(C)}catch{}}function Kz(){let z=Jz(import.meta.dir,"../bin/tail-sim-bin");if(P(z))return z;throw Error(`tail-sim-bin binary not found. Run 'bun run build:swift' first.
4
+ Checked: ${z}`)}function g(){try{let z=M("xcrun simctl list devices booted -j",{encoding:"utf-8"}),C=JSON.parse(z);for(let G of Object.values(C.devices))for(let K of G)if(K.state==="Booted")return K.udid}catch{}return null}function A(z){try{let C=M("xcrun simctl list devices -j",{encoding:"utf-8"}),G=JSON.parse(C);for(let K of Object.values(G.devices))for(let V of K)if(V.udid===z)return V.name}catch{}return null}function q(z){if(/^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i.test(z))return z;try{let C=M("xcrun simctl list devices -j",{encoding:"utf-8"}),G=JSON.parse(C);for(let K of Object.values(G.devices))for(let V of K)if(V.name.toLowerCase()===z.toLowerCase())return V.udid}catch{}console.error(`Could not resolve device: ${z}`),process.exit(1)}function n(z){try{let C=M("xcrun simctl list devices -j",{encoding:"utf-8"}),G=JSON.parse(C);for(let K of Object.values(G.devices))for(let V of K)if(V.udid===z)return V.state==="Booted"}catch{}return!1}function T(z){try{return process.kill(z,0),!0}catch{return!1}}function D(z){try{process.kill(z,"SIGTERM")}catch{return}let C=Date.now()+500;while(Date.now()<C)try{process.kill(z,0),Bun.sleepSync(25)}catch{return}try{process.kill(z,"SIGKILL")}catch{}let G=Date.now()+500;while(Date.now()<G)try{process.kill(z,0),Bun.sleepSync(25)}catch{return}}function Qz(z){try{let C=M(`lsof -ti tcp:${z}`,{encoding:"utf-8",stdio:"pipe"}).trim();if(C){let G=process.pid;for(let K of C.split(`
5
+ `)){let V=parseInt(K,10);if(V!==G)try{process.kill(V,"SIGKILL")}catch{}}Bun.sleepSync(100)}}catch{}}function Vz(z){if(!n(z))try{M(`xcrun simctl boot ${z}`,{encoding:"utf-8",stdio:"pipe"})}catch(C){let G=(C.stderr??C.message??"").toLowerCase();if(!G.includes("booted")&&!G.includes("current state"))throw Error(`Failed to boot device ${z}: ${C.stderr||C.message}`)}try{M("open -ga Simulator",{encoding:"utf-8",stdio:"pipe"})}catch{}}async function p(z){let C=new Set(F().map((G)=>G.port));for(let G=z;G<z+100;G++){if(C.has(G))continue;try{return Bun.serve({port:G,fetch:()=>new Response("ok")}).stop(!0),G}catch{continue}}throw Error(`No available port found in range ${z}-${z+99}`)}async function Wz(z){Vz(z);try{M(`xcrun simctl bootstatus ${z} -b`,{encoding:"utf-8",stdio:"pipe",timeout:60000})}catch(C){if(!n(z))console.error(`Device ${z} failed to reach booted state: ${C.stderr||C.message}`),process.exit(1)}}async function u(z,C,G,K){let V=!1;for(let Q=0;Q<30;Q++){if(!K())break;try{if((await fetch(`${C}/health`)).ok){V=!0;break}}catch{}await new Promise((Y)=>setTimeout(Y,100))}if(V){let Q=Date.now()+8000;while(Date.now()<Q){if(await new Promise((Y)=>setTimeout(Y,200)),!K()){V=!1;break}try{if(E(G,"utf-8").includes("Capture started"))break}catch{}}}let W="";try{W=E(G,"utf-8").trim()}catch{}return{ready:V,log:W}}async function Yz(z){let{helperPath:C,udid:G,port:K,host:V,logFile:W}=z,Q=`http://${V}:${K}`;m();let Y=S(W,"w"),J=h(C,[G,"--port",String(K)],{detached:!0,stdio:["ignore",Y,Y]});J.unref(),v(Y);let Z=J.pid,X=!1;J.once("exit",()=>{X=!0});let{ready:$,log:L}=await u(Z,Q,W,()=>!X&&T(Z));return{ready:$,pid:Z,exited:X||!T(Z),log:L}}async function Zz(z){let{helperPath:C,udid:G,port:K,host:V,logFile:W}=z,Q=`http://${V}:${K}`;m();let Y=S(W,"w"),J=h(C,[G,"--port",String(K)],{detached:!1,stdio:["ignore",Y,Y]});v(Y);let Z=J.pid,X=!1;J.once("exit",()=>{X=!0});let{ready:$,log:L}=await u(Z,Q,W,()=>!X&&T(Z));return{ready:$,child:J,log:L}}async function c(z,C,G){await Wz(z);let K="127.0.0.1",V=Kz(),W=Gz(B,`server-${z}.log`),Q={helperPath:V,udid:z,port:C,host:K,logFile:W},Y="",J=2;for(let X=1;X<=J;X++){if(Qz(C),G.detach){let $=await Yz(Q);if($.ready){let L={pid:$.pid,port:C,device:z,url:`http://${K}:${C}`,streamUrl:`http://${K}:${C}/stream.mjpeg`,wsUrl:`ws://${K}:${C}/ws`};return y(L),{pid:$.pid}}D($.pid),Y=$.log}else{let $=await Zz(Q);if($.ready){let L={pid:$.child.pid,port:C,device:z,url:`http://${K}:${C}`,streamUrl:`http://${K}:${C}/stream.mjpeg`,wsUrl:`ws://${K}:${C}/ws`};return y(L),{pid:$.child.pid,child:$.child}}D($.child.pid),Y=$.log}if(X<J)await new Promise(($)=>setTimeout($,500))}let Z=Y?`Helper failed:
6
+ ${Y}`:"Helper process failed to start";console.error(Z),process.exit(1)}async function $z(z,C,G){let K=z.length>0?z.map(q):(()=>{let J=g();if(!J)console.error("No device specified and no booted simulator found."),process.exit(1);return[J]})(),V=new Map,W=[],Q=C;for(let J of K){let Z=U(J);if(Z){if(!G){let b=A(J)??J;if(K.length>1)console.error(`
7
+ ==> ${b} (${J}) <==`);console.error(` Already running on port ${Z.port}`),console.error(` Stream: ${Z.streamUrl}`),console.error(` WebSocket: ${Z.wsUrl}`)}W.push(Z);continue}Q=await p(Q);let{pid:X,child:$}=await c(J,Q,{detach:!1});if($)V.set(J,$);let L="127.0.0.1",O={pid:X,port:Q,device:J,url:`http://${L}:${Q}`,streamUrl:`http://${L}:${Q}/stream.mjpeg`,wsUrl:`ws://${L}:${Q}/ws`};if(W.push(O),!G){let b=A(J)??J;if(K.length>1)console.error(`
8
+ ==> ${b} (${J}) <==`);console.error(` Stream: ${O.streamUrl}`),console.error(` WebSocket: ${O.wsUrl}`),console.error(` Port: ${Q}`)}Q++}if(W.length===1){let J=W[0];console.log(JSON.stringify({url:J.url,streamUrl:J.streamUrl,wsUrl:J.wsUrl,port:J.port,device:J.device}))}else console.log(JSON.stringify({devices:W.map((J)=>({url:J.url,streamUrl:J.streamUrl,wsUrl:J.wsUrl,port:J.port,device:J.device}))}));if(V.size===0)return;for(let[J,Z]of V)Z.on("exit",(X)=>{if(!G)console.error(`[${J}] Helper exited (code ${X})`);if(k(J),V.delete(J),V.size===0)process.exit(X??1)});let Y=()=>{if(!G)console.error(`
9
+ [tail-sim] Shutting down...`);for(let[J,Z]of V){try{process.kill(Z.pid,"SIGTERM")}catch{}k(J)}process.exit(0)};process.on("SIGINT",Y),process.on("SIGTERM",Y),await new Promise(()=>{})}async function Xz(z,C,G){let K=z.length>0?z.map(q):(()=>{let Q=g();if(!Q)console.error("No device specified and no booted simulator found."),process.exit(1);return[Q]})(),V=[],W=C;for(let Q of K){let Y=U(Q);if(Y){V.push(Y);continue}W=await p(W),await c(Q,W,{detach:!0});let J="127.0.0.1";V.push({pid:U(Q).pid,port:W,device:Q,url:`http://${J}:${W}`,streamUrl:`http://${J}:${W}/stream.mjpeg`,wsUrl:`ws://${J}:${W}/ws`}),W++}if(V.length===1){let Q=V[0];console.log(JSON.stringify({url:Q.url,streamUrl:Q.streamUrl,wsUrl:Q.wsUrl,port:Q.port,device:Q.device}))}else console.log(JSON.stringify({devices:V.map((Q)=>({url:Q.url,streamUrl:Q.streamUrl,wsUrl:Q.wsUrl,port:Q.port,device:Q.device}))}))}function Nz(z){if(z){let G=q(z),K=U(G);if(!K)console.log(JSON.stringify({running:!1,device:G}));else console.log(JSON.stringify({running:!0,url:K.url,streamUrl:K.streamUrl,wsUrl:K.wsUrl,port:K.port,device:K.device,pid:K.pid}));return}let C=F();if(C.length===0)console.log(JSON.stringify({running:!1}));else if(C.length===1){let G=C[0];console.log(JSON.stringify({running:!0,url:G.url,streamUrl:G.streamUrl,wsUrl:G.wsUrl,port:G.port,device:G.device,pid:G.pid}))}else console.log(JSON.stringify({running:!0,streams:C.map((G)=>({url:G.url,streamUrl:G.streamUrl,wsUrl:G.wsUrl,port:G.port,device:G.device,pid:G.pid}))}))}function Lz(z){if(z){let C=q(z),G=U(C);if(!G){console.log(JSON.stringify({disconnected:!0,device:C}));return}try{process.kill(G.pid,"SIGTERM")}catch{}k(C),console.log(JSON.stringify({disconnected:!0,device:G.device}))}else{let C=F();if(C.length===0){console.log(JSON.stringify({disconnected:!0,devices:[]}));return}let G=[];for(let K of C){try{process.kill(K.pid,"SIGTERM")}catch{}G.push(K.device)}k(),console.log(JSON.stringify({disconnected:!0,devices:G}))}}async function Bz(z){let C,G=[];for(let Q=0;Q<z.length;Q++)if(z[Q]==="--device"||z[Q]==="-d")C=z[++Q];else G.push(z[Q]);let K=U(C);if(!K)console.error("No tail-sim server running. Run `tail-sim` first."),process.exit(1);let V=G[0];if(!V)console.error("Usage: tail-sim gesture '<json>'"),console.error(`Example: tail-sim gesture '{"type":"begin","x":0.5,"y":0.5}'`),process.exit(1);let W;try{W=JSON.parse(V)}catch{console.error("Invalid JSON:",V),process.exit(1)}return new Promise((Q,Y)=>{let J=new WebSocket(K.wsUrl);J.binaryType="arraybuffer",J.onopen=()=>{let Z=new TextEncoder().encode(JSON.stringify(W)),X=new Uint8Array(1+Z.length);X[0]=3,X.set(Z,1),J.send(X),setTimeout(()=>{J.close(),Q()},50)},J.onerror=()=>{console.error("Failed to connect to tail-sim server at",K.wsUrl),Y(Error("WebSocket connection failed"))}})}async function Mz(z){let C,G=[];for(let W=0;W<z.length;W++)if(z[W]==="--device"||z[W]==="-d")C=z[++W];else G.push(z[W]);let K=U(C);if(!K)console.error("No tail-sim server running. Run `tail-sim` first."),process.exit(1);let V=G[0]??"home";return new Promise((W,Q)=>{let Y=new WebSocket(K.wsUrl);Y.binaryType="arraybuffer",Y.onopen=()=>{let J=new TextEncoder().encode(JSON.stringify({button:V})),Z=new Uint8Array(1+J.length);Z[0]=4,Z.set(J,1),Y.send(Z),setTimeout(()=>{Y.close(),W()},50)},Y.onerror=()=>{console.error("Failed to connect to tail-sim server at",K.wsUrl),Q(Error("WebSocket connection failed"))}})}function l(){console.log(`
10
10
  tail-sim - Stream iOS Simulator to the browser
11
11
 
12
12
  Usage:
@@ -31,4 +31,4 @@ Examples:
31
31
  tail-sim --kill Stop all streams
32
32
  tail-sim gesture '{"type":"begin","x":0.5,"y":0.5}'
33
33
  tail-sim button home -d <udid>
34
- `)}var N=process.argv.slice(2);if(N[0]==="gesture")await Uz(N.slice(1)),process.exit(0);if(N[0]==="button")await Bz(N.slice(1)),process.exit(0);var x=3100,r=!1,f=!1,d=!1,s=!1,i=!1,P=[],t,e;for(let z=0;z<N.length;z++){let G=N[z];switch(G){case"--port":case"-p":x=parseInt(N[++z]??"3100",10);break;case"--detach":case"-d":r=!0;break;case"--quiet":case"-q":f=!0;break;case"--list":case"-l":if(d=!0,N[z+1]&&!N[z+1].startsWith("-"))t=N[++z];break;case"--kill":case"-k":if(s=!0,N[z+1]&&!N[z+1].startsWith("-"))e=N[++z];break;case"--help":case"-h":case"help":i=!0;break;default:if(!G.startsWith("-"))P.push(G);else console.error(`Unknown flag: ${G}`),a(),process.exit(1)}}if(i)a(),process.exit(0);if(d)Lz(t),process.exit(0);if(s)Mz(e),process.exit(0);if(r)await Nz(P,x,f);else await Xz(P,x,f);
34
+ `)}var N=process.argv.slice(2);if(N[0]==="gesture")await Bz(N.slice(1)),process.exit(0);if(N[0]==="button")await Mz(N.slice(1)),process.exit(0);var w=3100,o=!1,x=!1,a=!1,r=!1,d=!1,f=[],s,i;for(let z=0;z<N.length;z++){let C=N[z];switch(C){case"--port":case"-p":w=parseInt(N[++z]??"3100",10);break;case"--detach":case"-d":o=!0;break;case"--quiet":case"-q":x=!0;break;case"--list":case"-l":if(a=!0,N[z+1]&&!N[z+1].startsWith("-"))s=N[++z];break;case"--kill":case"-k":if(r=!0,N[z+1]&&!N[z+1].startsWith("-"))i=N[++z];break;case"--help":case"-h":case"help":d=!0;break;default:if(!C.startsWith("-"))f.push(C);else console.error(`Unknown flag: ${C}`),l(),process.exit(1)}}if(d)l(),process.exit(0);if(a)Nz(s),process.exit(0);if(r)Lz(i),process.exit(0);if(o)await Xz(f,w,x);else await $z(f,w,x);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tail-sim",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "tail-sim": "dist/tail-sim.js"