@xenon-device-management/xenon 1.7.2 → 1.7.4

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.
Files changed (103) hide show
  1. package/lib/package.json +2 -1
  2. package/lib/public/assets/{ApiKeyGate-BousCIQZ.js → ApiKeyGate-DX4HKDBA.js} +1 -1
  3. package/lib/public/assets/{BugReportButton-Gw4lnGnV.js → BugReportButton-N-NBBb3l.js} +1 -1
  4. package/lib/public/assets/{DeviceMosaicView-BX2hfhBf.js → DeviceMosaicView-BEDnJqk5.js} +1 -1
  5. package/lib/public/assets/{EmptyState-CPOfuV3O.js → EmptyState-X7DYPoPj.js} +1 -1
  6. package/lib/public/assets/{FieldGroup-C5UkCHbe.js → FieldGroup-a9gzUh4W.js} +1 -1
  7. package/lib/public/assets/{Modal-MMGhL8eW.js → Modal-Cuva-zxU.js} +1 -1
  8. package/lib/public/assets/{SegmentedControl-CZfAicws.js → SegmentedControl-Bdc4A0UR.js} +1 -1
  9. package/lib/public/assets/{SettingCard-Dp7XIdcl.js → SettingCard-DVZUpQJR.js} +1 -1
  10. package/lib/public/assets/{Table-DsN_IX6J.js → Table-Casq-_Hh.js} +1 -1
  11. package/lib/public/assets/{activity-DCBAvxuo.js → activity-KiLkzk_U.js} +1 -1
  12. package/lib/public/assets/{ai-settings-yNS_bU1m.js → ai-settings-0dEPftlc.js} +1 -1
  13. package/lib/public/assets/{api-keys-CQ9g1g1b.js → api-keys-Cg1cbSUt.js} +1 -1
  14. package/lib/public/assets/{apps-Bqh1eav9.js → apps-CyetRRfI.js} +1 -1
  15. package/lib/public/assets/{arrow-left-GX0vzYtn.js → arrow-left-C1v7ukDM.js} +1 -1
  16. package/lib/public/assets/{arrow-up-right-DePHHOby.js → arrow-up-right-B-YmrLSY.js} +1 -1
  17. package/lib/public/assets/{auth-shell-BBGlC6i3.js → auth-shell-BXwxuIPK.js} +1 -1
  18. package/lib/public/assets/{builds-page-Czf7RsmD.js → builds-page-wquO6Xb2.js} +1 -1
  19. package/lib/public/assets/{button-ybNY3GYx.js → button-xZrEx8cE.js} +1 -1
  20. package/lib/public/assets/{check-BZMx8AhR.js → check-CDOZKQih.js} +1 -1
  21. package/lib/public/assets/{chevron-right-WL_bcB02.js → chevron-right-CnpP0W_l.js} +1 -1
  22. package/lib/public/assets/{circle-check-CI2xUlDo.js → circle-check-DDqpikKW.js} +1 -1
  23. package/lib/public/assets/{circle-x-De3thbcr.js → circle-x-DcBe5J67.js} +1 -1
  24. package/lib/public/assets/{clock-BFVLE7wW.js → clock-CfNmWqai.js} +1 -1
  25. package/lib/public/assets/{copy-ve3lNhva.js → copy-DkXT5Ggc.js} +1 -1
  26. package/lib/public/assets/{device-explorer-vzZxT4-Y.js → device-explorer-B3PK2XW9.js} +1 -1
  27. package/lib/public/assets/{download-BDZj44ke.js → download-B0tbRUtY.js} +1 -1
  28. package/lib/public/assets/{forgot-password-BSYQX2oR.js → forgot-password-DvHMwK0-.js} +1 -1
  29. package/lib/public/assets/{funnel-CptlpFZ9.js → funnel-DfsfiM0o.js} +1 -1
  30. package/lib/public/assets/{index-PtnKslHU.js → index-wbxPcwMM.js} +2 -2
  31. package/lib/public/assets/{layers-DRpwoKhI.js → layers-D-4LYQG3.js} +1 -1
  32. package/lib/public/assets/{lock-CrQhC34I.js → lock-Cf_J2NEl.js} +1 -1
  33. package/lib/public/assets/{login-DaJOt8Qz.js → login-DCIOMQb8.js} +1 -1
  34. package/lib/public/assets/{maintenance-settings-D629C2IC.js → maintenance-settings-CDDl4Q8B.js} +1 -1
  35. package/lib/public/assets/{mouse-pointer-2-Dpcc76zN.js → mouse-pointer-2-DoCqhosb.js} +1 -1
  36. package/lib/public/assets/{overview-CGEKYX62.js → overview-DNeH4FfG.js} +1 -1
  37. package/lib/public/assets/{page-header-CSvuGbSO.js → page-header-DZpQDeCw.js} +1 -1
  38. package/lib/public/assets/{plus-vJJF9g79.js → plus-CTjPrkU5.js} +1 -1
  39. package/lib/public/assets/{profile-page-CF7ZQNQz.js → profile-page-DKiZOb_Y.js} +1 -1
  40. package/lib/public/assets/{reset-password-BgpjbCac.js → reset-password-6rl6YA7S.js} +1 -1
  41. package/lib/public/assets/{runbook-page-Dxuzftyv.js → runbook-page-DTTw_cb3.js} +1 -1
  42. package/lib/public/assets/{selector-detail-page-B_puCd9g.js → selector-detail-page-BVPMp3ZK.js} +1 -1
  43. package/lib/public/assets/{selector-health-BjMHf106.js → selector-health-BVJ8fGyw.js} +1 -1
  44. package/lib/public/assets/{selector-health-page-Du_bGD2p.js → selector-health-page-CZ_AAjtV.js} +1 -1
  45. package/lib/public/assets/{session-detail-page-lQTe5_B1.js → session-detail-page-BApz2wZr.js} +1 -1
  46. package/lib/public/assets/{settings-DhxWGUzy.js → settings-CuYjTdtb.js} +1 -1
  47. package/lib/public/assets/{shield-JGzamJo5.js → shield-DVYhrtS6.js} +1 -1
  48. package/lib/public/assets/{shield-alert-DzE36jcb.js → shield-alert-BhxlmXys.js} +1 -1
  49. package/lib/public/assets/{status-pill-outline-50aN0wRU.js → status-pill-outline-DjKuXDLG.js} +1 -1
  50. package/lib/public/assets/{tablet-DHY88mxw.js → tablet-CxgLRRaU.js} +1 -1
  51. package/lib/public/assets/{teams-EOAIPbZC.js → teams-EuX6i7H2.js} +1 -1
  52. package/lib/public/assets/{trash-2-BrTSfFh5.js → trash-2-8zA1h40t.js} +1 -1
  53. package/lib/public/assets/useSocket-qyqK9PWr.js +1 -0
  54. package/lib/public/assets/{users-Dd44jLAB.js → users-CPafGHcL.js} +1 -1
  55. package/lib/public/assets/{webhook-settings-DBYmqim_.js → webhook-settings-y8EAM79b.js} +1 -1
  56. package/lib/public/assets/{zap-JUbOoK3o.js → zap-C-b2-XlO.js} +1 -1
  57. package/lib/public/index.html +1 -1
  58. package/lib/src/app/index.js +6 -0
  59. package/lib/src/app/routers/auth.js +30 -0
  60. package/lib/src/app/routers/capabilities.js +25 -0
  61. package/lib/src/app/routers/control.js +14 -0
  62. package/lib/src/app/routers/projects.js +55 -0
  63. package/lib/src/device-managers/IOSDeviceManager.js +24 -3
  64. package/lib/src/device-managers/ios/IOSStreamService.js +39 -1
  65. package/lib/src/device-managers/ios/iosBattery.js +24 -0
  66. package/lib/src/device-managers/ios/iosStreamDiagnostics.js +61 -0
  67. package/lib/src/generated/client/edge.js +22 -4
  68. package/lib/src/generated/client/index-browser.js +19 -1
  69. package/lib/src/generated/client/index.d.ts +2376 -158
  70. package/lib/src/generated/client/index.js +22 -4
  71. package/lib/src/generated/client/package.json +1 -1
  72. package/lib/src/generated/client/schema.prisma +21 -0
  73. package/lib/src/generated/client/wasm.js +19 -1
  74. package/lib/src/middleware/authMiddleware.js +64 -3
  75. package/lib/src/prisma.js +1 -1
  76. package/lib/src/services/EventLogService.js +81 -0
  77. package/lib/src/services/ServerManager.js +36 -0
  78. package/lib/src/services/SocketServer.js +2 -0
  79. package/lib/src/services/VideoPipelineService.js +51 -43
  80. package/lib/src/services/artifacts/ArtifactStore.js +60 -0
  81. package/lib/src/services/recording/RecordingOrchestrator.js +4 -5
  82. package/lib/src/services/token/JwtKeyService.js +137 -0
  83. package/lib/src/services/token/StreamTicketService.js +61 -0
  84. package/lib/test/unit/ArtifactStore.spec.js +59 -0
  85. package/lib/test/unit/CapabilitiesRouter.spec.js +18 -0
  86. package/lib/test/unit/EventLogService.spec.js +67 -0
  87. package/lib/test/unit/JwtKeyService.spec.js +86 -0
  88. package/lib/test/unit/ProjectsRouter.spec.js +45 -0
  89. package/lib/test/unit/StreamTicketService.spec.js +95 -0
  90. package/lib/test/unit/TokenEndpoint.spec.js +68 -0
  91. package/lib/test/unit/authMiddleware.bearer.spec.js +102 -0
  92. package/lib/test/unit/authMiddleware.streamTicket.spec.js +217 -0
  93. package/lib/test/unit/iosBattery.spec.js +29 -0
  94. package/lib/test/unit/iosStreamDiagnostics.spec.js +61 -0
  95. package/lib/test/unit/recording-instrumentation.spec.js +8 -0
  96. package/lib/test/unit/recording-orchestrator.spec.js +14 -0
  97. package/lib/test/unit/video-pipeline-args.spec.js +46 -0
  98. package/lib/tsconfig.tsbuildinfo +1 -1
  99. package/package.json +2 -1
  100. package/prisma/migrations/20260717063103_event_log/migration.sql +15 -0
  101. package/prisma/migrations/20260717070124_project_entity/migration.sql +10 -0
  102. package/prisma/schema.prisma +21 -0
  103. package/lib/public/assets/useSocket-BRto9a5m.js +0 -1
