@huyooo/ai-chat-bridge-electron 0.3.20 → 0.3.22
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/dist/main/index.js +1 -1
- package/package.json +6 -6
package/dist/main/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ipcMain as e,shell as t}from"electron";import*as n from"fs";import*as r from"path";import{ChatRuntime as i,DEFAULT_MODEL as a,buildModelOptions as o,isToolError as s,normalizeToolResult as c,resolveToolGovernanceSnapshot as l,resolveToolResultUi as u,serializeToolResult as d}from"@huyooo/ai-chat-core";import{assetManager as f,createDefaultToolExecutor as p}from"@huyooo/ai-chat-host-node";import{createStorage as m}from"@huyooo/ai-chat-storage";import*as h from"zlib";import g from"ws";import{v4 as _}from"uuid";const v={FULL_CLIENT_REQUEST:1,AUDIO_ONLY_REQUEST:2,FULL_SERVER_RESPONSE:9,ERROR_RESPONSE:15},y={NONE:0,HAS_SEQUENCE:1,LAST_PACKET:2,LAST_PACKET_WITH_SEQUENCE:3},b={NONE:0,JSON:1},x={NONE:0,GZIP:1};function S(e,t,n,r){return[17,e<<4|t,n<<4|r,0]}function C(e,t=!0){let n=S(v.FULL_CLIENT_REQUEST,y.NONE,b.JSON,t?x.GZIP:x.NONE),r=JSON.stringify(e),i=Buffer.from(r,`utf-8`),a=t?h.gzipSync(i):i,o=Buffer.alloc(4);return o.writeUInt32BE(a.length,0),Buffer.concat([Buffer.from(n),o,a])}function w(e,t=!1,n=!0){let r=S(v.AUDIO_ONLY_REQUEST,t?y.LAST_PACKET:y.NONE,b.NONE,n?x.GZIP:x.NONE),i=n?h.gzipSync(e):e,a=Buffer.alloc(4);return a.writeUInt32BE(i.length,0),Buffer.concat([Buffer.from(r),a,i])}function T(e){if(e.length<4)throw Error(`Invalid response: too short`);let t=e[0],n=e[1],r=e[2],i=t>>4&15,a=(t&15)*4,o=n>>4&15,s=n&15,c=r>>4&15,l=r&15;if(i!==1)throw Error(`Unsupported protocol version: ${i}`);let u=a,d;(s&1)==1&&e.length>=u+4&&(d=e.readUInt32BE(u),u+=4);let f=(s&2)==2;if(o===v.ERROR_RESPONSE){let t=e.readUInt32BE(u);u+=4;let n=e.readUInt32BE(u);u+=4;let r=e.slice(u,u+n).toString(`utf-8`);return{type:`error`,sequence:d,isLast:!0,data:{code:t,message:r}}}if(o===v.FULL_SERVER_RESPONSE){let t=e.readUInt32BE(u);if(u+=4,t===0)return{type:`result`,sequence:d,isLast:f,data:{}};let n=e.slice(u,u+t);if(l===x.GZIP)try{n=h.gunzipSync(n)}catch(e){throw e}let r;if(c===b.JSON){let e=n.toString(`utf-8`);try{r=JSON.parse(e)}catch(e){throw e}}else r={};return{type:`result`,sequence:d,isLast:f,data:r}}throw Error(`Unknown message type: ${o}`)}var E=class{config;ws=null;callbacks={};connectId=``;isConnected=!1;sessionConfig={};constructor(e){this.config=e}getWsUrl(){return this.config.useAsyncMode===!1?`wss://openspeech.bytedance.com/api/v3/sauc/bigmodel`:`wss://openspeech.bytedance.com/api/v3/sauc/bigmodel_async`}getResourceId(){return this.config.resourceId||`volc.bigasr.sauc.duration`}connect(e,t={}){return new Promise((n,r)=>{this.callbacks=e,this.sessionConfig=t,this.connectId=_(),this.ws=new g(this.getWsUrl(),{headers:{"X-Api-App-Key":this.config.appId,"X-Api-Access-Key":this.config.accessKey,"X-Api-Resource-Id":this.getResourceId(),"X-Api-Connect-Id":this.connectId}}),this.ws.on(`open`,()=>{this.isConnected=!0,this.sendFullClientRequest(),this.callbacks.onConnected?.(),n()}),this.ws.on(`message`,e=>{try{let t=T(e);if(t.type===`error`){let e=t.data;this.callbacks.onError?.(Error(`ASR Error ${e.code}: ${e.message}`))}else{let e=t.data;this.callbacks.onResult?.(e,t.isLast),t.isLast}}catch(e){this.callbacks.onError?.(e instanceof Error?e:Error(String(e)))}}),this.ws.on(`error`,e=>{this.callbacks.onError?.(e),r(e)}),this.ws.on(`close`,()=>{this.isConnected=!1,this.ws=null,this.callbacks.onClose?.()})})}sendFullClientRequest(){if(!this.ws||this.ws.readyState!==g.OPEN)return;let e=C({user:{uid:`ai-chat-user`},audio:{format:this.sessionConfig.format||`pcm`,rate:this.sessionConfig.sampleRate||16e3,bits:16,channel:1},request:{model_name:`bigmodel`,enable_itn:this.sessionConfig.enableItn??!0,enable_punc:this.sessionConfig.enablePunc??!0,enable_ddc:this.sessionConfig.enableDdc??!1,show_utterances:this.sessionConfig.showUtterances??!0,result_type:`full`}});this.ws.send(e)}sendAudio(e){if(!this.ws||this.ws.readyState!==g.OPEN)return;let t=w(e,!1);this.ws.send(t)}finish(){if(!this.ws||this.ws.readyState!==g.OPEN)return;let e=w(Buffer.alloc(0),!0);this.ws.send(e)}disconnect(){this.ws&&=(this.ws.close(),null),this.isConnected=!1}get connected(){return this.isConnected&&this.ws?.readyState===g.OPEN}};function D(t){let{channelPrefix:n=`ai-chat`,appId:r,accessKey:i,resourceId:a}=t,o=new Map;function s(e,t){let n=o.get(e);return n||(n={client:new E({appId:r,accessKey:i,resourceId:a,useAsyncMode:!0}),webContents:t},o.set(e,n),t.on(`destroyed`,()=>{let t=o.get(e);t&&(t.client.disconnect(),o.delete(e))})),n}return e.handle(`${n}:asr:start`,async(e,t)=>{let r=e.sender,i=r.id,a=s(i,r);a.client.connected&&a.client.disconnect();try{return await a.client.connect({onConnected:()=>{r.isDestroyed()||r.send(`${n}:asr:connected`)},onResult:(e,t)=>{r.isDestroyed()||r.send(`${n}:asr:result`,{result:e,isLast:t})},onError:e=>{r.isDestroyed()||r.send(`${n}:asr:error`,{message:e.message})},onClose:()=>{r.isDestroyed()||r.send(`${n}:asr:closed`)}},t),{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}),e.handle(`${n}:asr:sendAudio`,async(e,t)=>{let n=e.sender.id,r=o.get(n);if(!r||!r.client.connected)return{success:!1,error:`ASR 会话未启动`};try{let e=t instanceof ArrayBuffer?Buffer.from(t):Buffer.from(new Uint8Array(t));return r.client.sendAudio(e),{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}),e.handle(`${n}:asr:finish`,async e=>{let t=e.sender.id,n=o.get(t);if(!n||!n.client.connected)return{success:!1,error:`ASR 会话未启动`};try{return n.client.finish(),{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}),e.handle(`${n}:asr:stop`,async e=>{let t=e.sender.id,n=o.get(t);return n&&n.client.disconnect(),{success:!0}}),e.handle(`${n}:asr:status`,async e=>{let t=e.sender.id;return{connected:o.get(t)?.client.connected??!1}}),e.handle(`${n}:asr:warmup`,async(e,t)=>{let n=e.sender,r=n.id,i=s(r,n);if(i.client.connected)return{success:!0};try{return await i.client.connect({onConnected:()=>{},onResult:()=>{},onError:()=>{},onClose:()=>{}},t),i.client.disconnect(),{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}),{cleanup:()=>{for(let e of o.values())e.client.disconnect();o.clear()}}}async function O(e){let{dataDir:t,assetsDir:n,metadataStore:r,llm:i,maxIterations:a,maxDurationMs:o,maxToolCalls:s,maxTotalTokens:c,tools:l,preloadTools:u,asr:d,channelPrefix:p,cwd:m,defaultContext:h,sqliteFactory:g,beforeChat:_,dataEngine:v}=e,y=`${t}/data`,b=await f({tools:l,assetsDir:n??`${t}/assets`,metadataStore:r,preloadTools:u}),{agent:x,storage:S}=await P({llmConfig:i,maxIterations:a,maxDurationMs:o,maxToolCalls:s,maxTotalTokens:c,dataDir:y,tools:b.tools,cwd:m,channelPrefix:p,defaultContext:h,sqliteFactory:g,beforeChat:async e=>{let t=b.getEnabledSkillContents(),n=t.length>0?{...e,skillContents:[...t,...e.skillContents??[]]}:e;return _?_(n):n},dataEngine:v});return await b.bind(x),d&&D({...d,channelPrefix:p}),{agent:x,storage:S,assetManager:b}}function k(e,t){return e===`INVALID_PARAMS`?`validation`:e===`PERMISSION_DENIED`||e===`TOOL_NOT_ENABLED`?`permission`:e===`NETWORK_ERROR`?`network`:e===`NOT_FOUND`?`not_found`:e===`TIMEOUT`?`runtime`:t}function A(e){let{message:t,failureReason:n,code:r,category:i,retryable:a,suggestion:o,details:s,cause:c,stack:l}=e,u={...s??{},...c?{cause:c}:{},...l?{stack:l}:{}};return{status:`error`,failureReason:n,error:{message:t,...r?{code:r}:{},...i?{category:i}:{},...a===void 0?{}:{retryable:a},...o?{suggestion:o}:{},...Object.keys(u).length>0?{details:u}:{}}}}function j(e){if(typeof e!=`number`||!Number.isFinite(e))return;let t=Math.trunc(e);return t>0?t:void 0}function M(e){if(e)try{let t=JSON.parse(e);if(!t||typeof t!=`object`)return;let n={maxIterations:j(t.maxIterations),maxDurationMs:j(t.maxDurationMs),maxToolCalls:j(t.maxToolCalls),maxTotalTokens:j(t.maxTotalTokens)};return Object.values(n).some(e=>e!==void 0)?n:void 0}catch{return}}function N(e){return e instanceof Error?{name:e.name,message:e.message,stack:e.stack}:e}async function P(f){let{llmConfig:h,cwd:g=process.cwd(),maxIterations:_,maxDurationMs:v,maxToolCalls:y,maxTotalTokens:b,channelPrefix:x=`ai-chat`,dataDir:S,defaultContext:C={},sqliteFactory:w,tools:T,mcpServers:E,dataEngine:D,resolveAtFileContext:O}=f,j=r.join(S,`db.sqlite`),P=new Map,F=async e=>{let t=global.currentApprovalContext,n=t?.auditContext;f.audit?.recordToolApprovalRequest?.({id:e.id,name:e.name,toolName:e.toolName??null,extensionId:e.extensionId??null,displayName:e.displayName??null,args:e.args,context:n});let r=t?.webContents;return r?new Promise((t,i)=>{P.set(e.id,{resolve:t,reject:i,webContents:r,name:e.name,toolName:e.toolName,extensionId:e.extensionId,displayName:e.displayName,auditContext:n}),r.send(`${x}:toolApprovalRequest`,{id:e.id,name:e.name,args:e.args})}):(f.audit?.recordToolApprovalResponse?.({id:e.id,name:e.name,toolName:e.toolName??null,extensionId:e.extensionId??null,displayName:e.displayName??null,approved:!1,context:n}),!1)},I=await m({type:`sqlite`,sqlitePath:j,sqliteFactory:w}),L=()=>C,R=async()=>{try{let e=await I.getUserSetting(`autoRunConfig`,L());if(e)return JSON.parse(e)}catch{}},z=async()=>{try{return M(await I.getUserSetting(`agentExecutionBudget`,L()))}catch{return}},B=new i({llmConfig:h,cwd:g,maxIterations:_,maxDurationMs:v,maxToolCalls:y,maxTotalTokens:b,tools:T,mcpServers:E,onToolApprovalRequest:F,getAutoRunConfig:R,dataEngine:D,audit:f.audit},p(g));e.on(`${x}:debugLog`,(e,t)=>{Array.isArray(t?.args)&&t.args.map(N),`${t?.module||`unknown`}`,t?.level}),e.handle(`${x}:models`,()=>o(h)),e.handle(`${x}:getAllTools`,async()=>(await B.ensureInitialized(),B.getAllTools())),e.handle(`${x}:getToolDefinitions`,async()=>(await B.ensureInitialized(),Array.from(B.getToolRuntimeManager().getSessionTools().values()).map(e=>({name:e.name,description:e.description,parameters:{type:`object`,properties:e.parameters?.properties||{},required:e.parameters?.required||[]}})))),e.handle(`${x}:executeTool`,async(e,t)=>{let{name:n,args:r,toolCallId:i,sessionId:a}=t;await B.ensureInitialized();try{let e=()=>B.executeTool(n,r,void 0,{toolCallId:i??`client-tool-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`,toolName:n}),t=f.audit?await f.audit.runWithContext({conversationId:a??null},e):await e(),o=B.getToolRuntimeManager().getSessionTools().get(n),s=await l(o,r),p,m;typeof t==`string`?m=t:t&&typeof t==`object`&&`result`in t?m=String(t.result??``):(p=c(t),m=JSON.stringify(d(p)));let h=p?u(p,o?.ui):o?.ui;return{success:!0,result:m,...h?{ui:h}:{},...s.approvalPolicy?{approvalPolicy:s.approvalPolicy}:{},...s.sideEffectLevel?{sideEffectLevel:s.sideEffectLevel}:{},...s.hostDependency?{hostDependency:s.hostDependency}:{},...s.riskSummary?{riskSummary:s.riskSummary}:{},...s.riskTags?.length?{riskTags:s.riskTags}:{},...s.riskSignals?.length?{riskSignals:s.riskSignals}:{}}}catch(e){let t=e instanceof Error?e.message:String(e),n=s(e)?e.toolError:void 0,r=n?.code===`TIMEOUT`?`timeout`:`execution_error`,i={success:!1,result:JSON.stringify(A({message:n?.message??t,failureReason:r,code:n?.code,category:n?.category??k(n?.code,`runtime`),retryable:n?.retryable,suggestion:n?.suggestion,details:n?.details,cause:e instanceof Error&&e.cause?String(e.cause):void 0,stack:e instanceof Error?e.stack:void 0})),error:n?.message??t};return n?{...i,toolError:n}:i}}),e.handle(`${x}:send`,async(e,t)=>{let n=e.sender,{message:r,images:i,sessionId:a}=t,o=t.options||{};f.beforeChat&&(o=await f.beforeChat(o)||o);let s=await z();s&&(o={...s,...o});let c=f.audit?.startTurn({conversationId:a??null,model:o.model??null,mode:o.mode??null,userMessage:r,metadata:{imageCount:i?.length??0}});global.currentApprovalContext={webContents:n,auditContext:{conversationId:a??null,turnId:c??null}};let l=0,u=async()=>{for await(let e of B.chat(r,o,i))l+=1,n.isDestroyed()||n.send(`${x}:progress`,{...e,sessionId:a})};try{f.audit?await f.audit.runWithContext({conversationId:a??null,turnId:c??null},u):await u(),c&&f.audit?.finishTurn({turnId:c,eventCount:l})}catch(e){if(c&&f.audit?.failTurn({turnId:c,eventCount:l,error:e}),e instanceof Error,!n.isDestroyed()){let t=e instanceof Error?{category:`api`,message:e.message||String(e),cause:e.stack}:{category:`api`,message:String(e)};n.send(`${x}:progress`,{type:`error`,data:t,sessionId:a})}}finally{delete global.currentApprovalContext}}),e.handle(`${x}:toolApprovalResponse`,(e,t)=>{let{id:n,approved:r}=t,i=P.get(n);i&&(P.delete(n),f.audit?.recordToolApprovalResponse?.({id:n,name:i.name,toolName:i.toolName??null,extensionId:i.extensionId??null,displayName:i.displayName??null,approved:r,context:i.auditContext}),i.resolve(r))}),e.handle(`${x}:cancel`,()=>{B.abort()}),e.handle(`${x}:settings:get`,async(e,t)=>I.getUserSetting(t,L())),e.handle(`${x}:settings:set`,async(e,t,n)=>(await I.setUserSetting(t,n,L()),{success:!0})),e.handle(`${x}:settings:getAll`,async()=>I.getUserSettings(L())),e.handle(`${x}:settings:delete`,async(e,t)=>(await I.deleteUserSetting(t,L()),{success:!0})),e.handle(`${x}:setCwd`,(e,t)=>{B.setCwd(t)}),e.handle(`${x}:config`,async()=>{let[e,t]=await Promise.all([R(),z()]);return{...B.getConfig(),currentAutoRunConfig:e,currentExecutionBudget:t}}),e.handle(`${x}:sessions:list`,async()=>I.getSessions(L())),e.handle(`${x}:sessions:get`,async(e,t)=>I.getSession(t,L())),e.handle(`${x}:sessions:create`,async(e,t)=>{let n={id:t.id||crypto.randomUUID(),title:t.title||`新对话`,model:t.model||a,mode:t.mode||`agent`,webSearchEnabled:t.webSearchEnabled??!0,thinkingEnabled:t.thinkingEnabled??!0,hidden:t.hidden??!1};return I.createSession(n,L())}),e.handle(`${x}:sessions:update`,async(e,t,n)=>(await I.updateSession(t,n,L()),I.getSession(t,L()))),e.handle(`${x}:sessions:delete`,async(e,t)=>(await I.deleteSession(t,L()),{success:!0})),e.handle(`${x}:messages:list`,async(e,t)=>I.getMessages(t,L())),e.handle(`${x}:messages:listPage`,async(e,t,n)=>I.getMessagesPage(t,n,L())),e.handle(`${x}:at:chats:search`,async(e,t,n)=>I.searchAtChats(t,n,L())),e.handle(`${x}:messages:save`,async(e,t)=>{let n=t.id||crypto.randomUUID(),r={id:n,clientId:n,sessionId:t.sessionId,role:t.role,content:t.content,atContextItems:t.atContextItems||null,images:t.images||[],model:t.model||null,mode:t.mode||null,webSearchEnabled:t.webSearchEnabled??null,thinkingEnabled:t.thinkingEnabled??null,parts:t.parts||[],operationIds:t.operationIds||null,finishReason:t.finishReason??null};return I.saveMessage(r,L())}),e.handle(`${x}:messages:update`,async(e,t)=>(await I.updateMessage(t.id,{content:t.content,atContextItems:t.atContextItems,parts:t.parts,usage:t.usage,duration:t.duration,finishReason:t.finishReason},L()),{success:!0})),e.handle(`${x}:messages:deleteAfter`,async(e,t,n)=>(await I.deleteMessagesAfter(t,new Date(n),L()),{success:!0})),e.handle(`${x}:messages:deleteAfterMessageId`,async(e,t,n)=>(await I.deleteMessagesAfterMessageId(t,n,L()),{success:!0})),e.handle(`${x}:operations:list`,async(e,t)=>I.getOperations(t,L())),e.handle(`${x}:trash:list`,async()=>I.getTrashItems?.(L())||[]),e.handle(`${x}:trash:restore`,async(e,t)=>I.restoreFromTrash?.(t,L())),e.handle(`${x}:openExternal`,async(e,n)=>t.openExternal(n)),e.handle(`${x}:fs:listDir`,async(e,t)=>{try{let e=n.readdirSync(t,{withFileTypes:!0}),i=[];for(let a of e){let e=r.join(t,a.name);try{let t=n.statSync(e);i.push({name:a.name,path:e,isDirectory:a.isDirectory(),size:t.size,modifiedAt:t.mtime,extension:a.isDirectory()?``:r.extname(a.name).toLowerCase()})}catch{}}return i.sort((e,t)=>e.isDirectory&&!t.isDirectory?-1:!e.isDirectory&&t.isDirectory?1:e.name.localeCompare(t.name))}catch{return[]}}),e.handle(`${x}:fs:exists`,async(e,t)=>n.existsSync(t)),e.handle(`${x}:fs:stat`,async(e,t)=>{try{let e=n.statSync(t);return{name:r.basename(t),path:t,isDirectory:e.isDirectory(),size:e.size,modifiedAt:e.mtime,extension:e.isDirectory()?``:r.extname(t).toLowerCase()}}catch{return null}}),e.handle(`${x}:fs:readFile`,async(e,t)=>{try{return n.readFileSync(t,`utf-8`)}catch{return null}}),e.handle(`${x}:fs:resolveAtFileContext`,async(e,t)=>{if(!O)return null;try{return await O(t)}catch{return null}}),e.handle(`${x}:fs:readFileBase64`,async(e,t)=>{try{return n.readFileSync(t).toString(`base64`)}catch{return null}}),e.handle(`${x}:fs:homeDir`,async()=>process.env.HOME||process.env.USERPROFILE||`/`),e.handle(`${x}:fs:resolvePath`,async(e,t)=>{if(t.startsWith(`~`)){let e=process.env.HOME||process.env.USERPROFILE||`/`;return r.join(e,t.slice(1))}return r.resolve(t)}),e.handle(`${x}:fs:parentDir`,async(e,t)=>r.dirname(t));let V=new Map;return e.handle(`${x}:fs:watchDir`,async(e,t)=>{let r=e.sender;V.has(t)&&(V.get(t)?.close(),V.delete(t));try{let e=n.watch(t,{persistent:!1},(e,n)=>{r.isDestroyed()||r.send(`${x}:fs:dirChange`,{dirPath:t,eventType:e,filename:n})});return e.on(`error`,e=>{V.delete(t)}),V.set(t,e),!0}catch{return!1}}),e.handle(`${x}:fs:unwatchDir`,async(e,t)=>{let n=V.get(t);n&&(n.close(),V.delete(t))}),{agent:B,storage:I}}export{o as buildModelOptions,D as createAsrBridge,P as createElectronBridge,O as createElectronChat};
|
|
1
|
+
import{ipcMain as e,shell as t}from"electron";import*as n from"fs";import*as r from"path";import{ChatRuntime as i,DEFAULT_MODEL as a,buildModelOptions as o,isToolError as s,normalizeToolResult as c,resolveToolGovernanceSnapshot as l,resolveToolResultUi as u,serializeToolResult as d}from"@huyooo/ai-chat-core";import{assetManager as f,createDefaultToolExecutor as p}from"@huyooo/ai-chat-host-node";import{createStorage as m}from"@huyooo/ai-chat-storage";import*as h from"zlib";import g from"ws";import{v4 as _}from"uuid";const v={FULL_CLIENT_REQUEST:1,AUDIO_ONLY_REQUEST:2,FULL_SERVER_RESPONSE:9,ERROR_RESPONSE:15},y={NONE:0,HAS_SEQUENCE:1,LAST_PACKET:2,LAST_PACKET_WITH_SEQUENCE:3},b={NONE:0,JSON:1},x={NONE:0,GZIP:1};function S(e,t,n,r){return[17,e<<4|t,n<<4|r,0]}function C(e,t=!0){let n=S(v.FULL_CLIENT_REQUEST,y.NONE,b.JSON,t?x.GZIP:x.NONE),r=JSON.stringify(e),i=Buffer.from(r,`utf-8`),a=t?h.gzipSync(i):i,o=Buffer.alloc(4);return o.writeUInt32BE(a.length,0),Buffer.concat([Buffer.from(n),o,a])}function w(e,t=!1,n=!0){let r=S(v.AUDIO_ONLY_REQUEST,t?y.LAST_PACKET:y.NONE,b.NONE,n?x.GZIP:x.NONE),i=n?h.gzipSync(e):e,a=Buffer.alloc(4);return a.writeUInt32BE(i.length,0),Buffer.concat([Buffer.from(r),a,i])}function T(e){if(e.length<4)throw Error(`Invalid response: too short`);let t=e[0],n=e[1],r=e[2],i=t>>4&15,a=(t&15)*4,o=n>>4&15,s=n&15,c=r>>4&15,l=r&15;if(i!==1)throw Error(`Unsupported protocol version: ${i}`);let u=a,d;(s&1)==1&&e.length>=u+4&&(d=e.readUInt32BE(u),u+=4);let f=(s&2)==2;if(o===v.ERROR_RESPONSE){let t=e.readUInt32BE(u);u+=4;let n=e.readUInt32BE(u);u+=4;let r=e.slice(u,u+n).toString(`utf-8`);return{type:`error`,sequence:d,isLast:!0,data:{code:t,message:r}}}if(o===v.FULL_SERVER_RESPONSE){let t=e.readUInt32BE(u);if(u+=4,t===0)return{type:`result`,sequence:d,isLast:f,data:{}};let n=e.slice(u,u+t);if(l===x.GZIP)try{n=h.gunzipSync(n)}catch(e){throw e}let r;if(c===b.JSON){let e=n.toString(`utf-8`);try{r=JSON.parse(e)}catch(e){throw e}}else r={};return{type:`result`,sequence:d,isLast:f,data:r}}throw Error(`Unknown message type: ${o}`)}var E=class{config;ws=null;callbacks={};connectId=``;isConnected=!1;sessionConfig={};constructor(e){this.config=e}getWsUrl(){return this.config.useAsyncMode===!1?`wss://openspeech.bytedance.com/api/v3/sauc/bigmodel`:`wss://openspeech.bytedance.com/api/v3/sauc/bigmodel_async`}getResourceId(){return this.config.resourceId||`volc.bigasr.sauc.duration`}connect(e,t={}){return new Promise((n,r)=>{this.callbacks=e,this.sessionConfig=t,this.connectId=_(),this.ws=new g(this.getWsUrl(),{headers:{"X-Api-App-Key":this.config.appId,"X-Api-Access-Key":this.config.accessKey,"X-Api-Resource-Id":this.getResourceId(),"X-Api-Connect-Id":this.connectId}}),this.ws.on(`open`,()=>{this.isConnected=!0,this.sendFullClientRequest(),this.callbacks.onConnected?.(),n()}),this.ws.on(`message`,e=>{try{let t=T(e);if(t.type===`error`){let e=t.data;this.callbacks.onError?.(Error(`ASR Error ${e.code}: ${e.message}`))}else{let e=t.data;this.callbacks.onResult?.(e,t.isLast),t.isLast}}catch(e){this.callbacks.onError?.(e instanceof Error?e:Error(String(e)))}}),this.ws.on(`error`,e=>{this.callbacks.onError?.(e),r(e)}),this.ws.on(`close`,()=>{this.isConnected=!1,this.ws=null,this.callbacks.onClose?.()})})}sendFullClientRequest(){if(!this.ws||this.ws.readyState!==g.OPEN)return;let e=C({user:{uid:`ai-chat-user`},audio:{format:this.sessionConfig.format||`pcm`,rate:this.sessionConfig.sampleRate||16e3,bits:16,channel:1},request:{model_name:`bigmodel`,enable_itn:this.sessionConfig.enableItn??!0,enable_punc:this.sessionConfig.enablePunc??!0,enable_ddc:this.sessionConfig.enableDdc??!1,show_utterances:this.sessionConfig.showUtterances??!0,result_type:`full`}});this.ws.send(e)}sendAudio(e){if(!this.ws||this.ws.readyState!==g.OPEN)return;let t=w(e,!1);this.ws.send(t)}finish(){if(!this.ws||this.ws.readyState!==g.OPEN)return;let e=w(Buffer.alloc(0),!0);this.ws.send(e)}disconnect(){this.ws&&=(this.ws.close(),null),this.isConnected=!1}get connected(){return this.isConnected&&this.ws?.readyState===g.OPEN}};function D(t){let{channelPrefix:n=`ai-chat`,appId:r,accessKey:i,resourceId:a}=t,o=new Map;function s(e,t){let n=o.get(e);return n||(n={client:new E({appId:r,accessKey:i,resourceId:a,useAsyncMode:!0}),webContents:t},o.set(e,n),t.on(`destroyed`,()=>{let t=o.get(e);t&&(t.client.disconnect(),o.delete(e))})),n}return e.handle(`${n}:asr:start`,async(e,t)=>{let r=e.sender,i=r.id,a=s(i,r);a.client.connected&&a.client.disconnect();try{return await a.client.connect({onConnected:()=>{r.isDestroyed()||r.send(`${n}:asr:connected`)},onResult:(e,t)=>{r.isDestroyed()||r.send(`${n}:asr:result`,{result:e,isLast:t})},onError:e=>{r.isDestroyed()||r.send(`${n}:asr:error`,{message:e.message})},onClose:()=>{r.isDestroyed()||r.send(`${n}:asr:closed`)}},t),{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}),e.handle(`${n}:asr:sendAudio`,async(e,t)=>{let n=e.sender.id,r=o.get(n);if(!r||!r.client.connected)return{success:!1,error:`ASR 会话未启动`};try{let e=t instanceof ArrayBuffer?Buffer.from(t):Buffer.from(new Uint8Array(t));return r.client.sendAudio(e),{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}),e.handle(`${n}:asr:finish`,async e=>{let t=e.sender.id,n=o.get(t);if(!n||!n.client.connected)return{success:!1,error:`ASR 会话未启动`};try{return n.client.finish(),{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}),e.handle(`${n}:asr:stop`,async e=>{let t=e.sender.id,n=o.get(t);return n&&n.client.disconnect(),{success:!0}}),e.handle(`${n}:asr:status`,async e=>{let t=e.sender.id;return{connected:o.get(t)?.client.connected??!1}}),e.handle(`${n}:asr:warmup`,async(e,t)=>{let n=e.sender,r=n.id,i=s(r,n);if(i.client.connected)return{success:!0};try{return await i.client.connect({onConnected:()=>{},onResult:()=>{},onError:()=>{},onClose:()=>{}},t),i.client.disconnect(),{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}),{cleanup:()=>{for(let e of o.values())e.client.disconnect();o.clear()}}}async function O(e){let{dataDir:t,assetsDir:n,metadataStore:r,llm:i,maxIterations:a,maxDurationMs:o,maxToolCalls:s,maxTotalTokens:c,tools:l,preloadTools:u,asr:d,channelPrefix:p,cwd:m,defaultContext:h,sqliteFactory:g,beforeChat:_,dataEngine:v}=e,y=`${t}/data`,b=await f({tools:l,assetsDir:n??`${t}/assets`,metadataStore:r,preloadTools:u}),{agent:x,storage:S}=await P({llmConfig:i,maxIterations:a,maxDurationMs:o,maxToolCalls:s,maxTotalTokens:c,dataDir:y,tools:b.tools,cwd:m,channelPrefix:p,defaultContext:h,sqliteFactory:g,beforeChat:async e=>{let t=b.getEnabledSkillContents(),n=t.length>0?{...e,skillContents:[...t,...e.skillContents??[]]}:e;return _?_(n):n},dataEngine:v});return await b.bind(x),d&&D({...d,channelPrefix:p}),{agent:x,storage:S,assetManager:b}}function k(e,t){return e===`INVALID_PARAMS`?`validation`:e===`PERMISSION_DENIED`||e===`TOOL_NOT_ENABLED`?`permission`:e===`NETWORK_ERROR`?`network`:e===`NOT_FOUND`?`not_found`:e===`TIMEOUT`?`runtime`:t}function A(e){let{message:t,failureReason:n,code:r,category:i,retryable:a,suggestion:o,details:s,cause:c,stack:l}=e,u={...s??{},...c?{cause:c}:{},...l?{stack:l}:{}};return{status:`error`,failureReason:n,error:{message:t,...r?{code:r}:{},...i?{category:i}:{},...a===void 0?{}:{retryable:a},...o?{suggestion:o}:{},...Object.keys(u).length>0?{details:u}:{}}}}function j(e){if(typeof e!=`number`||!Number.isFinite(e))return;let t=Math.trunc(e);return t>0?t:void 0}function M(e){if(e)try{let t=JSON.parse(e);if(!t||typeof t!=`object`)return;let n={maxIterations:j(t.maxIterations),maxDurationMs:j(t.maxDurationMs),maxToolCalls:j(t.maxToolCalls),maxTotalTokens:j(t.maxTotalTokens)};return Object.values(n).some(e=>e!==void 0)?n:void 0}catch{return}}function N(e){return e instanceof Error?{name:e.name,message:e.message,stack:e.stack}:e}async function P(f){let{llmConfig:h,cwd:g=process.cwd(),maxIterations:_,maxDurationMs:v,maxToolCalls:y,maxTotalTokens:b,channelPrefix:x=`ai-chat`,dataDir:S,defaultContext:C={},sqliteFactory:w,tools:T,mcpServers:E,dataEngine:D,resolveAtFileContext:O}=f,j=r.join(S,`db.sqlite`),P=new Map,F=async e=>{let t=global.currentApprovalContext,n=t?.auditContext;f.audit?.recordToolApprovalRequest?.({id:e.id,name:e.name,toolName:e.toolName??null,extensionId:e.extensionId??null,displayName:e.displayName??null,args:e.args,context:n});let r=t?.webContents;return r?new Promise((t,i)=>{P.set(e.id,{resolve:t,reject:i,webContents:r,name:e.name,toolName:e.toolName,extensionId:e.extensionId,displayName:e.displayName,auditContext:n}),r.send(`${x}:toolApprovalRequest`,{id:e.id,name:e.name,args:e.args})}):(f.audit?.recordToolApprovalResponse?.({id:e.id,name:e.name,toolName:e.toolName??null,extensionId:e.extensionId??null,displayName:e.displayName??null,approved:!1,context:n}),!1)},I=await m({type:`sqlite`,sqlitePath:j,sqliteFactory:w}),L=()=>C,R=async()=>{try{let e=await I.getUserSetting(`autoRunConfig`,L());if(e)return JSON.parse(e)}catch{}},z=async()=>{try{return M(await I.getUserSetting(`agentExecutionBudget`,L()))}catch{return}},B=new i({llmConfig:h,cwd:g,maxIterations:_,maxDurationMs:v,maxToolCalls:y,maxTotalTokens:b,tools:T,mcpServers:E,onToolApprovalRequest:F,getAutoRunConfig:R,dataEngine:D,audit:f.audit},p(g));e.on(`${x}:debugLog`,(e,t)=>{Array.isArray(t?.args)&&t.args.map(N),`${t?.module||`unknown`}`,t?.level}),e.handle(`${x}:models`,()=>o(h)),e.handle(`${x}:getAllTools`,async()=>(await B.ensureInitialized(),B.getAllTools())),e.handle(`${x}:getToolDefinitions`,async()=>(await B.ensureInitialized(),Array.from(B.getToolRuntimeManager().getSessionTools().values()).map(e=>({name:e.name,description:e.description,parameters:{type:`object`,properties:e.parameters?.properties||{},required:e.parameters?.required||[]}})))),e.handle(`${x}:executeTool`,async(e,t)=>{let{name:n,args:r,toolCallId:i,sessionId:a}=t;await B.ensureInitialized();try{let e=()=>B.executeTool(n,r,void 0,{toolCallId:i??`client-tool-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`,toolName:n}),t=f.audit?await f.audit.runWithContext({conversationId:a??null},e):await e(),o=B.getToolRuntimeManager().getSessionTools().get(n),s=await l(o,r),p,m;typeof t==`string`?m=t:t&&typeof t==`object`&&`result`in t?m=String(t.result??``):(p=c(t),m=JSON.stringify(d(p)));let h=p?u(p,o?.ui):o?.ui;return{success:!0,result:m,...h?{ui:h}:{},...s.approvalPolicy?{approvalPolicy:s.approvalPolicy}:{},...s.sideEffectLevel?{sideEffectLevel:s.sideEffectLevel}:{},...s.hostDependency?{hostDependency:s.hostDependency}:{},...s.riskSummary?{riskSummary:s.riskSummary}:{},...s.riskTags?.length?{riskTags:s.riskTags}:{},...s.riskSignals?.length?{riskSignals:s.riskSignals}:{}}}catch(e){let t=e instanceof Error?e.message:String(e),n=s(e)?e.toolError:void 0,r=n?.code===`TIMEOUT`?`timeout`:`execution_error`,i={success:!1,result:JSON.stringify(A({message:n?.message??t,failureReason:r,code:n?.code,category:n?.category??k(n?.code,`runtime`),retryable:n?.retryable,suggestion:n?.suggestion,details:n?.details,cause:e instanceof Error&&e.cause?String(e.cause):void 0,stack:e instanceof Error?e.stack:void 0})),error:n?.message??t};return n?{...i,toolError:n}:i}}),e.handle(`${x}:send`,async(e,t)=>{let n=e.sender,{message:r,images:i,sessionId:a}=t,o=t.options||{};f.beforeChat&&(o=await f.beforeChat(o)||o);let s=await z();s&&(o={...s,...o});let c=f.audit?.startTurn({conversationId:a??null,model:o.model??null,mode:o.mode??null,userMessage:r,metadata:{imageCount:i?.length??0}});global.currentApprovalContext={webContents:n,auditContext:{conversationId:a??null,turnId:c??null}};let l=0,u=async()=>{for await(let e of B.chat(r,o,i))l+=1,n.isDestroyed()||n.send(`${x}:progress`,{...e,sessionId:a})};try{f.audit?await f.audit.runWithContext({conversationId:a??null,turnId:c??null},u):await u(),c&&f.audit?.finishTurn({turnId:c,eventCount:l})}catch(e){if(c&&f.audit?.failTurn({turnId:c,eventCount:l,error:e}),e instanceof Error,!n.isDestroyed()){let t=e instanceof Error?{category:`api`,message:e.message||String(e),cause:e.stack}:{category:`api`,message:String(e)};n.send(`${x}:progress`,{type:`error`,data:t,sessionId:a})}}finally{delete global.currentApprovalContext}}),e.handle(`${x}:toolApprovalResponse`,(e,t)=>{let{id:n,approved:r}=t,i=P.get(n);i&&(P.delete(n),f.audit?.recordToolApprovalResponse?.({id:n,name:i.name,toolName:i.toolName??null,extensionId:i.extensionId??null,displayName:i.displayName??null,approved:r,context:i.auditContext}),i.resolve(r))}),e.handle(`${x}:cancel`,()=>{B.abort()}),e.handle(`${x}:settings:get`,async(e,t)=>I.getUserSetting(t,L())),e.handle(`${x}:settings:set`,async(e,t,n)=>(await I.setUserSetting(t,n,L()),{success:!0})),e.handle(`${x}:settings:getAll`,async()=>I.getUserSettings(L())),e.handle(`${x}:settings:delete`,async(e,t)=>(await I.deleteUserSetting(t,L()),{success:!0})),e.handle(`${x}:setCwd`,(e,t)=>{B.setCwd(t)}),e.handle(`${x}:config`,async()=>{let[e,t]=await Promise.all([R(),z()]);return{...B.getConfig(),currentAutoRunConfig:e,currentExecutionBudget:t}}),e.handle(`${x}:sessions:list`,async()=>I.getSessions(L())),e.handle(`${x}:sessions:get`,async(e,t)=>I.getSession(t,L())),e.handle(`${x}:sessions:create`,async(e,t)=>{let n={id:t.id||crypto.randomUUID(),title:t.title||`新对话`,model:t.model||a,mode:t.mode||`agent`,webSearchEnabled:t.webSearchEnabled??!0,thinkingEnabled:t.thinkingEnabled??!0,hidden:t.hidden??!1};return I.createSession(n,L())}),e.handle(`${x}:sessions:update`,async(e,t,n)=>(await I.updateSession(t,n,L()),I.getSession(t,L()))),e.handle(`${x}:sessions:delete`,async(e,t)=>(await I.deleteSession(t,L()),{success:!0})),e.handle(`${x}:messages:list`,async(e,t)=>I.getMessages(t,L())),e.handle(`${x}:messages:listPage`,async(e,t,n)=>I.getMessagesPage(t,n,L())),e.handle(`${x}:at:chats:search`,async(e,t,n)=>I.searchAtChats(t,n,L())),e.handle(`${x}:messages:save`,async(e,t)=>{let n=t.id||crypto.randomUUID(),r={id:n,clientId:n,sessionId:t.sessionId,role:t.role,content:t.content,atContextItems:t.atContextItems||null,images:t.images||[],model:t.model||null,modelDisplayName:t.modelDisplayName||null,mode:t.mode||null,webSearchEnabled:t.webSearchEnabled??null,thinkingEnabled:t.thinkingEnabled??null,parts:t.parts||[],operationIds:t.operationIds||null,finishReason:t.finishReason??null};return I.saveMessage(r,L())}),e.handle(`${x}:messages:update`,async(e,t)=>(await I.updateMessage(t.id,{content:t.content,atContextItems:t.atContextItems,parts:t.parts,usage:t.usage,duration:t.duration,finishReason:t.finishReason},L()),{success:!0})),e.handle(`${x}:messages:deleteAfter`,async(e,t,n)=>(await I.deleteMessagesAfter(t,new Date(n),L()),{success:!0})),e.handle(`${x}:messages:deleteAfterMessageId`,async(e,t,n)=>(await I.deleteMessagesAfterMessageId(t,n,L()),{success:!0})),e.handle(`${x}:operations:list`,async(e,t)=>I.getOperations(t,L())),e.handle(`${x}:trash:list`,async()=>I.getTrashItems?.(L())||[]),e.handle(`${x}:trash:restore`,async(e,t)=>I.restoreFromTrash?.(t,L())),e.handle(`${x}:openExternal`,async(e,n)=>t.openExternal(n)),e.handle(`${x}:fs:listDir`,async(e,t)=>{try{let e=n.readdirSync(t,{withFileTypes:!0}),i=[];for(let a of e){let e=r.join(t,a.name);try{let t=n.statSync(e);i.push({name:a.name,path:e,isDirectory:a.isDirectory(),size:t.size,modifiedAt:t.mtime,extension:a.isDirectory()?``:r.extname(a.name).toLowerCase()})}catch{}}return i.sort((e,t)=>e.isDirectory&&!t.isDirectory?-1:!e.isDirectory&&t.isDirectory?1:e.name.localeCompare(t.name))}catch{return[]}}),e.handle(`${x}:fs:exists`,async(e,t)=>n.existsSync(t)),e.handle(`${x}:fs:stat`,async(e,t)=>{try{let e=n.statSync(t);return{name:r.basename(t),path:t,isDirectory:e.isDirectory(),size:e.size,modifiedAt:e.mtime,extension:e.isDirectory()?``:r.extname(t).toLowerCase()}}catch{return null}}),e.handle(`${x}:fs:readFile`,async(e,t)=>{try{return n.readFileSync(t,`utf-8`)}catch{return null}}),e.handle(`${x}:fs:resolveAtFileContext`,async(e,t)=>{if(!O)return null;try{return await O(t)}catch{return null}}),e.handle(`${x}:fs:readFileBase64`,async(e,t)=>{try{return n.readFileSync(t).toString(`base64`)}catch{return null}}),e.handle(`${x}:fs:homeDir`,async()=>process.env.HOME||process.env.USERPROFILE||`/`),e.handle(`${x}:fs:resolvePath`,async(e,t)=>{if(t.startsWith(`~`)){let e=process.env.HOME||process.env.USERPROFILE||`/`;return r.join(e,t.slice(1))}return r.resolve(t)}),e.handle(`${x}:fs:parentDir`,async(e,t)=>r.dirname(t));let V=new Map;return e.handle(`${x}:fs:watchDir`,async(e,t)=>{let r=e.sender;V.has(t)&&(V.get(t)?.close(),V.delete(t));try{let e=n.watch(t,{persistent:!1},(e,n)=>{r.isDestroyed()||r.send(`${x}:fs:dirChange`,{dirPath:t,eventType:e,filename:n})});return e.on(`error`,e=>{V.delete(t)}),V.set(t,e),!0}catch{return!1}}),e.handle(`${x}:fs:unwatchDir`,async(e,t)=>{let n=V.get(t);n&&(n.close(),V.delete(t))}),{agent:B,storage:I}}export{o as buildModelOptions,D as createAsrBridge,P as createElectronBridge,O as createElectronChat};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huyooo/ai-chat-bridge-electron",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.22",
|
|
4
4
|
"description": "AI Chat Electron Bridge - IPC integration for Electron apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/main/index.js",
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"clean": "rm -rf dist tsconfig.tsbuildinfo"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@huyooo/ai-chat-core": "^0.3.
|
|
36
|
-
"@huyooo/ai-chat-host-node": "^0.3.
|
|
37
|
-
"@huyooo/ai-chat-storage": "^0.3.
|
|
38
|
-
"@huyooo/ai-chat-tools-node": "^0.3.
|
|
39
|
-
"@huyooo/ai-chat-types": "^0.3.
|
|
35
|
+
"@huyooo/ai-chat-core": "^0.3.22",
|
|
36
|
+
"@huyooo/ai-chat-host-node": "^0.3.22",
|
|
37
|
+
"@huyooo/ai-chat-storage": "^0.3.22",
|
|
38
|
+
"@huyooo/ai-chat-tools-node": "^0.3.22",
|
|
39
|
+
"@huyooo/ai-chat-types": "^0.3.22",
|
|
40
40
|
"uuid": "^11.1.0",
|
|
41
41
|
"ws": "^8.18.3"
|
|
42
42
|
},
|