livedesk 0.1.680 → 0.1.682

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.
@@ -5,4 +5,4 @@ ${this.session.workspaceRole}`:"",s=t?`${t.user.id}
5
5
  ${t.workspaceId}
6
6
  ${t.workspaceRole}`:"";this.session=t,i!==s&&(this.teardown("console-session-replaced",null,!0),this.lastFailure=null)}preferDirect(){this.preferred=!0}markLocalHubReachable(){this.preferred=!1,this.teardown("local-hub-reachable",null,!0),this.lastFailure=null}resume(){if(this.invalidateLivenessEvidence(),this.hasReadyPayloadPeer()){this.lastFailure=null;return}this.teardown("console-resume",null,!0),this.lastFailure=null}suspendLivenessEvidence(){this.invalidateLivenessEvidence()}retry(){this.preferred=!0,this.teardown("console-manual-retry",null,!0),this.lastFailure=null}shouldUseDirect(){return this.preferred&&!!this.session?.accessToken}inspect(){const e=this.wireBudget?.inspect()||{retainedBytes:0,reservationCount:0},t=[...this.channels.values()].filter(c=>c.wireDeadline!==null).length,i=[...this.channels.values()].filter(c=>c.openDeadline!==null).length,s=this.connectTimer===null?0:1,r=this.peerDisconnectTimer===null?0:1,o=this.controlWireDeadline===null?0:1,a=this.workspaceReauthAttempt?.deadline==null?0:1;return Object.freeze({preferred:this.preferred,stage:this.stage,directReady:this.directReady,generation:this.generation,signalSockets:this.signalSocket?1:0,peerCount:this.peer?1:0,controlChannels:this.controlChannel?1:0,logicalChannels:this.channels.size,openLogicalChannels:[...this.channels.values()].filter(c=>c.opened).length,pendingHttpRequests:this.pendingHttp.size,livenessEvidenceEpoch:this.livenessEvidenceEpoch,workspaceReauthRequests:this.workspaceReauthAttempt?1:0,consecutiveLivenessRequestTimeouts:this.consecutiveLivenessRequestTimeouts,livenessRequestTimeoutCount:this.livenessRequestTimeoutCount,consecutiveMediaLogicalOpenFailures:this.consecutiveMediaLogicalOpenFailures,logicalChannelFailureCount:this.logicalChannelFailureCount,lastLogicalChannelFailure:this.lastLogicalChannelFailure,partialMediaWireTimeoutCount:this.partialMediaWireTimeoutCount,lastPartialMediaWireTimeout:this.lastPartialMediaWireTimeout,preOpenMediaCoalesceCount:this.preOpenMediaCoalesceCount,lastPreOpenMediaCoalesce:this.lastPreOpenMediaCoalesce,logicalWireFailureCount:this.logicalWireFailureCount,lastLogicalWireFailure:this.lastLogicalWireFailure,controlMediaRecoveryCount:this.controlMediaRecoveryCount,lastControlMediaRecovery:this.lastControlMediaRecovery,mediaAttachmentHoldGeneration:this.mediaAttachmentHoldGeneration,heldMediaLogicalChannels:[...this.channels.values()].filter(c=>(c.purpose==="frame"||c.purpose==="atlas")&&c.owner===null&&c.dataChannel===null).length,pendingRemoteCandidates:this.pendingRemoteCandidates.length,connectTimers:s,peerDisconnectTimers:r,controlWireTimers:o,workspaceReauthTimers:a,logicalOpenTimers:i,logicalWireTimers:t,resourceTimers:s+r+o+a+i+t+this.pendingHttp.size,wireRetainedBytes:e.retainedBytes,wireReservations:e.reservationCount,sendBufferedBytes:this.peerBufferedAmount(),lastTerminalWireBytes:this.lastTerminalWireBytes,lastTerminalWireReservations:this.lastTerminalWireReservations,failure:this.lastFailure})}nextMessageId(){return this.wireMessageId=this.wireMessageId>=4294967295?1:this.wireMessageId+1,this.wireMessageId}isSignalOwner(e,t){return e===this.generation&&this.signalSocket===t}isPeerOwner(e){return tt(this.peerOwner,e)}hasReadyPayloadPeer(){return this.directReady&&!!this.peerOwner&&!!this.peer&&this.peer?.connectionState!=="failed"&&this.peer?.connectionState!=="closed"&&this.controlChannel?.readyState==="open"}detachSignalFromReadyPeer(e,t,i){if(!this.isSignalOwner(e,t)||!this.hasReadyPayloadPeer())return!1;this.cancelWorkspaceReauthAttempt(e,t),this.signalSocket=null,this.signalReady=!1,this.consoleId="";try{t.close(1e3,i)}catch{}return!0}clearConnectTimer(){this.connectTimer!==null&&(window.clearTimeout(this.connectTimer),this.connectTimer=null)}clearPeerDisconnectTimer(){this.peerDisconnectTimer!==null&&(window.clearTimeout(this.peerDisconnectTimer),this.peerDisconnectTimer=null)}cancelWorkspaceReauthAttempt(e,t){const i=this.workspaceReauthAttempt;if(!i||e!==void 0&&i.generation!==e||t!==void 0&&i.socket!==t)return!1;this.workspaceReauthAttempt=null,i.deadline!==null&&(window.clearTimeout(i.deadline),i.deadline=null);try{i.controller.abort()}catch{}return!0}refreshPeerDisconnectDeadline(e,t){if(!this.isPeerOwner(e)||this.peer!==t)return;if(!(t.connectionState==="disconnected"||t.iceConnectionState==="disconnected")){this.clearPeerDisconnectTimer();return}if(this.peerDisconnectTimer!==null)return;const s=window.setTimeout(()=>{this.peerDisconnectTimer===s&&(this.peerDisconnectTimer=null,!(!this.isPeerOwner(e)||this.peer!==t)&&(t.connectionState==="disconnected"||t.iceConnectionState==="disconnected")&&this.fail(g(),"peer-disconnected-timeout",!0))},ct);this.peerDisconnectTimer=s}clearControlWireDeadline(){this.controlWireDeadline!==null&&(window.clearTimeout(this.controlWireDeadline),this.controlWireDeadline=null)}refreshControlWireDeadline(e,t){if((t.inspect().pendingMessages||0)===0){this.clearControlWireDeadline();return}if(this.controlWireDeadline!==null)return;const s=window.setTimeout(()=>{this.controlWireDeadline===s&&(this.controlWireDeadline=null,!(!this.isPeerOwner(e)||this.controlAssembler!==t)&&(t.inspect().pendingMessages||0)>0&&this.fail(g(),"control-wire-assembly-timeout",!0))},ut);this.controlWireDeadline=s}resolveCurrentConnect(){const e=this.resolveConnect;this.resolveConnect=null,this.rejectConnect=null,this.connectPromise=null,this.clearConnectTimer(),e?.()}rejectCurrentConnect(e){const t=this.rejectConnect;this.resolveConnect=null,this.rejectConnect=null,this.connectPromise=null,this.clearConnectTimer(),t?.(e)}signal(e){if(!St.has(String(e.type||"")))throw new Error("console-direct-signal-type-forbidden");const t=this.signalSocket;if(!t||t.readyState!==WebSocket.OPEN||!this.signalReady)throw S();t.send(JSON.stringify(e))}peerBufferedAmount(){let e=Number(this.controlChannel?.bufferedAmount||0);for(const t of this.channels.values())e+=Number(t.dataChannel?.bufferedAmount||0);return e}sendWire(e,t,i,s){if(e.readyState!=="open")throw new Error("console-direct-data-channel-not-open");const r=je(t,{messageId:this.nextMessageId(),kind:i,maxMessageBytes:s}),o=r.reduce((a,c)=>a+c.byteLength,0);if(o>Ce||this.peerBufferedAmount()>Ce-o)throw new Error("console-direct-send-buffer-full");for(const a of r)e.send(a)}sendControl(e){const t=this.controlChannel;if(!t||!this.directReady||!this.peerOwner)throw g();this.sendWire(t,JSON.stringify(e),"control",k)}fail(e,t,i=!0){this.lastFailure=X(e),this.teardown(t,e,i)}teardown(e,t,i,s=!1){const r=this.signalSocket,o=this.peerOwner;if(i&&r?.readyState===WebSocket.OPEN&&this.signalReady&&o)try{this.signal({type:"rtc-close",connectionId:o.connectionId,hubEpoch:o.hubEpoch,reason:String(e||"console-direct-closed").slice(0,120)})}catch{}s?this.stage="hub-offline":(this.generation+=1,this.stage="idle",this.signalSocket=null,this.signalReady=!1,this.consoleId="",this.cancelWorkspaceReauthAttempt()),this.hubEpoch="",this.peerOwner=null,this.directReady=!1,this.remoteDescriptionApplied=!1,this.pendingRemoteCandidates=[],this.invalidateLivenessEvidence(),this.clearConnectTimer(),this.clearPeerDisconnectTimer();const a=t||new Error(e||"console-direct-closed");this.rejectCurrentConnect(a);for(const[h,p]of this.pendingHttp)window.clearTimeout(p.timeout),p.detachAbort(),p.reject(a),this.pendingHttp.delete(h);for(const h of[...this.channels.values()])this.retireLogicalEntry(h,t?1011:1001,e,!!t,!1);this.clearControlWireDeadline(),this.controlAssembler?.dispose(),this.controlAssembler=null;const c=this.controlChannel;if(this.controlChannel=null,c){c.onopen=null,c.onmessage=null,c.onerror=null,c.onclose=null;try{c.close()}catch{}}const u=this.peer;if(this.peer=null,u){u.onicecandidate=null,u.onconnectionstatechange=null,u.oniceconnectionstatechange=null;try{u.close()}catch{}}if(!s)try{r?.close(1e3,String(e||"console-direct-closed").slice(0,120))}catch{}const d=this.wireBudget?.inspect();this.lastTerminalWireBytes=Number(d?.retainedBytes||0),this.lastTerminalWireReservations=Number(d?.reservationCount||0),this.wireBudget=null}async connect(){if(this.directReady&&this.peerOwner&&this.controlChannel?.readyState==="open")return;if(this.connectPromise)return this.connectPromise;const e=this.session;if(!e)throw S();const t=this.signalSocket?.readyState===WebSocket.OPEN&&this.signalReady;if(t&&this.stage==="hub-offline"){const r=V();throw this.lastFailure=X(r),r}if(t&&this.stage==="p2p"&&this.peerOwner&&this.peer){const r=this.generation,o=new Promise((a,c)=>{this.resolveConnect=a,this.rejectConnect=c});return this.connectPromise=o,this.connectTimer===null&&(this.connectTimer=window.setTimeout(()=>{r===this.generation&&!this.directReady&&this.fail(g(),"direct-ready-timeout",!0)},Y)),o}const i=this.generation+1;this.generation=i,this.stage="signal",this.lastFailure=null;const s=new Promise((r,o)=>{this.resolveConnect=r,this.rejectConnect=o});this.connectPromise=s,this.connectTimer=window.setTimeout(()=>{if(i!==this.generation)return;const r=this.stage==="signal"?S():g();this.fail(r,this.stage==="signal"?"signal-timeout":"direct-ready-timeout",!1)},this.stage==="signal"?at:Y);try{const r=new WebSocket(vt(e.workspaceId),[it,`${st}${e.accessToken}`]);this.signalSocket=r,r.addEventListener("message",o=>{if(!this.isSignalOwner(i,r)||typeof o.data!="string")return;let a;try{a=JSON.parse(o.data)}catch{this.fail(S(),"invalid-signal-json",!1);return}this.handleSignal(a,i,r)}),r.addEventListener("error",()=>{this.isSignalOwner(i,r)&&(this.detachSignalFromReadyPeer(i,r,"signal-detached-after-ready")||this.fail(S(),"signal-service-unavailable",!1))}),r.addEventListener("close",()=>{this.isSignalOwner(i,r)&&(this.detachSignalFromReadyPeer(i,r,"signal-closed-after-ready")||this.fail(S(),"signal-service-closed",!1))})}catch{this.fail(S(),"signal-constructor-failed",!1)}return s}handleSignal(e,t,i){if(!this.isSignalOwner(t,i))return;if(e.type==="workspace-reauth-required"){const r=this.session,o=String(e.workspaceId||r?.workspaceId||"").trim(),a=String(e.challengeNonce||"").trim();if(!r||o!==r.workspaceId||!N.test(a)){this.fail(S(),"workspace-reauth-owner-mismatch",!1);return}if(this.workspaceReauthAttempt?.challengeNonce===a&&this.workspaceReauthAttempt.generation===t&&this.workspaceReauthAttempt.socket===i)return;this.cancelWorkspaceReauthAttempt();const c=new AbortController,u={challengeNonce:a,generation:t,socket:i,controller:c,deadline:null},d=Number(e.deadlineAt||0),h=Number.isFinite(d)&&d>0?Math.max(1,d-Date.now()):me;u.deadline=window.setTimeout(()=>{this.workspaceReauthAttempt===u&&this.cancelWorkspaceReauthAttempt(t,i)},Math.max(1,Math.min(me,h))),this.workspaceReauthAttempt=u,(async()=>{try{await fetch(yt("/v2/rtc/reauth"),{method:"POST",headers:{Authorization:`Bearer ${r.accessToken}`,"Content-Type":"application/json"},body:JSON.stringify({workspaceId:r.workspaceId,challengeNonce:a}),signal:c.signal,cache:"no-store"})}catch{}})().finally(()=>{this.workspaceReauthAttempt===u&&(this.workspaceReauthAttempt=null,u.deadline!==null&&(window.clearTimeout(u.deadline),u.deadline=null))});return}if(e.type==="workspace-access-refreshed"||e.type==="workspace-reauth-ok"){e.type==="workspace-reauth-ok"&&this.cancelWorkspaceReauthAttempt(t,i);return}if(e.type==="workspace-access-expired"){this.fail(S(),"workspace-access-expired",!1);return}if(e.type==="signal-ready"){const r=String(e.consoleId||""),o=String(e.hubEpoch||"");if(e.role!=="console"||!N.test(r)){this.fail(S(),"invalid-signal-ready",!1);return}if(this.signalReady=!0,this.consoleId=r,e.hubOnline!==!0){const a=V();this.lastFailure=X(a),this.teardown("hub-offline",a,!1,!0);return}if(!N.test(o)){this.fail(S(),"invalid-hub-epoch",!1);return}if(this.peerOwner){this.peerOwner.hubEpoch!==o&&this.fail(g(),"hub-epoch-replaced",!1);return}this.hubEpoch=o,this.stage="p2p",this.lastFailure=null,this.clearConnectTimer(),this.connectTimer=window.setTimeout(()=>{t===this.generation&&!this.directReady&&this.fail(g(),"direct-ready-timeout",!0)},Y),this.beginPeer(t,o);return}const s=this.peerOwner;if(!(!s||String(e.connectionId||"")!==s.connectionId||String(e.hubEpoch||"")!==s.hubEpoch)){if(e.type==="rtc-answer")this.acceptAnswer(s,e.description);else if(e.type==="rtc-ice")this.acceptRemoteCandidate(s,e.candidate,e.sdpMid);else if(e.type==="rtc-close"){const r=String(e.reason||"rtc-closed"),o=/hub-offline/i.test(r)?V():g();this.fail(o,r,!1)}}}beginPeer(e,t){if(e!==this.generation||this.peer)return;const i=this.session;if(!i){this.fail(S(),"session-missing",!1);return}const s=et({userId:i.user.id,connectionId:crypto.randomUUID(),hubEpoch:t,generation:e});this.consecutiveMediaLogicalOpenFailures=0,this.peerOwner=s,this.wireBudget=Le({maxBytes:wt}),this.controlAssembler=ge({budget:this.wireBudget,maxMessageBytes:k});let r;try{r=new RTCPeerConnection({iceServers:[{urls:kt()}],iceTransportPolicy:"all",bundlePolicy:"max-bundle",rtcpMuxPolicy:"require"})}catch{this.fail(g(),"peer-constructor-failed",!0);return}this.peer=r;const o=r.createDataChannel(rt,{ordered:!0});o.binaryType="arraybuffer",this.controlChannel=o,r.onicecandidate=a=>{if(!this.isPeerOwner(s)||this.peer!==r||!a.candidate)return;const c=String(a.candidate.candidate||"");if(P(c)){this.fail(g(),"relay-ice-candidate-forbidden",!0);return}try{this.signal({type:"rtc-ice",connectionId:s.connectionId,hubEpoch:s.hubEpoch,candidate:c,sdpMid:a.candidate.sdpMid})}catch{this.fail(S(),"signal-ice-failed",!1)}},r.onconnectionstatechange=()=>{if(!(!this.isPeerOwner(s)||this.peer!==r)){if(r.connectionState==="failed"||r.connectionState==="closed"){this.fail(g(),`peer-${r.connectionState}`,!0);return}this.refreshPeerDisconnectDeadline(s,r)}},r.oniceconnectionstatechange=()=>{if(!(!this.isPeerOwner(s)||this.peer!==r)){if(r.iceConnectionState==="failed"||r.iceConnectionState==="closed"){this.fail(g(),`ice-${r.iceConnectionState}`,!0);return}this.refreshPeerDisconnectDeadline(s,r)}},o.onmessage=a=>{!this.isPeerOwner(s)||this.controlChannel!==o||this.handleControlWire(s,a.data)},o.onerror=()=>{!this.isPeerOwner(s)||this.controlChannel!==o||this.fail(g(),"control-channel-error",!0)},o.onclose=()=>{!this.isPeerOwner(s)||this.controlChannel!==o||this.fail(g(),"control-channel-closed",!0)},(async()=>{try{const a=await r.createOffer();if(!this.isPeerOwner(s)||this.peer!==r)return;if(a.type!=="offer"||P(a.sdp))throw new Error("relay-sdp-forbidden");if(await r.setLocalDescription(a),!this.isPeerOwner(s)||this.peer!==r)return;const c=r.localDescription||a;if(c.type!=="offer"||P(c.sdp))throw new Error("relay-sdp-forbidden");this.signal({type:"rtc-offer",connectionId:s.connectionId,hubEpoch:s.hubEpoch,description:{type:"offer",sdp:c.sdp||""}})}catch(a){if(!this.isPeerOwner(s))return;const c=/signal/i.test(String(a?.message||""))?S():g();this.fail(c,"rtc-offer-failed",!0)}})()}async acceptAnswer(e,t){const i=this.peer;if(!(!this.isPeerOwner(e)||!i||this.remoteDescriptionApplied)){if(t?.type!=="answer"||typeof t.sdp!="string"||P(t.sdp)){this.fail(g(),"rtc-answer-invalid",!0);return}try{if(await i.setRemoteDescription({type:"answer",sdp:t.sdp}),!this.isPeerOwner(e)||this.peer!==i)return;this.remoteDescriptionApplied=!0;const s=this.pendingRemoteCandidates;this.pendingRemoteCandidates=[];for(const r of s){if(!this.isPeerOwner(e)||this.peer!==i)return;await i.addIceCandidate(r.candidate?r:null)}}catch{this.isPeerOwner(e)&&this.fail(g(),"rtc-answer-apply-failed",!0)}}}async acceptRemoteCandidate(e,t,i){if(!this.isPeerOwner(e))return;const s=String(t??""),r=i==null?null:String(i);if(s.length>4*1024||r!==null&&r.length>256||P(s)){this.fail(g(),"rtc-candidate-invalid",!0);return}const o=this.peer;if(o){if(!this.remoteDescriptionApplied){if(this.pendingRemoteCandidates.length>=ft){this.fail(g(),"rtc-candidate-capacity",!0);return}this.pendingRemoteCandidates.push({candidate:s,sdpMid:r});return}try{await o.addIceCandidate(s?{candidate:s,sdpMid:r}:null)}catch{this.isPeerOwner(e)&&this.fail(g(),"rtc-candidate-apply-failed",!0)}}}handleControlWire(e,t){const i=this.controlAssembler;if(!(!this.isPeerOwner(e)||!i))try{const s=i.push(se(t));if(this.refreshControlWireDeadline(e,i),!s)return;if(s.kind!=="control"||typeof s.data!="string")throw new Error("control-wire-kind-invalid");const r=JSON.parse(s.data);if(r.type==="direct-ready"){if(r.connectionId!==e.connectionId||r.hubEpoch!==e.hubEpoch||this.controlChannel?.readyState!=="open")throw new Error("direct-ready-owner-invalid");this.directReady||(this.consecutiveLivenessRequestTimeouts=0,this.directReady=!0,this.stage="ready",this.resolveCurrentConnect());return}if(r.type==="http-response"){this.handleHttpResponse(e,r);return}if(r.type==="ws-opened"||r.type==="ws-closed"||r.type==="ws-error"){this.handleLogicalLifecycle(e,r);return}throw new Error("control-message-type-invalid")}catch{this.isPeerOwner(e)&&this.fail(g(),"control-wire-invalid",!0)}}handleLogicalLifecycle(e,t){if(t.connectionId!==e.connectionId||t.hubEpoch!==e.hubEpoch)throw new Error("logical-lifecycle-owner-invalid");const i=String(t.channelId||"");if(!N.test(i))throw new Error("logical-lifecycle-channel-invalid");const s=this.channels.get(i);if(!s)return;const r=s.dataChannel;if(!r||!s.openRequested||!this.logicalEntryIsCurrent(s,e,r))throw new Error("logical-lifecycle-owner-invalid");if(t.type==="ws-opened"){if(s.opened)return;s.opened=!0,(s.purpose==="frame"||s.purpose==="atlas")&&(this.consecutiveMediaLogicalOpenFailures=0),this.clearLogicalOpenDeadline(s);const o=s.pendingInbound.splice(0);if(s.pendingInboundBytes=0,s.socket.managerOpened(),!this.logicalEntryIsCurrent(s,e,r)||!s.opened)return;for(const a of o)this.deliverLogicalMessage(s,a);return}if(t.type==="ws-closed"){this.retireLogicalEntry(s,ie(t.code,1e3),String(t.reason||""),!1,!1);return}this.retireLogicalEntry(s,1011,String(t.error||"console-direct-websocket-error"),!0,!1)}handleHttpResponse(e,t){const i=String(t.requestId||""),s=this.pendingHttp.get(i);if(!s||s.generation!==e.generation||!this.isPeerOwner(e))return;this.consecutiveLivenessRequestTimeouts=0,this.livenessMediaRecoveryGeneration=0;const r=this.mediaAttachmentHoldGeneration===e.generation;this.pendingHttp.delete(i),window.clearTimeout(s.timeout),s.detachAbort();try{const o=Number(t.status||502),a=t.bodyBase64?At(t.bodyBase64):new TextEncoder().encode(JSON.stringify({ok:!1,error:t.error||"console-direct-request-failed"})),c=new Response(a,{status:Number.isInteger(o)&&o>=200&&o<=599?o:502,statusText:String(t.statusText||""),headers:t.headers||{"content-type":"application/json"}});s.resolve(c),r&&(this.mediaAttachmentHoldGeneration=0,this.attachHeldMediaChannels(e))}catch(o){s.reject(K(o))}}async fetch(e,t,i){await Ot(this.connect(),t?.signal);const s=this.peerOwner;if(!s||!this.directReady||this.controlChannel?.readyState!=="open")throw g();if(this.pendingHttp.size>=Ee)throw new Error("console-direct-http-capacity-reached");const r=await Mt(t?.body);if(r.byteLength>pt)throw new Error("console-direct-http-body-too-large");if(!this.isPeerOwner(s)||!this.directReady)throw g();if(this.pendingHttp.size>=Ee)throw new Error("console-direct-http-capacity-reached");if(t?.signal?.aborted)throw t.signal.reason||new Error("Mobile Console request cancelled.");const o=crypto.randomUUID(),a=Number.isFinite(i)&&i>0?Math.floor(i):4e3,c=String(t?.method||"GET").toUpperCase(),u=c==="GET"&&gt.has(e),d=u?this.livenessEvidenceEpoch:null,h=new Promise((p,w)=>{const C=(E,m=!1)=>{const L=this.pendingHttp.get(o);if(L){this.pendingHttp.delete(o),window.clearTimeout(L.timeout),L.detachAbort();try{this.sendControl({type:"http-cancel",requestId:o})}catch{}w(E),m&&u&&this.recordLivenessRequestTimeout(s,L.livenessEvidenceEpoch)}},b=()=>C(t?.signal?.reason instanceof Error?t.signal.reason:new Error("Mobile Console request cancelled."));t?.signal?.addEventListener("abort",b,{once:!0});const l=window.setTimeout(()=>{C(new Error("Mobile Console direct request timed out."),!0)},a);this.pendingHttp.set(o,{generation:s.generation,livenessEvidenceEpoch:d,resolve:p,reject:w,timeout:l,detachAbort:()=>t?.signal?.removeEventListener("abort",b)})});try{this.sendControl({type:"http-request",requestId:o,method:c,path:e,headers:Dt(t),bodyBase64:r.byteLength>0?Tt(r):""})}catch(p){const w=this.pendingHttp.get(o);w&&(this.pendingHttp.delete(o),window.clearTimeout(w.timeout),w.detachAbort(),w.reject(K(p))),this.fail(g(),"control-send-failed",!0)}return h}openWebSocket(e,t){if(!It.has(t))throw new Error("console-direct-purpose-invalid");if(this.channels.size>=ht)throw new Error("console-direct-channel-capacity-reached");const i=crypto.randomUUID(),s=new _t(this,i,t,e),r={channelId:i,path:e,purpose:t,socket:s,owner:null,dataChannel:null,assembler:null,openDeadline:null,wireDeadline:null,wireStartedAtEpochMs:0,wireLastProgressAtEpochMs:0,wireAbsoluteDeadlineAtEpochMs:0,openRequested:!1,opened:!1,pendingInbound:[],pendingInboundBytes:0};return this.channels.set(i,r),this.connect().then(()=>{this.channels.get(i)===r&&this.attachLogicalChannel(r)}).catch(o=>{this.channels.get(i)===r&&this.retireLogicalEntry(r,1011,K(o).message,!0,!1)}),s}attachLogicalChannel(e){const t=this.peerOwner,i=this.peer,s=this.wireBudget;if(!t||!i||!s||!this.directReady||this.channels.get(e.channelId)!==e){this.retireLogicalEntry(e,1011,"console-direct-not-ready",!0,!1);return}const r=e.purpose==="frame"||e.purpose==="atlas";if(r&&this.mediaAttachmentHoldGeneration===t.generation)return;e.owner=t;let o;try{o=i.createDataChannel(`${ot}${e.channelId}:${e.purpose}`,{ordered:!0})}catch{this.retireLogicalOpenFailure(e,t,1011,"console-direct-channel-create-failed",!0,!0);return}o.binaryType="arraybuffer",e.dataChannel=o,e.assembler=ge({budget:s,maxMessageBytes:r?Se:k,maxPendingMessages:r?mt:2}),this.refreshLogicalOpenDeadline(e,t,o),o.onopen=()=>{if(this.logicalEntryIsCurrent(e,t,o))try{this.sendControl({type:"ws-open",connectionId:t.connectionId,hubEpoch:t.hubEpoch,channelId:e.channelId,purpose:e.purpose,path:e.path}),e.openRequested=!0}catch{this.retireLogicalEntry(e,1011,"console-direct-ws-open-failed",!0,!1)}},o.onmessage=a=>{this.logicalEntryIsCurrent(e,t,o)&&this.handleLogicalWire(e,t,a.data)},o.onerror=()=>{this.logicalEntryIsCurrent(e,t,o)&&this.retireLogicalOpenFailure(e,t,1011,"console-direct-channel-error",!0,!1)},o.onclose=()=>{this.logicalEntryIsCurrent(e,t,o)&&this.retireLogicalOpenFailure(e,t,1006,"console-direct-channel-closed",!1,!1)}}retireLogicalOpenFailure(e,t,i,s,r,o){if(this.channels.get(e.channelId)!==e||!this.isPeerOwner(t))return;const a=!e.openRequested&&(e.purpose==="frame"||e.purpose==="atlas");this.logicalChannelFailureCount+=1,this.lastLogicalChannelFailure=Object.freeze({reason:String(s||"console-direct-logical-channel-failed").slice(0,120),purpose:e.purpose,generation:t.generation,occurredAt:Date.now()}),a&&(this.consecutiveMediaLogicalOpenFailures+=1);const c=[...this.channels.values()].some(d=>d!==e&&d.opened&&(d.purpose==="frame"||d.purpose==="atlas")&&d.dataChannel?.readyState==="open"),u=a&&!c&&(o||this.consecutiveMediaLogicalOpenFailures>=Ct);this.retireLogicalEntry(e,i,s,r,!1),u&&this.isPeerOwner(t)&&this.fail(g(),"console-direct-media-channel-unusable",!0)}logicalEntryIsCurrent(e,t,i){return this.channels.get(e.channelId)===e&&e.owner===t&&e.dataChannel===i&&this.isPeerOwner(t)}clearLogicalOpenDeadline(e){e.openDeadline!==null&&(window.clearTimeout(e.openDeadline),e.openDeadline=null)}refreshLogicalOpenDeadline(e,t,i){this.clearLogicalOpenDeadline(e);const s=window.setTimeout(()=>{e.openDeadline===s&&(e.openDeadline=null,!(!this.logicalEntryIsCurrent(e,t,i)||e.opened)&&this.retireLogicalOpenFailure(e,t,1011,"console-direct-logical-open-timeout",!0,!1))},lt);e.openDeadline=s}invalidateLivenessEvidence(){this.livenessEvidenceEpoch+=1,this.consecutiveLivenessRequestTimeouts=0,this.livenessMediaRecoveryGeneration=0,this.peerOwner||(this.mediaAttachmentHoldGeneration=0)}attachHeldMediaChannels(e){if(!this.isPeerOwner(e)||!this.directReady)return 0;let t=0;for(const i of[...this.channels.values()])i.owner!==null||i.dataChannel!==null||i.purpose!=="frame"&&i.purpose!=="atlas"||(this.attachLogicalChannel(i),t+=1);return t}recoverControlFromMediaPressure(e){if(!this.isPeerOwner(e)||this.livenessMediaRecoveryGeneration===e.generation)return!1;const t=[...this.channels.values()].filter(i=>(i.purpose==="frame"||i.purpose==="atlas")&&i.owner===e&&i.dataChannel?.readyState==="open");if(t.length===0)return!1;this.livenessMediaRecoveryGeneration=e.generation,this.mediaAttachmentHoldGeneration=e.generation,this.livenessEvidenceEpoch+=1,this.consecutiveLivenessRequestTimeouts=0,this.controlMediaRecoveryCount+=1,this.lastControlMediaRecovery=Object.freeze({generation:e.generation,retiredMediaLanes:t.length,occurredAt:Date.now()});for(const i of t)this.retireLogicalEntry(i,1013,"console-direct-control-media-recovery",!1,!0);return!0}recordLivenessRequestTimeout(e,t){t===null||t!==this.livenessEvidenceEpoch||!this.isPeerOwner(e)||!this.directReady||(this.livenessRequestTimeoutCount+=1,this.consecutiveLivenessRequestTimeouts+=1,!(this.consecutiveLivenessRequestTimeouts<2)&&(this.recoverControlFromMediaPressure(e)||this.fail(g(),"control-http-timeout",!0)))}handleLogicalWire(e,t,i){const s=e.assembler;if(!s||!this.logicalEntryIsCurrent(e,t,e.dataChannel))return;const r=s.inspect();let o={chunkBytes:0,headerReadable:!1,messageId:0,chunkIndex:0,chunkCount:0,totalLength:0};try{o=Rt(i);const a=s.push(se(i));if(this.refreshLogicalWireDeadline(e,t),!a)return;if(a.kind==="control")throw new Error("logical-control-forbidden");let c;if(a.kind==="text"&&typeof a.data=="string")c={kind:"text",data:a.data,byteLength:a.byteLength};else if(a.kind==="binary"&&a.data instanceof Uint8Array)c={kind:"binary",data:a.data,byteLength:a.byteLength};else throw new Error("logical-wire-kind-invalid");if(!e.opened){this.queuePreOpenLogicalMessage(e,c);return}this.deliverLogicalMessage(e,c)}catch(a){this.logicalWireFailureCount+=1,this.lastLogicalWireFailure=Object.freeze({errorCode:a instanceof B?a.code:a instanceof Error?a.message.slice(0,120):"logical-wire-invalid",purpose:e.purpose,generation:t.generation,pendingMessagesBefore:Math.max(0,Number(r.pendingMessages||0)),pendingBytesBefore:Math.max(0,Number(r.pendingBytes||0)),...o,occurredAt:Date.now()}),this.retireLogicalEntry(e,1011,"console-direct-wire-invalid",!0,!1)}}queuePreOpenLogicalMessage(e,t){const i=e.purpose==="frame"||e.purpose==="atlas",s=i?Se:k;if(i){if(e.pendingInbound.length===0){e.pendingInbound.push(t),e.pendingInboundBytes=t.byteLength;return}if(e.pendingInbound.length<Et&&e.pendingInboundBytes+t.byteLength<=s){e.pendingInbound.push(t),e.pendingInboundBytes+=t.byteLength;return}const r=e.pendingInbound[0],o=r.byteLength+t.byteLength,a=o<=s?"replace-latest":"drop-over-budget";a==="replace-latest"&&(e.pendingInbound=[r,t],e.pendingInboundBytes=o),this.preOpenMediaCoalesceCount+=1,this.lastPreOpenMediaCoalesce=Object.freeze({action:a,purpose:e.purpose==="atlas"?"atlas":"frame",generation:e.owner?.generation||0,retainedMessages:e.pendingInbound.length,retainedBytes:e.pendingInboundBytes,incomingBytes:t.byteLength,occurredAt:Date.now()});return}if(e.pendingInbound.length>=bt||e.pendingInboundBytes+t.byteLength>s)throw new Error("logical-preopen-buffer-full");e.pendingInbound.push(t),e.pendingInboundBytes+=t.byteLength}deliverLogicalMessage(e,t){if(t.kind==="text"){e.socket.managerMessage(t.data);return}const i=t.data,s=i.buffer.slice(i.byteOffset,i.byteOffset+i.byteLength);e.socket.managerMessage(e.socket.binaryType==="arraybuffer"?s:new Blob([s]))}logicalBufferedAmount(e){return Number(this.channels.get(e)?.dataChannel?.bufferedAmount||0)}clearLogicalWireDeadline(e){e.wireDeadline!==null&&(window.clearTimeout(e.wireDeadline),e.wireDeadline=null),e.wireStartedAtEpochMs=0,e.wireLastProgressAtEpochMs=0,e.wireAbsoluteDeadlineAtEpochMs=0}scheduleLogicalWireDeadline(e,t){e.wireDeadline!==null&&window.clearTimeout(e.wireDeadline);const i=e.purpose==="frame"||e.purpose==="atlas",s=Date.now(),r=e.assembler?.inspect(),o=i?we(Number(r?.pendingBytes||0)):Number.POSITIVE_INFINITY,a=i?Math.max(1,o-(s-e.wireLastProgressAtEpochMs)):Number.POSITIVE_INFINITY,c=Math.max(1,e.wireAbsoluteDeadlineAtEpochMs-s),u=window.setTimeout(()=>{if(e.wireDeadline!==u)return;e.wireDeadline=null;const d=e.dataChannel;if(!d||!this.logicalEntryIsCurrent(e,t,d))return;const h=e.assembler?.inspect(),p=Number(h?.pendingMessages||0);if(p===0){this.clearLogicalWireDeadline(e);return}const w=Date.now(),C=Math.max(0,w-e.wireLastProgressAtEpochMs),b=Math.max(0,w-e.wireStartedAtEpochMs),l=i?we(Number(h?.pendingBytes||0)):Number.POSITIVE_INFINITY;if(!(!i||C>=l||w>=e.wireAbsoluteDeadlineAtEpochMs)){this.scheduleLogicalWireDeadline(e,t);return}if(i){this.partialMediaWireTimeoutCount+=1,this.lastPartialMediaWireTimeout=Object.freeze({recovery:"abandon-message",purpose:e.purpose,generation:t.generation,pendingMessages:p,pendingBytes:Math.max(0,Number(h?.pendingBytes||0)),stalledForMs:C,lifetimeMs:b,idleDeadlineMs:l,absoluteDeadlineMs:Math.max(0,e.wireAbsoluteDeadlineAtEpochMs-e.wireStartedAtEpochMs),occurredAt:w}),e.assembler?.abandonPending(),this.clearLogicalWireDeadline(e);return}this.retireLogicalEntry(e,1011,"console-direct-partial-message-timeout",!0,!1)},Math.min(a,c));e.wireDeadline=u}refreshLogicalWireDeadline(e,t){const i=e.assembler?.inspect();if(Number(i?.pendingMessages||0)===0){this.clearLogicalWireDeadline(e);return}const r=Date.now(),o=e.purpose==="frame"||e.purpose==="atlas";e.wireStartedAtEpochMs<=0&&(e.wireStartedAtEpochMs=r,e.wireAbsoluteDeadlineAtEpochMs=r+(o?Ze(Number(i?.pendingBytes||0)):dt)),e.wireLastProgressAtEpochMs=r,this.scheduleLogicalWireDeadline(e,t)}sendLogicalMessage(e,t){const i=this.channels.get(e),s=i?.dataChannel;if(!i||!s||!i.opened||s.readyState!=="open")throw new DOMException("WebSocket is not open.","InvalidStateError");try{if(typeof t=="string")this.sendWire(s,t,"text",k);else if(t instanceof ArrayBuffer)this.sendWire(s,t,"binary",k);else if(ArrayBuffer.isView(t)){const r=new Uint8Array(t.buffer,t.byteOffset,t.byteLength);this.sendWire(s,r,"binary",k)}else throw new DOMException("Blob sends are not supported by the direct Console channel.","NotSupportedError")}catch(r){throw this.retireLogicalEntry(i,1011,"console-direct-send-failed",!0,!1),r}}closeLogicalChannel(e,t,i,s){const r=this.channels.get(e);r&&this.retireLogicalEntry(r,t,i,!1,s)}retireLogicalEntry(e,t,i,s,r){if(this.channels.get(e.channelId)!==e)return;this.channels.delete(e.channelId);const o=e.dataChannel,a=e.owner;if(r&&a&&e.openRequested&&this.isPeerOwner(a)&&this.controlChannel?.readyState==="open")try{this.sendControl({type:"ws-close",connectionId:a.connectionId,hubEpoch:a.hubEpoch,channelId:e.channelId,code:t,reason:i})}catch{s=!0}if(this.clearLogicalWireDeadline(e),this.clearLogicalOpenDeadline(e),e.assembler?.dispose(),e.assembler=null,e.pendingInbound=[],e.pendingInboundBytes=0,e.dataChannel=null,e.owner=null,e.openRequested=!1,e.opened=!1,o){o.onopen=null,o.onmessage=null,o.onerror=null,o.onclose=null;try{o.close()}catch{}}e.socket.managerClosed(t,i,s)}}const I=new Pt;function Ae(n){I.configure(n)}function Nt(){I.preferDirect()}function xt(){I.markLocalHubReachable()}function ln(){I.resume()}function un(){I.suspendLivenessEvidence()}function dn(){I.retry()}function J(n,e,t){return I.preferDirect(),I.fetch(n,e,t)}function hn(n,e,t){return I.shouldUseDirect()?I.openWebSocket(e,t):new WebSocket(n)}function fn(){return I.inspect()}function Wt(n){let e=null;return{run(){if(e)return e;const t=Promise.resolve().then(n);e=t.finally(()=>{(e===t||e===i)&&(e=null)});const i=e;return i}}}async function Bt(n,e){return n&&await n.catch(()=>{}),e()}function Ut(n,e={}){if(e.publicConsole===!0)throw new Error("Public Console does not create a Hub cookie session.");return n?.workspaceKind==="team"&&n?.workspaceRole==="operator"?{accessToken:n.accessToken,workspaceId:n.workspaceId}:{accessToken:n.accessToken,refreshToken:n.refreshToken,expiresAt:n.expiresAt,user:n.user,workspaceId:n.workspaceId}}function le(n){return!n?.user?.id||!n?.workspaceId?"":[n.user.id,n.workspaceId,n.workspaceKind,n.workspaceRole,Number(n.membershipRevision||0)].join(`