@@ -1,4 +1,4 @@
1
- import{g as a}from"./index-PtnKslHU.js";/**
1
+ import{g as a}from"./index-wbxPcwMM.js";/**
2
2
  * @license lucide-react v0.555.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -0,0 +1 @@
1
+ import{r as A}from"./index-wbxPcwMM.js";const y=Object.create(null);y.open="0";y.close="1";y.ping="2";y.pong="3";y.message="4";y.upgrade="5";y.noop="6";const O=Object.create(null);Object.keys(y).forEach(n=>{O[y[n]]=n});const I={type:"error",data:"parser error"},Z=typeof Blob=="function"||typeof Blob<"u"&&Object.prototype.toString.call(Blob)==="[object BlobConstructor]",ee=typeof ArrayBuffer=="function",te=n=>typeof ArrayBuffer.isView=="function"?ArrayBuffer.isView(n):n&&n.buffer instanceof ArrayBuffer,K=({type:n,data:e},t,s)=>Z&&e instanceof Blob?t?s(e):J(e,s):ee&&(e instanceof ArrayBuffer||te(e))?t?s(e):J(new Blob([e]),s):s(y[n]+(e||"")),J=(n,e)=>{const t=new FileReader;return t.onload=function(){const s=t.result.split(",")[1];e("b"+(s||""))},t.readAsDataURL(n)};function X(n){return n instanceof Uint8Array?n:n instanceof ArrayBuffer?new Uint8Array(n):new Uint8Array(n.buffer,n.byteOffset,n.byteLength)}let L;function le(n,e){if(Z&&n.data instanceof Blob)return n.data.arrayBuffer().then(X).then(e);if(ee&&(n.data instanceof ArrayBuffer||te(n.data)))return e(X(n.data));K(n,!1,t=>{L||(L=new TextEncoder),e(L.encode(t))})}const Q="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",v=typeof Uint8Array>"u"?[]:new Uint8Array(256);for(let n=0;n<Q.length;n++)v[Q.charCodeAt(n)]=n;const pe=n=>{let e=n.length*.75,t=n.length,s,i=0,r,o,a,h;n[n.length-1]==="="&&(e--,n[n.length-2]==="="&&e--);const g=new ArrayBuffer(e),f=new Uint8Array(g);for(s=0;s<t;s+=4)r=v[n.charCodeAt(s)],o=v[n.charCodeAt(s+1)],a=v[n.charCodeAt(s+2)],h=v[n.charCodeAt(s+3)],f[i++]=r<<2|o>>4,f[i++]=(o&15)<<4|a>>2,f[i++]=(a&3)<<6|h&63;return g},de=typeof ArrayBuffer=="function",W=(n,e)=>{if(typeof n!="string")return{type:"message",data:se(n,e)};const t=n.charAt(0);return t==="b"?{type:"message",data:ye(n.substring(1),e)}:O[t]?n.length>1?{type:O[t],data:n.substring(1)}:{type:O[t]}:I},ye=(n,e)=>{if(de){const t=pe(n);return se(t,e)}else return{base64:!0,data:n}},se=(n,e)=>{switch(e){case"blob":return n instanceof Blob?n:new Blob([n]);case"arraybuffer":default:return n instanceof ArrayBuffer?n:n.buffer}},ne="",ge=(n,e)=>{const t=n.length,s=new Array(t);let i=0;n.forEach((r,o)=>{K(r,!1,a=>{s[o]=a,++i===t&&e(s.join(ne))})})},me=(n,e)=>{const t=n.split(ne),s=[];for(let i=0;i<t.length;i++){const r=W(t[i],e);if(s.push(r),r.type==="error")break}return s};function _e(){return new TransformStream({transform(n,e){le(n,t=>{const s=t.length;let i;if(s<126)i=new Uint8Array(1),new DataView(i.buffer).setUint8(0,s);else if(s<65536){i=new Uint8Array(3);const r=new DataView(i.buffer);r.setUint8(0,126),r.setUint16(1,s)}else{i=new Uint8Array(9);const r=new DataView(i.buffer);r.setUint8(0,127),r.setBigUint64(1,BigInt(s))}n.data&&typeof n.data!="string"&&(i[0]|=128),e.enqueue(i),e.enqueue(t)})}})}let P;function T(n){return n.reduce((e,t)=>e+t.length,0)}function R(n,e){if(n[0].length===e)return n.shift();const t=new Uint8Array(e);let s=0;for(let i=0;i<e;i++)t[i]=n[0][s++],s===n[0].length&&(n.shift(),s=0);return n.length&&s<n[0].length&&(n[0]=n[0].slice(s)),t}function be(n,e){P||(P=new TextDecoder);const t=[];let s=0,i=-1,r=!1;return new TransformStream({transform(o,a){for(t.push(o);;){if(s===0){if(T(t)<1)break;const h=R(t,1);r=(h[0]&128)===128,i=h[0]&127,i<126?s=3:i===126?s=1:s=2}else if(s===1){if(T(t)<2)break;const h=R(t,2);i=new DataView(h.buffer,h.byteOffset,h.length).getUint16(0),s=3}else if(s===2){if(T(t)<8)break;const h=R(t,8),g=new DataView(h.buffer,h.byteOffset,h.length),f=g.getUint32(0);if(f>Math.pow(2,21)-1){a.enqueue(I);break}i=f*Math.pow(2,32)+g.getUint32(4),s=3}else{if(T(t)<i)break;const h=R(t,i);a.enqueue(W(r?h:P.decode(h),e)),s=0}if(i===0||i>n){a.enqueue(I);break}}}})}const ie=4;function u(n){if(n)return we(n)}function we(n){for(var e in u.prototype)n[e]=u.prototype[e];return n}u.prototype.on=u.prototype.addEventListener=function(n,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+n]=this._callbacks["$"+n]||[]).push(e),this};u.prototype.once=function(n,e){function t(){this.off(n,t),e.apply(this,arguments)}return t.fn=e,this.on(n,t),this};u.prototype.off=u.prototype.removeListener=u.prototype.removeAllListeners=u.prototype.removeEventListener=function(n,e){if(this._callbacks=this._callbacks||{},arguments.length==0)return this._callbacks={},this;var t=this._callbacks["$"+n];if(!t)return this;if(arguments.length==1)return delete this._callbacks["$"+n],this;for(var s,i=0;i<t.length;i++)if(s=t[i],s===e||s.fn===e){t.splice(i,1);break}return t.length===0&&delete this._callbacks["$"+n],this};u.prototype.emit=function(n){this._callbacks=this._callbacks||{};for(var e=new Array(arguments.length-1),t=this._callbacks["$"+n],s=1;s<arguments.length;s++)e[s-1]=arguments[s];if(t){t=t.slice(0);for(var s=0,i=t.length;s<i;++s)t[s].apply(this,e)}return this};u.prototype.emitReserved=u.prototype.emit;u.prototype.listeners=function(n){return this._callbacks=this._callbacks||{},this._callbacks["$"+n]||[]};u.prototype.hasListeners=function(n){return!!this.listeners(n).length};const x=typeof Promise=="function"&&typeof Promise.resolve=="function"?e=>Promise.resolve().then(e):(e,t)=>t(e,0),l=typeof self<"u"?self:typeof window<"u"?window:Function("return this")(),Ee="arraybuffer";function re(n,...e){return e.reduce((t,s)=>(n.hasOwnProperty(s)&&(t[s]=n[s]),t),{})}const ve=l.setTimeout,ke=l.clearTimeout;function N(n,e){e.useNativeTimers?(n.setTimeoutFn=ve.bind(l),n.clearTimeoutFn=ke.bind(l)):(n.setTimeoutFn=l.setTimeout.bind(l),n.clearTimeoutFn=l.clearTimeout.bind(l))}const Ae=1.33;function Te(n){return typeof n=="string"?Re(n):Math.ceil((n.byteLength||n.size)*Ae)}function Re(n){let e=0,t=0;for(let s=0,i=n.length;s<i;s++)e=n.charCodeAt(s),e<128?t+=1:e<2048?t+=2:e<55296||e>=57344?t+=3:(s++,t+=4);return t}function oe(){return Date.now().toString(36).substring(3)+Math.random().toString(36).substring(2,5)}function Oe(n){let e="";for(let t in n)n.hasOwnProperty(t)&&(e.length&&(e+="&"),e+=encodeURIComponent(t)+"="+encodeURIComponent(n[t]));return e}function Se(n){let e={},t=n.split("&");for(let s=0,i=t.length;s<i;s++){let r=t[s].split("=");e[decodeURIComponent(r[0])]=decodeURIComponent(r[1])}return e}class Ce extends Error{constructor(e,t,s){super(e),this.description=t,this.context=s,this.type="TransportError"}}class $ extends u{constructor(e){super(),this.writable=!1,N(this,e),this.opts=e,this.query=e.query,this.socket=e.socket,this.supportsBinary=!e.forceBase64}onError(e,t,s){return super.emitReserved("error",new Ce(e,t,s)),this}open(){return this.readyState="opening",this.doOpen(),this}close(){return(this.readyState==="opening"||this.readyState==="open")&&(this.doClose(),this.onClose()),this}send(e){this.readyState==="open"&&this.write(e)}onOpen(){this.readyState="open",this.writable=!0,super.emitReserved("open")}onData(e){const t=W(e,this.socket.binaryType);this.onPacket(t)}onPacket(e){super.emitReserved("packet",e)}onClose(e){this.readyState="closed",super.emitReserved("close",e)}pause(e){}createUri(e,t={}){return e+"://"+this._hostname()+this._port()+this.opts.path+this._query(t)}_hostname(){const e=this.opts.hostname;return e.indexOf(":")===-1?e:"["+e+"]"}_port(){return this.opts.port&&(this.opts.secure&&Number(this.opts.port)!==443||!this.opts.secure&&Number(this.opts.port)!==80)?":"+this.opts.port:""}_query(e){const t=Oe(e);return t.length?"?"+t:""}}class Be extends ${constructor(){super(...arguments),this._polling=!1}get name(){return"polling"}doOpen(){this._poll()}pause(e){this.readyState="pausing";const t=()=>{this.readyState="paused",e()};if(this._polling||!this.writable){let s=0;this._polling&&(s++,this.once("pollComplete",function(){--s||t()})),this.writable||(s++,this.once("drain",function(){--s||t()}))}else t()}_poll(){this._polling=!0,this.doPoll(),this.emitReserved("poll")}onData(e){const t=s=>{if(this.readyState==="opening"&&s.type==="open"&&this.onOpen(),s.type==="close")return this.onClose({description:"transport closed by the server"}),!1;this.onPacket(s)};me(e,this.socket.binaryType).forEach(t),this.readyState!=="closed"&&(this._polling=!1,this.emitReserved("pollComplete"),this.readyState==="open"&&this._poll())}doClose(){const e=()=>{this.write([{type:"close"}])};this.readyState==="open"?e():this.once("open",e)}write(e){this.writable=!1,ge(e,t=>{this.doWrite(t,()=>{this.writable=!0,this.emitReserved("drain")})})}uri(){const e=this.opts.secure?"https":"http",t=this.query||{};return this.opts.timestampRequests!==!1&&(t[this.opts.timestampParam]=oe()),!this.supportsBinary&&!t.sid&&(t.b64=1),this.createUri(e,t)}}let ce=!1;try{ce=typeof XMLHttpRequest<"u"&&"withCredentials"in new XMLHttpRequest}catch{}const xe=ce;function Ne(){}class Le extends Be{constructor(e){if(super(e),typeof location<"u"){const t=location.protocol==="https:";let s=location.port;s||(s=t?"443":"80"),this.xd=typeof location<"u"&&e.hostname!==location.hostname||s!==e.port}}doWrite(e,t){const s=this.request({method:"POST",data:e});s.on("success",t),s.on("error",(i,r)=>{this.onError("xhr post error",i,r)})}doPoll(){const e=this.request();e.on("data",this.onData.bind(this)),e.on("error",(t,s)=>{this.onError("xhr poll error",t,s)}),this.pollXhr=e}}class d extends u{constructor(e,t,s){super(),this.createRequest=e,N(this,s),this._opts=s,this._method=s.method||"GET",this._uri=t,this._data=s.data!==void 0?s.data:null,this._create()}_create(){var e;const t=re(this._opts,"agent","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","autoUnref");t.xdomain=!!this._opts.xd;const s=this._xhr=this.createRequest(t);try{s.open(this._method,this._uri,!0);try{if(this._opts.extraHeaders){s.setDisableHeaderCheck&&s.setDisableHeaderCheck(!0);for(let i in this._opts.extraHeaders)this._opts.extraHeaders.hasOwnProperty(i)&&s.setRequestHeader(i,this._opts.extraHeaders[i])}}catch{}if(this._method==="POST")try{s.setRequestHeader("Content-type","text/plain;charset=UTF-8")}catch{}try{s.setRequestHeader("Accept","*/*")}catch{}(e=this._opts.cookieJar)===null||e===void 0||e.addCookies(s),"withCredentials"in s&&(s.withCredentials=this._opts.withCredentials),this._opts.requestTimeout&&(s.timeout=this._opts.requestTimeout),s.onreadystatechange=()=>{var i;s.readyState===3&&((i=this._opts.cookieJar)===null||i===void 0||i.parseCookies(s.getResponseHeader("set-cookie"))),s.readyState===4&&(s.status===200||s.status===1223?this._onLoad():this.setTimeoutFn(()=>{this._onError(typeof s.status=="number"?s.status:0)},0))},s.send(this._data)}catch(i){this.setTimeoutFn(()=>{this._onError(i)},0);return}typeof document<"u"&&(this._index=d.requestsCount++,d.requests[this._index]=this)}_onError(e){this.emitReserved("error",e,this._xhr),this._cleanup(!0)}_cleanup(e){if(!(typeof this._xhr>"u"||this._xhr===null)){if(this._xhr.onreadystatechange=Ne,e)try{this._xhr.abort()}catch{}typeof document<"u"&&delete d.requests[this._index],this._xhr=null}}_onLoad(){const e=this._xhr.responseText;e!==null&&(this.emitReserved("data",e),this.emitReserved("success"),this._cleanup())}abort(){this._cleanup()}}d.requestsCount=0;d.requests={};if(typeof document<"u"){if(typeof attachEvent=="function")attachEvent("onunload",j);else if(typeof addEventListener=="function"){const n="onpagehide"in l?"pagehide":"unload";addEventListener(n,j,!1)}}function j(){for(let n in d.requests)d.requests.hasOwnProperty(n)&&d.requests[n].abort()}const Pe=function(){const n=ae({xdomain:!1});return n&&n.responseType!==null}();class qe extends Le{constructor(e){super(e);const t=e&&e.forceBase64;this.supportsBinary=Pe&&!t}request(e={}){return Object.assign(e,{xd:this.xd},this.opts),new d(ae,this.uri(),e)}}function ae(n){const e=n.xdomain;try{if(typeof XMLHttpRequest<"u"&&(!e||xe))return new XMLHttpRequest}catch{}if(!e)try{return new l[["Active"].concat("Object").join("X")]("Microsoft.XMLHTTP")}catch{}}const he=typeof navigator<"u"&&typeof navigator.product=="string"&&navigator.product.toLowerCase()==="reactnative";class De extends ${get name(){return"websocket"}doOpen(){const e=this.uri(),t=this.opts.protocols,s=he?{}:re(this.opts,"agent","perMessageDeflate","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","localAddress","protocolVersion","origin","maxPayload","family","checkServerIdentity");this.opts.extraHeaders&&(s.headers=this.opts.extraHeaders);try{this.ws=this.createSocket(e,t,s)}catch(i){return this.emitReserved("error",i)}this.ws.binaryType=this.socket.binaryType,this.addEventListeners()}addEventListeners(){this.ws.onopen=()=>{this.opts.autoUnref&&this.ws._socket.unref(),this.onOpen()},this.ws.onclose=e=>this.onClose({description:"websocket connection closed",context:e}),this.ws.onmessage=e=>this.onData(e.data),this.ws.onerror=e=>this.onError("websocket error",e)}write(e){this.writable=!1;for(let t=0;t<e.length;t++){const s=e[t],i=t===e.length-1;K(s,this.supportsBinary,r=>{try{this.doWrite(s,r)}catch{}i&&x(()=>{this.writable=!0,this.emitReserved("drain")},this.setTimeoutFn)})}}doClose(){typeof this.ws<"u"&&(this.ws.onerror=()=>{},this.ws.close(),this.ws=null)}uri(){const e=this.opts.secure?"wss":"ws",t=this.query||{};return this.opts.timestampRequests&&(t[this.opts.timestampParam]=oe()),this.supportsBinary||(t.b64=1),this.createUri(e,t)}}const q=l.WebSocket||l.MozWebSocket;class Ie extends De{createSocket(e,t,s){return he?new q(e,t,s):t?new q(e,t):new q(e)}doWrite(e,t){this.ws.send(t)}}class Ue extends ${get name(){return"webtransport"}doOpen(){try{this._transport=new WebTransport(this.createUri("https"),this.opts.transportOptions[this.name])}catch(e){return this.emitReserved("error",e)}this._transport.closed.then(()=>{this.onClose()}).catch(e=>{this.onError("webtransport error",e)}),this._transport.ready.then(()=>{this._transport.createBidirectionalStream().then(e=>{const t=be(Number.MAX_SAFE_INTEGER,this.socket.binaryType),s=e.readable.pipeThrough(t).getReader(),i=_e();i.readable.pipeTo(e.writable),this._writer=i.writable.getWriter();const r=()=>{s.read().then(({done:a,value:h})=>{a||(this.onPacket(h),r())}).catch(a=>{})};r();const o={type:"open"};this.query.sid&&(o.data=`{"sid":"${this.query.sid}"}`),this._writer.write(o).then(()=>this.onOpen())})})}write(e){this.writable=!1;for(let t=0;t<e.length;t++){const s=e[t],i=t===e.length-1;this._writer.write(s).then(()=>{i&&x(()=>{this.writable=!0,this.emitReserved("drain")},this.setTimeoutFn)})}}doClose(){var e;(e=this._transport)===null||e===void 0||e.close()}}const Fe={websocket:Ie,webtransport:Ue,polling:qe},Ve=/^(?:(?![^:@\/?#]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@\/?#]*)(?::([^:@\/?#]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,Me=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];function U(n){if(n.length>8e3)throw"URI too long";const e=n,t=n.indexOf("["),s=n.indexOf("]");t!=-1&&s!=-1&&(n=n.substring(0,t)+n.substring(t,s).replace(/:/g,";")+n.substring(s,n.length));let i=Ve.exec(n||""),r={},o=14;for(;o--;)r[Me[o]]=i[o]||"";return t!=-1&&s!=-1&&(r.source=e,r.host=r.host.substring(1,r.host.length-1).replace(/;/g,":"),r.authority=r.authority.replace("[","").replace("]","").replace(/;/g,":"),r.ipv6uri=!0),r.pathNames=He(r,r.path),r.queryKey=Ke(r,r.query),r}function He(n,e){const t=/\/{2,9}/g,s=e.replace(t,"/").split("/");return(e.slice(0,1)=="/"||e.length===0)&&s.splice(0,1),e.slice(-1)=="/"&&s.splice(s.length-1,1),s}function Ke(n,e){const t={};return e.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,function(s,i,r){i&&(t[i]=r)}),t}const F=typeof addEventListener=="function"&&typeof removeEventListener=="function",S=[];F&&addEventListener("offline",()=>{S.forEach(n=>n())},!1);class _ extends u{constructor(e,t){if(super(),this.binaryType=Ee,this.writeBuffer=[],this._prevBufferLen=0,this._pingInterval=-1,this._pingTimeout=-1,this._maxPayload=-1,this._pingTimeoutTime=1/0,e&&typeof e=="object"&&(t=e,e=null),e){const s=U(e);t.hostname=s.host,t.secure=s.protocol==="https"||s.protocol==="wss",t.port=s.port,s.query&&(t.query=s.query)}else t.host&&(t.hostname=U(t.host).host);N(this,t),this.secure=t.secure!=null?t.secure:typeof location<"u"&&location.protocol==="https:",t.hostname&&!t.port&&(t.port=this.secure?"443":"80"),this.hostname=t.hostname||(typeof location<"u"?location.hostname:"localhost"),this.port=t.port||(typeof location<"u"&&location.port?location.port:this.secure?"443":"80"),this.transports=[],this._transportsByName={},t.transports.forEach(s=>{const i=s.prototype.name;this.transports.push(i),this._transportsByName[i]=s}),this.opts=Object.assign({path:"/engine.io",agent:!1,withCredentials:!1,upgrade:!0,timestampParam:"t",rememberUpgrade:!1,addTrailingSlash:!0,rejectUnauthorized:!0,perMessageDeflate:{threshold:1024},transportOptions:{},closeOnBeforeunload:!1},t),this.opts.path=this.opts.path.replace(/\/$/,"")+(this.opts.addTrailingSlash?"/":""),typeof this.opts.query=="string"&&(this.opts.query=Se(this.opts.query)),F&&(this.opts.closeOnBeforeunload&&(this._beforeunloadEventListener=()=>{this.transport&&(this.transport.removeAllListeners(),this.transport.close())},addEventListener("beforeunload",this._beforeunloadEventListener,!1)),this.hostname!=="localhost"&&(this._offlineEventListener=()=>{this._onClose("transport close",{description:"network connection lost"})},S.push(this._offlineEventListener))),this.opts.withCredentials&&(this._cookieJar=void 0),this._open()}createTransport(e){const t=Object.assign({},this.opts.query);t.EIO=ie,t.transport=e,this.id&&(t.sid=this.id);const s=Object.assign({},this.opts,{query:t,socket:this,hostname:this.hostname,secure:this.secure,port:this.port},this.opts.transportOptions[e]);return new this._transportsByName[e](s)}_open(){if(this.transports.length===0){this.setTimeoutFn(()=>{this.emitReserved("error","No transports available")},0);return}const e=this.opts.rememberUpgrade&&_.priorWebsocketSuccess&&this.transports.indexOf("websocket")!==-1?"websocket":this.transports[0];this.readyState="opening";const t=this.createTransport(e);t.open(),this.setTransport(t)}setTransport(e){this.transport&&this.transport.removeAllListeners(),this.transport=e,e.on("drain",this._onDrain.bind(this)).on("packet",this._onPacket.bind(this)).on("error",this._onError.bind(this)).on("close",t=>this._onClose("transport close",t))}onOpen(){this.readyState="open",_.priorWebsocketSuccess=this.transport.name==="websocket",this.emitReserved("open"),this.flush()}_onPacket(e){if(this.readyState==="opening"||this.readyState==="open"||this.readyState==="closing")switch(this.emitReserved("packet",e),this.emitReserved("heartbeat"),e.type){case"open":this.onHandshake(JSON.parse(e.data));break;case"ping":this._sendPacket("pong"),this.emitReserved("ping"),this.emitReserved("pong"),this._resetPingTimeout();break;case"error":const t=new Error("server error");t.code=e.data,this._onError(t);break;case"message":this.emitReserved("data",e.data),this.emitReserved("message",e.data);break}}onHandshake(e){this.emitReserved("handshake",e),this.id=e.sid,this.transport.query.sid=e.sid,this._pingInterval=e.pingInterval,this._pingTimeout=e.pingTimeout,this._maxPayload=e.maxPayload,this.onOpen(),this.readyState!=="closed"&&this._resetPingTimeout()}_resetPingTimeout(){this.clearTimeoutFn(this._pingTimeoutTimer);const e=this._pingInterval+this._pingTimeout;this._pingTimeoutTime=Date.now()+e,this._pingTimeoutTimer=this.setTimeoutFn(()=>{this._onClose("ping timeout")},e),this.opts.autoUnref&&this._pingTimeoutTimer.unref()}_onDrain(){this.writeBuffer.splice(0,this._prevBufferLen),this._prevBufferLen=0,this.writeBuffer.length===0?this.emitReserved("drain"):this.flush()}flush(){if(this.readyState!=="closed"&&this.transport.writable&&!this.upgrading&&this.writeBuffer.length){const e=this._getWritablePackets();this.transport.send(e),this._prevBufferLen=e.length,this.emitReserved("flush")}}_getWritablePackets(){if(!(this._maxPayload&&this.transport.name==="polling"&&this.writeBuffer.length>1))return this.writeBuffer;let t=1;for(let s=0;s<this.writeBuffer.length;s++){const i=this.writeBuffer[s].data;if(i&&(t+=Te(i)),s>0&&t>this._maxPayload)return this.writeBuffer.slice(0,s);t+=2}return this.writeBuffer}_hasPingExpired(){if(!this._pingTimeoutTime)return!0;const e=Date.now()>this._pingTimeoutTime;return e&&(this._pingTimeoutTime=0,x(()=>{this._onClose("ping timeout")},this.setTimeoutFn)),e}write(e,t,s){return this._sendPacket("message",e,t,s),this}send(e,t,s){return this._sendPacket("message",e,t,s),this}_sendPacket(e,t,s,i){if(typeof t=="function"&&(i=t,t=void 0),typeof s=="function"&&(i=s,s=null),this.readyState==="closing"||this.readyState==="closed")return;s=s||{},s.compress=s.compress!==!1;const r={type:e,data:t,options:s};this.emitReserved("packetCreate",r),this.writeBuffer.push(r),i&&this.once("flush",i),this.flush()}close(){const e=()=>{this._onClose("forced close"),this.transport.close()},t=()=>{this.off("upgrade",t),this.off("upgradeError",t),e()},s=()=>{this.once("upgrade",t),this.once("upgradeError",t)};return(this.readyState==="opening"||this.readyState==="open")&&(this.readyState="closing",this.writeBuffer.length?this.once("drain",()=>{this.upgrading?s():e()}):this.upgrading?s():e()),this}_onError(e){if(_.priorWebsocketSuccess=!1,this.opts.tryAllTransports&&this.transports.length>1&&this.readyState==="opening")return this.transports.shift(),this._open();this.emitReserved("error",e),this._onClose("transport error",e)}_onClose(e,t){if(this.readyState==="opening"||this.readyState==="open"||this.readyState==="closing"){if(this.clearTimeoutFn(this._pingTimeoutTimer),this.transport.removeAllListeners("close"),this.transport.close(),this.transport.removeAllListeners(),F&&(this._beforeunloadEventListener&&removeEventListener("beforeunload",this._beforeunloadEventListener,!1),this._offlineEventListener)){const s=S.indexOf(this._offlineEventListener);s!==-1&&S.splice(s,1)}this.readyState="closed",this.id=null,this.emitReserved("close",e,t),this.writeBuffer=[],this._prevBufferLen=0}}}_.protocol=ie;class We extends _{constructor(){super(...arguments),this._upgrades=[]}onOpen(){if(super.onOpen(),this.readyState==="open"&&this.opts.upgrade)for(let e=0;e<this._upgrades.length;e++)this._probe(this._upgrades[e])}_probe(e){let t=this.createTransport(e),s=!1;_.priorWebsocketSuccess=!1;const i=()=>{s||(t.send([{type:"ping",data:"probe"}]),t.once("packet",m=>{if(!s)if(m.type==="pong"&&m.data==="probe"){if(this.upgrading=!0,this.emitReserved("upgrading",t),!t)return;_.priorWebsocketSuccess=t.name==="websocket",this.transport.pause(()=>{s||this.readyState!=="closed"&&(f(),this.setTransport(t),t.send([{type:"upgrade"}]),this.emitReserved("upgrade",t),t=null,this.upgrading=!1,this.flush())})}else{const w=new Error("probe error");w.transport=t.name,this.emitReserved("upgradeError",w)}}))};function r(){s||(s=!0,f(),t.close(),t=null)}const o=m=>{const w=new Error("probe error: "+m);w.transport=t.name,r(),this.emitReserved("upgradeError",w)};function a(){o("transport closed")}function h(){o("socket closed")}function g(m){t&&m.name!==t.name&&r()}const f=()=>{t.removeListener("open",i),t.removeListener("error",o),t.removeListener("close",a),this.off("close",h),this.off("upgrading",g)};t.once("open",i),t.once("error",o),t.once("close",a),this.once("close",h),this.once("upgrading",g),this._upgrades.indexOf("webtransport")!==-1&&e!=="webtransport"?this.setTimeoutFn(()=>{s||t.open()},200):t.open()}onHandshake(e){this._upgrades=this._filterUpgrades(e.upgrades),super.onHandshake(e)}_filterUpgrades(e){const t=[];for(let s=0;s<e.length;s++)~this.transports.indexOf(e[s])&&t.push(e[s]);return t}}let $e=class extends We{constructor(e,t={}){const s=typeof e=="object"?e:t;(!s.transports||s.transports&&typeof s.transports[0]=="string")&&(s.transports=(s.transports||["polling","websocket","webtransport"]).map(i=>Fe[i]).filter(i=>!!i)),super(e,s)}};function Ye(n,e="",t){let s=n;t=t||typeof location<"u"&&location,n==null&&(n=t.protocol+"//"+t.host),typeof n=="string"&&(n.charAt(0)==="/"&&(n.charAt(1)==="/"?n=t.protocol+n:n=t.host+n),/^(https?|wss?):\/\//.test(n)||(typeof t<"u"?n=t.protocol+"//"+n:n="https://"+n),s=U(n)),s.port||(/^(http|ws)$/.test(s.protocol)?s.port="80":/^(http|ws)s$/.test(s.protocol)&&(s.port="443")),s.path=s.path||"/";const r=s.host.indexOf(":")!==-1?"["+s.host+"]":s.host;return s.id=s.protocol+"://"+r+":"+s.port+e,s.href=s.protocol+"://"+r+(t&&t.port===s.port?"":":"+s.port),s}const ze=typeof ArrayBuffer=="function",Je=n=>typeof ArrayBuffer.isView=="function"?ArrayBuffer.isView(n):n.buffer instanceof ArrayBuffer,ue=Object.prototype.toString,Xe=typeof Blob=="function"||typeof Blob<"u"&&ue.call(Blob)==="[object BlobConstructor]",Qe=typeof File=="function"||typeof File<"u"&&ue.call(File)==="[object FileConstructor]";function Y(n){return ze&&(n instanceof ArrayBuffer||Je(n))||Xe&&n instanceof Blob||Qe&&n instanceof File}function C(n,e){if(!n||typeof n!="object")return!1;if(Array.isArray(n)){for(let t=0,s=n.length;t<s;t++)if(C(n[t]))return!0;return!1}if(Y(n))return!0;if(n.toJSON&&typeof n.toJSON=="function"&&arguments.length===1)return C(n.toJSON(),!0);for(const t in n)if(Object.prototype.hasOwnProperty.call(n,t)&&C(n[t]))return!0;return!1}function je(n){const e=[],t=n.data,s=n;return s.data=V(t,e),s.attachments=e.length,{packet:s,buffers:e}}function V(n,e){if(!n)return n;if(Y(n)){const t={_placeholder:!0,num:e.length};return e.push(n),t}else if(Array.isArray(n)){const t=new Array(n.length);for(let s=0;s<n.length;s++)t[s]=V(n[s],e);return t}else if(typeof n=="object"&&!(n instanceof Date)){const t={};for(const s in n)Object.prototype.hasOwnProperty.call(n,s)&&(t[s]=V(n[s],e));return t}return n}function Ge(n,e){return n.data=M(n.data,e),delete n.attachments,n}function M(n,e){if(!n)return n;if(n&&n._placeholder===!0){if(typeof n.num=="number"&&n.num>=0&&n.num<e.length)return e[n.num];throw new Error("illegal attachments")}else if(Array.isArray(n))for(let t=0;t<n.length;t++)n[t]=M(n[t],e);else if(typeof n=="object")for(const t in n)Object.prototype.hasOwnProperty.call(n,t)&&(n[t]=M(n[t],e));return n}const Ze=["connect","connect_error","disconnect","disconnecting","newListener","removeListener"];var c;(function(n){n[n.CONNECT=0]="CONNECT",n[n.DISCONNECT=1]="DISCONNECT",n[n.EVENT=2]="EVENT",n[n.ACK=3]="ACK",n[n.CONNECT_ERROR=4]="CONNECT_ERROR",n[n.BINARY_EVENT=5]="BINARY_EVENT",n[n.BINARY_ACK=6]="BINARY_ACK"})(c||(c={}));class et{constructor(e){this.replacer=e}encode(e){return(e.type===c.EVENT||e.type===c.ACK)&&C(e)?this.encodeAsBinary({type:e.type===c.EVENT?c.BINARY_EVENT:c.BINARY_ACK,nsp:e.nsp,data:e.data,id:e.id}):[this.encodeAsString(e)]}encodeAsString(e){let t=""+e.type;return(e.type===c.BINARY_EVENT||e.type===c.BINARY_ACK)&&(t+=e.attachments+"-"),e.nsp&&e.nsp!=="/"&&(t+=e.nsp+","),e.id!=null&&(t+=e.id),e.data!=null&&(t+=JSON.stringify(e.data,this.replacer)),t}encodeAsBinary(e){const t=je(e),s=this.encodeAsString(t.packet),i=t.buffers;return i.unshift(s),i}}class z extends u{constructor(e){super(),this.reviver=e}add(e){let t;if(typeof e=="string"){if(this.reconstructor)throw new Error("got plaintext data when reconstructing a packet");t=this.decodeString(e);const s=t.type===c.BINARY_EVENT;s||t.type===c.BINARY_ACK?(t.type=s?c.EVENT:c.ACK,this.reconstructor=new tt(t),t.attachments===0&&super.emitReserved("decoded",t)):super.emitReserved("decoded",t)}else if(Y(e)||e.base64)if(this.reconstructor)t=this.reconstructor.takeBinaryData(e),t&&(this.reconstructor=null,super.emitReserved("decoded",t));else throw new Error("got binary data when not reconstructing a packet");else throw new Error("Unknown type: "+e)}decodeString(e){let t=0;const s={type:Number(e.charAt(0))};if(c[s.type]===void 0)throw new Error("unknown packet type "+s.type);if(s.type===c.BINARY_EVENT||s.type===c.BINARY_ACK){const r=t+1;for(;e.charAt(++t)!=="-"&&t!=e.length;);const o=e.substring(r,t);if(o!=Number(o)||e.charAt(t)!=="-")throw new Error("Illegal attachments");s.attachments=Number(o)}if(e.charAt(t+1)==="/"){const r=t+1;for(;++t&&!(e.charAt(t)===","||t===e.length););s.nsp=e.substring(r,t)}else s.nsp="/";const i=e.charAt(t+1);if(i!==""&&Number(i)==i){const r=t+1;for(;++t;){const o=e.charAt(t);if(o==null||Number(o)!=o){--t;break}if(t===e.length)break}s.id=Number(e.substring(r,t+1))}if(e.charAt(++t)){const r=this.tryParse(e.substr(t));if(z.isPayloadValid(s.type,r))s.data=r;else throw new Error("invalid payload")}return s}tryParse(e){try{return JSON.parse(e,this.reviver)}catch{return!1}}static isPayloadValid(e,t){switch(e){case c.CONNECT:return G(t);case c.DISCONNECT:return t===void 0;case c.CONNECT_ERROR:return typeof t=="string"||G(t);case c.EVENT:case c.BINARY_EVENT:return Array.isArray(t)&&(typeof t[0]=="number"||typeof t[0]=="string"&&Ze.indexOf(t[0])===-1);case c.ACK:case c.BINARY_ACK:return Array.isArray(t)}}destroy(){this.reconstructor&&(this.reconstructor.finishedReconstruction(),this.reconstructor=null)}}class tt{constructor(e){this.packet=e,this.buffers=[],this.reconPack=e}takeBinaryData(e){if(this.buffers.push(e),this.buffers.length===this.reconPack.attachments){const t=Ge(this.reconPack,this.buffers);return this.finishedReconstruction(),t}return null}finishedReconstruction(){this.reconPack=null,this.buffers=[]}}function G(n){return Object.prototype.toString.call(n)==="[object Object]"}const st=Object.freeze(Object.defineProperty({__proto__:null,Decoder:z,Encoder:et,get PacketType(){return c}},Symbol.toStringTag,{value:"Module"}));function p(n,e,t){return n.on(e,t),function(){n.off(e,t)}}const nt=Object.freeze({connect:1,connect_error:1,disconnect:1,disconnecting:1,newListener:1,removeListener:1});class fe extends u{constructor(e,t,s){super(),this.connected=!1,this.recovered=!1,this.receiveBuffer=[],this.sendBuffer=[],this._queue=[],this._queueSeq=0,this.ids=0,this.acks={},this.flags={},this.io=e,this.nsp=t,s&&s.auth&&(this.auth=s.auth),this._opts=Object.assign({},s),this.io._autoConnect&&this.open()}get disconnected(){return!this.connected}subEvents(){if(this.subs)return;const e=this.io;this.subs=[p(e,"open",this.onopen.bind(this)),p(e,"packet",this.onpacket.bind(this)),p(e,"error",this.onerror.bind(this)),p(e,"close",this.onclose.bind(this))]}get active(){return!!this.subs}connect(){return this.connected?this:(this.subEvents(),this.io._reconnecting||this.io.open(),this.io._readyState==="open"&&this.onopen(),this)}open(){return this.connect()}send(...e){return e.unshift("message"),this.emit.apply(this,e),this}emit(e,...t){var s,i,r;if(nt.hasOwnProperty(e))throw new Error('"'+e.toString()+'" is a reserved event name');if(t.unshift(e),this._opts.retries&&!this.flags.fromQueue&&!this.flags.volatile)return this._addToQueue(t),this;const o={type:c.EVENT,data:t};if(o.options={},o.options.compress=this.flags.compress!==!1,typeof t[t.length-1]=="function"){const f=this.ids++,m=t.pop();this._registerAckCallback(f,m),o.id=f}const a=(i=(s=this.io.engine)===null||s===void 0?void 0:s.transport)===null||i===void 0?void 0:i.writable,h=this.connected&&!(!((r=this.io.engine)===null||r===void 0)&&r._hasPingExpired());return this.flags.volatile&&!a||(h?(this.notifyOutgoingListeners(o),this.packet(o)):this.sendBuffer.push(o)),this.flags={},this}_registerAckCallback(e,t){var s;const i=(s=this.flags.timeout)!==null&&s!==void 0?s:this._opts.ackTimeout;if(i===void 0){this.acks[e]=t;return}const r=this.io.setTimeoutFn(()=>{delete this.acks[e];for(let a=0;a<this.sendBuffer.length;a++)this.sendBuffer[a].id===e&&this.sendBuffer.splice(a,1);t.call(this,new Error("operation has timed out"))},i),o=(...a)=>{this.io.clearTimeoutFn(r),t.apply(this,a)};o.withError=!0,this.acks[e]=o}emitWithAck(e,...t){return new Promise((s,i)=>{const r=(o,a)=>o?i(o):s(a);r.withError=!0,t.push(r),this.emit(e,...t)})}_addToQueue(e){let t;typeof e[e.length-1]=="function"&&(t=e.pop());const s={id:this._queueSeq++,tryCount:0,pending:!1,args:e,flags:Object.assign({fromQueue:!0},this.flags)};e.push((i,...r)=>(this._queue[0],i!==null?s.tryCount>this._opts.retries&&(this._queue.shift(),t&&t(i)):(this._queue.shift(),t&&t(null,...r)),s.pending=!1,this._drainQueue())),this._queue.push(s),this._drainQueue()}_drainQueue(e=!1){if(!this.connected||this._queue.length===0)return;const t=this._queue[0];t.pending&&!e||(t.pending=!0,t.tryCount++,this.flags=t.flags,this.emit.apply(this,t.args))}packet(e){e.nsp=this.nsp,this.io._packet(e)}onopen(){typeof this.auth=="function"?this.auth(e=>{this._sendConnectPacket(e)}):this._sendConnectPacket(this.auth)}_sendConnectPacket(e){this.packet({type:c.CONNECT,data:this._pid?Object.assign({pid:this._pid,offset:this._lastOffset},e):e})}onerror(e){this.connected||this.emitReserved("connect_error",e)}onclose(e,t){this.connected=!1,delete this.id,this.emitReserved("disconnect",e,t),this._clearAcks()}_clearAcks(){Object.keys(this.acks).forEach(e=>{if(!this.sendBuffer.some(s=>String(s.id)===e)){const s=this.acks[e];delete this.acks[e],s.withError&&s.call(this,new Error("socket has been disconnected"))}})}onpacket(e){if(e.nsp===this.nsp)switch(e.type){case c.CONNECT:e.data&&e.data.sid?this.onconnect(e.data.sid,e.data.pid):this.emitReserved("connect_error",new Error("It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)"));break;case c.EVENT:case c.BINARY_EVENT:this.onevent(e);break;case c.ACK:case c.BINARY_ACK:this.onack(e);break;case c.DISCONNECT:this.ondisconnect();break;case c.CONNECT_ERROR:this.destroy();const s=new Error(e.data.message);s.data=e.data.data,this.emitReserved("connect_error",s);break}}onevent(e){const t=e.data||[];e.id!=null&&t.push(this.ack(e.id)),this.connected?this.emitEvent(t):this.receiveBuffer.push(Object.freeze(t))}emitEvent(e){if(this._anyListeners&&this._anyListeners.length){const t=this._anyListeners.slice();for(const s of t)s.apply(this,e)}super.emit.apply(this,e),this._pid&&e.length&&typeof e[e.length-1]=="string"&&(this._lastOffset=e[e.length-1])}ack(e){const t=this;let s=!1;return function(...i){s||(s=!0,t.packet({type:c.ACK,id:e,data:i}))}}onack(e){const t=this.acks[e.id];typeof t=="function"&&(delete this.acks[e.id],t.withError&&e.data.unshift(null),t.apply(this,e.data))}onconnect(e,t){this.id=e,this.recovered=t&&this._pid===t,this._pid=t,this.connected=!0,this.emitBuffered(),this._drainQueue(!0),this.emitReserved("connect")}emitBuffered(){this.receiveBuffer.forEach(e=>this.emitEvent(e)),this.receiveBuffer=[],this.sendBuffer.forEach(e=>{this.notifyOutgoingListeners(e),this.packet(e)}),this.sendBuffer=[]}ondisconnect(){this.destroy(),this.onclose("io server disconnect")}destroy(){this.subs&&(this.subs.forEach(e=>e()),this.subs=void 0),this.io._destroy(this)}disconnect(){return this.connected&&this.packet({type:c.DISCONNECT}),this.destroy(),this.connected&&this.onclose("io client disconnect"),this}close(){return this.disconnect()}compress(e){return this.flags.compress=e,this}get volatile(){return this.flags.volatile=!0,this}timeout(e){return this.flags.timeout=e,this}onAny(e){return this._anyListeners=this._anyListeners||[],this._anyListeners.push(e),this}prependAny(e){return this._anyListeners=this._anyListeners||[],this._anyListeners.unshift(e),this}offAny(e){if(!this._anyListeners)return this;if(e){const t=this._anyListeners;for(let s=0;s<t.length;s++)if(e===t[s])return t.splice(s,1),this}else this._anyListeners=[];return this}listenersAny(){return this._anyListeners||[]}onAnyOutgoing(e){return this._anyOutgoingListeners=this._anyOutgoingListeners||[],this._anyOutgoingListeners.push(e),this}prependAnyOutgoing(e){return this._anyOutgoingListeners=this._anyOutgoingListeners||[],this._anyOutgoingListeners.unshift(e),this}offAnyOutgoing(e){if(!this._anyOutgoingListeners)return this;if(e){const t=this._anyOutgoingListeners;for(let s=0;s<t.length;s++)if(e===t[s])return t.splice(s,1),this}else this._anyOutgoingListeners=[];return this}listenersAnyOutgoing(){return this._anyOutgoingListeners||[]}notifyOutgoingListeners(e){if(this._anyOutgoingListeners&&this._anyOutgoingListeners.length){const t=this._anyOutgoingListeners.slice();for(const s of t)s.apply(this,e.data)}}}function b(n){n=n||{},this.ms=n.min||100,this.max=n.max||1e4,this.factor=n.factor||2,this.jitter=n.jitter>0&&n.jitter<=1?n.jitter:0,this.attempts=0}b.prototype.duration=function(){var n=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var e=Math.random(),t=Math.floor(e*this.jitter*n);n=Math.floor(e*10)&1?n+t:n-t}return Math.min(n,this.max)|0};b.prototype.reset=function(){this.attempts=0};b.prototype.setMin=function(n){this.ms=n};b.prototype.setMax=function(n){this.max=n};b.prototype.setJitter=function(n){this.jitter=n};class H extends u{constructor(e,t){var s;super(),this.nsps={},this.subs=[],e&&typeof e=="object"&&(t=e,e=void 0),t=t||{},t.path=t.path||"/socket.io",this.opts=t,N(this,t),this.reconnection(t.reconnection!==!1),this.reconnectionAttempts(t.reconnectionAttempts||1/0),this.reconnectionDelay(t.reconnectionDelay||1e3),this.reconnectionDelayMax(t.reconnectionDelayMax||5e3),this.randomizationFactor((s=t.randomizationFactor)!==null&&s!==void 0?s:.5),this.backoff=new b({min:this.reconnectionDelay(),max:this.reconnectionDelayMax(),jitter:this.randomizationFactor()}),this.timeout(t.timeout==null?2e4:t.timeout),this._readyState="closed",this.uri=e;const i=t.parser||st;this.encoder=new i.Encoder,this.decoder=new i.Decoder,this._autoConnect=t.autoConnect!==!1,this._autoConnect&&this.open()}reconnection(e){return arguments.length?(this._reconnection=!!e,e||(this.skipReconnect=!0),this):this._reconnection}reconnectionAttempts(e){return e===void 0?this._reconnectionAttempts:(this._reconnectionAttempts=e,this)}reconnectionDelay(e){var t;return e===void 0?this._reconnectionDelay:(this._reconnectionDelay=e,(t=this.backoff)===null||t===void 0||t.setMin(e),this)}randomizationFactor(e){var t;return e===void 0?this._randomizationFactor:(this._randomizationFactor=e,(t=this.backoff)===null||t===void 0||t.setJitter(e),this)}reconnectionDelayMax(e){var t;return e===void 0?this._reconnectionDelayMax:(this._reconnectionDelayMax=e,(t=this.backoff)===null||t===void 0||t.setMax(e),this)}timeout(e){return arguments.length?(this._timeout=e,this):this._timeout}maybeReconnectOnOpen(){!this._reconnecting&&this._reconnection&&this.backoff.attempts===0&&this.reconnect()}open(e){if(~this._readyState.indexOf("open"))return this;this.engine=new $e(this.uri,this.opts);const t=this.engine,s=this;this._readyState="opening",this.skipReconnect=!1;const i=p(t,"open",function(){s.onopen(),e&&e()}),r=a=>{this.cleanup(),this._readyState="closed",this.emitReserved("error",a),e?e(a):this.maybeReconnectOnOpen()},o=p(t,"error",r);if(this._timeout!==!1){const a=this._timeout,h=this.setTimeoutFn(()=>{i(),r(new Error("timeout")),t.close()},a);this.opts.autoUnref&&h.unref(),this.subs.push(()=>{this.clearTimeoutFn(h)})}return this.subs.push(i),this.subs.push(o),this}connect(e){return this.open(e)}onopen(){this.cleanup(),this._readyState="open",this.emitReserved("open");const e=this.engine;this.subs.push(p(e,"ping",this.onping.bind(this)),p(e,"data",this.ondata.bind(this)),p(e,"error",this.onerror.bind(this)),p(e,"close",this.onclose.bind(this)),p(this.decoder,"decoded",this.ondecoded.bind(this)))}onping(){this.emitReserved("ping")}ondata(e){try{this.decoder.add(e)}catch(t){this.onclose("parse error",t)}}ondecoded(e){x(()=>{this.emitReserved("packet",e)},this.setTimeoutFn)}onerror(e){this.emitReserved("error",e)}socket(e,t){let s=this.nsps[e];return s?this._autoConnect&&!s.active&&s.connect():(s=new fe(this,e,t),this.nsps[e]=s),s}_destroy(e){const t=Object.keys(this.nsps);for(const s of t)if(this.nsps[s].active)return;this._close()}_packet(e){const t=this.encoder.encode(e);for(let s=0;s<t.length;s++)this.engine.write(t[s],e.options)}cleanup(){this.subs.forEach(e=>e()),this.subs.length=0,this.decoder.destroy()}_close(){this.skipReconnect=!0,this._reconnecting=!1,this.onclose("forced close")}disconnect(){return this._close()}onclose(e,t){var s;this.cleanup(),(s=this.engine)===null||s===void 0||s.close(),this.backoff.reset(),this._readyState="closed",this.emitReserved("close",e,t),this._reconnection&&!this.skipReconnect&&this.reconnect()}reconnect(){if(this._reconnecting||this.skipReconnect)return this;const e=this;if(this.backoff.attempts>=this._reconnectionAttempts)this.backoff.reset(),this.emitReserved("reconnect_failed"),this._reconnecting=!1;else{const t=this.backoff.duration();this._reconnecting=!0;const s=this.setTimeoutFn(()=>{e.skipReconnect||(this.emitReserved("reconnect_attempt",e.backoff.attempts),!e.skipReconnect&&e.open(i=>{i?(e._reconnecting=!1,e.reconnect(),this.emitReserved("reconnect_error",i)):e.onreconnect()}))},t);this.opts.autoUnref&&s.unref(),this.subs.push(()=>{this.clearTimeoutFn(s)})}}onreconnect(){const e=this.backoff.attempts;this._reconnecting=!1,this.backoff.reset(),this.emitReserved("reconnect",e)}}const E={};function B(n,e){typeof n=="object"&&(e=n,n=void 0),e=e||{};const t=Ye(n,e.path||"/socket.io"),s=t.source,i=t.id,r=t.path,o=E[i]&&r in E[i].nsps,a=e.forceNew||e["force new connection"]||e.multiplex===!1||o;let h;return a?h=new H(s,e):(E[i]||(E[i]=new H(s,e)),h=E[i]),t.query&&!e.query&&(e.query=t.queryKey),h.socket(t.path,e)}Object.assign(B,{Manager:H,Socket:fe,io:B,connect:B});let D=null;const k=new Map;function it(n,e){const t=k.get(n);t&&Array.from(t).forEach(s=>s(e))}function rt(){if(D)return D;const n=B(window.location.origin,{path:"/socket.io",reconnection:!0,withCredentials:!0});return n.on("connect",()=>{console.log("[Socket] Connected to Hub"),n.emit("register_dashboard")}),n.on("disconnect",()=>{console.log("[Socket] Disconnected from Hub")}),n.onAny((e,t)=>it(e,t)),D=n,n}function ot(n,e){let t=k.get(n);return t||(t=new Set,k.set(n,t)),t.add(e),()=>{const s=k.get(n);s&&(s.delete(e),s.size===0&&k.delete(n))}}const ht=()=>{const[n]=A.useState(rt),[e,t]=A.useState(n.connected);A.useEffect(()=>{const i=()=>t(!0),r=()=>t(!1);return n.on("connect",i),n.on("disconnect",r),t(n.connected),()=>{n.off("connect",i),n.off("disconnect",r)}},[n]);const s=A.useCallback((i,r)=>ot(i,r),[]);return{socket:n,isConnected:e,on:s}};export{ht as u};
@@ -1,4 +1,4 @@
1
- import{g,b as w,r as s,j as e,z as S,f as E}from"./index-PtnKslHU.js";import{l as k,d as M,c as C,u as R}from"./users-cwrwUhwo.js";import{f as A}from"./time-B8FIgwPE.js";import{P as I}from"./page-header-CSvuGbSO.js";import{P}from"./plus-vJJF9g79.js";import{T as D}from"./trash-2-BrTSfFh5.js";/**
1
+ import{g,b as w,r as s,j as e,z as S,f as E}from"./index-wbxPcwMM.js";import{l as k,d as M,c as C,u as R}from"./users-cwrwUhwo.js";import{f as A}from"./time-B8FIgwPE.js";import{P as I}from"./page-header-DZpQDeCw.js";import{P}from"./plus-CTjPrkU5.js";import{T as D}from"./trash-2-8zA1h40t.js";/**
2
2
  * @license lucide-react v0.555.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{m as P,r,X as c,j as e,x as u,v as b,C as U,H as D}from"./index-PtnKslHU.js";/* empty css */import{F as S}from"./FieldGroup-C5UkCHbe.js";import{P as H}from"./page-header-CSvuGbSO.js";import{T as O}from"./trash-2-BrTSfFh5.js";import{P as x}from"./plus-vJJF9g79.js";import{C as V}from"./circle-x-De3thbcr.js";import{C as B}from"./circle-check-CI2xUlDo.js";import{Z as I}from"./zap-JUbOoK3o.js";import{A as X}from"./activity-DCBAvxuo.js";const C=[{id:"device_offline",label:"Device Offline",icon:U,tone:"red"},{id:"device_new",label:"New Device",icon:x,tone:"green"},{id:"session_failed",label:"Session Failed",icon:V,tone:"amber"},{id:"selector_health_digest",label:"Selector Health Digest",icon:D,tone:"green"}],J=["udid","host","name","sessionId","failureReason","eventType","platform"],ae=()=>{const{toast:o}=P(),[g,T]=r.useState([]),[A,w]=r.useState(!0),[l,j]=r.useState(""),[d,k]=r.useState(["device_offline","session_failed"]),[h,f]=r.useState(!1),[v,N]=r.useState(!1),[n,_]=r.useState(!1),[y,m]=r.useState("");r.useEffect(()=>{p()},[]);const p=async()=>{w(!0);try{const s=await c.getWebhookConfigs();T(s||[])}catch(s){console.error("Failed to load webhook configs",s),o("Failed to load webhooks.","error")}finally{w(!1)}},z=async()=>{if(!l.trim()){o("Webhook URL is required.","error");return}if(d.length===0){o("Select at least one trigger event.","error");return}f(!0);try{await c.addWebhookConfig(l.trim(),d,"slack",y||void 0),j(""),m(""),_(!1),k(["device_offline","session_failed"]),await p(),o("Webhook saved.","success")}catch(s){console.error("Failed to add webhook",s),o(`Failed to add webhook: ${s.message||"unknown error"}`,"error")}finally{f(!1)}},E=async(s,t)=>{if(window.confirm(`Remove webhook for "${t}"?`))try{await c.deleteWebhookConfig(s),await p(),o("Webhook removed.","success")}catch(a){console.error("Failed to delete webhook",a),o(`Failed to delete: ${a.message||"unknown error"}`,"error")}},L=async()=>{if(l.trim()){N(!0);try{await c.testWebhook(l.trim(),"slack"),o("Test payload delivered.","success")}catch(s){o(`Test failed: ${s.message||"check the URL"}`,"error")}finally{N(!1)}}},$=s=>{k(t=>t.includes(s)?t.filter(a=>a!==s):[...t,s])},F=s=>{m(t=>t+`{{${s}}} `)};return e.jsxs("div",{className:"settings-container",children:[e.jsx(H,{icon:u,title:"Notifications",subtitle:"Configure Slack or generic webhooks to receive alerts for critical infrastructure events."}),e.jsxs("div",{className:"settings-content",children:[A?e.jsxs("div",{className:"settings-loading",style:{height:200},children:[e.jsx(b,{className:"animate-spin",size:28}),e.jsx("span",{children:"Loading webhooks…"})]}):g.length===0?e.jsxs("div",{className:"empty-state",children:[e.jsx("div",{className:"empty-state__icon",children:e.jsx(u,{size:28})}),e.jsx("h3",{className:"empty-state__title",children:"No webhooks configured"}),e.jsx("p",{className:"empty-state__copy",children:"Wire up a Slack incoming webhook or any HTTPS endpoint to receive alerts when devices go offline, new devices register, or sessions fail. Configure your first webhook below."})]}):e.jsx("div",{className:"webhook-list-grid",children:g.map(s=>{const t=(()=>{try{return JSON.parse(s.events)}catch{return[]}})();return e.jsxs("div",{className:"webhook-row-card",children:[e.jsxs("div",{className:"webhook-row-card__header",children:[e.jsx("span",{className:`pill-chip ${s.payloadTemplate?"pill-chip--admin":"pill-chip--scope"}`,children:s.payloadTemplate?"CUSTOM":"SLACK"}),e.jsx("span",{className:"webhook-row-card__url",title:s.url,children:s.url}),e.jsxs("button",{type:"button",className:"row-action row-action--danger",onClick:()=>E(s.id,s.url),"aria-label":`Remove webhook for ${s.url}`,children:[e.jsx(O,{size:13}),e.jsx("span",{children:"Remove"})]})]}),e.jsx("div",{className:"webhook-row-card__events",children:t.length===0?e.jsx("span",{className:"webhook-row-card__no-events",children:"No triggers"}):t.map(a=>{const i=C.find(W=>W.id===a),R=(i==null?void 0:i.icon)??u;return e.jsxs("span",{className:`event-pill event-pill--${(i==null?void 0:i.tone)||"gray"}`,children:[e.jsx(R,{size:11}),(i==null?void 0:i.label)||a]},a)})})]},s.id)})}),e.jsxs("div",{className:"add-webhook-card",children:[e.jsxs("div",{className:"add-webhook-card__header",children:[e.jsx(x,{size:14}),e.jsx("span",{children:"Add a new webhook"})]}),e.jsxs("div",{className:"add-webhook-card__body",children:[e.jsx(S,{label:"Webhook URL",description:"The endpoint we POST webhook events to.",htmlFor:"webhook-url",children:e.jsx("div",{className:"setting-input-wrapper",children:e.jsx("input",{id:"webhook-url",type:"url",placeholder:"https://hooks.slack.com/services/...",value:l,onChange:s=>j(s.target.value)})})}),e.jsx(S,{label:"Trigger Events",children:e.jsx("div",{className:"event-toggle-grid",children:C.map(s=>{const t=d.includes(s.id),a=s.icon;return e.jsxs("button",{type:"button",role:"checkbox","aria-checked":t,className:`event-toggle event-toggle--${s.tone} ${t?"is-selected":""}`,onClick:()=>$(s.id),children:[e.jsx(a,{size:14,className:"event-toggle__lead"}),e.jsx("span",{children:s.label}),t&&e.jsx(B,{size:14,className:"event-toggle__check"})]},s.id)})})}),e.jsxs("div",{className:"template-section",children:[e.jsxs("button",{type:"button",className:"template-section__toggle",onClick:()=>_(!n),"aria-expanded":n,children:[e.jsx(I,{size:14,className:n?"tone-amber":"tone-dim"}),e.jsx("span",{children:"Use custom payload (optional)"}),e.jsx("span",{className:"template-section__indicator",children:n?"−":"+"})]}),n&&e.jsxs("div",{className:"template-editor",children:[e.jsx("p",{className:"template-editor__hint",children:"Define a JSON or text template. Click a variable below to insert it."}),e.jsx("div",{className:"template-editor__chips",children:J.map(s=>e.jsx("button",{type:"button",className:"template-editor__chip",onClick:()=>F(s),children:`{{${s}}}`},s))}),e.jsx("textarea",{className:"template-editor__textarea",placeholder:'Example: { "text": "Alert: Device {{udid}} is offline!" }',value:y,onChange:s=>m(s.target.value),rows:4})]})]})]}),e.jsxs("div",{className:"add-webhook-card__footer",children:[e.jsxs("button",{type:"button",className:"page-header-action page-header-action--ghost",onClick:L,disabled:!l.trim()||v,children:[v?e.jsx(b,{size:14,className:"animate-spin"}):e.jsx(X,{size:14}),e.jsx("span",{children:"Test payload"})]}),e.jsxs("button",{type:"button",className:"page-header-action",onClick:z,disabled:h||!l.trim(),children:[h?e.jsx(b,{size:14,className:"animate-spin"}):e.jsx(x,{size:14}),e.jsx("span",{children:h?"Saving…":"Save webhook"})]})]})]})]})]})};export{ae as WebhookSettings};
