@graphen.ai/aiia-sdk 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/aiia-worklet.js +1 -0
- package/dist/browser.cjs +1 -1
- package/dist/browser.d.ts +99 -33
- package/dist/browser.mjs +1 -1
- package/dist/node.cjs +1 -1
- package/dist/node.d.ts +46 -9
- package/dist/node.mjs +1 -1
- package/package.json +2 -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-11-4 */
|
|
1
2
|
!function(){"use strict";var t;!function(t){t[t.unknow=0]="unknow",t[t.allowed=1]="allowed",t[t.rejected=2]="rejected"}(t||(t={}));class e extends AudioWorkletProcessor{inputBuffer;inputBufferLength;processChunkSize;outputSampleRate;originalSampleRate;constructor(t){super(),this.inputBuffer=[],this.inputBufferLength=0;const{outputSampleRate:e,chunkTimeInSeconds:n}=Object.assign({outputSampleRate:16e3,chunkTimeInSeconds:1},t?.processorOptions);this.originalSampleRate=sampleRate,this.outputSampleRate=e,this.processChunkSize=function(t,e,n=1){const s=e*n,u=t/e;return Math.ceil(s*u)}(this.originalSampleRate,this.outputSampleRate,n)}process(t,e,n){const s=t[0][0];if(!s)return!0;const u=new Float32Array(s);if(this.inputBuffer.push(u),this.inputBufferLength+=s.length,this.inputBufferLength>=this.processChunkSize){const t=function(t){const e=t.reduce((t,e)=>t+e.length,0),n=new Float32Array(e);let s=0;for(const e of t)n.set(e,s),s+=e.length;return n}(this.inputBuffer);this.inputBuffer=[],this.inputBufferLength=0;const e=function(t,e,n){if(e===n)return t;const s=n/e,u=Math.ceil(t.length*s),i=new Float32Array(u);for(let e=0;e<u;e++){const n=e/s,u=Math.floor(n),o=n-u,r=t[u],a=t[Math.min(u+1,t.length-1)];i[e]=r+(a-r)*o}return i}(t,this.originalSampleRate,this.outputSampleRate);this.port.postMessage({float32:e,outputSampleRate:this.outputSampleRate},[e.buffer])}return!0}}registerProcessor("aiia-vad",e)}();
|
package/dist/browser.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("lodash-es"),t=require("moment");require("mathjs");var i,s=require("rxjs"),a=require("@mediapipe/tasks-vision"),n=require("socket.io-client");!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(i||(i={}));const o="undefined"!=typeof window;"undefined"!=typeof process&&null!=process.versions&&process.versions.node;class r{config;get webserver(){return this.config.webserver}get environment(){return this.config.env}get license(){return this.config.license??""}get port(){return function(e,t){const i=t??NaN;if(e){const t=parseInt(e);return isNaN(t)?i:t}return i}(process.env.PORT,this.config.port??3e3)}get ws_url(){return this.config.ws_url??""}get worklet_url(){return this.config.worklet_url??""}get project(){const{project:t}=e.assign({},this.config);return void 0===t?{specific:!1}:"string"==typeof t?{name:t,id:t,specific:!1}:e.assign({},t,{specific:!0})}get endPoint(){return this.environment,{api:"https://aiia-content-management-dev-21193779403.asia-east1.run.app",socket:"wss://graphen-agentic-workflow-dev-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(){const{user:t,aiia:i}=e.assign({},this.config.autoClearSubtitle);let s=5e3,a=5e3;return"number"==typeof t?s=t>=0?t:s:!1===t&&(s=NaN),"number"==typeof i?a=i>=0?i:a:!1===i&&(a=NaN),{userDelayTime:s,aiiaDelayTime:a}}get eyeTrackEnable(){return this.config.eyeTrackEnable??!1}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 C{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 v{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 _{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 v(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;resetSession(){throw new Error("Please use `reset` instead, 請使用`reset`代替")}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 C(i.mediaStream);d.code(c["指定依賴已完成"],"config");const a=new _(i,s);return d.code(c["指定依賴已完成"],"sdk"),""!==i.worklet_url&&s.init(i.worklet_url,i),a}throw new Error(i["環境錯誤(browser)"])};
|
|
1
|
+
"use strict";var e=require("lodash-es"),t=require("moment");require("mathjs");var s,i=require("rxjs"),a=require("@mediapipe/tasks-vision"),n=require("socket.io-client");!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(s||(s={}));const o="undefined"!=typeof window;"undefined"!=typeof process&&null!=process.versions&&process.versions.node;class r{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e.get(t,"id",void 0),i=e.get(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof i?i:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return e.assign({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return e.assign({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e.get(this.config,t,void 0)}}var c;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(c||(c={}));const u={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},d=[];const h=new Proxy(u,{get(s,i,a){if("string"==typeof i)switch(i){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...a)=>{const n=t().format("yyyy-MM-DD[T]HH:mm:ss:sss");e.forEach(d,e=>{e.next(i,n,...a)}),s[i](`[Aiia::${i}_${n}_]`,...a)};default:return}},set:(e,t,s,i)=>!0});var l,b;function p(e,t,s){return Math.max(s,Math.min(e,t))}function m(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=t.getInt16(2*i,!0)/32768;s.push(e)}return new Float32Array(s)}function g(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=p(32768*t.getFloat32(4*i,!0),32767,-32768);s.push(e)}return s}!function(e){e[e.unknow=0]="unknow",e[e.allowed=1]="allowed",e[e.rejected=2]="rejected"}(l||(l={}));class S{audioContext;type="audio";currentNode;bufferQueue;gainNode;set volume(e){e<0&&(e=0),e>100&&(e=100),this.gainNode.gain.value=e/100}get volume(){return Math.round(100*this.gainNode.gain.value)}constructor(e){this.audioContext=e,this.bufferQueue=[],this.currentNode=null,this.gainNode=this.audioContext.createGain(),this.gainNode.connect(this.audioContext.destination)}addBuffer(e){this.bufferQueue.push(e)}addBufferByFloat32(e,t=1){const s=e.length,i=this.audioContext.sampleRate,a=this.audioContext.createBuffer(t,s,i),n=e.slice();for(let e=0;e<t;e++)a.copyToChannel(n,e);this.addBuffer(a)}addBufferByInt16(e,t=1){this.addBufferByFloat32(m(e),t)}startSpeech(){null===this.currentNode&&this.continue()}pauseSpeech(){this.audioContext.suspend().catch(e=>{h.error("pause speech fail",e)})}resumeSpeech(){this.audioContext.resume().catch(e=>{h.error("resume speech fail",e)})}nextSpeech(){this.bufferQueue.length>0&&(this.clearNode(),this.continue())}stopSpeech(){this.bufferQueue=[],this.clearNode()}clearNode(){this.currentNode&&(this.currentNode.onended=function(){},this.currentNode.stop(),this.currentNode.disconnect(),this.currentNode=null)}continue(){const e=this.bufferQueue.shift();if(e){this.currentNode=this.audioContext.createBufferSource(),this.currentNode.buffer=e,this.currentNode.connect(this.gainNode);const t=this.continue.bind(this);this.currentNode.onended=t,this.currentNode.start()}else this.clearNode()}}class f{stream;audioContext;type="vad";workletNode;source;isRecording=!1;outputSampleRate;chunkTimeInSeconds;pcmSub;get pcm(){return this.pcmSub.asObservable()}constructor(e,t,s){this.stream=e,this.audioContext=t,this.outputSampleRate=s?.llmSampleRate??16e3,this.chunkTimeInSeconds=s?.chunkTimeInSeconds??1,this.pcmSub=new i.Subject}async startRecord(){this.isRecording||(this.source=this.audioContext.createMediaStreamSource(this.stream),this.workletNode=new AudioWorkletNode(this.audioContext,"aiia-vad",{processorOptions:{outputSampleRate:this.outputSampleRate,chunkTimeInSeconds:this.chunkTimeInSeconds}}),this.source.connect(this.workletNode),this.workletNode.port.onmessage=e=>{const{float32:t}=e.data;t&&this.pcmSub.next(new Float32Array(t))},this.isRecording=!0)}stopRecord(){this.isRecording&&(this.workletNode&&(this.workletNode.port.onmessage=null,this.workletNode.disconnect()),this.source&&this.source.disconnect(),this.isRecording=!1)}}class _{type="camera";aiiaCamera;get detections(){return this.aiiaCamera.detections}get landmarks(){return this.aiiaCamera.landmarks}get videoSize(){return this.aiiaCamera.videoSize}constructor(t){const{perSecond:s}=e.assign({perSecond:3},t);this.aiiaCamera=new y,this.aiiaCamera.setFrequency(s),document.body.append(this.aiiaCamera)}async init(e){try{const t=await a.FilesetResolver.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.0/wasm"),s=await a.FaceDetector.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite",delegate:"GPU"},runningMode:"VIDEO"});this.aiiaCamera.setStream(e),this.aiiaCamera.setFaceDetector(s);const i=await a.PoseLandmarker.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task",delegate:"GPU"},runningMode:"VIDEO",numPoses:5});this.aiiaCamera.setPoseLandmarker(i)}catch(e){h.error("Camera init fail",e)}}startCapture(){this.aiiaCamera.startCapture()}stopCapture(){this.aiiaCamera.stopCapture()}onDestroy(){this.aiiaCamera.remove()}}class y extends HTMLElement{static get observedAttributes(){return["debug"]}_shadow;_containerEl;_styleEl;_videoEl;_canvasEl;_canvasCtx;_isDebug;_debugElements;_interval;_lastTimestamp;_loopFrameID;_faceDetector;_detectionsSub;_poseLandmarker;_landmarksSub;_isReady;_drawingUtils;get detections(){return this._detectionsSub.asObservable()}get landmarks(){return this._landmarksSub.asObservable()}get videoSize(){return{width:this._videoEl.videoWidth,height:this._videoEl.videoHeight}}constructor(){super(),this._isDebug=!1,this._isReady=!1,this._shadow=this.attachShadow({mode:"open"}),this._containerEl=document.createElement("div"),this._videoEl=document.createElement("video"),this._styleEl=document.createElement("style"),this._canvasEl=document.createElement("canvas"),this._debugElements=[],this._loopFrameID=null,this._detectionsSub=new i.Subject,this._landmarksSub=new i.Subject,this._interval=Math.ceil(1e3/3),this._lastTimestamp=0,this.predictWebcam=this.predictWebcam.bind(this),this._settingContainer(),this._settingStyle(),this._settingVideo(),this._containerEl.appendChild(this._videoEl),this._shadow.appendChild(this._styleEl),this._shadow.appendChild(this._containerEl),this._containerEl.appendChild(this._canvasEl);const e=this._canvasEl.getContext("2d");e?(this._canvasCtx=e,this._drawingUtils=new a.DrawingUtils(this._canvasCtx)):(this._canvasCtx=null,this._drawingUtils=null)}connectedCallback(){this.startCapture()}disconnectedCallback(){this.stopCapture()}attributeChangedCallback(e,t,s){"debug"===e&&(this._isDebug=null!==s&&("true"===s||""===s),this._containerEl.style.visibility=this._isDebug?"visible":"hidden")}setStream(e){this._videoEl.onloadedmetadata=()=>{this._isReady=!0},this._videoEl.srcObject=e}setFaceDetector(e){this._faceDetector=e}setPoseLandmarker(e){this._poseLandmarker=e}setFrequency(e){this._interval=Math.ceil(1e3/e)}_settingStyle(){this._styleEl.textContent="\n .aiia_container {\n visibility: hidden;\n position: fixed;\n top: 0;\n left: 0;\n\n > canvas {\n position: absolute;\n top: 0;\n left: 0;\n width: 640px;\n height: 480px;\n }\n }\n .aiia_container .highlighter {\n background: rgba(0, 255, 0, 0.25);\n border: 1px dashed #fff;\n z-index: 1;\n position: absolute;\n }\n .aiia_container .confidence {\n position: absolute;\n padding-bottom: 5px;\n padding-top: 5px;\n background-color: #007f8b;\n color: #fff;\n border: 1px dashed rgba(255, 255, 255, 0.7);\n z-index: 2;\n font-size: 12px;\n margin: 0;\n }\n "}_settingContainer(){this._containerEl.classList.add("aiia_container")}_settingVideo(){this._videoEl.autoplay=!0,this._videoEl.playsInline=!0,this._videoEl.muted=!0}getVideo(){return this._videoEl}startCapture(){null===this._loopFrameID&&(this._videoEl.play(),this._loopFrameID=window.requestAnimationFrame(this.predictWebcam))}stopCapture(){null!==this._loopFrameID&&(window.cancelAnimationFrame(this._loopFrameID),this._loopFrameID=null),this._videoEl.pause()}async predictWebcam(){if(this._loopFrameID=window.requestAnimationFrame(this.predictWebcam),void 0===this._faceDetector||!this._isReady)return;if(void 0===this._poseLandmarker||!this._isReady)return;const e=performance.now();if(this._lastTimestamp+this._interval<=e){this._lastTimestamp=e;const t=this._faceDetector.detectForVideo(this._videoEl,e).detections,s=this._poseLandmarker.detectForVideo(this._videoEl,e).landmarks;this._isDebug&&(this._drawRect(t),this._drawRect2(s)),this._detectionsSub.next(t),this._landmarksSub.next(s)}}_drawRect(t){e.forEach(t,(e,t)=>{const{categories:s,boundingBox:i}=e;if(i){let e=this._debugElements[2*t],a=this._debugElements[2*t+1];e&&a||(e=document.createElement("div"),e.classList.add("highlighter"),a=document.createElement("p"),a.classList.add("confidence"),this._containerEl.append(a,e),this._debugElements.push(e,a)),e.style.display="block",a.style.display="block";const n=100*s[0].score;a.innerText=`Confidence: ${Math.round(n)} %`,a.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",a.style.top=i.originY-30+"px",a.style.width=i.width-10+"px",e.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",e.style.top=`${i.originY}px`,e.style.width=i.width-10+"px",e.style.height=`${i.height}px`}});for(let e=2*t.length;e<this._debugElements.length;e++)this._debugElements[e].style.display="none"}landmarkColors=["gray","lightgray","gray","gray","lightgray"];connectorColors=["green","blue","red","yellow","purple"];_drawRect2(t){this._canvasCtx&&this._drawingUtils&&(this._canvasCtx.save(),this._canvasCtx.clearRect(0,0,640,480),e.forEach(t,(e,t)=>{this._canvasCtx?.save(),this._drawingUtils?.drawLandmarks(e,{radius:e=>a.DrawingUtils.lerp(e.from.z,-.15,.1,5,1),color:this.landmarkColors[t]}),this._drawingUtils?.drawConnectors(e,a.PoseLandmarker.POSE_CONNECTIONS,{color:this.connectorColors[t],lineWidth:2})}),this._canvasCtx.restore())}destroy(){this._detectionsSub.complete(),this.remove()}}customElements.define("aiia-camera",y),function(e){e[e.padding=0]="padding",e[e.rejected=1]="rejected",e[e.loadfail=2]="loadfail",e[e.allowed=3]="allowed"}(b||(b={}));class v{stream;_state;stateSub;audioCtx;audioManager;vadManager;camManager;destroy;pcmSub;detectionsSub;landmarksSub;get sampleRate(){return this.audioCtx?this.audioCtx.sampleRate:16e3}get pcm(){return this.pcmSub.asObservable()}get detections(){return this.detectionsSub.asObservable()}get landmarks(){return this.landmarksSub.asObservable()}get state(){return this._state}set state(e){e!==this._state&&(this._state=e,this.stateSub.next(e))}get stateObs(){return this.stateSub.asObservable()}constructor(e){this.stream=e,this.stateSub=new i.ReplaySubject(1),this.pcmSub=new i.Subject,this.destroy=new i.Subject,this.detectionsSub=new i.Subject,this.landmarksSub=new i.Subject,this.state=b.padding}async init(e,t){const a=this.stream??await async function(e){try{return await navigator.mediaDevices.getUserMedia(e)}catch(e){return null}}({audio:{echoCancellation:!0,noiseSuppression:!0},video:!0});if(null===a)throw this.state=b.rejected,new Error(s["未獲得媒體裝置權限"]);this.stream=a,this.audioCtx=new AudioContext;try{await this.audioCtx.audioWorklet.addModule(e)}catch(e){throw h.fatal(e),this.state=b.loadfail,new Error(s["Worklet模組載入失敗"])}this.state=b.allowed,this.audioManager=new S(this.audioCtx),this.vadManager=new f(a,this.audioCtx,t),this.vadManager.pcm.pipe(i.takeUntil(this.destroy)).subscribe(e=>{this.pcmSub.next(e)});const n=new _(t?.cameraDetection);n.detections.pipe(i.takeUntil(this.destroy)).subscribe(e=>{const{width:t,height:s}=n.videoSize;this.detectionsSub.next({detections:e,clientWidth:t,clientHeight:s})}),n.landmarks.pipe(i.takeUntil(this.destroy)).subscribe(e=>{this.landmarksSub.next(e)}),n.init(a),this.camManager=n}addAudioQueue(t){const s=e.get(t,"buffer",void 0),i=e.get(t,"float32",void 0),a=e.get(t,"int16",void 0),n=e.get(t,"numberOfChannels",void 0);s?this.audioManager?.addBuffer(s):i?this.audioManager?.addBufferByFloat32(i,n):a&&this.audioManager?.addBufferByInt16(a,n)}playAudio(){this.audioManager?.startSpeech()}stopAudio(){this.audioManager?.stopSpeech()}startRecord(){this.vadManager?.startRecord()}stopRecord(){this.vadManager?.stopRecord()}setVolume(e){this.audioManager&&(this.audioManager.volume=e)}getVolume(){return this.audioManager?this.audioManager.volume:0}startCapture(){this.camManager?.startCapture()}stopCapture(){this.camManager?.stopCapture()}onDestroy(){this.camManager?.onDestroy(),this.audioCtx?.close(),this.destroy.next(),this.destroy.complete()}}class C{socket;messageSub;destorySub;get message(){return this.messageSub.asObservable()}constructor(t){this.destorySub=new i.Subject,this.messageSub=new i.Subject;const{specific:s,id:a}=t.project;this.socket=n.io(t.ws_url,e.assign({autoConnect:!1},s&&void 0!==a?{query:{uuid:a}}:{}))}start(){const e="list",t="cloud",s="channel";i.fromEvent(this.socket,"connect").pipe(i.takeUntil(this.destorySub)).subscribe(()=>{h.debug("Client ID: ",this.socket.id)}),i.fromEvent(this.socket,e).pipe(i.take(1),i.takeUntil(this.destorySub)).subscribe(t=>{this.messageSub.next({event:e,data:t})}),i.fromEvent(this.socket,t).pipe(i.takeUntil(this.destorySub)).subscribe(e=>{this.messageSub.next({event:t,data:e})}),i.fromEvent(this.socket,s).pipe(i.takeUntil(this.destorySub)).subscribe(e=>{this.messageSub.next({event:s,data:e})}),this.socket.connect()}send(e,t){this.socket.emit(e,t)}sendToCloud(e){this.send("cloud",e)}onDestroy(){this.destorySub.next(),this.destorySub.complete(),this.messageSub.complete(),this.socket.disconnect()}}class w{config;media;chat;destroySub;projectsSub;layoutSub;aiiaSubtitleSub;userSubtitleSub;sampleRateOfWhisper;channelState;stateSub;_currentState;isMuteAiia;aiiaSubtitleClearSub;userSubtitleClearSub;_lastProjects;guestSub;_hasGuest;mediaEventReady;isStart;get state(){return this.stateSub.asObservable()}get currentState(){return this._currentState}get layout(){return this.layoutSub.asObservable()}get projects(){return this.projectsSub.asObservable()}get lastProjects(){return e.map(this._lastProjects,e=>({...e}))}get aiiaSubtitle(){return this.aiiaSubtitleSub.asObservable()}get userSubtitle(){return this.userSubtitleSub.asObservable()}get guest(){return this.guestSub.asObservable()}get hasGuest(){return this._hasGuest}set volume(e){this.media.setVolume(e)}get volume(){return this.media.getVolume()}constructor(e,t){this.config=e,this.media=t,this.layoutSub=new i.Subject,this.projectsSub=new i.ReplaySubject(1),this.destroySub=new i.Subject,this.aiiaSubtitleSub=new i.Subject,this.userSubtitleSub=new i.Subject,this.aiiaSubtitleClearSub=new i.Subject,this.userSubtitleClearSub=new i.Subject,this.stateSub=new i.BehaviorSubject("NotStart"),this.sampleRateOfWhisper={input:e.llmSampleRate,output:16e3},this.channelState={proxy:!1,cloud:!1},this.isMuteAiia=!1,this._lastProjects=[],this.guestSub=new i.ReplaySubject(1),this._hasGuest=!1,this.mediaEventReady=!1,this.isStart=!1,this.chat=new C(e),h.code(c["指定依賴已完成"],"chat"),this.close=this.onDestroy,this.behavior=this.behavior.bind(this);const{userDelayTime:s,aiiaDelayTime:a}=e.autoClearSubtitle;isNaN(a)||this.aiiaSubtitleClearSub.pipe(i.debounceTime(a),i.takeUntil(this.destroySub)).subscribe(()=>{this.aiiaSubtitleSub.next("")}),isNaN(s)||this.userSubtitleClearSub.pipe(i.debounceTime(s),i.takeUntil(this.destroySub)).subscribe(()=>{this.userSubtitleSub.next("")}),this.stateSub.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this._currentState=e}),this.projectsSub.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this._lastProjects=e}),this.guestSub.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this._hasGuest=e}),t.stateObs.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{b.allowed===e&&this.channelCheck()})}start(){this.isStart?h.code(c["已啟動SDK"]):(this.isStart=!0,this.chat.message.pipe(i.takeUntil(this.destroySub)).subscribe(({event:t,data:s})=>{switch(t){case"cloud":e.map(s,this.behavior);break;case"list":{const{id:t,name:i,specific:a}=this.config.project;let n=null;if(a&&void 0!==t)n=t;else{const a=e.find(s,e=>e.name===i||e.id===t);a&&(n=a.id)}null!==n?this.chooseProject(n):(this.projectsSub.next(s),this.stateSub.next("WaitingProjectID"),h.code(c["請訂閱專案列表"]));break}case"channel":switch(this.channelState.proxy=!0,s){case"open":this.channelCheck();break;case"close":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("CloseService");break;case"no_permissions":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("NoPermissions");break;case"reconnect":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("ReconnectingCloud")}}}),this.chat.start(),this.stateSub.next("ConnectingCloud"),h.code(c["啟動SDK"]))}close;skip(){this.chat.sendToCloud({request:"skip"})}reset(){this.chat.sendToCloud({request:"reset"})}chooseProject(e){this.chat.send("uuid",e),this.stateSub.next("ConnectingCloud")}mute(){this.isMuteAiia=!0,this.media.stopAudio(),this.media.stopRecord()}unmute(){this.isMuteAiia=!1,this.media.startRecord()}behavior(t){switch(t.signal){case"layout":this.layoutSub.next(t.content);break;case"audio":if("sampleRate"===t.command)this.sampleRateOfWhisper.output=t.content;else switch(t.command){case"pcm":if(!this.isMuteAiia){const e=function(e,t,s){if(t===s)return e;const i=s/t,a=Math.ceil(e.length*i),n=new Float32Array(a);for(let t=0;t<a;t++){const s=t/i,a=Math.floor(s),o=s-a,r=e[a],c=e[Math.min(a+1,e.length-1)];n[t]=r+(c-r)*o}return n}(m(new Int16Array(t.content)),this.sampleRateOfWhisper.output,this.media.sampleRate);this.media.addAudioQueue({float32:e}),this.media.playAudio()}break;case"interrupted":this.media.stopAudio(),this.aiiaSubtitleSub.next("")}break;case"status":"Connected"===e.get(t,"content")&&(this.channelState.cloud=!0,this.channelCheck(),this.layoutSub.next({type:"resetLayout"}));break;case"agent":if(!this.isMuteAiia){const s=e.get(t,"content.function_result.response");void 0!==s&&(this.aiiaSubtitleSub.next(s),this.aiiaSubtitleClearSub.next())}break;case"asr":if(!this.isMuteAiia&&"ASR"===e.get(t,"content.function_type")){const s=e.get(t,"content.function_result.result");void 0!==s&&(this.userSubtitleSub.next(s),this.userSubtitleClearSub.next())}break;case"cv":{const s=e.get(t,"content.status");"user present"===s?this.guestSub.next(!0):"user leave"===s?this.guestSub.next(!1):h.warn("Unexpect value",e.get(t,"content"));break}default:h.debug(t.signal)}}channelCheck(){const{proxy:t,cloud:s}=this.channelState;if(t&&s&&this.media.state===b.allowed){if(this.chat.sendToCloud({request:"audio",command:"sampleRate",content:this.sampleRateOfWhisper.input}),!this.mediaEventReady){this.mediaEventReady=!0,this.media.pcm.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"audio",command:"pcm",content:g(e)})});const t=(a=this.config.detection.confidence,function(t,s){const{width:i,height:n}=s,o=e.filter(t,e=>100*(e.categories[0]?.score??0)>=a),r=e.compact(e.map(o,e=>{if(void 0!==e.boundingBox){const t=e.boundingBox,s=t.width*t.height,a=e.categories[0]?.score??0;return{...t,area:s,score:100*a,clientWidth:i,clientHeight:n}}return null}));return e.sortBy(r,["area","score"])[0]??null});this.media.detections.pipe(i.takeUntil(this.destroySub),i.map(({detections:e,clientHeight:s,clientWidth:i})=>t(e,{width:i,height:s}))).subscribe(e=>{this.chat.send("face",e)}),this.media.landmarks.pipe(i.takeUntil(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"pose_detect",content:e})})}this.media.startRecord(),this.media.startCapture(),this.stateSub.next("InService"),h.code(c["通道、雲端服務、麥克風都已就緒"])}else t&&s&&this.media.state!==b.allowed&&(this.stateSub.next("InServiceNoMedia"),h.code(c["未取得媒體裝置權限,請重新設定或忽略此訊息"]));var a}onDestroy(){this.chat.onDestroy(),this.media.onDestroy(),this.destroySub.next(),this.destroySub.complete(),this.stateSub.next("Destroy"),this.stateSub.complete(),h.code(c["結束SDK"])}}exports.initSdk=function(t){if(o){!function(e){const{info:t,error:s,warn:i,debug:a,fatal:n,code:o}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);o||(u.code=()=>{}),a||(u.debug=()=>{}),t||(u.info=()=>{}),i||(u.warn=()=>{}),s||(u.error=()=>{}),n||(u.fatal=()=>{})}(e.get(t,"debug",!0)),h.code(c["初始化必要依賴"]);const s=new r(e.assign({},t));h.code(c["指定依賴已完成"],"config");const i=new v(s.mediaStream);h.code(c["指定依賴已完成"],"config");const a=new w(s,i);return h.code(c["指定依賴已完成"],"sdk"),""!==s.worklet_url&&i.init(s.worklet_url,s),a}throw new Error(s["環境錯誤(browser)"])};
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Detection } from '@mediapipe/tasks-vision';
|
|
2
2
|
import { Http2SecureServer } from 'http2';
|
|
3
3
|
import { Http2Server } from 'http2';
|
|
4
|
+
import type { Landmark } from '@mediapipe/tasks-vision';
|
|
4
5
|
import { Observable } from 'rxjs';
|
|
5
6
|
import { Server } from 'http';
|
|
6
7
|
import { Server as Server_2 } from 'https';
|
|
@@ -55,6 +56,7 @@ export declare namespace aiia_msg {
|
|
|
55
56
|
LAYOUT,
|
|
56
57
|
AUDIO,
|
|
57
58
|
CV,
|
|
59
|
+
MovementToUE,
|
|
58
60
|
CLOUD_SOCKET_MSG,
|
|
59
61
|
Signal,
|
|
60
62
|
SendMessage_MiniGame,
|
|
@@ -64,6 +66,8 @@ export declare namespace aiia_msg {
|
|
|
64
66
|
SendMessage_Audio,
|
|
65
67
|
SendMessage_Origin,
|
|
66
68
|
SendMessage_Face,
|
|
69
|
+
LandmarkResult,
|
|
70
|
+
SendMessage_Pose,
|
|
67
71
|
SendMessage
|
|
68
72
|
}
|
|
69
73
|
}
|
|
@@ -71,14 +75,14 @@ export declare namespace aiia_msg {
|
|
|
71
75
|
declare class AiiaConfig {
|
|
72
76
|
private config;
|
|
73
77
|
get webserver(): (Server<any, any> | Server_2<any, any> | Http2SecureServer<any, any, any, any> | Http2Server<any, any, any, any>) | undefined;
|
|
74
|
-
get environment():
|
|
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
|
};
|
|
@@ -96,19 +100,31 @@ declare class AiiaConfig {
|
|
|
96
100
|
aiiaDelayTime: number;
|
|
97
101
|
};
|
|
98
102
|
get eyeTrackEnable(): boolean;
|
|
103
|
+
get eyeTrackCorrection(): {
|
|
104
|
+
x_axis_px: number;
|
|
105
|
+
y_axis_px: number;
|
|
106
|
+
};
|
|
99
107
|
constructor(config: InitOptions);
|
|
108
|
+
private safeRead;
|
|
100
109
|
}
|
|
101
110
|
|
|
102
|
-
|
|
111
|
+
declare interface AiiaProjectItem {
|
|
103
112
|
id: string;
|
|
104
113
|
name: string;
|
|
105
114
|
}
|
|
106
115
|
|
|
116
|
+
export declare interface AiiaProjectItemWithAvatar extends AiiaProjectItem {
|
|
117
|
+
avatar: {
|
|
118
|
+
name: string;
|
|
119
|
+
url: string;
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
107
123
|
declare class AiiaSdk {
|
|
108
124
|
private config;
|
|
109
125
|
private media;
|
|
110
126
|
private chat;
|
|
111
|
-
private
|
|
127
|
+
private destroySub;
|
|
112
128
|
private projectsSub;
|
|
113
129
|
private layoutSub;
|
|
114
130
|
private aiiaSubtitleSub;
|
|
@@ -123,6 +139,8 @@ declare class AiiaSdk {
|
|
|
123
139
|
private _lastProjects;
|
|
124
140
|
private guestSub;
|
|
125
141
|
private _hasGuest;
|
|
142
|
+
private mediaEventReady;
|
|
143
|
+
private isStart;
|
|
126
144
|
/**
|
|
127
145
|
* [zh]
|
|
128
146
|
* 可訂閱,服務狀態
|
|
@@ -170,7 +188,7 @@ declare class AiiaSdk {
|
|
|
170
188
|
* [en]
|
|
171
189
|
* If no Project ID is explicitly specified, please subscribe to this object
|
|
172
190
|
*/
|
|
173
|
-
get projects(): Observable<
|
|
191
|
+
get projects(): Observable<AiiaProjectItemWithAvatar[]>;
|
|
174
192
|
/**
|
|
175
193
|
* [zh]
|
|
176
194
|
* 可讀取最近一次收到的專案列表
|
|
@@ -179,6 +197,10 @@ declare class AiiaSdk {
|
|
|
179
197
|
* You can read the list of projects received recently
|
|
180
198
|
*/
|
|
181
199
|
get lastProjects(): {
|
|
200
|
+
avatar: {
|
|
201
|
+
name: string;
|
|
202
|
+
url: string;
|
|
203
|
+
};
|
|
182
204
|
id: string;
|
|
183
205
|
name: string;
|
|
184
206
|
}[];
|
|
@@ -248,16 +270,6 @@ declare class AiiaSdk {
|
|
|
248
270
|
* Stop all services to release resources
|
|
249
271
|
*/
|
|
250
272
|
close: () => void;
|
|
251
|
-
/**
|
|
252
|
-
* @deprecated
|
|
253
|
-
*
|
|
254
|
-
* [zh]
|
|
255
|
-
* 重置當前對話,清除播放中的語音
|
|
256
|
-
*
|
|
257
|
-
* [en]
|
|
258
|
-
* Reset the current conversation and clear the playing voice.
|
|
259
|
-
*/
|
|
260
|
-
resetSession(): void;
|
|
261
273
|
/**
|
|
262
274
|
* [zh]
|
|
263
275
|
* 中斷當前對話
|
|
@@ -282,7 +294,7 @@ declare class AiiaSdk {
|
|
|
282
294
|
* If no Project is explicitly specified, please execute this method.
|
|
283
295
|
* @param project_id
|
|
284
296
|
*/
|
|
285
|
-
chooseProject(project_id:
|
|
297
|
+
chooseProject(project_id: AiiaProjectItemWithAvatar["id"]): void;
|
|
286
298
|
/**
|
|
287
299
|
* [zh]
|
|
288
300
|
* 不同於`this.volume = 0`的調整輸出音量
|
|
@@ -303,7 +315,6 @@ declare class AiiaSdk {
|
|
|
303
315
|
* Restore audio signal data
|
|
304
316
|
*/
|
|
305
317
|
unmute(): void;
|
|
306
|
-
private cloudToObj;
|
|
307
318
|
private behavior;
|
|
308
319
|
private channelCheck;
|
|
309
320
|
/**
|
|
@@ -316,7 +327,7 @@ declare class AiiaSdk {
|
|
|
316
327
|
onDestroy(): void;
|
|
317
328
|
}
|
|
318
329
|
|
|
319
|
-
export declare type AiiaState = "NotStart" | "
|
|
330
|
+
export declare type AiiaState = "NotStart" | "WaitingProjectID" | "ConnectingCloud" | "ReconnectingCloud" | "InService" | "InServiceNoMedia" | "NoPermissions" | "CloseService" | "Destroy";
|
|
320
331
|
|
|
321
332
|
declare interface ASR {
|
|
322
333
|
signal: "asr";
|
|
@@ -351,9 +362,9 @@ declare type AUDIO = {
|
|
|
351
362
|
command: "interrupted";
|
|
352
363
|
};
|
|
353
364
|
|
|
354
|
-
export declare type BrowserInitOptions = Omit<InitOptions, "webserver" | "port" | "license" | "eyeTrackEnable">;
|
|
365
|
+
export declare type BrowserInitOptions = Omit<InitOptions, "webserver" | "port" | "license" | "eyeTrackEnable" | "correction">;
|
|
355
366
|
|
|
356
|
-
declare type CLOUD_SOCKET_MSG = TTS | MIC | ASR | AGENT | OLD_ASR | OLD_AGENT | RATINGS | QUESTIONNAIRE | LAYOUT | AUDIO | CV;
|
|
367
|
+
declare type CLOUD_SOCKET_MSG = TTS | MIC | ASR | AGENT | OLD_ASR | OLD_AGENT | RATINGS | QUESTIONNAIRE | LAYOUT | AUDIO | CV | MovementToUE;
|
|
357
368
|
|
|
358
369
|
declare interface CV {
|
|
359
370
|
signal: "cv";
|
|
@@ -381,10 +392,10 @@ declare interface InitOptions {
|
|
|
381
392
|
project?: string | Partial<AiiaProjectItem>;
|
|
382
393
|
/**
|
|
383
394
|
* [zh]
|
|
384
|
-
* 指定的環境,預設值:
|
|
395
|
+
* 指定的環境,預設值: developement
|
|
385
396
|
*
|
|
386
397
|
* [en]
|
|
387
|
-
* Specific Env, default:
|
|
398
|
+
* Specific Env, default: developement
|
|
388
399
|
*/
|
|
389
400
|
env?: "developement" | "production";
|
|
390
401
|
/**
|
|
@@ -456,15 +467,17 @@ declare interface InitOptions {
|
|
|
456
467
|
*/
|
|
457
468
|
llmSampleRate?: number;
|
|
458
469
|
/**
|
|
470
|
+
* @deprecated
|
|
471
|
+
*
|
|
459
472
|
* [zh]
|
|
460
|
-
* 人臉辨識
|
|
473
|
+
* 人臉辨識(已棄用,請改用 `cameraDetection`),同時設定會以 `cameraDetection` 為主
|
|
461
474
|
*
|
|
462
475
|
* @property perSecond: 每秒檢測幾次, 預設: 每秒 3 次
|
|
463
476
|
*
|
|
464
477
|
* @property confidence: 分數(0 ~ 100), 預設: 80
|
|
465
478
|
*
|
|
466
479
|
* [en]
|
|
467
|
-
*
|
|
480
|
+
* Face recognition (deprecated, please use `cameraDetection` instead), and the settings will be based on `cameraDetection`
|
|
468
481
|
*
|
|
469
482
|
* @property perSecond: Detection times per second, default: 3 times per second
|
|
470
483
|
*
|
|
@@ -474,6 +487,25 @@ declare interface InitOptions {
|
|
|
474
487
|
perSecond?: number;
|
|
475
488
|
confidence?: number;
|
|
476
489
|
};
|
|
490
|
+
/**
|
|
491
|
+
* [zh]
|
|
492
|
+
* 攝影機辨識設定
|
|
493
|
+
*
|
|
494
|
+
* @property perSecond: 每秒檢測幾次, 預設: 每秒 3 次
|
|
495
|
+
*
|
|
496
|
+
* @property confidence: 人臉辨識分數(0 ~ 100), 預設: 80
|
|
497
|
+
*
|
|
498
|
+
* [en]
|
|
499
|
+
* Camera Identification Settings
|
|
500
|
+
*
|
|
501
|
+
* @property perSecond: Detection times per second, default: 3 times per second
|
|
502
|
+
*
|
|
503
|
+
* @property confidence: Face recognition score(0 ~ 100), default: 80
|
|
504
|
+
*/
|
|
505
|
+
cameraDetection?: {
|
|
506
|
+
perSecond?: number;
|
|
507
|
+
confidence?: number;
|
|
508
|
+
};
|
|
477
509
|
/**
|
|
478
510
|
* [zh]
|
|
479
511
|
* 清除字幕,可指定時間(毫秒),預設 5000 毫秒後清除
|
|
@@ -481,10 +513,15 @@ declare interface InitOptions {
|
|
|
481
513
|
* [en]
|
|
482
514
|
* Clear subtitles, you can specify the time (milliseconds), the default is to clear after 5000 milliseconds.
|
|
483
515
|
*/
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
516
|
+
autoClearUserSubtitle?: number;
|
|
517
|
+
/**
|
|
518
|
+
* [zh]
|
|
519
|
+
* 清除字幕,可指定時間(毫秒),預設 音訊結束時 清除
|
|
520
|
+
*
|
|
521
|
+
* [en]
|
|
522
|
+
* Clear subtitles, you can specify the time (milliseconds), the default Clear when audio ends.
|
|
523
|
+
*/
|
|
524
|
+
autoClearAiiaSubtitle?: number;
|
|
488
525
|
/**
|
|
489
526
|
* [zh]
|
|
490
527
|
* 啟動眼球追蹤
|
|
@@ -493,10 +530,23 @@ declare interface InitOptions {
|
|
|
493
530
|
*
|
|
494
531
|
*/
|
|
495
532
|
eyeTrackEnable?: boolean;
|
|
533
|
+
/**
|
|
534
|
+
* [zh]
|
|
535
|
+
* 眼球追蹤補正,請根據 Camera 裝置的物理位置添加補正值。
|
|
536
|
+
*
|
|
537
|
+
* [en]
|
|
538
|
+
* Eye tracking correction, please add correction value according to the physical position of the camera device.
|
|
539
|
+
*/
|
|
540
|
+
correction?: {
|
|
541
|
+
x_axis_px?: number;
|
|
542
|
+
y_axis_px?: number;
|
|
543
|
+
};
|
|
496
544
|
}
|
|
497
545
|
|
|
498
546
|
export declare function initSdk(options?: BrowserInitOptions): AiiaSdk;
|
|
499
547
|
|
|
548
|
+
declare type LandmarkResult = Landmark[][];
|
|
549
|
+
|
|
500
550
|
declare interface LAYOUT {
|
|
501
551
|
signal: "layout";
|
|
502
552
|
content: {
|
|
@@ -528,9 +578,10 @@ declare class MediaManager {
|
|
|
528
578
|
private audioManager?;
|
|
529
579
|
private vadManager?;
|
|
530
580
|
private camManager?;
|
|
531
|
-
private
|
|
581
|
+
private destroy;
|
|
532
582
|
private pcmSub;
|
|
533
583
|
private detectionsSub;
|
|
584
|
+
private landmarksSub;
|
|
534
585
|
get sampleRate(): number;
|
|
535
586
|
get pcm(): Observable<Float32Array<ArrayBufferLike>>;
|
|
536
587
|
get detections(): Observable< {
|
|
@@ -538,6 +589,7 @@ declare class MediaManager {
|
|
|
538
589
|
clientWidth: number;
|
|
539
590
|
clientHeight: number;
|
|
540
591
|
}>;
|
|
592
|
+
get landmarks(): Observable<aiia_msg.LandmarkResult>;
|
|
541
593
|
get state(): MediaStateEnum;
|
|
542
594
|
set state(v: MediaStateEnum);
|
|
543
595
|
get stateObs(): Observable<MediaStateEnum>;
|
|
@@ -545,7 +597,7 @@ declare class MediaManager {
|
|
|
545
597
|
init(workletUrl: string, config?: {
|
|
546
598
|
llmSampleRate?: AiiaConfig["llmSampleRate"];
|
|
547
599
|
chunkTimeInSeconds?: AiiaConfig["chunkTimeInSeconds"];
|
|
548
|
-
|
|
600
|
+
cameraDetection?: AiiaConfig["detection"];
|
|
549
601
|
}): Promise<void>;
|
|
550
602
|
addAudioQueue(input: {
|
|
551
603
|
buffer: AudioBuffer;
|
|
@@ -581,6 +633,15 @@ declare interface MIC {
|
|
|
581
633
|
};
|
|
582
634
|
}
|
|
583
635
|
|
|
636
|
+
declare interface MovementToUE {
|
|
637
|
+
signal: "osc";
|
|
638
|
+
content: {
|
|
639
|
+
host: string;
|
|
640
|
+
port: number;
|
|
641
|
+
message: [string, ...(string | number)[]];
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
|
|
584
645
|
/** @deprecated */
|
|
585
646
|
declare interface OLD_AGENT {
|
|
586
647
|
signal: "response";
|
|
@@ -633,7 +694,7 @@ declare type SendMessage = {
|
|
|
633
694
|
request: "reset";
|
|
634
695
|
} | {
|
|
635
696
|
request: "skip";
|
|
636
|
-
} | SendMessage_Layout | SendMessage_MiniGame | SendMessage_Audio | SendMessage_Face;
|
|
697
|
+
} | SendMessage_Layout | SendMessage_MiniGame | SendMessage_Audio | SendMessage_Face | SendMessage_Pose;
|
|
637
698
|
|
|
638
699
|
declare type SendMessage_Audio = SendMessage_Audio_Init | SendMessage_Audio_PCM;
|
|
639
700
|
|
|
@@ -681,6 +742,11 @@ declare interface SendMessage_Origin {
|
|
|
681
742
|
content?: any;
|
|
682
743
|
}
|
|
683
744
|
|
|
745
|
+
declare interface SendMessage_Pose {
|
|
746
|
+
request: "pose_detect";
|
|
747
|
+
content: LandmarkResult;
|
|
748
|
+
}
|
|
749
|
+
|
|
684
750
|
declare type Signal = CLOUD_SOCKET_MSG["signal"];
|
|
685
751
|
|
|
686
752
|
declare interface TTS {
|
package/dist/browser.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{assign as e,forEach as t,filter as i,compact as s,map as a,sortBy as o,get as n,find as r,isArray as c,values as u}from"lodash-es";import h from"moment";import"mathjs";import{Subject as d,takeUntil as l,fromEvent as p,take as b,ReplaySubject as m,BehaviorSubject as g,debounceTime as f,map as S}from"rxjs";import{FilesetResolver as y,FaceDetector as w}from"@mediapipe/tasks-vision";import{io as C}from"socket.io-client";var _;!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"}(_||(_={}));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(){return this.environment,{api:"https://aiia-content-management-dev-21193779403.asia-east1.run.app",socket:"wss://graphen-agentic-workflow-dev-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(){const{user:t,aiia:i}=e({},this.config.autoClearSubtitle);let s=5e3,a=5e3;return"number"==typeof t?s=t>=0?t:s:!1===t&&(s=NaN),"number"==typeof i?a=i>=0?i:a:!1===i&&(a=NaN),{userDelayTime:s,aiiaDelayTime:a}}get eyeTrackEnable(){return this.config.eyeTrackEnable??!1}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 I{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 A{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(_["未獲得媒體裝置權限"]);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(_["Worklet模組載入失敗"])}this.state=N.allowed,this.audioManager=new I(this.audioCtx),this.vadManager=new A(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=C(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;resetSession(){throw new Error("Please use `reset` instead, 請使用`reset`代替")}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(_["環境錯誤(browser)"])}export{q as initSdk};
|
|
1
|
+
import{get as e,assign as t,forEach as s,filter as i,compact as a,map as n,sortBy as o,find as r}from"lodash-es";import c from"moment";import"mathjs";import{Subject as u,ReplaySubject as d,takeUntil as h,fromEvent as l,take as p,BehaviorSubject as b,debounceTime as m,map as g}from"rxjs";import{DrawingUtils as f,PoseLandmarker as S,FilesetResolver as _,FaceDetector as y}from"@mediapipe/tasks-vision";import{io as C}from"socket.io-client";var w;!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(w||(w={}));const v="undefined"!=typeof window;"undefined"!=typeof process&&null!=process.versions&&process.versions.node;class x{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e(t,"id",void 0),i=e(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof i?i:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return t({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return t({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e(this.config,t,void 0)}}var R;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(R||(R={}));const E={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},k=[];const D=new Proxy(E,{get(e,t,i){if("string"==typeof t)switch(t){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...i)=>{const a=c().format("yyyy-MM-DD[T]HH:mm:ss:sss");s(k,e=>{e.next(t,a,...i)}),e[t](`[Aiia::${t}_${a}_]`,...i)};default:return}},set:(e,t,s,i)=>!0});var O,N;function M(e,t,s){return Math.max(s,Math.min(e,t))}function T(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=t.getInt16(2*i,!0)/32768;s.push(e)}return new Float32Array(s)}function A(e){const t=new DataView(e.buffer),s=[];for(let i=0;i<e.length;i++){const e=M(32768*t.getFloat32(4*i,!0),32767,-32768);s.push(e)}return s}!function(e){e[e.unknow=0]="unknow",e[e.allowed=1]="allowed",e[e.rejected=2]="rejected"}(O||(O={}));class j{audioContext;type="audio";currentNode;bufferQueue;gainNode;set volume(e){e<0&&(e=0),e>100&&(e=100),this.gainNode.gain.value=e/100}get volume(){return Math.round(100*this.gainNode.gain.value)}constructor(e){this.audioContext=e,this.bufferQueue=[],this.currentNode=null,this.gainNode=this.audioContext.createGain(),this.gainNode.connect(this.audioContext.destination)}addBuffer(e){this.bufferQueue.push(e)}addBufferByFloat32(e,t=1){const s=e.length,i=this.audioContext.sampleRate,a=this.audioContext.createBuffer(t,s,i),n=e.slice();for(let e=0;e<t;e++)a.copyToChannel(n,e);this.addBuffer(a)}addBufferByInt16(e,t=1){this.addBufferByFloat32(T(e),t)}startSpeech(){null===this.currentNode&&this.continue()}pauseSpeech(){this.audioContext.suspend().catch(e=>{D.error("pause speech fail",e)})}resumeSpeech(){this.audioContext.resume().catch(e=>{D.error("resume speech fail",e)})}nextSpeech(){this.bufferQueue.length>0&&(this.clearNode(),this.continue())}stopSpeech(){this.bufferQueue=[],this.clearNode()}clearNode(){this.currentNode&&(this.currentNode.onended=function(){},this.currentNode.stop(),this.currentNode.disconnect(),this.currentNode=null)}continue(){const e=this.bufferQueue.shift();if(e){this.currentNode=this.audioContext.createBufferSource(),this.currentNode.buffer=e,this.currentNode.connect(this.gainNode);const t=this.continue.bind(this);this.currentNode.onended=t,this.currentNode.start()}else this.clearNode()}}class I{stream;audioContext;type="vad";workletNode;source;isRecording=!1;outputSampleRate;chunkTimeInSeconds;pcmSub;get pcm(){return this.pcmSub.asObservable()}constructor(e,t,s){this.stream=e,this.audioContext=t,this.outputSampleRate=s?.llmSampleRate??16e3,this.chunkTimeInSeconds=s?.chunkTimeInSeconds??1,this.pcmSub=new u}async startRecord(){this.isRecording||(this.source=this.audioContext.createMediaStreamSource(this.stream),this.workletNode=new AudioWorkletNode(this.audioContext,"aiia-vad",{processorOptions:{outputSampleRate:this.outputSampleRate,chunkTimeInSeconds:this.chunkTimeInSeconds}}),this.source.connect(this.workletNode),this.workletNode.port.onmessage=e=>{const{float32:t}=e.data;t&&this.pcmSub.next(new Float32Array(t))},this.isRecording=!0)}stopRecord(){this.isRecording&&(this.workletNode&&(this.workletNode.port.onmessage=null,this.workletNode.disconnect()),this.source&&this.source.disconnect(),this.isRecording=!1)}}class F{type="camera";aiiaCamera;get detections(){return this.aiiaCamera.detections}get landmarks(){return this.aiiaCamera.landmarks}get videoSize(){return this.aiiaCamera.videoSize}constructor(e){const{perSecond:s}=t({perSecond:3},e);this.aiiaCamera=new W,this.aiiaCamera.setFrequency(s),document.body.append(this.aiiaCamera)}async init(e){try{const t=await _.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.0/wasm"),s=await y.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite",delegate:"GPU"},runningMode:"VIDEO"});this.aiiaCamera.setStream(e),this.aiiaCamera.setFaceDetector(s);const i=await S.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task",delegate:"GPU"},runningMode:"VIDEO",numPoses:5});this.aiiaCamera.setPoseLandmarker(i)}catch(e){D.error("Camera init fail",e)}}startCapture(){this.aiiaCamera.startCapture()}stopCapture(){this.aiiaCamera.stopCapture()}onDestroy(){this.aiiaCamera.remove()}}class W extends HTMLElement{static get observedAttributes(){return["debug"]}_shadow;_containerEl;_styleEl;_videoEl;_canvasEl;_canvasCtx;_isDebug;_debugElements;_interval;_lastTimestamp;_loopFrameID;_faceDetector;_detectionsSub;_poseLandmarker;_landmarksSub;_isReady;_drawingUtils;get detections(){return this._detectionsSub.asObservable()}get landmarks(){return this._landmarksSub.asObservable()}get videoSize(){return{width:this._videoEl.videoWidth,height:this._videoEl.videoHeight}}constructor(){super(),this._isDebug=!1,this._isReady=!1,this._shadow=this.attachShadow({mode:"open"}),this._containerEl=document.createElement("div"),this._videoEl=document.createElement("video"),this._styleEl=document.createElement("style"),this._canvasEl=document.createElement("canvas"),this._debugElements=[],this._loopFrameID=null,this._detectionsSub=new u,this._landmarksSub=new u,this._interval=Math.ceil(1e3/3),this._lastTimestamp=0,this.predictWebcam=this.predictWebcam.bind(this),this._settingContainer(),this._settingStyle(),this._settingVideo(),this._containerEl.appendChild(this._videoEl),this._shadow.appendChild(this._styleEl),this._shadow.appendChild(this._containerEl),this._containerEl.appendChild(this._canvasEl);const e=this._canvasEl.getContext("2d");e?(this._canvasCtx=e,this._drawingUtils=new f(this._canvasCtx)):(this._canvasCtx=null,this._drawingUtils=null)}connectedCallback(){this.startCapture()}disconnectedCallback(){this.stopCapture()}attributeChangedCallback(e,t,s){"debug"===e&&(this._isDebug=null!==s&&("true"===s||""===s),this._containerEl.style.visibility=this._isDebug?"visible":"hidden")}setStream(e){this._videoEl.onloadedmetadata=()=>{this._isReady=!0},this._videoEl.srcObject=e}setFaceDetector(e){this._faceDetector=e}setPoseLandmarker(e){this._poseLandmarker=e}setFrequency(e){this._interval=Math.ceil(1e3/e)}_settingStyle(){this._styleEl.textContent="\n .aiia_container {\n visibility: hidden;\n position: fixed;\n top: 0;\n left: 0;\n\n > canvas {\n position: absolute;\n top: 0;\n left: 0;\n width: 640px;\n height: 480px;\n }\n }\n .aiia_container .highlighter {\n background: rgba(0, 255, 0, 0.25);\n border: 1px dashed #fff;\n z-index: 1;\n position: absolute;\n }\n .aiia_container .confidence {\n position: absolute;\n padding-bottom: 5px;\n padding-top: 5px;\n background-color: #007f8b;\n color: #fff;\n border: 1px dashed rgba(255, 255, 255, 0.7);\n z-index: 2;\n font-size: 12px;\n margin: 0;\n }\n "}_settingContainer(){this._containerEl.classList.add("aiia_container")}_settingVideo(){this._videoEl.autoplay=!0,this._videoEl.playsInline=!0,this._videoEl.muted=!0}getVideo(){return this._videoEl}startCapture(){null===this._loopFrameID&&(this._videoEl.play(),this._loopFrameID=window.requestAnimationFrame(this.predictWebcam))}stopCapture(){null!==this._loopFrameID&&(window.cancelAnimationFrame(this._loopFrameID),this._loopFrameID=null),this._videoEl.pause()}async predictWebcam(){if(this._loopFrameID=window.requestAnimationFrame(this.predictWebcam),void 0===this._faceDetector||!this._isReady)return;if(void 0===this._poseLandmarker||!this._isReady)return;const e=performance.now();if(this._lastTimestamp+this._interval<=e){this._lastTimestamp=e;const t=this._faceDetector.detectForVideo(this._videoEl,e).detections,s=this._poseLandmarker.detectForVideo(this._videoEl,e).landmarks;this._isDebug&&(this._drawRect(t),this._drawRect2(s)),this._detectionsSub.next(t),this._landmarksSub.next(s)}}_drawRect(e){s(e,(e,t)=>{const{categories:s,boundingBox:i}=e;if(i){let e=this._debugElements[2*t],a=this._debugElements[2*t+1];e&&a||(e=document.createElement("div"),e.classList.add("highlighter"),a=document.createElement("p"),a.classList.add("confidence"),this._containerEl.append(a,e),this._debugElements.push(e,a)),e.style.display="block",a.style.display="block";const n=100*s[0].score;a.innerText=`Confidence: ${Math.round(n)} %`,a.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",a.style.top=i.originY-30+"px",a.style.width=i.width-10+"px",e.style.right=this._videoEl.offsetWidth-i.width-i.originX+"px",e.style.top=`${i.originY}px`,e.style.width=i.width-10+"px",e.style.height=`${i.height}px`}});for(let t=2*e.length;t<this._debugElements.length;t++)this._debugElements[t].style.display="none"}landmarkColors=["gray","lightgray","gray","gray","lightgray"];connectorColors=["green","blue","red","yellow","purple"];_drawRect2(e){this._canvasCtx&&this._drawingUtils&&(this._canvasCtx.save(),this._canvasCtx.clearRect(0,0,640,480),s(e,(e,t)=>{this._canvasCtx?.save(),this._drawingUtils?.drawLandmarks(e,{radius:e=>f.lerp(e.from.z,-.15,.1,5,1),color:this.landmarkColors[t]}),this._drawingUtils?.drawConnectors(e,S.POSE_CONNECTIONS,{color:this.connectorColors[t],lineWidth:2})}),this._canvasCtx.restore())}destroy(){this._detectionsSub.complete(),this.remove()}}customElements.define("aiia-camera",W),function(e){e[e.padding=0]="padding",e[e.rejected=1]="rejected",e[e.loadfail=2]="loadfail",e[e.allowed=3]="allowed"}(N||(N={}));class P{stream;_state;stateSub;audioCtx;audioManager;vadManager;camManager;destroy;pcmSub;detectionsSub;landmarksSub;get sampleRate(){return this.audioCtx?this.audioCtx.sampleRate:16e3}get pcm(){return this.pcmSub.asObservable()}get detections(){return this.detectionsSub.asObservable()}get landmarks(){return this.landmarksSub.asObservable()}get state(){return this._state}set state(e){e!==this._state&&(this._state=e,this.stateSub.next(e))}get stateObs(){return this.stateSub.asObservable()}constructor(e){this.stream=e,this.stateSub=new d(1),this.pcmSub=new u,this.destroy=new u,this.detectionsSub=new u,this.landmarksSub=new u,this.state=N.padding}async init(e,t){const s=this.stream??await async function(e){try{return await navigator.mediaDevices.getUserMedia(e)}catch(e){return null}}({audio:{echoCancellation:!0,noiseSuppression:!0},video:!0});if(null===s)throw this.state=N.rejected,new Error(w["未獲得媒體裝置權限"]);this.stream=s,this.audioCtx=new AudioContext;try{await this.audioCtx.audioWorklet.addModule(e)}catch(e){throw D.fatal(e),this.state=N.loadfail,new Error(w["Worklet模組載入失敗"])}this.state=N.allowed,this.audioManager=new j(this.audioCtx),this.vadManager=new I(s,this.audioCtx,t),this.vadManager.pcm.pipe(h(this.destroy)).subscribe(e=>{this.pcmSub.next(e)});const i=new F(t?.cameraDetection);i.detections.pipe(h(this.destroy)).subscribe(e=>{const{width:t,height:s}=i.videoSize;this.detectionsSub.next({detections:e,clientWidth:t,clientHeight:s})}),i.landmarks.pipe(h(this.destroy)).subscribe(e=>{this.landmarksSub.next(e)}),i.init(s),this.camManager=i}addAudioQueue(t){const s=e(t,"buffer",void 0),i=e(t,"float32",void 0),a=e(t,"int16",void 0),n=e(t,"numberOfChannels",void 0);s?this.audioManager?.addBuffer(s):i?this.audioManager?.addBufferByFloat32(i,n):a&&this.audioManager?.addBufferByInt16(a,n)}playAudio(){this.audioManager?.startSpeech()}stopAudio(){this.audioManager?.stopSpeech()}startRecord(){this.vadManager?.startRecord()}stopRecord(){this.vadManager?.stopRecord()}setVolume(e){this.audioManager&&(this.audioManager.volume=e)}getVolume(){return this.audioManager?this.audioManager.volume:0}startCapture(){this.camManager?.startCapture()}stopCapture(){this.camManager?.stopCapture()}onDestroy(){this.camManager?.onDestroy(),this.audioCtx?.close(),this.destroy.next(),this.destroy.complete()}}class B{socket;messageSub;destorySub;get message(){return this.messageSub.asObservable()}constructor(e){this.destorySub=new u,this.messageSub=new u;const{specific:s,id:i}=e.project;this.socket=C(e.ws_url,t({autoConnect:!1},s&&void 0!==i?{query:{uuid:i}}:{}))}start(){const e="list",t="cloud",s="channel";l(this.socket,"connect").pipe(h(this.destorySub)).subscribe(()=>{D.debug("Client ID: ",this.socket.id)}),l(this.socket,e).pipe(p(1),h(this.destorySub)).subscribe(t=>{this.messageSub.next({event:e,data:t})}),l(this.socket,t).pipe(h(this.destorySub)).subscribe(e=>{this.messageSub.next({event:t,data:e})}),l(this.socket,s).pipe(h(this.destorySub)).subscribe(e=>{this.messageSub.next({event:s,data:e})}),this.socket.connect()}send(e,t){this.socket.emit(e,t)}sendToCloud(e){this.send("cloud",e)}onDestroy(){this.destorySub.next(),this.destorySub.complete(),this.messageSub.complete(),this.socket.disconnect()}}class V{config;media;chat;destroySub;projectsSub;layoutSub;aiiaSubtitleSub;userSubtitleSub;sampleRateOfWhisper;channelState;stateSub;_currentState;isMuteAiia;aiiaSubtitleClearSub;userSubtitleClearSub;_lastProjects;guestSub;_hasGuest;mediaEventReady;isStart;get state(){return this.stateSub.asObservable()}get currentState(){return this._currentState}get layout(){return this.layoutSub.asObservable()}get projects(){return this.projectsSub.asObservable()}get lastProjects(){return n(this._lastProjects,e=>({...e}))}get aiiaSubtitle(){return this.aiiaSubtitleSub.asObservable()}get userSubtitle(){return this.userSubtitleSub.asObservable()}get guest(){return this.guestSub.asObservable()}get hasGuest(){return this._hasGuest}set volume(e){this.media.setVolume(e)}get volume(){return this.media.getVolume()}constructor(e,t){this.config=e,this.media=t,this.layoutSub=new u,this.projectsSub=new d(1),this.destroySub=new u,this.aiiaSubtitleSub=new u,this.userSubtitleSub=new u,this.aiiaSubtitleClearSub=new u,this.userSubtitleClearSub=new u,this.stateSub=new b("NotStart"),this.sampleRateOfWhisper={input:e.llmSampleRate,output:16e3},this.channelState={proxy:!1,cloud:!1},this.isMuteAiia=!1,this._lastProjects=[],this.guestSub=new d(1),this._hasGuest=!1,this.mediaEventReady=!1,this.isStart=!1,this.chat=new B(e),D.code(R["指定依賴已完成"],"chat"),this.close=this.onDestroy,this.behavior=this.behavior.bind(this);const{userDelayTime:s,aiiaDelayTime:i}=e.autoClearSubtitle;isNaN(i)||this.aiiaSubtitleClearSub.pipe(m(i),h(this.destroySub)).subscribe(()=>{this.aiiaSubtitleSub.next("")}),isNaN(s)||this.userSubtitleClearSub.pipe(m(s),h(this.destroySub)).subscribe(()=>{this.userSubtitleSub.next("")}),this.stateSub.pipe(h(this.destroySub)).subscribe(e=>{this._currentState=e}),this.projectsSub.pipe(h(this.destroySub)).subscribe(e=>{this._lastProjects=e}),this.guestSub.pipe(h(this.destroySub)).subscribe(e=>{this._hasGuest=e}),t.stateObs.pipe(h(this.destroySub)).subscribe(e=>{N.allowed===e&&this.channelCheck()})}start(){this.isStart?D.code(R["已啟動SDK"]):(this.isStart=!0,this.chat.message.pipe(h(this.destroySub)).subscribe(({event:e,data:t})=>{switch(e){case"cloud":n(t,this.behavior);break;case"list":{const{id:e,name:s,specific:i}=this.config.project;let a=null;if(i&&void 0!==e)a=e;else{const i=r(t,t=>t.name===s||t.id===e);i&&(a=i.id)}null!==a?this.chooseProject(a):(this.projectsSub.next(t),this.stateSub.next("WaitingProjectID"),D.code(R["請訂閱專案列表"]));break}case"channel":switch(this.channelState.proxy=!0,t){case"open":this.channelCheck();break;case"close":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("CloseService");break;case"no_permissions":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("NoPermissions");break;case"reconnect":this.channelState.cloud=!1,this.media.stopRecord(),this.media.stopCapture(),this.stateSub.next("ReconnectingCloud")}}}),this.chat.start(),this.stateSub.next("ConnectingCloud"),D.code(R["啟動SDK"]))}close;skip(){this.chat.sendToCloud({request:"skip"})}reset(){this.chat.sendToCloud({request:"reset"})}chooseProject(e){this.chat.send("uuid",e),this.stateSub.next("ConnectingCloud")}mute(){this.isMuteAiia=!0,this.media.stopAudio(),this.media.stopRecord()}unmute(){this.isMuteAiia=!1,this.media.startRecord()}behavior(t){switch(t.signal){case"layout":this.layoutSub.next(t.content);break;case"audio":if("sampleRate"===t.command)this.sampleRateOfWhisper.output=t.content;else switch(t.command){case"pcm":if(!this.isMuteAiia){const e=function(e,t,s){if(t===s)return e;const i=s/t,a=Math.ceil(e.length*i),n=new Float32Array(a);for(let t=0;t<a;t++){const s=t/i,a=Math.floor(s),o=s-a,r=e[a],c=e[Math.min(a+1,e.length-1)];n[t]=r+(c-r)*o}return n}(T(new Int16Array(t.content)),this.sampleRateOfWhisper.output,this.media.sampleRate);this.media.addAudioQueue({float32:e}),this.media.playAudio()}break;case"interrupted":this.media.stopAudio(),this.aiiaSubtitleSub.next("")}break;case"status":"Connected"===e(t,"content")&&(this.channelState.cloud=!0,this.channelCheck(),this.layoutSub.next({type:"resetLayout"}));break;case"agent":if(!this.isMuteAiia){const s=e(t,"content.function_result.response");void 0!==s&&(this.aiiaSubtitleSub.next(s),this.aiiaSubtitleClearSub.next())}break;case"asr":if(!this.isMuteAiia&&"ASR"===e(t,"content.function_type")){const s=e(t,"content.function_result.result");void 0!==s&&(this.userSubtitleSub.next(s),this.userSubtitleClearSub.next())}break;case"cv":{const s=e(t,"content.status");"user present"===s?this.guestSub.next(!0):"user leave"===s?this.guestSub.next(!1):D.warn("Unexpect value",e(t,"content"));break}default:D.debug(t.signal)}}channelCheck(){const{proxy:e,cloud:t}=this.channelState;if(e&&t&&this.media.state===N.allowed){if(this.chat.sendToCloud({request:"audio",command:"sampleRate",content:this.sampleRateOfWhisper.input}),!this.mediaEventReady){this.mediaEventReady=!0,this.media.pcm.pipe(h(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"audio",command:"pcm",content:A(e)})});const e=(s=this.config.detection.confidence,function(e,t){const{width:r,height:c}=t,u=i(e,e=>100*(e.categories[0]?.score??0)>=s),d=a(n(u,e=>{if(void 0!==e.boundingBox){const t=e.boundingBox,s=t.width*t.height,i=e.categories[0]?.score??0;return{...t,area:s,score:100*i,clientWidth:r,clientHeight:c}}return null}));return o(d,["area","score"])[0]??null});this.media.detections.pipe(h(this.destroySub),g(({detections:t,clientHeight:s,clientWidth:i})=>e(t,{width:i,height:s}))).subscribe(e=>{this.chat.send("face",e)}),this.media.landmarks.pipe(h(this.destroySub)).subscribe(e=>{this.chat.sendToCloud({request:"pose_detect",content:e})})}this.media.startRecord(),this.media.startCapture(),this.stateSub.next("InService"),D.code(R["通道、雲端服務、麥克風都已就緒"])}else e&&t&&this.media.state!==N.allowed&&(this.stateSub.next("InServiceNoMedia"),D.code(R["未取得媒體裝置權限,請重新設定或忽略此訊息"]));var s}onDestroy(){this.chat.onDestroy(),this.media.onDestroy(),this.destroySub.next(),this.destroySub.complete(),this.stateSub.next("Destroy"),this.stateSub.complete(),D.code(R["結束SDK"])}}function L(s){if(v){!function(e){const{info:t,error:s,warn:i,debug:a,fatal:n,code:o}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);o||(E.code=()=>{}),a||(E.debug=()=>{}),t||(E.info=()=>{}),i||(E.warn=()=>{}),s||(E.error=()=>{}),n||(E.fatal=()=>{})}(e(s,"debug",!0)),D.code(R["初始化必要依賴"]);const i=new x(t({},s));D.code(R["指定依賴已完成"],"config");const a=new P(i.mediaStream);D.code(R["指定依賴已完成"],"config");const n=new V(i,a);return D.code(R["指定依賴已完成"],"sdk"),""!==i.worklet_url&&a.init(i.worklet_url,i),n}throw new Error(w["環境錯誤(browser)"])}export{L as initSdk};
|
package/dist/node.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e,t=require("lodash-es"),s=require("moment"),o=require("mathjs"),i=require("socket.io"),r=require("rxjs"),n=require("axios"),c=require("ws"),a=require("fs"),h=require("os"),u=require("path"),l=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(){return this.environment,{api:"https://aiia-content-management-dev-21193779403.asia-east1.run.app",socket:"wss://graphen-agentic-workflow-dev-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(){const{user:e,aiia:s}=t.assign({},this.config.autoClearSubtitle);let o=5e3,i=5e3;return"number"==typeof e?o=e>=0?e:o:!1===e&&(o=NaN),"number"==typeof s?i=s>=0?s:i:!1===s&&(i=NaN),{userDelayTime:o,aiiaDelayTime:i}}get eyeTrackEnable(){return this.config.eyeTrackEnable??!1}constructor(e){this.config=e}}var g;!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"}(g||(g={}));const p={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},E=[];function _(e){return E.push(e),_}const f=new Proxy(p,{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(E,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()}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}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(g["啟動服務"],"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,type:o,wasClean:i}=e;switch(t){case O.WHEN_BROWSER_CLOSE:f.code(g["無副作用的關閉連線"]);break;case O.MDN_NORMAL_CLOSURE:case O.MDN_GOING_AWAY:this.closeSub.next("close"),f.code(g["安全的關閉連線"]);break;case O.MDN_NO_STATUS_RECEIVED:case O.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),f.code(g["與雲端的服務中斷"],{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(g["沒有授權,請聯繫Graphen"]);break;case O.WS_5000_NORAML_CLOSURE:case O.WS_5003_TOKEN_EXPIRED:this.closeSub.next("reconnect"),f.code(g["自動重新連線連端服務"]);break;default:this.closeSub.next("close"),f.code(g["未預期的斷線,請聯繫Graphen"],{code:t,reason:s,type:o,wasClean:i})}})}reconnect(e){this.onDestroy(),this.destorySub=new r.Subject,this.start(e)}flushMessageQueue(){for(;this.messageQueue.length>0&&this.isConnected&&void 0!==this.socket;){const e=this.messageQueue.shift();e&&this.socket.send(e,e=>{e&&f.error("send message fail",e?.message,e)})}}send(e){this.isConnected&&void 0!==this.socket?this.socket.send(e,e=>{e&&f.error("send message fail",e?.message,e)}):this.messageQueue.push(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=u.join(this.homedir,"Library","Application Support");break;case"win32":this.isMAC=!1,this.isWIN=!0,this.isLINUX=!1,this.saveFolder=process.env.APPDATA||u.join(this.homedir,"AppData","Roaming");break;case"linux":this.isMAC=!1,this.isWIN=!1,this.isLINUX=!0,this.saveFolder=process.env.XDG_CONFIG_HOME||u.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=u.join(this.saveFolder,this.AppName),this.folderCheck(this.appFolder)}addPath(...e){return u.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 l.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(g["啟動服務"],"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(g["無副作用的關閉連線"]);break;case O.WS_5000_NORAML_CLOSURE:case O.MDN_NORMAL_CLOSURE:case O.MDN_GOING_AWAY:this.closeSub.next("close"),f.code(g["安全的關閉連線"]);break;case O.MDN_NO_STATUS_RECEIVED:case O.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),f.code(g["與雲端的服務中斷"],{code:t,reason:s});break;case O.WS_5001_UNAUTHORIZED:case O.WS_5002_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),f.code(g["沒有授權,請聯繫Graphen"]);break;case O.WS_5003_TOKEN_EXPIRED:this.closeSub.next("reconnect"),f.code(g["自動重新連線連端服務"]);break;default:this.closeSub.next("close"),f.code(g["未預期的斷線,請聯繫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(g["啟動服務"],"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(g["無副作用的關閉連線"]);break;case O.WS_5000_NORAML_CLOSURE:case O.MDN_NORMAL_CLOSURE:case O.MDN_GOING_AWAY:this.closeSub.next("close"),f.code(g["安全的關閉連線"]);break;case O.MDN_NO_STATUS_RECEIVED:case O.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),f.code(g["與雲端的服務中斷"],{code:t,reason:s});break;case O.WS_5001_UNAUTHORIZED:case O.WS_5002_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),f.code(g["沒有授權,請聯繫Graphen"]);break;case O.WS_5003_TOKEN_EXPIRED:this.closeSub.next("reconnect"),f.code(g["自動重新連線連端服務"]);break;default:this.closeSub.next("close"),f.code(g["未預期的斷線,請聯繫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 M{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(g["指定模組初始化完成"],"api"),this.chat=new y(this.config),f.code(g["指定模組初始化完成"],"chat"),this.udpProxy=new R(this.config),f.code(g["指定模組初始化完成"],"udp"),this.audioProxy=new C,f.code(g["指定模組初始化完成"],"audio"),this.audioIntrProxy=new A,f.code(g["指定模組初始化完成"],"audio intr"),e.eyeTrackEnable&&(this.eyeBallUdp=new l.Client("127.0.0.1",6745),f.code(g["指定模組初始化完成"],"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(g["啟動服務"],"chat"),this.udpProxy.start(),f.code(g["啟動服務"],"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)}),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:u,clientWidth:l,originX:d,originY:S,width:b,height:g}=e,p=o.matrix([[a/l,0,n],[0,h/u,c],[0,0,1]]),E=d+b/2,_=u-(S+g/2),f=E>l?l:E<0?0:E,y=_>u?u:_<0?0:_,m=o.matrix([[f],[y],[1]]),O=o.multiply(p,m);return{x:O.get([0,0]),y:O.get([1,0])}}(s);this.eyeBallUdp.send(["/",e,i],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(g["服務結束"])}}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||(p.code=()=>{}),i||(p.debug=()=>{}),t||(p.info=()=>{}),o||(p.warn=()=>{}),s||(p.error=()=>{}),r||(p.fatal=()=>{}),_}(t.get(s,"debug",!0))(new w),f.code(g["開始初始化必要模組"]);const o=new b(t.assign({},s));f.code(g["指定模組初始化完成"],"config");const i=new k;f.code(g["指定模組初始化完成"],"storage");const r=new M(o,i);return f.code(g["指定模組初始化完成"],"core"),r.start(),f.code(g["啟動服務"]),()=>{r.onDestroy()}}throw new Error(e["環境錯誤(nodejs)"])};
|
|
1
|
+
"use strict";var e=require("lodash-es"),t=require("moment"),s=require("mathjs"),r=require("socket.io"),i=require("rxjs"),n=require("axios"),o=require("ws"),a=require("fs"),c=require("os"),h=require("path");require("child_process");var l,u=require("node-osc");!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(l||(l={}));const d="access_token",p="undefined"!=typeof process&&null!=process.versions&&null!=process.versions.node;class S{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e.get(t,"id",void 0),r=e.get(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof r?r:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return e.assign({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return e.assign({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e.get(this.config,t,void 0)}}var f;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(f||(f={}));const _={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},g=[];function b(e){return g.push(e),b}const E=new Proxy(_,{get(s,r,i){if("string"==typeof r)switch(r){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...i)=>{const n=t().format("yyyy-MM-DD[T]HH:mm:ss:sss");e.forEach(g,e=>{e.next(r,n,...i)}),s[r](`[Aiia::${r}_${n}_]`,...i)};default:return}},set:(e,t,s,r)=>!0});class y{config;server;memberSub;get port(){return this.config.port}get webserver(){return this.config.webserver}get memberChange(){return this.memberSub.asObservable()}constructor(e){this.config=e,this.memberSub=new i.Subject}start(){if(this.server=new r.Server(this.webserver,{cors:{origin:"*"}}),this.server.on("connection",e=>{E.debug("client on",e.id),this.memberSub.next({type:"in",socket:e}),e.on("disconnect",t=>{E.debug("client off",e.id,"reason",t),this.memberSub.next({type:"out",socket:e})})}),void 0===this.webserver){const e=this.port;this.server.listen(e),E.info("Websocket server run on",e)}else E.info("Websocket server rely your webserver.")}onDestroy(){this.server&&this.server.close()}}class m{instance;controller;constructor(e){this.controller=new AbortController,this.instance=n.create({baseURL:e,signal:this.controller.signal})}async getAccessToken(t,s){const r={license_number:t,token_value:s??""},i=await this.instance.post("/api/v1/license/agent_token",r).then(t=>e.get(t.data,"token_value","")).catch(()=>null);if(null===i)throw new Error(l["網絡錯誤"]);if(""===i)throw new Error(l["獲取權杖失敗"]);return i}async getProjectList(t){const s=await this.instance.post("/api/v1/license/projects",{license_number:t}).then(t=>e.map(e.get(t,"data.projects",[]),({id:e,sub_project_name:t,avatar:s})=>({avatar:s,id:e,name:t}))).catch(e=>null);if(null===s)throw new Error(l["網絡錯誤"]);if(0===s.length)throw new Error(l["沒有可用專案"]);return s}onDestroy(){this.controller.abort()}}var O;!function(e){e[e.MDN_NORMAL_CLOSURE=1e3]="MDN_NORMAL_CLOSURE",e[e.MDN_GOING_AWAY=1001]="MDN_GOING_AWAY",e[e.MDN_PROTOCOL_ERROR=1002]="MDN_PROTOCOL_ERROR",e[e.MDN_UNSUPPORTED_DATA=1003]="MDN_UNSUPPORTED_DATA",e[e.MDN_RESERVED_1004=1004]="MDN_RESERVED_1004",e[e.MDN_NO_STATUS_RECEIVED=1005]="MDN_NO_STATUS_RECEIVED",e[e.MDN_ABNORMAL_CLOSURE=1006]="MDN_ABNORMAL_CLOSURE",e[e.MDN_INVALID_FRAME_PAYLOAD=1007]="MDN_INVALID_FRAME_PAYLOAD",e[e.MDN_POLICY_VIOLATION=1008]="MDN_POLICY_VIOLATION",e[e.MDN_MESSAGE_TOO_BIG=1009]="MDN_MESSAGE_TOO_BIG",e[e.MDN_MANDATORY_EXTENSION=1010]="MDN_MANDATORY_EXTENSION",e[e.MDN_INTERNAL_ERROR=1011]="MDN_INTERNAL_ERROR",e[e.MDN_SERVICE_RESTART=1012]="MDN_SERVICE_RESTART",e[e.MDN_TRY_AGAIN_LATER=1013]="MDN_TRY_AGAIN_LATER",e[e.MDN_BAD_GATEWAY=1014]="MDN_BAD_GATEWAY",e[e.MDN_TLS_HANDSHAKE=1015]="MDN_TLS_HANDSHAKE",e[e.WHEN_BROWSER_CLOSE=4001]="WHEN_BROWSER_CLOSE",e[e.WS_4100_NORAML_CLOSURE=4100]="WS_4100_NORAML_CLOSURE",e[e.WS_4101_UNAUTHORIZED=4101]="WS_4101_UNAUTHORIZED",e[e.WS_4102_LICENSE_EXPIRED=4102]="WS_4102_LICENSE_EXPIRED",e[e.WS_4103_TOKEN_EXPIRED=4103]="WS_4103_TOKEN_EXPIRED",e[e.WS_4104_LLM_SESSION_ERROR=4104]="WS_4104_LLM_SESSION_ERROR",e[e.WS_4105_TOO_MANY_CONNECTIONS=4105]="WS_4105_TOO_MANY_CONNECTIONS"}(O||(O={}));class D{id;uuid;socket;messageSub;linkStartSub;destorySub;closeSub;isFinished;debugInfo;get message(){return this.messageSub.asObservable()}get close(){return this.closeSub.asObservable()}get linkStart(){return this.linkStartSub.asObservable()}get isConnected(){return this.socket?.readyState===o.OPEN}constructor(e){this.id=e,this.messageSub=new i.Subject,this.linkStartSub=new i.Subject,this.closeSub=new i.Subject,this.destorySub=new i.Subject,this.isFinished=!1,this.uuid=crypto.randomUUID(),this.debugInfo=`browser(${this.id}), proxy(${this.uuid})`,E.info(`create proxy for browser(${this.id})`)}start(e){this.socket=new o(e,{autoPong:!0}),i.fromEvent(this.socket,"open").pipe(i.takeUntil(this.destorySub)).subscribe(()=>{E.code(f["啟動服務"],"proxy",this.debugInfo),this.linkStartSub.next()}),i.fromEvent(this.socket,"message").pipe(i.takeUntil(this.destorySub)).subscribe(e=>{const{data:t}=e;this.messageSub.next(t)}),i.fromEvent(this.socket,"error").pipe(i.takeUntil(this.destorySub)).subscribe(e=>{const{error:t}=e;E.error(this.debugInfo,"Proxy has some problem",t)}),i.fromEvent(this.socket,"close").pipe(i.takeUntil(this.destorySub)).subscribe(e=>{const{code:t,reason:s,type:r,wasClean:i}=e;switch(t){case O.WHEN_BROWSER_CLOSE:E.code(f["無副作用的關閉連線"],this.debugInfo),this.onDestroy();break;case O.MDN_NORMAL_CLOSURE:case O.MDN_GOING_AWAY:this.closeSub.next("close"),E.code(f["安全的關閉連線"],this.debugInfo);break;case O.MDN_NO_STATUS_RECEIVED:case O.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),E.code(f["與雲端的服務中斷"],this.debugInfo,{code:t,reason:s,type:r,wasClean:i});break;case O.WS_4100_NORAML_CLOSURE:case O.WS_4103_TOKEN_EXPIRED:case O.WS_4104_LLM_SESSION_ERROR:case O.WS_4105_TOO_MANY_CONNECTIONS:this.closeSub.next("reconnect"),E.code(f["自動重新連線雲端服務"],this.debugInfo,`socket code: ${t}`);break;case O.WS_4101_UNAUTHORIZED:case O.WS_4102_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),E.code(f["沒有授權,請聯繫Graphen"],this.debugInfo,`socket code: ${t}`);break;default:this.closeSub.next("close"),E.code(f["未預期的斷線,請聯繫Graphen"],this.debugInfo,{code:t,reason:s,type:r,wasClean:i})}})}reconnect(e){this.isFinished||(this.destorySub.next(),this.destorySub.complete(),this.destorySub=new i.Subject,this.start(e))}send(e){const s=`currentTime: ${t().format("yyyy-MM-DD[T]HH:mm:ss:sss")}`;this.isConnected?this.socket.send(e,t=>{t&&E.error("send message fail",this.debugInfo,s,e.slice(0,100),t)}):E.warn("The socket is not connected",this.debugInfo,s,e.slice(0,100))}sendToCloud(e){this.send(JSON.stringify(e))}onClose(){if(this.socket)switch(this.socket.readyState){case 0:E.info("Once the cloud connection is complete, the connection will be disconnected."),this.linkStart.pipe(i.takeUntil(this.destorySub),i.take(1)).subscribe(()=>{this.socket?.close(O.WHEN_BROWSER_CLOSE)});break;case 1:E.info("Close safely, no side effects"),this.socket.close(O.WHEN_BROWSER_CLOSE);break;case 2:case 3:E.info("The connection have been lost.")}}onDestroy(){this.isFinished=!0,this.destorySub.next(),this.destorySub.complete(),E.info("Destroy proxy",this.debugInfo)}}const R=new class{AppName;platform;isMAC;isWIN;isLINUX;saveFolder;homedir;appFolder;EOL;constructor(e){switch(this.AppName=e,this.platform=process.platform,this.homedir=c.homedir(),this.EOL=c.EOL,this.platform){case"darwin":this.isMAC=!0,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=h.join(this.homedir,"Library","Application Support");break;case"win32":this.isMAC=!1,this.isWIN=!0,this.isLINUX=!1,this.saveFolder=process.env.APPDATA||h.join(this.homedir,"AppData","Roaming");break;case"linux":this.isMAC=!1,this.isWIN=!1,this.isLINUX=!0,this.saveFolder=process.env.XDG_CONFIG_HOME||h.join(this.homedir,".config");break;default:console.log(`當前系統是未知的平台: ${this.platform}`),this.isMAC=!1,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=this.homedir}this.appFolder=h.join(this.saveFolder,this.AppName),this.folderCheck(this.appFolder)}addPath(...e){return h.join(...e)}folderCheck(e){a.mkdirSync(e,{recursive:!0})}fileCheck(e){a.existsSync(e)||a.writeFileSync(e,"",{encoding:"utf8"})}}("aiia-sdk");class k{type="node";filePath;fileName;constructor(){this.fileName="storage.json",this.filePath=R.addPath(R.appFolder,this.fileName),R.fileCheck(this.filePath)}getFileDataSync(){const e=a.readFileSync(this.filePath,"utf8");try{return JSON.parse(e)}catch(e){return{}}}setFileDataSync(e){try{const t=JSON.stringify(e);a.writeFileSync(this.filePath,t,"utf8")}catch(e){}}async getFileData(){return new Promise(e=>{a.readFile(this.filePath,"utf8",(t,s)=>{if(t)e({});else try{e(JSON.parse(s))}catch(t){e({})}})})}async setFileData(e){return new Promise(t=>{try{const s=JSON.stringify(e);a.writeFile(this.filePath,s,"utf8",e=>{t()})}catch(e){}})}async getItem(e){return(await this.getFileData())[e]??null}async setItem(e,t){const s=await this.getFileData();s[e]=t,await this.setFileData(s)}async removeItem(t){const s=await this.getFileData(),r=e.omit(s,t);await this.setFileData(r)}async clear(){await this.setFileData({})}getItemSync(e){return this.getFileDataSync()[e]??null}setItemSync(e,t){const s=this.getFileDataSync();s[e]=t,this.setFileDataSync(s)}removeItemSync(t){const s=e.omit(this.getFileDataSync(),t);this.setFileDataSync(s)}clearSync(){this.setFileDataSync({})}}const N={[f["通道、雲端服務、麥克風都已就緒"]]:"通道、雲端服務、麥克風都已就緒",[f["未取得媒體裝置權限,請重新設定或忽略此訊息"]]:"未取得媒體裝置權限,請重新設定或忽略此訊息",[f["請訂閱專案列表"]]:"請訂閱專案列表",[f["初始化必要依賴"]]:"初始化必要依賴",[f["指定依賴已完成"]]:"指定依賴已完成",[f["啟動SDK"]]:"啟動SDK",[f["結束SDK"]]:"結束SDK",[f["已啟動SDK"]]:"已啟動SDK",[f["安全的關閉連線"]]:"安全的關閉連線",[f["無副作用的關閉連線"]]:"無副作用的關閉連線",[f["與雲端的服務中斷"]]:"與雲端的服務中斷",[f["沒有授權,請聯繫Graphen"]]:"沒有授權,請聯繫Graphen",[f["自動重新連線雲端服務"]]:"自動重新連線雲端服務",[f["未預期的斷線,請聯繫Graphen"]]:"未預期的斷線,請聯繫Graphen",[f["開始初始化必要模組"]]:"開始初始化必要模組",[f["指定模組初始化完成"]]:"指定模組初始化完成",[f["啟動服務"]]:"啟動服務",[f["服務結束"]]:"服務結束",[l["網絡錯誤"]]:"網絡錯誤",[l["環境錯誤(browser)"]]:"環境錯誤",[l["Worklet模組載入失敗"]]:"Worklet模組載入失敗",[l["未獲得媒體裝置權限"]]:"未獲得媒體裝置權限",[l["實例已摧毀"]]:"實例已摧毀",[l["環境錯誤(nodejs)"]]:"nodejs",[l["未提供憑證"]]:"未提供憑證",[l["獲取權杖失敗"]]:"獲取權杖失敗",[l["沒有可用專案"]]:"沒有可用專案",[l["重複初始化"]]:"重複初始化"},w=new RegExp(/^(ERROR )?CODE: [0-9]{3,4}$/);class C{next(e,s,...r){const i=`log_${t().format("yyyy-MM-DD")}.txt`,n=R.addPath(R.appFolder,i),o="code"===e?e=>w.test(e)?`${e}(${N[e]})`:e:e=>e;try{const t=r.reduce((e,t)=>{if(t instanceof Error){let s=`Error[${t.message??"No Message"}]${R.EOL}`;return s+=t.stack??"unknow stack","string"==typeof t.cause&&(s+=`${R.EOL}cause: ${t.cause}`),`${e}${R.EOL}${s}${R.EOL}`}switch(typeof t){case"string":return`${e} ${o(t)}`;case"number":case"bigint":return`${e} ${t.toString()}`;case"boolean":return`${e} ${t?"true":"false"}`;case"object":try{return`${e} ${JSON.stringify(t)}`}catch(t){return e}case"symbol":case"undefined":case"function":return`${e} [${typeof t}]`}},"");a.appendFileSync(n,`LOG(${e})__${s}__::${t}${R.EOL}`,{encoding:"utf8"})}catch(e){}}}class M{clientMap;eyeBallUdp;x_axis_px;y_axis_px;constructor(){this.clientMap=new Map,this.x_axis_px=0,this.y_axis_px=0}enableEyeTrack(e){this.eyeBallUdp=new u.Client("127.0.0.1",6745),this.x_axis_px=e.x_axis_px,this.y_axis_px=e.y_axis_px}eyeTrack(t){if(void 0!==this.eyeBallUdp){const r=null!==t,{x:i,y:n}=(()=>{if(r){const{x:r,y:i}=function(t,r){const{maxX:i,maxY:n,minX:o,minY:a}=e.assign({maxX:25,minX:-25,maxY:50,minY:-50},r),c=s.abs(i)+s.abs(o),h=s.abs(n)+s.abs(a),{clientHeight:l,clientWidth:u,originX:d,originY:p,width:S,height:f}=t,_=s.matrix([[c/u,0,o],[0,h/l,a],[0,0,1]]),g=d+S/2,b=l-(p+f/2),E=s.matrix([[g],[b],[1]]),y=s.multiply(_,E);return{x:y.get([0,0]),y:y.get([1,0])}}(t);return{x:this.toFloat(r+this.x_axis_px),y:this.toFloat(i+this.y_axis_px)}}return{x:this.toFloat(7),y:this.toFloat(20)}})();this.eyeBallUdp.send(["/",i,n],e=>{null!=e&&E.error("Eye track error:",e)})}}movement(t){e.map(t,({content:e})=>{const{port:t,host:s,message:r}=e;this.getClient(t,s).send(r,e=>{null!=e&&E.error("Occur error when send message to UE",e)})})}getClient(e,t){if(this.clientMap.has(e))return this.clientMap.get(e);{const s=new u.Client(t,e);return this.clientMap.set(e,s),s}}toFloat(t){return e.isInteger(t)?t+1e-5:t}onDestroy(){e.forEach(Array.from(this.clientMap),([e,t])=>{t.close()}),E.info("Close all UnrealEngine's UDP")}}class A{speechWS;interruptWS;get speechIsConnected(){return this.speechWS?.readyState===o.OPEN}get intrIsConnected(){return this.interruptWS?.readyState===o.OPEN}connectSocket(){this.speechWS=new o("ws://localhost:8080"),this.interruptWS=new o("ws://localhost:8081"),this.speechWS.onerror=e=>{E.error(`speech tool fail message: ${e.message}`,e.error)},this.interruptWS.onerror=e=>{E.error(`interrupt tool fail message: ${e.message}`,e.error)}}interrupted(){this.intrIsConnected&&this.interruptWS.send("interrupted")}speech(e){if(this.speechIsConnected){const t=Buffer.allocUnsafe(2*e.length);for(let s=0;s<e.length;s++)t.writeInt16LE(e[s],2*s);this.speechWS.send(t)}}onDestroy(){this.speechWS?.close(),this.interruptWS?.close(),E.info("Close all UnrealEngine's WebSocket")}}class T{config;udpManager;socketManager;movement;eyeTrack;interrupted;speech;constructor(e){this.config=e,this.udpManager=new M,this.socketManager=new A,this.movement=()=>{},this.eyeTrack=()=>{},this.interrupted=()=>{},this.speech=()=>{},this.checkUnrealEngineStarted()}async checkUnrealEngineStarted(){this.socketManager.connectSocket(),this.movement=this.udpManager.movement.bind(this.udpManager),this.eyeTrack=this.udpManager.eyeTrack.bind(this.udpManager),this.interrupted=this.socketManager.interrupted.bind(this.socketManager),this.speech=this.socketManager.speech.bind(this.socketManager),this.config.eyeTrackEnable&&this.udpManager.enableEyeTrack(this.config.eyeTrackCorrection)}onDestroy(){this.udpManager.onDestroy(),this.socketManager.onDestroy(),E.info("UE tools destroy")}}class I{config;stroage;apiProxy;chat;ueTools;isStart;browserSocketMap;destorySub;access_token;timestamp;socketAddr;get accessToken(){return this.access_token}constructor(e,t){this.config=e,this.stroage=t,this.isStart=!1,this.destorySub=new i.Subject,this.browserSocketMap=new Map,this.timestamp=0,this.access_token=t.getItemSync(d);const{api:s,socket:r}=e.endPoint;this.socketAddr=r,this.apiProxy=new m(s),E.code(f["指定模組初始化完成"],"api"),this.chat=new y(e),E.code(f["指定模組初始化完成"],"chat"),this.ueTools=new T(e),E.code(f["指定模組初始化完成"],"udp tools"),this.openChannel=this.openChannel.bind(this),i.interval(18e4).pipe(i.takeUntil(this.destorySub)).subscribe(()=>{this.inspectClientSocket()})}async start(){if(this.isStart)throw new Error(l["重複初始化"]);this.isStart=!0,await this.replaceAccessToken(),this.chat.memberChange.pipe(i.takeUntil(this.destorySub)).subscribe(({type:t,socket:s})=>{switch(t){case"in":{const t=new D(s.id),n=new i.Subject,o=(r=s.handshake.query.uuid,e.isArray(r)?e.get(r,"[0]",void 0):r??void 0);void 0===o?(this.getProjectList().then(e=>{s.emit("list",e)}),i.fromEvent(s,"uuid").pipe(i.take(1),i.takeUntil(n)).subscribe(e=>{this.openChannel(e,s,t,n)})):this.openChannel(o,s,t,n),this.browserSocketMap.set(s,n);break}case"out":if(this.browserSocketMap.has(s)){const e=this.browserSocketMap.get(s);e.next(),e.complete(),this.browserSocketMap.delete(s)}}var r}),this.chat.start(),E.code(f["啟動服務"],"chat")}async replaceAccessToken(){const e=await this.apiProxy.getAccessToken(this.config.license,this.access_token);return this.access_token=e,this.timestamp=0,this.stroage.setItemSync(d,e),e}async getProjectList(){return await this.apiProxy.getProjectList(this.config.license)}async getProxySocketURL(e){const t=Date.now()-this.timestamp;var s;return t<5e3&&await(s=5100-t,new Promise(e=>{setTimeout(e,s)})),this.timestamp=Date.now(),`${this.socketAddr}/v1/chat/project/${this.accessToken}/${e}`}async openChannel(t,s,r,n){i.fromEvent(s,"cloud").pipe(i.takeUntil(n)).subscribe(e=>{r.sendToCloud(e)}),i.fromEvent(s,"face").pipe(i.takeUntil(n)).subscribe(e=>{const t=e,s=null!==t;this.ueTools.eyeTrack(t),r.sendToCloud({request:"face_detect",content:s})}),r.message.pipe(i.takeUntil(n)).subscribe(t=>{const r=function(t){try{const s=JSON.parse(t);return e.get(s,"signal")?[s]:e.isArray(s)?s:e.values(s)}catch(e){return E.error("message transfer fail",e),[]}}(t),i=[],n=[];e.forEach(r,e=>{switch(e.signal){case"osc":n.push(e);break;case"audio":"pcm"===e.command?this.ueTools.speech(e.content):"interrupted"===e.command&&this.ueTools.interrupted();default:i.push(e)}}),i.length>0&&s.emit("cloud",i),n.length>0&&this.ueTools.movement(n)}),r.close.pipe(i.takeUntil(n)).subscribe(async e=>{switch(s.emit("channel",e),e){case"close":case"no_permissions":r.onDestroy();break;case"reconnect":{await this.replaceAccessToken();const e=await this.getProxySocketURL(t);s.connected?r.reconnect(e):E.error(`The browser(${r.id}) leaves before reconnecting to the cloud`);break}}}),r.linkStart.pipe(i.takeUntil(n)).subscribe(()=>{s.emit("channel","open")}),n.subscribe(()=>{r.onClose()});const o=await this.getProxySocketURL(t);s.connected?r.start(o):E.error(`The browser(${r.id}) leaves before connecting to the cloud`)}inspectClientSocket(){const t=Array.from(this.browserSocketMap);e.forEach(t,([e,t])=>{e.disconnected&&(t.next(),t.complete(),this.browserSocketMap.delete(e),E.info(`The browser(${e.id}) should be closed. Auto clean service.`))})}onDestroy(){this.apiProxy.onDestroy(),this.chat.onDestroy(),this.ueTools.onDestroy(),this.destorySub.next(),this.destorySub.complete(),E.code(f["服務結束"])}}exports.aiiaCore=function(t){if(p){if("string"!=typeof e.get(t,"license",void 0))throw new Error(l["未提供憑證"]);!function(e){const{info:t,error:s,warn:r,debug:i,fatal:n,code:o}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);return o||(_.code=()=>{}),i||(_.debug=()=>{}),t||(_.info=()=>{}),r||(_.warn=()=>{}),s||(_.error=()=>{}),n||(_.fatal=()=>{}),b}(e.get(t,"debug",!0))(new C),E.code(f["開始初始化必要模組"]);const s=new S(e.assign({},t));E.code(f["指定模組初始化完成"],"config");const r=new k;E.code(f["指定模組初始化完成"],"storage");const i=new I(s,r);return E.code(f["指定模組初始化完成"],"core"),i.start(),E.code(f["啟動服務"],"main core"),()=>{i.onDestroy()}}throw new Error(l["環境錯誤(nodejs)"])};
|
package/dist/node.d.ts
CHANGED
|
@@ -34,10 +34,10 @@ declare interface InitOptions {
|
|
|
34
34
|
project?: string | Partial<AiiaProjectItem>;
|
|
35
35
|
/**
|
|
36
36
|
* [zh]
|
|
37
|
-
* 指定的環境,預設值:
|
|
37
|
+
* 指定的環境,預設值: developement
|
|
38
38
|
*
|
|
39
39
|
* [en]
|
|
40
|
-
* Specific Env, default:
|
|
40
|
+
* Specific Env, default: developement
|
|
41
41
|
*/
|
|
42
42
|
env?: "developement" | "production";
|
|
43
43
|
/**
|
|
@@ -109,15 +109,17 @@ declare interface InitOptions {
|
|
|
109
109
|
*/
|
|
110
110
|
llmSampleRate?: number;
|
|
111
111
|
/**
|
|
112
|
+
* @deprecated
|
|
113
|
+
*
|
|
112
114
|
* [zh]
|
|
113
|
-
* 人臉辨識
|
|
115
|
+
* 人臉辨識(已棄用,請改用 `cameraDetection`),同時設定會以 `cameraDetection` 為主
|
|
114
116
|
*
|
|
115
117
|
* @property perSecond: 每秒檢測幾次, 預設: 每秒 3 次
|
|
116
118
|
*
|
|
117
119
|
* @property confidence: 分數(0 ~ 100), 預設: 80
|
|
118
120
|
*
|
|
119
121
|
* [en]
|
|
120
|
-
*
|
|
122
|
+
* Face recognition (deprecated, please use `cameraDetection` instead), and the settings will be based on `cameraDetection`
|
|
121
123
|
*
|
|
122
124
|
* @property perSecond: Detection times per second, default: 3 times per second
|
|
123
125
|
*
|
|
@@ -127,6 +129,25 @@ declare interface InitOptions {
|
|
|
127
129
|
perSecond?: number;
|
|
128
130
|
confidence?: number;
|
|
129
131
|
};
|
|
132
|
+
/**
|
|
133
|
+
* [zh]
|
|
134
|
+
* 攝影機辨識設定
|
|
135
|
+
*
|
|
136
|
+
* @property perSecond: 每秒檢測幾次, 預設: 每秒 3 次
|
|
137
|
+
*
|
|
138
|
+
* @property confidence: 人臉辨識分數(0 ~ 100), 預設: 80
|
|
139
|
+
*
|
|
140
|
+
* [en]
|
|
141
|
+
* Camera Identification Settings
|
|
142
|
+
*
|
|
143
|
+
* @property perSecond: Detection times per second, default: 3 times per second
|
|
144
|
+
*
|
|
145
|
+
* @property confidence: Face recognition score(0 ~ 100), default: 80
|
|
146
|
+
*/
|
|
147
|
+
cameraDetection?: {
|
|
148
|
+
perSecond?: number;
|
|
149
|
+
confidence?: number;
|
|
150
|
+
};
|
|
130
151
|
/**
|
|
131
152
|
* [zh]
|
|
132
153
|
* 清除字幕,可指定時間(毫秒),預設 5000 毫秒後清除
|
|
@@ -134,10 +155,15 @@ declare interface InitOptions {
|
|
|
134
155
|
* [en]
|
|
135
156
|
* Clear subtitles, you can specify the time (milliseconds), the default is to clear after 5000 milliseconds.
|
|
136
157
|
*/
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
158
|
+
autoClearUserSubtitle?: number;
|
|
159
|
+
/**
|
|
160
|
+
* [zh]
|
|
161
|
+
* 清除字幕,可指定時間(毫秒),預設 音訊結束時 清除
|
|
162
|
+
*
|
|
163
|
+
* [en]
|
|
164
|
+
* Clear subtitles, you can specify the time (milliseconds), the default Clear when audio ends.
|
|
165
|
+
*/
|
|
166
|
+
autoClearAiiaSubtitle?: number;
|
|
141
167
|
/**
|
|
142
168
|
* [zh]
|
|
143
169
|
* 啟動眼球追蹤
|
|
@@ -146,11 +172,22 @@ declare interface InitOptions {
|
|
|
146
172
|
*
|
|
147
173
|
*/
|
|
148
174
|
eyeTrackEnable?: boolean;
|
|
175
|
+
/**
|
|
176
|
+
* [zh]
|
|
177
|
+
* 眼球追蹤補正,請根據 Camera 裝置的物理位置添加補正值。
|
|
178
|
+
*
|
|
179
|
+
* [en]
|
|
180
|
+
* Eye tracking correction, please add correction value according to the physical position of the camera device.
|
|
181
|
+
*/
|
|
182
|
+
correction?: {
|
|
183
|
+
x_axis_px?: number;
|
|
184
|
+
y_axis_px?: number;
|
|
185
|
+
};
|
|
149
186
|
}
|
|
150
187
|
|
|
151
188
|
declare type LoggerLevel = "info" | "error" | "warn" | "debug" | "fatal" | "code";
|
|
152
189
|
|
|
153
|
-
export declare type NodeInitOptions = Omit<InitOptions, "project" | "ws_url" | "worklet_url" | "mediaStream" | "chunkTimeInSeconds" | "llmSampleRate" | "
|
|
190
|
+
export declare type NodeInitOptions = Omit<InitOptions, "project" | "ws_url" | "worklet_url" | "mediaStream" | "chunkTimeInSeconds" | "llmSampleRate" | "autoClearUserSubtitle" | "autoClearAiiaSubtitle" | "faceDetection" | "cameraDetection">;
|
|
154
191
|
|
|
155
192
|
declare type Webserver = Server<any, any> | Server_2<any, any> | Http2SecureServer<any, any, any, any> | Http2Server<any, any, any, any>;
|
|
156
193
|
|
package/dist/node.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{assign as e,forEach as t,get as s,map as o,omit as i,isArray as r}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 f from"os";import y 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(){return this.environment,{api:"https://aiia-content-management-dev-21193779403.asia-east1.run.app",socket:"wss://graphen-agentic-workflow-dev-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(){const{user:t,aiia:s}=e({},this.config.autoClearSubtitle);let o=5e3,i=5e3;return"number"==typeof t?o=t>=0?t:o:!1===t&&(o=NaN),"number"==typeof s?i=s>=0?s:i:!1===s&&(i=NaN),{userDelayTime:o,aiiaDelayTime:i}}get eyeTrackEnable(){return this.config.eyeTrackEnable??!1}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 i=n().format("yyyy-MM-DD[T]HH:mm:ss");t(C,e=>{e.next(s,i,...o)}),e[s](`[Aiia::${s}_${i}_]`,...o)};default:return}},set:(e,t,s,o)=>!0});class M{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 L{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??""},i=await this.instance.post("/api/v1/license/agent_token",o).then(e=>s(e.data,"token_value","")).catch(()=>null);if(null===i)throw new Error(m["網絡錯誤"]);if(""===i)throw new Error(m["獲取權杖失敗"]);return i}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 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.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"}(x||(x={}));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()}messageQueue=[];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.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,type:o,wasClean:i}=e;switch(t){case x.WHEN_BROWSER_CLOSE:w.code(R["無副作用的關閉連線"]);break;case x.MDN_NORMAL_CLOSURE:case x.MDN_GOING_AWAY:this.closeSub.next("close"),w.code(R["安全的關閉連線"]);break;case x.MDN_NO_STATUS_RECEIVED:case x.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),w.code(R["與雲端的服務中斷"],{code:t,reason:s,type:o,wasClean:i});break;case x.WS_5001_UNAUTHORIZED:case x.WS_5002_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),w.code(R["沒有授權,請聯繫Graphen"]);break;case x.WS_5000_NORAML_CLOSURE:case x.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:i})}})}reconnect(e){this.onDestroy(),this.destorySub=new u,this.start(e)}flushMessageQueue(){for(;this.messageQueue.length>0&&this.isConnected&&void 0!==this.socket;){const e=this.messageQueue.shift();e&&this.socket.send(e,e=>{e&&w.error("send message fail",e?.message,e)})}}send(e){this.isConnected&&void 0!==this.socket?this.socket.send(e,e=>{e&&w.error("send message fail",e?.message,e)}):this.messageQueue.push(e)}sendToCloud(e){this.send(JSON.stringify(e))}onClose(){this.socket?.close(x.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=f.homedir(),this.EOL=f.EOL,this.platform){case"darwin":this.isMAC=!0,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=y.join(this.homedir,"Library","Application Support");break;case"win32":this.isMAC=!1,this.isWIN=!0,this.isLINUX=!1,this.saveFolder=process.env.APPDATA||y.join(this.homedir,"AppData","Roaming");break;case"linux":this.isMAC=!1,this.isWIN=!1,this.isLINUX=!0,this.saveFolder=process.env.XDG_CONFIG_HOME||y.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=y.join(this.saveFolder,this.AppName),this.folderCheck(this.appFolder)}addPath(...e){return y.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=i(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=i(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 x.MANUAL_CLOSE:w.code(R["無副作用的關閉連線"]);break;case x.WS_5000_NORAML_CLOSURE:case x.MDN_NORMAL_CLOSURE:case x.MDN_GOING_AWAY:this.closeSub.next("close"),w.code(R["安全的關閉連線"]);break;case x.MDN_NO_STATUS_RECEIVED:case x.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),w.code(R["與雲端的服務中斷"],{code:t,reason:s});break;case x.WS_5001_UNAUTHORIZED:case x.WS_5002_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),w.code(R["沒有授權,請聯繫Graphen"]);break;case x.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(x.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 x.MANUAL_CLOSE:w.code(R["無副作用的關閉連線"]);break;case x.WS_5000_NORAML_CLOSURE:case x.MDN_NORMAL_CLOSURE:case x.MDN_GOING_AWAY:this.closeSub.next("close"),w.code(R["安全的關閉連線"]);break;case x.MDN_NO_STATUS_RECEIVED:case x.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),w.code(R["與雲端的服務中斷"],{code:t,reason:s});break;case x.WS_5001_UNAUTHORIZED:case x.WS_5002_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),w.code(R["沒有授權,請聯繫Graphen"]);break;case x.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(x.MANUAL_CLOSE)}onDestroy(){this.destorySub.next(),this.destorySub.complete()}}class F{next(e,t,...s){const o=`log_${n().format("yyyy-MM-DD")}.txt`,i=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(i,`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 L(this.config.endPoint.api),w.code(R["指定模組初始化完成"],"api"),this.chat=new M(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,i=new u,n=(o=t.handshake.query.uuid,r(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(i)).subscribe(s=>{this.openChannel(s,t,e,i)})):this.openChannel(n,t,e,i),this.channelMap.set(t.id,()=>{i.next(),i.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,i){d(s,"cloud").pipe(S(i)).subscribe(e=>{o.send(e)}),d(s,"face").pipe(S(i)).subscribe(t=>{const s=t,i=null!==s;if(i&&void 0!==this.eyeBallUdp){const{x:t,y:o}=function(t,s){const{maxX:o,maxY:i,minX:r,minY:n}=e({maxX:25,minX:-25,maxY:50,minY:-50},s),l=c(o)+c(r),u=c(i)+c(n),{clientHeight:d,clientWidth:S,originX:b,originY:p,width:_,height:g}=t,f=a([[l/S,0,r],[0,u/d,n],[0,0,1]]),y=b+_/2,E=d-(p+g/2),m=a([[y>S?S:y<0?0:y],[E>d?d:E<0?0:E],[1]]),O=h(f,m);return{x:O.get([0,0]),y:O.get([1,0])}}(s);this.eyeBallUdp.send(["/",t,o],e=>{null!=e&&w.error("eye track error:",e)})}o.sendToCloud({request:"face_detect",content:i})}),o.message.pipe(S(i)).subscribe(e=>{s.emit("cloud",e),this.udpProxy.send(e),this.audioProxy.send(e),this.audioIntrProxy.send(e)}),o.close.pipe(S(i)).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(i)).subscribe(()=>{s.emit("channel","open")}),i.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: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||(k.code=()=>{}),i||(k.debug=()=>{}),t||(k.info=()=>{}),o||(k.warn=()=>{}),s||(k.error=()=>{}),r||(k.fatal=()=>{}),A}(s(t,"debug",!0))(new F),w.code(R["開始初始化必要模組"]);const o=new N(e({},t));w.code(R["指定模組初始化完成"],"config");const i=new T;w.code(R["指定模組初始化完成"],"storage");const r=new G(o,i);return w.code(R["指定模組初始化完成"],"core"),r.start(),w.code(R["啟動服務"]),()=>{r.onDestroy()}}throw new Error(m["環境錯誤(nodejs)"])}export{j as aiiaCore};
|
|
1
|
+
import{get as e,assign as t,forEach as s,map as r,omit as o,isInteger as i,isArray as n,values as a}from"lodash-es";import c from"moment";import{abs as h,matrix as l,multiply as u}from"mathjs";import{Server as d}from"socket.io";import{Subject as p,fromEvent as S,takeUntil as f,take as _,interval as b}from"rxjs";import g from"axios";import m from"ws";import E from"fs";import y from"os";import O from"path";import"child_process";import{Client as D}from"node-osc";var R;!function(e){e["網絡錯誤"]="ERROR CODE: 500",e["環境錯誤(browser)"]="ERROR CODE: 1001",e["Worklet模組載入失敗"]="ERROR CODE: 1002",e["未獲得媒體裝置權限"]="ERROR CODE: 1003",e["實例已摧毀"]="ERROR CODE: 1004",e["環境錯誤(nodejs)"]="ERROR CODE: 2001",e["未提供憑證"]="ERROR CODE: 2002",e["獲取權杖失敗"]="ERROR CODE: 2003",e["沒有可用專案"]="ERROR CODE: 2004",e["重複初始化"]="ERROR CODE: 2005"}(R||(R={}));const N="access_token",w="undefined"!=typeof process&&null!=process.versions&&null!=process.versions.node;class k{config;get webserver(){return this.safeRead("webserver")}get environment(){const e=this.safeRead("env");return"string"==typeof e?e:"developement"}get license(){return this.safeRead("license")??""}get port(){return function(e,t){const s="number"==typeof t?t:NaN;if(e){const t=Number(e);return isNaN(t)?s:t}return s}(process.env.PORT,this.safeRead("port")??3e3)}get ws_url(){const e=this.safeRead("ws_url");return"string"==typeof e?e:""}get worklet_url(){const e=this.safeRead("worklet_url");return"string"==typeof e?e:""}get project(){const t=this.safeRead("project");if(void 0===t)return{specific:!1};if("string"==typeof t)return{name:t,id:t,specific:!1};{const s=e(t,"id",void 0),r=e(t,"name",void 0);return{id:"string"==typeof s?s:void 0,name:"string"==typeof r?r:void 0,specific:!0}}}get endPoint(){let e="dev";switch(this.environment){case"production":e="prod";break;case"test":e="test"}const t=`graphen-agentic-workflow-${e}-21193779403.asia-east1.run.app`;return{api:`https://${t}`,socket:`wss://${t}`}}get debug(){return this.safeRead("debug")??!0}get mediaStream(){return this.safeRead("mediaStream")}get chunkTimeInSeconds(){const e=this.safeRead("chunkTimeInSeconds");return"number"==typeof e&&e>0?e:.3}get llmSampleRate(){const e=this.safeRead("llmSampleRate");return"number"==typeof e&&e>0?e:16e3}get detection(){return t({perSecond:3,confidence:80},this.safeRead("faceDetection"),this.safeRead("cameraDetection"))}get autoClearSubtitle(){const e=this.safeRead("autoClearUserSubtitle"),t=this.safeRead("autoClearAiiaSubtitle");return{userDelayTime:"number"==typeof e?e:5e3,aiiaDelayTime:"number"==typeof t?t:NaN}}get eyeTrackEnable(){return Boolean(this.safeRead("eyeTrackEnable"))}get eyeTrackCorrection(){return t({x_axis_px:0,y_axis_px:0},this.safeRead("correction"))}constructor(e){this.config=e}safeRead(t){return e(this.config,t,void 0)}}var C;!function(e){e["通道、雲端服務、麥克風都已就緒"]="CODE: 1000",e["未取得媒體裝置權限,請重新設定或忽略此訊息"]="CODE: 1001",e["請訂閱專案列表"]="CODE: 1002",e["初始化必要依賴"]="CODE: 1010",e["指定依賴已完成"]="CODE: 1011",e["啟動SDK"]="CODE: 1012",e["結束SDK"]="CODE: 1013",e["已啟動SDK"]="CODE: 1014",e["安全的關閉連線"]="CODE: 2000",e["無副作用的關閉連線"]="CODE: 2001",e["與雲端的服務中斷"]="CODE: 2002",e["沒有授權,請聯繫Graphen"]="CODE: 2003",e["自動重新連線雲端服務"]="CODE: 2004",e["未預期的斷線,請聯繫Graphen"]="CODE: 2005",e["開始初始化必要模組"]="CODE: 2010",e["指定模組初始化完成"]="CODE: 2011",e["啟動服務"]="CODE: 2012",e["服務結束"]="CODE: 2013"}(C||(C={}));const M={code:console.log,debug:console.log,info:console.log,warn:console.warn,error:console.error,fatal:console.error},A=[];function T(e){return A.push(e),T}const I=new Proxy(M,{get(e,t,r){if("string"==typeof t)switch(t){case"debug":case"info":case"error":case"warn":case"fatal":case"code":return(...r)=>{const o=c().format("yyyy-MM-DD[T]HH:mm:ss:sss");s(A,e=>{e.next(t,o,...r)}),e[t](`[Aiia::${t}_${o}_]`,...r)};default:return}},set:(e,t,s,r)=>!0});class x{config;server;memberSub;get port(){return this.config.port}get webserver(){return this.config.webserver}get memberChange(){return this.memberSub.asObservable()}constructor(e){this.config=e,this.memberSub=new p}start(){if(this.server=new d(this.webserver,{cors:{origin:"*"}}),this.server.on("connection",e=>{I.debug("client on",e.id),this.memberSub.next({type:"in",socket:e}),e.on("disconnect",t=>{I.debug("client off",e.id,"reason",t),this.memberSub.next({type:"out",socket:e})})}),void 0===this.webserver){const e=this.port;this.server.listen(e),I.info("Websocket server run on",e)}else I.info("Websocket server rely your webserver.")}onDestroy(){this.server&&this.server.close()}}class v{instance;controller;constructor(e){this.controller=new AbortController,this.instance=g.create({baseURL:e,signal:this.controller.signal})}async getAccessToken(t,s){const r={license_number:t,token_value:s??""},o=await this.instance.post("/api/v1/license/agent_token",r).then(t=>e(t.data,"token_value","")).catch(()=>null);if(null===o)throw new Error(R["網絡錯誤"]);if(""===o)throw new Error(R["獲取權杖失敗"]);return o}async getProjectList(t){const s=await this.instance.post("/api/v1/license/projects",{license_number:t}).then(t=>r(e(t,"data.projects",[]),({id:e,sub_project_name:t,avatar:s})=>({avatar:s,id:e,name:t}))).catch(e=>null);if(null===s)throw new Error(R["網絡錯誤"]);if(0===s.length)throw new Error(R["沒有可用專案"]);return s}onDestroy(){this.controller.abort()}}var L;!function(e){e[e.MDN_NORMAL_CLOSURE=1e3]="MDN_NORMAL_CLOSURE",e[e.MDN_GOING_AWAY=1001]="MDN_GOING_AWAY",e[e.MDN_PROTOCOL_ERROR=1002]="MDN_PROTOCOL_ERROR",e[e.MDN_UNSUPPORTED_DATA=1003]="MDN_UNSUPPORTED_DATA",e[e.MDN_RESERVED_1004=1004]="MDN_RESERVED_1004",e[e.MDN_NO_STATUS_RECEIVED=1005]="MDN_NO_STATUS_RECEIVED",e[e.MDN_ABNORMAL_CLOSURE=1006]="MDN_ABNORMAL_CLOSURE",e[e.MDN_INVALID_FRAME_PAYLOAD=1007]="MDN_INVALID_FRAME_PAYLOAD",e[e.MDN_POLICY_VIOLATION=1008]="MDN_POLICY_VIOLATION",e[e.MDN_MESSAGE_TOO_BIG=1009]="MDN_MESSAGE_TOO_BIG",e[e.MDN_MANDATORY_EXTENSION=1010]="MDN_MANDATORY_EXTENSION",e[e.MDN_INTERNAL_ERROR=1011]="MDN_INTERNAL_ERROR",e[e.MDN_SERVICE_RESTART=1012]="MDN_SERVICE_RESTART",e[e.MDN_TRY_AGAIN_LATER=1013]="MDN_TRY_AGAIN_LATER",e[e.MDN_BAD_GATEWAY=1014]="MDN_BAD_GATEWAY",e[e.MDN_TLS_HANDSHAKE=1015]="MDN_TLS_HANDSHAKE",e[e.WHEN_BROWSER_CLOSE=4001]="WHEN_BROWSER_CLOSE",e[e.WS_4100_NORAML_CLOSURE=4100]="WS_4100_NORAML_CLOSURE",e[e.WS_4101_UNAUTHORIZED=4101]="WS_4101_UNAUTHORIZED",e[e.WS_4102_LICENSE_EXPIRED=4102]="WS_4102_LICENSE_EXPIRED",e[e.WS_4103_TOKEN_EXPIRED=4103]="WS_4103_TOKEN_EXPIRED",e[e.WS_4104_LLM_SESSION_ERROR=4104]="WS_4104_LLM_SESSION_ERROR",e[e.WS_4105_TOO_MANY_CONNECTIONS=4105]="WS_4105_TOO_MANY_CONNECTIONS"}(L||(L={}));class W{id;uuid;socket;messageSub;linkStartSub;destorySub;closeSub;isFinished;debugInfo;get message(){return this.messageSub.asObservable()}get close(){return this.closeSub.asObservable()}get linkStart(){return this.linkStartSub.asObservable()}get isConnected(){return this.socket?.readyState===m.OPEN}constructor(e){this.id=e,this.messageSub=new p,this.linkStartSub=new p,this.closeSub=new p,this.destorySub=new p,this.isFinished=!1,this.uuid=crypto.randomUUID(),this.debugInfo=`browser(${this.id}), proxy(${this.uuid})`,I.info(`create proxy for browser(${this.id})`)}start(e){this.socket=new m(e,{autoPong:!0}),S(this.socket,"open").pipe(f(this.destorySub)).subscribe(()=>{I.code(C["啟動服務"],"proxy",this.debugInfo),this.linkStartSub.next()}),S(this.socket,"message").pipe(f(this.destorySub)).subscribe(e=>{const{data:t}=e;this.messageSub.next(t)}),S(this.socket,"error").pipe(f(this.destorySub)).subscribe(e=>{const{error:t}=e;I.error(this.debugInfo,"Proxy has some problem",t)}),S(this.socket,"close").pipe(f(this.destorySub)).subscribe(e=>{const{code:t,reason:s,type:r,wasClean:o}=e;switch(t){case L.WHEN_BROWSER_CLOSE:I.code(C["無副作用的關閉連線"],this.debugInfo),this.onDestroy();break;case L.MDN_NORMAL_CLOSURE:case L.MDN_GOING_AWAY:this.closeSub.next("close"),I.code(C["安全的關閉連線"],this.debugInfo);break;case L.MDN_NO_STATUS_RECEIVED:case L.MDN_ABNORMAL_CLOSURE:this.closeSub.next("close"),I.code(C["與雲端的服務中斷"],this.debugInfo,{code:t,reason:s,type:r,wasClean:o});break;case L.WS_4100_NORAML_CLOSURE:case L.WS_4103_TOKEN_EXPIRED:case L.WS_4104_LLM_SESSION_ERROR:case L.WS_4105_TOO_MANY_CONNECTIONS:this.closeSub.next("reconnect"),I.code(C["自動重新連線雲端服務"],this.debugInfo,`socket code: ${t}`);break;case L.WS_4101_UNAUTHORIZED:case L.WS_4102_LICENSE_EXPIRED:this.closeSub.next("no_permissions"),I.code(C["沒有授權,請聯繫Graphen"],this.debugInfo,`socket code: ${t}`);break;default:this.closeSub.next("close"),I.code(C["未預期的斷線,請聯繫Graphen"],this.debugInfo,{code:t,reason:s,type:r,wasClean:o})}})}reconnect(e){this.isFinished||(this.destorySub.next(),this.destorySub.complete(),this.destorySub=new p,this.start(e))}send(e){const t=`currentTime: ${c().format("yyyy-MM-DD[T]HH:mm:ss:sss")}`;this.isConnected?this.socket.send(e,s=>{s&&I.error("send message fail",this.debugInfo,t,e.slice(0,100),s)}):I.warn("The socket is not connected",this.debugInfo,t,e.slice(0,100))}sendToCloud(e){this.send(JSON.stringify(e))}onClose(){if(this.socket)switch(this.socket.readyState){case 0:I.info("Once the cloud connection is complete, the connection will be disconnected."),this.linkStart.pipe(f(this.destorySub),_(1)).subscribe(()=>{this.socket?.close(L.WHEN_BROWSER_CLOSE)});break;case 1:I.info("Close safely, no side effects"),this.socket.close(L.WHEN_BROWSER_CLOSE);break;case 2:case 3:I.info("The connection have been lost.")}}onDestroy(){this.isFinished=!0,this.destorySub.next(),this.destorySub.complete(),I.info("Destroy proxy",this.debugInfo)}}const P=new class{AppName;platform;isMAC;isWIN;isLINUX;saveFolder;homedir;appFolder;EOL;constructor(e){switch(this.AppName=e,this.platform=process.platform,this.homedir=y.homedir(),this.EOL=y.EOL,this.platform){case"darwin":this.isMAC=!0,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=O.join(this.homedir,"Library","Application Support");break;case"win32":this.isMAC=!1,this.isWIN=!0,this.isLINUX=!1,this.saveFolder=process.env.APPDATA||O.join(this.homedir,"AppData","Roaming");break;case"linux":this.isMAC=!1,this.isWIN=!1,this.isLINUX=!0,this.saveFolder=process.env.XDG_CONFIG_HOME||O.join(this.homedir,".config");break;default:console.log(`當前系統是未知的平台: ${this.platform}`),this.isMAC=!1,this.isWIN=!1,this.isLINUX=!1,this.saveFolder=this.homedir}this.appFolder=O.join(this.saveFolder,this.AppName),this.folderCheck(this.appFolder)}addPath(...e){return O.join(...e)}folderCheck(e){E.mkdirSync(e,{recursive:!0})}fileCheck(e){E.existsSync(e)||E.writeFileSync(e,"",{encoding:"utf8"})}}("aiia-sdk");class U{type="node";filePath;fileName;constructor(){this.fileName="storage.json",this.filePath=P.addPath(P.appFolder,this.fileName),P.fileCheck(this.filePath)}getFileDataSync(){const e=E.readFileSync(this.filePath,"utf8");try{return JSON.parse(e)}catch(e){return{}}}setFileDataSync(e){try{const t=JSON.stringify(e);E.writeFileSync(this.filePath,t,"utf8")}catch(e){}}async getFileData(){return new Promise(e=>{E.readFile(this.filePath,"utf8",(t,s)=>{if(t)e({});else try{e(JSON.parse(s))}catch(t){e({})}})})}async setFileData(e){return new Promise(t=>{try{const s=JSON.stringify(e);E.writeFile(this.filePath,s,"utf8",e=>{t()})}catch(e){}})}async getItem(e){return(await this.getFileData())[e]??null}async setItem(e,t){const s=await this.getFileData();s[e]=t,await this.setFileData(s)}async removeItem(e){const t=await this.getFileData(),s=o(t,e);await this.setFileData(s)}async clear(){await this.setFileData({})}getItemSync(e){return this.getFileDataSync()[e]??null}setItemSync(e,t){const s=this.getFileDataSync();s[e]=t,this.setFileDataSync(s)}removeItemSync(e){const t=o(this.getFileDataSync(),e);this.setFileDataSync(t)}clearSync(){this.setFileDataSync({})}}const $={[C["通道、雲端服務、麥克風都已就緒"]]:"通道、雲端服務、麥克風都已就緒",[C["未取得媒體裝置權限,請重新設定或忽略此訊息"]]:"未取得媒體裝置權限,請重新設定或忽略此訊息",[C["請訂閱專案列表"]]:"請訂閱專案列表",[C["初始化必要依賴"]]:"初始化必要依賴",[C["指定依賴已完成"]]:"指定依賴已完成",[C["啟動SDK"]]:"啟動SDK",[C["結束SDK"]]:"結束SDK",[C["已啟動SDK"]]:"已啟動SDK",[C["安全的關閉連線"]]:"安全的關閉連線",[C["無副作用的關閉連線"]]:"無副作用的關閉連線",[C["與雲端的服務中斷"]]:"與雲端的服務中斷",[C["沒有授權,請聯繫Graphen"]]:"沒有授權,請聯繫Graphen",[C["自動重新連線雲端服務"]]:"自動重新連線雲端服務",[C["未預期的斷線,請聯繫Graphen"]]:"未預期的斷線,請聯繫Graphen",[C["開始初始化必要模組"]]:"開始初始化必要模組",[C["指定模組初始化完成"]]:"指定模組初始化完成",[C["啟動服務"]]:"啟動服務",[C["服務結束"]]:"服務結束",[R["網絡錯誤"]]:"網絡錯誤",[R["環境錯誤(browser)"]]:"環境錯誤",[R["Worklet模組載入失敗"]]:"Worklet模組載入失敗",[R["未獲得媒體裝置權限"]]:"未獲得媒體裝置權限",[R["實例已摧毀"]]:"實例已摧毀",[R["環境錯誤(nodejs)"]]:"nodejs",[R["未提供憑證"]]:"未提供憑證",[R["獲取權杖失敗"]]:"獲取權杖失敗",[R["沒有可用專案"]]:"沒有可用專案",[R["重複初始化"]]:"重複初始化"},F=new RegExp(/^(ERROR )?CODE: [0-9]{3,4}$/);class G{next(e,t,...s){const r=`log_${c().format("yyyy-MM-DD")}.txt`,o=P.addPath(P.appFolder,r),i="code"===e?e=>F.test(e)?`${e}(${$[e]})`:e:e=>e;try{const r=s.reduce((e,t)=>{if(t instanceof Error){let s=`Error[${t.message??"No Message"}]${P.EOL}`;return s+=t.stack??"unknow stack","string"==typeof t.cause&&(s+=`${P.EOL}cause: ${t.cause}`),`${e}${P.EOL}${s}${P.EOL}`}switch(typeof t){case"string":return`${e} ${i(t)}`;case"number":case"bigint":return`${e} ${t.toString()}`;case"boolean":return`${e} ${t?"true":"false"}`;case"object":try{return`${e} ${JSON.stringify(t)}`}catch(t){return e}case"symbol":case"undefined":case"function":return`${e} [${typeof t}]`}},"");E.appendFileSync(o,`LOG(${e})__${t}__::${r}${P.EOL}`,{encoding:"utf8"})}catch(e){}}}class j{clientMap;eyeBallUdp;x_axis_px;y_axis_px;constructor(){this.clientMap=new Map,this.x_axis_px=0,this.y_axis_px=0}enableEyeTrack(e){this.eyeBallUdp=new D("127.0.0.1",6745),this.x_axis_px=e.x_axis_px,this.y_axis_px=e.y_axis_px}eyeTrack(e){if(void 0!==this.eyeBallUdp){const s=null!==e,{x:r,y:o}=(()=>{if(s){const{x:s,y:r}=function(e,s){const{maxX:r,maxY:o,minX:i,minY:n}=t({maxX:25,minX:-25,maxY:50,minY:-50},s),a=h(r)+h(i),c=h(o)+h(n),{clientHeight:d,clientWidth:p,originX:S,originY:f,width:_,height:b}=e,g=l([[a/p,0,i],[0,c/d,n],[0,0,1]]),m=l([[S+_/2],[d-(f+b/2)],[1]]),E=u(g,m);return{x:E.get([0,0]),y:E.get([1,0])}}(e);return{x:this.toFloat(s+this.x_axis_px),y:this.toFloat(r+this.y_axis_px)}}return{x:this.toFloat(7),y:this.toFloat(20)}})();this.eyeBallUdp.send(["/",r,o],e=>{null!=e&&I.error("Eye track error:",e)})}}movement(e){r(e,({content:e})=>{const{port:t,host:s,message:r}=e;this.getClient(t,s).send(r,e=>{null!=e&&I.error("Occur error when send message to UE",e)})})}getClient(e,t){if(this.clientMap.has(e))return this.clientMap.get(e);{const s=new D(t,e);return this.clientMap.set(e,s),s}}toFloat(e){return i(e)?e+1e-5:e}onDestroy(){s(Array.from(this.clientMap),([e,t])=>{t.close()}),I.info("Close all UnrealEngine's UDP")}}class Y{speechWS;interruptWS;get speechIsConnected(){return this.speechWS?.readyState===m.OPEN}get intrIsConnected(){return this.interruptWS?.readyState===m.OPEN}connectSocket(){this.speechWS=new m("ws://localhost:8080"),this.interruptWS=new m("ws://localhost:8081"),this.speechWS.onerror=e=>{I.error(`speech tool fail message: ${e.message}`,e.error)},this.interruptWS.onerror=e=>{I.error(`interrupt tool fail message: ${e.message}`,e.error)}}interrupted(){this.intrIsConnected&&this.interruptWS.send("interrupted")}speech(e){if(this.speechIsConnected){const t=Buffer.allocUnsafe(2*e.length);for(let s=0;s<e.length;s++)t.writeInt16LE(e[s],2*s);this.speechWS.send(t)}}onDestroy(){this.speechWS?.close(),this.interruptWS?.close(),I.info("Close all UnrealEngine's WebSocket")}}class X{config;udpManager;socketManager;movement;eyeTrack;interrupted;speech;constructor(e){this.config=e,this.udpManager=new j,this.socketManager=new Y,this.movement=()=>{},this.eyeTrack=()=>{},this.interrupted=()=>{},this.speech=()=>{},this.checkUnrealEngineStarted()}async checkUnrealEngineStarted(){this.socketManager.connectSocket(),this.movement=this.udpManager.movement.bind(this.udpManager),this.eyeTrack=this.udpManager.eyeTrack.bind(this.udpManager),this.interrupted=this.socketManager.interrupted.bind(this.socketManager),this.speech=this.socketManager.speech.bind(this.socketManager),this.config.eyeTrackEnable&&this.udpManager.enableEyeTrack(this.config.eyeTrackCorrection)}onDestroy(){this.udpManager.onDestroy(),this.socketManager.onDestroy(),I.info("UE tools destroy")}}class B{config;stroage;apiProxy;chat;ueTools;isStart;browserSocketMap;destorySub;access_token;timestamp;socketAddr;get accessToken(){return this.access_token}constructor(e,t){this.config=e,this.stroage=t,this.isStart=!1,this.destorySub=new p,this.browserSocketMap=new Map,this.timestamp=0,this.access_token=t.getItemSync(N);const{api:s,socket:r}=e.endPoint;this.socketAddr=r,this.apiProxy=new v(s),I.code(C["指定模組初始化完成"],"api"),this.chat=new x(e),I.code(C["指定模組初始化完成"],"chat"),this.ueTools=new X(e),I.code(C["指定模組初始化完成"],"udp tools"),this.openChannel=this.openChannel.bind(this),b(18e4).pipe(f(this.destorySub)).subscribe(()=>{this.inspectClientSocket()})}async start(){if(this.isStart)throw new Error(R["重複初始化"]);this.isStart=!0,await this.replaceAccessToken(),this.chat.memberChange.pipe(f(this.destorySub)).subscribe(({type:t,socket:s})=>{switch(t){case"in":{const t=new W(s.id),o=new p,i=(r=s.handshake.query.uuid,n(r)?e(r,"[0]",void 0):r??void 0);void 0===i?(this.getProjectList().then(e=>{s.emit("list",e)}),S(s,"uuid").pipe(_(1),f(o)).subscribe(e=>{this.openChannel(e,s,t,o)})):this.openChannel(i,s,t,o),this.browserSocketMap.set(s,o);break}case"out":if(this.browserSocketMap.has(s)){const e=this.browserSocketMap.get(s);e.next(),e.complete(),this.browserSocketMap.delete(s)}}var r}),this.chat.start(),I.code(C["啟動服務"],"chat")}async replaceAccessToken(){const e=await this.apiProxy.getAccessToken(this.config.license,this.access_token);return this.access_token=e,this.timestamp=0,this.stroage.setItemSync(N,e),e}async getProjectList(){return await this.apiProxy.getProjectList(this.config.license)}async getProxySocketURL(e){const t=Date.now()-this.timestamp;var s;return t<5e3&&await(s=5100-t,new Promise(e=>{setTimeout(e,s)})),this.timestamp=Date.now(),`${this.socketAddr}/v1/chat/project/${this.accessToken}/${e}`}async openChannel(t,r,o,i){S(r,"cloud").pipe(f(i)).subscribe(e=>{o.sendToCloud(e)}),S(r,"face").pipe(f(i)).subscribe(e=>{const t=e,s=null!==t;this.ueTools.eyeTrack(t),o.sendToCloud({request:"face_detect",content:s})}),o.message.pipe(f(i)).subscribe(t=>{const o=function(t){try{const s=JSON.parse(t);return e(s,"signal")?[s]:n(s)?s:a(s)}catch(e){return I.error("message transfer fail",e),[]}}(t),i=[],c=[];s(o,e=>{switch(e.signal){case"osc":c.push(e);break;case"audio":"pcm"===e.command?this.ueTools.speech(e.content):"interrupted"===e.command&&this.ueTools.interrupted();default:i.push(e)}}),i.length>0&&r.emit("cloud",i),c.length>0&&this.ueTools.movement(c)}),o.close.pipe(f(i)).subscribe(async e=>{switch(r.emit("channel",e),e){case"close":case"no_permissions":o.onDestroy();break;case"reconnect":{await this.replaceAccessToken();const e=await this.getProxySocketURL(t);r.connected?o.reconnect(e):I.error(`The browser(${o.id}) leaves before reconnecting to the cloud`);break}}}),o.linkStart.pipe(f(i)).subscribe(()=>{r.emit("channel","open")}),i.subscribe(()=>{o.onClose()});const c=await this.getProxySocketURL(t);r.connected?o.start(c):I.error(`The browser(${o.id}) leaves before connecting to the cloud`)}inspectClientSocket(){const e=Array.from(this.browserSocketMap);s(e,([e,t])=>{e.disconnected&&(t.next(),t.complete(),this.browserSocketMap.delete(e),I.info(`The browser(${e.id}) should be closed. Auto clean service.`))})}onDestroy(){this.apiProxy.onDestroy(),this.chat.onDestroy(),this.ueTools.onDestroy(),this.destorySub.next(),this.destorySub.complete(),I.code(C["服務結束"])}}function H(s){if(w){if("string"!=typeof e(s,"license",void 0))throw new Error(R["未提供憑證"]);!function(e){const{info:t,error:s,warn:r,debug:o,fatal:i,code:n}=function(e){if(void 0===e||"all"===e)return{code:!0,error:!0,warn:!0,info:!0,debug:!0,fatal:!0};if("none"===e)return{error:!1,code:!1,warn:!1,info:!1,debug:!1,fatal:!1};if("boolean"==typeof e)return{code:e,error:e,warn:e,info:e,debug:e,fatal:e};{const t=new Set(e);return{code:t.has("code"),debug:t.has("debug"),fatal:t.has("fatal"),error:t.has("error"),warn:t.has("warn"),info:t.has("info")}}}(e);return n||(M.code=()=>{}),o||(M.debug=()=>{}),t||(M.info=()=>{}),r||(M.warn=()=>{}),s||(M.error=()=>{}),i||(M.fatal=()=>{}),T}(e(s,"debug",!0))(new G),I.code(C["開始初始化必要模組"]);const r=new k(t({},s));I.code(C["指定模組初始化完成"],"config");const o=new U;I.code(C["指定模組初始化完成"],"storage");const i=new B(r,o);return I.code(C["指定模組初始化完成"],"core"),i.start(),I.code(C["啟動服務"],"main core"),()=>{i.onDestroy()}}throw new Error(R["環境錯誤(nodejs)"])}export{H as aiiaCore};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphen.ai/aiia-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"keywords": [],
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Jay Huang",
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"@types/node": "^24.2.1",
|
|
61
61
|
"@types/node-osc": "^9.1.0",
|
|
62
62
|
"@types/ws": "^8.18.1",
|
|
63
|
+
"@unrs/resolver-binding-darwin-arm64": "^1.11.1",
|
|
63
64
|
"jest": "^30.1.3",
|
|
64
65
|
"jest-environment-jsdom": "^30.1.2",
|
|
65
66
|
"rollup": "^4.46.2",
|