@graphen.ai/aiia-sdk 1.0.17 → 1.0.18

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.
@@ -1,2 +1,2 @@
1
- /** @license AiiaVad Released: 2025-11-4 */
1
+ /** @license AiiaVad Released: 2025-12-8 */
2
2
  !function(){"use strict";var t;!function(t){t[t.unknow=0]="unknow",t[t.allowed=1]="allowed",t[t.rejected=2]="rejected"}(t||(t={}));class e extends AudioWorkletProcessor{inputBuffer;inputBufferLength;processChunkSize;outputSampleRate;originalSampleRate;constructor(t){super(),this.inputBuffer=[],this.inputBufferLength=0;const{outputSampleRate:e,chunkTimeInSeconds:n}=Object.assign({outputSampleRate:16e3,chunkTimeInSeconds:1},t?.processorOptions);this.originalSampleRate=sampleRate,this.outputSampleRate=e,this.processChunkSize=function(t,e,n=1){const s=e*n,u=t/e;return Math.ceil(s*u)}(this.originalSampleRate,this.outputSampleRate,n)}process(t,e,n){const s=t[0][0];if(!s)return!0;const u=new Float32Array(s);if(this.inputBuffer.push(u),this.inputBufferLength+=s.length,this.inputBufferLength>=this.processChunkSize){const t=function(t){const e=t.reduce((t,e)=>t+e.length,0),n=new Float32Array(e);let s=0;for(const e of t)n.set(e,s),s+=e.length;return n}(this.inputBuffer);this.inputBuffer=[],this.inputBufferLength=0;const e=function(t,e,n){if(e===n)return t;const s=n/e,u=Math.ceil(t.length*s),i=new Float32Array(u);for(let e=0;e<u;e++){const n=e/s,u=Math.floor(n),o=n-u,r=t[u],a=t[Math.min(u+1,t.length-1)];i[e]=r+(a-r)*o}return i}(t,this.originalSampleRate,this.outputSampleRate);this.port.postMessage({float32:e,outputSampleRate:this.outputSampleRate},[e.buffer])}return!0}}registerProcessor("aiia-vad",e)}();