1
+ import{m as P,r,X as c,j as e,x as u,v as b,C as U,H as D}from"./index-wbxPcwMM.js";/* empty css */import{F as S}from"./FieldGroup-a9gzUh4W.js";import{P as H}from"./page-header-DZpQDeCw.js";import{T as O}from"./trash-2-8zA1h40t.js";import{P as x}from"./plus-CTjPrkU5.js";import{C as V}from"./circle-x-DcBe5J67.js";import{C as B}from"./circle-check-DDqpikKW.js";import{Z as I}from"./zap-C-b2-XlO.js";import{A as X}from"./activity-KiLkzk_U.js";const C=[{id:"device_offline",label:"Device Offline",icon:U,tone:"red"},{id:"device_new",label:"New Device",icon:x,tone:"green"},{id:"session_failed",label:"Session Failed",icon:V,tone:"amber"},{id:"selector_health_digest",label:"Selector Health Digest",icon:D,tone:"green"}],J=["udid","host","name","sessionId","failureReason","eventType","platform"],ae=()=>{const{toast:o}=P(),[g,T]=r.useState([]),[A,w]=r.useState(!0),[l,j]=r.useState(""),[d,k]=r.useState(["device_offline","session_failed"]),[h,f]=r.useState(!1),[v,N]=r.useState(!1),[n,_]=r.useState(!1),[y,m]=r.useState("");r.useEffect(()=>{p()},[]);const p=async()=>{w(!0);try{const s=await c.getWebhookConfigs();T(s||[])}catch(s){console.error("Failed to load webhook configs",s),o("Failed to load webhooks.","error")}finally{w(!1)}},z=async()=>{if(!l.trim()){o("Webhook URL is required.","error");return}if(d.length===0){o("Select at least one trigger event.","error");return}f(!0);try{await c.addWebhookConfig(l.trim(),d,"slack",y||void 0),j(""),m(""),_(!1),k(["device_offline","session_failed"]),await p(),o("Webhook saved.","success")}catch(s){console.error("Failed to add webhook",s),o(`Failed to add webhook: ${s.message||"unknown error"}`,"error")}finally{f(!1)}},E=async(s,t)=>{if(window.confirm(`Remove webhook for "${t}"?`))try{await c.deleteWebhookConfig(s),await p(),o("Webhook removed.","success")}catch(a){console.error("Failed to delete webhook",a),o(`Failed to delete: ${a.message||"unknown error"}`,"error")}},L=async()=>{if(l.trim()){N(!0);try{await c.testWebhook(l.trim(),"slack"),o("Test payload delivered.","success")}catch(s){o(`Test failed: ${s.message||"check the URL"}`,"error")}finally{N(!1)}}},$=s=>{k(t=>t.includes(s)?t.filter(a=>a!==s):[...t,s])},F=s=>{m(t=>t+`{{${s}}} `)};return e.jsxs("div",{className:"settings-container",children:[e.jsx(H,{icon:u,title:"Notifications",subtitle:"Configure Slack or generic webhooks to receive alerts for critical infrastructure events."}),e.jsxs("div",{className:"settings-content",children:[A?e.jsxs("div",{className:"settings-loading",style:{height:200},children:[e.jsx(b,{className:"animate-spin",size:28}),e.jsx("span",{children:"Loading webhooks…"})]}):g.length===0?e.jsxs("div",{className:"empty-state",children:[e.jsx("div",{className:"empty-state__icon",children:e.jsx(u,{size:28})}),e.jsx("h3",{className:"empty-state__title",children:"No webhooks configured"}),e.jsx("p",{className:"empty-state__copy",children:"Wire up a Slack incoming webhook or any HTTPS endpoint to receive alerts when devices go offline, new devices register, or sessions fail. Configure your first webhook below."})]}):e.jsx("div",{className:"webhook-list-grid",children:g.map(s=>{const t=(()=>{try{return JSON.parse(s.events)}catch{return[]}})();return e.jsxs("div",{className:"webhook-row-card",children:[e.jsxs("div",{className:"webhook-row-card__header",children:[e.jsx("span",{className:`pill-chip ${s.payloadTemplate?"pill-chip--admin":"pill-chip--scope"}`,children:s.payloadTemplate?"CUSTOM":"SLACK"}),e.jsx("span",{className:"webhook-row-card__url",title:s.url,children:s.url}),e.jsxs("button",{type:"button",className:"row-action row-action--danger",onClick:()=>E(s.id,s.url),"aria-label":`Remove webhook for ${s.url}`,children:[e.jsx(O,{size:13}),e.jsx("span",{children:"Remove"})]})]}),e.jsx("div",{className:"webhook-row-card__events",children:t.length===0?e.jsx("span",{className:"webhook-row-card__no-events",children:"No triggers"}):t.map(a=>{const i=C.find(W=>W.id===a),R=(i==null?void 0:i.icon)??u;return e.jsxs("span",{className:`event-pill event-pill--${(i==null?void 0:i.tone)||"gray"}`,children:[e.jsx(R,{size:11}),(i==null?void 0:i.label)||a]},a)})})]},s.id)})}),e.jsxs("div",{className:"add-webhook-card",children:[e.jsxs("div",{className:"add-webhook-card__header",children:[e.jsx(x,{size:14}),e.jsx("span",{children:"Add a new webhook"})]}),e.jsxs("div",{className:"add-webhook-card__body",children:[e.jsx(S,{label:"Webhook URL",description:"The endpoint we POST webhook events to.",htmlFor:"webhook-url",children:e.jsx("div",{className:"setting-input-wrapper",children:e.jsx("input",{id:"webhook-url",type:"url",placeholder:"https://hooks.slack.com/services/...",value:l,onChange:s=>j(s.target.value)})})}),e.jsx(S,{label:"Trigger Events",children:e.jsx("div",{className:"event-toggle-grid",children:C.map(s=>{const t=d.includes(s.id),a=s.icon;return e.jsxs("button",{type:"button",role:"checkbox","aria-checked":t,className:`event-toggle event-toggle--${s.tone} ${t?"is-selected":""}`,onClick:()=>$(s.id),children:[e.jsx(a,{size:14,className:"event-toggle__lead"}),e.jsx("span",{children:s.label}),t&&e.jsx(B,{size:14,className:"event-toggle__check"})]},s.id)})})}),e.jsxs("div",{className:"template-section",children:[e.jsxs("button",{type:"button",className:"template-section__toggle",onClick:()=>_(!n),"aria-expanded":n,children:[e.jsx(I,{size:14,className:n?"tone-amber":"tone-dim"}),e.jsx("span",{children:"Use custom payload (optional)"}),e.jsx("span",{className:"template-section__indicator",children:n?"−":"+"})]}),n&&e.jsxs("div",{className:"template-editor",children:[e.jsx("p",{className:"template-editor__hint",children:"Define a JSON or text template. Click a variable below to insert it."}),e.jsx("div",{className:"template-editor__chips",children:J.map(s=>e.jsx("button",{type:"button",className:"template-editor__chip",onClick:()=>F(s),children:`{{${s}}}`},s))}),e.jsx("textarea",{className:"template-editor__textarea",placeholder:'Example: { "text": "Alert: Device {{udid}} is offline!" }',value:y,onChange:s=>m(s.target.value),rows:4})]})]})]}),e.jsxs("div",{className:"add-webhook-card__footer",children:[e.jsxs("button",{type:"button",className:"page-header-action page-header-action--ghost",onClick:L,disabled:!l.trim()||v,children:[v?e.jsx(b,{size:14,className:"animate-spin"}):e.jsx(X,{size:14}),e.jsx("span",{children:"Test payload"})]}),e.jsxs("button",{type:"button",className:"page-header-action",onClick:z,disabled:h||!l.trim(),children:[h?e.jsx(b,{size:14,className:"animate-spin"}):e.jsx(x,{size:14}),e.jsx("span",{children:h?"Saving…":"Save webhook"})]})]})]})]})]})};export{ae as WebhookSettings};
@@ -1,4 +1,4 @@
1
- import{g as a}from"./index-PtnKslHU.js";/**
1
+ import{g as a}from"./index-wbxPcwMM.js";/**
2
2
  * @license lucide-react v0.555.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -24,7 +24,7 @@
24
24
  Learn how to configure a non-root public URL by running `npm run build`.
25
25
  -->
26
26
  <title>Xenon</title>
27
- <script type="module" crossorigin src="/xenon/assets/index-PtnKslHU.js"></script>
27
+ <script type="module" crossorigin src="/xenon/assets/index-wbxPcwMM.js"></script>
28
28
  <link rel="stylesheet" crossorigin href="/xenon/assets/index-BW3kENYH.css">
29
29
  </head>
30
30
 
@@ -77,6 +77,8 @@ const auth_1 = require("./routers/auth");
77
77
  const processes_1 = require("./routers/processes");
78
78
  const profile_1 = require("./routers/profile");
79
79
  const users_1 = require("./routers/users");
80
+ const capabilities_1 = require("./routers/capabilities");
81
+ const projects_1 = require("./routers/projects");
80
82
  const authMiddleware_1 = require("../middleware/authMiddleware");
81
83
  const rateLimitMiddleware_1 = require("../middleware/rateLimitMiddleware");
82
84
  const csrfMiddleware_1 = require("../middleware/csrfMiddleware");
@@ -243,6 +245,10 @@ function createRouter(pluginArgs) {
243
245
  apiRouter.use('/teams', (0, teams_1.teamsRouter)());
244
246
  // Admin: running process snapshot (ops debugging)
245
247
  apiRouter.use('/processes', (0, processes_1.processesRouter)());
248
+ // Feature detection endpoint
249
+ apiRouter.use('/capabilities', (0, capabilities_1.capabilitiesRouter)());
250
+ // Project entity skeleton — container for later runs/flows/secrets (ARB guard #4)
251
+ apiRouter.use('/projects', (0, projects_1.projectsRouter)());
246
252
  // Exposes plugin CLI args (may include host, hub URL, etc.) — auth-gated.
247
253
  apiRouter.get('/cliArgs', (_req, res) => __awaiter(this, void 0, void 0, function* () {
248
254
  res.json(yield (0, pluginArgs_1.getCLIArgs)());
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.issueToken = issueToken;
12
13
  exports.authPublicRouter = authPublicRouter;
13
14
  exports.authAuthedRouter = authAuthedRouter;
14
15
  exports.authRouter = authRouter;
@@ -23,8 +24,24 @@ const EmailService_1 = require("../../services/EmailService");
23
24
  const loginRateLimiter_1 = require("../../middleware/loginRateLimiter");
24
25
  const config_1 = require("../../config");
25
26
  const prisma_1 = require("../../prisma");
27
+ const JwtKeyService_1 = require("../../services/token/JwtKeyService");
26
28
  const SESSION_COOKIE = 'xenon_dashboard_session';
27
29
  const isSecureFromReq = (req) => req.secure || req.headers['x-forwarded-proto'] === 'https';
30
+ const MCP_TTL_SEC = Number(process.env.XENON_MCP_TOKEN_TTL_SEC || 86400);
31
+ const REST_TTL_SEC = 3600;
32
+ const MINTABLE_AUDIENCES = ['xenon-rest', 'xenon-mcp'];
33
+ function issueToken(auth, body) {
34
+ return __awaiter(this, void 0, void 0, function* () {
35
+ var _a, _b;
36
+ const audience = (_a = body.audience) !== null && _a !== void 0 ? _a : 'xenon-rest';
37
+ if (!MINTABLE_AUDIENCES.includes(audience)) {
38
+ throw new Error(`unsupported audience: ${audience}`);
39
+ }
40
+ const expiresIn = audience === 'xenon-mcp' ? MCP_TTL_SEC : REST_TTL_SEC;
41
+ const token = yield typedi_1.Container.get(JwtKeyService_1.JwtKeyService).sign({ sub: auth.userId, role: auth.role, scopes: auth.scopes, teamId: (_b = auth.teamId) !== null && _b !== void 0 ? _b : null }, { audience, ttlSeconds: expiresIn });
42
+ return { token, expiresIn, audience };
43
+ });
44
+ }
28
45
  // Public — anonymous-callable. Mounted before authMiddleware.
29
46
  function authPublicRouter() {
30
47
  var _a, _b;
@@ -108,6 +125,9 @@ function authPublicRouter() {
108
125
  if (elapsed < 50)
109
126
  yield new Promise((res2) => setTimeout(res2, 50 - elapsed));
110
127
  }));
128
+ r.get('/jwks.json', (_req, res) => {
129
+ res.json(typedi_1.Container.get(JwtKeyService_1.JwtKeyService).jwks());
130
+ });
111
131
  r.get('/reset-password/check/:token', (req, res) => __awaiter(this, void 0, void 0, function* () {
112
132
  const resetSvc = typedi_1.Container.get(PasswordResetService_1.PasswordResetService);
113
133
  const row = yield resetSvc.verifyToken(req.params.token);
@@ -213,6 +233,16 @@ function authAuthedRouter() {
213
233
  teams,
214
234
  });
215
235
  }));
236
+ r.post('/token', (req, res) => __awaiter(this, void 0, void 0, function* () {
237
+ var _a;
238
+ try {
239
+ const out = yield issueToken(req.auth, (_a = req.body) !== null && _a !== void 0 ? _a : {});
240
+ res.json(out);
241
+ }
242
+ catch (e) {
243
+ res.status(400).json({ error: e.message });
244
+ }
245
+ }));
216
246
  r.post('/dashboard-session', (req, res) => __awaiter(this, void 0, void 0, function* () {
217
247
  const { apiKey } = req.body;
218
248
  if (!apiKey)
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildCapabilities = buildCapabilities;
4
+ exports.capabilitiesRouter = capabilitiesRouter;
5
+ const express_1 = require("express");
6
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
7
+ const pkg = require('../../../package.json');
8
+ function buildCapabilities() {
9
+ return {
10
+ version: String(pkg.version),
11
+ features: {
12
+ bearerAuth: true,
13
+ tokenIssuance: true,
14
+ streamTickets: true,
15
+ leases: true, // pre-existing: /sdk/leases
16
+ eventLog: true, // Task 6
17
+ projects: true, // Task 8
18
+ },
19
+ };
20
+ }
21
+ function capabilitiesRouter() {
22
+ const r = (0, express_1.Router)();
23
+ r.get('/', (_req, res) => res.json(buildCapabilities()));
24
+ return r;
25
+ }
@@ -60,6 +60,7 @@ const os_1 = __importDefault(require("os"));
60
60
  const fs_extra_1 = __importDefault(require("fs-extra"));
61
61
  const OmniVisionService_1 = require("../../services/omni-vision/OmniVisionService");
62
62
  const InspectorService_1 = require("../../services/InspectorService");
63
+ const StreamTicketService_1 = require("../../services/token/StreamTicketService");
63
64
  const scopeGuard_1 = require("../../middleware/scopeGuard");
64
65
  const roleGuard_1 = require("../../middleware/roleGuard");
65
66
  const manualLock_1 = require("../../services/recording/manualLock");
@@ -607,6 +608,19 @@ router.post('/:udid/stream/start', (req, res) => __awaiter(void 0, void 0, void
607
608
  });
608
609
  }
609
610
  }));
611
+ /**
612
+ * Mint a single-use, udid-bound stream ticket for the webview <img> MJPEG
613
+ * path (spec §2.3/§7.1) — a raw <img src> can carry neither auth headers nor
614
+ * the SameSite=strict dashboard cookie, so it authenticates via ?ticket=.
615
+ */
616
+ router.post('/:udid/stream/ticket', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
617
+ var _a, _b, _c;
618
+ const actorId = (_b = (_a = req.apiKey) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : (_c = req.auth) === null || _c === void 0 ? void 0 : _c.userId;
619
+ if (!actorId)
620
+ return res.status(401).json({ error: 'unauthenticated' });
621
+ const ticket = yield typedi_1.Container.get(StreamTicketService_1.StreamTicketService).mint(req.params.udid, actorId);
622
+ res.json({ ticket, expiresIn: 60 });
623
+ }));
610
624
  /**
611
625
  * Stop stream endpoint
612
626
  */
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.listProjects = listProjects;
13
+ exports.createProject = createProject;
14
+ exports.projectsRouter = projectsRouter;
15
+ const express_1 = require("express");
16
+ const prisma_1 = require("../../prisma");
17
+ const scopeGuard_1 = require("../../middleware/scopeGuard");
18
+ function listProjects(client, teamIds) {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ const where = teamIds ? { where: { OR: [{ teamId: { in: teamIds } }, { teamId: null }] } } : {};
21
+ return client.project.findMany(where);
22
+ });
23
+ }
24
+ function createProject(client, body) {
25
+ return __awaiter(this, void 0, void 0, function* () {
26
+ var _a;
27
+ if (!body.name || !body.name.trim())
28
+ throw new Error('name is required');
29
+ return client.project.create({
30
+ data: { name: body.name.trim(), teamId: ((_a = body.teamId) === null || _a === void 0 ? void 0 : _a.trim()) || null },
31
+ });
32
+ });
33
+ }
34
+ function projectsRouter() {
35
+ const r = (0, express_1.Router)();
36
+ r.get('/', (req, res) => __awaiter(this, void 0, void 0, function* () {
37
+ var _a;
38
+ try {
39
+ res.json(yield listProjects(prisma_1.prisma, (_a = req.auth) === null || _a === void 0 ? void 0 : _a.teamIds));
40
+ }
41
+ catch (e) {
42
+ res.status(500).json({ error: e.message });
43
+ }
44
+ }));
45
+ r.post('/', (0, scopeGuard_1.scopeGuard)(['admin']), (req, res) => __awaiter(this, void 0, void 0, function* () {
46
+ var _a;
47
+ try {
48
+ res.status(201).json(yield createProject(prisma_1.prisma, (_a = req.body) !== null && _a !== void 0 ? _a : {}));
49
+ }
50
+ catch (e) {
51
+ res.status(400).json({ error: e.message });
52
+ }
53
+ }));
54
+ return r;
55
+ }
@@ -35,6 +35,7 @@ const PluginContext_1 = require("../PluginContext");
35
35
  const IOSStreamService_1 = __importDefault(require("./ios/IOSStreamService"));
