|
|
@@ -8,6 +8,7 @@ import type {LocalAuthStore} from './localAuth.js'
|
|
|
import type {WorkspaceNodes,GatewayTarget} from './workspaceGateway.js'
|
|
|
import {allowsHostEnvironment,type WorkspaceAgent} from '../shared/workspace.js'
|
|
|
import {readHostTools} from './hostToolSettings.js'
|
|
|
+import {readComputerNames} from './computerNames.js'
|
|
|
import type {ComputerFrame,ComputerControlStatus} from '../shared/computerControl.js'
|
|
|
import {requireTeamToolBridge} from './workspaceToolLease.js'
|
|
|
|
|
|
@@ -84,6 +85,8 @@ export class DesktopEnvironments {
|
|
|
/** 本机 is the computer running YaoYao. A non-local binding still wins for the
|
|
|
* default session. Other script machines are selected by name, never by being the only one online. */
|
|
|
private computers(){try{const s=readHostTools(this.store.home);return {script:s.scriptMachine,server:s.serverComputer}}catch{return {script:false,server:false}}}
|
|
|
+ private names(){try{return readComputerNames(this.store.home)}catch{return {}}}
|
|
|
+ private label(s:HostState){const custom=this.names()[s.key];if(s.key==='local')return custom||'本机';return custom||s.info?.name||''}
|
|
|
private hostOpen(s:HostState){const g=this.computers();return s.key==='local'?g.server:g.script}
|
|
|
private sessionFor(agent:WorkspaceAgent):HostState|undefined{
|
|
|
const g=this.computers()
|
|
|
@@ -103,7 +106,7 @@ export class DesktopEnvironments {
|
|
|
throw new HttpError(409,'夭夭所在的电脑未连接,请在这台电脑上打开夭夭。','desktop_offline')
|
|
|
}
|
|
|
if(!g.script)throw new HttpError(409,'全局设置未开放脚本机操作。','desktop_offline')
|
|
|
- for(const s of this.sessions.values())if(s.key===host||(s.info&&s.info.name.toLowerCase()===host.toLowerCase()))return s
|
|
|
+ const names=this.names();for(const s of this.sessions.values())if(s.key===host||(s.info&&s.info.name.toLowerCase()===host.toLowerCase())||(names[s.key]&&names[s.key].toLowerCase()===host.toLowerCase()))return s
|
|
|
throw new HttpError(409,`指定的电脑不在线:${host.slice(0,64)}`,'desktop_host_unknown')
|
|
|
}
|
|
|
selected(owner:string,agent:WorkspaceAgent):Mode|undefined {if(agent.archived||agent.remoteAgentId||agent.temporaryGoalId)return undefined;const g=this.computers();if(!g.script&&!g.server)return undefined;if(agent.envs)return 'local';if(agent.computer==='local')return 'local';if(agent.computer==='auto'&&agent.execution!=='computer'){const s=this.sessionFor(agent);if(s&&this.ready(s,owner))return 'local'}return undefined}
|
|
|
@@ -130,11 +133,11 @@ export class DesktopEnvironments {
|
|
|
if(!s)throw new HttpError(409,'桌面端未连接,请在电脑上打开夭夭。','desktop_offline')
|
|
|
return s
|
|
|
}
|
|
|
- onlineHostsLine(owner:string){const names=[...this.sessions.values()].filter(s=>this.hostOnline(s)&&this.hostOpen(s)).map(s=>s.key==='local'?'本机':(s.info?.name??s.key));return names.length?`当前在线电脑:${names.join('、')}。desktop_* 工具可用 host 参数(名称或编号)选择目标电脑;省略 host 或写「本机」时使用夭夭所在的这台电脑。`:''}
|
|
|
+ onlineHostsLine(owner:string){const names=[...this.sessions.values()].filter(s=>this.hostOnline(s)&&this.hostOpen(s)).map(s=>s.key==='local'?(this.names()[s.key]?this.names()[s.key]+'(本机)':'本机'):(this.label(s)||s.key));return names.length?`当前在线电脑:${names.join('、')}。desktop_* 工具可用 host 参数(名称或编号)选择目标电脑;省略 host 或写「本机」时使用夭夭所在的这台电脑。`:''}
|
|
|
private agent(owner:string,id:string){const a=this.store.require<WorkspaceAgent>(owner,'agent',id);this.nodes.requireSource(owner,a);if(a.archived||a.remoteAgentId||a.temporaryGoalId)throw new HttpError(409,'此机器人无法使用桌面环境','computer_unavailable');return a}
|
|
|
private resource(owner:string,agent:WorkspaceAgent,mode:Mode){return mode==='local'?'local':digest(this.ownerKey(owner)+':'+agent.id+':'+(agent.browserProfile??'persistent'))}
|
|
|
- hostStates(owner:string){return [...this.sessions.values()].map(s=>{const online=this.hostOnline(s),info=online?s.info!:undefined;return {id:s.key,name:info?.name??'',platform:info?.platform??'',online,local:{supported:info?.platform==='darwin',authorized:!!info&&info.approved.includes(this.ownerKey(owner)),screen:info?.screen===true,accessibility:info?.accessibility===true,ready:!!info&&this.ready(s,owner),fullAuthorized:!!info&&(info.full??[]).includes(this.ownerKey(owner))},browser:{available:online}}}).sort((a,b)=>a.id==='local'?-1:b.id==='local'?1:a.name.localeCompare(b.name))}
|
|
|
- state(owner:string,agent:WorkspaceAgent){const mode=this.selected(owner,agent),s=this.sessionFor(agent),online=!!s&&this.hostOnline(s),info=online?s!.info:undefined;return {online,host:info?{id:s!.key,name:info.name,platform:info.platform}:null,local:{supported:info?.platform==='darwin',authorized:!!info&&info.approved.includes(this.ownerKey(owner)),screen:info?.screen===true,accessibility:info?.accessibility===true,ready:!!info&&this.ready(s!,owner),fullAuthorized:!!info&&(info.full??[]).includes(this.ownerKey(owner))},browser:{available:online,profile:agent.browserProfile??'persistent'},selected:mode??null,agent:this.store.agentSummary(agent),hosts:this.hostStates(owner)}}
|
|
|
+ hostStates(owner:string){return [...this.sessions.values()].map(s=>{const online=this.hostOnline(s),info=online?s.info!:undefined;return {id:s.key,name:this.label(s),platform:info?.platform??'',online,local:{supported:info?.platform==='darwin',authorized:!!info&&info.approved.includes(this.ownerKey(owner)),screen:info?.screen===true,accessibility:info?.accessibility===true,ready:!!info&&this.ready(s,owner),fullAuthorized:!!info&&(info.full??[]).includes(this.ownerKey(owner))},browser:{available:online}}}).sort((a,b)=>a.id==='local'?-1:b.id==='local'?1:a.name.localeCompare(b.name))}
|
|
|
+ state(owner:string,agent:WorkspaceAgent){const mode=this.selected(owner,agent),s=this.sessionFor(agent),online=!!s&&this.hostOnline(s),info=online?s!.info:undefined;return {online,host:info?{id:s!.key,name:this.label(s!),platform:info.platform}:null,local:{supported:info?.platform==='darwin',authorized:!!info&&info.approved.includes(this.ownerKey(owner)),screen:info?.screen===true,accessibility:info?.accessibility===true,ready:!!info&&this.ready(s!,owner),fullAuthorized:!!info&&(info.full??[]).includes(this.ownerKey(owner))},browser:{available:online,profile:agent.browserProfile??'persistent'},selected:mode??null,agent:this.store.agentSummary(agent),hosts:this.hostStates(owner)}}
|
|
|
/** Parallel envs allow each requested leg; legacy agents still use the single toolMode. */
|
|
|
private modeOpen(owner:string,agent:WorkspaceAgent,mode:Mode){if(agent.envs){const wanted=this.modes(owner,agent);return mode==='browser'?wanted.browser:wanted.desktop}return this.toolMode(owner,agent)===mode}
|
|
|
private check(s:HostState|undefined,owner:string,agentId:string,mode:Mode,epoch:string,version:number){const a=this.agent(owner,agentId);if(!s||!this.hostOnline(s)||s.info!.id!==epoch)throw new HttpError(409,'桌面端已断开,请在电脑上打开夭夭后重试。','desktop_offline');if(this.auth.pushAuthorizationVersion(owner)!==version||!this.modeOpen(owner,a,mode))throw new HttpError(410,'电脑授权已改变,请重新连接。','computer_control_expired');if(mode==='local'&&!this.ready(s,owner))throw new HttpError(403,'请在桌面端授权本机控制,并开启屏幕录制和辅助功能。','desktop_permission_required')}
|