7
7
  `)}function Ht(n){const e=le(n);return!e||!n?.accessToken||!n?.refreshToken?"":[e,n.accessToken,n.refreshToken,Number(n.expiresAt||0)].join(`
8
- `)}function Ft({publicConsole:n,path:e,requiresUiSession:t}){return n?String(e||"").split("?")[0]==="/api/auth/session"?"reject-auth-session":t?"direct":"local":"local"}const x=5179,_=H(`http://127.0.0.1:${x}`);function qt(n){const e=n.split(".").map(t=>Number(t));return e.length===4&&e.every(t=>Number.isInteger(t)&&t>=0&&t<=255)?e:[]}function Me(n){const e=n.trim().toLowerCase();return e==="localhost"||e==="127.0.0.1"||e==="::1"||e==="[::1]"}function Gt(n){const e=n.trim().toLowerCase();return!e||e==="0.0.0.0"||e==="::"||e==="[::]"}function De(n){const e=qt(n);return e.length===0?!1:e[0]===10||e[0]===172&&e[1]>=16&&e[1]<=31||e[0]===192&&e[1]===168}function jt(n){return n.includes(":")&&!n.startsWith("[")?`[${n}]`:n}function $t(n){const e=String(n||"").trim();if(!e)return null;const t=/^[a-z][a-z\d+.-]*:\/\//i.test(e)?e:`tcp://${e}`;try{const i=new URL(t),s=i.hostname.replace(/^\[|\]$/g,""),r=Number(i.port);return!s||!Number.isInteger(r)||r<1||r>65535||Gt(s)?null:{host:s,port:r}}catch{return null}}function H(n){const e=String(n||"").trim()||`http://127.0.0.1:${x}`,t=/^[a-z][a-z\d+.-]*:\/\//i.test(e)?e:`http://${e}`;try{const i=new URL(t);return i.port||(i.port=String(x)),i.hash="",i.search="",i.toString().replace(/\/+$/,"")}catch{return`http://127.0.0.1:${x}`}}function zt(n){const e=$t(n);return e?H(`http://${jt(e.host)}:${x}`):""}function pn(n){const t=[n?.endpoint,...Array.isArray(n?.endpoint_candidates)?n.endpoint_candidates:[]].map(i=>zt(String(i||""))).filter(Boolean);return[...new Set(t)]}function Vt(n=_){if(!(typeof window>"u"||window.location.protocol!=="https:"))try{const e=new URL(n);if(e.protocol!=="http:")return;if(Me(e.hostname))return"loopback";if(De(e.hostname))return"local"}catch{return}}function gn(n,e=_){const t=new URL(n,H(e));return t.protocol=t.protocol==="https:"?"wss:":"ws:",t.toString()}let v=null,O="",A="";const y=new Map,R=new Map,U=new Map,q=new Set;function $(n){const e=U.get(n);e!==void 0&&typeof window<"u"&&window.clearTimeout(e),U.delete(n)}function Re(){for(const n of U.keys())$(n)}function re(n){const e=Number(n||0);if(Number.isFinite(e)&&e>0)return e<1e10?e*1e3:e;const t=Date.parse(String(n||""));return Number.isFinite(t)?t:0}function Oe(n,e,t){if($(n),typeof window>"u"||e<=Date.now())return;const i=Math.max(1e3,Math.min(45e3,e-Date.now()-15e3)),s=window.setTimeout(()=>{U.delete(n),!(t!==O||v?.workspaceKind!=="team")&&j(n,!0).catch(()=>{})},i);U.set(n,s)}function Xt(n,e,t){const i=v?.workspaceKind==="team",s=i?re(e.accessExpiresAt)||Date.now()+6e4:0;y.set(n,{csrfToken:e.csrfToken,expiresAt:e.expiresAt,accessExpiresAt:s,ownerSignature:A}),q.add(n),i?Oe(n,s,t):$(n)}function wn(n){const e=n?.accessToken&&n?.refreshToken&&n?.user?.id?n:null,t=le(e),i=Ht(e);if(t!==A&&(e&&y.clear(),A=t),i!==O&&(Re(),O=i,e?.workspaceKind==="team"))for(const[s,r]of y)r.ownerSignature===t&&Oe(s,r.accessExpiresAt,i);v=e,Ae(e?{accessToken:e.accessToken,user:{id:e.user.id},workspaceId:e.workspaceId,workspaceKind:e.workspaceKind,workspaceRole:e.workspaceRole,membershipRevision:e.membershipRevision}:null),e&&z()&&Nt()}function z(){if(typeof window>"u"||window.location.protocol!=="https:")return!1;const n=window.location.hostname.replace(/^\[|\]$/g,"");return!Me(n)&&!De(n)}function Q(n){const e=n.split("?")[0];return e==="/api/health"||e==="/api/runtime/status"||e==="/api/auth/status"||e==="/api/auth/session"}async function G(n,e,t,i,s){const r=Vt(t);return r&&(e.targetAddressSpace=r),Ne(s,i,async o=>{e.signal=o;try{return await fetch(`${t}${n}`,e)}catch(a){throw!o.aborted&&r?new Error("VuvoDesk Hub is not reachable at this local network address. If Chrome shows a local network prompt, allow it; otherwise restart the Hub so port 5179 is open on the network."):a}})}async function j(n,e=!1){if(z())throw new Error("Public Console uses verified P2P authorization instead of a Hub cookie session.");const t=y.get(n);if(!e&&t&&t.ownerSignature===A&&t.expiresAt>Date.now()+3e4&&(t.accessExpiresAt===0||t.accessExpiresAt>Date.now()+15e3))return;e&&y.delete(n);const i=v,s=O;if(!i)return;const r=R.get(n);if(r)return r.authSignature===s?r.promise:r.promise.catch(()=>{}).then(()=>j(n,e));let o;const a=(async()=>{const c=await G("/api/auth/session",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(Ut(i,{publicConsole:!1})),credentials:"include",cache:"no-store"},n,W),u=await c.json().catch(()=>null);if(!c.ok)throw new Error(`${c.status} ${u?.error||c.statusText}`);const d=String(u?.uiSession?.csrfToken||"").trim(),h=Number(u?.uiSession?.expiresAt||0);if(!d||!Number.isFinite(h)||h<=Date.now())throw new Error("VuvoDesk Hub did not issue a valid UI session.");if(s!==O)throw y.set(n,{csrfToken:d,expiresAt:h,accessExpiresAt:re(u?.uiSession?.accessExpiresAt),ownerSignature:le(i)}),q.add(n),new Error("VuvoDesk workspace changed while the Hub session was opening.");Xt(n,{csrfToken:d,expiresAt:h,accessExpiresAt:re(u?.uiSession?.accessExpiresAt)},s)})().finally(()=>{R.get(n)===o&&R.delete(n)});return o={authSignature:s,promise:a},R.set(n,o),a}async function Yt(){const n=new Set(q);for(const t of R.keys())n.add(t);z()||n.add(H(_));const e=[...n].map(t=>Bt(R.get(t)?.promise,async()=>{const i=y.get(t),s=await G("/api/auth/session",{method:"DELETE",headers:i?.csrfToken?{"X-LiveDesk-CSRF":i.csrfToken}:{},credentials:"include",cache:"no-store"},t,W);if(!s.ok&&s.status!==401){const r=await s.json().catch(()=>null);throw new Error(`${s.status} ${r?.error||s.statusText}`)}q.delete(t),y.delete(t),$(t)}));await Promise.all(e)}const Kt=Wt(Yt);function mn(){return v=null,O="",A="",Re(),Ae(null),Kt.run()}async function Jt(n,e,t=_){const i=H(t),{timeoutMs:s=W,signal:r,transport:o="auto",...a}=e||{},c=!Q(n),u=c?A:"",d=()=>!c||!!u&&u===A,h=()=>{if(!d())throw new Error("VuvoDesk workspace changed while the Hub request was running.")},p=Ft({publicConsole:z(),path:n,requiresUiSession:c});if(c&&!v)throw new Error("VuvoDesk account authority is not available for this Hub request.");const w=()=>{const C=String(a.method||"GET").toUpperCase(),b=y.get(i),l=b?.ownerSignature===A?b.csrfToken:"";return{...a,...r?{signal:r}:{},headers:{...a.headers||{},...!["GET","HEAD","OPTIONS"].includes(C)&&l?{"X-LiveDesk-CSRF":l}:{}},credentials:"include",cache:"no-store"}};if(p==="reject-auth-session")throw new Error("Public Console does not create a Hub cookie session.");if(p==="direct"){if(h(),r?.aborted)throw r.reason||new Error("VuvoDesk Hub request cancelled.");return J(n,w(),s)}if(o==="p2p"){if(h(),r?.aborted)throw r.reason||new Error("VuvoDesk Hub request cancelled.");if(!v||Q(n))throw new Error("Mobile Console direct connection requires an authenticated management request.");return J(n,w(),s)}try{c&&await j(i),h();let C=await G(n,w(),i,s,r);if(h(),xt(),c&&C.status===401&&v){const b=await C.clone().json().catch(()=>null);/^hub-ui-session-(required|expired)$/.test(String(b?.error||""))&&(await j(i,!0),h(),C=await G(n,w(),i,s,r),h())}return C}catch(C){if(!d()||r?.aborted||!v||Q(n))throw C;return J(n,w(),s)}}async function Qt(n,e,t=_){const i=await Jt(n,{...e||{},headers:{"Content-Type":"application/json",...e?.headers||{}}},t);let s=null;try{s=await i.json()}catch(r){if(e?.signal?.aborted)throw r;s=null}if(!i.ok){const r=s&&typeof s=="object"&&"error"in s?String(s.error||""):"";throw new Error(`${i.status} ${r||i.statusText}`)}return s}async function bn({baseUrl:n=_,targetRole:e,previousRuntimePid:t=0,allowedStates:i=["running","waiting-auth"],timeoutMs:s=3e4}){const r=Date.now()+s;let o=null;for(;Date.now()<r;){try{const c=await Qt("/api/runtime/status",void 0,n),u=String(c.role||"").toLowerCase(),d=String(c.state||"").toLowerCase(),h=Number(c.runtimePid||0),p=t>1?h>1&&h!==t:h>1;if(u===e&&i.map(w=>w.toLowerCase()).includes(d)&&p)return c}catch(c){o=c}await new Promise(c=>setTimeout(c,300))}const a=o instanceof Error?` ${o.message}`:"";throw new Error(`VuvoDesk runtime did not become ${e} with a new process.${a}`)}function En(n,e=120){if(typeof window>"u")return;const t=new URL(window.location.href);t.port=String(n),t.pathname="/",t.search="",t.hash="";const i=t.toString();let s=0;const r=()=>{fetch(i,{cache:"no-store",mode:"no-cors"}).then(()=>window.location.replace(i)).catch(()=>{if(s+=1,s>=e){window.location.replace(i);return}window.setTimeout(r,250)})};r()}function Cn(n){if(!n)return"-";const e=Date.now()-Date.parse(n);return Number.isFinite(e)?e<1e3?"now":e<6e4?`${Math.round(e/1e3)}s`:e<36e5?`${Math.round(e/6e4)}m`:`${Math.round(e/36e5)}h`:"-"}async function Sn(n){await navigator.clipboard.writeText(n)}const Zt=5,en=15,tn=50,nn=Number.POSITIVE_INFINITY,sn=5,In={free:{name:"Free",price:"$0",limit:Zt,people:1,audience:"Personal, ad-supported",note:"Up to 5 personal devices supported by a standard banner."},ltd:{name:"Plus",price:"$5",ltd:"$79 LTD",limit:en,people:1,audience:"Creators and homelabs",note:"15 devices, no ads, personal use."},pro:{name:"Pro / Business",price:"$15",ltd:"$199 LTD",limit:tn,people:1,audience:"Schools, academies, studios, and businesses",note:"Up to 50 devices for school, academy, and commercial use."},team:{name:"Team",price:"$49",limit:nn,people:sn,audience:"Shared business operations",note:"Commercial use, a shared Team Space, and up to 5 people using their own accounts."}},rn="0.1.680",on={version:rn},vn=String(on.version).trim();function yn({size:n=22}){return M.jsxs("svg",{width:n,height:n,viewBox:"0 0 28 28",fill:"none",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",children:[M.jsx("rect",{x:"2.5",y:"4",width:"14",height:"9",rx:"1.5",stroke:"currentColor",strokeWidth:"1.7",opacity:"0.42"}),M.jsx("path",{d:"M7 16v1.5h5",stroke:"currentColor",strokeWidth:"1.7",strokeLinecap:"round",opacity:"0.42"}),M.jsx("rect",{x:"10.5",y:"2.5",width:"14",height:"9",rx:"1.5",stroke:"currentColor",strokeWidth:"1.7",opacity:"0.62"}),M.jsx("path",{d:"M15 14.5V16h5",stroke:"currentColor",strokeWidth:"1.7",strokeLinecap:"round",opacity:"0.62"}),M.jsx("rect",{x:"6.5",y:"8.5",width:"15",height:"10",rx:"1.7",fill:"currentColor",fillOpacity:"0.12",stroke:"currentColor",strokeWidth:"1.9"}),M.jsx("path",{d:"M12 21v1.5H9.5M16 21v1.5h2.5M9.5 22.5h9",stroke:"currentColor",strokeWidth:"1.9",strokeLinecap:"round"})]})}export{_ as D,Zt as F,vn as L,yn as M,en as P,sn as T,tn as a,mn as b,Sn as c,X as d,dn as e,Cn as f,Vt as g,Qt as h,z as i,hn as j,gn as k,H as l,ln as m,En as n,Jt as o,In as p,wn as q,cn as r,un as s,pn as t,fn as u,bn as w};
8
+ `)}function Ft({publicConsole:n,path:e,requiresUiSession:t}){return n?String(e||"").split("?")[0]==="/api/auth/session"?"reject-auth-session":t?"direct":"local":"local"}const x=5179,_=H(`http://127.0.0.1:${x}`);function qt(n){const e=n.split(".").map(t=>Number(t));return e.length===4&&e.every(t=>Number.isInteger(t)&&t>=0&&t<=255)?e:[]}function Me(n){const e=n.trim().toLowerCase();return e==="localhost"||e==="127.0.0.1"||e==="::1"||e==="[::1]"}function Gt(n){const e=n.trim().toLowerCase();return!e||e==="0.0.0.0"||e==="::"||e==="[::]"}function De(n){const e=qt(n);return e.length===0?!1:e[0]===10||e[0]===172&&e[1]>=16&&e[1]<=31||e[0]===192&&e[1]===168}function jt(n){return n.includes(":")&&!n.startsWith("[")?`[${n}]`:n}function $t(n){const e=String(n||"").trim();if(!e)return null;const t=/^[a-z][a-z\d+.-]*:\/\//i.test(e)?e:`tcp://${e}`;try{const i=new URL(t),s=i.hostname.replace(/^\[|\]$/g,""),r=Number(i.port);return!s||!Number.isInteger(r)||r<1||r>65535||Gt(s)?null:{host:s,port:r}}catch{return null}}function H(n){const e=String(n||"").trim()||`http://127.0.0.1:${x}`,t=/^[a-z][a-z\d+.-]*:\/\//i.test(e)?e:`http://${e}`;try{const i=new URL(t);return i.port||(i.port=String(x)),i.hash="",i.search="",i.toString().replace(/\/+$/,"")}catch{return`http://127.0.0.1:${x}`}}function zt(n){const e=$t(n);return e?H(`http://${jt(e.host)}:${x}`):""}function pn(n){const t=[n?.endpoint,...Array.isArray(n?.endpoint_candidates)?n.endpoint_candidates:[]].map(i=>zt(String(i||""))).filter(Boolean);return[...new Set(t)]}function Vt(n=_){if(!(typeof window>"u"||window.location.protocol!=="https:"))try{const e=new URL(n);if(e.protocol!=="http:")return;if(Me(e.hostname))return"loopback";if(De(e.hostname))return"local"}catch{return}}function gn(n,e=_){const t=new URL(n,H(e));return t.protocol=t.protocol==="https:"?"wss:":"ws:",t.toString()}let v=null,O="",A="";const y=new Map,R=new Map,U=new Map,q=new Set;function $(n){const e=U.get(n);e!==void 0&&typeof window<"u"&&window.clearTimeout(e),U.delete(n)}function Re(){for(const n of U.keys())$(n)}function re(n){const e=Number(n||0);if(Number.isFinite(e)&&e>0)return e<1e10?e*1e3:e;const t=Date.parse(String(n||""));return Number.isFinite(t)?t:0}function Oe(n,e,t){if($(n),typeof window>"u"||e<=Date.now())return;const i=Math.max(1e3,Math.min(45e3,e-Date.now()-15e3)),s=window.setTimeout(()=>{U.delete(n),!(t!==O||v?.workspaceKind!=="team")&&j(n,!0).catch(()=>{})},i);U.set(n,s)}function Xt(n,e,t){const i=v?.workspaceKind==="team",s=i?re(e.accessExpiresAt)||Date.now()+6e4:0;y.set(n,{csrfToken:e.csrfToken,expiresAt:e.expiresAt,accessExpiresAt:s,ownerSignature:A}),q.add(n),i?Oe(n,s,t):$(n)}function wn(n){const e=n?.accessToken&&n?.refreshToken&&n?.user?.id?n:null,t=le(e),i=Ht(e);if(t!==A&&(e&&y.clear(),A=t),i!==O&&(Re(),O=i,e?.workspaceKind==="team"))for(const[s,r]of y)r.ownerSignature===t&&Oe(s,r.accessExpiresAt,i);v=e,Ae(e?{accessToken:e.accessToken,user:{id:e.user.id},workspaceId:e.workspaceId,workspaceKind:e.workspaceKind,workspaceRole:e.workspaceRole,membershipRevision:e.membershipRevision}:null),e&&z()&&Nt()}function z(){if(typeof window>"u"||window.location.protocol!=="https:")return!1;const n=window.location.hostname.replace(/^\[|\]$/g,"");return!Me(n)&&!De(n)}function Q(n){const e=n.split("?")[0];return e==="/api/health"||e==="/api/runtime/status"||e==="/api/auth/status"||e==="/api/auth/session"}async function G(n,e,t,i,s){const r=Vt(t);return r&&(e.targetAddressSpace=r),Ne(s,i,async o=>{e.signal=o;try{return await fetch(`${t}${n}`,e)}catch(a){throw!o.aborted&&r?new Error("VuvoDesk Hub is not reachable at this local network address. If Chrome shows a local network prompt, allow it; otherwise restart the Hub so port 5179 is open on the network."):a}})}async function j(n,e=!1){if(z())throw new Error("Public Console uses verified P2P authorization instead of a Hub cookie session.");const t=y.get(n);if(!e&&t&&t.ownerSignature===A&&t.expiresAt>Date.now()+3e4&&(t.accessExpiresAt===0||t.accessExpiresAt>Date.now()+15e3))return;e&&y.delete(n);const i=v,s=O;if(!i)return;const r=R.get(n);if(r)return r.authSignature===s?r.promise:r.promise.catch(()=>{}).then(()=>j(n,e));let o;const a=(async()=>{const c=await G("/api/auth/session",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(Ut(i,{publicConsole:!1})),credentials:"include",cache:"no-store"},n,W),u=await c.json().catch(()=>null);if(!c.ok)throw new Error(`${c.status} ${u?.error||c.statusText}`);const d=String(u?.uiSession?.csrfToken||"").trim(),h=Number(u?.uiSession?.expiresAt||0);if(!d||!Number.isFinite(h)||h<=Date.now())throw new Error("VuvoDesk Hub did not issue a valid UI session.");if(s!==O)throw y.set(n,{csrfToken:d,expiresAt:h,accessExpiresAt:re(u?.uiSession?.accessExpiresAt),ownerSignature:le(i)}),q.add(n),new Error("VuvoDesk workspace changed while the Hub session was opening.");Xt(n,{csrfToken:d,expiresAt:h,accessExpiresAt:re(u?.uiSession?.accessExpiresAt)},s)})().finally(()=>{R.get(n)===o&&R.delete(n)});return o={authSignature:s,promise:a},R.set(n,o),a}async function Yt(){const n=new Set(q);for(const t of R.keys())n.add(t);z()||n.add(H(_));const e=[...n].map(t=>Bt(R.get(t)?.promise,async()=>{const i=y.get(t),s=await G("/api/auth/session",{method:"DELETE",headers:i?.csrfToken?{"X-LiveDesk-CSRF":i.csrfToken}:{},credentials:"include",cache:"no-store"},t,W);if(!s.ok&&s.status!==401){const r=await s.json().catch(()=>null);throw new Error(`${s.status} ${r?.error||s.statusText}`)}q.delete(t),y.delete(t),$(t)}));await Promise.all(e)}const Kt=Wt(Yt);function mn(){return v=null,O="",A="",Re(),Ae(null),Kt.run()}async function Jt(n,e,t=_){const i=H(t),{timeoutMs:s=W,signal:r,transport:o="auto",...a}=e||{},c=!Q(n),u=c?A:"",d=()=>!c||!!u&&u===A,h=()=>{if(!d())throw new Error("VuvoDesk workspace changed while the Hub request was running.")},p=Ft({publicConsole:z(),path:n,requiresUiSession:c});if(c&&!v)throw new Error("VuvoDesk account authority is not available for this Hub request.");const w=()=>{const C=String(a.method||"GET").toUpperCase(),b=y.get(i),l=b?.ownerSignature===A?b.csrfToken:"";return{...a,...r?{signal:r}:{},headers:{...a.headers||{},...!["GET","HEAD","OPTIONS"].includes(C)&&l?{"X-LiveDesk-CSRF":l}:{}},credentials:"include",cache:"no-store"}};if(p==="reject-auth-session")throw new Error("Public Console does not create a Hub cookie session.");if(p==="direct"){if(h(),r?.aborted)throw r.reason||new Error("VuvoDesk Hub request cancelled.");return J(n,w(),s)}if(o==="p2p"){if(h(),r?.aborted)throw r.reason||new Error("VuvoDesk Hub request cancelled.");if(!v||Q(n))throw new Error("Mobile Console direct connection requires an authenticated management request.");return J(n,w(),s)}try{c&&await j(i),h();let C=await G(n,w(),i,s,r);if(h(),xt(),c&&C.status===401&&v){const b=await C.clone().json().catch(()=>null);/^hub-ui-session-(required|expired)$/.test(String(b?.error||""))&&(await j(i,!0),h(),C=await G(n,w(),i,s,r),h())}return C}catch(C){if(!d()||r?.aborted||!v||Q(n))throw C;return J(n,w(),s)}}async function Qt(n,e,t=_){const i=await Jt(n,{...e||{},headers:{"Content-Type":"application/json",...e?.headers||{}}},t);let s=null;try{s=await i.json()}catch(r){if(e?.signal?.aborted)throw r;s=null}if(!i.ok){const r=s&&typeof s=="object"&&"error"in s?String(s.error||""):"";throw new Error(`${i.status} ${r||i.statusText}`)}return s}async function bn({baseUrl:n=_,targetRole:e,previousRuntimePid:t=0,allowedStates:i=["running","waiting-auth"],timeoutMs:s=3e4}){const r=Date.now()+s;let o=null;for(;Date.now()<r;){try{const c=await Qt("/api/runtime/status",void 0,n),u=String(c.role||"").toLowerCase(),d=String(c.state||"").toLowerCase(),h=Number(c.runtimePid||0),p=t>1?h>1&&h!==t:h>1;if(u===e&&i.map(w=>w.toLowerCase()).includes(d)&&p)return c}catch(c){o=c}await new Promise(c=>setTimeout(c,300))}const a=o instanceof Error?` ${o.message}`:"";throw new Error(`VuvoDesk runtime did not become ${e} with a new process.${a}`)}function En(n,e=120){if(typeof window>"u")return;const t=new URL(window.location.href);t.port=String(n),t.pathname="/",t.search="",t.hash="";const i=t.toString();let s=0;const r=()=>{fetch(i,{cache:"no-store",mode:"no-cors"}).then(()=>window.location.replace(i)).catch(()=>{if(s+=1,s>=e){window.location.replace(i);return}window.setTimeout(r,250)})};r()}function Cn(n){if(!n)return"-";const e=Date.now()-Date.parse(n);return Number.isFinite(e)?e<1e3?"now":e<6e4?`${Math.round(e/1e3)}s`:e<36e5?`${Math.round(e/6e4)}m`:`${Math.round(e/36e5)}h`:"-"}async function Sn(n){await navigator.clipboard.writeText(n)}const Zt=5,en=15,tn=50,nn=Number.POSITIVE_INFINITY,sn=5,In={free:{name:"Free",price:"$0",limit:Zt,people:1,audience:"Personal, ad-supported",note:"Up to 5 personal devices supported by a standard banner."},ltd:{name:"Plus",price:"$5",ltd:"$79 LTD",limit:en,people:1,audience:"Creators and homelabs",note:"15 devices, no ads, personal use."},pro:{name:"Pro / Business",price:"$15",ltd:"$199 LTD",limit:tn,people:1,audience:"Schools, academies, studios, and businesses",note:"Up to 50 devices for school, academy, and commercial use."},team:{name:"Team",price:"$49",limit:nn,people:sn,audience:"Shared business operations",note:"Commercial use, a shared Team Space, and up to 5 people using their own accounts."}},rn="0.1.682",on={version:rn},vn=String(on.version).trim();function yn({size:n=22}){return M.jsxs("svg",{width:n,height:n,viewBox:"0 0 28 28",fill:"none",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",children:[M.jsx("rect",{x:"2.5",y:"4",width:"14",height:"9",rx:"1.5",stroke:"currentColor",strokeWidth:"1.7",opacity:"0.42"}),M.jsx("path",{d:"M7 16v1.5h5",stroke:"currentColor",strokeWidth:"1.7",strokeLinecap:"round",opacity:"0.42"}),M.jsx("rect",{x:"10.5",y:"2.5",width:"14",height:"9",rx:"1.5",stroke:"currentColor",strokeWidth:"1.7",opacity:"0.62"}),M.jsx("path",{d:"M15 14.5V16h5",stroke:"currentColor",strokeWidth:"1.7",strokeLinecap:"round",opacity:"0.62"}),M.jsx("rect",{x:"6.5",y:"8.5",width:"15",height:"10",rx:"1.7",fill:"currentColor",fillOpacity:"0.12",stroke:"currentColor",strokeWidth:"1.9"}),M.jsx("path",{d:"M12 21v1.5H9.5M16 21v1.5h2.5M9.5 22.5h9",stroke:"currentColor",strokeWidth:"1.9",strokeLinecap:"round"})]})}export{_ as D,Zt as F,vn as L,yn as M,en as P,sn as T,tn as a,mn as b,Sn as c,X as d,dn as e,Cn as f,Vt as g,Qt as h,z as i,hn as j,gn as k,H as l,ln as m,En as n,Jt as o,In as p,wn as q,cn as r,un as s,pn as t,fn as u,bn as w};
@@ -1 +1 @@
1
- import{j as e}from"./pwa-startup-DvbrYEQi.js";import{r as i,W as je,S as ee,F as D,b as ve,M,c as be,L as we,P as Re,A as $}from"./icons-CuXJn-Hz.js";import{h as C,w as Se,n as ye}from"./MonitorStackIcon-DDuMtcFd.js";import"./pwa-bootstrap-ljOQTt2e.js";const x={revision:0,settings:{connection:{showThisComputer:!1},security:{accessMode:"trusted-only"},control:{allowKeyboardMouse:!0,allowRemoteRestart:!1},filesAudio:{allowFileTransfer:!0,allowRemoteAudio:!0,rollingBufferEnabled:!1,recordingQuality:"standard",timelapseIntervalSeconds:10,includeRemoteCursor:!0}}},Z=[{id:"connection",label:"Connection",icon:je},{id:"remote-access",label:"Remote Access",icon:ee},{id:"capture-audio",label:"Capture & Audio",icon:D},{id:"agent",label:"Agent",icon:ve}];function A(t,n){return t?.settings?.[n]||x.settings[n]||{}}function _(t,n){return{...t,settings:{...t.settings,[n.section]:{...A(t,n.section),[n.key]:n.value}}}}function O(t){return{revision:Number(t.revision||0),updatedAt:t.updatedAt,settings:t.settings}}function g({icon:t,title:n,description:d,children:m,recommended:a=!1}){return e.jsxs("div",{className:"product-setting-row",children:[e.jsx("div",{className:"product-setting-control",children:m}),t&&e.jsx("span",{className:"product-setting-icon",children:e.jsx(t,{size:17})}),e.jsxs("div",{className:"product-setting-copy",children:[e.jsx("strong",{children:n}),a&&e.jsx("em",{children:"Recommended"}),e.jsx("small",{children:d})]})]})}function R({checked:t,onChange:n,label:d}){return e.jsxs("label",{className:"product-switch",children:[e.jsx("input",{"aria-label":d,type:"checkbox",checked:t,onChange:m=>n(m.target.checked)}),e.jsx("span",{})]})}function U({value:t,onChange:n,options:d,label:m}){return e.jsx("select",{"aria-label":m,value:String(t),onChange:a=>n(a.target.value),children:d.map(([a,F])=>e.jsx("option",{value:a,children:F},a))})}function Te({hubUrl:t="",mobileLayout:n=!1,runtimeRole:d="hub",runtimeDeviceId:m="",sessionAccessToken:a="",showThisComputerSetting:F=!0,onShowThisComputerChange:J,initialTab:Q="connection",pin:te,agent:se,captureGallery:ne,desktopUpdate:ie}){const[r,q]=i.useState(()=>n&&Q==="connection"?"remote-access":Q),[j,k]=i.useState(null),[oe,v]=i.useState("loading"),[G,T]=i.useState(""),[K,E]=i.useState("idle"),[L,z]=i.useState(""),b=i.useRef(null),u=i.useRef(null),p=i.useRef([]),B=i.useRef(Promise.resolve()),re=i.useRef(0),f=i.useRef(0);i.useEffect(()=>{const s=++f.current;let l=!0;return p.current=[],u.current=null,b.current=null,B.current=Promise.resolve(),v("loading"),C("/api/settings",void 0,t).then(o=>{if(!l||s!==f.current)return;const c=O(o);u.current=c,b.current=c,k(c),v("saved")}).catch(o=>{!l||s!==f.current||(T(o instanceof Error?o.message:String(o)),u.current=x,b.current=x,k(x),v("failed"))}),()=>{l=!1}},[t]);const ce=async(s,l,o)=>{const c={id:++re.current,section:s,key:l,value:o},H=f.current;p.current.push(c);const X=_(b.current||u.current||x,c);b.current=X,k(X),v("saving"),T(""),B.current=B.current.then(async()=>{if(H!==f.current)return;let S=null,y=null;for(let N=0;N<2&&!S;N+=1){const xe=u.current||x;try{const w=await C("/api/settings",{method:"PATCH",body:JSON.stringify({revision:xe.revision,[c.section]:{[c.key]:c.value}})},t);S=O(w)}catch(w){if(y=w,N!==0||!String(w instanceof Error?w.message:w).startsWith("409 "))break;try{const W=await C("/api/settings",void 0,t);if(H!==f.current)return;u.current=O(W)}catch(W){y=W;break}}}if(H!==f.current)return;p.current=p.current.filter(N=>N.id!==c.id),S&&(u.current=S);const fe=u.current||x,Y=p.current.reduce(_,fe);b.current=Y,k(Y),S?(T(""),v(p.current.length>0?"saving":"saved")):(v(p.current.length>0?"saving":"failed"),T(y instanceof Error?y.message:String(y||"Settings could not be saved.")))}),await B.current},h=(s,l)=>o=>{ce(s,l,o)},ae=async()=>{if(d!=="hub"||!m||!a){E("failed"),z("Sign in to this Hub before changing its runtime role.");return}E("changing"),z("");try{const s=await C("/api/runtime/status",void 0,t);await C("/api/runtime/role",{method:"POST",headers:{Authorization:`Bearer ${a}`},body:JSON.stringify({role:"client"})},t),E("accepted"),await Se({baseUrl:t,targetRole:"client",previousRuntimePid:Number(s.runtimePid||0),allowedStates:["running","waiting-auth"]}),ye(5179)}catch(s){E("failed"),z(s instanceof Error?s.message:String(s))}},le=A(j,"security"),V=A(j,"control"),P=A(j,"filesAudio"),I=A(j,"connection"),de=`settings-panel-${r}`,ue=n?Z.filter(s=>s.id!=="connection"):Z;i.useEffect(()=>{n&&r==="connection"&&q("remote-access")},[r,n]),i.useEffect(()=>{j&&J?.(!!I.showThisComputer)},[I.showThisComputer,J,j]);const he=e.jsxs("div",{className:"settings-tab-content product-settings-content",children:[te,ie,F&&e.jsxs("section",{className:"settings-section-card",children:[e.jsxs("div",{className:"settings-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"Hub computer list"}),e.jsx("h3",{children:"This computer"})]}),e.jsx(M,{size:20})]}),e.jsx(g,{icon:M,title:"Show this computer",description:"Show this Hub computer in its own Wall and computer lists. It stays available from other computers and the mobile app when hidden.",children:e.jsx(R,{checked:!!I.showThisComputer,onChange:h("connection","showThisComputer"),label:"Show this computer"})})]}),e.jsxs("section",{className:"settings-section-card runtime-role-settings",children:[e.jsxs("div",{className:"settings-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"Runtime role"}),e.jsx("h3",{children:"Use this computer as a Client"})]}),e.jsx(be,{size:20})]}),e.jsx("p",{className:"settings-help",children:"Stop hosting from this computer and reconnect it to the selected Hub."}),K==="accepted"?e.jsx("p",{className:"settings-help runtime-role-success",children:"VuvoDesk is restarting as Client."}):L?e.jsx("p",{className:"settings-help runtime-role-error",role:"alert",children:L}):null,e.jsx("button",{className:"settings-reset",onClick:()=>{ae()},disabled:K==="changing"||d!=="hub",type:"button",children:K==="changing"?"Switching role...":"Switch this computer to Client"})]})]}),ge=e.jsxs("div",{className:"settings-tab-content product-settings-content",children:[e.jsxs("section",{className:"settings-section-card",children:[e.jsxs("div",{className:"settings-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"Remote access"}),e.jsx("h3",{children:"Who may connect?"})]}),e.jsx(we,{size:20})]}),e.jsx(g,{icon:ee,title:"Access mode",description:"Choose the default permission boundary for remote sessions.",recommended:!0,children:e.jsx(U,{value:String(le.accessMode),onChange:h("security","accessMode"),label:"Access mode",options:[["trusted-only","Trusted devices only"],["block-remote-access","Block remote access"]]})})]}),e.jsxs("section",{className:"settings-section-card",children:[e.jsxs("div",{className:"settings-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"Session permissions"}),e.jsx("h3",{children:"What may remote users do?"})]}),e.jsx(M,{size:20})]}),e.jsx(g,{icon:M,title:"Keyboard and mouse control",description:"Allow Control to send keyboard and pointer input.",recommended:!0,children:e.jsx(R,{checked:!!V.allowKeyboardMouse,onChange:h("control","allowKeyboardMouse"),label:"Keyboard and mouse control"})}),e.jsx(g,{icon:Re,title:"Power actions",description:"Allow confirmed sleep, restart, and shut down commands from the Hub.",children:e.jsx(R,{checked:!!V.allowRemoteRestart,onChange:h("control","allowRemoteRestart"),label:"Power actions"})}),e.jsx(g,{icon:D,title:"File transfer",description:"Allow files to be delivered to connected computers.",children:e.jsx(R,{checked:!!P.allowFileTransfer,onChange:h("filesAudio","allowFileTransfer"),label:"File transfer"})}),e.jsx(g,{icon:$,title:"Remote audio",description:"Allow sound capture from connected computers.",children:e.jsx(R,{checked:!!P.allowRemoteAudio,onChange:h("filesAudio","allowRemoteAudio"),label:"Remote audio"})})]})]}),me=e.jsxs("div",{className:"settings-tab-content product-settings-content",children:[e.jsxs("section",{className:"settings-section-card",children:[e.jsxs("div",{className:"settings-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"Capture"}),e.jsx("h3",{children:"Recording defaults"})]}),e.jsx(D,{size:20})]}),e.jsx(g,{icon:$,title:"Keep a Last 30s buffer",description:"Keep recent video in memory until you explicitly save it.",children:e.jsx(R,{checked:!!P.rollingBufferEnabled,onChange:h("filesAudio","rollingBufferEnabled"),label:"Keep a Last 30s buffer"})}),e.jsx(g,{icon:$,title:"Timelapse interval",description:"Choose how often a timelapse frame is captured.",children:e.jsx(U,{value:Number(P.timelapseIntervalSeconds),onChange:s=>h("filesAudio","timelapseIntervalSeconds")(Number(s)),label:"Timelapse interval",options:[["5","5 seconds"],["10","10 seconds"],["30","30 seconds"],["60","60 seconds"]]})})]}),ne]}),pe=r==="connection"?he:r==="remote-access"?ge:r==="capture-audio"?me:se;return e.jsxs("section",{className:"settings-page",children:[e.jsx("div",{className:"settings-page-tabs",role:"tablist","aria-label":"VuvoDesk settings",children:ue.map(({id:s,label:l,icon:o})=>e.jsxs("button",{className:r===s?"active":"",id:`settings-tab-${s}`,role:"tab","aria-selected":r===s,"aria-controls":`settings-panel-${s}`,tabIndex:r===s?0:-1,onClick:()=>q(s),type:"button",children:[e.jsx(o,{size:16}),l]},s))}),oe==="saving"&&e.jsx("div",{className:"product-save-state",role:"status",children:"Saving settings..."}),G&&e.jsx("div",{className:"agent-settings-alert error",role:"alert",children:G}),e.jsx("section",{className:"settings-page-panel",id:de,role:"tabpanel","aria-labelledby":`settings-tab-${r}`,tabIndex:0,children:pe})]})}export{Te as SettingsPage};
1
+ import{j as e}from"./pwa-startup-DvbrYEQi.js";import{r as i,W as je,S as ee,F as D,b as ve,M,c as be,L as we,P as Re,A as $}from"./icons-CuXJn-Hz.js";import{h as C,w as Se,n as ye}from"./MonitorStackIcon-CAj6nVrZ.js";import"./pwa-bootstrap-ljOQTt2e.js";const x={revision:0,settings:{connection:{showThisComputer:!1},security:{accessMode:"trusted-only"},control:{allowKeyboardMouse:!0,allowRemoteRestart:!1},filesAudio:{allowFileTransfer:!0,allowRemoteAudio:!0,rollingBufferEnabled:!1,recordingQuality:"standard",timelapseIntervalSeconds:10,includeRemoteCursor:!0}}},Z=[{id:"connection",label:"Connection",icon:je},{id:"remote-access",label:"Remote Access",icon:ee},{id:"capture-audio",label:"Capture & Audio",icon:D},{id:"agent",label:"Agent",icon:ve}];function A(t,n){return t?.settings?.[n]||x.settings[n]||{}}function _(t,n){return{...t,settings:{...t.settings,[n.section]:{...A(t,n.section),[n.key]:n.value}}}}function O(t){return{revision:Number(t.revision||0),updatedAt:t.updatedAt,settings:t.settings}}function g({icon:t,title:n,description:d,children:m,recommended:a=!1}){return e.jsxs("div",{className:"product-setting-row",children:[e.jsx("div",{className:"product-setting-control",children:m}),t&&e.jsx("span",{className:"product-setting-icon",children:e.jsx(t,{size:17})}),e.jsxs("div",{className:"product-setting-copy",children:[e.jsx("strong",{children:n}),a&&e.jsx("em",{children:"Recommended"}),e.jsx("small",{children:d})]})]})}function R({checked:t,onChange:n,label:d}){return e.jsxs("label",{className:"product-switch",children:[e.jsx("input",{"aria-label":d,type:"checkbox",checked:t,onChange:m=>n(m.target.checked)}),e.jsx("span",{})]})}function U({value:t,onChange:n,options:d,label:m}){return e.jsx("select",{"aria-label":m,value:String(t),onChange:a=>n(a.target.value),children:d.map(([a,F])=>e.jsx("option",{value:a,children:F},a))})}function Te({hubUrl:t="",mobileLayout:n=!1,runtimeRole:d="hub",runtimeDeviceId:m="",sessionAccessToken:a="",showThisComputerSetting:F=!0,onShowThisComputerChange:J,initialTab:Q="connection",pin:te,agent:se,captureGallery:ne,desktopUpdate:ie}){const[r,q]=i.useState(()=>n&&Q==="connection"?"remote-access":Q),[j,k]=i.useState(null),[oe,v]=i.useState("loading"),[G,T]=i.useState(""),[K,E]=i.useState("idle"),[L,z]=i.useState(""),b=i.useRef(null),u=i.useRef(null),p=i.useRef([]),B=i.useRef(Promise.resolve()),re=i.useRef(0),f=i.useRef(0);i.useEffect(()=>{const s=++f.current;let l=!0;return p.current=[],u.current=null,b.current=null,B.current=Promise.resolve(),v("loading"),C("/api/settings",void 0,t).then(o=>{if(!l||s!==f.current)return;const c=O(o);u.current=c,b.current=c,k(c),v("saved")}).catch(o=>{!l||s!==f.current||(T(o instanceof Error?o.message:String(o)),u.current=x,b.current=x,k(x),v("failed"))}),()=>{l=!1}},[t]);const ce=async(s,l,o)=>{const c={id:++re.current,section:s,key:l,value:o},H=f.current;p.current.push(c);const X=_(b.current||u.current||x,c);b.current=X,k(X),v("saving"),T(""),B.current=B.current.then(async()=>{if(H!==f.current)return;let S=null,y=null;for(let N=0;N<2&&!S;N+=1){const xe=u.current||x;try{const w=await C("/api/settings",{method:"PATCH",body:JSON.stringify({revision:xe.revision,[c.section]:{[c.key]:c.value}})},t);S=O(w)}catch(w){if(y=w,N!==0||!String(w instanceof Error?w.message:w).startsWith("409 "))break;try{const W=await C("/api/settings",void 0,t);if(H!==f.current)return;u.current=O(W)}catch(W){y=W;break}}}if(H!==f.current)return;p.current=p.current.filter(N=>N.id!==c.id),S&&(u.current=S);const fe=u.current||x,Y=p.current.reduce(_,fe);b.current=Y,k(Y),S?(T(""),v(p.current.length>0?"saving":"saved")):(v(p.current.length>0?"saving":"failed"),T(y instanceof Error?y.message:String(y||"Settings could not be saved.")))}),await B.current},h=(s,l)=>o=>{ce(s,l,o)},ae=async()=>{if(d!=="hub"||!m||!a){E("failed"),z("Sign in to this Hub before changing its runtime role.");return}E("changing"),z("");try{const s=await C("/api/runtime/status",void 0,t);await C("/api/runtime/role",{method:"POST",headers:{Authorization:`Bearer ${a}`},body:JSON.stringify({role:"client"})},t),E("accepted"),await Se({baseUrl:t,targetRole:"client",previousRuntimePid:Number(s.runtimePid||0),allowedStates:["running","waiting-auth"]}),ye(5179)}catch(s){E("failed"),z(s instanceof Error?s.message:String(s))}},le=A(j,"security"),V=A(j,"control"),P=A(j,"filesAudio"),I=A(j,"connection"),de=`settings-panel-${r}`,ue=n?Z.filter(s=>s.id!=="connection"):Z;i.useEffect(()=>{n&&r==="connection"&&q("remote-access")},[r,n]),i.useEffect(()=>{j&&J?.(!!I.showThisComputer)},[I.showThisComputer,J,j]);const he=e.jsxs("div",{className:"settings-tab-content product-settings-content",children:[te,ie,F&&e.jsxs("section",{className:"settings-section-card",children:[e.jsxs("div",{className:"settings-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"Hub computer list"}),e.jsx("h3",{children:"This computer"})]}),e.jsx(M,{size:20})]}),e.jsx(g,{icon:M,title:"Show this computer",description:"Show this Hub computer in its own Wall and computer lists. It stays available from other computers and the mobile app when hidden.",children:e.jsx(R,{checked:!!I.showThisComputer,onChange:h("connection","showThisComputer"),label:"Show this computer"})})]}),e.jsxs("section",{className:"settings-section-card runtime-role-settings",children:[e.jsxs("div",{className:"settings-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"Runtime role"}),e.jsx("h3",{children:"Use this computer as a Client"})]}),e.jsx(be,{size:20})]}),e.jsx("p",{className:"settings-help",children:"Stop hosting from this computer and reconnect it to the selected Hub."}),K==="accepted"?e.jsx("p",{className:"settings-help runtime-role-success",children:"VuvoDesk is restarting as Client."}):L?e.jsx("p",{className:"settings-help runtime-role-error",role:"alert",children:L}):null,e.jsx("button",{className:"settings-reset",onClick:()=>{ae()},disabled:K==="changing"||d!=="hub",type:"button",children:K==="changing"?"Switching role...":"Switch this computer to Client"})]})]}),ge=e.jsxs("div",{className:"settings-tab-content product-settings-content",children:[e.jsxs("section",{className:"settings-section-card",children:[e.jsxs("div",{className:"settings-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"Remote access"}),e.jsx("h3",{children:"Who may connect?"})]}),e.jsx(we,{size:20})]}),e.jsx(g,{icon:ee,title:"Access mode",description:"Choose the default permission boundary for remote sessions.",recommended:!0,children:e.jsx(U,{value:String(le.accessMode),onChange:h("security","accessMode"),label:"Access mode",options:[["trusted-only","Trusted devices only"],["block-remote-access","Block remote access"]]})})]}),e.jsxs("section",{className:"settings-section-card",children:[e.jsxs("div",{className:"settings-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"Session permissions"}),e.jsx("h3",{children:"What may remote users do?"})]}),e.jsx(M,{size:20})]}),e.jsx(g,{icon:M,title:"Keyboard and mouse control",description:"Allow Control to send keyboard and pointer input.",recommended:!0,children:e.jsx(R,{checked:!!V.allowKeyboardMouse,onChange:h("control","allowKeyboardMouse"),label:"Keyboard and mouse control"})}),e.jsx(g,{icon:Re,title:"Power actions",description:"Allow confirmed sleep, restart, and shut down commands from the Hub.",children:e.jsx(R,{checked:!!V.allowRemoteRestart,onChange:h("control","allowRemoteRestart"),label:"Power actions"})}),e.jsx(g,{icon:D,title:"File transfer",description:"Allow files to be delivered to connected computers.",children:e.jsx(R,{checked:!!P.allowFileTransfer,onChange:h("filesAudio","allowFileTransfer"),label:"File transfer"})}),e.jsx(g,{icon:$,title:"Remote audio",description:"Allow sound capture from connected computers.",children:e.jsx(R,{checked:!!P.allowRemoteAudio,onChange:h("filesAudio","allowRemoteAudio"),label:"Remote audio"})})]})]}),me=e.jsxs("div",{className:"settings-tab-content product-settings-content",children:[e.jsxs("section",{className:"settings-section-card",children:[e.jsxs("div",{className:"settings-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"Capture"}),e.jsx("h3",{children:"Recording defaults"})]}),e.jsx(D,{size:20})]}),e.jsx(g,{icon:$,title:"Keep a Last 30s buffer",description:"Keep recent video in memory until you explicitly save it.",children:e.jsx(R,{checked:!!P.rollingBufferEnabled,onChange:h("filesAudio","rollingBufferEnabled"),label:"Keep a Last 30s buffer"})}),e.jsx(g,{icon:$,title:"Timelapse interval",description:"Choose how often a timelapse frame is captured.",children:e.jsx(U,{value:Number(P.timelapseIntervalSeconds),onChange:s=>h("filesAudio","timelapseIntervalSeconds")(Number(s)),label:"Timelapse interval",options:[["5","5 seconds"],["10","10 seconds"],["30","30 seconds"],["60","60 seconds"]]})})]}),ne]}),pe=r==="connection"?he:r==="remote-access"?ge:r==="capture-audio"?me:se;return e.jsxs("section",{className:"settings-page",children:[e.jsx("div",{className:"settings-page-tabs",role:"tablist","aria-label":"VuvoDesk settings",children:ue.map(({id:s,label:l,icon:o})=>e.jsxs("button",{className:r===s?"active":"",id:`settings-tab-${s}`,role:"tab","aria-selected":r===s,"aria-controls":`settings-panel-${s}`,tabIndex:r===s?0:-1,onClick:()=>q(s),type:"button",children:[e.jsx(o,{size:16}),l]},s))}),oe==="saving"&&e.jsx("div",{className:"product-save-state",role:"status",children:"Saving settings..."}),G&&e.jsx("div",{className:"agent-settings-alert error",role:"alert",children:G}),e.jsx("section",{className:"settings-page-panel",id:de,role:"tabpanel","aria-labelledby":`settings-tab-${r}`,tabIndex:0,children:pe})]})}export{Te as SettingsPage};