@lsamien/yaovi 0.1.0 → 0.1.2

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.
Files changed (64) hide show
  1. package/build-info.json +44 -3
  2. package/desktop/main.mjs +18 -9
  3. package/dist/assets/{AboutDialog-BW25cB-W.js → AboutDialog-CJ9tCjSs.js} +2 -2
  4. package/dist/assets/AboutDialog-CJ9tCjSs.js.map +1 -0
  5. package/dist/assets/{BotAutomationsView-BoK2R918.js → BotAutomationsView-CsoI3js3.js} +2 -2
  6. package/dist/assets/{BotAutomationsView-BoK2R918.js.map → BotAutomationsView-CsoI3js3.js.map} +1 -1
  7. package/dist/assets/{BotControlSettingsPanel-BbhD2eC0.js → BotControlSettingsPanel-DIuBZlLu.js} +2 -2
  8. package/dist/assets/{BotControlSettingsPanel-BbhD2eC0.js.map → BotControlSettingsPanel-DIuBZlLu.js.map} +1 -1
  9. package/dist/assets/{BotPluginsDialog-DjwFw-lt.js → BotPluginsDialog-BsGszDMH.js} +2 -2
  10. package/dist/assets/{BotPluginsDialog-DjwFw-lt.js.map → BotPluginsDialog-BsGszDMH.js.map} +1 -1
  11. package/dist/assets/{BotSettingsPanel-DLmnL6pH.js → BotSettingsPanel-j3ThYgt2.js} +3 -3
  12. package/dist/assets/{BotSettingsPanel-DLmnL6pH.js.map → BotSettingsPanel-j3ThYgt2.js.map} +1 -1
  13. package/dist/assets/{ComputerPanel-CRIF1YEd.js → ComputerPanel-DdlgOm-B.js} +2 -2
  14. package/dist/assets/{ComputerPanel-CRIF1YEd.js.map → ComputerPanel-DdlgOm-B.js.map} +1 -1
  15. package/dist/assets/{ComputerViewerView-CizYWH2R.js → ComputerViewerView-I3adPI6O.js} +2 -2
  16. package/dist/assets/{ComputerViewerView-CizYWH2R.js.map → ComputerViewerView-I3adPI6O.js.map} +1 -1
  17. package/dist/assets/ConversationsView-CBhzg-9w.css +1 -0
  18. package/dist/assets/{ConversationsView-1igFR9VY.js → ConversationsView-CxVFk5sQ.js} +73 -73
  19. package/dist/assets/ConversationsView-CxVFk5sQ.js.map +1 -0
  20. package/dist/assets/{DevicePairingDialog-orOS4xqu.js → DevicePairingDialog-C307ARt-.js} +2 -2
  21. package/dist/assets/{DevicePairingDialog-orOS4xqu.js.map → DevicePairingDialog-C307ARt-.js.map} +1 -1
  22. package/dist/assets/{LocalVmSettingsPanel-DzM--Lpg.js → LocalVmSettingsPanel-Zs7CiXdf.js} +2 -2
  23. package/dist/assets/{LocalVmSettingsPanel-DzM--Lpg.js.map → LocalVmSettingsPanel-Zs7CiXdf.js.map} +1 -1
  24. package/dist/assets/{ProviderLoginDialog-Bw5KVMfi.js → ProviderLoginDialog-D3mqdAlo.js} +2 -2
  25. package/dist/assets/{ProviderLoginDialog-Bw5KVMfi.js.map → ProviderLoginDialog-D3mqdAlo.js.map} +1 -1
  26. package/dist/assets/{RunnerSettingsPanel-B-fwuBEs.js → RunnerSettingsPanel-DjMGnXGP.js} +2 -2
  27. package/dist/assets/{RunnerSettingsPanel-B-fwuBEs.js.map → RunnerSettingsPanel-DjMGnXGP.js.map} +1 -1
  28. package/dist/assets/{StandaloneDialog-7GkhtRlT.js → StandaloneDialog-BXCK22Dx.js} +2 -2
  29. package/dist/assets/{StandaloneDialog-7GkhtRlT.js.map → StandaloneDialog-BXCK22Dx.js.map} +1 -1
  30. package/dist/assets/{UpdateCheckDialog-DYiM4xBv.js → UpdateCheckDialog-DAY0VEQm.js} +2 -2
  31. package/dist/assets/{UpdateCheckDialog-DYiM4xBv.js.map → UpdateCheckDialog-DAY0VEQm.js.map} +1 -1
  32. package/dist/assets/{UsageView-D1cjS1hy.js → UsageView-DNwG8LAx.js} +2 -2
  33. package/dist/assets/{UsageView-D1cjS1hy.js.map → UsageView-DNwG8LAx.js.map} +1 -1
  34. package/dist/assets/{WorkspaceKnowledgePanel-DcGdLRS1.js → WorkspaceKnowledgePanel-hy_IkIJI.js} +2 -2
  35. package/dist/assets/{WorkspaceKnowledgePanel-DcGdLRS1.js.map → WorkspaceKnowledgePanel-hy_IkIJI.js.map} +1 -1
  36. package/dist/assets/{WorkspaceRoutinesPanel-DLntimKs.js → WorkspaceRoutinesPanel-B7PkBP7x.js} +2 -2
  37. package/dist/assets/{WorkspaceRoutinesPanel-DLntimKs.js.map → WorkspaceRoutinesPanel-B7PkBP7x.js.map} +1 -1
  38. package/dist/assets/{WorkstreamsView-DMoeV8Eu.js → WorkstreamsView-BeE2TRXK.js} +2 -2
  39. package/dist/assets/{WorkstreamsView-DMoeV8Eu.js.map → WorkstreamsView-BeE2TRXK.js.map} +1 -1
  40. package/dist/assets/{index-D0pMYnOi.js → index-D11cgXI9.js} +3 -3
  41. package/dist/assets/{index-D0pMYnOi.js.map → index-D11cgXI9.js.map} +1 -1
  42. package/dist/assets/{pdf-BCisgslg.js → pdf-BjTcV6As.js} +2 -2
  43. package/dist/assets/{pdf-BCisgslg.js.map → pdf-BjTcV6As.js.map} +1 -1
  44. package/dist/index.html +1 -1
  45. package/dist-server/server/botControl.js +13 -1
  46. package/dist-server/server/desktopEnvironments.js +10 -9
  47. package/dist-server/server/desktopHostProtocol.js +1 -1
  48. package/dist-server/server/harness/agentRuntime.js +22 -8
  49. package/dist-server/server/workspaceDialoguePolicy.js +3 -1
  50. package/dist-server/server/workspaceKnowledge.js +25 -16
  51. package/dist-server/server/workspaceMemoryContext.js +75 -19
  52. package/dist-server/server/workspacePrompt.js +11 -4
  53. package/dist-server/server/workspaceRuntime.js +35 -13
  54. package/dist-server/shared/botEnvironment.js +10 -4
  55. package/dist-server/shared/fileTransferEndpoint.mjs +5 -5
  56. package/dist-server/shared/hostPaths.mjs +14 -4
  57. package/package.json +4 -3
  58. package/release.json +3 -3
  59. package/runtime/hermes/hermes_state_messages.py +17 -0
  60. package/runtime/worker.py +34 -1
  61. package/dist/assets/AboutDialog-BW25cB-W.js.map +0 -1
  62. package/dist/assets/ConversationsView-1igFR9VY.js.map +0 -1
  63. package/dist/assets/ConversationsView-h94_iPtv.css +0 -1
  64. package/dist-server/server/harness/context.js +0 -88
