@graphen.ai/aiia-sdk 1.0.16 → 1.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,7 +23,7 @@ yarn add @graphen.ai/aiia-sdk
23
23
  ```typescript
24
24
  import { initSdk } from '@graphen.ai/aiia-sdk/browser';
25
25
 
26
- const sdk = new initSdk({
26
+ const sdk = initSdk({
27
27
  worklet_url: "<Please copy '@graphen.ai/aiia-sdk/dist/aiia-worklet.js' to your project's public folder and provide the source url>"
28
28
  });
29
29
  sdk.start()
@@ -63,11 +63,12 @@ const core = aiiaCore({ license: "<Your Token>" });
63
63
  | CODE: 1011 | 指定依賴已完成 | Specified Dependencies Completed |
64
64
  | CODE: 1012 | 啟動SDK | Starting SDK |
65
65
  | CODE: 1013 | 結束SDK | Ending SDK |
66
+ | CODE: 1014 | 已啟動SDK | SDK has been started |
66
67
  | CODE: 2000 | 安全的關閉連線 | Safely Closing Connection |
67
68
  | CODE: 2001 | 無副作用的關閉連線 | Closing Connection Without Side Effects |
68
69
  | CODE: 2002 | 與雲端的服務中斷 | Disconnected from Cloud Service |
69
70
  | CODE: 2003 | 沒有授權,請聯繫Graphen | No Authorization, Please Contact Graphen |
70
- | CODE: 2004 | 自動重新連線連端服務 | Automatically Reconnecting to Cloud Service |
71
+ | CODE: 2004 | 自動重新連線雲端服務 | Automatically Reconnecting to Cloud Service |
71
72
  | CODE: 2005 | 未預期的斷線,請聯繫Graphen | Unexpected Disconnection, Please Contact Graphen |
72
73
  | CODE: 2010 | 開始初始化必要模組 | Starting Initialization of Required Modules |
73
74
  | CODE: 2011 | 指定模組初始化完成 | Specified Module Initialization Completed |
@@ -1 +1,2 @@
1
+ /** @license AiiaVad Released: 2025-11-4 */
1
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 i,s=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"}(i||(i={}));const o="undefined"!=typeof window;"undefined"!=typeof process&&null!=process.versions&&process.versions.node;class r{config;get webserver(){return this.config.webserver}get environment(){return this.config.env}get license(){return this.config.license??""}get port(){return function(e,t){const i=t??NaN;if(e){const t=parseInt(e);return isNaN(t)?i:t}return i}(process.env.PORT,this.config.port??3e3)}get ws_url(){return this.config.ws_url??""}get worklet_url(){return this.config.worklet_url??""}get project(){const{project:t}=e.assign({},this.config);return void 0===t?{specific:!1}:"string"==typeof t?{name:t,id:t,specific:!1}:e.assign({},t,{specific:!0})}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}return{api:`https://aiia-content-management-${e}-21193779403.asia-east1.run.app`,socket:`wss://graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`}}get debug(){return this.config.debug??!0}get mediaStream(){return this.config.mediaStream}get chunkTimeInSeconds(){return"number"==typeof this.config.chunkTimeInSeconds&&this.config.chunkTimeInSeconds>0?this.config.chunkTimeInSeconds:.3}get llmSampleRate(){return"number"==typeof this.config.llmSampleRate&&this.config.llmSampleRate>0?this.config.llmSampleRate:16e3}get faceDetection(){return e.assign({perSecond:3,confidence:80},this.config.faceDetection)}get autoClearSubtitle(){return{userDelayTime:this.config.autoClearUserSubtitle??5e3,aiiaDelayTime:this.config.autoClearAiiaSubtitle??NaN}}get eyeTrackEnable(){return this.config.eyeTrackEnable??!1}get eyeTrackCorrection(){return e.assign({x_axis_px:0,y_axis_px:0},this.config.correction)}constructor(e){this.config=e}}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["安全的關閉連線"]="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},h=[];const d=new Proxy(u,{get(i,s,a){if("string"==typeof s)switch(s){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");e.forEach(h,e=>{e.next(s,n,...a)}),i[s](`[Aiia::${s}_${n}_]`,...a)};default:return}},set:(e,t,i,s)=>!0});var l,b;function p(e,t,i){return Math.max(i,Math.min(e,t))}function m(e){const t=new DataView(e.buffer),i=[];for(let s=0;s<e.length;s++){const e=t.getInt16(2*s,!0)/32768;i.push(e)}return new Float32Array(i)}function g(e){const t=new DataView(e.buffer),i=[];for(let s=0;s<e.length;s++){const e=p(32768*t.getFloat32(4*s,!0),32767,-32768);i.push(e)}return i}!function(e){e[e.unknow=0]="unknow",e[e.allowed=1]="allowed",e[e.rejected=2]="rejected"}(l||(l={}));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 i=e.length,s=this.audioContext.sampleRate,a=this.audioContext.createBuffer(t,i,s),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=>{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 S{stream;audioContext;type="vad";workletNode;source;isRecording=!1;outputSampleRate;chunkTimeInSeconds;pcmSub;get pcm(){return this.pcmSub.asObservable()}constructor(e,t,i){this.stream=e,this.audioContext=t,this.outputSampleRate=i?.llmSampleRate??16e3,this.chunkTimeInSeconds=i?.chunkTimeInSeconds??1,this.pcmSub=new s.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 videoSize(){return this.aiiaCamera.videoSize}constructor(t){const{perSecond:i}=e.assign({perSecond:3},t);this.aiiaCamera=new w,this.aiiaCamera.setFrequency(i),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"),i=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(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;_isDebug;_debugElements;_interval;_lastTimestamp;_loopFrameID;_faceDetector;_detectionsSub;_isReady;get detections(){return this._detectionsSub.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._debugElements=[],this._loopFrameID=null,this._detectionsSub=new s.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)}connectedCallback(){this.startCapture()}disconnectedCallback(){this.stopCapture()}attributeChangedCallback(e,t,i){"debug"===e&&(this._isDebug=null!==i&&("true"===i||""===i),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}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 .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;const e=performance.now();if(this._lastTimestamp+this._interval<=e){this._lastTimestamp=e;const t=this._faceDetector.detectForVideo(this._videoEl,e).detections;this._isDebug&&this._drawRect(t),this._detectionsSub.next(t)}}_drawRect(t){e.forEach(t,(e,t)=>{const{categories:i,boundingBox:s}=e;if(s){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*i[0].score;a.innerText=`Confidence: ${Math.round(n)} %`,a.style.right=this._videoEl.offsetWidth-s.width-s.originX+"px",a.style.top=s.originY-30+"px",a.style.width=s.width-10+"px",e.style.right=this._videoEl.offsetWidth-s.width-s.originX+"px",e.style.top=`${s.originY}px`,e.style.width=s.width-10+"px",e.style.height=`${s.height}px`}});for(let e=2*t.length;e<this._debugElements.length;e++)this._debugElements[e].style.display="none"}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"}(b||(b={}));class _{stream;_state;stateSub;audioCtx;audioManager;vadManager;camManager;destory;pcmSub;detectionsSub;get sampleRate(){return this.audioCtx?this.audioCtx.sampleRate:16e3}get pcm(){return this.pcmSub.asObservable()}get detections(){return this.detectionsSub.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 s.Subject,this.pcmSub=new s.Subject,this.destory=new s.Subject,this.detectionsSub=new s.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(i["未獲得媒體裝置權限"]);this.stream=a,this.audioCtx=new AudioContext;try{await this.audioCtx.audioWorklet.addModule(e)}catch(e){throw d.fatal(e),this.state=b.loadfail,new Error(i["Worklet模組載入失敗"])}this.state=b.allowed,this.audioManager=new f(this.audioCtx),this.vadManager=new S(a,this.audioCtx,t),this.vadManager.pcm.pipe(s.takeUntil(this.destory)).subscribe(e=>{this.pcmSub.next(e)});const n=new y(t?.faceDetection);n.detections.pipe(s.takeUntil(this.destory)).subscribe(e=>{const{width:t,height:i}=n.videoSize;this.detectionsSub.next({detections:e,clientWidth:t,clientHeight:i})}),n.init(a),this.camManager=n}addAudioQueue(t){const i=e.get(t,"buffer",void 0),s=e.get(t,"float32",void 0),a=e.get(t,"int16",void 0),n=e.get(t,"numberOfChannels",void 0);i?this.audioManager?.addBuffer(i):s?this.audioManager?.addBufferByFloat32(s,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.destory.next(),this.destory.complete()}}class C{socket;messageSub;destorySub;get message(){return this.messageSub.asObservable()}constructor(t){this.destorySub=new s.Subject,this.messageSub=new s.Subject;const{specific:i,id:a}=t.project;this.socket=n.io(t.ws_url,e.assign({autoConnect:!1},i&&void 0!==a?{query:{uuid:a}}:{}))}start(){const e="list",t="cloud",i="channel";s.fromEvent(this.socket,"connect").pipe(s.takeUntil(this.destorySub)).subscribe(()=>{d.debug("Client ID: ",this.socket.id)}),s.fromEvent(this.socket,e).pipe(s.take(1),s.takeUntil(this.destorySub)).subscribe(t=>{this.messageSub.next({event:e,data:t})}),s.fromEvent(this.socket,t).pipe(s.takeUntil(this.destorySub)).subscribe(e=>{this.messageSub.next({event:t,data:e})}),s.fromEvent(this.socket,i).pipe(s.takeUntil(this.destorySub)).subscribe(e=>{this.messageSub.next({event:i,data:e})}),this.socket.connect()}send(e,t){this.socket.emit(e,t)}sendToCloud(e){this.send("cloud",JSON.stringify(e))}onDestroy(){this.destorySub.next(),this.destorySub.complete(),this.messageSub.complete(),this.socket.disconnect()}}class v{config;media;chat;destorySub;projectsSub;layoutSub;aiiaSubtitleSub;userSubtitleSub;sampleRateOfWhisper;channelState;stateSub;_currentState;isMuteAiia;aiiaSubtitleClearSub;userSubtitleClearSub;_lastProjects;guestSub;_hasGuest;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 s.Subject,this.projectsSub=new s.ReplaySubject(1),this.destorySub=new s.Subject,this.aiiaSubtitleSub=new s.Subject,this.userSubtitleSub=new s.Subject,this.aiiaSubtitleClearSub=new s.Subject,this.userSubtitleClearSub=new s.Subject,this.stateSub=new s.BehaviorSubject("NotStart"),this.sampleRateOfWhisper={input:e.llmSampleRate,output:16e3},this.channelState={proxy:!1,cloud:!1},this.isMuteAiia=!1,this._lastProjects=[],this.guestSub=new s.ReplaySubject(1),this._hasGuest=!1,this.chat=new C(e),d.code(c["指定依賴已完成"],"chat"),this.close=this.onDestroy,this.behavior=this.behavior.bind(this),this.cloudToObj=this.cloudToObj.bind(this);const{userDelayTime:i,aiiaDelayTime:a}=e.autoClearSubtitle;isNaN(a)||this.aiiaSubtitleClearSub.pipe(s.debounceTime(a),s.takeUntil(this.destorySub)).subscribe(()=>{this.aiiaSubtitleSub.next("")}),isNaN(i)||this.userSubtitleClearSub.pipe(s.debounceTime(i),s.takeUntil(this.destorySub)).subscribe(()=>{this.userSubtitleSub.next("")}),this.stateSub.pipe(s.takeUntil(this.destorySub)).subscribe(e=>{this._currentState=e}),this.projectsSub.pipe(s.takeUntil(this.destorySub)).subscribe(e=>{this._lastProjects=e}),this.guestSub.pipe(s.takeUntil(this.destorySub)).subscribe(e=>{this._hasGuest=e})}start(){this.media.stateObs.pipe(s.takeUntil(this.destorySub)).subscribe(()=>{this.channelCheck()}),this.chat.message.pipe(s.takeUntil(this.destorySub)).subscribe(({event:t,data:i})=>{switch(t){case"cloud":this.cloudToObj(i);break;case"list":{const{id:t,name:s,specific:a}=this.config.project;let n=null;if(a&&void 0!==t)n=t;else{const a=e.find(i,e=>e.name===s||e.id===t);a&&(n=a.id)}null!==n?this.chooseProject(n):(this.projectsSub.next(i),this.stateSub.next("WattingProjectID"),d.code(c["請訂閱專案列表"]));break}case"channel":switch(i){case"open":this.channelState.proxy=!0,this.channelCheck();break;case"close":this.channelState.proxy=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("CloseService");break;case"no_permissions":this.channelState.proxy=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("NoPermissions");break;case"reconnect":this.channelState.proxy=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("ReconnectingCloud")}}}),this.chat.start(),this.stateSub.next("ConnectingCloud"),d.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()}cloudToObj(t){try{const i=JSON.parse(t);void 0!==e.get(i,"signal")?this.behavior(i):e.isArray(i)?e.map(i,this.behavior):e.map(e.values(i),this.behavior)}catch(e){d.error("message transfer fail",e)}}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,i){if(t===i)return e;const s=i/t,a=Math.ceil(e.length*s),n=new Float32Array(a);for(let t=0;t<a;t++){const i=t/s,a=Math.floor(i),o=i-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 i=e.get(t,"content.function_result.response");void 0!==i&&(this.aiiaSubtitleSub.next(i),this.aiiaSubtitleClearSub.next())}break;case"asr":if(!this.isMuteAiia&&"ASR"===e.get(t,"content.function_type")){const i=e.get(t,"content.function_result.result");void 0!==i&&(this.userSubtitleSub.next(i),this.userSubtitleClearSub.next())}break;case"cv":{const i=e.get(t,"content.status");"user present"===i?this.guestSub.next(!0):"user leave"===i?this.guestSub.next(!1):d.warn("Unexpect value",e.get(t,"content"));break}default:d.debug(t.signal)}}channelCheck(){const{proxy:t,cloud:i}=this.channelState;if(t&&i&&this.media.state===b.allowed){this.chat.sendToCloud({request:"audio",command:"sampleRate",content:this.sampleRateOfWhisper.input}),this.media.pcm.pipe(s.takeUntil(this.destorySub)).subscribe(e=>{this.chat.sendToCloud({request:"audio",command:"pcm",content:g(e)})});const t=(a=this.config.faceDetection.confidence,function(t,i){const{width:s,height:n}=i,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,i=t.width*t.height,a=e.categories[0]?.score??0;return{...t,area:i,score:100*a,clientWidth:s,clientHeight:n}}return null}));return e.sortBy(r,["area","score"])[0]??null});this.media.detections.pipe(s.takeUntil(this.destorySub),s.map(({detections:e,clientHeight:i,clientWidth:s})=>t(e,{width:s,height:i}))).subscribe(e=>{this.chat.send("face",e)}),this.media.startRecord(),this.media.startCapture(),this.stateSub.next("InService"),d.code(c["通道、雲端服務、麥克風都已就緒"])}else t&&i&&this.media.state!==b.allowed&&(this.stateSub.next("InServiceNoMedia"),d.code(c["未取得媒體裝置權限,請重新設定或忽略此訊息"]));var a}onDestroy(){this.chat.onDestroy(),this.media.onDestroy(),this.destorySub.next(),this.destorySub.complete(),this.stateSub.next("Destroy"),this.stateSub.complete(),d.code(c["結束SDK"])}}exports.initSdk=function(t){if(o){!function(e){const{info:t,error:i,warn:s,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=()=>{}),s||(u.warn=()=>{}),i||(u.error=()=>{}),n||(u.fatal=()=>{})}(e.get(t,"debug",!0)),d.code(c["初始化必要依賴"]);const i=new r(e.assign({},t));d.code(c["指定依賴已完成"],"config");const s=new _(i.mediaStream);d.code(c["指定依賴已完成"],"config");const a=new v(i,s);return d.code(c["指定依賴已完成"],"sdk"),""!==i.worklet_url&&s.init(i.worklet_url,i),a}throw new Error(i["環境錯誤(browser)"])};
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)"])};
package/dist/browser.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Detection } from '@mediapipe/tasks-vision';
2
2
  import { Http2SecureServer } from 'http2';
