toc-ocra 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,7 +30,7 @@ node dist/src/index.js run
30
30
  1. 打开「本机设备代理」开关:桌面端用自带的 Node 运行插件包里的 `dist/service/agent.mjs`(`tocPlugin.entries.service`,权限 `service:local`),不需要终端,也不需要另装 Node。
31
31
  2. 桌面端给这次启动生成令牌(`TOC_PLUGIN_SERVICE_TOKEN`)并交给插件,插件直接用它访问本机控制接口,不再输入 6 位连接码,随后自动绑定到当前登录的服务器。
32
32
  3. 设备代理跟随桌面端:开关保持打开时,下次打开桌面端自动启动;退出桌面端、关闭开关、禁用或卸载插件时停止。
33
- 4. 这台电脑已有 toc-ocra 在运行(后台服务或终端启动)时,桌面端启动的实例因端口被占用而退出(退出码 3),插件改为连接已有实例(首次输入连接码),保证一台设备只有一个 Agent。
33
+ 4. 这台电脑已有 toc-ocra 在运行(后台服务或终端启动)时,桌面端不会再启动第二个:开关直接显示「已启用」并连接已有实例(首次需输入连接码;此时开关不可操作,它不随桌面端退出而停止,停用请 `toc-ocra service:uninstall`)。若桌面端仍启动了实例,会因端口被占用退出(退出码 3),插件同样按已有实例处理,保证一台设备只有一个 Agent。
34
34
  5. 旧版桌面端没有这个能力时,插件仍显示终端命令:`npx toc-ocra@latest run`,或 `npm i -g toc-ocra && toc-ocra service:install`。
35
35
 
36
36
  **其他电脑**(一条命令接入)