@@ -1 +1 @@
1
- {"version":3,"file":"ComputerPanel-CRIF1YEd.js","names":[],"sources":["../../src/client/utils/managedBrowser.ts","../../src/client/utils/browserDrag.ts","../../src/client/components/workspace/BrowserToolbar.vue","../../src/client/components/workspace/BrowserToolbar.vue","../../src/client/components/workspace/ComputerPanel.vue","../../src/client/components/workspace/ComputerPanel.vue"],"sourcesContent":["import { apiRequest } from '@/api/client'\nimport type { ManagedBrowserState } from '@shared/managedBrowser'\n\nexport function browserCanPrepare(state?: ManagedBrowserState): boolean {\n return !!state?.enabled && (!!state.available || !!state.installation)\n}\n\n/** Preparing installs the runtime only; taking control remains a separate action. */\nexport async function prepareManagedBrowser(agentId: string, initial: ManagedBrowserState | undefined, signal: AbortSignal, update: (state: ManagedBrowserState) => void): Promise<void> {\n const path = `/api/app/agents/${encodeURIComponent(agentId)}/managed-browser`\n let state = initial ?? await apiRequest<ManagedBrowserState>(path, { signal })\n signal.throwIfAborted()\n if (!browserCanPrepare(state)) throw new Error(state.reason || '托管浏览器不可用,请检查执行节点与授权')\n if (!state.available && state.installation?.status !== 'installing') {\n state = await apiRequest<ManagedBrowserState>(path + '/prepare', { method: 'POST', body: { retry: state.installation?.status === 'failed' }, signal })\n }\n const started = Date.now()\n for (;;) {\n signal.throwIfAborted(); update(state)\n if (state.available) return\n if (state.installation?.status === 'failed') throw new Error(state.installation.error || state.installation.message || '浏览器准备失败,请重试')\n if (!browserCanPrepare(state)) throw new Error(state.reason || '执行节点已不可用,请重新连接后重试')\n if (Date.now() - started > 10 * 60_000) throw new Error('浏览器仍在准备中,可稍后重新接管查看进度')\n await new Promise<void>((resolve, reject) => {\n const abort = () => { clearTimeout(timer); reject(signal.reason ?? new DOMException('Aborted', 'AbortError')) }\n const timer = setTimeout(() => { signal.removeEventListener('abort', abort); resolve() }, 1000)\n signal.addEventListener('abort', abort, { once: true })\n if (signal.aborted) abort()\n })\n state = await apiRequest<ManagedBrowserState>(path, { signal })\n }\n}\n","import type {BrowserPointerAction} from '@shared/managedBrowser'\nimport {createUuid} from './id'\n\ntype Point={x:number;y:number}\n/** One in-flight input, one latest move, and a final release. Never replay a failed movement. */\nexport class BrowserDragSession {\n readonly id=createUuid()\n readonly done:Promise<void>\n private resolve!:()=>void\n private queue:BrowserPointerAction[]=[]\n private running=false\n private ending=false\n private finished=false\n private heartbeat:ReturnType<typeof setInterval>\n private latest:Point\n constructor(start:Point,private send:(action:BrowserPointerAction)=>Promise<void>,private failed:(error:unknown)=>void){\n this.latest={x:start.x,y:start.y};this.done=new Promise(resolve=>{this.resolve=resolve})\n this.heartbeat=setInterval(()=>{if(!this.ending)this.move(this.latest)},1000)\n this.enqueue('start',start)\n }\n move(point:Point){if(!this.ending)this.enqueue('move',point)}\n end(point:Point){if(this.ending)return;this.ending=true;clearInterval(this.heartbeat);this.enqueue('end',point)}\n cancel(){\n if(this.finished)return this.done\n this.ending=true;clearInterval(this.heartbeat);this.queue=[];this.enqueue('cancel',this.latest)\n return this.done\n }\n private enqueue(phase:BrowserPointerAction['phase'],point:Point){\n // Type annotations do not strip the viewer's DOM element and pointer metadata.\n this.latest={x:point.x,y:point.y}\n const action:BrowserPointerAction={kind:'pointer',gestureId:this.id,phase,x:point.x,y:point.y}\n if(phase==='move'&&this.queue.at(-1)?.phase==='move')this.queue[this.queue.length-1]=action\n else this.queue.push(action)\n void this.flush()\n }\n private async flush(){\n if(this.running||this.finished)return\n this.running=true\n try{\n while(this.queue.length){\n const action=this.queue.shift()!\n await this.send(action)\n if(action.phase==='end'||action.phase==='cancel'){this.finish();return}\n }\n }catch(error){\n this.ending=true;this.queue=[];clearInterval(this.heartbeat)\n // The server may have executed a timed-out start/move. Release it explicitly.\n try{await this.send({kind:'pointer',gestureId:this.id,phase:'cancel',...this.latest})}catch{/* Runner also releases idle gestures. */}\n this.failed(error);this.finish()\n }finally{this.running=false}\n }\n private finish(){this.finished=true;this.ending=true;this.queue=[];clearInterval(this.heartbeat);this.resolve()}\n}\n","<script setup lang=\"ts\">\nimport {computed,ref,watch} from 'vue'\nimport type {BotBrowserState} from '@shared/desktopEnvironment'\nconst props=defineProps<{state?:BotBrowserState;enabled:boolean}>(),emit=defineEmits<{action:[action:Record<string,string>]}>()\nconst address=ref(''),editing=ref(false),active=computed(()=>props.state?.tabs.find(t=>t.active))\nwatch(()=>active.value?.url,url=>{if(!editing.value)address.value=url==='about:blank'?'':url??''},{immediate:true})\nfunction navigate(){let url=address.value.trim();if(!url)return;if(!/^https?:\\/\\//i.test(url))url='https://'+url;editing.value=false;emit('action',{kind:'navigate',url})}\n</script>\n<template>\n <div class=\"browser-toolbar\" aria-label=\"机器人浏览器\">\n <div class=\"browser-tabs\" role=\"tablist\" aria-label=\"浏览器标签页\">\n <div v-for=\"tab in state?.tabs\" :key=\"tab.id\" class=\"tab\" :class=\"{active:tab.active}\">\n <button role=\"tab\" :aria-selected=\"tab.active\" :title=\"tab.title\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'select-tab',tabId:tab.id})\">{{tab.title}}</button>\n <button class=\"close-tab\" :aria-label=\"`关闭标签页 ${tab.title}`\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'close-tab',tabId:tab.id})\">×</button>\n </div>\n <button class=\"new-tab\" aria-label=\"新建标签页\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'new-tab',url:'https://www.google.com/'})\">+</button>\n </div>\n <form class=\"navigation\" @submit.prevent=\"navigate\">\n <button type=\"button\" aria-label=\"后退\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'back'})\">←</button>\n <button type=\"button\" aria-label=\"前进\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'forward'})\">→</button>\n <button type=\"button\" aria-label=\"重新加载页面\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'reload'})\">↻</button>\n <input v-model=\"address\" type=\"text\" inputmode=\"url\" autocomplete=\"off\" spellcheck=\"false\" aria-label=\"浏览器地址\" placeholder=\"输入网址\" :disabled=\"!enabled\" @focus=\"editing=true\" @blur=\"editing=false\">\n <button type=\"submit\" :disabled=\"!enabled||!address.trim()\">前往</button>\n </form>\n </div>\n</template>\n<style scoped>\n.browser-toolbar{min-width:0;background:var(--surface-soft);border-bottom:1px solid var(--line)}.browser-tabs{display:flex;overflow-x:auto;gap:4px;padding:7px 10px 0}.tab{display:flex;align-items:center;min-width:90px;max-width:210px;border:1px solid transparent;border-radius:8px 8px 0 0}.tab.active{background:var(--surface);border-color:var(--line);border-bottom-color:var(--surface)}.tab button:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:160px}.browser-toolbar button{font:inherit;font-size:12px;min-width:36px;min-height:40px;background:transparent;border:0;color:var(--text-primary);cursor:pointer}.tab .close-tab{min-width:32px}.browser-toolbar button:disabled{opacity:.45;cursor:default}.navigation{display:flex;align-items:center;gap:3px;padding:5px 10px}.navigation input{flex:1;min-width:0;min-height:40px;border:1px solid var(--line);border-radius:8px;padding:6px 10px;background:var(--surface);color:var(--text-primary);font:inherit;font-size:13px}button:focus-visible,input:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}@media(max-width:600px){.navigation{padding:4px}.browser-toolbar button{min-width:40px;min-height:44px}.tab button:first-child{max-width:130px}}\n</style>\n","<script setup lang=\"ts\">\nimport {computed,ref,watch} from 'vue'\nimport type {BotBrowserState} from '@shared/desktopEnvironment'\nconst props=defineProps<{state?:BotBrowserState;enabled:boolean}>(),emit=defineEmits<{action:[action:Record<string,string>]}>()\nconst address=ref(''),editing=ref(false),active=computed(()=>props.state?.tabs.find(t=>t.active))\nwatch(()=>active.value?.url,url=>{if(!editing.value)address.value=url==='about:blank'?'':url??''},{immediate:true})\nfunction navigate(){let url=address.value.trim();if(!url)return;if(!/^https?:\\/\\//i.test(url))url='https://'+url;editing.value=false;emit('action',{kind:'navigate',url})}\n</script>\n<template>\n <div class=\"browser-toolbar\" aria-label=\"机器人浏览器\">\n <div class=\"browser-tabs\" role=\"tablist\" aria-label=\"浏览器标签页\">\n <div v-for=\"tab in state?.tabs\" :key=\"tab.id\" class=\"tab\" :class=\"{active:tab.active}\">\n <button role=\"tab\" :aria-selected=\"tab.active\" :title=\"tab.title\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'select-tab',tabId:tab.id})\">{{tab.title}}</button>\n <button class=\"close-tab\" :aria-label=\"`关闭标签页 ${tab.title}`\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'close-tab',tabId:tab.id})\">×</button>\n </div>\n <button class=\"new-tab\" aria-label=\"新建标签页\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'new-tab',url:'https://www.google.com/'})\">+</button>\n </div>\n <form class=\"navigation\" @submit.prevent=\"navigate\">\n <button type=\"button\" aria-label=\"后退\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'back'})\">←</button>\n <button type=\"button\" aria-label=\"前进\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'forward'})\">→</button>\n <button type=\"button\" aria-label=\"重新加载页面\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'reload'})\">↻</button>\n <input v-model=\"address\" type=\"text\" inputmode=\"url\" autocomplete=\"off\" spellcheck=\"false\" aria-label=\"浏览器地址\" placeholder=\"输入网址\" :disabled=\"!enabled\" @focus=\"editing=true\" @blur=\"editing=false\">\n <button type=\"submit\" :disabled=\"!enabled||!address.trim()\">前往</button>\n </form>\n </div>\n</template>\n<style scoped>\n.browser-toolbar{min-width:0;background:var(--surface-soft);border-bottom:1px solid var(--line)}.browser-tabs{display:flex;overflow-x:auto;gap:4px;padding:7px 10px 0}.tab{display:flex;align-items:center;min-width:90px;max-width:210px;border:1px solid transparent;border-radius:8px 8px 0 0}.tab.active{background:var(--surface);border-color:var(--line);border-bottom-color:var(--surface)}.tab button:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:160px}.browser-toolbar button{font:inherit;font-size:12px;min-width:36px;min-height:40px;background:transparent;border:0;color:var(--text-primary);cursor:pointer}.tab .close-tab{min-width:32px}.browser-toolbar button:disabled{opacity:.45;cursor:default}.navigation{display:flex;align-items:center;gap:3px;padding:5px 10px}.navigation input{flex:1;min-width:0;min-height:40px;border:1px solid var(--line);border-radius:8px;padding:6px 10px;background:var(--surface);color:var(--text-primary);font:inherit;font-size:13px}button:focus-visible,input:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}@media(max-width:600px){.navigation{padding:4px}.browser-toolbar button{min-width:40px;min-height:44px}.tab button:first-child{max-width:130px}}\n</style>\n","<script setup lang=\"ts\">\nimport type { ComputerBackend, ManagedBrowserState } from '@shared/managedBrowser'\nimport {computed,onBeforeUnmount,onMounted,ref,watch} from 'vue'\nimport {apiRequest} from '@/api/client'\nimport {browserCanPrepare,prepareManagedBrowser} from '@/utils/managedBrowser'\nimport {BrowserDragSession} from '@/utils/browserDrag'\nimport BrowserToolbar from './BrowserToolbar.vue'\nimport type {BotBrowserState} from '@shared/desktopEnvironment'\nimport {createUuid} from '@/utils/id'\nimport type {WorkspaceAgent} from '@shared/workspace'\nimport type {ComputerControlStatus,ComputerFrame,ComputerInput} from '@shared/computerControl'\nimport type {JsonValue} from '@shared/types'\nconst props=defineProps<{agents:WorkspaceAgent[];embedded?:boolean;standalone?:boolean;autoTake?:boolean;backend?:ComputerBackend,host?:string}>(),emit=defineEmits<{close:[];changed:[];controlRequest:[];targetChanged:[backend:ComputerBackend,host?:string]}>()\nconst selected=ref(props.agents[0]?.id??''),dialog=ref<HTMLDialogElement>()\nconst backend=ref(props.backend),host=ref(props.host??(props.backend==='desktop'?'local':'')),envTargets=ref<{cloudConfigured:boolean;vmEnabled:boolean;managedBrowser?:ManagedBrowserState}>()\nconst state=ref<ComputerControlStatus>({mode:'off'}),frame=ref<ComputerFrame>(),ticket=ref<{controlId:string;token:string}>()\nconst controlsOpen=ref(false),actualSize=ref(false),browserState=ref<BotBrowserState>()\nconst dragging=ref(false)\nconst managedBrowser=ref<ManagedBrowserState>()\nconst preparing=ref(false)\nconst recheckingBrowser=ref(false)\nconst closingBrowser=ref(false)\nconst loadingTargets=ref(true)\nlet preparation:AbortController|undefined\nconst installation=computed(()=>backend.value==='managed-browser'?managedBrowser.value?.installation:undefined)\nconst selectedAgent=computed(()=>props.agents.find(agent=>agent.id===selected.value))\nconst error=ref(''),busy=ref(false),text=ref(''),notes=ref(''),connectionOK=ref(false)\nlet takeRequestId:string|undefined\nlet actionError=''\nlet timer:ReturnType<typeof setTimeout>|undefined,closed=false,polling=false,lastRenew=0,generation=0\nconst labels:Record<ComputerControlStatus['mode'],string>={off:'电脑未运行',idle:'电脑空闲 · 仅查看',agent:'机器人正在操作',pausing:'正在等待当前操作结束',human:'人工控制中',resuming:'正在交还机器人',error:'电脑需要检查'}\nconst mine=computed(()=>!!ticket.value&&ticket.value.controlId===state.value.controlId)\nconst canCloseBrowser=computed(()=>backend.value==='managed-browser'&&mine.value&&state.value.mode==='human'&&!state.value.canResume)\nconst canSend=computed(()=>connectionOK.value&&!!frame.value&&mine.value&&state.value.mode==='human'&&frame.value?.generation===state.value.generation)\nconst controllable=computed(()=>canSend.value&&!busy.value&&!dragging.value)\nconst computerSettings=ref({vm:true,cloud:true,managedBrowser:false})\nconst targets=computed(()=>{\n const envs=computerSettings.value,info=envTargets.value\n const list:{key:string;label:string;backend:ComputerBackend;host?:string;disabled?:boolean}[]=[]\n if(envs.cloud)list.push({key:'cloud',label:'云虚拟机'+(loadingTargets.value?' · 正在检查':info?.cloudConfigured?'':' · 未连接'),backend:'cloud',disabled:loadingTargets.value||!info?.cloudConfigured})\n if(envs.vm)list.push({key:'vm',label:'本地虚拟机'+(loadingTargets.value?' · 正在检查':info?.vmEnabled?'':' · 未启用'),backend:'vm',disabled:loadingTargets.value||!info?.vmEnabled})\n if(envs.managedBrowser){const browser=info?.managedBrowser;list.push({key:'managed-browser',label:'托管浏览器'+(loadingTargets.value?' · 正在检查':browser?.available?'':browserCanPrepare(browser)?' · '+(browser?.installation?.status==='failed'?'准备失败':'待准备'):' · 不可用'),backend:'managed-browser',disabled:loadingTargets.value||!browserCanPrepare(browser)})}\n return list\n})\nconst currentKey=computed(()=>targets.value.find(t=>t.backend===backend.value&&(t.host??'')===host.value)?.key??'')\nconst targetAvailable=computed(()=>targets.value.some(t=>t.key===currentKey.value&&!t.disabled))\nwatch([backend,host],([selectedBackend,selectedHost])=>{if(selectedBackend)emit('targetChanged',selectedBackend,selectedHost||undefined)},{immediate:true})\nasync function loadTargets(){\n if(!selected.value)return\n const selectedId=selected.value,id=encodeURIComponent(selectedId)\n loadingTargets.value=true\n try{\n const settings=await apiRequest<{scriptMachine:boolean;serverComputer:boolean;vm:boolean;cloud:boolean;managedBrowser?:boolean}>('/api/app/settings/host-tools')\n if(closed||selectedId!==selected.value)return\n computerSettings.value={vm:settings.vm,cloud:settings.cloud,managedBrowser:settings.managedBrowser===true}\n const [cloud,vm,browser]=await Promise.all([\n computerSettings.value.cloud?apiRequest<{configured:boolean}>(`/api/app/agents/${id}/cloud-computer`).catch(()=>undefined):Promise.resolve(undefined),\n computerSettings.value.vm?apiRequest<{enabled:boolean}>(`/api/app/agents/${id}/local-vm`).catch(()=>undefined):Promise.resolve(undefined),\n computerSettings.value.managedBrowser?apiRequest<ManagedBrowserState>(`/api/app/agents/${id}/managed-browser`).catch(()=>undefined):Promise.resolve(undefined),\n ])\n if(closed||selectedId!==selected.value)return\n envTargets.value={cloudConfigured:!!(cloud as {configured?:boolean}|undefined)?.configured,vmEnabled:!!(vm as {enabled?:boolean}|undefined)?.enabled,managedBrowser:browser}\n if(backend.value==='managed-browser'){managedBrowser.value=browser;browserState.value=browser}\n }finally{if(selectedId===selected.value)loadingTargets.value=false}\n}\nasync function switchTarget(key:string){\n const next=targets.value.find(t=>t.key===key)\n if(!next||next.disabled||key===currentKey.value)return\n await cancelDrag()\n if(ticket.value){\n try{await post('/giveback',{...ticket.value,notes:''})}catch(cause){error.value=cause instanceof Error?cause.message:'请先交还当前电脑';return}\n }\n backend.value=next.backend;host.value=next.host??''\n preparation?.abort()\n clearTimeout(clickTimer);pendingClick=undefined;browserState.value=undefined;managedBrowser.value=undefined\n inputs.length=0;connectionOK.value=false;generation++;takeRequestId=undefined;frame.value=undefined;ticket.value=undefined;state.value={mode:'off'};actionError='';error.value=''\n await refresh()\n}\nconst endpoint=()=>`/api/app/agents/${encodeURIComponent(selected.value)}/computer`\nconst query=()=>{const q=new URLSearchParams();if(backend.value)q.set('backend',backend.value);if(backend.value==='desktop'&&host.value)q.set('host',host.value);const qs=q.toString();return qs?'?'+qs:''}\nasync function post(path:string,body:unknown){return apiRequest<any>(endpoint()+path+query(),{method:'POST',body:body as JsonValue})}\nasync function refresh(){\n if(polling||busy.value||closed||!selected.value||document.hidden||(!targetAvailable.value&&!(backend.value==='managed-browser'&&computerSettings.value.managedBrowser)))return\n polling=true;const current=++generation,base=endpoint(),targetQuery=query(),id=selected.value\n try{\n if(backend.value==='managed-browser'){\n const browser=await apiRequest<ManagedBrowserState>(`/api/app/agents/${encodeURIComponent(id)}/managed-browser`).catch(cause=>{\n if(current===generation&&!closed){managedBrowser.value=undefined;browserState.value=undefined;if(envTargets.value)envTargets.value.managedBrowser=undefined;state.value={mode:'off',backend:'managed-browser'}}\n throw cause\n })\n if(current!==generation||closed)return\n managedBrowser.value=browser;browserState.value=browser\n if(envTargets.value)envTargets.value.managedBrowser=browser\n if(!browser.available){state.value={mode:'off',backend:'managed-browser'};connectionOK.value=false;frame.value=undefined;error.value=actionError;return}\n }\n const status=await apiRequest<ComputerControlStatus>(base+targetQuery)\n if(current!==generation||closed)return\n state.value=status\n connectionOK.value=true\n if(ticket.value&&status.controlId!==ticket.value.controlId&&status.mode!=='pausing')ticket.value=undefined\n if(mine.value&&Date.now()-lastRenew>8000){await post('/renew',ticket.value);lastRenew=Date.now()}\n if(status.backend==='browser'){\n const browser=await apiRequest<BotBrowserState>(`/api/app/agents/${encodeURIComponent(selected.value)}/browser`)\n if(current!==generation||closed)return\n browserState.value=browser;managedBrowser.value=undefined\n }else if(status.backend!=='managed-browser'){browserState.value=undefined;managedBrowser.value=undefined}\n if(status.mode!=='off'&&(!['browser','managed-browser'].includes(status.backend??'')||browserState.value?.open)){\n const image=await apiRequest<ComputerFrame>(base+'/frame'+targetQuery)\n if(current===generation&&!closed)frame.value=image\n }else frame.value=undefined\n error.value=actionError\n }catch(cause){if(current===generation&&!closed){connectionOK.value=false;frame.value=undefined;inputs.length=0;error.value=cause instanceof Error?cause.message:'无法读取电脑状态';{const code=Number((cause as {status?:number}).status)\n if([401,410].includes(code)||(code===403&&ticket.value)){ticket.value=undefined;frame.value=undefined;state.value={mode:'off'}}}}}\n finally{polling=false}\n}\nasync function run(action:()=>Promise<void>){\n if(busy.value)return;generation++;busy.value=true;actionError='';error.value=''\n let failure=''\n try{await action()}catch(cause){failure=cause instanceof Error?cause.message:'电脑操作失败'}finally{busy.value=false;if(!closed){await refresh();if(failure){actionError=failure;error.value=failure}void flushInputs()}}\n}\nasync function recheckBrowser(){if(busy.value)return;recheckingBrowser.value=true;try{await run(loadTargets)}finally{recheckingBrowser.value=false}}\nfunction take(){return run(async()=>{\n if(!targetAvailable.value)throw new Error('所选电脑不可用,请重新选择')\n const id=selected.value,target=backend.value,base=endpoint(),targetQuery=query(),controller=new AbortController()\n preparation=controller\n try{\n if(target==='managed-browser'){\n preparing.value=!managedBrowser.value?.available\n await prepareManagedBrowser(id,managedBrowser.value,controller.signal,browser=>{\n if(!closed&&id===selected.value&&target===backend.value){managedBrowser.value=browser;browserState.value=browser;if(envTargets.value)envTargets.value.managedBrowser=browser}\n })\n }\n controller.signal.throwIfAborted()\n if(closed||id!==selected.value||target!==backend.value)return\n preparing.value=false;takeRequestId??=createUuid()\n // Do not abort a submitted takeover: retain its receipt so a late response\n // can be returned to the exact original target after this view closes.\n const value=await apiRequest<any>(base+'/take'+targetQuery,{method:'POST',body:{requestId:takeRequestId}})\n const credentials={controlId:value.controlId,token:value.token}\n if(closed||controller.signal.aborted||id!==selected.value||target!==backend.value){await apiRequest(base+'/giveback'+targetQuery,{method:'POST',body:{...credentials,notes:''}});return}\n ticket.value=credentials;state.value=value;lastRenew=0\n }catch(cause){if(!controller.signal.aborted)throw cause}\n finally{if(preparation===controller){preparation=undefined;preparing.value=false}}\n})}\nconst inputs:ComputerInput[]=[];let flushing=false,pendingClick:ComputerInput|undefined,clickTimer:ReturnType<typeof setTimeout>|undefined\nfunction send(action:ComputerInput){\n if(!canSend.value||closed||dragging.value)return\n if(pendingClick){inputs.push(pendingClick);pendingClick=undefined;clearTimeout(clickTimer)}\n const last=inputs.at(-1)\n if(action.kind==='text'&&last?.kind==='text'&&last.text.length+action.text.length<=16000)last.text+=action.text\n else if(inputs.length<64)inputs.push(action)\n else {error.value='输入过快,请稍后再试';return}\n void flushInputs()\n}\nasync function flushInputs(){\n if(flushing||busy.value||!inputs.length||closed)return\n if(!canSend.value||!frame.value||!ticket.value){inputs.length=0;return}\n flushing=true\n const action=inputs.shift()!,image=frame.value,credentials=ticket.value\n try{await run(async()=>{await post('/input',{...credentials,requestId:createUuid(),generation:image.generation,frameId:image.id,action})});if(error.value)inputs.length=0}\n finally{flushing=false;if(inputs.length)setTimeout(()=>void flushInputs(),0)}\n}\nfunction browserAction(action:Record<string,string>){\n if(!controllable.value||!frame.value||!ticket.value)return\n const f=frame.value,credentials=ticket.value\n void run(async()=>{\n if(backend.value==='managed-browser')await apiRequest(`/api/app/agents/${encodeURIComponent(selected.value)}/managed-browser/action`,{method:'POST',body:{...credentials,requestId:createUuid(),generation:f.generation,action}})\n else await post('/browser',{...credentials,requestId:createUuid(),generation:f.generation,frameId:f.id,action})\n })\n}\nfunction point(event:MouseEvent|PointerEvent,clamp=false){\n const r=(event.currentTarget as HTMLImageElement).getBoundingClientRect(),f=frame.value!\n const scale=Math.min(r.width/f.width,r.height/f.height),left=r.left+(r.width-f.width*scale)/2,top=r.top+(r.height-f.height*scale)/2\n const x=Math.floor((event.clientX-left)/scale),y=Math.floor((event.clientY-top)/scale)\n if(clamp)return {x:Math.max(0,Math.min(f.width-1,x)),y:Math.max(0,Math.min(f.height-1,y))}\n if(x<0||y<0||x>=f.width||y>=f.height)return\n return {x,y}\n}\nlet dragStart:{x:number;y:number;clientX:number;clientY:number;pointerId:number;element:HTMLElement}|undefined,dragged=false,liveDrag:BrowserDragSession|undefined\nfunction down(event:PointerEvent){\n if(!controllable.value||dragStart||event.button!==0||inputs.length)return\n const element=event.currentTarget as HTMLElement;element.focus({preventScroll:true})\n const p=point(event);if(!p)return\n dragged=false;dragStart={...p,clientX:event.clientX,clientY:event.clientY,pointerId:event.pointerId,element};element.setPointerCapture(event.pointerId)\n}\nfunction startLiveDrag(start:{x:number;y:number}){\n const base=endpoint(),targetQuery=query(),credentials=ticket.value!,controlGeneration=frame.value!.generation\n let image=frame.value!\n clearTimeout(clickTimer);pendingClick=undefined;error.value='';dragging.value=true\n const current=()=>!closed&&endpoint()===base&&query()===targetQuery&&ticket.value?.controlId===credentials.controlId&&state.value.generation===controlGeneration\n const session=new BrowserDragSession(start,async action=>{\n if(action.phase!=='cancel'&&(!current()||!canSend.value))throw new Error('控制权已变化,请重新接管')\n await apiRequest(base+'/input'+targetQuery,{method:'POST',timeoutMs:5000,body:{...credentials,requestId:createUuid(),generation:controlGeneration,frameId:image.id,action:{...action}}})\n if(action.phase==='cancel')return\n const next=await apiRequest<ComputerFrame>(base+'/frame'+targetQuery,{timeoutMs:5000})\n if(!current()||next.generation!==controlGeneration)throw new Error('控制权已变化,请重新接管')\n image=next;frame.value=next\n },cause=>{if(current())error.value=cause instanceof Error?cause.message:'拖动失败,请重新按下鼠标'})\n liveDrag=session\n void session.done.then(async()=>{if(liveDrag===session){liveDrag=undefined;dragging.value=false;const failure=error.value;await refresh();if(current()&&failure)error.value=failure}})\n}\nfunction move(event:PointerEvent){\n const start=dragStart\n if(!start||event.pointerId!==start.pointerId||!canSend.value)return\n if(backend.value!=='managed-browser')return\n if(!liveDrag&&Math.hypot(event.clientX-start.clientX,event.clientY-start.clientY)>5){dragged=true;startLiveDrag(start)}\n const p=point(event,true);if(p)liveDrag?.move(p)\n}\nfunction releaseCapture(){const start=dragStart;dragStart=undefined;if(start?.element.hasPointerCapture(start.pointerId))start.element.releasePointerCapture(start.pointerId)}\nfunction cancelDrag(){releaseCapture();if(liveDrag){dragged=true;return liveDrag.cancel()}return Promise.resolve()}\nfunction up(event:PointerEvent){\n const start=dragStart;if(!start||event.pointerId!==start.pointerId)return\n if(!canSend.value){void cancelDrag();return}\n move(event)\n const end=point(event,true);releaseCapture()\n if(liveDrag){if(end)liveDrag.end(end);else void cancelDrag();return}\n if(end&&Math.hypot(event.clientX-start.clientX,event.clientY-start.clientY)>5){dragged=true;send({kind:'drag',fromX:start.x,fromY:start.y,toX:end.x,toY:end.y})}\n}\nconst windowBlur=()=>{void cancelDrag()},visibility=()=>{if(document.hidden)void cancelDrag()}\nwatch(canSend,allowed=>{if(!allowed)void cancelDrag()})\nfunction wheel(event:WheelEvent){if(!canSend.value)return;event.preventDefault();send({kind:'scroll',direction:Math.abs(event.deltaX)>Math.abs(event.deltaY)?event.deltaX>0?'right':'left':event.deltaY>0?'down':'up',amount:Math.min(20,Math.max(1,Math.ceil(Math.abs(event.deltaY||event.deltaX)/80)))})}\nfunction keydown(event:KeyboardEvent){if(!canSend.value)return;event.preventDefault();event.stopPropagation();if(event.key==='Escape'){void cancelDrag();(event.currentTarget as HTMLElement).blur();return}const modifiers=[event.ctrlKey||(backend.value==='managed-browser'&&event.metaKey)?'ctrl':'',event.altKey?'alt':'',event.shiftKey?'shift':'',event.metaKey&&backend.value!=='managed-browser'?'super':''].filter(Boolean);if(event.key.length===1&&!event.ctrlKey&&!event.altKey&&!event.metaKey)send({kind:'text',text:event.key});else if(!['Control','Alt','Shift','Meta'].includes(event.key))send({kind:'key',key:({Enter:'Return',ArrowUp:'Up',ArrowDown:'Down',ArrowLeft:'Left',ArrowRight:'Right',Backspace:'BackSpace'} as Record<string,string>)[event.key]??event.key,modifiers})}\nfunction click(event:MouseEvent){\n if(dragged){dragged=false;return}\n if(!canSend.value||!frame.value)return\n const p=point(event);if(!p)return\n const action:ComputerInput={kind:'click',...p,button:event.button===2?'right':'left',count:event.detail>1?2:1}\n if(event.button===2||event.detail>1){clearTimeout(clickTimer);pendingClick=undefined;send(action)}\n else {if(pendingClick){const old=pendingClick;pendingClick=undefined;clearTimeout(clickTimer);send(old)}pendingClick=action;clickTimer=setTimeout(()=>{const next=pendingClick;pendingClick=undefined;if(next)send(next)},180)}\n}\nasync function giveBack(){await cancelDrag();if(ticket.value){await post('/giveback',{...ticket.value,notes:notes.value});ticket.value=undefined;takeRequestId=undefined;notes.value=''}}\nasync function closeBrowser(){\n if(busy.value||!canCloseBrowser.value||!ticket.value)return\n await cancelDrag()\n const id=selected.value,credentials=ticket.value\n closingBrowser.value=true\n try{await run(async()=>{\n clearTimeout(clickTimer);pendingClick=undefined;inputs.length=0\n const status=await apiRequest<ComputerControlStatus>(`/api/app/agents/${encodeURIComponent(id)}/managed-browser/close`,{method:'POST',body:credentials})\n if(closed||selected.value!==id||backend.value!=='managed-browser'||ticket.value!==credentials)return\n state.value=status;ticket.value=undefined;takeRequestId=undefined;notes.value='';frame.value=undefined;connectionOK.value=false;controlsOpen.value=false\n emit('changed')\n })}finally{closingBrowser.value=false}\n}\nasync function close(){if(preparing.value){preparation?.abort();closed=true;dialog.value?.close?.();emit('close');return}if(busy.value)return;await run(giveBack);if(!ticket.value){closed=true;if(!props.embedded&&!props.standalone)dialog.value?.close();emit('close')}}\nasync function fullscreen(){try{if(document.fullscreenElement)await document.exitFullscreen();else await dialog.value?.requestFullscreen()}catch{error.value='无法进入全屏,请使用窗口最大化'}}\nasync function cycle(){await refresh();if(!closed)timer=setTimeout(()=>void cycle(),1200)}\nwatch(selected,()=>{\n void cancelDrag()\n preparation?.abort()\n clearTimeout(clickTimer);pendingClick=undefined;browserState.value=undefined;managedBrowser.value=undefined\n inputs.length=0;connectionOK.value=false;generation++;takeRequestId=undefined;frame.value=undefined;ticket.value=undefined;state.value={mode:'off'};actionError='';error.value=''\n backend.value=props.backend;host.value=props.host??(props.backend==='desktop'?'local':'');envTargets.value=undefined\n void loadTargets().then(async()=>{if(!backend.value){const first=targets.value.find(t=>!t.disabled);if(first)await switchTarget(first.key)}await refresh()}).catch(cause=>{error.value=cause instanceof Error?cause.message:'无法读取电脑列表'})\n})\nonMounted(async()=>{\n window.addEventListener('blur',windowBlur);document.addEventListener('visibilitychange',visibility)\n if(!props.embedded&&!props.standalone)dialog.value?.showModal()\n try{\n await loadTargets()\n if(closed)return\n if(!backend.value){const first=targets.value.find(t=>!t.disabled);if(first)await switchTarget(first.key)}\n if(!targetAvailable.value&&backend.value!=='managed-browser')throw new Error('所选电脑不可用,请重新选择;未打开其他电脑')\n if(props.autoTake&&!closed&&targetAvailable.value)await take()\n }catch(cause){error.value=cause instanceof Error?cause.message:'无法读取电脑列表'}\n if(!closed)void cycle()\n})\nonBeforeUnmount(()=>{void cancelDrag();window.removeEventListener('blur',windowBlur);document.removeEventListener('visibilitychange',visibility);preparation?.abort();if(ticket.value)void post('/giveback',{...ticket.value,notes:notes.value}).catch(()=>{});inputs.length=0;pendingClick=undefined;clearTimeout(clickTimer);closed=true;generation++;clearTimeout(timer)})\ndefineExpose({take,close,releaseControl:async()=>{if(preparing.value){preparation?.abort();return}if(busy.value)throw new Error('请等待当前操作完成');await run(giveBack);if(ticket.value)throw new Error(error.value||'请先交还控制权')},hasControl:()=>!!ticket.value})\n</script>\n<template>\n <Teleport to=\"body\" :disabled=\"embedded||standalone\">\n <component :is=\"embedded||standalone?'section':'dialog'\" ref=\"dialog\" class=\"computer-panel\" :class=\"{embedded,standalone}\" :aria-label=\"targets.find(t=>t.key===currentKey)?.label||'电脑桌面'\" @cancel.prevent=\"close\">\n <header>\n <div><strong>{{backend==='managed-browser'?'托管浏览器':backend==='cloud'?'云虚拟机':'本地虚拟机'}}</strong><small>{{ preparing?'正在准备浏览器':labels[state.mode] }}</small></div>\n <select v-if=\"targets.length>1\" :value=\"currentKey\" aria-label=\"切换电脑环境\" :disabled=\"!!ticket||busy\" @change=\"switchTarget(($event.target as HTMLSelectElement).value)\"><option v-for=\"target in targets\" :key=\"target.key\" :value=\"target.key\" :disabled=\"target.disabled\">{{ target.label }}</option></select>\n <select v-if=\"agents.length>1\" v-model=\"selected\" aria-label=\"选择电脑\" :disabled=\"!!ticket||busy\"><option v-for=\"agent in agents\" :key=\"agent.id\" :value=\"agent.id\">{{ agent.name }}</option></select>\n <span v-else-if=\"!embedded\">{{ agents[0]?.name }}</span>\n <button v-if=\"embedded&&!ticket\" :disabled=\"busy||state.mode==='human'\" @click=\"emit('controlRequest')\">接管电脑</button>\n <button v-if=\"embedded&&mine\" @click=\"controlsOpen=!controlsOpen\">键盘与操作</button>\n <button v-if=\"embedded&&mine\" :disabled=\"busy||state.mode!=='human'\" @click=\"run(giveBack)\">交还</button>\n <template v-if=\"!embedded\">\n <button v-if=\"mine\" type=\"button\" :aria-expanded=\"controlsOpen\" @click=\"controlsOpen=!controlsOpen\">键盘与操作</button>\n <button type=\"button\" :aria-pressed=\"actualSize\" @click=\"actualSize=!actualSize\">{{actualSize?'适应窗口':'原始大小'}}</button>\n <button type=\"button\" @click=\"fullscreen\">全屏</button>\n </template>\n <button v-if=\"!embedded\" class=\"return-control\" type=\"button\" :disabled=\"busy&&!preparing\" @click=\"close\">{{ backend==='managed-browser' ? (ticket ? '交还并收起' : '收起窗口') : (ticket ? '交还并关闭' : '关闭') }}</button>\n </header>\n <BrowserToolbar v-if=\"state.backend==='browser'||state.backend==='managed-browser'\" :state=\"browserState\" :enabled=\"controllable\" @action=\"browserAction\"/>\n <section v-if=\"installation&&installation.status!=='ready'\" class=\"browser-installation\" :aria-busy=\"preparing\">\n <p role=\"status\">{{ installation.message }}</p>\n <progress v-if=\"installation.status==='installing'||installation.status==='checking'\" :value=\"installation.progress\" max=\"100\" aria-label=\"浏览器准备进度\"/>\n <small v-if=\"preparing\">关闭窗口后仍会继续准备。</small>\n <p v-if=\"installation.error\" role=\"alert\">{{ installation.error }}</p>\n </section>\n <section v-if=\"!loadingTargets&&backend==='managed-browser'&&!managedBrowser?.available\" class=\"browser-availability\" :aria-busy=\"recheckingBrowser\">\n <p v-if=\"!installation\" role=\"status\">{{ managedBrowser?.reason || (computerSettings.managedBrowser?'执行节点暂时不可用,请检查连接后重新检测。':'托管浏览器尚未开放,请在「设置 → 环境与权限 → 运行环境」中开启。') }}</p>\n <button type=\"button\" :disabled=\"busy\" @click=\"recheckBrowser\">{{ recheckingBrowser?'正在检测…':'重新检测' }}</button>\n </section>\n <div class=\"computer-screen\" :class=\"{controlling:canSend,'actual-size':actualSize}\">\n <img v-if=\"frame\" :src=\"`data:image/png;base64,${frame.data}`\" :alt=\"(targets.find(t=>t.key===currentKey)?.label||'电脑')+'当前画面'\" draggable=\"false\" tabindex=\"0\" title=\"点按后可使用键盘、滚轮和拖动;按 Esc 退出键盘控制\" @pointerdown=\"down\" @pointermove=\"move\" @pointerup=\"up\" @pointercancel=\"cancelDrag\" @lostpointercapture=\"dragStart && cancelDrag()\" @blur=\"cancelDrag\" @wheel=\"wheel\" @keydown=\"keydown\" @click=\"click\" @contextmenu.prevent=\"click\">\n <p v-else role=\"status\">{{ loadingTargets?'正在检查电脑环境…':preparing?'准备完成后会自动接管,无需再次点击。':state.mode==='off' ? (backend==='managed-browser'?'浏览器尚未打开。接管后可打开网页。':'打开电脑后可查看桌面。') : '正在读取电脑画面…' }}</p>\n <span v-if=\"frame&&(!mine||frame.generation!==state.generation)\" class=\"view-label\">{{mine?'正在同步画面':state.mode==='off'?'上次画面':'仅查看'}}</span>\n </div>\n <p v-if=\"error||state.error\" class=\"computer-error\" role=\"alert\">{{ error||state.error }}</p>\n <footer v-if=\"controlsOpen||(!embedded&&!ticket)\">\n <p v-if=\"state.backend==='grok'\">多个机器人共享同一台 Grok Bot 云端电脑、工作文件与浏览器资料。</p>\n <p v-else-if=\"state.backend==='local'\">当前显示{{state.hostName||'所选电脑'}}的桌面,点击、输入、按键和滚动都会发送到这台电脑。</p>\n <p v-if=\"backend==='vm'&&selectedAgent?.computerEnvironmentId\">共享电脑:{{ selectedAgent.computerEnvironmentName }}。成员共用工作文件和浏览器资料,可同时执行命令与文件操作,桌面操作交替进行;人工接管时机器人暂停等待。</p>\n <p v-if=\"state.backend==='managed-browser'\">当前是托管浏览器,与电脑桌面和虚拟机分别管理;网页操作无需启动虚拟机。</p>\n <details v-if=\"managedBrowser?.downloads.length\" class=\"browser-downloads\"><summary>浏览器下载({{managedBrowser.downloads.length}})</summary><ul><li v-for=\"file in managedBrowser.downloads\" :key=\"file.id\"><span>{{file.name}}</span><small>{{(file.size/1024).toFixed(1)}} KiB</small></li></ul></details>\n <p v-if=\"!ticket\">查看不会输入任何内容。接管后机器人会暂停,已有操作结束后才允许人工操作。</p>\n <button v-if=\"!ticket&&!targetAvailable&&backend!=='managed-browser'\" type=\"button\" :disabled=\"busy\" @click=\"run(loadTargets)\">重新检查连接</button>\n <button v-if=\"!ticket&&state.mode!=='human'\" type=\"button\" :disabled=\"busy||!targetAvailable\" @click=\"embedded?emit('controlRequest'):take()\">{{ preparing?'正在准备浏览器…':installation?.status==='failed'?'重试并接管浏览器':state.mode==='off'?'打开并控制':'接管电脑' }}</button>\n <template v-if=\"mine\">\n\n <section v-if=\"controlsOpen\" class=\"manual-input\">\n <div class=\"key-row\"><button v-for=\"key in ['Return','Tab','Escape','BackSpace','Up','Down','Left','Right']\" :key=\"key\" type=\"button\" :disabled=\"!controllable\" @click=\"send({kind:'key',key})\">{{ ({Return:'回车',Escape:'Esc',BackSpace:'退格',Up:'↑',Down:'↓',Left:'←',Right:'→'} as Record<string,string>)[key]||key }}</button></div>\n <div class=\"key-row\"><button :disabled=\"!controllable\" @click=\"send({kind:'key',key:'a',modifiers:[state.backend==='local'||state.backend==='browser'?'super':'ctrl']})\">全选</button><button :disabled=\"!controllable\" @click=\"send({kind:'key',key:'c',modifiers:[state.backend==='local'||state.backend==='browser'?'super':'ctrl']})\">复制</button><button :disabled=\"!controllable\" @click=\"send({kind:'key',key:'v',modifiers:[state.backend==='local'||state.backend==='browser'?'super':'ctrl']})\">粘贴</button></div>\n <label>输入到电脑<textarea v-model=\"text\" rows=\"2\" maxlength=\"16000\" placeholder=\"输入文字后点击发送\" :disabled=\"!controllable\" /></label>\n <button type=\"button\" :disabled=\"!controllable||!text\" @click=\"send({kind:'text',text});text=''\">输入文字</button>\n </section>\n <label>交还说明<textarea v-model=\"notes\" rows=\"2\" maxlength=\"4000\" placeholder=\"告诉机器人你完成了什么,以及接下来需要做什么\" /></label>\n <button type=\"button\" :disabled=\"busy||state.mode!=='human'\" @click=\"run(giveBack)\">{{ state.canResume ? '交还机器人并继续' : backend==='managed-browser' ? '结束接管' : '结束控制' }}</button>\n <small v-if=\"backend==='managed-browser'\">{{ managedBrowser?.supportsRetention===true ? '交还或收起只会释放操作权,页面会保留;长时间空闲后可能回收。' : managedBrowser?.supportsRetention===false ? '此节点需更新后才能跨任务保留页面;任务结束后浏览器会关闭。' : '交还或收起会结束当前接管。' }}</small>\n <small v-else>控制只属于当前页面。页面断开后控制权会到期,任务不会自行恢复。</small>\n </template>\n </footer>\n <section v-if=\"canCloseBrowser\" class=\"browser-session-actions\" :aria-busy=\"closingBrowser\">\n <button type=\"button\" :disabled=\"busy\" @click=\"closeBrowser\">{{ closingBrowser?'正在关闭浏览器…':'关闭浏览器' }}</button>\n <small>结束当前浏览器会话并关闭标签页。</small>\n </section>\n </component>\n </Teleport>\n</template>\n<style scoped>\n.browser-session-actions{display:flex;align-items:center;flex-wrap:wrap;gap:12px;padding:12px 20px;border-top:1px solid var(--line)}.browser-session-actions button{min-height:44px;color:var(--danger)}.browser-session-actions small{font-size:12px;line-height:1.6;color:var(--text-secondary)}\n.browser-availability{display:grid;gap:10px;padding:12px 20px;background:var(--surface-soft);color:var(--text-secondary);border-bottom:1px solid var(--line)}.browser-availability p{margin:0;font-size:13px;line-height:1.6;overflow-wrap:anywhere}.browser-availability button{justify-self:start;min-height:44px}\n.browser-installation{display:grid;gap:10px;padding:16px 20px;border-bottom:1px solid var(--line);background:var(--surface-soft);color:var(--text-primary)}.browser-installation p{margin:0;font-size:13px;line-height:1.6;overflow-wrap:anywhere}.browser-installation progress{width:100%;height:6px;accent-color:var(--accent)}.browser-installation [role=alert]{color:var(--danger)}\n.computer-panel.embedded{position:static;width:100%;max-height:none;height:100%;margin:0;border:0;border-radius:0;overflow:auto}.computer-panel.embedded{display:flex;flex-direction:column}.embedded .computer-screen{flex:1;min-height:150px}.embedded .computer-screen img{max-height:calc(100dvh - 250px);object-fit:contain}.embedded footer{max-height:45vh;overflow:auto}.manual-input{display:grid;gap:10px}\n.computer-panel{width:min(1120px,calc(100vw - 32px));max-height:calc(100dvh - 32px);padding:0;border:1px solid var(--line);border-radius:16px;background:var(--surface);color:var(--text-primary);overflow:auto}\n.computer-panel.standalone{display:flex;flex-direction:column;width:100vw;height:100dvh;max-height:none;margin:0;border:0;border-radius:0;overflow:hidden}\n.standalone .computer-screen{flex:1;min-height:0;overflow:hidden}.standalone .computer-screen img{height:100%;width:100%;max-height:100%;max-width:100%;object-fit:contain}.standalone header{flex-shrink:0;background:#1c1f23;color:#f4f4f5}.standalone header small{color:#bec4cc}.standalone header button,.standalone header select{background:#2b2f34;color:#f4f4f5;border-color:#474c54}.standalone header .return-control{background:#f4f4f5;color:#202327;border-color:#f4f4f5}.standalone footer{position:absolute;right:16px;top:78px;width:min(360px,calc(100vw - 64px));max-height:calc(100dvh - 130px);overflow:auto;background:var(--surface);border:1px solid var(--line);border-radius:12px;box-shadow:0 16px 50px #0006;z-index:2}\n.computer-panel:fullscreen{display:flex;flex-direction:column;width:100vw;height:100dvh;max-height:none;border:0;border-radius:0}.computer-panel:fullscreen .computer-screen{flex:1;min-height:0}.computer-panel:fullscreen .computer-screen img{max-height:100%}\n.computer-screen.actual-size{overflow:auto;align-items:flex-start;justify-content:flex-start}.computer-screen.actual-size img{width:auto;height:auto;max-width:none;max-height:none;flex:none}.return-control{font-weight:600}.standalone .computer-error{flex:none;margin:0;padding:10px 18px}\n.computer-panel::backdrop{background:rgba(0,0,0,.45)}header{display:flex;align-items:center;gap:14px;padding:14px 18px;border-bottom:1px solid var(--line)}header div{display:grid;gap:4px;margin-right:auto}small{font-size:12px;color:var(--text-muted);line-height:1.5}\nbutton,select,textarea,input{font:inherit;border:1px solid var(--line);border-radius:8px;background:var(--surface-raised);color:var(--text-primary)}button,select{min-height:44px;padding:8px 12px}button{cursor:pointer}button:disabled{opacity:.5;cursor:default}button:focus-visible,select:focus-visible,textarea:focus-visible,input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}\n.computer-screen{position:relative;display:flex;align-items:center;justify-content:center;min-height:220px;background:#14181e}.computer-screen img{max-width:100%;max-height:58vh;width:auto;height:auto;user-select:none}.controlling img{cursor:crosshair;touch-action:none}.computer-screen img:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.computer-screen p{color:#d8dee7}.view-label{position:absolute;top:10px;left:12px;padding:4px 8px;border-radius:6px;background:rgba(0,0,0,.65);color:white;font-size:12px;pointer-events:none}\nfooter{padding:16px 18px;display:grid;gap:12px}footer p{margin:0;font-size:13px;color:var(--text-secondary);line-height:1.6}label{display:grid;gap:6px;font-size:13px}textarea{padding:10px;min-width:0;resize:vertical}.key-row{display:flex;gap:6px;flex-wrap:wrap}.computer-error{color:var(--danger);padding:0 18px;font-size:13px}\n.browser-downloads{font-size:13px}.browser-downloads summary{cursor:pointer;min-height:44px;display:flex;align-items:center}.browser-downloads ul{margin:0;padding-left:20px;display:grid;gap:8px}.browser-downloads li{overflow-wrap:anywhere}.browser-downloads small{margin-left:8px}\n.sharing-settings{display:grid;gap:12px;padding:12px;border:1px solid var(--line);border-radius:10px}.sharing-settings input:not([type=checkbox]){min-height:44px;padding:8px 10px}.share-option{display:flex;align-items:center;gap:8px;min-height:44px}.share-option input{width:18px;height:18px;flex:none}fieldset{border:1px solid var(--line);border-radius:8px}\n@media(max-width:600px){.computer-panel{width:100vw;max-height:100dvh;border-radius:0}header{flex-wrap:wrap;padding:12px}footer{padding:12px}.computer-screen img{max-height:48vh}}\n</style>\n","<script setup lang=\"ts\">\nimport type { ComputerBackend, ManagedBrowserState } from '@shared/managedBrowser'\nimport {computed,onBeforeUnmount,onMounted,ref,watch} from 'vue'\nimport {apiRequest} from '@/api/client'\nimport {browserCanPrepare,prepareManagedBrowser} from '@/utils/managedBrowser'\nimport {BrowserDragSession} from '@/utils/browserDrag'\nimport BrowserToolbar from './BrowserToolbar.vue'\nimport type {BotBrowserState} from '@shared/desktopEnvironment'\nimport {createUuid} from '@/utils/id'\nimport type {WorkspaceAgent} from '@shared/workspace'\nimport type {ComputerControlStatus,ComputerFrame,ComputerInput} from '@shared/computerControl'\nimport type {JsonValue} from '@shared/types'\nconst props=defineProps<{agents:WorkspaceAgent[];embedded?:boolean;standalone?:boolean;autoTake?:boolean;backend?:ComputerBackend,host?:string}>(),emit=defineEmits<{close:[];changed:[];controlRequest:[];targetChanged:[backend:ComputerBackend,host?:string]}>()\nconst selected=ref(props.agents[0]?.id??''),dialog=ref<HTMLDialogElement>()\nconst backend=ref(props.backend),host=ref(props.host??(props.backend==='desktop'?'local':'')),envTargets=ref<{cloudConfigured:boolean;vmEnabled:boolean;managedBrowser?:ManagedBrowserState}>()\nconst state=ref<ComputerControlStatus>({mode:'off'}),frame=ref<ComputerFrame>(),ticket=ref<{controlId:string;token:string}>()\nconst controlsOpen=ref(false),actualSize=ref(false),browserState=ref<BotBrowserState>()\nconst dragging=ref(false)\nconst managedBrowser=ref<ManagedBrowserState>()\nconst preparing=ref(false)\nconst recheckingBrowser=ref(false)\nconst closingBrowser=ref(false)\nconst loadingTargets=ref(true)\nlet preparation:AbortController|undefined\nconst installation=computed(()=>backend.value==='managed-browser'?managedBrowser.value?.installation:undefined)\nconst selectedAgent=computed(()=>props.agents.find(agent=>agent.id===selected.value))\nconst error=ref(''),busy=ref(false),text=ref(''),notes=ref(''),connectionOK=ref(false)\nlet takeRequestId:string|undefined\nlet actionError=''\nlet timer:ReturnType<typeof setTimeout>|undefined,closed=false,polling=false,lastRenew=0,generation=0\nconst labels:Record<ComputerControlStatus['mode'],string>={off:'电脑未运行',idle:'电脑空闲 · 仅查看',agent:'机器人正在操作',pausing:'正在等待当前操作结束',human:'人工控制中',resuming:'正在交还机器人',error:'电脑需要检查'}\nconst mine=computed(()=>!!ticket.value&&ticket.value.controlId===state.value.controlId)\nconst canCloseBrowser=computed(()=>backend.value==='managed-browser'&&mine.value&&state.value.mode==='human'&&!state.value.canResume)\nconst canSend=computed(()=>connectionOK.value&&!!frame.value&&mine.value&&state.value.mode==='human'&&frame.value?.generation===state.value.generation)\nconst controllable=computed(()=>canSend.value&&!busy.value&&!dragging.value)\nconst computerSettings=ref({vm:true,cloud:true,managedBrowser:false})\nconst targets=computed(()=>{\n const envs=computerSettings.value,info=envTargets.value\n const list:{key:string;label:string;backend:ComputerBackend;host?:string;disabled?:boolean}[]=[]\n if(envs.cloud)list.push({key:'cloud',label:'云虚拟机'+(loadingTargets.value?' · 正在检查':info?.cloudConfigured?'':' · 未连接'),backend:'cloud',disabled:loadingTargets.value||!info?.cloudConfigured})\n if(envs.vm)list.push({key:'vm',label:'本地虚拟机'+(loadingTargets.value?' · 正在检查':info?.vmEnabled?'':' · 未启用'),backend:'vm',disabled:loadingTargets.value||!info?.vmEnabled})\n if(envs.managedBrowser){const browser=info?.managedBrowser;list.push({key:'managed-browser',label:'托管浏览器'+(loadingTargets.value?' · 正在检查':browser?.available?'':browserCanPrepare(browser)?' · '+(browser?.installation?.status==='failed'?'准备失败':'待准备'):' · 不可用'),backend:'managed-browser',disabled:loadingTargets.value||!browserCanPrepare(browser)})}\n return list\n})\nconst currentKey=computed(()=>targets.value.find(t=>t.backend===backend.value&&(t.host??'')===host.value)?.key??'')\nconst targetAvailable=computed(()=>targets.value.some(t=>t.key===currentKey.value&&!t.disabled))\nwatch([backend,host],([selectedBackend,selectedHost])=>{if(selectedBackend)emit('targetChanged',selectedBackend,selectedHost||undefined)},{immediate:true})\nasync function loadTargets(){\n if(!selected.value)return\n const selectedId=selected.value,id=encodeURIComponent(selectedId)\n loadingTargets.value=true\n try{\n const settings=await apiRequest<{scriptMachine:boolean;serverComputer:boolean;vm:boolean;cloud:boolean;managedBrowser?:boolean}>('/api/app/settings/host-tools')\n if(closed||selectedId!==selected.value)return\n computerSettings.value={vm:settings.vm,cloud:settings.cloud,managedBrowser:settings.managedBrowser===true}\n const [cloud,vm,browser]=await Promise.all([\n computerSettings.value.cloud?apiRequest<{configured:boolean}>(`/api/app/agents/${id}/cloud-computer`).catch(()=>undefined):Promise.resolve(undefined),\n computerSettings.value.vm?apiRequest<{enabled:boolean}>(`/api/app/agents/${id}/local-vm`).catch(()=>undefined):Promise.resolve(undefined),\n computerSettings.value.managedBrowser?apiRequest<ManagedBrowserState>(`/api/app/agents/${id}/managed-browser`).catch(()=>undefined):Promise.resolve(undefined),\n ])\n if(closed||selectedId!==selected.value)return\n envTargets.value={cloudConfigured:!!(cloud as {configured?:boolean}|undefined)?.configured,vmEnabled:!!(vm as {enabled?:boolean}|undefined)?.enabled,managedBrowser:browser}\n if(backend.value==='managed-browser'){managedBrowser.value=browser;browserState.value=browser}\n }finally{if(selectedId===selected.value)loadingTargets.value=false}\n}\nasync function switchTarget(key:string){\n const next=targets.value.find(t=>t.key===key)\n if(!next||next.disabled||key===currentKey.value)return\n await cancelDrag()\n if(ticket.value){\n try{await post('/giveback',{...ticket.value,notes:''})}catch(cause){error.value=cause instanceof Error?cause.message:'请先交还当前电脑';return}\n }\n backend.value=next.backend;host.value=next.host??''\n preparation?.abort()\n clearTimeout(clickTimer);pendingClick=undefined;browserState.value=undefined;managedBrowser.value=undefined\n inputs.length=0;connectionOK.value=false;generation++;takeRequestId=undefined;frame.value=undefined;ticket.value=undefined;state.value={mode:'off'};actionError='';error.value=''\n await refresh()\n}\nconst endpoint=()=>`/api/app/agents/${encodeURIComponent(selected.value)}/computer`\nconst query=()=>{const q=new URLSearchParams();if(backend.value)q.set('backend',backend.value);if(backend.value==='desktop'&&host.value)q.set('host',host.value);const qs=q.toString();return qs?'?'+qs:''}\nasync function post(path:string,body:unknown){return apiRequest<any>(endpoint()+path+query(),{method:'POST',body:body as JsonValue})}\nasync function refresh(){\n if(polling||busy.value||closed||!selected.value||document.hidden||(!targetAvailable.value&&!(backend.value==='managed-browser'&&computerSettings.value.managedBrowser)))return\n polling=true;const current=++generation,base=endpoint(),targetQuery=query(),id=selected.value\n try{\n if(backend.value==='managed-browser'){\n const browser=await apiRequest<ManagedBrowserState>(`/api/app/agents/${encodeURIComponent(id)}/managed-browser`).catch(cause=>{\n if(current===generation&&!closed){managedBrowser.value=undefined;browserState.value=undefined;if(envTargets.value)envTargets.value.managedBrowser=undefined;state.value={mode:'off',backend:'managed-browser'}}\n throw cause\n })\n if(current!==generation||closed)return\n managedBrowser.value=browser;browserState.value=browser\n if(envTargets.value)envTargets.value.managedBrowser=browser\n if(!browser.available){state.value={mode:'off',backend:'managed-browser'};connectionOK.value=false;frame.value=undefined;error.value=actionError;return}\n }\n const status=await apiRequest<ComputerControlStatus>(base+targetQuery)\n if(current!==generation||closed)return\n state.value=status\n connectionOK.value=true\n if(ticket.value&&status.controlId!==ticket.value.controlId&&status.mode!=='pausing')ticket.value=undefined\n if(mine.value&&Date.now()-lastRenew>8000){await post('/renew',ticket.value);lastRenew=Date.now()}\n if(status.backend==='browser'){\n const browser=await apiRequest<BotBrowserState>(`/api/app/agents/${encodeURIComponent(selected.value)}/browser`)\n if(current!==generation||closed)return\n browserState.value=browser;managedBrowser.value=undefined\n }else if(status.backend!=='managed-browser'){browserState.value=undefined;managedBrowser.value=undefined}\n if(status.mode!=='off'&&(!['browser','managed-browser'].includes(status.backend??'')||browserState.value?.open)){\n const image=await apiRequest<ComputerFrame>(base+'/frame'+targetQuery)\n if(current===generation&&!closed)frame.value=image\n }else frame.value=undefined\n error.value=actionError\n }catch(cause){if(current===generation&&!closed){connectionOK.value=false;frame.value=undefined;inputs.length=0;error.value=cause instanceof Error?cause.message:'无法读取电脑状态';{const code=Number((cause as {status?:number}).status)\n if([401,410].includes(code)||(code===403&&ticket.value)){ticket.value=undefined;frame.value=undefined;state.value={mode:'off'}}}}}\n finally{polling=false}\n}\nasync function run(action:()=>Promise<void>){\n if(busy.value)return;generation++;busy.value=true;actionError='';error.value=''\n let failure=''\n try{await action()}catch(cause){failure=cause instanceof Error?cause.message:'电脑操作失败'}finally{busy.value=false;if(!closed){await refresh();if(failure){actionError=failure;error.value=failure}void flushInputs()}}\n}\nasync function recheckBrowser(){if(busy.value)return;recheckingBrowser.value=true;try{await run(loadTargets)}finally{recheckingBrowser.value=false}}\nfunction take(){return run(async()=>{\n if(!targetAvailable.value)throw new Error('所选电脑不可用,请重新选择')\n const id=selected.value,target=backend.value,base=endpoint(),targetQuery=query(),controller=new AbortController()\n preparation=controller\n try{\n if(target==='managed-browser'){\n preparing.value=!managedBrowser.value?.available\n await prepareManagedBrowser(id,managedBrowser.value,controller.signal,browser=>{\n if(!closed&&id===selected.value&&target===backend.value){managedBrowser.value=browser;browserState.value=browser;if(envTargets.value)envTargets.value.managedBrowser=browser}\n })\n }\n controller.signal.throwIfAborted()\n if(closed||id!==selected.value||target!==backend.value)return\n preparing.value=false;takeRequestId??=createUuid()\n // Do not abort a submitted takeover: retain its receipt so a late response\n // can be returned to the exact original target after this view closes.\n const value=await apiRequest<any>(base+'/take'+targetQuery,{method:'POST',body:{requestId:takeRequestId}})\n const credentials={controlId:value.controlId,token:value.token}\n if(closed||controller.signal.aborted||id!==selected.value||target!==backend.value){await apiRequest(base+'/giveback'+targetQuery,{method:'POST',body:{...credentials,notes:''}});return}\n ticket.value=credentials;state.value=value;lastRenew=0\n }catch(cause){if(!controller.signal.aborted)throw cause}\n finally{if(preparation===controller){preparation=undefined;preparing.value=false}}\n})}\nconst inputs:ComputerInput[]=[];let flushing=false,pendingClick:ComputerInput|undefined,clickTimer:ReturnType<typeof setTimeout>|undefined\nfunction send(action:ComputerInput){\n if(!canSend.value||closed||dragging.value)return\n if(pendingClick){inputs.push(pendingClick);pendingClick=undefined;clearTimeout(clickTimer)}\n const last=inputs.at(-1)\n if(action.kind==='text'&&last?.kind==='text'&&last.text.length+action.text.length<=16000)last.text+=action.text\n else if(inputs.length<64)inputs.push(action)\n else {error.value='输入过快,请稍后再试';return}\n void flushInputs()\n}\nasync function flushInputs(){\n if(flushing||busy.value||!inputs.length||closed)return\n if(!canSend.value||!frame.value||!ticket.value){inputs.length=0;return}\n flushing=true\n const action=inputs.shift()!,image=frame.value,credentials=ticket.value\n try{await run(async()=>{await post('/input',{...credentials,requestId:createUuid(),generation:image.generation,frameId:image.id,action})});if(error.value)inputs.length=0}\n finally{flushing=false;if(inputs.length)setTimeout(()=>void flushInputs(),0)}\n}\nfunction browserAction(action:Record<string,string>){\n if(!controllable.value||!frame.value||!ticket.value)return\n const f=frame.value,credentials=ticket.value\n void run(async()=>{\n if(backend.value==='managed-browser')await apiRequest(`/api/app/agents/${encodeURIComponent(selected.value)}/managed-browser/action`,{method:'POST',body:{...credentials,requestId:createUuid(),generation:f.generation,action}})\n else await post('/browser',{...credentials,requestId:createUuid(),generation:f.generation,frameId:f.id,action})\n })\n}\nfunction point(event:MouseEvent|PointerEvent,clamp=false){\n const r=(event.currentTarget as HTMLImageElement).getBoundingClientRect(),f=frame.value!\n const scale=Math.min(r.width/f.width,r.height/f.height),left=r.left+(r.width-f.width*scale)/2,top=r.top+(r.height-f.height*scale)/2\n const x=Math.floor((event.clientX-left)/scale),y=Math.floor((event.clientY-top)/scale)\n if(clamp)return {x:Math.max(0,Math.min(f.width-1,x)),y:Math.max(0,Math.min(f.height-1,y))}\n if(x<0||y<0||x>=f.width||y>=f.height)return\n return {x,y}\n}\nlet dragStart:{x:number;y:number;clientX:number;clientY:number;pointerId:number;element:HTMLElement}|undefined,dragged=false,liveDrag:BrowserDragSession|undefined\nfunction down(event:PointerEvent){\n if(!controllable.value||dragStart||event.button!==0||inputs.length)return\n const element=event.currentTarget as HTMLElement;element.focus({preventScroll:true})\n const p=point(event);if(!p)return\n dragged=false;dragStart={...p,clientX:event.clientX,clientY:event.clientY,pointerId:event.pointerId,element};element.setPointerCapture(event.pointerId)\n}\nfunction startLiveDrag(start:{x:number;y:number}){\n const base=endpoint(),targetQuery=query(),credentials=ticket.value!,controlGeneration=frame.value!.generation\n let image=frame.value!\n clearTimeout(clickTimer);pendingClick=undefined;error.value='';dragging.value=true\n const current=()=>!closed&&endpoint()===base&&query()===targetQuery&&ticket.value?.controlId===credentials.controlId&&state.value.generation===controlGeneration\n const session=new BrowserDragSession(start,async action=>{\n if(action.phase!=='cancel'&&(!current()||!canSend.value))throw new Error('控制权已变化,请重新接管')\n await apiRequest(base+'/input'+targetQuery,{method:'POST',timeoutMs:5000,body:{...credentials,requestId:createUuid(),generation:controlGeneration,frameId:image.id,action:{...action}}})\n if(action.phase==='cancel')return\n const next=await apiRequest<ComputerFrame>(base+'/frame'+targetQuery,{timeoutMs:5000})\n if(!current()||next.generation!==controlGeneration)throw new Error('控制权已变化,请重新接管')\n image=next;frame.value=next\n },cause=>{if(current())error.value=cause instanceof Error?cause.message:'拖动失败,请重新按下鼠标'})\n liveDrag=session\n void session.done.then(async()=>{if(liveDrag===session){liveDrag=undefined;dragging.value=false;const failure=error.value;await refresh();if(current()&&failure)error.value=failure}})\n}\nfunction move(event:PointerEvent){\n const start=dragStart\n if(!start||event.pointerId!==start.pointerId||!canSend.value)return\n if(backend.value!=='managed-browser')return\n if(!liveDrag&&Math.hypot(event.clientX-start.clientX,event.clientY-start.clientY)>5){dragged=true;startLiveDrag(start)}\n const p=point(event,true);if(p)liveDrag?.move(p)\n}\nfunction releaseCapture(){const start=dragStart;dragStart=undefined;if(start?.element.hasPointerCapture(start.pointerId))start.element.releasePointerCapture(start.pointerId)}\nfunction cancelDrag(){releaseCapture();if(liveDrag){dragged=true;return liveDrag.cancel()}return Promise.resolve()}\nfunction up(event:PointerEvent){\n const start=dragStart;if(!start||event.pointerId!==start.pointerId)return\n if(!canSend.value){void cancelDrag();return}\n move(event)\n const end=point(event,true);releaseCapture()\n if(liveDrag){if(end)liveDrag.end(end);else void cancelDrag();return}\n if(end&&Math.hypot(event.clientX-start.clientX,event.clientY-start.clientY)>5){dragged=true;send({kind:'drag',fromX:start.x,fromY:start.y,toX:end.x,toY:end.y})}\n}\nconst windowBlur=()=>{void cancelDrag()},visibility=()=>{if(document.hidden)void cancelDrag()}\nwatch(canSend,allowed=>{if(!allowed)void cancelDrag()})\nfunction wheel(event:WheelEvent){if(!canSend.value)return;event.preventDefault();send({kind:'scroll',direction:Math.abs(event.deltaX)>Math.abs(event.deltaY)?event.deltaX>0?'right':'left':event.deltaY>0?'down':'up',amount:Math.min(20,Math.max(1,Math.ceil(Math.abs(event.deltaY||event.deltaX)/80)))})}\nfunction keydown(event:KeyboardEvent){if(!canSend.value)return;event.preventDefault();event.stopPropagation();if(event.key==='Escape'){void cancelDrag();(event.currentTarget as HTMLElement).blur();return}const modifiers=[event.ctrlKey||(backend.value==='managed-browser'&&event.metaKey)?'ctrl':'',event.altKey?'alt':'',event.shiftKey?'shift':'',event.metaKey&&backend.value!=='managed-browser'?'super':''].filter(Boolean);if(event.key.length===1&&!event.ctrlKey&&!event.altKey&&!event.metaKey)send({kind:'text',text:event.key});else if(!['Control','Alt','Shift','Meta'].includes(event.key))send({kind:'key',key:({Enter:'Return',ArrowUp:'Up',ArrowDown:'Down',ArrowLeft:'Left',ArrowRight:'Right',Backspace:'BackSpace'} as Record<string,string>)[event.key]??event.key,modifiers})}\nfunction click(event:MouseEvent){\n if(dragged){dragged=false;return}\n if(!canSend.value||!frame.value)return\n const p=point(event);if(!p)return\n const action:ComputerInput={kind:'click',...p,button:event.button===2?'right':'left',count:event.detail>1?2:1}\n if(event.button===2||event.detail>1){clearTimeout(clickTimer);pendingClick=undefined;send(action)}\n else {if(pendingClick){const old=pendingClick;pendingClick=undefined;clearTimeout(clickTimer);send(old)}pendingClick=action;clickTimer=setTimeout(()=>{const next=pendingClick;pendingClick=undefined;if(next)send(next)},180)}\n}\nasync function giveBack(){await cancelDrag();if(ticket.value){await post('/giveback',{...ticket.value,notes:notes.value});ticket.value=undefined;takeRequestId=undefined;notes.value=''}}\nasync function closeBrowser(){\n if(busy.value||!canCloseBrowser.value||!ticket.value)return\n await cancelDrag()\n const id=selected.value,credentials=ticket.value\n closingBrowser.value=true\n try{await run(async()=>{\n clearTimeout(clickTimer);pendingClick=undefined;inputs.length=0\n const status=await apiRequest<ComputerControlStatus>(`/api/app/agents/${encodeURIComponent(id)}/managed-browser/close`,{method:'POST',body:credentials})\n if(closed||selected.value!==id||backend.value!=='managed-browser'||ticket.value!==credentials)return\n state.value=status;ticket.value=undefined;takeRequestId=undefined;notes.value='';frame.value=undefined;connectionOK.value=false;controlsOpen.value=false\n emit('changed')\n })}finally{closingBrowser.value=false}\n}\nasync function close(){if(preparing.value){preparation?.abort();closed=true;dialog.value?.close?.();emit('close');return}if(busy.value)return;await run(giveBack);if(!ticket.value){closed=true;if(!props.embedded&&!props.standalone)dialog.value?.close();emit('close')}}\nasync function fullscreen(){try{if(document.fullscreenElement)await document.exitFullscreen();else await dialog.value?.requestFullscreen()}catch{error.value='无法进入全屏,请使用窗口最大化'}}\nasync function cycle(){await refresh();if(!closed)timer=setTimeout(()=>void cycle(),1200)}\nwatch(selected,()=>{\n void cancelDrag()\n preparation?.abort()\n clearTimeout(clickTimer);pendingClick=undefined;browserState.value=undefined;managedBrowser.value=undefined\n inputs.length=0;connectionOK.value=false;generation++;takeRequestId=undefined;frame.value=undefined;ticket.value=undefined;state.value={mode:'off'};actionError='';error.value=''\n backend.value=props.backend;host.value=props.host??(props.backend==='desktop'?'local':'');envTargets.value=undefined\n void loadTargets().then(async()=>{if(!backend.value){const first=targets.value.find(t=>!t.disabled);if(first)await switchTarget(first.key)}await refresh()}).catch(cause=>{error.value=cause instanceof Error?cause.message:'无法读取电脑列表'})\n})\nonMounted(async()=>{\n window.addEventListener('blur',windowBlur);document.addEventListener('visibilitychange',visibility)\n if(!props.embedded&&!props.standalone)dialog.value?.showModal()\n try{\n await loadTargets()\n if(closed)return\n if(!backend.value){const first=targets.value.find(t=>!t.disabled);if(first)await switchTarget(first.key)}\n if(!targetAvailable.value&&backend.value!=='managed-browser')throw new Error('所选电脑不可用,请重新选择;未打开其他电脑')\n if(props.autoTake&&!closed&&targetAvailable.value)await take()\n }catch(cause){error.value=cause instanceof Error?cause.message:'无法读取电脑列表'}\n if(!closed)void cycle()\n})\nonBeforeUnmount(()=>{void cancelDrag();window.removeEventListener('blur',windowBlur);document.removeEventListener('visibilitychange',visibility);preparation?.abort();if(ticket.value)void post('/giveback',{...ticket.value,notes:notes.value}).catch(()=>{});inputs.length=0;pendingClick=undefined;clearTimeout(clickTimer);closed=true;generation++;clearTimeout(timer)})\ndefineExpose({take,close,releaseControl:async()=>{if(preparing.value){preparation?.abort();return}if(busy.value)throw new Error('请等待当前操作完成');await run(giveBack);if(ticket.value)throw new Error(error.value||'请先交还控制权')},hasControl:()=>!!ticket.value})\n</script>\n<template>\n <Teleport to=\"body\" :disabled=\"embedded||standalone\">\n <component :is=\"embedded||standalone?'section':'dialog'\" ref=\"dialog\" class=\"computer-panel\" :class=\"{embedded,standalone}\" :aria-label=\"targets.find(t=>t.key===currentKey)?.label||'电脑桌面'\" @cancel.prevent=\"close\">\n <header>\n <div><strong>{{backend==='managed-browser'?'托管浏览器':backend==='cloud'?'云虚拟机':'本地虚拟机'}}</strong><small>{{ preparing?'正在准备浏览器':labels[state.mode] }}</small></div>\n <select v-if=\"targets.length>1\" :value=\"currentKey\" aria-label=\"切换电脑环境\" :disabled=\"!!ticket||busy\" @change=\"switchTarget(($event.target as HTMLSelectElement).value)\"><option v-for=\"target in targets\" :key=\"target.key\" :value=\"target.key\" :disabled=\"target.disabled\">{{ target.label }}</option></select>\n <select v-if=\"agents.length>1\" v-model=\"selected\" aria-label=\"选择电脑\" :disabled=\"!!ticket||busy\"><option v-for=\"agent in agents\" :key=\"agent.id\" :value=\"agent.id\">{{ agent.name }}</option></select>\n <span v-else-if=\"!embedded\">{{ agents[0]?.name }}</span>\n <button v-if=\"embedded&&!ticket\" :disabled=\"busy||state.mode==='human'\" @click=\"emit('controlRequest')\">接管电脑</button>\n <button v-if=\"embedded&&mine\" @click=\"controlsOpen=!controlsOpen\">键盘与操作</button>\n <button v-if=\"embedded&&mine\" :disabled=\"busy||state.mode!=='human'\" @click=\"run(giveBack)\">交还</button>\n <template v-if=\"!embedded\">\n <button v-if=\"mine\" type=\"button\" :aria-expanded=\"controlsOpen\" @click=\"controlsOpen=!controlsOpen\">键盘与操作</button>\n <button type=\"button\" :aria-pressed=\"actualSize\" @click=\"actualSize=!actualSize\">{{actualSize?'适应窗口':'原始大小'}}</button>\n <button type=\"button\" @click=\"fullscreen\">全屏</button>\n </template>\n <button v-if=\"!embedded\" class=\"return-control\" type=\"button\" :disabled=\"busy&&!preparing\" @click=\"close\">{{ backend==='managed-browser' ? (ticket ? '交还并收起' : '收起窗口') : (ticket ? '交还并关闭' : '关闭') }}</button>\n </header>\n <BrowserToolbar v-if=\"state.backend==='browser'||state.backend==='managed-browser'\" :state=\"browserState\" :enabled=\"controllable\" @action=\"browserAction\"/>\n <section v-if=\"installation&&installation.status!=='ready'\" class=\"browser-installation\" :aria-busy=\"preparing\">\n <p role=\"status\">{{ installation.message }}</p>\n <progress v-if=\"installation.status==='installing'||installation.status==='checking'\" :value=\"installation.progress\" max=\"100\" aria-label=\"浏览器准备进度\"/>\n <small v-if=\"preparing\">关闭窗口后仍会继续准备。</small>\n <p v-if=\"installation.error\" role=\"alert\">{{ installation.error }}</p>\n </section>\n <section v-if=\"!loadingTargets&&backend==='managed-browser'&&!managedBrowser?.available\" class=\"browser-availability\" :aria-busy=\"recheckingBrowser\">\n <p v-if=\"!installation\" role=\"status\">{{ managedBrowser?.reason || (computerSettings.managedBrowser?'执行节点暂时不可用,请检查连接后重新检测。':'托管浏览器尚未开放,请在「设置 → 环境与权限 → 运行环境」中开启。') }}</p>\n <button type=\"button\" :disabled=\"busy\" @click=\"recheckBrowser\">{{ recheckingBrowser?'正在检测…':'重新检测' }}</button>\n </section>\n <div class=\"computer-screen\" :class=\"{controlling:canSend,'actual-size':actualSize}\">\n <img v-if=\"frame\" :src=\"`data:image/png;base64,${frame.data}`\" :alt=\"(targets.find(t=>t.key===currentKey)?.label||'电脑')+'当前画面'\" draggable=\"false\" tabindex=\"0\" title=\"点按后可使用键盘、滚轮和拖动;按 Esc 退出键盘控制\" @pointerdown=\"down\" @pointermove=\"move\" @pointerup=\"up\" @pointercancel=\"cancelDrag\" @lostpointercapture=\"dragStart && cancelDrag()\" @blur=\"cancelDrag\" @wheel=\"wheel\" @keydown=\"keydown\" @click=\"click\" @contextmenu.prevent=\"click\">\n <p v-else role=\"status\">{{ loadingTargets?'正在检查电脑环境…':preparing?'准备完成后会自动接管,无需再次点击。':state.mode==='off' ? (backend==='managed-browser'?'浏览器尚未打开。接管后可打开网页。':'打开电脑后可查看桌面。') : '正在读取电脑画面…' }}</p>\n <span v-if=\"frame&&(!mine||frame.generation!==state.generation)\" class=\"view-label\">{{mine?'正在同步画面':state.mode==='off'?'上次画面':'仅查看'}}</span>\n </div>\n <p v-if=\"error||state.error\" class=\"computer-error\" role=\"alert\">{{ error||state.error }}</p>\n <footer v-if=\"controlsOpen||(!embedded&&!ticket)\">\n <p v-if=\"state.backend==='grok'\">多个机器人共享同一台 Grok Bot 云端电脑、工作文件与浏览器资料。</p>\n <p v-else-if=\"state.backend==='local'\">当前显示{{state.hostName||'所选电脑'}}的桌面,点击、输入、按键和滚动都会发送到这台电脑。</p>\n <p v-if=\"backend==='vm'&&selectedAgent?.computerEnvironmentId\">共享电脑:{{ selectedAgent.computerEnvironmentName }}。成员共用工作文件和浏览器资料,可同时执行命令与文件操作,桌面操作交替进行;人工接管时机器人暂停等待。</p>\n <p v-if=\"state.backend==='managed-browser'\">当前是托管浏览器,与电脑桌面和虚拟机分别管理;网页操作无需启动虚拟机。</p>\n <details v-if=\"managedBrowser?.downloads.length\" class=\"browser-downloads\"><summary>浏览器下载({{managedBrowser.downloads.length}})</summary><ul><li v-for=\"file in managedBrowser.downloads\" :key=\"file.id\"><span>{{file.name}}</span><small>{{(file.size/1024).toFixed(1)}} KiB</small></li></ul></details>\n <p v-if=\"!ticket\">查看不会输入任何内容。接管后机器人会暂停,已有操作结束后才允许人工操作。</p>\n <button v-if=\"!ticket&&!targetAvailable&&backend!=='managed-browser'\" type=\"button\" :disabled=\"busy\" @click=\"run(loadTargets)\">重新检查连接</button>\n <button v-if=\"!ticket&&state.mode!=='human'\" type=\"button\" :disabled=\"busy||!targetAvailable\" @click=\"embedded?emit('controlRequest'):take()\">{{ preparing?'正在准备浏览器…':installation?.status==='failed'?'重试并接管浏览器':state.mode==='off'?'打开并控制':'接管电脑' }}</button>\n <template v-if=\"mine\">\n\n <section v-if=\"controlsOpen\" class=\"manual-input\">\n <div class=\"key-row\"><button v-for=\"key in ['Return','Tab','Escape','BackSpace','Up','Down','Left','Right']\" :key=\"key\" type=\"button\" :disabled=\"!controllable\" @click=\"send({kind:'key',key})\">{{ ({Return:'回车',Escape:'Esc',BackSpace:'退格',Up:'↑',Down:'↓',Left:'←',Right:'→'} as Record<string,string>)[key]||key }}</button></div>\n <div class=\"key-row\"><button :disabled=\"!controllable\" @click=\"send({kind:'key',key:'a',modifiers:[state.backend==='local'||state.backend==='browser'?'super':'ctrl']})\">全选</button><button :disabled=\"!controllable\" @click=\"send({kind:'key',key:'c',modifiers:[state.backend==='local'||state.backend==='browser'?'super':'ctrl']})\">复制</button><button :disabled=\"!controllable\" @click=\"send({kind:'key',key:'v',modifiers:[state.backend==='local'||state.backend==='browser'?'super':'ctrl']})\">粘贴</button></div>\n <label>输入到电脑<textarea v-model=\"text\" rows=\"2\" maxlength=\"16000\" placeholder=\"输入文字后点击发送\" :disabled=\"!controllable\" /></label>\n <button type=\"button\" :disabled=\"!controllable||!text\" @click=\"send({kind:'text',text});text=''\">输入文字</button>\n </section>\n <label>交还说明<textarea v-model=\"notes\" rows=\"2\" maxlength=\"4000\" placeholder=\"告诉机器人你完成了什么,以及接下来需要做什么\" /></label>\n <button type=\"button\" :disabled=\"busy||state.mode!=='human'\" @click=\"run(giveBack)\">{{ state.canResume ? '交还机器人并继续' : backend==='managed-browser' ? '结束接管' : '结束控制' }}</button>\n <small v-if=\"backend==='managed-browser'\">{{ managedBrowser?.supportsRetention===true ? '交还或收起只会释放操作权,页面会保留;长时间空闲后可能回收。' : managedBrowser?.supportsRetention===false ? '此节点需更新后才能跨任务保留页面;任务结束后浏览器会关闭。' : '交还或收起会结束当前接管。' }}</small>\n <small v-else>控制只属于当前页面。页面断开后控制权会到期,任务不会自行恢复。</small>\n </template>\n </footer>\n <section v-if=\"canCloseBrowser\" class=\"browser-session-actions\" :aria-busy=\"closingBrowser\">\n <button type=\"button\" :disabled=\"busy\" @click=\"closeBrowser\">{{ closingBrowser?'正在关闭浏览器…':'关闭浏览器' }}</button>\n <small>结束当前浏览器会话并关闭标签页。</small>\n </section>\n </component>\n </Teleport>\n</template>\n<style scoped>\n.browser-session-actions{display:flex;align-items:center;flex-wrap:wrap;gap:12px;padding:12px 20px;border-top:1px solid var(--line)}.browser-session-actions button{min-height:44px;color:var(--danger)}.browser-session-actions small{font-size:12px;line-height:1.6;color:var(--text-secondary)}\n.browser-availability{display:grid;gap:10px;padding:12px 20px;background:var(--surface-soft);color:var(--text-secondary);border-bottom:1px solid var(--line)}.browser-availability p{margin:0;font-size:13px;line-height:1.6;overflow-wrap:anywhere}.browser-availability button{justify-self:start;min-height:44px}\n.browser-installation{display:grid;gap:10px;padding:16px 20px;border-bottom:1px solid var(--line);background:var(--surface-soft);color:var(--text-primary)}.browser-installation p{margin:0;font-size:13px;line-height:1.6;overflow-wrap:anywhere}.browser-installation progress{width:100%;height:6px;accent-color:var(--accent)}.browser-installation [role=alert]{color:var(--danger)}\n.computer-panel.embedded{position:static;width:100%;max-height:none;height:100%;margin:0;border:0;border-radius:0;overflow:auto}.computer-panel.embedded{display:flex;flex-direction:column}.embedded .computer-screen{flex:1;min-height:150px}.embedded .computer-screen img{max-height:calc(100dvh - 250px);object-fit:contain}.embedded footer{max-height:45vh;overflow:auto}.manual-input{display:grid;gap:10px}\n.computer-panel{width:min(1120px,calc(100vw - 32px));max-height:calc(100dvh - 32px);padding:0;border:1px solid var(--line);border-radius:16px;background:var(--surface);color:var(--text-primary);overflow:auto}\n.computer-panel.standalone{display:flex;flex-direction:column;width:100vw;height:100dvh;max-height:none;margin:0;border:0;border-radius:0;overflow:hidden}\n.standalone .computer-screen{flex:1;min-height:0;overflow:hidden}.standalone .computer-screen img{height:100%;width:100%;max-height:100%;max-width:100%;object-fit:contain}.standalone header{flex-shrink:0;background:#1c1f23;color:#f4f4f5}.standalone header small{color:#bec4cc}.standalone header button,.standalone header select{background:#2b2f34;color:#f4f4f5;border-color:#474c54}.standalone header .return-control{background:#f4f4f5;color:#202327;border-color:#f4f4f5}.standalone footer{position:absolute;right:16px;top:78px;width:min(360px,calc(100vw - 64px));max-height:calc(100dvh - 130px);overflow:auto;background:var(--surface);border:1px solid var(--line);border-radius:12px;box-shadow:0 16px 50px #0006;z-index:2}\n.computer-panel:fullscreen{display:flex;flex-direction:column;width:100vw;height:100dvh;max-height:none;border:0;border-radius:0}.computer-panel:fullscreen .computer-screen{flex:1;min-height:0}.computer-panel:fullscreen .computer-screen img{max-height:100%}\n.computer-screen.actual-size{overflow:auto;align-items:flex-start;justify-content:flex-start}.computer-screen.actual-size img{width:auto;height:auto;max-width:none;max-height:none;flex:none}.return-control{font-weight:600}.standalone .computer-error{flex:none;margin:0;padding:10px 18px}\n.computer-panel::backdrop{background:rgba(0,0,0,.45)}header{display:flex;align-items:center;gap:14px;padding:14px 18px;border-bottom:1px solid var(--line)}header div{display:grid;gap:4px;margin-right:auto}small{font-size:12px;color:var(--text-muted);line-height:1.5}\nbutton,select,textarea,input{font:inherit;border:1px solid var(--line);border-radius:8px;background:var(--surface-raised);color:var(--text-primary)}button,select{min-height:44px;padding:8px 12px}button{cursor:pointer}button:disabled{opacity:.5;cursor:default}button:focus-visible,select:focus-visible,textarea:focus-visible,input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}\n.computer-screen{position:relative;display:flex;align-items:center;justify-content:center;min-height:220px;background:#14181e}.computer-screen img{max-width:100%;max-height:58vh;width:auto;height:auto;user-select:none}.controlling img{cursor:crosshair;touch-action:none}.computer-screen img:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.computer-screen p{color:#d8dee7}.view-label{position:absolute;top:10px;left:12px;padding:4px 8px;border-radius:6px;background:rgba(0,0,0,.65);color:white;font-size:12px;pointer-events:none}\nfooter{padding:16px 18px;display:grid;gap:12px}footer p{margin:0;font-size:13px;color:var(--text-secondary);line-height:1.6}label{display:grid;gap:6px;font-size:13px}textarea{padding:10px;min-width:0;resize:vertical}.key-row{display:flex;gap:6px;flex-wrap:wrap}.computer-error{color:var(--danger);padding:0 18px;font-size:13px}\n.browser-downloads{font-size:13px}.browser-downloads summary{cursor:pointer;min-height:44px;display:flex;align-items:center}.browser-downloads ul{margin:0;padding-left:20px;display:grid;gap:8px}.browser-downloads li{overflow-wrap:anywhere}.browser-downloads small{margin-left:8px}\n.sharing-settings{display:grid;gap:12px;padding:12px;border:1px solid var(--line);border-radius:10px}.sharing-settings input:not([type=checkbox]){min-height:44px;padding:8px 10px}.share-option{display:flex;align-items:center;gap:8px;min-height:44px}.share-option input{width:18px;height:18px;flex:none}fieldset{border:1px solid var(--line);border-radius:8px}\n@media(max-width:600px){.computer-panel{width:100vw;max-height:100dvh;border-radius:0}header{flex-wrap:wrap;padding:12px}footer{padding:12px}.computer-screen img{max-height:48vh}}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"ComputerPanel-DdlgOm-B.js","names":[],"sources":["../../src/client/utils/managedBrowser.ts","../../src/client/utils/browserDrag.ts","../../src/client/components/workspace/BrowserToolbar.vue","../../src/client/components/workspace/BrowserToolbar.vue","../../src/client/components/workspace/ComputerPanel.vue","../../src/client/components/workspace/ComputerPanel.vue"],"sourcesContent":["import { apiRequest } from '@/api/client'\nimport type { ManagedBrowserState } from '@shared/managedBrowser'\n\nexport function browserCanPrepare(state?: ManagedBrowserState): boolean {\n return !!state?.enabled && (!!state.available || !!state.installation)\n}\n\n/** Preparing installs the runtime only; taking control remains a separate action. */\nexport async function prepareManagedBrowser(agentId: string, initial: ManagedBrowserState | undefined, signal: AbortSignal, update: (state: ManagedBrowserState) => void): Promise<void> {\n const path = `/api/app/agents/${encodeURIComponent(agentId)}/managed-browser`\n let state = initial ?? await apiRequest<ManagedBrowserState>(path, { signal })\n signal.throwIfAborted()\n if (!browserCanPrepare(state)) throw new Error(state.reason || '托管浏览器不可用,请检查执行节点与授权')\n if (!state.available && state.installation?.status !== 'installing') {\n state = await apiRequest<ManagedBrowserState>(path + '/prepare', { method: 'POST', body: { retry: state.installation?.status === 'failed' }, signal })\n }\n const started = Date.now()\n for (;;) {\n signal.throwIfAborted(); update(state)\n if (state.available) return\n if (state.installation?.status === 'failed') throw new Error(state.installation.error || state.installation.message || '浏览器准备失败,请重试')\n if (!browserCanPrepare(state)) throw new Error(state.reason || '执行节点已不可用,请重新连接后重试')\n if (Date.now() - started > 10 * 60_000) throw new Error('浏览器仍在准备中,可稍后重新接管查看进度')\n await new Promise<void>((resolve, reject) => {\n const abort = () => { clearTimeout(timer); reject(signal.reason ?? new DOMException('Aborted', 'AbortError')) }\n const timer = setTimeout(() => { signal.removeEventListener('abort', abort); resolve() }, 1000)\n signal.addEventListener('abort', abort, { once: true })\n if (signal.aborted) abort()\n })\n state = await apiRequest<ManagedBrowserState>(path, { signal })\n }\n}\n","import type {BrowserPointerAction} from '@shared/managedBrowser'\nimport {createUuid} from './id'\n\ntype Point={x:number;y:number}\n/** One in-flight input, one latest move, and a final release. Never replay a failed movement. */\nexport class BrowserDragSession {\n readonly id=createUuid()\n readonly done:Promise<void>\n private resolve!:()=>void\n private queue:BrowserPointerAction[]=[]\n private running=false\n private ending=false\n private finished=false\n private heartbeat:ReturnType<typeof setInterval>\n private latest:Point\n constructor(start:Point,private send:(action:BrowserPointerAction)=>Promise<void>,private failed:(error:unknown)=>void){\n this.latest={x:start.x,y:start.y};this.done=new Promise(resolve=>{this.resolve=resolve})\n this.heartbeat=setInterval(()=>{if(!this.ending)this.move(this.latest)},1000)\n this.enqueue('start',start)\n }\n move(point:Point){if(!this.ending)this.enqueue('move',point)}\n end(point:Point){if(this.ending)return;this.ending=true;clearInterval(this.heartbeat);this.enqueue('end',point)}\n cancel(){\n if(this.finished)return this.done\n this.ending=true;clearInterval(this.heartbeat);this.queue=[];this.enqueue('cancel',this.latest)\n return this.done\n }\n private enqueue(phase:BrowserPointerAction['phase'],point:Point){\n // Type annotations do not strip the viewer's DOM element and pointer metadata.\n this.latest={x:point.x,y:point.y}\n const action:BrowserPointerAction={kind:'pointer',gestureId:this.id,phase,x:point.x,y:point.y}\n if(phase==='move'&&this.queue.at(-1)?.phase==='move')this.queue[this.queue.length-1]=action\n else this.queue.push(action)\n void this.flush()\n }\n private async flush(){\n if(this.running||this.finished)return\n this.running=true\n try{\n while(this.queue.length){\n const action=this.queue.shift()!\n await this.send(action)\n if(action.phase==='end'||action.phase==='cancel'){this.finish();return}\n }\n }catch(error){\n this.ending=true;this.queue=[];clearInterval(this.heartbeat)\n // The server may have executed a timed-out start/move. Release it explicitly.\n try{await this.send({kind:'pointer',gestureId:this.id,phase:'cancel',...this.latest})}catch{/* Runner also releases idle gestures. */}\n this.failed(error);this.finish()\n }finally{this.running=false}\n }\n private finish(){this.finished=true;this.ending=true;this.queue=[];clearInterval(this.heartbeat);this.resolve()}\n}\n","<script setup lang=\"ts\">\nimport {computed,ref,watch} from 'vue'\nimport type {BotBrowserState} from '@shared/desktopEnvironment'\nconst props=defineProps<{state?:BotBrowserState;enabled:boolean}>(),emit=defineEmits<{action:[action:Record<string,string>]}>()\nconst address=ref(''),editing=ref(false),active=computed(()=>props.state?.tabs.find(t=>t.active))\nwatch(()=>active.value?.url,url=>{if(!editing.value)address.value=url==='about:blank'?'':url??''},{immediate:true})\nfunction navigate(){let url=address.value.trim();if(!url)return;if(!/^https?:\\/\\//i.test(url))url='https://'+url;editing.value=false;emit('action',{kind:'navigate',url})}\n</script>\n<template>\n <div class=\"browser-toolbar\" aria-label=\"机器人浏览器\">\n <div class=\"browser-tabs\" role=\"tablist\" aria-label=\"浏览器标签页\">\n <div v-for=\"tab in state?.tabs\" :key=\"tab.id\" class=\"tab\" :class=\"{active:tab.active}\">\n <button role=\"tab\" :aria-selected=\"tab.active\" :title=\"tab.title\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'select-tab',tabId:tab.id})\">{{tab.title}}</button>\n <button class=\"close-tab\" :aria-label=\"`关闭标签页 ${tab.title}`\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'close-tab',tabId:tab.id})\">×</button>\n </div>\n <button class=\"new-tab\" aria-label=\"新建标签页\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'new-tab',url:'https://www.google.com/'})\">+</button>\n </div>\n <form class=\"navigation\" @submit.prevent=\"navigate\">\n <button type=\"button\" aria-label=\"后退\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'back'})\">←</button>\n <button type=\"button\" aria-label=\"前进\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'forward'})\">→</button>\n <button type=\"button\" aria-label=\"重新加载页面\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'reload'})\">↻</button>\n <input v-model=\"address\" type=\"text\" inputmode=\"url\" autocomplete=\"off\" spellcheck=\"false\" aria-label=\"浏览器地址\" placeholder=\"输入网址\" :disabled=\"!enabled\" @focus=\"editing=true\" @blur=\"editing=false\">\n <button type=\"submit\" :disabled=\"!enabled||!address.trim()\">前往</button>\n </form>\n </div>\n</template>\n<style scoped>\n.browser-toolbar{min-width:0;background:var(--surface-soft);border-bottom:1px solid var(--line)}.browser-tabs{display:flex;overflow-x:auto;gap:4px;padding:7px 10px 0}.tab{display:flex;align-items:center;min-width:90px;max-width:210px;border:1px solid transparent;border-radius:8px 8px 0 0}.tab.active{background:var(--surface);border-color:var(--line);border-bottom-color:var(--surface)}.tab button:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:160px}.browser-toolbar button{font:inherit;font-size:12px;min-width:36px;min-height:40px;background:transparent;border:0;color:var(--text-primary);cursor:pointer}.tab .close-tab{min-width:32px}.browser-toolbar button:disabled{opacity:.45;cursor:default}.navigation{display:flex;align-items:center;gap:3px;padding:5px 10px}.navigation input{flex:1;min-width:0;min-height:40px;border:1px solid var(--line);border-radius:8px;padding:6px 10px;background:var(--surface);color:var(--text-primary);font:inherit;font-size:13px}button:focus-visible,input:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}@media(max-width:600px){.navigation{padding:4px}.browser-toolbar button{min-width:40px;min-height:44px}.tab button:first-child{max-width:130px}}\n</style>\n","<script setup lang=\"ts\">\nimport {computed,ref,watch} from 'vue'\nimport type {BotBrowserState} from '@shared/desktopEnvironment'\nconst props=defineProps<{state?:BotBrowserState;enabled:boolean}>(),emit=defineEmits<{action:[action:Record<string,string>]}>()\nconst address=ref(''),editing=ref(false),active=computed(()=>props.state?.tabs.find(t=>t.active))\nwatch(()=>active.value?.url,url=>{if(!editing.value)address.value=url==='about:blank'?'':url??''},{immediate:true})\nfunction navigate(){let url=address.value.trim();if(!url)return;if(!/^https?:\\/\\//i.test(url))url='https://'+url;editing.value=false;emit('action',{kind:'navigate',url})}\n</script>\n<template>\n <div class=\"browser-toolbar\" aria-label=\"机器人浏览器\">\n <div class=\"browser-tabs\" role=\"tablist\" aria-label=\"浏览器标签页\">\n <div v-for=\"tab in state?.tabs\" :key=\"tab.id\" class=\"tab\" :class=\"{active:tab.active}\">\n <button role=\"tab\" :aria-selected=\"tab.active\" :title=\"tab.title\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'select-tab',tabId:tab.id})\">{{tab.title}}</button>\n <button class=\"close-tab\" :aria-label=\"`关闭标签页 ${tab.title}`\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'close-tab',tabId:tab.id})\">×</button>\n </div>\n <button class=\"new-tab\" aria-label=\"新建标签页\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'new-tab',url:'https://www.google.com/'})\">+</button>\n </div>\n <form class=\"navigation\" @submit.prevent=\"navigate\">\n <button type=\"button\" aria-label=\"后退\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'back'})\">←</button>\n <button type=\"button\" aria-label=\"前进\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'forward'})\">→</button>\n <button type=\"button\" aria-label=\"重新加载页面\" :disabled=\"!enabled\" @click=\"emit('action',{kind:'reload'})\">↻</button>\n <input v-model=\"address\" type=\"text\" inputmode=\"url\" autocomplete=\"off\" spellcheck=\"false\" aria-label=\"浏览器地址\" placeholder=\"输入网址\" :disabled=\"!enabled\" @focus=\"editing=true\" @blur=\"editing=false\">\n <button type=\"submit\" :disabled=\"!enabled||!address.trim()\">前往</button>\n </form>\n </div>\n</template>\n<style scoped>\n.browser-toolbar{min-width:0;background:var(--surface-soft);border-bottom:1px solid var(--line)}.browser-tabs{display:flex;overflow-x:auto;gap:4px;padding:7px 10px 0}.tab{display:flex;align-items:center;min-width:90px;max-width:210px;border:1px solid transparent;border-radius:8px 8px 0 0}.tab.active{background:var(--surface);border-color:var(--line);border-bottom-color:var(--surface)}.tab button:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:160px}.browser-toolbar button{font:inherit;font-size:12px;min-width:36px;min-height:40px;background:transparent;border:0;color:var(--text-primary);cursor:pointer}.tab .close-tab{min-width:32px}.browser-toolbar button:disabled{opacity:.45;cursor:default}.navigation{display:flex;align-items:center;gap:3px;padding:5px 10px}.navigation input{flex:1;min-width:0;min-height:40px;border:1px solid var(--line);border-radius:8px;padding:6px 10px;background:var(--surface);color:var(--text-primary);font:inherit;font-size:13px}button:focus-visible,input:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}@media(max-width:600px){.navigation{padding:4px}.browser-toolbar button{min-width:40px;min-height:44px}.tab button:first-child{max-width:130px}}\n</style>\n","<script setup lang=\"ts\">\nimport type { ComputerBackend, ManagedBrowserState } from '@shared/managedBrowser'\nimport {computed,onBeforeUnmount,onMounted,ref,watch} from 'vue'\nimport {apiRequest} from '@/api/client'\nimport {browserCanPrepare,prepareManagedBrowser} from '@/utils/managedBrowser'\nimport {BrowserDragSession} from '@/utils/browserDrag'\nimport BrowserToolbar from './BrowserToolbar.vue'\nimport type {BotBrowserState} from '@shared/desktopEnvironment'\nimport {createUuid} from '@/utils/id'\nimport type {WorkspaceAgent} from '@shared/workspace'\nimport type {ComputerControlStatus,ComputerFrame,ComputerInput} from '@shared/computerControl'\nimport type {JsonValue} from '@shared/types'\nconst props=defineProps<{agents:WorkspaceAgent[];embedded?:boolean;standalone?:boolean;autoTake?:boolean;backend?:ComputerBackend,host?:string}>(),emit=defineEmits<{close:[];changed:[];controlRequest:[];targetChanged:[backend:ComputerBackend,host?:string]}>()\nconst selected=ref(props.agents[0]?.id??''),dialog=ref<HTMLDialogElement>()\nconst backend=ref(props.backend),host=ref(props.host??(props.backend==='desktop'?'local':'')),envTargets=ref<{cloudConfigured:boolean;vmEnabled:boolean;managedBrowser?:ManagedBrowserState}>()\nconst state=ref<ComputerControlStatus>({mode:'off'}),frame=ref<ComputerFrame>(),ticket=ref<{controlId:string;token:string}>()\nconst controlsOpen=ref(false),actualSize=ref(false),browserState=ref<BotBrowserState>()\nconst dragging=ref(false)\nconst managedBrowser=ref<ManagedBrowserState>()\nconst preparing=ref(false)\nconst recheckingBrowser=ref(false)\nconst closingBrowser=ref(false)\nconst loadingTargets=ref(true)\nlet preparation:AbortController|undefined\nconst installation=computed(()=>backend.value==='managed-browser'?managedBrowser.value?.installation:undefined)\nconst selectedAgent=computed(()=>props.agents.find(agent=>agent.id===selected.value))\nconst error=ref(''),busy=ref(false),text=ref(''),notes=ref(''),connectionOK=ref(false)\nlet takeRequestId:string|undefined\nlet actionError=''\nlet timer:ReturnType<typeof setTimeout>|undefined,closed=false,polling=false,lastRenew=0,generation=0\nconst labels:Record<ComputerControlStatus['mode'],string>={off:'电脑未运行',idle:'电脑空闲 · 仅查看',agent:'机器人正在操作',pausing:'正在等待当前操作结束',human:'人工控制中',resuming:'正在交还机器人',error:'电脑需要检查'}\nconst mine=computed(()=>!!ticket.value&&ticket.value.controlId===state.value.controlId)\nconst canCloseBrowser=computed(()=>backend.value==='managed-browser'&&mine.value&&state.value.mode==='human'&&!state.value.canResume)\nconst canSend=computed(()=>connectionOK.value&&!!frame.value&&mine.value&&state.value.mode==='human'&&frame.value?.generation===state.value.generation)\nconst controllable=computed(()=>canSend.value&&!busy.value&&!dragging.value)\nconst computerSettings=ref({vm:true,cloud:true,managedBrowser:false})\nconst targets=computed(()=>{\n const envs=computerSettings.value,info=envTargets.value\n const list:{key:string;label:string;backend:ComputerBackend;host?:string;disabled?:boolean}[]=[]\n if(envs.cloud)list.push({key:'cloud',label:'云虚拟机'+(loadingTargets.value?' · 正在检查':info?.cloudConfigured?'':' · 未连接'),backend:'cloud',disabled:loadingTargets.value||!info?.cloudConfigured})\n if(envs.vm)list.push({key:'vm',label:'本地虚拟机'+(loadingTargets.value?' · 正在检查':info?.vmEnabled?'':' · 未启用'),backend:'vm',disabled:loadingTargets.value||!info?.vmEnabled})\n if(envs.managedBrowser){const browser=info?.managedBrowser;list.push({key:'managed-browser',label:'托管浏览器'+(loadingTargets.value?' · 正在检查':browser?.available?'':browserCanPrepare(browser)?' · '+(browser?.installation?.status==='failed'?'准备失败':'待准备'):' · 不可用'),backend:'managed-browser',disabled:loadingTargets.value||!browserCanPrepare(browser)})}\n return list\n})\nconst currentKey=computed(()=>targets.value.find(t=>t.backend===backend.value&&(t.host??'')===host.value)?.key??'')\nconst targetAvailable=computed(()=>targets.value.some(t=>t.key===currentKey.value&&!t.disabled))\nwatch([backend,host],([selectedBackend,selectedHost])=>{if(selectedBackend)emit('targetChanged',selectedBackend,selectedHost||undefined)},{immediate:true})\nasync function loadTargets(){\n if(!selected.value)return\n const selectedId=selected.value,id=encodeURIComponent(selectedId)\n loadingTargets.value=true\n try{\n const settings=await apiRequest<{scriptMachine:boolean;serverComputer:boolean;vm:boolean;cloud:boolean;managedBrowser?:boolean}>('/api/app/settings/host-tools')\n if(closed||selectedId!==selected.value)return\n computerSettings.value={vm:settings.vm,cloud:settings.cloud,managedBrowser:settings.managedBrowser===true}\n const [cloud,vm,browser]=await Promise.all([\n computerSettings.value.cloud?apiRequest<{configured:boolean}>(`/api/app/agents/${id}/cloud-computer`).catch(()=>undefined):Promise.resolve(undefined),\n computerSettings.value.vm?apiRequest<{enabled:boolean}>(`/api/app/agents/${id}/local-vm`).catch(()=>undefined):Promise.resolve(undefined),\n computerSettings.value.managedBrowser?apiRequest<ManagedBrowserState>(`/api/app/agents/${id}/managed-browser`).catch(()=>undefined):Promise.resolve(undefined),\n ])\n if(closed||selectedId!==selected.value)return\n envTargets.value={cloudConfigured:!!(cloud as {configured?:boolean}|undefined)?.configured,vmEnabled:!!(vm as {enabled?:boolean}|undefined)?.enabled,managedBrowser:browser}\n if(backend.value==='managed-browser'){managedBrowser.value=browser;browserState.value=browser}\n }finally{if(selectedId===selected.value)loadingTargets.value=false}\n}\nasync function switchTarget(key:string){\n const next=targets.value.find(t=>t.key===key)\n if(!next||next.disabled||key===currentKey.value)return\n await cancelDrag()\n if(ticket.value){\n try{await post('/giveback',{...ticket.value,notes:''})}catch(cause){error.value=cause instanceof Error?cause.message:'请先交还当前电脑';return}\n }\n backend.value=next.backend;host.value=next.host??''\n preparation?.abort()\n clearTimeout(clickTimer);pendingClick=undefined;browserState.value=undefined;managedBrowser.value=undefined\n inputs.length=0;connectionOK.value=false;generation++;takeRequestId=undefined;frame.value=undefined;ticket.value=undefined;state.value={mode:'off'};actionError='';error.value=''\n await refresh()\n}\nconst endpoint=()=>`/api/app/agents/${encodeURIComponent(selected.value)}/computer`\nconst query=()=>{const q=new URLSearchParams();if(backend.value)q.set('backend',backend.value);if(backend.value==='desktop'&&host.value)q.set('host',host.value);const qs=q.toString();return qs?'?'+qs:''}\nasync function post(path:string,body:unknown){return apiRequest<any>(endpoint()+path+query(),{method:'POST',body:body as JsonValue})}\nasync function refresh(){\n if(polling||busy.value||closed||!selected.value||document.hidden||(!targetAvailable.value&&!(backend.value==='managed-browser'&&computerSettings.value.managedBrowser)))return\n polling=true;const current=++generation,base=endpoint(),targetQuery=query(),id=selected.value\n try{\n if(backend.value==='managed-browser'){\n const browser=await apiRequest<ManagedBrowserState>(`/api/app/agents/${encodeURIComponent(id)}/managed-browser`).catch(cause=>{\n if(current===generation&&!closed){managedBrowser.value=undefined;browserState.value=undefined;if(envTargets.value)envTargets.value.managedBrowser=undefined;state.value={mode:'off',backend:'managed-browser'}}\n throw cause\n })\n if(current!==generation||closed)return\n managedBrowser.value=browser;browserState.value=browser\n if(envTargets.value)envTargets.value.managedBrowser=browser\n if(!browser.available){state.value={mode:'off',backend:'managed-browser'};connectionOK.value=false;frame.value=undefined;error.value=actionError;return}\n }\n const status=await apiRequest<ComputerControlStatus>(base+targetQuery)\n if(current!==generation||closed)return\n state.value=status\n connectionOK.value=true\n if(ticket.value&&status.controlId!==ticket.value.controlId&&status.mode!=='pausing')ticket.value=undefined\n if(mine.value&&Date.now()-lastRenew>8000){await post('/renew',ticket.value);lastRenew=Date.now()}\n if(status.backend==='browser'){\n const browser=await apiRequest<BotBrowserState>(`/api/app/agents/${encodeURIComponent(selected.value)}/browser`)\n if(current!==generation||closed)return\n browserState.value=browser;managedBrowser.value=undefined\n }else if(status.backend!=='managed-browser'){browserState.value=undefined;managedBrowser.value=undefined}\n if(status.mode!=='off'&&(!['browser','managed-browser'].includes(status.backend??'')||browserState.value?.open)){\n const image=await apiRequest<ComputerFrame>(base+'/frame'+targetQuery)\n if(current===generation&&!closed)frame.value=image\n }else frame.value=undefined\n error.value=actionError\n }catch(cause){if(current===generation&&!closed){connectionOK.value=false;frame.value=undefined;inputs.length=0;error.value=cause instanceof Error?cause.message:'无法读取电脑状态';{const code=Number((cause as {status?:number}).status)\n if([401,410].includes(code)||(code===403&&ticket.value)){ticket.value=undefined;frame.value=undefined;state.value={mode:'off'}}}}}\n finally{polling=false}\n}\nasync function run(action:()=>Promise<void>){\n if(busy.value)return;generation++;busy.value=true;actionError='';error.value=''\n let failure=''\n try{await action()}catch(cause){failure=cause instanceof Error?cause.message:'电脑操作失败'}finally{busy.value=false;if(!closed){await refresh();if(failure){actionError=failure;error.value=failure}void flushInputs()}}\n}\nasync function recheckBrowser(){if(busy.value)return;recheckingBrowser.value=true;try{await run(loadTargets)}finally{recheckingBrowser.value=false}}\nfunction take(){return run(async()=>{\n if(!targetAvailable.value)throw new Error('所选电脑不可用,请重新选择')\n const id=selected.value,target=backend.value,base=endpoint(),targetQuery=query(),controller=new AbortController()\n preparation=controller\n try{\n if(target==='managed-browser'){\n preparing.value=!managedBrowser.value?.available\n await prepareManagedBrowser(id,managedBrowser.value,controller.signal,browser=>{\n if(!closed&&id===selected.value&&target===backend.value){managedBrowser.value=browser;browserState.value=browser;if(envTargets.value)envTargets.value.managedBrowser=browser}\n })\n }\n controller.signal.throwIfAborted()\n if(closed||id!==selected.value||target!==backend.value)return\n preparing.value=false;takeRequestId??=createUuid()\n // Do not abort a submitted takeover: retain its receipt so a late response\n // can be returned to the exact original target after this view closes.\n const value=await apiRequest<any>(base+'/take'+targetQuery,{method:'POST',body:{requestId:takeRequestId}})\n const credentials={controlId:value.controlId,token:value.token}\n if(closed||controller.signal.aborted||id!==selected.value||target!==backend.value){await apiRequest(base+'/giveback'+targetQuery,{method:'POST',body:{...credentials,notes:''}});return}\n ticket.value=credentials;state.value=value;lastRenew=0\n }catch(cause){if(!controller.signal.aborted)throw cause}\n finally{if(preparation===controller){preparation=undefined;preparing.value=false}}\n})}\nconst inputs:ComputerInput[]=[];let flushing=false,pendingClick:ComputerInput|undefined,clickTimer:ReturnType<typeof setTimeout>|undefined\nfunction send(action:ComputerInput){\n if(!canSend.value||closed||dragging.value)return\n if(pendingClick){inputs.push(pendingClick);pendingClick=undefined;clearTimeout(clickTimer)}\n const last=inputs.at(-1)\n if(action.kind==='text'&&last?.kind==='text'&&last.text.length+action.text.length<=16000)last.text+=action.text\n else if(inputs.length<64)inputs.push(action)\n else {error.value='输入过快,请稍后再试';return}\n void flushInputs()\n}\nasync function flushInputs(){\n if(flushing||busy.value||!inputs.length||closed)return\n if(!canSend.value||!frame.value||!ticket.value){inputs.length=0;return}\n flushing=true\n const action=inputs.shift()!,image=frame.value,credentials=ticket.value\n try{await run(async()=>{await post('/input',{...credentials,requestId:createUuid(),generation:image.generation,frameId:image.id,action})});if(error.value)inputs.length=0}\n finally{flushing=false;if(inputs.length)setTimeout(()=>void flushInputs(),0)}\n}\nfunction browserAction(action:Record<string,string>){\n if(!controllable.value||!frame.value||!ticket.value)return\n const f=frame.value,credentials=ticket.value\n void run(async()=>{\n if(backend.value==='managed-browser')await apiRequest(`/api/app/agents/${encodeURIComponent(selected.value)}/managed-browser/action`,{method:'POST',body:{...credentials,requestId:createUuid(),generation:f.generation,action}})\n else await post('/browser',{...credentials,requestId:createUuid(),generation:f.generation,frameId:f.id,action})\n })\n}\nfunction point(event:MouseEvent|PointerEvent,clamp=false){\n const r=(event.currentTarget as HTMLImageElement).getBoundingClientRect(),f=frame.value!\n const scale=Math.min(r.width/f.width,r.height/f.height),left=r.left+(r.width-f.width*scale)/2,top=r.top+(r.height-f.height*scale)/2\n const x=Math.floor((event.clientX-left)/scale),y=Math.floor((event.clientY-top)/scale)\n if(clamp)return {x:Math.max(0,Math.min(f.width-1,x)),y:Math.max(0,Math.min(f.height-1,y))}\n if(x<0||y<0||x>=f.width||y>=f.height)return\n return {x,y}\n}\nlet dragStart:{x:number;y:number;clientX:number;clientY:number;pointerId:number;element:HTMLElement}|undefined,dragged=false,liveDrag:BrowserDragSession|undefined\nfunction down(event:PointerEvent){\n if(!controllable.value||dragStart||event.button!==0||inputs.length)return\n const element=event.currentTarget as HTMLElement;element.focus({preventScroll:true})\n const p=point(event);if(!p)return\n dragged=false;dragStart={...p,clientX:event.clientX,clientY:event.clientY,pointerId:event.pointerId,element};element.setPointerCapture(event.pointerId)\n}\nfunction startLiveDrag(start:{x:number;y:number}){\n const base=endpoint(),targetQuery=query(),credentials=ticket.value!,controlGeneration=frame.value!.generation\n let image=frame.value!\n clearTimeout(clickTimer);pendingClick=undefined;error.value='';dragging.value=true\n const current=()=>!closed&&endpoint()===base&&query()===targetQuery&&ticket.value?.controlId===credentials.controlId&&state.value.generation===controlGeneration\n const session=new BrowserDragSession(start,async action=>{\n if(action.phase!=='cancel'&&(!current()||!canSend.value))throw new Error('控制权已变化,请重新接管')\n await apiRequest(base+'/input'+targetQuery,{method:'POST',timeoutMs:5000,body:{...credentials,requestId:createUuid(),generation:controlGeneration,frameId:image.id,action:{...action}}})\n if(action.phase==='cancel')return\n const next=await apiRequest<ComputerFrame>(base+'/frame'+targetQuery,{timeoutMs:5000})\n if(!current()||next.generation!==controlGeneration)throw new Error('控制权已变化,请重新接管')\n image=next;frame.value=next\n },cause=>{if(current())error.value=cause instanceof Error?cause.message:'拖动失败,请重新按下鼠标'})\n liveDrag=session\n void session.done.then(async()=>{if(liveDrag===session){liveDrag=undefined;dragging.value=false;const failure=error.value;await refresh();if(current()&&failure)error.value=failure}})\n}\nfunction move(event:PointerEvent){\n const start=dragStart\n if(!start||event.pointerId!==start.pointerId||!canSend.value)return\n if(backend.value!=='managed-browser')return\n if(!liveDrag&&Math.hypot(event.clientX-start.clientX,event.clientY-start.clientY)>5){dragged=true;startLiveDrag(start)}\n const p=point(event,true);if(p)liveDrag?.move(p)\n}\nfunction releaseCapture(){const start=dragStart;dragStart=undefined;if(start?.element.hasPointerCapture(start.pointerId))start.element.releasePointerCapture(start.pointerId)}\nfunction cancelDrag(){releaseCapture();if(liveDrag){dragged=true;return liveDrag.cancel()}return Promise.resolve()}\nfunction up(event:PointerEvent){\n const start=dragStart;if(!start||event.pointerId!==start.pointerId)return\n if(!canSend.value){void cancelDrag();return}\n move(event)\n const end=point(event,true);releaseCapture()\n if(liveDrag){if(end)liveDrag.end(end);else void cancelDrag();return}\n if(end&&Math.hypot(event.clientX-start.clientX,event.clientY-start.clientY)>5){dragged=true;send({kind:'drag',fromX:start.x,fromY:start.y,toX:end.x,toY:end.y})}\n}\nconst windowBlur=()=>{void cancelDrag()},visibility=()=>{if(document.hidden)void cancelDrag()}\nwatch(canSend,allowed=>{if(!allowed)void cancelDrag()})\nfunction wheel(event:WheelEvent){if(!canSend.value)return;event.preventDefault();send({kind:'scroll',direction:Math.abs(event.deltaX)>Math.abs(event.deltaY)?event.deltaX>0?'right':'left':event.deltaY>0?'down':'up',amount:Math.min(20,Math.max(1,Math.ceil(Math.abs(event.deltaY||event.deltaX)/80)))})}\nfunction keydown(event:KeyboardEvent){if(!canSend.value)return;event.preventDefault();event.stopPropagation();if(event.key==='Escape'){void cancelDrag();(event.currentTarget as HTMLElement).blur();return}const modifiers=[event.ctrlKey||(backend.value==='managed-browser'&&event.metaKey)?'ctrl':'',event.altKey?'alt':'',event.shiftKey?'shift':'',event.metaKey&&backend.value!=='managed-browser'?'super':''].filter(Boolean);if(event.key.length===1&&!event.ctrlKey&&!event.altKey&&!event.metaKey)send({kind:'text',text:event.key});else if(!['Control','Alt','Shift','Meta'].includes(event.key))send({kind:'key',key:({Enter:'Return',ArrowUp:'Up',ArrowDown:'Down',ArrowLeft:'Left',ArrowRight:'Right',Backspace:'BackSpace'} as Record<string,string>)[event.key]??event.key,modifiers})}\nfunction click(event:MouseEvent){\n if(dragged){dragged=false;return}\n if(!canSend.value||!frame.value)return\n const p=point(event);if(!p)return\n const action:ComputerInput={kind:'click',...p,button:event.button===2?'right':'left',count:event.detail>1?2:1}\n if(event.button===2||event.detail>1){clearTimeout(clickTimer);pendingClick=undefined;send(action)}\n else {if(pendingClick){const old=pendingClick;pendingClick=undefined;clearTimeout(clickTimer);send(old)}pendingClick=action;clickTimer=setTimeout(()=>{const next=pendingClick;pendingClick=undefined;if(next)send(next)},180)}\n}\nasync function giveBack(){await cancelDrag();if(ticket.value){await post('/giveback',{...ticket.value,notes:notes.value});ticket.value=undefined;takeRequestId=undefined;notes.value=''}}\nasync function closeBrowser(){\n if(busy.value||!canCloseBrowser.value||!ticket.value)return\n await cancelDrag()\n const id=selected.value,credentials=ticket.value\n closingBrowser.value=true\n try{await run(async()=>{\n clearTimeout(clickTimer);pendingClick=undefined;inputs.length=0\n const status=await apiRequest<ComputerControlStatus>(`/api/app/agents/${encodeURIComponent(id)}/managed-browser/close`,{method:'POST',body:credentials})\n if(closed||selected.value!==id||backend.value!=='managed-browser'||ticket.value!==credentials)return\n state.value=status;ticket.value=undefined;takeRequestId=undefined;notes.value='';frame.value=undefined;connectionOK.value=false;controlsOpen.value=false\n emit('changed')\n })}finally{closingBrowser.value=false}\n}\nasync function close(){if(preparing.value){preparation?.abort();closed=true;dialog.value?.close?.();emit('close');return}if(busy.value)return;await run(giveBack);if(!ticket.value){closed=true;if(!props.embedded&&!props.standalone)dialog.value?.close();emit('close')}}\nasync function fullscreen(){try{if(document.fullscreenElement)await document.exitFullscreen();else await dialog.value?.requestFullscreen()}catch{error.value='无法进入全屏,请使用窗口最大化'}}\nasync function cycle(){await refresh();if(!closed)timer=setTimeout(()=>void cycle(),1200)}\nwatch(selected,()=>{\n void cancelDrag()\n preparation?.abort()\n clearTimeout(clickTimer);pendingClick=undefined;browserState.value=undefined;managedBrowser.value=undefined\n inputs.length=0;connectionOK.value=false;generation++;takeRequestId=undefined;frame.value=undefined;ticket.value=undefined;state.value={mode:'off'};actionError='';error.value=''\n backend.value=props.backend;host.value=props.host??(props.backend==='desktop'?'local':'');envTargets.value=undefined\n void loadTargets().then(async()=>{if(!backend.value){const first=targets.value.find(t=>!t.disabled);if(first)await switchTarget(first.key)}await refresh()}).catch(cause=>{error.value=cause instanceof Error?cause.message:'无法读取电脑列表'})\n})\nonMounted(async()=>{\n window.addEventListener('blur',windowBlur);document.addEventListener('visibilitychange',visibility)\n if(!props.embedded&&!props.standalone)dialog.value?.showModal()\n try{\n await loadTargets()\n if(closed)return\n if(!backend.value){const first=targets.value.find(t=>!t.disabled);if(first)await switchTarget(first.key)}\n if(!targetAvailable.value&&backend.value!=='managed-browser')throw new Error('所选电脑不可用,请重新选择;未打开其他电脑')\n if(props.autoTake&&!closed&&targetAvailable.value)await take()\n }catch(cause){error.value=cause instanceof Error?cause.message:'无法读取电脑列表'}\n if(!closed)void cycle()\n})\nonBeforeUnmount(()=>{void cancelDrag();window.removeEventListener('blur',windowBlur);document.removeEventListener('visibilitychange',visibility);preparation?.abort();if(ticket.value)void post('/giveback',{...ticket.value,notes:notes.value}).catch(()=>{});inputs.length=0;pendingClick=undefined;clearTimeout(clickTimer);closed=true;generation++;clearTimeout(timer)})\ndefineExpose({take,close,releaseControl:async()=>{if(preparing.value){preparation?.abort();return}if(busy.value)throw new Error('请等待当前操作完成');await run(giveBack);if(ticket.value)throw new Error(error.value||'请先交还控制权')},hasControl:()=>!!ticket.value})\n</script>\n<template>\n <Teleport to=\"body\" :disabled=\"embedded||standalone\">\n <component :is=\"embedded||standalone?'section':'dialog'\" ref=\"dialog\" class=\"computer-panel\" :class=\"{embedded,standalone}\" :aria-label=\"targets.find(t=>t.key===currentKey)?.label||'电脑桌面'\" @cancel.prevent=\"close\">\n <header>\n <div><strong>{{backend==='managed-browser'?'托管浏览器':backend==='cloud'?'云虚拟机':'本地虚拟机'}}</strong><small>{{ preparing?'正在准备浏览器':labels[state.mode] }}</small></div>\n <select v-if=\"targets.length>1\" :value=\"currentKey\" aria-label=\"切换电脑环境\" :disabled=\"!!ticket||busy\" @change=\"switchTarget(($event.target as HTMLSelectElement).value)\"><option v-for=\"target in targets\" :key=\"target.key\" :value=\"target.key\" :disabled=\"target.disabled\">{{ target.label }}</option></select>\n <select v-if=\"agents.length>1\" v-model=\"selected\" aria-label=\"选择电脑\" :disabled=\"!!ticket||busy\"><option v-for=\"agent in agents\" :key=\"agent.id\" :value=\"agent.id\">{{ agent.name }}</option></select>\n <span v-else-if=\"!embedded\">{{ agents[0]?.name }}</span>\n <button v-if=\"embedded&&!ticket\" :disabled=\"busy||state.mode==='human'\" @click=\"emit('controlRequest')\">接管电脑</button>\n <button v-if=\"embedded&&mine\" @click=\"controlsOpen=!controlsOpen\">键盘与操作</button>\n <button v-if=\"embedded&&mine\" :disabled=\"busy||state.mode!=='human'\" @click=\"run(giveBack)\">交还</button>\n <template v-if=\"!embedded\">\n <button v-if=\"mine\" type=\"button\" :aria-expanded=\"controlsOpen\" @click=\"controlsOpen=!controlsOpen\">键盘与操作</button>\n <button type=\"button\" :aria-pressed=\"actualSize\" @click=\"actualSize=!actualSize\">{{actualSize?'适应窗口':'原始大小'}}</button>\n <button type=\"button\" @click=\"fullscreen\">全屏</button>\n </template>\n <button v-if=\"!embedded\" class=\"return-control\" type=\"button\" :disabled=\"busy&&!preparing\" @click=\"close\">{{ backend==='managed-browser' ? (ticket ? '交还并收起' : '收起窗口') : (ticket ? '交还并关闭' : '关闭') }}</button>\n </header>\n <BrowserToolbar v-if=\"state.backend==='browser'||state.backend==='managed-browser'\" :state=\"browserState\" :enabled=\"controllable\" @action=\"browserAction\"/>\n <section v-if=\"installation&&installation.status!=='ready'\" class=\"browser-installation\" :aria-busy=\"preparing\">\n <p role=\"status\">{{ installation.message }}</p>\n <progress v-if=\"installation.status==='installing'||installation.status==='checking'\" :value=\"installation.progress\" max=\"100\" aria-label=\"浏览器准备进度\"/>\n <small v-if=\"preparing\">关闭窗口后仍会继续准备。</small>\n <p v-if=\"installation.error\" role=\"alert\">{{ installation.error }}</p>\n </section>\n <section v-if=\"!loadingTargets&&backend==='managed-browser'&&!managedBrowser?.available\" class=\"browser-availability\" :aria-busy=\"recheckingBrowser\">\n <p v-if=\"!installation\" role=\"status\">{{ managedBrowser?.reason || (computerSettings.managedBrowser?'执行节点暂时不可用,请检查连接后重新检测。':'托管浏览器尚未开放,请在「设置 → 环境与权限 → 运行环境」中开启。') }}</p>\n <button type=\"button\" :disabled=\"busy\" @click=\"recheckBrowser\">{{ recheckingBrowser?'正在检测…':'重新检测' }}</button>\n </section>\n <div class=\"computer-screen\" :class=\"{controlling:canSend,'actual-size':actualSize}\">\n <img v-if=\"frame\" :src=\"`data:image/png;base64,${frame.data}`\" :alt=\"(targets.find(t=>t.key===currentKey)?.label||'电脑')+'当前画面'\" draggable=\"false\" tabindex=\"0\" title=\"点按后可使用键盘、滚轮和拖动;按 Esc 退出键盘控制\" @pointerdown=\"down\" @pointermove=\"move\" @pointerup=\"up\" @pointercancel=\"cancelDrag\" @lostpointercapture=\"dragStart && cancelDrag()\" @blur=\"cancelDrag\" @wheel=\"wheel\" @keydown=\"keydown\" @click=\"click\" @contextmenu.prevent=\"click\">\n <p v-else role=\"status\">{{ loadingTargets?'正在检查电脑环境…':preparing?'准备完成后会自动接管,无需再次点击。':state.mode==='off' ? (backend==='managed-browser'?'浏览器尚未打开。接管后可打开网页。':'打开电脑后可查看桌面。') : '正在读取电脑画面…' }}</p>\n <span v-if=\"frame&&(!mine||frame.generation!==state.generation)\" class=\"view-label\">{{mine?'正在同步画面':state.mode==='off'?'上次画面':'仅查看'}}</span>\n </div>\n <p v-if=\"error||state.error\" class=\"computer-error\" role=\"alert\">{{ error||state.error }}</p>\n <footer v-if=\"controlsOpen||(!embedded&&!ticket)\">\n <p v-if=\"state.backend==='grok'\">多个机器人共享同一台 Grok Bot 云端电脑、工作文件与浏览器资料。</p>\n <p v-else-if=\"state.backend==='local'\">当前显示{{state.hostName||'所选电脑'}}的桌面,点击、输入、按键和滚动都会发送到这台电脑。</p>\n <p v-if=\"backend==='vm'&&selectedAgent?.computerEnvironmentId\">共享电脑:{{ selectedAgent.computerEnvironmentName }}。成员共用工作文件和浏览器资料,可同时执行命令与文件操作,桌面操作交替进行;人工接管时机器人暂停等待。</p>\n <p v-if=\"state.backend==='managed-browser'\">当前是托管浏览器,与电脑桌面和虚拟机分别管理;网页操作无需启动虚拟机。</p>\n <details v-if=\"managedBrowser?.downloads.length\" class=\"browser-downloads\"><summary>浏览器下载({{managedBrowser.downloads.length}})</summary><ul><li v-for=\"file in managedBrowser.downloads\" :key=\"file.id\"><span>{{file.name}}</span><small>{{(file.size/1024).toFixed(1)}} KiB</small></li></ul></details>\n <p v-if=\"!ticket\">查看不会输入任何内容。接管后机器人会暂停,已有操作结束后才允许人工操作。</p>\n <button v-if=\"!ticket&&!targetAvailable&&backend!=='managed-browser'\" type=\"button\" :disabled=\"busy\" @click=\"run(loadTargets)\">重新检查连接</button>\n <button v-if=\"!ticket&&state.mode!=='human'\" type=\"button\" :disabled=\"busy||!targetAvailable\" @click=\"embedded?emit('controlRequest'):take()\">{{ preparing?'正在准备浏览器…':installation?.status==='failed'?'重试并接管浏览器':state.mode==='off'?'打开并控制':'接管电脑' }}</button>\n <template v-if=\"mine\">\n\n <section v-if=\"controlsOpen\" class=\"manual-input\">\n <div class=\"key-row\"><button v-for=\"key in ['Return','Tab','Escape','BackSpace','Up','Down','Left','Right']\" :key=\"key\" type=\"button\" :disabled=\"!controllable\" @click=\"send({kind:'key',key})\">{{ ({Return:'回车',Escape:'Esc',BackSpace:'退格',Up:'↑',Down:'↓',Left:'←',Right:'→'} as Record<string,string>)[key]||key }}</button></div>\n <div class=\"key-row\"><button :disabled=\"!controllable\" @click=\"send({kind:'key',key:'a',modifiers:[state.backend==='local'||state.backend==='browser'?'super':'ctrl']})\">全选</button><button :disabled=\"!controllable\" @click=\"send({kind:'key',key:'c',modifiers:[state.backend==='local'||state.backend==='browser'?'super':'ctrl']})\">复制</button><button :disabled=\"!controllable\" @click=\"send({kind:'key',key:'v',modifiers:[state.backend==='local'||state.backend==='browser'?'super':'ctrl']})\">粘贴</button></div>\n <label>输入到电脑<textarea v-model=\"text\" rows=\"2\" maxlength=\"16000\" placeholder=\"输入文字后点击发送\" :disabled=\"!controllable\" /></label>\n <button type=\"button\" :disabled=\"!controllable||!text\" @click=\"send({kind:'text',text});text=''\">输入文字</button>\n </section>\n <label>交还说明<textarea v-model=\"notes\" rows=\"2\" maxlength=\"4000\" placeholder=\"告诉机器人你完成了什么,以及接下来需要做什么\" /></label>\n <button type=\"button\" :disabled=\"busy||state.mode!=='human'\" @click=\"run(giveBack)\">{{ state.canResume ? '交还机器人并继续' : backend==='managed-browser' ? '结束接管' : '结束控制' }}</button>\n <small v-if=\"backend==='managed-browser'\">{{ managedBrowser?.supportsRetention===true ? '交还或收起只会释放操作权,页面会保留;长时间空闲后可能回收。' : managedBrowser?.supportsRetention===false ? '此节点需更新后才能跨任务保留页面;任务结束后浏览器会关闭。' : '交还或收起会结束当前接管。' }}</small>\n <small v-else>控制只属于当前页面。页面断开后控制权会到期,任务不会自行恢复。</small>\n </template>\n </footer>\n <section v-if=\"canCloseBrowser\" class=\"browser-session-actions\" :aria-busy=\"closingBrowser\">\n <button type=\"button\" :disabled=\"busy\" @click=\"closeBrowser\">{{ closingBrowser?'正在关闭浏览器…':'关闭浏览器' }}</button>\n <small>结束当前浏览器会话并关闭标签页。</small>\n </section>\n </component>\n </Teleport>\n</template>\n<style scoped>\n.browser-session-actions{display:flex;align-items:center;flex-wrap:wrap;gap:12px;padding:12px 20px;border-top:1px solid var(--line)}.browser-session-actions button{min-height:44px;color:var(--danger)}.browser-session-actions small{font-size:12px;line-height:1.6;color:var(--text-secondary)}\n.browser-availability{display:grid;gap:10px;padding:12px 20px;background:var(--surface-soft);color:var(--text-secondary);border-bottom:1px solid var(--line)}.browser-availability p{margin:0;font-size:13px;line-height:1.6;overflow-wrap:anywhere}.browser-availability button{justify-self:start;min-height:44px}\n.browser-installation{display:grid;gap:10px;padding:16px 20px;border-bottom:1px solid var(--line);background:var(--surface-soft);color:var(--text-primary)}.browser-installation p{margin:0;font-size:13px;line-height:1.6;overflow-wrap:anywhere}.browser-installation progress{width:100%;height:6px;accent-color:var(--accent)}.browser-installation [role=alert]{color:var(--danger)}\n.computer-panel.embedded{position:static;width:100%;max-height:none;height:100%;margin:0;border:0;border-radius:0;overflow:auto}.computer-panel.embedded{display:flex;flex-direction:column}.embedded .computer-screen{flex:1;min-height:150px}.embedded .computer-screen img{max-height:calc(100dvh - 250px);object-fit:contain}.embedded footer{max-height:45vh;overflow:auto}.manual-input{display:grid;gap:10px}\n.computer-panel{width:min(1120px,calc(100vw - 32px));max-height:calc(100dvh - 32px);padding:0;border:1px solid var(--line);border-radius:16px;background:var(--surface);color:var(--text-primary);overflow:auto}\n.computer-panel.standalone{display:flex;flex-direction:column;width:100vw;height:100dvh;max-height:none;margin:0;border:0;border-radius:0;overflow:hidden}\n.standalone .computer-screen{flex:1;min-height:0;overflow:hidden}.standalone .computer-screen img{height:100%;width:100%;max-height:100%;max-width:100%;object-fit:contain}.standalone header{flex-shrink:0;background:#1c1f23;color:#f4f4f5}.standalone header small{color:#bec4cc}.standalone header button,.standalone header select{background:#2b2f34;color:#f4f4f5;border-color:#474c54}.standalone header .return-control{background:#f4f4f5;color:#202327;border-color:#f4f4f5}.standalone footer{position:absolute;right:16px;top:78px;width:min(360px,calc(100vw - 64px));max-height:calc(100dvh - 130px);overflow:auto;background:var(--surface);border:1px solid var(--line);border-radius:12px;box-shadow:0 16px 50px #0006;z-index:2}\n.computer-panel:fullscreen{display:flex;flex-direction:column;width:100vw;height:100dvh;max-height:none;border:0;border-radius:0}.computer-panel:fullscreen .computer-screen{flex:1;min-height:0}.computer-panel:fullscreen .computer-screen img{max-height:100%}\n.computer-screen.actual-size{overflow:auto;align-items:flex-start;justify-content:flex-start}.computer-screen.actual-size img{width:auto;height:auto;max-width:none;max-height:none;flex:none}.return-control{font-weight:600}.standalone .computer-error{flex:none;margin:0;padding:10px 18px}\n.computer-panel::backdrop{background:rgba(0,0,0,.45)}header{display:flex;align-items:center;gap:14px;padding:14px 18px;border-bottom:1px solid var(--line)}header div{display:grid;gap:4px;margin-right:auto}small{font-size:12px;color:var(--text-muted);line-height:1.5}\nbutton,select,textarea,input{font:inherit;border:1px solid var(--line);border-radius:8px;background:var(--surface-raised);color:var(--text-primary)}button,select{min-height:44px;padding:8px 12px}button{cursor:pointer}button:disabled{opacity:.5;cursor:default}button:focus-visible,select:focus-visible,textarea:focus-visible,input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}\n.computer-screen{position:relative;display:flex;align-items:center;justify-content:center;min-height:220px;background:#14181e}.computer-screen img{max-width:100%;max-height:58vh;width:auto;height:auto;user-select:none}.controlling img{cursor:crosshair;touch-action:none}.computer-screen img:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.computer-screen p{color:#d8dee7}.view-label{position:absolute;top:10px;left:12px;padding:4px 8px;border-radius:6px;background:rgba(0,0,0,.65);color:white;font-size:12px;pointer-events:none}\nfooter{padding:16px 18px;display:grid;gap:12px}footer p{margin:0;font-size:13px;color:var(--text-secondary);line-height:1.6}label{display:grid;gap:6px;font-size:13px}textarea{padding:10px;min-width:0;resize:vertical}.key-row{display:flex;gap:6px;flex-wrap:wrap}.computer-error{color:var(--danger);padding:0 18px;font-size:13px}\n.browser-downloads{font-size:13px}.browser-downloads summary{cursor:pointer;min-height:44px;display:flex;align-items:center}.browser-downloads ul{margin:0;padding-left:20px;display:grid;gap:8px}.browser-downloads li{overflow-wrap:anywhere}.browser-downloads small{margin-left:8px}\n.sharing-settings{display:grid;gap:12px;padding:12px;border:1px solid var(--line);border-radius:10px}.sharing-settings input:not([type=checkbox]){min-height:44px;padding:8px 10px}.share-option{display:flex;align-items:center;gap:8px;min-height:44px}.share-option input{width:18px;height:18px;flex:none}fieldset{border:1px solid var(--line);border-radius:8px}\n@media(max-width:600px){.computer-panel{width:100vw;max-height:100dvh;border-radius:0}header{flex-wrap:wrap;padding:12px}footer{padding:12px}.computer-screen img{max-height:48vh}}\n</style>\n","<script setup lang=\"ts\">\nimport type { ComputerBackend, ManagedBrowserState } from '@shared/managedBrowser'\nimport {computed,onBeforeUnmount,onMounted,ref,watch} from 'vue'\nimport {apiRequest} from '@/api/client'\nimport {browserCanPrepare,prepareManagedBrowser} from '@/utils/managedBrowser'\nimport {BrowserDragSession} from '@/utils/browserDrag'\nimport BrowserToolbar from './BrowserToolbar.vue'\nimport type {BotBrowserState} from '@shared/desktopEnvironment'\nimport {createUuid} from '@/utils/id'\nimport type {WorkspaceAgent} from '@shared/workspace'\nimport type {ComputerControlStatus,ComputerFrame,ComputerInput} from '@shared/computerControl'\nimport type {JsonValue} from '@shared/types'\nconst props=defineProps<{agents:WorkspaceAgent[];embedded?:boolean;standalone?:boolean;autoTake?:boolean;backend?:ComputerBackend,host?:string}>(),emit=defineEmits<{close:[];changed:[];controlRequest:[];targetChanged:[backend:ComputerBackend,host?:string]}>()\nconst selected=ref(props.agents[0]?.id??''),dialog=ref<HTMLDialogElement>()\nconst backend=ref(props.backend),host=ref(props.host??(props.backend==='desktop'?'local':'')),envTargets=ref<{cloudConfigured:boolean;vmEnabled:boolean;managedBrowser?:ManagedBrowserState}>()\nconst state=ref<ComputerControlStatus>({mode:'off'}),frame=ref<ComputerFrame>(),ticket=ref<{controlId:string;token:string}>()\nconst controlsOpen=ref(false),actualSize=ref(false),browserState=ref<BotBrowserState>()\nconst dragging=ref(false)\nconst managedBrowser=ref<ManagedBrowserState>()\nconst preparing=ref(false)\nconst recheckingBrowser=ref(false)\nconst closingBrowser=ref(false)\nconst loadingTargets=ref(true)\nlet preparation:AbortController|undefined\nconst installation=computed(()=>backend.value==='managed-browser'?managedBrowser.value?.installation:undefined)\nconst selectedAgent=computed(()=>props.agents.find(agent=>agent.id===selected.value))\nconst error=ref(''),busy=ref(false),text=ref(''),notes=ref(''),connectionOK=ref(false)\nlet takeRequestId:string|undefined\nlet actionError=''\nlet timer:ReturnType<typeof setTimeout>|undefined,closed=false,polling=false,lastRenew=0,generation=0\nconst labels:Record<ComputerControlStatus['mode'],string>={off:'电脑未运行',idle:'电脑空闲 · 仅查看',agent:'机器人正在操作',pausing:'正在等待当前操作结束',human:'人工控制中',resuming:'正在交还机器人',error:'电脑需要检查'}\nconst mine=computed(()=>!!ticket.value&&ticket.value.controlId===state.value.controlId)\nconst canCloseBrowser=computed(()=>backend.value==='managed-browser'&&mine.value&&state.value.mode==='human'&&!state.value.canResume)\nconst canSend=computed(()=>connectionOK.value&&!!frame.value&&mine.value&&state.value.mode==='human'&&frame.value?.generation===state.value.generation)\nconst controllable=computed(()=>canSend.value&&!busy.value&&!dragging.value)\nconst computerSettings=ref({vm:true,cloud:true,managedBrowser:false})\nconst targets=computed(()=>{\n const envs=computerSettings.value,info=envTargets.value\n const list:{key:string;label:string;backend:ComputerBackend;host?:string;disabled?:boolean}[]=[]\n if(envs.cloud)list.push({key:'cloud',label:'云虚拟机'+(loadingTargets.value?' · 正在检查':info?.cloudConfigured?'':' · 未连接'),backend:'cloud',disabled:loadingTargets.value||!info?.cloudConfigured})\n if(envs.vm)list.push({key:'vm',label:'本地虚拟机'+(loadingTargets.value?' · 正在检查':info?.vmEnabled?'':' · 未启用'),backend:'vm',disabled:loadingTargets.value||!info?.vmEnabled})\n if(envs.managedBrowser){const browser=info?.managedBrowser;list.push({key:'managed-browser',label:'托管浏览器'+(loadingTargets.value?' · 正在检查':browser?.available?'':browserCanPrepare(browser)?' · '+(browser?.installation?.status==='failed'?'准备失败':'待准备'):' · 不可用'),backend:'managed-browser',disabled:loadingTargets.value||!browserCanPrepare(browser)})}\n return list\n})\nconst currentKey=computed(()=>targets.value.find(t=>t.backend===backend.value&&(t.host??'')===host.value)?.key??'')\nconst targetAvailable=computed(()=>targets.value.some(t=>t.key===currentKey.value&&!t.disabled))\nwatch([backend,host],([selectedBackend,selectedHost])=>{if(selectedBackend)emit('targetChanged',selectedBackend,selectedHost||undefined)},{immediate:true})\nasync function loadTargets(){\n if(!selected.value)return\n const selectedId=selected.value,id=encodeURIComponent(selectedId)\n loadingTargets.value=true\n try{\n const settings=await apiRequest<{scriptMachine:boolean;serverComputer:boolean;vm:boolean;cloud:boolean;managedBrowser?:boolean}>('/api/app/settings/host-tools')\n if(closed||selectedId!==selected.value)return\n computerSettings.value={vm:settings.vm,cloud:settings.cloud,managedBrowser:settings.managedBrowser===true}\n const [cloud,vm,browser]=await Promise.all([\n computerSettings.value.cloud?apiRequest<{configured:boolean}>(`/api/app/agents/${id}/cloud-computer`).catch(()=>undefined):Promise.resolve(undefined),\n computerSettings.value.vm?apiRequest<{enabled:boolean}>(`/api/app/agents/${id}/local-vm`).catch(()=>undefined):Promise.resolve(undefined),\n computerSettings.value.managedBrowser?apiRequest<ManagedBrowserState>(`/api/app/agents/${id}/managed-browser`).catch(()=>undefined):Promise.resolve(undefined),\n ])\n if(closed||selectedId!==selected.value)return\n envTargets.value={cloudConfigured:!!(cloud as {configured?:boolean}|undefined)?.configured,vmEnabled:!!(vm as {enabled?:boolean}|undefined)?.enabled,managedBrowser:browser}\n if(backend.value==='managed-browser'){managedBrowser.value=browser;browserState.value=browser}\n }finally{if(selectedId===selected.value)loadingTargets.value=false}\n}\nasync function switchTarget(key:string){\n const next=targets.value.find(t=>t.key===key)\n if(!next||next.disabled||key===currentKey.value)return\n await cancelDrag()\n if(ticket.value){\n try{await post('/giveback',{...ticket.value,notes:''})}catch(cause){error.value=cause instanceof Error?cause.message:'请先交还当前电脑';return}\n }\n backend.value=next.backend;host.value=next.host??''\n preparation?.abort()\n clearTimeout(clickTimer);pendingClick=undefined;browserState.value=undefined;managedBrowser.value=undefined\n inputs.length=0;connectionOK.value=false;generation++;takeRequestId=undefined;frame.value=undefined;ticket.value=undefined;state.value={mode:'off'};actionError='';error.value=''\n await refresh()\n}\nconst endpoint=()=>`/api/app/agents/${encodeURIComponent(selected.value)}/computer`\nconst query=()=>{const q=new URLSearchParams();if(backend.value)q.set('backend',backend.value);if(backend.value==='desktop'&&host.value)q.set('host',host.value);const qs=q.toString();return qs?'?'+qs:''}\nasync function post(path:string,body:unknown){return apiRequest<any>(endpoint()+path+query(),{method:'POST',body:body as JsonValue})}\nasync function refresh(){\n if(polling||busy.value||closed||!selected.value||document.hidden||(!targetAvailable.value&&!(backend.value==='managed-browser'&&computerSettings.value.managedBrowser)))return\n polling=true;const current=++generation,base=endpoint(),targetQuery=query(),id=selected.value\n try{\n if(backend.value==='managed-browser'){\n const browser=await apiRequest<ManagedBrowserState>(`/api/app/agents/${encodeURIComponent(id)}/managed-browser`).catch(cause=>{\n if(current===generation&&!closed){managedBrowser.value=undefined;browserState.value=undefined;if(envTargets.value)envTargets.value.managedBrowser=undefined;state.value={mode:'off',backend:'managed-browser'}}\n throw cause\n })\n if(current!==generation||closed)return\n managedBrowser.value=browser;browserState.value=browser\n if(envTargets.value)envTargets.value.managedBrowser=browser\n if(!browser.available){state.value={mode:'off',backend:'managed-browser'};connectionOK.value=false;frame.value=undefined;error.value=actionError;return}\n }\n const status=await apiRequest<ComputerControlStatus>(base+targetQuery)\n if(current!==generation||closed)return\n state.value=status\n connectionOK.value=true\n if(ticket.value&&status.controlId!==ticket.value.controlId&&status.mode!=='pausing')ticket.value=undefined\n if(mine.value&&Date.now()-lastRenew>8000){await post('/renew',ticket.value);lastRenew=Date.now()}\n if(status.backend==='browser'){\n const browser=await apiRequest<BotBrowserState>(`/api/app/agents/${encodeURIComponent(selected.value)}/browser`)\n if(current!==generation||closed)return\n browserState.value=browser;managedBrowser.value=undefined\n }else if(status.backend!=='managed-browser'){browserState.value=undefined;managedBrowser.value=undefined}\n if(status.mode!=='off'&&(!['browser','managed-browser'].includes(status.backend??'')||browserState.value?.open)){\n const image=await apiRequest<ComputerFrame>(base+'/frame'+targetQuery)\n if(current===generation&&!closed)frame.value=image\n }else frame.value=undefined\n error.value=actionError\n }catch(cause){if(current===generation&&!closed){connectionOK.value=false;frame.value=undefined;inputs.length=0;error.value=cause instanceof Error?cause.message:'无法读取电脑状态';{const code=Number((cause as {status?:number}).status)\n if([401,410].includes(code)||(code===403&&ticket.value)){ticket.value=undefined;frame.value=undefined;state.value={mode:'off'}}}}}\n finally{polling=false}\n}\nasync function run(action:()=>Promise<void>){\n if(busy.value)return;generation++;busy.value=true;actionError='';error.value=''\n let failure=''\n try{await action()}catch(cause){failure=cause instanceof Error?cause.message:'电脑操作失败'}finally{busy.value=false;if(!closed){await refresh();if(failure){actionError=failure;error.value=failure}void flushInputs()}}\n}\nasync function recheckBrowser(){if(busy.value)return;recheckingBrowser.value=true;try{await run(loadTargets)}finally{recheckingBrowser.value=false}}\nfunction take(){return run(async()=>{\n if(!targetAvailable.value)throw new Error('所选电脑不可用,请重新选择')\n const id=selected.value,target=backend.value,base=endpoint(),targetQuery=query(),controller=new AbortController()\n preparation=controller\n try{\n if(target==='managed-browser'){\n preparing.value=!managedBrowser.value?.available\n await prepareManagedBrowser(id,managedBrowser.value,controller.signal,browser=>{\n if(!closed&&id===selected.value&&target===backend.value){managedBrowser.value=browser;browserState.value=browser;if(envTargets.value)envTargets.value.managedBrowser=browser}\n })\n }\n controller.signal.throwIfAborted()\n if(closed||id!==selected.value||target!==backend.value)return\n preparing.value=false;takeRequestId??=createUuid()\n // Do not abort a submitted takeover: retain its receipt so a late response\n // can be returned to the exact original target after this view closes.\n const value=await apiRequest<any>(base+'/take'+targetQuery,{method:'POST',body:{requestId:takeRequestId}})\n const credentials={controlId:value.controlId,token:value.token}\n if(closed||controller.signal.aborted||id!==selected.value||target!==backend.value){await apiRequest(base+'/giveback'+targetQuery,{method:'POST',body:{...credentials,notes:''}});return}\n ticket.value=credentials;state.value=value;lastRenew=0\n }catch(cause){if(!controller.signal.aborted)throw cause}\n finally{if(preparation===controller){preparation=undefined;preparing.value=false}}\n})}\nconst inputs:ComputerInput[]=[];let flushing=false,pendingClick:ComputerInput|undefined,clickTimer:ReturnType<typeof setTimeout>|undefined\nfunction send(action:ComputerInput){\n if(!canSend.value||closed||dragging.value)return\n if(pendingClick){inputs.push(pendingClick);pendingClick=undefined;clearTimeout(clickTimer)}\n const last=inputs.at(-1)\n if(action.kind==='text'&&last?.kind==='text'&&last.text.length+action.text.length<=16000)last.text+=action.text\n else if(inputs.length<64)inputs.push(action)\n else {error.value='输入过快,请稍后再试';return}\n void flushInputs()\n}\nasync function flushInputs(){\n if(flushing||busy.value||!inputs.length||closed)return\n if(!canSend.value||!frame.value||!ticket.value){inputs.length=0;return}\n flushing=true\n const action=inputs.shift()!,image=frame.value,credentials=ticket.value\n try{await run(async()=>{await post('/input',{...credentials,requestId:createUuid(),generation:image.generation,frameId:image.id,action})});if(error.value)inputs.length=0}\n finally{flushing=false;if(inputs.length)setTimeout(()=>void flushInputs(),0)}\n}\nfunction browserAction(action:Record<string,string>){\n if(!controllable.value||!frame.value||!ticket.value)return\n const f=frame.value,credentials=ticket.value\n void run(async()=>{\n if(backend.value==='managed-browser')await apiRequest(`/api/app/agents/${encodeURIComponent(selected.value)}/managed-browser/action`,{method:'POST',body:{...credentials,requestId:createUuid(),generation:f.generation,action}})\n else await post('/browser',{...credentials,requestId:createUuid(),generation:f.generation,frameId:f.id,action})\n })\n}\nfunction point(event:MouseEvent|PointerEvent,clamp=false){\n const r=(event.currentTarget as HTMLImageElement).getBoundingClientRect(),f=frame.value!\n const scale=Math.min(r.width/f.width,r.height/f.height),left=r.left+(r.width-f.width*scale)/2,top=r.top+(r.height-f.height*scale)/2\n const x=Math.floor((event.clientX-left)/scale),y=Math.floor((event.clientY-top)/scale)\n if(clamp)return {x:Math.max(0,Math.min(f.width-1,x)),y:Math.max(0,Math.min(f.height-1,y))}\n if(x<0||y<0||x>=f.width||y>=f.height)return\n return {x,y}\n}\nlet dragStart:{x:number;y:number;clientX:number;clientY:number;pointerId:number;element:HTMLElement}|undefined,dragged=false,liveDrag:BrowserDragSession|undefined\nfunction down(event:PointerEvent){\n if(!controllable.value||dragStart||event.button!==0||inputs.length)return\n const element=event.currentTarget as HTMLElement;element.focus({preventScroll:true})\n const p=point(event);if(!p)return\n dragged=false;dragStart={...p,clientX:event.clientX,clientY:event.clientY,pointerId:event.pointerId,element};element.setPointerCapture(event.pointerId)\n}\nfunction startLiveDrag(start:{x:number;y:number}){\n const base=endpoint(),targetQuery=query(),credentials=ticket.value!,controlGeneration=frame.value!.generation\n let image=frame.value!\n clearTimeout(clickTimer);pendingClick=undefined;error.value='';dragging.value=true\n const current=()=>!closed&&endpoint()===base&&query()===targetQuery&&ticket.value?.controlId===credentials.controlId&&state.value.generation===controlGeneration\n const session=new BrowserDragSession(start,async action=>{\n if(action.phase!=='cancel'&&(!current()||!canSend.value))throw new Error('控制权已变化,请重新接管')\n await apiRequest(base+'/input'+targetQuery,{method:'POST',timeoutMs:5000,body:{...credentials,requestId:createUuid(),generation:controlGeneration,frameId:image.id,action:{...action}}})\n if(action.phase==='cancel')return\n const next=await apiRequest<ComputerFrame>(base+'/frame'+targetQuery,{timeoutMs:5000})\n if(!current()||next.generation!==controlGeneration)throw new Error('控制权已变化,请重新接管')\n image=next;frame.value=next\n },cause=>{if(current())error.value=cause instanceof Error?cause.message:'拖动失败,请重新按下鼠标'})\n liveDrag=session\n void session.done.then(async()=>{if(liveDrag===session){liveDrag=undefined;dragging.value=false;const failure=error.value;await refresh();if(current()&&failure)error.value=failure}})\n}\nfunction move(event:PointerEvent){\n const start=dragStart\n if(!start||event.pointerId!==start.pointerId||!canSend.value)return\n if(backend.value!=='managed-browser')return\n if(!liveDrag&&Math.hypot(event.clientX-start.clientX,event.clientY-start.clientY)>5){dragged=true;startLiveDrag(start)}\n const p=point(event,true);if(p)liveDrag?.move(p)\n}\nfunction releaseCapture(){const start=dragStart;dragStart=undefined;if(start?.element.hasPointerCapture(start.pointerId))start.element.releasePointerCapture(start.pointerId)}\nfunction cancelDrag(){releaseCapture();if(liveDrag){dragged=true;return liveDrag.cancel()}return Promise.resolve()}\nfunction up(event:PointerEvent){\n const start=dragStart;if(!start||event.pointerId!==start.pointerId)return\n if(!canSend.value){void cancelDrag();return}\n move(event)\n const end=point(event,true);releaseCapture()\n if(liveDrag){if(end)liveDrag.end(end);else void cancelDrag();return}\n if(end&&Math.hypot(event.clientX-start.clientX,event.clientY-start.clientY)>5){dragged=true;send({kind:'drag',fromX:start.x,fromY:start.y,toX:end.x,toY:end.y})}\n}\nconst windowBlur=()=>{void cancelDrag()},visibility=()=>{if(document.hidden)void cancelDrag()}\nwatch(canSend,allowed=>{if(!allowed)void cancelDrag()})\nfunction wheel(event:WheelEvent){if(!canSend.value)return;event.preventDefault();send({kind:'scroll',direction:Math.abs(event.deltaX)>Math.abs(event.deltaY)?event.deltaX>0?'right':'left':event.deltaY>0?'down':'up',amount:Math.min(20,Math.max(1,Math.ceil(Math.abs(event.deltaY||event.deltaX)/80)))})}\nfunction keydown(event:KeyboardEvent){if(!canSend.value)return;event.preventDefault();event.stopPropagation();if(event.key==='Escape'){void cancelDrag();(event.currentTarget as HTMLElement).blur();return}const modifiers=[event.ctrlKey||(backend.value==='managed-browser'&&event.metaKey)?'ctrl':'',event.altKey?'alt':'',event.shiftKey?'shift':'',event.metaKey&&backend.value!=='managed-browser'?'super':''].filter(Boolean);if(event.key.length===1&&!event.ctrlKey&&!event.altKey&&!event.metaKey)send({kind:'text',text:event.key});else if(!['Control','Alt','Shift','Meta'].includes(event.key))send({kind:'key',key:({Enter:'Return',ArrowUp:'Up',ArrowDown:'Down',ArrowLeft:'Left',ArrowRight:'Right',Backspace:'BackSpace'} as Record<string,string>)[event.key]??event.key,modifiers})}\nfunction click(event:MouseEvent){\n if(dragged){dragged=false;return}\n if(!canSend.value||!frame.value)return\n const p=point(event);if(!p)return\n const action:ComputerInput={kind:'click',...p,button:event.button===2?'right':'left',count:event.detail>1?2:1}\n if(event.button===2||event.detail>1){clearTimeout(clickTimer);pendingClick=undefined;send(action)}\n else {if(pendingClick){const old=pendingClick;pendingClick=undefined;clearTimeout(clickTimer);send(old)}pendingClick=action;clickTimer=setTimeout(()=>{const next=pendingClick;pendingClick=undefined;if(next)send(next)},180)}\n}\nasync function giveBack(){await cancelDrag();if(ticket.value){await post('/giveback',{...ticket.value,notes:notes.value});ticket.value=undefined;takeRequestId=undefined;notes.value=''}}\nasync function closeBrowser(){\n if(busy.value||!canCloseBrowser.value||!ticket.value)return\n await cancelDrag()\n const id=selected.value,credentials=ticket.value\n closingBrowser.value=true\n try{await run(async()=>{\n clearTimeout(clickTimer);pendingClick=undefined;inputs.length=0\n const status=await apiRequest<ComputerControlStatus>(`/api/app/agents/${encodeURIComponent(id)}/managed-browser/close`,{method:'POST',body:credentials})\n if(closed||selected.value!==id||backend.value!=='managed-browser'||ticket.value!==credentials)return\n state.value=status;ticket.value=undefined;takeRequestId=undefined;notes.value='';frame.value=undefined;connectionOK.value=false;controlsOpen.value=false\n emit('changed')\n })}finally{closingBrowser.value=false}\n}\nasync function close(){if(preparing.value){preparation?.abort();closed=true;dialog.value?.close?.();emit('close');return}if(busy.value)return;await run(giveBack);if(!ticket.value){closed=true;if(!props.embedded&&!props.standalone)dialog.value?.close();emit('close')}}\nasync function fullscreen(){try{if(document.fullscreenElement)await document.exitFullscreen();else await dialog.value?.requestFullscreen()}catch{error.value='无法进入全屏,请使用窗口最大化'}}\nasync function cycle(){await refresh();if(!closed)timer=setTimeout(()=>void cycle(),1200)}\nwatch(selected,()=>{\n void cancelDrag()\n preparation?.abort()\n clearTimeout(clickTimer);pendingClick=undefined;browserState.value=undefined;managedBrowser.value=undefined\n inputs.length=0;connectionOK.value=false;generation++;takeRequestId=undefined;frame.value=undefined;ticket.value=undefined;state.value={mode:'off'};actionError='';error.value=''\n backend.value=props.backend;host.value=props.host??(props.backend==='desktop'?'local':'');envTargets.value=undefined\n void loadTargets().then(async()=>{if(!backend.value){const first=targets.value.find(t=>!t.disabled);if(first)await switchTarget(first.key)}await refresh()}).catch(cause=>{error.value=cause instanceof Error?cause.message:'无法读取电脑列表'})\n})\nonMounted(async()=>{\n window.addEventListener('blur',windowBlur);document.addEventListener('visibilitychange',visibility)\n if(!props.embedded&&!props.standalone)dialog.value?.showModal()\n try{\n await loadTargets()\n if(closed)return\n if(!backend.value){const first=targets.value.find(t=>!t.disabled);if(first)await switchTarget(first.key)}\n if(!targetAvailable.value&&backend.value!=='managed-browser')throw new Error('所选电脑不可用,请重新选择;未打开其他电脑')\n if(props.autoTake&&!closed&&targetAvailable.value)await take()\n }catch(cause){error.value=cause instanceof Error?cause.message:'无法读取电脑列表'}\n if(!closed)void cycle()\n})\nonBeforeUnmount(()=>{void cancelDrag();window.removeEventListener('blur',windowBlur);document.removeEventListener('visibilitychange',visibility);preparation?.abort();if(ticket.value)void post('/giveback',{...ticket.value,notes:notes.value}).catch(()=>{});inputs.length=0;pendingClick=undefined;clearTimeout(clickTimer);closed=true;generation++;clearTimeout(timer)})\ndefineExpose({take,close,releaseControl:async()=>{if(preparing.value){preparation?.abort();return}if(busy.value)throw new Error('请等待当前操作完成');await run(giveBack);if(ticket.value)throw new Error(error.value||'请先交还控制权')},hasControl:()=>!!ticket.value})\n</script>\n<template>\n <Teleport to=\"body\" :disabled=\"embedded||standalone\">\n <component :is=\"embedded||standalone?'section':'dialog'\" ref=\"dialog\" class=\"computer-panel\" :class=\"{embedded,standalone}\" :aria-label=\"targets.find(t=>t.key===currentKey)?.label||'电脑桌面'\" @cancel.prevent=\"close\">\n <header>\n <div><strong>{{backend==='managed-browser'?'托管浏览器':backend==='cloud'?'云虚拟机':'本地虚拟机'}}</strong><small>{{ preparing?'正在准备浏览器':labels[state.mode] }}</small></div>\n <select v-if=\"targets.length>1\" :value=\"currentKey\" aria-label=\"切换电脑环境\" :disabled=\"!!ticket||busy\" @change=\"switchTarget(($event.target as HTMLSelectElement).value)\"><option v-for=\"target in targets\" :key=\"target.key\" :value=\"target.key\" :disabled=\"target.disabled\">{{ target.label }}</option></select>\n <select v-if=\"agents.length>1\" v-model=\"selected\" aria-label=\"选择电脑\" :disabled=\"!!ticket||busy\"><option v-for=\"agent in agents\" :key=\"agent.id\" :value=\"agent.id\">{{ agent.name }}</option></select>\n <span v-else-if=\"!embedded\">{{ agents[0]?.name }}</span>\n <button v-if=\"embedded&&!ticket\" :disabled=\"busy||state.mode==='human'\" @click=\"emit('controlRequest')\">接管电脑</button>\n <button v-if=\"embedded&&mine\" @click=\"controlsOpen=!controlsOpen\">键盘与操作</button>\n <button v-if=\"embedded&&mine\" :disabled=\"busy||state.mode!=='human'\" @click=\"run(giveBack)\">交还</button>\n <template v-if=\"!embedded\">\n <button v-if=\"mine\" type=\"button\" :aria-expanded=\"controlsOpen\" @click=\"controlsOpen=!controlsOpen\">键盘与操作</button>\n <button type=\"button\" :aria-pressed=\"actualSize\" @click=\"actualSize=!actualSize\">{{actualSize?'适应窗口':'原始大小'}}</button>\n <button type=\"button\" @click=\"fullscreen\">全屏</button>\n </template>\n <button v-if=\"!embedded\" class=\"return-control\" type=\"button\" :disabled=\"busy&&!preparing\" @click=\"close\">{{ backend==='managed-browser' ? (ticket ? '交还并收起' : '收起窗口') : (ticket ? '交还并关闭' : '关闭') }}</button>\n </header>\n <BrowserToolbar v-if=\"state.backend==='browser'||state.backend==='managed-browser'\" :state=\"browserState\" :enabled=\"controllable\" @action=\"browserAction\"/>\n <section v-if=\"installation&&installation.status!=='ready'\" class=\"browser-installation\" :aria-busy=\"preparing\">\n <p role=\"status\">{{ installation.message }}</p>\n <progress v-if=\"installation.status==='installing'||installation.status==='checking'\" :value=\"installation.progress\" max=\"100\" aria-label=\"浏览器准备进度\"/>\n <small v-if=\"preparing\">关闭窗口后仍会继续准备。</small>\n <p v-if=\"installation.error\" role=\"alert\">{{ installation.error }}</p>\n </section>\n <section v-if=\"!loadingTargets&&backend==='managed-browser'&&!managedBrowser?.available\" class=\"browser-availability\" :aria-busy=\"recheckingBrowser\">\n <p v-if=\"!installation\" role=\"status\">{{ managedBrowser?.reason || (computerSettings.managedBrowser?'执行节点暂时不可用,请检查连接后重新检测。':'托管浏览器尚未开放,请在「设置 → 环境与权限 → 运行环境」中开启。') }}</p>\n <button type=\"button\" :disabled=\"busy\" @click=\"recheckBrowser\">{{ recheckingBrowser?'正在检测…':'重新检测' }}</button>\n </section>\n <div class=\"computer-screen\" :class=\"{controlling:canSend,'actual-size':actualSize}\">\n <img v-if=\"frame\" :src=\"`data:image/png;base64,${frame.data}`\" :alt=\"(targets.find(t=>t.key===currentKey)?.label||'电脑')+'当前画面'\" draggable=\"false\" tabindex=\"0\" title=\"点按后可使用键盘、滚轮和拖动;按 Esc 退出键盘控制\" @pointerdown=\"down\" @pointermove=\"move\" @pointerup=\"up\" @pointercancel=\"cancelDrag\" @lostpointercapture=\"dragStart && cancelDrag()\" @blur=\"cancelDrag\" @wheel=\"wheel\" @keydown=\"keydown\" @click=\"click\" @contextmenu.prevent=\"click\">\n <p v-else role=\"status\">{{ loadingTargets?'正在检查电脑环境…':preparing?'准备完成后会自动接管,无需再次点击。':state.mode==='off' ? (backend==='managed-browser'?'浏览器尚未打开。接管后可打开网页。':'打开电脑后可查看桌面。') : '正在读取电脑画面…' }}</p>\n <span v-if=\"frame&&(!mine||frame.generation!==state.generation)\" class=\"view-label\">{{mine?'正在同步画面':state.mode==='off'?'上次画面':'仅查看'}}</span>\n </div>\n <p v-if=\"error||state.error\" class=\"computer-error\" role=\"alert\">{{ error||state.error }}</p>\n <footer v-if=\"controlsOpen||(!embedded&&!ticket)\">\n <p v-if=\"state.backend==='grok'\">多个机器人共享同一台 Grok Bot 云端电脑、工作文件与浏览器资料。</p>\n <p v-else-if=\"state.backend==='local'\">当前显示{{state.hostName||'所选电脑'}}的桌面,点击、输入、按键和滚动都会发送到这台电脑。</p>\n <p v-if=\"backend==='vm'&&selectedAgent?.computerEnvironmentId\">共享电脑:{{ selectedAgent.computerEnvironmentName }}。成员共用工作文件和浏览器资料,可同时执行命令与文件操作,桌面操作交替进行;人工接管时机器人暂停等待。</p>\n <p v-if=\"state.backend==='managed-browser'\">当前是托管浏览器,与电脑桌面和虚拟机分别管理;网页操作无需启动虚拟机。</p>\n <details v-if=\"managedBrowser?.downloads.length\" class=\"browser-downloads\"><summary>浏览器下载({{managedBrowser.downloads.length}})</summary><ul><li v-for=\"file in managedBrowser.downloads\" :key=\"file.id\"><span>{{file.name}}</span><small>{{(file.size/1024).toFixed(1)}} KiB</small></li></ul></details>\n <p v-if=\"!ticket\">查看不会输入任何内容。接管后机器人会暂停,已有操作结束后才允许人工操作。</p>\n <button v-if=\"!ticket&&!targetAvailable&&backend!=='managed-browser'\" type=\"button\" :disabled=\"busy\" @click=\"run(loadTargets)\">重新检查连接</button>\n <button v-if=\"!ticket&&state.mode!=='human'\" type=\"button\" :disabled=\"busy||!targetAvailable\" @click=\"embedded?emit('controlRequest'):take()\">{{ preparing?'正在准备浏览器…':installation?.status==='failed'?'重试并接管浏览器':state.mode==='off'?'打开并控制':'接管电脑' }}</button>\n <template v-if=\"mine\">\n\n <section v-if=\"controlsOpen\" class=\"manual-input\">\n <div class=\"key-row\"><button v-for=\"key in ['Return','Tab','Escape','BackSpace','Up','Down','Left','Right']\" :key=\"key\" type=\"button\" :disabled=\"!controllable\" @click=\"send({kind:'key',key})\">{{ ({Return:'回车',Escape:'Esc',BackSpace:'退格',Up:'↑',Down:'↓',Left:'←',Right:'→'} as Record<string,string>)[key]||key }}</button></div>\n <div class=\"key-row\"><button :disabled=\"!controllable\" @click=\"send({kind:'key',key:'a',modifiers:[state.backend==='local'||state.backend==='browser'?'super':'ctrl']})\">全选</button><button :disabled=\"!controllable\" @click=\"send({kind:'key',key:'c',modifiers:[state.backend==='local'||state.backend==='browser'?'super':'ctrl']})\">复制</button><button :disabled=\"!controllable\" @click=\"send({kind:'key',key:'v',modifiers:[state.backend==='local'||state.backend==='browser'?'super':'ctrl']})\">粘贴</button></div>\n <label>输入到电脑<textarea v-model=\"text\" rows=\"2\" maxlength=\"16000\" placeholder=\"输入文字后点击发送\" :disabled=\"!controllable\" /></label>\n <button type=\"button\" :disabled=\"!controllable||!text\" @click=\"send({kind:'text',text});text=''\">输入文字</button>\n </section>\n <label>交还说明<textarea v-model=\"notes\" rows=\"2\" maxlength=\"4000\" placeholder=\"告诉机器人你完成了什么,以及接下来需要做什么\" /></label>\n <button type=\"button\" :disabled=\"busy||state.mode!=='human'\" @click=\"run(giveBack)\">{{ state.canResume ? '交还机器人并继续' : backend==='managed-browser' ? '结束接管' : '结束控制' }}</button>\n <small v-if=\"backend==='managed-browser'\">{{ managedBrowser?.supportsRetention===true ? '交还或收起只会释放操作权,页面会保留;长时间空闲后可能回收。' : managedBrowser?.supportsRetention===false ? '此节点需更新后才能跨任务保留页面;任务结束后浏览器会关闭。' : '交还或收起会结束当前接管。' }}</small>\n <small v-else>控制只属于当前页面。页面断开后控制权会到期,任务不会自行恢复。</small>\n </template>\n </footer>\n <section v-if=\"canCloseBrowser\" class=\"browser-session-actions\" :aria-busy=\"closingBrowser\">\n <button type=\"button\" :disabled=\"busy\" @click=\"closeBrowser\">{{ closingBrowser?'正在关闭浏览器…':'关闭浏览器' }}</button>\n <small>结束当前浏览器会话并关闭标签页。</small>\n </section>\n </component>\n </Teleport>\n</template>\n<style scoped>\n.browser-session-actions{display:flex;align-items:center;flex-wrap:wrap;gap:12px;padding:12px 20px;border-top:1px solid var(--line)}.browser-session-actions button{min-height:44px;color:var(--danger)}.browser-session-actions small{font-size:12px;line-height:1.6;color:var(--text-secondary)}\n.browser-availability{display:grid;gap:10px;padding:12px 20px;background:var(--surface-soft);color:var(--text-secondary);border-bottom:1px solid var(--line)}.browser-availability p{margin:0;font-size:13px;line-height:1.6;overflow-wrap:anywhere}.browser-availability button{justify-self:start;min-height:44px}\n.browser-installation{display:grid;gap:10px;padding:16px 20px;border-bottom:1px solid var(--line);background:var(--surface-soft);color:var(--text-primary)}.browser-installation p{margin:0;font-size:13px;line-height:1.6;overflow-wrap:anywhere}.browser-installation progress{width:100%;height:6px;accent-color:var(--accent)}.browser-installation [role=alert]{color:var(--danger)}\n.computer-panel.embedded{position:static;width:100%;max-height:none;height:100%;margin:0;border:0;border-radius:0;overflow:auto}.computer-panel.embedded{display:flex;flex-direction:column}.embedded .computer-screen{flex:1;min-height:150px}.embedded .computer-screen img{max-height:calc(100dvh - 250px);object-fit:contain}.embedded footer{max-height:45vh;overflow:auto}.manual-input{display:grid;gap:10px}\n.computer-panel{width:min(1120px,calc(100vw - 32px));max-height:calc(100dvh - 32px);padding:0;border:1px solid var(--line);border-radius:16px;background:var(--surface);color:var(--text-primary);overflow:auto}\n.computer-panel.standalone{display:flex;flex-direction:column;width:100vw;height:100dvh;max-height:none;margin:0;border:0;border-radius:0;overflow:hidden}\n.standalone .computer-screen{flex:1;min-height:0;overflow:hidden}.standalone .computer-screen img{height:100%;width:100%;max-height:100%;max-width:100%;object-fit:contain}.standalone header{flex-shrink:0;background:#1c1f23;color:#f4f4f5}.standalone header small{color:#bec4cc}.standalone header button,.standalone header select{background:#2b2f34;color:#f4f4f5;border-color:#474c54}.standalone header .return-control{background:#f4f4f5;color:#202327;border-color:#f4f4f5}.standalone footer{position:absolute;right:16px;top:78px;width:min(360px,calc(100vw - 64px));max-height:calc(100dvh - 130px);overflow:auto;background:var(--surface);border:1px solid var(--line);border-radius:12px;box-shadow:0 16px 50px #0006;z-index:2}\n.computer-panel:fullscreen{display:flex;flex-direction:column;width:100vw;height:100dvh;max-height:none;border:0;border-radius:0}.computer-panel:fullscreen .computer-screen{flex:1;min-height:0}.computer-panel:fullscreen .computer-screen img{max-height:100%}\n.computer-screen.actual-size{overflow:auto;align-items:flex-start;justify-content:flex-start}.computer-screen.actual-size img{width:auto;height:auto;max-width:none;max-height:none;flex:none}.return-control{font-weight:600}.standalone .computer-error{flex:none;margin:0;padding:10px 18px}\n.computer-panel::backdrop{background:rgba(0,0,0,.45)}header{display:flex;align-items:center;gap:14px;padding:14px 18px;border-bottom:1px solid var(--line)}header div{display:grid;gap:4px;margin-right:auto}small{font-size:12px;color:var(--text-muted);line-height:1.5}\nbutton,select,textarea,input{font:inherit;border:1px solid var(--line);border-radius:8px;background:var(--surface-raised);color:var(--text-primary)}button,select{min-height:44px;padding:8px 12px}button{cursor:pointer}button:disabled{opacity:.5;cursor:default}button:focus-visible,select:focus-visible,textarea:focus-visible,input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}\n.computer-screen{position:relative;display:flex;align-items:center;justify-content:center;min-height:220px;background:#14181e}.computer-screen img{max-width:100%;max-height:58vh;width:auto;height:auto;user-select:none}.controlling img{cursor:crosshair;touch-action:none}.computer-screen img:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.computer-screen p{color:#d8dee7}.view-label{position:absolute;top:10px;left:12px;padding:4px 8px;border-radius:6px;background:rgba(0,0,0,.65);color:white;font-size:12px;pointer-events:none}\nfooter{padding:16px 18px;display:grid;gap:12px}footer p{margin:0;font-size:13px;color:var(--text-secondary);line-height:1.6}label{display:grid;gap:6px;font-size:13px}textarea{padding:10px;min-width:0;resize:vertical}.key-row{display:flex;gap:6px;flex-wrap:wrap}.computer-error{color:var(--danger);padding:0 18px;font-size:13px}\n.browser-downloads{font-size:13px}.browser-downloads summary{cursor:pointer;min-height:44px;display:flex;align-items:center}.browser-downloads ul{margin:0;padding-left:20px;display:grid;gap:8px}.browser-downloads li{overflow-wrap:anywhere}.browser-downloads small{margin-left:8px}\n.sharing-settings{display:grid;gap:12px;padding:12px;border:1px solid var(--line);border-radius:10px}.sharing-settings input:not([type=checkbox]){min-height:44px;padding:8px 10px}.share-option{display:flex;align-items:center;gap:8px;min-height:44px}.share-option input{width:18px;height:18px;flex:none}fieldset{border:1px solid var(--line);border-radius:8px}\n@media(max-width:600px){.computer-panel{width:100vw;max-height:100dvh;border-radius:0}header{flex-wrap:wrap;padding:12px}footer{padding:12px}.computer-screen img{max-height:48vh}}\n</style>\n"],"mappings":"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"}