@graphen.ai/aiia-sdk 1.0.16 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/aiia-worklet.js +1 -0
- package/dist/browser.cjs +1 -1
- package/dist/browser.d.ts +78 -41
- package/dist/browser.mjs +1 -1
- package/dist/node.cjs +1 -1
- package/dist/node.d.ts +54 -5
- package/dist/node.mjs +1 -1
- package/package.json +4 -1
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 =
|
|
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 |
|
|
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 |
|
package/dist/aiia-worklet.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
+
/** @license AiiaVad Released: 2025-12-8 */
|
|
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("socket.io-client"),n=require("@mediapipe/tasks-vision");!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(s||(s={}));const o="undefined"!=typeof window;"undefined"!=typeof process&&null!=process.versions&&process.versions.node;class r{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e.get(t,"id",void 0),i=e.get(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof i?i:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return e.assign({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return e.assign({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e.get(this.config,t,void 0)}}var c;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(c||(c={}));const u={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},d=[];const h=new Proxy(u,{get(s,i,a){if("string"==typeof i)switch(i){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...a)=>{const n=t().format("yyyy-MM-DD[T]HH:mm:ss:sss");e.forEach(d,e=>{e.next(i,n,...a)}),s[i](`[Aiia::${i}_${n}_]`,...a)};default:return}},set:(e,t,s,i)=>!0});class l{socket;messageSub;destorySub;get message(){return this.messageSub.asObservable()}constructor(t){this.destorySub=new i.Subject,this.messageSub=new i.Subject;const{specific:s,id:n}=t.project;this.socket=a.io(t.ws_url,e.assign({autoConnect:!1},s&&void 0!==n?{query:{uuid:n}}:{}))}start(){const e="list",t="cloud",s="channel";i.fromEvent(this.socket,"connect").pipe(i.takeUntil(this.destorySub)).subscribe(()=>{h.debug("Client ID: ",this.socket.id)}),i.fromEvent(this.socket,e).pipe(i.take(1),i.takeUntil(this.destorySub)).subscribe(t=>{this.messageSub.next({event:e,data:t})}),i.fromEvent(this.socket,t).pipe(i.takeUntil(this.destorySub)).subscribe(e=>{this.messageSub.next({event:t,data:e})}),i.fromEvent(this.socket,s).pipe(i.takeUntil(this.destorySub)).subscribe(e=>{this.messageSub.next({event:s,data:e})}),this.socket.connect()}send(e,t){this.socket.emit(e,t)}sendToCloud(e){this.send("cloud",e)}onDestroy(){this.destorySub.next(),this.destorySub.complete(),this.messageSub.complete(),this.socket.disconnect()}}var b,p;function m(e,t,s){return Math.max(s,Math.min(e,t))}function g(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=t.getInt16(2*i,!0)/32768;s.push(e)}return new Float32Array(s)}function S(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=m(32768*t.getFloat32(4*i,!0),32767,-32768);s.push(e)}return s}!function(e){e[e.unknow=0]="unknow",e[e.allowed=1]="allowed",e[e.rejected=2]="rejected"}(b||(b={}));class f{audioContext;type="audio";currentNode;bufferQueue;gainNode;set volume(e){e<0&&(e=0),e>100&&(e=100),this.gainNode.gain.value=e/100}get volume(){return Math.round(100*this.gainNode.gain.value)}constructor(e){this.audioContext=e,this.bufferQueue=[],this.currentNode=null,this.gainNode=this.audioContext.createGain(),this.gainNode.connect(this.audioContext.destination)}addBuffer(e){this.bufferQueue.push(e)}addBufferByFloat32(e,t=1){const s=e.length,i=this.audioContext.sampleRate,a=this.audioContext.createBuffer(t,s,i),n=e.slice();for(let e=0;e<t;e++)a.copyToChannel(n,e);this.addBuffer(a)}addBufferByInt16(e,t=1){this.addBufferByFloat32(g(e),t)}startSpeech(){null===this.currentNode&&this.continue()}pauseSpeech(){this.audioContext.suspend().catch(e=>{h.error("pause speech fail",e)})}resumeSpeech(){this.audioContext.resume().catch(e=>{h.error("resume speech fail",e)})}nextSpeech(){this.bufferQueue.length>0&&(this.clearNode(),this.continue())}stopSpeech(){this.bufferQueue=[],this.clearNode()}clearNode(){this.currentNode&&(this.currentNode.onended=function(){},this.currentNode.stop(),this.currentNode.disconnect(),this.currentNode=null)}continue(){const e=this.bufferQueue.shift();if(e){this.currentNode=this.audioContext.createBufferSource(),this.currentNode.buffer=e,this.currentNode.connect(this.gainNode);const t=this.continue.bind(this);this.currentNode.onended=t,this.currentNode.start()}else this.clearNode()}}class _{stream;audioContext;type="vad";workletNode;source;isRecording=!1;outputSampleRate;chunkTimeInSeconds;pcmSub;get pcm(){return this.pcmSub.asObservable()}constructor(e,t,s){this.stream=e,this.audioContext=t,this.outputSampleRate=s?.llmSampleRate??16e3,this.chunkTimeInSeconds=s?.chunkTimeInSeconds??1,this.pcmSub=new i.Subject}async startRecord(){this.isRecording||(this.source=this.audioContext.createMediaStreamSource(this.stream),this.workletNode=new AudioWorkletNode(this.audioContext,"aiia-vad",{processorOptions:{outputSampleRate:this.outputSampleRate,chunkTimeInSeconds:this.chunkTimeInSeconds}}),this.source.connect(this.workletNode),this.workletNode.port.onmessage=e=>{const{float32:t}=e.data;t&&this.pcmSub.next(new Float32Array(t))},this.isRecording=!0)}stopRecord(){this.isRecording&&(this.workletNode&&(this.workletNode.port.onmessage=null,this.workletNode.disconnect()),this.source&&this.source.disconnect(),this.isRecording=!1)}}class y{type="camera";aiiaCamera;get detections(){return this.aiiaCamera.detections}get landmarks(){return this.aiiaCamera.landmarks}get videoSize(){return this.aiiaCamera.videoSize}constructor(t){const{perSecond:s}=e.assign({perSecond:3},t);this.aiiaCamera=new C,this.aiiaCamera.setFrequency(s),document.body.append(this.aiiaCamera)}async init(e){try{const t=await n.FilesetResolver.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.0/wasm"),[s,i]=await Promise.all([n.FaceDetector.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite",delegate:"GPU"},runningMode:"VIDEO"}),n.PoseLandmarker.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task",delegate:"GPU"},runningMode:"VIDEO",numPoses:5})]);this.aiiaCamera.setStream(e),this.aiiaCamera.setFaceDetector(s),this.aiiaCamera.setPoseLandmarker(i)}catch(e){h.error("Camera init fail",e)}}startCapture(){this.aiiaCamera.startCapture()}stopCapture(){this.aiiaCamera.stopCapture()}onDestroy(){this.aiiaCamera.remove()}}class C extends HTMLElement{static get observedAttributes(){return["debug"]}_shadow;_containerEl;_styleEl;_videoEl;_canvasEl;_canvasCtx;_isDebug;_debugElements;_interval;_lastTimestamp;_loopFrameID;_faceDetector;_detectionsSub;_poseLandmarker;_landmarksSub;_isReady;_drawingUtils;landmarkColors=["gray","lightgray","gray","gray","lightgray"];connectorColors=["green","blue","red","yellow","purple"];get detections(){return this._detectionsSub.asObservable()}get landmarks(){return this._landmarksSub.asObservable()}get videoSize(){return{width:this._videoEl.videoWidth,height:this._videoEl.videoHeight}}constructor(){super(),this.id="AiiaCamera",this._isDebug=!1,this._isReady=!1,this._shadow=this.attachShadow({mode:"open"}),this._containerEl=document.createElement("div"),this._videoEl=document.createElement("video"),this._styleEl=document.createElement("style"),this._canvasEl=document.createElement("canvas"),this._debugElements=[],this._loopFrameID=null,this._detectionsSub=new i.Subject,this._landmarksSub=new i.Subject,this._interval=Math.ceil(1e3/3),this._lastTimestamp=0,this.predictWebcam=this.predictWebcam.bind(this),this._settingContainer(),this._settingStyle(),this._settingVideo(),this._containerEl.appendChild(this._videoEl),this._shadow.appendChild(this._styleEl),this._shadow.appendChild(this._containerEl),this._containerEl.appendChild(this._canvasEl);const e=this._canvasEl.getContext("2d");e?(this._canvasCtx=e,this._drawingUtils=new n.DrawingUtils(this._canvasCtx)):(this._canvasCtx=null,this._drawingUtils=null)}connectedCallback(){this.startCapture()}disconnectedCallback(){this.stopCapture()}attributeChangedCallback(e,t,s){"debug"===e&&(this._isDebug=null!==s&&("true"===s||""===s),this._containerEl.style.visibility=this._isDebug?"visible":"hidden")}setStream(e){this._videoEl.onloadedmetadata=()=>{this._isReady=!0},this._videoEl.srcObject=e}setFaceDetector(e){this._faceDetector=e}setPoseLandmarker(e){this._poseLandmarker=e}setFrequency(e){this._interval=Math.ceil(1e3/e)}_settingStyle(){this._styleEl.textContent="\n .aiia_container {\n visibility: hidden;\n position: fixed;\n top: 0;\n left: 0;\n\n > canvas {\n position: absolute;\n top: 0;\n left: 0;\n width: 640px;\n height: 480px;\n }\n }\n .aiia_container .highlighter {\n background: rgba(0, 255, 0, 0.25);\n border: 1px dashed #fff;\n z-index: 1;\n position: absolute;\n }\n .aiia_container .confidence {\n position: absolute;\n padding-bottom: 5px;\n padding-top: 5px;\n background-color: #007f8b;\n color: #fff;\n border: 1px dashed rgba(255, 255, 255, 0.7);\n z-index: 2;\n font-size: 12px;\n margin: 0;\n }\n "}_settingContainer(){this._containerEl.classList.add("aiia_container")}_settingVideo(){this._videoEl.autoplay=!0,this._videoEl.playsInline=!0,this._videoEl.muted=!0}getVideo(){return this._videoEl}startCapture(){null===this._loopFrameID&&(this._videoEl.play(),this._loopFrameID=window.requestAnimationFrame(this.predictWebcam))}stopCapture(){null!==this._loopFrameID&&(window.cancelAnimationFrame(this._loopFrameID),this._loopFrameID=null),this._videoEl.pause()}async predictWebcam(){if(this._isReady){const e=performance.now();if(this._lastTimestamp+this._interval<=e){this._lastTimestamp=e;const t=this._faceDetector?.detectForVideo(this._videoEl,e).detections,s=this._poseLandmarker?.detectForVideo(this._videoEl,e).landmarks;t&&(this._isDebug&&this._drawRect(t),this._detectionsSub.next(t)),s&&(this._isDebug&&this._drawRect2(s),this._landmarksSub.next(s))}}this._loopFrameID=window.requestAnimationFrame(this.predictWebcam)}_drawRect(t){e.forEach(t,(e,t)=>{const{categories:s,boundingBox:i}=e;if(i){let e=this._debugElements[2*t],a=this._debugElements[2*t+1];e&&a||(e=document.createElement("div"),e.classList.add("highlighter"),a=document.createElement("p"),a.classList.add("confidence"),this._containerEl.append(a,e),this._debugElements.push(e,a)),e.style.display="block",a.style.display="block";const n=100*s[0].score;a.innerText=`Confidence: ${Math.round(n)} %`,a.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",a.style.top=i.originY-30+"px",a.style.width=i.width-10+"px",e.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",e.style.top=`${i.originY}px`,e.style.width=i.width-10+"px",e.style.height=`${i.height}px`}});for(let e=2*t.length;e<this._debugElements.length;e++)this._debugElements[e].style.display="none"}_drawRect2(t){this._canvasCtx&&this._drawingUtils&&(this._canvasCtx.save(),this._canvasCtx.clearRect(0,0,640,480),e.forEach(t,(e,t)=>{this._canvasCtx?.save(),this._drawingUtils?.drawLandmarks(e,{radius:e=>n.DrawingUtils.lerp(e.from.z,-.15,.1,5,1),color:this.landmarkColors[t]}),this._drawingUtils?.drawConnectors(e,n.PoseLandmarker.POSE_CONNECTIONS,{color:this.connectorColors[t],lineWidth:2})}),this._canvasCtx.restore())}destroy(){this._detectionsSub.complete(),this.remove()}}customElements.define("aiia-camera",C),function(e){e[e.padding=0]="padding",e[e.rejected=1]="rejected",e[e.loadfail=2]="loadfail",e[e.allowed=3]="allowed"}(p||(p={}));class v{stream;_state;stateSub;audioCtx;audioManager;vadManager;camManager;destroy;pcmSub;detectionsSub;landmarksSub;get sampleRate(){return this.audioCtx?this.audioCtx.sampleRate:16e3}get pcm(){return this.pcmSub.asObservable()}get detections(){return this.detectionsSub.asObservable()}get landmarks(){return this.landmarksSub.asObservable()}get state(){return this._state}set state(e){e!==this._state&&(this._state=e,this.stateSub.next(e))}get stateObs(){return this.stateSub.asObservable()}constructor(e){this.stream=e,this.stateSub=new i.ReplaySubject(1),this.pcmSub=new i.Subject,this.destroy=new i.Subject,this.detectionsSub=new i.Subject,this.landmarksSub=new i.Subject,this.state=p.padding}async init(e,t){const a=this.stream??await async function(e){try{return await navigator.mediaDevices.getUserMedia(e)}catch(e){return null}}({audio:{echoCancellation:!0,noiseSuppression:!0},video:!0});if(null===a)throw this.state=p.rejected,new Error(s["未獲得媒體裝置權限"]);this.stream=a,this.audioCtx=new AudioContext;try{await this.audioCtx.audioWorklet.addModule(e)}catch(e){throw h.fatal(e),this.state=p.loadfail,new Error(s["Worklet模組載入失敗"])}this.state=p.allowed,this.audioManager=new f(this.audioCtx),this.vadManager=new _(a,this.audioCtx,t),this.vadManager.pcm.pipe(i.takeUntil(this.destroy)).subscribe(e=>{this.pcmSub.next(e)});const n=new y(t?.cameraDetection);n.detections.pipe(i.takeUntil(this.destroy)).subscribe(e=>{const{width:t,height:s}=n.videoSize;this.detectionsSub.next({detections:e,clientWidth:t,clientHeight:s})}),n.landmarks.pipe(i.takeUntil(this.destroy)).subscribe(e=>{this.landmarksSub.next(e)}),n.init(a),this.camManager=n}addAudioQueue(t){const s=e.get(t,"buffer",void 0),i=e.get(t,"float32",void 0),a=e.get(t,"int16",void 0),n=e.get(t,"numberOfChannels",void 0);s?this.audioManager?.addBuffer(s):i?this.audioManager?.addBufferByFloat32(i,n):a&&this.audioManager?.addBufferByInt16(a,n)}playAudio(){this.audioManager?.startSpeech()}stopAudio(){this.audioManager?.stopSpeech()}startRecord(){this.vadManager?.startRecord()}stopRecord(){this.vadManager?.stopRecord()}setVolume(e){this.audioManager&&(this.audioManager.volume=e)}getVolume(){return this.audioManager?this.audioManager.volume:0}startCapture(){this.camManager?.startCapture()}stopCapture(){this.camManager?.stopCapture()}onDestroy(){this.camManager?.onDestroy(),this.audioCtx?.close(),this.destroy.next(),this.destroy.complete()}}class w{config;media;chat;destroySub;projectsSub;layoutSub;aiiaSubtitleSub;userSubtitleSub;sampleRateOfWhisper;channelState;stateSub;_currentState;isMuteAiia;aiiaSubtitleClearSub;userSubtitleClearSub;_lastProjects;guestSub;_hasGuest;mediaEventReady;isStart;get state(){return this.stateSub.asObservable()}get currentState(){return this._currentState}get layout(){return this.layoutSub.asObservable()}get projects(){return this.projectsSub.asObservable()}get lastProjects(){return e.cloneDeep(this._lastProjects)}get aiiaSubtitle(){return this.aiiaSubtitleSub.asObservable()}get userSubtitle(){return this.userSubtitleSub.asObservable()}get guest(){return this.guestSub.asObservable()}get hasGuest(){return this._hasGuest}set volume(e){this.media.setVolume(e)}get volume(){return this.media.getVolume()}constructor(e,t){this.config=e,this.media=t,this.layoutSub=new i.Subject,this.projectsSub=new i.ReplaySubject(1),this.destroySub=new i.Subject,this.aiiaSubtitleSub=new i.Subject,this.userSubtitleSub=new i.Subject,this.aiiaSubtitleClearSub=new i.Subject,this.userSubtitleClearSub=new i.Subject,this.stateSub=new i.BehaviorSubject("NotStart"),this.sampleRateOfWhisper={input:e.llmSampleRate,output:16e3},this.channelState={proxy:!1,cloud:!1},this.isMuteAiia=!1,this._lastProjects=[],this.guestSub=new i.ReplaySubject(1),this._hasGuest=!1,this.mediaEventReady=!1,this.isStart=!1,this.chat=new l(e),h.code(c["指定依賴已完成"],"chat"),this.close=this.onDestroy,this.behavior=this.behavior.bind(this);const{userDelayTime:s,aiiaDelayTime:a}=e.autoClearSubtitle;isNaN(a)||this.aiiaSubtitleClearSub.pipe(i.debounceTime(a),i.takeUntil(this.destroySub)).subscribe(()=>{this.aiiaSubtitleSub.next("")}),isNaN(s)||this.userSubtitleClearSub.pipe(i.debounceTime(s),i.takeUntil(this.destroySub)).subscribe(()=>{this.userSubtitleSub.next("")}),this.stateSub.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this._currentState=e}),this.projectsSub.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this._lastProjects=e}),this.guestSub.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this._hasGuest=e}),t.stateObs.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{p.allowed===e&&this.channelCheck()})}start(){this.isStart?h.code(c["已啟動SDK"]):(this.isStart=!0,this.chat.message.pipe(i.takeUntil(this.destroySub)).subscribe(({event:t,data:s})=>{switch(t){case"cloud":e.map(s,this.behavior);break;case"list":{const{id:t,name:i,specific:a}=this.config.project;let n=null;if(a&&void 0!==t)n=t;else{const a=e.find(s,e=>e.name===i||e.id===t);a&&(n=a.id)}null!==n?this.chooseProject(n):null===s?(this.stateSub.next("CloseService"),h.error("No project ready or lose network")):(this.projectsSub.next(s),this.stateSub.next("WaitingProjectID"),h.code(c["請訂閱專案列表"]));break}case"channel":switch(this.channelState.proxy=!0,s){case"open":this.channelCheck();break;case"close":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("CloseService");break;case"no_permissions":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("NoPermissions");break;case"reconnect":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("ReconnectingCloud");break;case"net_error":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("CloseService"),h.error("Please check network status")}}}),this.chat.start(),this.stateSub.next("ConnectingCloud"),h.code(c["啟動SDK"]))}close;skip(){this.chat.sendToCloud({request:"skip"})}reset(){this.chat.sendToCloud({request:"reset"})}chooseProject(e){this.chat.send("uuid",e),this.stateSub.next("ConnectingCloud")}mute(){this.isMuteAiia=!0,this.media.stopAudio(),this.media.stopRecord()}unmute(){this.isMuteAiia=!1,this.media.startRecord()}behavior(t){switch(t.signal){case"layout":this.layoutSub.next(t.content);break;case"audio":if("sampleRate"===t.command)this.sampleRateOfWhisper.output=t.content;else switch(t.command){case"pcm":if(!this.isMuteAiia){const e=function(e,t,s){if(t===s)return e;const i=s/t,a=Math.ceil(e.length*i),n=new Float32Array(a);for(let t=0;t<a;t++){const s=t/i,a=Math.floor(s),o=s-a,r=e[a],c=e[Math.min(a+1,e.length-1)];n[t]=r+(c-r)*o}return n}(g(new Int16Array(t.content)),this.sampleRateOfWhisper.output,this.media.sampleRate);this.media.addAudioQueue({float32:e}),this.media.playAudio()}break;case"interrupted":this.media.stopAudio(),this.aiiaSubtitleSub.next("")}break;case"status":"Connected"===e.get(t,"content")&&(this.channelState.cloud=!0,this.channelCheck(),this.layoutSub.next({type:"resetLayout"}));break;case"agent":if(!this.isMuteAiia){const s=e.get(t,"content.function_result.response");void 0!==s&&(this.aiiaSubtitleSub.next(s),this.aiiaSubtitleClearSub.next())}break;case"asr":if(!this.isMuteAiia&&"ASR"===e.get(t,"content.function_type")){const s=e.get(t,"content.function_result.result");void 0!==s&&(this.userSubtitleSub.next(s),this.userSubtitleClearSub.next())}break;case"cv":{const s=e.get(t,"content.status");"user present"===s?this.guestSub.next(!0):"user leave"===s?this.guestSub.next(!1):h.warn("Unexpect value",e.get(t,"content"));break}default:h.debug(t.signal)}}channelCheck(){const{proxy:t,cloud:s}=this.channelState;if(t&&s&&this.media.state===p.allowed){if(this.chat.sendToCloud({request:"audio",command:"sampleRate",content:this.sampleRateOfWhisper.input}),!this.mediaEventReady){this.mediaEventReady=!0,this.media.pcm.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"audio",command:"pcm",content:S(e)})});const t=(a=this.config.detection.confidence,function(t,s){const{width:i,height:n}=s,o=e.filter(t,e=>100*(e.categories[0]?.score??0)>=a),r=e.compact(e.map(o,e=>{if(void 0!==e.boundingBox){const t=e.boundingBox,s=t.width*t.height,a=e.categories[0]?.score??0;return{...t,area:s,score:100*a,clientWidth:i,clientHeight:n}}return null}));return e.sortBy(r,["area","score"])[0]??null});this.media.detections.pipe(i.takeUntil(this.destroySub),i.map(({detections:e,clientHeight:s,clientWidth:i})=>t(e,{width:i,height:s}))).subscribe(e=>{this.chat.send("face",e)}),this.media.landmarks.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"pose_detect",content:e})})}this.media.startRecord(),this.media.startCapture(),this.stateSub.next("InService"),h.code(c["通道、雲端服務、麥克風都已就緒"])}else t&&s&&this.media.state!==p.allowed&&(this.stateSub.next("InServiceNoMedia"),h.code(c["未取得媒體裝置權限,請重新設定或忽略此訊息"]));var a}onDestroy(){this.chat.onDestroy(),this.media.onDestroy(),this.destroySub.next(),this.destroySub.complete(),this.stateSub.next("Destroy"),this.stateSub.complete(),h.code(c["結束SDK"])}}exports.initSdk=function(t){if(o){!function(e){const{info:t,error:s,warn:i,debug:a,fatal:n,code:o}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);o||(u.code=()=>{}),a||(u.debug=()=>{}),t||(u.info=()=>{}),i||(u.warn=()=>{}),s||(u.error=()=>{}),n||(u.fatal=()=>{})}(e.get(t,"debug",!0)),h.code(c["初始化必要依賴"]);const s=new r(e.assign({},t));h.code(c["指定依賴已完成"],"config");const i=new v(s.mediaStream);h.code(c["指定依賴已完成"],"config");const a=new w(s,i);return h.code(c["指定依賴已完成"],"sdk"),""!==s.worklet_url&&i.init(s.worklet_url,s),a}throw new Error(s["環境錯誤(browser)"])};
|
package/dist/browser.d.ts
CHANGED
|
@@ -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';
|
|
@@ -52,9 +53,10 @@ export declare namespace aiia_msg {
|
|
|
52
53
|
OLD_AGENT,
|
|
53
54
|
QUESTIONNAIRE,
|
|
54
55
|
LayoutID,
|
|
55
|
-
LAYOUT,
|
|
56
|
+
LAYOUT_2 as 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():
|
|
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():
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
* 可訂閱,服務狀態
|
|
@@ -150,23 +164,7 @@ declare class AiiaSdk {
|
|
|
150
164
|
* [en]
|
|
151
165
|
* Subscribable, Control layout
|
|
152
166
|
*/
|
|
153
|
-
get layout(): Observable<
|
|
154
|
-
type: "mount";
|
|
155
|
-
id: aiia_msg.LayoutID;
|
|
156
|
-
panel: string;
|
|
157
|
-
props?: Record<string, unknown>;
|
|
158
|
-
zIndex?: number;
|
|
159
|
-
} | {
|
|
160
|
-
type: "update";
|
|
161
|
-
id: aiia_msg.LayoutID;
|
|
162
|
-
props?: Record<string, unknown>;
|
|
163
|
-
zIndex?: number;
|
|
164
|
-
} | {
|
|
165
|
-
type: "unmount";
|
|
166
|
-
id: aiia_msg.LayoutID;
|
|
167
|
-
} | {
|
|
168
|
-
type: "resetLayout";
|
|
169
|
-
}>;
|
|
167
|
+
get layout(): Observable<LAYOUT>;
|
|
170
168
|
/**
|
|
171
169
|
* [zh]
|
|
172
170
|
* 若沒有明確指定 Project ID 時,請訂閱此物件
|
|
@@ -174,7 +172,7 @@ declare class AiiaSdk {
|
|
|
174
172
|
* [en]
|
|
175
173
|
* If no Project ID is explicitly specified, please subscribe to this object
|
|
176
174
|
*/
|
|
177
|
-
get projects(): Observable<
|
|
175
|
+
get projects(): Observable<AiiaProjectItemWithAvatar[]>;
|
|
178
176
|
/**
|
|
179
177
|
* [zh]
|
|
180
178
|
* 可讀取最近一次收到的專案列表
|
|
@@ -182,10 +180,7 @@ declare class AiiaSdk {
|
|
|
182
180
|
* [en]
|
|
183
181
|
* You can read the list of projects received recently
|
|
184
182
|
*/
|
|
185
|
-
get lastProjects():
|
|
186
|
-
id: string;
|
|
187
|
-
name: string;
|
|
188
|
-
}[];
|
|
183
|
+
get lastProjects(): AiiaProjectItemWithAvatar[];
|
|
189
184
|
/**
|
|
190
185
|
* [zh]
|
|
191
186
|
* 可訂閱,Aiia 的回應字幕
|
|
@@ -276,7 +271,7 @@ declare class AiiaSdk {
|
|
|
276
271
|
* If no Project is explicitly specified, please execute this method.
|
|
277
272
|
* @param project_id
|
|
278
273
|
*/
|
|
279
|
-
chooseProject(project_id:
|
|
274
|
+
chooseProject(project_id: AiiaProjectItemWithAvatar["id"]): void;
|
|
280
275
|
/**
|
|
281
276
|
* [zh]
|
|
282
277
|
* 不同於`this.volume = 0`的調整輸出音量
|
|
@@ -297,7 +292,6 @@ declare class AiiaSdk {
|
|
|
297
292
|
* Restore audio signal data
|
|
298
293
|
*/
|
|
299
294
|
unmute(): void;
|
|
300
|
-
private cloudToObj;
|
|
301
295
|
private behavior;
|
|
302
296
|
private channelCheck;
|
|
303
297
|
/**
|
|
@@ -310,7 +304,7 @@ declare class AiiaSdk {
|
|
|
310
304
|
onDestroy(): void;
|
|
311
305
|
}
|
|
312
306
|
|
|
313
|
-
export declare type AiiaState = "NotStart" | "
|
|
307
|
+
export declare type AiiaState = "NotStart" | "WaitingProjectID" | "ConnectingCloud" | "ReconnectingCloud" | "InService" | "InServiceNoMedia" | "NoPermissions" | "CloseService" | "Destroy";
|
|
314
308
|
|
|
315
309
|
declare interface ASR {
|
|
316
310
|
signal: "asr";
|
|
@@ -347,7 +341,7 @@ declare type AUDIO = {
|
|
|
347
341
|
|
|
348
342
|
export declare type BrowserInitOptions = Omit<InitOptions, "webserver" | "port" | "license" | "eyeTrackEnable" | "correction">;
|
|
349
343
|
|
|
350
|
-
declare type CLOUD_SOCKET_MSG = TTS | MIC | ASR | AGENT | OLD_ASR | OLD_AGENT | RATINGS | QUESTIONNAIRE |
|
|
344
|
+
declare type CLOUD_SOCKET_MSG = TTS | MIC | ASR | AGENT | OLD_ASR | OLD_AGENT | RATINGS | QUESTIONNAIRE | LAYOUT_2 | AUDIO | CV | MovementToUE;
|
|
351
345
|
|
|
352
346
|
declare interface CV {
|
|
353
347
|
signal: "cv";
|
|
@@ -375,10 +369,10 @@ declare interface InitOptions {
|
|
|
375
369
|
project?: string | Partial<AiiaProjectItem>;
|
|
376
370
|
/**
|
|
377
371
|
* [zh]
|
|
378
|
-
* 指定的環境,預設值:
|
|
372
|
+
* 指定的環境,預設值: developement
|
|
379
373
|
*
|
|
380
374
|
* [en]
|
|
381
|
-
* Specific Env, default:
|
|
375
|
+
* Specific Env, default: developement
|
|
382
376
|
*/
|
|
383
377
|
env?: "developement" | "production";
|
|
384
378
|
/**
|
|
@@ -450,15 +444,17 @@ declare interface InitOptions {
|
|
|
450
444
|
*/
|
|
451
445
|
llmSampleRate?: number;
|
|
452
446
|
/**
|
|
447
|
+
* @deprecated
|
|
448
|
+
*
|
|
453
449
|
* [zh]
|
|
454
|
-
* 人臉辨識
|
|
450
|
+
* 人臉辨識(已棄用,請改用 `cameraDetection`),同時設定會以 `cameraDetection` 為主
|
|
455
451
|
*
|
|
456
452
|
* @property perSecond: 每秒檢測幾次, 預設: 每秒 3 次
|
|
457
453
|
*
|
|
458
454
|
* @property confidence: 分數(0 ~ 100), 預設: 80
|
|
459
455
|
*
|
|
460
456
|
* [en]
|
|
461
|
-
*
|
|
457
|
+
* Face recognition (deprecated, please use `cameraDetection` instead), and the settings will be based on `cameraDetection`
|
|
462
458
|
*
|
|
463
459
|
* @property perSecond: Detection times per second, default: 3 times per second
|
|
464
460
|
*
|
|
@@ -468,6 +464,25 @@ declare interface InitOptions {
|
|
|
468
464
|
perSecond?: number;
|
|
469
465
|
confidence?: number;
|
|
470
466
|
};
|
|
467
|
+
/**
|
|
468
|
+
* [zh]
|
|
469
|
+
* 攝影機辨識設定
|
|
470
|
+
*
|
|
471
|
+
* @property perSecond: 每秒檢測幾次, 預設: 每秒 3 次
|
|
472
|
+
*
|
|
473
|
+
* @property confidence: 人臉辨識分數(0 ~ 100), 預設: 80
|
|
474
|
+
*
|
|
475
|
+
* [en]
|
|
476
|
+
* Camera Identification Settings
|
|
477
|
+
*
|
|
478
|
+
* @property perSecond: Detection times per second, default: 3 times per second
|
|
479
|
+
*
|
|
480
|
+
* @property confidence: Face recognition score(0 ~ 100), default: 80
|
|
481
|
+
*/
|
|
482
|
+
cameraDetection?: {
|
|
483
|
+
perSecond?: number;
|
|
484
|
+
confidence?: number;
|
|
485
|
+
};
|
|
471
486
|
/**
|
|
472
487
|
* [zh]
|
|
473
488
|
* 清除字幕,可指定時間(毫秒),預設 5000 毫秒後清除
|
|
@@ -507,7 +522,13 @@ declare interface InitOptions {
|
|
|
507
522
|
|
|
508
523
|
export declare function initSdk(options?: BrowserInitOptions): AiiaSdk;
|
|
509
524
|
|
|
510
|
-
declare
|
|
525
|
+
declare type LandmarkResult = Landmark[][];
|
|
526
|
+
|
|
527
|
+
export declare type LAYOUT = aiia_msg.LAYOUT["content"] | {
|
|
528
|
+
type: "resetLayout";
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
declare interface LAYOUT_2 {
|
|
511
532
|
signal: "layout";
|
|
512
533
|
content: {
|
|
513
534
|
type: "mount";
|
|
@@ -538,9 +559,10 @@ declare class MediaManager {
|
|
|
538
559
|
private audioManager?;
|
|
539
560
|
private vadManager?;
|
|
540
561
|
private camManager?;
|
|
541
|
-
private
|
|
562
|
+
private destroy;
|
|
542
563
|
private pcmSub;
|
|
543
564
|
private detectionsSub;
|
|
565
|
+
private landmarksSub;
|
|
544
566
|
get sampleRate(): number;
|
|
545
567
|
get pcm(): Observable<Float32Array<ArrayBufferLike>>;
|
|
546
568
|
get detections(): Observable< {
|
|
@@ -548,6 +570,7 @@ declare class MediaManager {
|
|
|
548
570
|
clientWidth: number;
|
|
549
571
|
clientHeight: number;
|
|
550
572
|
}>;
|
|
573
|
+
get landmarks(): Observable<aiia_msg.LandmarkResult>;
|
|
551
574
|
get state(): MediaStateEnum;
|
|
552
575
|
set state(v: MediaStateEnum);
|
|
553
576
|
get stateObs(): Observable<MediaStateEnum>;
|
|
@@ -555,7 +578,7 @@ declare class MediaManager {
|
|
|
555
578
|
init(workletUrl: string, config?: {
|
|
556
579
|
llmSampleRate?: AiiaConfig["llmSampleRate"];
|
|
557
580
|
chunkTimeInSeconds?: AiiaConfig["chunkTimeInSeconds"];
|
|
558
|
-
|
|
581
|
+
cameraDetection?: AiiaConfig["detection"];
|
|
559
582
|
}): Promise<void>;
|
|
560
583
|
addAudioQueue(input: {
|
|
561
584
|
buffer: AudioBuffer;
|
|
@@ -591,6 +614,15 @@ declare interface MIC {
|
|
|
591
614
|
};
|
|
592
615
|
}
|
|
593
616
|
|
|
617
|
+
declare interface MovementToUE {
|
|
618
|
+
signal: "osc";
|
|
619
|
+
content: {
|
|
620
|
+
host: string;
|
|
621
|
+
port: number;
|
|
622
|
+
message: [string, ...(string | number)[]];
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
|
|
594
626
|
/** @deprecated */
|
|
595
627
|
declare interface OLD_AGENT {
|
|
596
628
|
signal: "response";
|
|
@@ -643,7 +675,7 @@ declare type SendMessage = {
|
|
|
643
675
|
request: "reset";
|
|
644
676
|
} | {
|
|
645
677
|
request: "skip";
|
|
646
|
-
} | SendMessage_Layout | SendMessage_MiniGame | SendMessage_Audio | SendMessage_Face;
|
|
678
|
+
} | SendMessage_Layout | SendMessage_MiniGame | SendMessage_Audio | SendMessage_Face | SendMessage_Pose;
|
|
647
679
|
|
|
648
680
|
declare type SendMessage_Audio = SendMessage_Audio_Init | SendMessage_Audio_PCM;
|
|
649
681
|
|
|
@@ -691,6 +723,11 @@ declare interface SendMessage_Origin {
|
|
|
691
723
|
content?: any;
|
|
692
724
|
}
|
|
693
725
|
|
|
726
|
+
declare interface SendMessage_Pose {
|
|
727
|
+
request: "pose_detect";
|
|
728
|
+
content: LandmarkResult;
|
|
729
|
+
}
|
|
730
|
+
|
|
694
731
|
declare type Signal = CLOUD_SOCKET_MSG["signal"];
|
|
695
732
|
|
|
696
733
|
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,cloneDeep as r,find as c}from"lodash-es";import u from"moment";import"mathjs";import{Subject as d,fromEvent as h,takeUntil as l,take as p,ReplaySubject as b,BehaviorSubject as m,debounceTime as g,map as f}from"rxjs";import{io as S}from"socket.io-client";import{DrawingUtils as _,PoseLandmarker as y,FilesetResolver as C,FaceDetector as w}from"@mediapipe/tasks-vision";var v;!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(v||(v={}));const x="undefined"!=typeof window;"undefined"!=typeof process&&null!=process.versions&&process.versions.node;class k{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e(t,"id",void 0),i=e(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof i?i:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return t({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return t({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e(this.config,t,void 0)}}var R;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(R||(R={}));const E={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},D=[];const O=new Proxy(E,{get(e,t,i){if("string"==typeof t)switch(t){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...i)=>{const a=u().format("yyyy-MM-DD[T]HH:mm:ss:sss");s(D,e=>{e.next(t,a,...i)}),e[t](`[Aiia::${t}_${a}_]`,...i)};default:return}},set:(e,t,s,i)=>!0});class N{socket;messageSub;destorySub;get message(){return this.messageSub.asObservable()}constructor(e){this.destorySub=new d,this.messageSub=new d;const{specific:s,id:i}=e.project;this.socket=S(e.ws_url,t({autoConnect:!1},s&&void 0!==i?{query:{uuid:i}}:{}))}start(){const e="list",t="cloud",s="channel";h(this.socket,"connect").pipe(l(this.destorySub)).subscribe(()=>{O.debug("Client ID: ",this.socket.id)}),h(this.socket,e).pipe(p(1),l(this.destorySub)).subscribe(t=>{this.messageSub.next({event:e,data:t})}),h(this.socket,t).pipe(l(this.destorySub)).subscribe(e=>{this.messageSub.next({event:t,data:e})}),h(this.socket,s).pipe(l(this.destorySub)).subscribe(e=>{this.messageSub.next({event:s,data:e})}),this.socket.connect()}send(e,t){this.socket.emit(e,t)}sendToCloud(e){this.send("cloud",e)}onDestroy(){this.destorySub.next(),this.destorySub.complete(),this.messageSub.complete(),this.socket.disconnect()}}var M,T;function A(e,t,s){return Math.max(s,Math.min(e,t))}function j(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=t.getInt16(2*i,!0)/32768;s.push(e)}return new Float32Array(s)}function I(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=A(32768*t.getFloat32(4*i,!0),32767,-32768);s.push(e)}return s}!function(e){e[e.unknow=0]="unknow",e[e.allowed=1]="allowed",e[e.rejected=2]="rejected"}(M||(M={}));class F{audioContext;type="audio";currentNode;bufferQueue;gainNode;set volume(e){e<0&&(e=0),e>100&&(e=100),this.gainNode.gain.value=e/100}get volume(){return Math.round(100*this.gainNode.gain.value)}constructor(e){this.audioContext=e,this.bufferQueue=[],this.currentNode=null,this.gainNode=this.audioContext.createGain(),this.gainNode.connect(this.audioContext.destination)}addBuffer(e){this.bufferQueue.push(e)}addBufferByFloat32(e,t=1){const s=e.length,i=this.audioContext.sampleRate,a=this.audioContext.createBuffer(t,s,i),n=e.slice();for(let e=0;e<t;e++)a.copyToChannel(n,e);this.addBuffer(a)}addBufferByInt16(e,t=1){this.addBufferByFloat32(j(e),t)}startSpeech(){null===this.currentNode&&this.continue()}pauseSpeech(){this.audioContext.suspend().catch(e=>{O.error("pause speech fail",e)})}resumeSpeech(){this.audioContext.resume().catch(e=>{O.error("resume speech fail",e)})}nextSpeech(){this.bufferQueue.length>0&&(this.clearNode(),this.continue())}stopSpeech(){this.bufferQueue=[],this.clearNode()}clearNode(){this.currentNode&&(this.currentNode.onended=function(){},this.currentNode.stop(),this.currentNode.disconnect(),this.currentNode=null)}continue(){const e=this.bufferQueue.shift();if(e){this.currentNode=this.audioContext.createBufferSource(),this.currentNode.buffer=e,this.currentNode.connect(this.gainNode);const t=this.continue.bind(this);this.currentNode.onended=t,this.currentNode.start()}else this.clearNode()}}class P{stream;audioContext;type="vad";workletNode;source;isRecording=!1;outputSampleRate;chunkTimeInSeconds;pcmSub;get pcm(){return this.pcmSub.asObservable()}constructor(e,t,s){this.stream=e,this.audioContext=t,this.outputSampleRate=s?.llmSampleRate??16e3,this.chunkTimeInSeconds=s?.chunkTimeInSeconds??1,this.pcmSub=new d}async startRecord(){this.isRecording||(this.source=this.audioContext.createMediaStreamSource(this.stream),this.workletNode=new AudioWorkletNode(this.audioContext,"aiia-vad",{processorOptions:{outputSampleRate:this.outputSampleRate,chunkTimeInSeconds:this.chunkTimeInSeconds}}),this.source.connect(this.workletNode),this.workletNode.port.onmessage=e=>{const{float32:t}=e.data;t&&this.pcmSub.next(new Float32Array(t))},this.isRecording=!0)}stopRecord(){this.isRecording&&(this.workletNode&&(this.workletNode.port.onmessage=null,this.workletNode.disconnect()),this.source&&this.source.disconnect(),this.isRecording=!1)}}class W{type="camera";aiiaCamera;get detections(){return this.aiiaCamera.detections}get landmarks(){return this.aiiaCamera.landmarks}get videoSize(){return this.aiiaCamera.videoSize}constructor(e){const{perSecond:s}=t({perSecond:3},e);this.aiiaCamera=new B,this.aiiaCamera.setFrequency(s),document.body.append(this.aiiaCamera)}async init(e){try{const t=await C.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.0/wasm"),[s,i]=await Promise.all([w.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite",delegate:"GPU"},runningMode:"VIDEO"}),y.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task",delegate:"GPU"},runningMode:"VIDEO",numPoses:5})]);this.aiiaCamera.setStream(e),this.aiiaCamera.setFaceDetector(s),this.aiiaCamera.setPoseLandmarker(i)}catch(e){O.error("Camera init fail",e)}}startCapture(){this.aiiaCamera.startCapture()}stopCapture(){this.aiiaCamera.stopCapture()}onDestroy(){this.aiiaCamera.remove()}}class B extends HTMLElement{static get observedAttributes(){return["debug"]}_shadow;_containerEl;_styleEl;_videoEl;_canvasEl;_canvasCtx;_isDebug;_debugElements;_interval;_lastTimestamp;_loopFrameID;_faceDetector;_detectionsSub;_poseLandmarker;_landmarksSub;_isReady;_drawingUtils;landmarkColors=["gray","lightgray","gray","gray","lightgray"];connectorColors=["green","blue","red","yellow","purple"];get detections(){return this._detectionsSub.asObservable()}get landmarks(){return this._landmarksSub.asObservable()}get videoSize(){return{width:this._videoEl.videoWidth,height:this._videoEl.videoHeight}}constructor(){super(),this.id="AiiaCamera",this._isDebug=!1,this._isReady=!1,this._shadow=this.attachShadow({mode:"open"}),this._containerEl=document.createElement("div"),this._videoEl=document.createElement("video"),this._styleEl=document.createElement("style"),this._canvasEl=document.createElement("canvas"),this._debugElements=[],this._loopFrameID=null,this._detectionsSub=new d,this._landmarksSub=new d,this._interval=Math.ceil(1e3/3),this._lastTimestamp=0,this.predictWebcam=this.predictWebcam.bind(this),this._settingContainer(),this._settingStyle(),this._settingVideo(),this._containerEl.appendChild(this._videoEl),this._shadow.appendChild(this._styleEl),this._shadow.appendChild(this._containerEl),this._containerEl.appendChild(this._canvasEl);const e=this._canvasEl.getContext("2d");e?(this._canvasCtx=e,this._drawingUtils=new _(this._canvasCtx)):(this._canvasCtx=null,this._drawingUtils=null)}connectedCallback(){this.startCapture()}disconnectedCallback(){this.stopCapture()}attributeChangedCallback(e,t,s){"debug"===e&&(this._isDebug=null!==s&&("true"===s||""===s),this._containerEl.style.visibility=this._isDebug?"visible":"hidden")}setStream(e){this._videoEl.onloadedmetadata=()=>{this._isReady=!0},this._videoEl.srcObject=e}setFaceDetector(e){this._faceDetector=e}setPoseLandmarker(e){this._poseLandmarker=e}setFrequency(e){this._interval=Math.ceil(1e3/e)}_settingStyle(){this._styleEl.textContent="\n .aiia_container {\n visibility: hidden;\n position: fixed;\n top: 0;\n left: 0;\n\n > canvas {\n position: absolute;\n top: 0;\n left: 0;\n width: 640px;\n height: 480px;\n }\n }\n .aiia_container .highlighter {\n background: rgba(0, 255, 0, 0.25);\n border: 1px dashed #fff;\n z-index: 1;\n position: absolute;\n }\n .aiia_container .confidence {\n position: absolute;\n padding-bottom: 5px;\n padding-top: 5px;\n background-color: #007f8b;\n color: #fff;\n border: 1px dashed rgba(255, 255, 255, 0.7);\n z-index: 2;\n font-size: 12px;\n margin: 0;\n }\n "}_settingContainer(){this._containerEl.classList.add("aiia_container")}_settingVideo(){this._videoEl.autoplay=!0,this._videoEl.playsInline=!0,this._videoEl.muted=!0}getVideo(){return this._videoEl}startCapture(){null===this._loopFrameID&&(this._videoEl.play(),this._loopFrameID=window.requestAnimationFrame(this.predictWebcam))}stopCapture(){null!==this._loopFrameID&&(window.cancelAnimationFrame(this._loopFrameID),this._loopFrameID=null),this._videoEl.pause()}async predictWebcam(){if(this._isReady){const e=performance.now();if(this._lastTimestamp+this._interval<=e){this._lastTimestamp=e;const t=this._faceDetector?.detectForVideo(this._videoEl,e).detections,s=this._poseLandmarker?.detectForVideo(this._videoEl,e).landmarks;t&&(this._isDebug&&this._drawRect(t),this._detectionsSub.next(t)),s&&(this._isDebug&&this._drawRect2(s),this._landmarksSub.next(s))}}this._loopFrameID=window.requestAnimationFrame(this.predictWebcam)}_drawRect(e){s(e,(e,t)=>{const{categories:s,boundingBox:i}=e;if(i){let e=this._debugElements[2*t],a=this._debugElements[2*t+1];e&&a||(e=document.createElement("div"),e.classList.add("highlighter"),a=document.createElement("p"),a.classList.add("confidence"),this._containerEl.append(a,e),this._debugElements.push(e,a)),e.style.display="block",a.style.display="block";const n=100*s[0].score;a.innerText=`Confidence: ${Math.round(n)} %`,a.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",a.style.top=i.originY-30+"px",a.style.width=i.width-10+"px",e.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",e.style.top=`${i.originY}px`,e.style.width=i.width-10+"px",e.style.height=`${i.height}px`}});for(let t=2*e.length;t<this._debugElements.length;t++)this._debugElements[t].style.display="none"}_drawRect2(e){this._canvasCtx&&this._drawingUtils&&(this._canvasCtx.save(),this._canvasCtx.clearRect(0,0,640,480),s(e,(e,t)=>{this._canvasCtx?.save(),this._drawingUtils?.drawLandmarks(e,{radius:e=>_.lerp(e.from.z,-.15,.1,5,1),color:this.landmarkColors[t]}),this._drawingUtils?.drawConnectors(e,y.POSE_CONNECTIONS,{color:this.connectorColors[t],lineWidth:2})}),this._canvasCtx.restore())}destroy(){this._detectionsSub.complete(),this.remove()}}customElements.define("aiia-camera",B),function(e){e[e.padding=0]="padding",e[e.rejected=1]="rejected",e[e.loadfail=2]="loadfail",e[e.allowed=3]="allowed"}(T||(T={}));class V{stream;_state;stateSub;audioCtx;audioManager;vadManager;camManager;destroy;pcmSub;detectionsSub;landmarksSub;get sampleRate(){return this.audioCtx?this.audioCtx.sampleRate:16e3}get pcm(){return this.pcmSub.asObservable()}get detections(){return this.detectionsSub.asObservable()}get landmarks(){return this.landmarksSub.asObservable()}get state(){return this._state}set state(e){e!==this._state&&(this._state=e,this.stateSub.next(e))}get stateObs(){return this.stateSub.asObservable()}constructor(e){this.stream=e,this.stateSub=new b(1),this.pcmSub=new d,this.destroy=new d,this.detectionsSub=new d,this.landmarksSub=new d,this.state=T.padding}async init(e,t){const s=this.stream??await async function(e){try{return await navigator.mediaDevices.getUserMedia(e)}catch(e){return null}}({audio:{echoCancellation:!0,noiseSuppression:!0},video:!0});if(null===s)throw this.state=T.rejected,new Error(v["未獲得媒體裝置權限"]);this.stream=s,this.audioCtx=new AudioContext;try{await this.audioCtx.audioWorklet.addModule(e)}catch(e){throw O.fatal(e),this.state=T.loadfail,new Error(v["Worklet模組載入失敗"])}this.state=T.allowed,this.audioManager=new F(this.audioCtx),this.vadManager=new P(s,this.audioCtx,t),this.vadManager.pcm.pipe(l(this.destroy)).subscribe(e=>{this.pcmSub.next(e)});const i=new W(t?.cameraDetection);i.detections.pipe(l(this.destroy)).subscribe(e=>{const{width:t,height:s}=i.videoSize;this.detectionsSub.next({detections:e,clientWidth:t,clientHeight:s})}),i.landmarks.pipe(l(this.destroy)).subscribe(e=>{this.landmarksSub.next(e)}),i.init(s),this.camManager=i}addAudioQueue(t){const s=e(t,"buffer",void 0),i=e(t,"float32",void 0),a=e(t,"int16",void 0),n=e(t,"numberOfChannels",void 0);s?this.audioManager?.addBuffer(s):i?this.audioManager?.addBufferByFloat32(i,n):a&&this.audioManager?.addBufferByInt16(a,n)}playAudio(){this.audioManager?.startSpeech()}stopAudio(){this.audioManager?.stopSpeech()}startRecord(){this.vadManager?.startRecord()}stopRecord(){this.vadManager?.stopRecord()}setVolume(e){this.audioManager&&(this.audioManager.volume=e)}getVolume(){return this.audioManager?this.audioManager.volume:0}startCapture(){this.camManager?.startCapture()}stopCapture(){this.camManager?.stopCapture()}onDestroy(){this.camManager?.onDestroy(),this.audioCtx?.close(),this.destroy.next(),this.destroy.complete()}}class L{config;media;chat;destroySub;projectsSub;layoutSub;aiiaSubtitleSub;userSubtitleSub;sampleRateOfWhisper;channelState;stateSub;_currentState;isMuteAiia;aiiaSubtitleClearSub;userSubtitleClearSub;_lastProjects;guestSub;_hasGuest;mediaEventReady;isStart;get state(){return this.stateSub.asObservable()}get currentState(){return this._currentState}get layout(){return this.layoutSub.asObservable()}get projects(){return this.projectsSub.asObservable()}get lastProjects(){return r(this._lastProjects)}get aiiaSubtitle(){return this.aiiaSubtitleSub.asObservable()}get userSubtitle(){return this.userSubtitleSub.asObservable()}get guest(){return this.guestSub.asObservable()}get hasGuest(){return this._hasGuest}set volume(e){this.media.setVolume(e)}get volume(){return this.media.getVolume()}constructor(e,t){this.config=e,this.media=t,this.layoutSub=new d,this.projectsSub=new b(1),this.destroySub=new d,this.aiiaSubtitleSub=new d,this.userSubtitleSub=new d,this.aiiaSubtitleClearSub=new d,this.userSubtitleClearSub=new d,this.stateSub=new m("NotStart"),this.sampleRateOfWhisper={input:e.llmSampleRate,output:16e3},this.channelState={proxy:!1,cloud:!1},this.isMuteAiia=!1,this._lastProjects=[],this.guestSub=new b(1),this._hasGuest=!1,this.mediaEventReady=!1,this.isStart=!1,this.chat=new N(e),O.code(R["指定依賴已完成"],"chat"),this.close=this.onDestroy,this.behavior=this.behavior.bind(this);const{userDelayTime:s,aiiaDelayTime:i}=e.autoClearSubtitle;isNaN(i)||this.aiiaSubtitleClearSub.pipe(g(i),l(this.destroySub)).subscribe(()=>{this.aiiaSubtitleSub.next("")}),isNaN(s)||this.userSubtitleClearSub.pipe(g(s),l(this.destroySub)).subscribe(()=>{this.userSubtitleSub.next("")}),this.stateSub.pipe(l(this.destroySub)).subscribe(e=>{this._currentState=e}),this.projectsSub.pipe(l(this.destroySub)).subscribe(e=>{this._lastProjects=e}),this.guestSub.pipe(l(this.destroySub)).subscribe(e=>{this._hasGuest=e}),t.stateObs.pipe(l(this.destroySub)).subscribe(e=>{T.allowed===e&&this.channelCheck()})}start(){this.isStart?O.code(R["已啟動SDK"]):(this.isStart=!0,this.chat.message.pipe(l(this.destroySub)).subscribe(({event:e,data:t})=>{switch(e){case"cloud":n(t,this.behavior);break;case"list":{const{id:e,name:s,specific:i}=this.config.project;let a=null;if(i&&void 0!==e)a=e;else{const i=c(t,t=>t.name===s||t.id===e);i&&(a=i.id)}null!==a?this.chooseProject(a):null===t?(this.stateSub.next("CloseService"),O.error("No project ready or lose network")):(this.projectsSub.next(t),this.stateSub.next("WaitingProjectID"),O.code(R["請訂閱專案列表"]));break}case"channel":switch(this.channelState.proxy=!0,t){case"open":this.channelCheck();break;case"close":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("CloseService");break;case"no_permissions":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("NoPermissions");break;case"reconnect":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("ReconnectingCloud");break;case"net_error":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("CloseService"),O.error("Please check network status")}}}),this.chat.start(),this.stateSub.next("ConnectingCloud"),O.code(R["啟動SDK"]))}close;skip(){this.chat.sendToCloud({request:"skip"})}reset(){this.chat.sendToCloud({request:"reset"})}chooseProject(e){this.chat.send("uuid",e),this.stateSub.next("ConnectingCloud")}mute(){this.isMuteAiia=!0,this.media.stopAudio(),this.media.stopRecord()}unmute(){this.isMuteAiia=!1,this.media.startRecord()}behavior(t){switch(t.signal){case"layout":this.layoutSub.next(t.content);break;case"audio":if("sampleRate"===t.command)this.sampleRateOfWhisper.output=t.content;else switch(t.command){case"pcm":if(!this.isMuteAiia){const e=function(e,t,s){if(t===s)return e;const i=s/t,a=Math.ceil(e.length*i),n=new Float32Array(a);for(let t=0;t<a;t++){const s=t/i,a=Math.floor(s),o=s-a,r=e[a],c=e[Math.min(a+1,e.length-1)];n[t]=r+(c-r)*o}return n}(j(new Int16Array(t.content)),this.sampleRateOfWhisper.output,this.media.sampleRate);this.media.addAudioQueue({float32:e}),this.media.playAudio()}break;case"interrupted":this.media.stopAudio(),this.aiiaSubtitleSub.next("")}break;case"status":"Connected"===e(t,"content")&&(this.channelState.cloud=!0,this.channelCheck(),this.layoutSub.next({type:"resetLayout"}));break;case"agent":if(!this.isMuteAiia){const s=e(t,"content.function_result.response");void 0!==s&&(this.aiiaSubtitleSub.next(s),this.aiiaSubtitleClearSub.next())}break;case"asr":if(!this.isMuteAiia&&"ASR"===e(t,"content.function_type")){const s=e(t,"content.function_result.result");void 0!==s&&(this.userSubtitleSub.next(s),this.userSubtitleClearSub.next())}break;case"cv":{const s=e(t,"content.status");"user present"===s?this.guestSub.next(!0):"user leave"===s?this.guestSub.next(!1):O.warn("Unexpect value",e(t,"content"));break}default:O.debug(t.signal)}}channelCheck(){const{proxy:e,cloud:t}=this.channelState;if(e&&t&&this.media.state===T.allowed){if(this.chat.sendToCloud({request:"audio",command:"sampleRate",content:this.sampleRateOfWhisper.input}),!this.mediaEventReady){this.mediaEventReady=!0,this.media.pcm.pipe(l(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"audio",command:"pcm",content:I(e)})});const e=(s=this.config.detection.confidence,function(e,t){const{width:r,height:c}=t,u=i(e,e=>100*(e.categories[0]?.score??0)>=s),d=a(n(u,e=>{if(void 0!==e.boundingBox){const t=e.boundingBox,s=t.width*t.height,i=e.categories[0]?.score??0;return{...t,area:s,score:100*i,clientWidth:r,clientHeight:c}}return null}));return o(d,["area","score"])[0]??null});this.media.detections.pipe(l(this.destroySub),f(({detections:t,clientHeight:s,clientWidth:i})=>e(t,{width:i,height:s}))).subscribe(e=>{this.chat.send("face",e)}),this.media.landmarks.pipe(l(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"pose_detect",content:e})})}this.media.startRecord(),this.media.startCapture(),this.stateSub.next("InService"),O.code(R["通道、雲端服務、麥克風都已就緒"])}else e&&t&&this.media.state!==T.allowed&&(this.stateSub.next("InServiceNoMedia"),O.code(R["未取得媒體裝置權限,請重新設定或忽略此訊息"]));var s}onDestroy(){this.chat.onDestroy(),this.media.onDestroy(),this.destroySub.next(),this.destroySub.complete(),this.stateSub.next("Destroy"),this.stateSub.complete(),O.code(R["結束SDK"])}}function U(s){if(x){!function(e){const{info:t,error:s,warn:i,debug:a,fatal:n,code:o}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);o||(E.code=()=>{}),a||(E.debug=()=>{}),t||(E.info=()=>{}),i||(E.warn=()=>{}),s||(E.error=()=>{}),n||(E.fatal=()=>{})}(e(s,"debug",!0)),O.code(R["初始化必要依賴"]);const i=new k(t({},s));O.code(R["指定依賴已完成"],"config");const a=new V(i.mediaStream);O.code(R["指定依賴已完成"],"config");const n=new L(i,a);return O.code(R["指定依賴已完成"],"sdk"),""!==i.worklet_url&&a.init(i.worklet_url,i),n}throw new Error(v["環境錯誤(browser)"])}export{U as initSdk};
|
package/dist/node.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e,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"),o=require("rxjs"),n=require("axios"),i=require("ws"),a=require("fs"),c=require("os"),h=require("path"),l=require("node-osc"),u=require("child_process"),d=require("systeminformation"),p=require("@nut-tree-fork/nut-js"),f=require("readline"),g=require("net");function y(e){var t=Object.create(null);return e&&Object.keys(e).forEach(function(s){if("default"!==s){var r=Object.getOwnPropertyDescriptor(e,s);Object.defineProperty(t,s,r.get?r:{enumerable:!0,get:function(){return e[s]}})}}),t.default=e,Object.freeze(t)}var m,b=y(f);!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(m||(m={}));const S="access_token",_="undefined"!=typeof process&&null!=process.versions&&null!=process.versions.node;function w(e){return new Promise(t=>{setTimeout(t,e)})}class E{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e.get(t,"id",void 0),r=e.get(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof r?r:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return e.assign({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return e.assign({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e.get(this.config,t,void 0)}}var k;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(k||(k={}));const D={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},O=[];function N(e){return O.push(e),N}const R=new Proxy(D,{get(s,r,o){if("string"==typeof r)switch(r){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...o)=>{const n=t().format("yyyy-MM-DD[T]HH:mm:ss:sss");e.forEach(O,e=>{e.next(r,n,...o)}),s[r](`[Aiia::${r}_${n}_]`,...o)};default:return}},set:(e,t,s,r)=>!0});class C{config;server;memberSub;get port(){return this.config.port}get webserver(){return this.config.webserver}get memberChange(){return this.memberSub.asObservable()}constructor(e){this.config=e,this.memberSub=new o.Subject}start(){if(this.server=new r.Server(this.webserver,{cors:{origin:"*"}}),this.server.on("connection",e=>{R.debug("client on",e.id),this.memberSub.next({type:"in",socket:e}),e.on("disconnect",t=>{R.debug("client off",e.id,"reason",t),this.memberSub.next({type:"out",socket:e})})}),void 0===this.webserver){const e=this.port;this.server.listen(e),R.info("Websocket server run on",e)}else R.info("Websocket server rely your webserver.")}onDestroy(){this.server&&this.server.close()}}class A{instance;controller;constructor(e){this.controller=new AbortController,this.instance=n.create({baseURL:e,signal:this.controller.signal})}async getAccessToken(t,s){const r=performance.now(),o={license_number:t,token_value:s??""},n=await this.instance.post("/api/v1/license/agent_token",o).then(t=>e.get(t.data,"token_value","")).catch(()=>null);if(R.debug("Complete certification",`Took ${performance.now()-r} milliseconds.`),null===n)throw new Error(m["網絡錯誤"]);if(""===n)throw new Error(m["獲取權杖失敗"]);return n}async getProjectList(t){const s=performance.now(),r=await this.instance.post("/api/v1/license/projects",{license_number:t}).then(t=>e.map(e.get(t,"data.projects",[]),({id:e,sub_project_name:t,avatar:s})=>{const{name:r,url:o}=s;return{avatar:{name:r,url:o},id:e,name:t}})).catch(e=>null);if(R.debug("Got Project List",`Took ${performance.now()-s} milliseconds.`),null===r)throw new Error(m["網絡錯誤"]);if(0===r.length)throw new Error(m["沒有可用專案"]);return r}onDestroy(){this.controller.abort()}}var M;!function(e){e[e.MDN_NORMAL_CLOSURE=1e3]="MDN_NORMAL_CLOSURE",e[e.MDN_GOING_AWAY=1001]="MDN_GOING_AWAY",e[e.MDN_PROTOCOL_ERROR=1002]="MDN_PROTOCOL_ERROR",e[e.MDN_UNSUPPORTED_DATA=1003]="MDN_UNSUPPORTED_DATA",e[e.MDN_RESERVED_1004=1004]="MDN_RESERVED_1004",e[e.MDN_NO_STATUS_RECEIVED=1005]="MDN_NO_STATUS_RECEIVED",e[e.MDN_ABNORMAL_CLOSURE=1006]="MDN_ABNORMAL_CLOSURE",e[e.MDN_INVALID_FRAME_PAYLOAD=1007]="MDN_INVALID_FRAME_PAYLOAD",e[e.MDN_POLICY_VIOLATION=1008]="MDN_POLICY_VIOLATION",e[e.MDN_MESSAGE_TOO_BIG=1009]="MDN_MESSAGE_TOO_BIG",e[e.MDN_MANDATORY_EXTENSION=1010]="MDN_MANDATORY_EXTENSION",e[e.MDN_INTERNAL_ERROR=1011]="MDN_INTERNAL_ERROR",e[e.MDN_SERVICE_RESTART=1012]="MDN_SERVICE_RESTART",e[e.MDN_TRY_AGAIN_LATER=1013]="MDN_TRY_AGAIN_LATER",e[e.MDN_BAD_GATEWAY=1014]="MDN_BAD_GATEWAY",e[e.MDN_TLS_HANDSHAKE=1015]="MDN_TLS_HANDSHAKE",e[e.WHEN_BROWSER_CLOSE=4001]="WHEN_BROWSER_CLOSE",e[e.WS_4100_NORAML_CLOSURE=4100]="WS_4100_NORAML_CLOSURE",e[e.WS_4101_UNAUTHORIZED=4101]="WS_4101_UNAUTHORIZED",e[e.WS_4102_LICENSE_EXPIRED=4102]="WS_4102_LICENSE_EXPIRED",e[e.WS_4103_TOKEN_EXPIRED=4103]="WS_4103_TOKEN_EXPIRED",e[e.WS_4104_LLM_SESSION_ERROR=4104]="WS_4104_LLM_SESSION_ERROR",e[e.WS_4105_TOO_MANY_CONNECTIONS=4105]="WS_4105_TOO_MANY_CONNECTIONS"}(M||(M={}));class P{id;uuid;socket;messageSub;linkStartSub;destorySub;closeSub;isFinished;debugInfo;lastCloseLog;get message(){return this.messageSub.asObservable()}get close(){return this.closeSub.asObservable()}get linkStart(){return this.linkStartSub.asObservable()}get isConnected(){return this.socket?.readyState===i.OPEN}constructor(e){this.id=e,this.messageSub=new o.Subject,this.linkStartSub=new o.Subject,this.closeSub=new o.Subject,this.destorySub=new o.Subject,this.isFinished=!1,this.uuid=crypto.randomUUID(),this.debugInfo=`browser(${this.id}), proxy(${this.uuid})`,R.debug(`create proxy for browser(${this.id})`)}start(e){this.socket=new i(e,{autoPong:!0}),o.fromEvent(this.socket,"open").pipe(o.takeUntil(this.destorySub)).subscribe(()=>{R.code(k["啟動服務"],"proxy",this.debugInfo),this.linkStartSub.next()}),o.fromEvent(this.socket,"message").pipe(o.takeUntil(this.destorySub)).subscribe(e=>{const{data:t}=e;this.messageSub.next(t)}),o.fromEvent(this.socket,"error").pipe(o.takeUntil(this.destorySub)).subscribe(e=>{const{error:t}=e;R.error(this.debugInfo,"Proxy has some problem",t)}),o.fromEvent(this.socket,"close").pipe(o.takeUntil(this.destorySub)).subscribe(e=>{const{code:t,reason:s,type:r,wasClean:o}=e;switch(this.lastCloseLog=`最近一次關閉原因: ${s}, 錯誤碼: ${t}, Type: ${r}`,t){case M.WHEN_BROWSER_CLOSE:R.code(k["無副作用的關閉連線"],this.debugInfo),this.onDestroy();break;case M.MDN_NORMAL_CLOSURE:case M.MDN_GOING_AWAY:this.closeSub.next("close"),R.code(k["安全的關閉連線"],this.debugInfo);break;case M.MDN_NO_STATUS_RECEIVED:case M.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),R.code(k["與雲端的服務中斷"],this.debugInfo,{code:t,reason:s,type:r,wasClean:o});break;case M.WS_4100_NORAML_CLOSURE:case M.WS_4103_TOKEN_EXPIRED:case M.WS_4104_LLM_SESSION_ERROR:case M.WS_4105_TOO_MANY_CONNECTIONS:this.closeSub.next("reconnect"),R.code(k["自動重新連線雲端服務"],this.debugInfo,`socket code: ${t}`);break;case M.WS_4101_UNAUTHORIZED:case M.WS_4102_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),R.code(k["沒有授權,請聯繫Graphen"],this.debugInfo,`socket code: ${t}`);break;default:this.closeSub.next("close"),R.code(k["未預期的斷線,請聯繫Graphen"],this.debugInfo,{code:t,reason:s,type:r,wasClean:o})}})}reconnect(e){this.isFinished||(this.destorySub.next(),this.destorySub.complete(),this.destorySub=new o.Subject,this.start(e))}send(e){const s=`currentTime: ${t().format("yyyy-MM-DD[T]HH:mm:ss:sss")}`;this.isConnected?this.socket.send(e,t=>{t&&R.error("send message fail",this.debugInfo,s,e.slice(0,100),t)}):R.warn("The socket is not connected",this.debugInfo,s,e.slice(0,100))}sendToCloud(e){this.send(JSON.stringify(e))}onClose(){if(this.socket)switch(this.socket.readyState){case 0:R.debug("Once the cloud connection is complete, the connection will be disconnected."),this.linkStart.pipe(o.takeUntil(this.destorySub),o.take(1)).subscribe(()=>{this.socket?.close(M.WHEN_BROWSER_CLOSE)});break;case 1:R.debug("Close safely, no side effects"),this.socket.close(M.WHEN_BROWSER_CLOSE);break;case 2:case 3:R.debug("The connection have been lost.")}}getStatusInfo(){const e=(()=>{if(!this.socket)return"雲端服務尚未啟動";{const e=this.socket.readyState;switch(e){case 0:return"正在連線雲端服務中";case 1:return"雲端服務正常運作";case 2:return"正在關閉雲端服務";case 3:return"雲端服務已關閉";default:return`未知的 Websocket 狀態: ${e}`}}})(),t=this.lastCloseLog?`${e}, ${this.lastCloseLog}`:e;return`瀏覽器(${this.id}) - 雲端代理(${this.uuid}), ${t}`}onDestroy(){this.isFinished=!0,this.destorySub.next(),this.destorySub.complete(),R.debug(`Destroy cloud channel(${this.debugInfo})`)}}const T=new class{AppName;platform;isMAC;isWIN;isLINUX;saveFolder;homedir;appFolder;EOL;constructor(e){switch(this.AppName=e,this.platform=process.platform,this.homedir=c.homedir(),this.EOL=c.EOL,this.platform){case"darwin":this.isMAC=!0,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=h.join(this.homedir,"Library","Application Support");break;case"win32":this.isMAC=!1,this.isWIN=!0,this.isLINUX=!1,this.saveFolder=process.env.APPDATA||h.join(this.homedir,"AppData","Roaming");break;case"linux":this.isMAC=!1,this.isWIN=!1,this.isLINUX=!0,this.saveFolder=process.env.XDG_CONFIG_HOME||h.join(this.homedir,".config");break;default:console.log(`當前系統是未知的平台: ${this.platform}`),this.isMAC=!1,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=this.homedir}this.appFolder=h.join(this.saveFolder,this.AppName),this.folderCheck(this.appFolder)}addPath(...e){return h.join(...e)}folderCheck(e){a.mkdirSync(e,{recursive:!0})}createFolder(e){const t=this.addPath(this.appFolder,e);return this.folderCheck(t),t}fileCheck(e,t=""){a.existsSync(e)||a.writeFileSync(e,t,{encoding:"utf8"})}}("aiia-sdk");class v{type="node";filePath;fileName;constructor(){this.fileName="storage.json",this.filePath=T.addPath(T.appFolder,this.fileName),T.fileCheck(this.filePath)}getFileDataSync(){const e=a.readFileSync(this.filePath,"utf8");try{return JSON.parse(e)}catch(e){return{}}}setFileDataSync(e){try{const t=JSON.stringify(e);a.writeFileSync(this.filePath,t,"utf8")}catch(e){}}async getFileData(){return new Promise(e=>{a.readFile(this.filePath,"utf8",(t,s)=>{if(t)e({});else try{e(JSON.parse(s))}catch(t){e({})}})})}async setFileData(e){return new Promise(t=>{try{const s=JSON.stringify(e);a.writeFile(this.filePath,s,"utf8",e=>{t()})}catch(e){}})}async getItem(e){return(await this.getFileData())[e]??null}async setItem(e,t){const s=await this.getFileData();s[e]=t,await this.setFileData(s)}async removeItem(t){const s=await this.getFileData(),r=e.omit(s,t);await this.setFileData(r)}async clear(){await this.setFileData({})}getItemSync(e){return this.getFileDataSync()[e]??null}setItemSync(e,t){const s=this.getFileDataSync();s[e]=t,this.setFileDataSync(s)}removeItemSync(t){const s=e.omit(this.getFileDataSync(),t);this.setFileDataSync(s)}clearSync(){this.setFileDataSync({})}}const x={[k["通道、雲端服務、麥克風都已就緒"]]:"通道、雲端服務、麥克風都已就緒",[k["未取得媒體裝置權限,請重新設定或忽略此訊息"]]:"未取得媒體裝置權限,請重新設定或忽略此訊息",[k["請訂閱專案列表"]]:"請訂閱專案列表",[k["初始化必要依賴"]]:"初始化必要依賴",[k["指定依賴已完成"]]:"指定依賴已完成",[k["啟動SDK"]]:"啟動SDK",[k["結束SDK"]]:"結束SDK",[k["已啟動SDK"]]:"已啟動SDK",[k["安全的關閉連線"]]:"安全的關閉連線",[k["無副作用的關閉連線"]]:"無副作用的關閉連線",[k["與雲端的服務中斷"]]:"與雲端的服務中斷",[k["沒有授權,請聯繫Graphen"]]:"沒有授權,請聯繫Graphen",[k["自動重新連線雲端服務"]]:"自動重新連線雲端服務",[k["未預期的斷線,請聯繫Graphen"]]:"未預期的斷線,請聯繫Graphen",[k["開始初始化必要模組"]]:"開始初始化必要模組",[k["指定模組初始化完成"]]:"指定模組初始化完成",[k["啟動服務"]]:"啟動服務",[k["服務結束"]]:"服務結束",[m["網絡錯誤"]]:"網絡錯誤",[m["環境錯誤(browser)"]]:"環境錯誤",[m["Worklet模組載入失敗"]]:"Worklet模組載入失敗",[m["未獲得媒體裝置權限"]]:"未獲得媒體裝置權限",[m["實例已摧毀"]]:"實例已摧毀",[m["環境錯誤(nodejs)"]]:"nodejs",[m["未提供憑證"]]:"未提供憑證",[m["獲取權杖失敗"]]:"獲取權杖失敗",[m["沒有可用專案"]]:"沒有可用專案",[m["重複初始化"]]:"重複初始化"},I=new RegExp(/^(ERROR )?CODE: [0-9]{3,4}$/);class L{path;constructor(e){this.path=e?T.createFolder(e):T.appFolder}next(e,s,...r){const o=`log_${t().format("yyyy-MM-DD")}.txt`,n=T.addPath(this.path,o),i="code"===e?e=>I.test(e)?`${e}(${x[e]})`:e:e=>e;try{const t=r.reduce((e,t)=>{if(t instanceof Error){let s=`Error[${t.message??"No Message"}]${T.EOL}`;return s+=t.stack??"unknow stack","string"==typeof t.cause&&(s+=`${T.EOL}cause: ${t.cause}`),`${e}${T.EOL}${s}${T.EOL}`}switch(typeof t){case"string":return`${e} ${i(t)}`;case"number":case"bigint":return`${e} ${t.toString()}`;case"boolean":return`${e} ${t?"true":"false"}`;case"object":try{return`${e} ${JSON.stringify(t)}`}catch(t){return e}case"symbol":case"undefined":case"function":return`${e} [${typeof t}]`}},"");a.appendFileSync(n,`LOG(${e})__${s}__::${t}${T.EOL}`,{encoding:"utf8"})}catch(e){}}}const $="127.0.0.1",U={AvatarPort:"9876",ActionPort:"6969",LocationPort:"4060",LookPort:"6745",BackGroundPort:"2400"},W={StreamAudioPort:"8080",CleanAudioPort:"8081"};function F(){return{...U,...W}}class j{clientMap;eyeBallUdp;x_axis_px;y_axis_px;defPortMap;constructor(){this.clientMap=new Map,this.x_axis_px=0,this.y_axis_px=0,this.defPortMap={};const t=e.pick(U,"AvatarPort","ActionPort","LocationPort","BackGroundPort");e.forEach(e.keys(t),e=>{const s=e,r=t[s];this.defPortMap[r]=s})}enableUDPClient(t){e.forEach(e.keys(t),e=>{const s=new l.Client($,t[e]);this.clientMap.set(e,s)})}enableEyeTrack(e){this.eyeBallUdp=new l.Client($,e.port),this.x_axis_px=e.x_axis_px,this.y_axis_px=e.y_axis_px}eyeTrack(t){if(void 0!==this.eyeBallUdp){const r=null!==t,{x:o,y:n}=(()=>{if(r){const{x:r,y:o}=function(t,r){const{maxX:o,maxY:n,minX:i,minY:a}=e.assign({maxX:25,minX:-25,maxY:50,minY:-50},r),c=s.abs(o)+s.abs(i),h=s.abs(n)+s.abs(a),{clientHeight:l,clientWidth:u,originX:d,originY:p,width:f,height:g}=t,y=s.matrix([[c/u,0,i],[0,h/l,a],[0,0,1]]),m=d+f/2,b=l-(p+g/2),S=s.matrix([[m],[b],[1]]),_=s.multiply(y,S);return{x:_.get([0,0]),y:_.get([1,0])}}(t);return{x:this.toFloat(r+this.x_axis_px),y:this.toFloat(o+this.y_axis_px)}}return{x:this.toFloat(7),y:this.toFloat(20)}})();this.eyeBallUdp.send(["/",o,n],e=>{null!=e&&R.error("Eye track error:",e)})}}movement(t){e.map(t,({content:t})=>{const s=e.get(t,"port",""),r=e.get(t,"message"),o=this.defPortMap[s.toString()];this.clientMap.has(o)&&this.clientMap.get(o).send(r,e=>{null!=e&&R.error("Occur error when send message to UE",e)})})}toFloat(t){return e.isInteger(t)?t+1e-5:t}onDestroy(){e.forEach(Array.from(this.clientMap),([e,t])=>{t.close()}),R.info("Close all UnrealEngine's UDP")}}class G{destroy;speechWS;interruptWS;get speechIsConnected(){return this.speechWS?.readyState===i.OPEN}get intrIsConnected(){return this.interruptWS?.readyState===i.OPEN}constructor(){this.destroy=new o.Subject}connectSocket(e){this.speechWS=this.getSocket(e.speech,"speech"),this.interruptWS=this.getSocket(e.interrupt,"interrupt")}getSocket(e,t){const s=new i(`ws://${$}:${e}`);return s.onerror=s=>{const{message:r,error:o,type:n}=s;R.error(`UE Tool(${t}) fail: ${r}`,o,{port:e,type:n})},s.onclose=s=>{const{code:r,reason:o,type:n}=s;R.debug(`UE Tool(${t}) close`,{code:r,reason:o,type:n,port:e})},s}interrupted(){this.intrIsConnected&&this.interruptWS.send("interrupted")}speech(e){if(this.speechIsConnected){const t=Buffer.allocUnsafe(2*e.length);for(let s=0;s<e.length;s++)t.writeInt16LE(e[s],2*s);this.speechWS.send(t)}}onDestroy(){this.speechWS?.close(),this.interruptWS?.close(),this.destroy.next(),this.destroy.complete(),R.info("Close all UnrealEngine's WebSocket")}}class B{config;udpManager;socketManager;movement;eyeTrack;interrupted;speech;constructor(e){this.config=e,this.udpManager=new j,this.socketManager=new G,this.movement=()=>{},this.eyeTrack=()=>{},this.interrupted=()=>{},this.speech=()=>{}}onStart(t){const s={},r=F(),o=e.assign(F(),t);e.forEach(e.keys(r),e=>{const t=o[e],n=Number(t);s[e]=isNaN(n)?Number(r[e]):n});const{StreamAudioPort:n,CleanAudioPort:i,LookPort:a,...c}=s;this.udpManager.enableUDPClient(c),this.socketManager.connectSocket({speech:n,interrupt:i}),this.movement=this.udpManager.movement.bind(this.udpManager),this.eyeTrack=this.udpManager.eyeTrack.bind(this.udpManager),this.interrupted=this.socketManager.interrupted.bind(this.socketManager),this.speech=this.socketManager.speech.bind(this.socketManager),this.config.eyeTrackEnable&&this.udpManager.enableEyeTrack({...this.config.eyeTrackCorrection,port:a})}onDestroy(){this.udpManager.onDestroy(),this.socketManager.onDestroy(),R.info("UE tools destroy")}}async function Y(e){return new Promise(t=>{a.stat(e,e=>{t(e?[!1,e]:[!0])})})}async function H(e,t,...s){return T.isWIN?new Promise(r=>{const o=[`cd ${e} && ${t} `,...s].join(" "),n=u.exec(o);n.once("error",e=>{r([!1,e])}),n.once("spawn",()=>{n.unref(),r([!0])})}):Promise.resolve([!1,new Error("The function isn't implement in this system")])}async function K(){try{return await d.networkConnections()}catch(e){return console.error("檢查 Port 時發生錯誤:",e),[]}}async function X(t){const s=t.toString(),r=await K();return e.some(r,e=>e.localPort===s)}async function q(e){const t=await p.getWindows();let s=null;const r=new RegExp(e.toLocaleLowerCase());for(const e of t){const t=await e.title;if(r.test(t.toLocaleLowerCase())){s=e;break}}return s}function V(e){return[2e3,2e3,2e3,1e3,1e3,2e3,3e3,5e3][e]??[6e3,4e3][e%2]}function J(){if(T.isWIN){const e="UEProject_5_6";return{proc:e,name:`${e}.exe`}}return null}class Z{config;ueConfig;destroySub;constructor(t){this.destroySub=new o.Subject;const s={isKiosk:!1,browserKeeping:!1},{unrealFolder:r,openUrl:n,mode:i,foreground:a,remoteDebug:c}=e.assign({},t);if("string"==typeof r&&""!==r.trim()){s.path=r;const e=J();e&&(this.ueConfig=new z(r,e.proc))}"string"==typeof n&&""!==n.trim()&&(s.url=n),"string"==typeof i&&""!==i.trim()&&(s.isKiosk="kiosk"===i),"string"==typeof a&&""!==a.trim()&&(s.browserKeeping="always"===a),"number"==typeof c&&(s.remoteDebug=c),this.config=s}async exec(){let e=!1,t=await this.isUnrealRunning();t||(this.ueConfig&&await this.ueConfig.getSafePortTable(),t=await this.openUnreal()),t&&(e=await this.isUnrealInService()),e&&await this.openBrowser();return this.ueConfig?await this.ueConfig.getPortTable():F()}async isUnrealRunning(){const t=J();if(null===t)return!1;const{proc:s}=t,r=await async function(...e){const t=e.length>0?e.join(", "):"*";return await d.processLoad(t).catch(()=>[])}(s);if(r.length<=0)return!1;const o=s.toLocaleLowerCase(),n=e.find(r,e=>e.proc.toLocaleLowerCase()===o);return!!(n&&"number"==typeof n.pid&&n.pids.length>=2)}async isUnrealInService(e=12){let t=!1,s=0;const{StreamAudioPort:r}=this.ueConfig?await this.ueConfig.getPortTable():F();for(;!t&&s<e;)t=await X(r),await w(V(s)),s++;return t}async openUnreal(){const e=J();if(null===e)return!1;const{path:t}=this.config;if(void 0===t)return!1;const s=T.addPath(t,e.name),[r,o]=await Y(s);if(!r)return R.error(o),!1;const[n,i]=await async function(e,...t){return T.isWIN?new Promise(s=>{const r=u.spawn(e,t,{detached:!0,stdio:"ignore"});r.once("error",e=>{s([!1,e])}),r.once("spawn",()=>{r.unref(),s([!0])})}):Promise.resolve([!1,new Error("The function isn't implement in this system")])}(s);return i&&R.error(i),n}async openBrowser(){const{url:e,isKiosk:t,remoteDebug:s}=this.config;if(void 0===e)return!1;const r=T.isWIN?{edge:["C:/Program Files (x86)/Microsoft/Edge/Application","msedge.exe"],chrome:["C:/Program Files/Google/Chrome/Application","chrome.exe"],chrome2:[`${T.homedir}/AppData/Local/Google/Chrome/Application`,"chrome.exe"]}:null;if(null===r)return!1;const{proc:o}=J(),{chrome:n,chrome2:i,edge:a}=r,[[c],[h],[l]]=await Promise.all([Y(T.addPath(...n)),Y(T.addPath(...a)),Y(T.addPath(...i))]);if(c||l){const r=[];if(t&&(r.push("--kiosk"),r.push(`--auto-select-desktop-capture-source="${o}"`)),void 0!==s){r.push(`--remote-debugging-port=${s}`);const e=T.createFolder("google");r.push(`--user-data-dir=${e}`)}r.push(e);const[a,h]=c?n:i,[l,u]=await H(a,h,...r);return l?(this.setAppInForeground("chrome"),!0):(R.error(u),!1)}if(h){const r=[];if(t&&(r.push("--kiosk"),r.push(`--auto-select-desktop-capture-source="${o}"`)),void 0!==s){r.push(`--remote-debugging-port=${s}`);const e=T.createFolder("edge");r.push(`--user-data-dir=${e}`)}r.push(e);const[n,i]=a,[c,h]=await H(n,i,...r);return c?(this.setAppInForeground("edge"),!0):(R.error(h),!1)}return R.info("Can't find browser"),!1}async setAppInForeground(e){await w(3e3);const t=await q(e);t?.focus(),this.config.browserKeeping&&o.interval(1e4).pipe(o.takeUntil(this.destroySub)).subscribe(()=>{q(e).then(e=>{e?.focus()})})}async getStateInfo(){const e=await this.isUnrealRunning();return{isOpen:e,isReady:!!e&&await this.isUnrealInService(1)}}onDestroy(){this.destroySub.next(),this.destroySub.complete()}}class z{folder;file;constructor(e,t){this.folder=T.addPath(e,t,"config"),this.file=T.addPath(this.folder,"config.json"),T.folderCheck(this.folder),T.fileCheck(this.file,JSON.stringify(F()))}async getSafePortTable(){const t=F(),s=await K(),r=await async function(t,s){const r=s??await K(),o={};return e.forEach(e.keys(t),s=>{const n=e.some(r,({localPort:e})=>e===t[s]);o[s]=n}),o}(t,s);if(e.some(e.values(r),e=>e)){const o=new Set(e.filter(e.map(s,e=>Number(e.localPort)),e=>!isNaN(e)));e.forEach(e.keys(r),e=>{const s=e;if(r[s]){const e=Number(t[s]);let r=1,n=NaN;for(;isNaN(n)&&r<=200;){const t=e+r;o.has(t)?r++:(o.add(t),n=t)}isNaN(n)||(t[s]=n.toString())}})}await this.setPortTable(t)}getPortTable(){return new Promise(e=>{a.readFile(this.file,"utf8",(t,s)=>{if(t)e(F());else try{e(JSON.parse(s))}catch(t){e(F())}})})}async setPortTable(e){try{const t=JSON.stringify(e);await new Promise(e=>{a.writeFile(this.file,t,"utf8",t=>{e()})})}catch(e){}}}function Q(...e){switch(e[0]){case"--status":return"服務狀態確認\n 用法: status [額外參數]\n 範例:\n status\n";case"--log":return"日誌讀取(無參數時,會返回可用日誌)\n 用法: log [額外參數]\n\n 範例:\n log --y\n log --yesterday\n log --specific=2025-11-28\n 可用參數:\n today : 讀取今天的日誌\n t : 讀取今天的日誌\n yesterday : 讀取昨天的日誌\n y : 讀取昨天的日誌\n specific=<yyyy-MM-dd> : 讀取指定日期的日誌\n s=<yyyy-MM-dd> : 讀取指定日期的日誌\n";case"--exit":case"--close":return"安全離開\n 用法 1: exit\n 用法 2: close\n";default:return"用法: [可用指令] [額外參數]\n 範例:\n help\n help --log\n 可用指令列表:\n help : 獲得可用的指令,使用 [--可用指令] 獲得該指令的詳細說明。\n status : 顯示當前的服務狀態\n log : 讀取今天的日誌\n exit : 關閉連線\n close : exit 的別稱,關閉連線\n"}}function ee(e){return async(...t)=>{const{core:s,channel:r}=e.getStatusInfo(),o=r.reduce((e,t,s)=>e+` channel ${s+1} - ${t}\n`,"\n");let n=`服務狀態\n 核心狀態 : ${s}\n 通道列表 : ${0===r.length?"無服務通道":o}\n`;if(e.automaion){const{isOpen:t,isReady:s}=await e.automaion.getStateInfo();n+=" Unreal : ",n+=t?s?"已啟動,服務已就緒":"已啟動,但服務未就緒":"尚未啟動",n+="\n"}return n}}function te(...e){const s=e[0];if(!s)return new Promise(e=>{a.readdir(T.appFolder,(t,s)=>{e(t?"沒有可用日誌":s.filter(e=>e.endsWith(".txt")).join("\n"))})});{let e=null;switch(s){case"--today":case"--t":e=t();break;case"--yesterday":case"--y":e=t().add(-1);break;default:{const r=new RegExp(/^--(specific|s)=([0-9]{4}-[0-9]{1,2}-[0-9]{1,2})$/),o=s.match(r);if(o){const[s,r,n]=o[2].split("-");e=t(`${s}-${r.padStart(2,"0")}-${n.padStart(2,"0")}`);break}return"請使用 -- 開始,或使用 help --log 查看幫助"}}if(!e.isValid())return"指定了無效的日期,請再次確認";{const t=`log_${e.format("yyyy-MM-DD")}.txt`,s=T.addPath(T.appFolder,t);try{return a.readFileSync(s,"utf8")}catch(e){return"找不到日誌"}}}}class se{server;constructor(t,s){const r=function(e){return e?{help:Q,status:ee(e),log:te}:{}}(s),{port:o,host:n}=t;this.server=g.createServer(t=>{console.log(`⚙️ 新的連線來自: ${t.remoteAddress}`);const s=b.createInterface({input:t,output:t,prompt:"Aiia Admin > "});t.setEncoding("utf8"),t.write("歡迎使用Aiia Admin!\n"),t.write("使用 `help` 獲得幫助\n"),s.prompt(),s.on("line",async o=>{const[n,...i]=o.trim().split(/\s/),a=n.toLocaleLowerCase();if("exit"===a||"close"===a)t.write("👋 切斷連線...\n"),t.end();else{const o=e.get(r,a,null);if(o){const e=await o(...i);t.write("\n"+e+"\n")}else t.write(`"${n}"是無效的指令.\n`);s.prompt()}}),t.on("end",()=>{console.log(`⚙️ 切斷連線: ${t.remoteAddress}`)})}),this.server.listen(o,n,()=>{console.log(`連線指令: telnet 127.0.0.1 ${o}`)})}onDestroy(){this.server.close()}}class re{config;stroage;expiment;apiProxy;chat;ueTools;automaion;state;browserSocketMap;destorySub;access_token;timestamp;socketAddr;get accessToken(){return this.access_token}constructor(e,t,s){this.config=e,this.stroage=t,this.expiment=s,this.state=0,this.destorySub=new o.Subject,this.browserSocketMap=new Map,this.timestamp=0,this.access_token=t.getItemSync(S);const{api:r,socket:n}=e.endPoint;this.socketAddr=n,this.apiProxy=new A(r),R.code(k["指定模組初始化完成"],"api"),this.chat=new C(e),R.code(k["指定模組初始化完成"],"chat"),this.ueTools=new B(e),R.code(k["指定模組初始化完成"],"udp tools"),this.openChannel=this.openChannel.bind(this),this.initService()}async start(){if(this.state>=1)throw new Error(m["重複初始化"]);this.state=1,await this.replaceAccessToken(),this.chat.memberChange.pipe(o.takeUntil(this.destorySub)).subscribe(({type:t,socket:s})=>{const r=s;switch(t){case"in":{const t=new P(s.id),i=new o.Subject;r.__cloudProxy=t;const a=(n=s.handshake.query.uuid,e.isArray(n)?e.get(n,"[0]",void 0):n??void 0);void 0===a?(this.getProjectList().then(e=>{s.emit("list",e)}).catch(e=>{R.error(e),s.emit("list",null)}),o.fromEvent(s,"uuid").pipe(o.take(1),o.takeUntil(i)).subscribe(e=>{this.openChannel(e,r,t,i)})):this.openChannel(a,r,t,i),this.browserSocketMap.set(r,i);break}case"out":if(this.browserSocketMap.has(r)){const e=this.browserSocketMap.get(r);e.next(),e.complete(),this.browserSocketMap.delete(r)}}var n}),this.chat.start(),R.code(k["啟動服務"],"chat");const{interaction:t}=e.assign({},this.expiment);if(void 0!==t&&"number"==typeof t.port){const{port:e,host:s}=t,r=new se({port:e,host:s},this);this.destorySub.subscribe(()=>{r.onDestroy()})}}initService(){const{automaion:t}=e.assign({},this.expiment);if(void 0!==t){const e=new Z(t);e.exec().then(e=>{this.ueTools.onStart(e)}),this.destorySub.subscribe(()=>{e.onDestroy()}),this.automaion=e}else this.ueTools.onStart();o.interval(18e4).pipe(o.takeUntil(this.destorySub)).subscribe(()=>{this.inspectClientSocket()})}async replaceAccessToken(){try{const e=await this.apiProxy.getAccessToken(this.config.license,this.access_token),s=this.access_token;this.access_token=e,this.timestamp=0,this.stroage.setItemSync(S,e),this.stroage.setItemSync(t().format("yyyy-MM-DD[T]HH:mm:ss:sss"),s)}catch(e){throw R.error(e),e}}async getProjectList(){try{return await this.apiProxy.getProjectList(this.config.license)}catch(e){throw R.error(e),e}}async getProxySocketURL(e){const t=Date.now()-this.timestamp;return t<5e3&&await w(5100-t),this.timestamp=Date.now(),`${this.socketAddr}/v1/chat/project/${this.accessToken}/${e}`}async openChannel(t,s,r,n){o.fromEvent(s,"cloud").pipe(o.takeUntil(n)).subscribe(e=>{r.sendToCloud(e)}),o.fromEvent(s,"face").pipe(o.takeUntil(n)).subscribe(e=>{const t=e,s=null!==t;this.ueTools.eyeTrack(t),r.sendToCloud({request:"face_detect",content:s})}),r.message.pipe(o.takeUntil(n)).subscribe(t=>{const r=function(t){try{const s=JSON.parse(t);return e.get(s,"signal")?[s]:e.isArray(s)?s:e.values(s)}catch(e){return R.error("message transfer fail",e),[]}}(t),o=[],n=[];e.forEach(r,e=>{switch(e.signal){case"osc":n.push(e);break;case"audio":"pcm"===e.command?this.ueTools.speech(e.content):"interrupted"===e.command&&this.ueTools.interrupted();default:o.push(e)}}),o.length>0&&s.emit("cloud",o),n.length>0&&this.ueTools.movement(n)}),r.close.pipe(o.takeUntil(n)).subscribe(async e=>{switch(s.emit("channel",e),e){case"close":case"no_permissions":r.onDestroy();break;case"reconnect":try{await this.replaceAccessToken();const e=await this.getProxySocketURL(t);s.connected?r.reconnect(e):R.error(`The browser(${r.id}) leaves before reconnecting to the cloud`)}catch(e){r.onDestroy(),s.emit("channel","net_error"),R.error(e)}}}),r.linkStart.pipe(o.takeUntil(n)).subscribe(()=>{s.emit("channel","open")}),n.subscribe(()=>{r.onClose()});const i=await this.getProxySocketURL(t);s.connected?r.start(i):R.error(`The browser(${r.id}) leaves before connecting to the cloud`)}inspectClientSocket(){const t=Array.from(this.browserSocketMap);e.forEach(t,([e,t])=>{e.disconnected&&(t.next(),t.complete(),this.browserSocketMap.delete(e),R.info(`The browser(${e.id}) should be closed. Auto clean service.`))})}getStatusInfo(){return{core:0===this.state?"未啟動":1===this.state?"服務中":"已結束",channel:Array.from(this.browserSocketMap).map(([e])=>e.__cloudProxy.getStatusInfo())}}onDestroy(){this.state=2,this.apiProxy.onDestroy(),this.chat.onDestroy(),this.ueTools.onDestroy(),this.destorySub.next(),this.destorySub.complete(),R.code(k["服務結束"])}}exports.aiiaCore=function(t){if(_){if("string"!=typeof e.get(t,"license",void 0))throw new Error(m["未提供憑證"]);!function(e){const{info:t,error:s,warn:r,debug:o,fatal:n,code:i}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);return i||(D.code=()=>{}),o||(D.debug=()=>{}),t||(D.info=()=>{}),r||(D.warn=()=>{}),s||(D.error=()=>{}),n||(D.fatal=()=>{}),N}(e.get(t,"debug",!0))(new L),R.code(k["開始初始化必要模組"]);const s=new E(e.assign({},t));R.code(k["指定模組初始化完成"],"config");const r=new v;R.code(k["指定模組初始化完成"],"storage");const o=new re(s,r,e.get(t,"experiment"));return R.code(k["指定模組初始化完成"],"core"),o.start(),R.code(k["啟動服務"],"main core"),()=>{o.onDestroy()}}throw new Error(m["環境錯誤(nodejs)"])};
|
package/dist/node.d.ts
CHANGED
|
@@ -15,6 +15,32 @@ export declare interface AiiaProjectItem {
|
|
|
15
15
|
name: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* [zh]
|
|
20
|
+
* 自動啟動完整服務
|
|
21
|
+
*
|
|
22
|
+
* @property options.unrealFolder 放置應用程式的相對或絕對路徑(有資訊則會自動檢查 UE 狀態並啟動)
|
|
23
|
+
* @property options.openUrl 自動開啟網頁
|
|
24
|
+
* @property options.mode 開啟的模式
|
|
25
|
+
* @property options.foreground 強制瀏覽器至於前景
|
|
26
|
+
* @property options.remoteDebug 遠程調控
|
|
27
|
+
*/
|
|
28
|
+
declare interface AutomationOptions {
|
|
29
|
+
unrealFolder?: string;
|
|
30
|
+
openUrl?: string;
|
|
31
|
+
mode?: "kiosk";
|
|
32
|
+
foreground?: "always";
|
|
33
|
+
remoteDebug?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export declare interface ExperimentOptions {
|
|
37
|
+
interaction?: {
|
|
38
|
+
port?: number;
|
|
39
|
+
host?: string;
|
|
40
|
+
};
|
|
41
|
+
automaion?: AutomationOptions;
|
|
42
|
+
}
|
|
43
|
+
|
|
18
44
|
declare interface InitOptions {
|
|
19
45
|
/**
|
|
20
46
|
* [zh]
|
|
@@ -34,10 +60,10 @@ declare interface InitOptions {
|
|
|
34
60
|
project?: string | Partial<AiiaProjectItem>;
|
|
35
61
|
/**
|
|
36
62
|
* [zh]
|
|
37
|
-
* 指定的環境,預設值:
|
|
63
|
+
* 指定的環境,預設值: developement
|
|
38
64
|
*
|
|
39
65
|
* [en]
|
|
40
|
-
* Specific Env, default:
|
|
66
|
+
* Specific Env, default: developement
|
|
41
67
|
*/
|
|
42
68
|
env?: "developement" | "production";
|
|
43
69
|
/**
|
|
@@ -109,15 +135,17 @@ declare interface InitOptions {
|
|
|
109
135
|
*/
|
|
110
136
|
llmSampleRate?: number;
|
|
111
137
|
/**
|
|
138
|
+
* @deprecated
|
|
139
|
+
*
|
|
112
140
|
* [zh]
|
|
113
|
-
* 人臉辨識
|
|
141
|
+
* 人臉辨識(已棄用,請改用 `cameraDetection`),同時設定會以 `cameraDetection` 為主
|
|
114
142
|
*
|
|
115
143
|
* @property perSecond: 每秒檢測幾次, 預設: 每秒 3 次
|
|
116
144
|
*
|
|
117
145
|
* @property confidence: 分數(0 ~ 100), 預設: 80
|
|
118
146
|
*
|
|
119
147
|
* [en]
|
|
120
|
-
*
|
|
148
|
+
* Face recognition (deprecated, please use `cameraDetection` instead), and the settings will be based on `cameraDetection`
|
|
121
149
|
*
|
|
122
150
|
* @property perSecond: Detection times per second, default: 3 times per second
|
|
123
151
|
*
|
|
@@ -127,6 +155,25 @@ declare interface InitOptions {
|
|
|
127
155
|
perSecond?: number;
|
|
128
156
|
confidence?: number;
|
|
129
157
|
};
|
|
158
|
+
/**
|
|
159
|
+
* [zh]
|
|
160
|
+
* 攝影機辨識設定
|
|
161
|
+
*
|
|
162
|
+
* @property perSecond: 每秒檢測幾次, 預設: 每秒 3 次
|
|
163
|
+
*
|
|
164
|
+
* @property confidence: 人臉辨識分數(0 ~ 100), 預設: 80
|
|
165
|
+
*
|
|
166
|
+
* [en]
|
|
167
|
+
* Camera Identification Settings
|
|
168
|
+
*
|
|
169
|
+
* @property perSecond: Detection times per second, default: 3 times per second
|
|
170
|
+
*
|
|
171
|
+
* @property confidence: Face recognition score(0 ~ 100), default: 80
|
|
172
|
+
*/
|
|
173
|
+
cameraDetection?: {
|
|
174
|
+
perSecond?: number;
|
|
175
|
+
confidence?: number;
|
|
176
|
+
};
|
|
130
177
|
/**
|
|
131
178
|
* [zh]
|
|
132
179
|
* 清除字幕,可指定時間(毫秒),預設 5000 毫秒後清除
|
|
@@ -166,7 +213,9 @@ declare interface InitOptions {
|
|
|
166
213
|
|
|
167
214
|
declare type LoggerLevel = "info" | "error" | "warn" | "debug" | "fatal" | "code";
|
|
168
215
|
|
|
169
|
-
export declare type NodeInitOptions = Omit<InitOptions, "project" | "ws_url" | "worklet_url" | "mediaStream" | "chunkTimeInSeconds" | "llmSampleRate" | "autoClearUserSubtitle" | "autoClearAiiaSubtitle" | "faceDetection"
|
|
216
|
+
export declare type NodeInitOptions = Omit<InitOptions, "project" | "ws_url" | "worklet_url" | "mediaStream" | "chunkTimeInSeconds" | "llmSampleRate" | "autoClearUserSubtitle" | "autoClearAiiaSubtitle" | "faceDetection" | "cameraDetection"> & {
|
|
217
|
+
experiment?: ExperimentOptions;
|
|
218
|
+
};
|
|
170
219
|
|
|
171
220
|
declare type Webserver = Server<any, any> | Server_2<any, any> | Http2SecureServer<any, any, any, any> | Http2Server<any, any, any, any>;
|
|
172
221
|
|
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 o,omit as r,pick as n,keys as i,isInteger as a,find as c,some as h,values as l,filter as u,isArray as d}from"lodash-es";import p from"moment";import{abs as f,matrix as g,multiply as m}from"mathjs";import{Server as y}from"socket.io";import{Subject as S,fromEvent as b,takeUntil as _,take as w,interval as E}from"rxjs";import D from"axios";import O from"ws";import N from"fs";import k from"os";import R from"path";import{Client as C}from"node-osc";import{spawn as A,exec as M}from"child_process";import P from"systeminformation";import{getWindows as T}from"@nut-tree-fork/nut-js";import*as I from"readline";import{createServer as x}from"net";var L;!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(L||(L={}));const v="access_token",$="undefined"!=typeof process&&null!=process.versions&&null!=process.versions.node;function F(e){return new Promise(t=>{setTimeout(t,e)})}class W{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e(t,"id",void 0),o=e(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof o?o:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return t({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return t({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e(this.config,t,void 0)}}var U;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(U||(U={}));const j={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},G=[];function B(e){return G.push(e),B}const Y=new Proxy(j,{get(e,t,o){if("string"==typeof t)switch(t){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...o)=>{const r=p().format("yyyy-MM-DD[T]HH:mm:ss:sss");s(G,e=>{e.next(t,r,...o)}),e[t](`[Aiia::${t}_${r}_]`,...o)};default:return}},set:(e,t,s,o)=>!0});class H{config;server;memberSub;get port(){return this.config.port}get webserver(){return this.config.webserver}get memberChange(){return this.memberSub.asObservable()}constructor(e){this.config=e,this.memberSub=new S}start(){if(this.server=new y(this.webserver,{cors:{origin:"*"}}),this.server.on("connection",e=>{Y.debug("client on",e.id),this.memberSub.next({type:"in",socket:e}),e.on("disconnect",t=>{Y.debug("client off",e.id,"reason",t),this.memberSub.next({type:"out",socket:e})})}),void 0===this.webserver){const e=this.port;this.server.listen(e),Y.info("Websocket server run on",e)}else Y.info("Websocket server rely your webserver.")}onDestroy(){this.server&&this.server.close()}}class K{instance;controller;constructor(e){this.controller=new AbortController,this.instance=D.create({baseURL:e,signal:this.controller.signal})}async getAccessToken(t,s){const o=performance.now(),r={license_number:t,token_value:s??""},n=await this.instance.post("/api/v1/license/agent_token",r).then(t=>e(t.data,"token_value","")).catch(()=>null);if(Y.debug("Complete certification",`Took ${performance.now()-o} milliseconds.`),null===n)throw new Error(L["網絡錯誤"]);if(""===n)throw new Error(L["獲取權杖失敗"]);return n}async getProjectList(t){const s=performance.now(),r=await this.instance.post("/api/v1/license/projects",{license_number:t}).then(t=>o(e(t,"data.projects",[]),({id:e,sub_project_name:t,avatar:s})=>{const{name:o,url:r}=s;return{avatar:{name:o,url:r},id:e,name:t}})).catch(e=>null);if(Y.debug("Got Project List",`Took ${performance.now()-s} milliseconds.`),null===r)throw new Error(L["網絡錯誤"]);if(0===r.length)throw new Error(L["沒有可用專案"]);return r}onDestroy(){this.controller.abort()}}var X;!function(e){e[e.MDN_NORMAL_CLOSURE=1e3]="MDN_NORMAL_CLOSURE",e[e.MDN_GOING_AWAY=1001]="MDN_GOING_AWAY",e[e.MDN_PROTOCOL_ERROR=1002]="MDN_PROTOCOL_ERROR",e[e.MDN_UNSUPPORTED_DATA=1003]="MDN_UNSUPPORTED_DATA",e[e.MDN_RESERVED_1004=1004]="MDN_RESERVED_1004",e[e.MDN_NO_STATUS_RECEIVED=1005]="MDN_NO_STATUS_RECEIVED",e[e.MDN_ABNORMAL_CLOSURE=1006]="MDN_ABNORMAL_CLOSURE",e[e.MDN_INVALID_FRAME_PAYLOAD=1007]="MDN_INVALID_FRAME_PAYLOAD",e[e.MDN_POLICY_VIOLATION=1008]="MDN_POLICY_VIOLATION",e[e.MDN_MESSAGE_TOO_BIG=1009]="MDN_MESSAGE_TOO_BIG",e[e.MDN_MANDATORY_EXTENSION=1010]="MDN_MANDATORY_EXTENSION",e[e.MDN_INTERNAL_ERROR=1011]="MDN_INTERNAL_ERROR",e[e.MDN_SERVICE_RESTART=1012]="MDN_SERVICE_RESTART",e[e.MDN_TRY_AGAIN_LATER=1013]="MDN_TRY_AGAIN_LATER",e[e.MDN_BAD_GATEWAY=1014]="MDN_BAD_GATEWAY",e[e.MDN_TLS_HANDSHAKE=1015]="MDN_TLS_HANDSHAKE",e[e.WHEN_BROWSER_CLOSE=4001]="WHEN_BROWSER_CLOSE",e[e.WS_4100_NORAML_CLOSURE=4100]="WS_4100_NORAML_CLOSURE",e[e.WS_4101_UNAUTHORIZED=4101]="WS_4101_UNAUTHORIZED",e[e.WS_4102_LICENSE_EXPIRED=4102]="WS_4102_LICENSE_EXPIRED",e[e.WS_4103_TOKEN_EXPIRED=4103]="WS_4103_TOKEN_EXPIRED",e[e.WS_4104_LLM_SESSION_ERROR=4104]="WS_4104_LLM_SESSION_ERROR",e[e.WS_4105_TOO_MANY_CONNECTIONS=4105]="WS_4105_TOO_MANY_CONNECTIONS"}(X||(X={}));class V{id;uuid;socket;messageSub;linkStartSub;destorySub;closeSub;isFinished;debugInfo;lastCloseLog;get message(){return this.messageSub.asObservable()}get close(){return this.closeSub.asObservable()}get linkStart(){return this.linkStartSub.asObservable()}get isConnected(){return this.socket?.readyState===O.OPEN}constructor(e){this.id=e,this.messageSub=new S,this.linkStartSub=new S,this.closeSub=new S,this.destorySub=new S,this.isFinished=!1,this.uuid=crypto.randomUUID(),this.debugInfo=`browser(${this.id}), proxy(${this.uuid})`,Y.debug(`create proxy for browser(${this.id})`)}start(e){this.socket=new O(e,{autoPong:!0}),b(this.socket,"open").pipe(_(this.destorySub)).subscribe(()=>{Y.code(U["啟動服務"],"proxy",this.debugInfo),this.linkStartSub.next()}),b(this.socket,"message").pipe(_(this.destorySub)).subscribe(e=>{const{data:t}=e;this.messageSub.next(t)}),b(this.socket,"error").pipe(_(this.destorySub)).subscribe(e=>{const{error:t}=e;Y.error(this.debugInfo,"Proxy has some problem",t)}),b(this.socket,"close").pipe(_(this.destorySub)).subscribe(e=>{const{code:t,reason:s,type:o,wasClean:r}=e;switch(this.lastCloseLog=`最近一次關閉原因: ${s}, 錯誤碼: ${t}, Type: ${o}`,t){case X.WHEN_BROWSER_CLOSE:Y.code(U["無副作用的關閉連線"],this.debugInfo),this.onDestroy();break;case X.MDN_NORMAL_CLOSURE:case X.MDN_GOING_AWAY:this.closeSub.next("close"),Y.code(U["安全的關閉連線"],this.debugInfo);break;case X.MDN_NO_STATUS_RECEIVED:case X.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),Y.code(U["與雲端的服務中斷"],this.debugInfo,{code:t,reason:s,type:o,wasClean:r});break;case X.WS_4100_NORAML_CLOSURE:case X.WS_4103_TOKEN_EXPIRED:case X.WS_4104_LLM_SESSION_ERROR:case X.WS_4105_TOO_MANY_CONNECTIONS:this.closeSub.next("reconnect"),Y.code(U["自動重新連線雲端服務"],this.debugInfo,`socket code: ${t}`);break;case X.WS_4101_UNAUTHORIZED:case X.WS_4102_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),Y.code(U["沒有授權,請聯繫Graphen"],this.debugInfo,`socket code: ${t}`);break;default:this.closeSub.next("close"),Y.code(U["未預期的斷線,請聯繫Graphen"],this.debugInfo,{code:t,reason:s,type:o,wasClean:r})}})}reconnect(e){this.isFinished||(this.destorySub.next(),this.destorySub.complete(),this.destorySub=new S,this.start(e))}send(e){const t=`currentTime: ${p().format("yyyy-MM-DD[T]HH:mm:ss:sss")}`;this.isConnected?this.socket.send(e,s=>{s&&Y.error("send message fail",this.debugInfo,t,e.slice(0,100),s)}):Y.warn("The socket is not connected",this.debugInfo,t,e.slice(0,100))}sendToCloud(e){this.send(JSON.stringify(e))}onClose(){if(this.socket)switch(this.socket.readyState){case 0:Y.debug("Once the cloud connection is complete, the connection will be disconnected."),this.linkStart.pipe(_(this.destorySub),w(1)).subscribe(()=>{this.socket?.close(X.WHEN_BROWSER_CLOSE)});break;case 1:Y.debug("Close safely, no side effects"),this.socket.close(X.WHEN_BROWSER_CLOSE);break;case 2:case 3:Y.debug("The connection have been lost.")}}getStatusInfo(){const e=(()=>{if(!this.socket)return"雲端服務尚未啟動";{const e=this.socket.readyState;switch(e){case 0:return"正在連線雲端服務中";case 1:return"雲端服務正常運作";case 2:return"正在關閉雲端服務";case 3:return"雲端服務已關閉";default:return`未知的 Websocket 狀態: ${e}`}}})(),t=this.lastCloseLog?`${e}, ${this.lastCloseLog}`:e;return`瀏覽器(${this.id}) - 雲端代理(${this.uuid}), ${t}`}onDestroy(){this.isFinished=!0,this.destorySub.next(),this.destorySub.complete(),Y.debug(`Destroy cloud channel(${this.debugInfo})`)}}const J=new class{AppName;platform;isMAC;isWIN;isLINUX;saveFolder;homedir;appFolder;EOL;constructor(e){switch(this.AppName=e,this.platform=process.platform,this.homedir=k.homedir(),this.EOL=k.EOL,this.platform){case"darwin":this.isMAC=!0,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=R.join(this.homedir,"Library","Application Support");break;case"win32":this.isMAC=!1,this.isWIN=!0,this.isLINUX=!1,this.saveFolder=process.env.APPDATA||R.join(this.homedir,"AppData","Roaming");break;case"linux":this.isMAC=!1,this.isWIN=!1,this.isLINUX=!0,this.saveFolder=process.env.XDG_CONFIG_HOME||R.join(this.homedir,".config");break;default:console.log(`當前系統是未知的平台: ${this.platform}`),this.isMAC=!1,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=this.homedir}this.appFolder=R.join(this.saveFolder,this.AppName),this.folderCheck(this.appFolder)}addPath(...e){return R.join(...e)}folderCheck(e){N.mkdirSync(e,{recursive:!0})}createFolder(e){const t=this.addPath(this.appFolder,e);return this.folderCheck(t),t}fileCheck(e,t=""){N.existsSync(e)||N.writeFileSync(e,t,{encoding:"utf8"})}}("aiia-sdk");class Z{type="node";filePath;fileName;constructor(){this.fileName="storage.json",this.filePath=J.addPath(J.appFolder,this.fileName),J.fileCheck(this.filePath)}getFileDataSync(){const e=N.readFileSync(this.filePath,"utf8");try{return JSON.parse(e)}catch(e){return{}}}setFileDataSync(e){try{const t=JSON.stringify(e);N.writeFileSync(this.filePath,t,"utf8")}catch(e){}}async getFileData(){return new Promise(e=>{N.readFile(this.filePath,"utf8",(t,s)=>{if(t)e({});else try{e(JSON.parse(s))}catch(t){e({})}})})}async setFileData(e){return new Promise(t=>{try{const s=JSON.stringify(e);N.writeFile(this.filePath,s,"utf8",e=>{t()})}catch(e){}})}async getItem(e){return(await this.getFileData())[e]??null}async setItem(e,t){const s=await this.getFileData();s[e]=t,await this.setFileData(s)}async removeItem(e){const t=await this.getFileData(),s=r(t,e);await this.setFileData(s)}async clear(){await this.setFileData({})}getItemSync(e){return this.getFileDataSync()[e]??null}setItemSync(e,t){const s=this.getFileDataSync();s[e]=t,this.setFileDataSync(s)}removeItemSync(e){const t=r(this.getFileDataSync(),e);this.setFileDataSync(t)}clearSync(){this.setFileDataSync({})}}const q={[U["通道、雲端服務、麥克風都已就緒"]]:"通道、雲端服務、麥克風都已就緒",[U["未取得媒體裝置權限,請重新設定或忽略此訊息"]]:"未取得媒體裝置權限,請重新設定或忽略此訊息",[U["請訂閱專案列表"]]:"請訂閱專案列表",[U["初始化必要依賴"]]:"初始化必要依賴",[U["指定依賴已完成"]]:"指定依賴已完成",[U["啟動SDK"]]:"啟動SDK",[U["結束SDK"]]:"結束SDK",[U["已啟動SDK"]]:"已啟動SDK",[U["安全的關閉連線"]]:"安全的關閉連線",[U["無副作用的關閉連線"]]:"無副作用的關閉連線",[U["與雲端的服務中斷"]]:"與雲端的服務中斷",[U["沒有授權,請聯繫Graphen"]]:"沒有授權,請聯繫Graphen",[U["自動重新連線雲端服務"]]:"自動重新連線雲端服務",[U["未預期的斷線,請聯繫Graphen"]]:"未預期的斷線,請聯繫Graphen",[U["開始初始化必要模組"]]:"開始初始化必要模組",[U["指定模組初始化完成"]]:"指定模組初始化完成",[U["啟動服務"]]:"啟動服務",[U["服務結束"]]:"服務結束",[L["網絡錯誤"]]:"網絡錯誤",[L["環境錯誤(browser)"]]:"環境錯誤",[L["Worklet模組載入失敗"]]:"Worklet模組載入失敗",[L["未獲得媒體裝置權限"]]:"未獲得媒體裝置權限",[L["實例已摧毀"]]:"實例已摧毀",[L["環境錯誤(nodejs)"]]:"nodejs",[L["未提供憑證"]]:"未提供憑證",[L["獲取權杖失敗"]]:"獲取權杖失敗",[L["沒有可用專案"]]:"沒有可用專案",[L["重複初始化"]]:"重複初始化"},z=new RegExp(/^(ERROR )?CODE: [0-9]{3,4}$/);class Q{path;constructor(e){this.path=e?J.createFolder(e):J.appFolder}next(e,t,...s){const o=`log_${p().format("yyyy-MM-DD")}.txt`,r=J.addPath(this.path,o),n="code"===e?e=>z.test(e)?`${e}(${q[e]})`:e:e=>e;try{const o=s.reduce((e,t)=>{if(t instanceof Error){let s=`Error[${t.message??"No Message"}]${J.EOL}`;return s+=t.stack??"unknow stack","string"==typeof t.cause&&(s+=`${J.EOL}cause: ${t.cause}`),`${e}${J.EOL}${s}${J.EOL}`}switch(typeof t){case"string":return`${e} ${n(t)}`;case"number":case"bigint":return`${e} ${t.toString()}`;case"boolean":return`${e} ${t?"true":"false"}`;case"object":try{return`${e} ${JSON.stringify(t)}`}catch(t){return e}case"symbol":case"undefined":case"function":return`${e} [${typeof t}]`}},"");N.appendFileSync(r,`LOG(${e})__${t}__::${o}${J.EOL}`,{encoding:"utf8"})}catch(e){}}}const ee="127.0.0.1",te={AvatarPort:"9876",ActionPort:"6969",LocationPort:"4060",LookPort:"6745",BackGroundPort:"2400"},se={StreamAudioPort:"8080",CleanAudioPort:"8081"};function oe(){return{...te,...se}}class re{clientMap;eyeBallUdp;x_axis_px;y_axis_px;defPortMap;constructor(){this.clientMap=new Map,this.x_axis_px=0,this.y_axis_px=0,this.defPortMap={};const e=n(te,"AvatarPort","ActionPort","LocationPort","BackGroundPort");s(i(e),t=>{const s=t,o=e[s];this.defPortMap[o]=s})}enableUDPClient(e){s(i(e),t=>{const s=new C(ee,e[t]);this.clientMap.set(t,s)})}enableEyeTrack(e){this.eyeBallUdp=new C(ee,e.port),this.x_axis_px=e.x_axis_px,this.y_axis_px=e.y_axis_px}eyeTrack(e){if(void 0!==this.eyeBallUdp){const s=null!==e,{x:o,y:r}=(()=>{if(s){const{x:s,y:o}=function(e,s){const{maxX:o,maxY:r,minX:n,minY:i}=t({maxX:25,minX:-25,maxY:50,minY:-50},s),a=f(o)+f(n),c=f(r)+f(i),{clientHeight:h,clientWidth:l,originX:u,originY:d,width:p,height:y}=e,S=g([[a/l,0,n],[0,c/h,i],[0,0,1]]),b=g([[u+p/2],[h-(d+y/2)],[1]]),_=m(S,b);return{x:_.get([0,0]),y:_.get([1,0])}}(e);return{x:this.toFloat(s+this.x_axis_px),y:this.toFloat(o+this.y_axis_px)}}return{x:this.toFloat(7),y:this.toFloat(20)}})();this.eyeBallUdp.send(["/",o,r],e=>{null!=e&&Y.error("Eye track error:",e)})}}movement(t){o(t,({content:t})=>{const s=e(t,"port",""),o=e(t,"message"),r=this.defPortMap[s.toString()];this.clientMap.has(r)&&this.clientMap.get(r).send(o,e=>{null!=e&&Y.error("Occur error when send message to UE",e)})})}toFloat(e){return a(e)?e+1e-5:e}onDestroy(){s(Array.from(this.clientMap),([e,t])=>{t.close()}),Y.info("Close all UnrealEngine's UDP")}}class ne{destroy;speechWS;interruptWS;get speechIsConnected(){return this.speechWS?.readyState===O.OPEN}get intrIsConnected(){return this.interruptWS?.readyState===O.OPEN}constructor(){this.destroy=new S}connectSocket(e){this.speechWS=this.getSocket(e.speech,"speech"),this.interruptWS=this.getSocket(e.interrupt,"interrupt")}getSocket(e,t){const s=new O(`ws://${ee}:${e}`);return s.onerror=s=>{const{message:o,error:r,type:n}=s;Y.error(`UE Tool(${t}) fail: ${o}`,r,{port:e,type:n})},s.onclose=s=>{const{code:o,reason:r,type:n}=s;Y.debug(`UE Tool(${t}) close`,{code:o,reason:r,type:n,port:e})},s}interrupted(){this.intrIsConnected&&this.interruptWS.send("interrupted")}speech(e){if(this.speechIsConnected){const t=Buffer.allocUnsafe(2*e.length);for(let s=0;s<e.length;s++)t.writeInt16LE(e[s],2*s);this.speechWS.send(t)}}onDestroy(){this.speechWS?.close(),this.interruptWS?.close(),this.destroy.next(),this.destroy.complete(),Y.info("Close all UnrealEngine's WebSocket")}}class ie{config;udpManager;socketManager;movement;eyeTrack;interrupted;speech;constructor(e){this.config=e,this.udpManager=new re,this.socketManager=new ne,this.movement=()=>{},this.eyeTrack=()=>{},this.interrupted=()=>{},this.speech=()=>{}}onStart(e){const o={},r=oe(),n=t(oe(),e);s(i(r),e=>{const t=n[e],s=Number(t);o[e]=isNaN(s)?Number(r[e]):s});const{StreamAudioPort:a,CleanAudioPort:c,LookPort:h,...l}=o;this.udpManager.enableUDPClient(l),this.socketManager.connectSocket({speech:a,interrupt:c}),this.movement=this.udpManager.movement.bind(this.udpManager),this.eyeTrack=this.udpManager.eyeTrack.bind(this.udpManager),this.interrupted=this.socketManager.interrupted.bind(this.socketManager),this.speech=this.socketManager.speech.bind(this.socketManager),this.config.eyeTrackEnable&&this.udpManager.enableEyeTrack({...this.config.eyeTrackCorrection,port:h})}onDestroy(){this.udpManager.onDestroy(),this.socketManager.onDestroy(),Y.info("UE tools destroy")}}async function ae(e){return new Promise(t=>{N.stat(e,e=>{t(e?[!1,e]:[!0])})})}async function ce(e,t,...s){return J.isWIN?new Promise(o=>{const r=[`cd ${e} && ${t} `,...s].join(" "),n=M(r);n.once("error",e=>{o([!1,e])}),n.once("spawn",()=>{n.unref(),o([!0])})}):Promise.resolve([!1,new Error("The function isn't implement in this system")])}async function he(){try{return await P.networkConnections()}catch(e){return console.error("檢查 Port 時發生錯誤:",e),[]}}async function le(e){const t=e.toString(),s=await he();return h(s,e=>e.localPort===t)}async function ue(e){const t=await T();let s=null;const o=new RegExp(e.toLocaleLowerCase());for(const e of t){const t=await e.title;if(o.test(t.toLocaleLowerCase())){s=e;break}}return s}function de(e){return[2e3,2e3,2e3,1e3,1e3,2e3,3e3,5e3][e]??[6e3,4e3][e%2]}function pe(){if(J.isWIN){const e="UEProject_5_6";return{proc:e,name:`${e}.exe`}}return null}class fe{config;ueConfig;destroySub;constructor(e){this.destroySub=new S;const s={isKiosk:!1,browserKeeping:!1},{unrealFolder:o,openUrl:r,mode:n,foreground:i,remoteDebug:a}=t({},e);if("string"==typeof o&&""!==o.trim()){s.path=o;const e=pe();e&&(this.ueConfig=new ge(o,e.proc))}"string"==typeof r&&""!==r.trim()&&(s.url=r),"string"==typeof n&&""!==n.trim()&&(s.isKiosk="kiosk"===n),"string"==typeof i&&""!==i.trim()&&(s.browserKeeping="always"===i),"number"==typeof a&&(s.remoteDebug=a),this.config=s}async exec(){let e=!1,t=await this.isUnrealRunning();t||(this.ueConfig&&await this.ueConfig.getSafePortTable(),t=await this.openUnreal()),t&&(e=await this.isUnrealInService()),e&&await this.openBrowser();return this.ueConfig?await this.ueConfig.getPortTable():oe()}async isUnrealRunning(){const e=pe();if(null===e)return!1;const{proc:t}=e,s=await async function(...e){const t=e.length>0?e.join(", "):"*";return await P.processLoad(t).catch(()=>[])}(t);if(s.length<=0)return!1;const o=t.toLocaleLowerCase(),r=c(s,e=>e.proc.toLocaleLowerCase()===o);return!!(r&&"number"==typeof r.pid&&r.pids.length>=2)}async isUnrealInService(e=12){let t=!1,s=0;const{StreamAudioPort:o}=this.ueConfig?await this.ueConfig.getPortTable():oe();for(;!t&&s<e;)t=await le(o),await F(de(s)),s++;return t}async openUnreal(){const e=pe();if(null===e)return!1;const{path:t}=this.config;if(void 0===t)return!1;const s=J.addPath(t,e.name),[o,r]=await ae(s);if(!o)return Y.error(r),!1;const[n,i]=await async function(e,...t){return J.isWIN?new Promise(s=>{const o=A(e,t,{detached:!0,stdio:"ignore"});o.once("error",e=>{s([!1,e])}),o.once("spawn",()=>{o.unref(),s([!0])})}):Promise.resolve([!1,new Error("The function isn't implement in this system")])}(s);return i&&Y.error(i),n}async openBrowser(){const{url:e,isKiosk:t,remoteDebug:s}=this.config;if(void 0===e)return!1;const o=J.isWIN?{edge:["C:/Program Files (x86)/Microsoft/Edge/Application","msedge.exe"],chrome:["C:/Program Files/Google/Chrome/Application","chrome.exe"],chrome2:[`${J.homedir}/AppData/Local/Google/Chrome/Application`,"chrome.exe"]}:null;if(null===o)return!1;const{proc:r}=pe(),{chrome:n,chrome2:i,edge:a}=o,[[c],[h],[l]]=await Promise.all([ae(J.addPath(...n)),ae(J.addPath(...a)),ae(J.addPath(...i))]);if(c||l){const o=[];if(t&&(o.push("--kiosk"),o.push(`--auto-select-desktop-capture-source="${r}"`)),void 0!==s){o.push(`--remote-debugging-port=${s}`);const e=J.createFolder("google");o.push(`--user-data-dir=${e}`)}o.push(e);const[a,h]=c?n:i,[l,u]=await ce(a,h,...o);return l?(this.setAppInForeground("chrome"),!0):(Y.error(u),!1)}if(h){const o=[];if(t&&(o.push("--kiosk"),o.push(`--auto-select-desktop-capture-source="${r}"`)),void 0!==s){o.push(`--remote-debugging-port=${s}`);const e=J.createFolder("edge");o.push(`--user-data-dir=${e}`)}o.push(e);const[n,i]=a,[c,h]=await ce(n,i,...o);return c?(this.setAppInForeground("edge"),!0):(Y.error(h),!1)}return Y.info("Can't find browser"),!1}async setAppInForeground(e){await F(3e3);const t=await ue(e);t?.focus(),this.config.browserKeeping&&E(1e4).pipe(_(this.destroySub)).subscribe(()=>{ue(e).then(e=>{e?.focus()})})}async getStateInfo(){const e=await this.isUnrealRunning();return{isOpen:e,isReady:!!e&&await this.isUnrealInService(1)}}onDestroy(){this.destroySub.next(),this.destroySub.complete()}}class ge{folder;file;constructor(e,t){this.folder=J.addPath(e,t,"config"),this.file=J.addPath(this.folder,"config.json"),J.folderCheck(this.folder),J.fileCheck(this.file,JSON.stringify(oe()))}async getSafePortTable(){const e=oe(),t=await he(),r=await async function(e,t){const o=t??await he(),r={};return s(i(e),t=>{const s=h(o,({localPort:s})=>s===e[t]);r[t]=s}),r}(e,t);if(h(l(r),e=>e)){const n=new Set(u(o(t,e=>Number(e.localPort)),e=>!isNaN(e)));s(i(r),t=>{const s=t;if(r[s]){const t=Number(e[s]);let o=1,r=NaN;for(;isNaN(r)&&o<=200;){const e=t+o;n.has(e)?o++:(n.add(e),r=e)}isNaN(r)||(e[s]=r.toString())}})}await this.setPortTable(e)}getPortTable(){return new Promise(e=>{N.readFile(this.file,"utf8",(t,s)=>{if(t)e(oe());else try{e(JSON.parse(s))}catch(t){e(oe())}})})}async setPortTable(e){try{const t=JSON.stringify(e);await new Promise(e=>{N.writeFile(this.file,t,"utf8",t=>{e()})})}catch(e){}}}function me(...e){switch(e[0]){case"--status":return"服務狀態確認\n 用法: status [額外參數]\n 範例:\n status\n";case"--log":return"日誌讀取(無參數時,會返回可用日誌)\n 用法: log [額外參數]\n\n 範例:\n log --y\n log --yesterday\n log --specific=2025-11-28\n 可用參數:\n today : 讀取今天的日誌\n t : 讀取今天的日誌\n yesterday : 讀取昨天的日誌\n y : 讀取昨天的日誌\n specific=<yyyy-MM-dd> : 讀取指定日期的日誌\n s=<yyyy-MM-dd> : 讀取指定日期的日誌\n";case"--exit":case"--close":return"安全離開\n 用法 1: exit\n 用法 2: close\n";default:return"用法: [可用指令] [額外參數]\n 範例:\n help\n help --log\n 可用指令列表:\n help : 獲得可用的指令,使用 [--可用指令] 獲得該指令的詳細說明。\n status : 顯示當前的服務狀態\n log : 讀取今天的日誌\n exit : 關閉連線\n close : exit 的別稱,關閉連線\n"}}function ye(e){return async(...t)=>{const{core:s,channel:o}=e.getStatusInfo(),r=o.reduce((e,t,s)=>e+` channel ${s+1} - ${t}\n`,"\n");let n=`服務狀態\n 核心狀態 : ${s}\n 通道列表 : ${0===o.length?"無服務通道":r}\n`;if(e.automaion){const{isOpen:t,isReady:s}=await e.automaion.getStateInfo();n+=" Unreal : ",n+=t?s?"已啟動,服務已就緒":"已啟動,但服務未就緒":"尚未啟動",n+="\n"}return n}}function Se(...e){const t=e[0];if(!t)return new Promise(e=>{N.readdir(J.appFolder,(t,s)=>{e(t?"沒有可用日誌":s.filter(e=>e.endsWith(".txt")).join("\n"))})});{let e=null;switch(t){case"--today":case"--t":e=p();break;case"--yesterday":case"--y":e=p().add(-1);break;default:{const s=new RegExp(/^--(specific|s)=([0-9]{4}-[0-9]{1,2}-[0-9]{1,2})$/),o=t.match(s);if(o){const[t,s,r]=o[2].split("-");e=p(`${t}-${s.padStart(2,"0")}-${r.padStart(2,"0")}`);break}return"請使用 -- 開始,或使用 help --log 查看幫助"}}if(!e.isValid())return"指定了無效的日期,請再次確認";{const t=`log_${e.format("yyyy-MM-DD")}.txt`,s=J.addPath(J.appFolder,t);try{return N.readFileSync(s,"utf8")}catch(e){return"找不到日誌"}}}}class be{server;constructor(t,s){const o=function(e){return e?{help:me,status:ye(e),log:Se}:{}}(s),{port:r,host:n}=t;this.server=x(t=>{console.log(`⚙️ 新的連線來自: ${t.remoteAddress}`);const s=I.createInterface({input:t,output:t,prompt:"Aiia Admin > "});t.setEncoding("utf8"),t.write("歡迎使用Aiia Admin!\n"),t.write("使用 `help` 獲得幫助\n"),s.prompt(),s.on("line",async r=>{const[n,...i]=r.trim().split(/\s/),a=n.toLocaleLowerCase();if("exit"===a||"close"===a)t.write("👋 切斷連線...\n"),t.end();else{const r=e(o,a,null);if(r){const e=await r(...i);t.write("\n"+e+"\n")}else t.write(`"${n}"是無效的指令.\n`);s.prompt()}}),t.on("end",()=>{console.log(`⚙️ 切斷連線: ${t.remoteAddress}`)})}),this.server.listen(r,n,()=>{console.log(`連線指令: telnet 127.0.0.1 ${r}`)})}onDestroy(){this.server.close()}}class _e{config;stroage;expiment;apiProxy;chat;ueTools;automaion;state;browserSocketMap;destorySub;access_token;timestamp;socketAddr;get accessToken(){return this.access_token}constructor(e,t,s){this.config=e,this.stroage=t,this.expiment=s,this.state=0,this.destorySub=new S,this.browserSocketMap=new Map,this.timestamp=0,this.access_token=t.getItemSync(v);const{api:o,socket:r}=e.endPoint;this.socketAddr=r,this.apiProxy=new K(o),Y.code(U["指定模組初始化完成"],"api"),this.chat=new H(e),Y.code(U["指定模組初始化完成"],"chat"),this.ueTools=new ie(e),Y.code(U["指定模組初始化完成"],"udp tools"),this.openChannel=this.openChannel.bind(this),this.initService()}async start(){if(this.state>=1)throw new Error(L["重複初始化"]);this.state=1,await this.replaceAccessToken(),this.chat.memberChange.pipe(_(this.destorySub)).subscribe(({type:t,socket:s})=>{const o=s;switch(t){case"in":{const t=new V(s.id),n=new S;o.__cloudProxy=t;const i=(r=s.handshake.query.uuid,d(r)?e(r,"[0]",void 0):r??void 0);void 0===i?(this.getProjectList().then(e=>{s.emit("list",e)}).catch(e=>{Y.error(e),s.emit("list",null)}),b(s,"uuid").pipe(w(1),_(n)).subscribe(e=>{this.openChannel(e,o,t,n)})):this.openChannel(i,o,t,n),this.browserSocketMap.set(o,n);break}case"out":if(this.browserSocketMap.has(o)){const e=this.browserSocketMap.get(o);e.next(),e.complete(),this.browserSocketMap.delete(o)}}var r}),this.chat.start(),Y.code(U["啟動服務"],"chat");const{interaction:s}=t({},this.expiment);if(void 0!==s&&"number"==typeof s.port){const{port:e,host:t}=s,o=new be({port:e,host:t},this);this.destorySub.subscribe(()=>{o.onDestroy()})}}initService(){const{automaion:e}=t({},this.expiment);if(void 0!==e){const t=new fe(e);t.exec().then(e=>{this.ueTools.onStart(e)}),this.destorySub.subscribe(()=>{t.onDestroy()}),this.automaion=t}else this.ueTools.onStart();E(18e4).pipe(_(this.destorySub)).subscribe(()=>{this.inspectClientSocket()})}async replaceAccessToken(){try{const e=await this.apiProxy.getAccessToken(this.config.license,this.access_token),t=this.access_token;this.access_token=e,this.timestamp=0,this.stroage.setItemSync(v,e),this.stroage.setItemSync(p().format("yyyy-MM-DD[T]HH:mm:ss:sss"),t)}catch(e){throw Y.error(e),e}}async getProjectList(){try{return await this.apiProxy.getProjectList(this.config.license)}catch(e){throw Y.error(e),e}}async getProxySocketURL(e){const t=Date.now()-this.timestamp;return t<5e3&&await F(5100-t),this.timestamp=Date.now(),`${this.socketAddr}/v1/chat/project/${this.accessToken}/${e}`}async openChannel(t,o,r,n){b(o,"cloud").pipe(_(n)).subscribe(e=>{r.sendToCloud(e)}),b(o,"face").pipe(_(n)).subscribe(e=>{const t=e,s=null!==t;this.ueTools.eyeTrack(t),r.sendToCloud({request:"face_detect",content:s})}),r.message.pipe(_(n)).subscribe(t=>{const r=function(t){try{const s=JSON.parse(t);return e(s,"signal")?[s]:d(s)?s:l(s)}catch(e){return Y.error("message transfer fail",e),[]}}(t),n=[],i=[];s(r,e=>{switch(e.signal){case"osc":i.push(e);break;case"audio":"pcm"===e.command?this.ueTools.speech(e.content):"interrupted"===e.command&&this.ueTools.interrupted();default:n.push(e)}}),n.length>0&&o.emit("cloud",n),i.length>0&&this.ueTools.movement(i)}),r.close.pipe(_(n)).subscribe(async e=>{switch(o.emit("channel",e),e){case"close":case"no_permissions":r.onDestroy();break;case"reconnect":try{await this.replaceAccessToken();const e=await this.getProxySocketURL(t);o.connected?r.reconnect(e):Y.error(`The browser(${r.id}) leaves before reconnecting to the cloud`)}catch(e){r.onDestroy(),o.emit("channel","net_error"),Y.error(e)}}}),r.linkStart.pipe(_(n)).subscribe(()=>{o.emit("channel","open")}),n.subscribe(()=>{r.onClose()});const i=await this.getProxySocketURL(t);o.connected?r.start(i):Y.error(`The browser(${r.id}) leaves before connecting to the cloud`)}inspectClientSocket(){const e=Array.from(this.browserSocketMap);s(e,([e,t])=>{e.disconnected&&(t.next(),t.complete(),this.browserSocketMap.delete(e),Y.info(`The browser(${e.id}) should be closed. Auto clean service.`))})}getStatusInfo(){return{core:0===this.state?"未啟動":1===this.state?"服務中":"已結束",channel:Array.from(this.browserSocketMap).map(([e])=>e.__cloudProxy.getStatusInfo())}}onDestroy(){this.state=2,this.apiProxy.onDestroy(),this.chat.onDestroy(),this.ueTools.onDestroy(),this.destorySub.next(),this.destorySub.complete(),Y.code(U["服務結束"])}}function we(s){if($){if("string"!=typeof e(s,"license",void 0))throw new Error(L["未提供憑證"]);!function(e){const{info:t,error:s,warn:o,debug:r,fatal:n,code:i}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);return i||(j.code=()=>{}),r||(j.debug=()=>{}),t||(j.info=()=>{}),o||(j.warn=()=>{}),s||(j.error=()=>{}),n||(j.fatal=()=>{}),B}(e(s,"debug",!0))(new Q),Y.code(U["開始初始化必要模組"]);const o=new W(t({},s));Y.code(U["指定模組初始化完成"],"config");const r=new Z;Y.code(U["指定模組初始化完成"],"storage");const n=new _e(o,r,e(s,"experiment"));return Y.code(U["指定模組初始化完成"],"core"),n.start(),Y.code(U["啟動服務"],"main core"),()=>{n.onDestroy()}}throw new Error(L["環境錯誤(nodejs)"])}export{we as aiiaCore};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphen.ai/aiia-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
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",
|
|
@@ -70,6 +71,7 @@
|
|
|
70
71
|
},
|
|
71
72
|
"dependencies": {
|
|
72
73
|
"@mediapipe/tasks-vision": "^0.10.22-rc.20250304",
|
|
74
|
+
"@nut-tree-fork/nut-js": "^4.2.6",
|
|
73
75
|
"axios": "^1.11.0",
|
|
74
76
|
"lodash-es": "^4.17.21",
|
|
75
77
|
"mathjs": "^14.8.1",
|
|
@@ -78,6 +80,7 @@
|
|
|
78
80
|
"rxjs": "^7.8.2",
|
|
79
81
|
"socket.io": "^4.8.1",
|
|
80
82
|
"socket.io-client": "^4.8.1",
|
|
83
|
+
"systeminformation": "^5.27.11",
|
|
81
84
|
"ws": "^8.18.3"
|
|
82
85
|
}
|
|
83
86
|
}
|