3
3
  import { Http2Server } from 'http2';
4
+ import type { Landmark } from '@mediapipe/tasks-vision';
4
5
  import { Observable } from 'rxjs';
5
6
  import { Server } from 'http';
6
7
  import { Server as Server_2 } from 'https';
@@ -55,6 +56,7 @@ export declare namespace aiia_msg {
55
56
  LAYOUT,
56
57
  AUDIO,
57
58
  CV,
59
+ MovementToUE,
58
60
  CLOUD_SOCKET_MSG,
59
61
  Signal,
60
62
  SendMessage_MiniGame,
@@ -64,6 +66,8 @@ export declare namespace aiia_msg {
64
66
  SendMessage_Audio,
65
67
  SendMessage_Origin,
66
68
  SendMessage_Face,
69
+ LandmarkResult,
70
+ SendMessage_Pose,
67
71
  SendMessage
68
72
  }
69
73
  }
@@ -71,14 +75,14 @@ export declare namespace aiia_msg {
71
75
  declare class AiiaConfig {
72
76
  private config;
73
77
  get webserver(): (Server<any, any> | Server_2<any, any> | Http2SecureServer<any, any, any, any> | Http2Server<any, any, any, any>) | undefined;
74
- get environment(): "developement" | "production" | undefined;
78
+ get environment(): string;
75
79
  get license(): string;
76
80
  get port(): number;
77
81
  get ws_url(): string;
78
82
  get worklet_url(): string;
79
- get project(): Partial<AiiaProjectItem> & {
83
+ get project(): {
80
84
  specific: boolean;
81
- };
85
+ } & Partial<AiiaProjectItem>;
82
86
  get endPoint(): {
83
87
  api: string;
84
88
  socket: string;
@@ -87,7 +91,7 @@ declare class AiiaConfig {
87
91
  get mediaStream(): MediaStream | undefined;
88
92
  get chunkTimeInSeconds(): number;
89
93
  get llmSampleRate(): number;
90
- get faceDetection(): {
94
+ get detection(): {
91
95
  perSecond: number;
92
96
  confidence: number;
93
97
  };
@@ -101,18 +105,26 @@ declare class AiiaConfig {
101
105
  y_axis_px: number;
102
106
  };
103
107
  constructor(config: InitOptions);
108
+ private safeRead;
104
109
  }
105
110
 
106
- export declare interface AiiaProjectItem {
111
+ declare interface AiiaProjectItem {
107
112
  id: string;
108
113
  name: string;
109
114
  }
110
115
 
116
+ export declare interface AiiaProjectItemWithAvatar extends AiiaProjectItem {
117
+ avatar: {
118
+ name: string;
119
+ url: string;
120
+ };
121
+ }
122
+
111
123
  declare class AiiaSdk {
112
124
  private config;
113
125
  private media;
114
126
  private chat;
115
- private destorySub;
127
+ private destroySub;
116
128
  private projectsSub;
117
129
  private layoutSub;
118
130
  private aiiaSubtitleSub;
@@ -127,6 +139,8 @@ declare class AiiaSdk {
127
139
  private _lastProjects;
128
140
  private guestSub;
129
141
  private _hasGuest;
142
+ private mediaEventReady;
143
+ private isStart;
130
144
  /**
131
145
  * [zh]
132
146
  * 可訂閱,服務狀態
@@ -174,7 +188,7 @@ declare class AiiaSdk {
174
188
  * [en]
175
189
  * If no Project ID is explicitly specified, please subscribe to this object
176
190
  */
177
- get projects(): Observable<AiiaProjectItem[]>;
191
+ get projects(): Observable<AiiaProjectItemWithAvatar[]>;
178
192
  /**
179
193
  * [zh]
180
194
  * 可讀取最近一次收到的專案列表
@@ -183,6 +197,10 @@ declare class AiiaSdk {
183
197
  * You can read the list of projects received recently
184
198
  */
185
199
  get lastProjects(): {
200
+ avatar: {
201
+ name: string;
202
+ url: string;
203
+ };
186
204
  id: string;
187
205
  name: string;
188
206
  }[];
@@ -276,7 +294,7 @@ declare class AiiaSdk {
276
294
  * If no Project is explicitly specified, please execute this method.
277
295
  * @param project_id
278
296
  */
279
- chooseProject(project_id: AiiaProjectItem["id"]): void;
297
+ chooseProject(project_id: AiiaProjectItemWithAvatar["id"]): void;
280
298
  /**
281
299
  * [zh]
282
300
  * 不同於`this.volume = 0`的調整輸出音量
@@ -297,7 +315,6 @@ declare class AiiaSdk {
297
315
  * Restore audio signal data
298
316
  */
299
317
  unmute(): void;
300
- private cloudToObj;
301
318
  private behavior;
302
319
  private channelCheck;
303
320
  /**
@@ -310,7 +327,7 @@ declare class AiiaSdk {
310
327
  onDestroy(): void;
311
328
  }
312
329
 
313
- export declare type AiiaState = "NotStart" | "WattingProjectID" | "ConnectingCloud" | "ReconnectingCloud" | "InService" | "InServiceNoMedia" | "NoPermissions" | "CloseService" | "Destroy";
330
+ export declare type AiiaState = "NotStart" | "WaitingProjectID" | "ConnectingCloud" | "ReconnectingCloud" | "InService" | "InServiceNoMedia" | "NoPermissions" | "CloseService" | "Destroy";
314
331
 
315
332
  declare interface ASR {
316
333
  signal: "asr";
@@ -347,7 +364,7 @@ declare type AUDIO = {
347
364
 
348
365
  export declare type BrowserInitOptions = Omit<InitOptions, "webserver" | "port" | "license" | "eyeTrackEnable" | "correction">;
349
366
 
350
- declare type CLOUD_SOCKET_MSG = TTS | MIC | ASR | AGENT | OLD_ASR | OLD_AGENT | RATINGS | QUESTIONNAIRE | LAYOUT | AUDIO | CV;
367
+ declare type CLOUD_SOCKET_MSG = TTS | MIC | ASR | AGENT | OLD_ASR | OLD_AGENT | RATINGS | QUESTIONNAIRE | LAYOUT | AUDIO | CV | MovementToUE;
351
368
 
352
369
  declare interface CV {
353
370
  signal: "cv";
@@ -375,10 +392,10 @@ declare interface InitOptions {
375
392
  project?: string | Partial<AiiaProjectItem>;
376
393
  /**
377
394
  * [zh]
378
- * 指定的環境,預設值: production
395
+ * 指定的環境,預設值: developement
379
396
  *
380
397
  * [en]
381
- * Specific Env, default: production
398
+ * Specific Env, default: developement
382
399
  */
383
400
  env?: "developement" | "production";
384
401
  /**
@@ -450,15 +467,17 @@ declare interface InitOptions {
450
467
  */
451
468
  llmSampleRate?: number;
452
469
  /**
470
+ * @deprecated
471
+ *
453
472
  * [zh]
454
- * 人臉辨識
473
+ * 人臉辨識(已棄用,請改用 `cameraDetection`),同時設定會以 `cameraDetection` 為主
455
474
  *
456
475
  * @property perSecond: 每秒檢測幾次, 預設: 每秒 3 次
457
476
  *
458
477
  * @property confidence: 分數(0 ~ 100), 預設: 80
459
478
  *
460
479
  * [en]
461
- * facial recognition
480
+ * Face recognition (deprecated, please use `cameraDetection` instead), and the settings will be based on `cameraDetection`
462
481
  *
463
482
  * @property perSecond: Detection times per second, default: 3 times per second
464
483
  *
@@ -468,6 +487,25 @@ declare interface InitOptions {
468
487
  perSecond?: number;
469
488
  confidence?: number;
470
489
  };
490
+ /**
491
+ * [zh]
492
+ * 攝影機辨識設定
493
+ *
494
+ * @property perSecond: 每秒檢測幾次, 預設: 每秒 3 次
495
+ *
496
+ * @property confidence: 人臉辨識分數(0 ~ 100), 預設: 80
497
+ *
498
+ * [en]
499
+ * Camera Identification Settings
500
+ *
501
+ * @property perSecond: Detection times per second, default: 3 times per second
502
+ *
503
+ * @property confidence: Face recognition score(0 ~ 100), default: 80
504
+ */
505
+ cameraDetection?: {
506
+ perSecond?: number;
507
+ confidence?: number;
508
+ };
471
509
  /**
472
510
  * [zh]
473
511
  * 清除字幕,可指定時間(毫秒),預設 5000 毫秒後清除
@@ -507,6 +545,8 @@ declare interface InitOptions {
507
545
 
508
546
  export declare function initSdk(options?: BrowserInitOptions): AiiaSdk;
509
547
 
548
+ declare type LandmarkResult = Landmark[][];
549
+
510
550
  declare interface LAYOUT {
511
551
  signal: "layout";
512
552
  content: {
@@ -538,9 +578,10 @@ declare class MediaManager {
538
578
  private audioManager?;
539
579
  private vadManager?;
540
580
  private camManager?;
541
- private destory;
581
+ private destroy;
542
582
  private pcmSub;
543
583
  private detectionsSub;
584
+ private landmarksSub;
544
585
  get sampleRate(): number;
545
586
  get pcm(): Observable<Float32Array<ArrayBufferLike>>;
546
587
  get detections(): Observable< {
@@ -548,6 +589,7 @@ declare class MediaManager {
548
589
  clientWidth: number;
549
590
  clientHeight: number;
550
591
  }>;
592
+ get landmarks(): Observable<aiia_msg.LandmarkResult>;
551
593
  get state(): MediaStateEnum;
552
594
  set state(v: MediaStateEnum);
553
595
  get stateObs(): Observable<MediaStateEnum>;
@@ -555,7 +597,7 @@ declare class MediaManager {
555
597
  init(workletUrl: string, config?: {
556
598
  llmSampleRate?: AiiaConfig["llmSampleRate"];
557
599
  chunkTimeInSeconds?: AiiaConfig["chunkTimeInSeconds"];
558
- faceDetection?: AiiaConfig["faceDetection"];
600
+ cameraDetection?: AiiaConfig["detection"];
559
601
  }): Promise<void>;
560
602
  addAudioQueue(input: {
561
603
  buffer: AudioBuffer;
@@ -591,6 +633,15 @@ declare interface MIC {
591
633
  };
592
634
  }
593
635
 
636
+ declare interface MovementToUE {
637
+ signal: "osc";
638
+ content: {
639
+ host: string;
640
+ port: number;
641
+ message: [string, ...(string | number)[]];
642
+ };
643
+ }
644
+
594
645
  /** @deprecated */
595
646
  declare interface OLD_AGENT {
596
647
  signal: "response";
@@ -643,7 +694,7 @@ declare type SendMessage = {
643
694
  request: "reset";
644
695
  } | {
645
696
  request: "skip";
646
- } | SendMessage_Layout | SendMessage_MiniGame | SendMessage_Audio | SendMessage_Face;
697
+ } | SendMessage_Layout | SendMessage_MiniGame | SendMessage_Audio | SendMessage_Face | SendMessage_Pose;
647
698
 
648
699
  declare type SendMessage_Audio = SendMessage_Audio_Init | SendMessage_Audio_PCM;
649
700
 
@@ -691,6 +742,11 @@ declare interface SendMessage_Origin {
691
742
  content?: any;
692
743
  }
693
744
 
745
+ declare interface SendMessage_Pose {
746
+ request: "pose_detect";
747
+ content: LandmarkResult;
748
+ }
749
+
694
750
  declare type Signal = CLOUD_SOCKET_MSG["signal"];
695
751
 
696
752
  declare interface TTS {
package/dist/browser.mjs CHANGED
@@ -1 +1 @@
1
- import{assign as e,forEach as t,filter as i,compact as s,map as a,sortBy as o,get as n,find as r,isArray as c,values as u}from"lodash-es";import h from"moment";import"mathjs";import{Subject as d,takeUntil as l,fromEvent as p,take as b,ReplaySubject as m,BehaviorSubject as g,debounceTime as f,map as S}from"rxjs";import{FilesetResolver as y,FaceDetector as w}from"@mediapipe/tasks-vision";import{io as _}from"socket.io-client";var C;!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"}(C||(C={}));const v="undefined"!=typeof window;"undefined"!=typeof process&&null!=process.versions&&process.versions.node;class x{config;get webserver(){return this.config.webserver}get environment(){return this.config.env}get license(){return this.config.license??""}get port(){return function(e,t){const i=t??NaN;if(e){const t=parseInt(e);return isNaN(t)?i:t}return i}(process.env.PORT,this.config.port??3e3)}get ws_url(){return this.config.ws_url??""}get worklet_url(){return this.config.worklet_url??""}get project(){const{project:t}=e({},this.config);return void 0===t?{specific:!1}:"string"==typeof t?{name:t,id:t,specific:!1}:e({},t,{specific:!0})}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}return{api:`https://aiia-content-management-${e}-21193779403.asia-east1.run.app`,socket:`wss://graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`}}get debug(){return this.config.debug??!0}get mediaStream(){return this.config.mediaStream}get chunkTimeInSeconds(){return"number"==typeof this.config.chunkTimeInSeconds&&this.config.chunkTimeInSeconds>0?this.config.chunkTimeInSeconds:.3}get llmSampleRate(){return"number"==typeof this.config.llmSampleRate&&this.config.llmSampleRate>0?this.config.llmSampleRate:16e3}get faceDetection(){return e({perSecond:3,confidence:80},this.config.faceDetection)}get autoClearSubtitle(){return{userDelayTime:this.config.autoClearUserSubtitle??5e3,aiiaDelayTime:this.config.autoClearAiiaSubtitle??NaN}}get eyeTrackEnable(){return this.config.eyeTrackEnable??!1}get eyeTrackCorrection(){return e({x_axis_px:0,y_axis_px:0},this.config.correction)}constructor(e){this.config=e}}var E;!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["安全的關閉連線"]="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"}(E||(E={}));const R={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},D=[];const k=new Proxy(R,{get(e,i,s){if("string"==typeof i)switch(i){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...s)=>{const a=h().format("yyyy-MM-DD[T]HH:mm:ss");t(D,e=>{e.next(i,a,...s)}),e[i](`[Aiia::${i}_${a}_]`,...s)};default:return}},set:(e,t,i,s)=>!0});var O,N;function M(e,t,i){return Math.max(i,Math.min(e,t))}function T(e){const t=new DataView(e.buffer),i=[];for(let s=0;s<e.length;s++){const e=t.getInt16(2*s,!0)/32768;i.push(e)}return new Float32Array(i)}function j(e){const t=new DataView(e.buffer),i=[];for(let s=0;s<e.length;s++){const e=M(32768*t.getFloat32(4*s,!0),32767,-32768);i.push(e)}return i}!function(e){e[e.unknow=0]="unknow",e[e.allowed=1]="allowed",e[e.rejected=2]="rejected"}(O||(O={}));class A{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 i=e.length,s=this.audioContext.sampleRate,a=this.audioContext.createBuffer(t,i,s),o=e.slice();for(let e=0;e<t;e++)a.copyToChannel(o,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=>{k.error("pause speech fail",e)})}resumeSpeech(){this.audioContext.resume().catch(e=>{k.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,i){this.stream=e,this.audioContext=t,this.outputSampleRate=i?.llmSampleRate??16e3,this.chunkTimeInSeconds=i?.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 F{type="camera";aiiaCamera;get detections(){return this.aiiaCamera.detections}get videoSize(){return this.aiiaCamera.videoSize}constructor(t){const{perSecond:i}=e({perSecond:3},t);this.aiiaCamera=new W,this.aiiaCamera.setFrequency(i),document.body.append(this.aiiaCamera)}async init(e){try{const t=await y.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.0/wasm"),i=await 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"});this.aiiaCamera.setStream(e),this.aiiaCamera.setFaceDetector(i)}catch(e){k.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;_isDebug;_debugElements;_interval;_lastTimestamp;_loopFrameID;_faceDetector;_detectionsSub;_isReady;get detections(){return this._detectionsSub.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._debugElements=[],this._loopFrameID=null,this._detectionsSub=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)}connectedCallback(){this.startCapture()}disconnectedCallback(){this.stopCapture()}attributeChangedCallback(e,t,i){"debug"===e&&(this._isDebug=null!==i&&("true"===i||""===i),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}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 .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;const e=performance.now();if(this._lastTimestamp+this._interval<=e){this._lastTimestamp=e;const t=this._faceDetector.detectForVideo(this._videoEl,e).detections;this._isDebug&&this._drawRect(t),this._detectionsSub.next(t)}}_drawRect(e){t(e,(e,t)=>{const{categories:i,boundingBox:s}=e;if(s){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 o=100*i[0].score;a.innerText=`Confidence: ${Math.round(o)} %`,a.style.right=this._videoEl.offsetWidth-s.width-s.originX+"px",a.style.top=s.originY-30+"px",a.style.width=s.width-10+"px",e.style.right=this._videoEl.offsetWidth-s.width-s.originX+"px",e.style.top=`${s.originY}px`,e.style.width=s.width-10+"px",e.style.height=`${s.height}px`}});for(let t=2*e.length;t<this._debugElements.length;t++)this._debugElements[t].style.display="none"}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 B{stream;_state;stateSub;audioCtx;audioManager;vadManager;camManager;destory;pcmSub;detectionsSub;get sampleRate(){return this.audioCtx?this.audioCtx.sampleRate:16e3}get pcm(){return this.pcmSub.asObservable()}get detections(){return this.detectionsSub.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,this.pcmSub=new d,this.destory=new d,this.detectionsSub=new d,this.state=N.padding}async init(e,t){const i=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===i)throw this.state=N.rejected,new Error(C["未獲得媒體裝置權限"]);this.stream=i,this.audioCtx=new AudioContext;try{await this.audioCtx.audioWorklet.addModule(e)}catch(e){throw k.fatal(e),this.state=N.loadfail,new Error(C["Worklet模組載入失敗"])}this.state=N.allowed,this.audioManager=new A(this.audioCtx),this.vadManager=new I(i,this.audioCtx,t),this.vadManager.pcm.pipe(l(this.destory)).subscribe(e=>{this.pcmSub.next(e)});const s=new F(t?.faceDetection);s.detections.pipe(l(this.destory)).subscribe(e=>{const{width:t,height:i}=s.videoSize;this.detectionsSub.next({detections:e,clientWidth:t,clientHeight:i})}),s.init(i),this.camManager=s}addAudioQueue(e){const t=n(e,"buffer",void 0),i=n(e,"float32",void 0),s=n(e,"int16",void 0),a=n(e,"numberOfChannels",void 0);t?this.audioManager?.addBuffer(t):i?this.audioManager?.addBufferByFloat32(i,a):s&&this.audioManager?.addBufferByInt16(s,a)}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.destory.next(),this.destory.complete()}}class P{socket;messageSub;destorySub;get message(){return this.messageSub.asObservable()}constructor(t){this.destorySub=new d,this.messageSub=new d;const{specific:i,id:s}=t.project;this.socket=_(t.ws_url,e({autoConnect:!1},i&&void 0!==s?{query:{uuid:s}}:{}))}start(){const e="list",t="cloud",i="channel";p(this.socket,"connect").pipe(l(this.destorySub)).subscribe(()=>{k.debug("Client ID: ",this.socket.id)}),p(this.socket,e).pipe(b(1),l(this.destorySub)).subscribe(t=>{this.messageSub.next({event:e,data:t})}),p(this.socket,t).pipe(l(this.destorySub)).subscribe(e=>{this.messageSub.next({event:t,data:e})}),p(this.socket,i).pipe(l(this.destorySub)).subscribe(e=>{this.messageSub.next({event:i,data:e})}),this.socket.connect()}send(e,t){this.socket.emit(e,t)}sendToCloud(e){this.send("cloud",JSON.stringify(e))}onDestroy(){this.destorySub.next(),this.destorySub.complete(),this.messageSub.complete(),this.socket.disconnect()}}class V{config;media;chat;destorySub;projectsSub;layoutSub;aiiaSubtitleSub;userSubtitleSub;sampleRateOfWhisper;channelState;stateSub;_currentState;isMuteAiia;aiiaSubtitleClearSub;userSubtitleClearSub;_lastProjects;guestSub;_hasGuest;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 a(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 d,this.projectsSub=new m(1),this.destorySub=new d,this.aiiaSubtitleSub=new d,this.userSubtitleSub=new d,this.aiiaSubtitleClearSub=new d,this.userSubtitleClearSub=new d,this.stateSub=new g("NotStart"),this.sampleRateOfWhisper={input:e.llmSampleRate,output:16e3},this.channelState={proxy:!1,cloud:!1},this.isMuteAiia=!1,this._lastProjects=[],this.guestSub=new m(1),this._hasGuest=!1,this.chat=new P(e),k.code(E["指定依賴已完成"],"chat"),this.close=this.onDestroy,this.behavior=this.behavior.bind(this),this.cloudToObj=this.cloudToObj.bind(this);const{userDelayTime:i,aiiaDelayTime:s}=e.autoClearSubtitle;isNaN(s)||this.aiiaSubtitleClearSub.pipe(f(s),l(this.destorySub)).subscribe(()=>{this.aiiaSubtitleSub.next("")}),isNaN(i)||this.userSubtitleClearSub.pipe(f(i),l(this.destorySub)).subscribe(()=>{this.userSubtitleSub.next("")}),this.stateSub.pipe(l(this.destorySub)).subscribe(e=>{this._currentState=e}),this.projectsSub.pipe(l(this.destorySub)).subscribe(e=>{this._lastProjects=e}),this.guestSub.pipe(l(this.destorySub)).subscribe(e=>{this._hasGuest=e})}start(){this.media.stateObs.pipe(l(this.destorySub)).subscribe(()=>{this.channelCheck()}),this.chat.message.pipe(l(this.destorySub)).subscribe(({event:e,data:t})=>{switch(e){case"cloud":this.cloudToObj(t);break;case"list":{const{id:e,name:i,specific:s}=this.config.project;let a=null;if(s&&void 0!==e)a=e;else{const s=r(t,t=>t.name===i||t.id===e);s&&(a=s.id)}null!==a?this.chooseProject(a):(this.projectsSub.next(t),this.stateSub.next("WattingProjectID"),k.code(E["請訂閱專案列表"]));break}case"channel":switch(t){case"open":this.channelState.proxy=!0,this.channelCheck();break;case"close":this.channelState.proxy=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("CloseService");break;case"no_permissions":this.channelState.proxy=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("NoPermissions");break;case"reconnect":this.channelState.proxy=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("ReconnectingCloud")}}}),this.chat.start(),this.stateSub.next("ConnectingCloud"),k.code(E["啟動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()}cloudToObj(e){try{const t=JSON.parse(e);void 0!==n(t,"signal")?this.behavior(t):c(t)?a(t,this.behavior):a(u(t),this.behavior)}catch(e){k.error("message transfer fail",e)}}behavior(e){switch(e.signal){case"layout":this.layoutSub.next(e.content);break;case"audio":if("sampleRate"===e.command)this.sampleRateOfWhisper.output=e.content;else switch(e.command){case"pcm":if(!this.isMuteAiia){const t=function(e,t,i){if(t===i)return e;const s=i/t,a=Math.ceil(e.length*s),o=new Float32Array(a);for(let t=0;t<a;t++){const i=t/s,a=Math.floor(i),n=i-a,r=e[a],c=e[Math.min(a+1,e.length-1)];o[t]=r+(c-r)*n}return o}(T(new Int16Array(e.content)),this.sampleRateOfWhisper.output,this.media.sampleRate);this.media.addAudioQueue({float32:t}),this.media.playAudio()}break;case"interrupted":this.media.stopAudio(),this.aiiaSubtitleSub.next("")}break;case"status":"Connected"===n(e,"content")&&(this.channelState.cloud=!0,this.channelCheck(),this.layoutSub.next({type:"resetLayout"}));break;case"agent":if(!this.isMuteAiia){const t=n(e,"content.function_result.response");void 0!==t&&(this.aiiaSubtitleSub.next(t),this.aiiaSubtitleClearSub.next())}break;case"asr":if(!this.isMuteAiia&&"ASR"===n(e,"content.function_type")){const t=n(e,"content.function_result.result");void 0!==t&&(this.userSubtitleSub.next(t),this.userSubtitleClearSub.next())}break;case"cv":{const t=n(e,"content.status");"user present"===t?this.guestSub.next(!0):"user leave"===t?this.guestSub.next(!1):k.warn("Unexpect value",n(e,"content"));break}default:k.debug(e.signal)}}channelCheck(){const{proxy:e,cloud:t}=this.channelState;if(e&&t&&this.media.state===N.allowed){this.chat.sendToCloud({request:"audio",command:"sampleRate",content:this.sampleRateOfWhisper.input}),this.media.pcm.pipe(l(this.destorySub)).subscribe(e=>{this.chat.sendToCloud({request:"audio",command:"pcm",content:j(e)})});const e=(n=this.config.faceDetection.confidence,function(e,t){const{width:r,height:c}=t,u=i(e,e=>100*(e.categories[0]?.score??0)>=n),h=s(a(u,e=>{if(void 0!==e.boundingBox){const t=e.boundingBox,i=t.width*t.height,s=e.categories[0]?.score??0;return{...t,area:i,score:100*s,clientWidth:r,clientHeight:c}}return null}));return o(h,["area","score"])[0]??null});this.media.detections.pipe(l(this.destorySub),S(({detections:t,clientHeight:i,clientWidth:s})=>e(t,{width:s,height:i}))).subscribe(e=>{this.chat.send("face",e)}),this.media.startRecord(),this.media.startCapture(),this.stateSub.next("InService"),k.code(E["通道、雲端服務、麥克風都已就緒"])}else e&&t&&this.media.state!==N.allowed&&(this.stateSub.next("InServiceNoMedia"),k.code(E["未取得媒體裝置權限,請重新設定或忽略此訊息"]));var n}onDestroy(){this.chat.onDestroy(),this.media.onDestroy(),this.destorySub.next(),this.destorySub.complete(),this.stateSub.next("Destroy"),this.stateSub.complete(),k.code(E["結束SDK"])}}function q(t){if(v){!function(e){const{info:t,error:i,warn:s,debug:a,fatal:o,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);n||(R.code=()=>{}),a||(R.debug=()=>{}),t||(R.info=()=>{}),s||(R.warn=()=>{}),i||(R.error=()=>{}),o||(R.fatal=()=>{})}(n(t,"debug",!0)),k.code(E["初始化必要依賴"]);const i=new x(e({},t));k.code(E["指定依賴已完成"],"config");const s=new B(i.mediaStream);k.code(E["指定依賴已完成"],"config");const a=new V(i,s);return k.code(E["指定依賴已完成"],"sdk"),""!==i.worklet_url&&s.init(i.worklet_url,i),a}throw new Error(C["環境錯誤(browser)"])}export{q 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,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};
package/dist/node.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var e,t=require("lodash-es"),s=require("moment"),o=require("mathjs"),i=require("socket.io"),r=require("rxjs"),n=require("axios"),c=require("ws"),a=require("fs"),h=require("os"),l=require("path"),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"}(e||(e={}));const d="access_token",S="undefined"!=typeof process&&null!=process.versions&&null!=process.versions.node;class b{config;get webserver(){return this.config.webserver}get environment(){return this.config.env}get license(){return this.config.license??""}get port(){return function(e,t){const s=t??NaN;if(e){const t=parseInt(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.config.port??3e3)}get ws_url(){return this.config.ws_url??""}get worklet_url(){return this.config.worklet_url??""}get project(){const{project:e}=t.assign({},this.config);return void 0===e?{specific:!1}:"string"==typeof e?{name:e,id:e,specific:!1}:t.assign({},e,{specific:!0})}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}return{api:`https://aiia-content-management-${e}-21193779403.asia-east1.run.app`,socket:`wss://graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`}}get debug(){return this.config.debug??!0}get mediaStream(){return this.config.mediaStream}get chunkTimeInSeconds(){return"number"==typeof this.config.chunkTimeInSeconds&&this.config.chunkTimeInSeconds>0?this.config.chunkTimeInSeconds:.3}get llmSampleRate(){return"number"==typeof this.config.llmSampleRate&&this.config.llmSampleRate>0?this.config.llmSampleRate:16e3}get faceDetection(){return t.assign({perSecond:3,confidence:80},this.config.faceDetection)}get autoClearSubtitle(){return{userDelayTime:this.config.autoClearUserSubtitle??5e3,aiiaDelayTime:this.config.autoClearAiiaSubtitle??NaN}}get eyeTrackEnable(){return this.config.eyeTrackEnable??!1}get eyeTrackCorrection(){return t.assign({x_axis_px:0,y_axis_px:0},this.config.correction)}constructor(e){this.config=e}}var p;!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["安全的關閉連線"]="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"}(p||(p={}));const _={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},g=[];function E(e){return g.push(e),E}const f=new Proxy(_,{get(e,o,i){if("string"==typeof o)switch(o){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...i)=>{const r=s().format("yyyy-MM-DD[T]HH:mm:ss");t.forEach(g,e=>{e.next(o,r,...i)}),e[o](`[Aiia::${o}_${r}_]`,...i)};default:return}},set:(e,t,s,o)=>!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 r.Subject}start(){if(this.server=new i.Server(this.webserver,{cors:{origin:"*"}}),this.server.on("connection",e=>{this.memberSub.next({type:"in",socket:e}),f.debug("client on",e.id),e.on("disconnect",t=>{this.memberSub.next({type:"out",socket:e}),f.debug("client off",t)})}),void 0===this.webserver){const e=this.port;this.server.listen(e),f.info("Websocket server run on",e)}else f.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(s,o){const i={license_number:s,token_value:o??""},r=await this.instance.post("/api/v1/license/agent_token",i).then(e=>t.get(e.data,"token_value","")).catch(()=>null);if(null===r)throw new Error(e["網絡錯誤"]);if(""===r)throw new Error(e["獲取權杖失敗"]);return r}async getProjectList(s){const o=await this.instance.post("/api/v1/license/projects",{license_number:s}).then(e=>t.map(t.get(e,"data.projects",[]),({id:e,sub_project_name:t})=>({id:e,name:t}))).catch(e=>null);if(null===o)throw new Error(e["網絡錯誤"]);if(0===o.length)throw new Error(e["沒有可用專案"]);return o}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.MANUAL_CLOSE=4002]="MANUAL_CLOSE",e[e.WS_5000_NORAML_CLOSURE=4100]="WS_5000_NORAML_CLOSURE",e[e.WS_5001_UNAUTHORIZED=4101]="WS_5001_UNAUTHORIZED",e[e.WS_5002_LICENSE_EXPIRED=4102]="WS_5002_LICENSE_EXPIRED",e[e.WS_5003_TOKEN_EXPIRED=4103]="WS_5003_TOKEN_EXPIRED"}(O||(O={}));class D{socket;messageSub;linkStartSub;destorySub;closeSub;get message(){return this.messageSub.asObservable()}get close(){return this.closeSub.asObservable()}get linkStart(){return this.linkStartSub.asObservable()}isConnected;constructor(){this.isConnected=!1,this.messageSub=new r.Subject,this.linkStartSub=new r.Subject,this.closeSub=new r.Subject,this.destorySub=new r.Subject}start(e){this.socket=new c(e,{autoPong:!0}),r.fromEvent(this.socket,"open").pipe(r.takeUntil(this.destorySub)).subscribe(()=>{this.isConnected=!0,f.code(p["啟動服務"],"proxy"),this.linkStartSub.next()}),r.fromEvent(this.socket,"message").pipe(r.takeUntil(this.destorySub)).subscribe(e=>{const{data:t}=e;this.messageSub.next(t)}),r.fromEvent(this.socket,"error").pipe(r.takeUntil(this.destorySub)).subscribe(e=>{const{error:t}=e;f.error("Proxy has some problem",t)}),r.fromEvent(this.socket,"close").pipe(r.takeUntil(this.destorySub)).subscribe(e=>{this.isConnected=!1;const{code:t,reason:s,type:o,wasClean:i}=e;switch(t){case O.WHEN_BROWSER_CLOSE:f.code(p["無副作用的關閉連線"]);break;case O.MDN_NORMAL_CLOSURE:case O.MDN_GOING_AWAY:this.closeSub.next("close"),f.code(p["安全的關閉連線"]);break;case O.MDN_NO_STATUS_RECEIVED:case O.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),f.code(p["與雲端的服務中斷"],{code:t,reason:s,type:o,wasClean:i});break;case O.WS_5001_UNAUTHORIZED:case O.WS_5002_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),f.code(p["沒有授權,請聯繫Graphen"]);break;case O.WS_5000_NORAML_CLOSURE:case O.WS_5003_TOKEN_EXPIRED:this.closeSub.next("reconnect"),f.code(p["自動重新連線連端服務"]);break;default:this.closeSub.next("close"),f.code(p["未預期的斷線,請聯繫Graphen"],{code:t,reason:s,type:o,wasClean:i})}})}reconnect(e){this.onDestroy(),this.destorySub=new r.Subject,this.start(e)}send(e){this.isConnected&&void 0!==this.socket&&this.socket.send(e,e=>{e&&f.error("send message fail",e?.message,e)})}sendToCloud(e){this.send(JSON.stringify(e))}onClose(){this.socket?.close(O.WHEN_BROWSER_CLOSE)}onDestroy(){this.destorySub.next(),this.destorySub.complete()}}const N=new class{AppName;platform;isMAC;isWIN;isLINUX;saveFolder;homedir;appFolder;EOL;constructor(e){switch(this.AppName=e,this.platform=process.platform,this.homedir=h.homedir(),this.EOL=h.EOL,this.platform){case"darwin":this.isMAC=!0,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=l.join(this.homedir,"Library","Application Support");break;case"win32":this.isMAC=!1,this.isWIN=!0,this.isLINUX=!1,this.saveFolder=process.env.APPDATA||l.join(this.homedir,"AppData","Roaming");break;case"linux":this.isMAC=!1,this.isWIN=!1,this.isLINUX=!0,this.saveFolder=process.env.XDG_CONFIG_HOME||l.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=l.join(this.saveFolder,this.AppName),this.folderCheck(this.appFolder)}addPath(...e){return l.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=N.addPath(N.appFolder,this.fileName),N.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(e){const s=await this.getFileData(),o=t.omit(s,e);await this.setFileData(o)}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 s=t.omit(this.getFileDataSync(),e);this.setFileDataSync(s)}clearSync(){this.setFileDataSync({})}}class R{config;constructor(e){this.config=e,f.info("UEProxy, config=",e)}start(){f.info("UEProxy, start proxy")}send(e){let t=this.cloudToObj(e);if(t&&t.signal&&"osc"===t.signal){f.info("UEProxy, payload=",t);try{let e=t.content;const s=new u.Client(e.host,e.port);f.debug("UEProxy, client open to port "+e.port),s.send(e.message,e=>{e&&f.error(e),s.close(),f.debug("UEProxy, client closed")})}catch(e){f.error(e)}}}cloudToObj(e){try{return JSON.parse(e)}catch(e){f.error("message transfer fail",e)}}onDestroy(){f.info("UEProxy, destroy proxy")}}class C{socket;messageSub;linkStartSub;destorySub;closeSub;get message(){return this.messageSub.asObservable()}get close(){return this.closeSub.asObservable()}get linkStart(){return this.linkStartSub.asObservable()}messageQueue=[];isConnected;constructor(){this.isConnected=!1,this.messageSub=new r.Subject,this.linkStartSub=new r.Subject,this.closeSub=new r.Subject,this.destorySub=new r.Subject}url="ws://localhost:8080";start(){this.socket=new c(this.url,{autoPong:!0}),r.fromEvent(this.socket,"open").pipe(r.takeUntil(this.destorySub)).subscribe(()=>{this.isConnected=!0,f.code(p["啟動服務"],"proxy"),this.flushMessageQueue(),this.linkStartSub.next()}),r.fromEvent(this.socket,"message").pipe(r.takeUntil(this.destorySub)).subscribe(e=>{const{data:t}=e;this.messageSub.next(t)}),r.fromEvent(this.socket,"error").pipe(r.takeUntil(this.destorySub)).subscribe(e=>{const{error:t}=e;f.error("Proxy has some problem",t)}),r.fromEvent(this.socket,"close").pipe(r.takeUntil(this.destorySub)).subscribe(e=>{this.isConnected=!1;const{code:t,reason:s}=e;switch(t){case O.MANUAL_CLOSE:f.code(p["無副作用的關閉連線"]);break;case O.WS_5000_NORAML_CLOSURE:case O.MDN_NORMAL_CLOSURE:case O.MDN_GOING_AWAY:this.closeSub.next("close"),f.code(p["安全的關閉連線"]);break;case O.MDN_NO_STATUS_RECEIVED:case O.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),f.code(p["與雲端的服務中斷"],{code:t,reason:s});break;case O.WS_5001_UNAUTHORIZED:case O.WS_5002_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),f.code(p["沒有授權,請聯繫Graphen"]);break;case O.WS_5003_TOKEN_EXPIRED:this.closeSub.next("reconnect"),f.code(p["自動重新連線連端服務"]);break;default:this.closeSub.next("close"),f.code(p["未預期的斷線,請聯繫Graphen"],{code:t,reason:s})}})}reconnect(){this.onDestroy(),this.destorySub=new r.Subject,this.start()}flushMessageQueue(){for(;this.messageQueue.length>0&&this.isConnected&&void 0!==this.socket;){const e=this.messageQueue.shift();e&&this.socket.send(e)}}send(e){let t=this.cloudToObj(e);if(t&&t.signal&&"audio"===t.signal&&"pcm"===t.command&&(f.info("AudioSocketProxy, payload=",t),this.isConnected&&void 0!==this.socket))try{const e=t.content,s=Buffer.allocUnsafe(2*e.length);for(let t=0;t<e.length;t++)s.writeInt16LE(e[t],2*t);this.socket.send(s)}catch(e){f.error(e)}}cloudToObj(e){try{return JSON.parse(e)}catch(e){f.error("message transfer fail",e)}}onClose(){this.socket?.close(O.MANUAL_CLOSE)}onDestroy(){this.destorySub.next(),this.destorySub.complete()}}class A{socket;messageSub;linkStartSub;destorySub;closeSub;get message(){return this.messageSub.asObservable()}get close(){return this.closeSub.asObservable()}get linkStart(){return this.linkStartSub.asObservable()}messageQueue=[];isConnected;constructor(){this.isConnected=!1,this.messageSub=new r.Subject,this.linkStartSub=new r.Subject,this.closeSub=new r.Subject,this.destorySub=new r.Subject}url="ws://localhost:8081";start(){this.socket=new c(this.url,{autoPong:!0}),r.fromEvent(this.socket,"open").pipe(r.takeUntil(this.destorySub)).subscribe(()=>{this.isConnected=!0,f.code(p["啟動服務"],"proxy"),this.flushMessageQueue(),this.linkStartSub.next()}),r.fromEvent(this.socket,"message").pipe(r.takeUntil(this.destorySub)).subscribe(e=>{const{data:t}=e;this.messageSub.next(t)}),r.fromEvent(this.socket,"error").pipe(r.takeUntil(this.destorySub)).subscribe(e=>{const{error:t}=e;f.error("Proxy has some problem",t)}),r.fromEvent(this.socket,"close").pipe(r.takeUntil(this.destorySub)).subscribe(e=>{this.isConnected=!1;const{code:t,reason:s}=e;switch(t){case O.MANUAL_CLOSE:f.code(p["無副作用的關閉連線"]);break;case O.WS_5000_NORAML_CLOSURE:case O.MDN_NORMAL_CLOSURE:case O.MDN_GOING_AWAY:this.closeSub.next("close"),f.code(p["安全的關閉連線"]);break;case O.MDN_NO_STATUS_RECEIVED:case O.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),f.code(p["與雲端的服務中斷"],{code:t,reason:s});break;case O.WS_5001_UNAUTHORIZED:case O.WS_5002_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),f.code(p["沒有授權,請聯繫Graphen"]);break;case O.WS_5003_TOKEN_EXPIRED:this.closeSub.next("reconnect"),f.code(p["自動重新連線連端服務"]);break;default:this.closeSub.next("close"),f.code(p["未預期的斷線,請聯繫Graphen"],{code:t,reason:s})}})}reconnect(){this.onDestroy(),this.destorySub=new r.Subject,this.start()}flushMessageQueue(){for(;this.messageQueue.length>0&&this.isConnected&&void 0!==this.socket;){const e=this.messageQueue.shift();e&&this.socket.send(e)}}send(e){let t=this.cloudToObj(e);if(t&&t.signal&&"audio"===t.signal&&"interrupted"===t.command&&(f.info("AudioInterruptSocketProxy, payload=",t),this.isConnected&&void 0!==this.socket))try{this.socket.send(t.command)}catch(e){f.error(e)}}cloudToObj(e){try{return JSON.parse(e)}catch(e){f.error("message transfer fail",e)}}onClose(){this.socket?.close(O.MANUAL_CLOSE)}onDestroy(){this.destorySub.next(),this.destorySub.complete()}}class w{next(e,t,...o){const i=`log_${s().format("yyyy-MM-DD")}.txt`,r=N.addPath(N.appFolder,i);try{const s=o.reduce((e,t)=>{switch(typeof t){case"string":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(r,`LOG(${e})__${t}__::${s}${N.EOL}`,{encoding:"utf8"})}catch(e){}}}class x{config;stroage;apiProxy;chat;udpProxy;audioProxy;audioIntrProxy;isStart;channelMap;destorySub;access_token;eyeBallUdp;get accessToken(){return this.access_token}constructor(e,t){this.config=e,this.stroage=t,this.isStart=!1,this.destorySub=new r.Subject,this.channelMap=new Map,this.access_token=t.getItemSync(d),this.apiProxy=new m(this.config.endPoint.api),f.code(p["指定模組初始化完成"],"api"),this.chat=new y(this.config),f.code(p["指定模組初始化完成"],"chat"),this.udpProxy=new R(this.config),f.code(p["指定模組初始化完成"],"udp"),this.audioProxy=new C,f.code(p["指定模組初始化完成"],"audio"),this.audioIntrProxy=new A,f.code(p["指定模組初始化完成"],"audio intr"),e.eyeTrackEnable&&(this.eyeBallUdp=new u.Client("127.0.0.1",6745),f.code(p["指定模組初始化完成"],"eye ball track")),this.openChannel=this.openChannel.bind(this)}async start(){if(this.isStart)throw new Error(e["重複初始化"]);this.isStart=!0,await this.replaceAccessToken(),this.chat.memberChange.pipe(r.takeUntil(this.destorySub)).subscribe(({type:e,socket:s})=>{switch(e){case"in":{const e=new D,i=new r.Subject,n=(o=s.handshake.query.uuid,t.isArray(o)?t.get(o,"[0]",void 0):o??void 0);void 0===n?(this.getProjectList().then(e=>{s.emit("list",e)}),r.fromEvent(s,"uuid").pipe(r.take(1),r.takeUntil(i)).subscribe(t=>{this.openChannel(t,s,e,i)})):this.openChannel(n,s,e,i),this.channelMap.set(s.id,()=>{i.next(),i.complete()});break}case"out":this.channelMap.has(s.id)&&(this.channelMap.get(s.id)(),this.channelMap.delete(s.id))}var o}),this.chat.start(),f.code(p["啟動服務"],"chat"),this.udpProxy.start(),f.code(p["啟動服務"],"udp")}async replaceAccessToken(){const e=await this.apiProxy.getAccessToken(this.config.license,this.access_token);return this.access_token=e,this.stroage.setItemSync(d,e),e}async getProjectList(){return await this.apiProxy.getProjectList(this.config.license)}getProxySocketURL(e){return`${this.config.endPoint.socket}/v1/chat/project/${this.accessToken}/${e}`}openChannel(e,s,i,n){r.fromEvent(s,"cloud").pipe(r.takeUntil(n)).subscribe(e=>{i.send(e)});const{x_axis_px:c,y_axis_px:a}=this.config.eyeTrackCorrection;r.fromEvent(s,"face").pipe(r.takeUntil(n)).subscribe(e=>{const s=e,r=null!==s;if(r&&void 0!==this.eyeBallUdp){const{x:e,y:i}=function(e,s){const{maxX:i,maxY:r,minX:n,minY:c}=t.assign({maxX:25,minX:-25,maxY:50,minY:-50},s),a=o.abs(i)+o.abs(n),h=o.abs(r)+o.abs(c),{clientHeight:l,clientWidth:u,originX:d,originY:S,width:b,height:p}=e,_=o.matrix([[a/u,0,n],[0,h/l,c],[0,0,1]]),g=d+b/2,E=l-(S+p/2),f=g>u?u:g<0?0:g,y=E>l?l:E<0?0:E,m=o.matrix([[f],[y],[1]]),O=o.multiply(_,m);return{x:O.get([0,0]),y:O.get([1,0])}}(s);this.eyeBallUdp.send(["/",e+c,i+a],e=>{null!=e&&f.error("eye track error:",e)})}i.sendToCloud({request:"face_detect",content:r})}),i.message.pipe(r.takeUntil(n)).subscribe(e=>{s.emit("cloud",e),this.udpProxy.send(e),this.audioProxy.send(e),this.audioIntrProxy.send(e)}),i.close.pipe(r.takeUntil(n)).subscribe(async t=>{switch(s.emit("channel",t),t){case"close":case"no_permissions":i.onDestroy();break;case"reconnect":await this.replaceAccessToken(),i.reconnect(this.getProxySocketURL(e))}}),i.linkStart.pipe(r.takeUntil(n)).subscribe(()=>{s.emit("channel","open")}),n.subscribe(()=>{i.onClose(),this.audioProxy.onClose(),this.audioIntrProxy.onClose()}),i.start(this.getProxySocketURL(e)),this.audioProxy.start(),this.audioIntrProxy.start()}onDestroy(){this.apiProxy.onDestroy(),this.chat.onDestroy(),this.udpProxy.onDestroy(),this.destorySub.next(),this.destorySub.complete(),this.eyeBallUdp?.close(),f.code(p["服務結束"])}}exports.aiiaCore=function(s){if(S){if(void 0===t.get(s,"license",void 0))throw new Error(e["未提供憑證"]);!function(e){const{info:t,error:s,warn:o,debug:i,fatal:r,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||(_.code=()=>{}),i||(_.debug=()=>{}),t||(_.info=()=>{}),o||(_.warn=()=>{}),s||(_.error=()=>{}),r||(_.fatal=()=>{}),E}(t.get(s,"debug",!0))(new w),f.code(p["開始初始化必要模組"]);const o=new b(t.assign({},s));f.code(p["指定模組初始化完成"],"config");const i=new k;f.code(p["指定模組初始化完成"],"storage");const r=new x(o,i);return f.code(p["指定模組初始化完成"],"core"),r.start(),f.code(p["啟動服務"]),()=>{r.onDestroy()}}throw new Error(e["環境錯誤(nodejs)"])};
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)"])};
package/dist/node.d.ts CHANGED
@@ -34,10 +34,10 @@ declare interface InitOptions {
34
34
  project?: string | Partial<AiiaProjectItem>;
35
35
  /**
36
36
  * [zh]
37
- * 指定的環境,預設值: production
37
+ * 指定的環境,預設值: developement
38
38
  *
39
39
  * [en]
40
- * Specific Env, default: production
40
+ * Specific Env, default: developement
41
41
  */
42
42
  env?: "developement" | "production";
43
43
  /**
@@ -109,15 +109,17 @@ declare interface InitOptions {
109
109
  */
110
110
  llmSampleRate?: number;
111
111
  /**
112
+ * @deprecated
113
+ *
112
114
  * [zh]
113
- * 人臉辨識
115
+ * 人臉辨識(已棄用,請改用 `cameraDetection`),同時設定會以 `cameraDetection` 為主
114
116
  *
115
117
  * @property perSecond: 每秒檢測幾次, 預設: 每秒 3 次
116
118
  *
117
119
  * @property confidence: 分數(0 ~ 100), 預設: 80
118
120
  *
119
121
  * [en]
120
- * facial recognition
122
+ * Face recognition (deprecated, please use `cameraDetection` instead), and the settings will be based on `cameraDetection`
121
123
  *
122
124
  * @property perSecond: Detection times per second, default: 3 times per second
123
125
  *
@@ -127,6 +129,25 @@ declare interface InitOptions {
127
129
  perSecond?: number;
128
130
  confidence?: number;
129
131
  };
132
+ /**
133
+ * [zh]
134
+ * 攝影機辨識設定
135
+ *
136
+ * @property perSecond: 每秒檢測幾次, 預設: 每秒 3 次
137
+ *
138
+ * @property confidence: 人臉辨識分數(0 ~ 100), 預設: 80
139
+ *
140
+ * [en]
141
+ * Camera Identification Settings
142
+ *
143
+ * @property perSecond: Detection times per second, default: 3 times per second
144
+ *
145
+ * @property confidence: Face recognition score(0 ~ 100), default: 80
146
+ */
147
+ cameraDetection?: {
148
+ perSecond?: number;
149
+ confidence?: number;
150
+ };
130
151
  /**
131
152
  * [zh]
132
153
  * 清除字幕,可指定時間(毫秒),預設 5000 毫秒後清除
@@ -166,7 +187,7 @@ declare interface InitOptions {
166
187
 
167
188
  declare type LoggerLevel = "info" | "error" | "warn" | "debug" | "fatal" | "code";
168
189
 
169
- export declare type NodeInitOptions = Omit<InitOptions, "project" | "ws_url" | "worklet_url" | "mediaStream" | "chunkTimeInSeconds" | "llmSampleRate" | "autoClearUserSubtitle" | "autoClearAiiaSubtitle" | "faceDetection">;
190
+ export declare type NodeInitOptions = Omit<InitOptions, "project" | "ws_url" | "worklet_url" | "mediaStream" | "chunkTimeInSeconds" | "llmSampleRate" | "autoClearUserSubtitle" | "autoClearAiiaSubtitle" | "faceDetection" | "cameraDetection">;
170
191
 
171
192
  declare type Webserver = Server<any, any> | Server_2<any, any> | Http2SecureServer<any, any, any, any> | Http2Server<any, any, any, any>;
172
193
 
package/dist/node.mjs CHANGED
@@ -1 +1 @@
1
- import{assign as e,forEach as t,get as s,map as o,omit as r,isArray as i}from"lodash-es";import n from"moment";import{abs as c,matrix as a,multiply as h}from"mathjs";import{Server as l}from"socket.io";import{Subject as u,fromEvent as d,takeUntil as S,take as b}from"rxjs";import p from"axios";import _ from"ws";import g from"fs";import y from"os";import f from"path";import{Client as E}from"node-osc";var m;!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 O="access_token",D="undefined"!=typeof process&&null!=process.versions&&null!=process.versions.node;class N{config;get webserver(){return this.config.webserver}get environment(){return this.config.env}get license(){return this.config.license??""}get port(){return function(e,t){const s=t??NaN;if(e){const t=parseInt(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.config.port??3e3)}get ws_url(){return this.config.ws_url??""}get worklet_url(){return this.config.worklet_url??""}get project(){const{project:t}=e({},this.config);return void 0===t?{specific:!1}:"string"==typeof t?{name:t,id:t,specific:!1}:e({},t,{specific:!0})}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}return{api:`https://aiia-content-management-${e}-21193779403.asia-east1.run.app`,socket:`wss://graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`}}get debug(){return this.config.debug??!0}get mediaStream(){return this.config.mediaStream}get chunkTimeInSeconds(){return"number"==typeof this.config.chunkTimeInSeconds&&this.config.chunkTimeInSeconds>0?this.config.chunkTimeInSeconds:.3}get llmSampleRate(){return"number"==typeof this.config.llmSampleRate&&this.config.llmSampleRate>0?this.config.llmSampleRate:16e3}get faceDetection(){return e({perSecond:3,confidence:80},this.config.faceDetection)}get autoClearSubtitle(){return{userDelayTime:this.config.autoClearUserSubtitle??5e3,aiiaDelayTime:this.config.autoClearAiiaSubtitle??NaN}}get eyeTrackEnable(){return this.config.eyeTrackEnable??!1}get eyeTrackCorrection(){return e({x_axis_px:0,y_axis_px:0},this.config.correction)}constructor(e){this.config=e}}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["安全的關閉連線"]="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 k={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},C=[];function A(e){return C.push(e),A}const w=new Proxy(k,{get(e,s,o){if("string"==typeof s)switch(s){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...o)=>{const r=n().format("yyyy-MM-DD[T]HH:mm:ss");t(C,e=>{e.next(s,r,...o)}),e[s](`[Aiia::${s}_${r}_]`,...o)};default:return}},set:(e,t,s,o)=>!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 u}start(){if(this.server=new l(this.webserver,{cors:{origin:"*"}}),this.server.on("connection",e=>{this.memberSub.next({type:"in",socket:e}),w.debug("client on",e.id),e.on("disconnect",t=>{this.memberSub.next({type:"out",socket:e}),w.debug("client off",t)})}),void 0===this.webserver){const e=this.port;this.server.listen(e),w.info("Websocket server run on",e)}else w.info("Websocket server rely your webserver.")}onDestroy(){this.server&&this.server.close()}}class M{instance;controller;constructor(e){this.controller=new AbortController,this.instance=p.create({baseURL:e,signal:this.controller.signal})}async getAccessToken(e,t){const o={license_number:e,token_value:t??""},r=await this.instance.post("/api/v1/license/agent_token",o).then(e=>s(e.data,"token_value","")).catch(()=>null);if(null===r)throw new Error(m["網絡錯誤"]);if(""===r)throw new Error(m["獲取權杖失敗"]);return r}async getProjectList(e){const t=await this.instance.post("/api/v1/license/projects",{license_number:e}).then(e=>o(s(e,"data.projects",[]),({id:e,sub_project_name:t})=>({id:e,name:t}))).catch(e=>null);if(null===t)throw new Error(m["網絡錯誤"]);if(0===t.length)throw new Error(m["沒有可用專案"]);return t}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.MANUAL_CLOSE=4002]="MANUAL_CLOSE",e[e.WS_5000_NORAML_CLOSURE=4100]="WS_5000_NORAML_CLOSURE",e[e.WS_5001_UNAUTHORIZED=4101]="WS_5001_UNAUTHORIZED",e[e.WS_5002_LICENSE_EXPIRED=4102]="WS_5002_LICENSE_EXPIRED",e[e.WS_5003_TOKEN_EXPIRED=4103]="WS_5003_TOKEN_EXPIRED"}(L||(L={}));class I{socket;messageSub;linkStartSub;destorySub;closeSub;get message(){return this.messageSub.asObservable()}get close(){return this.closeSub.asObservable()}get linkStart(){return this.linkStartSub.asObservable()}isConnected;constructor(){this.isConnected=!1,this.messageSub=new u,this.linkStartSub=new u,this.closeSub=new u,this.destorySub=new u}start(e){this.socket=new _(e,{autoPong:!0}),d(this.socket,"open").pipe(S(this.destorySub)).subscribe(()=>{this.isConnected=!0,w.code(R["啟動服務"],"proxy"),this.linkStartSub.next()}),d(this.socket,"message").pipe(S(this.destorySub)).subscribe(e=>{const{data:t}=e;this.messageSub.next(t)}),d(this.socket,"error").pipe(S(this.destorySub)).subscribe(e=>{const{error:t}=e;w.error("Proxy has some problem",t)}),d(this.socket,"close").pipe(S(this.destorySub)).subscribe(e=>{this.isConnected=!1;const{code:t,reason:s,type:o,wasClean:r}=e;switch(t){case L.WHEN_BROWSER_CLOSE:w.code(R["無副作用的關閉連線"]);break;case L.MDN_NORMAL_CLOSURE:case L.MDN_GOING_AWAY:this.closeSub.next("close"),w.code(R["安全的關閉連線"]);break;case L.MDN_NO_STATUS_RECEIVED:case L.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),w.code(R["與雲端的服務中斷"],{code:t,reason:s,type:o,wasClean:r});break;case L.WS_5001_UNAUTHORIZED:case L.WS_5002_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),w.code(R["沒有授權,請聯繫Graphen"]);break;case L.WS_5000_NORAML_CLOSURE:case L.WS_5003_TOKEN_EXPIRED:this.closeSub.next("reconnect"),w.code(R["自動重新連線連端服務"]);break;default:this.closeSub.next("close"),w.code(R["未預期的斷線,請聯繫Graphen"],{code:t,reason:s,type:o,wasClean:r})}})}reconnect(e){this.onDestroy(),this.destorySub=new u,this.start(e)}send(e){this.isConnected&&void 0!==this.socket&&this.socket.send(e,e=>{e&&w.error("send message fail",e?.message,e)})}sendToCloud(e){this.send(JSON.stringify(e))}onClose(){this.socket?.close(L.WHEN_BROWSER_CLOSE)}onDestroy(){this.destorySub.next(),this.destorySub.complete()}}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=f.join(this.homedir,"Library","Application Support");break;case"win32":this.isMAC=!1,this.isWIN=!0,this.isLINUX=!1,this.saveFolder=process.env.APPDATA||f.join(this.homedir,"AppData","Roaming");break;case"linux":this.isMAC=!1,this.isWIN=!1,this.isLINUX=!0,this.saveFolder=process.env.XDG_CONFIG_HOME||f.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=f.join(this.saveFolder,this.AppName),this.folderCheck(this.appFolder)}addPath(...e){return f.join(...e)}folderCheck(e){g.mkdirSync(e,{recursive:!0})}fileCheck(e){g.existsSync(e)||g.writeFileSync(e,"",{encoding:"utf8"})}}("aiia-sdk");class T{type="node";filePath;fileName;constructor(){this.fileName="storage.json",this.filePath=P.addPath(P.appFolder,this.fileName),P.fileCheck(this.filePath)}getFileDataSync(){const e=g.readFileSync(this.filePath,"utf8");try{return JSON.parse(e)}catch(e){return{}}}setFileDataSync(e){try{const t=JSON.stringify(e);g.writeFileSync(this.filePath,t,"utf8")}catch(e){}}async getFileData(){return new Promise(e=>{g.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);g.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({})}}class v{config;constructor(e){this.config=e,w.info("UEProxy, config=",e)}start(){w.info("UEProxy, start proxy")}send(e){let t=this.cloudToObj(e);if(t&&t.signal&&"osc"===t.signal){w.info("UEProxy, payload=",t);try{let e=t.content;const s=new E(e.host,e.port);w.debug("UEProxy, client open to port "+e.port),s.send(e.message,e=>{e&&w.error(e),s.close(),w.debug("UEProxy, client closed")})}catch(e){w.error(e)}}}cloudToObj(e){try{return JSON.parse(e)}catch(e){w.error("message transfer fail",e)}}onDestroy(){w.info("UEProxy, destroy proxy")}}class U{socket;messageSub;linkStartSub;destorySub;closeSub;get message(){return this.messageSub.asObservable()}get close(){return this.closeSub.asObservable()}get linkStart(){return this.linkStartSub.asObservable()}messageQueue=[];isConnected;constructor(){this.isConnected=!1,this.messageSub=new u,this.linkStartSub=new u,this.closeSub=new u,this.destorySub=new u}url="ws://localhost:8080";start(){this.socket=new _(this.url,{autoPong:!0}),d(this.socket,"open").pipe(S(this.destorySub)).subscribe(()=>{this.isConnected=!0,w.code(R["啟動服務"],"proxy"),this.flushMessageQueue(),this.linkStartSub.next()}),d(this.socket,"message").pipe(S(this.destorySub)).subscribe(e=>{const{data:t}=e;this.messageSub.next(t)}),d(this.socket,"error").pipe(S(this.destorySub)).subscribe(e=>{const{error:t}=e;w.error("Proxy has some problem",t)}),d(this.socket,"close").pipe(S(this.destorySub)).subscribe(e=>{this.isConnected=!1;const{code:t,reason:s}=e;switch(t){case L.MANUAL_CLOSE:w.code(R["無副作用的關閉連線"]);break;case L.WS_5000_NORAML_CLOSURE:case L.MDN_NORMAL_CLOSURE:case L.MDN_GOING_AWAY:this.closeSub.next("close"),w.code(R["安全的關閉連線"]);break;case L.MDN_NO_STATUS_RECEIVED:case L.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),w.code(R["與雲端的服務中斷"],{code:t,reason:s});break;case L.WS_5001_UNAUTHORIZED:case L.WS_5002_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),w.code(R["沒有授權,請聯繫Graphen"]);break;case L.WS_5003_TOKEN_EXPIRED:this.closeSub.next("reconnect"),w.code(R["自動重新連線連端服務"]);break;default:this.closeSub.next("close"),w.code(R["未預期的斷線,請聯繫Graphen"],{code:t,reason:s})}})}reconnect(){this.onDestroy(),this.destorySub=new u,this.start()}flushMessageQueue(){for(;this.messageQueue.length>0&&this.isConnected&&void 0!==this.socket;){const e=this.messageQueue.shift();e&&this.socket.send(e)}}send(e){let t=this.cloudToObj(e);if(t&&t.signal&&"audio"===t.signal&&"pcm"===t.command&&(w.info("AudioSocketProxy, payload=",t),this.isConnected&&void 0!==this.socket))try{const e=t.content,s=Buffer.allocUnsafe(2*e.length);for(let t=0;t<e.length;t++)s.writeInt16LE(e[t],2*t);this.socket.send(s)}catch(e){w.error(e)}}cloudToObj(e){try{return JSON.parse(e)}catch(e){w.error("message transfer fail",e)}}onClose(){this.socket?.close(L.MANUAL_CLOSE)}onDestroy(){this.destorySub.next(),this.destorySub.complete()}}class W{socket;messageSub;linkStartSub;destorySub;closeSub;get message(){return this.messageSub.asObservable()}get close(){return this.closeSub.asObservable()}get linkStart(){return this.linkStartSub.asObservable()}messageQueue=[];isConnected;constructor(){this.isConnected=!1,this.messageSub=new u,this.linkStartSub=new u,this.closeSub=new u,this.destorySub=new u}url="ws://localhost:8081";start(){this.socket=new _(this.url,{autoPong:!0}),d(this.socket,"open").pipe(S(this.destorySub)).subscribe(()=>{this.isConnected=!0,w.code(R["啟動服務"],"proxy"),this.flushMessageQueue(),this.linkStartSub.next()}),d(this.socket,"message").pipe(S(this.destorySub)).subscribe(e=>{const{data:t}=e;this.messageSub.next(t)}),d(this.socket,"error").pipe(S(this.destorySub)).subscribe(e=>{const{error:t}=e;w.error("Proxy has some problem",t)}),d(this.socket,"close").pipe(S(this.destorySub)).subscribe(e=>{this.isConnected=!1;const{code:t,reason:s}=e;switch(t){case L.MANUAL_CLOSE:w.code(R["無副作用的關閉連線"]);break;case L.WS_5000_NORAML_CLOSURE:case L.MDN_NORMAL_CLOSURE:case L.MDN_GOING_AWAY:this.closeSub.next("close"),w.code(R["安全的關閉連線"]);break;case L.MDN_NO_STATUS_RECEIVED:case L.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),w.code(R["與雲端的服務中斷"],{code:t,reason:s});break;case L.WS_5001_UNAUTHORIZED:case L.WS_5002_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),w.code(R["沒有授權,請聯繫Graphen"]);break;case L.WS_5003_TOKEN_EXPIRED:this.closeSub.next("reconnect"),w.code(R["自動重新連線連端服務"]);break;default:this.closeSub.next("close"),w.code(R["未預期的斷線,請聯繫Graphen"],{code:t,reason:s})}})}reconnect(){this.onDestroy(),this.destorySub=new u,this.start()}flushMessageQueue(){for(;this.messageQueue.length>0&&this.isConnected&&void 0!==this.socket;){const e=this.messageQueue.shift();e&&this.socket.send(e)}}send(e){let t=this.cloudToObj(e);if(t&&t.signal&&"audio"===t.signal&&"interrupted"===t.command&&(w.info("AudioInterruptSocketProxy, payload=",t),this.isConnected&&void 0!==this.socket))try{this.socket.send(t.command)}catch(e){w.error(e)}}cloudToObj(e){try{return JSON.parse(e)}catch(e){w.error("message transfer fail",e)}}onClose(){this.socket?.close(L.MANUAL_CLOSE)}onDestroy(){this.destorySub.next(),this.destorySub.complete()}}class F{next(e,t,...s){const o=`log_${n().format("yyyy-MM-DD")}.txt`,r=P.addPath(P.appFolder,o);try{const o=s.reduce((e,t)=>{switch(typeof t){case"string":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}]`}},"");g.appendFileSync(r,`LOG(${e})__${t}__::${o}${P.EOL}`,{encoding:"utf8"})}catch(e){}}}class G{config;stroage;apiProxy;chat;udpProxy;audioProxy;audioIntrProxy;isStart;channelMap;destorySub;access_token;eyeBallUdp;get accessToken(){return this.access_token}constructor(e,t){this.config=e,this.stroage=t,this.isStart=!1,this.destorySub=new u,this.channelMap=new Map,this.access_token=t.getItemSync(O),this.apiProxy=new M(this.config.endPoint.api),w.code(R["指定模組初始化完成"],"api"),this.chat=new x(this.config),w.code(R["指定模組初始化完成"],"chat"),this.udpProxy=new v(this.config),w.code(R["指定模組初始化完成"],"udp"),this.audioProxy=new U,w.code(R["指定模組初始化完成"],"audio"),this.audioIntrProxy=new W,w.code(R["指定模組初始化完成"],"audio intr"),e.eyeTrackEnable&&(this.eyeBallUdp=new E("127.0.0.1",6745),w.code(R["指定模組初始化完成"],"eye ball track")),this.openChannel=this.openChannel.bind(this)}async start(){if(this.isStart)throw new Error(m["重複初始化"]);this.isStart=!0,await this.replaceAccessToken(),this.chat.memberChange.pipe(S(this.destorySub)).subscribe(({type:e,socket:t})=>{switch(e){case"in":{const e=new I,r=new u,n=(o=t.handshake.query.uuid,i(o)?s(o,"[0]",void 0):o??void 0);void 0===n?(this.getProjectList().then(e=>{t.emit("list",e)}),d(t,"uuid").pipe(b(1),S(r)).subscribe(s=>{this.openChannel(s,t,e,r)})):this.openChannel(n,t,e,r),this.channelMap.set(t.id,()=>{r.next(),r.complete()});break}case"out":this.channelMap.has(t.id)&&(this.channelMap.get(t.id)(),this.channelMap.delete(t.id))}var o}),this.chat.start(),w.code(R["啟動服務"],"chat"),this.udpProxy.start(),w.code(R["啟動服務"],"udp")}async replaceAccessToken(){const e=await this.apiProxy.getAccessToken(this.config.license,this.access_token);return this.access_token=e,this.stroage.setItemSync(O,e),e}async getProjectList(){return await this.apiProxy.getProjectList(this.config.license)}getProxySocketURL(e){return`${this.config.endPoint.socket}/v1/chat/project/${this.accessToken}/${e}`}openChannel(t,s,o,r){d(s,"cloud").pipe(S(r)).subscribe(e=>{o.send(e)});const{x_axis_px:i,y_axis_px:n}=this.config.eyeTrackCorrection;d(s,"face").pipe(S(r)).subscribe(t=>{const s=t,r=null!==s;if(r&&void 0!==this.eyeBallUdp){const{x:t,y:o}=function(t,s){const{maxX:o,maxY:r,minX:i,minY:n}=e({maxX:25,minX:-25,maxY:50,minY:-50},s),l=c(o)+c(i),u=c(r)+c(n),{clientHeight:d,clientWidth:S,originX:b,originY:p,width:_,height:g}=t,y=a([[l/S,0,i],[0,u/d,n],[0,0,1]]),f=b+_/2,E=d-(p+g/2),m=a([[f>S?S:f<0?0:f],[E>d?d:E<0?0:E],[1]]),O=h(y,m);return{x:O.get([0,0]),y:O.get([1,0])}}(s);this.eyeBallUdp.send(["/",t+i,o+n],e=>{null!=e&&w.error("eye track error:",e)})}o.sendToCloud({request:"face_detect",content:r})}),o.message.pipe(S(r)).subscribe(e=>{s.emit("cloud",e),this.udpProxy.send(e),this.audioProxy.send(e),this.audioIntrProxy.send(e)}),o.close.pipe(S(r)).subscribe(async e=>{switch(s.emit("channel",e),e){case"close":case"no_permissions":o.onDestroy();break;case"reconnect":await this.replaceAccessToken(),o.reconnect(this.getProxySocketURL(t))}}),o.linkStart.pipe(S(r)).subscribe(()=>{s.emit("channel","open")}),r.subscribe(()=>{o.onClose(),this.audioProxy.onClose(),this.audioIntrProxy.onClose()}),o.start(this.getProxySocketURL(t)),this.audioProxy.start(),this.audioIntrProxy.start()}onDestroy(){this.apiProxy.onDestroy(),this.chat.onDestroy(),this.udpProxy.onDestroy(),this.destorySub.next(),this.destorySub.complete(),this.eyeBallUdp?.close(),w.code(R["服務結束"])}}function j(t){if(D){if(void 0===s(t,"license",void 0))throw new Error(m["未提供憑證"]);!function(e){const{info:t,error:s,warn:o,debug:r,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||(k.code=()=>{}),r||(k.debug=()=>{}),t||(k.info=()=>{}),o||(k.warn=()=>{}),s||(k.error=()=>{}),i||(k.fatal=()=>{}),A}(s(t,"debug",!0))(new F),w.code(R["開始初始化必要模組"]);const o=new N(e({},t));w.code(R["指定模組初始化完成"],"config");const r=new T;w.code(R["指定模組初始化完成"],"storage");const i=new G(o,r);return w.code(R["指定模組初始化完成"],"core"),i.start(),w.code(R["啟動服務"]),()=>{i.onDestroy()}}throw new Error(m["環境錯誤(nodejs)"])}export{j as aiiaCore};
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphen.ai/aiia-sdk",
3
- "version": "1.0.16",
3
+ "version": "1.0.17",
4
4
  "keywords": [],
5
5
  "author": {
6
6
  "name": "Jay Huang",
@@ -60,6 +60,7 @@
60
60
  "@types/node": "^24.2.1",
61
61
  "@types/node-osc": "^9.1.0",
62
62
  "@types/ws": "^8.18.1",
63
+ "@unrs/resolver-binding-darwin-arm64": "^1.11.1",
63
64
  "jest": "^30.1.3",
64
65
  "jest-environment-jsdom": "^30.1.2",
65
66
  "rollup": "^4.46.2",