@fengye404/termpilot 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +58 -168
  2. package/dist/cli.js +432 -59
  3. package/docs/.vitepress/config.mts +64 -0
  4. package/docs/.vitepress/dist/404.html +23 -0
  5. package/docs/.vitepress/dist/README.html +26 -0
  6. package/docs/.vitepress/dist/architecture.html +42 -0
  7. package/docs/.vitepress/dist/assets/README.md.B4-OJVNQ.js +1 -0
  8. package/docs/.vitepress/dist/assets/README.md.B4-OJVNQ.lean.js +1 -0
  9. package/docs/.vitepress/dist/assets/app.BG4pRgiG.js +1 -0
  10. package/docs/.vitepress/dist/assets/architecture.md.JnC1zyYV.js +17 -0
  11. package/docs/.vitepress/dist/assets/architecture.md.JnC1zyYV.lean.js +1 -0
  12. package/docs/.vitepress/dist/assets/chunks/@localSearchIndexroot.l5vdUGaZ.js +1 -0
  13. package/docs/.vitepress/dist/assets/chunks/VPLocalSearchBox.DMeTzGam.js +9 -0
  14. package/docs/.vitepress/dist/assets/chunks/framework.BZohXCq9.js +19 -0
  15. package/docs/.vitepress/dist/assets/chunks/theme.D0_6rd9F.js +2 -0
  16. package/docs/.vitepress/dist/assets/development.md.iwUVjeO6.js +7 -0
  17. package/docs/.vitepress/dist/assets/development.md.iwUVjeO6.lean.js +1 -0
  18. package/docs/.vitepress/dist/assets/getting-started.md.Cirp1CHi.js +1 -0
  19. package/docs/.vitepress/dist/assets/getting-started.md.Cirp1CHi.lean.js +1 -0
  20. package/docs/.vitepress/dist/assets/index.md.D9XElNRh.js +1 -0
  21. package/docs/.vitepress/dist/assets/index.md.D9XElNRh.lean.js +1 -0
  22. package/docs/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  23. package/docs/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  24. package/docs/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  25. package/docs/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  26. package/docs/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  27. package/docs/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  28. package/docs/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  29. package/docs/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  30. package/docs/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  31. package/docs/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  32. package/docs/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  33. package/docs/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  34. package/docs/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  35. package/docs/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  36. package/docs/.vitepress/dist/assets/operations-guide.md.DfNiIg5F.js +18 -0
  37. package/docs/.vitepress/dist/assets/operations-guide.md.DfNiIg5F.lean.js +1 -0
  38. package/docs/.vitepress/dist/assets/protocol.md.CCXFJUPR.js +40 -0
  39. package/docs/.vitepress/dist/assets/protocol.md.CCXFJUPR.lean.js +1 -0
  40. package/docs/.vitepress/dist/assets/style.B0lvUXq1.css +1 -0
  41. package/docs/.vitepress/dist/assets/tech-selection-2026.md.Dk_ymWTx.js +1 -0
  42. package/docs/.vitepress/dist/assets/tech-selection-2026.md.Dk_ymWTx.lean.js +1 -0
  43. package/docs/.vitepress/dist/development.html +32 -0
  44. package/docs/.vitepress/dist/favicon.svg +6 -0
  45. package/docs/.vitepress/dist/getting-started.html +26 -0
  46. package/docs/.vitepress/dist/hashmap.json +1 -0
  47. package/docs/.vitepress/dist/index.html +26 -0
  48. package/docs/.vitepress/dist/operations-guide.html +43 -0
  49. package/docs/.vitepress/dist/protocol.html +65 -0
  50. package/docs/.vitepress/dist/tech-selection-2026.html +26 -0
  51. package/docs/.vitepress/dist/vp-icons.css +1 -0
  52. package/docs/.vitepress/theme/custom.css +42 -0
  53. package/docs/.vitepress/theme/index.ts +10 -0
  54. package/docs/README.md +10 -6
  55. package/docs/architecture.md +10 -7
  56. package/docs/getting-started.md +136 -0
  57. package/docs/index.md +57 -0
  58. package/docs/operations-guide.md +445 -0
  59. package/docs/public/favicon.svg +6 -0
  60. package/package.json +6 -2