@@ -1,2 +1,2 @@
1
- var Pe=Object.create;var ie=Object.defineProperty;var Te=Object.getOwnPropertyDescriptor;var Le=Object.getOwnPropertyNames;var xe=Object.getPrototypeOf,Ee=Object.prototype.hasOwnProperty;var te=(e,t)=>()=>{try{return t||e((t={exports:{}}).exports,t),t.exports}catch(r){throw t=0,r}};var Be=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let c of Le(t))!Ee.call(e,c)&&c!==r&&ie(e,c,{get:()=>t[c],enumerable:!(o=Te(t,c))||o.enumerable});return e};var P=(e,t,r)=>(r=e!=null?Pe(xe(e)):{},Be(t||!e||!e.__esModule?ie(r,"default",{value:e,enumerable:!0}):r,e));var H=te((nt,le)=>{var se=globalThis.__TOC_PLUGIN_SHARED__,de=se&&se.react;if(!de)throw new Error("The host does not provide react; this plugin needs a newer app version.");le.exports=de});var J=te((ot,ge)=>{var pe=globalThis.__TOC_PLUGIN_SHARED__,ue=pe&&pe["@toc-starter/plugin-sdk/ui"];if(!ue)throw new Error("The host does not provide @toc-starter/plugin-sdk/ui; this plugin needs a newer app version.");ge.exports=ue});var M=te((lt,be)=>{var fe=globalThis.__TOC_PLUGIN_SHARED__,ve=fe&&fe["react/jsx-runtime"];if(!ve)throw new Error("The host does not provide react/jsx-runtime; this plugin needs a newer app version.");be.exports=ve});var y=P(H(),1);var ce=P(H(),1);function me(e){if(!e||typeof e.activate!="function")throw new TypeError("definePlugin() needs an object with an activate(context) function");return e}function ae(e,t){return(0,ce.useSyncExternalStore)(r=>e.core.onContextChange(t,r),()=>e.core.context(t))}var m=P(J(),1);var ne="http://127.0.0.1:17339",L=class extends Error{constructor(r,o,c=""){super(o);this.kind=r;this.code=c}kind;code};async function q(e,t,r={}){let o=new Headers(r.headers);r.token&&o.set("Authorization",`Bearer ${r.token}`),r.body&&o.set("Content-Type","application/json");let c;try{c=await fetch(`${e.replace(/\/+$/,"")}${t}`,{...r,headers:o,cache:"no-store"})}catch{throw new L("offline","local service unreachable")}let h=await c.json().catch(()=>null);if(c.status===401)throw new L("unauthorized","unauthorized");if(!c.ok){let s=typeof h?.error=="string"?h.error:"",b=typeof h?.message=="string"?h.message:s||`HTTP ${c.status}`;throw new L("rejected",b,s)}return h}var B={info:e=>q(e,"/control/info"),pair:(e,t)=>q(e,"/control/pair",{method:"POST",body:JSON.stringify({code:t})}),status:(e,t)=>q(e,"/control/status",{token:t}),enroll:(e,t,r,o)=>q(e,"/control/enroll",{method:"POST",token:t,body:JSON.stringify({server_url:r,code:o})}),unpair:(e,t)=>q(e,"/control/unpair",{method:"POST",token:t}),detect:(e,t)=>q(e,"/control/detect",{method:"POST",token:t})};function V(e,t){let r=o=>{try{return new URL(o).origin.toLowerCase()}catch{return o.trim().replace(/\/+$/,"").toLowerCase()}};return!!(e&&t)&&r(e)===r(t)}var C=P(H(),1),f=P(J(),1);var he=P(H(),1),we=P(J(),1);var W=P(M(),1),A="controlToken",re="localUrl",Y="autoBind",G="localService";function F(e,t){return t instanceof L&&t.kind==="offline"?e.t("error.offline"):t instanceof Error?t.message:String(t)}function K({ctx:e,value:t}){let[r,o]=(0,he.useState)(!1);return(0,W.jsxs)("div",{className:"da-command",children:[(0,W.jsx)("code",{children:t}),(0,W.jsx)(we.Btn,{onClick:()=>{navigator.clipboard.writeText(t).then(()=>{o(!0),window.setTimeout(()=>o(!1),1500)})},children:r?e.t("common.copied"):e.t("common.copy")})]})}var n=P(M(),1),Q=["linux","mac","win"],Ie=1e4,Re=3e3,X="remoteServerUrl",Oe=new Set(["127.0.0.1","localhost","[::1]","::1","0.0.0.0"]);function Ue(e){try{let t=new URL(e.trim());return t.protocol==="http:"||t.protocol==="https:"?t.origin:null}catch{return null}}function De(e){try{return Oe.has(new URL(e).hostname)}catch{return!1}}function Ae(e,t,r){let o=`toc-ocra setup --code ${t} --server ${r}`;return e==="win"?`npm install -g toc-ocra@latest; if ($?) { ${o} }`:`npm install -g toc-ocra@latest && ${o}`}function $e({ctx:e,device:t}){return t.status==="online"?(0,n.jsx)(f.Badge,{variant:"ok",children:e.t("remote.status.online")}):t.status==="disabled"?(0,n.jsx)(f.Badge,{variant:"warn",children:e.t("remote.status.disabled")}):t.status==="offline"?(0,n.jsx)(f.Badge,{variant:"err",children:e.t("remote.status.offline")}):(0,n.jsx)(f.Badge,{variant:"muted",children:e.t("remote.status.notConnected")})}function ye({ctx:e,server:t,workspace:r,localDeviceId:o}){let[c,h]=(0,C.useState)(null),[s,b]=(0,C.useState)(""),[v,d]=(0,C.useState)(!1),[u,p]=(0,C.useState)(null),[x,I]=(0,C.useState)(!1),[O,U]=(0,C.useState)(""),[S,N]=(0,C.useState)("linux"),[_,g]=(0,C.useState)(()=>e.storage.get(X,"")||t?.origin||""),E=(0,C.useRef)(u);E.current=u;let R=(0,C.useRef)(!0);(0,C.useEffect)(()=>{!e.storage.get(X,"")&&t?.origin&&g(t.origin)},[e,t?.origin]);let $=(0,C.useCallback)(async()=>{if(r?.id)try{let i=await e.core.request(`/workspaces/${encodeURIComponent(r.id)}/devices`);if(!R.current)return;let k=(i.items??[]).filter(z=>z.is_mine&&z.id!==o);h(k);let D=E.current;if(D&&!D.matched){let z=k.find(Ne=>Ne.id===D.deviceId);z?.agent_id&&z.agent_id!==D.baseline&&z.terminal_online&&(p({...D,matched:!0}),e.notify({title:e.t("page.title"),body:e.t("remote.matchedNotice",{name:z.name})}))}}catch(i){R.current&&b(F(e,i))}},[e,o,r?.id]),j=!!(u&&!u.matched);(0,C.useEffect)(()=>{R.current=!0,$();let i=window.setInterval(()=>{$()},j?Re:Ie);return()=>{R.current=!1,window.clearInterval(i)}},[$,j]);let l=async i=>{d(!0),b("");try{await i()}catch(k){b(F(e,k))}finally{d(!1)}},w=Ue(_),T=i=>{g(i),i.trim()&&i.trim()!==t?.origin?e.storage.set(X,i.trim()):e.storage.remove(X)},oe=i=>l(async()=>{if(!w)throw new Error(e.t("remote.serverInvalid"));let k=await e.core.request(`/devices/${encodeURIComponent(i.id)}/pairing-code`,{method:"POST"});p({deviceId:i.id,deviceName:i.name,platform:Q.includes(i.platform)?i.platform:"linux",code:k.code,expiresAt:k.expires_at,baseline:i.agent_id??null,matched:!1})}),ke=()=>l(async()=>{if(!r?.id)throw new Error(e.t("bind.noWorkspace"));if(!w)throw new Error(e.t("remote.serverInvalid"));let i=O.trim()||e.t(`remote.platform.${S}`),k=await e.core.request(`/workspaces/${encodeURIComponent(r.id)}/devices`,{method:"POST",body:JSON.stringify({name:i,location:S==="linux"?"cloud":"local",platform:S,note:"",share_to_workspace:!1})}),D=await e.core.request(`/devices/${encodeURIComponent(k.id)}/pairing-code`,{method:"POST"});p({deviceId:k.id,deviceName:k.name||i,platform:S,code:D.code,expiresAt:D.expires_at,baseline:null,matched:!1}),I(!1),U(""),await $()}),Se=(i,k)=>l(async()=>{await e.core.request(`/devices/${encodeURIComponent(i.id)}/enabled`,{method:"PUT",body:JSON.stringify({enabled:k})}),await $()});return(0,n.jsxs)(n.Fragment,{children:[s&&(0,n.jsx)("div",{role:"alert",className:"da-alert",children:s}),(0,n.jsxs)(f.Card,{children:[(0,n.jsx)(f.CardTitle,{children:e.t("remote.title")}),(0,n.jsx)("p",{className:"da-text",children:e.t("remote.body")}),(0,n.jsxs)("label",{className:"da-field",children:[(0,n.jsx)("span",{children:e.t("remote.server")}),(0,n.jsx)(f.Input,{value:_,onChange:i=>T(i.target.value),spellCheck:!1,className:"da-mono"})]}),!w&&(0,n.jsx)("p",{className:"da-muted da-warn",children:e.t("remote.serverInvalid")}),w&&De(w)&&(0,n.jsx)("p",{className:"da-muted da-warn",children:e.t("remote.loopback")}),!x&&(0,n.jsx)("div",{className:"da-row",children:(0,n.jsx)(f.Btn,{primary:!0,disabled:v||!r?.id,onClick:()=>I(!0),children:e.t("remote.add")})}),x&&(0,n.jsxs)("form",{className:"da-form",onSubmit:i=>{i.preventDefault(),ke()},children:[(0,n.jsxs)("label",{className:"da-field",children:[(0,n.jsx)("span",{children:e.t("remote.platformLabel")}),(0,n.jsx)("select",{className:"da-select",value:S,onChange:i=>N(i.target.value),children:Q.map(i=>(0,n.jsx)("option",{value:i,children:e.t(`remote.platform.${i}`)},i))})]}),(0,n.jsxs)("label",{className:"da-field",children:[(0,n.jsx)("span",{children:e.t("remote.name")}),(0,n.jsx)(f.Input,{value:O,onChange:i=>U(i.target.value),maxLength:100,placeholder:e.t("remote.namePlaceholder")})]}),(0,n.jsxs)("div",{className:"da-row",children:[(0,n.jsx)(f.Btn,{primary:!0,type:"submit",disabled:v||!w,children:e.t(v?"remote.working":"remote.generate")}),(0,n.jsx)(f.Btn,{type:"button",disabled:v,onClick:()=>I(!1),children:e.t("remote.cancel")})]})]})]}),u&&w&&(0,n.jsx)(ze,{ctx:e,pending:u,origin:w,busy:v,onPlatform:i=>p({...u,platform:i}),onRegenerate:()=>{oe({id:u.deviceId,name:u.deviceName,platform:u.platform,agent_id:u.baseline})},onClose:()=>p(null)}),(0,n.jsxs)(f.Card,{children:[(0,n.jsx)(f.CardTitle,{children:e.t("remote.listTitle")}),c===null&&(0,n.jsx)("p",{className:"da-muted",children:e.t("common.checking")}),c?.length===0&&(0,n.jsx)("p",{className:"da-muted",children:e.t("remote.empty")}),c&&c.length>0&&(0,n.jsx)("ul",{className:"da-devices",children:c.map(i=>(0,n.jsxs)("li",{children:[(0,n.jsxs)("div",{className:"da-device-main",children:[(0,n.jsx)("span",{className:"da-device-name",children:i.name}),(0,n.jsxs)("span",{className:"da-muted",children:[e.t(`remote.platform.${Q.includes(i.platform)?i.platform:"linux"}`),i.agent_version?` \xB7 toc-ocra ${i.agent_version}`:""]})]}),(0,n.jsx)($e,{ctx:e,device:i}),(0,n.jsxs)("div",{className:"da-device-actions",children:[(!i.paired||i.status==="offline"||i.status==="not_connected")&&(0,n.jsx)(f.Btn,{disabled:v||!w,onClick:()=>{oe(i)},children:e.t(i.paired?"remote.repair":"remote.install")}),i.paired&&(0,n.jsxs)("label",{className:"da-toggle",children:[(0,n.jsx)("span",{children:e.t("remote.enabled")}),(0,n.jsx)(f.Toggle,{checked:!i.disabled,disabled:v,label:e.t("remote.enabled"),onChange:k=>{Se(i,k)}})]})]})]},i.id))}),(0,n.jsx)("p",{className:"da-muted",children:e.t("remote.disableHint")}),(0,n.jsx)("div",{className:"da-step",children:e.t("remote.uninstall")}),(0,n.jsx)(K,{ctx:e,value:"toc-ocra service:uninstall"})]})]})}function ze({ctx:e,pending:t,origin:r,busy:o,onPlatform:c,onRegenerate:h,onClose:s}){let[b,v]=(0,C.useState)(()=>Math.floor(Date.now()/1e3));(0,C.useEffect)(()=>{if(t.matched)return;let p=window.setInterval(()=>v(Math.floor(Date.now()/1e3)),1e3);return()=>window.clearInterval(p)},[t.matched]);let d=Math.max(0,t.expiresAt-b),u=!t.matched&&d===0;return(0,n.jsxs)(f.Card,{children:[(0,n.jsx)(f.CardTitle,{children:e.t("remote.pendingTitle",{name:t.deviceName})}),t.matched?(0,n.jsxs)(n.Fragment,{children:[(0,n.jsxs)("p",{className:"da-text",children:[(0,n.jsx)(f.Badge,{variant:"ok",children:e.t("remote.matched")})," ",e.t("remote.matchedBody",{name:t.deviceName})]}),(0,n.jsx)("div",{className:"da-row",children:(0,n.jsx)(f.Btn,{primary:!0,onClick:s,children:e.t("remote.done")})})]}):(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)("p",{className:"da-text",children:e.t("remote.pendingBody")}),(0,n.jsx)("div",{className:"da-segment",role:"radiogroup","aria-label":e.t("remote.platformLabel"),children:Q.map(p=>(0,n.jsx)("button",{type:"button",role:"radio","aria-checked":t.platform===p,className:t.platform===p?"da-seg da-seg-active":"da-seg",onClick:()=>c(p),children:e.t(`remote.platform.${p}`)},p))}),(0,n.jsx)(K,{ctx:e,value:Ae(t.platform,t.code,r)}),(0,n.jsxs)("ul",{className:"da-hints",children:[(0,n.jsx)("li",{children:e.t("remote.hintNode")}),t.platform==="linux"&&(0,n.jsx)("li",{children:e.t("remote.hintLinux")}),t.platform==="win"&&(0,n.jsx)("li",{children:e.t("remote.hintWindows")}),(0,n.jsx)("li",{children:u?e.t("remote.expired"):e.t("remote.expires",{minutes:Math.floor(d/60),seconds:String(d%60).padStart(2,"0")})})]}),(0,n.jsx)("p",{className:"da-muted",children:u?"":e.t("remote.waiting")}),(0,n.jsxs)("div",{className:"da-row",children:[(0,n.jsx)(f.Btn,{disabled:o,onClick:h,children:e.t("remote.regenerate")}),(0,n.jsx)(f.Btn,{disabled:o,onClick:s,children:e.t("remote.cancel")})]})]})]})}var a=P(M(),1),Me=3e3,Ke=1e4,qe=6e4,He=3,Ce="tab",We=new URL("../../assets/icon.svg",import.meta.url).href;function Ye(){return(0,a.jsx)("img",{src:We,alt:"",width:16,height:16,draggable:!1})}function _e(e){let t=e.core.service;try{return t&&t.available()?t:void 0}catch{return}}var Z=e=>e?.state==="running"||e?.state==="starting",ee;function Fe({ctx:e}){let t=ae(e,"server"),r=ae(e,"workspace"),[o,c]=(0,y.useState)(()=>e.storage.get(Ce,"local")==="remote"?"remote":"local"),h=l=>{e.storage.set(Ce,l),c(l)},[s]=(0,y.useState)(()=>e.storage.get(re,ne)),[b,v]=(0,y.useState)(()=>e.secureStorage.get(A,"")),[d,u]=(0,y.useState)({kind:"checking"}),[p,x]=(0,y.useState)(null),[I,O]=(0,y.useState)(""),[U,S]=(0,y.useState)(!1),N=(0,y.useRef)(!0),_=_e(e),g=(0,y.useCallback)(async()=>{let l=null;if(_)try{l=await _.status()}catch{l=null}if(!N.current)return;x(l);let w=b;Z(l)&&l?.token&&(w=l.token,l.token!==b&&(e.secureStorage.set(A,l.token),v(l.token)));try{await B.info(s)}catch{N.current&&u({kind:Z(l)?"starting":"offline"});return}if(!w){N.current&&u({kind:"needsCode"});return}try{let T=await B.status(s,w);N.current&&u({kind:"ready",status:T})}catch(T){if(!N.current)return;T instanceof L&&T.kind==="unauthorized"?(e.secureStorage.remove(A),v(""),u({kind:"needsCode"})):u({kind:"offline"})}},[e,s,_,b]);(0,y.useEffect)(()=>{N.current=!0,g();let l=window.setInterval(()=>{g()},Me);return()=>{N.current=!1,window.clearInterval(l)}},[g]),(0,y.useEffect)(()=>{if(d.kind==="ready"){let l=d.status;e.reportHealth(l.paired&&l.connected?{status:"healthy",summary:e.t("health.online",{name:l.device_name})}:{status:"degraded",summary:e.t(l.paired?"health.disconnected":"health.unpaired")})}else d.kind==="offline"&&e.reportHealth({status:"degraded",summary:e.t(_?"health.disabled":"health.offline")})},[e,d,_]);let E=async l=>{S(!0),O("");try{await l()}catch(w){O(F(e,w))}finally{S(!1)}},R=l=>E(async()=>{if(!_)return;if(!l){e.storage.set(G,"off"),x(await _.stop()),await g();return}e.storage.set(G,"on"),e.storage.remove(Y);let w=await _.start();x(w),w.token&&(e.secureStorage.set(A,w.token),v(w.token)),await je(s,()=>N.current)&&ee?.(),await g()}),$=p?.state==="exited"?p:null,j=!!_&&!Z(p)&&(d.kind==="ready"||d.kind==="needsCode");return(0,a.jsxs)("div",{className:"da-app",children:[(0,a.jsx)(m.PageHeader,{title:e.t("page.title"),subtitle:e.t("page.subtitle")}),(0,a.jsx)("div",{className:"da-tabs",role:"tablist","aria-label":e.t("page.title"),children:["local","remote"].map(l=>(0,a.jsx)("button",{type:"button",role:"tab","aria-selected":o===l,className:o===l?"da-tab da-tab-active":"da-tab",onClick:()=>h(l),children:e.t(`tabs.${l}`)},l))}),I&&(0,a.jsx)("div",{role:"alert",className:"da-alert",children:I}),o==="remote"&&(0,a.jsx)(ye,{ctx:e,server:t,workspace:r,localDeviceId:d.kind==="ready"&&d.status.paired?d.status.device_id:""}),o==="local"&&(0,a.jsxs)(a.Fragment,{children:[_&&(0,a.jsx)(Je,{ctx:e,status:p,external:j,exit:$,busy:U,onToggle:l=>{R(l)}}),d.kind==="checking"&&(0,a.jsx)(m.Card,{children:(0,a.jsx)("span",{className:"da-muted",children:e.t("common.checking")})}),d.kind==="starting"&&(0,a.jsx)(m.Card,{children:(0,a.jsx)("span",{className:"da-muted",children:e.t("service.starting")})}),d.kind==="offline"&&!_&&(0,a.jsx)(Ve,{ctx:e,localUrl:s}),d.kind==="needsCode"&&(0,a.jsx)(Ge,{ctx:e,busy:U,onPair:l=>E(async()=>{let w=await B.pair(s,l).catch(T=>{throw T instanceof L&&T.code==="code_wrong"?new Error(e.t("pair.wrong")):T instanceof L&&T.code==="locked"?new Error(e.t("pair.locked")):T});e.secureStorage.set(A,w.token),v(w.token)})}),d.kind==="ready"&&(0,a.jsx)(Xe,{ctx:e,status:d.status,server:t,workspace:r,busy:U,run:E,localUrl:s,token:b,onStatus:l=>u({kind:"ready",status:l})})]})]})}async function je(e,t){for(let r=0;r<30&&t();r+=1)try{return await B.info(e),!0}catch{await new Promise(o=>window.setTimeout(o,500))}return!1}function Je({ctx:e,status:t,external:r,exit:o,busy:c,onToggle:h}){let s=Z(t),b=o?.exitCode===He;return(0,a.jsxs)(m.Card,{children:[(0,a.jsxs)("div",{className:"da-switch",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)(m.CardTitle,{children:e.t("service.title")}),(0,a.jsx)("p",{className:"da-text",children:e.t(s?"service.onBody":"service.offBody")})]}),(0,a.jsx)(m.Toggle,{checked:s,disabled:c,label:e.t("service.title"),onChange:h})]}),s&&(0,a.jsx)("p",{className:"da-muted",children:e.t("service.followsApp")}),!s&&(r||b)&&(0,a.jsx)("p",{className:"da-muted",children:e.t("service.external")}),o&&!b&&o.exitCode!==0&&(0,a.jsx)("div",{role:"alert",className:"da-alert",children:e.t("service.crashed",{error:o.error||`exit ${o.exitCode??o.signal??"?"}`})})]})}function Ve({ctx:e,localUrl:t}){return(0,a.jsxs)(m.Card,{children:[(0,a.jsx)(m.CardTitle,{children:e.t("offline.title")}),(0,a.jsx)("p",{className:"da-text",children:e.t("offline.body",{url:t})}),(0,a.jsx)("div",{className:"da-step",children:e.t("offline.once")}),(0,a.jsx)(K,{ctx:e,value:"npx toc-ocra@latest run"}),(0,a.jsx)("div",{className:"da-step",children:e.t("offline.service")}),(0,a.jsx)(K,{ctx:e,value:"npm install -g toc-ocra@latest && toc-ocra service:install"}),(0,a.jsx)("p",{className:"da-muted",children:e.t("offline.node")})]})}function Ge({ctx:e,busy:t,onPair:r}){let[o,c]=(0,y.useState)(""),h=/^\d{6}$/.test(o);return(0,a.jsxs)(m.Card,{children:[(0,a.jsx)(m.CardTitle,{children:e.t("pair.title")}),(0,a.jsx)("p",{className:"da-text",children:e.t("pair.body")}),(0,a.jsx)(K,{ctx:e,value:"toc-ocra code"}),(0,a.jsxs)("form",{className:"da-row",onSubmit:s=>{s.preventDefault(),h&&r(o)},children:[(0,a.jsx)(m.Input,{value:o,onChange:s=>c(s.target.value.replace(/\D/g,"").slice(0,6)),placeholder:e.t("pair.placeholder"),inputMode:"numeric",autoComplete:"one-time-code","aria-label":e.t("pair.placeholder"),className:"da-code-input"}),(0,a.jsx)(m.Btn,{primary:!0,type:"submit",disabled:!h||t,children:e.t("pair.submit")})]})]})}function Xe({ctx:e,status:t,server:r,workspace:o,busy:c,run:h,localUrl:s,token:b,onStatus:v}){let d=t.paired&&r&&!V(t.server_url,r.origin),u=()=>h(async()=>{t.device_id&&r&&V(t.server_url,r.origin)&&await e.core.request(`/devices/${encodeURIComponent(t.device_id)}/agent`,{method:"DELETE"}).catch(()=>{}),e.storage.set(Y,"off"),v(await B.unpair(s,b))});return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(m.Card,{children:[(0,a.jsx)(m.CardTitle,{children:e.t("status.title")}),(0,a.jsxs)("dl",{className:"da-grid",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:e.t("status.service")}),(0,a.jsxs)("dd",{children:["toc-ocra ",t.version," \xB7 ",t.hostname]})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:e.t("status.binding")}),(0,a.jsx)("dd",{children:(0,a.jsx)(Qe,{ctx:e,status:t})})]}),t.paired&&(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:e.t("status.device")}),(0,a.jsx)("dd",{children:t.device_name})]}),t.paired&&(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:e.t("status.server")}),(0,a.jsx)("dd",{className:"da-mono",children:t.server_url})]}),t.paired&&(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:e.t("status.runs")}),(0,a.jsx)("dd",{children:t.active_runs})]})]}),t.paired&&!t.connected&&t.connection_error&&(0,a.jsx)("p",{className:"da-muted",children:e.t("status.lastError",{error:t.connection_error})}),t.token_from_env&&(0,a.jsx)("p",{className:"da-muted",children:e.t("status.tokenFromEnv")}),t.paired&&(0,a.jsx)("div",{className:"da-row",children:(0,a.jsx)(m.Btn,{danger:!0,disabled:c||t.token_from_env,onClick:()=>{u()},children:e.t("status.unpair")})})]}),(!t.paired||t.unauthorized||d)&&(0,a.jsx)(Ze,{ctx:e,status:t,server:r,workspace:o,busy:c,run:h,localUrl:s,token:b,onStatus:v,rebind:t.paired}),(0,a.jsxs)(m.Card,{children:[(0,a.jsx)(m.CardTitle,{children:e.t("cli.title")}),(0,a.jsxs)("ul",{className:"da-list",children:[t.providers.filter(p=>p.installed).map(p=>(0,a.jsxs)("li",{children:[(0,a.jsx)("span",{className:"da-mono",children:p.id}),(0,a.jsx)("span",{className:"da-muted",children:p.version}),p.runnable?(0,a.jsx)(m.Badge,{variant:"ok",children:e.t("cli.runnable")}):(0,a.jsx)(m.Badge,{variant:"muted",children:e.t("cli.detectOnly")})]},p.id)),!t.providers.some(p=>p.installed)&&(0,a.jsx)("li",{className:"da-muted",children:e.t("cli.none")})]}),(0,a.jsx)("div",{className:"da-row",children:(0,a.jsx)(m.Btn,{disabled:c,onClick:()=>{h(async()=>v(await B.detect(s,b)))},children:e.t("cli.redetect")})})]})]})}function Qe({ctx:e,status:t}){return t.paired?t.unauthorized?(0,a.jsx)(m.Badge,{variant:"err",children:e.t("binding.revoked")}):t.connected?(0,a.jsx)(m.Badge,{variant:"ok",children:e.t("binding.online")}):(0,a.jsx)(m.Badge,{variant:"warn",children:e.t("binding.connecting")}):(0,a.jsx)(m.Badge,{variant:"muted",children:e.t("binding.unpaired")})}function Ze({ctx:e,status:t,server:r,workspace:o,busy:c,run:h,localUrl:s,token:b,onStatus:v,rebind:d}){let[u,p]=(0,y.useState)([]),[x,I]=(0,y.useState)("new"),[O,U]=(0,y.useState)(t.hostname),[S,N]=(0,y.useState)(r?.origin??"");(0,y.useEffect)(()=>{r?.origin&&N(r.origin)},[r?.origin]),(0,y.useEffect)(()=>{if(!o?.id)return;let g=!1;return e.core.request(`/workspaces/${encodeURIComponent(o.id)}/devices`).then(E=>{g||p((E.items??[]).filter(R=>R.is_mine))}).catch(()=>{g||p([])}),()=>{g=!0}},[e,o?.id]);let _=()=>h(async()=>{if(!o?.id)throw new Error(e.t("bind.noWorkspace"));if(!S.trim())throw new Error(e.t("bind.noServer"));let g=x;x==="new"&&(g=(await e.core.request(`/workspaces/${encodeURIComponent(o.id)}/devices`,{method:"POST",body:JSON.stringify({name:O.trim()||t.hostname,location:"local",platform:t.platform,note:"",share_to_workspace:!1})})).id,I(g));let E=await e.core.request(`/devices/${encodeURIComponent(g)}/pairing-code`,{method:"POST"});v(await B.enroll(s,b,S.trim(),E.code)),e.storage.remove(Y),e.notify({title:e.t("page.title"),body:e.t("bind.done",{server:r?.name??S})})});return(0,a.jsxs)(m.Card,{children:[(0,a.jsx)(m.CardTitle,{children:e.t(d?"bind.rebindTitle":"bind.title")}),d&&t.unauthorized&&(0,a.jsx)("p",{className:"da-text",children:e.t("bind.revoked")}),d&&!t.unauthorized&&(0,a.jsx)("p",{className:"da-text",children:e.t("bind.elsewhere",{bound:t.server_url,current:r?.origin??""})}),(0,a.jsx)("p",{className:"da-text",children:e.t("bind.body",{server:r?.name??"",workspace:o?.name??""})}),(0,a.jsxs)("label",{className:"da-field",children:[(0,a.jsx)("span",{children:e.t("bind.server")}),(0,a.jsx)(m.Input,{value:S,onChange:g=>N(g.target.value),spellCheck:!1,className:"da-mono"})]}),(0,a.jsxs)("label",{className:"da-field",children:[(0,a.jsx)("span",{children:e.t("bind.device")}),(0,a.jsxs)("select",{className:"da-select",value:x,onChange:g=>I(g.target.value),children:[(0,a.jsx)("option",{value:"new",children:e.t("bind.newDevice")}),u.map(g=>(0,a.jsxs)("option",{value:g.id,children:[g.name,g.paired?` \xB7 ${e.t("bind.replaces")}`:""]},g.id))]})]}),x==="new"&&(0,a.jsxs)("label",{className:"da-field",children:[(0,a.jsx)("span",{children:e.t("bind.name")}),(0,a.jsx)(m.Input,{value:O,onChange:g=>U(g.target.value),maxLength:100})]}),(0,a.jsx)("div",{className:"da-row",children:(0,a.jsx)(m.Btn,{primary:!0,disabled:c||!o?.id,onClick:()=>{_()},children:e.t(c?"bind.working":"bind.submit")})})]})}function et(e){let t=!1,r="",o=new Map,c=async()=>{if(t||e.signal.aborted)return;let s=e.core.context("server"),b=e.secureStorage.get(A,"");if(!(!s?.origin||!b||e.storage.get(Y,"on")==="off")&&!(Date.now()-(o.get(s.origin)??0)<qe)){t=!0;try{let v=e.storage.get(re,ne),d=await B.status(v,b);if(!d.machine_id||d.token_from_env)return;if(d.paired&&(d.unauthorized||V(d.server_url,s.origin))){r="";return}if(d.active_runs>0){r!==s.origin&&(r=s.origin,e.notify({title:e.t("page.title"),body:e.t("auto.deferred",{server:s.name||s.origin})}));return}let u=await e.core.request("/devices/machine/pairing-code",{method:"POST",body:JSON.stringify({machine_id:d.machine_id,hostname:d.hostname,platform:d.platform,current_device_id:d.paired?d.device_id:""})});await B.enroll(v,b,s.origin,u.code),r="",o.delete(s.origin),e.notify({title:e.t("page.title"),body:e.t("auto.done",{server:s.name||s.origin,device:u.device_name})})}catch(v){(!(v instanceof L)||v.kind==="rejected")&&o.set(s.origin,Date.now())}finally{t=!1}}},h=window.setInterval(()=>{c()},Ke);ee=()=>{c()},e.registerDisposer(()=>{window.clearInterval(h),ee=void 0}),e.registerDisposer(e.core.onContextChange("server",()=>{c()})),window.setTimeout(()=>{c()},1500)}function tt(e){let t=_e(e);!t||e.storage.get(G,"off")!=="on"||t.start().then(r=>{r.token&&e.secureStorage.set(A,r.token),window.setTimeout(()=>ee?.(),3e3)}).catch(()=>{})}var wt=me({activate(e){e.registerNavigation("agent.nav",(0,a.jsx)(Ye,{})),e.registerRoute("agent.home",(0,a.jsx)(Fe,{ctx:e})),tt(e),et(e)}});export{wt as default};
1
+ var Te=Object.create;var ie=Object.defineProperty;var Le=Object.getOwnPropertyDescriptor;var xe=Object.getOwnPropertyNames;var Ee=Object.getPrototypeOf,Be=Object.prototype.hasOwnProperty;var ne=(e,t)=>()=>{try{return t||e((t={exports:{}}).exports,t),t.exports}catch(r){throw t=0,r}};var Ie=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let c of xe(t))!Be.call(e,c)&&c!==r&&ie(e,c,{get:()=>t[c],enumerable:!(o=Le(t,c))||o.enumerable});return e};var P=(e,t,r)=>(r=e!=null?Te(Ee(e)):{},Ie(t||!e||!e.__esModule?ie(r,"default",{value:e,enumerable:!0}):r,e));var W=ne((rt,le)=>{var se=globalThis.__TOC_PLUGIN_SHARED__,de=se&&se.react;if(!de)throw new Error("The host does not provide react; this plugin needs a newer app version.");le.exports=de});var G=ne((it,ge)=>{var pe=globalThis.__TOC_PLUGIN_SHARED__,ue=pe&&pe["@toc-starter/plugin-sdk/ui"];if(!ue)throw new Error("The host does not provide @toc-starter/plugin-sdk/ui; this plugin needs a newer app version.");ge.exports=ue});var M=ne((ct,be)=>{var fe=globalThis.__TOC_PLUGIN_SHARED__,ve=fe&&fe["react/jsx-runtime"];if(!ve)throw new Error("The host does not provide react/jsx-runtime; this plugin needs a newer app version.");be.exports=ve});var y=P(W(),1);var ce=P(W(),1);function me(e){if(!e||typeof e.activate!="function")throw new TypeError("definePlugin() needs an object with an activate(context) function");return e}function re(e,t){return(0,ce.useSyncExternalStore)(r=>e.core.onContextChange(t,r),()=>e.core.context(t))}var m=P(G(),1);var X="http://127.0.0.1:17339",L=class extends Error{constructor(r,o,c=""){super(o);this.kind=r;this.code=c}kind;code};async function q(e,t,r={}){let o=new Headers(r.headers);r.token&&o.set("Authorization",`Bearer ${r.token}`),r.body&&o.set("Content-Type","application/json");let c;try{c=await fetch(`${e.replace(/\/+$/,"")}${t}`,{...r,headers:o,cache:"no-store"})}catch{throw new L("offline","local service unreachable")}let h=await c.json().catch(()=>null);if(c.status===401)throw new L("unauthorized","unauthorized");if(!c.ok){let s=typeof h?.error=="string"?h.error:"",v=typeof h?.message=="string"?h.message:s||`HTTP ${c.status}`;throw new L("rejected",v,s)}return h}var E={info:e=>q(e,"/control/info"),pair:(e,t)=>q(e,"/control/pair",{method:"POST",body:JSON.stringify({code:t})}),status:(e,t)=>q(e,"/control/status",{token:t}),enroll:(e,t,r,o)=>q(e,"/control/enroll",{method:"POST",token:t,body:JSON.stringify({server_url:r,code:o})}),unpair:(e,t)=>q(e,"/control/unpair",{method:"POST",token:t}),detect:(e,t)=>q(e,"/control/detect",{method:"POST",token:t})};function Q(e,t){let r=o=>{try{return new URL(o).origin.toLowerCase()}catch{return o.trim().replace(/\/+$/,"").toLowerCase()}};return!!(e&&t)&&r(e)===r(t)}var C=P(W(),1),b=P(G(),1);var he=P(W(),1),we=P(G(),1);var Y=P(M(),1),A="controlToken",Z="localUrl",F="autoBind",ee="localService";function j(e,t){return t instanceof L&&t.kind==="offline"?e.t("error.offline"):t instanceof Error?t.message:String(t)}function K({ctx:e,value:t}){let[r,o]=(0,he.useState)(!1);return(0,Y.jsxs)("div",{className:"da-command",children:[(0,Y.jsx)("code",{children:t}),(0,Y.jsx)(we.Btn,{onClick:()=>{navigator.clipboard.writeText(t).then(()=>{o(!0),window.setTimeout(()=>o(!1),1500)})},children:r?e.t("common.copied"):e.t("common.copy")})]})}var n=P(M(),1),ae=["linux","mac","win"],Re=1e4,Ue=3e3,te="remoteServerUrl",Oe=new Set(["127.0.0.1","localhost","[::1]","::1","0.0.0.0"]);function De(e){try{let t=new URL(e.trim());return t.protocol==="http:"||t.protocol==="https:"?t.origin:null}catch{return null}}function Ae(e){try{return Oe.has(new URL(e).hostname)}catch{return!1}}function $e(e,t,r){let o=`toc-ocra setup --code ${t} --server ${r}`;return e==="win"?`npm install -g toc-ocra@latest; if ($?) { ${o} }`:`npm install -g toc-ocra@latest && ${o}`}function ze({ctx:e,device:t}){return t.status==="online"?(0,n.jsx)(b.Badge,{variant:"ok",children:e.t("remote.status.online")}):t.status==="disabled"?(0,n.jsx)(b.Badge,{variant:"warn",children:e.t("remote.status.disabled")}):t.status==="offline"?(0,n.jsx)(b.Badge,{variant:"err",children:e.t("remote.status.offline")}):(0,n.jsx)(b.Badge,{variant:"muted",children:e.t("remote.status.notConnected")})}function ye({ctx:e,server:t,workspace:r,localDeviceId:o}){let[c,h]=(0,C.useState)(null),[s,v]=(0,C.useState)(""),[u,d]=(0,C.useState)(!1),[g,p]=(0,C.useState)(null),[x,I]=(0,C.useState)(!1),[U,O]=(0,C.useState)(""),[S,N]=(0,C.useState)("linux"),[_,f]=(0,C.useState)(()=>e.storage.get(te,"")||t?.origin||""),B=(0,C.useRef)(g);B.current=g;let R=(0,C.useRef)(!0);(0,C.useEffect)(()=>{!e.storage.get(te,"")&&t?.origin&&f(t.origin)},[e,t?.origin]);let $=(0,C.useCallback)(async()=>{if(r?.id)try{let i=await e.core.request(`/workspaces/${encodeURIComponent(r.id)}/devices`);if(!R.current)return;let k=(i.items??[]).filter(z=>z.is_mine&&z.id!==o);h(k);let D=B.current;if(D&&!D.matched){let z=k.find(Pe=>Pe.id===D.deviceId);z?.agent_id&&z.agent_id!==D.baseline&&z.terminal_online&&(p({...D,matched:!0}),e.notify({title:e.t("page.title"),body:e.t("remote.matchedNotice",{name:z.name})}))}}catch(i){R.current&&v(j(e,i))}},[e,o,r?.id]),V=!!(g&&!g.matched);(0,C.useEffect)(()=>{R.current=!0,$();let i=window.setInterval(()=>{$()},V?Ue:Re);return()=>{R.current=!1,window.clearInterval(i)}},[$,V]);let l=async i=>{d(!0),v("");try{await i()}catch(k){v(j(e,k))}finally{d(!1)}},w=De(_),T=i=>{f(i),i.trim()&&i.trim()!==t?.origin?e.storage.set(te,i.trim()):e.storage.remove(te)},oe=i=>l(async()=>{if(!w)throw new Error(e.t("remote.serverInvalid"));let k=await e.core.request(`/devices/${encodeURIComponent(i.id)}/pairing-code`,{method:"POST"});p({deviceId:i.id,deviceName:i.name,platform:ae.includes(i.platform)?i.platform:"linux",code:k.code,expiresAt:k.expires_at,baseline:i.agent_id??null,matched:!1})}),Se=()=>l(async()=>{if(!r?.id)throw new Error(e.t("bind.noWorkspace"));if(!w)throw new Error(e.t("remote.serverInvalid"));let i=U.trim()||e.t(`remote.platform.${S}`),k=await e.core.request(`/workspaces/${encodeURIComponent(r.id)}/devices`,{method:"POST",body:JSON.stringify({name:i,location:S==="linux"?"cloud":"local",platform:S,note:"",share_to_workspace:!1})}),D=await e.core.request(`/devices/${encodeURIComponent(k.id)}/pairing-code`,{method:"POST"});p({deviceId:k.id,deviceName:k.name||i,platform:S,code:D.code,expiresAt:D.expires_at,baseline:null,matched:!1}),I(!1),O(""),await $()}),Ne=(i,k)=>l(async()=>{await e.core.request(`/devices/${encodeURIComponent(i.id)}/enabled`,{method:"PUT",body:JSON.stringify({enabled:k})}),await $()});return(0,n.jsxs)(n.Fragment,{children:[s&&(0,n.jsx)("div",{role:"alert",className:"da-alert",children:s}),(0,n.jsxs)(b.Card,{children:[(0,n.jsx)(b.CardTitle,{children:e.t("remote.title")}),(0,n.jsx)("p",{className:"da-text",children:e.t("remote.body")}),(0,n.jsxs)("label",{className:"da-field",children:[(0,n.jsx)("span",{children:e.t("remote.server")}),(0,n.jsx)(b.Input,{value:_,onChange:i=>T(i.target.value),spellCheck:!1,className:"da-mono"})]}),!w&&(0,n.jsx)("p",{className:"da-muted da-warn",children:e.t("remote.serverInvalid")}),w&&Ae(w)&&(0,n.jsx)("p",{className:"da-muted da-warn",children:e.t("remote.loopback")}),!x&&(0,n.jsx)("div",{className:"da-row",children:(0,n.jsx)(b.Btn,{primary:!0,disabled:u||!r?.id,onClick:()=>I(!0),children:e.t("remote.add")})}),x&&(0,n.jsxs)("form",{className:"da-form",onSubmit:i=>{i.preventDefault(),Se()},children:[(0,n.jsxs)("label",{className:"da-field",children:[(0,n.jsx)("span",{children:e.t("remote.platformLabel")}),(0,n.jsx)("select",{className:"da-select",value:S,onChange:i=>N(i.target.value),children:ae.map(i=>(0,n.jsx)("option",{value:i,children:e.t(`remote.platform.${i}`)},i))})]}),(0,n.jsxs)("label",{className:"da-field",children:[(0,n.jsx)("span",{children:e.t("remote.name")}),(0,n.jsx)(b.Input,{value:U,onChange:i=>O(i.target.value),maxLength:100,placeholder:e.t("remote.namePlaceholder")})]}),(0,n.jsxs)("div",{className:"da-row",children:[(0,n.jsx)(b.Btn,{primary:!0,type:"submit",disabled:u||!w,children:e.t(u?"remote.working":"remote.generate")}),(0,n.jsx)(b.Btn,{type:"button",disabled:u,onClick:()=>I(!1),children:e.t("remote.cancel")})]})]})]}),g&&w&&(0,n.jsx)(Me,{ctx:e,pending:g,origin:w,busy:u,onPlatform:i=>p({...g,platform:i}),onRegenerate:()=>{oe({id:g.deviceId,name:g.deviceName,platform:g.platform,agent_id:g.baseline})},onClose:()=>p(null)}),(0,n.jsxs)(b.Card,{children:[(0,n.jsx)(b.CardTitle,{children:e.t("remote.listTitle")}),c===null&&(0,n.jsx)("p",{className:"da-muted",children:e.t("common.checking")}),c?.length===0&&(0,n.jsx)("p",{className:"da-muted",children:e.t("remote.empty")}),c&&c.length>0&&(0,n.jsx)("ul",{className:"da-devices",children:c.map(i=>(0,n.jsxs)("li",{children:[(0,n.jsxs)("div",{className:"da-device-main",children:[(0,n.jsx)("span",{className:"da-device-name",children:i.name}),(0,n.jsxs)("span",{className:"da-muted",children:[e.t(`remote.platform.${ae.includes(i.platform)?i.platform:"linux"}`),i.agent_version?` \xB7 toc-ocra ${i.agent_version}`:""]})]}),(0,n.jsx)(ze,{ctx:e,device:i}),(0,n.jsxs)("div",{className:"da-device-actions",children:[(!i.paired||i.status==="offline"||i.status==="not_connected")&&(0,n.jsx)(b.Btn,{disabled:u||!w,onClick:()=>{oe(i)},children:e.t(i.paired?"remote.repair":"remote.install")}),i.paired&&(0,n.jsxs)("label",{className:"da-toggle",children:[(0,n.jsx)("span",{children:e.t("remote.enabled")}),(0,n.jsx)(b.Toggle,{checked:!i.disabled,disabled:u,label:e.t("remote.enabled"),onChange:k=>{Ne(i,k)}})]})]})]},i.id))}),(0,n.jsx)("p",{className:"da-muted",children:e.t("remote.disableHint")}),(0,n.jsx)("div",{className:"da-step",children:e.t("remote.uninstall")}),(0,n.jsx)(K,{ctx:e,value:"toc-ocra service:uninstall"})]})]})}function Me({ctx:e,pending:t,origin:r,busy:o,onPlatform:c,onRegenerate:h,onClose:s}){let[v,u]=(0,C.useState)(()=>Math.floor(Date.now()/1e3));(0,C.useEffect)(()=>{if(t.matched)return;let p=window.setInterval(()=>u(Math.floor(Date.now()/1e3)),1e3);return()=>window.clearInterval(p)},[t.matched]);let d=Math.max(0,t.expiresAt-v),g=!t.matched&&d===0;return(0,n.jsxs)(b.Card,{children:[(0,n.jsx)(b.CardTitle,{children:e.t("remote.pendingTitle",{name:t.deviceName})}),t.matched?(0,n.jsxs)(n.Fragment,{children:[(0,n.jsxs)("p",{className:"da-text",children:[(0,n.jsx)(b.Badge,{variant:"ok",children:e.t("remote.matched")})," ",e.t("remote.matchedBody",{name:t.deviceName})]}),(0,n.jsx)("div",{className:"da-row",children:(0,n.jsx)(b.Btn,{primary:!0,onClick:s,children:e.t("remote.done")})})]}):(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)("p",{className:"da-text",children:e.t("remote.pendingBody")}),(0,n.jsx)("div",{className:"da-segment",role:"radiogroup","aria-label":e.t("remote.platformLabel"),children:ae.map(p=>(0,n.jsx)("button",{type:"button",role:"radio","aria-checked":t.platform===p,className:t.platform===p?"da-seg da-seg-active":"da-seg",onClick:()=>c(p),children:e.t(`remote.platform.${p}`)},p))}),(0,n.jsx)(K,{ctx:e,value:$e(t.platform,t.code,r)}),(0,n.jsxs)("ul",{className:"da-hints",children:[(0,n.jsx)("li",{children:e.t("remote.hintNode")}),t.platform==="linux"&&(0,n.jsx)("li",{children:e.t("remote.hintLinux")}),t.platform==="win"&&(0,n.jsx)("li",{children:e.t("remote.hintWindows")}),(0,n.jsx)("li",{children:g?e.t("remote.expired"):e.t("remote.expires",{minutes:Math.floor(d/60),seconds:String(d%60).padStart(2,"0")})})]}),(0,n.jsx)("p",{className:"da-muted",children:g?"":e.t("remote.waiting")}),(0,n.jsxs)("div",{className:"da-row",children:[(0,n.jsx)(b.Btn,{disabled:o,onClick:h,children:e.t("remote.regenerate")}),(0,n.jsx)(b.Btn,{disabled:o,onClick:s,children:e.t("remote.cancel")})]})]})]})}var a=P(M(),1),Ke=3e3,qe=1e4,He=6e4,We=3,Ce="tab",Ye=new URL("../../assets/icon.svg",import.meta.url).href;function Fe(){return(0,a.jsx)("img",{src:Ye,alt:"",width:16,height:16,draggable:!1})}function _e(e){let t=e.core.service;try{return t&&t.available()?t:void 0}catch{return}}var J=e=>e?.state==="running"||e?.state==="starting";async function ke(e){try{return await E.info(e),!0}catch{return!1}}var H;function je({ctx:e}){let t=re(e,"server"),r=re(e,"workspace"),[o,c]=(0,y.useState)(()=>e.storage.get(Ce,"local")==="remote"?"remote":"local"),h=l=>{e.storage.set(Ce,l),c(l)},[s]=(0,y.useState)(()=>e.storage.get(Z,X)),[v,u]=(0,y.useState)(()=>e.secureStorage.get(A,"")),[d,g]=(0,y.useState)({kind:"checking"}),[p,x]=(0,y.useState)(null),[I,U]=(0,y.useState)(""),[O,S]=(0,y.useState)(!1),N=(0,y.useRef)(!0),_=_e(e),f=(0,y.useCallback)(async()=>{let l=null;if(_)try{l=await _.status()}catch{l=null}if(!N.current)return;x(l);let w=v;J(l)&&l?.token&&(w=l.token,l.token!==v&&(e.secureStorage.set(A,l.token),u(l.token)));try{await E.info(s)}catch{N.current&&g({kind:J(l)?"starting":"offline"});return}if(!w){N.current&&g({kind:"needsCode"});return}try{let T=await E.status(s,w);N.current&&g({kind:"ready",status:T})}catch(T){if(!N.current)return;T instanceof L&&T.kind==="unauthorized"?(e.secureStorage.remove(A),u(""),g({kind:"needsCode"})):g({kind:"offline"})}},[e,s,_,v]);(0,y.useEffect)(()=>{N.current=!0,f();let l=window.setInterval(()=>{f()},Ke);return()=>{N.current=!1,window.clearInterval(l)}},[f]),(0,y.useEffect)(()=>{if(d.kind==="ready"){let l=d.status;e.reportHealth(l.paired&&l.connected?{status:"healthy",summary:e.t("health.online",{name:l.device_name})}:{status:"degraded",summary:e.t(l.paired?"health.disconnected":"health.unpaired")})}else d.kind==="offline"&&e.reportHealth({status:"degraded",summary:e.t(_?"health.disabled":"health.offline")})},[e,d,_]);let B=async l=>{S(!0),U("");try{await l()}catch(w){U(j(e,w))}finally{S(!1)}},R=l=>B(async()=>{if(!_)return;if(!l){e.storage.set(ee,"off"),x(await _.stop()),await f();return}if(e.storage.set(ee,"on"),e.storage.remove(F),!J(p)&&await ke(s)){H?.(),await f();return}let w=await _.start();x(w),w.token&&(e.secureStorage.set(A,w.token),u(w.token)),await Je(s,()=>N.current)&&H?.(),await f()}),$=p?.state==="exited"?p:null,V=!!_&&!J(p)&&(d.kind==="ready"||d.kind==="needsCode");return(0,a.jsxs)("div",{className:"da-app",children:[(0,a.jsx)(m.PageHeader,{title:e.t("page.title"),subtitle:e.t("page.subtitle")}),(0,a.jsx)("div",{className:"da-tabs",role:"tablist","aria-label":e.t("page.title"),children:["local","remote"].map(l=>(0,a.jsx)("button",{type:"button",role:"tab","aria-selected":o===l,className:o===l?"da-tab da-tab-active":"da-tab",onClick:()=>h(l),children:e.t(`tabs.${l}`)},l))}),I&&(0,a.jsx)("div",{role:"alert",className:"da-alert",children:I}),o==="remote"&&(0,a.jsx)(ye,{ctx:e,server:t,workspace:r,localDeviceId:d.kind==="ready"&&d.status.paired?d.status.device_id:""}),o==="local"&&(0,a.jsxs)(a.Fragment,{children:[_&&(0,a.jsx)(Ve,{ctx:e,status:p,external:V,exit:$,busy:O,onToggle:l=>{R(l)}}),d.kind==="checking"&&(0,a.jsx)(m.Card,{children:(0,a.jsx)("span",{className:"da-muted",children:e.t("common.checking")})}),d.kind==="starting"&&(0,a.jsx)(m.Card,{children:(0,a.jsx)("span",{className:"da-muted",children:e.t("service.starting")})}),d.kind==="offline"&&!_&&(0,a.jsx)(Ge,{ctx:e,localUrl:s}),d.kind==="needsCode"&&(0,a.jsx)(Xe,{ctx:e,busy:O,onPair:l=>B(async()=>{let w=await E.pair(s,l).catch(T=>{throw T instanceof L&&T.code==="code_wrong"?new Error(e.t("pair.wrong")):T instanceof L&&T.code==="locked"?new Error(e.t("pair.locked")):T});e.secureStorage.set(A,w.token),u(w.token)})}),d.kind==="ready"&&(0,a.jsx)(Qe,{ctx:e,status:d.status,server:t,workspace:r,busy:O,run:B,localUrl:s,token:v,onStatus:l=>g({kind:"ready",status:l})})]})]})}async function Je(e,t){for(let r=0;r<30&&t();r+=1)try{return await E.info(e),!0}catch{await new Promise(o=>window.setTimeout(o,500))}return!1}function Ve({ctx:e,status:t,external:r,exit:o,busy:c,onToggle:h}){let s=J(t),v=!s&&r,u=s||v,d=o?.exitCode===We;return(0,a.jsxs)(m.Card,{children:[(0,a.jsxs)("div",{className:"da-switch",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)(m.CardTitle,{children:e.t("service.title")}),(0,a.jsx)("p",{className:"da-text",children:e.t(v?"service.onExternalBody":u?"service.onBody":"service.offBody")})]}),(0,a.jsx)(m.Toggle,{checked:u,disabled:c||v,label:e.t("service.title"),onChange:h})]}),s&&(0,a.jsx)("p",{className:"da-muted",children:e.t("service.followsApp")}),v&&(0,a.jsx)("p",{className:"da-muted",children:e.t("service.external")}),!u&&d&&(0,a.jsx)("p",{className:"da-muted",children:e.t("service.portTaken")}),o&&!d&&o.exitCode!==0&&(0,a.jsx)("div",{role:"alert",className:"da-alert",children:e.t("service.crashed",{error:o.error||`exit ${o.exitCode??o.signal??"?"}`})})]})}function Ge({ctx:e,localUrl:t}){return(0,a.jsxs)(m.Card,{children:[(0,a.jsx)(m.CardTitle,{children:e.t("offline.title")}),(0,a.jsx)("p",{className:"da-text",children:e.t("offline.body",{url:t})}),(0,a.jsx)("div",{className:"da-step",children:e.t("offline.once")}),(0,a.jsx)(K,{ctx:e,value:"npx toc-ocra@latest run"}),(0,a.jsx)("div",{className:"da-step",children:e.t("offline.service")}),(0,a.jsx)(K,{ctx:e,value:"npm install -g toc-ocra@latest && toc-ocra service:install"}),(0,a.jsx)("p",{className:"da-muted",children:e.t("offline.node")})]})}function Xe({ctx:e,busy:t,onPair:r}){let[o,c]=(0,y.useState)(""),h=/^\d{6}$/.test(o);return(0,a.jsxs)(m.Card,{children:[(0,a.jsx)(m.CardTitle,{children:e.t("pair.title")}),(0,a.jsx)("p",{className:"da-text",children:e.t("pair.body")}),(0,a.jsx)(K,{ctx:e,value:"toc-ocra code"}),(0,a.jsxs)("form",{className:"da-row",onSubmit:s=>{s.preventDefault(),h&&r(o)},children:[(0,a.jsx)(m.Input,{value:o,onChange:s=>c(s.target.value.replace(/\D/g,"").slice(0,6)),placeholder:e.t("pair.placeholder"),inputMode:"numeric",autoComplete:"one-time-code","aria-label":e.t("pair.placeholder"),className:"da-code-input"}),(0,a.jsx)(m.Btn,{primary:!0,type:"submit",disabled:!h||t,children:e.t("pair.submit")})]})]})}function Qe({ctx:e,status:t,server:r,workspace:o,busy:c,run:h,localUrl:s,token:v,onStatus:u}){let d=t.paired&&r&&!Q(t.server_url,r.origin),g=()=>h(async()=>{t.device_id&&r&&Q(t.server_url,r.origin)&&await e.core.request(`/devices/${encodeURIComponent(t.device_id)}/agent`,{method:"DELETE"}).catch(()=>{}),e.storage.set(F,"off"),u(await E.unpair(s,v))});return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(m.Card,{children:[(0,a.jsx)(m.CardTitle,{children:e.t("status.title")}),(0,a.jsxs)("dl",{className:"da-grid",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:e.t("status.service")}),(0,a.jsxs)("dd",{children:["toc-ocra ",t.version," \xB7 ",t.hostname]})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:e.t("status.binding")}),(0,a.jsx)("dd",{children:(0,a.jsx)(Ze,{ctx:e,status:t})})]}),t.paired&&(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:e.t("status.device")}),(0,a.jsx)("dd",{children:t.device_name})]}),t.paired&&(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:e.t("status.server")}),(0,a.jsx)("dd",{className:"da-mono",children:t.server_url})]}),t.paired&&(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:e.t("status.runs")}),(0,a.jsx)("dd",{children:t.active_runs})]})]}),t.paired&&!t.connected&&t.connection_error&&(0,a.jsx)("p",{className:"da-muted",children:e.t("status.lastError",{error:t.connection_error})}),t.token_from_env&&(0,a.jsx)("p",{className:"da-muted",children:e.t("status.tokenFromEnv")}),t.paired&&(0,a.jsx)("div",{className:"da-row",children:(0,a.jsx)(m.Btn,{danger:!0,disabled:c||t.token_from_env,onClick:()=>{g()},children:e.t("status.unpair")})})]}),(!t.paired||t.unauthorized||d)&&(0,a.jsx)(et,{ctx:e,status:t,server:r,workspace:o,busy:c,run:h,localUrl:s,token:v,onStatus:u,rebind:t.paired}),(0,a.jsxs)(m.Card,{children:[(0,a.jsx)(m.CardTitle,{children:e.t("cli.title")}),(0,a.jsxs)("ul",{className:"da-list",children:[t.providers.filter(p=>p.installed).map(p=>(0,a.jsxs)("li",{children:[(0,a.jsx)("span",{className:"da-mono",children:p.id}),(0,a.jsx)("span",{className:"da-muted",children:p.version}),p.runnable?(0,a.jsx)(m.Badge,{variant:"ok",children:e.t("cli.runnable")}):(0,a.jsx)(m.Badge,{variant:"muted",children:e.t("cli.detectOnly")})]},p.id)),!t.providers.some(p=>p.installed)&&(0,a.jsx)("li",{className:"da-muted",children:e.t("cli.none")})]}),(0,a.jsx)("div",{className:"da-row",children:(0,a.jsx)(m.Btn,{disabled:c,onClick:()=>{h(async()=>u(await E.detect(s,v)))},children:e.t("cli.redetect")})})]})]})}function Ze({ctx:e,status:t}){return t.paired?t.unauthorized?(0,a.jsx)(m.Badge,{variant:"err",children:e.t("binding.revoked")}):t.connected?(0,a.jsx)(m.Badge,{variant:"ok",children:e.t("binding.online")}):(0,a.jsx)(m.Badge,{variant:"warn",children:e.t("binding.connecting")}):(0,a.jsx)(m.Badge,{variant:"muted",children:e.t("binding.unpaired")})}function et({ctx:e,status:t,server:r,workspace:o,busy:c,run:h,localUrl:s,token:v,onStatus:u,rebind:d}){let[g,p]=(0,y.useState)([]),[x,I]=(0,y.useState)("new"),[U,O]=(0,y.useState)(t.hostname),[S,N]=(0,y.useState)(r?.origin??"");(0,y.useEffect)(()=>{r?.origin&&N(r.origin)},[r?.origin]),(0,y.useEffect)(()=>{if(!o?.id)return;let f=!1;return e.core.request(`/workspaces/${encodeURIComponent(o.id)}/devices`).then(B=>{f||p((B.items??[]).filter(R=>R.is_mine))}).catch(()=>{f||p([])}),()=>{f=!0}},[e,o?.id]);let _=()=>h(async()=>{if(!o?.id)throw new Error(e.t("bind.noWorkspace"));if(!S.trim())throw new Error(e.t("bind.noServer"));let f=x;x==="new"&&(f=(await e.core.request(`/workspaces/${encodeURIComponent(o.id)}/devices`,{method:"POST",body:JSON.stringify({name:U.trim()||t.hostname,location:"local",platform:t.platform,note:"",share_to_workspace:!1})})).id,I(f));let B=await e.core.request(`/devices/${encodeURIComponent(f)}/pairing-code`,{method:"POST"});u(await E.enroll(s,v,S.trim(),B.code)),e.storage.remove(F),e.notify({title:e.t("page.title"),body:e.t("bind.done",{server:r?.name??S})})});return(0,a.jsxs)(m.Card,{children:[(0,a.jsx)(m.CardTitle,{children:e.t(d?"bind.rebindTitle":"bind.title")}),d&&t.unauthorized&&(0,a.jsx)("p",{className:"da-text",children:e.t("bind.revoked")}),d&&!t.unauthorized&&(0,a.jsx)("p",{className:"da-text",children:e.t("bind.elsewhere",{bound:t.server_url,current:r?.origin??""})}),(0,a.jsx)("p",{className:"da-text",children:e.t("bind.body",{server:r?.name??"",workspace:o?.name??""})}),(0,a.jsxs)("label",{className:"da-field",children:[(0,a.jsx)("span",{children:e.t("bind.server")}),(0,a.jsx)(m.Input,{value:S,onChange:f=>N(f.target.value),spellCheck:!1,className:"da-mono"})]}),(0,a.jsxs)("label",{className:"da-field",children:[(0,a.jsx)("span",{children:e.t("bind.device")}),(0,a.jsxs)("select",{className:"da-select",value:x,onChange:f=>I(f.target.value),children:[(0,a.jsx)("option",{value:"new",children:e.t("bind.newDevice")}),g.map(f=>(0,a.jsxs)("option",{value:f.id,children:[f.name,f.paired?` \xB7 ${e.t("bind.replaces")}`:""]},f.id))]})]}),x==="new"&&(0,a.jsxs)("label",{className:"da-field",children:[(0,a.jsx)("span",{children:e.t("bind.name")}),(0,a.jsx)(m.Input,{value:U,onChange:f=>O(f.target.value),maxLength:100})]}),(0,a.jsx)("div",{className:"da-row",children:(0,a.jsx)(m.Btn,{primary:!0,disabled:c||!o?.id,onClick:()=>{_()},children:e.t(c?"bind.working":"bind.submit")})})]})}function tt(e){let t=!1,r="",o=new Map,c=async()=>{if(t||e.signal.aborted)return;let s=e.core.context("server"),v=e.secureStorage.get(A,"");if(!(!s?.origin||!v||e.storage.get(F,"on")==="off")&&!(Date.now()-(o.get(s.origin)??0)<He)){t=!0;try{let u=e.storage.get(Z,X),d=await E.status(u,v);if(!d.machine_id||d.token_from_env)return;if(d.paired&&(d.unauthorized||Q(d.server_url,s.origin))){r="";return}if(d.active_runs>0){r!==s.origin&&(r=s.origin,e.notify({title:e.t("page.title"),body:e.t("auto.deferred",{server:s.name||s.origin})}));return}let g=await e.core.request("/devices/machine/pairing-code",{method:"POST",body:JSON.stringify({machine_id:d.machine_id,hostname:d.hostname,platform:d.platform,current_device_id:d.paired?d.device_id:""})});await E.enroll(u,v,s.origin,g.code),r="",o.delete(s.origin),e.notify({title:e.t("page.title"),body:e.t("auto.done",{server:s.name||s.origin,device:g.device_name})})}catch(u){(!(u instanceof L)||u.kind==="rejected")&&o.set(s.origin,Date.now())}finally{t=!1}}},h=window.setInterval(()=>{c()},qe);H=()=>{c()},e.registerDisposer(()=>{window.clearInterval(h),H=void 0}),e.registerDisposer(e.core.onContextChange("server",()=>{c()})),window.setTimeout(()=>{c()},1500)}function at(e){let t=_e(e);if(!t||e.storage.get(ee,"off")!=="on")return;let r=e.storage.get(Z,X);ke(r).then(o=>{if(o){window.setTimeout(()=>H?.(),3e3);return}return t.start()}).then(o=>{o&&(o.token&&e.secureStorage.set(A,o.token),window.setTimeout(()=>H?.(),3e3))}).catch(()=>{})}var yt=me({activate(e){e.registerNavigation("agent.nav",(0,a.jsx)(Fe,{})),e.registerRoute("agent.home",(0,a.jsx)(je,{ctx:e})),at(e),tt(e)}});export{yt as default};
2
2
  //# sourceMappingURL=renderer.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["toc-shared:react", "toc-shared:@toc-starter/plugin-sdk/ui", "toc-shared:react/jsx-runtime", "../../plugin/index.tsx", "../../node_modules/@toc-starter/plugin-sdk/index.js", "../../plugin/local.ts", "../../plugin/remote.tsx", "../../plugin/shared.tsx"],