package/dist/browser.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("lodash-es"),t=require("moment");require("mathjs");var s,i=require("rxjs"),a=require("@mediapipe/tasks-vision"),n=require("socket.io-client");!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(s||(s={}));const o="undefined"!=typeof window;"undefined"!=typeof process&&null!=process.versions&&process.versions.node;class r{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e.get(t,"id",void 0),i=e.get(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof i?i:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return e.assign({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return e.assign({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e.get(this.config,t,void 0)}}var c;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(c||(c={}));const u={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},d=[];const h=new Proxy(u,{get(s,i,a){if("string"==typeof i)switch(i){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...a)=>{const n=t().format("yyyy-MM-DD[T]HH:mm:ss:sss");e.forEach(d,e=>{e.next(i,n,...a)}),s[i](`[Aiia::${i}_${n}_]`,...a)};default:return}},set:(e,t,s,i)=>!0});var l,b;function p(e,t,s){return Math.max(s,Math.min(e,t))}function m(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=t.getInt16(2*i,!0)/32768;s.push(e)}return new Float32Array(s)}function g(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=p(32768*t.getFloat32(4*i,!0),32767,-32768);s.push(e)}return s}!function(e){e[e.unknow=0]="unknow",e[e.allowed=1]="allowed",e[e.rejected=2]="rejected"}(l||(l={}));class S{audioContext;type="audio";currentNode;bufferQueue;gainNode;set volume(e){e<0&&(e=0),e>100&&(e=100),this.gainNode.gain.value=e/100}get volume(){return Math.round(100*this.gainNode.gain.value)}constructor(e){this.audioContext=e,this.bufferQueue=[],this.currentNode=null,this.gainNode=this.audioContext.createGain(),this.gainNode.connect(this.audioContext.destination)}addBuffer(e){this.bufferQueue.push(e)}addBufferByFloat32(e,t=1){const s=e.length,i=this.audioContext.sampleRate,a=this.audioContext.createBuffer(t,s,i),n=e.slice();for(let e=0;e<t;e++)a.copyToChannel(n,e);this.addBuffer(a)}addBufferByInt16(e,t=1){this.addBufferByFloat32(m(e),t)}startSpeech(){null===this.currentNode&&this.continue()}pauseSpeech(){this.audioContext.suspend().catch(e=>{h.error("pause speech fail",e)})}resumeSpeech(){this.audioContext.resume().catch(e=>{h.error("resume speech fail",e)})}nextSpeech(){this.bufferQueue.length>0&&(this.clearNode(),this.continue())}stopSpeech(){this.bufferQueue=[],this.clearNode()}clearNode(){this.currentNode&&(this.currentNode.onended=function(){},this.currentNode.stop(),this.currentNode.disconnect(),this.currentNode=null)}continue(){const e=this.bufferQueue.shift();if(e){this.currentNode=this.audioContext.createBufferSource(),this.currentNode.buffer=e,this.currentNode.connect(this.gainNode);const t=this.continue.bind(this);this.currentNode.onended=t,this.currentNode.start()}else this.clearNode()}}class f{stream;audioContext;type="vad";workletNode;source;isRecording=!1;outputSampleRate;chunkTimeInSeconds;pcmSub;get pcm(){return this.pcmSub.asObservable()}constructor(e,t,s){this.stream=e,this.audioContext=t,this.outputSampleRate=s?.llmSampleRate??16e3,this.chunkTimeInSeconds=s?.chunkTimeInSeconds??1,this.pcmSub=new i.Subject}async startRecord(){this.isRecording||(this.source=this.audioContext.createMediaStreamSource(this.stream),this.workletNode=new AudioWorkletNode(this.audioContext,"aiia-vad",{processorOptions:{outputSampleRate:this.outputSampleRate,chunkTimeInSeconds:this.chunkTimeInSeconds}}),this.source.connect(this.workletNode),this.workletNode.port.onmessage=e=>{const{float32:t}=e.data;t&&this.pcmSub.next(new Float32Array(t))},this.isRecording=!0)}stopRecord(){this.isRecording&&(this.workletNode&&(this.workletNode.port.onmessage=null,this.workletNode.disconnect()),this.source&&this.source.disconnect(),this.isRecording=!1)}}class _{type="camera";aiiaCamera;get detections(){return this.aiiaCamera.detections}get landmarks(){return this.aiiaCamera.landmarks}get videoSize(){return this.aiiaCamera.videoSize}constructor(t){const{perSecond:s}=e.assign({perSecond:3},t);this.aiiaCamera=new y,this.aiiaCamera.setFrequency(s),document.body.append(this.aiiaCamera)}async init(e){try{const t=await a.FilesetResolver.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.0/wasm"),s=await a.FaceDetector.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite",delegate:"GPU"},runningMode:"VIDEO"});this.aiiaCamera.setStream(e),this.aiiaCamera.setFaceDetector(s);const i=await a.PoseLandmarker.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task",delegate:"GPU"},runningMode:"VIDEO",numPoses:5});this.aiiaCamera.setPoseLandmarker(i)}catch(e){h.error("Camera init fail",e)}}startCapture(){this.aiiaCamera.startCapture()}stopCapture(){this.aiiaCamera.stopCapture()}onDestroy(){this.aiiaCamera.remove()}}class y extends HTMLElement{static get observedAttributes(){return["debug"]}_shadow;_containerEl;_styleEl;_videoEl;_canvasEl;_canvasCtx;_isDebug;_debugElements;_interval;_lastTimestamp;_loopFrameID;_faceDetector;_detectionsSub;_poseLandmarker;_landmarksSub;_isReady;_drawingUtils;get detections(){return this._detectionsSub.asObservable()}get landmarks(){return this._landmarksSub.asObservable()}get videoSize(){return{width:this._videoEl.videoWidth,height:this._videoEl.videoHeight}}constructor(){super(),this._isDebug=!1,this._isReady=!1,this._shadow=this.attachShadow({mode:"open"}),this._containerEl=document.createElement("div"),this._videoEl=document.createElement("video"),this._styleEl=document.createElement("style"),this._canvasEl=document.createElement("canvas"),this._debugElements=[],this._loopFrameID=null,this._detectionsSub=new i.Subject,this._landmarksSub=new i.Subject,this._interval=Math.ceil(1e3/3),this._lastTimestamp=0,this.predictWebcam=this.predictWebcam.bind(this),this._settingContainer(),this._settingStyle(),this._settingVideo(),this._containerEl.appendChild(this._videoEl),this._shadow.appendChild(this._styleEl),this._shadow.appendChild(this._containerEl),this._containerEl.appendChild(this._canvasEl);const e=this._canvasEl.getContext("2d");e?(this._canvasCtx=e,this._drawingUtils=new a.DrawingUtils(this._canvasCtx)):(this._canvasCtx=null,this._drawingUtils=null)}connectedCallback(){this.startCapture()}disconnectedCallback(){this.stopCapture()}attributeChangedCallback(e,t,s){"debug"===e&&(this._isDebug=null!==s&&("true"===s||""===s),this._containerEl.style.visibility=this._isDebug?"visible":"hidden")}setStream(e){this._videoEl.onloadedmetadata=()=>{this._isReady=!0},this._videoEl.srcObject=e}setFaceDetector(e){this._faceDetector=e}setPoseLandmarker(e){this._poseLandmarker=e}setFrequency(e){this._interval=Math.ceil(1e3/e)}_settingStyle(){this._styleEl.textContent="\n .aiia_container {\n visibility: hidden;\n position: fixed;\n top: 0;\n left: 0;\n\n > canvas {\n position: absolute;\n top: 0;\n left: 0;\n width: 640px;\n height: 480px;\n }\n }\n .aiia_container .highlighter {\n background: rgba(0, 255, 0, 0.25);\n border: 1px dashed #fff;\n z-index: 1;\n position: absolute;\n }\n .aiia_container .confidence {\n position: absolute;\n padding-bottom: 5px;\n padding-top: 5px;\n background-color: #007f8b;\n color: #fff;\n border: 1px dashed rgba(255, 255, 255, 0.7);\n z-index: 2;\n font-size: 12px;\n margin: 0;\n }\n "}_settingContainer(){this._containerEl.classList.add("aiia_container")}_settingVideo(){this._videoEl.autoplay=!0,this._videoEl.playsInline=!0,this._videoEl.muted=!0}getVideo(){return this._videoEl}startCapture(){null===this._loopFrameID&&(this._videoEl.play(),this._loopFrameID=window.requestAnimationFrame(this.predictWebcam))}stopCapture(){null!==this._loopFrameID&&(window.cancelAnimationFrame(this._loopFrameID),this._loopFrameID=null),this._videoEl.pause()}async predictWebcam(){if(this._loopFrameID=window.requestAnimationFrame(this.predictWebcam),void 0===this._faceDetector||!this._isReady)return;if(void 0===this._poseLandmarker||!this._isReady)return;const e=performance.now();if(this._lastTimestamp+this._interval<=e){this._lastTimestamp=e;const t=this._faceDetector.detectForVideo(this._videoEl,e).detections,s=this._poseLandmarker.detectForVideo(this._videoEl,e).landmarks;this._isDebug&&(this._drawRect(t),this._drawRect2(s)),this._detectionsSub.next(t),this._landmarksSub.next(s)}}_drawRect(t){e.forEach(t,(e,t)=>{const{categories:s,boundingBox:i}=e;if(i){let e=this._debugElements[2*t],a=this._debugElements[2*t+1];e&&a||(e=document.createElement("div"),e.classList.add("highlighter"),a=document.createElement("p"),a.classList.add("confidence"),this._containerEl.append(a,e),this._debugElements.push(e,a)),e.style.display="block",a.style.display="block";const n=100*s[0].score;a.innerText=`Confidence: ${Math.round(n)} %`,a.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",a.style.top=i.originY-30+"px",a.style.width=i.width-10+"px",e.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",e.style.top=`${i.originY}px`,e.style.width=i.width-10+"px",e.style.height=`${i.height}px`}});for(let e=2*t.length;e<this._debugElements.length;e++)this._debugElements[e].style.display="none"}landmarkColors=["gray","lightgray","gray","gray","lightgray"];connectorColors=["green","blue","red","yellow","purple"];_drawRect2(t){this._canvasCtx&&this._drawingUtils&&(this._canvasCtx.save(),this._canvasCtx.clearRect(0,0,640,480),e.forEach(t,(e,t)=>{this._canvasCtx?.save(),this._drawingUtils?.drawLandmarks(e,{radius:e=>a.DrawingUtils.lerp(e.from.z,-.15,.1,5,1),color:this.landmarkColors[t]}),this._drawingUtils?.drawConnectors(e,a.PoseLandmarker.POSE_CONNECTIONS,{color:this.connectorColors[t],lineWidth:2})}),this._canvasCtx.restore())}destroy(){this._detectionsSub.complete(),this.remove()}}customElements.define("aiia-camera",y),function(e){e[e.padding=0]="padding",e[e.rejected=1]="rejected",e[e.loadfail=2]="loadfail",e[e.allowed=3]="allowed"}(b||(b={}));class v{stream;_state;stateSub;audioCtx;audioManager;vadManager;camManager;destroy;pcmSub;detectionsSub;landmarksSub;get sampleRate(){return this.audioCtx?this.audioCtx.sampleRate:16e3}get pcm(){return this.pcmSub.asObservable()}get detections(){return this.detectionsSub.asObservable()}get landmarks(){return this.landmarksSub.asObservable()}get state(){return this._state}set state(e){e!==this._state&&(this._state=e,this.stateSub.next(e))}get stateObs(){return this.stateSub.asObservable()}constructor(e){this.stream=e,this.stateSub=new i.ReplaySubject(1),this.pcmSub=new i.Subject,this.destroy=new i.Subject,this.detectionsSub=new i.Subject,this.landmarksSub=new i.Subject,this.state=b.padding}async init(e,t){const a=this.stream??await async function(e){try{return await navigator.mediaDevices.getUserMedia(e)}catch(e){return null}}({audio:{echoCancellation:!0,noiseSuppression:!0},video:!0});if(null===a)throw this.state=b.rejected,new Error(s["未獲得媒體裝置權限"]);this.stream=a,this.audioCtx=new AudioContext;try{await this.audioCtx.audioWorklet.addModule(e)}catch(e){throw h.fatal(e),this.state=b.loadfail,new Error(s["Worklet模組載入失敗"])}this.state=b.allowed,this.audioManager=new S(this.audioCtx),this.vadManager=new f(a,this.audioCtx,t),this.vadManager.pcm.pipe(i.takeUntil(this.destroy)).subscribe(e=>{this.pcmSub.next(e)});const n=new _(t?.cameraDetection);n.detections.pipe(i.takeUntil(this.destroy)).subscribe(e=>{const{width:t,height:s}=n.videoSize;this.detectionsSub.next({detections:e,clientWidth:t,clientHeight:s})}),n.landmarks.pipe(i.takeUntil(this.destroy)).subscribe(e=>{this.landmarksSub.next(e)}),n.init(a),this.camManager=n}addAudioQueue(t){const s=e.get(t,"buffer",void 0),i=e.get(t,"float32",void 0),a=e.get(t,"int16",void 0),n=e.get(t,"numberOfChannels",void 0);s?this.audioManager?.addBuffer(s):i?this.audioManager?.addBufferByFloat32(i,n):a&&this.audioManager?.addBufferByInt16(a,n)}playAudio(){this.audioManager?.startSpeech()}stopAudio(){this.audioManager?.stopSpeech()}startRecord(){this.vadManager?.startRecord()}stopRecord(){this.vadManager?.stopRecord()}setVolume(e){this.audioManager&&(this.audioManager.volume=e)}getVolume(){return this.audioManager?this.audioManager.volume:0}startCapture(){this.camManager?.startCapture()}stopCapture(){this.camManager?.stopCapture()}onDestroy(){this.camManager?.onDestroy(),this.audioCtx?.close(),this.destroy.next(),this.destroy.complete()}}class C{socket;messageSub;destorySub;get message(){return this.messageSub.asObservable()}constructor(t){this.destorySub=new i.Subject,this.messageSub=new i.Subject;const{specific:s,id:a}=t.project;this.socket=n.io(t.ws_url,e.assign({autoConnect:!1},s&&void 0!==a?{query:{uuid:a}}:{}))}start(){const e="list",t="cloud",s="channel";i.fromEvent(this.socket,"connect").pipe(i.takeUntil(this.destorySub)).subscribe(()=>{h.debug("Client ID: ",this.socket.id)}),i.fromEvent(this.socket,e).pipe(i.take(1),i.takeUntil(this.destorySub)).subscribe(t=>{this.messageSub.next({event:e,data:t})}),i.fromEvent(this.socket,t).pipe(i.takeUntil(this.destorySub)).subscribe(e=>{this.messageSub.next({event:t,data:e})}),i.fromEvent(this.socket,s).pipe(i.takeUntil(this.destorySub)).subscribe(e=>{this.messageSub.next({event:s,data:e})}),this.socket.connect()}send(e,t){this.socket.emit(e,t)}sendToCloud(e){this.send("cloud",e)}onDestroy(){this.destorySub.next(),this.destorySub.complete(),this.messageSub.complete(),this.socket.disconnect()}}class w{config;media;chat;destroySub;projectsSub;layoutSub;aiiaSubtitleSub;userSubtitleSub;sampleRateOfWhisper;channelState;stateSub;_currentState;isMuteAiia;aiiaSubtitleClearSub;userSubtitleClearSub;_lastProjects;guestSub;_hasGuest;mediaEventReady;isStart;get state(){return this.stateSub.asObservable()}get currentState(){return this._currentState}get layout(){return this.layoutSub.asObservable()}get projects(){return this.projectsSub.asObservable()}get lastProjects(){return e.map(this._lastProjects,e=>({...e}))}get aiiaSubtitle(){return this.aiiaSubtitleSub.asObservable()}get userSubtitle(){return this.userSubtitleSub.asObservable()}get guest(){return this.guestSub.asObservable()}get hasGuest(){return this._hasGuest}set volume(e){this.media.setVolume(e)}get volume(){return this.media.getVolume()}constructor(e,t){this.config=e,this.media=t,this.layoutSub=new i.Subject,this.projectsSub=new i.ReplaySubject(1),this.destroySub=new i.Subject,this.aiiaSubtitleSub=new i.Subject,this.userSubtitleSub=new i.Subject,this.aiiaSubtitleClearSub=new i.Subject,this.userSubtitleClearSub=new i.Subject,this.stateSub=new i.BehaviorSubject("NotStart"),this.sampleRateOfWhisper={input:e.llmSampleRate,output:16e3},this.channelState={proxy:!1,cloud:!1},this.isMuteAiia=!1,this._lastProjects=[],this.guestSub=new i.ReplaySubject(1),this._hasGuest=!1,this.mediaEventReady=!1,this.isStart=!1,this.chat=new C(e),h.code(c["指定依賴已完成"],"chat"),this.close=this.onDestroy,this.behavior=this.behavior.bind(this);const{userDelayTime:s,aiiaDelayTime:a}=e.autoClearSubtitle;isNaN(a)||this.aiiaSubtitleClearSub.pipe(i.debounceTime(a),i.takeUntil(this.destroySub)).subscribe(()=>{this.aiiaSubtitleSub.next("")}),isNaN(s)||this.userSubtitleClearSub.pipe(i.debounceTime(s),i.takeUntil(this.destroySub)).subscribe(()=>{this.userSubtitleSub.next("")}),this.stateSub.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this._currentState=e}),this.projectsSub.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this._lastProjects=e}),this.guestSub.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this._hasGuest=e}),t.stateObs.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{b.allowed===e&&this.channelCheck()})}start(){this.isStart?h.code(c["已啟動SDK"]):(this.isStart=!0,this.chat.message.pipe(i.takeUntil(this.destroySub)).subscribe(({event:t,data:s})=>{switch(t){case"cloud":e.map(s,this.behavior);break;case"list":{const{id:t,name:i,specific:a}=this.config.project;let n=null;if(a&&void 0!==t)n=t;else{const a=e.find(s,e=>e.name===i||e.id===t);a&&(n=a.id)}null!==n?this.chooseProject(n):(this.projectsSub.next(s),this.stateSub.next("WaitingProjectID"),h.code(c["請訂閱專案列表"]));break}case"channel":switch(this.channelState.proxy=!0,s){case"open":this.channelCheck();break;case"close":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("CloseService");break;case"no_permissions":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("NoPermissions");break;case"reconnect":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("ReconnectingCloud")}}}),this.chat.start(),this.stateSub.next("ConnectingCloud"),h.code(c["啟動SDK"]))}close;skip(){this.chat.sendToCloud({request:"skip"})}reset(){this.chat.sendToCloud({request:"reset"})}chooseProject(e){this.chat.send("uuid",e),this.stateSub.next("ConnectingCloud")}mute(){this.isMuteAiia=!0,this.media.stopAudio(),this.media.stopRecord()}unmute(){this.isMuteAiia=!1,this.media.startRecord()}behavior(t){switch(t.signal){case"layout":this.layoutSub.next(t.content);break;case"audio":if("sampleRate"===t.command)this.sampleRateOfWhisper.output=t.content;else switch(t.command){case"pcm":if(!this.isMuteAiia){const e=function(e,t,s){if(t===s)return e;const i=s/t,a=Math.ceil(e.length*i),n=new Float32Array(a);for(let t=0;t<a;t++){const s=t/i,a=Math.floor(s),o=s-a,r=e[a],c=e[Math.min(a+1,e.length-1)];n[t]=r+(c-r)*o}return n}(m(new Int16Array(t.content)),this.sampleRateOfWhisper.output,this.media.sampleRate);this.media.addAudioQueue({float32:e}),this.media.playAudio()}break;case"interrupted":this.media.stopAudio(),this.aiiaSubtitleSub.next("")}break;case"status":"Connected"===e.get(t,"content")&&(this.channelState.cloud=!0,this.channelCheck(),this.layoutSub.next({type:"resetLayout"}));break;case"agent":if(!this.isMuteAiia){const s=e.get(t,"content.function_result.response");void 0!==s&&(this.aiiaSubtitleSub.next(s),this.aiiaSubtitleClearSub.next())}break;case"asr":if(!this.isMuteAiia&&"ASR"===e.get(t,"content.function_type")){const s=e.get(t,"content.function_result.result");void 0!==s&&(this.userSubtitleSub.next(s),this.userSubtitleClearSub.next())}break;case"cv":{const s=e.get(t,"content.status");"user present"===s?this.guestSub.next(!0):"user leave"===s?this.guestSub.next(!1):h.warn("Unexpect value",e.get(t,"content"));break}default:h.debug(t.signal)}}channelCheck(){const{proxy:t,cloud:s}=this.channelState;if(t&&s&&this.media.state===b.allowed){if(this.chat.sendToCloud({request:"audio",command:"sampleRate",content:this.sampleRateOfWhisper.input}),!this.mediaEventReady){this.mediaEventReady=!0,this.media.pcm.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"audio",command:"pcm",content:g(e)})});const t=(a=this.config.detection.confidence,function(t,s){const{width:i,height:n}=s,o=e.filter(t,e=>100*(e.categories[0]?.score??0)>=a),r=e.compact(e.map(o,e=>{if(void 0!==e.boundingBox){const t=e.boundingBox,s=t.width*t.height,a=e.categories[0]?.score??0;return{...t,area:s,score:100*a,clientWidth:i,clientHeight:n}}return null}));return e.sortBy(r,["area","score"])[0]??null});this.media.detections.pipe(i.takeUntil(this.destroySub),i.map(({detections:e,clientHeight:s,clientWidth:i})=>t(e,{width:i,height:s}))).subscribe(e=>{this.chat.send("face",e)}),this.media.landmarks.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"pose_detect",content:e})})}this.media.startRecord(),this.media.startCapture(),this.stateSub.next("InService"),h.code(c["通道、雲端服務、麥克風都已就緒"])}else t&&s&&this.media.state!==b.allowed&&(this.stateSub.next("InServiceNoMedia"),h.code(c["未取得媒體裝置權限,請重新設定或忽略此訊息"]));var a}onDestroy(){this.chat.onDestroy(),this.media.onDestroy(),this.destroySub.next(),this.destroySub.complete(),this.stateSub.next("Destroy"),this.stateSub.complete(),h.code(c["結束SDK"])}}exports.initSdk=function(t){if(o){!function(e){const{info:t,error:s,warn:i,debug:a,fatal:n,code:o}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);o||(u.code=()=>{}),a||(u.debug=()=>{}),t||(u.info=()=>{}),i||(u.warn=()=>{}),s||(u.error=()=>{}),n||(u.fatal=()=>{})}(e.get(t,"debug",!0)),h.code(c["初始化必要依賴"]);const s=new r(e.assign({},t));h.code(c["指定依賴已完成"],"config");const i=new v(s.mediaStream);h.code(c["指定依賴已完成"],"config");const a=new w(s,i);return h.code(c["指定依賴已完成"],"sdk"),""!==s.worklet_url&&i.init(s.worklet_url,s),a}throw new Error(s["環境錯誤(browser)"])};
1
+ "use strict";var e=require("lodash-es"),t=require("moment");require("mathjs");var s,i=require("rxjs"),a=require("socket.io-client"),n=require("@mediapipe/tasks-vision");!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(s||(s={}));const o="undefined"!=typeof window;"undefined"!=typeof process&&null!=process.versions&&process.versions.node;class r{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e.get(t,"id",void 0),i=e.get(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof i?i:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return e.assign({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return e.assign({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e.get(this.config,t,void 0)}}var c;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(c||(c={}));const u={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},d=[];const h=new Proxy(u,{get(s,i,a){if("string"==typeof i)switch(i){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...a)=>{const n=t().format("yyyy-MM-DD[T]HH:mm:ss:sss");e.forEach(d,e=>{e.next(i,n,...a)}),s[i](`[Aiia::${i}_${n}_]`,...a)};default:return}},set:(e,t,s,i)=>!0});class l{socket;messageSub;destorySub;get message(){return this.messageSub.asObservable()}constructor(t){this.destorySub=new i.Subject,this.messageSub=new i.Subject;const{specific:s,id:n}=t.project;this.socket=a.io(t.ws_url,e.assign({autoConnect:!1},s&&void 0!==n?{query:{uuid:n}}:{}))}start(){const e="list",t="cloud",s="channel";i.fromEvent(this.socket,"connect").pipe(i.takeUntil(this.destorySub)).subscribe(()=>{h.debug("Client ID: ",this.socket.id)}),i.fromEvent(this.socket,e).pipe(i.take(1),i.takeUntil(this.destorySub)).subscribe(t=>{this.messageSub.next({event:e,data:t})}),i.fromEvent(this.socket,t).pipe(i.takeUntil(this.destorySub)).subscribe(e=>{this.messageSub.next({event:t,data:e})}),i.fromEvent(this.socket,s).pipe(i.takeUntil(this.destorySub)).subscribe(e=>{this.messageSub.next({event:s,data:e})}),this.socket.connect()}send(e,t){this.socket.emit(e,t)}sendToCloud(e){this.send("cloud",e)}onDestroy(){this.destorySub.next(),this.destorySub.complete(),this.messageSub.complete(),this.socket.disconnect()}}var b,p;function m(e,t,s){return Math.max(s,Math.min(e,t))}function g(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=t.getInt16(2*i,!0)/32768;s.push(e)}return new Float32Array(s)}function S(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=m(32768*t.getFloat32(4*i,!0),32767,-32768);s.push(e)}return s}!function(e){e[e.unknow=0]="unknow",e[e.allowed=1]="allowed",e[e.rejected=2]="rejected"}(b||(b={}));class f{audioContext;type="audio";currentNode;bufferQueue;gainNode;set volume(e){e<0&&(e=0),e>100&&(e=100),this.gainNode.gain.value=e/100}get volume(){return Math.round(100*this.gainNode.gain.value)}constructor(e){this.audioContext=e,this.bufferQueue=[],this.currentNode=null,this.gainNode=this.audioContext.createGain(),this.gainNode.connect(this.audioContext.destination)}addBuffer(e){this.bufferQueue.push(e)}addBufferByFloat32(e,t=1){const s=e.length,i=this.audioContext.sampleRate,a=this.audioContext.createBuffer(t,s,i),n=e.slice();for(let e=0;e<t;e++)a.copyToChannel(n,e);this.addBuffer(a)}addBufferByInt16(e,t=1){this.addBufferByFloat32(g(e),t)}startSpeech(){null===this.currentNode&&this.continue()}pauseSpeech(){this.audioContext.suspend().catch(e=>{h.error("pause speech fail",e)})}resumeSpeech(){this.audioContext.resume().catch(e=>{h.error("resume speech fail",e)})}nextSpeech(){this.bufferQueue.length>0&&(this.clearNode(),this.continue())}stopSpeech(){this.bufferQueue=[],this.clearNode()}clearNode(){this.currentNode&&(this.currentNode.onended=function(){},this.currentNode.stop(),this.currentNode.disconnect(),this.currentNode=null)}continue(){const e=this.bufferQueue.shift();if(e){this.currentNode=this.audioContext.createBufferSource(),this.currentNode.buffer=e,this.currentNode.connect(this.gainNode);const t=this.continue.bind(this);this.currentNode.onended=t,this.currentNode.start()}else this.clearNode()}}class _{stream;audioContext;type="vad";workletNode;source;isRecording=!1;outputSampleRate;chunkTimeInSeconds;pcmSub;get pcm(){return this.pcmSub.asObservable()}constructor(e,t,s){this.stream=e,this.audioContext=t,this.outputSampleRate=s?.llmSampleRate??16e3,this.chunkTimeInSeconds=s?.chunkTimeInSeconds??1,this.pcmSub=new i.Subject}async startRecord(){this.isRecording||(this.source=this.audioContext.createMediaStreamSource(this.stream),this.workletNode=new AudioWorkletNode(this.audioContext,"aiia-vad",{processorOptions:{outputSampleRate:this.outputSampleRate,chunkTimeInSeconds:this.chunkTimeInSeconds}}),this.source.connect(this.workletNode),this.workletNode.port.onmessage=e=>{const{float32:t}=e.data;t&&this.pcmSub.next(new Float32Array(t))},this.isRecording=!0)}stopRecord(){this.isRecording&&(this.workletNode&&(this.workletNode.port.onmessage=null,this.workletNode.disconnect()),this.source&&this.source.disconnect(),this.isRecording=!1)}}class y{type="camera";aiiaCamera;get detections(){return this.aiiaCamera.detections}get landmarks(){return this.aiiaCamera.landmarks}get videoSize(){return this.aiiaCamera.videoSize}constructor(t){const{perSecond:s}=e.assign({perSecond:3},t);this.aiiaCamera=new C,this.aiiaCamera.setFrequency(s),document.body.append(this.aiiaCamera)}async init(e){try{const t=await n.FilesetResolver.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.0/wasm"),[s,i]=await Promise.all([n.FaceDetector.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite",delegate:"GPU"},runningMode:"VIDEO"}),n.PoseLandmarker.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task",delegate:"GPU"},runningMode:"VIDEO",numPoses:5})]);this.aiiaCamera.setStream(e),this.aiiaCamera.setFaceDetector(s),this.aiiaCamera.setPoseLandmarker(i)}catch(e){h.error("Camera init fail",e)}}startCapture(){this.aiiaCamera.startCapture()}stopCapture(){this.aiiaCamera.stopCapture()}onDestroy(){this.aiiaCamera.remove()}}class C extends HTMLElement{static get observedAttributes(){return["debug"]}_shadow;_containerEl;_styleEl;_videoEl;_canvasEl;_canvasCtx;_isDebug;_debugElements;_interval;_lastTimestamp;_loopFrameID;_faceDetector;_detectionsSub;_poseLandmarker;_landmarksSub;_isReady;_drawingUtils;landmarkColors=["gray","lightgray","gray","gray","lightgray"];connectorColors=["green","blue","red","yellow","purple"];get detections(){return this._detectionsSub.asObservable()}get landmarks(){return this._landmarksSub.asObservable()}get videoSize(){return{width:this._videoEl.videoWidth,height:this._videoEl.videoHeight}}constructor(){super(),this.id="AiiaCamera",this._isDebug=!1,this._isReady=!1,this._shadow=this.attachShadow({mode:"open"}),this._containerEl=document.createElement("div"),this._videoEl=document.createElement("video"),this._styleEl=document.createElement("style"),this._canvasEl=document.createElement("canvas"),this._debugElements=[],this._loopFrameID=null,this._detectionsSub=new i.Subject,this._landmarksSub=new i.Subject,this._interval=Math.ceil(1e3/3),this._lastTimestamp=0,this.predictWebcam=this.predictWebcam.bind(this),this._settingContainer(),this._settingStyle(),this._settingVideo(),this._containerEl.appendChild(this._videoEl),this._shadow.appendChild(this._styleEl),this._shadow.appendChild(this._containerEl),this._containerEl.appendChild(this._canvasEl);const e=this._canvasEl.getContext("2d");e?(this._canvasCtx=e,this._drawingUtils=new n.DrawingUtils(this._canvasCtx)):(this._canvasCtx=null,this._drawingUtils=null)}connectedCallback(){this.startCapture()}disconnectedCallback(){this.stopCapture()}attributeChangedCallback(e,t,s){"debug"===e&&(this._isDebug=null!==s&&("true"===s||""===s),this._containerEl.style.visibility=this._isDebug?"visible":"hidden")}setStream(e){this._videoEl.onloadedmetadata=()=>{this._isReady=!0},this._videoEl.srcObject=e}setFaceDetector(e){this._faceDetector=e}setPoseLandmarker(e){this._poseLandmarker=e}setFrequency(e){this._interval=Math.ceil(1e3/e)}_settingStyle(){this._styleEl.textContent="\n .aiia_container {\n visibility: hidden;\n position: fixed;\n top: 0;\n left: 0;\n\n > canvas {\n position: absolute;\n top: 0;\n left: 0;\n width: 640px;\n height: 480px;\n }\n }\n .aiia_container .highlighter {\n background: rgba(0, 255, 0, 0.25);\n border: 1px dashed #fff;\n z-index: 1;\n position: absolute;\n }\n .aiia_container .confidence {\n position: absolute;\n padding-bottom: 5px;\n padding-top: 5px;\n background-color: #007f8b;\n color: #fff;\n border: 1px dashed rgba(255, 255, 255, 0.7);\n z-index: 2;\n font-size: 12px;\n margin: 0;\n }\n "}_settingContainer(){this._containerEl.classList.add("aiia_container")}_settingVideo(){this._videoEl.autoplay=!0,this._videoEl.playsInline=!0,this._videoEl.muted=!0}getVideo(){return this._videoEl}startCapture(){null===this._loopFrameID&&(this._videoEl.play(),this._loopFrameID=window.requestAnimationFrame(this.predictWebcam))}stopCapture(){null!==this._loopFrameID&&(window.cancelAnimationFrame(this._loopFrameID),this._loopFrameID=null),this._videoEl.pause()}async predictWebcam(){if(this._isReady){const e=performance.now();if(this._lastTimestamp+this._interval<=e){this._lastTimestamp=e;const t=this._faceDetector?.detectForVideo(this._videoEl,e).detections,s=this._poseLandmarker?.detectForVideo(this._videoEl,e).landmarks;t&&(this._isDebug&&this._drawRect(t),this._detectionsSub.next(t)),s&&(this._isDebug&&this._drawRect2(s),this._landmarksSub.next(s))}}this._loopFrameID=window.requestAnimationFrame(this.predictWebcam)}_drawRect(t){e.forEach(t,(e,t)=>{const{categories:s,boundingBox:i}=e;if(i){let e=this._debugElements[2*t],a=this._debugElements[2*t+1];e&&a||(e=document.createElement("div"),e.classList.add("highlighter"),a=document.createElement("p"),a.classList.add("confidence"),this._containerEl.append(a,e),this._debugElements.push(e,a)),e.style.display="block",a.style.display="block";const n=100*s[0].score;a.innerText=`Confidence: ${Math.round(n)} %`,a.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",a.style.top=i.originY-30+"px",a.style.width=i.width-10+"px",e.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",e.style.top=`${i.originY}px`,e.style.width=i.width-10+"px",e.style.height=`${i.height}px`}});for(let e=2*t.length;e<this._debugElements.length;e++)this._debugElements[e].style.display="none"}_drawRect2(t){this._canvasCtx&&this._drawingUtils&&(this._canvasCtx.save(),this._canvasCtx.clearRect(0,0,640,480),e.forEach(t,(e,t)=>{this._canvasCtx?.save(),this._drawingUtils?.drawLandmarks(e,{radius:e=>n.DrawingUtils.lerp(e.from.z,-.15,.1,5,1),color:this.landmarkColors[t]}),this._drawingUtils?.drawConnectors(e,n.PoseLandmarker.POSE_CONNECTIONS,{color:this.connectorColors[t],lineWidth:2})}),this._canvasCtx.restore())}destroy(){this._detectionsSub.complete(),this.remove()}}customElements.define("aiia-camera",C),function(e){e[e.padding=0]="padding",e[e.rejected=1]="rejected",e[e.loadfail=2]="loadfail",e[e.allowed=3]="allowed"}(p||(p={}));class v{stream;_state;stateSub;audioCtx;audioManager;vadManager;camManager;destroy;pcmSub;detectionsSub;landmarksSub;get sampleRate(){return this.audioCtx?this.audioCtx.sampleRate:16e3}get pcm(){return this.pcmSub.asObservable()}get detections(){return this.detectionsSub.asObservable()}get landmarks(){return this.landmarksSub.asObservable()}get state(){return this._state}set state(e){e!==this._state&&(this._state=e,this.stateSub.next(e))}get stateObs(){return this.stateSub.asObservable()}constructor(e){this.stream=e,this.stateSub=new i.ReplaySubject(1),this.pcmSub=new i.Subject,this.destroy=new i.Subject,this.detectionsSub=new i.Subject,this.landmarksSub=new i.Subject,this.state=p.padding}async init(e,t){const a=this.stream??await async function(e){try{return await navigator.mediaDevices.getUserMedia(e)}catch(e){return null}}({audio:{echoCancellation:!0,noiseSuppression:!0},video:!0});if(null===a)throw this.state=p.rejected,new Error(s["未獲得媒體裝置權限"]);this.stream=a,this.audioCtx=new AudioContext;try{await this.audioCtx.audioWorklet.addModule(e)}catch(e){throw h.fatal(e),this.state=p.loadfail,new Error(s["Worklet模組載入失敗"])}this.state=p.allowed,this.audioManager=new f(this.audioCtx),this.vadManager=new _(a,this.audioCtx,t),this.vadManager.pcm.pipe(i.takeUntil(this.destroy)).subscribe(e=>{this.pcmSub.next(e)});const n=new y(t?.cameraDetection);n.detections.pipe(i.takeUntil(this.destroy)).subscribe(e=>{const{width:t,height:s}=n.videoSize;this.detectionsSub.next({detections:e,clientWidth:t,clientHeight:s})}),n.landmarks.pipe(i.takeUntil(this.destroy)).subscribe(e=>{this.landmarksSub.next(e)}),n.init(a),this.camManager=n}addAudioQueue(t){const s=e.get(t,"buffer",void 0),i=e.get(t,"float32",void 0),a=e.get(t,"int16",void 0),n=e.get(t,"numberOfChannels",void 0);s?this.audioManager?.addBuffer(s):i?this.audioManager?.addBufferByFloat32(i,n):a&&this.audioManager?.addBufferByInt16(a,n)}playAudio(){this.audioManager?.startSpeech()}stopAudio(){this.audioManager?.stopSpeech()}startRecord(){this.vadManager?.startRecord()}stopRecord(){this.vadManager?.stopRecord()}setVolume(e){this.audioManager&&(this.audioManager.volume=e)}getVolume(){return this.audioManager?this.audioManager.volume:0}startCapture(){this.camManager?.startCapture()}stopCapture(){this.camManager?.stopCapture()}onDestroy(){this.camManager?.onDestroy(),this.audioCtx?.close(),this.destroy.next(),this.destroy.complete()}}class w{config;media;chat;destroySub;projectsSub;layoutSub;aiiaSubtitleSub;userSubtitleSub;sampleRateOfWhisper;channelState;stateSub;_currentState;isMuteAiia;aiiaSubtitleClearSub;userSubtitleClearSub;_lastProjects;guestSub;_hasGuest;mediaEventReady;isStart;get state(){return this.stateSub.asObservable()}get currentState(){return this._currentState}get layout(){return this.layoutSub.asObservable()}get projects(){return this.projectsSub.asObservable()}get lastProjects(){return e.cloneDeep(this._lastProjects)}get aiiaSubtitle(){return this.aiiaSubtitleSub.asObservable()}get userSubtitle(){return this.userSubtitleSub.asObservable()}get guest(){return this.guestSub.asObservable()}get hasGuest(){return this._hasGuest}set volume(e){this.media.setVolume(e)}get volume(){return this.media.getVolume()}constructor(e,t){this.config=e,this.media=t,this.layoutSub=new i.Subject,this.projectsSub=new i.ReplaySubject(1),this.destroySub=new i.Subject,this.aiiaSubtitleSub=new i.Subject,this.userSubtitleSub=new i.Subject,this.aiiaSubtitleClearSub=new i.Subject,this.userSubtitleClearSub=new i.Subject,this.stateSub=new i.BehaviorSubject("NotStart"),this.sampleRateOfWhisper={input:e.llmSampleRate,output:16e3},this.channelState={proxy:!1,cloud:!1},this.isMuteAiia=!1,this._lastProjects=[],this.guestSub=new i.ReplaySubject(1),this._hasGuest=!1,this.mediaEventReady=!1,this.isStart=!1,this.chat=new l(e),h.code(c["指定依賴已完成"],"chat"),this.close=this.onDestroy,this.behavior=this.behavior.bind(this);const{userDelayTime:s,aiiaDelayTime:a}=e.autoClearSubtitle;isNaN(a)||this.aiiaSubtitleClearSub.pipe(i.debounceTime(a),i.takeUntil(this.destroySub)).subscribe(()=>{this.aiiaSubtitleSub.next("")}),isNaN(s)||this.userSubtitleClearSub.pipe(i.debounceTime(s),i.takeUntil(this.destroySub)).subscribe(()=>{this.userSubtitleSub.next("")}),this.stateSub.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this._currentState=e}),this.projectsSub.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this._lastProjects=e}),this.guestSub.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this._hasGuest=e}),t.stateObs.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{p.allowed===e&&this.channelCheck()})}start(){this.isStart?h.code(c["已啟動SDK"]):(this.isStart=!0,this.chat.message.pipe(i.takeUntil(this.destroySub)).subscribe(({event:t,data:s})=>{switch(t){case"cloud":e.map(s,this.behavior);break;case"list":{const{id:t,name:i,specific:a}=this.config.project;let n=null;if(a&&void 0!==t)n=t;else{const a=e.find(s,e=>e.name===i||e.id===t);a&&(n=a.id)}null!==n?this.chooseProject(n):null===s?(this.stateSub.next("CloseService"),h.error("No project ready or lose network")):(this.projectsSub.next(s),this.stateSub.next("WaitingProjectID"),h.code(c["請訂閱專案列表"]));break}case"channel":switch(this.channelState.proxy=!0,s){case"open":this.channelCheck();break;case"close":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("CloseService");break;case"no_permissions":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("NoPermissions");break;case"reconnect":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("ReconnectingCloud");break;case"net_error":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("CloseService"),h.error("Please check network status")}}}),this.chat.start(),this.stateSub.next("ConnectingCloud"),h.code(c["啟動SDK"]))}close;skip(){this.chat.sendToCloud({request:"skip"})}reset(){this.chat.sendToCloud({request:"reset"})}chooseProject(e){this.chat.send("uuid",e),this.stateSub.next("ConnectingCloud")}mute(){this.isMuteAiia=!0,this.media.stopAudio(),this.media.stopRecord()}unmute(){this.isMuteAiia=!1,this.media.startRecord()}behavior(t){switch(t.signal){case"layout":this.layoutSub.next(t.content);break;case"audio":if("sampleRate"===t.command)this.sampleRateOfWhisper.output=t.content;else switch(t.command){case"pcm":if(!this.isMuteAiia){const e=function(e,t,s){if(t===s)return e;const i=s/t,a=Math.ceil(e.length*i),n=new Float32Array(a);for(let t=0;t<a;t++){const s=t/i,a=Math.floor(s),o=s-a,r=e[a],c=e[Math.min(a+1,e.length-1)];n[t]=r+(c-r)*o}return n}(g(new Int16Array(t.content)),this.sampleRateOfWhisper.output,this.media.sampleRate);this.media.addAudioQueue({float32:e}),this.media.playAudio()}break;case"interrupted":this.media.stopAudio(),this.aiiaSubtitleSub.next("")}break;case"status":"Connected"===e.get(t,"content")&&(this.channelState.cloud=!0,this.channelCheck(),this.layoutSub.next({type:"resetLayout"}));break;case"agent":if(!this.isMuteAiia){const s=e.get(t,"content.function_result.response");void 0!==s&&(this.aiiaSubtitleSub.next(s),this.aiiaSubtitleClearSub.next())}break;case"asr":if(!this.isMuteAiia&&"ASR"===e.get(t,"content.function_type")){const s=e.get(t,"content.function_result.result");void 0!==s&&(this.userSubtitleSub.next(s),this.userSubtitleClearSub.next())}break;case"cv":{const s=e.get(t,"content.status");"user present"===s?this.guestSub.next(!0):"user leave"===s?this.guestSub.next(!1):h.warn("Unexpect value",e.get(t,"content"));break}default:h.debug(t.signal)}}channelCheck(){const{proxy:t,cloud:s}=this.channelState;if(t&&s&&this.media.state===p.allowed){if(this.chat.sendToCloud({request:"audio",command:"sampleRate",content:this.sampleRateOfWhisper.input}),!this.mediaEventReady){this.mediaEventReady=!0,this.media.pcm.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"audio",command:"pcm",content:S(e)})});const t=(a=this.config.detection.confidence,function(t,s){const{width:i,height:n}=s,o=e.filter(t,e=>100*(e.categories[0]?.score??0)>=a),r=e.compact(e.map(o,e=>{if(void 0!==e.boundingBox){const t=e.boundingBox,s=t.width*t.height,a=e.categories[0]?.score??0;return{...t,area:s,score:100*a,clientWidth:i,clientHeight:n}}return null}));return e.sortBy(r,["area","score"])[0]??null});this.media.detections.pipe(i.takeUntil(this.destroySub),i.map(({detections:e,clientHeight:s,clientWidth:i})=>t(e,{width:i,height:s}))).subscribe(e=>{this.chat.send("face",e)}),this.media.landmarks.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"pose_detect",content:e})})}this.media.startRecord(),this.media.startCapture(),this.stateSub.next("InService"),h.code(c["通道、雲端服務、麥克風都已就緒"])}else t&&s&&this.media.state!==p.allowed&&(this.stateSub.next("InServiceNoMedia"),h.code(c["未取得媒體裝置權限,請重新設定或忽略此訊息"]));var a}onDestroy(){this.chat.onDestroy(),this.media.onDestroy(),this.destroySub.next(),this.destroySub.complete(),this.stateSub.next("Destroy"),this.stateSub.complete(),h.code(c["結束SDK"])}}exports.initSdk=function(t){if(o){!function(e){const{info:t,error:s,warn:i,debug:a,fatal:n,code:o}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);o||(u.code=()=>{}),a||(u.debug=()=>{}),t||(u.info=()=>{}),i||(u.warn=()=>{}),s||(u.error=()=>{}),n||(u.fatal=()=>{})}(e.get(t,"debug",!0)),h.code(c["初始化必要依賴"]);const s=new r(e.assign({},t));h.code(c["指定依賴已完成"],"config");const i=new v(s.mediaStream);h.code(c["指定依賴已完成"],"config");const a=new w(s,i);return h.code(c["指定依賴已完成"],"sdk"),""!==s.worklet_url&&i.init(s.worklet_url,s),a}throw new Error(s["環境錯誤(browser)"])};
package/dist/browser.d.ts CHANGED
@@ -53,7 +53,7 @@ export declare namespace aiia_msg {
53
53
  OLD_AGENT,
54
54
  QUESTIONNAIRE,
55
55
  LayoutID,
56
- LAYOUT,
56
+ LAYOUT_2 as LAYOUT,
57
57
  AUDIO,
58
58
  CV,
59
59
  MovementToUE,
@@ -164,23 +164,7 @@ declare class AiiaSdk {
164
164
  * [en]
165
165
  * Subscribable, Control layout
166
166
  */
167
- get layout(): Observable< {
168
- type: "mount";
169
- id: aiia_msg.LayoutID;
170
- panel: string;
171
- props?: Record<string, unknown>;
172
- zIndex?: number;
173
- } | {
174
- type: "update";
175
- id: aiia_msg.LayoutID;
176
- props?: Record<string, unknown>;
177
- zIndex?: number;
178
- } | {
179
- type: "unmount";
180
- id: aiia_msg.LayoutID;
181
- } | {
182
- type: "resetLayout";
183
- }>;
167
+ get layout(): Observable<LAYOUT>;
184
168
  /**
185
169
  * [zh]
186
170
  * 若沒有明確指定 Project ID 時,請訂閱此物件
@@ -196,14 +180,7 @@ declare class AiiaSdk {
196
180
  * [en]
197
181
  * You can read the list of projects received recently
198
182
  */
199
- get lastProjects(): {
200
- avatar: {
201
- name: string;
202
- url: string;
203
- };
204
- id: string;
205
- name: string;
206
- }[];
183
+ get lastProjects(): AiiaProjectItemWithAvatar[];
207
184
  /**
208
185
  * [zh]
209
186
  * 可訂閱,Aiia 的回應字幕
@@ -364,7 +341,7 @@ declare type AUDIO = {
364
341
 
365
342
  export declare type BrowserInitOptions = Omit<InitOptions, "webserver" | "port" | "license" | "eyeTrackEnable" | "correction">;
366
343
 
367
- declare type CLOUD_SOCKET_MSG = TTS | MIC | ASR | AGENT | OLD_ASR | OLD_AGENT | RATINGS | QUESTIONNAIRE | LAYOUT | AUDIO | CV | MovementToUE;
344
+ declare type CLOUD_SOCKET_MSG = TTS | MIC | ASR | AGENT | OLD_ASR | OLD_AGENT | RATINGS | QUESTIONNAIRE | LAYOUT_2 | AUDIO | CV | MovementToUE;
368
345
 
369
346
  declare interface CV {
370
347
  signal: "cv";
@@ -547,7 +524,11 @@ export declare function initSdk(options?: BrowserInitOptions): AiiaSdk;
547
524
 
548
525
  declare type LandmarkResult = Landmark[][];
549
526
 
550
- declare interface LAYOUT {
527
+ export declare type LAYOUT = aiia_msg.LAYOUT["content"] | {
528
+ type: "resetLayout";
529
+ };
530
+
531
+ declare interface LAYOUT_2 {
551
532
  signal: "layout";
552
533
  content: {
553
534
  type: "mount";
package/dist/browser.mjs CHANGED
@@ -1 +1 @@
1
- import{get as e,assign as t,forEach as s,filter as i,compact as a,map as n,sortBy as o,find as r}from"lodash-es";import c from"moment";import"mathjs";import{Subject as u,ReplaySubject as d,takeUntil as h,fromEvent as l,take as p,BehaviorSubject as b,debounceTime as m,map as g}from"rxjs";import{DrawingUtils as f,PoseLandmarker as S,FilesetResolver as _,FaceDetector as y}from"@mediapipe/tasks-vision";import{io as C}from"socket.io-client";var w;!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(w||(w={}));const v="undefined"!=typeof window;"undefined"!=typeof process&&null!=process.versions&&process.versions.node;class x{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e(t,"id",void 0),i=e(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof i?i:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return t({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return t({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e(this.config,t,void 0)}}var R;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(R||(R={}));const E={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},k=[];const D=new Proxy(E,{get(e,t,i){if("string"==typeof t)switch(t){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...i)=>{const a=c().format("yyyy-MM-DD[T]HH:mm:ss:sss");s(k,e=>{e.next(t,a,...i)}),e[t](`[Aiia::${t}_${a}_]`,...i)};default:return}},set:(e,t,s,i)=>!0});var O,N;function M(e,t,s){return Math.max(s,Math.min(e,t))}function T(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=t.getInt16(2*i,!0)/32768;s.push(e)}return new Float32Array(s)}function A(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=M(32768*t.getFloat32(4*i,!0),32767,-32768);s.push(e)}return s}!function(e){e[e.unknow=0]="unknow",e[e.allowed=1]="allowed",e[e.rejected=2]="rejected"}(O||(O={}));class j{audioContext;type="audio";currentNode;bufferQueue;gainNode;set volume(e){e<0&&(e=0),e>100&&(e=100),this.gainNode.gain.value=e/100}get volume(){return Math.round(100*this.gainNode.gain.value)}constructor(e){this.audioContext=e,this.bufferQueue=[],this.currentNode=null,this.gainNode=this.audioContext.createGain(),this.gainNode.connect(this.audioContext.destination)}addBuffer(e){this.bufferQueue.push(e)}addBufferByFloat32(e,t=1){const s=e.length,i=this.audioContext.sampleRate,a=this.audioContext.createBuffer(t,s,i),n=e.slice();for(let e=0;e<t;e++)a.copyToChannel(n,e);this.addBuffer(a)}addBufferByInt16(e,t=1){this.addBufferByFloat32(T(e),t)}startSpeech(){null===this.currentNode&&this.continue()}pauseSpeech(){this.audioContext.suspend().catch(e=>{D.error("pause speech fail",e)})}resumeSpeech(){this.audioContext.resume().catch(e=>{D.error("resume speech fail",e)})}nextSpeech(){this.bufferQueue.length>0&&(this.clearNode(),this.continue())}stopSpeech(){this.bufferQueue=[],this.clearNode()}clearNode(){this.currentNode&&(this.currentNode.onended=function(){},this.currentNode.stop(),this.currentNode.disconnect(),this.currentNode=null)}continue(){const e=this.bufferQueue.shift();if(e){this.currentNode=this.audioContext.createBufferSource(),this.currentNode.buffer=e,this.currentNode.connect(this.gainNode);const t=this.continue.bind(this);this.currentNode.onended=t,this.currentNode.start()}else this.clearNode()}}class I{stream;audioContext;type="vad";workletNode;source;isRecording=!1;outputSampleRate;chunkTimeInSeconds;pcmSub;get pcm(){return this.pcmSub.asObservable()}constructor(e,t,s){this.stream=e,this.audioContext=t,this.outputSampleRate=s?.llmSampleRate??16e3,this.chunkTimeInSeconds=s?.chunkTimeInSeconds??1,this.pcmSub=new u}async startRecord(){this.isRecording||(this.source=this.audioContext.createMediaStreamSource(this.stream),this.workletNode=new AudioWorkletNode(this.audioContext,"aiia-vad",{processorOptions:{outputSampleRate:this.outputSampleRate,chunkTimeInSeconds:this.chunkTimeInSeconds}}),this.source.connect(this.workletNode),this.workletNode.port.onmessage=e=>{const{float32:t}=e.data;t&&this.pcmSub.next(new Float32Array(t))},this.isRecording=!0)}stopRecord(){this.isRecording&&(this.workletNode&&(this.workletNode.port.onmessage=null,this.workletNode.disconnect()),this.source&&this.source.disconnect(),this.isRecording=!1)}}class F{type="camera";aiiaCamera;get detections(){return this.aiiaCamera.detections}get landmarks(){return this.aiiaCamera.landmarks}get videoSize(){return this.aiiaCamera.videoSize}constructor(e){const{perSecond:s}=t({perSecond:3},e);this.aiiaCamera=new W,this.aiiaCamera.setFrequency(s),document.body.append(this.aiiaCamera)}async init(e){try{const t=await _.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.0/wasm"),s=await y.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite",delegate:"GPU"},runningMode:"VIDEO"});this.aiiaCamera.setStream(e),this.aiiaCamera.setFaceDetector(s);const i=await S.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task",delegate:"GPU"},runningMode:"VIDEO",numPoses:5});this.aiiaCamera.setPoseLandmarker(i)}catch(e){D.error("Camera init fail",e)}}startCapture(){this.aiiaCamera.startCapture()}stopCapture(){this.aiiaCamera.stopCapture()}onDestroy(){this.aiiaCamera.remove()}}class W extends HTMLElement{static get observedAttributes(){return["debug"]}_shadow;_containerEl;_styleEl;_videoEl;_canvasEl;_canvasCtx;_isDebug;_debugElements;_interval;_lastTimestamp;_loopFrameID;_faceDetector;_detectionsSub;_poseLandmarker;_landmarksSub;_isReady;_drawingUtils;get detections(){return this._detectionsSub.asObservable()}get landmarks(){return this._landmarksSub.asObservable()}get videoSize(){return{width:this._videoEl.videoWidth,height:this._videoEl.videoHeight}}constructor(){super(),this._isDebug=!1,this._isReady=!1,this._shadow=this.attachShadow({mode:"open"}),this._containerEl=document.createElement("div"),this._videoEl=document.createElement("video"),this._styleEl=document.createElement("style"),this._canvasEl=document.createElement("canvas"),this._debugElements=[],this._loopFrameID=null,this._detectionsSub=new u,this._landmarksSub=new u,this._interval=Math.ceil(1e3/3),this._lastTimestamp=0,this.predictWebcam=this.predictWebcam.bind(this),this._settingContainer(),this._settingStyle(),this._settingVideo(),this._containerEl.appendChild(this._videoEl),this._shadow.appendChild(this._styleEl),this._shadow.appendChild(this._containerEl),this._containerEl.appendChild(this._canvasEl);const e=this._canvasEl.getContext("2d");e?(this._canvasCtx=e,this._drawingUtils=new f(this._canvasCtx)):(this._canvasCtx=null,this._drawingUtils=null)}connectedCallback(){this.startCapture()}disconnectedCallback(){this.stopCapture()}attributeChangedCallback(e,t,s){"debug"===e&&(this._isDebug=null!==s&&("true"===s||""===s),this._containerEl.style.visibility=this._isDebug?"visible":"hidden")}setStream(e){this._videoEl.onloadedmetadata=()=>{this._isReady=!0},this._videoEl.srcObject=e}setFaceDetector(e){this._faceDetector=e}setPoseLandmarker(e){this._poseLandmarker=e}setFrequency(e){this._interval=Math.ceil(1e3/e)}_settingStyle(){this._styleEl.textContent="\n .aiia_container {\n visibility: hidden;\n position: fixed;\n top: 0;\n left: 0;\n\n > canvas {\n position: absolute;\n top: 0;\n left: 0;\n width: 640px;\n height: 480px;\n }\n }\n .aiia_container .highlighter {\n background: rgba(0, 255, 0, 0.25);\n border: 1px dashed #fff;\n z-index: 1;\n position: absolute;\n }\n .aiia_container .confidence {\n position: absolute;\n padding-bottom: 5px;\n padding-top: 5px;\n background-color: #007f8b;\n color: #fff;\n border: 1px dashed rgba(255, 255, 255, 0.7);\n z-index: 2;\n font-size: 12px;\n margin: 0;\n }\n "}_settingContainer(){this._containerEl.classList.add("aiia_container")}_settingVideo(){this._videoEl.autoplay=!0,this._videoEl.playsInline=!0,this._videoEl.muted=!0}getVideo(){return this._videoEl}startCapture(){null===this._loopFrameID&&(this._videoEl.play(),this._loopFrameID=window.requestAnimationFrame(this.predictWebcam))}stopCapture(){null!==this._loopFrameID&&(window.cancelAnimationFrame(this._loopFrameID),this._loopFrameID=null),this._videoEl.pause()}async predictWebcam(){if(this._loopFrameID=window.requestAnimationFrame(this.predictWebcam),void 0===this._faceDetector||!this._isReady)return;if(void 0===this._poseLandmarker||!this._isReady)return;const e=performance.now();if(this._lastTimestamp+this._interval<=e){this._lastTimestamp=e;const t=this._faceDetector.detectForVideo(this._videoEl,e).detections,s=this._poseLandmarker.detectForVideo(this._videoEl,e).landmarks;this._isDebug&&(this._drawRect(t),this._drawRect2(s)),this._detectionsSub.next(t),this._landmarksSub.next(s)}}_drawRect(e){s(e,(e,t)=>{const{categories:s,boundingBox:i}=e;if(i){let e=this._debugElements[2*t],a=this._debugElements[2*t+1];e&&a||(e=document.createElement("div"),e.classList.add("highlighter"),a=document.createElement("p"),a.classList.add("confidence"),this._containerEl.append(a,e),this._debugElements.push(e,a)),e.style.display="block",a.style.display="block";const n=100*s[0].score;a.innerText=`Confidence: ${Math.round(n)} %`,a.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",a.style.top=i.originY-30+"px",a.style.width=i.width-10+"px",e.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",e.style.top=`${i.originY}px`,e.style.width=i.width-10+"px",e.style.height=`${i.height}px`}});for(let t=2*e.length;t<this._debugElements.length;t++)this._debugElements[t].style.display="none"}landmarkColors=["gray","lightgray","gray","gray","lightgray"];connectorColors=["green","blue","red","yellow","purple"];_drawRect2(e){this._canvasCtx&&this._drawingUtils&&(this._canvasCtx.save(),this._canvasCtx.clearRect(0,0,640,480),s(e,(e,t)=>{this._canvasCtx?.save(),this._drawingUtils?.drawLandmarks(e,{radius:e=>f.lerp(e.from.z,-.15,.1,5,1),color:this.landmarkColors[t]}),this._drawingUtils?.drawConnectors(e,S.POSE_CONNECTIONS,{color:this.connectorColors[t],lineWidth:2})}),this._canvasCtx.restore())}destroy(){this._detectionsSub.complete(),this.remove()}}customElements.define("aiia-camera",W),function(e){e[e.padding=0]="padding",e[e.rejected=1]="rejected",e[e.loadfail=2]="loadfail",e[e.allowed=3]="allowed"}(N||(N={}));class P{stream;_state;stateSub;audioCtx;audioManager;vadManager;camManager;destroy;pcmSub;detectionsSub;landmarksSub;get sampleRate(){return this.audioCtx?this.audioCtx.sampleRate:16e3}get pcm(){return this.pcmSub.asObservable()}get detections(){return this.detectionsSub.asObservable()}get landmarks(){return this.landmarksSub.asObservable()}get state(){return this._state}set state(e){e!==this._state&&(this._state=e,this.stateSub.next(e))}get stateObs(){return this.stateSub.asObservable()}constructor(e){this.stream=e,this.stateSub=new d(1),this.pcmSub=new u,this.destroy=new u,this.detectionsSub=new u,this.landmarksSub=new u,this.state=N.padding}async init(e,t){const s=this.stream??await async function(e){try{return await navigator.mediaDevices.getUserMedia(e)}catch(e){return null}}({audio:{echoCancellation:!0,noiseSuppression:!0},video:!0});if(null===s)throw this.state=N.rejected,new Error(w["未獲得媒體裝置權限"]);this.stream=s,this.audioCtx=new AudioContext;try{await this.audioCtx.audioWorklet.addModule(e)}catch(e){throw D.fatal(e),this.state=N.loadfail,new Error(w["Worklet模組載入失敗"])}this.state=N.allowed,this.audioManager=new j(this.audioCtx),this.vadManager=new I(s,this.audioCtx,t),this.vadManager.pcm.pipe(h(this.destroy)).subscribe(e=>{this.pcmSub.next(e)});const i=new F(t?.cameraDetection);i.detections.pipe(h(this.destroy)).subscribe(e=>{const{width:t,height:s}=i.videoSize;this.detectionsSub.next({detections:e,clientWidth:t,clientHeight:s})}),i.landmarks.pipe(h(this.destroy)).subscribe(e=>{this.landmarksSub.next(e)}),i.init(s),this.camManager=i}addAudioQueue(t){const s=e(t,"buffer",void 0),i=e(t,"float32",void 0),a=e(t,"int16",void 0),n=e(t,"numberOfChannels",void 0);s?this.audioManager?.addBuffer(s):i?this.audioManager?.addBufferByFloat32(i,n):a&&this.audioManager?.addBufferByInt16(a,n)}playAudio(){this.audioManager?.startSpeech()}stopAudio(){this.audioManager?.stopSpeech()}startRecord(){this.vadManager?.startRecord()}stopRecord(){this.vadManager?.stopRecord()}setVolume(e){this.audioManager&&(this.audioManager.volume=e)}getVolume(){return this.audioManager?this.audioManager.volume:0}startCapture(){this.camManager?.startCapture()}stopCapture(){this.camManager?.stopCapture()}onDestroy(){this.camManager?.onDestroy(),this.audioCtx?.close(),this.destroy.next(),this.destroy.complete()}}class B{socket;messageSub;destorySub;get message(){return this.messageSub.asObservable()}constructor(e){this.destorySub=new u,this.messageSub=new u;const{specific:s,id:i}=e.project;this.socket=C(e.ws_url,t({autoConnect:!1},s&&void 0!==i?{query:{uuid:i}}:{}))}start(){const e="list",t="cloud",s="channel";l(this.socket,"connect").pipe(h(this.destorySub)).subscribe(()=>{D.debug("Client ID: ",this.socket.id)}),l(this.socket,e).pipe(p(1),h(this.destorySub)).subscribe(t=>{this.messageSub.next({event:e,data:t})}),l(this.socket,t).pipe(h(this.destorySub)).subscribe(e=>{this.messageSub.next({event:t,data:e})}),l(this.socket,s).pipe(h(this.destorySub)).subscribe(e=>{this.messageSub.next({event:s,data:e})}),this.socket.connect()}send(e,t){this.socket.emit(e,t)}sendToCloud(e){this.send("cloud",e)}onDestroy(){this.destorySub.next(),this.destorySub.complete(),this.messageSub.complete(),this.socket.disconnect()}}class V{config;media;chat;destroySub;projectsSub;layoutSub;aiiaSubtitleSub;userSubtitleSub;sampleRateOfWhisper;channelState;stateSub;_currentState;isMuteAiia;aiiaSubtitleClearSub;userSubtitleClearSub;_lastProjects;guestSub;_hasGuest;mediaEventReady;isStart;get state(){return this.stateSub.asObservable()}get currentState(){return this._currentState}get layout(){return this.layoutSub.asObservable()}get projects(){return this.projectsSub.asObservable()}get lastProjects(){return n(this._lastProjects,e=>({...e}))}get aiiaSubtitle(){return this.aiiaSubtitleSub.asObservable()}get userSubtitle(){return this.userSubtitleSub.asObservable()}get guest(){return this.guestSub.asObservable()}get hasGuest(){return this._hasGuest}set volume(e){this.media.setVolume(e)}get volume(){return this.media.getVolume()}constructor(e,t){this.config=e,this.media=t,this.layoutSub=new u,this.projectsSub=new d(1),this.destroySub=new u,this.aiiaSubtitleSub=new u,this.userSubtitleSub=new u,this.aiiaSubtitleClearSub=new u,this.userSubtitleClearSub=new u,this.stateSub=new b("NotStart"),this.sampleRateOfWhisper={input:e.llmSampleRate,output:16e3},this.channelState={proxy:!1,cloud:!1},this.isMuteAiia=!1,this._lastProjects=[],this.guestSub=new d(1),this._hasGuest=!1,this.mediaEventReady=!1,this.isStart=!1,this.chat=new B(e),D.code(R["指定依賴已完成"],"chat"),this.close=this.onDestroy,this.behavior=this.behavior.bind(this);const{userDelayTime:s,aiiaDelayTime:i}=e.autoClearSubtitle;isNaN(i)||this.aiiaSubtitleClearSub.pipe(m(i),h(this.destroySub)).subscribe(()=>{this.aiiaSubtitleSub.next("")}),isNaN(s)||this.userSubtitleClearSub.pipe(m(s),h(this.destroySub)).subscribe(()=>{this.userSubtitleSub.next("")}),this.stateSub.pipe(h(this.destroySub)).subscribe(e=>{this._currentState=e}),this.projectsSub.pipe(h(this.destroySub)).subscribe(e=>{this._lastProjects=e}),this.guestSub.pipe(h(this.destroySub)).subscribe(e=>{this._hasGuest=e}),t.stateObs.pipe(h(this.destroySub)).subscribe(e=>{N.allowed===e&&this.channelCheck()})}start(){this.isStart?D.code(R["已啟動SDK"]):(this.isStart=!0,this.chat.message.pipe(h(this.destroySub)).subscribe(({event:e,data:t})=>{switch(e){case"cloud":n(t,this.behavior);break;case"list":{const{id:e,name:s,specific:i}=this.config.project;let a=null;if(i&&void 0!==e)a=e;else{const i=r(t,t=>t.name===s||t.id===e);i&&(a=i.id)}null!==a?this.chooseProject(a):(this.projectsSub.next(t),this.stateSub.next("WaitingProjectID"),D.code(R["請訂閱專案列表"]));break}case"channel":switch(this.channelState.proxy=!0,t){case"open":this.channelCheck();break;case"close":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("CloseService");break;case"no_permissions":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("NoPermissions");break;case"reconnect":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("ReconnectingCloud")}}}),this.chat.start(),this.stateSub.next("ConnectingCloud"),D.code(R["啟動SDK"]))}close;skip(){this.chat.sendToCloud({request:"skip"})}reset(){this.chat.sendToCloud({request:"reset"})}chooseProject(e){this.chat.send("uuid",e),this.stateSub.next("ConnectingCloud")}mute(){this.isMuteAiia=!0,this.media.stopAudio(),this.media.stopRecord()}unmute(){this.isMuteAiia=!1,this.media.startRecord()}behavior(t){switch(t.signal){case"layout":this.layoutSub.next(t.content);break;case"audio":if("sampleRate"===t.command)this.sampleRateOfWhisper.output=t.content;else switch(t.command){case"pcm":if(!this.isMuteAiia){const e=function(e,t,s){if(t===s)return e;const i=s/t,a=Math.ceil(e.length*i),n=new Float32Array(a);for(let t=0;t<a;t++){const s=t/i,a=Math.floor(s),o=s-a,r=e[a],c=e[Math.min(a+1,e.length-1)];n[t]=r+(c-r)*o}return n}(T(new Int16Array(t.content)),this.sampleRateOfWhisper.output,this.media.sampleRate);this.media.addAudioQueue({float32:e}),this.media.playAudio()}break;case"interrupted":this.media.stopAudio(),this.aiiaSubtitleSub.next("")}break;case"status":"Connected"===e(t,"content")&&(this.channelState.cloud=!0,this.channelCheck(),this.layoutSub.next({type:"resetLayout"}));break;case"agent":if(!this.isMuteAiia){const s=e(t,"content.function_result.response");void 0!==s&&(this.aiiaSubtitleSub.next(s),this.aiiaSubtitleClearSub.next())}break;case"asr":if(!this.isMuteAiia&&"ASR"===e(t,"content.function_type")){const s=e(t,"content.function_result.result");void 0!==s&&(this.userSubtitleSub.next(s),this.userSubtitleClearSub.next())}break;case"cv":{const s=e(t,"content.status");"user present"===s?this.guestSub.next(!0):"user leave"===s?this.guestSub.next(!1):D.warn("Unexpect value",e(t,"content"));break}default:D.debug(t.signal)}}channelCheck(){const{proxy:e,cloud:t}=this.channelState;if(e&&t&&this.media.state===N.allowed){if(this.chat.sendToCloud({request:"audio",command:"sampleRate",content:this.sampleRateOfWhisper.input}),!this.mediaEventReady){this.mediaEventReady=!0,this.media.pcm.pipe(h(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"audio",command:"pcm",content:A(e)})});const e=(s=this.config.detection.confidence,function(e,t){const{width:r,height:c}=t,u=i(e,e=>100*(e.categories[0]?.score??0)>=s),d=a(n(u,e=>{if(void 0!==e.boundingBox){const t=e.boundingBox,s=t.width*t.height,i=e.categories[0]?.score??0;return{...t,area:s,score:100*i,clientWidth:r,clientHeight:c}}return null}));return o(d,["area","score"])[0]??null});this.media.detections.pipe(h(this.destroySub),g(({detections:t,clientHeight:s,clientWidth:i})=>e(t,{width:i,height:s}))).subscribe(e=>{this.chat.send("face",e)}),this.media.landmarks.pipe(h(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"pose_detect",content:e})})}this.media.startRecord(),this.media.startCapture(),this.stateSub.next("InService"),D.code(R["通道、雲端服務、麥克風都已就緒"])}else e&&t&&this.media.state!==N.allowed&&(this.stateSub.next("InServiceNoMedia"),D.code(R["未取得媒體裝置權限,請重新設定或忽略此訊息"]));var s}onDestroy(){this.chat.onDestroy(),this.media.onDestroy(),this.destroySub.next(),this.destroySub.complete(),this.stateSub.next("Destroy"),this.stateSub.complete(),D.code(R["結束SDK"])}}function L(s){if(v){!function(e){const{info:t,error:s,warn:i,debug:a,fatal:n,code:o}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);o||(E.code=()=>{}),a||(E.debug=()=>{}),t||(E.info=()=>{}),i||(E.warn=()=>{}),s||(E.error=()=>{}),n||(E.fatal=()=>{})}(e(s,"debug",!0)),D.code(R["初始化必要依賴"]);const i=new x(t({},s));D.code(R["指定依賴已完成"],"config");const a=new P(i.mediaStream);D.code(R["指定依賴已完成"],"config");const n=new V(i,a);return D.code(R["指定依賴已完成"],"sdk"),""!==i.worklet_url&&a.init(i.worklet_url,i),n}throw new Error(w["環境錯誤(browser)"])}export{L as initSdk};
1
+ import{get as e,assign as t,forEach as s,filter as i,compact as a,map as n,sortBy as o,cloneDeep as r,find as c}from"lodash-es";import u from"moment";import"mathjs";import{Subject as d,fromEvent as h,takeUntil as l,take as p,ReplaySubject as b,BehaviorSubject as m,debounceTime as g,map as f}from"rxjs";import{io as S}from"socket.io-client";import{DrawingUtils as _,PoseLandmarker as y,FilesetResolver as C,FaceDetector as w}from"@mediapipe/tasks-vision";var v;!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(v||(v={}));const x="undefined"!=typeof window;"undefined"!=typeof process&&null!=process.versions&&process.versions.node;class k{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e(t,"id",void 0),i=e(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof i?i:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return t({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return t({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e(this.config,t,void 0)}}var R;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(R||(R={}));const E={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},D=[];const O=new Proxy(E,{get(e,t,i){if("string"==typeof t)switch(t){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...i)=>{const a=u().format("yyyy-MM-DD[T]HH:mm:ss:sss");s(D,e=>{e.next(t,a,...i)}),e[t](`[Aiia::${t}_${a}_]`,...i)};default:return}},set:(e,t,s,i)=>!0});class N{socket;messageSub;destorySub;get message(){return this.messageSub.asObservable()}constructor(e){this.destorySub=new d,this.messageSub=new d;const{specific:s,id:i}=e.project;this.socket=S(e.ws_url,t({autoConnect:!1},s&&void 0!==i?{query:{uuid:i}}:{}))}start(){const e="list",t="cloud",s="channel";h(this.socket,"connect").pipe(l(this.destorySub)).subscribe(()=>{O.debug("Client ID: ",this.socket.id)}),h(this.socket,e).pipe(p(1),l(this.destorySub)).subscribe(t=>{this.messageSub.next({event:e,data:t})}),h(this.socket,t).pipe(l(this.destorySub)).subscribe(e=>{this.messageSub.next({event:t,data:e})}),h(this.socket,s).pipe(l(this.destorySub)).subscribe(e=>{this.messageSub.next({event:s,data:e})}),this.socket.connect()}send(e,t){this.socket.emit(e,t)}sendToCloud(e){this.send("cloud",e)}onDestroy(){this.destorySub.next(),this.destorySub.complete(),this.messageSub.complete(),this.socket.disconnect()}}var M,T;function A(e,t,s){return Math.max(s,Math.min(e,t))}function j(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=t.getInt16(2*i,!0)/32768;s.push(e)}return new Float32Array(s)}function I(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=A(32768*t.getFloat32(4*i,!0),32767,-32768);s.push(e)}return s}!function(e){e[e.unknow=0]="unknow",e[e.allowed=1]="allowed",e[e.rejected=2]="rejected"}(M||(M={}));class F{audioContext;type="audio";currentNode;bufferQueue;gainNode;set volume(e){e<0&&(e=0),e>100&&(e=100),this.gainNode.gain.value=e/100}get volume(){return Math.round(100*this.gainNode.gain.value)}constructor(e){this.audioContext=e,this.bufferQueue=[],this.currentNode=null,this.gainNode=this.audioContext.createGain(),this.gainNode.connect(this.audioContext.destination)}addBuffer(e){this.bufferQueue.push(e)}addBufferByFloat32(e,t=1){const s=e.length,i=this.audioContext.sampleRate,a=this.audioContext.createBuffer(t,s,i),n=e.slice();for(let e=0;e<t;e++)a.copyToChannel(n,e);this.addBuffer(a)}addBufferByInt16(e,t=1){this.addBufferByFloat32(j(e),t)}startSpeech(){null===this.currentNode&&this.continue()}pauseSpeech(){this.audioContext.suspend().catch(e=>{O.error("pause speech fail",e)})}resumeSpeech(){this.audioContext.resume().catch(e=>{O.error("resume speech fail",e)})}nextSpeech(){this.bufferQueue.length>0&&(this.clearNode(),this.continue())}stopSpeech(){this.bufferQueue=[],this.clearNode()}clearNode(){this.currentNode&&(this.currentNode.onended=function(){},this.currentNode.stop(),this.currentNode.disconnect(),this.currentNode=null)}continue(){const e=this.bufferQueue.shift();if(e){this.currentNode=this.audioContext.createBufferSource(),this.currentNode.buffer=e,this.currentNode.connect(this.gainNode);const t=this.continue.bind(this);this.currentNode.onended=t,this.currentNode.start()}else this.clearNode()}}class P{stream;audioContext;type="vad";workletNode;source;isRecording=!1;outputSampleRate;chunkTimeInSeconds;pcmSub;get pcm(){return this.pcmSub.asObservable()}constructor(e,t,s){this.stream=e,this.audioContext=t,this.outputSampleRate=s?.llmSampleRate??16e3,this.chunkTimeInSeconds=s?.chunkTimeInSeconds??1,this.pcmSub=new d}async startRecord(){this.isRecording||(this.source=this.audioContext.createMediaStreamSource(this.stream),this.workletNode=new AudioWorkletNode(this.audioContext,"aiia-vad",{processorOptions:{outputSampleRate:this.outputSampleRate,chunkTimeInSeconds:this.chunkTimeInSeconds}}),this.source.connect(this.workletNode),this.workletNode.port.onmessage=e=>{const{float32:t}=e.data;t&&this.pcmSub.next(new Float32Array(t))},this.isRecording=!0)}stopRecord(){this.isRecording&&(this.workletNode&&(this.workletNode.port.onmessage=null,this.workletNode.disconnect()),this.source&&this.source.disconnect(),this.isRecording=!1)}}class W{type="camera";aiiaCamera;get detections(){return this.aiiaCamera.detections}get landmarks(){return this.aiiaCamera.landmarks}get videoSize(){return this.aiiaCamera.videoSize}constructor(e){const{perSecond:s}=t({perSecond:3},e);this.aiiaCamera=new B,this.aiiaCamera.setFrequency(s),document.body.append(this.aiiaCamera)}async init(e){try{const t=await C.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.0/wasm"),[s,i]=await Promise.all([w.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite",delegate:"GPU"},runningMode:"VIDEO"}),y.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task",delegate:"GPU"},runningMode:"VIDEO",numPoses:5})]);this.aiiaCamera.setStream(e),this.aiiaCamera.setFaceDetector(s),this.aiiaCamera.setPoseLandmarker(i)}catch(e){O.error("Camera init fail",e)}}startCapture(){this.aiiaCamera.startCapture()}stopCapture(){this.aiiaCamera.stopCapture()}onDestroy(){this.aiiaCamera.remove()}}class B extends HTMLElement{static get observedAttributes(){return["debug"]}_shadow;_containerEl;_styleEl;_videoEl;_canvasEl;_canvasCtx;_isDebug;_debugElements;_interval;_lastTimestamp;_loopFrameID;_faceDetector;_detectionsSub;_poseLandmarker;_landmarksSub;_isReady;_drawingUtils;landmarkColors=["gray","lightgray","gray","gray","lightgray"];connectorColors=["green","blue","red","yellow","purple"];get detections(){return this._detectionsSub.asObservable()}get landmarks(){return this._landmarksSub.asObservable()}get videoSize(){return{width:this._videoEl.videoWidth,height:this._videoEl.videoHeight}}constructor(){super(),this.id="AiiaCamera",this._isDebug=!1,this._isReady=!1,this._shadow=this.attachShadow({mode:"open"}),this._containerEl=document.createElement("div"),this._videoEl=document.createElement("video"),this._styleEl=document.createElement("style"),this._canvasEl=document.createElement("canvas"),this._debugElements=[],this._loopFrameID=null,this._detectionsSub=new d,this._landmarksSub=new d,this._interval=Math.ceil(1e3/3),this._lastTimestamp=0,this.predictWebcam=this.predictWebcam.bind(this),this._settingContainer(),this._settingStyle(),this._settingVideo(),this._containerEl.appendChild(this._videoEl),this._shadow.appendChild(this._styleEl),this._shadow.appendChild(this._containerEl),this._containerEl.appendChild(this._canvasEl);const e=this._canvasEl.getContext("2d");e?(this._canvasCtx=e,this._drawingUtils=new _(this._canvasCtx)):(this._canvasCtx=null,this._drawingUtils=null)}connectedCallback(){this.startCapture()}disconnectedCallback(){this.stopCapture()}attributeChangedCallback(e,t,s){"debug"===e&&(this._isDebug=null!==s&&("true"===s||""===s),this._containerEl.style.visibility=this._isDebug?"visible":"hidden")}setStream(e){this._videoEl.onloadedmetadata=()=>{this._isReady=!0},this._videoEl.srcObject=e}setFaceDetector(e){this._faceDetector=e}setPoseLandmarker(e){this._poseLandmarker=e}setFrequency(e){this._interval=Math.ceil(1e3/e)}_settingStyle(){this._styleEl.textContent="\n .aiia_container {\n visibility: hidden;\n position: fixed;\n top: 0;\n left: 0;\n\n > canvas {\n position: absolute;\n top: 0;\n left: 0;\n width: 640px;\n height: 480px;\n }\n }\n .aiia_container .highlighter {\n background: rgba(0, 255, 0, 0.25);\n border: 1px dashed #fff;\n z-index: 1;\n position: absolute;\n }\n .aiia_container .confidence {\n position: absolute;\n padding-bottom: 5px;\n padding-top: 5px;\n background-color: #007f8b;\n color: #fff;\n border: 1px dashed rgba(255, 255, 255, 0.7);\n z-index: 2;\n font-size: 12px;\n margin: 0;\n }\n "}_settingContainer(){this._containerEl.classList.add("aiia_container")}_settingVideo(){this._videoEl.autoplay=!0,this._videoEl.playsInline=!0,this._videoEl.muted=!0}getVideo(){return this._videoEl}startCapture(){null===this._loopFrameID&&(this._videoEl.play(),this._loopFrameID=window.requestAnimationFrame(this.predictWebcam))}stopCapture(){null!==this._loopFrameID&&(window.cancelAnimationFrame(this._loopFrameID),this._loopFrameID=null),this._videoEl.pause()}async predictWebcam(){if(this._isReady){const e=performance.now();if(this._lastTimestamp+this._interval<=e){this._lastTimestamp=e;const t=this._faceDetector?.detectForVideo(this._videoEl,e).detections,s=this._poseLandmarker?.detectForVideo(this._videoEl,e).landmarks;t&&(this._isDebug&&this._drawRect(t),this._detectionsSub.next(t)),s&&(this._isDebug&&this._drawRect2(s),this._landmarksSub.next(s))}}this._loopFrameID=window.requestAnimationFrame(this.predictWebcam)}_drawRect(e){s(e,(e,t)=>{const{categories:s,boundingBox:i}=e;if(i){let e=this._debugElements[2*t],a=this._debugElements[2*t+1];e&&a||(e=document.createElement("div"),e.classList.add("highlighter"),a=document.createElement("p"),a.classList.add("confidence"),this._containerEl.append(a,e),this._debugElements.push(e,a)),e.style.display="block",a.style.display="block";const n=100*s[0].score;a.innerText=`Confidence: ${Math.round(n)} %`,a.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",a.style.top=i.originY-30+"px",a.style.width=i.width-10+"px",e.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",e.style.top=`${i.originY}px`,e.style.width=i.width-10+"px",e.style.height=`${i.height}px`}});for(let t=2*e.length;t<this._debugElements.length;t++)this._debugElements[t].style.display="none"}_drawRect2(e){this._canvasCtx&&this._drawingUtils&&(this._canvasCtx.save(),this._canvasCtx.clearRect(0,0,640,480),s(e,(e,t)=>{this._canvasCtx?.save(),this._drawingUtils?.drawLandmarks(e,{radius:e=>_.lerp(e.from.z,-.15,.1,5,1),color:this.landmarkColors[t]}),this._drawingUtils?.drawConnectors(e,y.POSE_CONNECTIONS,{color:this.connectorColors[t],lineWidth:2})}),this._canvasCtx.restore())}destroy(){this._detectionsSub.complete(),this.remove()}}customElements.define("aiia-camera",B),function(e){e[e.padding=0]="padding",e[e.rejected=1]="rejected",e[e.loadfail=2]="loadfail",e[e.allowed=3]="allowed"}(T||(T={}));class V{stream;_state;stateSub;audioCtx;audioManager;vadManager;camManager;destroy;pcmSub;detectionsSub;landmarksSub;get sampleRate(){return this.audioCtx?this.audioCtx.sampleRate:16e3}get pcm(){return this.pcmSub.asObservable()}get detections(){return this.detectionsSub.asObservable()}get landmarks(){return this.landmarksSub.asObservable()}get state(){return this._state}set state(e){e!==this._state&&(this._state=e,this.stateSub.next(e))}get stateObs(){return this.stateSub.asObservable()}constructor(e){this.stream=e,this.stateSub=new b(1),this.pcmSub=new d,this.destroy=new d,this.detectionsSub=new d,this.landmarksSub=new d,this.state=T.padding}async init(e,t){const s=this.stream??await async function(e){try{return await navigator.mediaDevices.getUserMedia(e)}catch(e){return null}}({audio:{echoCancellation:!0,noiseSuppression:!0},video:!0});if(null===s)throw this.state=T.rejected,new Error(v["未獲得媒體裝置權限"]);this.stream=s,this.audioCtx=new AudioContext;try{await this.audioCtx.audioWorklet.addModule(e)}catch(e){throw O.fatal(e),this.state=T.loadfail,new Error(v["Worklet模組載入失敗"])}this.state=T.allowed,this.audioManager=new F(this.audioCtx),this.vadManager=new P(s,this.audioCtx,t),this.vadManager.pcm.pipe(l(this.destroy)).subscribe(e=>{this.pcmSub.next(e)});const i=new W(t?.cameraDetection);i.detections.pipe(l(this.destroy)).subscribe(e=>{const{width:t,height:s}=i.videoSize;this.detectionsSub.next({detections:e,clientWidth:t,clientHeight:s})}),i.landmarks.pipe(l(this.destroy)).subscribe(e=>{this.landmarksSub.next(e)}),i.init(s),this.camManager=i}addAudioQueue(t){const s=e(t,"buffer",void 0),i=e(t,"float32",void 0),a=e(t,"int16",void 0),n=e(t,"numberOfChannels",void 0);s?this.audioManager?.addBuffer(s):i?this.audioManager?.addBufferByFloat32(i,n):a&&this.audioManager?.addBufferByInt16(a,n)}playAudio(){this.audioManager?.startSpeech()}stopAudio(){this.audioManager?.stopSpeech()}startRecord(){this.vadManager?.startRecord()}stopRecord(){this.vadManager?.stopRecord()}setVolume(e){this.audioManager&&(this.audioManager.volume=e)}getVolume(){return this.audioManager?this.audioManager.volume:0}startCapture(){this.camManager?.startCapture()}stopCapture(){this.camManager?.stopCapture()}onDestroy(){this.camManager?.onDestroy(),this.audioCtx?.close(),this.destroy.next(),this.destroy.complete()}}class L{config;media;chat;destroySub;projectsSub;layoutSub;aiiaSubtitleSub;userSubtitleSub;sampleRateOfWhisper;channelState;stateSub;_currentState;isMuteAiia;aiiaSubtitleClearSub;userSubtitleClearSub;_lastProjects;guestSub;_hasGuest;mediaEventReady;isStart;get state(){return this.stateSub.asObservable()}get currentState(){return this._currentState}get layout(){return this.layoutSub.asObservable()}get projects(){return this.projectsSub.asObservable()}get lastProjects(){return r(this._lastProjects)}get aiiaSubtitle(){return this.aiiaSubtitleSub.asObservable()}get userSubtitle(){return this.userSubtitleSub.asObservable()}get guest(){return this.guestSub.asObservable()}get hasGuest(){return this._hasGuest}set volume(e){this.media.setVolume(e)}get volume(){return this.media.getVolume()}constructor(e,t){this.config=e,this.media=t,this.layoutSub=new d,this.projectsSub=new b(1),this.destroySub=new d,this.aiiaSubtitleSub=new d,this.userSubtitleSub=new d,this.aiiaSubtitleClearSub=new d,this.userSubtitleClearSub=new d,this.stateSub=new m("NotStart"),this.sampleRateOfWhisper={input:e.llmSampleRate,output:16e3},this.channelState={proxy:!1,cloud:!1},this.isMuteAiia=!1,this._lastProjects=[],this.guestSub=new b(1),this._hasGuest=!1,this.mediaEventReady=!1,this.isStart=!1,this.chat=new N(e),O.code(R["指定依賴已完成"],"chat"),this.close=this.onDestroy,this.behavior=this.behavior.bind(this);const{userDelayTime:s,aiiaDelayTime:i}=e.autoClearSubtitle;isNaN(i)||this.aiiaSubtitleClearSub.pipe(g(i),l(this.destroySub)).subscribe(()=>{this.aiiaSubtitleSub.next("")}),isNaN(s)||this.userSubtitleClearSub.pipe(g(s),l(this.destroySub)).subscribe(()=>{this.userSubtitleSub.next("")}),this.stateSub.pipe(l(this.destroySub)).subscribe(e=>{this._currentState=e}),this.projectsSub.pipe(l(this.destroySub)).subscribe(e=>{this._lastProjects=e}),this.guestSub.pipe(l(this.destroySub)).subscribe(e=>{this._hasGuest=e}),t.stateObs.pipe(l(this.destroySub)).subscribe(e=>{T.allowed===e&&this.channelCheck()})}start(){this.isStart?O.code(R["已啟動SDK"]):(this.isStart=!0,this.chat.message.pipe(l(this.destroySub)).subscribe(({event:e,data:t})=>{switch(e){case"cloud":n(t,this.behavior);break;case"list":{const{id:e,name:s,specific:i}=this.config.project;let a=null;if(i&&void 0!==e)a=e;else{const i=c(t,t=>t.name===s||t.id===e);i&&(a=i.id)}null!==a?this.chooseProject(a):null===t?(this.stateSub.next("CloseService"),O.error("No project ready or lose network")):(this.projectsSub.next(t),this.stateSub.next("WaitingProjectID"),O.code(R["請訂閱專案列表"]));break}case"channel":switch(this.channelState.proxy=!0,t){case"open":this.channelCheck();break;case"close":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("CloseService");break;case"no_permissions":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("NoPermissions");break;case"reconnect":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("ReconnectingCloud");break;case"net_error":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("CloseService"),O.error("Please check network status")}}}),this.chat.start(),this.stateSub.next("ConnectingCloud"),O.code(R["啟動SDK"]))}close;skip(){this.chat.sendToCloud({request:"skip"})}reset(){this.chat.sendToCloud({request:"reset"})}chooseProject(e){this.chat.send("uuid",e),this.stateSub.next("ConnectingCloud")}mute(){this.isMuteAiia=!0,this.media.stopAudio(),this.media.stopRecord()}unmute(){this.isMuteAiia=!1,this.media.startRecord()}behavior(t){switch(t.signal){case"layout":this.layoutSub.next(t.content);break;case"audio":if("sampleRate"===t.command)this.sampleRateOfWhisper.output=t.content;else switch(t.command){case"pcm":if(!this.isMuteAiia){const e=function(e,t,s){if(t===s)return e;const i=s/t,a=Math.ceil(e.length*i),n=new Float32Array(a);for(let t=0;t<a;t++){const s=t/i,a=Math.floor(s),o=s-a,r=e[a],c=e[Math.min(a+1,e.length-1)];n[t]=r+(c-r)*o}return n}(j(new Int16Array(t.content)),this.sampleRateOfWhisper.output,this.media.sampleRate);this.media.addAudioQueue({float32:e}),this.media.playAudio()}break;case"interrupted":this.media.stopAudio(),this.aiiaSubtitleSub.next("")}break;case"status":"Connected"===e(t,"content")&&(this.channelState.cloud=!0,this.channelCheck(),this.layoutSub.next({type:"resetLayout"}));break;case"agent":if(!this.isMuteAiia){const s=e(t,"content.function_result.response");void 0!==s&&(this.aiiaSubtitleSub.next(s),this.aiiaSubtitleClearSub.next())}break;case"asr":if(!this.isMuteAiia&&"ASR"===e(t,"content.function_type")){const s=e(t,"content.function_result.result");void 0!==s&&(this.userSubtitleSub.next(s),this.userSubtitleClearSub.next())}break;case"cv":{const s=e(t,"content.status");"user present"===s?this.guestSub.next(!0):"user leave"===s?this.guestSub.next(!1):O.warn("Unexpect value",e(t,"content"));break}default:O.debug(t.signal)}}channelCheck(){const{proxy:e,cloud:t}=this.channelState;if(e&&t&&this.media.state===T.allowed){if(this.chat.sendToCloud({request:"audio",command:"sampleRate",content:this.sampleRateOfWhisper.input}),!this.mediaEventReady){this.mediaEventReady=!0,this.media.pcm.pipe(l(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"audio",command:"pcm",content:I(e)})});const e=(s=this.config.detection.confidence,function(e,t){const{width:r,height:c}=t,u=i(e,e=>100*(e.categories[0]?.score??0)>=s),d=a(n(u,e=>{if(void 0!==e.boundingBox){const t=e.boundingBox,s=t.width*t.height,i=e.categories[0]?.score??0;return{...t,area:s,score:100*i,clientWidth:r,clientHeight:c}}return null}));return o(d,["area","score"])[0]??null});this.media.detections.pipe(l(this.destroySub),f(({detections:t,clientHeight:s,clientWidth:i})=>e(t,{width:i,height:s}))).subscribe(e=>{this.chat.send("face",e)}),this.media.landmarks.pipe(l(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"pose_detect",content:e})})}this.media.startRecord(),this.media.startCapture(),this.stateSub.next("InService"),O.code(R["通道、雲端服務、麥克風都已就緒"])}else e&&t&&this.media.state!==T.allowed&&(this.stateSub.next("InServiceNoMedia"),O.code(R["未取得媒體裝置權限,請重新設定或忽略此訊息"]));var s}onDestroy(){this.chat.onDestroy(),this.media.onDestroy(),this.destroySub.next(),this.destroySub.complete(),this.stateSub.next("Destroy"),this.stateSub.complete(),O.code(R["結束SDK"])}}function U(s){if(x){!function(e){const{info:t,error:s,warn:i,debug:a,fatal:n,code:o}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);o||(E.code=()=>{}),a||(E.debug=()=>{}),t||(E.info=()=>{}),i||(E.warn=()=>{}),s||(E.error=()=>{}),n||(E.fatal=()=>{})}(e(s,"debug",!0)),O.code(R["初始化必要依賴"]);const i=new k(t({},s));O.code(R["指定依賴已完成"],"config");const a=new V(i.mediaStream);O.code(R["指定依賴已完成"],"config");const n=new L(i,a);return O.code(R["指定依賴已完成"],"sdk"),""!==i.worklet_url&&a.init(i.worklet_url,i),n}throw new Error(v["環境錯誤(browser)"])}export{U as initSdk};
package/dist/node.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("lodash-es"),t=require("moment"),s=require("mathjs"),r=require("socket.io"),i=require("rxjs"),n=require("axios"),o=require("ws"),a=require("fs"),c=require("os"),h=require("path");require("child_process");var l,u=require("node-osc");!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(l||(l={}));const d="access_token",p="undefined"!=typeof process&&null!=process.versions&&null!=process.versions.node;class S{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e.get(t,"id",void 0),r=e.get(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof r?r:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return e.assign({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return e.assign({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e.get(this.config,t,void 0)}}var f;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(f||(f={}));const _={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},g=[];function b(e){return g.push(e),b}const E=new Proxy(_,{get(s,r,i){if("string"==typeof r)switch(r){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...i)=>{const n=t().format("yyyy-MM-DD[T]HH:mm:ss:sss");e.forEach(g,e=>{e.next(r,n,...i)}),s[r](`[Aiia::${r}_${n}_]`,...i)};default:return}},set:(e,t,s,r)=>!0});class y{config;server;memberSub;get port(){return this.config.port}get webserver(){return this.config.webserver}get memberChange(){return this.memberSub.asObservable()}constructor(e){this.config=e,this.memberSub=new i.Subject}start(){if(this.server=new r.Server(this.webserver,{cors:{origin:"*"}}),this.server.on("connection",e=>{E.debug("client on",e.id),this.memberSub.next({type:"in",socket:e}),e.on("disconnect",t=>{E.debug("client off",e.id,"reason",t),this.memberSub.next({type:"out",socket:e})})}),void 0===this.webserver){const e=this.port;this.server.listen(e),E.info("Websocket server run on",e)}else E.info("Websocket server rely your webserver.")}onDestroy(){this.server&&this.server.close()}}class m{instance;controller;constructor(e){this.controller=new AbortController,this.instance=n.create({baseURL:e,signal:this.controller.signal})}async getAccessToken(t,s){const r={license_number:t,token_value:s??""},i=await this.instance.post("/api/v1/license/agent_token",r).then(t=>e.get(t.data,"token_value","")).catch(()=>null);if(null===i)throw new Error(l["網絡錯誤"]);if(""===i)throw new Error(l["獲取權杖失敗"]);return i}async getProjectList(t){const s=await this.instance.post("/api/v1/license/projects",{license_number:t}).then(t=>e.map(e.get(t,"data.projects",[]),({id:e,sub_project_name:t,avatar:s})=>({avatar:s,id:e,name:t}))).catch(e=>null);if(null===s)throw new Error(l["網絡錯誤"]);if(0===s.length)throw new Error(l["沒有可用專案"]);return s}onDestroy(){this.controller.abort()}}var O;!function(e){e[e.MDN_NORMAL_CLOSURE=1e3]="MDN_NORMAL_CLOSURE",e[e.MDN_GOING_AWAY=1001]="MDN_GOING_AWAY",e[e.MDN_PROTOCOL_ERROR=1002]="MDN_PROTOCOL_ERROR",e[e.MDN_UNSUPPORTED_DATA=1003]="MDN_UNSUPPORTED_DATA",e[e.MDN_RESERVED_1004=1004]="MDN_RESERVED_1004",e[e.MDN_NO_STATUS_RECEIVED=1005]="MDN_NO_STATUS_RECEIVED",e[e.MDN_ABNORMAL_CLOSURE=1006]="MDN_ABNORMAL_CLOSURE",e[e.MDN_INVALID_FRAME_PAYLOAD=1007]="MDN_INVALID_FRAME_PAYLOAD",e[e.MDN_POLICY_VIOLATION=1008]="MDN_POLICY_VIOLATION",e[e.MDN_MESSAGE_TOO_BIG=1009]="MDN_MESSAGE_TOO_BIG",e[e.MDN_MANDATORY_EXTENSION=1010]="MDN_MANDATORY_EXTENSION",e[e.MDN_INTERNAL_ERROR=1011]="MDN_INTERNAL_ERROR",e[e.MDN_SERVICE_RESTART=1012]="MDN_SERVICE_RESTART",e[e.MDN_TRY_AGAIN_LATER=1013]="MDN_TRY_AGAIN_LATER",e[e.MDN_BAD_GATEWAY=1014]="MDN_BAD_GATEWAY",e[e.MDN_TLS_HANDSHAKE=1015]="MDN_TLS_HANDSHAKE",e[e.WHEN_BROWSER_CLOSE=4001]="WHEN_BROWSER_CLOSE",e[e.WS_4100_NORAML_CLOSURE=4100]="WS_4100_NORAML_CLOSURE",e[e.WS_4101_UNAUTHORIZED=4101]="WS_4101_UNAUTHORIZED",e[e.WS_4102_LICENSE_EXPIRED=4102]="WS_4102_LICENSE_EXPIRED",e[e.WS_4103_TOKEN_EXPIRED=4103]="WS_4103_TOKEN_EXPIRED",e[e.WS_4104_LLM_SESSION_ERROR=4104]="WS_4104_LLM_SESSION_ERROR",e[e.WS_4105_TOO_MANY_CONNECTIONS=4105]="WS_4105_TOO_MANY_CONNECTIONS"}(O||(O={}));class D{id;uuid;socket;messageSub;linkStartSub;destorySub;closeSub;isFinished;debugInfo;get message(){return this.messageSub.asObservable()}get close(){return this.closeSub.asObservable()}get linkStart(){return this.linkStartSub.asObservable()}get isConnected(){return this.socket?.readyState===o.OPEN}constructor(e){this.id=e,this.messageSub=new i.Subject,this.linkStartSub=new i.Subject,this.closeSub=new i.Subject,this.destorySub=new i.Subject,this.isFinished=!1,this.uuid=crypto.randomUUID(),this.debugInfo=`browser(${this.id}), proxy(${this.uuid})`,E.info(`create proxy for browser(${this.id})`)}start(e){this.socket=new o(e,{autoPong:!0}),i.fromEvent(this.socket,"open").pipe(i.takeUntil(this.destorySub)).subscribe(()=>{E.code(f["啟動服務"],"proxy",this.debugInfo),this.linkStartSub.next()}),i.fromEvent(this.socket,"message").pipe(i.takeUntil(this.destorySub)).subscribe(e=>{const{data:t}=e;this.messageSub.next(t)}),i.fromEvent(this.socket,"error").pipe(i.takeUntil(this.destorySub)).subscribe(e=>{const{error:t}=e;E.error(this.debugInfo,"Proxy has some problem",t)}),i.fromEvent(this.socket,"close").pipe(i.takeUntil(this.destorySub)).subscribe(e=>{const{code:t,reason:s,type:r,wasClean:i}=e;switch(t){case O.WHEN_BROWSER_CLOSE:E.code(f["無副作用的關閉連線"],this.debugInfo),this.onDestroy();break;case O.MDN_NORMAL_CLOSURE:case O.MDN_GOING_AWAY:this.closeSub.next("close"),E.code(f["安全的關閉連線"],this.debugInfo);break;case O.MDN_NO_STATUS_RECEIVED:case O.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),E.code(f["與雲端的服務中斷"],this.debugInfo,{code:t,reason:s,type:r,wasClean:i});break;case O.WS_4100_NORAML_CLOSURE:case O.WS_4103_TOKEN_EXPIRED:case O.WS_4104_LLM_SESSION_ERROR:case O.WS_4105_TOO_MANY_CONNECTIONS:this.closeSub.next("reconnect"),E.code(f["自動重新連線雲端服務"],this.debugInfo,`socket code: ${t}`);break;case O.WS_4101_UNAUTHORIZED:case O.WS_4102_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),E.code(f["沒有授權,請聯繫Graphen"],this.debugInfo,`socket code: ${t}`);break;default:this.closeSub.next("close"),E.code(f["未預期的斷線,請聯繫Graphen"],this.debugInfo,{code:t,reason:s,type:r,wasClean:i})}})}reconnect(e){this.isFinished||(this.destorySub.next(),this.destorySub.complete(),this.destorySub=new i.Subject,this.start(e))}send(e){const s=`currentTime: ${t().format("yyyy-MM-DD[T]HH:mm:ss:sss")}`;this.isConnected?this.socket.send(e,t=>{t&&E.error("send message fail",this.debugInfo,s,e.slice(0,100),t)}):E.warn("The socket is not connected",this.debugInfo,s,e.slice(0,100))}sendToCloud(e){this.send(JSON.stringify(e))}onClose(){if(this.socket)switch(this.socket.readyState){case 0:E.info("Once the cloud connection is complete, the connection will be disconnected."),this.linkStart.pipe(i.takeUntil(this.destorySub),i.take(1)).subscribe(()=>{this.socket?.close(O.WHEN_BROWSER_CLOSE)});break;case 1:E.info("Close safely, no side effects"),this.socket.close(O.WHEN_BROWSER_CLOSE);break;case 2:case 3:E.info("The connection have been lost.")}}onDestroy(){this.isFinished=!0,this.destorySub.next(),this.destorySub.complete(),E.info("Destroy proxy",this.debugInfo)}}const R=new class{AppName;platform;isMAC;isWIN;isLINUX;saveFolder;homedir;appFolder;EOL;constructor(e){switch(this.AppName=e,this.platform=process.platform,this.homedir=c.homedir(),this.EOL=c.EOL,this.platform){case"darwin":this.isMAC=!0,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=h.join(this.homedir,"Library","Application Support");break;case"win32":this.isMAC=!1,this.isWIN=!0,this.isLINUX=!1,this.saveFolder=process.env.APPDATA||h.join(this.homedir,"AppData","Roaming");break;case"linux":this.isMAC=!1,this.isWIN=!1,this.isLINUX=!0,this.saveFolder=process.env.XDG_CONFIG_HOME||h.join(this.homedir,".config");break;default:console.log(`當前系統是未知的平台: ${this.platform}`),this.isMAC=!1,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=this.homedir}this.appFolder=h.join(this.saveFolder,this.AppName),this.folderCheck(this.appFolder)}addPath(...e){return h.join(...e)}folderCheck(e){a.mkdirSync(e,{recursive:!0})}fileCheck(e){a.existsSync(e)||a.writeFileSync(e,"",{encoding:"utf8"})}}("aiia-sdk");class k{type="node";filePath;fileName;constructor(){this.fileName="storage.json",this.filePath=R.addPath(R.appFolder,this.fileName),R.fileCheck(this.filePath)}getFileDataSync(){const e=a.readFileSync(this.filePath,"utf8");try{return JSON.parse(e)}catch(e){return{}}}setFileDataSync(e){try{const t=JSON.stringify(e);a.writeFileSync(this.filePath,t,"utf8")}catch(e){}}async getFileData(){return new Promise(e=>{a.readFile(this.filePath,"utf8",(t,s)=>{if(t)e({});else try{e(JSON.parse(s))}catch(t){e({})}})})}async setFileData(e){return new Promise(t=>{try{const s=JSON.stringify(e);a.writeFile(this.filePath,s,"utf8",e=>{t()})}catch(e){}})}async getItem(e){return(await this.getFileData())[e]??null}async setItem(e,t){const s=await this.getFileData();s[e]=t,await this.setFileData(s)}async removeItem(t){const s=await this.getFileData(),r=e.omit(s,t);await this.setFileData(r)}async clear(){await this.setFileData({})}getItemSync(e){return this.getFileDataSync()[e]??null}setItemSync(e,t){const s=this.getFileDataSync();s[e]=t,this.setFileDataSync(s)}removeItemSync(t){const s=e.omit(this.getFileDataSync(),t);this.setFileDataSync(s)}clearSync(){this.setFileDataSync({})}}const N={[f["通道、雲端服務、麥克風都已就緒"]]:"通道、雲端服務、麥克風都已就緒",[f["未取得媒體裝置權限,請重新設定或忽略此訊息"]]:"未取得媒體裝置權限,請重新設定或忽略此訊息",[f["請訂閱專案列表"]]:"請訂閱專案列表",[f["初始化必要依賴"]]:"初始化必要依賴",[f["指定依賴已完成"]]:"指定依賴已完成",[f["啟動SDK"]]:"啟動SDK",[f["結束SDK"]]:"結束SDK",[f["已啟動SDK"]]:"已啟動SDK",[f["安全的關閉連線"]]:"安全的關閉連線",[f["無副作用的關閉連線"]]:"無副作用的關閉連線",[f["與雲端的服務中斷"]]:"與雲端的服務中斷",[f["沒有授權,請聯繫Graphen"]]:"沒有授權,請聯繫Graphen",[f["自動重新連線雲端服務"]]:"自動重新連線雲端服務",[f["未預期的斷線,請聯繫Graphen"]]:"未預期的斷線,請聯繫Graphen",[f["開始初始化必要模組"]]:"開始初始化必要模組",[f["指定模組初始化完成"]]:"指定模組初始化完成",[f["啟動服務"]]:"啟動服務",[f["服務結束"]]:"服務結束",[l["網絡錯誤"]]:"網絡錯誤",[l["環境錯誤(browser)"]]:"環境錯誤",[l["Worklet模組載入失敗"]]:"Worklet模組載入失敗",[l["未獲得媒體裝置權限"]]:"未獲得媒體裝置權限",[l["實例已摧毀"]]:"實例已摧毀",[l["環境錯誤(nodejs)"]]:"nodejs",[l["未提供憑證"]]:"未提供憑證",[l["獲取權杖失敗"]]:"獲取權杖失敗",[l["沒有可用專案"]]:"沒有可用專案",[l["重複初始化"]]:"重複初始化"},w=new RegExp(/^(ERROR )?CODE: [0-9]{3,4}$/);class C{next(e,s,...r){const i=`log_${t().format("yyyy-MM-DD")}.txt`,n=R.addPath(R.appFolder,i),o="code"===e?e=>w.test(e)?`${e}(${N[e]})`:e:e=>e;try{const t=r.reduce((e,t)=>{if(t instanceof Error){let s=`Error[${t.message??"No Message"}]${R.EOL}`;return s+=t.stack??"unknow stack","string"==typeof t.cause&&(s+=`${R.EOL}cause: ${t.cause}`),`${e}${R.EOL}${s}${R.EOL}`}switch(typeof t){case"string":return`${e} ${o(t)}`;case"number":case"bigint":return`${e} ${t.toString()}`;case"boolean":return`${e} ${t?"true":"false"}`;case"object":try{return`${e} ${JSON.stringify(t)}`}catch(t){return e}case"symbol":case"undefined":case"function":return`${e} [${typeof t}]`}},"");a.appendFileSync(n,`LOG(${e})__${s}__::${t}${R.EOL}`,{encoding:"utf8"})}catch(e){}}}class M{clientMap;eyeBallUdp;x_axis_px;y_axis_px;constructor(){this.clientMap=new Map,this.x_axis_px=0,this.y_axis_px=0}enableEyeTrack(e){this.eyeBallUdp=new u.Client("127.0.0.1",6745),this.x_axis_px=e.x_axis_px,this.y_axis_px=e.y_axis_px}eyeTrack(t){if(void 0!==this.eyeBallUdp){const r=null!==t,{x:i,y:n}=(()=>{if(r){const{x:r,y:i}=function(t,r){const{maxX:i,maxY:n,minX:o,minY:a}=e.assign({maxX:25,minX:-25,maxY:50,minY:-50},r),c=s.abs(i)+s.abs(o),h=s.abs(n)+s.abs(a),{clientHeight:l,clientWidth:u,originX:d,originY:p,width:S,height:f}=t,_=s.matrix([[c/u,0,o],[0,h/l,a],[0,0,1]]),g=d+S/2,b=l-(p+f/2),E=s.matrix([[g],[b],[1]]),y=s.multiply(_,E);return{x:y.get([0,0]),y:y.get([1,0])}}(t);return{x:this.toFloat(r+this.x_axis_px),y:this.toFloat(i+this.y_axis_px)}}return{x:this.toFloat(7),y:this.toFloat(20)}})();this.eyeBallUdp.send(["/",i,n],e=>{null!=e&&E.error("Eye track error:",e)})}}movement(t){e.map(t,({content:e})=>{const{port:t,host:s,message:r}=e;this.getClient(t,s).send(r,e=>{null!=e&&E.error("Occur error when send message to UE",e)})})}getClient(e,t){if(this.clientMap.has(e))return this.clientMap.get(e);{const s=new u.Client(t,e);return this.clientMap.set(e,s),s}}toFloat(t){return e.isInteger(t)?t+1e-5:t}onDestroy(){e.forEach(Array.from(this.clientMap),([e,t])=>{t.close()}),E.info("Close all UnrealEngine's UDP")}}class A{speechWS;interruptWS;get speechIsConnected(){return this.speechWS?.readyState===o.OPEN}get intrIsConnected(){return this.interruptWS?.readyState===o.OPEN}connectSocket(){this.speechWS=new o("ws://localhost:8080"),this.interruptWS=new o("ws://localhost:8081"),this.speechWS.onerror=e=>{E.error(`speech tool fail message: ${e.message}`,e.error)},this.interruptWS.onerror=e=>{E.error(`interrupt tool fail message: ${e.message}`,e.error)}}interrupted(){this.intrIsConnected&&this.interruptWS.send("interrupted")}speech(e){if(this.speechIsConnected){const t=Buffer.allocUnsafe(2*e.length);for(let s=0;s<e.length;s++)t.writeInt16LE(e[s],2*s);this.speechWS.send(t)}}onDestroy(){this.speechWS?.close(),this.interruptWS?.close(),E.info("Close all UnrealEngine's WebSocket")}}class T{config;udpManager;socketManager;movement;eyeTrack;interrupted;speech;constructor(e){this.config=e,this.udpManager=new M,this.socketManager=new A,this.movement=()=>{},this.eyeTrack=()=>{},this.interrupted=()=>{},this.speech=()=>{},this.checkUnrealEngineStarted()}async checkUnrealEngineStarted(){this.socketManager.connectSocket(),this.movement=this.udpManager.movement.bind(this.udpManager),this.eyeTrack=this.udpManager.eyeTrack.bind(this.udpManager),this.interrupted=this.socketManager.interrupted.bind(this.socketManager),this.speech=this.socketManager.speech.bind(this.socketManager),this.config.eyeTrackEnable&&this.udpManager.enableEyeTrack(this.config.eyeTrackCorrection)}onDestroy(){this.udpManager.onDestroy(),this.socketManager.onDestroy(),E.info("UE tools destroy")}}class I{config;stroage;apiProxy;chat;ueTools;isStart;browserSocketMap;destorySub;access_token;timestamp;socketAddr;get accessToken(){return this.access_token}constructor(e,t){this.config=e,this.stroage=t,this.isStart=!1,this.destorySub=new i.Subject,this.browserSocketMap=new Map,this.timestamp=0,this.access_token=t.getItemSync(d);const{api:s,socket:r}=e.endPoint;this.socketAddr=r,this.apiProxy=new m(s),E.code(f["指定模組初始化完成"],"api"),this.chat=new y(e),E.code(f["指定模組初始化完成"],"chat"),this.ueTools=new T(e),E.code(f["指定模組初始化完成"],"udp tools"),this.openChannel=this.openChannel.bind(this),i.interval(18e4).pipe(i.takeUntil(this.destorySub)).subscribe(()=>{this.inspectClientSocket()})}async start(){if(this.isStart)throw new Error(l["重複初始化"]);this.isStart=!0,await this.replaceAccessToken(),this.chat.memberChange.pipe(i.takeUntil(this.destorySub)).subscribe(({type:t,socket:s})=>{switch(t){case"in":{const t=new D(s.id),n=new i.Subject,o=(r=s.handshake.query.uuid,e.isArray(r)?e.get(r,"[0]",void 0):r??void 0);void 0===o?(this.getProjectList().then(e=>{s.emit("list",e)}),i.fromEvent(s,"uuid").pipe(i.take(1),i.takeUntil(n)).subscribe(e=>{this.openChannel(e,s,t,n)})):this.openChannel(o,s,t,n),this.browserSocketMap.set(s,n);break}case"out":if(this.browserSocketMap.has(s)){const e=this.browserSocketMap.get(s);e.next(),e.complete(),this.browserSocketMap.delete(s)}}var r}),this.chat.start(),E.code(f["啟動服務"],"chat")}async replaceAccessToken(){const e=await this.apiProxy.getAccessToken(this.config.license,this.access_token);return this.access_token=e,this.timestamp=0,this.stroage.setItemSync(d,e),e}async getProjectList(){return await this.apiProxy.getProjectList(this.config.license)}async getProxySocketURL(e){const t=Date.now()-this.timestamp;var s;return t<5e3&&await(s=5100-t,new Promise(e=>{setTimeout(e,s)})),this.timestamp=Date.now(),`${this.socketAddr}/v1/chat/project/${this.accessToken}/${e}`}async openChannel(t,s,r,n){i.fromEvent(s,"cloud").pipe(i.takeUntil(n)).subscribe(e=>{r.sendToCloud(e)}),i.fromEvent(s,"face").pipe(i.takeUntil(n)).subscribe(e=>{const t=e,s=null!==t;this.ueTools.eyeTrack(t),r.sendToCloud({request:"face_detect",content:s})}),r.message.pipe(i.takeUntil(n)).subscribe(t=>{const r=function(t){try{const s=JSON.parse(t);return e.get(s,"signal")?[s]:e.isArray(s)?s:e.values(s)}catch(e){return E.error("message transfer fail",e),[]}}(t),i=[],n=[];e.forEach(r,e=>{switch(e.signal){case"osc":n.push(e);break;case"audio":"pcm"===e.command?this.ueTools.speech(e.content):"interrupted"===e.command&&this.ueTools.interrupted();default:i.push(e)}}),i.length>0&&s.emit("cloud",i),n.length>0&&this.ueTools.movement(n)}),r.close.pipe(i.takeUntil(n)).subscribe(async e=>{switch(s.emit("channel",e),e){case"close":case"no_permissions":r.onDestroy();break;case"reconnect":{await this.replaceAccessToken();const e=await this.getProxySocketURL(t);s.connected?r.reconnect(e):E.error(`The browser(${r.id}) leaves before reconnecting to the cloud`);break}}}),r.linkStart.pipe(i.takeUntil(n)).subscribe(()=>{s.emit("channel","open")}),n.subscribe(()=>{r.onClose()});const o=await this.getProxySocketURL(t);s.connected?r.start(o):E.error(`The browser(${r.id}) leaves before connecting to the cloud`)}inspectClientSocket(){const t=Array.from(this.browserSocketMap);e.forEach(t,([e,t])=>{e.disconnected&&(t.next(),t.complete(),this.browserSocketMap.delete(e),E.info(`The browser(${e.id}) should be closed. Auto clean service.`))})}onDestroy(){this.apiProxy.onDestroy(),this.chat.onDestroy(),this.ueTools.onDestroy(),this.destorySub.next(),this.destorySub.complete(),E.code(f["服務結束"])}}exports.aiiaCore=function(t){if(p){if("string"!=typeof e.get(t,"license",void 0))throw new Error(l["未提供憑證"]);!function(e){const{info:t,error:s,warn:r,debug:i,fatal:n,code:o}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);return o||(_.code=()=>{}),i||(_.debug=()=>{}),t||(_.info=()=>{}),r||(_.warn=()=>{}),s||(_.error=()=>{}),n||(_.fatal=()=>{}),b}(e.get(t,"debug",!0))(new C),E.code(f["開始初始化必要模組"]);const s=new S(e.assign({},t));E.code(f["指定模組初始化完成"],"config");const r=new k;E.code(f["指定模組初始化完成"],"storage");const i=new I(s,r);return E.code(f["指定模組初始化完成"],"core"),i.start(),E.code(f["啟動服務"],"main core"),()=>{i.onDestroy()}}throw new Error(l["環境錯誤(nodejs)"])};
1
+ "use strict";var e=require("lodash-es"),t=require("moment"),s=require("mathjs"),r=require("socket.io"),o=require("rxjs"),n=require("axios"),i=require("ws"),a=require("fs"),c=require("os"),h=require("path"),l=require("node-osc"),u=require("child_process"),d=require("systeminformation"),p=require("@nut-tree-fork/nut-js"),f=require("readline"),g=require("net");function y(e){var t=Object.create(null);return e&&Object.keys(e).forEach(function(s){if("default"!==s){var r=Object.getOwnPropertyDescriptor(e,s);Object.defineProperty(t,s,r.get?r:{enumerable:!0,get:function(){return e[s]}})}}),t.default=e,Object.freeze(t)}var m,b=y(f);!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(m||(m={}));const S="access_token",_="undefined"!=typeof process&&null!=process.versions&&null!=process.versions.node;function w(e){return new Promise(t=>{setTimeout(t,e)})}class E{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e.get(t,"id",void 0),r=e.get(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof r?r:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return e.assign({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return e.assign({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e.get(this.config,t,void 0)}}var k;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(k||(k={}));const D={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},O=[];function N(e){return O.push(e),N}const R=new Proxy(D,{get(s,r,o){if("string"==typeof r)switch(r){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...o)=>{const n=t().format("yyyy-MM-DD[T]HH:mm:ss:sss");e.forEach(O,e=>{e.next(r,n,...o)}),s[r](`[Aiia::${r}_${n}_]`,...o)};default:return}},set:(e,t,s,r)=>!0});class C{config;server;memberSub;get port(){return this.config.port}get webserver(){return this.config.webserver}get memberChange(){return this.memberSub.asObservable()}constructor(e){this.config=e,this.memberSub=new o.Subject}start(){if(this.server=new r.Server(this.webserver,{cors:{origin:"*"}}),this.server.on("connection",e=>{R.debug("client on",e.id),this.memberSub.next({type:"in",socket:e}),e.on("disconnect",t=>{R.debug("client off",e.id,"reason",t),this.memberSub.next({type:"out",socket:e})})}),void 0===this.webserver){const e=this.port;this.server.listen(e),R.info("Websocket server run on",e)}else R.info("Websocket server rely your webserver.")}onDestroy(){this.server&&this.server.close()}}class A{instance;controller;constructor(e){this.controller=new AbortController,this.instance=n.create({baseURL:e,signal:this.controller.signal})}async getAccessToken(t,s){const r=performance.now(),o={license_number:t,token_value:s??""},n=await this.instance.post("/api/v1/license/agent_token",o).then(t=>e.get(t.data,"token_value","")).catch(()=>null);if(R.debug("Complete certification",`Took ${performance.now()-r} milliseconds.`),null===n)throw new Error(m["網絡錯誤"]);if(""===n)throw new Error(m["獲取權杖失敗"]);return n}async getProjectList(t){const s=performance.now(),r=await this.instance.post("/api/v1/license/projects",{license_number:t}).then(t=>e.map(e.get(t,"data.projects",[]),({id:e,sub_project_name:t,avatar:s})=>{const{name:r,url:o}=s;return{avatar:{name:r,url:o},id:e,name:t}})).catch(e=>null);if(R.debug("Got Project List",`Took ${performance.now()-s} milliseconds.`),null===r)throw new Error(m["網絡錯誤"]);if(0===r.length)throw new Error(m["沒有可用專案"]);return r}onDestroy(){this.controller.abort()}}var M;!function(e){e[e.MDN_NORMAL_CLOSURE=1e3]="MDN_NORMAL_CLOSURE",e[e.MDN_GOING_AWAY=1001]="MDN_GOING_AWAY",e[e.MDN_PROTOCOL_ERROR=1002]="MDN_PROTOCOL_ERROR",e[e.MDN_UNSUPPORTED_DATA=1003]="MDN_UNSUPPORTED_DATA",e[e.MDN_RESERVED_1004=1004]="MDN_RESERVED_1004",e[e.MDN_NO_STATUS_RECEIVED=1005]="MDN_NO_STATUS_RECEIVED",e[e.MDN_ABNORMAL_CLOSURE=1006]="MDN_ABNORMAL_CLOSURE",e[e.MDN_INVALID_FRAME_PAYLOAD=1007]="MDN_INVALID_FRAME_PAYLOAD",e[e.MDN_POLICY_VIOLATION=1008]="MDN_POLICY_VIOLATION",e[e.MDN_MESSAGE_TOO_BIG=1009]="MDN_MESSAGE_TOO_BIG",e[e.MDN_MANDATORY_EXTENSION=1010]="MDN_MANDATORY_EXTENSION",e[e.MDN_INTERNAL_ERROR=1011]="MDN_INTERNAL_ERROR",e[e.MDN_SERVICE_RESTART=1012]="MDN_SERVICE_RESTART",e[e.MDN_TRY_AGAIN_LATER=1013]="MDN_TRY_AGAIN_LATER",e[e.MDN_BAD_GATEWAY=1014]="MDN_BAD_GATEWAY",e[e.MDN_TLS_HANDSHAKE=1015]="MDN_TLS_HANDSHAKE",e[e.WHEN_BROWSER_CLOSE=4001]="WHEN_BROWSER_CLOSE",e[e.WS_4100_NORAML_CLOSURE=4100]="WS_4100_NORAML_CLOSURE",e[e.WS_4101_UNAUTHORIZED=4101]="WS_4101_UNAUTHORIZED",e[e.WS_4102_LICENSE_EXPIRED=4102]="WS_4102_LICENSE_EXPIRED",e[e.WS_4103_TOKEN_EXPIRED=4103]="WS_4103_TOKEN_EXPIRED",e[e.WS_4104_LLM_SESSION_ERROR=4104]="WS_4104_LLM_SESSION_ERROR",e[e.WS_4105_TOO_MANY_CONNECTIONS=4105]="WS_4105_TOO_MANY_CONNECTIONS"}(M||(M={}));class P{id;uuid;socket;messageSub;linkStartSub;destorySub;closeSub;isFinished;debugInfo;lastCloseLog;get message(){return this.messageSub.asObservable()}get close(){return this.closeSub.asObservable()}get linkStart(){return this.linkStartSub.asObservable()}get isConnected(){return this.socket?.readyState===i.OPEN}constructor(e){this.id=e,this.messageSub=new o.Subject,this.linkStartSub=new o.Subject,this.closeSub=new o.Subject,this.destorySub=new o.Subject,this.isFinished=!1,this.uuid=crypto.randomUUID(),this.debugInfo=`browser(${this.id}), proxy(${this.uuid})`,R.debug(`create proxy for browser(${this.id})`)}start(e){this.socket=new i(e,{autoPong:!0}),o.fromEvent(this.socket,"open").pipe(o.takeUntil(this.destorySub)).subscribe(()=>{R.code(k["啟動服務"],"proxy",this.debugInfo),this.linkStartSub.next()}),o.fromEvent(this.socket,"message").pipe(o.takeUntil(this.destorySub)).subscribe(e=>{const{data:t}=e;this.messageSub.next(t)}),o.fromEvent(this.socket,"error").pipe(o.takeUntil(this.destorySub)).subscribe(e=>{const{error:t}=e;R.error(this.debugInfo,"Proxy has some problem",t)}),o.fromEvent(this.socket,"close").pipe(o.takeUntil(this.destorySub)).subscribe(e=>{const{code:t,reason:s,type:r,wasClean:o}=e;switch(this.lastCloseLog=`最近一次關閉原因: ${s}, 錯誤碼: ${t}, Type: ${r}`,t){case M.WHEN_BROWSER_CLOSE:R.code(k["無副作用的關閉連線"],this.debugInfo),this.onDestroy();break;case M.MDN_NORMAL_CLOSURE:case M.MDN_GOING_AWAY:this.closeSub.next("close"),R.code(k["安全的關閉連線"],this.debugInfo);break;case M.MDN_NO_STATUS_RECEIVED:case M.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),R.code(k["與雲端的服務中斷"],this.debugInfo,{code:t,reason:s,type:r,wasClean:o});break;case M.WS_4100_NORAML_CLOSURE:case M.WS_4103_TOKEN_EXPIRED:case M.WS_4104_LLM_SESSION_ERROR:case M.WS_4105_TOO_MANY_CONNECTIONS:this.closeSub.next("reconnect"),R.code(k["自動重新連線雲端服務"],this.debugInfo,`socket code: ${t}`);break;case M.WS_4101_UNAUTHORIZED:case M.WS_4102_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),R.code(k["沒有授權,請聯繫Graphen"],this.debugInfo,`socket code: ${t}`);break;default:this.closeSub.next("close"),R.code(k["未預期的斷線,請聯繫Graphen"],this.debugInfo,{code:t,reason:s,type:r,wasClean:o})}})}reconnect(e){this.isFinished||(this.destorySub.next(),this.destorySub.complete(),this.destorySub=new o.Subject,this.start(e))}send(e){const s=`currentTime: ${t().format("yyyy-MM-DD[T]HH:mm:ss:sss")}`;this.isConnected?this.socket.send(e,t=>{t&&R.error("send message fail",this.debugInfo,s,e.slice(0,100),t)}):R.warn("The socket is not connected",this.debugInfo,s,e.slice(0,100))}sendToCloud(e){this.send(JSON.stringify(e))}onClose(){if(this.socket)switch(this.socket.readyState){case 0:R.debug("Once the cloud connection is complete, the connection will be disconnected."),this.linkStart.pipe(o.takeUntil(this.destorySub),o.take(1)).subscribe(()=>{this.socket?.close(M.WHEN_BROWSER_CLOSE)});break;case 1:R.debug("Close safely, no side effects"),this.socket.close(M.WHEN_BROWSER_CLOSE);break;case 2:case 3:R.debug("The connection have been lost.")}}getStatusInfo(){const e=(()=>{if(!this.socket)return"雲端服務尚未啟動";{const e=this.socket.readyState;switch(e){case 0:return"正在連線雲端服務中";case 1:return"雲端服務正常運作";case 2:return"正在關閉雲端服務";case 3:return"雲端服務已關閉";default:return`未知的 Websocket 狀態: ${e}`}}})(),t=this.lastCloseLog?`${e}, ${this.lastCloseLog}`:e;return`瀏覽器(${this.id}) - 雲端代理(${this.uuid}), ${t}`}onDestroy(){this.isFinished=!0,this.destorySub.next(),this.destorySub.complete(),R.debug(`Destroy cloud channel(${this.debugInfo})`)}}const T=new class{AppName;platform;isMAC;isWIN;isLINUX;saveFolder;homedir;appFolder;EOL;constructor(e){switch(this.AppName=e,this.platform=process.platform,this.homedir=c.homedir(),this.EOL=c.EOL,this.platform){case"darwin":this.isMAC=!0,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=h.join(this.homedir,"Library","Application Support");break;case"win32":this.isMAC=!1,this.isWIN=!0,this.isLINUX=!1,this.saveFolder=process.env.APPDATA||h.join(this.homedir,"AppData","Roaming");break;case"linux":this.isMAC=!1,this.isWIN=!1,this.isLINUX=!0,this.saveFolder=process.env.XDG_CONFIG_HOME||h.join(this.homedir,".config");break;default:console.log(`當前系統是未知的平台: ${this.platform}`),this.isMAC=!1,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=this.homedir}this.appFolder=h.join(this.saveFolder,this.AppName),this.folderCheck(this.appFolder)}addPath(...e){return h.join(...e)}folderCheck(e){a.mkdirSync(e,{recursive:!0})}createFolder(e){const t=this.addPath(this.appFolder,e);return this.folderCheck(t),t}fileCheck(e,t=""){a.existsSync(e)||a.writeFileSync(e,t,{encoding:"utf8"})}}("aiia-sdk");class v{type="node";filePath;fileName;constructor(){this.fileName="storage.json",this.filePath=T.addPath(T.appFolder,this.fileName),T.fileCheck(this.filePath)}getFileDataSync(){const e=a.readFileSync(this.filePath,"utf8");try{return JSON.parse(e)}catch(e){return{}}}setFileDataSync(e){try{const t=JSON.stringify(e);a.writeFileSync(this.filePath,t,"utf8")}catch(e){}}async getFileData(){return new Promise(e=>{a.readFile(this.filePath,"utf8",(t,s)=>{if(t)e({});else try{e(JSON.parse(s))}catch(t){e({})}})})}async setFileData(e){return new Promise(t=>{try{const s=JSON.stringify(e);a.writeFile(this.filePath,s,"utf8",e=>{t()})}catch(e){}})}async getItem(e){return(await this.getFileData())[e]??null}async setItem(e,t){const s=await this.getFileData();s[e]=t,await this.setFileData(s)}async removeItem(t){const s=await this.getFileData(),r=e.omit(s,t);await this.setFileData(r)}async clear(){await this.setFileData({})}getItemSync(e){return this.getFileDataSync()[e]??null}setItemSync(e,t){const s=this.getFileDataSync();s[e]=t,this.setFileDataSync(s)}removeItemSync(t){const s=e.omit(this.getFileDataSync(),t);this.setFileDataSync(s)}clearSync(){this.setFileDataSync({})}}const x={[k["通道、雲端服務、麥克風都已就緒"]]:"通道、雲端服務、麥克風都已就緒",[k["未取得媒體裝置權限,請重新設定或忽略此訊息"]]:"未取得媒體裝置權限,請重新設定或忽略此訊息",[k["請訂閱專案列表"]]:"請訂閱專案列表",[k["初始化必要依賴"]]:"初始化必要依賴",[k["指定依賴已完成"]]:"指定依賴已完成",[k["啟動SDK"]]:"啟動SDK",[k["結束SDK"]]:"結束SDK",[k["已啟動SDK"]]:"已啟動SDK",[k["安全的關閉連線"]]:"安全的關閉連線",[k["無副作用的關閉連線"]]:"無副作用的關閉連線",[k["與雲端的服務中斷"]]:"與雲端的服務中斷",[k["沒有授權,請聯繫Graphen"]]:"沒有授權,請聯繫Graphen",[k["自動重新連線雲端服務"]]:"自動重新連線雲端服務",[k["未預期的斷線,請聯繫Graphen"]]:"未預期的斷線,請聯繫Graphen",[k["開始初始化必要模組"]]:"開始初始化必要模組",[k["指定模組初始化完成"]]:"指定模組初始化完成",[k["啟動服務"]]:"啟動服務",[k["服務結束"]]:"服務結束",[m["網絡錯誤"]]:"網絡錯誤",[m["環境錯誤(browser)"]]:"環境錯誤",[m["Worklet模組載入失敗"]]:"Worklet模組載入失敗",[m["未獲得媒體裝置權限"]]:"未獲得媒體裝置權限",[m["實例已摧毀"]]:"實例已摧毀",[m["環境錯誤(nodejs)"]]:"nodejs",[m["未提供憑證"]]:"未提供憑證",[m["獲取權杖失敗"]]:"獲取權杖失敗",[m["沒有可用專案"]]:"沒有可用專案",[m["重複初始化"]]:"重複初始化"},I=new RegExp(/^(ERROR )?CODE: [0-9]{3,4}$/);class L{path;constructor(e){this.path=e?T.createFolder(e):T.appFolder}next(e,s,...r){const o=`log_${t().format("yyyy-MM-DD")}.txt`,n=T.addPath(this.path,o),i="code"===e?e=>I.test(e)?`${e}(${x[e]})`:e:e=>e;try{const t=r.reduce((e,t)=>{if(t instanceof Error){let s=`Error[${t.message??"No Message"}]${T.EOL}`;return s+=t.stack??"unknow stack","string"==typeof t.cause&&(s+=`${T.EOL}cause: ${t.cause}`),`${e}${T.EOL}${s}${T.EOL}`}switch(typeof t){case"string":return`${e} ${i(t)}`;case"number":case"bigint":return`${e} ${t.toString()}`;case"boolean":return`${e} ${t?"true":"false"}`;case"object":try{return`${e} ${JSON.stringify(t)}`}catch(t){return e}case"symbol":case"undefined":case"function":return`${e} [${typeof t}]`}},"");a.appendFileSync(n,`LOG(${e})__${s}__::${t}${T.EOL}`,{encoding:"utf8"})}catch(e){}}}const $="127.0.0.1",U={AvatarPort:"9876",ActionPort:"6969",LocationPort:"4060",LookPort:"6745",BackGroundPort:"2400"},W={StreamAudioPort:"8080",CleanAudioPort:"8081"};function F(){return{...U,...W}}class j{clientMap;eyeBallUdp;x_axis_px;y_axis_px;defPortMap;constructor(){this.clientMap=new Map,this.x_axis_px=0,this.y_axis_px=0,this.defPortMap={};const t=e.pick(U,"AvatarPort","ActionPort","LocationPort","BackGroundPort");e.forEach(e.keys(t),e=>{const s=e,r=t[s];this.defPortMap[r]=s})}enableUDPClient(t){e.forEach(e.keys(t),e=>{const s=new l.Client($,t[e]);this.clientMap.set(e,s)})}enableEyeTrack(e){this.eyeBallUdp=new l.Client($,e.port),this.x_axis_px=e.x_axis_px,this.y_axis_px=e.y_axis_px}eyeTrack(t){if(void 0!==this.eyeBallUdp){const r=null!==t,{x:o,y:n}=(()=>{if(r){const{x:r,y:o}=function(t,r){const{maxX:o,maxY:n,minX:i,minY:a}=e.assign({maxX:25,minX:-25,maxY:50,minY:-50},r),c=s.abs(o)+s.abs(i),h=s.abs(n)+s.abs(a),{clientHeight:l,clientWidth:u,originX:d,originY:p,width:f,height:g}=t,y=s.matrix([[c/u,0,i],[0,h/l,a],[0,0,1]]),m=d+f/2,b=l-(p+g/2),S=s.matrix([[m],[b],[1]]),_=s.multiply(y,S);return{x:_.get([0,0]),y:_.get([1,0])}}(t);return{x:this.toFloat(r+this.x_axis_px),y:this.toFloat(o+this.y_axis_px)}}return{x:this.toFloat(7),y:this.toFloat(20)}})();this.eyeBallUdp.send(["/",o,n],e=>{null!=e&&R.error("Eye track error:",e)})}}movement(t){e.map(t,({content:t})=>{const s=e.get(t,"port",""),r=e.get(t,"message"),o=this.defPortMap[s.toString()];this.clientMap.has(o)&&this.clientMap.get(o).send(r,e=>{null!=e&&R.error("Occur error when send message to UE",e)})})}toFloat(t){return e.isInteger(t)?t+1e-5:t}onDestroy(){e.forEach(Array.from(this.clientMap),([e,t])=>{t.close()}),R.info("Close all UnrealEngine's UDP")}}class G{destroy;speechWS;interruptWS;get speechIsConnected(){return this.speechWS?.readyState===i.OPEN}get intrIsConnected(){return this.interruptWS?.readyState===i.OPEN}constructor(){this.destroy=new o.Subject}connectSocket(e){this.speechWS=this.getSocket(e.speech,"speech"),this.interruptWS=this.getSocket(e.interrupt,"interrupt")}getSocket(e,t){const s=new i(`ws://${$}:${e}`);return s.onerror=s=>{const{message:r,error:o,type:n}=s;R.error(`UE Tool(${t}) fail: ${r}`,o,{port:e,type:n})},s.onclose=s=>{const{code:r,reason:o,type:n}=s;R.debug(`UE Tool(${t}) close`,{code:r,reason:o,type:n,port:e})},s}interrupted(){this.intrIsConnected&&this.interruptWS.send("interrupted")}speech(e){if(this.speechIsConnected){const t=Buffer.allocUnsafe(2*e.length);for(let s=0;s<e.length;s++)t.writeInt16LE(e[s],2*s);this.speechWS.send(t)}}onDestroy(){this.speechWS?.close(),this.interruptWS?.close(),this.destroy.next(),this.destroy.complete(),R.info("Close all UnrealEngine's WebSocket")}}class B{config;udpManager;socketManager;movement;eyeTrack;interrupted;speech;constructor(e){this.config=e,this.udpManager=new j,this.socketManager=new G,this.movement=()=>{},this.eyeTrack=()=>{},this.interrupted=()=>{},this.speech=()=>{}}onStart(t){const s={},r=F(),o=e.assign(F(),t);e.forEach(e.keys(r),e=>{const t=o[e],n=Number(t);s[e]=isNaN(n)?Number(r[e]):n});const{StreamAudioPort:n,CleanAudioPort:i,LookPort:a,...c}=s;this.udpManager.enableUDPClient(c),this.socketManager.connectSocket({speech:n,interrupt:i}),this.movement=this.udpManager.movement.bind(this.udpManager),this.eyeTrack=this.udpManager.eyeTrack.bind(this.udpManager),this.interrupted=this.socketManager.interrupted.bind(this.socketManager),this.speech=this.socketManager.speech.bind(this.socketManager),this.config.eyeTrackEnable&&this.udpManager.enableEyeTrack({...this.config.eyeTrackCorrection,port:a})}onDestroy(){this.udpManager.onDestroy(),this.socketManager.onDestroy(),R.info("UE tools destroy")}}async function Y(e){return new Promise(t=>{a.stat(e,e=>{t(e?[!1,e]:[!0])})})}async function H(e,t,...s){return T.isWIN?new Promise(r=>{const o=[`cd ${e} && ${t} `,...s].join(" "),n=u.exec(o);n.once("error",e=>{r([!1,e])}),n.once("spawn",()=>{n.unref(),r([!0])})}):Promise.resolve([!1,new Error("The function isn't implement in this system")])}async function K(){try{return await d.networkConnections()}catch(e){return console.error("檢查 Port 時發生錯誤:",e),[]}}async function X(t){const s=t.toString(),r=await K();return e.some(r,e=>e.localPort===s)}async function q(e){const t=await p.getWindows();let s=null;const r=new RegExp(e.toLocaleLowerCase());for(const e of t){const t=await e.title;if(r.test(t.toLocaleLowerCase())){s=e;break}}return s}function V(e){return[2e3,2e3,2e3,1e3,1e3,2e3,3e3,5e3][e]??[6e3,4e3][e%2]}function J(){if(T.isWIN){const e="UEProject_5_6";return{proc:e,name:`${e}.exe`}}return null}class Z{config;ueConfig;destroySub;constructor(t){this.destroySub=new o.Subject;const s={isKiosk:!1,browserKeeping:!1},{unrealFolder:r,openUrl:n,mode:i,foreground:a,remoteDebug:c}=e.assign({},t);if("string"==typeof r&&""!==r.trim()){s.path=r;const e=J();e&&(this.ueConfig=new z(r,e.proc))}"string"==typeof n&&""!==n.trim()&&(s.url=n),"string"==typeof i&&""!==i.trim()&&(s.isKiosk="kiosk"===i),"string"==typeof a&&""!==a.trim()&&(s.browserKeeping="always"===a),"number"==typeof c&&(s.remoteDebug=c),this.config=s}async exec(){let e=!1,t=await this.isUnrealRunning();t||(this.ueConfig&&await this.ueConfig.getSafePortTable(),t=await this.openUnreal()),t&&(e=await this.isUnrealInService()),e&&await this.openBrowser();return this.ueConfig?await this.ueConfig.getPortTable():F()}async isUnrealRunning(){const t=J();if(null===t)return!1;const{proc:s}=t,r=await async function(...e){const t=e.length>0?e.join(", "):"*";return await d.processLoad(t).catch(()=>[])}(s);if(r.length<=0)return!1;const o=s.toLocaleLowerCase(),n=e.find(r,e=>e.proc.toLocaleLowerCase()===o);return!!(n&&"number"==typeof n.pid&&n.pids.length>=2)}async isUnrealInService(e=12){let t=!1,s=0;const{StreamAudioPort:r}=this.ueConfig?await this.ueConfig.getPortTable():F();for(;!t&&s<e;)t=await X(r),await w(V(s)),s++;return t}async openUnreal(){const e=J();if(null===e)return!1;const{path:t}=this.config;if(void 0===t)return!1;const s=T.addPath(t,e.name),[r,o]=await Y(s);if(!r)return R.error(o),!1;const[n,i]=await async function(e,...t){return T.isWIN?new Promise(s=>{const r=u.spawn(e,t,{detached:!0,stdio:"ignore"});r.once("error",e=>{s([!1,e])}),r.once("spawn",()=>{r.unref(),s([!0])})}):Promise.resolve([!1,new Error("The function isn't implement in this system")])}(s);return i&&R.error(i),n}async openBrowser(){const{url:e,isKiosk:t,remoteDebug:s}=this.config;if(void 0===e)return!1;const r=T.isWIN?{edge:["C:/Program Files (x86)/Microsoft/Edge/Application","msedge.exe"],chrome:["C:/Program Files/Google/Chrome/Application","chrome.exe"],chrome2:[`${T.homedir}/AppData/Local/Google/Chrome/Application`,"chrome.exe"]}:null;if(null===r)return!1;const{proc:o}=J(),{chrome:n,chrome2:i,edge:a}=r,[[c],[h],[l]]=await Promise.all([Y(T.addPath(...n)),Y(T.addPath(...a)),Y(T.addPath(...i))]);if(c||l){const r=[];if(t&&(r.push("--kiosk"),r.push(`--auto-select-desktop-capture-source="${o}"`)),void 0!==s){r.push(`--remote-debugging-port=${s}`);const e=T.createFolder("google");r.push(`--user-data-dir=${e}`)}r.push(e);const[a,h]=c?n:i,[l,u]=await H(a,h,...r);return l?(this.setAppInForeground("chrome"),!0):(R.error(u),!1)}if(h){const r=[];if(t&&(r.push("--kiosk"),r.push(`--auto-select-desktop-capture-source="${o}"`)),void 0!==s){r.push(`--remote-debugging-port=${s}`);const e=T.createFolder("edge");r.push(`--user-data-dir=${e}`)}r.push(e);const[n,i]=a,[c,h]=await H(n,i,...r);return c?(this.setAppInForeground("edge"),!0):(R.error(h),!1)}return R.info("Can't find browser"),!1}async setAppInForeground(e){await w(3e3);const t=await q(e);t?.focus(),this.config.browserKeeping&&o.interval(1e4).pipe(o.takeUntil(this.destroySub)).subscribe(()=>{q(e).then(e=>{e?.focus()})})}async getStateInfo(){const e=await this.isUnrealRunning();return{isOpen:e,isReady:!!e&&await this.isUnrealInService(1)}}onDestroy(){this.destroySub.next(),this.destroySub.complete()}}class z{folder;file;constructor(e,t){this.folder=T.addPath(e,t,"config"),this.file=T.addPath(this.folder,"config.json"),T.folderCheck(this.folder),T.fileCheck(this.file,JSON.stringify(F()))}async getSafePortTable(){const t=F(),s=await K(),r=await async function(t,s){const r=s??await K(),o={};return e.forEach(e.keys(t),s=>{const n=e.some(r,({localPort:e})=>e===t[s]);o[s]=n}),o}(t,s);if(e.some(e.values(r),e=>e)){const o=new Set(e.filter(e.map(s,e=>Number(e.localPort)),e=>!isNaN(e)));e.forEach(e.keys(r),e=>{const s=e;if(r[s]){const e=Number(t[s]);let r=1,n=NaN;for(;isNaN(n)&&r<=200;){const t=e+r;o.has(t)?r++:(o.add(t),n=t)}isNaN(n)||(t[s]=n.toString())}})}await this.setPortTable(t)}getPortTable(){return new Promise(e=>{a.readFile(this.file,"utf8",(t,s)=>{if(t)e(F());else try{e(JSON.parse(s))}catch(t){e(F())}})})}async setPortTable(e){try{const t=JSON.stringify(e);await new Promise(e=>{a.writeFile(this.file,t,"utf8",t=>{e()})})}catch(e){}}}function Q(...e){switch(e[0]){case"--status":return"服務狀態確認\n 用法: status [額外參數]\n 範例:\n status\n";case"--log":return"日誌讀取(無參數時,會返回可用日誌)\n 用法: log [額外參數]\n\n 範例:\n log --y\n log --yesterday\n log --specific=2025-11-28\n 可用參數:\n today : 讀取今天的日誌\n t : 讀取今天的日誌\n yesterday : 讀取昨天的日誌\n y : 讀取昨天的日誌\n specific=<yyyy-MM-dd> : 讀取指定日期的日誌\n s=<yyyy-MM-dd> : 讀取指定日期的日誌\n";case"--exit":case"--close":return"安全離開\n 用法 1: exit\n 用法 2: close\n";default:return"用法: [可用指令] [額外參數]\n 範例:\n help\n help --log\n 可用指令列表:\n help : 獲得可用的指令,使用 [--可用指令] 獲得該指令的詳細說明。\n status : 顯示當前的服務狀態\n log : 讀取今天的日誌\n exit : 關閉連線\n close : exit 的別稱,關閉連線\n"}}function ee(e){return async(...t)=>{const{core:s,channel:r}=e.getStatusInfo(),o=r.reduce((e,t,s)=>e+` channel ${s+1} - ${t}\n`,"\n");let n=`服務狀態\n 核心狀態 : ${s}\n 通道列表 : ${0===r.length?"無服務通道":o}\n`;if(e.automaion){const{isOpen:t,isReady:s}=await e.automaion.getStateInfo();n+=" Unreal : ",n+=t?s?"已啟動,服務已就緒":"已啟動,但服務未就緒":"尚未啟動",n+="\n"}return n}}function te(...e){const s=e[0];if(!s)return new Promise(e=>{a.readdir(T.appFolder,(t,s)=>{e(t?"沒有可用日誌":s.filter(e=>e.endsWith(".txt")).join("\n"))})});{let e=null;switch(s){case"--today":case"--t":e=t();break;case"--yesterday":case"--y":e=t().add(-1);break;default:{const r=new RegExp(/^--(specific|s)=([0-9]{4}-[0-9]{1,2}-[0-9]{1,2})$/),o=s.match(r);if(o){const[s,r,n]=o[2].split("-");e=t(`${s}-${r.padStart(2,"0")}-${n.padStart(2,"0")}`);break}return"請使用 -- 開始,或使用 help --log 查看幫助"}}if(!e.isValid())return"指定了無效的日期,請再次確認";{const t=`log_${e.format("yyyy-MM-DD")}.txt`,s=T.addPath(T.appFolder,t);try{return a.readFileSync(s,"utf8")}catch(e){return"找不到日誌"}}}}class se{server;constructor(t,s){const r=function(e){return e?{help:Q,status:ee(e),log:te}:{}}(s),{port:o,host:n}=t;this.server=g.createServer(t=>{console.log(`⚙️ 新的連線來自: ${t.remoteAddress}`);const s=b.createInterface({input:t,output:t,prompt:"Aiia Admin > "});t.setEncoding("utf8"),t.write("歡迎使用Aiia Admin!\n"),t.write("使用 `help` 獲得幫助\n"),s.prompt(),s.on("line",async o=>{const[n,...i]=o.trim().split(/\s/),a=n.toLocaleLowerCase();if("exit"===a||"close"===a)t.write("👋 切斷連線...\n"),t.end();else{const o=e.get(r,a,null);if(o){const e=await o(...i);t.write("\n"+e+"\n")}else t.write(`"${n}"是無效的指令.\n`);s.prompt()}}),t.on("end",()=>{console.log(`⚙️ 切斷連線: ${t.remoteAddress}`)})}),this.server.listen(o,n,()=>{console.log(`連線指令: telnet 127.0.0.1 ${o}`)})}onDestroy(){this.server.close()}}class re{config;stroage;expiment;apiProxy;chat;ueTools;automaion;state;browserSocketMap;destorySub;access_token;timestamp;socketAddr;get accessToken(){return this.access_token}constructor(e,t,s){this.config=e,this.stroage=t,this.expiment=s,this.state=0,this.destorySub=new o.Subject,this.browserSocketMap=new Map,this.timestamp=0,this.access_token=t.getItemSync(S);const{api:r,socket:n}=e.endPoint;this.socketAddr=n,this.apiProxy=new A(r),R.code(k["指定模組初始化完成"],"api"),this.chat=new C(e),R.code(k["指定模組初始化完成"],"chat"),this.ueTools=new B(e),R.code(k["指定模組初始化完成"],"udp tools"),this.openChannel=this.openChannel.bind(this),this.initService()}async start(){if(this.state>=1)throw new Error(m["重複初始化"]);this.state=1,await this.replaceAccessToken(),this.chat.memberChange.pipe(o.takeUntil(this.destorySub)).subscribe(({type:t,socket:s})=>{const r=s;switch(t){case"in":{const t=new P(s.id),i=new o.Subject;r.__cloudProxy=t;const a=(n=s.handshake.query.uuid,e.isArray(n)?e.get(n,"[0]",void 0):n??void 0);void 0===a?(this.getProjectList().then(e=>{s.emit("list",e)}).catch(e=>{R.error(e),s.emit("list",null)}),o.fromEvent(s,"uuid").pipe(o.take(1),o.takeUntil(i)).subscribe(e=>{this.openChannel(e,r,t,i)})):this.openChannel(a,r,t,i),this.browserSocketMap.set(r,i);break}case"out":if(this.browserSocketMap.has(r)){const e=this.browserSocketMap.get(r);e.next(),e.complete(),this.browserSocketMap.delete(r)}}var n}),this.chat.start(),R.code(k["啟動服務"],"chat");const{interaction:t}=e.assign({},this.expiment);if(void 0!==t&&"number"==typeof t.port){const{port:e,host:s}=t,r=new se({port:e,host:s},this);this.destorySub.subscribe(()=>{r.onDestroy()})}}initService(){const{automaion:t}=e.assign({},this.expiment);if(void 0!==t){const e=new Z(t);e.exec().then(e=>{this.ueTools.onStart(e)}),this.destorySub.subscribe(()=>{e.onDestroy()}),this.automaion=e}else this.ueTools.onStart();o.interval(18e4).pipe(o.takeUntil(this.destorySub)).subscribe(()=>{this.inspectClientSocket()})}async replaceAccessToken(){try{const e=await this.apiProxy.getAccessToken(this.config.license,this.access_token),s=this.access_token;this.access_token=e,this.timestamp=0,this.stroage.setItemSync(S,e),this.stroage.setItemSync(t().format("yyyy-MM-DD[T]HH:mm:ss:sss"),s)}catch(e){throw R.error(e),e}}async getProjectList(){try{return await this.apiProxy.getProjectList(this.config.license)}catch(e){throw R.error(e),e}}async getProxySocketURL(e){const t=Date.now()-this.timestamp;return t<5e3&&await w(5100-t),this.timestamp=Date.now(),`${this.socketAddr}/v1/chat/project/${this.accessToken}/${e}`}async openChannel(t,s,r,n){o.fromEvent(s,"cloud").pipe(o.takeUntil(n)).subscribe(e=>{r.sendToCloud(e)}),o.fromEvent(s,"face").pipe(o.takeUntil(n)).subscribe(e=>{const t=e,s=null!==t;this.ueTools.eyeTrack(t),r.sendToCloud({request:"face_detect",content:s})}),r.message.pipe(o.takeUntil(n)).subscribe(t=>{const r=function(t){try{const s=JSON.parse(t);return e.get(s,"signal")?[s]:e.isArray(s)?s:e.values(s)}catch(e){return R.error("message transfer fail",e),[]}}(t),o=[],n=[];e.forEach(r,e=>{switch(e.signal){case"osc":n.push(e);break;case"audio":"pcm"===e.command?this.ueTools.speech(e.content):"interrupted"===e.command&&this.ueTools.interrupted();default:o.push(e)}}),o.length>0&&s.emit("cloud",o),n.length>0&&this.ueTools.movement(n)}),r.close.pipe(o.takeUntil(n)).subscribe(async e=>{switch(s.emit("channel",e),e){case"close":case"no_permissions":r.onDestroy();break;case"reconnect":try{await this.replaceAccessToken();const e=await this.getProxySocketURL(t);s.connected?r.reconnect(e):R.error(`The browser(${r.id}) leaves before reconnecting to the cloud`)}catch(e){r.onDestroy(),s.emit("channel","net_error"),R.error(e)}}}),r.linkStart.pipe(o.takeUntil(n)).subscribe(()=>{s.emit("channel","open")}),n.subscribe(()=>{r.onClose()});const i=await this.getProxySocketURL(t);s.connected?r.start(i):R.error(`The browser(${r.id}) leaves before connecting to the cloud`)}inspectClientSocket(){const t=Array.from(this.browserSocketMap);e.forEach(t,([e,t])=>{e.disconnected&&(t.next(),t.complete(),this.browserSocketMap.delete(e),R.info(`The browser(${e.id}) should be closed. Auto clean service.`))})}getStatusInfo(){return{core:0===this.state?"未啟動":1===this.state?"服務中":"已結束",channel:Array.from(this.browserSocketMap).map(([e])=>e.__cloudProxy.getStatusInfo())}}onDestroy(){this.state=2,this.apiProxy.onDestroy(),this.chat.onDestroy(),this.ueTools.onDestroy(),this.destorySub.next(),this.destorySub.complete(),R.code(k["服務結束"])}}exports.aiiaCore=function(t){if(_){if("string"!=typeof e.get(t,"license",void 0))throw new Error(m["未提供憑證"]);!function(e){const{info:t,error:s,warn:r,debug:o,fatal:n,code:i}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);return i||(D.code=()=>{}),o||(D.debug=()=>{}),t||(D.info=()=>{}),r||(D.warn=()=>{}),s||(D.error=()=>{}),n||(D.fatal=()=>{}),N}(e.get(t,"debug",!0))(new L),R.code(k["開始初始化必要模組"]);const s=new E(e.assign({},t));R.code(k["指定模組初始化完成"],"config");const r=new v;R.code(k["指定模組初始化完成"],"storage");const o=new re(s,r,e.get(t,"experiment"));return R.code(k["指定模組初始化完成"],"core"),o.start(),R.code(k["啟動服務"],"main core"),()=>{o.onDestroy()}}throw new Error(m["環境錯誤(nodejs)"])};
package/dist/node.d.ts CHANGED
@@ -15,6 +15,32 @@ export declare interface AiiaProjectItem {
15
15
  name: string;
16
16
  }
17
17
 
18
+ /**
19
+ * [zh]
20
+ * 自動啟動完整服務
21
+ *
22
+ * @property options.unrealFolder 放置應用程式的相對或絕對路徑(有資訊則會自動檢查 UE 狀態並啟動)
23
+ * @property options.openUrl 自動開啟網頁
24
+ * @property options.mode 開啟的模式
25
+ * @property options.foreground 強制瀏覽器至於前景
26
+ * @property options.remoteDebug 遠程調控
27
+ */
28
+ declare interface AutomationOptions {
29
+ unrealFolder?: string;
30
+ openUrl?: string;
31
+ mode?: "kiosk";
32
+ foreground?: "always";
33
+ remoteDebug?: number;
34
+ }
35
+
36
+ export declare interface ExperimentOptions {
37
+ interaction?: {
38
+ port?: number;
39
+ host?: string;
40
+ };
41
+ automaion?: AutomationOptions;
42
+ }
43
+
18
44
  declare interface InitOptions {
19
45
  /**
20
46
  * [zh]
@@ -187,7 +213,9 @@ declare interface InitOptions {
187
213
 
188
214
  declare type LoggerLevel = "info" | "error" | "warn" | "debug" | "fatal" | "code";
189
215
 
190
- export declare type NodeInitOptions = Omit<InitOptions, "project" | "ws_url" | "worklet_url" | "mediaStream" | "chunkTimeInSeconds" | "llmSampleRate" | "autoClearUserSubtitle" | "autoClearAiiaSubtitle" | "faceDetection" | "cameraDetection">;
216
+ export declare type NodeInitOptions = Omit<InitOptions, "project" | "ws_url" | "worklet_url" | "mediaStream" | "chunkTimeInSeconds" | "llmSampleRate" | "autoClearUserSubtitle" | "autoClearAiiaSubtitle" | "faceDetection" | "cameraDetection"> & {
217
+ experiment?: ExperimentOptions;
218
+ };
191
219
 
192
220
  declare type Webserver = Server<any, any> | Server_2<any, any> | Http2SecureServer<any, any, any, any> | Http2Server<any, any, any, any>;
193
221
 
package/dist/node.mjs CHANGED
@@ -1 +1 @@
1
- import{get as e,assign as t,forEach as s,map as r,omit as o,isInteger as i,isArray as n,values as a}from"lodash-es";import c from"moment";import{abs as h,matrix as l,multiply as u}from"mathjs";import{Server as d}from"socket.io";import{Subject as p,fromEvent as S,takeUntil as f,take as _,interval as b}from"rxjs";import g from"axios";import m from"ws";import E from"fs";import y from"os";import O from"path";import"child_process";import{Client as D}from"node-osc";var R;!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(R||(R={}));const N="access_token",w="undefined"!=typeof process&&null!=process.versions&&null!=process.versions.node;class k{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e(t,"id",void 0),r=e(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof r?r:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return t({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return t({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e(this.config,t,void 0)}}var C;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(C||(C={}));const M={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},A=[];function T(e){return A.push(e),T}const I=new Proxy(M,{get(e,t,r){if("string"==typeof t)switch(t){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...r)=>{const o=c().format("yyyy-MM-DD[T]HH:mm:ss:sss");s(A,e=>{e.next(t,o,...r)}),e[t](`[Aiia::${t}_${o}_]`,...r)};default:return}},set:(e,t,s,r)=>!0});class x{config;server;memberSub;get port(){return this.config.port}get webserver(){return this.config.webserver}get memberChange(){return this.memberSub.asObservable()}constructor(e){this.config=e,this.memberSub=new p}start(){if(this.server=new d(this.webserver,{cors:{origin:"*"}}),this.server.on("connection",e=>{I.debug("client on",e.id),this.memberSub.next({type:"in",socket:e}),e.on("disconnect",t=>{I.debug("client off",e.id,"reason",t),this.memberSub.next({type:"out",socket:e})})}),void 0===this.webserver){const e=this.port;this.server.listen(e),I.info("Websocket server run on",e)}else I.info("Websocket server rely your webserver.")}onDestroy(){this.server&&this.server.close()}}class v{instance;controller;constructor(e){this.controller=new AbortController,this.instance=g.create({baseURL:e,signal:this.controller.signal})}async getAccessToken(t,s){const r={license_number:t,token_value:s??""},o=await this.instance.post("/api/v1/license/agent_token",r).then(t=>e(t.data,"token_value","")).catch(()=>null);if(null===o)throw new Error(R["網絡錯誤"]);if(""===o)throw new Error(R["獲取權杖失敗"]);return o}async getProjectList(t){const s=await this.instance.post("/api/v1/license/projects",{license_number:t}).then(t=>r(e(t,"data.projects",[]),({id:e,sub_project_name:t,avatar:s})=>({avatar:s,id:e,name:t}))).catch(e=>null);if(null===s)throw new Error(R["網絡錯誤"]);if(0===s.length)throw new Error(R["沒有可用專案"]);return s}onDestroy(){this.controller.abort()}}var L;!function(e){e[e.MDN_NORMAL_CLOSURE=1e3]="MDN_NORMAL_CLOSURE",e[e.MDN_GOING_AWAY=1001]="MDN_GOING_AWAY",e[e.MDN_PROTOCOL_ERROR=1002]="MDN_PROTOCOL_ERROR",e[e.MDN_UNSUPPORTED_DATA=1003]="MDN_UNSUPPORTED_DATA",e[e.MDN_RESERVED_1004=1004]="MDN_RESERVED_1004",e[e.MDN_NO_STATUS_RECEIVED=1005]="MDN_NO_STATUS_RECEIVED",e[e.MDN_ABNORMAL_CLOSURE=1006]="MDN_ABNORMAL_CLOSURE",e[e.MDN_INVALID_FRAME_PAYLOAD=1007]="MDN_INVALID_FRAME_PAYLOAD",e[e.MDN_POLICY_VIOLATION=1008]="MDN_POLICY_VIOLATION",e[e.MDN_MESSAGE_TOO_BIG=1009]="MDN_MESSAGE_TOO_BIG",e[e.MDN_MANDATORY_EXTENSION=1010]="MDN_MANDATORY_EXTENSION",e[e.MDN_INTERNAL_ERROR=1011]="MDN_INTERNAL_ERROR",e[e.MDN_SERVICE_RESTART=1012]="MDN_SERVICE_RESTART",e[e.MDN_TRY_AGAIN_LATER=1013]="MDN_TRY_AGAIN_LATER",e[e.MDN_BAD_GATEWAY=1014]="MDN_BAD_GATEWAY",e[e.MDN_TLS_HANDSHAKE=1015]="MDN_TLS_HANDSHAKE",e[e.WHEN_BROWSER_CLOSE=4001]="WHEN_BROWSER_CLOSE",e[e.WS_4100_NORAML_CLOSURE=4100]="WS_4100_NORAML_CLOSURE",e[e.WS_4101_UNAUTHORIZED=4101]="WS_4101_UNAUTHORIZED",e[e.WS_4102_LICENSE_EXPIRED=4102]="WS_4102_LICENSE_EXPIRED",e[e.WS_4103_TOKEN_EXPIRED=4103]="WS_4103_TOKEN_EXPIRED",e[e.WS_4104_LLM_SESSION_ERROR=4104]="WS_4104_LLM_SESSION_ERROR",e[e.WS_4105_TOO_MANY_CONNECTIONS=4105]="WS_4105_TOO_MANY_CONNECTIONS"}(L||(L={}));class W{id;uuid;socket;messageSub;linkStartSub;destorySub;closeSub;isFinished;debugInfo;get message(){return this.messageSub.asObservable()}get close(){return this.closeSub.asObservable()}get linkStart(){return this.linkStartSub.asObservable()}get isConnected(){return this.socket?.readyState===m.OPEN}constructor(e){this.id=e,this.messageSub=new p,this.linkStartSub=new p,this.closeSub=new p,this.destorySub=new p,this.isFinished=!1,this.uuid=crypto.randomUUID(),this.debugInfo=`browser(${this.id}), proxy(${this.uuid})`,I.info(`create proxy for browser(${this.id})`)}start(e){this.socket=new m(e,{autoPong:!0}),S(this.socket,"open").pipe(f(this.destorySub)).subscribe(()=>{I.code(C["啟動服務"],"proxy",this.debugInfo),this.linkStartSub.next()}),S(this.socket,"message").pipe(f(this.destorySub)).subscribe(e=>{const{data:t}=e;this.messageSub.next(t)}),S(this.socket,"error").pipe(f(this.destorySub)).subscribe(e=>{const{error:t}=e;I.error(this.debugInfo,"Proxy has some problem",t)}),S(this.socket,"close").pipe(f(this.destorySub)).subscribe(e=>{const{code:t,reason:s,type:r,wasClean:o}=e;switch(t){case L.WHEN_BROWSER_CLOSE:I.code(C["無副作用的關閉連線"],this.debugInfo),this.onDestroy();break;case L.MDN_NORMAL_CLOSURE:case L.MDN_GOING_AWAY:this.closeSub.next("close"),I.code(C["安全的關閉連線"],this.debugInfo);break;case L.MDN_NO_STATUS_RECEIVED:case L.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),I.code(C["與雲端的服務中斷"],this.debugInfo,{code:t,reason:s,type:r,wasClean:o});break;case L.WS_4100_NORAML_CLOSURE:case L.WS_4103_TOKEN_EXPIRED:case L.WS_4104_LLM_SESSION_ERROR:case L.WS_4105_TOO_MANY_CONNECTIONS:this.closeSub.next("reconnect"),I.code(C["自動重新連線雲端服務"],this.debugInfo,`socket code: ${t}`);break;case L.WS_4101_UNAUTHORIZED:case L.WS_4102_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),I.code(C["沒有授權,請聯繫Graphen"],this.debugInfo,`socket code: ${t}`);break;default:this.closeSub.next("close"),I.code(C["未預期的斷線,請聯繫Graphen"],this.debugInfo,{code:t,reason:s,type:r,wasClean:o})}})}reconnect(e){this.isFinished||(this.destorySub.next(),this.destorySub.complete(),this.destorySub=new p,this.start(e))}send(e){const t=`currentTime: ${c().format("yyyy-MM-DD[T]HH:mm:ss:sss")}`;this.isConnected?this.socket.send(e,s=>{s&&I.error("send message fail",this.debugInfo,t,e.slice(0,100),s)}):I.warn("The socket is not connected",this.debugInfo,t,e.slice(0,100))}sendToCloud(e){this.send(JSON.stringify(e))}onClose(){if(this.socket)switch(this.socket.readyState){case 0:I.info("Once the cloud connection is complete, the connection will be disconnected."),this.linkStart.pipe(f(this.destorySub),_(1)).subscribe(()=>{this.socket?.close(L.WHEN_BROWSER_CLOSE)});break;case 1:I.info("Close safely, no side effects"),this.socket.close(L.WHEN_BROWSER_CLOSE);break;case 2:case 3:I.info("The connection have been lost.")}}onDestroy(){this.isFinished=!0,this.destorySub.next(),this.destorySub.complete(),I.info("Destroy proxy",this.debugInfo)}}const P=new class{AppName;platform;isMAC;isWIN;isLINUX;saveFolder;homedir;appFolder;EOL;constructor(e){switch(this.AppName=e,this.platform=process.platform,this.homedir=y.homedir(),this.EOL=y.EOL,this.platform){case"darwin":this.isMAC=!0,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=O.join(this.homedir,"Library","Application Support");break;case"win32":this.isMAC=!1,this.isWIN=!0,this.isLINUX=!1,this.saveFolder=process.env.APPDATA||O.join(this.homedir,"AppData","Roaming");break;case"linux":this.isMAC=!1,this.isWIN=!1,this.isLINUX=!0,this.saveFolder=process.env.XDG_CONFIG_HOME||O.join(this.homedir,".config");break;default:console.log(`當前系統是未知的平台: ${this.platform}`),this.isMAC=!1,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=this.homedir}this.appFolder=O.join(this.saveFolder,this.AppName),this.folderCheck(this.appFolder)}addPath(...e){return O.join(...e)}folderCheck(e){E.mkdirSync(e,{recursive:!0})}fileCheck(e){E.existsSync(e)||E.writeFileSync(e,"",{encoding:"utf8"})}}("aiia-sdk");class U{type="node";filePath;fileName;constructor(){this.fileName="storage.json",this.filePath=P.addPath(P.appFolder,this.fileName),P.fileCheck(this.filePath)}getFileDataSync(){const e=E.readFileSync(this.filePath,"utf8");try{return JSON.parse(e)}catch(e){return{}}}setFileDataSync(e){try{const t=JSON.stringify(e);E.writeFileSync(this.filePath,t,"utf8")}catch(e){}}async getFileData(){return new Promise(e=>{E.readFile(this.filePath,"utf8",(t,s)=>{if(t)e({});else try{e(JSON.parse(s))}catch(t){e({})}})})}async setFileData(e){return new Promise(t=>{try{const s=JSON.stringify(e);E.writeFile(this.filePath,s,"utf8",e=>{t()})}catch(e){}})}async getItem(e){return(await this.getFileData())[e]??null}async setItem(e,t){const s=await this.getFileData();s[e]=t,await this.setFileData(s)}async removeItem(e){const t=await this.getFileData(),s=o(t,e);await this.setFileData(s)}async clear(){await this.setFileData({})}getItemSync(e){return this.getFileDataSync()[e]??null}setItemSync(e,t){const s=this.getFileDataSync();s[e]=t,this.setFileDataSync(s)}removeItemSync(e){const t=o(this.getFileDataSync(),e);this.setFileDataSync(t)}clearSync(){this.setFileDataSync({})}}const $={[C["通道、雲端服務、麥克風都已就緒"]]:"通道、雲端服務、麥克風都已就緒",[C["未取得媒體裝置權限,請重新設定或忽略此訊息"]]:"未取得媒體裝置權限,請重新設定或忽略此訊息",[C["請訂閱專案列表"]]:"請訂閱專案列表",[C["初始化必要依賴"]]:"初始化必要依賴",[C["指定依賴已完成"]]:"指定依賴已完成",[C["啟動SDK"]]:"啟動SDK",[C["結束SDK"]]:"結束SDK",[C["已啟動SDK"]]:"已啟動SDK",[C["安全的關閉連線"]]:"安全的關閉連線",[C["無副作用的關閉連線"]]:"無副作用的關閉連線",[C["與雲端的服務中斷"]]:"與雲端的服務中斷",[C["沒有授權,請聯繫Graphen"]]:"沒有授權,請聯繫Graphen",[C["自動重新連線雲端服務"]]:"自動重新連線雲端服務",[C["未預期的斷線,請聯繫Graphen"]]:"未預期的斷線,請聯繫Graphen",[C["開始初始化必要模組"]]:"開始初始化必要模組",[C["指定模組初始化完成"]]:"指定模組初始化完成",[C["啟動服務"]]:"啟動服務",[C["服務結束"]]:"服務結束",[R["網絡錯誤"]]:"網絡錯誤",[R["環境錯誤(browser)"]]:"環境錯誤",[R["Worklet模組載入失敗"]]:"Worklet模組載入失敗",[R["未獲得媒體裝置權限"]]:"未獲得媒體裝置權限",[R["實例已摧毀"]]:"實例已摧毀",[R["環境錯誤(nodejs)"]]:"nodejs",[R["未提供憑證"]]:"未提供憑證",[R["獲取權杖失敗"]]:"獲取權杖失敗",[R["沒有可用專案"]]:"沒有可用專案",[R["重複初始化"]]:"重複初始化"},F=new RegExp(/^(ERROR )?CODE: [0-9]{3,4}$/);class G{next(e,t,...s){const r=`log_${c().format("yyyy-MM-DD")}.txt`,o=P.addPath(P.appFolder,r),i="code"===e?e=>F.test(e)?`${e}(${$[e]})`:e:e=>e;try{const r=s.reduce((e,t)=>{if(t instanceof Error){let s=`Error[${t.message??"No Message"}]${P.EOL}`;return s+=t.stack??"unknow stack","string"==typeof t.cause&&(s+=`${P.EOL}cause: ${t.cause}`),`${e}${P.EOL}${s}${P.EOL}`}switch(typeof t){case"string":return`${e} ${i(t)}`;case"number":case"bigint":return`${e} ${t.toString()}`;case"boolean":return`${e} ${t?"true":"false"}`;case"object":try{return`${e} ${JSON.stringify(t)}`}catch(t){return e}case"symbol":case"undefined":case"function":return`${e} [${typeof t}]`}},"");E.appendFileSync(o,`LOG(${e})__${t}__::${r}${P.EOL}`,{encoding:"utf8"})}catch(e){}}}class j{clientMap;eyeBallUdp;x_axis_px;y_axis_px;constructor(){this.clientMap=new Map,this.x_axis_px=0,this.y_axis_px=0}enableEyeTrack(e){this.eyeBallUdp=new D("127.0.0.1",6745),this.x_axis_px=e.x_axis_px,this.y_axis_px=e.y_axis_px}eyeTrack(e){if(void 0!==this.eyeBallUdp){const s=null!==e,{x:r,y:o}=(()=>{if(s){const{x:s,y:r}=function(e,s){const{maxX:r,maxY:o,minX:i,minY:n}=t({maxX:25,minX:-25,maxY:50,minY:-50},s),a=h(r)+h(i),c=h(o)+h(n),{clientHeight:d,clientWidth:p,originX:S,originY:f,width:_,height:b}=e,g=l([[a/p,0,i],[0,c/d,n],[0,0,1]]),m=l([[S+_/2],[d-(f+b/2)],[1]]),E=u(g,m);return{x:E.get([0,0]),y:E.get([1,0])}}(e);return{x:this.toFloat(s+this.x_axis_px),y:this.toFloat(r+this.y_axis_px)}}return{x:this.toFloat(7),y:this.toFloat(20)}})();this.eyeBallUdp.send(["/",r,o],e=>{null!=e&&I.error("Eye track error:",e)})}}movement(e){r(e,({content:e})=>{const{port:t,host:s,message:r}=e;this.getClient(t,s).send(r,e=>{null!=e&&I.error("Occur error when send message to UE",e)})})}getClient(e,t){if(this.clientMap.has(e))return this.clientMap.get(e);{const s=new D(t,e);return this.clientMap.set(e,s),s}}toFloat(e){return i(e)?e+1e-5:e}onDestroy(){s(Array.from(this.clientMap),([e,t])=>{t.close()}),I.info("Close all UnrealEngine's UDP")}}class Y{speechWS;interruptWS;get speechIsConnected(){return this.speechWS?.readyState===m.OPEN}get intrIsConnected(){return this.interruptWS?.readyState===m.OPEN}connectSocket(){this.speechWS=new m("ws://localhost:8080"),this.interruptWS=new m("ws://localhost:8081"),this.speechWS.onerror=e=>{I.error(`speech tool fail message: ${e.message}`,e.error)},this.interruptWS.onerror=e=>{I.error(`interrupt tool fail message: ${e.message}`,e.error)}}interrupted(){this.intrIsConnected&&this.interruptWS.send("interrupted")}speech(e){if(this.speechIsConnected){const t=Buffer.allocUnsafe(2*e.length);for(let s=0;s<e.length;s++)t.writeInt16LE(e[s],2*s);this.speechWS.send(t)}}onDestroy(){this.speechWS?.close(),this.interruptWS?.close(),I.info("Close all UnrealEngine's WebSocket")}}class X{config;udpManager;socketManager;movement;eyeTrack;interrupted;speech;constructor(e){this.config=e,this.udpManager=new j,this.socketManager=new Y,this.movement=()=>{},this.eyeTrack=()=>{},this.interrupted=()=>{},this.speech=()=>{},this.checkUnrealEngineStarted()}async checkUnrealEngineStarted(){this.socketManager.connectSocket(),this.movement=this.udpManager.movement.bind(this.udpManager),this.eyeTrack=this.udpManager.eyeTrack.bind(this.udpManager),this.interrupted=this.socketManager.interrupted.bind(this.socketManager),this.speech=this.socketManager.speech.bind(this.socketManager),this.config.eyeTrackEnable&&this.udpManager.enableEyeTrack(this.config.eyeTrackCorrection)}onDestroy(){this.udpManager.onDestroy(),this.socketManager.onDestroy(),I.info("UE tools destroy")}}class B{config;stroage;apiProxy;chat;ueTools;isStart;browserSocketMap;destorySub;access_token;timestamp;socketAddr;get accessToken(){return this.access_token}constructor(e,t){this.config=e,this.stroage=t,this.isStart=!1,this.destorySub=new p,this.browserSocketMap=new Map,this.timestamp=0,this.access_token=t.getItemSync(N);const{api:s,socket:r}=e.endPoint;this.socketAddr=r,this.apiProxy=new v(s),I.code(C["指定模組初始化完成"],"api"),this.chat=new x(e),I.code(C["指定模組初始化完成"],"chat"),this.ueTools=new X(e),I.code(C["指定模組初始化完成"],"udp tools"),this.openChannel=this.openChannel.bind(this),b(18e4).pipe(f(this.destorySub)).subscribe(()=>{this.inspectClientSocket()})}async start(){if(this.isStart)throw new Error(R["重複初始化"]);this.isStart=!0,await this.replaceAccessToken(),this.chat.memberChange.pipe(f(this.destorySub)).subscribe(({type:t,socket:s})=>{switch(t){case"in":{const t=new W(s.id),o=new p,i=(r=s.handshake.query.uuid,n(r)?e(r,"[0]",void 0):r??void 0);void 0===i?(this.getProjectList().then(e=>{s.emit("list",e)}),S(s,"uuid").pipe(_(1),f(o)).subscribe(e=>{this.openChannel(e,s,t,o)})):this.openChannel(i,s,t,o),this.browserSocketMap.set(s,o);break}case"out":if(this.browserSocketMap.has(s)){const e=this.browserSocketMap.get(s);e.next(),e.complete(),this.browserSocketMap.delete(s)}}var r}),this.chat.start(),I.code(C["啟動服務"],"chat")}async replaceAccessToken(){const e=await this.apiProxy.getAccessToken(this.config.license,this.access_token);return this.access_token=e,this.timestamp=0,this.stroage.setItemSync(N,e),e}async getProjectList(){return await this.apiProxy.getProjectList(this.config.license)}async getProxySocketURL(e){const t=Date.now()-this.timestamp;var s;return t<5e3&&await(s=5100-t,new Promise(e=>{setTimeout(e,s)})),this.timestamp=Date.now(),`${this.socketAddr}/v1/chat/project/${this.accessToken}/${e}`}async openChannel(t,r,o,i){S(r,"cloud").pipe(f(i)).subscribe(e=>{o.sendToCloud(e)}),S(r,"face").pipe(f(i)).subscribe(e=>{const t=e,s=null!==t;this.ueTools.eyeTrack(t),o.sendToCloud({request:"face_detect",content:s})}),o.message.pipe(f(i)).subscribe(t=>{const o=function(t){try{const s=JSON.parse(t);return e(s,"signal")?[s]:n(s)?s:a(s)}catch(e){return I.error("message transfer fail",e),[]}}(t),i=[],c=[];s(o,e=>{switch(e.signal){case"osc":c.push(e);break;case"audio":"pcm"===e.command?this.ueTools.speech(e.content):"interrupted"===e.command&&this.ueTools.interrupted();default:i.push(e)}}),i.length>0&&r.emit("cloud",i),c.length>0&&this.ueTools.movement(c)}),o.close.pipe(f(i)).subscribe(async e=>{switch(r.emit("channel",e),e){case"close":case"no_permissions":o.onDestroy();break;case"reconnect":{await this.replaceAccessToken();const e=await this.getProxySocketURL(t);r.connected?o.reconnect(e):I.error(`The browser(${o.id}) leaves before reconnecting to the cloud`);break}}}),o.linkStart.pipe(f(i)).subscribe(()=>{r.emit("channel","open")}),i.subscribe(()=>{o.onClose()});const c=await this.getProxySocketURL(t);r.connected?o.start(c):I.error(`The browser(${o.id}) leaves before connecting to the cloud`)}inspectClientSocket(){const e=Array.from(this.browserSocketMap);s(e,([e,t])=>{e.disconnected&&(t.next(),t.complete(),this.browserSocketMap.delete(e),I.info(`The browser(${e.id}) should be closed. Auto clean service.`))})}onDestroy(){this.apiProxy.onDestroy(),this.chat.onDestroy(),this.ueTools.onDestroy(),this.destorySub.next(),this.destorySub.complete(),I.code(C["服務結束"])}}function H(s){if(w){if("string"!=typeof e(s,"license",void 0))throw new Error(R["未提供憑證"]);!function(e){const{info:t,error:s,warn:r,debug:o,fatal:i,code:n}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);return n||(M.code=()=>{}),o||(M.debug=()=>{}),t||(M.info=()=>{}),r||(M.warn=()=>{}),s||(M.error=()=>{}),i||(M.fatal=()=>{}),T}(e(s,"debug",!0))(new G),I.code(C["開始初始化必要模組"]);const r=new k(t({},s));I.code(C["指定模組初始化完成"],"config");const o=new U;I.code(C["指定模組初始化完成"],"storage");const i=new B(r,o);return I.code(C["指定模組初始化完成"],"core"),i.start(),I.code(C["啟動服務"],"main core"),()=>{i.onDestroy()}}throw new Error(R["環境錯誤(nodejs)"])}export{H as aiiaCore};
1
+ import{get as e,assign as t,forEach as s,map as o,omit as r,pick as n,keys as i,isInteger as a,find as c,some as h,values as l,filter as u,isArray as d}from"lodash-es";import p from"moment";import{abs as f,matrix as g,multiply as m}from"mathjs";import{Server as y}from"socket.io";import{Subject as S,fromEvent as b,takeUntil as _,take as w,interval as E}from"rxjs";import D from"axios";import O from"ws";import N from"fs";import k from"os";import R from"path";import{Client as C}from"node-osc";import{spawn as A,exec as M}from"child_process";import P from"systeminformation";import{getWindows as T}from"@nut-tree-fork/nut-js";import*as I from"readline";import{createServer as x}from"net";var L;!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(L||(L={}));const v="access_token",$="undefined"!=typeof process&&null!=process.versions&&null!=process.versions.node;function F(e){return new Promise(t=>{setTimeout(t,e)})}class W{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e(t,"id",void 0),o=e(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof o?o:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return t({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return t({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e(this.config,t,void 0)}}var U;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(U||(U={}));const j={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},G=[];function B(e){return G.push(e),B}const Y=new Proxy(j,{get(e,t,o){if("string"==typeof t)switch(t){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...o)=>{const r=p().format("yyyy-MM-DD[T]HH:mm:ss:sss");s(G,e=>{e.next(t,r,...o)}),e[t](`[Aiia::${t}_${r}_]`,...o)};default:return}},set:(e,t,s,o)=>!0});class H{config;server;memberSub;get port(){return this.config.port}get webserver(){return this.config.webserver}get memberChange(){return this.memberSub.asObservable()}constructor(e){this.config=e,this.memberSub=new S}start(){if(this.server=new y(this.webserver,{cors:{origin:"*"}}),this.server.on("connection",e=>{Y.debug("client on",e.id),this.memberSub.next({type:"in",socket:e}),e.on("disconnect",t=>{Y.debug("client off",e.id,"reason",t),this.memberSub.next({type:"out",socket:e})})}),void 0===this.webserver){const e=this.port;this.server.listen(e),Y.info("Websocket server run on",e)}else Y.info("Websocket server rely your webserver.")}onDestroy(){this.server&&this.server.close()}}class K{instance;controller;constructor(e){this.controller=new AbortController,this.instance=D.create({baseURL:e,signal:this.controller.signal})}async getAccessToken(t,s){const o=performance.now(),r={license_number:t,token_value:s??""},n=await this.instance.post("/api/v1/license/agent_token",r).then(t=>e(t.data,"token_value","")).catch(()=>null);if(Y.debug("Complete certification",`Took ${performance.now()-o} milliseconds.`),null===n)throw new Error(L["網絡錯誤"]);if(""===n)throw new Error(L["獲取權杖失敗"]);return n}async getProjectList(t){const s=performance.now(),r=await this.instance.post("/api/v1/license/projects",{license_number:t}).then(t=>o(e(t,"data.projects",[]),({id:e,sub_project_name:t,avatar:s})=>{const{name:o,url:r}=s;return{avatar:{name:o,url:r},id:e,name:t}})).catch(e=>null);if(Y.debug("Got Project List",`Took ${performance.now()-s} milliseconds.`),null===r)throw new Error(L["網絡錯誤"]);if(0===r.length)throw new Error(L["沒有可用專案"]);return r}onDestroy(){this.controller.abort()}}var X;!function(e){e[e.MDN_NORMAL_CLOSURE=1e3]="MDN_NORMAL_CLOSURE",e[e.MDN_GOING_AWAY=1001]="MDN_GOING_AWAY",e[e.MDN_PROTOCOL_ERROR=1002]="MDN_PROTOCOL_ERROR",e[e.MDN_UNSUPPORTED_DATA=1003]="MDN_UNSUPPORTED_DATA",e[e.MDN_RESERVED_1004=1004]="MDN_RESERVED_1004",e[e.MDN_NO_STATUS_RECEIVED=1005]="MDN_NO_STATUS_RECEIVED",e[e.MDN_ABNORMAL_CLOSURE=1006]="MDN_ABNORMAL_CLOSURE",e[e.MDN_INVALID_FRAME_PAYLOAD=1007]="MDN_INVALID_FRAME_PAYLOAD",e[e.MDN_POLICY_VIOLATION=1008]="MDN_POLICY_VIOLATION",e[e.MDN_MESSAGE_TOO_BIG=1009]="MDN_MESSAGE_TOO_BIG",e[e.MDN_MANDATORY_EXTENSION=1010]="MDN_MANDATORY_EXTENSION",e[e.MDN_INTERNAL_ERROR=1011]="MDN_INTERNAL_ERROR",e[e.MDN_SERVICE_RESTART=1012]="MDN_SERVICE_RESTART",e[e.MDN_TRY_AGAIN_LATER=1013]="MDN_TRY_AGAIN_LATER",e[e.MDN_BAD_GATEWAY=1014]="MDN_BAD_GATEWAY",e[e.MDN_TLS_HANDSHAKE=1015]="MDN_TLS_HANDSHAKE",e[e.WHEN_BROWSER_CLOSE=4001]="WHEN_BROWSER_CLOSE",e[e.WS_4100_NORAML_CLOSURE=4100]="WS_4100_NORAML_CLOSURE",e[e.WS_4101_UNAUTHORIZED=4101]="WS_4101_UNAUTHORIZED",e[e.WS_4102_LICENSE_EXPIRED=4102]="WS_4102_LICENSE_EXPIRED",e[e.WS_4103_TOKEN_EXPIRED=4103]="WS_4103_TOKEN_EXPIRED",e[e.WS_4104_LLM_SESSION_ERROR=4104]="WS_4104_LLM_SESSION_ERROR",e[e.WS_4105_TOO_MANY_CONNECTIONS=4105]="WS_4105_TOO_MANY_CONNECTIONS"}(X||(X={}));class V{id;uuid;socket;messageSub;linkStartSub;destorySub;closeSub;isFinished;debugInfo;lastCloseLog;get message(){return this.messageSub.asObservable()}get close(){return this.closeSub.asObservable()}get linkStart(){return this.linkStartSub.asObservable()}get isConnected(){return this.socket?.readyState===O.OPEN}constructor(e){this.id=e,this.messageSub=new S,this.linkStartSub=new S,this.closeSub=new S,this.destorySub=new S,this.isFinished=!1,this.uuid=crypto.randomUUID(),this.debugInfo=`browser(${this.id}), proxy(${this.uuid})`,Y.debug(`create proxy for browser(${this.id})`)}start(e){this.socket=new O(e,{autoPong:!0}),b(this.socket,"open").pipe(_(this.destorySub)).subscribe(()=>{Y.code(U["啟動服務"],"proxy",this.debugInfo),this.linkStartSub.next()}),b(this.socket,"message").pipe(_(this.destorySub)).subscribe(e=>{const{data:t}=e;this.messageSub.next(t)}),b(this.socket,"error").pipe(_(this.destorySub)).subscribe(e=>{const{error:t}=e;Y.error(this.debugInfo,"Proxy has some problem",t)}),b(this.socket,"close").pipe(_(this.destorySub)).subscribe(e=>{const{code:t,reason:s,type:o,wasClean:r}=e;switch(this.lastCloseLog=`最近一次關閉原因: ${s}, 錯誤碼: ${t}, Type: ${o}`,t){case X.WHEN_BROWSER_CLOSE:Y.code(U["無副作用的關閉連線"],this.debugInfo),this.onDestroy();break;case X.MDN_NORMAL_CLOSURE:case X.MDN_GOING_AWAY:this.closeSub.next("close"),Y.code(U["安全的關閉連線"],this.debugInfo);break;case X.MDN_NO_STATUS_RECEIVED:case X.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),Y.code(U["與雲端的服務中斷"],this.debugInfo,{code:t,reason:s,type:o,wasClean:r});break;case X.WS_4100_NORAML_CLOSURE:case X.WS_4103_TOKEN_EXPIRED:case X.WS_4104_LLM_SESSION_ERROR:case X.WS_4105_TOO_MANY_CONNECTIONS:this.closeSub.next("reconnect"),Y.code(U["自動重新連線雲端服務"],this.debugInfo,`socket code: ${t}`);break;case X.WS_4101_UNAUTHORIZED:case X.WS_4102_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),Y.code(U["沒有授權,請聯繫Graphen"],this.debugInfo,`socket code: ${t}`);break;default:this.closeSub.next("close"),Y.code(U["未預期的斷線,請聯繫Graphen"],this.debugInfo,{code:t,reason:s,type:o,wasClean:r})}})}reconnect(e){this.isFinished||(this.destorySub.next(),this.destorySub.complete(),this.destorySub=new S,this.start(e))}send(e){const t=`currentTime: ${p().format("yyyy-MM-DD[T]HH:mm:ss:sss")}`;this.isConnected?this.socket.send(e,s=>{s&&Y.error("send message fail",this.debugInfo,t,e.slice(0,100),s)}):Y.warn("The socket is not connected",this.debugInfo,t,e.slice(0,100))}sendToCloud(e){this.send(JSON.stringify(e))}onClose(){if(this.socket)switch(this.socket.readyState){case 0:Y.debug("Once the cloud connection is complete, the connection will be disconnected."),this.linkStart.pipe(_(this.destorySub),w(1)).subscribe(()=>{this.socket?.close(X.WHEN_BROWSER_CLOSE)});break;case 1:Y.debug("Close safely, no side effects"),this.socket.close(X.WHEN_BROWSER_CLOSE);break;case 2:case 3:Y.debug("The connection have been lost.")}}getStatusInfo(){const e=(()=>{if(!this.socket)return"雲端服務尚未啟動";{const e=this.socket.readyState;switch(e){case 0:return"正在連線雲端服務中";case 1:return"雲端服務正常運作";case 2:return"正在關閉雲端服務";case 3:return"雲端服務已關閉";default:return`未知的 Websocket 狀態: ${e}`}}})(),t=this.lastCloseLog?`${e}, ${this.lastCloseLog}`:e;return`瀏覽器(${this.id}) - 雲端代理(${this.uuid}), ${t}`}onDestroy(){this.isFinished=!0,this.destorySub.next(),this.destorySub.complete(),Y.debug(`Destroy cloud channel(${this.debugInfo})`)}}const J=new class{AppName;platform;isMAC;isWIN;isLINUX;saveFolder;homedir;appFolder;EOL;constructor(e){switch(this.AppName=e,this.platform=process.platform,this.homedir=k.homedir(),this.EOL=k.EOL,this.platform){case"darwin":this.isMAC=!0,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=R.join(this.homedir,"Library","Application Support");break;case"win32":this.isMAC=!1,this.isWIN=!0,this.isLINUX=!1,this.saveFolder=process.env.APPDATA||R.join(this.homedir,"AppData","Roaming");break;case"linux":this.isMAC=!1,this.isWIN=!1,this.isLINUX=!0,this.saveFolder=process.env.XDG_CONFIG_HOME||R.join(this.homedir,".config");break;default:console.log(`當前系統是未知的平台: ${this.platform}`),this.isMAC=!1,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=this.homedir}this.appFolder=R.join(this.saveFolder,this.AppName),this.folderCheck(this.appFolder)}addPath(...e){return R.join(...e)}folderCheck(e){N.mkdirSync(e,{recursive:!0})}createFolder(e){const t=this.addPath(this.appFolder,e);return this.folderCheck(t),t}fileCheck(e,t=""){N.existsSync(e)||N.writeFileSync(e,t,{encoding:"utf8"})}}("aiia-sdk");class Z{type="node";filePath;fileName;constructor(){this.fileName="storage.json",this.filePath=J.addPath(J.appFolder,this.fileName),J.fileCheck(this.filePath)}getFileDataSync(){const e=N.readFileSync(this.filePath,"utf8");try{return JSON.parse(e)}catch(e){return{}}}setFileDataSync(e){try{const t=JSON.stringify(e);N.writeFileSync(this.filePath,t,"utf8")}catch(e){}}async getFileData(){return new Promise(e=>{N.readFile(this.filePath,"utf8",(t,s)=>{if(t)e({});else try{e(JSON.parse(s))}catch(t){e({})}})})}async setFileData(e){return new Promise(t=>{try{const s=JSON.stringify(e);N.writeFile(this.filePath,s,"utf8",e=>{t()})}catch(e){}})}async getItem(e){return(await this.getFileData())[e]??null}async setItem(e,t){const s=await this.getFileData();s[e]=t,await this.setFileData(s)}async removeItem(e){const t=await this.getFileData(),s=r(t,e);await this.setFileData(s)}async clear(){await this.setFileData({})}getItemSync(e){return this.getFileDataSync()[e]??null}setItemSync(e,t){const s=this.getFileDataSync();s[e]=t,this.setFileDataSync(s)}removeItemSync(e){const t=r(this.getFileDataSync(),e);this.setFileDataSync(t)}clearSync(){this.setFileDataSync({})}}const q={[U["通道、雲端服務、麥克風都已就緒"]]:"通道、雲端服務、麥克風都已就緒",[U["未取得媒體裝置權限,請重新設定或忽略此訊息"]]:"未取得媒體裝置權限,請重新設定或忽略此訊息",[U["請訂閱專案列表"]]:"請訂閱專案列表",[U["初始化必要依賴"]]:"初始化必要依賴",[U["指定依賴已完成"]]:"指定依賴已完成",[U["啟動SDK"]]:"啟動SDK",[U["結束SDK"]]:"結束SDK",[U["已啟動SDK"]]:"已啟動SDK",[U["安全的關閉連線"]]:"安全的關閉連線",[U["無副作用的關閉連線"]]:"無副作用的關閉連線",[U["與雲端的服務中斷"]]:"與雲端的服務中斷",[U["沒有授權,請聯繫Graphen"]]:"沒有授權,請聯繫Graphen",[U["自動重新連線雲端服務"]]:"自動重新連線雲端服務",[U["未預期的斷線,請聯繫Graphen"]]:"未預期的斷線,請聯繫Graphen",[U["開始初始化必要模組"]]:"開始初始化必要模組",[U["指定模組初始化完成"]]:"指定模組初始化完成",[U["啟動服務"]]:"啟動服務",[U["服務結束"]]:"服務結束",[L["網絡錯誤"]]:"網絡錯誤",[L["環境錯誤(browser)"]]:"環境錯誤",[L["Worklet模組載入失敗"]]:"Worklet模組載入失敗",[L["未獲得媒體裝置權限"]]:"未獲得媒體裝置權限",[L["實例已摧毀"]]:"實例已摧毀",[L["環境錯誤(nodejs)"]]:"nodejs",[L["未提供憑證"]]:"未提供憑證",[L["獲取權杖失敗"]]:"獲取權杖失敗",[L["沒有可用專案"]]:"沒有可用專案",[L["重複初始化"]]:"重複初始化"},z=new RegExp(/^(ERROR )?CODE: [0-9]{3,4}$/);class Q{path;constructor(e){this.path=e?J.createFolder(e):J.appFolder}next(e,t,...s){const o=`log_${p().format("yyyy-MM-DD")}.txt`,r=J.addPath(this.path,o),n="code"===e?e=>z.test(e)?`${e}(${q[e]})`:e:e=>e;try{const o=s.reduce((e,t)=>{if(t instanceof Error){let s=`Error[${t.message??"No Message"}]${J.EOL}`;return s+=t.stack??"unknow stack","string"==typeof t.cause&&(s+=`${J.EOL}cause: ${t.cause}`),`${e}${J.EOL}${s}${J.EOL}`}switch(typeof t){case"string":return`${e} ${n(t)}`;case"number":case"bigint":return`${e} ${t.toString()}`;case"boolean":return`${e} ${t?"true":"false"}`;case"object":try{return`${e} ${JSON.stringify(t)}`}catch(t){return e}case"symbol":case"undefined":case"function":return`${e} [${typeof t}]`}},"");N.appendFileSync(r,`LOG(${e})__${t}__::${o}${J.EOL}`,{encoding:"utf8"})}catch(e){}}}const ee="127.0.0.1",te={AvatarPort:"9876",ActionPort:"6969",LocationPort:"4060",LookPort:"6745",BackGroundPort:"2400"},se={StreamAudioPort:"8080",CleanAudioPort:"8081"};function oe(){return{...te,...se}}class re{clientMap;eyeBallUdp;x_axis_px;y_axis_px;defPortMap;constructor(){this.clientMap=new Map,this.x_axis_px=0,this.y_axis_px=0,this.defPortMap={};const e=n(te,"AvatarPort","ActionPort","LocationPort","BackGroundPort");s(i(e),t=>{const s=t,o=e[s];this.defPortMap[o]=s})}enableUDPClient(e){s(i(e),t=>{const s=new C(ee,e[t]);this.clientMap.set(t,s)})}enableEyeTrack(e){this.eyeBallUdp=new C(ee,e.port),this.x_axis_px=e.x_axis_px,this.y_axis_px=e.y_axis_px}eyeTrack(e){if(void 0!==this.eyeBallUdp){const s=null!==e,{x:o,y:r}=(()=>{if(s){const{x:s,y:o}=function(e,s){const{maxX:o,maxY:r,minX:n,minY:i}=t({maxX:25,minX:-25,maxY:50,minY:-50},s),a=f(o)+f(n),c=f(r)+f(i),{clientHeight:h,clientWidth:l,originX:u,originY:d,width:p,height:y}=e,S=g([[a/l,0,n],[0,c/h,i],[0,0,1]]),b=g([[u+p/2],[h-(d+y/2)],[1]]),_=m(S,b);return{x:_.get([0,0]),y:_.get([1,0])}}(e);return{x:this.toFloat(s+this.x_axis_px),y:this.toFloat(o+this.y_axis_px)}}return{x:this.toFloat(7),y:this.toFloat(20)}})();this.eyeBallUdp.send(["/",o,r],e=>{null!=e&&Y.error("Eye track error:",e)})}}movement(t){o(t,({content:t})=>{const s=e(t,"port",""),o=e(t,"message"),r=this.defPortMap[s.toString()];this.clientMap.has(r)&&this.clientMap.get(r).send(o,e=>{null!=e&&Y.error("Occur error when send message to UE",e)})})}toFloat(e){return a(e)?e+1e-5:e}onDestroy(){s(Array.from(this.clientMap),([e,t])=>{t.close()}),Y.info("Close all UnrealEngine's UDP")}}class ne{destroy;speechWS;interruptWS;get speechIsConnected(){return this.speechWS?.readyState===O.OPEN}get intrIsConnected(){return this.interruptWS?.readyState===O.OPEN}constructor(){this.destroy=new S}connectSocket(e){this.speechWS=this.getSocket(e.speech,"speech"),this.interruptWS=this.getSocket(e.interrupt,"interrupt")}getSocket(e,t){const s=new O(`ws://${ee}:${e}`);return s.onerror=s=>{const{message:o,error:r,type:n}=s;Y.error(`UE Tool(${t}) fail: ${o}`,r,{port:e,type:n})},s.onclose=s=>{const{code:o,reason:r,type:n}=s;Y.debug(`UE Tool(${t}) close`,{code:o,reason:r,type:n,port:e})},s}interrupted(){this.intrIsConnected&&this.interruptWS.send("interrupted")}speech(e){if(this.speechIsConnected){const t=Buffer.allocUnsafe(2*e.length);for(let s=0;s<e.length;s++)t.writeInt16LE(e[s],2*s);this.speechWS.send(t)}}onDestroy(){this.speechWS?.close(),this.interruptWS?.close(),this.destroy.next(),this.destroy.complete(),Y.info("Close all UnrealEngine's WebSocket")}}class ie{config;udpManager;socketManager;movement;eyeTrack;interrupted;speech;constructor(e){this.config=e,this.udpManager=new re,this.socketManager=new ne,this.movement=()=>{},this.eyeTrack=()=>{},this.interrupted=()=>{},this.speech=()=>{}}onStart(e){const o={},r=oe(),n=t(oe(),e);s(i(r),e=>{const t=n[e],s=Number(t);o[e]=isNaN(s)?Number(r[e]):s});const{StreamAudioPort:a,CleanAudioPort:c,LookPort:h,...l}=o;this.udpManager.enableUDPClient(l),this.socketManager.connectSocket({speech:a,interrupt:c}),this.movement=this.udpManager.movement.bind(this.udpManager),this.eyeTrack=this.udpManager.eyeTrack.bind(this.udpManager),this.interrupted=this.socketManager.interrupted.bind(this.socketManager),this.speech=this.socketManager.speech.bind(this.socketManager),this.config.eyeTrackEnable&&this.udpManager.enableEyeTrack({...this.config.eyeTrackCorrection,port:h})}onDestroy(){this.udpManager.onDestroy(),this.socketManager.onDestroy(),Y.info("UE tools destroy")}}async function ae(e){return new Promise(t=>{N.stat(e,e=>{t(e?[!1,e]:[!0])})})}async function ce(e,t,...s){return J.isWIN?new Promise(o=>{const r=[`cd ${e} && ${t} `,...s].join(" "),n=M(r);n.once("error",e=>{o([!1,e])}),n.once("spawn",()=>{n.unref(),o([!0])})}):Promise.resolve([!1,new Error("The function isn't implement in this system")])}async function he(){try{return await P.networkConnections()}catch(e){return console.error("檢查 Port 時發生錯誤:",e),[]}}async function le(e){const t=e.toString(),s=await he();return h(s,e=>e.localPort===t)}async function ue(e){const t=await T();let s=null;const o=new RegExp(e.toLocaleLowerCase());for(const e of t){const t=await e.title;if(o.test(t.toLocaleLowerCase())){s=e;break}}return s}function de(e){return[2e3,2e3,2e3,1e3,1e3,2e3,3e3,5e3][e]??[6e3,4e3][e%2]}function pe(){if(J.isWIN){const e="UEProject_5_6";return{proc:e,name:`${e}.exe`}}return null}class fe{config;ueConfig;destroySub;constructor(e){this.destroySub=new S;const s={isKiosk:!1,browserKeeping:!1},{unrealFolder:o,openUrl:r,mode:n,foreground:i,remoteDebug:a}=t({},e);if("string"==typeof o&&""!==o.trim()){s.path=o;const e=pe();e&&(this.ueConfig=new ge(o,e.proc))}"string"==typeof r&&""!==r.trim()&&(s.url=r),"string"==typeof n&&""!==n.trim()&&(s.isKiosk="kiosk"===n),"string"==typeof i&&""!==i.trim()&&(s.browserKeeping="always"===i),"number"==typeof a&&(s.remoteDebug=a),this.config=s}async exec(){let e=!1,t=await this.isUnrealRunning();t||(this.ueConfig&&await this.ueConfig.getSafePortTable(),t=await this.openUnreal()),t&&(e=await this.isUnrealInService()),e&&await this.openBrowser();return this.ueConfig?await this.ueConfig.getPortTable():oe()}async isUnrealRunning(){const e=pe();if(null===e)return!1;const{proc:t}=e,s=await async function(...e){const t=e.length>0?e.join(", "):"*";return await P.processLoad(t).catch(()=>[])}(t);if(s.length<=0)return!1;const o=t.toLocaleLowerCase(),r=c(s,e=>e.proc.toLocaleLowerCase()===o);return!!(r&&"number"==typeof r.pid&&r.pids.length>=2)}async isUnrealInService(e=12){let t=!1,s=0;const{StreamAudioPort:o}=this.ueConfig?await this.ueConfig.getPortTable():oe();for(;!t&&s<e;)t=await le(o),await F(de(s)),s++;return t}async openUnreal(){const e=pe();if(null===e)return!1;const{path:t}=this.config;if(void 0===t)return!1;const s=J.addPath(t,e.name),[o,r]=await ae(s);if(!o)return Y.error(r),!1;const[n,i]=await async function(e,...t){return J.isWIN?new Promise(s=>{const o=A(e,t,{detached:!0,stdio:"ignore"});o.once("error",e=>{s([!1,e])}),o.once("spawn",()=>{o.unref(),s([!0])})}):Promise.resolve([!1,new Error("The function isn't implement in this system")])}(s);return i&&Y.error(i),n}async openBrowser(){const{url:e,isKiosk:t,remoteDebug:s}=this.config;if(void 0===e)return!1;const o=J.isWIN?{edge:["C:/Program Files (x86)/Microsoft/Edge/Application","msedge.exe"],chrome:["C:/Program Files/Google/Chrome/Application","chrome.exe"],chrome2:[`${J.homedir}/AppData/Local/Google/Chrome/Application`,"chrome.exe"]}:null;if(null===o)return!1;const{proc:r}=pe(),{chrome:n,chrome2:i,edge:a}=o,[[c],[h],[l]]=await Promise.all([ae(J.addPath(...n)),ae(J.addPath(...a)),ae(J.addPath(...i))]);if(c||l){const o=[];if(t&&(o.push("--kiosk"),o.push(`--auto-select-desktop-capture-source="${r}"`)),void 0!==s){o.push(`--remote-debugging-port=${s}`);const e=J.createFolder("google");o.push(`--user-data-dir=${e}`)}o.push(e);const[a,h]=c?n:i,[l,u]=await ce(a,h,...o);return l?(this.setAppInForeground("chrome"),!0):(Y.error(u),!1)}if(h){const o=[];if(t&&(o.push("--kiosk"),o.push(`--auto-select-desktop-capture-source="${r}"`)),void 0!==s){o.push(`--remote-debugging-port=${s}`);const e=J.createFolder("edge");o.push(`--user-data-dir=${e}`)}o.push(e);const[n,i]=a,[c,h]=await ce(n,i,...o);return c?(this.setAppInForeground("edge"),!0):(Y.error(h),!1)}return Y.info("Can't find browser"),!1}async setAppInForeground(e){await F(3e3);const t=await ue(e);t?.focus(),this.config.browserKeeping&&E(1e4).pipe(_(this.destroySub)).subscribe(()=>{ue(e).then(e=>{e?.focus()})})}async getStateInfo(){const e=await this.isUnrealRunning();return{isOpen:e,isReady:!!e&&await this.isUnrealInService(1)}}onDestroy(){this.destroySub.next(),this.destroySub.complete()}}class ge{folder;file;constructor(e,t){this.folder=J.addPath(e,t,"config"),this.file=J.addPath(this.folder,"config.json"),J.folderCheck(this.folder),J.fileCheck(this.file,JSON.stringify(oe()))}async getSafePortTable(){const e=oe(),t=await he(),r=await async function(e,t){const o=t??await he(),r={};return s(i(e),t=>{const s=h(o,({localPort:s})=>s===e[t]);r[t]=s}),r}(e,t);if(h(l(r),e=>e)){const n=new Set(u(o(t,e=>Number(e.localPort)),e=>!isNaN(e)));s(i(r),t=>{const s=t;if(r[s]){const t=Number(e[s]);let o=1,r=NaN;for(;isNaN(r)&&o<=200;){const e=t+o;n.has(e)?o++:(n.add(e),r=e)}isNaN(r)||(e[s]=r.toString())}})}await this.setPortTable(e)}getPortTable(){return new Promise(e=>{N.readFile(this.file,"utf8",(t,s)=>{if(t)e(oe());else try{e(JSON.parse(s))}catch(t){e(oe())}})})}async setPortTable(e){try{const t=JSON.stringify(e);await new Promise(e=>{N.writeFile(this.file,t,"utf8",t=>{e()})})}catch(e){}}}function me(...e){switch(e[0]){case"--status":return"服務狀態確認\n 用法: status [額外參數]\n 範例:\n status\n";case"--log":return"日誌讀取(無參數時,會返回可用日誌)\n 用法: log [額外參數]\n\n 範例:\n log --y\n log --yesterday\n log --specific=2025-11-28\n 可用參數:\n today : 讀取今天的日誌\n t : 讀取今天的日誌\n yesterday : 讀取昨天的日誌\n y : 讀取昨天的日誌\n specific=<yyyy-MM-dd> : 讀取指定日期的日誌\n s=<yyyy-MM-dd> : 讀取指定日期的日誌\n";case"--exit":case"--close":return"安全離開\n 用法 1: exit\n 用法 2: close\n";default:return"用法: [可用指令] [額外參數]\n 範例:\n help\n help --log\n 可用指令列表:\n help : 獲得可用的指令,使用 [--可用指令] 獲得該指令的詳細說明。\n status : 顯示當前的服務狀態\n log : 讀取今天的日誌\n exit : 關閉連線\n close : exit 的別稱,關閉連線\n"}}function ye(e){return async(...t)=>{const{core:s,channel:o}=e.getStatusInfo(),r=o.reduce((e,t,s)=>e+` channel ${s+1} - ${t}\n`,"\n");let n=`服務狀態\n 核心狀態 : ${s}\n 通道列表 : ${0===o.length?"無服務通道":r}\n`;if(e.automaion){const{isOpen:t,isReady:s}=await e.automaion.getStateInfo();n+=" Unreal : ",n+=t?s?"已啟動,服務已就緒":"已啟動,但服務未就緒":"尚未啟動",n+="\n"}return n}}function Se(...e){const t=e[0];if(!t)return new Promise(e=>{N.readdir(J.appFolder,(t,s)=>{e(t?"沒有可用日誌":s.filter(e=>e.endsWith(".txt")).join("\n"))})});{let e=null;switch(t){case"--today":case"--t":e=p();break;case"--yesterday":case"--y":e=p().add(-1);break;default:{const s=new RegExp(/^--(specific|s)=([0-9]{4}-[0-9]{1,2}-[0-9]{1,2})$/),o=t.match(s);if(o){const[t,s,r]=o[2].split("-");e=p(`${t}-${s.padStart(2,"0")}-${r.padStart(2,"0")}`);break}return"請使用 -- 開始,或使用 help --log 查看幫助"}}if(!e.isValid())return"指定了無效的日期,請再次確認";{const t=`log_${e.format("yyyy-MM-DD")}.txt`,s=J.addPath(J.appFolder,t);try{return N.readFileSync(s,"utf8")}catch(e){return"找不到日誌"}}}}class be{server;constructor(t,s){const o=function(e){return e?{help:me,status:ye(e),log:Se}:{}}(s),{port:r,host:n}=t;this.server=x(t=>{console.log(`⚙️ 新的連線來自: ${t.remoteAddress}`);const s=I.createInterface({input:t,output:t,prompt:"Aiia Admin > "});t.setEncoding("utf8"),t.write("歡迎使用Aiia Admin!\n"),t.write("使用 `help` 獲得幫助\n"),s.prompt(),s.on("line",async r=>{const[n,...i]=r.trim().split(/\s/),a=n.toLocaleLowerCase();if("exit"===a||"close"===a)t.write("👋 切斷連線...\n"),t.end();else{const r=e(o,a,null);if(r){const e=await r(...i);t.write("\n"+e+"\n")}else t.write(`"${n}"是無效的指令.\n`);s.prompt()}}),t.on("end",()=>{console.log(`⚙️ 切斷連線: ${t.remoteAddress}`)})}),this.server.listen(r,n,()=>{console.log(`連線指令: telnet 127.0.0.1 ${r}`)})}onDestroy(){this.server.close()}}class _e{config;stroage;expiment;apiProxy;chat;ueTools;automaion;state;browserSocketMap;destorySub;access_token;timestamp;socketAddr;get accessToken(){return this.access_token}constructor(e,t,s){this.config=e,this.stroage=t,this.expiment=s,this.state=0,this.destorySub=new S,this.browserSocketMap=new Map,this.timestamp=0,this.access_token=t.getItemSync(v);const{api:o,socket:r}=e.endPoint;this.socketAddr=r,this.apiProxy=new K(o),Y.code(U["指定模組初始化完成"],"api"),this.chat=new H(e),Y.code(U["指定模組初始化完成"],"chat"),this.ueTools=new ie(e),Y.code(U["指定模組初始化完成"],"udp tools"),this.openChannel=this.openChannel.bind(this),this.initService()}async start(){if(this.state>=1)throw new Error(L["重複初始化"]);this.state=1,await this.replaceAccessToken(),this.chat.memberChange.pipe(_(this.destorySub)).subscribe(({type:t,socket:s})=>{const o=s;switch(t){case"in":{const t=new V(s.id),n=new S;o.__cloudProxy=t;const i=(r=s.handshake.query.uuid,d(r)?e(r,"[0]",void 0):r??void 0);void 0===i?(this.getProjectList().then(e=>{s.emit("list",e)}).catch(e=>{Y.error(e),s.emit("list",null)}),b(s,"uuid").pipe(w(1),_(n)).subscribe(e=>{this.openChannel(e,o,t,n)})):this.openChannel(i,o,t,n),this.browserSocketMap.set(o,n);break}case"out":if(this.browserSocketMap.has(o)){const e=this.browserSocketMap.get(o);e.next(),e.complete(),this.browserSocketMap.delete(o)}}var r}),this.chat.start(),Y.code(U["啟動服務"],"chat");const{interaction:s}=t({},this.expiment);if(void 0!==s&&"number"==typeof s.port){const{port:e,host:t}=s,o=new be({port:e,host:t},this);this.destorySub.subscribe(()=>{o.onDestroy()})}}initService(){const{automaion:e}=t({},this.expiment);if(void 0!==e){const t=new fe(e);t.exec().then(e=>{this.ueTools.onStart(e)}),this.destorySub.subscribe(()=>{t.onDestroy()}),this.automaion=t}else this.ueTools.onStart();E(18e4).pipe(_(this.destorySub)).subscribe(()=>{this.inspectClientSocket()})}async replaceAccessToken(){try{const e=await this.apiProxy.getAccessToken(this.config.license,this.access_token),t=this.access_token;this.access_token=e,this.timestamp=0,this.stroage.setItemSync(v,e),this.stroage.setItemSync(p().format("yyyy-MM-DD[T]HH:mm:ss:sss"),t)}catch(e){throw Y.error(e),e}}async getProjectList(){try{return await this.apiProxy.getProjectList(this.config.license)}catch(e){throw Y.error(e),e}}async getProxySocketURL(e){const t=Date.now()-this.timestamp;return t<5e3&&await F(5100-t),this.timestamp=Date.now(),`${this.socketAddr}/v1/chat/project/${this.accessToken}/${e}`}async openChannel(t,o,r,n){b(o,"cloud").pipe(_(n)).subscribe(e=>{r.sendToCloud(e)}),b(o,"face").pipe(_(n)).subscribe(e=>{const t=e,s=null!==t;this.ueTools.eyeTrack(t),r.sendToCloud({request:"face_detect",content:s})}),r.message.pipe(_(n)).subscribe(t=>{const r=function(t){try{const s=JSON.parse(t);return e(s,"signal")?[s]:d(s)?s:l(s)}catch(e){return Y.error("message transfer fail",e),[]}}(t),n=[],i=[];s(r,e=>{switch(e.signal){case"osc":i.push(e);break;case"audio":"pcm"===e.command?this.ueTools.speech(e.content):"interrupted"===e.command&&this.ueTools.interrupted();default:n.push(e)}}),n.length>0&&o.emit("cloud",n),i.length>0&&this.ueTools.movement(i)}),r.close.pipe(_(n)).subscribe(async e=>{switch(o.emit("channel",e),e){case"close":case"no_permissions":r.onDestroy();break;case"reconnect":try{await this.replaceAccessToken();const e=await this.getProxySocketURL(t);o.connected?r.reconnect(e):Y.error(`The browser(${r.id}) leaves before reconnecting to the cloud`)}catch(e){r.onDestroy(),o.emit("channel","net_error"),Y.error(e)}}}),r.linkStart.pipe(_(n)).subscribe(()=>{o.emit("channel","open")}),n.subscribe(()=>{r.onClose()});const i=await this.getProxySocketURL(t);o.connected?r.start(i):Y.error(`The browser(${r.id}) leaves before connecting to the cloud`)}inspectClientSocket(){const e=Array.from(this.browserSocketMap);s(e,([e,t])=>{e.disconnected&&(t.next(),t.complete(),this.browserSocketMap.delete(e),Y.info(`The browser(${e.id}) should be closed. Auto clean service.`))})}getStatusInfo(){return{core:0===this.state?"未啟動":1===this.state?"服務中":"已結束",channel:Array.from(this.browserSocketMap).map(([e])=>e.__cloudProxy.getStatusInfo())}}onDestroy(){this.state=2,this.apiProxy.onDestroy(),this.chat.onDestroy(),this.ueTools.onDestroy(),this.destorySub.next(),this.destorySub.complete(),Y.code(U["服務結束"])}}function we(s){if($){if("string"!=typeof e(s,"license",void 0))throw new Error(L["未提供憑證"]);!function(e){const{info:t,error:s,warn:o,debug:r,fatal:n,code:i}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);return i||(j.code=()=>{}),r||(j.debug=()=>{}),t||(j.info=()=>{}),o||(j.warn=()=>{}),s||(j.error=()=>{}),n||(j.fatal=()=>{}),B}(e(s,"debug",!0))(new Q),Y.code(U["開始初始化必要模組"]);const o=new W(t({},s));Y.code(U["指定模組初始化完成"],"config");const r=new Z;Y.code(U["指定模組初始化完成"],"storage");const n=new _e(o,r,e(s,"experiment"));return Y.code(U["指定模組初始化完成"],"core"),n.start(),Y.code(U["啟動服務"],"main core"),()=>{n.onDestroy()}}throw new Error(L["環境錯誤(nodejs)"])}export{we as aiiaCore};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphen.ai/aiia-sdk",
3
- "version": "1.0.17",
3
+ "version": "1.0.18",
4
4
  "keywords": [],
5
5
  "author": {
6
6
  "name": "Jay Huang",
@@ -71,6 +71,7 @@
71
71
  },
72
72
  "dependencies": {
73
73
  "@mediapipe/tasks-vision": "^0.10.22-rc.20250304",
74
+ "@nut-tree-fork/nut-js": "^4.2.6",
74
75
  "axios": "^1.11.0",
75
76
  "lodash-es": "^4.17.21",
76
77
  "mathjs": "^14.8.1",
@@ -79,6 +80,7 @@
79
80
  "rxjs": "^7.8.2",
80
81
  "socket.io": "^4.8.1",
81
82
  "socket.io-client": "^4.8.1",
83
+ "systeminformation": "^5.27.11",
82
84
  "ws": "^8.18.3"
83
85
  }
84
86
  }