@@ -0,0 +1,2 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/chunks/VPLocalSearchBox.DMeTzGam.js","assets/chunks/framework.BZohXCq9.js"])))=>i.map(i=>d[i]);
2
+ import{d as p,c as u,r as c,n as N,o as s,a as j,t as M,b as _,w as h,T as ue,e as m,_ as g,u as Ce,i as He,f as Be,g as de,h as y,j as d,k as i,l as z,m as se,p as S,q as F,s as X,v as U,x as ve,y as fe,z as Ee,A as Fe,F as x,B as A,C as W,D as Y,E as k,G as $e,H as B,I as ye,J as Q,K as G,L as Z,M as De,N as Pe,O as ie,P as Le,Q as Ve,R as ee,S as Oe,U as Ge,V as Ue,W as Se,X as Te,Y as je,Z as ze,$ as We,a0 as Ke,a1 as qe}from"./framework.BZohXCq9.js";const Re=p({__name:"VPBadge",props:{text:{},type:{default:"tip"}},setup(e){return(t,n)=>(s(),u("span",{class:N(["VPBadge",e.type])},[c(t.$slots,"default",{},()=>[j(M(e.text),1)])],2))}}),Je={key:0,class:"VPBackdrop"},Xe=p({__name:"VPBackdrop",props:{show:{type:Boolean}},setup(e){return(t,n)=>(s(),_(ue,{name:"fade"},{default:h(()=>[e.show?(s(),u("div",Je)):m("",!0)]),_:1}))}}),Ye=g(Xe,[["__scopeId","data-v-62bbbb3a"]]),L=Ce;function Qe(e,t){let n,a=!1;return()=>{n&&clearTimeout(n),a?n=setTimeout(e,t):(e(),(a=!0)&&setTimeout(()=>a=!1,t))}}function re(e){return e.startsWith("/")?e:`/${e}`}function he(e){const{pathname:t,search:n,hash:a,protocol:o}=new URL(e,"http://a.com");if(He(e)||e.startsWith("#")||!o.startsWith("http")||!Be(t))return e;const{site:r}=L(),l=t.endsWith("/")||t.endsWith(".html")?e:e.replace(/(?:(^\.+)\/)?.*$/,`$1${t.replace(/(\.md)?$/,r.value.cleanUrls?"":".html")}${n}${a}`);return de(l)}function q({correspondingLink:e=!1}={}){const{site:t,localeIndex:n,page:a,theme:o,hash:r}=L(),l=y(()=>{var f,$;return{label:(f=t.value.locales[n.value])==null?void 0:f.label,link:(($=t.value.locales[n.value])==null?void 0:$.link)||(n.value==="root"?"/":`/${n.value}/`)}});return{localeLinks:y(()=>Object.entries(t.value.locales).flatMap(([f,$])=>l.value.label===$.label?[]:{text:$.label,link:Ze($.link||(f==="root"?"/":`/${f}/`),o.value.i18nRouting!==!1&&e,a.value.relativePath.slice(l.value.link.length-1),!t.value.cleanUrls)+r.value})),currentLang:l}}function Ze(e,t,n,a){return t?e.replace(/\/$/,"")+re(n.replace(/(^|\/)index\.md$/,"$1").replace(/\.md$/,a?".html":"")):e}const et={class:"NotFound"},tt={class:"code"},nt={class:"title"},at={class:"quote"},ot={class:"action"},st=["href","aria-label"],it=p({__name:"NotFound",setup(e){const{theme:t}=L(),{currentLang:n}=q();return(a,o)=>{var r,l,v,f,$;return s(),u("div",et,[d("p",tt,M(((r=i(t).notFound)==null?void 0:r.code)??"404"),1),d("h1",nt,M(((l=i(t).notFound)==null?void 0:l.title)??"PAGE NOT FOUND"),1),o[0]||(o[0]=d("div",{class:"divider"},null,-1)),d("blockquote",at,M(((v=i(t).notFound)==null?void 0:v.quote)??"But if you don't change your direction, and if you keep looking, you may end up where you are heading."),1),d("div",ot,[d("a",{class:"link",href:i(de)(i(n).link),"aria-label":((f=i(t).notFound)==null?void 0:f.linkLabel)??"go to home"},M((($=i(t).notFound)==null?void 0:$.linkText)??"Take me home"),9,st)])])}}}),rt=g(it,[["__scopeId","data-v-b82097f8"]]);function Ne(e,t){if(Array.isArray(e))return R(e);if(e==null)return[];t=re(t);const n=Object.keys(e).sort((o,r)=>r.split("/").length-o.split("/").length).find(o=>t.startsWith(re(o))),a=n?e[n]:[];return Array.isArray(a)?R(a):R(a.items,a.base)}function lt(e){const t=[];let n=0;for(const a in e){const o=e[a];if(o.items){n=t.push(o);continue}t[n]||t.push({items:[]}),t[n].items.push(o)}return t}function ct(e){const t=[];function n(a){for(const o of a)o.text&&o.link&&t.push({text:o.text,link:o.link,docFooterText:o.docFooterText}),o.items&&n(o.items)}return n(e),t}function le(e,t){return Array.isArray(t)?t.some(n=>le(e,n)):z(e,t.link)?!0:t.items?le(e,t.items):!1}function R(e,t){return[...e].map(n=>{const a={...n},o=a.base||t;return o&&a.link&&(a.link=o+a.link),a.items&&(a.items=R(a.items,o)),a})}function D(){const{frontmatter:e,page:t,theme:n}=L(),a=se("(min-width: 960px)"),o=S(!1),r=y(()=>{const w=n.value.sidebar,C=t.value.relativePath;return w?Ne(w,C):[]}),l=S(r.value);F(r,(w,C)=>{JSON.stringify(w)!==JSON.stringify(C)&&(l.value=r.value)});const v=y(()=>e.value.sidebar!==!1&&l.value.length>0&&e.value.layout!=="home"),f=y(()=>$?e.value.aside==null?n.value.aside==="left":e.value.aside==="left":!1),$=y(()=>e.value.layout==="home"?!1:e.value.aside!=null?!!e.value.aside:n.value.aside!==!1),V=y(()=>v.value&&a.value),b=y(()=>v.value?lt(l.value):[]);function P(){o.value=!0}function T(){o.value=!1}function I(){o.value?T():P()}return{isOpen:o,sidebar:l,sidebarGroups:b,hasSidebar:v,hasAside:$,leftAside:f,isSidebarEnabled:V,open:P,close:T,toggle:I}}function ut(e,t){let n;X(()=>{n=e.value?document.activeElement:void 0}),U(()=>{window.addEventListener("keyup",a)}),ve(()=>{window.removeEventListener("keyup",a)});function a(o){o.key==="Escape"&&e.value&&(t(),n==null||n.focus())}}function dt(e){const{page:t,hash:n}=L(),a=S(!1),o=y(()=>e.value.collapsed!=null),r=y(()=>!!e.value.link),l=S(!1),v=()=>{l.value=z(t.value.relativePath,e.value.link)};F([t,e,n],v),U(v);const f=y(()=>l.value?!0:e.value.items?le(t.value.relativePath,e.value.items):!1),$=y(()=>!!(e.value.items&&e.value.items.length));X(()=>{a.value=!!(o.value&&e.value.collapsed)}),fe(()=>{(l.value||f.value)&&(a.value=!1)});function V(){o.value&&(a.value=!a.value)}return{collapsed:a,collapsible:o,isLink:r,isActiveLink:l,hasActiveLink:f,hasChildren:$,toggle:V}}function vt(){const{hasSidebar:e}=D(),t=se("(min-width: 960px)"),n=se("(min-width: 1280px)");return{isAsideEnabled:y(()=>!n.value&&!t.value?!1:e.value?n.value:t.value)}}const ft=/\b(?:VPBadge|header-anchor|footnote-ref|ignore-header)\b/,ce=[];function Me(e){return typeof e.outline=="object"&&!Array.isArray(e.outline)&&e.outline.label||e.outlineTitle||"On this page"}function me(e){const t=[...document.querySelectorAll(".VPDoc :where(h1,h2,h3,h4,h5,h6)")].filter(n=>n.id&&n.hasChildNodes()).map(n=>{const a=Number(n.tagName[1]);return{element:n,title:ht(n),link:"#"+n.id,level:a}});return mt(t,e)}function ht(e){let t="";for(const n of e.childNodes)if(n.nodeType===1){if(ft.test(n.className))continue;t+=n.textContent}else n.nodeType===3&&(t+=n.textContent);return t.trim()}function mt(e,t){if(t===!1)return[];const n=(typeof t=="object"&&!Array.isArray(t)?t.level:t)||2,[a,o]=typeof n=="number"?[n,n]:n==="deep"?[2,6]:n;return _t(e,a,o)}function pt(e,t){const{isAsideEnabled:n}=vt(),a=Qe(r,100);let o=null;U(()=>{requestAnimationFrame(r),window.addEventListener("scroll",a)}),Ee(()=>{l(location.hash)}),ve(()=>{window.removeEventListener("scroll",a)});function r(){if(!n.value)return;const v=window.scrollY,f=window.innerHeight,$=document.body.offsetHeight,V=Math.abs(v+f-$)<1,b=ce.map(({element:T,link:I})=>({link:I,top:kt(T)})).filter(({top:T})=>!Number.isNaN(T)).sort((T,I)=>T.top-I.top);if(!b.length){l(null);return}if(v<1){l(null);return}if(V){l(b[b.length-1].link);return}let P=null;for(const{link:T,top:I}of b){if(I>v+Fe()+4)break;P=T}l(P)}function l(v){o&&o.classList.remove("active"),v==null?o=null:o=e.value.querySelector(`a[href="${decodeURIComponent(v)}"]`);const f=o;f?(f.classList.add("active"),t.value.style.top=f.offsetTop+39+"px",t.value.style.opacity="1"):(t.value.style.top="33px",t.value.style.opacity="0")}}function kt(e){let t=0;for(;e!==document.body;){if(e===null)return NaN;t+=e.offsetTop,e=e.offsetParent}return t}function _t(e,t,n){ce.length=0;const a=[],o=[];return e.forEach(r=>{const l={...r,children:[]};let v=o[o.length-1];for(;v&&v.level>=l.level;)o.pop(),v=o[o.length-1];if(l.element.classList.contains("ignore-header")||v&&"shouldIgnore"in v){o.push({level:l.level,shouldIgnore:!0});return}l.level>n||l.level<t||(ce.push({element:l.element,link:l.link}),v?v.children.push(l):a.push(l),o.push(l))}),a}const bt=["href","title"],gt=p({__name:"VPDocOutlineItem",props:{headers:{},root:{type:Boolean}},setup(e){function t({target:n}){const a=n.href.split("#")[1],o=document.getElementById(decodeURIComponent(a));o==null||o.focus({preventScroll:!0})}return(n,a)=>{const o=W("VPDocOutlineItem",!0);return s(),u("ul",{class:N(["VPDocOutlineItem",e.root?"root":"nested"])},[(s(!0),u(x,null,A(e.headers,({children:r,link:l,title:v})=>(s(),u("li",null,[d("a",{class:"outline-link",href:l,onClick:t,title:v},M(v),9,bt),r!=null&&r.length?(s(),_(o,{key:0,headers:r},null,8,["headers"])):m("",!0)]))),256))],2)}}}),xe=g(gt,[["__scopeId","data-v-87a812ef"]]),$t={class:"content"},yt={"aria-level":"2",class:"outline-title",id:"doc-outline-aria-label",role:"heading"},Pt=p({__name:"VPDocAsideOutline",setup(e){const{frontmatter:t,theme:n}=L(),a=$e([]);Y(()=>{a.value=me(t.value.outline??n.value.outline)});const o=S(),r=S();return pt(o,r),(l,v)=>(s(),u("nav",{"aria-labelledby":"doc-outline-aria-label",class:N(["VPDocAsideOutline",{"has-outline":a.value.length>0}]),ref_key:"container",ref:o},[d("div",$t,[d("div",{class:"outline-marker",ref_key:"marker",ref:r},null,512),d("div",yt,M(i(Me)(i(n))),1),k(xe,{headers:a.value,root:!0},null,8,["headers"])])],2))}}),Lt=g(Pt,[["__scopeId","data-v-3d03f0c2"]]),Vt={class:"VPDocAsideCarbonAds"},St=p({__name:"VPDocAsideCarbonAds",props:{carbonAds:{}},setup(e){const t=()=>null;return(n,a)=>(s(),u("div",Vt,[k(i(t),{"carbon-ads":e.carbonAds},null,8,["carbon-ads"])]))}}),Tt={class:"VPDocAside"},Nt=p({__name:"VPDocAside",setup(e){const{theme:t}=L();return(n,a)=>(s(),u("div",Tt,[c(n.$slots,"aside-top",{},void 0,!0),c(n.$slots,"aside-outline-before",{},void 0,!0),k(Lt),c(n.$slots,"aside-outline-after",{},void 0,!0),a[0]||(a[0]=d("div",{class:"spacer"},null,-1)),c(n.$slots,"aside-ads-before",{},void 0,!0),i(t).carbonAds?(s(),_(St,{key:0,"carbon-ads":i(t).carbonAds},null,8,["carbon-ads"])):m("",!0),c(n.$slots,"aside-ads-after",{},void 0,!0),c(n.$slots,"aside-bottom",{},void 0,!0)]))}}),Mt=g(Nt,[["__scopeId","data-v-2bdf3a15"]]);function xt(){const{theme:e,page:t}=L();return y(()=>{const{text:n="Edit this page",pattern:a=""}=e.value.editLink||{};let o;return typeof a=="function"?o=a(t.value):o=a.replace(/:path/g,t.value.filePath),{url:o,text:n}})}function It(){const{page:e,theme:t,frontmatter:n}=L();return y(()=>{var $,V,b,P,T,I,w,C;const a=Ne(t.value.sidebar,e.value.relativePath),o=ct(a),r=wt(o,H=>H.link.replace(/[?#].*$/,"")),l=r.findIndex(H=>z(e.value.relativePath,H.link)),v=(($=t.value.docFooter)==null?void 0:$.prev)===!1&&!n.value.prev||n.value.prev===!1,f=((V=t.value.docFooter)==null?void 0:V.next)===!1&&!n.value.next||n.value.next===!1;return{prev:v?void 0:{text:(typeof n.value.prev=="string"?n.value.prev:typeof n.value.prev=="object"?n.value.prev.text:void 0)??((b=r[l-1])==null?void 0:b.docFooterText)??((P=r[l-1])==null?void 0:P.text),link:(typeof n.value.prev=="object"?n.value.prev.link:void 0)??((T=r[l-1])==null?void 0:T.link)},next:f?void 0:{text:(typeof n.value.next=="string"?n.value.next:typeof n.value.next=="object"?n.value.next.text:void 0)??((I=r[l+1])==null?void 0:I.docFooterText)??((w=r[l+1])==null?void 0:w.text),link:(typeof n.value.next=="object"?n.value.next.link:void 0)??((C=r[l+1])==null?void 0:C.link)}}})}function wt(e,t){const n=new Set;return e.filter(a=>{const o=t(a);return n.has(o)?!1:n.add(o)})}const E=p({__name:"VPLink",props:{tag:{},href:{},noIcon:{type:Boolean},target:{},rel:{}},setup(e){const t=e,n=y(()=>t.tag??(t.href?"a":"span")),a=y(()=>t.href&&ye.test(t.href)||t.target==="_blank");return(o,r)=>(s(),_(B(n.value),{class:N(["VPLink",{link:e.href,"vp-external-link-icon":a.value,"no-icon":e.noIcon}]),href:e.href?i(he)(e.href):void 0,target:e.target??(a.value?"_blank":void 0),rel:e.rel??(a.value?"noreferrer":void 0)},{default:h(()=>[c(o.$slots,"default")]),_:3},8,["class","href","target","rel"]))}}),At={class:"VPLastUpdated"},Ct=["datetime"],Ht=p({__name:"VPDocFooterLastUpdated",setup(e){const{theme:t,page:n,lang:a}=L(),o=y(()=>new Date(n.value.lastUpdated)),r=y(()=>o.value.toISOString()),l=S("");return U(()=>{X(()=>{var v,f,$;l.value=new Intl.DateTimeFormat((f=(v=t.value.lastUpdated)==null?void 0:v.formatOptions)!=null&&f.forceLocale?a.value:void 0,(($=t.value.lastUpdated)==null?void 0:$.formatOptions)??{dateStyle:"short",timeStyle:"short"}).format(o.value)})}),(v,f)=>{var $;return s(),u("p",At,[j(M((($=i(t).lastUpdated)==null?void 0:$.text)||i(t).lastUpdatedText||"Last updated")+": ",1),d("time",{datetime:r.value},M(l.value),9,Ct)])}}}),Bt=g(Ht,[["__scopeId","data-v-33a4642e"]]),Et={key:0,class:"VPDocFooter"},Ft={key:0,class:"edit-info"},Dt={key:0,class:"edit-link"},Ot={key:1,class:"last-updated"},Gt={key:1,class:"prev-next","aria-labelledby":"doc-footer-aria-label"},Ut={class:"pager"},jt=["innerHTML"],zt=["innerHTML"],Wt={class:"pager"},Kt=["innerHTML"],qt=["innerHTML"],Rt=p({__name:"VPDocFooter",setup(e){const{theme:t,page:n,frontmatter:a}=L(),o=xt(),r=It(),l=y(()=>t.value.editLink&&a.value.editLink!==!1),v=y(()=>n.value.lastUpdated),f=y(()=>l.value||v.value||r.value.prev||r.value.next);return($,V)=>{var b,P,T,I;return f.value?(s(),u("footer",Et,[c($.$slots,"doc-footer-before",{},void 0,!0),l.value||v.value?(s(),u("div",Ft,[l.value?(s(),u("div",Dt,[k(E,{class:"edit-link-button",href:i(o).url,"no-icon":!0},{default:h(()=>[V[0]||(V[0]=d("span",{class:"vpi-square-pen edit-link-icon"},null,-1)),j(" "+M(i(o).text),1)]),_:1},8,["href"])])):m("",!0),v.value?(s(),u("div",Ot,[k(Bt)])):m("",!0)])):m("",!0),(b=i(r).prev)!=null&&b.link||(P=i(r).next)!=null&&P.link?(s(),u("nav",Gt,[V[1]||(V[1]=d("span",{class:"visually-hidden",id:"doc-footer-aria-label"},"Pager",-1)),d("div",Ut,[(T=i(r).prev)!=null&&T.link?(s(),_(E,{key:0,class:"pager-link prev",href:i(r).prev.link},{default:h(()=>{var w;return[d("span",{class:"desc",innerHTML:((w=i(t).docFooter)==null?void 0:w.prev)||"Previous page"},null,8,jt),d("span",{class:"title",innerHTML:i(r).prev.text},null,8,zt)]}),_:1},8,["href"])):m("",!0)]),d("div",Wt,[(I=i(r).next)!=null&&I.link?(s(),_(E,{key:0,class:"pager-link next",href:i(r).next.link},{default:h(()=>{var w;return[d("span",{class:"desc",innerHTML:((w=i(t).docFooter)==null?void 0:w.next)||"Next page"},null,8,Kt),d("span",{class:"title",innerHTML:i(r).next.text},null,8,qt)]}),_:1},8,["href"])):m("",!0)])])):m("",!0)])):m("",!0)}}}),Jt=g(Rt,[["__scopeId","data-v-b170fa4f"]]),Xt={class:"container"},Yt={class:"aside-container"},Qt={class:"aside-content"},Zt={class:"content"},en={class:"content-container"},tn={class:"main"},nn=p({__name:"VPDoc",setup(e){const{theme:t}=L(),n=Q(),{hasSidebar:a,hasAside:o,leftAside:r}=D(),l=y(()=>n.path.replace(/[./]+/g,"_").replace(/_html$/,""));return(v,f)=>{const $=W("Content");return s(),u("div",{class:N(["VPDoc",{"has-sidebar":i(a),"has-aside":i(o)}])},[c(v.$slots,"doc-top",{},void 0,!0),d("div",Xt,[i(o)?(s(),u("div",{key:0,class:N(["aside",{"left-aside":i(r)}])},[f[0]||(f[0]=d("div",{class:"aside-curtain"},null,-1)),d("div",Yt,[d("div",Qt,[k(Mt,null,{"aside-top":h(()=>[c(v.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":h(()=>[c(v.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":h(()=>[c(v.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":h(()=>[c(v.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":h(()=>[c(v.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":h(()=>[c(v.$slots,"aside-ads-after",{},void 0,!0)]),_:3})])])],2)):m("",!0),d("div",Zt,[d("div",en,[c(v.$slots,"doc-before",{},void 0,!0),d("main",tn,[k($,{class:N(["vp-doc",[l.value,i(t).externalLinkIcon&&"external-link-icon-enabled"]])},null,8,["class"])]),k(Jt,null,{"doc-footer-before":h(()=>[c(v.$slots,"doc-footer-before",{},void 0,!0)]),_:3}),c(v.$slots,"doc-after",{},void 0,!0)])])]),c(v.$slots,"doc-bottom",{},void 0,!0)],2)}}}),an=g(nn,[["__scopeId","data-v-1870bdbd"]]),on=p({__name:"VPButton",props:{tag:{},size:{default:"medium"},theme:{default:"brand"},text:{},href:{},target:{},rel:{}},setup(e){const t=e,n=y(()=>t.href&&ye.test(t.href)),a=y(()=>t.tag||(t.href?"a":"button"));return(o,r)=>(s(),_(B(a.value),{class:N(["VPButton",[e.size,e.theme]]),href:e.href?i(he)(e.href):void 0,target:t.target??(n.value?"_blank":void 0),rel:t.rel??(n.value?"noreferrer":void 0)},{default:h(()=>[j(M(e.text),1)]),_:1},8,["class","href","target","rel"]))}}),sn=g(on,[["__scopeId","data-v-e4597ec5"]]),rn=["src","alt"],ln=p({inheritAttrs:!1,__name:"VPImage",props:{image:{},alt:{}},setup(e){return(t,n)=>{const a=W("VPImage",!0);return e.image?(s(),u(x,{key:0},[typeof e.image=="string"||"src"in e.image?(s(),u("img",G({key:0,class:"VPImage"},typeof e.image=="string"?t.$attrs:{...e.image,...t.$attrs},{src:i(de)(typeof e.image=="string"?e.image:e.image.src),alt:e.alt??(typeof e.image=="string"?"":e.image.alt||"")}),null,16,rn)):(s(),u(x,{key:1},[k(a,G({class:"dark",image:e.image.dark,alt:e.image.alt},t.$attrs),null,16,["image","alt"]),k(a,G({class:"light",image:e.image.light,alt:e.image.alt},t.$attrs),null,16,["image","alt"])],64))],64)):m("",!0)}}}),J=g(ln,[["__scopeId","data-v-105fdaa6"]]),cn={class:"container"},un={class:"main"},dn={class:"heading"},vn=["innerHTML"],fn=["innerHTML"],hn=["innerHTML"],mn={key:0,class:"actions"},pn={key:0,class:"image"},kn={class:"image-container"},_n=p({__name:"VPHero",props:{name:{},text:{},tagline:{},image:{},actions:{}},setup(e){const t=Z("hero-image-slot-exists");return(n,a)=>(s(),u("div",{class:N(["VPHero",{"has-image":e.image||i(t)}])},[d("div",cn,[d("div",un,[c(n.$slots,"home-hero-info-before",{},void 0,!0),c(n.$slots,"home-hero-info",{},()=>[d("h1",dn,[e.name?(s(),u("span",{key:0,innerHTML:e.name,class:"name clip"},null,8,vn)):m("",!0),e.text?(s(),u("span",{key:1,innerHTML:e.text,class:"text"},null,8,fn)):m("",!0)]),e.tagline?(s(),u("p",{key:0,innerHTML:e.tagline,class:"tagline"},null,8,hn)):m("",!0)],!0),c(n.$slots,"home-hero-info-after",{},void 0,!0),e.actions?(s(),u("div",mn,[(s(!0),u(x,null,A(e.actions,o=>(s(),u("div",{key:o.link,class:"action"},[k(sn,{tag:"a",size:"medium",theme:o.theme,text:o.text,href:o.link,target:o.target,rel:o.rel},null,8,["theme","text","href","target","rel"])]))),128))])):m("",!0),c(n.$slots,"home-hero-actions-after",{},void 0,!0)]),e.image||i(t)?(s(),u("div",pn,[d("div",kn,[a[0]||(a[0]=d("div",{class:"image-bg"},null,-1)),c(n.$slots,"home-hero-image",{},()=>[e.image?(s(),_(J,{key:0,class:"image-src",image:e.image},null,8,["image"])):m("",!0)],!0)])])):m("",!0)])],2))}}),bn=g(_n,[["__scopeId","data-v-f735660c"]]),gn=p({__name:"VPHomeHero",setup(e){const{frontmatter:t}=L();return(n,a)=>i(t).hero?(s(),_(bn,{key:0,class:"VPHomeHero",name:i(t).hero.name,text:i(t).hero.text,tagline:i(t).hero.tagline,image:i(t).hero.image,actions:i(t).hero.actions},{"home-hero-info-before":h(()=>[c(n.$slots,"home-hero-info-before")]),"home-hero-info":h(()=>[c(n.$slots,"home-hero-info")]),"home-hero-info-after":h(()=>[c(n.$slots,"home-hero-info-after")]),"home-hero-actions-after":h(()=>[c(n.$slots,"home-hero-actions-after")]),"home-hero-image":h(()=>[c(n.$slots,"home-hero-image")]),_:3},8,["name","text","tagline","image","actions"])):m("",!0)}}),$n={class:"box"},yn={key:0,class:"icon"},Pn=["innerHTML"],Ln=["innerHTML"],Vn=["innerHTML"],Sn={key:4,class:"link-text"},Tn={class:"link-text-value"},Nn=p({__name:"VPFeature",props:{icon:{},title:{},details:{},link:{},linkText:{},rel:{},target:{}},setup(e){return(t,n)=>(s(),_(E,{class:"VPFeature",href:e.link,rel:e.rel,target:e.target,"no-icon":!0,tag:e.link?"a":"div"},{default:h(()=>[d("article",$n,[typeof e.icon=="object"&&e.icon.wrap?(s(),u("div",yn,[k(J,{image:e.icon,alt:e.icon.alt,height:e.icon.height||48,width:e.icon.width||48},null,8,["image","alt","height","width"])])):typeof e.icon=="object"?(s(),_(J,{key:1,image:e.icon,alt:e.icon.alt,height:e.icon.height||48,width:e.icon.width||48},null,8,["image","alt","height","width"])):e.icon?(s(),u("div",{key:2,class:"icon",innerHTML:e.icon},null,8,Pn)):m("",!0),d("h2",{class:"title",innerHTML:e.title},null,8,Ln),e.details?(s(),u("p",{key:3,class:"details",innerHTML:e.details},null,8,Vn)):m("",!0),e.linkText?(s(),u("div",Sn,[d("p",Tn,[j(M(e.linkText)+" ",1),n[0]||(n[0]=d("span",{class:"vpi-arrow-right link-text-icon"},null,-1))])])):m("",!0)])]),_:1},8,["href","rel","target","tag"]))}}),Mn=g(Nn,[["__scopeId","data-v-b01aa713"]]),xn={key:0,class:"VPFeatures"},In={class:"container"},wn={class:"items"},An=p({__name:"VPFeatures",props:{features:{}},setup(e){const t=e,n=y(()=>{const a=t.features.length;if(a){if(a===2)return"grid-2";if(a===3)return"grid-3";if(a%3===0)return"grid-6";if(a>3)return"grid-4"}else return});return(a,o)=>e.features?(s(),u("div",xn,[d("div",In,[d("div",wn,[(s(!0),u(x,null,A(e.features,r=>(s(),u("div",{key:r.title,class:N(["item",[n.value]])},[k(Mn,{icon:r.icon,title:r.title,details:r.details,link:r.link,"link-text":r.linkText,rel:r.rel,target:r.target},null,8,["icon","title","details","link","link-text","rel","target"])],2))),128))])])])):m("",!0)}}),Cn=g(An,[["__scopeId","data-v-65135a9f"]]),Hn=p({__name:"VPHomeFeatures",setup(e){const{frontmatter:t}=L();return(n,a)=>i(t).features?(s(),_(Cn,{key:0,class:"VPHomeFeatures",features:i(t).features},null,8,["features"])):m("",!0)}}),Bn=p({__name:"VPHomeContent",setup(e){const{width:t}=De({initialWidth:0,includeScrollbar:!1});return(n,a)=>(s(),u("div",{class:"vp-doc container",style:Pe(i(t)?{"--vp-offset":`calc(50% - ${i(t)/2}px)`}:{})},[c(n.$slots,"default",{},void 0,!0)],4))}}),En=g(Bn,[["__scopeId","data-v-3b12a21d"]]),Fn=p({__name:"VPHome",setup(e){const{frontmatter:t,theme:n}=L();return(a,o)=>{const r=W("Content");return s(),u("div",{class:N(["VPHome",{"external-link-icon-enabled":i(n).externalLinkIcon}])},[c(a.$slots,"home-hero-before",{},void 0,!0),k(gn,null,{"home-hero-info-before":h(()=>[c(a.$slots,"home-hero-info-before",{},void 0,!0)]),"home-hero-info":h(()=>[c(a.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-info-after":h(()=>[c(a.$slots,"home-hero-info-after",{},void 0,!0)]),"home-hero-actions-after":h(()=>[c(a.$slots,"home-hero-actions-after",{},void 0,!0)]),"home-hero-image":h(()=>[c(a.$slots,"home-hero-image",{},void 0,!0)]),_:3}),c(a.$slots,"home-hero-after",{},void 0,!0),c(a.$slots,"home-features-before",{},void 0,!0),k(Hn),c(a.$slots,"home-features-after",{},void 0,!0),i(t).markdownStyles!==!1?(s(),_(En,{key:0},{default:h(()=>[k(r)]),_:1})):(s(),_(r,{key:1}))],2)}}}),Dn=g(Fn,[["__scopeId","data-v-79fcb04a"]]),On={},Gn={class:"VPPage"};function Un(e,t){const n=W("Content");return s(),u("div",Gn,[c(e.$slots,"page-top"),k(n),c(e.$slots,"page-bottom")])}const jn=g(On,[["render",Un]]),zn=p({__name:"VPContent",setup(e){const{page:t,frontmatter:n}=L(),{hasSidebar:a}=D();return(o,r)=>(s(),u("div",{class:N(["VPContent",{"has-sidebar":i(a),"is-home":i(n).layout==="home"}]),id:"VPContent"},[i(t).isNotFound?c(o.$slots,"not-found",{key:0},()=>[k(rt)],!0):i(n).layout==="page"?(s(),_(jn,{key:1},{"page-top":h(()=>[c(o.$slots,"page-top",{},void 0,!0)]),"page-bottom":h(()=>[c(o.$slots,"page-bottom",{},void 0,!0)]),_:3})):i(n).layout==="home"?(s(),_(Dn,{key:2},{"home-hero-before":h(()=>[c(o.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info-before":h(()=>[c(o.$slots,"home-hero-info-before",{},void 0,!0)]),"home-hero-info":h(()=>[c(o.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-info-after":h(()=>[c(o.$slots,"home-hero-info-after",{},void 0,!0)]),"home-hero-actions-after":h(()=>[c(o.$slots,"home-hero-actions-after",{},void 0,!0)]),"home-hero-image":h(()=>[c(o.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":h(()=>[c(o.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":h(()=>[c(o.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":h(()=>[c(o.$slots,"home-features-after",{},void 0,!0)]),_:3})):i(n).layout&&i(n).layout!=="doc"?(s(),_(B(i(n).layout),{key:3})):(s(),_(an,{key:4},{"doc-top":h(()=>[c(o.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":h(()=>[c(o.$slots,"doc-bottom",{},void 0,!0)]),"doc-footer-before":h(()=>[c(o.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":h(()=>[c(o.$slots,"doc-before",{},void 0,!0)]),"doc-after":h(()=>[c(o.$slots,"doc-after",{},void 0,!0)]),"aside-top":h(()=>[c(o.$slots,"aside-top",{},void 0,!0)]),"aside-outline-before":h(()=>[c(o.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":h(()=>[c(o.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":h(()=>[c(o.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":h(()=>[c(o.$slots,"aside-ads-after",{},void 0,!0)]),"aside-bottom":h(()=>[c(o.$slots,"aside-bottom",{},void 0,!0)]),_:3}))],2))}}),Wn=g(zn,[["__scopeId","data-v-f89b4fb6"]]),Kn={class:"container"},qn=["innerHTML"],Rn=["innerHTML"],Jn=p({__name:"VPFooter",setup(e){const{theme:t,frontmatter:n}=L(),{hasSidebar:a}=D();return(o,r)=>i(t).footer&&i(n).footer!==!1?(s(),u("footer",{key:0,class:N(["VPFooter",{"has-sidebar":i(a)}])},[d("div",Kn,[i(t).footer.message?(s(),u("p",{key:0,class:"message",innerHTML:i(t).footer.message},null,8,qn)):m("",!0),i(t).footer.copyright?(s(),u("p",{key:1,class:"copyright",innerHTML:i(t).footer.copyright},null,8,Rn)):m("",!0)])],2)):m("",!0)}}),Xn=g(Jn,[["__scopeId","data-v-9a5ea44b"]]);function Yn(){const{theme:e,frontmatter:t}=L(),n=$e([]),a=y(()=>n.value.length>0);return Y(()=>{n.value=me(t.value.outline??e.value.outline)}),{headers:n,hasLocalNav:a}}const Qn={class:"menu-text"},Zn={class:"header"},ea={class:"outline"},ta=p({__name:"VPLocalNavOutlineDropdown",props:{headers:{},navHeight:{}},setup(e){const t=e,{theme:n}=L(),a=S(!1),o=S(0),r=S(),l=S();function v(b){var P;(P=r.value)!=null&&P.contains(b.target)||(a.value=!1)}F(a,b=>{if(b){document.addEventListener("click",v);return}document.removeEventListener("click",v)}),ie("Escape",()=>{a.value=!1}),Y(()=>{a.value=!1});function f(){a.value=!a.value,o.value=window.innerHeight+Math.min(window.scrollY-t.navHeight,0)}function $(b){b.target.classList.contains("outline-link")&&(l.value&&(l.value.style.transition="none"),Le(()=>{a.value=!1}))}function V(){a.value=!1,window.scrollTo({top:0,left:0,behavior:"smooth"})}return(b,P)=>(s(),u("div",{class:"VPLocalNavOutlineDropdown",style:Pe({"--vp-vh":o.value+"px"}),ref_key:"main",ref:r},[e.headers.length>0?(s(),u("button",{key:0,onClick:f,class:N({open:a.value})},[d("span",Qn,M(i(Me)(i(n))),1),P[0]||(P[0]=d("span",{class:"vpi-chevron-right icon"},null,-1))],2)):(s(),u("button",{key:1,onClick:V},M(i(n).returnToTopLabel||"Return to top"),1)),k(ue,{name:"flyout"},{default:h(()=>[a.value?(s(),u("div",{key:0,ref_key:"items",ref:l,class:"items",onClick:$},[d("div",Zn,[d("a",{class:"top-link",href:"#",onClick:V},M(i(n).returnToTopLabel||"Return to top"),1)]),d("div",ea,[k(xe,{headers:e.headers},null,8,["headers"])])],512)):m("",!0)]),_:1})],4))}}),na=g(ta,[["__scopeId","data-v-bdfacdf2"]]),aa={class:"container"},oa=["aria-expanded"],sa={class:"menu-text"},ia=p({__name:"VPLocalNav",props:{open:{type:Boolean}},emits:["open-menu"],setup(e){const{theme:t,frontmatter:n}=L(),{hasSidebar:a}=D(),{headers:o}=Yn(),{y:r}=Ve(),l=S(0);U(()=>{l.value=parseInt(getComputedStyle(document.documentElement).getPropertyValue("--vp-nav-height"))}),Y(()=>{o.value=me(n.value.outline??t.value.outline)});const v=y(()=>o.value.length===0),f=y(()=>v.value&&!a.value),$=y(()=>({VPLocalNav:!0,"has-sidebar":a.value,empty:v.value,fixed:f.value}));return(V,b)=>i(n).layout!=="home"&&(!f.value||i(r)>=l.value)?(s(),u("div",{key:0,class:N($.value)},[d("div",aa,[i(a)?(s(),u("button",{key:0,class:"menu","aria-expanded":e.open,"aria-controls":"VPSidebarNav",onClick:b[0]||(b[0]=P=>V.$emit("open-menu"))},[b[1]||(b[1]=d("span",{class:"vpi-align-left menu-icon"},null,-1)),d("span",sa,M(i(t).sidebarMenuLabel||"Menu"),1)],8,oa)):m("",!0),k(na,{headers:i(o),navHeight:l.value},null,8,["headers","navHeight"])])],2)):m("",!0)}}),ra=g(ia,[["__scopeId","data-v-18894e30"]]);function la(){const e=S(!1);function t(){e.value=!0,window.addEventListener("resize",o)}function n(){e.value=!1,window.removeEventListener("resize",o)}function a(){e.value?n():t()}function o(){window.outerWidth>=768&&n()}const r=Q();return F(()=>r.path,n),{isScreenOpen:e,openScreen:t,closeScreen:n,toggleScreen:a}}const ca={},ua={class:"VPSwitch",type:"button",role:"switch"},da={class:"check"},va={key:0,class:"icon"};function fa(e,t){return s(),u("button",ua,[d("span",da,[e.$slots.default?(s(),u("span",va,[c(e.$slots,"default",{},void 0,!0)])):m("",!0)])])}const ha=g(ca,[["render",fa],["__scopeId","data-v-b1949eaf"]]),ma=p({__name:"VPSwitchAppearance",setup(e){const{isDark:t,theme:n}=L(),a=Z("toggle-appearance",()=>{t.value=!t.value}),o=S("");return fe(()=>{o.value=t.value?n.value.lightModeSwitchTitle||"Switch to light theme":n.value.darkModeSwitchTitle||"Switch to dark theme"}),(r,l)=>(s(),_(ha,{title:o.value,class:"VPSwitchAppearance","aria-checked":i(t),onClick:i(a)},{default:h(()=>[...l[0]||(l[0]=[d("span",{class:"vpi-sun sun"},null,-1),d("span",{class:"vpi-moon moon"},null,-1)])]),_:1},8,["title","aria-checked","onClick"]))}}),pe=g(ma,[["__scopeId","data-v-9a85cf30"]]),pa={key:0,class:"VPNavBarAppearance"},ka=p({__name:"VPNavBarAppearance",setup(e){const{site:t}=L();return(n,a)=>i(t).appearance&&i(t).appearance!=="force-dark"&&i(t).appearance!=="force-auto"?(s(),u("div",pa,[k(pe)])):m("",!0)}}),_a=g(ka,[["__scopeId","data-v-d994237e"]]),ke=S();let Ie=!1,oe=0;function ba(e){const t=S(!1);if(ee){!Ie&&ga(),oe++;const n=F(ke,a=>{var o,r,l;a===e.el.value||(o=e.el.value)!=null&&o.contains(a)?(t.value=!0,(r=e.onFocus)==null||r.call(e)):(t.value=!1,(l=e.onBlur)==null||l.call(e))});ve(()=>{n(),oe--,oe||$a()})}return Oe(t)}function ga(){document.addEventListener("focusin",we),Ie=!0,ke.value=document.activeElement}function $a(){document.removeEventListener("focusin",we)}function we(){ke.value=document.activeElement}const ya={class:"VPMenuLink"},Pa=["innerHTML"],La=p({__name:"VPMenuLink",props:{item:{}},setup(e){const{page:t}=L();return(n,a)=>(s(),u("div",ya,[k(E,{class:N({active:i(z)(i(t).relativePath,e.item.activeMatch||e.item.link,!!e.item.activeMatch)}),href:e.item.link,target:e.item.target,rel:e.item.rel,"no-icon":e.item.noIcon},{default:h(()=>[d("span",{innerHTML:e.item.text},null,8,Pa)]),_:1},8,["class","href","target","rel","no-icon"])]))}}),te=g(La,[["__scopeId","data-v-ad368623"]]),Va={class:"VPMenuGroup"},Sa={key:0,class:"title"},Ta=p({__name:"VPMenuGroup",props:{text:{},items:{}},setup(e){return(t,n)=>(s(),u("div",Va,[e.text?(s(),u("p",Sa,M(e.text),1)):m("",!0),(s(!0),u(x,null,A(e.items,a=>(s(),u(x,null,["link"in a?(s(),_(te,{key:0,item:a},null,8,["item"])):m("",!0)],64))),256))]))}}),Na=g(Ta,[["__scopeId","data-v-236ce156"]]),Ma={class:"VPMenu"},xa={key:0,class:"items"},Ia=p({__name:"VPMenu",props:{items:{}},setup(e){return(t,n)=>(s(),u("div",Ma,[e.items?(s(),u("div",xa,[(s(!0),u(x,null,A(e.items,a=>(s(),u(x,{key:JSON.stringify(a)},["link"in a?(s(),_(te,{key:0,item:a},null,8,["item"])):"component"in a?(s(),_(B(a.component),G({key:1,ref_for:!0},a.props),null,16)):(s(),_(Na,{key:2,text:a.text,items:a.items},null,8,["text","items"]))],64))),128))])):m("",!0),c(t.$slots,"default",{},void 0,!0)]))}}),wa=g(Ia,[["__scopeId","data-v-52b08d90"]]),Aa=["aria-expanded","aria-label"],Ca={key:0,class:"text"},Ha=["innerHTML"],Ba={key:1,class:"vpi-more-horizontal icon"},Ea={class:"menu"},Fa=p({__name:"VPFlyout",props:{icon:{},button:{},label:{},items:{}},setup(e){const t=S(!1),n=S();ba({el:n,onBlur:a});function a(){t.value=!1}return(o,r)=>(s(),u("div",{class:"VPFlyout",ref_key:"el",ref:n,onMouseenter:r[1]||(r[1]=l=>t.value=!0),onMouseleave:r[2]||(r[2]=l=>t.value=!1)},[d("button",{type:"button",class:"button","aria-haspopup":"true","aria-expanded":t.value,"aria-label":e.label,onClick:r[0]||(r[0]=l=>t.value=!t.value)},[e.button||e.icon?(s(),u("span",Ca,[e.icon?(s(),u("span",{key:0,class:N([e.icon,"option-icon"])},null,2)):m("",!0),e.button?(s(),u("span",{key:1,innerHTML:e.button},null,8,Ha)):m("",!0),r[3]||(r[3]=d("span",{class:"vpi-chevron-down text-icon"},null,-1))])):(s(),u("span",Ba))],8,Aa),d("div",Ea,[k(wa,{items:e.items},{default:h(()=>[c(o.$slots,"default",{},void 0,!0)]),_:3},8,["items"])])],544))}}),_e=g(Fa,[["__scopeId","data-v-35350ec6"]]),Da=["href","aria-label","innerHTML"],Oa=p({__name:"VPSocialLink",props:{icon:{},link:{},ariaLabel:{}},setup(e){const t=e,n=S();U(async()=>{var r;await Le();const o=(r=n.value)==null?void 0:r.children[0];o instanceof HTMLElement&&o.className.startsWith("vpi-social-")&&(getComputedStyle(o).maskImage||getComputedStyle(o).webkitMaskImage)==="none"&&o.style.setProperty("--icon",`url('https://api.iconify.design/simple-icons/${t.icon}.svg')`)});const a=y(()=>typeof t.icon=="object"?t.icon.svg:`<span class="vpi-social-${t.icon}"></span>`);return(o,r)=>(s(),u("a",{ref_key:"el",ref:n,class:"VPSocialLink no-icon",href:e.link,"aria-label":e.ariaLabel??(typeof e.icon=="string"?e.icon:""),target:"_blank",rel:"noopener",innerHTML:a.value},null,8,Da))}}),Ga=g(Oa,[["__scopeId","data-v-8a10446f"]]),Ua={class:"VPSocialLinks"},ja=p({__name:"VPSocialLinks",props:{links:{}},setup(e){return(t,n)=>(s(),u("div",Ua,[(s(!0),u(x,null,A(e.links,({link:a,icon:o,ariaLabel:r})=>(s(),_(Ga,{key:a,icon:o,link:a,ariaLabel:r},null,8,["icon","link","ariaLabel"]))),128))]))}}),be=g(ja,[["__scopeId","data-v-f3c77898"]]),za={key:0,class:"group translations"},Wa={class:"trans-title"},Ka={key:1,class:"group"},qa={class:"item appearance"},Ra={class:"label"},Ja={class:"appearance-action"},Xa={key:2,class:"group"},Ya={class:"item social-links"},Qa=p({__name:"VPNavBarExtra",setup(e){const{site:t,theme:n}=L(),{localeLinks:a,currentLang:o}=q({correspondingLink:!0}),r=y(()=>a.value.length&&o.value.label||t.value.appearance||n.value.socialLinks);return(l,v)=>r.value?(s(),_(_e,{key:0,class:"VPNavBarExtra",label:"extra navigation"},{default:h(()=>[i(a).length&&i(o).label?(s(),u("div",za,[d("p",Wa,M(i(o).label),1),(s(!0),u(x,null,A(i(a),f=>(s(),_(te,{key:f.link,item:f},null,8,["item"]))),128))])):m("",!0),i(t).appearance&&i(t).appearance!=="force-dark"&&i(t).appearance!=="force-auto"?(s(),u("div",Ka,[d("div",qa,[d("p",Ra,M(i(n).darkModeSwitchLabel||"Appearance"),1),d("div",Ja,[k(pe)])])])):m("",!0),i(n).socialLinks?(s(),u("div",Xa,[d("div",Ya,[k(be,{class:"social-links-list",links:i(n).socialLinks},null,8,["links"])])])):m("",!0)]),_:1})):m("",!0)}}),Za=g(Qa,[["__scopeId","data-v-6d16081b"]]),eo=["aria-expanded"],to=p({__name:"VPNavBarHamburger",props:{active:{type:Boolean}},emits:["click"],setup(e){return(t,n)=>(s(),u("button",{type:"button",class:N(["VPNavBarHamburger",{active:e.active}]),"aria-label":"mobile navigation","aria-expanded":e.active,"aria-controls":"VPNavScreen",onClick:n[0]||(n[0]=a=>t.$emit("click"))},[...n[1]||(n[1]=[d("span",{class:"container"},[d("span",{class:"top"}),d("span",{class:"middle"}),d("span",{class:"bottom"})],-1)])],10,eo))}}),no=g(to,[["__scopeId","data-v-1e510fee"]]),ao=["innerHTML"],oo=p({__name:"VPNavBarMenuLink",props:{item:{}},setup(e){const{page:t}=L();return(n,a)=>(s(),_(E,{class:N({VPNavBarMenuLink:!0,active:i(z)(i(t).relativePath,e.item.activeMatch||e.item.link,!!e.item.activeMatch)}),href:e.item.link,target:e.item.target,rel:e.item.rel,"no-icon":e.item.noIcon,tabindex:"0"},{default:h(()=>[d("span",{innerHTML:e.item.text},null,8,ao)]),_:1},8,["class","href","target","rel","no-icon"]))}}),so=g(oo,[["__scopeId","data-v-82ee0671"]]),io=p({__name:"VPNavBarMenuGroup",props:{item:{}},setup(e){const t=e,{page:n}=L(),a=r=>"component"in r?!1:"link"in r?z(n.value.relativePath,r.link,!!t.item.activeMatch):r.items.some(a),o=y(()=>a(t.item));return(r,l)=>(s(),_(_e,{class:N({VPNavBarMenuGroup:!0,active:i(z)(i(n).relativePath,e.item.activeMatch,!!e.item.activeMatch)||o.value}),button:e.item.text,items:e.item.items},null,8,["class","button","items"]))}}),ro={key:0,"aria-labelledby":"main-nav-aria-label",class:"VPNavBarMenu"},lo=p({__name:"VPNavBarMenu",setup(e){const{theme:t}=L();return(n,a)=>i(t).nav?(s(),u("nav",ro,[a[0]||(a[0]=d("span",{id:"main-nav-aria-label",class:"visually-hidden"}," Main Navigation ",-1)),(s(!0),u(x,null,A(i(t).nav,o=>(s(),u(x,{key:JSON.stringify(o)},["link"in o?(s(),_(so,{key:0,item:o},null,8,["item"])):"component"in o?(s(),_(B(o.component),G({key:1,ref_for:!0},o.props),null,16)):(s(),_(io,{key:2,item:o},null,8,["item"]))],64))),128))])):m("",!0)}}),co=g(lo,[["__scopeId","data-v-d1dc8fec"]]);function uo(e){const{localeIndex:t,theme:n}=L();function a(o){var I,w,C;const r=o.split("."),l=(I=n.value.search)==null?void 0:I.options,v=l&&typeof l=="object",f=v&&((C=(w=l.locales)==null?void 0:w[t.value])==null?void 0:C.translations)||null,$=v&&l.translations||null;let V=f,b=$,P=e;const T=r.pop();for(const H of r){let O=null;const K=P==null?void 0:P[H];K&&(O=P=K);const ne=b==null?void 0:b[H];ne&&(O=b=ne);const ae=V==null?void 0:V[H];ae&&(O=V=ae),K||(P=O),ne||(b=O),ae||(V=O)}return(V==null?void 0:V[T])??(b==null?void 0:b[T])??(P==null?void 0:P[T])??""}return a}const vo=["aria-label"],fo={class:"DocSearch-Button-Container"},ho={class:"DocSearch-Button-Placeholder"},ge=p({__name:"VPNavBarSearchButton",setup(e){const n=uo({button:{buttonText:"Search",buttonAriaLabel:"Search"}});return(a,o)=>(s(),u("button",{type:"button",class:"DocSearch DocSearch-Button","aria-label":i(n)("button.buttonAriaLabel")},[d("span",fo,[o[0]||(o[0]=d("span",{class:"vp-icon DocSearch-Search-Icon"},null,-1)),d("span",ho,M(i(n)("button.buttonText")),1)]),o[1]||(o[1]=d("span",{class:"DocSearch-Button-Keys"},[d("kbd",{class:"DocSearch-Button-Key"}),d("kbd",{class:"DocSearch-Button-Key"},"K")],-1))],8,vo))}}),mo={class:"VPNavBarSearch"},po={id:"local-search"},ko={key:1,id:"docsearch"},_o=p({__name:"VPNavBarSearch",setup(e){const t=Ge(()=>Ue(()=>import("./VPLocalSearchBox.DMeTzGam.js"),__vite__mapDeps([0,1]))),n=()=>null,{theme:a}=L(),o=S(!1),r=S(!1);U(()=>{});function l(){o.value||(o.value=!0,setTimeout(v,16))}function v(){const b=new Event("keydown");b.key="k",b.metaKey=!0,window.dispatchEvent(b),setTimeout(()=>{document.querySelector(".DocSearch-Modal")||v()},16)}function f(b){const P=b.target,T=P.tagName;return P.isContentEditable||T==="INPUT"||T==="SELECT"||T==="TEXTAREA"}const $=S(!1);ie("k",b=>{(b.ctrlKey||b.metaKey)&&(b.preventDefault(),$.value=!0)}),ie("/",b=>{f(b)||(b.preventDefault(),$.value=!0)});const V="local";return(b,P)=>{var T;return s(),u("div",mo,[i(V)==="local"?(s(),u(x,{key:0},[$.value?(s(),_(i(t),{key:0,onClose:P[0]||(P[0]=I=>$.value=!1)})):m("",!0),d("div",po,[k(ge,{onClick:P[1]||(P[1]=I=>$.value=!0)})])],64)):i(V)==="algolia"?(s(),u(x,{key:1},[o.value?(s(),_(i(n),{key:0,algolia:((T=i(a).search)==null?void 0:T.options)??i(a).algolia,onVnodeBeforeMount:P[2]||(P[2]=I=>r.value=!0)},null,8,["algolia"])):m("",!0),r.value?m("",!0):(s(),u("div",ko,[k(ge,{onClick:l})]))],64)):m("",!0)])}}}),bo=p({__name:"VPNavBarSocialLinks",setup(e){const{theme:t}=L();return(n,a)=>i(t).socialLinks?(s(),_(be,{key:0,class:"VPNavBarSocialLinks",links:i(t).socialLinks},null,8,["links"])):m("",!0)}}),go=g(bo,[["__scopeId","data-v-a61e22dd"]]),$o=["href","rel","target"],yo=["innerHTML"],Po={key:2},Lo=p({__name:"VPNavBarTitle",setup(e){const{site:t,theme:n}=L(),{hasSidebar:a}=D(),{currentLang:o}=q(),r=y(()=>{var f;return typeof n.value.logoLink=="string"?n.value.logoLink:(f=n.value.logoLink)==null?void 0:f.link}),l=y(()=>{var f;return typeof n.value.logoLink=="string"||(f=n.value.logoLink)==null?void 0:f.rel}),v=y(()=>{var f;return typeof n.value.logoLink=="string"||(f=n.value.logoLink)==null?void 0:f.target});return(f,$)=>(s(),u("div",{class:N(["VPNavBarTitle",{"has-sidebar":i(a)}])},[d("a",{class:"title",href:r.value??i(he)(i(o).link),rel:l.value,target:v.value},[c(f.$slots,"nav-bar-title-before",{},void 0,!0),i(n).logo?(s(),_(J,{key:0,class:"logo",image:i(n).logo},null,8,["image"])):m("",!0),i(n).siteTitle?(s(),u("span",{key:1,innerHTML:i(n).siteTitle},null,8,yo)):i(n).siteTitle===void 0?(s(),u("span",Po,M(i(t).title),1)):m("",!0),c(f.$slots,"nav-bar-title-after",{},void 0,!0)],8,$o)],2))}}),Vo=g(Lo,[["__scopeId","data-v-6ce24249"]]),So={class:"items"},To={class:"title"},No=p({__name:"VPNavBarTranslations",setup(e){const{theme:t}=L(),{localeLinks:n,currentLang:a}=q({correspondingLink:!0});return(o,r)=>i(n).length&&i(a).label?(s(),_(_e,{key:0,class:"VPNavBarTranslations",icon:"vpi-languages",label:i(t).langMenuLabel||"Change language"},{default:h(()=>[d("div",So,[d("p",To,M(i(a).label),1),(s(!0),u(x,null,A(i(n),l=>(s(),_(te,{key:l.link,item:l},null,8,["item"]))),128))])]),_:1},8,["label"])):m("",!0)}}),Mo=g(No,[["__scopeId","data-v-0c36def1"]]),xo={class:"wrapper"},Io={class:"container"},wo={class:"title"},Ao={class:"content"},Co={class:"content-body"},Ho=p({__name:"VPNavBar",props:{isScreenOpen:{type:Boolean}},emits:["toggle-screen"],setup(e){const t=e,{y:n}=Ve(),{hasSidebar:a}=D(),{frontmatter:o}=L(),r=S({});return fe(()=>{r.value={"has-sidebar":a.value,home:o.value.layout==="home",top:n.value===0,"screen-open":t.isScreenOpen}}),(l,v)=>(s(),u("div",{class:N(["VPNavBar",r.value])},[d("div",xo,[d("div",Io,[d("div",wo,[k(Vo,null,{"nav-bar-title-before":h(()=>[c(l.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":h(()=>[c(l.$slots,"nav-bar-title-after",{},void 0,!0)]),_:3})]),d("div",Ao,[d("div",Co,[c(l.$slots,"nav-bar-content-before",{},void 0,!0),k(_o,{class:"search"}),k(co,{class:"menu"}),k(Mo,{class:"translations"}),k(_a,{class:"appearance"}),k(go,{class:"social-links"}),k(Za,{class:"extra"}),c(l.$slots,"nav-bar-content-after",{},void 0,!0),k(no,{class:"hamburger",active:e.isScreenOpen,onClick:v[0]||(v[0]=f=>l.$emit("toggle-screen"))},null,8,["active"])])])])]),v[1]||(v[1]=d("div",{class:"divider"},[d("div",{class:"divider-line"})],-1))],2))}}),Bo=g(Ho,[["__scopeId","data-v-bfcfe748"]]),Eo={key:0,class:"VPNavScreenAppearance"},Fo={class:"text"},Do=p({__name:"VPNavScreenAppearance",setup(e){const{site:t,theme:n}=L();return(a,o)=>i(t).appearance&&i(t).appearance!=="force-dark"&&i(t).appearance!=="force-auto"?(s(),u("div",Eo,[d("p",Fo,M(i(n).darkModeSwitchLabel||"Appearance"),1),k(pe)])):m("",!0)}}),Oo=g(Do,[["__scopeId","data-v-31823f39"]]),Go=["innerHTML"],Uo=p({__name:"VPNavScreenMenuLink",props:{item:{}},setup(e){const t=Z("close-screen");return(n,a)=>(s(),_(E,{class:"VPNavScreenMenuLink",href:e.item.link,target:e.item.target,rel:e.item.rel,"no-icon":e.item.noIcon,onClick:i(t)},{default:h(()=>[d("span",{innerHTML:e.item.text},null,8,Go)]),_:1},8,["href","target","rel","no-icon","onClick"]))}}),jo=g(Uo,[["__scopeId","data-v-b8afa252"]]),zo=["innerHTML"],Wo=p({__name:"VPNavScreenMenuGroupLink",props:{item:{}},setup(e){const t=Z("close-screen");return(n,a)=>(s(),_(E,{class:"VPNavScreenMenuGroupLink",href:e.item.link,target:e.item.target,rel:e.item.rel,"no-icon":e.item.noIcon,onClick:i(t)},{default:h(()=>[d("span",{innerHTML:e.item.text},null,8,zo)]),_:1},8,["href","target","rel","no-icon","onClick"]))}}),Ae=g(Wo,[["__scopeId","data-v-dade941e"]]),Ko={class:"VPNavScreenMenuGroupSection"},qo={key:0,class:"title"},Ro=p({__name:"VPNavScreenMenuGroupSection",props:{text:{},items:{}},setup(e){return(t,n)=>(s(),u("div",Ko,[e.text?(s(),u("p",qo,M(e.text),1)):m("",!0),(s(!0),u(x,null,A(e.items,a=>(s(),_(Ae,{key:a.text,item:a},null,8,["item"]))),128))]))}}),Jo=g(Ro,[["__scopeId","data-v-9b13b90a"]]),Xo=["aria-controls","aria-expanded"],Yo=["innerHTML"],Qo=["id"],Zo={key:0,class:"item"},es={key:1,class:"item"},ts={key:2,class:"group"},ns=p({__name:"VPNavScreenMenuGroup",props:{text:{},items:{}},setup(e){const t=e,n=S(!1),a=y(()=>`NavScreenGroup-${t.text.replace(" ","-").toLowerCase()}`);function o(){n.value=!n.value}return(r,l)=>(s(),u("div",{class:N(["VPNavScreenMenuGroup",{open:n.value}])},[d("button",{class:"button","aria-controls":a.value,"aria-expanded":n.value,onClick:o},[d("span",{class:"button-text",innerHTML:e.text},null,8,Yo),l[0]||(l[0]=d("span",{class:"vpi-plus button-icon"},null,-1))],8,Xo),d("div",{id:a.value,class:"items"},[(s(!0),u(x,null,A(e.items,v=>(s(),u(x,{key:JSON.stringify(v)},["link"in v?(s(),u("div",Zo,[k(Ae,{item:v},null,8,["item"])])):"component"in v?(s(),u("div",es,[(s(),_(B(v.component),G({ref_for:!0},v.props,{"screen-menu":""}),null,16))])):(s(),u("div",ts,[k(Jo,{text:v.text,items:v.items},null,8,["text","items"])]))],64))),128))],8,Qo)],2))}}),as=g(ns,[["__scopeId","data-v-decc2297"]]),os={key:0,class:"VPNavScreenMenu"},ss=p({__name:"VPNavScreenMenu",setup(e){const{theme:t}=L();return(n,a)=>i(t).nav?(s(),u("nav",os,[(s(!0),u(x,null,A(i(t).nav,o=>(s(),u(x,{key:JSON.stringify(o)},["link"in o?(s(),_(jo,{key:0,item:o},null,8,["item"])):"component"in o?(s(),_(B(o.component),G({key:1,ref_for:!0},o.props,{"screen-menu":""}),null,16)):(s(),_(as,{key:2,text:o.text||"",items:o.items},null,8,["text","items"]))],64))),128))])):m("",!0)}}),is=p({__name:"VPNavScreenSocialLinks",setup(e){const{theme:t}=L();return(n,a)=>i(t).socialLinks?(s(),_(be,{key:0,class:"VPNavScreenSocialLinks",links:i(t).socialLinks},null,8,["links"])):m("",!0)}}),rs={class:"list"},ls=p({__name:"VPNavScreenTranslations",setup(e){const{localeLinks:t,currentLang:n}=q({correspondingLink:!0}),a=S(!1);function o(){a.value=!a.value}return(r,l)=>i(t).length&&i(n).label?(s(),u("div",{key:0,class:N(["VPNavScreenTranslations",{open:a.value}])},[d("button",{class:"title",onClick:o},[l[0]||(l[0]=d("span",{class:"vpi-languages icon lang"},null,-1)),j(" "+M(i(n).label)+" ",1),l[1]||(l[1]=d("span",{class:"vpi-chevron-down icon chevron"},null,-1))]),d("ul",rs,[(s(!0),u(x,null,A(i(t),v=>(s(),u("li",{key:v.link,class:"item"},[k(E,{class:"link",href:v.link},{default:h(()=>[j(M(v.text),1)]),_:2},1032,["href"])]))),128))])],2)):m("",!0)}}),cs=g(ls,[["__scopeId","data-v-2d933f52"]]),us={class:"container"},ds=p({__name:"VPNavScreen",props:{open:{type:Boolean}},setup(e){const t=S(null),n=Se(ee?document.body:null);return(a,o)=>(s(),_(ue,{name:"fade",onEnter:o[0]||(o[0]=r=>n.value=!0),onAfterLeave:o[1]||(o[1]=r=>n.value=!1)},{default:h(()=>[e.open?(s(),u("div",{key:0,class:"VPNavScreen",ref_key:"screen",ref:t,id:"VPNavScreen"},[d("div",us,[c(a.$slots,"nav-screen-content-before",{},void 0,!0),k(ss,{class:"menu"}),k(cs,{class:"translations"}),k(Oo,{class:"appearance"}),k(is,{class:"social-links"}),c(a.$slots,"nav-screen-content-after",{},void 0,!0)])],512)):m("",!0)]),_:3}))}}),vs=g(ds,[["__scopeId","data-v-6d99f486"]]),fs={key:0,class:"VPNav"},hs=p({__name:"VPNav",setup(e){const{isScreenOpen:t,closeScreen:n,toggleScreen:a}=la(),{frontmatter:o}=L(),r=y(()=>o.value.navbar!==!1);return Te("close-screen",n),X(()=>{ee&&document.documentElement.classList.toggle("hide-nav",!r.value)}),(l,v)=>r.value?(s(),u("header",fs,[k(Bo,{"is-screen-open":i(t),onToggleScreen:i(a)},{"nav-bar-title-before":h(()=>[c(l.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":h(()=>[c(l.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":h(()=>[c(l.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":h(()=>[c(l.$slots,"nav-bar-content-after",{},void 0,!0)]),_:3},8,["is-screen-open","onToggleScreen"]),k(vs,{open:i(t)},{"nav-screen-content-before":h(()=>[c(l.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":h(()=>[c(l.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3},8,["open"])])):m("",!0)}}),ms=g(hs,[["__scopeId","data-v-07e0ee65"]]),ps=["role","tabindex"],ks={key:1,class:"items"},_s=p({__name:"VPSidebarItem",props:{item:{},depth:{}},setup(e){const t=e,{collapsed:n,collapsible:a,isLink:o,isActiveLink:r,hasActiveLink:l,hasChildren:v,toggle:f}=dt(y(()=>t.item)),$=y(()=>v.value?"section":"div"),V=y(()=>o.value?"a":"div"),b=y(()=>v.value?t.depth+2===7?"p":`h${t.depth+2}`:"p"),P=y(()=>o.value?void 0:"button"),T=y(()=>[[`level-${t.depth}`],{collapsible:a.value},{collapsed:n.value},{"is-link":o.value},{"is-active":r.value},{"has-active":l.value}]);function I(C){"key"in C&&C.key!=="Enter"||!t.item.link&&f()}function w(){t.item.link&&f()}return(C,H)=>{const O=W("VPSidebarItem",!0);return s(),_(B($.value),{class:N(["VPSidebarItem",T.value])},{default:h(()=>[e.item.text?(s(),u("div",G({key:0,class:"item",role:P.value},je(e.item.items?{click:I,keydown:I}:{},!0),{tabindex:e.item.items&&0}),[H[1]||(H[1]=d("div",{class:"indicator"},null,-1)),e.item.link?(s(),_(E,{key:0,tag:V.value,class:"link",href:e.item.link,rel:e.item.rel,target:e.item.target},{default:h(()=>[(s(),_(B(b.value),{class:"text",innerHTML:e.item.text},null,8,["innerHTML"]))]),_:1},8,["tag","href","rel","target"])):(s(),_(B(b.value),{key:1,class:"text",innerHTML:e.item.text},null,8,["innerHTML"])),e.item.collapsed!=null&&e.item.items&&e.item.items.length?(s(),u("div",{key:2,class:"caret",role:"button","aria-label":"toggle section",onClick:w,onKeydown:ze(w,["enter"]),tabindex:"0"},[...H[0]||(H[0]=[d("span",{class:"vpi-chevron-right caret-icon"},null,-1)])],32)):m("",!0)],16,ps)):m("",!0),e.item.items&&e.item.items.length?(s(),u("div",ks,[e.depth<5?(s(!0),u(x,{key:0},A(e.item.items,K=>(s(),_(O,{key:K.text,item:K,depth:e.depth+1},null,8,["item","depth"]))),128)):m("",!0)])):m("",!0)]),_:1},8,["class"])}}}),bs=g(_s,[["__scopeId","data-v-cd30f15d"]]),gs=p({__name:"VPSidebarGroup",props:{items:{}},setup(e){const t=S(!0);let n=null;return U(()=>{n=setTimeout(()=>{n=null,t.value=!1},300)}),We(()=>{n!=null&&(clearTimeout(n),n=null)}),(a,o)=>(s(!0),u(x,null,A(e.items,r=>(s(),u("div",{key:r.text,class:N(["group",{"no-transition":t.value}])},[k(bs,{item:r,depth:0},null,8,["item"])],2))),128))}}),$s=g(gs,[["__scopeId","data-v-594fed9e"]]),ys={class:"nav",id:"VPSidebarNav","aria-labelledby":"sidebar-aria-label",tabindex:"-1"},Ps=p({__name:"VPSidebar",props:{open:{type:Boolean}},setup(e){const{sidebarGroups:t,hasSidebar:n}=D(),a=e,o=S(null),r=Se(ee?document.body:null);F([a,o],()=>{var v;a.open?(r.value=!0,(v=o.value)==null||v.focus()):r.value=!1},{immediate:!0,flush:"post"});const l=S(0);return F(t,()=>{l.value+=1},{deep:!0}),(v,f)=>i(n)?(s(),u("aside",{key:0,class:N(["VPSidebar",{open:e.open}]),ref_key:"navEl",ref:o,onClick:f[0]||(f[0]=Ke(()=>{},["stop"]))},[f[2]||(f[2]=d("div",{class:"curtain"},null,-1)),d("nav",ys,[f[1]||(f[1]=d("span",{class:"visually-hidden",id:"sidebar-aria-label"}," Sidebar Navigation ",-1)),c(v.$slots,"sidebar-nav-before",{},void 0,!0),(s(),_($s,{items:i(t),key:l.value},null,8,["items"])),c(v.$slots,"sidebar-nav-after",{},void 0,!0)])],2)):m("",!0)}}),Ls=g(Ps,[["__scopeId","data-v-1d1d7027"]]),Vs=p({__name:"VPSkipLink",setup(e){const{theme:t}=L(),n=Q(),a=S();F(()=>n.path,()=>a.value.focus());function o({target:r}){const l=document.getElementById(decodeURIComponent(r.hash).slice(1));if(l){const v=()=>{l.removeAttribute("tabindex"),l.removeEventListener("blur",v)};l.setAttribute("tabindex","-1"),l.addEventListener("blur",v),l.focus(),window.scrollTo(0,0)}}return(r,l)=>(s(),u(x,null,[d("span",{ref_key:"backToTop",ref:a,tabindex:"-1"},null,512),d("a",{href:"#VPContent",class:"VPSkipLink visually-hidden",onClick:o},M(i(t).skipToContentLabel||"Skip to content"),1)],64))}}),Ss=g(Vs,[["__scopeId","data-v-0a4b5d46"]]),Ts=p({__name:"Layout",setup(e){const{isOpen:t,open:n,close:a}=D(),o=Q();F(()=>o.path,a),ut(t,a);const{frontmatter:r}=L(),l=qe(),v=y(()=>!!l["home-hero-image"]);return Te("hero-image-slot-exists",v),(f,$)=>{const V=W("Content");return i(r).layout!==!1?(s(),u("div",{key:0,class:N(["Layout",i(r).pageClass])},[c(f.$slots,"layout-top",{},void 0,!0),k(Ss),k(Ye,{class:"backdrop",show:i(t),onClick:i(a)},null,8,["show","onClick"]),k(ms,null,{"nav-bar-title-before":h(()=>[c(f.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":h(()=>[c(f.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":h(()=>[c(f.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":h(()=>[c(f.$slots,"nav-bar-content-after",{},void 0,!0)]),"nav-screen-content-before":h(()=>[c(f.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":h(()=>[c(f.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3}),k(ra,{open:i(t),onOpenMenu:i(n)},null,8,["open","onOpenMenu"]),k(Ls,{open:i(t)},{"sidebar-nav-before":h(()=>[c(f.$slots,"sidebar-nav-before",{},void 0,!0)]),"sidebar-nav-after":h(()=>[c(f.$slots,"sidebar-nav-after",{},void 0,!0)]),_:3},8,["open"]),k(Wn,null,{"page-top":h(()=>[c(f.$slots,"page-top",{},void 0,!0)]),"page-bottom":h(()=>[c(f.$slots,"page-bottom",{},void 0,!0)]),"not-found":h(()=>[c(f.$slots,"not-found",{},void 0,!0)]),"home-hero-before":h(()=>[c(f.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info-before":h(()=>[c(f.$slots,"home-hero-info-before",{},void 0,!0)]),"home-hero-info":h(()=>[c(f.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-info-after":h(()=>[c(f.$slots,"home-hero-info-after",{},void 0,!0)]),"home-hero-actions-after":h(()=>[c(f.$slots,"home-hero-actions-after",{},void 0,!0)]),"home-hero-image":h(()=>[c(f.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":h(()=>[c(f.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":h(()=>[c(f.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":h(()=>[c(f.$slots,"home-features-after",{},void 0,!0)]),"doc-footer-before":h(()=>[c(f.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":h(()=>[c(f.$slots,"doc-before",{},void 0,!0)]),"doc-after":h(()=>[c(f.$slots,"doc-after",{},void 0,!0)]),"doc-top":h(()=>[c(f.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":h(()=>[c(f.$slots,"doc-bottom",{},void 0,!0)]),"aside-top":h(()=>[c(f.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":h(()=>[c(f.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":h(()=>[c(f.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":h(()=>[c(f.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":h(()=>[c(f.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":h(()=>[c(f.$slots,"aside-ads-after",{},void 0,!0)]),_:3}),k(Xn),c(f.$slots,"layout-bottom",{},void 0,!0)],2)):(s(),_(V,{key:1}))}}}),Ns=g(Ts,[["__scopeId","data-v-1bdd7537"]]),Ms={Layout:Ns,enhanceApp:({app:e})=>{e.component("Badge",Re)}},Is={...Ms};export{uo as c,Is as t,L as u};
@@ -0,0 +1,7 @@
1
+ import{_ as s,o as i,c as e,ag as l}from"./chunks/framework.BZohXCq9.js";const k=JSON.parse('{"title":"开发文档","description":"","frontmatter":{},"headers":[],"relativePath":"development.md","filePath":"development.md","lastUpdated":1773162470000}'),t={name:"development.md"};function n(p,a,h,d,o,c){return i(),e("div",null,[...a[0]||(a[0]=[l(`<h1 id="开发文档" tabindex="-1">开发文档 <a class="header-anchor" href="#开发文档" aria-label="Permalink to &quot;开发文档&quot;">​</a></h1><h2 id="本地开发" tabindex="-1">本地开发 <a class="header-anchor" href="#本地开发" aria-label="Permalink to &quot;本地开发&quot;">​</a></h2><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> install</span></span>
2
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> dev:relay</span></span>
3
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> dev:app</span></span>
4
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> dev:agent</span></span></code></pre></div><h2 id="常用检查" tabindex="-1">常用检查 <a class="header-anchor" href="#常用检查" aria-label="Permalink to &quot;常用检查&quot;">​</a></h2><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> typecheck</span></span>
5
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> build</span></span>
6
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> test:ui-smoke</span></span>
7
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> check:stability</span></span></code></pre></div><p>说明:</p><ul><li><code>pnpm test:ui-smoke</code> 现在按手机视口跑,覆盖配对、切会话、关闭会话、清除绑定,以及“查看后终端区进入可视区”</li><li><code>pnpm check:stability</code> 会验证 relay / agent 长时间运行下的输出缓冲和重连一致性</li></ul><h2 id="当前仓库形态" tabindex="-1">当前仓库形态 <a class="header-anchor" href="#当前仓库形态" aria-label="Permalink to &quot;当前仓库形态&quot;">​</a></h2><ul><li>根入口:<code>src/cli.ts</code></li><li>PC 端:<code>agent/</code></li><li>手机端:<code>app/</code></li><li>relay:<code>relay/</code></li><li>共享协议:<code>packages/protocol/</code></li></ul><h2 id="发布流程" tabindex="-1">发布流程 <a class="header-anchor" href="#发布流程" aria-label="Permalink to &quot;发布流程&quot;">​</a></h2><ol><li>提升版本号</li><li>运行 <code>pnpm build</code></li><li>运行 <code>pnpm test:ui-smoke</code></li><li>运行 <code>pnpm check:stability</code></li><li>执行 <code>npm publish --access public</code></li></ol><p>当前已发布包名:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">@fengye404/termpilot</span></span></code></pre></div>`,13)])])}const g=s(t,[["render",n]]);export{k as __pageData,g as default};
@@ -0,0 +1 @@
1
+ import{_ as s,o as i,c as e,ag as l}from"./chunks/framework.BZohXCq9.js";const k=JSON.parse('{"title":"开发文档","description":"","frontmatter":{},"headers":[],"relativePath":"development.md","filePath":"development.md","lastUpdated":1773162470000}'),t={name:"development.md"};function n(p,a,h,d,o,c){return i(),e("div",null,[...a[0]||(a[0]=[l("",13)])])}const g=s(t,[["render",n]]);export{k as __pageData,g as default};
@@ -0,0 +1 @@
1
+ import{_ as i,o as e,c as s,ag as t}from"./chunks/framework.BZohXCq9.js";const k=JSON.parse('{"title":"快速开始","description":"","frontmatter":{},"headers":[],"relativePath":"getting-started.md","filePath":"getting-started.md","lastUpdated":null}'),l={name:"getting-started.md"};function p(h,a,n,o,d,r){return e(),s("div",null,[...a[0]||(a[0]=[t('<h1 id="快速开始" tabindex="-1">快速开始 <a class="header-anchor" href="#快速开始" aria-label="Permalink to &quot;快速开始&quot;">​</a></h1><p>这份文档面向第一次真正把 TermPilot 跑起来的用户。目标不是解释所有细节,而是让你在 5 分钟内跑通第一条完整链路。</p><h2 id="_1-你需要准备什么" tabindex="-1">1. 你需要准备什么 <a class="header-anchor" href="#_1-你需要准备什么" aria-label="Permalink to &quot;1. 你需要准备什么&quot;">​</a></h2><ul><li>一台服务器,或者一台手机能访问到的局域网机器</li><li>一台作为主力开发机的电脑</li><li>电脑上已经安装 <code>tmux</code></li><li>两端都安装了 <code>Node.js</code></li></ul><p>安装 TermPilot:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> -g</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> @fengye404/termpilot</span></span></code></pre></div><h2 id="_2-在服务器启动-relay" tabindex="-1">2. 在服务器启动 relay <a class="header-anchor" href="#_2-在服务器启动-relay" aria-label="Permalink to &quot;2. 在服务器启动 relay&quot;">​</a></h2><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span></span></code></pre></div><p>默认行为:</p><ul><li>后台启动</li><li>默认监听 <code>0.0.0.0:8787</code></li><li>同时提供网页和 <code>/ws</code> WebSocket</li></ul><p>如果你要停掉它:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> stop</span></span></code></pre></div><p>如果你要前台看日志:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> run</span></span></code></pre></div><h2 id="_3-在电脑启动-agent" tabindex="-1">3. 在电脑启动 agent <a class="header-anchor" href="#_3-在电脑启动-agent" aria-label="Permalink to &quot;3. 在电脑启动 agent&quot;">​</a></h2><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span></span></code></pre></div><p>第一次运行时,终端会提示你输入:</p><ol><li>relay 域名或 IP</li><li>端口,直接回车默认 <code>8787</code></li></ol><p>然后它会自动:</p><ul><li>保存本地配置</li><li>后台启动 agent</li><li>打印一次性配对码</li></ul><p>以后日常再次执行:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span></span></code></pre></div><p>如果后台 agent 已存在,它会直接显示当前状态。</p><h2 id="_4-在手机完成配对" tabindex="-1">4. 在手机完成配对 <a class="header-anchor" href="#_4-在手机完成配对" aria-label="Permalink to &quot;4. 在手机完成配对&quot;">​</a></h2><p>手机浏览器打开:</p><ul><li><code>http://your-domain.com:8787</code></li><li>或配置 HTTPS 反代后的 <code>https://your-domain.com</code></li></ul><p>未配对时,首页只需要做一件事:</p><ul><li>输入电脑端打印出来的配对码</li></ul><p>配对成功后,你会直接进入会话列表。</p><h2 id="_5-直接跑一个可同步的任务" tabindex="-1">5. 直接跑一个可同步的任务 <a class="header-anchor" href="#_5-直接跑一个可同步的任务" aria-label="Permalink to &quot;5. 直接跑一个可同步的任务&quot;">​</a></h2><p>如果你平时主要跑 Claude Code,最短路径就是:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> claude</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> code</span></span></code></pre></div><p>如果你主要跑 OpenCode:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> open</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> code</span></span></code></pre></div><p>这两条命令都会:</p><ul><li>创建一个受 TermPilot 管理的 <code>tmux</code> 会话</li><li>把命令写进这个会话</li><li>让当前终端直接 attach 到这个会话</li></ul><p>此时手机上会看到同一个会话输出。</p><h2 id="_6-第一次跑通后你应该验证什么" tabindex="-1">6. 第一次跑通后你应该验证什么 <a class="header-anchor" href="#_6-第一次跑通后你应该验证什么" aria-label="Permalink to &quot;6. 第一次跑通后你应该验证什么&quot;">​</a></h2><p>建议你做这 4 个最小动作:</p><ol><li>电脑上看到任务开始流式输出</li><li>手机上打开同一个会话,确认输出同步</li><li>手机上发一条短命令,确认会进入同一个会话</li><li>手机上关闭这个会话,确认电脑端也同步结束</li></ol><h2 id="_7-常见问题" tabindex="-1">7. 常见问题 <a class="header-anchor" href="#_7-常见问题" aria-label="Permalink to &quot;7. 常见问题&quot;">​</a></h2><h3 id="termpilot-relay-为什么不占当前窗口" tabindex="-1"><code>termpilot relay</code> 为什么不占当前窗口 <a class="header-anchor" href="#termpilot-relay-为什么不占当前窗口" aria-label="Permalink to &quot;`termpilot relay` 为什么不占当前窗口&quot;">​</a></h3><p>因为它默认是后台启动。要看实时日志,请用:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> run</span></span></code></pre></div><h3 id="termpilot-agent-为什么第二次执行没有再问域名" tabindex="-1"><code>termpilot agent</code> 为什么第二次执行没有再问域名 <a class="header-anchor" href="#termpilot-agent-为什么第二次执行没有再问域名" aria-label="Permalink to &quot;`termpilot agent` 为什么第二次执行没有再问域名&quot;">​</a></h3><p>因为第一次配置已经保存到了本地。再次执行时,它会直接使用本地配置启动或显示状态。</p><h3 id="为什么普通-iterm-标签页不会出现在手机上" tabindex="-1">为什么普通 iTerm 标签页不会出现在手机上 <a class="header-anchor" href="#为什么普通-iterm-标签页不会出现在手机上" aria-label="Permalink to &quot;为什么普通 iTerm 标签页不会出现在手机上&quot;">​</a></h3><p>因为只有 TermPilot 管理的会话才会同步。正确做法是从一开始就用:</p><ul><li><code>termpilot claude code</code></li><li><code>termpilot open code</code></li><li><code>termpilot create ...</code></li></ul><h2 id="_8-下一步看什么" tabindex="-1">8. 下一步看什么 <a class="header-anchor" href="#_8-下一步看什么" aria-label="Permalink to &quot;8. 下一步看什么&quot;">​</a></h2><ul><li>想长期部署:看 <a href="./operations-guide">部署与运维指南</a></li><li>想了解内部结构:看 <a href="./architecture">代码架构</a></li></ul>',51)])])}const g=i(l,[["render",p]]);export{k as __pageData,g as default};
@@ -0,0 +1 @@
1
+ import{_ as i,o as e,c as s,ag as t}from"./chunks/framework.BZohXCq9.js";const k=JSON.parse('{"title":"快速开始","description":"","frontmatter":{},"headers":[],"relativePath":"getting-started.md","filePath":"getting-started.md","lastUpdated":null}'),l={name:"getting-started.md"};function p(h,a,n,o,d,r){return e(),s("div",null,[...a[0]||(a[0]=[t("",51)])])}const g=i(l,[["render",p]]);export{k as __pageData,g as default};
@@ -0,0 +1 @@
1
+ import{_ as t,o as a,c as l,ag as i}from"./chunks/framework.BZohXCq9.js";const m=JSON.parse('{"title":"","description":"","frontmatter":{"layout":"home","hero":{"name":"TermPilot","text":"手机和电脑共享同一个 tmux 会话","tagline":"一个 npm 包,两个命令,手机浏览器直接接管你的长期任务。","actions":[{"theme":"brand","text":"5 分钟快速上手","link":"/getting-started"},{"theme":"alt","text":"部署与运维","link":"/operations-guide"},{"theme":"alt","text":"GitHub","link":"https://github.com/fengye404/TermPilot"}]},"features":[{"title":"一个包","details":"服务器和电脑都使用同一个 npm 包,对外只暴露 `termpilot relay` 和 `termpilot agent` 两个主命令。"},{"title":"手机不安装","details":"relay 同时托管网页和中继层,手机直接打开域名即可,不需要 App Store 或安卓安装包。"},{"title":"同一个会话","details":"电脑和手机看到的是同一批 tmux 会话,创建、查看、关闭和输入都互通。"},{"title":"面向长期任务","details":"适合 Claude Code、OpenCode、脚本批处理和各种需要离开电脑后继续观察的任务。"}]},"headers":[],"relativePath":"index.md","filePath":"index.md","lastUpdated":null}'),o={name:"index.md"};function r(d,e,n,s,c,h){return a(),l("div",null,[...e[0]||(e[0]=[i('<h2 id="你会怎么用它" tabindex="-1">你会怎么用它 <a class="header-anchor" href="#你会怎么用它" aria-label="Permalink to &quot;你会怎么用它&quot;">​</a></h2><p>最短路径只有四步:</p><ol><li>在服务器执行 <code>termpilot relay</code></li><li>在电脑执行 <code>termpilot agent</code></li><li>手机上打开 relay 域名并输入配对码</li><li>在电脑执行 <code>termpilot claude code</code></li></ol><p>之后你会得到:</p><ul><li>电脑和手机同步看到同一个会话输出</li><li>手机上能补命令、发快捷键、关闭会话</li><li>电脑离开当前桌面后,任务仍然在 <code>tmux</code> 里继续运行</li></ul><h2 id="文档地图" tabindex="-1">文档地图 <a class="header-anchor" href="#文档地图" aria-label="Permalink to &quot;文档地图&quot;">​</a></h2><ul><li><a href="/getting-started">快速开始</a></li><li><a href="/operations-guide">部署与运维指南</a></li><li><a href="/architecture">代码架构</a></li><li><a href="/protocol">协议说明</a></li><li><a href="/development">开发文档</a></li></ul><h2 id="推荐阅读顺序" tabindex="-1">推荐阅读顺序 <a class="header-anchor" href="#推荐阅读顺序" aria-label="Permalink to &quot;推荐阅读顺序&quot;">​</a></h2><ol><li>先看 <a href="/getting-started">快速开始</a></li><li>准备长期部署时再看 <a href="/operations-guide">部署与运维指南</a></li><li>想改代码时看 <a href="/architecture">代码架构</a> 和 <a href="/development">开发文档</a></li></ol>',9)])])}const u=t(o,[["render",r]]);export{m as __pageData,u as default};
@@ -0,0 +1 @@
1
+ import{_ as t,o as a,c as l,ag as i}from"./chunks/framework.BZohXCq9.js";const m=JSON.parse('{"title":"","description":"","frontmatter":{"layout":"home","hero":{"name":"TermPilot","text":"手机和电脑共享同一个 tmux 会话","tagline":"一个 npm 包,两个命令,手机浏览器直接接管你的长期任务。","actions":[{"theme":"brand","text":"5 分钟快速上手","link":"/getting-started"},{"theme":"alt","text":"部署与运维","link":"/operations-guide"},{"theme":"alt","text":"GitHub","link":"https://github.com/fengye404/TermPilot"}]},"features":[{"title":"一个包","details":"服务器和电脑都使用同一个 npm 包,对外只暴露 `termpilot relay` 和 `termpilot agent` 两个主命令。"},{"title":"手机不安装","details":"relay 同时托管网页和中继层,手机直接打开域名即可,不需要 App Store 或安卓安装包。"},{"title":"同一个会话","details":"电脑和手机看到的是同一批 tmux 会话,创建、查看、关闭和输入都互通。"},{"title":"面向长期任务","details":"适合 Claude Code、OpenCode、脚本批处理和各种需要离开电脑后继续观察的任务。"}]},"headers":[],"relativePath":"index.md","filePath":"index.md","lastUpdated":null}'),o={name:"index.md"};function r(d,e,n,s,c,h){return a(),l("div",null,[...e[0]||(e[0]=[i("",9)])])}const u=t(o,[["render",r]]);export{m as __pageData,u as default};
@@ -0,0 +1,18 @@
1
+ import{_ as i,o as s,c as e,ag as l}from"./chunks/framework.BZohXCq9.js";const c=JSON.parse('{"title":"TermPilot 部署与运维指南","description":"","frontmatter":{},"headers":[],"relativePath":"operations-guide.md","filePath":"operations-guide.md","lastUpdated":1773250545000}'),t={name:"operations-guide.md"};function p(h,a,n,d,o,r){return s(),e("div",null,[...a[0]||(a[0]=[l(`<h1 id="termpilot-部署与运维指南" tabindex="-1">TermPilot 部署与运维指南 <a class="header-anchor" href="#termpilot-部署与运维指南" aria-label="Permalink to &quot;TermPilot 部署与运维指南&quot;">​</a></h1><p>这份文档面向准备长期使用 TermPilot 的用户。它不重复 <code>README</code> 里的 5 分钟快速上手,而是把部署、反代、日常运维、排障和安全边界收成一份更完整的运行手册。</p><p>文档风格参考了成熟开源项目常见的写法:先说明适用场景,再给推荐拓扑、部署步骤、运维动作、排障清单和安全边界。你可以把它当成 TermPilot 的管理员手册来用。</p><h2 id="_0-阅读这份文档前-你应该已经知道什么" tabindex="-1">0. 阅读这份文档前,你应该已经知道什么 <a class="header-anchor" href="#_0-阅读这份文档前-你应该已经知道什么" aria-label="Permalink to &quot;0. 阅读这份文档前,你应该已经知道什么&quot;">​</a></h2><p>建议你已经完成过下面这件事中的至少一件:</p><ul><li>在本地把 <code>termpilot relay</code>、<code>termpilot agent</code> 跑通过一次</li><li>已经看过 <a href="./getting-started">快速开始</a></li></ul><p>如果你还没有跑通过最小链路,请先回到 <a href="./getting-started">快速开始</a>。</p><h2 id="_1-适用场景" tabindex="-1">1. 适用场景 <a class="header-anchor" href="#_1-适用场景" aria-label="Permalink to &quot;1. 适用场景&quot;">​</a></h2><p>适合下面这些情况:</p><ul><li>你已经确认 TermPilot 的基本流程可用,准备长期跑在自己的服务器上</li><li>你希望用域名和 HTTPS/WSS 暴露 relay,而不是直接用裸 IP 和端口</li><li>你需要给自己或团队整理一份可维护的运行说明</li></ul><p>不适合下面这些情况:</p><ul><li>第一次体验产品</li><li>只想在局域网里临时试一下</li></ul><p>第一次使用请先看 <a href="./getting-started">快速开始</a>。</p><h2 id="_2-部署清单" tabindex="-1">2. 部署清单 <a class="header-anchor" href="#_2-部署清单" aria-label="Permalink to &quot;2. 部署清单&quot;">​</a></h2><p>开始之前,先确认下面这些前置条件:</p><ul><li>一台能被手机访问到的服务器</li><li>一个已经解析到服务器的域名</li><li>服务器已经放行 <code>80</code> 和 <code>443</code></li><li>电脑端已经安装 <code>tmux</code></li><li>服务器和电脑都已经安装 <code>@fengye404/termpilot</code></li></ul><p>推荐你按这个顺序推进:</p><ol><li>先让服务器上的 <code>termpilot relay</code> 跑起来</li><li>再让域名和 HTTPS 反代跑起来</li><li>再在电脑执行 <code>termpilot agent</code></li><li>最后用手机完成第一次配对</li></ol><h2 id="_3-运行模型" tabindex="-1">3. 运行模型 <a class="header-anchor" href="#_3-运行模型" aria-label="Permalink to &quot;3. 运行模型&quot;">​</a></h2><p>TermPilot 由三部分组成:</p><ul><li><code>relay</code>:运行在云服务器上,负责网页托管、WebSocket 中继、配对、设备权限和会话元数据</li><li><code>agent</code>:运行在你的电脑上,负责管理本地 <code>tmux</code> 会话并连接 relay</li><li><code>app</code>:手机浏览器直接打开 relay 域名,不需要单独安装 App</li></ul><p>推荐拓扑:</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>手机浏览器 --https/wss--&gt; 域名 / 反向代理 --&gt; relay</span></span>
2
+ <span class="line"><span> ^</span></span>
3
+ <span class="line"><span> |</span></span>
4
+ <span class="line"><span> agent --wss--&gt; /ws</span></span></code></pre></div><h2 id="_4-推荐部署模式" tabindex="-1">4. 推荐部署模式 <a class="header-anchor" href="#_4-推荐部署模式" aria-label="Permalink to &quot;4. 推荐部署模式&quot;">​</a></h2><h3 id="模式-a-最低成本验证" tabindex="-1">模式 A:最低成本验证 <a class="header-anchor" href="#模式-a-最低成本验证" aria-label="Permalink to &quot;模式 A:最低成本验证&quot;">​</a></h3><ul><li>服务器上直接运行 <code>termpilot relay</code></li><li>对外暴露 <code>8787</code></li><li>手机访问 <code>http://your-ip:8787</code></li><li>电脑连接 <code>ws://your-ip:8787/ws</code></li></ul><p>适合:</p><ul><li>自己先试通链路</li><li>不想先配置域名和 HTTPS</li></ul><p>缺点:</p><ul><li>没有 HTTPS/WSS</li><li>不适合长期使用</li></ul><h3 id="模式-b-推荐生产模式" tabindex="-1">模式 B:推荐生产模式 <a class="header-anchor" href="#模式-b-推荐生产模式" aria-label="Permalink to &quot;模式 B:推荐生产模式&quot;">​</a></h3><ul><li>服务器上运行 <code>termpilot relay</code></li><li>前面放一个反向代理,例如 Caddy</li><li>域名直接指向服务器</li><li>手机访问 <code>https://your-domain.com</code></li><li>电脑连接 <code>wss://your-domain.com/ws</code></li></ul><p>适合:</p><ul><li>个人长期使用</li><li>多设备跨网络访问</li><li>想降低手机端访问阻力</li></ul><h2 id="_5-生产部署步骤" tabindex="-1">5. 生产部署步骤 <a class="header-anchor" href="#_5-生产部署步骤" aria-label="Permalink to &quot;5. 生产部署步骤&quot;">​</a></h2><h3 id="_5-1-域名解析" tabindex="-1">5.1 域名解析 <a class="header-anchor" href="#_5-1-域名解析" aria-label="Permalink to &quot;5.1 域名解析&quot;">​</a></h3><p>把你的域名 A 记录指向服务器公网 IP,例如:</p><ul><li><code>fengye404.top -&gt; 你的服务器公网 IP</code></li></ul><h3 id="_5-2-服务器启动-relay" tabindex="-1">5.2 服务器启动 relay <a class="header-anchor" href="#_5-2-服务器启动-relay" aria-label="Permalink to &quot;5.2 服务器启动 relay&quot;">​</a></h3><p>最简单的后台启动:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span></span></code></pre></div><p>常用命令:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span></span>
5
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> stop</span></span>
6
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> run</span></span></code></pre></div><p>说明:</p><ul><li><code>termpilot relay</code> 或 <code>termpilot relay start</code>:后台启动</li><li><code>termpilot relay stop</code>:停止后台 relay</li><li><code>termpilot relay run</code>:前台运行,适合看日志</li></ul><p>默认监听:</p><ul><li><code>host=0.0.0.0</code></li><li><code>port=8787</code></li></ul><h3 id="_5-3-反向代理" tabindex="-1">5.3 反向代理 <a class="header-anchor" href="#_5-3-反向代理" aria-label="Permalink to &quot;5.3 反向代理&quot;">​</a></h3><p>推荐用 Caddy。最小配置如下:</p><div class="language-caddyfile vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">caddyfile</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>fengye404.top {</span></span>
7
+ <span class="line"><span> reverse_proxy 127.0.0.1:8787</span></span>
8
+ <span class="line"><span>}</span></span></code></pre></div><p>这会同时转发:</p><ul><li>网页请求 <code>/</code></li><li>WebSocket <code>/ws</code></li></ul><h3 id="_5-4-电脑启动-agent" tabindex="-1">5.4 电脑启动 agent <a class="header-anchor" href="#_5-4-电脑启动-agent" aria-label="Permalink to &quot;5.4 电脑启动 agent&quot;">​</a></h3><p>第一次:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span></span></code></pre></div><p>然后在终端里输入:</p><ol><li>relay 域名或 IP</li><li>端口,直接回车默认 <code>8787</code></li></ol><p>TermPilot 会自动:</p><ul><li>保存本地配置</li><li>后台启动 agent</li><li>输出一次性配对码</li></ul><p>以后日常:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span></span></code></pre></div><p>如果你只想重新生成一个配对码:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --pair</span></span></code></pre></div><h3 id="_5-5-首次上线后的验收" tabindex="-1">5.5 首次上线后的验收 <a class="header-anchor" href="#_5-5-首次上线后的验收" aria-label="Permalink to &quot;5.5 首次上线后的验收&quot;">​</a></h3><p>如果你刚完成一套新部署,建议按下面顺序验收:</p><ol><li>服务器执行 <code>termpilot relay</code>,确认后台已启动</li><li>服务器本机执行 <code>curl http://127.0.0.1:8787/health</code></li><li>手机打开 <code>https://your-domain.com</code></li><li>电脑执行 <code>termpilot agent</code></li><li>确认终端里已经打印出配对码</li><li>手机输入配对码并进入会话列表</li><li>电脑执行 <code>termpilot claude code</code></li><li>确认手机端能看到同一个会话的输出</li></ol><h2 id="_6-目录、数据与状态文件" tabindex="-1">6. 目录、数据与状态文件 <a class="header-anchor" href="#_6-目录、数据与状态文件" aria-label="Permalink to &quot;6. 目录、数据与状态文件&quot;">​</a></h2><p>默认状态目录:</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>~/.termpilot</span></span></code></pre></div><p>常见文件:</p><ul><li><code>config.json</code>:agent 本地保存的 relay 配置</li><li><code>agent-runtime.json</code>:后台 agent 运行时状态</li><li><code>relay-runtime.json</code>:后台 relay 运行时状态</li><li><code>agent.log</code>:agent 日志</li><li><code>relay.log</code>:relay 日志</li><li><code>state.json</code>:本地会话状态</li></ul><p>如果你想切换状态目录,可以设置:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">TERMPILOT_HOME</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">/your/path</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span></span>
9
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">TERMPILOT_HOME</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">/your/path</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span></span></code></pre></div><h2 id="_7-推荐的日常工作流" tabindex="-1">7. 推荐的日常工作流 <a class="header-anchor" href="#_7-推荐的日常工作流" aria-label="Permalink to &quot;7. 推荐的日常工作流&quot;">​</a></h2><h3 id="_7-1-服务器" tabindex="-1">7.1 服务器 <a class="header-anchor" href="#_7-1-服务器" aria-label="Permalink to &quot;7.1 服务器&quot;">​</a></h3><p>长期保持:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span></span></code></pre></div><p>只有排障时才用:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> run</span></span></code></pre></div><h3 id="_7-2-电脑" tabindex="-1">7.2 电脑 <a class="header-anchor" href="#_7-2-电脑" aria-label="Permalink to &quot;7.2 电脑&quot;">​</a></h3><p>日常只记住:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span></span></code></pre></div><p>如果你要跑任务,最短路径是:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> claude</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> code</span></span></code></pre></div><p>或者:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> open</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> code</span></span></code></pre></div><h3 id="_7-3-手机" tabindex="-1">7.3 手机 <a class="header-anchor" href="#_7-3-手机" aria-label="Permalink to &quot;7.3 手机&quot;">​</a></h3><p>长期固定访问:</p><ul><li><code>https://your-domain.com</code></li></ul><p>第一次用配对码,之后正常重连不应该要求重新配对。</p><h2 id="_8-运维动作速查" tabindex="-1">8. 运维动作速查 <a class="header-anchor" href="#_8-运维动作速查" aria-label="Permalink to &quot;8. 运维动作速查&quot;">​</a></h2><h3 id="_8-1-relay" tabindex="-1">8.1 relay <a class="header-anchor" href="#_8-1-relay" aria-label="Permalink to &quot;8.1 relay&quot;">​</a></h3><p>后台启动:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span></span></code></pre></div><p>查看前台日志:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> run</span></span></code></pre></div><p>停止后台 relay:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> stop</span></span></code></pre></div><h3 id="_8-2-agent" tabindex="-1">8.2 agent <a class="header-anchor" href="#_8-2-agent" aria-label="Permalink to &quot;8.2 agent&quot;">​</a></h3><p>按本机配置启动或查看状态:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span></span></code></pre></div><p>重新生成配对码:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --pair</span></span></code></pre></div><p>查看后台状态:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> status</span></span></code></pre></div><p>停止后台 agent:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> stop</span></span></code></pre></div><h3 id="_8-3-会话" tabindex="-1">8.3 会话 <a class="header-anchor" href="#_8-3-会话" aria-label="Permalink to &quot;8.3 会话&quot;">​</a></h3><p>直接启动常见任务:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> claude</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> code</span></span>
10
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> open</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> code</span></span></code></pre></div><p>手动管理会话:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> create</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> my-task</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --cwd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> /path/to/project</span></span>
11
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> list</span></span>
12
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> attach</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --sid</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">si</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">d</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&gt;</span></span>
13
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> kill</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --sid</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">si</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">d</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&gt;</span></span></code></pre></div><h2 id="_9-故障排查" tabindex="-1">9. 故障排查 <a class="header-anchor" href="#_9-故障排查" aria-label="Permalink to &quot;9. 故障排查&quot;">​</a></h2><h3 id="_9-1-手机打开域名-但页面进不去" tabindex="-1">9.1 手机打开域名,但页面进不去 <a class="header-anchor" href="#_9-1-手机打开域名-但页面进不去" aria-label="Permalink to &quot;9.1 手机打开域名,但页面进不去&quot;">​</a></h3><p>优先检查:</p><ul><li>DNS 是否已经生效</li><li><code>80</code> / <code>443</code> 是否放行</li><li>反向代理是否已启动</li><li><code>termpilot relay</code> 是否真的在跑</li></ul><p>服务器检查:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span></span>
14
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">curl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> http://127.0.0.1:8787/health</span></span></code></pre></div><h3 id="_9-2-电脑端执行-termpilot-agent-后手机还是看不到设备" tabindex="-1">9.2 电脑端执行 <code>termpilot agent</code> 后手机还是看不到设备 <a class="header-anchor" href="#_9-2-电脑端执行-termpilot-agent-后手机还是看不到设备" aria-label="Permalink to &quot;9.2 电脑端执行 \`termpilot agent\` 后手机还是看不到设备&quot;">​</a></h3><p>优先检查:</p><ul><li>电脑是否真的能连到 relay</li><li>agent 是否已经后台运行</li><li>是否第一次配对还没完成</li></ul><p>电脑检查:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span></span>
15
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> status</span></span></code></pre></div><h3 id="_9-3-手机端看不到某个任务" tabindex="-1">9.3 手机端看不到某个任务 <a class="header-anchor" href="#_9-3-手机端看不到某个任务" aria-label="Permalink to &quot;9.3 手机端看不到某个任务&quot;">​</a></h3><p>最常见原因:</p><ul><li>这个任务不是通过 TermPilot 管理的会话启动的</li></ul><p>正确做法:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> claude</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> code</span></span></code></pre></div><p>或:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> create</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> my-task</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --cwd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> /path/to/project</span></span>
16
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> attach</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --sid</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">si</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">d</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&gt;</span></span></code></pre></div><h3 id="_9-4-想重新绑定手机" tabindex="-1">9.4 想重新绑定手机 <a class="header-anchor" href="#_9-4-想重新绑定手机" aria-label="Permalink to &quot;9.4 想重新绑定手机&quot;">​</a></h3><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --pair</span></span></code></pre></div><p>如果要撤销旧设备访问令牌:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> grants</span></span>
17
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> revoke</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --token</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">accessToke</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">n</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&gt;</span></span></code></pre></div><h3 id="_9-5-relay-或-agent-想看实时日志" tabindex="-1">9.5 relay 或 agent 想看实时日志 <a class="header-anchor" href="#_9-5-relay-或-agent-想看实时日志" aria-label="Permalink to &quot;9.5 relay 或 agent 想看实时日志&quot;">​</a></h3><p>relay:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> relay</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> run</span></span></code></pre></div><p>agent:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> --foreground</span></span></code></pre></div><h3 id="_9-6-我改了域名或端口-电脑还是连旧地址" tabindex="-1">9.6 我改了域名或端口,电脑还是连旧地址 <a class="header-anchor" href="#_9-6-我改了域名或端口-电脑还是连旧地址" aria-label="Permalink to &quot;9.6 我改了域名或端口,电脑还是连旧地址&quot;">​</a></h3><p>先停掉后台 agent,再重新执行一次交互配置:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> stop</span></span>
18
+ <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">termpilot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> agent</span></span></code></pre></div><p>如果你使用了自定义状态目录,也要确认是不是改到了另一个 <code>TERMPILOT_HOME</code>。</p><h2 id="_10-安全建议" tabindex="-1">10. 安全建议 <a class="header-anchor" href="#_10-安全建议" aria-label="Permalink to &quot;10. 安全建议&quot;">​</a></h2><ul><li>正式环境优先使用域名 + HTTPS/WSS,不要长期裸露 <code>ws://ip:8787/ws</code></li><li>不要长期传播手机端访问令牌</li><li>换手机或多人共享设备后,及时撤销旧令牌</li><li>如果准备长期保存会话元数据,relay 建议接 PostgreSQL</li><li>不要把外网入口直接暴露到非预期端口和无 TLS 配置上</li></ul><h2 id="_11-升级建议" tabindex="-1">11. 升级建议 <a class="header-anchor" href="#_11-升级建议" aria-label="Permalink to &quot;11. 升级建议&quot;">​</a></h2><p>推荐的升级节奏:</p><ol><li>先在一台日常不关键的机器上升级验证</li><li>确认 <code>termpilot relay</code> 和 <code>termpilot agent</code> 都能正常启动</li><li>用手机完成一次真实配对和会话查看</li><li>再升级主力机器</li></ol><p>如果升级后出现异常,优先检查:</p><ul><li><code>~/.termpilot/relay.log</code></li><li><code>~/.termpilot/agent.log</code></li><li><code>curl http://127.0.0.1:8787/health</code></li><li><code>termpilot agent status</code></li></ul><h2 id="_12-建议的文档阅读顺序" tabindex="-1">12. 建议的文档阅读顺序 <a class="header-anchor" href="#_12-建议的文档阅读顺序" aria-label="Permalink to &quot;12. 建议的文档阅读顺序&quot;">​</a></h2><ol><li><a href="./getting-started">快速开始</a></li><li><a href="./architecture">代码架构</a></li><li><a href="./protocol">协议说明</a></li><li>本文档</li></ol>`,152)])])}const g=i(t,[["render",p]]);export{c as __pageData,g as default};
@@ -0,0 +1 @@
1
+ import{_ as i,o as s,c as e,ag as l}from"./chunks/framework.BZohXCq9.js";const c=JSON.parse('{"title":"TermPilot 部署与运维指南","description":"","frontmatter":{},"headers":[],"relativePath":"operations-guide.md","filePath":"operations-guide.md","lastUpdated":1773250545000}'),t={name:"operations-guide.md"};function p(h,a,n,d,o,r){return s(),e("div",null,[...a[0]||(a[0]=[l("",152)])])}const g=i(t,[["render",p]]);export{c as __pageData,g as default};
@@ -0,0 +1,40 @@
1
+ import{_ as i,o as a,c as e,ag as l}from"./chunks/framework.BZohXCq9.js";const r=JSON.parse('{"title":"TermPilot 当前协议","description":"","frontmatter":{},"headers":[],"relativePath":"protocol.md","filePath":"protocol.md","lastUpdated":1773157981000}'),t={name:"protocol.md"};function n(h,s,p,o,d,k){return a(),e("div",null,[...s[0]||(s[0]=[l(`<h1 id="termpilot-当前协议" tabindex="-1">TermPilot 当前协议 <a class="header-anchor" href="#termpilot-当前协议" aria-label="Permalink to &quot;TermPilot 当前协议&quot;">​</a></h1><h2 id="_1-连接入口" tabindex="-1">1. 连接入口 <a class="header-anchor" href="#_1-连接入口" aria-label="Permalink to &quot;1. 连接入口&quot;">​</a></h2><p>手机端和 agent 都通过 relay 的同一个 WebSocket 入口连接:</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>ws(s)://&lt;relay-host&gt;/ws?role=&lt;agent|client&gt;&amp;token=&lt;token&gt;&amp;deviceId=&lt;deviceId?&gt;</span></span></code></pre></div><ul><li><code>role=agent</code>:PC 端 agent</li><li><code>role=client</code>:手机端或浏览器端</li><li><code>token</code>:agent 固定令牌或 client 访问令牌</li><li><code>deviceId</code>:agent 连接时必须带;client 在业务消息里指定</li></ul><h2 id="_2-当前消息类型" tabindex="-1">2. 当前消息类型 <a class="header-anchor" href="#_2-当前消息类型" aria-label="Permalink to &quot;2. 当前消息类型&quot;">​</a></h2><p>系统消息:</p><ul><li><code>auth.ok</code></li><li><code>error</code></li><li><code>relay.state</code></li></ul><p>会话消息:</p><ul><li><code>session.list</code></li><li><code>session.list.result</code></li><li><code>session.create</code></li><li><code>session.created</code></li><li><code>session.input</code></li><li><code>session.resize</code></li><li><code>session.kill</code></li><li><code>session.replay</code></li><li><code>session.output</code></li><li><code>session.state</code></li><li><code>session.exit</code></li></ul><h2 id="_3-会话模型" tabindex="-1">3. 会话模型 <a class="header-anchor" href="#_3-会话模型" aria-label="Permalink to &quot;3. 会话模型&quot;">​</a></h2><p>当前会话对象字段:</p><ul><li><code>sid</code></li><li><code>deviceId</code></li><li><code>name</code></li><li><code>backend</code></li><li><code>shell</code></li><li><code>cwd</code></li><li><code>status</code></li><li><code>startedAt</code></li><li><code>lastSeq</code></li><li><code>lastActivityAt</code></li><li><code>tmuxSessionName</code></li></ul><p>当前 <code>backend</code> 固定为 <code>tmux</code>。</p><h2 id="_4-输入与快捷键" tabindex="-1">4. 输入与快捷键 <a class="header-anchor" href="#_4-输入与快捷键" aria-label="Permalink to &quot;4. 输入与快捷键&quot;">​</a></h2><p>当前支持的特殊按键:</p><ul><li><code>enter</code></li><li><code>tab</code></li><li><code>ctrl_c</code></li><li><code>ctrl_d</code></li><li><code>escape</code></li><li><code>arrow_up</code></li><li><code>arrow_down</code></li><li><code>arrow_left</code></li><li><code>arrow_right</code></li></ul><p>普通输入仍走:</p><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span></span>
2
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;type&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;session.input&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
3
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;deviceId&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;pc-main&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
4
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;sid&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;s_123&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
5
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;payload&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
6
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;text&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;echo hello</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">\\n</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;</span></span>
7
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
8
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><h2 id="_5-输出同步策略" tabindex="-1">5. 输出同步策略 <a class="header-anchor" href="#_5-输出同步策略" aria-label="Permalink to &quot;5. 输出同步策略&quot;">​</a></h2><p>当前不是字节级终端流,而是“快照替换”:</p><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span></span>
9
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;type&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;session.output&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
10
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;deviceId&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;pc-main&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
11
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;sid&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;s_123&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
12
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;seq&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">12</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
13
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;payload&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
14
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;data&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;...当前 pane 快照...&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
15
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;mode&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;replace&quot;</span></span>
16
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
17
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><p>对应实现:</p><ul><li>agent 轮询 <code>tmux capture-pane</code></li><li>只有缓冲变化时才推新帧</li><li>relay 保留最近一段输出帧</li><li>client 重连后用 <code>session.replay</code> 补拉</li></ul><h2 id="_6-http-接口" tabindex="-1">6. HTTP 接口 <a class="header-anchor" href="#_6-http-接口" aria-label="Permalink to &quot;6. HTTP 接口&quot;">​</a></h2><h3 id="创建一次性配对码" tabindex="-1">创建一次性配对码 <a class="header-anchor" href="#创建一次性配对码" aria-label="Permalink to &quot;创建一次性配对码&quot;">​</a></h3><p><code>POST /api/pairing-codes</code></p><ul><li>需要 <code>Authorization: Bearer &lt;agent-token&gt;</code></li><li>请求体:<code>{ &quot;deviceId&quot;: &quot;pc-main&quot; }</code></li></ul><h3 id="兑换配对码" tabindex="-1">兑换配对码 <a class="header-anchor" href="#兑换配对码" aria-label="Permalink to &quot;兑换配对码&quot;">​</a></h3><p><code>POST /api/pairings/redeem</code></p><ul><li>请求体:<code>{ &quot;pairingCode&quot;: &quot;ABC-234&quot; }</code></li></ul><h3 id="查看当前设备已发出的访问令牌" tabindex="-1">查看当前设备已发出的访问令牌 <a class="header-anchor" href="#查看当前设备已发出的访问令牌" aria-label="Permalink to &quot;查看当前设备已发出的访问令牌&quot;">​</a></h3><p><code>GET /api/devices/:deviceId/grants</code></p><h3 id="撤销访问令牌" tabindex="-1">撤销访问令牌 <a class="header-anchor" href="#撤销访问令牌" aria-label="Permalink to &quot;撤销访问令牌&quot;">​</a></h3><p><code>DELETE /api/devices/:deviceId/grants/:accessToken</code></p><h3 id="查看审计事件" tabindex="-1">查看审计事件 <a class="header-anchor" href="#查看审计事件" aria-label="Permalink to &quot;查看审计事件&quot;">​</a></h3><p><code>GET /api/devices/:deviceId/audit-events?limit=20</code></p><h3 id="健康检查" tabindex="-1">健康检查 <a class="header-anchor" href="#健康检查" aria-label="Permalink to &quot;健康检查&quot;">​</a></h3><p><code>GET /health</code></p><p>返回:</p><ul><li><code>ok</code></li><li><code>storeMode</code></li><li><code>agentsOnline</code></li><li><code>clientsOnline</code></li><li><code>webUiReady</code></li></ul><h2 id="_7-当前产品入口与协议的关系" tabindex="-1">7. 当前产品入口与协议的关系 <a class="header-anchor" href="#_7-当前产品入口与协议的关系" aria-label="Permalink to &quot;7. 当前产品入口与协议的关系&quot;">​</a></h2><ul><li><code>termpilot relay</code> 对外暴露 HTTP + WebSocket</li><li>手机端直接访问 relay 域名,再走 <code>/ws</code></li><li><code>termpilot agent</code> 始终只和 <code>/ws</code>、<code>/api/*</code> 交互</li><li>手机端网页不再要求单独部署</li></ul><p>当前审计动作包括:</p><ul><li><code>pairing.code_created</code></li><li><code>pairing.redeemed</code></li><li><code>grant.revoked</code></li><li><code>session.create_requested</code></li><li><code>session.kill_requested</code></li></ul><h2 id="_8-当前协议边界" tabindex="-1">8. 当前协议边界 <a class="header-anchor" href="#_8-当前协议边界" aria-label="Permalink to &quot;8. 当前协议边界&quot;">​</a></h2><ul><li>client 侧仍直接处理较多状态拼装,没有事件聚合接口</li><li>输出补拉依赖最近缓冲,不是完整历史回放</li><li>审计目前只记录关键控制动作,不记录每一次普通输入</li></ul><p>字段说明:</p><ul><li><code>seq</code>:输出序号,递增</li><li><code>data</code>:当前终端快照</li><li><code>mode=replace</code>:客户端收到后直接替换当前渲染内容</li></ul><h2 id="_9-会话状态消息" tabindex="-1">9. 会话状态消息 <a class="header-anchor" href="#_9-会话状态消息" aria-label="Permalink to &quot;9. 会话状态消息&quot;">​</a></h2><p>会话状态变化时发送:</p><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span></span>
18
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;type&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;session.state&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
19
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;deviceId&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;pc-main&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
20
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;sid&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;s_123&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
21
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;payload&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
22
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;session&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {}</span></span>
23
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
24
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><p>会话退出时发送:</p><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span></span>
25
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;type&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;session.exit&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
26
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;deviceId&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;pc-main&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
27
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;sid&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;s_123&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
28
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;payload&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
29
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;reason&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;用户主动关闭会话&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
30
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;exitCode&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">null</span></span>
31
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
32
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><h2 id="_10-输出补拉" tabindex="-1">10. 输出补拉 <a class="header-anchor" href="#_10-输出补拉" aria-label="Permalink to &quot;10. 输出补拉&quot;">​</a></h2><p>手机端重连或重新进入会话时,可以请求补拉最近输出:</p><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span></span>
33
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;type&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;session.replay&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
34
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;reqId&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;req_replay_001&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
35
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;deviceId&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;pc-main&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
36
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;sid&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;s_123&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
37
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;payload&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
38
+ <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;afterSeq&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">8</span></span>
39
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
40
+ <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div>`,57)])])}const E=i(t,[["render",n]]);export{r as __pageData,E as default};
@@ -0,0 +1 @@
1
+ import{_ as i,o as a,c as e,ag as l}from"./chunks/framework.BZohXCq9.js";const r=JSON.parse('{"title":"TermPilot 当前协议","description":"","frontmatter":{},"headers":[],"relativePath":"protocol.md","filePath":"protocol.md","lastUpdated":1773157981000}'),t={name:"protocol.md"};function n(h,s,p,o,d,k){return a(),e("div",null,[...s[0]||(s[0]=[l("",57)])])}const E=i(t,[["render",n]]);export{r as __pageData,E as default};