4
- "sourcesContent": ["var registry = globalThis[\"__TOC_PLUGIN_SHARED__\"];\nvar shared = registry && registry[\"react\"];\nif (!shared) throw new Error(\"The host does not provide react; this plugin needs a newer app version.\");\nmodule.exports = shared;", "var registry = globalThis[\"__TOC_PLUGIN_SHARED__\"];\nvar shared = registry && registry[\"@toc-starter/plugin-sdk/ui\"];\nif (!shared) throw new Error(\"The host does not provide @toc-starter/plugin-sdk/ui; this plugin needs a newer app version.\");\nmodule.exports = shared;", "var registry = globalThis[\"__TOC_PLUGIN_SHARED__\"];\nvar shared = registry && registry[\"react/jsx-runtime\"];\nif (!shared) throw new Error(\"The host does not provide react/jsx-runtime; this plugin needs a newer app version.\");\nmodule.exports = shared;", "import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { definePlugin, useHostContext, type LocalServiceApi, type LocalServiceStatus, type PluginContext } from \"@toc-starter/plugin-sdk\";\nimport { Badge, Btn, Card, CardTitle, Input, PageHeader, Toggle } from \"@toc-starter/plugin-sdk/ui\";\nimport \"./styles.css\";\nimport { DEFAULT_LOCAL_URL, LocalError, local, sameServer, type LocalStatus } from \"./local\";\nimport { RemoteTab } from \"./remote\";\nimport { AUTO_BIND_KEY, Command, LOCAL_SERVICE_KEY, LOCAL_URL_KEY, TOKEN_KEY, errorText, type Device, type ServerContext, type WorkspaceContext } from \"./shared\";\n\nconst POLL_MS = 3_000;\nconst AUTO_BIND_MS = 10_000;\nconst AUTO_BIND_RETRY_MS = 60_000;\n/** toc-ocra's service entry exits with this when another toc-ocra holds the local port. */\nconst EXIT_PORT_IN_USE = 3;\nconst TAB_KEY = \"tab\";\n\n/** Resolved against dist/plugin/renderer.js. */\nconst iconUrl = new URL(\"../../assets/icon.svg\", import.meta.url).href;\n\ntype Phase =\n | { kind: \"checking\" }\n | { kind: \"offline\" }\n | { kind: \"starting\" }\n | { kind: \"needsCode\" }\n | { kind: \"ready\"; status: LocalStatus };\n\ntype Tab = \"local\" | \"remote\";\n\nfunction Icon() {\n return <img src={iconUrl} alt=\"\" width={16} height={16} draggable={false} />;\n}\n\n/** The desktop app's service API; undefined on hosts that predate it. */\nfunction localService(ctx: PluginContext): LocalServiceApi | undefined {\n const service = (ctx.core as { service?: LocalServiceApi }).service;\n try {\n return service && service.available() ? service : undefined;\n } catch {\n return undefined;\n }\n}\n\nconst isActive = (status: LocalServiceStatus | null | undefined) => status?.state === \"running\" || status?.state === \"starting\";\n\n/** Runs the auto-bind check right away (set by {@link startAutoBind}). */\nlet triggerAutoBind: (() => void) | undefined;\n\nfunction AgentPage({ ctx }: { ctx: PluginContext }) {\n const server = useHostContext<ServerContext>(ctx, \"server\");\n const workspace = useHostContext<WorkspaceContext>(ctx, \"workspace\");\n const [tab, setTabState] = useState<Tab>(() => (ctx.storage.get<string>(TAB_KEY, \"local\") === \"remote\" ? \"remote\" : \"local\"));\n const setTab = (next: Tab) => {\n ctx.storage.set(TAB_KEY, next);\n setTabState(next);\n };\n const [localUrl] = useState(() => ctx.storage.get(LOCAL_URL_KEY, DEFAULT_LOCAL_URL));\n const [token, setToken] = useState(() => ctx.secureStorage.get(TOKEN_KEY, \"\"));\n const [phase, setPhase] = useState<Phase>({ kind: \"checking\" });\n const [service, setService] = useState<LocalServiceStatus | null>(null);\n const [error, setError] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const alive = useRef(true);\n const svc = localService(ctx);\n\n const refresh = useCallback(async () => {\n let hosted: LocalServiceStatus | null = null;\n if (svc) {\n try {\n hosted = await svc.status();\n } catch {\n hosted = null;\n }\n }\n if (!alive.current) return;\n setService(hosted);\n // A service the app started brings its own control token.\n let active = token;\n if (isActive(hosted) && hosted?.token) {\n active = hosted.token;\n if (hosted.token !== token) {\n ctx.secureStorage.set(TOKEN_KEY, hosted.token);\n setToken(hosted.token);\n }\n }\n try {\n await local.info(localUrl);\n } catch {\n if (alive.current) setPhase({ kind: isActive(hosted) ? \"starting\" : \"offline\" });\n return;\n }\n if (!active) {\n if (alive.current) setPhase({ kind: \"needsCode\" });\n return;\n }\n try {\n const status = await local.status(localUrl, active);\n if (alive.current) setPhase({ kind: \"ready\", status });\n } catch (reason) {\n if (!alive.current) return;\n if (reason instanceof LocalError && reason.kind === \"unauthorized\") {\n // Reinstalled service (new control token), or another toc-ocra holds the port: pair again.\n ctx.secureStorage.remove(TOKEN_KEY);\n setToken(\"\");\n setPhase({ kind: \"needsCode\" });\n } else {\n setPhase({ kind: \"offline\" });\n }\n }\n }, [ctx, localUrl, svc, token]);\n\n useEffect(() => {\n alive.current = true;\n void refresh();\n const timer = window.setInterval(() => void refresh(), POLL_MS);\n return () => { alive.current = false; window.clearInterval(timer); };\n }, [refresh]);\n\n useEffect(() => {\n if (phase.kind === \"ready\") {\n const s = phase.status;\n ctx.reportHealth(s.paired && s.connected ? { status: \"healthy\", summary: ctx.t(\"health.online\", { name: s.device_name }) } : { status: \"degraded\", summary: ctx.t(s.paired ? \"health.disconnected\" : \"health.unpaired\") });\n } else if (phase.kind === \"offline\") {\n ctx.reportHealth({ status: \"degraded\", summary: ctx.t(svc ? \"health.disabled\" : \"health.offline\") });\n }\n }, [ctx, phase, svc]);\n\n const run = async (action: () => Promise<void>) => {\n setBusy(true);\n setError(\"\");\n try {\n await action();\n } catch (reason) {\n setError(errorText(ctx, reason));\n } finally {\n setBusy(false);\n }\n };\n\n const toggleService = (on: boolean) => run(async () => {\n if (!svc) return;\n if (!on) {\n ctx.storage.set(LOCAL_SERVICE_KEY, \"off\");\n setService(await svc.stop());\n await refresh();\n return;\n }\n ctx.storage.set(LOCAL_SERVICE_KEY, \"on\");\n // Turning it on means \"take tasks here\": undo a manual unbind.\n ctx.storage.remove(AUTO_BIND_KEY);\n const started = await svc.start();\n setService(started);\n if (started.token) {\n ctx.secureStorage.set(TOKEN_KEY, started.token);\n setToken(started.token);\n }\n if (await waitForLocalService(localUrl, () => alive.current)) triggerAutoBind?.();\n await refresh();\n });\n\n const hostedExit = service?.state === \"exited\" ? service : null;\n const external = Boolean(svc) && !isActive(service) && (phase.kind === \"ready\" || phase.kind === \"needsCode\");\n\n return (\n <div className=\"da-app\">\n <PageHeader title={ctx.t(\"page.title\")} subtitle={ctx.t(\"page.subtitle\")} />\n <div className=\"da-tabs\" role=\"tablist\" aria-label={ctx.t(\"page.title\")}>\n {([\"local\", \"remote\"] as const).map(item => (\n <button key={item} type=\"button\" role=\"tab\" aria-selected={tab === item} className={tab === item ? \"da-tab da-tab-active\" : \"da-tab\"} onClick={() => setTab(item)}>\n {ctx.t(`tabs.${item}`)}\n </button>\n ))}\n </div>\n {error && <div role=\"alert\" className=\"da-alert\">{error}</div>}\n\n {tab === \"remote\" && (\n <RemoteTab ctx={ctx} server={server} workspace={workspace} localDeviceId={phase.kind === \"ready\" && phase.status.paired ? phase.status.device_id : \"\"} />\n )}\n\n {tab === \"local\" && (\n <>\n {svc && (\n <LocalServiceCard\n ctx={ctx}\n status={service}\n external={external}\n exit={hostedExit}\n busy={busy}\n onToggle={on => void toggleService(on)}\n />\n )}\n {phase.kind === \"checking\" && <Card><span className=\"da-muted\">{ctx.t(\"common.checking\")}</span></Card>}\n {phase.kind === \"starting\" && <Card><span className=\"da-muted\">{ctx.t(\"service.starting\")}</span></Card>}\n {phase.kind === \"offline\" && !svc && <OfflineCard ctx={ctx} localUrl={localUrl} />}\n {phase.kind === \"needsCode\" && (\n <PairCard ctx={ctx} busy={busy} onPair={code => run(async () => {\n const result = await local.pair(localUrl, code).catch((reason: unknown) => {\n if (reason instanceof LocalError && reason.code === \"code_wrong\") throw new Error(ctx.t(\"pair.wrong\"));\n if (reason instanceof LocalError && reason.code === \"locked\") throw new Error(ctx.t(\"pair.locked\"));\n throw reason;\n });\n ctx.secureStorage.set(TOKEN_KEY, result.token);\n setToken(result.token);\n })} />\n )}\n {phase.kind === \"ready\" && (\n <ReadyView\n ctx={ctx}\n status={phase.status}\n server={server}\n workspace={workspace}\n busy={busy}\n run={run}\n localUrl={localUrl}\n token={token}\n onStatus={status => setPhase({ kind: \"ready\", status })}\n />\n )}\n </>\n )}\n </div>\n );\n}\n\n/** Waits up to 15 s for the local service to answer. */\nasync function waitForLocalService(localUrl: string, alive: () => boolean): Promise<boolean> {\n for (let attempt = 0; attempt < 30 && alive(); attempt += 1) {\n try {\n await local.info(localUrl);\n return true;\n } catch {\n await new Promise(resolve => window.setTimeout(resolve, 500));\n }\n }\n return false;\n}\n\nfunction LocalServiceCard({ ctx, status, external, exit, busy, onToggle }: {\n ctx: PluginContext;\n status: LocalServiceStatus | null;\n external: boolean;\n exit: LocalServiceStatus | null;\n busy: boolean;\n onToggle: (on: boolean) => void;\n}) {\n const on = isActive(status);\n const portTaken = exit?.exitCode === EXIT_PORT_IN_USE;\n return (\n <Card>\n <div className=\"da-switch\">\n <div>\n <CardTitle>{ctx.t(\"service.title\")}</CardTitle>\n <p className=\"da-text\">{ctx.t(on ? \"service.onBody\" : \"service.offBody\")}</p>\n </div>\n <Toggle checked={on} disabled={busy} label={ctx.t(\"service.title\")} onChange={onToggle} />\n </div>\n {on && <p className=\"da-muted\">{ctx.t(\"service.followsApp\")}</p>}\n {!on && (external || portTaken) && <p className=\"da-muted\">{ctx.t(\"service.external\")}</p>}\n {exit && !portTaken && exit.exitCode !== 0 && (\n <div role=\"alert\" className=\"da-alert\">{ctx.t(\"service.crashed\", { error: exit.error || `exit ${exit.exitCode ?? exit.signal ?? \"?\"}` })}</div>\n )}\n </Card>\n );\n}\n\nfunction OfflineCard({ ctx, localUrl }: { ctx: PluginContext; localUrl: string }) {\n return (\n <Card>\n <CardTitle>{ctx.t(\"offline.title\")}</CardTitle>\n <p className=\"da-text\">{ctx.t(\"offline.body\", { url: localUrl })}</p>\n <div className=\"da-step\">{ctx.t(\"offline.once\")}</div>\n <Command ctx={ctx} value=\"npx toc-ocra@latest run\" />\n <div className=\"da-step\">{ctx.t(\"offline.service\")}</div>\n <Command ctx={ctx} value=\"npm install -g toc-ocra@latest && toc-ocra service:install\" />\n <p className=\"da-muted\">{ctx.t(\"offline.node\")}</p>\n </Card>\n );\n}\n\nfunction PairCard({ ctx, busy, onPair }: { ctx: PluginContext; busy: boolean; onPair: (code: string) => void }) {\n const [code, setCode] = useState(\"\");\n const valid = /^\\d{6}$/.test(code);\n return (\n <Card>\n <CardTitle>{ctx.t(\"pair.title\")}</CardTitle>\n <p className=\"da-text\">{ctx.t(\"pair.body\")}</p>\n <Command ctx={ctx} value=\"toc-ocra code\" />\n <form className=\"da-row\" onSubmit={event => { event.preventDefault(); if (valid) onPair(code); }}>\n <Input\n value={code}\n onChange={event => setCode(event.target.value.replace(/\\D/g, \"\").slice(0, 6))}\n placeholder={ctx.t(\"pair.placeholder\")}\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n aria-label={ctx.t(\"pair.placeholder\")}\n className=\"da-code-input\"\n />\n <Btn primary type=\"submit\" disabled={!valid || busy}>{ctx.t(\"pair.submit\")}</Btn>\n </form>\n </Card>\n );\n}\n\nfunction ReadyView({ ctx, status, server, workspace, busy, run, localUrl, token, onStatus }: {\n ctx: PluginContext;\n status: LocalStatus;\n server: ServerContext | undefined;\n workspace: WorkspaceContext | undefined;\n busy: boolean;\n run: (action: () => Promise<void>) => Promise<void>;\n localUrl: string;\n token: string;\n onStatus: (status: LocalStatus) => void;\n}) {\n const boundElsewhere = status.paired && server && !sameServer(status.server_url, server.origin);\n const unpair = () => run(async () => {\n // Revoke on the server too when the device belongs to the server shown.\n if (status.device_id && server && sameServer(status.server_url, server.origin)) {\n await ctx.core.request(`/devices/${encodeURIComponent(status.device_id)}/agent`, { method: \"DELETE\" }).catch(() => undefined);\n }\n ctx.storage.set(AUTO_BIND_KEY, \"off\");\n onStatus(await local.unpair(localUrl, token));\n });\n return (\n <>\n <Card>\n <CardTitle>{ctx.t(\"status.title\")}</CardTitle>\n <dl className=\"da-grid\">\n <div><dt>{ctx.t(\"status.service\")}</dt><dd>toc-ocra {status.version} \u00B7 {status.hostname}</dd></div>\n <div><dt>{ctx.t(\"status.binding\")}</dt><dd><BindingBadge ctx={ctx} status={status} /></dd></div>\n {status.paired && <div><dt>{ctx.t(\"status.device\")}</dt><dd>{status.device_name}</dd></div>}\n {status.paired && <div><dt>{ctx.t(\"status.server\")}</dt><dd className=\"da-mono\">{status.server_url}</dd></div>}\n {status.paired && <div><dt>{ctx.t(\"status.runs\")}</dt><dd>{status.active_runs}</dd></div>}\n </dl>\n {status.paired && !status.connected && status.connection_error && <p className=\"da-muted\">{ctx.t(\"status.lastError\", { error: status.connection_error })}</p>}\n {status.token_from_env && <p className=\"da-muted\">{ctx.t(\"status.tokenFromEnv\")}</p>}\n {status.paired && (\n <div className=\"da-row\">\n <Btn danger disabled={busy || status.token_from_env} onClick={() => void unpair()}>{ctx.t(\"status.unpair\")}</Btn>\n </div>\n )}\n </Card>\n\n {(!status.paired || status.unauthorized || boundElsewhere) && (\n <BindCard ctx={ctx} status={status} server={server} workspace={workspace} busy={busy} run={run} localUrl={localUrl} token={token} onStatus={onStatus} rebind={status.paired} />\n )}\n\n <Card>\n <CardTitle>{ctx.t(\"cli.title\")}</CardTitle>\n <ul className=\"da-list\">\n {status.providers.filter(provider => provider.installed).map(provider => (\n <li key={provider.id}>\n <span className=\"da-mono\">{provider.id}</span>\n <span className=\"da-muted\">{provider.version}</span>\n {provider.runnable ? <Badge variant=\"ok\">{ctx.t(\"cli.runnable\")}</Badge> : <Badge variant=\"muted\">{ctx.t(\"cli.detectOnly\")}</Badge>}\n </li>\n ))}\n {!status.providers.some(provider => provider.installed) && <li className=\"da-muted\">{ctx.t(\"cli.none\")}</li>}\n </ul>\n <div className=\"da-row\">\n <Btn disabled={busy} onClick={() => void run(async () => onStatus(await local.detect(localUrl, token)))}>{ctx.t(\"cli.redetect\")}</Btn>\n </div>\n </Card>\n </>\n );\n}\n\nfunction BindingBadge({ ctx, status }: { ctx: PluginContext; status: LocalStatus }) {\n if (!status.paired) return <Badge variant=\"muted\">{ctx.t(\"binding.unpaired\")}</Badge>;\n if (status.unauthorized) return <Badge variant=\"err\">{ctx.t(\"binding.revoked\")}</Badge>;\n if (status.connected) return <Badge variant=\"ok\">{ctx.t(\"binding.online\")}</Badge>;\n return <Badge variant=\"warn\">{ctx.t(\"binding.connecting\")}</Badge>;\n}\n\nfunction BindCard({ ctx, status, server, workspace, busy, run, localUrl, token, onStatus, rebind }: {\n ctx: PluginContext;\n status: LocalStatus;\n server: ServerContext | undefined;\n workspace: WorkspaceContext | undefined;\n busy: boolean;\n run: (action: () => Promise<void>) => Promise<void>;\n localUrl: string;\n token: string;\n onStatus: (status: LocalStatus) => void;\n rebind: boolean;\n}) {\n const [devices, setDevices] = useState<Device[]>([]);\n const [choice, setChoice] = useState(\"new\");\n const [name, setName] = useState(status.hostname);\n const [serverUrl, setServerUrl] = useState(server?.origin ?? \"\");\n useEffect(() => { if (server?.origin) setServerUrl(server.origin); }, [server?.origin]);\n\n useEffect(() => {\n if (!workspace?.id) return;\n let cancelled = false;\n ctx.core.request<{ items?: Device[] }>(`/workspaces/${encodeURIComponent(workspace.id)}/devices`)\n .then(result => { if (!cancelled) setDevices((result.items ?? []).filter(device => device.is_mine)); })\n .catch(() => { if (!cancelled) setDevices([]); });\n return () => { cancelled = true; };\n }, [ctx, workspace?.id]);\n\n const bind = () => run(async () => {\n if (!workspace?.id) throw new Error(ctx.t(\"bind.noWorkspace\"));\n if (!serverUrl.trim()) throw new Error(ctx.t(\"bind.noServer\"));\n let deviceId = choice;\n if (choice === \"new\") {\n const created = await ctx.core.request<{ id: string }>(`/workspaces/${encodeURIComponent(workspace.id)}/devices`, {\n method: \"POST\",\n body: JSON.stringify({ name: name.trim() || status.hostname, location: \"local\", platform: status.platform, note: \"\", share_to_workspace: false }),\n });\n deviceId = created.id;\n setChoice(deviceId);\n }\n const pairing = await ctx.core.request<{ code: string }>(`/devices/${encodeURIComponent(deviceId)}/pairing-code`, { method: \"POST\" });\n onStatus(await local.enroll(localUrl, token, serverUrl.trim(), pairing.code));\n ctx.storage.remove(AUTO_BIND_KEY);\n ctx.notify({ title: ctx.t(\"page.title\"), body: ctx.t(\"bind.done\", { server: server?.name ?? serverUrl }) });\n });\n\n return (\n <Card>\n <CardTitle>{ctx.t(rebind ? \"bind.rebindTitle\" : \"bind.title\")}</CardTitle>\n {rebind && status.unauthorized && <p className=\"da-text\">{ctx.t(\"bind.revoked\")}</p>}\n {rebind && !status.unauthorized && <p className=\"da-text\">{ctx.t(\"bind.elsewhere\", { bound: status.server_url, current: server?.origin ?? \"\" })}</p>}\n <p className=\"da-text\">{ctx.t(\"bind.body\", { server: server?.name ?? \"\", workspace: workspace?.name ?? \"\" })}</p>\n <label className=\"da-field\">\n <span>{ctx.t(\"bind.server\")}</span>\n <Input value={serverUrl} onChange={event => setServerUrl(event.target.value)} spellCheck={false} className=\"da-mono\" />\n </label>\n <label className=\"da-field\">\n <span>{ctx.t(\"bind.device\")}</span>\n <select className=\"da-select\" value={choice} onChange={event => setChoice(event.target.value)}>\n <option value=\"new\">{ctx.t(\"bind.newDevice\")}</option>\n {devices.map(device => (\n <option key={device.id} value={device.id}>{device.name}{device.paired ? ` \u00B7 ${ctx.t(\"bind.replaces\")}` : \"\"}</option>\n ))}\n </select>\n </label>\n {choice === \"new\" && (\n <label className=\"da-field\">\n <span>{ctx.t(\"bind.name\")}</span>\n <Input value={name} onChange={event => setName(event.target.value)} maxLength={100} />\n </label>\n )}\n <div className=\"da-row\">\n <Btn primary disabled={busy || !workspace?.id} onClick={() => void bind()}>{ctx.t(busy ? \"bind.working\" : \"bind.submit\")}</Btn>\n </div>\n </Card>\n );\n}\n\n/**\n * Keeps this computer bound to the server the desktop app is signed in to\n * (public or LAN): when they differ, asks the server for this machine's device\n * (matched by machine id, so the same device is reused) and re-enrolls the\n * local service. Waits while tasks are running; stays out of the way after\n * the user unbinds by hand or when the server revoked the device.\n */\nfunction startAutoBind(ctx: PluginContext) {\n let busy = false;\n let deferredFor = \"\";\n const failedAt = new Map<string, number>();\n\n const tick = async () => {\n if (busy || ctx.signal.aborted) return;\n const server = ctx.core.context<ServerContext>(\"server\");\n const token = ctx.secureStorage.get(TOKEN_KEY, \"\");\n if (!server?.origin || !token || ctx.storage.get<string>(AUTO_BIND_KEY, \"on\") === \"off\") return;\n if (Date.now() - (failedAt.get(server.origin) ?? 0) < AUTO_BIND_RETRY_MS) return;\n busy = true;\n try {\n const localUrl = ctx.storage.get(LOCAL_URL_KEY, DEFAULT_LOCAL_URL);\n const status = await local.status(localUrl, token);\n if (!status.machine_id || status.token_from_env) return;\n if (status.paired && (status.unauthorized || sameServer(status.server_url, server.origin))) {\n deferredFor = \"\";\n return;\n }\n if (status.active_runs > 0) {\n if (deferredFor !== server.origin) {\n deferredFor = server.origin;\n ctx.notify({ title: ctx.t(\"page.title\"), body: ctx.t(\"auto.deferred\", { server: server.name || server.origin }) });\n }\n return;\n }\n const pairing = await ctx.core.request<{ code: string; device_name: string }>(\"/devices/machine/pairing-code\", {\n method: \"POST\",\n body: JSON.stringify({\n machine_id: status.machine_id,\n hostname: status.hostname,\n platform: status.platform,\n current_device_id: status.paired ? status.device_id : \"\",\n }),\n });\n await local.enroll(localUrl, token, server.origin, pairing.code);\n deferredFor = \"\";\n failedAt.delete(server.origin);\n ctx.notify({ title: ctx.t(\"page.title\"), body: ctx.t(\"auto.done\", { server: server.name || server.origin, device: pairing.device_name }) });\n } catch (reason) {\n // Local service offline / plugin not paired yet: just try again next tick.\n // A server or enroll failure backs off for this server.\n if (!(reason instanceof LocalError) || reason.kind === \"rejected\") failedAt.set(server.origin, Date.now());\n } finally {\n busy = false;\n }\n };\n\n const timer = window.setInterval(() => void tick(), AUTO_BIND_MS);\n triggerAutoBind = () => void tick();\n ctx.registerDisposer(() => {\n window.clearInterval(timer);\n triggerAutoBind = undefined;\n });\n ctx.registerDisposer(ctx.core.onContextChange(\"server\", () => void tick()));\n window.setTimeout(() => void tick(), 1_500);\n}\n\n/**\n * Follows the desktop app: when the user left the local agent on, start it\n * again with the app. The app stops it when it quits.\n */\nfunction startLocalServiceIfEnabled(ctx: PluginContext) {\n const svc = localService(ctx);\n if (!svc || ctx.storage.get<string>(LOCAL_SERVICE_KEY, \"off\") !== \"on\") return;\n void svc.start()\n .then(status => {\n if (status.token) ctx.secureStorage.set(TOKEN_KEY, status.token);\n window.setTimeout(() => triggerAutoBind?.(), 3_000);\n })\n .catch(() => undefined);\n}\n\nexport default definePlugin({\n activate(ctx) {\n ctx.registerNavigation(\"agent.nav\", <Icon />);\n ctx.registerRoute(\"agent.home\", <AgentPage ctx={ctx} />);\n startLocalServiceIfEnabled(ctx);\n startAutoBind(ctx);\n },\n});\n", "/**\n * Runtime part of the SDK that ends up inside plugin bundles. Keep it tiny:\n * everything here is copied into every plugin. Build-time helpers live in lib/.\n * `react` resolves to the host's instance through `toc-plugin build`.\n */\nimport { useSyncExternalStore } from \"react\";\n\n/** Host contract this SDK targets. Must match PLUGIN_API_VERSION in lib/constants.mjs. */\nexport const PLUGIN_API_VERSION = \"1.0.0\";\n\n/**\n * Identity helper that gives a plugin module full type inference.\n *\n * @template {import(\"./index\").PluginModule} T\n * @param {T} plugin\n * @returns {T}\n */\nexport function definePlugin(plugin) {\n if (!plugin || typeof plugin.activate !== \"function\") {\n throw new TypeError(\"definePlugin() needs an object with an activate(context) function\");\n }\n return plugin;\n}\n\n/**\n * Re-renders when the app publishes a new value under `key`.\n *\n * @template T\n * @param {import(\"./index\").PluginContext} ctx\n * @param {string} key\n * @returns {T | undefined}\n */\nexport function useHostContext(ctx, key) {\n return useSyncExternalStore(\n listener => ctx.core.onContextChange(key, listener),\n () => ctx.core.context(key),\n );\n}\n", "/**\n * Client for the local toc-ocra service (`toc-ocra run`) on this computer.\n * The plugin pairs once with the 6-digit connection code and then sends the\n * control token as a bearer token.\n */\nexport const DEFAULT_LOCAL_URL = \"http://127.0.0.1:17339\";\n\nexport interface LocalInfo {\n name: string;\n version: string;\n protocol: number;\n paired: boolean;\n}\n\nexport interface LocalProvider {\n id: string;\n installed: boolean;\n runnable: boolean;\n version: string;\n}\n\nexport interface LocalStatus {\n version: string;\n hostname: string;\n /** Stable id of this installation (older services omit it). */\n machine_id?: string;\n platform: \"mac\" | \"win\" | \"linux\";\n paired: boolean;\n running: boolean;\n device_id: string;\n device_name: string;\n server_url: string;\n connected: boolean;\n unauthorized: boolean;\n connection_error: string;\n active_runs: number;\n allowed_roots: string[];\n token_from_env: boolean;\n providers: LocalProvider[];\n}\n\n/** Why a local call failed: the service is not running, the token is not accepted, or the service said no. */\nexport class LocalError extends Error {\n constructor(readonly kind: \"offline\" | \"unauthorized\" | \"rejected\", message: string, readonly code = \"\") {\n super(message);\n }\n}\n\nasync function call<T>(base: string, path: string, init: RequestInit & { token?: string } = {}): Promise<T> {\n const headers = new Headers(init.headers);\n if (init.token) headers.set(\"Authorization\", `Bearer ${init.token}`);\n if (init.body) headers.set(\"Content-Type\", \"application/json\");\n let response: Response;\n try {\n response = await fetch(`${base.replace(/\\/+$/, \"\")}${path}`, { ...init, headers, cache: \"no-store\" });\n } catch {\n throw new LocalError(\"offline\", \"local service unreachable\");\n }\n const body = (await response.json().catch(() => null)) as Record<string, unknown> | null;\n if (response.status === 401) throw new LocalError(\"unauthorized\", \"unauthorized\");\n if (!response.ok) {\n const code = typeof body?.error === \"string\" ? body.error : \"\";\n const message = typeof body?.message === \"string\" ? body.message : code || `HTTP ${response.status}`;\n throw new LocalError(\"rejected\", message, code);\n }\n return body as T;\n}\n\nexport const local = {\n info: (base: string) => call<LocalInfo>(base, \"/control/info\"),\n pair: (base: string, code: string) => call<{ token: string }>(base, \"/control/pair\", { method: \"POST\", body: JSON.stringify({ code }) }),\n status: (base: string, token: string) => call<LocalStatus>(base, \"/control/status\", { token }),\n enroll: (base: string, token: string, serverUrl: string, code: string) =>\n call<LocalStatus>(base, \"/control/enroll\", { method: \"POST\", token, body: JSON.stringify({ server_url: serverUrl, code }) }),\n unpair: (base: string, token: string) => call<LocalStatus>(base, \"/control/unpair\", { method: \"POST\", token }),\n detect: (base: string, token: string) => call<LocalStatus>(base, \"/control/detect\", { method: \"POST\", token }),\n};\n\n/** Two backend addresses name the same server (trailing slash and case insensitive). */\nexport function sameServer(left: string, right: string): boolean {\n const normalize = (value: string) => {\n try {\n return new URL(value).origin.toLowerCase();\n } catch {\n return value.trim().replace(/\\/+$/, \"\").toLowerCase();\n }\n };\n return Boolean(left && right) && normalize(left) === normalize(right);\n}\n", "import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { PluginContext } from \"@toc-starter/plugin-sdk\";\nimport { Badge, Btn, Card, CardTitle, Input, Toggle } from \"@toc-starter/plugin-sdk/ui\";\nimport { Command, errorText, type Device, type ServerContext, type WorkspaceContext } from \"./shared\";\n\n/**\n * \u5176\u4ED6\u7535\u8111: install the device agent on another machine (a Linux server,\n * another Mac, Windows). The page creates the device and a one-time pairing\n * code, shows one command to run there (install + pair + background\n * service), and polls until that machine comes online. Enabling / disabling\n * later is a server-side switch that keeps the pairing.\n */\n\ntype Platform = \"mac\" | \"linux\" | \"win\";\nconst PLATFORMS: readonly Platform[] = [\"linux\", \"mac\", \"win\"];\nconst IDLE_POLL_MS = 10_000;\nconst WAIT_POLL_MS = 3_000;\nconst REMOTE_SERVER_KEY = \"remoteServerUrl\";\n\ninterface Pending {\n deviceId: string;\n deviceName: string;\n platform: Platform;\n code: string;\n expiresAt: number;\n /** Credential id before pairing; a new one means the command ran. */\n baseline: string | null;\n matched: boolean;\n}\n\nconst LOOPBACK = new Set([\"127.0.0.1\", \"localhost\", \"[::1]\", \"::1\", \"0.0.0.0\"]);\n\n/** The server origin to put in the command, or null when it is not an http(s) URL. */\nfunction commandServer(value: string): string | null {\n try {\n const url = new URL(value.trim());\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? url.origin : null;\n } catch {\n return null;\n }\n}\n\nfunction isLoopback(origin: string): boolean {\n try {\n return LOOPBACK.has(new URL(origin).hostname);\n } catch {\n return false;\n }\n}\n\nexport function setupCommand(platform: Platform, code: string, server: string): string {\n const setup = `toc-ocra setup --code ${code} --server ${server}`;\n // PowerShell 5.1 has no `&&`; `if ($?)` runs setup only after a successful install.\n if (platform === \"win\") return `npm install -g toc-ocra@latest; if ($?) { ${setup} }`;\n return `npm install -g toc-ocra@latest && ${setup}`;\n}\n\nfunction DeviceBadge({ ctx, device }: { ctx: PluginContext; device: Device }) {\n if (device.status === \"online\") return <Badge variant=\"ok\">{ctx.t(\"remote.status.online\")}</Badge>;\n if (device.status === \"disabled\") return <Badge variant=\"warn\">{ctx.t(\"remote.status.disabled\")}</Badge>;\n if (device.status === \"offline\") return <Badge variant=\"err\">{ctx.t(\"remote.status.offline\")}</Badge>;\n return <Badge variant=\"muted\">{ctx.t(\"remote.status.notConnected\")}</Badge>;\n}\n\nexport function RemoteTab({ ctx, server, workspace, localDeviceId }: {\n ctx: PluginContext;\n server: ServerContext | undefined;\n workspace: WorkspaceContext | undefined;\n /** This computer's device, listed on the \u672C\u673A tab instead. */\n localDeviceId: string;\n}) {\n const [devices, setDevices] = useState<Device[] | null>(null);\n const [error, setError] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const [pending, setPending] = useState<Pending | null>(null);\n const [adding, setAdding] = useState(false);\n const [name, setName] = useState(\"\");\n const [platform, setPlatform] = useState<Platform>(\"linux\");\n const [serverUrl, setServerUrl] = useState(() => ctx.storage.get<string>(REMOTE_SERVER_KEY, \"\") || server?.origin || \"\");\n const pendingRef = useRef(pending);\n pendingRef.current = pending;\n const alive = useRef(true);\n\n useEffect(() => {\n if (!ctx.storage.get<string>(REMOTE_SERVER_KEY, \"\") && server?.origin) setServerUrl(server.origin);\n }, [ctx, server?.origin]);\n\n const load = useCallback(async () => {\n if (!workspace?.id) return;\n try {\n const result = await ctx.core.request<{ items?: Device[] }>(`/workspaces/${encodeURIComponent(workspace.id)}/devices`);\n if (!alive.current) return;\n const mine = (result.items ?? []).filter(device => device.is_mine && device.id !== localDeviceId);\n setDevices(mine);\n const waiting = pendingRef.current;\n if (waiting && !waiting.matched) {\n const device = mine.find(item => item.id === waiting.deviceId);\n if (device?.agent_id && device.agent_id !== waiting.baseline && device.terminal_online) {\n setPending({ ...waiting, matched: true });\n ctx.notify({ title: ctx.t(\"page.title\"), body: ctx.t(\"remote.matchedNotice\", { name: device.name }) });\n }\n }\n } catch (reason) {\n if (alive.current) setError(errorText(ctx, reason));\n }\n }, [ctx, localDeviceId, workspace?.id]);\n\n const waiting = Boolean(pending && !pending.matched);\n useEffect(() => {\n alive.current = true;\n void load();\n const timer = window.setInterval(() => void load(), waiting ? WAIT_POLL_MS : IDLE_POLL_MS);\n return () => { alive.current = false; window.clearInterval(timer); };\n }, [load, waiting]);\n\n const run = async (action: () => Promise<void>) => {\n setBusy(true);\n setError(\"\");\n try {\n await action();\n } catch (reason) {\n setError(errorText(ctx, reason));\n } finally {\n setBusy(false);\n }\n };\n\n const origin = commandServer(serverUrl);\n const saveServer = (value: string) => {\n setServerUrl(value);\n if (value.trim() && value.trim() !== server?.origin) ctx.storage.set(REMOTE_SERVER_KEY, value.trim());\n else ctx.storage.remove(REMOTE_SERVER_KEY);\n };\n\n const pair = (device: Pick<Device, \"id\" | \"name\" | \"platform\" | \"agent_id\">) => run(async () => {\n if (!origin) throw new Error(ctx.t(\"remote.serverInvalid\"));\n const result = await ctx.core.request<{ code: string; expires_at: number }>(`/devices/${encodeURIComponent(device.id)}/pairing-code`, { method: \"POST\" });\n setPending({\n deviceId: device.id,\n deviceName: device.name,\n platform: PLATFORMS.includes(device.platform) ? device.platform : \"linux\",\n code: result.code,\n expiresAt: result.expires_at,\n baseline: device.agent_id ?? null,\n matched: false,\n });\n });\n\n const addComputer = () => run(async () => {\n if (!workspace?.id) throw new Error(ctx.t(\"bind.noWorkspace\"));\n if (!origin) throw new Error(ctx.t(\"remote.serverInvalid\"));\n const deviceName = name.trim() || ctx.t(`remote.platform.${platform}`);\n const created = await ctx.core.request<Device>(`/workspaces/${encodeURIComponent(workspace.id)}/devices`, {\n method: \"POST\",\n body: JSON.stringify({ name: deviceName, location: platform === \"linux\" ? \"cloud\" : \"local\", platform, note: \"\", share_to_workspace: false }),\n });\n const result = await ctx.core.request<{ code: string; expires_at: number }>(`/devices/${encodeURIComponent(created.id)}/pairing-code`, { method: \"POST\" });\n setPending({ deviceId: created.id, deviceName: created.name || deviceName, platform, code: result.code, expiresAt: result.expires_at, baseline: null, matched: false });\n setAdding(false);\n setName(\"\");\n await load();\n });\n\n const setEnabled = (device: Device, enabled: boolean) => run(async () => {\n await ctx.core.request(`/devices/${encodeURIComponent(device.id)}/enabled`, { method: \"PUT\", body: JSON.stringify({ enabled }) });\n await load();\n });\n\n return (\n <>\n {error && <div role=\"alert\" className=\"da-alert\">{error}</div>}\n <Card>\n <CardTitle>{ctx.t(\"remote.title\")}</CardTitle>\n <p className=\"da-text\">{ctx.t(\"remote.body\")}</p>\n <label className=\"da-field\">\n <span>{ctx.t(\"remote.server\")}</span>\n <Input value={serverUrl} onChange={event => saveServer(event.target.value)} spellCheck={false} className=\"da-mono\" />\n </label>\n {!origin && <p className=\"da-muted da-warn\">{ctx.t(\"remote.serverInvalid\")}</p>}\n {origin && isLoopback(origin) && <p className=\"da-muted da-warn\">{ctx.t(\"remote.loopback\")}</p>}\n {!adding && (\n <div className=\"da-row\">\n <Btn primary disabled={busy || !workspace?.id} onClick={() => setAdding(true)}>{ctx.t(\"remote.add\")}</Btn>\n </div>\n )}\n {adding && (\n <form className=\"da-form\" onSubmit={event => { event.preventDefault(); void addComputer(); }}>\n <label className=\"da-field\">\n <span>{ctx.t(\"remote.platformLabel\")}</span>\n <select className=\"da-select\" value={platform} onChange={event => setPlatform(event.target.value as Platform)}>\n {PLATFORMS.map(item => <option key={item} value={item}>{ctx.t(`remote.platform.${item}`)}</option>)}\n </select>\n </label>\n <label className=\"da-field\">\n <span>{ctx.t(\"remote.name\")}</span>\n <Input value={name} onChange={event => setName(event.target.value)} maxLength={100} placeholder={ctx.t(\"remote.namePlaceholder\")} />\n </label>\n <div className=\"da-row\">\n <Btn primary type=\"submit\" disabled={busy || !origin}>{ctx.t(busy ? \"remote.working\" : \"remote.generate\")}</Btn>\n <Btn type=\"button\" disabled={busy} onClick={() => setAdding(false)}>{ctx.t(\"remote.cancel\")}</Btn>\n </div>\n </form>\n )}\n </Card>\n\n {pending && origin && (\n <PendingCard ctx={ctx} pending={pending} origin={origin} busy={busy}\n onPlatform={value => setPending({ ...pending, platform: value })}\n onRegenerate={() => void pair({ id: pending.deviceId, name: pending.deviceName, platform: pending.platform, agent_id: pending.baseline })}\n onClose={() => setPending(null)} />\n )}\n\n <Card>\n <CardTitle>{ctx.t(\"remote.listTitle\")}</CardTitle>\n {devices === null && <p className=\"da-muted\">{ctx.t(\"common.checking\")}</p>}\n {devices?.length === 0 && <p className=\"da-muted\">{ctx.t(\"remote.empty\")}</p>}\n {devices && devices.length > 0 && (\n <ul className=\"da-devices\">\n {devices.map(device => (\n <li key={device.id}>\n <div className=\"da-device-main\">\n <span className=\"da-device-name\">{device.name}</span>\n <span className=\"da-muted\">{ctx.t(`remote.platform.${PLATFORMS.includes(device.platform) ? device.platform : \"linux\"}`)}{device.agent_version ? ` \u00B7 toc-ocra ${device.agent_version}` : \"\"}</span>\n </div>\n <DeviceBadge ctx={ctx} device={device} />\n <div className=\"da-device-actions\">\n {(!device.paired || device.status === \"offline\" || device.status === \"not_connected\") && (\n <Btn disabled={busy || !origin} onClick={() => void pair(device)}>{ctx.t(device.paired ? \"remote.repair\" : \"remote.install\")}</Btn>\n )}\n {device.paired && (\n <label className=\"da-toggle\">\n <span>{ctx.t(\"remote.enabled\")}</span>\n <Toggle checked={!device.disabled} disabled={busy} label={ctx.t(\"remote.enabled\")} onChange={value => void setEnabled(device, value)} />\n </label>\n )}\n </div>\n </li>\n ))}\n </ul>\n )}\n <p className=\"da-muted\">{ctx.t(\"remote.disableHint\")}</p>\n <div className=\"da-step\">{ctx.t(\"remote.uninstall\")}</div>\n <Command ctx={ctx} value=\"toc-ocra service:uninstall\" />\n </Card>\n </>\n );\n}\n\nfunction PendingCard({ ctx, pending, origin, busy, onPlatform, onRegenerate, onClose }: {\n ctx: PluginContext;\n pending: Pending;\n origin: string;\n busy: boolean;\n onPlatform: (platform: Platform) => void;\n onRegenerate: () => void;\n onClose: () => void;\n}) {\n const [now, setNow] = useState(() => Math.floor(Date.now() / 1000));\n useEffect(() => {\n if (pending.matched) return;\n const timer = window.setInterval(() => setNow(Math.floor(Date.now() / 1000)), 1_000);\n return () => window.clearInterval(timer);\n }, [pending.matched]);\n const left = Math.max(0, pending.expiresAt - now);\n const expired = !pending.matched && left === 0;\n\n return (\n <Card>\n <CardTitle>{ctx.t(\"remote.pendingTitle\", { name: pending.deviceName })}</CardTitle>\n {pending.matched ? (\n <>\n <p className=\"da-text\"><Badge variant=\"ok\">{ctx.t(\"remote.matched\")}</Badge> {ctx.t(\"remote.matchedBody\", { name: pending.deviceName })}</p>\n <div className=\"da-row\"><Btn primary onClick={onClose}>{ctx.t(\"remote.done\")}</Btn></div>\n </>\n ) : (\n <>\n <p className=\"da-text\">{ctx.t(\"remote.pendingBody\")}</p>\n <div className=\"da-segment\" role=\"radiogroup\" aria-label={ctx.t(\"remote.platformLabel\")}>\n {PLATFORMS.map(item => (\n <button key={item} type=\"button\" role=\"radio\" aria-checked={pending.platform === item} className={pending.platform === item ? \"da-seg da-seg-active\" : \"da-seg\"} onClick={() => onPlatform(item)}>\n {ctx.t(`remote.platform.${item}`)}\n </button>\n ))}\n </div>\n <Command ctx={ctx} value={setupCommand(pending.platform, pending.code, origin)} />\n <ul className=\"da-hints\">\n <li>{ctx.t(\"remote.hintNode\")}</li>\n {pending.platform === \"linux\" && <li>{ctx.t(\"remote.hintLinux\")}</li>}\n {pending.platform === \"win\" && <li>{ctx.t(\"remote.hintWindows\")}</li>}\n <li>{expired ? ctx.t(\"remote.expired\") : ctx.t(\"remote.expires\", { minutes: Math.floor(left / 60), seconds: String(left % 60).padStart(2, \"0\") })}</li>\n </ul>\n <p className=\"da-muted\">{expired ? \"\" : ctx.t(\"remote.waiting\")}</p>\n <div className=\"da-row\">\n <Btn disabled={busy} onClick={onRegenerate}>{ctx.t(\"remote.regenerate\")}</Btn>\n <Btn disabled={busy} onClick={onClose}>{ctx.t(\"remote.cancel\")}</Btn>\n </div>\n </>\n )}\n </Card>\n );\n}\n", "import { useState } from \"react\";\nimport type { PluginContext } from \"@toc-starter/plugin-sdk\";\nimport { Btn } from \"@toc-starter/plugin-sdk/ui\";\nimport { LocalError } from \"./local\";\n\nexport const TOKEN_KEY = \"controlToken\";\nexport const LOCAL_URL_KEY = \"localUrl\";\n/** \"off\" after the user unbinds by hand; binding by hand turns it back on. */\nexport const AUTO_BIND_KEY = \"autoBind\";\n/** \"on\" while the user wants the desktop app to run the local agent. */\nexport const LOCAL_SERVICE_KEY = \"localService\";\n\nexport interface ServerContext { kind: \"cloud\" | \"lan\"; name: string; origin: string }\nexport interface WorkspaceContext { id: string; name: string }\n\n/** The fields of echo_admin's device view the plugin uses. */\nexport interface Device {\n id: string;\n name: string;\n platform: \"mac\" | \"win\" | \"linux\";\n location?: string;\n paired: boolean;\n status: \"not_connected\" | \"online\" | \"offline\" | \"disabled\";\n is_mine: boolean;\n /** Terminal agent credential id; only the owner sees it. Changes on every pairing. */\n agent_id?: string | null;\n terminal_online?: boolean;\n disabled?: boolean;\n agent_version?: string;\n last_seen_at?: number | null;\n}\n\nexport function errorText(ctx: PluginContext, error: unknown): string {\n if (error instanceof LocalError && error.kind === \"offline\") return ctx.t(\"error.offline\");\n return error instanceof Error ? error.message : String(error);\n}\n\nexport function Command({ ctx, value }: { ctx: PluginContext; value: string }) {\n const [copied, setCopied] = useState(false);\n return (\n <div className=\"da-command\">\n <code>{value}</code>\n <Btn onClick={() => void navigator.clipboard.writeText(value).then(() => { setCopied(true); window.setTimeout(() => setCopied(false), 1500); })}>\n {copied ? ctx.t(\"common.copied\") : ctx.t(\"common.copy\")}\n </Btn>\n </div>\n );\n}\n"],
5
- "mappings": "0jBAAA,IAAAA,EAAAC,GAAA,CAAAC,GAAAC,KAAA,KAAIC,GAAW,WAAW,sBACtBC,GAASD,IAAYA,GAAS,MAClC,GAAI,CAACC,GAAQ,MAAM,IAAI,MAAM,yEAAyE,EACtGF,GAAO,QAAUE,KCHjB,IAAAC,EAAAC,GAAA,CAAAC,GAAAC,KAAA,KAAIC,GAAW,WAAW,sBACtBC,GAASD,IAAYA,GAAS,4BAA4B,EAC9D,GAAI,CAACC,GAAQ,MAAM,IAAI,MAAM,8FAA8F,EAC3HF,GAAO,QAAUE,KCHjB,IAAAC,EAAAC,GAAA,CAAAC,GAAAC,KAAA,KAAIC,GAAW,WAAW,sBACtBC,GAASD,IAAYA,GAAS,mBAAmB,EACrD,GAAI,CAACC,GAAQ,MAAM,IAAI,MAAM,qFAAqF,EAClHF,GAAO,QAAUE,KCHjB,IAAAC,EAAyD,SCKzD,IAAAC,GAAqC,SAY9B,SAASC,GAAaC,EAAQ,CACnC,GAAI,CAACA,GAAU,OAAOA,EAAO,UAAa,WACxC,MAAM,IAAI,UAAU,mEAAmE,EAEzF,OAAOA,CACT,CAUO,SAASC,GAAeC,EAAKC,EAAK,CACvC,SAAO,yBACLC,GAAYF,EAAI,KAAK,gBAAgBC,EAAKC,CAAQ,EAClD,IAAMF,EAAI,KAAK,QAAQC,CAAG,CAC5B,CACF,CDnCA,IAAAE,EAAuE,SEGhE,IAAMC,GAAoB,yBAqCpBC,EAAN,cAAyB,KAAM,CACpC,YAAqBC,EAA+CC,EAA0BC,EAAO,GAAI,CACvG,MAAMD,CAAO,EADM,UAAAD,EAAyE,UAAAE,CAE9F,CAFqB,KAAyE,IAGhG,EAEA,eAAeC,EAAQC,EAAcC,EAAcC,EAAyC,CAAC,EAAe,CAC1G,IAAMC,EAAU,IAAI,QAAQD,EAAK,OAAO,EACpCA,EAAK,OAAOC,EAAQ,IAAI,gBAAiB,UAAUD,EAAK,KAAK,EAAE,EAC/DA,EAAK,MAAMC,EAAQ,IAAI,eAAgB,kBAAkB,EAC7D,IAAIC,EACJ,GAAI,CACFA,EAAW,MAAM,MAAM,GAAGJ,EAAK,QAAQ,OAAQ,EAAE,CAAC,GAAGC,CAAI,GAAI,CAAE,GAAGC,EAAM,QAAAC,EAAS,MAAO,UAAW,CAAC,CACtG,MAAQ,CACN,MAAM,IAAIR,EAAW,UAAW,2BAA2B,CAC7D,CACA,IAAMU,EAAQ,MAAMD,EAAS,KAAK,EAAE,MAAM,IAAM,IAAI,EACpD,GAAIA,EAAS,SAAW,IAAK,MAAM,IAAIT,EAAW,eAAgB,cAAc,EAChF,GAAI,CAACS,EAAS,GAAI,CAChB,IAAMN,EAAO,OAAOO,GAAM,OAAU,SAAWA,EAAK,MAAQ,GACtDR,EAAU,OAAOQ,GAAM,SAAY,SAAWA,EAAK,QAAUP,GAAQ,QAAQM,EAAS,MAAM,GAClG,MAAM,IAAIT,EAAW,WAAYE,EAASC,CAAI,CAChD,CACA,OAAOO,CACT,CAEO,IAAMC,EAAQ,CACnB,KAAON,GAAiBD,EAAgBC,EAAM,eAAe,EAC7D,KAAM,CAACA,EAAcF,IAAiBC,EAAwBC,EAAM,gBAAiB,CAAE,OAAQ,OAAQ,KAAM,KAAK,UAAU,CAAE,KAAAF,CAAK,CAAC,CAAE,CAAC,EACvI,OAAQ,CAACE,EAAcO,IAAkBR,EAAkBC,EAAM,kBAAmB,CAAE,MAAAO,CAAM,CAAC,EAC7F,OAAQ,CAACP,EAAcO,EAAeC,EAAmBV,IACvDC,EAAkBC,EAAM,kBAAmB,CAAE,OAAQ,OAAQ,MAAAO,EAAO,KAAM,KAAK,UAAU,CAAE,WAAYC,EAAW,KAAAV,CAAK,CAAC,CAAE,CAAC,EAC7H,OAAQ,CAACE,EAAcO,IAAkBR,EAAkBC,EAAM,kBAAmB,CAAE,OAAQ,OAAQ,MAAAO,CAAM,CAAC,EAC7G,OAAQ,CAACP,EAAcO,IAAkBR,EAAkBC,EAAM,kBAAmB,CAAE,OAAQ,OAAQ,MAAAO,CAAM,CAAC,CAC/G,EAGO,SAASE,EAAWC,EAAcC,EAAwB,CAC/D,IAAMC,EAAaC,GAAkB,CACnC,GAAI,CACF,OAAO,IAAI,IAAIA,CAAK,EAAE,OAAO,YAAY,CAC3C,MAAQ,CACN,OAAOA,EAAM,KAAK,EAAE,QAAQ,OAAQ,EAAE,EAAE,YAAY,CACtD,CACF,EACA,MAAO,GAAQH,GAAQC,IAAUC,EAAUF,CAAI,IAAME,EAAUD,CAAK,CACtE,CCxFA,IAAAG,EAAyD,SAEzDC,EAA2D,SCF3D,IAAAC,GAAyB,SAEzBC,GAAoB,SAsChB,IAAAC,EAAA,SAnCSC,EAAY,eACZC,GAAgB,WAEhBC,EAAgB,WAEhBC,EAAoB,eAsB1B,SAASC,EAAUC,EAAoBC,EAAwB,CACpE,OAAIA,aAAiBC,GAAcD,EAAM,OAAS,UAAkBD,EAAI,EAAE,eAAe,EAClFC,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,CAC9D,CAEO,SAASE,EAAQ,CAAE,IAAAH,EAAK,MAAAI,CAAM,EAA0C,CAC7E,GAAM,CAACC,EAAQC,CAAS,KAAI,aAAS,EAAK,EAC1C,SACE,QAAC,OAAI,UAAU,aACb,oBAAC,QAAM,SAAAF,EAAM,KACb,OAAC,QAAI,QAAS,IAAG,CAAQ,UAAU,UAAU,UAAUA,CAAK,EAAE,KAAK,IAAM,CAAEE,EAAU,EAAI,EAAG,OAAO,WAAW,IAAMA,EAAU,EAAK,EAAG,IAAI,CAAG,CAAC,GAC3I,SAAAD,EAASL,EAAI,EAAE,eAAe,EAAIA,EAAI,EAAE,aAAa,EACxD,GACF,CAEJ,CDWyC,IAAAO,EAAA,SA5CnCC,EAAiC,CAAC,QAAS,MAAO,KAAK,EACvDC,GAAe,IACfC,GAAe,IACfC,EAAoB,kBAapBC,GAAW,IAAI,IAAI,CAAC,YAAa,YAAa,QAAS,MAAO,SAAS,CAAC,EAG9E,SAASC,GAAcC,EAA8B,CACnD,GAAI,CACF,IAAMC,EAAM,IAAI,IAAID,EAAM,KAAK,CAAC,EAChC,OAAOC,EAAI,WAAa,SAAWA,EAAI,WAAa,SAAWA,EAAI,OAAS,IAC9E,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAASC,GAAWC,EAAyB,CAC3C,GAAI,CACF,OAAOL,GAAS,IAAI,IAAI,IAAIK,CAAM,EAAE,QAAQ,CAC9C,MAAQ,CACN,MAAO,EACT,CACF,CAEO,SAASC,GAAaC,EAAoBC,EAAcC,EAAwB,CACrF,IAAMC,EAAQ,yBAAyBF,CAAI,aAAaC,CAAM,GAE9D,OAAIF,IAAa,MAAc,6CAA6CG,CAAK,KAC1E,qCAAqCA,CAAK,EACnD,CAEA,SAASC,GAAY,CAAE,IAAAC,EAAK,OAAAC,CAAO,EAA2C,CAC5E,OAAIA,EAAO,SAAW,YAAiB,OAAC,SAAM,QAAQ,KAAM,SAAAD,EAAI,EAAE,sBAAsB,EAAE,EACtFC,EAAO,SAAW,cAAmB,OAAC,SAAM,QAAQ,OAAQ,SAAAD,EAAI,EAAE,wBAAwB,EAAE,EAC5FC,EAAO,SAAW,aAAkB,OAAC,SAAM,QAAQ,MAAO,SAAAD,EAAI,EAAE,uBAAuB,EAAE,KACtF,OAAC,SAAM,QAAQ,QAAS,SAAAA,EAAI,EAAE,4BAA4B,EAAE,CACrE,CAEO,SAASE,GAAU,CAAE,IAAAF,EAAK,OAAAH,EAAQ,UAAAM,EAAW,cAAAC,CAAc,EAM/D,CACD,GAAM,CAACC,EAASC,CAAU,KAAI,YAA0B,IAAI,EACtD,CAACC,EAAOC,CAAQ,KAAI,YAAS,EAAE,EAC/B,CAACC,EAAMC,CAAO,KAAI,YAAS,EAAK,EAChC,CAACC,EAASC,CAAU,KAAI,YAAyB,IAAI,EACrD,CAACC,EAAQC,CAAS,KAAI,YAAS,EAAK,EACpC,CAACC,EAAMC,CAAO,KAAI,YAAS,EAAE,EAC7B,CAACrB,EAAUsB,CAAW,KAAI,YAAmB,OAAO,EACpD,CAACC,EAAWC,CAAY,KAAI,YAAS,IAAMnB,EAAI,QAAQ,IAAYb,EAAmB,EAAE,GAAKU,GAAQ,QAAU,EAAE,EACjHuB,KAAa,UAAOT,CAAO,EACjCS,EAAW,QAAUT,EACrB,IAAMU,KAAQ,UAAO,EAAI,KAEzB,aAAU,IAAM,CACV,CAACrB,EAAI,QAAQ,IAAYb,EAAmB,EAAE,GAAKU,GAAQ,QAAQsB,EAAatB,EAAO,MAAM,CACnG,EAAG,CAACG,EAAKH,GAAQ,MAAM,CAAC,EAExB,IAAMyB,KAAO,eAAY,SAAY,CACnC,GAAKnB,GAAW,GAChB,GAAI,CACF,IAAMoB,EAAS,MAAMvB,EAAI,KAAK,QAA8B,eAAe,mBAAmBG,EAAU,EAAE,CAAC,UAAU,EACrH,GAAI,CAACkB,EAAM,QAAS,OACpB,IAAMG,GAAQD,EAAO,OAAS,CAAC,GAAG,OAAOtB,GAAUA,EAAO,SAAWA,EAAO,KAAOG,CAAa,EAChGE,EAAWkB,CAAI,EACf,IAAMC,EAAUL,EAAW,QAC3B,GAAIK,GAAW,CAACA,EAAQ,QAAS,CAC/B,IAAMxB,EAASuB,EAAK,KAAKE,IAAQA,GAAK,KAAOD,EAAQ,QAAQ,EACzDxB,GAAQ,UAAYA,EAAO,WAAawB,EAAQ,UAAYxB,EAAO,kBACrEW,EAAW,CAAE,GAAGa,EAAS,QAAS,EAAK,CAAC,EACxCzB,EAAI,OAAO,CAAE,MAAOA,EAAI,EAAE,YAAY,EAAG,KAAMA,EAAI,EAAE,uBAAwB,CAAE,KAAMC,EAAO,IAAK,CAAC,CAAE,CAAC,EAEzG,CACF,OAAS0B,EAAQ,CACXN,EAAM,SAASb,EAASoB,EAAU5B,EAAK2B,CAAM,CAAC,CACpD,CACF,EAAG,CAAC3B,EAAKI,EAAeD,GAAW,EAAE,CAAC,EAEhCsB,EAAU,GAAQd,GAAW,CAACA,EAAQ,YAC5C,aAAU,IAAM,CACdU,EAAM,QAAU,GACXC,EAAK,EACV,IAAMO,EAAQ,OAAO,YAAY,IAAG,CAAQP,EAAK,GAAGG,EAAUvC,GAAeD,EAAY,EACzF,MAAO,IAAM,CAAEoC,EAAM,QAAU,GAAO,OAAO,cAAcQ,CAAK,CAAG,CACrE,EAAG,CAACP,EAAMG,CAAO,CAAC,EAElB,IAAMK,EAAM,MAAOC,GAAgC,CACjDrB,EAAQ,EAAI,EACZF,EAAS,EAAE,EACX,GAAI,CACF,MAAMuB,EAAO,CACf,OAASJ,EAAQ,CACfnB,EAASoB,EAAU5B,EAAK2B,CAAM,CAAC,CACjC,QAAE,CACAjB,EAAQ,EAAK,CACf,CACF,EAEMjB,EAASJ,GAAc6B,CAAS,EAChCc,EAAc1C,GAAkB,CACpC6B,EAAa7B,CAAK,EACdA,EAAM,KAAK,GAAKA,EAAM,KAAK,IAAMO,GAAQ,OAAQG,EAAI,QAAQ,IAAIb,EAAmBG,EAAM,KAAK,CAAC,EAC/FU,EAAI,QAAQ,OAAOb,CAAiB,CAC3C,EAEM8C,GAAQhC,GAAkE6B,EAAI,SAAY,CAC9F,GAAI,CAACrC,EAAQ,MAAM,IAAI,MAAMO,EAAI,EAAE,sBAAsB,CAAC,EAC1D,IAAMuB,EAAS,MAAMvB,EAAI,KAAK,QAA8C,YAAY,mBAAmBC,EAAO,EAAE,CAAC,gBAAiB,CAAE,OAAQ,MAAO,CAAC,EACxJW,EAAW,CACT,SAAUX,EAAO,GACjB,WAAYA,EAAO,KACnB,SAAUjB,EAAU,SAASiB,EAAO,QAAQ,EAAIA,EAAO,SAAW,QAClE,KAAMsB,EAAO,KACb,UAAWA,EAAO,WAClB,SAAUtB,EAAO,UAAY,KAC7B,QAAS,EACX,CAAC,CACH,CAAC,EAEKiC,GAAc,IAAMJ,EAAI,SAAY,CACxC,GAAI,CAAC3B,GAAW,GAAI,MAAM,IAAI,MAAMH,EAAI,EAAE,kBAAkB,CAAC,EAC7D,GAAI,CAACP,EAAQ,MAAM,IAAI,MAAMO,EAAI,EAAE,sBAAsB,CAAC,EAC1D,IAAMmC,EAAapB,EAAK,KAAK,GAAKf,EAAI,EAAE,mBAAmBL,CAAQ,EAAE,EAC/DyC,EAAU,MAAMpC,EAAI,KAAK,QAAgB,eAAe,mBAAmBG,EAAU,EAAE,CAAC,WAAY,CACxG,OAAQ,OACR,KAAM,KAAK,UAAU,CAAE,KAAMgC,EAAY,SAAUxC,IAAa,QAAU,QAAU,QAAS,SAAAA,EAAU,KAAM,GAAI,mBAAoB,EAAM,CAAC,CAC9I,CAAC,EACK4B,EAAS,MAAMvB,EAAI,KAAK,QAA8C,YAAY,mBAAmBoC,EAAQ,EAAE,CAAC,gBAAiB,CAAE,OAAQ,MAAO,CAAC,EACzJxB,EAAW,CAAE,SAAUwB,EAAQ,GAAI,WAAYA,EAAQ,MAAQD,EAAY,SAAAxC,EAAU,KAAM4B,EAAO,KAAM,UAAWA,EAAO,WAAY,SAAU,KAAM,QAAS,EAAM,CAAC,EACtKT,EAAU,EAAK,EACfE,EAAQ,EAAE,EACV,MAAMM,EAAK,CACb,CAAC,EAEKe,GAAa,CAACpC,EAAgBqC,IAAqBR,EAAI,SAAY,CACvE,MAAM9B,EAAI,KAAK,QAAQ,YAAY,mBAAmBC,EAAO,EAAE,CAAC,WAAY,CAAE,OAAQ,MAAO,KAAM,KAAK,UAAU,CAAE,QAAAqC,CAAQ,CAAC,CAAE,CAAC,EAChI,MAAMhB,EAAK,CACb,CAAC,EAED,SACE,oBACG,UAAAf,MAAS,OAAC,OAAI,KAAK,QAAQ,UAAU,WAAY,SAAAA,EAAM,KACxD,QAAC,QACC,oBAAC,aAAW,SAAAP,EAAI,EAAE,cAAc,EAAE,KAClC,OAAC,KAAE,UAAU,UAAW,SAAAA,EAAI,EAAE,aAAa,EAAE,KAC7C,QAAC,SAAM,UAAU,WACf,oBAAC,QAAM,SAAAA,EAAI,EAAE,eAAe,EAAE,KAC9B,OAAC,SAAM,MAAOkB,EAAW,SAAUqB,GAASP,EAAWO,EAAM,OAAO,KAAK,EAAG,WAAY,GAAO,UAAU,UAAU,GACrH,EACC,CAAC9C,MAAU,OAAC,KAAE,UAAU,mBAAoB,SAAAO,EAAI,EAAE,sBAAsB,EAAE,EAC1EP,GAAUD,GAAWC,CAAM,MAAK,OAAC,KAAE,UAAU,mBAAoB,SAAAO,EAAI,EAAE,iBAAiB,EAAE,EAC1F,CAACa,MACA,OAAC,OAAI,UAAU,SACb,mBAAC,OAAI,QAAO,GAAC,SAAUJ,GAAQ,CAACN,GAAW,GAAI,QAAS,IAAMW,EAAU,EAAI,EAAI,SAAAd,EAAI,EAAE,YAAY,EAAE,EACtG,EAEDa,MACC,QAAC,QAAK,UAAU,UAAU,SAAU0B,GAAS,CAAEA,EAAM,eAAe,EAAQL,GAAY,CAAG,EACzF,qBAAC,SAAM,UAAU,WACf,oBAAC,QAAM,SAAAlC,EAAI,EAAE,sBAAsB,EAAE,KACrC,OAAC,UAAO,UAAU,YAAY,MAAOL,EAAU,SAAU4C,GAAStB,EAAYsB,EAAM,OAAO,KAAiB,EACzG,SAAAvD,EAAU,IAAI0C,MAAQ,OAAC,UAAkB,MAAOA,EAAO,SAAA1B,EAAI,EAAE,mBAAmB0B,CAAI,EAAE,GAAnDA,CAAqD,CAAS,EACpG,GACF,KACA,QAAC,SAAM,UAAU,WACf,oBAAC,QAAM,SAAA1B,EAAI,EAAE,aAAa,EAAE,KAC5B,OAAC,SAAM,MAAOe,EAAM,SAAUwB,GAASvB,EAAQuB,EAAM,OAAO,KAAK,EAAG,UAAW,IAAK,YAAavC,EAAI,EAAE,wBAAwB,EAAG,GACpI,KACA,QAAC,OAAI,UAAU,SACb,oBAAC,OAAI,QAAO,GAAC,KAAK,SAAS,SAAUS,GAAQ,CAAChB,EAAS,SAAAO,EAAI,EAAES,EAAO,iBAAmB,iBAAiB,EAAE,KAC1G,OAAC,OAAI,KAAK,SAAS,SAAUA,EAAM,QAAS,IAAMK,EAAU,EAAK,EAAI,SAAAd,EAAI,EAAE,eAAe,EAAE,GAC9F,GACF,GAEJ,EAECW,GAAWlB,MACV,OAAC+C,GAAA,CAAY,IAAKxC,EAAK,QAASW,EAAS,OAAQlB,EAAQ,KAAMgB,EAC7D,WAAYnB,GAASsB,EAAW,CAAE,GAAGD,EAAS,SAAUrB,CAAM,CAAC,EAC/D,aAAc,IAAG,CAAQ2C,GAAK,CAAE,GAAItB,EAAQ,SAAU,KAAMA,EAAQ,WAAY,SAAUA,EAAQ,SAAU,SAAUA,EAAQ,QAAS,CAAC,GACxI,QAAS,IAAMC,EAAW,IAAI,EAAG,KAGrC,QAAC,QACC,oBAAC,aAAW,SAAAZ,EAAI,EAAE,kBAAkB,EAAE,EACrCK,IAAY,SAAQ,OAAC,KAAE,UAAU,WAAY,SAAAL,EAAI,EAAE,iBAAiB,EAAE,EACtEK,GAAS,SAAW,MAAK,OAAC,KAAE,UAAU,WAAY,SAAAL,EAAI,EAAE,cAAc,EAAE,EACxEK,GAAWA,EAAQ,OAAS,MAC3B,OAAC,MAAG,UAAU,aACX,SAAAA,EAAQ,IAAIJ,MACX,QAAC,MACC,qBAAC,OAAI,UAAU,iBACb,oBAAC,QAAK,UAAU,iBAAkB,SAAAA,EAAO,KAAK,KAC9C,QAAC,QAAK,UAAU,WAAY,UAAAD,EAAI,EAAE,mBAAmBhB,EAAU,SAASiB,EAAO,QAAQ,EAAIA,EAAO,SAAW,OAAO,EAAE,EAAGA,EAAO,cAAgB,kBAAeA,EAAO,aAAa,GAAK,IAAG,GAC7L,KACA,OAACF,GAAA,CAAY,IAAKC,EAAK,OAAQC,EAAQ,KACvC,QAAC,OAAI,UAAU,oBACX,YAACA,EAAO,QAAUA,EAAO,SAAW,WAAaA,EAAO,SAAW,qBACnE,OAAC,OAAI,SAAUQ,GAAQ,CAAChB,EAAQ,QAAS,IAAG,CAAQwC,GAAKhC,CAAM,GAAI,SAAAD,EAAI,EAAEC,EAAO,OAAS,gBAAkB,gBAAgB,EAAE,EAE9HA,EAAO,WACN,QAAC,SAAM,UAAU,YACf,oBAAC,QAAM,SAAAD,EAAI,EAAE,gBAAgB,EAAE,KAC/B,OAAC,UAAO,QAAS,CAACC,EAAO,SAAU,SAAUQ,EAAM,MAAOT,EAAI,EAAE,gBAAgB,EAAG,SAAUV,GAAM,CAAQ+C,GAAWpC,EAAQX,CAAK,GAAG,GACxI,GAEJ,IAhBOW,EAAO,EAiBhB,CACD,EACH,KAEF,OAAC,KAAE,UAAU,WAAY,SAAAD,EAAI,EAAE,oBAAoB,EAAE,KACrD,OAAC,OAAI,UAAU,UAAW,SAAAA,EAAI,EAAE,kBAAkB,EAAE,KACpD,OAACyC,EAAA,CAAQ,IAAKzC,EAAK,MAAM,6BAA6B,GACxD,GACF,CAEJ,CAEA,SAASwC,GAAY,CAAE,IAAAxC,EAAK,QAAAW,EAAS,OAAAlB,EAAQ,KAAAgB,EAAM,WAAAiC,EAAY,aAAAC,EAAc,QAAAC,CAAQ,EAQlF,CACD,GAAM,CAACC,EAAKC,CAAM,KAAI,YAAS,IAAM,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,CAAC,KAClE,aAAU,IAAM,CACd,GAAInC,EAAQ,QAAS,OACrB,IAAMkB,EAAQ,OAAO,YAAY,IAAMiB,EAAO,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,CAAC,EAAG,GAAK,EACnF,MAAO,IAAM,OAAO,cAAcjB,CAAK,CACzC,EAAG,CAAClB,EAAQ,OAAO,CAAC,EACpB,IAAMoC,EAAO,KAAK,IAAI,EAAGpC,EAAQ,UAAYkC,CAAG,EAC1CG,EAAU,CAACrC,EAAQ,SAAWoC,IAAS,EAE7C,SACE,QAAC,QACC,oBAAC,aAAW,SAAA/C,EAAI,EAAE,sBAAuB,CAAE,KAAMW,EAAQ,UAAW,CAAC,EAAE,EACtEA,EAAQ,WACP,oBACE,qBAAC,KAAE,UAAU,UAAU,oBAAC,SAAM,QAAQ,KAAM,SAAAX,EAAI,EAAE,gBAAgB,EAAE,EAAQ,IAAEA,EAAI,EAAE,qBAAsB,CAAE,KAAMW,EAAQ,UAAW,CAAC,GAAE,KACxI,OAAC,OAAI,UAAU,SAAS,mBAAC,OAAI,QAAO,GAAC,QAASiC,EAAU,SAAA5C,EAAI,EAAE,aAAa,EAAE,EAAM,GACrF,KAEA,oBACE,oBAAC,KAAE,UAAU,UAAW,SAAAA,EAAI,EAAE,oBAAoB,EAAE,KACpD,OAAC,OAAI,UAAU,aAAa,KAAK,aAAa,aAAYA,EAAI,EAAE,sBAAsB,EACnF,SAAAhB,EAAU,IAAI0C,MACb,OAAC,UAAkB,KAAK,SAAS,KAAK,QAAQ,eAAcf,EAAQ,WAAae,EAAM,UAAWf,EAAQ,WAAae,EAAO,uBAAyB,SAAU,QAAS,IAAMgB,EAAWhB,CAAI,EAC5L,SAAA1B,EAAI,EAAE,mBAAmB0B,CAAI,EAAE,GADrBA,CAEb,CACD,EACH,KACA,OAACe,EAAA,CAAQ,IAAKzC,EAAK,MAAON,GAAaiB,EAAQ,SAAUA,EAAQ,KAAMlB,CAAM,EAAG,KAChF,QAAC,MAAG,UAAU,WACZ,oBAAC,MAAI,SAAAO,EAAI,EAAE,iBAAiB,EAAE,EAC7BW,EAAQ,WAAa,YAAW,OAAC,MAAI,SAAAX,EAAI,EAAE,kBAAkB,EAAE,EAC/DW,EAAQ,WAAa,UAAS,OAAC,MAAI,SAAAX,EAAI,EAAE,oBAAoB,EAAE,KAChE,OAAC,MAAI,SAAAgD,EAAUhD,EAAI,EAAE,gBAAgB,EAAIA,EAAI,EAAE,iBAAkB,CAAE,QAAS,KAAK,MAAM+C,EAAO,EAAE,EAAG,QAAS,OAAOA,EAAO,EAAE,EAAE,SAAS,EAAG,GAAG,CAAE,CAAC,EAAE,GACpJ,KACA,OAAC,KAAE,UAAU,WAAY,SAAAC,EAAU,GAAKhD,EAAI,EAAE,gBAAgB,EAAE,KAChE,QAAC,OAAI,UAAU,SACb,oBAAC,OAAI,SAAUS,EAAM,QAASkC,EAAe,SAAA3C,EAAI,EAAE,mBAAmB,EAAE,KACxE,OAAC,OAAI,SAAUS,EAAM,QAASmC,EAAU,SAAA5C,EAAI,EAAE,eAAe,EAAE,GACjE,GACF,GAEJ,CAEJ,CHhRS,IAAAiD,EAAA,SApBHC,GAAU,IACVC,GAAe,IACfC,GAAqB,IAErBC,GAAmB,EACnBC,GAAU,MAGVC,GAAU,IAAI,IAAI,wBAAyB,YAAY,GAAG,EAAE,KAWlE,SAASC,IAAO,CACd,SAAO,OAAC,OAAI,IAAKD,GAAS,IAAI,GAAG,MAAO,GAAI,OAAQ,GAAI,UAAW,GAAO,CAC5E,CAGA,SAASE,GAAaC,EAAiD,CACrE,IAAMC,EAAWD,EAAI,KAAuC,QAC5D,GAAI,CACF,OAAOC,GAAWA,EAAQ,UAAU,EAAIA,EAAU,MACpD,MAAQ,CACN,MACF,CACF,CAEA,IAAMC,EAAYC,GAAkDA,GAAQ,QAAU,WAAaA,GAAQ,QAAU,WAGjHC,GAEJ,SAASC,GAAU,CAAE,IAAAL,CAAI,EAA2B,CAClD,IAAMM,EAASC,GAA8BP,EAAK,QAAQ,EACpDQ,EAAYD,GAAiCP,EAAK,WAAW,EAC7D,CAACS,EAAKC,CAAW,KAAI,YAAc,IAAOV,EAAI,QAAQ,IAAYJ,GAAS,OAAO,IAAM,SAAW,SAAW,OAAQ,EACtHe,EAAUC,GAAc,CAC5BZ,EAAI,QAAQ,IAAIJ,GAASgB,CAAI,EAC7BF,EAAYE,CAAI,CAClB,EACM,CAACC,CAAQ,KAAI,YAAS,IAAMb,EAAI,QAAQ,IAAIc,GAAeC,EAAiB,CAAC,EAC7E,CAACC,EAAOC,CAAQ,KAAI,YAAS,IAAMjB,EAAI,cAAc,IAAIkB,EAAW,EAAE,CAAC,EACvE,CAACC,EAAOC,CAAQ,KAAI,YAAgB,CAAE,KAAM,UAAW,CAAC,EACxD,CAACnB,EAASoB,CAAU,KAAI,YAAoC,IAAI,EAChE,CAACC,EAAOC,CAAQ,KAAI,YAAS,EAAE,EAC/B,CAACC,EAAMC,CAAO,KAAI,YAAS,EAAK,EAChCC,KAAQ,UAAO,EAAI,EACnBC,EAAM5B,GAAaC,CAAG,EAEtB4B,KAAU,eAAY,SAAY,CACtC,IAAIC,EAAoC,KACxC,GAAIF,EACF,GAAI,CACFE,EAAS,MAAMF,EAAI,OAAO,CAC5B,MAAQ,CACNE,EAAS,IACX,CAEF,GAAI,CAACH,EAAM,QAAS,OACpBL,EAAWQ,CAAM,EAEjB,IAAIC,EAASd,EACTd,EAAS2B,CAAM,GAAKA,GAAQ,QAC9BC,EAASD,EAAO,MACZA,EAAO,QAAUb,IACnBhB,EAAI,cAAc,IAAIkB,EAAWW,EAAO,KAAK,EAC7CZ,EAASY,EAAO,KAAK,IAGzB,GAAI,CACF,MAAME,EAAM,KAAKlB,CAAQ,CAC3B,MAAQ,CACFa,EAAM,SAASN,EAAS,CAAE,KAAMlB,EAAS2B,CAAM,EAAI,WAAa,SAAU,CAAC,EAC/E,MACF,CACA,GAAI,CAACC,EAAQ,CACPJ,EAAM,SAASN,EAAS,CAAE,KAAM,WAAY,CAAC,EACjD,MACF,CACA,GAAI,CACF,IAAMjB,EAAS,MAAM4B,EAAM,OAAOlB,EAAUiB,CAAM,EAC9CJ,EAAM,SAASN,EAAS,CAAE,KAAM,QAAS,OAAAjB,CAAO,CAAC,CACvD,OAAS6B,EAAQ,CACf,GAAI,CAACN,EAAM,QAAS,OAChBM,aAAkBC,GAAcD,EAAO,OAAS,gBAElDhC,EAAI,cAAc,OAAOkB,CAAS,EAClCD,EAAS,EAAE,EACXG,EAAS,CAAE,KAAM,WAAY,CAAC,GAE9BA,EAAS,CAAE,KAAM,SAAU,CAAC,CAEhC,CACF,EAAG,CAACpB,EAAKa,EAAUc,EAAKX,CAAK,CAAC,KAE9B,aAAU,IAAM,CACdU,EAAM,QAAU,GACXE,EAAQ,EACb,IAAMM,EAAQ,OAAO,YAAY,IAAG,CAAQN,EAAQ,GAAGpC,EAAO,EAC9D,MAAO,IAAM,CAAEkC,EAAM,QAAU,GAAO,OAAO,cAAcQ,CAAK,CAAG,CACrE,EAAG,CAACN,CAAO,CAAC,KAEZ,aAAU,IAAM,CACd,GAAIT,EAAM,OAAS,QAAS,CAC1B,IAAMgB,EAAIhB,EAAM,OAChBnB,EAAI,aAAamC,EAAE,QAAUA,EAAE,UAAY,CAAE,OAAQ,UAAW,QAASnC,EAAI,EAAE,gBAAiB,CAAE,KAAMmC,EAAE,WAAY,CAAC,CAAE,EAAI,CAAE,OAAQ,WAAY,QAASnC,EAAI,EAAEmC,EAAE,OAAS,sBAAwB,iBAAiB,CAAE,CAAC,CAC3N,MAAWhB,EAAM,OAAS,WACxBnB,EAAI,aAAa,CAAE,OAAQ,WAAY,QAASA,EAAI,EAAE2B,EAAM,kBAAoB,gBAAgB,CAAE,CAAC,CAEvG,EAAG,CAAC3B,EAAKmB,EAAOQ,CAAG,CAAC,EAEpB,IAAMS,EAAM,MAAOC,GAAgC,CACjDZ,EAAQ,EAAI,EACZF,EAAS,EAAE,EACX,GAAI,CACF,MAAMc,EAAO,CACf,OAASL,EAAQ,CACfT,EAASe,EAAUtC,EAAKgC,CAAM,CAAC,CACjC,QAAE,CACAP,EAAQ,EAAK,CACf,CACF,EAEMc,EAAiBC,GAAgBJ,EAAI,SAAY,CACrD,GAAI,CAACT,EAAK,OACV,GAAI,CAACa,EAAI,CACPxC,EAAI,QAAQ,IAAIyC,EAAmB,KAAK,EACxCpB,EAAW,MAAMM,EAAI,KAAK,CAAC,EAC3B,MAAMC,EAAQ,EACd,MACF,CACA5B,EAAI,QAAQ,IAAIyC,EAAmB,IAAI,EAEvCzC,EAAI,QAAQ,OAAO0C,CAAa,EAChC,IAAMC,EAAU,MAAMhB,EAAI,MAAM,EAChCN,EAAWsB,CAAO,EACdA,EAAQ,QACV3C,EAAI,cAAc,IAAIkB,EAAWyB,EAAQ,KAAK,EAC9C1B,EAAS0B,EAAQ,KAAK,GAEpB,MAAMC,GAAoB/B,EAAU,IAAMa,EAAM,OAAO,GAAGtB,KAAkB,EAChF,MAAMwB,EAAQ,CAChB,CAAC,EAEKiB,EAAa5C,GAAS,QAAU,SAAWA,EAAU,KACrD6C,EAAW,EAAQnB,GAAQ,CAACzB,EAASD,CAAO,IAAMkB,EAAM,OAAS,SAAWA,EAAM,OAAS,aAEjG,SACE,QAAC,OAAI,UAAU,SACb,oBAAC,cAAW,MAAOnB,EAAI,EAAE,YAAY,EAAG,SAAUA,EAAI,EAAE,eAAe,EAAG,KAC1E,OAAC,OAAI,UAAU,UAAU,KAAK,UAAU,aAAYA,EAAI,EAAE,YAAY,EAClE,UAAC,QAAS,QAAQ,EAAY,IAAI+C,MAClC,OAAC,UAAkB,KAAK,SAAS,KAAK,MAAM,gBAAetC,IAAQsC,EAAM,UAAWtC,IAAQsC,EAAO,uBAAyB,SAAU,QAAS,IAAMpC,EAAOoC,CAAI,EAC7J,SAAA/C,EAAI,EAAE,QAAQ+C,CAAI,EAAE,GADVA,CAEb,CACD,EACH,EACCzB,MAAS,OAAC,OAAI,KAAK,QAAQ,UAAU,WAAY,SAAAA,EAAM,EAEvDb,IAAQ,aACP,OAACuC,GAAA,CAAU,IAAKhD,EAAK,OAAQM,EAAQ,UAAWE,EAAW,cAAeW,EAAM,OAAS,SAAWA,EAAM,OAAO,OAASA,EAAM,OAAO,UAAY,GAAI,EAGxJV,IAAQ,YACP,oBACG,UAAAkB,MACC,OAACsB,GAAA,CACC,IAAKjD,EACL,OAAQC,EACR,SAAU6C,EACV,KAAMD,EACN,KAAMrB,EACN,SAAUgB,GAAG,CAAQD,EAAcC,CAAE,GACvC,EAEDrB,EAAM,OAAS,eAAc,OAAC,QAAK,mBAAC,QAAK,UAAU,WAAY,SAAAnB,EAAI,EAAE,iBAAiB,EAAE,EAAO,EAC/FmB,EAAM,OAAS,eAAc,OAAC,QAAK,mBAAC,QAAK,UAAU,WAAY,SAAAnB,EAAI,EAAE,kBAAkB,EAAE,EAAO,EAChGmB,EAAM,OAAS,WAAa,CAACQ,MAAO,OAACuB,GAAA,CAAY,IAAKlD,EAAK,SAAUa,EAAU,EAC/EM,EAAM,OAAS,gBACd,OAACgC,GAAA,CAAS,IAAKnD,EAAK,KAAMwB,EAAM,OAAQ4B,GAAQhB,EAAI,SAAY,CAC9D,IAAMiB,EAAS,MAAMtB,EAAM,KAAKlB,EAAUuC,CAAI,EAAE,MAAOpB,GAAoB,CACzE,MAAIA,aAAkBC,GAAcD,EAAO,OAAS,aAAoB,IAAI,MAAMhC,EAAI,EAAE,YAAY,CAAC,EACjGgC,aAAkBC,GAAcD,EAAO,OAAS,SAAgB,IAAI,MAAMhC,EAAI,EAAE,aAAa,CAAC,EAC5FgC,CACR,CAAC,EACDhC,EAAI,cAAc,IAAIkB,EAAWmC,EAAO,KAAK,EAC7CpC,EAASoC,EAAO,KAAK,CACvB,CAAC,EAAG,EAELlC,EAAM,OAAS,YACd,OAACmC,GAAA,CACC,IAAKtD,EACL,OAAQmB,EAAM,OACd,OAAQb,EACR,UAAWE,EACX,KAAMgB,EACN,IAAKY,EACL,SAAUvB,EACV,MAAOG,EACP,SAAUb,GAAUiB,EAAS,CAAE,KAAM,QAAS,OAAAjB,CAAO,CAAC,EACxD,GAEJ,GAEJ,CAEJ,CAGA,eAAeyC,GAAoB/B,EAAkBa,EAAwC,CAC3F,QAAS6B,EAAU,EAAGA,EAAU,IAAM7B,EAAM,EAAG6B,GAAW,EACxD,GAAI,CACF,aAAMxB,EAAM,KAAKlB,CAAQ,EAClB,EACT,MAAQ,CACN,MAAM,IAAI,QAAQ2C,GAAW,OAAO,WAAWA,EAAS,GAAG,CAAC,CAC9D,CAEF,MAAO,EACT,CAEA,SAASP,GAAiB,CAAE,IAAAjD,EAAK,OAAAG,EAAQ,SAAA2C,EAAU,KAAAW,EAAM,KAAAjC,EAAM,SAAAkC,CAAS,EAOrE,CACD,IAAMlB,EAAKtC,EAASC,CAAM,EACpBwD,EAAYF,GAAM,WAAa9D,GACrC,SACE,QAAC,QACC,qBAAC,OAAI,UAAU,YACb,qBAAC,OACC,oBAAC,aAAW,SAAAK,EAAI,EAAE,eAAe,EAAE,KACnC,OAAC,KAAE,UAAU,UAAW,SAAAA,EAAI,EAAEwC,EAAK,iBAAmB,iBAAiB,EAAE,GAC3E,KACA,OAAC,UAAO,QAASA,EAAI,SAAUhB,EAAM,MAAOxB,EAAI,EAAE,eAAe,EAAG,SAAU0D,EAAU,GAC1F,EACClB,MAAM,OAAC,KAAE,UAAU,WAAY,SAAAxC,EAAI,EAAE,oBAAoB,EAAE,EAC3D,CAACwC,IAAOM,GAAYa,OAAc,OAAC,KAAE,UAAU,WAAY,SAAA3D,EAAI,EAAE,kBAAkB,EAAE,EACrFyD,GAAQ,CAACE,GAAaF,EAAK,WAAa,MACvC,OAAC,OAAI,KAAK,QAAQ,UAAU,WAAY,SAAAzD,EAAI,EAAE,kBAAmB,CAAE,MAAOyD,EAAK,OAAS,QAAQA,EAAK,UAAYA,EAAK,QAAU,GAAG,EAAG,CAAC,EAAE,GAE7I,CAEJ,CAEA,SAASP,GAAY,CAAE,IAAAlD,EAAK,SAAAa,CAAS,EAA6C,CAChF,SACE,QAAC,QACC,oBAAC,aAAW,SAAAb,EAAI,EAAE,eAAe,EAAE,KACnC,OAAC,KAAE,UAAU,UAAW,SAAAA,EAAI,EAAE,eAAgB,CAAE,IAAKa,CAAS,CAAC,EAAE,KACjE,OAAC,OAAI,UAAU,UAAW,SAAAb,EAAI,EAAE,cAAc,EAAE,KAChD,OAAC4D,EAAA,CAAQ,IAAK5D,EAAK,MAAM,0BAA0B,KACnD,OAAC,OAAI,UAAU,UAAW,SAAAA,EAAI,EAAE,iBAAiB,EAAE,KACnD,OAAC4D,EAAA,CAAQ,IAAK5D,EAAK,MAAM,6DAA6D,KACtF,OAAC,KAAE,UAAU,WAAY,SAAAA,EAAI,EAAE,cAAc,EAAE,GACjD,CAEJ,CAEA,SAASmD,GAAS,CAAE,IAAAnD,EAAK,KAAAwB,EAAM,OAAAqC,CAAO,EAA0E,CAC9G,GAAM,CAACT,EAAMU,CAAO,KAAI,YAAS,EAAE,EAC7BC,EAAQ,UAAU,KAAKX,CAAI,EACjC,SACE,QAAC,QACC,oBAAC,aAAW,SAAApD,EAAI,EAAE,YAAY,EAAE,KAChC,OAAC,KAAE,UAAU,UAAW,SAAAA,EAAI,EAAE,WAAW,EAAE,KAC3C,OAAC4D,EAAA,CAAQ,IAAK5D,EAAK,MAAM,gBAAgB,KACzC,QAAC,QAAK,UAAU,SAAS,SAAUgE,GAAS,CAAEA,EAAM,eAAe,EAAOD,GAAOF,EAAOT,CAAI,CAAG,EAC7F,oBAAC,SACC,MAAOA,EACP,SAAUY,GAASF,EAAQE,EAAM,OAAO,MAAM,QAAQ,MAAO,EAAE,EAAE,MAAM,EAAG,CAAC,CAAC,EAC5E,YAAahE,EAAI,EAAE,kBAAkB,EACrC,UAAU,UACV,aAAa,gBACb,aAAYA,EAAI,EAAE,kBAAkB,EACpC,UAAU,gBACZ,KACA,OAAC,OAAI,QAAO,GAAC,KAAK,SAAS,SAAU,CAAC+D,GAASvC,EAAO,SAAAxB,EAAI,EAAE,aAAa,EAAE,GAC7E,GACF,CAEJ,CAEA,SAASsD,GAAU,CAAE,IAAAtD,EAAK,OAAAG,EAAQ,OAAAG,EAAQ,UAAAE,EAAW,KAAAgB,EAAM,IAAAY,EAAK,SAAAvB,EAAU,MAAAG,EAAO,SAAAiD,CAAS,EAUvF,CACD,IAAMC,EAAiB/D,EAAO,QAAUG,GAAU,CAAC6D,EAAWhE,EAAO,WAAYG,EAAO,MAAM,EACxF8D,EAAS,IAAMhC,EAAI,SAAY,CAE/BjC,EAAO,WAAaG,GAAU6D,EAAWhE,EAAO,WAAYG,EAAO,MAAM,GAC3E,MAAMN,EAAI,KAAK,QAAQ,YAAY,mBAAmBG,EAAO,SAAS,CAAC,SAAU,CAAE,OAAQ,QAAS,CAAC,EAAE,MAAM,IAAG,EAAY,EAE9HH,EAAI,QAAQ,IAAI0C,EAAe,KAAK,EACpCuB,EAAS,MAAMlC,EAAM,OAAOlB,EAAUG,CAAK,CAAC,CAC9C,CAAC,EACD,SACE,oBACE,qBAAC,QACC,oBAAC,aAAW,SAAAhB,EAAI,EAAE,cAAc,EAAE,KAClC,QAAC,MAAG,UAAU,UACZ,qBAAC,OAAI,oBAAC,MAAI,SAAAA,EAAI,EAAE,gBAAgB,EAAE,KAAK,QAAC,MAAG,sBAAUG,EAAO,QAAQ,SAAIA,EAAO,UAAS,GAAK,KAC7F,QAAC,OAAI,oBAAC,MAAI,SAAAH,EAAI,EAAE,gBAAgB,EAAE,KAAK,OAAC,MAAG,mBAACqE,GAAA,CAAa,IAAKrE,EAAK,OAAQG,EAAQ,EAAE,GAAK,EACzFA,EAAO,WAAU,QAAC,OAAI,oBAAC,MAAI,SAAAH,EAAI,EAAE,eAAe,EAAE,KAAK,OAAC,MAAI,SAAAG,EAAO,YAAY,GAAK,EACpFA,EAAO,WAAU,QAAC,OAAI,oBAAC,MAAI,SAAAH,EAAI,EAAE,eAAe,EAAE,KAAK,OAAC,MAAG,UAAU,UAAW,SAAAG,EAAO,WAAW,GAAK,EACvGA,EAAO,WAAU,QAAC,OAAI,oBAAC,MAAI,SAAAH,EAAI,EAAE,aAAa,EAAE,KAAK,OAAC,MAAI,SAAAG,EAAO,YAAY,GAAK,GACrF,EACCA,EAAO,QAAU,CAACA,EAAO,WAAaA,EAAO,qBAAoB,OAAC,KAAE,UAAU,WAAY,SAAAH,EAAI,EAAE,mBAAoB,CAAE,MAAOG,EAAO,gBAAiB,CAAC,EAAE,EACxJA,EAAO,mBAAkB,OAAC,KAAE,UAAU,WAAY,SAAAH,EAAI,EAAE,qBAAqB,EAAE,EAC/EG,EAAO,WACN,OAAC,OAAI,UAAU,SACb,mBAAC,OAAI,OAAM,GAAC,SAAUqB,GAAQrB,EAAO,eAAgB,QAAS,IAAG,CAAQiE,EAAO,GAAI,SAAApE,EAAI,EAAE,eAAe,EAAE,EAC7G,GAEJ,GAEE,CAACG,EAAO,QAAUA,EAAO,cAAgB+D,OACzC,OAACI,GAAA,CAAS,IAAKtE,EAAK,OAAQG,EAAQ,OAAQG,EAAQ,UAAWE,EAAW,KAAMgB,EAAM,IAAKY,EAAK,SAAUvB,EAAU,MAAOG,EAAO,SAAUiD,EAAU,OAAQ9D,EAAO,OAAQ,KAG/K,QAAC,QACC,oBAAC,aAAW,SAAAH,EAAI,EAAE,WAAW,EAAE,KAC/B,QAAC,MAAG,UAAU,UACX,UAAAG,EAAO,UAAU,OAAOoE,GAAYA,EAAS,SAAS,EAAE,IAAIA,MAC3D,QAAC,MACC,oBAAC,QAAK,UAAU,UAAW,SAAAA,EAAS,GAAG,KACvC,OAAC,QAAK,UAAU,WAAY,SAAAA,EAAS,QAAQ,EAC5CA,EAAS,YAAW,OAAC,SAAM,QAAQ,KAAM,SAAAvE,EAAI,EAAE,cAAc,EAAE,KAAW,OAAC,SAAM,QAAQ,QAAS,SAAAA,EAAI,EAAE,gBAAgB,EAAE,IAHpHuE,EAAS,EAIlB,CACD,EACA,CAACpE,EAAO,UAAU,KAAKoE,GAAYA,EAAS,SAAS,MAAK,OAAC,MAAG,UAAU,WAAY,SAAAvE,EAAI,EAAE,UAAU,EAAE,GACzG,KACA,OAAC,OAAI,UAAU,SACb,mBAAC,OAAI,SAAUwB,EAAM,QAAS,IAAG,CAAQY,EAAI,SAAY6B,EAAS,MAAMlC,EAAM,OAAOlB,EAAUG,CAAK,CAAC,CAAC,GAAI,SAAAhB,EAAI,EAAE,cAAc,EAAE,EAClI,GACF,GACF,CAEJ,CAEA,SAASqE,GAAa,CAAE,IAAArE,EAAK,OAAAG,CAAO,EAAgD,CAClF,OAAKA,EAAO,OACRA,EAAO,gBAAqB,OAAC,SAAM,QAAQ,MAAO,SAAAH,EAAI,EAAE,iBAAiB,EAAE,EAC3EG,EAAO,aAAkB,OAAC,SAAM,QAAQ,KAAM,SAAAH,EAAI,EAAE,gBAAgB,EAAE,KACnE,OAAC,SAAM,QAAQ,OAAQ,SAAAA,EAAI,EAAE,oBAAoB,EAAE,KAH/B,OAAC,SAAM,QAAQ,QAAS,SAAAA,EAAI,EAAE,kBAAkB,EAAE,CAI/E,CAEA,SAASsE,GAAS,CAAE,IAAAtE,EAAK,OAAAG,EAAQ,OAAAG,EAAQ,UAAAE,EAAW,KAAAgB,EAAM,IAAAY,EAAK,SAAAvB,EAAU,MAAAG,EAAO,SAAAiD,EAAU,OAAAO,CAAO,EAW9F,CACD,GAAM,CAACC,EAASC,CAAU,KAAI,YAAmB,CAAC,CAAC,EAC7C,CAACC,EAAQC,CAAS,KAAI,YAAS,KAAK,EACpC,CAACC,EAAMC,CAAO,KAAI,YAAS3E,EAAO,QAAQ,EAC1C,CAAC4E,EAAWC,CAAY,KAAI,YAAS1E,GAAQ,QAAU,EAAE,KAC/D,aAAU,IAAM,CAAMA,GAAQ,QAAQ0E,EAAa1E,EAAO,MAAM,CAAG,EAAG,CAACA,GAAQ,MAAM,CAAC,KAEtF,aAAU,IAAM,CACd,GAAI,CAACE,GAAW,GAAI,OACpB,IAAIyE,EAAY,GAChB,OAAAjF,EAAI,KAAK,QAA8B,eAAe,mBAAmBQ,EAAU,EAAE,CAAC,UAAU,EAC7F,KAAK6C,GAAU,CAAO4B,GAAWP,GAAYrB,EAAO,OAAS,CAAC,GAAG,OAAO6B,GAAUA,EAAO,OAAO,CAAC,CAAG,CAAC,EACrG,MAAM,IAAM,CAAOD,GAAWP,EAAW,CAAC,CAAC,CAAG,CAAC,EAC3C,IAAM,CAAEO,EAAY,EAAM,CACnC,EAAG,CAACjF,EAAKQ,GAAW,EAAE,CAAC,EAEvB,IAAM2E,EAAO,IAAM/C,EAAI,SAAY,CACjC,GAAI,CAAC5B,GAAW,GAAI,MAAM,IAAI,MAAMR,EAAI,EAAE,kBAAkB,CAAC,EAC7D,GAAI,CAAC+E,EAAU,KAAK,EAAG,MAAM,IAAI,MAAM/E,EAAI,EAAE,eAAe,CAAC,EAC7D,IAAIoF,EAAWT,EACXA,IAAW,QAKbS,GAJgB,MAAMpF,EAAI,KAAK,QAAwB,eAAe,mBAAmBQ,EAAU,EAAE,CAAC,WAAY,CAChH,OAAQ,OACR,KAAM,KAAK,UAAU,CAAE,KAAMqE,EAAK,KAAK,GAAK1E,EAAO,SAAU,SAAU,QAAS,SAAUA,EAAO,SAAU,KAAM,GAAI,mBAAoB,EAAM,CAAC,CAClJ,CAAC,GACkB,GACnByE,EAAUQ,CAAQ,GAEpB,IAAMC,EAAU,MAAMrF,EAAI,KAAK,QAA0B,YAAY,mBAAmBoF,CAAQ,CAAC,gBAAiB,CAAE,OAAQ,MAAO,CAAC,EACpInB,EAAS,MAAMlC,EAAM,OAAOlB,EAAUG,EAAO+D,EAAU,KAAK,EAAGM,EAAQ,IAAI,CAAC,EAC5ErF,EAAI,QAAQ,OAAO0C,CAAa,EAChC1C,EAAI,OAAO,CAAE,MAAOA,EAAI,EAAE,YAAY,EAAG,KAAMA,EAAI,EAAE,YAAa,CAAE,OAAQM,GAAQ,MAAQyE,CAAU,CAAC,CAAE,CAAC,CAC5G,CAAC,EAED,SACE,QAAC,QACC,oBAAC,aAAW,SAAA/E,EAAI,EAAEwE,EAAS,mBAAqB,YAAY,EAAE,EAC7DA,GAAUrE,EAAO,iBAAgB,OAAC,KAAE,UAAU,UAAW,SAAAH,EAAI,EAAE,cAAc,EAAE,EAC/EwE,GAAU,CAACrE,EAAO,iBAAgB,OAAC,KAAE,UAAU,UAAW,SAAAH,EAAI,EAAE,iBAAkB,CAAE,MAAOG,EAAO,WAAY,QAASG,GAAQ,QAAU,EAAG,CAAC,EAAE,KAChJ,OAAC,KAAE,UAAU,UAAW,SAAAN,EAAI,EAAE,YAAa,CAAE,OAAQM,GAAQ,MAAQ,GAAI,UAAWE,GAAW,MAAQ,EAAG,CAAC,EAAE,KAC7G,QAAC,SAAM,UAAU,WACf,oBAAC,QAAM,SAAAR,EAAI,EAAE,aAAa,EAAE,KAC5B,OAAC,SAAM,MAAO+E,EAAW,SAAUf,GAASgB,EAAahB,EAAM,OAAO,KAAK,EAAG,WAAY,GAAO,UAAU,UAAU,GACvH,KACA,QAAC,SAAM,UAAU,WACf,oBAAC,QAAM,SAAAhE,EAAI,EAAE,aAAa,EAAE,KAC5B,QAAC,UAAO,UAAU,YAAY,MAAO2E,EAAQ,SAAUX,GAASY,EAAUZ,EAAM,OAAO,KAAK,EAC1F,oBAAC,UAAO,MAAM,MAAO,SAAAhE,EAAI,EAAE,gBAAgB,EAAE,EAC5CyE,EAAQ,IAAIS,MACX,QAAC,UAAuB,MAAOA,EAAO,GAAK,UAAAA,EAAO,KAAMA,EAAO,OAAS,SAAMlF,EAAI,EAAE,eAAe,CAAC,GAAK,KAA5FkF,EAAO,EAAwF,CAC7G,GACH,GACF,EACCP,IAAW,UACV,QAAC,SAAM,UAAU,WACf,oBAAC,QAAM,SAAA3E,EAAI,EAAE,WAAW,EAAE,KAC1B,OAAC,SAAM,MAAO6E,EAAM,SAAUb,GAASc,EAAQd,EAAM,OAAO,KAAK,EAAG,UAAW,IAAK,GACtF,KAEF,OAAC,OAAI,UAAU,SACb,mBAAC,OAAI,QAAO,GAAC,SAAUxC,GAAQ,CAAChB,GAAW,GAAI,QAAS,IAAG,CAAQ2E,EAAK,GAAI,SAAAnF,EAAI,EAAEwB,EAAO,eAAiB,aAAa,EAAE,EAC3H,GACF,CAEJ,CASA,SAAS8D,GAActF,EAAoB,CACzC,IAAIwB,EAAO,GACP+D,EAAc,GACZC,EAAW,IAAI,IAEfC,EAAO,SAAY,CACvB,GAAIjE,GAAQxB,EAAI,OAAO,QAAS,OAChC,IAAMM,EAASN,EAAI,KAAK,QAAuB,QAAQ,EACjDgB,EAAQhB,EAAI,cAAc,IAAIkB,EAAW,EAAE,EACjD,GAAI,GAACZ,GAAQ,QAAU,CAACU,GAAShB,EAAI,QAAQ,IAAY0C,EAAe,IAAI,IAAM,QAC9E,OAAK,IAAI,GAAK8C,EAAS,IAAIlF,EAAO,MAAM,GAAK,GAAKZ,IACtD,CAAA8B,EAAO,GACP,GAAI,CACF,IAAMX,EAAWb,EAAI,QAAQ,IAAIc,GAAeC,EAAiB,EAC3DZ,EAAS,MAAM4B,EAAM,OAAOlB,EAAUG,CAAK,EACjD,GAAI,CAACb,EAAO,YAAcA,EAAO,eAAgB,OACjD,GAAIA,EAAO,SAAWA,EAAO,cAAgBgE,EAAWhE,EAAO,WAAYG,EAAO,MAAM,GAAI,CAC1FiF,EAAc,GACd,MACF,CACA,GAAIpF,EAAO,YAAc,EAAG,CACtBoF,IAAgBjF,EAAO,SACzBiF,EAAcjF,EAAO,OACrBN,EAAI,OAAO,CAAE,MAAOA,EAAI,EAAE,YAAY,EAAG,KAAMA,EAAI,EAAE,gBAAiB,CAAE,OAAQM,EAAO,MAAQA,EAAO,MAAO,CAAC,CAAE,CAAC,GAEnH,MACF,CACA,IAAM+E,EAAU,MAAMrF,EAAI,KAAK,QAA+C,gCAAiC,CAC7G,OAAQ,OACR,KAAM,KAAK,UAAU,CACnB,WAAYG,EAAO,WACnB,SAAUA,EAAO,SACjB,SAAUA,EAAO,SACjB,kBAAmBA,EAAO,OAASA,EAAO,UAAY,EACxD,CAAC,CACH,CAAC,EACD,MAAM4B,EAAM,OAAOlB,EAAUG,EAAOV,EAAO,OAAQ+E,EAAQ,IAAI,EAC/DE,EAAc,GACdC,EAAS,OAAOlF,EAAO,MAAM,EAC7BN,EAAI,OAAO,CAAE,MAAOA,EAAI,EAAE,YAAY,EAAG,KAAMA,EAAI,EAAE,YAAa,CAAE,OAAQM,EAAO,MAAQA,EAAO,OAAQ,OAAQ+E,EAAQ,WAAY,CAAC,CAAE,CAAC,CAC5I,OAASrD,EAAQ,EAGX,EAAEA,aAAkBC,IAAeD,EAAO,OAAS,aAAYwD,EAAS,IAAIlF,EAAO,OAAQ,KAAK,IAAI,CAAC,CAC3G,QAAE,CACAkB,EAAO,EACT,EACF,EAEMU,EAAQ,OAAO,YAAY,IAAG,CAAQuD,EAAK,GAAGhG,EAAY,EAChEW,GAAkB,IAAG,CAAQqF,EAAK,GAClCzF,EAAI,iBAAiB,IAAM,CACzB,OAAO,cAAckC,CAAK,EAC1B9B,GAAkB,MACpB,CAAC,EACDJ,EAAI,iBAAiBA,EAAI,KAAK,gBAAgB,SAAU,IAAG,CAAQyF,EAAK,EAAC,CAAC,EAC1E,OAAO,WAAW,IAAG,CAAQA,EAAK,GAAG,IAAK,CAC5C,CAMA,SAASC,GAA2B1F,EAAoB,CACtD,IAAM2B,EAAM5B,GAAaC,CAAG,EACxB,CAAC2B,GAAO3B,EAAI,QAAQ,IAAYyC,EAAmB,KAAK,IAAM,MAC7Dd,EAAI,MAAM,EACZ,KAAKxB,GAAU,CACVA,EAAO,OAAOH,EAAI,cAAc,IAAIkB,EAAWf,EAAO,KAAK,EAC/D,OAAO,WAAW,IAAMC,KAAkB,EAAG,GAAK,CACpD,CAAC,EACA,MAAM,IAAG,EAAY,CAC1B,CAEA,IAAOuF,GAAQC,GAAa,CAC1B,SAAS5F,EAAK,CACZA,EAAI,mBAAmB,eAAa,OAACF,GAAA,EAAK,CAAE,EAC5CE,EAAI,cAAc,gBAAc,OAACK,GAAA,CAAU,IAAKL,EAAK,CAAE,EACvD0F,GAA2B1F,CAAG,EAC9BsF,GAActF,CAAG,CACnB,CACF,CAAC",
6
- "names": ["require_react", "__commonJSMin", "exports", "module", "registry", "shared", "require_ui", "__commonJSMin", "exports", "module", "registry", "shared", "require_jsx_runtime", "__commonJSMin", "exports", "module", "registry", "shared", "import_react", "import_react", "definePlugin", "plugin", "useHostContext", "ctx", "key", "listener", "import_ui", "DEFAULT_LOCAL_URL", "LocalError", "kind", "message", "code", "call", "base", "path", "init", "headers", "response", "body", "local", "token", "serverUrl", "sameServer", "left", "right", "normalize", "value", "import_react", "import_ui", "import_react", "import_ui", "import_jsx_runtime", "TOKEN_KEY", "LOCAL_URL_KEY", "AUTO_BIND_KEY", "LOCAL_SERVICE_KEY", "errorText", "ctx", "error", "LocalError", "Command", "value", "copied", "setCopied", "import_jsx_runtime", "PLATFORMS", "IDLE_POLL_MS", "WAIT_POLL_MS", "REMOTE_SERVER_KEY", "LOOPBACK", "commandServer", "value", "url", "isLoopback", "origin", "setupCommand", "platform", "code", "server", "setup", "DeviceBadge", "ctx", "device", "RemoteTab", "workspace", "localDeviceId", "devices", "setDevices", "error", "setError", "busy", "setBusy", "pending", "setPending", "adding", "setAdding", "name", "setName", "setPlatform", "serverUrl", "setServerUrl", "pendingRef", "alive", "load", "result", "mine", "waiting", "item", "reason", "errorText", "timer", "run", "action", "saveServer", "pair", "addComputer", "deviceName", "created", "setEnabled", "enabled", "event", "PendingCard", "Command", "onPlatform", "onRegenerate", "onClose", "now", "setNow", "left", "expired", "import_jsx_runtime", "POLL_MS", "AUTO_BIND_MS", "AUTO_BIND_RETRY_MS", "EXIT_PORT_IN_USE", "TAB_KEY", "iconUrl", "Icon", "localService", "ctx", "service", "isActive", "status", "triggerAutoBind", "AgentPage", "server", "useHostContext", "workspace", "tab", "setTabState", "setTab", "next", "localUrl", "LOCAL_URL_KEY", "DEFAULT_LOCAL_URL", "token", "setToken", "TOKEN_KEY", "phase", "setPhase", "setService", "error", "setError", "busy", "setBusy", "alive", "svc", "refresh", "hosted", "active", "local", "reason", "LocalError", "timer", "s", "run", "action", "errorText", "toggleService", "on", "LOCAL_SERVICE_KEY", "AUTO_BIND_KEY", "started", "waitForLocalService", "hostedExit", "external", "item", "RemoteTab", "LocalServiceCard", "OfflineCard", "PairCard", "code", "result", "ReadyView", "attempt", "resolve", "exit", "onToggle", "portTaken", "Command", "onPair", "setCode", "valid", "event", "onStatus", "boundElsewhere", "sameServer", "unpair", "BindingBadge", "BindCard", "provider", "rebind", "devices", "setDevices", "choice", "setChoice", "name", "setName", "serverUrl", "setServerUrl", "cancelled", "device", "bind", "deviceId", "pairing", "startAutoBind", "deferredFor", "failedAt", "tick", "startLocalServiceIfEnabled", "index_default", "definePlugin"]
4
+ "sourcesContent": ["var registry = globalThis[\"__TOC_PLUGIN_SHARED__\"];\nvar shared = registry && registry[\"react\"];\nif (!shared) throw new Error(\"The host does not provide react; this plugin needs a newer app version.\");\nmodule.exports = shared;", "var registry = globalThis[\"__TOC_PLUGIN_SHARED__\"];\nvar shared = registry && registry[\"@toc-starter/plugin-sdk/ui\"];\nif (!shared) throw new Error(\"The host does not provide @toc-starter/plugin-sdk/ui; this plugin needs a newer app version.\");\nmodule.exports = shared;", "var registry = globalThis[\"__TOC_PLUGIN_SHARED__\"];\nvar shared = registry && registry[\"react/jsx-runtime\"];\nif (!shared) throw new Error(\"The host does not provide react/jsx-runtime; this plugin needs a newer app version.\");\nmodule.exports = shared;", "import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { definePlugin, useHostContext, type LocalServiceApi, type LocalServiceStatus, type PluginContext } from \"@toc-starter/plugin-sdk\";\nimport { Badge, Btn, Card, CardTitle, Input, PageHeader, Toggle } from \"@toc-starter/plugin-sdk/ui\";\nimport \"./styles.css\";\nimport { DEFAULT_LOCAL_URL, LocalError, local, sameServer, type LocalStatus } from \"./local\";\nimport { RemoteTab } from \"./remote\";\nimport { AUTO_BIND_KEY, Command, LOCAL_SERVICE_KEY, LOCAL_URL_KEY, TOKEN_KEY, errorText, type Device, type ServerContext, type WorkspaceContext } from \"./shared\";\n\nconst POLL_MS = 3_000;\nconst AUTO_BIND_MS = 10_000;\nconst AUTO_BIND_RETRY_MS = 60_000;\n/** toc-ocra's service entry exits with this when another toc-ocra holds the local port. */\nconst EXIT_PORT_IN_USE = 3;\nconst TAB_KEY = \"tab\";\n\n/** Resolved against dist/plugin/renderer.js. */\nconst iconUrl = new URL(\"../../assets/icon.svg\", import.meta.url).href;\n\ntype Phase =\n | { kind: \"checking\" }\n | { kind: \"offline\" }\n | { kind: \"starting\" }\n | { kind: \"needsCode\" }\n | { kind: \"ready\"; status: LocalStatus };\n\ntype Tab = \"local\" | \"remote\";\n\nfunction Icon() {\n return <img src={iconUrl} alt=\"\" width={16} height={16} draggable={false} />;\n}\n\n/** The desktop app's service API; undefined on hosts that predate it. */\nfunction localService(ctx: PluginContext): LocalServiceApi | undefined {\n const service = (ctx.core as { service?: LocalServiceApi }).service;\n try {\n return service && service.available() ? service : undefined;\n } catch {\n return undefined;\n }\n}\n\nconst isActive = (status: LocalServiceStatus | null | undefined) => status?.state === \"running\" || status?.state === \"starting\";\n\n/** Another toc-ocra (background service, terminal) already answers on this computer. */\nasync function hasLocalInstance(localUrl: string): Promise<boolean> {\n try {\n await local.info(localUrl);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Runs the auto-bind check right away (set by {@link startAutoBind}). */\nlet triggerAutoBind: (() => void) | undefined;\n\nfunction AgentPage({ ctx }: { ctx: PluginContext }) {\n const server = useHostContext<ServerContext>(ctx, \"server\");\n const workspace = useHostContext<WorkspaceContext>(ctx, \"workspace\");\n const [tab, setTabState] = useState<Tab>(() => (ctx.storage.get<string>(TAB_KEY, \"local\") === \"remote\" ? \"remote\" : \"local\"));\n const setTab = (next: Tab) => {\n ctx.storage.set(TAB_KEY, next);\n setTabState(next);\n };\n const [localUrl] = useState(() => ctx.storage.get(LOCAL_URL_KEY, DEFAULT_LOCAL_URL));\n const [token, setToken] = useState(() => ctx.secureStorage.get(TOKEN_KEY, \"\"));\n const [phase, setPhase] = useState<Phase>({ kind: \"checking\" });\n const [service, setService] = useState<LocalServiceStatus | null>(null);\n const [error, setError] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const alive = useRef(true);\n const svc = localService(ctx);\n\n const refresh = useCallback(async () => {\n let hosted: LocalServiceStatus | null = null;\n if (svc) {\n try {\n hosted = await svc.status();\n } catch {\n hosted = null;\n }\n }\n if (!alive.current) return;\n setService(hosted);\n // A service the app started brings its own control token.\n let active = token;\n if (isActive(hosted) && hosted?.token) {\n active = hosted.token;\n if (hosted.token !== token) {\n ctx.secureStorage.set(TOKEN_KEY, hosted.token);\n setToken(hosted.token);\n }\n }\n try {\n await local.info(localUrl);\n } catch {\n if (alive.current) setPhase({ kind: isActive(hosted) ? \"starting\" : \"offline\" });\n return;\n }\n if (!active) {\n if (alive.current) setPhase({ kind: \"needsCode\" });\n return;\n }\n try {\n const status = await local.status(localUrl, active);\n if (alive.current) setPhase({ kind: \"ready\", status });\n } catch (reason) {\n if (!alive.current) return;\n if (reason instanceof LocalError && reason.kind === \"unauthorized\") {\n // Reinstalled service (new control token), or another toc-ocra holds the port: pair again.\n ctx.secureStorage.remove(TOKEN_KEY);\n setToken(\"\");\n setPhase({ kind: \"needsCode\" });\n } else {\n setPhase({ kind: \"offline\" });\n }\n }\n }, [ctx, localUrl, svc, token]);\n\n useEffect(() => {\n alive.current = true;\n void refresh();\n const timer = window.setInterval(() => void refresh(), POLL_MS);\n return () => { alive.current = false; window.clearInterval(timer); };\n }, [refresh]);\n\n useEffect(() => {\n if (phase.kind === \"ready\") {\n const s = phase.status;\n ctx.reportHealth(s.paired && s.connected ? { status: \"healthy\", summary: ctx.t(\"health.online\", { name: s.device_name }) } : { status: \"degraded\", summary: ctx.t(s.paired ? \"health.disconnected\" : \"health.unpaired\") });\n } else if (phase.kind === \"offline\") {\n ctx.reportHealth({ status: \"degraded\", summary: ctx.t(svc ? \"health.disabled\" : \"health.offline\") });\n }\n }, [ctx, phase, svc]);\n\n const run = async (action: () => Promise<void>) => {\n setBusy(true);\n setError(\"\");\n try {\n await action();\n } catch (reason) {\n setError(errorText(ctx, reason));\n } finally {\n setBusy(false);\n }\n };\n\n const toggleService = (on: boolean) => run(async () => {\n if (!svc) return;\n if (!on) {\n ctx.storage.set(LOCAL_SERVICE_KEY, \"off\");\n setService(await svc.stop());\n await refresh();\n return;\n }\n ctx.storage.set(LOCAL_SERVICE_KEY, \"on\");\n // Turning it on means \"take tasks here\": undo a manual unbind.\n ctx.storage.remove(AUTO_BIND_KEY);\n // A background service already runs the agent: use it, do not start a\n // second one that could only fail on the taken port.\n if (!isActive(service) && (await hasLocalInstance(localUrl))) {\n triggerAutoBind?.();\n await refresh();\n return;\n }\n const started = await svc.start();\n setService(started);\n if (started.token) {\n ctx.secureStorage.set(TOKEN_KEY, started.token);\n setToken(started.token);\n }\n if (await waitForLocalService(localUrl, () => alive.current)) triggerAutoBind?.();\n await refresh();\n });\n\n const hostedExit = service?.state === \"exited\" ? service : null;\n const external = Boolean(svc) && !isActive(service) && (phase.kind === \"ready\" || phase.kind === \"needsCode\");\n\n return (\n <div className=\"da-app\">\n <PageHeader title={ctx.t(\"page.title\")} subtitle={ctx.t(\"page.subtitle\")} />\n <div className=\"da-tabs\" role=\"tablist\" aria-label={ctx.t(\"page.title\")}>\n {([\"local\", \"remote\"] as const).map(item => (\n <button key={item} type=\"button\" role=\"tab\" aria-selected={tab === item} className={tab === item ? \"da-tab da-tab-active\" : \"da-tab\"} onClick={() => setTab(item)}>\n {ctx.t(`tabs.${item}`)}\n </button>\n ))}\n </div>\n {error && <div role=\"alert\" className=\"da-alert\">{error}</div>}\n\n {tab === \"remote\" && (\n <RemoteTab ctx={ctx} server={server} workspace={workspace} localDeviceId={phase.kind === \"ready\" && phase.status.paired ? phase.status.device_id : \"\"} />\n )}\n\n {tab === \"local\" && (\n <>\n {svc && (\n <LocalServiceCard\n ctx={ctx}\n status={service}\n external={external}\n exit={hostedExit}\n busy={busy}\n onToggle={on => void toggleService(on)}\n />\n )}\n {phase.kind === \"checking\" && <Card><span className=\"da-muted\">{ctx.t(\"common.checking\")}</span></Card>}\n {phase.kind === \"starting\" && <Card><span className=\"da-muted\">{ctx.t(\"service.starting\")}</span></Card>}\n {phase.kind === \"offline\" && !svc && <OfflineCard ctx={ctx} localUrl={localUrl} />}\n {phase.kind === \"needsCode\" && (\n <PairCard ctx={ctx} busy={busy} onPair={code => run(async () => {\n const result = await local.pair(localUrl, code).catch((reason: unknown) => {\n if (reason instanceof LocalError && reason.code === \"code_wrong\") throw new Error(ctx.t(\"pair.wrong\"));\n if (reason instanceof LocalError && reason.code === \"locked\") throw new Error(ctx.t(\"pair.locked\"));\n throw reason;\n });\n ctx.secureStorage.set(TOKEN_KEY, result.token);\n setToken(result.token);\n })} />\n )}\n {phase.kind === \"ready\" && (\n <ReadyView\n ctx={ctx}\n status={phase.status}\n server={server}\n workspace={workspace}\n busy={busy}\n run={run}\n localUrl={localUrl}\n token={token}\n onStatus={status => setPhase({ kind: \"ready\", status })}\n />\n )}\n </>\n )}\n </div>\n );\n}\n\n/** Waits up to 15 s for the local service to answer. */\nasync function waitForLocalService(localUrl: string, alive: () => boolean): Promise<boolean> {\n for (let attempt = 0; attempt < 30 && alive(); attempt += 1) {\n try {\n await local.info(localUrl);\n return true;\n } catch {\n await new Promise(resolve => window.setTimeout(resolve, 500));\n }\n }\n return false;\n}\n\nfunction LocalServiceCard({ ctx, status, external, exit, busy, onToggle }: {\n ctx: PluginContext;\n status: LocalServiceStatus | null;\n external: boolean;\n exit: LocalServiceStatus | null;\n busy: boolean;\n onToggle: (on: boolean) => void;\n}) {\n const hosted = isActive(status);\n // Another toc-ocra (background service / terminal) already serves this\n // computer: the agent is on, it just is not owned by the desktop app.\n const viaExternal = !hosted && external;\n const on = hosted || viaExternal;\n const portTaken = exit?.exitCode === EXIT_PORT_IN_USE;\n return (\n <Card>\n <div className=\"da-switch\">\n <div>\n <CardTitle>{ctx.t(\"service.title\")}</CardTitle>\n <p className=\"da-text\">{ctx.t(viaExternal ? \"service.onExternalBody\" : on ? \"service.onBody\" : \"service.offBody\")}</p>\n </div>\n <Toggle checked={on} disabled={busy || viaExternal} label={ctx.t(\"service.title\")} onChange={onToggle} />\n </div>\n {hosted && <p className=\"da-muted\">{ctx.t(\"service.followsApp\")}</p>}\n {viaExternal && <p className=\"da-muted\">{ctx.t(\"service.external\")}</p>}\n {!on && portTaken && <p className=\"da-muted\">{ctx.t(\"service.portTaken\")}</p>}\n {exit && !portTaken && exit.exitCode !== 0 && (\n <div role=\"alert\" className=\"da-alert\">{ctx.t(\"service.crashed\", { error: exit.error || `exit ${exit.exitCode ?? exit.signal ?? \"?\"}` })}</div>\n )}\n </Card>\n );\n}\n\nfunction OfflineCard({ ctx, localUrl }: { ctx: PluginContext; localUrl: string }) {\n return (\n <Card>\n <CardTitle>{ctx.t(\"offline.title\")}</CardTitle>\n <p className=\"da-text\">{ctx.t(\"offline.body\", { url: localUrl })}</p>\n <div className=\"da-step\">{ctx.t(\"offline.once\")}</div>\n <Command ctx={ctx} value=\"npx toc-ocra@latest run\" />\n <div className=\"da-step\">{ctx.t(\"offline.service\")}</div>\n <Command ctx={ctx} value=\"npm install -g toc-ocra@latest && toc-ocra service:install\" />\n <p className=\"da-muted\">{ctx.t(\"offline.node\")}</p>\n </Card>\n );\n}\n\nfunction PairCard({ ctx, busy, onPair }: { ctx: PluginContext; busy: boolean; onPair: (code: string) => void }) {\n const [code, setCode] = useState(\"\");\n const valid = /^\\d{6}$/.test(code);\n return (\n <Card>\n <CardTitle>{ctx.t(\"pair.title\")}</CardTitle>\n <p className=\"da-text\">{ctx.t(\"pair.body\")}</p>\n <Command ctx={ctx} value=\"toc-ocra code\" />\n <form className=\"da-row\" onSubmit={event => { event.preventDefault(); if (valid) onPair(code); }}>\n <Input\n value={code}\n onChange={event => setCode(event.target.value.replace(/\\D/g, \"\").slice(0, 6))}\n placeholder={ctx.t(\"pair.placeholder\")}\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n aria-label={ctx.t(\"pair.placeholder\")}\n className=\"da-code-input\"\n />\n <Btn primary type=\"submit\" disabled={!valid || busy}>{ctx.t(\"pair.submit\")}</Btn>\n </form>\n </Card>\n );\n}\n\nfunction ReadyView({ ctx, status, server, workspace, busy, run, localUrl, token, onStatus }: {\n ctx: PluginContext;\n status: LocalStatus;\n server: ServerContext | undefined;\n workspace: WorkspaceContext | undefined;\n busy: boolean;\n run: (action: () => Promise<void>) => Promise<void>;\n localUrl: string;\n token: string;\n onStatus: (status: LocalStatus) => void;\n}) {\n const boundElsewhere = status.paired && server && !sameServer(status.server_url, server.origin);\n const unpair = () => run(async () => {\n // Revoke on the server too when the device belongs to the server shown.\n if (status.device_id && server && sameServer(status.server_url, server.origin)) {\n await ctx.core.request(`/devices/${encodeURIComponent(status.device_id)}/agent`, { method: \"DELETE\" }).catch(() => undefined);\n }\n ctx.storage.set(AUTO_BIND_KEY, \"off\");\n onStatus(await local.unpair(localUrl, token));\n });\n return (\n <>\n <Card>\n <CardTitle>{ctx.t(\"status.title\")}</CardTitle>\n <dl className=\"da-grid\">\n <div><dt>{ctx.t(\"status.service\")}</dt><dd>toc-ocra {status.version} \u00B7 {status.hostname}</dd></div>\n <div><dt>{ctx.t(\"status.binding\")}</dt><dd><BindingBadge ctx={ctx} status={status} /></dd></div>\n {status.paired && <div><dt>{ctx.t(\"status.device\")}</dt><dd>{status.device_name}</dd></div>}\n {status.paired && <div><dt>{ctx.t(\"status.server\")}</dt><dd className=\"da-mono\">{status.server_url}</dd></div>}\n {status.paired && <div><dt>{ctx.t(\"status.runs\")}</dt><dd>{status.active_runs}</dd></div>}\n </dl>\n {status.paired && !status.connected && status.connection_error && <p className=\"da-muted\">{ctx.t(\"status.lastError\", { error: status.connection_error })}</p>}\n {status.token_from_env && <p className=\"da-muted\">{ctx.t(\"status.tokenFromEnv\")}</p>}\n {status.paired && (\n <div className=\"da-row\">\n <Btn danger disabled={busy || status.token_from_env} onClick={() => void unpair()}>{ctx.t(\"status.unpair\")}</Btn>\n </div>\n )}\n </Card>\n\n {(!status.paired || status.unauthorized || boundElsewhere) && (\n <BindCard ctx={ctx} status={status} server={server} workspace={workspace} busy={busy} run={run} localUrl={localUrl} token={token} onStatus={onStatus} rebind={status.paired} />\n )}\n\n <Card>\n <CardTitle>{ctx.t(\"cli.title\")}</CardTitle>\n <ul className=\"da-list\">\n {status.providers.filter(provider => provider.installed).map(provider => (\n <li key={provider.id}>\n <span className=\"da-mono\">{provider.id}</span>\n <span className=\"da-muted\">{provider.version}</span>\n {provider.runnable ? <Badge variant=\"ok\">{ctx.t(\"cli.runnable\")}</Badge> : <Badge variant=\"muted\">{ctx.t(\"cli.detectOnly\")}</Badge>}\n </li>\n ))}\n {!status.providers.some(provider => provider.installed) && <li className=\"da-muted\">{ctx.t(\"cli.none\")}</li>}\n </ul>\n <div className=\"da-row\">\n <Btn disabled={busy} onClick={() => void run(async () => onStatus(await local.detect(localUrl, token)))}>{ctx.t(\"cli.redetect\")}</Btn>\n </div>\n </Card>\n </>\n );\n}\n\nfunction BindingBadge({ ctx, status }: { ctx: PluginContext; status: LocalStatus }) {\n if (!status.paired) return <Badge variant=\"muted\">{ctx.t(\"binding.unpaired\")}</Badge>;\n if (status.unauthorized) return <Badge variant=\"err\">{ctx.t(\"binding.revoked\")}</Badge>;\n if (status.connected) return <Badge variant=\"ok\">{ctx.t(\"binding.online\")}</Badge>;\n return <Badge variant=\"warn\">{ctx.t(\"binding.connecting\")}</Badge>;\n}\n\nfunction BindCard({ ctx, status, server, workspace, busy, run, localUrl, token, onStatus, rebind }: {\n ctx: PluginContext;\n status: LocalStatus;\n server: ServerContext | undefined;\n workspace: WorkspaceContext | undefined;\n busy: boolean;\n run: (action: () => Promise<void>) => Promise<void>;\n localUrl: string;\n token: string;\n onStatus: (status: LocalStatus) => void;\n rebind: boolean;\n}) {\n const [devices, setDevices] = useState<Device[]>([]);\n const [choice, setChoice] = useState(\"new\");\n const [name, setName] = useState(status.hostname);\n const [serverUrl, setServerUrl] = useState(server?.origin ?? \"\");\n useEffect(() => { if (server?.origin) setServerUrl(server.origin); }, [server?.origin]);\n\n useEffect(() => {\n if (!workspace?.id) return;\n let cancelled = false;\n ctx.core.request<{ items?: Device[] }>(`/workspaces/${encodeURIComponent(workspace.id)}/devices`)\n .then(result => { if (!cancelled) setDevices((result.items ?? []).filter(device => device.is_mine)); })\n .catch(() => { if (!cancelled) setDevices([]); });\n return () => { cancelled = true; };\n }, [ctx, workspace?.id]);\n\n const bind = () => run(async () => {\n if (!workspace?.id) throw new Error(ctx.t(\"bind.noWorkspace\"));\n if (!serverUrl.trim()) throw new Error(ctx.t(\"bind.noServer\"));\n let deviceId = choice;\n if (choice === \"new\") {\n const created = await ctx.core.request<{ id: string }>(`/workspaces/${encodeURIComponent(workspace.id)}/devices`, {\n method: \"POST\",\n body: JSON.stringify({ name: name.trim() || status.hostname, location: \"local\", platform: status.platform, note: \"\", share_to_workspace: false }),\n });\n deviceId = created.id;\n setChoice(deviceId);\n }\n const pairing = await ctx.core.request<{ code: string }>(`/devices/${encodeURIComponent(deviceId)}/pairing-code`, { method: \"POST\" });\n onStatus(await local.enroll(localUrl, token, serverUrl.trim(), pairing.code));\n ctx.storage.remove(AUTO_BIND_KEY);\n ctx.notify({ title: ctx.t(\"page.title\"), body: ctx.t(\"bind.done\", { server: server?.name ?? serverUrl }) });\n });\n\n return (\n <Card>\n <CardTitle>{ctx.t(rebind ? \"bind.rebindTitle\" : \"bind.title\")}</CardTitle>\n {rebind && status.unauthorized && <p className=\"da-text\">{ctx.t(\"bind.revoked\")}</p>}\n {rebind && !status.unauthorized && <p className=\"da-text\">{ctx.t(\"bind.elsewhere\", { bound: status.server_url, current: server?.origin ?? \"\" })}</p>}\n <p className=\"da-text\">{ctx.t(\"bind.body\", { server: server?.name ?? \"\", workspace: workspace?.name ?? \"\" })}</p>\n <label className=\"da-field\">\n <span>{ctx.t(\"bind.server\")}</span>\n <Input value={serverUrl} onChange={event => setServerUrl(event.target.value)} spellCheck={false} className=\"da-mono\" />\n </label>\n <label className=\"da-field\">\n <span>{ctx.t(\"bind.device\")}</span>\n <select className=\"da-select\" value={choice} onChange={event => setChoice(event.target.value)}>\n <option value=\"new\">{ctx.t(\"bind.newDevice\")}</option>\n {devices.map(device => (\n <option key={device.id} value={device.id}>{device.name}{device.paired ? ` \u00B7 ${ctx.t(\"bind.replaces\")}` : \"\"}</option>\n ))}\n </select>\n </label>\n {choice === \"new\" && (\n <label className=\"da-field\">\n <span>{ctx.t(\"bind.name\")}</span>\n <Input value={name} onChange={event => setName(event.target.value)} maxLength={100} />\n </label>\n )}\n <div className=\"da-row\">\n <Btn primary disabled={busy || !workspace?.id} onClick={() => void bind()}>{ctx.t(busy ? \"bind.working\" : \"bind.submit\")}</Btn>\n </div>\n </Card>\n );\n}\n\n/**\n * Keeps this computer bound to the server the desktop app is signed in to\n * (public or LAN): when they differ, asks the server for this machine's device\n * (matched by machine id, so the same device is reused) and re-enrolls the\n * local service. Waits while tasks are running; stays out of the way after\n * the user unbinds by hand or when the server revoked the device.\n */\nfunction startAutoBind(ctx: PluginContext) {\n let busy = false;\n let deferredFor = \"\";\n const failedAt = new Map<string, number>();\n\n const tick = async () => {\n if (busy || ctx.signal.aborted) return;\n const server = ctx.core.context<ServerContext>(\"server\");\n const token = ctx.secureStorage.get(TOKEN_KEY, \"\");\n if (!server?.origin || !token || ctx.storage.get<string>(AUTO_BIND_KEY, \"on\") === \"off\") return;\n if (Date.now() - (failedAt.get(server.origin) ?? 0) < AUTO_BIND_RETRY_MS) return;\n busy = true;\n try {\n const localUrl = ctx.storage.get(LOCAL_URL_KEY, DEFAULT_LOCAL_URL);\n const status = await local.status(localUrl, token);\n if (!status.machine_id || status.token_from_env) return;\n if (status.paired && (status.unauthorized || sameServer(status.server_url, server.origin))) {\n deferredFor = \"\";\n return;\n }\n if (status.active_runs > 0) {\n if (deferredFor !== server.origin) {\n deferredFor = server.origin;\n ctx.notify({ title: ctx.t(\"page.title\"), body: ctx.t(\"auto.deferred\", { server: server.name || server.origin }) });\n }\n return;\n }\n const pairing = await ctx.core.request<{ code: string; device_name: string }>(\"/devices/machine/pairing-code\", {\n method: \"POST\",\n body: JSON.stringify({\n machine_id: status.machine_id,\n hostname: status.hostname,\n platform: status.platform,\n current_device_id: status.paired ? status.device_id : \"\",\n }),\n });\n await local.enroll(localUrl, token, server.origin, pairing.code);\n deferredFor = \"\";\n failedAt.delete(server.origin);\n ctx.notify({ title: ctx.t(\"page.title\"), body: ctx.t(\"auto.done\", { server: server.name || server.origin, device: pairing.device_name }) });\n } catch (reason) {\n // Local service offline / plugin not paired yet: just try again next tick.\n // A server or enroll failure backs off for this server.\n if (!(reason instanceof LocalError) || reason.kind === \"rejected\") failedAt.set(server.origin, Date.now());\n } finally {\n busy = false;\n }\n };\n\n const timer = window.setInterval(() => void tick(), AUTO_BIND_MS);\n triggerAutoBind = () => void tick();\n ctx.registerDisposer(() => {\n window.clearInterval(timer);\n triggerAutoBind = undefined;\n });\n ctx.registerDisposer(ctx.core.onContextChange(\"server\", () => void tick()));\n window.setTimeout(() => void tick(), 1_500);\n}\n\n/**\n * Follows the desktop app: when the user left the local agent on, start it\n * again with the app. The app stops it when it quits.\n */\nfunction startLocalServiceIfEnabled(ctx: PluginContext) {\n const svc = localService(ctx);\n if (!svc || ctx.storage.get<string>(LOCAL_SERVICE_KEY, \"off\") !== \"on\") return;\n const localUrl = ctx.storage.get(LOCAL_URL_KEY, DEFAULT_LOCAL_URL);\n void hasLocalInstance(localUrl)\n .then(running => {\n if (running) {\n // Already served by a background service: nothing to start.\n window.setTimeout(() => triggerAutoBind?.(), 3_000);\n return undefined;\n }\n return svc.start();\n })\n .then(status => {\n if (!status) return;\n if (status.token) ctx.secureStorage.set(TOKEN_KEY, status.token);\n window.setTimeout(() => triggerAutoBind?.(), 3_000);\n })\n .catch(() => undefined);\n}\n\nexport default definePlugin({\n activate(ctx) {\n ctx.registerNavigation(\"agent.nav\", <Icon />);\n ctx.registerRoute(\"agent.home\", <AgentPage ctx={ctx} />);\n startLocalServiceIfEnabled(ctx);\n startAutoBind(ctx);\n },\n});\n", "/**\n * Runtime part of the SDK that ends up inside plugin bundles. Keep it tiny:\n * everything here is copied into every plugin. Build-time helpers live in lib/.\n * `react` resolves to the host's instance through `toc-plugin build`.\n */\nimport { useSyncExternalStore } from \"react\";\n\n/** Host contract this SDK targets. Must match PLUGIN_API_VERSION in lib/constants.mjs. */\nexport const PLUGIN_API_VERSION = \"1.0.0\";\n\n/**\n * Identity helper that gives a plugin module full type inference.\n *\n * @template {import(\"./index\").PluginModule} T\n * @param {T} plugin\n * @returns {T}\n */\nexport function definePlugin(plugin) {\n if (!plugin || typeof plugin.activate !== \"function\") {\n throw new TypeError(\"definePlugin() needs an object with an activate(context) function\");\n }\n return plugin;\n}\n\n/**\n * Re-renders when the app publishes a new value under `key`.\n *\n * @template T\n * @param {import(\"./index\").PluginContext} ctx\n * @param {string} key\n * @returns {T | undefined}\n */\nexport function useHostContext(ctx, key) {\n return useSyncExternalStore(\n listener => ctx.core.onContextChange(key, listener),\n () => ctx.core.context(key),\n );\n}\n", "/**\n * Client for the local toc-ocra service (`toc-ocra run`) on this computer.\n * The plugin pairs once with the 6-digit connection code and then sends the\n * control token as a bearer token.\n */\nexport const DEFAULT_LOCAL_URL = \"http://127.0.0.1:17339\";\n\nexport interface LocalInfo {\n name: string;\n version: string;\n protocol: number;\n paired: boolean;\n}\n\nexport interface LocalProvider {\n id: string;\n installed: boolean;\n runnable: boolean;\n version: string;\n}\n\nexport interface LocalStatus {\n version: string;\n hostname: string;\n /** Stable id of this installation (older services omit it). */\n machine_id?: string;\n platform: \"mac\" | \"win\" | \"linux\";\n paired: boolean;\n running: boolean;\n device_id: string;\n device_name: string;\n server_url: string;\n connected: boolean;\n unauthorized: boolean;\n connection_error: string;\n active_runs: number;\n allowed_roots: string[];\n token_from_env: boolean;\n providers: LocalProvider[];\n}\n\n/** Why a local call failed: the service is not running, the token is not accepted, or the service said no. */\nexport class LocalError extends Error {\n constructor(readonly kind: \"offline\" | \"unauthorized\" | \"rejected\", message: string, readonly code = \"\") {\n super(message);\n }\n}\n\nasync function call<T>(base: string, path: string, init: RequestInit & { token?: string } = {}): Promise<T> {\n const headers = new Headers(init.headers);\n if (init.token) headers.set(\"Authorization\", `Bearer ${init.token}`);\n if (init.body) headers.set(\"Content-Type\", \"application/json\");\n let response: Response;\n try {\n response = await fetch(`${base.replace(/\\/+$/, \"\")}${path}`, { ...init, headers, cache: \"no-store\" });\n } catch {\n throw new LocalError(\"offline\", \"local service unreachable\");\n }\n const body = (await response.json().catch(() => null)) as Record<string, unknown> | null;\n if (response.status === 401) throw new LocalError(\"unauthorized\", \"unauthorized\");\n if (!response.ok) {\n const code = typeof body?.error === \"string\" ? body.error : \"\";\n const message = typeof body?.message === \"string\" ? body.message : code || `HTTP ${response.status}`;\n throw new LocalError(\"rejected\", message, code);\n }\n return body as T;\n}\n\nexport const local = {\n info: (base: string) => call<LocalInfo>(base, \"/control/info\"),\n pair: (base: string, code: string) => call<{ token: string }>(base, \"/control/pair\", { method: \"POST\", body: JSON.stringify({ code }) }),\n status: (base: string, token: string) => call<LocalStatus>(base, \"/control/status\", { token }),\n enroll: (base: string, token: string, serverUrl: string, code: string) =>\n call<LocalStatus>(base, \"/control/enroll\", { method: \"POST\", token, body: JSON.stringify({ server_url: serverUrl, code }) }),\n unpair: (base: string, token: string) => call<LocalStatus>(base, \"/control/unpair\", { method: \"POST\", token }),\n detect: (base: string, token: string) => call<LocalStatus>(base, \"/control/detect\", { method: \"POST\", token }),\n};\n\n/** Two backend addresses name the same server (trailing slash and case insensitive). */\nexport function sameServer(left: string, right: string): boolean {\n const normalize = (value: string) => {\n try {\n return new URL(value).origin.toLowerCase();\n } catch {\n return value.trim().replace(/\\/+$/, \"\").toLowerCase();\n }\n };\n return Boolean(left && right) && normalize(left) === normalize(right);\n}\n", "import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { PluginContext } from \"@toc-starter/plugin-sdk\";\nimport { Badge, Btn, Card, CardTitle, Input, Toggle } from \"@toc-starter/plugin-sdk/ui\";\nimport { Command, errorText, type Device, type ServerContext, type WorkspaceContext } from \"./shared\";\n\n/**\n * \u5176\u4ED6\u7535\u8111: install the device agent on another machine (a Linux server,\n * another Mac, Windows). The page creates the device and a one-time pairing\n * code, shows one command to run there (install + pair + background\n * service), and polls until that machine comes online. Enabling / disabling\n * later is a server-side switch that keeps the pairing.\n */\n\ntype Platform = \"mac\" | \"linux\" | \"win\";\nconst PLATFORMS: readonly Platform[] = [\"linux\", \"mac\", \"win\"];\nconst IDLE_POLL_MS = 10_000;\nconst WAIT_POLL_MS = 3_000;\nconst REMOTE_SERVER_KEY = \"remoteServerUrl\";\n\ninterface Pending {\n deviceId: string;\n deviceName: string;\n platform: Platform;\n code: string;\n expiresAt: number;\n /** Credential id before pairing; a new one means the command ran. */\n baseline: string | null;\n matched: boolean;\n}\n\nconst LOOPBACK = new Set([\"127.0.0.1\", \"localhost\", \"[::1]\", \"::1\", \"0.0.0.0\"]);\n\n/** The server origin to put in the command, or null when it is not an http(s) URL. */\nfunction commandServer(value: string): string | null {\n try {\n const url = new URL(value.trim());\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? url.origin : null;\n } catch {\n return null;\n }\n}\n\nfunction isLoopback(origin: string): boolean {\n try {\n return LOOPBACK.has(new URL(origin).hostname);\n } catch {\n return false;\n }\n}\n\nexport function setupCommand(platform: Platform, code: string, server: string): string {\n const setup = `toc-ocra setup --code ${code} --server ${server}`;\n // PowerShell 5.1 has no `&&`; `if ($?)` runs setup only after a successful install.\n if (platform === \"win\") return `npm install -g toc-ocra@latest; if ($?) { ${setup} }`;\n return `npm install -g toc-ocra@latest && ${setup}`;\n}\n\nfunction DeviceBadge({ ctx, device }: { ctx: PluginContext; device: Device }) {\n if (device.status === \"online\") return <Badge variant=\"ok\">{ctx.t(\"remote.status.online\")}</Badge>;\n if (device.status === \"disabled\") return <Badge variant=\"warn\">{ctx.t(\"remote.status.disabled\")}</Badge>;\n if (device.status === \"offline\") return <Badge variant=\"err\">{ctx.t(\"remote.status.offline\")}</Badge>;\n return <Badge variant=\"muted\">{ctx.t(\"remote.status.notConnected\")}</Badge>;\n}\n\nexport function RemoteTab({ ctx, server, workspace, localDeviceId }: {\n ctx: PluginContext;\n server: ServerContext | undefined;\n workspace: WorkspaceContext | undefined;\n /** This computer's device, listed on the \u672C\u673A tab instead. */\n localDeviceId: string;\n}) {\n const [devices, setDevices] = useState<Device[] | null>(null);\n const [error, setError] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const [pending, setPending] = useState<Pending | null>(null);\n const [adding, setAdding] = useState(false);\n const [name, setName] = useState(\"\");\n const [platform, setPlatform] = useState<Platform>(\"linux\");\n const [serverUrl, setServerUrl] = useState(() => ctx.storage.get<string>(REMOTE_SERVER_KEY, \"\") || server?.origin || \"\");\n const pendingRef = useRef(pending);\n pendingRef.current = pending;\n const alive = useRef(true);\n\n useEffect(() => {\n if (!ctx.storage.get<string>(REMOTE_SERVER_KEY, \"\") && server?.origin) setServerUrl(server.origin);\n }, [ctx, server?.origin]);\n\n const load = useCallback(async () => {\n if (!workspace?.id) return;\n try {\n const result = await ctx.core.request<{ items?: Device[] }>(`/workspaces/${encodeURIComponent(workspace.id)}/devices`);\n if (!alive.current) return;\n const mine = (result.items ?? []).filter(device => device.is_mine && device.id !== localDeviceId);\n setDevices(mine);\n const waiting = pendingRef.current;\n if (waiting && !waiting.matched) {\n const device = mine.find(item => item.id === waiting.deviceId);\n if (device?.agent_id && device.agent_id !== waiting.baseline && device.terminal_online) {\n setPending({ ...waiting, matched: true });\n ctx.notify({ title: ctx.t(\"page.title\"), body: ctx.t(\"remote.matchedNotice\", { name: device.name }) });\n }\n }\n } catch (reason) {\n if (alive.current) setError(errorText(ctx, reason));\n }\n }, [ctx, localDeviceId, workspace?.id]);\n\n const waiting = Boolean(pending && !pending.matched);\n useEffect(() => {\n alive.current = true;\n void load();\n const timer = window.setInterval(() => void load(), waiting ? WAIT_POLL_MS : IDLE_POLL_MS);\n return () => { alive.current = false; window.clearInterval(timer); };\n }, [load, waiting]);\n\n const run = async (action: () => Promise<void>) => {\n setBusy(true);\n setError(\"\");\n try {\n await action();\n } catch (reason) {\n setError(errorText(ctx, reason));\n } finally {\n setBusy(false);\n }\n };\n\n const origin = commandServer(serverUrl);\n const saveServer = (value: string) => {\n setServerUrl(value);\n if (value.trim() && value.trim() !== server?.origin) ctx.storage.set(REMOTE_SERVER_KEY, value.trim());\n else ctx.storage.remove(REMOTE_SERVER_KEY);\n };\n\n const pair = (device: Pick<Device, \"id\" | \"name\" | \"platform\" | \"agent_id\">) => run(async () => {\n if (!origin) throw new Error(ctx.t(\"remote.serverInvalid\"));\n const result = await ctx.core.request<{ code: string; expires_at: number }>(`/devices/${encodeURIComponent(device.id)}/pairing-code`, { method: \"POST\" });\n setPending({\n deviceId: device.id,\n deviceName: device.name,\n platform: PLATFORMS.includes(device.platform) ? device.platform : \"linux\",\n code: result.code,\n expiresAt: result.expires_at,\n baseline: device.agent_id ?? null,\n matched: false,\n });\n });\n\n const addComputer = () => run(async () => {\n if (!workspace?.id) throw new Error(ctx.t(\"bind.noWorkspace\"));\n if (!origin) throw new Error(ctx.t(\"remote.serverInvalid\"));\n const deviceName = name.trim() || ctx.t(`remote.platform.${platform}`);\n const created = await ctx.core.request<Device>(`/workspaces/${encodeURIComponent(workspace.id)}/devices`, {\n method: \"POST\",\n body: JSON.stringify({ name: deviceName, location: platform === \"linux\" ? \"cloud\" : \"local\", platform, note: \"\", share_to_workspace: false }),\n });\n const result = await ctx.core.request<{ code: string; expires_at: number }>(`/devices/${encodeURIComponent(created.id)}/pairing-code`, { method: \"POST\" });\n setPending({ deviceId: created.id, deviceName: created.name || deviceName, platform, code: result.code, expiresAt: result.expires_at, baseline: null, matched: false });\n setAdding(false);\n setName(\"\");\n await load();\n });\n\n const setEnabled = (device: Device, enabled: boolean) => run(async () => {\n await ctx.core.request(`/devices/${encodeURIComponent(device.id)}/enabled`, { method: \"PUT\", body: JSON.stringify({ enabled }) });\n await load();\n });\n\n return (\n <>\n {error && <div role=\"alert\" className=\"da-alert\">{error}</div>}\n <Card>\n <CardTitle>{ctx.t(\"remote.title\")}</CardTitle>\n <p className=\"da-text\">{ctx.t(\"remote.body\")}</p>\n <label className=\"da-field\">\n <span>{ctx.t(\"remote.server\")}</span>\n <Input value={serverUrl} onChange={event => saveServer(event.target.value)} spellCheck={false} className=\"da-mono\" />\n </label>\n {!origin && <p className=\"da-muted da-warn\">{ctx.t(\"remote.serverInvalid\")}</p>}\n {origin && isLoopback(origin) && <p className=\"da-muted da-warn\">{ctx.t(\"remote.loopback\")}</p>}\n {!adding && (\n <div className=\"da-row\">\n <Btn primary disabled={busy || !workspace?.id} onClick={() => setAdding(true)}>{ctx.t(\"remote.add\")}</Btn>\n </div>\n )}\n {adding && (\n <form className=\"da-form\" onSubmit={event => { event.preventDefault(); void addComputer(); }}>\n <label className=\"da-field\">\n <span>{ctx.t(\"remote.platformLabel\")}</span>\n <select className=\"da-select\" value={platform} onChange={event => setPlatform(event.target.value as Platform)}>\n {PLATFORMS.map(item => <option key={item} value={item}>{ctx.t(`remote.platform.${item}`)}</option>)}\n </select>\n </label>\n <label className=\"da-field\">\n <span>{ctx.t(\"remote.name\")}</span>\n <Input value={name} onChange={event => setName(event.target.value)} maxLength={100} placeholder={ctx.t(\"remote.namePlaceholder\")} />\n </label>\n <div className=\"da-row\">\n <Btn primary type=\"submit\" disabled={busy || !origin}>{ctx.t(busy ? \"remote.working\" : \"remote.generate\")}</Btn>\n <Btn type=\"button\" disabled={busy} onClick={() => setAdding(false)}>{ctx.t(\"remote.cancel\")}</Btn>\n </div>\n </form>\n )}\n </Card>\n\n {pending && origin && (\n <PendingCard ctx={ctx} pending={pending} origin={origin} busy={busy}\n onPlatform={value => setPending({ ...pending, platform: value })}\n onRegenerate={() => void pair({ id: pending.deviceId, name: pending.deviceName, platform: pending.platform, agent_id: pending.baseline })}\n onClose={() => setPending(null)} />\n )}\n\n <Card>\n <CardTitle>{ctx.t(\"remote.listTitle\")}</CardTitle>\n {devices === null && <p className=\"da-muted\">{ctx.t(\"common.checking\")}</p>}\n {devices?.length === 0 && <p className=\"da-muted\">{ctx.t(\"remote.empty\")}</p>}\n {devices && devices.length > 0 && (\n <ul className=\"da-devices\">\n {devices.map(device => (\n <li key={device.id}>\n <div className=\"da-device-main\">\n <span className=\"da-device-name\">{device.name}</span>\n <span className=\"da-muted\">{ctx.t(`remote.platform.${PLATFORMS.includes(device.platform) ? device.platform : \"linux\"}`)}{device.agent_version ? ` \u00B7 toc-ocra ${device.agent_version}` : \"\"}</span>\n </div>\n <DeviceBadge ctx={ctx} device={device} />\n <div className=\"da-device-actions\">\n {(!device.paired || device.status === \"offline\" || device.status === \"not_connected\") && (\n <Btn disabled={busy || !origin} onClick={() => void pair(device)}>{ctx.t(device.paired ? \"remote.repair\" : \"remote.install\")}</Btn>\n )}\n {device.paired && (\n <label className=\"da-toggle\">\n <span>{ctx.t(\"remote.enabled\")}</span>\n <Toggle checked={!device.disabled} disabled={busy} label={ctx.t(\"remote.enabled\")} onChange={value => void setEnabled(device, value)} />\n </label>\n )}\n </div>\n </li>\n ))}\n </ul>\n )}\n <p className=\"da-muted\">{ctx.t(\"remote.disableHint\")}</p>\n <div className=\"da-step\">{ctx.t(\"remote.uninstall\")}</div>\n <Command ctx={ctx} value=\"toc-ocra service:uninstall\" />\n </Card>\n </>\n );\n}\n\nfunction PendingCard({ ctx, pending, origin, busy, onPlatform, onRegenerate, onClose }: {\n ctx: PluginContext;\n pending: Pending;\n origin: string;\n busy: boolean;\n onPlatform: (platform: Platform) => void;\n onRegenerate: () => void;\n onClose: () => void;\n}) {\n const [now, setNow] = useState(() => Math.floor(Date.now() / 1000));\n useEffect(() => {\n if (pending.matched) return;\n const timer = window.setInterval(() => setNow(Math.floor(Date.now() / 1000)), 1_000);\n return () => window.clearInterval(timer);\n }, [pending.matched]);\n const left = Math.max(0, pending.expiresAt - now);\n const expired = !pending.matched && left === 0;\n\n return (\n <Card>\n <CardTitle>{ctx.t(\"remote.pendingTitle\", { name: pending.deviceName })}</CardTitle>\n {pending.matched ? (\n <>\n <p className=\"da-text\"><Badge variant=\"ok\">{ctx.t(\"remote.matched\")}</Badge> {ctx.t(\"remote.matchedBody\", { name: pending.deviceName })}</p>\n <div className=\"da-row\"><Btn primary onClick={onClose}>{ctx.t(\"remote.done\")}</Btn></div>\n </>\n ) : (\n <>\n <p className=\"da-text\">{ctx.t(\"remote.pendingBody\")}</p>\n <div className=\"da-segment\" role=\"radiogroup\" aria-label={ctx.t(\"remote.platformLabel\")}>\n {PLATFORMS.map(item => (\n <button key={item} type=\"button\" role=\"radio\" aria-checked={pending.platform === item} className={pending.platform === item ? \"da-seg da-seg-active\" : \"da-seg\"} onClick={() => onPlatform(item)}>\n {ctx.t(`remote.platform.${item}`)}\n </button>\n ))}\n </div>\n <Command ctx={ctx} value={setupCommand(pending.platform, pending.code, origin)} />\n <ul className=\"da-hints\">\n <li>{ctx.t(\"remote.hintNode\")}</li>\n {pending.platform === \"linux\" && <li>{ctx.t(\"remote.hintLinux\")}</li>}\n {pending.platform === \"win\" && <li>{ctx.t(\"remote.hintWindows\")}</li>}\n <li>{expired ? ctx.t(\"remote.expired\") : ctx.t(\"remote.expires\", { minutes: Math.floor(left / 60), seconds: String(left % 60).padStart(2, \"0\") })}</li>\n </ul>\n <p className=\"da-muted\">{expired ? \"\" : ctx.t(\"remote.waiting\")}</p>\n <div className=\"da-row\">\n <Btn disabled={busy} onClick={onRegenerate}>{ctx.t(\"remote.regenerate\")}</Btn>\n <Btn disabled={busy} onClick={onClose}>{ctx.t(\"remote.cancel\")}</Btn>\n </div>\n </>\n )}\n </Card>\n );\n}\n", "import { useState } from \"react\";\nimport type { PluginContext } from \"@toc-starter/plugin-sdk\";\nimport { Btn } from \"@toc-starter/plugin-sdk/ui\";\nimport { LocalError } from \"./local\";\n\nexport const TOKEN_KEY = \"controlToken\";\nexport const LOCAL_URL_KEY = \"localUrl\";\n/** \"off\" after the user unbinds by hand; binding by hand turns it back on. */\nexport const AUTO_BIND_KEY = \"autoBind\";\n/** \"on\" while the user wants the desktop app to run the local agent. */\nexport const LOCAL_SERVICE_KEY = \"localService\";\n\nexport interface ServerContext { kind: \"cloud\" | \"lan\"; name: string; origin: string }\nexport interface WorkspaceContext { id: string; name: string }\n\n/** The fields of echo_admin's device view the plugin uses. */\nexport interface Device {\n id: string;\n name: string;\n platform: \"mac\" | \"win\" | \"linux\";\n location?: string;\n paired: boolean;\n status: \"not_connected\" | \"online\" | \"offline\" | \"disabled\";\n is_mine: boolean;\n /** Terminal agent credential id; only the owner sees it. Changes on every pairing. */\n agent_id?: string | null;\n terminal_online?: boolean;\n disabled?: boolean;\n agent_version?: string;\n last_seen_at?: number | null;\n}\n\nexport function errorText(ctx: PluginContext, error: unknown): string {\n if (error instanceof LocalError && error.kind === \"offline\") return ctx.t(\"error.offline\");\n return error instanceof Error ? error.message : String(error);\n}\n\nexport function Command({ ctx, value }: { ctx: PluginContext; value: string }) {\n const [copied, setCopied] = useState(false);\n return (\n <div className=\"da-command\">\n <code>{value}</code>\n <Btn onClick={() => void navigator.clipboard.writeText(value).then(() => { setCopied(true); window.setTimeout(() => setCopied(false), 1500); })}>\n {copied ? ctx.t(\"common.copied\") : ctx.t(\"common.copy\")}\n </Btn>\n </div>\n );\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["require_react", "__commonJSMin", "exports", "module", "registry", "shared", "require_ui", "__commonJSMin", "exports", "module", "registry", "shared", "require_jsx_runtime", "__commonJSMin", "exports", "module", "registry", "shared", "import_react", "import_react", "definePlugin", "plugin", "useHostContext", "ctx", "key", "listener", "import_ui", "DEFAULT_LOCAL_URL", "LocalError", "kind", "message", "code", "call", "base", "path", "init", "headers", "response", "body", "local", "token", "serverUrl", "sameServer", "left", "right", "normalize", "value", "import_react", "import_ui", "import_react", "import_ui", "import_jsx_runtime", "TOKEN_KEY", "LOCAL_URL_KEY", "AUTO_BIND_KEY", "LOCAL_SERVICE_KEY", "errorText", "ctx", "error", "LocalError", "Command", "value", "copied", "setCopied", "import_jsx_runtime", "PLATFORMS", "IDLE_POLL_MS", "WAIT_POLL_MS", "REMOTE_SERVER_KEY", "LOOPBACK", "commandServer", "value", "url", "isLoopback", "origin", "setupCommand", "platform", "code", "server", "setup", "DeviceBadge", "ctx", "device", "RemoteTab", "workspace", "localDeviceId", "devices", "setDevices", "error", "setError", "busy", "setBusy", "pending", "setPending", "adding", "setAdding", "name", "setName", "setPlatform", "serverUrl", "setServerUrl", "pendingRef", "alive", "load", "result", "mine", "waiting", "item", "reason", "errorText", "timer", "run", "action", "saveServer", "pair", "addComputer", "deviceName", "created", "setEnabled", "enabled", "event", "PendingCard", "Command", "onPlatform", "onRegenerate", "onClose", "now", "setNow", "left", "expired", "import_jsx_runtime", "POLL_MS", "AUTO_BIND_MS", "AUTO_BIND_RETRY_MS", "EXIT_PORT_IN_USE", "TAB_KEY", "iconUrl", "Icon", "localService", "ctx", "service", "isActive", "status", "hasLocalInstance", "localUrl", "local", "triggerAutoBind", "AgentPage", "server", "useHostContext", "workspace", "tab", "setTabState", "setTab", "next", "LOCAL_URL_KEY", "DEFAULT_LOCAL_URL", "token", "setToken", "TOKEN_KEY", "phase", "setPhase", "setService", "error", "setError", "busy", "setBusy", "alive", "svc", "refresh", "hosted", "active", "reason", "LocalError", "timer", "s", "run", "action", "errorText", "toggleService", "on", "LOCAL_SERVICE_KEY", "AUTO_BIND_KEY", "started", "waitForLocalService", "hostedExit", "external", "item", "RemoteTab", "LocalServiceCard", "OfflineCard", "PairCard", "code", "result", "ReadyView", "attempt", "resolve", "exit", "onToggle", "viaExternal", "portTaken", "Command", "onPair", "setCode", "valid", "event", "onStatus", "boundElsewhere", "sameServer", "unpair", "BindingBadge", "BindCard", "provider", "rebind", "devices", "setDevices", "choice", "setChoice", "name", "setName", "serverUrl", "setServerUrl", "cancelled", "device", "bind", "deviceId", "pairing", "startAutoBind", "deferredFor", "failedAt", "tick", "startLocalServiceIfEnabled", "running", "index_default", "definePlugin"]
7
7
  }
