@longleash/cli 0.1.0-rc.5 → 0.1.0-rc.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/longleash.mjs +111 -13
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/runtime/app/dist/assets/app-DS3tqdbW.js +27 -0
- package/runtime/app/dist/assets/{welcome-D-UkLoke.js → welcome-CitqOflI.js} +19 -10
- package/runtime/app/dist/build.json +1 -1
- package/runtime/app/dist/index.html +1 -1
- package/runtime/app/dist/welcome.html +1 -1
- package/runtime/daemon/bin/longleashd.mjs +24 -5
- package/runtime/app/dist/assets/app-CsfqXy-8.js +0 -27
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{d,e,r as
|
|
1
|
+
import{d as l,e,r as f,y as A,A as p,s as x,t as j,W as N,k as m,C as S,v as E,p as G,m as O,l as Y,S as $,z as _}from"./styles-C-1gzhnT.js";const U=[["path",{d:"M10.268 21a2 2 0 0 0 3.464 0",key:"vwvbt9"}],["path",{d:"M22 8c0-2.3-.8-4.3-2-6",key:"5bb3ad"}],["path",{d:"M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326",key:"11g9vi"}],["path",{d:"M4 2C2.8 3.7 2 5.7 2 8",key:"tap9e0"}]],D=l("bell-ring",U);const B=[["path",{d:"M12 5v16",key:"1f6ucr"}],["path",{d:"M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z",key:"1fyvmf"}]],q=l("book-open",B);const Q=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],K=l("circle-question-mark",Q);const X=[["path",{d:"M22 7.7c0-.6-.4-1.2-.8-1.5l-6.3-3.9a1.72 1.72 0 0 0-1.7 0l-10.3 6c-.5.2-.9.8-.9 1.4v6.6c0 .5.4 1.2.8 1.5l6.3 3.9a1.72 1.72 0 0 0 1.7 0l10.3-6c.5-.3.9-1 .9-1.5Z",key:"1t2lqe"}],["path",{d:"M10 21.9V14L2.1 9.1",key:"o7czzq"}],["path",{d:"m10 14 11.9-6.9",key:"zm5e20"}],["path",{d:"M14 19.8v-8.1",key:"159ecu"}],["path",{d:"M18 17.5V9.4",key:"11uown"}]],Z=l("container",X);const J=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],ee=l("external-link",J);const se=[["path",{d:"M4 12.15V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-3.35",key:"1wthlu"}],["path",{d:"M14 2v5a1 1 0 0 0 1 1h5",key:"wfsgrz"}],["path",{d:"m5 16-3 3 3 3",key:"331omg"}],["path",{d:"m9 22 3-3-3-3",key:"lsp7cz"}]],I=l("file-code-corner",se);const ne=[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2",key:"usdka0"}]],ie=l("folder-open",ne);const re=[["path",{d:"M15 6a9 9 0 0 0-9 9V3",key:"1cii5b"}],["circle",{cx:"18",cy:"6",r:"3",key:"1h7g24"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}]],T=l("git-branch",re);const ae=[["polyline",{points:"22 12 16 12 14 15 10 15 8 12 2 12",key:"o97t9d"}],["path",{d:"M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z",key:"oot6mr"}]],M=l("inbox",ae);const oe=[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 10 0v4",key:"fwvmzm"}]],L=l("lock",oe);const te=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],le=l("refresh-cw",te);const ce=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]],de=l("server",ce);const he=[["path",{d:"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",key:"1i5ecw"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]],W=l("settings",he);const pe=[["rect",{width:"14",height:"20",x:"5",y:"2",rx:"2",ry:"2",key:"1yt0o3"}],["path",{d:"M12 18h.01",key:"mhygvu"}]],g=l("smartphone",pe);const ue=[["line",{x1:"10",x2:"14",y1:"2",y2:"2",key:"14vaq8"}],["line",{x1:"12",x2:"15",y1:"14",y2:"11",key:"17fdiu"}],["circle",{cx:"12",cy:"14",r:"8",key:"1e1u0o"}]],me=l("timer",ue),d="https://github.com/Sahith59/LongLe-sh",R="npm exec --yes --package=@longleash/cli@rc -- longleash setup";function xe(s){const n=s.indexOf("#");return n===-1?{pathname:s,hash:""}:{pathname:s.slice(0,n),hash:s.slice(n)}}function r(s){return typeof window>"u"?s:je(s,window.location)}function je(s,n){const{pathname:i,hash:a}=xe(s);return n.hostname.endsWith(".workers.dev")?`${i==="/"?"/welcome":`/welcome${i}`}${a}`:n.pathname.endsWith("/welcome.html")?`/welcome.html?site=${encodeURIComponent(i==="/"?"/":i)}${a}`:`${i}${a}`}function ye(){return typeof window>"u"?"/":fe(window.location)}function fe(s){let i=new URLSearchParams(s.search).get("site")??s.pathname;return i==="/welcome"||i==="/welcome/"||i==="/welcome.html"?"/":(i.startsWith("/welcome/")&&(i=i.slice(8)),i=i.replace(/\/+$/,""),i||"/")}function P(){if(typeof window>"u")return"/";const{hostname:s,protocol:n}=window.location;if(s==="localhost"||s==="127.0.0.1"||s.endsWith(".workers.dev")||s.startsWith("app."))return"/";const i=s.startsWith("www.")?s.slice(4):s;return`${n}//app.${i}`}function H(){return e.jsxs("header",{className:"land-rail",children:[e.jsxs("a",{className:"land-mark",href:r("/"),"aria-label":"LongLeash home",children:[e.jsx("img",{src:"/icon-192.png",alt:"",width:32,height:32}),"Long",e.jsx("i",{children:"Leash"})]}),e.jsxs("nav",{className:"land-nav","aria-label":"Primary navigation",children:[e.jsx("a",{href:r("/#product"),children:"Product"}),e.jsx("a",{href:r("/#start"),children:"Setup"}),e.jsx("a",{href:r("/docs"),children:"Docs"}),e.jsx("a",{href:r("/docs/connectivity"),children:"Connectivity"}),e.jsx("a",{href:r("/docs/self-hosting"),children:"Self-hosting"}),e.jsx("a",{href:r("/roadmap"),children:"Roadmap"})]}),e.jsxs("div",{className:"land-actions",children:[e.jsx("a",{className:"land-icon-link",href:d,"aria-label":"View source on GitHub",children:e.jsx(ee,{size:18,"aria-hidden":"true"})}),e.jsx("a",{className:"key sm",href:P(),children:"Open app"})]})]})}function z({children:s}){return e.jsxs("footer",{className:"land-foot",children:[e.jsxs("a",{className:"foot-brand",href:r("/"),"aria-label":"LongLeash home",children:[e.jsx("img",{src:"/icon-192.png",alt:"",width:26,height:26}),e.jsxs("span",{children:["Long",e.jsx("i",{children:"Leash"})]})]}),s,e.jsxs("nav",{className:"foot-links","aria-label":"Footer navigation",children:[e.jsx("a",{href:r("/docs"),children:"Documentation"}),e.jsx("a",{href:r("/docs/connectivity"),children:"Connectivity"}),e.jsx("a",{href:r("/roadmap"),children:"Roadmap"}),e.jsx("a",{href:r("/license"),children:"License"}),e.jsx("a",{href:r("/privacy"),children:"Privacy"}),e.jsx("a",{href:r("/terms"),children:"Terms"}),e.jsx("a",{href:d,children:"Source on GitHub"}),e.jsxs("span",{className:"mono",children:["build ","3f15e73"]})]})]})}function ge({children:s}){return e.jsxs("div",{className:"land",children:[e.jsx("a",{className:"skip-link",href:"#main",children:"Skip to content"}),e.jsx(H,{}),s,e.jsx(z,{})]})}const V=[{path:"/docs/getting-started",label:"Getting started",description:"Install, pair, verify, and complete one safe first run.",icon:q},{path:"/docs/connectivity",label:"Connectivity choices",description:"Compare the hosted relay, your own relay, and LAN-only operation.",icon:x},{path:"/docs/self-hosting",label:"Self-hosting operations",description:"Run Cloudflare or Docker safely, with TLS, monitoring, updates, and recovery.",icon:de},{path:"/docs/background-service",label:"Background service",description:"Keep the laptop reachable after closing Terminal; inspect, stop, or remove it safely.",icon:A},{path:"/docs/daily-use",label:"Daily use",description:"Sessions, approvals, tuning, handoffs, updates, and stopping.",icon:g},{path:"/docs/troubleshooting",label:"Troubleshooting",description:"Symptom-first recovery that preserves the evidence needed to diagnose a bug.",icon:N},{path:"/docs/security",label:"Security model",description:"What stays local, what the relay sees, and how device authority works.",icon:j},{path:"/docs/session-portability",label:"Session portability",description:"Move work safely between phone, Terminal, and VS Code.",icon:m},{path:"/docs/faq",label:"FAQ",description:"Straight answers about accounts, providers, platforms, cost, and limitations.",icon:K}],ve={"/docs":"Documentation","/docs/getting-started":"Getting started","/docs/connectivity":"Connectivity choices","/docs/self-hosting":"Self-hosting operations","/docs/background-service":"Background service","/docs/daily-use":"Daily use","/docs/troubleshooting":"Troubleshooting","/docs/security":"Security model","/docs/session-portability":"Session portability","/docs/faq":"Frequently asked questions","/roadmap":"Roadmap","/license":"MIT License","/privacy":"Privacy","/terms":"Terms of Service"},be=`MIT License
|
|
2
2
|
|
|
3
3
|
Copyright (c) 2026 Sahith Reddy Thummala
|
|
4
4
|
|
|
@@ -18,14 +18,23 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.`;function
|
|
22
|
-
longleash service logs`}),e.jsxs("p",{children:["Then follow the ",e.jsx("a",{href:
|
|
23
|
-
pnpm --filter @longleash/relay check:selfhost`}),e.jsx("ol",{start:4,children:e.jsxs("li",{children:["Deploy only after the dry run lists ",e.jsx("code",{children:"ROOM"})," and ",e.jsx("code",{children:"ASSETS"}),", with no LongLeash production domains or Clerk bindings."]})}),e.jsx(
|
|
24
|
-
longleash
|
|
21
|
+
SOFTWARE.`;function we({path:s}){f.useEffect(()=>{const i=ve[s];document.title=i?`${i} | LongLeash`:"Page not found | LongLeash",window.scrollTo(0,0);const a=window.location.hash.slice(1);a&&window.requestAnimationFrame(()=>document.getElementById(a)?.scrollIntoView())},[s]);let n;switch(s){case"/docs":n=e.jsx(ke,{});break;case"/docs/getting-started":n=e.jsx(Le,{});break;case"/docs/connectivity":n=e.jsx(Ne,{});break;case"/docs/self-hosting":n=e.jsx(Se,{});break;case"/docs/background-service":n=e.jsx(Te,{});break;case"/docs/daily-use":n=e.jsx(Ce,{});break;case"/docs/troubleshooting":n=e.jsx(Re,{});break;case"/docs/security":n=e.jsx(Ae,{});break;case"/docs/session-portability":n=e.jsx(Ie,{});break;case"/docs/faq":n=e.jsx(Pe,{});break;case"/roadmap":n=e.jsx(Oe,{});break;case"/license":n=e.jsx(qe,{});break;case"/privacy":n=e.jsx(Ee,{});break;case"/terms":n=e.jsx(De,{});break;default:n=e.jsx(Me,{})}return e.jsx(ge,{children:n})}function c({path:s,eyebrow:n="Documentation",title:i,summary:a,children:t}){return e.jsxs("main",{className:"public-main",id:"main",children:[e.jsxs("header",{className:"page-intro",children:[e.jsx("p",{className:"land-label",children:n}),e.jsx("h1",{children:i}),e.jsx("p",{children:a})]}),e.jsxs("div",{className:"docs-shell",children:[e.jsxs("aside",{className:"docs-sidebar","aria-label":"Documentation sections",children:[e.jsx("a",{className:s==="/docs"?"active":"",href:r("/docs"),children:"Overview"}),V.map(h=>e.jsx("a",{className:s===h.path?"active":"",href:r(h.path),"aria-current":s===h.path?"page":void 0,children:h.label},h.path)),e.jsx("span",{className:"docs-nav-rule"}),e.jsx("a",{className:s==="/roadmap"?"active":"",href:r("/roadmap"),children:"Roadmap"}),e.jsx("a",{className:s==="/privacy"?"active":"",href:r("/privacy"),children:"Privacy"}),e.jsx("a",{className:s==="/license"?"active":"",href:r("/license"),children:"License"})]}),e.jsx("article",{className:"docs-article doc-prose",children:t})]})]})}function ke(){return e.jsxs(c,{path:"/docs",title:"Use LongLeash with confidence.",summary:"Clear paths for setup, everyday control, recovery, security review, and moving a real conversation between surfaces.",children:[e.jsx("h2",{id:"mental-model",children:"The 45-second mental model"}),e.jsxs("div",{className:"mental-model","aria-label":"LongLeash system model",children:[e.jsx(C,{icon:A,title:"Laptop",body:"Runs the daemon, agents, code, and credentials."}),e.jsx(p,{"aria-hidden":"true"}),e.jsx(C,{icon:x,title:"Relay",body:"Routes end-to-end encrypted frames; it cannot read them."}),e.jsx(p,{"aria-hidden":"true"}),e.jsx(C,{icon:g,title:"Phone",body:"Shows sessions and sends small, typed decisions."})]}),e.jsxs("div",{className:"doc-callout safe",children:[e.jsx(j,{size:19,"aria-hidden":"true"}),e.jsxs("p",{children:[e.jsx("b",{children:"Your laptop remains the authority."})," LongLeash is not a cloud coding environment and does not upload your repository, provider login, or transcript database."]})]}),e.jsx("h2",{children:"Choose a guide"}),e.jsx("div",{className:"guide-grid",children:V.map(({path:s,label:n,description:i,icon:a})=>e.jsxs("a",{className:"guide-card",href:r(s),children:[e.jsx("span",{className:"doc-icon",children:e.jsx(a,{size:19,"aria-hidden":"true"})}),e.jsxs("span",{children:[e.jsx("b",{children:n}),e.jsx("small",{children:i})]}),e.jsx(p,{size:16,"aria-hidden":"true"})]},s))}),e.jsx("h2",{children:"When something feels wrong"}),e.jsx("p",{children:"Do not repeatedly restart, pair, or kill processes. Preserve the first useful evidence:"}),e.jsx(o,{command:`longleash doctor
|
|
22
|
+
longleash service logs`}),e.jsxs("p",{children:["Then follow the ",e.jsx("a",{href:r("/docs/troubleshooting"),children:"symptom-first recovery guide"}),". Pairing URLs are secrets. Remove them before sharing a screenshot."]})]})}function Le(){return e.jsxs(c,{path:"/docs/getting-started",title:"Laptop first. Phone second.",summary:"Install the local daemon, pair one phone, and prove the whole loop on a harmless task before trusting it with important work.",children:[e.jsx("h2",{id:"requirements",children:"Before you install"}),e.jsxs("ul",{className:"check-list",children:[e.jsx("li",{children:"macOS or Linux with Node.js 22 or newer and Git."}),e.jsx("li",{children:"Claude Code, Codex, or both already installed and signed in."}),e.jsx("li",{children:"A normal signed-in user session; LongLeash does not install a root daemon."})]}),e.jsx("p",{children:"LongLeash does not include an AI subscription and never asks for a provider API key."}),e.jsx("h2",{id:"install",children:"1. Install"}),e.jsx(o,{command:R}),e.jsxs("p",{children:["Setup verifies the pinned npm package, installs under your home directory, reviews roots and connectivity, and configures supported lifecycle hooks. It does not use ",e.jsx("code",{children:"sudo"}),". You can inspect the ",e.jsx("a",{href:`${d}/tree/main/packages/cli`,children:"CLI package source"})," and"," ",e.jsx("a",{href:`${d}/blob/main/docs/NPM-RELEASE.md`,children:"release controls"})," before running it."]}),e.jsx("h2",{id:"start",children:"2. Configure the laptop service"}),e.jsx(o,{command:"longleash setup"}),e.jsxs("p",{children:["Review the allowed folders, connectivity choice, and login-start behavior before accepting. At the folder question, press Enter to accept the displayed folder or type an existing directory path; answer ",e.jsx("code",{children:"y"})," only when the prompt explicitly shows a yes/no choice. The recommended per-user service keeps the laptop reachable after this terminal closes. Use ",e.jsx("code",{children:"longleash run"})," only when you deliberately want foreground diagnosis."]}),e.jsx("h2",{id:"pair",children:"3. Install, sign in, then pair the phone app"}),e.jsxs("ol",{children:[e.jsxs("li",{children:["On iPhone, open ",e.jsx("a",{href:P(),children:"the hosted app"})," in Safari, use ",e.jsx("b",{children:"Share → Add to Home Screen"}),", then open LongLeash from the new home-screen icon."]}),e.jsx("li",{children:"Sign in inside that installed app with Google, an email code, or email and password."}),e.jsxs("li",{children:["Choose ",e.jsx("b",{children:"Scan the QR"})," inside the installed app, not from the iPhone Camera app."]}),e.jsx("li",{children:"Scan the fresh QR shown by the daemon. Keep its entire quiet border in view."}),e.jsxs("li",{children:["Require the header to show ",e.jsx("code",{children:"linked · relay"})," or ",e.jsx("code",{children:"linked · direct"}),"."]})]}),e.jsx("p",{children:"If you opened or paired the link in an ordinary Safari tab first, nothing unsafe happened, but that tab and the installed app may not share credentials. Open the home-screen app and scan a fresh QR there instead."}),e.jsxs("div",{className:"doc-callout warn",children:[e.jsx(L,{size:19,"aria-hidden":"true"}),e.jsxs("p",{children:[e.jsx("b",{children:"A pairing link is a single-use secret."})," If it was completed, exposed, or failed in another browser, run ",e.jsx("code",{children:"longleash pair"})," for a fresh one."]})]}),e.jsx("h2",{id:"verify",children:"4. Verify the installation"}),e.jsx(o,{command:"longleash doctor"}),e.jsx("p",{children:"The daemon must be reachable, installed hooks current, and the app, daemon, and relay builds aligned."}),e.jsx("h2",{id:"first-test",children:"5. Complete one safe first test"}),e.jsxs("ol",{children:[e.jsx("li",{children:"Start Claude or Codex on a disposable folder."}),e.jsx("li",{children:"Ask it to inspect files and trigger one harmless approval."}),e.jsx("li",{children:"Answer from the phone and confirm the correct session continues."}),e.jsx("li",{children:"Stop it, reveal the handoff, and copy the native resume command."}),e.jsx("li",{children:"Resume at the laptop and confirm the same conversation is present."})]}),e.jsxs("p",{children:["If any step is ambiguous, stop there and use ",e.jsx("a",{href:r("/docs/troubleshooting"),children:"Troubleshooting"}),"."]})]})}function Ne(){return e.jsxs(c,{path:"/docs/connectivity",title:"Choose who operates the path.",summary:"Your agents and code stay on the laptop in every mode. The difference is how the phone reaches that laptop and who keeps that route online.",children:[e.jsxs("div",{className:"mode-summary","aria-label":"LongLeash connectivity modes",children:[e.jsxs("section",{children:[e.jsx("span",{className:"doc-icon",children:e.jsx(x,{size:19,"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("b",{children:"Hosted relay"}),e.jsx("small",{children:"Fastest setup. LongLeash operates the encrypted route."})]})]}),e.jsxs("section",{children:[e.jsx("span",{className:"doc-icon",children:e.jsx(A,{size:19,"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("b",{children:"Self-hosted relay"}),e.jsx("small",{children:"Remote access through infrastructure you operate."})]})]}),e.jsxs("section",{children:[e.jsx("span",{className:"doc-icon",children:e.jsx(g,{size:19,"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("b",{children:"LAN-only"}),e.jsx("small",{children:"No relay. Phone and laptop must share a trusted network."})]})]})]}),e.jsx("h2",{id:"compare",children:"Compare the three modes"}),e.jsx("div",{className:"comparison-scroll",role:"region","aria-label":"Connectivity mode comparison",tabIndex:0,children:e.jsxs("table",{className:"mode-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{scope:"col",children:"Question"}),e.jsx("th",{scope:"col",children:"Hosted relay"}),e.jsx("th",{scope:"col",children:"Self-hosted relay"}),e.jsx("th",{scope:"col",children:"LAN-only"})]})}),e.jsxs("tbody",{children:[e.jsxs("tr",{children:[e.jsx("th",{scope:"row",children:"Works away from home"}),e.jsx("td",{children:"Yes"}),e.jsx("td",{children:"Yes, when your relay is internet reachable"}),e.jsx("td",{children:"No"})]}),e.jsxs("tr",{children:[e.jsx("th",{scope:"row",children:"Internet service operator"}),e.jsx("td",{children:"LongLeash"}),e.jsx("td",{children:"You"}),e.jsx("td",{children:"None"})]}),e.jsxs("tr",{children:[e.jsx("th",{scope:"row",children:"LongLeash account"}),e.jsx("td",{children:"Required for the official hosted service"}),e.jsx("td",{children:"Not required by the reference relay"}),e.jsx("td",{children:"Not required"})]}),e.jsxs("tr",{children:[e.jsx("th",{scope:"row",children:"Can the relay read content"}),e.jsx("td",{children:"No"}),e.jsx("td",{children:"No, when the reference protocol is unchanged"}),e.jsx("td",{children:"Not applicable"})]}),e.jsxs("tr",{children:[e.jsx("th",{scope:"row",children:"Who manages TLS and uptime"}),e.jsx("td",{children:"LongLeash"}),e.jsx("td",{children:"You"}),e.jsx("td",{children:"Not applicable"})]}),e.jsxs("tr",{children:[e.jsx("th",{scope:"row",children:"Best fit"}),e.jsx("td",{children:"Quick, managed remote access"}),e.jsx("td",{children:"Remote access with infrastructure control"}),e.jsx("td",{children:"Same-network use with no internet relay"})]})]})]})}),e.jsx("h2",{id:"hosted",children:"Hosted relay: the shortest path"}),e.jsxs("ol",{children:[e.jsx("li",{children:"Install LongLeash on the laptop and connect at least one supported provider."}),e.jsxs("li",{children:["Use the default ",e.jsx("code",{children:"wss://app.longleash.dev/ws"})," relay during setup."]}),e.jsx("li",{children:"Install the phone app from Safari, sign in, and scan a fresh QR inside the installed app."}),e.jsxs("li",{children:["Confirm the app reports ",e.jsx("code",{children:"linked · relay"})," and run one harmless acceptance test."]})]}),e.jsx("p",{children:"The hosted service sees ordinary routing metadata such as timing, frame sizes, opaque room identifiers, and network addresses. End-to-end encryption keeps prompts, code, paths, transcripts, approvals, and device credentials unreadable to the relay."}),e.jsx(k,{mode:"hosted relay"}),e.jsx("h2",{id:"self-hosted",children:"Self-hosted relay: remote access you operate"}),e.jsx("p",{children:"Self-hosting does not mean remote access can run only on the laptop. Access from another network still needs an internet-reachable relay with DNS, TLS, WebSocket support, monitoring, and updates. You operate that service and its metadata boundary."}),e.jsxs("ol",{children:[e.jsx("li",{children:"Choose Cloudflare Workers or the reference container behind an HTTPS reverse proxy."}),e.jsx("li",{children:"From a trusted source checkout, install dependencies and build the app."}),e.jsx("li",{children:"Dry-run the accountless configuration before creating anything in your Cloudflare account."})]}),e.jsx(o,{command:`pnpm install
|
|
23
|
+
pnpm --filter @longleash/relay check:selfhost`}),e.jsx("ol",{start:4,children:e.jsxs("li",{children:["Deploy only after the dry run lists ",e.jsx("code",{children:"ROOM"})," and ",e.jsx("code",{children:"ASSETS"}),", with no LongLeash production domains or Clerk bindings."]})}),e.jsx(o,{command:"pnpm --filter @longleash/relay deploy:selfhost"}),e.jsxs("ol",{start:5,children:[e.jsxs("li",{children:["Copy the ",e.jsx("code",{children:"workers.dev"})," URL printed by Wrangler and verify ",e.jsx("code",{children:"https://your-worker.workers.dev/health"}),"."]}),e.jsxs("li",{children:["Configure the laptop once with your ",e.jsx("code",{children:"wss://your-relay.example/ws"})," endpoint."]}),e.jsxs("li",{children:["Pair through the app served by that deployment and require matching builds in ",e.jsx("code",{children:"longleash doctor"}),"."]})]}),e.jsxs("div",{className:"doc-callout warn",children:[e.jsx(j,{size:19,"aria-hidden":"true"}),e.jsxs("p",{children:[e.jsx("b",{children:"You own the operations boundary."})," LongLeash does not govern your server, DNS, TLS, logs, retention, availability, access policy, backups, or upgrades. Preserve the reference encryption protocol if you want the relay to remain content-blind."]})]}),e.jsxs("p",{children:["Continue with the website-native ",e.jsx("a",{href:r("/docs/self-hosting"),children:"self-hosting operations guide"})," for Docker or VPS setup, custom-domain TLS, monitoring, controlled updates, rollback, and failure recovery. The copyable default above deliberately uses the separate accountless Worker configuration."]}),e.jsx(k,{mode:"self-hosted relay"}),e.jsx("h2",{id:"lan",children:"LAN-only: no internet relay"}),e.jsxs("ol",{children:[e.jsxs("li",{children:["Set the relay to ",e.jsx("code",{children:"off"})," during installation or configuration."]}),e.jsx("li",{children:"Keep the phone and laptop on the same trusted local network."}),e.jsxs("li",{children:["Start LongLeash, scan the local QR, and require ",e.jsx("code",{children:"linked · direct"}),"."]})]}),e.jsx("p",{children:"This is the only mode with no relay service. It does not provide away-from-home access, and some guest Wi-Fi, VPN, hotspot, or client-isolation policies may block direct discovery."}),e.jsx(k,{mode:"LAN-only"}),e.jsx("h2",{id:"daemon",children:"What must remain running"}),e.jsx("p",{children:"The laptop must be awake, online, and running the LongLeash daemon because the actual agents live there. npm can distribute the software and MCP can configure it, but neither carries phone traffic. The recommended setup installs a per-user macOS LaunchAgent or Linux systemd user service, so closing Terminal does not end the connection. Foreground mode remains available for diagnosis."})]})}function Se(){return e.jsxs(c,{path:"/docs/self-hosting",eyebrow:"Operator guide",title:"Own the route without guessing.",summary:"Two maintained deployment paths, explicit security boundaries, and a recovery order you can follow while the service is under pressure.",children:[e.jsxs("div",{className:"mode-summary","aria-label":"Self-hosting deployment paths",children:[e.jsxs("section",{children:[e.jsx("span",{className:"doc-icon",children:e.jsx(x,{size:19,"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("b",{children:"Cloudflare Worker"}),e.jsx("small",{children:"Lowest operations burden and the recommended reference path."})]})]}),e.jsxs("section",{children:[e.jsx("span",{className:"doc-icon",children:e.jsx(Z,{size:19,"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("b",{children:"Docker on a VPS"}),e.jsx("small",{children:"Your server, Caddy-managed TLS, and your uptime responsibility."})]})]}),e.jsxs("section",{children:[e.jsx("span",{className:"doc-icon",children:e.jsx(le,{size:19,"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("b",{children:"Operate deliberately"}),e.jsx("small",{children:"Verify health, inspect logs, update, and retain a rollback path."})]})]})]}),e.jsxs("div",{className:"doc-callout warn",children:[e.jsx(j,{size:19,"aria-hidden":"true"}),e.jsxs("p",{children:[e.jsx("b",{children:"Self-hosted means operator-owned."})," LongLeash cannot monitor, patch, recover, or govern infrastructure in your account. Repository data, provider credentials, sessions, and transcripts still remain on the laptop; your relay handles sealed frames and ordinary network metadata."]})]}),e.jsx("h2",{id:"choose",children:"Choose the operating model"}),e.jsx("div",{className:"comparison-scroll",role:"region","aria-label":"Self-hosting platform comparison",tabIndex:0,children:e.jsxs("table",{className:"mode-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{scope:"col",children:"Responsibility"}),e.jsx("th",{scope:"col",children:"Cloudflare Worker"}),e.jsx("th",{scope:"col",children:"Docker or VPS"})]})}),e.jsxs("tbody",{children:[e.jsxs("tr",{children:[e.jsx("th",{scope:"row",children:"TLS certificates"}),e.jsx("td",{children:"Cloudflare issues and renews them"}),e.jsx("td",{children:"Caddy issues and renews them when DNS and ports are correct"})]}),e.jsxs("tr",{children:[e.jsx("th",{scope:"row",children:"WebSocket routing"}),e.jsx("td",{children:"Worker and Durable Object"}),e.jsx("td",{children:"Caddy to the private relay container"})]}),e.jsxs("tr",{children:[e.jsx("th",{scope:"row",children:"Host patching"}),e.jsx("td",{children:"Managed platform"}),e.jsx("td",{children:"You patch the OS and container runtime"})]}),e.jsxs("tr",{children:[e.jsx("th",{scope:"row",children:"Logs"}),e.jsx("td",{children:"Workers Observability"}),e.jsx("td",{children:"Docker Compose and host logs"})]}),e.jsxs("tr",{children:[e.jsx("th",{scope:"row",children:"Best fit"}),e.jsx("td",{children:"Most individual operators"}),e.jsx("td",{children:"Teams already operating a hardened server"})]})]})]})}),e.jsx("h2",{id:"cloudflare",children:"Cloudflare Worker runbook"}),e.jsx("h3",{children:"1. Validate the isolated configuration"}),e.jsx(o,{command:`pnpm install
|
|
24
|
+
pnpm --filter @longleash/relay check:selfhost`}),e.jsxs("p",{children:["Stop if the dry run contains LongLeash production domains, Clerk bindings, hosted ticket secrets, or production rate-limit namespaces. A self-hosted dry run should list the",e.jsx("code",{children:" ROOM"})," Durable Object and ",e.jsx("code",{children:"ASSETS"})," binding only."]}),e.jsx("h3",{children:"2. Deploy and prove the public edge"}),e.jsx(o,{command:`pnpm --filter @longleash/relay deploy:selfhost
|
|
25
|
+
curl -fsS https://your-worker.workers.dev/health`}),e.jsxs("p",{children:["The health response must report ",e.jsx("code",{children:'"ok":true'})," and ",e.jsx("code",{children:'"role":"relay"'}),". A successful deployment is not acceptance evidence until the public HTTPS URL answers."]}),e.jsx("h3",{children:"3. Add a custom domain without hand-managing certificates"}),e.jsxs("ol",{children:[e.jsx("li",{children:"Add the domain to an active Cloudflare zone."}),e.jsx("li",{children:"Open Workers & Pages, select the relay Worker, then choose Settings, Domains & Routes, Add, Custom Domain."}),e.jsxs("li",{children:["Enter one exact hostname such as ",e.jsx("code",{children:"relay.example.com"}),". Do not reuse a hostname with an existing CNAME."]}),e.jsxs("li",{children:["Wait for the DNS record and certificate to become active, then verify ",e.jsx("code",{children:"https://relay.example.com/health"}),"."]}),e.jsxs("li",{children:["Set the laptop endpoint to ",e.jsx("code",{children:"wss://relay.example.com/ws"})," and run ",e.jsx("code",{children:"longleash doctor"}),"."]})]}),e.jsxs("p",{children:["Cloudflare Custom Domains make the Worker the origin and create the DNS record and edge certificate. Root and ",e.jsx("code",{children:"www"})," hostnames are separate; configure both or redirect one explicitly."]}),e.jsx("h2",{id:"docker",children:"Docker or VPS runbook"}),e.jsx("p",{children:"Use a dedicated Ubuntu host with a hostname already pointing to its public address. Open TCP ports 80 and 443 in both the cloud firewall and the host firewall before requesting TLS. Keep SSH restricted to trusted addresses and apply operating-system security updates."}),e.jsx(o,{command:`ssh ubuntu@your-server
|
|
26
|
+
curl -fsSL https://raw.githubusercontent.com/Sahith59/LongLe-sh/main/scripts/relay-setup.sh | bash -s -- relay.example.com`}),e.jsx("p",{children:"The setup script installs Docker when needed, starts the relay behind Caddy, and waits for a public health response. The relay container is not published directly to the host; Caddy is the only public entry point and passes WebSocket upgrades through to it."}),e.jsx("h3",{children:"TLS acceptance"}),e.jsx(o,{command:`curl -I http://relay.example.com
|
|
27
|
+
curl -fsS https://relay.example.com/health`}),e.jsxs("ul",{children:[e.jsx("li",{children:"DNS must resolve to the VPS."}),e.jsx("li",{children:"Ports 80 and 443 must be reachable from the public internet."}),e.jsx("li",{children:"The HTTPS health check must succeed without bypassing certificate verification."}),e.jsx("li",{children:"Never use an insecure TLS skip option to make a failed certificate appear healthy."})]}),e.jsx("h2",{id:"monitor",children:"Monitoring and logs"}),e.jsx("h3",{children:"Cloudflare"}),e.jsx("p",{children:"The reference self-hosted configuration enables Workers Observability. In the Cloudflare dashboard, open the Worker and select Observability to inspect invocation logs, exceptions, and WebSocket events. For a quick live investigation, use Wrangler tail from the relay package."}),e.jsx(o,{command:"pnpm --filter @longleash/relay exec wrangler tail --config wrangler.selfhost.jsonc"}),e.jsx("h3",{children:"Docker or VPS"}),e.jsx(o,{command:`cd ~/longleash/deploy
|
|
28
|
+
docker compose ps
|
|
29
|
+
docker compose logs --tail=200 relay caddy`}),e.jsx("p",{children:"Monitor the external HTTPS health endpoint from outside the VPS. Alert on repeated failures, certificate expiry warnings, restart loops, disk pressure, and host patch failures. Do not log pairing links, URL fragments, device credentials, or decrypted application content."}),e.jsx("h2",{id:"update",children:"Controlled updates and rollback"}),e.jsxs("ol",{children:[e.jsx("li",{children:"Record the currently healthy Git commit and confirm the public health endpoint."}),e.jsx("li",{children:"Read the release notes and run the repository checks before deploying."}),e.jsx("li",{children:"Deploy during a window where you can test one laptop and one phone immediately."}),e.jsx("li",{children:"Verify HTTPS health, WebSocket reconnection, pairing, and a harmless reply."}),e.jsx("li",{children:"If acceptance fails, restore the recorded revision and verify health again."})]}),e.jsx("h3",{children:"Cloudflare update"}),e.jsx(o,{command:"pnpm --filter @longleash/relay deploy:selfhost"}),e.jsx("p",{children:"Cloudflare keeps Worker versions and deployments. Use the dashboard deployment history or Wrangler version controls to return traffic to a previously verified version. A code rollback is incomplete until the public health and phone reconnection checks pass."}),e.jsx("h3",{children:"Docker or VPS update"}),e.jsx(o,{command:`cd ~/longleash
|
|
30
|
+
git rev-parse HEAD
|
|
31
|
+
git pull --ff-only
|
|
32
|
+
cd deploy && docker compose up -d --build`}),e.jsx("p",{children:"To roll back, check out the recorded healthy commit and rebuild the same Compose project. Preserve the named Caddy volumes so certificate state survives container replacement."}),e.jsx("h2",{id:"recover",children:"Failure recovery order"}),e.jsxs("ol",{children:[e.jsxs("li",{children:[e.jsx("b",{children:"Prove DNS."})," Confirm the hostname resolves to the expected edge or VPS."]}),e.jsxs("li",{children:[e.jsx("b",{children:"Prove HTTPS."})," Request ",e.jsx("code",{children:"/health"})," from a network outside the server."]}),e.jsxs("li",{children:[e.jsx("b",{children:"Inspect the edge."})," Check Worker exceptions or Caddy certificate and proxy logs."]}),e.jsxs("li",{children:[e.jsx("b",{children:"Inspect the relay."})," Look for crash loops, unhealthy containers, or deployment errors."]}),e.jsxs("li",{children:[e.jsx("b",{children:"Prove the laptop."})," Run ",e.jsx("code",{children:"longleash doctor"})," and verify the configured WebSocket URL."]}),e.jsxs("li",{children:[e.jsx("b",{children:"Prove the phone."})," Reopen the installed app, wait for linked status, then use a fresh pairing QR only if the device was revoked."]})]}),e.jsxs("div",{className:"doc-callout safe",children:[e.jsx(S,{size:19,"aria-hidden":"true"}),e.jsxs("p",{children:[e.jsx("b",{children:"Recovery should preserve evidence."})," Capture the failing health response and relevant logs before rebuilding. Do not revoke every device, delete volumes, rotate secrets, or wipe local state as a first troubleshooting step."]})]}),e.jsx("h2",{id:"operator-checklist",children:"Operator acceptance checklist"}),e.jsxs("ul",{children:[e.jsx("li",{children:"Public HTTPS health returns the relay role with a valid certificate."}),e.jsxs("li",{children:["The laptop connects only to the intended ",e.jsx("code",{children:"wss://"})," endpoint."]}),e.jsx("li",{children:"A newly paired phone links from both Wi-Fi and cellular data."}),e.jsx("li",{children:"Closing Terminal does not stop a configured background service."}),e.jsx("li",{children:"Logs contain operational metadata but no pairing secrets or plaintext transcript content."}),e.jsx("li",{children:"An update and a documented rollback both preserve a working health check."})]}),e.jsxs("p",{children:["The repository remains the source of deployable code and scripts, but this page is the complete operator path. Use ",e.jsx("a",{href:d,children:"source on GitHub"})," only when you need to audit or modify that implementation."]}),e.jsx(k,{mode:"self-hosted relay operations"})]})}function Te(){return e.jsxs(c,{path:"/docs/background-service",title:"Close Terminal. Keep control.",summary:"LongLeash runs in your signed-in user session, reports authenticated health, and removes cleanly without deleting your data.",children:[e.jsx("h2",{children:"What it changes"}),e.jsxs("p",{children:["macOS installs a per-user LaunchAgent. Linux installs a systemd user unit. Neither path uses",e.jsx("code",{children:" sudo"}),", opens a public port, or moves agents, repositories, credentials, or transcripts off the laptop."]}),e.jsx(o,{command:`longleash service status
|
|
33
|
+
longleash service logs`}),e.jsx("h2",{children:"Control the lifecycle"}),e.jsx(o,{command:`longleash service start
|
|
25
34
|
longleash service stop
|
|
26
35
|
longleash service restart
|
|
27
|
-
longleash service uninstall`}),e.jsxs("p",{children:["Stop and uninstall preserve settings, paired devices, audit history, repositories, and the managed runtime. Use ",e.jsx("code",{children:"longleash pair"})," to print a fresh QR without storing its complete link in service logs."]}),e.jsx("h2",{children:"Foreground mode remains available"}),e.jsx(
|
|
28
|
-
longleash doctor`}),e.jsx("p",{children:"Restart the daemon after updating. An already running Node process cannot load the new files from disk."})]})}function
|
|
29
|
-
longleash service logs`}),e.jsx("p",{children:"Record the local time, provider, origin label, session title, project path, and the exact action that failed. Redact source code and pairing URLs before posting evidence."}),e.jsx("h2",{id:"pairing",children:"Pairing says the laptop did not answer"}),e.jsxs("ol",{children:[e.jsxs("li",{children:["Leave the terminal running ",e.jsx("code",{children:"longleash"})," open."]}),e.jsxs("li",{children:["Run ",e.jsx("code",{children:"longleash doctor"})," in a second terminal and require ",e.jsx("b",{children:"reachable"}),"."]}),e.jsxs("li",{children:["Press ",e.jsx("code",{children:"n"})," then Enter in the daemon terminal for a new single-use QR."]}),e.jsx("li",{children:"Scan from inside the installed PWA; Safari and the home-screen app have separate storage on iPhone."}),e.jsx("li",{children:"If scanning remains unreliable, paste the complete fresh link into the app."})]}),e.jsx("h2",{id:"daemon",children:"“Already running” or “connection refused”"}),e.jsxs("p",{children:["“Already running” means one daemon owns the laptop profile; use it instead of starting a second. “Connection refused” means a hook or client reached an address with no listening daemon. Do not use a broad ",e.jsx("code",{children:"pkill"}),". Identify the listener first:"]}),e.jsx(
|
|
30
|
-
ps -p <PID> -o pid,ppid,command`}),e.jsx("h2",{id:"missing-session",children:"A Terminal or VS Code session is missing"}),e.jsxs("ol",{children:[e.jsxs("li",{children:["Run ",e.jsx("code",{children:"longleash doctor"}),"."]}),e.jsxs("li",{children:["If a hook is stale or missing, run ",e.jsx("code",{children:"longleash hooks"}),"."]}),e.jsx("li",{children:"Start a new provider process; an existing one retains its old environment and hook configuration."}),e.jsx("li",{children:"Cause one lifecycle/tool event. LongLeash discovers structured events, not terminal pixels."})]}),e.jsx("h2",{id:"stale",children:"A stale approval or old session remains"}),e.jsxs("p",{children:["Do not approve again. Record the session and decision time, refresh once, and compare builds with ",e.jsx("code",{children:"longleash doctor"}),". A decided request must close; a dormant conversation may remain deliberately as resumable history but must not look live."]}),e.jsx("h2",{id:"writer",children:"“This checkout is already controlled”"}),e.jsxs("p",{children:["One physical checkout permits one writer. Continue in the owner, stop/release it, or choose",e.jsx("b",{children:" Safe parallel"})," so LongLeash creates a Git worktree. Dirty tracked changes cause a safe refusal because silently copying an older HEAD would lose context."]}),e.jsx("h2",{id:"handoff",children:"Stop, takeover, or handoff did not complete"}),e.jsx("p",{children:"LongLeash requires proof that the native process exited before granting another writer. Close the relevant Terminal or IDE process on the laptop, wait for its lifecycle event, and retry once. If it still fails, keep the log. The refusal is protecting the conversation and checkout."}),e.jsxs("div",{className:"doc-callout",children:[e.jsx(
|
|
31
|
-
longleash revoke <device-id>`}),e.jsx("h2",{children:"Account identity is a separate lock"}),e.jsx("p",{children:"The official hosted app requires a verified LongLeash account before it can request a short-lived relay connection ticket. That account chooses which browser credential slot is visible; the QR still grants authority over a specific laptop. Signing into an account alone cannot control a machine, and a pairing secret is never sent to Google or the account provider. LAN and self-hosted deployments remain accountless."}),e.jsx("h2",{children:"The phone cannot execute arbitrary commands"}),e.jsx("p",{children:"It sends validated operations such as start a session, reply, approve a specific request, stop, or tune. There is no generic “run any shell command” endpoint. Workspace allowlists, one-writer leases, provider approval policy, and audit records remain in force."}),e.jsx("h2",{children:"Practical safety rules"}),e.jsxs("ul",{children:[e.jsx("li",{children:"Never share a pairing URL or unredacted screenshot containing one."}),e.jsx("li",{children:"Keep the OS, Node, provider CLI, browser, and LongLeash current."}),e.jsx("li",{children:"Review the session, folder, tool, and input before approving."}),e.jsx("li",{children:"Revoke devices you no longer control and use a device passcode."}),e.jsxs("li",{children:["Run ",e.jsx("code",{children:"longleash doctor"})," after every update."]})]}),e.jsxs("p",{children:["For implementation-level review, the"," ",e.jsx("a",{href:`${t}/blob/main/docs/ARCHITECTURE.md`,children:"architecture source"})," remains public."]})]})}function Ne(){return e.jsxs(l,{path:"/docs/session-portability",title:"One conversation. One active writer.",summary:"LongLeash preserves provider-native conversation IDs and only moves control after the current writer has verifiably released it.",children:[e.jsx("h2",{children:"Why the one-writer rule exists"}),e.jsx("p",{children:"Two interfaces writing the same live provider conversation can reorder messages, duplicate tools, or corrupt ownership. LongLeash therefore treats Terminal, VS Code, and phone control as mutually exclusive writers to one conversation."}),e.jsxs("div",{className:"portable-flow",children:[e.jsx("span",{children:"Terminal or VS Code"}),e.jsx(p,{"aria-hidden":"true"}),e.jsx("span",{children:"verified release"}),e.jsx(p,{"aria-hidden":"true"}),e.jsx("span",{children:"LongLeash"})]}),e.jsx("h2",{children:"Phone to Terminal"}),e.jsxs("p",{children:["Open the session, reveal ",e.jsx("b",{children:"Terminal handoff"}),", stop/release the live process if needed, copy the command, and run it on the laptop. Claude and Codex use their own native resume IDs."]}),e.jsx("h2",{children:"Phone to VS Code"}),e.jsx("p",{children:"The workspace handoff opens the correct folder. Claude can resume through its CLI/IDE connection. Codex resumes in the VS Code terminal today. LongLeash does not claim it can inject an existing thread into another vendor extension’s private chat panel."}),e.jsx("h2",{children:"Terminal or VS Code to phone"}),e.jsx("p",{children:"Choose the explicit transfer control option. The daemon reserves the checkout, asks the verified native process to end, confirms its exit, then resumes the same conversation as a managed session. If confirmation fails, the transfer is cancelled and the original owner remains."}),e.jsx("h2",{children:"Parallel work in one Git project"}),e.jsxs("p",{children:["A second managed agent can use ",e.jsx("b",{children:"Safe parallel"}),". LongLeash creates an isolated worktree and branch so each agent writes different filesystems. It never auto-merges, commits, pushes, or deletes that work. Non-Git directories remain sequential."]}),e.jsxs("div",{className:"doc-callout safe",children:[e.jsx(L,{size:19,"aria-hidden":"true"}),e.jsxs("p",{children:[e.jsx("b",{children:"Conversation portability and code parallelism are different."})," A handoff moves one conversation; a worktree isolates a second writer."]})]})]})}function Le(){return e.jsxs(l,{path:"/docs/faq",title:"Straight answers before you install.",summary:"The current public preview is local-first and honest about hosted identity, provider, and platform boundaries.",children:[e.jsx(u,{q:"Do I need a LongLeash account?",children:"The official hosted app requires a free LongLeash account through Google or verified email. A QR is still required for each laptop: account identity and device authority are separate. LAN and self-hosted use remain accountless. Provider logins stay with Claude Code or Codex."}),e.jsx(u,{q:"Does my laptop need to stay awake?",children:"Yes. Your laptop runs the agents and daemon. Relay mode reaches it from another network, but LongLeash cannot operate a sleeping, powered-off, or disconnected machine."}),e.jsxs(u,{q:"Can I use cellular data?",children:["Yes, when the daemon reports a configured relay and the app shows ",e.jsx("code",{children:"linked · relay"}),". Direct mode is an optional same-network path."]}),e.jsx(u,{q:"Which agents work today?",children:"Claude Code and Codex. LongLeash uses structured provider integrations and lifecycle hooks; it does not screen-scrape arbitrary agent TUIs."}),e.jsx(u,{q:"Can I change model, effort, or thinking mid-session?",children:"Managed sessions expose Tune for settings the provider supports. An externally controlled Terminal or VS Code session must explicitly transfer to LongLeash first so there is one writer."}),e.jsx(u,{q:"Can two agents work in the same project?",children:"Yes for managed sessions in a clean Git repository through Safe parallel worktrees. Two agents must not write the same physical checkout. Non-Git folders remain sequential."}),e.jsx(u,{q:"Is LongLeash free?",children:"The repository is MIT licensed. You still need your own provider access. Hosted relay and future team or premium services may develop separate pricing; nothing on this page promises an unshipped tier."}),e.jsx(u,{q:"Is the VS Code companion available?",children:"Not publicly yet. Its secure protocol, package verification, compatibility checks, and native session-tree foundation exist. Authenticated live daemon sync is the next engineering checkpoint."})]})}function Te(){return e.jsxs(l,{path:"/roadmap",eyebrow:"Product roadmap",title:"What works. What comes next.",summary:"A quick view of the product you can use today and the capabilities we are proving next. Building and exploring are directions, not promised dates.",children:[e.jsx(v,{icon:g,state:"Available now",tone:"shipped",title:"Control real agent sessions from your phone",children:e.jsxs("ul",{className:"roadmap-list",children:[e.jsx("li",{children:"See Claude Code and Codex sessions from Terminal, VS Code, and phone launches."}),e.jsx("li",{children:"Reply, approve, deny, stop, tune, and return to a provider-native terminal conversation."}),e.jsx("li",{children:"Run safe parallel work in isolated Git worktrees without creating two writers."}),e.jsx("li",{children:"Review and edit an agent-to-agent briefing before starting an attributed child session."}),e.jsx("li",{children:"Choose the hosted relay, your own relay, or LAN-only operation."}),e.jsx("li",{children:"Keep the laptop reachable with a per-user background service and authenticated health checks."})]})}),e.jsx(v,{icon:m,state:"Building",tone:"building",title:"Install once and keep the laptop ready",children:e.jsxs("ul",{className:"roadmap-list",children:[e.jsx("li",{children:"A provenance-backed npm package with no maintainer-specific install URL."}),e.jsx("li",{children:"A matching code on phone and terminal before a durable device is trusted."}),e.jsx("li",{children:"A local dashboard for service health, devices, sessions, approvals, roots, and updates."})]})}),e.jsx(v,{icon:A,state:"Building",tone:"building",title:"A trustworthy VS Code companion",children:e.jsxs("ul",{className:"roadmap-list",children:[e.jsx("li",{children:"An authenticated extension connection with workspace-trust enforcement."}),e.jsx("li",{children:"A native session tree and exact provider-aware status inside the IDE."}),e.jsx("li",{children:"Visible fallbacks when a vendor does not expose an existing conversation to its chat panel."}),e.jsx("li",{children:"Fail-closed compatibility when a provider changes an integration contract."})]})}),e.jsx(v,{icon:N,state:"Building",tone:"building",title:"Let your agent set it up safely",children:e.jsxs("ul",{className:"roadmap-list",children:[e.jsx("li",{children:"A local MCP server for setup plans, diagnostics, service lifecycle, devices, and sessions."}),e.jsx("li",{children:"Dry-run first, explicit confirmation for persistent changes, and atomic rollback."}),e.jsx("li",{children:"No generic shell, secret return, approval bypass, or public MCP listener."})]})}),e.jsx(v,{icon:L,state:"Exploring",tone:"planned",title:"Teams of specialists with visible human review",children:e.jsxs("ul",{className:"roadmap-list",children:[e.jsx("li",{children:"Multiple isolated children with explicit scope, budgets, comparison, and return review."}),e.jsx("li",{children:"Safe merge workflows that never hide an unbounded autonomous loop."}),e.jsx("li",{children:"Optional team administration and operated services without moving provider credentials or code into our account plane."})]})}),e.jsxs("p",{className:"roadmap-engineering-note",children:["Want implementation detail? Maintainers can inspect the granular"," ",e.jsx("a",{href:`${t}/blob/main/PLAN.md`,children:"engineering plan"}),". Public product language stays focused on outcomes rather than internal milestones."]})]})}function Se(){return e.jsxs(l,{path:"/privacy",eyebrow:"Legal & trust",title:"Privacy by a smaller data boundary.",summary:"This notice describes the account-enabled public preview and the deliberately small hosted data boundary.",children:[e.jsx("p",{className:"effective",children:"Effective 15 August 2026"}),e.jsx("h2",{children:"What the hosted account contains"}),e.jsx("p",{children:"The official hosted app uses Clerk for authentication. You may sign in with Google, an email verification code, or email and password. LongLeash never receives or stores your password. The account may contain a stable account identifier, name, email address, profile image, sign-in times, and security/session metadata needed to prevent abuse. LongLeash currently has no advertising, mailing-list, payment, or product-analytics integration and does not sell personal information."}),e.jsx("h2",{children:"Development data stays local"}),e.jsx("p",{children:"Repositories, provider credentials, agent processes, transcript history, approval records, and the durable audit database stay on the laptop running LongLeash. The phone receives current content only after an authenticated, encrypted connection."}),e.jsx("h2",{children:"Pairing credentials stay in the browser and laptop"}),e.jsx("p",{children:"Each hosted account receives a separate browser storage slot for its paired-device token, relay key, and relay address. Those credentials are not uploaded to Clerk or Google. A short-lived signed relay ticket contains an opaque one-way account tag, room, role, and expiry; it cannot decrypt session traffic and is not stored as conversation history."}),e.jsx("h2",{children:"Relay and hosting metadata"}),e.jsx("p",{children:"The hosted relay processes encrypted frames and normal network metadata needed to route a connection, such as IP addresses, timestamps, frame sizes, and opaque room identifiers. It does not have the keys required to read frame content and stores no conversation history. Cloudflare may process ordinary security and operational data as the hosting provider."}),e.jsx("h2",{children:"Push notifications"}),e.jsx("p",{children:"If enabled, push messages contain identifiers needed to wake and route the app, not prompt, source code, path, transcript, or approval content. The app fetches the current item after reconnecting."}),e.jsx("h2",{children:"Processors and purpose"}),e.jsx("p",{children:"Cloudflare hosts the public site, static app, abuse controls, and encrypted relay; Clerk provides account, credential, verification, and session management; Google verifies identity only when you choose Google sign-in. LongLeash requests only basic OpenID identity scopes: name, email, and profile. It does not request Gmail, Drive, source repositories, or provider accounts. Each processor may handle ordinary network and security metadata under its own terms and privacy notice."}),e.jsx("h2",{children:"Retention, export, and deletion"}),e.jsxs("p",{children:["Account records remain until you delete the account or they must be retained for security or legal obligations. Open the account control in the app header to download the hosted account fields LongLeash exposes or permanently delete the account. Deletion also clears that account’s credentials from the current browser; laptop-local transcripts are outside the hosted account and must be managed on the laptop. You may also contact"," ",e.jsx("a",{href:"mailto:privacy@longleash.dev",children:"privacy@longleash.dev"})," for an access, correction, export, or deletion request, subject to applicable law and identity verification."]}),e.jsx("h2",{children:"Security and privacy contact"}),e.jsxs("p",{children:["Use ",e.jsx("a",{href:"mailto:security@longleash.dev",children:"security@longleash.dev"})," for private security reports and ",e.jsx("a",{href:"mailto:support@longleash.dev",children:"support@longleash.dev"})," for ordinary questions. Do not place pairing links, tokens, private code, or exploit details in a public issue. The public ",e.jsx("a",{href:`${t}/issues`,children:"issue tracker"})," is only for redacted, non-sensitive reports."]})]})}function Ce(){return e.jsxs(l,{path:"/terms",eyebrow:"Legal",title:"Terms of Service",summary:"Rules for the free hosted public preview. The MIT license continues to govern the open-source code itself.",children:[e.jsx("p",{className:"effective",children:"Effective 15 August 2026"}),e.jsx("h2",{children:"Agreement and operator"}),e.jsxs("p",{children:["These terms govern use of the LongLeash-hosted website, account service, and encrypted relay, operated by Sahith Reddy Thummala. By using the hosted service you agree to these terms and the ",e.jsx("a",{href:a("/privacy"),children:"Privacy Notice"}),". If you do not agree, use the MIT-licensed source locally or stop using the hosted service."]}),e.jsx("h2",{children:"Eligibility and your responsibilities"}),e.jsx("p",{children:"You must be legally able to enter this agreement and use LongLeash only with computers, repositories, provider accounts, and data you are authorized to control. You are responsible for securing your LongLeash sign-in method, phone, laptop, pairing links, provider subscriptions, and actions approved through the product."}),e.jsx("h2",{children:"Acceptable use"}),e.jsx("p",{children:"Do not use the hosted service to gain unauthorized access; distribute malware; evade provider or network controls; interfere with another user; probe rooms or accounts you do not own; overload the relay; violate law or third-party rights; or expose another person’s secrets. LongLeash may rate-limit, suspend, or terminate access needed to protect users and infrastructure."}),e.jsx("h2",{children:"Third-party services"}),e.jsx("p",{children:"Claude, Codex, Google, Clerk, Cloudflare, GitHub, and any other third-party service have their own terms, availability, and fees. LongLeash does not include an AI subscription and cannot grant rights those providers do not give you."}),e.jsx("h2",{children:"Preview availability and changes"}),e.jsx("p",{children:"The hosted service is a free public preview. Features may change, be rate-limited, or be discontinued, and temporary outages may occur. Material changes to these terms or the hosted data boundary will be published before they take effect when reasonably possible."}),e.jsx("h2",{children:"Open-source license and feedback"}),e.jsxs("p",{children:["Repository code is offered under the ",e.jsx("a",{href:a("/license"),children:"MIT License"}),". These service terms do not remove rights granted by that license. You may provide feedback; unless separately agreed, LongLeash may use it without obligation while respecting confidential information."]}),e.jsx("h2",{children:"Disclaimers and liability"}),e.jsx("p",{children:"To the maximum extent permitted by applicable law, the preview is provided “as is” and “as available,” without warranties. You remain responsible for reviewing agent output, approvals, backups, repository state, and consequences of commands. To the maximum extent permitted by law, LongLeash is not liable for indirect, incidental, special, consequential, or lost-profit damages arising from the free hosted preview. Rights that cannot legally be excluded remain unaffected."}),e.jsx("h2",{children:"Ending use and contact"}),e.jsxs("p",{children:["You may stop using the service and delete your account at any time. LongLeash may suspend or end hosted access for breach, abuse, security risk, or service closure. Questions about these terms may be sent to ",e.jsx("a",{href:"mailto:support@longleash.dev",children:"support@longleash.dev"}),"."]})]})}function Re(){return e.jsxs(l,{path:"/license",eyebrow:"Legal",title:"MIT License",summary:"LongLeash is open-source software. This is the complete license text shipped in the repository.",children:[e.jsx("pre",{className:"license-copy",children:xe}),e.jsxs("p",{children:["The canonical plain-text copy is also available in the ",e.jsx("a",{href:`${t}/blob/main/LICENSE`,children:"source repository"}),"."]})]})}function Ie(){return e.jsxs("main",{className:"public-main not-found",id:"main",children:[e.jsx("p",{className:"land-label",children:"404"}),e.jsx("h1",{children:"This page is not on the leash."}),e.jsx("p",{children:"The address may have changed. The documentation index and product remain available."}),e.jsxs("div",{className:"hero-acts",children:[e.jsx("a",{className:"key primary",href:a("/docs"),children:"Open documentation"}),e.jsx("a",{className:"tap",href:a("/"),children:"Return home"})]})]})}function T({icon:s,title:n,body:i}){return e.jsxs("span",{className:"model-step",children:[e.jsx(s,{size:20,"aria-hidden":"true"}),e.jsxs("span",{children:[e.jsx("b",{children:n}),e.jsx("small",{children:i})]})]})}function w({term:s,body:n}){return e.jsxs("div",{children:[e.jsx("dt",{children:s}),e.jsx("dd",{children:n})]})}function u({q:s,children:n}){return e.jsxs("section",{className:"faq-item",children:[e.jsx("h2",{children:s}),e.jsx("p",{children:n})]})}function S({mode:s}){return e.jsxs("section",{className:"agent-setup-note",children:[e.jsx("span",{className:"doc-icon",children:e.jsx(N,{size:19,"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("span",{className:"road-state building",children:"Coming before public release"}),e.jsx("h3",{children:"Got no time? Let your agent wire it."}),e.jsxs("p",{children:["The LongLeash MCP will inspect prerequisites, propose a dry-run plan for ",s,", apply only the changes you approve, and return a redacted health report. It will not receive pairing secrets, provider credentials, or generic shell access."]})]})]})}function v({icon:s,state:n,tone:i,title:r,children:o}){return e.jsxs("section",{className:"roadmap-stage",children:[e.jsx("span",{className:"roadmap-stage-icon",children:e.jsx(s,{size:20,"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("span",{className:`road-state ${i}`,children:n}),e.jsx("h2",{children:r}),o]})]})}function c({command:s}){const[n,i]=j.useState(!1);async function r(){try{await navigator.clipboard.writeText(s),i(!0),window.setTimeout(()=>i(!1),1800)}catch{i(!1)}}return e.jsxs("div",{className:"doc-code",children:[e.jsx("pre",{children:e.jsx("code",{children:s})}),e.jsxs("button",{type:"button",onClick:()=>{r()},"aria-label":"Copy command",children:[n?e.jsx(I,{size:15,"aria-hidden":"true"}):e.jsx(O,{size:15,"aria-hidden":"true"}),n?"Copied":"Copy"]})]})}const Ae=[{title:"Start here",body:"Requirements, installation, pairing, daily use, updates, and honest limits.",href:"/docs/getting-started",icon:D},{title:"Choose your connection",body:"Compare the hosted relay, your own relay, and LAN-only operation before setup.",href:"/docs/connectivity",icon:y},{title:"Troubleshooting",body:"Symptom-first fixes for pairing, hooks, stale state, handoffs, and connection errors.",href:"/docs/troubleshooting",icon:N},{title:"Security model",body:"What the laptop, phone, and relay can see, plus where LongLeash deliberately stops.",href:"/docs/security",icon:f},{title:"Session portability",body:"Move work between phone, Terminal, and VS Code without creating two writers.",href:"/docs/session-portability",icon:m}];function Pe(){const s=he();if(s!=="/")return e.jsx(je,{path:s});const n=P();return e.jsxs("div",{className:"land",children:[e.jsx("a",{className:"skip-link",href:"#main",children:"Skip to content"}),e.jsx(z,{}),e.jsxs("main",{id:"main",children:[e.jsxs("section",{className:"hero",id:"top",children:[e.jsxs("div",{className:"hero-copy",children:[e.jsxs("p",{className:"eyebrow",children:[e.jsx("span",{className:"live-dot","aria-hidden":"true"})," Public preview · Open source"]}),e.jsxs("h1",{children:["Your agents keep working. ",e.jsx("em",{children:"You can leave the desk."})]}),e.jsx("p",{className:"lede",children:"Run Claude Code and Codex on your laptop, then read, steer, approve, stop, tune, and hand off their sessions from your phone. Your laptop stays in control; LongLeash is the secure control surface in your pocket."}),e.jsxs("div",{className:"hero-acts",children:[e.jsxs("a",{className:"key primary",href:a("/#start"),children:["Install in five minutes",e.jsx(p,{size:17,strokeWidth:2.4,"aria-hidden":"true"})]}),e.jsx("a",{className:"tap",href:n,children:"Already paired? Open app"})]}),e.jsx("p",{className:"hero-fine",children:"macOS or Linux · Node.js 22+ · your existing Claude or Codex login · free hosted account"})]}),e.jsx(De,{})]}),e.jsxs("section",{className:"signal-strip","aria-label":"Current product support",children:[e.jsxs("span",{children:[e.jsx("b",{children:"Claude Code"})," managed + observed sessions"]}),e.jsxs("span",{children:[e.jsx("b",{children:"Codex"})," managed + observed sessions"]}),e.jsxs("span",{children:[e.jsx("b",{children:"Anywhere"})," encrypted relay"]})]}),e.jsxs("section",{className:"product",id:"product",children:[e.jsx(b,{eyebrow:"What ships today",title:"One place to see what needs you.",body:"This is working software, not a concept page. See Claude Code and Codex sessions, make typed decisions, preserve provider conversations, and delegate work with a human review point."}),e.jsxs("div",{className:"feature-grid",children:[e.jsx(x,{icon:M,title:"Sessions and approvals",body:"See phone, Terminal, and VS Code sessions with clear provider and origin labels. Approve, deny, answer, steer, or stop from the exact session."}),e.jsx(x,{icon:L,title:"Safe parallel work",body:"Start a second writer in the same Git project through an isolated worktree instead of letting two agents overwrite one checkout."}),e.jsx(x,{icon:H,title:"Model and reasoning controls",body:"Choose model, effort, and supported thinking settings at launch, during a managed conversation, or for a reviewed delegated child."}),e.jsx(x,{icon:y,title:"Reviewed agent delegation",body:"Build and edit a briefing on your phone, start a Claude or Codex child, then review its return before anything reaches the parent."}),e.jsx(x,{icon:m,title:"Exact terminal handoff",body:"Copy a provider-native resume command and move the same conversation back to your keyboard without pretending a new chat is the old one."}),e.jsx(x,{icon:q,title:"Private notifications",body:"Push notifications wake the app with identifiers only. Prompt, code, path, and approval content are fetched after the encrypted link reconnects."})]})]}),e.jsxs("section",{className:"system",children:[e.jsx(b,{eyebrow:"How it works",title:"The powerful part never leaves your laptop.",body:"LongLeash is a control plane, not a hosted coding environment. Your provider credentials, repositories, agent processes, transcripts, and durable audit data stay local."}),e.jsxs("div",{className:"system-flow","aria-label":"LongLeash data flow",children:[e.jsxs("div",{className:"flow-node",children:[e.jsx("span",{className:"flow-icon",children:e.jsx(m,{"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("b",{children:"Your laptop"}),e.jsx("span",{children:"daemon · agents · code · keys"})]})]}),e.jsxs("span",{className:"flow-link",children:[e.jsx(k,{size:13,"aria-hidden":"true"})," sealed frames"]}),e.jsxs("div",{className:"flow-node",children:[e.jsx("span",{className:"flow-icon",children:e.jsx(y,{"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("b",{children:"Relay"}),e.jsx("span",{children:"routes ciphertext · stores no history"})]})]}),e.jsxs("span",{className:"flow-link",children:[e.jsx(k,{size:13,"aria-hidden":"true"})," sealed frames"]}),e.jsxs("div",{className:"flow-node",children:[e.jsx("span",{className:"flow-icon",children:e.jsx(g,{"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("b",{children:"Your phone"}),e.jsx("span",{children:"review · decide · steer"})]})]})]}),e.jsxs("div",{className:"truth-row",children:[e.jsxs("p",{children:[e.jsx("b",{children:"Two separate locks."})," Your verified account identifies the hosted user; a fresh QR grants one browser authority over one laptop. Local/self-hosted use stays accountless."]}),e.jsxs("p",{children:[e.jsx("b",{children:"No generic remote shell."})," The phone can call a small typed protocol, not execute arbitrary commands behind your back."]}),e.jsxs("p",{children:[e.jsx("b",{children:"No public port at home."})," The daemon dials outward to the relay; router changes and inbound firewall holes are unnecessary."]})]})]}),e.jsxs("section",{className:"start",id:"start",children:[e.jsx(b,{eyebrow:"Start in five minutes",title:"Laptop first. Phone second.",body:"LongLeash does not include Claude or Codex. Install and sign in to at least one provider CLI first, then follow this path."}),e.jsxs("div",{className:"install-shell",children:[e.jsxs("div",{className:"install-head",children:[e.jsxs("span",{children:[e.jsx(m,{size:17,"aria-hidden":"true"})," Terminal"]}),e.jsx("span",{className:"mono",children:"macOS · Linux"})]}),e.jsx("code",{children:C}),e.jsx(Ee,{command:C})]}),e.jsxs("p",{className:"inspect-note",children:["Prefer to inspect before running?"," ",e.jsx("a",{href:`${t}/tree/main/packages/cli`,children:"Read the CLI package source"})," and"," ",e.jsx("a",{href:`${t}/blob/main/docs/NPM-RELEASE.md`,children:"release controls"})," first."]}),e.jsxs("ol",{className:"steps",children:[e.jsxs("li",{children:[e.jsx("span",{className:"n",children:"01"}),e.jsxs("div",{children:[e.jsx("h3",{children:"Install LongLeash"}),e.jsxs("p",{children:["Setup never uses ",e.jsx("code",{children:"sudo"}),". It verifies the pinned npm package, installs under your home directory, reviews roots and connectivity, and wires supported hooks."]})]})]}),e.jsxs("li",{children:[e.jsx("span",{className:"n",children:"02"}),e.jsxs("div",{children:[e.jsx("h3",{children:"Keep the laptop ready"}),e.jsxs("p",{children:["Accept the recommended per-user background service, or deliberately choose"," ",e.jsx("code",{children:"longleash run ~/code"})," and keep that foreground terminal open."]})]})]}),e.jsxs("li",{children:[e.jsx("span",{className:"n",children:"03"}),e.jsxs("div",{children:[e.jsx("h3",{children:"Install the phone app, sign in, then pair"}),e.jsxs("p",{children:["On iPhone, open the hosted app in Safari and add it to your home screen first. Open that installed copy, sign in with Google or verified email, then use its scanner on the fresh QR. Require the header to say"," ",e.jsx("code",{children:"linked · relay"})," or ",e.jsx("code",{children:"linked · direct"}),". Pairing links are single-use secrets."]})]})]}),e.jsxs("li",{children:[e.jsx("span",{className:"n",children:"04"}),e.jsxs("div",{children:[e.jsx("h3",{children:"Verify before relying on it"}),e.jsxs("p",{children:["Run ",e.jsx("code",{children:"longleash doctor"}),". The daemon must be reachable, every installed hook must be current, and laptop, daemon, and relay builds must agree."]})]})]})]}),e.jsxs("div",{className:"first-run",children:[e.jsxs("div",{children:[e.jsx("span",{className:"land-stamp",children:"FIRST REAL TEST"}),e.jsx("h3",{children:"Start with a harmless task"}),e.jsx("p",{children:"Ask Claude or Codex to inspect a small folder, trigger one approval, answer it from the phone, stop the session, then reopen it from the terminal handoff. Only move to important work after that loop behaves correctly on your devices and network."})]}),e.jsxs("a",{className:"key",href:a("/docs/getting-started#first-test"),children:["Open acceptance checklist",e.jsx(p,{size:16,"aria-hidden":"true"})]})]})]}),e.jsxs("section",{className:"docs",id:"docs",children:[e.jsx(b,{eyebrow:"Documentation",title:"Answers before guesswork.",body:"Read the complete guides here without leaving the product site. GitHub remains available when you want source code or the issue tracker."}),e.jsx("div",{className:"docs-grid",children:Ae.map(({title:i,body:r,href:o,icon:h})=>e.jsxs("a",{className:"doc-card",href:a(o),children:[e.jsx("span",{className:"doc-icon",children:e.jsx(h,{size:19,"aria-hidden":"true"})}),e.jsxs("span",{children:[e.jsx("b",{children:i}),e.jsx("span",{children:r})]}),e.jsx(p,{size:15,"aria-hidden":"true"})]},i))}),e.jsxs("div",{className:"support-line",children:[e.jsxs("span",{children:["Something failed? Run ",e.jsx("code",{children:"longleash doctor"})," before restarting anything."]}),e.jsxs("span",{children:[e.jsx("a",{href:a("/docs/troubleshooting"),children:"Open troubleshooting"})," · ",e.jsx("a",{href:`${t}/issues/new`,children:"Report on GitHub"})]})]})]}),e.jsxs("section",{className:"roadmap",id:"roadmap",children:[e.jsx(b,{eyebrow:"Roadmap",title:"What works. What comes next.",body:"See the product you can use today and the capabilities we are proving next. Building means active work, not a promised date."}),e.jsxs("div",{className:"roadmap-grid",children:[e.jsxs("article",{children:[e.jsx("span",{className:"roadmap-card-icon",children:e.jsx(g,{size:19,"aria-hidden":"true"})}),e.jsx("span",{className:"road-state shipped",children:"Available now"}),e.jsx("h3",{children:"Phone control and reviewed delegation"}),e.jsx("p",{children:"Claude and Codex sessions, approvals, tuning, handoffs, safe parallel phone starts, and human-reviewed agent-to-agent briefing and return flows."})]}),e.jsxs("article",{children:[e.jsx("span",{className:"roadmap-card-icon",children:e.jsx(m,{size:19,"aria-hidden":"true"})}),e.jsx("span",{className:"road-state shipped",children:"Available now"}),e.jsx("h3",{children:"Reliable install and background service"}),e.jsx("p",{children:"Verified npm distribution, a per-user background service, authenticated health, redacted logs, clean removal, and safe foreground fallback."})]}),e.jsxs("article",{children:[e.jsx("span",{className:"roadmap-card-icon",children:e.jsx(f,{size:19,"aria-hidden":"true"})}),e.jsx("span",{className:"road-state building",children:"Building"}),e.jsx("h3",{children:"Verified pairing and local operations"}),e.jsx("p",{children:"A phone-to-terminal matching code and a local service, device, session, and update dashboard."})]}),e.jsxs("article",{children:[e.jsx("span",{className:"roadmap-card-icon",children:e.jsx(A,{size:19,"aria-hidden":"true"})}),e.jsx("span",{className:"road-state building",children:"Building"}),e.jsx("h3",{children:"VS Code companion and agent setup"}),e.jsx("p",{children:"Authenticated IDE visibility plus a local MCP that can plan setup, diagnose failures, and apply only the changes you approve."})]})]}),e.jsxs("div",{className:"honesty",children:[e.jsx(f,{size:19,"aria-hidden":"true"}),e.jsxs("p",{children:[e.jsx("b",{children:"An honest platform boundary:"})," LongLeash never writes into another extension’s private chat webview. Unsupported native-panel continuation fails visibly and keeps a copyable terminal fallback. The companion extension will use only documented or LongLeash-owned surfaces."]})]}),e.jsxs("a",{className:"roadmap-link",href:a("/roadmap"),children:["Read the public roadmap ",e.jsx(p,{size:16,"aria-hidden":"true"})]})]})]}),e.jsx(W,{})]})}function b({eyebrow:s,title:n,body:i}){return e.jsxs("div",{className:"section-heading",children:[e.jsx("p",{className:"land-label",children:s}),e.jsx("h2",{children:n}),e.jsx("p",{children:i})]})}function x({icon:s,title:n,body:i}){return e.jsxs("article",{className:"feature",children:[e.jsx("span",{className:"feature-icon",children:e.jsx(s,{size:19,"aria-hidden":"true"})}),e.jsx("h3",{children:n}),e.jsx("p",{children:i})]})}function Ee({command:s}){const[n,i]=j.useState(!1);async function r(){try{await navigator.clipboard.writeText(s),i(!0),window.setTimeout(()=>i(!1),1800)}catch{i(!1)}}return e.jsxs("button",{className:"copy-command",type:"button",onClick:()=>{r()},children:[n?e.jsx(I,{size:16,"aria-hidden":"true"}):e.jsx(O,{size:16,"aria-hidden":"true"}),n?"Copied":"Copy"]})}const Oe=6200,qe={hidden:{y:-84,scale:.9,opacity:0,filter:"blur(6px)"},shown:{y:0,scale:1,opacity:1,filter:"blur(0px)",transition:{type:"spring",stiffness:320,damping:28}},answered:{y:-84,scale:.92,opacity:0,filter:"blur(4px)",transition:{duration:.3,ease:[.4,0,1,1]}}};function De(){const s=Y(),[n,i]=j.useState("shown");return j.useEffect(()=>{if(s)return;let r=!0;const o=[],h=()=>{r&&(i("shown"),o.push(setTimeout(()=>r&&i("answered"),3600)),o.push(setTimeout(()=>r&&i("hidden"),4100)),o.push(setTimeout(h,Oe)))};return o.push(setTimeout(h,1200)),()=>{r=!1;for(const G of o)clearTimeout(G)}},[s]),e.jsxs(E.div,{className:"phone",initial:{opacity:0,y:26},animate:{opacity:1,y:0},transition:$,"aria-hidden":"true",children:[e.jsxs("div",{className:"phone-screen",children:[e.jsxs("div",{className:"phone-status",children:[e.jsx("span",{className:"phone-time",children:new Date().toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",hour12:!1})}),e.jsx("span",{className:`phone-lock${n==="answered"?" open":""}`,children:e.jsx(k,{size:13,strokeWidth:2.4})})]}),e.jsxs(E.div,{className:"notif",variants:qe,initial:"shown",animate:n,children:[e.jsx("span",{className:"notif-icon",children:e.jsx("img",{src:"/icon-192.png",alt:"",width:32,height:32})}),e.jsxs("span",{className:"notif-body",children:[e.jsxs("span",{className:"notif-top",children:[e.jsx("b",{children:"LongLeash"}),e.jsx("span",{className:"notif-ago",children:"now"})]}),e.jsxs("span",{className:"notif-text",children:["Claude wants to run ",e.jsx("b",{children:"Bash"})," in ~/LongLeash"]})]}),e.jsx("span",{className:"notif-tick","data-on":n==="answered"?"y":"n",children:e.jsx(I,{size:13,strokeWidth:3})})]}),e.jsx("div",{className:"phone-grid",children:[m,L,A,V,se,M,ce,H].map((r,o)=>e.jsxs("span",{className:"phone-key",children:[e.jsx(r,{size:20,strokeWidth:1.8}),o===5?e.jsx("span",{className:"phone-badge",children:"1"}):null]},o))})]}),e.jsx("div",{className:"phone-fade"}),e.jsxs("p",{className:"phone-caption",children:[e.jsx("b",{children:"The moment that matters."})," An agent hit something only you can answer, and you are not at your desk. Now that is fine."]})]})}_.createRoot(document.getElementById("root")).render(e.jsx(j.StrictMode,{children:e.jsx(Pe,{})}));
|
|
36
|
+
longleash service uninstall`}),e.jsxs("p",{children:["Stop and uninstall preserve settings, paired devices, audit history, repositories, and the managed runtime. Use ",e.jsx("code",{children:"longleash pair"})," to print a fresh QR without storing its complete link in service logs."]}),e.jsx("h2",{children:"Foreground mode remains available"}),e.jsx(o,{command:"longleash run ~/code"}),e.jsx("p",{children:"Keep that terminal open. LongLeash refuses to start a second daemon against the same data directory, so service and foreground processes cannot become competing database writers."}),e.jsx("h2",{children:"Linux logout behavior"}),e.jsx("p",{children:"A systemd user service may stop after a full logout unless lingering is enabled by the operating-system administrator. LongLeash reports this honestly and never enables lingering or requests root silently."})]})}function Ce(){return e.jsxs(c,{path:"/docs/daily-use",title:"One control surface, several kinds of session.",summary:"The origin label tells you where a process lives; the capability state tells you what LongLeash can safely do to it.",children:[e.jsx("h2",{children:"Read the session labels first"}),e.jsxs("dl",{className:"definition-grid",children:[e.jsx(w,{term:"From your phone",body:"A managed process LongLeash launched and can steer, stop, tune, and resume."}),e.jsx(w,{term:"In a terminal",body:"A provider process discovered through lifecycle hooks. Observation and takeover depend on its state."}),e.jsx(w,{term:"In VS Code",body:"A provider process associated with the IDE. LongLeash does not scrape or inject into private chat panels."}),e.jsx(w,{term:"Waiting for you",body:"A live question or permission needs a decision. Open that exact item before acting."})]}),e.jsx("h2",{children:"Approvals and questions"}),e.jsx("p",{children:"Open the notification or Inbox item, verify the session, working folder, tool, and input, then approve, deny, or answer. Decisions are idempotent: tapping twice cannot approve two operations. Expired or ended-session requests become non-actionable."}),e.jsx("h2",{children:"Steer, tune, and stop"}),e.jsxs("ul",{children:[e.jsxs("li",{children:[e.jsx("b",{children:"Reply"})," sends the next user turn to a managed session."]}),e.jsxs("li",{children:[e.jsx("b",{children:"Tune"})," applies supported model, effort, or thinking settings to the next turn while preserving the native conversation ID."]}),e.jsxs("li",{children:[e.jsx("b",{children:"Stop"})," asks the real provider process to end and waits for evidence that it exited."]})]}),e.jsx("p",{children:"A Terminal or VS Code process must transfer control before phone-side tuning. That explicit checkbox prevents LongLeash and the native surface from becoming two writers."}),e.jsx("h2",{children:"Move back to the laptop"}),e.jsxs("p",{children:["Reveal the terminal handoff and copy its provider-native resume command. If the process is active, stop or release it first. For the detailed ownership rules, see"," ",e.jsx("a",{href:r("/docs/session-portability"),children:"Session portability"}),"."]}),e.jsx("h2",{children:"Update without guessing"}),e.jsx(o,{command:`longleash update
|
|
37
|
+
longleash doctor`}),e.jsx("p",{children:"Restart the daemon after updating. An already running Node process cannot load the new files from disk."})]})}function Re(){return e.jsxs(c,{path:"/docs/troubleshooting",title:"Diagnose first. Restart second.",summary:"Start with the symptom you can see, preserve the log, and use the smallest recovery action that proves what changed.",children:[e.jsx("h2",{id:"first-minute",children:"The first minute"}),e.jsx(o,{command:`longleash doctor
|
|
38
|
+
longleash service logs`}),e.jsx("p",{children:"Record the local time, provider, origin label, session title, project path, and the exact action that failed. Redact source code and pairing URLs before posting evidence."}),e.jsx("h2",{id:"pairing",children:"Pairing says the laptop did not answer"}),e.jsxs("ol",{children:[e.jsxs("li",{children:["Leave the terminal running ",e.jsx("code",{children:"longleash"})," open."]}),e.jsxs("li",{children:["Run ",e.jsx("code",{children:"longleash doctor"})," in a second terminal and require ",e.jsx("b",{children:"reachable"}),"."]}),e.jsxs("li",{children:["Press ",e.jsx("code",{children:"n"})," then Enter in the daemon terminal for a new single-use QR."]}),e.jsx("li",{children:"Scan from inside the installed PWA; Safari and the home-screen app have separate storage on iPhone."}),e.jsx("li",{children:"If scanning remains unreliable, paste the complete fresh link into the app."})]}),e.jsx("h2",{id:"daemon",children:"“Already running” or “connection refused”"}),e.jsxs("p",{children:["“Already running” means one daemon owns the laptop profile; use it instead of starting a second. “Connection refused” means a hook or client reached an address with no listening daemon. Do not use a broad ",e.jsx("code",{children:"pkill"}),". Identify the listener first:"]}),e.jsx(o,{command:`lsof -nP -iTCP:4321 -sTCP:LISTEN
|
|
39
|
+
ps -p <PID> -o pid,ppid,command`}),e.jsx("h2",{id:"missing-session",children:"A Terminal or VS Code session is missing"}),e.jsxs("ol",{children:[e.jsxs("li",{children:["Run ",e.jsx("code",{children:"longleash doctor"}),"."]}),e.jsxs("li",{children:["If a hook is stale or missing, run ",e.jsx("code",{children:"longleash hooks"}),"."]}),e.jsx("li",{children:"Start a new provider process; an existing one retains its old environment and hook configuration."}),e.jsx("li",{children:"Cause one lifecycle/tool event. LongLeash discovers structured events, not terminal pixels."})]}),e.jsx("h2",{id:"stale",children:"A stale approval or old session remains"}),e.jsxs("p",{children:["Do not approve again. Record the session and decision time, refresh once, and compare builds with ",e.jsx("code",{children:"longleash doctor"}),". A decided request must close; a dormant conversation may remain deliberately as resumable history but must not look live."]}),e.jsx("h2",{id:"writer",children:"“This checkout is already controlled”"}),e.jsxs("p",{children:["One physical checkout permits one writer. Continue in the owner, stop/release it, or choose",e.jsx("b",{children:" Safe parallel"})," so LongLeash creates a Git worktree. Dirty tracked changes cause a safe refusal because silently copying an older HEAD would lose context."]}),e.jsx("h2",{id:"handoff",children:"Stop, takeover, or handoff did not complete"}),e.jsx("p",{children:"LongLeash requires proof that the native process exited before granting another writer. Close the relevant Terminal or IDE process on the laptop, wait for its lifecycle event, and retry once. If it still fails, keep the log. The refusal is protecting the conversation and checkout."}),e.jsxs("div",{className:"doc-callout",children:[e.jsx(D,{size:19,"aria-hidden":"true"}),e.jsxs("p",{children:["Still stuck? File a ",e.jsx("a",{href:`${d}/issues/new`,children:"reproducible GitHub issue"})," with doctor output and redacted logs. GitHub is the support tracker; this page remains the user guide."]})]})]})}function Ae(){return e.jsxs(c,{path:"/docs/security",title:"The laptop keeps authority.",summary:"LongLeash gives a paired phone a narrow control protocol. It does not turn your laptop into a generic remote shell or move your development environment into the cloud.",children:[e.jsx("h2",{children:"What stays on the laptop"}),e.jsxs("ul",{className:"check-list",children:[e.jsx("li",{children:"Provider credentials and subscription sessions."}),e.jsx("li",{children:"Repositories, working files, and Git worktrees."}),e.jsx("li",{children:"Provider processes and the durable transcript/audit database."}),e.jsx("li",{children:"The authority to start, stop, approve, tune, and revoke devices."})]}),e.jsx("h2",{children:"What the hosted relay can see"}),e.jsx("p",{children:"The relay routes end-to-end encrypted frames. It can observe operational metadata such as opaque room identifiers, timing, frame sizes, joins, leaves, and IP-level connection data. It cannot decrypt prompt, code, path, transcript, or approval content, and it stores no conversation history."}),e.jsx("h2",{children:"Pairing is device identity"}),e.jsx("p",{children:"A fresh QR contains a short-lived, single-use secret. Successful pairing creates revocable device credentials. If a phone is lost or a QR is exposed, revoke it from the laptop:"}),e.jsx(o,{command:`longleash devices
|
|
40
|
+
longleash revoke <device-id>`}),e.jsx("h2",{children:"Account identity is a separate lock"}),e.jsx("p",{children:"The official hosted app requires a verified LongLeash account before it can request a short-lived relay connection ticket. That account chooses which browser credential slot is visible; the QR still grants authority over a specific laptop. Signing into an account alone cannot control a machine, and a pairing secret is never sent to Google or the account provider. LAN and self-hosted deployments remain accountless."}),e.jsx("h2",{children:"The phone cannot execute arbitrary commands"}),e.jsx("p",{children:"It sends validated operations such as start a session, reply, approve a specific request, stop, or tune. There is no generic “run any shell command” endpoint. Workspace allowlists, one-writer leases, provider approval policy, and audit records remain in force."}),e.jsx("h2",{children:"Practical safety rules"}),e.jsxs("ul",{children:[e.jsx("li",{children:"Never share a pairing URL or unredacted screenshot containing one."}),e.jsx("li",{children:"Keep the OS, Node, provider CLI, browser, and LongLeash current."}),e.jsx("li",{children:"Review the session, folder, tool, and input before approving."}),e.jsx("li",{children:"Revoke devices you no longer control and use a device passcode."}),e.jsxs("li",{children:["Run ",e.jsx("code",{children:"longleash doctor"})," after every update."]})]}),e.jsxs("p",{children:["For implementation-level review, the"," ",e.jsx("a",{href:`${d}/blob/main/docs/ARCHITECTURE.md`,children:"architecture source"})," remains public."]})]})}function Ie(){return e.jsxs(c,{path:"/docs/session-portability",title:"One conversation. One active writer.",summary:"LongLeash preserves provider-native conversation IDs and only moves control after the current writer has verifiably released it.",children:[e.jsx("h2",{children:"Why the one-writer rule exists"}),e.jsx("p",{children:"Two interfaces writing the same live provider conversation can reorder messages, duplicate tools, or corrupt ownership. LongLeash therefore treats Terminal, VS Code, and phone control as mutually exclusive writers to one conversation."}),e.jsxs("div",{className:"portable-flow",children:[e.jsx("span",{children:"Terminal or VS Code"}),e.jsx(p,{"aria-hidden":"true"}),e.jsx("span",{children:"verified release"}),e.jsx(p,{"aria-hidden":"true"}),e.jsx("span",{children:"LongLeash"})]}),e.jsx("h2",{children:"Phone to Terminal"}),e.jsxs("p",{children:["Open the session, reveal ",e.jsx("b",{children:"Terminal handoff"}),", stop/release the live process if needed, copy the command, and run it on the laptop. Claude and Codex use their own native resume IDs."]}),e.jsx("h2",{children:"Phone to VS Code"}),e.jsx("p",{children:"The workspace handoff opens the correct folder. Claude can resume through its CLI/IDE connection. Codex resumes in the VS Code terminal today. LongLeash does not claim it can inject an existing thread into another vendor extension’s private chat panel."}),e.jsx("h2",{children:"Terminal or VS Code to phone"}),e.jsx("p",{children:"Choose the explicit transfer control option. The daemon reserves the checkout, asks the verified native process to end, confirms its exit, then resumes the same conversation as a managed session. If confirmation fails, the transfer is cancelled and the original owner remains."}),e.jsx("h2",{children:"Parallel work in one Git project"}),e.jsxs("p",{children:["A second managed agent can use ",e.jsx("b",{children:"Safe parallel"}),". LongLeash creates an isolated worktree and branch so each agent writes different filesystems. It never auto-merges, commits, pushes, or deletes that work. Non-Git directories remain sequential."]}),e.jsxs("div",{className:"doc-callout safe",children:[e.jsx(T,{size:19,"aria-hidden":"true"}),e.jsxs("p",{children:[e.jsx("b",{children:"Conversation portability and code parallelism are different."})," A handoff moves one conversation; a worktree isolates a second writer."]})]})]})}function Pe(){return e.jsxs(c,{path:"/docs/faq",title:"Straight answers before you install.",summary:"The current public preview is local-first and honest about hosted identity, provider, and platform boundaries.",children:[e.jsx(u,{q:"Do I need a LongLeash account?",children:"The official hosted app requires a free LongLeash account through Google or verified email. A QR is still required for each laptop: account identity and device authority are separate. LAN and self-hosted use remain accountless. Provider logins stay with Claude Code or Codex."}),e.jsx(u,{q:"Does my laptop need to stay awake?",children:"Yes. Your laptop runs the agents and daemon. Relay mode reaches it from another network, but LongLeash cannot operate a sleeping, powered-off, or disconnected machine."}),e.jsxs(u,{q:"Can I use cellular data?",children:["Yes, when the daemon reports a configured relay and the app shows ",e.jsx("code",{children:"linked · relay"}),". Direct mode is an optional same-network path."]}),e.jsx(u,{q:"Which agents work today?",children:"Claude Code and Codex. LongLeash uses structured provider integrations and lifecycle hooks; it does not screen-scrape arbitrary agent TUIs."}),e.jsx(u,{q:"Can I change model, effort, or thinking mid-session?",children:"Managed sessions expose Tune for settings the provider supports. An externally controlled Terminal or VS Code session must explicitly transfer to LongLeash first so there is one writer."}),e.jsx(u,{q:"Can two agents work in the same project?",children:"Yes for managed sessions in a clean Git repository through Safe parallel worktrees. Two agents must not write the same physical checkout. Non-Git folders remain sequential."}),e.jsx(u,{q:"Is LongLeash free?",children:"The repository is MIT licensed. You still need your own provider access. Hosted relay and future team or premium services may develop separate pricing; nothing on this page promises an unshipped tier."}),e.jsx(u,{q:"Is the VS Code companion available?",children:"Not publicly yet. Its secure protocol, package verification, compatibility checks, and native session-tree foundation exist. Authenticated live daemon sync is the next engineering checkpoint."})]})}function Oe(){return e.jsxs(c,{path:"/roadmap",eyebrow:"Product roadmap",title:"What works. What comes next.",summary:"A quick view of the product you can use today and the capabilities we are proving next. Building and exploring are directions, not promised dates.",children:[e.jsx(v,{icon:g,state:"Available now",tone:"shipped",title:"Control real agent sessions from your phone",children:e.jsxs("ul",{className:"roadmap-list",children:[e.jsx("li",{children:"See Claude Code and Codex sessions from Terminal, VS Code, and phone launches."}),e.jsx("li",{children:"Reply, approve, deny, stop, tune, and return to a provider-native terminal conversation."}),e.jsx("li",{children:"Run safe parallel work in isolated Git worktrees without creating two writers."}),e.jsx("li",{children:"Review and edit an agent-to-agent briefing before starting an attributed child session."}),e.jsx("li",{children:"Choose the hosted relay, your own relay, or LAN-only operation."}),e.jsx("li",{children:"Keep the laptop reachable with a per-user background service and authenticated health checks."})]})}),e.jsx(v,{icon:m,state:"Building",tone:"building",title:"Install once and keep the laptop ready",children:e.jsxs("ul",{className:"roadmap-list",children:[e.jsx("li",{children:"A provenance-backed npm package with no maintainer-specific install URL."}),e.jsx("li",{children:"A matching code on phone and terminal before a durable device is trusted."}),e.jsx("li",{children:"A local dashboard for service health, devices, sessions, approvals, roots, and updates."})]})}),e.jsx(v,{icon:I,state:"Building",tone:"building",title:"A trustworthy VS Code companion",children:e.jsxs("ul",{className:"roadmap-list",children:[e.jsx("li",{children:"An authenticated extension connection with workspace-trust enforcement."}),e.jsx("li",{children:"A native session tree and exact provider-aware status inside the IDE."}),e.jsx("li",{children:"Visible fallbacks when a vendor does not expose an existing conversation to its chat panel."}),e.jsx("li",{children:"Fail-closed compatibility when a provider changes an integration contract."})]})}),e.jsx(v,{icon:N,state:"Building",tone:"building",title:"Let your agent set it up safely",children:e.jsxs("ul",{className:"roadmap-list",children:[e.jsx("li",{children:"A local MCP server for setup plans, diagnostics, service lifecycle, devices, and sessions."}),e.jsx("li",{children:"Dry-run first, explicit confirmation for persistent changes, and atomic rollback."}),e.jsx("li",{children:"No generic shell, secret return, approval bypass, or public MCP listener."})]})}),e.jsx(v,{icon:T,state:"Exploring",tone:"planned",title:"Teams of specialists with visible human review",children:e.jsxs("ul",{className:"roadmap-list",children:[e.jsx("li",{children:"Multiple isolated children with explicit scope, budgets, comparison, and return review."}),e.jsx("li",{children:"Safe merge workflows that never hide an unbounded autonomous loop."}),e.jsx("li",{children:"Optional team administration and operated services without moving provider credentials or code into our account plane."})]})}),e.jsxs("p",{className:"roadmap-engineering-note",children:["Want implementation detail? Maintainers can inspect the granular"," ",e.jsx("a",{href:`${d}/blob/main/PLAN.md`,children:"engineering plan"}),". Public product language stays focused on outcomes rather than internal milestones."]})]})}function Ee(){return e.jsxs(c,{path:"/privacy",eyebrow:"Legal & trust",title:"Privacy by a smaller data boundary.",summary:"This notice describes the account-enabled public preview and the deliberately small hosted data boundary.",children:[e.jsx("p",{className:"effective",children:"Effective 15 August 2026"}),e.jsx("h2",{children:"What the hosted account contains"}),e.jsx("p",{children:"The official hosted app uses Clerk for authentication. You may sign in with Google, an email verification code, or email and password. LongLeash never receives or stores your password. The account may contain a stable account identifier, name, email address, profile image, sign-in times, and security/session metadata needed to prevent abuse. LongLeash currently has no advertising, mailing-list, payment, or product-analytics integration and does not sell personal information."}),e.jsx("h2",{children:"Development data stays local"}),e.jsx("p",{children:"Repositories, provider credentials, agent processes, transcript history, approval records, and the durable audit database stay on the laptop running LongLeash. The phone receives current content only after an authenticated, encrypted connection."}),e.jsx("h2",{children:"Pairing credentials stay in the browser and laptop"}),e.jsx("p",{children:"Each hosted account receives a separate browser storage slot for its paired-device token, relay key, and relay address. Those credentials are not uploaded to Clerk or Google. A short-lived signed relay ticket contains an opaque one-way account tag, room, role, and expiry; it cannot decrypt session traffic and is not stored as conversation history."}),e.jsx("h2",{children:"Relay and hosting metadata"}),e.jsx("p",{children:"The hosted relay processes encrypted frames and normal network metadata needed to route a connection, such as IP addresses, timestamps, frame sizes, and opaque room identifiers. It does not have the keys required to read frame content and stores no conversation history. Cloudflare may process ordinary security and operational data as the hosting provider."}),e.jsx("h2",{children:"Push notifications"}),e.jsx("p",{children:"If enabled, push messages contain identifiers needed to wake and route the app, not prompt, source code, path, transcript, or approval content. The app fetches the current item after reconnecting."}),e.jsx("h2",{children:"Processors and purpose"}),e.jsx("p",{children:"Cloudflare hosts the public site, static app, abuse controls, and encrypted relay; Clerk provides account, credential, verification, and session management; Google verifies identity only when you choose Google sign-in. LongLeash requests only basic OpenID identity scopes: name, email, and profile. It does not request Gmail, Drive, source repositories, or provider accounts. Each processor may handle ordinary network and security metadata under its own terms and privacy notice."}),e.jsx("h2",{children:"Retention, export, and deletion"}),e.jsxs("p",{children:["Account records remain until you delete the account or they must be retained for security or legal obligations. Open the account control in the app header to download the hosted account fields LongLeash exposes or permanently delete the account. Deletion also clears that account’s credentials from the current browser; laptop-local transcripts are outside the hosted account and must be managed on the laptop. You may also contact"," ",e.jsx("a",{href:"mailto:privacy@longleash.dev",children:"privacy@longleash.dev"})," for an access, correction, export, or deletion request, subject to applicable law and identity verification."]}),e.jsx("h2",{children:"Security and privacy contact"}),e.jsxs("p",{children:["Use ",e.jsx("a",{href:"mailto:security@longleash.dev",children:"security@longleash.dev"})," for private security reports and ",e.jsx("a",{href:"mailto:support@longleash.dev",children:"support@longleash.dev"})," for ordinary questions. Do not place pairing links, tokens, private code, or exploit details in a public issue. The public ",e.jsx("a",{href:`${d}/issues`,children:"issue tracker"})," is only for redacted, non-sensitive reports."]})]})}function De(){return e.jsxs(c,{path:"/terms",eyebrow:"Legal",title:"Terms of Service",summary:"Rules for the free hosted public preview. The MIT license continues to govern the open-source code itself.",children:[e.jsx("p",{className:"effective",children:"Effective 15 August 2026"}),e.jsx("h2",{children:"Agreement and operator"}),e.jsxs("p",{children:["These terms govern use of the LongLeash-hosted website, account service, and encrypted relay, operated by Sahith Reddy Thummala. By using the hosted service you agree to these terms and the ",e.jsx("a",{href:r("/privacy"),children:"Privacy Notice"}),". If you do not agree, use the MIT-licensed source locally or stop using the hosted service."]}),e.jsx("h2",{children:"Eligibility and your responsibilities"}),e.jsx("p",{children:"You must be legally able to enter this agreement and use LongLeash only with computers, repositories, provider accounts, and data you are authorized to control. You are responsible for securing your LongLeash sign-in method, phone, laptop, pairing links, provider subscriptions, and actions approved through the product."}),e.jsx("h2",{children:"Acceptable use"}),e.jsx("p",{children:"Do not use the hosted service to gain unauthorized access; distribute malware; evade provider or network controls; interfere with another user; probe rooms or accounts you do not own; overload the relay; violate law or third-party rights; or expose another person’s secrets. LongLeash may rate-limit, suspend, or terminate access needed to protect users and infrastructure."}),e.jsx("h2",{children:"Third-party services"}),e.jsx("p",{children:"Claude, Codex, Google, Clerk, Cloudflare, GitHub, and any other third-party service have their own terms, availability, and fees. LongLeash does not include an AI subscription and cannot grant rights those providers do not give you."}),e.jsx("h2",{children:"Preview availability and changes"}),e.jsx("p",{children:"The hosted service is a free public preview. Features may change, be rate-limited, or be discontinued, and temporary outages may occur. Material changes to these terms or the hosted data boundary will be published before they take effect when reasonably possible."}),e.jsx("h2",{children:"Open-source license and feedback"}),e.jsxs("p",{children:["Repository code is offered under the ",e.jsx("a",{href:r("/license"),children:"MIT License"}),". These service terms do not remove rights granted by that license. You may provide feedback; unless separately agreed, LongLeash may use it without obligation while respecting confidential information."]}),e.jsx("h2",{children:"Disclaimers and liability"}),e.jsx("p",{children:"To the maximum extent permitted by applicable law, the preview is provided “as is” and “as available,” without warranties. You remain responsible for reviewing agent output, approvals, backups, repository state, and consequences of commands. To the maximum extent permitted by law, LongLeash is not liable for indirect, incidental, special, consequential, or lost-profit damages arising from the free hosted preview. Rights that cannot legally be excluded remain unaffected."}),e.jsx("h2",{children:"Ending use and contact"}),e.jsxs("p",{children:["You may stop using the service and delete your account at any time. LongLeash may suspend or end hosted access for breach, abuse, security risk, or service closure. Questions about these terms may be sent to ",e.jsx("a",{href:"mailto:support@longleash.dev",children:"support@longleash.dev"}),"."]})]})}function qe(){return e.jsxs(c,{path:"/license",eyebrow:"Legal",title:"MIT License",summary:"LongLeash is open-source software. This is the complete license text shipped in the repository.",children:[e.jsx("pre",{className:"license-copy",children:be}),e.jsxs("p",{children:["The canonical plain-text copy is also available in the ",e.jsx("a",{href:`${d}/blob/main/LICENSE`,children:"source repository"}),"."]})]})}function Me(){return e.jsxs("main",{className:"public-main not-found",id:"main",children:[e.jsx("p",{className:"land-label",children:"404"}),e.jsx("h1",{children:"This page is not on the leash."}),e.jsx("p",{children:"The address may have changed. The documentation index and product remain available."}),e.jsxs("div",{className:"hero-acts",children:[e.jsx("a",{className:"key primary",href:r("/docs"),children:"Open documentation"}),e.jsx("a",{className:"tap",href:r("/"),children:"Return home"})]})]})}function C({icon:s,title:n,body:i}){return e.jsxs("span",{className:"model-step",children:[e.jsx(s,{size:20,"aria-hidden":"true"}),e.jsxs("span",{children:[e.jsx("b",{children:n}),e.jsx("small",{children:i})]})]})}function w({term:s,body:n}){return e.jsxs("div",{children:[e.jsx("dt",{children:s}),e.jsx("dd",{children:n})]})}function u({q:s,children:n}){return e.jsxs("section",{className:"faq-item",children:[e.jsx("h2",{children:s}),e.jsx("p",{children:n})]})}function k({mode:s}){return e.jsxs("section",{className:"agent-setup-note",children:[e.jsx("span",{className:"doc-icon",children:e.jsx(N,{size:19,"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("span",{className:"road-state building",children:"Coming before public release"}),e.jsx("h3",{children:"Got no time? Let your agent wire it."}),e.jsxs("p",{children:["The LongLeash MCP will inspect prerequisites, propose a dry-run plan for ",s,", apply only the changes you approve, and return a redacted health report. It will not receive pairing secrets, provider credentials, or generic shell access."]})]})]})}function v({icon:s,state:n,tone:i,title:a,children:t}){return e.jsxs("section",{className:"roadmap-stage",children:[e.jsx("span",{className:"roadmap-stage-icon",children:e.jsx(s,{size:20,"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("span",{className:`road-state ${i}`,children:n}),e.jsx("h2",{children:a}),t]})]})}function o({command:s}){const[n,i]=f.useState(!1);async function a(){try{await navigator.clipboard.writeText(s),i(!0),window.setTimeout(()=>i(!1),1800)}catch{i(!1)}}return e.jsxs("div",{className:"doc-code",children:[e.jsx("pre",{children:e.jsx("code",{children:s})}),e.jsxs("button",{type:"button",onClick:()=>{a()},"aria-label":"Copy command",children:[n?e.jsx(S,{size:15,"aria-hidden":"true"}):e.jsx(E,{size:15,"aria-hidden":"true"}),n?"Copied":"Copy"]})]})}const We=[{title:"Start here",body:"Requirements, installation, pairing, daily use, updates, and honest limits.",href:"/docs/getting-started",icon:q},{title:"Choose your connection",body:"Compare the hosted relay, your own relay, and LAN-only operation before setup.",href:"/docs/connectivity",icon:x},{title:"Troubleshooting",body:"Symptom-first fixes for pairing, hooks, stale state, handoffs, and connection errors.",href:"/docs/troubleshooting",icon:N},{title:"Security model",body:"What the laptop, phone, and relay can see, plus where LongLeash deliberately stops.",href:"/docs/security",icon:j},{title:"Session portability",body:"Move work between phone, Terminal, and VS Code without creating two writers.",href:"/docs/session-portability",icon:m}];function He(){const s=ye();if(s!=="/")return e.jsx(we,{path:s});const n=P();return e.jsxs("div",{className:"land",children:[e.jsx("a",{className:"skip-link",href:"#main",children:"Skip to content"}),e.jsx(H,{}),e.jsxs("main",{id:"main",children:[e.jsxs("section",{className:"hero",id:"top",children:[e.jsxs("div",{className:"hero-copy",children:[e.jsxs("p",{className:"eyebrow",children:[e.jsx("span",{className:"live-dot","aria-hidden":"true"})," Public preview · Open source"]}),e.jsxs("h1",{children:["Your agents keep working. ",e.jsx("em",{children:"You can leave the desk."})]}),e.jsx("p",{className:"lede",children:"Run Claude Code and Codex on your laptop, then read, steer, approve, stop, tune, and hand off their sessions from your phone. Your laptop stays in control; LongLeash is the secure control surface in your pocket."}),e.jsxs("div",{className:"hero-acts",children:[e.jsxs("a",{className:"key primary",href:r("/#start"),children:["Install in five minutes",e.jsx(p,{size:17,strokeWidth:2.4,"aria-hidden":"true"})]}),e.jsx("a",{className:"tap",href:n,children:"Already paired? Open app"})]}),e.jsx("p",{className:"hero-fine",children:"macOS or Linux · Node.js 22+ · your existing Claude or Codex login · free hosted account"})]}),e.jsx(Ge,{})]}),e.jsxs("section",{className:"signal-strip","aria-label":"Current product support",children:[e.jsxs("span",{children:[e.jsx("b",{children:"Claude Code"})," managed + observed sessions"]}),e.jsxs("span",{children:[e.jsx("b",{children:"Codex"})," managed + observed sessions"]}),e.jsxs("span",{children:[e.jsx("b",{children:"Anywhere"})," encrypted relay"]})]}),e.jsxs("section",{className:"product",id:"product",children:[e.jsx(b,{eyebrow:"What ships today",title:"One place to see what needs you.",body:"This is working software, not a concept page. See Claude Code and Codex sessions, make typed decisions, preserve provider conversations, and delegate work with a human review point."}),e.jsxs("div",{className:"feature-grid",children:[e.jsx(y,{icon:M,title:"Sessions and approvals",body:"See phone, Terminal, and VS Code sessions with clear provider and origin labels. Approve, deny, answer, steer, or stop from the exact session."}),e.jsx(y,{icon:T,title:"Safe parallel work",body:"Start a second writer in the same Git project through an isolated worktree instead of letting two agents overwrite one checkout."}),e.jsx(y,{icon:W,title:"Model and reasoning controls",body:"Choose model, effort, and supported thinking settings at launch, during a managed conversation, or for a reviewed delegated child."}),e.jsx(y,{icon:x,title:"Reviewed agent delegation",body:"Build and edit a briefing on your phone, start a Claude or Codex child, then review its return before anything reaches the parent."}),e.jsx(y,{icon:m,title:"Exact terminal handoff",body:"Copy a provider-native resume command and move the same conversation back to your keyboard without pretending a new chat is the old one."}),e.jsx(y,{icon:D,title:"Private notifications",body:"Push notifications wake the app with identifiers only. Prompt, code, path, and approval content are fetched after the encrypted link reconnects."})]})]}),e.jsxs("section",{className:"system",children:[e.jsx(b,{eyebrow:"How it works",title:"The powerful part never leaves your laptop.",body:"LongLeash is a control plane, not a hosted coding environment. Your provider credentials, repositories, agent processes, transcripts, and durable audit data stay local."}),e.jsxs("div",{className:"system-flow","aria-label":"LongLeash data flow",children:[e.jsxs("div",{className:"flow-node",children:[e.jsx("span",{className:"flow-icon",children:e.jsx(m,{"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("b",{children:"Your laptop"}),e.jsx("span",{children:"daemon · agents · code · keys"})]})]}),e.jsxs("span",{className:"flow-link",children:[e.jsx(L,{size:13,"aria-hidden":"true"})," sealed frames"]}),e.jsxs("div",{className:"flow-node",children:[e.jsx("span",{className:"flow-icon",children:e.jsx(x,{"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("b",{children:"Relay"}),e.jsx("span",{children:"routes ciphertext · stores no history"})]})]}),e.jsxs("span",{className:"flow-link",children:[e.jsx(L,{size:13,"aria-hidden":"true"})," sealed frames"]}),e.jsxs("div",{className:"flow-node",children:[e.jsx("span",{className:"flow-icon",children:e.jsx(g,{"aria-hidden":"true"})}),e.jsxs("div",{children:[e.jsx("b",{children:"Your phone"}),e.jsx("span",{children:"review · decide · steer"})]})]})]}),e.jsxs("div",{className:"truth-row",children:[e.jsxs("p",{children:[e.jsx("b",{children:"Two separate locks."})," Your verified account identifies the hosted user; a fresh QR grants one browser authority over one laptop. Local/self-hosted use stays accountless."]}),e.jsxs("p",{children:[e.jsx("b",{children:"No generic remote shell."})," The phone can call a small typed protocol, not execute arbitrary commands behind your back."]}),e.jsxs("p",{children:[e.jsx("b",{children:"No public port at home."})," The daemon dials outward to the relay; router changes and inbound firewall holes are unnecessary."]})]})]}),e.jsxs("section",{className:"start",id:"start",children:[e.jsx(b,{eyebrow:"Start in five minutes",title:"Laptop first. Phone second.",body:"LongLeash does not include Claude or Codex. Install and sign in to at least one provider CLI first, then follow this path."}),e.jsxs("div",{className:"install-shell",children:[e.jsxs("div",{className:"install-head",children:[e.jsxs("span",{children:[e.jsx(m,{size:17,"aria-hidden":"true"})," Terminal"]}),e.jsx("span",{className:"mono",children:"macOS · Linux"})]}),e.jsx("code",{children:R}),e.jsx(ze,{command:R})]}),e.jsxs("p",{className:"inspect-note",children:["Prefer to inspect before running?"," ",e.jsx("a",{href:`${d}/tree/main/packages/cli`,children:"Read the CLI package source"})," and"," ",e.jsx("a",{href:`${d}/blob/main/docs/NPM-RELEASE.md`,children:"release controls"})," first."]}),e.jsxs("ol",{className:"steps",children:[e.jsxs("li",{children:[e.jsx("span",{className:"n",children:"01"}),e.jsxs("div",{children:[e.jsx("h3",{children:"Install LongLeash"}),e.jsxs("p",{children:["Setup never uses ",e.jsx("code",{children:"sudo"}),". It verifies the pinned npm package, installs under your home directory, reviews roots and connectivity, and wires supported hooks."]})]})]}),e.jsxs("li",{children:[e.jsx("span",{className:"n",children:"02"}),e.jsxs("div",{children:[e.jsx("h3",{children:"Keep the laptop ready"}),e.jsxs("p",{children:["Accept the recommended per-user background service, or deliberately choose"," ",e.jsx("code",{children:"longleash run ~/code"})," and keep that foreground terminal open."]})]})]}),e.jsxs("li",{children:[e.jsx("span",{className:"n",children:"03"}),e.jsxs("div",{children:[e.jsx("h3",{children:"Install the phone app, sign in, then pair"}),e.jsxs("p",{children:["On iPhone, open the hosted app in Safari and add it to your home screen first. Open that installed copy, sign in with Google or verified email, then use its scanner on the fresh QR. Require the header to say"," ",e.jsx("code",{children:"linked · relay"})," or ",e.jsx("code",{children:"linked · direct"}),". Pairing links are single-use secrets."]})]})]}),e.jsxs("li",{children:[e.jsx("span",{className:"n",children:"04"}),e.jsxs("div",{children:[e.jsx("h3",{children:"Verify before relying on it"}),e.jsxs("p",{children:["Run ",e.jsx("code",{children:"longleash doctor"}),". The daemon must be reachable, every installed hook must be current, and laptop, daemon, and relay builds must agree."]})]})]})]}),e.jsxs("div",{className:"first-run",children:[e.jsxs("div",{children:[e.jsx("span",{className:"land-stamp",children:"FIRST REAL TEST"}),e.jsx("h3",{children:"Start with a harmless task"}),e.jsx("p",{children:"Ask Claude or Codex to inspect a small folder, trigger one approval, answer it from the phone, stop the session, then reopen it from the terminal handoff. Only move to important work after that loop behaves correctly on your devices and network."})]}),e.jsxs("a",{className:"key",href:r("/docs/getting-started#first-test"),children:["Open acceptance checklist",e.jsx(p,{size:16,"aria-hidden":"true"})]})]})]}),e.jsxs("section",{className:"docs",id:"docs",children:[e.jsx(b,{eyebrow:"Documentation",title:"Answers before guesswork.",body:"Read the complete guides here without leaving the product site. GitHub remains available when you want source code or the issue tracker."}),e.jsx("div",{className:"docs-grid",children:We.map(({title:i,body:a,href:t,icon:h})=>e.jsxs("a",{className:"doc-card",href:r(t),children:[e.jsx("span",{className:"doc-icon",children:e.jsx(h,{size:19,"aria-hidden":"true"})}),e.jsxs("span",{children:[e.jsx("b",{children:i}),e.jsx("span",{children:a})]}),e.jsx(p,{size:15,"aria-hidden":"true"})]},i))}),e.jsxs("div",{className:"support-line",children:[e.jsxs("span",{children:["Something failed? Run ",e.jsx("code",{children:"longleash doctor"})," before restarting anything."]}),e.jsxs("span",{children:[e.jsx("a",{href:r("/docs/troubleshooting"),children:"Open troubleshooting"})," · ",e.jsx("a",{href:`${d}/issues/new`,children:"Report on GitHub"})]})]})]}),e.jsxs("section",{className:"roadmap",id:"roadmap",children:[e.jsx(b,{eyebrow:"Roadmap",title:"What works. What comes next.",body:"See the product you can use today and the capabilities we are proving next. Building means active work, not a promised date."}),e.jsxs("div",{className:"roadmap-grid",children:[e.jsxs("article",{children:[e.jsx("span",{className:"roadmap-card-icon",children:e.jsx(g,{size:19,"aria-hidden":"true"})}),e.jsx("span",{className:"road-state shipped",children:"Available now"}),e.jsx("h3",{children:"Phone control and reviewed delegation"}),e.jsx("p",{children:"Claude and Codex sessions, approvals, tuning, handoffs, safe parallel phone starts, and human-reviewed agent-to-agent briefing and return flows."})]}),e.jsxs("article",{children:[e.jsx("span",{className:"roadmap-card-icon",children:e.jsx(m,{size:19,"aria-hidden":"true"})}),e.jsx("span",{className:"road-state shipped",children:"Available now"}),e.jsx("h3",{children:"Reliable install and background service"}),e.jsx("p",{children:"Verified npm distribution, a per-user background service, authenticated health, redacted logs, clean removal, and safe foreground fallback."})]}),e.jsxs("article",{children:[e.jsx("span",{className:"roadmap-card-icon",children:e.jsx(j,{size:19,"aria-hidden":"true"})}),e.jsx("span",{className:"road-state building",children:"Building"}),e.jsx("h3",{children:"Verified pairing and local operations"}),e.jsx("p",{children:"A phone-to-terminal matching code and a local service, device, session, and update dashboard."})]}),e.jsxs("article",{children:[e.jsx("span",{className:"roadmap-card-icon",children:e.jsx(I,{size:19,"aria-hidden":"true"})}),e.jsx("span",{className:"road-state building",children:"Building"}),e.jsx("h3",{children:"VS Code companion and agent setup"}),e.jsx("p",{children:"Authenticated IDE visibility plus a local MCP that can plan setup, diagnose failures, and apply only the changes you approve."})]})]}),e.jsxs("div",{className:"honesty",children:[e.jsx(j,{size:19,"aria-hidden":"true"}),e.jsxs("p",{children:[e.jsx("b",{children:"An honest platform boundary:"})," LongLeash never writes into another extension’s private chat webview. Unsupported native-panel continuation fails visibly and keeps a copyable terminal fallback. The companion extension will use only documented or LongLeash-owned surfaces."]})]}),e.jsxs("a",{className:"roadmap-link",href:r("/roadmap"),children:["Read the public roadmap ",e.jsx(p,{size:16,"aria-hidden":"true"})]})]})]}),e.jsx(z,{})]})}function b({eyebrow:s,title:n,body:i}){return e.jsxs("div",{className:"section-heading",children:[e.jsx("p",{className:"land-label",children:s}),e.jsx("h2",{children:n}),e.jsx("p",{children:i})]})}function y({icon:s,title:n,body:i}){return e.jsxs("article",{className:"feature",children:[e.jsx("span",{className:"feature-icon",children:e.jsx(s,{size:19,"aria-hidden":"true"})}),e.jsx("h3",{children:n}),e.jsx("p",{children:i})]})}function ze({command:s}){const[n,i]=f.useState(!1);async function a(){try{await navigator.clipboard.writeText(s),i(!0),window.setTimeout(()=>i(!1),1800)}catch{i(!1)}}return e.jsxs("button",{className:"copy-command",type:"button",onClick:()=>{a()},children:[n?e.jsx(S,{size:16,"aria-hidden":"true"}):e.jsx(E,{size:16,"aria-hidden":"true"}),n?"Copied":"Copy"]})}const Ve=6200,Fe={hidden:{y:-84,scale:.9,opacity:0,filter:"blur(6px)"},shown:{y:0,scale:1,opacity:1,filter:"blur(0px)",transition:{type:"spring",stiffness:320,damping:28}},answered:{y:-84,scale:.92,opacity:0,filter:"blur(4px)",transition:{duration:.3,ease:[.4,0,1,1]}}};function Ge(){const s=G(),[n,i]=f.useState("shown");return f.useEffect(()=>{if(s)return;let a=!0;const t=[],h=()=>{a&&(i("shown"),t.push(setTimeout(()=>a&&i("answered"),3600)),t.push(setTimeout(()=>a&&i("hidden"),4100)),t.push(setTimeout(h,Ve)))};return t.push(setTimeout(h,1200)),()=>{a=!1;for(const F of t)clearTimeout(F)}},[s]),e.jsxs(O.div,{className:"phone",initial:{opacity:0,y:26},animate:{opacity:1,y:0},transition:Y,"aria-hidden":"true",children:[e.jsxs("div",{className:"phone-screen",children:[e.jsxs("div",{className:"phone-status",children:[e.jsx("span",{className:"phone-time",children:new Date().toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",hour12:!1})}),e.jsx("span",{className:`phone-lock${n==="answered"?" open":""}`,children:e.jsx(L,{size:13,strokeWidth:2.4})})]}),e.jsxs(O.div,{className:"notif",variants:Fe,initial:"shown",animate:n,children:[e.jsx("span",{className:"notif-icon",children:e.jsx("img",{src:"/icon-192.png",alt:"",width:32,height:32})}),e.jsxs("span",{className:"notif-body",children:[e.jsxs("span",{className:"notif-top",children:[e.jsx("b",{children:"LongLeash"}),e.jsx("span",{className:"notif-ago",children:"now"})]}),e.jsxs("span",{className:"notif-text",children:["Claude wants to run ",e.jsx("b",{children:"Bash"})," in ~/LongLeash"]})]}),e.jsx("span",{className:"notif-tick","data-on":n==="answered"?"y":"n",children:e.jsx(S,{size:13,strokeWidth:3})})]}),e.jsx("div",{className:"phone-grid",children:[m,T,I,$,ie,M,me,W].map((a,t)=>e.jsxs("span",{className:"phone-key",children:[e.jsx(a,{size:20,strokeWidth:1.8}),t===5?e.jsx("span",{className:"phone-badge",children:"1"}):null]},t))})]}),e.jsx("div",{className:"phone-fade"}),e.jsxs("p",{className:"phone-caption",children:[e.jsx("b",{children:"The moment that matters."})," An agent hit something only you can answer, and you are not at your desk. Now that is fine."]})]})}_.createRoot(document.getElementById("root")).render(e.jsx(f.StrictMode,{children:e.jsx(He,{})}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"build":"
|
|
1
|
+
{"build":"3f15e73"}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
crossorigin
|
|
28
28
|
/>
|
|
29
29
|
<title>LongLeash</title>
|
|
30
|
-
<script type="module" crossorigin src="/assets/app-
|
|
30
|
+
<script type="module" crossorigin src="/assets/app-DS3tqdbW.js"></script>
|
|
31
31
|
<link rel="modulepreload" crossorigin href="/assets/styles-C-1gzhnT.js">
|
|
32
32
|
<link rel="stylesheet" crossorigin href="/assets/styles-CkAMG7sK.css">
|
|
33
33
|
</head>
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
crossorigin
|
|
34
34
|
/>
|
|
35
35
|
<title>LongLeash | Leave the desk. Keep control.</title>
|
|
36
|
-
<script type="module" crossorigin src="/assets/welcome-
|
|
36
|
+
<script type="module" crossorigin src="/assets/welcome-CitqOflI.js"></script>
|
|
37
37
|
<link rel="modulepreload" crossorigin href="/assets/styles-C-1gzhnT.js">
|
|
38
38
|
<link rel="stylesheet" crossorigin href="/assets/styles-CkAMG7sK.css">
|
|
39
39
|
<link rel="stylesheet" crossorigin href="/assets/welcome-ai7kxCj7.css">
|
|
@@ -5614,7 +5614,9 @@ var subscribeMessage = external_exports.object({
|
|
|
5614
5614
|
...clientBase,
|
|
5615
5615
|
type: external_exports.literal("subscribe"),
|
|
5616
5616
|
sessionId: external_exports.string().min(1),
|
|
5617
|
-
fromCursor: external_exports.number().int().nonnegative()
|
|
5617
|
+
fromCursor: external_exports.number().int().nonnegative(),
|
|
5618
|
+
/** Optional replay barrier. New clients use the echoed id to paint catch-up only once. */
|
|
5619
|
+
syncId: external_exports.string().min(1).max(120).optional()
|
|
5618
5620
|
}).passthrough();
|
|
5619
5621
|
var decisionMessage = external_exports.object({
|
|
5620
5622
|
...clientBase,
|
|
@@ -8636,6 +8638,9 @@ var RelayLink = class {
|
|
|
8636
8638
|
reconnectTimer = null;
|
|
8637
8639
|
keepaliveTimer = null;
|
|
8638
8640
|
keepaliveMs;
|
|
8641
|
+
/** WebCrypto promises may resolve out of order; preserve the server's event order explicitly. */
|
|
8642
|
+
outbound = Promise.resolve();
|
|
8643
|
+
inbound = Promise.resolve();
|
|
8639
8644
|
constructor(opts) {
|
|
8640
8645
|
this.opts = opts;
|
|
8641
8646
|
this.log = opts.log ?? (() => {
|
|
@@ -8672,10 +8677,12 @@ var RelayLink = class {
|
|
|
8672
8677
|
const socket = this.socket;
|
|
8673
8678
|
const identity = this.identity;
|
|
8674
8679
|
if (this.currentStatus !== "connected" || !socket || !identity) return;
|
|
8675
|
-
|
|
8676
|
-
|
|
8680
|
+
this.outbound = this.outbound.then(async () => {
|
|
8681
|
+
const payload = await seal(identity, plaintext);
|
|
8682
|
+
if (this.socket === socket && socket.readyState === WebSocket.OPEN) {
|
|
8677
8683
|
socket.send(JSON.stringify({ v: 1, type: "frame", payload }));
|
|
8678
8684
|
}
|
|
8685
|
+
}).catch(() => {
|
|
8679
8686
|
});
|
|
8680
8687
|
}
|
|
8681
8688
|
async connect() {
|
|
@@ -8714,9 +8721,12 @@ var RelayLink = class {
|
|
|
8714
8721
|
return;
|
|
8715
8722
|
}
|
|
8716
8723
|
if (message.type === "frame" && typeof message.payload === "string") {
|
|
8717
|
-
|
|
8724
|
+
const payload = message.payload;
|
|
8725
|
+
this.inbound = this.inbound.then(async () => {
|
|
8726
|
+
const plaintext = await open(identity, payload);
|
|
8718
8727
|
if (plaintext !== null) this.opts.onMessage(plaintext);
|
|
8719
8728
|
else this.log("dropped a relay frame that failed to open");
|
|
8729
|
+
}).catch(() => {
|
|
8720
8730
|
});
|
|
8721
8731
|
return;
|
|
8722
8732
|
}
|
|
@@ -10354,7 +10364,7 @@ var LongLeashServer = class {
|
|
|
10354
10364
|
roots: this.sessions?.listAllowedRoots() ?? [],
|
|
10355
10365
|
// Everything the daemon knows about, so a reloaded phone rebuilds instead of showing
|
|
10356
10366
|
// nothing — including sessions the human started in a terminal.
|
|
10357
|
-
sessions: [...this.sessions?.listSessions() ?? [], ...this.external?.listSessions() ?? []],
|
|
10367
|
+
sessions: [...this.sessions?.listSessions() ?? [], ...this.external?.listSessions() ?? []].sort((left, right) => left.startedAt - right.startedAt || left.sessionId.localeCompare(right.sessionId)),
|
|
10358
10368
|
capabilities: {
|
|
10359
10369
|
startSession: this.sessions !== null,
|
|
10360
10370
|
stopSession: this.sessions !== null,
|
|
@@ -10432,6 +10442,15 @@ var LongLeashServer = class {
|
|
|
10432
10442
|
return;
|
|
10433
10443
|
}
|
|
10434
10444
|
for (const event2 of replay.events) this.sendTo(connection, event2);
|
|
10445
|
+
if (message.syncId !== void 0) {
|
|
10446
|
+
this.sendTo(connection, {
|
|
10447
|
+
v: PROTOCOL_VERSION,
|
|
10448
|
+
type: "sync.complete",
|
|
10449
|
+
sessionId: message.sessionId,
|
|
10450
|
+
syncId: message.syncId,
|
|
10451
|
+
cursor: this.eventLog.latestSeq(message.sessionId)
|
|
10452
|
+
});
|
|
10453
|
+
}
|
|
10435
10454
|
return;
|
|
10436
10455
|
}
|
|
10437
10456
|
if (message.type === "pushSubscribe") {
|