36
36
  const IOSDiscoveryService_1 = require("./ios/IOSDiscoveryService");
37
37
  const WDAClient_1 = require("./ios/WDAClient");
38
+ const iosBattery_1 = require("./ios/iosBattery");
38
39
  const execPromise = (0, util_1.promisify)(child_process_1.exec);
39
40
  let IOSDeviceManager = class IOSDeviceManager {
40
41
  constructor(context) {
@@ -205,11 +206,13 @@ let IOSDeviceManager = class IOSDeviceManager {
205
206
  return { healthStatus: 'Healthy' };
206
207
  try {
207
208
  if (device.realDevice) {
209
+ // Best-effort battery telemetry (iOS exposes level but not thermal).
210
+ const batteryLevel = yield this.collectBatteryLevel(device.udid);
208
211
  // Collect hardware stats via WDAClient (which uses go-ios if available)
209
212
  // For simplicity, we'll keep some health check logic here but use the client for WDA status
210
213
  const isReady = yield typedi_1.Container.get(WDAClient_1.WDAClient).verifyWDAStatus(device.udid);
211
214
  if (isReady)
212
- return { healthStatus: 'Healthy' };
215
+ return { healthStatus: 'Healthy', batteryLevel };
213
216
  // Principal Intelligence: If the device is IDLE (not busy) and WDA is down,
214
217
  // check if this is expected (e.g. watchdog stopped the stream).
215
218
  if (!device.busy) {
@@ -217,10 +220,10 @@ let IOSDeviceManager = class IOSDeviceManager {
217
220
  const streamStatus = streamService.getStreamStatus(device.udid);
218
221
  if (!streamStatus ||
219
222
  (streamStatus.status !== 'running' && streamStatus.status !== 'starting')) {
220
- return { healthStatus: 'Healthy', healthCheckError: 'Idle (WDA Stopped)' };
223
+ return { healthStatus: 'Healthy', healthCheckError: 'Idle (WDA Stopped)', batteryLevel };
221
224
  }
222
225
  }
223
- return { healthStatus: 'Unhealthy', healthCheckError: 'WDA not responding' };
226
+ return { healthStatus: 'Unhealthy', healthCheckError: 'WDA not responding', batteryLevel };
224
227
  }
225
228
  else {
226
229
  const simctl = new node_simctl_1.default();
@@ -236,6 +239,24 @@ let IOSDeviceManager = class IOSDeviceManager {
236
239
  }
237
240
  });
238
241
  }
242
+ /**
243
+ * Best-effort iOS battery level (0–100) via libimobiledevice. Returns
244
+ * undefined on any failure (tool missing, device locked, parse miss) so the
245
+ * health check never breaks over telemetry — the card just shows no battery,
246
+ * exactly as before this was collected.
247
+ */
248
+ collectBatteryLevel(udid) {
249
+ return __awaiter(this, void 0, void 0, function* () {
250
+ try {
251
+ const { stdout } = yield execPromise(`ideviceinfo -u ${udid} -q com.apple.mobile.battery`);
252
+ return (0, iosBattery_1.parseBatteryCapacity)(stdout);
253
+ }
254
+ catch (e) {
255
+ this.log.debug(`Battery telemetry unavailable for ${udid}: ${e.message}`);
256
+ return undefined;
257
+ }
258
+ });
259
+ }
239
260
  readyForSession(device) {
240
261
  return __awaiter(this, void 0, void 0, function* () {
241
262
  var _a;
@@ -74,6 +74,7 @@ const logger_1 = __importDefault(require("../../logger"));
74
74
  const helpers_1 = require("../../helpers");
75
75
  const PortAllocator_1 = require("../../services/PortAllocator");
76
76
  const device_store_1 = require("../../data-service/device-store");
77
+ const iosStreamDiagnostics_1 = require("./iosStreamDiagnostics");
77
78
  const device_service_1 = require("../../data-service/device-service");
78
79
  const execPromise = (0, util_1.promisify)(child_process_1.exec);
79
80
  const typedi_2 = require("typedi");
@@ -218,7 +219,23 @@ let IOSStreamService = class IOSStreamService {
218
219
  });
219
220
  typedi_2.Container.get(ProcessRegistry_1.ProcessRegistry).track({ kind: 'other', udid, process: tunnelProcess });
220
221
  (_a = tunnelProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => logger_1.default.debug(`Tunnel [${udid}]: ${data}`));
221
- (_b = tunnelProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => logger_1.default.debug(`Tunnel Err [${udid}]: ${data}`));
222
+ // go-ios repeatedly warns about any connected pre-iOS-17 device (which
223
+ // needs no tunnel). Log that once — attributed to the real target udid,
224
+ // not this tunnel's owner — and suppress the rest to avoid log spam.
225
+ const loggedUnsupported = new Set();
226
+ (_b = tunnelProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
227
+ const text = String(data);
228
+ const { unsupported, udid: targetUdid } = (0, iosStreamDiagnostics_1.classifyTunnelStderr)(text);
229
+ if (unsupported) {
230
+ const key = targetUdid !== null && targetUdid !== void 0 ? targetUdid : 'unknown';
231
+ if (!loggedUnsupported.has(key)) {
232
+ loggedUnsupported.add(key);
233
+ logger_1.default.debug(`Tunnel [${udid}]: device ${key} is pre-iOS 17 and needs no tunnel; suppressing repeated go-ios warnings`);
234
+ }
235
+ return;
236
+ }
237
+ logger_1.default.debug(`Tunnel Err [${udid}]: ${text}`);
238
+ });
222
239
  // Wait for tunnel to establish by checking the go-ios agent port
223
240
  logger_1.default.info('Waiting for tunnel agent on port 60105 to be ready...');
224
241
  let tunnelReady = false;
@@ -492,6 +509,19 @@ let IOSStreamService = class IOSStreamService {
492
509
  logger_1.default.info(`[${udid}] Stream exists but unhealthy, stopping before restart...`);
493
510
  yield this.stopStream(udid);
494
511
  }
512
+ // Startup-failure circuit-breaker: a failed start (e.g. WDA not installed on
513
+ // the device) sets a recovery cooldown. Until it elapses, short-circuit with
514
+ // a clear error instead of re-running the full, doomed startup on every
515
+ // stream request — otherwise a client polling the stream URL drives a
516
+ // resource-burning restart loop.
517
+ if (!existingSession || existingSession.status !== 'running') {
518
+ if (!this.canAttemptRecovery(udid)) {
519
+ const lastAttempt = this.recoveryCooldowns.get(udid);
520
+ const waitMs = this.RECOVERY_COOLDOWN_MS - (Date.now() - (lastAttempt || 0));
521
+ const reason = (existingSession === null || existingSession === void 0 ? void 0 : existingSession.lastError) ? ` Last error: ${existingSession.lastError}` : '';
522
+ throw new Error(`Stream start is cooling down for ${Math.ceil(waitMs / 1000)}s after a failed attempt.${reason}`);
523
+ }
524
+ }
495
525
  // Define the core logic as an internal async function
496
526
  const performStartup = () => __awaiter(this, void 0, void 0, function* () {
497
527
  var _a, _b, _c, _d;
@@ -728,6 +758,11 @@ let IOSStreamService = class IOSStreamService {
728
758
  }
729
759
  if (((_c = session.wdaProcess) === null || _c === void 0 ? void 0 : _c.exitCode) !== null) {
730
760
  const logContent = fs_extra_1.default.existsSync(wdaRunLog) ? fs_extra_1.default.readFileSync(wdaRunLog, 'utf8') : '';
761
+ // Missing WDA is permanent until the app is installed — surface an
762
+ // actionable reason instead of a raw exit code + log tail.
763
+ if ((0, iosStreamDiagnostics_1.isMissingWdaError)(logContent)) {
764
+ throw new Error((0, iosStreamDiagnostics_1.missingWdaMessage)(udid));
765
+ }
731
766
  throw new Error(`WDA process exited with code ${(_d = session.wdaProcess) === null || _d === void 0 ? void 0 : _d.exitCode}. Log: ${logContent.slice(-200)}`);
732
767
  }
733
768
  yield new Promise((resolve) => setTimeout(resolve, 1000));
@@ -740,6 +775,9 @@ let IOSStreamService = class IOSStreamService {
740
775
  session.status = 'error';
741
776
  session.lastError = error.message;
742
777
  }
778
+ // Back off before the next attempt so a polling client can't drive a
779
+ // restart loop against a device that keeps failing to start.
780
+ this.markRecoveryAttempt(udid);
743
781
  logger_1.default.error(`Stream start failed for ${udid}: ${error.message}`);
744
782
  throw error;
745
783
  }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseBatteryCapacity = parseBatteryCapacity;
4
+ /**
5
+ * Parse the current battery charge level from
6
+ * `ideviceinfo -q com.apple.mobile.battery` output.
7
+ *
8
+ * iOS exposes battery capacity (0–100 via `BatteryCurrentCapacity`) through
9
+ * libimobiledevice, but not a thermal/temperature reading — so only the level
10
+ * is collected for iOS device telemetry (Android additionally reports thermal
11
+ * state via `dumpsys battery`).
12
+ *
13
+ * Returns undefined when the field is absent or unparseable, so callers can
14
+ * treat battery telemetry as best-effort.
15
+ */
16
+ function parseBatteryCapacity(ideviceinfoOutput) {
17
+ const match = /BatteryCurrentCapacity:\s*(\d+)/.exec(ideviceinfoOutput !== null && ideviceinfoOutput !== void 0 ? ideviceinfoOutput : '');
18
+ if (!match)
19
+ return undefined;
20
+ const value = parseInt(match[1], 10);
21
+ if (Number.isNaN(value))
22
+ return undefined;
23
+ return Math.max(0, Math.min(100, value));
24
+ }
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ /**
3
+ * Pure diagnostics helpers for the iOS stream pipeline.
4
+ *
5
+ * Kept free of side effects so they can be unit-tested. IOSStreamService uses
6
+ * them to (a) turn an opaque WDA crash into an actionable message and (b) tame
7
+ * the go-ios tunnel's repetitive stderr.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.isMissingWdaError = isMissingWdaError;
11
+ exports.missingWdaMessage = missingWdaMessage;
12
+ exports.classifyTunnelStderr = classifyTunnelStderr;
13
+ /**
14
+ * True when a WDA run log indicates the WebDriverAgentRunner app is missing on
15
+ * the device (a permanent condition — retrying the launch is pointless until
16
+ * WDA is installed).
17
+ */
18
+ function isMissingWdaError(logContent) {
19
+ if (!logContent)
20
+ return false;
21
+ return (/Did not find test app/i.test(logContent) ||
22
+ /WebDriverAgentRunner[^\n]*\b(not installed|not found|is it installed)/i.test(logContent));
23
+ }
24
+ /** Human-facing, actionable message for the missing-WDA case. */
25
+ function missingWdaMessage(udid) {
26
+ return (`WebDriverAgent is not installed on ${udid}. ` +
27
+ `Live streaming requires the WebDriverAgentRunner app — install WDA on this device to enable the mirror.`);
28
+ }
29
+ /**
30
+ * Classify a chunk of go-ios `tunnel start` stderr.
31
+ *
32
+ * When a connected device runs iOS < 17 (which needs no tunnel), go-ios emits a
33
+ * repeated "unsupported iOS version" warning for that device. We detect it so
34
+ * the caller can log it once, attributed to the *actual* target udid rather than
35
+ * the tunnel owner's udid.
36
+ */
37
+ function classifyTunnelStderr(text) {
38
+ const trimmed = (text !== null && text !== void 0 ? text : '').trim();
39
+ if (!/unsupported iOS version/i.test(trimmed)) {
40
+ return { unsupported: false, udid: null };
41
+ }
42
+ let udid = null;
43
+ const start = trimmed.indexOf('{');
44
+ const end = trimmed.lastIndexOf('}');
45
+ if (start !== -1 && end > start) {
46
+ try {
47
+ const parsed = JSON.parse(trimmed.slice(start, end + 1));
48
+ if (typeof (parsed === null || parsed === void 0 ? void 0 : parsed.udid) === 'string')
49
+ udid = parsed.udid;
50
+ }
51
+ catch (_a) {
52
+ /* fall through to regex */
53
+ }
54
+ }
55
+ if (!udid) {
56
+ const m = trimmed.match(/"udid"\s*:\s*"([^"]+)"/);
57
+ if (m)
58
+ udid = m[1];
59
+ }
60
+ return { unsupported: true, udid };
61
+ }