package/locales/en.json CHANGED
@@ -91,7 +91,9 @@
91
91
  "offBody": "Turn it on and the desktop app starts the device agent on this computer and binds it to the current server. No terminal needed.",
92
92
  "followsApp": "The agent follows the desktop app: it starts with the app and stops when the app quits (it shows offline about 90 seconds later).",
93
93
  "starting": "Starting the local device agent…",
94
- "external": "A device agent is already running on this computer (a background service or toc-ocra started in a terminal), so the plugin connects to it. The switch only controls the agent the desktop app starts.",
94
+ "onExternalBody": "Enabled: the device agent on this computer is already running (a background service or toc-ocra started in a terminal). It receives task-chain work and runs Codex, Claude and other CLIs here.",
95
+ "external": "Connected to the agent already running on this computer instead of starting another. It does not stop when the desktop app quits; to turn it off run toc-ocra service:uninstall in a terminal (or stop the toc-ocra you started).",
96
+ "portTaken": "The local service port is used by another program, so the desktop app cannot start the device agent.",
95
97
  "crashed": "The device agent stopped unexpectedly: {{error}}"
96
98
  },
97
99
  "remote": {
@@ -91,7 +91,9 @@
91
91
  "offBody": "打开后,桌面端会在这台电脑上启动设备代理,并自动绑定到当前服务器,不需要打开终端。",
92
92
  "followsApp": "设备代理跟随桌面端运行:打开桌面端时自动启动,退出桌面端即停止(停止后约 90 秒显示离线)。",
93
93
  "starting": "正在启动本机设备代理…",
94
- "external": "这台电脑上已有设备代理在运行(后台服务或终端启动的 toc-ocra),已直接连接它;开关只控制由桌面端启动的设备代理。",
94
+ "onExternalBody": "已启用:这台电脑上的设备代理已在运行(后台服务或终端启动的 toc-ocra),会接收任务链派发的任务,并在本机运行 Codex、Claude 等 CLI。",
95
+ "external": "已直接连接这台电脑上正在运行的设备代理,不再另外启动。它不随桌面端退出而停止;要停用请在终端运行 toc-ocra service:uninstall(或停止你启动的 toc-ocra)。",
96
+ "portTaken": "本机服务端口被其他程序占用,桌面端无法启动设备代理。",
95
97
  "crashed": "设备代理意外退出:{{error}}"
96
98
  },
97
99
  "remote": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toc-ocra",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Echo device agent: local CLI execution service plus its desktop plugin (heartbeat + long-poll, headless CLIs)",
5
5
  "type": "module",
6
6
  "bin": {