@leftoff/cli 0.7.0 → 0.7.1

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/leftoff.js CHANGED
@@ -1,45 +1,45 @@
1
1
  #!/usr/bin/env node
2
- var CS=Object.defineProperty;var J=(n,e)=>()=>(n&&(e=n(n=0)),e);var Qt=(n,e)=>{for(var t in e)CS(n,t,{get:e[t],enumerable:!0})};function co(n,e){let t=Array.isArray(n)?n.slice():{...n};if(!e||typeof e!="object")return t;for(let s of Object.keys(e)){let o=n?n[s]:void 0,r=e[s];o&&r&&typeof o=="object"&&typeof r=="object"&&!Array.isArray(o)&&!Array.isArray(r)?t[s]=co(o,r):t[s]=r}return t}function br(n){return String(n||"").replace(/[^A-Za-z0-9]/g,"").slice(0,8).toLowerCase()||"unknown0"}function du(n,e=40){return String(n||"").toLowerCase().normalize("NFKD").replace(/[\u0300-\u036f]/g,"").replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,e).replace(/-+$/g,"")||"untitled"}function ia(n){let e=String(n||"").split(/[\\/]+/).filter(Boolean);return e.length?e[e.length-1]:""}function me(n){let e=n&&n.repo;return e&&e.name?String(e.name).split("/").pop():e&&e.root?ia(e.root):ia(n&&n.cwd)||"no-repo"}function F(n,e){let t=String(n??"").replace(/\s+/g," ").trim();return t.length>e?t.slice(0,Math.max(0,e-1)).trimEnd()+"…":t}function G(n){let e=n instanceof Date?n:new Date(typeof n=="number"?n:Date.parse(n));return isNaN(e.getTime())?null:e.toISOString().replace(/\.\d{3}Z$/,"Z")}function R(n){return n==null||n===""?NaN:typeof n=="number"?n:Date.parse(n)}function NS(n){return G(n).replace(/[-:]/g,"")}function MS(n){let e=G(n);return e.slice(0,10)+"T"+e.slice(11,19).replace(/:/g,"")+"Z"}function ca(n,e,t=0){return"cp_"+NS(e)+"_"+br(n)+(t?"-"+(t+1):"")}function gu(n,e=0){return MS(n)+(e?"-"+(e+1):"")+".md"}function Ph(n){return[(G(n.startedAt||Date.now())||"").slice(0,10),du(me(n),24),du(n.title,40),br(n.id)].join("_")}function gr(n){return du(String(n||"").replace(/\.local$/i,""),40)||"unknown"}function ua({env:n,hostname:e,cfg:t={}}={}){if(n)return gr(n);let s=t.machineNames||{};return e&&s[e]?gr(s[e]):t.machine?gr(t.machine):gr(e||"unknown")}function fu(n){return n.replace(/[A-Z]/g,e=>"_"+e.toLowerCase())}function jh(n){return n.replace(/_([a-z0-9])/g,(e,t)=>t.toUpperCase())}function aa(n,e,t){if(Array.isArray(n))return n.map(o=>t?aa(o,e,t):o);if(!n||typeof n!="object")return n;let s={};for(let[o,r]of Object.entries(n))s[e(o)]=t&&r&&typeof r=="object"?aa(r,e,t):r;return s}function DS(n){return!!(n===""||LS.test(n)||Hh.test(n)&&/\d/.test(n)||/^[\s\-?:,\[\]{}#&*!|>'"%@`]/.test(n)||/\s$/.test(n)||/:(\s|$)|\s#|[,\[\]{}\n\r\t"]/.test(n))}function hu(n){if(n==null)return"null";if(typeof n=="boolean"||typeof n=="number")return Number.isFinite(n)||typeof n=="boolean"?String(n):"null";if(Array.isArray(n))return"["+n.map(hu).join(", ")+"]";if(typeof n=="object")return"{"+Object.entries(n).filter(([,s])=>s!==void 0).map(([s,o])=>s+": "+hu(o)).join(", ")+"}";let e=String(n);return DS(e)?JSON.stringify(e):e}function As(n,e=[]){let t=[...e.filter(o=>o in n),...Object.keys(n).filter(o=>!e.includes(o))],s=["---"];for(let o of t)n[o]!==void 0&&s.push(o+": "+hu(n[o]));return s.push("---"),s.join(`
2
+ var RS=Object.defineProperty;var J=(n,e)=>()=>(n&&(e=n(n=0)),e);var tn=(n,e)=>{for(var t in e)RS(n,t,{get:e[t],enumerable:!0})};function fo(n,e){let t=Array.isArray(n)?n.slice():{...n};if(!e||typeof e!="object")return t;for(let s of Object.keys(e)){let o=n?n[s]:void 0,r=e[s];o&&r&&typeof o=="object"&&typeof r=="object"&&!Array.isArray(o)&&!Array.isArray(r)?t[s]=fo(o,r):t[s]=r}return t}function br(n){return String(n||"").replace(/[^A-Za-z0-9]/g,"").slice(0,8).toLowerCase()||"unknown0"}function hu(n,e=40){return String(n||"").toLowerCase().normalize("NFKD").replace(/[\u0300-\u036f]/g,"").replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,e).replace(/-+$/g,"")||"untitled"}function ca(n){let e=String(n||"").split(/[\\/]+/).filter(Boolean);return e.length?e[e.length-1]:""}function ge(n){let e=n&&n.repo;return e&&e.name?String(e.name).split("/").pop():e&&e.root?ca(e.root):ca(n&&n.cwd)||"no-repo"}function F(n,e){let t=String(n??"").replace(/\s+/g," ").trim();return t.length>e?t.slice(0,Math.max(0,e-1)).trimEnd()+"…":t}function G(n){let e=n instanceof Date?n:new Date(typeof n=="number"?n:Date.parse(n));return isNaN(e.getTime())?null:e.toISOString().replace(/\.\d{3}Z$/,"Z")}function R(n){return n==null||n===""?NaN:typeof n=="number"?n:Date.parse(n)}function jS(n){return G(n).replace(/[-:]/g,"")}function FS(n){let e=G(n);return e.slice(0,10)+"T"+e.slice(11,19).replace(/:/g,"")+"Z"}function fa(n,e,t=0){return"cp_"+jS(e)+"_"+br(n)+(t?"-"+(t+1):"")}function wu(n,e=0){return FS(n)+(e?"-"+(e+1):"")+".md"}function Hh(n){return[(G(n.startedAt||Date.now())||"").slice(0,10),hu(ge(n),24),hu(n.title,40),br(n.id)].join("_")}function gr(n){return hu(String(n||"").replace(/\.local$/i,""),40)||"unknown"}function ha({env:n,hostname:e,cfg:t={}}={}){if(n)return gr(n);let s=t.machineNames||{};return e&&s[e]?gr(s[e]):t.machine?gr(t.machine):gr(e||"unknown")}function pu(n){return n.replace(/[A-Z]/g,e=>"_"+e.toLowerCase())}function Bh(n){return n.replace(/_([a-z0-9])/g,(e,t)=>t.toUpperCase())}function ua(n,e,t){if(Array.isArray(n))return n.map(o=>t?ua(o,e,t):o);if(!n||typeof n!="object")return n;let s={};for(let[o,r]of Object.entries(n))s[e(o)]=t&&r&&typeof r=="object"?ua(r,e,t):r;return s}function HS(n){return!!(n===""||US.test(n)||$h.test(n)&&/\d/.test(n)||/^[\s\-?:,\[\]{}#&*!|>'"%@`]/.test(n)||/\s$/.test(n)||/:(\s|$)|\s#|[,\[\]{}\n\r\t"]/.test(n))}function mu(n){if(n==null)return"null";if(typeof n=="boolean"||typeof n=="number")return Number.isFinite(n)||typeof n=="boolean"?String(n):"null";if(Array.isArray(n))return"["+n.map(mu).join(", ")+"]";if(typeof n=="object")return"{"+Object.entries(n).filter(([,s])=>s!==void 0).map(([s,o])=>s+": "+mu(o)).join(", ")+"}";let e=String(n);return HS(e)?JSON.stringify(e):e}function Es(n,e=[]){let t=[...e.filter(o=>o in n),...Object.keys(n).filter(o=>!e.includes(o))],s=["---"];for(let o of t)n[o]!==void 0&&s.push(o+": "+mu(n[o]));return s.push("---"),s.join(`
3
3
  `)+`
4
- `}function Ih(n){let e=[],t=0,s=null,o="";for(let r=0;r<n.length;r++){let i=n[r];if(s){if(o+=i,i==="\\"&&s==='"'){o+=n[++r]||"";continue}if(i===s){if(s==="'"&&n[r+1]==="'"){o+=n[++r];continue}s=null}continue}if(i==='"'||i==="'"){s=i,o+=i;continue}if((i==="["||i==="{")&&t++,(i==="]"||i==="}")&&t--,i===","&&t===0){e.push(o.trim()),o="";continue}o+=i}return o.trim()!==""&&e.push(o.trim()),e}function RS(n){let e=null;for(let t=0;t<n.length;t++){let s=n[t];if(e){s===e&&(e=null);continue}if(s==='"'||s==="'"){e=s;continue}if(s==="#"&&(t===0||/\s/.test(n[t-1])))return n.slice(0,t).trimEnd()}return n}function yr(n){let e=RS(String(n).trim());if(e===""||e==="~"||e==="null"||e==="Null"||e==="NULL")return null;if(/^(true|True|TRUE)$/.test(e))return!0;if(/^(false|False|FALSE)$/.test(e))return!1;if(e[0]==='"')try{return JSON.parse(e)}catch{return e.slice(1,-1)}if(e[0]==="'")return e.slice(1,e.endsWith("'")?-1:void 0).replace(/''/g,"'");if(e[0]==="[")return Ih(e.slice(1,e.lastIndexOf("]"))).map(yr);if(e[0]==="{"){let t={};for(let s of Ih(e.slice(1,e.lastIndexOf("}")))){let o=s.match(/^("(?:[^"\\]|\\.)*"|'[^']*'|[^:]+?)\s*:\s*(.*)$/);o&&(t[String(yr(o[1]))]=yr(o[2]))}return t}if(Hh.test(e)&&/\d/.test(e)){let t=Number(e.replace(/_/g,""));if(!isNaN(t))return t}return e}function Ye(n){let e=String(n||"").replace(/^\uFEFF/,"").replace(/\r\n/g,`
4
+ `}function Dh(n){let e=[],t=0,s=null,o="";for(let r=0;r<n.length;r++){let i=n[r];if(s){if(o+=i,i==="\\"&&s==='"'){o+=n[++r]||"";continue}if(i===s){if(s==="'"&&n[r+1]==="'"){o+=n[++r];continue}s=null}continue}if(i==='"'||i==="'"){s=i,o+=i;continue}if((i==="["||i==="{")&&t++,(i==="]"||i==="}")&&t--,i===","&&t===0){e.push(o.trim()),o="";continue}o+=i}return o.trim()!==""&&e.push(o.trim()),e}function BS(n){let e=null;for(let t=0;t<n.length;t++){let s=n[t];if(e){s===e&&(e=null);continue}if(s==='"'||s==="'"){e=s;continue}if(s==="#"&&(t===0||/\s/.test(n[t-1])))return n.slice(0,t).trimEnd()}return n}function yr(n){let e=BS(String(n).trim());if(e===""||e==="~"||e==="null"||e==="Null"||e==="NULL")return null;if(/^(true|True|TRUE)$/.test(e))return!0;if(/^(false|False|FALSE)$/.test(e))return!1;if(e[0]==='"')try{return JSON.parse(e)}catch{return e.slice(1,-1)}if(e[0]==="'")return e.slice(1,e.endsWith("'")?-1:void 0).replace(/''/g,"'");if(e[0]==="[")return Dh(e.slice(1,e.lastIndexOf("]"))).map(yr);if(e[0]==="{"){let t={};for(let s of Dh(e.slice(1,e.lastIndexOf("}")))){let o=s.match(/^("(?:[^"\\]|\\.)*"|'[^']*'|[^:]+?)\s*:\s*(.*)$/);o&&(t[String(yr(o[1]))]=yr(o[2]))}return t}if($h.test(e)&&/\d/.test(e)){let t=Number(e.replace(/_/g,""));if(!isNaN(t))return t}return e}function Ye(n){let e=String(n||"").replace(/^\uFEFF/,"").replace(/\r\n/g,`
5
5
  `);if(!e.startsWith(`---
6
6
  `)&&e!=="---")return{data:{},body:e};let t=e.indexOf(`
7
7
  ---`,4);if(t<0)return{data:{},body:e};let s=e.slice(4,t),o=e.slice(t+4);o=o.replace(/^[^\n]*\n?/,"");let r={},i=s.split(`
8
- `);for(let a=0;a<i.length;a++){let l=i[a];if(!l.trim()||/^\s*#/.test(l))continue;let c=l.match(/^([A-Za-z0-9_.-]+)\s*:\s?(.*)$/);if(!c)continue;let[,u,d]=c;if(d.trim()===""){let f=[];for(;a+1<i.length&&/^\s+-\s+/.test(i[a+1]);)f.push(yr(i[++a].replace(/^\s+-\s+/,"")));r[u]=f.length?f:null}else r[u]=yr(d)}return{data:r,body:o}}function uo(n){let e=n.id||n.sessionId,t=n.cwd;return(t?"cd "+ao(t)+" && ":"")+yu(n,["--resume",e])}function ao(n){let e=String(n);return/^[A-Za-z0-9_./~@%+=:,-]+$/.test(e)?e:"'"+e.replace(/'/g,"'\\''")+"'"}function lo(n){let e=String(n||"").trim().toLowerCase();return!e||e==="<synthetic>"?!0:/claude/.test(e)||/^(opus|sonnet|haiku|opusplan|default|best)(\[1m\])?$/.test(e)}function da(n){try{let e=new URL(String(n).trim());return/^https?:$/.test(e.protocol)?e:null}catch{return null}}function FS(n){return/(^|\.)anthropic\.com$/i.test(String(n||""))}function US(n={},e=null){if(e&&!lo(e))return String(e);let t=String(e||"").toLowerCase(),s=/haiku/.test(t)?n.ANTHROPIC_DEFAULT_HAIKU_MODEL:/sonnet/.test(t)?n.ANTHROPIC_DEFAULT_SONNET_MODEL:n.ANTHROPIC_DEFAULT_OPUS_MODEL;for(let o of[s,n.ANTHROPIC_MODEL,n.ANTHROPIC_DEFAULT_OPUS_MODEL,n.ANTHROPIC_DEFAULT_SONNET_MODEL,n.ANTHROPIC_DEFAULT_HAIKU_MODEL,n.CLAUDE_CODE_SUBAGENT_MODEL])if(o&&String(o).trim()&&!lo(o))return String(o).trim();return null}function Gh(n={},{model:e=null}={}){let t=n.ANTHROPIC_BASE_URL?da(n.ANTHROPIC_BASE_URL):null;if(!t||FS(t.hostname))return null;let s=t.hostname.toLowerCase(),o=US(n,e);return t.port===Bh||/(^|\.)ollama\.com$/.test(s)||n.ANTHROPIC_AUTH_TOKEN==="ollama"?kr({kind:"ollama",model:o,baseUrl:t.origin}):kr({kind:"custom",host:t.host,model:o})}function Kh(n){if(!n||typeof n!="object")return null;let e=typeof n.model=="string"&&n.model.trim()?F(n.model.trim(),120):null;if(n.kind==="ollama"){let t=n.baseUrl?da(n.baseUrl):null;return kr({kind:"ollama",model:e,baseUrl:t?t.origin:null,inferred:n.inferred?!0:null})}if(n.kind==="custom"){let t=typeof n.host=="string"&&n.host.trim()?F(n.host.trim().replace(/^[a-z]+:\/\//i,"").replace(/[/?#@].*$/,""),120):null;return kr({kind:"custom",host:t,model:e,inferred:n.inferred?!0:null})}return null}function $h(n){if(!n||lo(n))return null;let e=String(n).trim();return/^[\w.\-/]+:[\w.\-]+$/.test(e)?{kind:"ollama",model:e,inferred:!0}:{kind:"custom",model:e,inferred:!0}}function Yh(n){let e=n&&n.backend;if(!e)return n&&n.model||null;let t=e.model||(n.model&&!lo(n.model)?n.model:null);if(e.kind==="ollama")return t?t+" · Ollama":"Ollama";let s=e.host||"custom server";return t?t+" · "+s:s}function zh(n){return!!(n&&n.kind==="ollama"&&!Wh(n.baseUrl))}function Wh(n){let e=n?da(n):null;return!!(e&&/(^|\.)ollama\.com$/i.test(e.hostname))}function yu(n,e=[]){let t=n&&n.backend,s=e.map(ao).join(" ");if(t&&t.kind==="ollama"){if(Wh(t.baseUrl))return"ANTHROPIC_BASE_URL="+ao(t.baseUrl)+" ANTHROPIC_API_KEY= claude"+(t.model?" --model "+ao(t.model):"")+(s?" "+s:"");let o=t.baseUrl?da(t.baseUrl):null;return(o&&!(jS.has(o.hostname)&&o.port===Bh&&o.protocol==="http:")?"OLLAMA_HOST="+ao(o.origin)+" ":"")+"ollama launch claude"+(t.model?" --model "+ao(t.model):"")+" --"+(s?" "+s:"")}return"claude"+(s?" "+s:"")}function Jh(n){let e={id:n.id,session_id:n.sessionId,session_title:n.sessionTitle||null,created_at:G(n.createdAt),trigger:n.trigger,machine:n.machine||void 0,repo:n.repo||null,branch:n.branch||null,cwd:n.cwd||null,files_touched:n.filesTouched||[],turns_since_last:n.turnsSinceLast==null?null:n.turnsSinceLast,user_note:n.userNote||void 0,open_question:n.openQuestion||void 0,parse_ok:n.parseOk===!1?!1:void 0,length:n.length&&Tr[n.length]?n.length:void 0,generator:n.generator?Fh(n.generator):{by:"unknown"},schema:1},t=["**Doing:** "+mn(n.doing),"**Stopped at:** "+mn(n.stoppedAt),"**Next:** "+mn(n.next)];n.openQuestion&&t.push("**Open question:** "+mn(n.openQuestion)),n.userNote&&t.push("**Note:** "+mn(n.userNote));let s=r=>"- "+mn(r).replace(/^\*+/,"");for(let r of ek){let i=n[r];if(r==="recap"){i&&t.push("","### "+Lh[r],mn(i).replace(/^\*+/,""));continue}if(!(!Array.isArray(i)||!i.length))if(t.push("","### "+Lh[r]),r==="files")for(let a of i)t.push(s(a.path?"`"+String(a.path).replace(/`/g,"")+"`"+(a.why?" — "+mn(a.why):""):a.why));else if(r==="nextSteps")i.forEach((a,l)=>t.push(l+1+". "+mn(a).replace(/^\*+/,"")));else for(let a of i)t.push(s(a))}let o=uo({id:n.sessionId,cwd:n.cwd,backend:n.backend});return As(e,PS)+t.join(`
8
+ `);for(let a=0;a<i.length;a++){let l=i[a];if(!l.trim()||/^\s*#/.test(l))continue;let c=l.match(/^([A-Za-z0-9_.-]+)\s*:\s?(.*)$/);if(!c)continue;let[,u,d]=c;if(d.trim()===""){let f=[];for(;a+1<i.length&&/^\s+-\s+/.test(i[a+1]);)f.push(yr(i[++a].replace(/^\s+-\s+/,"")));r[u]=f.length?f:null}else r[u]=yr(d)}return{data:r,body:o}}function ho(n){let e=n.id||n.sessionId,t=vr(n);return(t?"cd "+co(t)+" && ":"")+Su(n,["--resume",e])}function vr(n){let e=n&&n.cwd,t=n&&n.repo&&n.repo.worktree;return e&&t&&typeof t=="string"&&(e===t||String(e).startsWith(t.replace(/\/+$/,"")+"/"))?t:e||null}function co(n){let e=String(n);return/^[A-Za-z0-9_./~@%+=:,-]+$/.test(e)?e:"'"+e.replace(/'/g,"'\\''")+"'"}function uo(n){let e=String(n||"").trim().toLowerCase();return!e||e==="<synthetic>"?!0:/claude/.test(e)||/^(opus|sonnet|haiku|opusplan|default|best)(\[1m\])?$/.test(e)}function pa(n){try{let e=new URL(String(n).trim());return/^https?:$/.test(e.protocol)?e:null}catch{return null}}function $S(n){return/(^|\.)anthropic\.com$/i.test(String(n||""))}function zS(n={},e=null){if(e&&!uo(e))return String(e);let t=String(e||"").toLowerCase(),s=/haiku/.test(t)?n.ANTHROPIC_DEFAULT_HAIKU_MODEL:/sonnet/.test(t)?n.ANTHROPIC_DEFAULT_SONNET_MODEL:n.ANTHROPIC_DEFAULT_OPUS_MODEL;for(let o of[s,n.ANTHROPIC_MODEL,n.ANTHROPIC_DEFAULT_OPUS_MODEL,n.ANTHROPIC_DEFAULT_SONNET_MODEL,n.ANTHROPIC_DEFAULT_HAIKU_MODEL,n.CLAUDE_CODE_SUBAGENT_MODEL])if(o&&String(o).trim()&&!uo(o))return String(o).trim();return null}function Yh(n={},{model:e=null}={}){let t=n.ANTHROPIC_BASE_URL?pa(n.ANTHROPIC_BASE_URL):null;if(!t||$S(t.hostname))return null;let s=t.hostname.toLowerCase(),o=zS(n,e);return t.port===zh||/(^|\.)ollama\.com$/.test(s)||n.ANTHROPIC_AUTH_TOKEN==="ollama"?kr({kind:"ollama",model:o,baseUrl:t.origin}):kr({kind:"custom",host:t.host,model:o})}function Wh(n){if(!n||typeof n!="object")return null;let e=typeof n.model=="string"&&n.model.trim()?F(n.model.trim(),120):null;if(n.kind==="ollama"){let t=n.baseUrl?pa(n.baseUrl):null;return kr({kind:"ollama",model:e,baseUrl:t?t.origin:null,inferred:n.inferred?!0:null})}if(n.kind==="custom"){let t=typeof n.host=="string"&&n.host.trim()?F(n.host.trim().replace(/^[a-z]+:\/\//i,"").replace(/[/?#@].*$/,""),120):null;return kr({kind:"custom",host:t,model:e,inferred:n.inferred?!0:null})}return null}function Jh(n){if(!n||uo(n))return null;let e=String(n).trim();return/^[\w.\-/]+:[\w.\-]+$/.test(e)?{kind:"ollama",model:e,inferred:!0}:{kind:"custom",model:e,inferred:!0}}function qh(n){let e=n&&n.backend;if(!e)return n&&n.model||null;let t=e.model||(n.model&&!uo(n.model)?n.model:null);if(e.kind==="ollama")return t?t+" · Ollama":"Ollama";let s=e.host||"custom server";return t?t+" · "+s:s}function Vh(n){return!!(n&&n.kind==="ollama"&&!Xh(n.baseUrl))}function Xh(n){let e=n?pa(n):null;return!!(e&&/(^|\.)ollama\.com$/i.test(e.hostname))}function Su(n,e=[]){let t=n&&n.backend,s=e.map(co).join(" ");if(t&&t.kind==="ollama"){if(Xh(t.baseUrl))return"ANTHROPIC_BASE_URL="+co(t.baseUrl)+" ANTHROPIC_API_KEY= claude"+(t.model?" --model "+co(t.model):"")+(s?" "+s:"");let o=t.baseUrl?pa(t.baseUrl):null;return(o&&!(KS.has(o.hostname)&&o.port===zh&&o.protocol==="http:")?"OLLAMA_HOST="+co(o.origin)+" ":"")+"ollama launch claude"+(t.model?" --model "+co(t.model):"")+" --"+(s?" "+s:"")}return"claude"+(s?" "+s:"")}function Zh(n){let e={id:n.id,session_id:n.sessionId,session_title:n.sessionTitle||null,created_at:G(n.createdAt),trigger:n.trigger,machine:n.machine||void 0,repo:n.repo||null,branch:n.branch||null,cwd:n.cwd||null,files_touched:n.filesTouched||[],turns_since_last:n.turnsSinceLast==null?null:n.turnsSinceLast,user_note:n.userNote||void 0,open_question:n.openQuestion||void 0,parse_ok:n.parseOk===!1?!1:void 0,length:n.length&&_r[n.length]?n.length:void 0,generator:n.generator?Gh(n.generator):{by:"unknown"},schema:1},t=["**Doing:** "+yn(n.doing),"**Stopped at:** "+yn(n.stoppedAt),"**Next:** "+yn(n.next)];n.openQuestion&&t.push("**Open question:** "+yn(n.openQuestion)),n.userNote&&t.push("**Note:** "+yn(n.userNote));let s=r=>"- "+yn(r).replace(/^\*+/,"");for(let r of ik){let i=n[r];if(r==="recap"){i&&t.push("","### "+jh[r],yn(i).replace(/^\*+/,""));continue}if(!(!Array.isArray(i)||!i.length))if(t.push("","### "+jh[r]),r==="files")for(let a of i)t.push(s(a.path?"`"+String(a.path).replace(/`/g,"")+"`"+(a.why?" — "+yn(a.why):""):a.why));else if(r==="nextSteps")i.forEach((a,l)=>t.push(l+1+". "+yn(a).replace(/^\*+/,"")));else for(let a of i)t.push(s(a))}let o=ho({id:n.sessionId,cwd:n.cwd,backend:n.backend});return Es(e,GS)+t.join(`
9
9
  `)+`
10
10
 
11
- Resume: \``+o+"`\n"}function mn(n){return String(n??"").replace(/\s*\n\s*/g," ").trim()}function qh(n){let{data:e,body:t}=Ye(n),s={id:e.id||null,sessionId:e.session_id||null,sessionTitle:e.session_title||null,createdAt:e.created_at?G(e.created_at):null,trigger:e.trigger||"manual",machine:e.machine==null?null:String(e.machine),repo:e.repo==null?null:String(e.repo),branch:e.branch==null?null:String(e.branch),cwd:e.cwd||null,filesTouched:Array.isArray(e.files_touched)?e.files_touched.map(String):[],turnsSinceLast:typeof e.turns_since_last=="number"?e.turns_since_last:null,userNote:e.user_note||null,openQuestion:e.open_question||null,parseOk:e.parse_ok!==!1,generator:e.generator&&typeof e.generator=="object"?Uh(e.generator):{by:"unknown"},length:e.length&&Tr[e.length]?e.length:null,doing:"",stoppedAt:"",next:""},o=t.split(`
12
- `),r=0;for(;r<o.length;r++){let a=o[r];if(/^###\s/.test(a))break;let l=a.match(/^\s*\*\*([A-Za-z ]+):\*\*\s*(.*)$/);if(!l)continue;let c=HS[l[1].trim().toLowerCase()];c&&(s[c]=l[2].trim())}let i=[];for(;r<o.length&&!/^Resume: `/.test(o[r]);r++)i.push(o[r]);return Object.assign(s,sp(i)),s}function Vh(n,e=[]){let t={};for(let a of KS)t[fu(a)]=n[a]===void 0?null:n[a];t.repo&&typeof t.repo=="object"&&(t.repo=Fh(t.repo));for(let a of GS)a==="folder"||a==="statusChanges"||n[a]!==void 0&&n[a]!==null&&n[a]!==""&&n[a]!==0&&n[a]!==!1&&(t[fu(a)]=n[a]);t.schema=1;let s=e.slice().sort((a,l)=>R(l.createdAt)-R(a.createdAt)),o=["# "+(n.title||"Untitled session"),""],r=me(n),i=n.repo&&n.repo.branch;o.push("`"+r+(i?" · "+i:"")+"` · "+(n.machine?n.machine+" · ":"")+(n.status||"unknown")+" · started "+(G(n.startedAt)||"?")),o.push(""),o.push("Resume: `"+uo(n)+"`"),o.push(""),o.push("## Checkpoints"),o.push(""),s.length||o.push("_None yet._");for(let a of s){let l=G(a.createdAt).replace("T"," ").slice(0,16)+"Z";o.push("- ["+l+" · "+a.trigger+"](checkpoints/"+(a.file||gu(a.createdAt))+") — Next: "+F(a.next,160))}return As(t,BS)+o.join(`
11
+ Resume: \``+o+"`\n"}function yn(n){return String(n??"").replace(/\s*\n\s*/g," ").trim()}function Qh(n){let{data:e,body:t}=Ye(n),s={id:e.id||null,sessionId:e.session_id||null,sessionTitle:e.session_title||null,createdAt:e.created_at?G(e.created_at):null,trigger:e.trigger||"manual",machine:e.machine==null?null:String(e.machine),repo:e.repo==null?null:String(e.repo),branch:e.branch==null?null:String(e.branch),cwd:e.cwd||null,filesTouched:Array.isArray(e.files_touched)?e.files_touched.map(String):[],turnsSinceLast:typeof e.turns_since_last=="number"?e.turns_since_last:null,userNote:e.user_note||null,openQuestion:e.open_question||null,parseOk:e.parse_ok!==!1,generator:e.generator&&typeof e.generator=="object"?Kh(e.generator):{by:"unknown"},length:e.length&&_r[e.length]?e.length:null,doing:"",stoppedAt:"",next:""},o=t.split(`
12
+ `),r=0;for(;r<o.length;r++){let a=o[r];if(/^###\s/.test(a))break;let l=a.match(/^\s*\*\*([A-Za-z ]+):\*\*\s*(.*)$/);if(!l)continue;let c=YS[l[1].trim().toLowerCase()];c&&(s[c]=l[2].trim())}let i=[];for(;r<o.length&&!/^Resume: `/.test(o[r]);r++)i.push(o[r]);return Object.assign(s,ap(i)),s}function ep(n,e=[]){let t={};for(let a of qS)t[pu(a)]=n[a]===void 0?null:n[a];t.repo&&typeof t.repo=="object"&&(t.repo=Gh(t.repo));for(let a of JS)a==="folder"||a==="statusChanges"||n[a]!==void 0&&n[a]!==null&&n[a]!==""&&n[a]!==0&&n[a]!==!1&&(t[pu(a)]=n[a]);t.schema=1;let s=e.slice().sort((a,l)=>R(l.createdAt)-R(a.createdAt)),o=["# "+(n.title||"Untitled session"),""],r=ge(n),i=n.repo&&n.repo.branch;o.push("`"+r+(i?" · "+i:"")+"` · "+(n.machine?n.machine+" · ":"")+(n.status||"unknown")+" · started "+(G(n.startedAt)||"?")),o.push(""),o.push("Resume: `"+ho(n)+"`"),o.push(""),o.push("## Checkpoints"),o.push(""),s.length||o.push("_None yet._");for(let a of s){let l=G(a.createdAt).replace("T"," ").slice(0,16)+"Z";o.push("- ["+l+" · "+a.trigger+"](checkpoints/"+(a.file||wu(a.createdAt))+") — Next: "+F(a.next,160))}return Es(t,WS)+o.join(`
13
13
  `)+`
14
- `}function Xh(n){let{data:e}=Ye(n),t={};for(let[s,o]of Object.entries(e))s!=="schema"&&(t[jh(s)]=o);t.repo&&typeof t.repo=="object"&&(t.repo=Uh(t.repo)),t.repo&&typeof t.repo=="string"&&(t.repo={root:null,name:t.repo});for(let s of["startedAt","lastActivityAt","lastHeartbeatAt","endedAt","lastCheckpointAt","lastEventAt","dormantStretch","closedAt","pidStartedAt"])t[s]&&(t[s]=G(t[s]));if(t.turns=typeof t.turns=="number"?t.turns:0,t.turnsSinceCheckpoint=typeof t.turnsSinceCheckpoint=="number"?t.turnsSinceCheckpoint:0,t.backgroundTasks=typeof t.backgroundTasks=="number"?t.backgroundTasks:0,t.pinned=!!t.pinned,t.archived=!!t.archived,t.backend!==void 0){let s=Kh(t.backend);s?t.backend=s:delete t.backend}return t}function ze(n){return br(n).slice(0,6)}function vr(n){return"cp:"+ze(n)}function xr(n){let e=String(n??""),t=e.match($S);return t?{name:e.slice(0,t.index).trim(),id:t[1].toLowerCase()}:{name:e.trim(),id:null}}function vs(n){return xr(n).name}function YS(n,e){if(!n||!e)return!1;if(xr(n).id)return!0;let t=e.title||null;return!!t&&String(n).trim()===Su("{name}",{name:t,id:e.id})}function zS(n){return typeof n!="string"||!n.trim()||n===wu||!/\{name\}|\{repo\}|\{id\}/.test(n)?ke.tabTitleFormat:n}function Su(n,{name:e,id:t,repo:s,marker:o=!1}={}){let r=zS(n),i=F(vs(e||"")||s||"claude",60).replace(/[\x00-\x1f\x7f]/g," "),a=r.replace(/\{name\}/g,i).replace(/\{repo\}/g,String(s||"").replace(/[\x00-\x1f\x7f]/g," ")).replace(/\{id\}/g,ze(t)).replace(/\s+/g," ").trim();return o&&t&&!r.includes("{id}")&&(a+=" · "+vr(t)),a}function WS(n){let e=String(n.TERM_PROGRAM||""),t=String(n.TERM||""),s=e.toLowerCase();return s==="ghostty"?"ghostty":s==="iterm.app"?"iterm2":s==="wezterm"?"wezterm":s==="apple_terminal"?"apple-terminal":s==="vscode"?"vscode":s==="kitty"||n.KITTY_WINDOW_ID||t==="xterm-kitty"?"kitty":n.WEZTERM_PANE?"wezterm":n.ITERM_SESSION_ID?"iterm2":n.GHOSTTY_RESOURCES_DIR||t==="xterm-ghostty"?"ghostty":"unknown"}function Bn(n={}){let e=n||{},t={kind:"unknown",label:"",program:e.TERM_PROGRAM||null,version:e.TERM_PROGRAM_VERSION||null},s=WS(e),o={};if(e.ITERM_SESSION_ID){let r=String(e.ITERM_SESSION_ID);o.itermSessionId=r.includes(":")?r.slice(r.indexOf(":")+1):r}return e.WEZTERM_PANE&&(o.weztermPane=String(e.WEZTERM_PANE)),e.WEZTERM_UNIX_SOCKET&&(o.weztermSocket=String(e.WEZTERM_UNIX_SOCKET)),e.KITTY_WINDOW_ID&&(o.kittyWindowId=String(e.KITTY_WINDOW_ID)),e.KITTY_LISTEN_ON&&(o.kittyListenOn=String(e.KITTY_LISTEN_ON)),e.TERM_SESSION_ID&&(o.termSessionId=String(e.TERM_SESSION_ID)),e.TMUX&&e.TMUX_PANE?(t.kind="tmux",t.tmuxPane=String(e.TMUX_PANE),t.tmuxSocket=String(e.TMUX).split(",")[0]||null,t.host=s==="unknown"?null:s,t.label="tmux"+(t.host?" in "+Nh[t.host]:""),{...t,...o}):(t.kind=s,t.label=s==="unknown"?e.TERM_PROGRAM||e.TERM||"unknown terminal":Nh[s],s==="ghostty"&&e.GHOSTTY_RESOURCES_DIR&&(t.resourcesDir=String(e.GHOSTTY_RESOURCES_DIR)),s==="vscode"&&/cursor/i.test(String(e.TERM_PROGRAM_VERSION||""))&&(t.label="Cursor"),{...t,...o})}function Ar(n){let e=String(n||"").trim().match(/^(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+)$/);return e?Number(e[1]||0)*86400+Number(e[2]||0)*3600+Number(e[3])*60+Number(e[4]):null}function ku(n){let e=String(n||"").trim();return!e||/^\?+$/.test(e)||e==="-"?null:e.startsWith("/dev/")?e:"/dev/"+(/^s\d+$/.test(e)?"tty"+e:e)}function Qh(n,e=Date.now()){let t=String(n||"").trim().split(/\s+/),s=Number(t[0]);if(!Number.isInteger(s)||s<=1)return null;let o=Ar(t[2]);return{pid:s,tty:ku(t[1]),pidStartedAt:o==null?null:G(e-o*1e3)}}function bt(n,e,t=ke){if(n.endedAt)return"ended";let s=R(n.lastActivityAt||n.startedAt),o=isNaN(s)?0:e-s,r=t.dormantAfterMin*oa;if(n.capture==="mod"){let i=R(n.lastHeartbeatAt||n.lastEventAt||n.startedAt),a=R(n.pidAliveAt);if(!(!isNaN(a)&&e-a<=Math.max(t.staleAfterSec*1e3,9e4))&&!isNaN(i)&&e-i>t.staleAfterSec*1e3)return"stale"}return n.needsInput?"needs_input":n.inTurn?n.capture==="hooks"&&o>=2*r?"dormant":"active":n.backgroundTasks>0?"background":o>=r?"dormant":"waiting"}function bu(n,e,t=ke){if(n.ignored)return null;let s=bt(n,e,t),o=R(n.lastCheckpointAt),r=R(n.lastActivityAt||n.startedAt),i=isNaN(o)||e-o>=t.minGapMin*oa,a=(n.turnsSinceCheckpoint||0)>0||!isNaN(r)&&(isNaN(o)?!!n.lastActivityAt:r>o);if(s==="ended"||s==="stale")return n.finalCheckpointDone||!a||!n.lastActivityAt?null:n.endReason==="clear"?"clear":"session_end";if(e-r<t.dormantAfterMin*oa){if(t.longRunCheckpointMin&&s==="active"&&n.activeSince){let c=Math.max(R(n.activeSince),isNaN(o)?0:o);if(e-c>=t.longRunCheckpointMin*oa&&i)return"long_run"}return null}return n.dormantStretch&&n.dormantStretch===n.lastActivityAt||!i?null:s==="dormant"&&(n.turnsSinceCheckpoint||0)>=1||s==="needs_input"&&a?"dormant":null}function ft(n,e=0){return n?!(Number(n.turns)>0)&&!n.title&&!(Number(e)>0)&&!n.lastCheckpointId&&!(Number(n.historyTurns)>0)&&n.startSource!=="resume":!0}function fo(n,e=0){return ft(n,e)&&!n.cwd&&!(Number(n.pid)>1)}function ep(n,e){let t=G(e.at)||G(Date.now()),s=e.data||{};n.id=n.id||e.sessionId,n.capture=e.capture||n.capture||"hooks",e.machine&&!n.machine&&(n.machine=gr(e.machine)),n.startedAt=n.startedAt||t,n.lastEventAt=t;let o=()=>{n.lastActivityAt=t,n.finalCheckpointDone=!1,n.endedAt&&e.type!=="session.ended"&&(n.endedAt=null,n.endReason=null,n.closedAt=null)};if(s.cwd&&(n.cwd=s.cwd),s.transcriptPath&&(n.transcriptPath=s.transcriptPath),s.model&&(n.model=s.model),"backend"in s){let r=Kh(s.backend);r?n.backend=r:delete n.backend}switch(s.surface&&(n.surface=s.surface),s.repo&&typeof s.repo=="object"&&(n.repo={...n.repo||{},...kr(s.repo)}),s.sessionTitle&&!xr(s.sessionTitle).id&&s.sessionTitle!==n.title&&!YS(s.sessionTitle,n)&&(n.title=s.sessionTitle,n.titleSource="custom"),s.proc&&typeof s.proc=="object"&&Number(s.proc.pid)>1&&(n.pid=Number(s.proc.pid),n.tty=s.proc.tty||null,n.pidStartedAt=s.proc.pidStartedAt||null,n.pidSource=s.proc.source||(n.capture==="mod"?"mod":"hook")),s.term&&typeof s.term=="object"?n.term=s.term:s.termEnv&&typeof s.termEnv=="object"&&(n.term=Bn(s.termEnv)),e.type){case"session.opened":n.lastHeartbeatAt=t,n.endedAt&&(n.endedAt=null,n.endReason=null,n.closedAt=null);break;case"session.started":s.parentSessionId&&s.parentSessionId!==n.id&&(n.parentSessionId=s.parentSessionId),(s.source==="resume"||s.source==="fork"||s.source==="clear")&&(n.endedAt=null,n.endReason=null,n.closedAt=null),s.source==="resume"&&(n.startSource="resume"),n.capture==="mod"&&(n.lastHeartbeatAt=t);break;case"activity":o(),s.kind==="tool"&&s.phase==="post"&&n.needsInput&&n.lastNotification==="permission_prompt"&&(n.needsInput=!1),s.kind==="prompt"&&(n.needsInput=!1,n.question=null,n.lastNotification=null,s.prompt&&!n.firstPrompt&&(n.firstPrompt=F(s.prompt,300)),n.capture==="hooks"&&(n.inTurn=!0,n.activeSince=n.activeSince||t));break;case"turn_started":o(),n.inTurn=!0,n.needsInput=!1,n.question=null,n.activeSince=n.activeSince||t;break;case"turn_completed":o(),n.inTurn=!1,n.activeSince=null,n.needsInput=!1,n.turns=(n.turns||0)+1,n.turnsSinceCheckpoint=(n.turnsSinceCheckpoint||0)+1,n.backgroundTasks=Number(s.backgroundTasks||0)+Number(s.sessionCrons||0),s.lastAssistantPreview&&(n.lastPreview=F(s.lastAssistantPreview,280)),n.question=s.question?F(s.question,400):null,n.questionAt=s.question?t:null;break;case"notification":n.lastNotification=s.notificationType||null,n.lastNotificationMessage=s.message?F(s.message,200):null,(IS.includes(s.notificationType)||/^elicitation/.test(s.notificationType||""))&&(n.needsInput||(n.needsInputAt=t),n.needsInput=!0);break;case"title_changed":s.title&&vs(s.title)&&(n.title=vs(s.title),n.titleSource=s.titleSource||"custom");break;case"heartbeat":n.lastHeartbeatAt=t,typeof s.inTurn=="boolean"&&!s.inTurn&&n.inTurn&&n.capture==="mod"&&(n.inTurn=!1);break;case"session.ended":n.endedAt=t,n.endReason=s.reason||"other",n.inTurn=!1;break}return n}function vu(n){let e=String(n??"").replace(/\r\n?/g,`
14
+ `}function tp(n){let{data:e}=Ye(n),t={};for(let[s,o]of Object.entries(e))s!=="schema"&&(t[Bh(s)]=o);t.repo&&typeof t.repo=="object"&&(t.repo=Kh(t.repo)),t.repo&&typeof t.repo=="string"&&(t.repo={root:null,name:t.repo});for(let s of["startedAt","lastActivityAt","lastHeartbeatAt","endedAt","lastCheckpointAt","lastEventAt","dormantStretch","closedAt","pidStartedAt"])t[s]&&(t[s]=G(t[s]));if(t.turns=typeof t.turns=="number"?t.turns:0,t.turnsSinceCheckpoint=typeof t.turnsSinceCheckpoint=="number"?t.turnsSinceCheckpoint:0,t.backgroundTasks=typeof t.backgroundTasks=="number"?t.backgroundTasks:0,t.pinned=!!t.pinned,t.archived=!!t.archived,t.backend!==void 0){let s=Wh(t.backend);s?t.backend=s:delete t.backend}return t}function We(n){return br(n).slice(0,6)}function xr(n){return"cp:"+We(n)}function Ar(n){let e=String(n??""),t=e.match(VS);return t?{name:e.slice(0,t.index).trim(),id:t[1].toLowerCase()}:{name:e.trim(),id:null}}function Ts(n){return Ar(n).name}function XS(n,e){if(!n||!e)return!1;if(Ar(n).id)return!0;let t=e.title||null;return!!t&&String(n).trim()===bu("{name}",{name:t,id:e.id})}function ZS(n){return typeof n!="string"||!n.trim()||n===ku||!/\{name\}|\{repo\}|\{id\}/.test(n)?ke.tabTitleFormat:n}function bu(n,{name:e,id:t,repo:s,marker:o=!1}={}){let r=ZS(n),i=F(Ts(e||"")||s||"claude",60).replace(/[\x00-\x1f\x7f]/g," "),a=r.replace(/\{name\}/g,i).replace(/\{repo\}/g,String(s||"").replace(/[\x00-\x1f\x7f]/g," ")).replace(/\{id\}/g,We(t)).replace(/\s+/g," ").trim();return o&&t&&!r.includes("{id}")&&(a+=" · "+xr(t)),a}function QS(n){let e=String(n.TERM_PROGRAM||""),t=String(n.TERM||""),s=e.toLowerCase();return s==="ghostty"?"ghostty":s==="iterm.app"?"iterm2":s==="wezterm"?"wezterm":s==="apple_terminal"?"apple-terminal":s==="vscode"?"vscode":s==="kitty"||n.KITTY_WINDOW_ID||t==="xterm-kitty"?"kitty":n.WEZTERM_PANE?"wezterm":n.ITERM_SESSION_ID?"iterm2":n.GHOSTTY_RESOURCES_DIR||t==="xterm-ghostty"?"ghostty":"unknown"}function Kn(n={}){let e=n||{},t={kind:"unknown",label:"",program:e.TERM_PROGRAM||null,version:e.TERM_PROGRAM_VERSION||null},s=QS(e),o={};if(e.ITERM_SESSION_ID){let r=String(e.ITERM_SESSION_ID);o.itermSessionId=r.includes(":")?r.slice(r.indexOf(":")+1):r}return e.WEZTERM_PANE&&(o.weztermPane=String(e.WEZTERM_PANE)),e.WEZTERM_UNIX_SOCKET&&(o.weztermSocket=String(e.WEZTERM_UNIX_SOCKET)),e.KITTY_WINDOW_ID&&(o.kittyWindowId=String(e.KITTY_WINDOW_ID)),e.KITTY_LISTEN_ON&&(o.kittyListenOn=String(e.KITTY_LISTEN_ON)),e.TERM_SESSION_ID&&(o.termSessionId=String(e.TERM_SESSION_ID)),e.TMUX&&e.TMUX_PANE?(t.kind="tmux",t.tmuxPane=String(e.TMUX_PANE),t.tmuxSocket=String(e.TMUX).split(",")[0]||null,t.host=s==="unknown"?null:s,t.label="tmux"+(t.host?" in "+Rh[t.host]:""),{...t,...o}):(t.kind=s,t.label=s==="unknown"?e.TERM_PROGRAM||e.TERM||"unknown terminal":Rh[s],s==="ghostty"&&e.GHOSTTY_RESOURCES_DIR&&(t.resourcesDir=String(e.GHOSTTY_RESOURCES_DIR)),s==="vscode"&&/cursor/i.test(String(e.TERM_PROGRAM_VERSION||""))&&(t.label="Cursor"),{...t,...o})}function Tr(n){let e=String(n||"").trim().match(/^(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+)$/);return e?Number(e[1]||0)*86400+Number(e[2]||0)*3600+Number(e[3])*60+Number(e[4]):null}function vu(n){let e=String(n||"").trim();return!e||/^\?+$/.test(e)||e==="-"?null:e.startsWith("/dev/")?e:"/dev/"+(/^s\d+$/.test(e)?"tty"+e:e)}function sp(n,e=Date.now()){let t=String(n||"").trim().split(/\s+/),s=Number(t[0]);if(!Number.isInteger(s)||s<=1)return null;let o=Tr(t[2]);return{pid:s,tty:vu(t[1]),pidStartedAt:o==null?null:G(e-o*1e3)}}function xt(n,e,t=ke){if(n.endedAt)return"ended";let s=R(n.lastActivityAt||n.startedAt),o=isNaN(s)?0:e-s,r=t.dormantAfterMin*aa;if(n.capture==="mod"){let i=R(n.lastHeartbeatAt||n.lastEventAt||n.startedAt),a=R(n.pidAliveAt);if(!(!isNaN(a)&&e-a<=Math.max(t.staleAfterSec*1e3,9e4))&&!isNaN(i)&&e-i>t.staleAfterSec*1e3)return"stale"}return n.needsInput?"needs_input":n.inTurn?n.capture==="hooks"&&o>=2*r?"dormant":"active":n.backgroundTasks>0?"background":o>=r?"dormant":"waiting"}function xu(n,e,t=ke){if(n.ignored)return null;let s=xt(n,e,t),o=R(n.lastCheckpointAt),r=R(n.lastActivityAt||n.startedAt),i=isNaN(o)||e-o>=t.minGapMin*aa,a=(n.turnsSinceCheckpoint||0)>0||!isNaN(r)&&(isNaN(o)?!!n.lastActivityAt:r>o);if(s==="ended"||s==="stale")return n.finalCheckpointDone||!a||!n.lastActivityAt?null:n.endReason==="clear"?"clear":"session_end";if(e-r<t.dormantAfterMin*aa){if(t.longRunCheckpointMin&&s==="active"&&n.activeSince){let c=Math.max(R(n.activeSince),isNaN(o)?0:o);if(e-c>=t.longRunCheckpointMin*aa&&i)return"long_run"}return null}return n.dormantStretch&&n.dormantStretch===n.lastActivityAt||!i?null:s==="dormant"&&(n.turnsSinceCheckpoint||0)>=1||s==="needs_input"&&a?"dormant":null}function pt(n,e=0){return n?!(Number(n.turns)>0)&&!n.title&&!n.firstPrompt&&!(Number(e)>0)&&!n.lastCheckpointId&&!(Number(n.historyTurns)>0)&&n.startSource!=="resume":!0}function po(n,e=0){return pt(n,e)&&!n.cwd&&!(Number(n.pid)>1)}function op(n,e){let t=G(e.at)||G(Date.now()),s=e.data||{};n.id=n.id||e.sessionId,n.capture=e.capture||n.capture||"hooks",e.machine&&!n.machine&&(n.machine=gr(e.machine)),n.startedAt=n.startedAt||t,n.lastEventAt=t;let o=()=>{n.lastActivityAt=t,n.finalCheckpointDone=!1,n.endedAt&&e.type!=="session.ended"&&(n.endedAt=null,n.endReason=null,n.closedAt=null)};if(s.cwd&&(n.cwd=s.cwd),s.transcriptPath&&(n.transcriptPath=s.transcriptPath),s.model&&(n.model=s.model),"backend"in s){let r=Wh(s.backend);r?n.backend=r:delete n.backend}switch(s.surface&&(n.surface=s.surface),s.repo&&typeof s.repo=="object"&&(n.repo={...n.repo||{},...kr(s.repo)}),s.sessionTitle&&!Ar(s.sessionTitle).id&&s.sessionTitle!==n.title&&!XS(s.sessionTitle,n)&&(n.title=s.sessionTitle,n.titleSource="custom"),s.proc&&typeof s.proc=="object"&&Number(s.proc.pid)>1&&(n.pid=Number(s.proc.pid),n.tty=s.proc.tty||null,n.pidStartedAt=s.proc.pidStartedAt||null,n.pidSource=s.proc.source||(n.capture==="mod"?"mod":"hook")),s.term&&typeof s.term=="object"?n.term=s.term:s.termEnv&&typeof s.termEnv=="object"&&(n.term=Kn(s.termEnv)),e.type){case"session.opened":n.lastHeartbeatAt=t,n.endedAt&&(n.endedAt=null,n.endReason=null,n.closedAt=null);break;case"session.started":s.parentSessionId&&s.parentSessionId!==n.id&&(n.parentSessionId=s.parentSessionId),(s.source==="resume"||s.source==="fork"||s.source==="clear")&&(n.endedAt=null,n.endReason=null,n.closedAt=null),s.source==="resume"&&(n.startSource="resume"),n.capture==="mod"&&(n.lastHeartbeatAt=t);break;case"activity":o(),s.kind==="tool"&&s.phase==="post"&&n.needsInput&&n.lastNotification==="permission_prompt"&&(n.needsInput=!1),s.kind==="prompt"&&(n.needsInput=!1,n.question=null,n.lastNotification=null,s.prompt&&!n.firstPrompt&&(n.firstPrompt=F(s.prompt,300)),n.capture==="hooks"&&(n.inTurn=!0,n.activeSince=n.activeSince||t));break;case"turn_started":o(),n.inTurn=!0,n.needsInput=!1,n.question=null,n.activeSince=n.activeSince||t;break;case"turn_completed":o(),n.inTurn=!1,n.activeSince=null,n.needsInput=!1,n.turns=(n.turns||0)+1,n.turnsSinceCheckpoint=(n.turnsSinceCheckpoint||0)+1,n.backgroundTasks=Number(s.backgroundTasks||0)+Number(s.sessionCrons||0),s.lastAssistantPreview&&(n.lastPreview=F(s.lastAssistantPreview,280)),n.question=s.question?F(s.question,400):null,n.questionAt=s.question?t:null;break;case"notification":n.lastNotification=s.notificationType||null,n.lastNotificationMessage=s.message?F(s.message,200):null,(PS.includes(s.notificationType)||/^elicitation/.test(s.notificationType||""))&&(n.needsInput||(n.needsInputAt=t),n.needsInput=!0);break;case"title_changed":s.title&&Ts(s.title)&&(n.title=Ts(s.title),n.titleSource=s.titleSource||"custom");break;case"heartbeat":n.lastHeartbeatAt=t,typeof s.inTurn=="boolean"&&!s.inTurn&&n.inTurn&&n.capture==="mod"&&(n.inTurn=!1);break;case"session.ended":n.endedAt=t,n.endReason=s.reason||"other",n.inTurn=!1;break}return n}function Au(n){let e=String(n??"").replace(/\r\n?/g,`
15
15
  `).trim();if(!e||/```\s*$/.test(e))return null;let t=e.split(/\n\s*\n/).map(r=>r.trim()).filter(Boolean),s=t[t.length-1]||"",o=s.replace(/[\s*_`)\]"'”’»]+$/u,"");return/[??]$/.test(o)?(t.length>1&&/^([-*•]|\d+[.)])\s/.test(s)&&/[?:]\s*$/.test(t[t.length-2])&&(s=t[t.length-2]+`
16
- `+s),s=s.replace(/^#+\s*/,"").replace(/[*_`]+$/,""),s.length>400?"…"+s.slice(-399):s):null}function kr(n){let e={};for(let[t,s]of Object.entries(n))s!=null&&(e[t]=s);return e}function JS(n=[]){let e=[];for(let t of n)try{let s=String(t),o="g";s.startsWith("(?i)")&&(s=s.slice(4),o="gi"),e.push(new RegExp(s,o))}catch{}return e}function fa(n,e){let t=String(n??"");for(let s of JS(e))t=t.replace(s,"[REDACTED]");return t}function wr(n,e){let t=String(n||"");return e&&t.startsWith(e.replace(/\/+$/,"")+"/")?t.slice(e.replace(/\/+$/,"").length+1):t}function la(n,e={},t){let s=e||{};return s.file_path?wr(s.file_path,t):s.notebook_path?wr(s.notebook_path,t):s.command?F(s.command,80):s.pattern?F(s.pattern,60):s.url?F(s.url,80):s.path?wr(s.path,t):s.description?F(s.description,60):""}function qS(n,e){let t=[];for(let s of n||[])for(let o of s.toolUses||[]){let r=o.tool||o.name,i=o.input&&(o.input.file_path||o.input.notebook_path);if(tp.includes(r)&&i){let a=wr(i,e);t.includes(a)||t.push(a)}}return t}function VS(n,{maxInputChars:e=24e3,cwd:t}={}){let s=[],o=0,r=!1;for(let i=(n||[]).length-1;i>=0;i--){let a=n[i],l=[];if(a.role==="user"){if(a.text&&a.text.trim()&&l.push("USER: "+a.text.trim()),!r){let u=(a.toolResults||[]).find(d=>d.isError);u&&(l.push("LAST ERROR: "+F(u.text,500)),r=!0)}}else{a.text&&a.text.trim()&&l.push("ASSISTANT: "+F(a.text,1500));for(let u of a.toolUses||[]){let d=u.tool||u.name,f=" -> "+d+(la(d,u.input,t)?" "+la(d,u.input,t):"");u.isError&&(f+=" -> error",!r&&u.text&&(f+=": "+F(u.text,500),r=!0)),l.push(f)}}if(!l.length)continue;let c=l.join(`
16
+ `+s),s=s.replace(/^#+\s*/,"").replace(/[*_`]+$/,""),s.length>400?"…"+s.slice(-399):s):null}function kr(n){let e={};for(let[t,s]of Object.entries(n))s!=null&&(e[t]=s);return e}function ek(n=[]){let e=[];for(let t of n)try{let s=String(t),o="g";s.startsWith("(?i)")&&(s=s.slice(4),o="gi"),e.push(new RegExp(s,o))}catch{}return e}function ma(n,e){let t=String(n??"");for(let s of ek(e))t=t.replace(s,"[REDACTED]");return t}function wr(n,e){let t=String(n||"");return e&&t.startsWith(e.replace(/\/+$/,"")+"/")?t.slice(e.replace(/\/+$/,"").length+1):t}function da(n,e={},t){let s=e||{};return s.file_path?wr(s.file_path,t):s.notebook_path?wr(s.notebook_path,t):s.command?F(s.command,80):s.pattern?F(s.pattern,60):s.url?F(s.url,80):s.path?wr(s.path,t):s.description?F(s.description,60):""}function tk(n,e){let t=[];for(let s of n||[])for(let o of s.toolUses||[]){let r=o.tool||o.name,i=o.input&&(o.input.file_path||o.input.notebook_path);if(rp.includes(r)&&i){let a=wr(i,e);t.includes(a)||t.push(a)}}return t}function nk(n,{maxInputChars:e=24e3,cwd:t}={}){let s=[],o=0,r=!1;for(let i=(n||[]).length-1;i>=0;i--){let a=n[i],l=[];if(a.role==="user"){if(a.text&&a.text.trim()&&l.push("USER: "+a.text.trim()),!r){let u=(a.toolResults||[]).find(d=>d.isError);u&&(l.push("LAST ERROR: "+F(u.text,500)),r=!0)}}else{a.text&&a.text.trim()&&l.push("ASSISTANT: "+F(a.text,1500));for(let u of a.toolUses||[]){let d=u.tool||u.name,f=" -> "+d+(da(d,u.input,t)?" "+da(d,u.input,t):"");u.isError&&(f+=" -> error",!r&&u.text&&(f+=": "+F(u.text,500),r=!0)),l.push(f)}}if(!l.length)continue;let c=l.join(`
17
17
  `);if(o+c.length>e&&s.length)break;s.push(c),o+=c.length+1}return s.reverse().join(`
18
- `)}function gn(n,{strict:e=!1}={}){let t=String(n??"").trim().toLowerCase();if(Tr[t])return t;let s={s:"short",brief:"short",i:"informative",info:"informative",medium:"informative",normal:"informative",default:"informative",l:"long",full:"long",detailed:"long"}[t];return s||(e?null:XS)}function xu(n=ke,e){if(e){let t=gn(e,{strict:!0});if(t)return t}return gn(n&&n.summary&&n.summary.length)}function ha({session:n,previous:e,messages:t,cfg:s=ke,wantTitle:o=!1,userNote:r,length:i}){let a=xu(s,i),l=s.summarizer&&s.summarizer.maxInputChars||24e3,c=n.cwd,u=qS(t,c),d=n.repo&&n.repo.branch,f=e?["Doing: "+e.doing,"Stopped at: "+e.stoppedAt,"Next: "+e.next].join(`
19
- `):"none",h=["Repo: "+me(n)+(d?" (branch "+d+")":"")+" · cwd "+(c||"?"),"Title: "+(n.title||"(none yet)"),"Previous checkpoint: "+f,"Files touched since then: "+(u.length?u.join(", "):"none")];r&&h.push("Note from the developer: "+r),n.needsInput&&h.push("State: the session is waiting on the developer ("+(n.lastNotification||"a prompt")+")."),o&&h.push("Also write the line: Title: <max 6 words>.");let p=VS(t,{maxInputChars:l,cwd:c}),m=s.redact&&s.redact.patterns||[],g=fa(h.join(`
18
+ `)}function wn(n,{strict:e=!1}={}){let t=String(n??"").trim().toLowerCase();if(_r[t])return t;let s={s:"short",brief:"short",i:"informative",info:"informative",medium:"informative",normal:"informative",default:"informative",l:"long",full:"long",detailed:"long"}[t];return s||(e?null:sk)}function Tu(n=ke,e){if(e){let t=wn(e,{strict:!0});if(t)return t}return wn(n&&n.summary&&n.summary.length)}function ga({session:n,previous:e,messages:t,cfg:s=ke,wantTitle:o=!1,userNote:r,length:i}){let a=Tu(s,i),l=s.summarizer&&s.summarizer.maxInputChars||24e3,c=n.cwd,u=tk(t,c),d=n.repo&&n.repo.branch,f=e?["Doing: "+e.doing,"Stopped at: "+e.stoppedAt,"Next: "+e.next].join(`
19
+ `):"none",h=["Repo: "+ge(n)+(d?" (branch "+d+")":"")+" · cwd "+(c||"?"),"Title: "+(n.title||"(none yet)"),"Previous checkpoint: "+f,"Files touched since then: "+(u.length?u.join(", "):"none")];r&&h.push("Note from the developer: "+r),n.needsInput&&h.push("State: the session is waiting on the developer ("+(n.lastNotification||"a prompt")+")."),o&&h.push("Also write the line: Title: <max 6 words>.");let p=nk(t,{maxInputChars:l,cwd:c}),m=s.redact&&s.redact.patterns||[],g=ma(h.join(`
20
20
  `)+`
21
21
  Transcript (oldest→newest, trimmed):
22
- `+(p||"(empty)"),m);return{system:np[a],prompt:g,filesTouched:u,inputChars:g.length,length:a,maxTokens:Tr[a].maxTokens}}function nk(n){let e=String(n||"").trim(),t=e.match(/^`?([^`\s][^`]*?)`?\s*(?:—|–|-{1,2}|:)\s+(.+)$/);if(t&&/[./]/.test(t[1])&&!/\s/.test(t[1].trim()))return{path:t[1].trim(),why:t[2].trim()};let s=e.match(/^`?([^`\s]+)`?$/);return s?{path:s[1],why:""}:{path:"",why:e}}function Au(){return{decisions:[],questions:[],files:[],didntWork:[],recap:"",nextSteps:[]}}function sp(n){let e=Au(),t=null,s=(o,r)=>{if(r=String(r||"").replace(/\*\*/g,"").trim(),!(!r||tk.test(r))){if(o==="recap"){e.recap=e.recap?e.recap+" "+r:r;return}if(o==="files"){let i=nk(r);(i.path||i.why)&&e.files.push(i);return}e[o].push(r)}};for(let o of n){let r=String(o).trim();if(!r)continue;let i=r.replace(/^#+\s*/,"").replace(/\*\*/g,"").replace(/^_+|_+$/g,"").trim(),a=i.match(/^([A-Za-z' ’0-9]+?)\s*:\s*(.*)$/)||i.match(/^([A-Za-z' ’0-9]+?)$/),l=a?QS[a[1].replace(/’/g,"'").trim().toLowerCase()]:null;if(l&&(a[2]!==void 0||/^#/.test(r)||!t)){t=l,a[2]&&s(l,a[2].replace(/^[-*•]\s*/,""));continue}if(!t)continue;let c=r.match(/^(?:[-*•]|\d+[.)])\s+(.*)$/);c?s(t,c[1]):t==="recap"?s(t,r):/^(doing|stopped at|next|title|open question|note|resume)\s*:/i.test(i)?t=null:s(t,r)}for(let o of Object.keys(Sr))e[o]=e[o].slice(0,Sr[o]);return e.recap=F(e.recap,1600),e}function op(n){let e={doing:"",stoppedAt:"",next:"",title:null,openQuestion:null,parseOk:!0},t={doing:"doing","stopped at":"stoppedAt",next:"next",title:"title","open question":"openQuestion"},s=[];for(let o of String(n||"").split(`
23
- `)){let i=o.replace(/^[\s*_#>-]+/,"").replace(/\*\*/g,"").match(/^(doing|stopped at|next|title|open question)\s*:\s*(.+)$/i);i?e[t[i[1].toLowerCase()]]=i[2].trim():s.push(o)}return!e.doing||!e.stoppedAt||!e.next?{doing:F(n,600)||"(empty reply)",stoppedAt:e.stoppedAt||"",next:e.next||"",title:e.title,openQuestion:null,parseOk:!1,...Au()}:(e.title&&(e.title=F(e.title.replace(/^["']|["']$/g,""),60)),{...e,...sp(s)})}function Tu(n,e={},{length:t="short",cwd:s}={}){let o=n||[],r=[...o].reverse().find(w=>w.role==="user"&&w.text&&w.text.trim()),i=[...o].reverse().find(w=>w.role==="assistant"&&w.text&&w.text.trim()),a=null;for(let w=o.length-1;w>=0&&!a;w--){let x=o[w],v=(x.toolResults||[]).find(y=>y.isError),b=(x.toolUses||[]).find(y=>y.isError);if(v?a=v.text:b&&(a=b.text||b.tool+" failed"),x===i&&!a)break}let l=w=>{let x=F(w,400),v=x.match(/^(.{20,240}?[.!?])(\s|$)/);return v?v[1]:F(x,240)},c=r?"Working on: “"+F(r.text.split(`
24
- `)[0],140)+"”":e.firstPrompt?"Working on: “"+F(e.firstPrompt,140)+"”":"No prompt captured yet.",u=a?"Last error: "+F(a,200):i?l(i.text):e.lastPreview?l(e.lastPreview):"No reply captured yet.",d="Resume the session and continue from the last reply.",h=(i?F(i.text,4e3).slice(-300):"").match(/([^.!?\n]{8,200}\?)\s*$/);e.needsInput?d="YOU: answer the pending "+String(e.lastNotification||"prompt").replace(/_/g," ")+" in this session.":h?d="YOU: "+F(h[1],180):a&&(d="Look at the last error and decide whether to retry or change approach.");let p={doing:c,stoppedAt:u,next:d,title:null,openQuestion:null,parseOk:!0},m=gn(t);if(m==="short")return p;let g=Au(),S=s||e.cwd,k=new Map;for(let w of o)for(let x of w.toolUses||[]){let v=x.tool||x.name,b=x.input&&(x.input.file_path||x.input.notebook_path);if(tp.includes(v)&&b){let y=wr(b,S);k.has(y)||k.set(y,v==="Write"?"written":"edited")}x.isError&&g.didntWork.length<Sr.didntWork&&g.didntWork.push(F(v+(la(v,x.input,S)?" "+la(v,x.input,S):"")+" failed"+(x.text?": "+x.text.split(`
25
- `)[0]:""),200))}g.files=[...k].slice(-Sr.files).map(([w,x])=>({path:w,why:x}));for(let w of o)if(w.role==="assistant"&&w.text){let x=vu(w.text);x&&!g.questions.includes(x)&&g.questions.push(F(x,200))}if(g.questions=g.questions.slice(-Sr.questions),m==="long"){let w=o.find(v=>v.role==="user"&&v.text&&v.text.trim()),x=o.filter(v=>v.role==="user"&&v.text&&v.text.trim()).length;g.recap=[w?"The session started with: “"+F(w.text.split(`
26
- `)[0],160)+"”.":"",x>1?"There were "+x+" prompts since.":"",k.size?k.size+" file"+(k.size===1?" was":"s were")+" changed.":"",a?"The last tool call failed.":"","Written without a model, from the transcript alone."].filter(Boolean).join(" "),g.nextSteps=[d]}return{...p,...g}}function rp(n){let e=String(n.firstPrompt||"").replace(/\s+/g," ").trim().split(" ").filter(Boolean).slice(0,6).join(" "),t=n.repo&&n.repo.branch;return me(n)+(t?"@"+t:"")+(e?" · "+e:"")}function ap(n){let e=String(n||"").split(`
27
- `).map(t=>t.trim()).find(Boolean)||"";return F(e.replace(/^title\s*:\s*/i,"").replace(/^["'`*]+|["'`*.]+$/g,""),60)}function lp(n,e=6){let t=String(n||"").replace(/<think>[\s\S]*?<\/think>/gi,"").trim(),s=[];for(let o of t.split(`
28
- `)){let r=o.match(/^\s*(?:[-*•]|\d+[.)])\s+(.*\S)\s*$/);r&&s.push(r[1].replace(/\*\*/g,"").trim())}return s.length<2?null:s.slice(0,e)}function ra(n){return String(n||"").replace(/\*\*(.+?)\*\*/g,"$1").replace(/__(.+?)__/g,"$1").replace(/\s*<!--.*?-->\s*$/,"").replace(/\s+/g," ").trim()}function uu(n){let e=n.match(sk);return e?{indent:e[1].replace(/\t/g," ").length,done:e[2]!==" ",checkbox:!0,text:e[3]}:(e=n.match(ok),e?{indent:e[1].replace(/\t/g," ").length,done:!1,checkbox:!1,numbered:!0,n:Number(e[2]),text:e[3]}:(e=n.match(rk),e&&!/^[-*_]{3,}$/.test(n.trim())?{indent:e[1].replace(/\t/g," ").length,done:!1,checkbox:!1,text:e[2]}:null))}function xs(n){return String(n||"").replace(/^#+\s*/,"").replace(/\*\*|__/g,"").replace(/[::]\s*$/,"").replace(/\s+/g," ").trim()}function cp(n,{minItems:e=2}={}){let t=String(n||"").replace(/\r\n/g,`
22
+ `+(p||"(empty)"),m);return{system:ip[a],prompt:g,filesTouched:u,inputChars:g.length,length:a,maxTokens:_r[a].maxTokens}}function lk(n){let e=String(n||"").trim(),t=e.match(/^`?([^`\s][^`]*?)`?\s*(?:—|–|-{1,2}|:)\s+(.+)$/);if(t&&/[./]/.test(t[1])&&!/\s/.test(t[1].trim()))return{path:t[1].trim(),why:t[2].trim()};let s=e.match(/^`?([^`\s]+)`?$/);return s?{path:s[1],why:""}:{path:"",why:e}}function _u(){return{decisions:[],questions:[],files:[],didntWork:[],recap:"",nextSteps:[]}}function ap(n){let e=_u(),t=null,s=(o,r)=>{if(r=String(r||"").replace(/\*\*/g,"").trim(),!(!r||ak.test(r))){if(o==="recap"){e.recap=e.recap?e.recap+" "+r:r;return}if(o==="files"){let i=lk(r);(i.path||i.why)&&e.files.push(i);return}e[o].push(r)}};for(let o of n){let r=String(o).trim();if(!r)continue;let i=r.replace(/^#+\s*/,"").replace(/\*\*/g,"").replace(/^_+|_+$/g,"").trim(),a=i.match(/^([A-Za-z' ’0-9]+?)\s*:\s*(.*)$/)||i.match(/^([A-Za-z' ’0-9]+?)$/),l=a?rk[a[1].replace(/’/g,"'").trim().toLowerCase()]:null;if(l&&(a[2]!==void 0||/^#/.test(r)||!t)){t=l,a[2]&&s(l,a[2].replace(/^[-*•]\s*/,""));continue}if(!t)continue;let c=r.match(/^(?:[-*•]|\d+[.)])\s+(.*)$/);c?s(t,c[1]):t==="recap"?s(t,r):/^(doing|stopped at|next|title|open question|note|resume)\s*:/i.test(i)?t=null:s(t,r)}for(let o of Object.keys(Sr))e[o]=e[o].slice(0,Sr[o]);return e.recap=F(e.recap,1600),e}function lp(n){let e={doing:"",stoppedAt:"",next:"",title:null,openQuestion:null,parseOk:!0},t={doing:"doing","stopped at":"stoppedAt",next:"next",title:"title","open question":"openQuestion"},s=[];for(let o of String(n||"").split(`
23
+ `)){let i=o.replace(/^[\s*_#>-]+/,"").replace(/\*\*/g,"").match(/^(doing|stopped at|next|title|open question)\s*:\s*(.+)$/i);i?e[t[i[1].toLowerCase()]]=i[2].trim():s.push(o)}return!e.doing||!e.stoppedAt||!e.next?{doing:F(n,600)||"(empty reply)",stoppedAt:e.stoppedAt||"",next:e.next||"",title:e.title,openQuestion:null,parseOk:!1,..._u()}:(e.title&&(e.title=F(e.title.replace(/^["']|["']$/g,""),60)),{...e,...ap(s)})}function Eu(n,e={},{length:t="short",cwd:s}={}){let o=n||[],r=[...o].reverse().find(w=>w.role==="user"&&w.text&&w.text.trim()),i=[...o].reverse().find(w=>w.role==="assistant"&&w.text&&w.text.trim()),a=null;for(let w=o.length-1;w>=0&&!a;w--){let x=o[w],v=(x.toolResults||[]).find(y=>y.isError),b=(x.toolUses||[]).find(y=>y.isError);if(v?a=v.text:b&&(a=b.text||b.tool+" failed"),x===i&&!a)break}let l=w=>{let x=F(w,400),v=x.match(/^(.{20,240}?[.!?])(\s|$)/);return v?v[1]:F(x,240)},c=r?"Working on: “"+F(r.text.split(`
24
+ `)[0],140)+"”":e.firstPrompt?"Working on: “"+F(e.firstPrompt,140)+"”":"No prompt captured yet.",u=a?"Last error: "+F(a,200):i?l(i.text):e.lastPreview?l(e.lastPreview):"No reply captured yet.",d="Resume the session and continue from the last reply.",h=(i?F(i.text,4e3).slice(-300):"").match(/([^.!?\n]{8,200}\?)\s*$/);e.needsInput?d="YOU: answer the pending "+String(e.lastNotification||"prompt").replace(/_/g," ")+" in this session.":h?d="YOU: "+F(h[1],180):a&&(d="Look at the last error and decide whether to retry or change approach.");let p={doing:c,stoppedAt:u,next:d,title:null,openQuestion:null,parseOk:!0},m=wn(t);if(m==="short")return p;let g=_u(),S=s||e.cwd,k=new Map;for(let w of o)for(let x of w.toolUses||[]){let v=x.tool||x.name,b=x.input&&(x.input.file_path||x.input.notebook_path);if(rp.includes(v)&&b){let y=wr(b,S);k.has(y)||k.set(y,v==="Write"?"written":"edited")}x.isError&&g.didntWork.length<Sr.didntWork&&g.didntWork.push(F(v+(da(v,x.input,S)?" "+da(v,x.input,S):"")+" failed"+(x.text?": "+x.text.split(`
25
+ `)[0]:""),200))}g.files=[...k].slice(-Sr.files).map(([w,x])=>({path:w,why:x}));for(let w of o)if(w.role==="assistant"&&w.text){let x=Au(w.text);x&&!g.questions.includes(x)&&g.questions.push(F(x,200))}if(g.questions=g.questions.slice(-Sr.questions),m==="long"){let w=o.find(v=>v.role==="user"&&v.text&&v.text.trim()),x=o.filter(v=>v.role==="user"&&v.text&&v.text.trim()).length;g.recap=[w?"The session started with: “"+F(w.text.split(`
26
+ `)[0],160)+"”.":"",x>1?"There were "+x+" prompts since.":"",k.size?k.size+" file"+(k.size===1?" was":"s were")+" changed.":"",a?"The last tool call failed.":"","Written without a model, from the transcript alone."].filter(Boolean).join(" "),g.nextSteps=[d]}return{...p,...g}}function cp(n){let e=String(n.firstPrompt||"").replace(/\s+/g," ").trim().split(" ").filter(Boolean).slice(0,6).join(" "),t=n.repo&&n.repo.branch;return ge(n)+(t?"@"+t:"")+(e?" · "+e:"")}function dp(n){let e=String(n||"").split(`
27
+ `).map(t=>t.trim()).find(Boolean)||"";return F(e.replace(/^title\s*:\s*/i,"").replace(/^["'`*]+|["'`*.]+$/g,""),60)}function fp(n,e=6){let t=String(n||"").replace(/<think>[\s\S]*?<\/think>/gi,"").trim(),s=[];for(let o of t.split(`
28
+ `)){let r=o.match(/^\s*(?:[-*•]|\d+[.)])\s+(.*\S)\s*$/);r&&s.push(r[1].replace(/\*\*/g,"").trim())}return s.length<2?null:s.slice(0,e)}function la(n){return String(n||"").replace(/\*\*(.+?)\*\*/g,"$1").replace(/__(.+?)__/g,"$1").replace(/\s*<!--.*?-->\s*$/,"").replace(/\s+/g," ").trim()}function fu(n){let e=n.match(ck);return e?{indent:e[1].replace(/\t/g," ").length,done:e[2]!==" ",checkbox:!0,text:e[3]}:(e=n.match(uk),e?{indent:e[1].replace(/\t/g," ").length,done:!1,checkbox:!1,numbered:!0,n:Number(e[2]),text:e[3]}:(e=n.match(dk),e&&!/^[-*_]{3,}$/.test(n.trim())?{indent:e[1].replace(/\t/g," ").length,done:!1,checkbox:!1,text:e[2]}:null))}function _s(n){return String(n||"").replace(/^#+\s*/,"").replace(/\*\*|__/g,"").replace(/[::]\s*$/,"").replace(/\s+/g," ").trim()}function hp(n,{minItems:e=2}={}){let t=String(n||"").replace(/\r\n/g,`
29
29
  `).split(`
30
- `),s=[],o=!1,r=null,i=-10;for(let a=0;a<t.length;a++){let l=t[a];if(Dh.test(l)){o=!o;continue}if(o||!l.trim())continue;let c=uu(l);if(!c){let k=l.match(ik);k?(r=xs(k[1]||k[2]||k[3]||k[4]),i=a):r=null;continue}let u=c.indent,d=[{text:ra(c.text),done:c.done,checkbox:c.checkbox,numbered:!!c.numbered}],f=a+1;for(;f<t.length;f++){let k=t[f];if(Dh.test(k))break;if(!k.trim()){let v=f+1;for(;v<t.length&&!t[v].trim();)v++;let b=v<t.length?uu(t[v]):null;if(b&&b.indent<=u+1){f=v-1;continue}break}let w=uu(k),x=k.match(/^\s*/)[0].replace(/\t/g," ").length;if(w&&w.indent<=u+1){d.push({text:ra(w.text),done:w.done,checkbox:w.checkbox,numbered:!!w.numbered});continue}if(x>u){let v=d[d.length-1],b=ra(w?w.text:k);v&&b&&(v.text=w?v.sub?v.text+"; "+b:v.text+" — "+b:v.text+" "+b),v&&w&&(v.sub=!0);continue}break}let h=d.filter(k=>k.checkbox).length,p=r&&i>=a-3?r:null,m=h>=Math.ceil(d.length/2)?"checklist":d.every(k=>k.numbered)?"numbered":"bulleted",g=m==="checklist"||p&&ak.test(p),S=d.filter(k=>k.text);g&&S.length>=e&&s.push({kind:m,heading:p,items:S.map(k=>({text:k.text,done:!!k.done})),line:a}),r=null,a=f-1}return s}function pa(n,{minItems:e=2,fallback:t=null}={}){let s=[];return(n||[]).forEach((o,r)=>{if(!(!o||o.role!=="assistant"||!o.text))for(let i of cp(o.text,{minItems:e}))s.push({...i,message:r,at:o.at||null})}),s.map((o,r)=>({...o,n:r+1,name:_r(o,{fallback:t})}))}function ma(n,e){let t=(n||[]).map(r=>pu(r.text||r)).filter(Boolean),s=new Set((e||[]).map(r=>pu(r.text||r)).filter(Boolean));return!t.length||!s.size?0:t.filter(r=>s.has(r)).length/Math.min(t.length,s.size)}function lk(n){let e=2166136261;for(let t of(n||[]).map(s=>pu(s.text||s)).join("|"))e^=t.charCodeAt(0),e=Math.imul(e,16777619)>>>0;return e.toString(16).padStart(8,"0")}function _r(n,{fallback:e=null}={}){let t=xs(n&&n.heading);if(t=t.split(/(?<=[.!?])\s+/).pop(),t=t.replace(/^(?:ok(?:ay)?|sure|great|alright|done)[,.!]?\s+/i,""),t=t.replace(/^(?:so\s+)?(?:here(?:'|’)?s|here is|here are|these are|this is|below (?:is|are))\s+(?:(?:the|my|a|an|your|our|some)\s+)?/i,""),t=t.replace(/^(?:the|my|your|our)\s+/i,"").replace(/[.!]+$/,"").trim(),t)return F(t.charAt(0).toUpperCase()+t.slice(1),60);let s=xs(e);return s?F(s,48)+" checklist":"Checklist"}function up(n,{minItems:e=3,maxItems:t=60,fallback:s=null}={}){let o=cp(n,{minItems:e});if(!o.length)return null;let r=o[o.length-1],i=r.items.slice(0,t).map(a=>({text:F(a.text,300),done:!!a.done}));return{count:r.items.length,heading:r.heading||null,kind:r.kind,name:_r(r,{fallback:s}),items:i,fp:lk(i)}}function ga(n,e=48){return String(n||"").toLowerCase().normalize("NFKD").replace(/[\u0300-\u036f]/g,"").replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,e).replace(/-+$/,"")||"list"}function _u(n){let{data:e,body:t}=Ye(n),s={name:e.name==null?null:String(e.name),repo:e.repo==null?null:String(e.repo),repoKey:e.repo_key==null?null:String(e.repo_key),sourceSession:e.source_session||null,sourceMachine:e.source_machine==null?null:String(e.source_machine),sourceTitle:e.source_title==null?null:String(e.source_title),created:e.created?G(e.created):null,updated:e.updated?G(e.updated):null,archived:e.archived===!0,nextId:Number(e.next_id)||0},o=[],r=[],i=!1;for(let a of String(t||"").replace(/\r\n/g,`
30
+ `),s=[],o=!1,r=null,i=-10;for(let a=0;a<t.length;a++){let l=t[a];if(Fh.test(l)){o=!o;continue}if(o||!l.trim())continue;let c=fu(l);if(!c){let k=l.match(fk);k?(r=_s(k[1]||k[2]||k[3]||k[4]),i=a):r=null;continue}let u=c.indent,d=[{text:la(c.text),done:c.done,checkbox:c.checkbox,numbered:!!c.numbered}],f=a+1;for(;f<t.length;f++){let k=t[f];if(Fh.test(k))break;if(!k.trim()){let v=f+1;for(;v<t.length&&!t[v].trim();)v++;let b=v<t.length?fu(t[v]):null;if(b&&b.indent<=u+1){f=v-1;continue}break}let w=fu(k),x=k.match(/^\s*/)[0].replace(/\t/g," ").length;if(w&&w.indent<=u+1){d.push({text:la(w.text),done:w.done,checkbox:w.checkbox,numbered:!!w.numbered});continue}if(x>u){let v=d[d.length-1],b=la(w?w.text:k);v&&b&&(v.text=w?v.sub?v.text+"; "+b:v.text+" — "+b:v.text+" "+b),v&&w&&(v.sub=!0);continue}break}let h=d.filter(k=>k.checkbox).length,p=r&&i>=a-3?r:null,m=h>=Math.ceil(d.length/2)?"checklist":d.every(k=>k.numbered)?"numbered":"bulleted",g=m==="checklist"||p&&hk.test(p),S=d.filter(k=>k.text);g&&S.length>=e&&s.push({kind:m,heading:p,items:S.map(k=>({text:k.text,done:!!k.done})),line:a}),r=null,a=f-1}return s}function ya(n,{minItems:e=2,fallback:t=null}={}){let s=[];return(n||[]).forEach((o,r)=>{if(!(!o||o.role!=="assistant"||!o.text))for(let i of hp(o.text,{minItems:e}))s.push({...i,message:r,at:o.at||null})}),s.map((o,r)=>({...o,n:r+1,name:Er(o,{fallback:t})}))}function wa(n,e){let t=(n||[]).map(r=>gu(r.text||r)).filter(Boolean),s=new Set((e||[]).map(r=>gu(r.text||r)).filter(Boolean));return!t.length||!s.size?0:t.filter(r=>s.has(r)).length/Math.min(t.length,s.size)}function pk(n){let e=2166136261;for(let t of(n||[]).map(s=>gu(s.text||s)).join("|"))e^=t.charCodeAt(0),e=Math.imul(e,16777619)>>>0;return e.toString(16).padStart(8,"0")}function Er(n,{fallback:e=null}={}){let t=_s(n&&n.heading);if(t=t.split(/(?<=[.!?])\s+/).pop(),t=t.replace(/^(?:ok(?:ay)?|sure|great|alright|done)[,.!]?\s+/i,""),t=t.replace(/^(?:so\s+)?(?:here(?:'|’)?s|here is|here are|these are|this is|below (?:is|are))\s+(?:(?:the|my|a|an|your|our|some)\s+)?/i,""),t=t.replace(/^(?:the|my|your|our)\s+/i,"").replace(/[.!]+$/,"").trim(),t)return F(t.charAt(0).toUpperCase()+t.slice(1),60);let s=_s(e);return s?F(s,48)+" checklist":"Checklist"}function pp(n,{minItems:e=3,maxItems:t=60,fallback:s=null}={}){let o=hp(n,{minItems:e});if(!o.length)return null;let r=o[o.length-1],i=r.items.slice(0,t).map(a=>({text:F(a.text,300),done:!!a.done}));return{count:r.items.length,heading:r.heading||null,kind:r.kind,name:Er(r,{fallback:s}),items:i,fp:pk(i)}}function Sa(n,e=48){return String(n||"").toLowerCase().normalize("NFKD").replace(/[\u0300-\u036f]/g,"").replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,e).replace(/-+$/,"")||"list"}function Ou(n){let{data:e,body:t}=Ye(n),s={name:e.name==null?null:String(e.name),repo:e.repo==null?null:String(e.repo),repoKey:e.repo_key==null?null:String(e.repo_key),sourceSession:e.source_session||null,sourceMachine:e.source_machine==null?null:String(e.source_machine),sourceTitle:e.source_title==null?null:String(e.source_title),created:e.created?G(e.created):null,updated:e.updated?G(e.updated):null,archived:e.archived===!0,nextId:Number(e.next_id)||0},o=[],r=[],i=!1;for(let a of String(t||"").replace(/\r\n/g,`
31
31
  `).replace(/\n+$/,"").split(`
32
- `)){let l=a.match(uk);if(l&&!l[1]){let c={id:l[4]?Number(l[4]):null,text:l[3].trim(),done:l[2]!==" ",at:l[5]?G(l[5]):null};o.push(c),r.push({item:c});continue}if(!i&&/^#\s+/.test(a)){i=!0,s.name||(s.name=a.replace(/^#\s+/,"").trim()),r.push({title:!0});continue}r.push({line:a})}return{meta:s,items:o,segs:r}}function ho(n){let e=Math.max(n.meta.nextId||1,...n.items.map(s=>(s.id||0)+1)),t=new Set;for(let s of n.items)(!s.id||t.has(s.id))&&(s.id=e++),t.add(s.id);return n.meta.nextId=e,n}function Eu(n){ho(n);let e=n.meta,t=As({name:e.name||"List",repo:e.repo||null,repo_key:e.repoKey||null,source_session:e.sourceSession||null,source_machine:e.sourceMachine||null,source_title:e.sourceTitle||null,created:e.created||null,updated:e.updated||null,archived:!!e.archived,next_id:e.nextId,schema:1},ck),s=n.segs&&n.segs.length?n.segs.slice():[{title:!0},{line:""}],o=new Set(s.filter(d=>d.item).map(d=>d.item)),r=n.items.filter(d=>!o.has(d)),i=new Set(n.items),a=-1;s.forEach((d,f)=>{d.item&&(a=f)});let l=a>=0?a+1:s.length;s.splice(l,0,...r.map(d=>({item:d}))),s.some(d=>d.title)||s.unshift({title:!0},{line:""});let c=d=>String(d??"").replace(/\s*\n\s*/g," ").replace(/<!--/g,"<!-").trim(),u=[];for(let d of s)d.title?u.push("# "+c(e.name||"List")):d.item?i.has(d.item)&&u.push("- ["+(d.item.done?"x":" ")+"] "+c(d.item.text)+" <!-- #"+d.item.id+(d.item.at?" "+d.item.at:"")+" -->"):u.push(d.line);return t+u.join(`
32
+ `)){let l=a.match(gk);if(l&&!l[1]){let c={id:l[4]?Number(l[4]):null,text:l[3].trim(),done:l[2]!==" ",at:l[5]?G(l[5]):null};o.push(c),r.push({item:c});continue}if(!i&&/^#\s+/.test(a)){i=!0,s.name||(s.name=a.replace(/^#\s+/,"").trim()),r.push({title:!0});continue}r.push({line:a})}return{meta:s,items:o,segs:r}}function mo(n){let e=Math.max(n.meta.nextId||1,...n.items.map(s=>(s.id||0)+1)),t=new Set;for(let s of n.items)(!s.id||t.has(s.id))&&(s.id=e++),t.add(s.id);return n.meta.nextId=e,n}function Cu(n){mo(n);let e=n.meta,t=Es({name:e.name||"List",repo:e.repo||null,repo_key:e.repoKey||null,source_session:e.sourceSession||null,source_machine:e.sourceMachine||null,source_title:e.sourceTitle||null,created:e.created||null,updated:e.updated||null,archived:!!e.archived,next_id:e.nextId,schema:1},mk),s=n.segs&&n.segs.length?n.segs.slice():[{title:!0},{line:""}],o=new Set(s.filter(d=>d.item).map(d=>d.item)),r=n.items.filter(d=>!o.has(d)),i=new Set(n.items),a=-1;s.forEach((d,f)=>{d.item&&(a=f)});let l=a>=0?a+1:s.length;s.splice(l,0,...r.map(d=>({item:d}))),s.some(d=>d.title)||s.unshift({title:!0},{line:""});let c=d=>String(d??"").replace(/\s*\n\s*/g," ").replace(/<!--/g,"<!-").trim(),u=[];for(let d of s)d.title?u.push("# "+c(e.name||"List")):d.item?i.has(d.item)&&u.push("- ["+(d.item.done?"x":" ")+"] "+c(d.item.text)+" <!-- #"+d.item.id+(d.item.at?" "+d.item.at:"")+" -->"):u.push(d.line);return t+u.join(`
33
33
  `).replace(/\n+$/,"")+`
34
- `}function dp({name:n,items:e=[],repo:t=null,repoKey:s=null,sourceSession:o=null,sourceMachine:r=null,sourceTitle:i=null,now:a=new Date().toISOString()}){let l={meta:{name:xs(n)||"List",repo:t,repoKey:s,sourceSession:o,sourceMachine:r,sourceTitle:i,created:G(a),updated:G(a),archived:!1,nextId:0},items:[],segs:null};for(let c of e)c&&String(c.text||"").trim()&&l.items.push({id:null,text:ra(c.text),done:!!c.done,at:c.done?G(a):null});return ho(l)}function fp(n,e){let t=(R(e.meta.updated)||0)>(R(n.meta.updated)||0)?e:n,s=t===n?e:n,o=c=>String(c||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim(),r={meta:{...s.meta,...t.meta,nextId:Math.max(n.meta.nextId||0,e.meta.nextId||0)},items:[],segs:t.segs},i=new Map,a=c=>{let u=c.id?i.get(c.id):null;if(u&&o(u.text)!==o(c.text)&&!dk(u.text,c.text)&&(u=null),u||(u=r.items.find(d=>o(d.text)===o(c.text))),!u){let d={...c};r.items.push(d),d.id&&!i.has(d.id)?i.set(d.id,d):d.id&&(d.id=null);return}(R(c.at)||0)>(R(u.at)||0)&&Object.assign(u,{text:c.text,done:c.done,at:c.at})};for(let c of t.items)a(c);for(let c of s.items)a(c);r.segs=(t.segs||[]).map(c=>c.item?{item:r.items.find(u=>u.id===c.item.id&&u.id)||r.items.find(u=>o(u.text)===o(c.item.text))||c.item}:c);let l=new Set;return r.segs=r.segs.filter(c=>!c.item||!l.has(c.item)&&l.add(c.item)),ho(r)}function dk(n,e){let t=new Set(String(n).toLowerCase().split(/[^a-z0-9]+/).filter(Boolean)),s=[...new Set(String(e).toLowerCase().split(/[^a-z0-9]+/).filter(Boolean))];return!t.size||!s.length?!1:s.filter(o=>t.has(o)).length/Math.max(t.size,s.length)>.5}function hp(n){let e=n.items.length,t=n.items.filter(s=>s.done).length;return{done:t,total:e,label:t+"/"+e}}var ke,mu,Rh,IS,Fh,Uh,LS,Hh,PS,Bh,jS,HS,BS,GS,KS,$S,wu,Zh,Nh,oa,tp,Tr,zT,XS,cu,Mh,ZS,np,WT,QS,Lh,ek,tk,Sr,ip,Dh,sk,ok,rk,ik,ak,pu,ck,uk,Te=J(()=>{ke={port:7429,dormantAfterMin:15,minGapMin:30,staleAfterSec:180,longRunCheckpointMin:null,digest:{time:"07:00",lookbackHours:24,topOfMind:!0,days:["sun","mon","tue","wed","thu","fri","sat"],skipEmpty:!0,timeZone:null},summarizer:{provider:"auto",model:"haiku",baseUrl:null,apiKeyEnv:null,maxInputChars:24e3},machine:null,machineNames:{},redact:{patterns:["(?i)api[_-]?key\\s*[:=]\\s*\\S+","sk-[A-Za-z0-9_-]{20,}"]},ignoreRepos:[],updates:{check:!0,source:null},remoteHosts:[],remoteRequireToken:!0,tabTitle:!0,tabTitleFormat:"{name}",liveCheckSec:30,notify:{needsYou:!0,delaySec:10},remote:{allowAnswers:!1},summary:{length:"informative"}},mu=["dormant","manual","session_end","clear","long_run"],Rh=["session.opened","session.started","activity","turn_started","turn_completed","notification","title_changed","heartbeat","session.ended"],IS=["permission_prompt","elicitation_dialog","agent_needs_input"];Fh=n=>aa(n,fu,!0),Uh=n=>aa(n,jh,!0),LS=/^(null|~|true|false|yes|no|on|off|y|n)$/i,Hh=/^[-+]?(\d[\d_]*)?(\.\d+)?([eE][-+]?\d+)?$/;PS=["id","session_id","session_title","created_at","trigger","machine","repo","branch","cwd","files_touched","turns_since_last","user_note","open_question","parse_ok","length","generator","schema"];Bh="11434",jS=new Set(["127.0.0.1","localhost","[::1]","::1","0.0.0.0"]);HS={doing:"doing","stopped at":"stoppedAt",next:"next","open question":"openQuestion",note:"userNote"};BS=["id","title","title_source","machine","cwd","repo","transcript_path","model","surface","capture","status","started_at","last_activity_at","last_heartbeat_at","ended_at","end_reason","turns","last_checkpoint_id","last_checkpoint_at","parent_session_id","pinned","archived","ignored","schema"],GS=["inTurn","needsInput","backgroundTasks","turnsSinceCheckpoint","dormantStretch","finalCheckpointDone","firstPrompt","lastPreview","lastNotification","lastEventAt","statusChanges","folder","pid","tty","pidStartedAt","pidSource","term","closedAt","answers","lastNotificationMessage","needsInputAt","question","questionAt","startSource","historyTurns","healedAt","healedVia","archivedReason","backend","prLinks","prUnlinked","todos","files"],KS=["id","title","titleSource","machine","cwd","repo","transcriptPath","model","surface","capture","status","startedAt","lastActivityAt","lastHeartbeatAt","endedAt","endReason","turns","lastCheckpointId","lastCheckpointAt","parentSessionId","pinned","archived","ignored"];$S=/\s*(?:·|-|\|)?\s*cp:([a-z0-9]{6})\s*$/i;wu="{name} · cp:{id}";Zh=["TERM_PROGRAM","TERM_PROGRAM_VERSION","TERM","GHOSTTY_RESOURCES_DIR","ITERM_SESSION_ID","WEZTERM_PANE","WEZTERM_UNIX_SOCKET","KITTY_WINDOW_ID","KITTY_LISTEN_ON","TMUX","TMUX_PANE","TERM_SESSION_ID"],Nh={ghostty:"Ghostty",iterm2:"iTerm2",wezterm:"WezTerm",kitty:"kitty","apple-terminal":"Terminal.app",tmux:"tmux",vscode:"VS Code",unknown:"unknown terminal"};oa=6e4;tp=["Edit","Write","MultiEdit","NotebookEdit"];Tr={short:{label:"Short",maxTokens:300,approx:"~300 tokens",blurb:"Doing / Stopped at / Next. Three lines, nothing else."},informative:{label:"Informative",maxTokens:700,approx:"~700 tokens",blurb:"The three lines, plus key decisions, open questions, files touched and why, and what didn't work."},long:{label:"Long",maxTokens:1500,approx:"~1500 tokens",blurb:"Everything in Informative, plus a short narrative recap and the next 3 steps."}},zT=Object.keys(Tr),XS="informative";cu=["You write checkpoints for a developer juggling many coding-agent sessions. Given the recent transcript of ONE session, start with exactly these three lines:","Doing: <what the session was working on, one sentence, concrete nouns: files, features, errors>","Stopped at: <the exact state when it went quiet: what passed, what failed, what is half-done>","Next: <the single next action a human or the agent should take>",'If the session is blocked on the human (a question, a permission, a decision), start Next with "YOU:".'],Mh=['Then these sections, each a heading line followed by "- " bullets (write "- none" when there is nothing; never invent):',"Decisions:","- <a decision made in the session and the reason, one line each, at most 4>","Open questions:","- <an unresolved question or uncertainty, at most 3>","Files:","- <path> — <why it was touched, a few words; at most 6 files>","Didn't work:","- <an approach that was tried and abandoned or failed, and why; at most 3>"],ZS=["Then:","Recap: <a narrative paragraph of 4-7 sentences: the goal, how the session got here, the current state>","Next steps:","1. <the next step>","2. <the step after>","3. <the step after that>"],np={short:[...cu,"Write only those three lines.",'Optionally a 4th line "Title: <max 6 words>" if asked for a title.',"No preamble. No markdown. Under 60 words total."].join(`
35
- `),informative:[...cu,...Mh,'Optionally a final line "Title: <max 6 words>" if asked for a title.',"No preamble. No other markdown. Under 250 words total."].join(`
36
- `),long:[...cu,...Mh,...ZS,'Optionally a final line "Title: <max 6 words>" if asked for a title.',"No preamble. No other markdown. Under 600 words total."].join(`
37
- `)},WT=np.short;QS={decisions:"decisions","key decisions":"decisions","open questions":"questions",questions:"questions",files:"files","files touched":"files","didn't work":"didntWork","didnt work":"didntWork","did not work":"didntWork","what didn't work":"didntWork","what didnt work":"didntWork",tried:"didntWork",recap:"recap","next steps":"nextSteps","next 3 steps":"nextSteps"},Lh={decisions:"Key decisions",questions:"Open questions",files:"Files touched",didntWork:"What didn't work",recap:"Recap",nextSteps:"Next steps"},ek=["decisions","questions","files","didntWork","recap","nextSteps"],tk=/^(none|n\/a|nothing|-|—|none yet|no(ne)? (open )?questions?)\.?$/i,Sr={decisions:6,questions:5,files:10,didntWork:5,nextSteps:5};ip="Write a title of at most 6 words for a coding session, based on its first prompt. Reply with the title only, no quotes, no punctuation at the end.";Dh=/^\s*(```|~~~)/,sk=/^(\s*)(?:[-*+]|\d{1,3}[.)])\s+\[( |x|X)\]\s+(.*\S)\s*$/,ok=/^(\s*)(\d{1,3})[.)]\s+(.*\S)\s*$/,rk=/^(\s*)[-*+•]\s+(.*\S)\s*$/,ik=/^\s*(?:#{1,6}\s+(.+?)\s*#*|\*\*(.+?)\*\*\s*:?|__(.+?)__\s*:?|(\S[^\n]{0,100}?)\s*:)\s*$/,ak=/\b(need to|you'?ll have to|make sure|in order to|do next|to do|how to|instructions|checklist|next(?: steps?)?|steps?|to-?dos?|tasks?|plan|remaining|follow[- ]?ups?|action items?|check-?list|left to do|what'?s left|still to do|open items?|road ?map|phases?|milestones?|punch ?list|before (?:we|you|i) (?:ship|merge|launch))\b/i;pu=n=>String(n||"").toLowerCase().replace(/[`*_~]/g,"").replace(/[^a-z0-9]+/g," ").trim();ck=["name","repo","repo_key","source_session","source_machine","source_title","created","updated","archived","next_id","schema"],uk=/^(\s*)[-*+]\s+\[( |x|X)\]\s+(.*?)\s*(?:<!--\s*#(\d+)(?:\s+(\S+))?\s*-->)?\s*$/});import ue from"node:fs";import ya from"node:os";import _e from"node:path";import fk from"node:crypto";function wa(){return _e.join(ya.homedir(),"checkpoints")}function pp(){return _e.join(process.env.XDG_CONFIG_HOME||_e.join(ya.homedir(),".config"),"checkpoints","folder")}function hk(){try{return ue.readFileSync(pp(),"utf8").trim()||null}catch{return null}}function mp(n){let e=pp();if(_e.resolve(n)===wa()){try{ue.unlinkSync(e)}catch{}return null}return ue.mkdirSync(_e.dirname(e),{recursive:!0}),ue.writeFileSync(e,_e.resolve(n)+`
38
- `),e}function Z(){return process.env.LEFTOFF_DIR||process.env.CHECKPOINTS_DIR||hk()||wa()}function de(n){return ua({env:process.env.CHECKPOINTS_MACHINE,hostname:ya.hostname(),cfg:n||{}})}function be(n=Z()){return{root:n,config:_e.join(n,"config.json"),readme:_e.join(n,"README.md"),sessions:_e.join(n,"sessions"),digests:_e.join(n,"digests"),catchups:_e.join(n,"catchups"),spool:_e.join(n,".spool"),secret:_e.join(n,".secret"),token:_e.join(n,".secret","token")}}function Oe(n=Z()){let e=be(n);for(let t of[e.root,e.sessions,e.digests,e.spool])ue.mkdirSync(t,{recursive:!0});ue.mkdirSync(e.secret,{recursive:!0,mode:448}),ue.existsSync(e.readme)||ue.writeFileSync(e.readme,pk),ue.existsSync(e.config)||ue.writeFileSync(e.config,JSON.stringify(ke,null,2)+`
39
- `),ue.existsSync(e.token)||ue.writeFileSync(e.token,fk.randomBytes(32).toString("hex")+`
40
- `,{mode:384});try{ue.chmodSync(e.token,384)}catch{}return e}function Q(n=Z()){let e=be(n),t={};try{t=JSON.parse(ue.readFileSync(e.config,"utf8"))}catch{}t.summarizer&&t.summarizer.mode&&!t.summarizer.provider&&(t.summarizer={...t.summarizer,provider:t.summarizer.mode}),t.tabTitleFormat===wu&&delete t.tabTitleFormat;let s=co(ke,t);s.summarizer&&delete s.summarizer.mode;let o=process.env.LEFTOFF_PORT||process.env.CHECKPOINTS_PORT;return o&&(s.port=Number(o)),process.env.CHECKPOINTS_SUMMARIZER&&(s=co(s,{summarizer:{provider:process.env.CHECKPOINTS_SUMMARIZER}})),s}function po(n,e=Z()){let t=be(e),s=t.config+".tmp-"+process.pid;ue.writeFileSync(s,JSON.stringify(n,null,2)+`
41
- `),ue.renameSync(s,t.config)}function ve(n=Z()){try{return ue.readFileSync(be(n).token,"utf8").trim()}catch{return null}}function yn(n=Z()){try{return JSON.parse(ue.readFileSync(be(n).config,"utf8"))}catch{return null}}function en(n=process.env){return _e.join(n.XDG_CONFIG_HOME||_e.join(n.HOME||ya.homedir(),".config"),"checkpoints","secrets")}function gp(n,e=process.env){let t=String(n||"").match(/^\.secret\/([^/]+)$/);return t?_e.join(en(e),t[1]):null}function Sa(n,e,t=Z(),s=process.env){let o=en(s);ue.mkdirSync(o,{recursive:!0,mode:448});try{ue.chmodSync(o,448)}catch{}let r=_e.join(o,n);ue.writeFileSync(r,String(e).trim()+`
42
- `,{mode:384});try{ue.chmodSync(r,384)}catch{}try{ue.unlinkSync(_e.join(be(t).secret,n))}catch{}return _e.join(".secret",n)}function yp(n=Z(),e=process.env){let t=[],s=[];try{s=ue.readdirSync(be(n).secret)}catch{return t}for(let o of s){if(!/-api-key$/.test(o))continue;let r=_e.join(be(n).secret,o),i=_e.join(en(e),o);try{if(!ue.existsSync(i)&&(ue.mkdirSync(_e.dirname(i),{recursive:!0,mode:448}),ue.copyFileSync(r,i),ue.chmodSync(i,384),ue.readFileSync(i,"utf8")!==ue.readFileSync(r,"utf8")))throw new Error("copy differs");ue.unlinkSync(r),t.push(o)}catch{}}return t}var X,pk,xe=J(()=>{Te();X=(()=>{try{return JSON.parse(ue.readFileSync(new URL("../package.json",import.meta.url),"utf8")).version}catch{return"0.0.0"}})();pk=`# Leftoff
34
+ `}function mp({name:n,items:e=[],repo:t=null,repoKey:s=null,sourceSession:o=null,sourceMachine:r=null,sourceTitle:i=null,now:a=new Date().toISOString()}){let l={meta:{name:_s(n)||"List",repo:t,repoKey:s,sourceSession:o,sourceMachine:r,sourceTitle:i,created:G(a),updated:G(a),archived:!1,nextId:0},items:[],segs:null};for(let c of e)c&&String(c.text||"").trim()&&l.items.push({id:null,text:la(c.text),done:!!c.done,at:c.done?G(a):null});return mo(l)}function gp(n,e){let t=(R(e.meta.updated)||0)>(R(n.meta.updated)||0)?e:n,s=t===n?e:n,o=c=>String(c||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim(),r={meta:{...s.meta,...t.meta,nextId:Math.max(n.meta.nextId||0,e.meta.nextId||0)},items:[],segs:t.segs},i=new Map,a=c=>{let u=c.id?i.get(c.id):null;if(u&&o(u.text)!==o(c.text)&&!yk(u.text,c.text)&&(u=null),u||(u=r.items.find(d=>o(d.text)===o(c.text))),!u){let d={...c};r.items.push(d),d.id&&!i.has(d.id)?i.set(d.id,d):d.id&&(d.id=null);return}(R(c.at)||0)>(R(u.at)||0)&&Object.assign(u,{text:c.text,done:c.done,at:c.at})};for(let c of t.items)a(c);for(let c of s.items)a(c);r.segs=(t.segs||[]).map(c=>c.item?{item:r.items.find(u=>u.id===c.item.id&&u.id)||r.items.find(u=>o(u.text)===o(c.item.text))||c.item}:c);let l=new Set;return r.segs=r.segs.filter(c=>!c.item||!l.has(c.item)&&l.add(c.item)),mo(r)}function yk(n,e){let t=new Set(String(n).toLowerCase().split(/[^a-z0-9]+/).filter(Boolean)),s=[...new Set(String(e).toLowerCase().split(/[^a-z0-9]+/).filter(Boolean))];return!t.size||!s.length?!1:s.filter(o=>t.has(o)).length/Math.max(t.size,s.length)>.5}function yp(n){let e=n.items.length,t=n.items.filter(s=>s.done).length;return{done:t,total:e,label:t+"/"+e}}var ke,yu,Uh,PS,Gh,Kh,US,$h,GS,zh,KS,YS,WS,JS,qS,VS,ku,np,Rh,aa,rp,_r,QT,sk,du,Ph,ok,ip,e_,rk,jh,ik,ak,Sr,up,Fh,ck,uk,dk,fk,hk,gu,mk,gk,Te=J(()=>{ke={port:7429,dormantAfterMin:15,minGapMin:30,staleAfterSec:180,longRunCheckpointMin:null,digest:{time:"07:00",lookbackHours:24,topOfMind:!0,days:["sun","mon","tue","wed","thu","fri","sat"],skipEmpty:!0,timeZone:null},summarizer:{provider:"auto",model:"haiku",baseUrl:null,apiKeyEnv:null,maxInputChars:24e3},machine:null,machineNames:{},redact:{patterns:["(?i)api[_-]?key\\s*[:=]\\s*\\S+","sk-[A-Za-z0-9_-]{20,}"]},ignoreRepos:[],updates:{check:!0,source:null},remoteHosts:[],remoteRequireToken:!0,tabTitle:!0,tabTitleFormat:"{name}",liveCheckSec:30,notify:{needsYou:!0,delaySec:10},remote:{allowAnswers:!1},summary:{length:"informative"}},yu=["dormant","manual","session_end","clear","long_run"],Uh=["session.opened","session.started","activity","turn_started","turn_completed","notification","title_changed","heartbeat","session.ended"],PS=["permission_prompt","elicitation_dialog","agent_needs_input"];Gh=n=>ua(n,pu,!0),Kh=n=>ua(n,Bh,!0),US=/^(null|~|true|false|yes|no|on|off|y|n)$/i,$h=/^[-+]?(\d[\d_]*)?(\.\d+)?([eE][-+]?\d+)?$/;GS=["id","session_id","session_title","created_at","trigger","machine","repo","branch","cwd","files_touched","turns_since_last","user_note","open_question","parse_ok","length","generator","schema"];zh="11434",KS=new Set(["127.0.0.1","localhost","[::1]","::1","0.0.0.0"]);YS={doing:"doing","stopped at":"stoppedAt",next:"next","open question":"openQuestion",note:"userNote"};WS=["id","title","title_source","machine","cwd","repo","transcript_path","model","surface","capture","status","started_at","last_activity_at","last_heartbeat_at","ended_at","end_reason","turns","last_checkpoint_id","last_checkpoint_at","parent_session_id","pinned","archived","ignored","schema"],JS=["inTurn","needsInput","backgroundTasks","turnsSinceCheckpoint","dormantStretch","finalCheckpointDone","firstPrompt","lastPreview","lastNotification","lastEventAt","statusChanges","folder","pid","tty","pidStartedAt","pidSource","term","closedAt","answers","lastNotificationMessage","needsInputAt","question","questionAt","startSource","historyTurns","healedAt","healedVia","archivedReason","backend","prLinks","prUnlinked","todos","files","startRepo","editedRepos"],qS=["id","title","titleSource","machine","cwd","repo","transcriptPath","model","surface","capture","status","startedAt","lastActivityAt","lastHeartbeatAt","endedAt","endReason","turns","lastCheckpointId","lastCheckpointAt","parentSessionId","pinned","archived","ignored"];VS=/\s*(?:·|-|\|)?\s*cp:([a-z0-9]{6})\s*$/i;ku="{name} · cp:{id}";np=["TERM_PROGRAM","TERM_PROGRAM_VERSION","TERM","GHOSTTY_RESOURCES_DIR","ITERM_SESSION_ID","WEZTERM_PANE","WEZTERM_UNIX_SOCKET","KITTY_WINDOW_ID","KITTY_LISTEN_ON","TMUX","TMUX_PANE","TERM_SESSION_ID"],Rh={ghostty:"Ghostty",iterm2:"iTerm2",wezterm:"WezTerm",kitty:"kitty","apple-terminal":"Terminal.app",tmux:"tmux",vscode:"VS Code",unknown:"unknown terminal"};aa=6e4;rp=["Edit","Write","MultiEdit","NotebookEdit"];_r={short:{label:"Short",maxTokens:300,approx:"~300 tokens",blurb:"Doing / Stopped at / Next. Three lines, nothing else."},informative:{label:"Informative",maxTokens:700,approx:"~700 tokens",blurb:"The three lines, plus key decisions, open questions, files touched and why, and what didn't work."},long:{label:"Long",maxTokens:1500,approx:"~1500 tokens",blurb:"Everything in Informative, plus a short narrative recap and the next 3 steps."}},QT=Object.keys(_r),sk="informative";du=["You write checkpoints for a developer juggling many coding-agent sessions. Given the recent transcript of ONE session, start with exactly these three lines:","Doing: <what the session was working on, one sentence, concrete nouns: files, features, errors>","Stopped at: <the exact state when it went quiet: what passed, what failed, what is half-done>","Next: <the single next action a human or the agent should take>",'If the session is blocked on the human (a question, a permission, a decision), start Next with "YOU:".'],Ph=['Then these sections, each a heading line followed by "- " bullets (write "- none" when there is nothing; never invent):',"Decisions:","- <a decision made in the session and the reason, one line each, at most 4>","Open questions:","- <an unresolved question or uncertainty, at most 3>","Files:","- <path> — <why it was touched, a few words; at most 6 files>","Didn't work:","- <an approach that was tried and abandoned or failed, and why; at most 3>"],ok=["Then:","Recap: <a narrative paragraph of 4-7 sentences: the goal, how the session got here, the current state>","Next steps:","1. <the next step>","2. <the step after>","3. <the step after that>"],ip={short:[...du,"Write only those three lines.",'Optionally a 4th line "Title: <max 6 words>" if asked for a title.',"No preamble. No markdown. Under 60 words total."].join(`
35
+ `),informative:[...du,...Ph,'Optionally a final line "Title: <max 6 words>" if asked for a title.',"No preamble. No other markdown. Under 250 words total."].join(`
36
+ `),long:[...du,...Ph,...ok,'Optionally a final line "Title: <max 6 words>" if asked for a title.',"No preamble. No other markdown. Under 600 words total."].join(`
37
+ `)},e_=ip.short;rk={decisions:"decisions","key decisions":"decisions","open questions":"questions",questions:"questions",files:"files","files touched":"files","didn't work":"didntWork","didnt work":"didntWork","did not work":"didntWork","what didn't work":"didntWork","what didnt work":"didntWork",tried:"didntWork",recap:"recap","next steps":"nextSteps","next 3 steps":"nextSteps"},jh={decisions:"Key decisions",questions:"Open questions",files:"Files touched",didntWork:"What didn't work",recap:"Recap",nextSteps:"Next steps"},ik=["decisions","questions","files","didntWork","recap","nextSteps"],ak=/^(none|n\/a|nothing|-|—|none yet|no(ne)? (open )?questions?)\.?$/i,Sr={decisions:6,questions:5,files:10,didntWork:5,nextSteps:5};up="Write a title of at most 6 words for a coding session, based on its first prompt. Reply with the title only, no quotes, no punctuation at the end.";Fh=/^\s*(```|~~~)/,ck=/^(\s*)(?:[-*+]|\d{1,3}[.)])\s+\[( |x|X)\]\s+(.*\S)\s*$/,uk=/^(\s*)(\d{1,3})[.)]\s+(.*\S)\s*$/,dk=/^(\s*)[-*+•]\s+(.*\S)\s*$/,fk=/^\s*(?:#{1,6}\s+(.+?)\s*#*|\*\*(.+?)\*\*\s*:?|__(.+?)__\s*:?|(\S[^\n]{0,100}?)\s*:)\s*$/,hk=/\b(need to|you'?ll have to|make sure|in order to|do next|to do|how to|instructions|checklist|next(?: steps?)?|steps?|to-?dos?|tasks?|plan|remaining|follow[- ]?ups?|action items?|check-?list|left to do|what'?s left|still to do|open items?|road ?map|phases?|milestones?|punch ?list|before (?:we|you|i) (?:ship|merge|launch))\b/i;gu=n=>String(n||"").toLowerCase().replace(/[`*_~]/g,"").replace(/[^a-z0-9]+/g," ").trim();mk=["name","repo","repo_key","source_session","source_machine","source_title","created","updated","archived","next_id","schema"],gk=/^(\s*)[-*+]\s+\[( |x|X)\]\s+(.*?)\s*(?:<!--\s*#(\d+)(?:\s+(\S+))?\s*-->)?\s*$/});import ue from"node:fs";import ka from"node:os";import _e from"node:path";import wk from"node:crypto";function ba(){return _e.join(ka.homedir(),"checkpoints")}function wp(){return _e.join(process.env.XDG_CONFIG_HOME||_e.join(ka.homedir(),".config"),"checkpoints","folder")}function Sk(){try{return ue.readFileSync(wp(),"utf8").trim()||null}catch{return null}}function Sp(n){let e=wp();if(_e.resolve(n)===ba()){try{ue.unlinkSync(e)}catch{}return null}return ue.mkdirSync(_e.dirname(e),{recursive:!0}),ue.writeFileSync(e,_e.resolve(n)+`
38
+ `),e}function Z(){return process.env.LEFTOFF_DIR||process.env.CHECKPOINTS_DIR||Sk()||ba()}function de(n){return ha({env:process.env.CHECKPOINTS_MACHINE,hostname:ka.hostname(),cfg:n||{}})}function be(n=Z()){return{root:n,config:_e.join(n,"config.json"),readme:_e.join(n,"README.md"),sessions:_e.join(n,"sessions"),digests:_e.join(n,"digests"),catchups:_e.join(n,"catchups"),spool:_e.join(n,".spool"),secret:_e.join(n,".secret"),token:_e.join(n,".secret","token")}}function Oe(n=Z()){let e=be(n);for(let t of[e.root,e.sessions,e.digests,e.spool])ue.mkdirSync(t,{recursive:!0});ue.mkdirSync(e.secret,{recursive:!0,mode:448}),ue.existsSync(e.readme)||ue.writeFileSync(e.readme,kk),ue.existsSync(e.config)||ue.writeFileSync(e.config,JSON.stringify(ke,null,2)+`
39
+ `),ue.existsSync(e.token)||ue.writeFileSync(e.token,wk.randomBytes(32).toString("hex")+`
40
+ `,{mode:384});try{ue.chmodSync(e.token,384)}catch{}return e}function Q(n=Z()){let e=be(n),t={};try{t=JSON.parse(ue.readFileSync(e.config,"utf8"))}catch{}t.summarizer&&t.summarizer.mode&&!t.summarizer.provider&&(t.summarizer={...t.summarizer,provider:t.summarizer.mode}),t.tabTitleFormat===ku&&delete t.tabTitleFormat;let s=fo(ke,t);s.summarizer&&delete s.summarizer.mode;let o=process.env.LEFTOFF_PORT||process.env.CHECKPOINTS_PORT;return o&&(s.port=Number(o)),process.env.CHECKPOINTS_SUMMARIZER&&(s=fo(s,{summarizer:{provider:process.env.CHECKPOINTS_SUMMARIZER}})),s}function go(n,e=Z()){let t=be(e),s=t.config+".tmp-"+process.pid;ue.writeFileSync(s,JSON.stringify(n,null,2)+`
41
+ `),ue.renameSync(s,t.config)}function ve(n=Z()){try{return ue.readFileSync(be(n).token,"utf8").trim()}catch{return null}}function Sn(n=Z()){try{return JSON.parse(ue.readFileSync(be(n).config,"utf8"))}catch{return null}}function nn(n=process.env){return _e.join(n.XDG_CONFIG_HOME||_e.join(n.HOME||ka.homedir(),".config"),"checkpoints","secrets")}function kp(n,e=process.env){let t=String(n||"").match(/^\.secret\/([^/]+)$/);return t?_e.join(nn(e),t[1]):null}function va(n,e,t=Z(),s=process.env){let o=nn(s);ue.mkdirSync(o,{recursive:!0,mode:448});try{ue.chmodSync(o,448)}catch{}let r=_e.join(o,n);ue.writeFileSync(r,String(e).trim()+`
42
+ `,{mode:384});try{ue.chmodSync(r,384)}catch{}try{ue.unlinkSync(_e.join(be(t).secret,n))}catch{}return _e.join(".secret",n)}function bp(n=Z(),e=process.env){let t=[],s=[];try{s=ue.readdirSync(be(n).secret)}catch{return t}for(let o of s){if(!/-api-key$/.test(o))continue;let r=_e.join(be(n).secret,o),i=_e.join(nn(e),o);try{if(!ue.existsSync(i)&&(ue.mkdirSync(_e.dirname(i),{recursive:!0,mode:448}),ue.copyFileSync(r,i),ue.chmodSync(i,384),ue.readFileSync(i,"utf8")!==ue.readFileSync(r,"utf8")))throw new Error("copy differs");ue.unlinkSync(r),t.push(o)}catch{}}return t}var X,kk,xe=J(()=>{Te();X=(()=>{try{return JSON.parse(ue.readFileSync(new URL("../package.json",import.meta.url),"utf8")).version}catch{return"0.0.0"}})();kk=`# Leftoff
43
43
 
44
44
  This folder is written by the Leftoff daemon and the Claude Code mod.
45
45
  It is the source of truth: plain markdown with YAML frontmatter, safe to read,
@@ -56,90 +56,90 @@ grep and sync. The daemon rebuilds its index from here at start.
56
56
  - .spool/: events written while the daemon was down (drained at start)
57
57
  - .secret/token: the local API token (mode 0600). Don't share it.
58
58
  (API keys live outside the folder, in ~/.config/checkpoints/secrets/, since 0.5.3.)
59
- `});var Pp={};Qt(Pp,{BREW_URL:()=>xp,CODE_PREFIX:()=>Er,DOWNLOAD_URL:()=>vp,FOLDER_ID:()=>Ee,FOLDER_LABEL:()=>kp,INSTALL_COMMANDS:()=>Iu,INSTALL_LINE:()=>Or,PAIR_TTL_MS:()=>bp,PROOF_RE:()=>Ru,PairCodeError:()=>wn,STIGNORE_LINES:()=>va,SyncthingClient:()=>ba,SyncthingError:()=>nt,acceptKnocked:()=>jr,armPairing:()=>Dr,autoAcceptTick:()=>Rr,brewPath:()=>Aa,cancelPairing:()=>ju,codeDigest:()=>Du,connect:()=>ht,decodePairCode:()=>Lu,encodePairCode:()=>Cu,ensureDevice:()=>_a,ensureFolder:()=>Ta,explainBrewFailure:()=>Ip,findSyncthingConfig:()=>Op,guiUrl:()=>Ep,installPlan:()=>_s,knockedDevices:()=>Pr,localDir:()=>go,nameWithoutProof:()=>yo,nestedIn:()=>Os,pairProof:()=>Ea,parseSyncthingConfig:()=>_p,peerAccepted:()=>Oa,progressLine:()=>Uu,proofName:()=>Pu,publicPairing:()=>$n,readManaged:()=>bn,readPairing:()=>Pt,readRunStatus:()=>Mu,runInstall:()=>Ir,sameDevice:()=>Ce,shortId:()=>Gn,startSyncthing:()=>Nr,statusFile:()=>Nu,stopSyncing:()=>Lr,stuckSteps:()=>Fu,suggestPath:()=>Mr,syncthingConfigCandidates:()=>Tp,syncthingInstall:()=>kn,syncthingStatus:()=>tn,tilde:()=>Ne,untilde:()=>xa,waitForSyncthing:()=>Es,writeManaged:()=>Kn,writeRunStatus:()=>So,writeStignore:()=>Mp});import Pe from"node:fs";import mk from"node:os";import ce from"node:path";import gk from"node:http";import yk from"node:https";import wo from"node:crypto";import{spawn as Sp,spawnSync as wp}from"node:child_process";function go(n=process.env){return ce.join(n.XDG_CONFIG_HOME||ce.join(vt(n),".config"),"checkpoints")}function Cr(n){try{return JSON.parse(Pe.readFileSync(n,"utf8"))}catch{return null}}function Ts(n,e){Pe.mkdirSync(ce.dirname(n),{recursive:!0,mode:448});let t=n+".tmp-"+process.pid;Pe.writeFileSync(t,JSON.stringify(e,null,2)+`
60
- `,{mode:384}),Pe.renameSync(t,n);try{Pe.chmodSync(n,384)}catch{}}function Tp({env:n=process.env,platform:e=process.platform}={}){let t=vt(n),s=[];for(let i of[n.LEFTOFF_SYNCTHING_HOME,n.STHOMEDIR])i&&s.push(ce.join(i,"config.xml"));let o=ce.join(t,"Library","Application Support","Syncthing","config.xml"),r=[ce.join(n.XDG_STATE_HOME||ce.join(t,".local","state"),"syncthing","config.xml"),ce.join(n.XDG_CONFIG_HOME||ce.join(t,".config"),"syncthing","config.xml")];return e==="darwin"?s.push(o,...r):s.push(...r,o),[...new Set(s)]}function _p(n,{home:e=vt()}={}){let t={gui:null,folders:[],devices:[]},s=String(n).match(/<gui\b([^>]*)>([\s\S]*?)<\/gui>/);if(s){let o=((s[2].match(/<address>([^<]*)<\/address>/)||[])[1]||"").trim(),r=((s[2].match(/<apikey>([^<]*)<\/apikey>/)||[])[1]||"").trim(),i=/\btls="true"/.test(s[1]);t.gui={address:ka(o),tls:i,apiKey:ka(r)||null,url:Ep(ka(o),i)}}for(let o of String(n).matchAll(/<folder\b([^>]*?)(\/>|>([\s\S]*?)<\/folder>)/g)){let r=o[1],i=mo(r,"path");if(!i)continue;i=ce.resolve(i.replace(/^~(?=\/|$)/,e));let a=[...String(o[3]||"").matchAll(/<device\b[^>]*\bid="([^"]+)"/g)].map(l=>l[1]);t.folders.push({id:mo(r,"id")||"",label:mo(r,"label")||"",path:i,type:mo(r,"type")||"sendreceive",devices:a})}for(let o of String(n).matchAll(/<device\b([^>]*\bname="[^"]*"[^>]*)>/g))t.devices.push({id:mo(o[1],"id"),name:mo(o[1],"name")});return t}function Ep(n,e=!1){if(!n||/^unix:/.test(n))return null;let s=n.replace(/^https?:\/\//,"").match(/^(\[[^\]]+\]|[^:]*):(\d+)$/);if(!s)return null;let o=s[1];return(o===""||o==="0.0.0.0")&&(o="127.0.0.1"),o==="[::]"&&(o="[::1]"),(e?"https":"http")+"://"+o+":"+s[2]}function Op(n={}){let e=n.env||process.env;for(let t of Tp(n)){let s;try{s=Pe.readFileSync(t,"utf8")}catch{continue}return{file:t,..._p(s,{home:vt(e)})}}return null}function ht(n={}){let e=n.env||process.env,t=Op({env:e,platform:n.platform});if(!t)return{ok:!1,code:"no_config",reason:"Syncthing isn’t set up on this Mac yet"};try{return{ok:!0,cfg:t,client:ba.fromConfig(t,{managedDevices:()=>bn(e).devices,...n.client})}}catch(s){return{ok:!1,cfg:t,code:s.code,reason:s.message}}}function Aa(n=process.env){return n.LEFTOFF_NO_BREW?null:Cp("brew",n)||["/opt/homebrew/bin/brew","/usr/local/bin/brew","/home/linuxbrew/.linuxbrew/bin/brew"].find(e=>{try{return Pe.accessSync(e,Pe.constants.X_OK),!0}catch{return!1}})||null}function kn(n=process.env){let e=Aa(n);if(e){let s=ce.join(ce.dirname(e),"syncthing");if(wk(s))return{installed:!0,via:"brew",path:s,brew:e}}let t=Cp("syncthing",n);if(t)return{installed:!0,via:e&&t.startsWith(ce.dirname(ce.dirname(e)))?"brew":"path",path:t,brew:e};for(let s of["/Applications/Syncthing.app",ce.join(vt(n),"Applications","Syncthing.app")])if(Sk(s))return{installed:!0,via:"app",path:s,brew:e};return{installed:!1,via:null,path:null,brew:e}}function _s(n=process.env){return Aa(n)?{brew:!0,command:Or,steps:Iu.map(t=>["brew",...t].join(" "))}:{brew:!1,directions:["Install Homebrew from brew.sh (one line in Terminal), then come back and press Check again. Leftoff will install Syncthing for you.","Or download Syncthing for macOS from syncthing.net/downloads, open it once, then press Check again."],links:{brew:xp,download:vp}}}function Ip(n,e){let t=n.join(`
61
- `);if(/Permission denied|Operation not permitted/i.test(t))return"Homebrew couldn’t write to its folder (permissions). Run `brew doctor` in Terminal, then try again.";if(/Could not resolve host|Failed to download|curl: \(\d+\)|network/i.test(t))return"Homebrew couldn’t download Syncthing. Check your internet connection and try again.";if(/another active Homebrew|already running|lock/i.test(t)&&/brew/i.test(t))return"Homebrew is busy with another install. Wait for it to finish, then try again.";if(/No available formula|No formulae found/i.test(t))return"Homebrew doesn’t know Syncthing (an old Homebrew?). Run `brew update`, then try again.";if(/xcode-select|Command Line Tools/i.test(t))return"Homebrew needs Apple’s Command Line Tools. Run `xcode-select --install`, then try again.";let s=n.filter(o=>/\S/.test(o)).slice(-1)[0]||"";return"Homebrew stopped (exit "+e+")"+(s?": "+s.replace(/^Error:\s*/,"").slice(0,200):"")+"."}async function Ir({env:n=process.env,onLine:e=()=>{},onStage:t=()=>{}}={}){let s=Aa(n);if(!s)return{ok:!1,code:"no_brew",error:"Homebrew isn’t installed on this Mac."};let o=[],r={...n,HOMEBREW_NO_ENV_HINTS:"1",HOMEBREW_NO_INSTALL_CLEANUP:"1",NONINTERACTIVE:"1"};for(let i of Iu){let a=["brew",...i].join(" ");t(a),e("$ "+a);let l=await new Promise(c=>{let u=Sp(s,i,{env:r,stdio:["ignore","pipe","pipe"]}),d="",f=h=>{d+=h;let p;for(;(p=d.search(/[\r\n]/))>=0;){let m=d.slice(0,p);d=d.slice(p+1),m.trim()&&(o.push(m),o.length>400&&o.shift(),e(m))}};u.stdout.on("data",f),u.stderr.on("data",f),u.on("error",h=>{o.push(h.message),e(h.message),c(127)}),u.on("close",h=>{d.trim()&&(o.push(d),e(d)),c(h??1)})});if(l!==0)return{ok:!1,code:"brew_failed",exit:l,stage:a,error:Ip(o,l),lines:o}}return{ok:!0,lines:o}}function Nr(n=process.env){let e=kn(n);if(!e.installed)return{ok:!1,code:"not_installed",error:"Syncthing isn’t installed"};if(e.via==="brew"){let o=wp(e.brew,["services","start","syncthing"],{env:{...n,HOMEBREW_NO_ENV_HINTS:"1"},encoding:"utf8",timeout:6e4});return o.status===0?{ok:!0,how:"brew services start syncthing"}:{ok:!1,code:"start_failed",error:(o.stderr||o.stdout||"").trim().split(`
62
- `).pop()||"brew services failed"}}if(e.via==="app")return wp("open",["-a",e.path],{encoding:"utf8"}).status===0?{ok:!0,how:"opened Syncthing.app"}:{ok:!1,code:"start_failed",error:"couldn’t open Syncthing.app"};Pe.mkdirSync(go(n),{recursive:!0});let t=Pe.openSync(ce.join(go(n),"syncthing.log"),"a"),s=Sp(e.path,["serve","--no-browser","--no-restart"],{env:n,detached:!0,stdio:["ignore",t,t]});return s.unref(),{ok:!0,how:"syncthing serve (pid "+s.pid+")"}}async function Es({env:n=process.env,timeoutMs:e=9e4,every:t=500}={}){let s=Date.now(),o=null;for(;Date.now()-s<e;){let r=ht({env:n});if(r.ok)try{return await r.client.ping(),{ok:!0,...r}}catch(i){o=i.message}else o=r.reason;await Ap(t)}return{ok:!1,code:"not_running",error:"Syncthing didn’t come up"+(o?" ("+o+")":"")}}function bn(n=process.env){let e=Cr(Np(n))||{};return{devices:e.devices||[],originalName:e.originalName||null,usedCodes:e.usedCodes||[],folderPath:e.folderPath||null}}function Kn(n,e=process.env){Ts(Np(e),{...bn(e),...n})}function kk(n,e){let t=bn(e);t.devices.some(s=>Ce(s,n))||Kn({devices:[...t.devices,n]},e)}function Mu(n=process.env){return Cr(Nu(n))}function So(n,e=process.env){Ts(Nu(e),{...n,at:new Date().toISOString()})}function Os(n,e,t=Ee){let s=ce.resolve(n);for(let o of e||[]){if(o.id===t)continue;let r=ce.resolve(o.path);if(s===r||s.startsWith(r+ce.sep))return{id:o.id,label:o.label||o.id,path:r,how:"inside"};if(r.startsWith(s+ce.sep))return{id:o.id,label:o.label||o.id,path:r,how:"contains"}}return null}function Mr(n,e=process.env){let t=(n||[]).find(s=>s.id===Ee);if(t)return t.path;for(let s of[ce.join(vt(e),"Sync","Leftoff"),ce.join(vt(e),"Leftoff"),ce.join(vt(e),"Leftoff-Sync")])if(!Os(s,n))return s;return ce.join(vt(e),"Leftoff-Sync")}async function Ta(n,{path:e,peers:t=[],myID:s}){let o=await n.folders(),r=Os(e,o);if(r)throw new nt(Ne(e)+(r.how==="inside"?" is inside":" contains")+" your Syncthing folder “"+r.label+"”. Pick a folder outside it; Leftoff won’t change that folder.",{code:"nested"});let i=o.find(c=>c.id===Ee);if(i){if(ce.resolve(i.path.replace(/^~(?=\/|$)/,vt()))!==ce.resolve(e))throw new nt("Syncthing already has a Leftoff folder at "+Ne(i.path)+". Use that one, or press Stop syncing first.",{code:"folder_elsewhere"});let c=new Set((i.devices||[]).map(d=>Rt(d.deviceID))),u=t.filter(d=>!c.has(Rt(d)));return u.length&&await n.patchFolder(Ee,{devices:[...i.devices||[],...u.map(d=>({deviceID:d,introducedBy:"",encryptionPassword:""}))]}),await n.setIgnores(Ee,va).catch(()=>{}),{created:!1,folder:{...i,path:e}}}Pe.mkdirSync(e,{recursive:!0}),Mp(e);let l={...await n.defaultsFolder()||{},id:Ee,label:kp,path:e,type:"sendreceive",paused:!1,fsWatcherEnabled:!0,rescanIntervalS:3600,ignorePerms:!1,devices:[s,...t].filter(Boolean).map(c=>({deviceID:c,introducedBy:"",encryptionPassword:""}))};return await n.putFolder(l),await n.setIgnores(Ee,va).catch(()=>{}),{created:!0,folder:l}}function Mp(n){let e=ce.join(n,".stignore"),t="";try{t=Pe.readFileSync(e,"utf8")}catch{}return t.includes("Written by Leftoff")?!1:(Pe.writeFileSync(e,(t?t.replace(/\n?$/,`
63
- `):"")+va.join(`
59
+ `});var Hp={};tn(Hp,{BREW_URL:()=>Ep,CODE_PREFIX:()=>Or,DOWNLOAD_URL:()=>_p,FOLDER_ID:()=>Ee,FOLDER_LABEL:()=>Ap,INSTALL_COMMANDS:()=>Mu,INSTALL_LINE:()=>Cr,PAIR_TTL_MS:()=>Tp,PROOF_RE:()=>ju,PairCodeError:()=>kn,STIGNORE_LINES:()=>Ta,SyncthingClient:()=>Aa,SyncthingError:()=>ot,acceptKnocked:()=>Fr,armPairing:()=>Rr,autoAcceptTick:()=>Pr,brewPath:()=>Ea,cancelPairing:()=>Uu,codeDigest:()=>Pu,connect:()=>mt,decodePairCode:()=>Ru,encodePairCode:()=>Iu,ensureDevice:()=>Ca,ensureFolder:()=>Oa,explainBrewFailure:()=>Dp,findSyncthingConfig:()=>Mp,guiUrl:()=>Ip,installPlan:()=>Cs,knockedDevices:()=>jr,localDir:()=>wo,nameWithoutProof:()=>So,nestedIn:()=>Is,pairProof:()=>Na,parseSyncthingConfig:()=>Np,peerAccepted:()=>Ia,progressLine:()=>Bu,proofName:()=>Fu,publicPairing:()=>Yn,readManaged:()=>xn,readPairing:()=>Ft,readRunStatus:()=>Du,runInstall:()=>Ir,sameDevice:()=>Ce,shortId:()=>$n,startSyncthing:()=>Mr,statusFile:()=>Lu,stopSyncing:()=>Dr,stuckSteps:()=>Hu,suggestPath:()=>Lr,syncthingConfigCandidates:()=>Cp,syncthingInstall:()=>vn,syncthingStatus:()=>sn,tilde:()=>Me,untilde:()=>_a,waitForSyncthing:()=>Ns,writeManaged:()=>zn,writeRunStatus:()=>bo,writeStignore:()=>Pp});import je from"node:fs";import bk from"node:os";import ce from"node:path";import vk from"node:http";import xk from"node:https";import ko from"node:crypto";import{spawn as xp,spawnSync as vp}from"node:child_process";function wo(n=process.env){return ce.join(n.XDG_CONFIG_HOME||ce.join(At(n),".config"),"checkpoints")}function Nr(n){try{return JSON.parse(je.readFileSync(n,"utf8"))}catch{return null}}function Os(n,e){je.mkdirSync(ce.dirname(n),{recursive:!0,mode:448});let t=n+".tmp-"+process.pid;je.writeFileSync(t,JSON.stringify(e,null,2)+`
60
+ `,{mode:384}),je.renameSync(t,n);try{je.chmodSync(n,384)}catch{}}function Cp({env:n=process.env,platform:e=process.platform}={}){let t=At(n),s=[];for(let i of[n.LEFTOFF_SYNCTHING_HOME,n.STHOMEDIR])i&&s.push(ce.join(i,"config.xml"));let o=ce.join(t,"Library","Application Support","Syncthing","config.xml"),r=[ce.join(n.XDG_STATE_HOME||ce.join(t,".local","state"),"syncthing","config.xml"),ce.join(n.XDG_CONFIG_HOME||ce.join(t,".config"),"syncthing","config.xml")];return e==="darwin"?s.push(o,...r):s.push(...r,o),[...new Set(s)]}function Np(n,{home:e=At()}={}){let t={gui:null,folders:[],devices:[]},s=String(n).match(/<gui\b([^>]*)>([\s\S]*?)<\/gui>/);if(s){let o=((s[2].match(/<address>([^<]*)<\/address>/)||[])[1]||"").trim(),r=((s[2].match(/<apikey>([^<]*)<\/apikey>/)||[])[1]||"").trim(),i=/\btls="true"/.test(s[1]);t.gui={address:xa(o),tls:i,apiKey:xa(r)||null,url:Ip(xa(o),i)}}for(let o of String(n).matchAll(/<folder\b([^>]*?)(\/>|>([\s\S]*?)<\/folder>)/g)){let r=o[1],i=yo(r,"path");if(!i)continue;i=ce.resolve(i.replace(/^~(?=\/|$)/,e));let a=[...String(o[3]||"").matchAll(/<device\b[^>]*\bid="([^"]+)"/g)].map(l=>l[1]);t.folders.push({id:yo(r,"id")||"",label:yo(r,"label")||"",path:i,type:yo(r,"type")||"sendreceive",devices:a})}for(let o of String(n).matchAll(/<device\b([^>]*\bname="[^"]*"[^>]*)>/g))t.devices.push({id:yo(o[1],"id"),name:yo(o[1],"name")});return t}function Ip(n,e=!1){if(!n||/^unix:/.test(n))return null;let s=n.replace(/^https?:\/\//,"").match(/^(\[[^\]]+\]|[^:]*):(\d+)$/);if(!s)return null;let o=s[1];return(o===""||o==="0.0.0.0")&&(o="127.0.0.1"),o==="[::]"&&(o="[::1]"),(e?"https":"http")+"://"+o+":"+s[2]}function Mp(n={}){let e=n.env||process.env;for(let t of Cp(n)){let s;try{s=je.readFileSync(t,"utf8")}catch{continue}return{file:t,...Np(s,{home:At(e)})}}return null}function mt(n={}){let e=n.env||process.env,t=Mp({env:e,platform:n.platform});if(!t)return{ok:!1,code:"no_config",reason:"Syncthing isn’t set up on this Mac yet"};try{return{ok:!0,cfg:t,client:Aa.fromConfig(t,{managedDevices:()=>xn(e).devices,...n.client})}}catch(s){return{ok:!1,cfg:t,code:s.code,reason:s.message}}}function Ea(n=process.env){return n.LEFTOFF_NO_BREW?null:Lp("brew",n)||["/opt/homebrew/bin/brew","/usr/local/bin/brew","/home/linuxbrew/.linuxbrew/bin/brew"].find(e=>{try{return je.accessSync(e,je.constants.X_OK),!0}catch{return!1}})||null}function vn(n=process.env){let e=Ea(n);if(e){let s=ce.join(ce.dirname(e),"syncthing");if(Ak(s))return{installed:!0,via:"brew",path:s,brew:e}}let t=Lp("syncthing",n);if(t)return{installed:!0,via:e&&t.startsWith(ce.dirname(ce.dirname(e)))?"brew":"path",path:t,brew:e};for(let s of["/Applications/Syncthing.app",ce.join(At(n),"Applications","Syncthing.app")])if(Tk(s))return{installed:!0,via:"app",path:s,brew:e};return{installed:!1,via:null,path:null,brew:e}}function Cs(n=process.env){return Ea(n)?{brew:!0,command:Cr,steps:Mu.map(t=>["brew",...t].join(" "))}:{brew:!1,directions:["Install Homebrew from brew.sh (one line in Terminal), then come back and press Check again. Leftoff will install Syncthing for you.","Or download Syncthing for macOS from syncthing.net/downloads, open it once, then press Check again."],links:{brew:Ep,download:_p}}}function Dp(n,e){let t=n.join(`
61
+ `);if(/Permission denied|Operation not permitted/i.test(t))return"Homebrew couldn’t write to its folder (permissions). Run `brew doctor` in Terminal, then try again.";if(/Could not resolve host|Failed to download|curl: \(\d+\)|network/i.test(t))return"Homebrew couldn’t download Syncthing. Check your internet connection and try again.";if(/another active Homebrew|already running|lock/i.test(t)&&/brew/i.test(t))return"Homebrew is busy with another install. Wait for it to finish, then try again.";if(/No available formula|No formulae found/i.test(t))return"Homebrew doesn’t know Syncthing (an old Homebrew?). Run `brew update`, then try again.";if(/xcode-select|Command Line Tools/i.test(t))return"Homebrew needs Apple’s Command Line Tools. Run `xcode-select --install`, then try again.";let s=n.filter(o=>/\S/.test(o)).slice(-1)[0]||"";return"Homebrew stopped (exit "+e+")"+(s?": "+s.replace(/^Error:\s*/,"").slice(0,200):"")+"."}async function Ir({env:n=process.env,onLine:e=()=>{},onStage:t=()=>{}}={}){let s=Ea(n);if(!s)return{ok:!1,code:"no_brew",error:"Homebrew isn’t installed on this Mac."};let o=[],r={...n,HOMEBREW_NO_ENV_HINTS:"1",HOMEBREW_NO_INSTALL_CLEANUP:"1",NONINTERACTIVE:"1"};for(let i of Mu){let a=["brew",...i].join(" ");t(a),e("$ "+a);let l=await new Promise(c=>{let u=xp(s,i,{env:r,stdio:["ignore","pipe","pipe"]}),d="",f=h=>{d+=h;let p;for(;(p=d.search(/[\r\n]/))>=0;){let m=d.slice(0,p);d=d.slice(p+1),m.trim()&&(o.push(m),o.length>400&&o.shift(),e(m))}};u.stdout.on("data",f),u.stderr.on("data",f),u.on("error",h=>{o.push(h.message),e(h.message),c(127)}),u.on("close",h=>{d.trim()&&(o.push(d),e(d)),c(h??1)})});if(l!==0)return{ok:!1,code:"brew_failed",exit:l,stage:a,error:Dp(o,l),lines:o}}return{ok:!0,lines:o}}function Mr(n=process.env){let e=vn(n);if(!e.installed)return{ok:!1,code:"not_installed",error:"Syncthing isn’t installed"};if(e.via==="brew"){let o=vp(e.brew,["services","start","syncthing"],{env:{...n,HOMEBREW_NO_ENV_HINTS:"1"},encoding:"utf8",timeout:6e4});return o.status===0?{ok:!0,how:"brew services start syncthing"}:{ok:!1,code:"start_failed",error:(o.stderr||o.stdout||"").trim().split(`
62
+ `).pop()||"brew services failed"}}if(e.via==="app")return vp("open",["-a",e.path],{encoding:"utf8"}).status===0?{ok:!0,how:"opened Syncthing.app"}:{ok:!1,code:"start_failed",error:"couldn’t open Syncthing.app"};je.mkdirSync(wo(n),{recursive:!0});let t=je.openSync(ce.join(wo(n),"syncthing.log"),"a"),s=xp(e.path,["serve","--no-browser","--no-restart"],{env:n,detached:!0,stdio:["ignore",t,t]});return s.unref(),{ok:!0,how:"syncthing serve (pid "+s.pid+")"}}async function Ns({env:n=process.env,timeoutMs:e=9e4,every:t=500}={}){let s=Date.now(),o=null;for(;Date.now()-s<e;){let r=mt({env:n});if(r.ok)try{return await r.client.ping(),{ok:!0,...r}}catch(i){o=i.message}else o=r.reason;await Op(t)}return{ok:!1,code:"not_running",error:"Syncthing didn’t come up"+(o?" ("+o+")":"")}}function xn(n=process.env){let e=Nr(Rp(n))||{};return{devices:e.devices||[],originalName:e.originalName||null,usedCodes:e.usedCodes||[],folderPath:e.folderPath||null}}function zn(n,e=process.env){Os(Rp(e),{...xn(e),...n})}function _k(n,e){let t=xn(e);t.devices.some(s=>Ce(s,n))||zn({devices:[...t.devices,n]},e)}function Du(n=process.env){return Nr(Lu(n))}function bo(n,e=process.env){Os(Lu(e),{...n,at:new Date().toISOString()})}function Is(n,e,t=Ee){let s=ce.resolve(n);for(let o of e||[]){if(o.id===t)continue;let r=ce.resolve(o.path);if(s===r||s.startsWith(r+ce.sep))return{id:o.id,label:o.label||o.id,path:r,how:"inside"};if(r.startsWith(s+ce.sep))return{id:o.id,label:o.label||o.id,path:r,how:"contains"}}return null}function Lr(n,e=process.env){let t=(n||[]).find(s=>s.id===Ee);if(t)return t.path;for(let s of[ce.join(At(e),"Sync","Leftoff"),ce.join(At(e),"Leftoff"),ce.join(At(e),"Leftoff-Sync")])if(!Is(s,n))return s;return ce.join(At(e),"Leftoff-Sync")}async function Oa(n,{path:e,peers:t=[],myID:s}){let o=await n.folders(),r=Is(e,o);if(r)throw new ot(Me(e)+(r.how==="inside"?" is inside":" contains")+" your Syncthing folder “"+r.label+"”. Pick a folder outside it; Leftoff won’t change that folder.",{code:"nested"});let i=o.find(c=>c.id===Ee);if(i){if(ce.resolve(i.path.replace(/^~(?=\/|$)/,At()))!==ce.resolve(e))throw new ot("Syncthing already has a Leftoff folder at "+Me(i.path)+". Use that one, or press Stop syncing first.",{code:"folder_elsewhere"});let c=new Set((i.devices||[]).map(d=>jt(d.deviceID))),u=t.filter(d=>!c.has(jt(d)));return u.length&&await n.patchFolder(Ee,{devices:[...i.devices||[],...u.map(d=>({deviceID:d,introducedBy:"",encryptionPassword:""}))]}),await n.setIgnores(Ee,Ta).catch(()=>{}),{created:!1,folder:{...i,path:e}}}je.mkdirSync(e,{recursive:!0}),Pp(e);let l={...await n.defaultsFolder()||{},id:Ee,label:Ap,path:e,type:"sendreceive",paused:!1,fsWatcherEnabled:!0,rescanIntervalS:3600,ignorePerms:!1,devices:[s,...t].filter(Boolean).map(c=>({deviceID:c,introducedBy:"",encryptionPassword:""}))};return await n.putFolder(l),await n.setIgnores(Ee,Ta).catch(()=>{}),{created:!0,folder:l}}function Pp(n){let e=ce.join(n,".stignore"),t="";try{t=je.readFileSync(e,"utf8")}catch{}return t.includes("Written by Leftoff")?!1:(je.writeFileSync(e,(t?t.replace(/\n?$/,`
63
+ `):"")+Ta.join(`
64
64
  `)+`
65
- `),!0)}async function _a(n,{id:e,name:t,env:s=process.env}){let r=(await n.devices()).find(l=>Ce(l.deviceID,e));if(r)return{created:!1,device:r};kk(e,s);let a={...await n.defaultsDevice()||{},deviceID:e,name:t,addresses:["dynamic"],compression:"metadata",introducer:!1,autoAcceptFolders:!1,paused:!1};return await n.putNewDevice(a),{created:!0,device:a}}async function Lr(n){let e=await n.folder(Ee);return e?(await n.deleteFolder(Ee),{removed:!0,path:e.path}):{removed:!1,reason:"no Leftoff folder in Syncthing"}}function Lp(n){let e=0,t=0,s="";for(let o of n)for(t=t<<8|o,e+=8;e>=5;)s+=Ou[t>>>e-5&31],e-=5;return e>0&&(s+=Ou[t<<5-e&31]),s}function bk(n){let e=String(n).toUpperCase().replace(/[OIL]/g,r=>r==="O"?"0":"1").replace(/[^0-9A-Z]/g,""),t=0,s=0,o=[];for(let r of e){let i=Ou.indexOf(r);if(i<0)throw new Error("bad character");s=(s<<5|i)&65535,t+=5,t>=8&&(o.push(s>>>t-8&255),t-=8)}return Buffer.from(o)}function vk(n){let e=Rt(n);if(e.length!==56)throw new Error("not a Syncthing device id");let t=0,s=0,o=[];for(let r of e)s=(s<<5|Dp.indexOf(r))&65535,t+=5,t>=8&&(o.push(s>>>t-8&255),t-=8);return Buffer.from(o)}function xk(n){let e=0,t=0,s="";for(let o of n)for(t=(t<<8|o)&65535,e+=8;e>=5;)s+=Dp[t>>>e-5&31],e-=5;return s.match(/.{7}/g).join("-")}function Cu({deviceId:n,folderId:e=Ee,machine:t="",secret:s,expiresAt:o}){if(!s||s.length!==16)throw new Error("secret must be 16 bytes");let r=Buffer.from(String(t).slice(0,40),"utf8").subarray(0,40),i=Buffer.from(String(e),"utf8").subarray(0,64),a=Buffer.alloc(4);a.writeUInt32BE(Math.floor(o/1e3));let l=Buffer.concat([Buffer.from([1]),a,s,vk(n),Buffer.from([i.length]),i,Buffer.from([r.length]),r]),c=wo.createHash("sha256").update(l).digest().subarray(0,4);return Er+"-"+Lp(Buffer.concat([l,c])).match(/.{1,5}/g).join("-")}function Lu(n,{now:e=Date.now()}={}){let t=String(n||"").trim().replace(/^.*?\bjoin\s+/i,"").replace(/\s+/g,"");if(!new RegExp("^"+Er+"-?","i").test(t))throw new wn("That isn’t a Leftoff pairing code (it starts with "+Er+"-).","bad_code");t=t.replace(new RegExp("^"+Er+"-?","i"),"");let s;try{s=bk(t)}catch{throw new wn("That code has a character that doesn’t belong; copy it again.","bad_code")}if(s.length<62)throw new wn("That code is cut short; copy all of it.","bad_code");let o=s.subarray(0,s.length-4),r=s.subarray(s.length-4);if(!wo.createHash("sha256").update(o).digest().subarray(0,4).equals(r))throw new wn("That code has a typo or is cut short; copy it again.","bad_code");if(o[0]!==1)throw new wn("That code is from a newer Leftoff; upgrade this Mac (leftoff upgrade).","bad_code");let i=1,a=o.readUInt32BE(i)*1e3;i+=4;let l=Buffer.from(o.subarray(i,i+16));i+=16;let c=xk(o.subarray(i,i+35));i+=35;let u=o[i++],d=o.subarray(i,i+u).toString("utf8");i+=u;let f=o[i++],h=o.subarray(i,i+f).toString("utf8");if(e>a)throw new wn("That code expired. Make a new one on "+(h||"the other Mac")+" (Settings → Sync → Pair another Mac).","expired");return{deviceId:c,folderId:d,machine:h,secret:l,expiresAt:a}}function Ea(n,{joinerId:e,firstId:t,folderId:s=Ee}){let o=wo.createHmac("sha256",n).update(`leftoff-pair-v1
66
- `+Rt(e)+`
67
- `+Rt(t)+`
68
- `+s).digest();return Lp(o).slice(0,16)}function Pt(n=process.env,e=Date.now()){let t=Cr(Sn(n));return t?{...t,live:!t.used&&e<t.expiresAt}:null}function $n(n,e=Date.now()){return n?{armed:!!(n.live&&e<n.expiresAt),expiresAt:new Date(n.expiresAt).toISOString(),expired:!n.used&&e>=n.expiresAt,used:!!n.used,paired:n.paired||null,ignored:(n.ignored||[]).length}:{armed:!1}}function Dr({deviceId:n,machine:e,folderId:t=Ee,ttlMs:s=bp,env:o=process.env,now:r=Date.now(),reuse:i=!1}){let a=Pt(o,r);if(i&&a&&a.live&&Ce(a.deviceId,n))return{...a,code:Cu({deviceId:n,folderId:a.folderId,machine:a.machine,secret:Buffer.from(a.secret,"base64"),expiresAt:a.expiresAt})};let l=wo.randomBytes(16),c={deviceId:n,folderId:t,machine:e,secret:l.toString("base64"),createdAt:r,expiresAt:r+s,used:!1,paired:null,ignored:[]};return Ts(Sn(o),c),{...c,live:!0,code:Cu({deviceId:n,folderId:t,machine:e,secret:l,expiresAt:c.expiresAt})}}function ju(n=process.env){try{Pe.unlinkSync(Sn(n))}catch{}}async function Rr({client:n,env:e=process.env,now:t=Date.now(),log:s=()=>{}}){let o=Pt(e,t);if(!o)return{state:"idle"};if(o.used)return{state:"paired",paired:o.paired};if(t>=o.expiresAt)return o.expiredLogged||(Ts(Sn(e),{...o,secret:null,expiredLogged:!0,live:void 0}),s("syncthing: the pairing code expired unused; nothing was accepted")),{state:"expired"};let r=Buffer.from(o.secret,"base64"),i=await n.pendingDevices(),a=new Set(o.ignored||[]),l=new Set(o.dismissed||[]),c=[...o.knocked||[]],u=!1;for(let[d,f]of Object.entries(i||{})){if(!/^[A-Z2-7]{56}$/.test(Rt(d)))continue;let h=String(f&&f.name||""),p=h.match(Ru);if(!(p&&wo.timingSafeEqual(Buffer.from(p[1]),Buffer.from(Ea(r,{joinerId:d,firstId:o.deviceId,folderId:o.folderId}))))){let k=yo(h).slice(0,80),w=c.find(x=>Ce(x.deviceId,d));if(w?(w.name!==k||w.at!==(f&&f.time||w.at))&&(w.name=k,w.at=f&&f.time||w.at,w.seen=new Date(t).toISOString(),u=!0):(c.push({deviceId:d,name:k,at:f&&f.time||new Date(t).toISOString(),seen:new Date(t).toISOString()}),u=!0),a.has(Rt(d))||(a.add(Rt(d)),u=!0,s("syncthing: a device named “"+k+"” ("+Gn(d)+") asked to connect without this pairing code’s proof; not accepted")),!l.has(Rt(d))){l.add(Rt(d)),u=!0;try{await n.dismissPendingDevice(d),s("syncthing: cleared Syncthing’s pending record of "+Gn(d)+" once, so it can knock again with its current name")}catch{}}continue}let g=yo(h)||"other Mac",S={deviceId:d,name:g,at:new Date(t).toISOString(),how:"code"};return Ts(Sn(e),{...o,live:void 0,secret:null,used:!0,paired:S,ignored:[...a],dismissed:[...l],knocked:c.filter(k=>!Ce(k.deviceId,d))}),await Rp(n,{id:d,name:g,folderId:o.folderId,env:e}),s("syncthing: paired with "+g+" ("+Gn(d)+") by code; sharing the Leftoff folder with it"),{state:"accepted",paired:S}}return u&&Ts(Sn(e),{...o,live:void 0,ignored:[...a],dismissed:[...l],knocked:c}),{state:"waiting",knocked:c.length}}async function Rp(n,{id:e,name:t,folderId:s=Ee,env:o}){await _a(n,{id:e,name:t,env:o});let r=await n.folder(s);r&&!(r.devices||[]).some(i=>Ce(i.deviceID,e))&&await n.patchFolder(s,{devices:[...r.devices||[],{deviceID:e,introducedBy:"",encryptionPassword:""}]})}async function Pr(n,{env:e=process.env}={}){let t=Cr(Sn(e));if(!t||t.used||!(t.knocked||[]).length)return[];let[s,o]=await Promise.all([n.devices().catch(()=>[]),n.pendingDevices().catch(()=>({}))]),r=[];for(let i of t.knocked){if(s.some(c=>Ce(c.deviceID,i.deviceId))||t.paired&&Ce(t.paired.deviceId,i.deviceId))continue;let a=Object.entries(o||{}).find(([c])=>Ce(c,i.deviceId));if(!a&&!(Date.parse(i.seen||i.at)>Date.now()-30*6e4))continue;let l=a&&yo(a[1]&&a[1].name).slice(0,80)||i.name;r.push({deviceId:i.deviceId,short:Gn(i.deviceId),name:l||Gn(i.deviceId),at:a&&a[1]&&a[1].time||i.at,pending:!!a})}return r}async function jr(n,{deviceId:e,env:t=process.env,now:s=Date.now(),log:o=()=>{}}){let i=(await Pr(n,{env:t})).find(c=>Ce(c.deviceId,e)||e&&c.name===e);if(!i)throw new nt("That Mac didn’t ask to connect during this pairing (or it’s already added). Make a new code and try again.",{code:"not_knocked"});let a=Cr(Sn(t))||{},l={deviceId:i.deviceId,name:i.name,at:new Date(s).toISOString(),how:"manual"};return Ts(Sn(t),{...a,live:void 0,secret:null,used:!0,paired:l,knocked:(a.knocked||[]).filter(c=>!Ce(c.deviceId,i.deviceId))}),await Rp(n,{id:i.deviceId,name:i.name,folderId:a.folderId||Ee,env:t}),o("syncthing: accepted "+i.name+" ("+i.short+") by an explicit click, without the pairing proof; sharing only the Leftoff folder with it"),{accepted:!0,paired:l}}async function Oa(n,{peerId:e,folderId:t=Ee}){let s=await n.connections().catch(()=>({})),o=Object.entries(s||{}).find(([l])=>Ce(l,e));if(!o||!o[1]||!o[1].connected)return{connected:!1,accepted:!1,state:"disconnected"};let r=await n.completion(e,t).catch(()=>null),i=r&&r.remoteState;if(i==="valid")return{connected:!0,accepted:!0,state:"valid"};if(i)return{connected:!0,accepted:!1,state:i};let a=await n.dbStatus(t).catch(()=>null);return{connected:!0,accepted:!!(r&&a&&a.globalFiles>0),state:"unknown"}}function Fu({first:n,name:e}){return["On "+n+", open http://127.0.0.1:8384 (Syncthing).","Click Add Device on the “"+e+"” wants to connect banner, then Save.","On that device’s Sharing tab, tick Leftoff, then Save."]}function Ak(n,e){let t=n&&n[Ee];return[t&&t.lastFile&&t.lastFile.at,t&&t.lastScan,e&&e.stateChanged].filter(o=>o&&!/^0001-/.test(o)).sort().slice(-1)[0]||null}async function tn({env:n=process.env,root:e=null,platform:t}={}){let s=kn(n),o={installed:s.installed,via:s.via,brew:!!s.brew,running:!1,version:null,reason:null,folder:null,peers:[],others:0,suggestedPath:null,nested:null,knocked:[]},r=ht({env:n,platform:t});if(r.cfg&&!s.installed&&(o.installed=!0,o.via="config"),!r.ok)return o.reason=r.reason,o;try{await r.client.ping(),o.running=!0}catch(a){return o.reason=a.message,o}let i=r.client;try{let[a,l,c,u]=await Promise.all([i.version().catch(()=>null),i.systemStatus(),i.folders(),i.devices()]);o.version=a&&a.version,o.myID=l.myID,o.others=c.filter(f=>f.id!==Ee).length,o.suggestedPath=Mr(c,n),e&&(o.nested=Os(e,c));let d=c.find(f=>f.id===Ee);if(d){let[f,h,p,m,g]=await Promise.all([i.dbStatus().catch(()=>null),i.folderErrors(),i.statsFolder().catch(()=>({})),i.connections().catch(()=>({})),i.statsDevice().catch(()=>({}))]);o.folder={id:d.id,label:d.label,path:d.path,display:Ne(d.path,n),isCurrent:!!e&&ce.resolve(d.path)===ce.resolve(e),paused:!!d.paused,state:f?f.state:"unknown",globalFiles:f?f.globalFiles:null,inSyncFiles:f?f.inSyncFiles:null,needFiles:f?f.needFiles:null,errors:[...(h||[]).map(S=>({path:S.path,error:S.error})),...f&&f.error?[{path:"",error:f.error}]:[]].slice(0,10),lastSync:Ak(p,f)};for(let S of d.devices||[]){if(Ce(S.deviceID,l.myID))continue;let k=u.find(y=>Ce(y.deviceID,S.deviceID))||{},w=m[S.deviceID]||{},x=null,v=null;if(w.connected)try{let y=await i.completion(S.deviceID);x=y&&y.completion!=null?Math.max(0,Math.min(100,Math.round(y.completion))):null,v=y&&y.remoteState||null}catch{}let b=!v||v==="valid";o.peers.push({name:k.name||Gn(S.deviceID),short:Gn(S.deviceID),connected:!!w.connected&&b,notSharing:!!w.connected&&!b,lastSeen:(g[S.deviceID]||{}).lastSeen||null,completion:b?x:null})}o.knocked=await Pr(i,{env:n}).catch(()=>[])}}catch(a){o.reason=a.message}return o}function Uu(n){if(!n)return"Waiting for Syncthing…";let e=n.globalFiles||0,t=n.inSyncFiles!=null?n.inSyncFiles:Math.max(0,e-(n.needFiles||0));return e?n.needFiles>0||n.state==="syncing"?"Syncing "+t+"/"+e+" files…":"In sync · "+e+" files":"Waiting for the first files…"}var Ee,kp,bp,Iu,Or,vp,xp,vt,wk,Sk,Ap,Ne,xa,ka,mo,nt,Rt,Ce,Gn,ba,Cp,Np,Nu,va,Ou,Dp,Er,wn,Du,Ru,Pu,yo,Sn,ko=J(()=>{Ee="leftoff",kp="Leftoff",bp=900*1e3,Iu=[["install","syncthing"],["services","start","syncthing"]],Or="brew install syncthing && brew services start syncthing",vp="https://syncthing.net/downloads/",xp="https://brew.sh",vt=(n=process.env)=>n.HOME||mk.homedir(),wk=n=>{try{return Pe.statSync(n),!0}catch{return!1}},Sk=n=>{try{return Pe.statSync(n).isDirectory()}catch{return!1}},Ap=n=>new Promise(e=>setTimeout(e,n)),Ne=(n,e=process.env)=>{let t=vt(e);return n&&(n===t?"~":n.startsWith(t+ce.sep)?"~"+n.slice(t.length):n)},xa=(n,e=process.env)=>n==="~"?vt(e):String(n).startsWith("~/")?ce.join(vt(e),String(n).slice(2)):String(n);ka=n=>String(n).replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&apos;/g,"'").replace(/&amp;/g,"&"),mo=(n,e)=>{let t=n.match(new RegExp("\\b"+e+'="([^"]*)"'));return t?ka(t[1]):null};nt=class extends Error{constructor(e,{status:t=0,code:s="syncthing"}={}){super(e),this.status=t,this.code=s}},Rt=n=>String(n||"").toUpperCase().replace(/[^A-Z2-7]/g,""),Ce=(n,e)=>!!n&&!!e&&Rt(n)===Rt(e),Gn=n=>String(n||"").split("-")[0]||"",ba=class n{constructor({url:e,apiKey:t,timeoutMs:s=6e3,folderId:o=Ee,managedDevices:r=()=>[],log:i=null}={}){if(!e)throw new nt("Syncthing’s GUI address isn’t readable from its config",{code:"no_address"});this.url=e.replace(/\/$/,""),Object.defineProperty(this,"apiKey",{value:t,enumerable:!1}),this.timeoutMs=s,this.folderId=o,this.managedDevices=r,this.calls=[],this.log=i}static fromConfig(e,t={}){if(!e||!e.gui)throw new nt("Syncthing hasn’t written its settings yet",{code:"no_config"});if(!e.gui.apiKey)throw new nt("Syncthing’s config has no API key yet",{code:"no_api_key"});return new n({url:e.gui.url,apiKey:e.gui.apiKey,...t})}req(e,t,s,{timeoutMs:o}={}){let r=new URL(this.url+t),i=r.protocol==="https:"?yk:gk,a=s===void 0?null:Buffer.from(JSON.stringify(s));return e!=="GET"&&this.calls.push({method:e,path:r.pathname+r.search}),new Promise((l,c)=>{let u=i.request(r,{method:e,headers:{"X-API-Key":this.apiKey||"",accept:"application/json",...a?{"content-type":"application/json","content-length":a.length}:{}},rejectUnauthorized:!1,timeout:o||this.timeoutMs},d=>{let f=[];d.on("data",h=>f.push(h)),d.on("end",()=>{let h=Buffer.concat(f).toString("utf8");if(d.statusCode>=400){let p=d.statusCode===401||d.statusCode===403?"Syncthing refused Leftoff’s request (its API key changed? Restart Syncthing and try again)":"Syncthing answered "+d.statusCode+(h?": "+h.trim().slice(0,160):"");return c(new nt(p,{status:d.statusCode,code:d.statusCode===403||d.statusCode===401?"syncthing_forbidden":d.statusCode===404?"syncthing_not_found":"syncthing_error"}))}if(!h)return l(null);try{l(JSON.parse(h))}catch{l(h)}})});u.on("timeout",()=>u.destroy(new nt("Syncthing didn’t answer in time",{code:"syncthing_timeout"}))),u.on("error",d=>c(d instanceof nt?d:new nt(d.code==="ECONNREFUSED"?"Syncthing isn’t running":"Can’t reach Syncthing ("+(d.code||d.message)+")",{code:d.code==="ECONNREFUSED"?"syncthing_down":"syncthing_unreachable"}))),a&&u.write(a),u.end()})}ping(){return this.req("GET","/rest/system/ping",void 0,{timeoutMs:2500})}systemStatus(){return this.req("GET","/rest/system/status")}version(){return this.req("GET","/rest/system/version")}folders(){return this.req("GET","/rest/config/folders")}devices(){return this.req("GET","/rest/config/devices")}async folder(e=this.folderId){try{return await this.req("GET","/rest/config/folders/"+encodeURIComponent(e))}catch(t){if(t.status===404)return null;throw t}}defaultsFolder(){return this.req("GET","/rest/config/defaults/folder").catch(()=>({}))}defaultsDevice(){return this.req("GET","/rest/config/defaults/device").catch(()=>({}))}pendingDevices(){return this.req("GET","/rest/cluster/pending/devices").then(e=>e||{})}pendingFolders(){return this.req("GET","/rest/cluster/pending/folders").then(e=>e||{})}completion(e,t=this.folderId){return this.req("GET","/rest/db/completion?folder="+encodeURIComponent(t)+(e?"&device="+encodeURIComponent(e):""))}dbStatus(e=this.folderId){return this.req("GET","/rest/db/status?folder="+encodeURIComponent(e))}connections(){return this.req("GET","/rest/system/connections").then(e=>e&&e.connections||{})}statsFolder(){return this.req("GET","/rest/stats/folder").then(e=>e||{})}statsDevice(){return this.req("GET","/rest/stats/device").then(e=>e||{})}folderErrors(e=this.folderId){return this.req("GET","/rest/folder/errors?folder="+encodeURIComponent(e)).then(t=>t&&t.errors||[]).catch(()=>[])}events(e=0,{timeoutS:t=1,types:s=null}={}){return this.req("GET","/rest/events?since="+e+"&timeout="+t+(s?"&events="+s.join(","):""),void 0,{timeoutMs:(t+3)*1e3}).then(o=>o||[])}assertOurFolder(e){if(e!==this.folderId)throw new nt("Leftoff never changes another Syncthing folder ("+e+")",{code:"not_ours"})}assertOurDevice(e,{self:t=null}={}){if(!(t&&Ce(e,t))&&!this.managedDevices().some(s=>Ce(s,e)))throw new nt("Leftoff never changes a Syncthing device it didn’t add",{code:"not_ours"})}async putFolder(e){return this.assertOurFolder(e.id),this.req("PUT","/rest/config/folders/"+encodeURIComponent(e.id),e)}async patchFolder(e,t){return this.assertOurFolder(e),this.req("PATCH","/rest/config/folders/"+encodeURIComponent(e),t)}async deleteFolder(e){return this.assertOurFolder(e),this.req("DELETE","/rest/config/folders/"+encodeURIComponent(e))}async setIgnores(e,t){return this.assertOurFolder(e),this.req("POST","/rest/db/ignores?folder="+encodeURIComponent(e),{ignore:t})}async putNewDevice(e){return this.assertOurDevice(e.deviceID),this.req("PUT","/rest/config/devices/"+encodeURIComponent(e.deviceID),e)}async patchDevice(e,t,{self:s=null}={}){return this.assertOurDevice(e,{self:s}),this.req("PATCH","/rest/config/devices/"+encodeURIComponent(e),t)}async reconnectDevice(e,{allow:t=null}={}){t&&Ce(e,t)||this.assertOurDevice(e);let s="?device="+encodeURIComponent(e);return await this.req("POST","/rest/system/pause"+s),await Ap(150),this.req("POST","/rest/system/resume"+s)}dismissPendingDevice(e){return this.req("DELETE","/rest/cluster/pending/devices?device="+encodeURIComponent(e))}};Cp=(n,e)=>{for(let t of String(e.PATH||"").split(ce.delimiter)){if(!t)continue;let s=ce.join(t,n);try{if(Pe.accessSync(s,Pe.constants.X_OK),Pe.statSync(s).isFile())return s}catch{}}return null};Np=n=>ce.join(go(n),"syncthing.json");Nu=(n=process.env)=>ce.join(go(n),"syncthing-status.json");va=["// Written by Leftoff. These stay on each Mac and never sync.","/.secret","/.daemon.pid","/.daemon.log","(?d).DS_Store","(?d)*.tmp","(?d)*.tmp-*","(?d)*.lock","(?d)/lists/.lock*"];Ou="0123456789ABCDEFGHJKMNPQRSTVWXYZ";Dp="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";Er="LO1";wn=class extends Error{constructor(e,t){super(e),this.code=t}};Du=n=>wo.createHash("sha256").update(String(n).toUpperCase().replace(/[^0-9A-Z]/g,"")).digest("hex").slice(0,24);Ru=/\(leftoff-pair ([0-9A-Z]{16})\)\s*$/,Pu=(n,e)=>String(n||"Mac").slice(0,40)+" (leftoff-pair "+e+")",yo=n=>String(n||"").replace(Ru,"").trim(),Sn=n=>ce.join(go(n),"syncthing-pairing.json")});import Fr from"node:fs";function jp(n){return n==null?"":typeof n=="string"?n:Array.isArray(n)?n.map(e=>typeof e=="string"?e:e&&e.type==="text"&&e.text||"").filter(Boolean).join(`
69
- `):typeof n=="object"&&n.text?String(n.text):""}function bo(n,{times:e=!1}={}){let t=[],s={cwd:null,gitBranch:null,sessionId:null,customTitle:null,summary:null,model:null,lines:0,skipped:0},o=new Map;for(let r of String(n||"").split(`
70
- `)){if(!r.trim())continue;s.lines++;let i;try{i=JSON.parse(r)}catch{s.skipped++;continue}if(!i||typeof i!="object"){s.skipped++;continue}if(i.cwd&&(s.cwd=i.cwd),i.gitBranch&&(s.gitBranch=i.gitBranch),i.sessionId&&(s.sessionId=i.sessionId),i.type==="summary"&&i.summary){s.summary=i.summary;continue}if((i.type==="custom-title"||i.type==="custom_title")&&(i.customTitle||i.title)){s.customTitle=i.customTitle||i.title;continue}if(i.isSidechain||i.isMeta)continue;let a=i.message;if(!a||i.type!=="user"&&i.type!=="assistant"){i.type!=="system"&&s.skipped++;continue}let l=a.role||i.type;l==="assistant"&&typeof a.model=="string"&&a.model&&a.model!=="<synthetic>"&&(s.model=a.model);let c=a.content;if(l==="user"){let u=[],d="";if(typeof c=="string")d=c;else if(Array.isArray(c)){for(let f of c)if(f)if(f.type==="tool_result"){let h={tool_use_id:f.tool_use_id,text:jp(f.content),isError:!!f.is_error};u.push(h);let p=o.get(f.tool_use_id);p&&(p.isError=h.isError,p.text=h.text)}else f.type==="text"&&(d+=(d?`
71
- `:"")+(f.text||""))}if(/^\s*<(local-command-stdout|command-name|local-command-caveat)/.test(d)&&(d=""),!d&&!u.length)continue;t.push(e?{role:"user",text:d,toolUses:[],toolResults:u,ts:i.timestamp||null}:{role:"user",text:d,toolUses:[],toolResults:u})}else{let u=[],d="";for(let h of Array.isArray(c)?c:[{type:"text",text:jp(c)}])if(h){if(h.type==="text"&&h.text)d+=(d?`
65
+ `),!0)}async function Ca(n,{id:e,name:t,env:s=process.env}){let r=(await n.devices()).find(l=>Ce(l.deviceID,e));if(r)return{created:!1,device:r};_k(e,s);let a={...await n.defaultsDevice()||{},deviceID:e,name:t,addresses:["dynamic"],compression:"metadata",introducer:!1,autoAcceptFolders:!1,paused:!1};return await n.putNewDevice(a),{created:!0,device:a}}async function Dr(n){let e=await n.folder(Ee);return e?(await n.deleteFolder(Ee),{removed:!0,path:e.path}):{removed:!1,reason:"no Leftoff folder in Syncthing"}}function jp(n){let e=0,t=0,s="";for(let o of n)for(t=t<<8|o,e+=8;e>=5;)s+=Nu[t>>>e-5&31],e-=5;return e>0&&(s+=Nu[t<<5-e&31]),s}function Ek(n){let e=String(n).toUpperCase().replace(/[OIL]/g,r=>r==="O"?"0":"1").replace(/[^0-9A-Z]/g,""),t=0,s=0,o=[];for(let r of e){let i=Nu.indexOf(r);if(i<0)throw new Error("bad character");s=(s<<5|i)&65535,t+=5,t>=8&&(o.push(s>>>t-8&255),t-=8)}return Buffer.from(o)}function Ok(n){let e=jt(n);if(e.length!==56)throw new Error("not a Syncthing device id");let t=0,s=0,o=[];for(let r of e)s=(s<<5|Fp.indexOf(r))&65535,t+=5,t>=8&&(o.push(s>>>t-8&255),t-=8);return Buffer.from(o)}function Ck(n){let e=0,t=0,s="";for(let o of n)for(t=(t<<8|o)&65535,e+=8;e>=5;)s+=Fp[t>>>e-5&31],e-=5;return s.match(/.{7}/g).join("-")}function Iu({deviceId:n,folderId:e=Ee,machine:t="",secret:s,expiresAt:o}){if(!s||s.length!==16)throw new Error("secret must be 16 bytes");let r=Buffer.from(String(t).slice(0,40),"utf8").subarray(0,40),i=Buffer.from(String(e),"utf8").subarray(0,64),a=Buffer.alloc(4);a.writeUInt32BE(Math.floor(o/1e3));let l=Buffer.concat([Buffer.from([1]),a,s,Ok(n),Buffer.from([i.length]),i,Buffer.from([r.length]),r]),c=ko.createHash("sha256").update(l).digest().subarray(0,4);return Or+"-"+jp(Buffer.concat([l,c])).match(/.{1,5}/g).join("-")}function Ru(n,{now:e=Date.now()}={}){let t=String(n||"").trim().replace(/^.*?\bjoin\s+/i,"").replace(/\s+/g,"");if(!new RegExp("^"+Or+"-?","i").test(t))throw new kn("That isn’t a Leftoff pairing code (it starts with "+Or+"-).","bad_code");t=t.replace(new RegExp("^"+Or+"-?","i"),"");let s;try{s=Ek(t)}catch{throw new kn("That code has a character that doesn’t belong; copy it again.","bad_code")}if(s.length<62)throw new kn("That code is cut short; copy all of it.","bad_code");let o=s.subarray(0,s.length-4),r=s.subarray(s.length-4);if(!ko.createHash("sha256").update(o).digest().subarray(0,4).equals(r))throw new kn("That code has a typo or is cut short; copy it again.","bad_code");if(o[0]!==1)throw new kn("That code is from a newer Leftoff; upgrade this Mac (leftoff upgrade).","bad_code");let i=1,a=o.readUInt32BE(i)*1e3;i+=4;let l=Buffer.from(o.subarray(i,i+16));i+=16;let c=Ck(o.subarray(i,i+35));i+=35;let u=o[i++],d=o.subarray(i,i+u).toString("utf8");i+=u;let f=o[i++],h=o.subarray(i,i+f).toString("utf8");if(e>a)throw new kn("That code expired. Make a new one on "+(h||"the other Mac")+" (Settings → Sync → Pair another Mac).","expired");return{deviceId:c,folderId:d,machine:h,secret:l,expiresAt:a}}function Na(n,{joinerId:e,firstId:t,folderId:s=Ee}){let o=ko.createHmac("sha256",n).update(`leftoff-pair-v1
66
+ `+jt(e)+`
67
+ `+jt(t)+`
68
+ `+s).digest();return jp(o).slice(0,16)}function Ft(n=process.env,e=Date.now()){let t=Nr(bn(n));return t?{...t,live:!t.used&&e<t.expiresAt}:null}function Yn(n,e=Date.now()){return n?{armed:!!(n.live&&e<n.expiresAt),expiresAt:new Date(n.expiresAt).toISOString(),expired:!n.used&&e>=n.expiresAt,used:!!n.used,paired:n.paired||null,ignored:(n.ignored||[]).length}:{armed:!1}}function Rr({deviceId:n,machine:e,folderId:t=Ee,ttlMs:s=Tp,env:o=process.env,now:r=Date.now(),reuse:i=!1}){let a=Ft(o,r);if(i&&a&&a.live&&Ce(a.deviceId,n))return{...a,code:Iu({deviceId:n,folderId:a.folderId,machine:a.machine,secret:Buffer.from(a.secret,"base64"),expiresAt:a.expiresAt})};let l=ko.randomBytes(16),c={deviceId:n,folderId:t,machine:e,secret:l.toString("base64"),createdAt:r,expiresAt:r+s,used:!1,paired:null,ignored:[]};return Os(bn(o),c),{...c,live:!0,code:Iu({deviceId:n,folderId:t,machine:e,secret:l,expiresAt:c.expiresAt})}}function Uu(n=process.env){try{je.unlinkSync(bn(n))}catch{}}async function Pr({client:n,env:e=process.env,now:t=Date.now(),log:s=()=>{}}){let o=Ft(e,t);if(!o)return{state:"idle"};if(o.used)return{state:"paired",paired:o.paired};if(t>=o.expiresAt)return o.expiredLogged||(Os(bn(e),{...o,secret:null,expiredLogged:!0,live:void 0}),s("syncthing: the pairing code expired unused; nothing was accepted")),{state:"expired"};let r=Buffer.from(o.secret,"base64"),i=await n.pendingDevices(),a=new Set(o.ignored||[]),l=new Set(o.dismissed||[]),c=[...o.knocked||[]],u=!1;for(let[d,f]of Object.entries(i||{})){if(!/^[A-Z2-7]{56}$/.test(jt(d)))continue;let h=String(f&&f.name||""),p=h.match(ju);if(!(p&&ko.timingSafeEqual(Buffer.from(p[1]),Buffer.from(Na(r,{joinerId:d,firstId:o.deviceId,folderId:o.folderId}))))){let k=So(h).slice(0,80),w=c.find(x=>Ce(x.deviceId,d));if(w?(w.name!==k||w.at!==(f&&f.time||w.at))&&(w.name=k,w.at=f&&f.time||w.at,w.seen=new Date(t).toISOString(),u=!0):(c.push({deviceId:d,name:k,at:f&&f.time||new Date(t).toISOString(),seen:new Date(t).toISOString()}),u=!0),a.has(jt(d))||(a.add(jt(d)),u=!0,s("syncthing: a device named “"+k+"” ("+$n(d)+") asked to connect without this pairing code’s proof; not accepted")),!l.has(jt(d))){l.add(jt(d)),u=!0;try{await n.dismissPendingDevice(d),s("syncthing: cleared Syncthing’s pending record of "+$n(d)+" once, so it can knock again with its current name")}catch{}}continue}let g=So(h)||"other Mac",S={deviceId:d,name:g,at:new Date(t).toISOString(),how:"code"};return Os(bn(e),{...o,live:void 0,secret:null,used:!0,paired:S,ignored:[...a],dismissed:[...l],knocked:c.filter(k=>!Ce(k.deviceId,d))}),await Up(n,{id:d,name:g,folderId:o.folderId,env:e}),s("syncthing: paired with "+g+" ("+$n(d)+") by code; sharing the Leftoff folder with it"),{state:"accepted",paired:S}}return u&&Os(bn(e),{...o,live:void 0,ignored:[...a],dismissed:[...l],knocked:c}),{state:"waiting",knocked:c.length}}async function Up(n,{id:e,name:t,folderId:s=Ee,env:o}){await Ca(n,{id:e,name:t,env:o});let r=await n.folder(s);r&&!(r.devices||[]).some(i=>Ce(i.deviceID,e))&&await n.patchFolder(s,{devices:[...r.devices||[],{deviceID:e,introducedBy:"",encryptionPassword:""}]})}async function jr(n,{env:e=process.env}={}){let t=Nr(bn(e));if(!t||t.used||!(t.knocked||[]).length)return[];let[s,o]=await Promise.all([n.devices().catch(()=>[]),n.pendingDevices().catch(()=>({}))]),r=[];for(let i of t.knocked){if(s.some(c=>Ce(c.deviceID,i.deviceId))||t.paired&&Ce(t.paired.deviceId,i.deviceId))continue;let a=Object.entries(o||{}).find(([c])=>Ce(c,i.deviceId));if(!a&&!(Date.parse(i.seen||i.at)>Date.now()-30*6e4))continue;let l=a&&So(a[1]&&a[1].name).slice(0,80)||i.name;r.push({deviceId:i.deviceId,short:$n(i.deviceId),name:l||$n(i.deviceId),at:a&&a[1]&&a[1].time||i.at,pending:!!a})}return r}async function Fr(n,{deviceId:e,env:t=process.env,now:s=Date.now(),log:o=()=>{}}){let i=(await jr(n,{env:t})).find(c=>Ce(c.deviceId,e)||e&&c.name===e);if(!i)throw new ot("That Mac didn’t ask to connect during this pairing (or it’s already added). Make a new code and try again.",{code:"not_knocked"});let a=Nr(bn(t))||{},l={deviceId:i.deviceId,name:i.name,at:new Date(s).toISOString(),how:"manual"};return Os(bn(t),{...a,live:void 0,secret:null,used:!0,paired:l,knocked:(a.knocked||[]).filter(c=>!Ce(c.deviceId,i.deviceId))}),await Up(n,{id:i.deviceId,name:i.name,folderId:a.folderId||Ee,env:t}),o("syncthing: accepted "+i.name+" ("+i.short+") by an explicit click, without the pairing proof; sharing only the Leftoff folder with it"),{accepted:!0,paired:l}}async function Ia(n,{peerId:e,folderId:t=Ee}){let s=await n.connections().catch(()=>({})),o=Object.entries(s||{}).find(([l])=>Ce(l,e));if(!o||!o[1]||!o[1].connected)return{connected:!1,accepted:!1,state:"disconnected"};let r=await n.completion(e,t).catch(()=>null),i=r&&r.remoteState;if(i==="valid")return{connected:!0,accepted:!0,state:"valid"};if(i)return{connected:!0,accepted:!1,state:i};let a=await n.dbStatus(t).catch(()=>null);return{connected:!0,accepted:!!(r&&a&&a.globalFiles>0),state:"unknown"}}function Hu({first:n,name:e}){return["On "+n+", open http://127.0.0.1:8384 (Syncthing).","Click Add Device on the “"+e+"” wants to connect banner, then Save.","On that device’s Sharing tab, tick Leftoff, then Save."]}function Nk(n,e){let t=n&&n[Ee];return[t&&t.lastFile&&t.lastFile.at,t&&t.lastScan,e&&e.stateChanged].filter(o=>o&&!/^0001-/.test(o)).sort().slice(-1)[0]||null}async function sn({env:n=process.env,root:e=null,platform:t}={}){let s=vn(n),o={installed:s.installed,via:s.via,brew:!!s.brew,running:!1,version:null,reason:null,folder:null,peers:[],others:0,suggestedPath:null,nested:null,knocked:[]},r=mt({env:n,platform:t});if(r.cfg&&!s.installed&&(o.installed=!0,o.via="config"),!r.ok)return o.reason=r.reason,o;try{await r.client.ping(),o.running=!0}catch(a){return o.reason=a.message,o}let i=r.client;try{let[a,l,c,u]=await Promise.all([i.version().catch(()=>null),i.systemStatus(),i.folders(),i.devices()]);o.version=a&&a.version,o.myID=l.myID,o.others=c.filter(f=>f.id!==Ee).length,o.suggestedPath=Lr(c,n),e&&(o.nested=Is(e,c));let d=c.find(f=>f.id===Ee);if(d){let[f,h,p,m,g]=await Promise.all([i.dbStatus().catch(()=>null),i.folderErrors(),i.statsFolder().catch(()=>({})),i.connections().catch(()=>({})),i.statsDevice().catch(()=>({}))]);o.folder={id:d.id,label:d.label,path:d.path,display:Me(d.path,n),isCurrent:!!e&&ce.resolve(d.path)===ce.resolve(e),paused:!!d.paused,state:f?f.state:"unknown",globalFiles:f?f.globalFiles:null,inSyncFiles:f?f.inSyncFiles:null,needFiles:f?f.needFiles:null,errors:[...(h||[]).map(S=>({path:S.path,error:S.error})),...f&&f.error?[{path:"",error:f.error}]:[]].slice(0,10),lastSync:Nk(p,f)};for(let S of d.devices||[]){if(Ce(S.deviceID,l.myID))continue;let k=u.find(y=>Ce(y.deviceID,S.deviceID))||{},w=m[S.deviceID]||{},x=null,v=null;if(w.connected)try{let y=await i.completion(S.deviceID);x=y&&y.completion!=null?Math.max(0,Math.min(100,Math.round(y.completion))):null,v=y&&y.remoteState||null}catch{}let b=!v||v==="valid";o.peers.push({name:k.name||$n(S.deviceID),short:$n(S.deviceID),connected:!!w.connected&&b,notSharing:!!w.connected&&!b,lastSeen:(g[S.deviceID]||{}).lastSeen||null,completion:b?x:null})}o.knocked=await jr(i,{env:n}).catch(()=>[])}}catch(a){o.reason=a.message}return o}function Bu(n){if(!n)return"Waiting for Syncthing…";let e=n.globalFiles||0,t=n.inSyncFiles!=null?n.inSyncFiles:Math.max(0,e-(n.needFiles||0));return e?n.needFiles>0||n.state==="syncing"?"Syncing "+t+"/"+e+" files…":"In sync · "+e+" files":"Waiting for the first files…"}var Ee,Ap,Tp,Mu,Cr,_p,Ep,At,Ak,Tk,Op,Me,_a,xa,yo,ot,jt,Ce,$n,Aa,Lp,Rp,Lu,Ta,Nu,Fp,Or,kn,Pu,ju,Fu,So,bn,vo=J(()=>{Ee="leftoff",Ap="Leftoff",Tp=900*1e3,Mu=[["install","syncthing"],["services","start","syncthing"]],Cr="brew install syncthing && brew services start syncthing",_p="https://syncthing.net/downloads/",Ep="https://brew.sh",At=(n=process.env)=>n.HOME||bk.homedir(),Ak=n=>{try{return je.statSync(n),!0}catch{return!1}},Tk=n=>{try{return je.statSync(n).isDirectory()}catch{return!1}},Op=n=>new Promise(e=>setTimeout(e,n)),Me=(n,e=process.env)=>{let t=At(e);return n&&(n===t?"~":n.startsWith(t+ce.sep)?"~"+n.slice(t.length):n)},_a=(n,e=process.env)=>n==="~"?At(e):String(n).startsWith("~/")?ce.join(At(e),String(n).slice(2)):String(n);xa=n=>String(n).replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&apos;/g,"'").replace(/&amp;/g,"&"),yo=(n,e)=>{let t=n.match(new RegExp("\\b"+e+'="([^"]*)"'));return t?xa(t[1]):null};ot=class extends Error{constructor(e,{status:t=0,code:s="syncthing"}={}){super(e),this.status=t,this.code=s}},jt=n=>String(n||"").toUpperCase().replace(/[^A-Z2-7]/g,""),Ce=(n,e)=>!!n&&!!e&&jt(n)===jt(e),$n=n=>String(n||"").split("-")[0]||"",Aa=class n{constructor({url:e,apiKey:t,timeoutMs:s=6e3,folderId:o=Ee,managedDevices:r=()=>[],log:i=null}={}){if(!e)throw new ot("Syncthing’s GUI address isn’t readable from its config",{code:"no_address"});this.url=e.replace(/\/$/,""),Object.defineProperty(this,"apiKey",{value:t,enumerable:!1}),this.timeoutMs=s,this.folderId=o,this.managedDevices=r,this.calls=[],this.log=i}static fromConfig(e,t={}){if(!e||!e.gui)throw new ot("Syncthing hasn’t written its settings yet",{code:"no_config"});if(!e.gui.apiKey)throw new ot("Syncthing’s config has no API key yet",{code:"no_api_key"});return new n({url:e.gui.url,apiKey:e.gui.apiKey,...t})}req(e,t,s,{timeoutMs:o}={}){let r=new URL(this.url+t),i=r.protocol==="https:"?xk:vk,a=s===void 0?null:Buffer.from(JSON.stringify(s));return e!=="GET"&&this.calls.push({method:e,path:r.pathname+r.search}),new Promise((l,c)=>{let u=i.request(r,{method:e,headers:{"X-API-Key":this.apiKey||"",accept:"application/json",...a?{"content-type":"application/json","content-length":a.length}:{}},rejectUnauthorized:!1,timeout:o||this.timeoutMs},d=>{let f=[];d.on("data",h=>f.push(h)),d.on("end",()=>{let h=Buffer.concat(f).toString("utf8");if(d.statusCode>=400){let p=d.statusCode===401||d.statusCode===403?"Syncthing refused Leftoff’s request (its API key changed? Restart Syncthing and try again)":"Syncthing answered "+d.statusCode+(h?": "+h.trim().slice(0,160):"");return c(new ot(p,{status:d.statusCode,code:d.statusCode===403||d.statusCode===401?"syncthing_forbidden":d.statusCode===404?"syncthing_not_found":"syncthing_error"}))}if(!h)return l(null);try{l(JSON.parse(h))}catch{l(h)}})});u.on("timeout",()=>u.destroy(new ot("Syncthing didn’t answer in time",{code:"syncthing_timeout"}))),u.on("error",d=>c(d instanceof ot?d:new ot(d.code==="ECONNREFUSED"?"Syncthing isn’t running":"Can’t reach Syncthing ("+(d.code||d.message)+")",{code:d.code==="ECONNREFUSED"?"syncthing_down":"syncthing_unreachable"}))),a&&u.write(a),u.end()})}ping(){return this.req("GET","/rest/system/ping",void 0,{timeoutMs:2500})}systemStatus(){return this.req("GET","/rest/system/status")}version(){return this.req("GET","/rest/system/version")}folders(){return this.req("GET","/rest/config/folders")}devices(){return this.req("GET","/rest/config/devices")}async folder(e=this.folderId){try{return await this.req("GET","/rest/config/folders/"+encodeURIComponent(e))}catch(t){if(t.status===404)return null;throw t}}defaultsFolder(){return this.req("GET","/rest/config/defaults/folder").catch(()=>({}))}defaultsDevice(){return this.req("GET","/rest/config/defaults/device").catch(()=>({}))}pendingDevices(){return this.req("GET","/rest/cluster/pending/devices").then(e=>e||{})}pendingFolders(){return this.req("GET","/rest/cluster/pending/folders").then(e=>e||{})}completion(e,t=this.folderId){return this.req("GET","/rest/db/completion?folder="+encodeURIComponent(t)+(e?"&device="+encodeURIComponent(e):""))}dbStatus(e=this.folderId){return this.req("GET","/rest/db/status?folder="+encodeURIComponent(e))}connections(){return this.req("GET","/rest/system/connections").then(e=>e&&e.connections||{})}statsFolder(){return this.req("GET","/rest/stats/folder").then(e=>e||{})}statsDevice(){return this.req("GET","/rest/stats/device").then(e=>e||{})}folderErrors(e=this.folderId){return this.req("GET","/rest/folder/errors?folder="+encodeURIComponent(e)).then(t=>t&&t.errors||[]).catch(()=>[])}events(e=0,{timeoutS:t=1,types:s=null}={}){return this.req("GET","/rest/events?since="+e+"&timeout="+t+(s?"&events="+s.join(","):""),void 0,{timeoutMs:(t+3)*1e3}).then(o=>o||[])}assertOurFolder(e){if(e!==this.folderId)throw new ot("Leftoff never changes another Syncthing folder ("+e+")",{code:"not_ours"})}assertOurDevice(e,{self:t=null}={}){if(!(t&&Ce(e,t))&&!this.managedDevices().some(s=>Ce(s,e)))throw new ot("Leftoff never changes a Syncthing device it didn’t add",{code:"not_ours"})}async putFolder(e){return this.assertOurFolder(e.id),this.req("PUT","/rest/config/folders/"+encodeURIComponent(e.id),e)}async patchFolder(e,t){return this.assertOurFolder(e),this.req("PATCH","/rest/config/folders/"+encodeURIComponent(e),t)}async deleteFolder(e){return this.assertOurFolder(e),this.req("DELETE","/rest/config/folders/"+encodeURIComponent(e))}async setIgnores(e,t){return this.assertOurFolder(e),this.req("POST","/rest/db/ignores?folder="+encodeURIComponent(e),{ignore:t})}async putNewDevice(e){return this.assertOurDevice(e.deviceID),this.req("PUT","/rest/config/devices/"+encodeURIComponent(e.deviceID),e)}async patchDevice(e,t,{self:s=null}={}){return this.assertOurDevice(e,{self:s}),this.req("PATCH","/rest/config/devices/"+encodeURIComponent(e),t)}async reconnectDevice(e,{allow:t=null}={}){t&&Ce(e,t)||this.assertOurDevice(e);let s="?device="+encodeURIComponent(e);return await this.req("POST","/rest/system/pause"+s),await Op(150),this.req("POST","/rest/system/resume"+s)}dismissPendingDevice(e){return this.req("DELETE","/rest/cluster/pending/devices?device="+encodeURIComponent(e))}};Lp=(n,e)=>{for(let t of String(e.PATH||"").split(ce.delimiter)){if(!t)continue;let s=ce.join(t,n);try{if(je.accessSync(s,je.constants.X_OK),je.statSync(s).isFile())return s}catch{}}return null};Rp=n=>ce.join(wo(n),"syncthing.json");Lu=(n=process.env)=>ce.join(wo(n),"syncthing-status.json");Ta=["// Written by Leftoff. These stay on each Mac and never sync.","/.secret","/.daemon.pid","/.daemon.log","(?d).DS_Store","(?d)*.tmp","(?d)*.tmp-*","(?d)*.lock","(?d)/lists/.lock*"];Nu="0123456789ABCDEFGHJKMNPQRSTVWXYZ";Fp="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";Or="LO1";kn=class extends Error{constructor(e,t){super(e),this.code=t}};Pu=n=>ko.createHash("sha256").update(String(n).toUpperCase().replace(/[^0-9A-Z]/g,"")).digest("hex").slice(0,24);ju=/\(leftoff-pair ([0-9A-Z]{16})\)\s*$/,Fu=(n,e)=>String(n||"Mac").slice(0,40)+" (leftoff-pair "+e+")",So=n=>String(n||"").replace(ju,"").trim(),bn=n=>ce.join(wo(n),"syncthing-pairing.json")});import Ur from"node:fs";function Bp(n){return n==null?"":typeof n=="string"?n:Array.isArray(n)?n.map(e=>typeof e=="string"?e:e&&e.type==="text"&&e.text||"").filter(Boolean).join(`
69
+ `):typeof n=="object"&&n.text?String(n.text):""}function xo(n,{times:e=!1}={}){let t=[],s={cwd:null,gitBranch:null,sessionId:null,customTitle:null,summary:null,model:null,lines:0,skipped:0},o=new Map;for(let r of String(n||"").split(`
70
+ `)){if(!r.trim())continue;s.lines++;let i;try{i=JSON.parse(r)}catch{s.skipped++;continue}if(!i||typeof i!="object"){s.skipped++;continue}if(i.cwd&&(s.cwd=i.cwd),i.gitBranch&&(s.gitBranch=i.gitBranch),i.sessionId&&(s.sessionId=i.sessionId),i.type==="summary"&&i.summary){s.summary=i.summary;continue}if((i.type==="custom-title"||i.type==="custom_title")&&(i.customTitle||i.title)){s.customTitle=i.customTitle||i.title;continue}if(i.isSidechain||i.isMeta)continue;let a=i.message;if(!a||i.type!=="user"&&i.type!=="assistant"){i.type!=="system"&&s.skipped++;continue}let l=a.role||i.type;l==="assistant"&&typeof a.model=="string"&&a.model&&a.model!=="<synthetic>"&&(s.model=a.model);let c=a.content;if(l==="user"){let u=[],d="";if(typeof c=="string")d=c;else if(Array.isArray(c)){for(let f of c)if(f)if(f.type==="tool_result"){let h={tool_use_id:f.tool_use_id,text:Bp(f.content),isError:!!f.is_error};u.push(h);let p=o.get(f.tool_use_id);p&&(p.isError=h.isError,p.text=h.text)}else f.type==="text"&&(d+=(d?`
71
+ `:"")+(f.text||""))}if(/^\s*<(local-command-stdout|command-name|local-command-caveat)/.test(d)&&(d=""),!d&&!u.length)continue;t.push(e?{role:"user",text:d,toolUses:[],toolResults:u,ts:i.timestamp||null}:{role:"user",text:d,toolUses:[],toolResults:u})}else{let u=[],d="";for(let h of Array.isArray(c)?c:[{type:"text",text:Bp(c)}])if(h){if(h.type==="text"&&h.text)d+=(d?`
72
72
  `:"")+h.text;else if(h.type==="tool_use"){let p={tool_use_id:h.id,tool:h.name,input:h.input||{},isError:!1,text:void 0};u.push(p),h.id&&o.set(h.id,p)}}if(!d&&!u.length)continue;let f=t[t.length-1];if(f&&f.role==="assistant"&&f._mid&&f._mid===a.id){d&&(f.text+=(f.text?`
73
- `:"")+d),f.toolUses.push(...u),e&&i.timestamp&&(f.ts=i.timestamp);continue}t.push(e?{role:"assistant",text:d,toolUses:u,_mid:a.id||null,ts:i.timestamp||null}:{role:"assistant",text:d,toolUses:u,_mid:a.id||null})}}for(let r of t)delete r._mid;return{messages:t,meta:s}}function nn(n,{maxBytes:e=8*1024*1024,times:t=!1}={}){try{let s=Fr.statSync(n),o;if(s.size>e){let r=Fr.openSync(n,"r"),i=Buffer.alloc(e);Fr.readSync(r,i,0,e,s.size-e),Fr.closeSync(r),o=i.toString("utf8"),o=o.slice(o.indexOf(`
74
- `)+1)}else o=Fr.readFileSync(n,"utf8");return bo(o,{times:t})}catch{return{messages:[],meta:{missing:!0}}}}var Ur=J(()=>{});import Yn from"node:fs";import Tk from"node:os";import Hu from"node:path";import{execFile as _k}from"node:child_process";function Ck(n=process.env){return n.CLAUDE_CONFIG_DIR||Hu.join(n.HOME||Tk.homedir(),".claude")}function vo(n,{env:e=process.env,dirs:t}={}){if(!n||!Ok.test(n))return null;let s=t||[Hu.join(Ck(e),"projects")],o=null;for(let r of s){let i=[];try{i=Yn.readdirSync(r,{withFileTypes:!0}).filter(a=>a.isDirectory()).map(a=>a.name)}catch{continue}for(let a of i){let l=Hu.join(r,a,n+".jsonl");try{let c=Yn.statSync(l);(!o||c.mtimeMs>o.mtimeMs)&&(o={file:l,mtimeMs:c.mtimeMs,size:c.size})}catch{}}}return o?o.file:null}function Bu(n,{maxBytes:e=Ek}={}){try{let t=Yn.statSync(n).size,s;if(t>e){let a=Yn.openSync(n,"r"),l=Buffer.alloc(256*1024),c=Yn.readSync(a,l,0,l.length,0);Yn.closeSync(a),s=l.subarray(0,c).toString("utf8");let{meta:u}=bo(s);return{cwd:u.cwd||null,prompts:1,customTitle:u.customTitle||null,gitBranch:u.gitBranch||null,truncated:!0}}s=Yn.readFileSync(n,"utf8");let{messages:o,meta:r}=bo(s),i=o.filter(a=>a.role==="user"&&a.text&&a.text.trim()).length;return{cwd:r.cwd||null,prompts:i,customTitle:r.customTitle||null,gitBranch:r.gitBranch||null,...r.model?{model:r.model}:{}}}catch{return null}}function Up(n,e,t=5e3){return new Promise(s=>{_k(n,e,{env:{...process.env,LC_ALL:"C"},timeout:t,maxBuffer:8*1024*1024},(o,r)=>s(o&&!r?"":String(r||"")))})}function Ik(n){let e=[];for(let t of String(n||"").split(`
75
- `)){let s=t.match(/^\s*(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(.*)$/);s&&e.push({pid:Number(s[1]),ppid:Number(s[2]),tty:s[3],etime:s[4],args:s[5].trim()})}return e}function Nk(n){let e=new Map;for(let t of String(n||"").split(`
76
- `)){let s=t.match(/^\s*(\d+)\s+(.*)$/);s&&e.set(Number(s[1]),s[2].trim())}return e}function Hp(n,e){let t=Ca(e||"").toLowerCase(),s=Ca(String(n.args||"").split(/\s+/)[0]).toLowerCase();if(/^claude(\s|$)/.test(t)||s==="claude")return!0;let o=String(n.args||"").split(/\s+/);return/^(node|bun)$/.test(s)&&(Ca(o[1])==="claude"||/@anthropic-ai\/claude-code\/cli\.m?js$/.test(o[1]||""))}function Mk(n,e,t=new Map){let s=String(n).replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=new RegExp("(?:^|\\s)(?:--resume|-r|--session-id)(?:=|\\s+)"+s+"(?=\\s|$)"),r=e.filter(l=>o.test(l.args)&&Hp(l,t.get(l.pid))),i=new Set(r.map(l=>l.pid)),a=r.filter(l=>!r.some(c=>c.ppid===l.pid&&i.has(c.pid)));return a.length===1?a[0]:null}async function Fp(n,{platform:e=process.platform,runner:t=Up}={}){let s=new Map,o=n.filter(a=>Number.isInteger(a)&&a>1);if(!o.length)return s;if(e==="linux"){for(let a of o)try{s.set(a,Yn.readlinkSync("/proc/"+a+"/cwd"))}catch{}return s}let r=await t("lsof",["-a","-d","cwd","-p",o.join(","),"-Fpn"]),i=null;for(let a of r.split(`
77
- `))a.startsWith("p")?i=Number(a.slice(1)):a.startsWith("n")&&i&&s.set(i,a.slice(1));return s}function Dk(n,e,t=new Map){let s=new Map(e.map(r=>[r.pid,r])),o=s.get(n);for(let r=0;o&&r<12;r++){let i=Ca(t.get(o.pid)||String(o.args).split(/\s+/)[0]);for(let[a,l]of Lk)if(a.test(i))return{...Bn(l==="kitty"?{TERM:"xterm-kitty"}:{TERM_PROGRAM:l}),source:"ps"};if(/^tmux/i.test(i))return null;o=s.get(o.ppid)}return null}async function Bp(n,{cwd:e=null,claimedPids:t=new Set,env:s=process.env,platform:o=process.platform,runner:r=Up,now:i=Date.now(),transcriptDirs:a}={}){let l={via:[]},c=vo(n,{env:s,dirs:a});if(c){l.transcriptPath=c;let p=Bu(c);p&&(p.cwd&&(l.cwd=p.cwd),l.prompts=p.prompts,p.customTitle&&(l.customTitle=p.customTitle),l.via.push("transcript"))}let u=e||l.cwd||null,d=Ik(await r("ps",["-axo","pid=,ppid=,tty=,etime=,command="]));if(!d.length)return l;let f=Nk(await r("ps",["-axo","pid=,comm="])),h=Mk(n,d,f);if(h)l.via.push("ps-args");else if(u){let p=d.filter(k=>Hp(k,f.get(k.pid))&&!t.has(k.pid)),m=p.filter(k=>!p.some(w=>w.ppid===k.pid)),g=await Fp(m.map(k=>k.pid),{platform:o,runner:r}),S=m.filter(k=>g.get(k.pid)===u);S.length===1&&(h=S[0],l.via.push("ps-cwd"))}if(h){let p=Ar(h.etime);l.proc={pid:h.pid,tty:ku(h.tty),pidStartedAt:p==null?null:G(i-p*1e3),source:"daemon"};let m=Dk(h.pid,d,f);if(m&&(l.term=m,l.via.push("ps-term")),!l.cwd&&!e){let g=(await Fp([h.pid],{platform:o,runner:r})).get(h.pid);g&&(l.cwd=g)}}return l}var Ek,Ok,Ca,Lk,Gu=J(()=>{Ur();Te();Ek=32*1024*1024,Ok=/^[0-9A-Za-z][0-9A-Za-z_-]{6,80}$/;Ca=n=>String(n||"").split("/").pop();Lk=[[/^ghostty$/i,"ghostty"],[/^iterm2?$/i,"iTerm.app"],[/^terminal$/i,"Apple_Terminal"],[/^wezterm(-gui)?$/i,"WezTerm"],[/^kitty$/i,"kitty"]]});import Wn from"node:fs";import zn from"node:path";import Rk from"node:crypto";function Gr(n){let e=Pk[n]||n;if(!Br.includes(e))throw Object.assign(new Error("kind is machine, repo, session or agent"),{code:"bad_kind"});return e}function Kr(n){return Kp.test(String(n))}function Na(n){return String(n).replace(Kp,"")}function $r(n){let e=String(n??"").normalize("NFC").replace(/\s+/g," ").trim();if(!e)throw Object.assign(new Error("a space needs a name"),{code:"bad_space"});if(e.length>32)throw Object.assign(new Error("a space name is at most 32 characters"),{code:"bad_space"});if(!/^[\p{L}\p{N}][\p{L}\p{N} ._&'-]*$/u.test(e))throw Object.assign(new Error("a space name is letters, numbers, spaces and . _ & ' -"),{code:"bad_space"});return e}function $p(n,e){let t=String(e??"").trim();if(!t||t.length>400||/[\u0000-\u001F]/.test(t))throw Object.assign(new Error("bad "+n.replace(/s$/,"")+" key"),{code:"bad_key"});return t}function Cs(){return{schema:1,machines:{},repos:{},sessions:{},agents:{}}}function jk(n){if(n==null)return null;if(typeof n=="string")return{space:n,at:"1970-01-01T00:00:00.000Z",by:""};if(typeof n!="object")return null;let e=n.space==null?null:String(n.space),t=typeof n.at=="string"&&!Number.isNaN(Date.parse(n.at))?n.at:"1970-01-01T00:00:00.000Z";return{space:e,at:t,by:typeof n.by=="string"?n.by:""}}function Yp(n){let e=Cs();if(!n||typeof n!="object")return e;for(let t of Br){let s=n[t];if(!(!s||typeof s!="object"))for(let[o,r]of Object.entries(s)){let i=jk(r);i&&(e[t][o]=i)}}return e}function Fk(n,e){if(!n)return e;if(!e)return n;let t=Date.parse(n.at),s=Date.parse(e.at);return t!==s?t>s?n:e:n.by!==e.by?n.by>e.by?n:e:n.space==null!=(e.space==null)?n.space==null?e:n:String(n.space)>=String(e.space)?n:e}function Uk(...n){let e=Cs();for(let t of n){let s=Yp(t);for(let o of Br)for(let[r,i]of Object.entries(s[o]))e[o][r]=Fk(e[o][r],i)}return e}function Is(n){let e=[],t=[];try{t=Wn.readdirSync(n)}catch{}for(let r of t)(r===Hr||r.startsWith("spaces.")&&r.endsWith(".json")&&Kr(r)&&Na(r)===Hr)&&e.push(r);let s=[],o=[];for(let r of e.sort())try{s.push(JSON.parse(Wn.readFileSync(zn.join(n,r),"utf8"))),r!==Hr&&o.push(r)}catch{}return{map:Uk(...s),conflicts:o}}function Ma(n,e){Wn.mkdirSync(zn.dirname(n),{recursive:!0});let t=zn.join(zn.dirname(n),".syncthing."+zn.basename(n)+"."+process.pid+"."+Rk.randomBytes(4).toString("hex")+".tmp");Wn.writeFileSync(t,e),Wn.renameSync(t,n)}function Hk(n,e,{removeConflicts:t=[]}={}){let s=Yp(e),o={schema:1};for(let r of Br)o[r]=Object.fromEntries(Object.entries(s[r]).sort(([i],[a])=>i.localeCompare(a)));Ma(zn.join(n,Hr),JSON.stringify(o,null,2)+`
78
- `);for(let r of t)try{Wn.unlinkSync(zn.join(n,r))}catch{}}function Ku(n,e,{machine:t="",now:s=Date.now()}={}){let{map:o,conflicts:r}=Is(n),i={at:new Date(s).toISOString(),by:t},a=e(o,i);return Hk(n,o,{removeConflicts:r}),a===void 0?o:a}function jt(n,e){let t={};for(let[s,o]of Object.entries(n&&n[e]||{}))o&&o.space!=null&&(t[s]=o.space);return t}function sn(n,e=[]){let t=new Set;for(let s of Br)for(let o of Object.values(jt(n,s)))t.add(o);for(let s of e)t.add(Jn(n,s));return Ao([...t])}function Ao(n){return[...new Set(n.filter(Boolean))].sort((e,t)=>e==="Work"?-1:t==="Work"?1:e.localeCompare(t,"en",{sensitivity:"base"})||e.localeCompare(t))}function Jn(n,e){let t=n&&n.machines&&n.machines[e];return t&&t.space!=null?t.space:e||"Unsorted"}function Gk(n,{repoKey:e,repoName:t,repoLabel:s}={}){let o=jt(n,"repos");if(e&&o[e]!=null)return{key:e,space:o[e]};let r=[s,t,Bk(e)].filter(Boolean).map(Gp);for(let i of r)for(let[a,l]of Object.entries(o))if(Gp(a)===i)return{key:a,space:l};return null}function qn(n,e,{repoKey:t,repoName:s,repoLabel:o,machine:r}={}){let i=jt(n,"sessions");if(e&&e.id&&i[e.id]!=null)return{space:i[e.id],source:"session",key:e.id};let a=e&&(e.agent&&e.agent.name||(typeof e.id=="string"&&e.id.startsWith("agent:")?e.id.split(":")[1]:null));if(a){let d=jt(n,"agents");if(d[a]!=null)return{space:d[a],source:"agent",key:a}}let l=!a&&Gk(n,{repoKey:t||e&&(e.repoKey||e.repo&&e.repo.root||e.cwd),repoName:s||e&&(e.repoName||e.repo&&e.repo.name),repoLabel:o||e&&e.repoLabel});if(l)return{space:l.space,source:"repo",key:l.key};let c=r||(e&&a?e.agent&&e.agent.puller:e&&e.machine)||"",u=n&&n.machines&&n.machines[c];return u&&u.space!=null?{space:u.space,source:"machine",key:c}:{space:c||"Unsorted",source:"default",key:c}}function zp(n,{home:e=""}={}){let t=[e,"/root"].filter(Boolean).map(i=>i.replace(/\/+$/,"")),s=i=>t.includes(i)||/^\/(Users|home)\/[^/]+$/.test(i)||/^[A-Za-z]:\\Users\\[^\\]+$/.test(i),o=i=>{for(let l of t)if(l&&(i===l||i.startsWith(l+"/")))return"~"+i.slice(l.length);let a=/^\/(Users|home)\/[^/]+(?=\/|$)/.exec(i);return a?"~"+i.slice(a[0].length):i},r=new Map;for(let i of n||[]){let a=String(i&&i.key||"").trim().replace(/\/+$/,"")||(i&&i.key?"/":""),l=String(i&&i.name||"").trim();if(!l||["/",".","..","~","no-repo"].includes(l)||/^[\\/]+$/.test(l)||!a||a==="/"||a==="~"||s(a))continue;let c=Date.parse(i&&i.at||"")||0,u=l.toLowerCase(),d=r.get(u);if(d&&d.t>=c)continue;let f=a.includes("/")?o(a.slice(0,a.lastIndexOf("/"))||"/"):"";r.set(u,{name:l,parent:f,lastAt:c?new Date(c).toISOString():null,t:c})}return[...r.values()].sort((i,a)=>a.t-i.t||i.name.localeCompare(a.name)).map(({t:i,...a})=>a)}var Hr,xo,Br,Pk,Kp,Gp,Bk,Ia,Ns=J(()=>{Hr="spaces.json",xo=["Work","Personal"],Br=["machines","repos","sessions","agents"],Pk={machine:"machines",repo:"repos",session:"sessions",agent:"agents"};Kp=/\.sync-conflict-(\d{8})-(\d{6})-([A-Za-z0-9]{1,16})(?=\.[^.]+$|$)/;Gp=n=>String(n||"").toLowerCase(),Bk=n=>{let e=String(n||"").split(/[\\/]+/).filter(Boolean);return e.length?e[e.length-1]:""};Ia=class{constructor(e,t,{now:s=()=>Date.now()}={}){this.root=e,this.machine=t,this.now=s,this.cache=null,this.checkedAt=0}sig(){let e=[];try{e=Wn.readdirSync(this.root).filter(t=>t===Hr||t.startsWith("spaces.")&&Kr(t))}catch{}return e.sort().map(t=>{try{let s=Wn.statSync(zn.join(this.root,t));return t+":"+s.mtimeMs+":"+s.size}catch{return t}}).join("|")}map({fresh:e=!1}={}){let t=Date.now();if(!e&&this.cache&&t-this.checkedAt<2e3)return this.cache.map;let s=this.sig();if(this.checkedAt=t,!e&&this.cache&&this.cache.sig===s)return this.cache.map;let{map:o,conflicts:r}=Is(this.root);return this.cache={sig:s,map:o,conflicts:r},o}update(e){let t=Ku(this.root,e,{machine:this.machine,now:this.now()});return this.cache=null,t}resolve(e,t){return qn(this.map(),e,t)}machineSpace(e=this.machine){return Jn(this.map(),e)}}});import ae from"node:fs";import ie from"node:path";import $u from"node:crypto";function Vn(n,e){ae.mkdirSync(ie.dirname(n),{recursive:!0});let t=ie.join(ie.dirname(n),"."+ie.basename(n)+"."+process.pid+"."+$u.randomBytes(4).toString("hex")+".tmp");ae.writeFileSync(t,e),ae.renameSync(t,n)}function vn(n){try{return ae.readdirSync(n,{withFileTypes:!0}).filter(e=>e.isDirectory()&&!e.name.startsWith(".")).map(e=>e.name)}catch{return[]}}function Wp(n){return ae.existsSync(ie.join(n,"session.md"))||ae.existsSync(ie.join(n,"checkpoints"))}function Jp(n,e,t,s){let o=ie.join(be(n).spool,e,String(t||"unknown").replace(/[^A-Za-z0-9_-]/g,"_"));ae.mkdirSync(o,{recursive:!0});let r=ie.join(o,Date.now()+"-"+$u.randomBytes(4).toString("hex")+".json");return ae.writeFileSync(r,JSON.stringify(s)+`
79
- `,{flag:"wx"}),r}var xt,xn=J(()=>{Te();xe();Ns();xt=class{constructor(e,t="unknown"){this.root=e,this.machine=t,this.p=be(e)}get mySessions(){return ie.join(this.p.sessions,this.machine)}get mySpool(){return ie.join(this.p.spool,this.machine)}get myDigests(){return ie.join(this.p.digests,this.machine)}sessionDir(e){return e.folder||(e.folder=this.findFolderFor(e.id)||this.machine+"/"+Ph(e)),ie.join(this.p.sessions,e.folder)}findFolderFor(e){let t="_"+br(e);for(let s of vn(this.mySessions)){if(!s.endsWith(t))continue;let o=ie.join(this.mySessions,s,"session.md");if(!ae.existsSync(o))return this.machine+"/"+s;try{if(Ye(ae.readFileSync(o,"utf8")).data.id===e)return this.machine+"/"+s}catch{}}return null}writeSession(e,t){let s=ie.join(this.sessionDir(e),"session.md");return Vn(s,Vh(e,t)),s}writeCheckpoint(e,t){let s=ie.join(this.sessionDir(e),"checkpoints");ae.mkdirSync(s,{recursive:!0});for(let o=0;o<50;o++){let r=ie.join(s,gu(t.createdAt,o)),i={...t,machine:t.machine||e.machine||this.machine,id:t.id&&o===0?t.id:ca(t.sessionId,t.createdAt,o),file:ie.basename(r)};try{return ae.writeFileSync(r,Jh({...i,backend:e.backend}),{flag:"wx"}),i}catch(a){if(a.code!=="EEXIST")throw a}}throw new Error("could not find a free checkpoint file name")}loadSessionDir(e,t,s){let o=[];try{for(let i of ae.readdirSync(ie.join(e,"checkpoints")).filter(a=>a.endsWith(".md")).sort())try{let a=qh(ae.readFileSync(ie.join(e,"checkpoints",i),"utf8"));a.file=i,!a.machine&&s&&(a.machine=s),a.sessionId&&a.createdAt&&o.push(a)}catch{}}catch{}let r=null;try{r=Xh(ae.readFileSync(ie.join(e,"session.md"),"utf8"))}catch{}if(!r||!r.id){if(!o.length)return null;let i=o[o.length-1];r={id:i.sessionId,title:i.sessionTitle,titleSource:i.sessionTitle?"custom":null,cwd:i.cwd,machine:i.machine||s,repo:i.repo?{name:i.repo,branch:i.branch}:null,capture:"mod",status:"stale",startedAt:o[0].createdAt,lastActivityAt:i.createdAt,turns:0,turnsSinceCheckpoint:0,backgroundTasks:0,lastHeartbeatAt:i.createdAt,needsRewrite:!0}}return!r.machine&&s&&(r.machine=s),r.folder=t,{session:r,checkpoints:o}}loadAll({machine:e}={}){let t=[];for(let s of vn(this.p.sessions)){let o=ie.join(this.p.sessions,s);if(Wp(o)){let r=this.loadSessionDir(o,s,null);r&&(!e||r.session.machine===e)&&t.push(r);continue}if(!(e&&s!==e))for(let r of vn(o)){let i=this.loadSessionDir(ie.join(o,r),s+"/"+r,s);i&&t.push(i)}}return t}machines(){let e=new Set([this.machine]);for(let t of vn(this.p.sessions))Wp(ie.join(this.p.sessions,t))||e.add(t);for(let t of vn(this.p.digests))e.add(t);return[...e].sort()}writeDigest(e,t){let s=ie.join(this.p.digests,e+".md");Ma(s,t);for(let o of this.digestCopies(e))if(o!==e+".md")try{ae.unlinkSync(ie.join(this.p.digests,o))}catch{}return s}writeMachineDigest(e,t){let s=ie.join(this.myDigests,e+".md");return Vn(s,t),s}digestCopies(e){let t=[];try{t=ae.readdirSync(this.p.digests)}catch{}return t.filter(s=>s===e+".md"||s.startsWith(e+".")&&s.endsWith(".md")&&Kr(s)&&Na(s)===e+".md")}readSharedDigest(e){let t=null,s=this.digestCopies(e);for(let o of s)try{let r=ie.join(this.p.digests,o),i=ae.readFileSync(r,"utf8"),a=Ye(i).data||{},l=Date.parse(a.generated_at)||ae.statSync(r).mtimeMs;(!t||l>t.t||l===t.t&&o===e+".md")&&(t={t:l,file:o,markdown:i,by:a.machine||null})}catch{}return t?{machine:null,shared:!0,by:t.by,file:t.file,duplicates:s.length-1,markdown:t.markdown}:null}readDigest(e,t){if(t&&t!=="all")try{return{machine:t,shared:!1,markdown:ae.readFileSync(ie.join(this.p.digests,t,e+".md"),"utf8")}}catch{return null}let s=this.readSharedDigest(e);if(s||t==="all")return s;for(let o of[this.machine,...vn(this.p.digests).filter(r=>r!==this.machine)])try{return{machine:o,shared:!1,markdown:ae.readFileSync(ie.join(this.p.digests,o,e+".md"),"utf8")}}catch{}return null}listDigests(){let e=[],t=new Set;try{for(let o of ae.readdirSync(this.p.digests)){let r=/^(\d{4}-\d{2}-\d{2})\.(?:.*\.)?md$/.exec(o);r&&(o===r[1]+".md"||Na(o)===r[1]+".md")&&t.add(r[1])}}catch{}for(let o of t)e.push({date:o,machine:null,shared:!0});for(let o of vn(this.p.digests))try{for(let r of ae.readdirSync(ie.join(this.p.digests,o)))/^\d{4}-\d{2}-\d{2}\.md$/.test(r)&&e.push({date:r.slice(0,10),machine:o})}catch{}let s=o=>o.shared?0:o.machine===this.machine?1:2;return e.sort((o,r)=>r.date.localeCompare(o.date)||s(o)-s(r)||String(o.machine).localeCompare(String(r.machine)))}stampAlive(e={},t=Date.now()){let s=ie.join(this.p.digests,".machines",this.machine.replace(/[^\w.-]/g,"_")+".json");Ma(s,JSON.stringify({machine:this.machine,seenAt:new Date(t).toISOString(),...e},null,2)+`
80
- `)}aliveMachines(){let e=[],t=ie.join(this.p.digests,".machines"),s=[];try{s=ae.readdirSync(t).filter(o=>o.endsWith(".json")&&!o.startsWith(".")&&!Kr(o))}catch{}for(let o of s)try{let r=JSON.parse(ae.readFileSync(ie.join(t,o),"utf8"));r&&r.machine&&e.push(r)}catch{}return e}writeCatchup(e,t){ae.mkdirSync(this.p.catchups,{recursive:!0});let s=String(e).replace(/\.md$/,"");for(let o=0;o<50;o++){let r=o?s+"-"+(o+1):s,i=ie.join(this.p.catchups,r+".md");try{let a=ie.join(this.p.catchups,"."+r+"."+process.pid+"."+$u.randomBytes(4).toString("hex")+".tmp");ae.writeFileSync(a,t);try{try{ae.linkSync(a,i)}catch(l){if(l.code==="EEXIST")throw l;ae.writeFileSync(i,t,{flag:"wx"})}}finally{try{ae.unlinkSync(a)}catch{}}return{name:r,file:i}}catch(a){if(a.code!=="EEXIST")throw a}}throw new Error("could not find a free catch-up file name")}readCatchup(e){if(!/^\d{4}-\d{2}-\d{2}T\d{4}(?:-\d+)?$/.test(String(e||"")))return null;try{return{name:e,markdown:ae.readFileSync(ie.join(this.p.catchups,e+".md"),"utf8")}}catch{return null}}listCatchups(){let e=[];try{e=ae.readdirSync(this.p.catchups).filter(o=>/^\d{4}-\d{2}-\d{2}T\d{4}(?:-\d+)?\.md$/.test(o))}catch{return[]}let t=[];for(let o of e){let r={};try{let i=ae.openSync(ie.join(this.p.catchups,o),"r");try{let a=Buffer.alloc(8192),l=ae.readSync(i,a,0,a.length,0);r=Ye(a.slice(0,l).toString("utf8")).data||{}}finally{ae.closeSync(i)}}catch{}t.push({name:o.slice(0,-3),generatedAt:r.generated_at?G(r.generated_at):null,from:r.window_from?G(r.window_from):null,to:r.window_to?G(r.window_to):null,label:r.window_label||null,sessions:typeof r.sessions=="number"?r.sessions:null,needsYou:Array.isArray(r.needs_you)?r.needs_you.length:0,repos:r.repos==="all"||r.repos==null?"all":[].concat(r.repos).map(String),machine:r.made_on==null?null:String(r.made_on),model:r.model==null?null:String(r.model)})}let s=o=>Date.parse(o.generatedAt||"")||0;return t.sort((o,r)=>s(r)-s(o)||r.name.localeCompare(o.name))}lastCatchupAt(){let e=this.listCatchups()[0];if(!e)return null;let t=Date.parse(e.to||e.generatedAt||"");return isNaN(t)?null:t}readSpool(){let e=[];for(let t of vn(this.mySpool)){let s=ie.join(this.mySpool,t),o=[];try{o=ae.readdirSync(s)}catch{}for(let r of o){if(!r.endsWith(".json"))continue;let i=ie.join(s,r),a=null;try{a=ae.readFileSync(i,"utf8")}catch{continue}e.push({file:i,t:Number(r.split("-")[0])||0,body:a})}}return e.sort((t,s)=>t.t-s.t||t.file.localeCompare(s.file))}spoolCount(){let e=0;for(let t of vn(this.mySpool))try{e+=ae.readdirSync(ie.join(this.mySpool,t)).filter(s=>s.endsWith(".json")).length}catch{}return e}}});import Kk from"node:path";function qp(n){let e=[],t=!1;for(let s of Yu(n).split(`
73
+ `:"")+d),f.toolUses.push(...u),e&&i.timestamp&&(f.ts=i.timestamp);continue}t.push(e?{role:"assistant",text:d,toolUses:u,_mid:a.id||null,ts:i.timestamp||null}:{role:"assistant",text:d,toolUses:u,_mid:a.id||null})}}for(let r of t)delete r._mid;return{messages:t,meta:s}}function on(n,{maxBytes:e=8*1024*1024,times:t=!1}={}){try{let s=Ur.statSync(n),o;if(s.size>e){let r=Ur.openSync(n,"r"),i=Buffer.alloc(e);Ur.readSync(r,i,0,e,s.size-e),Ur.closeSync(r),o=i.toString("utf8"),o=o.slice(o.indexOf(`
74
+ `)+1)}else o=Ur.readFileSync(n,"utf8");return xo(o,{times:t})}catch{return{messages:[],meta:{missing:!0}}}}var Hr=J(()=>{});import Wn from"node:fs";import Ik from"node:os";import Gu from"node:path";import{execFile as Mk}from"node:child_process";function Rk(n=process.env){return n.CLAUDE_CONFIG_DIR||Gu.join(n.HOME||Ik.homedir(),".claude")}function Ao(n,{env:e=process.env,dirs:t}={}){if(!n||!Dk.test(n))return null;let s=t||[Gu.join(Rk(e),"projects")],o=null;for(let r of s){let i=[];try{i=Wn.readdirSync(r,{withFileTypes:!0}).filter(a=>a.isDirectory()).map(a=>a.name)}catch{continue}for(let a of i){let l=Gu.join(r,a,n+".jsonl");try{let c=Wn.statSync(l);(!o||c.mtimeMs>o.mtimeMs)&&(o={file:l,mtimeMs:c.mtimeMs,size:c.size})}catch{}}}return o?o.file:null}function Ku(n,{maxBytes:e=Lk}={}){try{let t=Wn.statSync(n).size,s;if(t>e){let a=Wn.openSync(n,"r"),l=Buffer.alloc(256*1024),c=Wn.readSync(a,l,0,l.length,0);Wn.closeSync(a),s=l.subarray(0,c).toString("utf8");let{meta:u}=xo(s);return{cwd:u.cwd||null,prompts:1,customTitle:u.customTitle||null,gitBranch:u.gitBranch||null,truncated:!0}}s=Wn.readFileSync(n,"utf8");let{messages:o,meta:r}=xo(s),i=o.filter(a=>a.role==="user"&&a.text&&a.text.trim()).length;return{cwd:r.cwd||null,prompts:i,customTitle:r.customTitle||null,gitBranch:r.gitBranch||null,...r.model?{model:r.model}:{}}}catch{return null}}function Kp(n,e,t=5e3){return new Promise(s=>{Mk(n,e,{env:{...process.env,LC_ALL:"C"},timeout:t,maxBuffer:8*1024*1024},(o,r)=>s(o&&!r?"":String(r||"")))})}function Pk(n){let e=[];for(let t of String(n||"").split(`
75
+ `)){let s=t.match(/^\s*(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(.*)$/);s&&e.push({pid:Number(s[1]),ppid:Number(s[2]),tty:s[3],etime:s[4],args:s[5].trim()})}return e}function jk(n){let e=new Map;for(let t of String(n||"").split(`
76
+ `)){let s=t.match(/^\s*(\d+)\s+(.*)$/);s&&e.set(Number(s[1]),s[2].trim())}return e}function $p(n,e){let t=Ma(e||"").toLowerCase(),s=Ma(String(n.args||"").split(/\s+/)[0]).toLowerCase();if(/^claude(\s|$)/.test(t)||s==="claude")return!0;let o=String(n.args||"").split(/\s+/);return/^(node|bun)$/.test(s)&&(Ma(o[1])==="claude"||/@anthropic-ai\/claude-code\/cli\.m?js$/.test(o[1]||""))}function Fk(n,e,t=new Map){let s=String(n).replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=new RegExp("(?:^|\\s)(?:--resume|-r|--session-id)(?:=|\\s+)"+s+"(?=\\s|$)"),r=e.filter(l=>o.test(l.args)&&$p(l,t.get(l.pid))),i=new Set(r.map(l=>l.pid)),a=r.filter(l=>!r.some(c=>c.ppid===l.pid&&i.has(c.pid)));return a.length===1?a[0]:null}async function Gp(n,{platform:e=process.platform,runner:t=Kp}={}){let s=new Map,o=n.filter(a=>Number.isInteger(a)&&a>1);if(!o.length)return s;if(e==="linux"){for(let a of o)try{s.set(a,Wn.readlinkSync("/proc/"+a+"/cwd"))}catch{}return s}let r=await t("lsof",["-a","-d","cwd","-p",o.join(","),"-Fpn"]),i=null;for(let a of r.split(`
77
+ `))a.startsWith("p")?i=Number(a.slice(1)):a.startsWith("n")&&i&&s.set(i,a.slice(1));return s}function Hk(n,e,t=new Map){let s=new Map(e.map(r=>[r.pid,r])),o=s.get(n);for(let r=0;o&&r<12;r++){let i=Ma(t.get(o.pid)||String(o.args).split(/\s+/)[0]);for(let[a,l]of Uk)if(a.test(i))return{...Kn(l==="kitty"?{TERM:"xterm-kitty"}:{TERM_PROGRAM:l}),source:"ps"};if(/^tmux/i.test(i))return null;o=s.get(o.ppid)}return null}async function zp(n,{cwd:e=null,claimedPids:t=new Set,env:s=process.env,platform:o=process.platform,runner:r=Kp,now:i=Date.now(),transcriptDirs:a}={}){let l={via:[]},c=Ao(n,{env:s,dirs:a});if(c){l.transcriptPath=c;let p=Ku(c);p&&(p.cwd&&(l.cwd=p.cwd),l.prompts=p.prompts,p.customTitle&&(l.customTitle=p.customTitle),l.via.push("transcript"))}let u=e||l.cwd||null,d=Pk(await r("ps",["-axo","pid=,ppid=,tty=,etime=,command="]));if(!d.length)return l;let f=jk(await r("ps",["-axo","pid=,comm="])),h=Fk(n,d,f);if(h)l.via.push("ps-args");else if(u){let p=d.filter(k=>$p(k,f.get(k.pid))&&!t.has(k.pid)),m=p.filter(k=>!p.some(w=>w.ppid===k.pid)),g=await Gp(m.map(k=>k.pid),{platform:o,runner:r}),S=m.filter(k=>g.get(k.pid)===u);S.length===1&&(h=S[0],l.via.push("ps-cwd"))}if(h){let p=Tr(h.etime);l.proc={pid:h.pid,tty:vu(h.tty),pidStartedAt:p==null?null:G(i-p*1e3),source:"daemon"};let m=Hk(h.pid,d,f);if(m&&(l.term=m,l.via.push("ps-term")),!l.cwd&&!e){let g=(await Gp([h.pid],{platform:o,runner:r})).get(h.pid);g&&(l.cwd=g)}}return l}var Lk,Dk,Ma,Uk,$u=J(()=>{Hr();Te();Lk=32*1024*1024,Dk=/^[0-9A-Za-z][0-9A-Za-z_-]{6,80}$/;Ma=n=>String(n||"").split("/").pop();Uk=[[/^ghostty$/i,"ghostty"],[/^iterm2?$/i,"iTerm.app"],[/^terminal$/i,"Apple_Terminal"],[/^wezterm(-gui)?$/i,"WezTerm"],[/^kitty$/i,"kitty"]]});import qn from"node:fs";import Jn from"node:path";import Bk from"node:crypto";function Kr(n){let e=Gk[n]||n;if(!Gr.includes(e))throw Object.assign(new Error("kind is machine, repo, session or agent"),{code:"bad_kind"});return e}function $r(n){return Wp.test(String(n))}function Da(n){return String(n).replace(Wp,"")}function zr(n){let e=String(n??"").normalize("NFC").replace(/\s+/g," ").trim();if(!e)throw Object.assign(new Error("a space needs a name"),{code:"bad_space"});if(e.length>32)throw Object.assign(new Error("a space name is at most 32 characters"),{code:"bad_space"});if(!/^[\p{L}\p{N}][\p{L}\p{N} ._&'-]*$/u.test(e))throw Object.assign(new Error("a space name is letters, numbers, spaces and . _ & ' -"),{code:"bad_space"});return e}function Jp(n,e){let t=String(e??"").trim();if(!t||t.length>400||/[\u0000-\u001F]/.test(t))throw Object.assign(new Error("bad "+n.replace(/s$/,"")+" key"),{code:"bad_key"});return t}function Ms(){return{schema:1,machines:{},repos:{},sessions:{},agents:{}}}function Kk(n){if(n==null)return null;if(typeof n=="string")return{space:n,at:"1970-01-01T00:00:00.000Z",by:""};if(typeof n!="object")return null;let e=n.space==null?null:String(n.space),t=typeof n.at=="string"&&!Number.isNaN(Date.parse(n.at))?n.at:"1970-01-01T00:00:00.000Z";return{space:e,at:t,by:typeof n.by=="string"?n.by:""}}function qp(n){let e=Ms();if(!n||typeof n!="object")return e;for(let t of Gr){let s=n[t];if(!(!s||typeof s!="object"))for(let[o,r]of Object.entries(s)){let i=Kk(r);i&&(e[t][o]=i)}}return e}function $k(n,e){if(!n)return e;if(!e)return n;let t=Date.parse(n.at),s=Date.parse(e.at);return t!==s?t>s?n:e:n.by!==e.by?n.by>e.by?n:e:n.space==null!=(e.space==null)?n.space==null?e:n:String(n.space)>=String(e.space)?n:e}function zk(...n){let e=Ms();for(let t of n){let s=qp(t);for(let o of Gr)for(let[r,i]of Object.entries(s[o]))e[o][r]=$k(e[o][r],i)}return e}function Ls(n){let e=[],t=[];try{t=qn.readdirSync(n)}catch{}for(let r of t)(r===Br||r.startsWith("spaces.")&&r.endsWith(".json")&&$r(r)&&Da(r)===Br)&&e.push(r);let s=[],o=[];for(let r of e.sort())try{s.push(JSON.parse(qn.readFileSync(Jn.join(n,r),"utf8"))),r!==Br&&o.push(r)}catch{}return{map:zk(...s),conflicts:o}}function Ra(n,e){qn.mkdirSync(Jn.dirname(n),{recursive:!0});let t=Jn.join(Jn.dirname(n),".syncthing."+Jn.basename(n)+"."+process.pid+"."+Bk.randomBytes(4).toString("hex")+".tmp");qn.writeFileSync(t,e),qn.renameSync(t,n)}function Yk(n,e,{removeConflicts:t=[]}={}){let s=qp(e),o={schema:1};for(let r of Gr)o[r]=Object.fromEntries(Object.entries(s[r]).sort(([i],[a])=>i.localeCompare(a)));Ra(Jn.join(n,Br),JSON.stringify(o,null,2)+`
78
+ `);for(let r of t)try{qn.unlinkSync(Jn.join(n,r))}catch{}}function zu(n,e,{machine:t="",now:s=Date.now()}={}){let{map:o,conflicts:r}=Ls(n),i={at:new Date(s).toISOString(),by:t},a=e(o,i);return Yk(n,o,{removeConflicts:r}),a===void 0?o:a}function Ut(n,e){let t={};for(let[s,o]of Object.entries(n&&n[e]||{}))o&&o.space!=null&&(t[s]=o.space);return t}function rn(n,e=[]){let t=new Set;for(let s of Gr)for(let o of Object.values(Ut(n,s)))t.add(o);for(let s of e)t.add(Vn(n,s));return _o([...t])}function _o(n){return[...new Set(n.filter(Boolean))].sort((e,t)=>e==="Work"?-1:t==="Work"?1:e.localeCompare(t,"en",{sensitivity:"base"})||e.localeCompare(t))}function Vn(n,e){let t=n&&n.machines&&n.machines[e];return t&&t.space!=null?t.space:e||"Unsorted"}function Jk(n,{repoKey:e,repoName:t,repoLabel:s}={}){let o=Ut(n,"repos");if(e&&o[e]!=null)return{key:e,space:o[e]};let r=[s,t,Wk(e)].filter(Boolean).map(Yp);for(let i of r)for(let[a,l]of Object.entries(o))if(Yp(a)===i)return{key:a,space:l};return null}function Xn(n,e,{repoKey:t,repoName:s,repoLabel:o,machine:r}={}){let i=Ut(n,"sessions");if(e&&e.id&&i[e.id]!=null)return{space:i[e.id],source:"session",key:e.id};let a=e&&(e.agent&&e.agent.name||(typeof e.id=="string"&&e.id.startsWith("agent:")?e.id.split(":")[1]:null));if(a){let d=Ut(n,"agents");if(d[a]!=null)return{space:d[a],source:"agent",key:a}}let l=!a&&Jk(n,{repoKey:t||e&&(e.repoKey||e.repo&&e.repo.root||e.cwd),repoName:s||e&&(e.repoName||e.repo&&e.repo.name),repoLabel:o||e&&e.repoLabel});if(l)return{space:l.space,source:"repo",key:l.key};let c=r||(e&&a?e.agent&&e.agent.puller:e&&e.machine)||"",u=n&&n.machines&&n.machines[c];return u&&u.space!=null?{space:u.space,source:"machine",key:c}:{space:c||"Unsorted",source:"default",key:c}}function Vp(n,{home:e=""}={}){let t=[e,"/root"].filter(Boolean).map(i=>i.replace(/\/+$/,"")),s=i=>t.includes(i)||/^\/(Users|home)\/[^/]+$/.test(i)||/^[A-Za-z]:\\Users\\[^\\]+$/.test(i),o=i=>{for(let l of t)if(l&&(i===l||i.startsWith(l+"/")))return"~"+i.slice(l.length);let a=/^\/(Users|home)\/[^/]+(?=\/|$)/.exec(i);return a?"~"+i.slice(a[0].length):i},r=new Map;for(let i of n||[]){let a=String(i&&i.key||"").trim().replace(/\/+$/,"")||(i&&i.key?"/":""),l=String(i&&i.name||"").trim();if(!l||["/",".","..","~","no-repo"].includes(l)||/^[\\/]+$/.test(l)||!a||a==="/"||a==="~"||s(a))continue;let c=Date.parse(i&&i.at||"")||0,u=l.toLowerCase(),d=r.get(u);if(d&&d.t>=c)continue;let f=a.includes("/")?o(a.slice(0,a.lastIndexOf("/"))||"/"):"";r.set(u,{name:l,parent:f,lastAt:c?new Date(c).toISOString():null,t:c})}return[...r.values()].sort((i,a)=>a.t-i.t||i.name.localeCompare(a.name)).map(({t:i,...a})=>a)}var Br,To,Gr,Gk,Wp,Yp,Wk,La,Ds=J(()=>{Br="spaces.json",To=["Work","Personal"],Gr=["machines","repos","sessions","agents"],Gk={machine:"machines",repo:"repos",session:"sessions",agent:"agents"};Wp=/\.sync-conflict-(\d{8})-(\d{6})-([A-Za-z0-9]{1,16})(?=\.[^.]+$|$)/;Yp=n=>String(n||"").toLowerCase(),Wk=n=>{let e=String(n||"").split(/[\\/]+/).filter(Boolean);return e.length?e[e.length-1]:""};La=class{constructor(e,t,{now:s=()=>Date.now()}={}){this.root=e,this.machine=t,this.now=s,this.cache=null,this.checkedAt=0}sig(){let e=[];try{e=qn.readdirSync(this.root).filter(t=>t===Br||t.startsWith("spaces.")&&$r(t))}catch{}return e.sort().map(t=>{try{let s=qn.statSync(Jn.join(this.root,t));return t+":"+s.mtimeMs+":"+s.size}catch{return t}}).join("|")}map({fresh:e=!1}={}){let t=Date.now();if(!e&&this.cache&&t-this.checkedAt<2e3)return this.cache.map;let s=this.sig();if(this.checkedAt=t,!e&&this.cache&&this.cache.sig===s)return this.cache.map;let{map:o,conflicts:r}=Ls(this.root);return this.cache={sig:s,map:o,conflicts:r},o}update(e){let t=zu(this.root,e,{machine:this.machine,now:this.now()});return this.cache=null,t}resolve(e,t){return Xn(this.map(),e,t)}machineSpace(e=this.machine){return Vn(this.map(),e)}}});import ae from"node:fs";import ie from"node:path";import Yu from"node:crypto";function Zn(n,e){ae.mkdirSync(ie.dirname(n),{recursive:!0});let t=ie.join(ie.dirname(n),"."+ie.basename(n)+"."+process.pid+"."+Yu.randomBytes(4).toString("hex")+".tmp");ae.writeFileSync(t,e),ae.renameSync(t,n)}function An(n){try{return ae.readdirSync(n,{withFileTypes:!0}).filter(e=>e.isDirectory()&&!e.name.startsWith(".")).map(e=>e.name)}catch{return[]}}function Xp(n){return ae.existsSync(ie.join(n,"session.md"))||ae.existsSync(ie.join(n,"checkpoints"))}function Zp(n,e,t,s){let o=ie.join(be(n).spool,e,String(t||"unknown").replace(/[^A-Za-z0-9_-]/g,"_"));ae.mkdirSync(o,{recursive:!0});let r=ie.join(o,Date.now()+"-"+Yu.randomBytes(4).toString("hex")+".json");return ae.writeFileSync(r,JSON.stringify(s)+`
79
+ `,{flag:"wx"}),r}var Tt,Tn=J(()=>{Te();xe();Ds();Tt=class{constructor(e,t="unknown"){this.root=e,this.machine=t,this.p=be(e)}get mySessions(){return ie.join(this.p.sessions,this.machine)}get mySpool(){return ie.join(this.p.spool,this.machine)}get myDigests(){return ie.join(this.p.digests,this.machine)}sessionDir(e){return e.folder||(e.folder=this.findFolderFor(e.id)||this.machine+"/"+Hh(e)),ie.join(this.p.sessions,e.folder)}findFolderFor(e){let t="_"+br(e);for(let s of An(this.mySessions)){if(!s.endsWith(t))continue;let o=ie.join(this.mySessions,s,"session.md");if(!ae.existsSync(o))return this.machine+"/"+s;try{if(Ye(ae.readFileSync(o,"utf8")).data.id===e)return this.machine+"/"+s}catch{}}return null}writeSession(e,t){let s=ie.join(this.sessionDir(e),"session.md");return Zn(s,ep(e,t)),s}writeCheckpoint(e,t){let s=ie.join(this.sessionDir(e),"checkpoints");ae.mkdirSync(s,{recursive:!0});for(let o=0;o<50;o++){let r=ie.join(s,wu(t.createdAt,o)),i={...t,machine:t.machine||e.machine||this.machine,id:t.id&&o===0?t.id:fa(t.sessionId,t.createdAt,o),file:ie.basename(r)};try{return ae.writeFileSync(r,Zh({...i,backend:e.backend}),{flag:"wx"}),i}catch(a){if(a.code!=="EEXIST")throw a}}throw new Error("could not find a free checkpoint file name")}loadSessionDir(e,t,s){let o=[];try{for(let i of ae.readdirSync(ie.join(e,"checkpoints")).filter(a=>a.endsWith(".md")).sort())try{let a=Qh(ae.readFileSync(ie.join(e,"checkpoints",i),"utf8"));a.file=i,!a.machine&&s&&(a.machine=s),a.sessionId&&a.createdAt&&o.push(a)}catch{}}catch{}let r=null;try{r=tp(ae.readFileSync(ie.join(e,"session.md"),"utf8"))}catch{}if(!r||!r.id){if(!o.length)return null;let i=o[o.length-1];r={id:i.sessionId,title:i.sessionTitle,titleSource:i.sessionTitle?"custom":null,cwd:i.cwd,machine:i.machine||s,repo:i.repo?{name:i.repo,branch:i.branch}:null,capture:"mod",status:"stale",startedAt:o[0].createdAt,lastActivityAt:i.createdAt,turns:0,turnsSinceCheckpoint:0,backgroundTasks:0,lastHeartbeatAt:i.createdAt,needsRewrite:!0}}return!r.machine&&s&&(r.machine=s),r.folder=t,{session:r,checkpoints:o}}loadAll({machine:e}={}){let t=[];for(let s of An(this.p.sessions)){let o=ie.join(this.p.sessions,s);if(Xp(o)){let r=this.loadSessionDir(o,s,null);r&&(!e||r.session.machine===e)&&t.push(r);continue}if(!(e&&s!==e))for(let r of An(o)){let i=this.loadSessionDir(ie.join(o,r),s+"/"+r,s);i&&t.push(i)}}return t}machines(){let e=new Set([this.machine]);for(let t of An(this.p.sessions))Xp(ie.join(this.p.sessions,t))||e.add(t);for(let t of An(this.p.digests))e.add(t);return[...e].sort()}writeDigest(e,t){let s=ie.join(this.p.digests,e+".md");Ra(s,t);for(let o of this.digestCopies(e))if(o!==e+".md")try{ae.unlinkSync(ie.join(this.p.digests,o))}catch{}return s}writeMachineDigest(e,t){let s=ie.join(this.myDigests,e+".md");return Zn(s,t),s}digestCopies(e){let t=[];try{t=ae.readdirSync(this.p.digests)}catch{}return t.filter(s=>s===e+".md"||s.startsWith(e+".")&&s.endsWith(".md")&&$r(s)&&Da(s)===e+".md")}readSharedDigest(e){let t=null,s=this.digestCopies(e);for(let o of s)try{let r=ie.join(this.p.digests,o),i=ae.readFileSync(r,"utf8"),a=Ye(i).data||{},l=Date.parse(a.generated_at)||ae.statSync(r).mtimeMs;(!t||l>t.t||l===t.t&&o===e+".md")&&(t={t:l,file:o,markdown:i,by:a.machine||null})}catch{}return t?{machine:null,shared:!0,by:t.by,file:t.file,duplicates:s.length-1,markdown:t.markdown}:null}readDigest(e,t){if(t&&t!=="all")try{return{machine:t,shared:!1,markdown:ae.readFileSync(ie.join(this.p.digests,t,e+".md"),"utf8")}}catch{return null}let s=this.readSharedDigest(e);if(s||t==="all")return s;for(let o of[this.machine,...An(this.p.digests).filter(r=>r!==this.machine)])try{return{machine:o,shared:!1,markdown:ae.readFileSync(ie.join(this.p.digests,o,e+".md"),"utf8")}}catch{}return null}listDigests(){let e=[],t=new Set;try{for(let o of ae.readdirSync(this.p.digests)){let r=/^(\d{4}-\d{2}-\d{2})\.(?:.*\.)?md$/.exec(o);r&&(o===r[1]+".md"||Da(o)===r[1]+".md")&&t.add(r[1])}}catch{}for(let o of t)e.push({date:o,machine:null,shared:!0});for(let o of An(this.p.digests))try{for(let r of ae.readdirSync(ie.join(this.p.digests,o)))/^\d{4}-\d{2}-\d{2}\.md$/.test(r)&&e.push({date:r.slice(0,10),machine:o})}catch{}let s=o=>o.shared?0:o.machine===this.machine?1:2;return e.sort((o,r)=>r.date.localeCompare(o.date)||s(o)-s(r)||String(o.machine).localeCompare(String(r.machine)))}stampAlive(e={},t=Date.now()){let s=ie.join(this.p.digests,".machines",this.machine.replace(/[^\w.-]/g,"_")+".json");Ra(s,JSON.stringify({machine:this.machine,seenAt:new Date(t).toISOString(),...e},null,2)+`
80
+ `)}aliveMachines(){let e=[],t=ie.join(this.p.digests,".machines"),s=[];try{s=ae.readdirSync(t).filter(o=>o.endsWith(".json")&&!o.startsWith(".")&&!$r(o))}catch{}for(let o of s)try{let r=JSON.parse(ae.readFileSync(ie.join(t,o),"utf8"));r&&r.machine&&e.push(r)}catch{}return e}writeCatchup(e,t){ae.mkdirSync(this.p.catchups,{recursive:!0});let s=String(e).replace(/\.md$/,"");for(let o=0;o<50;o++){let r=o?s+"-"+(o+1):s,i=ie.join(this.p.catchups,r+".md");try{let a=ie.join(this.p.catchups,"."+r+"."+process.pid+"."+Yu.randomBytes(4).toString("hex")+".tmp");ae.writeFileSync(a,t);try{try{ae.linkSync(a,i)}catch(l){if(l.code==="EEXIST")throw l;ae.writeFileSync(i,t,{flag:"wx"})}}finally{try{ae.unlinkSync(a)}catch{}}return{name:r,file:i}}catch(a){if(a.code!=="EEXIST")throw a}}throw new Error("could not find a free catch-up file name")}readCatchup(e){if(!/^\d{4}-\d{2}-\d{2}T\d{4}(?:-\d+)?$/.test(String(e||"")))return null;try{return{name:e,markdown:ae.readFileSync(ie.join(this.p.catchups,e+".md"),"utf8")}}catch{return null}}listCatchups(){let e=[];try{e=ae.readdirSync(this.p.catchups).filter(o=>/^\d{4}-\d{2}-\d{2}T\d{4}(?:-\d+)?\.md$/.test(o))}catch{return[]}let t=[];for(let o of e){let r={};try{let i=ae.openSync(ie.join(this.p.catchups,o),"r");try{let a=Buffer.alloc(8192),l=ae.readSync(i,a,0,a.length,0);r=Ye(a.slice(0,l).toString("utf8")).data||{}}finally{ae.closeSync(i)}}catch{}t.push({name:o.slice(0,-3),generatedAt:r.generated_at?G(r.generated_at):null,from:r.window_from?G(r.window_from):null,to:r.window_to?G(r.window_to):null,label:r.window_label||null,sessions:typeof r.sessions=="number"?r.sessions:null,needsYou:Array.isArray(r.needs_you)?r.needs_you.length:0,repos:r.repos==="all"||r.repos==null?"all":[].concat(r.repos).map(String),machine:r.made_on==null?null:String(r.made_on),model:r.model==null?null:String(r.model)})}let s=o=>Date.parse(o.generatedAt||"")||0;return t.sort((o,r)=>s(r)-s(o)||r.name.localeCompare(o.name))}lastCatchupAt(){let e=this.listCatchups()[0];if(!e)return null;let t=Date.parse(e.to||e.generatedAt||"");return isNaN(t)?null:t}readSpool(){let e=[];for(let t of An(this.mySpool)){let s=ie.join(this.mySpool,t),o=[];try{o=ae.readdirSync(s)}catch{}for(let r of o){if(!r.endsWith(".json"))continue;let i=ie.join(s,r),a=null;try{a=ae.readFileSync(i,"utf8")}catch{continue}e.push({file:i,t:Number(r.split("-")[0])||0,body:a})}}return e.sort((t,s)=>t.t-s.t||t.file.localeCompare(s.file))}spoolCount(){let e=0;for(let t of An(this.mySpool))try{e+=ae.readdirSync(ie.join(this.mySpool,t)).filter(s=>s.endsWith(".json")).length}catch{}return e}}});import qk from"node:path";function Qp(n){let e=[],t=!1;for(let s of Wu(n).split(`
81
81
  `)){if(/^\s*(```|~~~)/.test(s)){t=!t;continue}t||e.push(s)}return e.join(`
82
- `)}function $k(n,e){let t=Yu(n).trimEnd(),s=Ms(e).replace(/[*_`]+/g,"");if(!s)return t;let o=s.split(`
82
+ `)}function Vk(n,e){let t=Wu(n).trimEnd(),s=Rs(e).replace(/[*_`]+/g,"");if(!s)return t;let o=s.split(`
83
83
  `)[0].slice(0,40).toLowerCase(),r=[],i="";for(let c=0;c<t.length;c++){let u=t[c];if(!/[*_`]/.test(u)){if(/\s/.test(u)){if(i.endsWith(" ")||!i)continue;i+=" ",r.push(c);continue}i+=u.toLowerCase(),r.push(c)}}let a=i.lastIndexOf(o);if(a>=0)return t.slice(0,r[a]).trimEnd();let l=t.split(/\n\s*\n/);return/[??][\s*_`)"'”’]*$/.test(l[l.length-1]||"")&&l.pop(),l.join(`
84
84
 
85
- `).trimEnd()}function Vp(n=[],{question:e=null,max:t=3,width:s=280}={}){let o=[],r=[],i=!0;for(let a=n.length-1;a>=0&&o.length<t;a--){let l=n[a];if(Yk(l))break;if(!l||l.role!=="assistant"||!l.text||!l.text.trim())continue;let c=l.text;i&&(r=!e||zu.test(e)?Wu(c):[],e&&(c=$k(c,e)),i=!1);let u=qp(c).split(`
86
- `).map(d=>d.trim()).filter(d=>d&&!/^#{1,6}\s*$/.test(d)&&!/^[-*_]{3,}$/.test(d));for(let d=u.length-1;d>=0&&o.length<t;d--)o.unshift(on(u[d].replace(/^#{1,6}\s+/,""),s))}return!o.length&&!r.length?null:{lines:o,options:r}}function Wu(n){let e=qp(n).split(`
87
- `).map(a=>a.trim()).filter(Boolean),t=[],s=[];for(let a of e){let l=a.match(/^(?:\*\*)?(?:option\s+)?([0-9]{1,2}|[a-fA-F])[.):](?:\*\*)?\s+(.+)$/i);l?t.push({key:l[1],text:l[2]}):t.length&&(s.splice(0,s.length,...t),t=[])}let o=t.length?t:s;if(o.length<2||o.length>6)return[];let r=o.map(a=>a.key.toLowerCase());return r.every((a,l)=>/\d/.test(a)?Number(a)===Number(r[0])+l:a.charCodeAt(0)===r[0].charCodeAt(0)+l)?o.map(a=>on(a.key+". "+a.text.replace(/\*\*/g,"").replace(/\s+[—–-]\s+.*$/,"").replace(/[:.]$/,"").trim(),60)):[]}function Jk(n={},{cwd:e}={}){let t=n.input||{},s=String(n.tool||"tool"),o=zk(t.file_path||t.notebook_path||t.path,e);switch(s){case"Edit":case"MultiEdit":case"NotebookEdit":return"Edited "+(o||"a file");case"Write":return"Wrote "+(o||"a file");case"Read":return"Read "+(o||"a file");case"Bash":{let r=Ms(t.command);return Wk.test(r)?"Ran tests":/^git\s+commit\b/.test(r)?"Committed":/^git\s+push\b/.test(r)?"Pushed":"Ran "+on(t.description?Ms(t.description):r||"a command",48)}case"Grep":return"Searched “"+on(Ms(t.pattern),32)+"”";case"Glob":return"Listed "+on(Ms(t.pattern),32);case"LS":return"Listed "+(o||"a folder");case"WebFetch":{let r="";try{r=new URL(t.url).hostname}catch{}return"Fetched "+(r||"a page")}case"WebSearch":return"Searched the web for “"+on(Ms(t.query),32)+"”";case"TodoWrite":return"Updated the to-do list";case"Task":case"Agent":return"Ran an agent"+(t.description?": "+on(Ms(t.description),36):"");case"AskUserQuestion":return"Asked you";case"ExitPlanMode":return"Proposed a plan";default:return"Used "+on(s.replace(/^mcp__/,"").replace(/__/g," "),40)}}function Xp(n=[],{limit:e=20,cwd:t=null,textMax:s=6e3}={}){let o=[];for(let r of n){if(!r)continue;if(r.role==="user"){if(!r.text||!r.text.trim())continue;o.push({role:"user",text:on(r.text.trim(),s),chips:[]});continue}let i=(r.text||"").trim(),a=(r.toolUses||[]).map(c=>({label:Jk(c,{cwd:t}),error:!!c.isError})),l=o[o.length-1];(!l||l.role!=="assistant"||i&&l.chips.length)&&(l={role:"assistant",text:"",chips:[]},o.push(l)),i&&(l.text=on(l.text?l.text+`
88
-
89
- `+i:i,s));for(let c of a){let u=l.chips[l.chips.length-1];u&&u.label===c.label&&u.error===c.error?u.n=(u.n||1)+1:l.chips.push(c)}}return o.slice(-Math.max(1,e))}var on,Yu,Ms,zu,Yk,zk,Wk,Zp=J(()=>{on=(n,e)=>(n=String(n??""),n.length>e?n.slice(0,e-1).trimEnd()+"…":n),Yu=n=>String(n??"").replace(/\r\n?/g,`
90
- `),Ms=n=>Yu(n).replace(/\s+/g," ").trim();zu=/\b(which|or|options?|prefer|pick|choose|one of)\b/i,Yk=n=>n&&n.role==="user"&&n.text&&n.text.trim();zk=(n,e)=>{let t=String(n||"");return t?e&&t.startsWith(String(e).replace(/\/$/,"")+"/")?t.slice(String(e).replace(/\/$/,"").length+1):t.includes("/")?Kk.basename(t):t:""},Wk=/(^|[\s;&|(])((p?npm|yarn|pnpm|bun)\s+(run\s+)?test\b|node\s+--test\b|(npx\s+)?(jest|vitest|mocha|pytest|playwright\s+test)\b|go\s+test\b|cargo\s+test\b|rspec\b|make\s+test\b|swift\s+test\b|xcodebuild\s+test\b|node\s+test\/)/});import{spawn as qk,execFileSync as Vk}from"node:child_process";import Xk from"node:fs";import Ju from"node:path";function tb(){if(process.env.CHECKPOINTS_NO_CLAUDE)return!1;try{return Vk(process.platform==="win32"?"where":"which",["claude"],{stdio:"ignore"}),!0}catch{return!1}}function nb(n){let e=n.summarizer||{},t=String(e.provider||e.mode||"auto").toLowerCase();return em[t]?em[t]:tb()?"claude":process.env.ANTHROPIC_API_KEY?"anthropic":"heuristic"}function sb({system:n,prompt:e,model:t},s=6e4){return new Promise((o,r)=>{let i=["-p","--model",t||"haiku","--output-format","text"];n&&i.push("--append-system-prompt",n);let a=qk("claude",i,{env:{...process.env,CHECKPOINTS_INTERNAL:"1",LEFTOFF_INTERNAL:"1"},stdio:["pipe","pipe","pipe"]}),l="",c="",u=setTimeout(()=>{a.kill("SIGKILL"),r(new Error("claude -p timed out"))},s);a.stdout.on("data",d=>l+=d),a.stderr.on("data",d=>c+=d),a.on("error",d=>{clearTimeout(u),r(d)}),a.on("close",d=>{clearTimeout(u),d===0?o({text:l.trim(),usage:null}):r(new Error("claude -p exited "+d+": "+c.slice(0,300)))}),a.stdin.end(e)})}async function qu(n,e,t,s,o){let r=await n(e,{method:"POST",headers:{"content-type":"application/json",...t},body:JSON.stringify(s),signal:AbortSignal.timeout(o)}),i=await r.json().catch(()=>({}));if(!r.ok)throw new Error(e+" -> "+r.status+": "+(i.error&&(i.error.message||i.error)||"error"));return i}function ob(n={},e=process.env){return An("anthropic",n,e)}function An(n,{apiKeyEnv:e,apiKeyFile:t,root:s}={},o=process.env){let r=Da[n]||{},i=e||r.env;if(i&&o[i])return o[i];for(let a of[t,e==="__none__"?null:r.file]){if(!a)continue;let l=Ju.isAbsolute(a)?null:gp(a,o.HOME||o.XDG_CONFIG_HOME?o:process.env);for(let c of[l,Ju.isAbsolute(a)?a:Ju.join(s||Z(),a)])if(c)try{let u=Xk.readFileSync(c,"utf8").trim();if(u)return u}catch{}}return null}async function rb(n,{system:e,prompt:t,model:s,maxTokens:o,apiKeyEnv:r,apiKeyFile:i,root:a}){let l=ob({apiKeyEnv:r,apiKeyFile:i,root:a});if(!l)throw new Error((r||"ANTHROPIC_API_KEY")+" is not set");let c=(process.env.ANTHROPIC_BASE_URL||"https://api.anthropic.com").replace(/\/$/,""),u=await qu(n,c+"/v1/messages",{"x-api-key":l,"anthropic-version":"2023-06-01"},{model:Qp[s]||s||Qp.haiku,max_tokens:o||300,system:e,messages:[{role:"user",content:t}]},3e4);return{text:(u.content||[]).filter(d=>d.type==="text").map(d=>d.text).join(`
91
- `),usage:u.usage?{inputTokens:u.usage.input_tokens,outputTokens:u.usage.output_tokens}:null}}async function tm(n,{system:e,prompt:t,model:s,maxTokens:o,baseUrl:r,key:i,cloud:a}){let l=await qu(n,r.replace(/\/$/,"")+"/api/chat",i?{authorization:"Bearer "+i}:{},{model:s,stream:!1,messages:[...e?[{role:"system",content:e}]:[],{role:"user",content:t}],options:{num_predict:a?Math.max(1024,(o||300)*4):o||300,temperature:.2}},a?6e4:12e4);return{text:l.message&&l.message.content||"",usage:{inputTokens:l.prompt_eval_count,outputTokens:l.eval_count}}}async function ib(n,{system:e,prompt:t,model:s,maxTokens:o,baseUrl:r,apiKeyEnv:i}){let a=i?process.env[i]:null,l=await qu(n,r.replace(/\/$/,"")+"/chat/completions",a?{authorization:"Bearer "+a}:{},{model:s,max_tokens:o||300,temperature:.2,messages:[...e?[{role:"system",content:e}]:[],{role:"user",content:t}]},12e4),c=l.choices&&l.choices[0];return{text:c&&c.message&&c.message.content||c&&c.text||"",usage:l.usage?{inputTokens:l.usage.prompt_tokens,outputTokens:l.usage.completion_tokens}:null}}var Qp,Zk,Qk,eb,La,Da,em,At,Ls=J(()=>{xe();Te();Qp={haiku:"claude-haiku-5-5",sonnet:"claude-sonnet-5-5",opus:"claude-opus-5-5"},Zk=["haiku","sonnet","opus",""],Qk="https://ollama.com",eb="gpt-oss:20b",La={ollama:{baseUrl:"http://localhost:11434",model:"llama3.2"},"ollama-cloud":{baseUrl:Qk,model:eb},"openai-compatible":{baseUrl:"http://localhost:1234/v1",model:"local-model"}},Da={anthropic:{env:"ANTHROPIC_API_KEY",file:".secret/anthropic-api-key"},"ollama-cloud":{env:"OLLAMA_API_KEY",file:".secret/ollama-api-key"}},em={stub:"heuristic",none:"heuristic",heuristic:"heuristic",api:"anthropic",anthropic:"anthropic",claude:"claude",ollama:"ollama","ollama-cloud":"ollama-cloud","ollama.com":"ollama-cloud",cloud:"ollama-cloud","openai-compatible":"openai-compatible",openai:"openai-compatible",lmstudio:"openai-compatible",llamacpp:"openai-compatible",vllm:"openai-compatible"};At=class{constructor(e,{fetchImpl:t,root:s}={}){this.cfg=e,this.root=s||null,this.fetchImpl=t||((...o)=>fetch(...o)),this.mode=nb(e)}get model(){let e=String(this.cfg.summarizer&&this.cfg.summarizer.model||"");return La[this.mode]&&Zk.includes(e)?La[this.mode].model:e||"haiku"}get baseUrl(){return this.cfg.summarizer&&this.cfg.summarizer.baseUrl||La[this.mode]&&La[this.mode].baseUrl||null}describe(){return{provider:this.mode,model:this.mode==="heuristic"?null:this.model,baseUrl:this.baseUrl}}async complete({system:e,prompt:t,maxTokens:s}){let o=this.cfg.summarizer||{},r={system:e,prompt:t,maxTokens:s,model:this.model,baseUrl:this.baseUrl,apiKeyEnv:o.apiKeyEnv,apiKeyFile:o.apiKeyFile,root:this.root};if(this.mode==="claude")return sb(r);if(this.mode==="anthropic")return rb(this.fetchImpl,r);if(this.mode==="ollama")return tm(this.fetchImpl,r);if(this.mode==="ollama-cloud"){let i=An("ollama-cloud",{apiKeyEnv:o.apiKeyEnv,apiKeyFile:o.apiKeyFile,root:this.root});if(!i)throw new Error((o.apiKeyEnv||"OLLAMA_API_KEY")+" is not set (create a key at https://ollama.com/settings/keys)");return tm(this.fetchImpl,{...r,key:i,cloud:!0})}if(this.mode==="openai-compatible")return ib(this.fetchImpl,r);throw new Error("no model in heuristic mode")}async checkpoint({system:e,prompt:t,messages:s,session:o,maxTokens:r=300,length:i="short"}){if(this.mode!=="heuristic")try{let a=await this.complete({system:e,prompt:t,maxTokens:r}),l=op(String(a.text||"").replace(/<think>[\s\S]*?<\/think>/gi,"").trim()),c={by:"daemon",provider:this.mode,model:this.model};return a.usage&&a.usage.inputTokens!=null&&(c.inputTokens=a.usage.inputTokens),a.usage&&a.usage.outputTokens!=null&&(c.outputTokens=a.usage.outputTokens),{...l,generator:c}}catch(a){return{...Tu(s,o,{length:i}),generator:{by:"heuristic",fallbackFrom:this.mode,error:String(a.message||a).slice(0,160)}}}return{...Tu(s,o,{length:i}),generator:{by:"heuristic"}}}async title(e){if(this.mode!=="heuristic"&&e.firstPrompt)try{let t=await this.complete({system:ip,prompt:`First prompt:
92
- `+e.firstPrompt,maxTokens:30}),s=ap(t.text);if(s)return{title:s,titleSource:"generated"}}catch{}return{title:rp(e),titleSource:"fallback"}}async paragraph(e,t){if(this.mode==="heuristic")return null;try{return(await this.complete({system:e,prompt:t,maxTokens:200})).text.trim()}catch{return null}}}});import To from"node:fs";import ab from"node:os";import Ra from"node:path";import{execFileSync as H_,spawn as lb,spawnSync as cb}from"node:child_process";import{fileURLToPath as ub}from"node:url";async function Wr({baseUrl:n=process.env.CHECKPOINTS_CLOUD_URL||Ds,fetchImpl:e=fetch,timeoutMs:t=5e3}={}){let s=n.replace(/\/$/,"");try{let o=await ja(e,s+"/api/tags",t);return{ok:!0,baseUrl:s,models:(o.models||[]).map(r=>r.name||r.model).filter(Boolean),error:null}}catch(o){return{ok:!1,baseUrl:s,models:[],error:o.cause&&o.cause.code||o.message}}}function Fa(n=[],{curated:e=zr,current:t=null}={}){let s=r=>n.some(i=>We(i,r)),o=e.map(r=>({...r,curated:!0,available:n.length?s(r.tag):null,current:!!t&&We(t,r.tag)}));for(let r of n)o.some(i=>We(i.tag,r))||o.push({tag:r,name:r,curated:!1,available:!0,recommended:!1,current:!!t&&We(t,r)});return o}function Pa(n){let e=String(n||"").trim().toLowerCase();return e?e.includes(":")?e:e+":latest":""}function We(n,e){return Pa(n)===Pa(e)}function Eo(n=[],{curated:e=Yr,current:t=null}={}){let s=n.map(i=>typeof i=="string"?{name:i}:i),o=i=>s.some(a=>We(a.name||a.model,i)),r=e.map(i=>({...i,curated:!0,installed:o(i.tag),current:!!t&&We(t,i.tag)}));for(let i of s){let a=i.name||i.model;!a||r.some(l=>We(l.tag,a))||/embed/i.test(a)||r.push({tag:a,name:a,sizeBytes:i.size||null,curated:!1,installed:!0,recommended:!1,current:!!t&&We(t,a)})}return r}function sm(n,{totalMemBytes:e=ab.totalmem()}={}){let t=n.find(r=>r.current&&r.installed);if(t)return t.tag;let s=n.find(r=>r.recommended);if(s&&s.installed)return s.tag;let o=n.find(r=>r.curated&&r.installed);if(o)return o.tag;if(e&&e<85e8){let r=n.filter(i=>i.curated).sort((i,a)=>i.ramGB-a.ramGB)[0];if(r)return r.tag}return s?s.tag:n[0]&&n[0].tag}function Ua(n,e){let t=[];return n.sizeBytes&&t.push(e(n.sizeBytes)),n.ramGB&&t.push("~"+n.ramGB+" GB RAM"),n.speed&&t.push(n.speed),t.join(" · ")}function Oo(n,e=process.env){let t=String(e.PATH||"").split(Ra.delimiter).filter(Boolean),s=process.platform==="win32"?[".exe",".cmd",".bat",""]:[""];for(let o of t)for(let r of s){let i=Ra.join(o,n+r);try{if(To.statSync(i).isFile())return To.accessSync(i,To.constants.X_OK),i}catch{}}return null}function Rs(n=process.env){let e=Oo("ollama",n);if(e)return e;if(n.CHECKPOINTS_TEST_NO_SYSTEM_BINS)return null;for(let t of["/opt/homebrew/bin/ollama","/usr/local/bin/ollama","/Applications/Ollama.app/Contents/Resources/ollama"])if(To.existsSync(t))return t;return null}async function ja(n,e,t=1500){let s=await n(e,{signal:AbortSignal.timeout(t)});if(!s.ok)throw new Error(e+" -> HTTP "+s.status);return s.json()}async function Gt({baseUrl:n=pt,fetchImpl:e=fetch,env:t=process.env}={}){let s=n.replace(/\/$/,""),o={kind:"ollama",running:!1,baseUrl:s,version:null,models:[],binary:Rs(t),error:null};try{let r=await ja(e,s+"/api/tags");o.running=!0,o.models=(r.models||[]).map(i=>({name:i.name||i.model,size:i.size,modifiedAt:i.modified_at,parameterSize:i.details&&i.details.parameter_size,quantization:i.details&&i.details.quantization_level}));try{o.version=(await ja(e,s+"/api/version")).version||null}catch{}}catch(r){o.error=r.cause&&r.cause.code||r.name||r.message}return o}async function Tn({baseUrl:n=rn,fetchImpl:e=fetch}={}){let t=n.replace(/\/$/,""),s={kind:"openai-compatible",running:!1,baseUrl:t,models:[],error:null};try{let o=await ja(e,t+"/models");s.running=!0,s.models=(o.data||[]).map(r=>r.id).filter(r=>r&&!/embed/i.test(r))}catch(o){s.error=o.cause&&o.cause.code||o.name||o.message}return s}async function Co({baseUrl:n=pt,fetchImpl:e=fetch,timeoutMs:t=2e4}={}){let s=Date.now()+t;for(;Date.now()<s;){let o=await Gt({baseUrl:n,fetchImpl:e});if(o.running)return o;await fb(500)}return Gt({baseUrl:n,fetchImpl:e})}function hb(n,e){let t=n||{layers:{},status:"",total:0,completed:0,fraction:0,done:!1,error:null};if(e.error)return{...t,error:String(e.error)};let s={...t.layers};e.digest&&e.total&&(s[e.digest]={total:e.total,completed:Math.min(e.completed||0,e.total)});let o=Object.values(s),r=o.reduce((c,u)=>c+u.total,0),i=o.reduce((c,u)=>c+u.completed,0),a=e.status==="success",l=a?1:r?Math.min(.999,i/r):t.fraction;return{layers:s,status:e.status||t.status,total:r,completed:i,fraction:Math.max(t.fraction,l),done:a,error:null}}async function Io({tag:n,baseUrl:e=pt,fetchImpl:t=fetch,onProgress:s=()=>{},signal:o}={}){let r=await t(e.replace(/\/$/,"")+"/api/pull",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({model:n,stream:!0}),signal:o});if(!r.ok||!r.body){let u="HTTP "+r.status;try{let d=await r.json();d.error&&(u=d.error)}catch{}throw new Error("pull "+n+" failed: "+u)}let i=null,a="",l=new TextDecoder,c=u=>{if(!u.trim())return;let d;try{d=JSON.parse(u)}catch{return}if(i=hb(i,d),i.error)throw new Error("pull "+n+" failed: "+i.error);s(i)};for await(let u of r.body){a+=l.decode(u,{stream:!0});let d;for(;(d=a.indexOf(`
93
- `))>=0;)c(a.slice(0,d)),a=a.slice(d+1)}if(c(a),!i||!i.done)throw new Error("pull "+n+" ended early (last status: "+(i&&i.status||"none")+")");return i}async function Ps(n,{fetchImpl:e,transcriptFile:t=db,root:s}={}){let o=co(ke,{summarizer:n}),{messages:r,meta:i}=bo(To.readFileSync(t,"utf8")),a={id:i.sessionId||"sample",cwd:i.cwd||"/work/larry",title:i.summary||"Sample session",repo:{root:i.cwd,branch:i.gitBranch}},l=ha({session:a,messages:r,cfg:o}),c=new At(o,{...e?{fetchImpl:e}:{},...s?{root:s}:{}}),u=Date.now(),d=await c.checkpoint({...l,messages:r,session:a}),f=Date.now()-u,h=d.generator&&d.generator.by==="heuristic"&&d.generator.fallbackFrom;return{ok:!h,doing:d.doing,stoppedAt:d.stoppedAt,next:d.next,parseOk:d.parseOk!==!1,ms:f,generator:d.generator,error:h?d.generator.error:null}}function om(n=process.platform,e=process.env){let t={id:"script",label:"Official install script",shell:"curl -fsSL https://ollama.com/install.sh | sh",cmd:["sh","-c","curl -fsSL https://ollama.com/install.sh | sh"]};if(n==="darwin"){let s=[];return Oo("brew",e)&&s.push({id:"brew",label:"Homebrew",shell:"brew install ollama",cmd:["brew","install","ollama"]}),s.push({...t,note:"installs the Ollama app"}),s.push({id:"manual",label:"Download the app myself",shell:"open https://ollama.com/download",cmd:null,url:"https://ollama.com/download"}),s}return n==="linux"?[{...t,note:"asks for sudo; sets up a systemd service"},{id:"manual",label:"Install it myself",shell:"see https://ollama.com/download/linux",cmd:null,url:"https://ollama.com/download/linux"}]:n==="win32"?[{id:"manual",label:"PowerShell installer",shell:"irm https://ollama.com/install.ps1 | iex",cmd:null,url:"https://ollama.com/download/windows"}]:[{id:"manual",label:"Install it myself",shell:"see https://ollama.com/download",cmd:null,url:"https://ollama.com/download"}]}function Jr(n=process.platform,e=Rs()){return n==="darwin"&&To.existsSync("/Applications/Ollama.app")?{shell:"open -a Ollama",cmd:["open","-a","Ollama"],detached:!1}:e?{shell:e+" serve",cmd:[e,"serve"],detached:!0}:null}function Ha({platform:n=process.platform,binary:e,env:t=process.env}={}){if(t.CHECKPOINTS_OLLAMA_START_CMD)try{let s=JSON.parse(t.CHECKPOINTS_OLLAMA_START_CMD);if(Array.isArray(s)&&s.length)return{shell:s.join(" "),cmd:s,detached:!0}}catch{}return e?Jr(n,e):null}function rm(n,{spawnImpl:e=lb,spawnSyncImpl:t=cb}={}){if(!n||!Array.isArray(n.cmd))return{started:!1,shell:null,error:"no start command"};try{if(n.detached){let s=e(n.cmd[0],n.cmd.slice(1),{detached:!0,stdio:"ignore"});s.on("error",()=>{}),s.unref()}else{let s=t(n.cmd[0],n.cmd.slice(1),{stdio:"ignore",timeout:15e3});if(s&&s.status)return{started:!1,shell:n.shell,error:n.shell+" exited "+s.status}}return{started:!0,shell:n.shell}}catch(s){return{started:!1,shell:n.shell,error:String(s.message||s)}}}var pt,rn,db,_o,Yr,Ds,nm,zr,fb,js=J(()=>{Ur();Te();Ls();pt="http://localhost:11434",rn="http://localhost:1234/v1",db=Ra.resolve(Ra.dirname(ub(import.meta.url)),"..","assets","sample-transcript.jsonl"),_o="2026-10-09",Yr=[{tag:"qwen2.5:3b",name:"Qwen 2.5 · 3B",sizeBytes:19e8,ramGB:4,speed:"fast",note:"Sticks to the 3-line format, good with code and file names",recommended:!0},{tag:"llama3.2:3b",name:"Llama 3.2 · 3B",sizeBytes:2e9,ramGB:4,speed:"fast",note:"Solid all-rounder from Meta"},{tag:"gemma3:4b",name:"Gemma 3 · 4B",sizeBytes:34e8,ramGB:6,speed:"medium",note:"Best-written summaries of the four, slower without a GPU"},{tag:"llama3.2:1b",name:"Llama 3.2 · 1B",sizeBytes:13e8,ramGB:2.5,speed:"fastest",note:'For 8 GB machines; terser, sometimes vague on "Next"'}],Ds="https://ollama.com",nm="2026-10-09",zr=[{tag:"gpt-oss:20b",name:"gpt-oss · 20B",note:"OpenAI's open-weight model; quick, plenty for 3 lines",recommended:!0},{tag:"gemma4:31b",name:"Gemma 4 · 31B",note:"Google; the model Ollama's own cloud docs use"},{tag:"nemotron-3-nano:30b",name:"Nemotron 3 Nano · 30B",note:"NVIDIA; small and fast"},{tag:"gpt-oss:120b",name:"gpt-oss · 120B",note:"Bigger and slower; more than a checkpoint needs"}];fb=n=>new Promise(e=>setTimeout(e,n))});import im from"node:fs";import{execFile as pb}from"node:child_process";function lm(n,e){let t=Oo(n,e);if(t)return t;if(e.CHECKPOINTS_TEST_NO_SYSTEM_BINS)return null;for(let s of mb){let o=s+"/"+n;try{return im.accessSync(o,im.constants.X_OK),o}catch{}}return null}function Fs(n=process.env){return n.LEFTOFF_GH||lm("gh",n)}function Vu(n=process.env){return n.LEFTOFF_BREW||lm("brew",n)}function am(n,e,{env:t,timeoutMs:s}){return new Promise(o=>{pb(n,e,{env:Xu(t),timeout:s,maxBuffer:1<<20},(r,i,a)=>{o({ok:!r,code:r?r.code==="ENOENT"?"ENOENT":r.killed?"timeout":r.code:0,out:String(i||"")+String(a||"")})})})}function gb(n){let e=String(n||"").match(/Logged in to (\S+)(?: account| as) ([A-Za-z0-9-]+)/);return e?{host:e[1],account:e[2]}:null}function yb(n,{brew:e=!1}={}){return n==="missing"?e?{cmd:"brew install gh && gh auth login",text:"Install gh with Homebrew, then sign in"}:{cmd:"gh auth login",url:Ba,text:"Install gh from "+Ba+", then run gh auth login"}:n==="signed-out"?{cmd:"gh auth login",text:"Sign in to GitHub"}:null}async function qr({env:n=process.env,timeoutMs:e=4e3}={}){let t=!!Vu(n),s={version:null,account:null,host:null,brew:t,checkedAt:new Date().toISOString()},o=Fs(n),r=(u,d={})=>({status:u,...s,...d,fix:yb(u,{brew:t})});if(!o)return r("missing");let i=await am(o,["--version"],{env:n,timeoutMs:e});if(!i.ok)return r("missing");let a=(i.out.match(/gh version (\S+)/)||[])[1]||null,l=await am(o,["auth","status"],{env:n,timeoutMs:e}),c=gb(l.out);return l.ok||l.code==="timeout"&&c?r("ready",{version:a,...c||{}}):r("signed-out",{version:a})}async function Ga({env:n=process.env,fresh:e=!1,now:t=Date.now,read:s=qr}={}){let o=t(),r=Ft.value&&Ft.value.status==="ready"?10*6e4:3e4;return!e&&Ft.value&&o-Ft.at<r?Ft.value:(Ft.pending||(Ft.pending=Promise.resolve().then(()=>s({env:n})).then(i=>(Ft.value=i,Ft.at=t(),i)).finally(()=>{Ft.pending=null})),Ft.pending)}var Ba,mb,Xu,Ft,No=J(()=>{js();Ba="https://cli.github.com",mb=["/opt/homebrew/bin","/usr/local/bin"];Xu=(n=process.env)=>({...n,GH_PROMPT_DISABLED:"1",GH_NO_UPDATE_NOTIFIER:"1",NO_COLOR:"1",CLICOLOR:"0"});Ft={at:0,value:null,pending:null}});import{execFile as wb}from"node:child_process";import Sb from"node:fs";function Zu(n){let e=Array.isArray(n)?n:[],t=0,s=0,o=0;for(let a of e){if(!a||typeof a!="object")continue;let l=String(a.conclusion||(a.__typename==="StatusContext"||a.state&&!a.status?a.state:"")||"").toUpperCase(),c=String(a.status||"").toUpperCase();bb.has(l)?s++:vb.has(l)||c==="COMPLETED"&&!l?t++:o++}let r=t+s+o,i;return r?s?i=s+" of "+r+" check"+(r===1?"":"s")+" failing":o?i="Checks running · "+t+"/"+r+" passed":i=r===1?"Check passed":"All "+r+" checks passed":i="No checks",{total:r,passed:t,failed:s,pending:o,summary:i}}function xb(n){let e=n;if(typeof n=="string")try{e=JSON.parse(n)}catch{return null}if(!e||typeof e!="object"||!Number.isInteger(e.number)||e.number<=0||String(e.state||"").toUpperCase()!=="OPEN")return null;let s=null;try{let o=new URL(String(e.url||""));o.protocol==="https:"&&!o.username&&!o.password&&(s=o.href)}catch{}return{number:e.number,title:String(e.title||"").replace(/[\u0000-\u001F\u007F]+/g," ").trim().slice(0,200)||"Pull request #"+e.number,state:e.isDraft?"draft":"open",url:s,branch:e.headRefName?String(e.headRefName).slice(0,200):null,checks:Zu(e.statusCheckRollup)}}function Ab({cwd:n,branch:e,gh:t,timeoutMs:s=6e3}={}){return t=t||Fs()||"gh",new Promise(o=>{if(!n||!e)return o(null);try{if(!Sb.statSync(n).isDirectory())return o(null)}catch{return o(null)}wb(t,["pr","view",e,"--json",kb],{cwd:n,timeout:s,maxBuffer:1<<20,env:{...process.env,GH_PROMPT_DISABLED:"1",GH_NO_UPDATE_NOTIFIER:"1",NO_COLOR:"1"}},(r,i)=>{if(r&&r.code==="ENOENT")return o({unavailable:!0});if(r)return o(null);o(xb(String(i)))})})}var kb,bb,vb,Ka,Qu=J(()=>{No();kb="number,title,state,url,isDraft,headRefName,statusCheckRollup",bb=new Set(["FAILURE","ERROR","TIMED_OUT","CANCELLED","ACTION_REQUIRED","STARTUP_FAILURE","STALE"]),vb=new Set(["SUCCESS","NEUTRAL","SKIPPED"]);Ka=class{constructor({ttlMs:e=5*6e4,missTtlMs:t=10*6e4,view:s=Ab,onUpdate:o=()=>{},now:r=Date.now,max:i=2}={}){Object.assign(this,{ttlMs:e,missTtlMs:t,view:s,onUpdate:o,now:r,max:i}),this.items=new Map,this.running=new Set,this.queue=[],this.offUntil=0}key(e,t){return e+"\0"+t}get(e,t){if(!e||!t||process.env.LEFTOFF_PR==="0")return null;let s=this.key(e,t),o=this.items.get(s),r=this.now();return!(o&&r-o.at<(o.pr?this.ttlMs:this.missTtlMs))&&r>=this.offUntil&&this.refresh(e,t),o?o.pr:null}seed(e,t,s){this.items.set(this.key(e,t),{pr:s,at:this.now()+365*864e5})}refresh(e,t){let s=this.key(e,t);this.running.has(s)||this.queue.some(o=>o.k===s)||(this.queue.push({k:s,cwd:e,branch:t}),this.pump())}pump(){for(;this.running.size<this.max&&this.queue.length;){let{k:e,cwd:t,branch:s}=this.queue.shift();this.running.add(e),Promise.resolve().then(()=>this.view({cwd:t,branch:s})).catch(()=>null).then(o=>{this.running.delete(e),o&&o.unavailable&&(this.offUntil=this.now()+30*6e4,o=null);let r=this.items.get(e);if(this.items.set(e,{pr:o||null,at:this.now()}),JSON.stringify(r?r.pr:null)!==JSON.stringify(o||null))try{this.onUpdate({cwd:t,branch:s,pr:o||null})}catch{}this.pump()})}}}});import $a from"node:fs";import{execFile as Tb}from"node:child_process";function Us(n){let e=new RegExp(dm.source).exec(String(n||""));if(!e)return null;let t=e[1]+"/"+e[2].replace(/\.git$/,""),s=Number(e[3]);return s?{repo:t,number:s,url:"https://github.com/"+t+"/pull/"+s}:null}function Ya(n){let e=[],t=String(n||""),s=new RegExp(dm.source,"g"),o;for(;o=s.exec(t);){let r=Us(o[0]);r&&!e.some(i=>i.url===r.url)&&e.push(r)}return e}function hm(n,{repo:e}={}){let t=String(n??"").trim();if(!t)return{error:"empty"};let s=Us(t);if(s)return s;let o=t.match(/^([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)#(\d{1,7})$/);return o&&Number(o[2])?{repo:o[1],number:Number(o[2]),url:"https://github.com/"+o[1]+"/pull/"+Number(o[2])}:(o=t.match(/^#?(\d{1,7})$/),o&&Number(o[1])?e?{repo:e,number:Number(o[1]),url:"https://github.com/"+e+"/pull/"+Number(o[1])}:{error:"no_repo"}:{error:"bad_ref"})}function Ja(n){let t=String(n||"").trim().match(/^(?:https?:\/\/(?:[^@/]+@)?github\.com\/|git@github\.com:|ssh:\/\/git@github\.com\/)([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git)?\/?$/);return t?t[1]+"/"+t[2]:null}function pm(n){return typeof n=="string"?n:Array.isArray(n)?n.map(e=>e&&typeof e=="object"?e.type==="text"?e.text:e.type==="tool_result"?pm(e.content):"":typeof e=="string"?e:"").join(`
94
- `):""}function mm(n,{pending:e=new Set}={}){let t=new Map,s=(o,r)=>{let i=t.get(o.url);i?r==="created"&&(i.via="created"):t.set(o.url,{...o,via:r})};for(let o of String(n||"").split(`
95
- `)){if(!o||o.indexOf("github.com")<0&&o.indexOf("gh pr create")<0&&o.indexOf("gh ")<0)continue;let r;try{r=JSON.parse(o)}catch{continue}let i=r&&r.message;if(!i)continue;let a=i.content;if(typeof a=="string"){for(let l of Ya(a))s(l,"mentioned");continue}if(Array.isArray(a)){for(let l of a)if(!(!l||typeof l!="object")){if(l.type==="text")for(let c of Ya(l.text))s(c,"mentioned");else if(l.type==="tool_use"){let c=l.input&&typeof l.input.command=="string"?l.input.command:"";if(_b.test(c)&&l.id&&e.add(l.id),/\bgh\s+pr\s+(view|checkout|merge|edit|ready|review|comment|checks|diff)\b/.test(c))for(let u of Ya(c))s(u,"mentioned")}else if(l.type==="tool_result"&&l.tool_use_id&&e.has(l.tool_use_id)){e.delete(l.tool_use_id);let c=pm(l.content)+`
96
- `+(r.toolUseResult&&typeof r.toolUseResult=="object"?String(r.toolUseResult.stdout||""):""),u=Ya(c);u.length&&s(u[u.length-1],"created")}}}}return[...t.values()]}function gm(n,e,t=[]){let s=Array.isArray(n)?n.map(r=>({...r})):[],o=!1;for(let r of e){if(t.includes(r.url))continue;let i=s.find(a=>a.url===r.url);if(i){r.via==="created"&&i.via==="mentioned"&&(i.via="created",o=!0);continue}s.push({url:r.url,via:r.via}),o=!0}for(;s.length>fm;){let r=s.findIndex(i=>i.via==="mentioned");s.splice(r>=0?r:0,1),o=!0}return{links:s,changed:o}}function Xr(n){if(!Array.isArray(n))return[];let e=[];for(let t of n){let s=Us(t&&typeof t=="object"?t.url:t);if(!s||e.some(r=>r.url===s.url))continue;let o=t&&typeof t=="object"&&["created","mentioned","manual"].includes(t.via)?t.via:"manual";e.push({url:s.url,via:o})}return e.slice(0,fm)}function Zr(n){return Array.isArray(n)?[...new Set(n.map(e=>(Us(e)||{}).url).filter(Boolean))].slice(0,100):[]}function um(n){if(!n||typeof n!="object"||!Number.isInteger(n.number)||n.number<=0)return null;let e=Us(n.url);if(!e)return null;let t=String(n.state||"").toUpperCase(),s=t==="MERGED"?"merged":t==="CLOSED"?"closed":n.isDraft?"draft":"open",o=Array.isArray(n.reviewRequests)?n.reviewRequests.map(i=>i&&(i.login||i.slug||i.name)||null).filter(Boolean).map(i=>Vr(i,80)):[],r=String(n.reviewDecision||"").toUpperCase();return{repo:e.repo,number:n.number,url:e.url,title:Vr(n.title)||"Pull request #"+n.number,state:s,branch:n.headRefName?Vr(n.headRefName):null,base:n.baseRefName?Vr(n.baseRefName):null,author:n.author&&n.author.login?Vr(n.author.login,80):null,createdAt:n.createdAt||null,updatedAt:n.updatedAt||null,mergedAt:n.mergedAt||null,review:r==="APPROVED"?"approved":r==="CHANGES_REQUESTED"?"changes_requested":r==="REVIEW_REQUIRED"?"review_required":null,reviewRequested:o,checks:Zu(n.statusCheckRollup)}}function ed(n,e){return!n||n.state!=="open"&&n.state!=="draft"?null:n.checks&&n.checks.failed?"Checks failing":n.review==="changes_requested"?"Changes requested":e&&(n.reviewRequested||[]).some(t=>t.toLowerCase()===String(e).toLowerCase())?"Your review":null}function ym(n,{now:e=Date.now(),me:t=null}={}){let s={attention:[],open:[],draft:[],merged:[]};for(let r of n){let i=r.pr,a=ed(i,t);a?s.attention.push({...r,attention:a}):i.state==="open"?s.open.push(r):i.state==="draft"?s.draft.push(r):i.state==="merged"&&i.mergedAt&&e-Date.parse(i.mergedAt)<=7*za&&s.merged.push(r)}let o=r=>(i,a)=>String(a.pr[r]||"").localeCompare(String(i.pr[r]||""));for(let r of Object.keys(s))s[r].sort(o(r==="merged"?"mergedAt":"updatedAt"));return[["attention","Needs attention"],["open","Open"],["draft","Draft"],["merged","Merged this week"]].map(([r,i])=>({key:r,label:i,rows:s[r]}))}function wm(n,e){let t=n.checks||{};return{repo:n.repo,number:n.number,url:n.url,title:n.title||null,state:n.state||null,via:e||null,checks:n.state?{total:t.total||0,failed:t.failed||0,pending:t.pending||0,summary:t.summary||""}:null}}function td(n,{gh:e,timeoutMs:t=1e4,env:s=process.env}={}){let o=e||Fs(s);return new Promise(r=>{if(!o)return r({ok:!1,code:"ENOENT",out:""});Tb(o,n,{env:Xu(s),timeout:t,maxBuffer:8<<20},(i,a)=>{r({ok:!i,code:i?i.code==="ENOENT"?"ENOENT":i.killed?"timeout":i.code:0,out:String(a||"")})})})}function Sm(n,e,{maxBytes:t=16<<20}={}){let s=$a.statSync(n),o=e>s.size?0:e;if(s.size-o>t&&(o=s.size-t),s.size===o)return{text:"",next:o,size:s.size};let r=$a.openSync(n,"r");try{let i=Buffer.alloc(s.size-o);$a.readSync(r,i,0,i.length,o);let a=i.lastIndexOf(10);return a<0?{text:"",next:o,size:s.size}:{text:i.subarray(0,a+1).toString("utf8"),next:o+a+1,size:s.size}}finally{$a.closeSync(r)}}var cm,dm,fm,_b,Vr,za,Wa,nd=J(()=>{No();Qu();cm="number,title,state,url,isDraft,headRefName,baseRefName,author,createdAt,updatedAt,mergedAt,reviewDecision,reviewRequests,statusCheckRollup",dm=/https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)\/pull\/(\d{1,7})(?![\d])/g,fm=24;_b=/\bgh\s+pr\s+create\b/;Vr=(n,e=200)=>String(n??"").replace(/[\u0000-\u001F\u007F]+/g," ").trim().slice(0,e);za=864e5;Wa=class{constructor({run:e=td,now:t=Date.now,ttlMs:s=5*6e4,missTtlMs:o=10*6e4,onUpdate:r=()=>{},max:i=2}={}){Object.assign(this,{run:e,now:t,ttlMs:s,missTtlMs:o,onUpdate:r,max:i}),this.views=new Map,this.lists=new Map,this.running=new Map,this.queue=[],this.offUntil=0}off(){return this.now()<this.offUntil||process.env.LEFTOFF_PR==="0"}stale(e,t){return!e||this.now()-e.at>=(t?this.ttlMs:this.missTtlMs)}viewStale(e){return e&&e.pr&&(e.pr.state==="merged"||e.pr.state==="closed")?this.now()-e.at>=6*36e5:this.stale(e,e&&e.pr)}view(e){let t=this.views.get(e);return this.viewStale(t)&&this.want("v",e),t?t.pr:null}list(e){let t=this.lists.get(e);return this.stale(t,t&&t.ok)&&this.want("l",e),t?t.prs:null}absorb(e){let t=this.now(),s=new Map;for(let o of e||[]){if(!o||!o.url||!o.state||!o.repo)continue;let r=this.views.get(o.url);(!r||t-r.at>3e4)&&this.views.set(o.url,{pr:o,at:t-this.ttlMs+6e4}),(o.state==="open"||o.state==="draft")&&(s.has(o.repo)||s.set(o.repo,[]),s.get(o.repo).push(o))}for(let[o,r]of s){let i=this.lists.get(o);(!i||!i.ok)&&this.lists.set(o,{prs:r,at:t-this.ttlMs+6e4,ok:!1})}}seedList(e,t){this.lists.set(e,{prs:t,at:this.now()+365*za,ok:!0});for(let s of t)this.views.set(s.url,{pr:s,at:this.now()+365*za})}seedView(e){this.views.set(e.url,{pr:e,at:this.now()+365*za})}want(e,t){if(this.off())return null;let s=e+":"+t;if(this.running.has(s))return this.running.get(s);let o=this.queue.find(a=>a.k===s);if(o)return o.p;let r,i=new Promise(a=>{r=a});return this.queue.push({k:s,kind:e,arg:t,p:i,res:r}),this.pump(),i}pump(){for(;this.running.size<this.max&&this.queue.length;){let e=this.queue.shift();this.running.set(e.k,e.p),Promise.resolve().then(()=>this.fetch(e.kind,e.arg)).catch(()=>null).then(t=>{if(this.running.delete(e.k),e.res(t),t)try{this.onUpdate({kind:e.kind,arg:e.arg})}catch{}this.pump()})}}async fetch(e,t){let s=e==="v"?["pr","view",t,"--json",cm]:["pr","list","--repo",t,"--state","open","--limit","100","--json",cm],o=await this.run(s);if(o&&o.code==="ENOENT")return this.offUntil=this.now()+30*6e4,!1;let r=null;if(o&&o.ok)try{r=JSON.parse(o.out)}catch{}if(e==="v"){let l=r?um(r):null,c=this.views.get(t);return!l&&c&&c.pr?(this.views.set(t,{pr:c.pr,at:this.now()-this.ttlMs+this.missTtlMs/2}),!1):(this.views.set(t,{pr:l,at:this.now()}),JSON.stringify(c?c.pr:null)!==JSON.stringify(l))}let i=Array.isArray(r)?r.map(um).filter(Boolean):null,a=this.lists.get(t);if(!i)return this.lists.set(t,{prs:a?a.prs:null,at:this.now(),ok:!1}),!1;this.lists.set(t,{prs:i,at:this.now(),ok:!0});for(let l of i){let c=this.views.get(l.url);(!c||c.at<this.now())&&this.views.set(l.url,{pr:l,at:this.now()})}return JSON.stringify(a?a.prs:null)!==JSON.stringify(i)}async settle({urls:e=[],repos:t=[],waitMs:s=8e3,fresh:o=!1}={}){if(o){for(let l of e){let c=this.views.get(l);c&&c.at<=this.now()&&(c.at=0)}for(let l of t){let c=this.lists.get(l);c&&c.at<=this.now()&&(c.at=0)}}let r=[];for(let l of t){let c=this.lists.get(l);if(this.stale(c,c&&c.ok)){let u=this.want("l",l);u&&r.push(u)}}for(let l of e){let c=this.views.get(l);if(this.viewStale(c)){let u=this.want("v",l);u&&r.push(u)}}if(!r.length)return!0;let i,a=await Promise.race([Promise.all(r).then(()=>!0),new Promise(l=>{i=setTimeout(()=>l(!1),s),i.unref&&i.unref()})]);return clearTimeout(i),a}busy(){return this.running.size+this.queue.length>0}}});import qa from"node:fs";import km from"node:path";import Eb from"node:crypto";function Hs(n,e){if(!e||!n)return null;let t;return e.kind==="permission"||e.kind==="question"?t="ask:"+(e.ask&&e.ask.id||""):e.kind==="terminal"?t="terminal:"+(e.since||"")+":"+(e.message||""):e.kind==="reply"?t="reply:"+(e.since||"")+":"+(e.question||""):e.kind==="agent"?t="agent:"+(e.items||[]).map(s=>(s.id||"")+"="+(s.text||"")).sort().join(`
97
- `):t=String(e.kind)+":"+(e.since||""),Eb.createHash("sha256").update(String(n)+`
98
- `+t).digest("hex").slice(0,20)}var Ob,Cb,bm,Va,vm=J(()=>{Ob=30;Cb=/^\.needs-dismissed\..+\.json$/,bm=n=>{try{let e=JSON.parse(qa.readFileSync(n,"utf8"));return e&&typeof e=="object"?e:null}catch{return null}},Va=class{constructor({root:e,machine:t,now:s=Date.now,ttlMs:o=2e3}={}){Object.assign(this,{root:e,machine:t,now:s,ttlMs:o}),this.cache=null}file(){return km.join(this.root,".needs-dismissed."+String(this.machine).replace(/[^\w.-]/g,"_")+".json")}own(){let e=bm(this.file())||{};return{dismissed:e.dismissed||{},restored:e.restored||{}}}active(){let e=this.now();if(this.cache&&e-this.cache.at<this.ttlMs)return this.cache.map;let t=new Map,s=new Map,o=[];try{o=qa.readdirSync(this.root).filter(i=>Cb.test(i))}catch{}for(let i of o){let a=bm(km.join(this.root,i));if(a){for(let[l,c]of Object.entries(a.dismissed||{}))c&&(!t.has(l)||Date.parse(c.at)>Date.parse(t.get(l).at))&&t.set(l,c);for(let[l,c]of Object.entries(a.restored||{}))(!s.has(l)||Date.parse(c)>Date.parse(s.get(l)))&&s.set(l,c)}}let r=new Map;for(let[i,a]of t)(!s.has(i)||Date.parse(a.at)>Date.parse(s.get(i)))&&r.set(i,{key:i,...a});return this.cache={at:e,map:r},r}has(e){return!!e&&this.active().has(e)}list(){return[...this.active().values()].sort((e,t)=>Date.parse(t.at)-Date.parse(e.at))}write(e){let t=this.now()-Ob*864e5;for(let[r,i]of Object.entries(e.dismissed))(!i||Date.parse(i.at)<t)&&delete e.dismissed[r];for(let[r,i]of Object.entries(e.restored))Date.parse(i)<t&&delete e.restored[r];let s=this.file(),o=s+".tmp-"+process.pid;qa.writeFileSync(o,JSON.stringify(e,null,2)+`
99
- `),qa.renameSync(o,s),this.cache=null}dismiss(e,t={}){if(!e||typeof e!="string"||!/^[0-9a-f]{8,64}$/.test(e))throw new Error("bad key");let s=this.own(),o=(r,i)=>r==null?null:String(r).slice(0,i);return s.dismissed[e]={at:new Date(this.now()).toISOString(),sessionId:o(t.sessionId,200),title:o(t.title,200),line:o(t.line,300),kind:o(t.kind,20)},delete s.restored[e],this.write(s),s.dismissed[e]}restore(e){let t=this.own(),s=new Date(this.now()).toISOString(),o=0;for(let r of e)r&&(delete t.dismissed[r],t.restored[r]=s,o++);return o&&this.write(t),o}}});import Ib from"node:fs";import _n from"node:path";import Nb from"node:crypto";function Za(n){let e=[],t=/`([^`\n]{2,200})`/g,s;for(;s=t.exec(String(n||""));){let o=s[1].trim();o&&!e.includes(o)&&e.push(o)}return e}function Xn(n,e=140){let t=String(n||"").replace(/\s+/g," ").trim();if(t=t.replace(/\[([^\]]+)\]\([^)]*\)/g,"$1").replace(/(\*\*|__)(.+?)\1/g,"$2").replace(/(^|\s)[*_](\S[^*_]*?)[*_](?=\s|$|[.,;:])/g,"$1$2").replace(/^#+\s*/,""),t=t.replace(/^(?:\d{1,2}[.)]|[-*+•])\s+/,"").replace(/^\[[ x]\]\s+/,""),t=t.replace(/\s*:\s*$/,""),t.length>e){let s=t.slice(0,e-1);t=s.slice(0,Math.max(s.lastIndexOf(" "),e-20)).replace(/[\s,;:.-]+$/,"")+"…"}return t}function _m(n,e=[]){let t=Xn(n);if(!t||t.length<6)return null;let s=[...Za(n),...e].filter((o,r,i)=>i.indexOf(o)===r).slice(0,3);return{text:t,codes:s}}function Bb(n){let e=[],t=String(n||"").split(`
100
- `),s=new Set;for(let o=0;o<t.length;o++){let r=t[o].trim(),i=Lb.test(r),a=!i&&Db.test(r);if(!i&&!a)continue;let l=0,c=0;for(let u=o+1;u<t.length&&l<8;u++){let d=t[u];if(!d.trim()){if(++c>1&&l)break;continue}if(/^\s*```/.test(d)){let m=u+1,g=[];for(;m<t.length&&!/^\s*```/.test(t[m]);)g.push(t[m++]);let S=e[e.length-1],k=g.map(w=>w.trim()).filter(Boolean);S&&S._list===o&&k.length&&k.join(`
85
+ `).trimEnd()}function em(n=[],{question:e=null,max:t=3,width:s=280}={}){let o=[],r=[],i=!0;for(let a=n.length-1;a>=0&&o.length<t;a--){let l=n[a];if(Xk(l))break;if(!l||l.role!=="assistant"||!l.text||!l.text.trim())continue;let c=l.text;i&&(r=!e||Ju.test(e)?qu(c):[],e&&(c=Vk(c,e)),i=!1);let u=Qp(c).split(`
86
+ `).map(d=>d.trim()).filter(d=>d&&!/^#{1,6}\s*$/.test(d)&&!/^[-*_]{3,}$/.test(d));for(let d=u.length-1;d>=0&&o.length<t;d--)o.unshift(an(u[d].replace(/^#{1,6}\s+/,""),s))}return!o.length&&!r.length?null:{lines:o,options:r}}function qu(n){let e=Qp(n).split(`
87
+ `).map(a=>a.trim()).filter(Boolean),t=[],s=[];for(let a of e){let l=a.match(/^(?:\*\*)?(?:option\s+)?([0-9]{1,2}|[a-fA-F])[.):](?:\*\*)?\s+(.+)$/i);l?t.push({key:l[1],text:l[2]}):t.length&&(s.splice(0,s.length,...t),t=[])}let o=t.length?t:s;if(o.length<2||o.length>6)return[];let r=o.map(a=>a.key.toLowerCase());return r.every((a,l)=>/\d/.test(a)?Number(a)===Number(r[0])+l:a.charCodeAt(0)===r[0].charCodeAt(0)+l)?o.map(a=>an(a.key+". "+a.text.replace(/\*\*/g,"").replace(/\s+[—–-]\s+.*$/,"").replace(/[:.]$/,"").trim(),60)):[]}function eb(n={},{cwd:e}={}){let t=n.input||{},s=String(n.tool||"tool"),o=Zk(t.file_path||t.notebook_path||t.path,e);switch(s){case"Edit":case"MultiEdit":case"NotebookEdit":return"Edited "+(o||"a file");case"Write":return"Wrote "+(o||"a file");case"Read":return"Read "+(o||"a file");case"Bash":{let r=Rs(t.command);return Qk.test(r)?"Ran tests":/^git\s+commit\b/.test(r)?"Committed":/^git\s+push\b/.test(r)?"Pushed":"Ran "+an(t.description?Rs(t.description):r||"a command",48)}case"Grep":return"Searched “"+an(Rs(t.pattern),32)+"”";case"Glob":return"Listed "+an(Rs(t.pattern),32);case"LS":return"Listed "+(o||"a folder");case"WebFetch":{let r="";try{r=new URL(t.url).hostname}catch{}return"Fetched "+(r||"a page")}case"WebSearch":return"Searched the web for “"+an(Rs(t.query),32)+"”";case"TodoWrite":return"Updated the to-do list";case"Task":case"Agent":return"Ran an agent"+(t.description?": "+an(Rs(t.description),36):"");case"AskUserQuestion":return"Asked you";case"ExitPlanMode":return"Proposed a plan";default:return"Used "+an(s.replace(/^mcp__/,"").replace(/__/g," "),40)}}function tm(n=[],{limit:e=20,cwd:t=null,textMax:s=6e3}={}){let o=[];for(let r of n){if(!r)continue;if(r.role==="user"){if(!r.text||!r.text.trim())continue;o.push({role:"user",text:an(r.text.trim(),s),chips:[]});continue}let i=(r.text||"").trim(),a=(r.toolUses||[]).map(c=>({label:eb(c,{cwd:t}),error:!!c.isError})),l=o[o.length-1];(!l||l.role!=="assistant"||i&&l.chips.length)&&(l={role:"assistant",text:"",chips:[]},o.push(l)),i&&(l.text=an(l.text?l.text+`
88
+
89
+ `+i:i,s));for(let c of a){let u=l.chips[l.chips.length-1];u&&u.label===c.label&&u.error===c.error?u.n=(u.n||1)+1:l.chips.push(c)}}return o.slice(-Math.max(1,e))}var an,Wu,Rs,Ju,Xk,Zk,Qk,nm=J(()=>{an=(n,e)=>(n=String(n??""),n.length>e?n.slice(0,e-1).trimEnd()+"…":n),Wu=n=>String(n??"").replace(/\r\n?/g,`
90
+ `),Rs=n=>Wu(n).replace(/\s+/g," ").trim();Ju=/\b(which|or|options?|prefer|pick|choose|one of)\b/i,Xk=n=>n&&n.role==="user"&&n.text&&n.text.trim();Zk=(n,e)=>{let t=String(n||"");return t?e&&t.startsWith(String(e).replace(/\/$/,"")+"/")?t.slice(String(e).replace(/\/$/,"").length+1):t.includes("/")?qk.basename(t):t:""},Qk=/(^|[\s;&|(])((p?npm|yarn|pnpm|bun)\s+(run\s+)?test\b|node\s+--test\b|(npx\s+)?(jest|vitest|mocha|pytest|playwright\s+test)\b|go\s+test\b|cargo\s+test\b|rspec\b|make\s+test\b|swift\s+test\b|xcodebuild\s+test\b|node\s+test\/)/});import{spawn as tb,execFileSync as nb}from"node:child_process";import sb from"node:fs";import Vu from"node:path";function ab(){if(process.env.CHECKPOINTS_NO_CLAUDE)return!1;try{return nb(process.platform==="win32"?"where":"which",["claude"],{stdio:"ignore"}),!0}catch{return!1}}function lb(n){let e=n.summarizer||{},t=String(e.provider||e.mode||"auto").toLowerCase();return om[t]?om[t]:ab()?"claude":process.env.ANTHROPIC_API_KEY?"anthropic":"heuristic"}function cb({system:n,prompt:e,model:t},s=6e4){return new Promise((o,r)=>{let i=["-p","--model",t||"haiku","--output-format","text"];n&&i.push("--append-system-prompt",n);let a=tb("claude",i,{env:{...process.env,CHECKPOINTS_INTERNAL:"1",LEFTOFF_INTERNAL:"1"},stdio:["pipe","pipe","pipe"]}),l="",c="",u=setTimeout(()=>{a.kill("SIGKILL"),r(new Error("claude -p timed out"))},s);a.stdout.on("data",d=>l+=d),a.stderr.on("data",d=>c+=d),a.on("error",d=>{clearTimeout(u),r(d)}),a.on("close",d=>{clearTimeout(u),d===0?o({text:l.trim(),usage:null}):r(new Error("claude -p exited "+d+": "+c.slice(0,300)))}),a.stdin.end(e)})}async function Xu(n,e,t,s,o){let r=await n(e,{method:"POST",headers:{"content-type":"application/json",...t},body:JSON.stringify(s),signal:AbortSignal.timeout(o)}),i=await r.json().catch(()=>({}));if(!r.ok)throw new Error(e+" -> "+r.status+": "+(i.error&&(i.error.message||i.error)||"error"));return i}function ub(n={},e=process.env){return _n("anthropic",n,e)}function _n(n,{apiKeyEnv:e,apiKeyFile:t,root:s}={},o=process.env){let r=ja[n]||{},i=e||r.env;if(i&&o[i])return o[i];for(let a of[t,e==="__none__"?null:r.file]){if(!a)continue;let l=Vu.isAbsolute(a)?null:kp(a,o.HOME||o.XDG_CONFIG_HOME?o:process.env);for(let c of[l,Vu.isAbsolute(a)?a:Vu.join(s||Z(),a)])if(c)try{let u=sb.readFileSync(c,"utf8").trim();if(u)return u}catch{}}return null}async function db(n,{system:e,prompt:t,model:s,maxTokens:o,apiKeyEnv:r,apiKeyFile:i,root:a}){let l=ub({apiKeyEnv:r,apiKeyFile:i,root:a});if(!l)throw new Error((r||"ANTHROPIC_API_KEY")+" is not set");let c=(process.env.ANTHROPIC_BASE_URL||"https://api.anthropic.com").replace(/\/$/,""),u=await Xu(n,c+"/v1/messages",{"x-api-key":l,"anthropic-version":"2023-06-01"},{model:sm[s]||s||sm.haiku,max_tokens:o||300,system:e,messages:[{role:"user",content:t}]},3e4);return{text:(u.content||[]).filter(d=>d.type==="text").map(d=>d.text).join(`
91
+ `),usage:u.usage?{inputTokens:u.usage.input_tokens,outputTokens:u.usage.output_tokens}:null}}async function rm(n,{system:e,prompt:t,model:s,maxTokens:o,baseUrl:r,key:i,cloud:a}){let l=await Xu(n,r.replace(/\/$/,"")+"/api/chat",i?{authorization:"Bearer "+i}:{},{model:s,stream:!1,messages:[...e?[{role:"system",content:e}]:[],{role:"user",content:t}],options:{num_predict:a?Math.max(1024,(o||300)*4):o||300,temperature:.2}},a?6e4:12e4);return{text:l.message&&l.message.content||"",usage:{inputTokens:l.prompt_eval_count,outputTokens:l.eval_count}}}async function fb(n,{system:e,prompt:t,model:s,maxTokens:o,baseUrl:r,apiKeyEnv:i}){let a=i?process.env[i]:null,l=await Xu(n,r.replace(/\/$/,"")+"/chat/completions",a?{authorization:"Bearer "+a}:{},{model:s,max_tokens:o||300,temperature:.2,messages:[...e?[{role:"system",content:e}]:[],{role:"user",content:t}]},12e4),c=l.choices&&l.choices[0];return{text:c&&c.message&&c.message.content||c&&c.text||"",usage:l.usage?{inputTokens:l.usage.prompt_tokens,outputTokens:l.usage.completion_tokens}:null}}var sm,ob,rb,ib,Pa,ja,om,_t,Ps=J(()=>{xe();Te();sm={haiku:"claude-haiku-5-5",sonnet:"claude-sonnet-5-5",opus:"claude-opus-5-5"},ob=["haiku","sonnet","opus",""],rb="https://ollama.com",ib="gpt-oss:20b",Pa={ollama:{baseUrl:"http://localhost:11434",model:"llama3.2"},"ollama-cloud":{baseUrl:rb,model:ib},"openai-compatible":{baseUrl:"http://localhost:1234/v1",model:"local-model"}},ja={anthropic:{env:"ANTHROPIC_API_KEY",file:".secret/anthropic-api-key"},"ollama-cloud":{env:"OLLAMA_API_KEY",file:".secret/ollama-api-key"}},om={stub:"heuristic",none:"heuristic",heuristic:"heuristic",api:"anthropic",anthropic:"anthropic",claude:"claude",ollama:"ollama","ollama-cloud":"ollama-cloud","ollama.com":"ollama-cloud",cloud:"ollama-cloud","openai-compatible":"openai-compatible",openai:"openai-compatible",lmstudio:"openai-compatible",llamacpp:"openai-compatible",vllm:"openai-compatible"};_t=class{constructor(e,{fetchImpl:t,root:s}={}){this.cfg=e,this.root=s||null,this.fetchImpl=t||((...o)=>fetch(...o)),this.mode=lb(e)}get model(){let e=String(this.cfg.summarizer&&this.cfg.summarizer.model||"");return Pa[this.mode]&&ob.includes(e)?Pa[this.mode].model:e||"haiku"}get baseUrl(){return this.cfg.summarizer&&this.cfg.summarizer.baseUrl||Pa[this.mode]&&Pa[this.mode].baseUrl||null}describe(){return{provider:this.mode,model:this.mode==="heuristic"?null:this.model,baseUrl:this.baseUrl}}async complete({system:e,prompt:t,maxTokens:s}){let o=this.cfg.summarizer||{},r={system:e,prompt:t,maxTokens:s,model:this.model,baseUrl:this.baseUrl,apiKeyEnv:o.apiKeyEnv,apiKeyFile:o.apiKeyFile,root:this.root};if(this.mode==="claude")return cb(r);if(this.mode==="anthropic")return db(this.fetchImpl,r);if(this.mode==="ollama")return rm(this.fetchImpl,r);if(this.mode==="ollama-cloud"){let i=_n("ollama-cloud",{apiKeyEnv:o.apiKeyEnv,apiKeyFile:o.apiKeyFile,root:this.root});if(!i)throw new Error((o.apiKeyEnv||"OLLAMA_API_KEY")+" is not set (create a key at https://ollama.com/settings/keys)");return rm(this.fetchImpl,{...r,key:i,cloud:!0})}if(this.mode==="openai-compatible")return fb(this.fetchImpl,r);throw new Error("no model in heuristic mode")}async checkpoint({system:e,prompt:t,messages:s,session:o,maxTokens:r=300,length:i="short"}){if(this.mode!=="heuristic")try{let a=await this.complete({system:e,prompt:t,maxTokens:r}),l=lp(String(a.text||"").replace(/<think>[\s\S]*?<\/think>/gi,"").trim()),c={by:"daemon",provider:this.mode,model:this.model};return a.usage&&a.usage.inputTokens!=null&&(c.inputTokens=a.usage.inputTokens),a.usage&&a.usage.outputTokens!=null&&(c.outputTokens=a.usage.outputTokens),{...l,generator:c}}catch(a){return{...Eu(s,o,{length:i}),generator:{by:"heuristic",fallbackFrom:this.mode,error:String(a.message||a).slice(0,160)}}}return{...Eu(s,o,{length:i}),generator:{by:"heuristic"}}}async title(e){if(this.mode!=="heuristic"&&e.firstPrompt)try{let t=await this.complete({system:up,prompt:`First prompt:
92
+ `+e.firstPrompt,maxTokens:30}),s=dp(t.text);if(s)return{title:s,titleSource:"generated"}}catch{}return{title:cp(e),titleSource:"fallback"}}async paragraph(e,t){if(this.mode==="heuristic")return null;try{return(await this.complete({system:e,prompt:t,maxTokens:200})).text.trim()}catch{return null}}}});import Eo from"node:fs";import hb from"node:os";import Fa from"node:path";import{execFileSync as W_,spawn as pb,spawnSync as mb}from"node:child_process";import{fileURLToPath as gb}from"node:url";async function Jr({baseUrl:n=process.env.CHECKPOINTS_CLOUD_URL||js,fetchImpl:e=fetch,timeoutMs:t=5e3}={}){let s=n.replace(/\/$/,"");try{let o=await Ha(e,s+"/api/tags",t);return{ok:!0,baseUrl:s,models:(o.models||[]).map(r=>r.name||r.model).filter(Boolean),error:null}}catch(o){return{ok:!1,baseUrl:s,models:[],error:o.cause&&o.cause.code||o.message}}}function Ba(n=[],{curated:e=Wr,current:t=null}={}){let s=r=>n.some(i=>Je(i,r)),o=e.map(r=>({...r,curated:!0,available:n.length?s(r.tag):null,current:!!t&&Je(t,r.tag)}));for(let r of n)o.some(i=>Je(i.tag,r))||o.push({tag:r,name:r,curated:!1,available:!0,recommended:!1,current:!!t&&Je(t,r)});return o}function Ua(n){let e=String(n||"").trim().toLowerCase();return e?e.includes(":")?e:e+":latest":""}function Je(n,e){return Ua(n)===Ua(e)}function Co(n=[],{curated:e=Yr,current:t=null}={}){let s=n.map(i=>typeof i=="string"?{name:i}:i),o=i=>s.some(a=>Je(a.name||a.model,i)),r=e.map(i=>({...i,curated:!0,installed:o(i.tag),current:!!t&&Je(t,i.tag)}));for(let i of s){let a=i.name||i.model;!a||r.some(l=>Je(l.tag,a))||/embed/i.test(a)||r.push({tag:a,name:a,sizeBytes:i.size||null,curated:!1,installed:!0,recommended:!1,current:!!t&&Je(t,a)})}return r}function am(n,{totalMemBytes:e=hb.totalmem()}={}){let t=n.find(r=>r.current&&r.installed);if(t)return t.tag;let s=n.find(r=>r.recommended);if(s&&s.installed)return s.tag;let o=n.find(r=>r.curated&&r.installed);if(o)return o.tag;if(e&&e<85e8){let r=n.filter(i=>i.curated).sort((i,a)=>i.ramGB-a.ramGB)[0];if(r)return r.tag}return s?s.tag:n[0]&&n[0].tag}function Ga(n,e){let t=[];return n.sizeBytes&&t.push(e(n.sizeBytes)),n.ramGB&&t.push("~"+n.ramGB+" GB RAM"),n.speed&&t.push(n.speed),t.join(" · ")}function No(n,e=process.env){let t=String(e.PATH||"").split(Fa.delimiter).filter(Boolean),s=process.platform==="win32"?[".exe",".cmd",".bat",""]:[""];for(let o of t)for(let r of s){let i=Fa.join(o,n+r);try{if(Eo.statSync(i).isFile())return Eo.accessSync(i,Eo.constants.X_OK),i}catch{}}return null}function Fs(n=process.env){let e=No("ollama",n);if(e)return e;if(n.CHECKPOINTS_TEST_NO_SYSTEM_BINS)return null;for(let t of["/opt/homebrew/bin/ollama","/usr/local/bin/ollama","/Applications/Ollama.app/Contents/Resources/ollama"])if(Eo.existsSync(t))return t;return null}async function Ha(n,e,t=1500){let s=await n(e,{signal:AbortSignal.timeout(t)});if(!s.ok)throw new Error(e+" -> HTTP "+s.status);return s.json()}async function $t({baseUrl:n=gt,fetchImpl:e=fetch,env:t=process.env}={}){let s=n.replace(/\/$/,""),o={kind:"ollama",running:!1,baseUrl:s,version:null,models:[],binary:Fs(t),error:null};try{let r=await Ha(e,s+"/api/tags");o.running=!0,o.models=(r.models||[]).map(i=>({name:i.name||i.model,size:i.size,modifiedAt:i.modified_at,parameterSize:i.details&&i.details.parameter_size,quantization:i.details&&i.details.quantization_level}));try{o.version=(await Ha(e,s+"/api/version")).version||null}catch{}}catch(r){o.error=r.cause&&r.cause.code||r.name||r.message}return o}async function En({baseUrl:n=ln,fetchImpl:e=fetch}={}){let t=n.replace(/\/$/,""),s={kind:"openai-compatible",running:!1,baseUrl:t,models:[],error:null};try{let o=await Ha(e,t+"/models");s.running=!0,s.models=(o.data||[]).map(r=>r.id).filter(r=>r&&!/embed/i.test(r))}catch(o){s.error=o.cause&&o.cause.code||o.name||o.message}return s}async function Io({baseUrl:n=gt,fetchImpl:e=fetch,timeoutMs:t=2e4}={}){let s=Date.now()+t;for(;Date.now()<s;){let o=await $t({baseUrl:n,fetchImpl:e});if(o.running)return o;await wb(500)}return $t({baseUrl:n,fetchImpl:e})}function Sb(n,e){let t=n||{layers:{},status:"",total:0,completed:0,fraction:0,done:!1,error:null};if(e.error)return{...t,error:String(e.error)};let s={...t.layers};e.digest&&e.total&&(s[e.digest]={total:e.total,completed:Math.min(e.completed||0,e.total)});let o=Object.values(s),r=o.reduce((c,u)=>c+u.total,0),i=o.reduce((c,u)=>c+u.completed,0),a=e.status==="success",l=a?1:r?Math.min(.999,i/r):t.fraction;return{layers:s,status:e.status||t.status,total:r,completed:i,fraction:Math.max(t.fraction,l),done:a,error:null}}async function Mo({tag:n,baseUrl:e=gt,fetchImpl:t=fetch,onProgress:s=()=>{},signal:o}={}){let r=await t(e.replace(/\/$/,"")+"/api/pull",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({model:n,stream:!0}),signal:o});if(!r.ok||!r.body){let u="HTTP "+r.status;try{let d=await r.json();d.error&&(u=d.error)}catch{}throw new Error("pull "+n+" failed: "+u)}let i=null,a="",l=new TextDecoder,c=u=>{if(!u.trim())return;let d;try{d=JSON.parse(u)}catch{return}if(i=Sb(i,d),i.error)throw new Error("pull "+n+" failed: "+i.error);s(i)};for await(let u of r.body){a+=l.decode(u,{stream:!0});let d;for(;(d=a.indexOf(`
93
+ `))>=0;)c(a.slice(0,d)),a=a.slice(d+1)}if(c(a),!i||!i.done)throw new Error("pull "+n+" ended early (last status: "+(i&&i.status||"none")+")");return i}async function Us(n,{fetchImpl:e,transcriptFile:t=yb,root:s}={}){let o=fo(ke,{summarizer:n}),{messages:r,meta:i}=xo(Eo.readFileSync(t,"utf8")),a={id:i.sessionId||"sample",cwd:i.cwd||"/work/larry",title:i.summary||"Sample session",repo:{root:i.cwd,branch:i.gitBranch}},l=ga({session:a,messages:r,cfg:o}),c=new _t(o,{...e?{fetchImpl:e}:{},...s?{root:s}:{}}),u=Date.now(),d=await c.checkpoint({...l,messages:r,session:a}),f=Date.now()-u,h=d.generator&&d.generator.by==="heuristic"&&d.generator.fallbackFrom;return{ok:!h,doing:d.doing,stoppedAt:d.stoppedAt,next:d.next,parseOk:d.parseOk!==!1,ms:f,generator:d.generator,error:h?d.generator.error:null}}function lm(n=process.platform,e=process.env){let t={id:"script",label:"Official install script",shell:"curl -fsSL https://ollama.com/install.sh | sh",cmd:["sh","-c","curl -fsSL https://ollama.com/install.sh | sh"]};if(n==="darwin"){let s=[];return No("brew",e)&&s.push({id:"brew",label:"Homebrew",shell:"brew install ollama",cmd:["brew","install","ollama"]}),s.push({...t,note:"installs the Ollama app"}),s.push({id:"manual",label:"Download the app myself",shell:"open https://ollama.com/download",cmd:null,url:"https://ollama.com/download"}),s}return n==="linux"?[{...t,note:"asks for sudo; sets up a systemd service"},{id:"manual",label:"Install it myself",shell:"see https://ollama.com/download/linux",cmd:null,url:"https://ollama.com/download/linux"}]:n==="win32"?[{id:"manual",label:"PowerShell installer",shell:"irm https://ollama.com/install.ps1 | iex",cmd:null,url:"https://ollama.com/download/windows"}]:[{id:"manual",label:"Install it myself",shell:"see https://ollama.com/download",cmd:null,url:"https://ollama.com/download"}]}function qr(n=process.platform,e=Fs()){return n==="darwin"&&Eo.existsSync("/Applications/Ollama.app")?{shell:"open -a Ollama",cmd:["open","-a","Ollama"],detached:!1}:e?{shell:e+" serve",cmd:[e,"serve"],detached:!0}:null}function Ka({platform:n=process.platform,binary:e,env:t=process.env}={}){if(t.CHECKPOINTS_OLLAMA_START_CMD)try{let s=JSON.parse(t.CHECKPOINTS_OLLAMA_START_CMD);if(Array.isArray(s)&&s.length)return{shell:s.join(" "),cmd:s,detached:!0}}catch{}return e?qr(n,e):null}function cm(n,{spawnImpl:e=pb,spawnSyncImpl:t=mb}={}){if(!n||!Array.isArray(n.cmd))return{started:!1,shell:null,error:"no start command"};try{if(n.detached){let s=e(n.cmd[0],n.cmd.slice(1),{detached:!0,stdio:"ignore"});s.on("error",()=>{}),s.unref()}else{let s=t(n.cmd[0],n.cmd.slice(1),{stdio:"ignore",timeout:15e3});if(s&&s.status)return{started:!1,shell:n.shell,error:n.shell+" exited "+s.status}}return{started:!0,shell:n.shell}}catch(s){return{started:!1,shell:n.shell,error:String(s.message||s)}}}var gt,ln,yb,Oo,Yr,js,im,Wr,wb,Hs=J(()=>{Hr();Te();Ps();gt="http://localhost:11434",ln="http://localhost:1234/v1",yb=Fa.resolve(Fa.dirname(gb(import.meta.url)),"..","assets","sample-transcript.jsonl"),Oo="2026-10-09",Yr=[{tag:"qwen2.5:3b",name:"Qwen 2.5 · 3B",sizeBytes:19e8,ramGB:4,speed:"fast",note:"Sticks to the 3-line format, good with code and file names",recommended:!0},{tag:"llama3.2:3b",name:"Llama 3.2 · 3B",sizeBytes:2e9,ramGB:4,speed:"fast",note:"Solid all-rounder from Meta"},{tag:"gemma3:4b",name:"Gemma 3 · 4B",sizeBytes:34e8,ramGB:6,speed:"medium",note:"Best-written summaries of the four, slower without a GPU"},{tag:"llama3.2:1b",name:"Llama 3.2 · 1B",sizeBytes:13e8,ramGB:2.5,speed:"fastest",note:'For 8 GB machines; terser, sometimes vague on "Next"'}],js="https://ollama.com",im="2026-10-09",Wr=[{tag:"gpt-oss:20b",name:"gpt-oss · 20B",note:"OpenAI's open-weight model; quick, plenty for 3 lines",recommended:!0},{tag:"gemma4:31b",name:"Gemma 4 · 31B",note:"Google; the model Ollama's own cloud docs use"},{tag:"nemotron-3-nano:30b",name:"Nemotron 3 Nano · 30B",note:"NVIDIA; small and fast"},{tag:"gpt-oss:120b",name:"gpt-oss · 120B",note:"Bigger and slower; more than a checkpoint needs"}];wb=n=>new Promise(e=>setTimeout(e,n))});import um from"node:fs";import{execFile as kb}from"node:child_process";function fm(n,e){let t=No(n,e);if(t)return t;if(e.CHECKPOINTS_TEST_NO_SYSTEM_BINS)return null;for(let s of bb){let o=s+"/"+n;try{return um.accessSync(o,um.constants.X_OK),o}catch{}}return null}function Bs(n=process.env){return n.LEFTOFF_GH||fm("gh",n)}function Zu(n=process.env){return n.LEFTOFF_BREW||fm("brew",n)}function dm(n,e,{env:t,timeoutMs:s}){return new Promise(o=>{kb(n,e,{env:Qu(t),timeout:s,maxBuffer:1<<20},(r,i,a)=>{o({ok:!r,code:r?r.code==="ENOENT"?"ENOENT":r.killed?"timeout":r.code:0,out:String(i||"")+String(a||"")})})})}function vb(n){let e=String(n||"").match(/Logged in to (\S+)(?: account| as) ([A-Za-z0-9-]+)/);return e?{host:e[1],account:e[2]}:null}function xb(n,{brew:e=!1}={}){return n==="missing"?e?{cmd:"brew install gh && gh auth login",text:"Install gh with Homebrew, then sign in"}:{cmd:"gh auth login",url:$a,text:"Install gh from "+$a+", then run gh auth login"}:n==="signed-out"?{cmd:"gh auth login",text:"Sign in to GitHub"}:null}async function Vr({env:n=process.env,timeoutMs:e=4e3}={}){let t=!!Zu(n),s={version:null,account:null,host:null,brew:t,checkedAt:new Date().toISOString()},o=Bs(n),r=(u,d={})=>({status:u,...s,...d,fix:xb(u,{brew:t})});if(!o)return r("missing");let i=await dm(o,["--version"],{env:n,timeoutMs:e});if(!i.ok)return r("missing");let a=(i.out.match(/gh version (\S+)/)||[])[1]||null,l=await dm(o,["auth","status"],{env:n,timeoutMs:e}),c=vb(l.out);return l.ok||l.code==="timeout"&&c?r("ready",{version:a,...c||{}}):r("signed-out",{version:a})}async function za({env:n=process.env,fresh:e=!1,now:t=Date.now,read:s=Vr}={}){let o=t(),r=Ht.value&&Ht.value.status==="ready"?10*6e4:3e4;return!e&&Ht.value&&o-Ht.at<r?Ht.value:(Ht.pending||(Ht.pending=Promise.resolve().then(()=>s({env:n})).then(i=>(Ht.value=i,Ht.at=t(),i)).finally(()=>{Ht.pending=null})),Ht.pending)}var $a,bb,Qu,Ht,Lo=J(()=>{Hs();$a="https://cli.github.com",bb=["/opt/homebrew/bin","/usr/local/bin"];Qu=(n=process.env)=>({...n,GH_PROMPT_DISABLED:"1",GH_NO_UPDATE_NOTIFIER:"1",NO_COLOR:"1",CLICOLOR:"0"});Ht={at:0,value:null,pending:null}});import{execFile as Ab}from"node:child_process";import Tb from"node:fs";function ed(n){let e=Array.isArray(n)?n:[],t=0,s=0,o=0;for(let a of e){if(!a||typeof a!="object")continue;let l=String(a.conclusion||(a.__typename==="StatusContext"||a.state&&!a.status?a.state:"")||"").toUpperCase(),c=String(a.status||"").toUpperCase();Eb.has(l)?s++:Ob.has(l)||c==="COMPLETED"&&!l?t++:o++}let r=t+s+o,i;return r?s?i=s+" of "+r+" check"+(r===1?"":"s")+" failing":o?i="Checks running · "+t+"/"+r+" passed":i=r===1?"Check passed":"All "+r+" checks passed":i="No checks",{total:r,passed:t,failed:s,pending:o,summary:i}}function Cb(n){let e=n;if(typeof n=="string")try{e=JSON.parse(n)}catch{return null}if(!e||typeof e!="object"||!Number.isInteger(e.number)||e.number<=0||String(e.state||"").toUpperCase()!=="OPEN")return null;let s=null;try{let o=new URL(String(e.url||""));o.protocol==="https:"&&!o.username&&!o.password&&(s=o.href)}catch{}return{number:e.number,title:String(e.title||"").replace(/[\u0000-\u001F\u007F]+/g," ").trim().slice(0,200)||"Pull request #"+e.number,state:e.isDraft?"draft":"open",url:s,branch:e.headRefName?String(e.headRefName).slice(0,200):null,checks:ed(e.statusCheckRollup)}}function Nb({cwd:n,branch:e,gh:t,timeoutMs:s=6e3}={}){return t=t||Bs()||"gh",new Promise(o=>{if(!n||!e)return o(null);try{if(!Tb.statSync(n).isDirectory())return o(null)}catch{return o(null)}Ab(t,["pr","view",e,"--json",_b],{cwd:n,timeout:s,maxBuffer:1<<20,env:{...process.env,GH_PROMPT_DISABLED:"1",GH_NO_UPDATE_NOTIFIER:"1",NO_COLOR:"1"}},(r,i)=>{if(r&&r.code==="ENOENT")return o({unavailable:!0});if(r)return o(null);o(Cb(String(i)))})})}var _b,Eb,Ob,Ya,td=J(()=>{Lo();_b="number,title,state,url,isDraft,headRefName,statusCheckRollup",Eb=new Set(["FAILURE","ERROR","TIMED_OUT","CANCELLED","ACTION_REQUIRED","STARTUP_FAILURE","STALE"]),Ob=new Set(["SUCCESS","NEUTRAL","SKIPPED"]);Ya=class{constructor({ttlMs:e=5*6e4,missTtlMs:t=10*6e4,view:s=Nb,onUpdate:o=()=>{},now:r=Date.now,max:i=2}={}){Object.assign(this,{ttlMs:e,missTtlMs:t,view:s,onUpdate:o,now:r,max:i}),this.items=new Map,this.running=new Set,this.queue=[],this.offUntil=0}key(e,t){return e+"\0"+t}get(e,t){if(!e||!t||process.env.LEFTOFF_PR==="0")return null;let s=this.key(e,t),o=this.items.get(s),r=this.now();return!(o&&r-o.at<(o.pr?this.ttlMs:this.missTtlMs))&&r>=this.offUntil&&this.refresh(e,t),o?o.pr:null}seed(e,t,s){this.items.set(this.key(e,t),{pr:s,at:this.now()+365*864e5})}refresh(e,t){let s=this.key(e,t);this.running.has(s)||this.queue.some(o=>o.k===s)||(this.queue.push({k:s,cwd:e,branch:t}),this.pump())}pump(){for(;this.running.size<this.max&&this.queue.length;){let{k:e,cwd:t,branch:s}=this.queue.shift();this.running.add(e),Promise.resolve().then(()=>this.view({cwd:t,branch:s})).catch(()=>null).then(o=>{this.running.delete(e),o&&o.unavailable&&(this.offUntil=this.now()+30*6e4,o=null);let r=this.items.get(e);if(this.items.set(e,{pr:o||null,at:this.now()}),JSON.stringify(r?r.pr:null)!==JSON.stringify(o||null))try{this.onUpdate({cwd:t,branch:s,pr:o||null})}catch{}this.pump()})}}}});import Wa from"node:fs";import{execFile as Ib}from"node:child_process";function Gs(n){let e=new RegExp(mm.source).exec(String(n||""));if(!e)return null;let t=e[1]+"/"+e[2].replace(/\.git$/,""),s=Number(e[3]);return s?{repo:t,number:s,url:"https://github.com/"+t+"/pull/"+s}:null}function Ja(n){let e=[],t=String(n||""),s=new RegExp(mm.source,"g"),o;for(;o=s.exec(t);){let r=Gs(o[0]);r&&!e.some(i=>i.url===r.url)&&e.push(r)}return e}function ym(n,{repo:e}={}){let t=String(n??"").trim();if(!t)return{error:"empty"};let s=Gs(t);if(s)return s;let o=t.match(/^([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)#(\d{1,7})$/);return o&&Number(o[2])?{repo:o[1],number:Number(o[2]),url:"https://github.com/"+o[1]+"/pull/"+Number(o[2])}:(o=t.match(/^#?(\d{1,7})$/),o&&Number(o[1])?e?{repo:e,number:Number(o[1]),url:"https://github.com/"+e+"/pull/"+Number(o[1])}:{error:"no_repo"}:{error:"bad_ref"})}function Zr(n){let t=String(n||"").trim().match(/^(?:https?:\/\/(?:[^@/]+@)?github\.com\/|git@github\.com:|ssh:\/\/git@github\.com\/)([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git)?\/?$/);return t?t[1]+"/"+t[2]:null}function wm(n){return typeof n=="string"?n:Array.isArray(n)?n.map(e=>e&&typeof e=="object"?e.type==="text"?e.text:e.type==="tool_result"?wm(e.content):"":typeof e=="string"?e:"").join(`
94
+ `):""}function Sm(n,{pending:e=new Set}={}){let t=new Map,s=(o,r)=>{let i=t.get(o.url);i?r==="created"&&(i.via="created"):t.set(o.url,{...o,via:r})};for(let o of String(n||"").split(`
95
+ `)){if(!o||o.indexOf("github.com")<0&&o.indexOf("gh pr create")<0&&o.indexOf("gh ")<0)continue;let r;try{r=JSON.parse(o)}catch{continue}let i=r&&r.message;if(!i)continue;let a=i.content;if(typeof a=="string"){for(let l of Ja(a))s(l,"mentioned");continue}if(Array.isArray(a)){for(let l of a)if(!(!l||typeof l!="object")){if(l.type==="text")for(let c of Ja(l.text))s(c,"mentioned");else if(l.type==="tool_use"){let c=l.input&&typeof l.input.command=="string"?l.input.command:"";if(Mb.test(c)&&l.id&&e.add(l.id),/\bgh\s+pr\s+(view|checkout|merge|edit|ready|review|comment|checks|diff)\b/.test(c))for(let u of Ja(c))s(u,"mentioned")}else if(l.type==="tool_result"&&l.tool_use_id&&e.has(l.tool_use_id)){e.delete(l.tool_use_id);let c=wm(l.content)+`
96
+ `+(r.toolUseResult&&typeof r.toolUseResult=="object"?String(r.toolUseResult.stdout||""):""),u=Ja(c);u.length&&s(u[u.length-1],"created")}}}}return[...t.values()]}function km(n,e,t=[]){let s=Array.isArray(n)?n.map(r=>({...r})):[],o=!1;for(let r of e){if(t.includes(r.url))continue;let i=s.find(a=>a.url===r.url);if(i){r.via==="created"&&i.via==="mentioned"&&(i.via="created",o=!0);continue}s.push({url:r.url,via:r.via}),o=!0}for(;s.length>gm;){let r=s.findIndex(i=>i.via==="mentioned");s.splice(r>=0?r:0,1),o=!0}return{links:s,changed:o}}function Qr(n){if(!Array.isArray(n))return[];let e=[];for(let t of n){let s=Gs(t&&typeof t=="object"?t.url:t);if(!s||e.some(r=>r.url===s.url))continue;let o=t&&typeof t=="object"&&["created","mentioned","manual"].includes(t.via)?t.via:"manual";e.push({url:s.url,via:o})}return e.slice(0,gm)}function ei(n){return Array.isArray(n)?[...new Set(n.map(e=>(Gs(e)||{}).url).filter(Boolean))].slice(0,100):[]}function pm(n){if(!n||typeof n!="object"||!Number.isInteger(n.number)||n.number<=0)return null;let e=Gs(n.url);if(!e)return null;let t=String(n.state||"").toUpperCase(),s=t==="MERGED"?"merged":t==="CLOSED"?"closed":n.isDraft?"draft":"open",o=Array.isArray(n.reviewRequests)?n.reviewRequests.map(i=>i&&(i.login||i.slug||i.name)||null).filter(Boolean).map(i=>Xr(i,80)):[],r=String(n.reviewDecision||"").toUpperCase();return{repo:e.repo,number:n.number,url:e.url,title:Xr(n.title)||"Pull request #"+n.number,state:s,branch:n.headRefName?Xr(n.headRefName):null,base:n.baseRefName?Xr(n.baseRefName):null,author:n.author&&n.author.login?Xr(n.author.login,80):null,createdAt:n.createdAt||null,updatedAt:n.updatedAt||null,mergedAt:n.mergedAt||null,review:r==="APPROVED"?"approved":r==="CHANGES_REQUESTED"?"changes_requested":r==="REVIEW_REQUIRED"?"review_required":null,reviewRequested:o,checks:ed(n.statusCheckRollup)}}function nd(n,e){return!n||n.state!=="open"&&n.state!=="draft"?null:n.checks&&n.checks.failed?"Checks failing":n.review==="changes_requested"?"Changes requested":e&&(n.reviewRequested||[]).some(t=>t.toLowerCase()===String(e).toLowerCase())?"Your review":null}function bm(n,{now:e=Date.now(),me:t=null}={}){let s={attention:[],open:[],draft:[],merged:[]};for(let r of n){let i=r.pr,a=nd(i,t);a?s.attention.push({...r,attention:a}):i.state==="open"?s.open.push(r):i.state==="draft"?s.draft.push(r):i.state==="merged"&&i.mergedAt&&e-Date.parse(i.mergedAt)<=7*qa&&s.merged.push(r)}let o=r=>(i,a)=>String(a.pr[r]||"").localeCompare(String(i.pr[r]||""));for(let r of Object.keys(s))s[r].sort(o(r==="merged"?"mergedAt":"updatedAt"));return[["attention","Needs attention"],["open","Open"],["draft","Draft"],["merged","Merged this week"]].map(([r,i])=>({key:r,label:i,rows:s[r]}))}function vm(n,e){let t=n.checks||{};return{repo:n.repo,number:n.number,url:n.url,title:n.title||null,state:n.state||null,via:e||null,checks:n.state?{total:t.total||0,failed:t.failed||0,pending:t.pending||0,summary:t.summary||""}:null}}function sd(n,{gh:e,timeoutMs:t=1e4,env:s=process.env}={}){let o=e||Bs(s);return new Promise(r=>{if(!o)return r({ok:!1,code:"ENOENT",out:""});Ib(o,n,{env:Qu(s),timeout:t,maxBuffer:8<<20},(i,a)=>{r({ok:!i,code:i?i.code==="ENOENT"?"ENOENT":i.killed?"timeout":i.code:0,out:String(a||"")})})})}function xm(n,e,{maxBytes:t=16<<20}={}){let s=Wa.statSync(n),o=e>s.size?0:e;if(s.size-o>t&&(o=s.size-t),s.size===o)return{text:"",next:o,size:s.size};let r=Wa.openSync(n,"r");try{let i=Buffer.alloc(s.size-o);Wa.readSync(r,i,0,i.length,o);let a=i.lastIndexOf(10);return a<0?{text:"",next:o,size:s.size}:{text:i.subarray(0,a+1).toString("utf8"),next:o+a+1,size:s.size}}finally{Wa.closeSync(r)}}var hm,mm,gm,Mb,Xr,qa,Va,od=J(()=>{Lo();td();hm="number,title,state,url,isDraft,headRefName,baseRefName,author,createdAt,updatedAt,mergedAt,reviewDecision,reviewRequests,statusCheckRollup",mm=/https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)\/pull\/(\d{1,7})(?![\d])/g,gm=24;Mb=/\bgh\s+pr\s+create\b/;Xr=(n,e=200)=>String(n??"").replace(/[\u0000-\u001F\u007F]+/g," ").trim().slice(0,e);qa=864e5;Va=class{constructor({run:e=sd,now:t=Date.now,ttlMs:s=5*6e4,missTtlMs:o=10*6e4,onUpdate:r=()=>{},max:i=2}={}){Object.assign(this,{run:e,now:t,ttlMs:s,missTtlMs:o,onUpdate:r,max:i}),this.views=new Map,this.lists=new Map,this.running=new Map,this.queue=[],this.offUntil=0}off(){return this.now()<this.offUntil||process.env.LEFTOFF_PR==="0"}stale(e,t){return!e||this.now()-e.at>=(t?this.ttlMs:this.missTtlMs)}viewStale(e){return e&&e.pr&&(e.pr.state==="merged"||e.pr.state==="closed")?this.now()-e.at>=6*36e5:this.stale(e,e&&e.pr)}view(e){let t=this.views.get(e);return this.viewStale(t)&&this.want("v",e),t?t.pr:null}list(e){let t=this.lists.get(e);return this.stale(t,t&&t.ok)&&this.want("l",e),t?t.prs:null}absorb(e){let t=this.now(),s=new Map;for(let o of e||[]){if(!o||!o.url||!o.state||!o.repo)continue;let r=this.views.get(o.url);(!r||t-r.at>3e4)&&this.views.set(o.url,{pr:o,at:t-this.ttlMs+6e4}),(o.state==="open"||o.state==="draft")&&(s.has(o.repo)||s.set(o.repo,[]),s.get(o.repo).push(o))}for(let[o,r]of s){let i=this.lists.get(o);(!i||!i.ok)&&this.lists.set(o,{prs:r,at:t-this.ttlMs+6e4,ok:!1})}}seedList(e,t){this.lists.set(e,{prs:t,at:this.now()+365*qa,ok:!0});for(let s of t)this.views.set(s.url,{pr:s,at:this.now()+365*qa})}seedView(e){this.views.set(e.url,{pr:e,at:this.now()+365*qa})}want(e,t){if(this.off())return null;let s=e+":"+t;if(this.running.has(s))return this.running.get(s);let o=this.queue.find(a=>a.k===s);if(o)return o.p;let r,i=new Promise(a=>{r=a});return this.queue.push({k:s,kind:e,arg:t,p:i,res:r}),this.pump(),i}pump(){for(;this.running.size<this.max&&this.queue.length;){let e=this.queue.shift();this.running.set(e.k,e.p),Promise.resolve().then(()=>this.fetch(e.kind,e.arg)).catch(()=>null).then(t=>{if(this.running.delete(e.k),e.res(t),t)try{this.onUpdate({kind:e.kind,arg:e.arg})}catch{}this.pump()})}}async fetch(e,t){let s=e==="v"?["pr","view",t,"--json",hm]:["pr","list","--repo",t,"--state","open","--limit","100","--json",hm],o=await this.run(s);if(o&&o.code==="ENOENT")return this.offUntil=this.now()+30*6e4,!1;let r=null;if(o&&o.ok)try{r=JSON.parse(o.out)}catch{}if(e==="v"){let l=r?pm(r):null,c=this.views.get(t);return!l&&c&&c.pr?(this.views.set(t,{pr:c.pr,at:this.now()-this.ttlMs+this.missTtlMs/2}),!1):(this.views.set(t,{pr:l,at:this.now()}),JSON.stringify(c?c.pr:null)!==JSON.stringify(l))}let i=Array.isArray(r)?r.map(pm).filter(Boolean):null,a=this.lists.get(t);if(!i)return this.lists.set(t,{prs:a?a.prs:null,at:this.now(),ok:!1}),!1;this.lists.set(t,{prs:i,at:this.now(),ok:!0});for(let l of i){let c=this.views.get(l.url);(!c||c.at<this.now())&&this.views.set(l.url,{pr:l,at:this.now()})}return JSON.stringify(a?a.prs:null)!==JSON.stringify(i)}async settle({urls:e=[],repos:t=[],waitMs:s=8e3,fresh:o=!1}={}){if(o){for(let l of e){let c=this.views.get(l);c&&c.at<=this.now()&&(c.at=0)}for(let l of t){let c=this.lists.get(l);c&&c.at<=this.now()&&(c.at=0)}}let r=[];for(let l of t){let c=this.lists.get(l);if(this.stale(c,c&&c.ok)){let u=this.want("l",l);u&&r.push(u)}}for(let l of e){let c=this.views.get(l);if(this.viewStale(c)){let u=this.want("v",l);u&&r.push(u)}}if(!r.length)return!0;let i,a=await Promise.race([Promise.all(r).then(()=>!0),new Promise(l=>{i=setTimeout(()=>l(!1),s),i.unref&&i.unref()})]);return clearTimeout(i),a}busy(){return this.running.size+this.queue.length>0}}});import Xa from"node:fs";import Am from"node:path";import Lb from"node:crypto";function Ks(n,e){if(!e||!n)return null;let t;return e.kind==="permission"||e.kind==="question"?t="ask:"+(e.ask&&e.ask.id||""):e.kind==="terminal"?t="terminal:"+(e.since||"")+":"+(e.message||""):e.kind==="reply"?t="reply:"+(e.since||"")+":"+(e.question||""):e.kind==="agent"?t="agent:"+(e.items||[]).map(s=>(s.id||"")+"="+(s.text||"")).sort().join(`
97
+ `):t=String(e.kind)+":"+(e.since||""),Lb.createHash("sha256").update(String(n)+`
98
+ `+t).digest("hex").slice(0,20)}var Db,Rb,Tm,Za,_m=J(()=>{Db=30;Rb=/^\.needs-dismissed\..+\.json$/,Tm=n=>{try{let e=JSON.parse(Xa.readFileSync(n,"utf8"));return e&&typeof e=="object"?e:null}catch{return null}},Za=class{constructor({root:e,machine:t,now:s=Date.now,ttlMs:o=2e3}={}){Object.assign(this,{root:e,machine:t,now:s,ttlMs:o}),this.cache=null}file(){return Am.join(this.root,".needs-dismissed."+String(this.machine).replace(/[^\w.-]/g,"_")+".json")}own(){let e=Tm(this.file())||{};return{dismissed:e.dismissed||{},restored:e.restored||{}}}active(){let e=this.now();if(this.cache&&e-this.cache.at<this.ttlMs)return this.cache.map;let t=new Map,s=new Map,o=[];try{o=Xa.readdirSync(this.root).filter(i=>Rb.test(i))}catch{}for(let i of o){let a=Tm(Am.join(this.root,i));if(a){for(let[l,c]of Object.entries(a.dismissed||{}))c&&(!t.has(l)||Date.parse(c.at)>Date.parse(t.get(l).at))&&t.set(l,c);for(let[l,c]of Object.entries(a.restored||{}))(!s.has(l)||Date.parse(c)>Date.parse(s.get(l)))&&s.set(l,c)}}let r=new Map;for(let[i,a]of t)(!s.has(i)||Date.parse(a.at)>Date.parse(s.get(i)))&&r.set(i,{key:i,...a});return this.cache={at:e,map:r},r}has(e){return!!e&&this.active().has(e)}list(){return[...this.active().values()].sort((e,t)=>Date.parse(t.at)-Date.parse(e.at))}write(e){let t=this.now()-Db*864e5;for(let[r,i]of Object.entries(e.dismissed))(!i||Date.parse(i.at)<t)&&delete e.dismissed[r];for(let[r,i]of Object.entries(e.restored))Date.parse(i)<t&&delete e.restored[r];let s=this.file(),o=s+".tmp-"+process.pid;Xa.writeFileSync(o,JSON.stringify(e,null,2)+`
99
+ `),Xa.renameSync(o,s),this.cache=null}dismiss(e,t={}){if(!e||typeof e!="string"||!/^[0-9a-f]{8,64}$/.test(e))throw new Error("bad key");let s=this.own(),o=(r,i)=>r==null?null:String(r).slice(0,i);return s.dismissed[e]={at:new Date(this.now()).toISOString(),sessionId:o(t.sessionId,200),title:o(t.title,200),line:o(t.line,300),kind:o(t.kind,20)},delete s.restored[e],this.write(s),s.dismissed[e]}restore(e){let t=this.own(),s=new Date(this.now()).toISOString(),o=0;for(let r of e)r&&(delete t.dismissed[r],t.restored[r]=s,o++);return o&&this.write(t),o}}});import Pb from"node:fs";import On from"node:path";import jb from"node:crypto";function el(n){let e=[],t=/`([^`\n]{2,200})`/g,s;for(;s=t.exec(String(n||""));){let o=s[1].trim();o&&!e.includes(o)&&e.push(o)}return e}function Qn(n,e=140){let t=String(n||"").replace(/\s+/g," ").trim();if(t=t.replace(/\[([^\]]+)\]\([^)]*\)/g,"$1").replace(/(\*\*|__)(.+?)\1/g,"$2").replace(/(^|\s)[*_](\S[^*_]*?)[*_](?=\s|$|[.,;:])/g,"$1$2").replace(/^#+\s*/,""),t=t.replace(/^(?:\d{1,2}[.)]|[-*+•])\s+/,"").replace(/^\[[ x]\]\s+/,""),t=t.replace(/\s*:\s*$/,""),t.length>e){let s=t.slice(0,e-1);t=s.slice(0,Math.max(s.lastIndexOf(" "),e-20)).replace(/[\s,;:.-]+$/,"")+"…"}return t}function Nm(n,e=[]){let t=Qn(n);if(!t||t.length<6)return null;let s=[...el(n),...e].filter((o,r,i)=>i.indexOf(o)===r).slice(0,3);return{text:t,codes:s}}function Wb(n){let e=[],t=String(n||"").split(`
100
+ `),s=new Set;for(let o=0;o<t.length;o++){let r=t[o].trim(),i=Ub.test(r),a=!i&&Hb.test(r);if(!i&&!a)continue;let l=0,c=0;for(let u=o+1;u<t.length&&l<8;u++){let d=t[u];if(!d.trim()){if(++c>1&&l)break;continue}if(/^\s*```/.test(d)){let m=u+1,g=[];for(;m<t.length&&!/^\s*```/.test(t[m]);)g.push(t[m++]);let S=e[e.length-1],k=g.map(w=>w.trim()).filter(Boolean);S&&S._list===o&&k.length&&k.join(`
101
101
  `).length<=300&&(S.codes=[...S.codes,k.join(`
102
- `)].filter((w,x,v)=>v.indexOf(w)===x).slice(0,3)),s.add(u),u=m,s.add(m);continue}let f=d.match(xm);if(!f){if(/^\s{2,}\S/.test(d)&&l)continue;break}c=0,s.add(u);let h=f[1].trim();if(/^\s{3,}/.test(d)&&!/^\s{0,2}\d/.test(d)&&l||(l++,Tm.test(Xn(h)))||a&&!(Xa.test(Xn(h))&&(Am.test(h)||/^(?:run|paste|type|enter)\b/i.test(Xn(h))&&Za(h).length))||i&&!Xa.test(Xn(h))&&!Am.test(h)&&!Za(h).length)continue;let p=_m(h);p&&(p._list=o,e.push(p))}}for(let o=0;o<t.length;o++){if(s.has(o))continue;let r=t[o];if(!(!r.trim()||/^\s*(?:#|```|>|\|)/.test(r)))for(let i of Hb(r)){let a=i.trim();if(a.length<12||a.length>400)continue;let l=Xn(a,400),c=l.match(Pb);if(!(c&&!Ub.test(c[1])&&Xa.test(c[1])||jb.test(l)||sd.test(l)||Fb.test(l)&&Xa.test(l.replace(/^(?:you(?:'|’)ll|you)\s+/i,""))&&/\byou|\byour|^(?:run|add|set|restart)\b/i.test(l))||Tm.test(l)&&!sd.test(l))continue;let d=[];if(/:\s*$/.test(a))for(let m=o+1;m<t.length&&m<o+12;m++){if(!t[m].trim()){if(d.length)break;continue}if(/^\s*```/.test(t[m])){let S=m+1,k=[];for(;S<t.length&&!/^\s*```/.test(t[S]);)k.push(t[S++]);k.join(`
102
+ `)].filter((w,x,v)=>v.indexOf(w)===x).slice(0,3)),s.add(u),u=m,s.add(m);continue}let f=d.match(Em);if(!f){if(/^\s{2,}\S/.test(d)&&l)continue;break}c=0,s.add(u);let h=f[1].trim();if(/^\s{3,}/.test(d)&&!/^\s{0,2}\d/.test(d)&&l||(l++,Cm.test(Qn(h)))||a&&!(Qa.test(Qn(h))&&(Om.test(h)||/^(?:run|paste|type|enter)\b/i.test(Qn(h))&&el(h).length))||i&&!Qa.test(Qn(h))&&!Om.test(h)&&!el(h).length)continue;let p=Nm(h);p&&(p._list=o,e.push(p))}}for(let o=0;o<t.length;o++){if(s.has(o))continue;let r=t[o];if(!(!r.trim()||/^\s*(?:#|```|>|\|)/.test(r)))for(let i of Yb(r)){let a=i.trim();if(a.length<12||a.length>400)continue;let l=Qn(a,400),c=l.match(Gb);if(!(c&&!zb.test(c[1])&&Qa.test(c[1])||Kb.test(l)||rd.test(l)||$b.test(l)&&Qa.test(l.replace(/^(?:you(?:'|’)ll|you)\s+/i,""))&&/\byou|\byour|^(?:run|add|set|restart)\b/i.test(l))||Cm.test(l)&&!rd.test(l))continue;let d=[];if(/:\s*$/.test(a))for(let m=o+1;m<t.length&&m<o+12;m++){if(!t[m].trim()){if(d.length)break;continue}if(/^\s*```/.test(t[m])){let S=m+1,k=[];for(;S<t.length&&!/^\s*```/.test(t[S]);)k.push(t[S++]);k.join(`
103
103
  `).trim().length<=300&&d.push(k.join(`
104
- `).trim());for(let w=m;w<=S;w++)s.add(w);break}let g=t[m].match(xm);if(!g)break;d.push(...Za(g[1])),s.add(m)}let f=a,h=l.search(/\b(?:you(?:'|’)ll|you will|you)\s+(?:also\s+|still\s+|now\s+|first\s+)?(?:need|have|must)/i);h>0&&h<80&&!sd.test(l)&&(f=l.slice(h));let p=_m(f,d);p&&e.push(p)}}return e.map(({_list:o,...r})=>r)}function Dm(n,{sessionId:e=""}={}){let t=Array.isArray(n)?n:[],s=[],o=new Set;for(let r=0;r<t.length;r++){let i=t[r];if(!i||i.role!=="assistant"||!i.text||i.toolUses&&i.toolUses.length)continue;let a=!1;for(let c=r+1;c<t.length;c++){let u=t[c];if(u.role==="user"&&u.text)break;if(u.role==="assistant"&&u.toolUses&&u.toolUses.length){a=!0;break}}if(a)continue;let l=[];for(let c=r+1;c<t.length;c++)for(let u of t[c].toolUses||[])u.input&&typeof u.input.command=="string"&&l.push(Em(u.input.command));for(let c of Bb(i.text)){let u=c.codes.filter(h=>/\s/.test(h)||/^[a-z][\w.-]*$/.test(h));if(u.length&&u.some(h=>/\s/.test(h)&&l.some(p=>p.includes(Em(h)))))continue;let d=Lm(c.text),f=d.slice(0,48);!d||o.has(d)||[...o].some(h=>h.slice(0,48)===f)||(o.add(d),s.push({key:Mb(e,c.text),text:c.text,codes:c.codes,at:i.ts||null}))}}return s.slice(-Cm)}function id(n){let e=_n.extname(String(n||"")).toLowerCase();return e===".md"||e===".mdx"||e===".markdown"?"markdown":Kb.has(e)?"image":e===".pdf"?"pdf":e===".html"||e===".htm"?"html":e===".txt"||e===".csv"?"text":"other"}function Yb(n){let e=String(n||""),t=_n.extname(e).toLowerCase();if(Gb.has(t))return!0;let s=_n.basename(e).toLowerCase();return!!(/^readme(\.|$)/.test(s)||/^(changelog|contributing|license)(\.|$)/.test(s)&&!t||/(^|\/)(spec|specs|plans?|docs?|design)\//i.test(e)&&(!t||t===".rst"||t===".adoc"||t===".org"))}function Wb(n,{cwd:e=null,home:t=null}={}){let s=String(n||"");for(let o of[e].filter(Boolean)){let r=String(o).replace(/\/+$/,"")+"/";if(s.startsWith(r))return s.slice(r.length)}if(t){let o=String(t).replace(/\/+$/,"")+"/";if(s.startsWith(o))return"~/"+s.slice(o.length)}return s}function ad(n,{cwd:e=null,home:t=null}={}){let s=new Map,o=new Set,r=[],i=new Map,a=new Set,l=(d,f,h)=>{let p=String(d).replace(/[).,]+$/,""),m=i.get(p),g=f?Xn(String(f),90):null;m?(g&&(m.title=g),h&&(!m.at||h>=m.at)&&(m.at=h)):i.set(p,{url:p,title:g,at:h||null})};for(let d of Array.isArray(n)?n:[])if(d){if(d.role==="assistant"&&d.text)for(let f of d.text.match(od)||[])l(f,null,d.ts);if(d.role==="user")for(let f of d.toolResults||[])for(let h of String(f.text||"").match(od)||[])l(h,null,d.ts);for(let f of d.toolUses||[]){let h=String(f.tool||""),p=f.input&&typeof f.input=="object"?f.input:{};if(h==="Read"&&p.file_path&&a.add(String(p.file_path)),h==="ExitPlanMode"&&typeof p.plan=="string"&&p.plan.trim()){r.push({id:f.tool_use_id||rd(p.plan,12),title:Jb(p.plan),at:d.ts||null});continue}if(/artifact/i.test(h)){let w=(JSON.stringify(p)+`
105
- `+String(f.text||"")).match(od)||[],x=p.title||p.name||p.label||p.artifact&&(p.artifact.title||p.artifact.name)||null;if(w.length)for(let v of w)l(v,x,d.ts);continue}if(!$b.has(h)||f.isError)continue;let m=String(p.file_path||p.notebook_path||p.path||"");if(!m||!_n.isAbsolute(m))continue;if(zb(m)){let k=s.get(m);s.set(m,{path:m,name:_n.basename(m),kind:k?k.kind:"new",type:"markdown",at:d.ts||null,plan:!0}),a.add(m);continue}if(!Yb(m)||h==="NotebookEdit"){o.add(m),a.add(m);continue}let g=s.get(m),S;g?S=g.kind:h==="Write"?S=f.text&&qb.test(f.text)&&!Om.test(f.text)?"edited":f.text&&Om.test(f.text)?"new":a.has(m)?"edited":"new":S="edited",a.add(m),s.set(m,{path:m,name:Wb(m,{cwd:e,home:t}),kind:S,type:id(m),at:d.ts||g&&g.at||null})}}let c=[...s.values()].sort((d,f)=>String(f.at||"").localeCompare(String(d.at||""))).slice(0,Im),u=[...i.values()].map(d=>({...d,title:d.title||"Artifact"})).sort((d,f)=>String(f.at||"").localeCompare(String(d.at||""))).slice(0,Nm);return{docs:c,plans:r.slice(-Mm).reverse(),artifacts:u,code:o.size}}function ld(n){if(!n||typeof n!="object")return null;let e=(n.docs||[]).filter(r=>!r.plan).length,t=(n.plans||[]).length+(n.docs||[]).filter(r=>r.plan).length,s=(n.artifacts||[]).length,o=Number(n.code)||0;return!e&&!t&&!s&&!o?null:{docs:e,plans:t,artifacts:s,code:o}}function el(n){return Array.isArray(n)?n.filter(e=>e&&typeof e.text=="string"&&typeof e.key=="string"&&/^[0-9a-f]{8,32}$/.test(e.key)).slice(-Cm).map(e=>({key:e.key,text:e.text.slice(0,200),codes:(Array.isArray(e.codes)?e.codes:[]).filter(t=>typeof t=="string").map(t=>t.slice(0,300)).slice(0,3),at:typeof e.at=="string"?e.at:null})):[]}function ei(n){if(!n||typeof n!="object")return null;let e=(i,a)=>typeof i=="string"?i.slice(0,a):null,t=(Array.isArray(n.docs)?n.docs:[]).filter(i=>i&&typeof i.path=="string").slice(0,Im).map(i=>({path:i.path.slice(0,1e3),name:e(i.name,300)||_n.basename(i.path),kind:i.kind==="new"?"new":"edited",type:id(i.path),at:e(i.at,40),...i.plan?{plan:!0}:{}})),s=(Array.isArray(n.plans)?n.plans:[]).filter(i=>i&&typeof i.id=="string").slice(0,Mm).map(i=>({id:i.id.slice(0,80),title:e(i.title,120)||"Plan",at:e(i.at,40)})),o=(Array.isArray(n.artifacts)?n.artifacts:[]).filter(i=>i&&Vb(i.url)).slice(0,Nm).map(i=>({url:i.url,title:e(i.title,120)||"Artifact",at:e(i.at,40)})),r=Math.max(0,Math.min(1e5,Number(n.code)||0));return!t.length&&!s.length&&!o.length&&!r?null:{docs:t,plans:s,artifacts:o,code:r}}function Pm(n,e){return typeof e!="string"||!e||e.length>1e3||!_n.isAbsolute(e)||e.includes("\0")||_n.normalize(e)!==e||e.split("/").includes("..")?!1:!!(n&&(n.docs||[]).some(t=>t.path===e))}function jm(n,{maxBytes:e=Qr,fsImpl:t=Ib}={}){let s;try{s=t.lstatSync(n)}catch{return{missing:!0}}if(!s.isFile()||s.isSymbolicLink())return{missing:!0};let o=id(n),r=Zb(n);if(s.size>e)return{tooBig:!0,size:s.size,type:o,mime:r};let i=t.readFileSync(n);return o==="markdown"||o==="text"||o==="html"||r==="image/svg+xml"?{type:o,mime:r,size:s.size,text:i.toString("utf8")}:{type:o,mime:r,size:s.size,base64:i.toString("base64")}}function Fm(n,e){for(let t of Array.isArray(n)?n:[])for(let s of t.toolUses||[])if(!(s.tool!=="ExitPlanMode"||!s.input||typeof s.input.plan!="string")&&(s.tool_use_id||rd(s.input.plan,12))===e)return s.input.plan;return null}var Cm,Im,Nm,Mm,Qr,Qa,rd,Lm,Mb,Lb,Db,xm,Rb,Xa,Am,Pb,jb,Fb,sd,Tm,Ub,Hb,Em,Gb,Kb,$b,od,zb,Jb,Om,qb,Rm,Vb,Xb,Zb,tl=J(()=>{Cm=20,Im=40,Nm=20,Mm=10,Qr=1024*1024,Qa=600*1024,rd=(n,e=16)=>Nb.createHash("sha256").update(String(n)).digest("hex").slice(0,e),Lm=n=>String(n||"").toLowerCase().replace(/`[^`]*`/g,e=>e.replace(/[^\w]/g,"")).replace(/[^a-z0-9]+/g," ").trim(),Mb=(n,e)=>rd(String(n||"")+`
106
- `+Lm(e)),Lb=/^(?:#{1,4}\s*)?(?:\*\*|__)?\s*(?:next steps? (?:for you|on your (?:end|side))|(?:manual|remaining manual) steps?|steps? (?:for you|you(?:'|’)ll need to (?:take|do))|to ?dos?(?: for you)?|action items(?: for you)?|what you(?:'|’)ll need to do|what you need to do|you(?:'|’)ll need to|you need to|things you(?:'|’)ll need to do|on your (?:end|side)|before (?:this|it) (?:works|goes live)|to finish (?:setup|setting (?:it|this) up)(?:,? you(?:'|’)ll need to)?)\s*(?:\(.*\))?\s*:?\s*(?:\*\*|__)?\s*:?\s*$/i,Db=/^(?:#{1,4}\s*)?(?:\*\*|__)?\s*(?:next steps?|follow[- ]ups?|remaining steps?)\s*:?\s*(?:\*\*|__)?\s*:?\s*$/i,xm=/^\s{0,6}(?:\d{1,2}[.)]|[-*+•])\s+(?:\[[ x]\]\s+)?(.+)$/,Rb="run|add|set|create|configure|enable|disable|turn on|turn off|install|restart|reload|relaunch|quit|reopen|open|log in|login|sign in|sign up|log out|verify|confirm|check|ask|request|grant|approve|accept|invite|merge|deploy|publish|push|upload|download|paste|copy|update|change|point|rotate|revoke|generate|register|connect|link|go to|visit|navigate to|click|select|choose|pick|enter|type|save|export|import|delete|remove|rename|move|buy|purchase|renew|pay|top up|email|tell|message|contact|reply|schedule|book|test|try|review|plug|unplug|reboot|wait for|make sure|ensure|replace|fill in|submit",Xa=new RegExp("^(?:then\\s+|and\\s+|also\\s+|finally,?\\s+|please\\s+)?(?:manually\\s+|go\\s+and\\s+|first\\s+)?(?:"+Rb+")\\b","i"),Am=/\b(dashboard|console|portal|settings page|app store|admin|dns|registrar|secret|api key|token|credentials?|password|2fa|login|log in|sign in|browser|account|billing|invoice|domain|cloudflare|vercel|netlify|stripe|github settings|repo settings|environment variables?|env var|keychain|system settings|preferences|permissions?|terminal|shell|ghostty|iterm|vs ?code|xcode|simulator|device|phone|iphone|restart|relaunch|manually|by hand|yourself|on your (?:end|side|machine|mac)|your (?:team|admin|manager|boss|it team))\b/i,Pb=/\b(?:you(?:'|’)ll|you will|you(?:'|’)d|you)\s+(?:also\s+|still\s+|now\s+|first\s+)?(?:need|have|want)\s+to\s+(\w[\w ]*)/i,jb=/\b(?:you\s+must|you(?:'|’)ll\s+have\s+to|you\s+should\s+(?:now\s+)?(?:run|add|set|restart|log in|sign in|verify|update|create|configure|enable))\b/i,Fb=/\b(?:manually|by hand|on your (?:end|side)|yourself)\b/i,sd=/\bI\s+(?:can(?:'|’)?t|cannot|don(?:'|’)t have (?:access|permission)|wasn(?:'|’)t able to|am unable to)\b[^.!?]*\b(?:you(?:'|’)ll|you will|you need|you(?:'|’)d need|you can|please)\b/i,Tm=/^(?:if|when|once|whenever|unless|in case|so|because|since|although|though)\b|\?\s*$|\b(?:let me|i(?:'|’)ll|i will|i(?:'|’)m going to|i(?:'|’)ve|i have|we(?:'|’)ll|i can do|want me to|should i|shall i)\b|\byou (?:can|could|may|might)\b(?! (?:need|have) to)|\bno need\b|\bdon(?:'|’)t need\b|\bwon(?:'|’)t need\b|\bnot need\b|\bdidn(?:'|’)t need\b/i,Ub=/^(?:know|be aware|understand|keep in mind|remember that|worry|think|decide whether|choose whether|pick whether)\b/i;Hb=n=>String(n).split(/(?<=[.!])\s+(?=[A-Z`*"(])/);Em=n=>String(n||"").replace(/\s+/g," ").trim().toLowerCase();Gb=new Set([".md",".mdx",".markdown",".txt",".html",".htm",".svg",".png",".jpg",".jpeg",".gif",".webp",".pdf",".csv"]),Kb=new Set([".png",".jpg",".jpeg",".gif",".webp",".svg"]),$b=new Set(["Write","Edit","MultiEdit","NotebookEdit"]),od=/https:\/\/claude\.ai\/code\/artifact\/[A-Za-z0-9_-]{4,80}/g;zb=n=>/\/\.claude\/plans\/[^/]+\.md$/i.test(String(n||""));Jb=n=>{let e=String(n||"").split(`
107
- `).map(s=>s.trim()).filter(Boolean),t=e.find(s=>/^#{1,3}\s+\S/.test(s));return Xn((t||e[0]||"Plan").replace(/^#+\s*/,""),80)||"Plan"},Om=/\b(?:created|new file)\b/i,qb=/\b(?:updated|overwr|modified|edited)\b/i;Rm=n=>{let e=ld(n);return!!(e&&(e.docs||e.plans||e.artifacts))};Vb=n=>typeof n=="string"&&/^https:\/\/claude\.ai\/code\/artifact\/[A-Za-z0-9_-]{4,80}$/.test(n);Xb={".md":"text/markdown",".mdx":"text/markdown",".markdown":"text/markdown",".txt":"text/plain",".csv":"text/csv",".html":"text/html",".htm":"text/html",".svg":"image/svg+xml",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".pdf":"application/pdf"},Zb=n=>Xb[_n.extname(String(n||"")).toLowerCase()]||"application/octet-stream"});import ti from"node:fs";import Um from"node:path";import Qb from"node:crypto";function Km(n,e=Date.now()){if(!n||!n.length)return[];let t=Math.min(...n.map(i=>Date.parse(i.at)||e)),s=Math.floor((e-t)/864e5),o=s>=1?", oldest "+s+(s===1?" day":" days"):"",r=["## To do","",n.length+(n.length===1?" to-do":" to-dos")+" open"+o+"."];for(let i of n.slice(0,10))r.push("- "+String(i.text).slice(0,140)+(i.sessionTitle?" ("+String(i.sessionTitle).slice(0,60)+")":""));return n.length>10&&r.push("- +"+(n.length-10)+" more"),r}var ev,Hm,tv,Bm,Gm,cd,nl,sl,ud=J(()=>{ev=30,Hm=200,tv=/^\.todos\..+\.json$/,Bm=/^[0-9a-f]{8,32}$/,Gm=n=>{try{let e=JSON.parse(ti.readFileSync(n,"utf8"));return e&&typeof e=="object"?e:null}catch{return null}},cd=(n,e)=>n==null?null:String(n).slice(0,e),nl=(n,e)=>Date.parse(n||0)>Date.parse(e||0),sl=class{constructor({root:e,machine:t,now:s=Date.now,ttlMs:o=2e3,random:r=()=>Qb.randomBytes(8).toString("hex")}={}){Object.assign(this,{root:e,machine:t,now:s,ttlMs:o,random:r}),this.cache=null}file(){return Um.join(this.root,".todos."+String(this.machine).replace(/[^\w.-]/g,"_")+".json")}own(){let e=Gm(this.file())||{};return{added:e.added||{},done:e.done||{},restored:e.restored||{},removed:e.removed||{}}}state(){let e=this.now();if(this.cache&&e-this.cache.at<this.ttlMs)return this.cache.v;let t=new Map,s=new Map,o=new Map,r=new Map,i=[];try{i=ti.readdirSync(this.root).filter(l=>tv.test(l))}catch{}for(let l of i){let c=Gm(Um.join(this.root,l));if(c){for(let[u,d]of Object.entries(c.added||{}))d&&typeof d.text=="string"&&!t.has(u)&&t.set(u,{key:u,text:d.text,sessionId:d.sessionId||null,at:d.at||null});for(let[u,d]of Object.entries(c.done||{}))d&&(!s.has(u)||nl(d.at,s.get(u).at))&&s.set(u,{key:u,...d});for(let[u,d]of Object.entries(c.restored||{}))(!o.has(u)||nl(d,o.get(u)))&&o.set(u,d);for(let[u,d]of Object.entries(c.removed||{}))(!r.has(u)||nl(d,r.get(u)))&&r.set(u,d)}}for(let[l,c]of o){let u=s.get(l);u&&!nl(u.at,c)&&s.delete(l)}for(let l of r.keys())t.delete(l),s.delete(l);let a={added:t,done:s};return this.cache={at:e,v:a},a}isDone(e){return!!e&&this.state().done.has(e)}doneList(){return[...this.state().done.values()].sort((e,t)=>Date.parse(t.at)-Date.parse(e.at))}addedList(){return[...this.state().added.values()]}write(e){let t=this.now()-ev*864e5;for(let[i,a]of Object.entries(e.done))(!a||Date.parse(a.at)<t)&&delete e.done[i];for(let[i,a]of Object.entries(e.restored))Date.parse(a)<t&&delete e.restored[i];for(let[i,a]of Object.entries(e.removed))Date.parse(a)<t&&delete e.removed[i];let s=Object.keys(e.added);if(s.length>Hm)for(let i of s.slice(0,s.length-Hm))delete e.added[i];ti.mkdirSync(this.root,{recursive:!0});let o=this.file(),r=o+".tmp-"+process.pid;ti.writeFileSync(r,JSON.stringify(e,null,2)+`
108
- `),ti.renameSync(r,o),this.cache=null}add(e,{sessionId:t=null}={}){let s=String(e??"").replace(/\s+/g," ").trim();if(!s)throw Object.assign(new Error("Type a to-do first."),{code:"empty"});let o=this.own(),r=String(this.random()).replace(/[^0-9a-f]/g,"").slice(0,16).padEnd(16,"0");return o.added[r]={text:s.slice(0,200),sessionId:cd(t,200),at:new Date(this.now()).toISOString()},this.write(o),{key:r,...o.added[r]}}done(e,t={}){if(typeof e!="string"||!Bm.test(e))throw Object.assign(new Error("bad key"),{code:"bad_key"});let s=this.own();return s.done[e]={at:new Date(this.now()).toISOString(),text:cd(t.text,200),sessionId:cd(t.sessionId,200)},delete s.restored[e],this.write(s),s.done[e]}restore(e){let t=this.own(),s=new Date(this.now()).toISOString(),o=0;for(let r of e)typeof r!="string"||!Bm.test(r)||(delete t.done[r],t.restored[r]=s,o++);return o&&this.write(t),o}}});function fd(n){return String(n||"").split(/[\\/]+/).filter(Boolean)}function nv(n){let e=fd(n);return e.length?e[e.length-1]:""}function ni(n){if(!n)return"no-repo";if(n.repoKey)return n.repoKey;let e=n.repo||{};return n.repoRoot||e.root||n.cwd||e.name||n.repoName||"no-repo"}function Kt(n){let e=[...new Set([...n].filter(Boolean))],t=new Map(e.map(o=>[o,1])),s=o=>{if(o==="no-repo")return"no repo";let r=fd(o);return r.length?r.slice(-Math.min(t.get(o),r.length)).join("/"):o};for(let o=0;o<12;o++){let r=new Map;for(let a of e){let l=s(a);r.set(l,[...r.get(l)||[],a])}let i=!1;for(let a of r.values())if(!(a.length<2))for(let l of a)t.get(l)<fd(l).length&&(t.set(l,t.get(l)+1),i=!0);if(!i)break}return new Map(e.map(o=>[o,s(o)]))}function $m(n,{countIn:e}={}){let t=new Map;for(let o of n){let r=ni(o),i=t.get(r)||{key:r,label:o.repoLabel||null,count:0,total:0,last:0};i.total++,i.last=Math.max(i.last,sv(o)),!i.label&&o.repoLabel&&(i.label=o.repoLabel),t.set(r,i)}if(e)for(let o of e){let r=t.get(ni(o));r&&r.count++}else for(let o of t.values())o.count=o.total;let s=Kt(t.keys());for(let o of t.values())o.label||(o.label=s.get(o.key));return[...t.values()].sort((o,r)=>r.last-o.last||o.label.localeCompare(r.label))}function Bs(n,e,{loose:t=!1}={}){let s=String(e||"").trim().toLowerCase().replace(/\/+$/,"");if(!s)return!1;let o=ni(n),r=[o,n.repoLabel,n.repoName,nv(o)].filter(Boolean).map(i=>String(i).toLowerCase());return r.includes(s)||s.includes("/")&&String(o).toLowerCase().endsWith("/"+s)?!0:t&&r.some(i=>i.includes(s))}function ol(n,e){if(!e)return n;let t=n.filter(s=>Bs(s,e));return t.length?t:n.filter(s=>Bs(s,e,{loose:!0}))}var dd,sv,Gs=J(()=>{dd=n=>{let e=Date.parse(n||"");return isNaN(e)?0:e},sv=n=>Math.max(dd(n.lastActivityAt),dd(n.endedAt),dd(n.startedAt))});function $t(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||"UTC"}catch{return"UTC"}}function Mo(n){if(!n||typeof n!="string")return!1;try{return new Intl.DateTimeFormat("en-US",{timeZone:n}),!0}catch{return!1}}function rv(n){return hd.has(n)||hd.set(n,new Intl.DateTimeFormat("en-US",{timeZone:n,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",weekday:"short",hourCycle:"h23"})),hd.get(n)}function Yt(n,e){let t={};for(let c of rv(e).formatToParts(new Date(n)))t[c.type]=c.value;let s=Number(t.year),o=Number(t.month),r=Number(t.day),i=Number(t.hour)%24,a=Number(t.minute),l=an.indexOf(String(t.weekday).slice(0,3).toLowerCase());return{y:s,mo:o,d:r,hh:i,mm:a,ss:Number(t.second),dow:l,day:an[l],date:s+"-"+String(o).padStart(2,"0")+"-"+String(r).padStart(2,"0")}}function Ym(n,e){let t=Yt(n,e);return Date.UTC(t.y,t.mo-1,t.d,t.hh,t.mm,t.ss)-Math.floor(n/1e3)*1e3}function zt(n,e,t,s,o,r){let i=Date.UTC(n,e-1,t,s,o),a=Ym(i,r),l=i-a,c=Ym(l,r);return c!==a&&(l=i-c),l}function pd(n,e){try{let t=new Intl.DateTimeFormat("en-US",{timeZone:e,timeZoneName:"short"}).formatToParts(new Date(n)).find(s=>s.type==="timeZoneName");return t?t.value:""}catch{return""}}function Lo(n){if(!Array.isArray(n))return an.slice();let e=an.filter(t=>n.map(s=>String(s).toLowerCase().slice(0,3)).includes(t));return e.length?e:an.slice()}function il(n={},e=$t()){let t=n||{},s=t.time===null?null:/^\d{1,2}:\d{2}$/.test(String(t.time||""))?String(t.time):"07:00",o=Mo(t.timeZone)?t.timeZone:e;return{time:s,tz:o,days:Lo(t.days),skipEmpty:t.skipEmpty!==!1,zoneOverride:Mo(t.timeZone)?t.timeZone:null}}function Do(n,e){let t=Yt(n,e.tz),s=e.days.includes(t.day),o=e.time?zt(t.y,t.mo,t.d,Math.floor(rl(e.time)/60),rl(e.time)%60,e.tz):null;return{date:t.date,day:t.day,isDay:s,at:o,past:o!=null&&n>=o}}function zm(n,e,{doneToday:t=!1}={}){if(!e.time)return null;let s=Yt(n,e.tz),o=Math.floor(rl(e.time)/60),r=rl(e.time)%60;for(let i=0;i<=8;i++){let a=new Date(Date.UTC(s.y,s.mo-1,s.d+i)),l=an[a.getUTCDay()];if(!e.days.includes(l))continue;let c=zt(a.getUTCFullYear(),a.getUTCMonth()+1,a.getUTCDate(),o,r,e.tz);if(i===0){if(t)continue;return c<=n?n:c}return c}return null}function Wm(n,e,t=Date.now()){if(!n)return"off";let[s,o]=n.split(":").map(Number),r=(s%12||12)+":"+String(o).padStart(2,"0")+" "+(s<12?"AM":"PM"),i=pd(t,e);return r+" · "+e+(i&&i!==e?" ("+i+")":"")}function Jm(n){let e=Lo(n);return e.length===7?"Every day":e.length===5&&Ks.every(t=>e.includes(t))?"Weekdays":e.length===2&&e.includes("sat")&&e.includes("sun")?"Weekends":e.map(t=>ov[t]).join(", ")}function qm({machines:n=[],me:e,slotAt:t=null,graceMs:s=20*6e4}={}){let o=[...new Set([...n,e].filter(Boolean))].sort(),r=o.indexOf(e);return{order:o,rank:r,first:o[0]||e,graceMs:s,at:t==null?null:t+r*s}}var an,ov,Ks,hd,rl,si=J(()=>{an=["sun","mon","tue","wed","thu","fri","sat"],ov={sun:"Sun",mon:"Mon",tue:"Tue",wed:"Wed",thu:"Thu",fri:"Fri",sat:"Sat"},Ks=["mon","tue","wed","thu","fri"];hd=new Map;rl=n=>{let[e,t]=String(n).split(":").map(Number);return(e||0)*60+(t||0)}});var hg={};Qt(hg,{CATCHUP_LENGTHS:()=>wd,CATCHUP_NAME_RE:()=>iv,CATCHUP_SCHEMA:()=>Zm,CATCHUP_SYSTEM:()=>ig,availableFrom:()=>xd,buildRecap:()=>og,catchupFileName:()=>dg,catchupLength:()=>Sd,catchupOptions:()=>hl,catchupZone:()=>dl,collectSessions:()=>vd,createCatchup:()=>ii,heuristicSummary:()=>oi,lastWorkday:()=>bd,modelLabel:()=>ug,parseBullets:()=>lg,parseCatchupMd:()=>Ad,parseDuration:()=>Qm,parseWhen:()=>yd,pickRepos:()=>ng,recapSession:()=>sg,renderCatchupMd:()=>fg,resolveWindow:()=>fl,stillOpenLists:()=>rg,summarize:()=>cg,summaryPrompt:()=>ag,windowChoices:()=>eg,workdayLabel:()=>lv,workdaysOf:()=>kd});function Sd(n=ke,e){let t=xu(n||ke,e);return{key:t,...wd[t]}}function dl(n=ke){let e=n&&n.digest&&n.digest.timeZone;return Mo(e)?e:$t()}function kd(n=ke){let e=n&&n.catchup&&n.catchup.workdays,t=Array.isArray(e)?Lo(e):null;return t&&t.length?t:Ks}function md(n){let e=new Error(n);return e.badWindow=!0,e}function cl(n,e,t){let s=Yt(n,t),o=new Date(Date.UTC(s.y,s.mo-1,s.d-e));return zt(o.getUTCFullYear(),o.getUTCMonth()+1,o.getUTCDate(),0,0,t)}function bd(n,e,t=Ks){for(let s=1;s<=7;s++){let o=cl(n,s,e),r=Yt(o+2*ri,e);if(t.includes(r.day))return{from:o,day:r.day,daysAgo:s}}return{from:cl(n,1,e),day:Yt(n-ll,e).day,daysAgo:1}}function lv(n,e,t=Ks){return"Since "+al[bd(n,e,t).day]}function cv(n){let e=n/ri;if(e<1||e<3&&!Number.isInteger(e))return"last "+Math.round(n/6e4)+" minutes";if(e===1)return"last hour";if(e<48&&Number.isInteger(e))return"last "+e+" hours";let t=e/24;return t===7?"last 7 days":Number.isInteger(t)?"last "+t+" days":"last "+Math.round(e)+" hours"}function Qm(n){let e=String(n??"").trim().toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(m|min|mins|minutes?|h|hr|hrs|hours?|d|days?|w|wk|weeks?)$/);if(!e)return null;let t=Number(e[1]),s=e[2][0],o=t*(s==="m"?6e4:s==="h"?ri:s==="d"?ll:7*ll);return o>0?o:null}function yd(n,e){let t=String(n??"").trim(),s=t.match(/^(\d{4})-(\d{2})-(\d{2})$/);if(s)return zt(+s[1],+s[2],+s[3],0,0,e);if(s=t.match(/^(\d{4})-(\d{2})-(\d{2})[T ](\d{1,2}):(\d{2})(?::\d{2}(?:\.\d+)?)?$/),s)return zt(+s[1],+s[2],+s[3],+s[4],+s[5],e);if(/^\d{4}-\d{2}-\d{2}T.*(Z|[+-]\d{2}:?\d{2})$/i.test(t)){let o=Date.parse(t);return isNaN(o)?null:o}return null}function fl(n,{now:e=Date.now(),tz:t=$t(),lastAt:s=null,to:o=null,workdays:r=Ks}={}){let i=o!=null&&o!==""?typeof o=="number"?o:yd(o,t):e;if(i==null||isNaN(i))throw md(`can't read the end of the window "`+o+'" (use a date like 2026-10-09 or 2026-10-09 18:00)');let a=n==null||n===!0?"":String(n).trim(),l=a.toLowerCase().replace(/[\s_]+/g,"-"),c=(h,p,m,g={})=>{if(!(h<i))throw md("the window starts after it ends ("+new Date(h).toISOString()+" → "+new Date(i).toISOString()+")");return{from:h,to:i,spec:a||"last",kind:p,label:m,...g}};if(!l||l==="last"||l==="since-last"||l==="last-catchup"||l==="since-last-catchup")return s!=null&&!isNaN(s)&&s<i?c(s,"last","Since your last catch-up"):c(i-ll,"last","Last 24 hours",{fallback:s!=null&&s>=i?"last catch-up is at or after the end of the window":"no earlier catch-up"});if(["workday","last-workday","lastworkday","since-workday"].includes(l)){let h=bd(i,t,r);return c(h.from,"workday","Since "+al[h.day])}if(l==="today")return c(av(i,t),"day","Today");if(l==="yesterday")return c(cl(i,1,t),"day","Since yesterday");let u=l.replace(/^since-/,"").slice(0,3);if(/^(since-)?(sun|mon|tue|wed|thu|fri|sat)[a-z]*$/.test(l)&&an.includes(u)&&al[u].toLowerCase().startsWith(l.replace(/^since-/,"")))for(let h=1;h<=7;h++){let p=cl(i,h,t);if(Yt(p+2*ri,t).day===u)return c(p,"day","Since "+al[u])}let d=Qm(l);if(d)return c(i-d,"duration",cv(d).replace(/^l/,"L"));let f=yd(a,t);if(f!=null)return c(f,"custom","Since "+ul(f,t));throw md(`can't read the window "`+a+'". Try: 24h, 3d, 1w, workday, yesterday, friday, or a date like 2026-10-09')}function eg({now:n=Date.now(),tz:e=$t(),lastAt:t=null,workdays:s=Ks}={}){let o=c=>{try{return fl(c,{now:n,tz:e,lastAt:t,workdays:s})}catch{return null}},r=o("last"),i=o("24h"),a=o("workday"),l=o("7d");return[{key:"last",label:t!=null?"Since I last caught up":"Since I last caught up (none yet: 24h)",from:r&&G(r.from)},{key:"24h",label:"Last 24 hours",from:i&&G(i.from)},{key:"workday",label:a?a.label:"Since the last workday",from:a&&G(a.from)},{key:"7d",label:"Last 7 days",from:l&&G(l.from)},{key:"custom",label:"Custom…",from:null}]}function vd(n,e={}){let{from:t,to:s,now:o=Date.now(),cfg:r=ke,machine:i=null}=e,a=e.repoKeyFor||(c=>ni(c)),l=[];for(let{session:c,checkpoints:u=[]}of n){if(!c||!c.id||c.archived||c.ignored||!c.agent&&(ft(c,u.length)||fo(c,u.length)))continue;let d=u.filter(x=>x&&x.createdAt).slice().sort((x,v)=>R(x.createdAt)-R(v.createdAt)),f=d.filter(x=>Vm(R(x.createdAt),t,s)),h=i&&c.machine&&c.machine!==i,p=c.agent?c.agentStatus:bt(c,o,h?{...r,staleAfterSec:Math.max(r.staleAfterSec||180,900)}:r),m=[c.lastActivityAt,c.startedAt,c.endedAt].some(x=>Vm(R(x),t,s)),g=c.agent?uv(c):(e.needsOf?e.needsOf(c,p):dv(c,p))||null,S=d[d.length-1]||null,k=S&&/^YOU:/i.test(S.next||"")&&p!=="ended"&&p!=="stale"&&!c.endedAt,w=s>=o-6e4;!f.length&&!m&&!(w&&(g||k))||l.push({s:c,cps:d,inWin:f,latest:S,status:p,needs:g,youNext:k,repoKey:a(c)})}return l}function uv(n){let e=n.agentNeeds;return!e||!e.items||!e.items.length?null:{kind:"agent",message:e.items.map(t=>t.text.replace(/\s*\n\s*/g," ")).join("; ")+(n.agent.reply?" ("+n.agent.reply.label+")":n.agent.replyHint?" ("+n.agent.replyHint+")":"")}}function dv(n,e){return e==="ended"||e==="stale"||n.endedAt?null:n.needsInput?{kind:"terminal",message:n.lastNotificationMessage||(n.lastNotification?String(n.lastNotification).replace(/_/g," "):"waiting in the terminal")}:n.question&&!n.inTurn?{kind:"reply",message:typeof n.question=="string"?n.question:n.question.text||"asked you a question"}:null}function xd(n,{labels:e}={}){let t=new Map;for(let i of n){let a=t.get(i.repoKey)||{key:i.repoKey,count:0,needsYou:0,last:0};a.count++,(i.needs||i.youNext)&&a.needsYou++,a.last=Math.max(a.last,tg(i)),t.set(i.repoKey,a)}let s=e||Kt(t.keys()),o=[...t.values()].map(i=>({...i,label:s.get(i.key)||i.key,last:i.last?G(i.last):null})).sort((i,a)=>(a.needsYou>0)-(i.needsYou>0)||(R(a.last)||0)-(R(i.last)||0)||i.label.localeCompare(a.label)),r=[...new Set(n.map(i=>i.s.machine).filter(Boolean))].sort();return{repos:o,machines:r}}function ng(n,e=[],t){let s=(e||[]).flatMap(a=>String(a).split(",")).map(a=>a.trim()).filter(Boolean);if(!s.length)return{keys:null,unmatched:[]};let o=new Set,r=[],i=t||Kt(n.map(a=>a.repoKey));for(let a of s){let l=u=>n.filter(d=>Bs({...d.s,repoKey:d.repoKey,repoLabel:i.get(d.repoKey),repoName:me(d.s)},a,{loose:u})),c=l(!1);c.length||(c=l(!0)),c.length||r.push(a);for(let u of c)o.add(u.repoKey)}return{keys:o,unmatched:r}}function hv(n){return n.lastPreview?"no checkpoint yet; last reply: "+F(n.lastPreview,220):n.firstPrompt?"no checkpoint yet; started with “"+F(n.firstPrompt,200)+"”":"no checkpoint yet"+(n.turns?" ("+n.turns+" turn"+(n.turns===1?"":"s")+")":"")}function pv(n,e){let t=n.s;return n.needs?"needs you":n.status==="ended"?"closed"+(t.endedAt?" "+ul(R(t.endedAt),e):""):n.status==="stale"?"gone quiet":n.status==="dormant"?"idle":n.status==="needs_input"?"needs you":n.status}function sg(n,{length:e,tz:t,patterns:s,labels:o}={}){let r=e||wd.informative,i=m=>m==null||m===""?m:fa(String(m),s),a=n.s,l=n.latest,c=gd([...n.inWin.map(m=>m.doing),...r.done>4&&l&&l.recap&&n.inWin.includes(l)?[l.recap]:[]]).slice(-r.done),u=l&&(R(a.lastActivityAt)||0)>R(l.createdAt)+5*6e4,d=Po(l&&l.next).toLowerCase().replace(/[.!?…]+$/,""),f=gd([l&&l.openQuestion,...l&&l.questions||[]]).filter(m=>Po(m).toLowerCase().replace(/[.!?…]+$/,"")!==d).slice(0,r.questions),h=gd((l&&l.nextSteps||[]).map(Po)).filter(m=>m.toLowerCase()!==Po(l&&l.next).toLowerCase()).slice(0,Math.max(0,r.steps-1)),p=a.repo&&a.repo.branch;return{id:a.id,title:i(a.title||"Untitled session"),repoKey:n.repoKey,repo:o&&o.get(n.repoKey)||me(a),branch:p||null,machine:a.machine||null,cwd:a.cwd||null,status:n.status,state:pv(n,t),lastActivityAt:G(tg(n))||null,checkpoints:n.inWin.length,done:c.map(m=>i(F(m,240))),stoppedAt:i(l?F(l.stoppedAt,280):hv(a)),next:l?i(F(l.next,240)):null,nextSteps:h.map(m=>i(F(m,200))),questions:f.map(m=>i(F(m,240))),needsYou:n.needs?i(F(n.needs.message||n.needs.question||"waiting on you",200)):n.youNext?i(F(Po(l.next),200)):null,needsKind:n.needs?n.needs.kind||"terminal":n.youNext?"next":null,behind:!!u,lastReply:u&&a.lastPreview&&!fv(a.lastPreview,l&&l.stoppedAt)?i(F(a.lastPreview,200)):null,resume:a.agent?null:uo(a),agent:a.agent?{name:a.agent.name,display:a.agent.display,url:a.agent.url||null,reply:a.agent.reply||null}:null}}function og(n,e={}){let{now:t=Date.now(),cfg:s=ke,machine:o=null}=e,r=e.tz||dl(s),i=e.window,a=Sd(s,e.lengthOverride),l=s.redact&&s.redact.patterns||[],c=vd(n,{...e,from:i.from,to:i.to,now:t,cfg:s,machine:o}),u=(e.machines||[]).flatMap(w=>String(w).split(",")).map(w=>w.trim()).filter(Boolean);u.length&&(c=c.filter(w=>u.includes(w.s.machine||o)));let d=Kt(c.map(w=>w.repoKey)),f=xd(c,{labels:d}),{keys:h,unmatched:p}=ng(c,e.repos,d);h&&(c=c.filter(w=>h.has(w.repoKey)));let m=c.map(w=>sg(w,{length:a,tz:r,patterns:l,labels:d})).sort((w,x)=>!!x.needsYou-!!w.needsYou||(R(x.lastActivityAt)||0)-(R(w.lastActivityAt)||0)),g=new Map;for(let w of m)g.has(w.repoKey)||g.set(w.repoKey,{key:w.repoKey,label:w.repo,sessions:[]}),g.get(w.repoKey).sessions.push(w);let S=[...g.values()],k=[...new Set(m.map(w=>w.machine).filter(Boolean))].sort();return{window:{from:G(i.from),to:G(i.to),spec:i.spec,kind:i.kind,label:i.label,fallback:i.fallback||null,tz:r},length:a.key,repos:S,sessions:m,needsYou:m.filter(w=>w.needsYou),stillOpen:rg(e.lists,{keys:h,labels:S.map(w=>w.label),from:R(i.from),to:R(i.to)}),machines:k,selection:{repos:h?S.map(w=>w.label):"all",repoKeys:h?[...h]:null,machines:u.length?u:"all",unmatched:p},available:f,madeOn:o}}function rg(n,{keys:e=null,labels:t=[],from:s=0,to:o=1/0}={}){let r=[];for(let i of n||[]){if(!i||i.archived||!(i.total>i.done)||e&&!(e.has(i.repoKey)||t.includes(i.repo)))continue;let a=(i.items||[]).filter(c=>!c.done),l=(i.items||[]).filter(c=>c.done&&c.at&&R(c.at)>=s&&R(c.at)<=o);r.push({ref:i.ref,name:i.name,repo:i.repo||"no repo",done:i.done,total:i.total,next:a.slice(0,3).map(c=>({n:c.n,text:c.text})),ticked:l.map(c=>({n:c.n,text:c.text})),sourceSession:i.sourceSession||null,updated:i.updated})}return r.sort((i,a)=>(R(a.updated)||0)-(R(i.updated)||0)).slice(0,20)}function ag(n,{maxChars:e=24e3}={}){let t=["Window: "+n.window.label+" ("+n.window.from+" to "+n.window.to+")",""];for(let o of n.repos){t.push("Repo "+o.label+":");for(let r of o.sessions)t.push('- "'+r.title+'" ['+r.state+"]"+(r.needsYou?" NEEDS YOU: "+r.needsYou+".":"")+(r.done.length?" Done: "+r.done.map(En).join("; ")+".":"")+(r.stoppedAt?" Stopped at: "+En(r.stoppedAt)+".":"")+(r.next?" Next: "+En(r.next)+".":"")+(r.questions.length?" Open: "+r.questions.map(En).join("; ")+".":""))}for(let o of n.stillOpen||[])t.push('Open list "'+o.name+'" ('+o.repo+"): "+o.done+"/"+o.total+" done"+(o.ticked.length?", "+o.ticked.length+" ticked off in this window":"")+"; next: "+o.next.map(r=>En(r.text)).join("; ")+".");let s=t.join(`
104
+ `).trim());for(let w=m;w<=S;w++)s.add(w);break}let g=t[m].match(Em);if(!g)break;d.push(...el(g[1])),s.add(m)}let f=a,h=l.search(/\b(?:you(?:'|’)ll|you will|you)\s+(?:also\s+|still\s+|now\s+|first\s+)?(?:need|have|must)/i);h>0&&h<80&&!rd.test(l)&&(f=l.slice(h));let p=Nm(f,d);p&&e.push(p)}}return e.map(({_list:o,...r})=>r)}function Um(n,{sessionId:e=""}={}){let t=Array.isArray(n)?n:[],s=[],o=new Set;for(let r=0;r<t.length;r++){let i=t[r];if(!i||i.role!=="assistant"||!i.text||i.toolUses&&i.toolUses.length)continue;let a=!1;for(let c=r+1;c<t.length;c++){let u=t[c];if(u.role==="user"&&u.text)break;if(u.role==="assistant"&&u.toolUses&&u.toolUses.length){a=!0;break}}if(a)continue;let l=[];for(let c=r+1;c<t.length;c++)for(let u of t[c].toolUses||[])u.input&&typeof u.input.command=="string"&&l.push(Im(u.input.command));for(let c of Wb(i.text)){let u=c.codes.filter(h=>/\s/.test(h)||/^[a-z][\w.-]*$/.test(h));if(u.length&&u.some(h=>/\s/.test(h)&&l.some(p=>p.includes(Im(h)))))continue;let d=Fm(c.text),f=d.slice(0,48);!d||o.has(d)||[...o].some(h=>h.slice(0,48)===f)||(o.add(d),s.push({key:Fb(e,c.text),text:c.text,codes:c.codes,at:i.ts||null}))}}return s.slice(-Dm)}function ld(n){let e=On.extname(String(n||"")).toLowerCase();return e===".md"||e===".mdx"||e===".markdown"?"markdown":qb.has(e)?"image":e===".pdf"?"pdf":e===".html"||e===".htm"?"html":e===".txt"||e===".csv"?"text":"other"}function Xb(n){let e=String(n||""),t=On.extname(e).toLowerCase();if(Jb.has(t))return!0;let s=On.basename(e).toLowerCase();return!!(/^readme(\.|$)/.test(s)||/^(changelog|contributing|license)(\.|$)/.test(s)&&!t||/(^|\/)(spec|specs|plans?|docs?|design)\//i.test(e)&&(!t||t===".rst"||t===".adoc"||t===".org"))}function Zb(n,{cwd:e=null,home:t=null}={}){let s=String(n||"");for(let o of[e].filter(Boolean)){let r=String(o).replace(/\/+$/,"")+"/";if(s.startsWith(r))return s.slice(r.length)}if(t){let o=String(t).replace(/\/+$/,"")+"/";if(s.startsWith(o))return"~/"+s.slice(o.length)}return s}function cd(n,{cwd:e=null,home:t=null}={}){let s=new Map,o=new Set,r=[],i=new Map,a=new Set,l=new Set,c=(f,h,p)=>{let m=String(f).replace(/[).,]+$/,""),g=i.get(m),S=h?Qn(String(h),90):null;g?(S&&(g.title=S),p&&(!g.at||p>=g.at)&&(g.at=p)):i.set(m,{url:m,title:S,at:p||null})};for(let f of Array.isArray(n)?n:[])if(f){if(f.role==="assistant"&&f.text)for(let h of f.text.match(id)||[])c(h,null,f.ts);if(f.role==="user")for(let h of f.toolResults||[])for(let p of String(h.text||"").match(id)||[])c(p,null,f.ts);for(let h of f.toolUses||[]){let p=String(h.tool||""),m=h.input&&typeof h.input=="object"?h.input:{};if(p==="Read"&&m.file_path&&a.add(String(m.file_path)),p==="ExitPlanMode"&&typeof m.plan=="string"&&m.plan.trim()){r.push({id:h.tool_use_id||ad(m.plan,12),title:Qb(m.plan),at:f.ts||null});continue}if(/artifact/i.test(p)){let x=(JSON.stringify(m)+`
105
+ `+String(h.text||"")).match(id)||[],v=m.title||m.name||m.label||m.artifact&&(m.artifact.title||m.artifact.name)||null;if(x.length)for(let b of x)c(b,v,f.ts);continue}if(!Vb.has(p)||h.isError)continue;let g=String(m.file_path||m.notebook_path||m.path||"");if(!g||!On.isAbsolute(g))continue;if(!Mm(g)&&l.size<2e3&&l.add(g),Mm(g)){let w=s.get(g);s.set(g,{path:g,name:On.basename(g),kind:w?w.kind:"new",type:"markdown",at:f.ts||null,plan:!0}),a.add(g);continue}if(!Xb(g)||p==="NotebookEdit"){o.add(g),a.add(g);continue}let S=s.get(g),k;S?k=S.kind:p==="Write"?k=h.text&&ev.test(h.text)&&!Lm.test(h.text)?"edited":h.text&&Lm.test(h.text)?"new":a.has(g)?"edited":"new":k="edited",a.add(g),s.set(g,{path:g,name:Zb(g,{cwd:e,home:t}),kind:k,type:ld(g),at:f.ts||S&&S.at||null})}}let u=[...s.values()].sort((f,h)=>String(h.at||"").localeCompare(String(f.at||""))).slice(0,Rm),d=[...i.values()].map(f=>({...f,title:f.title||"Artifact"})).sort((f,h)=>String(h.at||"").localeCompare(String(f.at||""))).slice(0,Pm);return{docs:u,plans:r.slice(-jm).reverse(),artifacts:d,code:o.size,editPaths:[...l]}}function ud(n){if(!n||typeof n!="object")return null;let e=(n.docs||[]).filter(r=>!r.plan).length,t=(n.plans||[]).length+(n.docs||[]).filter(r=>r.plan).length,s=(n.artifacts||[]).length,o=Number(n.code)||0;return!e&&!t&&!s&&!o?null:{docs:e,plans:t,artifacts:s,code:o}}function nl(n){return Array.isArray(n)?n.filter(e=>e&&typeof e.text=="string"&&typeof e.key=="string"&&/^[0-9a-f]{8,32}$/.test(e.key)).slice(-Dm).map(e=>({key:e.key,text:e.text.slice(0,200),codes:(Array.isArray(e.codes)?e.codes:[]).filter(t=>typeof t=="string").map(t=>t.slice(0,300)).slice(0,3),at:typeof e.at=="string"?e.at:null})):[]}function ni(n){if(!n||typeof n!="object")return null;let e=(i,a)=>typeof i=="string"?i.slice(0,a):null,t=(Array.isArray(n.docs)?n.docs:[]).filter(i=>i&&typeof i.path=="string").slice(0,Rm).map(i=>({path:i.path.slice(0,1e3),name:e(i.name,300)||On.basename(i.path),kind:i.kind==="new"?"new":"edited",type:ld(i.path),at:e(i.at,40),...i.plan?{plan:!0}:{}})),s=(Array.isArray(n.plans)?n.plans:[]).filter(i=>i&&typeof i.id=="string").slice(0,jm).map(i=>({id:i.id.slice(0,80),title:e(i.title,120)||"Plan",at:e(i.at,40)})),o=(Array.isArray(n.artifacts)?n.artifacts:[]).filter(i=>i&&tv(i.url)).slice(0,Pm).map(i=>({url:i.url,title:e(i.title,120)||"Artifact",at:e(i.at,40)})),r=Math.max(0,Math.min(1e5,Number(n.code)||0));return!t.length&&!s.length&&!o.length&&!r?null:{docs:t,plans:s,artifacts:o,code:r}}function Bm(n,e){return typeof e!="string"||!e||e.length>1e3||!On.isAbsolute(e)||e.includes("\0")||On.normalize(e)!==e||e.split("/").includes("..")?!1:!!(n&&(n.docs||[]).some(t=>t.path===e))}function Gm(n,{maxBytes:e=ti,fsImpl:t=Pb}={}){let s;try{s=t.lstatSync(n)}catch{return{missing:!0}}if(!s.isFile()||s.isSymbolicLink())return{missing:!0};let o=ld(n),r=sv(n);if(s.size>e)return{tooBig:!0,size:s.size,type:o,mime:r};let i=t.readFileSync(n);return o==="markdown"||o==="text"||o==="html"||r==="image/svg+xml"?{type:o,mime:r,size:s.size,text:i.toString("utf8")}:{type:o,mime:r,size:s.size,base64:i.toString("base64")}}function Km(n,e){for(let t of Array.isArray(n)?n:[])for(let s of t.toolUses||[])if(!(s.tool!=="ExitPlanMode"||!s.input||typeof s.input.plan!="string")&&(s.tool_use_id||ad(s.input.plan,12))===e)return s.input.plan;return null}var Dm,Rm,Pm,jm,ti,tl,ad,Fm,Fb,Ub,Hb,Em,Bb,Qa,Om,Gb,Kb,$b,rd,Cm,zb,Yb,Im,Jb,qb,Vb,id,Mm,Qb,Lm,ev,Hm,tv,nv,sv,sl=J(()=>{Dm=20,Rm=40,Pm=20,jm=10,ti=1024*1024,tl=600*1024,ad=(n,e=16)=>jb.createHash("sha256").update(String(n)).digest("hex").slice(0,e),Fm=n=>String(n||"").toLowerCase().replace(/`[^`]*`/g,e=>e.replace(/[^\w]/g,"")).replace(/[^a-z0-9]+/g," ").trim(),Fb=(n,e)=>ad(String(n||"")+`
106
+ `+Fm(e)),Ub=/^(?:#{1,4}\s*)?(?:\*\*|__)?\s*(?:next steps? (?:for you|on your (?:end|side))|(?:manual|remaining manual) steps?|steps? (?:for you|you(?:'|’)ll need to (?:take|do))|to ?dos?(?: for you)?|action items(?: for you)?|what you(?:'|’)ll need to do|what you need to do|you(?:'|’)ll need to|you need to|things you(?:'|’)ll need to do|on your (?:end|side)|before (?:this|it) (?:works|goes live)|to finish (?:setup|setting (?:it|this) up)(?:,? you(?:'|’)ll need to)?)\s*(?:\(.*\))?\s*:?\s*(?:\*\*|__)?\s*:?\s*$/i,Hb=/^(?:#{1,4}\s*)?(?:\*\*|__)?\s*(?:next steps?|follow[- ]ups?|remaining steps?)\s*:?\s*(?:\*\*|__)?\s*:?\s*$/i,Em=/^\s{0,6}(?:\d{1,2}[.)]|[-*+•])\s+(?:\[[ x]\]\s+)?(.+)$/,Bb="run|add|set|create|configure|enable|disable|turn on|turn off|install|restart|reload|relaunch|quit|reopen|open|log in|login|sign in|sign up|log out|verify|confirm|check|ask|request|grant|approve|accept|invite|merge|deploy|publish|push|upload|download|paste|copy|update|change|point|rotate|revoke|generate|register|connect|link|go to|visit|navigate to|click|select|choose|pick|enter|type|save|export|import|delete|remove|rename|move|buy|purchase|renew|pay|top up|email|tell|message|contact|reply|schedule|book|test|try|review|plug|unplug|reboot|wait for|make sure|ensure|replace|fill in|submit",Qa=new RegExp("^(?:then\\s+|and\\s+|also\\s+|finally,?\\s+|please\\s+)?(?:manually\\s+|go\\s+and\\s+|first\\s+)?(?:"+Bb+")\\b","i"),Om=/\b(dashboard|console|portal|settings page|app store|admin|dns|registrar|secret|api key|token|credentials?|password|2fa|login|log in|sign in|browser|account|billing|invoice|domain|cloudflare|vercel|netlify|stripe|github settings|repo settings|environment variables?|env var|keychain|system settings|preferences|permissions?|terminal|shell|ghostty|iterm|vs ?code|xcode|simulator|device|phone|iphone|restart|relaunch|manually|by hand|yourself|on your (?:end|side|machine|mac)|your (?:team|admin|manager|boss|it team))\b/i,Gb=/\b(?:you(?:'|’)ll|you will|you(?:'|’)d|you)\s+(?:also\s+|still\s+|now\s+|first\s+)?(?:need|have|want)\s+to\s+(\w[\w ]*)/i,Kb=/\b(?:you\s+must|you(?:'|’)ll\s+have\s+to|you\s+should\s+(?:now\s+)?(?:run|add|set|restart|log in|sign in|verify|update|create|configure|enable))\b/i,$b=/\b(?:manually|by hand|on your (?:end|side)|yourself)\b/i,rd=/\bI\s+(?:can(?:'|’)?t|cannot|don(?:'|’)t have (?:access|permission)|wasn(?:'|’)t able to|am unable to)\b[^.!?]*\b(?:you(?:'|’)ll|you will|you need|you(?:'|’)d need|you can|please)\b/i,Cm=/^(?:if|when|once|whenever|unless|in case|so|because|since|although|though)\b|\?\s*$|\b(?:let me|i(?:'|’)ll|i will|i(?:'|’)m going to|i(?:'|’)ve|i have|we(?:'|’)ll|i can do|want me to|should i|shall i)\b|\byou (?:can|could|may|might)\b(?! (?:need|have) to)|\bno need\b|\bdon(?:'|’)t need\b|\bwon(?:'|’)t need\b|\bnot need\b|\bdidn(?:'|’)t need\b/i,zb=/^(?:know|be aware|understand|keep in mind|remember that|worry|think|decide whether|choose whether|pick whether)\b/i;Yb=n=>String(n).split(/(?<=[.!])\s+(?=[A-Z`*"(])/);Im=n=>String(n||"").replace(/\s+/g," ").trim().toLowerCase();Jb=new Set([".md",".mdx",".markdown",".txt",".html",".htm",".svg",".png",".jpg",".jpeg",".gif",".webp",".pdf",".csv"]),qb=new Set([".png",".jpg",".jpeg",".gif",".webp",".svg"]),Vb=new Set(["Write","Edit","MultiEdit","NotebookEdit"]),id=/https:\/\/claude\.ai\/code\/artifact\/[A-Za-z0-9_-]{4,80}/g;Mm=n=>/\/\.claude\/plans\/[^/]+\.md$/i.test(String(n||""));Qb=n=>{let e=String(n||"").split(`
107
+ `).map(s=>s.trim()).filter(Boolean),t=e.find(s=>/^#{1,3}\s+\S/.test(s));return Qn((t||e[0]||"Plan").replace(/^#+\s*/,""),80)||"Plan"},Lm=/\b(?:created|new file)\b/i,ev=/\b(?:updated|overwr|modified|edited)\b/i;Hm=n=>{let e=ud(n);return!!(e&&(e.docs||e.plans||e.artifacts))};tv=n=>typeof n=="string"&&/^https:\/\/claude\.ai\/code\/artifact\/[A-Za-z0-9_-]{4,80}$/.test(n);nv={".md":"text/markdown",".mdx":"text/markdown",".markdown":"text/markdown",".txt":"text/plain",".csv":"text/csv",".html":"text/html",".htm":"text/html",".svg":"image/svg+xml",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".pdf":"application/pdf"},sv=n=>nv[On.extname(String(n||"")).toLowerCase()]||"application/octet-stream"});import si from"node:fs";import $m from"node:path";import ov from"node:crypto";function Jm(n,e=Date.now()){if(!n||!n.length)return[];let t=Math.min(...n.map(i=>Date.parse(i.at)||e)),s=Math.floor((e-t)/864e5),o=s>=1?", oldest "+s+(s===1?" day":" days"):"",r=["## To do","",n.length+(n.length===1?" to-do":" to-dos")+" open"+o+"."];for(let i of n.slice(0,10))r.push("- "+String(i.text).slice(0,140)+(i.sessionTitle?" ("+String(i.sessionTitle).slice(0,60)+")":""));return n.length>10&&r.push("- +"+(n.length-10)+" more"),r}var rv,zm,iv,Ym,Wm,dd,ol,rl,fd=J(()=>{rv=30,zm=200,iv=/^\.todos\..+\.json$/,Ym=/^[0-9a-f]{8,32}$/,Wm=n=>{try{let e=JSON.parse(si.readFileSync(n,"utf8"));return e&&typeof e=="object"?e:null}catch{return null}},dd=(n,e)=>n==null?null:String(n).slice(0,e),ol=(n,e)=>Date.parse(n||0)>Date.parse(e||0),rl=class{constructor({root:e,machine:t,now:s=Date.now,ttlMs:o=2e3,random:r=()=>ov.randomBytes(8).toString("hex")}={}){Object.assign(this,{root:e,machine:t,now:s,ttlMs:o,random:r}),this.cache=null}file(){return $m.join(this.root,".todos."+String(this.machine).replace(/[^\w.-]/g,"_")+".json")}own(){let e=Wm(this.file())||{};return{added:e.added||{},done:e.done||{},restored:e.restored||{},removed:e.removed||{}}}state(){let e=this.now();if(this.cache&&e-this.cache.at<this.ttlMs)return this.cache.v;let t=new Map,s=new Map,o=new Map,r=new Map,i=[];try{i=si.readdirSync(this.root).filter(l=>iv.test(l))}catch{}for(let l of i){let c=Wm($m.join(this.root,l));if(c){for(let[u,d]of Object.entries(c.added||{}))d&&typeof d.text=="string"&&!t.has(u)&&t.set(u,{key:u,text:d.text,sessionId:d.sessionId||null,at:d.at||null});for(let[u,d]of Object.entries(c.done||{}))d&&(!s.has(u)||ol(d.at,s.get(u).at))&&s.set(u,{key:u,...d});for(let[u,d]of Object.entries(c.restored||{}))(!o.has(u)||ol(d,o.get(u)))&&o.set(u,d);for(let[u,d]of Object.entries(c.removed||{}))(!r.has(u)||ol(d,r.get(u)))&&r.set(u,d)}}for(let[l,c]of o){let u=s.get(l);u&&!ol(u.at,c)&&s.delete(l)}for(let l of r.keys())t.delete(l),s.delete(l);let a={added:t,done:s};return this.cache={at:e,v:a},a}isDone(e){return!!e&&this.state().done.has(e)}doneList(){return[...this.state().done.values()].sort((e,t)=>Date.parse(t.at)-Date.parse(e.at))}addedList(){return[...this.state().added.values()]}write(e){let t=this.now()-rv*864e5;for(let[i,a]of Object.entries(e.done))(!a||Date.parse(a.at)<t)&&delete e.done[i];for(let[i,a]of Object.entries(e.restored))Date.parse(a)<t&&delete e.restored[i];for(let[i,a]of Object.entries(e.removed))Date.parse(a)<t&&delete e.removed[i];let s=Object.keys(e.added);if(s.length>zm)for(let i of s.slice(0,s.length-zm))delete e.added[i];si.mkdirSync(this.root,{recursive:!0});let o=this.file(),r=o+".tmp-"+process.pid;si.writeFileSync(r,JSON.stringify(e,null,2)+`
108
+ `),si.renameSync(r,o),this.cache=null}add(e,{sessionId:t=null}={}){let s=String(e??"").replace(/\s+/g," ").trim();if(!s)throw Object.assign(new Error("Type a to-do first."),{code:"empty"});let o=this.own(),r=String(this.random()).replace(/[^0-9a-f]/g,"").slice(0,16).padEnd(16,"0");return o.added[r]={text:s.slice(0,200),sessionId:dd(t,200),at:new Date(this.now()).toISOString()},this.write(o),{key:r,...o.added[r]}}done(e,t={}){if(typeof e!="string"||!Ym.test(e))throw Object.assign(new Error("bad key"),{code:"bad_key"});let s=this.own();return s.done[e]={at:new Date(this.now()).toISOString(),text:dd(t.text,200),sessionId:dd(t.sessionId,200)},delete s.restored[e],this.write(s),s.done[e]}restore(e){let t=this.own(),s=new Date(this.now()).toISOString(),o=0;for(let r of e)typeof r!="string"||!Ym.test(r)||(delete t.done[r],t.restored[r]=s,o++);return o&&this.write(t),o}}});function pd(n){return String(n||"").split(/[\\/]+/).filter(Boolean)}function av(n){let e=pd(n);return e.length?e[e.length-1]:""}function oi(n){if(!n)return"no-repo";if(n.repoKey)return n.repoKey;let e=n.repo||{};return n.repoRoot||e.root||n.cwd||e.name||n.repoName||"no-repo"}function zt(n){let e=[...new Set([...n].filter(Boolean))],t=new Map(e.map(o=>[o,1])),s=o=>{if(o==="no-repo")return"no repo";let r=pd(o);return r.length?r.slice(-Math.min(t.get(o),r.length)).join("/"):o};for(let o=0;o<12;o++){let r=new Map;for(let a of e){let l=s(a);r.set(l,[...r.get(l)||[],a])}let i=!1;for(let a of r.values())if(!(a.length<2))for(let l of a)t.get(l)<pd(l).length&&(t.set(l,t.get(l)+1),i=!0);if(!i)break}return new Map(e.map(o=>[o,s(o)]))}function qm(n,{countIn:e}={}){let t=new Map;for(let o of n){let r=oi(o),i=t.get(r)||{key:r,label:o.repoLabel||null,count:0,total:0,last:0};i.total++,i.last=Math.max(i.last,lv(o)),!i.label&&o.repoLabel&&(i.label=o.repoLabel),t.set(r,i)}if(e)for(let o of e){let r=t.get(oi(o));r&&r.count++}else for(let o of t.values())o.count=o.total;let s=zt(t.keys());for(let o of t.values())o.label||(o.label=s.get(o.key));return[...t.values()].sort((o,r)=>r.last-o.last||o.label.localeCompare(r.label))}function es(n,e,{loose:t=!1}={}){let s=String(e||"").trim().toLowerCase().replace(/\/+$/,"");if(!s)return!1;let o=oi(n),r=[o,n.repoLabel,n.repoName,av(o)].filter(Boolean).map(i=>String(i).toLowerCase());return r.includes(s)||s.includes("/")&&String(o).toLowerCase().endsWith("/"+s)||t&&r.some(i=>i.includes(s))?!0:Array.isArray(n.alsoRepos)&&n.alsoRepos.some(i=>i&&i.key&&es({repoKey:i.key,repoLabel:i.label},e,{loose:t}))}function il(n,e){if(!e)return n;let t=n.filter(s=>es(s,e));return t.length?t:n.filter(s=>es(s,e,{loose:!0}))}var hd,lv,$s=J(()=>{hd=n=>{let e=Date.parse(n||"");return isNaN(e)?0:e},lv=n=>Math.max(hd(n.lastActivityAt),hd(n.endedAt),hd(n.startedAt))});function Yt(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||"UTC"}catch{return"UTC"}}function Do(n){if(!n||typeof n!="string")return!1;try{return new Intl.DateTimeFormat("en-US",{timeZone:n}),!0}catch{return!1}}function uv(n){return md.has(n)||md.set(n,new Intl.DateTimeFormat("en-US",{timeZone:n,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",weekday:"short",hourCycle:"h23"})),md.get(n)}function Wt(n,e){let t={};for(let c of uv(e).formatToParts(new Date(n)))t[c.type]=c.value;let s=Number(t.year),o=Number(t.month),r=Number(t.day),i=Number(t.hour)%24,a=Number(t.minute),l=cn.indexOf(String(t.weekday).slice(0,3).toLowerCase());return{y:s,mo:o,d:r,hh:i,mm:a,ss:Number(t.second),dow:l,day:cn[l],date:s+"-"+String(o).padStart(2,"0")+"-"+String(r).padStart(2,"0")}}function Vm(n,e){let t=Wt(n,e);return Date.UTC(t.y,t.mo-1,t.d,t.hh,t.mm,t.ss)-Math.floor(n/1e3)*1e3}function Jt(n,e,t,s,o,r){let i=Date.UTC(n,e-1,t,s,o),a=Vm(i,r),l=i-a,c=Vm(l,r);return c!==a&&(l=i-c),l}function gd(n,e){try{let t=new Intl.DateTimeFormat("en-US",{timeZone:e,timeZoneName:"short"}).formatToParts(new Date(n)).find(s=>s.type==="timeZoneName");return t?t.value:""}catch{return""}}function Ro(n){if(!Array.isArray(n))return cn.slice();let e=cn.filter(t=>n.map(s=>String(s).toLowerCase().slice(0,3)).includes(t));return e.length?e:cn.slice()}function ll(n={},e=Yt()){let t=n||{},s=t.time===null?null:/^\d{1,2}:\d{2}$/.test(String(t.time||""))?String(t.time):"07:00",o=Do(t.timeZone)?t.timeZone:e;return{time:s,tz:o,days:Ro(t.days),skipEmpty:t.skipEmpty!==!1,zoneOverride:Do(t.timeZone)?t.timeZone:null}}function Po(n,e){let t=Wt(n,e.tz),s=e.days.includes(t.day),o=e.time?Jt(t.y,t.mo,t.d,Math.floor(al(e.time)/60),al(e.time)%60,e.tz):null;return{date:t.date,day:t.day,isDay:s,at:o,past:o!=null&&n>=o}}function Xm(n,e,{doneToday:t=!1}={}){if(!e.time)return null;let s=Wt(n,e.tz),o=Math.floor(al(e.time)/60),r=al(e.time)%60;for(let i=0;i<=8;i++){let a=new Date(Date.UTC(s.y,s.mo-1,s.d+i)),l=cn[a.getUTCDay()];if(!e.days.includes(l))continue;let c=Jt(a.getUTCFullYear(),a.getUTCMonth()+1,a.getUTCDate(),o,r,e.tz);if(i===0){if(t)continue;return c<=n?n:c}return c}return null}function Zm(n,e,t=Date.now()){if(!n)return"off";let[s,o]=n.split(":").map(Number),r=(s%12||12)+":"+String(o).padStart(2,"0")+" "+(s<12?"AM":"PM"),i=gd(t,e);return r+" · "+e+(i&&i!==e?" ("+i+")":"")}function Qm(n){let e=Ro(n);return e.length===7?"Every day":e.length===5&&zs.every(t=>e.includes(t))?"Weekdays":e.length===2&&e.includes("sat")&&e.includes("sun")?"Weekends":e.map(t=>cv[t]).join(", ")}function eg({machines:n=[],me:e,slotAt:t=null,graceMs:s=20*6e4}={}){let o=[...new Set([...n,e].filter(Boolean))].sort(),r=o.indexOf(e);return{order:o,rank:r,first:o[0]||e,graceMs:s,at:t==null?null:t+r*s}}var cn,cv,zs,md,al,ri=J(()=>{cn=["sun","mon","tue","wed","thu","fri","sat"],cv={sun:"Sun",mon:"Mon",tue:"Tue",wed:"Wed",thu:"Thu",fri:"Fri",sat:"Sat"},zs=["mon","tue","wed","thu","fri"];md=new Map;al=n=>{let[e,t]=String(n).split(":").map(Number);return(e||0)*60+(t||0)}});var wg={};tn(wg,{CATCHUP_LENGTHS:()=>kd,CATCHUP_NAME_RE:()=>dv,CATCHUP_SCHEMA:()=>sg,CATCHUP_SYSTEM:()=>dg,availableFrom:()=>Td,buildRecap:()=>cg,catchupFileName:()=>gg,catchupLength:()=>bd,catchupOptions:()=>ml,catchupZone:()=>hl,collectSessions:()=>Ad,createCatchup:()=>li,heuristicSummary:()=>ii,lastWorkday:()=>xd,modelLabel:()=>mg,parseBullets:()=>hg,parseCatchupMd:()=>_d,parseDuration:()=>og,parseWhen:()=>Sd,pickRepos:()=>ag,recapSession:()=>lg,renderCatchupMd:()=>yg,resolveWindow:()=>pl,stillOpenLists:()=>ug,summarize:()=>pg,summaryPrompt:()=>fg,windowChoices:()=>rg,workdayLabel:()=>hv,workdaysOf:()=>vd});function bd(n=ke,e){let t=Tu(n||ke,e);return{key:t,...kd[t]}}function hl(n=ke){let e=n&&n.digest&&n.digest.timeZone;return Do(e)?e:Yt()}function vd(n=ke){let e=n&&n.catchup&&n.catchup.workdays,t=Array.isArray(e)?Ro(e):null;return t&&t.length?t:zs}function yd(n){let e=new Error(n);return e.badWindow=!0,e}function dl(n,e,t){let s=Wt(n,t),o=new Date(Date.UTC(s.y,s.mo-1,s.d-e));return Jt(o.getUTCFullYear(),o.getUTCMonth()+1,o.getUTCDate(),0,0,t)}function xd(n,e,t=zs){for(let s=1;s<=7;s++){let o=dl(n,s,e),r=Wt(o+2*ai,e);if(t.includes(r.day))return{from:o,day:r.day,daysAgo:s}}return{from:dl(n,1,e),day:Wt(n-ul,e).day,daysAgo:1}}function hv(n,e,t=zs){return"Since "+cl[xd(n,e,t).day]}function pv(n){let e=n/ai;if(e<1||e<3&&!Number.isInteger(e))return"last "+Math.round(n/6e4)+" minutes";if(e===1)return"last hour";if(e<48&&Number.isInteger(e))return"last "+e+" hours";let t=e/24;return t===7?"last 7 days":Number.isInteger(t)?"last "+t+" days":"last "+Math.round(e)+" hours"}function og(n){let e=String(n??"").trim().toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(m|min|mins|minutes?|h|hr|hrs|hours?|d|days?|w|wk|weeks?)$/);if(!e)return null;let t=Number(e[1]),s=e[2][0],o=t*(s==="m"?6e4:s==="h"?ai:s==="d"?ul:7*ul);return o>0?o:null}function Sd(n,e){let t=String(n??"").trim(),s=t.match(/^(\d{4})-(\d{2})-(\d{2})$/);if(s)return Jt(+s[1],+s[2],+s[3],0,0,e);if(s=t.match(/^(\d{4})-(\d{2})-(\d{2})[T ](\d{1,2}):(\d{2})(?::\d{2}(?:\.\d+)?)?$/),s)return Jt(+s[1],+s[2],+s[3],+s[4],+s[5],e);if(/^\d{4}-\d{2}-\d{2}T.*(Z|[+-]\d{2}:?\d{2})$/i.test(t)){let o=Date.parse(t);return isNaN(o)?null:o}return null}function pl(n,{now:e=Date.now(),tz:t=Yt(),lastAt:s=null,to:o=null,workdays:r=zs}={}){let i=o!=null&&o!==""?typeof o=="number"?o:Sd(o,t):e;if(i==null||isNaN(i))throw yd(`can't read the end of the window "`+o+'" (use a date like 2026-10-09 or 2026-10-09 18:00)');let a=n==null||n===!0?"":String(n).trim(),l=a.toLowerCase().replace(/[\s_]+/g,"-"),c=(h,p,m,g={})=>{if(!(h<i))throw yd("the window starts after it ends ("+new Date(h).toISOString()+" → "+new Date(i).toISOString()+")");return{from:h,to:i,spec:a||"last",kind:p,label:m,...g}};if(!l||l==="last"||l==="since-last"||l==="last-catchup"||l==="since-last-catchup")return s!=null&&!isNaN(s)&&s<i?c(s,"last","Since your last catch-up"):c(i-ul,"last","Last 24 hours",{fallback:s!=null&&s>=i?"last catch-up is at or after the end of the window":"no earlier catch-up"});if(["workday","last-workday","lastworkday","since-workday"].includes(l)){let h=xd(i,t,r);return c(h.from,"workday","Since "+cl[h.day])}if(l==="today")return c(fv(i,t),"day","Today");if(l==="yesterday")return c(dl(i,1,t),"day","Since yesterday");let u=l.replace(/^since-/,"").slice(0,3);if(/^(since-)?(sun|mon|tue|wed|thu|fri|sat)[a-z]*$/.test(l)&&cn.includes(u)&&cl[u].toLowerCase().startsWith(l.replace(/^since-/,"")))for(let h=1;h<=7;h++){let p=dl(i,h,t);if(Wt(p+2*ai,t).day===u)return c(p,"day","Since "+cl[u])}let d=og(l);if(d)return c(i-d,"duration",pv(d).replace(/^l/,"L"));let f=Sd(a,t);if(f!=null)return c(f,"custom","Since "+fl(f,t));throw yd(`can't read the window "`+a+'". Try: 24h, 3d, 1w, workday, yesterday, friday, or a date like 2026-10-09')}function rg({now:n=Date.now(),tz:e=Yt(),lastAt:t=null,workdays:s=zs}={}){let o=c=>{try{return pl(c,{now:n,tz:e,lastAt:t,workdays:s})}catch{return null}},r=o("last"),i=o("24h"),a=o("workday"),l=o("7d");return[{key:"last",label:t!=null?"Since I last caught up":"Since I last caught up (none yet: 24h)",from:r&&G(r.from)},{key:"24h",label:"Last 24 hours",from:i&&G(i.from)},{key:"workday",label:a?a.label:"Since the last workday",from:a&&G(a.from)},{key:"7d",label:"Last 7 days",from:l&&G(l.from)},{key:"custom",label:"Custom…",from:null}]}function Ad(n,e={}){let{from:t,to:s,now:o=Date.now(),cfg:r=ke,machine:i=null}=e,a=e.repoKeyFor||(c=>oi(c)),l=[];for(let{session:c,checkpoints:u=[]}of n){if(!c||!c.id||c.archived||c.ignored||!c.agent&&(pt(c,u.length)||po(c,u.length)))continue;let d=u.filter(x=>x&&x.createdAt).slice().sort((x,v)=>R(x.createdAt)-R(v.createdAt)),f=d.filter(x=>tg(R(x.createdAt),t,s)),h=i&&c.machine&&c.machine!==i,p=c.agent?c.agentStatus:xt(c,o,h?{...r,staleAfterSec:Math.max(r.staleAfterSec||180,900)}:r),m=[c.lastActivityAt,c.startedAt,c.endedAt].some(x=>tg(R(x),t,s)),g=c.agent?mv(c):(e.needsOf?e.needsOf(c,p):gv(c,p))||null,S=d[d.length-1]||null,k=S&&/^YOU:/i.test(S.next||"")&&p!=="ended"&&p!=="stale"&&!c.endedAt,w=s>=o-6e4;!f.length&&!m&&!(w&&(g||k))||l.push({s:c,cps:d,inWin:f,latest:S,status:p,needs:g,youNext:k,repoKey:a(c)})}return l}function mv(n){let e=n.agentNeeds;return!e||!e.items||!e.items.length?null:{kind:"agent",message:e.items.map(t=>t.text.replace(/\s*\n\s*/g," ")).join("; ")+(n.agent.reply?" ("+n.agent.reply.label+")":n.agent.replyHint?" ("+n.agent.replyHint+")":"")}}function gv(n,e){return e==="ended"||e==="stale"||n.endedAt?null:n.needsInput?{kind:"terminal",message:n.lastNotificationMessage||(n.lastNotification?String(n.lastNotification).replace(/_/g," "):"waiting in the terminal")}:n.question&&!n.inTurn?{kind:"reply",message:typeof n.question=="string"?n.question:n.question.text||"asked you a question"}:null}function Td(n,{labels:e}={}){let t=new Map;for(let i of n){let a=t.get(i.repoKey)||{key:i.repoKey,count:0,needsYou:0,last:0};a.count++,(i.needs||i.youNext)&&a.needsYou++,a.last=Math.max(a.last,ig(i)),t.set(i.repoKey,a)}let s=e||zt(t.keys()),o=[...t.values()].map(i=>({...i,label:s.get(i.key)||i.key,last:i.last?G(i.last):null})).sort((i,a)=>(a.needsYou>0)-(i.needsYou>0)||(R(a.last)||0)-(R(i.last)||0)||i.label.localeCompare(a.label)),r=[...new Set(n.map(i=>i.s.machine).filter(Boolean))].sort();return{repos:o,machines:r}}function ag(n,e=[],t){let s=(e||[]).flatMap(a=>String(a).split(",")).map(a=>a.trim()).filter(Boolean);if(!s.length)return{keys:null,unmatched:[]};let o=new Set,r=[],i=t||zt(n.map(a=>a.repoKey));for(let a of s){let l=u=>n.filter(d=>es({...d.s,repoKey:d.repoKey,repoLabel:i.get(d.repoKey),repoName:ge(d.s)},a,{loose:u})),c=l(!1);c.length||(c=l(!0)),c.length||r.push(a);for(let u of c)o.add(u.repoKey)}return{keys:o,unmatched:r}}function wv(n){return n.lastPreview?"no checkpoint yet; last reply: "+F(n.lastPreview,220):n.firstPrompt?"no checkpoint yet; started with “"+F(n.firstPrompt,200)+"”":"no checkpoint yet"+(n.turns?" ("+n.turns+" turn"+(n.turns===1?"":"s")+")":"")}function Sv(n,e){let t=n.s;return n.needs?"needs you":n.status==="ended"?"closed"+(t.endedAt?" "+fl(R(t.endedAt),e):""):n.status==="stale"?"gone quiet":n.status==="dormant"?"idle":n.status==="needs_input"?"needs you":n.status}function lg(n,{length:e,tz:t,patterns:s,labels:o}={}){let r=e||kd.informative,i=m=>m==null||m===""?m:ma(String(m),s),a=n.s,l=n.latest,c=wd([...n.inWin.map(m=>m.doing),...r.done>4&&l&&l.recap&&n.inWin.includes(l)?[l.recap]:[]]).slice(-r.done),u=l&&(R(a.lastActivityAt)||0)>R(l.createdAt)+5*6e4,d=Fo(l&&l.next).toLowerCase().replace(/[.!?…]+$/,""),f=wd([l&&l.openQuestion,...l&&l.questions||[]]).filter(m=>Fo(m).toLowerCase().replace(/[.!?…]+$/,"")!==d).slice(0,r.questions),h=wd((l&&l.nextSteps||[]).map(Fo)).filter(m=>m.toLowerCase()!==Fo(l&&l.next).toLowerCase()).slice(0,Math.max(0,r.steps-1)),p=a.repo&&a.repo.branch;return{id:a.id,title:i(a.title||"Untitled session"),repoKey:n.repoKey,repo:o&&o.get(n.repoKey)||ge(a),branch:p||null,machine:a.machine||null,cwd:a.cwd||null,status:n.status,state:Sv(n,t),lastActivityAt:G(ig(n))||null,checkpoints:n.inWin.length,done:c.map(m=>i(F(m,240))),stoppedAt:i(l?F(l.stoppedAt,280):wv(a)),next:l?i(F(l.next,240)):null,nextSteps:h.map(m=>i(F(m,200))),questions:f.map(m=>i(F(m,240))),needsYou:n.needs?i(F(n.needs.message||n.needs.question||"waiting on you",200)):n.youNext?i(F(Fo(l.next),200)):null,needsKind:n.needs?n.needs.kind||"terminal":n.youNext?"next":null,behind:!!u,lastReply:u&&a.lastPreview&&!yv(a.lastPreview,l&&l.stoppedAt)?i(F(a.lastPreview,200)):null,resume:a.agent?null:ho(a),agent:a.agent?{name:a.agent.name,display:a.agent.display,url:a.agent.url||null,reply:a.agent.reply||null}:null}}function cg(n,e={}){let{now:t=Date.now(),cfg:s=ke,machine:o=null}=e,r=e.tz||hl(s),i=e.window,a=bd(s,e.lengthOverride),l=s.redact&&s.redact.patterns||[],c=Ad(n,{...e,from:i.from,to:i.to,now:t,cfg:s,machine:o}),u=(e.machines||[]).flatMap(w=>String(w).split(",")).map(w=>w.trim()).filter(Boolean);u.length&&(c=c.filter(w=>u.includes(w.s.machine||o)));let d=zt(c.map(w=>w.repoKey)),f=Td(c,{labels:d}),{keys:h,unmatched:p}=ag(c,e.repos,d);h&&(c=c.filter(w=>h.has(w.repoKey)));let m=c.map(w=>lg(w,{length:a,tz:r,patterns:l,labels:d})).sort((w,x)=>!!x.needsYou-!!w.needsYou||(R(x.lastActivityAt)||0)-(R(w.lastActivityAt)||0)),g=new Map;for(let w of m)g.has(w.repoKey)||g.set(w.repoKey,{key:w.repoKey,label:w.repo,sessions:[]}),g.get(w.repoKey).sessions.push(w);let S=[...g.values()],k=[...new Set(m.map(w=>w.machine).filter(Boolean))].sort();return{window:{from:G(i.from),to:G(i.to),spec:i.spec,kind:i.kind,label:i.label,fallback:i.fallback||null,tz:r},length:a.key,repos:S,sessions:m,needsYou:m.filter(w=>w.needsYou),stillOpen:ug(e.lists,{keys:h,labels:S.map(w=>w.label),from:R(i.from),to:R(i.to)}),machines:k,selection:{repos:h?S.map(w=>w.label):"all",repoKeys:h?[...h]:null,machines:u.length?u:"all",unmatched:p},available:f,madeOn:o}}function ug(n,{keys:e=null,labels:t=[],from:s=0,to:o=1/0}={}){let r=[];for(let i of n||[]){if(!i||i.archived||!(i.total>i.done)||e&&!(e.has(i.repoKey)||t.includes(i.repo)))continue;let a=(i.items||[]).filter(c=>!c.done),l=(i.items||[]).filter(c=>c.done&&c.at&&R(c.at)>=s&&R(c.at)<=o);r.push({ref:i.ref,name:i.name,repo:i.repo||"no repo",done:i.done,total:i.total,next:a.slice(0,3).map(c=>({n:c.n,text:c.text})),ticked:l.map(c=>({n:c.n,text:c.text})),sourceSession:i.sourceSession||null,updated:i.updated})}return r.sort((i,a)=>(R(a.updated)||0)-(R(i.updated)||0)).slice(0,20)}function fg(n,{maxChars:e=24e3}={}){let t=["Window: "+n.window.label+" ("+n.window.from+" to "+n.window.to+")",""];for(let o of n.repos){t.push("Repo "+o.label+":");for(let r of o.sessions)t.push('- "'+r.title+'" ['+r.state+"]"+(r.needsYou?" NEEDS YOU: "+r.needsYou+".":"")+(r.done.length?" Done: "+r.done.map(Cn).join("; ")+".":"")+(r.stoppedAt?" Stopped at: "+Cn(r.stoppedAt)+".":"")+(r.next?" Next: "+Cn(r.next)+".":"")+(r.questions.length?" Open: "+r.questions.map(Cn).join("; ")+".":""))}for(let o of n.stillOpen||[])t.push('Open list "'+o.name+'" ('+o.repo+"): "+o.done+"/"+o.total+" done"+(o.ticked.length?", "+o.ticked.length+" ticked off in this window":"")+"; next: "+o.next.map(r=>Cn(r.text)).join("; ")+".");let s=t.join(`
109
109
  `);return s.length>e&&(s=s.slice(0,e)+`
110
- …`),s}function oi(n,e=5){let t=n.sessions;if(!t.length)return["Nothing happened "+n.window.label.toLowerCase().replace(/^last/,"in the last")+": no Claude Code sessions with activity."];let s=[],o=n.repos.slice().sort((d,f)=>f.sessions.length-d.sessions.length).slice(0,4).map(d=>d.label+" ("+d.sessions.length+")").join(", ")+(n.repos.length>4?", +"+(n.repos.length-4)+" more":"");s.push(t.length+" session"+(t.length===1?"":"s")+" in "+n.repos.length+" repo"+(n.repos.length===1?"":"s")+": "+o+".");let r=n.needsYou;r.length===1?s.push("Waiting on you: "+Ro(r[0].title)+" ("+r[0].repo+"), "+En(r[0].needsYou)+"."):r.length>1&&s.push(r.length+" sessions are waiting on you: "+r.slice(0,3).map(d=>Ro(d.title)).join(", ")+(r.length>3?" and more":"")+".");let i=t.filter(d=>d.done.length&&!d.needsYou);for(let d of i.slice(0,2))s.push("In "+d.repo+", "+Ro(d.title)+": "+En(d.done[d.done.length-1])+(d.status==="ended"?" (closed).":"."));let a=t.find(d=>!d.needsYou&&d.status!=="ended"&&d.next)||t.find(d=>!d.needsYou&&d.next);a&&s.push("Pick up first: "+Ro(a.title)+", next: "+En(Po(a.next))+".");let l=t.filter(d=>["active","background"].includes(d.status));l.length&&s.push("Still running: "+l.slice(0,3).map(d=>Ro(d.title)).join(", ")+(l.length>3?" +"+(l.length-3):"")+".");let c=n.stillOpen||[];c.length&&s.length<e&&s.push("Still open: "+c.slice(0,2).map(d=>Ro(d.name)+" ("+d.done+"/"+d.total+", next: "+En(d.next[0]?d.next[0].text:"")+")").join("; ")+(c.length>2?" and "+(c.length-2)+" more list"+(c.length>3?"s":""):"")+".");let u=t.filter(d=>d.status==="ended").length;return u&&s.length<e&&s.push(u+" session"+(u===1?"":"s")+" closed in this stretch."),s.slice(0,Math.max(1,e))}async function cg(n,{summarizer:e=null,cfg:t=ke,given:s=null}={}){let o=Sd(t,n.length),r=t.redact&&t.redact.patterns||[],i=a=>a.map(l=>fa(F(l,400),r));if(!n.sessions.length)return{bullets:oi(n,o.bullets),by:{by:"heuristic",reason:"empty"}};if(s&&Array.isArray(s.bullets)&&s.bullets.length)return{bullets:i(s.bullets.map(String).filter(Boolean).slice(0,6)),by:{by:"model",provider:s.provider||"claude-in-session",model:s.model||null}};if(e&&e.mode&&e.mode!=="heuristic")try{let a=t.summarizer&&t.summarizer.maxInputChars||24e3,l=await e.complete({system:ig.replace("{n}",String(o.bullets)),prompt:ag(n,{maxChars:a}),maxTokens:o.maxTokens}),c=lg(l&&l.text,Math.max(o.bullets,3));return c?{bullets:i(c),by:{by:"model",provider:e.mode,model:e.model||null}}:{bullets:oi(n,o.bullets),by:{by:"heuristic",fallbackFrom:e.mode,error:"the model reply had no bullets"}}}catch(a){return{bullets:oi(n,o.bullets),by:{by:"heuristic",fallbackFrom:e.mode,error:String(a&&a.message||a).slice(0,160)}}}return{bullets:oi(n,o.bullets),by:{by:"heuristic"}}}function ug(n){return!n||n.by!=="model"?"heuristic":(n.provider||"model")+(n.model?"/"+n.model:"")}function dg(n,e,t=0){let s=Yt(n,e);return s.date+"T"+String(s.hh).padStart(2,"0")+String(s.mm).padStart(2,"0")+(t?"-"+(t+1):"")+".md"}function fg(n,e,{now:t=Date.now()}={}){let s=n.window.tz||$t(),o=R(n.window.from),r=R(n.window.to),i={kind:"catchup",generated_at:G(t),window_from:n.window.from,window_to:n.window.to,window_label:n.window.label,window_spec:n.window.spec,time_zone:s,repos:n.selection.repos==="all"?"all":n.selection.repos,repo_keys:n.repos.map(c=>c.key),machines:n.machines,machine_filter:n.selection.machines,made_on:n.madeOn||null,model:ug(e.by),summary_by:e.by,length:n.length,sessions:n.sessions.length,session_ids:n.sessions.map(c=>c.id),needs_you:n.needsYou.map(c=>c.id),schema:Zm},a=n.machines.length>1,l=[];l.push("# Catch me up: "+n.window.label.replace(/^\w/,c=>c.toLowerCase()).replace(/^since/,"since"),""),l.push("_"+ul(o,s)+" → "+ul(r,s)+" · "+n.sessions.length+" session"+(n.sessions.length===1?"":"s")+" in "+n.repos.length+" repo"+(n.repos.length===1?"":"s")+(n.machines.length?" · "+n.machines.join(", "):"")+"_",""),l.push("## Summary","");for(let c of e.bullets)l.push("- "+je(c));if(l.push(""),n.needsYou.length){l.push("## Needs you ("+n.needsYou.length+")","");for(let c of n.needsYou)l.push("- **"+je(c.title)+"** ("+c.repo+(a&&c.machine?" · "+c.machine:"")+") — "+je(c.needsYou)+" <!-- session: "+c.id+" -->");l.push("")}if((n.stillOpen||[]).length){l.push("## Still open ("+n.stillOpen.length+")","");for(let c of n.stillOpen)l.push("- **"+je(c.name)+"** ("+je(c.repo)+" · "+c.done+"/"+c.total+" done) — next: "+c.next.map(u=>"#"+u.n+" "+je(u.text)).join("; ")+" <!-- list: "+c.ref+" -->"),c.ticked.length&&l.push(" - Ticked off in this window: "+c.ticked.map(u=>"#"+u.n+" "+je(u.text)).join("; "));l.push("")}for(let c of n.repos){l.push("## "+je(c.label)+" ("+c.sessions.length+")","");for(let u of c.sessions){if(l.push("### "+je(u.title),"<!-- session: "+u.id+" -->"),l.push("`"+[u.repo+(u.branch?" · "+u.branch:""),a||u.machine?u.machine:null,u.state].filter(Boolean).join(" · ")+"`",""),u.needsYou&&u.needsKind!=="next"&&l.push("**Needs you:** "+je(u.needsYou)),u.done.length===1)l.push("**Done:** "+je(u.done[0]));else if(u.done.length){l.push("**Done:**");for(let d of u.done)l.push("- "+je(d))}u.stoppedAt&&l.push((u.agent?"**Latest:** ":"**Stopped at:** ")+je(u.stoppedAt)),u.lastReply&&l.push("**Since then:** "+je(u.lastReply)),u.next&&l.push("**Next:** "+je(u.next));for(let d of u.nextSteps)l.push("- "+je(d));if(u.questions.length){l.push("**Open questions:**");for(let d of u.questions)l.push("- "+je(d))}u.agent?l.push("","From "+je(u.agent.display)+"’s feed (read-only)"+(u.agent.reply&&/^[^<>\s]+$/.test(u.agent.reply.url)?" · "+je(u.agent.reply.label).replace(/[<>→]/g,"")+" → <"+u.agent.reply.url+">":""),""):l.push("","Resume: `"+u.resume+"`","")}}return n.sessions.length||l.push("_No Claude Code sessions with activity in this window._",""),As(i,mv)+l.join(`
110
+ …`),s}function ii(n,e=5){let t=n.sessions;if(!t.length)return["Nothing happened "+n.window.label.toLowerCase().replace(/^last/,"in the last")+": no Claude Code sessions with activity."];let s=[],o=n.repos.slice().sort((d,f)=>f.sessions.length-d.sessions.length).slice(0,4).map(d=>d.label+" ("+d.sessions.length+")").join(", ")+(n.repos.length>4?", +"+(n.repos.length-4)+" more":"");s.push(t.length+" session"+(t.length===1?"":"s")+" in "+n.repos.length+" repo"+(n.repos.length===1?"":"s")+": "+o+".");let r=n.needsYou;r.length===1?s.push("Waiting on you: "+jo(r[0].title)+" ("+r[0].repo+"), "+Cn(r[0].needsYou)+"."):r.length>1&&s.push(r.length+" sessions are waiting on you: "+r.slice(0,3).map(d=>jo(d.title)).join(", ")+(r.length>3?" and more":"")+".");let i=t.filter(d=>d.done.length&&!d.needsYou);for(let d of i.slice(0,2))s.push("In "+d.repo+", "+jo(d.title)+": "+Cn(d.done[d.done.length-1])+(d.status==="ended"?" (closed).":"."));let a=t.find(d=>!d.needsYou&&d.status!=="ended"&&d.next)||t.find(d=>!d.needsYou&&d.next);a&&s.push("Pick up first: "+jo(a.title)+", next: "+Cn(Fo(a.next))+".");let l=t.filter(d=>["active","background"].includes(d.status));l.length&&s.push("Still running: "+l.slice(0,3).map(d=>jo(d.title)).join(", ")+(l.length>3?" +"+(l.length-3):"")+".");let c=n.stillOpen||[];c.length&&s.length<e&&s.push("Still open: "+c.slice(0,2).map(d=>jo(d.name)+" ("+d.done+"/"+d.total+", next: "+Cn(d.next[0]?d.next[0].text:"")+")").join("; ")+(c.length>2?" and "+(c.length-2)+" more list"+(c.length>3?"s":""):"")+".");let u=t.filter(d=>d.status==="ended").length;return u&&s.length<e&&s.push(u+" session"+(u===1?"":"s")+" closed in this stretch."),s.slice(0,Math.max(1,e))}async function pg(n,{summarizer:e=null,cfg:t=ke,given:s=null}={}){let o=bd(t,n.length),r=t.redact&&t.redact.patterns||[],i=a=>a.map(l=>ma(F(l,400),r));if(!n.sessions.length)return{bullets:ii(n,o.bullets),by:{by:"heuristic",reason:"empty"}};if(s&&Array.isArray(s.bullets)&&s.bullets.length)return{bullets:i(s.bullets.map(String).filter(Boolean).slice(0,6)),by:{by:"model",provider:s.provider||"claude-in-session",model:s.model||null}};if(e&&e.mode&&e.mode!=="heuristic")try{let a=t.summarizer&&t.summarizer.maxInputChars||24e3,l=await e.complete({system:dg.replace("{n}",String(o.bullets)),prompt:fg(n,{maxChars:a}),maxTokens:o.maxTokens}),c=hg(l&&l.text,Math.max(o.bullets,3));return c?{bullets:i(c),by:{by:"model",provider:e.mode,model:e.model||null}}:{bullets:ii(n,o.bullets),by:{by:"heuristic",fallbackFrom:e.mode,error:"the model reply had no bullets"}}}catch(a){return{bullets:ii(n,o.bullets),by:{by:"heuristic",fallbackFrom:e.mode,error:String(a&&a.message||a).slice(0,160)}}}return{bullets:ii(n,o.bullets),by:{by:"heuristic"}}}function mg(n){return!n||n.by!=="model"?"heuristic":(n.provider||"model")+(n.model?"/"+n.model:"")}function gg(n,e,t=0){let s=Wt(n,e);return s.date+"T"+String(s.hh).padStart(2,"0")+String(s.mm).padStart(2,"0")+(t?"-"+(t+1):"")+".md"}function yg(n,e,{now:t=Date.now()}={}){let s=n.window.tz||Yt(),o=R(n.window.from),r=R(n.window.to),i={kind:"catchup",generated_at:G(t),window_from:n.window.from,window_to:n.window.to,window_label:n.window.label,window_spec:n.window.spec,time_zone:s,repos:n.selection.repos==="all"?"all":n.selection.repos,repo_keys:n.repos.map(c=>c.key),machines:n.machines,machine_filter:n.selection.machines,made_on:n.madeOn||null,model:mg(e.by),summary_by:e.by,length:n.length,sessions:n.sessions.length,session_ids:n.sessions.map(c=>c.id),needs_you:n.needsYou.map(c=>c.id),schema:sg},a=n.machines.length>1,l=[];l.push("# Catch me up: "+n.window.label.replace(/^\w/,c=>c.toLowerCase()).replace(/^since/,"since"),""),l.push("_"+fl(o,s)+" → "+fl(r,s)+" · "+n.sessions.length+" session"+(n.sessions.length===1?"":"s")+" in "+n.repos.length+" repo"+(n.repos.length===1?"":"s")+(n.machines.length?" · "+n.machines.join(", "):"")+"_",""),l.push("## Summary","");for(let c of e.bullets)l.push("- "+Fe(c));if(l.push(""),n.needsYou.length){l.push("## Needs you ("+n.needsYou.length+")","");for(let c of n.needsYou)l.push("- **"+Fe(c.title)+"** ("+c.repo+(a&&c.machine?" · "+c.machine:"")+") — "+Fe(c.needsYou)+" <!-- session: "+c.id+" -->");l.push("")}if((n.stillOpen||[]).length){l.push("## Still open ("+n.stillOpen.length+")","");for(let c of n.stillOpen)l.push("- **"+Fe(c.name)+"** ("+Fe(c.repo)+" · "+c.done+"/"+c.total+" done) — next: "+c.next.map(u=>"#"+u.n+" "+Fe(u.text)).join("; ")+" <!-- list: "+c.ref+" -->"),c.ticked.length&&l.push(" - Ticked off in this window: "+c.ticked.map(u=>"#"+u.n+" "+Fe(u.text)).join("; "));l.push("")}for(let c of n.repos){l.push("## "+Fe(c.label)+" ("+c.sessions.length+")","");for(let u of c.sessions){if(l.push("### "+Fe(u.title),"<!-- session: "+u.id+" -->"),l.push("`"+[u.repo+(u.branch?" · "+u.branch:""),a||u.machine?u.machine:null,u.state].filter(Boolean).join(" · ")+"`",""),u.needsYou&&u.needsKind!=="next"&&l.push("**Needs you:** "+Fe(u.needsYou)),u.done.length===1)l.push("**Done:** "+Fe(u.done[0]));else if(u.done.length){l.push("**Done:**");for(let d of u.done)l.push("- "+Fe(d))}u.stoppedAt&&l.push((u.agent?"**Latest:** ":"**Stopped at:** ")+Fe(u.stoppedAt)),u.lastReply&&l.push("**Since then:** "+Fe(u.lastReply)),u.next&&l.push("**Next:** "+Fe(u.next));for(let d of u.nextSteps)l.push("- "+Fe(d));if(u.questions.length){l.push("**Open questions:**");for(let d of u.questions)l.push("- "+Fe(d))}u.agent?l.push("","From "+Fe(u.agent.display)+"’s feed (read-only)"+(u.agent.reply&&/^[^<>\s]+$/.test(u.agent.reply.url)?" · "+Fe(u.agent.reply.label).replace(/[<>→]/g,"")+" → <"+u.agent.reply.url+">":""),""):l.push("","Resume: `"+u.resume+"`","")}}return n.sessions.length||l.push("_No Claude Code sessions with activity in this window._",""),Es(i,kv)+l.join(`
111
111
  `).replace(/\n+$/,"")+`
112
- `}function Ad(n){let{data:e,body:t}=Ye(n),s={generatedAt:e.generated_at?G(e.generated_at):null,window:{from:e.window_from?G(e.window_from):null,to:e.window_to?G(e.window_to):null,label:e.window_label||null,spec:e.window_spec||null,tz:e.time_zone||null},repos:e.repos==="all"||e.repos==null?"all":[].concat(e.repos).map(String),repoKeys:Array.isArray(e.repo_keys)?e.repo_keys.map(String):[],machines:Array.isArray(e.machines)?e.machines.map(String):[],machineFilter:e.machine_filter==="all"||e.machine_filter==null?"all":[].concat(e.machine_filter).map(String),madeOn:e.made_on==null?null:String(e.made_on),model:e.model==null?null:String(e.model),summaryBy:e.summary_by&&typeof e.summary_by=="object"?e.summary_by:null,length:e.length||null,sessions:typeof e.sessions=="number"?e.sessions:null,sessionIds:Array.isArray(e.session_ids)?e.session_ids.map(String):[],needsYou:Array.isArray(e.needs_you)?e.needs_you.map(String):[]},o=t.split(`
113
- `),r=[],i=[],a=[],l=[],c=null,u=null,d=null,f=null,h=p=>{let m=p.match(/<!--\s*session:\s*([^\s>]+)\s*-->/);return m?m[1]:null};for(let p of o){let m=p.replace(/\s+$/,""),g;if(g=m.match(/^#\s+(.*)$/)){s.title=g[1];continue}if(g=m.match(/^##\s+(.*?)(?:\s+\((\d+)\))?$/)){d=null,f=null,/^summary$/i.test(g[1])?c="summary":/^needs you$/i.test(g[1])?c="needs":/^still open$/i.test(g[1])?c="open":(c="repo",u={label:g[1],sessions:[]},a.push(u));continue}if(c==="summary"){(g=m.match(/^-\s+(.*)$/))&&r.push(g[1]);continue}if(c==="needs"){(g=m.match(/^-\s+\*\*(.+?)\*\*\s+\((.*?)\)\s+—\s+(.*?)(?:\s*<!--.*-->)?$/))&&i.push({id:h(m),title:g[1],repo:g[2].split(" · ")[0],needsYou:g[3]});continue}if(c==="open"){if(g=m.match(/^-\s+\*\*(.+?)\*\*\s+\((.*?) · (\d+)\/(\d+) done\)\s+—\s+next:\s*(.*?)(?:\s*<!--\s*list:\s*(\S+)\s*-->)?$/)){let S=k=>k.split(/;\s+(?=#\d+ )/).map(w=>{let x=w.match(/^#(\d+)\s+(.*)$/);return x?{n:Number(x[1]),text:x[2]}:null}).filter(Boolean);l.push({name:g[1],repo:g[2],done:Number(g[3]),total:Number(g[4]),next:S(g[5]),ref:g[6]||null,ticked:[]})}else(g=m.match(/^\s+-\s+Ticked off in this window:\s*(.*)$/))&&l.length&&(l[l.length-1].ticked=g[1].split(/;\s+(?=#\d+ )/).map(S=>{let k=S.match(/^#(\d+)\s+(.*)$/);return k?{n:Number(k[1]),text:k[2]}:null}).filter(Boolean));continue}if(c==="repo"){if(g=m.match(/^###\s+(.*)$/)){d={id:null,title:g[1],repo:u.label,done:[],nextSteps:[],questions:[],stoppedAt:null,next:null,needsYou:null,lastReply:null,resume:null,line:null},u.sessions.push(d),f=null;continue}if(d){if(h(m)&&!d.id){d.id=h(m);continue}if(!d.line&&(g=m.match(/^`([^`]+)`$/))){d.line=g[1];continue}if(g=m.match(/^Resume:\s+`(.*)`$/)){d.resume=g[1],f=null;continue}if(g=m.match(/^From (.+?)’s feed \(read-only\) · ([^<>→]{1,60}?) → <([^>\s]+)>$/)){d.agent={display:g[1],url:null,reply:{label:g[2],url:g[3]}},f=null;continue}if(g=m.match(/^From (.+?)’s feed \(read-only\)(?:\. Reply: <(https:\/\/[^>\s]+)>)?$/)){d.agent={display:g[1],url:g[2]||null},f=null;continue}if(g=m.match(/^\*\*([A-Za-z ]+):\*\*\s*(.*)$/)){let S=g[1].toLowerCase(),k=g[2].trim();f=null,S==="done"?k?d.done.push(k):f=d.done:S==="stopped at"?d.stoppedAt=k:S==="latest"?(d.stoppedAt=k,d.agent=d.agent||{display:null,url:null}):S==="next"?(d.next=k,f=d.nextSteps):S==="open questions"?f=d.questions:S==="needs you"?d.needsYou=k:S==="since then"&&(d.lastReply=k);continue}if((g=m.match(/^-\s+(.*)$/))&&f){f.push(g[1]);continue}m.trim()}}}return{meta:s,summary:r,needsYou:i,stillOpen:l,repos:a}}async function ii(n,e={}){let t=e.now!=null?e.now:Date.now(),s=e.cfg||ke,o=dl(s),r=fl(e.since,{now:t,tz:o,lastAt:e.lastAt==null?null:e.lastAt,to:e.to||null,workdays:kd(s)}),i=og(n,{...e,now:t,cfg:s,tz:o,window:r,lengthOverride:e.length}),a=await cg(i,{summarizer:e.summarizer||null,cfg:s,given:e.given||null}),l=fg(i,a,{now:t});return{recap:i,summary:a,markdown:l,fileName:dg(t,o)}}function hl(n,e={}){let t=e.now!=null?e.now:Date.now(),s=e.cfg||ke,o=dl(s),r=kd(s),i=e.lastAt==null?null:e.lastAt,a=fl(e.since,{now:t,tz:o,lastAt:i,to:e.to||null,workdays:r}),l=vd(n,{...e,from:a.from,to:a.to,now:t,cfg:s}),c=xd(l),u=[...new Set(n.map(d=>d.session&&d.session.machine).filter(Boolean))].sort();return{tz:o,now:G(t),window:{from:G(a.from),to:G(a.to),spec:a.spec,kind:a.kind,label:a.label,fallback:a.fallback||null},windows:eg({now:t,tz:o,lastAt:i,workdays:r}),lastAt:i!=null?G(i):null,repos:c.repos,machines:c.machines,allMachines:u,sessions:l.length}}var Zm,iv,ri,ll,al,wd,av,ul,Vm,tg,gd,Xm,fv,Po,En,ig,lg,Ro,mv,je,pl=J(()=>{Te();Gs();si();Zm=1,iv=/^\d{4}-\d{2}-\d{2}T\d{4}(?:-\d+)?$/,ri=36e5,ll=24*ri,al={sun:"Sunday",mon:"Monday",tue:"Tuesday",wed:"Wednesday",thu:"Thursday",fri:"Friday",sat:"Saturday"},wd={short:{bullets:3,done:2,steps:1,questions:1,maxTokens:350},informative:{bullets:5,done:4,steps:3,questions:3,maxTokens:600},long:{bullets:6,done:8,steps:5,questions:5,maxTokens:900}};av=(n,e)=>{let t=Yt(n,e);return zt(t.y,t.mo,t.d,0,0,e)};ul=(n,e)=>new Date(n).toLocaleString("en-US",{timeZone:e,weekday:"short",month:"short",day:"numeric",hour:"numeric",minute:"2-digit"}).replace(/,(?= \w{3} \d)/,"");Vm=(n,e,t)=>!isNaN(n)&&n>=e&&n<=t;tg=n=>Math.max(R(n.s.lastActivityAt)||0,R(n.s.endedAt)||0,n.latest?R(n.latest.createdAt):0,R(n.s.startedAt)||0);gd=n=>{let e=new Set;return n.filter(t=>{let s=String(t||"").trim().toLowerCase();return!s||e.has(s)?!1:(e.add(s),!0)})},Xm=n=>String(n||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim(),fv=(n,e)=>{let t=Xm(n),s=Xm(e);return!!t&&!!s&&(t.includes(s)||s.includes(t))},Po=n=>String(n||"").replace(/^YOU:\s*/i,""),En=n=>String(n||"").replace(/[.!?…]+$/,"");ig='You catch a developer up after time away from their Claude Code sessions ("WTF was I working on?"). From the per-session notes, write {n} short bullets, most important first: what is waiting on them, what got done, where things stopped, and the one thing to pick up first. Name the session or repo in each bullet. Plain sentences, no headings, no preamble, no bold. Start every line with "- ".';lg=lp,Ro=n=>"“"+n+"”";mv=["kind","generated_at","window_from","window_to","window_label","window_spec","time_zone","repos","repo_keys","machines","machine_filter","made_on","model","summary_by","length","sessions","session_ids","needs_you","schema"],je=n=>String(n??"").replace(/\s*\n\s*/g," ").trim()});import mt from"node:fs";import Ae from"node:path";function Td(n){let e=Ae.basename(n),t=e.match(/^(.+?)\.sync-conflict-[\w-]+\.md$/)||e.match(/^(.+?) \([^)]*conflicted copy[^)]*\)\.md$/i)||e.match(/^(.+?) \d{1,2}\.md$/);return t?t[1]+".md":null}function Sv(n){return ga(n||"no-repo",60)||"no-repo"}function ml(n){try{return mt.accessSync(n),!0}catch{return!1}}function gt(n,{items:e=!0}={}){let t=hp(n);return{ref:n.ref,slug:n.slug,repoDir:n.repoDir,name:n.meta.name,repo:n.meta.repo,repoKey:n.meta.repoKey,archived:!!n.meta.archived,sourceSession:n.meta.sourceSession,sourceMachine:n.meta.sourceMachine,sourceTitle:n.meta.sourceTitle,created:n.meta.created,updated:n.meta.updated,done:t.done,total:t.total,progress:t.label,open:t.total-t.done,...e?{items:n.items.map(s=>({n:s.id,text:s.text,done:!!s.done,at:s.at||null}))}:{}}}function _d(n,{cli:e="leftoff"}={}){let t=(n||[]).filter(o=>!o.archived);if(!t.length)return"";let s=["Leftoff is tracking "+(t.length===1?"a checklist":t.length+" checklists")+" for the user from this session (their own list, separate from your internal todo list):"];for(let o of t.slice(0,4)){s.push("",'"'+o.name+'" ('+o.progress+" done; id "+o.ref+"):");for(let r of(o.items||[]).slice(0,40))s.push(" "+r.n+". ["+(r.done?"x":" ")+"] "+F(r.text,140))}return s.push("",'When you finish one of these steps, or the user says a step is done ("step 3 is done"), tick it by running: '+e+" todo done "+t[0].ref+" <n>"+(t.length>1?" (use that list's id)":"")+". To add a step: "+e+" todo add "+t[0].ref+` "<text>". Item numbers never change. Don't rewrite the list file yourself.`),s.join(`
114
- `)}var gv,yv,wv,Zn,gl=J(()=>{Te();xn();gv=1e4,yv=4e3,wv=n=>{try{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,n)}catch{}};Zn=class{constructor(e,t="unknown",{now:s}={}){this.root=e,this.machine=t,this.now=s||(()=>Date.now()),this.dir=Ae.join(e,"lists"),this.lockDir=Ae.join(e,".locks",String(t).replace(/[^\w.-]/g,"_"))}files(){let e=[],t=[];try{t=mt.readdirSync(this.dir,{withFileTypes:!0}).filter(s=>s.isDirectory()&&!s.name.startsWith(".")).map(s=>s.name)}catch{return e}for(let s of t){let o=[];try{o=mt.readdirSync(Ae.join(this.dir,s)).filter(r=>r.endsWith(".md")&&!r.startsWith("."))}catch{}for(let r of o)e.push(Ae.join(this.dir,s,r))}return e}refOf(e){return Ae.basename(Ae.dirname(e))+"/"+Ae.basename(e,".md")}fileOf(e){let[t,s]=String(e).split("/");return Ae.join(this.dir,t,s+".md")}read(e){let t;try{t=mt.readFileSync(e,"utf8")}catch{return null}let s=_u(t);return ho(s),this.decorate(s,e)}decorate(e,t){return e.file=t,e.ref=this.refOf(t),e.slug=Ae.basename(t,".md"),e.repoDir=Ae.basename(Ae.dirname(t)),e}healConflicts(){let e=0;for(let t of this.files()){let s=Td(t);if(!s)continue;let o=Ae.join(Ae.dirname(t),s);if(ml(o))try{this.update(this.refOf(o),i=>{let a=_u(mt.readFileSync(t,"utf8")),l=fp(i,a);i.meta=l.meta,i.items=l.items,i.segs=l.segs},{touch:!1});let r=Ae.join(this.dir,".merged",Ae.basename(Ae.dirname(t)));mt.mkdirSync(r,{recursive:!0}),mt.renameSync(t,Ae.join(r,Ae.basename(t))),e++}catch{}}return e}all({includeArchived:e=!1,repo:t=null,session:s=null}={}){this.healConflicts();let o=[];for(let r of this.files()){if(Td(r)&&ml(Ae.join(Ae.dirname(r),Td(r))))continue;let i=this.read(r);i&&(i.meta.archived&&!e||t&&i.repoDir!==t&&i.meta.repo!==t&&i.meta.repoKey!==t||s&&i.meta.sourceSession!==s||o.push(i))}return o.sort((r,i)=>(R(i.meta.updated)||0)-(R(r.meta.updated)||0))}resolve(e,{includeArchived:t=!0}={}){let s=String(e||"").trim();if(!s)throw Object.assign(new Error("which list? (give its name or repo/slug; `leftoff todo` lists them)"),{code:"no_list"});if(/^[\w.-]+\/[\w.-]+$/.test(s)&&ml(this.fileOf(s)))return this.read(this.fileOf(s));if(/^\d+$/.test(s)){let c=this.all()[Number(s)-1];if(c)return c}let o=this.all({includeArchived:t}),r=s.toLowerCase(),i=ga(s),a=[l=>l.slug===s||l.ref===s,l=>String(l.meta.name||"").toLowerCase()===r,l=>l.slug===i,l=>l.slug.startsWith(i)||String(l.meta.name||"").toLowerCase().startsWith(r),l=>l.slug.includes(i)];for(let l of a){let c=o.filter(l),u=c.filter(d=>!d.meta.archived);if(u.length===1||c.length===1)return u[0]||c[0];if(c.length>1)throw Object.assign(new Error('"'+s+'" matches '+c.length+" lists: "+c.map(d=>d.ref).join(", ")+". Use repo/slug."),{code:"ambiguous_list"})}throw Object.assign(new Error('no list called "'+s+'" (`leftoff todo --all` lists them)'),{code:"no_list"})}lock(e){mt.mkdirSync(this.lockDir,{recursive:!0});let t=Ae.join(this.lockDir,e.replace(/[^\w.-]+/g,"_")+".lock"),s=Date.now();for(;;){try{return mt.writeFileSync(t,String(process.pid),{flag:"wx"}),()=>{try{mt.unlinkSync(t)}catch{}}}catch(o){if(o.code!=="EEXIST")throw o}try{if(Date.now()-mt.statSync(t).mtimeMs>gv){mt.unlinkSync(t);continue}}catch{continue}if(Date.now()-s>yv)throw Object.assign(new Error("the list is busy (locked by another writer); try again"),{code:"list_busy"});wv(15)}}update(e,t,{touch:s=!0}={}){let o=this.fileOf(typeof e=="string"?e:e.ref),r=this.lock(this.refOf(o));try{if(!ml(o))throw Object.assign(new Error("that list is gone"),{code:"no_list"});let i=this.read(o);return t(i)===!1?i:(s&&(i.meta.updated=G(this.now())),Vn(o,Eu(i)),this.read(o))}finally{r()}}create({name:e,items:t=[],repo:s=null,repoKey:o=null,sourceSession:r=null,sourceMachine:i=null,sourceTitle:a=null}){let l=xs(e)||"Checklist",c=dp({name:l,items:t,repo:s,repoKey:o,sourceSession:r,sourceMachine:i||(r?this.machine:null),sourceTitle:a,now:G(this.now())});if(!c.items.length)throw Object.assign(new Error("a list needs at least one item"),{code:"empty_list"});let u=Ae.join(this.dir,Sv(s));mt.mkdirSync(u,{recursive:!0});let d=ga(l);for(let f=1;f<200;f++){let h=Ae.join(u,(f===1?d:d+"-"+f)+".md");try{return mt.writeFileSync(h,Eu(c),{flag:"wx"}),this.read(h)}catch(p){if(p.code!=="EEXIST")throw p}}throw new Error("no free file name for that list")}setDone(e,t,s=!0){let o=[],r=this.update(e,i=>{let a=!1;for(let l of t){let c=i.items.find(u=>u.id===Number(l));if(!c){o.push(l);continue}c.done!==!!s&&(c.done=!!s,c.at=G(this.now()),a=!0)}if(!a)return!1});if(o.length&&o.length===t.length)throw Object.assign(new Error("no item "+o.join(", ")+' on "'+r.meta.name+'" (it has '+r.items.map(i=>i.id).join(", ")+")"),{code:"no_item",list:r});return{list:r,missing:o}}findItem(e,t){let s=new Set(["the","and","for","step","item","done","with","that","this","from","into","its"]),o=String(t||"").toLowerCase().split(/[^a-z0-9]+/).filter(a=>a.length>2&&!s.has(a)),r=null,i=0;for(let a of e.items){let l=a.text.toLowerCase(),c=o.filter(u=>l.includes(u)).length/Math.max(1,o.length);c>i&&(r=a,i=c)}return i>=.6?r:null}addItem(e,t){let s=String(t||"").replace(/\s+/g," ").trim();if(!s)throw Object.assign(new Error("the item is empty"),{code:"empty_item"});let o=null,r=this.update(e,i=>{o={id:null,text:F(s,500),done:!1,at:G(this.now())},i.items.push(o),ho(i)});return{list:r,item:r.items.find(i=>i.text===o.text&&i.id===o.id)||r.items[r.items.length-1]}}editItem(e,t,{text:s,done:o}={}){let r=null,i=this.update(e,a=>{let l=a.items.find(c=>c.id===Number(t));if(!l)return!1;r=l,s!=null&&String(s).trim()&&(l.text=F(String(s).replace(/\s+/g," ").trim(),500)),o!=null&&(l.done=!!o),l.at=G(this.now())});if(!r)throw Object.assign(new Error("no item "+t+' on "'+i.meta.name+'"'),{code:"no_item"});return i}removeItem(e,t){let s=!1,o=this.update(e,r=>{let i=r.items.length;if(r.items=r.items.filter(a=>a.id!==Number(t)),s=r.items.length<i,!s)return!1});if(!s)throw Object.assign(new Error("no item "+t),{code:"no_item"});return o}rename(e,t){let s=xs(t);if(!s)throw Object.assign(new Error("the name is empty"),{code:"empty_name"});return this.update(e,o=>{o.meta.name=s})}archive(e,t=!0){return this.update(e,s=>{s.meta.archived=!!t})}}});function pg(n){let e={indent:-1,children:[]},t=[e],s=null;for(let r of String(n||"").split(/\r?\n/)){let i=r.match(/^(\s*)[-*+]\s+(.*)$/);if(i){let a=i[1].replace(/\t/g," ").length;for(;t.length>1&&a<=t[t.length-1].indent;)t.pop();s={indent:a,text:i[2].trim(),children:[]},t[t.length-1].children.push(s),t.push(s)}else if(s&&/^\s+\S/.test(r))s.text+=" "+r.trim();else if(r.trim())s=null,t.length=1;else continue}let o=r=>r.map(({text:i,children:a})=>({text:i,children:o(a)}));return o(e.children)}var mg=J(()=>{});import kv from"node:fs";import gg from"node:path";import{fileURLToPath as bv}from"node:url";function yg(n){let e=String(n||"").trim().replace(/^v/i,"").match(/^(\d+)\.(\d+)\.(\d+)/);return e?[Number(e[1]),Number(e[2]),Number(e[3])]:null}function qe(n,e){let t=yg(n),s=yg(e);if(!t||!s)return t?1:s?-1:0;for(let o=0;o<3;o++)if(t[o]!==s[o])return t[o]>s[o]?1:-1;return 0}function Ed(n){let e=[],t=null,s=null;for(let r of String(n||"").split(/\r?\n/)){let i=r.match(/^##\s+\[?v?(\d+\.\d+\.\d+[^\]\s]*)\]?(?:\s*[-–—]\s*(\d{4}-\d{2}-\d{2}))?/);if(i){t={version:i[1],date:i[2]||null,sections:[],lines:[]},e.push(t),s=null;continue}if(!t)continue;if(/^##\s/.test(r)){t=null;continue}t.lines.push(r);let a=r.match(/^###\s+(.+?)\s*$/);if(a){s={title:a[1],items:[],lines:[]},t.sections.push(s);continue}s&&s.lines.push(r);let l=r.match(/^\s*[-*]\s+(.*)$/);l?(s||(s={title:"Changes",items:[],lines:[r]},t.sections.push(s)),s.items.push(l[1].trim())):s&&s.items.length&&/^\s{2,}\S/.test(r)&&(s.items[s.items.length-1]+=" "+r.trim())}let o=({lines:r,...i})=>({...i,list:pg(r.join(`
112
+ `}function _d(n){let{data:e,body:t}=Ye(n),s={generatedAt:e.generated_at?G(e.generated_at):null,window:{from:e.window_from?G(e.window_from):null,to:e.window_to?G(e.window_to):null,label:e.window_label||null,spec:e.window_spec||null,tz:e.time_zone||null},repos:e.repos==="all"||e.repos==null?"all":[].concat(e.repos).map(String),repoKeys:Array.isArray(e.repo_keys)?e.repo_keys.map(String):[],machines:Array.isArray(e.machines)?e.machines.map(String):[],machineFilter:e.machine_filter==="all"||e.machine_filter==null?"all":[].concat(e.machine_filter).map(String),madeOn:e.made_on==null?null:String(e.made_on),model:e.model==null?null:String(e.model),summaryBy:e.summary_by&&typeof e.summary_by=="object"?e.summary_by:null,length:e.length||null,sessions:typeof e.sessions=="number"?e.sessions:null,sessionIds:Array.isArray(e.session_ids)?e.session_ids.map(String):[],needsYou:Array.isArray(e.needs_you)?e.needs_you.map(String):[]},o=t.split(`
113
+ `),r=[],i=[],a=[],l=[],c=null,u=null,d=null,f=null,h=p=>{let m=p.match(/<!--\s*session:\s*([^\s>]+)\s*-->/);return m?m[1]:null};for(let p of o){let m=p.replace(/\s+$/,""),g;if(g=m.match(/^#\s+(.*)$/)){s.title=g[1];continue}if(g=m.match(/^##\s+(.*?)(?:\s+\((\d+)\))?$/)){d=null,f=null,/^summary$/i.test(g[1])?c="summary":/^needs you$/i.test(g[1])?c="needs":/^still open$/i.test(g[1])?c="open":(c="repo",u={label:g[1],sessions:[]},a.push(u));continue}if(c==="summary"){(g=m.match(/^-\s+(.*)$/))&&r.push(g[1]);continue}if(c==="needs"){(g=m.match(/^-\s+\*\*(.+?)\*\*\s+\((.*?)\)\s+—\s+(.*?)(?:\s*<!--.*-->)?$/))&&i.push({id:h(m),title:g[1],repo:g[2].split(" · ")[0],needsYou:g[3]});continue}if(c==="open"){if(g=m.match(/^-\s+\*\*(.+?)\*\*\s+\((.*?) · (\d+)\/(\d+) done\)\s+—\s+next:\s*(.*?)(?:\s*<!--\s*list:\s*(\S+)\s*-->)?$/)){let S=k=>k.split(/;\s+(?=#\d+ )/).map(w=>{let x=w.match(/^#(\d+)\s+(.*)$/);return x?{n:Number(x[1]),text:x[2]}:null}).filter(Boolean);l.push({name:g[1],repo:g[2],done:Number(g[3]),total:Number(g[4]),next:S(g[5]),ref:g[6]||null,ticked:[]})}else(g=m.match(/^\s+-\s+Ticked off in this window:\s*(.*)$/))&&l.length&&(l[l.length-1].ticked=g[1].split(/;\s+(?=#\d+ )/).map(S=>{let k=S.match(/^#(\d+)\s+(.*)$/);return k?{n:Number(k[1]),text:k[2]}:null}).filter(Boolean));continue}if(c==="repo"){if(g=m.match(/^###\s+(.*)$/)){d={id:null,title:g[1],repo:u.label,done:[],nextSteps:[],questions:[],stoppedAt:null,next:null,needsYou:null,lastReply:null,resume:null,line:null},u.sessions.push(d),f=null;continue}if(d){if(h(m)&&!d.id){d.id=h(m);continue}if(!d.line&&(g=m.match(/^`([^`]+)`$/))){d.line=g[1];continue}if(g=m.match(/^Resume:\s+`(.*)`$/)){d.resume=g[1],f=null;continue}if(g=m.match(/^From (.+?)’s feed \(read-only\) · ([^<>→]{1,60}?) → <([^>\s]+)>$/)){d.agent={display:g[1],url:null,reply:{label:g[2],url:g[3]}},f=null;continue}if(g=m.match(/^From (.+?)’s feed \(read-only\)(?:\. Reply: <(https:\/\/[^>\s]+)>)?$/)){d.agent={display:g[1],url:g[2]||null},f=null;continue}if(g=m.match(/^\*\*([A-Za-z ]+):\*\*\s*(.*)$/)){let S=g[1].toLowerCase(),k=g[2].trim();f=null,S==="done"?k?d.done.push(k):f=d.done:S==="stopped at"?d.stoppedAt=k:S==="latest"?(d.stoppedAt=k,d.agent=d.agent||{display:null,url:null}):S==="next"?(d.next=k,f=d.nextSteps):S==="open questions"?f=d.questions:S==="needs you"?d.needsYou=k:S==="since then"&&(d.lastReply=k);continue}if((g=m.match(/^-\s+(.*)$/))&&f){f.push(g[1]);continue}m.trim()}}}return{meta:s,summary:r,needsYou:i,stillOpen:l,repos:a}}async function li(n,e={}){let t=e.now!=null?e.now:Date.now(),s=e.cfg||ke,o=hl(s),r=pl(e.since,{now:t,tz:o,lastAt:e.lastAt==null?null:e.lastAt,to:e.to||null,workdays:vd(s)}),i=cg(n,{...e,now:t,cfg:s,tz:o,window:r,lengthOverride:e.length}),a=await pg(i,{summarizer:e.summarizer||null,cfg:s,given:e.given||null}),l=yg(i,a,{now:t});return{recap:i,summary:a,markdown:l,fileName:gg(t,o)}}function ml(n,e={}){let t=e.now!=null?e.now:Date.now(),s=e.cfg||ke,o=hl(s),r=vd(s),i=e.lastAt==null?null:e.lastAt,a=pl(e.since,{now:t,tz:o,lastAt:i,to:e.to||null,workdays:r}),l=Ad(n,{...e,from:a.from,to:a.to,now:t,cfg:s}),c=Td(l),u=[...new Set(n.map(d=>d.session&&d.session.machine).filter(Boolean))].sort();return{tz:o,now:G(t),window:{from:G(a.from),to:G(a.to),spec:a.spec,kind:a.kind,label:a.label,fallback:a.fallback||null},windows:rg({now:t,tz:o,lastAt:i,workdays:r}),lastAt:i!=null?G(i):null,repos:c.repos,machines:c.machines,allMachines:u,sessions:l.length}}var sg,dv,ai,ul,cl,kd,fv,fl,tg,ig,wd,ng,yv,Fo,Cn,dg,hg,jo,kv,Fe,gl=J(()=>{Te();$s();ri();sg=1,dv=/^\d{4}-\d{2}-\d{2}T\d{4}(?:-\d+)?$/,ai=36e5,ul=24*ai,cl={sun:"Sunday",mon:"Monday",tue:"Tuesday",wed:"Wednesday",thu:"Thursday",fri:"Friday",sat:"Saturday"},kd={short:{bullets:3,done:2,steps:1,questions:1,maxTokens:350},informative:{bullets:5,done:4,steps:3,questions:3,maxTokens:600},long:{bullets:6,done:8,steps:5,questions:5,maxTokens:900}};fv=(n,e)=>{let t=Wt(n,e);return Jt(t.y,t.mo,t.d,0,0,e)};fl=(n,e)=>new Date(n).toLocaleString("en-US",{timeZone:e,weekday:"short",month:"short",day:"numeric",hour:"numeric",minute:"2-digit"}).replace(/,(?= \w{3} \d)/,"");tg=(n,e,t)=>!isNaN(n)&&n>=e&&n<=t;ig=n=>Math.max(R(n.s.lastActivityAt)||0,R(n.s.endedAt)||0,n.latest?R(n.latest.createdAt):0,R(n.s.startedAt)||0);wd=n=>{let e=new Set;return n.filter(t=>{let s=String(t||"").trim().toLowerCase();return!s||e.has(s)?!1:(e.add(s),!0)})},ng=n=>String(n||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim(),yv=(n,e)=>{let t=ng(n),s=ng(e);return!!t&&!!s&&(t.includes(s)||s.includes(t))},Fo=n=>String(n||"").replace(/^YOU:\s*/i,""),Cn=n=>String(n||"").replace(/[.!?…]+$/,"");dg='You catch a developer up after time away from their Claude Code sessions ("WTF was I working on?"). From the per-session notes, write {n} short bullets, most important first: what is waiting on them, what got done, where things stopped, and the one thing to pick up first. Name the session or repo in each bullet. Plain sentences, no headings, no preamble, no bold. Start every line with "- ".';hg=fp,jo=n=>"“"+n+"”";kv=["kind","generated_at","window_from","window_to","window_label","window_spec","time_zone","repos","repo_keys","machines","machine_filter","made_on","model","summary_by","length","sessions","session_ids","needs_you","schema"],Fe=n=>String(n??"").replace(/\s*\n\s*/g," ").trim()});import yt from"node:fs";import Ae from"node:path";function Ed(n){let e=Ae.basename(n),t=e.match(/^(.+?)\.sync-conflict-[\w-]+\.md$/)||e.match(/^(.+?) \([^)]*conflicted copy[^)]*\)\.md$/i)||e.match(/^(.+?) \d{1,2}\.md$/);return t?t[1]+".md":null}function Av(n){return Sa(n||"no-repo",60)||"no-repo"}function yl(n){try{return yt.accessSync(n),!0}catch{return!1}}function wt(n,{items:e=!0}={}){let t=yp(n);return{ref:n.ref,slug:n.slug,repoDir:n.repoDir,name:n.meta.name,repo:n.meta.repo,repoKey:n.meta.repoKey,archived:!!n.meta.archived,sourceSession:n.meta.sourceSession,sourceMachine:n.meta.sourceMachine,sourceTitle:n.meta.sourceTitle,created:n.meta.created,updated:n.meta.updated,done:t.done,total:t.total,progress:t.label,open:t.total-t.done,...e?{items:n.items.map(s=>({n:s.id,text:s.text,done:!!s.done,at:s.at||null}))}:{}}}function Od(n,{cli:e="leftoff"}={}){let t=(n||[]).filter(o=>!o.archived);if(!t.length)return"";let s=["Leftoff is tracking "+(t.length===1?"a checklist":t.length+" checklists")+" for the user from this session (their own list, separate from your internal todo list):"];for(let o of t.slice(0,4)){s.push("",'"'+o.name+'" ('+o.progress+" done; id "+o.ref+"):");for(let r of(o.items||[]).slice(0,40))s.push(" "+r.n+". ["+(r.done?"x":" ")+"] "+F(r.text,140))}return s.push("",'When you finish one of these steps, or the user says a step is done ("step 3 is done"), tick it by running: '+e+" todo done "+t[0].ref+" <n>"+(t.length>1?" (use that list's id)":"")+". To add a step: "+e+" todo add "+t[0].ref+` "<text>". Item numbers never change. Don't rewrite the list file yourself.`),s.join(`
114
+ `)}var bv,vv,xv,ts,wl=J(()=>{Te();Tn();bv=1e4,vv=4e3,xv=n=>{try{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,n)}catch{}};ts=class{constructor(e,t="unknown",{now:s}={}){this.root=e,this.machine=t,this.now=s||(()=>Date.now()),this.dir=Ae.join(e,"lists"),this.lockDir=Ae.join(e,".locks",String(t).replace(/[^\w.-]/g,"_"))}files(){let e=[],t=[];try{t=yt.readdirSync(this.dir,{withFileTypes:!0}).filter(s=>s.isDirectory()&&!s.name.startsWith(".")).map(s=>s.name)}catch{return e}for(let s of t){let o=[];try{o=yt.readdirSync(Ae.join(this.dir,s)).filter(r=>r.endsWith(".md")&&!r.startsWith("."))}catch{}for(let r of o)e.push(Ae.join(this.dir,s,r))}return e}refOf(e){return Ae.basename(Ae.dirname(e))+"/"+Ae.basename(e,".md")}fileOf(e){let[t,s]=String(e).split("/");return Ae.join(this.dir,t,s+".md")}read(e){let t;try{t=yt.readFileSync(e,"utf8")}catch{return null}let s=Ou(t);return mo(s),this.decorate(s,e)}decorate(e,t){return e.file=t,e.ref=this.refOf(t),e.slug=Ae.basename(t,".md"),e.repoDir=Ae.basename(Ae.dirname(t)),e}healConflicts(){let e=0;for(let t of this.files()){let s=Ed(t);if(!s)continue;let o=Ae.join(Ae.dirname(t),s);if(yl(o))try{this.update(this.refOf(o),i=>{let a=Ou(yt.readFileSync(t,"utf8")),l=gp(i,a);i.meta=l.meta,i.items=l.items,i.segs=l.segs},{touch:!1});let r=Ae.join(this.dir,".merged",Ae.basename(Ae.dirname(t)));yt.mkdirSync(r,{recursive:!0}),yt.renameSync(t,Ae.join(r,Ae.basename(t))),e++}catch{}}return e}all({includeArchived:e=!1,repo:t=null,session:s=null}={}){this.healConflicts();let o=[];for(let r of this.files()){if(Ed(r)&&yl(Ae.join(Ae.dirname(r),Ed(r))))continue;let i=this.read(r);i&&(i.meta.archived&&!e||t&&i.repoDir!==t&&i.meta.repo!==t&&i.meta.repoKey!==t||s&&i.meta.sourceSession!==s||o.push(i))}return o.sort((r,i)=>(R(i.meta.updated)||0)-(R(r.meta.updated)||0))}resolve(e,{includeArchived:t=!0}={}){let s=String(e||"").trim();if(!s)throw Object.assign(new Error("which list? (give its name or repo/slug; `leftoff todo` lists them)"),{code:"no_list"});if(/^[\w.-]+\/[\w.-]+$/.test(s)&&yl(this.fileOf(s)))return this.read(this.fileOf(s));if(/^\d+$/.test(s)){let c=this.all()[Number(s)-1];if(c)return c}let o=this.all({includeArchived:t}),r=s.toLowerCase(),i=Sa(s),a=[l=>l.slug===s||l.ref===s,l=>String(l.meta.name||"").toLowerCase()===r,l=>l.slug===i,l=>l.slug.startsWith(i)||String(l.meta.name||"").toLowerCase().startsWith(r),l=>l.slug.includes(i)];for(let l of a){let c=o.filter(l),u=c.filter(d=>!d.meta.archived);if(u.length===1||c.length===1)return u[0]||c[0];if(c.length>1)throw Object.assign(new Error('"'+s+'" matches '+c.length+" lists: "+c.map(d=>d.ref).join(", ")+". Use repo/slug."),{code:"ambiguous_list"})}throw Object.assign(new Error('no list called "'+s+'" (`leftoff todo --all` lists them)'),{code:"no_list"})}lock(e){yt.mkdirSync(this.lockDir,{recursive:!0});let t=Ae.join(this.lockDir,e.replace(/[^\w.-]+/g,"_")+".lock"),s=Date.now();for(;;){try{return yt.writeFileSync(t,String(process.pid),{flag:"wx"}),()=>{try{yt.unlinkSync(t)}catch{}}}catch(o){if(o.code!=="EEXIST")throw o}try{if(Date.now()-yt.statSync(t).mtimeMs>bv){yt.unlinkSync(t);continue}}catch{continue}if(Date.now()-s>vv)throw Object.assign(new Error("the list is busy (locked by another writer); try again"),{code:"list_busy"});xv(15)}}update(e,t,{touch:s=!0}={}){let o=this.fileOf(typeof e=="string"?e:e.ref),r=this.lock(this.refOf(o));try{if(!yl(o))throw Object.assign(new Error("that list is gone"),{code:"no_list"});let i=this.read(o);return t(i)===!1?i:(s&&(i.meta.updated=G(this.now())),Zn(o,Cu(i)),this.read(o))}finally{r()}}create({name:e,items:t=[],repo:s=null,repoKey:o=null,sourceSession:r=null,sourceMachine:i=null,sourceTitle:a=null}){let l=_s(e)||"Checklist",c=mp({name:l,items:t,repo:s,repoKey:o,sourceSession:r,sourceMachine:i||(r?this.machine:null),sourceTitle:a,now:G(this.now())});if(!c.items.length)throw Object.assign(new Error("a list needs at least one item"),{code:"empty_list"});let u=Ae.join(this.dir,Av(s));yt.mkdirSync(u,{recursive:!0});let d=Sa(l);for(let f=1;f<200;f++){let h=Ae.join(u,(f===1?d:d+"-"+f)+".md");try{return yt.writeFileSync(h,Cu(c),{flag:"wx"}),this.read(h)}catch(p){if(p.code!=="EEXIST")throw p}}throw new Error("no free file name for that list")}setDone(e,t,s=!0){let o=[],r=this.update(e,i=>{let a=!1;for(let l of t){let c=i.items.find(u=>u.id===Number(l));if(!c){o.push(l);continue}c.done!==!!s&&(c.done=!!s,c.at=G(this.now()),a=!0)}if(!a)return!1});if(o.length&&o.length===t.length)throw Object.assign(new Error("no item "+o.join(", ")+' on "'+r.meta.name+'" (it has '+r.items.map(i=>i.id).join(", ")+")"),{code:"no_item",list:r});return{list:r,missing:o}}findItem(e,t){let s=new Set(["the","and","for","step","item","done","with","that","this","from","into","its"]),o=String(t||"").toLowerCase().split(/[^a-z0-9]+/).filter(a=>a.length>2&&!s.has(a)),r=null,i=0;for(let a of e.items){let l=a.text.toLowerCase(),c=o.filter(u=>l.includes(u)).length/Math.max(1,o.length);c>i&&(r=a,i=c)}return i>=.6?r:null}addItem(e,t){let s=String(t||"").replace(/\s+/g," ").trim();if(!s)throw Object.assign(new Error("the item is empty"),{code:"empty_item"});let o=null,r=this.update(e,i=>{o={id:null,text:F(s,500),done:!1,at:G(this.now())},i.items.push(o),mo(i)});return{list:r,item:r.items.find(i=>i.text===o.text&&i.id===o.id)||r.items[r.items.length-1]}}editItem(e,t,{text:s,done:o}={}){let r=null,i=this.update(e,a=>{let l=a.items.find(c=>c.id===Number(t));if(!l)return!1;r=l,s!=null&&String(s).trim()&&(l.text=F(String(s).replace(/\s+/g," ").trim(),500)),o!=null&&(l.done=!!o),l.at=G(this.now())});if(!r)throw Object.assign(new Error("no item "+t+' on "'+i.meta.name+'"'),{code:"no_item"});return i}removeItem(e,t){let s=!1,o=this.update(e,r=>{let i=r.items.length;if(r.items=r.items.filter(a=>a.id!==Number(t)),s=r.items.length<i,!s)return!1});if(!s)throw Object.assign(new Error("no item "+t),{code:"no_item"});return o}rename(e,t){let s=_s(t);if(!s)throw Object.assign(new Error("the name is empty"),{code:"empty_name"});return this.update(e,o=>{o.meta.name=s})}archive(e,t=!0){return this.update(e,s=>{s.meta.archived=!!t})}}});function Sg(n){let e={indent:-1,children:[]},t=[e],s=null;for(let r of String(n||"").split(/\r?\n/)){let i=r.match(/^(\s*)[-*+]\s+(.*)$/);if(i){let a=i[1].replace(/\t/g," ").length;for(;t.length>1&&a<=t[t.length-1].indent;)t.pop();s={indent:a,text:i[2].trim(),children:[]},t[t.length-1].children.push(s),t.push(s)}else if(s&&/^\s+\S/.test(r))s.text+=" "+r.trim();else if(r.trim())s=null,t.length=1;else continue}let o=r=>r.map(({text:i,children:a})=>({text:i,children:o(a)}));return o(e.children)}var kg=J(()=>{});import Tv from"node:fs";import bg from"node:path";import{fileURLToPath as _v}from"node:url";function vg(n){let e=String(n||"").trim().replace(/^v/i,"").match(/^(\d+)\.(\d+)\.(\d+)/);return e?[Number(e[1]),Number(e[2]),Number(e[3])]:null}function Xe(n,e){let t=vg(n),s=vg(e);if(!t||!s)return t?1:s?-1:0;for(let o=0;o<3;o++)if(t[o]!==s[o])return t[o]>s[o]?1:-1;return 0}function Cd(n){let e=[],t=null,s=null;for(let r of String(n||"").split(/\r?\n/)){let i=r.match(/^##\s+\[?v?(\d+\.\d+\.\d+[^\]\s]*)\]?(?:\s*[-–—]\s*(\d{4}-\d{2}-\d{2}))?/);if(i){t={version:i[1],date:i[2]||null,sections:[],lines:[]},e.push(t),s=null;continue}if(!t)continue;if(/^##\s/.test(r)){t=null;continue}t.lines.push(r);let a=r.match(/^###\s+(.+?)\s*$/);if(a){s={title:a[1],items:[],lines:[]},t.sections.push(s);continue}s&&s.lines.push(r);let l=r.match(/^\s*[-*]\s+(.*)$/);l?(s||(s={title:"Changes",items:[],lines:[r]},t.sections.push(s)),s.items.push(l[1].trim())):s&&s.items.length&&/^\s{2,}\S/.test(r)&&(s.items[s.items.length-1]+=" "+r.trim())}let o=({lines:r,...i})=>({...i,list:Sg(r.join(`
115
115
  `))});return e.map(({lines:r,...i})=>({...i,sections:i.sections.map(o),markdown:r.join(`
116
- `).trim()}))}function jo(n=vv){try{return Ed(kv.readFileSync(n,"utf8"))}catch{return[]}}function Fo(n,e,t){return n.filter(s=>(!e||qe(s.version,e)>0)&&(!t||qe(s.version,t)<=0))}function wg(n,e,t){return n.map(s=>({...s,isNew:(!t||qe(s.version,t)<=0)&&(!e||qe(s.version,e)>0)}))}function yl(n,{colors:e=null,width:t=78}={}){let s=e||{bold:i=>i,dim:i=>i,cyan:i=>i,green:i=>i},o=[],r=(i,a)=>{let l=String(i).replace(/\*\*(.+?)\*\*/g,"$1").split(/\s+/),c=[],u="";for(let d of l)(u+" "+d).trim().length>t-a.length?(c.push(u),u=d):u=(u+" "+d).trim();return u&&c.push(u),c.map((d,f)=>(f?a:"")+d)};for(let i of n){o.push(s.bold(s.cyan("v"+i.version))+(i.date?s.dim(" "+i.date):"")+(i.isNew?" "+s.green("new"):""));for(let a of i.sections){o.push(" "+s.bold(a.title));for(let l of a.items)o.push(" • "+r(l," ").join(`
116
+ `).trim()}))}function Uo(n=Ev){try{return Cd(Tv.readFileSync(n,"utf8"))}catch{return[]}}function Ho(n,e,t){return n.filter(s=>(!e||Xe(s.version,e)>0)&&(!t||Xe(s.version,t)<=0))}function xg(n,e,t){return n.map(s=>({...s,isNew:(!t||Xe(s.version,t)<=0)&&(!e||Xe(s.version,e)>0)}))}function Sl(n,{colors:e=null,width:t=78}={}){let s=e||{bold:i=>i,dim:i=>i,cyan:i=>i,green:i=>i},o=[],r=(i,a)=>{let l=String(i).replace(/\*\*(.+?)\*\*/g,"$1").split(/\s+/),c=[],u="";for(let d of l)(u+" "+d).trim().length>t-a.length?(c.push(u),u=d):u=(u+" "+d).trim();return u&&c.push(u),c.map((d,f)=>(f?a:"")+d)};for(let i of n){o.push(s.bold(s.cyan("v"+i.version))+(i.date?s.dim(" "+i.date):"")+(i.isNew?" "+s.green("new"):""));for(let a of i.sections){o.push(" "+s.bold(a.title));for(let l of a.items)o.push(" • "+r(l," ").join(`
117
117
  `))}o.push("")}return o.join(`
118
- `)}var vv,wl=J(()=>{mg();vv=gg.resolve(gg.dirname(bv(import.meta.url)),"..","CHANGELOG.md")});var Ge,V,st,Sg,ai,$s,Od,kg,Ys,Cd,bg,vg,xg,Tt,Ag,_t=J(()=>{Ge="Leftoff",V="leftoff",st="OneSillyApp/leftoff",Sg=["joelash/checkpoints","joelash/leftoff"],ai="leftoff-hook",$s="checkpoints-hook",Od="dev.leftoff.daemon",kg="dev.checkpoints.daemon",Ys="leftoff.service",Cd="checkpoints.service",bg="checkpoints",vg="leftoff",xg="checkpoints",Tt="leftoff.localhost",Ag="checkpoints.localhost"});var Rg={};Qt(Rg,{CHECK_EVERY_MS:()=>Ld,DEFAULT_SOURCE:()=>li,GITHUB_REPO:()=>st,NPM_PACKAGE:()=>ot,PACKAGE_DIRS:()=>ci,REPO_DIR:()=>Qn,STAMP_FILE:()=>Md,checkForUpdate:()=>di,defaultCacheDir:()=>Pd,fetchRemoteFiles:()=>Dg,installInfo:()=>ui,isNpmBuild:()=>Cg,isNpxCacheDir:()=>Nd,lsRemote:()=>Lg,normalizeSource:()=>Ho,npmLatest:()=>Ng,npmPackageUrl:()=>kl,npmRegistry:()=>Sl,npmUpgradeCommand:()=>Rd,packageManagerOf:()=>Ig,readPkg:()=>Og,readStamp:()=>_g,registryPath:()=>Eg,renamedSource:()=>zs,runGit:()=>Et,sshFallback:()=>Mg,writeStamp:()=>Dd});import Uo from"node:fs";import xv from"node:os";import On from"node:path";import{execFile as Av}from"node:child_process";import{fileURLToPath as Tv}from"node:url";function Ho(n){let e=String(n||"").trim();if(!e)return li;let t=e.match(/^(?:github:)?([\w.-]+)\/([\w.-]+?)(?:\.git)?(?:#.*)?$/);return t&&!e.includes("://")&&!e.startsWith("/")&&!e.startsWith(".")?zs("https://github.com/"+t[1]+"/"+t[2]+".git"):zs(e)}function zs(n){let e=String(n||"");for(let t of Sg){let s=t.replace(/[.]/g,"\\."),o=new RegExp("^(https://github\\.com/|git@github\\.com:|ssh://git@github\\.com/)"+s+"(?:\\.git)?/?$","i"),r=e.match(o);if(r)return r[1]+st+".git"}return e}function Et(n,{cwd:e,timeoutMs:t=15e3,env:s=process.env}={}){return new Promise(o=>{let r={...s,GIT_TERMINAL_PROMPT:"0",GIT_ASKPASS:s.GIT_ASKPASS||"echo",SSH_ASKPASS:"echo",GIT_SSH_COMMAND:s.GIT_SSH_COMMAND||"ssh -o BatchMode=yes"};Av(s.CHECKPOINTS_GIT||"git",n,{cwd:e,timeout:t,env:r,maxBuffer:4*1024*1024},(i,a,l)=>{o({code:i?typeof i.code=="number"?i.code:-1:0,stdout:String(a||""),stderr:String(l||(i&&!a?i.message:"")||"")})})})}function _g(n=Qn){try{return JSON.parse(Uo.readFileSync(On.join(n,Md),"utf8"))}catch{return null}}function Dd(n,e=Qn){let t={version:n.version||X,sha:n.sha||null,source:n.source||null,at:n.at||new Date().toISOString()};return Uo.writeFileSync(On.join(e,Md),JSON.stringify(t,null,2)+`
119
- `),t}function Nd(n){return/[\\/]_npx[\\/]/.test(n)||/[\\/]\.npm[\\/]/.test(n)}function Eg(n=ot){return n.startsWith("@")?"@"+n.slice(1).split("/").map(encodeURIComponent).join("%2f"):encodeURIComponent(n)}function kl(n=process.env,e=ot){return Sl(n)+"/"+Eg(e)}function Og(n=Qn){try{return JSON.parse(Uo.readFileSync(On.join(n,"package.json"),"utf8"))}catch{return null}}function Cg(n=Qn){let e=Og(n);return!!(e&&e.leftoffBuild==="npm")}function Ig(n,e=process.env){if(e.LEFTOFF_PM)return e.LEFTOFF_PM;let t=String(n||"");return/[\\/]\.?pnpm[\\/]/i.test(t)||/[\\/]pnpm-global[\\/]/i.test(t)?"pnpm":/[\\/]\.bun[\\/]/.test(t)?"bun":"npm"}function Rd(n,e=ot+"@latest"){let t=process.platform==="win32";return n==="pnpm"?[t?"pnpm.cmd":"pnpm","add","-g",e]:n==="bun"?["bun","add","-g",e]:[t?"npm.cmd":"npm","install","-g",e,"--no-fund","--no-audit","--loglevel=error"]}function Sl(n=process.env){return String(n.LEFTOFF_NPM_REGISTRY||n.npm_config_registry||"https://registry.npmjs.org").replace(/\/+$/,"")}async function Ng({env:n=process.env,fetchImpl:e=fetch,timeoutMs:t=15e3}={}){let s=kl(n);try{let o=await e(s,{headers:{accept:"application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8"},signal:AbortSignal.timeout(t)});if(o.status===404)return{ok:!1,version:null,error:ot+" isn’t on "+Sl(n)+" (404)"};if(!o.ok)return{ok:!1,version:null,error:Sl(n)+" answered "+o.status};let r=await o.json(),i=r&&(r["dist-tags"]&&r["dist-tags"].latest||r.version);return i?{ok:!0,version:i,error:null}:{ok:!1,version:null,error:"no version in the registry answer"}}catch(o){return{ok:!1,version:null,error:o.message}}}async function ui({repoDir:n=Qn,env:e=process.env,globalRoot:t}={}){let s={version:X,sha:null,kind:"local",source:null,repoDir:n};if(Uo.existsSync(On.join(n,".git"))){s.kind="clone";let r=await Et(["rev-parse","HEAD"],{cwd:n,env:e});r.code===0&&(s.sha=r.stdout.trim());let i=await Et(["remote","get-url","origin"],{cwd:n,env:e});return s.source=i.code===0?i.stdout.trim():null,s}if(Cg(n))return s.kind="npm",s.source="npm:"+ot,s.pm=Ig(n,e),s.npx=Nd(n),s;let o=_g(n);return o&&(s.sha=o.sha||null,s.source=o.source||null),Nd(n)?s.kind="npx":(t?n.startsWith(t):/[\\/]node_modules[\\/](checkpoints|leftoff|@leftoff[\\/]cli)$/.test(n))&&(s.kind="global"),s}function Mg(n){let e=String(n||"").match(/^https:\/\/github\.com\/([\w.-]+)\/([\w.-]+?)(?:\.git)?\/?$/);return e?"git@github.com:"+e[1]+"/"+e[2]+".git":null}async function Tg(n,{env:e,timeoutMs:t}){let s=await Et(["ls-remote",n,"HEAD"],{env:e,timeoutMs:t}),o=s.code===0?(s.stdout.match(/^([0-9a-f]{40})\s+HEAD$/m)||[])[1]:null;return o?{ok:!0,sha:o,error:null,source:n}:{ok:!1,sha:null,error:Id(s.stderr)||"git ls-remote exited "+s.code,source:n}}async function Lg(n,{env:e=process.env,timeoutMs:t=15e3}={}){let s=Ho(n),o=await Tg(s,{env:e,timeoutMs:t});if(o.ok)return o;let r=Mg(s);if(!r)return o;let i=await Tg(r,{env:e,timeoutMs:t});return i.ok?i:{ok:!1,sha:null,source:s,error:o.error+" (also tried "+r+": "+i.error+")"}}async function Dg(n,{cacheDir:e=Pd(),env:t=process.env,timeoutMs:s=3e4}={}){try{if(Uo.mkdirSync(e,{recursive:!0}),!Uo.existsSync(On.join(e,"HEAD"))){let c=await Et(["init","--bare","-q",e],{env:t});if(c.code!==0)return{ok:!1,error:Id(c.stderr)}}let o=await Et(["fetch","-q","--depth=1",Ho(n),"HEAD"],{cwd:e,env:t,timeoutMs:s});if(o.code!==0)return{ok:!1,error:Id(o.stderr)||"git fetch exited "+o.code};let r=(await Et(["rev-parse","FETCH_HEAD"],{cwd:e,env:t})).stdout.trim()||null,i=await Et(["show","FETCH_HEAD:CHANGELOG.md"],{cwd:e,env:t}),a=await Et(["show","FETCH_HEAD:package.json"],{cwd:e,env:t}),l=null;try{l=JSON.parse(a.stdout).version||null}catch{}return{ok:!0,sha:r,version:l,changelog:i.code===0?i.stdout:null,error:null}}catch(o){return{ok:!1,error:o.message}}}function Pd(n=process.env){return On.join(n.XDG_CACHE_HOME||On.join(xv.homedir(),".cache"),"checkpoints","remote.git")}async function di({info:n,source:e,env:t=process.env,cacheDir:s,now:o=Date.now(),fetchFiles:r=!0}={}){let i=n||await ui({env:t});if(i.kind==="npm"){let d={checkedAt:new Date(o).toISOString(),source:"npm:"+ot,current:{version:i.version,sha:null},remote:{sha:null,version:null},available:!1,incoming:[],error:null},f=await Ng({env:t});return f.ok?(d.remote.version=f.version,d.available=qe(f.version,i.version)===1,d):(d.error=f.error,d)}let a=Ho(e||i.source||li),l={checkedAt:new Date(o).toISOString(),source:a,current:{version:i.version,sha:i.sha},remote:{sha:null,version:null},available:!1,incoming:[],error:null},c=await Lg(a,{env:t});if(!c.ok)return l.error=c.error,l;if(l.source=c.source,l.remote.sha=c.sha,i.sha&&i.sha===c.sha)return l;if(r){let d=await Dg(l.source,{cacheDir:s||Pd(t),env:t});d.ok?(l.remote.version=d.version,d.changelog&&(l.incoming=Fo(Ed(d.changelog),i.version,d.version).map(f=>({version:f.version,date:f.date,sections:f.sections})))):l.fetchError=d.error}let u=l.remote.version?qe(l.remote.version,i.version):null;return l.available=u===1||u===0&&!!i.sha||u===null&&!!i.sha,u===-1&&(l.available=!1),l}var Qn,li,Md,Ld,Id,ot,ci,fi=J(()=>{_t();xe();wl();Qn=On.resolve(On.dirname(Tv(import.meta.url)),".."),li="https://github.com/"+st+".git",Md=".install.json",Ld=24*3600*1e3;Id=n=>String(n||"").trim().split(`
120
- `).filter(Boolean).pop()||"";ot="@leftoff/cli",ci=[ot,"leftoff","checkpoints"]});import Fg from"node:crypto";function Hg(){return"rq_"+Fg.randomBytes(9).toString("base64url")}function hi(n,e){let t=s=>Array.isArray(s)?"["+s.map(t).join(",")+"]":s&&typeof s=="object"?"{"+Object.keys(s).filter(o=>o!=="answers").sort().map(o=>JSON.stringify(o)+":"+t(s[o])).join(",")+"}":JSON.stringify(s===void 0?null:s);return Fg.createHash("sha256").update(String(n||"")+`
121
- `+t(e||{})).digest("hex").slice(0,16)}function Ev(n,e={}){let t=e&&typeof e=="object"?e:{};switch(n){case"Bash":return{verb:"Run",detail:Ve(t.command,400),note:t.description?Ve(t.description,160):null};case"Edit":case"MultiEdit":return{verb:"Edit",detail:Ve(t.file_path,300),note:null};case"Write":return{verb:"Write",detail:Ve(t.file_path,300),note:t.content?Ve(String(t.content).split(`
122
- `).length+" lines",40):null};case"NotebookEdit":return{verb:"Edit notebook",detail:Ve(t.notebook_path,300),note:null};case"Read":return{verb:"Read",detail:Ve(t.file_path,300),note:null};case"WebFetch":return{verb:"Fetch",detail:Ve(t.url,300),note:null};case"WebSearch":return{verb:"Search the web for",detail:Ve(t.query,300),note:null};default:{let s=Object.entries(t).find(([,o])=>typeof o=="string");return{verb:"Use "+n,detail:s?Ve(s[1],300):"",note:null}}}}function Ov(n={}){return(Array.isArray(n&&n.questions)?n.questions:[]).slice(0,6).map(t=>({question:String(t.question||""),header:t.header?String(t.header):null,kind:t.kind==="text"||t.kind==="number"?t.kind:"choice",multiSelect:!!t.multiSelect,options:(Array.isArray(t.options)?t.options:[]).slice(0,8).map(s=>({label:String(s.label||s),description:s.description?String(s.description):null}))})).filter(t=>t.question)}function Cv(n,e,t={}){let s=Array.isArray(e)?e.filter(r=>r&&(r.type==="addRules"&&Array.isArray(r.rules)&&r.behavior!=="deny"||r.type==="addDirectories"&&Array.isArray(r.directories))):[];return s.length?s.map(r=>r.type==="addRules"?{type:"addRules",rules:r.rules,behavior:"allow",destination:"session"}:{type:"addDirectories",directories:r.directories,destination:"session"}):[{type:"addRules",rules:[n==="Bash"&&t&&typeof t.command=="string"?{toolName:"Bash",ruleContent:t.command}:{toolName:n}],behavior:"allow",destination:"session"}]}function Iv(n,e={}){let t=r=>{let i=new Error(r);throw i.status=400,i.code="bad_answer",i},s=e.action;Pg.includes(s)||t("action is one of "+Pg.join(", "));let o=e.message?Ve(e.message,500):"";if(s==="deny")return{behavior:"deny",message:o||"Denied from the Leftoff board."};if(n.kind==="question"){s!=="answer"&&t('a question takes action "answer" (or "deny")');let r=e.answers&&typeof e.answers=="object"?e.answers:{},i={};for(let a of n.questions){let l=r[a.question];if(Array.isArray(l)&&(l=l.map(String).filter(Boolean)),!(l==null||l===""||Array.isArray(l)&&!l.length))if(a.kind==="choice"&&a.options.length){let c=Array.isArray(l)?l:[String(l)],u=c.filter(d=>!a.options.some(f=>f.label===d));u.length&&!e.allowOther&&t('"'+u[0]+'" isn’t one of the options for "'+a.question+'"'),i[a.question]=a.multiSelect?c.join(", "):c[0]}else i[a.question]=Array.isArray(l)?l.join(", "):String(l)}return Object.keys(i).length||t("no answer given"),{behavior:"allow",updatedInput:{...n.input||{},answers:i}}}return s==="answer"&&t("a permission takes allow, allow_session or deny"),s==="allow_session"?{behavior:"allow",updatedPermissions:Cv(n.tool,n.suggestions,n.input)}:{behavior:"allow"}}function Bg(n){return n?{hookSpecificOutput:{hookEventName:"PermissionRequest",decision:n}}:{}}function pi(n,e={}){if(e.action==="deny")return"Denied"+(e.message?" (“"+Ve(e.message,160)+"”)":"");if(e.action==="allow")return"Allowed once";if(e.action==="allow_session")return"Allowed for this session";if(e.action==="answer"){let t=e.answers&&typeof e.answers=="object"?e.answers:{};return"Answered "+Object.entries(t).map(([s,o])=>"“"+Ve(s,80)+"” → "+Ve(Array.isArray(o)?o.join(", "):o,120)).join("; ")}return e.action==="reply"?"Replied “"+Ve(e.text,200)+"”":String(e.action||"")}function jd({via:n,ip:e,host:t,who:s,userAgent:o,peer:r,transport:i}={}){if(n==="peer")return"from "+(r||"another machine")+"’s board"+(i?" ("+(i==="relay"?"through the relay":"direct")+")":"");let a=String(o||""),l=/Edg\//.test(a)?"Edge":/Chrome\//.test(a)?"Chrome":/Firefox\//.test(a)?"Firefox":/Safari\//.test(a)?"Safari":a?"a browser":null,c=/iPhone|iPad/.test(a)?"iPhone/iPad":/Android/.test(a)?"Android":null;return n==="remote"?"over Tailscale"+(s?" as "+s:"")+(t?" ("+t+")":"")+(c||l?" · "+[c,l].filter(Boolean).join(" "):""):"on this machine"+(l?" · "+l:"")+(e?" ("+e+")":"")}function Gg({max:n=30,windowMs:e=6e4,now:t=()=>Date.now()}={}){let s=new Map;return{take(o="all"){let r=t(),i=(s.get(o)||[]).filter(a=>r-a<e);return i.length>=n?(s.set(o,i),!1):(i.push(r),s.set(o,i),!0)}}}var Pg,_v,Ug,bl,jg,Ve,vl,xl,mi=J(()=>{Pg=["allow","allow_session","deny","answer"],_v=9.5*60*1e3,Ug=45*1e3,bl=25*1e3,jg=4e3;Ve=(n,e)=>{let t=String(n??"").replace(/\s+/g," ").trim();return t.length>e?t.slice(0,e-1)+"…":t};vl=class{constructor({now:e=()=>Date.now(),timeoutMs:t=_v,noListenerMs:s=Ug,keep:o=200,onChange:r=()=>{}}={}){this.now=e,this.timeoutMs=t,this.noListenerMs=s,this.keep=o,this.onChange=r,this.items=new Map}open({sessionId:e,tool:t,input:s,suggestions:o,capture:r,cwd:i,permissionMode:a}={}){if(!e||typeof e!="string"){let f=new Error("sessionId is required");throw f.status=400,f.code="bad_ask",f}if(!t||typeof t!="string"){let f=new Error("tool is required");throw f.status=400,f.code="bad_ask",f}let l=hi(t,s);for(let f of this.items.values())if(f.state==="waiting"&&f.sessionId===e&&f.key===l)return f.lastPollAt=this.now(),f.captures.add(r||"hooks"),f;let c=this.now(),u=t==="AskUserQuestion"?"question":"permission",d={id:Hg(),sessionId:e,kind:u,tool:t,key:l,input:s&&typeof s=="object"?s:{},suggestions:Array.isArray(o)?o:[],captures:new Set([r||"hooks"]),cwd:i||null,permissionMode:a||null,questions:u==="question"?Ov(s):null,describe:u==="permission"?Ev(t,s):null,createdAt:c,expiresAt:c+this.timeoutMs,lastPollAt:c,state:"waiting",reason:null,decision:null,answer:null,answeredAt:null,settledAt:null,by:null,delivered:!1,waiters:new Set};return this.items.set(d.id,d),this.trim(),this.onChange(d,"opened"),d}get(e){return this.items.get(e)||null}waiting({sessionId:e}={}){return this.sweep(),[...this.items.values()].filter(t=>t.state==="waiting"&&(!e||t.sessionId===e)).sort((t,s)=>t.createdAt-s.createdAt)}wait(e,t=bl,s=null){let o=this.items.get(e);if(!o)return Promise.resolve({id:e,state:"gone",reason:"unknown"});if(o.lastPollAt=this.now(),o.state!=="waiting")return Promise.resolve(this.forHook(o,!0));let r=Math.max(0,Math.min(Number(t)||0,bl,o.expiresAt-this.now()));return new Promise(i=>{let a={resolve:null,timer:null};a.resolve=l=>{clearTimeout(a.timer),o.waiters.delete(a),o.lastPollAt=this.now(),i(l)},a.timer=setTimeout(()=>{this.sweep(),a.resolve(this.forHook(o,!1))},r),a.timer.unref&&a.timer.unref(),o.waiters.add(a),s&&s.addEventListener("abort",()=>{o.waiters.has(a)&&(clearTimeout(a.timer),o.waiters.delete(a),i(null))},{once:!0})})}forHook(e,t){return e.state==="answered"&&t&&(e.delivered=!0),{id:e.id,state:e.state,reason:e.reason,...e.state==="answered"?{decision:e.decision}:{}}}listening(e){return e.waiters.size>0||this.now()-e.lastPollAt<this.noListenerMs}answer(e,t={},s={}){let o=this.items.get(e);if(!o){let a=new Error("no such request");throw a.status=404,a.code="not_found",a}if(this.sweep(),o.state!=="waiting"){let a=new Error(o.state==="answered"?"already answered"+(o.by&&o.by.where?" "+o.by.where:""):o.reason==="answered_in_terminal"?"already answered in the terminal":"this request is no longer waiting ("+(o.reason||o.state)+")");throw a.status=409,a.code="not_waiting",a.state=o.state,a}let r=Iv(o,t);o.state="answered",o.decision=r,o.answer={action:t.action,message:t.message?Ve(t.message,500):null,answers:t.answers||null},o.answeredAt=o.settledAt=this.now(),o.by=s;let i=this.forHook(o,!1);if(o.waiters.size){o.delivered=!0;for(let a of[...o.waiters])a.resolve(i)}return this.onChange(o,"answered"),o}delivery(e,t=3e3){let s=this.items.get(e);return s?s.delivered?Promise.resolve(!0):new Promise(o=>{let r=this.now(),i=()=>{if(s.delivered)return o(!0);if(this.now()-r>=t)return o(!1);setTimeout(i,50)};i()}):Promise.resolve(!1)}settle(e,t="answered_in_terminal"){let s=this.items.get(e);if(!s||s.state!=="waiting")return s||null;s.state="gone",s.reason=String(t||"gone").slice(0,40),s.settledAt=this.now();let o=this.forHook(s,!1);for(let r of[...s.waiters])r.resolve(o);return this.onChange(s,"settled"),s}settleSession(e,t,{key:s,tool:o}={}){let r=[];for(let i of this.items.values())i.state!=="waiting"||i.sessionId!==e||s&&i.key!==s||o&&i.tool!==o||r.push(this.settle(i.id,t));return r}sweep(){let e=this.now();for(let t of this.items.values())if(t.state==="waiting")if(e>=t.expiresAt){t.state="expired",t.reason="timeout",t.settledAt=e;let s=this.forHook(t,!1);for(let o of[...t.waiters])o.resolve(s);this.onChange(t,"expired")}else this.listening(t)||this.settle(t.id,"no_listener")}trim(){if(!(this.items.size<=this.keep))for(let e of[...this.items.values()].sort((t,s)=>t.createdAt-s.createdAt)){if(this.items.size<=this.keep)break;e.state!=="waiting"&&this.items.delete(e.id)}}publicAsk(e){return e?{id:e.id,sessionId:e.sessionId,kind:e.kind,tool:e.tool,describe:e.describe,questions:e.questions,canAllowSession:e.kind==="permission",state:e.state,reason:e.reason,createdAt:new Date(e.createdAt).toISOString(),expiresAt:new Date(e.expiresAt).toISOString(),answeredAt:e.answeredAt?new Date(e.answeredAt).toISOString():null,settledAt:e.settledAt?new Date(e.settledAt).toISOString():null,by:e.by?{where:e.by.where||null,via:e.by.via||null}:null,summary:e.answer?pi(e,e.answer):null,capture:[...e.captures].includes("mod")?"mod":"hooks"}:null}},xl=class n{constructor({now:e=()=>Date.now(),ttlMs:t=600*1e3}={}){this.now=e,this.ttlMs=t,this.queues=new Map,this.waiters=new Map,this.lastPoll=new Map}static check(e){let t=String(e??"").replace(/\r\n?/g,`
123
- `).trim(),s=o=>{let r=new Error(o);throw r.status=400,r.code="bad_reply",r};return t||s("the reply is empty"),t.startsWith("/")&&s("a reply can’t start with “/” (slash commands aren’t sent from the board)"),t.length>jg&&s("the reply is over "+jg+" characters"),t}listening(e){return(this.waiters.get(e)||new Set).size>0||this.now()-(this.lastPoll.get(e)||0)<Ug}put(e,t,s={}){let o={id:Hg().replace("rq_","rp_"),text:n.check(t),at:this.now(),by:s},r=this.waiters.get(e);if(r&&r.size)return r.values().next().value.resolve(o),{...o,delivered:!0};let i=this.queues.get(e)||[];return i.push(o),this.queues.set(e,i),{...o,delivered:!1}}pending(e){let t=(this.queues.get(e)||[]).filter(s=>this.now()-s.at<this.ttlMs);return this.queues.set(e,t),t}take(e,t=bl,s=null){this.lastPoll.set(e,this.now());let o=this.pending(e);if(o.length)return Promise.resolve(o.shift());let r=Math.max(0,Math.min(Number(t)||0,bl));return new Promise(i=>{let a=this.waiters.get(e)||new Set;this.waiters.set(e,a);let l={resolve:null,timer:null};l.resolve=c=>{clearTimeout(l.timer),a.delete(l),this.lastPoll.set(e,this.now()),i(c)},l.timer=setTimeout(()=>l.resolve(null),r),l.timer.unref&&l.timer.unref(),a.add(l),s&&s.addEventListener("abort",()=>{a.has(l)&&l.resolve(null)},{once:!0})})}}});function Kg(n,e={},t=Date.now()){let s=e.session_id;if(!s)return[];let o=new Date(t).toISOString(),r={v:1,sessionId:s,at:o,capture:"hooks"},i={};e.cwd&&(i.cwd=e.cwd),e.transcript_path&&(i.transcriptPath=e.transcript_path),e.session_title&&(i.sessionTitle=e.session_title),e.model&&(i.model=typeof e.model=="string"?e.model:e.model.id||void 0);let a=String(n||e.hook_event_name||"");switch(a){case"SessionStart":{let l={},c=e._checkpoints;return c&&typeof c=="object"&&(c.proc&&typeof c.proc=="object"&&(l.proc={...c.proc,source:"hook"}),c.termEnv&&typeof c.termEnv=="object"&&(l.termEnv=c.termEnv),"backend"in c&&(l.backend=c.backend||null)),[{...r,type:"session.started",data:{...i,source:e.source||"startup",...l}}]}case"UserPromptSubmit":return[{...r,type:"activity",data:{...i,kind:"prompt",prompt:F(e.prompt||"",300)}}];case"PreToolUse":case"PostToolUse":case"PostToolUseFailure":return[{...r,type:"activity",data:{...i,kind:"tool",tool:e.tool_name||null,phase:a==="PreToolUse"?"pre":"post",inputKey:e.tool_name?hi(e.tool_name,e.tool_input):null}}];case"Stop":return[{...r,type:"turn_completed",data:{...i,lastAssistantPreview:F(e.last_assistant_message||"",280),question:vu(e.last_assistant_message||""),...(()=>{let l=up(e.last_assistant_message||"");return l?{listHint:l}:{}})(),backgroundTasks:Array.isArray(e.background_tasks)?e.background_tasks.length:Number(e.background_tasks||0),sessionCrons:Array.isArray(e.session_crons)?e.session_crons.length:Number(e.session_crons||0)}}];case"Notification":return[{...r,type:"notification",data:{...i,notificationType:e.notification_type||null,message:F(e.message||"",200)}}];case"SessionEnd":return[{...r,type:"session.ended",data:{...i,reason:e.reason||"other"}}];default:return[]}}var $g=J(()=>{Te();mi()});import{execFile as Nv}from"node:child_process";import Bo from"node:fs";import Ws from"node:path";function Al(n,e){return new Promise(t=>{Nv("git",["-C",n,...e],{timeout:1500},(s,o)=>t(s?null:String(o).trim()))})}async function Yg(n){if(!n||!Bo.existsSync(n))return null;let e=await Al(n,["rev-parse","--show-toplevel"]);if(!e)return null;let t=await Al(n,["symbolic-ref","--short","-q","HEAD"]),s=Ja(await Al(e,["config","--get","remote.origin.url"]));return{root:e,branch:t||null,name:Ws.basename(e),...s?{github:s}:{}}}async function zg(n){return!n||!Bo.existsSync(n)?null:Al(n,["config","--get","remote.origin.url"])}function Wg(n,e){if(!n)return!1;if((e.ignoreRepos||[]).includes(Ws.basename(n))||(e.ignoreRepos||[]).includes(n))return!0;try{return Bo.existsSync(Ws.join(n,".leftoff-ignore"))||Bo.existsSync(Ws.join(n,".checkpoints-ignore"))}catch{return!1}}function Jg(n){if(!n)return null;let e=Ws.resolve(String(n));try{if(!Bo.statSync(e).isDirectory())return null}catch{return null}for(let t=0;t<64;t++){try{if(Bo.existsSync(Ws.join(e,".git")))return e}catch{}let s=Ws.dirname(e);if(s===e)return null;e=s}return null}var qg=J(()=>{nd()});import Xe from"node:fs";import Mv from"node:os";import yt from"node:path";import Qg from"node:crypto";function Nl(n,{multiline:e=!1}={}){let t=String(n).replace(/\r\n?/g,`
124
- `).replace(Rv,"");return t=e?t.replace(/[\u0000-\u0008\u000B-\u001F\u007F]/g,""):t.replace(/[\u0000-\u001F\u007F]+/g," "),t.replace(/[ \t]+\n/g,`
118
+ `)}var Ev,kl=J(()=>{kg();Ev=bg.resolve(bg.dirname(_v(import.meta.url)),"..","CHANGELOG.md")});var Ke,V,rt,Ag,ci,Ys,Nd,Tg,Ws,Id,_g,Eg,Og,Et,Cg,Ot=J(()=>{Ke="Leftoff",V="leftoff",rt="OneSillyApp/leftoff",Ag=["joelash/checkpoints","joelash/leftoff"],ci="leftoff-hook",Ys="checkpoints-hook",Nd="dev.leftoff.daemon",Tg="dev.checkpoints.daemon",Ws="leftoff.service",Id="checkpoints.service",_g="checkpoints",Eg="leftoff",Og="checkpoints",Et="leftoff.localhost",Cg="checkpoints.localhost"});var Hg={};tn(Hg,{CHECK_EVERY_MS:()=>Rd,DEFAULT_SOURCE:()=>ui,GITHUB_REPO:()=>rt,NPM_PACKAGE:()=>it,PACKAGE_DIRS:()=>di,REPO_DIR:()=>ns,STAMP_FILE:()=>Dd,checkForUpdate:()=>hi,defaultCacheDir:()=>Fd,fetchRemoteFiles:()=>Ug,installInfo:()=>fi,isNpmBuild:()=>Dg,isNpxCacheDir:()=>Ld,lsRemote:()=>Fg,normalizeSource:()=>Go,npmLatest:()=>Pg,npmPackageUrl:()=>vl,npmRegistry:()=>bl,npmUpgradeCommand:()=>jd,packageManagerOf:()=>Rg,readPkg:()=>Lg,readStamp:()=>Ig,registryPath:()=>Mg,renamedSource:()=>Js,runGit:()=>Ct,sshFallback:()=>jg,writeStamp:()=>Pd});import Bo from"node:fs";import Ov from"node:os";import Nn from"node:path";import{execFile as Cv}from"node:child_process";import{fileURLToPath as Nv}from"node:url";function Go(n){let e=String(n||"").trim();if(!e)return ui;let t=e.match(/^(?:github:)?([\w.-]+)\/([\w.-]+?)(?:\.git)?(?:#.*)?$/);return t&&!e.includes("://")&&!e.startsWith("/")&&!e.startsWith(".")?Js("https://github.com/"+t[1]+"/"+t[2]+".git"):Js(e)}function Js(n){let e=String(n||"");for(let t of Ag){let s=t.replace(/[.]/g,"\\."),o=new RegExp("^(https://github\\.com/|git@github\\.com:|ssh://git@github\\.com/)"+s+"(?:\\.git)?/?$","i"),r=e.match(o);if(r)return r[1]+rt+".git"}return e}function Ct(n,{cwd:e,timeoutMs:t=15e3,env:s=process.env}={}){return new Promise(o=>{let r={...s,GIT_TERMINAL_PROMPT:"0",GIT_ASKPASS:s.GIT_ASKPASS||"echo",SSH_ASKPASS:"echo",GIT_SSH_COMMAND:s.GIT_SSH_COMMAND||"ssh -o BatchMode=yes"};Cv(s.CHECKPOINTS_GIT||"git",n,{cwd:e,timeout:t,env:r,maxBuffer:4*1024*1024},(i,a,l)=>{o({code:i?typeof i.code=="number"?i.code:-1:0,stdout:String(a||""),stderr:String(l||(i&&!a?i.message:"")||"")})})})}function Ig(n=ns){try{return JSON.parse(Bo.readFileSync(Nn.join(n,Dd),"utf8"))}catch{return null}}function Pd(n,e=ns){let t={version:n.version||X,sha:n.sha||null,source:n.source||null,at:n.at||new Date().toISOString()};return Bo.writeFileSync(Nn.join(e,Dd),JSON.stringify(t,null,2)+`
119
+ `),t}function Ld(n){return/[\\/]_npx[\\/]/.test(n)||/[\\/]\.npm[\\/]/.test(n)}function Mg(n=it){return n.startsWith("@")?"@"+n.slice(1).split("/").map(encodeURIComponent).join("%2f"):encodeURIComponent(n)}function vl(n=process.env,e=it){return bl(n)+"/"+Mg(e)}function Lg(n=ns){try{return JSON.parse(Bo.readFileSync(Nn.join(n,"package.json"),"utf8"))}catch{return null}}function Dg(n=ns){let e=Lg(n);return!!(e&&e.leftoffBuild==="npm")}function Rg(n,e=process.env){if(e.LEFTOFF_PM)return e.LEFTOFF_PM;let t=String(n||"");return/[\\/]\.?pnpm[\\/]/i.test(t)||/[\\/]pnpm-global[\\/]/i.test(t)?"pnpm":/[\\/]\.bun[\\/]/.test(t)?"bun":"npm"}function jd(n,e=it+"@latest"){let t=process.platform==="win32";return n==="pnpm"?[t?"pnpm.cmd":"pnpm","add","-g",e]:n==="bun"?["bun","add","-g",e]:[t?"npm.cmd":"npm","install","-g",e,"--no-fund","--no-audit","--loglevel=error"]}function bl(n=process.env){return String(n.LEFTOFF_NPM_REGISTRY||n.npm_config_registry||"https://registry.npmjs.org").replace(/\/+$/,"")}async function Pg({env:n=process.env,fetchImpl:e=fetch,timeoutMs:t=15e3}={}){let s=vl(n);try{let o=await e(s,{headers:{accept:"application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8"},signal:AbortSignal.timeout(t)});if(o.status===404)return{ok:!1,version:null,error:it+" isn’t on "+bl(n)+" (404)"};if(!o.ok)return{ok:!1,version:null,error:bl(n)+" answered "+o.status};let r=await o.json(),i=r&&(r["dist-tags"]&&r["dist-tags"].latest||r.version);return i?{ok:!0,version:i,error:null}:{ok:!1,version:null,error:"no version in the registry answer"}}catch(o){return{ok:!1,version:null,error:o.message}}}async function fi({repoDir:n=ns,env:e=process.env,globalRoot:t}={}){let s={version:X,sha:null,kind:"local",source:null,repoDir:n};if(Bo.existsSync(Nn.join(n,".git"))){s.kind="clone";let r=await Ct(["rev-parse","HEAD"],{cwd:n,env:e});r.code===0&&(s.sha=r.stdout.trim());let i=await Ct(["remote","get-url","origin"],{cwd:n,env:e});return s.source=i.code===0?i.stdout.trim():null,s}if(Dg(n))return s.kind="npm",s.source="npm:"+it,s.pm=Rg(n,e),s.npx=Ld(n),s;let o=Ig(n);return o&&(s.sha=o.sha||null,s.source=o.source||null),Ld(n)?s.kind="npx":(t?n.startsWith(t):/[\\/]node_modules[\\/](checkpoints|leftoff|@leftoff[\\/]cli)$/.test(n))&&(s.kind="global"),s}function jg(n){let e=String(n||"").match(/^https:\/\/github\.com\/([\w.-]+)\/([\w.-]+?)(?:\.git)?\/?$/);return e?"git@github.com:"+e[1]+"/"+e[2]+".git":null}async function Ng(n,{env:e,timeoutMs:t}){let s=await Ct(["ls-remote",n,"HEAD"],{env:e,timeoutMs:t}),o=s.code===0?(s.stdout.match(/^([0-9a-f]{40})\s+HEAD$/m)||[])[1]:null;return o?{ok:!0,sha:o,error:null,source:n}:{ok:!1,sha:null,error:Md(s.stderr)||"git ls-remote exited "+s.code,source:n}}async function Fg(n,{env:e=process.env,timeoutMs:t=15e3}={}){let s=Go(n),o=await Ng(s,{env:e,timeoutMs:t});if(o.ok)return o;let r=jg(s);if(!r)return o;let i=await Ng(r,{env:e,timeoutMs:t});return i.ok?i:{ok:!1,sha:null,source:s,error:o.error+" (also tried "+r+": "+i.error+")"}}async function Ug(n,{cacheDir:e=Fd(),env:t=process.env,timeoutMs:s=3e4}={}){try{if(Bo.mkdirSync(e,{recursive:!0}),!Bo.existsSync(Nn.join(e,"HEAD"))){let c=await Ct(["init","--bare","-q",e],{env:t});if(c.code!==0)return{ok:!1,error:Md(c.stderr)}}let o=await Ct(["fetch","-q","--depth=1",Go(n),"HEAD"],{cwd:e,env:t,timeoutMs:s});if(o.code!==0)return{ok:!1,error:Md(o.stderr)||"git fetch exited "+o.code};let r=(await Ct(["rev-parse","FETCH_HEAD"],{cwd:e,env:t})).stdout.trim()||null,i=await Ct(["show","FETCH_HEAD:CHANGELOG.md"],{cwd:e,env:t}),a=await Ct(["show","FETCH_HEAD:package.json"],{cwd:e,env:t}),l=null;try{l=JSON.parse(a.stdout).version||null}catch{}return{ok:!0,sha:r,version:l,changelog:i.code===0?i.stdout:null,error:null}}catch(o){return{ok:!1,error:o.message}}}function Fd(n=process.env){return Nn.join(n.XDG_CACHE_HOME||Nn.join(Ov.homedir(),".cache"),"checkpoints","remote.git")}async function hi({info:n,source:e,env:t=process.env,cacheDir:s,now:o=Date.now(),fetchFiles:r=!0}={}){let i=n||await fi({env:t});if(i.kind==="npm"){let d={checkedAt:new Date(o).toISOString(),source:"npm:"+it,current:{version:i.version,sha:null},remote:{sha:null,version:null},available:!1,incoming:[],error:null},f=await Pg({env:t});return f.ok?(d.remote.version=f.version,d.available=Xe(f.version,i.version)===1,d):(d.error=f.error,d)}let a=Go(e||i.source||ui),l={checkedAt:new Date(o).toISOString(),source:a,current:{version:i.version,sha:i.sha},remote:{sha:null,version:null},available:!1,incoming:[],error:null},c=await Fg(a,{env:t});if(!c.ok)return l.error=c.error,l;if(l.source=c.source,l.remote.sha=c.sha,i.sha&&i.sha===c.sha)return l;if(r){let d=await Ug(l.source,{cacheDir:s||Fd(t),env:t});d.ok?(l.remote.version=d.version,d.changelog&&(l.incoming=Ho(Cd(d.changelog),i.version,d.version).map(f=>({version:f.version,date:f.date,sections:f.sections})))):l.fetchError=d.error}let u=l.remote.version?Xe(l.remote.version,i.version):null;return l.available=u===1||u===0&&!!i.sha||u===null&&!!i.sha,u===-1&&(l.available=!1),l}var ns,ui,Dd,Rd,Md,it,di,pi=J(()=>{Ot();xe();kl();ns=Nn.resolve(Nn.dirname(Nv(import.meta.url)),".."),ui="https://github.com/"+rt+".git",Dd=".install.json",Rd=24*3600*1e3;Md=n=>String(n||"").trim().split(`
120
+ `).filter(Boolean).pop()||"";it="@leftoff/cli",di=[it,"leftoff","checkpoints"]});import Kg from"node:crypto";function zg(){return"rq_"+Kg.randomBytes(9).toString("base64url")}function mi(n,e){let t=s=>Array.isArray(s)?"["+s.map(t).join(",")+"]":s&&typeof s=="object"?"{"+Object.keys(s).filter(o=>o!=="answers").sort().map(o=>JSON.stringify(o)+":"+t(s[o])).join(",")+"}":JSON.stringify(s===void 0?null:s);return Kg.createHash("sha256").update(String(n||"")+`
121
+ `+t(e||{})).digest("hex").slice(0,16)}function Mv(n,e={}){let t=e&&typeof e=="object"?e:{};switch(n){case"Bash":return{verb:"Run",detail:Ze(t.command,400),note:t.description?Ze(t.description,160):null};case"Edit":case"MultiEdit":return{verb:"Edit",detail:Ze(t.file_path,300),note:null};case"Write":return{verb:"Write",detail:Ze(t.file_path,300),note:t.content?Ze(String(t.content).split(`
122
+ `).length+" lines",40):null};case"NotebookEdit":return{verb:"Edit notebook",detail:Ze(t.notebook_path,300),note:null};case"Read":return{verb:"Read",detail:Ze(t.file_path,300),note:null};case"WebFetch":return{verb:"Fetch",detail:Ze(t.url,300),note:null};case"WebSearch":return{verb:"Search the web for",detail:Ze(t.query,300),note:null};default:{let s=Object.entries(t).find(([,o])=>typeof o=="string");return{verb:"Use "+n,detail:s?Ze(s[1],300):"",note:null}}}}function Lv(n={}){return(Array.isArray(n&&n.questions)?n.questions:[]).slice(0,6).map(t=>({question:String(t.question||""),header:t.header?String(t.header):null,kind:t.kind==="text"||t.kind==="number"?t.kind:"choice",multiSelect:!!t.multiSelect,options:(Array.isArray(t.options)?t.options:[]).slice(0,8).map(s=>({label:String(s.label||s),description:s.description?String(s.description):null}))})).filter(t=>t.question)}function Dv(n,e,t={}){let s=Array.isArray(e)?e.filter(r=>r&&(r.type==="addRules"&&Array.isArray(r.rules)&&r.behavior!=="deny"||r.type==="addDirectories"&&Array.isArray(r.directories))):[];return s.length?s.map(r=>r.type==="addRules"?{type:"addRules",rules:r.rules,behavior:"allow",destination:"session"}:{type:"addDirectories",directories:r.directories,destination:"session"}):[{type:"addRules",rules:[n==="Bash"&&t&&typeof t.command=="string"?{toolName:"Bash",ruleContent:t.command}:{toolName:n}],behavior:"allow",destination:"session"}]}function Rv(n,e={}){let t=r=>{let i=new Error(r);throw i.status=400,i.code="bad_answer",i},s=e.action;Bg.includes(s)||t("action is one of "+Bg.join(", "));let o=e.message?Ze(e.message,500):"";if(s==="deny")return{behavior:"deny",message:o||"Denied from the Leftoff board."};if(n.kind==="question"){s!=="answer"&&t('a question takes action "answer" (or "deny")');let r=e.answers&&typeof e.answers=="object"?e.answers:{},i={};for(let a of n.questions){let l=r[a.question];if(Array.isArray(l)&&(l=l.map(String).filter(Boolean)),!(l==null||l===""||Array.isArray(l)&&!l.length))if(a.kind==="choice"&&a.options.length){let c=Array.isArray(l)?l:[String(l)],u=c.filter(d=>!a.options.some(f=>f.label===d));u.length&&!e.allowOther&&t('"'+u[0]+'" isn’t one of the options for "'+a.question+'"'),i[a.question]=a.multiSelect?c.join(", "):c[0]}else i[a.question]=Array.isArray(l)?l.join(", "):String(l)}return Object.keys(i).length||t("no answer given"),{behavior:"allow",updatedInput:{...n.input||{},answers:i}}}return s==="answer"&&t("a permission takes allow, allow_session or deny"),s==="allow_session"?{behavior:"allow",updatedPermissions:Dv(n.tool,n.suggestions,n.input)}:{behavior:"allow"}}function Yg(n){return n?{hookSpecificOutput:{hookEventName:"PermissionRequest",decision:n}}:{}}function gi(n,e={}){if(e.action==="deny")return"Denied"+(e.message?" (“"+Ze(e.message,160)+"”)":"");if(e.action==="allow")return"Allowed once";if(e.action==="allow_session")return"Allowed for this session";if(e.action==="answer"){let t=e.answers&&typeof e.answers=="object"?e.answers:{};return"Answered "+Object.entries(t).map(([s,o])=>"“"+Ze(s,80)+"” → "+Ze(Array.isArray(o)?o.join(", "):o,120)).join("; ")}return e.action==="reply"?"Replied “"+Ze(e.text,200)+"”":String(e.action||"")}function Ud({via:n,ip:e,host:t,who:s,userAgent:o,peer:r,transport:i}={}){if(n==="peer")return"from "+(r||"another machine")+"’s board"+(i?" ("+(i==="relay"?"through the relay":"direct")+")":"");let a=String(o||""),l=/Edg\//.test(a)?"Edge":/Chrome\//.test(a)?"Chrome":/Firefox\//.test(a)?"Firefox":/Safari\//.test(a)?"Safari":a?"a browser":null,c=/iPhone|iPad/.test(a)?"iPhone/iPad":/Android/.test(a)?"Android":null;return n==="remote"?"over Tailscale"+(s?" as "+s:"")+(t?" ("+t+")":"")+(c||l?" · "+[c,l].filter(Boolean).join(" "):""):"on this machine"+(l?" · "+l:"")+(e?" ("+e+")":"")}function Wg({max:n=30,windowMs:e=6e4,now:t=()=>Date.now()}={}){let s=new Map;return{take(o="all"){let r=t(),i=(s.get(o)||[]).filter(a=>r-a<e);return i.length>=n?(s.set(o,i),!1):(i.push(r),s.set(o,i),!0)}}}var Bg,Iv,$g,xl,Gg,Ze,Al,Tl,yi=J(()=>{Bg=["allow","allow_session","deny","answer"],Iv=9.5*60*1e3,$g=45*1e3,xl=25*1e3,Gg=4e3;Ze=(n,e)=>{let t=String(n??"").replace(/\s+/g," ").trim();return t.length>e?t.slice(0,e-1)+"…":t};Al=class{constructor({now:e=()=>Date.now(),timeoutMs:t=Iv,noListenerMs:s=$g,keep:o=200,onChange:r=()=>{}}={}){this.now=e,this.timeoutMs=t,this.noListenerMs=s,this.keep=o,this.onChange=r,this.items=new Map}open({sessionId:e,tool:t,input:s,suggestions:o,capture:r,cwd:i,permissionMode:a}={}){if(!e||typeof e!="string"){let f=new Error("sessionId is required");throw f.status=400,f.code="bad_ask",f}if(!t||typeof t!="string"){let f=new Error("tool is required");throw f.status=400,f.code="bad_ask",f}let l=mi(t,s);for(let f of this.items.values())if(f.state==="waiting"&&f.sessionId===e&&f.key===l)return f.lastPollAt=this.now(),f.captures.add(r||"hooks"),f;let c=this.now(),u=t==="AskUserQuestion"?"question":"permission",d={id:zg(),sessionId:e,kind:u,tool:t,key:l,input:s&&typeof s=="object"?s:{},suggestions:Array.isArray(o)?o:[],captures:new Set([r||"hooks"]),cwd:i||null,permissionMode:a||null,questions:u==="question"?Lv(s):null,describe:u==="permission"?Mv(t,s):null,createdAt:c,expiresAt:c+this.timeoutMs,lastPollAt:c,state:"waiting",reason:null,decision:null,answer:null,answeredAt:null,settledAt:null,by:null,delivered:!1,waiters:new Set};return this.items.set(d.id,d),this.trim(),this.onChange(d,"opened"),d}get(e){return this.items.get(e)||null}waiting({sessionId:e}={}){return this.sweep(),[...this.items.values()].filter(t=>t.state==="waiting"&&(!e||t.sessionId===e)).sort((t,s)=>t.createdAt-s.createdAt)}wait(e,t=xl,s=null){let o=this.items.get(e);if(!o)return Promise.resolve({id:e,state:"gone",reason:"unknown"});if(o.lastPollAt=this.now(),o.state!=="waiting")return Promise.resolve(this.forHook(o,!0));let r=Math.max(0,Math.min(Number(t)||0,xl,o.expiresAt-this.now()));return new Promise(i=>{let a={resolve:null,timer:null};a.resolve=l=>{clearTimeout(a.timer),o.waiters.delete(a),o.lastPollAt=this.now(),i(l)},a.timer=setTimeout(()=>{this.sweep(),a.resolve(this.forHook(o,!1))},r),a.timer.unref&&a.timer.unref(),o.waiters.add(a),s&&s.addEventListener("abort",()=>{o.waiters.has(a)&&(clearTimeout(a.timer),o.waiters.delete(a),i(null))},{once:!0})})}forHook(e,t){return e.state==="answered"&&t&&(e.delivered=!0),{id:e.id,state:e.state,reason:e.reason,...e.state==="answered"?{decision:e.decision}:{}}}listening(e){return e.waiters.size>0||this.now()-e.lastPollAt<this.noListenerMs}answer(e,t={},s={}){let o=this.items.get(e);if(!o){let a=new Error("no such request");throw a.status=404,a.code="not_found",a}if(this.sweep(),o.state!=="waiting"){let a=new Error(o.state==="answered"?"already answered"+(o.by&&o.by.where?" "+o.by.where:""):o.reason==="answered_in_terminal"?"already answered in the terminal":"this request is no longer waiting ("+(o.reason||o.state)+")");throw a.status=409,a.code="not_waiting",a.state=o.state,a}let r=Rv(o,t);o.state="answered",o.decision=r,o.answer={action:t.action,message:t.message?Ze(t.message,500):null,answers:t.answers||null},o.answeredAt=o.settledAt=this.now(),o.by=s;let i=this.forHook(o,!1);if(o.waiters.size){o.delivered=!0;for(let a of[...o.waiters])a.resolve(i)}return this.onChange(o,"answered"),o}delivery(e,t=3e3){let s=this.items.get(e);return s?s.delivered?Promise.resolve(!0):new Promise(o=>{let r=this.now(),i=()=>{if(s.delivered)return o(!0);if(this.now()-r>=t)return o(!1);setTimeout(i,50)};i()}):Promise.resolve(!1)}settle(e,t="answered_in_terminal"){let s=this.items.get(e);if(!s||s.state!=="waiting")return s||null;s.state="gone",s.reason=String(t||"gone").slice(0,40),s.settledAt=this.now();let o=this.forHook(s,!1);for(let r of[...s.waiters])r.resolve(o);return this.onChange(s,"settled"),s}settleSession(e,t,{key:s,tool:o}={}){let r=[];for(let i of this.items.values())i.state!=="waiting"||i.sessionId!==e||s&&i.key!==s||o&&i.tool!==o||r.push(this.settle(i.id,t));return r}sweep(){let e=this.now();for(let t of this.items.values())if(t.state==="waiting")if(e>=t.expiresAt){t.state="expired",t.reason="timeout",t.settledAt=e;let s=this.forHook(t,!1);for(let o of[...t.waiters])o.resolve(s);this.onChange(t,"expired")}else this.listening(t)||this.settle(t.id,"no_listener")}trim(){if(!(this.items.size<=this.keep))for(let e of[...this.items.values()].sort((t,s)=>t.createdAt-s.createdAt)){if(this.items.size<=this.keep)break;e.state!=="waiting"&&this.items.delete(e.id)}}publicAsk(e){return e?{id:e.id,sessionId:e.sessionId,kind:e.kind,tool:e.tool,describe:e.describe,questions:e.questions,canAllowSession:e.kind==="permission",state:e.state,reason:e.reason,createdAt:new Date(e.createdAt).toISOString(),expiresAt:new Date(e.expiresAt).toISOString(),answeredAt:e.answeredAt?new Date(e.answeredAt).toISOString():null,settledAt:e.settledAt?new Date(e.settledAt).toISOString():null,by:e.by?{where:e.by.where||null,via:e.by.via||null}:null,summary:e.answer?gi(e,e.answer):null,capture:[...e.captures].includes("mod")?"mod":"hooks"}:null}},Tl=class n{constructor({now:e=()=>Date.now(),ttlMs:t=600*1e3}={}){this.now=e,this.ttlMs=t,this.queues=new Map,this.waiters=new Map,this.lastPoll=new Map}static check(e){let t=String(e??"").replace(/\r\n?/g,`
123
+ `).trim(),s=o=>{let r=new Error(o);throw r.status=400,r.code="bad_reply",r};return t||s("the reply is empty"),t.startsWith("/")&&s("a reply can’t start with “/” (slash commands aren’t sent from the board)"),t.length>Gg&&s("the reply is over "+Gg+" characters"),t}listening(e){return(this.waiters.get(e)||new Set).size>0||this.now()-(this.lastPoll.get(e)||0)<$g}put(e,t,s={}){let o={id:zg().replace("rq_","rp_"),text:n.check(t),at:this.now(),by:s},r=this.waiters.get(e);if(r&&r.size)return r.values().next().value.resolve(o),{...o,delivered:!0};let i=this.queues.get(e)||[];return i.push(o),this.queues.set(e,i),{...o,delivered:!1}}pending(e){let t=(this.queues.get(e)||[]).filter(s=>this.now()-s.at<this.ttlMs);return this.queues.set(e,t),t}take(e,t=xl,s=null){this.lastPoll.set(e,this.now());let o=this.pending(e);if(o.length)return Promise.resolve(o.shift());let r=Math.max(0,Math.min(Number(t)||0,xl));return new Promise(i=>{let a=this.waiters.get(e)||new Set;this.waiters.set(e,a);let l={resolve:null,timer:null};l.resolve=c=>{clearTimeout(l.timer),a.delete(l),this.lastPoll.set(e,this.now()),i(c)},l.timer=setTimeout(()=>l.resolve(null),r),l.timer.unref&&l.timer.unref(),a.add(l),s&&s.addEventListener("abort",()=>{a.has(l)&&l.resolve(null)},{once:!0})})}}});function Jg(n,e={},t=Date.now()){let s=e.session_id;if(!s)return[];let o=new Date(t).toISOString(),r={v:1,sessionId:s,at:o,capture:"hooks"},i={};e.cwd&&(i.cwd=e.cwd),e.transcript_path&&(i.transcriptPath=e.transcript_path),e.session_title&&(i.sessionTitle=e.session_title),e.model&&(i.model=typeof e.model=="string"?e.model:e.model.id||void 0);let a=String(n||e.hook_event_name||"");switch(a){case"SessionStart":{let l={},c=e._checkpoints;return c&&typeof c=="object"&&(c.proc&&typeof c.proc=="object"&&(l.proc={...c.proc,source:"hook"}),c.termEnv&&typeof c.termEnv=="object"&&(l.termEnv=c.termEnv),"backend"in c&&(l.backend=c.backend||null)),[{...r,type:"session.started",data:{...i,source:e.source||"startup",...l}}]}case"UserPromptSubmit":return[{...r,type:"activity",data:{...i,kind:"prompt",prompt:F(e.prompt||"",300)}}];case"PreToolUse":case"PostToolUse":case"PostToolUseFailure":return[{...r,type:"activity",data:{...i,kind:"tool",tool:e.tool_name||null,phase:a==="PreToolUse"?"pre":"post",inputKey:e.tool_name?mi(e.tool_name,e.tool_input):null}}];case"Stop":return[{...r,type:"turn_completed",data:{...i,lastAssistantPreview:F(e.last_assistant_message||"",280),question:Au(e.last_assistant_message||""),...(()=>{let l=pp(e.last_assistant_message||"");return l?{listHint:l}:{}})(),backgroundTasks:Array.isArray(e.background_tasks)?e.background_tasks.length:Number(e.background_tasks||0),sessionCrons:Array.isArray(e.session_crons)?e.session_crons.length:Number(e.session_crons||0)}}];case"Notification":return[{...r,type:"notification",data:{...i,notificationType:e.notification_type||null,message:F(e.message||"",200)}}];case"CwdChanged":return[{...r,type:"activity",data:{...i,...e.new_cwd?{cwd:e.new_cwd}:{},kind:"cwd"}}];case"SessionEnd":return[{...r,type:"session.ended",data:{...i,reason:e.reason||"other"}}];default:return[]}}var qg=J(()=>{Te();yi()});import{execFile as Pv}from"node:child_process";import qs from"node:fs";import Ne from"node:path";function _l(n,e){return new Promise(t=>{Pv("git",["-C",n,...e],{timeout:1500},(s,o)=>t(s?null:String(o).trim()))})}async function Vg(n){if(!n||!qs.existsSync(n))return null;let e=wi(n),t=e&&e.top||await _l(n,["rev-parse","--show-toplevel"]);if(!t)return null;let s=e&&e.mainRoot||t,o=e&&e.branch||await _l(n,["symbolic-ref","--short","-q","HEAD"]),r=Zr(await _l(t,["config","--get","remote.origin.url"]));return{root:s,branch:o||null,name:jv(s),...r?{github:r}:{},...e&&e.worktree?{worktree:e.worktree.path,worktreeName:e.worktree.name}:{}}}function wi(n,{fsImpl:e=qs}={}){if(!n)return null;let t=Ne.resolve(String(n));for(let s=0;s<64;s++){let o=null;try{o=e.statSync(t)}catch{}if(o&&o.isDirectory())break;let r=Ne.dirname(t);if(r===t)return null;t=r}for(let s=0;s<64;s++){let o=Ne.join(t,".git"),r=null;try{r=e.statSync(o)}catch{}if(r){let a=o,l=o,c=!1;if(r.isFile()){let f="";try{f=e.readFileSync(o,"utf8")}catch{}let h=/^gitdir:\s*(.+?)\s*$/m.exec(f);if(!h)return null;a=Ne.resolve(t,h[1]);try{l=Ne.resolve(a,e.readFileSync(Ne.join(a,"commondir"),"utf8").trim()),c=!0}catch{l=a}}let u=c?Ne.basename(l)===".git"?Ne.dirname(l):l:t,d=null;try{let f=e.readFileSync(Ne.join(a,"HEAD"),"utf8").trim(),h=/^ref:\s*refs\/heads\/(.+)$/.exec(f);d=h?h[1]:null}catch{}return{top:t,mainRoot:u,branch:d,worktree:c&&u!==t?{path:t,name:Fv(t,u)}:null}}let i=Ne.dirname(t);if(i===t)return null;t=i}return null}function Fv(n,e){let t=Ne.relative(e||"",n||"").split(Ne.sep).join("/"),s=/^\.claude\/worktrees\/([^/]+)$/.exec(t);if(s)return s[1];let o=Ne.basename(String(n||"")),r=Ne.basename(String(e||"")).replace(/\.git$/,"");return r&&o.startsWith(r+"-")&&o.length>r.length+1?o.slice(r.length+1):o}function Xg(n){let e=wi(n);return e?e.mainRoot:null}async function Hd(n){return!n||!qs.existsSync(n)?null:_l(n,["config","--get","remote.origin.url"])}function Zg(n,e){if(!n)return!1;if((e.ignoreRepos||[]).includes(Ne.basename(n))||(e.ignoreRepos||[]).includes(n))return!0;try{return qs.existsSync(Ne.join(n,".leftoff-ignore"))||qs.existsSync(Ne.join(n,".checkpoints-ignore"))}catch{return!1}}function Qg(n){if(!n)return null;let e=Ne.resolve(String(n));try{if(!qs.statSync(e).isDirectory())return null}catch{return null}for(let t=0;t<64;t++){try{if(qs.existsSync(Ne.join(e,".git")))return e}catch{}let s=Ne.dirname(e);if(s===e)return null;e=s}return null}var jv,ey=J(()=>{od();jv=n=>Ne.basename(n).replace(/\.git$/,"")||Ne.basename(n)});import Qe from"node:fs";import Uv from"node:os";import St from"node:path";import oy from"node:crypto";function Ll(n,{multiline:e=!1}={}){let t=String(n).replace(/\r\n?/g,`
124
+ `).replace(Gv,"");return t=e?t.replace(/[\u0000-\u0008\u000B-\u001F\u007F]/g,""):t.replace(/[\u0000-\u001F\u007F]+/g," "),t.replace(/[ \t]+\n/g,`
125
125
  `).replace(/\n{3,}/g,`
126
126
 
127
- `).trim()}function ny(n){if(typeof n!="string"||!n||n.length>he.url)return null;let e;try{e=new URL(n)}catch{return null}return e.protocol!=="https:"||!e.hostname||e.username||e.password?null:e.href}function Ud(n){if(typeof n!="string"||!n||n.length>he.url||/[\s\u0000-\u001F\u007F<>"'`\\\u202A-\u202E\u2066-\u2069\u200E\u200F]/.test(n))return null;let e=/^([a-z][a-z0-9+.-]*:)(.*)$/i.exec(n);if(!e)return null;let t=e[1].toLowerCase();if(!Pv.includes(t))return null;if(t==="https:")return ny(n);let s=e[2].replace(/^\/\//,"");if(!s||s.startsWith("?")||s.startsWith("/"))return null;try{new URL(n)}catch{return null}return t+e[2]}function Gd(n){let e;try{e=new URL(String(n||"").trim())}catch{return{ok:!1,message:"not a URL"}}return e.username||e.password?{ok:!1,message:"no user:password@ in the URL; use --key"}:e.searchParams.has("key")?{ok:!1,message:"take key=… out of the URL and pass it with --key (the URL syncs; the key stays on this Mac)"}:e.protocol==="https:"?{ok:!0,url:e.href}:e.protocol==="http:"&&jv.has(e.hostname)?{ok:!0,url:e.href,loopback:!0}:{ok:!1,message:"the feed must be https (plain http only on localhost)"}}function Cn(n,e,t,s){for(let o of Object.keys(n))!e.includes(o)&&!o.startsWith("x-")&&s.add(t,'unknown field "'+o.slice(0,40)+'"')}function Wt(n){return n&&typeof n=="object"&&!Array.isArray(n)}function Ot(n,e,t,s,{required:o=!1,max:r,multiline:i=!1}={}){let a=n[e];if(a==null||a==="")return o&&s.add(t+"."+e,"required"),null;if(typeof a!="string")return s.add(t+"."+e,"must be a string"),null;let l=Nl(a,{multiline:i});return l?l.length>r?(s.add(t+"."+e,"longer than "+r+" characters"),null):l:(o&&s.add(t+"."+e,"required"),null)}function Fd(n,e,t,s){let o=n[e];if(typeof o!="string"&&typeof o!="number")return s.add(t+"."+e,"required"),null;let r=String(o);return r.length>he.id||!Lv.test(r)?(s.add(t+"."+e,"letters, digits and ._:@#/- only, up to "+he.id),null):r}function gi(n,e,t,s,{required:o=!1}={}){let r=n[e];return r==null||r===""?(o&&s.add(t+"."+e,"required"),null):typeof r!="string"||!Dv.test(r)||isNaN(Date.parse(r))?(s.add(t+"."+e,"must be an ISO 8601 time with a zone, like 2026-10-10T15:00:00Z"),null):new Date(Date.parse(r)).toISOString()}function Ko(n,e,t,s,o){let r=n[e];return r==null?[]:Array.isArray(r)?r.length>o?(s.add(t+"."+e,"more than "+o+" entries"),[]):r:(s.add(t+"."+e,"must be a list"),[])}function sy(n,e,t,s,{required:o=!1}={}){let r=n[e];if(r==null||r==="")return o&&s.add(t+"."+e,"required"),null;let i=ny(r);return i||s.add(t+"."+e,"must be an https:// link"),i}function oy(n,e,t){if(!Wt(n))return t.add(e,"must be an object"),null;Cn(n,["id","title","project","status","startedAt","updatedAt","checkpoints","needsYou","lists","links"],e,t);let s={id:Fd(n,"id",e,t),title:Ot(n,"title",e,t,{required:!0,max:he.title}),project:Ot(n,"project",e,t,{max:he.project}),status:null,startedAt:gi(n,"startedAt",e,t,{required:!0}),updatedAt:gi(n,"updatedAt",e,t,{required:!0}),checkpoints:[],needsYou:[],lists:[],links:[]};Vg.includes(n.status)?s.status=n.status:t.add(e+".status","must be one of "+Vg.join(", "));let o=new Set;Ko(n,"checkpoints",e,t,he.checkpoints).forEach((a,l)=>{let c=e+".checkpoints["+l+"]";if(!Wt(a))return t.add(c,"must be an object");Cn(a,["id","at","summary","done","next"],c,t);let u={id:Fd(a,"id",c,t),at:gi(a,"at",c,t,{required:!0}),summary:Ot(a,"summary",c,t,{required:!0,max:he.summary,multiline:!0}),done:Ot(a,"done",c,t,{max:he.summary,multiline:!0}),next:Ot(a,"next",c,t,{max:he.text,multiline:!0})};u.id&&o.has(u.id)&&t.add(c+".id",'duplicate "'+u.id+'"'),o.add(u.id),s.checkpoints.push(u)});let r=new Set;Ko(n,"needsYou",e,t,he.needsYou).forEach((a,l)=>{let c=e+".needsYou["+l+"]";if(!Wt(a))return t.add(c,"must be an object");Cn(a,["id","text","since"],c,t);let u={id:Fd(a,"id",c,t),text:Ot(a,"text",c,t,{required:!0,max:he.text,multiline:!0}),since:gi(a,"since",c,t)};u.id&&r.has(u.id)&&t.add(c+".id",'duplicate "'+u.id+'"'),r.add(u.id),s.needsYou.push(u)});let i=new Set;return Ko(n,"lists",e,t,he.lists).forEach((a,l)=>{let c=e+".lists["+l+"]";if(!Wt(a))return t.add(c,"must be an object");Cn(a,["name","items"],c,t);let u=Ot(a,"name",c,t,{required:!0,max:he.title});u&&i.has(u.toLowerCase())&&t.add(c+".name",'two lists named "'+u+'" in one session'),u&&i.add(u.toLowerCase());let d=[],f=new Set;Array.isArray(a.items)||t.add(c+".items","required (a list)"),Ko(a,"items",c,t,he.items).forEach((h,p)=>{let m=c+".items["+p+"]";if(!Wt(h))return t.add(m,"must be an object");Cn(h,["n","text","done"],m,t),!Number.isInteger(h.n)||h.n<1||h.n>he.n?t.add(m+".n","must be a whole number from 1 (stable: the same item keeps its n)"):f.has(h.n)&&t.add(m+".n","duplicate "+h.n),f.add(h.n),h.done!=null&&typeof h.done!="boolean"&&t.add(m+".done","must be true or false"),d.push({n:h.n,text:Ot(h,"text",m,t,{required:!0,max:he.text}),done:h.done===!0})}),s.lists.push({name:u,items:d})}),Ko(n,"links",e,t,he.links).forEach((a,l)=>{let c=e+".links["+l+"]";if(!Wt(a))return t.add(c,"must be an object");Cn(a,["label","url"],c,t),s.links.push({label:Ot(a,"label",c,t,{required:!0,max:he.label}),url:sy(a,"url",c,t,{required:!0})})}),s}function Fv(n){let e=new Ml;if(!Wt(n))return{ok:!1,errors:["feed: must be a JSON object"],feed:null};Cn(n,["version","agent","generatedAt","sessions"],"feed",e),n.version!==Ol&&e.add("feed.version",'must be "'+Ol+'"');let t=null;if(!Wt(n.agent))e.add("feed.agent","required ({ name, icon, url })");else{Cn(n.agent,["name","icon","url","reply","replyHint"],"feed.agent",e),t={name:Ot(n.agent,"name","feed.agent",e,{required:!0,max:he.agentName}),icon:Ot(n.agent,"icon","feed.agent",e,{max:he.icon}),url:sy(n.agent,"url","feed.agent",e)};let i=n.agent.reply;if(i!=null)if(!Wt(i))e.add("feed.agent.reply","must be an object ({ label, url })");else{Cn(i,["label","url"],"feed.agent.reply",e);let l=Ot(i,"label","feed.agent.reply",e,{required:!0,max:he.replyLabel}),c=null;i.url==null||i.url===""?e.add("feed.agent.reply.url","required"):(c=Ud(i.url))||e.add("feed.agent.reply.url","must be an https:, sms:, imessage:, mailto: or tel: link"),l&&c&&(t.reply={label:l,url:c})}let a=Ot(n.agent,"replyHint","feed.agent",e,{max:he.replyHint});a&&(t.replyHint=a)}let s=gi(n,"generatedAt","feed",e,{required:!0});Array.isArray(n.sessions)||e.add("feed.sessions","required (a list, may be empty)");let o=[],r=new Set;return Ko(n,"sessions","feed",e,he.sessions).forEach((i,a)=>{let l=oy(i,"sessions["+a+"]",e);l&&l.id&&(r.has(l.id)&&e.add("sessions["+a+"].id",'duplicate "'+l.id+'"'),r.add(l.id)),l&&o.push(l)}),e.list.length?{ok:!1,errors:e.list,feed:null}:{ok:!0,errors:[],feed:{version:Ol,agent:t,generatedAt:s,sessions:o}}}function Ll(n,{maxBytes:e=he.bytes}={}){let t=Buffer.isBuffer(n)?n:Buffer.from(String(n??""),"utf8");if(t.length>e)return{ok:!1,code:"too_large",errors:["feed is "+t.length+" bytes; the limit is "+e]};let s;try{s=JSON.parse(t.toString("utf8").replace(/^\uFEFF/,""))}catch(r){return{ok:!1,code:"not_json",errors:["not JSON: "+String(r.message).slice(0,120)]}}let o=Fv(s);return o.ok?{ok:!0,feed:o.feed,bytes:t.length}:{ok:!1,code:"invalid",errors:o.errors}}async function Uv(n,e){let t=Number(n.headers.get("content-length"));if(t&&t>e){try{await n.body?.cancel()}catch{}return{tooLarge:!0,bytes:t}}if(!n.body)return{buf:Buffer.from(await n.arrayBuffer())};let s=[],o=0;for await(let r of n.body){if(o+=r.length,o>e){try{await n.body.cancel()}catch{}return{tooLarge:!0,bytes:o}}s.push(Buffer.from(r))}return{buf:Buffer.concat(s)}}function Hv(n,e){let t=new URL(n);return t.searchParams.set("key",e),t.href}async function Bv({url:n,key:e=null,etag:t=null,auth:s="bearer",fetchImpl:o=globalThis.fetch,timeoutMs:r=15e3,maxBytes:i=he.bytes,userAgent:a="Leftoff"}={}){let l=e&&s==="query"?["query"]:e?["bearer","query"]:["none"],c=null;for(let u of l){let d=await Gv({url:n,key:e,etag:t,mode:u,fetchImpl:o,timeoutMs:r,maxBytes:i,userAgent:a});if(d.result==="error"&&(d.httpStatus===401||d.httpStatus===403)&&u==="bearer"){c=d;continue}return{...d,auth:u==="none"?null:u}}return{...c,auth:"bearer"}}async function Gv({url:n,key:e,etag:t,mode:s,fetchImpl:o,timeoutMs:r,maxBytes:i,userAgent:a}){let l=new URL(n).origin,c=s==="query"?Hv(n,e):n,u={accept:"application/json","user-agent":a};s==="bearer"&&(u.authorization="Bearer "+e),t&&(u["if-none-match"]=t);let d=new AbortController,f=setTimeout(()=>d.abort(),r);try{for(let h=0;h<4;h++){let p;try{p=await o(c,{headers:u,redirect:"manual",signal:d.signal})}catch(S){let k=d.signal.aborted?"no answer in "+Math.round(r/1e3)+" s":S&&S.cause&&(S.cause.code||S.cause.message)||S&&S.message||"network error";return{result:"error",error:{code:"unreachable",message:Tl("can’t reach the feed: "+k,e)}}}if([301,302,303,307,308].includes(p.status)){let S=p.headers.get("location"),k=null;try{k=S&&new URL(S,c)}catch{}if(!k||k.origin!==l)return{result:"error",httpStatus:p.status,error:{code:"redirect",message:"the feed redirects to another site; Leftoff won’t send the key there. Use the final URL."}};s==="query"&&!k.searchParams.has("key")&&k.searchParams.set("key",e),c=k.href;continue}if(p.status===304)return{result:"unchanged",etag:t,httpStatus:304};if(p.status===401||p.status===403)return{result:"error",httpStatus:p.status,error:{code:"unauthorized",message:"the feed refused the key (HTTP "+p.status+")"}};if(p.status===404)return{result:"error",httpStatus:404,error:{code:"not_found",message:"no feed at that URL (HTTP 404)"}};if(p.status<200||p.status>=300)return{result:"error",httpStatus:p.status,error:{code:"http",message:"the feed answered HTTP "+p.status}};let m=await Uv(p,i);if(m.tooLarge)return{result:"error",httpStatus:p.status,error:{code:"too_large",message:"the feed is bigger than "+Math.round(i/1024)+" KB"}};let g=Ll(m.buf,{maxBytes:i});return g.ok?{result:"ok",feed:g.feed,bytes:g.bytes,etag:p.headers.get("etag")||null,httpStatus:p.status}:{result:"error",httpStatus:p.status,error:{code:g.code,message:g.code==="invalid"?"the feed doesn’t match "+Ol+" ("+g.errors.length+" problem"+(g.errors.length===1?"":"s")+")":Tl(g.errors[0],e),details:g.errors.slice(0,10).map(S=>Tl(S,e))}}}return{result:"error",error:{code:"redirect",message:"too many redirects"}}}catch(h){return{result:"error",error:{code:"unreachable",message:Tl("can’t read the feed: "+(h&&h.message||h),e)}}}finally{clearTimeout(f)}}function ry(n=process.env){return yt.join(n.XDG_CONFIG_HOME||yt.join(n.HOME||Mv.homedir(),".config"),"checkpoints","agents")}function Kd(n,e=process.env){return Il(n),yt.join(ry(e),n+".key")}function Cl(n,e=process.env){try{return Xe.readFileSync(Kd(n,e),"utf8").trim()||null}catch{return null}}function Kv(n,e,t=process.env){let s=ry(t);Xe.mkdirSync(s,{recursive:!0,mode:448});try{Xe.chmodSync(s,448)}catch{}let o=Kd(n,t),r=o+"."+process.pid+".tmp";return Xe.writeFileSync(r,String(e).trim()+`
128
- `,{mode:384}),Xe.chmodSync(r,384),Xe.renameSync(r,o),o}function $v(n,e=process.env){try{return Xe.unlinkSync(Kd(n,e)),!0}catch{return!1}}function Il(n){if(typeof n!="string"||!ty.test(n)){let e=new Error('agent name: lowercase letters, digits and -, up to 31 (e.g. "tab")');throw e.code="bad_name",e}return n}function Go(n,...e){let t=yt.resolve(n),s=yt.resolve(t,...e);if(s!==t&&!s.startsWith(t+yt.sep))throw new Error("refusing to write outside "+t);return s}function Yv(n){return(String(n).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,48)||"session")+"-"+Qg.createHash("sha256").update(String(n)).digest("hex").slice(0,8)+".md"}function zv(n){return(String(n).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40)||"list")+"-"+Qg.createHash("sha256").update(String(n)).digest("hex").slice(0,6)}function Wv(n){let e=n.session,t=["---","kind: leftoff-agent-session","schema: 1","agent: "+_l(n.agent),"id: "+_l(e.id),"title: "+_l(e.title)];if(e.project&&t.push("project: "+_l(e.project)),t.push("status: "+e.status,"started_at: "+e.startedAt,"updated_at: "+e.updatedAt,"in_feed: "+(n.inFeed!==!1),"---",""),t.push("# "+e.title.replace(/\n/g," "),""),t.push("From the **"+(n.agentName||n.agent)+"** feed"+(e.project?" · project "+e.project.replace(/\n/g," "):"")+" · "+e.status+(n.inFeed===!1?" · no longer in the feed":"")+". Read-only: Leftoff rewrites this file on each pull.",""),e.needsYou.length){t.push("## Needs you","");for(let o of e.needsYou)t.push(El(o.text+(o.since?" (since "+o.since+")":"")),"")}if(e.lists.length){t.push("## Lists","");for(let o of e.lists){t.push("### "+o.name.replace(/\n/g," "),"");for(let r of o.items)t.push("- ["+(r.done?"x":" ")+"] "+r.n+". "+r.text.replace(/\n/g," "));t.push("")}}if(e.links.length){t.push("## Links","");for(let o of e.links)t.push("- "+o.label.replace(/\n/g," ")+": <"+o.url+">");t.push("")}if(e.checkpoints.length){t.push("## Checkpoints","");for(let o of e.checkpoints.slice().reverse())t.push("### "+o.at,"",El(o.summary)),o.done&&t.push(">",El("Done: "+o.done)),o.next&&t.push(">",El("Next: "+o.next)),t.push("")}let s={schema:1,agent:n.agent,agentName:n.agentName||null,inFeed:n.inFeed!==!1,firstSeenAt:n.firstSeenAt||null,goneAt:n.goneAt||null,pulledAt:n.pulledAt||null,session:e};return t.push("<!-- Leftoff reads the block below. Edits above this line are overwritten on the next pull. -->",Hd,JSON.stringify(s,null,1),"```",""),t.join(`
129
- `)}function Jv(n){let e=String(n||""),t=e.lastIndexOf(`
130
- `+Hd+`
131
- `);if(t<0)return null;let s=e.slice(t+Hd.length+1),o=s.indexOf("\n```");if(o<0)return null;let r;try{r=JSON.parse(s.slice(0,o))}catch{return null}if(!Wt(r)||!Wt(r.session))return null;let i=new Ml,a=oy(r.session,"session",i);return i.list.length||!a?null:{agent:typeof r.agent=="string"?r.agent:null,agentName:typeof r.agentName=="string"?Nl(r.agentName).slice(0,he.agentName):null,inFeed:r.inFeed!==!1,firstSeenAt:r.firstSeenAt||null,goneAt:r.goneAt||null,pulledAt:r.pulledAt||null,session:a}}function qv(n,e){if(!n)return{...e,checkpoints:e.checkpoints.slice().sort((o,r)=>o.at.localeCompare(r.at)).slice(-he.stored)};let t=new Map(n.checkpoints.map(o=>[o.id,o]));for(let o of e.checkpoints)t.set(o.id,o);let s=[...t.values()].sort((o,r)=>o.at.localeCompare(r.at)).slice(-he.stored);return{...e,checkpoints:s}}function Zg(n={}){return{ok:n.ok===!0?!0:n.ok===!1?!1:null,lastAttemptAt:n.lastAttemptAt||null,lastSuccessAt:n.lastSuccessAt||null,lastChangeAt:n.lastChangeAt||null,by:n.by||null,sessions:n.sessions==null?null:n.sessions,bytes:n.bytes||null,generatedAt:n.generatedAt||null,auth:n.auth||null,error:n.lastError?{at:n.lastError.at,code:n.lastError.code,message:n.lastError.message,details:n.lastError.details||null}:null}}function Vv(n){let e={reply:null,replyHint:null};if(!n||typeof n!="object")return e;let t=n.reply;if(t&&typeof t=="object"&&typeof t.label=="string"&&Ud(t.url)){let s=Nl(t.label).slice(0,he.replyLabel);s&&(e.reply={label:s,url:Ud(t.url)})}return typeof n.replyHint=="string"&&(e.replyHint=Nl(n.replyHint).slice(0,he.replyHint)||null),e}function yi(n){let e=n.session.status;return!n.inFeed||e==="done"?"ended":e==="waiting"?"waiting":"active"}function Yd(n,e){return"agents/"+n.name+"/"+(e.project?e.project.toLowerCase().replace(/[\\/]+/g,"-"):n.name)}function Bd(n,e,t){return{id:t.id,sessionId:Dl(n.name,e.session.id),createdAt:t.at,doing:t.done||t.summary,stoppedAt:t.summary,next:t.next||"",machine:n.display,trigger:"agent",summary:t.summary,done:t.done||null}}function ts(n,e,t=Date.now()){if(!e.inFeed||e.session.status==="done"||!e.session.needsYou.length)return null;let s=e.session.needsYou.map(r=>({id:r.id,text:r.text,since:r.since||e.session.updatedAt})),o=s.reduce((r,i)=>es(i.since)<es(r.since)?i:r,s[0]);return{kind:"agent",blocking:!1,agent:n.display,url:n.url||null,reply:n.reply||null,replyHint:n.replyHint||null,items:s,message:s[0].text,since:o.since,waitSec:Math.max(0,Math.round((t-es(o.since))/1e3))}}function zd(n,e,t=Date.now(),{full:s=!1}={}){let o=e.session,r=yi(e),i=o.checkpoints,a=i[i.length-1]||null,l=Yd(n,o),c={id:Dl(n.name,o.id),feedId:o.id,title:o.title,titleSource:"agent",agent:{...$d(n),puller:n.puller,pullsHere:n.pullsHere,ok:n.status.ok,lastSuccessAt:n.status.lastSuccessAt},machine:n.display,isLocal:!1,cwd:null,repo:{name:o.project||n.display,root:l},repoName:o.project||n.display,repoKey:l,repoLabel:o.project||n.display,project:o.project||null,feedStatus:o.status,inFeed:e.inFeed,goneAt:e.goneAt||null,capture:"agent",status:r,startedAt:o.startedAt,lastActivityAt:o.updatedAt,endedAt:r==="ended"?e.goneAt||o.updatedAt:null,turns:0,turnsSinceCheckpoint:0,idleSec:Math.max(0,Math.round((t-es(o.updatedAt))/1e3)),checkpointCount:i.length,latestCheckpoint:a?Bd(n,e,a):null,resumeCommand:null,jump:null,live:null,peer:null,pinned:!1,archived:!1,ignored:!1,empty:!1,ghost:!1,needsYou:ts(n,e,t),lists:o.lists.map(u=>{let d=u.items.filter(f=>f.done).length;return{ref:iy(n.name,o.id,u.name),name:u.name,done:d,total:u.items.length,progress:d+"/"+u.items.length,readOnly:!0}}),links:o.links,file:e.file||null};return s&&(c.checkpoints=i.slice().reverse().map(u=>({...Bd(n,e,u)}))),c}function iy(n,e,t){return"~"+n+"/"+zv(e+"\0"+t)}function Rl(n){let e=[];for(let t of n.agents)for(let s of t.records)if(s.inFeed)for(let o of s.session.lists){let r=o.items.filter(i=>i.done).length;e.push({ref:iy(t.name,s.session.id,o.name),name:o.name,repo:s.session.project||t.display,repoKey:Yd(t,s.session),repoDir:null,items:o.items.map(i=>({n:i.n,text:i.text,done:i.done})),done:r,total:o.items.length,open:o.items.length-r,progress:r+"/"+o.items.length,archived:s.session.status==="done",readOnly:!0,agent:$d(t),sourceSession:Dl(t.name,s.session.id),sourceTitle:s.session.title,sourceMachine:t.display,updated:s.session.updatedAt,created:s.firstSeenAt||s.session.startedAt})}return e}function Pl(n){let e=[];for(let t of n.agents)for(let s of t.records){let o=s.session,r=yi(s);e.push({session:{id:Dl(t.name,o.id),title:o.title,machine:t.display,capture:"agent",repo:{name:o.project||t.display,root:Yd(t,o)},startedAt:o.startedAt,lastActivityAt:o.updatedAt,endedAt:r==="ended"?s.goneAt||o.updatedAt:null,turns:o.checkpoints.length,agent:$d(t),agentStatus:r,agentNeeds:ts(t,s)},checkpoints:o.checkpoints.map(i=>Bd(t,s,i))})}return e}function Wd(n,e,t,{heading:s="##"}={}){let o=[];for(let a of n.agents)for(let l of a.records){let c=l.session,u=ts(a,l);!(es(c.updatedAt)>=e&&es(c.updatedAt)<=t)&&!u||o.push({a,rec:l,n:u})}if(!o.length)return[];o.sort((a,l)=>!!l.n-!!a.n||es(l.rec.session.updatedAt)-es(a.rec.session.updatedAt));let r=(a,l=220)=>{let c=String(a||"").replace(/\s*\n\s*/g," ");return c.length>l?c.slice(0,l-1)+"…":c},i=[s+" From your agents ("+o.length+")"];for(let{a,rec:l,n:c}of o.slice(0,15)){let u=l.session,d=u.checkpoints[u.checkpoints.length-1];i.push("- **"+r(u.title,120)+"** ("+(u.project?r(u.project,60)+" · ":"")+a.display+") — "+(c?"needs you":yi(l)==="ended"?"done":u.status)),c&&i.push(" Needs you: "+c.items.slice(0,3).map(f=>r(f.text,140)).join("; ")+(a.reply?" ("+a.reply.label+": "+a.reply.url+")":a.replyHint?" ("+r(a.replyHint,120)+")":"")),d&&i.push(" Latest: "+r(d.summary)+(d.next?" Next: "+r(d.next,160):""))}return i}var Ol,ey,he,Vg,ty,Lv,Dv,Rv,Pv,jv,Ml,Tl,_l,El,Hd,Xg,Ct,es,$d,Dl,Ze,ns=J(()=>{xn();Ol="leftoff-agent-feed/1",ey=12e4,he={bytes:1024*1024,sessions:200,checkpoints:200,needsYou:50,lists:30,items:300,links:20,id:120,agentName:60,icon:16,title:200,project:120,summary:4e3,text:2e3,label:200,url:2048,replyLabel:40,replyHint:120,n:1e5,stored:500},Vg=["active","waiting","done"],ty=/^[a-z0-9][a-z0-9-]{0,30}$/,Lv=/^[A-Za-z0-9][A-Za-z0-9._:@#\/-]*$/,Dv=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?(Z|[+-]\d{2}:?\d{2})$/,Rv=/[\u202A-\u202E\u2066-\u2069\u200E\u200F]/g;Pv=["https:","sms:","imessage:","mailto:","tel:"];jv=new Set(["127.0.0.1","localhost","[::1]","::1"]);Ml=class{constructor(){this.list=[]}add(e,t){this.list.length<50&&this.list.push(e+": "+t)}};Tl=(n,e)=>e?String(n).split(e).join("…"):String(n);_l=n=>n==null?"":JSON.stringify(String(n)),El=n=>String(n).split(`
127
+ `).trim()}function ay(n){if(typeof n!="string"||!n||n.length>he.url)return null;let e;try{e=new URL(n)}catch{return null}return e.protocol!=="https:"||!e.hostname||e.username||e.password?null:e.href}function Gd(n){if(typeof n!="string"||!n||n.length>he.url||/[\s\u0000-\u001F\u007F<>"'`\\\u202A-\u202E\u2066-\u2069\u200E\u200F]/.test(n))return null;let e=/^([a-z][a-z0-9+.-]*:)(.*)$/i.exec(n);if(!e)return null;let t=e[1].toLowerCase();if(!Kv.includes(t))return null;if(t==="https:")return ay(n);let s=e[2].replace(/^\/\//,"");if(!s||s.startsWith("?")||s.startsWith("/"))return null;try{new URL(n)}catch{return null}return t+e[2]}function zd(n){let e;try{e=new URL(String(n||"").trim())}catch{return{ok:!1,message:"not a URL"}}return e.username||e.password?{ok:!1,message:"no user:password@ in the URL; use --key"}:e.searchParams.has("key")?{ok:!1,message:"take key=… out of the URL and pass it with --key (the URL syncs; the key stays on this Mac)"}:e.protocol==="https:"?{ok:!0,url:e.href}:e.protocol==="http:"&&$v.has(e.hostname)?{ok:!0,url:e.href,loopback:!0}:{ok:!1,message:"the feed must be https (plain http only on localhost)"}}function In(n,e,t,s){for(let o of Object.keys(n))!e.includes(o)&&!o.startsWith("x-")&&s.add(t,'unknown field "'+o.slice(0,40)+'"')}function qt(n){return n&&typeof n=="object"&&!Array.isArray(n)}function Nt(n,e,t,s,{required:o=!1,max:r,multiline:i=!1}={}){let a=n[e];if(a==null||a==="")return o&&s.add(t+"."+e,"required"),null;if(typeof a!="string")return s.add(t+"."+e,"must be a string"),null;let l=Ll(a,{multiline:i});return l?l.length>r?(s.add(t+"."+e,"longer than "+r+" characters"),null):l:(o&&s.add(t+"."+e,"required"),null)}function Bd(n,e,t,s){let o=n[e];if(typeof o!="string"&&typeof o!="number")return s.add(t+"."+e,"required"),null;let r=String(o);return r.length>he.id||!Hv.test(r)?(s.add(t+"."+e,"letters, digits and ._:@#/- only, up to "+he.id),null):r}function Si(n,e,t,s,{required:o=!1}={}){let r=n[e];return r==null||r===""?(o&&s.add(t+"."+e,"required"),null):typeof r!="string"||!Bv.test(r)||isNaN(Date.parse(r))?(s.add(t+"."+e,"must be an ISO 8601 time with a zone, like 2026-10-10T15:00:00Z"),null):new Date(Date.parse(r)).toISOString()}function $o(n,e,t,s,o){let r=n[e];return r==null?[]:Array.isArray(r)?r.length>o?(s.add(t+"."+e,"more than "+o+" entries"),[]):r:(s.add(t+"."+e,"must be a list"),[])}function ly(n,e,t,s,{required:o=!1}={}){let r=n[e];if(r==null||r==="")return o&&s.add(t+"."+e,"required"),null;let i=ay(r);return i||s.add(t+"."+e,"must be an https:// link"),i}function cy(n,e,t){if(!qt(n))return t.add(e,"must be an object"),null;In(n,["id","title","project","status","startedAt","updatedAt","checkpoints","needsYou","lists","links"],e,t);let s={id:Bd(n,"id",e,t),title:Nt(n,"title",e,t,{required:!0,max:he.title}),project:Nt(n,"project",e,t,{max:he.project}),status:null,startedAt:Si(n,"startedAt",e,t,{required:!0}),updatedAt:Si(n,"updatedAt",e,t,{required:!0}),checkpoints:[],needsYou:[],lists:[],links:[]};ty.includes(n.status)?s.status=n.status:t.add(e+".status","must be one of "+ty.join(", "));let o=new Set;$o(n,"checkpoints",e,t,he.checkpoints).forEach((a,l)=>{let c=e+".checkpoints["+l+"]";if(!qt(a))return t.add(c,"must be an object");In(a,["id","at","summary","done","next"],c,t);let u={id:Bd(a,"id",c,t),at:Si(a,"at",c,t,{required:!0}),summary:Nt(a,"summary",c,t,{required:!0,max:he.summary,multiline:!0}),done:Nt(a,"done",c,t,{max:he.summary,multiline:!0}),next:Nt(a,"next",c,t,{max:he.text,multiline:!0})};u.id&&o.has(u.id)&&t.add(c+".id",'duplicate "'+u.id+'"'),o.add(u.id),s.checkpoints.push(u)});let r=new Set;$o(n,"needsYou",e,t,he.needsYou).forEach((a,l)=>{let c=e+".needsYou["+l+"]";if(!qt(a))return t.add(c,"must be an object");In(a,["id","text","since"],c,t);let u={id:Bd(a,"id",c,t),text:Nt(a,"text",c,t,{required:!0,max:he.text,multiline:!0}),since:Si(a,"since",c,t)};u.id&&r.has(u.id)&&t.add(c+".id",'duplicate "'+u.id+'"'),r.add(u.id),s.needsYou.push(u)});let i=new Set;return $o(n,"lists",e,t,he.lists).forEach((a,l)=>{let c=e+".lists["+l+"]";if(!qt(a))return t.add(c,"must be an object");In(a,["name","items"],c,t);let u=Nt(a,"name",c,t,{required:!0,max:he.title});u&&i.has(u.toLowerCase())&&t.add(c+".name",'two lists named "'+u+'" in one session'),u&&i.add(u.toLowerCase());let d=[],f=new Set;Array.isArray(a.items)||t.add(c+".items","required (a list)"),$o(a,"items",c,t,he.items).forEach((h,p)=>{let m=c+".items["+p+"]";if(!qt(h))return t.add(m,"must be an object");In(h,["n","text","done"],m,t),!Number.isInteger(h.n)||h.n<1||h.n>he.n?t.add(m+".n","must be a whole number from 1 (stable: the same item keeps its n)"):f.has(h.n)&&t.add(m+".n","duplicate "+h.n),f.add(h.n),h.done!=null&&typeof h.done!="boolean"&&t.add(m+".done","must be true or false"),d.push({n:h.n,text:Nt(h,"text",m,t,{required:!0,max:he.text}),done:h.done===!0})}),s.lists.push({name:u,items:d})}),$o(n,"links",e,t,he.links).forEach((a,l)=>{let c=e+".links["+l+"]";if(!qt(a))return t.add(c,"must be an object");In(a,["label","url"],c,t),s.links.push({label:Nt(a,"label",c,t,{required:!0,max:he.label}),url:ly(a,"url",c,t,{required:!0})})}),s}function zv(n){let e=new Dl;if(!qt(n))return{ok:!1,errors:["feed: must be a JSON object"],feed:null};In(n,["version","agent","generatedAt","sessions"],"feed",e),n.version!==Nl&&e.add("feed.version",'must be "'+Nl+'"');let t=null;if(!qt(n.agent))e.add("feed.agent","required ({ name, icon, url })");else{In(n.agent,["name","icon","url","reply","replyHint"],"feed.agent",e),t={name:Nt(n.agent,"name","feed.agent",e,{required:!0,max:he.agentName}),icon:Nt(n.agent,"icon","feed.agent",e,{max:he.icon}),url:ly(n.agent,"url","feed.agent",e)};let i=n.agent.reply;if(i!=null)if(!qt(i))e.add("feed.agent.reply","must be an object ({ label, url })");else{In(i,["label","url"],"feed.agent.reply",e);let l=Nt(i,"label","feed.agent.reply",e,{required:!0,max:he.replyLabel}),c=null;i.url==null||i.url===""?e.add("feed.agent.reply.url","required"):(c=Gd(i.url))||e.add("feed.agent.reply.url","must be an https:, sms:, imessage:, mailto: or tel: link"),l&&c&&(t.reply={label:l,url:c})}let a=Nt(n.agent,"replyHint","feed.agent",e,{max:he.replyHint});a&&(t.replyHint=a)}let s=Si(n,"generatedAt","feed",e,{required:!0});Array.isArray(n.sessions)||e.add("feed.sessions","required (a list, may be empty)");let o=[],r=new Set;return $o(n,"sessions","feed",e,he.sessions).forEach((i,a)=>{let l=cy(i,"sessions["+a+"]",e);l&&l.id&&(r.has(l.id)&&e.add("sessions["+a+"].id",'duplicate "'+l.id+'"'),r.add(l.id)),l&&o.push(l)}),e.list.length?{ok:!1,errors:e.list,feed:null}:{ok:!0,errors:[],feed:{version:Nl,agent:t,generatedAt:s,sessions:o}}}function Rl(n,{maxBytes:e=he.bytes}={}){let t=Buffer.isBuffer(n)?n:Buffer.from(String(n??""),"utf8");if(t.length>e)return{ok:!1,code:"too_large",errors:["feed is "+t.length+" bytes; the limit is "+e]};let s;try{s=JSON.parse(t.toString("utf8").replace(/^\uFEFF/,""))}catch(r){return{ok:!1,code:"not_json",errors:["not JSON: "+String(r.message).slice(0,120)]}}let o=zv(s);return o.ok?{ok:!0,feed:o.feed,bytes:t.length}:{ok:!1,code:"invalid",errors:o.errors}}async function Yv(n,e){let t=Number(n.headers.get("content-length"));if(t&&t>e){try{await n.body?.cancel()}catch{}return{tooLarge:!0,bytes:t}}if(!n.body)return{buf:Buffer.from(await n.arrayBuffer())};let s=[],o=0;for await(let r of n.body){if(o+=r.length,o>e){try{await n.body.cancel()}catch{}return{tooLarge:!0,bytes:o}}s.push(Buffer.from(r))}return{buf:Buffer.concat(s)}}function Wv(n,e){let t=new URL(n);return t.searchParams.set("key",e),t.href}async function Jv({url:n,key:e=null,etag:t=null,auth:s="bearer",fetchImpl:o=globalThis.fetch,timeoutMs:r=15e3,maxBytes:i=he.bytes,userAgent:a="Leftoff"}={}){let l=e&&s==="query"?["query"]:e?["bearer","query"]:["none"],c=null;for(let u of l){let d=await qv({url:n,key:e,etag:t,mode:u,fetchImpl:o,timeoutMs:r,maxBytes:i,userAgent:a});if(d.result==="error"&&(d.httpStatus===401||d.httpStatus===403)&&u==="bearer"){c=d;continue}return{...d,auth:u==="none"?null:u}}return{...c,auth:"bearer"}}async function qv({url:n,key:e,etag:t,mode:s,fetchImpl:o,timeoutMs:r,maxBytes:i,userAgent:a}){let l=new URL(n).origin,c=s==="query"?Wv(n,e):n,u={accept:"application/json","user-agent":a};s==="bearer"&&(u.authorization="Bearer "+e),t&&(u["if-none-match"]=t);let d=new AbortController,f=setTimeout(()=>d.abort(),r);try{for(let h=0;h<4;h++){let p;try{p=await o(c,{headers:u,redirect:"manual",signal:d.signal})}catch(S){let k=d.signal.aborted?"no answer in "+Math.round(r/1e3)+" s":S&&S.cause&&(S.cause.code||S.cause.message)||S&&S.message||"network error";return{result:"error",error:{code:"unreachable",message:El("can’t reach the feed: "+k,e)}}}if([301,302,303,307,308].includes(p.status)){let S=p.headers.get("location"),k=null;try{k=S&&new URL(S,c)}catch{}if(!k||k.origin!==l)return{result:"error",httpStatus:p.status,error:{code:"redirect",message:"the feed redirects to another site; Leftoff won’t send the key there. Use the final URL."}};s==="query"&&!k.searchParams.has("key")&&k.searchParams.set("key",e),c=k.href;continue}if(p.status===304)return{result:"unchanged",etag:t,httpStatus:304};if(p.status===401||p.status===403)return{result:"error",httpStatus:p.status,error:{code:"unauthorized",message:"the feed refused the key (HTTP "+p.status+")"}};if(p.status===404)return{result:"error",httpStatus:404,error:{code:"not_found",message:"no feed at that URL (HTTP 404)"}};if(p.status<200||p.status>=300)return{result:"error",httpStatus:p.status,error:{code:"http",message:"the feed answered HTTP "+p.status}};let m=await Yv(p,i);if(m.tooLarge)return{result:"error",httpStatus:p.status,error:{code:"too_large",message:"the feed is bigger than "+Math.round(i/1024)+" KB"}};let g=Rl(m.buf,{maxBytes:i});return g.ok?{result:"ok",feed:g.feed,bytes:g.bytes,etag:p.headers.get("etag")||null,httpStatus:p.status}:{result:"error",httpStatus:p.status,error:{code:g.code,message:g.code==="invalid"?"the feed doesn’t match "+Nl+" ("+g.errors.length+" problem"+(g.errors.length===1?"":"s")+")":El(g.errors[0],e),details:g.errors.slice(0,10).map(S=>El(S,e))}}}return{result:"error",error:{code:"redirect",message:"too many redirects"}}}catch(h){return{result:"error",error:{code:"unreachable",message:El("can’t read the feed: "+(h&&h.message||h),e)}}}finally{clearTimeout(f)}}function uy(n=process.env){return St.join(n.XDG_CONFIG_HOME||St.join(n.HOME||Uv.homedir(),".config"),"checkpoints","agents")}function Yd(n,e=process.env){return Ml(n),St.join(uy(e),n+".key")}function Il(n,e=process.env){try{return Qe.readFileSync(Yd(n,e),"utf8").trim()||null}catch{return null}}function Vv(n,e,t=process.env){let s=uy(t);Qe.mkdirSync(s,{recursive:!0,mode:448});try{Qe.chmodSync(s,448)}catch{}let o=Yd(n,t),r=o+"."+process.pid+".tmp";return Qe.writeFileSync(r,String(e).trim()+`
128
+ `,{mode:384}),Qe.chmodSync(r,384),Qe.renameSync(r,o),o}function Xv(n,e=process.env){try{return Qe.unlinkSync(Yd(n,e)),!0}catch{return!1}}function Ml(n){if(typeof n!="string"||!iy.test(n)){let e=new Error('agent name: lowercase letters, digits and -, up to 31 (e.g. "tab")');throw e.code="bad_name",e}return n}function Ko(n,...e){let t=St.resolve(n),s=St.resolve(t,...e);if(s!==t&&!s.startsWith(t+St.sep))throw new Error("refusing to write outside "+t);return s}function Zv(n){return(String(n).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,48)||"session")+"-"+oy.createHash("sha256").update(String(n)).digest("hex").slice(0,8)+".md"}function Qv(n){return(String(n).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40)||"list")+"-"+oy.createHash("sha256").update(String(n)).digest("hex").slice(0,6)}function e1(n){let e=n.session,t=["---","kind: leftoff-agent-session","schema: 1","agent: "+Ol(n.agent),"id: "+Ol(e.id),"title: "+Ol(e.title)];if(e.project&&t.push("project: "+Ol(e.project)),t.push("status: "+e.status,"started_at: "+e.startedAt,"updated_at: "+e.updatedAt,"in_feed: "+(n.inFeed!==!1),"---",""),t.push("# "+e.title.replace(/\n/g," "),""),t.push("From the **"+(n.agentName||n.agent)+"** feed"+(e.project?" · project "+e.project.replace(/\n/g," "):"")+" · "+e.status+(n.inFeed===!1?" · no longer in the feed":"")+". Read-only: Leftoff rewrites this file on each pull.",""),e.needsYou.length){t.push("## Needs you","");for(let o of e.needsYou)t.push(Cl(o.text+(o.since?" (since "+o.since+")":"")),"")}if(e.lists.length){t.push("## Lists","");for(let o of e.lists){t.push("### "+o.name.replace(/\n/g," "),"");for(let r of o.items)t.push("- ["+(r.done?"x":" ")+"] "+r.n+". "+r.text.replace(/\n/g," "));t.push("")}}if(e.links.length){t.push("## Links","");for(let o of e.links)t.push("- "+o.label.replace(/\n/g," ")+": <"+o.url+">");t.push("")}if(e.checkpoints.length){t.push("## Checkpoints","");for(let o of e.checkpoints.slice().reverse())t.push("### "+o.at,"",Cl(o.summary)),o.done&&t.push(">",Cl("Done: "+o.done)),o.next&&t.push(">",Cl("Next: "+o.next)),t.push("")}let s={schema:1,agent:n.agent,agentName:n.agentName||null,inFeed:n.inFeed!==!1,firstSeenAt:n.firstSeenAt||null,goneAt:n.goneAt||null,pulledAt:n.pulledAt||null,session:e};return t.push("<!-- Leftoff reads the block below. Edits above this line are overwritten on the next pull. -->",Kd,JSON.stringify(s,null,1),"```",""),t.join(`
129
+ `)}function t1(n){let e=String(n||""),t=e.lastIndexOf(`
130
+ `+Kd+`
131
+ `);if(t<0)return null;let s=e.slice(t+Kd.length+1),o=s.indexOf("\n```");if(o<0)return null;let r;try{r=JSON.parse(s.slice(0,o))}catch{return null}if(!qt(r)||!qt(r.session))return null;let i=new Dl,a=cy(r.session,"session",i);return i.list.length||!a?null:{agent:typeof r.agent=="string"?r.agent:null,agentName:typeof r.agentName=="string"?Ll(r.agentName).slice(0,he.agentName):null,inFeed:r.inFeed!==!1,firstSeenAt:r.firstSeenAt||null,goneAt:r.goneAt||null,pulledAt:r.pulledAt||null,session:a}}function n1(n,e){if(!n)return{...e,checkpoints:e.checkpoints.slice().sort((o,r)=>o.at.localeCompare(r.at)).slice(-he.stored)};let t=new Map(n.checkpoints.map(o=>[o.id,o]));for(let o of e.checkpoints)t.set(o.id,o);let s=[...t.values()].sort((o,r)=>o.at.localeCompare(r.at)).slice(-he.stored);return{...e,checkpoints:s}}function sy(n={}){return{ok:n.ok===!0?!0:n.ok===!1?!1:null,lastAttemptAt:n.lastAttemptAt||null,lastSuccessAt:n.lastSuccessAt||null,lastChangeAt:n.lastChangeAt||null,by:n.by||null,sessions:n.sessions==null?null:n.sessions,bytes:n.bytes||null,generatedAt:n.generatedAt||null,auth:n.auth||null,error:n.lastError?{at:n.lastError.at,code:n.lastError.code,message:n.lastError.message,details:n.lastError.details||null}:null}}function s1(n){let e={reply:null,replyHint:null};if(!n||typeof n!="object")return e;let t=n.reply;if(t&&typeof t=="object"&&typeof t.label=="string"&&Gd(t.url)){let s=Ll(t.label).slice(0,he.replyLabel);s&&(e.reply={label:s,url:Gd(t.url)})}return typeof n.replyHint=="string"&&(e.replyHint=Ll(n.replyHint).slice(0,he.replyHint)||null),e}function ki(n){let e=n.session.status;return!n.inFeed||e==="done"?"ended":e==="waiting"?"waiting":"active"}function Jd(n,e){return"agents/"+n.name+"/"+(e.project?e.project.toLowerCase().replace(/[\\/]+/g,"-"):n.name)}function $d(n,e,t){return{id:t.id,sessionId:Pl(n.name,e.session.id),createdAt:t.at,doing:t.done||t.summary,stoppedAt:t.summary,next:t.next||"",machine:n.display,trigger:"agent",summary:t.summary,done:t.done||null}}function os(n,e,t=Date.now()){if(!e.inFeed||e.session.status==="done"||!e.session.needsYou.length)return null;let s=e.session.needsYou.map(r=>({id:r.id,text:r.text,since:r.since||e.session.updatedAt})),o=s.reduce((r,i)=>ss(i.since)<ss(r.since)?i:r,s[0]);return{kind:"agent",blocking:!1,agent:n.display,url:n.url||null,reply:n.reply||null,replyHint:n.replyHint||null,items:s,message:s[0].text,since:o.since,waitSec:Math.max(0,Math.round((t-ss(o.since))/1e3))}}function qd(n,e,t=Date.now(),{full:s=!1}={}){let o=e.session,r=ki(e),i=o.checkpoints,a=i[i.length-1]||null,l=Jd(n,o),c={id:Pl(n.name,o.id),feedId:o.id,title:o.title,titleSource:"agent",agent:{...Wd(n),puller:n.puller,pullsHere:n.pullsHere,ok:n.status.ok,lastSuccessAt:n.status.lastSuccessAt},machine:n.display,isLocal:!1,cwd:null,repo:{name:o.project||n.display,root:l},repoName:o.project||n.display,repoKey:l,repoLabel:o.project||n.display,project:o.project||null,feedStatus:o.status,inFeed:e.inFeed,goneAt:e.goneAt||null,capture:"agent",status:r,startedAt:o.startedAt,lastActivityAt:o.updatedAt,endedAt:r==="ended"?e.goneAt||o.updatedAt:null,turns:0,turnsSinceCheckpoint:0,idleSec:Math.max(0,Math.round((t-ss(o.updatedAt))/1e3)),checkpointCount:i.length,latestCheckpoint:a?$d(n,e,a):null,resumeCommand:null,jump:null,live:null,peer:null,pinned:!1,archived:!1,ignored:!1,empty:!1,ghost:!1,needsYou:os(n,e,t),lists:o.lists.map(u=>{let d=u.items.filter(f=>f.done).length;return{ref:dy(n.name,o.id,u.name),name:u.name,done:d,total:u.items.length,progress:d+"/"+u.items.length,readOnly:!0}}),links:o.links,file:e.file||null};return s&&(c.checkpoints=i.slice().reverse().map(u=>({...$d(n,e,u)}))),c}function dy(n,e,t){return"~"+n+"/"+Qv(e+"\0"+t)}function jl(n){let e=[];for(let t of n.agents)for(let s of t.records)if(s.inFeed)for(let o of s.session.lists){let r=o.items.filter(i=>i.done).length;e.push({ref:dy(t.name,s.session.id,o.name),name:o.name,repo:s.session.project||t.display,repoKey:Jd(t,s.session),repoDir:null,items:o.items.map(i=>({n:i.n,text:i.text,done:i.done})),done:r,total:o.items.length,open:o.items.length-r,progress:r+"/"+o.items.length,archived:s.session.status==="done",readOnly:!0,agent:Wd(t),sourceSession:Pl(t.name,s.session.id),sourceTitle:s.session.title,sourceMachine:t.display,updated:s.session.updatedAt,created:s.firstSeenAt||s.session.startedAt})}return e}function Fl(n){let e=[];for(let t of n.agents)for(let s of t.records){let o=s.session,r=ki(s);e.push({session:{id:Pl(t.name,o.id),title:o.title,machine:t.display,capture:"agent",repo:{name:o.project||t.display,root:Jd(t,o)},startedAt:o.startedAt,lastActivityAt:o.updatedAt,endedAt:r==="ended"?s.goneAt||o.updatedAt:null,turns:o.checkpoints.length,agent:Wd(t),agentStatus:r,agentNeeds:os(t,s)},checkpoints:o.checkpoints.map(i=>$d(t,s,i))})}return e}function Vd(n,e,t,{heading:s="##"}={}){let o=[];for(let a of n.agents)for(let l of a.records){let c=l.session,u=os(a,l);!(ss(c.updatedAt)>=e&&ss(c.updatedAt)<=t)&&!u||o.push({a,rec:l,n:u})}if(!o.length)return[];o.sort((a,l)=>!!l.n-!!a.n||ss(l.rec.session.updatedAt)-ss(a.rec.session.updatedAt));let r=(a,l=220)=>{let c=String(a||"").replace(/\s*\n\s*/g," ");return c.length>l?c.slice(0,l-1)+"…":c},i=[s+" From your agents ("+o.length+")"];for(let{a,rec:l,n:c}of o.slice(0,15)){let u=l.session,d=u.checkpoints[u.checkpoints.length-1];i.push("- **"+r(u.title,120)+"** ("+(u.project?r(u.project,60)+" · ":"")+a.display+") — "+(c?"needs you":ki(l)==="ended"?"done":u.status)),c&&i.push(" Needs you: "+c.items.slice(0,3).map(f=>r(f.text,140)).join("; ")+(a.reply?" ("+a.reply.label+": "+a.reply.url+")":a.replyHint?" ("+r(a.replyHint,120)+")":"")),d&&i.push(" Latest: "+r(d.summary)+(d.next?" Next: "+r(d.next,160):""))}return i}var Nl,ry,he,ty,iy,Hv,Bv,Gv,Kv,$v,Dl,El,Ol,Cl,Kd,ny,It,ss,Wd,Pl,et,rs=J(()=>{Tn();Nl="leftoff-agent-feed/1",ry=12e4,he={bytes:1024*1024,sessions:200,checkpoints:200,needsYou:50,lists:30,items:300,links:20,id:120,agentName:60,icon:16,title:200,project:120,summary:4e3,text:2e3,label:200,url:2048,replyLabel:40,replyHint:120,n:1e5,stored:500},ty=["active","waiting","done"],iy=/^[a-z0-9][a-z0-9-]{0,30}$/,Hv=/^[A-Za-z0-9][A-Za-z0-9._:@#\/-]*$/,Bv=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?(Z|[+-]\d{2}:?\d{2})$/,Gv=/[\u202A-\u202E\u2066-\u2069\u200E\u200F]/g;Kv=["https:","sms:","imessage:","mailto:","tel:"];$v=new Set(["127.0.0.1","localhost","[::1]","::1"]);Dl=class{constructor(){this.list=[]}add(e,t){this.list.length<50&&this.list.push(e+": "+t)}};El=(n,e)=>e?String(n).split(e).join("…"):String(n);Ol=n=>n==null?"":JSON.stringify(String(n)),Cl=n=>String(n).split(`
132
132
  `).map(e=>"> "+e).join(`
133
- `),Hd="```json leftoff-agent-session";Xg=n=>{try{return JSON.parse(Xe.readFileSync(n,"utf8"))}catch{return null}},Ct=class{constructor(e,t,{now:s=()=>Date.now(),env:o=process.env,log:r=()=>{},fetchImpl:i=null,userAgent:a="Leftoff"}={}){this.root=e,this.machine=t,this.now=s,this.env=o,this.log=r,this.fetchImpl=i,this.userAgent=a,this.cache=null,this.inflight=new Map}get dir(){return yt.join(this.root,"agents")}agentDir(e){return Go(this.dir,Il(e))}sources(){let e=[];try{e=Xe.readdirSync(this.dir,{withFileTypes:!0}).filter(s=>s.isDirectory()&&ty.test(s.name)).map(s=>s.name).sort()}catch{}let t=[];for(let s of e){let o=Xg(yt.join(this.dir,s,"source.json"));o&&o.name===s&&typeof o.url=="string"&&t.push(o)}return t}source(e){return Il(e),this.sources().find(t=>t.name===e)||null}status(e){return Xg(yt.join(this.agentDir(e),"status.json"))||{}}writeStatus(e,t){Vn(Go(this.agentDir(e),"status.json"),JSON.stringify(t,null,2)+`
134
- `),this.cache=null}add({name:e,url:t,key:s}){Il(e);let o=Gd(t);if(!o.ok){let l=new Error(o.message);throw l.code="bad_url",l}if(s!=null&&typeof s!="string")throw Object.assign(new Error("key must be text"),{code:"bad_key"});let r=s?String(s).trim():"";if(r&&(r.length>4096||/[\s\u0000-\u001F]/.test(r)))throw Object.assign(new Error("the key has spaces or control characters in it"),{code:"bad_key"});let i=this.source(e),a={name:e,url:o.url,addedAt:i?i.addedAt:new Date(this.now()).toISOString(),addedOn:i?i.addedOn:this.machine,puller:this.machine,updatedAt:new Date(this.now()).toISOString()};if(Vn(Go(this.agentDir(e),"source.json"),JSON.stringify(a,null,2)+`
135
- `),r&&Kv(e,r,this.env),i&&i.url!==a.url){let l=this.status(e);delete l.etag,delete l.auth,this.writeStatus(e,l)}return this.cache=null,{source:a,replaced:!!i,previousPuller:i&&i.puller!==this.machine?i.puller:null,hasKey:!!Cl(e,this.env)}}remove(e,{purge:t=!1}={}){let s=this.agentDir(e);if(!Xe.existsSync(s)){let i=new Error("no agent feed named "+e);throw i.code="not_found",i}let o=null;if(t)Xe.rmSync(s,{recursive:!0,force:!0});else{let i=new Date(this.now()).toISOString().replace(/[:.]/g,"-"),a=Go(yt.join(this.dir,".removed"),e+"-"+i);Xe.mkdirSync(yt.dirname(a),{recursive:!0}),Xe.renameSync(s,a),o=yt.relative(this.root,a)}let r=$v(e,this.env);return this.cache=null,{removed:e,movedTo:o,purged:!!t,keyDeleted:r}}hasKey(e){return!!Cl(e,this.env)}pullsHere(e){return e&&e.puller===this.machine&&this.hasKey(e.name)}loadRecords(e){let t=yt.join(this.agentDir(e),"sessions"),s=[],o=[];try{o=Xe.readdirSync(t).filter(r=>r.endsWith(".md")&&!r.startsWith("."))}catch{}for(let r of o){let i=null;try{i=Jv(Xe.readFileSync(yt.join(t,r),"utf8"))}catch{}i&&(i.file="agents/"+e+"/sessions/"+r,s.push(i))}return s}apply(e,t){let s=this.agentDir(e),o=Go(s,"sessions"),r=new Date(this.now()).toISOString(),i=new Map(this.loadRecords(e).map(h=>[h.session.id,h])),a=0,l=0,c=0,u=t.agent&&t.agent.name||e,d=h=>{let p=Go(o,Yv(h.session.id)),m=Wv(h),g=null;try{g=Xe.readFileSync(p,"utf8")}catch{}g!==m&&(Vn(p,m),a++)},f=new Set;for(let h of t.sessions){f.add(h.id);let p=i.get(h.id);p||l++;let m=qv(p&&p.session,h),g=p&&p.inFeed&&JSON.stringify(p.session)===JSON.stringify(m)&&p.agentName===u;d({agent:e,agentName:u,inFeed:!0,firstSeenAt:p&&p.firstSeenAt||r,goneAt:null,pulledAt:g?p.pulledAt:r,session:m})}for(let[h,p]of i)f.has(h)||!p.inFeed||(c++,d({...p,inFeed:!1,goneAt:r,session:{...p.session,needsYou:[]}}));return this.cache=null,{sessions:t.sessions.length,written:a,added:l,gone:c}}pull(e,{force:t=!1}={}){if(this.inflight.has(e))return this.inflight.get(e);let s=this.pullNow(e,{force:t}).finally(()=>this.inflight.delete(e));return this.inflight.set(e,s),s}async pullNow(e,{force:t=!1}={}){let s=this.source(e);if(!s){let u=new Error("no agent feed named "+e);throw u.code="not_found",u}if(s.puller!==this.machine){let u=new Error(e+" is pulled by "+s.puller+", not this Mac. Add it here with its key to pull from this Mac instead.");throw u.code="not_puller",u.puller=s.puller,u}let o=Cl(e,this.env),r=this.status(e),i=new Date(this.now()).toISOString(),a=await Bv({url:s.url,key:o,etag:t?null:r.etag,auth:r.auth||"bearer",fetchImpl:this.fetchImpl||globalThis.fetch,userAgent:this.userAgent}),l={...r,lastAttemptAt:i,by:this.machine},c=null;return a.result==="ok"?(c=this.apply(e,a.feed),Object.assign(l,{lastSuccessAt:i,lastChangeAt:i,lastError:null,etag:a.etag||null,auth:a.auth,agent:a.feed.agent,generatedAt:a.feed.generatedAt,sessions:a.feed.sessions.length,bytes:a.bytes,ok:!0}),this.log("agents: pulled "+e+" ("+a.feed.sessions.length+" session"+(a.feed.sessions.length===1?"":"s")+", "+c.written+" file"+(c.written===1?"":"s")+" written)")):a.result==="unchanged"?Object.assign(l,{lastSuccessAt:i,lastError:null,ok:!0,unchangedAt:i}):(l.ok=!1,l.lastError={at:i,code:a.error.code,message:a.error.message,details:a.error.details||null,httpStatus:a.httpStatus||null},this.log("agents: "+e+": "+a.error.message)),this.writeStatus(e,l),{name:e,result:a.result,applied:c,status:Zg(l),error:l.lastError||null}}view({fresh:e=!1}={}){if(!e&&this.cache&&this.now()-this.cache.at<3e3)return this.cache.v;let t=[];for(let o of this.sources()){let r=this.status(o.name),i=this.loadRecords(o.name),a=r.agent&&r.agent.name||i[0]&&i[0].agentName||o.name;t.push({name:o.name,display:a,icon:r.agent&&r.agent.icon||null,url:r.agent&&r.agent.url||null,...Vv(r.agent),source:o,puller:o.puller,pullsHere:this.pullsHere(o),hasKey:this.hasKey(o.name),status:Zg(r),records:i})}let s={agents:t};return this.cache={at:this.now(),v:s},s}};es=n=>{let e=Date.parse(n||"");return isNaN(e)?0:e};$d=n=>({name:n.name,display:n.display,icon:n.icon||null,url:n.url||null,reply:n.reply||null,replyHint:n.replyHint||null}),Dl=(n,e)=>"agent:"+n+":"+e,Ze=n=>typeof n=="string"&&n.startsWith("agent:")});function Xv(n=Date.now()){let e=new Date(n);return e.getFullYear()+"-"+String(e.getMonth()+1).padStart(2,"0")+"-"+String(e.getDate()).padStart(2,"0")}function Zv(n){let e=Math.round(n/6e4);if(e<60)return e+"m";let t=Math.round(e/60);return t<48?t+"h":Math.round(t/24)+"d"}function ay(n){return new Date(n).toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"})}function Jd(n,e,t=ke,s,o){let r={needsYou:[],active:[],dormant:[],finished:[]};for(let{session:a,checkpoints:l}of n){if(a.archived||ft(a,l.length))continue;let c=l.slice().sort((p,m)=>R(m.createdAt)-R(p.createdAt))[0]||null,u=o&&a.machine&&a.machine!==o,d=bt(a,e,u?{...t,staleAfterSec:Math.max(t.staleAfterSec,900)}:t),f=Math.max(R(a.lastActivityAt)||0,R(a.endedAt)||0,c?R(c.createdAt):0,R(a.startedAt)||0);if(d==="ended"&&f<s||d!=="ended"&&f<s&&!c)continue;let h={session:a,latest:c,status:d};d==="needs_input"||c&&/^YOU:/i.test(c.next||"")&&d!=="ended"?r.needsYou.push(h):d==="ended"?r.finished.push(h):d==="dormant"||d==="stale"?r.dormant.push(h):r.active.push(h)}let i=(a,l)=>(R(l.session.lastActivityAt)||0)-(R(a.session.lastActivityAt)||0);for(let a of Object.values(r))a.sort(i);return r}function e1(n,e,t,s,o){let r=n.session,i=n.latest,a=r.repo&&r.repo.branch,l=(o&&o.get($o(r))||me(r))+(a?" · "+a:"")+(s&&r.machine?" · "+r.machine:""),c;t==="needsYou"?c=r.needsInput?"waiting on "+String(r.lastNotification||"input").replace(/_/g," ")+" since "+ay(r.lastEventAt||r.lastActivityAt):"blocked on you":t==="finished"?c="closed "+ay(r.endedAt):t==="active"?c=n.status:c="idle "+Zv(e-(R(r.lastActivityAt)||e));let u=["- **"+(r.title||"Untitled session")+"** ("+l+") — "+c];return i?t==="dormant"||t==="finished"?u.push(" Stopped at: "+F(i.stoppedAt,220)+" Next: "+F(i.next,200)):u.push(" Next: "+F(i.next,220)):r.lastPreview&&u.push(" Last reply: "+F(r.lastPreview,200)),u}function t1(n){let e=n.needsYou[0]||n.dormant[0]||n.active[0];if(!e)return"Nothing open. Clean slate.";let t=e.session.title||"your most recent session",s=n.needsYou[0]?"it is blocked on you":n.dormant[0]?"it went quiet mid-task":"it is still running",o="Start with “"+t+"”: "+s+(e.latest?" (next: "+F(e.latest.next.replace(/^YOU:\s*/i,""),120).replace(/[.!?…]+$/,"")+").":"."),r=n.needsYou.length+n.dormant.length-1;return r>0&&(o+=" "+r+" more session"+(r===1?"":"s")+" waiting after that."),o}function n1(n,e,t){let s=[],o=0,r=(n||[]).filter(a=>!a.archived),i=[];for(let a of r){let l=(a.items||[]).filter(c=>c.done&&c.at&&R(c.at)>=e&&R(c.at)<=t);!l.length&&!(a.total>a.done)||(o+=l.length,i.push({l:a,got:l}))}if(!i.length)return s;i.sort((a,l)=>l.got.length-a.got.length),s.push("## Lists"+(o?" ("+o+" item"+(o===1?"":"s")+" done)":""));for(let{l:a,got:l}of i.slice(0,12)){let c=(a.items||[]).filter(u=>!u.done);s.push("- **"+F(a.name,80)+"** ("+(a.repo||"no repo")+"): "+a.done+"/"+a.total+" done"+(l.length?". Done: "+l.slice(0,4).map(u=>F(u.text,80)).join("; ")+(l.length>4?" +"+(l.length-4):""):"")+(c.length?". Next: "+F(c[0].text,100):". All done."))}return s.ticked=o,s}function qd(n,e){return qn(n||Cs(),e,{repoKey:$o(e),repoName:me(e)}).space}async function jl(n,{now:e=Date.now(),cfg:t=ke,date:s,summarizer:o,machine:r,todos:i=[],lists:a=[],agents:l=null,spaces:c=null,agentSpace:u=null}={}){let d=(t.digest&&t.digest.lookbackHours||24)*36e5,f=e-d,h=Jd(n,e,t,f,r),p=[...new Set(n.map(U=>U.session.machine).filter(Boolean))].sort(),m=s||Xv(e),g=new Date(m+"T12:00:00").toLocaleDateString("en-US",{weekday:"short",month:"short",day:"numeric"}).replace(",",""),S=h.needsYou.length+h.active.length+h.dormant.length+h.finished.length,k={date:m,generatedAt:G(e),window:{from:G(f),to:G(e)},sessions:S,needsYou:h.needsYou.map(U=>U.session.id),path:"digests/"+m+".md",machine:r||null,shared:!0,machines:p},w=Kt(Object.values(h).flat().map(U=>$o(U.session))),x=new Map,v=U=>(x.has(U)||x.set(U,{needsYou:[],active:[],dormant:[],finished:[],agents:[]}),x.get(U));for(let[U,E]of Object.entries(h))for(let P of E)v(qd(c,P.session))[U].push(P);let b=new Map;if(l&&l.agents)for(let U of l.agents){let E=u?u(U):qn(c||Cs(),{id:"agent:"+U.name+":x",agent:{name:U.name,puller:U.puller}}).space;Wd({agents:[U]},f,e,{heading:"###"}).length&&(v(E),b.set(E,[...b.get(E)||[],U]))}let y=Ao([...x.keys()]);k.spaces=y;let _=[];_.push("# Where you left off — "+g,"");let A=0;for(let U of y){let E=x.get(U),P=E.needsYou.length+E.active.length+E.dormant.length+E.finished.length,oe=new Set(Object.values(E).flat().map(Ie=>Ie.session&&Ie.session.machine).filter(Boolean)).size>1;_.push("## "+U,"");let K=(Ie,Se,q)=>{if(Se.length){_.push("### "+Ie+" ("+Se.length+")");for(let re of Se)_.push(...e1(re,e,q,oe,w));_.push("")}};K("Needs you",E.needsYou,"needsYou"),K("Still running",E.active,"active"),K("Dormant, ready to pick up",E.dormant,"dormant"),K("Finished or closed",E.finished,"finished");let pe=b.get(U);if(pe){let Ie=Wd({agents:pe},f,e,{heading:"###"});A+=Ie.filter(Se=>Se.startsWith("- ")).length,_.push(...Ie,"")}else P||_.push("_Nothing here in the last "+Math.round(d/36e5)+" hours._","")}S||_.push("_No Claude Code sessions in the last "+Math.round(d/36e5)+" hours._",""),k.agentSessions=A;let O=Km(i,e);O.length&&_.push(...O,""),k.todosOpen=(i||[]).length;let I=n1(a,f,e);I.length&&_.push(...I,""),k.listsTicked=I.ticked||0;let N=null,M=null;if(S&&(!t.digest||t.digest.topOfMind!==!1)){if(o){let U=[...h.needsYou,...h.dormant,...h.active].slice(0,15).map(E=>"- "+(E.session.title||"Untitled")+" ["+E.status+"]"+(E.latest?" Doing: "+E.latest.doing+" Stopped at: "+E.latest.stoppedAt+" Next: "+E.latest.next:""));N=await o.paragraph(Qv,U.join(`
136
- `)),N&&(M="model")}N||(N=t1(h),M="heuristic"),_.push("## Top of mind","",N,"")}k.topOfMindBy=M;let z=As({date:m,machine:r||null,shared:!0,machines:p,spaces:y,generated_at:k.generatedAt,window_from:k.window.from,window_to:k.window.to,sessions:S,needs_you:k.needsYou,schema:2});return{meta:k,markdown:z+_.join(`
133
+ `),Kd="```json leftoff-agent-session";ny=n=>{try{return JSON.parse(Qe.readFileSync(n,"utf8"))}catch{return null}},It=class{constructor(e,t,{now:s=()=>Date.now(),env:o=process.env,log:r=()=>{},fetchImpl:i=null,userAgent:a="Leftoff"}={}){this.root=e,this.machine=t,this.now=s,this.env=o,this.log=r,this.fetchImpl=i,this.userAgent=a,this.cache=null,this.inflight=new Map}get dir(){return St.join(this.root,"agents")}agentDir(e){return Ko(this.dir,Ml(e))}sources(){let e=[];try{e=Qe.readdirSync(this.dir,{withFileTypes:!0}).filter(s=>s.isDirectory()&&iy.test(s.name)).map(s=>s.name).sort()}catch{}let t=[];for(let s of e){let o=ny(St.join(this.dir,s,"source.json"));o&&o.name===s&&typeof o.url=="string"&&t.push(o)}return t}source(e){return Ml(e),this.sources().find(t=>t.name===e)||null}status(e){return ny(St.join(this.agentDir(e),"status.json"))||{}}writeStatus(e,t){Zn(Ko(this.agentDir(e),"status.json"),JSON.stringify(t,null,2)+`
134
+ `),this.cache=null}add({name:e,url:t,key:s}){Ml(e);let o=zd(t);if(!o.ok){let l=new Error(o.message);throw l.code="bad_url",l}if(s!=null&&typeof s!="string")throw Object.assign(new Error("key must be text"),{code:"bad_key"});let r=s?String(s).trim():"";if(r&&(r.length>4096||/[\s\u0000-\u001F]/.test(r)))throw Object.assign(new Error("the key has spaces or control characters in it"),{code:"bad_key"});let i=this.source(e),a={name:e,url:o.url,addedAt:i?i.addedAt:new Date(this.now()).toISOString(),addedOn:i?i.addedOn:this.machine,puller:this.machine,updatedAt:new Date(this.now()).toISOString()};if(Zn(Ko(this.agentDir(e),"source.json"),JSON.stringify(a,null,2)+`
135
+ `),r&&Vv(e,r,this.env),i&&i.url!==a.url){let l=this.status(e);delete l.etag,delete l.auth,this.writeStatus(e,l)}return this.cache=null,{source:a,replaced:!!i,previousPuller:i&&i.puller!==this.machine?i.puller:null,hasKey:!!Il(e,this.env)}}remove(e,{purge:t=!1}={}){let s=this.agentDir(e);if(!Qe.existsSync(s)){let i=new Error("no agent feed named "+e);throw i.code="not_found",i}let o=null;if(t)Qe.rmSync(s,{recursive:!0,force:!0});else{let i=new Date(this.now()).toISOString().replace(/[:.]/g,"-"),a=Ko(St.join(this.dir,".removed"),e+"-"+i);Qe.mkdirSync(St.dirname(a),{recursive:!0}),Qe.renameSync(s,a),o=St.relative(this.root,a)}let r=Xv(e,this.env);return this.cache=null,{removed:e,movedTo:o,purged:!!t,keyDeleted:r}}hasKey(e){return!!Il(e,this.env)}pullsHere(e){return e&&e.puller===this.machine&&this.hasKey(e.name)}loadRecords(e){let t=St.join(this.agentDir(e),"sessions"),s=[],o=[];try{o=Qe.readdirSync(t).filter(r=>r.endsWith(".md")&&!r.startsWith("."))}catch{}for(let r of o){let i=null;try{i=t1(Qe.readFileSync(St.join(t,r),"utf8"))}catch{}i&&(i.file="agents/"+e+"/sessions/"+r,s.push(i))}return s}apply(e,t){let s=this.agentDir(e),o=Ko(s,"sessions"),r=new Date(this.now()).toISOString(),i=new Map(this.loadRecords(e).map(h=>[h.session.id,h])),a=0,l=0,c=0,u=t.agent&&t.agent.name||e,d=h=>{let p=Ko(o,Zv(h.session.id)),m=e1(h),g=null;try{g=Qe.readFileSync(p,"utf8")}catch{}g!==m&&(Zn(p,m),a++)},f=new Set;for(let h of t.sessions){f.add(h.id);let p=i.get(h.id);p||l++;let m=n1(p&&p.session,h),g=p&&p.inFeed&&JSON.stringify(p.session)===JSON.stringify(m)&&p.agentName===u;d({agent:e,agentName:u,inFeed:!0,firstSeenAt:p&&p.firstSeenAt||r,goneAt:null,pulledAt:g?p.pulledAt:r,session:m})}for(let[h,p]of i)f.has(h)||!p.inFeed||(c++,d({...p,inFeed:!1,goneAt:r,session:{...p.session,needsYou:[]}}));return this.cache=null,{sessions:t.sessions.length,written:a,added:l,gone:c}}pull(e,{force:t=!1}={}){if(this.inflight.has(e))return this.inflight.get(e);let s=this.pullNow(e,{force:t}).finally(()=>this.inflight.delete(e));return this.inflight.set(e,s),s}async pullNow(e,{force:t=!1}={}){let s=this.source(e);if(!s){let u=new Error("no agent feed named "+e);throw u.code="not_found",u}if(s.puller!==this.machine){let u=new Error(e+" is pulled by "+s.puller+", not this Mac. Add it here with its key to pull from this Mac instead.");throw u.code="not_puller",u.puller=s.puller,u}let o=Il(e,this.env),r=this.status(e),i=new Date(this.now()).toISOString(),a=await Jv({url:s.url,key:o,etag:t?null:r.etag,auth:r.auth||"bearer",fetchImpl:this.fetchImpl||globalThis.fetch,userAgent:this.userAgent}),l={...r,lastAttemptAt:i,by:this.machine},c=null;return a.result==="ok"?(c=this.apply(e,a.feed),Object.assign(l,{lastSuccessAt:i,lastChangeAt:i,lastError:null,etag:a.etag||null,auth:a.auth,agent:a.feed.agent,generatedAt:a.feed.generatedAt,sessions:a.feed.sessions.length,bytes:a.bytes,ok:!0}),this.log("agents: pulled "+e+" ("+a.feed.sessions.length+" session"+(a.feed.sessions.length===1?"":"s")+", "+c.written+" file"+(c.written===1?"":"s")+" written)")):a.result==="unchanged"?Object.assign(l,{lastSuccessAt:i,lastError:null,ok:!0,unchangedAt:i}):(l.ok=!1,l.lastError={at:i,code:a.error.code,message:a.error.message,details:a.error.details||null,httpStatus:a.httpStatus||null},this.log("agents: "+e+": "+a.error.message)),this.writeStatus(e,l),{name:e,result:a.result,applied:c,status:sy(l),error:l.lastError||null}}view({fresh:e=!1}={}){if(!e&&this.cache&&this.now()-this.cache.at<3e3)return this.cache.v;let t=[];for(let o of this.sources()){let r=this.status(o.name),i=this.loadRecords(o.name),a=r.agent&&r.agent.name||i[0]&&i[0].agentName||o.name;t.push({name:o.name,display:a,icon:r.agent&&r.agent.icon||null,url:r.agent&&r.agent.url||null,...s1(r.agent),source:o,puller:o.puller,pullsHere:this.pullsHere(o),hasKey:this.hasKey(o.name),status:sy(r),records:i})}let s={agents:t};return this.cache={at:this.now(),v:s},s}};ss=n=>{let e=Date.parse(n||"");return isNaN(e)?0:e};Wd=n=>({name:n.name,display:n.display,icon:n.icon||null,url:n.url||null,reply:n.reply||null,replyHint:n.replyHint||null}),Pl=(n,e)=>"agent:"+n+":"+e,et=n=>typeof n=="string"&&n.startsWith("agent:")});function o1(n=Date.now()){let e=new Date(n);return e.getFullYear()+"-"+String(e.getMonth()+1).padStart(2,"0")+"-"+String(e.getDate()).padStart(2,"0")}function r1(n){let e=Math.round(n/6e4);if(e<60)return e+"m";let t=Math.round(e/60);return t<48?t+"h":Math.round(t/24)+"d"}function fy(n){return new Date(n).toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"})}function Xd(n,e,t=ke,s,o){let r={needsYou:[],active:[],dormant:[],finished:[]};for(let{session:a,checkpoints:l}of n){if(a.archived||pt(a,l.length))continue;let c=l.slice().sort((p,m)=>R(m.createdAt)-R(p.createdAt))[0]||null,u=o&&a.machine&&a.machine!==o,d=xt(a,e,u?{...t,staleAfterSec:Math.max(t.staleAfterSec,900)}:t),f=Math.max(R(a.lastActivityAt)||0,R(a.endedAt)||0,c?R(c.createdAt):0,R(a.startedAt)||0);if(d==="ended"&&f<s||d!=="ended"&&f<s&&!c)continue;let h={session:a,latest:c,status:d};d==="needs_input"||c&&/^YOU:/i.test(c.next||"")&&d!=="ended"?r.needsYou.push(h):d==="ended"?r.finished.push(h):d==="dormant"||d==="stale"?r.dormant.push(h):r.active.push(h)}let i=(a,l)=>(R(l.session.lastActivityAt)||0)-(R(a.session.lastActivityAt)||0);for(let a of Object.values(r))a.sort(i);return r}function a1(n,e,t,s,o){let r=n.session,i=n.latest,a=r.repo&&r.repo.branch,l=(o&&o.get(zo(r))||ge(r))+(a?" · "+a:"")+(s&&r.machine?" · "+r.machine:""),c;t==="needsYou"?c=r.needsInput?"waiting on "+String(r.lastNotification||"input").replace(/_/g," ")+" since "+fy(r.lastEventAt||r.lastActivityAt):"blocked on you":t==="finished"?c="closed "+fy(r.endedAt):t==="active"?c=n.status:c="idle "+r1(e-(R(r.lastActivityAt)||e));let u=["- **"+(r.title||"Untitled session")+"** ("+l+") — "+c];return i?t==="dormant"||t==="finished"?u.push(" Stopped at: "+F(i.stoppedAt,220)+" Next: "+F(i.next,200)):u.push(" Next: "+F(i.next,220)):r.lastPreview&&u.push(" Last reply: "+F(r.lastPreview,200)),u}function l1(n){let e=n.needsYou[0]||n.dormant[0]||n.active[0];if(!e)return"Nothing open. Clean slate.";let t=e.session.title||"your most recent session",s=n.needsYou[0]?"it is blocked on you":n.dormant[0]?"it went quiet mid-task":"it is still running",o="Start with “"+t+"”: "+s+(e.latest?" (next: "+F(e.latest.next.replace(/^YOU:\s*/i,""),120).replace(/[.!?…]+$/,"")+").":"."),r=n.needsYou.length+n.dormant.length-1;return r>0&&(o+=" "+r+" more session"+(r===1?"":"s")+" waiting after that."),o}function c1(n,e,t){let s=[],o=0,r=(n||[]).filter(a=>!a.archived),i=[];for(let a of r){let l=(a.items||[]).filter(c=>c.done&&c.at&&R(c.at)>=e&&R(c.at)<=t);!l.length&&!(a.total>a.done)||(o+=l.length,i.push({l:a,got:l}))}if(!i.length)return s;i.sort((a,l)=>l.got.length-a.got.length),s.push("## Lists"+(o?" ("+o+" item"+(o===1?"":"s")+" done)":""));for(let{l:a,got:l}of i.slice(0,12)){let c=(a.items||[]).filter(u=>!u.done);s.push("- **"+F(a.name,80)+"** ("+(a.repo||"no repo")+"): "+a.done+"/"+a.total+" done"+(l.length?". Done: "+l.slice(0,4).map(u=>F(u.text,80)).join("; ")+(l.length>4?" +"+(l.length-4):""):"")+(c.length?". Next: "+F(c[0].text,100):". All done."))}return s.ticked=o,s}function Zd(n,e){return Xn(n||Ms(),e,{repoKey:zo(e),repoName:ge(e)}).space}async function Ul(n,{now:e=Date.now(),cfg:t=ke,date:s,summarizer:o,machine:r,todos:i=[],lists:a=[],agents:l=null,spaces:c=null,agentSpace:u=null}={}){let d=(t.digest&&t.digest.lookbackHours||24)*36e5,f=e-d,h=Xd(n,e,t,f,r),p=[...new Set(n.map(U=>U.session.machine).filter(Boolean))].sort(),m=s||o1(e),g=new Date(m+"T12:00:00").toLocaleDateString("en-US",{weekday:"short",month:"short",day:"numeric"}).replace(",",""),S=h.needsYou.length+h.active.length+h.dormant.length+h.finished.length,k={date:m,generatedAt:G(e),window:{from:G(f),to:G(e)},sessions:S,needsYou:h.needsYou.map(U=>U.session.id),path:"digests/"+m+".md",machine:r||null,shared:!0,machines:p},w=zt(Object.values(h).flat().map(U=>zo(U.session))),x=new Map,v=U=>(x.has(U)||x.set(U,{needsYou:[],active:[],dormant:[],finished:[],agents:[]}),x.get(U));for(let[U,E]of Object.entries(h))for(let P of E)v(Zd(c,P.session))[U].push(P);let b=new Map;if(l&&l.agents)for(let U of l.agents){let E=u?u(U):Xn(c||Ms(),{id:"agent:"+U.name+":x",agent:{name:U.name,puller:U.puller}}).space;Vd({agents:[U]},f,e,{heading:"###"}).length&&(v(E),b.set(E,[...b.get(E)||[],U]))}let y=_o([...x.keys()]);k.spaces=y;let _=[];_.push("# Where you left off — "+g,"");let A=0;for(let U of y){let E=x.get(U),P=E.needsYou.length+E.active.length+E.dormant.length+E.finished.length,oe=new Set(Object.values(E).flat().map(Ie=>Ie.session&&Ie.session.machine).filter(Boolean)).size>1;_.push("## "+U,"");let K=(Ie,Se,q)=>{if(Se.length){_.push("### "+Ie+" ("+Se.length+")");for(let re of Se)_.push(...a1(re,e,q,oe,w));_.push("")}};K("Needs you",E.needsYou,"needsYou"),K("Still running",E.active,"active"),K("Dormant, ready to pick up",E.dormant,"dormant"),K("Finished or closed",E.finished,"finished");let pe=b.get(U);if(pe){let Ie=Vd({agents:pe},f,e,{heading:"###"});A+=Ie.filter(Se=>Se.startsWith("- ")).length,_.push(...Ie,"")}else P||_.push("_Nothing here in the last "+Math.round(d/36e5)+" hours._","")}S||_.push("_No Claude Code sessions in the last "+Math.round(d/36e5)+" hours._",""),k.agentSessions=A;let O=Jm(i,e);O.length&&_.push(...O,""),k.todosOpen=(i||[]).length;let N=c1(a,f,e);N.length&&_.push(...N,""),k.listsTicked=N.ticked||0;let I=null,M=null;if(S&&(!t.digest||t.digest.topOfMind!==!1)){if(o){let U=[...h.needsYou,...h.dormant,...h.active].slice(0,15).map(E=>"- "+(E.session.title||"Untitled")+" ["+E.status+"]"+(E.latest?" Doing: "+E.latest.doing+" Stopped at: "+E.latest.stoppedAt+" Next: "+E.latest.next:""));I=await o.paragraph(i1,U.join(`
136
+ `)),I&&(M="model")}I||(I=l1(h),M="heuristic"),_.push("## Top of mind","",I,"")}k.topOfMindBy=M;let Y=Es({date:m,machine:r||null,shared:!0,machines:p,spaces:y,generated_at:k.generatedAt,window_from:k.window.from,window_to:k.window.to,sessions:S,needs_you:k.needsYou,schema:2});return{meta:k,markdown:Y+_.join(`
137
137
  `).replace(/\n+$/,"")+`
138
- `}}var Qv,$o,Fl=J(()=>{ud();Te();Gs();ns();Ns();Qv="You rank what a developer should pick up first this morning across their coding-agent sessions. Given the checkpoints, write 2-3 plain sentences naming the first thing to do and why. No preamble, no markdown, no lists.";$o=n=>n.repo&&n.repo.root||n.cwd||me(n)});function o1(n){let[e,t,s]=String(n).split("-").map(Number),o=new Date(Date.UTC(e,t-1,s,12));for(let r=0;r<7&&(o.setUTCDate(o.getUTCDate()-1),!s1.includes(an[o.getUTCDay()]));r++);return o.toISOString().slice(0,10)}function r1({date:n,now:e,tz:t="UTC",digestTime:s="07:00",prevDigestAt:o=null}){let r=o1(n),i=o?Date.parse(o):NaN;if(!Number.isFinite(i)){let[a,l,c]=r.split("-").map(Number),[u,d]=String(s||"07:00").split(":").map(Number);i=zt(a,l,c,u||0,d||0,t)}return{prev:r,from:i,to:e}}function Ul(n,{now:e=Date.now(),date:t,space:s,spaces:o=null,cfg:r={},tz:i="UTC",digestTime:a="07:00",prevDigestAt:l=null,agents:c=null,agentSpace:u=null,maxDormant:d=3}={}){let f=r1({date:t,now:e,tz:i,digestTime:a,prevDigestAt:l}),h=n.filter(y=>!s||qd(o,y.session)===s),p=Kt(h.map(y=>$o(y.session))),m=y=>"`"+(p.get($o(y))||me(y)||"no repo").replace(/`/g,"'")+"`",g=y=>Jt(y.title||"Untitled session",80),S=[],k=[],w=[],x=[];for(let{session:y,checkpoints:_}of h){if(y.archived||ft(y,_.length))continue;let A=M=>M&&M>=f.from&&M<=f.to,O=_.filter(M=>A(R(M.createdAt))).sort((M,z)=>R(z.createdAt)-R(M.createdAt)),I=A(R(y.endedAt));if(!O.length&&!I&&!A(R(y.lastActivityAt)))continue;let N=I?"done":O[0]?Jt(O[0].stoppedAt||O[0].doing,110):"worked on it";x.push({t:Math.max(R(y.lastActivityAt)||0,R(y.endedAt)||0),line:"• "+m(y)+" "+g(y)+(N==="done"?" (done)":" — "+N)})}x.sort((y,_)=>_.t-y.t),S.push(...x.slice(0,8).map(y=>y.line)),x.length>8&&S.push("• +"+(x.length-8)+" more");let v=Jd(h,e,r&&r.staleAfterSec?r:{...r,staleAfterSec:120},f.from);for(let y of v.needsYou){let _=y.session,A=y.latest,O=A&&/^YOU:/i.test(A.next||"")?Jt(A.next.replace(/^YOU:\s*/i,""),110):_.needsInput?"waiting on "+String(_.lastNotification||"input").replace(/_/g," "):"waiting on me";w.push("• "+m(_)+" "+g(_)+" — "+O)}for(let y of[...v.active,...v.dormant.slice(0,d)]){let _=y.session,A=y.latest;k.push("• "+m(_)+" "+g(_)+(A&&A.next?" — next: "+Jt(A.next,110):""))}if(c&&c.agents)for(let y of c.agents){let _=u?u(y):qn(o||Cs(),{id:"agent:"+y.name+":x",agent:{name:y.name,puller:y.puller}}).space;if(!(s&&_!==s))for(let A of y.records||[]){let O=A.session,I="`"+Jt(O.project||y.display,40)+"`",N=O.checkpoints&&O.checkpoints[O.checkpoints.length-1],M=ts(y,A,e),z=!!(M&&M.items&&M.items.length),U=yi(A),E=Date.parse(O.updatedAt);z?w.push("• "+I+" "+Jt(O.title,80)+" — "+Jt(M.items[0].text,110)):U!=="ended"&&k.push("• "+I+" "+Jt(O.title,80)+(N&&N.next?" — next: "+Jt(N.next,110):"")),E>=f.from&&E<=f.to&&!z&&S.push("• "+I+" "+Jt(O.title,80)+(U==="ended"?" (done)":N?" — "+Jt(N.summary,110):""))}}return{text:["Yesterday:",...S.length?S:["• Nothing logged"],"Today:",...k.length?k:["• Nothing open"],"Blocked:",...w.length?w:["• None"]].join(`
139
- `),space:s||null,counts:{yesterday:x.length,today:k.length,blocked:w.length},window:{from:new Date(f.from).toISOString(),to:new Date(f.to).toISOString(),previousWorkday:f.prev}}}var s1,Jt,Vd=J(()=>{Te();Gs();Fl();Ns();ns();si();s1=["mon","tue","wed","thu","fri"];Jt=(n,e)=>F(String(n||"").replace(/\s*\n\s*/g," ").replace(/`/g,"'").trim(),e).replace(/[\s.]+$/,"")});function c1(n){return n?n>=1e9?(n/1e9).toFixed(1)+" GB":Math.round(n/1e6)+" MB":""}async function ly(n,{current:e=null,baseUrl:t=null,fetchImpl:s=fetch,pulls:o={},env:r=process.env,platform:i=process.platform}={}){let a=String(n||"auto").toLowerCase();if(a==="ollama"){let l=await Gt({baseUrl:t||pt,fetchImpl:s,env:r}),c=Eo(l.models,{current:e}).map(f=>({tag:f.tag,name:f.name||f.tag,note:f.note||(f.curated?"":"already on this machine"),hint:[c1(f.sizeBytes),f.ramGB?"~"+f.ramGB+" GB RAM":null].filter(Boolean).join(" · "),recommended:!!f.recommended,current:!!f.current,installed:!!f.installed,curated:!!f.curated,canDownload:!!f.curated&&!f.installed&&l.running,pull:o[f.tag]||null})),u=l.running?null:Ha({platform:i,binary:l.binary,env:r}),d=l.running?{ok:!0,text:"Ollama "+(l.version?l.version+" ":"")+"is running"}:u?{ok:!1,text:"Ollama is installed but not running. Start it here, or run: "+u.shell,code:"ollama_down",canStart:!0,startShell:u.shell}:l.binary?{ok:!1,text:"Ollama is installed but not running. Start it with: "+l.binary+" serve",code:"ollama_down",canStart:!1}:{ok:!1,text:"Ollama isn’t running here. Install it from ollama.com, then start it, to download these.",code:"ollama_down",canStart:!1};return{provider:a,choices:c,status:d,checked:_o}}if(a==="ollama-cloud")return{provider:a,choices:zr.map(l=>Hl({tag:l.tag,name:l.name,note:l.note,hint:"runs on ollama.com · needs an API key",recommended:!!l.recommended},e)),status:null,checked:nm};if(a==="anthropic"){let l={haiku:"claude-haiku-5-5",sonnet:"claude-sonnet-5-5"}[e]||e;return{provider:a,choices:a1.map(c=>Hl({tag:c.tag,name:c.name,note:c.note,hint:c.cost,recommended:!!c.recommended},l)),status:null,checked:i1}}if(a==="claude"||a==="auto")return{provider:a,choices:l1.map(l=>Hl({tag:l.tag,name:l.name,note:l.note,hint:l.cost,recommended:!!l.recommended},e||"haiku")),status:null,checked:null};if(a==="openai-compatible"){let l=await Tn({baseUrl:t||rn,fetchImpl:s});return{provider:a,choices:l.models.map(c=>Hl({tag:c,name:c,note:"loaded in your local server",hint:"",recommended:!1},e)),status:l.running?{ok:!0,text:l.models.length+" model"+(l.models.length===1?"":"s")+" at "+l.baseUrl}:{ok:!1,text:"No server answered at "+l.baseUrl+". Start LM Studio’s server (or another OpenAI-compatible one).",code:"server_down"},checked:null}}return{provider:a,choices:[],status:null,checked:null}}function u1(n){return cy.some(e=>We(e,n))}function d1(n,e,t){if(!n||n.at==null)return{at:t,completed:e,bps:0};let s=(t-n.at)/1e3;if(s<.25)return n;let o=Math.max(0,(e-n.completed)/s);return{at:t,completed:e,bps:n.bps?n.bps*.6+o*.4:o}}var i1,a1,l1,Hl,cy,Bl,uy=J(()=>{js();i1="2026-10-10",a1=[{tag:"claude-haiku-5-5",name:"Claude Haiku 5.5",note:"Fastest and cheapest; plenty for a 3-line checkpoint",cost:"from $0.10 in / $0.50 out per M tokens",recommended:!0},{tag:"claude-sonnet-5-5",name:"Claude Sonnet 5.5",note:"Sharper summaries of long, messy sessions",cost:"$2 in / $10 out per M tokens"}],l1=[{tag:"haiku",name:"Haiku",note:"Quick; uses your Claude Code plan, no key",cost:"your Claude plan",recommended:!0},{tag:"sonnet",name:"Sonnet",note:"Slower, more careful summaries",cost:"your Claude plan"}];Hl=(n,e)=>({...n,current:!!e&&(We(e,n.tag)||e===n.tag)});cy=Yr.map(n=>n.tag);Bl=class{constructor({baseUrl:e=pt,fetchImpl:t=fetch,onChange:s=()=>{},pull:o=Io,now:r=()=>Date.now()}={}){Object.assign(this,{baseUrl:e,fetchImpl:t,onChange:s,pull:o,now:r}),this.jobs=new Map}view(e){return{tag:e.tag,status:e.status,fraction:e.fraction,completed:e.completed,total:e.total,bps:Math.round(e.rate.bps||0),done:e.done,error:e.error,cancelled:e.cancelled,use:e.use,startedAt:e.startedAt,finishedAt:e.finishedAt||null}}list(){let e={};for(let[t,s]of this.jobs)e[t]=this.view(s);return e}start(e,{use:t=!1,onDone:s=()=>{}}={}){if(!u1(e)){let l=new Error("Only the suggested models can be downloaded from the board: "+cy.join(", "));throw l.status=400,l.code="not_allowed",l}let o=this.jobs.get(e);if(o&&!o.done&&!o.error&&!o.cancelled)return t&&(o.use=!0),this.view(o);let r=new AbortController,i={tag:e,status:"starting",fraction:0,completed:0,total:0,rate:{},done:!1,error:null,cancelled:!1,use:t,startedAt:new Date(this.now()).toISOString(),ac:r};this.jobs.set(e,i);let a=0;return this.pull({tag:e,baseUrl:this.baseUrl,fetchImpl:this.fetchImpl,signal:r.signal,onProgress:l=>{i.status=l.status,i.fraction=l.fraction,i.completed=l.completed,i.total=l.total,i.rate=d1(i.rate,l.completed,this.now());let c=this.now();c-a>250&&(a=c,this.onChange(this.view(i)))}}).then(async()=>{i.done=!0,i.fraction=1,i.status="success",i.finishedAt=new Date(this.now()).toISOString();try{await s(i)}catch{}this.onChange(this.view(i))},l=>{r.signal.aborted?(i.cancelled=!0,i.status="cancelled"):(i.error=String(l&&l.message||l).replace(/^pull \S+ failed: /,""),i.status="failed"),i.finishedAt=new Date(this.now()).toISOString(),this.onChange(this.view(i))}),this.onChange(this.view(i)),this.view(i)}cancel(e){let t=this.jobs.get(e);return!t||t.done||t.error||t.cancelled?t?this.view(t):null:(t.ac.abort(),this.view(t))}}});import wi from"node:fs";import dy from"node:path";import Yo from"node:crypto";function hy(n){let e=String(n||"").trim().toLowerCase().replace(/\.$/,"").replace(/\.(?=:\d+$)/,"");if(!e)return null;let t=e.match(/^(\[[^\]]+\]|[^:\s/]+)(?::(\d+))?$/);return t?{name:t[1],port:t[2]?Number(t[2]):null}:null}function Gl(n){let e=hy(String(n||"").replace(/^https?:\/\//i,"").replace(/\/.*$/,""));return!e||Qd.includes(e.name)||!/^[a-z0-9.-]+$/.test(e.name)||!e.name.includes(".")?null:e}function Ut(n){let e=Gl(n);return e?e.name+(e.port&&e.port!==443?":"+e.port:""):null}function Si(n,e,t=[]){let s=hy(n);if(!s)return null;if(Qd.includes(s.name))return s.port===Number(e)?"loopback":null;for(let o of t||[]){let r=Gl(o);if(!(!r||r.name!==s.name)){if(r.port&&r.port!==443){if(s.port===r.port)return"remote"}else if(s.port===null||s.port===443||s.port===Number(e))return"remote"}}return null}function py(n,e,t=[]){if(!n)return!0;let s=String(n).toLowerCase(),o=s.match(/^http:\/\/([^/]+)$/);return o?Si(o[1],e,[])==="loopback":(o=s.match(/^https:\/\/([^/]+)$/),o?Si(o[1],e,t)==="remote":!1)}function Kl(n=[]){return(n||[]).map(Ut).filter(Boolean).map(e=>"https://"+e+"/")}function my(n){return dy.join(be(n).secret,"remote.json")}function Jo(n){try{let e=JSON.parse(wi.readFileSync(my(n),"utf8"));return{links:Array.isArray(e.links)?e.links:[],sessions:Array.isArray(e.sessions)?e.sessions:[],failures:Array.isArray(e.failures)?e.failures:[]}}catch{return{links:[],sessions:[],failures:[]}}}function Wo(n,e){let t=my(n);wi.mkdirSync(dy.dirname(t),{recursive:!0,mode:448});let s=t+".tmp-"+process.pid;wi.writeFileSync(s,JSON.stringify(e,null,2)+`
140
- `,{mode:384}),wi.renameSync(s,t);try{wi.chmodSync(t,384)}catch{}}function h1(n){let e=String(n||"").toUpperCase().replace(/[\s-]+/g,"");if(e.length!==fy)return null;for(let t of e)if(!Zd.includes(t))return null;return e}function p1(){return Array.from(Yo.randomBytes(fy),n=>Zd[n%Zd.length]).join("")}function vi(n,{now:e=Date.now(),ttlMs:t=qs,note:s=null,withCode:o=!0,codeOnly:r=!1}={}){let i=Jo(n),a=new Date(e+t).toISOString();i.links=i.links.filter(u=>Js(u,e));let l=null,c=null;return r||(l=Yo.randomBytes(24).toString("base64url"),i.links.push({kind:"link",hash:zo(l),createdAt:new Date(e).toISOString(),expiresAt:a,note:s})),(o||r)&&(c=p1(),i.links.push({kind:"code",hash:zo(c),createdAt:new Date(e).toISOString(),expiresAt:a,note:s})),Wo(n,i),{code:l,signInCode:c,expiresAt:a}}function gy(n,{now:e,userAgent:t,who:s,how:o}){let r=Yo.randomBytes(32).toString("base64url"),i=Yo.randomBytes(6).toString("hex"),a=new Date(e).toISOString();return n.sessions.push({id:i,hash:zo(r),createdAt:a,lastUsedAt:a,how:o,userAgent:String(t||"").slice(0,160),who:s?String(s).slice(0,120):null}),{id:i,secret:r}}function yy(n,e,{now:t=Date.now(),userAgent:s="",who:o=null}={}){if(!e||String(e).length>200)return null;let r=Jo(n),i=zo(e),a=r.links.find(c=>c.hash===i&&c.kind!=="code");if(!a||!Js(a,t))return null;a.usedAt=new Date(t).toISOString();let l=gy(r,{now:t,userAgent:s,who:o,how:"link"});return r.links=r.links.filter(c=>Js(c,t)),r.sessions=r.sessions.filter(c=>ki(c,t)),Wo(n,r),l}function wy(n,e,{now:t=Date.now(),userAgent:s="",who:o=null}={}){let r=Jo(n);if(r.failures=(r.failures||[]).filter(u=>t-Date.parse(u)<qs),r.failures.length>=Xd)return{ok:!1,reason:"locked"};let i=h1(e),a=i?zo(i):null,l=a?r.links.find(u=>u.kind==="code"&&u.hash===a&&Js(u,t)):null;if(!l)return r.failures.push(new Date(t).toISOString()),r.failures.length>=Xd&&(r.links=r.links.filter(u=>u.kind!=="code")),Wo(n,r),{ok:!1,reason:r.failures.length>=Xd?"locked":"bad_code"};l.usedAt=new Date(t).toISOString();let c=gy(r,{now:t,userAgent:s,who:o,how:"code"});return r.links=r.links.filter(u=>Js(u,t)),r.sessions=r.sessions.filter(u=>ki(u,t)),Wo(n,r),{ok:!0,...c}}function ki(n,e){return n.lastUsedAt?e-Date.parse(n.lastUsedAt)<ef*1e3:!0}function Yl(n,e,{now:t=Date.now(),touch:s=!1}={}){if(!e)return null;let o=Buffer.from(zo(e)),r=Jo(n),i=r.sessions.find(c=>{let u=Buffer.from(c.hash);return u.length===o.length&&Yo.timingSafeEqual(u,o)});if(!i||!ki(i,t))return null;if(s&&(!i.lastUsedAt||t-Date.parse(i.lastUsedAt)>f1)){i.lastUsedAt=new Date(t).toISOString(),r.sessions=r.sessions.filter(c=>ki(c,t));try{Wo(n,r)}catch{}}let{hash:a,...l}=i;return{...l,expiresAt:new Date(Date.parse(i.lastUsedAt||new Date(t).toISOString())+ef*1e3).toISOString()}}function zl(n,{maxAgeSec:e=ef}={}){return bi+"="+n+"; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age="+e}function xi(n){let t=Jo(n).sessions.length;return Wo(n,{links:[],sessions:[],failures:[]}),t}function Wl(n){let e=Jo(n),t=Date.now();return{sessions:e.sessions.filter(s=>ki(s,t)).map(s=>({id:s.id,createdAt:s.createdAt,lastUsedAt:s.lastUsedAt||null,how:s.how||"link",userAgent:s.userAgent,who:s.who})),pendingLinks:e.links.filter(s=>Js(s,t)&&s.kind!=="code").length,pendingCodes:e.links.filter(s=>Js(s,t)&&s.kind==="code").length}}var Qd,qs,bi,ef,f1,Zd,fy,Xd,zo,$l,Js,Jl=J(()=>{xe();_t();Qd=["127.0.0.1","[::1]","localhost",Tt,Ag],qs=900*1e3,bi="cp_remote",ef=720*3600,f1=3600*1e3,Zd="ABCDEFGHJKLMNPQRSTUVWXYZ23456789",fy=6,Xd=10;zo=n=>Yo.createHash("sha256").update(String(n)).digest("hex");$l=n=>n&&n.slice(0,3)+"-"+n.slice(3);Js=(n,e)=>!n.usedAt&&Date.parse(n.expiresAt)>e});import{execFile as m1}from"node:child_process";function y1(n,e=process.kill.bind(process)){if(!Number.isInteger(n)||n<=1)return!1;try{return e(n,0),!0}catch(t){return t&&t.code==="EPERM"}}function w1(n,{run:e=S1,now:t=Date.now()}={}){let s=[...new Set(n.filter(o=>Number.isInteger(o)&&o>1))];return s.length?e(s).then(o=>{let r=new Map;for(let i of String(o||"").split(`
141
- `)){let[a,l,c]=i.trim().split(/\s+/);if(a&&/^Z/.test(l||"")){r.set(Number(a),-1);continue}let u=Ar(c);a&&u!=null&&r.set(Number(a),t-u*1e3)}return r}).catch(()=>new Map):Promise.resolve(new Map)}function S1(n){return new Promise((e,t)=>{m1("ps",["-o","pid=,stat=,etime=","-p",n.join(",")],{env:{...process.env,LC_ALL:"C"},timeout:5e3},(s,o)=>{if(s&&!o)return s.code===1?e(""):t(s);e(o)})})}async function Sy(n,{kill:e,run:t,now:s=Date.now()}={}){let o=new Map,r=n.filter(c=>Number.isInteger(c.pid)&&c.pid>1),i=r.filter(c=>y1(c.pid,e));for(let c of r)i.includes(c)||o.set(c.id,{alive:!1,reason:"process_gone"});let a=i.filter(c=>c.pidStartedAt),l=a.length?await w1(a.map(c=>c.pid),{run:t,now:s}):new Map;for(let c of i){let u=l.get(c.pid);if(u===-1){o.set(c.id,{alive:!1,reason:"process_gone"});continue}let d=R(c.pidStartedAt);u!=null&&!isNaN(d)&&Math.abs(u-d)>g1?o.set(c.id,{alive:!1,reason:"pid_reused"}):o.set(c.id,{alive:!0,reason:"running"})}return o}var g1,ky=J(()=>{Te();g1=15e3});import tf from"node:fs";import Xl from"node:path";import _y from"node:os";import{execFile as k1}from"node:child_process";function nf({tty:n=!1}={}){return["on run argv",' if application "Ghostty" is not running then return "notrunning"'," set RS to character id 30"," set US to character id 31",' tell application "Ghostty"'," set ids to id of every terminal"," set nms to name of every terminal"," set wds to working directory of every terminal",...n?[" set ttys to {}"," try"," set ttys to «property Gtty» of every terminal"," end try"]:[]," end tell"," set n to count of ids",' if (count of nms) is not n or (count of wds) is not n then return "changed"',...n?[" set haveTty to ((count of ttys) is n)"]:[],' set out to "ok" & RS'," repeat with i from 1 to n",' set t to ""',...n?[" if haveTty then set t to item i of ttys"]:[]," set out to out & (item i of ids) & US & (item i of nms) & US & (item i of wds) & US & t & RS"," end repeat"," return out","end run"].join(`
142
- `)}function sf(n){let e=String(n||"").replace(/\n$/,"");if(e==="notrunning")return{state:"notrunning",terminals:[]};if(e==="changed")return{state:"changed",terminals:[]};if(!e.startsWith("ok"+by)&&e!=="ok")return{state:"bad",terminals:[]};let t=[];for(let s of e.slice(3).split(by)){if(!s)continue;let o=s.split(v1);o.length!==4||!o[0]||t.push({id:o[0],name:o[1],cwd:o[2],tty:o[3]||null})}return{state:"ok",terminals:t}}function vy(n,{realpath:e=!0}={}){let t=String(n||"").trim();if(!t)return"";if(t.startsWith("file://"))try{t=decodeURIComponent(new URL(t).pathname)}catch{}if((t==="~"||t.startsWith("~/"))&&(t=Xl.join(_y.homedir(),t.slice(1))),t.length>1&&(t=t.replace(/\/+$/,"")),e)try{t=tf.realpathSync.native(t)}catch{}return t}function Oy(n){return/^\s*[\u2733\u273b\u273d\u2736\u2722\u00b7*\u2800-\u28ff]\s/.test(String(n||""))}function Cy(n,e,{realpath:t=!0}={}){let s=n||{},o=e||[];if(s.tty&&o.some(c=>c.tty)){let c=o.find(u=>u.tty&&u.tty===s.tty);if(c)return{id:c.id,how:"tty"}}let r=s.marker||(s.id?vr(s.id):"");if(r){let c=o.find(u=>String(u.name||"").includes(r));if(c)return{id:c.id,how:"title"}}let i=xy(s.name),a=i?o.filter(c=>xy(c.name)===i):[];if(a.length===1)return{id:a[0].id,how:"name"};let l=vy(s.cwd,{realpath:t});if(l){let c=o.filter(u=>u.cwd&&vy(u.cwd,{realpath:t})===l);if(c.length>1&&a.length>1){let u=c.filter(d=>a.includes(d));if(u.length===1)return{id:u[0].id,how:"name+cwd"}}if(c.length===1)return{id:c[0].id,how:"cwd"};if(c.length>1){let u=c.filter(d=>Oy(d.name));return u.length===1?{id:u[0].id,how:"cwd+claude-title"}:{ambiguous:c.length}}}return null}function xy(n){let e=String(n||"").trim();return Oy(e)&&(e=e.replace(/^\s*\S+\s+/,"")),vs(e).replace(/\s+/g," ").trim().toLowerCase()}function ql(n){let e=String(n||"").match(/(\d+)\.(\d+)(?:\.(\d+))?/);if(!e)return null;let[t,s,o]=[Number(e[1]),Number(e[2]),Number(e[3]||0)];return t!==1?t>1:s!==3?s>3:o>1}function Ay({resourcesDir:n,home:e=_y.homedir()}={}){let t=[];if(n){let s=String(n).match(/^(.*?\.app)(?:\/|$)/);s&&t.push(s[1])}return t.push("/Applications/Ghostty.app",Xl.join(e,"Applications","Ghostty.app")),[...new Set(t)]}function Ty(n,e=t=>tf.readFileSync(t,"utf8")){try{let s=e(Xl.join(n,"Contents","Info.plist")).match(/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/);return s?s[1].trim():null}catch{return null}}function of({resourcesDir:n,version:e,home:t,readFile:s=o=>tf.readFileSync(o,"utf8")}={}){for(let o of Ay({resourcesDir:n,home:t})){let r=null;try{r=s(Xl.join(o,"Contents","Resources","Ghostty.sdef"))}catch{}if(r&&/class name="terminal"/.test(r))return{tty:/code="Gtty"/.test(r),version:Ty(o,s)||e||null,source:"sdef",bundle:o}}for(let o of Ay({resourcesDir:n,home:t})){let r=Ty(o,s);if(r)return{tty:ql(r)===!0,version:r,source:"Info.plist",bundle:o}}return e?{tty:ql(e)===!0,version:e,source:"TERM_PROGRAM_VERSION"}:{tty:!1,version:null,source:"unknown"}}function _1(n=process.env){let e=n.LC_ALL||n.LC_CTYPE||n.LANG||"";if(/utf-?8/i.test(e))return n;let t={...n,LC_CTYPE:"en_US.UTF-8"};return(!n.LANG||!/utf-?8/i.test(n.LANG))&&(t.LANG="en_US.UTF-8"),n.LC_ALL&&(t.LC_ALL="en_US.UTF-8"),t}function qt(n,{env:e,timeoutMs:t=qo}={}){let s=Date.now(),o=e?{...process.env,...e}:process.env;return/(^|\/)osascript$/.test(String(n[0]))&&(o=_1(o)),new Promise(r=>{k1(n[0],n.slice(1),{env:o,timeout:t,killSignal:"SIGKILL",encoding:"utf8"},(i,a,l)=>{let c=!!(i&&(i.killed||i.signal==="SIGKILL"));r({code:i?c?"timeout":typeof i.code=="number"?i.code:i.code||1:0,stdout:String(a||""),stderr:String(l||(c?"timed out after "+t+" ms":i&&i.code==="ENOENT"?"not found":"")),missing:!!(i&&i.code==="ENOENT"),ms:Date.now()-s})})})}function rf(n,e,t){return{ok:!1,code:"timeout",message:n+" didn't answer within "+Math.round(e/1e3)+" s"+(t?" ("+t+")":"")+". It may still switch in a moment. If macOS is showing a permission prompt, answer it and try again; `leftoff doctor` times each step."}}function O1(n){return E1[n]||n}async function Iy(n,{exec:e=qt,caps:t,capsFn:s=of,timeoutMs:o=qo,env:r,explain:i,onFound:a,onStale:l,activate:c="auto",realpath:u=!0}={}){let d=n.ghostty||{},f=Date.now(),h=[],p=async(y,_,A={})=>{let O=Date.now(),I=await e(_,{env:r,timeoutMs:A.timeoutMs||o}),N={step:y,ms:I.ms!=null?I.ms:Date.now()-O,result:I.code===0?String(I.stdout).trim().split(/[\x1e\n]/)[0]||"ok":String(I.code)};return h.push(N),{...I,step:N}},m=y=>({...y,steps:h,elapsedMs:Date.now()-f}),g=y=>y.code==="timeout"?m(rf("Ghostty",o,y.step.step)):y.missing?m({ok:!1,code:"missing_osascript",message:"Couldn't run `osascript`."}):m({ok:!1,...i?i(y.stderr,"Ghostty"):{code:"script_failed",message:String(y.stderr).trim()}}),S=()=>m({ok:!1,code:"not_running",message:"Ghostty isn't running."}),k=async y=>{let _=await p("focus by id",["osascript","-e",x1,y]);if(_.code!==0)return{fail:g(_)};let A=_.stdout.trim();return A==="ok"?{ok:!0}:A==="notrunning"?{fail:S()}:A.startsWith("stale")?(_.step.result="stale",{stale:!0}):{fail:m({ok:!1,code:"script_failed",message:'Ghostty answered "'+A.slice(0,80)+'"'})}},w=d.terminalId||null,x=null;if(w){let y=await k(w);if(y.fail)return y.fail;y.ok?x="cached id":(l&&l(w),w=null)}if(!x){let y=t||s({resourcesDir:d.resourcesDir,version:d.version}),_=null;for(let I=0;I<2;I++){let N=await p("list terminals"+(y.tty?" (with tty)":""),["osascript","-e",nf({tty:!!y.tty})]);if(N.code!==0)return g(N);if(_=sf(N.stdout),N.step.result=_.state==="ok"?Ey(_.terminals.length):_.state,_.state!=="changed")break}if(_.state==="notrunning")return S();if(_.state!=="ok")return m({ok:!1,code:"script_failed",message:"Ghostty's terminal list came back unreadable"+(_.state==="changed"?" (tabs were opening or closing); try again":"")+"."});let A=Cy({id:d.sessionId,marker:d.marker,name:d.name,cwd:d.cwd,tty:y.tty?d.tty:null},_.terminals,{realpath:u});if(!A)return m({ok:!1,code:"not_found",message:"Couldn't find the tab in Ghostty. It may have been closed, or its title was changed by hand."});if(A.ambiguous)return m({ok:!1,code:"ambiguous",message:A.ambiguous+" Ghostty tabs are in this folder and none has this session's name. Give it a name only it has (/cp-name), then try again."});a&&a(A.id,A.how,_.terminals);let O=await k(A.id);if(O.fail)return O.fail;if(O.stale)return m({ok:!1,code:"not_found",message:"The Ghostty tab closed while switching."});w=A.id,x=A.how}let v=null,b=c||"auto";if(b==="auto"){await b1(150);let y=await p("frontmost check",["osascript","-e",T1],{timeoutMs:3e3});v=y.code===0?y.stdout.trim()==="true":null,v===!1&&(await p("open -a Ghostty",["open","-a","Ghostty"],{timeoutMs:3e3})).code===0&&(v=null)}else b==="open"?await p("open -a Ghostty",["open","-a","Ghostty"],{timeoutMs:3e3}):(b==="applescript"||b==="always")&&await p("activate",["osascript","-e",A1],{timeoutMs:3e3});return m({ok:!0,how:x,terminalId:w,message:"Switched to Ghostty ("+(x==="cached id"?"remembered tab":"matched by "+O1(x))+")"})}async function Zl({exec:n=qt,caps:e,capsFn:t=of,timeoutMs:s=qo,hint:o={}}={}){let r=e||t(o),i=[],a=await n(["osascript","-e",'return "ok"'],{timeoutMs:s});i.push({step:"osascript start",ms:a.ms,result:a.code===0?"ok":String(a.code)});let l=await n(["osascript","-e",nf({tty:!!r.tty})],{timeoutMs:s}),c=l.code===0?sf(l.stdout):null;i.push({step:"list terminals"+(r.tty?" (with tty)":""),ms:l.ms,result:c?c.state==="ok"?Ey(c.terminals.length):c.state:String(l.code)});let u=!!(c&&c.state==="ok");return{ok:u,steps:i,terminals:u?c.terminals.length:null,caps:r,stderr:u?"":(l.stderr||"").trim(),code:l.code}}function Ql(n){return(n||[]).map(e=>e.step+" "+e.ms+" ms"+(e.result&&e.result!=="ok"?" ("+e.result+")":"")).join(" · ")}var qo,b1,Ey,by,v1,x1,A1,T1,E1,C1,Vl,Vo=J(()=>{Te();qo=Number(process.env.CHECKPOINTS_JUMP_STEP_TIMEOUT_MS)||8e3,b1=n=>new Promise(e=>setTimeout(e,n)),Ey=n=>n+(n===1?" terminal":" terminals"),by="",v1="";x1=`on run argv
138
+ `}}var i1,zo,Hl=J(()=>{fd();Te();$s();rs();Ds();i1="You rank what a developer should pick up first this morning across their coding-agent sessions. Given the checkpoints, write 2-3 plain sentences naming the first thing to do and why. No preamble, no markdown, no lists.";zo=n=>n.repo&&n.repo.root||n.cwd||ge(n)});function d1(n){let[e,t,s]=String(n).split("-").map(Number),o=new Date(Date.UTC(e,t-1,s,12));for(let r=0;r<7&&(o.setUTCDate(o.getUTCDate()-1),!u1.includes(cn[o.getUTCDay()]));r++);return o.toISOString().slice(0,10)}function f1({date:n,now:e,tz:t="UTC",digestTime:s="07:00",prevDigestAt:o=null}){let r=d1(n),i=o?Date.parse(o):NaN;if(!Number.isFinite(i)){let[a,l,c]=r.split("-").map(Number),[u,d]=String(s||"07:00").split(":").map(Number);i=Jt(a,l,c,u||0,d||0,t)}return{prev:r,from:i,to:e}}function Bl(n,{now:e=Date.now(),date:t,space:s,spaces:o=null,cfg:r={},tz:i="UTC",digestTime:a="07:00",prevDigestAt:l=null,agents:c=null,agentSpace:u=null,maxDormant:d=3}={}){let f=f1({date:t,now:e,tz:i,digestTime:a,prevDigestAt:l}),h=n.filter(y=>!s||Zd(o,y.session)===s),p=zt(h.map(y=>zo(y.session))),m=y=>"`"+(p.get(zo(y))||ge(y)||"no repo").replace(/`/g,"'")+"`",g=y=>Vt(y.title||"Untitled session",80),S=[],k=[],w=[],x=[];for(let{session:y,checkpoints:_}of h){if(y.archived||pt(y,_.length))continue;let A=M=>M&&M>=f.from&&M<=f.to,O=_.filter(M=>A(R(M.createdAt))).sort((M,Y)=>R(Y.createdAt)-R(M.createdAt)),N=A(R(y.endedAt));if(!O.length&&!N&&!A(R(y.lastActivityAt)))continue;let I=N?"done":O[0]?Vt(O[0].stoppedAt||O[0].doing,110):"worked on it";x.push({t:Math.max(R(y.lastActivityAt)||0,R(y.endedAt)||0),line:"• "+m(y)+" "+g(y)+(I==="done"?" (done)":" — "+I)})}x.sort((y,_)=>_.t-y.t),S.push(...x.slice(0,8).map(y=>y.line)),x.length>8&&S.push("• +"+(x.length-8)+" more");let v=Xd(h,e,r&&r.staleAfterSec?r:{...r,staleAfterSec:120},f.from);for(let y of v.needsYou){let _=y.session,A=y.latest,O=A&&/^YOU:/i.test(A.next||"")?Vt(A.next.replace(/^YOU:\s*/i,""),110):_.needsInput?"waiting on "+String(_.lastNotification||"input").replace(/_/g," "):"waiting on me";w.push("• "+m(_)+" "+g(_)+" — "+O)}for(let y of[...v.active,...v.dormant.slice(0,d)]){let _=y.session,A=y.latest;k.push("• "+m(_)+" "+g(_)+(A&&A.next?" — next: "+Vt(A.next,110):""))}if(c&&c.agents)for(let y of c.agents){let _=u?u(y):Xn(o||Ms(),{id:"agent:"+y.name+":x",agent:{name:y.name,puller:y.puller}}).space;if(!(s&&_!==s))for(let A of y.records||[]){let O=A.session,N="`"+Vt(O.project||y.display,40)+"`",I=O.checkpoints&&O.checkpoints[O.checkpoints.length-1],M=os(y,A,e),Y=!!(M&&M.items&&M.items.length),U=ki(A),E=Date.parse(O.updatedAt);Y?w.push("• "+N+" "+Vt(O.title,80)+" — "+Vt(M.items[0].text,110)):U!=="ended"&&k.push("• "+N+" "+Vt(O.title,80)+(I&&I.next?" — next: "+Vt(I.next,110):"")),E>=f.from&&E<=f.to&&!Y&&S.push("• "+N+" "+Vt(O.title,80)+(U==="ended"?" (done)":I?" — "+Vt(I.summary,110):""))}}return{text:["Yesterday:",...S.length?S:["• Nothing logged"],"Today:",...k.length?k:["• Nothing open"],"Blocked:",...w.length?w:["• None"]].join(`
139
+ `),space:s||null,counts:{yesterday:x.length,today:k.length,blocked:w.length},window:{from:new Date(f.from).toISOString(),to:new Date(f.to).toISOString(),previousWorkday:f.prev}}}var u1,Vt,Qd=J(()=>{Te();$s();Hl();Ds();rs();ri();u1=["mon","tue","wed","thu","fri"];Vt=(n,e)=>F(String(n||"").replace(/\s*\n\s*/g," ").replace(/`/g,"'").trim(),e).replace(/[\s.]+$/,"")});function g1(n){return n?n>=1e9?(n/1e9).toFixed(1)+" GB":Math.round(n/1e6)+" MB":""}async function hy(n,{current:e=null,baseUrl:t=null,fetchImpl:s=fetch,pulls:o={},env:r=process.env,platform:i=process.platform}={}){let a=String(n||"auto").toLowerCase();if(a==="ollama"){let l=await $t({baseUrl:t||gt,fetchImpl:s,env:r}),c=Co(l.models,{current:e}).map(f=>({tag:f.tag,name:f.name||f.tag,note:f.note||(f.curated?"":"already on this machine"),hint:[g1(f.sizeBytes),f.ramGB?"~"+f.ramGB+" GB RAM":null].filter(Boolean).join(" · "),recommended:!!f.recommended,current:!!f.current,installed:!!f.installed,curated:!!f.curated,canDownload:!!f.curated&&!f.installed&&l.running,pull:o[f.tag]||null})),u=l.running?null:Ka({platform:i,binary:l.binary,env:r}),d=l.running?{ok:!0,text:"Ollama "+(l.version?l.version+" ":"")+"is running"}:u?{ok:!1,text:"Ollama is installed but not running. Start it here, or run: "+u.shell,code:"ollama_down",canStart:!0,startShell:u.shell}:l.binary?{ok:!1,text:"Ollama is installed but not running. Start it with: "+l.binary+" serve",code:"ollama_down",canStart:!1}:{ok:!1,text:"Ollama isn’t running here. Install it from ollama.com, then start it, to download these.",code:"ollama_down",canStart:!1};return{provider:a,choices:c,status:d,checked:Oo}}if(a==="ollama-cloud")return{provider:a,choices:Wr.map(l=>Gl({tag:l.tag,name:l.name,note:l.note,hint:"runs on ollama.com · needs an API key",recommended:!!l.recommended},e)),status:null,checked:im};if(a==="anthropic"){let l={haiku:"claude-haiku-5-5",sonnet:"claude-sonnet-5-5"}[e]||e;return{provider:a,choices:p1.map(c=>Gl({tag:c.tag,name:c.name,note:c.note,hint:c.cost,recommended:!!c.recommended},l)),status:null,checked:h1}}if(a==="claude"||a==="auto")return{provider:a,choices:m1.map(l=>Gl({tag:l.tag,name:l.name,note:l.note,hint:l.cost,recommended:!!l.recommended},e||"haiku")),status:null,checked:null};if(a==="openai-compatible"){let l=await En({baseUrl:t||ln,fetchImpl:s});return{provider:a,choices:l.models.map(c=>Gl({tag:c,name:c,note:"loaded in your local server",hint:"",recommended:!1},e)),status:l.running?{ok:!0,text:l.models.length+" model"+(l.models.length===1?"":"s")+" at "+l.baseUrl}:{ok:!1,text:"No server answered at "+l.baseUrl+". Start LM Studio’s server (or another OpenAI-compatible one).",code:"server_down"},checked:null}}return{provider:a,choices:[],status:null,checked:null}}function y1(n){return py.some(e=>Je(e,n))}function w1(n,e,t){if(!n||n.at==null)return{at:t,completed:e,bps:0};let s=(t-n.at)/1e3;if(s<.25)return n;let o=Math.max(0,(e-n.completed)/s);return{at:t,completed:e,bps:n.bps?n.bps*.6+o*.4:o}}var h1,p1,m1,Gl,py,Kl,my=J(()=>{Hs();h1="2026-10-10",p1=[{tag:"claude-haiku-5-5",name:"Claude Haiku 5.5",note:"Fastest and cheapest; plenty for a 3-line checkpoint",cost:"from $0.10 in / $0.50 out per M tokens",recommended:!0},{tag:"claude-sonnet-5-5",name:"Claude Sonnet 5.5",note:"Sharper summaries of long, messy sessions",cost:"$2 in / $10 out per M tokens"}],m1=[{tag:"haiku",name:"Haiku",note:"Quick; uses your Claude Code plan, no key",cost:"your Claude plan",recommended:!0},{tag:"sonnet",name:"Sonnet",note:"Slower, more careful summaries",cost:"your Claude plan"}];Gl=(n,e)=>({...n,current:!!e&&(Je(e,n.tag)||e===n.tag)});py=Yr.map(n=>n.tag);Kl=class{constructor({baseUrl:e=gt,fetchImpl:t=fetch,onChange:s=()=>{},pull:o=Mo,now:r=()=>Date.now()}={}){Object.assign(this,{baseUrl:e,fetchImpl:t,onChange:s,pull:o,now:r}),this.jobs=new Map}view(e){return{tag:e.tag,status:e.status,fraction:e.fraction,completed:e.completed,total:e.total,bps:Math.round(e.rate.bps||0),done:e.done,error:e.error,cancelled:e.cancelled,use:e.use,startedAt:e.startedAt,finishedAt:e.finishedAt||null}}list(){let e={};for(let[t,s]of this.jobs)e[t]=this.view(s);return e}start(e,{use:t=!1,onDone:s=()=>{}}={}){if(!y1(e)){let l=new Error("Only the suggested models can be downloaded from the board: "+py.join(", "));throw l.status=400,l.code="not_allowed",l}let o=this.jobs.get(e);if(o&&!o.done&&!o.error&&!o.cancelled)return t&&(o.use=!0),this.view(o);let r=new AbortController,i={tag:e,status:"starting",fraction:0,completed:0,total:0,rate:{},done:!1,error:null,cancelled:!1,use:t,startedAt:new Date(this.now()).toISOString(),ac:r};this.jobs.set(e,i);let a=0;return this.pull({tag:e,baseUrl:this.baseUrl,fetchImpl:this.fetchImpl,signal:r.signal,onProgress:l=>{i.status=l.status,i.fraction=l.fraction,i.completed=l.completed,i.total=l.total,i.rate=w1(i.rate,l.completed,this.now());let c=this.now();c-a>250&&(a=c,this.onChange(this.view(i)))}}).then(async()=>{i.done=!0,i.fraction=1,i.status="success",i.finishedAt=new Date(this.now()).toISOString();try{await s(i)}catch{}this.onChange(this.view(i))},l=>{r.signal.aborted?(i.cancelled=!0,i.status="cancelled"):(i.error=String(l&&l.message||l).replace(/^pull \S+ failed: /,""),i.status="failed"),i.finishedAt=new Date(this.now()).toISOString(),this.onChange(this.view(i))}),this.onChange(this.view(i)),this.view(i)}cancel(e){let t=this.jobs.get(e);return!t||t.done||t.error||t.cancelled?t?this.view(t):null:(t.ac.abort(),this.view(t))}}});import bi from"node:fs";import gy from"node:path";import Yo from"node:crypto";function wy(n){let e=String(n||"").trim().toLowerCase().replace(/\.$/,"").replace(/\.(?=:\d+$)/,"");if(!e)return null;let t=e.match(/^(\[[^\]]+\]|[^:\s/]+)(?::(\d+))?$/);return t?{name:t[1],port:t[2]?Number(t[2]):null}:null}function $l(n){let e=wy(String(n||"").replace(/^https?:\/\//i,"").replace(/\/.*$/,""));return!e||nf.includes(e.name)||!/^[a-z0-9.-]+$/.test(e.name)||!e.name.includes(".")?null:e}function Bt(n){let e=$l(n);return e?e.name+(e.port&&e.port!==443?":"+e.port:""):null}function vi(n,e,t=[]){let s=wy(n);if(!s)return null;if(nf.includes(s.name))return s.port===Number(e)?"loopback":null;for(let o of t||[]){let r=$l(o);if(!(!r||r.name!==s.name)){if(r.port&&r.port!==443){if(s.port===r.port)return"remote"}else if(s.port===null||s.port===443||s.port===Number(e))return"remote"}}return null}function Sy(n,e,t=[]){if(!n)return!0;let s=String(n).toLowerCase(),o=s.match(/^http:\/\/([^/]+)$/);return o?vi(o[1],e,[])==="loopback":(o=s.match(/^https:\/\/([^/]+)$/),o?vi(o[1],e,t)==="remote":!1)}function zl(n=[]){return(n||[]).map(Bt).filter(Boolean).map(e=>"https://"+e+"/")}function ky(n){return gy.join(be(n).secret,"remote.json")}function qo(n){try{let e=JSON.parse(bi.readFileSync(ky(n),"utf8"));return{links:Array.isArray(e.links)?e.links:[],sessions:Array.isArray(e.sessions)?e.sessions:[],failures:Array.isArray(e.failures)?e.failures:[]}}catch{return{links:[],sessions:[],failures:[]}}}function Jo(n,e){let t=ky(n);bi.mkdirSync(gy.dirname(t),{recursive:!0,mode:448});let s=t+".tmp-"+process.pid;bi.writeFileSync(s,JSON.stringify(e,null,2)+`
140
+ `,{mode:384}),bi.renameSync(s,t);try{bi.chmodSync(t,384)}catch{}}function k1(n){let e=String(n||"").toUpperCase().replace(/[\s-]+/g,"");if(e.length!==yy)return null;for(let t of e)if(!tf.includes(t))return null;return e}function b1(){return Array.from(Yo.randomBytes(yy),n=>tf[n%tf.length]).join("")}function Ti(n,{now:e=Date.now(),ttlMs:t=Xs,note:s=null,withCode:o=!0,codeOnly:r=!1}={}){let i=qo(n),a=new Date(e+t).toISOString();i.links=i.links.filter(u=>Vs(u,e));let l=null,c=null;return r||(l=Yo.randomBytes(24).toString("base64url"),i.links.push({kind:"link",hash:Wo(l),createdAt:new Date(e).toISOString(),expiresAt:a,note:s})),(o||r)&&(c=b1(),i.links.push({kind:"code",hash:Wo(c),createdAt:new Date(e).toISOString(),expiresAt:a,note:s})),Jo(n,i),{code:l,signInCode:c,expiresAt:a}}function by(n,{now:e,userAgent:t,who:s,how:o}){let r=Yo.randomBytes(32).toString("base64url"),i=Yo.randomBytes(6).toString("hex"),a=new Date(e).toISOString();return n.sessions.push({id:i,hash:Wo(r),createdAt:a,lastUsedAt:a,how:o,userAgent:String(t||"").slice(0,160),who:s?String(s).slice(0,120):null}),{id:i,secret:r}}function vy(n,e,{now:t=Date.now(),userAgent:s="",who:o=null}={}){if(!e||String(e).length>200)return null;let r=qo(n),i=Wo(e),a=r.links.find(c=>c.hash===i&&c.kind!=="code");if(!a||!Vs(a,t))return null;a.usedAt=new Date(t).toISOString();let l=by(r,{now:t,userAgent:s,who:o,how:"link"});return r.links=r.links.filter(c=>Vs(c,t)),r.sessions=r.sessions.filter(c=>xi(c,t)),Jo(n,r),l}function xy(n,e,{now:t=Date.now(),userAgent:s="",who:o=null}={}){let r=qo(n);if(r.failures=(r.failures||[]).filter(u=>t-Date.parse(u)<Xs),r.failures.length>=ef)return{ok:!1,reason:"locked"};let i=k1(e),a=i?Wo(i):null,l=a?r.links.find(u=>u.kind==="code"&&u.hash===a&&Vs(u,t)):null;if(!l)return r.failures.push(new Date(t).toISOString()),r.failures.length>=ef&&(r.links=r.links.filter(u=>u.kind!=="code")),Jo(n,r),{ok:!1,reason:r.failures.length>=ef?"locked":"bad_code"};l.usedAt=new Date(t).toISOString();let c=by(r,{now:t,userAgent:s,who:o,how:"code"});return r.links=r.links.filter(u=>Vs(u,t)),r.sessions=r.sessions.filter(u=>xi(u,t)),Jo(n,r),{ok:!0,...c}}function xi(n,e){return n.lastUsedAt?e-Date.parse(n.lastUsedAt)<sf*1e3:!0}function Wl(n,e,{now:t=Date.now(),touch:s=!1}={}){if(!e)return null;let o=Buffer.from(Wo(e)),r=qo(n),i=r.sessions.find(c=>{let u=Buffer.from(c.hash);return u.length===o.length&&Yo.timingSafeEqual(u,o)});if(!i||!xi(i,t))return null;if(s&&(!i.lastUsedAt||t-Date.parse(i.lastUsedAt)>S1)){i.lastUsedAt=new Date(t).toISOString(),r.sessions=r.sessions.filter(c=>xi(c,t));try{Jo(n,r)}catch{}}let{hash:a,...l}=i;return{...l,expiresAt:new Date(Date.parse(i.lastUsedAt||new Date(t).toISOString())+sf*1e3).toISOString()}}function Jl(n,{maxAgeSec:e=sf}={}){return Ai+"="+n+"; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age="+e}function _i(n){let t=qo(n).sessions.length;return Jo(n,{links:[],sessions:[],failures:[]}),t}function ql(n){let e=qo(n),t=Date.now();return{sessions:e.sessions.filter(s=>xi(s,t)).map(s=>({id:s.id,createdAt:s.createdAt,lastUsedAt:s.lastUsedAt||null,how:s.how||"link",userAgent:s.userAgent,who:s.who})),pendingLinks:e.links.filter(s=>Vs(s,t)&&s.kind!=="code").length,pendingCodes:e.links.filter(s=>Vs(s,t)&&s.kind==="code").length}}var nf,Xs,Ai,sf,S1,tf,yy,ef,Wo,Yl,Vs,Vl=J(()=>{xe();Ot();nf=["127.0.0.1","[::1]","localhost",Et,Cg],Xs=900*1e3,Ai="cp_remote",sf=720*3600,S1=3600*1e3,tf="ABCDEFGHJKLMNPQRSTUVWXYZ23456789",yy=6,ef=10;Wo=n=>Yo.createHash("sha256").update(String(n)).digest("hex");Yl=n=>n&&n.slice(0,3)+"-"+n.slice(3);Vs=(n,e)=>!n.usedAt&&Date.parse(n.expiresAt)>e});import{execFile as v1}from"node:child_process";function A1(n,e=process.kill.bind(process)){if(!Number.isInteger(n)||n<=1)return!1;try{return e(n,0),!0}catch(t){return t&&t.code==="EPERM"}}function T1(n,{run:e=_1,now:t=Date.now()}={}){let s=[...new Set(n.filter(o=>Number.isInteger(o)&&o>1))];return s.length?e(s).then(o=>{let r=new Map;for(let i of String(o||"").split(`
141
+ `)){let[a,l,c]=i.trim().split(/\s+/);if(a&&/^Z/.test(l||"")){r.set(Number(a),-1);continue}let u=Tr(c);a&&u!=null&&r.set(Number(a),t-u*1e3)}return r}).catch(()=>new Map):Promise.resolve(new Map)}function _1(n){return new Promise((e,t)=>{v1("ps",["-o","pid=,stat=,etime=","-p",n.join(",")],{env:{...process.env,LC_ALL:"C"},timeout:5e3},(s,o)=>{if(s&&!o)return s.code===1?e(""):t(s);e(o)})})}async function Ay(n,{kill:e,run:t,now:s=Date.now()}={}){let o=new Map,r=n.filter(c=>Number.isInteger(c.pid)&&c.pid>1),i=r.filter(c=>A1(c.pid,e));for(let c of r)i.includes(c)||o.set(c.id,{alive:!1,reason:"process_gone"});let a=i.filter(c=>c.pidStartedAt),l=a.length?await T1(a.map(c=>c.pid),{run:t,now:s}):new Map;for(let c of i){let u=l.get(c.pid);if(u===-1){o.set(c.id,{alive:!1,reason:"process_gone"});continue}let d=R(c.pidStartedAt);u!=null&&!isNaN(d)&&Math.abs(u-d)>x1?o.set(c.id,{alive:!1,reason:"pid_reused"}):o.set(c.id,{alive:!0,reason:"running"})}return o}var x1,Ty=J(()=>{Te();x1=15e3});import of from"node:fs";import Ql from"node:path";import Iy from"node:os";import{execFile as E1}from"node:child_process";function rf({tty:n=!1}={}){return["on run argv",' if application "Ghostty" is not running then return "notrunning"'," set RS to character id 30"," set US to character id 31",' tell application "Ghostty"'," set ids to id of every terminal"," set nms to name of every terminal"," set wds to working directory of every terminal",...n?[" set ttys to {}"," try"," set ttys to «property Gtty» of every terminal"," end try"]:[]," end tell"," set n to count of ids",' if (count of nms) is not n or (count of wds) is not n then return "changed"',...n?[" set haveTty to ((count of ttys) is n)"]:[],' set out to "ok" & RS'," repeat with i from 1 to n",' set t to ""',...n?[" if haveTty then set t to item i of ttys"]:[]," set out to out & (item i of ids) & US & (item i of nms) & US & (item i of wds) & US & t & RS"," end repeat"," return out","end run"].join(`
142
+ `)}function af(n){let e=String(n||"").replace(/\n$/,"");if(e==="notrunning")return{state:"notrunning",terminals:[]};if(e==="changed")return{state:"changed",terminals:[]};if(!e.startsWith("ok"+_y)&&e!=="ok")return{state:"bad",terminals:[]};let t=[];for(let s of e.slice(3).split(_y)){if(!s)continue;let o=s.split(C1);o.length!==4||!o[0]||t.push({id:o[0],name:o[1],cwd:o[2],tty:o[3]||null})}return{state:"ok",terminals:t}}function Ey(n,{realpath:e=!0}={}){let t=String(n||"").trim();if(!t)return"";if(t.startsWith("file://"))try{t=decodeURIComponent(new URL(t).pathname)}catch{}if((t==="~"||t.startsWith("~/"))&&(t=Ql.join(Iy.homedir(),t.slice(1))),t.length>1&&(t=t.replace(/\/+$/,"")),e)try{t=of.realpathSync.native(t)}catch{}return t}function Ly(n){return/^\s*[\u2733\u273b\u273d\u2736\u2722\u00b7*\u2800-\u28ff]\s/.test(String(n||""))}function Dy(n,e,{realpath:t=!0}={}){let s=n||{},o=e||[];if(s.tty&&o.some(c=>c.tty)){let c=o.find(u=>u.tty&&u.tty===s.tty);if(c)return{id:c.id,how:"tty"}}let r=s.marker||(s.id?xr(s.id):"");if(r){let c=o.find(u=>String(u.name||"").includes(r));if(c)return{id:c.id,how:"title"}}let i=Oy(s.name),a=i?o.filter(c=>Oy(c.name)===i):[];if(a.length===1)return{id:a[0].id,how:"name"};let l=Ey(s.cwd,{realpath:t});if(l){let c=o.filter(u=>u.cwd&&Ey(u.cwd,{realpath:t})===l);if(c.length>1&&a.length>1){let u=c.filter(d=>a.includes(d));if(u.length===1)return{id:u[0].id,how:"name+cwd"}}if(c.length===1)return{id:c[0].id,how:"cwd"};if(c.length>1){let u=c.filter(d=>Ly(d.name));return u.length===1?{id:u[0].id,how:"cwd+claude-title"}:{ambiguous:c.length}}}return null}function Oy(n){let e=String(n||"").trim();return Ly(e)&&(e=e.replace(/^\s*\S+\s+/,"")),Ts(e).replace(/\s+/g," ").trim().toLowerCase()}function Xl(n){let e=String(n||"").match(/(\d+)\.(\d+)(?:\.(\d+))?/);if(!e)return null;let[t,s,o]=[Number(e[1]),Number(e[2]),Number(e[3]||0)];return t!==1?t>1:s!==3?s>3:o>1}function Cy({resourcesDir:n,home:e=Iy.homedir()}={}){let t=[];if(n){let s=String(n).match(/^(.*?\.app)(?:\/|$)/);s&&t.push(s[1])}return t.push("/Applications/Ghostty.app",Ql.join(e,"Applications","Ghostty.app")),[...new Set(t)]}function Ny(n,e=t=>of.readFileSync(t,"utf8")){try{let s=e(Ql.join(n,"Contents","Info.plist")).match(/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/);return s?s[1].trim():null}catch{return null}}function lf({resourcesDir:n,version:e,home:t,readFile:s=o=>of.readFileSync(o,"utf8")}={}){for(let o of Cy({resourcesDir:n,home:t})){let r=null;try{r=s(Ql.join(o,"Contents","Resources","Ghostty.sdef"))}catch{}if(r&&/class name="terminal"/.test(r))return{tty:/code="Gtty"/.test(r),version:Ny(o,s)||e||null,source:"sdef",bundle:o}}for(let o of Cy({resourcesDir:n,home:t})){let r=Ny(o,s);if(r)return{tty:Xl(r)===!0,version:r,source:"Info.plist",bundle:o}}return e?{tty:Xl(e)===!0,version:e,source:"TERM_PROGRAM_VERSION"}:{tty:!1,version:null,source:"unknown"}}function L1(n=process.env){let e=n.LC_ALL||n.LC_CTYPE||n.LANG||"";if(/utf-?8/i.test(e))return n;let t={...n,LC_CTYPE:"en_US.UTF-8"};return(!n.LANG||!/utf-?8/i.test(n.LANG))&&(t.LANG="en_US.UTF-8"),n.LC_ALL&&(t.LC_ALL="en_US.UTF-8"),t}function Xt(n,{env:e,timeoutMs:t=Vo}={}){let s=Date.now(),o=e?{...process.env,...e}:process.env;return/(^|\/)osascript$/.test(String(n[0]))&&(o=L1(o)),new Promise(r=>{E1(n[0],n.slice(1),{env:o,timeout:t,killSignal:"SIGKILL",encoding:"utf8"},(i,a,l)=>{let c=!!(i&&(i.killed||i.signal==="SIGKILL"));r({code:i?c?"timeout":typeof i.code=="number"?i.code:i.code||1:0,stdout:String(a||""),stderr:String(l||(c?"timed out after "+t+" ms":i&&i.code==="ENOENT"?"not found":"")),missing:!!(i&&i.code==="ENOENT"),ms:Date.now()-s})})})}function cf(n,e,t){return{ok:!1,code:"timeout",message:n+" didn't answer within "+Math.round(e/1e3)+" s"+(t?" ("+t+")":"")+". It may still switch in a moment. If macOS is showing a permission prompt, answer it and try again; `leftoff doctor` times each step."}}function R1(n){return D1[n]||n}async function Ry(n,{exec:e=Xt,caps:t,capsFn:s=lf,timeoutMs:o=Vo,env:r,explain:i,onFound:a,onStale:l,activate:c="auto",realpath:u=!0}={}){let d=n.ghostty||{},f=Date.now(),h=[],p=async(y,_,A={})=>{let O=Date.now(),N=await e(_,{env:r,timeoutMs:A.timeoutMs||o}),I={step:y,ms:N.ms!=null?N.ms:Date.now()-O,result:N.code===0?String(N.stdout).trim().split(/[\x1e\n]/)[0]||"ok":String(N.code)};return h.push(I),{...N,step:I}},m=y=>({...y,steps:h,elapsedMs:Date.now()-f}),g=y=>y.code==="timeout"?m(cf("Ghostty",o,y.step.step)):y.missing?m({ok:!1,code:"missing_osascript",message:"Couldn't run `osascript`."}):m({ok:!1,...i?i(y.stderr,"Ghostty"):{code:"script_failed",message:String(y.stderr).trim()}}),S=()=>m({ok:!1,code:"not_running",message:"Ghostty isn't running."}),k=async y=>{let _=await p("focus by id",["osascript","-e",N1,y]);if(_.code!==0)return{fail:g(_)};let A=_.stdout.trim();return A==="ok"?{ok:!0}:A==="notrunning"?{fail:S()}:A.startsWith("stale")?(_.step.result="stale",{stale:!0}):{fail:m({ok:!1,code:"script_failed",message:'Ghostty answered "'+A.slice(0,80)+'"'})}},w=d.terminalId||null,x=null;if(w){let y=await k(w);if(y.fail)return y.fail;y.ok?x="cached id":(l&&l(w),w=null)}if(!x){let y=t||s({resourcesDir:d.resourcesDir,version:d.version}),_=null;for(let N=0;N<2;N++){let I=await p("list terminals"+(y.tty?" (with tty)":""),["osascript","-e",rf({tty:!!y.tty})]);if(I.code!==0)return g(I);if(_=af(I.stdout),I.step.result=_.state==="ok"?My(_.terminals.length):_.state,_.state!=="changed")break}if(_.state==="notrunning")return S();if(_.state!=="ok")return m({ok:!1,code:"script_failed",message:"Ghostty's terminal list came back unreadable"+(_.state==="changed"?" (tabs were opening or closing); try again":"")+"."});let A=Dy({id:d.sessionId,marker:d.marker,name:d.name,cwd:d.cwd,tty:y.tty?d.tty:null},_.terminals,{realpath:u});if(!A)return m({ok:!1,code:"not_found",message:"Couldn't find the tab in Ghostty. It may have been closed, or its title was changed by hand."});if(A.ambiguous)return m({ok:!1,code:"ambiguous",message:A.ambiguous+" Ghostty tabs are in this folder and none has this session's name. Give it a name only it has (/cp-name), then try again."});a&&a(A.id,A.how,_.terminals);let O=await k(A.id);if(O.fail)return O.fail;if(O.stale)return m({ok:!1,code:"not_found",message:"The Ghostty tab closed while switching."});w=A.id,x=A.how}let v=null,b=c||"auto";if(b==="auto"){await O1(150);let y=await p("frontmost check",["osascript","-e",M1],{timeoutMs:3e3});v=y.code===0?y.stdout.trim()==="true":null,v===!1&&(await p("open -a Ghostty",["open","-a","Ghostty"],{timeoutMs:3e3})).code===0&&(v=null)}else b==="open"?await p("open -a Ghostty",["open","-a","Ghostty"],{timeoutMs:3e3}):(b==="applescript"||b==="always")&&await p("activate",["osascript","-e",I1],{timeoutMs:3e3});return m({ok:!0,how:x,terminalId:w,message:"Switched to Ghostty ("+(x==="cached id"?"remembered tab":"matched by "+R1(x))+")"})}async function ec({exec:n=Xt,caps:e,capsFn:t=lf,timeoutMs:s=Vo,hint:o={}}={}){let r=e||t(o),i=[],a=await n(["osascript","-e",'return "ok"'],{timeoutMs:s});i.push({step:"osascript start",ms:a.ms,result:a.code===0?"ok":String(a.code)});let l=await n(["osascript","-e",rf({tty:!!r.tty})],{timeoutMs:s}),c=l.code===0?af(l.stdout):null;i.push({step:"list terminals"+(r.tty?" (with tty)":""),ms:l.ms,result:c?c.state==="ok"?My(c.terminals.length):c.state:String(l.code)});let u=!!(c&&c.state==="ok");return{ok:u,steps:i,terminals:u?c.terminals.length:null,caps:r,stderr:u?"":(l.stderr||"").trim(),code:l.code}}function tc(n){return(n||[]).map(e=>e.step+" "+e.ms+" ms"+(e.result&&e.result!=="ok"?" ("+e.result+")":"")).join(" · ")}var Vo,O1,My,_y,C1,N1,I1,M1,D1,P1,Zl,Xo=J(()=>{Te();Vo=Number(process.env.CHECKPOINTS_JUMP_STEP_TIMEOUT_MS)||8e3,O1=n=>new Promise(e=>setTimeout(e,n)),My=n=>n+(n===1?" terminal":" terminals"),_y="",C1="";N1=`on run argv
143
143
  set termId to item 1 of argv
144
144
  if application "Ghostty" is not running then return "notrunning"
145
145
  tell application "Ghostty"
@@ -151,10 +151,10 @@ grep and sync. The daemon rebuilds its index from here at start.
151
151
  end try
152
152
  end tell
153
153
  return "ok"
154
- end run`,A1='tell application "Ghostty" to activate',T1=`if application "Ghostty" is not running then return "notrunning"
155
- tell application "Ghostty" to return frontmost as text`;E1={"cached id":"its remembered tab",tty:"tty",title:"title",name:"name","name+cwd":"name and folder",cwd:"folder","cwd+claude-title":"folder and Claude title"};C1=600*1e3,Vl=class{constructor({exec:e=qt,log:t=()=>{},now:s=()=>Date.now(),capsFn:o=of,timeoutMs:r=qo,realpath:i=!0}={}){this.exec=e,this.log=t,this.now=s,this.capsFn=o,this.timeoutMs=r,this.realpath=i,this.map=new Map,this.caps=null,this.capsAt=0,this.refreshing=null,this.last=null,this.refreshCount=0}getCaps(e={}){return(!this.caps||this.now()-this.capsAt>C1)&&(this.caps=this.capsFn(e),this.capsAt=this.now()),this.caps}get(e){let t=this.map.get(e);return t?t.id:null}entry(e){return this.map.get(e)||null}set(e,t,s){this.map.set(e,{id:t,how:s,at:this.now()})}forget(e){this.map.delete(e)}applyListing(e,t){let s=new Set((t||[]).map(i=>i.id));for(let[i,a]of this.map)s.has(a.id)||this.map.delete(i);let o=!!(this.caps&&this.caps.tty),r=0;for(let i of e||[]){let a=Cy({id:i.id,name:i.title||null,cwd:i.cwd,tty:o?i.tty:null},t,{realpath:this.realpath});a&&a.id&&(this.set(i.id,a.id,a.how),r++)}return r}refresh(e,{reason:t="manual"}={}){return this.refreshing?this.refreshing:(this.refreshing=this._refresh(e||[],t).finally(()=>{this.refreshing=null}),this.refreshing)}async _refresh(e,t){let s=this.now(),o=e[0]&&e[0].term?{resourcesDir:e[0].term.resourcesDir,version:e[0].term.version}:{},r=this.getCaps(o),i=null,a=null;for(let c=0;c<2&&(a=await this.exec(["osascript","-e",nf({tty:!!r.tty})],{timeoutMs:this.timeoutMs}),!(a.code!==0||(i=sf(a.stdout),i.state!=="changed")));c++);if(this.refreshCount++,!i||i.state!=="ok"){let c=i?i.state:a.code==="timeout"?"timeout":String(a.stderr||a.code).trim().split(`
156
- `).pop();return this.last={at:new Date(s).toISOString(),ms:this.now()-s,reason:t,error:c,stderr:a?a.stderr:""},i&&i.state==="notrunning"&&this.map.clear(),this.log("ghostty cache ("+t+"): "+c+" after "+this.last.ms+" ms"),this.last}let l=this.applyListing(e,i.terminals);return this.last={at:new Date(s).toISOString(),ms:this.now()-s,reason:t,terminals:i.terminals.length,sessions:e.length,matched:l},this.log("ghostty cache ("+t+"): "+i.terminals.length+" terminals, "+l+"/"+e.length+" sessions matched in "+this.last.ms+" ms"),this.last}}});import ec from"node:fs";import Ai from"node:path";function N1(n){return'"'+String(n??"").replace(/[\x00-\x08\x0b-\x1f\x7f]/g," ").replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\r?\n/g,"\\n")+'"'}function tc(n){let e=[];return n.cwd&&e.push(n.cwd),n.tty&&e.push(n.tty),e.join(" on ")}function Xo(n,{platform:e=lf()}={}){let t=n||{},s=t.term||t.terminal||Bn({}),o=s.kind||"unknown",r=s.label||o,i=e==="darwin",a=c=>({supported:!1,kind:o,label:r,reason:c,steps:[]}),l=(c,u,d={})=>({argv:["osascript","-e",c,...u.map(f=>String(f??""))],parse:"osascript",...d});switch(o){case"ghostty":return i?{supported:!0,kind:o,label:r,app:"Ghostty",steps:[],ghostty:{sessionId:t.id||null,marker:t.id?vr(t.id):"",name:t.title||null,cwd:t.cwd||"",tty:t.tty||"",terminalId:t.ghosttyTerminalId||null,resourcesDir:s.resourcesDir||null,version:s.version||null}}:a("Ghostty jump uses AppleScript, which is macOS only");case"iterm2":return i?s.itermSessionId?{supported:!0,kind:o,label:r,app:"iTerm2",steps:[l(M1,[s.itermSessionId])]}:a("no ITERM_SESSION_ID was recorded for this session"):a("iTerm2 is macOS only");case"apple-terminal":return i?t.tty?{supported:!0,kind:o,label:r,app:"Terminal",steps:[l(L1,[t.tty])]}:a("no tty was recorded for this session"):a("Terminal.app is macOS only");case"wezterm":{if(!s.weztermPane)return a("no WEZTERM_PANE was recorded for this session");let c=s.weztermSocket?{WEZTERM_UNIX_SOCKET:s.weztermSocket}:void 0,u=[{argv:["wezterm","cli","activate-pane","--pane-id",String(s.weztermPane)],env:c,bin:"wezterm"}];return i&&u.push({argv:["open","-a","WezTerm"],optional:!0}),{supported:!0,kind:o,label:r,app:"WezTerm",steps:u}}case"kitty":{if(!s.kittyWindowId)return a("no KITTY_WINDOW_ID was recorded for this session");if(!s.kittyListenOn)return a("kitty remote control is off. Add `allow_remote_control socket-only` and `listen_on unix:/tmp/kitty` to kitty.conf, restart kitty, then start a new session");let c=[{argv:["kitten","@","--to",s.kittyListenOn,"focus-window","--match","id:"+s.kittyWindowId],bin:"kitten"}];return i&&c.push({argv:["open","-a","kitty"],optional:!0}),{supported:!0,kind:o,label:r,app:"kitty",steps:c}}case"tmux":{if(!s.tmuxPane)return a("no TMUX_PANE was recorded for this session");let c=f=>({argv:["tmux",...s.tmuxSocket?["-S",s.tmuxSocket]:[],...f],bin:"tmux"}),u=[c(["select-window","-t",s.tmuxPane]),c(["select-pane","-t",s.tmuxPane]),{...c(["switch-client","-t",s.tmuxPane]),optional:!0}],d=s.host&&My[s.host];return i&&d&&u.push({argv:["open","-a",d],optional:!0}),{supported:!0,kind:o,label:r,app:d||null,steps:u}}case"vscode":return a(r+" terminals can't be focused from outside the editor"+(tc(t)?". It's in "+tc(t):""));default:return a("jumping isn't supported for "+(r||"this terminal")+" yet"+(tc(t)?". It's in "+tc(t):""))}}function af(n,e="the terminal"){let t=String(n||"");return/-1743|not authori[sz]ed to send apple events|not allowed to send apple events/i.test(t)?{code:"automation_denied",message:"macOS blocked the request. In System Settings → Privacy & Security → Automation, allow "+(e==="the terminal"?"it":"control of "+e)+' for the app that asked (your terminal for `leftoff go`, "node" for the board\'s Go button), then try again.'}:/-600|isn.t running|not running/i.test(t)?{code:"not_running",message:e+" isn't running."}:e==="Ghostty"&&/-2741|-2740|-1728|-1708|expected class name|expected end of line|can.t get every terminal|doesn.t understand/i.test(t)?{code:"ghostty_no_applescript",message:"This Ghostty has no AppleScript support. Jump needs Ghostty 1.3 or newer (Ghostty → Check for Updates), with macos-applescript left on (it is by default)."}:/-1712|timed? ?out/i.test(t)?{code:"timeout",message:e+" didn't answer. If macOS is showing a permission prompt, answer it and try again."}:{code:"script_failed",message:"The "+e+" script failed: "+F(t.trim().split(`
157
- `).pop(),200)}}function D1(n,{env:e=process.env,platform:t=process.platform}={}){for(let s of String(e.PATH||"").split(Ai.delimiter).filter(Boolean)){let o=Ai.join(s,n);try{return ec.accessSync(o,ec.constants.X_OK),o}catch{}}if(t==="darwin")for(let s of I1[n]||[])try{return ec.accessSync(s,ec.constants.X_OK),s}catch{}return null}function Ly(n,e={}){return qt(n,e)}async function nc(n,{exec:e=Ly,env:t=process.env,platform:s=lf(),timeoutMs:o=qo,ghostty:r={}}={}){let i=Date.now();if(!n||!n.supported)return{ok:!1,code:"unsupported",message:n&&n.reason||"jump is not supported here",steps:[],elapsedMs:0};if(n.kind==="ghostty"&&n.ghostty)return Iy(n,{exec:e,timeoutMs:o,explain:af,...r});let a=[],l=u=>({...u,steps:a,elapsedMs:Date.now()-i}),c=null;for(let u of n.steps){let d=u.argv;if(u.bin){let p=D1(u.bin,{env:t,platform:s});if(!p&&!u.optional)return l({ok:!1,code:"missing_"+u.bin,message:"Couldn't find `"+u.bin+"` on PATH"+(n.kind==="kitty"?" (it ships with kitty)":"")+"."});p&&(d=[p,...d.slice(1)])}let f=Date.now(),h=await e(d,{env:u.env,timeoutMs:o});if(a.push({step:u.name||(Ai.basename(d[0])+(d[0].endsWith("osascript")?"":" "+(d[1]||""))).trim(),ms:h.ms!=null?h.ms:Date.now()-f,result:h.code===0?"ok":String(h.code)}),!u.optional){if(h.code==="timeout")return l(rf(n.app||n.label||"The terminal",o));if(h.missing)return l({ok:!1,code:"missing_"+d[0],message:"Couldn't run `"+Ai.basename(d[0])+"`."});if(u.parse==="osascript"){if(h.code!==0)return l({ok:!1,...af(h.stderr,n.app)});let p=h.stdout.trim();if(p.startsWith("ok")){c=p.slice(2).trim()||null;continue}return p.startsWith("ambiguous")?l({ok:!1,code:"ambiguous",message:p.slice(9).trim()+" "+n.app+" tabs are in this folder and none has this session's name. Give it a name only it has (/cp-name), then try again."}):l({ok:!1,code:"not_found",message:"Couldn't find the tab in "+n.app+". It may have been closed, or its title was changed by hand."})}if(h.code!==0)return l({ok:!1,code:"command_failed",message:"`"+[Ai.basename(d[0]),...d.slice(1,3)].join(" ")+"` failed: "+F((h.stderr||h.stdout).trim(),200)})}}return l({ok:!0,how:c,message:"Switched to "+(n.label||n.kind)+(c?" (matched by "+c+")":"")})}function Ny(n){return String(n||"").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean)}function R1(n,e){let t=0;for(let s of e)s===n[t]&&t++;return t===n.length}function cf(n,e){let t=String(n||"").trim().toLowerCase();if(!t)return 1;let s=String(e.id||"").toLowerCase(),o=ze(e.id);if(t===o||t==="cp:"+o||t.length>=4&&s.startsWith(t.replace(/^cp:/,"")))return 1e3;let r=[[e.title,5],[e.repoName||e.repo&&e.repo.name,4],[e.repo&&e.repo.branch,2],[ia(e.cwd),3],[e.cwd,1],[e.machine,1]],i=0;for(let a of t.split(/\s+/).filter(Boolean)){let l=0;for(let[c,u]of r){if(!c)continue;let d=String(c).toLowerCase();Ny(d).includes(a)?l=Math.max(l,u*4):Ny(d).some(f=>f.startsWith(a))?l=Math.max(l,u*3):d.includes(a)?l=Math.max(l,u*2):a.length>=2&&R1(a,d.replace(/[^a-z0-9]/g,""))&&(l=Math.max(l,u))}if(!l)return 0;i+=l}return i}function Dy(n,e){return e.map(t=>({s:t,score:cf(n,t)})).filter(t=>t.score>0).sort((t,s)=>s.score-t.score||Date.parse(s.s.lastActivityAt||s.s.startedAt||0)-Date.parse(t.s.lastActivityAt||t.s.startedAt||0)).map(t=>t.s)}function P1(n){let e=String(n||"").match(/(\d+)\.(\d+)/);if(!e)return!0;let[t,s]=[Number(e[1]),Number(e[2])];return t>1||t===1&&s>=3}async function sc(n,{exec:e=Ly,platform:t=lf(),timeoutMs:s=8e3}={}){let o=My[n];if(t!=="darwin")return{kind:n,ok:null,message:"not macOS"};if(!o||n==="wezterm"||n==="kitty")return{kind:n,ok:null,message:"no AppleScript needed"};let r=await e(["osascript","-e","tell application "+N1(o)+" to get version"],{timeoutMs:s});if(r.code===0){let a=r.stdout.trim();return n==="ghostty"&&!P1(a)?{kind:n,ok:!1,version:a,code:"ghostty_old",message:"Ghostty "+a+" has no AppleScript; jump needs 1.3 or newer (Ghostty → Check for Updates)"}:{kind:n,ok:!0,version:a,message:o+" "+a+", automation allowed"}}let i=af(r.stderr,o);return{kind:n,ok:!1,code:i.code,message:i.message}}var My,I1,M1,L1,lf,Ti=J(()=>{Te();Vo();My={ghostty:"Ghostty",iterm2:"iTerm2","apple-terminal":"Terminal",wezterm:"WezTerm",kitty:"kitty"},I1={wezterm:["/Applications/WezTerm.app/Contents/MacOS/wezterm","/opt/homebrew/bin/wezterm","/usr/local/bin/wezterm"],kitten:["/Applications/kitty.app/Contents/MacOS/kitten","/opt/homebrew/bin/kitten","/usr/local/bin/kitten"],tmux:["/opt/homebrew/bin/tmux","/usr/local/bin/tmux","/usr/bin/tmux"]};M1=`on run argv
154
+ end run`,I1='tell application "Ghostty" to activate',M1=`if application "Ghostty" is not running then return "notrunning"
155
+ tell application "Ghostty" to return frontmost as text`;D1={"cached id":"its remembered tab",tty:"tty",title:"title",name:"name","name+cwd":"name and folder",cwd:"folder","cwd+claude-title":"folder and Claude title"};P1=600*1e3,Zl=class{constructor({exec:e=Xt,log:t=()=>{},now:s=()=>Date.now(),capsFn:o=lf,timeoutMs:r=Vo,realpath:i=!0}={}){this.exec=e,this.log=t,this.now=s,this.capsFn=o,this.timeoutMs=r,this.realpath=i,this.map=new Map,this.caps=null,this.capsAt=0,this.refreshing=null,this.last=null,this.refreshCount=0}getCaps(e={}){return(!this.caps||this.now()-this.capsAt>P1)&&(this.caps=this.capsFn(e),this.capsAt=this.now()),this.caps}get(e){let t=this.map.get(e);return t?t.id:null}entry(e){return this.map.get(e)||null}set(e,t,s){this.map.set(e,{id:t,how:s,at:this.now()})}forget(e){this.map.delete(e)}applyListing(e,t){let s=new Set((t||[]).map(i=>i.id));for(let[i,a]of this.map)s.has(a.id)||this.map.delete(i);let o=!!(this.caps&&this.caps.tty),r=0;for(let i of e||[]){let a=Dy({id:i.id,name:i.title||null,cwd:i.cwd,tty:o?i.tty:null},t,{realpath:this.realpath});a&&a.id&&(this.set(i.id,a.id,a.how),r++)}return r}refresh(e,{reason:t="manual"}={}){return this.refreshing?this.refreshing:(this.refreshing=this._refresh(e||[],t).finally(()=>{this.refreshing=null}),this.refreshing)}async _refresh(e,t){let s=this.now(),o=e[0]&&e[0].term?{resourcesDir:e[0].term.resourcesDir,version:e[0].term.version}:{},r=this.getCaps(o),i=null,a=null;for(let c=0;c<2&&(a=await this.exec(["osascript","-e",rf({tty:!!r.tty})],{timeoutMs:this.timeoutMs}),!(a.code!==0||(i=af(a.stdout),i.state!=="changed")));c++);if(this.refreshCount++,!i||i.state!=="ok"){let c=i?i.state:a.code==="timeout"?"timeout":String(a.stderr||a.code).trim().split(`
156
+ `).pop();return this.last={at:new Date(s).toISOString(),ms:this.now()-s,reason:t,error:c,stderr:a?a.stderr:""},i&&i.state==="notrunning"&&this.map.clear(),this.log("ghostty cache ("+t+"): "+c+" after "+this.last.ms+" ms"),this.last}let l=this.applyListing(e,i.terminals);return this.last={at:new Date(s).toISOString(),ms:this.now()-s,reason:t,terminals:i.terminals.length,sessions:e.length,matched:l},this.log("ghostty cache ("+t+"): "+i.terminals.length+" terminals, "+l+"/"+e.length+" sessions matched in "+this.last.ms+" ms"),this.last}}});import nc from"node:fs";import Ei from"node:path";function F1(n){return'"'+String(n??"").replace(/[\x00-\x08\x0b-\x1f\x7f]/g," ").replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\r?\n/g,"\\n")+'"'}function sc(n){let e=[];return n.cwd&&e.push(n.cwd),n.tty&&e.push(n.tty),e.join(" on ")}function Zo(n,{platform:e=df()}={}){let t=n||{},s=t.term||t.terminal||Kn({}),o=s.kind||"unknown",r=s.label||o,i=e==="darwin",a=c=>({supported:!1,kind:o,label:r,reason:c,steps:[]}),l=(c,u,d={})=>({argv:["osascript","-e",c,...u.map(f=>String(f??""))],parse:"osascript",...d});switch(o){case"ghostty":return i?{supported:!0,kind:o,label:r,app:"Ghostty",steps:[],ghostty:{sessionId:t.id||null,marker:t.id?xr(t.id):"",name:t.title||null,cwd:t.cwd||"",tty:t.tty||"",terminalId:t.ghosttyTerminalId||null,resourcesDir:s.resourcesDir||null,version:s.version||null}}:a("Ghostty jump uses AppleScript, which is macOS only");case"iterm2":return i?s.itermSessionId?{supported:!0,kind:o,label:r,app:"iTerm2",steps:[l(U1,[s.itermSessionId])]}:a("no ITERM_SESSION_ID was recorded for this session"):a("iTerm2 is macOS only");case"apple-terminal":return i?t.tty?{supported:!0,kind:o,label:r,app:"Terminal",steps:[l(H1,[t.tty])]}:a("no tty was recorded for this session"):a("Terminal.app is macOS only");case"wezterm":{if(!s.weztermPane)return a("no WEZTERM_PANE was recorded for this session");let c=s.weztermSocket?{WEZTERM_UNIX_SOCKET:s.weztermSocket}:void 0,u=[{argv:["wezterm","cli","activate-pane","--pane-id",String(s.weztermPane)],env:c,bin:"wezterm"}];return i&&u.push({argv:["open","-a","WezTerm"],optional:!0}),{supported:!0,kind:o,label:r,app:"WezTerm",steps:u}}case"kitty":{if(!s.kittyWindowId)return a("no KITTY_WINDOW_ID was recorded for this session");if(!s.kittyListenOn)return a("kitty remote control is off. Add `allow_remote_control socket-only` and `listen_on unix:/tmp/kitty` to kitty.conf, restart kitty, then start a new session");let c=[{argv:["kitten","@","--to",s.kittyListenOn,"focus-window","--match","id:"+s.kittyWindowId],bin:"kitten"}];return i&&c.push({argv:["open","-a","kitty"],optional:!0}),{supported:!0,kind:o,label:r,app:"kitty",steps:c}}case"tmux":{if(!s.tmuxPane)return a("no TMUX_PANE was recorded for this session");let c=f=>({argv:["tmux",...s.tmuxSocket?["-S",s.tmuxSocket]:[],...f],bin:"tmux"}),u=[c(["select-window","-t",s.tmuxPane]),c(["select-pane","-t",s.tmuxPane]),{...c(["switch-client","-t",s.tmuxPane]),optional:!0}],d=s.host&&jy[s.host];return i&&d&&u.push({argv:["open","-a",d],optional:!0}),{supported:!0,kind:o,label:r,app:d||null,steps:u}}case"vscode":return a(r+" terminals can't be focused from outside the editor"+(sc(t)?". It's in "+sc(t):""));default:return a("jumping isn't supported for "+(r||"this terminal")+" yet"+(sc(t)?". It's in "+sc(t):""))}}function uf(n,e="the terminal"){let t=String(n||"");return/-1743|not authori[sz]ed to send apple events|not allowed to send apple events/i.test(t)?{code:"automation_denied",message:"macOS blocked the request. In System Settings → Privacy & Security → Automation, allow "+(e==="the terminal"?"it":"control of "+e)+' for the app that asked (your terminal for `leftoff go`, "node" for the board\'s Go button), then try again.'}:/-600|isn.t running|not running/i.test(t)?{code:"not_running",message:e+" isn't running."}:e==="Ghostty"&&/-2741|-2740|-1728|-1708|expected class name|expected end of line|can.t get every terminal|doesn.t understand/i.test(t)?{code:"ghostty_no_applescript",message:"This Ghostty has no AppleScript support. Jump needs Ghostty 1.3 or newer (Ghostty → Check for Updates), with macos-applescript left on (it is by default)."}:/-1712|timed? ?out/i.test(t)?{code:"timeout",message:e+" didn't answer. If macOS is showing a permission prompt, answer it and try again."}:{code:"script_failed",message:"The "+e+" script failed: "+F(t.trim().split(`
157
+ `).pop(),200)}}function B1(n,{env:e=process.env,platform:t=process.platform}={}){for(let s of String(e.PATH||"").split(Ei.delimiter).filter(Boolean)){let o=Ei.join(s,n);try{return nc.accessSync(o,nc.constants.X_OK),o}catch{}}if(t==="darwin")for(let s of j1[n]||[])try{return nc.accessSync(s,nc.constants.X_OK),s}catch{}return null}function Fy(n,e={}){return Xt(n,e)}async function oc(n,{exec:e=Fy,env:t=process.env,platform:s=df(),timeoutMs:o=Vo,ghostty:r={}}={}){let i=Date.now();if(!n||!n.supported)return{ok:!1,code:"unsupported",message:n&&n.reason||"jump is not supported here",steps:[],elapsedMs:0};if(n.kind==="ghostty"&&n.ghostty)return Ry(n,{exec:e,timeoutMs:o,explain:uf,...r});let a=[],l=u=>({...u,steps:a,elapsedMs:Date.now()-i}),c=null;for(let u of n.steps){let d=u.argv;if(u.bin){let p=B1(u.bin,{env:t,platform:s});if(!p&&!u.optional)return l({ok:!1,code:"missing_"+u.bin,message:"Couldn't find `"+u.bin+"` on PATH"+(n.kind==="kitty"?" (it ships with kitty)":"")+"."});p&&(d=[p,...d.slice(1)])}let f=Date.now(),h=await e(d,{env:u.env,timeoutMs:o});if(a.push({step:u.name||(Ei.basename(d[0])+(d[0].endsWith("osascript")?"":" "+(d[1]||""))).trim(),ms:h.ms!=null?h.ms:Date.now()-f,result:h.code===0?"ok":String(h.code)}),!u.optional){if(h.code==="timeout")return l(cf(n.app||n.label||"The terminal",o));if(h.missing)return l({ok:!1,code:"missing_"+d[0],message:"Couldn't run `"+Ei.basename(d[0])+"`."});if(u.parse==="osascript"){if(h.code!==0)return l({ok:!1,...uf(h.stderr,n.app)});let p=h.stdout.trim();if(p.startsWith("ok")){c=p.slice(2).trim()||null;continue}return p.startsWith("ambiguous")?l({ok:!1,code:"ambiguous",message:p.slice(9).trim()+" "+n.app+" tabs are in this folder and none has this session's name. Give it a name only it has (/cp-name), then try again."}):l({ok:!1,code:"not_found",message:"Couldn't find the tab in "+n.app+". It may have been closed, or its title was changed by hand."})}if(h.code!==0)return l({ok:!1,code:"command_failed",message:"`"+[Ei.basename(d[0]),...d.slice(1,3)].join(" ")+"` failed: "+F((h.stderr||h.stdout).trim(),200)})}}return l({ok:!0,how:c,message:"Switched to "+(n.label||n.kind)+(c?" (matched by "+c+")":"")})}function Py(n){return String(n||"").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean)}function G1(n,e){let t=0;for(let s of e)s===n[t]&&t++;return t===n.length}function ff(n,e){let t=String(n||"").trim().toLowerCase();if(!t)return 1;let s=String(e.id||"").toLowerCase(),o=We(e.id);if(t===o||t==="cp:"+o||t.length>=4&&s.startsWith(t.replace(/^cp:/,"")))return 1e3;let r=[[e.title,5],[e.repoName||e.repo&&e.repo.name,4],[e.repo&&e.repo.branch,2],[ca(e.cwd),3],[e.cwd,1],[e.machine,1]],i=0;for(let a of t.split(/\s+/).filter(Boolean)){let l=0;for(let[c,u]of r){if(!c)continue;let d=String(c).toLowerCase();Py(d).includes(a)?l=Math.max(l,u*4):Py(d).some(f=>f.startsWith(a))?l=Math.max(l,u*3):d.includes(a)?l=Math.max(l,u*2):a.length>=2&&G1(a,d.replace(/[^a-z0-9]/g,""))&&(l=Math.max(l,u))}if(!l)return 0;i+=l}return i}function Uy(n,e){return e.map(t=>({s:t,score:ff(n,t)})).filter(t=>t.score>0).sort((t,s)=>s.score-t.score||Date.parse(s.s.lastActivityAt||s.s.startedAt||0)-Date.parse(t.s.lastActivityAt||t.s.startedAt||0)).map(t=>t.s)}function K1(n){let e=String(n||"").match(/(\d+)\.(\d+)/);if(!e)return!0;let[t,s]=[Number(e[1]),Number(e[2])];return t>1||t===1&&s>=3}async function rc(n,{exec:e=Fy,platform:t=df(),timeoutMs:s=8e3}={}){let o=jy[n];if(t!=="darwin")return{kind:n,ok:null,message:"not macOS"};if(!o||n==="wezterm"||n==="kitty")return{kind:n,ok:null,message:"no AppleScript needed"};let r=await e(["osascript","-e","tell application "+F1(o)+" to get version"],{timeoutMs:s});if(r.code===0){let a=r.stdout.trim();return n==="ghostty"&&!K1(a)?{kind:n,ok:!1,version:a,code:"ghostty_old",message:"Ghostty "+a+" has no AppleScript; jump needs 1.3 or newer (Ghostty → Check for Updates)"}:{kind:n,ok:!0,version:a,message:o+" "+a+", automation allowed"}}let i=uf(r.stderr,o);return{kind:n,ok:!1,code:i.code,message:i.message}}var jy,j1,U1,H1,df,Oi=J(()=>{Te();Xo();jy={ghostty:"Ghostty",iterm2:"iTerm2","apple-terminal":"Terminal",wezterm:"WezTerm",kitty:"kitty"},j1={wezterm:["/Applications/WezTerm.app/Contents/MacOS/wezterm","/opt/homebrew/bin/wezterm","/usr/local/bin/wezterm"],kitten:["/Applications/kitty.app/Contents/MacOS/kitten","/opt/homebrew/bin/kitten","/usr/local/bin/kitten"],tmux:["/opt/homebrew/bin/tmux","/usr/local/bin/tmux","/usr/bin/tmux"]};U1=`on run argv
158
158
  set sid to item 1 of argv
159
159
  tell application "iTerm2"
160
160
  repeat with w in windows
@@ -172,7 +172,7 @@ tell application "Ghostty" to return frontmost as text`;E1={"cached id":"its rem
172
172
  end repeat
173
173
  end tell
174
174
  return "notfound"
175
- end run`,L1=`on run argv
175
+ end run`,H1=`on run argv
176
176
  set ttyPath to item 1 of argv
177
177
  tell application "Terminal"
178
178
  repeat with w in windows
@@ -187,49 +187,49 @@ end run`,L1=`on run argv
187
187
  end repeat
188
188
  end tell
189
189
  return "notfound"
190
- end run`;lf=()=>process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform});import j1 from"node:http";import F1 from"node:https";import _i from"node:crypto";import{EventEmitter as Py}from"node:events";function Ry(n,{opcode:e=1,mask:t=!1}={}){let s=Buffer.isBuffer(n)?n:Buffer.from(String(n)),o=s.length,r=[128|e],i=Buffer.alloc(0);if(o<126?r.push((t?128:0)|o):o<65536?(r.push((t?128:0)|126),i=Buffer.alloc(2),i.writeUInt16BE(o)):(r.push((t?128:0)|127),i=Buffer.alloc(8),i.writeBigUInt64BE(BigInt(o))),!t)return Buffer.concat([Buffer.from(r),i,s]);let a=_i.randomBytes(4),l=Buffer.alloc(o);for(let c=0;c<o;c++)l[c]=s[c]^a[c&3];return Buffer.concat([Buffer.from(r),i,a,l])}function B1(n,{timeoutMs:e=5e3,hostHeader:t=null}={}){return new Promise((s,o)=>{let r=new URL(n),i=r.protocol==="wss:"||r.protocol==="https:",a=_i.randomBytes(16).toString("base64"),l=(i?F1:j1).request({host:r.hostname,port:r.port||(i?443:80),path:(r.pathname||"/")+(r.search||""),method:"GET",headers:{connection:"Upgrade",upgrade:"websocket","sec-websocket-version":"13","sec-websocket-key":a,"user-agent":"leftoff-peer",...t?{host:t}:{}},timeout:e,...i&&t?{servername:t.replace(/:\d+$/,"")}:{}});l.on("upgrade",(c,u,d)=>{let f=_i.createHash("sha1").update(a+U1).digest("base64");if(c.headers["sec-websocket-accept"]!==f)return u.destroy(),o(new Error("bad websocket handshake"));u.setNoDelay(!0),s(new df(u,{client:!0,head:d}))}),l.on("response",c=>{o(new Error("relay answered HTTP "+c.statusCode+" instead of upgrading")),c.resume()}),l.on("timeout",()=>{l.destroy(new Error("timed out"))}),l.on("error",o),l.end()})}var U1,H1,uf,df,Zo,ff=J(()=>{U1="258EAFA5-E914-47DA-95CA-C5AB0DC85B11",H1=1024*1024;uf=class{constructor(e){this.buf=Buffer.alloc(0),this.onFrame=e,this.parts=[],this.partOp=0}push(e){for(this.buf=Buffer.concat([this.buf,e]);;){if(this.buf.length<2)return;let t=this.buf[0],s=this.buf[1],o=!!(t&128),r=t&15,i=!!(s&128),a=s&127,l=2;if(a===126){if(this.buf.length<4)return;a=this.buf.readUInt16BE(2),l=4}else if(a===127){if(this.buf.length<10)return;a=Number(this.buf.readBigUInt64BE(2)),l=10}if(a>H1)throw new Error("frame too large");let c=l+(i?4:0)+a;if(this.buf.length<c)return;let u=this.buf.subarray(l+(i?4:0),c);if(i){let d=this.buf.subarray(l,l+4),f=Buffer.alloc(a);for(let h=0;h<a;h++)f[h]=u[h]^d[h&3];u=f}else u=Buffer.from(u);if(this.buf=this.buf.subarray(c),r===0){this.parts.push(u),o&&(this.onFrame({opcode:this.partOp,payload:Buffer.concat(this.parts)}),this.parts=[]);continue}if(!o&&(r===1||r===2)){this.partOp=r,this.parts=[u];continue}this.onFrame({opcode:r,payload:u})}}},df=class extends Py{constructor(e,{client:t,head:s=null}){super(),this.socket=e,this.client=t,this.open=!0;let o=new uf(({opcode:r,payload:i})=>{r===1?this.emit("message",i.toString("utf8")):r===8?this.close():r===9&&this.raw(i,10)});e.on("data",r=>{try{o.push(r)}catch{this.close()}}),s&&s.length&&setImmediate(()=>{try{o.push(s)}catch{this.close()}}),e.on("close",()=>{this.open&&(this.open=!1,this.emit("close"))}),e.on("error",()=>{})}raw(e,t){this.open&&this.socket.write(Ry(e,{opcode:t,mask:this.client}))}send(e){this.raw(Buffer.from(typeof e=="string"?e:JSON.stringify(e)),1)}ping(){this.raw(Buffer.alloc(0),9)}close(){if(this.open){try{this.socket.write(Ry(Buffer.alloc(0),{opcode:8,mask:this.client}))}catch{}this.open=!1;try{this.socket.end()}catch{}setTimeout(()=>{try{this.socket.destroy()}catch{}},200).unref(),this.emit("close")}}};Zo=class extends Py{constructor({url:e,id:t,log:s=()=>{},onEnvelope:o,accepts:r=()=>!0,user:i=null,hostHeader:a=null,helloExtra:l=()=>({})}){super(),this.url=e,this.id=t,this.log=s,this.onEnvelope=o,this.accepts=r,this.user=i,this.hostHeader=a,this.helloExtra=l,this.conn=null,this.connected=!1,this.closed=!1,this.backoff=1e3,this.waiting=new Map,this.calls=new Map,this.latencyMs=null,this.connectedAt=null,this.lastError=null,this.seq=0}async connect(){if(this.closed)return;let e;try{e=await B1(this.url,{hostHeader:this.hostHeader})}catch(t){return this.lastError=t.message,this.emit("state"),this.retry()}if(this.closed){e.close();return}this.conn=e,e.on("message",t=>this.onMessage(t)),e.on("close",()=>{let t=this.connected;this.connected=!1,this.conn=null,this.connectedAt=null;for(let[,s]of this.waiting)s.reject(Object.assign(new Error("relay connection closed"),{code:"relay_closed"}));this.waiting.clear();for(let[,s]of this.calls)s.reject(Object.assign(new Error("relay connection closed"),{code:"relay_closed"}));this.calls.clear(),t&&(this.log("peer: relay connection closed"),this.emit("state")),this.retry()}),clearInterval(this.keep),this.keep=setInterval(()=>this.ping(),Number(process.env.LEFTOFF_RELAY_PING_MS)||25e3),this.keep.unref()}ping(){this.conn&&(this.user?(this.pingAt=Date.now(),this.conn.send('{"t":"ping"}')):this.conn.ping())}retry(){this.closed||(clearInterval(this.keep),clearTimeout(this.retryTimer),this.retryTimer=setTimeout(()=>this.connect(),this.backoff),this.retryTimer.unref(),this.backoff=Math.min(3e4,this.backoff*2))}hello(){}send(e){return this.conn&&this.connected?(this.conn.send(e),!0):!1}ask(e,t=8e3){if(!this.conn||!this.connected)return Promise.reject(Object.assign(new Error("not connected to the relay"),{code:"relay_down"}));let s="c"+ ++this.seq+"."+_i.randomBytes(4).toString("hex");return new Promise((o,r)=>{let i=setTimeout(()=>{this.calls.delete(s),r(Object.assign(new Error("the relay didn’t answer"),{code:"relay_timeout"}))},t);this.calls.set(s,{resolve:a=>{clearTimeout(i),o(a)},reject:a=>{clearTimeout(i),r(a)}}),this.conn.send({...e,id:s})})}onMessage(e){let t;try{t=JSON.parse(e)}catch{return}if(t.t==="pong"){this.pingAt&&(this.latencyMs=Date.now()-this.pingAt,this.pingAt=0,this.emit("latency",this.latencyMs));return}if(t.t==="challenge"){if(this.user&&t.user&&t.user!==this.user)return this.lastError="wrong_user",this.log("relay: that relay is for "+t.user+", not "+this.user),this.conn.close();let s=t.user?t.user+"|":"",o=_i.sign(null,Buffer.from("leftoff-relay-hello|"+s+this.id.machine+"|"+t.nonce),this.id.signPriv).toString("base64url");this.conn.send({t:"hello",machine:this.id.machine,sign:this.id.signX,nonce:t.nonce,sig:o,...this.helloExtra()})}else if(t.t==="welcome")this.connected=!0,this.backoff=1e3,this.lastError=null,this.connectedAt=Date.now(),this.relayMachines=t.machines||null,this.log("peer: connected to the relay "+(this.hostHeader||this.url)),this.emit("state"),this.emit("welcome",t),this.user&&setTimeout(()=>this.ping(),50).unref();else if((t.t==="ack"||t.t==="devices"||t.t==="status")&&t.id&&this.calls.has(t.id)){let s=this.calls.get(t.id);this.calls.delete(t.id),s.resolve(t)}else if(t.t==="error"&&t.ref&&this.waiting.has(t.ref)){let s=this.waiting.get(t.ref);this.waiting.delete(t.ref),s.reject(Object.assign(new Error("relay: "+t.code),{code:"relay_"+t.code}))}else if(t.t==="error")this.lastError=t.code,this.log("peer: relay said "+t.code),this.emit("state");else if(t.t==="deliver"&&t.env){let s=t.env;if(s.kind==="res"||s.kind==="err"){let o=this.waiting.get(s.re||t.ref);o&&(this.waiting.delete(s.re||t.ref),o.resolve(s));return}if(!this.onEnvelope)return this.conn&&this.conn.send({t:"send",to:t.from,ref:s.id,env:{kind:"err",code:"not_allowed",re:s.id}});Promise.resolve(this.onEnvelope(s)).then(o=>{o&&this.conn&&this.conn.send({t:"send",to:t.from,ref:s.id,env:o.kind==="err"?{...o,re:s.id}:o})}).catch(()=>{})}else t.t&&this.emit("frame",t)}request(e,t,s=4e3){return!this.conn||!this.connected?Promise.reject(Object.assign(new Error("not connected to the relay"),{code:"relay_down"})):new Promise((o,r)=>{let i=setTimeout(()=>{this.waiting.delete(t.id),r(Object.assign(new Error("no answer through the relay"),{code:"relay_timeout"}))},s);this.waiting.set(t.id,{resolve:a=>{clearTimeout(i),o(a)},reject:a=>{clearTimeout(i),r(a)}}),this.conn.send({t:"send",to:e,ref:t.id,env:t})})}close(){this.closed=!0,clearInterval(this.keep),clearTimeout(this.retryTimer),this.conn&&this.conn.close()}}});var yf={};Qt(yf,{ACTIONS:()=>$1,MAX_TTL_MS:()=>Fy,PROTOCOL:()=>Qo,PeerError:()=>ye,PeerHub:()=>Oi,ReplayGuard:()=>cc,SKEW_MS:()=>Uy,TTL_MS:()=>jy,canonical:()=>It,cardFile:()=>Gy,fingerprint:()=>mf,identityDir:()=>uc,loadIdentity:()=>Nn,localAddresses:()=>zy,machinesDir:()=>ic,makeCard:()=>Ky,open:()=>hf,publishCard:()=>$y,readCards:()=>Vs,saveIdentity:()=>oc,seal:()=>rc,trustOf:()=>In,verifyCard:()=>gf});import Vt from"node:fs";import pf from"node:os";import ln from"node:path";import G1 from"node:http";import Fe from"node:crypto";import{EventEmitter as K1}from"node:events";function It(n){return n===null||typeof n!="object"?JSON.stringify(n===void 0?null:n):Array.isArray(n)?"["+n.map(It).join(",")+"]":"{"+Object.keys(n).filter(e=>n[e]!==void 0).sort().map(e=>JSON.stringify(e)+":"+It(n[e])).join(",")+"}"}function uc(n=process.env){return n.LEFTOFF_IDENTITY_DIR?n.LEFTOFF_IDENTITY_DIR:ln.join(n.XDG_CONFIG_HOME||ln.join(n.HOME||pf.homedir(),".config"),"checkpoints","identity")}function mf(n,e){return Fe.createHash("sha256").update(ss(n)).update(ss(e)).digest("hex").slice(0,16).toUpperCase().match(/.{4}/g).join("-")}function Y1(){let n=Fe.generateKeyPairSync("ed25519"),e=Fe.generateKeyPairSync("x25519");return{sign:n.privateKey.export({format:"jwk"}),box:e.privateKey.export({format:"jwk"})}}function z1(n,e){let t=Fe.createPrivateKey({key:n.sign,format:"jwk"}),s=Fe.createPrivateKey({key:n.box,format:"jwk"});return{machine:n.machine,file:e,state:n,signPriv:t,boxPriv:s,signX:n.sign.x,boxX:n.box.x,fingerprint:mf(n.sign.x,n.box.x),get allowIncoming(){return!!n.allowIncoming},get pins(){return n.pins||(n.pins={})}}}function Nn(n,{dir:e=uc()}={}){let t=ln.join(e,Hy(n)+".json"),s=null;try{s=JSON.parse(Vt.readFileSync(t,"utf8"))}catch{}(!s||!s.sign||!s.box)&&(s={v:1,machine:n,created:new Date().toISOString(),...Y1(),allowIncoming:!1,pins:{}},Vt.mkdirSync(e,{recursive:!0,mode:448}),By(t,s));try{Vt.chmodSync(t,384)}catch{}return z1(s,t)}function By(n,e){let t=n+"."+process.pid+".tmp";Vt.writeFileSync(t,JSON.stringify(e,null,2)+`
191
- `,{mode:384}),Vt.renameSync(t,n)}function oc(n){By(n.file,n.state)}function ic(n){return ln.join(n,"machines")}function Gy(n,e){return ln.join(ic(n),Hy(e)+".json")}function Ky(n,{addresses:e=[],relay:t=!1,version:s=null,hostname:o=pf.hostname(),now:r=Date.now(),anywhere:i=null}={}){let a={v:Qo,app:"leftoff",machine:n.machine,hostname:o,version:s,sign:n.signX,box:n.boxX,fingerprint:n.fingerprint,accepts:n.allowIncoming,addresses:n.allowIncoming?e:[],relay:!!(t&&n.allowIncoming),...i?{anywhere:i}:{},updatedAt:new Date(r).toISOString()};return a.sig=Ei(Fe.sign(null,Buffer.from(It(a)),n.signPriv)),a}function gf(n){if(!n||typeof n!="object"||!n.sign||!n.box||!n.sig||!n.machine)return!1;try{let{sig:e,...t}=n,s=Fe.createPublicKey({key:{kty:"OKP",crv:"Ed25519",x:n.sign},format:"jwk"});return Fe.verify(null,Buffer.from(It(t)),s,ss(e))&&mf(n.sign,n.box)===n.fingerprint}catch{return!1}}function $y(n,e){let t=Gy(n,e.machine);try{let o=JSON.parse(Vt.readFileSync(t,"utf8")),r=i=>{let{updatedAt:a,sig:l,...c}=i;return It(c)};if(r(o)===r(e)&&gf(o))return{file:t,changed:!1}}catch{}Vt.mkdirSync(ln.dirname(t),{recursive:!0});let s=ln.join(ln.dirname(t),"."+ln.basename(t)+"."+process.pid+".tmp");return Vt.writeFileSync(s,JSON.stringify(e,null,2)+`
192
- `),Vt.renameSync(s,t),{file:t,changed:!0}}function Vs(n){let e={},t=[];try{t=Vt.readdirSync(ic(n)).filter(s=>s.endsWith(".json")&&!s.startsWith("."))}catch{return e}for(let s of t)try{let o=JSON.parse(Vt.readFileSync(ln.join(ic(n),s),"utf8"));e[o.machine||s.slice(0,-5)]={card:o,valid:gf(o)}}catch{}return e}function In(n,e,{pinNew:t=!0}={}){if(!e||!e.valid)return"invalid";let s=n.pins[e.card.machine];return s?s===e.card.fingerprint?"trusted":"key_changed":t?(n.pins[e.card.machine]=e.card.fingerprint,"new"):"unpinned"}function Yy(n,e,t,s){let o=Fe.createPublicKey({key:{kty:"OKP",crv:"X25519",x:e},format:"jwk"}),r=Fe.diffieHellman({privateKey:n,publicKey:o});return Buffer.from(Fe.hkdfSync("sha256",r,t,Buffer.from(s),32))}function rc({me:n,to:e,kind:t="req",body:s,re:o=null,ttlMs:r=jy,now:i=Date.now()}){if(!e||!e.box||!e.machine)throw new ye("unknown_target","no published key for that machine",400);let a=Fe.generateKeyPairSync("x25519"),l=Ei(Fe.randomBytes(16)),c=Fe.randomBytes(12),u={v:Qo,kind:t,from:n.machine,to:e.machine,id:l,iat:i,exp:i+r,re:o||void 0,epk:a.publicKey.export({format:"jwk"}).x,iv:Ei(c)},d=Yy(a.privateKey,e.box,ss(l),"leftoff-peer-v1|"+u.from+"|"+u.to),f=Fe.createCipheriv("aes-256-gcm",d,c);f.setAAD(Buffer.from(It(lc(u,ac))));let h=Buffer.concat([f.update(Buffer.from(JSON.stringify(s))),f.final(),f.getAuthTag()]);return u.ct=Ei(h),u.sig=Ei(Fe.sign(null,Buffer.from(It(lc(u,[...ac,"ct"]))),n.signPriv)),u}function lc(n,e){let t={};for(let s of e)n[s]!==void 0&&(t[s]=n[s]);return t}function hf(n,{me:e,cardOf:t,replay:s,now:o=Date.now(),expectKind:r=null,expectRe:i=null}){if(!n||typeof n!="object"||n.v!==Qo||!n.id||!n.from||!n.ct||!n.sig||!n.epk||!n.iv)throw new ye("bad_envelope","not a Leftoff peer envelope",400);if(r&&n.kind!==r)throw new ye("bad_envelope","unexpected envelope kind",400);if(n.to!==e.machine)throw new ye("wrong_target","this envelope is for "+n.to+", not "+e.machine,400);let a=t(n.from);if(!a||!a.card)throw new ye("unknown_sender",n.from+" has no published key in this folder (has the sync caught up?)");if(a.trust==="key_changed")throw new ye("key_changed",n.from+"’s key changed since this Mac first saw it; confirm it in Settings → Privacy & security");if(a.trust==="invalid")throw new ye("unknown_sender",n.from+"’s published key file doesn’t verify");let l=!1;try{let u=Fe.createPublicKey({key:{kty:"OKP",crv:"Ed25519",x:a.card.sign},format:"jwk"});l=Fe.verify(null,Buffer.from(It(lc(n,[...ac,"ct"]))),u,ss(n.sig))}catch{}if(!l)throw new ye("bad_signature","the signature doesn’t match "+n.from+"’s key (tampered, or signed by someone else)");if(typeof n.exp!="number"||typeof n.iat!="number"||n.exp-n.iat>Fy||n.exp<n.iat)throw new ye("bad_envelope","bad lifetime",400);if(o>n.exp)throw new ye("expired","the command expired before it arrived ("+Math.round((o-n.exp)/1e3)+" s late; are both clocks right?)");if(n.iat>o+Uy)throw new ye("not_yet_valid","the command is from the future (check both Macs’ clocks)");if(i&&n.re!==i)throw new ye("bad_envelope","reply to a different request",400);if(!s.check(n.from,n.id,n.exp,o))throw new ye("replayed","this command was already used once");let c;try{let u=Yy(e.boxPriv,n.epk,ss(n.id),"leftoff-peer-v1|"+n.from+"|"+n.to),d=ss(n.ct),f=Fe.createDecipheriv("aes-256-gcm",u,ss(n.iv));f.setAAD(Buffer.from(It(lc(n,ac)))),f.setAuthTag(d.subarray(d.length-16)),c=JSON.parse(Buffer.concat([f.update(d.subarray(0,d.length-16)),f.final()]).toString("utf8"))}catch{throw new ye("decrypt_failed","couldn’t decrypt (wrong key?)",400)}return{env:n,body:c,sender:a}}function zy(n,e=pf.networkInterfaces()){let t=[];for(let[s,o]of Object.entries(e||{}))for(let r of o||[])r.internal||r.family!=="IPv4"&&r.family!==4||/^169\.254\./.test(r.address)||t.push({url:"http://"+r.address+":"+n,kind:/^100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\./.test(r.address)||/^(tailscale|utun)/.test(s)&&/^100\./.test(r.address)?"tailscale":"lan"});return t}var Qo,jy,Fy,Uy,$1,Ei,ss,ye,Hy,ac,cc,Oi,os=J(()=>{tl();ff();Qo=1,jy=60*1e3,Fy=120*1e3,Uy=30*1e3,$1=["ping","needs.list","ask.answer","session.reply","session.go","session.turns","session.resume","prs.list","session.prlink","session.file"],Ei=n=>Buffer.from(n).toString("base64url"),ss=n=>Buffer.from(String(n||""),"base64url"),ye=class extends Error{constructor(e,t,s=403){super(t||e),this.code=e,this.status=s}};Hy=n=>String(n).replace(/[^\w.-]/g,"_");ac=["v","kind","from","to","id","iat","exp","re","epk","iv"];cc=class{constructor({max:e=2e4}={}){this.seen=new Map,this.max=e}check(e,t,s,o=Date.now()){if(this.seen.size>this.max||this.seen.size%256===0)for(let[i,a]of this.seen)a<o&&this.seen.delete(i);let r=e+"|"+t;return this.seen.has(r)?!1:(this.seen.set(r,s),!0)}};Oi=class extends K1{constructor({engine:e,root:t,machine:s,cfg:o={},identity:r=null,idDir:i,fetchImpl:a=(...d)=>fetch(...d),now:l=()=>Date.now(),log:c=()=>{},version:u=null}){super(),this.engine=e,this.root=t,this.machine=s,this.cfg=o,this.fetch=a,this.now=l,this.log=c,this.version=u,this.id=r||Nn(s,{dir:i}),this.replay=new cc,this.status=new Map,this.remoteNeeds=new Map,this.cards={},this.cardsAt=0,this.server=null,this.relay=null,this.timer=null,this.stopped=!1}pcfg(){return this.engine&&this.engine.cfg&&this.engine.cfg.peers||this.cfg.peers||{}}get accepts(){return this.id.allowIncoming}listenPort(){let e=this.pcfg();if(process.env.LEFTOFF_PEER_PORT)return Number(process.env.LEFTOFF_PEER_PORT);if(e.port)return Number(e.port);let t=this.engine&&this.engine.cfg&&this.engine.cfg.port||7820;return Number(t)+1}relayUrl(){return process.env.LEFTOFF_RELAY||this.pcfg().relay||(this.hosted()?this.hosted().url():null)}hosted(){let e=this.engine&&this.engine.relayHost;return e&&e.active()?e:null}direct(){return process.env.LEFTOFF_PEER_DIRECT?process.env.LEFTOFF_PEER_DIRECT!=="0":this.pcfg().direct!==!1}async start(){this.publish(),this.accepts&&this.direct()&&await this.listen().catch(()=>{}),this.connectRelay();let e=Number(process.env.LEFTOFF_PEER_POLL_MS||this.pcfg().pollMs||4e3);this.timer=setInterval(()=>{this.poll().catch(()=>{})},e),this.timer.unref(),setTimeout(()=>this.poll().catch(()=>{}),300).unref()}async stop(){this.stopped=!0,this.timer&&clearInterval(this.timer),this.relay&&!this.relayShared&&this.relay.close(),this.server&&await new Promise(e=>this.server.close(()=>e())),this.server=null}addresses(){if(!this.direct())return[];let e=this.pcfg(),t=this.listenPort();return Array.isArray(e.addresses)&&e.addresses.length?e.addresses.map(s=>({url:String(s),kind:"configured"})):process.env.LEFTOFF_PEER_ADDRESSES?process.env.LEFTOFF_PEER_ADDRESSES.split(",").filter(Boolean).map(s=>({url:s.trim(),kind:"configured"})):zy(t)}publish(){let e=Ky(this.id,{addresses:this.addresses(),relay:!!this.relayUrl(),version:this.version,now:this.now(),anywhere:this.engine&&this.engine.relayHost?this.engine.relayHost.cardUser():null});try{let t=$y(this.root,e);return this.card=e,t}catch(t){return this.log("peer: could not publish machines/"+this.machine+".json: "+t.message),null}}async setAllowIncoming(e){if(this.id.state.allowIncoming=!!e,oc(this.id),this.publish(),e&&!this.server&&this.direct()&&await this.listen(),!e&&this.server){let t=this.server;this.server=null,await new Promise(s=>t.close(()=>s()))}return this.relay&&this.relay.hello(),this.emit("changed"),this.overview()}trust(e){let t=this.readCards()[e];if(!t||!t.valid)throw new ye("unknown_target","no valid key published for "+e,404);return this.id.pins[e]=t.card.fingerprint,oc(this.id),this.status.delete(e),this.emit("changed"),this.overview()}readCards({fresh:e=!1}={}){if(!e&&this.now()-this.cardsAt<1500)return this.cards;this.cards=Vs(this.root),this.cardsAt=this.now();let t=!1;for(let[s,o]of Object.entries(this.cards))s!==this.machine&&In(this.id,o)==="new"&&(t=!0);if(t)try{oc(this.id)}catch{}return this.cards}cardOf(e){let t=this.readCards()[e];return t?{card:t.card,trust:In(this.id,t,{pinNew:!1})==="unpinned"?"new":In(this.id,t,{pinNew:!1})}:null}usable(e){if(!e||e===this.machine)return{ok:!1,reason:"this machine"};let t=this.cardOf(e);if(!t)return{ok:!1,code:"no_key",reason:e+" hasn’t published a machine key yet (it needs Leftoff 0.5.2, and the sync has to catch up)"};if(t.trust==="invalid")return{ok:!1,code:"bad_key",reason:e+"’s key file doesn’t verify"};if(t.trust==="key_changed")return{ok:!1,code:"key_changed",reason:e+"’s key changed; confirm it in Settings → Privacy & security"};if(!t.card.accepts)return{ok:!1,code:"not_allowed",reason:e+" doesn’t allow other machines to answer or Go (Settings → Privacy & security, on "+e+")"};let s=this.status.get(e);return!t.card.addresses.length&&!(t.card.relay&&this.relayUrl())?{ok:!1,code:"no_route",reason:"no way to reach "+e+": it has no address and no relay is set"}:s&&s.reachable===!1?{ok:!1,code:"unreachable",reason:"can’t reach "+e+" right now"+(s.lastError?" ("+s.lastError+")":"")}:s?{ok:!0,via:s.via,reason:null}:{ok:!1,code:"checking",reason:"checking whether "+e+" is reachable…"}}overview(){let e=this.readCards({fresh:!0}),t=[];for(let[s,o]of Object.entries(e)){if(s===this.machine)continue;let r=In(this.id,o,{pinNew:!1}),i=this.status.get(s)||null;t.push({machine:s,hostname:o.card.hostname||null,version:o.card.version||null,fingerprint:o.card.fingerprint||null,valid:o.valid,trust:r==="unpinned"?"new":r,accepts:!!o.card.accepts,addresses:o.card.addresses||[],relay:!!o.card.relay,updatedAt:o.card.updatedAt||null,reachable:i?i.reachable:null,via:i?i.via:null,lastOkAt:i&&i.lastOkAt?new Date(i.lastOkAt).toISOString():null,lastError:i?i.lastError:null,usable:this.usable(s)})}return{machine:this.machine,fingerprint:this.id.fingerprint,allowIncoming:this.accepts,listening:this.server?this.listenPort():null,addresses:this.accepts?this.addresses():[],relay:this.relayUrl()?{url:this.relayUrl(),connected:!!(this.relay&&this.relay.connected)}:null,peers:t.sort((s,o)=>s.machine.localeCompare(o.machine))}}async receive(e,t){let s;try{s=hf(e,{me:this.id,cardOf:a=>this.cardOf(a),replay:this.replay,now:this.now(),expectKind:"req"})}catch(a){this.log("peer: refused an envelope from "+(e&&e.from)+" via "+t+": "+a.code);let l=e&&e.from?this.cardOf(e.from):null;if(l&&l.card&&l.trust!=="invalid"&&a.code!=="bad_signature"&&a.code!=="replayed")try{return rc({me:this.id,to:l.card,kind:"res",re:e.id,body:{ok:!1,error:{code:a.code,message:a.message,status:a.status}},now:this.now()})}catch{}return{v:Qo,kind:"err",code:a.code||"refused",message:a.message}}let{body:o,sender:r}=s,i;if(!this.accepts&&o.action!=="ping")i={ok:!1,error:{code:"not_allowed",status:403,message:this.machine+" doesn’t allow other machines to answer or Go here. Turn it on in Settings → Privacy & security on "+this.machine+"."}};else try{i={ok:!0,result:await this.execute(o,{from:e.from,transport:t})}}catch(a){i={ok:!1,error:{code:a.code||"error",status:a.status||500,message:a.message}}}return this.log("peer: "+o.action+" from "+e.from+" via "+t+" → "+(i.ok?"ok":i.error.code)),rc({me:this.id,to:r.card,kind:"res",re:e.id,body:i,now:this.now()})}async execute(e,{from:t,transport:s}){let o=this.engine,r={via:"peer",peer:t,transport:s};switch(e.action){case"ping":return{machine:this.machine,version:this.version,accepts:this.accepts};case"needs.list":return{items:o.needsList().filter(i=>i.session&&i.session.isLocal).map(i=>({...i,session:{id:i.session.id,title:i.session.title,machine:this.machine}}))};case"ask.answer":return await o.answerAsk(String(e.askId||""),e.body||{},r);case"session.reply":return await o.postReply(String(e.sessionId||""),e.text,r);case"session.go":return await o.jump(String(e.sessionId||""));case"session.turns":return await o.sessionTurns(String(e.sessionId||""),{limit:e.limit});case"session.resume":return await o.resume(String(e.sessionId||""),{fromPeer:!0});case"prs.list":return await o.prsOverview({view:e.view,fresh:!!e.fresh,fromPeer:!0});case"session.prlink":return await o.linkPr(String(e.sessionId||""),{ref:e.ref,unlink:!!e.unlink},{fromPeer:!0});case"session.file":return await o.sessionFile(String(e.sessionId||""),{path:e.path?String(e.path):null,plan:e.plan?String(e.plan):null,maxBytes:Qa,fromPeer:!0});default:throw new ye("unknown_action","unknown action "+e.action,400)}}async listen(){if(this.server)return;let e=process.env.LEFTOFF_PEER_BIND||this.pcfg().bind||"0.0.0.0",t=G1.createServer(async(s,o)=>{let r=(l,c)=>{let u=JSON.stringify(c);o.writeHead(l,{"content-type":"application/json","cache-control":"no-store"}),o.end(u)};if(s.method!=="POST"||s.url!=="/peer/v1/envelope")return r(404,{error:"not_found"});let i=0,a=[];s.on("data",l=>{i+=l.length,i>256*1024?s.destroy():a.push(l)}),s.on("end",async()=>{let l;try{l=JSON.parse(Buffer.concat(a).toString("utf8"))}catch{return r(400,{v:Qo,kind:"err",code:"bad_envelope"})}let c=await this.receive(l,"direct");r(c.kind==="err"?403:200,c)})});await new Promise((s,o)=>{t.once("error",o),t.listen(this.listenPort(),e,()=>{t.off("error",o),s()})}).catch(s=>{throw this.log("peer: could not listen on "+e+":"+this.listenPort()+": "+s.message),s}),this.server=t,this.log("peer: listening for other machines on "+e+":"+this.listenPort())}connectRelay(){let e=this.relayUrl();if(!(!e||this.relay)){if(!process.env.LEFTOFF_RELAY&&!this.pcfg().relay&&this.hosted()){this.hosted().client&&this.useRelay(this.hosted().client);return}this.relayShared=!1,this.relay=new Zo({url:e,id:this.id,log:this.log,onEnvelope:t=>this.receive(t,"relay"),accepts:()=>this.accepts}),this.relay.on("state",()=>this.emit("changed")),this.relay.connect()}}useRelay(e){this.relay&&!this.relayShared&&this.relay.close(),this.relay=e,this.relayShared=!0,e.onEnvelope=t=>this.receive(t,"relay"),this.onRelayState||(this.onRelayState=()=>this.emit("changed")),e.on("state",this.onRelayState),this.publish(),this.emit("changed")}dropRelay(e){this.relay===e&&(e.off("state",this.onRelayState),e.onEnvelope=null,this.relay=null,this.relayShared=!1,this.publish(),this.emit("changed"),this.connectRelay())}async request(e,t,s={},{timeoutMs:o=4e3,onlyVia:r=null}={}){let i=this.cardOf(e);if(!i)throw new ye("no_key",e+" hasn’t published a machine key yet",409);if(i.trust==="key_changed")throw new ye("key_changed",e+"’s key changed; confirm it in Settings → Privacy & security",409);if(i.trust==="invalid")throw new ye("bad_key",e+"’s key file doesn’t verify",409);let a=[],l=[];if(r!=="relay")for(let c of i.card.addresses||[])l.push({via:"direct",url:c.url||c});if(r!=="direct"&&this.relay&&i.card.relay&&l.push({via:"relay"}),!l.length)throw this.mark(e,!1,null,"no address and no relay"),new ye("no_route","no way to reach "+e+" (no address, no relay)",503);for(let c of l){let u=rc({me:this.id,to:i.card,kind:"req",body:{action:t,...s},now:this.now()}),d;try{c.via==="direct"?d=await(await this.fetch(String(c.url).replace(/\/$/,"")+"/peer/v1/envelope",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(u),signal:AbortSignal.timeout(o)})).json():d=await this.relay.request(e,u,o)}catch(h){a.push(c.via+": "+(h.code||h.cause&&h.cause.code||h.name||h.message));continue}if(d&&d.kind==="err")throw this.mark(e,!0,c.via,null),new ye(d.code||"refused",e+" refused the command: "+(d.message||d.code),403);let f=hf(d,{me:this.id,cardOf:h=>this.cardOf(h),replay:this.replay,now:this.now(),expectKind:"res",expectRe:u.id});if(this.mark(e,!0,c.via,null),!f.body.ok){let h=f.body.error||{};throw new ye(h.code||"error",h.message||"failed on "+e,h.status||409)}return{result:f.body.result,via:c.via}}throw this.mark(e,!1,null,a.join("; ")),new ye("unreachable","couldn’t reach "+e+" ("+a.join("; ")+")",503)}mark(e,t,s,o){let r=this.status.get(e),i={reachable:t,via:s,lastError:o,lastCheckedAt:this.now(),lastOkAt:t?this.now():r&&r.lastOkAt||null};this.status.set(e,i),(!r||r.reachable!==t||r.via!==s)&&this.emit("changed")}async poll(){if(!(this.stopped||this.polling)){this.polling=!0;try{let e=this.readCards({fresh:!0});for(let[t,s]of Object.entries(e))if(t!==this.machine){if(!s.valid||!s.card.accepts||In(this.id,s,{pinNew:!1})==="key_changed"){this.remoteNeeds.has(t)&&(this.remoteNeeds.delete(t),this.emit("needs",t));continue}try{let{result:o}=await this.request(t,"needs.list",{},{timeoutMs:2500}),r=this.remoteNeeds.get(t),i=JSON.stringify((o.items||[]).map(a=>[a.session.id,a.kind,a.ask&&a.ask.id,a.since]));this.remoteNeeds.set(t,{at:this.now(),items:o.items||[],sig:i}),(!r||r.sig!==i)&&this.emit("needs",t)}catch{this.remoteNeeds.has(t)&&(this.remoteNeeds.delete(t),this.emit("needs",t))}}}finally{this.polling=!1}}}needsFor(e,t){let s=this.remoteNeeds.get(t);return!s||this.now()-s.at>3e4?null:s.items.find(o=>o.session&&o.session.id===e)||null}machineForAsk(e){for(let[t,s]of this.remoteNeeds)if(s.items.some(o=>o.ask&&o.ask.id===e))return t;return null}}});import{execFile as W1,execFileSync as J1}from"node:child_process";import dc from"node:process";function q1(){if(Ci!==void 0)return Ci;try{Ci=J1("/bin/sh",["-c","command -v terminal-notifier"],{encoding:"utf8",timeout:1500}).trim()||null}catch{Ci=null}return Ci}function Wy({title:n,subtitle:e,message:t,sessionId:s,goBin:o,notifier:r=null}={}){let i=fc(n||"Leftoff",80),a=fc(e,120),l=fc(t,240);if(r){let c=["-title",i,"-subtitle",a,"-message",l||" ","-group","leftoff-"+fc(s,64)];return o&&/^[A-Za-z0-9_-]{6,64}$/.test(String(s||""))&&/^[\w./ -]+$/.test(o)&&c.push("-execute","'"+o.replace(/'/g,"")+"' go "+s),{cmd:r,args:c}}return{cmd:"osascript",args:["-e","on run argv","-e","display notification (item 1 of argv) with title (item 2 of argv) subtitle (item 3 of argv)","-e","end run",l||" ",i,a]}}function Jy(n,e){let t=e||"A Claude Code session";if(n.kind==="permission"&&n.ask){let s=n.ask.describe||{};return{subtitle:t+" needs your OK",message:(s.verb?s.verb+": ":"")+(s.detail||n.ask.tool)}}return n.kind==="question"&&n.ask?{subtitle:t+" has a question",message:((n.ask.questions||[])[0]||{}).question||"Claude is asking you something"}:n.kind==="terminal"?{subtitle:t+" needs you",message:n.message||"Waiting in the terminal"}:{subtitle:t+" asked you",message:n.question||""}}function qy(n,{platform:e=dc.platform,run:t=W1}={}){if(e!=="darwin"&&!dc.env.CHECKPOINTS_NOTIFY_CMD)return{ok:!1,skipped:"not macOS"};let s;dc.env.CHECKPOINTS_NOTIFY_CMD?s={cmd:dc.env.CHECKPOINTS_NOTIFY_CMD,args:Wy({...n}).args}:s=Wy({...n,notifier:n.notifier!==void 0?n.notifier:q1()});try{t(s.cmd,s.args,{timeout:5e3},()=>{})}catch(o){return{ok:!1,error:o.message}}return{ok:!0,via:s.cmd}}var Ci,fc,Vy=J(()=>{fc=(n,e)=>String(n??"").replace(/[\u0000-\u001f]+/g," ").replace(/\s+/g," ").trim().slice(0,e)});import Ue from"node:fs";import rs from"node:os";import De from"node:path";import{fileURLToPath as V1}from"node:url";import{spawnSync as X1}from"node:child_process";function cn(){return process.env.CLAUDE_SETTINGS_PATH||De.join(process.env.CLAUDE_CONFIG_DIR||De.join(rs.homedir(),".claude"),"settings.json")}function hc(n){return/^[A-Za-z0-9_./-]+$/.test(n)?n:JSON.stringify(n)}function nx(n,e=Ke){return hc(process.execPath)+" "+hc(e)+" hook "+n+" --tag="+ex}function sx(n=Ke){let e={};for(let t of Z1)e[t]=[{hooks:[{type:"command",command:nx(t,n),timeout:5,...Q1[t]||{}}]}];return e}function pc(n=cn()){try{let e=Ue.readFileSync(n,"utf8");return e.trim()?JSON.parse(e):{}}catch{return null}}function Ni(n=cn()){let e=pc(n),t=e?JSON.stringify(e.hooks||{}):"";return Xy.some(s=>t.includes(s))}function Mi(n,e,t=cn()){let s=pc(t),o=s||{},r=o.env?o.env[n]:void 0;return e==null&&r===void 0||r===e?!1:(s&&Ue.existsSync(t)?Ue.writeFileSync(t+".leftoff-backup-"+new Date().toISOString().replace(/[:.]/g,"-"),Ue.readFileSync(t,"utf8")):Ue.mkdirSync(De.dirname(t),{recursive:!0}),e==null?(delete o.env[n],Object.keys(o.env).length||delete o.env):o.env={...o.env||{},[n]:e},Ue.writeFileSync(t,JSON.stringify(o,null,2)+`
193
- `),!0)}function er(n=cn(),{bin:e=Ke}={}){let t={};if(Ue.existsSync(n)){let s=Ue.readFileSync(n,"utf8");t=s.trim()?JSON.parse(s):{};let o=n+".leftoff-backup-"+new Date().toISOString().replace(/[:.]/g,"-");Ue.writeFileSync(o,s)}else Ue.mkdirSync(De.dirname(n),{recursive:!0});Zy(t),t.hooks=t.hooks||{};for(let[s,o]of Object.entries(sx(e)))t.hooks[s]=[...t.hooks[s]||[],...o];return Ue.writeFileSync(n,JSON.stringify(t,null,2)+`
194
- `),n}function Zy(n){if(!n.hooks)return 0;let e=0;for(let t of Object.keys(n.hooks)){let s=(n.hooks[t]||[]).map(o=>{let r=(o.hooks||[]).length,i=(o.hooks||[]).filter(a=>!(a.command&&tx(a.command)));return e+=r-i.length,{...o,hooks:i}}).filter(o=>o.hooks.length);s.length?n.hooks[t]=s:delete n.hooks[t]}return Object.keys(n.hooks).length||delete n.hooks,e}function mc(n=cn()){if(!Ue.existsSync(n))return 0;let e=JSON.parse(Ue.readFileSync(n,"utf8")||"{}"),t=Zy(e);return t&&Ue.writeFileSync(n,JSON.stringify(e,null,2)+`
195
- `),t}function Mn(){return process.env.CHECKPOINTS_SERVICE_PLATFORM||process.platform}function Nt(n=Mn()){return n==="darwin"?De.join(rs.homedir(),"Library","LaunchAgents",Od+".plist"):De.join(process.env.XDG_CONFIG_HOME||De.join(rs.homedir(),".config"),"systemd","user",Ys)}function tr(n=Mn()){return n==="darwin"?De.join(rs.homedir(),"Library","LaunchAgents",kg+".plist"):De.join(process.env.XDG_CONFIG_HOME||De.join(rs.homedir(),".config"),"systemd","user",Cd)}function Ii(n){return String(n).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Ln({root:n=De.join(rs.homedir(),"checkpoints"),bin:e=Ke,platform:t=Mn()}={}){let s=De.resolve(n)!==De.join(rs.homedir(),"checkpoints"),o=De.join(n,".daemon.log");if(t==="darwin"){let r=s?` <key>EnvironmentVariables</key><dict><key>CHECKPOINTS_DIR</key><string>${Ii(n)}</string></dict>
190
+ end run`;df=()=>process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform});import $1 from"node:http";import z1 from"node:https";import Ci from"node:crypto";import{EventEmitter as By}from"node:events";function Hy(n,{opcode:e=1,mask:t=!1}={}){let s=Buffer.isBuffer(n)?n:Buffer.from(String(n)),o=s.length,r=[128|e],i=Buffer.alloc(0);if(o<126?r.push((t?128:0)|o):o<65536?(r.push((t?128:0)|126),i=Buffer.alloc(2),i.writeUInt16BE(o)):(r.push((t?128:0)|127),i=Buffer.alloc(8),i.writeBigUInt64BE(BigInt(o))),!t)return Buffer.concat([Buffer.from(r),i,s]);let a=Ci.randomBytes(4),l=Buffer.alloc(o);for(let c=0;c<o;c++)l[c]=s[c]^a[c&3];return Buffer.concat([Buffer.from(r),i,a,l])}function J1(n,{timeoutMs:e=5e3,hostHeader:t=null}={}){return new Promise((s,o)=>{let r=new URL(n),i=r.protocol==="wss:"||r.protocol==="https:",a=Ci.randomBytes(16).toString("base64"),l=(i?z1:$1).request({host:r.hostname,port:r.port||(i?443:80),path:(r.pathname||"/")+(r.search||""),method:"GET",headers:{connection:"Upgrade",upgrade:"websocket","sec-websocket-version":"13","sec-websocket-key":a,"user-agent":"leftoff-peer",...t?{host:t}:{}},timeout:e,...i&&t?{servername:t.replace(/:\d+$/,"")}:{}});l.on("upgrade",(c,u,d)=>{let f=Ci.createHash("sha1").update(a+Y1).digest("base64");if(c.headers["sec-websocket-accept"]!==f)return u.destroy(),o(new Error("bad websocket handshake"));u.setNoDelay(!0),s(new pf(u,{client:!0,head:d}))}),l.on("response",c=>{o(new Error("relay answered HTTP "+c.statusCode+" instead of upgrading")),c.resume()}),l.on("timeout",()=>{l.destroy(new Error("timed out"))}),l.on("error",o),l.end()})}var Y1,W1,hf,pf,Qo,mf=J(()=>{Y1="258EAFA5-E914-47DA-95CA-C5AB0DC85B11",W1=1024*1024;hf=class{constructor(e){this.buf=Buffer.alloc(0),this.onFrame=e,this.parts=[],this.partOp=0}push(e){for(this.buf=Buffer.concat([this.buf,e]);;){if(this.buf.length<2)return;let t=this.buf[0],s=this.buf[1],o=!!(t&128),r=t&15,i=!!(s&128),a=s&127,l=2;if(a===126){if(this.buf.length<4)return;a=this.buf.readUInt16BE(2),l=4}else if(a===127){if(this.buf.length<10)return;a=Number(this.buf.readBigUInt64BE(2)),l=10}if(a>W1)throw new Error("frame too large");let c=l+(i?4:0)+a;if(this.buf.length<c)return;let u=this.buf.subarray(l+(i?4:0),c);if(i){let d=this.buf.subarray(l,l+4),f=Buffer.alloc(a);for(let h=0;h<a;h++)f[h]=u[h]^d[h&3];u=f}else u=Buffer.from(u);if(this.buf=this.buf.subarray(c),r===0){this.parts.push(u),o&&(this.onFrame({opcode:this.partOp,payload:Buffer.concat(this.parts)}),this.parts=[]);continue}if(!o&&(r===1||r===2)){this.partOp=r,this.parts=[u];continue}this.onFrame({opcode:r,payload:u})}}},pf=class extends By{constructor(e,{client:t,head:s=null}){super(),this.socket=e,this.client=t,this.open=!0;let o=new hf(({opcode:r,payload:i})=>{r===1?this.emit("message",i.toString("utf8")):r===8?this.close():r===9&&this.raw(i,10)});e.on("data",r=>{try{o.push(r)}catch{this.close()}}),s&&s.length&&setImmediate(()=>{try{o.push(s)}catch{this.close()}}),e.on("close",()=>{this.open&&(this.open=!1,this.emit("close"))}),e.on("error",()=>{})}raw(e,t){this.open&&this.socket.write(Hy(e,{opcode:t,mask:this.client}))}send(e){this.raw(Buffer.from(typeof e=="string"?e:JSON.stringify(e)),1)}ping(){this.raw(Buffer.alloc(0),9)}close(){if(this.open){try{this.socket.write(Hy(Buffer.alloc(0),{opcode:8,mask:this.client}))}catch{}this.open=!1;try{this.socket.end()}catch{}setTimeout(()=>{try{this.socket.destroy()}catch{}},200).unref(),this.emit("close")}}};Qo=class extends By{constructor({url:e,id:t,log:s=()=>{},onEnvelope:o,accepts:r=()=>!0,user:i=null,hostHeader:a=null,helloExtra:l=()=>({})}){super(),this.url=e,this.id=t,this.log=s,this.onEnvelope=o,this.accepts=r,this.user=i,this.hostHeader=a,this.helloExtra=l,this.conn=null,this.connected=!1,this.closed=!1,this.backoff=1e3,this.waiting=new Map,this.calls=new Map,this.latencyMs=null,this.connectedAt=null,this.lastError=null,this.seq=0}async connect(){if(this.closed)return;let e;try{e=await J1(this.url,{hostHeader:this.hostHeader})}catch(t){return this.lastError=t.message,this.emit("state"),this.retry()}if(this.closed){e.close();return}this.conn=e,e.on("message",t=>this.onMessage(t)),e.on("close",()=>{let t=this.connected;this.connected=!1,this.conn=null,this.connectedAt=null;for(let[,s]of this.waiting)s.reject(Object.assign(new Error("relay connection closed"),{code:"relay_closed"}));this.waiting.clear();for(let[,s]of this.calls)s.reject(Object.assign(new Error("relay connection closed"),{code:"relay_closed"}));this.calls.clear(),t&&(this.log("peer: relay connection closed"),this.emit("state")),this.retry()}),clearInterval(this.keep),this.keep=setInterval(()=>this.ping(),Number(process.env.LEFTOFF_RELAY_PING_MS)||25e3),this.keep.unref()}ping(){this.conn&&(this.user?(this.pingAt=Date.now(),this.conn.send('{"t":"ping"}')):this.conn.ping())}retry(){this.closed||(clearInterval(this.keep),clearTimeout(this.retryTimer),this.retryTimer=setTimeout(()=>this.connect(),this.backoff),this.retryTimer.unref(),this.backoff=Math.min(3e4,this.backoff*2))}hello(){}send(e){return this.conn&&this.connected?(this.conn.send(e),!0):!1}ask(e,t=8e3){if(!this.conn||!this.connected)return Promise.reject(Object.assign(new Error("not connected to the relay"),{code:"relay_down"}));let s="c"+ ++this.seq+"."+Ci.randomBytes(4).toString("hex");return new Promise((o,r)=>{let i=setTimeout(()=>{this.calls.delete(s),r(Object.assign(new Error("the relay didn’t answer"),{code:"relay_timeout"}))},t);this.calls.set(s,{resolve:a=>{clearTimeout(i),o(a)},reject:a=>{clearTimeout(i),r(a)}}),this.conn.send({...e,id:s})})}onMessage(e){let t;try{t=JSON.parse(e)}catch{return}if(t.t==="pong"){this.pingAt&&(this.latencyMs=Date.now()-this.pingAt,this.pingAt=0,this.emit("latency",this.latencyMs));return}if(t.t==="challenge"){if(this.user&&t.user&&t.user!==this.user)return this.lastError="wrong_user",this.log("relay: that relay is for "+t.user+", not "+this.user),this.conn.close();let s=t.user?t.user+"|":"",o=Ci.sign(null,Buffer.from("leftoff-relay-hello|"+s+this.id.machine+"|"+t.nonce),this.id.signPriv).toString("base64url");this.conn.send({t:"hello",machine:this.id.machine,sign:this.id.signX,nonce:t.nonce,sig:o,...this.helloExtra()})}else if(t.t==="welcome")this.connected=!0,this.backoff=1e3,this.lastError=null,this.connectedAt=Date.now(),this.relayMachines=t.machines||null,this.log("peer: connected to the relay "+(this.hostHeader||this.url)),this.emit("state"),this.emit("welcome",t),this.user&&setTimeout(()=>this.ping(),50).unref();else if((t.t==="ack"||t.t==="devices"||t.t==="status")&&t.id&&this.calls.has(t.id)){let s=this.calls.get(t.id);this.calls.delete(t.id),s.resolve(t)}else if(t.t==="error"&&t.ref&&this.waiting.has(t.ref)){let s=this.waiting.get(t.ref);this.waiting.delete(t.ref),s.reject(Object.assign(new Error("relay: "+t.code),{code:"relay_"+t.code}))}else if(t.t==="error")this.lastError=t.code,this.log("peer: relay said "+t.code),this.emit("state");else if(t.t==="deliver"&&t.env){let s=t.env;if(s.kind==="res"||s.kind==="err"){let o=this.waiting.get(s.re||t.ref);o&&(this.waiting.delete(s.re||t.ref),o.resolve(s));return}if(!this.onEnvelope)return this.conn&&this.conn.send({t:"send",to:t.from,ref:s.id,env:{kind:"err",code:"not_allowed",re:s.id}});Promise.resolve(this.onEnvelope(s)).then(o=>{o&&this.conn&&this.conn.send({t:"send",to:t.from,ref:s.id,env:o.kind==="err"?{...o,re:s.id}:o})}).catch(()=>{})}else t.t&&this.emit("frame",t)}request(e,t,s=4e3){return!this.conn||!this.connected?Promise.reject(Object.assign(new Error("not connected to the relay"),{code:"relay_down"})):new Promise((o,r)=>{let i=setTimeout(()=>{this.waiting.delete(t.id),r(Object.assign(new Error("no answer through the relay"),{code:"relay_timeout"}))},s);this.waiting.set(t.id,{resolve:a=>{clearTimeout(i),o(a)},reject:a=>{clearTimeout(i),r(a)}}),this.conn.send({t:"send",to:e,ref:t.id,env:t})})}close(){this.closed=!0,clearInterval(this.keep),clearTimeout(this.retryTimer),this.conn&&this.conn.close()}}});var kf={};tn(kf,{ACTIONS:()=>X1,MAX_TTL_MS:()=>Ky,PROTOCOL:()=>er,PeerError:()=>ye,PeerHub:()=>Ii,ReplayGuard:()=>dc,SKEW_MS:()=>$y,TTL_MS:()=>Gy,canonical:()=>Mt,cardFile:()=>Wy,fingerprint:()=>wf,identityDir:()=>fc,loadIdentity:()=>Ln,localAddresses:()=>Xy,machinesDir:()=>lc,makeCard:()=>Jy,open:()=>gf,publishCard:()=>qy,readCards:()=>Zs,saveIdentity:()=>ic,seal:()=>ac,trustOf:()=>Mn,verifyCard:()=>Sf});import Zt from"node:fs";import yf from"node:os";import un from"node:path";import q1 from"node:http";import Ue from"node:crypto";import{EventEmitter as V1}from"node:events";function Mt(n){return n===null||typeof n!="object"?JSON.stringify(n===void 0?null:n):Array.isArray(n)?"["+n.map(Mt).join(",")+"]":"{"+Object.keys(n).filter(e=>n[e]!==void 0).sort().map(e=>JSON.stringify(e)+":"+Mt(n[e])).join(",")+"}"}function fc(n=process.env){return n.LEFTOFF_IDENTITY_DIR?n.LEFTOFF_IDENTITY_DIR:un.join(n.XDG_CONFIG_HOME||un.join(n.HOME||yf.homedir(),".config"),"checkpoints","identity")}function wf(n,e){return Ue.createHash("sha256").update(is(n)).update(is(e)).digest("hex").slice(0,16).toUpperCase().match(/.{4}/g).join("-")}function Z1(){let n=Ue.generateKeyPairSync("ed25519"),e=Ue.generateKeyPairSync("x25519");return{sign:n.privateKey.export({format:"jwk"}),box:e.privateKey.export({format:"jwk"})}}function Q1(n,e){let t=Ue.createPrivateKey({key:n.sign,format:"jwk"}),s=Ue.createPrivateKey({key:n.box,format:"jwk"});return{machine:n.machine,file:e,state:n,signPriv:t,boxPriv:s,signX:n.sign.x,boxX:n.box.x,fingerprint:wf(n.sign.x,n.box.x),get allowIncoming(){return!!n.allowIncoming},get pins(){return n.pins||(n.pins={})}}}function Ln(n,{dir:e=fc()}={}){let t=un.join(e,zy(n)+".json"),s=null;try{s=JSON.parse(Zt.readFileSync(t,"utf8"))}catch{}(!s||!s.sign||!s.box)&&(s={v:1,machine:n,created:new Date().toISOString(),...Z1(),allowIncoming:!1,pins:{}},Zt.mkdirSync(e,{recursive:!0,mode:448}),Yy(t,s));try{Zt.chmodSync(t,384)}catch{}return Q1(s,t)}function Yy(n,e){let t=n+"."+process.pid+".tmp";Zt.writeFileSync(t,JSON.stringify(e,null,2)+`
191
+ `,{mode:384}),Zt.renameSync(t,n)}function ic(n){Yy(n.file,n.state)}function lc(n){return un.join(n,"machines")}function Wy(n,e){return un.join(lc(n),zy(e)+".json")}function Jy(n,{addresses:e=[],relay:t=!1,version:s=null,hostname:o=yf.hostname(),now:r=Date.now(),anywhere:i=null}={}){let a={v:er,app:"leftoff",machine:n.machine,hostname:o,version:s,sign:n.signX,box:n.boxX,fingerprint:n.fingerprint,accepts:n.allowIncoming,addresses:n.allowIncoming?e:[],relay:!!(t&&n.allowIncoming),...i?{anywhere:i}:{},updatedAt:new Date(r).toISOString()};return a.sig=Ni(Ue.sign(null,Buffer.from(Mt(a)),n.signPriv)),a}function Sf(n){if(!n||typeof n!="object"||!n.sign||!n.box||!n.sig||!n.machine)return!1;try{let{sig:e,...t}=n,s=Ue.createPublicKey({key:{kty:"OKP",crv:"Ed25519",x:n.sign},format:"jwk"});return Ue.verify(null,Buffer.from(Mt(t)),s,is(e))&&wf(n.sign,n.box)===n.fingerprint}catch{return!1}}function qy(n,e){let t=Wy(n,e.machine);try{let o=JSON.parse(Zt.readFileSync(t,"utf8")),r=i=>{let{updatedAt:a,sig:l,...c}=i;return Mt(c)};if(r(o)===r(e)&&Sf(o))return{file:t,changed:!1}}catch{}Zt.mkdirSync(un.dirname(t),{recursive:!0});let s=un.join(un.dirname(t),"."+un.basename(t)+"."+process.pid+".tmp");return Zt.writeFileSync(s,JSON.stringify(e,null,2)+`
192
+ `),Zt.renameSync(s,t),{file:t,changed:!0}}function Zs(n){let e={},t=[];try{t=Zt.readdirSync(lc(n)).filter(s=>s.endsWith(".json")&&!s.startsWith("."))}catch{return e}for(let s of t)try{let o=JSON.parse(Zt.readFileSync(un.join(lc(n),s),"utf8"));e[o.machine||s.slice(0,-5)]={card:o,valid:Sf(o)}}catch{}return e}function Mn(n,e,{pinNew:t=!0}={}){if(!e||!e.valid)return"invalid";let s=n.pins[e.card.machine];return s?s===e.card.fingerprint?"trusted":"key_changed":t?(n.pins[e.card.machine]=e.card.fingerprint,"new"):"unpinned"}function Vy(n,e,t,s){let o=Ue.createPublicKey({key:{kty:"OKP",crv:"X25519",x:e},format:"jwk"}),r=Ue.diffieHellman({privateKey:n,publicKey:o});return Buffer.from(Ue.hkdfSync("sha256",r,t,Buffer.from(s),32))}function ac({me:n,to:e,kind:t="req",body:s,re:o=null,ttlMs:r=Gy,now:i=Date.now()}){if(!e||!e.box||!e.machine)throw new ye("unknown_target","no published key for that machine",400);let a=Ue.generateKeyPairSync("x25519"),l=Ni(Ue.randomBytes(16)),c=Ue.randomBytes(12),u={v:er,kind:t,from:n.machine,to:e.machine,id:l,iat:i,exp:i+r,re:o||void 0,epk:a.publicKey.export({format:"jwk"}).x,iv:Ni(c)},d=Vy(a.privateKey,e.box,is(l),"leftoff-peer-v1|"+u.from+"|"+u.to),f=Ue.createCipheriv("aes-256-gcm",d,c);f.setAAD(Buffer.from(Mt(uc(u,cc))));let h=Buffer.concat([f.update(Buffer.from(JSON.stringify(s))),f.final(),f.getAuthTag()]);return u.ct=Ni(h),u.sig=Ni(Ue.sign(null,Buffer.from(Mt(uc(u,[...cc,"ct"]))),n.signPriv)),u}function uc(n,e){let t={};for(let s of e)n[s]!==void 0&&(t[s]=n[s]);return t}function gf(n,{me:e,cardOf:t,replay:s,now:o=Date.now(),expectKind:r=null,expectRe:i=null}){if(!n||typeof n!="object"||n.v!==er||!n.id||!n.from||!n.ct||!n.sig||!n.epk||!n.iv)throw new ye("bad_envelope","not a Leftoff peer envelope",400);if(r&&n.kind!==r)throw new ye("bad_envelope","unexpected envelope kind",400);if(n.to!==e.machine)throw new ye("wrong_target","this envelope is for "+n.to+", not "+e.machine,400);let a=t(n.from);if(!a||!a.card)throw new ye("unknown_sender",n.from+" has no published key in this folder (has the sync caught up?)");if(a.trust==="key_changed")throw new ye("key_changed",n.from+"’s key changed since this Mac first saw it; confirm it in Settings → Privacy & security");if(a.trust==="invalid")throw new ye("unknown_sender",n.from+"’s published key file doesn’t verify");let l=!1;try{let u=Ue.createPublicKey({key:{kty:"OKP",crv:"Ed25519",x:a.card.sign},format:"jwk"});l=Ue.verify(null,Buffer.from(Mt(uc(n,[...cc,"ct"]))),u,is(n.sig))}catch{}if(!l)throw new ye("bad_signature","the signature doesn’t match "+n.from+"’s key (tampered, or signed by someone else)");if(typeof n.exp!="number"||typeof n.iat!="number"||n.exp-n.iat>Ky||n.exp<n.iat)throw new ye("bad_envelope","bad lifetime",400);if(o>n.exp)throw new ye("expired","the command expired before it arrived ("+Math.round((o-n.exp)/1e3)+" s late; are both clocks right?)");if(n.iat>o+$y)throw new ye("not_yet_valid","the command is from the future (check both Macs’ clocks)");if(i&&n.re!==i)throw new ye("bad_envelope","reply to a different request",400);if(!s.check(n.from,n.id,n.exp,o))throw new ye("replayed","this command was already used once");let c;try{let u=Vy(e.boxPriv,n.epk,is(n.id),"leftoff-peer-v1|"+n.from+"|"+n.to),d=is(n.ct),f=Ue.createDecipheriv("aes-256-gcm",u,is(n.iv));f.setAAD(Buffer.from(Mt(uc(n,cc)))),f.setAuthTag(d.subarray(d.length-16)),c=JSON.parse(Buffer.concat([f.update(d.subarray(0,d.length-16)),f.final()]).toString("utf8"))}catch{throw new ye("decrypt_failed","couldn’t decrypt (wrong key?)",400)}return{env:n,body:c,sender:a}}function Xy(n,e=yf.networkInterfaces()){let t=[];for(let[s,o]of Object.entries(e||{}))for(let r of o||[])r.internal||r.family!=="IPv4"&&r.family!==4||/^169\.254\./.test(r.address)||t.push({url:"http://"+r.address+":"+n,kind:/^100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\./.test(r.address)||/^(tailscale|utun)/.test(s)&&/^100\./.test(r.address)?"tailscale":"lan"});return t}var er,Gy,Ky,$y,X1,Ni,is,ye,zy,cc,dc,Ii,as=J(()=>{sl();mf();er=1,Gy=60*1e3,Ky=120*1e3,$y=30*1e3,X1=["ping","needs.list","ask.answer","session.reply","session.go","session.turns","session.resume","prs.list","session.prlink","session.file"],Ni=n=>Buffer.from(n).toString("base64url"),is=n=>Buffer.from(String(n||""),"base64url"),ye=class extends Error{constructor(e,t,s=403){super(t||e),this.code=e,this.status=s}};zy=n=>String(n).replace(/[^\w.-]/g,"_");cc=["v","kind","from","to","id","iat","exp","re","epk","iv"];dc=class{constructor({max:e=2e4}={}){this.seen=new Map,this.max=e}check(e,t,s,o=Date.now()){if(this.seen.size>this.max||this.seen.size%256===0)for(let[i,a]of this.seen)a<o&&this.seen.delete(i);let r=e+"|"+t;return this.seen.has(r)?!1:(this.seen.set(r,s),!0)}};Ii=class extends V1{constructor({engine:e,root:t,machine:s,cfg:o={},identity:r=null,idDir:i,fetchImpl:a=(...d)=>fetch(...d),now:l=()=>Date.now(),log:c=()=>{},version:u=null}){super(),this.engine=e,this.root=t,this.machine=s,this.cfg=o,this.fetch=a,this.now=l,this.log=c,this.version=u,this.id=r||Ln(s,{dir:i}),this.replay=new dc,this.status=new Map,this.remoteNeeds=new Map,this.cards={},this.cardsAt=0,this.server=null,this.relay=null,this.timer=null,this.stopped=!1}pcfg(){return this.engine&&this.engine.cfg&&this.engine.cfg.peers||this.cfg.peers||{}}get accepts(){return this.id.allowIncoming}listenPort(){let e=this.pcfg();if(process.env.LEFTOFF_PEER_PORT)return Number(process.env.LEFTOFF_PEER_PORT);if(e.port)return Number(e.port);let t=this.engine&&this.engine.cfg&&this.engine.cfg.port||7820;return Number(t)+1}relayUrl(){return process.env.LEFTOFF_RELAY||this.pcfg().relay||(this.hosted()?this.hosted().url():null)}hosted(){let e=this.engine&&this.engine.relayHost;return e&&e.active()?e:null}direct(){return process.env.LEFTOFF_PEER_DIRECT?process.env.LEFTOFF_PEER_DIRECT!=="0":this.pcfg().direct!==!1}async start(){this.publish(),this.accepts&&this.direct()&&await this.listen().catch(()=>{}),this.connectRelay();let e=Number(process.env.LEFTOFF_PEER_POLL_MS||this.pcfg().pollMs||4e3);this.timer=setInterval(()=>{this.poll().catch(()=>{})},e),this.timer.unref(),setTimeout(()=>this.poll().catch(()=>{}),300).unref()}async stop(){this.stopped=!0,this.timer&&clearInterval(this.timer),this.relay&&!this.relayShared&&this.relay.close(),this.server&&await new Promise(e=>this.server.close(()=>e())),this.server=null}addresses(){if(!this.direct())return[];let e=this.pcfg(),t=this.listenPort();return Array.isArray(e.addresses)&&e.addresses.length?e.addresses.map(s=>({url:String(s),kind:"configured"})):process.env.LEFTOFF_PEER_ADDRESSES?process.env.LEFTOFF_PEER_ADDRESSES.split(",").filter(Boolean).map(s=>({url:s.trim(),kind:"configured"})):Xy(t)}publish(){let e=Jy(this.id,{addresses:this.addresses(),relay:!!this.relayUrl(),version:this.version,now:this.now(),anywhere:this.engine&&this.engine.relayHost?this.engine.relayHost.cardUser():null});try{let t=qy(this.root,e);return this.card=e,t}catch(t){return this.log("peer: could not publish machines/"+this.machine+".json: "+t.message),null}}async setAllowIncoming(e){if(this.id.state.allowIncoming=!!e,ic(this.id),this.publish(),e&&!this.server&&this.direct()&&await this.listen(),!e&&this.server){let t=this.server;this.server=null,await new Promise(s=>t.close(()=>s()))}return this.relay&&this.relay.hello(),this.emit("changed"),this.overview()}trust(e){let t=this.readCards()[e];if(!t||!t.valid)throw new ye("unknown_target","no valid key published for "+e,404);return this.id.pins[e]=t.card.fingerprint,ic(this.id),this.status.delete(e),this.emit("changed"),this.overview()}readCards({fresh:e=!1}={}){if(!e&&this.now()-this.cardsAt<1500)return this.cards;this.cards=Zs(this.root),this.cardsAt=this.now();let t=!1;for(let[s,o]of Object.entries(this.cards))s!==this.machine&&Mn(this.id,o)==="new"&&(t=!0);if(t)try{ic(this.id)}catch{}return this.cards}cardOf(e){let t=this.readCards()[e];return t?{card:t.card,trust:Mn(this.id,t,{pinNew:!1})==="unpinned"?"new":Mn(this.id,t,{pinNew:!1})}:null}usable(e){if(!e||e===this.machine)return{ok:!1,reason:"this machine"};let t=this.cardOf(e);if(!t)return{ok:!1,code:"no_key",reason:e+" hasn’t published a machine key yet (it needs Leftoff 0.5.2, and the sync has to catch up)"};if(t.trust==="invalid")return{ok:!1,code:"bad_key",reason:e+"’s key file doesn’t verify"};if(t.trust==="key_changed")return{ok:!1,code:"key_changed",reason:e+"’s key changed; confirm it in Settings → Privacy & security"};if(!t.card.accepts)return{ok:!1,code:"not_allowed",reason:e+" doesn’t allow other machines to answer or Go (Settings → Privacy & security, on "+e+")"};let s=this.status.get(e);return!t.card.addresses.length&&!(t.card.relay&&this.relayUrl())?{ok:!1,code:"no_route",reason:"no way to reach "+e+": it has no address and no relay is set"}:s&&s.reachable===!1?{ok:!1,code:"unreachable",reason:"can’t reach "+e+" right now"+(s.lastError?" ("+s.lastError+")":"")}:s?{ok:!0,via:s.via,reason:null}:{ok:!1,code:"checking",reason:"checking whether "+e+" is reachable…"}}overview(){let e=this.readCards({fresh:!0}),t=[];for(let[s,o]of Object.entries(e)){if(s===this.machine)continue;let r=Mn(this.id,o,{pinNew:!1}),i=this.status.get(s)||null;t.push({machine:s,hostname:o.card.hostname||null,version:o.card.version||null,fingerprint:o.card.fingerprint||null,valid:o.valid,trust:r==="unpinned"?"new":r,accepts:!!o.card.accepts,addresses:o.card.addresses||[],relay:!!o.card.relay,updatedAt:o.card.updatedAt||null,reachable:i?i.reachable:null,via:i?i.via:null,lastOkAt:i&&i.lastOkAt?new Date(i.lastOkAt).toISOString():null,lastError:i?i.lastError:null,usable:this.usable(s)})}return{machine:this.machine,fingerprint:this.id.fingerprint,allowIncoming:this.accepts,listening:this.server?this.listenPort():null,addresses:this.accepts?this.addresses():[],relay:this.relayUrl()?{url:this.relayUrl(),connected:!!(this.relay&&this.relay.connected)}:null,peers:t.sort((s,o)=>s.machine.localeCompare(o.machine))}}async receive(e,t){let s;try{s=gf(e,{me:this.id,cardOf:a=>this.cardOf(a),replay:this.replay,now:this.now(),expectKind:"req"})}catch(a){this.log("peer: refused an envelope from "+(e&&e.from)+" via "+t+": "+a.code);let l=e&&e.from?this.cardOf(e.from):null;if(l&&l.card&&l.trust!=="invalid"&&a.code!=="bad_signature"&&a.code!=="replayed")try{return ac({me:this.id,to:l.card,kind:"res",re:e.id,body:{ok:!1,error:{code:a.code,message:a.message,status:a.status}},now:this.now()})}catch{}return{v:er,kind:"err",code:a.code||"refused",message:a.message}}let{body:o,sender:r}=s,i;if(!this.accepts&&o.action!=="ping")i={ok:!1,error:{code:"not_allowed",status:403,message:this.machine+" doesn’t allow other machines to answer or Go here. Turn it on in Settings → Privacy & security on "+this.machine+"."}};else try{i={ok:!0,result:await this.execute(o,{from:e.from,transport:t})}}catch(a){i={ok:!1,error:{code:a.code||"error",status:a.status||500,message:a.message}}}return this.log("peer: "+o.action+" from "+e.from+" via "+t+" → "+(i.ok?"ok":i.error.code)),ac({me:this.id,to:r.card,kind:"res",re:e.id,body:i,now:this.now()})}async execute(e,{from:t,transport:s}){let o=this.engine,r={via:"peer",peer:t,transport:s};switch(e.action){case"ping":return{machine:this.machine,version:this.version,accepts:this.accepts};case"needs.list":return{items:o.needsList().filter(i=>i.session&&i.session.isLocal).map(i=>({...i,session:{id:i.session.id,title:i.session.title,machine:this.machine}}))};case"ask.answer":return await o.answerAsk(String(e.askId||""),e.body||{},r);case"session.reply":return await o.postReply(String(e.sessionId||""),e.text,r);case"session.go":return await o.jump(String(e.sessionId||""));case"session.turns":return await o.sessionTurns(String(e.sessionId||""),{limit:e.limit});case"session.resume":return await o.resume(String(e.sessionId||""),{fromPeer:!0});case"prs.list":return await o.prsOverview({view:e.view,fresh:!!e.fresh,fromPeer:!0});case"session.prlink":return await o.linkPr(String(e.sessionId||""),{ref:e.ref,unlink:!!e.unlink},{fromPeer:!0});case"session.file":return await o.sessionFile(String(e.sessionId||""),{path:e.path?String(e.path):null,plan:e.plan?String(e.plan):null,maxBytes:tl,fromPeer:!0});default:throw new ye("unknown_action","unknown action "+e.action,400)}}async listen(){if(this.server)return;let e=process.env.LEFTOFF_PEER_BIND||this.pcfg().bind||"0.0.0.0",t=q1.createServer(async(s,o)=>{let r=(l,c)=>{let u=JSON.stringify(c);o.writeHead(l,{"content-type":"application/json","cache-control":"no-store"}),o.end(u)};if(s.method!=="POST"||s.url!=="/peer/v1/envelope")return r(404,{error:"not_found"});let i=0,a=[];s.on("data",l=>{i+=l.length,i>256*1024?s.destroy():a.push(l)}),s.on("end",async()=>{let l;try{l=JSON.parse(Buffer.concat(a).toString("utf8"))}catch{return r(400,{v:er,kind:"err",code:"bad_envelope"})}let c=await this.receive(l,"direct");r(c.kind==="err"?403:200,c)})});await new Promise((s,o)=>{t.once("error",o),t.listen(this.listenPort(),e,()=>{t.off("error",o),s()})}).catch(s=>{throw this.log("peer: could not listen on "+e+":"+this.listenPort()+": "+s.message),s}),this.server=t,this.log("peer: listening for other machines on "+e+":"+this.listenPort())}connectRelay(){let e=this.relayUrl();if(!(!e||this.relay)){if(!process.env.LEFTOFF_RELAY&&!this.pcfg().relay&&this.hosted()){this.hosted().client&&this.useRelay(this.hosted().client);return}this.relayShared=!1,this.relay=new Qo({url:e,id:this.id,log:this.log,onEnvelope:t=>this.receive(t,"relay"),accepts:()=>this.accepts}),this.relay.on("state",()=>this.emit("changed")),this.relay.connect()}}useRelay(e){this.relay&&!this.relayShared&&this.relay.close(),this.relay=e,this.relayShared=!0,e.onEnvelope=t=>this.receive(t,"relay"),this.onRelayState||(this.onRelayState=()=>this.emit("changed")),e.on("state",this.onRelayState),this.publish(),this.emit("changed")}dropRelay(e){this.relay===e&&(e.off("state",this.onRelayState),e.onEnvelope=null,this.relay=null,this.relayShared=!1,this.publish(),this.emit("changed"),this.connectRelay())}async request(e,t,s={},{timeoutMs:o=4e3,onlyVia:r=null}={}){let i=this.cardOf(e);if(!i)throw new ye("no_key",e+" hasn’t published a machine key yet",409);if(i.trust==="key_changed")throw new ye("key_changed",e+"’s key changed; confirm it in Settings → Privacy & security",409);if(i.trust==="invalid")throw new ye("bad_key",e+"’s key file doesn’t verify",409);let a=[],l=[];if(r!=="relay")for(let c of i.card.addresses||[])l.push({via:"direct",url:c.url||c});if(r!=="direct"&&this.relay&&i.card.relay&&l.push({via:"relay"}),!l.length)throw this.mark(e,!1,null,"no address and no relay"),new ye("no_route","no way to reach "+e+" (no address, no relay)",503);for(let c of l){let u=ac({me:this.id,to:i.card,kind:"req",body:{action:t,...s},now:this.now()}),d;try{c.via==="direct"?d=await(await this.fetch(String(c.url).replace(/\/$/,"")+"/peer/v1/envelope",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(u),signal:AbortSignal.timeout(o)})).json():d=await this.relay.request(e,u,o)}catch(h){a.push(c.via+": "+(h.code||h.cause&&h.cause.code||h.name||h.message));continue}if(d&&d.kind==="err")throw this.mark(e,!0,c.via,null),new ye(d.code||"refused",e+" refused the command: "+(d.message||d.code),403);let f=gf(d,{me:this.id,cardOf:h=>this.cardOf(h),replay:this.replay,now:this.now(),expectKind:"res",expectRe:u.id});if(this.mark(e,!0,c.via,null),!f.body.ok){let h=f.body.error||{};throw new ye(h.code||"error",h.message||"failed on "+e,h.status||409)}return{result:f.body.result,via:c.via}}throw this.mark(e,!1,null,a.join("; ")),new ye("unreachable","couldn’t reach "+e+" ("+a.join("; ")+")",503)}mark(e,t,s,o){let r=this.status.get(e),i={reachable:t,via:s,lastError:o,lastCheckedAt:this.now(),lastOkAt:t?this.now():r&&r.lastOkAt||null};this.status.set(e,i),(!r||r.reachable!==t||r.via!==s)&&this.emit("changed")}async poll(){if(!(this.stopped||this.polling)){this.polling=!0;try{let e=this.readCards({fresh:!0});for(let[t,s]of Object.entries(e))if(t!==this.machine){if(!s.valid||!s.card.accepts||Mn(this.id,s,{pinNew:!1})==="key_changed"){this.remoteNeeds.has(t)&&(this.remoteNeeds.delete(t),this.emit("needs",t));continue}try{let{result:o}=await this.request(t,"needs.list",{},{timeoutMs:2500}),r=this.remoteNeeds.get(t),i=JSON.stringify((o.items||[]).map(a=>[a.session.id,a.kind,a.ask&&a.ask.id,a.since]));this.remoteNeeds.set(t,{at:this.now(),items:o.items||[],sig:i}),(!r||r.sig!==i)&&this.emit("needs",t)}catch{this.remoteNeeds.has(t)&&(this.remoteNeeds.delete(t),this.emit("needs",t))}}}finally{this.polling=!1}}}needsFor(e,t){let s=this.remoteNeeds.get(t);return!s||this.now()-s.at>3e4?null:s.items.find(o=>o.session&&o.session.id===e)||null}machineForAsk(e){for(let[t,s]of this.remoteNeeds)if(s.items.some(o=>o.ask&&o.ask.id===e))return t;return null}}});import{execFile as ex,execFileSync as tx}from"node:child_process";import hc from"node:process";function nx(){if(Mi!==void 0)return Mi;try{Mi=tx("/bin/sh",["-c","command -v terminal-notifier"],{encoding:"utf8",timeout:1500}).trim()||null}catch{Mi=null}return Mi}function Zy({title:n,subtitle:e,message:t,sessionId:s,goBin:o,notifier:r=null}={}){let i=pc(n||"Leftoff",80),a=pc(e,120),l=pc(t,240);if(r){let c=["-title",i,"-subtitle",a,"-message",l||" ","-group","leftoff-"+pc(s,64)];return o&&/^[A-Za-z0-9_-]{6,64}$/.test(String(s||""))&&/^[\w./ -]+$/.test(o)&&c.push("-execute","'"+o.replace(/'/g,"")+"' go "+s),{cmd:r,args:c}}return{cmd:"osascript",args:["-e","on run argv","-e","display notification (item 1 of argv) with title (item 2 of argv) subtitle (item 3 of argv)","-e","end run",l||" ",i,a]}}function Qy(n,e){let t=e||"A Claude Code session";if(n.kind==="permission"&&n.ask){let s=n.ask.describe||{};return{subtitle:t+" needs your OK",message:(s.verb?s.verb+": ":"")+(s.detail||n.ask.tool)}}return n.kind==="question"&&n.ask?{subtitle:t+" has a question",message:((n.ask.questions||[])[0]||{}).question||"Claude is asking you something"}:n.kind==="terminal"?{subtitle:t+" needs you",message:n.message||"Waiting in the terminal"}:{subtitle:t+" asked you",message:n.question||""}}function ew(n,{platform:e=hc.platform,run:t=ex}={}){if(e!=="darwin"&&!hc.env.CHECKPOINTS_NOTIFY_CMD)return{ok:!1,skipped:"not macOS"};let s;hc.env.CHECKPOINTS_NOTIFY_CMD?s={cmd:hc.env.CHECKPOINTS_NOTIFY_CMD,args:Zy({...n}).args}:s=Zy({...n,notifier:n.notifier!==void 0?n.notifier:nx()});try{t(s.cmd,s.args,{timeout:5e3},()=>{})}catch(o){return{ok:!1,error:o.message}}return{ok:!0,via:s.cmd}}var Mi,pc,tw=J(()=>{pc=(n,e)=>String(n??"").replace(/[\u0000-\u001f]+/g," ").replace(/\s+/g," ").trim().slice(0,e)});import He from"node:fs";import ls from"node:os";import Re from"node:path";import{fileURLToPath as sx}from"node:url";import{spawnSync as ox}from"node:child_process";function dn(){return process.env.CLAUDE_SETTINGS_PATH||Re.join(process.env.CLAUDE_CONFIG_DIR||Re.join(ls.homedir(),".claude"),"settings.json")}function mc(n){return/^[A-Za-z0-9_./-]+$/.test(n)?n:JSON.stringify(n)}function cx(n,e=$e){return mc(process.execPath)+" "+mc(e)+" hook "+n+" --tag="+ax}function ux(n=$e){let e={};for(let t of rx)e[t]=[{hooks:[{type:"command",command:cx(t,n),timeout:5,...ix[t]||{}}]}];return e}function gc(n=dn()){try{let e=He.readFileSync(n,"utf8");return e.trim()?JSON.parse(e):{}}catch{return null}}function Di(n=dn()){let e=gc(n),t=e?JSON.stringify(e.hooks||{}):"";return nw.some(s=>t.includes(s))}function Ri(n,e,t=dn()){let s=gc(t),o=s||{},r=o.env?o.env[n]:void 0;return e==null&&r===void 0||r===e?!1:(s&&He.existsSync(t)?He.writeFileSync(t+".leftoff-backup-"+new Date().toISOString().replace(/[:.]/g,"-"),He.readFileSync(t,"utf8")):He.mkdirSync(Re.dirname(t),{recursive:!0}),e==null?(delete o.env[n],Object.keys(o.env).length||delete o.env):o.env={...o.env||{},[n]:e},He.writeFileSync(t,JSON.stringify(o,null,2)+`
193
+ `),!0)}function tr(n=dn(),{bin:e=$e}={}){let t={};if(He.existsSync(n)){let s=He.readFileSync(n,"utf8");t=s.trim()?JSON.parse(s):{};let o=n+".leftoff-backup-"+new Date().toISOString().replace(/[:.]/g,"-");He.writeFileSync(o,s)}else He.mkdirSync(Re.dirname(n),{recursive:!0});sw(t),t.hooks=t.hooks||{};for(let[s,o]of Object.entries(ux(e)))t.hooks[s]=[...t.hooks[s]||[],...o];return He.writeFileSync(n,JSON.stringify(t,null,2)+`
194
+ `),n}function sw(n){if(!n.hooks)return 0;let e=0;for(let t of Object.keys(n.hooks)){let s=(n.hooks[t]||[]).map(o=>{let r=(o.hooks||[]).length,i=(o.hooks||[]).filter(a=>!(a.command&&lx(a.command)));return e+=r-i.length,{...o,hooks:i}}).filter(o=>o.hooks.length);s.length?n.hooks[t]=s:delete n.hooks[t]}return Object.keys(n.hooks).length||delete n.hooks,e}function yc(n=dn()){if(!He.existsSync(n))return 0;let e=JSON.parse(He.readFileSync(n,"utf8")||"{}"),t=sw(e);return t&&He.writeFileSync(n,JSON.stringify(e,null,2)+`
195
+ `),t}function Dn(){return process.env.CHECKPOINTS_SERVICE_PLATFORM||process.platform}function Lt(n=Dn()){return n==="darwin"?Re.join(ls.homedir(),"Library","LaunchAgents",Nd+".plist"):Re.join(process.env.XDG_CONFIG_HOME||Re.join(ls.homedir(),".config"),"systemd","user",Ws)}function nr(n=Dn()){return n==="darwin"?Re.join(ls.homedir(),"Library","LaunchAgents",Tg+".plist"):Re.join(process.env.XDG_CONFIG_HOME||Re.join(ls.homedir(),".config"),"systemd","user",Id)}function Li(n){return String(n).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Rn({root:n=Re.join(ls.homedir(),"checkpoints"),bin:e=$e,platform:t=Dn()}={}){let s=Re.resolve(n)!==Re.join(ls.homedir(),"checkpoints"),o=Re.join(n,".daemon.log");if(t==="darwin"){let r=s?` <key>EnvironmentVariables</key><dict><key>CHECKPOINTS_DIR</key><string>${Li(n)}</string></dict>
196
196
  `:"";return`<?xml version="1.0" encoding="UTF-8"?>
197
197
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
198
198
  <plist version="1.0"><dict>
199
- <key>Label</key><string>${Od}</string>
200
- <key>ProgramArguments</key><array><string>${Ii(process.execPath)}</string><string>${Ii(e)}</string><string>serve</string></array>
199
+ <key>Label</key><string>${Nd}</string>
200
+ <key>ProgramArguments</key><array><string>${Li(process.execPath)}</string><string>${Li(e)}</string><string>serve</string></array>
201
201
  ${r} <key>ProcessType</key><string>Interactive</string>
202
202
  <key>RunAtLoad</key><true/>
203
203
  <key>KeepAlive</key><true/>
204
- <key>StandardOutPath</key><string>${Ii(o)}</string>
205
- <key>StandardErrorPath</key><string>${Ii(o)}</string>
204
+ <key>StandardOutPath</key><string>${Li(o)}</string>
205
+ <key>StandardErrorPath</key><string>${Li(o)}</string>
206
206
  </dict></plist>
207
207
  `}return`[Unit]
208
208
  Description=Leftoff daemon (Claude Code session tracker)
209
209
 
210
210
  [Service]
211
- ExecStart=${hc(process.execPath)} ${hc(e)} serve
211
+ ExecStart=${mc(process.execPath)} ${mc(e)} serve
212
212
  ${s?"Environment="+JSON.stringify("CHECKPOINTS_DIR="+n)+`
213
213
  `:""}Restart=on-failure
214
214
 
215
215
  [Install]
216
216
  WantedBy=default.target
217
- `}function Qy(){return Mn()==="darwin"?"launchctl load -w "+Nt():"systemctl --user daemon-reload && systemctl --user enable --now "+Ys}function Xs(n=Mn()){let e=Nt(n);return n==="darwin"?[["launchctl","unload",e],["launchctl","load","-w",e]]:[["systemctl","--user","daemon-reload"],["systemctl","--user","enable",Ys],["systemctl","--user","restart",Ys]]}function ox(n=Mn()){return n==="darwin"?[["launchctl","unload","-w",tr(n)]]:[["systemctl","--user","disable","--now",Cd]]}function wf(n){let e=String(n||""),t=e.match(/<key>CHECKPOINTS_DIR<\/key>\s*<string>([^<]*)<\/string>/)||e.match(/^Environment="?CHECKPOINTS_DIR=([^"\n]*)"?$/m);return t?t[1].replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&"):null}function gc({root:n,bin:e=Ke,platform:t=Mn(),dryRun:s=!1,run:o=X1}={}){let r=tr(t);if(!Ue.existsSync(r))return null;let i=Nt(t),a=Ue.readFileSync(r,"utf8"),l=wf(a)||n||De.join(rs.homedir(),"checkpoints"),c=[];if(!s)for(let u of ox(t)){let d=o(u[0],u.slice(1),{encoding:"utf8"});c.push({cmd:u,code:d.status,stderr:String(d.stderr||"").trim()})}return Ue.mkdirSync(De.dirname(i),{recursive:!0}),Ue.writeFileSync(i,Ln({root:l,bin:e,platform:t})),Ue.unlinkSync(r),{from:r,to:i,root:l,steps:c,dryRun:s}}var Ke,aC,Z1,Q1,ex,Xy,tx,is=J(()=>{_t();Ke=De.resolve(De.dirname(V1(import.meta.url)),"..","bin","leftoff.js"),aC=De.join(De.dirname(Ke),"checkpoints.js"),Z1=["SessionStart","UserPromptSubmit","Stop","Notification","SessionEnd","PermissionRequest","PostToolUse"],Q1={SessionEnd:{timeout:2},PermissionRequest:{timeout:600},PostToolUse:{timeout:5,async:!0}},ex=ai,Xy=[ai,$s],tx=n=>Xy.some(e=>String(n).includes(e))});import yc from"node:fs";import tw from"node:path";import{execFileSync as rx}from"node:child_process";function wc(n){return Math.max(as(n.lastHeartbeatAt),as(n.lastEventAt),as(n.lastActivityAt),as(n.pidAliveAt),as(n.startedAt))}function ux(n,e){return Math.max(0,...[n.lastHeartbeatAt,n.lastEventAt,n.lastActivityAt,n.pidAliveAt,n.startedAt].map(as).filter(t=>t&&t<=e))}function nw(n){if(n==null||n===!0||n==="")return null;let e=String(n).trim().toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(m|min|h|hr|hours?|d|days?)?$/);if(!e)return null;let t=Number(e[1]),s=(e[2]||"h")[0],o=s==="m"?t/60:s==="d"?t*24:t;return o>0?o:null}function sw({platform:n=process.platform,exec:e=rx,readFile:t=yc.readFileSync}={}){try{if(n==="darwin"){let o=String(e("/usr/sbin/sysctl",["-n","kern.boottime"],{encoding:"utf8",timeout:3e3})).match(/sec\s*=\s*(\d+)/);return o?Number(o[1])*1e3:null}if(n==="linux"){let s=String(t("/proc/stat","utf8")).match(/^btime\s+(\d+)/m);return s?Number(s[1])*1e3:null}}catch{}return null}function bf(n,e){try{return(JSON.parse(yc.readFileSync(ow(n,e),"utf8")).starts||[]).map(as).filter(Boolean).sort((s,o)=>s-o)}catch{return[]}}function rw(n,e,t=Date.now()){let s=[...bf(n,e),t].sort((r,i)=>r-i).slice(-20),o=ow(n,e);try{yc.mkdirSync(tw.dirname(o),{recursive:!0}),yc.writeFileSync(o,JSON.stringify({machine:e,starts:s.map(r=>G(r))},null,2)+`
218
- `)}catch{}return s}function iw({bootAt:n=null,starts:e=[],daemonStartedAt:t=null}={}){let s=[];if(n){let o=e.filter(r=>r>=n-6e4).sort((r,i)=>r-i)[0];s.push({kind:"boot",restartAt:n,upAt:Math.max(n,o||t||n)})}return t&&!s.some(o=>Math.abs(o.upAt-t)<6e4)&&s.push({kind:"daemon",restartAt:t,upAt:t}),s}function aw(n,{machine:e,moments:t=[],sinceHours:s=kf,isRunning:o=()=>!1,restored:r=new Map,now:i=Date.now(),repoFilter:a=null}={}){let l=n.filter(f=>f&&f.id&&(f.machine||e)===e),c=new Map,u=null;for(let f of t){let h=l.map(S=>ux(S,f.restartAt)).filter(Boolean),p=h.length?Math.max(...h):f.restartAt,m=Math.min(p,f.restartAt)-ix,g=f.upAt+ax;for(let S of l){if(c.has(S.id)||S.archived||S.ignored||!S.cwd||ft(S)||S.surface&&S.surface!=="terminal"||o(S))continue;let k=r.get(S.id);if(k&&i-k<lx)continue;let w=wc(S);if(!w||w<f.restartAt-s*36e5||w>f.upAt+12e4)continue;let x=as(S.endedAt),v;if(!x)v="never ended";else{if(S.endReason&&cx.has(S.endReason)||x<m||x>g)continue;v=S.endReason==="closed"?"process gone after the restart":S.endReason?"ended ("+S.endReason+") as the Mac shut down":"ended at the restart"}a&&!a(S)||(c.set(S.id,{s:S,lastAliveAt:w,endedAt:x||null,why:v,moment:f.kind}),u=u||f)}}let d=[...c.values()].sort((f,h)=>h.lastAliveAt-f.lastAliveAt);return{moment:u||t[0]||null,sessions:d}}function dx(n){let e=String(n);return/^[A-Za-z0-9_./~@%+=:,-]+$/.test(e)?e:"'"+e.replace(/'/g,"'\\''")+"'"}function fx(n,e={}){let t=n.title&&n.titleSource!=="none"?n.title:null;return e.tabTitle===!1?t:Su(e.tabTitleFormat,{name:t,id:n.id,repo:n.repo&&n.repo.name||null,marker:!!e.tabMarker})}function vf(n,e={}){let t=fx(n,e),s=yu(n,["--resume",n.id,...t?["--name",t]:[]]);return{cwd:n.cwd,name:t,claude:s,line:"cd "+dx(n.cwd)+" && "+s,ollama:zh(n.backend)}}function hx(){return["on u8(t)",' if t does not start with "'+Sf+'" then return t'," set h to text "+(Sf.length+1)+" thru -1 of t",' if h is "" then return ""',' return run script ((character id 171) & "data utf8" & h & (character id 187) & " as text")',"end u8","on run argv",' set out to ""',' tell application "Ghostty"'," set win to missing value"," try"," if (count of windows) > 0 then set win to front window"," end try"," set i to 1"," repeat while i < (count of argv)"," try"," set d to my u8(item i of argv)"," set c to my u8(item (i + 1) of argv)"," set cfg to new surface configuration"," set initial working directory of cfg to d"," set initial input of cfg to c & linefeed"," if win is missing value then"," set win to new window with configuration cfg"," else"," new tab in win with configuration cfg"," end if",' set out to out & "ok" & linefeed'," on error e",' set out to out & "error " & e & linefeed'," end try"," set i to i + 2"," end repeat"," activate"," end tell"," return out","end run"].join(`
219
- `)}async function Sc(n,{exec:e,platform:t=process.platform,timeoutMs:s=3e4}={}){if(!n.length)return{ok:!0,how:"ghostty",results:[],message:"nothing to restore"};if(t!=="darwin")return{ok:!1,how:"print",results:[],message:"Ghostty tabs can only be opened on macOS"};let o=["osascript","-e",hx(),...n.flatMap(c=>[ew(c.cwd),ew(c.claude)])],r=await e(o,{timeoutMs:s});if(r.missing)return{ok:!1,how:"print",results:[],message:"osascript isn't available"};if(r.code!==0){let c=String(r.stderr||"").trim(),u=/-1743|not allowed|Not authorized/i.test(c)?"macOS didn't let this app control Ghostty (System Settings → Privacy & Security → Automation)":/-2741|Expected|syntax error/i.test(c)?"this Ghostty has no AppleScript support (Ghostty 1.3.0 or newer has it)":/-1728|Can.t get application|-10814|Application isn.t running/i.test(c)?"Ghostty isn't installed":"Ghostty said: "+(c||"exit "+r.code);return{ok:!1,how:"print",results:[],message:u,stderr:c}}let i=String(r.stdout||"").split(/\r?\n/).filter(Boolean),a=n.map((c,u)=>{let d=i[u]||"";return d==="ok"?{ok:!0}:{ok:!1,error:d.replace(/^error\s*/,"")||"no answer from Ghostty"}}),l=a.filter(c=>c.ok).length;return{ok:l===n.length,how:"ghostty",results:a,message:l+" of "+n.length+" opened in Ghostty"}}var kf,ix,ax,lx,cx,as,ow,Sf,ew,xf=J(()=>{Te();xe();kf=24,ix=900*1e3,ax=600*1e3,lx=600*1e3,cx=new Set(["prompt_input_exit","clear","logout","bypass_permissions_disabled","resume"]),as=n=>{let e=R(n);return isNaN(e)?0:e};ow=(n,e)=>tw.join(be(n).secret,"daemon-starts-"+String(e||"this").replace(/[^A-Za-z0-9._-]/g,"_")+".json");Sf="u8:",ew=n=>Sf+Buffer.from(String(n),"utf8").toString("hex").toUpperCase()});import{EventEmitter as px}from"node:events";import He from"node:fs";import lw from"node:os";import nr from"node:path";function yx(n){try{return n()}catch(e){throw e&&e.badWindow?C(400,"bad_window",e.message):e}}async function wx(n){try{return await n()}catch(e){throw e&&e.badWindow?C(400,"bad_window",e.message):e}}function Sx(n){return n.kind==="permission"&&n.ask&&n.ask.describe?n.ask.describe.verb+": "+n.ask.describe.detail:n.kind==="question"&&n.ask?((n.ask.questions||[])[0]||{}).question||"A question":n.kind==="agent"?(n.items||[]).map(e=>e.text).join(" · "):n.question||n.message||""}function uw(n={}){let e=(o,r,i=400)=>(Array.isArray(o)?o:[]).map(a=>String(a??"").replace(/\s+/g," ").trim().slice(0,i)).filter(Boolean).slice(0,r),t=(Array.isArray(n.files)?n.files:[]).slice(0,10).map(o=>o&&typeof o=="object"?{path:String(o.path||"").trim().slice(0,300),why:String(o.why||"").replace(/\s+/g," ").trim().slice(0,300)}:{path:"",why:String(o||"").trim().slice(0,300)}).filter(o=>o.path||o.why);return{length:gn(n.length,{strict:!0})||null,decisions:e(n.decisions,6),questions:e(n.questions,5),files:t,didntWork:e(n.didntWork,5),recap:String(n.recap||"").replace(/\s+/g," ").trim().slice(0,1600),nextSteps:e(n.nextSteps,5)}}function C(n,e,t){let s=new Error(t);return s.status=n,s.code=e,s}var mx,gx,cw,Af,kc,Tf=J(()=>{ko();Te();Gu();xn();Ur();Zp();Qu();nd();No();vm();tl();ud();Ls();pl();gl();js();wl();fi();$g();qg();Gs();Fl();Ns();Vd();uy();si();xe();Jl();ky();Ti();Vo();mi();os();ns();Vy();is();xf();mx=async()=>({ok:!1,code:"ENOENT",out:""}),gx=18e4;cw=(n,e)=>"agent:"+n.name+":"+e.session.id;Af="Can’t resume through Ollama: ollama isn’t installed on this Mac (ollama.com/download).",kc=class extends px{constructor({root:e,cfg:t,summarizer:s,now:o,log:r,digestSettleMs:i,pullImpl:a,pullFetch:l,tickMs:c,settleMs:u,machine:d,updateChecker:f,updateStartDelayMs:h,liveChecker:p,jumpRunner:m,liveCheckMs:g,jumpExec:S,ghosttyRefreshMs:k,notifier:w,askTimeoutMs:x,askNoListenerMs:v,bootTime:b,restoreExec:y,platform:_,healer:A,agentFetch:O,agentEnv:I,agentPollMs:N,dirExists:M,findOllama:z,prRun:U,prGhStatus:E,gitRemote:P,handoffHome:W}={}){super(),this.root=e,this.cfg=t||Q(e),this.machine=d||de(this.cfg),this.folder=new xt(e,this.machine),this.lastRemoteScan=0,this.updateChecker=f||null,this.updateStartDelayMs=h,this.summarizer=s||new At(this.cfg,{root:this.root}),this.now=o||(()=>Date.now()),this.log=r||(()=>{}),this.pullImpl=a||null,this.pullFetch=l||null,this.tickMs=c||Number(process.env.CHECKPOINTS_TICK_MS)||15e3,this.settleMs=u??Number(process.env.CHECKPOINTS_SETTLE_MS||5e3),this.sessions=new Map,this.checkpoints=new Map,this.seen=new Map,this.generating=new Set,this.persistTimers=new Map,this.lastHbPersist=new Map,this.startedAt=Date.now(),this.bootTimeOpt=b,this.restoreExec=y||S||null,this.dirExists=M||(oe=>{try{return He.statSync(oe).isDirectory()}catch{return!1}}),this.findOllama=z||(()=>Rs()),this.backendChecked=new Set,this.platform=_||process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform,this.restored=new Map,this.restoring=!1,this.timers=[],this.liveChecker=p||Sy,this.jumpRunner=m||nc,this.jumpExec=S||null,this.ghostty=new Vl({exec:S||qt,log:oe=>this.log(oe)}),this.ghosttyRefreshMs=k||Number(process.env.CHECKPOINTS_GHOSTTY_REFRESH_MS)||6e4,this.boardClients=0,this.lastGoAt=0,this.jumping=null,this.liveCheckMs=g||Number(process.env.CHECKPOINTS_LIVE_CHECK_MS)||(Number(this.cfg.liveCheckSec)||30)*1e3,this.asks=new vl({now:this.now,onChange:(oe,K)=>this.onAskChange(oe,K),...x||process.env.CHECKPOINTS_ASK_TIMEOUT_MS?{timeoutMs:x||Number(process.env.CHECKPOINTS_ASK_TIMEOUT_MS)}:{},...v||process.env.CHECKPOINTS_ASK_NO_LISTENER_MS?{noListenerMs:v||Number(process.env.CHECKPOINTS_ASK_NO_LISTENER_MS)}:{}}),this.replies=new xl({now:this.now}),this.notifier=w||qy,this.notified=new Map,this.healer=A||(process.env.CHECKPOINTS_HEAL==="off"?null:Bp),this.healTries=new Map,this.healed=0,this.openStateTimer=null,this.lists=new Zn(this.root,this.machine,{now:this.now}),this.listCache=null,this.listHints=new Map(Object.entries(this.readState("list-hints")||{})),this.transcriptDirs=null,this.needCtx=new Map,this.transcriptHits=new Map,this.prs=new Ka({now:this.now,onUpdate:({cwd:oe,branch:K})=>{for(let pe of this.sessions.values())this.prKey(pe)&&this.prKey(pe).cwd===oe&&this.prKey(pe).branch===K&&this.emit("session.updated",this.publicSession(pe));this.emit("needs.updated",{what:"pr"})}}),this.prStore=new Wa({run:U||(process.env.NODE_TEST_CONTEXT?mx:td),now:this.now,onUpdate:()=>this.prsChanged()}),this.prGhStatus=E||(process.env.NODE_TEST_CONTEXT?async()=>({status:"missing"}):oe=>Ga(oe)),this.gitRemote=P||null,this.prScans=new Map,this.handoffScans=new Map,this.handoffHome=W||lw.homedir(),this.githubRepos=new Map,this.prPeer=null,this.agents=new Ct(this.root,this.machine,{now:this.now,log:oe=>this.log(oe),fetchImpl:O||null,env:I||process.env,userAgent:"Leftoff/"+X}),this.agentPollMs=N||Number(process.env.LEFTOFF_AGENTS_POLL_MS)||ey,this.spaces=new Ia(this.root,this.machine,{now:this.now}),this.digestSettleMs=i??Number(process.env.LEFTOFF_DIGEST_SETTLE_MS||9e4),this.awakeSince=this.now()}isMine(e){return!e.machine||e.machine===this.machine}cfgFor(e){return this.isMine(e)?this.cfg:{...this.cfg,staleAfterSec:Math.max(this.cfg.staleAfterSec,900)}}load(){for(let{session:o,checkpoints:r}of this.folder.loadAll()){if(this.sessions.get(o.id))continue;o.statusChanges=[],this.sessions.set(o.id,o),this.checkpoints.set(o.id,r.sort((l,c)=>R(l.createdAt)-R(c.createdAt)));let a=r[r.length-1];a&&(!o.lastCheckpointAt||R(a.createdAt)>R(o.lastCheckpointAt))&&(o.lastCheckpointAt=a.createdAt,o.lastCheckpointId=a.id),o.needsRewrite&&(delete o.needsRewrite,this.isMine(o)&&this.persist(o,!0))}let e=this.loadOpenState(),t=this.drainSpool(),s=this.archiveGhosts();return this.log("loaded "+this.sessions.size+" sessions ("+e+" open without turns yet); drained "+t+" spooled items"+(s?"; archived "+s+" empty sessions with no folder":"")),this.loadUiState(),this}stateFile(e){return nr.join(this.root,"."+e+"."+this.machine.replace(/[^\w.-]/g,"_")+".json")}readState(e){try{return JSON.parse(He.readFileSync(this.stateFile(e),"utf8"))}catch{return null}}writeState(e,t){try{He.writeFileSync(this.stateFile(e),JSON.stringify(t,null,2)+`
220
- `)}catch{}}loadUiState(){let e=this.readState("ui-state"),t=e;if(t)t.lastRunVersion!==X&&(t={...t,upgradedFrom:t.lastRunVersion||null,upgradeNoticeFor:X,lastRunVersion:X});else{let s=this.sessions.size===0;t={lastRunVersion:X,lastSeenVersion:s?X:null,upgradedFrom:s?null:"an earlier version",upgradeNoticeFor:s?null:X}}this.uiState=t,JSON.stringify(e)!==JSON.stringify(t)&&this.writeState("ui-state",t)}changelog(){let e=this.uiState||{},t=wg(jo(),e.lastSeenVersion,X);return{current:X,lastSeen:e.lastSeenVersion||null,upgradedFrom:e.upgradedFrom||null,upgradeNotice:e.upgradeNoticeFor===X,entries:t}}markChangelogSeen(e){let t=e||X,s={...this.uiState||{}};return(!s.lastSeenVersion||qe(t,s.lastSeenVersion)>0)&&(s.lastSeenVersion=qe(t,X)>0?X:t),s.upgradeNoticeFor=null,this.uiState=s,this.writeState("ui-state",s),this.changelog()}dismissUpgradeNotice(){return this.uiState={...this.uiState||{},upgradeNoticeFor:null},this.writeState("ui-state",this.uiState),{ok:!0}}updateChecksOn(){return!(this.cfg.updates&&this.cfg.updates.check===!1)&&!process.env.CHECKPOINTS_NO_UPDATE_CHECK}scheduleUpdateCheck(){if(this._updateTimer&&clearTimeout(this._updateTimer),!this.updateChecksOn())return;let e=this.readState("update-state"),t=e&&e.checkedAt?Date.now()-Date.parse(e.checkedAt):1/0,s=t<36e5?Ld-t:this.updateStartDelayMs??2e4;this._updateTimer=this.later(Math.max(1e3,s),async()=>{try{await this.runUpdateCheck()}catch(o){this.log("update check failed: "+o.message)}this.stopped||this.scheduleUpdateCheck()})}async runUpdateCheck({force:e=!1}={}){if(!e&&!this.updateChecksOn())return this.updateState();let t=this.cfg.updates&&this.cfg.updates.source||process.env.CHECKPOINTS_UPDATE_SOURCE||null,s=await(this.updateChecker||di)({source:t});return this.writeState("update-state",s),s.available&&this.log("update available: "+(s.remote.version?"v"+s.remote.version:s.remote.sha.slice(0,7))),this.emit("session.updated",{update:!0}),this.updateState()}updateState(){let e=this.readState("update-state");if(!e)return{checked:!1,enabled:this.updateChecksOn()};let t=e.current&&e.current.version!==X;return{...e,checked:!0,enabled:this.updateChecksOn(),available:!!e.available&&!t}}start(){let e=setInterval(()=>this.tick().catch(i=>this.log("tick error: "+i.message)),this.tickMs);e.unref&&e.unref(),this.timers.push(e);let t=setInterval(()=>this.checkLiveness().catch(i=>this.log("live check error: "+i.message)),this.liveCheckMs);t.unref&&t.unref(),this.timers.push(t),this.later(500,()=>this.checkLiveness().catch(()=>{}));for(let i of this.sessions.values())this.isMine(i)&&i.capture==="mod"&&!i.cwd&&!i.endedAt&&!i.archived&&this.scheduleHeal(i,2e3);this.daemonStarts=rw(this.root,this.machine,this.startedAt);let s=setInterval(()=>{(this.boardClients>0||Date.now()-this.lastGoAt<10*6e4)&&this.scheduleGhosttyRefresh("periodic",0)},this.ghosttyRefreshMs);s.unref&&s.unref(),this.timers.push(s),this.scheduleDigest(),this.scheduleUpdateCheck();let o=setInterval(()=>this.syncthingTick().catch(i=>this.log("syncthing: "+i.message)),Number(process.env.LEFTOFF_SYNCTHING_POLL_MS)||1500);o.unref&&o.unref(),this.timers.push(o);let r=setInterval(()=>this.agentsTick().catch(i=>this.log("agents: "+i.message)),this.agentPollMs);return r.unref&&r.unref(),this.timers.push(r),this.later(Number(process.env.LEFTOFF_AGENTS_FIRST_MS)||3e3,()=>this.agentsTick().catch(()=>{})),this}async agentsTick(){if(this.stopped)return[];let e=[];for(let t of this.agents.sources())if(this.agents.pullsHere(t))try{e.push(await this.pullAgent(t.name))}catch(s){this.log("agents: "+t.name+": "+s.message)}return e}agentError(e){if(e&&e.status)return e;let t=e&&e.code||"agent_error";return C(t==="not_found"?404:t==="not_puller"?409:t==="bad_name"||t==="bad_url"||t==="bad_key"?400:500,t,e&&e.message||String(e))}async pullAgent(e,{force:t=!1}={}){let s;try{s=await this.agents.pull(e,{force:t})}catch(o){throw this.agentError(o)}return s.result==="ok"&&s.applied&&s.applied.written&&(this.listCache=null,this.emit("session.updated",{agent:e}),this.emit("needs.updated",{agent:e}),this.emit("lists.updated",{agent:e})),this.emit("agents.updated",{name:e,result:s.result}),s}agentView(){return this.agents.view()}agentsOverview({local:e=!0}={}){let t=this.agents.view({fresh:!0});return{machine:this.machine,pollSec:Math.round(this.agentPollMs/1e3),agents:t.agents.map(s=>({name:s.name,display:s.display,icon:s.icon,url:s.url,reply:s.reply||null,replyHint:s.replyHint||null,feedUrl:e?s.source.url:(()=>{try{return new URL(s.source.url).host}catch{return null}})(),addedAt:s.source.addedAt,addedOn:s.source.addedOn,puller:s.puller,pullsHere:s.pullsHere,hasKey:s.hasKey,status:s.status,sessions:s.records.length,open:s.records.filter(o=>o.inFeed&&o.session.status!=="done").length,needsYou:s.records.reduce((o,r)=>o+(this.agentNeed(s,r)||{items:[]}).items.length,0)}))}}async addAgent(e={}){let t;try{t=this.agents.add({name:String(e.name||"").trim().toLowerCase(),url:e.url,key:e.key==null||e.key===""?null:e.key})}catch(o){throw this.agentError(o)}this.log("agents: "+(t.replaced?"updated ":"added ")+t.source.name+" ("+t.source.url+")"+(t.previousPuller?"; now pulled here instead of "+t.previousPuller:""));let s=null;if(e.pull!==!1)try{s=await this.pullAgent(t.source.name,{force:!0})}catch(o){s={result:"error",error:{message:o.message}}}return this.emit("agents.updated",{name:t.source.name,result:"added"}),{...t,pull:s}}removeAgent(e,{purge:t=!1}={}){let s;try{s=this.agents.remove(e,{purge:t})}catch(o){throw this.agentError(o)}return this.log("agents: removed "+e+(s.movedTo?" (history kept in "+s.movedTo+")":" (purged)")),this.listCache=null,this.emit("session.updated",{agent:e}),this.emit("needs.updated",{agent:e}),this.emit("lists.updated",{agent:e}),this.emit("agents.updated",{name:e,result:"removed"}),s}agentPub(e,t,s=this.now(),o){let r=zd(e,t,s,o);return r.needsYou=this.agentNeed(e,t,s),r}agentSessions(e=this.now()){let t=[];for(let s of this.agentView().agents)for(let o of s.records)t.push(this.withSpace(this.agentPub(s,o,e)));return t}agentSession(e,{full:t=!0}={}){for(let s of this.agentView().agents)for(let o of s.records)if("agent:"+s.name+":"+o.session.id===e)return this.withSpace(this.agentPub(s,o,this.now(),{full:t}));return null}assertNotAgent(e,t="that"){if(Ze(e))throw C(409,"agent_session","this session comes from an external agent’s feed and is read-only here; "+t+" happens with the agent itself")}async syncthingTick(){if(this.stBusy||this.stopped)return;let e=Pt();if(!(!e||e.used||e.expiredLogged)){this.stBusy=!0;try{let t=ht();if(!t.ok)return;await Rr({client:t.client,log:s=>this.log(s)})}finally{this.stBusy=!1}}}needsTabMarker(e,t=[]){if(!e||!this.isMine(e))return!1;let s=e.term||{};if(s.kind!=="ghostty"||ql(s.version)===!0)return!1;let o=c=>vs(c.title||"").trim().toLowerCase(),r=c=>String(c.cwd||"").replace(/\/+$/,""),i=o(e),a=r(e);return[...t,...[...this.sessions.values()].filter(c=>this.isMine(c)&&!c.endedAt&&!["ended","stale"].includes(c.status))].some(c=>c&&c.id!==e.id&&(i&&o(c)===i||a&&r(c)===a))}later(e,t){let s=setTimeout(t,e);return s.unref&&s.unref(),this.timers.push(s),s}stop(){this.stopped=!0;for(let e of this.timers)clearInterval(e),clearTimeout(e);for(let e of this.persistTimers.values())clearTimeout(e);for(let e of this.sessions.values())this.persistNow(e);this.openStateTimer&&(clearTimeout(this.openStateTimer),this.openStateTimer=null),this.writeOpenState()}openStateRows(){let e=["id","machine","capture","cwd","repo","transcriptPath","model","surface","pid","tty","pidStartedAt","pidSource","term","startedAt","lastEventAt","lastHeartbeatAt","lastActivityAt","parentSessionId","startSource","historyTurns","healedAt","healedVia","inTurn","needsInput"],t=[];for(let s of this.sessions.values()){if(!this.isMine(s)||s.endedAt||s.archived||this.worthPersisting(s))continue;let o={};for(let r of e)s[r]!==void 0&&s[r]!==null&&(o[r]=s[r]);t.push(o)}return t}scheduleOpenState(){this.openStateTimer||this.stopped||(this.openStateTimer=setTimeout(()=>{this.openStateTimer=null,this.writeOpenState()},1e3),this.openStateTimer.unref&&this.openStateTimer.unref())}writeOpenState(){let e=this.openStateRows(),t=this.lastOpenState,s=JSON.stringify({savedAt:G(Date.now()),sessions:e},null,2)+`
221
- `;if(t!==JSON.stringify(e)){this.lastOpenState=JSON.stringify(e);try{He.writeFileSync(this.stateFile("open-sessions"),s)}catch{}}}loadOpenState(){let e=this.readState("open-sessions");if(!e||!Array.isArray(e.sessions))return 0;let t=this.now(),s=0;for(let o of e.sessions){if(!o||!o.id||this.sessions.has(o.id))continue;let r=Math.max(R(o.lastHeartbeatAt)||0,R(o.lastEventAt)||0,R(o.startedAt)||0);if(!o.pid&&t-r>24*36e5)continue;let i={turns:0,turnsSinceCheckpoint:0,backgroundTasks:0,pinned:!1,archived:!1,...o,machine:o.machine||this.machine,statusChanges:[]};this.sessions.set(i.id,i),this.checkpoints.set(i.id,[]),s++}return this.lastOpenState=null,s}archiveGhosts(){let e=this.now(),t=0;for(let s of this.sessions.values()){if(!this.isMine(s)||s.archived||!s.folder||!fo(s,(this.checkpoints.get(s.id)||[]).length))continue;let o=R(s.lastHeartbeatAt||s.lastEventAt)||0;!s.endedAt&&e-o<(this.cfg.staleAfterSec||600)*1e3||(s.archived=!0,s.archivedReason="empty session with no folder or process (v0.4.8 cleanup)",this.persist(s,!0),t++)}if(t){let s=this.readState("cleanup")||{};this.writeState("cleanup",{...s,ghostsArchived:(s.ghostsArchived||0)+t,lastArchivedAt:G(e),lastArchived:t})}return t}emptyCounts(){let e=0,t=0;for(let o of this.sessions.values()){if(o.archived||!this.isMine(o))continue;let r=(this.checkpoints.get(o.id)||[]).length;ft(o,r)&&(e++,fo(o,r)&&t++)}let s=this.readState("cleanup")||{};return{empty:e,ghosts:t,healed:this.healed,archivedGhosts:s.ghostsArchived||0,lastArchivedAt:s.lastArchivedAt||null}}noteHistory(e){let t=Bu(e.transcriptPath);if(!t)return;e.historyTurns=t.prompts||0;let s=this.noteTranscriptModel(e,t.model);(e.historyTurns>0||s)&&this.persist(e)}noteTranscriptModel(e,t){if(!t||lo(t))return!1;if(!e.backend){let s=$h(t);return s?(e.backend=s,!0):!1}return e.backend.model===t?!1:(e.backend={...e.backend,model:t},!0)}inferBackend(e){if(!e||e.backend||!e.transcriptPath||this.backendChecked.has(e.id)||!this.isMine(e))return!1;this.backendChecked.add(e.id);let{meta:t}=nn(e.transcriptPath,{maxBytes:256*1024}),s=this.noteTranscriptModel(e,t&&t.model);return s&&this.persist(e),s}scheduleHeal(e,t=1e3){if(!this.healer||this.stopped||e.healTimer||e.healing)return;let s=this.healTries.get(e.id)||{n:0,at:0},o=[0,6e4,3e5,9e5][s.n]??18e5,r=Math.max(t,s.at+o-Date.now());e.healTimer=setTimeout(()=>{e.healTimer=null,this.healSession(e).catch(i=>this.log("heal "+e.id.slice(0,8)+": "+i.message))},r),e.healTimer.unref&&e.healTimer.unref()}async healSession(e){if(!this.healer||e.healing||e.archived||e.endedAt||!this.isMine(e)||e.cwd&&e.pid)return!1;let t=this.healTries.get(e.id)||{n:0,at:0};this.healTries.set(e.id,{n:t.n+1,at:Date.now()}),e.healing=!0;try{let s=new Set([...this.sessions.values()].filter(i=>i!==e&&i.pid&&!i.endedAt).map(i=>i.pid)),o=await this.healer(e.id,{cwd:e.cwd||null,claimedPids:s});if(!o)return!1;let r=[];return o.cwd&&!e.cwd&&(e.cwd=o.cwd,r.push("folder")),o.transcriptPath&&!e.transcriptPath&&(e.transcriptPath=o.transcriptPath),o.prompts!=null&&e.historyTurns==null&&(e.historyTurns=Math.max(0,o.prompts-(e.turns||0))),o.customTitle&&!e.title&&!xr(o.customTitle).id&&(e.title=F(o.customTitle,120),e.titleSource="custom",r.push("name")),o.proc&&o.proc.pid>1&&!e.pid&&(e.pid=o.proc.pid,e.tty=o.proc.tty||null,e.pidStartedAt=o.proc.pidStartedAt||null,e.pidSource="daemon",e.pidAliveAt=this.now(),r.push("process "+e.pid)),o.term&&!e.term&&(e.term=o.term,r.push("terminal "+(o.term.label||o.term.kind))),r.length?(e.healedAt=G(this.now()),e.healedVia=(o.via||[]).join(","),this.healed++,this.log("session "+e.id.slice(0,8)+" was known only by id; found its "+r.join(", ")+" ("+e.healedVia+")"),r.includes("folder")&&await this.enrichRepo(e),this.refreshStatus(e),this.persist(e,!0),this.emit("session.updated",this.publicSession(e)),!0):!1}finally{e.healing=!1,!e.endedAt&&!e.archived&&(!e.cwd||!e.pid)&&!this.stopped&&this.scheduleHeal(e,5e3)}}drainSpool(){let e=0;for(let t of this.folder.readSpool())try{let s=JSON.parse(t.body);s&&s.hook&&s.payload?this.ingestHook(s.hook,s.payload,s.at?R(s.at):t.t):s&&Array.isArray(s.events)?this.ingest(s.events):s&&s.type&&s.sessionId?this.ingest([s]):s&&s.checkpoint&&s.sessionId&&this.addCheckpoint(s.sessionId,s.checkpoint),He.unlinkSync(t.file),e++}catch(s){this.log("spool item skipped: "+t.file+" "+s.message);try{He.renameSync(t.file,t.file+".bad")}catch{}}return e}ingest(e){let t=this.now(),s=0,o=0,r=new Set;for(let u of e||[]){if(!u||!u.sessionId||!Rh.includes(u.type))throw C(400,"bad_event","each event needs sessionId and a known type");if(Ze(String(u.sessionId)))throw C(400,"bad_event","agent:… ids belong to external agents’ feeds");let d=u.type+"|"+(u.at||""),f=this.seen.get(u.sessionId);if(f||this.seen.set(u.sessionId,f=new Set),u.at&&f.has(d)){o++;continue}if(u.at&&(f.add(d),f.size>2e3&&f.delete(f.values().next().value)),process.env.CHECKPOINTS_IGNORE_SESSION&&u.sessionId===process.env.CHECKPOINTS_IGNORE_SESSION)continue;let h=this.sessions.get(u.sessionId),p=!h;h||(h={id:u.sessionId,machine:this.machine,turns:0,turnsSinceCheckpoint:0,backgroundTasks:0,pinned:!1,archived:!1,statusChanges:[]},this.sessions.set(h.id,h),this.checkpoints.set(h.id,[]));let m=h.title,g=h.turns;ep(h,{...u,at:u.at||G(t)}),(p||u.data&&u.data.cwd&&!(h.repo&&h.repo.root))&&this.enrichRepo(h),u.type==="session.started"&&h.startSource==="resume"&&h.historyTurns==null&&h.transcriptPath&&this.noteHistory(h),!h.cwd&&h.capture==="mod"&&this.isMine(h)&&this.scheduleHeal(h),h.turns===1&&g===0&&!h.title&&this.autoTitle(h),h.title!==m&&(h.titleChangedAt=G(t)),u.type==="session.ended"&&this.onEnded(h),u.type==="turn_completed"&&u.data&&u.data.listHint&&typeof u.data.listHint=="object"&&this.noteListHint(h,u.data.listHint,u.at||G(t)),this.settleAsksFor(h,u),h.term&&h.term.kind==="ghostty"&&this.isMine(h)&&(u.type==="session.opened"?this.scheduleGhosttyRefresh("session started",1500):h.title!==m&&this.scheduleGhosttyRefresh("renamed",3e3)),s++,r.add(h)}let i;for(let u of r){this.refreshStatus(u,t);let d=e.every(f=>f.type==="heartbeat");if(this.persist(u,!1,d),this.emit("session.updated",this.publicSession(u)),!i&&u.capture==="mod"&&!["ended","stale"].includes(u.status)){let f=bu(u,t,this.cfg);f&&(u.dueSince||(u.dueSince=t),i={sessionId:u.id,reason:f})}}let a=r.size===1?[...r][0]:null,l=!!(a&&a.capture==="mod"&&this.isMine(a)&&!a.cwd&&!a.endedAt),c=a?this.listSignature(a.id):null;return{ok:!0,accepted:s,duplicate:o,...i?{checkpointDue:i}:{},...a?{session:{id:a.id,title:a.title||null,repoName:me(a),tabMarker:this.needsTabMarker(a)}}:{},...l?{hello:!0}:{},...c?{lists:c}:{}}}ingestHook(e,t,s){if(t&&t.session_id&&process.env.CHECKPOINTS_IGNORE_SESSION===t.session_id)return{ok:!0,accepted:0};let o=Kg(e,t,s||this.now());return o.length?this.ingest(o):{ok:!0,accepted:0,ignored:!0}}async enrichRepo(e){if(e.cwd)try{let t=await Yg(e.cwd);t&&(e.repo={...t,...e.repo||{},root:e.repo&&e.repo.root||t.root,branch:e.repo&&e.repo.branch||t.branch}),Wg(e.repo&&e.repo.root||e.cwd,this.cfg)&&(e.ignored=!0),this.persist(e)}catch{}}onEnded(e){e.endSettleUntil=this.now()+this.settleMs,this.later(this.settleMs+20,()=>this.tick().catch(()=>{}))}refreshRemote(){this.lastRemoteScan=this.now();for(let{session:e,checkpoints:t}of this.folder.loadAll()){if(this.isMine(e))continue;let s=this.sessions.get(e.id),o=this.checkpoints.get(e.id)||[];if(s&&!this.isMine(s)){if(s.lastEventAt===e.lastEventAt&&s.lastActivityAt===e.lastActivityAt&&s.title===e.title&&o.length===t.length&&s.endedAt===e.endedAt&&JSON.stringify(s.prLinks||null)===JSON.stringify(e.prLinks||null)&&JSON.stringify(s.prUnlinked||null)===JSON.stringify(e.prUnlinked||null)&&JSON.stringify(s.todos||null)===JSON.stringify(e.todos||null)&&JSON.stringify(s.files||null)===JSON.stringify(e.files||null))continue;e.statusChanges=s.statusChanges||[],e.status=s.status}else{if(s)continue;e.statusChanges=[]}this.sessions.set(e.id,e),this.checkpoints.set(e.id,t.sort((r,i)=>R(r.createdAt)-R(i.createdAt))),this.refreshStatus(e),this.emit("session.updated",this.publicSession(e))}}refreshStatus(e,t=this.now()){let s=bt(e,t,this.cfgFor(e));return s!==e.status?(e.status=s,e.statusChanges=[...e.statusChanges||[],{at:G(t),status:s}].slice(-50),!0):!1}bootTime(){return this.bootTimeOpt!==void 0?typeof this.bootTimeOpt=="function"?this.bootTimeOpt():this.bootTimeOpt:(this.bootTimeCache===void 0&&(this.bootTimeCache=process.env.CHECKPOINTS_BOOT_TIME?Date.parse(process.env.CHECKPOINTS_BOOT_TIME)||null:sw()),this.bootTimeCache)}isRunningNow(e,t=this.now()){return this.liveInfo(e,bt(e,t,this.cfgFor(e)),t).live==="open"}restoreView(e,t,s,o=[]){let r=this.publicSession(e,s),i=vf(e,{...this.cfg,tabMarker:this.needsTabMarker(e,o)});return{id:e.id,shortId:r.shortId,title:r.title,repoLabel:r.repoLabel,repoKey:r.repoKey,branch:e.repo&&e.repo.branch||null,cwd:e.cwd,lastAliveAt:t?new Date(t.lastAliveAt).toISOString():wc(e)?new Date(wc(e)).toISOString():null,endedAt:e.endedAt||null,why:t?t.why:null,name:i.name,command:i.line,claude:i.claude,modelLabel:e.backend?r.modelLabel:null,...i.ollama&&!this.findOllama()?{blocked:"no_ollama",error:Af}:{}}}restoreCandidates({since:e,repo:t}={},s=this.now()){let o=nw(e)||kf,r=this.bootTime(),i=iw({bootAt:r,starts:this.daemonStarts||bf(this.root,this.machine),daemonStartedAt:this.startedAt}),a=t?String(t).toLowerCase():null,l=a?d=>[this.repoKeyFor(d),d.repo&&d.repo.name,d.cwd].some(f=>f&&String(f).toLowerCase().includes(a)):null,c=aw([...this.sessions.values()],{machine:this.machine,moments:i,sinceHours:o,isRunning:d=>this.isRunningNow(d,s),restored:this.restored,now:s,repoFilter:l}),u=c.moment;return{machine:this.machine,sinceHours:o,restartAt:u?new Date(u.restartAt).toISOString():null,restartKind:u?u.kind:null,bootAt:r?new Date(r).toISOString():null,daemonStartedAt:new Date(this.startedAt).toISOString(),sessions:c.sessions.map(d=>this.restoreView(d.s,d,s,c.sessions.map(f=>f.s))),ghostty:this.platform==="darwin"}}restoreTargets(e,t=this.now()){let s=[];for(let o of[...new Set((e||[]).map(String))].slice(0,50)){let r=this.sessions.get(o);if(!r)throw C(404,"not_found","no such session: "+o);if(!this.isMine(r))throw C(409,"other_machine",'"'+(r.title||o)+'" ran on '+(r.machine||"another machine")+"; restore it there");if(!r.cwd)throw C(409,"no_folder",'"'+(r.title||o)+'" has no folder on record');if(this.isRunningNow(r,t))throw C(409,"running",'"'+(r.title||o)+'" is already running (use Go)');this.inferBackend(r),s.push(r)}return s}markRestored(e,t=this.now()){for(let s of e)this.restored.set(s,t);this.emit("restore.updated",{ids:e})}async restore(e,{print:t=!1}={},s=this.now()){let o=this.restoreTargets(e,s);if(!o.length)throw C(400,"nothing","pick at least one session");if(this.restoring)throw C(409,"busy","a restore is already running");this.restoring=!0;try{let r=o.map(d=>this.restoreView(d,null,s,o));if(t)return{ok:!1,how:"print",opened:0,failed:0,sessions:r,message:"the commands, to run yourself"};let i=r.filter(d=>!d.blocked),a=i.length?await Sc(i.map(d=>({cwd:d.cwd,claude:d.claude})),{exec:this.restoreExec||qt,platform:this.platform}):{ok:!1,how:"ghostty",results:[],message:Af},l=new Map(i.map((d,f)=>[d.id,a.results[f]||null])),c=r.map(d=>{if(d.blocked)return{...d,ok:!1};let f=l.get(d.id);return{...d,ok:a.how==="ghostty"?!!(f&&f.ok):!1,error:f&&f.error?f.error:null}}),u=c.filter(d=>d.ok);return u.length&&this.markRestored(u.map(d=>d.id),s),this.log("restore: "+a.message+(a.how==="print"?" ("+o.length+" commands printed)":"")),{ok:a.ok&&i.length===r.length,how:a.how,opened:u.length,failed:c.length-u.length,sessions:c,message:i.length===r.length?a.message:a.message+(i.length?"; "+(r.length-i.length)+" need Ollama":"")}}finally{this.restoring=!1}}async resume(e,{fromPeer:t=!1}={},s=this.now()){this.assertNotAgent(e,"Resume");let o=this.sessions.get(e);if(!o)throw C(404,"not_found","no such session");if(!this.isMine(o)){if(t)throw C(409,"other_machine","That session runs on "+(o.machine||"another machine"));let c=await this.viaPeer(o.machine,"session.resume",{sessionId:o.id},"Resume");return{...c.result,peer:o.machine,peerVia:c.via}}let[r]=this.restoreTargets([e],s),i=vf(r,{...this.cfg,tabMarker:this.needsTabMarker(r,[r])});if(!this.dirExists(r.cwd))throw C(409,"folder_gone","Can’t resume: "+r.cwd+" isn’t there any more.");if(i.ollama&&!this.findOllama())throw C(409,"no_ollama",Af);let a=await this.restore([e],{},s),l=a.sessions[0]||{};if(a.how!=="ghostty")throw C(409,"no_ghostty","Couldn’t open Ghostty: "+a.message+". Use Copy resume.");if(!l.ok)throw C(409,"ghostty_error","Ghostty didn’t open it: "+(l.error||"no answer")+". Use Copy resume.");return{ok:!0,how:"ghostty",id:r.id,machine:this.machine,command:i.line,message:"Resumed in a new Ghostty tab"}}closedSessions({q:e,machine:t,repo:s,space:o,docs:r=!1,limit:i=30,offset:a=0}={},l=this.now()){let c=e?String(e).toLowerCase().trim():null,u=[];for(let m of this.sessions.values()){if(m.archived||m.ignored||!m.endedAt||bt(m,l,this.cfgFor(m))!=="ended"||t&&(m.machine||this.machine)!==t)continue;let g=this.publicSession(m,l);g.empty||o&&g.space!==o||r&&!Rm(g.files)||c&&!this.matches(g,c)||u.push(g)}u.sort((m,g)=>(R(g.endedAt)||0)-(R(m.endedAt)||0));let d=$m(u);if(s){let m=String(s).split(",").map(g=>g.trim()).filter(Boolean);u=u.filter(g=>m.some(S=>Bs(g,S)))}let f=Math.max(0,Number(a)||0),h=Math.max(1,Math.min(Number(i)||30,200));return{sessions:u.slice(f,f+h).map(m=>{let g=this.sessions.get(m.id);return g&&this.inferBackend(g)?this.publicSession(g,l):m}),total:u.length,more:u.length>f+h,repos:d,docs:!!r}}closedCount({machine:e}={},t=this.now()){let s=0;for(let o of this.sessions.values())o.archived||o.ignored||!o.endedAt||e&&(o.machine||this.machine)!==e||bt(o,t,this.cfgFor(o))==="ended"&&(ft(o,(this.checkpoints.get(o.id)||[]).length)||s++);return s}async checkLiveness(e=this.now()){if(this.checkingLive)return 0;let t=[...this.sessions.values()].filter(o=>this.isMine(o)&&o.pid&&!o.endedAt);if(!t.length)return 0;this.checkingLive=!0;let s=0;try{let o=await this.liveChecker(t,{now:e});for(let r of t){let i=o.get(r.id);if(i){if(i.alive){r.pidAliveAt=e,this.refreshStatus(r,e)&&this.emit("session.updated",this.publicSession(r,e));continue}r.endedAt||(r.endedAt=G(e),r.closedAt=r.endedAt,r.endReason="closed",r.inTurn=!1,r.pidAliveAt=null,this.log("session "+r.id.slice(0,8)+" closed (Claude Code pid "+r.pid+" "+(i.reason==="pid_reused"?"was reused":"is gone")+")"),this.refreshStatus(r,e),this.persist(r,!0),this.emit("session.updated",this.publicSession(r,e)),this.onEnded(r),s++)}}}finally{this.checkingLive=!1}return s}liveInfo(e,t,s=this.now()){return e.endedAt?{live:"closed",via:e.closedAt?"pid":"event",since:e.endedAt}:t==="stale"?{live:"closed",via:"heartbeat",since:e.lastHeartbeatAt||e.lastEventAt||null}:this.isMine(e)&&e.pid?{live:"open",via:"pid",since:e.pidStartedAt||e.startedAt||null}:e.capture==="mod"?{live:"open",via:"heartbeat",since:e.startedAt||null}:{live:"unknown",via:"hooks",since:null}}jumpInfo(e,t){if(!this.isMine(e)){if(!this.peers)return{supported:!1,reason:"runs on "+(e.machine||"another machine")};if(t==="closed")return{supported:!1,reason:"closed"};let o=this.peerUsable(e.machine);return o.ok?{supported:!0,reason:null,peer:e.machine,via:o.via}:{supported:!1,reason:"runs on "+(e.machine||"another machine")+(o.reason?": "+o.reason:""),peer:e.machine,code:o.code||null}}if(t!=="open"&&t!=="unknown")return{supported:!1,reason:"closed"};if(!e.term)return{supported:!1,reason:"no terminal recorded (sessions started before v0.4, or hooks without the mod)"};let s=Xo(e);return{supported:s.supported,reason:s.reason||null}}async jump(e){this.assertNotAgent(e,"Go");let t=this.sessions.get(e);if(!t)throw C(404,"not_found","no such session");if(!this.isMine(t)){let a=await this.viaPeer(t.machine,"session.go",{sessionId:t.id},"Go");return{...a.result,peer:t.machine,peerVia:a.via,label:(a.result&&a.result.label||"its terminal")+" on "+t.machine}}let s=this.publicSession(t);if(s.live==="closed")throw C(409,"closed","That session is closed"+(s.resumeCommand?". Resume it with: "+s.resumeCommand:""));if(!t.term)return{ok:!1,code:"unsupported",message:"No terminal was recorded for this session (it started before v0.4, or without the mod).",steps:[],elapsedMs:0};if(this.jumping)return this.jumping.id===t.id?this.jumping.p:{ok:!1,code:"busy",message:"Still switching to another session; try again in a moment.",steps:[],elapsedMs:0};this.lastGoAt=Date.now();let o=Xo({...t,ghosttyTerminalId:this.ghostty.get(t.id)}),r={...this.jumpExec?{exec:this.jumpExec}:{}};o.kind==="ghostty"&&(r.ghostty={caps:this.ghostty.getCaps({resourcesDir:t.term.resourcesDir,version:t.term.version}),activate:this.cfg.ghostty&&this.cfg.ghostty.activate||"auto",onStale:()=>this.ghostty.forget(t.id),onFound:(a,l,c)=>{this.ghostty.set(t.id,a,l),this.ghostty.applyListing(this.ghosttyOpenSessions(),c,"go")}});let i=(async()=>{let a=await this.jumpRunner(o,r);return o.kind==="ghostty"&&this.noteGhosttyAutomation(a),this.log("go "+t.id.slice(0,8)+" ("+o.kind+"): "+(a.ok?"ok"+(a.how?" by "+a.how:""):a.code+" "+a.message)+(a.elapsedMs!=null?" in "+a.elapsedMs+" ms":"")+(a.steps&&a.steps.length?" ["+Ql(a.steps)+"]":"")),{...a,kind:o.kind,label:o.label}})();this.jumping={id:t.id,p:i};try{return await i}finally{this.jumping=null}}ghosttyOpenSessions(){return[...this.sessions.values()].filter(e=>this.isMine(e)&&!e.endedAt&&e.term&&e.term.kind==="ghostty")}ghosttyAutoOk(){if((process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform)!=="darwin")return!1;let e=this.readState("ghostty-automation");return!!(e&&e.ok)}noteGhosttyAutomation(e){let t=this.readState("ghostty-automation"),s=null;e&&(e.ok||["not_found","ambiguous"].includes(e.code))?s={ok:!0}:e&&e.code==="automation_denied"&&(s={ok:!1,code:e.code}),s&&(!t||t.ok!==s.ok)&&this.writeState("ghostty-automation",{...s,at:new Date().toISOString()})}scheduleGhosttyRefresh(e,t=1500){return this.stopped||this.ghosttyTimer||!this.ghosttyAutoOk()||!this.ghosttyOpenSessions().length?!1:(this.ghosttyTimer=this.later(t,()=>{this.ghosttyTimer=null,this.refreshGhostty(e).catch(s=>this.log("ghostty cache error: "+s.message))}),!0)}async refreshGhostty(e="manual"){let t=this.ghosttyOpenSessions();if(!t.length)return null;let s=await this.ghostty.refresh(t,{reason:e});return s&&s.error&&/-1743|not authori[sz]ed/i.test(s.stderr||"")&&this.noteGhosttyAutomation({code:"automation_denied"}),s}boardOpened(){this.boardClients++,this.scheduleGhosttyRefresh("board opened",300)}boardClosed(){this.boardClients=Math.max(0,this.boardClients-1)}async jumpSpeed(){let e=this.ghosttyOpenSessions()[0],t=this.ghostty.getCaps(e?{resourcesDir:e.term.resourcesDir,version:e.term.version}:{}),s=await Zl({exec:this.jumpExec||qt,caps:t});return s.ok&&this.noteGhosttyAutomation({ok:!0}),{...s,cache:{sessions:this.ghosttyOpenSessions().length,remembered:this.ghostty.map.size,last:this.ghostty.last},pid:process.pid,launchd:process.env.XPC_SERVICE_NAME&&process.env.XPC_SERVICE_NAME!=="0"?process.env.XPC_SERVICE_NAME:null}}async jumpCheck(e){let t=await sc(e,this.jumpExec?{exec:this.jumpExec}:{});return e==="ghostty"&&t.ok&&this.noteGhosttyAutomation({ok:!0}),t}where(e){this.assertNotAgent(e,"Go");let t=this.sessions.get(e);if(!t)throw C(404,"not_found","no such session");return{id:t.id,title:t.title||null,isLocal:this.isMine(t),machine:t.machine||this.machine,cwd:t.cwd||null,tty:t.tty||null,pid:t.pid||null,term:t.term||null,ghosttyTerminalId:this.ghostty.get(t.id)}}worthPersisting(e){let t=(this.checkpoints.get(e.id)||[]).length;return!!(e.title||e.turns>0||t||e.folder||e.historyTurns>0||e.endedAt&&!ft(e,t))}persist(e,t=!1,s=!1){if(s){let r=this.lastHbPersist.get(e.id)||0;if(Date.now()-r<6e4)return;this.lastHbPersist.set(e.id,Date.now())}if(t)return this.persistNow(e);if(this.persistTimers.has(e.id))return;let o=setTimeout(()=>{this.persistTimers.delete(e.id),this.persistNow(e)},300);o.unref&&o.unref(),this.persistTimers.set(e.id,o)}persistNow(e){if(!this.isMine(e))return;if(!this.worthPersisting(e)){this.scheduleOpenState();return}let t=this.persistTimers.get(e.id);t&&(clearTimeout(t),this.persistTimers.delete(e.id));try{this.folder.writeSession(e,this.checkpoints.get(e.id)||[])}catch(s){this.log("write session failed: "+s.message)}}async autoTitle(e){if(!(e.title||e.titling)){e.titling=!0;try{let t=await this.summarizer.title(e);!e.title&&t.title&&(e.title=t.title,e.titleSource=t.titleSource,this.persist(e,!0),this.emit("session.updated",this.publicSession(e)))}finally{e.titling=!1}}}setTitle(e,t,s="custom"){let o=this.sessions.get(e);if(!o)throw C(404,"not_found","no such session");return o.title=F(t,120),o.titleSource=s,this.persist(o,!0),this.emit("session.updated",this.publicSession(o)),o}patchSession(e,t){this.assertNotAgent(e,"renaming, pinning and archiving");let s=this.sessions.get(e);if(!s)throw C(404,"not_found","no such session");if(this.assertMine(s),t.title!=null){if(typeof t.title!="string"||!t.title.trim())throw C(400,"bad_title","title must be a non-empty string");s.title=F(t.title,120),s.titleSource="custom",s.term&&s.term.kind==="ghostty"&&this.scheduleGhosttyRefresh("renamed",5e3)}return t.pinned!=null&&(s.pinned=!!t.pinned),t.archived!=null&&(s.archived=!!t.archived),this.persist(s,!0),this.emit("session.updated",this.publicSession(s)),this.publicSession(s)}assertMine(e){if(!this.isMine(e))throw C(409,"other_machine",'this session belongs to machine "'+e.machine+'"; change it from that machine')}addCheckpoint(e,t,{by:s}={}){this.assertNotAgent(e,"checkpointing");let o=this.sessions.get(e);o&&this.assertMine(o),o||(o={id:e,machine:this.machine,turns:0,turnsSinceCheckpoint:0,backgroundTasks:0,capture:"mod",startedAt:t.createdAt||G(this.now()),statusChanges:[]},this.sessions.set(e,o),this.checkpoints.set(e,[]));let r=t.trigger||"manual";if(!mu.includes(r))throw C(400,"bad_trigger","trigger must be one of "+mu.join(", "));for(let u of["doing","stoppedAt","next"])if(typeof t[u]!="string")throw C(400,"bad_checkpoint",u+" is required");let i=this.checkpoints.get(e);if(t.id&&i.some(u=>u.id===t.id))return i.find(u=>u.id===t.id);let a=G(t.createdAt||this.now()),l={id:t.id||ca(e,a),sessionId:e,machine:o.machine||this.machine,createdAt:a,trigger:r,sessionTitle:o.title||null,repo:o.repo?me(o):null,branch:o.repo&&o.repo.branch||null,cwd:o.cwd||null,doing:t.doing,stoppedAt:t.stoppedAt,next:t.next,openQuestion:t.openQuestion||(/^YOU:/i.test(t.next)?t.next.replace(/^YOU:\s*/i,""):null),userNote:t.userNote||null,filesTouched:Array.isArray(t.filesTouched)?t.filesTouched.map(String).slice(0,50):[],turnsSinceLast:o.turnsSinceCheckpoint||0,parseOk:t.parseOk!==!1,generator:t.generator||{by:s||"mod"},...uw(t)},c=this.folder.writeCheckpoint(o,l);return i.push(c),o.lastCheckpointId=c.id,o.lastCheckpointAt=c.createdAt,o.turnsSinceCheckpoint=0,o.dueSince=null,(r==="dormant"||r==="long_run")&&(o.dormantStretch=o.lastActivityAt),(r==="session_end"||r==="clear")&&(o.finalCheckpointDone=!0),(o.endedAt||o.status==="stale")&&(o.finalCheckpointDone=!0),this.persist(o,!0),this.emit("checkpoint.created",c),this.emit("session.updated",this.publicSession(o)),c}async generateCheckpoint(e,t,{userNote:s,length:o,save:r=!0}={}){this.assertNotAgent(e,"checkpointing");let i=this.sessions.get(e);if(!i)throw C(404,"not_found","no such session");if(this.assertMine(i),this.generating.has(e))throw C(409,"busy","a checkpoint is already being generated for this session");this.generating.add(e);try{let{messages:a,meta:l}=i.transcriptPath?nn(i.transcriptPath):{messages:[],meta:{}};l&&l.gitBranch&&!(i.repo&&i.repo.branch)&&(i.repo={...i.repo||{},branch:l.gitBranch}),l&&l.model&&this.noteTranscriptModel(i,l.model);let c=this.checkpoints.get(e)||[],u=c[c.length-1]||null,d=ha({session:i,previous:u,messages:a,cfg:this.cfg,userNote:s,length:o}),f;i.ignored?f={doing:"(repo opted out of summaries)",stoppedAt:i.status||"",next:"Open the session to see where it is.",parseOk:!0,generator:{by:"ignored"}}:f=await this.summarizer.checkpoint({system:d.system,prompt:d.prompt,messages:a,session:i,maxTokens:d.maxTokens,length:d.length});let h={trigger:t,doing:f.doing,stoppedAt:f.stoppedAt,next:f.next,openQuestion:f.openQuestion,userNote:s,filesTouched:d.filesTouched,parseOk:f.parseOk,generator:f.generator,length:d.length,decisions:f.decisions,questions:f.questions,files:f.files,didntWork:f.didntWork,recap:f.recap,nextSteps:f.nextSteps};return r?this.addCheckpoint(e,h,{by:"daemon"}):{...h,...uw(h),sessionId:e,preview:!0,sessionTitle:i.title||null,createdAt:G(this.now())}}finally{this.generating.delete(e)}}async tick(){if(this.stopped)return;let e=this.now();this.folder.spoolCount()&&this.drainSpool(),e-this.lastRemoteScan>(Number(process.env.CHECKPOINTS_REMOTE_SCAN_MS)||3e4)&&this.refreshRemote();for(let t of this.sessions.values()){if(this.refreshStatus(t,e)&&(this.persist(t),this.emit("session.updated",this.publicSession(t))),!this.isMine(t)||this.generating.has(t.id)||t.endSettleUntil&&e<t.endSettleUntil)continue;let s=bu(t,e,this.cfg);if(!s){t.dueSince=null;continue}if(!(t.capture==="mod"&&!["ended","stale"].includes(t.status)&&(t.dueSince||(t.dueSince=e),e-t.dueSince<gx)))try{await this.generateCheckpoint(t.id,s)}catch(r){this.log("checkpoint failed for "+t.id+": "+r.message)}}this.scanPrLinksTick(e),this.scanHandoffsTick(e),await this.maybeScheduledDigest()}async makeDigest({date:e,by:t="manual"}={}){for(let r of this.sessions.values())this.persistNow(r);let s=this.now(),o;try{o=await jl(this.folder.loadAll(),{now:s,cfg:this.cfg,machine:this.machine,date:e&&e!=="today"?e:this.digestDate(s),summarizer:this.summarizer,todos:this.todosOverview(s).open.map(r=>({text:r.text,at:r.at,sessionTitle:r.session&&r.session.title})),lists:this.listIndex({fresh:!0}).concat(this.agentListIndex()),agents:this.agents.view({fresh:!0}),spaces:this.spaces.map({fresh:!0})}),this.folder.writeDigest(o.meta.date,o.markdown)}catch(r){throw this.digestState={...this.digestState||{},lastError:{at:G(s),by:t,message:String(r&&r.message||r)}},this.log("digest failed ("+t+"): "+(r&&r.stack||r)),r}return this.digestState={...this.digestState||{},lastMadeAt:G(s),lastBy:t,lastSessions:o.meta.sessions,lastError:null},this.log("digest made ("+t+") for "+o.meta.date+": digests/"+o.meta.date+".md"),this.emit("digest.created",o.meta),o}standup({space:e=null,date:t=null}={}){for(let f of this.sessions.values())this.persistNow(f);let s=this.now(),o=this.digestSchedule(),r=this.digestDate(s),i=t&&t!=="today"?t:r;if(!/^\d{4}-\d{2}-\d{2}$/.test(i))throw C(400,"bad_date","date is YYYY-MM-DD");let a=s;if(i!==r){let f=this.folder.readDigest(i),h=f?Ye(f.markdown).data||{}:{},[p,m,g]=i.split("-").map(Number),[S,k]=String(o.time||"07:00").split(":").map(Number);a=Date.parse(h.generated_at)||zt(p,m,g,S,k,o.tz)}let l=this.spaces.map({fresh:!0}),c=null;if(e&&e!=="all")try{c=sn(l,this.knownMachines()).find(f=>f.toLowerCase()===String(e).toLowerCase())||$r(e)}catch(f){throw C(400,"bad_space",f.message)}let u=null;try{u=this.agents.view({fresh:!0})}catch{}return{...Ul(this.folder.loadAll(),{now:a,date:i,space:c,spaces:l,cfg:this.cfg,tz:o.tz,digestTime:o.time||"07:00",agents:u}),date:i}}knownMachines(){let e=new Set(this.folder.machines());try{for(let t of this.folder.aliveMachines())e.add(t.machine)}catch{}try{for(let t of He.readdirSync(nr.join(this.root,"machines")))if(t.endsWith(".json")&&!t.startsWith("."))try{let s=JSON.parse(He.readFileSync(nr.join(this.root,"machines",t),"utf8"));s&&s.machine&&e.add(s.machine)}catch{}}catch{}for(let t of Object.keys(jt(this.spaces.map(),"machines")))e.add(t);return[...e].filter(Boolean).sort()}spacesView(){let e=this.spaces.map({fresh:!0}),t=this.knownMachines(),s=a=>Object.entries(e[a]||{}).filter(([,l])=>l.space!=null).map(([l,c])=>({key:l,space:c.space,at:c.at,by:c.by||null})).sort((l,c)=>l.key.localeCompare(c.key)),o=zp([...this.sessions.values()].filter(a=>!a.archived).map(a=>{let l=this.repoKeyFor(a);return{key:l==="no-repo"?"":l,name:this.repoLabels().get(l)||me(a),at:a.lastActivityAt||a.startedAt}}),{home:lw.homedir()}),r=o.map(a=>a.name),i=[];try{i=this.agentView().agents.map(a=>({name:a.name,display:a.display,puller:a.puller||null,space:qn(e,{id:"agent:"+a.name+":x",agent:{name:a.name,puller:a.puller}}).space}))}catch{}return{machine:this.machine,quickPicks:xo,spaces:Ao([...sn(e,t),...xo]),inUse:sn(e,t),machines:t.map(a=>{let l=e.machines[a];return{machine:a,space:Jn(e,a),set:!!(l&&l.space!=null),at:l&&l.space!=null?l.at:null,by:l&&l.space!=null?l.by:null,here:a===this.machine}}),repos:s("repos"),sessions:s("sessions"),agentOverrides:s("agents"),agents:i,knownRepos:r,repoSuggestions:o,file:"spaces.json",conflicts:this.spaces.cache&&this.spaces.cache.conflicts.length||0}}setSpace(e={}){let t;try{t=Gr(e.kind)}catch(r){throw C(400,"bad_kind",r.message)}let s=String(e.key==null?"":e.key).trim();if(!s)throw C(400,"bad_key","which "+t.replace(/s$/,"")+"?");let o=e.space;if((o===""||o===void 0)&&(o=null),o!=null)try{o=$r(o)}catch(r){throw C(400,"bad_space",r.message)}try{this.spaces.update((r,i)=>{r[t][s]={space:o==null?null:sn(r,this.knownMachines()).concat(xo).find(a=>a.toLowerCase()===o.toLowerCase())||o,at:i.at,by:i.by}})}catch(r){throw C(400,r.code||"bad_space",r.message)}return this.emit("spaces.updated",{by:this.machine}),this.spacesView()}listIndex({fresh:e=!1}={}){let t=Date.now();if(!e&&this.listCache&&t-this.listCache.at<2e3)return this.listCache.lists;let s=[];try{s=this.lists.all({includeArchived:!0}).map(o=>gt(o))}catch(o){this.log("lists: "+(o&&o.message||o))}return this.listCache={at:t,lists:s},s}listSignature(e){let t=this.listIndex().filter(s=>s.sourceSession===e&&!s.archived);return t.length?t.map(s=>s.ref+":"+s.total+":"+s.items.filter(o=>o.done).map(o=>o.n).join(".")).sort().join("|"):null}listsChanged(e,t){this.listCache=null,this.emit("lists.updated",{ref:e&&e.ref||null,what:t})}sessionListInfo(e){let t=this.listIndex().filter(r=>r.sourceSession===e.id&&!r.archived),s=this.listHints.get(e.id),o=s&&!s.dismissed&&!s.savedRef?{count:s.count,name:s.name,kind:s.kind,at:s.at,fp:s.fp}:null;return{lists:t.map(r=>({ref:r.ref,name:r.name,done:r.done,total:r.total,progress:r.progress})),listHint:o}}saveListHints(){let e={},t=[...this.listHints.entries()].sort((s,o)=>(R(o[1].at)||0)-(R(s[1].at)||0)).slice(0,300);for(let[s,o]of t)e[s]=o;this.writeState("list-hints",e)}noteListHint(e,t,s){let o=(Array.isArray(t.items)?t.items:[]).slice(0,80).map(l=>({text:F(String(l&&l.text||l||""),500),done:!!(l&&l.done)})).filter(l=>l.text);if(o.length<3)return;let i=this.listIndex().filter(l=>l.sourceSession===e.id).find(l=>ma(o,l.items)>=.6),a=this.listHints.get(e.id);a&&a.fp&&a.fp===t.fp&&(a.dismissed||a.savedRef)||(this.listHints.set(e.id,{count:Number(t.count)||o.length,name:F(String(t.name||_r({heading:t.heading},{fallback:e.title})),80),kind:t.kind||null,items:o,at:s,fp:t.fp||null,...i?{savedRef:i.ref}:{}}),this.saveListHints())}dismissListHint(e){let t=this.listHints.get(e);t&&(t.dismissed=!0,this.saveListHints());let s=this.sessions.get(e);return s&&this.emit("session.updated",this.publicSession(s)),{ok:!0}}findListsInSession(e){this.assertNotAgent(e,"saving lists");let t=this.sessions.get(e);if(!t)throw C(404,"not_found","no such session");let s=(t.transcriptPath&&He.existsSync(t.transcriptPath)?t.transcriptPath:null)||vo(e,this.transcriptDirs?{dirs:this.transcriptDirs}:{});if(!s)throw this.isMine(t)?C(404,"no_transcript","Claude Code's transcript for this session isn't on this machine (it looked in ~/.claude/projects). Type /cp-todo in the session instead."):C(409,"other_machine","This session ran on "+t.machine+", and its transcript is on that machine. Open Leftoff there (or type /cp-todo in the session) to find its lists.");let{messages:o}=nn(s,{maxBytes:32*1024*1024}),r=pa(o,{minItems:2,fallback:t.title}),i=this.listIndex().filter(a=>a.sourceSession===e);return{transcript:s,messages:o.length,candidates:r.reverse().map(a=>{let l=i.find(c=>ma(a.items,c.items)>=.6);return{n:a.n,name:a.name,kind:a.kind,heading:a.heading,count:a.items.length,items:a.items,message:a.message,savedAs:l?{ref:l.ref,name:l.name}:null}})}}assertMayEditLists(e={}){if(e.via==="remote"&&!(this.cfg.remote&&this.cfg.remote.allowAnswers))throw C(403,"lists_remote_off","Changing lists from another device is off. On a Mac, turn on Settings → Privacy & security → Trust my paired devices.")}listError(e){if(e&&e.status)return e;let t=e&&e.code;return t==="no_list"?C(404,"no_list",e.message):t==="no_item"?C(404,"no_item",e.message):t==="ambiguous_list"?C(409,"ambiguous_list",e.message):t==="list_busy"?C(503,"list_busy",e.message):t&&/^empty_/.test(t)?C(400,t,e.message):e}saveList(e={},t={}){this.assertMayEditLists(t);let s=e.sessionId?this.sessions.get(e.sessionId):null;if(e.sessionId&&!s&&!Array.isArray(e.items))throw C(404,"not_found","no such session");let o=Array.isArray(e.items)?e.items:null,r=e.name?String(e.name):null,i=null;if(!o&&e.fromHint&&s){if(i=this.listHints.get(s.id),!i)throw C(404,"no_hint","Claude hasn't written a list in this session lately; use Find lists in this session");o=i.items,r=r||i.name}if(!o&&e.fromTranscript!=null&&s){let u=this.findListsInSession(s.id).candidates.find(d=>d.n===Number(e.fromTranscript));if(!u)throw C(404,"no_candidate","no list "+e.fromTranscript+" in that session's transcript");o=u.items,r=r||u.name}if(!o||!o.length)throw C(400,"empty_list","a list needs items (or fromHint / fromTranscript with a sessionId)");let a=s?me(s):e.repo||null,l=s?this.repoKeyFor(s):e.repo||null,c;try{c=this.lists.create({name:r||_r({heading:null},{fallback:s&&s.title}),items:o.map(u=>typeof u=="string"?{text:u}:u),repo:a&&a!=="no-repo"?a:null,repoKey:l,sourceSession:s?s.id:e.sessionId||null,sourceMachine:s?s.machine||this.machine:null,sourceTitle:s&&s.title||null})}catch(u){throw this.listError(u)}if(s){let u=this.listHints.get(s.id);u&&(i||ma(o,u.items)>=.6)&&(u.savedRef=c.ref,this.saveListHints())}return this.log("list saved: "+c.ref+" ("+c.items.length+" items"+(s?", from session "+ze(s.id):"")+")"),this.listsChanged(c,"created"),s&&this.emit("session.updated",this.publicSession(s)),{ok:!0,list:gt(c),context:_d([gt(c)])}}getList(e){if(String(e||"").startsWith("~")){let t=this.agentListIndex().find(s=>s.ref===e);if(!t)throw C(404,"not_found","no such list");return t}try{return gt(this.lists.resolve(e))}catch(t){throw this.listError(t)}}changeList(e,t={},s={}){if(String(e||"").startsWith("~"))throw C(409,"read_only","this list comes from an external agent’s feed and is read-only here; tell the agent instead");this.assertMayEditLists(s);let o,r={};try{if(o=this.lists.resolve(e),t.text&&!t.done&&!t.undone){let a=this.lists.findItem(o,t.text);if(!a)throw C(404,"no_item",'no item on "'+o.meta.name+'" matches "'+t.text+'"; give its number');t={...t,[t.undo?"undone":"done"]:[a.id]}}if(t.done||t.undone){let a=this.lists.setDone(o,[].concat(t.done||t.undone).map(Number),!!t.done);o=a.list,r.missing=a.missing}if(t.add){let a=this.lists.addItem(o,t.add);o=a.list,r.added=a.item.id}t.edit&&(o=this.lists.editItem(o,t.edit.n,t.edit)),t.remove!=null&&(o=this.lists.removeItem(o,t.remove)),t.name&&(o=this.lists.rename(o,t.name)),t.archived!=null&&(o=this.lists.archive(o,!!t.archived))}catch(a){throw this.listError(a)}this.listsChanged(o,"changed");let i=o.meta.sourceSession&&this.sessions.get(o.meta.sourceSession);return i&&this.emit("session.updated",this.publicSession(i)),{ok:!0,list:gt(o),...r}}agentListIndex(){try{return Rl(this.agentView())}catch(e){return this.log("agents: "+e.message),[]}}listsOverview({all:e=!1,repo:t=null}={}){let s=this.listIndex({fresh:!0}).concat(this.agentListIndex());e||(s=s.filter(r=>!r.archived)),t&&(s=s.filter(r=>r.repoDir===t||r.repo===t||r.repoKey===t));let o=new Map;for(let r of s){let i=r.repo||"no repo";o.has(i)||o.set(i,{repo:i,repoDir:r.repoDir,lists:[]}),o.get(i).lists.push(r)}for(let r of o.values())r.lists.sort((i,a)=>(i.open===0)-(a.open===0)||(R(a.updated)||0)-(R(i.updated)||0));return{lists:s,repos:[...o.values()].sort((r,i)=>r.repo.localeCompare(i.repo)),archived:this.listIndex().filter(r=>r.archived).length}}sessionLists(e){if(Ze(e))return{lists:this.agentListIndex().filter(i=>i.sourceSession===e),hint:null,context:null,signature:null,readOnly:!0};if(!this.sessions.get(e))throw C(404,"not_found","no such session");let s=this.listIndex({fresh:!0}).filter(r=>r.sourceSession===e),o=this.listHints.get(e);return{lists:s,hint:o&&!o.dismissed&&!o.savedRef?o:null,context:_d(s),signature:this.listSignature(e)}}catchupEntries(){for(let t of this.sessions.values())this.persistNow(t);let e=[];try{e=Pl(this.agents.view({fresh:!0}))}catch(t){this.log("agents: "+t.message)}return this.folder.loadAll().concat(e)}catchupNeeds(e,t){let s=this.sessions.get(e.id)||e,o=this.needsItem(s,t);if(!o)return null;let r="";return o.kind==="permission"&&o.ask&&o.ask.describe?r="needs your OK: "+o.ask.describe.verb+" "+o.ask.describe.detail:o.kind==="question"&&o.ask?r="asks: "+(((o.ask.questions||[])[0]||{}).question||"a question"):o.kind==="terminal"?r=o.message||"waiting in the terminal":o.kind==="reply"&&(r="asked you: "+(typeof o.question=="string"?o.question:"a question")),{kind:o.kind,message:r}}catchupArgs(e={}){let t=s=>(Array.isArray(s)?s:s==null||s===""?[]:String(s).split(",")).map(o=>String(o).trim()).filter(Boolean);return{since:e.since==null||e.since===""?void 0:String(e.since),to:e.to?String(e.to):null,repos:t(e.repos),machines:t(e.machines),length:e.length||void 0}}catchupOptions(e={}){let t=this.catchupArgs(e);return yx(()=>hl(this.catchupEntries(),{...t,now:this.now(),cfg:this.cfg,machine:this.machine,lastAt:this.folder.lastCatchupAt(),repoKeyFor:s=>this.repoKeyFor(s),needsOf:(s,o)=>this.catchupNeeds(s,o)}))}async makeCatchup(e={}){let t=this.catchupArgs(e),s=e.summary&&Array.isArray(e.summary.bullets)?e.summary:null,o=await wx(()=>ii(this.catchupEntries(),{...t,now:this.now(),cfg:this.cfg,machine:this.machine,lastAt:this.folder.lastCatchupAt(),summarizer:e.useModel===!1?null:this.summarizer,given:s,repoKeyFor:i=>this.repoKeyFor(i),needsOf:(i,a)=>this.catchupNeeds(i,a),lists:this.listIndex({fresh:!0}).concat(this.agentListIndex())})),r=null;return e.save!==!1&&(r=this.folder.writeCatchup(o.fileName,o.markdown),this.log("catch-up "+r.name+": "+o.recap.sessions.length+" session(s), "+o.recap.window.label.toLowerCase()+", summary by "+(o.summary.by.by==="model"?o.summary.by.provider:"heuristic")),this.emit("catchup.created",{name:r.name,sessions:o.recap.sessions.length})),{name:r?r.name:null,path:r?"catchups/"+r.name+".md":null,saved:!!r,recap:o.recap,summary:o.summary,markdown:o.markdown}}readCatchup(e){let t=this.folder.readCatchup(e);return t?{name:t.name,markdown:t.markdown,parsed:Ad(t.markdown)}:null}digestSchedule(){return il(this.cfg.digest,$t())}digestTime(){return this.digestSchedule().time}digestDate(e=this.now()){return Do(e,this.digestSchedule()).date}digestGraceMs(){let e=Number(this.cfg.digest&&this.cfg.digest.graceMin);return(Number.isFinite(e)&&e>=0?e:20)*6e4}digestMachines(e=this.now()){let s=[];try{s=this.folder.aliveMachines().filter(o=>e-(Date.parse(o.seenAt)||0)<6048e5&&o.digest!==!1).map(o=>o.machine)}catch{}return[...new Set([...s,this.machine])].sort()}digestTurnNow(e=this.now()){let t=this.digestSchedule(),s=Do(e,t);return{slot:s,...qm({machines:this.digestMachines(e),me:this.machine,slotAt:s.at,graceMs:this.digestGraceMs()})}}sharedDigest(e){return this.folder.readDigest(e,"all")}nextDigestAt(e=this.now()){let t=this.digestSchedule(),s=this.digestTurnNow(e),o=s.slot,r=!!this.sharedDigest(o.date),i=this.digestDue(e);if(!r&&o.isDay&&s.at!=null&&e<s.at)return G(s.at);let a=zm(e,t,{doneToday:r&&!i||o.isDay&&o.past&&!i});return a==null?null:G(a>e+1e3&&a!==o.at?a+s.rank*this.digestGraceMs():a)}hasRecentSessions(e=this.now()){let t=e-(this.cfg.digest&&this.cfg.digest.lookbackHours||24)*36e5;for(let s of this.sessions.values())if(!s.archived&&(this.worthPersisting(s)||!this.isMine(s))&&Math.max(R(s.lastActivityAt)||0,R(s.startedAt)||0)>=t)return!0;return!1}digestDue(e=this.now()){let t=this.digestSchedule();if(!t.time)return!1;let s=this.digestTurnNow(e),o=s.slot;if(!o.isDay||!o.past)return!1;let r=this.sharedDigest(o.date);if(!r)return e<s.at||e>=o.at+this.digestGraceMs()&&e<this.awakeSince+this.digestSettleMs*(s.rank+1)&&(s.order.length>1||this.knownMachines().length>1)||t.skipEmpty&&!this.hasRecentSessions(e)?!1:"missing";let i=Ye(r.markdown).data||{};return(!i.machine||i.machine===this.machine||!s.order.includes(i.machine))&&Number(i.sessions)===0&&e-(R(i.generated_at)||0)>=5*6e4&&this.hasRecentSessions(e)?"empty":!1}async maybeScheduledDigest(){let e=this.now();if(this.lastDigestCheck&&e-this.lastDigestCheck>12e4&&(this.awakeSince=e),this.lastDigestCheck=e,!this.digestTime()||this.digesting)return;if(!this.aliveAt||e-this.aliveAt>30*6e4)try{this.folder.stampAlive({version:X,digest:!0},e),this.aliveAt=e}catch(o){this.log("digest: could not stamp digests/.machines: "+o.message)}let t=this.digestState&&this.digestState.lastError;if(t&&t.by==="schedule"&&e-R(t.at)<10*6e4)return;let s=this.digestDue(e);if(s){this.digesting=!0;try{await this.makeDigest({by:"schedule"}),s==="empty"&&this.log("digest remade: the morning one had no sessions")}catch{}finally{this.digesting=!1}}}digestStatus(e=this.now()){let t=this.digestState||{},s=this.digestSchedule(),o=this.digestTurnNow(e),r=o.slot,i=this.sharedDigest(r.date),a=i?Ye(i.markdown).data||{}:null,l=null;return!i&&s.time&&(r.isDay?r.past&&s.skipEmpty&&!this.hasRecentSessions(e)?l="no activity":r.past&&o.at!=null&&e<o.at&&(l="waiting for "+o.order[0]):l="not a digest day"),{time:s.time,timeZone:s.tz,zoneOverride:s.zoneOverride,daemonZone:$t(),zoneAbbr:pd(e,s.tz),label:Wm(s.time,s.tz,e),days:s.days,daysLabel:Jm(s.days),skipEmpty:s.skipEmpty,nextAt:this.nextDigestAt(e),todayDate:r.date,todayDay:r.day,todayNote:l,today:a?{date:r.date,generatedAt:a.generated_at||null,sessions:a.sessions!=null?Number(a.sessions):null,by:a.machine||null,duplicates:i.duplicates||0}:null,shared:!0,order:o.order,rank:o.rank,first:o.first,graceMin:this.digestGraceMs()/6e4,lastMadeAt:t.lastMadeAt||null,lastBy:t.lastBy||null,lastError:t.lastError||null}}scheduleDigest(){this.later(1e3,()=>this.maybeScheduledDigest().catch(()=>{}))}repoKeyFor(e){return e.repo&&e.repo.root?e.repo.root:e.cwd?this.isMine(e)?(this.gitRootCache||(this.gitRootCache=new Map),this.gitRootCache.has(e.cwd)||(this.gitRootCache.size>2e3&&this.gitRootCache.clear(),this.gitRootCache.set(e.cwd,Jg(e.cwd))),this.gitRootCache.get(e.cwd)||e.cwd):e.cwd:e.repo&&e.repo.name||"no-repo"}repoLabels(){let e=[...new Set([...this.sessions.values()].map(s=>this.repoKeyFor(s)))].sort(),t=e.join(`
222
- `);return(!this.repoLabelMemo||this.repoLabelMemo.sig!==t)&&(this.repoLabelMemo={sig:t,map:Kt(e)}),this.repoLabelMemo.map}publicSession(e,t=this.now()){let s=this.repoKeyFor(e),o=this.checkpoints.get(e.id)||[],r=o[o.length-1]||null,i=bt(e,t,this.cfgFor(e));return{id:e.id,title:e.title||null,titleSource:e.titleSource||null,machine:e.machine||this.machine,isLocal:this.isMine(e),cwd:e.cwd||null,repo:e.repo||null,repoName:me(e),repoKey:s,repoLabel:this.repoLabels().get(s)||me(e),transcriptPath:e.transcriptPath||null,model:e.model||null,backend:e.backend||null,modelLabel:Yh(e),surface:e.surface||null,capture:e.capture||null,status:i,startedAt:e.startedAt||null,lastActivityAt:e.lastActivityAt||null,lastHeartbeatAt:e.lastHeartbeatAt||null,endedAt:e.endedAt||null,endReason:e.endReason||null,turns:e.turns||0,turnsSinceCheckpoint:e.turnsSinceCheckpoint||0,lastCheckpointId:e.lastCheckpointId||null,parentSessionId:e.parentSessionId||null,pinned:!!e.pinned,archived:!!e.archived,ignored:!!e.ignored,needsInput:!!e.needsInput,lastNotification:e.lastNotification||null,lastPreview:e.lastPreview||null,idleSec:e.lastActivityAt?Math.max(0,Math.round((t-R(e.lastActivityAt))/1e3)):null,checkpointCount:o.length,latestCheckpoint:r,resumeCommand:uo(e),statusChanges:e.statusChanges||[],folder:e.folder||null,empty:ft(e,o.length),ghost:fo(e,o.length),historyTurns:e.historyTurns||0,startSource:e.startSource||null,healedVia:e.healedVia||null,archivedReason:e.archivedReason||null,question:e.question||null,answers:(e.answers||[]).slice(-10),prs:this.sessionPrs(e),...this.handoffsPublic(e),...this.livePublic(e,i,t),needsYou:this.needsItem(e,i,t),peer:this.isMine(e)?null:{machine:e.machine,...this.peerUsable(e.machine)},...this.sessionListInfo(e),...this.spaceOf({id:e.id,machine:e.machine||this.machine,repoKey:s,repoName:me(e),repoLabel:this.repoLabels().get(s)||me(e)})}}spaceOf(e){try{let t=this.spaces.resolve(e);return{space:t.space,spaceSource:t.source}}catch{return{space:e.machine||this.machine,spaceSource:"default"}}}withSpace(e){return e&&Object.assign(e,this.spaceOf(e))}livePublic(e,t,s){let o=this.liveInfo(e,t,s),r=e.term||null;return{live:o.live,liveVia:o.via,liveSince:o.since,closedAt:o.live==="closed"&&(e.closedAt||e.endedAt||o.since)||null,pid:e.pid||null,tty:e.tty||null,shortId:ze(e.id),terminal:r?{kind:r.kind,label:r.label||r.kind}:null,jump:this.jumpInfo(e,o.live)}}listSessions({status:e,repo:t,q:s,machine:o,limit:r=50,includeArchived:i=!1}={}){let a=e?String(e).split(",").map(u=>u.trim()).filter(Boolean):null,l=s?String(s).toLowerCase():null,c=[...this.sessions.values()].map(u=>this.publicSession(u));try{c.push(...this.agentSessions().filter(u=>!l||this.agentMatches(u,l)))}catch(u){this.log("agents: "+u.message)}if(i||(c=c.filter(u=>!u.archived)),a&&(c=c.filter(u=>a.includes(u.status))),t){let u=String(t).split(",").map(d=>d.trim()).filter(Boolean);c=c.filter(d=>u.some(f=>Bs(d,f)))}return o&&(c=c.filter(u=>u.machine===o)),l&&(c=c.filter(u=>this.matches(u,l))),c.sort((u,d)=>d.pinned-u.pinned||(R(d.lastActivityAt||d.startedAt)||0)-(R(u.lastActivityAt||u.startedAt)||0)),c.slice(0,Math.min(Number(r)||50,500))}agentMatches(e,t){let s=this.agentSession(e.id)||e;return[s.title,s.project,s.machine,...(s.checkpoints||[]).flatMap(r=>[r.summary,r.done,r.next]),...(s.needsYou&&s.needsYou.items||[]).map(r=>r.text)].some(r=>r&&String(r).toLowerCase().includes(t))}matches(e,t){if(e.agent)return this.agentMatches(e,t);let s=[e.title,e.repoName,e.machine,e.cwd,e.repo&&e.repo.branch,e.lastPreview];e.files&&s.push(...(e.files.docs||[]).map(o=>o.name),...(e.files.plans||[]).map(o=>o.title),...(e.files.artifacts||[]).map(o=>o.title));for(let o of this.checkpoints.get(e.id)||[])s.push(o.doing,o.stoppedAt,o.next,o.userNote,...o.filesTouched||[]);return s.some(o=>o&&String(o).toLowerCase().includes(t))}getSession(e){if(Ze(e)){let o=this.agentSession(e);if(!o)throw C(404,"not_found","no such session");return o}let t=this.sessions.get(e);if(!t)throw C(404,"not_found","no such session");let s=this.checkpoints.get(e)||[];return this.inferBackend(t),{...this.publicSession(t),checkpoints:s.slice(-20).reverse()}}sessionCheckpoints(e,{before:t,limit:s=50}={}){if(Ze(e)){let r=this.agentSession(e);if(!r)throw C(404,"not_found","no such session");return r.checkpoints.filter(i=>!t||R(i.createdAt)<R(t)).slice(0,Math.min(Number(s)||50,500))}if(!this.sessions.has(e))throw C(404,"not_found","no such session");let o=(this.checkpoints.get(e)||[]).slice().reverse();return t&&(o=o.filter(r=>R(r.createdAt)<R(t))),o.slice(0,Math.min(Number(s)||50,500))}timeline({since:e,q:t,session:s,limit:o=100}={}){let r=[];if(s)r.push(...this.checkpoints.get(s)||[]);else for(let i of this.checkpoints.values())r.push(...i);if(e&&(r=r.filter(i=>R(i.createdAt)>=R(e))),t){let i=String(t).toLowerCase();r=r.filter(a=>[a.sessionTitle,a.doing,a.stoppedAt,a.next,a.repo,a.userNote,...a.filesTouched||[]].some(l=>l&&String(l).toLowerCase().includes(i)))}return r.sort((i,a)=>R(a.createdAt)-R(i.createdAt)),r.slice(0,Math.min(Number(o)||100,1e3))}search(e){let t=String(e||"").toLowerCase().trim();return t?{sessions:this.listSessions({q:t,includeArchived:!0,limit:50}),checkpoints:this.timeline({q:t,limit:50})}:{sessions:[],checkpoints:[]}}health(){let e=0;for(let t of this.sessions.values())["ended","stale"].includes(bt(t,this.now(),this.cfgFor(t)))||e++;return{ok:!0,version:X,pid:process.pid,folder:this.root,machine:this.machine,machines:this.folder.machines(),peers:this.peers?{allowIncoming:this.peers.accepts,fingerprint:this.peers.id.fingerprint}:null,uptimeSec:Math.round((Date.now()-this.startedAt)/1e3),sessionsOpen:e,sessionsTotal:this.sessions.size,needsYou:this.needsList().length,answering:this.answeringInfo(),spoolPending:this.folder.spoolCount(),summarizer:this.summarizer.mode,summarizerDetail:this.summarizer.describe(),listsOpen:this.listIndex().filter(t=>!t.archived&&t.done<t.total).length,agents:this.agentsHealth(),mod:this.modHealth(),remote:this.remoteHealth(),digest:this.digestStatus(),sessions:this.emptyCounts(),...this.versionHealth()}}agentsHealth(){try{return this.agentView().agents.map(e=>({name:e.name,display:e.display,icon:e.icon,ok:e.status.ok,lastSuccessAt:e.status.lastSuccessAt,error:e.status.error?e.status.error.message:null,puller:e.puller,pullsHere:e.pullsHere,sessions:e.records.length}))}catch{return[]}}settleAsksFor(e,t){let s=t.data||{};t.type==="activity"&&s.kind==="tool"&&s.phase==="post"&&s.inputKey?this.asks.settleSession(e.id,"answered_in_terminal",{key:s.inputKey}):t.type==="activity"&&s.kind==="prompt"?this.asks.settleSession(e.id,"new_prompt"):t.type==="turn_completed"?this.asks.settleSession(e.id,"turn_ended"):t.type==="session.ended"&&this.asks.settleSession(e.id,"session_ended"),(t.type==="turn_started"||t.type==="activity"&&s.kind==="prompt")&&(e.replyPending=null)}openAsk(e={}){let t=e.sessionId;if(t&&process.env.CHECKPOINTS_IGNORE_SESSION===t)return{id:null,state:"gone",reason:"ignored"};if(this.cfg.answers&&this.cfg.answers.enabled===!1)return{id:null,state:"gone",reason:"answering_off"};let s=this.asks.open({sessionId:t,tool:e.tool,input:e.input,suggestions:e.suggestions,capture:e.capture==="mod"?"mod":"hooks",cwd:e.cwd,permissionMode:e.permissionMode});return{id:s.id,state:s.state}}onAskChange(e,t){let s=this.sessions.get(e.sessionId);s&&(t==="opened"?(s.needsInput||(s.needsInputAt=new Date(e.createdAt).toISOString()),s.needsInput=!0):!this.asks.waiting({sessionId:s.id}).length&&s.needsInput&&(!s.lastNotification||s.lastNotification==="permission_prompt")&&(s.needsInput=!1),this.refreshStatus(s),this.persist(s),this.emit("session.updated",this.publicSession(s)),t==="opened"&&this.scheduleNotify(s)),this.log("ask "+e.id+" "+t+" ("+e.kind+" "+e.tool+(e.reason?", "+e.reason:"")+") session "+String(e.sessionId).slice(0,8)),this.emit("needs.updated",{sessionId:e.sessionId,id:e.id,state:e.state,what:t})}async startPeers(e={}){if(this.peers||process.env.LEFTOFF_PEERS==="0"||this.cfg.peers&&this.cfg.peers.enabled===!1)return this.peers;this.peers=new Oi({engine:this,root:this.root,machine:this.machine,log:this.log,version:X,now:this.now,...e}),this.peers.on("needs",t=>{for(let s of this.sessions.values())s.machine===t&&!this.isMine(s)&&this.emit("session.updated",this.publicSession(s));this.emit("needs.updated",{machine:t,what:"peer"})}),this.peers.on("changed",()=>this.emit("peers.updated",{}));try{await this.peers.start()}catch(t){this.log("peer: "+t.message)}return this.peers}async stopPeers(){this.peers&&(await this.peers.stop(),this.peers=null)}peerUsable(e){return this.peers?this.peers.usable(e):{ok:!1,code:"off",reason:"cross-machine actions are off on this machine"}}async viaPeer(e,t,s,o){if(!this.peers)throw C(409,"other_machine","That session runs on "+e+"; cross-machine actions are off here");let r=this.peers.usable(e);if(!r.ok&&!["unreachable","checking"].includes(r.code))throw C(409,"peer_"+(r.code||"unavailable"),o+" on "+e+" isn’t available: "+r.reason);try{return await this.peers.request(e,t,s)}catch(i){throw C(i.status&&i.status<500?i.status===403?403:409:503,i.code&&i.code.startsWith("peer_")?i.code:"peer_"+(i.code||"error"),i.message)}}peersOverview(){return this.peers?this.peers.overview():{machine:this.machine,off:!0,peers:[]}}needsItem(e,t,s=this.now()){let o=this.needsItemRaw(e,t,s,r=>this.dismissals.has(r));return o?(o.key=Hs(e.id,o),this.dismissals.has(o.key)?null:o):null}agentNeed(e,t,s=this.now()){let o=ts(e,t,s);return o?(o.key=Hs(cw(e,t),o),this.dismissals.has(o.key)?null:o):null}get dismissals(){return(!this._dismissals||this._dismissals.root!==this.root||this._dismissals.machine!==this.machine)&&(this._dismissals=new Va({root:this.root,machine:this.machine,now:this.now})),this._dismissals}dismissNeed(e){let t=this.needsListAll({skipDismissed:!0}).find(o=>o.key===e)||this.needsListAll().find(o=>o.key===e);if(!t)throw C(404,"not_found","That item isn’t waiting any more");let s=this.dismissals.dismiss(e,{sessionId:t.session.id,title:t.session.title,line:Sx(t),kind:t.kind});return this.emit("needs.updated",{dismissed:e}),{key:e,...s}}restoreNeeds(e){let t=this.dismissals.restore(e);return t&&this.emit("needs.updated",{restored:t}),{restored:t}}dismissedNeeds(){let e=new Set(this.needsListAll().map(t=>t.key));for(let t of this.asks.items.values())t.state==="waiting"&&e.add(Hs(t.sessionId,{kind:t.kind,ask:{id:t.id}}));return this.dismissals.list().filter(t=>e.has(t.key))}needsItemRaw(e,t,s=this.now(),o=null){if(e.archived)return null;if(!this.isMine(e)&&this.peers){let a=this.peers.needsFor(e.id,e.machine);if(a){let{session:l,...c}=a;return{...c,peer:e.machine}}}let r=this.asks.waiting({sessionId:e.id});if(o&&r.length>1){let a=r.filter(l=>!o(Hs(e.id,{kind:l.kind,ask:{id:l.id}})));a.length&&(r=a)}if(r.length){let a=r[0];return{kind:a.kind,blocking:!0,since:new Date(a.createdAt).toISOString(),waitSec:Math.round((s-a.createdAt)/1e3),ask:this.asks.publicAsk(a),more:r.length-1,...this.needExtras(e)}}let i=t||bt(e,s,this.cfgFor(e));if(i==="ended"||i==="stale"||e.endedAt)return null;if(e.needsInput){let a=e.needsInputAt||e.lastEventAt||e.lastActivityAt,l=this.isMine(e)?null:(()=>{let c=this.peerUsable(e.machine);return c.ok?null:"Answering from here: "+c.reason})();return{kind:"terminal",blocking:!0,since:a,waitSec:a?Math.round((s-R(a))/1e3):null,message:e.lastNotificationMessage||(e.lastNotification?e.lastNotification.replace(/_/g," "):"waiting in the terminal"),notification:e.lastNotification||null,...l?{peerNote:l}:{},...this.needExtras(e)}}if(e.question&&!e.inTurn&&this.isMine(e)){if(this.liveInfo(e,i,s).live==="closed")return null;let l=e.capture==="mod"&&this.replies.listening(e.id);return{kind:"reply",blocking:!1,since:e.questionAt,waitSec:e.questionAt?Math.round((s-R(e.questionAt))/1e3):null,question:e.question,canReply:l,replyPending:e.replyPending||null,why:l?null:e.capture==="mod"?"the mod in this session isn’t listening for replies (Claude Code closed, or a mod older than 0.4.4)":"replies need the mod (hooks-only capture can’t type into a session)",...this.needExtras(e,e.question)}}return null}needExtras(e,t=null){let s={};if(!this.isMine(e))return s;try{let o=typeof t=="string"?t:t&&t.text,r=this.needContextFor(e,o),i=e.repo&&e.repo.branch||null,a=r&&r.options.length?r.options:o&&zu.test(o)?Wu(o):[];(r||i||a.length)&&(s.context={lines:r?r.lines:[],options:a,branch:i,transcript:!!this.transcriptFile(e)});let l=this.prKey(e),c=l?this.prs.get(l.cwd,l.branch):null;c&&(s.pr=c)}catch(o){this.log("needs context: "+o.message)}return s}githubRepo(e){return e&&e.repo&&e.repo.github||null}async ensureGithub(e){if(!e||!e.repo)return null;if(e.repo.github)return e.repo.github;let t=e.repo.root||e.cwd;if(!t)return null;let s=this.githubRepos.get(t);if(!s||!s.repo&&this.now()-s.at>10*6e4){if(!s||!s.pending){let o=Promise.resolve().then(()=>this.gitRemote?this.gitRemote(t):zg(t)).catch(()=>null).then(r=>{let i=Ja(r);return this.githubRepos.set(t,{repo:i,at:this.now()}),i});this.githubRepos.set(t,{repo:s?s.repo:null,at:s?s.at:0,pending:o}),s=this.githubRepos.get(t)}await s.pending,s=this.githubRepos.get(t)}return s&&s.repo&&e.repo&&!e.repo.github&&(e.repo={...e.repo,github:s.repo},this.isMine(e)&&this.persist(e)),s&&s.repo||null}sessionPrRefs(e){if(!e||e.ignored||Ze(e.id))return[];let t=new Set(Zr(e.prUnlinked)),s=[];for(let i of Xr(e.prLinks))t.has(i.url)||s.push({...Us(i.url),via:i.via});let o=this.githubRepo(e),r=e.repo&&e.repo.branch;if(o&&r&&!["main","master","trunk","develop"].includes(r))for(let i of this.prStore.list(o)||[])i.branch===r&&(i.state==="open"||i.state==="draft")&&!t.has(i.url)&&!s.some(a=>a.url===i.url)&&s.push({repo:o,number:i.number,url:i.url,via:"branch"});return s}sessionPrs(e){try{return this.sessionPrRefs(e).map(t=>{let s=this.prStore.view(t.url);return s?wm(s,t.via):{repo:t.repo,number:t.number,url:t.url,title:null,state:null,via:t.via,checks:null}}).filter(t=>t.state!=="closed")}catch{return[]}}prsChanged(){this.prsTimer||(this.prsTimer=setTimeout(()=>{this.prsTimer=null,this.emit("prs.updated",{})},400),this.prsTimer.unref&&this.prsTimer.unref())}scanPrLinksTick(e=this.now()){let t=4;for(let s of this.sessions.values()){if(t<=0)break;if(!s.transcriptPath||!this.isMine(s)||s.archived||s.ignored||Ze(s.id))continue;let o=R(s.lastActivityAt);if(!(!o||e-o>14*864e5))try{this.scanPrLinks(s)&&t--}catch(r){this.log("pr links: "+r.message)}}}scanPrLinks(e){let t=e.transcriptPath,s;try{s=He.statSync(t)}catch{return!1}let o=this.prScans.get(e.id);if((!o||o.file!==t||s.size<o.offset)&&(o={file:t,offset:0,pending:new Set}),s.size===o.offset)return!1;let r=Sm(t,o.offset);if(o.offset=r.next,this.prScans.set(e.id,o),!r.text)return!0;let i=mm(r.text,{pending:o.pending});if(!i.length)return!0;let a=gm(Xr(e.prLinks),i,Zr(e.prUnlinked));if(a.changed){e.prLinks=a.links,this.persist(e);for(let l of a.links)this.prStore.view(l.url);this.emit("session.updated",this.publicSession(e))}return!0}get todoState(){return(!this._todoState||this._todoState.root!==this.root||this._todoState.machine!==this.machine)&&(this._todoState=new sl({root:this.root,machine:this.machine,now:this.now})),this._todoState}handoffsPublic(e){if(Ze(e.id))return{};let t=this.todoState,s=el(e.todos).filter(r=>!t.isDone(r.key));for(let r of t.addedList())r.sessionId===e.id&&!t.isDone(r.key)&&s.push({key:r.key,text:r.text,codes:[],at:r.at,manual:!0});let o=ei(e.files);return{todos:s,files:o,filesSummary:ld(o)}}scanHandoffsTick(e=this.now()){let t=2;for(let s of this.sessions.values()){if(t<=0)break;if(!this.isMine(s)||s.archived||s.ignored||Ze(s.id))continue;let o=R(s.lastActivityAt);if(!(this.handoffScans.has(s.id)&&(!o||e-o>14*864e5)))try{this.scanHandoffs(s)&&t--}catch(r){this.log("handoffs: "+r.message)}}}scanHandoffs(e){let t=this.transcriptFile(e);if(!t)return this.handoffScans.set(e.id,{file:null,size:-1}),!1;let s;try{s=He.statSync(t)}catch{return!1}let o=this.handoffScans.get(e.id);if(o&&o.file===t&&o.size===s.size)return!1;let{messages:r}=nn(t,{times:!0}),i=ad(r,{cwd:e.repo&&e.repo.root||e.cwd||null,home:this.handoffHome});this.handoffScans.set(e.id,{file:t,size:s.size,capture:i});let a=Dm(r,{sessionId:e.id}),l=el(e.todos),c=l.concat(a.filter(f=>!l.some(h=>h.key===f.key))).slice(-20),u=ei(i);return(JSON.stringify(c)!==JSON.stringify(l)||JSON.stringify(u)!==JSON.stringify(ei(e.files)))&&(e.todos=c.length?c:void 0,e.files=u||void 0,this.persist(e),this.emit("session.updated",this.publicSession(e)),c.length!==l.length&&this.emit("needs.updated",{what:"todos"})),!0}todoSessionInfo(e){if(!e)return null;let t=this.repoKeyFor(e);return{id:e.id,title:e.title||"Untitled session",repoLabel:this.repoLabels().get(t)||me(e),repoKey:t,machine:e.machine||this.machine,isLocal:this.isMine(e),endedAt:e.endedAt||null}}todosOverview(e=this.now()){let t=this.todoState,s=[],o=new Map;for(let a of this.sessions.values())if(!(a.archived||a.ignored||Ze(a.id)))for(let l of el(a.todos)){let c={...l,session:this.todoSessionInfo(a)};o.set(l.key,c),t.isDone(l.key)||s.push(c)}for(let a of t.addedList()){let l=a.sessionId?this.sessions.get(a.sessionId):null;if(l&&(l.archived||l.ignored))continue;let c={key:a.key,text:a.text,codes:[],at:a.at,manual:!0,session:this.todoSessionInfo(l)};o.set(a.key,c),t.isDone(a.key)||s.push(c)}s.sort((a,l)=>String(a.at||"").localeCompare(String(l.at||"")));let r=t.doneList().map(a=>{let l=o.get(a.key);return{key:a.key,text:l&&l.text||a.text||"To-do",doneAt:a.at,session:l&&l.session||this.todoSessionInfo(this.sessions.get(a.sessionId))}}),i=s.length?Math.min(...s.map(a=>R(a.at)||e)):null;return{open:s,done:r,count:s.length,oldestDays:i==null?null:Math.floor((e-i)/864e5)}}todoDone(e){let t=this.todosOverview().open.find(o=>o.key===e);if(!t)throw C(404,"not_found","That to-do isn’t open.");let s=this.todoState.done(e,{text:t.text,sessionId:t.session&&t.session.id});return this.todoChanged(t.session&&t.session.id),{ok:!0,key:e,done:s}}todoRestore(e){let t=this.todoState.restore(e);return this.todoChanged(null),{ok:!0,restored:t}}todoAdd(e,{sessionId:t=null}={}){let s=null;if(t){let r=this.sessions.get(String(t));if(!r||Ze(r.id))throw C(404,"not_found","no such session");s=r.id}let o;try{o=this.todoState.add(e,{sessionId:s})}catch(r){throw C(400,r.code||"bad_todo",r.message)}return this.todoChanged(s),{ok:!0,todo:{key:o.key,text:o.text,codes:[],at:o.at,manual:!0,session:this.todoSessionInfo(s&&this.sessions.get(s))}}}todoChanged(e){this.emit("needs.updated",{what:"todos"});let t=e?[e]:[];if(!e)for(let s of this.sessions.values())s.todos&&s.todos.length&&!s.archived&&t.push(s.id);for(let s of t){let o=this.sessions.get(s);o&&this.emit("session.updated",this.publicSession(o))}}async sessionFile(e,{path:t,plan:s,maxBytes:o=Qr,fromPeer:r=!1}={}){if(Ze(e))throw C(404,"not_found","External agents don’t share files.");let i=this.sessions.get(e);if(!i)throw C(404,"not_found","no such session");if(!this.isMine(i)){if(r)throw C(409,"other_machine","that session isn’t on "+this.machine);return{...(await this.viaPeer(i.machine,"session.file",{sessionId:e,path:t||null,plan:s||null},"Opening that file")).result,peer:i.machine}}let a=this.transcriptFile(i);if(!a)throw C(404,"no_transcript","Claude Code’s transcript for this session isn’t on this machine.");let{messages:l}=nn(a,{times:!0});if(s){let f=Fm(l,String(s));if(!f)throw C(404,"not_found","That plan isn’t in this session’s transcript.");let h=(ei(i.files)||{plans:[]}).plans.find(p=>p.id===String(s));return{name:h&&h.title||"Plan",plan:!0,type:"markdown",mime:"text/markdown",size:Buffer.byteLength(f),text:f.slice(0,o)}}let c=ad(l,{cwd:i.repo&&i.repo.root||i.cwd||null,home:this.handoffHome});if(!Pm(c,t))throw C(403,"not_allowed","Leftoff only opens files this session wrote.");let u=c.docs.find(f=>f.path===t),d=jm(t,{maxBytes:o});return d.missing?{name:u.name,path:t,missing:!0,message:"File no longer exists"}:d.tooBig?{name:u.name,path:t,tooBig:!0,size:d.size,type:d.type,mime:d.mime,message:"Too big to open here ("+Math.round(d.size/1024)+" KB)."}:{name:u.name,path:t,kind:u.kind,...d}}async linkPr(e,t={},{fromPeer:s=!1}={}){if(Ze(e))throw C(400,"agent_session","External agents don’t link PRs.");let o=this.sessions.get(e);if(!o)throw C(404,"not_found","no such session");let r=!!t.unlink;if(!this.isMine(o)){if(s)throw C(409,"other_machine","that session isn’t on "+this.machine);let f=(await this.viaPeer(o.machine,"session.prlink",{sessionId:e,ref:String(t.ref||""),unlink:r},"Linking a PR")).result||{};Array.isArray(f.prLinks)&&(o.prLinks=Xr(f.prLinks),o.prUnlinked=Zr(f.prUnlinked));let h=this.publicSession(o);return this.emit("session.updated",h),this.prsChanged(),{...f,prs:h.prs,peer:o.machine}}let i=await this.ensureGithub(o),a=hm(t.ref,{repo:i});if(a.error){let d={empty:"Paste a PR link or #number.",no_repo:"This session’s repo has no GitHub remote here, so paste the PR’s link.",bad_ref:"That isn’t a PR link or #number."}[a.error];throw C(400,a.error==="no_repo"?"no_repo":"bad_ref",d)}let l=Xr(o.prLinks),c=Zr(o.prUnlinked);r?(l=l.filter(d=>d.url!==a.url),c.includes(a.url)||c.push(a.url)):(c=c.filter(d=>d!==a.url),l.some(d=>d.url===a.url)||l.push({url:a.url,via:"manual"})),o.prLinks=l,o.prUnlinked=c,this.persist(o,!0),r||this.prStore.view(a.url);let u=this.publicSession(o);return this.emit("session.updated",u),this.prsChanged(),{ok:!0,sessionId:e,url:a.url,unlinked:r,prLinks:l,prUnlinked:c,prs:u.prs}}async prsOverview({view:e="mine",fresh:t=!1,fromPeer:s=!1,waitMs:o=8e3}={}){e=e==="team"?"team":"mine";let r;try{r=await this.prGhStatus({fresh:!!t})}catch{r=null}r=r||{status:"missing"};let i={status:r.status,account:r.account||null,fix:r.fix||null},a=G(this.now());if(r.status!=="ready"){if(!s&&this.peers){let w=await this.prsFromPeer(e,t);if(w)return s?w:this.hydratePrs(w)}return{view:e,gh:i,me:null,machine:this.machine,servedBy:null,repos:[],groups:[],loading:!1,updatedAt:a}}this.prStore.offUntil=0;let l=[...this.sessions.values()].filter(w=>!w.archived&&!w.ignored&&!Ze(w.id)),c=new Map;for(let w of l)if(w.repo&&!w.repo.github&&(w.repo.root||w.cwd)){let x=w.repo.root||w.cwd;c.has(x)||c.set(x,[]),c.get(x).push(w)}await Promise.all([...c.values()].slice(0,60).map(async w=>{let x=await this.ensureGithub(w[0]);if(x)for(let v of w.slice(1))v.repo&&!v.repo.github&&(v.repo={...v.repo,github:x},this.isMine(v)&&this.persist(v))}));let u=[...new Set(l.map(w=>this.githubRepo(w)).filter(Boolean))].sort(),d=e==="team"?u:[...new Set(l.filter(w=>w.repo&&w.repo.branch).map(w=>this.githubRepo(w)).filter(Boolean))],f=await this.prStore.settle({repos:d,waitMs:o,fresh:t}),h=new Map,p=(w,x)=>(R(x.lastActivityAt)||0)-(R(w.lastActivityAt)||0);for(let w of l.slice().sort(p))for(let x of this.sessionPrRefs(w)){let v=h.get(x.url);v||h.set(x.url,v={ref:x,via:new Set,sessionIds:[]}),v.via.add(x.via),v.sessionIds.includes(w.id)||v.sessionIds.push(w.id)}let m=r.account||null,g,S=0;if(e==="mine"){f=await this.prStore.settle({urls:[...h.keys()],waitMs:o,fresh:t})&&f;let w=[];for(let[x,v]of h){let b=this.prStore.views.get(x);if(!b||!b.pr){S++;continue}w.push({pr:b.pr,via:[...v.via],sessionIds:v.sessionIds})}g=ym(w,{now:this.now(),me:m})}else g=u.map(w=>{let x=this.prStore.lists.get(w),v=x&&x.prs;return{key:w,label:w,failed:!v,rows:(v||[]).slice().sort((b,y)=>String(y.updatedAt||"").localeCompare(String(b.updatedAt||""))).map(b=>{let y=h.get(b.url);return{pr:b,attention:ed(b,m),via:y?[...y.via]:[],sessionIds:y?y.sessionIds:[]}})}});let k={view:e,gh:i,me:m,machine:this.machine,servedBy:this.machine,repos:u,groups:g,unread:S,loading:!f||this.prStore.busy(),updatedAt:a};return s?k:this.hydratePrs(k)}hydratePrs(e){let t=this.now(),s=new Map,o=r=>{if(!s.has(r)){let i=this.sessions.get(r);s.set(r,i?this.publicSession(i,t):null)}return s.get(r)};for(let r of e.groups||[])for(let i of r.rows||[])i.sessions=(i.sessionIds||[]).map(o).filter(Boolean);return e}async prsFromPeer(e,t){let s=[];this.prPeer&&this.now()-this.prPeer.at<10*6e4&&s.push(this.prPeer.machine);let o=[];try{o=this.peers.overview().peers||[]}catch{}for(let r of o){let i=r.usable||{};(i.ok||["unreachable","checking"].includes(i.code))&&!s.includes(r.machine)&&s.push(r.machine)}for(let r of s.slice(0,3))try{let i=await this.peers.request(r,"prs.list",{view:e,fresh:!!t}),a=i&&i.result;if(a&&a.gh&&a.gh.status==="ready")return this.prPeer={machine:r,at:this.now()},this.prStore.absorb((a.groups||[]).flatMap(l=>(l.rows||[]).map(c=>c.pr))),{...a,servedBy:r}}catch{}return null}prKey(e){let t=e.repo&&e.repo.branch,s=e.repo&&e.repo.root||e.cwd;return!t||!s||e.ignored||["main","master","trunk","develop"].includes(t)?null:{cwd:s,branch:t}}transcriptFile(e){if(e.transcriptPath&&He.existsSync(e.transcriptPath))return e.transcriptPath;let t=this.transcriptHits.get(e.id);if(t&&t.dirs===this.transcriptDirs&&(t.file?He.existsSync(t.file):this.now()-t.at<15e3))return t.file;let s=null;try{s=vo(e.id,this.transcriptDirs?{dirs:this.transcriptDirs}:{})||null}catch{}return this.transcriptHits.set(e.id,{file:s,at:this.now(),dirs:this.transcriptDirs}),s}needContextFor(e,t){let s=this.transcriptFile(e);if(!s)return null;let o;try{o=He.statSync(s)}catch{return null}let r=s+":"+o.size+":"+o.mtimeMs+":"+(t||""),i=this.needCtx.get(e.id);if(i&&i.key===r)return i.value;let{messages:a}=nn(s,{maxBytes:512*1024}),l=Vp(a,{question:t});return this.needCtx.set(e.id,{key:r,value:l}),l}async sessionTurns(e,{limit:t=20}={}){let s=Math.max(1,Math.min(60,Number(t)||20));if(Ze(e))throw C(404,"no_transcript","External agents don’t share their transcripts.");let o=this.sessions.get(e);if(!o)throw C(404,"not_found","no such session");if(!this.isMine(o)){let l=await this.viaPeer(o.machine,"session.turns",{sessionId:e,limit:s},"Reading that session");return{...l.result,peer:o.machine,peerVia:l.via}}let r=this.transcriptFile(o);if(!r)throw C(404,"no_transcript","Claude Code’s transcript for this session isn’t on this machine.");let{messages:i,meta:a}=nn(r,{maxBytes:4*1024*1024});return{sessionId:e,machine:this.machine,branch:o.repo&&o.repo.branch||a.gitBranch||null,turns:Xp(i,{limit:s,cwd:o.repo&&o.repo.root||o.cwd||a.cwd})}}needsList(){return this.needsListAll({skipDismissed:!0}).filter(e=>!this.dismissals.has(e.key))}needsListAll({skipDismissed:e=!1}={}){let t=[],s=this.now();for(let o of this.sessions.values()){if(o.archived)continue;let r=this.needsItemRaw(o,null,s,e?a=>this.dismissals.has(a):null);if(r&&(r.key=Hs(o.id,r)),!r)continue;let i=this.publicSession(o,s);t.push({...r,session:{id:o.id,title:i.title,repoLabel:i.repoLabel,repoKey:i.repoKey,machine:i.machine,isLocal:i.isLocal,capture:i.capture,live:i.live,jump:i.jump,terminal:i.terminal,status:i.status}})}try{for(let o of this.agentView().agents)for(let r of o.records){let i=ts(o,r,s);if(!i)continue;i.key=Hs(cw(o,r),i);let a=zd(o,r,s);t.push({...i,session:{id:a.id,title:a.title,repoLabel:a.repoLabel,repoKey:a.repoKey,machine:a.machine,isLocal:!1,capture:"agent",live:null,jump:null,terminal:null,status:a.status,agent:a.agent}})}}catch(o){this.log("agents: "+o.message)}return t.sort((o,r)=>r.blocking-o.blocking||(r.waitSec||0)-(o.waitSec||0))}recentAnswered(e=20){let t={answered_in_terminal:"Answered in the terminal",cancelled_in_terminal:"Cancelled in the terminal (Esc)",new_prompt:"Moved on: you typed a new prompt",turn_ended:"Moved on: the turn ended",session_ended:"The session ended",no_listener:"Claude Code stopped waiting",unknown:"Gone"},s=[];for(let r of this.asks.items.values()){if(r.state==="waiting")continue;let i=this.sessions.get(r.sessionId),a=r.kind==="permission"?r.describe.verb+": "+r.describe.detail:(r.questions||[]).map(c=>c.question).join(" / "),l=r.state==="answered"?pi(r,r.answer)+" from the board":r.state==="expired"?"Timed out: left to the terminal":t[r.reason]||r.reason||"Gone";s.push({at:new Date(r.settledAt||r.createdAt).toISOString(),askedAt:new Date(r.createdAt).toISOString(),kind:r.kind,tool:r.tool,what:a,state:r.state,reason:r.reason,outcome:l,where:r.by?r.by.where:null,session:{id:r.sessionId,title:i?i.title||me(i):null,repoLabel:i?this.publicSession(i).repoLabel:null}})}let o=this.now()-24*3600*1e3;for(let r of this.sessions.values())for(let i of r.answers||[])i.kind!=="reply"||R(i.at)<o||s.push({at:i.at,askedAt:null,kind:"reply",tool:null,what:i.what,state:"answered",reason:null,outcome:i.summary+" from the board",where:i.where,session:{id:r.id,title:r.title||me(r),repoLabel:this.publicSession(r).repoLabel}});return s.sort((r,i)=>R(i.at)-R(r.at)).slice(0,e)}answeringInfo(e){let t=!!(this.cfg.remote&&this.cfg.remote.allowAnswers);return{remoteAllowed:t,allowedHere:e==="remote"?t:!0}}assertMayAnswer(e={}){if(e.via!=="peer"){if(e.via==="remote"&&!(this.cfg.remote&&this.cfg.remote.allowAnswers))throw C(403,"answers_remote_off","Answering from another device is off. On a Mac, turn on Settings → Privacy & security → Trust my paired devices.");if(e.proxied&&e.via!=="remote")throw C(403,"loopback_only","only from the machine running Leftoff")}}async answerAsk(e,t={},s={}){this.assertMayAnswer(s);let o=this.asks.get(e);if(!o&&this.peers&&this.peers.machineForAsk(e)){let u=this.peers.machineForAsk(e),d=await this.viaPeer(u,"ask.answer",{askId:e,body:t},"Answering");return this.log("answer forwarded to "+u+" via "+d.via+": "+(d.result&&d.result.summary)),this.peers.poll().catch(()=>{}),{...d.result,peer:u,peerVia:d.via}}if(!o)throw C(404,"not_found","That request is gone (Claude Code moved on, or the daemon restarted).");let r=this.sessions.get(o.sessionId);if(r&&!this.isMine(r))throw C(409,"other_machine","That session runs on "+r.machine);let i=jd(s),a=this.asks.answer(e,t,{where:i,via:s.via||"loopback",ip:s.ip||null,who:s.who||null}),l=await this.asks.delivery(e,3e3),c=pi(a,a.answer);return this.audit(a.sessionId,{at:new Date(a.answeredAt).toISOString(),kind:a.kind,tool:a.tool,what:a.kind==="permission"?a.describe.verb+": "+a.describe.detail:(a.questions||[]).map(u=>u.question).join(" / "),summary:c,action:t.action,where:i,via:s.via||"loopback",who:s.who||null,delivered:l}),{ok:!0,id:e,state:a.state,delivered:l,summary:c,ask:this.asks.publicAsk(a)}}async postReply(e,t,s={}){this.assertNotAgent(e,"replying"),this.assertMayAnswer(s);let o=this.sessions.get(e);if(!o)throw C(404,"not_found","no such session");if(!this.isMine(o)){let l=await this.viaPeer(o.machine,"session.reply",{sessionId:o.id,text:t},"Replying");return{...l.result,peer:o.machine,peerVia:l.via}}if(o.capture!=="mod")throw C(409,"needs_mod","Replies need the Leftoff mod; this session is captured by settings hooks, which can’t type into it. Use Go.");if(this.publicSession(o).live==="closed"||o.endedAt)throw C(409,"closed","That session is closed.");if(!this.replies.listening(o.id))throw C(409,"not_listening","This session’s mod isn’t picking up replies (Claude Code may be busy starting, or the mod is older than 0.4.4). Use Go.");let i=jd(s),a=this.replies.put(o.id,t,{where:i,via:s.via||"loopback"});return o.replyPending={id:a.id,at:new Date(a.at).toISOString(),text:a.text.slice(0,200)},this.audit(o.id,{at:new Date(a.at).toISOString(),kind:"reply",tool:null,what:o.question||"reply",summary:pi(null,{action:"reply",text:a.text}),action:"reply",where:i,via:s.via||"loopback",who:s.who||null,delivered:a.delivered,busy:!!o.inTurn}),this.emit("session.updated",this.publicSession(o)),this.emit("needs.updated",{sessionId:o.id,what:"replied"}),{ok:!0,id:a.id,delivered:a.delivered,queuedBehindTurn:!!o.inTurn}}async takeReply(e,t,s){let o=this.sessions.get(e),r=this.replies.listening(e),i=this.replies.take(e,t,s);!r&&o&&o.question&&this.emit("session.updated",this.publicSession(o));let a=await i;return a?{reply:{id:a.id,text:a.text}}:{reply:null}}audit(e,t){let s=this.sessions.get(e);if(this.log("answer from the board: "+t.summary+" · "+(t.what||"")+" · "+t.where+" · session "+String(e).slice(0,8)),!!s){s.answers=[...s.answers||[],t].slice(-20),this.persist(s,!0);try{let o=nr.join(this.folder.sessionDir(s),"answers.md"),r=He.existsSync(o)?"":`# Answered from the Leftoff board
217
+ `}function ow(){return Dn()==="darwin"?"launchctl load -w "+Lt():"systemctl --user daemon-reload && systemctl --user enable --now "+Ws}function Qs(n=Dn()){let e=Lt(n);return n==="darwin"?[["launchctl","unload",e],["launchctl","load","-w",e]]:[["systemctl","--user","daemon-reload"],["systemctl","--user","enable",Ws],["systemctl","--user","restart",Ws]]}function dx(n=Dn()){return n==="darwin"?[["launchctl","unload","-w",nr(n)]]:[["systemctl","--user","disable","--now",Id]]}function bf(n){let e=String(n||""),t=e.match(/<key>CHECKPOINTS_DIR<\/key>\s*<string>([^<]*)<\/string>/)||e.match(/^Environment="?CHECKPOINTS_DIR=([^"\n]*)"?$/m);return t?t[1].replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&"):null}function wc({root:n,bin:e=$e,platform:t=Dn(),dryRun:s=!1,run:o=ox}={}){let r=nr(t);if(!He.existsSync(r))return null;let i=Lt(t),a=He.readFileSync(r,"utf8"),l=bf(a)||n||Re.join(ls.homedir(),"checkpoints"),c=[];if(!s)for(let u of dx(t)){let d=o(u[0],u.slice(1),{encoding:"utf8"});c.push({cmd:u,code:d.status,stderr:String(d.stderr||"").trim()})}return He.mkdirSync(Re.dirname(i),{recursive:!0}),He.writeFileSync(i,Rn({root:l,bin:e,platform:t})),He.unlinkSync(r),{from:r,to:i,root:l,steps:c,dryRun:s}}var $e,pC,rx,ix,ax,nw,lx,cs=J(()=>{Ot();$e=Re.resolve(Re.dirname(sx(import.meta.url)),"..","bin","leftoff.js"),pC=Re.join(Re.dirname($e),"checkpoints.js"),rx=["SessionStart","UserPromptSubmit","Stop","Notification","SessionEnd","PermissionRequest","PostToolUse"],ix={SessionEnd:{timeout:2},PermissionRequest:{timeout:600},PostToolUse:{timeout:5,async:!0}},ax=ci,nw=[ci,Ys],lx=n=>nw.some(e=>String(n).includes(e))});import Sc from"node:fs";import iw from"node:path";import{execFileSync as fx}from"node:child_process";function kc(n){return Math.max(us(n.lastHeartbeatAt),us(n.lastEventAt),us(n.lastActivityAt),us(n.pidAliveAt),us(n.startedAt))}function yx(n,e){return Math.max(0,...[n.lastHeartbeatAt,n.lastEventAt,n.lastActivityAt,n.pidAliveAt,n.startedAt].map(us).filter(t=>t&&t<=e))}function aw(n){if(n==null||n===!0||n==="")return null;let e=String(n).trim().toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(m|min|h|hr|hours?|d|days?)?$/);if(!e)return null;let t=Number(e[1]),s=(e[2]||"h")[0],o=s==="m"?t/60:s==="d"?t*24:t;return o>0?o:null}function lw({platform:n=process.platform,exec:e=fx,readFile:t=Sc.readFileSync}={}){try{if(n==="darwin"){let o=String(e("/usr/sbin/sysctl",["-n","kern.boottime"],{encoding:"utf8",timeout:3e3})).match(/sec\s*=\s*(\d+)/);return o?Number(o[1])*1e3:null}if(n==="linux"){let s=String(t("/proc/stat","utf8")).match(/^btime\s+(\d+)/m);return s?Number(s[1])*1e3:null}}catch{}return null}function Af(n,e){try{return(JSON.parse(Sc.readFileSync(cw(n,e),"utf8")).starts||[]).map(us).filter(Boolean).sort((s,o)=>s-o)}catch{return[]}}function uw(n,e,t=Date.now()){let s=[...Af(n,e),t].sort((r,i)=>r-i).slice(-20),o=cw(n,e);try{Sc.mkdirSync(iw.dirname(o),{recursive:!0}),Sc.writeFileSync(o,JSON.stringify({machine:e,starts:s.map(r=>G(r))},null,2)+`
218
+ `)}catch{}return s}function dw({bootAt:n=null,starts:e=[],daemonStartedAt:t=null}={}){let s=[];if(n){let o=e.filter(r=>r>=n-6e4).sort((r,i)=>r-i)[0];s.push({kind:"boot",restartAt:n,upAt:Math.max(n,o||t||n)})}return t&&!s.some(o=>Math.abs(o.upAt-t)<6e4)&&s.push({kind:"daemon",restartAt:t,upAt:t}),s}function fw(n,{machine:e,moments:t=[],sinceHours:s=xf,isRunning:o=()=>!1,restored:r=new Map,now:i=Date.now(),repoFilter:a=null}={}){let l=n.filter(f=>f&&f.id&&(f.machine||e)===e),c=new Map,u=null;for(let f of t){let h=l.map(S=>yx(S,f.restartAt)).filter(Boolean),p=h.length?Math.max(...h):f.restartAt,m=Math.min(p,f.restartAt)-hx,g=f.upAt+px;for(let S of l){if(c.has(S.id)||S.archived||S.ignored||!S.cwd||pt(S)||S.surface&&S.surface!=="terminal"||o(S))continue;let k=r.get(S.id);if(k&&i-k<mx)continue;let w=kc(S);if(!w||w<f.restartAt-s*36e5||w>f.upAt+12e4)continue;let x=us(S.endedAt),v;if(!x)v="never ended";else{if(S.endReason&&gx.has(S.endReason)||x<m||x>g)continue;v=S.endReason==="closed"?"process gone after the restart":S.endReason?"ended ("+S.endReason+") as the Mac shut down":"ended at the restart"}a&&!a(S)||(c.set(S.id,{s:S,lastAliveAt:w,endedAt:x||null,why:v,moment:f.kind}),u=u||f)}}let d=[...c.values()].sort((f,h)=>h.lastAliveAt-f.lastAliveAt);return{moment:u||t[0]||null,sessions:d}}function wx(n){let e=String(n);return/^[A-Za-z0-9_./~@%+=:,-]+$/.test(e)?e:"'"+e.replace(/'/g,"'\\''")+"'"}function Sx(n,e={}){let t=n.title&&n.titleSource!=="none"?n.title:null;return e.tabTitle===!1?t:bu(e.tabTitleFormat,{name:t,id:n.id,repo:n.repo&&n.repo.name||null,marker:!!e.tabMarker})}function Tf(n,e={}){let t=Sx(n,e),s=Su(n,["--resume",n.id,...t?["--name",t]:[]]),o=vr(n);return{cwd:o,name:t,claude:s,line:"cd "+wx(o)+" && "+s,ollama:Vh(n.backend)}}function kx(){return["on u8(t)",' if t does not start with "'+vf+'" then return t'," set h to text "+(vf.length+1)+" thru -1 of t",' if h is "" then return ""',' return run script ((character id 171) & "data utf8" & h & (character id 187) & " as text")',"end u8","on run argv",' set out to ""',' tell application "Ghostty"'," set win to missing value"," try"," if (count of windows) > 0 then set win to front window"," end try"," set i to 1"," repeat while i < (count of argv)"," try"," set d to my u8(item i of argv)"," set c to my u8(item (i + 1) of argv)"," set cfg to new surface configuration"," set initial working directory of cfg to d"," set initial input of cfg to c & linefeed"," if win is missing value then"," set win to new window with configuration cfg"," else"," new tab in win with configuration cfg"," end if",' set out to out & "ok" & linefeed'," on error e",' set out to out & "error " & e & linefeed'," end try"," set i to i + 2"," end repeat"," activate"," end tell"," return out","end run"].join(`
219
+ `)}async function bc(n,{exec:e,platform:t=process.platform,timeoutMs:s=3e4}={}){if(!n.length)return{ok:!0,how:"ghostty",results:[],message:"nothing to restore"};if(t!=="darwin")return{ok:!1,how:"print",results:[],message:"Ghostty tabs can only be opened on macOS"};let o=["osascript","-e",kx(),...n.flatMap(c=>[rw(c.cwd),rw(c.claude)])],r=await e(o,{timeoutMs:s});if(r.missing)return{ok:!1,how:"print",results:[],message:"osascript isn't available"};if(r.code!==0){let c=String(r.stderr||"").trim(),u=/-1743|not allowed|Not authorized/i.test(c)?"macOS didn't let this app control Ghostty (System Settings → Privacy & Security → Automation)":/-2741|Expected|syntax error/i.test(c)?"this Ghostty has no AppleScript support (Ghostty 1.3.0 or newer has it)":/-1728|Can.t get application|-10814|Application isn.t running/i.test(c)?"Ghostty isn't installed":"Ghostty said: "+(c||"exit "+r.code);return{ok:!1,how:"print",results:[],message:u,stderr:c}}let i=String(r.stdout||"").split(/\r?\n/).filter(Boolean),a=n.map((c,u)=>{let d=i[u]||"";return d==="ok"?{ok:!0}:{ok:!1,error:d.replace(/^error\s*/,"")||"no answer from Ghostty"}}),l=a.filter(c=>c.ok).length;return{ok:l===n.length,how:"ghostty",results:a,message:l+" of "+n.length+" opened in Ghostty"}}var xf,hx,px,mx,gx,us,cw,vf,rw,_f=J(()=>{Te();xe();xf=24,hx=900*1e3,px=600*1e3,mx=600*1e3,gx=new Set(["prompt_input_exit","clear","logout","bypass_permissions_disabled","resume"]),us=n=>{let e=R(n);return isNaN(e)?0:e};cw=(n,e)=>iw.join(be(n).secret,"daemon-starts-"+String(e||"this").replace(/[^A-Za-z0-9._-]/g,"_")+".json");vf="u8:",rw=n=>vf+Buffer.from(String(n),"utf8").toString("hex").toUpperCase()});import{EventEmitter as bx}from"node:events";import Be from"node:fs";import hw from"node:os";import qe from"node:path";function Ax(n){try{return n()}catch(e){throw e&&e.badWindow?C(400,"bad_window",e.message):e}}async function Tx(n){try{return await n()}catch(e){throw e&&e.badWindow?C(400,"bad_window",e.message):e}}function _x(n){return n.kind==="permission"&&n.ask&&n.ask.describe?n.ask.describe.verb+": "+n.ask.describe.detail:n.kind==="question"&&n.ask?((n.ask.questions||[])[0]||{}).question||"A question":n.kind==="agent"?(n.items||[]).map(e=>e.text).join(" · "):n.question||n.message||""}function mw(n={}){let e=(o,r,i=400)=>(Array.isArray(o)?o:[]).map(a=>String(a??"").replace(/\s+/g," ").trim().slice(0,i)).filter(Boolean).slice(0,r),t=(Array.isArray(n.files)?n.files:[]).slice(0,10).map(o=>o&&typeof o=="object"?{path:String(o.path||"").trim().slice(0,300),why:String(o.why||"").replace(/\s+/g," ").trim().slice(0,300)}:{path:"",why:String(o||"").trim().slice(0,300)}).filter(o=>o.path||o.why);return{length:wn(n.length,{strict:!0})||null,decisions:e(n.decisions,6),questions:e(n.questions,5),files:t,didntWork:e(n.didntWork,5),recap:String(n.recap||"").replace(/\s+/g," ").trim().slice(0,1600),nextSteps:e(n.nextSteps,5)}}function C(n,e,t){let s=new Error(t);return s.status=n,s.code=e,s}function gw(n){return"Can’t resume: "+n+" isn’t there any more."}function Of(n){return Array.isArray(n)?n.filter(e=>e&&typeof e.root=="string"&&e.root).slice(0,8).map(e=>({root:e.root,name:typeof e.name=="string"&&e.name?e.name:qe.basename(e.root),branch:typeof e.branch=="string"?e.branch:null,n:Number(e.n)||0,...typeof e.worktree=="string"&&e.worktree?{worktree:e.worktree,worktreeName:typeof e.worktreeName=="string"?e.worktreeName:qe.basename(e.worktree)}:{}})):[]}var vx,xx,pw,Ef,vc,Cf=J(()=>{vo();Te();$u();Tn();Hr();nm();td();od();Lo();_m();sl();fd();Ps();gl();wl();Hs();kl();pi();qg();ey();$s();Hl();Ds();Qd();my();ri();xe();Vl();Ty();Oi();Xo();yi();as();rs();tw();cs();_f();vx=async()=>({ok:!1,code:"ENOENT",out:""}),xx=18e4;pw=(n,e)=>"agent:"+n.name+":"+e.session.id;Ef="Can’t resume through Ollama: ollama isn’t installed on this Mac (ollama.com/download).",vc=class extends bx{constructor({root:e,cfg:t,summarizer:s,now:o,log:r,digestSettleMs:i,pullImpl:a,pullFetch:l,tickMs:c,settleMs:u,machine:d,updateChecker:f,updateStartDelayMs:h,liveChecker:p,jumpRunner:m,liveCheckMs:g,jumpExec:S,ghosttyRefreshMs:k,notifier:w,askTimeoutMs:x,askNoListenerMs:v,bootTime:b,restoreExec:y,platform:_,healer:A,agentFetch:O,agentEnv:N,agentPollMs:I,dirExists:M,findOllama:Y,prRun:U,prGhStatus:E,gitRemote:P,handoffHome:W}={}){super(),this.root=e,this.cfg=t||Q(e),this.machine=d||de(this.cfg),this.folder=new Tt(e,this.machine),this.lastRemoteScan=0,this.updateChecker=f||null,this.updateStartDelayMs=h,this.summarizer=s||new _t(this.cfg,{root:this.root}),this.now=o||(()=>Date.now()),this.log=r||(()=>{}),this.pullImpl=a||null,this.pullFetch=l||null,this.tickMs=c||Number(process.env.CHECKPOINTS_TICK_MS)||15e3,this.settleMs=u??Number(process.env.CHECKPOINTS_SETTLE_MS||5e3),this.sessions=new Map,this.checkpoints=new Map,this.seen=new Map,this.generating=new Set,this.persistTimers=new Map,this.lastHbPersist=new Map,this.startedAt=Date.now(),this.bootTimeOpt=b,this.restoreExec=y||S||null,this.dirExists=M||(oe=>{try{return Be.statSync(oe).isDirectory()}catch{return!1}}),this.findOllama=Y||(()=>Fs()),this.backendChecked=new Set,this.platform=_||process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform,this.restored=new Map,this.restoring=!1,this.timers=[],this.liveChecker=p||Ay,this.jumpRunner=m||oc,this.jumpExec=S||null,this.ghostty=new Zl({exec:S||Xt,log:oe=>this.log(oe)}),this.ghosttyRefreshMs=k||Number(process.env.CHECKPOINTS_GHOSTTY_REFRESH_MS)||6e4,this.boardClients=0,this.lastGoAt=0,this.jumping=null,this.liveCheckMs=g||Number(process.env.CHECKPOINTS_LIVE_CHECK_MS)||(Number(this.cfg.liveCheckSec)||30)*1e3,this.asks=new Al({now:this.now,onChange:(oe,K)=>this.onAskChange(oe,K),...x||process.env.CHECKPOINTS_ASK_TIMEOUT_MS?{timeoutMs:x||Number(process.env.CHECKPOINTS_ASK_TIMEOUT_MS)}:{},...v||process.env.CHECKPOINTS_ASK_NO_LISTENER_MS?{noListenerMs:v||Number(process.env.CHECKPOINTS_ASK_NO_LISTENER_MS)}:{}}),this.replies=new Tl({now:this.now}),this.notifier=w||ew,this.notified=new Map,this.healer=A||(process.env.CHECKPOINTS_HEAL==="off"?null:zp),this.healTries=new Map,this.healed=0,this.openStateTimer=null,this.lists=new ts(this.root,this.machine,{now:this.now}),this.listCache=null,this.listHints=new Map(Object.entries(this.readState("list-hints")||{})),this.transcriptDirs=null,this.needCtx=new Map,this.transcriptHits=new Map,this.prs=new Ya({now:this.now,onUpdate:({cwd:oe,branch:K})=>{for(let pe of this.sessions.values())this.prKey(pe)&&this.prKey(pe).cwd===oe&&this.prKey(pe).branch===K&&this.emit("session.updated",this.publicSession(pe));this.emit("needs.updated",{what:"pr"})}}),this.prStore=new Va({run:U||(process.env.NODE_TEST_CONTEXT?vx:sd),now:this.now,onUpdate:()=>this.prsChanged()}),this.prGhStatus=E||(process.env.NODE_TEST_CONTEXT?async()=>({status:"missing"}):oe=>za(oe)),this.gitRemote=P||null,this.prScans=new Map,this.handoffScans=new Map,this.handoffHome=W||hw.homedir(),this.githubRepos=new Map,this.prPeer=null,this.agents=new It(this.root,this.machine,{now:this.now,log:oe=>this.log(oe),fetchImpl:O||null,env:N||process.env,userAgent:"Leftoff/"+X}),this.agentPollMs=I||Number(process.env.LEFTOFF_AGENTS_POLL_MS)||ry,this.spaces=new La(this.root,this.machine,{now:this.now}),this.digestSettleMs=i??Number(process.env.LEFTOFF_DIGEST_SETTLE_MS||9e4),this.awakeSince=this.now()}isMine(e){return!e.machine||e.machine===this.machine}cfgFor(e){return this.isMine(e)?this.cfg:{...this.cfg,staleAfterSec:Math.max(this.cfg.staleAfterSec,900)}}load(){for(let{session:o,checkpoints:r}of this.folder.loadAll()){if(this.sessions.get(o.id))continue;o.statusChanges=[],this.sessions.set(o.id,o),this.checkpoints.set(o.id,r.sort((l,c)=>R(l.createdAt)-R(c.createdAt)));let a=r[r.length-1];a&&(!o.lastCheckpointAt||R(a.createdAt)>R(o.lastCheckpointAt))&&(o.lastCheckpointAt=a.createdAt,o.lastCheckpointId=a.id),o.needsRewrite&&(delete o.needsRewrite,this.isMine(o)&&this.persist(o,!0))}let e=this.loadOpenState(),t=this.drainSpool(),s=this.archiveGhosts();return this.log("loaded "+this.sessions.size+" sessions ("+e+" open without turns yet); drained "+t+" spooled items"+(s?"; archived "+s+" empty sessions with no folder":"")),this.loadUiState(),this}stateFile(e){return qe.join(this.root,"."+e+"."+this.machine.replace(/[^\w.-]/g,"_")+".json")}readState(e){try{return JSON.parse(Be.readFileSync(this.stateFile(e),"utf8"))}catch{return null}}writeState(e,t){try{Be.writeFileSync(this.stateFile(e),JSON.stringify(t,null,2)+`
220
+ `)}catch{}}loadUiState(){let e=this.readState("ui-state"),t=e;if(t)t.lastRunVersion!==X&&(t={...t,upgradedFrom:t.lastRunVersion||null,upgradeNoticeFor:X,lastRunVersion:X});else{let s=this.sessions.size===0;t={lastRunVersion:X,lastSeenVersion:s?X:null,upgradedFrom:s?null:"an earlier version",upgradeNoticeFor:s?null:X}}this.uiState=t,JSON.stringify(e)!==JSON.stringify(t)&&this.writeState("ui-state",t)}changelog(){let e=this.uiState||{},t=xg(Uo(),e.lastSeenVersion,X);return{current:X,lastSeen:e.lastSeenVersion||null,upgradedFrom:e.upgradedFrom||null,upgradeNotice:e.upgradeNoticeFor===X,entries:t}}markChangelogSeen(e){let t=e||X,s={...this.uiState||{}};return(!s.lastSeenVersion||Xe(t,s.lastSeenVersion)>0)&&(s.lastSeenVersion=Xe(t,X)>0?X:t),s.upgradeNoticeFor=null,this.uiState=s,this.writeState("ui-state",s),this.changelog()}dismissUpgradeNotice(){return this.uiState={...this.uiState||{},upgradeNoticeFor:null},this.writeState("ui-state",this.uiState),{ok:!0}}updateChecksOn(){return!(this.cfg.updates&&this.cfg.updates.check===!1)&&!process.env.CHECKPOINTS_NO_UPDATE_CHECK}scheduleUpdateCheck(){if(this._updateTimer&&clearTimeout(this._updateTimer),!this.updateChecksOn())return;let e=this.readState("update-state"),t=e&&e.checkedAt?Date.now()-Date.parse(e.checkedAt):1/0,s=t<36e5?Rd-t:this.updateStartDelayMs??2e4;this._updateTimer=this.later(Math.max(1e3,s),async()=>{try{await this.runUpdateCheck()}catch(o){this.log("update check failed: "+o.message)}this.stopped||this.scheduleUpdateCheck()})}async runUpdateCheck({force:e=!1}={}){if(!e&&!this.updateChecksOn())return this.updateState();let t=this.cfg.updates&&this.cfg.updates.source||process.env.CHECKPOINTS_UPDATE_SOURCE||null,s=await(this.updateChecker||hi)({source:t});return this.writeState("update-state",s),s.available&&this.log("update available: "+(s.remote.version?"v"+s.remote.version:s.remote.sha.slice(0,7))),this.emit("session.updated",{update:!0}),this.updateState()}updateState(){let e=this.readState("update-state");if(!e)return{checked:!1,enabled:this.updateChecksOn()};let t=e.current&&e.current.version!==X;return{...e,checked:!0,enabled:this.updateChecksOn(),available:!!e.available&&!t}}start(){let e=setInterval(()=>this.tick().catch(i=>this.log("tick error: "+i.message)),this.tickMs);e.unref&&e.unref(),this.timers.push(e);let t=setInterval(()=>this.checkLiveness().catch(i=>this.log("live check error: "+i.message)),this.liveCheckMs);t.unref&&t.unref(),this.timers.push(t),this.later(500,()=>this.checkLiveness().catch(()=>{}));for(let i of this.sessions.values())this.isMine(i)&&i.capture==="mod"&&!i.cwd&&!i.endedAt&&!i.archived&&this.scheduleHeal(i,2e3);this.daemonStarts=uw(this.root,this.machine,this.startedAt);let s=setInterval(()=>{(this.boardClients>0||Date.now()-this.lastGoAt<10*6e4)&&this.scheduleGhosttyRefresh("periodic",0)},this.ghosttyRefreshMs);s.unref&&s.unref(),this.timers.push(s),this.scheduleDigest(),this.scheduleUpdateCheck();let o=setInterval(()=>this.syncthingTick().catch(i=>this.log("syncthing: "+i.message)),Number(process.env.LEFTOFF_SYNCTHING_POLL_MS)||1500);o.unref&&o.unref(),this.timers.push(o);let r=setInterval(()=>this.agentsTick().catch(i=>this.log("agents: "+i.message)),this.agentPollMs);return r.unref&&r.unref(),this.timers.push(r),this.later(Number(process.env.LEFTOFF_AGENTS_FIRST_MS)||3e3,()=>this.agentsTick().catch(()=>{})),this}async agentsTick(){if(this.stopped)return[];let e=[];for(let t of this.agents.sources())if(this.agents.pullsHere(t))try{e.push(await this.pullAgent(t.name))}catch(s){this.log("agents: "+t.name+": "+s.message)}return e}agentError(e){if(e&&e.status)return e;let t=e&&e.code||"agent_error";return C(t==="not_found"?404:t==="not_puller"?409:t==="bad_name"||t==="bad_url"||t==="bad_key"?400:500,t,e&&e.message||String(e))}async pullAgent(e,{force:t=!1}={}){let s;try{s=await this.agents.pull(e,{force:t})}catch(o){throw this.agentError(o)}return s.result==="ok"&&s.applied&&s.applied.written&&(this.listCache=null,this.emit("session.updated",{agent:e}),this.emit("needs.updated",{agent:e}),this.emit("lists.updated",{agent:e})),this.emit("agents.updated",{name:e,result:s.result}),s}agentView(){return this.agents.view()}agentsOverview({local:e=!0}={}){let t=this.agents.view({fresh:!0});return{machine:this.machine,pollSec:Math.round(this.agentPollMs/1e3),agents:t.agents.map(s=>({name:s.name,display:s.display,icon:s.icon,url:s.url,reply:s.reply||null,replyHint:s.replyHint||null,feedUrl:e?s.source.url:(()=>{try{return new URL(s.source.url).host}catch{return null}})(),addedAt:s.source.addedAt,addedOn:s.source.addedOn,puller:s.puller,pullsHere:s.pullsHere,hasKey:s.hasKey,status:s.status,sessions:s.records.length,open:s.records.filter(o=>o.inFeed&&o.session.status!=="done").length,needsYou:s.records.reduce((o,r)=>o+(this.agentNeed(s,r)||{items:[]}).items.length,0)}))}}async addAgent(e={}){let t;try{t=this.agents.add({name:String(e.name||"").trim().toLowerCase(),url:e.url,key:e.key==null||e.key===""?null:e.key})}catch(o){throw this.agentError(o)}this.log("agents: "+(t.replaced?"updated ":"added ")+t.source.name+" ("+t.source.url+")"+(t.previousPuller?"; now pulled here instead of "+t.previousPuller:""));let s=null;if(e.pull!==!1)try{s=await this.pullAgent(t.source.name,{force:!0})}catch(o){s={result:"error",error:{message:o.message}}}return this.emit("agents.updated",{name:t.source.name,result:"added"}),{...t,pull:s}}removeAgent(e,{purge:t=!1}={}){let s;try{s=this.agents.remove(e,{purge:t})}catch(o){throw this.agentError(o)}return this.log("agents: removed "+e+(s.movedTo?" (history kept in "+s.movedTo+")":" (purged)")),this.listCache=null,this.emit("session.updated",{agent:e}),this.emit("needs.updated",{agent:e}),this.emit("lists.updated",{agent:e}),this.emit("agents.updated",{name:e,result:"removed"}),s}agentPub(e,t,s=this.now(),o){let r=qd(e,t,s,o);return r.needsYou=this.agentNeed(e,t,s),r}agentSessions(e=this.now()){let t=[];for(let s of this.agentView().agents)for(let o of s.records)t.push(this.withSpace(this.agentPub(s,o,e)));return t}agentSession(e,{full:t=!0}={}){for(let s of this.agentView().agents)for(let o of s.records)if("agent:"+s.name+":"+o.session.id===e)return this.withSpace(this.agentPub(s,o,this.now(),{full:t}));return null}assertNotAgent(e,t="that"){if(et(e))throw C(409,"agent_session","this session comes from an external agent’s feed and is read-only here; "+t+" happens with the agent itself")}async syncthingTick(){if(this.stBusy||this.stopped)return;let e=Ft();if(!(!e||e.used||e.expiredLogged)){this.stBusy=!0;try{let t=mt();if(!t.ok)return;await Pr({client:t.client,log:s=>this.log(s)})}finally{this.stBusy=!1}}}needsTabMarker(e,t=[]){if(!e||!this.isMine(e))return!1;let s=e.term||{};if(s.kind!=="ghostty"||Xl(s.version)===!0)return!1;let o=c=>Ts(c.title||"").trim().toLowerCase(),r=c=>String(c.cwd||"").replace(/\/+$/,""),i=o(e),a=r(e);return[...t,...[...this.sessions.values()].filter(c=>this.isMine(c)&&!c.endedAt&&!["ended","stale"].includes(c.status))].some(c=>c&&c.id!==e.id&&(i&&o(c)===i||a&&r(c)===a))}later(e,t){let s=setTimeout(t,e);return s.unref&&s.unref(),this.timers.push(s),s}stop(){this.stopped=!0;for(let e of this.timers)clearInterval(e),clearTimeout(e);for(let e of this.persistTimers.values())clearTimeout(e);for(let e of this.sessions.values())this.persistNow(e);this.openStateTimer&&(clearTimeout(this.openStateTimer),this.openStateTimer=null),this.writeOpenState()}openStateRows(){let e=["id","machine","capture","cwd","repo","transcriptPath","model","surface","pid","tty","pidStartedAt","pidSource","term","startedAt","lastEventAt","lastHeartbeatAt","lastActivityAt","parentSessionId","startSource","historyTurns","healedAt","healedVia","inTurn","needsInput","firstPrompt","startRepo"],t=[];for(let s of this.sessions.values()){if(!this.isMine(s)||s.endedAt||s.archived||this.worthPersisting(s))continue;let o={};for(let r of e)s[r]!==void 0&&s[r]!==null&&(o[r]=s[r]);t.push(o)}return t}scheduleOpenState(){this.openStateTimer||this.stopped||(this.openStateTimer=setTimeout(()=>{this.openStateTimer=null,this.writeOpenState()},1e3),this.openStateTimer.unref&&this.openStateTimer.unref())}writeOpenState(){let e=this.openStateRows(),t=this.lastOpenState,s=JSON.stringify({savedAt:G(Date.now()),sessions:e},null,2)+`
221
+ `;if(t!==JSON.stringify(e)){this.lastOpenState=JSON.stringify(e);try{Be.writeFileSync(this.stateFile("open-sessions"),s)}catch{}}}loadOpenState(){let e=this.readState("open-sessions");if(!e||!Array.isArray(e.sessions))return 0;let t=this.now(),s=0;for(let o of e.sessions){if(!o||!o.id||this.sessions.has(o.id))continue;let r=Math.max(R(o.lastHeartbeatAt)||0,R(o.lastEventAt)||0,R(o.startedAt)||0);if(!o.pid&&t-r>24*36e5)continue;let i={turns:0,turnsSinceCheckpoint:0,backgroundTasks:0,pinned:!1,archived:!1,...o,machine:o.machine||this.machine,statusChanges:[]};this.sessions.set(i.id,i),this.checkpoints.set(i.id,[]),s++}return this.lastOpenState=null,s}archiveGhosts(){let e=this.now(),t=0;for(let s of this.sessions.values()){if(!this.isMine(s)||s.archived||!s.folder||!po(s,(this.checkpoints.get(s.id)||[]).length))continue;let o=R(s.lastHeartbeatAt||s.lastEventAt)||0;!s.endedAt&&e-o<(this.cfg.staleAfterSec||600)*1e3||(s.archived=!0,s.archivedReason="empty session with no folder or process (v0.4.8 cleanup)",this.persist(s,!0),t++)}if(t){let s=this.readState("cleanup")||{};this.writeState("cleanup",{...s,ghostsArchived:(s.ghostsArchived||0)+t,lastArchivedAt:G(e),lastArchived:t})}return t}emptyCounts(){let e=0,t=0;for(let o of this.sessions.values()){if(o.archived||!this.isMine(o))continue;let r=(this.checkpoints.get(o.id)||[]).length;pt(o,r)&&(e++,po(o,r)&&t++)}let s=this.readState("cleanup")||{};return{empty:e,ghosts:t,healed:this.healed,archivedGhosts:s.ghostsArchived||0,lastArchivedAt:s.lastArchivedAt||null}}noteHistory(e){let t=Ku(e.transcriptPath);if(!t)return;e.historyTurns=t.prompts||0;let s=this.noteTranscriptModel(e,t.model);(e.historyTurns>0||s)&&this.persist(e)}noteTranscriptModel(e,t){if(!t||uo(t))return!1;if(!e.backend){let s=Jh(t);return s?(e.backend=s,!0):!1}return e.backend.model===t?!1:(e.backend={...e.backend,model:t},!0)}inferBackend(e){if(!e||e.backend||!e.transcriptPath||this.backendChecked.has(e.id)||!this.isMine(e))return!1;this.backendChecked.add(e.id);let{meta:t}=on(e.transcriptPath,{maxBytes:256*1024}),s=this.noteTranscriptModel(e,t&&t.model);return s&&this.persist(e),s}scheduleHeal(e,t=1e3){if(!this.healer||this.stopped||e.healTimer||e.healing)return;let s=this.healTries.get(e.id)||{n:0,at:0},o=[0,6e4,3e5,9e5][s.n]??18e5,r=Math.max(t,s.at+o-Date.now());e.healTimer=setTimeout(()=>{e.healTimer=null,this.healSession(e).catch(i=>this.log("heal "+e.id.slice(0,8)+": "+i.message))},r),e.healTimer.unref&&e.healTimer.unref()}async healSession(e){if(!this.healer||e.healing||e.archived||e.endedAt||!this.isMine(e)||e.cwd&&e.pid)return!1;let t=this.healTries.get(e.id)||{n:0,at:0};this.healTries.set(e.id,{n:t.n+1,at:Date.now()}),e.healing=!0;try{let s=new Set([...this.sessions.values()].filter(i=>i!==e&&i.pid&&!i.endedAt).map(i=>i.pid)),o=await this.healer(e.id,{cwd:e.cwd||null,claimedPids:s});if(!o)return!1;let r=[];return o.cwd&&!e.cwd&&(e.cwd=o.cwd,r.push("folder")),o.transcriptPath&&!e.transcriptPath&&(e.transcriptPath=o.transcriptPath),o.prompts!=null&&e.historyTurns==null&&(e.historyTurns=Math.max(0,o.prompts-(e.turns||0))),o.customTitle&&!e.title&&!Ar(o.customTitle).id&&(e.title=F(o.customTitle,120),e.titleSource="custom",r.push("name")),o.proc&&o.proc.pid>1&&!e.pid&&(e.pid=o.proc.pid,e.tty=o.proc.tty||null,e.pidStartedAt=o.proc.pidStartedAt||null,e.pidSource="daemon",e.pidAliveAt=this.now(),r.push("process "+e.pid)),o.term&&!e.term&&(e.term=o.term,r.push("terminal "+(o.term.label||o.term.kind))),r.length?(e.healedAt=G(this.now()),e.healedVia=(o.via||[]).join(","),this.healed++,this.log("session "+e.id.slice(0,8)+" was known only by id; found its "+r.join(", ")+" ("+e.healedVia+")"),r.includes("folder")&&await this.enrichRepo(e),this.refreshStatus(e),this.persist(e,!0),this.emit("session.updated",this.publicSession(e)),!0):!1}finally{e.healing=!1,!e.endedAt&&!e.archived&&(!e.cwd||!e.pid)&&!this.stopped&&this.scheduleHeal(e,5e3)}}drainSpool(){let e=0;for(let t of this.folder.readSpool())try{let s=JSON.parse(t.body);s&&s.hook&&s.payload?this.ingestHook(s.hook,s.payload,s.at?R(s.at):t.t):s&&Array.isArray(s.events)?this.ingest(s.events):s&&s.type&&s.sessionId?this.ingest([s]):s&&s.checkpoint&&s.sessionId&&this.addCheckpoint(s.sessionId,s.checkpoint),Be.unlinkSync(t.file),e++}catch(s){this.log("spool item skipped: "+t.file+" "+s.message);try{Be.renameSync(t.file,t.file+".bad")}catch{}}return e}ingest(e){let t=this.now(),s=0,o=0,r=new Set;for(let u of e||[]){if(!u||!u.sessionId||!Uh.includes(u.type))throw C(400,"bad_event","each event needs sessionId and a known type");if(et(String(u.sessionId)))throw C(400,"bad_event","agent:… ids belong to external agents’ feeds");let d=u.type+"|"+(u.at||""),f=this.seen.get(u.sessionId);if(f||this.seen.set(u.sessionId,f=new Set),u.at&&f.has(d)){o++;continue}if(u.at&&(f.add(d),f.size>2e3&&f.delete(f.values().next().value)),process.env.CHECKPOINTS_IGNORE_SESSION&&u.sessionId===process.env.CHECKPOINTS_IGNORE_SESSION)continue;let h=this.sessions.get(u.sessionId),p=!h;h||(h={id:u.sessionId,machine:this.machine,turns:0,turnsSinceCheckpoint:0,backgroundTasks:0,pinned:!1,archived:!1,statusChanges:[]},this.sessions.set(h.id,h),this.checkpoints.set(h.id,[]));let m=h.title,g=h.turns,S=h.cwd;op(h,{...u,at:u.at||G(t)}),(p||u.data&&u.data.cwd&&(!(h.repo&&h.repo.root)||h.cwd!==S||u.data.repo&&typeof u.data.repo=="object")||u.type==="turn_completed")&&this.enrichRepo(h),u.type==="session.started"&&h.startSource==="resume"&&h.historyTurns==null&&h.transcriptPath&&this.noteHistory(h),!h.cwd&&h.capture==="mod"&&this.isMine(h)&&this.scheduleHeal(h),h.turns===1&&g===0&&!h.title&&this.autoTitle(h),h.title!==m&&(h.titleChangedAt=G(t)),u.type==="session.ended"&&this.onEnded(h),u.type==="turn_completed"&&u.data&&u.data.listHint&&typeof u.data.listHint=="object"&&this.noteListHint(h,u.data.listHint,u.at||G(t)),this.settleAsksFor(h,u),h.term&&h.term.kind==="ghostty"&&this.isMine(h)&&(u.type==="session.opened"?this.scheduleGhosttyRefresh("session started",1500):h.title!==m&&this.scheduleGhosttyRefresh("renamed",3e3)),s++,r.add(h)}let i;for(let u of r){this.refreshStatus(u,t);let d=e.every(f=>f.type==="heartbeat");if(this.persist(u,!1,d),this.emit("session.updated",this.publicSession(u)),!i&&u.capture==="mod"&&!["ended","stale"].includes(u.status)){let f=xu(u,t,this.cfg);f&&(u.dueSince||(u.dueSince=t),i={sessionId:u.id,reason:f})}}let a=r.size===1?[...r][0]:null,l=!!(a&&a.capture==="mod"&&this.isMine(a)&&!a.cwd&&!a.endedAt),c=a?this.listSignature(a.id):null;return{ok:!0,accepted:s,duplicate:o,...i?{checkpointDue:i}:{},...a?{session:{id:a.id,title:a.title||null,repoName:ge(a),tabMarker:this.needsTabMarker(a)}}:{},...l?{hello:!0}:{},...c?{lists:c}:{}}}ingestHook(e,t,s){if(t&&t.session_id&&process.env.CHECKPOINTS_IGNORE_SESSION===t.session_id)return{ok:!0,accepted:0};let o=Jg(e,t,s||this.now());return o.length?this.ingest(o):{ok:!0,accepted:0,ignored:!0}}enrichRepo(e){if(!e.cwd||!this.isMine(e))return;let t=this.dirExists(e.cwd)?wi(e.cwd):null;t&&this.applyWhere(e,t),Zg(e.repo&&e.repo.root||e.cwd,this.cfg)&&(e.ignored=!0);let s=e.repo&&e.repo.root;t&&s&&!e.repo.github&&Promise.resolve().then(()=>(this.gitRemote||Hd)(t.top)).then(o=>{let r=Zr(o);r&&e.repo&&e.repo.root===s&&!e.repo.github&&(e.repo={...e.repo,github:r},this.persist(e))}).catch(()=>{}),!t&&!e.repo&&Vg(e.cwd).then(o=>{o&&!(e.repo&&e.repo.root)&&(e.repo=o,this.persist(e))}).catch(()=>{}),this.persist(e)}applyWhere(e,t){let s=e.repo||{},o=t.mainRoot,r={...s.root===o?s:{},root:o,name:qe.basename(o).replace(/\.git$/,"")||qe.basename(o),branch:t.branch||null};delete r.worktree,delete r.worktreeName,t.worktree&&(r.worktree=t.worktree.path,r.worktreeName=t.worktree.name),s.root===o&&s.github&&(r.github=s.github),e.repo=r,e.startRepo||(e.startRepo=o)}onEnded(e){e.endSettleUntil=this.now()+this.settleMs,this.later(this.settleMs+20,()=>this.tick().catch(()=>{}))}refreshRemote(){this.lastRemoteScan=this.now();for(let{session:e,checkpoints:t}of this.folder.loadAll()){if(this.isMine(e))continue;let s=this.sessions.get(e.id),o=this.checkpoints.get(e.id)||[];if(s&&!this.isMine(s)){if(s.lastEventAt===e.lastEventAt&&s.lastActivityAt===e.lastActivityAt&&s.title===e.title&&o.length===t.length&&s.endedAt===e.endedAt&&JSON.stringify(s.prLinks||null)===JSON.stringify(e.prLinks||null)&&JSON.stringify(s.prUnlinked||null)===JSON.stringify(e.prUnlinked||null)&&JSON.stringify(s.todos||null)===JSON.stringify(e.todos||null)&&JSON.stringify(s.files||null)===JSON.stringify(e.files||null))continue;e.statusChanges=s.statusChanges||[],e.status=s.status}else{if(s)continue;e.statusChanges=[]}this.sessions.set(e.id,e),this.checkpoints.set(e.id,t.sort((r,i)=>R(r.createdAt)-R(i.createdAt))),this.refreshStatus(e),this.emit("session.updated",this.publicSession(e))}}refreshStatus(e,t=this.now()){let s=xt(e,t,this.cfgFor(e));return s!==e.status?(e.status=s,e.statusChanges=[...e.statusChanges||[],{at:G(t),status:s}].slice(-50),!0):!1}bootTime(){return this.bootTimeOpt!==void 0?typeof this.bootTimeOpt=="function"?this.bootTimeOpt():this.bootTimeOpt:(this.bootTimeCache===void 0&&(this.bootTimeCache=process.env.CHECKPOINTS_BOOT_TIME?Date.parse(process.env.CHECKPOINTS_BOOT_TIME)||null:lw()),this.bootTimeCache)}isRunningNow(e,t=this.now()){return this.liveInfo(e,xt(e,t,this.cfgFor(e)),t).live==="open"}restoreView(e,t,s,o=[]){let r=this.resumeTarget(e),i=this.publicSession(r,s),a=Tf(r,{...this.cfg,tabMarker:this.needsTabMarker(r,o)});return{id:r.id,shortId:i.shortId,title:i.title,repoLabel:i.repoLabel,repoKey:i.repoKey,branch:r.repo&&r.repo.branch||null,cwd:a.cwd,...i.worktree?{worktree:i.worktree.name}:{},...r.folderGone?{folderGone:r.folderGone}:{},lastAliveAt:t?new Date(t.lastAliveAt).toISOString():kc(r)?new Date(kc(r)).toISOString():null,endedAt:r.endedAt||null,why:t?t.why:null,name:a.name,command:a.line,claude:a.claude,modelLabel:r.backend?i.modelLabel:null,...a.ollama&&!this.findOllama()?{blocked:"no_ollama",error:Ef}:{}}}restoreCandidates({since:e,repo:t}={},s=this.now()){let o=aw(e)||xf,r=this.bootTime(),i=dw({bootAt:r,starts:this.daemonStarts||Af(this.root,this.machine),daemonStartedAt:this.startedAt}),a=t?String(t).toLowerCase():null,l=a?d=>[this.repoKeyFor(d),d.repo&&d.repo.name,d.cwd].some(f=>f&&String(f).toLowerCase().includes(a)):null,c=fw([...this.sessions.values()],{machine:this.machine,moments:i,sinceHours:o,isRunning:d=>this.isRunningNow(d,s),restored:this.restored,now:s,repoFilter:l}),u=c.moment;return{machine:this.machine,sinceHours:o,restartAt:u?new Date(u.restartAt).toISOString():null,restartKind:u?u.kind:null,bootAt:r?new Date(r).toISOString():null,daemonStartedAt:new Date(this.startedAt).toISOString(),sessions:c.sessions.map(d=>this.restoreView(d.s,d,s,c.sessions.map(f=>f.s))),ghostty:this.platform==="darwin"}}restoreTargets(e,t=this.now()){let s=[];for(let o of[...new Set((e||[]).map(String))].slice(0,50)){let r=this.sessions.get(o);if(!r)throw C(404,"not_found","no such session: "+o);if(!this.isMine(r))throw C(409,"other_machine",'"'+(r.title||o)+'" ran on '+(r.machine||"another machine")+"; restore it there");if(!r.cwd)throw C(409,"no_folder",'"'+(r.title||o)+'" has no folder on record');if(this.isRunningNow(r,t))throw C(409,"running",'"'+(r.title||o)+'" is already running (use Go)');this.inferBackend(r),s.push(this.resumeTarget(r))}return s}resumeTarget(e){if(!e||!this.isMine(e)||!e.cwd)return e;let t=vr(e);if(t!==e.cwd||e.repo&&e.repo.worktree){if(this.dirExists(t))return t===e.cwd?e:{...e,cwd:t};let s=e.repo&&e.repo.root;if(e.repo&&e.repo.worktree&&s&&s!==t&&this.dirExists(s))return{...e,cwd:s,folderGone:t}}return e}markRestored(e,t=this.now()){for(let s of e)this.restored.set(s,t);this.emit("restore.updated",{ids:e})}async restore(e,{print:t=!1}={},s=this.now()){let o=this.restoreTargets(e,s);if(!o.length)throw C(400,"nothing","pick at least one session");if(this.restoring)throw C(409,"busy","a restore is already running");this.restoring=!0;try{let r=o.map(d=>this.restoreView(d,null,s,o));if(t)return{ok:!1,how:"print",opened:0,failed:0,sessions:r,message:"the commands, to run yourself"};let i=r.filter(d=>!d.blocked),a=i.length?await bc(i.map(d=>({cwd:d.cwd,claude:d.claude})),{exec:this.restoreExec||Xt,platform:this.platform}):{ok:!1,how:"ghostty",results:[],message:Ef},l=new Map(i.map((d,f)=>[d.id,a.results[f]||null])),c=r.map(d=>{if(d.blocked)return{...d,ok:!1};let f=l.get(d.id);return{...d,ok:a.how==="ghostty"?!!(f&&f.ok):!1,error:f&&f.error?f.error:null}}),u=c.filter(d=>d.ok);return u.length&&this.markRestored(u.map(d=>d.id),s),this.log("restore: "+a.message+(a.how==="print"?" ("+o.length+" commands printed)":"")),{ok:a.ok&&i.length===r.length,how:a.how,opened:u.length,failed:c.length-u.length,sessions:c,message:i.length===r.length?a.message:a.message+(i.length?"; "+(r.length-i.length)+" need Ollama":"")}}finally{this.restoring=!1}}async resume(e,{fromPeer:t=!1}={},s=this.now()){this.assertNotAgent(e,"Resume");let o=this.sessions.get(e);if(!o)throw C(404,"not_found","no such session");if(!this.isMine(o)){if(t)throw C(409,"other_machine","That session runs on "+(o.machine||"another machine"));let c=await this.viaPeer(o.machine,"session.resume",{sessionId:o.id},"Resume");return{...c.result,peer:o.machine,peerVia:c.via}}let[r]=this.restoreTargets([e],s),i=Tf(r,{...this.cfg,tabMarker:this.needsTabMarker(r,[r])});if(!this.dirExists(r.cwd))throw C(409,"folder_gone",gw(r.cwd));if(i.ollama&&!this.findOllama())throw C(409,"no_ollama",Ef);let a=await this.restore([e],{},s),l=a.sessions[0]||{};if(a.how!=="ghostty")throw C(409,"no_ghostty","Couldn’t open Ghostty: "+a.message+". Use Copy resume.");if(!l.ok)throw C(409,"ghostty_error","Ghostty didn’t open it: "+(l.error||"no answer")+". Use Copy resume.");return{ok:!0,how:"ghostty",id:r.id,machine:this.machine,command:i.line,message:"Resumed in a new Ghostty tab",...r.folderGone?{folderGone:r.folderGone,note:gw(r.folderGone)+" Opened in "+qe.basename(r.cwd)+" instead."}:{}}}closedSessions({q:e,machine:t,repo:s,space:o,docs:r=!1,limit:i=30,offset:a=0}={},l=this.now()){let c=e?String(e).toLowerCase().trim():null,u=[];for(let m of this.sessions.values()){if(m.archived||m.ignored||!m.endedAt||xt(m,l,this.cfgFor(m))!=="ended"||t&&(m.machine||this.machine)!==t)continue;let g=this.publicSession(m,l);g.empty||o&&g.space!==o||r&&!Hm(g.files)||c&&!this.matches(g,c)||u.push(g)}u.sort((m,g)=>(R(g.endedAt)||0)-(R(m.endedAt)||0));let d=qm(u);if(s){let m=String(s).split(",").map(g=>g.trim()).filter(Boolean);u=u.filter(g=>m.some(S=>es(g,S)))}let f=Math.max(0,Number(a)||0),h=Math.max(1,Math.min(Number(i)||30,200));return{sessions:u.slice(f,f+h).map(m=>{let g=this.sessions.get(m.id);return g&&this.inferBackend(g)?this.publicSession(g,l):m}),total:u.length,more:u.length>f+h,repos:d,docs:!!r}}closedCount({machine:e}={},t=this.now()){let s=0;for(let o of this.sessions.values())o.archived||o.ignored||!o.endedAt||e&&(o.machine||this.machine)!==e||xt(o,t,this.cfgFor(o))==="ended"&&(pt(o,(this.checkpoints.get(o.id)||[]).length)||s++);return s}async checkLiveness(e=this.now()){if(this.checkingLive)return 0;let t=[...this.sessions.values()].filter(o=>this.isMine(o)&&o.pid&&!o.endedAt);if(!t.length)return 0;this.checkingLive=!0;let s=0;try{let o=await this.liveChecker(t,{now:e});for(let r of t){let i=o.get(r.id);if(i){if(i.alive){r.pidAliveAt=e,this.refreshStatus(r,e)&&this.emit("session.updated",this.publicSession(r,e));continue}r.endedAt||(r.endedAt=G(e),r.closedAt=r.endedAt,r.endReason="closed",r.inTurn=!1,r.pidAliveAt=null,this.log("session "+r.id.slice(0,8)+" closed (Claude Code pid "+r.pid+" "+(i.reason==="pid_reused"?"was reused":"is gone")+")"),this.refreshStatus(r,e),this.persist(r,!0),this.emit("session.updated",this.publicSession(r,e)),this.onEnded(r),s++)}}}finally{this.checkingLive=!1}return s}liveInfo(e,t,s=this.now()){return e.endedAt?{live:"closed",via:e.closedAt?"pid":"event",since:e.endedAt}:t==="stale"?{live:"closed",via:"heartbeat",since:e.lastHeartbeatAt||e.lastEventAt||null}:this.isMine(e)&&e.pid?{live:"open",via:"pid",since:e.pidStartedAt||e.startedAt||null}:e.capture==="mod"?{live:"open",via:"heartbeat",since:e.startedAt||null}:{live:"unknown",via:"hooks",since:null}}jumpInfo(e,t){if(!this.isMine(e)){if(!this.peers)return{supported:!1,reason:"runs on "+(e.machine||"another machine")};if(t==="closed")return{supported:!1,reason:"closed"};let o=this.peerUsable(e.machine);return o.ok?{supported:!0,reason:null,peer:e.machine,via:o.via}:{supported:!1,reason:"runs on "+(e.machine||"another machine")+(o.reason?": "+o.reason:""),peer:e.machine,code:o.code||null}}if(t!=="open"&&t!=="unknown")return{supported:!1,reason:"closed"};if(!e.term)return{supported:!1,reason:"no terminal recorded (sessions started before v0.4, or hooks without the mod)"};let s=Zo(e);return{supported:s.supported,reason:s.reason||null}}async jump(e){this.assertNotAgent(e,"Go");let t=this.sessions.get(e);if(!t)throw C(404,"not_found","no such session");if(!this.isMine(t)){let a=await this.viaPeer(t.machine,"session.go",{sessionId:t.id},"Go");return{...a.result,peer:t.machine,peerVia:a.via,label:(a.result&&a.result.label||"its terminal")+" on "+t.machine}}let s=this.publicSession(t);if(s.live==="closed")throw C(409,"closed","That session is closed"+(s.resumeCommand?". Resume it with: "+s.resumeCommand:""));if(!t.term)return{ok:!1,code:"unsupported",message:"No terminal was recorded for this session (it started before v0.4, or without the mod).",steps:[],elapsedMs:0};if(this.jumping)return this.jumping.id===t.id?this.jumping.p:{ok:!1,code:"busy",message:"Still switching to another session; try again in a moment.",steps:[],elapsedMs:0};this.lastGoAt=Date.now();let o=Zo({...t,ghosttyTerminalId:this.ghostty.get(t.id)}),r={...this.jumpExec?{exec:this.jumpExec}:{}};o.kind==="ghostty"&&(r.ghostty={caps:this.ghostty.getCaps({resourcesDir:t.term.resourcesDir,version:t.term.version}),activate:this.cfg.ghostty&&this.cfg.ghostty.activate||"auto",onStale:()=>this.ghostty.forget(t.id),onFound:(a,l,c)=>{this.ghostty.set(t.id,a,l),this.ghostty.applyListing(this.ghosttyOpenSessions(),c,"go")}});let i=(async()=>{let a=await this.jumpRunner(o,r);return o.kind==="ghostty"&&this.noteGhosttyAutomation(a),this.log("go "+t.id.slice(0,8)+" ("+o.kind+"): "+(a.ok?"ok"+(a.how?" by "+a.how:""):a.code+" "+a.message)+(a.elapsedMs!=null?" in "+a.elapsedMs+" ms":"")+(a.steps&&a.steps.length?" ["+tc(a.steps)+"]":"")),{...a,kind:o.kind,label:o.label}})();this.jumping={id:t.id,p:i};try{return await i}finally{this.jumping=null}}ghosttyOpenSessions(){return[...this.sessions.values()].filter(e=>this.isMine(e)&&!e.endedAt&&e.term&&e.term.kind==="ghostty")}ghosttyAutoOk(){if((process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform)!=="darwin")return!1;let e=this.readState("ghostty-automation");return!!(e&&e.ok)}noteGhosttyAutomation(e){let t=this.readState("ghostty-automation"),s=null;e&&(e.ok||["not_found","ambiguous"].includes(e.code))?s={ok:!0}:e&&e.code==="automation_denied"&&(s={ok:!1,code:e.code}),s&&(!t||t.ok!==s.ok)&&this.writeState("ghostty-automation",{...s,at:new Date().toISOString()})}scheduleGhosttyRefresh(e,t=1500){return this.stopped||this.ghosttyTimer||!this.ghosttyAutoOk()||!this.ghosttyOpenSessions().length?!1:(this.ghosttyTimer=this.later(t,()=>{this.ghosttyTimer=null,this.refreshGhostty(e).catch(s=>this.log("ghostty cache error: "+s.message))}),!0)}async refreshGhostty(e="manual"){let t=this.ghosttyOpenSessions();if(!t.length)return null;let s=await this.ghostty.refresh(t,{reason:e});return s&&s.error&&/-1743|not authori[sz]ed/i.test(s.stderr||"")&&this.noteGhosttyAutomation({code:"automation_denied"}),s}boardOpened(){this.boardClients++,this.scheduleGhosttyRefresh("board opened",300)}boardClosed(){this.boardClients=Math.max(0,this.boardClients-1)}async jumpSpeed(){let e=this.ghosttyOpenSessions()[0],t=this.ghostty.getCaps(e?{resourcesDir:e.term.resourcesDir,version:e.term.version}:{}),s=await ec({exec:this.jumpExec||Xt,caps:t});return s.ok&&this.noteGhosttyAutomation({ok:!0}),{...s,cache:{sessions:this.ghosttyOpenSessions().length,remembered:this.ghostty.map.size,last:this.ghostty.last},pid:process.pid,launchd:process.env.XPC_SERVICE_NAME&&process.env.XPC_SERVICE_NAME!=="0"?process.env.XPC_SERVICE_NAME:null}}async jumpCheck(e){let t=await rc(e,this.jumpExec?{exec:this.jumpExec}:{});return e==="ghostty"&&t.ok&&this.noteGhosttyAutomation({ok:!0}),t}where(e){this.assertNotAgent(e,"Go");let t=this.sessions.get(e);if(!t)throw C(404,"not_found","no such session");return{id:t.id,title:t.title||null,isLocal:this.isMine(t),machine:t.machine||this.machine,cwd:t.cwd||null,tty:t.tty||null,pid:t.pid||null,term:t.term||null,ghosttyTerminalId:this.ghostty.get(t.id)}}worthPersisting(e){let t=(this.checkpoints.get(e.id)||[]).length;return!!(e.title||e.turns>0||t||e.folder||e.historyTurns>0||e.endedAt&&!pt(e,t))}persist(e,t=!1,s=!1){if(s){let r=this.lastHbPersist.get(e.id)||0;if(Date.now()-r<6e4)return;this.lastHbPersist.set(e.id,Date.now())}if(t)return this.persistNow(e);if(this.persistTimers.has(e.id))return;let o=setTimeout(()=>{this.persistTimers.delete(e.id),this.persistNow(e)},300);o.unref&&o.unref(),this.persistTimers.set(e.id,o)}persistNow(e){if(!this.isMine(e))return;if(!this.worthPersisting(e)){this.scheduleOpenState();return}let t=this.persistTimers.get(e.id);t&&(clearTimeout(t),this.persistTimers.delete(e.id));try{this.folder.writeSession(e,this.checkpoints.get(e.id)||[])}catch(s){this.log("write session failed: "+s.message)}}async autoTitle(e){if(!(e.title||e.titling)){e.titling=!0;try{let t=await this.summarizer.title(e);!e.title&&t.title&&(e.title=t.title,e.titleSource=t.titleSource,this.persist(e,!0),this.emit("session.updated",this.publicSession(e)))}finally{e.titling=!1}}}setTitle(e,t,s="custom"){let o=this.sessions.get(e);if(!o)throw C(404,"not_found","no such session");return o.title=F(t,120),o.titleSource=s,this.persist(o,!0),this.emit("session.updated",this.publicSession(o)),o}patchSession(e,t){this.assertNotAgent(e,"renaming, pinning and archiving");let s=this.sessions.get(e);if(!s)throw C(404,"not_found","no such session");if(this.assertMine(s),t.title!=null){if(typeof t.title!="string"||!t.title.trim())throw C(400,"bad_title","title must be a non-empty string");s.title=F(t.title,120),s.titleSource="custom",s.term&&s.term.kind==="ghostty"&&this.scheduleGhosttyRefresh("renamed",5e3)}return t.pinned!=null&&(s.pinned=!!t.pinned),t.archived!=null&&(s.archived=!!t.archived),this.persist(s,!0),this.emit("session.updated",this.publicSession(s)),this.publicSession(s)}assertMine(e){if(!this.isMine(e))throw C(409,"other_machine",'this session belongs to machine "'+e.machine+'"; change it from that machine')}addCheckpoint(e,t,{by:s}={}){this.assertNotAgent(e,"checkpointing");let o=this.sessions.get(e);o&&this.assertMine(o),o||(o={id:e,machine:this.machine,turns:0,turnsSinceCheckpoint:0,backgroundTasks:0,capture:"mod",startedAt:t.createdAt||G(this.now()),statusChanges:[]},this.sessions.set(e,o),this.checkpoints.set(e,[]));let r=t.trigger||"manual";if(!yu.includes(r))throw C(400,"bad_trigger","trigger must be one of "+yu.join(", "));for(let u of["doing","stoppedAt","next"])if(typeof t[u]!="string")throw C(400,"bad_checkpoint",u+" is required");let i=this.checkpoints.get(e);if(t.id&&i.some(u=>u.id===t.id))return i.find(u=>u.id===t.id);let a=G(t.createdAt||this.now()),l={id:t.id||fa(e,a),sessionId:e,machine:o.machine||this.machine,createdAt:a,trigger:r,sessionTitle:o.title||null,repo:o.repo?ge(o):null,branch:o.repo&&o.repo.branch||null,cwd:o.cwd||null,doing:t.doing,stoppedAt:t.stoppedAt,next:t.next,openQuestion:t.openQuestion||(/^YOU:/i.test(t.next)?t.next.replace(/^YOU:\s*/i,""):null),userNote:t.userNote||null,filesTouched:Array.isArray(t.filesTouched)?t.filesTouched.map(String).slice(0,50):[],turnsSinceLast:o.turnsSinceCheckpoint||0,parseOk:t.parseOk!==!1,generator:t.generator||{by:s||"mod"},...mw(t)},c=this.folder.writeCheckpoint(o,l);return i.push(c),o.lastCheckpointId=c.id,o.lastCheckpointAt=c.createdAt,o.turnsSinceCheckpoint=0,o.dueSince=null,(r==="dormant"||r==="long_run")&&(o.dormantStretch=o.lastActivityAt),(r==="session_end"||r==="clear")&&(o.finalCheckpointDone=!0),(o.endedAt||o.status==="stale")&&(o.finalCheckpointDone=!0),this.persist(o,!0),this.emit("checkpoint.created",c),this.emit("session.updated",this.publicSession(o)),c}async generateCheckpoint(e,t,{userNote:s,length:o,save:r=!0}={}){this.assertNotAgent(e,"checkpointing");let i=this.sessions.get(e);if(!i)throw C(404,"not_found","no such session");if(this.assertMine(i),this.generating.has(e))throw C(409,"busy","a checkpoint is already being generated for this session");this.generating.add(e);try{let{messages:a,meta:l}=i.transcriptPath?on(i.transcriptPath):{messages:[],meta:{}};l&&l.gitBranch&&!(i.repo&&i.repo.branch)&&(i.repo={...i.repo||{},branch:l.gitBranch}),l&&l.model&&this.noteTranscriptModel(i,l.model);let c=this.checkpoints.get(e)||[],u=c[c.length-1]||null,d=ga({session:i,previous:u,messages:a,cfg:this.cfg,userNote:s,length:o}),f;i.ignored?f={doing:"(repo opted out of summaries)",stoppedAt:i.status||"",next:"Open the session to see where it is.",parseOk:!0,generator:{by:"ignored"}}:f=await this.summarizer.checkpoint({system:d.system,prompt:d.prompt,messages:a,session:i,maxTokens:d.maxTokens,length:d.length});let h={trigger:t,doing:f.doing,stoppedAt:f.stoppedAt,next:f.next,openQuestion:f.openQuestion,userNote:s,filesTouched:d.filesTouched,parseOk:f.parseOk,generator:f.generator,length:d.length,decisions:f.decisions,questions:f.questions,files:f.files,didntWork:f.didntWork,recap:f.recap,nextSteps:f.nextSteps};return r?this.addCheckpoint(e,h,{by:"daemon"}):{...h,...mw(h),sessionId:e,preview:!0,sessionTitle:i.title||null,createdAt:G(this.now())}}finally{this.generating.delete(e)}}async tick(){if(this.stopped)return;let e=this.now();this.folder.spoolCount()&&this.drainSpool(),e-this.lastRemoteScan>(Number(process.env.CHECKPOINTS_REMOTE_SCAN_MS)||3e4)&&this.refreshRemote();for(let t of this.sessions.values()){if(this.refreshStatus(t,e)&&(this.persist(t),this.emit("session.updated",this.publicSession(t))),!this.isMine(t)||this.generating.has(t.id)||t.endSettleUntil&&e<t.endSettleUntil)continue;let s=xu(t,e,this.cfg);if(!s){t.dueSince=null;continue}if(!(t.capture==="mod"&&!["ended","stale"].includes(t.status)&&(t.dueSince||(t.dueSince=e),e-t.dueSince<xx)))try{await this.generateCheckpoint(t.id,s)}catch(r){this.log("checkpoint failed for "+t.id+": "+r.message)}}this.scanPrLinksTick(e),this.scanHandoffsTick(e),await this.maybeScheduledDigest()}async makeDigest({date:e,by:t="manual"}={}){for(let r of this.sessions.values())this.persistNow(r);let s=this.now(),o;try{o=await Ul(this.folder.loadAll(),{now:s,cfg:this.cfg,machine:this.machine,date:e&&e!=="today"?e:this.digestDate(s),summarizer:this.summarizer,todos:this.todosOverview(s).open.map(r=>({text:r.text,at:r.at,sessionTitle:r.session&&r.session.title})),lists:this.listIndex({fresh:!0}).concat(this.agentListIndex()),agents:this.agents.view({fresh:!0}),spaces:this.spaces.map({fresh:!0})}),this.folder.writeDigest(o.meta.date,o.markdown)}catch(r){throw this.digestState={...this.digestState||{},lastError:{at:G(s),by:t,message:String(r&&r.message||r)}},this.log("digest failed ("+t+"): "+(r&&r.stack||r)),r}return this.digestState={...this.digestState||{},lastMadeAt:G(s),lastBy:t,lastSessions:o.meta.sessions,lastError:null},this.log("digest made ("+t+") for "+o.meta.date+": digests/"+o.meta.date+".md"),this.emit("digest.created",o.meta),o}standup({space:e=null,date:t=null}={}){for(let f of this.sessions.values())this.persistNow(f);let s=this.now(),o=this.digestSchedule(),r=this.digestDate(s),i=t&&t!=="today"?t:r;if(!/^\d{4}-\d{2}-\d{2}$/.test(i))throw C(400,"bad_date","date is YYYY-MM-DD");let a=s;if(i!==r){let f=this.folder.readDigest(i),h=f?Ye(f.markdown).data||{}:{},[p,m,g]=i.split("-").map(Number),[S,k]=String(o.time||"07:00").split(":").map(Number);a=Date.parse(h.generated_at)||Jt(p,m,g,S,k,o.tz)}let l=this.spaces.map({fresh:!0}),c=null;if(e&&e!=="all")try{c=rn(l,this.knownMachines()).find(f=>f.toLowerCase()===String(e).toLowerCase())||zr(e)}catch(f){throw C(400,"bad_space",f.message)}let u=null;try{u=this.agents.view({fresh:!0})}catch{}return{...Bl(this.folder.loadAll(),{now:a,date:i,space:c,spaces:l,cfg:this.cfg,tz:o.tz,digestTime:o.time||"07:00",agents:u}),date:i}}knownMachines(){let e=new Set(this.folder.machines());try{for(let t of this.folder.aliveMachines())e.add(t.machine)}catch{}try{for(let t of Be.readdirSync(qe.join(this.root,"machines")))if(t.endsWith(".json")&&!t.startsWith("."))try{let s=JSON.parse(Be.readFileSync(qe.join(this.root,"machines",t),"utf8"));s&&s.machine&&e.add(s.machine)}catch{}}catch{}for(let t of Object.keys(Ut(this.spaces.map(),"machines")))e.add(t);return[...e].filter(Boolean).sort()}spacesView(){let e=this.spaces.map({fresh:!0}),t=this.knownMachines(),s=a=>Object.entries(e[a]||{}).filter(([,l])=>l.space!=null).map(([l,c])=>({key:l,space:c.space,at:c.at,by:c.by||null})).sort((l,c)=>l.key.localeCompare(c.key)),o=Vp([...this.sessions.values()].filter(a=>!a.archived).map(a=>{let l=this.repoKeyFor(a);return{key:l==="no-repo"?"":l,name:this.repoLabels().get(l)||ge(a),at:a.lastActivityAt||a.startedAt}}),{home:hw.homedir()}),r=o.map(a=>a.name),i=[];try{i=this.agentView().agents.map(a=>({name:a.name,display:a.display,puller:a.puller||null,space:Xn(e,{id:"agent:"+a.name+":x",agent:{name:a.name,puller:a.puller}}).space}))}catch{}return{machine:this.machine,quickPicks:To,spaces:_o([...rn(e,t),...To]),inUse:rn(e,t),machines:t.map(a=>{let l=e.machines[a];return{machine:a,space:Vn(e,a),set:!!(l&&l.space!=null),at:l&&l.space!=null?l.at:null,by:l&&l.space!=null?l.by:null,here:a===this.machine}}),repos:s("repos"),sessions:s("sessions"),agentOverrides:s("agents"),agents:i,knownRepos:r,repoSuggestions:o,file:"spaces.json",conflicts:this.spaces.cache&&this.spaces.cache.conflicts.length||0}}setSpace(e={}){let t;try{t=Kr(e.kind)}catch(r){throw C(400,"bad_kind",r.message)}let s=String(e.key==null?"":e.key).trim();if(!s)throw C(400,"bad_key","which "+t.replace(/s$/,"")+"?");let o=e.space;if((o===""||o===void 0)&&(o=null),o!=null)try{o=zr(o)}catch(r){throw C(400,"bad_space",r.message)}try{this.spaces.update((r,i)=>{r[t][s]={space:o==null?null:rn(r,this.knownMachines()).concat(To).find(a=>a.toLowerCase()===o.toLowerCase())||o,at:i.at,by:i.by}})}catch(r){throw C(400,r.code||"bad_space",r.message)}return this.emit("spaces.updated",{by:this.machine}),this.spacesView()}listIndex({fresh:e=!1}={}){let t=Date.now();if(!e&&this.listCache&&t-this.listCache.at<2e3)return this.listCache.lists;let s=[];try{s=this.lists.all({includeArchived:!0}).map(o=>wt(o))}catch(o){this.log("lists: "+(o&&o.message||o))}return this.listCache={at:t,lists:s},s}listSignature(e){let t=this.listIndex().filter(s=>s.sourceSession===e&&!s.archived);return t.length?t.map(s=>s.ref+":"+s.total+":"+s.items.filter(o=>o.done).map(o=>o.n).join(".")).sort().join("|"):null}listsChanged(e,t){this.listCache=null,this.emit("lists.updated",{ref:e&&e.ref||null,what:t})}sessionListInfo(e){let t=this.listIndex().filter(r=>r.sourceSession===e.id&&!r.archived),s=this.listHints.get(e.id),o=s&&!s.dismissed&&!s.savedRef?{count:s.count,name:s.name,kind:s.kind,at:s.at,fp:s.fp}:null;return{lists:t.map(r=>({ref:r.ref,name:r.name,done:r.done,total:r.total,progress:r.progress})),listHint:o}}saveListHints(){let e={},t=[...this.listHints.entries()].sort((s,o)=>(R(o[1].at)||0)-(R(s[1].at)||0)).slice(0,300);for(let[s,o]of t)e[s]=o;this.writeState("list-hints",e)}noteListHint(e,t,s){let o=(Array.isArray(t.items)?t.items:[]).slice(0,80).map(l=>({text:F(String(l&&l.text||l||""),500),done:!!(l&&l.done)})).filter(l=>l.text);if(o.length<3)return;let i=this.listIndex().filter(l=>l.sourceSession===e.id).find(l=>wa(o,l.items)>=.6),a=this.listHints.get(e.id);a&&a.fp&&a.fp===t.fp&&(a.dismissed||a.savedRef)||(this.listHints.set(e.id,{count:Number(t.count)||o.length,name:F(String(t.name||Er({heading:t.heading},{fallback:e.title})),80),kind:t.kind||null,items:o,at:s,fp:t.fp||null,...i?{savedRef:i.ref}:{}}),this.saveListHints())}dismissListHint(e){let t=this.listHints.get(e);t&&(t.dismissed=!0,this.saveListHints());let s=this.sessions.get(e);return s&&this.emit("session.updated",this.publicSession(s)),{ok:!0}}findListsInSession(e){this.assertNotAgent(e,"saving lists");let t=this.sessions.get(e);if(!t)throw C(404,"not_found","no such session");let s=(t.transcriptPath&&Be.existsSync(t.transcriptPath)?t.transcriptPath:null)||Ao(e,this.transcriptDirs?{dirs:this.transcriptDirs}:{});if(!s)throw this.isMine(t)?C(404,"no_transcript","Claude Code's transcript for this session isn't on this machine (it looked in ~/.claude/projects). Type /cp-todo in the session instead."):C(409,"other_machine","This session ran on "+t.machine+", and its transcript is on that machine. Open Leftoff there (or type /cp-todo in the session) to find its lists.");let{messages:o}=on(s,{maxBytes:32*1024*1024}),r=ya(o,{minItems:2,fallback:t.title}),i=this.listIndex().filter(a=>a.sourceSession===e);return{transcript:s,messages:o.length,candidates:r.reverse().map(a=>{let l=i.find(c=>wa(a.items,c.items)>=.6);return{n:a.n,name:a.name,kind:a.kind,heading:a.heading,count:a.items.length,items:a.items,message:a.message,savedAs:l?{ref:l.ref,name:l.name}:null}})}}assertMayEditLists(e={}){if(e.via==="remote"&&!(this.cfg.remote&&this.cfg.remote.allowAnswers))throw C(403,"lists_remote_off","Changing lists from another device is off. On a Mac, turn on Settings → Privacy & security → Trust my paired devices.")}listError(e){if(e&&e.status)return e;let t=e&&e.code;return t==="no_list"?C(404,"no_list",e.message):t==="no_item"?C(404,"no_item",e.message):t==="ambiguous_list"?C(409,"ambiguous_list",e.message):t==="list_busy"?C(503,"list_busy",e.message):t&&/^empty_/.test(t)?C(400,t,e.message):e}saveList(e={},t={}){this.assertMayEditLists(t);let s=e.sessionId?this.sessions.get(e.sessionId):null;if(e.sessionId&&!s&&!Array.isArray(e.items))throw C(404,"not_found","no such session");let o=Array.isArray(e.items)?e.items:null,r=e.name?String(e.name):null,i=null;if(!o&&e.fromHint&&s){if(i=this.listHints.get(s.id),!i)throw C(404,"no_hint","Claude hasn't written a list in this session lately; use Find lists in this session");o=i.items,r=r||i.name}if(!o&&e.fromTranscript!=null&&s){let u=this.findListsInSession(s.id).candidates.find(d=>d.n===Number(e.fromTranscript));if(!u)throw C(404,"no_candidate","no list "+e.fromTranscript+" in that session's transcript");o=u.items,r=r||u.name}if(!o||!o.length)throw C(400,"empty_list","a list needs items (or fromHint / fromTranscript with a sessionId)");let a=s?ge(s):e.repo||null,l=s?this.repoKeyFor(s):e.repo||null,c;try{c=this.lists.create({name:r||Er({heading:null},{fallback:s&&s.title}),items:o.map(u=>typeof u=="string"?{text:u}:u),repo:a&&a!=="no-repo"?a:null,repoKey:l,sourceSession:s?s.id:e.sessionId||null,sourceMachine:s?s.machine||this.machine:null,sourceTitle:s&&s.title||null})}catch(u){throw this.listError(u)}if(s){let u=this.listHints.get(s.id);u&&(i||wa(o,u.items)>=.6)&&(u.savedRef=c.ref,this.saveListHints())}return this.log("list saved: "+c.ref+" ("+c.items.length+" items"+(s?", from session "+We(s.id):"")+")"),this.listsChanged(c,"created"),s&&this.emit("session.updated",this.publicSession(s)),{ok:!0,list:wt(c),context:Od([wt(c)])}}getList(e){if(String(e||"").startsWith("~")){let t=this.agentListIndex().find(s=>s.ref===e);if(!t)throw C(404,"not_found","no such list");return t}try{return wt(this.lists.resolve(e))}catch(t){throw this.listError(t)}}changeList(e,t={},s={}){if(String(e||"").startsWith("~"))throw C(409,"read_only","this list comes from an external agent’s feed and is read-only here; tell the agent instead");this.assertMayEditLists(s);let o,r={};try{if(o=this.lists.resolve(e),t.text&&!t.done&&!t.undone){let a=this.lists.findItem(o,t.text);if(!a)throw C(404,"no_item",'no item on "'+o.meta.name+'" matches "'+t.text+'"; give its number');t={...t,[t.undo?"undone":"done"]:[a.id]}}if(t.done||t.undone){let a=this.lists.setDone(o,[].concat(t.done||t.undone).map(Number),!!t.done);o=a.list,r.missing=a.missing}if(t.add){let a=this.lists.addItem(o,t.add);o=a.list,r.added=a.item.id}t.edit&&(o=this.lists.editItem(o,t.edit.n,t.edit)),t.remove!=null&&(o=this.lists.removeItem(o,t.remove)),t.name&&(o=this.lists.rename(o,t.name)),t.archived!=null&&(o=this.lists.archive(o,!!t.archived))}catch(a){throw this.listError(a)}this.listsChanged(o,"changed");let i=o.meta.sourceSession&&this.sessions.get(o.meta.sourceSession);return i&&this.emit("session.updated",this.publicSession(i)),{ok:!0,list:wt(o),...r}}agentListIndex(){try{return jl(this.agentView())}catch(e){return this.log("agents: "+e.message),[]}}listsOverview({all:e=!1,repo:t=null}={}){let s=this.listIndex({fresh:!0}).concat(this.agentListIndex());e||(s=s.filter(r=>!r.archived)),t&&(s=s.filter(r=>r.repoDir===t||r.repo===t||r.repoKey===t));let o=new Map;for(let r of s){let i=r.repo||"no repo";o.has(i)||o.set(i,{repo:i,repoDir:r.repoDir,lists:[]}),o.get(i).lists.push(r)}for(let r of o.values())r.lists.sort((i,a)=>(i.open===0)-(a.open===0)||(R(a.updated)||0)-(R(i.updated)||0));return{lists:s,repos:[...o.values()].sort((r,i)=>r.repo.localeCompare(i.repo)),archived:this.listIndex().filter(r=>r.archived).length}}sessionLists(e){if(et(e))return{lists:this.agentListIndex().filter(i=>i.sourceSession===e),hint:null,context:null,signature:null,readOnly:!0};if(!this.sessions.get(e))throw C(404,"not_found","no such session");let s=this.listIndex({fresh:!0}).filter(r=>r.sourceSession===e),o=this.listHints.get(e);return{lists:s,hint:o&&!o.dismissed&&!o.savedRef?o:null,context:Od(s),signature:this.listSignature(e)}}catchupEntries(){for(let t of this.sessions.values())this.persistNow(t);let e=[];try{e=Fl(this.agents.view({fresh:!0}))}catch(t){this.log("agents: "+t.message)}return this.folder.loadAll().concat(e)}catchupNeeds(e,t){let s=this.sessions.get(e.id)||e,o=this.needsItem(s,t);if(!o)return null;let r="";return o.kind==="permission"&&o.ask&&o.ask.describe?r="needs your OK: "+o.ask.describe.verb+" "+o.ask.describe.detail:o.kind==="question"&&o.ask?r="asks: "+(((o.ask.questions||[])[0]||{}).question||"a question"):o.kind==="terminal"?r=o.message||"waiting in the terminal":o.kind==="reply"&&(r="asked you: "+(typeof o.question=="string"?o.question:"a question")),{kind:o.kind,message:r}}catchupArgs(e={}){let t=s=>(Array.isArray(s)?s:s==null||s===""?[]:String(s).split(",")).map(o=>String(o).trim()).filter(Boolean);return{since:e.since==null||e.since===""?void 0:String(e.since),to:e.to?String(e.to):null,repos:t(e.repos),machines:t(e.machines),length:e.length||void 0}}catchupOptions(e={}){let t=this.catchupArgs(e);return Ax(()=>ml(this.catchupEntries(),{...t,now:this.now(),cfg:this.cfg,machine:this.machine,lastAt:this.folder.lastCatchupAt(),repoKeyFor:s=>this.repoKeyFor(s),needsOf:(s,o)=>this.catchupNeeds(s,o)}))}async makeCatchup(e={}){let t=this.catchupArgs(e),s=e.summary&&Array.isArray(e.summary.bullets)?e.summary:null,o=await Tx(()=>li(this.catchupEntries(),{...t,now:this.now(),cfg:this.cfg,machine:this.machine,lastAt:this.folder.lastCatchupAt(),summarizer:e.useModel===!1?null:this.summarizer,given:s,repoKeyFor:i=>this.repoKeyFor(i),needsOf:(i,a)=>this.catchupNeeds(i,a),lists:this.listIndex({fresh:!0}).concat(this.agentListIndex())})),r=null;return e.save!==!1&&(r=this.folder.writeCatchup(o.fileName,o.markdown),this.log("catch-up "+r.name+": "+o.recap.sessions.length+" session(s), "+o.recap.window.label.toLowerCase()+", summary by "+(o.summary.by.by==="model"?o.summary.by.provider:"heuristic")),this.emit("catchup.created",{name:r.name,sessions:o.recap.sessions.length})),{name:r?r.name:null,path:r?"catchups/"+r.name+".md":null,saved:!!r,recap:o.recap,summary:o.summary,markdown:o.markdown}}readCatchup(e){let t=this.folder.readCatchup(e);return t?{name:t.name,markdown:t.markdown,parsed:_d(t.markdown)}:null}digestSchedule(){return ll(this.cfg.digest,Yt())}digestTime(){return this.digestSchedule().time}digestDate(e=this.now()){return Po(e,this.digestSchedule()).date}digestGraceMs(){let e=Number(this.cfg.digest&&this.cfg.digest.graceMin);return(Number.isFinite(e)&&e>=0?e:20)*6e4}digestMachines(e=this.now()){let s=[];try{s=this.folder.aliveMachines().filter(o=>e-(Date.parse(o.seenAt)||0)<6048e5&&o.digest!==!1).map(o=>o.machine)}catch{}return[...new Set([...s,this.machine])].sort()}digestTurnNow(e=this.now()){let t=this.digestSchedule(),s=Po(e,t);return{slot:s,...eg({machines:this.digestMachines(e),me:this.machine,slotAt:s.at,graceMs:this.digestGraceMs()})}}sharedDigest(e){return this.folder.readDigest(e,"all")}nextDigestAt(e=this.now()){let t=this.digestSchedule(),s=this.digestTurnNow(e),o=s.slot,r=!!this.sharedDigest(o.date),i=this.digestDue(e);if(!r&&o.isDay&&s.at!=null&&e<s.at)return G(s.at);let a=Xm(e,t,{doneToday:r&&!i||o.isDay&&o.past&&!i});return a==null?null:G(a>e+1e3&&a!==o.at?a+s.rank*this.digestGraceMs():a)}hasRecentSessions(e=this.now()){let t=e-(this.cfg.digest&&this.cfg.digest.lookbackHours||24)*36e5;for(let s of this.sessions.values())if(!s.archived&&(this.worthPersisting(s)||!this.isMine(s))&&Math.max(R(s.lastActivityAt)||0,R(s.startedAt)||0)>=t)return!0;return!1}digestDue(e=this.now()){let t=this.digestSchedule();if(!t.time)return!1;let s=this.digestTurnNow(e),o=s.slot;if(!o.isDay||!o.past)return!1;let r=this.sharedDigest(o.date);if(!r)return e<s.at||e>=o.at+this.digestGraceMs()&&e<this.awakeSince+this.digestSettleMs*(s.rank+1)&&(s.order.length>1||this.knownMachines().length>1)||t.skipEmpty&&!this.hasRecentSessions(e)?!1:"missing";let i=Ye(r.markdown).data||{};return(!i.machine||i.machine===this.machine||!s.order.includes(i.machine))&&Number(i.sessions)===0&&e-(R(i.generated_at)||0)>=5*6e4&&this.hasRecentSessions(e)?"empty":!1}async maybeScheduledDigest(){let e=this.now();if(this.lastDigestCheck&&e-this.lastDigestCheck>12e4&&(this.awakeSince=e),this.lastDigestCheck=e,!this.digestTime()||this.digesting)return;if(!this.aliveAt||e-this.aliveAt>30*6e4)try{this.folder.stampAlive({version:X,digest:!0},e),this.aliveAt=e}catch(o){this.log("digest: could not stamp digests/.machines: "+o.message)}let t=this.digestState&&this.digestState.lastError;if(t&&t.by==="schedule"&&e-R(t.at)<10*6e4)return;let s=this.digestDue(e);if(s){this.digesting=!0;try{await this.makeDigest({by:"schedule"}),s==="empty"&&this.log("digest remade: the morning one had no sessions")}catch{}finally{this.digesting=!1}}}digestStatus(e=this.now()){let t=this.digestState||{},s=this.digestSchedule(),o=this.digestTurnNow(e),r=o.slot,i=this.sharedDigest(r.date),a=i?Ye(i.markdown).data||{}:null,l=null;return!i&&s.time&&(r.isDay?r.past&&s.skipEmpty&&!this.hasRecentSessions(e)?l="no activity":r.past&&o.at!=null&&e<o.at&&(l="waiting for "+o.order[0]):l="not a digest day"),{time:s.time,timeZone:s.tz,zoneOverride:s.zoneOverride,daemonZone:Yt(),zoneAbbr:gd(e,s.tz),label:Zm(s.time,s.tz,e),days:s.days,daysLabel:Qm(s.days),skipEmpty:s.skipEmpty,nextAt:this.nextDigestAt(e),todayDate:r.date,todayDay:r.day,todayNote:l,today:a?{date:r.date,generatedAt:a.generated_at||null,sessions:a.sessions!=null?Number(a.sessions):null,by:a.machine||null,duplicates:i.duplicates||0}:null,shared:!0,order:o.order,rank:o.rank,first:o.first,graceMin:this.digestGraceMs()/6e4,lastMadeAt:t.lastMadeAt||null,lastBy:t.lastBy||null,lastError:t.lastError||null}}scheduleDigest(){this.later(1e3,()=>this.maybeScheduledDigest().catch(()=>{}))}repoKeyFor(e){let t=this.workRepoOf(e);return t?t.root:e.repo&&e.repo.root?this.isMine(e)?this.mainRootCached(e.repo.root):e.repo.root:e.cwd?this.isMine(e)&&this.mainRootCached(e.cwd,!0)||e.cwd:e.repo&&e.repo.name||"no-repo"}mainRootCached(e,t=!1){return this.gitRootCache||(this.gitRootCache=new Map),this.gitRootCache.has(e)||(this.gitRootCache.size>2e3&&this.gitRootCache.clear(),this.gitRootCache.set(e,Xg(e)||Qg(e))),this.gitRootCache.get(e)||(t?null:e)}workRepoOf(e){let t=Of(e.editedRepos);if(!t.length)return null;let s=e.repo&&e.repo.root;return s&&t.some(o=>o.root===s)?null:t[0]}alsoRepos(e,t){let s=[],o=r=>{r&&r!==t&&!s.some(i=>i.key===r)&&s.push({key:r,label:this.repoLabels().get(r)||qe.basename(r)})};e.repo&&e.repo.root&&o(this.isMine(e)?this.mainRootCached(e.repo.root):e.repo.root),e.startRepo&&o(e.startRepo);for(let r of Of(e.editedRepos))o(r.root);return s}repoLabels(){let e=[...new Set([...this.sessions.values()].map(s=>this.repoKeyFor(s)))].sort(),t=e.join(`
222
+ `);return(!this.repoLabelMemo||this.repoLabelMemo.sig!==t)&&(this.repoLabelMemo={sig:t,map:zt(e)}),this.repoLabelMemo.map}publicSession(e,t=this.now()){let s=this.repoKeyFor(e),o=this.workRepoOf(e),r=o?{root:o.root,name:o.name,branch:o.branch||null,...o.worktree?{worktree:o.worktree,worktreeName:o.worktreeName}:{}}:e.repo||null,i=o?o.name:ge(e),a=this.alsoRepos(e,s),l=e.startRepo&&e.startRepo!==s?e.startRepo:null,c=this.checkpoints.get(e.id)||[],u=c[c.length-1]||null,d=xt(e,t,this.cfgFor(e));return{id:e.id,title:e.title||null,titleSource:e.titleSource||null,machine:e.machine||this.machine,isLocal:this.isMine(e),cwd:e.cwd||null,repo:r,repoName:i,repoKey:s,repoLabel:this.repoLabels().get(s)||i,transcriptPath:e.transcriptPath||null,model:e.model||null,worktree:r&&r.worktree?{path:r.worktree,name:r.worktreeName||qe.basename(r.worktree),branch:r.branch||null}:null,alsoRepos:a,startedIn:l?this.repoLabels().get(l)||qe.basename(l):null,backend:e.backend||null,modelLabel:qh(e),surface:e.surface||null,capture:e.capture||null,status:d,startedAt:e.startedAt||null,lastActivityAt:e.lastActivityAt||null,lastHeartbeatAt:e.lastHeartbeatAt||null,endedAt:e.endedAt||null,endReason:e.endReason||null,turns:e.turns||0,turnsSinceCheckpoint:e.turnsSinceCheckpoint||0,lastCheckpointId:e.lastCheckpointId||null,parentSessionId:e.parentSessionId||null,pinned:!!e.pinned,archived:!!e.archived,ignored:!!e.ignored,needsInput:!!e.needsInput,lastNotification:e.lastNotification||null,lastPreview:e.lastPreview||null,idleSec:e.lastActivityAt?Math.max(0,Math.round((t-R(e.lastActivityAt))/1e3)):null,checkpointCount:c.length,latestCheckpoint:u,resumeCommand:ho(this.resumeTarget(e)),statusChanges:e.statusChanges||[],folder:e.folder||null,empty:pt(e,c.length),ghost:po(e,c.length),historyTurns:e.historyTurns||0,startSource:e.startSource||null,healedVia:e.healedVia||null,archivedReason:e.archivedReason||null,question:e.question||null,answers:(e.answers||[]).slice(-10),prs:this.sessionPrs(e),...this.handoffsPublic(e),...this.livePublic(e,d,t),needsYou:this.needsItem(e,d,t),peer:this.isMine(e)?null:{machine:e.machine,...this.peerUsable(e.machine)},...this.sessionListInfo(e),...this.spaceOf({id:e.id,machine:e.machine||this.machine,repoKey:s,repoName:ge(e),repoLabel:this.repoLabels().get(s)||ge(e)})}}spaceOf(e){try{let t=this.spaces.resolve(e);return{space:t.space,spaceSource:t.source}}catch{return{space:e.machine||this.machine,spaceSource:"default"}}}withSpace(e){return e&&Object.assign(e,this.spaceOf(e))}livePublic(e,t,s){let o=this.liveInfo(e,t,s),r=e.term||null;return{live:o.live,liveVia:o.via,liveSince:o.since,closedAt:o.live==="closed"&&(e.closedAt||e.endedAt||o.since)||null,pid:e.pid||null,tty:e.tty||null,shortId:We(e.id),terminal:r?{kind:r.kind,label:r.label||r.kind}:null,jump:this.jumpInfo(e,o.live)}}listSessions({status:e,repo:t,q:s,machine:o,limit:r=50,includeArchived:i=!1}={}){let a=e?String(e).split(",").map(u=>u.trim()).filter(Boolean):null,l=s?String(s).toLowerCase():null,c=[...this.sessions.values()].map(u=>this.publicSession(u));try{c.push(...this.agentSessions().filter(u=>!l||this.agentMatches(u,l)))}catch(u){this.log("agents: "+u.message)}if(i||(c=c.filter(u=>!u.archived)),a&&(c=c.filter(u=>a.includes(u.status))),t){let u=String(t).split(",").map(d=>d.trim()).filter(Boolean);c=c.filter(d=>u.some(f=>es(d,f)))}return o&&(c=c.filter(u=>u.machine===o)),l&&(c=c.filter(u=>this.matches(u,l))),c.sort((u,d)=>d.pinned-u.pinned||(R(d.lastActivityAt||d.startedAt)||0)-(R(u.lastActivityAt||u.startedAt)||0)),c.slice(0,Math.min(Number(r)||50,500))}agentMatches(e,t){let s=this.agentSession(e.id)||e;return[s.title,s.project,s.machine,...(s.checkpoints||[]).flatMap(r=>[r.summary,r.done,r.next]),...(s.needsYou&&s.needsYou.items||[]).map(r=>r.text)].some(r=>r&&String(r).toLowerCase().includes(t))}matches(e,t){if(e.agent)return this.agentMatches(e,t);let s=[e.title,e.repoName,e.machine,e.cwd,e.repo&&e.repo.branch,e.lastPreview];e.files&&s.push(...(e.files.docs||[]).map(o=>o.name),...(e.files.plans||[]).map(o=>o.title),...(e.files.artifacts||[]).map(o=>o.title));for(let o of this.checkpoints.get(e.id)||[])s.push(o.doing,o.stoppedAt,o.next,o.userNote,...o.filesTouched||[]);return s.some(o=>o&&String(o).toLowerCase().includes(t))}getSession(e){if(et(e)){let o=this.agentSession(e);if(!o)throw C(404,"not_found","no such session");return o}let t=this.sessions.get(e);if(!t)throw C(404,"not_found","no such session");let s=this.checkpoints.get(e)||[];return this.inferBackend(t),{...this.publicSession(t),checkpoints:s.slice(-20).reverse()}}sessionCheckpoints(e,{before:t,limit:s=50}={}){if(et(e)){let r=this.agentSession(e);if(!r)throw C(404,"not_found","no such session");return r.checkpoints.filter(i=>!t||R(i.createdAt)<R(t)).slice(0,Math.min(Number(s)||50,500))}if(!this.sessions.has(e))throw C(404,"not_found","no such session");let o=(this.checkpoints.get(e)||[]).slice().reverse();return t&&(o=o.filter(r=>R(r.createdAt)<R(t))),o.slice(0,Math.min(Number(s)||50,500))}timeline({since:e,q:t,session:s,limit:o=100}={}){let r=[];if(s)r.push(...this.checkpoints.get(s)||[]);else for(let i of this.checkpoints.values())r.push(...i);if(e&&(r=r.filter(i=>R(i.createdAt)>=R(e))),t){let i=String(t).toLowerCase();r=r.filter(a=>[a.sessionTitle,a.doing,a.stoppedAt,a.next,a.repo,a.userNote,...a.filesTouched||[]].some(l=>l&&String(l).toLowerCase().includes(i)))}return r.sort((i,a)=>R(a.createdAt)-R(i.createdAt)),r.slice(0,Math.min(Number(o)||100,1e3))}search(e){let t=String(e||"").toLowerCase().trim();return t?{sessions:this.listSessions({q:t,includeArchived:!0,limit:50}),checkpoints:this.timeline({q:t,limit:50})}:{sessions:[],checkpoints:[]}}health(){let e=0;for(let t of this.sessions.values())["ended","stale"].includes(xt(t,this.now(),this.cfgFor(t)))||e++;return{ok:!0,version:X,pid:process.pid,folder:this.root,machine:this.machine,machines:this.folder.machines(),peers:this.peers?{allowIncoming:this.peers.accepts,fingerprint:this.peers.id.fingerprint}:null,uptimeSec:Math.round((Date.now()-this.startedAt)/1e3),sessionsOpen:e,sessionsTotal:this.sessions.size,needsYou:this.needsList().length,answering:this.answeringInfo(),spoolPending:this.folder.spoolCount(),summarizer:this.summarizer.mode,summarizerDetail:this.summarizer.describe(),listsOpen:this.listIndex().filter(t=>!t.archived&&t.done<t.total).length,agents:this.agentsHealth(),mod:this.modHealth(),remote:this.remoteHealth(),digest:this.digestStatus(),sessions:this.emptyCounts(),...this.versionHealth()}}agentsHealth(){try{return this.agentView().agents.map(e=>({name:e.name,display:e.display,icon:e.icon,ok:e.status.ok,lastSuccessAt:e.status.lastSuccessAt,error:e.status.error?e.status.error.message:null,puller:e.puller,pullsHere:e.pullsHere,sessions:e.records.length}))}catch{return[]}}settleAsksFor(e,t){let s=t.data||{};t.type==="activity"&&s.kind==="tool"&&s.phase==="post"&&s.inputKey?this.asks.settleSession(e.id,"answered_in_terminal",{key:s.inputKey}):t.type==="activity"&&s.kind==="prompt"?this.asks.settleSession(e.id,"new_prompt"):t.type==="turn_completed"?this.asks.settleSession(e.id,"turn_ended"):t.type==="session.ended"&&this.asks.settleSession(e.id,"session_ended"),(t.type==="turn_started"||t.type==="activity"&&s.kind==="prompt")&&(e.replyPending=null)}openAsk(e={}){let t=e.sessionId;if(t&&process.env.CHECKPOINTS_IGNORE_SESSION===t)return{id:null,state:"gone",reason:"ignored"};if(this.cfg.answers&&this.cfg.answers.enabled===!1)return{id:null,state:"gone",reason:"answering_off"};let s=this.asks.open({sessionId:t,tool:e.tool,input:e.input,suggestions:e.suggestions,capture:e.capture==="mod"?"mod":"hooks",cwd:e.cwd,permissionMode:e.permissionMode});return{id:s.id,state:s.state}}onAskChange(e,t){let s=this.sessions.get(e.sessionId);s&&(t==="opened"?(s.needsInput||(s.needsInputAt=new Date(e.createdAt).toISOString()),s.needsInput=!0):!this.asks.waiting({sessionId:s.id}).length&&s.needsInput&&(!s.lastNotification||s.lastNotification==="permission_prompt")&&(s.needsInput=!1),this.refreshStatus(s),this.persist(s),this.emit("session.updated",this.publicSession(s)),t==="opened"&&this.scheduleNotify(s)),this.log("ask "+e.id+" "+t+" ("+e.kind+" "+e.tool+(e.reason?", "+e.reason:"")+") session "+String(e.sessionId).slice(0,8)),this.emit("needs.updated",{sessionId:e.sessionId,id:e.id,state:e.state,what:t})}async startPeers(e={}){if(this.peers||process.env.LEFTOFF_PEERS==="0"||this.cfg.peers&&this.cfg.peers.enabled===!1)return this.peers;this.peers=new Ii({engine:this,root:this.root,machine:this.machine,log:this.log,version:X,now:this.now,...e}),this.peers.on("needs",t=>{for(let s of this.sessions.values())s.machine===t&&!this.isMine(s)&&this.emit("session.updated",this.publicSession(s));this.emit("needs.updated",{machine:t,what:"peer"})}),this.peers.on("changed",()=>this.emit("peers.updated",{}));try{await this.peers.start()}catch(t){this.log("peer: "+t.message)}return this.peers}async stopPeers(){this.peers&&(await this.peers.stop(),this.peers=null)}peerUsable(e){return this.peers?this.peers.usable(e):{ok:!1,code:"off",reason:"cross-machine actions are off on this machine"}}async viaPeer(e,t,s,o){if(!this.peers)throw C(409,"other_machine","That session runs on "+e+"; cross-machine actions are off here");let r=this.peers.usable(e);if(!r.ok&&!["unreachable","checking"].includes(r.code))throw C(409,"peer_"+(r.code||"unavailable"),o+" on "+e+" isn’t available: "+r.reason);try{return await this.peers.request(e,t,s)}catch(i){throw C(i.status&&i.status<500?i.status===403?403:409:503,i.code&&i.code.startsWith("peer_")?i.code:"peer_"+(i.code||"error"),i.message)}}peersOverview(){return this.peers?this.peers.overview():{machine:this.machine,off:!0,peers:[]}}needsItem(e,t,s=this.now()){let o=this.needsItemRaw(e,t,s,r=>this.dismissals.has(r));return o?(o.key=Ks(e.id,o),this.dismissals.has(o.key)?null:o):null}agentNeed(e,t,s=this.now()){let o=os(e,t,s);return o?(o.key=Ks(pw(e,t),o),this.dismissals.has(o.key)?null:o):null}get dismissals(){return(!this._dismissals||this._dismissals.root!==this.root||this._dismissals.machine!==this.machine)&&(this._dismissals=new Za({root:this.root,machine:this.machine,now:this.now})),this._dismissals}dismissNeed(e){let t=this.needsListAll({skipDismissed:!0}).find(o=>o.key===e)||this.needsListAll().find(o=>o.key===e);if(!t)throw C(404,"not_found","That item isn’t waiting any more");let s=this.dismissals.dismiss(e,{sessionId:t.session.id,title:t.session.title,line:_x(t),kind:t.kind});return this.emit("needs.updated",{dismissed:e}),{key:e,...s}}restoreNeeds(e){let t=this.dismissals.restore(e);return t&&this.emit("needs.updated",{restored:t}),{restored:t}}dismissedNeeds(){let e=new Set(this.needsListAll().map(t=>t.key));for(let t of this.asks.items.values())t.state==="waiting"&&e.add(Ks(t.sessionId,{kind:t.kind,ask:{id:t.id}}));return this.dismissals.list().filter(t=>e.has(t.key))}needsItemRaw(e,t,s=this.now(),o=null){if(e.archived)return null;if(!this.isMine(e)&&this.peers){let a=this.peers.needsFor(e.id,e.machine);if(a){let{session:l,...c}=a;return{...c,peer:e.machine}}}let r=this.asks.waiting({sessionId:e.id});if(o&&r.length>1){let a=r.filter(l=>!o(Ks(e.id,{kind:l.kind,ask:{id:l.id}})));a.length&&(r=a)}if(r.length){let a=r[0];return{kind:a.kind,blocking:!0,since:new Date(a.createdAt).toISOString(),waitSec:Math.round((s-a.createdAt)/1e3),ask:this.asks.publicAsk(a),more:r.length-1,...this.needExtras(e)}}let i=t||xt(e,s,this.cfgFor(e));if(i==="ended"||i==="stale"||e.endedAt)return null;if(e.needsInput){let a=e.needsInputAt||e.lastEventAt||e.lastActivityAt,l=this.isMine(e)?null:(()=>{let c=this.peerUsable(e.machine);return c.ok?null:"Answering from here: "+c.reason})();return{kind:"terminal",blocking:!0,since:a,waitSec:a?Math.round((s-R(a))/1e3):null,message:e.lastNotificationMessage||(e.lastNotification?e.lastNotification.replace(/_/g," "):"waiting in the terminal"),notification:e.lastNotification||null,...l?{peerNote:l}:{},...this.needExtras(e)}}if(e.question&&!e.inTurn&&this.isMine(e)){if(this.liveInfo(e,i,s).live==="closed")return null;let l=e.capture==="mod"&&this.replies.listening(e.id);return{kind:"reply",blocking:!1,since:e.questionAt,waitSec:e.questionAt?Math.round((s-R(e.questionAt))/1e3):null,question:e.question,canReply:l,replyPending:e.replyPending||null,why:l?null:e.capture==="mod"?"the mod in this session isn’t listening for replies (Claude Code closed, or a mod older than 0.4.4)":"replies need the mod (hooks-only capture can’t type into a session)",...this.needExtras(e,e.question)}}return null}needExtras(e,t=null){let s={};if(!this.isMine(e))return s;try{let o=typeof t=="string"?t:t&&t.text,r=this.needContextFor(e,o),i=e.repo&&e.repo.branch||null,a=r&&r.options.length?r.options:o&&Ju.test(o)?qu(o):[];(r||i||a.length)&&(s.context={lines:r?r.lines:[],options:a,branch:i,transcript:!!this.transcriptFile(e)});let l=this.prKey(e),c=l?this.prs.get(l.cwd,l.branch):null;c&&(s.pr=c)}catch(o){this.log("needs context: "+o.message)}return s}githubRepo(e){return e&&e.repo&&e.repo.github||null}async ensureGithub(e){if(!e||!e.repo)return null;if(e.repo.github)return e.repo.github;let t=e.repo.root||e.cwd;if(!t)return null;let s=this.githubRepos.get(t);if(!s||!s.repo&&this.now()-s.at>10*6e4){if(!s||!s.pending){let o=Promise.resolve().then(()=>this.gitRemote?this.gitRemote(t):Hd(t)).catch(()=>null).then(r=>{let i=Zr(r);return this.githubRepos.set(t,{repo:i,at:this.now()}),i});this.githubRepos.set(t,{repo:s?s.repo:null,at:s?s.at:0,pending:o}),s=this.githubRepos.get(t)}await s.pending,s=this.githubRepos.get(t)}return s&&s.repo&&e.repo&&!e.repo.github&&(e.repo={...e.repo,github:s.repo},this.isMine(e)&&this.persist(e)),s&&s.repo||null}sessionPrRefs(e){if(!e||e.ignored||et(e.id))return[];let t=new Set(ei(e.prUnlinked)),s=[];for(let i of Qr(e.prLinks))t.has(i.url)||s.push({...Gs(i.url),via:i.via});let o=this.githubRepo(e),r=e.repo&&e.repo.branch;if(o&&r&&!["main","master","trunk","develop"].includes(r))for(let i of this.prStore.list(o)||[])i.branch===r&&(i.state==="open"||i.state==="draft")&&!t.has(i.url)&&!s.some(a=>a.url===i.url)&&s.push({repo:o,number:i.number,url:i.url,via:"branch"});return s}sessionPrs(e){try{return this.sessionPrRefs(e).map(t=>{let s=this.prStore.view(t.url);return s?vm(s,t.via):{repo:t.repo,number:t.number,url:t.url,title:null,state:null,via:t.via,checks:null}}).filter(t=>t.state!=="closed")}catch{return[]}}prsChanged(){this.prsTimer||(this.prsTimer=setTimeout(()=>{this.prsTimer=null,this.emit("prs.updated",{})},400),this.prsTimer.unref&&this.prsTimer.unref())}scanPrLinksTick(e=this.now()){let t=4;for(let s of this.sessions.values()){if(t<=0)break;if(!s.transcriptPath||!this.isMine(s)||s.archived||s.ignored||et(s.id))continue;let o=R(s.lastActivityAt);if(!(!o||e-o>14*864e5))try{this.scanPrLinks(s)&&t--}catch(r){this.log("pr links: "+r.message)}}}scanPrLinks(e){let t=e.transcriptPath,s;try{s=Be.statSync(t)}catch{return!1}let o=this.prScans.get(e.id);if((!o||o.file!==t||s.size<o.offset)&&(o={file:t,offset:0,pending:new Set}),s.size===o.offset)return!1;let r=xm(t,o.offset);if(o.offset=r.next,this.prScans.set(e.id,o),!r.text)return!0;let i=Sm(r.text,{pending:o.pending});if(!i.length)return!0;let a=km(Qr(e.prLinks),i,ei(e.prUnlinked));if(a.changed){e.prLinks=a.links,this.persist(e);for(let l of a.links)this.prStore.view(l.url);this.emit("session.updated",this.publicSession(e))}return!0}get todoState(){return(!this._todoState||this._todoState.root!==this.root||this._todoState.machine!==this.machine)&&(this._todoState=new rl({root:this.root,machine:this.machine,now:this.now})),this._todoState}handoffsPublic(e){if(et(e.id))return{};let t=this.todoState,s=nl(e.todos).filter(r=>!t.isDone(r.key));for(let r of t.addedList())r.sessionId===e.id&&!t.isDone(r.key)&&s.push({key:r.key,text:r.text,codes:[],at:r.at,manual:!0});let o=ni(e.files);return{todos:s,files:o,filesSummary:ud(o)}}scanHandoffsTick(e=this.now()){let t=2;for(let s of this.sessions.values()){if(t<=0)break;if(!this.isMine(s)||s.archived||s.ignored||et(s.id))continue;let o=R(s.lastActivityAt);if(!(this.handoffScans.has(s.id)&&(!o||e-o>14*864e5)))try{this.scanHandoffs(s)&&t--}catch(r){this.log("handoffs: "+r.message)}}}scanHandoffs(e){let t=this.transcriptFile(e);if(!t)return this.handoffScans.set(e.id,{file:null,size:-1}),!1;let s;try{s=Be.statSync(t)}catch{return!1}let o=this.handoffScans.get(e.id);if(o&&o.file===t&&o.size===s.size)return!1;let{messages:r}=on(t,{times:!0}),i=cd(r,{cwd:this.filesCwd(e),home:this.handoffHome});this.handoffScans.set(e.id,{file:t,size:s.size,capture:i});let a=this.editedReposOf(i.editPaths||[]);JSON.stringify(a)!==JSON.stringify(Of(e.editedRepos))&&(e.editedRepos=a.length?a:void 0,this.persist(e),this.emit("session.updated",this.publicSession(e)));let l=Um(r,{sessionId:e.id}),c=nl(e.todos),u=c.concat(l.filter(h=>!c.some(p=>p.key===h.key))).slice(-20),d=ni(i);return(JSON.stringify(u)!==JSON.stringify(c)||JSON.stringify(d)!==JSON.stringify(ni(e.files)))&&(e.todos=u.length?u:void 0,e.files=d||void 0,this.persist(e),this.emit("session.updated",this.publicSession(e)),u.length!==c.length&&this.emit("needs.updated",{what:"todos"})),!0}filesCwd(e){return e.repo&&(e.repo.worktree||e.repo.root)||e.cwd||null}editedReposOf(e){let t=new Map,s=new Map;for(let o of e.slice(0,2e3)){let r=qe.dirname(o);s.has(r)||s.set(r,wi(r));let i=s.get(r);if(!i)continue;let a=i.mainRoot,l=t.get(a)||{root:a,name:qe.basename(a).replace(/\.git$/,"")||qe.basename(a),branch:i.branch||null,n:0};l.n++,i.worktree&&(l.worktree=i.worktree.path,l.worktreeName=i.worktree.name,l.branch=i.branch||l.branch),t.set(a,l)}return[...t.values()].sort((o,r)=>r.n-o.n).slice(0,8)}todoSessionInfo(e){if(!e)return null;let t=this.repoKeyFor(e);return{id:e.id,title:e.title||"Untitled session",repoLabel:this.repoLabels().get(t)||ge(e),repoKey:t,machine:e.machine||this.machine,isLocal:this.isMine(e),endedAt:e.endedAt||null}}todosOverview(e=this.now()){let t=this.todoState,s=[],o=new Map;for(let a of this.sessions.values())if(!(a.archived||a.ignored||et(a.id)))for(let l of nl(a.todos)){let c={...l,session:this.todoSessionInfo(a)};o.set(l.key,c),t.isDone(l.key)||s.push(c)}for(let a of t.addedList()){let l=a.sessionId?this.sessions.get(a.sessionId):null;if(l&&(l.archived||l.ignored))continue;let c={key:a.key,text:a.text,codes:[],at:a.at,manual:!0,session:this.todoSessionInfo(l)};o.set(a.key,c),t.isDone(a.key)||s.push(c)}s.sort((a,l)=>String(a.at||"").localeCompare(String(l.at||"")));let r=t.doneList().map(a=>{let l=o.get(a.key);return{key:a.key,text:l&&l.text||a.text||"To-do",doneAt:a.at,session:l&&l.session||this.todoSessionInfo(this.sessions.get(a.sessionId))}}),i=s.length?Math.min(...s.map(a=>R(a.at)||e)):null;return{open:s,done:r,count:s.length,oldestDays:i==null?null:Math.floor((e-i)/864e5)}}todoDone(e){let t=this.todosOverview().open.find(o=>o.key===e);if(!t)throw C(404,"not_found","That to-do isn’t open.");let s=this.todoState.done(e,{text:t.text,sessionId:t.session&&t.session.id});return this.todoChanged(t.session&&t.session.id),{ok:!0,key:e,done:s}}todoRestore(e){let t=this.todoState.restore(e);return this.todoChanged(null),{ok:!0,restored:t}}todoAdd(e,{sessionId:t=null}={}){let s=null;if(t){let r=this.sessions.get(String(t));if(!r||et(r.id))throw C(404,"not_found","no such session");s=r.id}let o;try{o=this.todoState.add(e,{sessionId:s})}catch(r){throw C(400,r.code||"bad_todo",r.message)}return this.todoChanged(s),{ok:!0,todo:{key:o.key,text:o.text,codes:[],at:o.at,manual:!0,session:this.todoSessionInfo(s&&this.sessions.get(s))}}}todoChanged(e){this.emit("needs.updated",{what:"todos"});let t=e?[e]:[];if(!e)for(let s of this.sessions.values())s.todos&&s.todos.length&&!s.archived&&t.push(s.id);for(let s of t){let o=this.sessions.get(s);o&&this.emit("session.updated",this.publicSession(o))}}async sessionFile(e,{path:t,plan:s,maxBytes:o=ti,fromPeer:r=!1}={}){if(et(e))throw C(404,"not_found","External agents don’t share files.");let i=this.sessions.get(e);if(!i)throw C(404,"not_found","no such session");if(!this.isMine(i)){if(r)throw C(409,"other_machine","that session isn’t on "+this.machine);return{...(await this.viaPeer(i.machine,"session.file",{sessionId:e,path:t||null,plan:s||null},"Opening that file")).result,peer:i.machine}}let a=this.transcriptFile(i);if(!a)throw C(404,"no_transcript","Claude Code’s transcript for this session isn’t on this machine.");let{messages:l}=on(a,{times:!0});if(s){let f=Km(l,String(s));if(!f)throw C(404,"not_found","That plan isn’t in this session’s transcript.");let h=(ni(i.files)||{plans:[]}).plans.find(p=>p.id===String(s));return{name:h&&h.title||"Plan",plan:!0,type:"markdown",mime:"text/markdown",size:Buffer.byteLength(f),text:f.slice(0,o)}}let c=cd(l,{cwd:this.filesCwd(i),home:this.handoffHome});if(!Bm(c,t))throw C(403,"not_allowed","Leftoff only opens files this session wrote.");let u=c.docs.find(f=>f.path===t),d=Gm(t,{maxBytes:o});return d.missing?{name:u.name,path:t,missing:!0,message:"File no longer exists"}:d.tooBig?{name:u.name,path:t,tooBig:!0,size:d.size,type:d.type,mime:d.mime,message:"Too big to open here ("+Math.round(d.size/1024)+" KB)."}:{name:u.name,path:t,kind:u.kind,...d}}async linkPr(e,t={},{fromPeer:s=!1}={}){if(et(e))throw C(400,"agent_session","External agents don’t link PRs.");let o=this.sessions.get(e);if(!o)throw C(404,"not_found","no such session");let r=!!t.unlink;if(!this.isMine(o)){if(s)throw C(409,"other_machine","that session isn’t on "+this.machine);let f=(await this.viaPeer(o.machine,"session.prlink",{sessionId:e,ref:String(t.ref||""),unlink:r},"Linking a PR")).result||{};Array.isArray(f.prLinks)&&(o.prLinks=Qr(f.prLinks),o.prUnlinked=ei(f.prUnlinked));let h=this.publicSession(o);return this.emit("session.updated",h),this.prsChanged(),{...f,prs:h.prs,peer:o.machine}}let i=await this.ensureGithub(o),a=ym(t.ref,{repo:i});if(a.error){let d={empty:"Paste a PR link or #number.",no_repo:"This session’s repo has no GitHub remote here, so paste the PR’s link.",bad_ref:"That isn’t a PR link or #number."}[a.error];throw C(400,a.error==="no_repo"?"no_repo":"bad_ref",d)}let l=Qr(o.prLinks),c=ei(o.prUnlinked);r?(l=l.filter(d=>d.url!==a.url),c.includes(a.url)||c.push(a.url)):(c=c.filter(d=>d!==a.url),l.some(d=>d.url===a.url)||l.push({url:a.url,via:"manual"})),o.prLinks=l,o.prUnlinked=c,this.persist(o,!0),r||this.prStore.view(a.url);let u=this.publicSession(o);return this.emit("session.updated",u),this.prsChanged(),{ok:!0,sessionId:e,url:a.url,unlinked:r,prLinks:l,prUnlinked:c,prs:u.prs}}async prsOverview({view:e="mine",fresh:t=!1,fromPeer:s=!1,waitMs:o=8e3}={}){e=e==="team"?"team":"mine";let r;try{r=await this.prGhStatus({fresh:!!t})}catch{r=null}r=r||{status:"missing"};let i={status:r.status,account:r.account||null,fix:r.fix||null},a=G(this.now());if(r.status!=="ready"){if(!s&&this.peers){let w=await this.prsFromPeer(e,t);if(w)return s?w:this.hydratePrs(w)}return{view:e,gh:i,me:null,machine:this.machine,servedBy:null,repos:[],groups:[],loading:!1,updatedAt:a}}this.prStore.offUntil=0;let l=[...this.sessions.values()].filter(w=>!w.archived&&!w.ignored&&!et(w.id)),c=new Map;for(let w of l)if(w.repo&&!w.repo.github&&(w.repo.root||w.cwd)){let x=w.repo.root||w.cwd;c.has(x)||c.set(x,[]),c.get(x).push(w)}await Promise.all([...c.values()].slice(0,60).map(async w=>{let x=await this.ensureGithub(w[0]);if(x)for(let v of w.slice(1))v.repo&&!v.repo.github&&(v.repo={...v.repo,github:x},this.isMine(v)&&this.persist(v))}));let u=[...new Set(l.map(w=>this.githubRepo(w)).filter(Boolean))].sort(),d=e==="team"?u:[...new Set(l.filter(w=>w.repo&&w.repo.branch).map(w=>this.githubRepo(w)).filter(Boolean))],f=await this.prStore.settle({repos:d,waitMs:o,fresh:t}),h=new Map,p=(w,x)=>(R(x.lastActivityAt)||0)-(R(w.lastActivityAt)||0);for(let w of l.slice().sort(p))for(let x of this.sessionPrRefs(w)){let v=h.get(x.url);v||h.set(x.url,v={ref:x,via:new Set,sessionIds:[]}),v.via.add(x.via),v.sessionIds.includes(w.id)||v.sessionIds.push(w.id)}let m=r.account||null,g,S=0;if(e==="mine"){f=await this.prStore.settle({urls:[...h.keys()],waitMs:o,fresh:t})&&f;let w=[];for(let[x,v]of h){let b=this.prStore.views.get(x);if(!b||!b.pr){S++;continue}w.push({pr:b.pr,via:[...v.via],sessionIds:v.sessionIds})}g=bm(w,{now:this.now(),me:m})}else g=u.map(w=>{let x=this.prStore.lists.get(w),v=x&&x.prs;return{key:w,label:w,failed:!v,rows:(v||[]).slice().sort((b,y)=>String(y.updatedAt||"").localeCompare(String(b.updatedAt||""))).map(b=>{let y=h.get(b.url);return{pr:b,attention:nd(b,m),via:y?[...y.via]:[],sessionIds:y?y.sessionIds:[]}})}});let k={view:e,gh:i,me:m,machine:this.machine,servedBy:this.machine,repos:u,groups:g,unread:S,loading:!f||this.prStore.busy(),updatedAt:a};return s?k:this.hydratePrs(k)}hydratePrs(e){let t=this.now(),s=new Map,o=r=>{if(!s.has(r)){let i=this.sessions.get(r);s.set(r,i?this.publicSession(i,t):null)}return s.get(r)};for(let r of e.groups||[])for(let i of r.rows||[])i.sessions=(i.sessionIds||[]).map(o).filter(Boolean);return e}async prsFromPeer(e,t){let s=[];this.prPeer&&this.now()-this.prPeer.at<10*6e4&&s.push(this.prPeer.machine);let o=[];try{o=this.peers.overview().peers||[]}catch{}for(let r of o){let i=r.usable||{};(i.ok||["unreachable","checking"].includes(i.code))&&!s.includes(r.machine)&&s.push(r.machine)}for(let r of s.slice(0,3))try{let i=await this.peers.request(r,"prs.list",{view:e,fresh:!!t}),a=i&&i.result;if(a&&a.gh&&a.gh.status==="ready")return this.prPeer={machine:r,at:this.now()},this.prStore.absorb((a.groups||[]).flatMap(l=>(l.rows||[]).map(c=>c.pr))),{...a,servedBy:r}}catch{}return null}prKey(e){let t=e.repo&&e.repo.branch,s=e.repo&&(e.repo.worktree||e.repo.root)||e.cwd;return!t||!s||e.ignored||["main","master","trunk","develop"].includes(t)?null:{cwd:s,branch:t}}transcriptFile(e){if(e.transcriptPath&&Be.existsSync(e.transcriptPath))return e.transcriptPath;let t=this.transcriptHits.get(e.id);if(t&&t.dirs===this.transcriptDirs&&(t.file?Be.existsSync(t.file):this.now()-t.at<15e3))return t.file;let s=null;try{s=Ao(e.id,this.transcriptDirs?{dirs:this.transcriptDirs}:{})||null}catch{}return this.transcriptHits.set(e.id,{file:s,at:this.now(),dirs:this.transcriptDirs}),s}needContextFor(e,t){let s=this.transcriptFile(e);if(!s)return null;let o;try{o=Be.statSync(s)}catch{return null}let r=s+":"+o.size+":"+o.mtimeMs+":"+(t||""),i=this.needCtx.get(e.id);if(i&&i.key===r)return i.value;let{messages:a}=on(s,{maxBytes:512*1024}),l=em(a,{question:t});return this.needCtx.set(e.id,{key:r,value:l}),l}async sessionTurns(e,{limit:t=20}={}){let s=Math.max(1,Math.min(60,Number(t)||20));if(et(e))throw C(404,"no_transcript","External agents don’t share their transcripts.");let o=this.sessions.get(e);if(!o)throw C(404,"not_found","no such session");if(!this.isMine(o)){let l=await this.viaPeer(o.machine,"session.turns",{sessionId:e,limit:s},"Reading that session");return{...l.result,peer:o.machine,peerVia:l.via}}let r=this.transcriptFile(o);if(!r)throw C(404,"no_transcript","Claude Code’s transcript for this session isn’t on this machine.");let{messages:i,meta:a}=on(r,{maxBytes:4*1024*1024});return{sessionId:e,machine:this.machine,branch:o.repo&&o.repo.branch||a.gitBranch||null,turns:tm(i,{limit:s,cwd:o.repo&&o.repo.root||o.cwd||a.cwd})}}needsList(){return this.needsListAll({skipDismissed:!0}).filter(e=>!this.dismissals.has(e.key))}needsListAll({skipDismissed:e=!1}={}){let t=[],s=this.now();for(let o of this.sessions.values()){if(o.archived)continue;let r=this.needsItemRaw(o,null,s,e?a=>this.dismissals.has(a):null);if(r&&(r.key=Ks(o.id,r)),!r)continue;let i=this.publicSession(o,s);t.push({...r,session:{id:o.id,title:i.title,repoLabel:i.repoLabel,repoKey:i.repoKey,machine:i.machine,isLocal:i.isLocal,capture:i.capture,live:i.live,jump:i.jump,terminal:i.terminal,status:i.status}})}try{for(let o of this.agentView().agents)for(let r of o.records){let i=os(o,r,s);if(!i)continue;i.key=Ks(pw(o,r),i);let a=qd(o,r,s);t.push({...i,session:{id:a.id,title:a.title,repoLabel:a.repoLabel,repoKey:a.repoKey,machine:a.machine,isLocal:!1,capture:"agent",live:null,jump:null,terminal:null,status:a.status,agent:a.agent}})}}catch(o){this.log("agents: "+o.message)}return t.sort((o,r)=>r.blocking-o.blocking||(r.waitSec||0)-(o.waitSec||0))}recentAnswered(e=20){let t={answered_in_terminal:"Answered in the terminal",cancelled_in_terminal:"Cancelled in the terminal (Esc)",new_prompt:"Moved on: you typed a new prompt",turn_ended:"Moved on: the turn ended",session_ended:"The session ended",no_listener:"Claude Code stopped waiting",unknown:"Gone"},s=[];for(let r of this.asks.items.values()){if(r.state==="waiting")continue;let i=this.sessions.get(r.sessionId),a=r.kind==="permission"?r.describe.verb+": "+r.describe.detail:(r.questions||[]).map(c=>c.question).join(" / "),l=r.state==="answered"?gi(r,r.answer)+" from the board":r.state==="expired"?"Timed out: left to the terminal":t[r.reason]||r.reason||"Gone";s.push({at:new Date(r.settledAt||r.createdAt).toISOString(),askedAt:new Date(r.createdAt).toISOString(),kind:r.kind,tool:r.tool,what:a,state:r.state,reason:r.reason,outcome:l,where:r.by?r.by.where:null,session:{id:r.sessionId,title:i?i.title||ge(i):null,repoLabel:i?this.publicSession(i).repoLabel:null}})}let o=this.now()-24*3600*1e3;for(let r of this.sessions.values())for(let i of r.answers||[])i.kind!=="reply"||R(i.at)<o||s.push({at:i.at,askedAt:null,kind:"reply",tool:null,what:i.what,state:"answered",reason:null,outcome:i.summary+" from the board",where:i.where,session:{id:r.id,title:r.title||ge(r),repoLabel:this.publicSession(r).repoLabel}});return s.sort((r,i)=>R(i.at)-R(r.at)).slice(0,e)}answeringInfo(e){let t=!!(this.cfg.remote&&this.cfg.remote.allowAnswers);return{remoteAllowed:t,allowedHere:e==="remote"?t:!0}}assertMayAnswer(e={}){if(e.via!=="peer"){if(e.via==="remote"&&!(this.cfg.remote&&this.cfg.remote.allowAnswers))throw C(403,"answers_remote_off","Answering from another device is off. On a Mac, turn on Settings → Privacy & security → Trust my paired devices.");if(e.proxied&&e.via!=="remote")throw C(403,"loopback_only","only from the machine running Leftoff")}}async answerAsk(e,t={},s={}){this.assertMayAnswer(s);let o=this.asks.get(e);if(!o&&this.peers&&this.peers.machineForAsk(e)){let u=this.peers.machineForAsk(e),d=await this.viaPeer(u,"ask.answer",{askId:e,body:t},"Answering");return this.log("answer forwarded to "+u+" via "+d.via+": "+(d.result&&d.result.summary)),this.peers.poll().catch(()=>{}),{...d.result,peer:u,peerVia:d.via}}if(!o)throw C(404,"not_found","That request is gone (Claude Code moved on, or the daemon restarted).");let r=this.sessions.get(o.sessionId);if(r&&!this.isMine(r))throw C(409,"other_machine","That session runs on "+r.machine);let i=Ud(s),a=this.asks.answer(e,t,{where:i,via:s.via||"loopback",ip:s.ip||null,who:s.who||null}),l=await this.asks.delivery(e,3e3),c=gi(a,a.answer);return this.audit(a.sessionId,{at:new Date(a.answeredAt).toISOString(),kind:a.kind,tool:a.tool,what:a.kind==="permission"?a.describe.verb+": "+a.describe.detail:(a.questions||[]).map(u=>u.question).join(" / "),summary:c,action:t.action,where:i,via:s.via||"loopback",who:s.who||null,delivered:l}),{ok:!0,id:e,state:a.state,delivered:l,summary:c,ask:this.asks.publicAsk(a)}}async postReply(e,t,s={}){this.assertNotAgent(e,"replying"),this.assertMayAnswer(s);let o=this.sessions.get(e);if(!o)throw C(404,"not_found","no such session");if(!this.isMine(o)){let l=await this.viaPeer(o.machine,"session.reply",{sessionId:o.id,text:t},"Replying");return{...l.result,peer:o.machine,peerVia:l.via}}if(o.capture!=="mod")throw C(409,"needs_mod","Replies need the Leftoff mod; this session is captured by settings hooks, which can’t type into it. Use Go.");if(this.publicSession(o).live==="closed"||o.endedAt)throw C(409,"closed","That session is closed.");if(!this.replies.listening(o.id))throw C(409,"not_listening","This session’s mod isn’t picking up replies (Claude Code may be busy starting, or the mod is older than 0.4.4). Use Go.");let i=Ud(s),a=this.replies.put(o.id,t,{where:i,via:s.via||"loopback"});return o.replyPending={id:a.id,at:new Date(a.at).toISOString(),text:a.text.slice(0,200)},this.audit(o.id,{at:new Date(a.at).toISOString(),kind:"reply",tool:null,what:o.question||"reply",summary:gi(null,{action:"reply",text:a.text}),action:"reply",where:i,via:s.via||"loopback",who:s.who||null,delivered:a.delivered,busy:!!o.inTurn}),this.emit("session.updated",this.publicSession(o)),this.emit("needs.updated",{sessionId:o.id,what:"replied"}),{ok:!0,id:a.id,delivered:a.delivered,queuedBehindTurn:!!o.inTurn}}async takeReply(e,t,s){let o=this.sessions.get(e),r=this.replies.listening(e),i=this.replies.take(e,t,s);!r&&o&&o.question&&this.emit("session.updated",this.publicSession(o));let a=await i;return a?{reply:{id:a.id,text:a.text}}:{reply:null}}audit(e,t){let s=this.sessions.get(e);if(this.log("answer from the board: "+t.summary+" · "+(t.what||"")+" · "+t.where+" · session "+String(e).slice(0,8)),!!s){s.answers=[...s.answers||[],t].slice(-20),this.persist(s,!0);try{let o=qe.join(this.folder.sessionDir(s),"answers.md"),r=Be.existsSync(o)?"":`# Answered from the Leftoff board
223
223
 
224
224
  Every answer given from the board for this session: when, what was asked, the answer, and where it came from.
225
225
 
226
226
  `,i="- "+t.at+" · "+t.summary+(t.what?" · "+(t.tool?t.tool+" — ":"")+String(t.what).replace(/\s+/g," ").slice(0,300):"")+" · from the board "+t.where+(t.delivered===!1?" · not yet picked up by Claude Code":"")+`
227
- `;He.mkdirSync(nr.dirname(o),{recursive:!0}),He.appendFileSync(o,r+i)}catch(o){this.log("answers.md: "+o.message)}}}scheduleNotify(e){let t=this.cfg.notify||{};if(t.needsYou===!1||!this.isMine(e))return;let s=this.needsItem(e);if(!s||!s.blocking)return;let o=e.id+"|"+(s.ask?s.ask.id:s.kind+s.since);if(this.notified.has(o))return;this.notified.set(o,"pending"),this.notified.size>500&&this.notified.delete(this.notified.keys().next().value);let r=Math.max(0,Number(t.delaySec!=null?t.delaySec:10))*1e3;this.later(r,()=>{let i=this.needsItem(e),a=i&&(i.ask?e.id+"|"+i.ask.id:e.id+"|"+i.kind+i.since);if(!i||a!==o)return;let l=Jy(i,e.title||me(e)),c=this.notifier({title:"Leftoff",subtitle:l.subtitle,message:l.message,sessionId:e.id,goBin:Ke});this.notified.set(o,c&&c.ok?"sent":"failed"),this.emit("needs.notified",{sessionId:e.id,key:o,result:c})})}remoteHealth(){let e=(this.cfg.remoteHosts||[]).map(Ut).filter(Boolean);return{enabled:e.length>0,hosts:e,urls:Kl(e),requireToken:this.cfg.remoteRequireToken!==!1}}remoteState(e){return{...this.remoteHealth(),via:e||null,...Wl(this.root)}}setRemoteHosts({add:e=[],remove:t=[],requireToken:s}={}){let o=a=>(Array.isArray(a)?a:[a]).map(Ut).filter(Boolean),r=new Set(o(t)),i=(this.cfg.remoteHosts||[]).map(Ut).filter(a=>a&&!r.has(a));for(let a of o(e))i.includes(a)||i.push(a);return this.cfg.remoteHosts=i,typeof s=="boolean"&&(this.cfg.remoteRequireToken=s),po(this.cfg,this.root),this.remoteHealth()}reloadRemote(){let e=Q(this.root);return this.cfg.remoteHosts=e.remoteHosts||[],this.cfg.remoteRequireToken=e.remoteRequireToken,this.remoteHealth()}versionHealth(){let e=this.updateState(),t=this.changelog();return{update:e.checked?{available:!!e.available,remoteVersion:e.remote&&e.remote.version,remoteSha:e.remote&&e.remote.sha&&e.remote.sha.slice(0,7),checkedAt:e.checkedAt,error:e.error||null,incoming:(e.incoming||[]).map(s=>s.version)}:{available:!1,checkedAt:null,enabled:e.enabled},whatsNew:{unseen:t.entries.filter(s=>s.isNew).length,upgradedFrom:t.upgradedFrom,upgradeNotice:t.upgradeNotice}}}modHealthFile(){return nr.join(this.root,".mod-health."+this.machine.replace(/[^\w.-]/g,"_")+".json")}recordModHealth(e){let t=Array.isArray(e&&e.commands)?e.commands.slice(0,10):null;if(!t)throw C(400,"bad_request","body must be { sessionId, commands: [{ key, want, name, ok, error }] }");let s=(o,r=200)=>o==null?null:String(o).slice(0,r);this._modHealth={at:s(e.at,40)||new Date(this.now()).toISOString(),sessionId:s(e.sessionId,80),machine:s(e.machine,60)||this.machine,modVersion:s(e.modVersion,20),commands:t.map(o=>({key:s(o.key,20),want:s(o.want,40),name:s(o.name,40),ok:!!o.ok,error:o.ok?null:s(o.error)}))};try{He.writeFileSync(this.modHealthFile(),JSON.stringify(this._modHealth,null,2)+`
228
- `)}catch{}return this.emit("session.updated",{mod:!0}),{ok:!0}}modHealth(){if(this._modHealth===void 0)try{this._modHealth=JSON.parse(He.readFileSync(this.modHealthFile(),"utf8"))}catch{this._modHealth=null}let e=this._modHealth;return e?{...e,failed:e.commands.filter(t=>!t.ok).map(t=>({want:t.want,error:t.error}))}:null}keyStatus(){let e=this.cfg.summarizer||{},t={};for(let[s,o]of Object.entries(Da)){let r=String(e.provider||"").toLowerCase()===s,i=r&&e.apiKeyEnv||o.env,a={apiKeyEnv:r?e.apiKeyEnv:null,apiKeyFile:r?e.apiKeyFile:null,root:this.root},l=!!An(s,a);t[s]={found:l,env:i,envSet:!!process.env[i],file:r&&e.apiKeyFile||o.file}}return t}get pulls(){if(!this._pulls){let e=this.cfg.summarizer||{},t=String(e.provider||"").toLowerCase()==="ollama"&&e.baseUrl?e.baseUrl:void 0;this._pulls=new Bl({...t?{baseUrl:t}:{},...this.pullImpl?{pull:this.pullImpl}:{},...this.pullFetch?{fetchImpl:this.pullFetch}:{},onChange:s=>this.emit("model.pull",s)})}return this._pulls}async modelChoices({provider:e,baseUrl:t}={}){let s=this.cfg.summarizer||{},o=String(e||s.provider||"auto").toLowerCase(),r=o===String(s.provider||"auto").toLowerCase();return ly(o,{current:r?s.model:null,baseUrl:t||(r?s.baseUrl:null),pulls:this.pulls.list(),...this.pullFetch?{fetchImpl:this.pullFetch}:{}})}async startOllama(){let e=this.cfg.summarizer||{},t=String(e.provider||"").toLowerCase()==="ollama"&&e.baseUrl||pt,s=await Gt({baseUrl:t,...this.pullFetch?{fetchImpl:this.pullFetch}:{}});if(s.running)return{ok:!0,already:!0,models:await this.modelChoices({provider:"ollama"})};let o=Ha({binary:s.binary});if(!o)throw C(409,"no_ollama","Ollama isn’t installed here. Install it from ollama.com first.");let r=rm(o);if(!r.started)throw C(500,"start_failed","Couldn’t start Ollama ("+(r.error||o.shell)+")");return this.log("ollama: started from the board ("+o.shell+")"),{ok:!!(await Co({baseUrl:t,timeoutMs:Number(process.env.CHECKPOINTS_OLLAMA_START_WAIT_MS)||2e4,...this.pullFetch?{fetchImpl:this.pullFetch}:{}})).running,shell:o.shell,models:await this.modelChoices({provider:"ollama"})}}startPull({tag:e,use:t=!1}={}){return this.pulls.start(String(e||""),{use:t,onDone:s=>{if(!s.use)return;let o=this.cfg.summarizer||{};this.updateConfig({summarizer:{...o,provider:"ollama",model:s.tag,apiKeyEnv:null,apiKeyFile:null}}),this.log("summarizer now ollama "+s.tag+" (downloaded from the board)")}})}publicConfig(){return{...this.cfg,summarizerResolved:this.summarizer.mode,folder:this.root,machineResolved:this.machine,keys:this.keyStatus(),modelHints:{ollama:Yr.map(e=>e.tag),"ollama-cloud":zr.map(e=>e.tag)}}}saveKey(e,t){let s=Da[e];if(!s)throw C(400,"bad_provider","no key needed for "+e);let o=String(t||"").trim();if(!o||o.length>400||/\s/.test(o))throw C(400,"bad_key","that doesn't look like an API key");let r=Sa(s.file.replace(/^\.secret\//,""),o,this.root),i=this.cfg.summarizer||{};return String(i.provider||"").toLowerCase()===e&&i.apiKeyFile&&i.apiKeyFile!==s.file&&(this.cfg.summarizer={...i,apiKeyFile:s.file},po(this.cfg,this.root)),this.summarizer=new At(this.cfg,{root:this.root}),{ok:!0,provider:e,file:r,keys:this.keyStatus()}}updateConfig(e,{via:t}={}){let s=["dormantAfterMin","minGapMin","staleAfterSec","longRunCheckpointMin","digest","summarizer","machineNames","updates","notify","remote","summary"];for(let o of Object.keys(e||{}))if(!s.includes(o))throw C(400,"bad_config","cannot change "+o+" here");if(e.remote!==void 0&&t==="remote")throw C(403,"loopback_only","Remote-access settings can only be changed on the machine running Leftoff");if(e.remote!==void 0){if(!e.remote||typeof e.remote!="object")throw C(400,"bad_config","remote is an object");let o={};for(let r of Object.keys(e.remote)){if(r!=="allowAnswers")throw C(400,"bad_config","cannot change remote."+r+" here");o.allowAnswers=e.remote.allowAnswers===!0}e={...e,remote:o},o.allowAnswers!==void 0&&this.log("settings: trust my paired devices "+(o.allowAnswers?"ON":"off"))}if(e.notify!==void 0){if(!e.notify||typeof e.notify!="object")throw C(400,"bad_config","notify is an object");let o={};if(e.notify.needsYou!==void 0&&(o.needsYou=!!e.notify.needsYou),e.notify.delaySec!==void 0){let r=Number(e.notify.delaySec);if(!(r>=0&&r<=600))throw C(400,"bad_config","notify.delaySec is 0–600");o.delaySec=r}e={...e,notify:o}}if(e.summary!==void 0){if(!e.summary||typeof e.summary!="object")throw C(400,"bad_config","summary is an object");let o={};for(let r of Object.keys(e.summary)){if(r!=="length")throw C(400,"bad_config","cannot change summary."+r+" here");if(o.length=gn(e.summary.length,{strict:!0}),!o.length)throw C(400,"bad_config","summary.length is short, informative or long")}e={...e,summary:o}}if(e.summarizer&&e.summarizer.mode&&!e.summarizer.provider&&(e.summarizer.provider=e.summarizer.mode),e.digest&&typeof e.digest=="object"){let o={...e.digest};if(o.graceMin!==void 0){let r=Number(o.graceMin);if(!Number.isFinite(r)||r<0||r>720)throw C(400,"bad_config","digest.graceMin is minutes, 0-720");o.graceMin=r}if(o.time!==void 0&&o.time!==null&&!/^\d{1,2}:\d{2}$/.test(String(o.time)))throw C(400,"bad_config","digest.time is HH:MM, or null for no automatic digest");if(o.days!==void 0){if(!Array.isArray(o.days)||!o.days.length)throw C(400,"bad_config","digest.days needs at least one day");o.days=Lo(o.days)}if(o.skipEmpty!==void 0&&(o.skipEmpty=!!o.skipEmpty),o.timeZone!==void 0){if(o.timeZone===""||o.timeZone===null)o.timeZone=null;else if(!Mo(o.timeZone))throw C(400,"bad_config","unknown time zone: "+o.timeZone)}e={...e,digest:o}}for(let o of s)e[o]!==void 0&&(this.cfg[o]=typeof e[o]=="object"&&e[o]&&!Array.isArray(e[o])?{...this.cfg[o],...e[o]}:e[o]);return po(this.cfg,this.root),e.summarizer&&(this.summarizer=new At(this.cfg,{root:this.root})),e.updates&&!this.stopped&&this.timers.length&&this.scheduleUpdateCheck(),this.publicConfig()}}});var sr,Qe,Li,ls,cs,us,Di,dw,fw,kx,bx,vx,xx,hw,Ax,Tx,_x,Ex,pw,lI,mw=J(()=>{sr=function(n,e){let o=n,r=Li[e],i=null,a=0,l=null,c=[],u={},d=function(y,_){a=o*4+17,i=(function(A){let O=new Array(A);for(let I=0;I<A;I+=1){O[I]=new Array(A);for(let N=0;N<A;N+=1)O[I][N]=null}return O})(a),f(0,0),f(a-7,0),f(0,a-7),m(),p(),S(y,_),o>=7&&g(y),l==null&&(l=x(o,r,c)),k(l,_)},f=function(y,_){for(let A=-1;A<=7;A+=1)if(!(y+A<=-1||a<=y+A))for(let O=-1;O<=7;O+=1)_+O<=-1||a<=_+O||(0<=A&&A<=6&&(O==0||O==6)||0<=O&&O<=6&&(A==0||A==6)||2<=A&&A<=4&&2<=O&&O<=4?i[y+A][_+O]=!0:i[y+A][_+O]=!1)},h=function(){let y=0,_=0;for(let A=0;A<8;A+=1){d(!0,A);let O=cs.getLostPoint(u);(A==0||y>O)&&(y=O,_=A)}return _},p=function(){for(let y=8;y<a-8;y+=1)i[y][6]==null&&(i[y][6]=y%2==0);for(let y=8;y<a-8;y+=1)i[6][y]==null&&(i[6][y]=y%2==0)},m=function(){let y=cs.getPatternPosition(o);for(let _=0;_<y.length;_+=1)for(let A=0;A<y.length;A+=1){let O=y[_],I=y[A];if(i[O][I]==null)for(let N=-2;N<=2;N+=1)for(let M=-2;M<=2;M+=1)N==-2||N==2||M==-2||M==2||N==0&&M==0?i[O+N][I+M]=!0:i[O+N][I+M]=!1}},g=function(y){let _=cs.getBCHTypeNumber(o);for(let A=0;A<18;A+=1){let O=!y&&(_>>A&1)==1;i[Math.floor(A/3)][A%3+a-8-3]=O}for(let A=0;A<18;A+=1){let O=!y&&(_>>A&1)==1;i[A%3+a-8-3][Math.floor(A/3)]=O}},S=function(y,_){let A=r<<3|_,O=cs.getBCHTypeInfo(A);for(let I=0;I<15;I+=1){let N=!y&&(O>>I&1)==1;I<6?i[I][8]=N:I<8?i[I+1][8]=N:i[a-15+I][8]=N}for(let I=0;I<15;I+=1){let N=!y&&(O>>I&1)==1;I<8?i[8][a-I-1]=N:I<9?i[8][15-I-1+1]=N:i[8][15-I-1]=N}i[a-8][8]=!y},k=function(y,_){let A=-1,O=a-1,I=7,N=0,M=cs.getMaskFunction(_);for(let z=a-1;z>0;z-=2)for(z==6&&(z-=1);;){for(let U=0;U<2;U+=1)if(i[O][z-U]==null){let E=!1;N<y.length&&(E=(y[N]>>>I&1)==1),M(O,z-U)&&(E=!E),i[O][z-U]=E,I-=1,I==-1&&(N+=1,I=7)}if(O+=A,O<0||a<=O){O-=A,A=-A;break}}},w=function(y,_){let A=0,O=0,I=0,N=new Array(_.length),M=new Array(_.length);for(let P=0;P<_.length;P+=1){let W=_[P].dataCount,oe=_[P].totalCount-W;O=Math.max(O,W),I=Math.max(I,oe),N[P]=new Array(W);for(let Se=0;Se<N[P].length;Se+=1)N[P][Se]=255&y.getBuffer()[Se+A];A+=W;let K=cs.getErrorCorrectPolynomial(oe),Ie=Di(N[P],K.getLength()-1).mod(K);M[P]=new Array(K.getLength()-1);for(let Se=0;Se<M[P].length;Se+=1){let q=Se+Ie.getLength()-M[P].length;M[P][Se]=q>=0?Ie.getAt(q):0}}let z=0;for(let P=0;P<_.length;P+=1)z+=_[P].totalCount;let U=new Array(z),E=0;for(let P=0;P<O;P+=1)for(let W=0;W<_.length;W+=1)P<N[W].length&&(U[E]=N[W][P],E+=1);for(let P=0;P<I;P+=1)for(let W=0;W<_.length;W+=1)P<M[W].length&&(U[E]=M[W][P],E+=1);return U},x=function(y,_,A){let O=dw.getRSBlocks(y,_),I=fw();for(let M=0;M<A.length;M+=1){let z=A[M];I.put(z.getMode(),4),I.put(z.getLength(),cs.getLengthInBits(z.getMode(),y)),z.write(I)}let N=0;for(let M=0;M<O.length;M+=1)N+=O[M].dataCount;if(I.getLengthInBits()>N*8)throw"code length overflow. ("+I.getLengthInBits()+">"+N*8+")";for(I.getLengthInBits()+4<=N*8&&I.put(0,4);I.getLengthInBits()%8!=0;)I.putBit(!1);for(;!(I.getLengthInBits()>=N*8||(I.put(236,8),I.getLengthInBits()>=N*8));)I.put(17,8);return w(I,O)};u.addData=function(y,_){_=_||"Byte";let A=null;switch(_){case"Numeric":A=kx(y);break;case"Alphanumeric":A=bx(y);break;case"Byte":A=vx(y);break;case"Kanji":A=xx(y);break;default:throw"mode:"+_}c.push(A),l=null},u.isDark=function(y,_){if(y<0||a<=y||_<0||a<=_)throw y+","+_;return i[y][_]},u.getModuleCount=function(){return a},u.make=function(){if(o<1){let y=1;for(;y<40;y++){let _=dw.getRSBlocks(y,r),A=fw();for(let I=0;I<c.length;I++){let N=c[I];A.put(N.getMode(),4),A.put(N.getLength(),cs.getLengthInBits(N.getMode(),y)),N.write(A)}let O=0;for(let I=0;I<_.length;I++)O+=_[I].dataCount;if(A.getLengthInBits()<=O*8)break}o=y}d(!1,h())},u.createTableTag=function(y,_){y=y||2,_=typeof _>"u"?y*4:_;let A="";A+='<table style="',A+=" border-width: 0px; border-style: none;",A+=" border-collapse: collapse;",A+=" padding: 0px; margin: "+_+"px;",A+='">',A+="<tbody>";for(let O=0;O<u.getModuleCount();O+=1){A+="<tr>";for(let I=0;I<u.getModuleCount();I+=1)A+='<td style="',A+=" border-width: 0px; border-style: none;",A+=" border-collapse: collapse;",A+=" padding: 0px; margin: 0px;",A+=" width: "+y+"px;",A+=" height: "+y+"px;",A+=" background-color: ",A+=u.isDark(O,I)?"#000000":"#ffffff",A+=";",A+='"/>';A+="</tr>"}return A+="</tbody>",A+="</table>",A},u.createSvgTag=function(y,_,A,O){let I={};typeof arguments[0]=="object"&&(I=arguments[0],y=I.cellSize,_=I.margin,A=I.alt,O=I.title),y=y||2,_=typeof _>"u"?y*4:_,A=typeof A=="string"?{text:A}:A||{},A.text=A.text||null,A.id=A.text?A.id||"qrcode-description":null,O=typeof O=="string"?{text:O}:O||{},O.text=O.text||null,O.id=O.text?O.id||"qrcode-title":null;let N=u.getModuleCount()*y+_*2,M,z,U,E,P="",W;for(W="l"+y+",0 0,"+y+" -"+y+",0 0,-"+y+"z ",P+='<svg version="1.1" xmlns="http://www.w3.org/2000/svg"',P+=I.scalable?"":' width="'+N+'px" height="'+N+'px"',P+=' viewBox="0 0 '+N+" "+N+'" ',P+=' preserveAspectRatio="xMinYMin meet"',P+=O.text||A.text?' role="img" aria-labelledby="'+v([O.id,A.id].join(" ").trim())+'"':"",P+=">",P+=O.text?'<title id="'+v(O.id)+'">'+v(O.text)+"</title>":"",P+=A.text?'<description id="'+v(A.id)+'">'+v(A.text)+"</description>":"",P+='<rect width="100%" height="100%" fill="white" cx="0" cy="0"/>',P+='<path d="',U=0;U<u.getModuleCount();U+=1)for(E=U*y+_,M=0;M<u.getModuleCount();M+=1)u.isDark(U,M)&&(z=M*y+_,P+="M"+z+","+E+W);return P+='" stroke="transparent" fill="black"/>',P+="</svg>",P},u.createDataURL=function(y,_){y=y||2,_=typeof _>"u"?y*4:_;let A=u.getModuleCount()*y+_*2,O=_,I=A-_;return Ex(A,A,function(N,M){if(O<=N&&N<I&&O<=M&&M<I){let z=Math.floor((N-O)/y),U=Math.floor((M-O)/y);return u.isDark(U,z)?0:1}else return 1})},u.createImgTag=function(y,_,A){y=y||2,_=typeof _>"u"?y*4:_;let O=u.getModuleCount()*y+_*2,I="";return I+="<img",I+=' src="',I+=u.createDataURL(y,_),I+='"',I+=' width="',I+=O,I+='"',I+=' height="',I+=O,I+='"',A&&(I+=' alt="',I+=v(A),I+='"'),I+="/>",I};let v=function(y){let _="";for(let A=0;A<y.length;A+=1){let O=y.charAt(A);switch(O){case"<":_+="&lt;";break;case">":_+="&gt;";break;case"&":_+="&amp;";break;case'"':_+="&quot;";break;default:_+=O;break}}return _},b=function(y){y=typeof y>"u"?2:y;let A=u.getModuleCount()*1+y*2,O=y,I=A-y,N,M,z,U,E,P={"██":"█","█ ":"▀"," █":"▄"," ":" "},W={"██":"▀","█ ":"▀"," █":" "," ":" "},oe="";for(N=0;N<A;N+=2){for(z=Math.floor((N-O)/1),U=Math.floor((N+1-O)/1),M=0;M<A;M+=1)E="█",O<=M&&M<I&&O<=N&&N<I&&u.isDark(z,Math.floor((M-O)/1))&&(E=" "),O<=M&&M<I&&O<=N+1&&N+1<I&&u.isDark(U,Math.floor((M-O)/1))?E+=" ":E+="█",oe+=y<1&&N+1>=I?W[E]:P[E];oe+=`
229
- `}return A%2&&y>0?oe.substring(0,oe.length-A-1)+Array(A+1).join("▀"):oe.substring(0,oe.length-1)};return u.createASCII=function(y,_){if(y=y||1,y<2)return b(_);y-=1,_=typeof _>"u"?y*2:_;let A=u.getModuleCount()*y+_*2,O=_,I=A-_,N,M,z,U,E=Array(y+1).join("██"),P=Array(y+1).join(" "),W="",oe="";for(N=0;N<A;N+=1){for(z=Math.floor((N-O)/y),oe="",M=0;M<A;M+=1)U=1,O<=M&&M<I&&O<=N&&N<I&&u.isDark(z,Math.floor((M-O)/y))&&(U=0),oe+=U?E:P;for(z=0;z<y;z+=1)W+=oe+`
230
- `}return W.substring(0,W.length-1)},u.renderTo2dContext=function(y,_){_=_||2;let A=u.getModuleCount();for(let O=0;O<A;O++)for(let I=0;I<A;I++)y.fillStyle=u.isDark(O,I)?"black":"white",y.fillRect(I*_,O*_,_,_)},u};sr.stringToBytes=function(n){let e=[];for(let t=0;t<n.length;t+=1){let s=n.charCodeAt(t);e.push(s&255)}return e};sr.createStringToBytes=function(n,e){let t=(function(){let o=Tx(n),r=function(){let l=o.read();if(l==-1)throw"eof";return l},i=0,a={};for(;;){let l=o.read();if(l==-1)break;let c=r(),u=r(),d=r(),f=String.fromCharCode(l<<8|c),h=u<<8|d;a[f]=h,i+=1}if(i!=e)throw i+" != "+e;return a})(),s=63;return function(o){let r=[];for(let i=0;i<o.length;i+=1){let a=o.charCodeAt(i);if(a<128)r.push(a);else{let l=t[o.charAt(i)];typeof l=="number"?(l&255)==l?r.push(l):(r.push(l>>>8),r.push(l&255)):r.push(s)}}return r}};Qe={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8},Li={L:1,M:0,Q:3,H:2},ls={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7},cs=(function(){let n=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],e=1335,t=7973,s=21522,o={},r=function(i){let a=0;for(;i!=0;)a+=1,i>>>=1;return a};return o.getBCHTypeInfo=function(i){let a=i<<10;for(;r(a)-r(e)>=0;)a^=e<<r(a)-r(e);return(i<<10|a)^s},o.getBCHTypeNumber=function(i){let a=i<<12;for(;r(a)-r(t)>=0;)a^=t<<r(a)-r(t);return i<<12|a},o.getPatternPosition=function(i){return n[i-1]},o.getMaskFunction=function(i){switch(i){case ls.PATTERN000:return function(a,l){return(a+l)%2==0};case ls.PATTERN001:return function(a,l){return a%2==0};case ls.PATTERN010:return function(a,l){return l%3==0};case ls.PATTERN011:return function(a,l){return(a+l)%3==0};case ls.PATTERN100:return function(a,l){return(Math.floor(a/2)+Math.floor(l/3))%2==0};case ls.PATTERN101:return function(a,l){return a*l%2+a*l%3==0};case ls.PATTERN110:return function(a,l){return(a*l%2+a*l%3)%2==0};case ls.PATTERN111:return function(a,l){return(a*l%3+(a+l)%2)%2==0};default:throw"bad maskPattern:"+i}},o.getErrorCorrectPolynomial=function(i){let a=Di([1],0);for(let l=0;l<i;l+=1)a=a.multiply(Di([1,us.gexp(l)],0));return a},o.getLengthInBits=function(i,a){if(1<=a&&a<10)switch(i){case Qe.MODE_NUMBER:return 10;case Qe.MODE_ALPHA_NUM:return 9;case Qe.MODE_8BIT_BYTE:return 8;case Qe.MODE_KANJI:return 8;default:throw"mode:"+i}else if(a<27)switch(i){case Qe.MODE_NUMBER:return 12;case Qe.MODE_ALPHA_NUM:return 11;case Qe.MODE_8BIT_BYTE:return 16;case Qe.MODE_KANJI:return 10;default:throw"mode:"+i}else if(a<41)switch(i){case Qe.MODE_NUMBER:return 14;case Qe.MODE_ALPHA_NUM:return 13;case Qe.MODE_8BIT_BYTE:return 16;case Qe.MODE_KANJI:return 12;default:throw"mode:"+i}else throw"type:"+a},o.getLostPoint=function(i){let a=i.getModuleCount(),l=0;for(let d=0;d<a;d+=1)for(let f=0;f<a;f+=1){let h=0,p=i.isDark(d,f);for(let m=-1;m<=1;m+=1)if(!(d+m<0||a<=d+m))for(let g=-1;g<=1;g+=1)f+g<0||a<=f+g||m==0&&g==0||p==i.isDark(d+m,f+g)&&(h+=1);h>5&&(l+=3+h-5)}for(let d=0;d<a-1;d+=1)for(let f=0;f<a-1;f+=1){let h=0;i.isDark(d,f)&&(h+=1),i.isDark(d+1,f)&&(h+=1),i.isDark(d,f+1)&&(h+=1),i.isDark(d+1,f+1)&&(h+=1),(h==0||h==4)&&(l+=3)}for(let d=0;d<a;d+=1)for(let f=0;f<a-6;f+=1)i.isDark(d,f)&&!i.isDark(d,f+1)&&i.isDark(d,f+2)&&i.isDark(d,f+3)&&i.isDark(d,f+4)&&!i.isDark(d,f+5)&&i.isDark(d,f+6)&&(l+=40);for(let d=0;d<a;d+=1)for(let f=0;f<a-6;f+=1)i.isDark(f,d)&&!i.isDark(f+1,d)&&i.isDark(f+2,d)&&i.isDark(f+3,d)&&i.isDark(f+4,d)&&!i.isDark(f+5,d)&&i.isDark(f+6,d)&&(l+=40);let c=0;for(let d=0;d<a;d+=1)for(let f=0;f<a;f+=1)i.isDark(f,d)&&(c+=1);let u=Math.abs(100*c/a/a-50)/5;return l+=u*10,l},o})(),us=(function(){let n=new Array(256),e=new Array(256);for(let s=0;s<8;s+=1)n[s]=1<<s;for(let s=8;s<256;s+=1)n[s]=n[s-4]^n[s-5]^n[s-6]^n[s-8];for(let s=0;s<255;s+=1)e[n[s]]=s;let t={};return t.glog=function(s){if(s<1)throw"glog("+s+")";return e[s]},t.gexp=function(s){for(;s<0;)s+=255;for(;s>=256;)s-=255;return n[s]},t})(),Di=function(n,e){if(typeof n.length>"u")throw n.length+"/"+e;let t=(function(){let o=0;for(;o<n.length&&n[o]==0;)o+=1;let r=new Array(n.length-o+e);for(let i=0;i<n.length-o;i+=1)r[i]=n[i+o];return r})(),s={};return s.getAt=function(o){return t[o]},s.getLength=function(){return t.length},s.multiply=function(o){let r=new Array(s.getLength()+o.getLength()-1);for(let i=0;i<s.getLength();i+=1)for(let a=0;a<o.getLength();a+=1)r[i+a]^=us.gexp(us.glog(s.getAt(i))+us.glog(o.getAt(a)));return Di(r,0)},s.mod=function(o){if(s.getLength()-o.getLength()<0)return s;let r=us.glog(s.getAt(0))-us.glog(o.getAt(0)),i=new Array(s.getLength());for(let a=0;a<s.getLength();a+=1)i[a]=s.getAt(a);for(let a=0;a<o.getLength();a+=1)i[a]^=us.gexp(us.glog(o.getAt(a))+r);return Di(i,0).mod(o)},s},dw=(function(){let n=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12,7,37,13],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],e=function(o,r){let i={};return i.totalCount=o,i.dataCount=r,i},t={},s=function(o,r){switch(r){case Li.L:return n[(o-1)*4+0];case Li.M:return n[(o-1)*4+1];case Li.Q:return n[(o-1)*4+2];case Li.H:return n[(o-1)*4+3];default:return}};return t.getRSBlocks=function(o,r){let i=s(o,r);if(typeof i>"u")throw"bad rs block @ typeNumber:"+o+"/errorCorrectionLevel:"+r;let a=i.length/3,l=[];for(let c=0;c<a;c+=1){let u=i[c*3+0],d=i[c*3+1],f=i[c*3+2];for(let h=0;h<u;h+=1)l.push(e(d,f))}return l},t})(),fw=function(){let n=[],e=0,t={};return t.getBuffer=function(){return n},t.getAt=function(s){let o=Math.floor(s/8);return(n[o]>>>7-s%8&1)==1},t.put=function(s,o){for(let r=0;r<o;r+=1)t.putBit((s>>>o-r-1&1)==1)},t.getLengthInBits=function(){return e},t.putBit=function(s){let o=Math.floor(e/8);n.length<=o&&n.push(0),s&&(n[o]|=128>>>e%8),e+=1},t},kx=function(n){let e=Qe.MODE_NUMBER,t=n,s={};s.getMode=function(){return e},s.getLength=function(i){return t.length},s.write=function(i){let a=t,l=0;for(;l+2<a.length;)i.put(o(a.substring(l,l+3)),10),l+=3;l<a.length&&(a.length-l==1?i.put(o(a.substring(l,l+1)),4):a.length-l==2&&i.put(o(a.substring(l,l+2)),7))};let o=function(i){let a=0;for(let l=0;l<i.length;l+=1)a=a*10+r(i.charAt(l));return a},r=function(i){if("0"<=i&&i<="9")return i.charCodeAt(0)-48;throw"illegal char :"+i};return s},bx=function(n){let e=Qe.MODE_ALPHA_NUM,t=n,s={};s.getMode=function(){return e},s.getLength=function(r){return t.length},s.write=function(r){let i=t,a=0;for(;a+1<i.length;)r.put(o(i.charAt(a))*45+o(i.charAt(a+1)),11),a+=2;a<i.length&&r.put(o(i.charAt(a)),6)};let o=function(r){if("0"<=r&&r<="9")return r.charCodeAt(0)-48;if("A"<=r&&r<="Z")return r.charCodeAt(0)-65+10;switch(r){case" ":return 36;case"$":return 37;case"%":return 38;case"*":return 39;case"+":return 40;case"-":return 41;case".":return 42;case"/":return 43;case":":return 44;default:throw"illegal char :"+r}};return s},vx=function(n){let e=Qe.MODE_8BIT_BYTE,t=n,s=sr.stringToBytes(n),o={};return o.getMode=function(){return e},o.getLength=function(r){return s.length},o.write=function(r){for(let i=0;i<s.length;i+=1)r.put(s[i],8)},o},xx=function(n){let e=Qe.MODE_KANJI,t=n,s=sr.stringToBytes;(function(i,a){let l=s(i);if(l.length!=2||(l[0]<<8|l[1])!=a)throw"sjis not supported."})("友",38726);let o=s(n),r={};return r.getMode=function(){return e},r.getLength=function(i){return~~(o.length/2)},r.write=function(i){let a=o,l=0;for(;l+1<a.length;){let c=(255&a[l])<<8|255&a[l+1];if(33088<=c&&c<=40956)c-=33088;else if(57408<=c&&c<=60351)c-=49472;else throw"illegal char at "+(l+1)+"/"+c;c=(c>>>8&255)*192+(c&255),i.put(c,13),l+=2}if(l<a.length)throw"illegal char at "+(l+1)},r},hw=function(){let n=[],e={};return e.writeByte=function(t){n.push(t&255)},e.writeShort=function(t){e.writeByte(t),e.writeByte(t>>>8)},e.writeBytes=function(t,s,o){s=s||0,o=o||t.length;for(let r=0;r<o;r+=1)e.writeByte(t[r+s])},e.writeString=function(t){for(let s=0;s<t.length;s+=1)e.writeByte(t.charCodeAt(s))},e.toByteArray=function(){return n},e.toString=function(){let t="";t+="[";for(let s=0;s<n.length;s+=1)s>0&&(t+=","),t+=n[s];return t+="]",t},e},Ax=function(){let n=0,e=0,t=0,s="",o={},r=function(a){s+=String.fromCharCode(i(a&63))},i=function(a){if(a<0)throw"n:"+a;if(a<26)return 65+a;if(a<52)return 97+(a-26);if(a<62)return 48+(a-52);if(a==62)return 43;if(a==63)return 47;throw"n:"+a};return o.writeByte=function(a){for(n=n<<8|a&255,e+=8,t+=1;e>=6;)r(n>>>e-6),e-=6},o.flush=function(){if(e>0&&(r(n<<6-e),n=0,e=0),t%3!=0){let a=3-t%3;for(let l=0;l<a;l+=1)s+="="}},o.toString=function(){return s},o},Tx=function(n){let e=n,t=0,s=0,o=0,r={};r.read=function(){for(;o<8;){if(t>=e.length){if(o==0)return-1;throw"unexpected end of file./"+o}let l=e.charAt(t);if(t+=1,l=="=")return o=0,-1;if(l.match(/^\s$/))continue;s=s<<6|i(l.charCodeAt(0)),o+=6}let a=s>>>o-8&255;return o-=8,a};let i=function(a){if(65<=a&&a<=90)return a-65;if(97<=a&&a<=122)return a-97+26;if(48<=a&&a<=57)return a-48+52;if(a==43)return 62;if(a==47)return 63;throw"c:"+a};return r},_x=function(n,e){let t=n,s=e,o=new Array(n*e),r={};r.setPixel=function(c,u,d){o[u*t+c]=d},r.write=function(c){c.writeString("GIF87a"),c.writeShort(t),c.writeShort(s),c.writeByte(128),c.writeByte(0),c.writeByte(0),c.writeByte(0),c.writeByte(0),c.writeByte(0),c.writeByte(255),c.writeByte(255),c.writeByte(255),c.writeString(","),c.writeShort(0),c.writeShort(0),c.writeShort(t),c.writeShort(s),c.writeByte(0);let u=2,d=a(u);c.writeByte(u);let f=0;for(;d.length-f>255;)c.writeByte(255),c.writeBytes(d,f,255),f+=255;c.writeByte(d.length-f),c.writeBytes(d,f,d.length-f),c.writeByte(0),c.writeString(";")};let i=function(c){let u=c,d=0,f=0,h={};return h.write=function(p,m){if(p>>>m)throw"length over";for(;d+m>=8;)u.writeByte(255&(p<<d|f)),m-=8-d,p>>>=8-d,f=0,d=0;f=p<<d|f,d=d+m},h.flush=function(){d>0&&u.writeByte(f)},h},a=function(c){let u=1<<c,d=(1<<c)+1,f=c+1,h=l();for(let k=0;k<u;k+=1)h.add(String.fromCharCode(k));h.add(String.fromCharCode(u)),h.add(String.fromCharCode(d));let p=hw(),m=i(p);m.write(u,f);let g=0,S=String.fromCharCode(o[g]);for(g+=1;g<o.length;){let k=String.fromCharCode(o[g]);g+=1,h.contains(S+k)?S=S+k:(m.write(h.indexOf(S),f),h.size()<4095&&(h.size()==1<<f&&(f+=1),h.add(S+k)),S=k)}return m.write(h.indexOf(S),f),m.write(d,f),m.flush(),p.toByteArray()},l=function(){let c={},u=0,d={};return d.add=function(f){if(d.contains(f))throw"dup key:"+f;c[f]=u,u+=1},d.size=function(){return u},d.indexOf=function(f){return c[f]},d.contains=function(f){return typeof c[f]<"u"},d};return r},Ex=function(n,e,t){let s=_x(n,e);for(let a=0;a<e;a+=1)for(let l=0;l<n;l+=1)s.setPixel(l,a,t(l,a));let o=hw();s.write(o);let r=Ax(),i=o.toByteArray();for(let a=0;a<i.length;a+=1)r.writeByte(i[a]);return r.flush(),"data:image/gif;base64,"+r},pw=sr,lI=sr.stringToBytes});function gw(n,{ecc:e="M"}={}){let t=pw(0,e);t.addData(String(n),"Byte"),t.make();let s=t.getModuleCount(),o=[];for(let r=0;r<s;r++){let i=[];for(let a=0;a<s;a++)i.push(t.isDark(r,a));o.push(i)}return o}function Ox(n,e){let t=n.length+e*2,s=[];for(let o=0;o<t;o++){let r=[];for(let i=0;i<t;i++){let a=o-e,l=i-e;r.push(a>=0&&l>=0&&a<n.length&&l<n.length?n[a][l]:!1)}s.push(r)}return s}function bc(n,{quiet:e=2,color:t=!0,invert:s=!0}={}){let o=Ox(gw(n),e);o.length%2&&o.push(o[0].map(()=>!1));let r=[];for(let i=0;i<o.length;i+=2){let a="";for(let l=0;l<o[i].length;l++){let c=o[i][l],u=o[i+1][l];!t&&s&&(c=!c,u=!u),a+=c&&u?"█":c?"▀":u?"▄":" "}r.push(t?"\x1B[30;107m"+a+"\x1B[0m":a)}return r.join(`
231
- `)}function vc(n,{quiet:e=4,dark:t="#000",light:s="#fff"}={}){let o=gw(n),r=o.length+e*2,i="";for(let a=0;a<o.length;a++)for(let l=0;l<o.length;l++){if(!o[a][l])continue;let c=1;for(;l+c<o.length&&o[a][l+c];)c++;i+="M"+(l+e)+" "+(a+e)+"h"+c+"v1h-"+c+"z",l+=c-1}return'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 '+r+" "+r+'" shape-rendering="crispEdges"><rect width="'+r+'" height="'+r+'" fill="'+s+'"/><path fill="'+t+'" d="'+i+'"/></svg>'}var Ri=J(()=>{mw()});var kw={};Qt(kw,{PROVIDERS:()=>ww,backupPath:()=>If,deleteBackup:()=>ji,detectSyncFolders:()=>or,executeMove:()=>Nf,findBackups:()=>ir,isLeftoffFolder:()=>Rn,joinLine:()=>ar,moveStatusFile:()=>Pi,ownerOf:()=>_f,planMove:()=>rr,readMoveStatus:()=>Zs,sha256:()=>Dn,syncedBy:()=>Sw,tilde:()=>we,walk:()=>xc,writeMoveStatus:()=>Cf});import le from"node:fs";import Ac from"node:os";import H from"node:path";import yw from"node:crypto";function Pi(n=process.env){return n.LEFTOFF_MOVE_STATUS?n.LEFTOFF_MOVE_STATUS:H.join(n.XDG_CONFIG_HOME||H.join(n.HOME||Ac.homedir(),".config"),"checkpoints","move-status.json")}function Zs(n=process.env){try{return JSON.parse(le.readFileSync(Pi(n),"utf8"))}catch{return null}}function Cf(n,e=process.env){let t=Pi(e);le.mkdirSync(H.dirname(t),{recursive:!0});let s=t+"."+process.pid+".tmp";le.writeFileSync(s,JSON.stringify({...n,at:new Date().toISOString()},null,2)+`
232
- `),le.renameSync(s,t)}function Cx(n){let e=[],t=[H.join(n,"Library","Application Support","Syncthing","config.xml"),H.join(n,".config","syncthing","config.xml"),H.join(n,".local","state","syncthing","config.xml")];for(let s of t){let o="";try{o=le.readFileSync(s,"utf8")}catch{continue}for(let r of o.matchAll(/<folder\b[^>]*>/g)){let i=r[0],a=(i.match(/\bpath="([^"]+)"/)||[])[1],l=(i.match(/\blabel="([^"]*)"/)||[])[1]||(i.match(/\bid="([^"]*)"/)||[])[1]||"";if(!a)continue;let c=a.replace(/^~(?=\/|$)/,n).replace(/&amp;/g,"&");e.push({provider:"syncthing",path:H.resolve(c),label:l||H.basename(c),source:s})}}return!e.length&&ds(H.join(n,"Sync"))&&e.push({provider:"syncthing",path:H.join(n,"Sync"),label:"Sync",source:"default folder"}),e}function or({home:n=Of(),current:e=null}={}){let t=[];t.push(...Cx(n));let s=H.join(n,"Library","CloudStorage");try{for(let a of le.readdirSync(s))/^Dropbox/i.test(a)&&t.push({provider:"dropbox",path:H.join(s,a),label:a.replace(/^Dropbox-?/,"Dropbox ").trim()})}catch{}ds(H.join(n,"Dropbox"))&&!t.some(a=>a.provider==="dropbox")&&t.push({provider:"dropbox",path:H.join(n,"Dropbox"),label:"Dropbox"});let o=H.join(n,"Library","Mobile Documents","com~apple~CloudDocs");ds(o)&&t.push({provider:"icloud",path:o,label:"iCloud Drive"});let r=new Set,i=[];for(let a of t){if(r.has(a.path))continue;r.add(a.path);let l=H.join(a.path,"Leftoff");i.push({...a,suggested:l,display:we(l),hasLeftoff:Rn(l),...ww[a.provider]})}return i.sort((a,l)=>(l.recommended?1:0)-(a.recommended?1:0)),{candidates:i,current:e?{path:e,display:we(e),syncedBy:Sw(e,i)}:null}}function Sw(n,e){let t=H.resolve(n);for(let s of e)if(t===s.path||t.startsWith(s.path+H.sep))return{provider:s.provider,label:s.label};return/com~apple~CloudDocs/.test(t)?{provider:"icloud",label:"iCloud Drive"}:/Dropbox/.test(t)?{provider:"dropbox",label:"Dropbox"}:null}function Rn(n){return rt(H.join(n,"config.json"))&&(ds(H.join(n,"sessions"))||rt(H.join(n,"README.md")))}function xc(n){let e=[],t=(s,o)=>{let r=[];try{r=le.readdirSync(s,{withFileTypes:!0})}catch{return}for(let i of r){let a=o?o+"/"+i.name:i.name;i.isSymbolicLink()||(i.isDirectory()?/^lists\/\.lock/.test(a)||t(H.join(s,i.name),a):i.isFile()&&!Ix(a)&&e.push(a))}};return t(n,""),e.sort()}function Dn(n){return yw.createHash("sha256").update(le.readFileSync(n)).digest("hex")}function _f(n){let e;return(e=n.match(/^(?:sessions|digests|\.spool)\/([^/]+)\//))&&e[1]!==""||(e=n.match(/^machines\/([^/]+)\.json$/))||(e=n.match(/^\.[\w-]+\.([^/]+)\.json$/))?e[1]:null}function Nx(n){return n==="config.json"?"config":n==="README.md"?"readme":n.startsWith(".secret/")?"secret":n.startsWith("lists/")?"list":n.startsWith("catchups/")?"catchup":"file"}function rr(n,e,{machine:t,hostname:s=Ac.hostname(),identity:o=null}={}){n=H.resolve(n),e=H.resolve(e);let r=[],i=[],a=[];n===e&&r.push({code:"same_folder",message:"That is already the folder Leftoff uses."}),e.startsWith(n+H.sep)&&r.push({code:"inside_old",message:"The new folder can’t be inside the current one."}),n.startsWith(e+H.sep)&&r.push({code:"contains_old",message:"The new folder can’t contain the current one; pick a subfolder such as "+we(H.join(e,"Leftoff"))+"."}),ds(n)||r.push({code:"no_source",message:"The current folder "+we(n)+" doesn’t exist."});let l=!1;if(rt(e))if(!ds(e))r.push({code:"not_a_folder",message:we(e)+" is a file, not a folder."});else{let f=le.readdirSync(e).filter(h=>!/^\.DS_Store$|^\.stfolder$|^\.stignore$|^Icon\r?$/.test(h));Rn(e)||f.some(h=>["sessions","machines","lists","catchups","digests"].includes(h))?l=!0:f.length&&r.push({code:"not_empty",message:we(e)+" already has other files in it. Pick an empty folder, or a new one such as "+we(H.join(e,"Leftoff"))+"."})}else{let f=H.dirname(e);for(;!rt(f)&&f!==H.dirname(f);)f=H.dirname(f);try{le.accessSync(f,le.constants.W_OK)}catch{r.push({code:"not_writable",message:"Can’t write to "+we(f)+"."})}}if(l&&t){try{let f=JSON.parse(le.readFileSync(H.join(e,"machines",String(t).replace(/[^\w.-]/g,"_")+".json"),"utf8"));(o?f.fingerprint===o.fingerprint:f.hostname===s)||a.push({machine:t,code:"machine_name_taken",message:"The folder already has a machine called “"+t+"” ("+(f.hostname||"another Mac")+", key "+(f.fingerprint||"?")+'). Give this Mac another name first: leftoff setup → Name for this machine, or "machineNames" in config.json.'})}catch{}if(!a.length&&ds(H.join(e,"sessions",t))){let f=new Set(le.readdirSync(H.join(e,"sessions",t))),h=ds(H.join(n,"sessions",t))?new Set(le.readdirSync(H.join(n,"sessions",t))):new Set,p=[...f].filter(m=>!m.startsWith(".")&&!h.has(m));p.length&&a.push({machine:t,code:"machine_name_maybe_taken",message:"The folder already has "+p.length+" session"+(p.length===1?"":"s")+" from a machine called “"+t+"” that this Mac didn’t write. If that’s another Mac, give this one a different name first (leftoff setup). If it really is this Mac, run again with --force."})}}let c=[],u=0,d={total:0,copy:0,same:0,keepTarget:0,conflictCopy:0,renameCopy:0};if(!r.length){for(let f of xc(n)){let h=H.join(n,f),p=H.join(e,f),m=le.statSync(h).size;d.total++;let g="copy",S=null,k=f;if(l&&rt(p))if(Dn(h)===Dn(p))g="same";else{let w=_f(f),x=Nx(f);w&&w===t?(g="copy",S="this machine’s own file: ours is newer"):x==="list"?(g="conflict-copy",k=f.replace(/\.md$/,"")+".sync-conflict-"+Ef(new Date,!0)+"-LEFTOFF.md",S="the list changed on both sides: merged item by item"):x==="catchup"?(g="rename-copy",k=Mx(e,f),S="same minute, different catch-up"):(g="keep-target",S=x==="config"?"the joined folder’s settings win":w?w+"’s own file: the joined folder has its latest":"the joined folder’s copy wins")}(g==="copy"||g==="conflict-copy"||g==="rename-copy")&&(u+=m),d[{copy:"copy",same:"same","keep-target":"keepTarget","conflict-copy":"conflictCopy","rename-copy":"renameCopy"}[g]]++,c.push({rel:f,dest:k,action:g,why:S,size:m})}if(l){let f=new Set;for(let h of xc(e)){let p=_f(h);p&&p!==t&&f.add(p)}f.size&&i.push({code:"merging",message:"Joining a folder that already has "+[...f].sort().join(", ")+"’s data. Nothing of theirs is overwritten."}),rt(H.join(e,"config.json"))&&rt(H.join(n,"config.json"))&&Dn(H.join(e,"config.json"))!==Dn(H.join(n,"config.json"))&&i.push({code:"config",message:"The joined folder has its own settings (config.json); those are kept. This Mac’s name is carried over."})}}return{from:n,to:e,merge:l,ok:!r.length&&!a.length,errors:r,warnings:i,collisions:a,files:c,counts:d,bytes:u,backup:If(n)}}function Ef(n=new Date,e=!1){let t=s=>String(s).padStart(2,"0");return e?n.getFullYear()+t(n.getMonth()+1)+t(n.getDate())+"-"+t(n.getHours())+t(n.getMinutes())+t(n.getSeconds()):n.getFullYear()+"-"+t(n.getMonth()+1)+"-"+t(n.getDate())+"-"+t(n.getHours())+t(n.getMinutes())}function Mx(n,e){let t=H.extname(e),s=e.slice(0,-t.length);for(let o=2;o<100;o++){let r=s+"-"+o+t;if(!rt(H.join(n,r)))return r}return s+"-"+yw.randomBytes(3).toString("hex")+t}function If(n,e=new Date){let t=n+".leftoff-backup-"+Ef(e);for(let s=2;rt(t);s++)t=n+".leftoff-backup-"+Ef(e)+"-"+s;return t}async function Nf(n,{hooks:e={},machine:t,hostname:s=Ac.hostname(),say:o=()=>{},copyFile:r=le.copyFileSync,now:i=new Date}={}){if(!n.ok)return{ok:!1,stage:"plan",errors:[...n.errors,...n.collisions]};let a=[],l=[],c={ok:!1,from:n.from,to:n.to,merge:n.merge,copied:0,verified:0,kept:0,conflicts:0,stragglers:0,backup:null},u=m=>{let g=[],S=m;for(;!rt(S);)g.push(S),S=H.dirname(S);for(let k of g.reverse())le.mkdirSync(k),l.push(k)},d=(m,g)=>{let S=H.join(n.to,g);u(H.dirname(S));let k=rt(S),w=H.join(H.dirname(S),"."+H.basename(S)+".move-"+process.pid+".tmp");r(m,w);try{le.chmodSync(w,le.statSync(m).mode&511)}catch{}return le.renameSync(w,S),k||a.push(S),S},f=()=>{for(let m of a.reverse())try{le.unlinkSync(m)}catch{}for(let m of l.reverse())try{le.rmdirSync(m)}catch{}},h=!1;try{o("Stopping the daemon so nothing writes mid-move"),e.stopWriters&&(await e.stopWriters(),h=!0),u(n.to),o("Copying "+n.counts.total+" files");let m=[];for(let k of n.files){if(k.action==="same"){c.verified++;continue}if(k.action==="keep-target"){c.kept++;continue}let w=d(H.join(n.from,k.rel),k.dest);(k.action==="conflict-copy"||k.action==="rename-copy")&&c.conflicts++,c.copied++,m.push([H.join(n.from,k.rel),w,k.rel])}n.files.find(k=>k.rel==="config.json"&&k.action==="keep-target")&&Lx(n.from,n.to,s),o("Verifying "+m.length+" copies");for(let[k,w,x]of m){if(Dn(k)!==Dn(w))throw Object.assign(new Error("verify failed: "+x+" doesn’t match after copying"),{code:"verify_failed"});c.verified++}if(c.verified+c.kept!==n.counts.total)throw Object.assign(new Error("verify failed: "+(c.verified+c.kept)+" of "+n.counts.total+" files accounted for"),{code:"verify_failed"});let S=new Set(n.files.map(k=>k.rel));for(let k of xc(n.from)){if(S.has(k)||rt(H.join(n.to,k)))continue;let w=d(H.join(n.from,k),k);if(Dn(H.join(n.from,k))!==Dn(w))throw Object.assign(new Error("verify failed: "+k),{code:"verify_failed"});c.stragglers++}}catch(m){if(f(),h&&e.startWriters)try{await e.startWriters(n.from)}catch{}return{...c,ok:!1,stage:"copy",error:m.message,code:m.code||"copy_failed",rolledBack:!0}}let p=If(n.from,i);try{le.renameSync(n.from,p),le.writeFileSync(H.join(p,"MOVED-HERE.md"),`# This is a Leftoff backup
227
+ `;Be.mkdirSync(qe.dirname(o),{recursive:!0}),Be.appendFileSync(o,r+i)}catch(o){this.log("answers.md: "+o.message)}}}scheduleNotify(e){let t=this.cfg.notify||{};if(t.needsYou===!1||!this.isMine(e))return;let s=this.needsItem(e);if(!s||!s.blocking)return;let o=e.id+"|"+(s.ask?s.ask.id:s.kind+s.since);if(this.notified.has(o))return;this.notified.set(o,"pending"),this.notified.size>500&&this.notified.delete(this.notified.keys().next().value);let r=Math.max(0,Number(t.delaySec!=null?t.delaySec:10))*1e3;this.later(r,()=>{let i=this.needsItem(e),a=i&&(i.ask?e.id+"|"+i.ask.id:e.id+"|"+i.kind+i.since);if(!i||a!==o)return;let l=Qy(i,e.title||ge(e)),c=this.notifier({title:"Leftoff",subtitle:l.subtitle,message:l.message,sessionId:e.id,goBin:$e});this.notified.set(o,c&&c.ok?"sent":"failed"),this.emit("needs.notified",{sessionId:e.id,key:o,result:c})})}remoteHealth(){let e=(this.cfg.remoteHosts||[]).map(Bt).filter(Boolean);return{enabled:e.length>0,hosts:e,urls:zl(e),requireToken:this.cfg.remoteRequireToken!==!1}}remoteState(e){return{...this.remoteHealth(),via:e||null,...ql(this.root)}}setRemoteHosts({add:e=[],remove:t=[],requireToken:s}={}){let o=a=>(Array.isArray(a)?a:[a]).map(Bt).filter(Boolean),r=new Set(o(t)),i=(this.cfg.remoteHosts||[]).map(Bt).filter(a=>a&&!r.has(a));for(let a of o(e))i.includes(a)||i.push(a);return this.cfg.remoteHosts=i,typeof s=="boolean"&&(this.cfg.remoteRequireToken=s),go(this.cfg,this.root),this.remoteHealth()}reloadRemote(){let e=Q(this.root);return this.cfg.remoteHosts=e.remoteHosts||[],this.cfg.remoteRequireToken=e.remoteRequireToken,this.remoteHealth()}versionHealth(){let e=this.updateState(),t=this.changelog();return{update:e.checked?{available:!!e.available,remoteVersion:e.remote&&e.remote.version,remoteSha:e.remote&&e.remote.sha&&e.remote.sha.slice(0,7),checkedAt:e.checkedAt,error:e.error||null,incoming:(e.incoming||[]).map(s=>s.version)}:{available:!1,checkedAt:null,enabled:e.enabled},whatsNew:{unseen:t.entries.filter(s=>s.isNew).length,upgradedFrom:t.upgradedFrom,upgradeNotice:t.upgradeNotice}}}modHealthFile(){return qe.join(this.root,".mod-health."+this.machine.replace(/[^\w.-]/g,"_")+".json")}recordModHealth(e){let t=Array.isArray(e&&e.commands)?e.commands.slice(0,10):null;if(!t)throw C(400,"bad_request","body must be { sessionId, commands: [{ key, want, name, ok, error }] }");let s=(o,r=200)=>o==null?null:String(o).slice(0,r);this._modHealth={at:s(e.at,40)||new Date(this.now()).toISOString(),sessionId:s(e.sessionId,80),machine:s(e.machine,60)||this.machine,modVersion:s(e.modVersion,20),commands:t.map(o=>({key:s(o.key,20),want:s(o.want,40),name:s(o.name,40),ok:!!o.ok,error:o.ok?null:s(o.error)}))};try{Be.writeFileSync(this.modHealthFile(),JSON.stringify(this._modHealth,null,2)+`
228
+ `)}catch{}return this.emit("session.updated",{mod:!0}),{ok:!0}}modHealth(){if(this._modHealth===void 0)try{this._modHealth=JSON.parse(Be.readFileSync(this.modHealthFile(),"utf8"))}catch{this._modHealth=null}let e=this._modHealth;return e?{...e,failed:e.commands.filter(t=>!t.ok).map(t=>({want:t.want,error:t.error}))}:null}keyStatus(){let e=this.cfg.summarizer||{},t={};for(let[s,o]of Object.entries(ja)){let r=String(e.provider||"").toLowerCase()===s,i=r&&e.apiKeyEnv||o.env,a={apiKeyEnv:r?e.apiKeyEnv:null,apiKeyFile:r?e.apiKeyFile:null,root:this.root},l=!!_n(s,a);t[s]={found:l,env:i,envSet:!!process.env[i],file:r&&e.apiKeyFile||o.file}}return t}get pulls(){if(!this._pulls){let e=this.cfg.summarizer||{},t=String(e.provider||"").toLowerCase()==="ollama"&&e.baseUrl?e.baseUrl:void 0;this._pulls=new Kl({...t?{baseUrl:t}:{},...this.pullImpl?{pull:this.pullImpl}:{},...this.pullFetch?{fetchImpl:this.pullFetch}:{},onChange:s=>this.emit("model.pull",s)})}return this._pulls}async modelChoices({provider:e,baseUrl:t}={}){let s=this.cfg.summarizer||{},o=String(e||s.provider||"auto").toLowerCase(),r=o===String(s.provider||"auto").toLowerCase();return hy(o,{current:r?s.model:null,baseUrl:t||(r?s.baseUrl:null),pulls:this.pulls.list(),...this.pullFetch?{fetchImpl:this.pullFetch}:{}})}async startOllama(){let e=this.cfg.summarizer||{},t=String(e.provider||"").toLowerCase()==="ollama"&&e.baseUrl||gt,s=await $t({baseUrl:t,...this.pullFetch?{fetchImpl:this.pullFetch}:{}});if(s.running)return{ok:!0,already:!0,models:await this.modelChoices({provider:"ollama"})};let o=Ka({binary:s.binary});if(!o)throw C(409,"no_ollama","Ollama isn’t installed here. Install it from ollama.com first.");let r=cm(o);if(!r.started)throw C(500,"start_failed","Couldn’t start Ollama ("+(r.error||o.shell)+")");return this.log("ollama: started from the board ("+o.shell+")"),{ok:!!(await Io({baseUrl:t,timeoutMs:Number(process.env.CHECKPOINTS_OLLAMA_START_WAIT_MS)||2e4,...this.pullFetch?{fetchImpl:this.pullFetch}:{}})).running,shell:o.shell,models:await this.modelChoices({provider:"ollama"})}}startPull({tag:e,use:t=!1}={}){return this.pulls.start(String(e||""),{use:t,onDone:s=>{if(!s.use)return;let o=this.cfg.summarizer||{};this.updateConfig({summarizer:{...o,provider:"ollama",model:s.tag,apiKeyEnv:null,apiKeyFile:null}}),this.log("summarizer now ollama "+s.tag+" (downloaded from the board)")}})}publicConfig(){return{...this.cfg,summarizerResolved:this.summarizer.mode,folder:this.root,machineResolved:this.machine,keys:this.keyStatus(),modelHints:{ollama:Yr.map(e=>e.tag),"ollama-cloud":Wr.map(e=>e.tag)}}}saveKey(e,t){let s=ja[e];if(!s)throw C(400,"bad_provider","no key needed for "+e);let o=String(t||"").trim();if(!o||o.length>400||/\s/.test(o))throw C(400,"bad_key","that doesn't look like an API key");let r=va(s.file.replace(/^\.secret\//,""),o,this.root),i=this.cfg.summarizer||{};return String(i.provider||"").toLowerCase()===e&&i.apiKeyFile&&i.apiKeyFile!==s.file&&(this.cfg.summarizer={...i,apiKeyFile:s.file},go(this.cfg,this.root)),this.summarizer=new _t(this.cfg,{root:this.root}),{ok:!0,provider:e,file:r,keys:this.keyStatus()}}updateConfig(e,{via:t}={}){let s=["dormantAfterMin","minGapMin","staleAfterSec","longRunCheckpointMin","digest","summarizer","machineNames","updates","notify","remote","summary"];for(let o of Object.keys(e||{}))if(!s.includes(o))throw C(400,"bad_config","cannot change "+o+" here");if(e.remote!==void 0&&t==="remote")throw C(403,"loopback_only","Remote-access settings can only be changed on the machine running Leftoff");if(e.remote!==void 0){if(!e.remote||typeof e.remote!="object")throw C(400,"bad_config","remote is an object");let o={};for(let r of Object.keys(e.remote)){if(r!=="allowAnswers")throw C(400,"bad_config","cannot change remote."+r+" here");o.allowAnswers=e.remote.allowAnswers===!0}e={...e,remote:o},o.allowAnswers!==void 0&&this.log("settings: trust my paired devices "+(o.allowAnswers?"ON":"off"))}if(e.notify!==void 0){if(!e.notify||typeof e.notify!="object")throw C(400,"bad_config","notify is an object");let o={};if(e.notify.needsYou!==void 0&&(o.needsYou=!!e.notify.needsYou),e.notify.delaySec!==void 0){let r=Number(e.notify.delaySec);if(!(r>=0&&r<=600))throw C(400,"bad_config","notify.delaySec is 0–600");o.delaySec=r}e={...e,notify:o}}if(e.summary!==void 0){if(!e.summary||typeof e.summary!="object")throw C(400,"bad_config","summary is an object");let o={};for(let r of Object.keys(e.summary)){if(r!=="length")throw C(400,"bad_config","cannot change summary."+r+" here");if(o.length=wn(e.summary.length,{strict:!0}),!o.length)throw C(400,"bad_config","summary.length is short, informative or long")}e={...e,summary:o}}if(e.summarizer&&e.summarizer.mode&&!e.summarizer.provider&&(e.summarizer.provider=e.summarizer.mode),e.digest&&typeof e.digest=="object"){let o={...e.digest};if(o.graceMin!==void 0){let r=Number(o.graceMin);if(!Number.isFinite(r)||r<0||r>720)throw C(400,"bad_config","digest.graceMin is minutes, 0-720");o.graceMin=r}if(o.time!==void 0&&o.time!==null&&!/^\d{1,2}:\d{2}$/.test(String(o.time)))throw C(400,"bad_config","digest.time is HH:MM, or null for no automatic digest");if(o.days!==void 0){if(!Array.isArray(o.days)||!o.days.length)throw C(400,"bad_config","digest.days needs at least one day");o.days=Ro(o.days)}if(o.skipEmpty!==void 0&&(o.skipEmpty=!!o.skipEmpty),o.timeZone!==void 0){if(o.timeZone===""||o.timeZone===null)o.timeZone=null;else if(!Do(o.timeZone))throw C(400,"bad_config","unknown time zone: "+o.timeZone)}e={...e,digest:o}}for(let o of s)e[o]!==void 0&&(this.cfg[o]=typeof e[o]=="object"&&e[o]&&!Array.isArray(e[o])?{...this.cfg[o],...e[o]}:e[o]);return go(this.cfg,this.root),e.summarizer&&(this.summarizer=new _t(this.cfg,{root:this.root})),e.updates&&!this.stopped&&this.timers.length&&this.scheduleUpdateCheck(),this.publicConfig()}}});var sr,tt,Pi,ds,fs,hs,ji,yw,ww,Ex,Ox,Cx,Nx,Sw,Ix,Mx,Lx,Dx,kw,mN,bw=J(()=>{sr=function(n,e){let o=n,r=Pi[e],i=null,a=0,l=null,c=[],u={},d=function(y,_){a=o*4+17,i=(function(A){let O=new Array(A);for(let N=0;N<A;N+=1){O[N]=new Array(A);for(let I=0;I<A;I+=1)O[N][I]=null}return O})(a),f(0,0),f(a-7,0),f(0,a-7),m(),p(),S(y,_),o>=7&&g(y),l==null&&(l=x(o,r,c)),k(l,_)},f=function(y,_){for(let A=-1;A<=7;A+=1)if(!(y+A<=-1||a<=y+A))for(let O=-1;O<=7;O+=1)_+O<=-1||a<=_+O||(0<=A&&A<=6&&(O==0||O==6)||0<=O&&O<=6&&(A==0||A==6)||2<=A&&A<=4&&2<=O&&O<=4?i[y+A][_+O]=!0:i[y+A][_+O]=!1)},h=function(){let y=0,_=0;for(let A=0;A<8;A+=1){d(!0,A);let O=fs.getLostPoint(u);(A==0||y>O)&&(y=O,_=A)}return _},p=function(){for(let y=8;y<a-8;y+=1)i[y][6]==null&&(i[y][6]=y%2==0);for(let y=8;y<a-8;y+=1)i[6][y]==null&&(i[6][y]=y%2==0)},m=function(){let y=fs.getPatternPosition(o);for(let _=0;_<y.length;_+=1)for(let A=0;A<y.length;A+=1){let O=y[_],N=y[A];if(i[O][N]==null)for(let I=-2;I<=2;I+=1)for(let M=-2;M<=2;M+=1)I==-2||I==2||M==-2||M==2||I==0&&M==0?i[O+I][N+M]=!0:i[O+I][N+M]=!1}},g=function(y){let _=fs.getBCHTypeNumber(o);for(let A=0;A<18;A+=1){let O=!y&&(_>>A&1)==1;i[Math.floor(A/3)][A%3+a-8-3]=O}for(let A=0;A<18;A+=1){let O=!y&&(_>>A&1)==1;i[A%3+a-8-3][Math.floor(A/3)]=O}},S=function(y,_){let A=r<<3|_,O=fs.getBCHTypeInfo(A);for(let N=0;N<15;N+=1){let I=!y&&(O>>N&1)==1;N<6?i[N][8]=I:N<8?i[N+1][8]=I:i[a-15+N][8]=I}for(let N=0;N<15;N+=1){let I=!y&&(O>>N&1)==1;N<8?i[8][a-N-1]=I:N<9?i[8][15-N-1+1]=I:i[8][15-N-1]=I}i[a-8][8]=!y},k=function(y,_){let A=-1,O=a-1,N=7,I=0,M=fs.getMaskFunction(_);for(let Y=a-1;Y>0;Y-=2)for(Y==6&&(Y-=1);;){for(let U=0;U<2;U+=1)if(i[O][Y-U]==null){let E=!1;I<y.length&&(E=(y[I]>>>N&1)==1),M(O,Y-U)&&(E=!E),i[O][Y-U]=E,N-=1,N==-1&&(I+=1,N=7)}if(O+=A,O<0||a<=O){O-=A,A=-A;break}}},w=function(y,_){let A=0,O=0,N=0,I=new Array(_.length),M=new Array(_.length);for(let P=0;P<_.length;P+=1){let W=_[P].dataCount,oe=_[P].totalCount-W;O=Math.max(O,W),N=Math.max(N,oe),I[P]=new Array(W);for(let Se=0;Se<I[P].length;Se+=1)I[P][Se]=255&y.getBuffer()[Se+A];A+=W;let K=fs.getErrorCorrectPolynomial(oe),Ie=ji(I[P],K.getLength()-1).mod(K);M[P]=new Array(K.getLength()-1);for(let Se=0;Se<M[P].length;Se+=1){let q=Se+Ie.getLength()-M[P].length;M[P][Se]=q>=0?Ie.getAt(q):0}}let Y=0;for(let P=0;P<_.length;P+=1)Y+=_[P].totalCount;let U=new Array(Y),E=0;for(let P=0;P<O;P+=1)for(let W=0;W<_.length;W+=1)P<I[W].length&&(U[E]=I[W][P],E+=1);for(let P=0;P<N;P+=1)for(let W=0;W<_.length;W+=1)P<M[W].length&&(U[E]=M[W][P],E+=1);return U},x=function(y,_,A){let O=yw.getRSBlocks(y,_),N=ww();for(let M=0;M<A.length;M+=1){let Y=A[M];N.put(Y.getMode(),4),N.put(Y.getLength(),fs.getLengthInBits(Y.getMode(),y)),Y.write(N)}let I=0;for(let M=0;M<O.length;M+=1)I+=O[M].dataCount;if(N.getLengthInBits()>I*8)throw"code length overflow. ("+N.getLengthInBits()+">"+I*8+")";for(N.getLengthInBits()+4<=I*8&&N.put(0,4);N.getLengthInBits()%8!=0;)N.putBit(!1);for(;!(N.getLengthInBits()>=I*8||(N.put(236,8),N.getLengthInBits()>=I*8));)N.put(17,8);return w(N,O)};u.addData=function(y,_){_=_||"Byte";let A=null;switch(_){case"Numeric":A=Ex(y);break;case"Alphanumeric":A=Ox(y);break;case"Byte":A=Cx(y);break;case"Kanji":A=Nx(y);break;default:throw"mode:"+_}c.push(A),l=null},u.isDark=function(y,_){if(y<0||a<=y||_<0||a<=_)throw y+","+_;return i[y][_]},u.getModuleCount=function(){return a},u.make=function(){if(o<1){let y=1;for(;y<40;y++){let _=yw.getRSBlocks(y,r),A=ww();for(let N=0;N<c.length;N++){let I=c[N];A.put(I.getMode(),4),A.put(I.getLength(),fs.getLengthInBits(I.getMode(),y)),I.write(A)}let O=0;for(let N=0;N<_.length;N++)O+=_[N].dataCount;if(A.getLengthInBits()<=O*8)break}o=y}d(!1,h())},u.createTableTag=function(y,_){y=y||2,_=typeof _>"u"?y*4:_;let A="";A+='<table style="',A+=" border-width: 0px; border-style: none;",A+=" border-collapse: collapse;",A+=" padding: 0px; margin: "+_+"px;",A+='">',A+="<tbody>";for(let O=0;O<u.getModuleCount();O+=1){A+="<tr>";for(let N=0;N<u.getModuleCount();N+=1)A+='<td style="',A+=" border-width: 0px; border-style: none;",A+=" border-collapse: collapse;",A+=" padding: 0px; margin: 0px;",A+=" width: "+y+"px;",A+=" height: "+y+"px;",A+=" background-color: ",A+=u.isDark(O,N)?"#000000":"#ffffff",A+=";",A+='"/>';A+="</tr>"}return A+="</tbody>",A+="</table>",A},u.createSvgTag=function(y,_,A,O){let N={};typeof arguments[0]=="object"&&(N=arguments[0],y=N.cellSize,_=N.margin,A=N.alt,O=N.title),y=y||2,_=typeof _>"u"?y*4:_,A=typeof A=="string"?{text:A}:A||{},A.text=A.text||null,A.id=A.text?A.id||"qrcode-description":null,O=typeof O=="string"?{text:O}:O||{},O.text=O.text||null,O.id=O.text?O.id||"qrcode-title":null;let I=u.getModuleCount()*y+_*2,M,Y,U,E,P="",W;for(W="l"+y+",0 0,"+y+" -"+y+",0 0,-"+y+"z ",P+='<svg version="1.1" xmlns="http://www.w3.org/2000/svg"',P+=N.scalable?"":' width="'+I+'px" height="'+I+'px"',P+=' viewBox="0 0 '+I+" "+I+'" ',P+=' preserveAspectRatio="xMinYMin meet"',P+=O.text||A.text?' role="img" aria-labelledby="'+v([O.id,A.id].join(" ").trim())+'"':"",P+=">",P+=O.text?'<title id="'+v(O.id)+'">'+v(O.text)+"</title>":"",P+=A.text?'<description id="'+v(A.id)+'">'+v(A.text)+"</description>":"",P+='<rect width="100%" height="100%" fill="white" cx="0" cy="0"/>',P+='<path d="',U=0;U<u.getModuleCount();U+=1)for(E=U*y+_,M=0;M<u.getModuleCount();M+=1)u.isDark(U,M)&&(Y=M*y+_,P+="M"+Y+","+E+W);return P+='" stroke="transparent" fill="black"/>',P+="</svg>",P},u.createDataURL=function(y,_){y=y||2,_=typeof _>"u"?y*4:_;let A=u.getModuleCount()*y+_*2,O=_,N=A-_;return Dx(A,A,function(I,M){if(O<=I&&I<N&&O<=M&&M<N){let Y=Math.floor((I-O)/y),U=Math.floor((M-O)/y);return u.isDark(U,Y)?0:1}else return 1})},u.createImgTag=function(y,_,A){y=y||2,_=typeof _>"u"?y*4:_;let O=u.getModuleCount()*y+_*2,N="";return N+="<img",N+=' src="',N+=u.createDataURL(y,_),N+='"',N+=' width="',N+=O,N+='"',N+=' height="',N+=O,N+='"',A&&(N+=' alt="',N+=v(A),N+='"'),N+="/>",N};let v=function(y){let _="";for(let A=0;A<y.length;A+=1){let O=y.charAt(A);switch(O){case"<":_+="&lt;";break;case">":_+="&gt;";break;case"&":_+="&amp;";break;case'"':_+="&quot;";break;default:_+=O;break}}return _},b=function(y){y=typeof y>"u"?2:y;let A=u.getModuleCount()*1+y*2,O=y,N=A-y,I,M,Y,U,E,P={"██":"█","█ ":"▀"," █":"▄"," ":" "},W={"██":"▀","█ ":"▀"," █":" "," ":" "},oe="";for(I=0;I<A;I+=2){for(Y=Math.floor((I-O)/1),U=Math.floor((I+1-O)/1),M=0;M<A;M+=1)E="█",O<=M&&M<N&&O<=I&&I<N&&u.isDark(Y,Math.floor((M-O)/1))&&(E=" "),O<=M&&M<N&&O<=I+1&&I+1<N&&u.isDark(U,Math.floor((M-O)/1))?E+=" ":E+="█",oe+=y<1&&I+1>=N?W[E]:P[E];oe+=`
229
+ `}return A%2&&y>0?oe.substring(0,oe.length-A-1)+Array(A+1).join("▀"):oe.substring(0,oe.length-1)};return u.createASCII=function(y,_){if(y=y||1,y<2)return b(_);y-=1,_=typeof _>"u"?y*2:_;let A=u.getModuleCount()*y+_*2,O=_,N=A-_,I,M,Y,U,E=Array(y+1).join("██"),P=Array(y+1).join(" "),W="",oe="";for(I=0;I<A;I+=1){for(Y=Math.floor((I-O)/y),oe="",M=0;M<A;M+=1)U=1,O<=M&&M<N&&O<=I&&I<N&&u.isDark(Y,Math.floor((M-O)/y))&&(U=0),oe+=U?E:P;for(Y=0;Y<y;Y+=1)W+=oe+`
230
+ `}return W.substring(0,W.length-1)},u.renderTo2dContext=function(y,_){_=_||2;let A=u.getModuleCount();for(let O=0;O<A;O++)for(let N=0;N<A;N++)y.fillStyle=u.isDark(O,N)?"black":"white",y.fillRect(N*_,O*_,_,_)},u};sr.stringToBytes=function(n){let e=[];for(let t=0;t<n.length;t+=1){let s=n.charCodeAt(t);e.push(s&255)}return e};sr.createStringToBytes=function(n,e){let t=(function(){let o=Mx(n),r=function(){let l=o.read();if(l==-1)throw"eof";return l},i=0,a={};for(;;){let l=o.read();if(l==-1)break;let c=r(),u=r(),d=r(),f=String.fromCharCode(l<<8|c),h=u<<8|d;a[f]=h,i+=1}if(i!=e)throw i+" != "+e;return a})(),s=63;return function(o){let r=[];for(let i=0;i<o.length;i+=1){let a=o.charCodeAt(i);if(a<128)r.push(a);else{let l=t[o.charAt(i)];typeof l=="number"?(l&255)==l?r.push(l):(r.push(l>>>8),r.push(l&255)):r.push(s)}}return r}};tt={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8},Pi={L:1,M:0,Q:3,H:2},ds={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7},fs=(function(){let n=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],e=1335,t=7973,s=21522,o={},r=function(i){let a=0;for(;i!=0;)a+=1,i>>>=1;return a};return o.getBCHTypeInfo=function(i){let a=i<<10;for(;r(a)-r(e)>=0;)a^=e<<r(a)-r(e);return(i<<10|a)^s},o.getBCHTypeNumber=function(i){let a=i<<12;for(;r(a)-r(t)>=0;)a^=t<<r(a)-r(t);return i<<12|a},o.getPatternPosition=function(i){return n[i-1]},o.getMaskFunction=function(i){switch(i){case ds.PATTERN000:return function(a,l){return(a+l)%2==0};case ds.PATTERN001:return function(a,l){return a%2==0};case ds.PATTERN010:return function(a,l){return l%3==0};case ds.PATTERN011:return function(a,l){return(a+l)%3==0};case ds.PATTERN100:return function(a,l){return(Math.floor(a/2)+Math.floor(l/3))%2==0};case ds.PATTERN101:return function(a,l){return a*l%2+a*l%3==0};case ds.PATTERN110:return function(a,l){return(a*l%2+a*l%3)%2==0};case ds.PATTERN111:return function(a,l){return(a*l%3+(a+l)%2)%2==0};default:throw"bad maskPattern:"+i}},o.getErrorCorrectPolynomial=function(i){let a=ji([1],0);for(let l=0;l<i;l+=1)a=a.multiply(ji([1,hs.gexp(l)],0));return a},o.getLengthInBits=function(i,a){if(1<=a&&a<10)switch(i){case tt.MODE_NUMBER:return 10;case tt.MODE_ALPHA_NUM:return 9;case tt.MODE_8BIT_BYTE:return 8;case tt.MODE_KANJI:return 8;default:throw"mode:"+i}else if(a<27)switch(i){case tt.MODE_NUMBER:return 12;case tt.MODE_ALPHA_NUM:return 11;case tt.MODE_8BIT_BYTE:return 16;case tt.MODE_KANJI:return 10;default:throw"mode:"+i}else if(a<41)switch(i){case tt.MODE_NUMBER:return 14;case tt.MODE_ALPHA_NUM:return 13;case tt.MODE_8BIT_BYTE:return 16;case tt.MODE_KANJI:return 12;default:throw"mode:"+i}else throw"type:"+a},o.getLostPoint=function(i){let a=i.getModuleCount(),l=0;for(let d=0;d<a;d+=1)for(let f=0;f<a;f+=1){let h=0,p=i.isDark(d,f);for(let m=-1;m<=1;m+=1)if(!(d+m<0||a<=d+m))for(let g=-1;g<=1;g+=1)f+g<0||a<=f+g||m==0&&g==0||p==i.isDark(d+m,f+g)&&(h+=1);h>5&&(l+=3+h-5)}for(let d=0;d<a-1;d+=1)for(let f=0;f<a-1;f+=1){let h=0;i.isDark(d,f)&&(h+=1),i.isDark(d+1,f)&&(h+=1),i.isDark(d,f+1)&&(h+=1),i.isDark(d+1,f+1)&&(h+=1),(h==0||h==4)&&(l+=3)}for(let d=0;d<a;d+=1)for(let f=0;f<a-6;f+=1)i.isDark(d,f)&&!i.isDark(d,f+1)&&i.isDark(d,f+2)&&i.isDark(d,f+3)&&i.isDark(d,f+4)&&!i.isDark(d,f+5)&&i.isDark(d,f+6)&&(l+=40);for(let d=0;d<a;d+=1)for(let f=0;f<a-6;f+=1)i.isDark(f,d)&&!i.isDark(f+1,d)&&i.isDark(f+2,d)&&i.isDark(f+3,d)&&i.isDark(f+4,d)&&!i.isDark(f+5,d)&&i.isDark(f+6,d)&&(l+=40);let c=0;for(let d=0;d<a;d+=1)for(let f=0;f<a;f+=1)i.isDark(f,d)&&(c+=1);let u=Math.abs(100*c/a/a-50)/5;return l+=u*10,l},o})(),hs=(function(){let n=new Array(256),e=new Array(256);for(let s=0;s<8;s+=1)n[s]=1<<s;for(let s=8;s<256;s+=1)n[s]=n[s-4]^n[s-5]^n[s-6]^n[s-8];for(let s=0;s<255;s+=1)e[n[s]]=s;let t={};return t.glog=function(s){if(s<1)throw"glog("+s+")";return e[s]},t.gexp=function(s){for(;s<0;)s+=255;for(;s>=256;)s-=255;return n[s]},t})(),ji=function(n,e){if(typeof n.length>"u")throw n.length+"/"+e;let t=(function(){let o=0;for(;o<n.length&&n[o]==0;)o+=1;let r=new Array(n.length-o+e);for(let i=0;i<n.length-o;i+=1)r[i]=n[i+o];return r})(),s={};return s.getAt=function(o){return t[o]},s.getLength=function(){return t.length},s.multiply=function(o){let r=new Array(s.getLength()+o.getLength()-1);for(let i=0;i<s.getLength();i+=1)for(let a=0;a<o.getLength();a+=1)r[i+a]^=hs.gexp(hs.glog(s.getAt(i))+hs.glog(o.getAt(a)));return ji(r,0)},s.mod=function(o){if(s.getLength()-o.getLength()<0)return s;let r=hs.glog(s.getAt(0))-hs.glog(o.getAt(0)),i=new Array(s.getLength());for(let a=0;a<s.getLength();a+=1)i[a]=s.getAt(a);for(let a=0;a<o.getLength();a+=1)i[a]^=hs.gexp(hs.glog(o.getAt(a))+r);return ji(i,0).mod(o)},s},yw=(function(){let n=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12,7,37,13],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],e=function(o,r){let i={};return i.totalCount=o,i.dataCount=r,i},t={},s=function(o,r){switch(r){case Pi.L:return n[(o-1)*4+0];case Pi.M:return n[(o-1)*4+1];case Pi.Q:return n[(o-1)*4+2];case Pi.H:return n[(o-1)*4+3];default:return}};return t.getRSBlocks=function(o,r){let i=s(o,r);if(typeof i>"u")throw"bad rs block @ typeNumber:"+o+"/errorCorrectionLevel:"+r;let a=i.length/3,l=[];for(let c=0;c<a;c+=1){let u=i[c*3+0],d=i[c*3+1],f=i[c*3+2];for(let h=0;h<u;h+=1)l.push(e(d,f))}return l},t})(),ww=function(){let n=[],e=0,t={};return t.getBuffer=function(){return n},t.getAt=function(s){let o=Math.floor(s/8);return(n[o]>>>7-s%8&1)==1},t.put=function(s,o){for(let r=0;r<o;r+=1)t.putBit((s>>>o-r-1&1)==1)},t.getLengthInBits=function(){return e},t.putBit=function(s){let o=Math.floor(e/8);n.length<=o&&n.push(0),s&&(n[o]|=128>>>e%8),e+=1},t},Ex=function(n){let e=tt.MODE_NUMBER,t=n,s={};s.getMode=function(){return e},s.getLength=function(i){return t.length},s.write=function(i){let a=t,l=0;for(;l+2<a.length;)i.put(o(a.substring(l,l+3)),10),l+=3;l<a.length&&(a.length-l==1?i.put(o(a.substring(l,l+1)),4):a.length-l==2&&i.put(o(a.substring(l,l+2)),7))};let o=function(i){let a=0;for(let l=0;l<i.length;l+=1)a=a*10+r(i.charAt(l));return a},r=function(i){if("0"<=i&&i<="9")return i.charCodeAt(0)-48;throw"illegal char :"+i};return s},Ox=function(n){let e=tt.MODE_ALPHA_NUM,t=n,s={};s.getMode=function(){return e},s.getLength=function(r){return t.length},s.write=function(r){let i=t,a=0;for(;a+1<i.length;)r.put(o(i.charAt(a))*45+o(i.charAt(a+1)),11),a+=2;a<i.length&&r.put(o(i.charAt(a)),6)};let o=function(r){if("0"<=r&&r<="9")return r.charCodeAt(0)-48;if("A"<=r&&r<="Z")return r.charCodeAt(0)-65+10;switch(r){case" ":return 36;case"$":return 37;case"%":return 38;case"*":return 39;case"+":return 40;case"-":return 41;case".":return 42;case"/":return 43;case":":return 44;default:throw"illegal char :"+r}};return s},Cx=function(n){let e=tt.MODE_8BIT_BYTE,t=n,s=sr.stringToBytes(n),o={};return o.getMode=function(){return e},o.getLength=function(r){return s.length},o.write=function(r){for(let i=0;i<s.length;i+=1)r.put(s[i],8)},o},Nx=function(n){let e=tt.MODE_KANJI,t=n,s=sr.stringToBytes;(function(i,a){let l=s(i);if(l.length!=2||(l[0]<<8|l[1])!=a)throw"sjis not supported."})("友",38726);let o=s(n),r={};return r.getMode=function(){return e},r.getLength=function(i){return~~(o.length/2)},r.write=function(i){let a=o,l=0;for(;l+1<a.length;){let c=(255&a[l])<<8|255&a[l+1];if(33088<=c&&c<=40956)c-=33088;else if(57408<=c&&c<=60351)c-=49472;else throw"illegal char at "+(l+1)+"/"+c;c=(c>>>8&255)*192+(c&255),i.put(c,13),l+=2}if(l<a.length)throw"illegal char at "+(l+1)},r},Sw=function(){let n=[],e={};return e.writeByte=function(t){n.push(t&255)},e.writeShort=function(t){e.writeByte(t),e.writeByte(t>>>8)},e.writeBytes=function(t,s,o){s=s||0,o=o||t.length;for(let r=0;r<o;r+=1)e.writeByte(t[r+s])},e.writeString=function(t){for(let s=0;s<t.length;s+=1)e.writeByte(t.charCodeAt(s))},e.toByteArray=function(){return n},e.toString=function(){let t="";t+="[";for(let s=0;s<n.length;s+=1)s>0&&(t+=","),t+=n[s];return t+="]",t},e},Ix=function(){let n=0,e=0,t=0,s="",o={},r=function(a){s+=String.fromCharCode(i(a&63))},i=function(a){if(a<0)throw"n:"+a;if(a<26)return 65+a;if(a<52)return 97+(a-26);if(a<62)return 48+(a-52);if(a==62)return 43;if(a==63)return 47;throw"n:"+a};return o.writeByte=function(a){for(n=n<<8|a&255,e+=8,t+=1;e>=6;)r(n>>>e-6),e-=6},o.flush=function(){if(e>0&&(r(n<<6-e),n=0,e=0),t%3!=0){let a=3-t%3;for(let l=0;l<a;l+=1)s+="="}},o.toString=function(){return s},o},Mx=function(n){let e=n,t=0,s=0,o=0,r={};r.read=function(){for(;o<8;){if(t>=e.length){if(o==0)return-1;throw"unexpected end of file./"+o}let l=e.charAt(t);if(t+=1,l=="=")return o=0,-1;if(l.match(/^\s$/))continue;s=s<<6|i(l.charCodeAt(0)),o+=6}let a=s>>>o-8&255;return o-=8,a};let i=function(a){if(65<=a&&a<=90)return a-65;if(97<=a&&a<=122)return a-97+26;if(48<=a&&a<=57)return a-48+52;if(a==43)return 62;if(a==47)return 63;throw"c:"+a};return r},Lx=function(n,e){let t=n,s=e,o=new Array(n*e),r={};r.setPixel=function(c,u,d){o[u*t+c]=d},r.write=function(c){c.writeString("GIF87a"),c.writeShort(t),c.writeShort(s),c.writeByte(128),c.writeByte(0),c.writeByte(0),c.writeByte(0),c.writeByte(0),c.writeByte(0),c.writeByte(255),c.writeByte(255),c.writeByte(255),c.writeString(","),c.writeShort(0),c.writeShort(0),c.writeShort(t),c.writeShort(s),c.writeByte(0);let u=2,d=a(u);c.writeByte(u);let f=0;for(;d.length-f>255;)c.writeByte(255),c.writeBytes(d,f,255),f+=255;c.writeByte(d.length-f),c.writeBytes(d,f,d.length-f),c.writeByte(0),c.writeString(";")};let i=function(c){let u=c,d=0,f=0,h={};return h.write=function(p,m){if(p>>>m)throw"length over";for(;d+m>=8;)u.writeByte(255&(p<<d|f)),m-=8-d,p>>>=8-d,f=0,d=0;f=p<<d|f,d=d+m},h.flush=function(){d>0&&u.writeByte(f)},h},a=function(c){let u=1<<c,d=(1<<c)+1,f=c+1,h=l();for(let k=0;k<u;k+=1)h.add(String.fromCharCode(k));h.add(String.fromCharCode(u)),h.add(String.fromCharCode(d));let p=Sw(),m=i(p);m.write(u,f);let g=0,S=String.fromCharCode(o[g]);for(g+=1;g<o.length;){let k=String.fromCharCode(o[g]);g+=1,h.contains(S+k)?S=S+k:(m.write(h.indexOf(S),f),h.size()<4095&&(h.size()==1<<f&&(f+=1),h.add(S+k)),S=k)}return m.write(h.indexOf(S),f),m.write(d,f),m.flush(),p.toByteArray()},l=function(){let c={},u=0,d={};return d.add=function(f){if(d.contains(f))throw"dup key:"+f;c[f]=u,u+=1},d.size=function(){return u},d.indexOf=function(f){return c[f]},d.contains=function(f){return typeof c[f]<"u"},d};return r},Dx=function(n,e,t){let s=Lx(n,e);for(let a=0;a<e;a+=1)for(let l=0;l<n;l+=1)s.setPixel(l,a,t(l,a));let o=Sw();s.write(o);let r=Ix(),i=o.toByteArray();for(let a=0;a<i.length;a+=1)r.writeByte(i[a]);return r.flush(),"data:image/gif;base64,"+r},kw=sr,mN=sr.stringToBytes});function vw(n,{ecc:e="M"}={}){let t=kw(0,e);t.addData(String(n),"Byte"),t.make();let s=t.getModuleCount(),o=[];for(let r=0;r<s;r++){let i=[];for(let a=0;a<s;a++)i.push(t.isDark(r,a));o.push(i)}return o}function Rx(n,e){let t=n.length+e*2,s=[];for(let o=0;o<t;o++){let r=[];for(let i=0;i<t;i++){let a=o-e,l=i-e;r.push(a>=0&&l>=0&&a<n.length&&l<n.length?n[a][l]:!1)}s.push(r)}return s}function xc(n,{quiet:e=2,color:t=!0,invert:s=!0}={}){let o=Rx(vw(n),e);o.length%2&&o.push(o[0].map(()=>!1));let r=[];for(let i=0;i<o.length;i+=2){let a="";for(let l=0;l<o[i].length;l++){let c=o[i][l],u=o[i+1][l];!t&&s&&(c=!c,u=!u),a+=c&&u?"█":c?"▀":u?"▄":" "}r.push(t?"\x1B[30;107m"+a+"\x1B[0m":a)}return r.join(`
231
+ `)}function Ac(n,{quiet:e=4,dark:t="#000",light:s="#fff"}={}){let o=vw(n),r=o.length+e*2,i="";for(let a=0;a<o.length;a++)for(let l=0;l<o.length;l++){if(!o[a][l])continue;let c=1;for(;l+c<o.length&&o[a][l+c];)c++;i+="M"+(l+e)+" "+(a+e)+"h"+c+"v1h-"+c+"z",l+=c-1}return'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 '+r+" "+r+'" shape-rendering="crispEdges"><rect width="'+r+'" height="'+r+'" fill="'+s+'"/><path fill="'+t+'" d="'+i+'"/></svg>'}var Fi=J(()=>{bw()});var _w={};tn(_w,{PROVIDERS:()=>Aw,backupPath:()=>Df,deleteBackup:()=>Hi,detectSyncFolders:()=>or,executeMove:()=>Rf,findBackups:()=>ir,isLeftoffFolder:()=>jn,joinLine:()=>ar,moveStatusFile:()=>Ui,ownerOf:()=>Nf,planMove:()=>rr,readMoveStatus:()=>eo,sha256:()=>Pn,syncedBy:()=>Tw,tilde:()=>we,walk:()=>Tc,writeMoveStatus:()=>Lf});import le from"node:fs";import _c from"node:os";import H from"node:path";import xw from"node:crypto";function Ui(n=process.env){return n.LEFTOFF_MOVE_STATUS?n.LEFTOFF_MOVE_STATUS:H.join(n.XDG_CONFIG_HOME||H.join(n.HOME||_c.homedir(),".config"),"checkpoints","move-status.json")}function eo(n=process.env){try{return JSON.parse(le.readFileSync(Ui(n),"utf8"))}catch{return null}}function Lf(n,e=process.env){let t=Ui(e);le.mkdirSync(H.dirname(t),{recursive:!0});let s=t+"."+process.pid+".tmp";le.writeFileSync(s,JSON.stringify({...n,at:new Date().toISOString()},null,2)+`
232
+ `),le.renameSync(s,t)}function Px(n){let e=[],t=[H.join(n,"Library","Application Support","Syncthing","config.xml"),H.join(n,".config","syncthing","config.xml"),H.join(n,".local","state","syncthing","config.xml")];for(let s of t){let o="";try{o=le.readFileSync(s,"utf8")}catch{continue}for(let r of o.matchAll(/<folder\b[^>]*>/g)){let i=r[0],a=(i.match(/\bpath="([^"]+)"/)||[])[1],l=(i.match(/\blabel="([^"]*)"/)||[])[1]||(i.match(/\bid="([^"]*)"/)||[])[1]||"";if(!a)continue;let c=a.replace(/^~(?=\/|$)/,n).replace(/&amp;/g,"&");e.push({provider:"syncthing",path:H.resolve(c),label:l||H.basename(c),source:s})}}return!e.length&&ps(H.join(n,"Sync"))&&e.push({provider:"syncthing",path:H.join(n,"Sync"),label:"Sync",source:"default folder"}),e}function or({home:n=Mf(),current:e=null}={}){let t=[];t.push(...Px(n));let s=H.join(n,"Library","CloudStorage");try{for(let a of le.readdirSync(s))/^Dropbox/i.test(a)&&t.push({provider:"dropbox",path:H.join(s,a),label:a.replace(/^Dropbox-?/,"Dropbox ").trim()})}catch{}ps(H.join(n,"Dropbox"))&&!t.some(a=>a.provider==="dropbox")&&t.push({provider:"dropbox",path:H.join(n,"Dropbox"),label:"Dropbox"});let o=H.join(n,"Library","Mobile Documents","com~apple~CloudDocs");ps(o)&&t.push({provider:"icloud",path:o,label:"iCloud Drive"});let r=new Set,i=[];for(let a of t){if(r.has(a.path))continue;r.add(a.path);let l=H.join(a.path,"Leftoff");i.push({...a,suggested:l,display:we(l),hasLeftoff:jn(l),...Aw[a.provider]})}return i.sort((a,l)=>(l.recommended?1:0)-(a.recommended?1:0)),{candidates:i,current:e?{path:e,display:we(e),syncedBy:Tw(e,i)}:null}}function Tw(n,e){let t=H.resolve(n);for(let s of e)if(t===s.path||t.startsWith(s.path+H.sep))return{provider:s.provider,label:s.label};return/com~apple~CloudDocs/.test(t)?{provider:"icloud",label:"iCloud Drive"}:/Dropbox/.test(t)?{provider:"dropbox",label:"Dropbox"}:null}function jn(n){return at(H.join(n,"config.json"))&&(ps(H.join(n,"sessions"))||at(H.join(n,"README.md")))}function Tc(n){let e=[],t=(s,o)=>{let r=[];try{r=le.readdirSync(s,{withFileTypes:!0})}catch{return}for(let i of r){let a=o?o+"/"+i.name:i.name;i.isSymbolicLink()||(i.isDirectory()?/^lists\/\.lock/.test(a)||t(H.join(s,i.name),a):i.isFile()&&!jx(a)&&e.push(a))}};return t(n,""),e.sort()}function Pn(n){return xw.createHash("sha256").update(le.readFileSync(n)).digest("hex")}function Nf(n){let e;return(e=n.match(/^(?:sessions|digests|\.spool)\/([^/]+)\//))&&e[1]!==""||(e=n.match(/^machines\/([^/]+)\.json$/))||(e=n.match(/^\.[\w-]+\.([^/]+)\.json$/))?e[1]:null}function Fx(n){return n==="config.json"?"config":n==="README.md"?"readme":n.startsWith(".secret/")?"secret":n.startsWith("lists/")?"list":n.startsWith("catchups/")?"catchup":"file"}function rr(n,e,{machine:t,hostname:s=_c.hostname(),identity:o=null}={}){n=H.resolve(n),e=H.resolve(e);let r=[],i=[],a=[];n===e&&r.push({code:"same_folder",message:"That is already the folder Leftoff uses."}),e.startsWith(n+H.sep)&&r.push({code:"inside_old",message:"The new folder can’t be inside the current one."}),n.startsWith(e+H.sep)&&r.push({code:"contains_old",message:"The new folder can’t contain the current one; pick a subfolder such as "+we(H.join(e,"Leftoff"))+"."}),ps(n)||r.push({code:"no_source",message:"The current folder "+we(n)+" doesn’t exist."});let l=!1;if(at(e))if(!ps(e))r.push({code:"not_a_folder",message:we(e)+" is a file, not a folder."});else{let f=le.readdirSync(e).filter(h=>!/^\.DS_Store$|^\.stfolder$|^\.stignore$|^Icon\r?$/.test(h));jn(e)||f.some(h=>["sessions","machines","lists","catchups","digests"].includes(h))?l=!0:f.length&&r.push({code:"not_empty",message:we(e)+" already has other files in it. Pick an empty folder, or a new one such as "+we(H.join(e,"Leftoff"))+"."})}else{let f=H.dirname(e);for(;!at(f)&&f!==H.dirname(f);)f=H.dirname(f);try{le.accessSync(f,le.constants.W_OK)}catch{r.push({code:"not_writable",message:"Can’t write to "+we(f)+"."})}}if(l&&t){try{let f=JSON.parse(le.readFileSync(H.join(e,"machines",String(t).replace(/[^\w.-]/g,"_")+".json"),"utf8"));(o?f.fingerprint===o.fingerprint:f.hostname===s)||a.push({machine:t,code:"machine_name_taken",message:"The folder already has a machine called “"+t+"” ("+(f.hostname||"another Mac")+", key "+(f.fingerprint||"?")+'). Give this Mac another name first: leftoff setup → Name for this machine, or "machineNames" in config.json.'})}catch{}if(!a.length&&ps(H.join(e,"sessions",t))){let f=new Set(le.readdirSync(H.join(e,"sessions",t))),h=ps(H.join(n,"sessions",t))?new Set(le.readdirSync(H.join(n,"sessions",t))):new Set,p=[...f].filter(m=>!m.startsWith(".")&&!h.has(m));p.length&&a.push({machine:t,code:"machine_name_maybe_taken",message:"The folder already has "+p.length+" session"+(p.length===1?"":"s")+" from a machine called “"+t+"” that this Mac didn’t write. If that’s another Mac, give this one a different name first (leftoff setup). If it really is this Mac, run again with --force."})}}let c=[],u=0,d={total:0,copy:0,same:0,keepTarget:0,conflictCopy:0,renameCopy:0};if(!r.length){for(let f of Tc(n)){let h=H.join(n,f),p=H.join(e,f),m=le.statSync(h).size;d.total++;let g="copy",S=null,k=f;if(l&&at(p))if(Pn(h)===Pn(p))g="same";else{let w=Nf(f),x=Fx(f);w&&w===t?(g="copy",S="this machine’s own file: ours is newer"):x==="list"?(g="conflict-copy",k=f.replace(/\.md$/,"")+".sync-conflict-"+If(new Date,!0)+"-LEFTOFF.md",S="the list changed on both sides: merged item by item"):x==="catchup"?(g="rename-copy",k=Ux(e,f),S="same minute, different catch-up"):(g="keep-target",S=x==="config"?"the joined folder’s settings win":w?w+"’s own file: the joined folder has its latest":"the joined folder’s copy wins")}(g==="copy"||g==="conflict-copy"||g==="rename-copy")&&(u+=m),d[{copy:"copy",same:"same","keep-target":"keepTarget","conflict-copy":"conflictCopy","rename-copy":"renameCopy"}[g]]++,c.push({rel:f,dest:k,action:g,why:S,size:m})}if(l){let f=new Set;for(let h of Tc(e)){let p=Nf(h);p&&p!==t&&f.add(p)}f.size&&i.push({code:"merging",message:"Joining a folder that already has "+[...f].sort().join(", ")+"’s data. Nothing of theirs is overwritten."}),at(H.join(e,"config.json"))&&at(H.join(n,"config.json"))&&Pn(H.join(e,"config.json"))!==Pn(H.join(n,"config.json"))&&i.push({code:"config",message:"The joined folder has its own settings (config.json); those are kept. This Mac’s name is carried over."})}}return{from:n,to:e,merge:l,ok:!r.length&&!a.length,errors:r,warnings:i,collisions:a,files:c,counts:d,bytes:u,backup:Df(n)}}function If(n=new Date,e=!1){let t=s=>String(s).padStart(2,"0");return e?n.getFullYear()+t(n.getMonth()+1)+t(n.getDate())+"-"+t(n.getHours())+t(n.getMinutes())+t(n.getSeconds()):n.getFullYear()+"-"+t(n.getMonth()+1)+"-"+t(n.getDate())+"-"+t(n.getHours())+t(n.getMinutes())}function Ux(n,e){let t=H.extname(e),s=e.slice(0,-t.length);for(let o=2;o<100;o++){let r=s+"-"+o+t;if(!at(H.join(n,r)))return r}return s+"-"+xw.randomBytes(3).toString("hex")+t}function Df(n,e=new Date){let t=n+".leftoff-backup-"+If(e);for(let s=2;at(t);s++)t=n+".leftoff-backup-"+If(e)+"-"+s;return t}async function Rf(n,{hooks:e={},machine:t,hostname:s=_c.hostname(),say:o=()=>{},copyFile:r=le.copyFileSync,now:i=new Date}={}){if(!n.ok)return{ok:!1,stage:"plan",errors:[...n.errors,...n.collisions]};let a=[],l=[],c={ok:!1,from:n.from,to:n.to,merge:n.merge,copied:0,verified:0,kept:0,conflicts:0,stragglers:0,backup:null},u=m=>{let g=[],S=m;for(;!at(S);)g.push(S),S=H.dirname(S);for(let k of g.reverse())le.mkdirSync(k),l.push(k)},d=(m,g)=>{let S=H.join(n.to,g);u(H.dirname(S));let k=at(S),w=H.join(H.dirname(S),"."+H.basename(S)+".move-"+process.pid+".tmp");r(m,w);try{le.chmodSync(w,le.statSync(m).mode&511)}catch{}return le.renameSync(w,S),k||a.push(S),S},f=()=>{for(let m of a.reverse())try{le.unlinkSync(m)}catch{}for(let m of l.reverse())try{le.rmdirSync(m)}catch{}},h=!1;try{o("Stopping the daemon so nothing writes mid-move"),e.stopWriters&&(await e.stopWriters(),h=!0),u(n.to),o("Copying "+n.counts.total+" files");let m=[];for(let k of n.files){if(k.action==="same"){c.verified++;continue}if(k.action==="keep-target"){c.kept++;continue}let w=d(H.join(n.from,k.rel),k.dest);(k.action==="conflict-copy"||k.action==="rename-copy")&&c.conflicts++,c.copied++,m.push([H.join(n.from,k.rel),w,k.rel])}n.files.find(k=>k.rel==="config.json"&&k.action==="keep-target")&&Hx(n.from,n.to,s),o("Verifying "+m.length+" copies");for(let[k,w,x]of m){if(Pn(k)!==Pn(w))throw Object.assign(new Error("verify failed: "+x+" doesn’t match after copying"),{code:"verify_failed"});c.verified++}if(c.verified+c.kept!==n.counts.total)throw Object.assign(new Error("verify failed: "+(c.verified+c.kept)+" of "+n.counts.total+" files accounted for"),{code:"verify_failed"});let S=new Set(n.files.map(k=>k.rel));for(let k of Tc(n.from)){if(S.has(k)||at(H.join(n.to,k)))continue;let w=d(H.join(n.from,k),k);if(Pn(H.join(n.from,k))!==Pn(w))throw Object.assign(new Error("verify failed: "+k),{code:"verify_failed"});c.stragglers++}}catch(m){if(f(),h&&e.startWriters)try{await e.startWriters(n.from)}catch{}return{...c,ok:!1,stage:"copy",error:m.message,code:m.code||"copy_failed",rolledBack:!0}}let p=Df(n.from,i);try{le.renameSync(n.from,p),le.writeFileSync(H.join(p,"MOVED-HERE.md"),`# This is a Leftoff backup
233
233
 
234
234
  On `+i.toISOString()+` Leftoff moved this folder to:
235
235
 
@@ -237,21 +237,21 @@ On `+i.toISOString()+` Leftoff moved this folder to:
237
237
 
238
238
  Every file was copied and checked (sha-256). This copy is kept until you delete it:
239
239
  Settings → Sync on the board, or \`leftoff move --delete-backup\`. Leftoff never deletes it on its own.
240
- `),c.backup=p,e.switchTo&&await e.switchTo(n.to)}catch(m){try{!rt(n.from)&&rt(p)&&le.renameSync(p,n.from)}catch{}try{le.unlinkSync(H.join(n.from,"MOVED-HERE.md"))}catch{}if(e.switchTo)try{await e.switchTo(n.from)}catch{}if(f(),e.startWriters)try{await e.startWriters(n.from)}catch{}return{...c,ok:!1,stage:"switch",error:m.message,code:"switch_failed",rolledBack:!0,backup:null}}if(e.startWriters){let m=await e.startWriters(n.to).catch(g=>({ok:!1,error:g.message}));if(!m||m.ok===!1){try{e.stopWriters&&await e.stopWriters()}catch{}try{le.unlinkSync(H.join(p,"MOVED-HERE.md"))}catch{}try{le.renameSync(p,n.from)}catch{}if(e.switchTo)try{await e.switchTo(n.from)}catch{}f();try{await e.startWriters(n.from)}catch{}return{...c,ok:!1,stage:"start",error:"the daemon didn’t start on the new folder"+(m&&m.error?": "+m.error:""),code:"start_failed",rolledBack:!0,backup:null}}}return{...c,ok:!0}}function Lx(n,e,t){try{let s=JSON.parse(le.readFileSync(H.join(n,"config.json"),"utf8")),o=JSON.parse(le.readFileSync(H.join(e,"config.json"),"utf8")),r=(s.machineNames||{})[t]||(s.machine&&!(o.machineNames||{})[t]?s.machine:null);if(!r||(o.machineNames||{})[t]===r)return;o.machineNames={...o.machineNames||{},[t]:r};let i=H.join(e,".config.json.move.tmp");le.writeFileSync(i,JSON.stringify(o,null,2)+`
241
- `),le.renameSync(i,H.join(e,"config.json"))}catch{}}function ir(n,{extra:e=[]}={}){let t=[],s=new Set,o=new Set([H.dirname(n),Of()]),r=Zs(),i=[];for(let a of[...e,r&&r.backup])a&&i.push(H.resolve(a));for(let a of o){let l=[];try{l=le.readdirSync(a)}catch{continue}for(let c of l)i.push(H.join(a,c))}for(let a of i){if(s.has(a)||(s.add(a),!/\.leftoff-backup-\d{4}-\d{2}-\d{2}-\d{4}(-\d+)?$/.test(H.basename(a))))continue;let l="";try{l=le.readFileSync(H.join(a,"MOVED-HERE.md"),"utf8")}catch{continue}let c=(l.match(/^ {4}(.+)$/m)||[])[1]||null;c&&H.resolve(c)!==H.resolve(n)||t.push({path:a,display:we(a),movedTo:c,at:(l.match(/^On (\S+)/m)||[])[1]||null})}return t}function ji(n){let e=H.resolve(n);if(!/\.leftoff-backup-/.test(H.basename(e))||!rt(H.join(e,"MOVED-HERE.md")))throw Object.assign(new Error("not a Leftoff backup folder: "+n),{code:"not_a_backup"});return le.rmSync(e,{recursive:!0,force:!0}),{deleted:e}}function ar(n,{cli:e="leftoff"}={}){let t=we(n),s=/\s/.test(t)?'"'+t.replace(/^~\//,"$HOME/")+'"':t;return{installed:e+" move "+s,fresh:"npx -y github:"+st+" setup --dir "+s}}var Of,rt,ds,we,ww,Ix,Qs=J(()=>{_t();Of=()=>process.env.HOME||Ac.homedir(),rt=n=>{try{return le.statSync(n),!0}catch{return!1}},ds=n=>{try{return le.statSync(n).isDirectory()}catch{return!1}},we=n=>{let e=Of();return n===e?"~":n.startsWith(e+H.sep)?"~"+n.slice(e.length):n};ww={syncthing:{label:"Syncthing",recommended:!0,tradeoff:"Peer to peer, fast, and it never offloads files. Both Macs need Syncthing running and the folder shared between them."},dropbox:{label:"Dropbox",tradeoff:"Works well. Turn off “online-only” for this folder (right-click → Make available offline) so files stay on disk."},icloud:{label:"iCloud Drive",tradeoff:"Works, but syncs slower and macOS may offload files to the cloud. Right-click the folder → “Keep Downloaded”."},other:{label:"Another folder",tradeoff:"Any folder works if something else syncs it (Google Drive, a NAS). Leftoff only needs plain files."}};Ix=n=>/(^|\/)\.[^/]*\.tmp$|\.tmp(-\d+)?$|\.lock$|(^|\/)\.lock(\/|$)|^\.daemon\.pid$|(^|\/)\.DS_Store$|^MOVED-HERE\.md$/.test(n)});import Dx from"node:readline";function fs(n=process.stdout){return process.env.NO_COLOR?!1:process.env.FORCE_COLOR&&process.env.FORCE_COLOR!=="0"?!0:!!(n&&n.isTTY)}function hs(n){let e={};for(let[t,[s,o]]of Object.entries(Rx))e[t]=r=>n?`\x1B[${s}m${r}\x1B[${o}m`:String(r);return e}function Ht(n){if(n==null||isNaN(n))return"?";let e=["B","KB","MB","GB","TB"],t=0,s=Number(n);for(;s>=1e3&&t<e.length-1;)s/=1e3,t++;return(s>=100||t===0?Math.round(s):s.toFixed(1))+" "+e[t]}function Ui(n){if(n<1e3)return Math.round(n)+" ms";let e=n/1e3;return e<60?e.toFixed(1)+" s":Math.floor(e/60)+"m "+String(Math.round(e%60)).padStart(2,"0")+"s"}function Px(n,e=24){let t=Math.max(0,Math.min(1,Number(n)||0)),s=Math.floor(t*e),o=["","▏","▎","▍","▌","▋","▊","▉"][Math.floor((t*e-s)*8)]||"";return"█".repeat(s)+o+" ".repeat(Math.max(0,e-s-(o?1:0)))}function it(n,{out:e=process.stdout,colors:t=ee}={}){let s=!!e.isTTY,o=0,r=n,i=null,a=()=>e.write("\r\x1B[2K"+t.cyan(bw[o++%bw.length])+" "+r);s&&(a(),i=setInterval(a,80),i.unref&&i.unref());let l=(c,u)=>{i&&clearInterval(i),i=null,e.write((s?"\r\x1B[2K":"")+c+" "+(u??r)+`
242
- `)};return{update(c){r=c,s&&!i&&a()},succeed(c){l(t.green("✔"),c)},fail(c){l(t.red("✖"),c)},warn(c){l(t.yellow("!"),c)},info(c){l(t.cyan("ℹ"),c)},stop(){i&&clearInterval(i),i=null,s&&e.write("\r\x1B[2K")},get active(){return!!i}}}function Tc(n,{out:e=process.stdout,colors:t=ee,width:s=26}={}){let o=!!e.isTTY,r=-10,i=0;return{update(a,l=""){let c=Math.floor((a||0)*100);if(o){let u=Date.now();if(u-i<60&&c<100)return;i=u;let f=Px(a,s).trimEnd();e.write("\r\x1B[2K "+t.cyan(f)+t.dim("░".repeat(s-f.length))+" "+String(c).padStart(3)+"% "+t.dim(l))}else c>=r+10&&(r=c-c%10,e.write(" "+n+" "+String(c).padStart(3)+"% "+l+`
240
+ `),c.backup=p,e.switchTo&&await e.switchTo(n.to)}catch(m){try{!at(n.from)&&at(p)&&le.renameSync(p,n.from)}catch{}try{le.unlinkSync(H.join(n.from,"MOVED-HERE.md"))}catch{}if(e.switchTo)try{await e.switchTo(n.from)}catch{}if(f(),e.startWriters)try{await e.startWriters(n.from)}catch{}return{...c,ok:!1,stage:"switch",error:m.message,code:"switch_failed",rolledBack:!0,backup:null}}if(e.startWriters){let m=await e.startWriters(n.to).catch(g=>({ok:!1,error:g.message}));if(!m||m.ok===!1){try{e.stopWriters&&await e.stopWriters()}catch{}try{le.unlinkSync(H.join(p,"MOVED-HERE.md"))}catch{}try{le.renameSync(p,n.from)}catch{}if(e.switchTo)try{await e.switchTo(n.from)}catch{}f();try{await e.startWriters(n.from)}catch{}return{...c,ok:!1,stage:"start",error:"the daemon didn’t start on the new folder"+(m&&m.error?": "+m.error:""),code:"start_failed",rolledBack:!0,backup:null}}}return{...c,ok:!0}}function Hx(n,e,t){try{let s=JSON.parse(le.readFileSync(H.join(n,"config.json"),"utf8")),o=JSON.parse(le.readFileSync(H.join(e,"config.json"),"utf8")),r=(s.machineNames||{})[t]||(s.machine&&!(o.machineNames||{})[t]?s.machine:null);if(!r||(o.machineNames||{})[t]===r)return;o.machineNames={...o.machineNames||{},[t]:r};let i=H.join(e,".config.json.move.tmp");le.writeFileSync(i,JSON.stringify(o,null,2)+`
241
+ `),le.renameSync(i,H.join(e,"config.json"))}catch{}}function ir(n,{extra:e=[]}={}){let t=[],s=new Set,o=new Set([H.dirname(n),Mf()]),r=eo(),i=[];for(let a of[...e,r&&r.backup])a&&i.push(H.resolve(a));for(let a of o){let l=[];try{l=le.readdirSync(a)}catch{continue}for(let c of l)i.push(H.join(a,c))}for(let a of i){if(s.has(a)||(s.add(a),!/\.leftoff-backup-\d{4}-\d{2}-\d{2}-\d{4}(-\d+)?$/.test(H.basename(a))))continue;let l="";try{l=le.readFileSync(H.join(a,"MOVED-HERE.md"),"utf8")}catch{continue}let c=(l.match(/^ {4}(.+)$/m)||[])[1]||null;c&&H.resolve(c)!==H.resolve(n)||t.push({path:a,display:we(a),movedTo:c,at:(l.match(/^On (\S+)/m)||[])[1]||null})}return t}function Hi(n){let e=H.resolve(n);if(!/\.leftoff-backup-/.test(H.basename(e))||!at(H.join(e,"MOVED-HERE.md")))throw Object.assign(new Error("not a Leftoff backup folder: "+n),{code:"not_a_backup"});return le.rmSync(e,{recursive:!0,force:!0}),{deleted:e}}function ar(n,{cli:e="leftoff"}={}){let t=we(n),s=/\s/.test(t)?'"'+t.replace(/^~\//,"$HOME/")+'"':t;return{installed:e+" move "+s,fresh:"npx -y github:"+rt+" setup --dir "+s}}var Mf,at,ps,we,Aw,jx,to=J(()=>{Ot();Mf=()=>process.env.HOME||_c.homedir(),at=n=>{try{return le.statSync(n),!0}catch{return!1}},ps=n=>{try{return le.statSync(n).isDirectory()}catch{return!1}},we=n=>{let e=Mf();return n===e?"~":n.startsWith(e+H.sep)?"~"+n.slice(e.length):n};Aw={syncthing:{label:"Syncthing",recommended:!0,tradeoff:"Peer to peer, fast, and it never offloads files. Both Macs need Syncthing running and the folder shared between them."},dropbox:{label:"Dropbox",tradeoff:"Works well. Turn off “online-only” for this folder (right-click → Make available offline) so files stay on disk."},icloud:{label:"iCloud Drive",tradeoff:"Works, but syncs slower and macOS may offload files to the cloud. Right-click the folder → “Keep Downloaded”."},other:{label:"Another folder",tradeoff:"Any folder works if something else syncs it (Google Drive, a NAS). Leftoff only needs plain files."}};jx=n=>/(^|\/)\.[^/]*\.tmp$|\.tmp(-\d+)?$|\.lock$|(^|\/)\.lock(\/|$)|^\.daemon\.pid$|(^|\/)\.DS_Store$|^MOVED-HERE\.md$/.test(n)});import Bx from"node:readline";function ms(n=process.stdout){return process.env.NO_COLOR?!1:process.env.FORCE_COLOR&&process.env.FORCE_COLOR!=="0"?!0:!!(n&&n.isTTY)}function gs(n){let e={};for(let[t,[s,o]]of Object.entries(Gx))e[t]=r=>n?`\x1B[${s}m${r}\x1B[${o}m`:String(r);return e}function Gt(n){if(n==null||isNaN(n))return"?";let e=["B","KB","MB","GB","TB"],t=0,s=Number(n);for(;s>=1e3&&t<e.length-1;)s/=1e3,t++;return(s>=100||t===0?Math.round(s):s.toFixed(1))+" "+e[t]}function Gi(n){if(n<1e3)return Math.round(n)+" ms";let e=n/1e3;return e<60?e.toFixed(1)+" s":Math.floor(e/60)+"m "+String(Math.round(e%60)).padStart(2,"0")+"s"}function Kx(n,e=24){let t=Math.max(0,Math.min(1,Number(n)||0)),s=Math.floor(t*e),o=["","▏","▎","▍","▌","▋","▊","▉"][Math.floor((t*e-s)*8)]||"";return"█".repeat(s)+o+" ".repeat(Math.max(0,e-s-(o?1:0)))}function lt(n,{out:e=process.stdout,colors:t=ee}={}){let s=!!e.isTTY,o=0,r=n,i=null,a=()=>e.write("\r\x1B[2K"+t.cyan(Ew[o++%Ew.length])+" "+r);s&&(a(),i=setInterval(a,80),i.unref&&i.unref());let l=(c,u)=>{i&&clearInterval(i),i=null,e.write((s?"\r\x1B[2K":"")+c+" "+(u??r)+`
242
+ `)};return{update(c){r=c,s&&!i&&a()},succeed(c){l(t.green("✔"),c)},fail(c){l(t.red("✖"),c)},warn(c){l(t.yellow("!"),c)},info(c){l(t.cyan("ℹ"),c)},stop(){i&&clearInterval(i),i=null,s&&e.write("\r\x1B[2K")},get active(){return!!i}}}function Ec(n,{out:e=process.stdout,colors:t=ee,width:s=26}={}){let o=!!e.isTTY,r=-10,i=0;return{update(a,l=""){let c=Math.floor((a||0)*100);if(o){let u=Date.now();if(u-i<60&&c<100)return;i=u;let f=Kx(a,s).trimEnd();e.write("\r\x1B[2K "+t.cyan(f)+t.dim("░".repeat(s-f.length))+" "+String(c).padStart(3)+"% "+t.dim(l))}else c>=r+10&&(r=c-c%10,e.write(" "+n+" "+String(c).padStart(3)+"% "+l+`
243
243
  `))},done(a){e.write((o?"\r\x1B[2K":"")+t.green("✔")+" "+a+`
244
244
  `)},fail(a){e.write((o?"\r\x1B[2K":"")+t.red("✖")+" "+a+`
245
- `)}}}function vw(n,e,{input:t=process.stdin,output:s=process.stdout,colors:o=ee}={}){return new Promise(r=>{let i=0,a="",l=0,c=()=>{let h=a.toLowerCase();return h?e.filter(p=>(p.search||p.label+" "+(p.hint||"")).toLowerCase().includes(h)):e},u=()=>{let h=c();i>=h.length&&(i=Math.max(0,h.length-1)),l&&s.write("\x1B["+l+"A\x1B[0J");let p=[o.cyan("?")+" "+o.bold(n)+" "+o.dim(a?"filter: "+a:"↑↓ move · enter go · type to filter · esc quit")];h.slice(0,15).forEach((m,g)=>{let S=g===i;p.push((S?o.cyan(" › "):" ")+o.dim((g<9?String(g+1):" ")+" ")+(S?o.bold(m.label):m.label)+(m.hint?" "+o.dim(m.hint):"")),m.detail&&p.push(" "+o.dim(m.detail))}),h.length||p.push(o.dim(" nothing matches")),s.write(p.join(`
245
+ `)}}}function Ow(n,e,{input:t=process.stdin,output:s=process.stdout,colors:o=ee}={}){return new Promise(r=>{let i=0,a="",l=0,c=()=>{let h=a.toLowerCase();return h?e.filter(p=>(p.search||p.label+" "+(p.hint||"")).toLowerCase().includes(h)):e},u=()=>{let h=c();i>=h.length&&(i=Math.max(0,h.length-1)),l&&s.write("\x1B["+l+"A\x1B[0J");let p=[o.cyan("?")+" "+o.bold(n)+" "+o.dim(a?"filter: "+a:"↑↓ move · enter go · type to filter · esc quit")];h.slice(0,15).forEach((m,g)=>{let S=g===i;p.push((S?o.cyan(" › "):" ")+o.dim((g<9?String(g+1):" ")+" ")+(S?o.bold(m.label):m.label)+(m.hint?" "+o.dim(m.hint):"")),m.detail&&p.push(" "+o.dim(m.detail))}),h.length||p.push(o.dim(" nothing matches")),s.write(p.join(`
246
246
  `)+`
247
247
  `),l=p.length},d=h=>{t.removeListener("data",f),t.setRawMode&&t.setRawMode(!1),t.pause(),s.write("\x1B[?25h"),r(h)},f=h=>{let p=String(h),m=c();if(p===""||p==="\x1B"||p==="q"&&!a)return d(null);if(p==="\r"||p===`
248
- `)return d(m[i]?m[i].value:null);if(p==="\x1B[A"||p==="k"&&!a)i=Math.max(0,i-1);else if(p==="\x1B[B"||p==="j"&&!a)i=Math.min(m.length-1,i+1);else{if(/^[1-9]$/.test(p)&&!a&&m[Number(p)-1])return d(m[Number(p)-1].value);p===""||p==="\b"?a=a.slice(0,-1):/^[\x20-\x7e]$/.test(p)&&(a+=p,i=0)}u()};t.setRawMode&&t.setRawMode(!0),t.resume(),t.on("data",f),s.write("\x1B[?25l"),u()})}function xw(n,e,{input:t=process.stdin,output:s=process.stdout,colors:o=ee}={}){return new Promise(r=>{let i=0,a=0,l=e.map(f=>f.checked!==!1),c=()=>{a&&s.write("\x1B["+a+"A\x1B[0J");let f=l.filter(Boolean).length,h=[o.cyan("?")+" "+o.bold(n)+" "+o.dim("space toggle · a all/none · enter restore "+f+" · esc quit")],p=Math.max(0,Math.min(i-7,e.length-15));e.slice(p,p+15).forEach((m,g)=>{let S=p+g,k=S===i;h.push((k?o.cyan(" › "):" ")+(l[S]?o.green("[x]"):o.dim("[ ]"))+" "+(k?o.bold(m.label):m.label)+(m.hint?" "+o.dim(m.hint):""))}),e.length>15&&h.push(o.dim(" … "+e.length+" in all")),s.write(h.join(`
248
+ `)return d(m[i]?m[i].value:null);if(p==="\x1B[A"||p==="k"&&!a)i=Math.max(0,i-1);else if(p==="\x1B[B"||p==="j"&&!a)i=Math.min(m.length-1,i+1);else{if(/^[1-9]$/.test(p)&&!a&&m[Number(p)-1])return d(m[Number(p)-1].value);p===""||p==="\b"?a=a.slice(0,-1):/^[\x20-\x7e]$/.test(p)&&(a+=p,i=0)}u()};t.setRawMode&&t.setRawMode(!0),t.resume(),t.on("data",f),s.write("\x1B[?25l"),u()})}function Cw(n,e,{input:t=process.stdin,output:s=process.stdout,colors:o=ee}={}){return new Promise(r=>{let i=0,a=0,l=e.map(f=>f.checked!==!1),c=()=>{a&&s.write("\x1B["+a+"A\x1B[0J");let f=l.filter(Boolean).length,h=[o.cyan("?")+" "+o.bold(n)+" "+o.dim("space toggle · a all/none · enter restore "+f+" · esc quit")],p=Math.max(0,Math.min(i-7,e.length-15));e.slice(p,p+15).forEach((m,g)=>{let S=p+g,k=S===i;h.push((k?o.cyan(" › "):" ")+(l[S]?o.green("[x]"):o.dim("[ ]"))+" "+(k?o.bold(m.label):m.label)+(m.hint?" "+o.dim(m.hint):""))}),e.length>15&&h.push(o.dim(" … "+e.length+" in all")),s.write(h.join(`
249
249
  `)+`
250
250
  `),a=h.length},u=f=>{t.removeListener("data",d),t.setRawMode&&t.setRawMode(!1),t.pause(),s.write("\x1B[?25h"),r(f)},d=f=>{let h=String(f);if(h===""||h==="\x1B"||h==="q")return u(null);if(h==="\r"||h===`
251
- `)return u(e.filter((p,m)=>l[m]).map(p=>p.value));if(h==="\x1B[A"||h==="k")i=Math.max(0,i-1);else if(h==="\x1B[B"||h==="j")i=Math.min(e.length-1,i+1);else if(h===" ")l[i]=!l[i];else if(h==="a"){let p=l.every(Boolean);l.fill(!p)}c()};t.setRawMode&&t.setRawMode(!0),t.resume(),t.on("data",d),s.write("\x1B[?25l"),c()})}var Fi,Rx,ee,bw,wt,Pn=J(()=>{Fi=class extends Error{constructor(e="cancelled"){super(e),this.cancelled=!0}};Rx={bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],gray:[90,39]};ee=hs(fs());bw=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];wt=class{constructor({input:e=process.stdin,output:t=process.stdout,yes:s=!1,colors:o=ee}={}){this.input=e,this.out=t,this.yes=s,this.c=o,this.queue=[],this.waiter=null,this.closed=!1,this.muted=!1,this.rl=null,this.interruptHandlers=new Set}async interruptible(e){let t=new AbortController,s=()=>t.abort();this.interruptHandlers.add(s);let o=()=>s();this.rl||process.once("SIGINT",o);try{return await e(t.signal)}catch(r){throw t.signal.aborted?new Fi("cancelled (Ctrl-C)"):r}finally{this.interruptHandlers.delete(s),process.removeListener("SIGINT",o)}}_open(){if(this.rl||this.yes)return;this.rl=Dx.createInterface({input:this.input,output:this.out,terminal:!!(this.input.isTTY&&this.out.isTTY)});let e=this.rl._writeToOutput&&this.rl._writeToOutput.bind(this.rl);e&&(this.rl._writeToOutput=t=>{this.muted?/\r|\n/.test(t)&&e(`
251
+ `)return u(e.filter((p,m)=>l[m]).map(p=>p.value));if(h==="\x1B[A"||h==="k")i=Math.max(0,i-1);else if(h==="\x1B[B"||h==="j")i=Math.min(e.length-1,i+1);else if(h===" ")l[i]=!l[i];else if(h==="a"){let p=l.every(Boolean);l.fill(!p)}c()};t.setRawMode&&t.setRawMode(!0),t.resume(),t.on("data",d),s.write("\x1B[?25l"),c()})}var Bi,Gx,ee,Ew,kt,Fn=J(()=>{Bi=class extends Error{constructor(e="cancelled"){super(e),this.cancelled=!0}};Gx={bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],gray:[90,39]};ee=gs(ms());Ew=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];kt=class{constructor({input:e=process.stdin,output:t=process.stdout,yes:s=!1,colors:o=ee}={}){this.input=e,this.out=t,this.yes=s,this.c=o,this.queue=[],this.waiter=null,this.closed=!1,this.muted=!1,this.rl=null,this.interruptHandlers=new Set}async interruptible(e){let t=new AbortController,s=()=>t.abort();this.interruptHandlers.add(s);let o=()=>s();this.rl||process.once("SIGINT",o);try{return await e(t.signal)}catch(r){throw t.signal.aborted?new Bi("cancelled (Ctrl-C)"):r}finally{this.interruptHandlers.delete(s),process.removeListener("SIGINT",o)}}_open(){if(this.rl||this.yes)return;this.rl=Bx.createInterface({input:this.input,output:this.out,terminal:!!(this.input.isTTY&&this.out.isTTY)});let e=this.rl._writeToOutput&&this.rl._writeToOutput.bind(this.rl);e&&(this.rl._writeToOutput=t=>{this.muted?/\r|\n/.test(t)&&e(`
252
252
  `):e(t)}),this.rl.on("line",t=>{if(this.waiter){let s=this.waiter;this.waiter=null,this.rl.terminal||this.out.write((this.muted?"":t)+`
253
- `),s.resolve(t)}else this.queue.push(t)}),this.rl.on("SIGINT",()=>{for(let t of this.interruptHandlers)try{t()}catch{}this._cancel("cancelled (Ctrl-C)")}),this.rl.on("close",()=>{this.closed=!0,this._cancel("input closed")})}_cancel(e){if(this.waiter){let t=this.waiter;this.waiter=null,t.reject(new Fi(e))}else this.pendingCancel=e}_line(e){if(this._open(),this.queue.length){let t=this.queue.shift();return this.rl.terminal||this.out.write(e+(this.muted?"":t)+`
254
- `),Promise.resolve(t)}return this.pendingCancel||this.closed?Promise.reject(new Fi(this.pendingCancel||"input closed")):new Promise((t,s)=>{this.waiter={resolve:t,reject:s},this.rl.terminal?(this.rl.setPrompt(e),this.rl.prompt()):this.out.write(e)})}_echoDefault(e,t){this.out.write(e+this.c.dim(t)+`
253
+ `),s.resolve(t)}else this.queue.push(t)}),this.rl.on("SIGINT",()=>{for(let t of this.interruptHandlers)try{t()}catch{}this._cancel("cancelled (Ctrl-C)")}),this.rl.on("close",()=>{this.closed=!0,this._cancel("input closed")})}_cancel(e){if(this.waiter){let t=this.waiter;this.waiter=null,t.reject(new Bi(e))}else this.pendingCancel=e}_line(e){if(this._open(),this.queue.length){let t=this.queue.shift();return this.rl.terminal||this.out.write(e+(this.muted?"":t)+`
254
+ `),Promise.resolve(t)}return this.pendingCancel||this.closed?Promise.reject(new Bi(this.pendingCancel||"input closed")):new Promise((t,s)=>{this.waiter={resolve:t,reject:s},this.rl.terminal?(this.rl.setPrompt(e),this.rl.prompt()):this.out.write(e)})}_echoDefault(e,t){this.out.write(e+this.c.dim(t)+`
255
255
  `)}async ask(e,t="",{hint:s,validate:o,secret:r=!1}={}){let i=this.c.cyan("?")+" "+this.c.bold(e)+(s?" "+this.c.dim(s):"")+(t!==""&&!r?" "+this.c.dim("("+t+")"):"")+" ";if(this.yes)return this._echoDefault(i,r?"••••":String(t)),t;for(;;){this.muted=!1,r&&(this.out.write(i),this.muted=!0);let a;try{a=(await this._line(r?"":i)).trim()}finally{this.muted=!1}r&&this.rl&&this.rl.terminal&&this.out.write(`
256
256
  `);let l=a===""?t:a,c=o?o(l):null;if(!c)return l;this.out.write(" "+this.c.red(c)+`
257
257
  `)}}async confirm(e,t=!0){let s=this.c.cyan("?")+" "+this.c.bold(e)+" "+this.c.dim(t?"(Y/n)":"(y/N)")+" ";if(this.yes)return this._echoDefault(s,t?"yes":"no"),t;for(;;){let o=(await this._line(s)).trim().toLowerCase();if(o==="")return t;if(["y","yes"].includes(o))return!0;if(["n","no"].includes(o))return!1;this.out.write(" "+this.c.red("Please answer y or n.")+`
@@ -259,12 +259,12 @@ Settings → Sync on the board, or \`leftoff move --delete-backup\`. Leftoff nev
259
259
  `),t.forEach((r,i)=>{let a=String(i+1).padStart(2),l=i===s?this.c.cyan("›"):" ",c=i===s?this.c.bold(r.label):r.label;this.out.write(" "+l+" "+this.c.dim(a+")")+" "+c+(r.hint?" "+this.c.dim(r.hint):"")+`
260
260
  `),r.detail&&this.out.write(" "+this.c.dim(r.detail)+`
261
261
  `)});let o=" "+this.c.dim("choose 1-"+t.length)+" "+this.c.dim("("+(s+1)+")")+" ";if(this.yes)return this._echoDefault(o,String(s+1)),t[s].value;for(;;){let r=(await this._line(o)).trim();if(r==="")return t[s].value;let i=Number(r);if(Number.isInteger(i)&&i>=1&&i<=t.length)return t[i-1].value;let a=t.find(l=>String(l.value)===r);if(a)return a.value;this.out.write(" "+this.c.red("Pick a number from 1 to "+t.length+".")+`
262
- `)}}pause(){this.rl&&this.rl.pause()}resume(){this.rl&&this.rl.resume()}close(){this.rl&&(this.rl.removeAllListeners("close"),this.rl.close(),this.rl=null)}}});import Mt from"node:fs";import jx from"node:os";import $e from"node:path";import{spawnSync as Fx}from"node:child_process";import{fileURLToPath as Ux}from"node:url";function Ec(n=process.env){return n.CHECKPOINTS_CLAUDE_BIN?n.CHECKPOINTS_CLAUDE_BIN:Oo("claude",n)}function jn(n,e,{env:t=process.env,timeoutMs:s=12e4}={}){let o=Fx(n,e,{env:{...t,CHECKPOINTS_INTERNAL:"1",LEFTOFF_INTERNAL:"1"},encoding:"utf8",timeout:s});return{code:o.status==null?-1:o.status,stdout:o.stdout||"",stderr:o.stderr||(o.error?String(o.error.message):""),args:e}}function Bx(n){let e=String(n||"").match(/(\d+)\.(\d+)\.(\d+)/);return e?e[0]:null}function Gx(n,e){let t=String(n).split(".").map(Number),s=String(e).split(".").map(Number);for(let o=0;o<3;o++){let r=(t[o]||0)-(s[o]||0);if(r)return r>0?1:-1}return 0}function Tw(n=process.env){let e=Ec(n);if(!e)return{found:!1,bin:null,version:null,modsSupported:!1};let t=jn(e,["--version"],{env:n,timeoutMs:15e3}),s=Bx(t.stdout+" "+t.stderr);return{found:!0,bin:e,version:s,modsSupported:!!s&&Gx(s,Hi)>=0}}function Mf(n){return/[\\/]_npx[\\/]/.test(n)||/[\\/]\.npm[\\/]/.test(n)}function Lf(n=lr){try{return JSON.parse(Mt.readFileSync($e.join(n,"package.json"),"utf8")).leftoffBuild==="npm"}catch{return!1}}function Kx(n=process.env){return n.LEFTOFF_PLUGIN_DIR?n.LEFTOFF_PLUGIN_DIR:$e.join(n.XDG_CONFIG_HOME||$e.join(n.HOME||jx.homedir(),".config"),"checkpoints","plugin")}function Df({from:n=lr,env:e=process.env}={}){let t=Kx(e);if(!Mt.existsSync($e.join(n,".claude-plugin","marketplace.json")))throw new Error("no .claude-plugin/marketplace.json in "+n);let s=t+"."+process.pid+".tmp";Mt.rmSync(s,{recursive:!0,force:!0}),Mt.mkdirSync($e.join(s,".claude-plugin"),{recursive:!0}),Mt.copyFileSync($e.join(n,".claude-plugin","marketplace.json"),$e.join(s,".claude-plugin","marketplace.json"));let o=r=>/^(tests|tsconfig\.json)$/.test(r)||/(^|[\\/])types([\\/]|$)/.test(r);return Mt.cpSync($e.join(n,"mod"),$e.join(s,"mod"),{recursive:!0,filter:r=>!o($e.relative($e.join(n,"mod"),r))}),Mt.writeFileSync($e.join(s,"README.md"),"Leftoff's Claude Code plugin (the mod), copied here by `leftoff setup` / `leftoff upgrade` from the installed package.\nClaude Code reads it as a local marketplace. Safe to delete: `leftoff setup` puts it back.\n"),Mt.rmSync(t,{recursive:!0,force:!0}),Mt.mkdirSync($e.dirname(t),{recursive:!0}),Mt.renameSync(s,t),t}function _w({repoDir:n=lr,override:e,env:t=process.env,copy:s=Df}={}){if(e)return e;if(!Mf(n)&&Mt.existsSync($e.join(n,".git"))&&Mt.existsSync($e.join(n,".claude-plugin","marketplace.json")))return n;if(Lf(n)&&Mt.existsSync($e.join(n,".claude-plugin","marketplace.json")))try{return s({from:n,env:t})}catch{}return _c}function Aw(n,e=Me){let t=String(n||""),s=t.search(new RegExp("(^|[^\\w-])"+e.replace(/[.@]/g,"\\$&")+"(?![\\w-])","m"));if(s<0)return{installed:!1,enabled:!1};let o=t.slice(s,s+400).split(/\n\s*\n/)[0];return{installed:!0,enabled:!(/disabled/i.test(o)&&!/✔\s*enabled|status:\s*enabled/i.test(o))}}function $x(n){return new RegExp("(^|[^\\w-])"+un+"([^\\w-]|$)","m").test(String(n||""))}function Oc(n,e){let t=jn(n,["plugin","list"],{env:e,timeoutMs:3e4}),s=jn(n,["plugin","marketplace","list"],{env:e,timeoutMs:3e4}),o=Aw(t.stdout,ps);return{...Aw(t.stdout),legacyInstalled:o.installed,legacyEnabled:o.enabled,marketplaceAdded:s.code===0&&$x(s.stdout),listOk:t.code===0}}function Rf({bin:n,env:e=process.env,state:t=null,onStep:s=()=>{}}){let o=t||Oc(n,e);if(!o.legacyInstalled)return{ok:!0,migrated:!1,steps:[]};let r=[],i=c=>{s("claude "+c.join(" "));let u=jn(n,c,{env:e});return r.push(u),u},a=c=>(c.stderr||c.stdout).trim().split(`
263
- `).pop()||"exit "+c.code;if(!o.installed){i(["plugin","marketplace","update",un]);let c=i(["plugin","install",Me]);if(c.code!==0&&!/already installed/i.test(c.stdout+c.stderr))return{ok:!1,migrated:!1,steps:r,error:a(c)};o.legacyEnabled===!1&&i(["plugin","disable",Me])}let l=i(["plugin","uninstall",ps]);return l.code!==0?{ok:!1,migrated:!0,steps:r,error:"installed "+Me+" but couldn't uninstall "+ps+": "+a(l)}:{ok:!0,migrated:!0,enabled:o.legacyEnabled!==!1,steps:r}}function Pf({bin:n,source:e,env:t=process.env,onStep:s=()=>{}}){let o=[],r=Oc(n,t);if(r.legacyInstalled){let l=Rf({bin:n,env:t,state:r,onStep:s});if(!l.ok)return{ok:!1,steps:l.steps,state:r,error:l.error};let c=Pf({bin:n,source:e,env:t,onStep:s});return{...c,migrated:l.migrated,steps:[...l.steps,...c.steps]}}if(r.installed&&r.enabled)return{ok:!0,already:!0,steps:o,state:r};if(r.installed&&!r.enabled){s("claude plugin enable "+Me);let l=jn(n,["plugin","enable",Me],{env:t});return o.push(l),{ok:l.code===0,already:!0,enabled:l.code===0,steps:o,state:r,error:l.code?(l.stderr||l.stdout).trim():null}}if(!r.marketplaceAdded){s("claude plugin marketplace add "+e);let l=jn(n,["plugin","marketplace","add",e],{env:t});if(o.push(l),l.code!==0&&!/already/i.test(l.stdout+l.stderr))return{ok:!1,steps:o,state:r,error:(l.stderr||l.stdout).trim()||"exit "+l.code}}s("claude plugin install "+Me);let i=jn(n,["plugin","install",Me],{env:t});o.push(i);let a=i.code===0||/already installed/i.test(i.stdout+i.stderr);return{ok:a,steps:o,state:r,error:a?null:(i.stderr||i.stdout).trim()||"exit "+i.code}}function Ew(n){return!!(n&&n.disableAllHooks===!0)}var lr,_c,un,Hx,Me,ps,Hi,Cc=J(()=>{js();_t();lr=$e.resolve($e.dirname(Ux(import.meta.url)),".."),_c=st,un=bg,Hx=vg,Me=Hx+"@"+un,ps=xg+"@"+un,Hi="2.1.287"});import Yx from"node:dns/promises";import zx from"node:http";import Wx from"node:fs";import Ow from"node:path";import{spawn as Jx}from"node:child_process";function qx(n){return{pretty:"http://"+Tt+":"+n+"/",plain:"http://127.0.0.1:"+n+"/"}}async function Xx(n=Tt,e=t=>Yx.lookup(t,{all:!0})){try{let t=await Promise.race([e(n),Iw(800).then(()=>{throw new Error("timeout")})]),s=(Array.isArray(t)?t:[t]).map(o=>typeof o=="string"?o:o&&o.address).filter(Boolean);return s.length>0&&s.every(Vx)}catch{return!1}}function Zx(n,e,t){return new Promise(s=>{let o=zx.request({host:"127.0.0.1",port:n,path:"/v1/health",method:"GET",headers:{host:e,authorization:"Bearer "+(t||"")},timeout:1500},r=>{r.resume(),s(r.statusCode===403?"refused":"ok")});o.on("timeout",()=>{o.destroy(),s("down")}),o.on("error",()=>s("down")),o.end()})}async function Bi({port:n,prefer:e="auto",token:t=null,resolve:s=Xx,probe:o=Zx}={}){let r=qx(n),i=String(e||"auto").toLowerCase();return["127.0.0.1","ip","plain","localhost-ip"].includes(i)?{url:r.plain,pretty:!1,reason:"asked for 127.0.0.1"}:[Tt,"pretty","name"].includes(i)?{url:r.pretty,pretty:!0,reason:"asked for "+Tt}:await s(Tt)?await o(n,Tt+":"+n,t)==="refused"?{url:r.plain,pretty:!1,reason:"the running daemon predates "+Tt+" support (restart it)"}:{url:r.pretty,pretty:!0,reason:Tt+" resolves to loopback"}:{url:r.plain,pretty:!1,reason:Tt+" doesn't resolve to loopback here"}}function Qx(n,{platform:e=process.platform,spawnImpl:t=Jx}={}){let s=e==="darwin"?["open",[n]]:e==="win32"?["cmd",["/c","start","",n]]:["xdg-open",[n]];try{let o=t(s[0],s[1],{detached:!0,stdio:"ignore"});return o&&o.on&&o.on("error",()=>{}),o&&o.unref&&o.unref(),!0}catch{return!1}}async function Cw(n,e){try{let t=await fetch("http://127.0.0.1:"+n+"/v1/health",{headers:{authorization:"Bearer "+(e||"")},signal:AbortSignal.timeout(1500)});return t.status===401?{up:!0,auth:!1}:t.ok?{up:!0,auth:!0,...await t.json()}:{up:!1}}catch{return{up:!1}}}async function Nw({flags:n={},out:e=process.stdout,bin:t,env:s=process.env,openImpl:o=Qx,startImpl:r}={}){let i=Z(),a=Q(i),l=Number(n.port||a.port),c=ve(i),u=m=>{n.quiet||e.write(m+`
264
- `)},d=await Cw(l,c);if(!d.up&&!n["no-start"]){if(!Wx.existsSync(Ow.join(i,"config.json")))return u("No Leftoff folder at "+i+" yet. Run: leftoff setup"),1;u("Daemon isn't running; starting it…"),(r||(await Promise.resolve().then(()=>(eo(),Ic))).startDaemonDetached)({root:i,bin:t,env:s});for(let g=0;g<40&&!(d=await Cw(l,c)).up;g++)await Iw(250);if(!d.up)return u("The daemon didn't start; see "+Ow.join(i,".daemon.log")),1}d.up&&!d.auth&&u("Note: something on port "+l+" doesn't know this folder's token (another Leftoff folder?).");let f=n.host||s.CHECKPOINTS_OPEN_HOST||a.openHost||"auto",h=await Bi({port:l,prefer:f,token:c});if(n.print||n["no-browser"])return e.write(h.url+`
265
- `),0;let p=o(h.url);return u((p?"Opened ":"Open ")+h.url+(h.pretty?"":" ("+h.reason+")")),0}var Iw,Vx,jf=J(()=>{xe();Ff();Iw=n=>new Promise(e=>setTimeout(e,n));Vx=n=>/^127\./.test(n)||n==="::1"||n==="0:0:0:0:0:0:0:1"});function Nc(n,{indent:e=""}={}){let t=[],s=r=>t.push(e+r);s("Doing: "+(n.doing||"")),s("Stopped at: "+(n.stoppedAt||"")),s("Next: "+(n.next||""));let o=(r,i,a=l=>l)=>{!Array.isArray(i)||!i.length||(s(r+":"),i.forEach((l,c)=>s((r==="Next steps"?" "+(c+1)+". ":" - ")+a(l))))};return o("Key decisions",n.decisions),o("Open questions",n.questions),o("Files touched",n.files,r=>(r.path||"")+(r.path&&r.why?" — ":"")+(r.why||"")),o("What didn’t work",n.didntWork),n.recap&&s("Recap: "+n.recap),o("Next steps",n.nextSteps),t.join(`
266
- `)}var Uf,Mw,eA,Lw,Dw,Hf=J(()=>{Uf={doing:"Adding retry with backoff to the sync client (src/sync/client.ts).",stoppedAt:"Unit tests pass; the integration test times out after 30s on the second retry.",next:"YOU: pick between faking the clock in the integration test or raising its timeout."},Mw={decisions:["Exponential backoff capped at 30s: the API rate-limits at 1 request/s.","Retries live in the client, not the job queue, so every caller gets them."],questions:["Should a 429 count toward the 5-retry cap?"],files:[{path:"src/sync/client.ts",why:"the retry loop and backoff"},{path:"src/sync/client.test.ts",why:"unit tests for 500, 429 and timeouts"},{path:"test/sync.int.test.ts",why:"raised the timeout to see if it was just slow"}],didntWork:["Jest fake timers with fetch: the mocked response never resolved."]},eA={recap:"The session set out to make sync survive the API's rate limit after Monday's failed imports. It added a retry loop with exponential backoff to the client and covered 500s, 429s and timeouts with unit tests. Faking timers broke fetch, so the integration test still runs on real time, and the second retry pushes it past 30 seconds. Everything else is green; the open choice is how to test the slow path.",nextSteps:["Fake the clock in the integration test with an injectable sleep().","Decide whether 429s count toward the retry cap and note it in the README.","Open the PR against main and link the import incident."]},Lw={short:{...Uf},informative:{...Uf,...Mw},long:{...Uf,...Mw,...eA}},Dw=[{key:"short",label:"Short",approx:"~300 tokens",blurb:"Doing, Stopped at, Next. Three lines."},{key:"informative",label:"Informative",approx:"~700 tokens",blurb:"Adds key decisions, open questions, files touched and why, and what didn’t work.",recommended:!0},{key:"long",label:"Long",approx:"~1500 tokens",blurb:"Adds a narrative recap and the next 3 steps."}]});var Rw={};Qt(Rw,{RESERVED:()=>Gi,checkUserName:()=>Fn,domainsOf:()=>sA,isReserved:()=>Gf,passthroughOf:()=>tA,userFromHost:()=>nA,validUser:()=>Bf});function Bf(n){let e=String(n||"");return/^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$/.test(e)&&e.length>=2&&!e.includes("--")}function Gf(n){return Gi.includes(String(n||"").toLowerCase())}function tA(n){return String(n&&n.PASSTHROUGH||"").toLowerCase().split(/[\s,]+/).filter(Boolean)}function Fn(n){let e=String(n||"").trim().toLowerCase();return Bf(e)?Gf(e)?{ok:!1,code:"reserved",message:e+" is reserved"}:{ok:!0,user:e}:{ok:!1,code:"bad_name",message:"2–32 lowercase letters, digits or hyphens"}}function nA(n,e,t=[]){let s=String(n||"").toLowerCase().replace(/:\d+$/,"").replace(/\.$/,"");for(let o of e){if(!o||!s.endsWith("."+o))continue;let r=s.slice(0,-(o.length+1));return r.includes(".")||!Bf(r)||Gf(r)||t.includes(r)?null:r}return null}function sA(n){let e=[String(n.DOMAIN||"leftoff.dev").toLowerCase()];return n.DEV==="1"&&e.push("localhost"),e}var Gi,Ki=J(()=>{Gi=["www","app","api","docs","feed","relay","admin","status","mail"]});var Bw={};Qt(Bw,{BAD_NAME_MESSAGE:()=>Lc,POLL_MS:()=>Pw,SIGNUP_TTL_MS:()=>jw,checkName:()=>Yi,relayGet:()=>Fw,relayPost:()=>$i,reservedMessage:()=>Mc,signupProblem:()=>Hw,signupStatus:()=>Rc,startSignup:()=>zi,validEmail:()=>Dc,waitForConfirm:()=>Kf});import oA from"node:http";import rA from"node:https";function Dc(n){let e=String(n||"").trim();return e.length<=254&&/^[^\s@<>()",;:]+@[^\s@<>()",;:]+\.[a-z]{2,}$/i.test(e)}function $i(n,e,t,s=null){return Uw(n,"POST",e,t,s)}function Fw(n,e){return Uw(n,"GET",e)}function Uw(n,e,t,s,o=null){let r=n.origin?new URL(n.origin):new URL("https://"+cr(n)),i=e==="GET"?null:Buffer.from(JSON.stringify(s)),a=r.protocol==="https:"?rA:oA;return new Promise((l,c)=>{let u=a.request({host:r.hostname,port:r.port||(r.protocol==="https:"?443:80),method:e,path:t,timeout:2e4,headers:{host:cr(n)+(n.origin&&r.port?":"+r.port:""),...i?{"content-type":"application/json","content-length":i.length}:{},...o?{authorization:"Bearer "+o}:{},"user-agent":"leftoff-cli"},...r.protocol==="https:"&&n.origin?{servername:cr(n)}:{}},d=>{let f="";d.on("data",h=>f+=h),d.on("end",()=>{let h={};try{h=JSON.parse(f)}catch{}l({status:d.statusCode,json:h,text:f})})});u.on("timeout",()=>u.destroy(new Error("timed out"))),u.on("error",c),u.end(i||void 0)})}function Hw(n,e){let t=n&&n.json||{};return n.status===404&&!t.code?cr(e)+" doesn’t take signups (is the relay on 0.6.1 or newer?)":t.message||"The relay answered "+n.status+(t.code?" ("+t.code+")":"")+"."}async function Yi({s:n,name:e,sign:t}){let s=Fn(e);if(!s.ok)return{ok:!1,code:s.code,message:s.code==="reserved"?Mc(String(e).trim().toLowerCase()):Lc};let o={...n,user:s.user},r=cr(o),i;try{i=await Fw(o,"/_signup/name"+(t?"?sign="+encodeURIComponent(t):""))}catch(c){return{ok:!1,code:"unreachable",message:"Couldn’t reach "+r+": "+c.message+"."}}let a=i.json||{};if(i.status!==200||!a.status)return{ok:!1,code:"legacy",status:i.status};if(a.status==="reserved")return{ok:!1,code:"reserved",message:Mc(s.user)};if(a.status==="invalid")return{ok:!1,code:"bad_name",message:Lc};let l={ok:!0,user:s.user,host:r,status:a.status,open:a.open!==!1,claimed:a.claimed??null};if(a.status==="yours")l.message="This Mac already has "+r+".";else if(a.status==="taken"||a.status==="pending")l.message=r+" is taken. Try another name.";else if(!l.open)return{ok:!1,code:"closed",message:a.message||"Signups aren’t open on this relay yet."};return l}async function zi({s:n,name:e,email:t,machine:s,sign:o}){let r=Fn(e);if(!r.ok)return{ok:!1,status:400,code:r.code,message:r.code==="reserved"?Mc(e):Lc};if(!Dc(t))return{ok:!1,status:400,code:"bad_email",message:"That doesn’t look like an email address."};let i={...n,user:r.user},a;try{a=await $i(i,"/_signup",{name:r.user,email:String(t).trim(),machine:s,sign:o})}catch(l){return{ok:!1,status:0,code:"unreachable",message:"Couldn’t reach "+cr(i)+": "+l.message+"."}}return a.status===200&&a.json.ok?{ok:!0,user:r.user,status:a.json.status,poll:a.json.poll||null,email:a.json.email||t,expiresAt:a.json.expiresAt||null,already:!!a.json.already,claimed:a.json.claimed||null}:{ok:!1,status:a.status,code:a.json.code||null,message:Hw(a,i),retryAfter:a.json.retryAfter||null}}async function Rc({s:n,user:e,poll:t}){try{let s=await $i({...n,user:e},"/_signup/status",{poll:t});return s.json.status||(s.status===404?"unknown":"error")}catch{return"error"}}async function Kf({s:n,user:e,poll:t,intervalMs:s=Pw,timeoutMs:o=jw+6e4,stop:r=()=>!1,onTick:i=()=>{}}){let a=Date.now();for(;;){if(r())return"stopped";let l=await Rc({s:n,user:e,poll:t});if(i(l),["confirmed","expired","replaced","unknown"].includes(l))return l;if(Date.now()-a>o)return"timeout";await new Promise(c=>setTimeout(c,s))}}var cr,Pw,jw,Mc,Lc,Pc=J(()=>{Ki();cr=n=>n.user+"."+n.domain,Pw=2e3,jw=1800*1e3;Mc=n=>"“"+n+"” is reserved (www, app, api, docs, feed, relay, admin, status, mail). Pick another.",Lc="A name is 2–32 lowercase letters, digits or hyphens."});function Wi(n){let e=n instanceof Uint8Array?n:new Uint8Array(n),t="";for(let s=0;s<e.length;s++)t+=String.fromCharCode(e[s]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function Ji(n){for(n=String(n||"").replace(/-/g,"+").replace(/_/g,"/");n.length%4;)n+="=";let e=atob(n),t=new Uint8Array(e.length);for(let s=0;s<e.length;s++)t[s]=e.charCodeAt(s);return t}function Yf(n){return Kw.getRandomValues(new Uint8Array(n))}function Yw(n=16){return Wi(Yf(n))}function zw(){let n=Yf(16),e="";for(let t=0;t<16;t++)e+=$w[n[t]&31];return e.match(/.{4}/g).join("-")}function Ww(n){let e=String(n||"").toUpperCase().replace(/[\s-]/g,"").replace(/O/g,"0").replace(/[IL]/g,"1");return e.length!==16||[...e].some(t=>!$w.includes(t))?null:e.match(/.{4}/g).join("-")}async function Jw(n){return Lt.importKey("raw",ms.encode(Ww(n)||""),{name:"HMAC",hash:"SHA-256"},!1,["sign","verify"])}async function qw(n,e){let t=await Jw(n);return Wi(await Lt.sign("HMAC",t,ms.encode("leftoff-pair-v1|"+e.join("|"))))}async function Vw(n,e,t){let s=await Jw(n);try{return await Lt.verify("HMAC",s,Ji(t),ms.encode("leftoff-pair-v1|"+e.join("|")))}catch{return!1}}async function Xw({extractable:n=!1}={}){let e=await Lt.generateKey({name:"ECDH",namedCurve:"P-256"},n,["deriveBits"]),t=Wi(await Lt.exportKey("raw",e.publicKey));return{privateKey:e.privateKey,pub:t}}async function Zw({privateKey:n,peerPub:e,code:t,device:s,extractable:o=!1}){let r=await Lt.importKey("raw",Ji(e),{name:"ECDH",namedCurve:"P-256"},!1,[]),i=await Lt.deriveBits({name:"ECDH",public:r},n,256),a=await Lt.importKey("raw",i,"HKDF",!1,["deriveKey","deriveBits"]),l={name:"HKDF",hash:"SHA-256",salt:ms.encode(Ww(t)),info:ms.encode("leftoff-relay-device-v1|"+s)};if(o){let c=new Uint8Array(await Lt.deriveBits(l,a,256));return{raw:c,key:await Fc(c)}}return{key:await Lt.deriveKey(l,a,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}}function Fc(n){return Lt.importKey("raw",n instanceof Uint8Array?n:Ji(n),{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}async function Qw(n,{keyFor:e,machine:t,seen:s,now:o=Date.now()}){if(!n||n.v!==1||typeof n.d!="string"||typeof n.id!="string"||typeof n.iv!="string"||typeof n.ct!="string"||typeof n.ts!="number")throw new dn("bad_envelope");if(n.to!==t)throw new dn("wrong_target","sealed for "+n.to);if(Math.abs(o-n.ts)>Gw)throw new dn("expired","the request is too old (or a clock is off)");let r=await e(n.d);if(!r)throw new dn("unknown_device","this browser isn’t paired with this Mac, or it was revoked");if(s&&s.has(n.d+"|"+n.id))throw new dn("replayed");let i;try{i=JSON.parse(iA.decode(await Lt.decrypt({name:"AES-GCM",iv:Ji(n.iv),additionalData:aA(n)},r,Ji(n.ct))))}catch{throw new dn("decrypt_failed")}return s&&s.add(n.d+"|"+n.id,n.ts+Gw),i}async function e0(n,e,t){let s=Yf(12),o=await Lt.encrypt({name:"AES-GCM",iv:s,additionalData:lA(null,e)},n,ms.encode(JSON.stringify(t)));return{v:1,re:e.id,iv:Wi(s),ct:Wi(o)}}var Kw,Lt,ms,iA,$w,$f,Gw,aA,lA,dn,jc,t0=J(()=>{Kw=globalThis.crypto,Lt=Kw.subtle,ms=new TextEncoder,iA=new TextDecoder,$w="0123456789ABCDEFGHJKMNPQRSTVWXYZ",$f=600*1e3,Gw=120*1e3;aA=n=>ms.encode(["leftoff-tunnel-v1","req",n.d,n.to,n.id,n.ts].join("|")),lA=(n,e)=>ms.encode(["leftoff-tunnel-v1","res",e.d,e.to,e.id].join("|")),dn=class extends Error{constructor(e,t){super(t||e),this.code=e}};jc=class{constructor(){this.m=new Map}has(e){return this.sweep(),this.m.has(e)}add(e,t){this.m.set(e,t)}sweep(e=Date.now()){if(!(this.m.size<500))for(let[t,s]of this.m)s<e&&this.m.delete(t)}}});var r0={};Qt(r0,{DEFAULT_DOMAIN:()=>Un,RESERVED:()=>Gi,RelayHost:()=>Xi,boardUrlFor:()=>Vi,connectTarget:()=>Wf,devicesDir:()=>to,hostFor:()=>Bt,localRelayFile:()=>Bc,problemText:()=>Vf,readRelaySettings:()=>Hn,sharedAccountFile:()=>Gc,unwrapKey:()=>qf,wrapKey:()=>Jf,writeRelaySettings:()=>ys});import gs from"node:fs";import Be from"node:path";import cA from"node:http";import at from"node:crypto";import{EventEmitter as uA}from"node:events";import{fileURLToPath as dA}from"node:url";function Bc(n=process.env){return Be.join(Be.dirname(en(n)),"relay.json")}function Gc(n){return Be.join(n,"relay","account.json")}function to(n){return Be.join(n,"relay","devices")}function no(n){try{return JSON.parse(gs.readFileSync(n,"utf8"))}catch{return null}}function qi(n,e,t){gs.mkdirSync(Be.dirname(n),{recursive:!0});let s=Be.join(Be.dirname(n),"."+Be.basename(n)+"."+process.pid+".tmp");gs.writeFileSync(s,JSON.stringify(e,null,2)+`
267
- `,t?{mode:t}:void 0),gs.renameSync(s,n)}function Hn(n,e=process.env){let t=no(Bc(e))||{},s=no(Gc(n))||{},o=String(e.LEFTOFF_RELAY_USER||t.user||s.user||"").toLowerCase()||null,r=String(e.LEFTOFF_RELAY_DOMAIN||t.domain||s.domain||Un).toLowerCase(),i=e.LEFTOFF_ANYWHERE?e.LEFTOFF_ANYWHERE==="1":!!t.enabled,a=e.LEFTOFF_RELAY_ORIGIN||t.origin||null;return{enabled:i,user:o,domain:r,origin:a,registeredAt:t.registeredAt||null}}function ys(n,e,t=process.env){let o={...no(Bc(t))||{},...e};if(qi(Bc(t),o,384),e.user){let r=no(Gc(n))||{};(r.user!==e.user||e.domain&&r.domain!==e.domain)&&qi(Gc(n),{user:e.user,domain:e.domain||r.domain||Un,updatedAt:new Date().toISOString()})}return o}function Bt(n){return n.user+"."+n.domain}function Vi(n){if(n.origin){let e=new URL(n.origin);return e.protocol+"//"+n.user+"."+(n.domain==="localhost"?"localhost":n.domain)+(e.port?":"+e.port:"")}return"https://"+Bt(n)}function Wf(n){if(n.origin){let e=new URL(n.origin);return{url:(e.protocol==="https:"?"wss://":"ws://")+e.host+"/_connect",hostHeader:Bt(n)+(e.port?":"+e.port:"")}}return{url:"wss://"+Bt(n)+"/_connect",hostHeader:null}}function Jf(n,{device:e,machine:t,box:s}){let o=at.generateKeyPairSync("x25519"),r=at.createPublicKey({key:{kty:"OKP",crv:"X25519",x:s},format:"jwk"}),i=at.diffieHellman({privateKey:o.privateKey,publicKey:r}),a=at.randomBytes(16),l=Buffer.from(at.hkdfSync("sha256",i,a,"leftoff-relay-wrap-v1|"+t+"|"+e,32)),c=at.randomBytes(12),u=at.createCipheriv("aes-256-gcm",l,c);u.setAAD(Buffer.from(e));let d=Buffer.concat([u.update(Buffer.from(n)),u.final(),u.getAuthTag()]);return{epk:o.publicKey.export({format:"jwk"}).x,salt:Uc(a),iv:Uc(c),ct:Uc(d)}}function qf(n,{device:e,machine:t,boxPriv:s}){let o=at.createPublicKey({key:{kty:"OKP",crv:"X25519",x:n.epk},format:"jwk"}),r=at.diffieHellman({privateKey:s,publicKey:o}),i=Buffer.from(at.hkdfSync("sha256",r,Hc(n.salt),"leftoff-relay-wrap-v1|"+t+"|"+e,32)),a=Hc(n.ct),l=at.createDecipheriv("aes-256-gcm",i,Hc(n.iv));return l.setAAD(Buffer.from(e)),l.setAuthTag(a.subarray(a.length-16)),Buffer.concat([l.update(a.subarray(0,a.length-16)),l.final()])}function zf(n,e){let{sig:t,...s}=n;return s.by=e.machine,{...s,sig:Uc(at.sign(null,Buffer.from(It(s)),e.signPriv))}}function hA(n,e){try{let{sig:t,...s}=n;return at.verify(null,Buffer.from(It(s)),at.createPublicKey({key:{kty:"OKP",crv:"Ed25519",x:e},format:"jwk"}),Hc(t))}catch{return!1}}function o0(n){return String(n||"Browser").replace(/[\u0000-\u001f<>]/g,"").trim().slice(0,60)||"Browser"}function Vf(n,e={}){return n?n==="not_registered"?"this Mac isn’t on "+(e.user||"?")+"."+(e.domain||Un)+" yet: a Mac that is on it adds this one while it’s awake with Use from anywhere on (your synced folder tells it about this Mac). New name? `leftoff relay signup "+(e.user||"<name>")+"`":n==="key_mismatch"?"the relay knows this Mac by a different key (was it reinstalled?): run `leftoff relay register "+(e.user||"<name>")+" --replace`":n==="wrong_user"?"that address belongs to a different account":/ENOTFOUND|EAI_AGAIN/.test(n)?"can’t find "+(e.user||"")+"."+(e.domain||Un)+" (no internet, or the relay isn’t deployed yet)":/HTTP 404/.test(n)?"no relay answers there yet: deploy it (docs/relay.md)":n:null}var Un,n0,fA,s0,Uc,Hc,Xi,Kc=J(()=>{ff();os();xe();Ri();Pc();Ki();t0();Un="leftoff.dev",n0=Be.join(Be.dirname(dA(import.meta.url)),"..","ui"),fA={".html":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"text/javascript; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".ico":"image/x-icon",".webmanifest":"application/manifest+json; charset=utf-8",".json":"application/json; charset=utf-8"},s0=4*1024*1024,Uc=n=>Buffer.from(n).toString("base64url"),Hc=n=>Buffer.from(String(n||""),"base64url");Xi=class extends uA{constructor({engine:e,root:t,port:s,log:o=()=>{},env:r=process.env,identity:i=null}){super(),this.engine=e,this.root=t,this.port=s,this.log=o,this.env=r,this.machine=e?e.machine:i&&i.machine,this.idOverride=i,this.client=null,this.pairs=new Map,this.keys=new Map,this.seen=new jc,this.innerSecret=at.randomBytes(24).toString("base64url"),this.lastActivity=null,this.relayDevices=null}get id(){return this.idOverride||this.engine&&this.engine.peers&&this.engine.peers.id||this._id||(this._id=Nn(this.machine))}settings(){return Hn(this.root,this.env)}active(){let e=this.settings();return!!(e.enabled&&e.user)}url(){return this.active()?Wf(this.settings()).url:null}cardUser(){let e=this.settings();return e.enabled&&e.user?e.user:null}boardUrl(){let e=this.settings();return e.user?Vi(e):null}async start(){this.active()&&this.connect(),this.timer=setInterval(()=>this.tick(),Number(this.env.LEFTOFF_RELAY_TICK_MS)||3e4),this.timer.unref()}stop(){clearInterval(this.timer),this.disconnect()}connect(){if(this.client)return;let e=this.settings(),t=Wf(e),s=new Zo({url:t.url,hostHeader:t.hostHeader,id:this.id,user:e.user,log:this.log,helloExtra:()=>({version:X,activeAt:this.activityAt()})});s.onEnvelope=o=>this.engine&&this.engine.peers?this.engine.peers.receive(o,"relay"):{kind:"err",code:"not_allowed"},s.on("frame",o=>this.onFrame(o).catch(r=>this.log("relay: "+r.message))),s.on("welcome",()=>this.onWelcome().catch(()=>{})),s.on("state",()=>this.changed()),this.client=s,s.connect(),this.engine&&this.engine.peers&&this.engine.peers.useRelay(s),this.log("relay: connecting to "+Bt(e)+" (Use from anywhere is on)")}disconnect(){let e=this.client;e&&(this.client=null,this.engine&&this.engine.peers&&this.engine.peers.dropRelay(e),e.close(),this.changed())}changed(){this.engine&&this.engine.emit("relay.updated",{})}async enable({user:e,domain:t}={}){let s=this.settings(),o=e||s.user,r=Fn(o);if(!r.ok)throw Object.assign(new Error(r.code==="reserved"?"“"+o+"” is reserved: pick another name":"The name needs 2–32 lowercase letters, digits or hyphens"),{status:400,code:r.code});let i=s.enabled&&s.user===r.user&&(!t||t===s.domain);return ys(this.root,{enabled:!0,user:r.user,...t?{domain:t}:{}},this.env),(!i||!this.client)&&(this.disconnect(),this.connect()),this.engine&&this.engine.peers&&this.engine.peers.publish(),this.status()}async disable(){return ys(this.root,{enabled:!1},this.env),this.disconnect(),this.engine&&this.engine.peers&&this.engine.peers.publish(),this.status()}async signupStart({user:e,email:t}={}){let s=this.settings(),o=String(e||"").trim().toLowerCase(),r=await Yi({s:{...s,user:o},name:o,sign:this.id.signX});if(r.ok&&r.status==="yours")return this.pendingSignup={user:r.user,email:null,poll:null,expiresAt:null,status:"confirmed",startedAt:new Date().toISOString(),claimed:null,already:!0},await this.signupDone(),this.signupState();if(r.ok&&(r.status==="taken"||r.status==="pending"))throw Object.assign(new Error(r.message),{status:409,code:"taken"});if(!r.ok&&["bad_name","reserved"].includes(r.code))throw Object.assign(new Error(r.message),{status:400,code:r.code});if(!r.ok&&r.code==="closed")throw Object.assign(new Error(r.message),{status:503,code:"signups_closed"});let i=await zi({s:{...s,user:o},name:o,email:t,machine:this.machine,sign:this.id.signX});if(!i.ok)throw Object.assign(new Error(i.message),{status:i.status===429?429:i.status>=400&&i.status<500?i.status:502,code:i.code||"signup_failed"});return this.pendingSignup={user:i.user,email:i.email,poll:i.poll,expiresAt:i.expiresAt,status:i.already?"confirmed":"pending",startedAt:new Date().toISOString(),claimed:i.claimed||null},i.already&&await this.signupDone(),this.signupState()}async signupCheck(){let e=this.pendingSignup;if(!e||e.status!=="pending")return this.signupState();let t=await Rc({s:{...this.settings(),user:e.user},user:e.user,poll:e.poll});return t==="confirmed"?(e.status="confirmed",await this.signupDone()):["expired","replaced","unknown"].includes(t)?e.status=t:e.expiresAt&&Date.parse(e.expiresAt)<Date.now()&&(e.status="expired"),this.signupState()}async signupDone(){let e=this.pendingSignup,t=this.settings();ys(this.root,{user:e.user,registeredAt:e.already&&t.user===e.user&&t.registeredAt?t.registeredAt:new Date().toISOString(),via:"signup"},this.env),await this.enable({user:e.user}),this.log("relay: "+e.user+"."+this.settings().domain+(e.already?" already belongs to this Mac":" confirmed by email")+"; Use from anywhere is on"),this.changed()}signupCancel(){return this.pendingSignup=null,this.signupState()}signupState(){let e=this.pendingSignup;return e?{status:e.status,user:e.user,email:e.email,expiresAt:e.expiresAt,startedAt:e.startedAt,claimed:e.claimed,already:!!e.already,host:e.user+"."+this.settings().domain}:{status:"none"}}status(){let e=this.settings(),t=this.client;return{enabled:e.enabled,configured:!!e.user,available:!0,user:e.user,domain:e.domain,host:e.user?Bt(e):null,boardUrl:e.user?Vi(e):null,machine:this.machine,connected:!!(t&&t.connected),connecting:!!(t&&!t.connected),latencyMs:t&&t.connected?t.latencyMs:null,connectedAt:t&&t.connectedAt?new Date(t.connectedAt).toISOString():null,lastError:t?t.lastError:null,problem:t&&!t.connected?Vf(t.lastError,e):null,relayMachines:t&&t.relayMachines?t.relayMachines:null,devices:this.devices().length,reserved:Gi,registered:!!e.registeredAt,signup:this.signupState()}}async onFrame(e){if(e.t==="static")return this.client.send(this.staticFile(e));if(e.t==="pair")return this.client.send({t:"pair.res",id:e.id,...await this.redeem(e)});if(e.t==="tunnel")return this.client.send({t:"tunnel.res",id:e.id,...await this.tunnel(e.env)})}staticFile(e){let t=String(e.path||"/").replace(/^\/+/,"")||"index.html",s=Be.join(n0,Be.normalize(t).replace(/^(\.\.[/\\])+/,""));return!s.startsWith(n0+Be.sep)||/(^|[/\\])\./.test(t)||!gs.existsSync(s)||gs.statSync(s).isDirectory()?{t:"static.res",id:e.id,status:404}:{t:"static.res",id:e.id,status:200,type:fA[Be.extname(s)]||"application/octet-stream",b64:gs.readFileSync(s).toString("base64")}}async tunnel(e){let t;try{t=await Qw(e,{keyFor:i=>this.keyFor(i),machine:this.machine,seen:this.seen})}catch(i){return{error:{code:i instanceof dn?i.code:"bad_envelope",message:i.message}}}let s=await this.keyFor(e.d),o=this.deviceRecord(e.d),r;return typeof t.p!="string"||!t.p.startsWith("/v1/")||!/^(GET|POST|PUT|PATCH|DELETE)$/.test(String(t.m))?r={s:400,h:{"content-type":"application/json"},b:JSON.stringify({error:{code:"bad_request",message:"only the board API goes through the tunnel"}})}:r=await this.local(t,{device:e.d,name:o?o.name:null}),{env:await e0(s,e,r)}}local(e,{device:t,name:s}){return new Promise(o=>{let r=e.b==null?null:Buffer.from(String(e.b)),i={host:"127.0.0.1:"+this.port,"x-leftoff-relay":this.innerSecret,"x-leftoff-device":t,"x-leftoff-device-name":encodeURIComponent(s||""),accept:"application/json"};r&&(i["content-type"]=e.h&&e.h["content-type"]||"application/json",i["content-length"]=r.length);let a=cA.request({host:"127.0.0.1",port:this.port,method:e.m,path:e.p,headers:i,timeout:6e4},l=>{let c=[],u=0;l.on("data",d=>{u+=d.length,u<=s0&&c.push(d)}),l.on("end",()=>o({s:l.statusCode,h:{"content-type":l.headers["content-type"]||"application/json"},b:u>s0?JSON.stringify({error:{code:"too_large"}}):Buffer.concat(c).toString("utf8")}))});a.on("timeout",()=>a.destroy(new Error("timed out"))),a.on("error",l=>o({s:502,h:{"content-type":"application/json"},b:JSON.stringify({error:{code:"local_error",message:l.message}})})),r&&a.write(r),a.end()})}async createPair({now:e=Date.now()}={}){if(!this.client||!this.client.connected)throw Object.assign(new Error("Not connected to the relay yet"+(this.client&&this.client.lastError?" ("+Vf(this.client.lastError,this.settings())+")":"")),{status:409,code:"relay_down"});for(let[a,l]of this.pairs)l.expiresAt<=e&&this.pairs.delete(a);let t;do t=zw();while(this.pairs.has(t.slice(0,4)));let s=t.slice(0,4),o=e+$f,r=await this.client.ask({t:"pair.create",pair:s,expiresAt:o});if(!r.ok)throw Object.assign(new Error("The relay refused the code: "+r.code),{status:502});this.pairs.set(s,{code:t,expiresAt:o,tries:0});let i=this.boardUrl()+"/pair.html#"+t;return{code:t,link:i,expiresAt:new Date(o).toISOString(),ttlSec:Math.round($f/1e3),qrSvg:vc(i)}}async cancelPairs(){for(let e of this.pairs.keys())try{await this.client.ask({t:"pair.cancel",pair:e})}catch{}this.pairs.clear()}async redeem(e,t=Date.now()){let s=this.pairs.get(e.pair);if(!s||s.expiresAt<=t)return this.pairs.delete(e.pair),{ok:!1,code:"expired"};if(!await Vw(s.code,["browser",e.pair,e.pub],e.mac))return s.tries++,s.tries>=5&&this.pairs.delete(e.pair),this.log("relay: a wrong pairing proof for code "+e.pair+"…"),{ok:!1,code:"bad_code"};this.pairs.delete(e.pair);let o=Yw(12),r=await Xw(),i;try{i=(await Zw({privateKey:r.privateKey,peerPub:e.pub,code:s.code,device:o,extractable:!0})).raw}catch{return{ok:!1,code:"bad_key"}}let a=await qw(s.code,["mac",e.pair,e.pub,r.pub,o]);return this.saveDevice({id:o,name:o0(e.name),createdAt:new Date(t).toISOString(),pairedOn:this.machine},i),this.keys.set(o,{key:await Fc(i),at:Date.now()}),this.log("relay: paired a new browser, "+o0(e.name)+" ("+o.slice(0,6)+"…)"),this.changed(),{ok:!0,device:o,pub:r.pub,mac:a}}trustedCards(){let e=Vs(this.root),t={};for(let[s,o]of Object.entries(e))if(o.valid){if(s===this.machine){o.card.sign===this.id.signX&&(t[s]=o.card);continue}In(this.id,o,{pinNew:!1})==="trusted"&&(t[s]=o.card)}return t[this.machine]={machine:this.machine,box:this.id.boxX,sign:this.id.signX},t}saveDevice(e,t){let s={};for(let[o,r]of Object.entries(this.trustedCards()))r.box&&(s[o]=Jf(t,{device:e.id,machine:o,box:r.box}));qi(Be.join(to(this.root),e.id+".json"),zf({v:1,...e,wraps:s},this.id))}deviceFiles(){let e=[];try{e=gs.readdirSync(to(this.root)).filter(t=>/^[\w-]+\.json$/.test(t))}catch{return[]}return e.map(t=>no(Be.join(to(this.root),t))).filter(t=>t&&t.id)}verified(e){let s=this.trustedCards()[e.by];return!!(s&&hA(e,s.sign))}deviceRecord(e){if(!/^[\w-]{8,64}$/.test(String(e||"")))return null;let t=no(Be.join(to(this.root),e+".json"));return t&&this.verified(t)?t:null}async keyFor(e){let t=this.keys.get(e);if(t&&Date.now()-t.at<5e3)return t.key;let s=this.deviceRecord(e);if(!s||s.revokedAt||!s.wraps||!s.wraps[this.machine])return this.keys.delete(e),null;try{let o=await Fc(qf(s.wraps[this.machine],{device:e,machine:this.machine,boxPriv:this.id.boxPriv}));return this.keys.set(e,{key:o,at:Date.now()}),o}catch{return null}}devices(){let e=new Map((this.relayDevices||[]).map(t=>[t.id,t]));return this.deviceFiles().filter(t=>!t.revokedAt&&this.verified(t)).map(t=>({id:t.id,name:t.name,createdAt:t.createdAt,pairedOn:t.pairedOn,lastSeenAt:(e.get(t.id)||{}).lastSeenAt||null,macs:Object.keys(t.wraps||{}).sort()})).sort((t,s)=>String(s.createdAt).localeCompare(String(t.createdAt)))}async refreshRelayDevices(){if(!this.client||!this.client.connected)return this.relayDevices;try{this.relayDevices=(await this.client.ask({t:"devices.list"})).devices||[]}catch{}return this.relayDevices}async revoke(e){let t=Be.join(to(this.root),e+".json"),s=no(t);if(!s||!this.verified(s))throw Object.assign(new Error("No paired device "+e),{status:404,code:"not_found"});if(!s.revokedAt){let{wraps:r,...i}=s;qi(t,zf({...i,wraps:{},revokedAt:new Date().toISOString(),revokedOn:this.machine},this.id))}this.keys.delete(e);let o="pending";if(this.client&&this.client.connected)try{await this.client.ask({t:"device.revoke",device:e}),o="done"}catch{}return this.log("relay: revoked "+(s.name||e)),this.changed(),{ok:!0,id:e,relay:o}}activityAt(){let e=0;if(this.engine&&this.engine.sessions)for(let t of this.engine.sessions.values()){if(t.archived||t.machine&&t.machine!==this.machine)continue;let s=Date.parse(t.lastActivityAt||t.startedAt||"")||0;s>e&&(e=s)}return e?new Date(e).toISOString():null}async onWelcome(){let e=this.deviceFiles().filter(t=>t.revokedAt&&this.verified(t)).map(t=>t.id);e.length&&await this.client.ask({t:"devices.sync",revoked:e}).catch(()=>{}),await this.enrollPeers(),this.rewrap(),this.lastActivity=null,this.tick()}async enrollPeers(){if(!this.client||!this.client.connected)return;let e=this.settings(),t=new Set(this.client.relayMachines||[]),s=Vs(this.root);for(let[o,r]of Object.entries(s))if(!(o===this.machine||t.has(o)||!r.valid||r.card.anywhere!==e.user)&&In(this.id,r,{pinNew:!1})==="trusted")try{let i=await this.client.ask({t:"machine.add",machine:o,sign:r.card.sign});i.ok?(t.add(o),this.log("relay: added "+o+" to "+e.user+"."+e.domain)):this.log("relay: couldn’t add "+o+": "+i.code)}catch{}this.client.relayMachines=[...t]}rewrap(){let e=this.trustedCards();for(let t of this.deviceFiles()){if(t.revokedAt||!t.wraps||!t.wraps[this.machine]||!this.verified(t))continue;let s=Object.keys(e).filter(o=>!t.wraps[o]&&e[o].box);if(s.length)try{let o=qf(t.wraps[this.machine],{device:t.id,machine:this.machine,boxPriv:this.id.boxPriv}),r={...t.wraps};for(let i of s)r[i]=Jf(o,{device:t.id,machine:i,box:e[i].box});qi(Be.join(to(this.root),t.id+".json"),zf({...t,wraps:r},this.id)),this.log("relay: "+t.name+" can now reach "+s.join(", "))}catch{}}}tick(){if(this.active()&&!this.client&&this.connect(),!this.active()&&this.client&&this.disconnect(),!this.client||!this.client.connected)return;let e=this.activityAt();e&&e!==this.lastActivity&&(this.lastActivity=e,this.client.send({t:"activity",at:e})),this.enrollPeers().catch(()=>{}),this.rewrap(),this.refreshRelayDevices().catch(()=>{})}}});var Ic={};Qt(Ic,{SUMMARIZER_FLAG:()=>Zf,daemonHealth:()=>dr,flagBool:()=>ct,ghSetupStep:()=>h0,globalBinPath:()=>Qf,lengthFlow:()=>d0,linkFolder:()=>Jc,localModelFlow:()=>qc,mergeSetupConfig:()=>zc,ollamaModelFlow:()=>u0,patchDaemonConfig:()=>$c,relaySignupStep:()=>f0,runSetup:()=>th,showTestResult:()=>ro,startDaemonDetached:()=>Yc,testFlow:()=>fr,writeConfigFile:()=>Wc});import et from"node:fs";import oo from"node:os";import Re from"node:path";import{spawn as ur,spawnSync as so}from"node:child_process";function ct(n,e){if(n["no-"+e])return!1;if(n[e]!==void 0)return mA(n[e])?!1:pA(n[e])||n[e]===""?!0:!!n[e]}function gA(n,e){let t=e;return{c:t,line:(s="")=>n.write(s+`
262
+ `)}}pause(){this.rl&&this.rl.pause()}resume(){this.rl&&this.rl.resume()}close(){this.rl&&(this.rl.removeAllListeners("close"),this.rl.close(),this.rl=null)}}});import Dt from"node:fs";import $x from"node:os";import ze from"node:path";import{spawnSync as zx}from"node:child_process";import{fileURLToPath as Yx}from"node:url";function Cc(n=process.env){return n.CHECKPOINTS_CLAUDE_BIN?n.CHECKPOINTS_CLAUDE_BIN:No("claude",n)}function Un(n,e,{env:t=process.env,timeoutMs:s=12e4}={}){let o=zx(n,e,{env:{...t,CHECKPOINTS_INTERNAL:"1",LEFTOFF_INTERNAL:"1"},encoding:"utf8",timeout:s});return{code:o.status==null?-1:o.status,stdout:o.stdout||"",stderr:o.stderr||(o.error?String(o.error.message):""),args:e}}function Jx(n){let e=String(n||"").match(/(\d+)\.(\d+)\.(\d+)/);return e?e[0]:null}function qx(n,e){let t=String(n).split(".").map(Number),s=String(e).split(".").map(Number);for(let o=0;o<3;o++){let r=(t[o]||0)-(s[o]||0);if(r)return r>0?1:-1}return 0}function Iw(n=process.env){let e=Cc(n);if(!e)return{found:!1,bin:null,version:null,modsSupported:!1};let t=Un(e,["--version"],{env:n,timeoutMs:15e3}),s=Jx(t.stdout+" "+t.stderr);return{found:!0,bin:e,version:s,modsSupported:!!s&&qx(s,Ki)>=0}}function Pf(n){return/[\\/]_npx[\\/]/.test(n)||/[\\/]\.npm[\\/]/.test(n)}function jf(n=lr){try{return JSON.parse(Dt.readFileSync(ze.join(n,"package.json"),"utf8")).leftoffBuild==="npm"}catch{return!1}}function Vx(n=process.env){return n.LEFTOFF_PLUGIN_DIR?n.LEFTOFF_PLUGIN_DIR:ze.join(n.XDG_CONFIG_HOME||ze.join(n.HOME||$x.homedir(),".config"),"checkpoints","plugin")}function Ff({from:n=lr,env:e=process.env}={}){let t=Vx(e);if(!Dt.existsSync(ze.join(n,".claude-plugin","marketplace.json")))throw new Error("no .claude-plugin/marketplace.json in "+n);let s=t+"."+process.pid+".tmp";Dt.rmSync(s,{recursive:!0,force:!0}),Dt.mkdirSync(ze.join(s,".claude-plugin"),{recursive:!0}),Dt.copyFileSync(ze.join(n,".claude-plugin","marketplace.json"),ze.join(s,".claude-plugin","marketplace.json"));let o=r=>/^(tests|tsconfig\.json)$/.test(r)||/(^|[\\/])types([\\/]|$)/.test(r);return Dt.cpSync(ze.join(n,"mod"),ze.join(s,"mod"),{recursive:!0,filter:r=>!o(ze.relative(ze.join(n,"mod"),r))}),Dt.writeFileSync(ze.join(s,"README.md"),"Leftoff's Claude Code plugin (the mod), copied here by `leftoff setup` / `leftoff upgrade` from the installed package.\nClaude Code reads it as a local marketplace. Safe to delete: `leftoff setup` puts it back.\n"),Dt.rmSync(t,{recursive:!0,force:!0}),Dt.mkdirSync(ze.dirname(t),{recursive:!0}),Dt.renameSync(s,t),t}function Mw({repoDir:n=lr,override:e,env:t=process.env,copy:s=Ff}={}){if(e)return e;if(!Pf(n)&&Dt.existsSync(ze.join(n,".git"))&&Dt.existsSync(ze.join(n,".claude-plugin","marketplace.json")))return n;if(jf(n)&&Dt.existsSync(ze.join(n,".claude-plugin","marketplace.json")))try{return s({from:n,env:t})}catch{}return Oc}function Nw(n,e=Le){let t=String(n||""),s=t.search(new RegExp("(^|[^\\w-])"+e.replace(/[.@]/g,"\\$&")+"(?![\\w-])","m"));if(s<0)return{installed:!1,enabled:!1};let o=t.slice(s,s+400).split(/\n\s*\n/)[0];return{installed:!0,enabled:!(/disabled/i.test(o)&&!/✔\s*enabled|status:\s*enabled/i.test(o))}}function Xx(n){return new RegExp("(^|[^\\w-])"+fn+"([^\\w-]|$)","m").test(String(n||""))}function Nc(n,e){let t=Un(n,["plugin","list"],{env:e,timeoutMs:3e4}),s=Un(n,["plugin","marketplace","list"],{env:e,timeoutMs:3e4}),o=Nw(t.stdout,ys);return{...Nw(t.stdout),legacyInstalled:o.installed,legacyEnabled:o.enabled,marketplaceAdded:s.code===0&&Xx(s.stdout),listOk:t.code===0}}function Uf({bin:n,env:e=process.env,state:t=null,onStep:s=()=>{}}){let o=t||Nc(n,e);if(!o.legacyInstalled)return{ok:!0,migrated:!1,steps:[]};let r=[],i=c=>{s("claude "+c.join(" "));let u=Un(n,c,{env:e});return r.push(u),u},a=c=>(c.stderr||c.stdout).trim().split(`
263
+ `).pop()||"exit "+c.code;if(!o.installed){i(["plugin","marketplace","update",fn]);let c=i(["plugin","install",Le]);if(c.code!==0&&!/already installed/i.test(c.stdout+c.stderr))return{ok:!1,migrated:!1,steps:r,error:a(c)};o.legacyEnabled===!1&&i(["plugin","disable",Le])}let l=i(["plugin","uninstall",ys]);return l.code!==0?{ok:!1,migrated:!0,steps:r,error:"installed "+Le+" but couldn't uninstall "+ys+": "+a(l)}:{ok:!0,migrated:!0,enabled:o.legacyEnabled!==!1,steps:r}}function Hf({bin:n,source:e,env:t=process.env,onStep:s=()=>{}}){let o=[],r=Nc(n,t);if(r.legacyInstalled){let l=Uf({bin:n,env:t,state:r,onStep:s});if(!l.ok)return{ok:!1,steps:l.steps,state:r,error:l.error};let c=Hf({bin:n,source:e,env:t,onStep:s});return{...c,migrated:l.migrated,steps:[...l.steps,...c.steps]}}if(r.installed&&r.enabled)return{ok:!0,already:!0,steps:o,state:r};if(r.installed&&!r.enabled){s("claude plugin enable "+Le);let l=Un(n,["plugin","enable",Le],{env:t});return o.push(l),{ok:l.code===0,already:!0,enabled:l.code===0,steps:o,state:r,error:l.code?(l.stderr||l.stdout).trim():null}}if(!r.marketplaceAdded){s("claude plugin marketplace add "+e);let l=Un(n,["plugin","marketplace","add",e],{env:t});if(o.push(l),l.code!==0&&!/already/i.test(l.stdout+l.stderr))return{ok:!1,steps:o,state:r,error:(l.stderr||l.stdout).trim()||"exit "+l.code}}s("claude plugin install "+Le);let i=Un(n,["plugin","install",Le],{env:t});o.push(i);let a=i.code===0||/already installed/i.test(i.stdout+i.stderr);return{ok:a,steps:o,state:r,error:a?null:(i.stderr||i.stdout).trim()||"exit "+i.code}}function Lw(n){return!!(n&&n.disableAllHooks===!0)}var lr,Oc,fn,Wx,Le,ys,Ki,Ic=J(()=>{Hs();Ot();lr=ze.resolve(ze.dirname(Yx(import.meta.url)),".."),Oc=rt,fn=_g,Wx=Eg,Le=Wx+"@"+fn,ys=Og+"@"+fn,Ki="2.1.287"});import Zx from"node:dns/promises";import Qx from"node:http";import eA from"node:fs";import Dw from"node:path";import{spawn as tA}from"node:child_process";function nA(n){return{pretty:"http://"+Et+":"+n+"/",plain:"http://127.0.0.1:"+n+"/"}}async function oA(n=Et,e=t=>Zx.lookup(t,{all:!0})){try{let t=await Promise.race([e(n),Pw(800).then(()=>{throw new Error("timeout")})]),s=(Array.isArray(t)?t:[t]).map(o=>typeof o=="string"?o:o&&o.address).filter(Boolean);return s.length>0&&s.every(sA)}catch{return!1}}function rA(n,e,t){return new Promise(s=>{let o=Qx.request({host:"127.0.0.1",port:n,path:"/v1/health",method:"GET",headers:{host:e,authorization:"Bearer "+(t||"")},timeout:1500},r=>{r.resume(),s(r.statusCode===403?"refused":"ok")});o.on("timeout",()=>{o.destroy(),s("down")}),o.on("error",()=>s("down")),o.end()})}async function $i({port:n,prefer:e="auto",token:t=null,resolve:s=oA,probe:o=rA}={}){let r=nA(n),i=String(e||"auto").toLowerCase();return["127.0.0.1","ip","plain","localhost-ip"].includes(i)?{url:r.plain,pretty:!1,reason:"asked for 127.0.0.1"}:[Et,"pretty","name"].includes(i)?{url:r.pretty,pretty:!0,reason:"asked for "+Et}:await s(Et)?await o(n,Et+":"+n,t)==="refused"?{url:r.plain,pretty:!1,reason:"the running daemon predates "+Et+" support (restart it)"}:{url:r.pretty,pretty:!0,reason:Et+" resolves to loopback"}:{url:r.plain,pretty:!1,reason:Et+" doesn't resolve to loopback here"}}function iA(n,{platform:e=process.platform,spawnImpl:t=tA}={}){let s=e==="darwin"?["open",[n]]:e==="win32"?["cmd",["/c","start","",n]]:["xdg-open",[n]];try{let o=t(s[0],s[1],{detached:!0,stdio:"ignore"});return o&&o.on&&o.on("error",()=>{}),o&&o.unref&&o.unref(),!0}catch{return!1}}async function Rw(n,e){try{let t=await fetch("http://127.0.0.1:"+n+"/v1/health",{headers:{authorization:"Bearer "+(e||"")},signal:AbortSignal.timeout(1500)});return t.status===401?{up:!0,auth:!1}:t.ok?{up:!0,auth:!0,...await t.json()}:{up:!1}}catch{return{up:!1}}}async function jw({flags:n={},out:e=process.stdout,bin:t,env:s=process.env,openImpl:o=iA,startImpl:r}={}){let i=Z(),a=Q(i),l=Number(n.port||a.port),c=ve(i),u=m=>{n.quiet||e.write(m+`
264
+ `)},d=await Rw(l,c);if(!d.up&&!n["no-start"]){if(!eA.existsSync(Dw.join(i,"config.json")))return u("No Leftoff folder at "+i+" yet. Run: leftoff setup"),1;u("Daemon isn't running; starting it…"),(r||(await Promise.resolve().then(()=>(no(),Mc))).startDaemonDetached)({root:i,bin:t,env:s});for(let g=0;g<40&&!(d=await Rw(l,c)).up;g++)await Pw(250);if(!d.up)return u("The daemon didn't start; see "+Dw.join(i,".daemon.log")),1}d.up&&!d.auth&&u("Note: something on port "+l+" doesn't know this folder's token (another Leftoff folder?).");let f=n.host||s.CHECKPOINTS_OPEN_HOST||a.openHost||"auto",h=await $i({port:l,prefer:f,token:c});if(n.print||n["no-browser"])return e.write(h.url+`
265
+ `),0;let p=o(h.url);return u((p?"Opened ":"Open ")+h.url+(h.pretty?"":" ("+h.reason+")")),0}var Pw,sA,Bf=J(()=>{xe();Gf();Pw=n=>new Promise(e=>setTimeout(e,n));sA=n=>/^127\./.test(n)||n==="::1"||n==="0:0:0:0:0:0:0:1"});function Lc(n,{indent:e=""}={}){let t=[],s=r=>t.push(e+r);s("Doing: "+(n.doing||"")),s("Stopped at: "+(n.stoppedAt||"")),s("Next: "+(n.next||""));let o=(r,i,a=l=>l)=>{!Array.isArray(i)||!i.length||(s(r+":"),i.forEach((l,c)=>s((r==="Next steps"?" "+(c+1)+". ":" - ")+a(l))))};return o("Key decisions",n.decisions),o("Open questions",n.questions),o("Files touched",n.files,r=>(r.path||"")+(r.path&&r.why?" — ":"")+(r.why||"")),o("What didn’t work",n.didntWork),n.recap&&s("Recap: "+n.recap),o("Next steps",n.nextSteps),t.join(`
266
+ `)}var Kf,Fw,aA,Uw,Hw,$f=J(()=>{Kf={doing:"Adding retry with backoff to the sync client (src/sync/client.ts).",stoppedAt:"Unit tests pass; the integration test times out after 30s on the second retry.",next:"YOU: pick between faking the clock in the integration test or raising its timeout."},Fw={decisions:["Exponential backoff capped at 30s: the API rate-limits at 1 request/s.","Retries live in the client, not the job queue, so every caller gets them."],questions:["Should a 429 count toward the 5-retry cap?"],files:[{path:"src/sync/client.ts",why:"the retry loop and backoff"},{path:"src/sync/client.test.ts",why:"unit tests for 500, 429 and timeouts"},{path:"test/sync.int.test.ts",why:"raised the timeout to see if it was just slow"}],didntWork:["Jest fake timers with fetch: the mocked response never resolved."]},aA={recap:"The session set out to make sync survive the API's rate limit after Monday's failed imports. It added a retry loop with exponential backoff to the client and covered 500s, 429s and timeouts with unit tests. Faking timers broke fetch, so the integration test still runs on real time, and the second retry pushes it past 30 seconds. Everything else is green; the open choice is how to test the slow path.",nextSteps:["Fake the clock in the integration test with an injectable sleep().","Decide whether 429s count toward the retry cap and note it in the README.","Open the PR against main and link the import incident."]},Uw={short:{...Kf},informative:{...Kf,...Fw},long:{...Kf,...Fw,...aA}},Hw=[{key:"short",label:"Short",approx:"~300 tokens",blurb:"Doing, Stopped at, Next. Three lines."},{key:"informative",label:"Informative",approx:"~700 tokens",blurb:"Adds key decisions, open questions, files touched and why, and what didn’t work.",recommended:!0},{key:"long",label:"Long",approx:"~1500 tokens",blurb:"Adds a narrative recap and the next 3 steps."}]});var Bw={};tn(Bw,{RESERVED:()=>zi,checkUserName:()=>Hn,domainsOf:()=>uA,isReserved:()=>Yf,passthroughOf:()=>lA,userFromHost:()=>cA,validUser:()=>zf});function zf(n){let e=String(n||"");return/^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$/.test(e)&&e.length>=2&&!e.includes("--")}function Yf(n){return zi.includes(String(n||"").toLowerCase())}function lA(n){return String(n&&n.PASSTHROUGH||"").toLowerCase().split(/[\s,]+/).filter(Boolean)}function Hn(n){let e=String(n||"").trim().toLowerCase();return zf(e)?Yf(e)?{ok:!1,code:"reserved",message:e+" is reserved"}:{ok:!0,user:e}:{ok:!1,code:"bad_name",message:"2–32 lowercase letters, digits or hyphens"}}function cA(n,e,t=[]){let s=String(n||"").toLowerCase().replace(/:\d+$/,"").replace(/\.$/,"");for(let o of e){if(!o||!s.endsWith("."+o))continue;let r=s.slice(0,-(o.length+1));return r.includes(".")||!zf(r)||Yf(r)||t.includes(r)?null:r}return null}function uA(n){let e=[String(n.DOMAIN||"leftoff.dev").toLowerCase()];return n.DEV==="1"&&e.push("localhost"),e}var zi,Yi=J(()=>{zi=["www","app","api","docs","feed","relay","admin","status","mail"]});var Ww={};tn(Ww,{BAD_NAME_MESSAGE:()=>Rc,POLL_MS:()=>Gw,SIGNUP_TTL_MS:()=>Kw,checkName:()=>Ji,relayGet:()=>$w,relayPost:()=>Wi,reservedMessage:()=>Dc,signupProblem:()=>Yw,signupStatus:()=>jc,startSignup:()=>qi,validEmail:()=>Pc,waitForConfirm:()=>Wf});import dA from"node:http";import fA from"node:https";function Pc(n){let e=String(n||"").trim();return e.length<=254&&/^[^\s@<>()",;:]+@[^\s@<>()",;:]+\.[a-z]{2,}$/i.test(e)}function Wi(n,e,t,s=null){return zw(n,"POST",e,t,s)}function $w(n,e){return zw(n,"GET",e)}function zw(n,e,t,s,o=null){let r=n.origin?new URL(n.origin):new URL("https://"+cr(n)),i=e==="GET"?null:Buffer.from(JSON.stringify(s)),a=r.protocol==="https:"?fA:dA;return new Promise((l,c)=>{let u=a.request({host:r.hostname,port:r.port||(r.protocol==="https:"?443:80),method:e,path:t,timeout:2e4,headers:{host:cr(n)+(n.origin&&r.port?":"+r.port:""),...i?{"content-type":"application/json","content-length":i.length}:{},...o?{authorization:"Bearer "+o}:{},"user-agent":"leftoff-cli"},...r.protocol==="https:"&&n.origin?{servername:cr(n)}:{}},d=>{let f="";d.on("data",h=>f+=h),d.on("end",()=>{let h={};try{h=JSON.parse(f)}catch{}l({status:d.statusCode,json:h,text:f})})});u.on("timeout",()=>u.destroy(new Error("timed out"))),u.on("error",c),u.end(i||void 0)})}function Yw(n,e){let t=n&&n.json||{};return n.status===404&&!t.code?cr(e)+" doesn’t take signups (is the relay on 0.6.1 or newer?)":t.message||"The relay answered "+n.status+(t.code?" ("+t.code+")":"")+"."}async function Ji({s:n,name:e,sign:t}){let s=Hn(e);if(!s.ok)return{ok:!1,code:s.code,message:s.code==="reserved"?Dc(String(e).trim().toLowerCase()):Rc};let o={...n,user:s.user},r=cr(o),i;try{i=await $w(o,"/_signup/name"+(t?"?sign="+encodeURIComponent(t):""))}catch(c){return{ok:!1,code:"unreachable",message:"Couldn’t reach "+r+": "+c.message+"."}}let a=i.json||{};if(i.status!==200||!a.status)return{ok:!1,code:"legacy",status:i.status};if(a.status==="reserved")return{ok:!1,code:"reserved",message:Dc(s.user)};if(a.status==="invalid")return{ok:!1,code:"bad_name",message:Rc};let l={ok:!0,user:s.user,host:r,status:a.status,open:a.open!==!1,claimed:a.claimed??null};if(a.status==="yours")l.message="This Mac already has "+r+".";else if(a.status==="taken"||a.status==="pending")l.message=r+" is taken. Try another name.";else if(!l.open)return{ok:!1,code:"closed",message:a.message||"Signups aren’t open on this relay yet."};return l}async function qi({s:n,name:e,email:t,machine:s,sign:o}){let r=Hn(e);if(!r.ok)return{ok:!1,status:400,code:r.code,message:r.code==="reserved"?Dc(e):Rc};if(!Pc(t))return{ok:!1,status:400,code:"bad_email",message:"That doesn’t look like an email address."};let i={...n,user:r.user},a;try{a=await Wi(i,"/_signup",{name:r.user,email:String(t).trim(),machine:s,sign:o})}catch(l){return{ok:!1,status:0,code:"unreachable",message:"Couldn’t reach "+cr(i)+": "+l.message+"."}}return a.status===200&&a.json.ok?{ok:!0,user:r.user,status:a.json.status,poll:a.json.poll||null,email:a.json.email||t,expiresAt:a.json.expiresAt||null,already:!!a.json.already,claimed:a.json.claimed||null}:{ok:!1,status:a.status,code:a.json.code||null,message:Yw(a,i),retryAfter:a.json.retryAfter||null}}async function jc({s:n,user:e,poll:t}){try{let s=await Wi({...n,user:e},"/_signup/status",{poll:t});return s.json.status||(s.status===404?"unknown":"error")}catch{return"error"}}async function Wf({s:n,user:e,poll:t,intervalMs:s=Gw,timeoutMs:o=Kw+6e4,stop:r=()=>!1,onTick:i=()=>{}}){let a=Date.now();for(;;){if(r())return"stopped";let l=await jc({s:n,user:e,poll:t});if(i(l),["confirmed","expired","replaced","unknown"].includes(l))return l;if(Date.now()-a>o)return"timeout";await new Promise(c=>setTimeout(c,s))}}var cr,Gw,Kw,Dc,Rc,Fc=J(()=>{Yi();cr=n=>n.user+"."+n.domain,Gw=2e3,Kw=1800*1e3;Dc=n=>"“"+n+"” is reserved (www, app, api, docs, feed, relay, admin, status, mail). Pick another.",Rc="A name is 2–32 lowercase letters, digits or hyphens."});function Vi(n){let e=n instanceof Uint8Array?n:new Uint8Array(n),t="";for(let s=0;s<e.length;s++)t+=String.fromCharCode(e[s]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function Xi(n){for(n=String(n||"").replace(/-/g,"+").replace(/_/g,"/");n.length%4;)n+="=";let e=atob(n),t=new Uint8Array(e.length);for(let s=0;s<e.length;s++)t[s]=e.charCodeAt(s);return t}function qf(n){return qw.getRandomValues(new Uint8Array(n))}function Xw(n=16){return Vi(qf(n))}function Zw(){let n=qf(16),e="";for(let t=0;t<16;t++)e+=Vw[n[t]&31];return e.match(/.{4}/g).join("-")}function Qw(n){let e=String(n||"").toUpperCase().replace(/[\s-]/g,"").replace(/O/g,"0").replace(/[IL]/g,"1");return e.length!==16||[...e].some(t=>!Vw.includes(t))?null:e.match(/.{4}/g).join("-")}async function e0(n){return Rt.importKey("raw",ws.encode(Qw(n)||""),{name:"HMAC",hash:"SHA-256"},!1,["sign","verify"])}async function t0(n,e){let t=await e0(n);return Vi(await Rt.sign("HMAC",t,ws.encode("leftoff-pair-v1|"+e.join("|"))))}async function n0(n,e,t){let s=await e0(n);try{return await Rt.verify("HMAC",s,Xi(t),ws.encode("leftoff-pair-v1|"+e.join("|")))}catch{return!1}}async function s0({extractable:n=!1}={}){let e=await Rt.generateKey({name:"ECDH",namedCurve:"P-256"},n,["deriveBits"]),t=Vi(await Rt.exportKey("raw",e.publicKey));return{privateKey:e.privateKey,pub:t}}async function o0({privateKey:n,peerPub:e,code:t,device:s,extractable:o=!1}){let r=await Rt.importKey("raw",Xi(e),{name:"ECDH",namedCurve:"P-256"},!1,[]),i=await Rt.deriveBits({name:"ECDH",public:r},n,256),a=await Rt.importKey("raw",i,"HKDF",!1,["deriveKey","deriveBits"]),l={name:"HKDF",hash:"SHA-256",salt:ws.encode(Qw(t)),info:ws.encode("leftoff-relay-device-v1|"+s)};if(o){let c=new Uint8Array(await Rt.deriveBits(l,a,256));return{raw:c,key:await Hc(c)}}return{key:await Rt.deriveKey(l,a,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}}function Hc(n){return Rt.importKey("raw",n instanceof Uint8Array?n:Xi(n),{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}async function r0(n,{keyFor:e,machine:t,seen:s,now:o=Date.now()}){if(!n||n.v!==1||typeof n.d!="string"||typeof n.id!="string"||typeof n.iv!="string"||typeof n.ct!="string"||typeof n.ts!="number")throw new hn("bad_envelope");if(n.to!==t)throw new hn("wrong_target","sealed for "+n.to);if(Math.abs(o-n.ts)>Jw)throw new hn("expired","the request is too old (or a clock is off)");let r=await e(n.d);if(!r)throw new hn("unknown_device","this browser isn’t paired with this Mac, or it was revoked");if(s&&s.has(n.d+"|"+n.id))throw new hn("replayed");let i;try{i=JSON.parse(hA.decode(await Rt.decrypt({name:"AES-GCM",iv:Xi(n.iv),additionalData:pA(n)},r,Xi(n.ct))))}catch{throw new hn("decrypt_failed")}return s&&s.add(n.d+"|"+n.id,n.ts+Jw),i}async function i0(n,e,t){let s=qf(12),o=await Rt.encrypt({name:"AES-GCM",iv:s,additionalData:mA(null,e)},n,ws.encode(JSON.stringify(t)));return{v:1,re:e.id,iv:Vi(s),ct:Vi(o)}}var qw,Rt,ws,hA,Vw,Jf,Jw,pA,mA,hn,Uc,a0=J(()=>{qw=globalThis.crypto,Rt=qw.subtle,ws=new TextEncoder,hA=new TextDecoder,Vw="0123456789ABCDEFGHJKMNPQRSTVWXYZ",Jf=600*1e3,Jw=120*1e3;pA=n=>ws.encode(["leftoff-tunnel-v1","req",n.d,n.to,n.id,n.ts].join("|")),mA=(n,e)=>ws.encode(["leftoff-tunnel-v1","res",e.d,e.to,e.id].join("|")),hn=class extends Error{constructor(e,t){super(t||e),this.code=e}};Uc=class{constructor(){this.m=new Map}has(e){return this.sweep(),this.m.has(e)}add(e,t){this.m.set(e,t)}sweep(e=Date.now()){if(!(this.m.size<500))for(let[t,s]of this.m)s<e&&this.m.delete(t)}}});var d0={};tn(d0,{DEFAULT_DOMAIN:()=>Bn,RESERVED:()=>zi,RelayHost:()=>ea,boardUrlFor:()=>Qi,connectTarget:()=>Xf,devicesDir:()=>so,hostFor:()=>Kt,localRelayFile:()=>Kc,problemText:()=>eh,readRelaySettings:()=>Gn,sharedAccountFile:()=>$c,unwrapKey:()=>Qf,wrapKey:()=>Zf,writeRelaySettings:()=>ks});import Ss from"node:fs";import Ge from"node:path";import gA from"node:http";import ct from"node:crypto";import{EventEmitter as yA}from"node:events";import{fileURLToPath as wA}from"node:url";function Kc(n=process.env){return Ge.join(Ge.dirname(nn(n)),"relay.json")}function $c(n){return Ge.join(n,"relay","account.json")}function so(n){return Ge.join(n,"relay","devices")}function oo(n){try{return JSON.parse(Ss.readFileSync(n,"utf8"))}catch{return null}}function Zi(n,e,t){Ss.mkdirSync(Ge.dirname(n),{recursive:!0});let s=Ge.join(Ge.dirname(n),"."+Ge.basename(n)+"."+process.pid+".tmp");Ss.writeFileSync(s,JSON.stringify(e,null,2)+`
267
+ `,t?{mode:t}:void 0),Ss.renameSync(s,n)}function Gn(n,e=process.env){let t=oo(Kc(e))||{},s=oo($c(n))||{},o=String(e.LEFTOFF_RELAY_USER||t.user||s.user||"").toLowerCase()||null,r=String(e.LEFTOFF_RELAY_DOMAIN||t.domain||s.domain||Bn).toLowerCase(),i=e.LEFTOFF_ANYWHERE?e.LEFTOFF_ANYWHERE==="1":!!t.enabled,a=e.LEFTOFF_RELAY_ORIGIN||t.origin||null;return{enabled:i,user:o,domain:r,origin:a,registeredAt:t.registeredAt||null}}function ks(n,e,t=process.env){let o={...oo(Kc(t))||{},...e};if(Zi(Kc(t),o,384),e.user){let r=oo($c(n))||{};(r.user!==e.user||e.domain&&r.domain!==e.domain)&&Zi($c(n),{user:e.user,domain:e.domain||r.domain||Bn,updatedAt:new Date().toISOString()})}return o}function Kt(n){return n.user+"."+n.domain}function Qi(n){if(n.origin){let e=new URL(n.origin);return e.protocol+"//"+n.user+"."+(n.domain==="localhost"?"localhost":n.domain)+(e.port?":"+e.port:"")}return"https://"+Kt(n)}function Xf(n){if(n.origin){let e=new URL(n.origin);return{url:(e.protocol==="https:"?"wss://":"ws://")+e.host+"/_connect",hostHeader:Kt(n)+(e.port?":"+e.port:"")}}return{url:"wss://"+Kt(n)+"/_connect",hostHeader:null}}function Zf(n,{device:e,machine:t,box:s}){let o=ct.generateKeyPairSync("x25519"),r=ct.createPublicKey({key:{kty:"OKP",crv:"X25519",x:s},format:"jwk"}),i=ct.diffieHellman({privateKey:o.privateKey,publicKey:r}),a=ct.randomBytes(16),l=Buffer.from(ct.hkdfSync("sha256",i,a,"leftoff-relay-wrap-v1|"+t+"|"+e,32)),c=ct.randomBytes(12),u=ct.createCipheriv("aes-256-gcm",l,c);u.setAAD(Buffer.from(e));let d=Buffer.concat([u.update(Buffer.from(n)),u.final(),u.getAuthTag()]);return{epk:o.publicKey.export({format:"jwk"}).x,salt:Bc(a),iv:Bc(c),ct:Bc(d)}}function Qf(n,{device:e,machine:t,boxPriv:s}){let o=ct.createPublicKey({key:{kty:"OKP",crv:"X25519",x:n.epk},format:"jwk"}),r=ct.diffieHellman({privateKey:s,publicKey:o}),i=Buffer.from(ct.hkdfSync("sha256",r,Gc(n.salt),"leftoff-relay-wrap-v1|"+t+"|"+e,32)),a=Gc(n.ct),l=ct.createDecipheriv("aes-256-gcm",i,Gc(n.iv));return l.setAAD(Buffer.from(e)),l.setAuthTag(a.subarray(a.length-16)),Buffer.concat([l.update(a.subarray(0,a.length-16)),l.final()])}function Vf(n,e){let{sig:t,...s}=n;return s.by=e.machine,{...s,sig:Bc(ct.sign(null,Buffer.from(Mt(s)),e.signPriv))}}function kA(n,e){try{let{sig:t,...s}=n;return ct.verify(null,Buffer.from(Mt(s)),ct.createPublicKey({key:{kty:"OKP",crv:"Ed25519",x:e},format:"jwk"}),Gc(t))}catch{return!1}}function u0(n){return String(n||"Browser").replace(/[\u0000-\u001f<>]/g,"").trim().slice(0,60)||"Browser"}function eh(n,e={}){return n?n==="not_registered"?"this Mac isn’t on "+(e.user||"?")+"."+(e.domain||Bn)+" yet: a Mac that is on it adds this one while it’s awake with Use from anywhere on (your synced folder tells it about this Mac). New name? `leftoff relay signup "+(e.user||"<name>")+"`":n==="key_mismatch"?"the relay knows this Mac by a different key (was it reinstalled?): run `leftoff relay register "+(e.user||"<name>")+" --replace`":n==="wrong_user"?"that address belongs to a different account":/ENOTFOUND|EAI_AGAIN/.test(n)?"can’t find "+(e.user||"")+"."+(e.domain||Bn)+" (no internet, or the relay isn’t deployed yet)":/HTTP 404/.test(n)?"no relay answers there yet: deploy it (docs/relay.md)":n:null}var Bn,l0,SA,c0,Bc,Gc,ea,zc=J(()=>{mf();as();xe();Fi();Fc();Yi();a0();Bn="leftoff.dev",l0=Ge.join(Ge.dirname(wA(import.meta.url)),"..","ui"),SA={".html":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"text/javascript; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".ico":"image/x-icon",".webmanifest":"application/manifest+json; charset=utf-8",".json":"application/json; charset=utf-8"},c0=4*1024*1024,Bc=n=>Buffer.from(n).toString("base64url"),Gc=n=>Buffer.from(String(n||""),"base64url");ea=class extends yA{constructor({engine:e,root:t,port:s,log:o=()=>{},env:r=process.env,identity:i=null}){super(),this.engine=e,this.root=t,this.port=s,this.log=o,this.env=r,this.machine=e?e.machine:i&&i.machine,this.idOverride=i,this.client=null,this.pairs=new Map,this.keys=new Map,this.seen=new Uc,this.innerSecret=ct.randomBytes(24).toString("base64url"),this.lastActivity=null,this.relayDevices=null}get id(){return this.idOverride||this.engine&&this.engine.peers&&this.engine.peers.id||this._id||(this._id=Ln(this.machine))}settings(){return Gn(this.root,this.env)}active(){let e=this.settings();return!!(e.enabled&&e.user)}url(){return this.active()?Xf(this.settings()).url:null}cardUser(){let e=this.settings();return e.enabled&&e.user?e.user:null}boardUrl(){let e=this.settings();return e.user?Qi(e):null}async start(){this.active()&&this.connect(),this.timer=setInterval(()=>this.tick(),Number(this.env.LEFTOFF_RELAY_TICK_MS)||3e4),this.timer.unref()}stop(){clearInterval(this.timer),this.disconnect()}connect(){if(this.client)return;let e=this.settings(),t=Xf(e),s=new Qo({url:t.url,hostHeader:t.hostHeader,id:this.id,user:e.user,log:this.log,helloExtra:()=>({version:X,activeAt:this.activityAt()})});s.onEnvelope=o=>this.engine&&this.engine.peers?this.engine.peers.receive(o,"relay"):{kind:"err",code:"not_allowed"},s.on("frame",o=>this.onFrame(o).catch(r=>this.log("relay: "+r.message))),s.on("welcome",()=>this.onWelcome().catch(()=>{})),s.on("state",()=>this.changed()),this.client=s,s.connect(),this.engine&&this.engine.peers&&this.engine.peers.useRelay(s),this.log("relay: connecting to "+Kt(e)+" (Use from anywhere is on)")}disconnect(){let e=this.client;e&&(this.client=null,this.engine&&this.engine.peers&&this.engine.peers.dropRelay(e),e.close(),this.changed())}changed(){this.engine&&this.engine.emit("relay.updated",{})}async enable({user:e,domain:t}={}){let s=this.settings(),o=e||s.user,r=Hn(o);if(!r.ok)throw Object.assign(new Error(r.code==="reserved"?"“"+o+"” is reserved: pick another name":"The name needs 2–32 lowercase letters, digits or hyphens"),{status:400,code:r.code});let i=s.enabled&&s.user===r.user&&(!t||t===s.domain);return ks(this.root,{enabled:!0,user:r.user,...t?{domain:t}:{}},this.env),(!i||!this.client)&&(this.disconnect(),this.connect()),this.engine&&this.engine.peers&&this.engine.peers.publish(),this.status()}async disable(){return ks(this.root,{enabled:!1},this.env),this.disconnect(),this.engine&&this.engine.peers&&this.engine.peers.publish(),this.status()}async signupStart({user:e,email:t}={}){let s=this.settings(),o=String(e||"").trim().toLowerCase(),r=await Ji({s:{...s,user:o},name:o,sign:this.id.signX});if(r.ok&&r.status==="yours")return this.pendingSignup={user:r.user,email:null,poll:null,expiresAt:null,status:"confirmed",startedAt:new Date().toISOString(),claimed:null,already:!0},await this.signupDone(),this.signupState();if(r.ok&&(r.status==="taken"||r.status==="pending"))throw Object.assign(new Error(r.message),{status:409,code:"taken"});if(!r.ok&&["bad_name","reserved"].includes(r.code))throw Object.assign(new Error(r.message),{status:400,code:r.code});if(!r.ok&&r.code==="closed")throw Object.assign(new Error(r.message),{status:503,code:"signups_closed"});let i=await qi({s:{...s,user:o},name:o,email:t,machine:this.machine,sign:this.id.signX});if(!i.ok)throw Object.assign(new Error(i.message),{status:i.status===429?429:i.status>=400&&i.status<500?i.status:502,code:i.code||"signup_failed"});return this.pendingSignup={user:i.user,email:i.email,poll:i.poll,expiresAt:i.expiresAt,status:i.already?"confirmed":"pending",startedAt:new Date().toISOString(),claimed:i.claimed||null},i.already&&await this.signupDone(),this.signupState()}async signupCheck(){let e=this.pendingSignup;if(!e||e.status!=="pending")return this.signupState();let t=await jc({s:{...this.settings(),user:e.user},user:e.user,poll:e.poll});return t==="confirmed"?(e.status="confirmed",await this.signupDone()):["expired","replaced","unknown"].includes(t)?e.status=t:e.expiresAt&&Date.parse(e.expiresAt)<Date.now()&&(e.status="expired"),this.signupState()}async signupDone(){let e=this.pendingSignup,t=this.settings();ks(this.root,{user:e.user,registeredAt:e.already&&t.user===e.user&&t.registeredAt?t.registeredAt:new Date().toISOString(),via:"signup"},this.env),await this.enable({user:e.user}),this.log("relay: "+e.user+"."+this.settings().domain+(e.already?" already belongs to this Mac":" confirmed by email")+"; Use from anywhere is on"),this.changed()}signupCancel(){return this.pendingSignup=null,this.signupState()}signupState(){let e=this.pendingSignup;return e?{status:e.status,user:e.user,email:e.email,expiresAt:e.expiresAt,startedAt:e.startedAt,claimed:e.claimed,already:!!e.already,host:e.user+"."+this.settings().domain}:{status:"none"}}status(){let e=this.settings(),t=this.client;return{enabled:e.enabled,configured:!!e.user,available:!0,user:e.user,domain:e.domain,host:e.user?Kt(e):null,boardUrl:e.user?Qi(e):null,machine:this.machine,connected:!!(t&&t.connected),connecting:!!(t&&!t.connected),latencyMs:t&&t.connected?t.latencyMs:null,connectedAt:t&&t.connectedAt?new Date(t.connectedAt).toISOString():null,lastError:t?t.lastError:null,problem:t&&!t.connected?eh(t.lastError,e):null,relayMachines:t&&t.relayMachines?t.relayMachines:null,devices:this.devices().length,reserved:zi,registered:!!e.registeredAt,signup:this.signupState()}}async onFrame(e){if(e.t==="static")return this.client.send(this.staticFile(e));if(e.t==="pair")return this.client.send({t:"pair.res",id:e.id,...await this.redeem(e)});if(e.t==="tunnel")return this.client.send({t:"tunnel.res",id:e.id,...await this.tunnel(e.env)})}staticFile(e){let t=String(e.path||"/").replace(/^\/+/,"")||"index.html",s=Ge.join(l0,Ge.normalize(t).replace(/^(\.\.[/\\])+/,""));return!s.startsWith(l0+Ge.sep)||/(^|[/\\])\./.test(t)||!Ss.existsSync(s)||Ss.statSync(s).isDirectory()?{t:"static.res",id:e.id,status:404}:{t:"static.res",id:e.id,status:200,type:SA[Ge.extname(s)]||"application/octet-stream",b64:Ss.readFileSync(s).toString("base64")}}async tunnel(e){let t;try{t=await r0(e,{keyFor:i=>this.keyFor(i),machine:this.machine,seen:this.seen})}catch(i){return{error:{code:i instanceof hn?i.code:"bad_envelope",message:i.message}}}let s=await this.keyFor(e.d),o=this.deviceRecord(e.d),r;return typeof t.p!="string"||!t.p.startsWith("/v1/")||!/^(GET|POST|PUT|PATCH|DELETE)$/.test(String(t.m))?r={s:400,h:{"content-type":"application/json"},b:JSON.stringify({error:{code:"bad_request",message:"only the board API goes through the tunnel"}})}:r=await this.local(t,{device:e.d,name:o?o.name:null}),{env:await i0(s,e,r)}}local(e,{device:t,name:s}){return new Promise(o=>{let r=e.b==null?null:Buffer.from(String(e.b)),i={host:"127.0.0.1:"+this.port,"x-leftoff-relay":this.innerSecret,"x-leftoff-device":t,"x-leftoff-device-name":encodeURIComponent(s||""),accept:"application/json"};r&&(i["content-type"]=e.h&&e.h["content-type"]||"application/json",i["content-length"]=r.length);let a=gA.request({host:"127.0.0.1",port:this.port,method:e.m,path:e.p,headers:i,timeout:6e4},l=>{let c=[],u=0;l.on("data",d=>{u+=d.length,u<=c0&&c.push(d)}),l.on("end",()=>o({s:l.statusCode,h:{"content-type":l.headers["content-type"]||"application/json"},b:u>c0?JSON.stringify({error:{code:"too_large"}}):Buffer.concat(c).toString("utf8")}))});a.on("timeout",()=>a.destroy(new Error("timed out"))),a.on("error",l=>o({s:502,h:{"content-type":"application/json"},b:JSON.stringify({error:{code:"local_error",message:l.message}})})),r&&a.write(r),a.end()})}async createPair({now:e=Date.now()}={}){if(!this.client||!this.client.connected)throw Object.assign(new Error("Not connected to the relay yet"+(this.client&&this.client.lastError?" ("+eh(this.client.lastError,this.settings())+")":"")),{status:409,code:"relay_down"});for(let[a,l]of this.pairs)l.expiresAt<=e&&this.pairs.delete(a);let t;do t=Zw();while(this.pairs.has(t.slice(0,4)));let s=t.slice(0,4),o=e+Jf,r=await this.client.ask({t:"pair.create",pair:s,expiresAt:o});if(!r.ok)throw Object.assign(new Error("The relay refused the code: "+r.code),{status:502});this.pairs.set(s,{code:t,expiresAt:o,tries:0});let i=this.boardUrl()+"/pair.html#"+t;return{code:t,link:i,expiresAt:new Date(o).toISOString(),ttlSec:Math.round(Jf/1e3),qrSvg:Ac(i)}}async cancelPairs(){for(let e of this.pairs.keys())try{await this.client.ask({t:"pair.cancel",pair:e})}catch{}this.pairs.clear()}async redeem(e,t=Date.now()){let s=this.pairs.get(e.pair);if(!s||s.expiresAt<=t)return this.pairs.delete(e.pair),{ok:!1,code:"expired"};if(!await n0(s.code,["browser",e.pair,e.pub],e.mac))return s.tries++,s.tries>=5&&this.pairs.delete(e.pair),this.log("relay: a wrong pairing proof for code "+e.pair+"…"),{ok:!1,code:"bad_code"};this.pairs.delete(e.pair);let o=Xw(12),r=await s0(),i;try{i=(await o0({privateKey:r.privateKey,peerPub:e.pub,code:s.code,device:o,extractable:!0})).raw}catch{return{ok:!1,code:"bad_key"}}let a=await t0(s.code,["mac",e.pair,e.pub,r.pub,o]);return this.saveDevice({id:o,name:u0(e.name),createdAt:new Date(t).toISOString(),pairedOn:this.machine},i),this.keys.set(o,{key:await Hc(i),at:Date.now()}),this.log("relay: paired a new browser, "+u0(e.name)+" ("+o.slice(0,6)+"…)"),this.changed(),{ok:!0,device:o,pub:r.pub,mac:a}}trustedCards(){let e=Zs(this.root),t={};for(let[s,o]of Object.entries(e))if(o.valid){if(s===this.machine){o.card.sign===this.id.signX&&(t[s]=o.card);continue}Mn(this.id,o,{pinNew:!1})==="trusted"&&(t[s]=o.card)}return t[this.machine]={machine:this.machine,box:this.id.boxX,sign:this.id.signX},t}saveDevice(e,t){let s={};for(let[o,r]of Object.entries(this.trustedCards()))r.box&&(s[o]=Zf(t,{device:e.id,machine:o,box:r.box}));Zi(Ge.join(so(this.root),e.id+".json"),Vf({v:1,...e,wraps:s},this.id))}deviceFiles(){let e=[];try{e=Ss.readdirSync(so(this.root)).filter(t=>/^[\w-]+\.json$/.test(t))}catch{return[]}return e.map(t=>oo(Ge.join(so(this.root),t))).filter(t=>t&&t.id)}verified(e){let s=this.trustedCards()[e.by];return!!(s&&kA(e,s.sign))}deviceRecord(e){if(!/^[\w-]{8,64}$/.test(String(e||"")))return null;let t=oo(Ge.join(so(this.root),e+".json"));return t&&this.verified(t)?t:null}async keyFor(e){let t=this.keys.get(e);if(t&&Date.now()-t.at<5e3)return t.key;let s=this.deviceRecord(e);if(!s||s.revokedAt||!s.wraps||!s.wraps[this.machine])return this.keys.delete(e),null;try{let o=await Hc(Qf(s.wraps[this.machine],{device:e,machine:this.machine,boxPriv:this.id.boxPriv}));return this.keys.set(e,{key:o,at:Date.now()}),o}catch{return null}}devices(){let e=new Map((this.relayDevices||[]).map(t=>[t.id,t]));return this.deviceFiles().filter(t=>!t.revokedAt&&this.verified(t)).map(t=>({id:t.id,name:t.name,createdAt:t.createdAt,pairedOn:t.pairedOn,lastSeenAt:(e.get(t.id)||{}).lastSeenAt||null,macs:Object.keys(t.wraps||{}).sort()})).sort((t,s)=>String(s.createdAt).localeCompare(String(t.createdAt)))}async refreshRelayDevices(){if(!this.client||!this.client.connected)return this.relayDevices;try{this.relayDevices=(await this.client.ask({t:"devices.list"})).devices||[]}catch{}return this.relayDevices}async revoke(e){let t=Ge.join(so(this.root),e+".json"),s=oo(t);if(!s||!this.verified(s))throw Object.assign(new Error("No paired device "+e),{status:404,code:"not_found"});if(!s.revokedAt){let{wraps:r,...i}=s;Zi(t,Vf({...i,wraps:{},revokedAt:new Date().toISOString(),revokedOn:this.machine},this.id))}this.keys.delete(e);let o="pending";if(this.client&&this.client.connected)try{await this.client.ask({t:"device.revoke",device:e}),o="done"}catch{}return this.log("relay: revoked "+(s.name||e)),this.changed(),{ok:!0,id:e,relay:o}}activityAt(){let e=0;if(this.engine&&this.engine.sessions)for(let t of this.engine.sessions.values()){if(t.archived||t.machine&&t.machine!==this.machine)continue;let s=Date.parse(t.lastActivityAt||t.startedAt||"")||0;s>e&&(e=s)}return e?new Date(e).toISOString():null}async onWelcome(){let e=this.deviceFiles().filter(t=>t.revokedAt&&this.verified(t)).map(t=>t.id);e.length&&await this.client.ask({t:"devices.sync",revoked:e}).catch(()=>{}),await this.enrollPeers(),this.rewrap(),this.lastActivity=null,this.tick()}async enrollPeers(){if(!this.client||!this.client.connected)return;let e=this.settings(),t=new Set(this.client.relayMachines||[]),s=Zs(this.root);for(let[o,r]of Object.entries(s))if(!(o===this.machine||t.has(o)||!r.valid||r.card.anywhere!==e.user)&&Mn(this.id,r,{pinNew:!1})==="trusted")try{let i=await this.client.ask({t:"machine.add",machine:o,sign:r.card.sign});i.ok?(t.add(o),this.log("relay: added "+o+" to "+e.user+"."+e.domain)):this.log("relay: couldn’t add "+o+": "+i.code)}catch{}this.client.relayMachines=[...t]}rewrap(){let e=this.trustedCards();for(let t of this.deviceFiles()){if(t.revokedAt||!t.wraps||!t.wraps[this.machine]||!this.verified(t))continue;let s=Object.keys(e).filter(o=>!t.wraps[o]&&e[o].box);if(s.length)try{let o=Qf(t.wraps[this.machine],{device:t.id,machine:this.machine,boxPriv:this.id.boxPriv}),r={...t.wraps};for(let i of s)r[i]=Zf(o,{device:t.id,machine:i,box:e[i].box});Zi(Ge.join(so(this.root),t.id+".json"),Vf({...t,wraps:r},this.id)),this.log("relay: "+t.name+" can now reach "+s.join(", "))}catch{}}}tick(){if(this.active()&&!this.client&&this.connect(),!this.active()&&this.client&&this.disconnect(),!this.client||!this.client.connected)return;let e=this.activityAt();e&&e!==this.lastActivity&&(this.lastActivity=e,this.client.send({t:"activity",at:e})),this.enrollPeers().catch(()=>{}),this.rewrap(),this.refreshRelayDevices().catch(()=>{})}}});var Mc={};tn(Mc,{SUMMARIZER_FLAG:()=>nh,daemonHealth:()=>dr,flagBool:()=>dt,ghSetupStep:()=>S0,globalBinPath:()=>sh,lengthFlow:()=>y0,linkFolder:()=>Vc,localModelFlow:()=>Xc,mergeSetupConfig:()=>Jc,ollamaModelFlow:()=>g0,patchDaemonConfig:()=>Yc,relaySignupStep:()=>w0,runSetup:()=>rh,showTestResult:()=>ao,startDaemonDetached:()=>Wc,testFlow:()=>fr,writeConfigFile:()=>qc});import nt from"node:fs";import io from"node:os";import Pe from"node:path";import{spawn as ur,spawnSync as ro}from"node:child_process";function dt(n,e){if(n["no-"+e])return!1;if(n[e]!==void 0)return vA(n[e])?!1:bA(n[e])||n[e]===""?!0:!!n[e]}function xA(n,e){let t=e;return{c:t,line:(s="")=>n.write(s+`
268
268
  `),ok:s=>n.write(t.green("✔")+" "+s+`
269
269
  `),warn:s=>n.write(t.yellow("!")+" "+s+`
270
270
  `),fail:s=>n.write(t.red("✖")+" "+s+`
@@ -273,40 +273,40 @@ Settings → Sync on the board, or \`leftoff move --delete-backup\`. Leftoff nev
273
273
  `),step:(s,o,r)=>n.write(`
274
274
  `+t.bold(t.magenta(`[${s}/${o}]`))+" "+t.bold(r)+`
275
275
  `),cmd:s=>n.write(" "+t.cyan("$ "+s)+`
276
- `)}}function eh(n,e,t,s){let o=i=>String(i).replace(/\x1b\[[0-9;]*m/g,""),r=Math.min(76,Math.max(o(t).length+4,...s.map(i=>o(i).length+4)));n.write(e.dim("╭─ ")+e.bold(t)+" "+e.dim("─".repeat(Math.max(0,r-o(t).length-4))+"╮")+`
276
+ `)}}function oh(n,e,t,s){let o=i=>String(i).replace(/\x1b\[[0-9;]*m/g,""),r=Math.min(76,Math.max(o(t).length+4,...s.map(i=>o(i).length+4)));n.write(e.dim("╭─ ")+e.bold(t)+" "+e.dim("─".repeat(Math.max(0,r-o(t).length-4))+"╮")+`
277
277
  `);for(let i of s)n.write(e.dim("│")+" "+i+" ".repeat(Math.max(0,r-o(i).length-2))+e.dim("│")+`
278
278
  `);n.write(e.dim("╰"+"─".repeat(r-1)+"╯")+`
279
- `)}function c0(n,e=66){let t=[],s="";for(let o of String(n).split(/\s+/))(s+" "+o).trim().length>e?(t.push(s),s=o):s=(s+" "+o).trim();return s&&t.push(s),t}function ro(n,e,t,s){if(!t.ok){n.write(e.red("✖")+" Test checkpoint failed with "+s+": "+(t.error||"no answer")+`
280
- `);return}let o=[];for(let[a,l]of[["Doing",t.doing],["Stopped at",t.stoppedAt],["Next",t.next]]){let c=c0(l||"",60);o.push(e.bold((a+":").padEnd(12))+(c[0]||""));for(let u of c.slice(1))o.push(" ".repeat(12)+u)}let r=t.generator||{},i=r.inputTokens!=null?" · "+r.inputTokens+" → "+r.outputTokens+" tokens":"";eh(n,e,"Sample checkpoint · "+s+" · "+Ui(t.ms)+i,o),t.parseOk||n.write(e.yellow("!")+` The model didn't follow the 3-line format; a bigger model usually does.
281
- `)}async function dr(n,e,t=fetch){try{let s=await t("http://127.0.0.1:"+n+"/v1/health",{headers:{authorization:"Bearer "+(e||"")},signal:AbortSignal.timeout(1500)});return s.status===401?{up:!0,auth:!1}:s.ok?{up:!0,auth:!0,...await s.json()}:{up:!1}}catch{return{up:!1}}}async function $c(n,e,t,s=fetch){let o=await s("http://127.0.0.1:"+n+"/v1/config",{method:"PATCH",headers:{authorization:"Bearer "+e,"content-type":"application/json"},body:JSON.stringify(t),signal:AbortSignal.timeout(4e3)});if(!o.ok)throw new Error("HTTP "+o.status);return o.json()}function Yc({root:n,bin:e=Ke,env:t=process.env}){let s=be(n);et.mkdirSync(s.root,{recursive:!0});let o=et.openSync(Re.join(n,".daemon.log"),"a"),r=ur(process.execPath,[e,"serve"],{detached:!0,stdio:["ignore",o,o],env:{...t,CHECKPOINTS_DIR:n}});return r.unref(),et.closeSync(o),et.writeFileSync(Re.join(n,".daemon.pid"),String(r.pid)+`
282
- `),r.pid}function yA(n){let e=process.platform==="darwin"?["open",[n]]:process.platform==="win32"?["cmd",["/c","start","",n]]:["xdg-open",[n]];try{let t=ur(e[0],e[1],{detached:!0,stdio:"ignore"});return t.on("error",()=>{}),t.unref(),!0}catch{return!1}}function zc(n,{summarizer:e,machine:t,hostname:s=oo.hostname(),port:o,summaryLength:r}={}){let i=ua({hostname:s,cfg:{}}),a=n&&typeof n=="object"?JSON.parse(JSON.stringify(n)):{};if(e){let l=a.summarizer&&a.summarizer.maxInputChars?{maxInputChars:a.summarizer.maxInputChars}:{};a.summarizer={provider:e.provider,model:e.model??null,baseUrl:e.baseUrl??null,apiKeyEnv:e.apiKeyEnv??null,...e.apiKeyFile?{apiKeyFile:e.apiKeyFile}:{},maxInputChars:24e3,...l},delete a.summarizer.mode}return t!==void 0&&(a.machineNames={...a.machineNames||{}},t&&t!==s&&t!==i?a.machineNames[s]=t:delete a.machineNames[s]),o&&(a.port=Number(o)),r&&(a.summary={...a.summary||{},length:r}),a}function Wc(n,e){let t=be(n),s=t.config+".tmp-"+process.pid;return et.writeFileSync(s,JSON.stringify(e,null,2)+`
283
- `),et.renameSync(s,t.config),t.config}function Jc(n){let e=wa(),t=mp(n);if(Re.resolve(n)===e)return Mi("CHECKPOINTS_DIR",null),{how:"default",pointer:t};let s=null;try{s=et.lstatSync(e)}catch{}if(s&&s.isSymbolicLink()){if(Re.resolve(Re.dirname(e),et.readlinkSync(e))===Re.resolve(n))return Mi("CHECKPOINTS_DIR",null),{how:"symlink",pointer:t,already:!0};et.unlinkSync(e),s=null}return s?(Mi("CHECKPOINTS_DIR",n),{how:"settings-env",pointer:t}):(et.symlinkSync(n,e,"dir"),Mi("CHECKPOINTS_DIR",null),{how:"symlink",pointer:t})}function Qf(n=process.env){let e=so(process.platform==="win32"?"npm.cmd":"npm",["prefix","-g"],{encoding:"utf8",env:n});if(e.status!==0)return null;let t=e.stdout.trim(),s=process.platform==="win32"?Re.join(t,"node_modules"):Re.join(t,"lib","node_modules");for(let o of ci)for(let r of["leftoff.js","checkpoints.js"]){let i=Re.join(s,o,"bin",r);if(et.existsSync(i))return i}return null}function wA(n,e,t,s){t.close(),s.write(`
284
- `);let o=so(n,e,{stdio:"inherit"});return s.write(`
285
- `),o.status===0}async function qc(n){let{p:e,u:t,out:s,flags:o,fetchImpl:r,env:i}=n,a=t.c,l=(o["ollama-url"]||(o["base-url"]&&n.wantProvider!=="openai-compatible"?o["base-url"]:null)||pt).replace(/\/$/,""),c=(o["lmstudio-url"]||(o["base-url"]&&n.wantProvider==="openai-compatible"?o["base-url"]:null)||rn).replace(/\/$/,""),u=it("Looking for local model servers…",{out:s,colors:a}),[d,f]=await Promise.all([Gt({baseUrl:l,fetchImpl:r,env:i}),n.wantProvider==="ollama"?{running:!1,models:[]}:Tn({baseUrl:c,fetchImpl:r})]);u.stop(),d.running?t.ok("Ollama "+(d.version?"v"+d.version+" ":"")+"running at "+d.baseUrl+a.dim(" · "+d.models.length+" model"+(d.models.length===1?"":"s")+" pulled")):d.binary?t.warn("Ollama is installed ("+lt(d.binary)+") but not running"):t.info("Ollama: not found"),f.running?t.ok("LM Studio / OpenAI-compatible server at "+f.baseUrl+a.dim(" · "+f.models.length+" loaded")):n.wantProvider!=="ollama"&&t.info("LM Studio / OpenAI-compatible server: not found at "+c);let h;if(n.wantProvider==="openai-compatible"?h="openai-compatible":n.wantProvider==="ollama"?h="ollama":d.running&&f.running?h=await e.select("Which one should write checkpoints?",[{label:"Ollama",value:"ollama",hint:"pull and switch models from here"},{label:"LM Studio / OpenAI-compatible",value:"openai-compatible",hint:"uses a model you load there"}],0):f.running&&!d.running?h="openai-compatible":h="ollama",h==="openai-compatible")return a0(n,f,c);if(!d.running){if(d.binary){let p=Jr(process.platform,d.binary);if(p&&await e.confirm("Start Ollama now? ("+p.shell+")",!0)){if(p.detached){let g=ur(p.cmd[0],p.cmd.slice(1),{detached:!0,stdio:"ignore"});g.on("error",()=>{}),g.unref()}else so(p.cmd[0],p.cmd.slice(1),{stdio:"ignore"});let m=it("Waiting for Ollama to come up…",{out:s,colors:a});d=await Co({baseUrl:l,fetchImpl:r,timeoutMs:2e4}),d.running?m.succeed("Ollama is up at "+d.baseUrl):m.fail("Ollama didn't answer at "+l+" within 20 s")}}else{if(n.yes&&!ct(o,"install-ollama"))throw new Error("Ollama isn't installed. Install it (https://ollama.com/download) or pass --install-ollama, then re-run");t.line(),t.line(" No local model server yet. "+a.bold("Ollama")+" is the easiest: one install, then models download from here.");let p=om(process.platform,i),m=await e.select("Install Ollama?",[...p.map(k=>({label:k.label,value:k.id,hint:k.shell+(k.note?" · "+k.note:"")})),{label:"I use LM Studio (or another OpenAI-compatible server)",value:"lmstudio",hint:c},{label:"Back: choose a different summarizer",value:"back"}],0);if(m==="back")return null;if(m==="lmstudio")return a0(n,f,c);let g=p.find(k=>k.id===m);if(g.cmd){if(t.line(" This runs:"),t.cmd(g.shell),!await e.confirm("Run it now?",!0))return null;if(!wA(g.cmd[0],g.cmd.slice(1),e,s))return t.fail("The Ollama installer exited with an error (see above)."),null;if(t.ok("Ollama installed"),g.id==="brew"){let k=Jr(process.platform,Rs(i));if(k){let w=ur(k.cmd[0],k.cmd.slice(1),{detached:k.detached,stdio:"ignore"});w.on("error",()=>{}),w.unref()}}}else{if(t.line(" Install it with:"),t.cmd(g.shell),g.url&&t.note("or download it from "+g.url),n.yes)throw new Error("Ollama isn't installed; install it and re-run setup");await e.ask("Press Enter once Ollama is installed and running","")}let S=it("Waiting for Ollama…",{out:s,colors:a});if(d=await Co({baseUrl:l,fetchImpl:r,timeoutMs:3e4}),!d.running&&d.binary){let k=Jr(process.platform,d.binary);if(k){let w=ur(k.cmd[0],k.cmd.slice(1),{detached:!0,stdio:"ignore"});w.on("error",()=>{}),w.unref(),d=await Co({baseUrl:l,fetchImpl:r,timeoutMs:2e4})}}d.running?S.succeed("Ollama is up at "+d.baseUrl):S.fail("Ollama isn't answering at "+l)}if(!d.running){if(n.yes)throw new Error("Ollama isn't running at "+l+" (start it with `ollama serve`, or pass --install-ollama)");return t.note("Start Ollama (`ollama serve` or the app) and run `leftoff models` to finish this later."),null}}return u0(n,d)}async function u0(n,e){let{p:t,u:s,out:o,flags:r,fetchImpl:i}=n,a=s.c,l=Eo(e.models,{current:n.currentModel}),c=r.model?Pa(r.model):sm(l);s.line(),s.line(" "+a.bold("Models for 3-line checkpoints")+a.dim(" (sizes from ollama.com/library, "+_o+")"));let u=l.map(p=>({label:p.tag.padEnd(13)+" "+a.dim((p.curated?Ua(p,Ht):(p.sizeBytes?Ht(p.sizeBytes)+" · ":"")+"on this machine").padEnd(30))+" "+(p.recommended?a.yellow("★ recommended")+" ":"")+(p.installed?a.green("✔ pulled"):""),detail:p.curated?p.note:null,value:p.tag}));u.push({label:"Other tag…",value:"__other__",hint:"any ollama.com/library tag, e.g. phi4-mini"});let d=u.findIndex(p=>We(p.value,c)),f;d<0&&r.model?f=String(r.model).trim():(f=await t.select("Which model?",u,Math.max(0,d)),f==="__other__"&&(f=await t.ask("Model tag","",{validate:p=>/^[\w.\-/]+(:[\w.\-]+)?$/.test(p)?null:"Use a tag like llama3.2:3b"})));let h=l.find(p=>We(p.tag,f));if(!h||!h.installed){let p=h&&h.sizeBytes?" ("+Ht(h.sizeBytes)+")":"";if(ct(r,"pull")===!1)throw new Error(f+" isn't pulled and --no-pull was given");if(!await t.confirm("Download "+f+p+" now?",!0))return null;let m=Tc("pull "+f,{out:o,colors:a}),g=Date.now();o.write(a.dim(" pulling "+f+" from ollama.com · Ctrl-C to stop (a re-run resumes)")+`
286
- `);try{await t.interruptible(S=>Io({tag:f,baseUrl:e.baseUrl,fetchImpl:i,signal:S,onProgress:k=>{let w=(Date.now()-g)/1e3,x=w>.5&&k.completed?Ht(k.completed/w)+"/s":"",v=k.total?Ht(k.completed)+" / "+Ht(k.total)+(x?" · "+x:""):k.status;m.update(k.fraction,v)}}))}catch(S){if(S.cancelled||(m.fail(S.message),n.yes))throw S;return null}m.done("Pulled "+f+a.dim(" in "+Ui(Date.now()-g)))}else s.ok(f+" is already pulled");return{provider:"ollama",model:f,baseUrl:e.baseUrl===pt?null:e.baseUrl}}async function a0(n,e,t){let{p:s,u:o,flags:r,fetchImpl:i}=n,a=o.c;if(!e.running){let c=(await s.ask("Server URL (OpenAI-compatible, ends in /v1)",t)).replace(/\/$/,"");if(e=await Tn({baseUrl:c,fetchImpl:i}),!e.running){if(o.fail("Nothing answered at "+c+"/models"),o.note("In LM Studio: Developer tab → Start Server, and load a model."),n.yes)throw new Error("no OpenAI-compatible server at "+c);return null}}for(;!e.models.length;){if(o.warn("The server is up but has no model loaded."),n.yes)throw new Error("no model loaded at "+e.baseUrl);if(!await s.confirm("Load one in LM Studio, then check again?",!0))return null;e=await Tn({baseUrl:e.baseUrl,fetchImpl:i})}let l=r.model;if(!l||!e.models.includes(l)){let c=Math.max(0,e.models.findIndex(u=>u===n.currentModel));l=await s.select("Which loaded model?",e.models.map(u=>({label:u,value:u})),c)}return o.ok("Using "+a.bold(l)+" at "+e.baseUrl),{provider:"openai-compatible",model:l,baseUrl:e.baseUrl===rn?null:e.baseUrl}}async function fr(n,e,t){let{p:s,u:o,out:r,fetchImpl:i}=n,a=it("Writing a sample checkpoint with "+t+"…"+(e.provider==="ollama"?o.c.dim(" (the first run loads the model)"):""),{out:r,colors:o.c}),l=await Ps(e,{fetchImpl:i});return l.ok?a.succeed("It works."):a.fail("The test didn't work."),ro(r,o.c,l,t),l}async function l0(n,e,{defaultEnv:t,secretName:s,testPrompt:o,testLabel:r}){let{p:i,u:a,flags:l,env:c}=n,u=n.currentSummarizer||{},d=u.provider===e.provider,f=await i.ask("Env var that holds the key",l["api-key-env"]||d&&u.apiKeyEnv||t,{validate:k=>/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)?null:"That isn't an env var name"});e.apiKeyEnv=f;let h=".secret/"+s,p=!!c[f],m=An(e.provider,{apiKeyEnv:"__none__",apiKeyFile:d&&u.apiKeyFile?u.apiKeyFile:h,root:n.root},{});p?a.ok("$"+f+" is set in this shell"):a.warn("$"+f+" isn't set in this shell");let g=p?c[f]:null,S=k=>{e.apiKeyFile=h,(n.pendingSecrets=n.pendingSecrets||[]).push({name:s,value:k})};if(!g&&m?(a.ok("A saved key is already in "+h),e.apiKeyFile=d&&u.apiKeyFile||h,g=m):!g&&!n.yes&&(g=await i.ask("Paste the key (hidden; Enter to skip)","",{secret:!0}),g&&S(g)),g&&p&&!n.yes&&await i.confirm("Also save it to "+h+" (mode 0600) so the background service can use it?",!0)&&S(g),g||a.note("Set $"+f+" before starting the daemon, or re-run setup to save a key."),a.note("The key itself never goes into config.json."),g&&ct(l,"test")!==!1&&(n.yes||await i.confirm(o,!0))){process.env.__CHECKPOINTS_TEST_KEY=g;try{await fr(n,{...e,apiKeyFile:void 0,apiKeyEnv:"__CHECKPOINTS_TEST_KEY"},r)}finally{delete process.env.__CHECKPOINTS_TEST_KEY}}return g}async function SA(n,e){let{p:t,u:s,flags:o,fetchImpl:r,env:i}=n,a=s.c;if(o.model)return String(o.model);let l=String(o["base-url"]||i.CHECKPOINTS_CLOUD_URL||Ds),c=n.yes?null:it("Fetching the cloud model list from "+l.replace(/^https?:\/\//,"")+"…",{out:n.out,colors:a}),u=await Wr({baseUrl:l,fetchImpl:r});c&&(u.ok?c.succeed(u.models.length+" cloud models available"):c.fail("Couldn't fetch the list ("+u.error+"); showing the usual picks"));let d=Fa(u.models,{current:e}),f=d.find(S=>S.current&&S.available!==!1)||d.find(S=>S.recommended&&S.available!==!1)||d.find(S=>S.available!==!1)||d[0];if(n.yes)return f.tag;let h=d.filter(S=>S.curated||S.current),p=h.map(S=>({label:S.tag+(S.recommended?" ★":""),value:S.tag,hint:(S.note||"")+(S.available===!1?" · not in today's list":"")})),m=d.filter(S=>!h.includes(S)).map(S=>S.tag);p.push({label:"Other model",value:"__other",hint:m.length?m.slice(0,6).join(", ")+(m.length>6?", …":""):"any tag from ollama.com/search?c=cloud"});let g=await t.select("Which cloud model?",p,Math.max(0,p.findIndex(S=>S.value===f.tag)));return g!=="__other"?g:t.ask("Model name (as ollama.com/api/tags lists it)",e||f.tag,{validate:S=>/^[\w.:/-]+$/.test(S)?null:"That doesn't look like a model tag"})}async function kA(n){let{p:e,u:t,flags:s,env:o}=n,r=t.c,i=n.currentSummarizer||{},a=Zf[String(i.provider||"auto").toLowerCase()]||"claude",l=s.summarizer?Zf[String(s.summarizer).toLowerCase()]:null;if(s.summarizer&&!l)throw new Error("unknown --summarizer="+s.summarizer+" (claude, ollama, lmstudio, ollama-cloud, anthropic, heuristic)");let c=[{label:"Claude, in the session",value:"claude",hint:"no key; uses your Claude Code login"},{label:"Local model",value:"local",hint:"free and private · Ollama or LM Studio"},{label:"Ollama cloud",value:"ollama-cloud",hint:"hosted gpt-oss, Gemma… · needs an ollama.com API key"},{label:"Anthropic API key",value:"anthropic",hint:"Haiku, about $0.007 per checkpoint"},{label:"No model",value:"heuristic",hint:"last prompt + last reply, offline"}],u=d=>d==="ollama"||d==="openai-compatible"?"local":d;for(;;){let d=u(l||a),f=await e.select("Who writes the 3-line checkpoints?",c,Math.max(0,c.findIndex(p=>p.value===d)));if(f==="claude"){let p={provider:"claude",model:"haiku"};return n.claude.found&&(ct(s,"test")===!0||!n.yes&&await e.confirm("Run a test checkpoint with `claude -p` now? (~10 s, uses your Claude login)",!1))?await fr(n,p,"claude -p (haiku)"):n.claude.found||t.note('Without the claude CLI the daemon falls back to "no model" for checkpoints it writes on its own.'),p}if(f==="heuristic")return{provider:"heuristic",model:null};if(f==="anthropic"){let p={provider:"anthropic",model:s.model||"haiku"};return await l0(n,p,{defaultEnv:"ANTHROPIC_API_KEY",secretName:"anthropic-api-key",testPrompt:"Test it now? (one Haiku call, well under a cent)",testLabel:"Claude Haiku (API)"}),p}if(f==="ollama-cloud"){let p=await SA(n,i.provider==="ollama-cloud"?i.model:null),m={provider:"ollama-cloud",model:p,...s["base-url"]?{baseUrl:String(s["base-url"])}:{}};return t.note("Your transcripts' last few turns go to ollama.com for each checkpoint. Keys: https://ollama.com/settings/keys"),await l0(n,m,{defaultEnv:"OLLAMA_API_KEY",secretName:"ollama-api-key",testPrompt:"Test it now? (one call to "+p+" on ollama.com)",testLabel:p+" (Ollama cloud)"}),m}let h=await qc({...n,wantProvider:l==="ollama"||l==="openai-compatible"?l:null});if(!h){if(n.yes)throw new Error("local model setup didn't finish");continue}if(ct(s,"test")!==!1){let p=await fr(n,h,h.model);if(!p.ok&&!n.yes&&await e.select("What now?",[{label:"Keep this model anyway",value:"keep"},{label:"Pick something else",value:"again"}],1)==="again")continue;if(!p.ok&&n.yes)throw new Error("test checkpoint failed: "+p.error)}return h}}async function d0(n,e={}){let{p:t,u:s,flags:o}=n,r=s.c,i=o["summary-length"]||o.length;if(i&&i!==!0){let u=gn(i,{strict:!0});if(!u)throw new Error("unknown --summary-length="+i+" (short, informative, long)");return s.ok("Checkpoint length: "+u),u}let a=gn(e.summary&&e.summary.length),l=Dw.map(u=>({label:u.label+(u.recommended?" (recommended)":""),value:u.key,hint:u.approx,detail:u.blurb})),c=Math.max(0,l.findIndex(u=>u.value===a));for(;;){let u=await t.select("How much should each checkpoint say?",l,c),d=Nc(Lw[u]).split(`
287
- `).flatMap(f=>c0(f,70));if(eh(n.out,r,"Example · "+u,d),n.yes||await t.confirm("Use "+u+" checkpoints? (change any time in Settings → Models & summaries; /cp --short or --long overrides once)",!0))return u;c=l.findIndex(f=>f.value===u)}}async function th({flags:n={},input:e=process.stdin,out:t=process.stdout,env:s=process.env,fetchImpl:o=(...r)=>fetch(...r)}={}){let r=!!(n.yes||n.y),i=hs(fs(t)),a=gA(t,i),l=i,c=new wt({input:e,output:t,yes:r,colors:i}),u={p:c,u:a,out:t,flags:n,env:s,fetchImpl:o,yes:r},d=()=>{t.write(`
288
- `),a.fail("Setup cancelled. Re-run `leftoff setup` any time; finished steps are skipped."),process.exit(130)};r&&process.on("SIGINT",d);let f=5;try{if(a.line(),a.line(l.bold(" ◆ Leftoff setup")+l.dim(" v"+X)),a.line(l.dim(" Track every Claude Code session and get a 3-line checkpoint when one goes quiet.")),a.line(l.dim(" About two minutes. Ctrl-C any time; re-running is safe.")),a.step(1,f,"Preflight"),Number(process.versions.node.split(".")[0])<20)throw new Error("Node "+process.versions.node+" is too old; Leftoff needs Node 20 or newer");a.ok("Node "+process.versions.node);let p=Tw(s);u.claude=p,p.found?p.modsSupported?a.ok("Claude Code "+(p.version||"?")+l.dim(" · "+lt(p.bin))):a.warn("Claude Code "+(p.version||"(version unknown)")+" · mods need "+Hi+"+ (update with `claude update`); settings hooks work on any version"):a.warn("Claude Code's `claude` command isn't on your PATH");let m=Ke;if(Mf(lr)){let Y=Qf(s);if(Y)m=Y,a.ok("leftoff is installed globally"+l.dim(" · "+lt(Y)));else{a.warn("Running from a temporary npx copy.");let ne=ct(n,"global"),se=Lf(lr)?ot:"github:"+_c;if(ne!==!1&&(ne===!0||!r&&await c.confirm("Install the `leftoff` command globally (npm install -g "+se+")?",!0))){let T=it("npm install -g "+se,{out:t,colors:l}),L=so(process.platform==="win32"?"npm.cmd":"npm",["install","-g",se],{encoding:"utf8",env:s}),j=L.status===0?Qf(s):null;j?(m=j,T.succeed("Installed `leftoff` globally")):T.fail("Global install failed: "+((L.stderr||"").trim().split(`
289
- `).pop()||"exit "+L.status)+" (continuing from the npx copy)")}m===Ke&&a.note("Settings hooks and the login service will point at this npx copy. If npm clears its cache, re-run setup (or pass --global).")}}if(u.bin=m,!s.CHECKPOINTS_NO_UPDATE_CHECK)try{let{installInfo:Y,lsRemote:ne,writeStamp:se,DEFAULT_SOURCE:T}=await Promise.resolve().then(()=>(fi(),Rg)),L=await Y({env:s});if(L.kind!=="clone"&&L.kind!=="npm"&&!L.sha){let j=await ne(T,{env:s,timeoutMs:8e3});j.ok&&se({sha:j.sha,source:T})}}catch{}let g=Z(),S=et.existsSync(be(g).config);S&&a.info("Found an existing setup in "+lt(g)+l.dim(" · your answers from last time are the defaults")),S||a.note("Tip: a synced folder (iCloud Drive, Dropbox, Syncthing) puts every machine on one board.");let k=await c.ask("Where should checkpoints live?",lt(Xf(n.dir||g)),{validate:Y=>Y&&Re.isAbsolute(Xf(Y))?null:"Use an absolute path or ~/…"}),w=Re.resolve(Xf(k));u.root=w;let x=yn(w)||(S&&w===g?yn(g):null)||{},v=Q(w),b=oo.hostname(),y=await c.ask("Name for this machine",n.machine||de(v),{hint:"(shown on each card)",validate:Y=>/^[\w .-]{1,40}$/.test(Y)?null:"Letters, numbers, spaces, dots and dashes; up to 40"});if(Re.resolve(w)!==Re.resolve(g)&&Rn(g)&&!n["no-move"]){let{planMove:Y,executeMove:ne,tilde:se}=await Promise.resolve().then(()=>(Qs(),kw)),{moveHooks:T}=await Promise.resolve().then(()=>(Zi(),Vc)),{loadIdentity:L}=await Promise.resolve().then(()=>(os(),yf)),j=null;try{j=L(y)}catch{}let $=Y(g,w,{machine:y,identity:j});if(!$.ok){for(let ge of[...$.errors,...$.collisions])a.warn(ge.message);throw new Error("Can’t "+($.merge?"join ":"move to ")+se(w)+" yet (see above). Nothing changed.")}if(a.info(($.merge?"That folder already has Leftoff data"+($.warnings.length?" ("+$.warnings.map(ge=>ge.message).join(" ")+")":"")+". ":"")+$.counts.total+" files in "+se(g)+"."),await c.confirm(($.merge?"Merge":"Move")+" your sessions into "+se(w)+"? (copied and checked; the old folder is kept as "+se($.backup)+")",!0)){let ge=Q(g),kt=await ne($,{hooks:T({from:g,cfg:ge,token:ve(g),noRestart:!0,env:s}),machine:y,say:pn=>a.line(" "+l.dim("· "+pn))});if(!kt.ok)throw new Error("The move stopped ("+kt.error+"); put back, still using "+se(g)+".");a.ok(($.merge?"Merged":"Moved")+": "+kt.copied+" copied, "+kt.verified+" verified. Backup: "+se(kt.backup))}}a.step(2,f,"Claude Code mod");let _=pc();Ew(_)&&a.warn('"disableAllHooks": true is set in your Claude settings, so neither mods nor settings hooks run until you remove it.');let A=n.capture?String(n.capture):ct(n,"hooks")===!0?"hooks":ct(n,"mod")===!1?"none":null,O=[];p.found&&O.push({label:"Install the mod",value:"mod",hint:p.modsSupported?"recommended · /cp, /cp-name and the /leftoff pane":"needs Claude Code "+Hi+"+"}),O.push({label:"Settings hooks only",value:"hooks",hint:"works on any version; no slash commands or pane"+(p.found?"":" · start working once Claude Code is installed")}),O.push({label:"Skip for now",value:"none"});let I=A||(p.found&&p.modsSupported?"mod":"hooks");p.found||(a.line(" Install Claude Code first ("+l.underline("https://code.claude.com/docs")+"), then re-run setup to add the mod."),I==="mod"&&(I="hooks")),I=await c.select("How should Leftoff see your sessions?",O,Math.max(0,O.findIndex(Y=>Y.value===I)));let N=!1;if(I==="mod"){let Y=_w({override:n.marketplace}),ne="",se=it("Checking installed plugins…",{out:t,colors:l}),T=Pf({bin:p.bin,source:Y,env:s,onStep:L=>{ne=L,se.update("Running "+l.cyan(L))}});if(T.ok&&T.migrated)se.succeed("Mod moved to its new name: "+Me+" (was "+ps+")");else if(T.ok&&T.already&&T.state&&!T.state.enabled)se.succeed("Mod was installed but disabled; enabled it ("+Me+")");else if(T.ok&&T.already)se.succeed("Mod already installed and enabled ("+Me+")");else if(T.ok){se.succeed("Mod installed: "+Me);for(let L of T.steps)a.cmd("claude "+L.args.map(j=>lt(j)).join(" "))}else se.fail("`"+ne+"` failed: "+String(T.error).split(`
290
- `).slice(-2).join(" "));N=T.ok,T.ok?p.modsSupported||a.warn("The mod is installed but loads only on Claude Code "+Hi+"+."):(/github|clone|auth|permission|denied|not found/i.test(String(T.error))&&Y===_c&&a.note("The repo is private: Claude Code clones it with your git credentials (try `gh auth login`), or run setup from a local clone."),I=await c.confirm("Use settings hooks instead? (same tracking, no slash commands)",!0)?"hooks":"none"),N&&Ni()&&await c.confirm("Remove the older settings hooks? The mod covers them.",!0)&&(mc(),a.ok("Removed the settings hooks"))}if(I==="hooks"){let Y=Ni(),ne=er(void 0,{bin:m});a.ok((Y?"Settings hooks refreshed in ":"Settings hooks added to ")+lt(ne)+l.dim(" (backup saved next to it)"))}I==="none"&&a.info("Skipped. Add it later with `leftoff setup`."),a.step(3,f,"Summarizer"),u.currentSummarizer=v.summarizer,u.currentModel=v.summarizer&&v.summarizer.model;let M=await kA(u),z=await d0(u,v);a.step(4,f,"Save and start"),Oe(w);for(let Y of u.pendingSecrets||[])Sa(Y.name,Y.value,w,s),a.ok("Key saved to "+lt(Re.join(en(s),Y.name))+l.dim(" (0600, on this Mac only)"));let U=zc(x,{summarizer:M,machine:y,hostname:b,port:n.port,summaryLength:z});Wc(w,U),a.ok("Config saved: "+lt(be(w).config)+l.dim(" · summarizer "+M.provider+(M.model?" / "+M.model:"")+" · "+z+" checkpoints · machine "+y));let E=Jc(w);E.how==="symlink"&&!E.already?a.ok("Linked ~/checkpoints → "+lt(w)+l.dim(" (so the mod and hooks find it)")):E.how==="settings-env"&&a.ok("Set CHECKPOINTS_DIR in your Claude settings so the mod and hooks find "+lt(w));let P=Q(w),W=P.port,oe=ve(w),K={...s,CHECKPOINTS_DIR:w},pe=await dr(W,oe,o),Ie=ct(n,"service"),Se=Ie!==void 0?Ie:!r&&process.platform!=="win32"?await c.confirm("Start Leftoff automatically when you log in?",!0):!1;if(Se){let Y=Nt(),ne=gc({root:w,bin:m,dryRun:!!n["service-dry-run"]});ne&&a.ok("Moved the login service to its new name: "+lt(ne.to)+l.dim(" (stopped and removed "+lt(ne.from)+")")),et.mkdirSync(Re.dirname(Y),{recursive:!0});let se=Ln({root:w,bin:m}),T=!et.existsSync(Y)||et.readFileSync(Y,"utf8")!==se;et.writeFileSync(Y,se),a.ok((T?"Login service written: ":"Login service up to date: ")+lt(Y));let L=!0;if(!n["service-dry-run"])for(let j of Xs()){let $=so(j[0],j.slice(1),{encoding:"utf8"});if($.status!==0&&j[1]!=="unload"){L=!1,a.warn("`"+j.join(" ")+"` failed: "+(($.stderr||$.error&&$.error.message||"").trim().split(`
291
- `)[0]||"exit "+$.status));break}}if(L?a.ok("Service started"):a.note("Starting the daemon directly instead; the service file stays for next login."),L)for(let j=0;j<20&&!(pe=await dr(W,oe,o)).up;j++)await i0(250)}if(ct(n,"start")===!1)a.info("Not starting the daemon (--no-start). Start it with `leftoff serve`.");else if(pe.up&&pe.auth){try{await $c(W,oe,{summarizer:U.summarizer,machineNames:U.machineNames||{},summary:{length:z}},o),a.ok("Daemon already running on 127.0.0.1:"+W+l.dim(" · config updated live"))}catch(Y){a.warn("Daemon is running but didn't take the new config ("+Y.message+"); restart it to apply.")}pe.machine&&pe.machine!==y&&a.note("Restart the daemon to show the new machine name.")}else if(pe.up&&!pe.auth)a.warn("Something else (another Leftoff folder?) is on port "+W+'. Stop it, or set "port" in config.json, then run `leftoff serve`.');else{let Y=it("Starting the daemon…",{out:t,colors:l});Yc({root:w,bin:m,env:K});for(let ne=0;ne<40&&!(pe=await dr(W,oe,o)).up;ne++)await i0(250);pe.up?(Y.succeed("Daemon running on 127.0.0.1:"+W),Se||a.note("Runs in the background until reboot; `leftoff setup --service` starts it at login.")):Y.fail("The daemon didn't start; see "+lt(Re.join(w,".daemon.log")))}if(a.step(5,f,"Check"),ct(n,"doctor")!==!1){let Y=so(process.execPath,[m,"doctor"],{encoding:"utf8",env:{...K,NO_COLOR:"1"}});for(let ne of(Y.stdout||"").trim().split(`
292
- `).filter(Boolean).map(se=>se.split(oo.homedir()+Re.sep).join("~"+Re.sep))){let se=ne.match(/^([✓✗·])\s(.*)$/);se?se[1]==="✓"?a.ok(se[2]):se[1]==="✗"?a.fail(se[2]):a.info(se[2]):a.line(" "+ne)}}let q=await f0(u,{root:w,machine:y,port:W,token:oe,up:!!pe.up});await h0({...u,input:e}).catch(Y=>a.info("PR cards: couldn't check gh ("+Y.message+")"));let re=pe.up?(await Bi({port:W,prefer:s.CHECKPOINTS_OPEN_HOST||P.openHost||"auto",token:oe})).url:"http://127.0.0.1:"+W+"/",B=ct(n,"open");pe.up&&(B===!0||B===void 0&&!r&&t.isTTY&&await c.confirm("Open the board in your browser?",!0))&&(yA(re)?a.ok("Opened "+re):a.info("Open "+re)),a.line();let Je=[l.bold("Board")+" "+l.cyan(re)];if(I==="mod"){let Y=(ne,se,T,L)=>Je.push(ne+" ".repeat(15-ne.replace(/\x1b\[[0-9;]*m/g,"").length)+l.cyan(se)+(T?" "+T:"")+" ".repeat(Math.max(1,21-se.length-(T?T.length+1:0)))+L);Y(l.bold("In any session"),"/cp","[note]","checkpoint now"),Y("","/cp-name","<title>","name the session"),Y("","/leftoff","","every open session"),Je.push(l.dim("Already-open sessions pick the mod up when they restart."))}else I==="hooks"?(Je.push(l.bold("Sessions")+" tracked through settings hooks;"),Je.push(" checkpoints happen on their own after 15 min idle")):Je.push(l.bold("Sessions")+" not hooked up yet: `leftoff setup`");return Je.push(l.bold("Models")+" "+l.cyan("leftoff models")+" list, pull, switch"),Je.push(l.bold("Digest")+" "+l.cyan("leftoff digest")+" where you left off"),Je.push(l.bold("Reopen")+" "+l.cyan("leftoff open")+" the board, any time"),q&&q.host?Je.push(l.bold("Anywhere")+" "+l.cyan("https://"+q.host)+l.dim(" (leftoff relay pair)")):Je.push(l.bold("Anywhere")+" "+l.cyan("leftoff relay signup")+" yourname"+l.dim(" (your board on your phone)")),eh(t,l,"You're set",Je),c.close(),r&&process.removeListener("SIGINT",d),{ok:!0,root:w,capture:I,summarizer:M,port:W,daemonUp:!!pe.up,anywhere:q}}catch(h){if(c.close(),r&&process.removeListener("SIGINT",d),h&&h.cancelled)return t.write(`
293
- `),a.fail("Setup cancelled ("+h.message.replace(/^cancelled \(?|\)$/g,"")+"). Re-run `leftoff setup` any time; finished steps are skipped."),{ok:!1,cancelled:!0};throw h}}async function f0(n,{root:e,machine:t,port:s,token:o,up:r}){let{p:i,u:a,flags:l,env:c,yes:u,fetchImpl:d}=n,f=i.c,{readRelaySettings:h,writeRelaySettings:p,hostFor:m,DEFAULT_DOMAIN:g}=await Promise.resolve().then(()=>(Kc(),r0)),{startSignup:S,waitForConfirm:k,validEmail:w,checkName:x}=await Promise.resolve().then(()=>(Pc(),Bw)),{checkUserName:v}=await Promise.resolve().then(()=>(Ki(),Rw)),{loadIdentity:b}=await Promise.resolve().then(()=>(os(),yf)),y=h(e,c);if(y.user)return a.ok("Use from anywhere: "+m(y)+(y.enabled?"":f.dim(" (off on this Mac: leftoff relay enable "+y.user+")"))),{host:m(y),existing:!0};if(ct(l,"signup")===!1)return{skipped:!0};let _=String(l.domain||y.domain||g),A=typeof l.signup=="string"?l.signup.trim().toLowerCase():"",O=typeof l.email=="string"?l.email.trim():"";if(u&&!(A&&O))return{skipped:!0};let I=!1;a.line();let N=U=>U&&U.cancelled&&/input closed/.test(U.message);try{if(!u&&!A&&!await i.confirm("Use your board from anywhere? Claim yourname."+_+f.dim(" (free in the beta; skip any time)"),!1))return a.info("Skipped. Later: "+f.cyan("leftoff relay signup yourname")),{skipped:!0};let U=!!A;for(let E=0;;E++){A||(A=(await i.ask("Your name","",{hint:"(for yourname."+_+")",validate:oe=>{let K=v(String(oe).toLowerCase());return K.ok?null:K.code==="reserved"?"That one’s reserved; pick another":"2–32 lowercase letters, digits or hyphens"}})).toLowerCase());let P=await x({s:{...y,user:A,domain:_},name:A,sign:b(t).signX});if(P.ok&&P.status==="yours"){I=!0;break}let W=P.ok?P.status==="available"?null:P.message:P.code==="legacy"?null:P.message;if(!W){P.ok&&P.claimed&&a.note(P.host+" was claimed on leftoff.dev. Use the email you claimed it with.");break}if(a.warn(W),U||u||E>=2||P.code==="unreachable"||P.code==="closed")return a.info("Try again any time: "+f.cyan("leftoff relay signup yourname")),{skipped:!0,error:W};A=""}!I&&!O&&(O=await i.ask("Email","",{hint:"(for the confirmation link)",validate:E=>w(E)?null:"That doesn’t look like an email address"}))}catch(U){if(!N(U))throw U;return a.line(),a.info("Skipped. Later: "+f.cyan("leftoff relay signup yourname")),{skipped:!0}}let M={...y,user:A,domain:_},z=I?{ok:!0,already:!0}:await S({s:M,name:A,email:O,machine:t,sign:b(t).signX});if(!z.ok)return a.warn(z.message),a.info("Try again any time: "+f.cyan("leftoff relay signup "+A)),{skipped:!0,error:z.message};if(z.already)a.ok("This Mac already has "+m(M)+". Pair your phone: "+f.cyan("leftoff relay pair"));else{a.ok("Sent a link to "+z.email+". Open it and press "+f.bold("Confirm "+m(M))+f.dim(" (30 minutes)")),z.claimed&&a.note("Your leftoff.dev claim for "+A+" carries over.");let U=it("Waiting for you to confirm… "+f.dim("(Ctrl-C skips; finish later with leftoff relay enable "+A+")"),{out:n.out,colors:f}),E=!1,P=()=>{E=!0};i.interruptHandlers.add(P),process.on("SIGINT",P);let W=await k({s:M,user:A,poll:z.poll,intervalMs:Number(c.LEFTOFF_SIGNUP_POLL_MS)||void 0,timeoutMs:Number(c.LEFTOFF_SIGNUP_WAIT_MS)||void 0,stop:()=>E});if(i.interruptHandlers.delete(P),process.removeListener("SIGINT",P),W!=="confirmed")return U.warn(W==="stopped"?"Stopped waiting. The link still works; then run "+f.cyan("leftoff relay enable "+A):W==="expired"||W==="timeout"?"The link expired. Run "+f.cyan("leftoff relay signup "+A)+" for a fresh one.":"The signup didn’t finish ("+W+"). Run "+f.cyan("leftoff relay signup "+A)+" again."),{skipped:!0,status:W};U.succeed(m(M)+" is yours. Pair your phone: "+f.cyan("leftoff relay pair"))}if(p(e,{enabled:!0,user:A,domain:_,registeredAt:new Date().toISOString(),via:"signup"},c),r)try{(await d("http://127.0.0.1:"+s+"/v1/relay/enable",{method:"POST",headers:{authorization:"Bearer "+o,"content-type":"application/json"},body:JSON.stringify({user:A,domain:_})})).ok&&a.ok("Use from anywhere is on; the daemon is connecting")}catch{}return{host:m(M)}}async function h0(n){let{p:e,u:t,flags:s={},env:o=process.env,yes:r}=n,i=t.c;if(ct(s,"gh")===!1)return{skipped:!0};let a=n.interactive!=null?n.interactive:!r&&!!(n.input&&n.input.isTTY||o.LEFTOFF_TEST_TTY==="1"),l=n.childStdio||"inherit",c=await qr({env:o}),u=()=>(t.ok("PR cards on (gh signed in"+(c.account?" as "+c.account:"")+")"),{status:"ready"});if(c.status==="ready")return u();if(c.status==="missing"){if(!c.brew)return t.info("PR cards: install gh from "+Ba+", then run "+i.cyan("gh auth login")),{status:"missing"};if(!a||!await e.confirm("PR cards need GitHub's gh. Install with Homebrew?",!0))return t.info("PR cards (optional): "+i.cyan("brew install gh && gh auth login")),{status:"missing"};let f=Vu(o),h=it("Installing gh with Homebrew…",{out:n.out,colors:i}),p=await new Promise(m=>{let g="",S;try{S=ur(f,["install","gh"],{env:{...o,HOMEBREW_NO_ENV_HINTS:"1"},stdio:["ignore","pipe","pipe"]})}catch(k){return m({ok:!1,log:k.message})}S.stdout.on("data",k=>g+=k),S.stderr.on("data",k=>g+=k),S.on("error",k=>m({ok:!1,log:g+k.message})),S.on("close",k=>m({ok:k===0,log:g}))});if(!p.ok){h.fail("Homebrew couldn't install gh:");for(let m of p.log.trim().split(`
294
- `).slice(-12))t.note(m);return t.info("PR cards: try "+i.cyan("brew install gh && gh auth login")+" later"),{status:"missing",error:"brew install failed"}}if(h.succeed("Installed gh"),c=await qr({env:o}),c.status==="ready")return u();if(c.status==="missing")return t.info("PR cards: gh isn't on your PATH yet; open a new terminal and run "+i.cyan("gh auth login")),{status:"missing"}}if(!a||!await e.confirm("PR cards need you signed in to GitHub. Run gh auth login now?",!0))return t.info("PR cards: run "+i.cyan("gh auth login")+" to turn them on"),{status:"signed-out"};let d=Fs(o);return e.close(),n.out.write(`
295
- `),so(d,["auth","login"],{stdio:l,env:o}),n.out.write(`
296
- `),c=await qr({env:o}),c.status==="ready"?u():(t.info("PR cards: not signed in; run "+i.cyan("gh auth login")+" any time"),{status:"signed-out"})}var i0,lt,Xf,pA,mA,Zf,eo=J(()=>{Pn();xe();is();Cc();js();Ls();fi();Qs();jf();No();Te();Hf();i0=n=>new Promise(e=>setTimeout(e,n)),lt=n=>n&&n.startsWith(oo.homedir()+Re.sep)?"~"+n.slice(oo.homedir().length):n,Xf=n=>n==="~"?oo.homedir():n&&n.startsWith("~/")?Re.join(oo.homedir(),n.slice(2)):n,pA=n=>n===!0||n==="true"||n==="1"||n==="yes",mA=n=>n===!1||n==="false"||n==="0"||n==="no";Zf={claude:"claude","in-session":"claude",session:"claude",auto:"claude",ollama:"ollama",local:"ollama",lmstudio:"openai-compatible","lm-studio":"openai-compatible",openai:"openai-compatible","openai-compatible":"openai-compatible","ollama-cloud":"ollama-cloud",cloud:"ollama-cloud","ollama.com":"ollama-cloud",anthropic:"anthropic",api:"anthropic",key:"anthropic",heuristic:"heuristic",none:"heuristic",stub:"heuristic",off:"heuristic"}});var Vc={};Qt(Vc,{moveHooks:()=>S0,parseMoveArgs:()=>w0,runMove:()=>nh});import Qi from"node:fs";import p0 from"node:os";import hr from"node:path";import{spawnSync as m0}from"node:child_process";async function Zc(n,e){try{let t=await fetch("http://127.0.0.1:"+n+"/v1/health",{headers:{authorization:"Bearer "+(e||"")},signal:AbortSignal.timeout(1500)});return t.ok?{up:!0,...await t.json()}:{up:t.status===401,status:t.status}}catch{return{up:!1}}}function w0(n){let e={to:null,dryRun:!1,yes:!1,force:!1,json:!1,backups:!1,deleteBackup:null,daemonPid:null,noRestart:!1};for(let t=0;t<n.length;t++){let s=n[t];s==="--dry-run"||s==="-n"?e.dryRun=!0:s==="--yes"||s==="-y"?e.yes=!0:s==="--force"?e.force=!0:s==="--json"?e.json=!0:s==="--backups"?e.backups=!0:s==="--no-restart"?e.noRestart=!0:s==="--help"||s==="-h"?e.help=!0:s==="--delete-backup"?e.deleteBackup=n[t+1]&&!n[t+1].startsWith("--")?n[++t]:!0:s==="--daemon-pid"?e.daemonPid=Number(n[++t])||null:e.to||(e.to=s)}return e}function S0({from:n,cfg:e,token:t,daemonPid:s=null,noRestart:o=!1,say:r=()=>{},env:i=process.env}){let a=Nt(),l=Qi.existsSync(a)&&!i.CHECKPOINTS_SERVICE_DRY_RUN,c=e.port,u=!1;return{async stopWriters(){let d=await Zc(c,t);if(u=d.up,!!d.up){if(l){let f=Mn()==="darwin"?["launchctl","unload",a]:["systemctl","--user","stop",Ys];m0(f[0],f.slice(1),{encoding:"utf8"})}else{let f=s||d.pid||null;if(!f)try{f=Number(Qi.readFileSync(hr.join(n,".daemon.pid"),"utf8"))||null}catch{}if(!f)throw new Error("the daemon is running but I can’t tell its process; stop it (Ctrl-C on `leftoff serve`) and run this again");try{process.kill(f,"SIGTERM")}catch{}for(let h=0;h<50&&y0(f);h++)await Xc(100);if(y0(f)){try{process.kill(f,"SIGKILL")}catch{}await Xc(200)}}for(let f=0;f<40&&(await Zc(c,t)).up;f++)await Xc(100);if((await Zc(c,t)).up)throw new Error("the daemon didn’t stop");r("Daemon stopped")}},async switchTo(d){if(Jc(d),Qi.existsSync(a)){let f=Ln({root:d,bin:Ke});Qi.readFileSync(a,"utf8")!==f&&Qi.writeFileSync(a,f)}},async startWriters(d){if(o||!u)return{ok:!0,skipped:!0};let f=ve(d),h=Q(d).port;if(l)for(let p of Xs())m0(p[0],p.slice(1),{encoding:"utf8"});else{let{LEFTOFF_DIR:p,CHECKPOINTS_DIR:m,...g}=i;Yc({root:d,bin:Ke,env:{...g,CHECKPOINTS_DIR:d}})}for(let p=0;p<80;p++){let m=await Zc(h,f);if(m.up&&m.folder&&hr.resolve(m.folder)===hr.resolve(d))return{ok:!0,port:h};if(m.up&&m.status===401)return{ok:!1,error:"something else is answering on port "+h};await Xc(150)}return{ok:!1,error:"no answer on port "+h}}}}async function nh({argv:n=[],out:e=process.stdout,input:t=process.stdin,env:s=process.env}={}){let o=ee,r=w0(n),i=w=>{r.json||e.write(w+`
297
- `)},a=Z();if(r.help)return e.write(bA),0;if(r.backups||r.deleteBackup){let w=ir(a);if(r.backups){if(r.json)return e.write(JSON.stringify({backups:w})+`
298
- `),0;w.length||i("No backups from a move next to "+we(a)+".");for(let v of w)i(" "+v.display+o.dim(" (moved "+(v.at||"?")+")"));return 0}let x=r.deleteBackup===!0?w.length===1?w[0].path:null:hr.resolve(g0(r.deleteBackup));if(!x)return i(w.length?"Several backups; name one: "+V+" move --delete-backup <path>":"No backups to delete."),1;if(!r.yes){let v=new wt({input:t,output:e});if(!await v.confirm("Delete "+we(x)+" for good?",!1).finally(()=>v.close&&v.close()))return i("Kept it."),0}try{return ji(x),i(o.green("✓")+" Deleted "+we(x)),0}catch(v){return i(o.red("✗")+" "+v.message),1}}if(!r.to){let w=or({current:a});if(i("usage: "+V+` move <folder> [--dry-run] [--yes]
279
+ `)}function m0(n,e=66){let t=[],s="";for(let o of String(n).split(/\s+/))(s+" "+o).trim().length>e?(t.push(s),s=o):s=(s+" "+o).trim();return s&&t.push(s),t}function ao(n,e,t,s){if(!t.ok){n.write(e.red("✖")+" Test checkpoint failed with "+s+": "+(t.error||"no answer")+`
280
+ `);return}let o=[];for(let[a,l]of[["Doing",t.doing],["Stopped at",t.stoppedAt],["Next",t.next]]){let c=m0(l||"",60);o.push(e.bold((a+":").padEnd(12))+(c[0]||""));for(let u of c.slice(1))o.push(" ".repeat(12)+u)}let r=t.generator||{},i=r.inputTokens!=null?" · "+r.inputTokens+" → "+r.outputTokens+" tokens":"";oh(n,e,"Sample checkpoint · "+s+" · "+Gi(t.ms)+i,o),t.parseOk||n.write(e.yellow("!")+` The model didn't follow the 3-line format; a bigger model usually does.
281
+ `)}async function dr(n,e,t=fetch){try{let s=await t("http://127.0.0.1:"+n+"/v1/health",{headers:{authorization:"Bearer "+(e||"")},signal:AbortSignal.timeout(1500)});return s.status===401?{up:!0,auth:!1}:s.ok?{up:!0,auth:!0,...await s.json()}:{up:!1}}catch{return{up:!1}}}async function Yc(n,e,t,s=fetch){let o=await s("http://127.0.0.1:"+n+"/v1/config",{method:"PATCH",headers:{authorization:"Bearer "+e,"content-type":"application/json"},body:JSON.stringify(t),signal:AbortSignal.timeout(4e3)});if(!o.ok)throw new Error("HTTP "+o.status);return o.json()}function Wc({root:n,bin:e=$e,env:t=process.env}){let s=be(n);nt.mkdirSync(s.root,{recursive:!0});let o=nt.openSync(Pe.join(n,".daemon.log"),"a"),r=ur(process.execPath,[e,"serve"],{detached:!0,stdio:["ignore",o,o],env:{...t,CHECKPOINTS_DIR:n}});return r.unref(),nt.closeSync(o),nt.writeFileSync(Pe.join(n,".daemon.pid"),String(r.pid)+`
282
+ `),r.pid}function AA(n){let e=process.platform==="darwin"?["open",[n]]:process.platform==="win32"?["cmd",["/c","start","",n]]:["xdg-open",[n]];try{let t=ur(e[0],e[1],{detached:!0,stdio:"ignore"});return t.on("error",()=>{}),t.unref(),!0}catch{return!1}}function Jc(n,{summarizer:e,machine:t,hostname:s=io.hostname(),port:o,summaryLength:r}={}){let i=ha({hostname:s,cfg:{}}),a=n&&typeof n=="object"?JSON.parse(JSON.stringify(n)):{};if(e){let l=a.summarizer&&a.summarizer.maxInputChars?{maxInputChars:a.summarizer.maxInputChars}:{};a.summarizer={provider:e.provider,model:e.model??null,baseUrl:e.baseUrl??null,apiKeyEnv:e.apiKeyEnv??null,...e.apiKeyFile?{apiKeyFile:e.apiKeyFile}:{},maxInputChars:24e3,...l},delete a.summarizer.mode}return t!==void 0&&(a.machineNames={...a.machineNames||{}},t&&t!==s&&t!==i?a.machineNames[s]=t:delete a.machineNames[s]),o&&(a.port=Number(o)),r&&(a.summary={...a.summary||{},length:r}),a}function qc(n,e){let t=be(n),s=t.config+".tmp-"+process.pid;return nt.writeFileSync(s,JSON.stringify(e,null,2)+`
283
+ `),nt.renameSync(s,t.config),t.config}function Vc(n){let e=ba(),t=Sp(n);if(Pe.resolve(n)===e)return Ri("CHECKPOINTS_DIR",null),{how:"default",pointer:t};let s=null;try{s=nt.lstatSync(e)}catch{}if(s&&s.isSymbolicLink()){if(Pe.resolve(Pe.dirname(e),nt.readlinkSync(e))===Pe.resolve(n))return Ri("CHECKPOINTS_DIR",null),{how:"symlink",pointer:t,already:!0};nt.unlinkSync(e),s=null}return s?(Ri("CHECKPOINTS_DIR",n),{how:"settings-env",pointer:t}):(nt.symlinkSync(n,e,"dir"),Ri("CHECKPOINTS_DIR",null),{how:"symlink",pointer:t})}function sh(n=process.env){let e=ro(process.platform==="win32"?"npm.cmd":"npm",["prefix","-g"],{encoding:"utf8",env:n});if(e.status!==0)return null;let t=e.stdout.trim(),s=process.platform==="win32"?Pe.join(t,"node_modules"):Pe.join(t,"lib","node_modules");for(let o of di)for(let r of["leftoff.js","checkpoints.js"]){let i=Pe.join(s,o,"bin",r);if(nt.existsSync(i))return i}return null}function TA(n,e,t,s){t.close(),s.write(`
284
+ `);let o=ro(n,e,{stdio:"inherit"});return s.write(`
285
+ `),o.status===0}async function Xc(n){let{p:e,u:t,out:s,flags:o,fetchImpl:r,env:i}=n,a=t.c,l=(o["ollama-url"]||(o["base-url"]&&n.wantProvider!=="openai-compatible"?o["base-url"]:null)||gt).replace(/\/$/,""),c=(o["lmstudio-url"]||(o["base-url"]&&n.wantProvider==="openai-compatible"?o["base-url"]:null)||ln).replace(/\/$/,""),u=lt("Looking for local model servers…",{out:s,colors:a}),[d,f]=await Promise.all([$t({baseUrl:l,fetchImpl:r,env:i}),n.wantProvider==="ollama"?{running:!1,models:[]}:En({baseUrl:c,fetchImpl:r})]);u.stop(),d.running?t.ok("Ollama "+(d.version?"v"+d.version+" ":"")+"running at "+d.baseUrl+a.dim(" · "+d.models.length+" model"+(d.models.length===1?"":"s")+" pulled")):d.binary?t.warn("Ollama is installed ("+ut(d.binary)+") but not running"):t.info("Ollama: not found"),f.running?t.ok("LM Studio / OpenAI-compatible server at "+f.baseUrl+a.dim(" · "+f.models.length+" loaded")):n.wantProvider!=="ollama"&&t.info("LM Studio / OpenAI-compatible server: not found at "+c);let h;if(n.wantProvider==="openai-compatible"?h="openai-compatible":n.wantProvider==="ollama"?h="ollama":d.running&&f.running?h=await e.select("Which one should write checkpoints?",[{label:"Ollama",value:"ollama",hint:"pull and switch models from here"},{label:"LM Studio / OpenAI-compatible",value:"openai-compatible",hint:"uses a model you load there"}],0):f.running&&!d.running?h="openai-compatible":h="ollama",h==="openai-compatible")return h0(n,f,c);if(!d.running){if(d.binary){let p=qr(process.platform,d.binary);if(p&&await e.confirm("Start Ollama now? ("+p.shell+")",!0)){if(p.detached){let g=ur(p.cmd[0],p.cmd.slice(1),{detached:!0,stdio:"ignore"});g.on("error",()=>{}),g.unref()}else ro(p.cmd[0],p.cmd.slice(1),{stdio:"ignore"});let m=lt("Waiting for Ollama to come up…",{out:s,colors:a});d=await Io({baseUrl:l,fetchImpl:r,timeoutMs:2e4}),d.running?m.succeed("Ollama is up at "+d.baseUrl):m.fail("Ollama didn't answer at "+l+" within 20 s")}}else{if(n.yes&&!dt(o,"install-ollama"))throw new Error("Ollama isn't installed. Install it (https://ollama.com/download) or pass --install-ollama, then re-run");t.line(),t.line(" No local model server yet. "+a.bold("Ollama")+" is the easiest: one install, then models download from here.");let p=lm(process.platform,i),m=await e.select("Install Ollama?",[...p.map(k=>({label:k.label,value:k.id,hint:k.shell+(k.note?" · "+k.note:"")})),{label:"I use LM Studio (or another OpenAI-compatible server)",value:"lmstudio",hint:c},{label:"Back: choose a different summarizer",value:"back"}],0);if(m==="back")return null;if(m==="lmstudio")return h0(n,f,c);let g=p.find(k=>k.id===m);if(g.cmd){if(t.line(" This runs:"),t.cmd(g.shell),!await e.confirm("Run it now?",!0))return null;if(!TA(g.cmd[0],g.cmd.slice(1),e,s))return t.fail("The Ollama installer exited with an error (see above)."),null;if(t.ok("Ollama installed"),g.id==="brew"){let k=qr(process.platform,Fs(i));if(k){let w=ur(k.cmd[0],k.cmd.slice(1),{detached:k.detached,stdio:"ignore"});w.on("error",()=>{}),w.unref()}}}else{if(t.line(" Install it with:"),t.cmd(g.shell),g.url&&t.note("or download it from "+g.url),n.yes)throw new Error("Ollama isn't installed; install it and re-run setup");await e.ask("Press Enter once Ollama is installed and running","")}let S=lt("Waiting for Ollama…",{out:s,colors:a});if(d=await Io({baseUrl:l,fetchImpl:r,timeoutMs:3e4}),!d.running&&d.binary){let k=qr(process.platform,d.binary);if(k){let w=ur(k.cmd[0],k.cmd.slice(1),{detached:!0,stdio:"ignore"});w.on("error",()=>{}),w.unref(),d=await Io({baseUrl:l,fetchImpl:r,timeoutMs:2e4})}}d.running?S.succeed("Ollama is up at "+d.baseUrl):S.fail("Ollama isn't answering at "+l)}if(!d.running){if(n.yes)throw new Error("Ollama isn't running at "+l+" (start it with `ollama serve`, or pass --install-ollama)");return t.note("Start Ollama (`ollama serve` or the app) and run `leftoff models` to finish this later."),null}}return g0(n,d)}async function g0(n,e){let{p:t,u:s,out:o,flags:r,fetchImpl:i}=n,a=s.c,l=Co(e.models,{current:n.currentModel}),c=r.model?Ua(r.model):am(l);s.line(),s.line(" "+a.bold("Models for 3-line checkpoints")+a.dim(" (sizes from ollama.com/library, "+Oo+")"));let u=l.map(p=>({label:p.tag.padEnd(13)+" "+a.dim((p.curated?Ga(p,Gt):(p.sizeBytes?Gt(p.sizeBytes)+" · ":"")+"on this machine").padEnd(30))+" "+(p.recommended?a.yellow("★ recommended")+" ":"")+(p.installed?a.green("✔ pulled"):""),detail:p.curated?p.note:null,value:p.tag}));u.push({label:"Other tag…",value:"__other__",hint:"any ollama.com/library tag, e.g. phi4-mini"});let d=u.findIndex(p=>Je(p.value,c)),f;d<0&&r.model?f=String(r.model).trim():(f=await t.select("Which model?",u,Math.max(0,d)),f==="__other__"&&(f=await t.ask("Model tag","",{validate:p=>/^[\w.\-/]+(:[\w.\-]+)?$/.test(p)?null:"Use a tag like llama3.2:3b"})));let h=l.find(p=>Je(p.tag,f));if(!h||!h.installed){let p=h&&h.sizeBytes?" ("+Gt(h.sizeBytes)+")":"";if(dt(r,"pull")===!1)throw new Error(f+" isn't pulled and --no-pull was given");if(!await t.confirm("Download "+f+p+" now?",!0))return null;let m=Ec("pull "+f,{out:o,colors:a}),g=Date.now();o.write(a.dim(" pulling "+f+" from ollama.com · Ctrl-C to stop (a re-run resumes)")+`
286
+ `);try{await t.interruptible(S=>Mo({tag:f,baseUrl:e.baseUrl,fetchImpl:i,signal:S,onProgress:k=>{let w=(Date.now()-g)/1e3,x=w>.5&&k.completed?Gt(k.completed/w)+"/s":"",v=k.total?Gt(k.completed)+" / "+Gt(k.total)+(x?" · "+x:""):k.status;m.update(k.fraction,v)}}))}catch(S){if(S.cancelled||(m.fail(S.message),n.yes))throw S;return null}m.done("Pulled "+f+a.dim(" in "+Gi(Date.now()-g)))}else s.ok(f+" is already pulled");return{provider:"ollama",model:f,baseUrl:e.baseUrl===gt?null:e.baseUrl}}async function h0(n,e,t){let{p:s,u:o,flags:r,fetchImpl:i}=n,a=o.c;if(!e.running){let c=(await s.ask("Server URL (OpenAI-compatible, ends in /v1)",t)).replace(/\/$/,"");if(e=await En({baseUrl:c,fetchImpl:i}),!e.running){if(o.fail("Nothing answered at "+c+"/models"),o.note("In LM Studio: Developer tab → Start Server, and load a model."),n.yes)throw new Error("no OpenAI-compatible server at "+c);return null}}for(;!e.models.length;){if(o.warn("The server is up but has no model loaded."),n.yes)throw new Error("no model loaded at "+e.baseUrl);if(!await s.confirm("Load one in LM Studio, then check again?",!0))return null;e=await En({baseUrl:e.baseUrl,fetchImpl:i})}let l=r.model;if(!l||!e.models.includes(l)){let c=Math.max(0,e.models.findIndex(u=>u===n.currentModel));l=await s.select("Which loaded model?",e.models.map(u=>({label:u,value:u})),c)}return o.ok("Using "+a.bold(l)+" at "+e.baseUrl),{provider:"openai-compatible",model:l,baseUrl:e.baseUrl===ln?null:e.baseUrl}}async function fr(n,e,t){let{p:s,u:o,out:r,fetchImpl:i}=n,a=lt("Writing a sample checkpoint with "+t+"…"+(e.provider==="ollama"?o.c.dim(" (the first run loads the model)"):""),{out:r,colors:o.c}),l=await Us(e,{fetchImpl:i});return l.ok?a.succeed("It works."):a.fail("The test didn't work."),ao(r,o.c,l,t),l}async function p0(n,e,{defaultEnv:t,secretName:s,testPrompt:o,testLabel:r}){let{p:i,u:a,flags:l,env:c}=n,u=n.currentSummarizer||{},d=u.provider===e.provider,f=await i.ask("Env var that holds the key",l["api-key-env"]||d&&u.apiKeyEnv||t,{validate:k=>/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)?null:"That isn't an env var name"});e.apiKeyEnv=f;let h=".secret/"+s,p=!!c[f],m=_n(e.provider,{apiKeyEnv:"__none__",apiKeyFile:d&&u.apiKeyFile?u.apiKeyFile:h,root:n.root},{});p?a.ok("$"+f+" is set in this shell"):a.warn("$"+f+" isn't set in this shell");let g=p?c[f]:null,S=k=>{e.apiKeyFile=h,(n.pendingSecrets=n.pendingSecrets||[]).push({name:s,value:k})};if(!g&&m?(a.ok("A saved key is already in "+h),e.apiKeyFile=d&&u.apiKeyFile||h,g=m):!g&&!n.yes&&(g=await i.ask("Paste the key (hidden; Enter to skip)","",{secret:!0}),g&&S(g)),g&&p&&!n.yes&&await i.confirm("Also save it to "+h+" (mode 0600) so the background service can use it?",!0)&&S(g),g||a.note("Set $"+f+" before starting the daemon, or re-run setup to save a key."),a.note("The key itself never goes into config.json."),g&&dt(l,"test")!==!1&&(n.yes||await i.confirm(o,!0))){process.env.__CHECKPOINTS_TEST_KEY=g;try{await fr(n,{...e,apiKeyFile:void 0,apiKeyEnv:"__CHECKPOINTS_TEST_KEY"},r)}finally{delete process.env.__CHECKPOINTS_TEST_KEY}}return g}async function _A(n,e){let{p:t,u:s,flags:o,fetchImpl:r,env:i}=n,a=s.c;if(o.model)return String(o.model);let l=String(o["base-url"]||i.CHECKPOINTS_CLOUD_URL||js),c=n.yes?null:lt("Fetching the cloud model list from "+l.replace(/^https?:\/\//,"")+"…",{out:n.out,colors:a}),u=await Jr({baseUrl:l,fetchImpl:r});c&&(u.ok?c.succeed(u.models.length+" cloud models available"):c.fail("Couldn't fetch the list ("+u.error+"); showing the usual picks"));let d=Ba(u.models,{current:e}),f=d.find(S=>S.current&&S.available!==!1)||d.find(S=>S.recommended&&S.available!==!1)||d.find(S=>S.available!==!1)||d[0];if(n.yes)return f.tag;let h=d.filter(S=>S.curated||S.current),p=h.map(S=>({label:S.tag+(S.recommended?" ★":""),value:S.tag,hint:(S.note||"")+(S.available===!1?" · not in today's list":"")})),m=d.filter(S=>!h.includes(S)).map(S=>S.tag);p.push({label:"Other model",value:"__other",hint:m.length?m.slice(0,6).join(", ")+(m.length>6?", …":""):"any tag from ollama.com/search?c=cloud"});let g=await t.select("Which cloud model?",p,Math.max(0,p.findIndex(S=>S.value===f.tag)));return g!=="__other"?g:t.ask("Model name (as ollama.com/api/tags lists it)",e||f.tag,{validate:S=>/^[\w.:/-]+$/.test(S)?null:"That doesn't look like a model tag"})}async function EA(n){let{p:e,u:t,flags:s,env:o}=n,r=t.c,i=n.currentSummarizer||{},a=nh[String(i.provider||"auto").toLowerCase()]||"claude",l=s.summarizer?nh[String(s.summarizer).toLowerCase()]:null;if(s.summarizer&&!l)throw new Error("unknown --summarizer="+s.summarizer+" (claude, ollama, lmstudio, ollama-cloud, anthropic, heuristic)");let c=[{label:"Claude, in the session",value:"claude",hint:"no key; uses your Claude Code login"},{label:"Local model",value:"local",hint:"free and private · Ollama or LM Studio"},{label:"Ollama cloud",value:"ollama-cloud",hint:"hosted gpt-oss, Gemma… · needs an ollama.com API key"},{label:"Anthropic API key",value:"anthropic",hint:"Haiku, about $0.007 per checkpoint"},{label:"No model",value:"heuristic",hint:"last prompt + last reply, offline"}],u=d=>d==="ollama"||d==="openai-compatible"?"local":d;for(;;){let d=u(l||a),f=await e.select("Who writes the 3-line checkpoints?",c,Math.max(0,c.findIndex(p=>p.value===d)));if(f==="claude"){let p={provider:"claude",model:"haiku"};return n.claude.found&&(dt(s,"test")===!0||!n.yes&&await e.confirm("Run a test checkpoint with `claude -p` now? (~10 s, uses your Claude login)",!1))?await fr(n,p,"claude -p (haiku)"):n.claude.found||t.note('Without the claude CLI the daemon falls back to "no model" for checkpoints it writes on its own.'),p}if(f==="heuristic")return{provider:"heuristic",model:null};if(f==="anthropic"){let p={provider:"anthropic",model:s.model||"haiku"};return await p0(n,p,{defaultEnv:"ANTHROPIC_API_KEY",secretName:"anthropic-api-key",testPrompt:"Test it now? (one Haiku call, well under a cent)",testLabel:"Claude Haiku (API)"}),p}if(f==="ollama-cloud"){let p=await _A(n,i.provider==="ollama-cloud"?i.model:null),m={provider:"ollama-cloud",model:p,...s["base-url"]?{baseUrl:String(s["base-url"])}:{}};return t.note("Your transcripts' last few turns go to ollama.com for each checkpoint. Keys: https://ollama.com/settings/keys"),await p0(n,m,{defaultEnv:"OLLAMA_API_KEY",secretName:"ollama-api-key",testPrompt:"Test it now? (one call to "+p+" on ollama.com)",testLabel:p+" (Ollama cloud)"}),m}let h=await Xc({...n,wantProvider:l==="ollama"||l==="openai-compatible"?l:null});if(!h){if(n.yes)throw new Error("local model setup didn't finish");continue}if(dt(s,"test")!==!1){let p=await fr(n,h,h.model);if(!p.ok&&!n.yes&&await e.select("What now?",[{label:"Keep this model anyway",value:"keep"},{label:"Pick something else",value:"again"}],1)==="again")continue;if(!p.ok&&n.yes)throw new Error("test checkpoint failed: "+p.error)}return h}}async function y0(n,e={}){let{p:t,u:s,flags:o}=n,r=s.c,i=o["summary-length"]||o.length;if(i&&i!==!0){let u=wn(i,{strict:!0});if(!u)throw new Error("unknown --summary-length="+i+" (short, informative, long)");return s.ok("Checkpoint length: "+u),u}let a=wn(e.summary&&e.summary.length),l=Hw.map(u=>({label:u.label+(u.recommended?" (recommended)":""),value:u.key,hint:u.approx,detail:u.blurb})),c=Math.max(0,l.findIndex(u=>u.value===a));for(;;){let u=await t.select("How much should each checkpoint say?",l,c),d=Lc(Uw[u]).split(`
287
+ `).flatMap(f=>m0(f,70));if(oh(n.out,r,"Example · "+u,d),n.yes||await t.confirm("Use "+u+" checkpoints? (change any time in Settings → Models & summaries; /cp --short or --long overrides once)",!0))return u;c=l.findIndex(f=>f.value===u)}}async function rh({flags:n={},input:e=process.stdin,out:t=process.stdout,env:s=process.env,fetchImpl:o=(...r)=>fetch(...r)}={}){let r=!!(n.yes||n.y),i=gs(ms(t)),a=xA(t,i),l=i,c=new kt({input:e,output:t,yes:r,colors:i}),u={p:c,u:a,out:t,flags:n,env:s,fetchImpl:o,yes:r},d=()=>{t.write(`
288
+ `),a.fail("Setup cancelled. Re-run `leftoff setup` any time; finished steps are skipped."),process.exit(130)};r&&process.on("SIGINT",d);let f=5;try{if(a.line(),a.line(l.bold(" ◆ Leftoff setup")+l.dim(" v"+X)),a.line(l.dim(" Track every Claude Code session and get a 3-line checkpoint when one goes quiet.")),a.line(l.dim(" About two minutes. Ctrl-C any time; re-running is safe.")),a.step(1,f,"Preflight"),Number(process.versions.node.split(".")[0])<20)throw new Error("Node "+process.versions.node+" is too old; Leftoff needs Node 20 or newer");a.ok("Node "+process.versions.node);let p=Iw(s);u.claude=p,p.found?p.modsSupported?a.ok("Claude Code "+(p.version||"?")+l.dim(" · "+ut(p.bin))):a.warn("Claude Code "+(p.version||"(version unknown)")+" · mods need "+Ki+"+ (update with `claude update`); settings hooks work on any version"):a.warn("Claude Code's `claude` command isn't on your PATH");let m=$e;if(Pf(lr)){let z=sh(s);if(z)m=z,a.ok("leftoff is installed globally"+l.dim(" · "+ut(z)));else{a.warn("Running from a temporary npx copy.");let ne=dt(n,"global"),se=jf(lr)?it:"github:"+Oc;if(ne!==!1&&(ne===!0||!r&&await c.confirm("Install the `leftoff` command globally (npm install -g "+se+")?",!0))){let T=lt("npm install -g "+se,{out:t,colors:l}),L=ro(process.platform==="win32"?"npm.cmd":"npm",["install","-g",se],{encoding:"utf8",env:s}),j=L.status===0?sh(s):null;j?(m=j,T.succeed("Installed `leftoff` globally")):T.fail("Global install failed: "+((L.stderr||"").trim().split(`
289
+ `).pop()||"exit "+L.status)+" (continuing from the npx copy)")}m===$e&&a.note("Settings hooks and the login service will point at this npx copy. If npm clears its cache, re-run setup (or pass --global).")}}if(u.bin=m,!s.CHECKPOINTS_NO_UPDATE_CHECK)try{let{installInfo:z,lsRemote:ne,writeStamp:se,DEFAULT_SOURCE:T}=await Promise.resolve().then(()=>(pi(),Hg)),L=await z({env:s});if(L.kind!=="clone"&&L.kind!=="npm"&&!L.sha){let j=await ne(T,{env:s,timeoutMs:8e3});j.ok&&se({sha:j.sha,source:T})}}catch{}let g=Z(),S=nt.existsSync(be(g).config);S&&a.info("Found an existing setup in "+ut(g)+l.dim(" · your answers from last time are the defaults")),S||a.note("Tip: a synced folder (iCloud Drive, Dropbox, Syncthing) puts every machine on one board.");let k=await c.ask("Where should checkpoints live?",ut(th(n.dir||g)),{validate:z=>z&&Pe.isAbsolute(th(z))?null:"Use an absolute path or ~/…"}),w=Pe.resolve(th(k));u.root=w;let x=Sn(w)||(S&&w===g?Sn(g):null)||{},v=Q(w),b=io.hostname(),y=await c.ask("Name for this machine",n.machine||de(v),{hint:"(shown on each card)",validate:z=>/^[\w .-]{1,40}$/.test(z)?null:"Letters, numbers, spaces, dots and dashes; up to 40"});if(Pe.resolve(w)!==Pe.resolve(g)&&jn(g)&&!n["no-move"]){let{planMove:z,executeMove:ne,tilde:se}=await Promise.resolve().then(()=>(to(),_w)),{moveHooks:T}=await Promise.resolve().then(()=>(ta(),Zc)),{loadIdentity:L}=await Promise.resolve().then(()=>(as(),kf)),j=null;try{j=L(y)}catch{}let $=z(g,w,{machine:y,identity:j});if(!$.ok){for(let me of[...$.errors,...$.collisions])a.warn(me.message);throw new Error("Can’t "+($.merge?"join ":"move to ")+se(w)+" yet (see above). Nothing changed.")}if(a.info(($.merge?"That folder already has Leftoff data"+($.warnings.length?" ("+$.warnings.map(me=>me.message).join(" ")+")":"")+". ":"")+$.counts.total+" files in "+se(g)+"."),await c.confirm(($.merge?"Merge":"Move")+" your sessions into "+se(w)+"? (copied and checked; the old folder is kept as "+se($.backup)+")",!0)){let me=Q(g),vt=await ne($,{hooks:T({from:g,cfg:me,token:ve(g),noRestart:!0,env:s}),machine:y,say:gn=>a.line(" "+l.dim("· "+gn))});if(!vt.ok)throw new Error("The move stopped ("+vt.error+"); put back, still using "+se(g)+".");a.ok(($.merge?"Merged":"Moved")+": "+vt.copied+" copied, "+vt.verified+" verified. Backup: "+se(vt.backup))}}a.step(2,f,"Claude Code mod");let _=gc();Lw(_)&&a.warn('"disableAllHooks": true is set in your Claude settings, so neither mods nor settings hooks run until you remove it.');let A=n.capture?String(n.capture):dt(n,"hooks")===!0?"hooks":dt(n,"mod")===!1?"none":null,O=[];p.found&&O.push({label:"Install the mod",value:"mod",hint:p.modsSupported?"recommended · /cp, /cp-name and the /leftoff pane":"needs Claude Code "+Ki+"+"}),O.push({label:"Settings hooks only",value:"hooks",hint:"works on any version; no slash commands or pane"+(p.found?"":" · start working once Claude Code is installed")}),O.push({label:"Skip for now",value:"none"});let N=A||(p.found&&p.modsSupported?"mod":"hooks");p.found||(a.line(" Install Claude Code first ("+l.underline("https://code.claude.com/docs")+"), then re-run setup to add the mod."),N==="mod"&&(N="hooks")),N=await c.select("How should Leftoff see your sessions?",O,Math.max(0,O.findIndex(z=>z.value===N)));let I=!1;if(N==="mod"){let z=Mw({override:n.marketplace}),ne="",se=lt("Checking installed plugins…",{out:t,colors:l}),T=Hf({bin:p.bin,source:z,env:s,onStep:L=>{ne=L,se.update("Running "+l.cyan(L))}});if(T.ok&&T.migrated)se.succeed("Mod moved to its new name: "+Le+" (was "+ys+")");else if(T.ok&&T.already&&T.state&&!T.state.enabled)se.succeed("Mod was installed but disabled; enabled it ("+Le+")");else if(T.ok&&T.already)se.succeed("Mod already installed and enabled ("+Le+")");else if(T.ok){se.succeed("Mod installed: "+Le);for(let L of T.steps)a.cmd("claude "+L.args.map(j=>ut(j)).join(" "))}else se.fail("`"+ne+"` failed: "+String(T.error).split(`
290
+ `).slice(-2).join(" "));I=T.ok,T.ok?p.modsSupported||a.warn("The mod is installed but loads only on Claude Code "+Ki+"+."):(/github|clone|auth|permission|denied|not found/i.test(String(T.error))&&z===Oc&&a.note("The repo is private: Claude Code clones it with your git credentials (try `gh auth login`), or run setup from a local clone."),N=await c.confirm("Use settings hooks instead? (same tracking, no slash commands)",!0)?"hooks":"none"),I&&Di()&&await c.confirm("Remove the older settings hooks? The mod covers them.",!0)&&(yc(),a.ok("Removed the settings hooks"))}if(N==="hooks"){let z=Di(),ne=tr(void 0,{bin:m});a.ok((z?"Settings hooks refreshed in ":"Settings hooks added to ")+ut(ne)+l.dim(" (backup saved next to it)"))}N==="none"&&a.info("Skipped. Add it later with `leftoff setup`."),a.step(3,f,"Summarizer"),u.currentSummarizer=v.summarizer,u.currentModel=v.summarizer&&v.summarizer.model;let M=await EA(u),Y=await y0(u,v);a.step(4,f,"Save and start"),Oe(w);for(let z of u.pendingSecrets||[])va(z.name,z.value,w,s),a.ok("Key saved to "+ut(Pe.join(nn(s),z.name))+l.dim(" (0600, on this Mac only)"));let U=Jc(x,{summarizer:M,machine:y,hostname:b,port:n.port,summaryLength:Y});qc(w,U),a.ok("Config saved: "+ut(be(w).config)+l.dim(" · summarizer "+M.provider+(M.model?" / "+M.model:"")+" · "+Y+" checkpoints · machine "+y));let E=Vc(w);E.how==="symlink"&&!E.already?a.ok("Linked ~/checkpoints → "+ut(w)+l.dim(" (so the mod and hooks find it)")):E.how==="settings-env"&&a.ok("Set CHECKPOINTS_DIR in your Claude settings so the mod and hooks find "+ut(w));let P=Q(w),W=P.port,oe=ve(w),K={...s,CHECKPOINTS_DIR:w},pe=await dr(W,oe,o),Ie=dt(n,"service"),Se=Ie!==void 0?Ie:!r&&process.platform!=="win32"?await c.confirm("Start Leftoff automatically when you log in?",!0):!1;if(Se){let z=Lt(),ne=wc({root:w,bin:m,dryRun:!!n["service-dry-run"]});ne&&a.ok("Moved the login service to its new name: "+ut(ne.to)+l.dim(" (stopped and removed "+ut(ne.from)+")")),nt.mkdirSync(Pe.dirname(z),{recursive:!0});let se=Rn({root:w,bin:m}),T=!nt.existsSync(z)||nt.readFileSync(z,"utf8")!==se;nt.writeFileSync(z,se),a.ok((T?"Login service written: ":"Login service up to date: ")+ut(z));let L=!0;if(!n["service-dry-run"])for(let j of Qs()){let $=ro(j[0],j.slice(1),{encoding:"utf8"});if($.status!==0&&j[1]!=="unload"){L=!1,a.warn("`"+j.join(" ")+"` failed: "+(($.stderr||$.error&&$.error.message||"").trim().split(`
291
+ `)[0]||"exit "+$.status));break}}if(L?a.ok("Service started"):a.note("Starting the daemon directly instead; the service file stays for next login."),L)for(let j=0;j<20&&!(pe=await dr(W,oe,o)).up;j++)await f0(250)}if(dt(n,"start")===!1)a.info("Not starting the daemon (--no-start). Start it with `leftoff serve`.");else if(pe.up&&pe.auth){try{await Yc(W,oe,{summarizer:U.summarizer,machineNames:U.machineNames||{},summary:{length:Y}},o),a.ok("Daemon already running on 127.0.0.1:"+W+l.dim(" · config updated live"))}catch(z){a.warn("Daemon is running but didn't take the new config ("+z.message+"); restart it to apply.")}pe.machine&&pe.machine!==y&&a.note("Restart the daemon to show the new machine name.")}else if(pe.up&&!pe.auth)a.warn("Something else (another Leftoff folder?) is on port "+W+'. Stop it, or set "port" in config.json, then run `leftoff serve`.');else{let z=lt("Starting the daemon…",{out:t,colors:l});Wc({root:w,bin:m,env:K});for(let ne=0;ne<40&&!(pe=await dr(W,oe,o)).up;ne++)await f0(250);pe.up?(z.succeed("Daemon running on 127.0.0.1:"+W),Se||a.note("Runs in the background until reboot; `leftoff setup --service` starts it at login.")):z.fail("The daemon didn't start; see "+ut(Pe.join(w,".daemon.log")))}if(a.step(5,f,"Check"),dt(n,"doctor")!==!1){let z=ro(process.execPath,[m,"doctor"],{encoding:"utf8",env:{...K,NO_COLOR:"1"}});for(let ne of(z.stdout||"").trim().split(`
292
+ `).filter(Boolean).map(se=>se.split(io.homedir()+Pe.sep).join("~"+Pe.sep))){let se=ne.match(/^([✓✗·])\s(.*)$/);se?se[1]==="✓"?a.ok(se[2]):se[1]==="✗"?a.fail(se[2]):a.info(se[2]):a.line(" "+ne)}}let q=await w0(u,{root:w,machine:y,port:W,token:oe,up:!!pe.up});await S0({...u,input:e}).catch(z=>a.info("PR cards: couldn't check gh ("+z.message+")"));let re=pe.up?(await $i({port:W,prefer:s.CHECKPOINTS_OPEN_HOST||P.openHost||"auto",token:oe})).url:"http://127.0.0.1:"+W+"/",B=dt(n,"open");pe.up&&(B===!0||B===void 0&&!r&&t.isTTY&&await c.confirm("Open the board in your browser?",!0))&&(AA(re)?a.ok("Opened "+re):a.info("Open "+re)),a.line();let Ve=[l.bold("Board")+" "+l.cyan(re)];if(N==="mod"){let z=(ne,se,T,L)=>Ve.push(ne+" ".repeat(15-ne.replace(/\x1b\[[0-9;]*m/g,"").length)+l.cyan(se)+(T?" "+T:"")+" ".repeat(Math.max(1,21-se.length-(T?T.length+1:0)))+L);z(l.bold("In any session"),"/cp","[note]","checkpoint now"),z("","/cp-name","<title>","name the session"),z("","/leftoff","","every open session"),Ve.push(l.dim("Already-open sessions pick the mod up when they restart."))}else N==="hooks"?(Ve.push(l.bold("Sessions")+" tracked through settings hooks;"),Ve.push(" checkpoints happen on their own after 15 min idle")):Ve.push(l.bold("Sessions")+" not hooked up yet: `leftoff setup`");return Ve.push(l.bold("Models")+" "+l.cyan("leftoff models")+" list, pull, switch"),Ve.push(l.bold("Digest")+" "+l.cyan("leftoff digest")+" where you left off"),Ve.push(l.bold("Reopen")+" "+l.cyan("leftoff open")+" the board, any time"),q&&q.host?Ve.push(l.bold("Anywhere")+" "+l.cyan("https://"+q.host)+l.dim(" (leftoff relay pair)")):Ve.push(l.bold("Anywhere")+" "+l.cyan("leftoff relay signup")+" yourname"+l.dim(" (your board on your phone)")),oh(t,l,"You're set",Ve),c.close(),r&&process.removeListener("SIGINT",d),{ok:!0,root:w,capture:N,summarizer:M,port:W,daemonUp:!!pe.up,anywhere:q}}catch(h){if(c.close(),r&&process.removeListener("SIGINT",d),h&&h.cancelled)return t.write(`
293
+ `),a.fail("Setup cancelled ("+h.message.replace(/^cancelled \(?|\)$/g,"")+"). Re-run `leftoff setup` any time; finished steps are skipped."),{ok:!1,cancelled:!0};throw h}}async function w0(n,{root:e,machine:t,port:s,token:o,up:r}){let{p:i,u:a,flags:l,env:c,yes:u,fetchImpl:d}=n,f=i.c,{readRelaySettings:h,writeRelaySettings:p,hostFor:m,DEFAULT_DOMAIN:g}=await Promise.resolve().then(()=>(zc(),d0)),{startSignup:S,waitForConfirm:k,validEmail:w,checkName:x}=await Promise.resolve().then(()=>(Fc(),Ww)),{checkUserName:v}=await Promise.resolve().then(()=>(Yi(),Bw)),{loadIdentity:b}=await Promise.resolve().then(()=>(as(),kf)),y=h(e,c);if(y.user)return a.ok("Use from anywhere: "+m(y)+(y.enabled?"":f.dim(" (off on this Mac: leftoff relay enable "+y.user+")"))),{host:m(y),existing:!0};if(dt(l,"signup")===!1)return{skipped:!0};let _=String(l.domain||y.domain||g),A=typeof l.signup=="string"?l.signup.trim().toLowerCase():"",O=typeof l.email=="string"?l.email.trim():"";if(u&&!(A&&O))return{skipped:!0};let N=!1;a.line();let I=U=>U&&U.cancelled&&/input closed/.test(U.message);try{if(!u&&!A&&!await i.confirm("Use your board from anywhere? Claim yourname."+_+f.dim(" (free in the beta; skip any time)"),!1))return a.info("Skipped. Later: "+f.cyan("leftoff relay signup yourname")),{skipped:!0};let U=!!A;for(let E=0;;E++){A||(A=(await i.ask("Your name","",{hint:"(for yourname."+_+")",validate:oe=>{let K=v(String(oe).toLowerCase());return K.ok?null:K.code==="reserved"?"That one’s reserved; pick another":"2–32 lowercase letters, digits or hyphens"}})).toLowerCase());let P=await x({s:{...y,user:A,domain:_},name:A,sign:b(t).signX});if(P.ok&&P.status==="yours"){N=!0;break}let W=P.ok?P.status==="available"?null:P.message:P.code==="legacy"?null:P.message;if(!W){P.ok&&P.claimed&&a.note(P.host+" was claimed on leftoff.dev. Use the email you claimed it with.");break}if(a.warn(W),U||u||E>=2||P.code==="unreachable"||P.code==="closed")return a.info("Try again any time: "+f.cyan("leftoff relay signup yourname")),{skipped:!0,error:W};A=""}!N&&!O&&(O=await i.ask("Email","",{hint:"(for the confirmation link)",validate:E=>w(E)?null:"That doesn’t look like an email address"}))}catch(U){if(!I(U))throw U;return a.line(),a.info("Skipped. Later: "+f.cyan("leftoff relay signup yourname")),{skipped:!0}}let M={...y,user:A,domain:_},Y=N?{ok:!0,already:!0}:await S({s:M,name:A,email:O,machine:t,sign:b(t).signX});if(!Y.ok)return a.warn(Y.message),a.info("Try again any time: "+f.cyan("leftoff relay signup "+A)),{skipped:!0,error:Y.message};if(Y.already)a.ok("This Mac already has "+m(M)+". Pair your phone: "+f.cyan("leftoff relay pair"));else{a.ok("Sent a link to "+Y.email+". Open it and press "+f.bold("Confirm "+m(M))+f.dim(" (30 minutes)")),Y.claimed&&a.note("Your leftoff.dev claim for "+A+" carries over.");let U=lt("Waiting for you to confirm… "+f.dim("(Ctrl-C skips; finish later with leftoff relay enable "+A+")"),{out:n.out,colors:f}),E=!1,P=()=>{E=!0};i.interruptHandlers.add(P),process.on("SIGINT",P);let W=await k({s:M,user:A,poll:Y.poll,intervalMs:Number(c.LEFTOFF_SIGNUP_POLL_MS)||void 0,timeoutMs:Number(c.LEFTOFF_SIGNUP_WAIT_MS)||void 0,stop:()=>E});if(i.interruptHandlers.delete(P),process.removeListener("SIGINT",P),W!=="confirmed")return U.warn(W==="stopped"?"Stopped waiting. The link still works; then run "+f.cyan("leftoff relay enable "+A):W==="expired"||W==="timeout"?"The link expired. Run "+f.cyan("leftoff relay signup "+A)+" for a fresh one.":"The signup didn’t finish ("+W+"). Run "+f.cyan("leftoff relay signup "+A)+" again."),{skipped:!0,status:W};U.succeed(m(M)+" is yours. Pair your phone: "+f.cyan("leftoff relay pair"))}if(p(e,{enabled:!0,user:A,domain:_,registeredAt:new Date().toISOString(),via:"signup"},c),r)try{(await d("http://127.0.0.1:"+s+"/v1/relay/enable",{method:"POST",headers:{authorization:"Bearer "+o,"content-type":"application/json"},body:JSON.stringify({user:A,domain:_})})).ok&&a.ok("Use from anywhere is on; the daemon is connecting")}catch{}return{host:m(M)}}async function S0(n){let{p:e,u:t,flags:s={},env:o=process.env,yes:r}=n,i=t.c;if(dt(s,"gh")===!1)return{skipped:!0};let a=n.interactive!=null?n.interactive:!r&&!!(n.input&&n.input.isTTY||o.LEFTOFF_TEST_TTY==="1"),l=n.childStdio||"inherit",c=await Vr({env:o}),u=()=>(t.ok("PR cards on (gh signed in"+(c.account?" as "+c.account:"")+")"),{status:"ready"});if(c.status==="ready")return u();if(c.status==="missing"){if(!c.brew)return t.info("PR cards: install gh from "+$a+", then run "+i.cyan("gh auth login")),{status:"missing"};if(!a||!await e.confirm("PR cards need GitHub's gh. Install with Homebrew?",!0))return t.info("PR cards (optional): "+i.cyan("brew install gh && gh auth login")),{status:"missing"};let f=Zu(o),h=lt("Installing gh with Homebrew…",{out:n.out,colors:i}),p=await new Promise(m=>{let g="",S;try{S=ur(f,["install","gh"],{env:{...o,HOMEBREW_NO_ENV_HINTS:"1"},stdio:["ignore","pipe","pipe"]})}catch(k){return m({ok:!1,log:k.message})}S.stdout.on("data",k=>g+=k),S.stderr.on("data",k=>g+=k),S.on("error",k=>m({ok:!1,log:g+k.message})),S.on("close",k=>m({ok:k===0,log:g}))});if(!p.ok){h.fail("Homebrew couldn't install gh:");for(let m of p.log.trim().split(`
294
+ `).slice(-12))t.note(m);return t.info("PR cards: try "+i.cyan("brew install gh && gh auth login")+" later"),{status:"missing",error:"brew install failed"}}if(h.succeed("Installed gh"),c=await Vr({env:o}),c.status==="ready")return u();if(c.status==="missing")return t.info("PR cards: gh isn't on your PATH yet; open a new terminal and run "+i.cyan("gh auth login")),{status:"missing"}}if(!a||!await e.confirm("PR cards need you signed in to GitHub. Run gh auth login now?",!0))return t.info("PR cards: run "+i.cyan("gh auth login")+" to turn them on"),{status:"signed-out"};let d=Bs(o);return e.close(),n.out.write(`
295
+ `),ro(d,["auth","login"],{stdio:l,env:o}),n.out.write(`
296
+ `),c=await Vr({env:o}),c.status==="ready"?u():(t.info("PR cards: not signed in; run "+i.cyan("gh auth login")+" any time"),{status:"signed-out"})}var f0,ut,th,bA,vA,nh,no=J(()=>{Fn();xe();cs();Ic();Hs();Ps();pi();to();Bf();Lo();Te();$f();f0=n=>new Promise(e=>setTimeout(e,n)),ut=n=>n&&n.startsWith(io.homedir()+Pe.sep)?"~"+n.slice(io.homedir().length):n,th=n=>n==="~"?io.homedir():n&&n.startsWith("~/")?Pe.join(io.homedir(),n.slice(2)):n,bA=n=>n===!0||n==="true"||n==="1"||n==="yes",vA=n=>n===!1||n==="false"||n==="0"||n==="no";nh={claude:"claude","in-session":"claude",session:"claude",auto:"claude",ollama:"ollama",local:"ollama",lmstudio:"openai-compatible","lm-studio":"openai-compatible",openai:"openai-compatible","openai-compatible":"openai-compatible","ollama-cloud":"ollama-cloud",cloud:"ollama-cloud","ollama.com":"ollama-cloud",anthropic:"anthropic",api:"anthropic",key:"anthropic",heuristic:"heuristic",none:"heuristic",stub:"heuristic",off:"heuristic"}});var Zc={};tn(Zc,{moveHooks:()=>T0,parseMoveArgs:()=>A0,runMove:()=>ih});import na from"node:fs";import k0 from"node:os";import hr from"node:path";import{spawnSync as b0}from"node:child_process";async function eu(n,e){try{let t=await fetch("http://127.0.0.1:"+n+"/v1/health",{headers:{authorization:"Bearer "+(e||"")},signal:AbortSignal.timeout(1500)});return t.ok?{up:!0,...await t.json()}:{up:t.status===401,status:t.status}}catch{return{up:!1}}}function A0(n){let e={to:null,dryRun:!1,yes:!1,force:!1,json:!1,backups:!1,deleteBackup:null,daemonPid:null,noRestart:!1};for(let t=0;t<n.length;t++){let s=n[t];s==="--dry-run"||s==="-n"?e.dryRun=!0:s==="--yes"||s==="-y"?e.yes=!0:s==="--force"?e.force=!0:s==="--json"?e.json=!0:s==="--backups"?e.backups=!0:s==="--no-restart"?e.noRestart=!0:s==="--help"||s==="-h"?e.help=!0:s==="--delete-backup"?e.deleteBackup=n[t+1]&&!n[t+1].startsWith("--")?n[++t]:!0:s==="--daemon-pid"?e.daemonPid=Number(n[++t])||null:e.to||(e.to=s)}return e}function T0({from:n,cfg:e,token:t,daemonPid:s=null,noRestart:o=!1,say:r=()=>{},env:i=process.env}){let a=Lt(),l=na.existsSync(a)&&!i.CHECKPOINTS_SERVICE_DRY_RUN,c=e.port,u=!1;return{async stopWriters(){let d=await eu(c,t);if(u=d.up,!!d.up){if(l){let f=Dn()==="darwin"?["launchctl","unload",a]:["systemctl","--user","stop",Ws];b0(f[0],f.slice(1),{encoding:"utf8"})}else{let f=s||d.pid||null;if(!f)try{f=Number(na.readFileSync(hr.join(n,".daemon.pid"),"utf8"))||null}catch{}if(!f)throw new Error("the daemon is running but I can’t tell its process; stop it (Ctrl-C on `leftoff serve`) and run this again");try{process.kill(f,"SIGTERM")}catch{}for(let h=0;h<50&&x0(f);h++)await Qc(100);if(x0(f)){try{process.kill(f,"SIGKILL")}catch{}await Qc(200)}}for(let f=0;f<40&&(await eu(c,t)).up;f++)await Qc(100);if((await eu(c,t)).up)throw new Error("the daemon didn’t stop");r("Daemon stopped")}},async switchTo(d){if(Vc(d),na.existsSync(a)){let f=Rn({root:d,bin:$e});na.readFileSync(a,"utf8")!==f&&na.writeFileSync(a,f)}},async startWriters(d){if(o||!u)return{ok:!0,skipped:!0};let f=ve(d),h=Q(d).port;if(l)for(let p of Qs())b0(p[0],p.slice(1),{encoding:"utf8"});else{let{LEFTOFF_DIR:p,CHECKPOINTS_DIR:m,...g}=i;Wc({root:d,bin:$e,env:{...g,CHECKPOINTS_DIR:d}})}for(let p=0;p<80;p++){let m=await eu(h,f);if(m.up&&m.folder&&hr.resolve(m.folder)===hr.resolve(d))return{ok:!0,port:h};if(m.up&&m.status===401)return{ok:!1,error:"something else is answering on port "+h};await Qc(150)}return{ok:!1,error:"no answer on port "+h}}}}async function ih({argv:n=[],out:e=process.stdout,input:t=process.stdin,env:s=process.env}={}){let o=ee,r=A0(n),i=w=>{r.json||e.write(w+`
297
+ `)},a=Z();if(r.help)return e.write(OA),0;if(r.backups||r.deleteBackup){let w=ir(a);if(r.backups){if(r.json)return e.write(JSON.stringify({backups:w})+`
298
+ `),0;w.length||i("No backups from a move next to "+we(a)+".");for(let v of w)i(" "+v.display+o.dim(" (moved "+(v.at||"?")+")"));return 0}let x=r.deleteBackup===!0?w.length===1?w[0].path:null:hr.resolve(v0(r.deleteBackup));if(!x)return i(w.length?"Several backups; name one: "+V+" move --delete-backup <path>":"No backups to delete."),1;if(!r.yes){let v=new kt({input:t,output:e});if(!await v.confirm("Delete "+we(x)+" for good?",!1).finally(()=>v.close&&v.close()))return i("Kept it."),0}try{return Hi(x),i(o.green("✓")+" Deleted "+we(x)),0}catch(v){return i(o.red("✗")+" "+v.message),1}}if(!r.to){let w=or({current:a});if(i("usage: "+V+` move <folder> [--dry-run] [--yes]
299
299
 
300
300
  Now: `+we(a)+(w.current.syncedBy?" (in "+w.current.syncedBy.label+")":" (not in a synced folder)")),w.candidates.length){i(`
301
301
  Synced folders on this Mac:`);for(let x of w.candidates)i(" "+x.label.padEnd(14)+" "+x.display+(x.hasLeftoff?o.dim(" · already has Leftoff data (joins it)"):"")+`
302
- `+" ".repeat(15)+o.dim(x.tradeoff))}return 1}let l=hr.resolve(g0(r.to)),c=Q(a),u=de(c),d=null;try{d=Nn(u,{dir:uc(s)})}catch{}let f=rr(a,l,{machine:u,identity:d});r.force&&(f.collisions=f.collisions.filter(w=>w.code!=="machine_name_maybe_taken"),f.ok=!f.errors.length&&!f.collisions.length);let h=w=>{try{Cf({from:a,to:l,...w},s)}catch{}};if(r.json&&r.dryRun){let{files:w,...x}=f;return e.write(JSON.stringify({...x,sample:w.filter(v=>v.action!=="same").slice(0,20)})+`
302
+ `+" ".repeat(15)+o.dim(x.tradeoff))}return 1}let l=hr.resolve(v0(r.to)),c=Q(a),u=de(c),d=null;try{d=Ln(u,{dir:fc(s)})}catch{}let f=rr(a,l,{machine:u,identity:d});r.force&&(f.collisions=f.collisions.filter(w=>w.code!=="machine_name_maybe_taken"),f.ok=!f.errors.length&&!f.collisions.length);let h=w=>{try{Lf({from:a,to:l,...w},s)}catch{}};if(r.json&&r.dryRun){let{files:w,...x}=f;return e.write(JSON.stringify({...x,sample:w.filter(v=>v.action!=="same").slice(0,20)})+`
303
303
  `),f.ok?0:1}i(o.bold((f.merge?"Join ":"Move to ")+we(l))+o.dim(" (from "+we(a)+")"));for(let w of[...f.errors,...f.collisions])i(" "+o.red("✗")+" "+w.message);if(!f.ok)return h({state:"refused",ok:!1,error:[...f.errors,...f.collisions][0].message}),r.json&&e.write(JSON.stringify({ok:!1,errors:f.errors,collisions:f.collisions})+`
304
- `),1;let p=f.counts;i(" "+p.total+" files: "+p.copy+" to copy"+(p.same?", "+p.same+" already there":"")+(p.keepTarget?", "+p.keepTarget+" kept from the joined folder":"")+(p.conflictCopy?", "+p.conflictCopy+" list"+(p.conflictCopy===1?"":"s")+" merged":"")+(p.renameCopy?", "+p.renameCopy+" renamed":""));for(let w of f.warnings)i(" "+o.yellow("!")+" "+w.message);if(i(" The old folder stays, renamed to "+we(f.backup)+", until you delete it."),r.dryRun)return i(o.dim("Dry run: nothing changed.")),0;if(!r.yes){let w=new wt({input:t,output:e});if(!await w.confirm(f.merge?"Join it?":"Move it?",!0).finally(()=>w.close&&w.close()))return i("Nothing changed."),0}h({state:"moving",ok:null,message:"Copying "+p.total+" files"});let m=S0({from:a,cfg:c,token:ve(a),daemonPid:r.daemonPid,noRestart:r.noRestart,say:w=>i(" "+o.dim("· "+w)),env:s}),g=await Nf(f,{hooks:m,machine:u,say:w=>{i(" "+o.dim("· "+w)),h({state:"moving",ok:null,message:w})}});if(!g.ok)return i(" "+o.red("✗")+" "+g.error+(g.rolledBack?o.dim(" · put back: still using "+we(a)):"")),h({state:"failed",ok:!1,error:g.error,stage:g.stage,rolledBack:!!g.rolledBack}),r.json&&e.write(JSON.stringify(g)+`
304
+ `),1;let p=f.counts;i(" "+p.total+" files: "+p.copy+" to copy"+(p.same?", "+p.same+" already there":"")+(p.keepTarget?", "+p.keepTarget+" kept from the joined folder":"")+(p.conflictCopy?", "+p.conflictCopy+" list"+(p.conflictCopy===1?"":"s")+" merged":"")+(p.renameCopy?", "+p.renameCopy+" renamed":""));for(let w of f.warnings)i(" "+o.yellow("!")+" "+w.message);if(i(" The old folder stays, renamed to "+we(f.backup)+", until you delete it."),r.dryRun)return i(o.dim("Dry run: nothing changed.")),0;if(!r.yes){let w=new kt({input:t,output:e});if(!await w.confirm(f.merge?"Join it?":"Move it?",!0).finally(()=>w.close&&w.close()))return i("Nothing changed."),0}h({state:"moving",ok:null,message:"Copying "+p.total+" files"});let m=T0({from:a,cfg:c,token:ve(a),daemonPid:r.daemonPid,noRestart:r.noRestart,say:w=>i(" "+o.dim("· "+w)),env:s}),g=await Rf(f,{hooks:m,machine:u,say:w=>{i(" "+o.dim("· "+w)),h({state:"moving",ok:null,message:w})}});if(!g.ok)return i(" "+o.red("✗")+" "+g.error+(g.rolledBack?o.dim(" · put back: still using "+we(a)):"")),h({state:"failed",ok:!1,error:g.error,stage:g.stage,rolledBack:!!g.rolledBack}),r.json&&e.write(JSON.stringify(g)+`
305
305
  `),1;let S=ar(l,{cli:V}),k=or({current:l});return h({state:"done",ok:!0,copied:g.copied,verified:g.verified,kept:g.kept,conflicts:g.conflicts,backup:g.backup,merge:f.merge,syncedBy:k.current.syncedBy,join:S}),i(" "+o.green("✓")+" "+(f.merge?"Joined":"Moved")+": "+g.copied+" copied, "+g.verified+" verified"+(g.kept?", "+g.kept+" kept":"")+". Leftoff now uses "+we(l)),i(" "+o.dim("Backup: "+we(g.backup)+" (delete it when you’re happy: "+V+" move --delete-backup)")),i(`
306
306
  On each other Mac, once the folder has synced there:
307
307
  `+o.cyan(S.installed)+o.dim(" (Leftoff installed)")+`
308
308
  `+o.cyan(S.fresh)+o.dim(" (not yet)")),r.json&&e.write(JSON.stringify({...g,join:S})+`
309
- `),0}var bA,Xc,g0,y0,Zi=J(()=>{xe();Qs();is();eo();os();Pn();_t();bA=`${V} move: move the Leftoff folder, or join a synced one another Mac uses
309
+ `),0}var OA,Qc,v0,x0,ta=J(()=>{xe();to();cs();no();as();Fn();Ot();OA=`${V} move: move the Leftoff folder, or join a synced one another Mac uses
310
310
 
311
311
  ${V} move ~/Sync/Leftoff move (or join) it: copy, verify, switch, keep a backup
312
312
  ${V} move ~/Sync/Leftoff --dry-run say what it would do; change nothing
@@ -317,27 +317,27 @@ On each other Mac, once the folder has synced there:
317
317
 
318
318
  The old folder is renamed to <name>.leftoff-backup-<date> and kept until you delete it.
319
319
  The board does the same from Settings → Sync → Set up syncing.
320
- `,Xc=n=>new Promise(e=>setTimeout(e,n)),g0=n=>n==="~"?p0.homedir():String(n).startsWith("~/")?hr.join(process.env.HOME||p0.homedir(),String(n).slice(2)):n;y0=n=>{try{return process.kill(n,0),!0}catch{return!1}}});import k0 from"node:fs";import pr from"node:path";function b0(n){let e={pos:[],yes:!1,json:!1,daemonPid:null,path:null,timeoutMs:null};for(let t=0;t<n.length;t++){let s=n[t];s==="--yes"||s==="-y"?e.yes=!0:s==="--json"?e.json=!0:s==="--help"||s==="-h"?e.help=!0:s==="--daemon-pid"?e.daemonPid=Number(n[++t])||null:s==="--path"||s==="--dir"?e.path=n[++t]:s.startsWith("--path=")||s.startsWith("--dir=")?e.path=s.split("=").slice(1).join("="):s==="--timeout"?e.timeoutMs=Number(n[++t])*1e3:e.pos.push(s)}return e}async function v0({env:n=process.env,say:e=()=>{},confirm:t=async()=>!1,onLine:s=()=>{}}={}){if(kn(n).installed)return{ok:!0,already:!0};let r=_s(n);if(!r.brew)return{ok:!1,code:"no_brew",directions:r.directions,links:r.links,error:"Homebrew isn’t installed, so Leftoff can’t install Syncthing for you."};if(!await t(r.command))return{ok:!1,code:"declined",error:"Not installed."};let i=await Ir({env:n,onLine:s});return i.ok?(e("Waiting for Syncthing to start…"),(await Es({env:n,timeoutMs:Number(n.LEFTOFF_SYNCTHING_START_MS)||9e4})).ok?{ok:!0}:{ok:!1,code:"not_running",error:"Syncthing installed, but it didn’t start. Try `brew services restart syncthing`."}):i}async function ih({env:n=process.env,say:e=()=>{}}={}){let t=ht({env:n});if(t.ok)try{return await t.client.ping(),{ok:!0,client:t.client}}catch{}if(!kn(n).installed&&!t.cfg)return{ok:!1,code:"not_installed",error:"Syncthing isn’t installed on this Mac."};e("Starting Syncthing…");let o=Nr(n);if(!o.ok)return{ok:!1,code:o.code,error:"Couldn’t start Syncthing: "+o.error};let r=await Es({env:n,timeoutMs:Number(n.LEFTOFF_SYNCTHING_START_MS)||6e4});return r.ok?{ok:!0,client:r.client}:{ok:!1,code:"not_running",error:r.error}}async function vA({env:n=process.env,target:e=null,daemonPid:t=null,say:s=()=>{},status:o=()=>{}}={}){let r=await ih({env:n,say:s});if(!r.ok)return r;let i=r.client,a=await i.systemStatus(),l=await i.folders(),c=Z(),u=l.find(p=>p.id===Ee),d=pr.resolve(xa(e||u&&u.path||Mr(l,n),n)),f=Os(d,l);if(f)return{ok:!1,code:"nested",error:Ne(d,n)+(f.how==="inside"?" is inside":" contains")+" your Syncthing folder “"+f.label+"”. Pick a folder outside it; Leftoff won’t change that folder."};if(u&&pr.resolve(u.path)!==d)return{ok:!1,code:"folder_elsewhere",error:"Syncthing already has a Leftoff folder at "+Ne(u.path,n)+". Use that path, or Stop syncing first."};if(pr.resolve(c)!==d){s("Moving your Leftoff folder to "+Ne(d,n)+" (copy, verify, keep a backup)…"),o({state:"moving",message:"Moving your folder to "+Ne(d,n)+"…"});let{runMove:p}=await Promise.resolve().then(()=>(Zi(),Vc)),m={write:()=>!0};if(await p({argv:[d,"--yes","--json",...t?["--daemon-pid",String(t)]:[]],out:m,env:n})!==0)return{ok:!1,code:"move_failed",error:"The move stopped: "+((Zs(n)||{}).error||"unknown error")+". Nothing changed; Leftoff still uses "+Ne(c,n)+"."}}s("Adding the Leftoff folder to Syncthing…"),o({state:"configuring",message:"Adding the folder to Syncthing…"});let h=await Ta(i,{path:d,myID:a.myID});return Kn({folderPath:d},n),{ok:!0,path:d,created:h.created,myID:a.myID}}async function xA(n,{env:e=process.env,target:t=null,daemonPid:s=null,yes:o=!1,say:r=()=>{},status:i=()=>{},confirmInstall:a=null,onLine:l=()=>{},input:c,out:u,now:d=()=>Date.now(),pollMs:f=Number(e.LEFTOFF_SYNCTHING_POLL_MS)||1e3,syncTimeoutMs:h=30*6e4}={}){let p;try{p=Lu(n,{now:d()})}catch(P){return{ok:!1,code:P.code||"bad_code",error:P.message}}let m=Du(n);if(bn(e).usedCodes.includes(m))return{ok:!1,code:"used",error:"This code was already used on this Mac. Make a new one on "+(p.machine||"the other Mac")+"."};let g=p.machine||"the other Mac";if(i({state:"starting",message:"Checking Syncthing…",with:p.machine}),!kn(e).installed&&!ht({env:e}).cfg){if(!a)return{ok:!1,code:"needs_install",error:"Syncthing isn’t installed on this Mac yet.",plan:_s(e)};i({state:"installing",message:"Installing Syncthing…"});let P=await v0({env:e,say:r,confirm:a,onLine:l});if(!P.ok)return P}let S=await ih({env:e,say:r});if(!S.ok)return S;let k=S.client,w=await k.systemStatus();if(Ce(w.myID,p.deviceId))return{ok:!1,code:"same_mac",error:"This is the Mac that made the code. Paste it on your other Mac."};let x=Z(),v=await k.folders(),b=v.find(P=>P.id===p.folderId),y=pr.resolve(xa(t||b&&b.path||Mr(v,e),e)),_=Os(y,v);if(_)return{ok:!1,code:"nested",error:Ne(y,e)+" is inside your Syncthing folder “"+_.label+"”. Pick another folder (--path); Leftoff won’t change that folder."};if(k0.existsSync(y)&&pr.resolve(x)!==y&&!Rn(y)&&k0.readdirSync(y).filter(W=>!/^\.DS_Store$|^\.stfolder$|^\.stignore$/.test(W)).length)return{ok:!1,code:"not_empty",error:Ne(y,e)+" already has other files in it. Pick an empty folder (--path)."};let O=(await k.devices()).find(P=>Ce(P.deviceID,w.myID))||{},I=bn(e),N=I.originalName||yo(O.name)||de(Q(x));I.originalName||Kn({originalName:N},e);let M=async()=>{try{bn(e).originalName&&(await k.patchDevice(w.myID,{name:N},{self:w.myID}),Kn({originalName:null},e))}catch{}},z=Ea(p.secret,{joinerId:w.myID,firstId:p.deviceId,folderId:p.folderId}),U=Pu(N,z);await k.patchDevice(w.myID,{name:U},{self:w.myID});try{r("Adding "+g+" to Syncthing and sharing the Leftoff folder at "+Ne(y,e)+"…");let P=await _a(k,{id:p.deviceId,name:p.machine?p.machine+" (Leftoff)":"Leftoff Mac",env:e});await Ta(k,{path:y,peers:[p.deviceId],myID:w.myID}),Kn({folderPath:y},e),P.created||P.device.paused||await k.reconnectDevice(p.deviceId,{allow:p.deviceId}).catch(()=>{});let W=d(),oe=!1,K="",pe=Number(e.LEFTOFF_JOIN_STUCK_MS)||6e4,Ie={first:g,name:N,url:"http://127.0.0.1:8384",steps:Fu({first:g,name:N}),board:"Or on "+g+"’s Leftoff board: Settings → Sync → Accept "+N+"."},Se=(Y,ne={})=>i({state:"waiting",message:Y,with:p.machine,...oe?{stuck:Ie}:{},...ne}),q=async({again:Y=!1}={})=>{let ne=0,se=d();for(Se(Y?"Lost the connection to "+g+". Waiting for it to come back…":"Waiting for "+g+" to accept this Mac…");;){let T=await Oa(k,{peerId:p.deviceId,folderId:p.folderId});if(ne=T.accepted?ne+1:0,ne>=2)return!0;let L=T.connected&&T.state==="notSharing"?g+" added this Mac but isn’t sharing the Leftoff folder with it yet. On "+g+", tick Leftoff on this Mac’s Sharing tab in Syncthing.":"";if(L&&L!==K&&(r(L),Se(L)),K=L,!Y&&!oe&&d()-W>=pe&&(oe=!0,r("If it’s stuck: "+Ie.steps.join(" ")+" "+Ie.board),Se("Still waiting for "+g+" to accept this Mac…")),!Y&&d()>p.expiresAt+6e4||Y&&d()-se>h)return!1;await sh(f)}};if(r("Waiting for "+g+" to accept this Mac…"),!await q())return{ok:!1,code:"expired",error:g+" didn’t accept this Mac before the code expired. Is Leftoff running there? Make a new code and try again, or add this Mac by hand: "+Ie.steps.join(" ")};await M(),Kn({usedCodes:[...bn(e).usedCodes,m].slice(-50)},e),r(g+" accepted this Mac. Syncing…");let re="",B=0,Je=d();for(;;){let Y=await Oa(k,{peerId:p.deviceId,folderId:p.folderId}),ne=await k.dbStatus(p.folderId).catch(()=>null),se=ne&&ne.globalFiles>0&&!ne.needFiles&&ne.state==="idle"&&Rn(y);if(!Y.accepted&&!se){if(r("Lost the connection to "+g+". Waiting for it to come back…"),re="",B=0,!await q({again:!0}))return{ok:!1,code:"sync_timeout",error:"Lost the connection to "+g+" and it didn’t come back. Syncthing keeps trying; run `"+V+" move "+Ne(y,e)+"` once the files are here."};r("Connected to "+g+" again. Syncing…");continue}let T=Uu(ne);if(T!==re&&(re=T,r(T),i({state:"syncing",message:T,with:p.machine,have:ne?ne.inSyncFiles:null,total:ne?ne.globalFiles:null})),B=se?B+1:0,B>=2)break;if(d()-Je>h)return{ok:!1,code:"sync_timeout",error:"The first sync is taking a long time ("+T+"). It keeps going in Syncthing; run `"+V+" join` again later, or `"+V+" move "+Ne(y,e)+"` once it’s done."};await sh(f)}}finally{await M()}if(i({state:"merging",message:"Joining the folder…",with:p.machine}),pr.resolve(x)===y)return{ok:!0,path:y,merged:!1,with:p.machine};if(Rn(x)){r("Joining: this Mac’s Leftoff data goes into "+Ne(y,e)+" (nothing of "+g+"’s is overwritten)…");let{runMove:P}=await Promise.resolve().then(()=>(Zi(),Vc));return await P({argv:[y,"--yes","--json",...s?["--daemon-pid",String(s)]:[]],out:{write:()=>!0},env:e})!==0?{ok:!1,code:"merge_failed",error:"Synced, but joining the folder stopped: "+((Zs(e)||{}).error||"unknown error")+". Run `"+V+" move "+Ne(y,e)+"` to try again."}:{ok:!0,path:y,merged:!0,with:p.machine}}r("Synced. Setting up Leftoff on this Mac with that folder…");let{runSetup:E}=await Promise.resolve().then(()=>(eo(),Ic));return await E({flags:{dir:y,...o?{yes:!0}:{}},input:c,out:u,env:e}),{ok:!0,path:y,merged:!1,fresh:!0,with:p.machine}}function rh(n){if(!n)return"never";let e=Math.max(0,(Date.now()-new Date(n).getTime())/1e3);return e<60?"just now":e<3600?Math.round(e/60)+" min ago":e<86400?Math.round(e/3600)+" h ago":Math.round(e/86400)+" d ago"}function ah(n,e=null){let t=[];if(!n.installed)return t.push(["info","Syncthing: not installed"+(n.brew?" · "+V+" sync install":" · get it from syncthing.net or install Homebrew first")]),t;if(!n.running)return t.push(["warn","Syncthing: installed, not running"+(n.reason?" ("+n.reason+")":"")+" · "+V+" sync start"]),t;if(t.push(["ok","Syncthing: running"+(n.version?" "+n.version:"")]),!n.folder)return t.push(["info","Leftoff folder: not in Syncthing yet · "+V+" sync setup"+(n.nested?" (already inside your Syncthing folder “"+n.nested.label+"”)":"")]),t;let s=n.folder;t.push([s.isCurrent?"ok":"warn","Leftoff folder: "+s.display+(s.isCurrent?"":" (Leftoff uses another folder; "+V+" sync setup)")+" · "+(s.state||"?")+(s.globalFiles!=null?" · "+(s.inSyncFiles!=null?s.inSyncFiles+"/":"")+s.globalFiles+" files":"")+" · last sync "+rh(s.lastSync)]),n.peers.length||t.push(["info","Peers: none yet · "+V+" sync pair"]);for(let o of n.peers)t.push([o.connected?"ok":"warn","Peer "+o.name+": "+(o.connected?"connected"+(o.completion!=null?" · "+o.completion+"% in sync":""):"not connected · last seen "+rh(o.lastSeen))]);for(let o of s.errors||[])t.push(["warn","Folder error: "+(o.path?o.path+": ":"")+o.error]);for(let o of n.knocked||[])t.push(["warn","A Mac named “"+o.name+"” asked to connect without the pairing proof ("+o.short+") · not accepted. If that’s your Mac: Accept "+o.name+" on the board (Settings → Sync) or "+V+' sync accept "'+o.name+'"']);return e&&e.armed&&t.push(["info","Pairing code: live until "+new Date(e.expiresAt).toLocaleTimeString()]),t}async function x0({argv:n=[],api:e=null,env:t=process.env,out:s=process.stdout,input:o=process.stdin}={}){let r=b0(n),i=ee,a=u=>{r.json||s.write(u+`
321
- `)},l=r.pos[0]||"status";if(r.help||l==="help")return s.write(oh),0;let c={ok:i.green("✓"),warn:i.yellow("!"),info:i.dim("·")};if(l==="status"){let u=await tn({env:t,root:Z()}),d=$n(Pt(t));if(r.json)return s.write(JSON.stringify({...u,pairing:d})+`
322
- `),0;for(let[f,h]of ah(u,d))a(c[f]+" "+h);return 0}if(l==="install"){let u=new wt({input:o,output:s,yes:r.yes}),d=await v0({env:t,say:a,onLine:f=>a(i.dim(" "+f)),confirm:async f=>(a(`Leftoff will run:
320
+ `,Qc=n=>new Promise(e=>setTimeout(e,n)),v0=n=>n==="~"?k0.homedir():String(n).startsWith("~/")?hr.join(process.env.HOME||k0.homedir(),String(n).slice(2)):n;x0=n=>{try{return process.kill(n,0),!0}catch{return!1}}});import _0 from"node:fs";import pr from"node:path";function E0(n){let e={pos:[],yes:!1,json:!1,daemonPid:null,path:null,timeoutMs:null};for(let t=0;t<n.length;t++){let s=n[t];s==="--yes"||s==="-y"?e.yes=!0:s==="--json"?e.json=!0:s==="--help"||s==="-h"?e.help=!0:s==="--daemon-pid"?e.daemonPid=Number(n[++t])||null:s==="--path"||s==="--dir"?e.path=n[++t]:s.startsWith("--path=")||s.startsWith("--dir=")?e.path=s.split("=").slice(1).join("="):s==="--timeout"?e.timeoutMs=Number(n[++t])*1e3:e.pos.push(s)}return e}async function O0({env:n=process.env,say:e=()=>{},confirm:t=async()=>!1,onLine:s=()=>{}}={}){if(vn(n).installed)return{ok:!0,already:!0};let r=Cs(n);if(!r.brew)return{ok:!1,code:"no_brew",directions:r.directions,links:r.links,error:"Homebrew isn’t installed, so Leftoff can’t install Syncthing for you."};if(!await t(r.command))return{ok:!1,code:"declined",error:"Not installed."};let i=await Ir({env:n,onLine:s});return i.ok?(e("Waiting for Syncthing to start…"),(await Ns({env:n,timeoutMs:Number(n.LEFTOFF_SYNCTHING_START_MS)||9e4})).ok?{ok:!0}:{ok:!1,code:"not_running",error:"Syncthing installed, but it didn’t start. Try `brew services restart syncthing`."}):i}async function uh({env:n=process.env,say:e=()=>{}}={}){let t=mt({env:n});if(t.ok)try{return await t.client.ping(),{ok:!0,client:t.client}}catch{}if(!vn(n).installed&&!t.cfg)return{ok:!1,code:"not_installed",error:"Syncthing isn’t installed on this Mac."};e("Starting Syncthing…");let o=Mr(n);if(!o.ok)return{ok:!1,code:o.code,error:"Couldn’t start Syncthing: "+o.error};let r=await Ns({env:n,timeoutMs:Number(n.LEFTOFF_SYNCTHING_START_MS)||6e4});return r.ok?{ok:!0,client:r.client}:{ok:!1,code:"not_running",error:r.error}}async function CA({env:n=process.env,target:e=null,daemonPid:t=null,say:s=()=>{},status:o=()=>{}}={}){let r=await uh({env:n,say:s});if(!r.ok)return r;let i=r.client,a=await i.systemStatus(),l=await i.folders(),c=Z(),u=l.find(p=>p.id===Ee),d=pr.resolve(_a(e||u&&u.path||Lr(l,n),n)),f=Is(d,l);if(f)return{ok:!1,code:"nested",error:Me(d,n)+(f.how==="inside"?" is inside":" contains")+" your Syncthing folder “"+f.label+"”. Pick a folder outside it; Leftoff won’t change that folder."};if(u&&pr.resolve(u.path)!==d)return{ok:!1,code:"folder_elsewhere",error:"Syncthing already has a Leftoff folder at "+Me(u.path,n)+". Use that path, or Stop syncing first."};if(pr.resolve(c)!==d){s("Moving your Leftoff folder to "+Me(d,n)+" (copy, verify, keep a backup)…"),o({state:"moving",message:"Moving your folder to "+Me(d,n)+"…"});let{runMove:p}=await Promise.resolve().then(()=>(ta(),Zc)),m={write:()=>!0};if(await p({argv:[d,"--yes","--json",...t?["--daemon-pid",String(t)]:[]],out:m,env:n})!==0)return{ok:!1,code:"move_failed",error:"The move stopped: "+((eo(n)||{}).error||"unknown error")+". Nothing changed; Leftoff still uses "+Me(c,n)+"."}}s("Adding the Leftoff folder to Syncthing…"),o({state:"configuring",message:"Adding the folder to Syncthing…"});let h=await Oa(i,{path:d,myID:a.myID});return zn({folderPath:d},n),{ok:!0,path:d,created:h.created,myID:a.myID}}async function NA(n,{env:e=process.env,target:t=null,daemonPid:s=null,yes:o=!1,say:r=()=>{},status:i=()=>{},confirmInstall:a=null,onLine:l=()=>{},input:c,out:u,now:d=()=>Date.now(),pollMs:f=Number(e.LEFTOFF_SYNCTHING_POLL_MS)||1e3,syncTimeoutMs:h=30*6e4}={}){let p;try{p=Ru(n,{now:d()})}catch(P){return{ok:!1,code:P.code||"bad_code",error:P.message}}let m=Pu(n);if(xn(e).usedCodes.includes(m))return{ok:!1,code:"used",error:"This code was already used on this Mac. Make a new one on "+(p.machine||"the other Mac")+"."};let g=p.machine||"the other Mac";if(i({state:"starting",message:"Checking Syncthing…",with:p.machine}),!vn(e).installed&&!mt({env:e}).cfg){if(!a)return{ok:!1,code:"needs_install",error:"Syncthing isn’t installed on this Mac yet.",plan:Cs(e)};i({state:"installing",message:"Installing Syncthing…"});let P=await O0({env:e,say:r,confirm:a,onLine:l});if(!P.ok)return P}let S=await uh({env:e,say:r});if(!S.ok)return S;let k=S.client,w=await k.systemStatus();if(Ce(w.myID,p.deviceId))return{ok:!1,code:"same_mac",error:"This is the Mac that made the code. Paste it on your other Mac."};let x=Z(),v=await k.folders(),b=v.find(P=>P.id===p.folderId),y=pr.resolve(_a(t||b&&b.path||Lr(v,e),e)),_=Is(y,v);if(_)return{ok:!1,code:"nested",error:Me(y,e)+" is inside your Syncthing folder “"+_.label+"”. Pick another folder (--path); Leftoff won’t change that folder."};if(_0.existsSync(y)&&pr.resolve(x)!==y&&!jn(y)&&_0.readdirSync(y).filter(W=>!/^\.DS_Store$|^\.stfolder$|^\.stignore$/.test(W)).length)return{ok:!1,code:"not_empty",error:Me(y,e)+" already has other files in it. Pick an empty folder (--path)."};let O=(await k.devices()).find(P=>Ce(P.deviceID,w.myID))||{},N=xn(e),I=N.originalName||So(O.name)||de(Q(x));N.originalName||zn({originalName:I},e);let M=async()=>{try{xn(e).originalName&&(await k.patchDevice(w.myID,{name:I},{self:w.myID}),zn({originalName:null},e))}catch{}},Y=Na(p.secret,{joinerId:w.myID,firstId:p.deviceId,folderId:p.folderId}),U=Fu(I,Y);await k.patchDevice(w.myID,{name:U},{self:w.myID});try{r("Adding "+g+" to Syncthing and sharing the Leftoff folder at "+Me(y,e)+"…");let P=await Ca(k,{id:p.deviceId,name:p.machine?p.machine+" (Leftoff)":"Leftoff Mac",env:e});await Oa(k,{path:y,peers:[p.deviceId],myID:w.myID}),zn({folderPath:y},e),P.created||P.device.paused||await k.reconnectDevice(p.deviceId,{allow:p.deviceId}).catch(()=>{});let W=d(),oe=!1,K="",pe=Number(e.LEFTOFF_JOIN_STUCK_MS)||6e4,Ie={first:g,name:I,url:"http://127.0.0.1:8384",steps:Hu({first:g,name:I}),board:"Or on "+g+"’s Leftoff board: Settings → Sync → Accept "+I+"."},Se=(z,ne={})=>i({state:"waiting",message:z,with:p.machine,...oe?{stuck:Ie}:{},...ne}),q=async({again:z=!1}={})=>{let ne=0,se=d();for(Se(z?"Lost the connection to "+g+". Waiting for it to come back…":"Waiting for "+g+" to accept this Mac…");;){let T=await Ia(k,{peerId:p.deviceId,folderId:p.folderId});if(ne=T.accepted?ne+1:0,ne>=2)return!0;let L=T.connected&&T.state==="notSharing"?g+" added this Mac but isn’t sharing the Leftoff folder with it yet. On "+g+", tick Leftoff on this Mac’s Sharing tab in Syncthing.":"";if(L&&L!==K&&(r(L),Se(L)),K=L,!z&&!oe&&d()-W>=pe&&(oe=!0,r("If it’s stuck: "+Ie.steps.join(" ")+" "+Ie.board),Se("Still waiting for "+g+" to accept this Mac…")),!z&&d()>p.expiresAt+6e4||z&&d()-se>h)return!1;await ah(f)}};if(r("Waiting for "+g+" to accept this Mac…"),!await q())return{ok:!1,code:"expired",error:g+" didn’t accept this Mac before the code expired. Is Leftoff running there? Make a new code and try again, or add this Mac by hand: "+Ie.steps.join(" ")};await M(),zn({usedCodes:[...xn(e).usedCodes,m].slice(-50)},e),r(g+" accepted this Mac. Syncing…");let re="",B=0,Ve=d();for(;;){let z=await Ia(k,{peerId:p.deviceId,folderId:p.folderId}),ne=await k.dbStatus(p.folderId).catch(()=>null),se=ne&&ne.globalFiles>0&&!ne.needFiles&&ne.state==="idle"&&jn(y);if(!z.accepted&&!se){if(r("Lost the connection to "+g+". Waiting for it to come back…"),re="",B=0,!await q({again:!0}))return{ok:!1,code:"sync_timeout",error:"Lost the connection to "+g+" and it didn’t come back. Syncthing keeps trying; run `"+V+" move "+Me(y,e)+"` once the files are here."};r("Connected to "+g+" again. Syncing…");continue}let T=Bu(ne);if(T!==re&&(re=T,r(T),i({state:"syncing",message:T,with:p.machine,have:ne?ne.inSyncFiles:null,total:ne?ne.globalFiles:null})),B=se?B+1:0,B>=2)break;if(d()-Ve>h)return{ok:!1,code:"sync_timeout",error:"The first sync is taking a long time ("+T+"). It keeps going in Syncthing; run `"+V+" join` again later, or `"+V+" move "+Me(y,e)+"` once it’s done."};await ah(f)}}finally{await M()}if(i({state:"merging",message:"Joining the folder…",with:p.machine}),pr.resolve(x)===y)return{ok:!0,path:y,merged:!1,with:p.machine};if(jn(x)){r("Joining: this Mac’s Leftoff data goes into "+Me(y,e)+" (nothing of "+g+"’s is overwritten)…");let{runMove:P}=await Promise.resolve().then(()=>(ta(),Zc));return await P({argv:[y,"--yes","--json",...s?["--daemon-pid",String(s)]:[]],out:{write:()=>!0},env:e})!==0?{ok:!1,code:"merge_failed",error:"Synced, but joining the folder stopped: "+((eo(e)||{}).error||"unknown error")+". Run `"+V+" move "+Me(y,e)+"` to try again."}:{ok:!0,path:y,merged:!0,with:p.machine}}r("Synced. Setting up Leftoff on this Mac with that folder…");let{runSetup:E}=await Promise.resolve().then(()=>(no(),Mc));return await E({flags:{dir:y,...o?{yes:!0}:{}},input:c,out:u,env:e}),{ok:!0,path:y,merged:!1,fresh:!0,with:p.machine}}function ch(n){if(!n)return"never";let e=Math.max(0,(Date.now()-new Date(n).getTime())/1e3);return e<60?"just now":e<3600?Math.round(e/60)+" min ago":e<86400?Math.round(e/3600)+" h ago":Math.round(e/86400)+" d ago"}function dh(n,e=null){let t=[];if(!n.installed)return t.push(["info","Syncthing: not installed"+(n.brew?" · "+V+" sync install":" · get it from syncthing.net or install Homebrew first")]),t;if(!n.running)return t.push(["warn","Syncthing: installed, not running"+(n.reason?" ("+n.reason+")":"")+" · "+V+" sync start"]),t;if(t.push(["ok","Syncthing: running"+(n.version?" "+n.version:"")]),!n.folder)return t.push(["info","Leftoff folder: not in Syncthing yet · "+V+" sync setup"+(n.nested?" (already inside your Syncthing folder “"+n.nested.label+"”)":"")]),t;let s=n.folder;t.push([s.isCurrent?"ok":"warn","Leftoff folder: "+s.display+(s.isCurrent?"":" (Leftoff uses another folder; "+V+" sync setup)")+" · "+(s.state||"?")+(s.globalFiles!=null?" · "+(s.inSyncFiles!=null?s.inSyncFiles+"/":"")+s.globalFiles+" files":"")+" · last sync "+ch(s.lastSync)]),n.peers.length||t.push(["info","Peers: none yet · "+V+" sync pair"]);for(let o of n.peers)t.push([o.connected?"ok":"warn","Peer "+o.name+": "+(o.connected?"connected"+(o.completion!=null?" · "+o.completion+"% in sync":""):"not connected · last seen "+ch(o.lastSeen))]);for(let o of s.errors||[])t.push(["warn","Folder error: "+(o.path?o.path+": ":"")+o.error]);for(let o of n.knocked||[])t.push(["warn","A Mac named “"+o.name+"” asked to connect without the pairing proof ("+o.short+") · not accepted. If that’s your Mac: Accept "+o.name+" on the board (Settings → Sync) or "+V+' sync accept "'+o.name+'"']);return e&&e.armed&&t.push(["info","Pairing code: live until "+new Date(e.expiresAt).toLocaleTimeString()]),t}async function C0({argv:n=[],api:e=null,env:t=process.env,out:s=process.stdout,input:o=process.stdin}={}){let r=E0(n),i=ee,a=u=>{r.json||s.write(u+`
321
+ `)},l=r.pos[0]||"status";if(r.help||l==="help")return s.write(lh),0;let c={ok:i.green("✓"),warn:i.yellow("!"),info:i.dim("·")};if(l==="status"){let u=await sn({env:t,root:Z()}),d=Yn(Ft(t));if(r.json)return s.write(JSON.stringify({...u,pairing:d})+`
322
+ `),0;for(let[f,h]of dh(u,d))a(c[f]+" "+h);return 0}if(l==="install"){let u=new kt({input:o,output:s,yes:r.yes}),d=await O0({env:t,say:a,onLine:f=>a(i.dim(" "+f)),confirm:async f=>(a(`Leftoff will run:
323
323
  `+i.cyan(f)+`
324
324
  `+i.dim(" (Syncthing from Homebrew, started as a login service so it runs in the background.)")),u.confirm("Install Syncthing?",!0))});return u.close&&u.close(),d.already?(a(c.ok+" Syncthing is already installed"+i.dim(" · next: "+V+" sync setup")),0):!d.ok&&d.code==="no_brew"?(a(c.warn+" "+d.error+`
325
325
  `),d.directions.forEach((f,h)=>a(" "+(h+1)+". "+f)),a(`
326
- Then run `+i.cyan(V+" sync install")+" again."),1):d.ok?(a(c.ok+" Syncthing is installed and running"+i.dim(" · next: "+V+" sync setup")),0):(a((d.code==="declined"?c.info:i.red("✗"))+" "+d.error),d.code==="declined"?0:1)}if(l==="start"){let u=await ih({env:t,say:a});return a(u.ok?c.ok+" Syncthing is running":i.red("✗")+" "+u.error+(u.code==="not_installed"?" · "+V+" sync install":"")),u.ok?0:1}if(l==="setup"){let u=r.path||r.pos[1]||null,d=h=>{try{So({kind:"setup",...h},t)}catch{}};d({state:"starting",message:"Checking Syncthing…"});let f;try{f=await vA({env:t,target:u,daemonPid:r.daemonPid,say:a,status:d})}catch(h){f={ok:!1,code:h.code||"error",error:h.message}}return f.ok?(d({state:"done",path:f.path,message:"The Leftoff folder is in Syncthing"}),a(c.ok+" The Leftoff folder is in Syncthing at "+Ne(f.path,t)+i.dim(" · next: "+V+" sync pair, to add your other Mac")),r.json&&s.write(JSON.stringify(f)+`
326
+ Then run `+i.cyan(V+" sync install")+" again."),1):d.ok?(a(c.ok+" Syncthing is installed and running"+i.dim(" · next: "+V+" sync setup")),0):(a((d.code==="declined"?c.info:i.red("✗"))+" "+d.error),d.code==="declined"?0:1)}if(l==="start"){let u=await uh({env:t,say:a});return a(u.ok?c.ok+" Syncthing is running":i.red("✗")+" "+u.error+(u.code==="not_installed"?" · "+V+" sync install":"")),u.ok?0:1}if(l==="setup"){let u=r.path||r.pos[1]||null,d=h=>{try{bo({kind:"setup",...h},t)}catch{}};d({state:"starting",message:"Checking Syncthing…"});let f;try{f=await CA({env:t,target:u,daemonPid:r.daemonPid,say:a,status:d})}catch(h){f={ok:!1,code:h.code||"error",error:h.message}}return f.ok?(d({state:"done",path:f.path,message:"The Leftoff folder is in Syncthing"}),a(c.ok+" The Leftoff folder is in Syncthing at "+Me(f.path,t)+i.dim(" · next: "+V+" sync pair, to add your other Mac")),r.json&&s.write(JSON.stringify(f)+`
327
327
  `),0):(d({state:"failed",error:f.error,code:f.code}),a(i.red("✗")+" "+f.error),r.json&&s.write(JSON.stringify(f)+`
328
- `),1)}if(l==="pair"){let u=ht({env:t});if(!u.ok)return a(i.red("✗")+" "+u.reason+" · "+V+" sync install"),1;let d;if(e)try{d=await e("POST","/v1/syncthing/pair",{})}catch(S){if(d=null,!/ECONNREFUSED|fetch failed/i.test(S.message))return a(i.red("✗")+" "+S.message),1}let f=!!d;if(!d){let S=await tn({env:t,root:Z()});if(!S.running)return a(i.red("✗")+" Syncthing isn’t running · "+V+" sync start"),1;if(!S.folder)return a(i.red("✗")+" The Leftoff folder isn’t in Syncthing yet · "+V+" sync setup"),1;let k=Dr({deviceId:S.myID,machine:de(Q()),env:t});d={code:k.code,expiresAt:new Date(k.expiresAt).toISOString()}}if(r.json)return s.write(JSON.stringify({code:d.code,expiresAt:d.expiresAt,command:io(d.code),fresh:io(d.code,{installed:!1})})+`
328
+ `),1)}if(l==="pair"){let u=mt({env:t});if(!u.ok)return a(i.red("✗")+" "+u.reason+" · "+V+" sync install"),1;let d;if(e)try{d=await e("POST","/v1/syncthing/pair",{})}catch(S){if(d=null,!/ECONNREFUSED|fetch failed/i.test(S.message))return a(i.red("✗")+" "+S.message),1}let f=!!d;if(!d){let S=await sn({env:t,root:Z()});if(!S.running)return a(i.red("✗")+" Syncthing isn’t running · "+V+" sync start"),1;if(!S.folder)return a(i.red("✗")+" The Leftoff folder isn’t in Syncthing yet · "+V+" sync setup"),1;let k=Rr({deviceId:S.myID,machine:de(Q()),env:t});d={code:k.code,expiresAt:new Date(k.expiresAt).toISOString()}}if(r.json)return s.write(JSON.stringify({code:d.code,expiresAt:d.expiresAt,command:lo(d.code),fresh:lo(d.code,{installed:!1})})+`
329
329
  `),0;a(`On your other Mac, run:
330
330
 
331
- `+i.cyan(io(d.code))+`
331
+ `+i.cyan(lo(d.code))+`
332
332
 
333
- `+i.dim(" Leftoff not installed there yet: ")+io(d.code,{installed:!1})+`
333
+ `+i.dim(" Leftoff not installed there yet: ")+lo(d.code,{installed:!1})+`
334
334
  `+i.dim(" Or on its board: Settings → Sync → Join another Mac, and paste the code.")+`
335
335
 
336
- `+i.dim(" Works once, for 15 minutes. Only the Mac that uses this code is accepted."));let h="",p=new Set,m=async()=>{f||await Rr({client:u.client,env:t,log:()=>{}}).catch(()=>{});let S=$n(Pt(t)),k=await tn({env:t,root:Z()}),w=S.paired?k.peers.find(v=>v.name===S.paired.name):null,x=S.expired?"The code expired; nothing was accepted. Run "+V+" sync pair for a new one.":S.paired?!w||!w.connected?"Paired with "+S.paired.name+" · connecting…":w.completion===100?"Done: "+S.paired.name+" is in sync.":"Paired with "+S.paired.name+" · syncing ("+(w.completion!=null?w.completion+"%":"…")+")":"Waiting for your other Mac…";x!==h&&(h=x,a(c.info+" "+x));for(let v of k.knocked||[])p.has(v.deviceId)||(p.add(v.deviceId),a(c.warn+" A Mac named “"+v.name+"” asked to connect without the pairing proof, so it wasn’t accepted. If that’s your Mac, run: "+V+' sync accept "'+v.name+'"'));return/^Done|expired/.test(x)},g=Date.now()+(r.timeoutMs||16*6e4);for(;Date.now()<g;){if(await m())return/^Done/.test(h)?0:1;await sh(Number(t.LEFTOFF_SYNCTHING_POLL_MS)||1500)}return 1}if(l==="accept"){let u=ht({env:t});if(!u.ok)return a(i.red("✗")+" "+u.reason),1;let d=await Pr(u.client,{env:t}).catch(()=>[]),f=r.pos.slice(1).join(" "),h=f?d.find(p=>p.name===f||Ce(p.deviceId,f)||p.short===f.toUpperCase()):d.length===1?d[0]:null;if(!h){if(!d.length)a(c.info+" No Mac asked to connect without the pairing proof. (Only ones that knocked while a code was live show up here.)");else{a("Which one?");for(let p of d)a(" "+V+' sync accept "'+p.name+'" ('+p.short+", "+rh(p.at)+")")}return 1}if(!r.yes){let p=new wt({input:o,output:s});if(!await p.confirm("Add “"+h.name+"” ("+h.short+") to Syncthing and share the Leftoff folder with it? Only do this if it’s your Mac.",!1).finally(()=>p.close&&p.close()))return a("Nothing changed."),0}try{await jr(u.client,{deviceId:h.deviceId,env:t})}catch(p){return a(i.red("✗")+" "+p.message),1}return a(c.ok+" Added "+h.name+" and shared the Leftoff folder with it. Its join finishes on its own once the files arrive."),0}if(l==="stop"){let u=ht({env:t});if(!u.ok)return a(i.red("✗")+" "+u.reason),1;let d=await u.client.folder(Ee).catch(()=>null);if(!d)return a(c.info+" The Leftoff folder isn’t in Syncthing; nothing to stop."),0;if(!r.yes){let h=new wt({input:o,output:s});if(!await h.confirm("Stop syncing "+Ne(d.path,t)+"? The files stay; other Macs stop getting updates.",!1).finally(()=>h.close&&h.close()))return a("Nothing changed."),0}let f=await Lr(u.client);return a(c.ok+" Stopped syncing. "+Ne(f.path,t)+" and its files are still here; Leftoff keeps using it."),0}return s.write(oh),1}async function A0({argv:n=[],env:e=process.env,out:t=process.stdout,input:s=process.stdin}={}){let o=b0(n),r=ee,i=f=>{o.json||t.write(f+`
337
- `)};if(o.help)return t.write(oh),0;let a=o.pos.join("")||e.LEFTOFF_JOIN_CODE||"";if(!a)return i("usage: "+V+" join <code> (the code from Settings → Sync → Pair another Mac, or `"+V+" sync pair`, on your first Mac)"),1;let l=f=>{try{So({kind:"join",...f},e)}catch{}},c=new wt({input:s,output:t,yes:o.yes}),u=async f=>(i(`Syncthing isn’t installed here. Leftoff will run:
338
- `+r.cyan(f)),c.confirm("Install Syncthing?",!0)),d;try{d=await xA(a,{env:e,target:o.path,daemonPid:o.daemonPid,yes:o.yes,input:s,out:t,say:f=>i(r.dim("· ")+f),status:l,confirmInstall:o.json?null:u,onLine:f=>i(r.dim(" "+f))})}catch(f){d={ok:!1,code:f.code||"error",error:f.message}}return c.close&&c.close(),d.ok?(l({state:"done",path:d.path,with:d.with,message:"Done"}),i(r.green("✓")+" Done. This Mac and "+(d.with||"the other Mac")+" share "+Ne(d.path,e)+" through Syncthing"+(d.merged?"; this Mac’s sessions were merged in.":".")),o.json&&t.write(JSON.stringify(d)+`
336
+ `+i.dim(" Works once, for 15 minutes. Only the Mac that uses this code is accepted."));let h="",p=new Set,m=async()=>{f||await Pr({client:u.client,env:t,log:()=>{}}).catch(()=>{});let S=Yn(Ft(t)),k=await sn({env:t,root:Z()}),w=S.paired?k.peers.find(v=>v.name===S.paired.name):null,x=S.expired?"The code expired; nothing was accepted. Run "+V+" sync pair for a new one.":S.paired?!w||!w.connected?"Paired with "+S.paired.name+" · connecting…":w.completion===100?"Done: "+S.paired.name+" is in sync.":"Paired with "+S.paired.name+" · syncing ("+(w.completion!=null?w.completion+"%":"…")+")":"Waiting for your other Mac…";x!==h&&(h=x,a(c.info+" "+x));for(let v of k.knocked||[])p.has(v.deviceId)||(p.add(v.deviceId),a(c.warn+" A Mac named “"+v.name+"” asked to connect without the pairing proof, so it wasn’t accepted. If that’s your Mac, run: "+V+' sync accept "'+v.name+'"'));return/^Done|expired/.test(x)},g=Date.now()+(r.timeoutMs||16*6e4);for(;Date.now()<g;){if(await m())return/^Done/.test(h)?0:1;await ah(Number(t.LEFTOFF_SYNCTHING_POLL_MS)||1500)}return 1}if(l==="accept"){let u=mt({env:t});if(!u.ok)return a(i.red("✗")+" "+u.reason),1;let d=await jr(u.client,{env:t}).catch(()=>[]),f=r.pos.slice(1).join(" "),h=f?d.find(p=>p.name===f||Ce(p.deviceId,f)||p.short===f.toUpperCase()):d.length===1?d[0]:null;if(!h){if(!d.length)a(c.info+" No Mac asked to connect without the pairing proof. (Only ones that knocked while a code was live show up here.)");else{a("Which one?");for(let p of d)a(" "+V+' sync accept "'+p.name+'" ('+p.short+", "+ch(p.at)+")")}return 1}if(!r.yes){let p=new kt({input:o,output:s});if(!await p.confirm("Add “"+h.name+"” ("+h.short+") to Syncthing and share the Leftoff folder with it? Only do this if it’s your Mac.",!1).finally(()=>p.close&&p.close()))return a("Nothing changed."),0}try{await Fr(u.client,{deviceId:h.deviceId,env:t})}catch(p){return a(i.red("✗")+" "+p.message),1}return a(c.ok+" Added "+h.name+" and shared the Leftoff folder with it. Its join finishes on its own once the files arrive."),0}if(l==="stop"){let u=mt({env:t});if(!u.ok)return a(i.red("✗")+" "+u.reason),1;let d=await u.client.folder(Ee).catch(()=>null);if(!d)return a(c.info+" The Leftoff folder isn’t in Syncthing; nothing to stop."),0;if(!r.yes){let h=new kt({input:o,output:s});if(!await h.confirm("Stop syncing "+Me(d.path,t)+"? The files stay; other Macs stop getting updates.",!1).finally(()=>h.close&&h.close()))return a("Nothing changed."),0}let f=await Dr(u.client);return a(c.ok+" Stopped syncing. "+Me(f.path,t)+" and its files are still here; Leftoff keeps using it."),0}return s.write(lh),1}async function N0({argv:n=[],env:e=process.env,out:t=process.stdout,input:s=process.stdin}={}){let o=E0(n),r=ee,i=f=>{o.json||t.write(f+`
337
+ `)};if(o.help)return t.write(lh),0;let a=o.pos.join("")||e.LEFTOFF_JOIN_CODE||"";if(!a)return i("usage: "+V+" join <code> (the code from Settings → Sync → Pair another Mac, or `"+V+" sync pair`, on your first Mac)"),1;let l=f=>{try{bo({kind:"join",...f},e)}catch{}},c=new kt({input:s,output:t,yes:o.yes}),u=async f=>(i(`Syncthing isn’t installed here. Leftoff will run:
338
+ `+r.cyan(f)),c.confirm("Install Syncthing?",!0)),d;try{d=await NA(a,{env:e,target:o.path,daemonPid:o.daemonPid,yes:o.yes,input:s,out:t,say:f=>i(r.dim("· ")+f),status:l,confirmInstall:o.json?null:u,onLine:f=>i(r.dim(" "+f))})}catch(f){d={ok:!1,code:f.code||"error",error:f.message}}return c.close&&c.close(),d.ok?(l({state:"done",path:d.path,with:d.with,message:"Done"}),i(r.green("✓")+" Done. This Mac and "+(d.with||"the other Mac")+" share "+Me(d.path,e)+" through Syncthing"+(d.merged?"; this Mac’s sessions were merged in.":".")),o.json&&t.write(JSON.stringify(d)+`
339
339
  `),0):(l({state:"failed",code:d.code,error:d.error}),i(r.red("✗")+" "+d.error),(d.code==="no_brew"||d.plan&&!d.plan.brew)&&(d.directions||d.plan.directions).forEach((f,h)=>i(" "+(h+1)+". "+f)),o.json&&t.write(JSON.stringify({ok:!1,code:d.code,error:d.error})+`
340
- `),1)}var sh,io,oh,lh=J(()=>{xe();Qs();Qs();ko();Pn();_t();sh=n=>new Promise(e=>setTimeout(e,n)),io=(n,{installed:e=!0}={})=>(e?V+" join ":"npx -y github:"+st+" join ")+n,oh=`${V} sync: Syncthing for Leftoff, without opening Syncthing
340
+ `),1)}var ah,lo,lh,fh=J(()=>{xe();to();to();vo();Fn();Ot();ah=n=>new Promise(e=>setTimeout(e,n)),lo=(n,{installed:e=!0}={})=>(e?V+" join ":"npx -y github:"+rt+" join ")+n,lh=`${V} sync: Syncthing for Leftoff, without opening Syncthing
341
341
 
342
342
  ${V} sync state: running, peers, last sync, folder errors
343
343
  ${V} sync install install Syncthing with Homebrew (shows the command, asks first)
@@ -349,39 +349,39 @@ Then run `+i.cyan(V+" sync install")+" again."),1):d.ok?(a(c.ok+" Syncthing is i
349
349
  ${V} join <code> on the other Mac: set up Syncthing, pair, sync, then join the folder
350
350
 
351
351
  The board does the same from Settings → Sync.
352
- `});import AA from"node:http";import ch from"node:fs";import ws from"node:path";import I0 from"node:crypto";import{fileURLToPath as TA}from"node:url";import{spawn as T0}from"node:child_process";function _A(n){return ut.state==="running"||(Object.assign(ut,{state:"running",lines:[],error:null,code:null,startedAt:new Date().toISOString(),finishedAt:null,stage:null}),n.log("syncthing: installing with Homebrew ("+Or+"), confirmed on the board"),(async()=>{let e=await Ir({onLine:s=>{ut.lines.push(String(s).slice(0,300)),ut.lines.length>200&&ut.lines.shift()},onStage:s=>{ut.stage=s}});if(!e.ok){Object.assign(ut,{state:"failed",error:e.error,code:e.code,finishedAt:new Date().toISOString()}),n.log("syncthing: install failed: "+e.error);return}if(ut.stage="Starting Syncthing…",!(await Es({timeoutMs:Number(process.env.LEFTOFF_SYNCTHING_START_MS)||9e4})).ok){Object.assign(ut,{state:"failed",error:"Syncthing installed, but it didn’t start. Try `brew services restart syncthing` in Terminal.",code:"not_running",finishedAt:new Date().toISOString()});return}Object.assign(ut,{state:"done",finishedAt:new Date().toISOString()}),n.log("syncthing: installed and running")})().catch(e=>Object.assign(ut,{state:"failed",error:e.message,finishedAt:new Date().toISOString()}))),ut}function EA(n,e){let t=n.headers["x-leftoff-relay"];if(!t)return!1;let s=e.relayHost,o=String(n.socket.remoteAddress||"");if(!s||!(o==="127.0.0.1"||o==="::1"||o==="::ffff:127.0.0.1"))return!1;let r=Buffer.from(String(t)),i=Buffer.from(s.innerSecret);return r.length===i.length&&I0.timingSafeEqual(r,i)}function OA(n){try{return decodeURIComponent(String(n||""))||null}catch{return null}}function E0(n){return n.method==="GET"&&/text\/html/.test(String(n.headers.accept||""))}function O0(n,e,t,s,o){let r='<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>'+hn(Ge)+": "+hn(s)+'</title><meta name="color-scheme" content="light dark"></head><body><main><h1>'+hn(s)+"</h1>"+o.map(i=>"<p>"+i+"</p>").join("")+"<p><small>"+hn(Ge)+" "+hn(X)+" · "+hn(t)+"</small></p></main></body></html>";n.writeHead(e,{"content-type":"text/html; charset=utf-8","cache-control":"no-store","x-content-type-options":"nosniff","content-security-policy":"default-src 'none'","x-leftoff-error":t}),n.end(r)}function D(n,e,t,s={}){let o=JSON.stringify(t);n.writeHead(e,{"content-type":"application/json; charset=utf-8","cache-control":"no-store","x-content-type-options":"nosniff",...s}),n.end(o)}function fn(n,e,t,s){D(n,e,{error:{code:t,message:s}})}function te(n){return new Promise((e,t)=>{let s=0,o=[];n.on("data",r=>{if(s+=r.length,s>CA){t(C(400,"too_large","request body is over 1 MB")),n.destroy();return}o.push(r)}),n.on("end",()=>{let r=Buffer.concat(o).toString("utf8");if(!r.trim())return e({});try{e(JSON.parse(r))}catch{t(C(400,"bad_json","body is not valid JSON"))}}),n.on("error",t)})}function MA(n,e){let t=Buffer.from(String(n||"")),s=Buffer.from(String(e||""));return t.length===s.length&&I0.timingSafeEqual(t,s)}function Qc(n,e){let t=String(n.headers.cookie||"").match(new RegExp("(?:^|;\\s*)"+e+"=([^;]+)"));return t?decodeURIComponent(t[1]):null}function tt(n){return LA.some(e=>n.headers[e]!==void 0)}function ea({burst:n=3,everyMs:e=2e3,now:t=()=>Date.now()}={}){let s=n,o=t();return{take(){let r=t();return s=Math.min(n,s+(r-o)/e),o=r,s<1?!1:(s-=1,!0)}}}function N0(n,{root:e,port:t,host:s="127.0.0.1",goLimit:o,answerLimit:r}={}){e=e||n.root;let i=ve(e),a=new Set,l=ea(o||{}),c=ea({burst:2,everyMs:15e3}),u=ea({burst:2,everyMs:1e4}),d=ea({burst:6,everyMs:5e3}),f=new Map,h={take:b=>(f.has(b)||(f.size>500&&f.clear(),f.set(b,ea({burst:5,everyMs:12e3}))),f.get(b).take())},p=b=>String(b.headers["x-forwarded-for"]||b.socket.remoteAddress||"").split(",")[0].trim(),m=Gg(r||(process.env.CHECKPOINTS_ANSWER_LIMIT?{max:Number(process.env.CHECKPOINTS_ANSWER_LIMIT)}:{})),g=[],S=0,k=new Set,w=(b,y)=>{let _="event: "+b+`
352
+ `});import IA from"node:http";import hh from"node:fs";import bs from"node:path";import P0 from"node:crypto";import{fileURLToPath as MA}from"node:url";import{spawn as I0}from"node:child_process";function LA(n){return ft.state==="running"||(Object.assign(ft,{state:"running",lines:[],error:null,code:null,startedAt:new Date().toISOString(),finishedAt:null,stage:null}),n.log("syncthing: installing with Homebrew ("+Cr+"), confirmed on the board"),(async()=>{let e=await Ir({onLine:s=>{ft.lines.push(String(s).slice(0,300)),ft.lines.length>200&&ft.lines.shift()},onStage:s=>{ft.stage=s}});if(!e.ok){Object.assign(ft,{state:"failed",error:e.error,code:e.code,finishedAt:new Date().toISOString()}),n.log("syncthing: install failed: "+e.error);return}if(ft.stage="Starting Syncthing…",!(await Ns({timeoutMs:Number(process.env.LEFTOFF_SYNCTHING_START_MS)||9e4})).ok){Object.assign(ft,{state:"failed",error:"Syncthing installed, but it didn’t start. Try `brew services restart syncthing` in Terminal.",code:"not_running",finishedAt:new Date().toISOString()});return}Object.assign(ft,{state:"done",finishedAt:new Date().toISOString()}),n.log("syncthing: installed and running")})().catch(e=>Object.assign(ft,{state:"failed",error:e.message,finishedAt:new Date().toISOString()}))),ft}function DA(n,e){let t=n.headers["x-leftoff-relay"];if(!t)return!1;let s=e.relayHost,o=String(n.socket.remoteAddress||"");if(!s||!(o==="127.0.0.1"||o==="::1"||o==="::ffff:127.0.0.1"))return!1;let r=Buffer.from(String(t)),i=Buffer.from(s.innerSecret);return r.length===i.length&&P0.timingSafeEqual(r,i)}function RA(n){try{return decodeURIComponent(String(n||""))||null}catch{return null}}function L0(n){return n.method==="GET"&&/text\/html/.test(String(n.headers.accept||""))}function D0(n,e,t,s,o){let r='<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>'+mn(Ke)+": "+mn(s)+'</title><meta name="color-scheme" content="light dark"></head><body><main><h1>'+mn(s)+"</h1>"+o.map(i=>"<p>"+i+"</p>").join("")+"<p><small>"+mn(Ke)+" "+mn(X)+" · "+mn(t)+"</small></p></main></body></html>";n.writeHead(e,{"content-type":"text/html; charset=utf-8","cache-control":"no-store","x-content-type-options":"nosniff","content-security-policy":"default-src 'none'","x-leftoff-error":t}),n.end(r)}function D(n,e,t,s={}){let o=JSON.stringify(t);n.writeHead(e,{"content-type":"application/json; charset=utf-8","cache-control":"no-store","x-content-type-options":"nosniff",...s}),n.end(o)}function pn(n,e,t,s){D(n,e,{error:{code:t,message:s}})}function te(n){return new Promise((e,t)=>{let s=0,o=[];n.on("data",r=>{if(s+=r.length,s>PA){t(C(400,"too_large","request body is over 1 MB")),n.destroy();return}o.push(r)}),n.on("end",()=>{let r=Buffer.concat(o).toString("utf8");if(!r.trim())return e({});try{e(JSON.parse(r))}catch{t(C(400,"bad_json","body is not valid JSON"))}}),n.on("error",t)})}function UA(n,e){let t=Buffer.from(String(n||"")),s=Buffer.from(String(e||""));return t.length===s.length&&P0.timingSafeEqual(t,s)}function tu(n,e){let t=String(n.headers.cookie||"").match(new RegExp("(?:^|;\\s*)"+e+"=([^;]+)"));return t?decodeURIComponent(t[1]):null}function st(n){return HA.some(e=>n.headers[e]!==void 0)}function sa({burst:n=3,everyMs:e=2e3,now:t=()=>Date.now()}={}){let s=n,o=t();return{take(){let r=t();return s=Math.min(n,s+(r-o)/e),o=r,s<1?!1:(s-=1,!0)}}}function j0(n,{root:e,port:t,host:s="127.0.0.1",goLimit:o,answerLimit:r}={}){e=e||n.root;let i=ve(e),a=new Set,l=sa(o||{}),c=sa({burst:2,everyMs:15e3}),u=sa({burst:2,everyMs:1e4}),d=sa({burst:6,everyMs:5e3}),f=new Map,h={take:b=>(f.has(b)||(f.size>500&&f.clear(),f.set(b,sa({burst:5,everyMs:12e3}))),f.get(b).take())},p=b=>String(b.headers["x-forwarded-for"]||b.socket.remoteAddress||"").split(",")[0].trim(),m=Wg(r||(process.env.CHECKPOINTS_ANSWER_LIMIT?{max:Number(process.env.CHECKPOINTS_ANSWER_LIMIT)}:{})),g=[],S=0,k=new Set,w=(b,y)=>{let _="event: "+b+`
353
353
  data: `+JSON.stringify(y)+`
354
354
 
355
355
  `;for(let A of a)A.write(_);g.push({seq:++S,event:b,data:y}),g.length>200&&g.shift();for(let A of k)A()};n.on("session.updated",b=>w("session.updated",b)),n.on("prs.updated",b=>w("prs.updated",b)),n.on("checkpoint.created",b=>w("checkpoint.created",b)),n.on("digest.created",b=>w("digest.created",b)),n.on("spaces.updated",b=>w("spaces.updated",b)),n.on("catchup.created",b=>w("catchup.created",b)),n.on("lists.updated",b=>w("lists.updated",b)),n.on("model.pull",b=>w("model.pull",b)),n.on("needs.updated",b=>w("needs.updated",b)),n.on("restore.updated",b=>w("restore.updated",b)),n.on("peers.updated",b=>w("peers.updated",b)),n.on("agents.updated",b=>w("agents.updated",b)),n.on("relay.updated",b=>w("relay.updated",b));let x=setInterval(()=>{for(let b of a)b.write(`: ping
356
356
 
357
- `)},25e3);x.unref();let v=AA.createServer(async(b,y)=>{try{let _=n.cfg&&n.cfg.remoteHosts||[],A=Si(b.headers.host,t,_),O=EA(b,n);O&&(A="remote");let I=b.headers["x-forwarded-host"]?String(b.headers["x-forwarded-host"]).split(",")[0].trim():"";if(A==="loopback"&&I&&Si(I,t,_)==="remote"&&(A="remote"),!A)return E0(b)?O0(y,403,"bad_host","This address isn’t on "+Ge+"’s list",[hn(Ge)+" only answers to its own names, so other sites can’t reach it through your browser. <code>"+hn(String(b.headers.host||"(none)"))+"</code> isn’t one of them.","On the machine running "+hn(Ge)+", run <code>"+V+" tailscale on</code>: it adds this machine’s tailnet name to the list and prints a QR code that signs your phone in."]):fn(y,403,"bad_host","Host must be 127.0.0.1, localhost or leftoff.localhost, port "+t+", or a name in config remoteHosts (leftoff tailscale on)");if(b.headers["tailscale-funnel-request"])return E0(b)?O0(y,403,"funnel_refused","Not on the public internet",[hn(Ge)+" is not served over Tailscale Funnel. Use your tailnet: <code>"+V+" tailscale on</code> (tailscale serve)."]):fn(y,403,"funnel_refused","Leftoff is not served over Tailscale Funnel; use tailscale serve (tailnet only)");let N=b.headers.origin;if(!py(N,t,_))return fn(y,403,"bad_origin","cross-origin requests are refused");if(A==="loopback"&&tt(b))return fn(y,403,"bad_host","a proxied request must use a name in config remoteHosts, not a loopback Host");let M=A==="remote",z=M&&!O&&n.cfg.remoteRequireToken!==!1,U=new URL(b.url,"http://"+b.headers.host),E=U.pathname;if(b.method==="GET"&&!E.startsWith("/v1/")&&!O){let T=E==="/"||E==="/index.html"?"index.html":E.slice(1),L=ws.join(_0,ws.normalize(T).replace(/^(\.\.[/\\])+/,""));if(!L.startsWith(_0)||!ch.existsSync(L)||ch.statSync(L).isDirectory())return fn(y,404,"not_found","no such page");let j={"content-type":IA[ws.extname(L)]||"application/octet-stream","cache-control":"no-cache","x-content-type-options":"nosniff","x-frame-options":"DENY","content-security-policy":NA};if(j["referrer-policy"]="no-referrer",T==="index.html")if(!z)j["set-cookie"]="cp_token="+encodeURIComponent(i)+"; HttpOnly; SameSite=Strict; Path=/"+(M?"; Secure":"");else if(U.searchParams.has("t")){let $=b.headers["tailscale-user-login"]||null,ge=yy(e,U.searchParams.get("t"),{userAgent:b.headers["user-agent"],who:$});return ge?(n.log("remote login: new browser session "+ge.id+" (link)"+($?" ("+$+")":"")+" via "+b.headers.host),y.writeHead(302,{location:"/","cache-control":"no-store","referrer-policy":"no-referrer","set-cookie":zl(ge.secret)}),y.end()):(y.writeHead(302,{location:"/?login=expired","cache-control":"no-store","referrer-policy":"no-referrer"}),y.end())}else{let $=Qc(b,bi);$&&Yl(e,$,{touch:!0})&&(j["set-cookie"]=zl($))}return T==="sw.js"&&(j["service-worker-allowed"]="/"),y.writeHead(200,j),ch.createReadStream(L).pipe(y)}if(b.method==="GET"&&E==="/v1/remote/ping"){let T=!z||M&&!!Yl(e,Qc(b,bi));return D(y,200,{ok:!0,app:Ge,version:X,via:A,proxied:tt(b),loginRequired:z,signedIn:T})}if(b.method==="POST"&&E==="/v1/remote/signin"){if(!M)throw C(400,"not_remote","sign-in codes are for other devices; this machine needs none");if(!z)return D(y,200,{ok:!0,needed:!1});if(!/application\/json/.test(String(b.headers["content-type"]||"")))throw C(415,"json_only","send JSON");if(!h.take(p(b)))throw C(429,"slow_down","Too many tries. Wait a minute, then try again.");let T=await te(b),L=b.headers["tailscale-user-login"]||null,j=wy(e,T.code,{userAgent:b.headers["user-agent"],who:L});if(!j.ok)throw n.log("remote login: wrong sign-in code via "+b.headers.host+(j.reason==="locked"?" (too many: pending codes voided)":"")),j.reason==="locked"?C(429,"locked","Too many wrong codes, so every pending code was cancelled. Get a new one: "+V+" link, on the machine running "+Ge+"."):C(401,"bad_code","That code isn’t right, or it was already used or has expired. Codes work once, for "+qs/6e4+" minutes.");return n.log("remote login: new browser session "+j.id+" (code)"+(L?" ("+L+")":"")+" via "+b.headers.host),D(y,200,{ok:!0},{"set-cookie":zl(j.secret)})}let P=String(b.headers.authorization||""),W=P.startsWith("Bearer ")?P.slice(7).trim():Qc(b,"cp_token");if(!(O||i&&MA(W,i)||M&&!!Yl(e,Qc(b,bi))))return z?fn(y,401,"login_required","this browser isn't signed in: on the machine running Leftoff, run `leftoff link` and scan the QR code, or type its sign-in code here"):fn(y,401,"unauthorized","missing or wrong token (see ~/checkpoints/.secret/token)");let K=()=>{if(M||tt(b))throw C(403,"loopback_only","only from the machine running Leftoff")},pe=!M&&!tt(b)?!0:M&&n.answeringInfo("remote").remoteAllowed,Ie=T=>{if(!pe)throw M||K(),C(403,"trust_off",T+" from another device is off. On a Mac, turn on Settings → Privacy & security → Trust my paired devices.")},Se=()=>{!M&&tt(b)&&K()},q=Object.fromEntries(U.searchParams),re=T=>E.match(T),B,Je=O?OA(b.headers["x-leftoff-device-name"]):null,Y=()=>({via:M?"remote":"loopback",proxied:tt(b),ip:O?null:b.socket.remoteAddress||null,host:O?"relay":b.headers.host||null,who:O?(Je||"a paired phone")+" (through the relay)":b.headers["tailscale-user-login"]||null,userAgent:b.headers["user-agent"]||null,relay:O||void 0}),ne=()=>{if(!m.take(O?"relay:"+b.headers["x-leftoff-device"]:M?"remote:"+(b.headers["x-forwarded-for"]||b.socket.remoteAddress):"local"))throw C(429,"rate_limited","Too many answers in a minute; wait a moment.")},se=()=>{let T=new AbortController;return y.on("close",()=>{y.writableEnded||T.abort()}),T.signal};if(b.method==="GET"&&E==="/v1/needs")return D(y,200,{items:n.needsList(),recent:n.recentAnswered(Math.min(50,Number(q.recent)||20)),answering:n.answeringInfo(M?"remote":"loopback"),dismissed:n.dismissedNeeds(),todos:n.todosOverview()});if(b.method==="GET"&&E==="/v1/todos")return D(y,200,n.todosOverview());if(b.method==="POST"&&E==="/v1/todos/done"){let T=await te(b);return D(y,200,n.todoDone(String(T.key||"")))}if(b.method==="POST"&&E==="/v1/todos/restore"){let T=await te(b),L=T.all===!0?n.todosOverview().done.map(j=>j.key):(Array.isArray(T.keys)?T.keys:[T.key]).filter(j=>typeof j=="string"&&/^[0-9a-f]{8,32}$/.test(j)).slice(0,500);return D(y,200,n.todoRestore(L))}if(b.method==="POST"&&E==="/v1/todos"){let T=await te(b);return D(y,200,n.todoAdd(T.text,{sessionId:T.sessionId||null}))}if(b.method==="POST"&&E==="/v1/needs/dismiss"){let T=await te(b);if(typeof T.key!="string"||!/^[0-9a-f]{8,64}$/.test(T.key))throw C(400,"bad_request","key is required");return D(y,200,n.dismissNeed(T.key))}if(b.method==="POST"&&E==="/v1/needs/restore"){let T=await te(b),L=T.all===!0?n.dismissedNeeds().map(j=>j.key):(Array.isArray(T.keys)?T.keys:[T.key]).filter(j=>typeof j=="string"&&/^[0-9a-f]{8,64}$/.test(j)).slice(0,500);if(!L.length&&T.all!==!0)throw C(400,"bad_request","key or keys is required");return D(y,200,n.restoreNeeds(L))}if(b.method==="POST"&&E==="/v1/asks")return K(),D(y,200,n.openAsk(await te(b)));if(b.method==="POST"&&E==="/v1/asks/settle"){K();let T=await te(b);if(!T.sessionId||!T.tool)throw C(400,"bad_settle","sessionId and tool are required");let L=n.asks.settleSession(T.sessionId,String(T.reason||"answered_in_terminal"),T.all?{}:{key:hi(T.tool,T.input)});return D(y,200,{ok:!0,settled:L.length})}if(b.method==="GET"&&(B=re(/^\/v1\/asks\/([\w-]+)\/wait$/))){K();let T=await n.asks.wait(B[1],Number(q.ms)||2e4,se());return T===null?void 0:D(y,200,T)}if(b.method==="POST"&&(B=re(/^\/v1\/asks\/([\w-]+)\/cancel$/))){K();let T=n.asks.settle(B[1],String((await te(b)).reason||"cancelled_in_terminal"));return D(y,200,T?{ok:!0,state:T.state}:{ok:!1,state:"gone"})}if(b.method==="GET"&&(B=re(/^\/v1\/asks\/([\w-]+)$/))){let T=n.asks.get(B[1]);if(!T)throw C(404,"not_found","no such request");return D(y,200,n.asks.publicAsk(T))}if(b.method==="POST"&&(B=re(/^\/v1\/asks\/([\w-]+)\/answer$/)))return n.assertMayAnswer(Y()),ne(),D(y,200,await n.answerAsk(B[1],await te(b),Y()));if(b.method==="POST"&&(B=re(/^\/v1\/sessions\/([^/]+)\/reply$/)))return n.assertMayAnswer(Y()),ne(),D(y,200,await n.postReply(decodeURIComponent(B[1]),(await te(b)).text,Y()));if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)\/replies\/next$/))){K();let T=se(),L=await n.takeReply(decodeURIComponent(B[1]),Number(q.ms)||2e4,T);return T.aborted?void 0:D(y,200,L)}if(b.method==="GET"&&E==="/v1/sync"){let T=or({current:e});return D(y,200,{...T,machine:n.machine,backups:ir(e),lastMove:Zs(),join:ar(e,{cli:V}),viewer:{local:!M&&!tt(b)}})}if(b.method==="POST"&&E==="/v1/sync/plan"){K();let T=await te(b);if(!T.to)throw C(400,"bad_request","to: the folder to move to");let L=rr(e,ws.resolve(C0(String(T.to))),{machine:n.machine,identity:n.peers?n.peers.id:null});T.force&&(L.collisions=L.collisions.filter(ge=>ge.code!=="machine_name_maybe_taken"),L.ok=!L.errors.length&&!L.collisions.length);let{files:j,...$}=L;return D(y,200,{...$,toDisplay:we(L.to),backupDisplay:we(L.backup),sample:j.filter(ge=>ge.action!=="same").slice(0,12),join:ar(L.to,{cli:V})})}if(b.method==="POST"&&E==="/v1/sync/move"){K();let T=await te(b);if(!T.to)throw C(400,"bad_request","to: the folder to move to");let L=ws.resolve(C0(String(T.to))),j=rr(e,L,{machine:n.machine,identity:n.peers?n.peers.id:null}),$=j.collisions.filter(pn=>!(T.force&&pn.code==="machine_name_maybe_taken"));if(j.errors.length||$.length)throw C(409,(j.errors[0]||$[0]).code,(j.errors[0]||$[0]).message);let ge=[Ke,"move",L,"--yes","--json","--daemon-pid",String(process.pid)];T.force&&ge.push("--force");let kt=T0(process.execPath,ge,{detached:!0,stdio:"ignore",env:{...process.env}});return kt.unref(),n.log("sync: moving the folder to "+L+" (leftoff move, pid "+kt.pid+")"),D(y,202,{started:!0,to:L,statusFile:Pi()})}if(b.method==="POST"&&E==="/v1/sync/backup/delete"){K();let T=await te(b);if(T.confirm!==!0)throw C(400,"confirm","send { path, confirm: true } to delete a backup");let L=ir(e).find(j=>j.path===T.path);if(!L)throw C(404,"not_found","no such Leftoff backup next to this folder");return D(y,200,ji(L.path))}if(b.method==="GET"&&E==="/v1/syncthing"){let T=!M&&!tt(b),L=await tn({root:e}),j=Mu(),$={...L,myID:void 0,pairing:$n(Pt()),run:j&&Date.now()-new Date(j.at).getTime()<6*36e5?j:null,viewer:{local:T}};return T?$.install={..._s(),job:{...ut,lines:ut.lines.slice(-60)}}:($.folder&&($.folder={...$.folder,path:void 0}),$.knocked=($.knocked||[]).map(ge=>({...ge,deviceId:void 0}))),D(y,200,$)}if(b.method==="POST"&&E==="/v1/syncthing/install"){if(K(),(await te(b)).confirm!==!0)throw C(400,"confirm","send { confirm: true } after showing the command: "+Or);if(kn().installed)return D(y,200,{already:!0});if(!_s().brew)throw C(409,"no_brew","Homebrew isn’t installed on this Mac");return D(y,202,{started:!0,job:_A(n)})}if(b.method==="POST"&&E==="/v1/syncthing/start"){K();let T=Nr();if(!T.ok)throw C(409,T.code||"start_failed",T.error);let L=await Es({timeoutMs:2e4});if(!L.ok)throw C(504,"not_running",L.error);return n.log("syncthing: started ("+T.how+")"),D(y,200,{ok:!0})}if(b.method==="POST"&&(E==="/v1/syncthing/setup"||E==="/v1/syncthing/join")){K();let T=await te(b),L=E.endsWith("/join");if(L&&!T.code)throw C(400,"bad_request","code: the pairing code from your other Mac");if(L)try{(await Promise.resolve().then(()=>(ko(),Pp))).decodePairCode(String(T.code))}catch(ge){throw C(400,ge.code||"bad_code",ge.message)}let j=[Ke,L?"join":"sync",...L?[]:["setup"],"--yes","--json","--daemon-pid",String(process.pid),...T.path?["--path",String(T.path)]:[]];So({kind:L?"join":"setup",state:"starting",message:"Starting…"});let $=T0(process.execPath,j,{detached:!0,stdio:"ignore",env:{...process.env,...L?{LEFTOFF_JOIN_CODE:String(T.code)}:{}}});return $.unref(),n.log("syncthing: "+(L?"joining another Mac by code":"putting the folder in Syncthing")+" (pid "+$.pid+")"),D(y,202,{started:!0})}if(b.method==="POST"&&E==="/v1/syncthing/pair"){K();let T=await tn({root:e});if(!T.running)throw C(409,"not_running","Syncthing isn’t running");if(!T.folder)throw C(409,"no_folder","The Leftoff folder isn’t in Syncthing yet");let L=await te(b),j=Dr({deviceId:T.myID,machine:n.machine,reuse:L.fresh!==!0});return n.log("syncthing: pairing code made; accepting the matching Mac until "+new Date(j.expiresAt).toISOString()),D(y,200,{code:j.code,command:io(j.code),fresh:io(j.code,{installed:!1}),expiresAt:new Date(j.expiresAt).toISOString()},{"cache-control":"no-store"})}if(b.method==="POST"&&E==="/v1/syncthing/pair/cancel")return K(),ju(),n.log("syncthing: pairing code cancelled"),D(y,200,{ok:!0});if(b.method==="POST"&&E==="/v1/syncthing/accept"){K();let T=await te(b);if(T.confirm!==!0||!T.deviceId)throw C(400,"confirm","send { deviceId, confirm: true } after the person clicked Accept");let L=ht();if(!L.ok)throw C(409,L.code||"no_syncthing",L.reason);let j;try{j=await jr(L.client,{deviceId:String(T.deviceId),log:$=>n.log($)})}catch($){throw C($.code==="not_knocked"?404:409,$.code||"accept_failed",$.message)}return D(y,200,j)}if(b.method==="POST"&&E==="/v1/syncthing/stop"){if(K(),(await te(b)).confirm!==!0)throw C(400,"confirm","send { confirm: true } to stop syncing");let L=ht();if(!L.ok)throw C(409,L.code||"no_syncthing",L.reason);let j=await Lr(L.client);return n.log("syncthing: stopped syncing the Leftoff folder (files kept)"),D(y,200,j)}if(b.method==="GET"&&E==="/v1/agents"){let T=!M&&!tt(b);return D(y,200,{...n.agentsOverview({local:T}),viewer:{local:T}})}if(b.method==="POST"&&E==="/v1/agents")return K(),D(y,200,await n.addAgent(await te(b)));if(b.method==="POST"&&(B=re(/^\/v1\/agents\/([a-z0-9-]+)\/refresh$/))){if(!d.take())throw C(429,"slow_down","too many refreshes; wait a moment");return D(y,200,await n.pullAgent(B[1],{force:!0}))}if(b.method==="DELETE"&&(B=re(/^\/v1\/agents\/([a-z0-9-]+)$/)))return K(),D(y,200,n.removeAgent(B[1],{purge:q.purge==="1"}));if(b.method==="GET"&&E==="/v1/peers")return D(y,200,{...n.peersOverview(),viewer:{local:!M&&!tt(b)}});if(b.method==="POST"&&E==="/v1/peers/allow"){if(K(),!n.peers)throw C(409,"peers_off","cross-machine actions are off on this machine (LEFTOFF_PEERS=0 or peers.enabled false)");return D(y,200,await n.peers.setAllowIncoming((await te(b)).allow===!0))}if(b.method==="POST"&&(B=re(/^\/v1\/peers\/([\w.-]+)\/trust$/))){if(K(),!n.peers)throw C(409,"peers_off","cross-machine actions are off on this machine");return D(y,200,n.peers.trust(decodeURIComponent(B[1])))}if(b.method==="POST"&&E==="/v1/peers/check")return n.peers&&await n.peers.poll(),D(y,200,n.peersOverview());if(b.method==="GET"&&E==="/v1/health")return D(y,200,{...n.health(),answering:n.answeringInfo(M?"remote":"loopback")});if(b.method==="POST"&&E==="/v1/mod/health")return D(y,200,n.recordModHealth(await te(b)));if(b.method==="GET"&&E==="/v1/changelog")return D(y,200,n.changelog());if(b.method==="POST"&&E==="/v1/changelog/seen")return D(y,200,n.markChangelogSeen((await te(b)).version));if(b.method==="POST"&&E==="/v1/changelog/dismiss")return D(y,200,n.dismissUpgradeNotice());if(b.method==="GET"&&E==="/v1/updates")return D(y,200,n.updateState());if(b.method==="POST"&&E==="/v1/updates/check")return D(y,200,await n.runUpdateCheck({force:!0}));if(b.method==="POST"&&E==="/v1/events"){let T=await te(b);if(!Array.isArray(T.events))throw C(400,"bad_request","body must be { events: Event[] }");return D(y,200,n.ingest(T.events))}if(b.method==="POST"&&(B=re(/^\/v1\/hooks\/([A-Za-z]+)$/))){let T=await te(b);return D(y,200,n.ingestHook(B[1],T))}if(b.method==="POST"&&(B=re(/^\/v1\/sessions\/([^/]+)\/checkpoints$/))){let T=decodeURIComponent(B[1]),L=await te(b);if(L.generate){let j=await n.generateCheckpoint(T,L.trigger||"manual",{userNote:L.userNote,length:L.length||void 0});return D(y,201,j)}return D(y,201,n.addCheckpoint(T,L))}if(b.method==="POST"&&E==="/v1/checkpoints/preview"){let T=await te(b);if(!T.sessionId)throw C(400,"bad_request","sessionId is required");return D(y,200,await n.generateCheckpoint(String(T.sessionId),"manual",{length:T.length||void 0,save:!1}))}if(b.method==="GET"&&E==="/v1/sessions")return D(y,200,{sessions:n.listSessions({...q,includeArchived:q.archived==="1"}),closedCount:n.closedCount({machine:q.machine}),todoCount:n.todosOverview().count,viewer:{local:!M,ghostty:n.platform==="darwin",mayAnswer:n.answeringInfo(M?"remote":"loopback").allowedHere}});if(b.method==="GET"&&E==="/v1/sessions/closed")return D(y,200,{...n.closedSessions({q:q.q,machine:q.machine,repo:q.repo,space:q.space,docs:q.docs==="1",limit:q.limit,offset:q.offset}),viewer:{local:!M&&!tt(b),ghostty:n.platform==="darwin"}});if(b.method==="GET"&&E==="/v1/gh"){let T=await Ga({fresh:q.fresh==="1"});return T.status==="ready"&&n.prs&&(n.prs.offUntil=0),D(y,200,T)}if(b.method==="GET"&&E==="/v1/prs")return D(y,200,await n.prsOverview({view:q.view,fresh:q.fresh==="1"}));if(b.method==="POST"&&(B=re(/^\/v1\/sessions\/([^/]+)\/prs$/)))return D(y,200,await n.linkPr(decodeURIComponent(B[1]),await te(b)));if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)\/turns$/)))return D(y,200,await n.sessionTurns(decodeURIComponent(B[1]),{limit:q.limit}));if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)\/file$/)))return D(y,200,await n.sessionFile(decodeURIComponent(B[1]),{path:q.path?String(q.path):null,plan:q.plan?String(q.plan):null,maxBytes:M||tt(b)?Qa:Qr}));if(b.method==="GET"&&E==="/v1/restore")return D(y,200,{...n.restoreCandidates({since:q.since,repo:q.repo}),viewer:{local:!M&&!tt(b)}});if(b.method==="POST"&&E==="/v1/restore"){K();let T=await te(b);if(!Array.isArray(T.ids))throw C(400,"bad_request","ids: the session ids to restore");if(T.print!==!0&&!u.take())throw C(429,"slow_down","too many restore requests; wait a moment");return D(y,200,await n.restore(T.ids,{print:T.print===!0}))}if(b.method==="POST"&&E==="/v1/restore/launched"){K();let T=await te(b),L=n.restoreTargets(Array.isArray(T.ids)?T.ids:[]).map(j=>j.id);return n.markRestored(L),D(y,200,{ok:!0,marked:L.length})}if(b.method==="POST"&&(B=re(/^\/v1\/sessions\/([^/]+)\/resume$/))){if(K(),!u.take())throw C(429,"slow_down","too many resume requests; wait a moment");return D(y,200,await n.resume(decodeURIComponent(B[1])))}if(b.method==="POST"&&(B=re(/^\/v1\/sessions\/([^/]+)\/go$/))){if(K(),!l.take())throw C(429,"slow_down","too many Go requests; wait a moment");return D(y,200,await n.jump(decodeURIComponent(B[1])))}if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)\/where$/)))return K(),D(y,200,n.where(decodeURIComponent(B[1])));if(b.method==="GET"&&E==="/v1/jump/speed")return K(),D(y,200,await n.jumpSpeed());if(b.method==="GET"&&E==="/v1/jump/check")return K(),D(y,200,await n.jumpCheck(q.kind||"ghostty"));if(b.method==="GET"&&E==="/v1/lists")return D(y,200,{...n.listsOverview({all:q.all==="1",repo:q.repo||null}),mayEdit:n.answeringInfo(M?"remote":"loopback").allowedHere});if(b.method==="GET"&&E==="/v1/lists/resolve")return D(y,200,{list:n.getList(q.ref)});if(b.method==="POST"&&E==="/v1/lists")return D(y,200,n.saveList(await te(b),Y()));if(b.method==="GET"&&(B=re(/^\/v1\/lists\/(~?[\w.-]+\/[\w.-]+)$/)))return D(y,200,{list:n.getList(B[1]),mayEdit:n.answeringInfo(M?"remote":"loopback").allowedHere});if(b.method==="POST"&&(B=re(/^\/v1\/lists\/(~?[\w.-]+\/[\w.-]+)\/change$/)))return D(y,200,n.changeList(B[1],await te(b),Y()));if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)\/lists$/)))return D(y,200,n.sessionLists(decodeURIComponent(B[1])));if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)\/lists\/find$/)))return D(y,200,n.findListsInSession(decodeURIComponent(B[1])));if(b.method==="POST"&&(B=re(/^\/v1\/sessions\/([^/]+)\/list-hint\/dismiss$/)))return n.assertMayEditLists(Y()),D(y,200,n.dismissListHint(decodeURIComponent(B[1])));if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)$/)))return D(y,200,{...n.getSession(decodeURIComponent(B[1])),viewer:{local:!M&&!tt(b),ghostty:n.platform==="darwin"}});if(b.method==="PATCH"&&(B=re(/^\/v1\/sessions\/([^/]+)$/)))return D(y,200,n.patchSession(decodeURIComponent(B[1]),await te(b)));if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)\/checkpoints$/)))return D(y,200,{checkpoints:n.sessionCheckpoints(decodeURIComponent(B[1]),q)});if(b.method==="GET"&&E==="/v1/checkpoints")return D(y,200,{checkpoints:n.timeline(q)});if(b.method==="GET"&&E==="/v1/search")return D(y,200,n.search(q.q));if(b.method==="GET"&&E==="/v1/catchups")return D(y,200,{catchups:n.folder.listCatchups(),lastAt:(()=>{let T=n.folder.lastCatchupAt();return T==null?null:new Date(T).toISOString()})()});if(b.method==="GET"&&E==="/v1/catchups/options")return D(y,200,n.catchupOptions({since:q.since,to:q.to}));if(b.method==="POST"&&E==="/v1/catchups/prepare"){let T=await te(b),L=await n.makeCatchup({...T,save:!1,useModel:!1}),{catchupLength:j,CATCHUP_SYSTEM:$,summaryPrompt:ge}=await Promise.resolve().then(()=>(pl(),hg)),kt=j(n.cfg,L.recap.length);return D(y,200,{window:L.recap.window,sessions:L.recap.sessions.length,needsYou:L.recap.needsYou.length,prompt:L.recap.sessions.length?{system:$.replace("{n}",String(kt.bullets)),prompt:ge(L.recap,{maxChars:n.cfg.summarizer&&n.cfg.summarizer.maxInputChars||24e3}),maxTokens:kt.maxTokens}:null})}if(b.method==="POST"&&E==="/v1/catchups"){let T=await te(b);return D(y,200,await n.makeCatchup(T))}if(b.method==="GET"&&(B=re(/^\/v1\/catchups\/(\d{4}-\d{2}-\d{2}T\d{4}(?:-\d+)?)$/))){let T=n.readCatchup(B[1]);if(!T)throw C(404,"not_found","no catch-up named "+B[1]);return D(y,200,T)}if(b.method==="GET"&&E==="/v1/digests")return D(y,200,{digests:n.folder.listDigests()});if(b.method==="GET"&&(B=re(/^\/v1\/digests\/(\d{4}-\d{2}-\d{2})$/))){let T=n.folder.readDigest(B[1],q.machine||null);if(!T)throw C(404,"not_found","no digest for "+B[1]);let L=n.folder.listDigests().filter(j=>j.date===B[1]&&!(j.shared&&T.shared)&&j.machine!==T.machine);return D(y,200,{meta:Ye(T.markdown).data,markdown:T.markdown,machine:T.machine,shared:!!T.shared,by:T.by||null,duplicates:T.duplicates||0,others:L})}if(b.method==="GET"&&E==="/v1/spaces")return D(y,200,n.spacesView());if(b.method==="POST"&&E==="/v1/spaces")return D(y,200,n.setSpace(await te(b)));if(b.method==="GET"&&E==="/v1/standup")return D(y,200,n.standup({space:q.space||null,date:q.date||null}));if(b.method==="POST"&&E==="/v1/digests"){let T=await te(b),L=await n.makeDigest({date:T.date});return D(y,201,L)}if(b.method==="GET"&&E==="/v1/models")return D(y,200,await n.modelChoices({provider:q.provider,baseUrl:q.baseUrl}));if(b.method==="POST"&&E==="/v1/models/ollama/start"){if(K(),!c.take())throw C(429,"slow_down","Starting Ollama already; wait a moment");return D(y,200,await n.startOllama())}if(b.method==="GET"&&E==="/v1/models/pulls")return D(y,200,{pulls:n.pulls.list()});if(b.method==="POST"&&E==="/v1/models/pull"){K();let T=await te(b);return D(y,202,n.startPull({tag:T.tag,use:!!T.use}))}if(b.method==="DELETE"&&(B=re(/^\/v1\/models\/pull\/(.+)$/))){K();let T=n.pulls.cancel(decodeURIComponent(B[1]));if(!T)throw C(404,"not_found","no download of that model");return D(y,200,T)}if(b.method==="GET"&&E==="/v1/remote")return D(y,200,n.remoteState(A));if(b.method==="POST"&&E==="/v1/remote/hosts")return K(),D(y,200,n.setRemoteHosts(await te(b)));if(b.method==="POST"&&E==="/v1/remote/reload")return K(),D(y,200,n.reloadRemote());if(b.method==="POST"&&E==="/v1/remote/revoke")return K(),D(y,200,{revoked:xi(e),...n.remoteState(A)});if(b.method==="POST"&&E==="/v1/remote/link"){let T=Kl(n.cfg.remoteHosts);if(!T.length)throw C(409,"remote_off","remote access is off: run leftoff tailscale on");if(n.cfg.remoteRequireToken===!1)return D(y,200,{needed:!1,urls:T});if(M&&!h.take("mint:"+p(b)))throw C(429,"slow_down","Too many codes in a row; wait a minute.");let L=!M||pe&&(await te(b).catch(()=>null)||{}).full===!0,j=vi(e,{codeOnly:!L,note:M?L?"from a trusted remote browser":"from a signed-in remote browser":null}),$={needed:!0,base:T[0],code:$l(j.signInCode),expiresAt:j.expiresAt,ttlMin:qs/6e4};return j.code&&Object.assign($,{urls:T.map(ge=>ge+"?t="+j.code),url:T[0]+"?t="+j.code,qrSvg:vc(T[0]+"?t="+j.code)}),D(y,200,$)}if(E==="/v1/relay"||E.startsWith("/v1/relay/")){let T=n.relayHost;if(!T){if(b.method==="GET"&&E==="/v1/relay")return D(y,200,{enabled:!1,configured:!1,available:!1,user:null,host:null,boardUrl:null,connected:!1,connecting:!1,latencyMs:null,devices:0,list:[]});if(b.method==="GET"&&E==="/v1/relay/devices")return D(y,200,{devices:[]});throw C(409,"relay_unavailable","the relay isn’t available in this process")}if(b.method==="GET"&&E==="/v1/relay")return await T.refreshRelayDevices().catch(()=>{}),D(y,200,{...T.status(),list:T.devices(),viewer:{relay:O,device:O?b.headers["x-leftoff-device"]:null,local:!M&&!tt(b),mayPair:pe,mayRevoke:M||!tt(b)}});if(b.method==="POST"&&E==="/v1/relay/enable")return K(),D(y,200,await T.enable(await te(b)));if(b.method==="POST"&&E==="/v1/relay/signup")return K(),D(y,200,await T.signupStart(await te(b)));if(b.method==="GET"&&E==="/v1/relay/signup")return K(),D(y,200,await T.signupCheck());if(b.method==="POST"&&E==="/v1/relay/signup/cancel")return K(),D(y,200,T.signupCancel());if(b.method==="POST"&&E==="/v1/relay/disable")return K(),D(y,200,await T.disable());if(b.method==="POST"&&E==="/v1/relay/pair"){Ie("Pairing");let L=await T.createPair();return M&&n.log("relay: pairing code made from "+(Je||"a remote browser")),D(y,200,L)}if(b.method==="POST"&&E==="/v1/relay/pair/cancel")return Ie("Pairing"),await T.cancelPairs(),D(y,200,{ok:!0});if(b.method==="GET"&&E==="/v1/relay/devices")return await T.refreshRelayDevices().catch(()=>{}),D(y,200,{devices:T.devices()});if(b.method==="POST"&&(B=re(/^\/v1\/relay\/devices\/([\w-]+)\/revoke$/))){Se();let L=O&&b.headers["x-leftoff-device"]===B[1];return D(y,200,{...await T.revoke(B[1]),self:L})}throw C(404,"not_found","no route for "+b.method+" "+E)}if(b.method==="GET"&&E==="/v1/events"){let T=Number(q.after),L=()=>g.filter($=>$.seq>T);if(!Number.isFinite(T)||T<0||T>S)return D(y,200,{seq:S,events:[],reset:Number.isFinite(T)&&T>S});let j=L();if(!j.length){let $=se();if(await new Promise(ge=>{let kt=setTimeout(pn,Math.min(25e3,Number(q.ms)||2e4));function pn(){clearTimeout(kt),k.delete(pn),$.removeEventListener("abort",pn),ge()}k.add(pn),$.addEventListener("abort",pn)}),$.aborted)return;j=L()}return n.boardOpened&&j.length,D(y,200,{seq:S,events:j})}if(b.method==="GET"&&E==="/v1/config")return D(y,200,n.publicConfig());if(b.method==="PATCH"&&E==="/v1/config")return D(y,200,n.updateConfig(await te(b),{via:M?"remote":"loopback"}));if(b.method==="PUT"&&(B=re(/^\/v1\/config\/keys\/([a-z-]+)$/)))return D(y,200,n.saveKey(B[1],(await te(b)).key));if(b.method==="GET"&&E==="/v1/stream"){y.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-store",connection:"keep-alive"}),y.write(`retry: 3000
357
+ `)},25e3);x.unref();let v=IA.createServer(async(b,y)=>{try{let _=n.cfg&&n.cfg.remoteHosts||[],A=vi(b.headers.host,t,_),O=DA(b,n);O&&(A="remote");let N=b.headers["x-forwarded-host"]?String(b.headers["x-forwarded-host"]).split(",")[0].trim():"";if(A==="loopback"&&N&&vi(N,t,_)==="remote"&&(A="remote"),!A)return L0(b)?D0(y,403,"bad_host","This address isn’t on "+Ke+"’s list",[mn(Ke)+" only answers to its own names, so other sites can’t reach it through your browser. <code>"+mn(String(b.headers.host||"(none)"))+"</code> isn’t one of them.","On the machine running "+mn(Ke)+", run <code>"+V+" tailscale on</code>: it adds this machine’s tailnet name to the list and prints a QR code that signs your phone in."]):pn(y,403,"bad_host","Host must be 127.0.0.1, localhost or leftoff.localhost, port "+t+", or a name in config remoteHosts (leftoff tailscale on)");if(b.headers["tailscale-funnel-request"])return L0(b)?D0(y,403,"funnel_refused","Not on the public internet",[mn(Ke)+" is not served over Tailscale Funnel. Use your tailnet: <code>"+V+" tailscale on</code> (tailscale serve)."]):pn(y,403,"funnel_refused","Leftoff is not served over Tailscale Funnel; use tailscale serve (tailnet only)");let I=b.headers.origin;if(!Sy(I,t,_))return pn(y,403,"bad_origin","cross-origin requests are refused");if(A==="loopback"&&st(b))return pn(y,403,"bad_host","a proxied request must use a name in config remoteHosts, not a loopback Host");let M=A==="remote",Y=M&&!O&&n.cfg.remoteRequireToken!==!1,U=new URL(b.url,"http://"+b.headers.host),E=U.pathname;if(b.method==="GET"&&!E.startsWith("/v1/")&&!O){let T=E==="/"||E==="/index.html"?"index.html":E.slice(1),L=bs.join(M0,bs.normalize(T).replace(/^(\.\.[/\\])+/,""));if(!L.startsWith(M0)||!hh.existsSync(L)||hh.statSync(L).isDirectory())return pn(y,404,"not_found","no such page");let j={"content-type":jA[bs.extname(L)]||"application/octet-stream","cache-control":"no-cache","x-content-type-options":"nosniff","x-frame-options":"DENY","content-security-policy":FA};if(j["referrer-policy"]="no-referrer",T==="index.html")if(!Y)j["set-cookie"]="cp_token="+encodeURIComponent(i)+"; HttpOnly; SameSite=Strict; Path=/"+(M?"; Secure":"");else if(U.searchParams.has("t")){let $=b.headers["tailscale-user-login"]||null,me=vy(e,U.searchParams.get("t"),{userAgent:b.headers["user-agent"],who:$});return me?(n.log("remote login: new browser session "+me.id+" (link)"+($?" ("+$+")":"")+" via "+b.headers.host),y.writeHead(302,{location:"/","cache-control":"no-store","referrer-policy":"no-referrer","set-cookie":Jl(me.secret)}),y.end()):(y.writeHead(302,{location:"/?login=expired","cache-control":"no-store","referrer-policy":"no-referrer"}),y.end())}else{let $=tu(b,Ai);$&&Wl(e,$,{touch:!0})&&(j["set-cookie"]=Jl($))}return T==="sw.js"&&(j["service-worker-allowed"]="/"),y.writeHead(200,j),hh.createReadStream(L).pipe(y)}if(b.method==="GET"&&E==="/v1/remote/ping"){let T=!Y||M&&!!Wl(e,tu(b,Ai));return D(y,200,{ok:!0,app:Ke,version:X,via:A,proxied:st(b),loginRequired:Y,signedIn:T})}if(b.method==="POST"&&E==="/v1/remote/signin"){if(!M)throw C(400,"not_remote","sign-in codes are for other devices; this machine needs none");if(!Y)return D(y,200,{ok:!0,needed:!1});if(!/application\/json/.test(String(b.headers["content-type"]||"")))throw C(415,"json_only","send JSON");if(!h.take(p(b)))throw C(429,"slow_down","Too many tries. Wait a minute, then try again.");let T=await te(b),L=b.headers["tailscale-user-login"]||null,j=xy(e,T.code,{userAgent:b.headers["user-agent"],who:L});if(!j.ok)throw n.log("remote login: wrong sign-in code via "+b.headers.host+(j.reason==="locked"?" (too many: pending codes voided)":"")),j.reason==="locked"?C(429,"locked","Too many wrong codes, so every pending code was cancelled. Get a new one: "+V+" link, on the machine running "+Ke+"."):C(401,"bad_code","That code isn’t right, or it was already used or has expired. Codes work once, for "+Xs/6e4+" minutes.");return n.log("remote login: new browser session "+j.id+" (code)"+(L?" ("+L+")":"")+" via "+b.headers.host),D(y,200,{ok:!0},{"set-cookie":Jl(j.secret)})}let P=String(b.headers.authorization||""),W=P.startsWith("Bearer ")?P.slice(7).trim():tu(b,"cp_token");if(!(O||i&&UA(W,i)||M&&!!Wl(e,tu(b,Ai))))return Y?pn(y,401,"login_required","this browser isn't signed in: on the machine running Leftoff, run `leftoff link` and scan the QR code, or type its sign-in code here"):pn(y,401,"unauthorized","missing or wrong token (see ~/checkpoints/.secret/token)");let K=()=>{if(M||st(b))throw C(403,"loopback_only","only from the machine running Leftoff")},pe=!M&&!st(b)?!0:M&&n.answeringInfo("remote").remoteAllowed,Ie=T=>{if(!pe)throw M||K(),C(403,"trust_off",T+" from another device is off. On a Mac, turn on Settings → Privacy & security → Trust my paired devices.")},Se=()=>{!M&&st(b)&&K()},q=Object.fromEntries(U.searchParams),re=T=>E.match(T),B,Ve=O?RA(b.headers["x-leftoff-device-name"]):null,z=()=>({via:M?"remote":"loopback",proxied:st(b),ip:O?null:b.socket.remoteAddress||null,host:O?"relay":b.headers.host||null,who:O?(Ve||"a paired phone")+" (through the relay)":b.headers["tailscale-user-login"]||null,userAgent:b.headers["user-agent"]||null,relay:O||void 0}),ne=()=>{if(!m.take(O?"relay:"+b.headers["x-leftoff-device"]:M?"remote:"+(b.headers["x-forwarded-for"]||b.socket.remoteAddress):"local"))throw C(429,"rate_limited","Too many answers in a minute; wait a moment.")},se=()=>{let T=new AbortController;return y.on("close",()=>{y.writableEnded||T.abort()}),T.signal};if(b.method==="GET"&&E==="/v1/needs")return D(y,200,{items:n.needsList(),recent:n.recentAnswered(Math.min(50,Number(q.recent)||20)),answering:n.answeringInfo(M?"remote":"loopback"),dismissed:n.dismissedNeeds(),todos:n.todosOverview()});if(b.method==="GET"&&E==="/v1/todos")return D(y,200,n.todosOverview());if(b.method==="POST"&&E==="/v1/todos/done"){let T=await te(b);return D(y,200,n.todoDone(String(T.key||"")))}if(b.method==="POST"&&E==="/v1/todos/restore"){let T=await te(b),L=T.all===!0?n.todosOverview().done.map(j=>j.key):(Array.isArray(T.keys)?T.keys:[T.key]).filter(j=>typeof j=="string"&&/^[0-9a-f]{8,32}$/.test(j)).slice(0,500);return D(y,200,n.todoRestore(L))}if(b.method==="POST"&&E==="/v1/todos"){let T=await te(b);return D(y,200,n.todoAdd(T.text,{sessionId:T.sessionId||null}))}if(b.method==="POST"&&E==="/v1/needs/dismiss"){let T=await te(b);if(typeof T.key!="string"||!/^[0-9a-f]{8,64}$/.test(T.key))throw C(400,"bad_request","key is required");return D(y,200,n.dismissNeed(T.key))}if(b.method==="POST"&&E==="/v1/needs/restore"){let T=await te(b),L=T.all===!0?n.dismissedNeeds().map(j=>j.key):(Array.isArray(T.keys)?T.keys:[T.key]).filter(j=>typeof j=="string"&&/^[0-9a-f]{8,64}$/.test(j)).slice(0,500);if(!L.length&&T.all!==!0)throw C(400,"bad_request","key or keys is required");return D(y,200,n.restoreNeeds(L))}if(b.method==="POST"&&E==="/v1/asks")return K(),D(y,200,n.openAsk(await te(b)));if(b.method==="POST"&&E==="/v1/asks/settle"){K();let T=await te(b);if(!T.sessionId||!T.tool)throw C(400,"bad_settle","sessionId and tool are required");let L=n.asks.settleSession(T.sessionId,String(T.reason||"answered_in_terminal"),T.all?{}:{key:mi(T.tool,T.input)});return D(y,200,{ok:!0,settled:L.length})}if(b.method==="GET"&&(B=re(/^\/v1\/asks\/([\w-]+)\/wait$/))){K();let T=await n.asks.wait(B[1],Number(q.ms)||2e4,se());return T===null?void 0:D(y,200,T)}if(b.method==="POST"&&(B=re(/^\/v1\/asks\/([\w-]+)\/cancel$/))){K();let T=n.asks.settle(B[1],String((await te(b)).reason||"cancelled_in_terminal"));return D(y,200,T?{ok:!0,state:T.state}:{ok:!1,state:"gone"})}if(b.method==="GET"&&(B=re(/^\/v1\/asks\/([\w-]+)$/))){let T=n.asks.get(B[1]);if(!T)throw C(404,"not_found","no such request");return D(y,200,n.asks.publicAsk(T))}if(b.method==="POST"&&(B=re(/^\/v1\/asks\/([\w-]+)\/answer$/)))return n.assertMayAnswer(z()),ne(),D(y,200,await n.answerAsk(B[1],await te(b),z()));if(b.method==="POST"&&(B=re(/^\/v1\/sessions\/([^/]+)\/reply$/)))return n.assertMayAnswer(z()),ne(),D(y,200,await n.postReply(decodeURIComponent(B[1]),(await te(b)).text,z()));if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)\/replies\/next$/))){K();let T=se(),L=await n.takeReply(decodeURIComponent(B[1]),Number(q.ms)||2e4,T);return T.aborted?void 0:D(y,200,L)}if(b.method==="GET"&&E==="/v1/sync"){let T=or({current:e});return D(y,200,{...T,machine:n.machine,backups:ir(e),lastMove:eo(),join:ar(e,{cli:V}),viewer:{local:!M&&!st(b)}})}if(b.method==="POST"&&E==="/v1/sync/plan"){K();let T=await te(b);if(!T.to)throw C(400,"bad_request","to: the folder to move to");let L=rr(e,bs.resolve(R0(String(T.to))),{machine:n.machine,identity:n.peers?n.peers.id:null});T.force&&(L.collisions=L.collisions.filter(me=>me.code!=="machine_name_maybe_taken"),L.ok=!L.errors.length&&!L.collisions.length);let{files:j,...$}=L;return D(y,200,{...$,toDisplay:we(L.to),backupDisplay:we(L.backup),sample:j.filter(me=>me.action!=="same").slice(0,12),join:ar(L.to,{cli:V})})}if(b.method==="POST"&&E==="/v1/sync/move"){K();let T=await te(b);if(!T.to)throw C(400,"bad_request","to: the folder to move to");let L=bs.resolve(R0(String(T.to))),j=rr(e,L,{machine:n.machine,identity:n.peers?n.peers.id:null}),$=j.collisions.filter(gn=>!(T.force&&gn.code==="machine_name_maybe_taken"));if(j.errors.length||$.length)throw C(409,(j.errors[0]||$[0]).code,(j.errors[0]||$[0]).message);let me=[$e,"move",L,"--yes","--json","--daemon-pid",String(process.pid)];T.force&&me.push("--force");let vt=I0(process.execPath,me,{detached:!0,stdio:"ignore",env:{...process.env}});return vt.unref(),n.log("sync: moving the folder to "+L+" (leftoff move, pid "+vt.pid+")"),D(y,202,{started:!0,to:L,statusFile:Ui()})}if(b.method==="POST"&&E==="/v1/sync/backup/delete"){K();let T=await te(b);if(T.confirm!==!0)throw C(400,"confirm","send { path, confirm: true } to delete a backup");let L=ir(e).find(j=>j.path===T.path);if(!L)throw C(404,"not_found","no such Leftoff backup next to this folder");return D(y,200,Hi(L.path))}if(b.method==="GET"&&E==="/v1/syncthing"){let T=!M&&!st(b),L=await sn({root:e}),j=Du(),$={...L,myID:void 0,pairing:Yn(Ft()),run:j&&Date.now()-new Date(j.at).getTime()<6*36e5?j:null,viewer:{local:T}};return T?$.install={...Cs(),job:{...ft,lines:ft.lines.slice(-60)}}:($.folder&&($.folder={...$.folder,path:void 0}),$.knocked=($.knocked||[]).map(me=>({...me,deviceId:void 0}))),D(y,200,$)}if(b.method==="POST"&&E==="/v1/syncthing/install"){if(K(),(await te(b)).confirm!==!0)throw C(400,"confirm","send { confirm: true } after showing the command: "+Cr);if(vn().installed)return D(y,200,{already:!0});if(!Cs().brew)throw C(409,"no_brew","Homebrew isn’t installed on this Mac");return D(y,202,{started:!0,job:LA(n)})}if(b.method==="POST"&&E==="/v1/syncthing/start"){K();let T=Mr();if(!T.ok)throw C(409,T.code||"start_failed",T.error);let L=await Ns({timeoutMs:2e4});if(!L.ok)throw C(504,"not_running",L.error);return n.log("syncthing: started ("+T.how+")"),D(y,200,{ok:!0})}if(b.method==="POST"&&(E==="/v1/syncthing/setup"||E==="/v1/syncthing/join")){K();let T=await te(b),L=E.endsWith("/join");if(L&&!T.code)throw C(400,"bad_request","code: the pairing code from your other Mac");if(L)try{(await Promise.resolve().then(()=>(vo(),Hp))).decodePairCode(String(T.code))}catch(me){throw C(400,me.code||"bad_code",me.message)}let j=[$e,L?"join":"sync",...L?[]:["setup"],"--yes","--json","--daemon-pid",String(process.pid),...T.path?["--path",String(T.path)]:[]];bo({kind:L?"join":"setup",state:"starting",message:"Starting…"});let $=I0(process.execPath,j,{detached:!0,stdio:"ignore",env:{...process.env,...L?{LEFTOFF_JOIN_CODE:String(T.code)}:{}}});return $.unref(),n.log("syncthing: "+(L?"joining another Mac by code":"putting the folder in Syncthing")+" (pid "+$.pid+")"),D(y,202,{started:!0})}if(b.method==="POST"&&E==="/v1/syncthing/pair"){K();let T=await sn({root:e});if(!T.running)throw C(409,"not_running","Syncthing isn’t running");if(!T.folder)throw C(409,"no_folder","The Leftoff folder isn’t in Syncthing yet");let L=await te(b),j=Rr({deviceId:T.myID,machine:n.machine,reuse:L.fresh!==!0});return n.log("syncthing: pairing code made; accepting the matching Mac until "+new Date(j.expiresAt).toISOString()),D(y,200,{code:j.code,command:lo(j.code),fresh:lo(j.code,{installed:!1}),expiresAt:new Date(j.expiresAt).toISOString()},{"cache-control":"no-store"})}if(b.method==="POST"&&E==="/v1/syncthing/pair/cancel")return K(),Uu(),n.log("syncthing: pairing code cancelled"),D(y,200,{ok:!0});if(b.method==="POST"&&E==="/v1/syncthing/accept"){K();let T=await te(b);if(T.confirm!==!0||!T.deviceId)throw C(400,"confirm","send { deviceId, confirm: true } after the person clicked Accept");let L=mt();if(!L.ok)throw C(409,L.code||"no_syncthing",L.reason);let j;try{j=await Fr(L.client,{deviceId:String(T.deviceId),log:$=>n.log($)})}catch($){throw C($.code==="not_knocked"?404:409,$.code||"accept_failed",$.message)}return D(y,200,j)}if(b.method==="POST"&&E==="/v1/syncthing/stop"){if(K(),(await te(b)).confirm!==!0)throw C(400,"confirm","send { confirm: true } to stop syncing");let L=mt();if(!L.ok)throw C(409,L.code||"no_syncthing",L.reason);let j=await Dr(L.client);return n.log("syncthing: stopped syncing the Leftoff folder (files kept)"),D(y,200,j)}if(b.method==="GET"&&E==="/v1/agents"){let T=!M&&!st(b);return D(y,200,{...n.agentsOverview({local:T}),viewer:{local:T}})}if(b.method==="POST"&&E==="/v1/agents")return K(),D(y,200,await n.addAgent(await te(b)));if(b.method==="POST"&&(B=re(/^\/v1\/agents\/([a-z0-9-]+)\/refresh$/))){if(!d.take())throw C(429,"slow_down","too many refreshes; wait a moment");return D(y,200,await n.pullAgent(B[1],{force:!0}))}if(b.method==="DELETE"&&(B=re(/^\/v1\/agents\/([a-z0-9-]+)$/)))return K(),D(y,200,n.removeAgent(B[1],{purge:q.purge==="1"}));if(b.method==="GET"&&E==="/v1/peers")return D(y,200,{...n.peersOverview(),viewer:{local:!M&&!st(b)}});if(b.method==="POST"&&E==="/v1/peers/allow"){if(K(),!n.peers)throw C(409,"peers_off","cross-machine actions are off on this machine (LEFTOFF_PEERS=0 or peers.enabled false)");return D(y,200,await n.peers.setAllowIncoming((await te(b)).allow===!0))}if(b.method==="POST"&&(B=re(/^\/v1\/peers\/([\w.-]+)\/trust$/))){if(K(),!n.peers)throw C(409,"peers_off","cross-machine actions are off on this machine");return D(y,200,n.peers.trust(decodeURIComponent(B[1])))}if(b.method==="POST"&&E==="/v1/peers/check")return n.peers&&await n.peers.poll(),D(y,200,n.peersOverview());if(b.method==="GET"&&E==="/v1/health")return D(y,200,{...n.health(),answering:n.answeringInfo(M?"remote":"loopback")});if(b.method==="POST"&&E==="/v1/mod/health")return D(y,200,n.recordModHealth(await te(b)));if(b.method==="GET"&&E==="/v1/changelog")return D(y,200,n.changelog());if(b.method==="POST"&&E==="/v1/changelog/seen")return D(y,200,n.markChangelogSeen((await te(b)).version));if(b.method==="POST"&&E==="/v1/changelog/dismiss")return D(y,200,n.dismissUpgradeNotice());if(b.method==="GET"&&E==="/v1/updates")return D(y,200,n.updateState());if(b.method==="POST"&&E==="/v1/updates/check")return D(y,200,await n.runUpdateCheck({force:!0}));if(b.method==="POST"&&E==="/v1/events"){let T=await te(b);if(!Array.isArray(T.events))throw C(400,"bad_request","body must be { events: Event[] }");return D(y,200,n.ingest(T.events))}if(b.method==="POST"&&(B=re(/^\/v1\/hooks\/([A-Za-z]+)$/))){let T=await te(b);return D(y,200,n.ingestHook(B[1],T))}if(b.method==="POST"&&(B=re(/^\/v1\/sessions\/([^/]+)\/checkpoints$/))){let T=decodeURIComponent(B[1]),L=await te(b);if(L.generate){let j=await n.generateCheckpoint(T,L.trigger||"manual",{userNote:L.userNote,length:L.length||void 0});return D(y,201,j)}return D(y,201,n.addCheckpoint(T,L))}if(b.method==="POST"&&E==="/v1/checkpoints/preview"){let T=await te(b);if(!T.sessionId)throw C(400,"bad_request","sessionId is required");return D(y,200,await n.generateCheckpoint(String(T.sessionId),"manual",{length:T.length||void 0,save:!1}))}if(b.method==="GET"&&E==="/v1/sessions")return D(y,200,{sessions:n.listSessions({...q,includeArchived:q.archived==="1"}),closedCount:n.closedCount({machine:q.machine}),todoCount:n.todosOverview().count,viewer:{local:!M,ghostty:n.platform==="darwin",mayAnswer:n.answeringInfo(M?"remote":"loopback").allowedHere}});if(b.method==="GET"&&E==="/v1/sessions/closed")return D(y,200,{...n.closedSessions({q:q.q,machine:q.machine,repo:q.repo,space:q.space,docs:q.docs==="1",limit:q.limit,offset:q.offset}),viewer:{local:!M&&!st(b),ghostty:n.platform==="darwin"}});if(b.method==="GET"&&E==="/v1/gh"){let T=await za({fresh:q.fresh==="1"});return T.status==="ready"&&n.prs&&(n.prs.offUntil=0),D(y,200,T)}if(b.method==="GET"&&E==="/v1/prs")return D(y,200,await n.prsOverview({view:q.view,fresh:q.fresh==="1"}));if(b.method==="POST"&&(B=re(/^\/v1\/sessions\/([^/]+)\/prs$/)))return D(y,200,await n.linkPr(decodeURIComponent(B[1]),await te(b)));if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)\/turns$/)))return D(y,200,await n.sessionTurns(decodeURIComponent(B[1]),{limit:q.limit}));if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)\/file$/)))return D(y,200,await n.sessionFile(decodeURIComponent(B[1]),{path:q.path?String(q.path):null,plan:q.plan?String(q.plan):null,maxBytes:M||st(b)?tl:ti}));if(b.method==="GET"&&E==="/v1/restore")return D(y,200,{...n.restoreCandidates({since:q.since,repo:q.repo}),viewer:{local:!M&&!st(b)}});if(b.method==="POST"&&E==="/v1/restore"){K();let T=await te(b);if(!Array.isArray(T.ids))throw C(400,"bad_request","ids: the session ids to restore");if(T.print!==!0&&!u.take())throw C(429,"slow_down","too many restore requests; wait a moment");return D(y,200,await n.restore(T.ids,{print:T.print===!0}))}if(b.method==="POST"&&E==="/v1/restore/launched"){K();let T=await te(b),L=n.restoreTargets(Array.isArray(T.ids)?T.ids:[]).map(j=>j.id);return n.markRestored(L),D(y,200,{ok:!0,marked:L.length})}if(b.method==="POST"&&(B=re(/^\/v1\/sessions\/([^/]+)\/resume$/))){if(K(),!u.take())throw C(429,"slow_down","too many resume requests; wait a moment");return D(y,200,await n.resume(decodeURIComponent(B[1])))}if(b.method==="POST"&&(B=re(/^\/v1\/sessions\/([^/]+)\/go$/))){if(K(),!l.take())throw C(429,"slow_down","too many Go requests; wait a moment");return D(y,200,await n.jump(decodeURIComponent(B[1])))}if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)\/where$/)))return K(),D(y,200,n.where(decodeURIComponent(B[1])));if(b.method==="GET"&&E==="/v1/jump/speed")return K(),D(y,200,await n.jumpSpeed());if(b.method==="GET"&&E==="/v1/jump/check")return K(),D(y,200,await n.jumpCheck(q.kind||"ghostty"));if(b.method==="GET"&&E==="/v1/lists")return D(y,200,{...n.listsOverview({all:q.all==="1",repo:q.repo||null}),mayEdit:n.answeringInfo(M?"remote":"loopback").allowedHere});if(b.method==="GET"&&E==="/v1/lists/resolve")return D(y,200,{list:n.getList(q.ref)});if(b.method==="POST"&&E==="/v1/lists")return D(y,200,n.saveList(await te(b),z()));if(b.method==="GET"&&(B=re(/^\/v1\/lists\/(~?[\w.-]+\/[\w.-]+)$/)))return D(y,200,{list:n.getList(B[1]),mayEdit:n.answeringInfo(M?"remote":"loopback").allowedHere});if(b.method==="POST"&&(B=re(/^\/v1\/lists\/(~?[\w.-]+\/[\w.-]+)\/change$/)))return D(y,200,n.changeList(B[1],await te(b),z()));if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)\/lists$/)))return D(y,200,n.sessionLists(decodeURIComponent(B[1])));if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)\/lists\/find$/)))return D(y,200,n.findListsInSession(decodeURIComponent(B[1])));if(b.method==="POST"&&(B=re(/^\/v1\/sessions\/([^/]+)\/list-hint\/dismiss$/)))return n.assertMayEditLists(z()),D(y,200,n.dismissListHint(decodeURIComponent(B[1])));if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)$/)))return D(y,200,{...n.getSession(decodeURIComponent(B[1])),viewer:{local:!M&&!st(b),ghostty:n.platform==="darwin"}});if(b.method==="PATCH"&&(B=re(/^\/v1\/sessions\/([^/]+)$/)))return D(y,200,n.patchSession(decodeURIComponent(B[1]),await te(b)));if(b.method==="GET"&&(B=re(/^\/v1\/sessions\/([^/]+)\/checkpoints$/)))return D(y,200,{checkpoints:n.sessionCheckpoints(decodeURIComponent(B[1]),q)});if(b.method==="GET"&&E==="/v1/checkpoints")return D(y,200,{checkpoints:n.timeline(q)});if(b.method==="GET"&&E==="/v1/search")return D(y,200,n.search(q.q));if(b.method==="GET"&&E==="/v1/catchups")return D(y,200,{catchups:n.folder.listCatchups(),lastAt:(()=>{let T=n.folder.lastCatchupAt();return T==null?null:new Date(T).toISOString()})()});if(b.method==="GET"&&E==="/v1/catchups/options")return D(y,200,n.catchupOptions({since:q.since,to:q.to}));if(b.method==="POST"&&E==="/v1/catchups/prepare"){let T=await te(b),L=await n.makeCatchup({...T,save:!1,useModel:!1}),{catchupLength:j,CATCHUP_SYSTEM:$,summaryPrompt:me}=await Promise.resolve().then(()=>(gl(),wg)),vt=j(n.cfg,L.recap.length);return D(y,200,{window:L.recap.window,sessions:L.recap.sessions.length,needsYou:L.recap.needsYou.length,prompt:L.recap.sessions.length?{system:$.replace("{n}",String(vt.bullets)),prompt:me(L.recap,{maxChars:n.cfg.summarizer&&n.cfg.summarizer.maxInputChars||24e3}),maxTokens:vt.maxTokens}:null})}if(b.method==="POST"&&E==="/v1/catchups"){let T=await te(b);return D(y,200,await n.makeCatchup(T))}if(b.method==="GET"&&(B=re(/^\/v1\/catchups\/(\d{4}-\d{2}-\d{2}T\d{4}(?:-\d+)?)$/))){let T=n.readCatchup(B[1]);if(!T)throw C(404,"not_found","no catch-up named "+B[1]);return D(y,200,T)}if(b.method==="GET"&&E==="/v1/digests")return D(y,200,{digests:n.folder.listDigests()});if(b.method==="GET"&&(B=re(/^\/v1\/digests\/(\d{4}-\d{2}-\d{2})$/))){let T=n.folder.readDigest(B[1],q.machine||null);if(!T)throw C(404,"not_found","no digest for "+B[1]);let L=n.folder.listDigests().filter(j=>j.date===B[1]&&!(j.shared&&T.shared)&&j.machine!==T.machine);return D(y,200,{meta:Ye(T.markdown).data,markdown:T.markdown,machine:T.machine,shared:!!T.shared,by:T.by||null,duplicates:T.duplicates||0,others:L})}if(b.method==="GET"&&E==="/v1/spaces")return D(y,200,n.spacesView());if(b.method==="POST"&&E==="/v1/spaces")return D(y,200,n.setSpace(await te(b)));if(b.method==="GET"&&E==="/v1/standup")return D(y,200,n.standup({space:q.space||null,date:q.date||null}));if(b.method==="POST"&&E==="/v1/digests"){let T=await te(b),L=await n.makeDigest({date:T.date});return D(y,201,L)}if(b.method==="GET"&&E==="/v1/models")return D(y,200,await n.modelChoices({provider:q.provider,baseUrl:q.baseUrl}));if(b.method==="POST"&&E==="/v1/models/ollama/start"){if(K(),!c.take())throw C(429,"slow_down","Starting Ollama already; wait a moment");return D(y,200,await n.startOllama())}if(b.method==="GET"&&E==="/v1/models/pulls")return D(y,200,{pulls:n.pulls.list()});if(b.method==="POST"&&E==="/v1/models/pull"){K();let T=await te(b);return D(y,202,n.startPull({tag:T.tag,use:!!T.use}))}if(b.method==="DELETE"&&(B=re(/^\/v1\/models\/pull\/(.+)$/))){K();let T=n.pulls.cancel(decodeURIComponent(B[1]));if(!T)throw C(404,"not_found","no download of that model");return D(y,200,T)}if(b.method==="GET"&&E==="/v1/remote")return D(y,200,n.remoteState(A));if(b.method==="POST"&&E==="/v1/remote/hosts")return K(),D(y,200,n.setRemoteHosts(await te(b)));if(b.method==="POST"&&E==="/v1/remote/reload")return K(),D(y,200,n.reloadRemote());if(b.method==="POST"&&E==="/v1/remote/revoke")return K(),D(y,200,{revoked:_i(e),...n.remoteState(A)});if(b.method==="POST"&&E==="/v1/remote/link"){let T=zl(n.cfg.remoteHosts);if(!T.length)throw C(409,"remote_off","remote access is off: run leftoff tailscale on");if(n.cfg.remoteRequireToken===!1)return D(y,200,{needed:!1,urls:T});if(M&&!h.take("mint:"+p(b)))throw C(429,"slow_down","Too many codes in a row; wait a minute.");let L=!M||pe&&(await te(b).catch(()=>null)||{}).full===!0,j=Ti(e,{codeOnly:!L,note:M?L?"from a trusted remote browser":"from a signed-in remote browser":null}),$={needed:!0,base:T[0],code:Yl(j.signInCode),expiresAt:j.expiresAt,ttlMin:Xs/6e4};return j.code&&Object.assign($,{urls:T.map(me=>me+"?t="+j.code),url:T[0]+"?t="+j.code,qrSvg:Ac(T[0]+"?t="+j.code)}),D(y,200,$)}if(E==="/v1/relay"||E.startsWith("/v1/relay/")){let T=n.relayHost;if(!T){if(b.method==="GET"&&E==="/v1/relay")return D(y,200,{enabled:!1,configured:!1,available:!1,user:null,host:null,boardUrl:null,connected:!1,connecting:!1,latencyMs:null,devices:0,list:[]});if(b.method==="GET"&&E==="/v1/relay/devices")return D(y,200,{devices:[]});throw C(409,"relay_unavailable","the relay isn’t available in this process")}if(b.method==="GET"&&E==="/v1/relay")return await T.refreshRelayDevices().catch(()=>{}),D(y,200,{...T.status(),list:T.devices(),viewer:{relay:O,device:O?b.headers["x-leftoff-device"]:null,local:!M&&!st(b),mayPair:pe,mayRevoke:M||!st(b)}});if(b.method==="POST"&&E==="/v1/relay/enable")return K(),D(y,200,await T.enable(await te(b)));if(b.method==="POST"&&E==="/v1/relay/signup")return K(),D(y,200,await T.signupStart(await te(b)));if(b.method==="GET"&&E==="/v1/relay/signup")return K(),D(y,200,await T.signupCheck());if(b.method==="POST"&&E==="/v1/relay/signup/cancel")return K(),D(y,200,T.signupCancel());if(b.method==="POST"&&E==="/v1/relay/disable")return K(),D(y,200,await T.disable());if(b.method==="POST"&&E==="/v1/relay/pair"){Ie("Pairing");let L=await T.createPair();return M&&n.log("relay: pairing code made from "+(Ve||"a remote browser")),D(y,200,L)}if(b.method==="POST"&&E==="/v1/relay/pair/cancel")return Ie("Pairing"),await T.cancelPairs(),D(y,200,{ok:!0});if(b.method==="GET"&&E==="/v1/relay/devices")return await T.refreshRelayDevices().catch(()=>{}),D(y,200,{devices:T.devices()});if(b.method==="POST"&&(B=re(/^\/v1\/relay\/devices\/([\w-]+)\/revoke$/))){Se();let L=O&&b.headers["x-leftoff-device"]===B[1];return D(y,200,{...await T.revoke(B[1]),self:L})}throw C(404,"not_found","no route for "+b.method+" "+E)}if(b.method==="GET"&&E==="/v1/events"){let T=Number(q.after),L=()=>g.filter($=>$.seq>T);if(!Number.isFinite(T)||T<0||T>S)return D(y,200,{seq:S,events:[],reset:Number.isFinite(T)&&T>S});let j=L();if(!j.length){let $=se();if(await new Promise(me=>{let vt=setTimeout(gn,Math.min(25e3,Number(q.ms)||2e4));function gn(){clearTimeout(vt),k.delete(gn),$.removeEventListener("abort",gn),me()}k.add(gn),$.addEventListener("abort",gn)}),$.aborted)return;j=L()}return n.boardOpened&&j.length,D(y,200,{seq:S,events:j})}if(b.method==="GET"&&E==="/v1/config")return D(y,200,n.publicConfig());if(b.method==="PATCH"&&E==="/v1/config")return D(y,200,n.updateConfig(await te(b),{via:M?"remote":"loopback"}));if(b.method==="PUT"&&(B=re(/^\/v1\/config\/keys\/([a-z-]+)$/)))return D(y,200,n.saveKey(B[1],(await te(b)).key));if(b.method==="GET"&&E==="/v1/stream"){y.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-store",connection:"keep-alive"}),y.write(`retry: 3000
358
358
 
359
- `),a.add(y),n.boardOpened&&n.boardOpened(),b.on("close",()=>{a.delete(y),n.boardClosed&&n.boardClosed()});return}return fn(y,404,"not_found","no route for "+b.method+" "+E)}catch(_){return _.status?fn(y,_.status,_.code||"error",_.message):(n.log("500: "+(_.stack||_.message)),fn(y,500,"internal",_.message))}});return v.on("close",()=>{clearInterval(x);for(let b of a)b.end()}),{server:v,listen:()=>new Promise((b,y)=>{v.once("error",y),v.listen(t,s,()=>b(v))}),close:()=>new Promise(b=>{for(let y of a)y.end();v.close(()=>b()),v.closeAllConnections&&v.closeAllConnections()})}}var ut,_0,CA,IA,NA,hn,C0,LA,Ff=J(()=>{xe();mi();Tf();tl();Te();Jl();Ri();No();xe();_t();Qs();is();ko();lh();ut={state:"idle",lines:[],error:null,startedAt:null,finishedAt:null,stage:null};_0=ws.join(ws.dirname(TA(import.meta.url)),"..","ui"),CA=1024*1024,IA={".html":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"text/javascript; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".ico":"image/x-icon",".webmanifest":"application/manifest+json; charset=utf-8",".json":"application/json; charset=utf-8"},NA="default-src 'self'; style-src 'self'; script-src 'self'; img-src 'self' data:; connect-src 'self'; manifest-src 'self'; worker-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'";hn=n=>String(n).replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[e]);C0=n=>n==="~"?process.env.HOME||"":n.startsWith("~/")?ws.join(process.env.HOME||"",n.slice(2)):n;LA=["x-forwarded-for","x-forwarded-host","x-forwarded-proto","forwarded","x-real-ip","tailscale-user-login","tailscale-user-name","tailscale-headers-info"]});for(let[n,e]of Object.entries(process.env))n.startsWith("LEFTOFF_")&&n.length>8&&e!==void 0&&(process.env["CHECKPOINTS_"+n.slice(8)]=e);xe();Tf();Ff();import Xt from"node:fs";import Oh from"node:os";import bs from"node:path";import{fileURLToPath as kT}from"node:url";xe();xn();Te();mi();import{execFileSync as DA}from"node:child_process";function RA({start:n=process.ppid,ps:e=PA,maxDepth:t=6,now:s=Date.now()}={}){let o=n;for(let r=0;r<t&&o>1;r++){let i=e(o);if(!i)return null;if(/claude/i.test(i.comm))return Qh(o+" "+i.tty+" "+i.etime,s);o=i.ppid}return null}function PA(n){try{let t=DA("ps",["-o","ppid=,tty=,etime=,comm=","-p",String(n)],{encoding:"utf8",env:{...process.env,LC_ALL:"C"},timeout:1e3}).trim().match(/^(\d+)\s+(\S+)\s+(\S+)\s+(.+)$/);return t?{ppid:Number(t[1]),tty:t[2],etime:t[3],comm:t[4]}:null}catch{return null}}function jA(n=process.env){let e={};for(let t of Zh)n[t]&&(e[t]=n[t]);return e}function FA(n={},e=process.env){let t=typeof n.model=="string"?n.model:n.model&&n.model.id||null;return{termEnv:jA(e),backend:Gh(e,{model:t})}}async function M0(n=2e3){return process.stdin.isTTY?"":new Promise(e=>{let t="",s=setTimeout(()=>e(t),n);process.stdin.setEncoding("utf8"),process.stdin.on("data",o=>t+=o),process.stdin.on("end",()=>{clearTimeout(s),e(t)}),process.stdin.on("error",()=>{clearTimeout(s),e(t)})})}async function L0(n,{stdinText:e,timeoutMs:t=800,env:s=process.env}={}){if(s.CHECKPOINTS_INTERNAL)return{skipped:"internal"};let o=Z(),r=e??await M0(),i;try{i=JSON.parse(r||"{}")}catch{return{skipped:"bad_json"}}let a=n||i.hook_event_name;if(!a||!i.session_id)return{skipped:"no_event"};if(a==="SessionStart"&&!i._checkpoints){let d=null;try{d=RA()}catch{}i._checkpoints={proc:d,...FA(i,s)}}let l=Q(o),c=ve(o),u=new Date().toISOString();try{let d=await fetch("http://127.0.0.1:"+l.port+"/v1/hooks/"+encodeURIComponent(a),{method:"POST",headers:{"content-type":"application/json",authorization:"Bearer "+(c||"")},body:JSON.stringify(i),signal:AbortSignal.timeout(t)});if(d.ok)return{posted:!0};if(d.status===400||d.status===404)return{rejected:d.status};throw new Error("status "+d.status)}catch{try{return Jp(o,de(l),i.session_id,{hook:a,payload:i,at:u}),{spooled:!0}}catch(d){return{lost:d.message}}}}async function D0({stdinText:n,out:e=process.stdout,maxMs:t=9.5*60*1e3,fetchImpl:s=fetch,onSignal:o=r=>{process.once("SIGTERM",r),process.once("SIGINT",r),process.once("SIGHUP",r)}}={}){if(process.env.CHECKPOINTS_INTERNAL)return{skipped:"internal"};let r=Z(),i=n??await M0(),a;try{a=JSON.parse(i||"{}")}catch{return{skipped:"bad_json"}}if(!a.session_id||!a.tool_name)return{skipped:"no_event"};let l=Q(r);if(l.answers&&l.answers.enabled===!1)return{skipped:"off"};let c=ve(r),u="http://127.0.0.1:"+l.port,d={"content-type":"application/json",authorization:"Bearer "+(c||"")},f=async(S,k,w,x)=>{let v=await s(u+k,{method:S,headers:d,body:w===void 0?void 0:JSON.stringify(w),signal:AbortSignal.timeout(x)});if(!v.ok)throw new Error("status "+v.status);return v.json()},h;try{h=await f("POST","/v1/asks",{sessionId:a.session_id,tool:a.tool_name,input:a.tool_input,suggestions:a.permission_suggestions,capture:"hooks",cwd:a.cwd,permissionMode:a.permission_mode},1500)}catch{return{skipped:"daemon_down"}}if(!h||!h.id||h.state!=="waiting")return{skipped:h&&h.reason?h.reason:"not_waiting"};let p=!1;o(async()=>{p=!0;try{await f("POST","/v1/asks/"+h.id+"/cancel",{reason:"cancelled_in_terminal"},800)}catch{}process.exit(0)});let m=Date.now(),g=0;for(;!p&&Date.now()-m<t;){let S;try{S=await f("GET","/v1/asks/"+h.id+"/wait?ms=20000",void 0,25e3),g=0}catch{if(++g>=3)return{gaveUp:"daemon_unreachable"};await new Promise(k=>setTimeout(k,1e3));continue}if(S.state==="answered"&&S.decision)return e.write(JSON.stringify(Bg(S.decision))+`
360
- `),{answered:S.decision.behavior};if(S.state!=="waiting")return{settled:S.state,reason:S.reason}}return{gaveUp:"timeout"}}Fl();xn();Ls();is();Cc();_t();eo();Pn();xe();js();Ls();eo();async function R0({sub:n,args:e=[],flags:t={},out:s=process.stdout,input:o=process.stdin,env:r=process.env,fetchImpl:i=(...a)=>fetch(...a)}={}){let a=hs(fs(s)),l=Z(),c=Q(l),u=c.summarizer||{},d=(t["ollama-url"]||u.provider==="ollama"&&u.baseUrl||pt).replace(/\/$/,""),f=(m="")=>s.write(m+`
361
- `),h=async m=>{Oe(l);let g=zc(yn(l)||{},{summarizer:m});Wc(l,g),f(a.green("✔")+" Summarizer is now "+a.bold(m.provider+(m.model?" / "+m.model:"")));let S=await dr(c.port,ve(l),i);if(S.up&&S.auth)try{await $c(c.port,ve(l),{summarizer:g.summarizer},i),f(a.green("✔")+" Running daemon switched over")}catch(k){f(a.yellow("!")+" Daemon didn't take it ("+k.message+"); restart it")}},p=async()=>{let m=String(t["cloud-url"]||u.provider==="ollama-cloud"&&u.baseUrl||r.CHECKPOINTS_CLOUD_URL||Ds).replace(/\/$/,""),g=await Wr({baseUrl:m,fetchImpl:i}),S=An("ollama-cloud",{apiKeyEnv:u.provider==="ollama-cloud"?u.apiKeyEnv:null,apiKeyFile:u.provider==="ollama-cloud"?u.apiKeyFile:null,root:l},r);if(f(a.bold("Ollama cloud")+a.dim(" · "+m+" · "+(S?"API key found":"no key: set OLLAMA_API_KEY (ollama.com/settings/keys)"))),!g.ok){f(a.dim(" couldn't fetch the model list ("+g.error+")"));return}let k=u.provider==="ollama-cloud"&&u.model||null;for(let w of Fa(g.models,{current:k})){let x=w.current?a.cyan("●"):w.available===!1?a.dim("×"):a.dim("○");f(" "+x+" "+w.tag.padEnd(22)+a.dim(w.note||"")+(w.recommended?a.yellow(" ★"):"")+(w.current?a.cyan(" (in use)"):"")+(w.available===!1?a.dim(" (not offered today)"):""))}f(a.dim(" `leftoff models use <tag> --cloud` switches · nothing to download"))};if(n==="cloud")return await p(),0;if(!n||n==="list"||n==="ls"){f(a.bold("Summarizer: ")+(u.provider||"auto")+(u.model?" / "+u.model:"")+(u.baseUrl?a.dim(" @ "+u.baseUrl):""));let m=await Gt({baseUrl:d,fetchImpl:i,env:r});if(f(),m.running){f(a.bold("Ollama")+a.dim(" · "+m.baseUrl+(m.version?" · v"+m.version:"")+" · sizes from ollama.com/library, "+_o));let S=u.provider==="ollama"?u.model:null;for(let k of Eo(m.models,{current:S})){let w=k.current?a.cyan("●"):k.installed?a.green("✔"):a.dim("○"),x=(k.recommended?a.yellow(" ★"):"")+(k.current?a.cyan(" (in use)"):""),v=k.curated?Ua(k,Ht)+" · "+k.note:k.sizeBytes?Ht(k.sizeBytes):"";f(" "+w+" "+k.tag.padEnd(16)+a.dim(v)+x)}f(a.dim(" ✔ pulled ○ not pulled · `leftoff models use <tag>` pulls and switches"))}else f(a.bold("Ollama")+": "+(m.binary?"installed, not running (`ollama serve`)":"not installed · https://ollama.com/download"));let g=await Tn({baseUrl:t["lmstudio-url"]||u.provider==="openai-compatible"&&u.baseUrl||rn,fetchImpl:i});if(f(),g.running){f(a.bold("LM Studio / OpenAI-compatible")+a.dim(" · "+g.baseUrl));for(let S of g.models)f(" "+(u.provider==="openai-compatible"&&u.model===S?a.cyan("●"):a.green("✔"))+" "+S);g.models.length||f(a.dim(" no model loaded"))}else f(a.bold("LM Studio")+": not running at "+g.baseUrl);return t.local||(f(),await p()),0}if(n==="pull"||n==="use"){let m=e[0];if(!m)throw new Error("usage: leftoff models "+n+" <tag>");if(n==="pull"&&(t.cloud||t.provider==="ollama-cloud"))throw new Error("cloud models run on ollama.com; nothing to pull. Use: leftoff models use "+m+" --cloud");if(n==="use"&&(t.cloud||t.provider==="ollama-cloud")){let k=String(t["base-url"]||Ds).replace(/\/$/,""),w=await Wr({baseUrl:k,fetchImpl:i});w.ok&&!w.models.some(y=>We(y,m))&&f(a.yellow("!")+" "+m+" isn't in ollama.com's cloud list right now ("+w.models.slice(0,5).join(", ")+", …); using it anyway");let x=u.provider==="ollama-cloud"?u:{},v={provider:"ollama-cloud",model:m,baseUrl:k===Ds?null:k,apiKeyEnv:x.apiKeyEnv||(t["api-key-env"]?String(t["api-key-env"]):null),...x.apiKeyFile?{apiKeyFile:x.apiKeyFile}:{}},b=An("ollama-cloud",{apiKeyEnv:v.apiKeyEnv,apiKeyFile:v.apiKeyFile,root:l},r);if(b||f(a.yellow("!")+" No key found: export "+(v.apiKeyEnv||"OLLAMA_API_KEY")+" (from ollama.com/settings/keys) or run `leftoff setup` to save one"),await h(v),!t["no-test"]&&b){let y=it("Writing a sample checkpoint with "+m+" on ollama.com…",{out:s,colors:a}),_=await Ps(v,{fetchImpl:i,root:l});return _.ok?y.succeed("It works."):y.fail("The test didn't work."),ro(s,a,_,m+" (cloud)"),_.ok?0:1}return 0}if(n==="use"&&(t.lmstudio||t.provider==="openai-compatible")){let k=(t["base-url"]||rn).replace(/\/$/,""),w=await Tn({baseUrl:k,fetchImpl:i});if(!w.running)throw new Error("nothing answers at "+k+"/models");if(!w.models.includes(m))throw new Error(m+" isn't loaded there (loaded: "+(w.models.join(", ")||"none")+")");let x={provider:"openai-compatible",model:m,baseUrl:k===rn?null:k};return await h(x),t["no-test"]||ro(s,a,await Ps(x,{fetchImpl:i}),m),0}let g=await Gt({baseUrl:d,fetchImpl:i,env:r});if(!g.running)throw new Error("Ollama isn't running at "+d+" (start it with `ollama serve`)");if(g.models.some(k=>We(k.name,m)))f(a.green("✔")+" "+m+" is already pulled");else{let k=Tc("pull "+m,{out:s,colors:a}),w=Date.now();try{await Io({tag:m,baseUrl:g.baseUrl,fetchImpl:i,onProgress:x=>k.update(x.fraction,x.total?Ht(x.completed)+" / "+Ht(x.total):x.status)})}catch(x){return k.fail(x.message),1}k.done("Pulled "+m+a.dim(" in "+Ui(Date.now()-w)))}if(n==="use"){let k={provider:"ollama",model:m,baseUrl:g.baseUrl===pt?null:g.baseUrl};if(await h(k),!t["no-test"]){let w=it("Writing a sample checkpoint with "+m+"…",{out:s,colors:a}),x=await Ps(k,{fetchImpl:i});return x.ok?w.succeed("It works."):w.fail("The test didn't work."),ro(s,a,x,m),x.ok?0:1}}return 0}if(n==="test"){let m=it("Writing a sample checkpoint with "+(u.provider||"auto")+(u.model?" / "+u.model:"")+"…",{out:s,colors:a}),g=await Ps(u,{fetchImpl:i,root:l});return g.ok?m.succeed("It works."):m.fail("The test didn't work."),ro(s,a,g,u.model||u.provider),g.ok?0:1}if(n==="pick"){let m=new wt({input:o,output:s,yes:!!t.yes,colors:a}),S={p:m,u:{c:a,line:f,ok:k=>f(a.green("✔")+" "+k),warn:k=>f(a.yellow("!")+" "+k),fail:k=>f(a.red("✖")+" "+k),info:k=>f(a.dim("·")+" "+k),note:k=>f(" "+a.dim(k)),cmd:k=>f(" "+a.cyan("$ "+k))},out:s,flags:t,env:r,fetchImpl:i,yes:!!t.yes,currentModel:u.model};try{let k=await qc(S);return k?(t["no-test"]||await fr(S,k,k.model),await h(k),0):(f("Nothing changed."),1)}catch(k){if(k.cancelled)return f(a.red("✖")+" Cancelled; nothing changed."),130;throw k}finally{m.close()}}throw new Error("unknown: leftoff models "+n+" (list, cloud, pull <tag>, use <tag> [--cloud|--lmstudio], test, pick)")}jf();xe();fi();wl();Cc();is();Pn();is();_t();import St from"node:fs";import Dt from"node:path";import{spawn as BA,spawnSync as tu}from"node:child_process";import uh from"node:fs";import P0 from"node:os";import ta from"node:path";var dh=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"],UA=n=>String(n).replace(/\x1b\[[0-9;]*[A-Za-z]/g,"");function HA(n=process.env){return ta.join(n.XDG_CONFIG_HOME||ta.join(n.HOME||P0.homedir(),".config"),"checkpoints","logs")}function j0(n=process.env,e=new Date){let t=e.getFullYear()+"-"+String(e.getMonth()+1).padStart(2,"0")+"-"+String(e.getDate()).padStart(2,"0");return ta.join(HA(n),"upgrade-"+t+".log")}var fh=(n,e=process.env)=>{let t=e.HOME||P0.homedir();return t&&n.startsWith(t+ta.sep)?"~"+n.slice(t.length):n};function F0({out:n=process.stdout,quiet:e=!0,c:t,logFile:s,tty:o=!!n.isTTY,env:r=process.env}){try{uh.mkdirSync(ta.dirname(s),{recursive:!0})}catch{}let i=(h="")=>{try{uh.appendFileSync(s,UA(h)+`
359
+ `),a.add(y),n.boardOpened&&n.boardOpened(),b.on("close",()=>{a.delete(y),n.boardClosed&&n.boardClosed()});return}return pn(y,404,"not_found","no route for "+b.method+" "+E)}catch(_){return _.status?pn(y,_.status,_.code||"error",_.message):(n.log("500: "+(_.stack||_.message)),pn(y,500,"internal",_.message))}});return v.on("close",()=>{clearInterval(x);for(let b of a)b.end()}),{server:v,listen:()=>new Promise((b,y)=>{v.once("error",y),v.listen(t,s,()=>b(v))}),close:()=>new Promise(b=>{for(let y of a)y.end();v.close(()=>b()),v.closeAllConnections&&v.closeAllConnections()})}}var ft,M0,PA,jA,FA,mn,R0,HA,Gf=J(()=>{xe();yi();Cf();sl();Te();Vl();Fi();Lo();xe();Ot();to();cs();vo();fh();ft={state:"idle",lines:[],error:null,startedAt:null,finishedAt:null,stage:null};M0=bs.join(bs.dirname(MA(import.meta.url)),"..","ui"),PA=1024*1024,jA={".html":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"text/javascript; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".ico":"image/x-icon",".webmanifest":"application/manifest+json; charset=utf-8",".json":"application/json; charset=utf-8"},FA="default-src 'self'; style-src 'self'; script-src 'self'; img-src 'self' data:; connect-src 'self'; manifest-src 'self'; worker-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'";mn=n=>String(n).replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[e]);R0=n=>n==="~"?process.env.HOME||"":n.startsWith("~/")?bs.join(process.env.HOME||"",n.slice(2)):n;HA=["x-forwarded-for","x-forwarded-host","x-forwarded-proto","forwarded","x-real-ip","tailscale-user-login","tailscale-user-name","tailscale-headers-info"]});for(let[n,e]of Object.entries(process.env))n.startsWith("LEFTOFF_")&&n.length>8&&e!==void 0&&(process.env["CHECKPOINTS_"+n.slice(8)]=e);xe();Cf();Gf();import Qt from"node:fs";import Mh from"node:os";import As from"node:path";import{fileURLToPath as ET}from"node:url";xe();Tn();Te();yi();import{execFileSync as BA}from"node:child_process";function GA({start:n=process.ppid,ps:e=KA,maxDepth:t=6,now:s=Date.now()}={}){let o=n;for(let r=0;r<t&&o>1;r++){let i=e(o);if(!i)return null;if(/claude/i.test(i.comm))return sp(o+" "+i.tty+" "+i.etime,s);o=i.ppid}return null}function KA(n){try{let t=BA("ps",["-o","ppid=,tty=,etime=,comm=","-p",String(n)],{encoding:"utf8",env:{...process.env,LC_ALL:"C"},timeout:1e3}).trim().match(/^(\d+)\s+(\S+)\s+(\S+)\s+(.+)$/);return t?{ppid:Number(t[1]),tty:t[2],etime:t[3],comm:t[4]}:null}catch{return null}}function $A(n=process.env){let e={};for(let t of np)n[t]&&(e[t]=n[t]);return e}function zA(n={},e=process.env){let t=typeof n.model=="string"?n.model:n.model&&n.model.id||null;return{termEnv:$A(e),backend:Yh(e,{model:t})}}async function F0(n=2e3){return process.stdin.isTTY?"":new Promise(e=>{let t="",s=setTimeout(()=>e(t),n);process.stdin.setEncoding("utf8"),process.stdin.on("data",o=>t+=o),process.stdin.on("end",()=>{clearTimeout(s),e(t)}),process.stdin.on("error",()=>{clearTimeout(s),e(t)})})}async function U0(n,{stdinText:e,timeoutMs:t=800,env:s=process.env}={}){if(s.CHECKPOINTS_INTERNAL)return{skipped:"internal"};let o=Z(),r=e??await F0(),i;try{i=JSON.parse(r||"{}")}catch{return{skipped:"bad_json"}}let a=n||i.hook_event_name;if(!a||!i.session_id)return{skipped:"no_event"};if(a==="SessionStart"&&!i._checkpoints){let d=null;try{d=GA()}catch{}i._checkpoints={proc:d,...zA(i,s)}}let l=Q(o),c=ve(o),u=new Date().toISOString();try{let d=await fetch("http://127.0.0.1:"+l.port+"/v1/hooks/"+encodeURIComponent(a),{method:"POST",headers:{"content-type":"application/json",authorization:"Bearer "+(c||"")},body:JSON.stringify(i),signal:AbortSignal.timeout(t)});if(d.ok)return{posted:!0};if(d.status===400||d.status===404)return{rejected:d.status};throw new Error("status "+d.status)}catch{try{return Zp(o,de(l),i.session_id,{hook:a,payload:i,at:u}),{spooled:!0}}catch(d){return{lost:d.message}}}}async function H0({stdinText:n,out:e=process.stdout,maxMs:t=9.5*60*1e3,fetchImpl:s=fetch,onSignal:o=r=>{process.once("SIGTERM",r),process.once("SIGINT",r),process.once("SIGHUP",r)}}={}){if(process.env.CHECKPOINTS_INTERNAL)return{skipped:"internal"};let r=Z(),i=n??await F0(),a;try{a=JSON.parse(i||"{}")}catch{return{skipped:"bad_json"}}if(!a.session_id||!a.tool_name)return{skipped:"no_event"};let l=Q(r);if(l.answers&&l.answers.enabled===!1)return{skipped:"off"};let c=ve(r),u="http://127.0.0.1:"+l.port,d={"content-type":"application/json",authorization:"Bearer "+(c||"")},f=async(S,k,w,x)=>{let v=await s(u+k,{method:S,headers:d,body:w===void 0?void 0:JSON.stringify(w),signal:AbortSignal.timeout(x)});if(!v.ok)throw new Error("status "+v.status);return v.json()},h;try{h=await f("POST","/v1/asks",{sessionId:a.session_id,tool:a.tool_name,input:a.tool_input,suggestions:a.permission_suggestions,capture:"hooks",cwd:a.cwd,permissionMode:a.permission_mode},1500)}catch{return{skipped:"daemon_down"}}if(!h||!h.id||h.state!=="waiting")return{skipped:h&&h.reason?h.reason:"not_waiting"};let p=!1;o(async()=>{p=!0;try{await f("POST","/v1/asks/"+h.id+"/cancel",{reason:"cancelled_in_terminal"},800)}catch{}process.exit(0)});let m=Date.now(),g=0;for(;!p&&Date.now()-m<t;){let S;try{S=await f("GET","/v1/asks/"+h.id+"/wait?ms=20000",void 0,25e3),g=0}catch{if(++g>=3)return{gaveUp:"daemon_unreachable"};await new Promise(k=>setTimeout(k,1e3));continue}if(S.state==="answered"&&S.decision)return e.write(JSON.stringify(Yg(S.decision))+`
360
+ `),{answered:S.decision.behavior};if(S.state!=="waiting")return{settled:S.state,reason:S.reason}}return{gaveUp:"timeout"}}Hl();Tn();Ps();cs();Ic();Ot();no();Fn();xe();Hs();Ps();no();async function B0({sub:n,args:e=[],flags:t={},out:s=process.stdout,input:o=process.stdin,env:r=process.env,fetchImpl:i=(...a)=>fetch(...a)}={}){let a=gs(ms(s)),l=Z(),c=Q(l),u=c.summarizer||{},d=(t["ollama-url"]||u.provider==="ollama"&&u.baseUrl||gt).replace(/\/$/,""),f=(m="")=>s.write(m+`
361
+ `),h=async m=>{Oe(l);let g=Jc(Sn(l)||{},{summarizer:m});qc(l,g),f(a.green("✔")+" Summarizer is now "+a.bold(m.provider+(m.model?" / "+m.model:"")));let S=await dr(c.port,ve(l),i);if(S.up&&S.auth)try{await Yc(c.port,ve(l),{summarizer:g.summarizer},i),f(a.green("✔")+" Running daemon switched over")}catch(k){f(a.yellow("!")+" Daemon didn't take it ("+k.message+"); restart it")}},p=async()=>{let m=String(t["cloud-url"]||u.provider==="ollama-cloud"&&u.baseUrl||r.CHECKPOINTS_CLOUD_URL||js).replace(/\/$/,""),g=await Jr({baseUrl:m,fetchImpl:i}),S=_n("ollama-cloud",{apiKeyEnv:u.provider==="ollama-cloud"?u.apiKeyEnv:null,apiKeyFile:u.provider==="ollama-cloud"?u.apiKeyFile:null,root:l},r);if(f(a.bold("Ollama cloud")+a.dim(" · "+m+" · "+(S?"API key found":"no key: set OLLAMA_API_KEY (ollama.com/settings/keys)"))),!g.ok){f(a.dim(" couldn't fetch the model list ("+g.error+")"));return}let k=u.provider==="ollama-cloud"&&u.model||null;for(let w of Ba(g.models,{current:k})){let x=w.current?a.cyan("●"):w.available===!1?a.dim("×"):a.dim("○");f(" "+x+" "+w.tag.padEnd(22)+a.dim(w.note||"")+(w.recommended?a.yellow(" ★"):"")+(w.current?a.cyan(" (in use)"):"")+(w.available===!1?a.dim(" (not offered today)"):""))}f(a.dim(" `leftoff models use <tag> --cloud` switches · nothing to download"))};if(n==="cloud")return await p(),0;if(!n||n==="list"||n==="ls"){f(a.bold("Summarizer: ")+(u.provider||"auto")+(u.model?" / "+u.model:"")+(u.baseUrl?a.dim(" @ "+u.baseUrl):""));let m=await $t({baseUrl:d,fetchImpl:i,env:r});if(f(),m.running){f(a.bold("Ollama")+a.dim(" · "+m.baseUrl+(m.version?" · v"+m.version:"")+" · sizes from ollama.com/library, "+Oo));let S=u.provider==="ollama"?u.model:null;for(let k of Co(m.models,{current:S})){let w=k.current?a.cyan("●"):k.installed?a.green("✔"):a.dim("○"),x=(k.recommended?a.yellow(" ★"):"")+(k.current?a.cyan(" (in use)"):""),v=k.curated?Ga(k,Gt)+" · "+k.note:k.sizeBytes?Gt(k.sizeBytes):"";f(" "+w+" "+k.tag.padEnd(16)+a.dim(v)+x)}f(a.dim(" ✔ pulled ○ not pulled · `leftoff models use <tag>` pulls and switches"))}else f(a.bold("Ollama")+": "+(m.binary?"installed, not running (`ollama serve`)":"not installed · https://ollama.com/download"));let g=await En({baseUrl:t["lmstudio-url"]||u.provider==="openai-compatible"&&u.baseUrl||ln,fetchImpl:i});if(f(),g.running){f(a.bold("LM Studio / OpenAI-compatible")+a.dim(" · "+g.baseUrl));for(let S of g.models)f(" "+(u.provider==="openai-compatible"&&u.model===S?a.cyan("●"):a.green("✔"))+" "+S);g.models.length||f(a.dim(" no model loaded"))}else f(a.bold("LM Studio")+": not running at "+g.baseUrl);return t.local||(f(),await p()),0}if(n==="pull"||n==="use"){let m=e[0];if(!m)throw new Error("usage: leftoff models "+n+" <tag>");if(n==="pull"&&(t.cloud||t.provider==="ollama-cloud"))throw new Error("cloud models run on ollama.com; nothing to pull. Use: leftoff models use "+m+" --cloud");if(n==="use"&&(t.cloud||t.provider==="ollama-cloud")){let k=String(t["base-url"]||js).replace(/\/$/,""),w=await Jr({baseUrl:k,fetchImpl:i});w.ok&&!w.models.some(y=>Je(y,m))&&f(a.yellow("!")+" "+m+" isn't in ollama.com's cloud list right now ("+w.models.slice(0,5).join(", ")+", …); using it anyway");let x=u.provider==="ollama-cloud"?u:{},v={provider:"ollama-cloud",model:m,baseUrl:k===js?null:k,apiKeyEnv:x.apiKeyEnv||(t["api-key-env"]?String(t["api-key-env"]):null),...x.apiKeyFile?{apiKeyFile:x.apiKeyFile}:{}},b=_n("ollama-cloud",{apiKeyEnv:v.apiKeyEnv,apiKeyFile:v.apiKeyFile,root:l},r);if(b||f(a.yellow("!")+" No key found: export "+(v.apiKeyEnv||"OLLAMA_API_KEY")+" (from ollama.com/settings/keys) or run `leftoff setup` to save one"),await h(v),!t["no-test"]&&b){let y=lt("Writing a sample checkpoint with "+m+" on ollama.com…",{out:s,colors:a}),_=await Us(v,{fetchImpl:i,root:l});return _.ok?y.succeed("It works."):y.fail("The test didn't work."),ao(s,a,_,m+" (cloud)"),_.ok?0:1}return 0}if(n==="use"&&(t.lmstudio||t.provider==="openai-compatible")){let k=(t["base-url"]||ln).replace(/\/$/,""),w=await En({baseUrl:k,fetchImpl:i});if(!w.running)throw new Error("nothing answers at "+k+"/models");if(!w.models.includes(m))throw new Error(m+" isn't loaded there (loaded: "+(w.models.join(", ")||"none")+")");let x={provider:"openai-compatible",model:m,baseUrl:k===ln?null:k};return await h(x),t["no-test"]||ao(s,a,await Us(x,{fetchImpl:i}),m),0}let g=await $t({baseUrl:d,fetchImpl:i,env:r});if(!g.running)throw new Error("Ollama isn't running at "+d+" (start it with `ollama serve`)");if(g.models.some(k=>Je(k.name,m)))f(a.green("✔")+" "+m+" is already pulled");else{let k=Ec("pull "+m,{out:s,colors:a}),w=Date.now();try{await Mo({tag:m,baseUrl:g.baseUrl,fetchImpl:i,onProgress:x=>k.update(x.fraction,x.total?Gt(x.completed)+" / "+Gt(x.total):x.status)})}catch(x){return k.fail(x.message),1}k.done("Pulled "+m+a.dim(" in "+Gi(Date.now()-w)))}if(n==="use"){let k={provider:"ollama",model:m,baseUrl:g.baseUrl===gt?null:g.baseUrl};if(await h(k),!t["no-test"]){let w=lt("Writing a sample checkpoint with "+m+"…",{out:s,colors:a}),x=await Us(k,{fetchImpl:i});return x.ok?w.succeed("It works."):w.fail("The test didn't work."),ao(s,a,x,m),x.ok?0:1}}return 0}if(n==="test"){let m=lt("Writing a sample checkpoint with "+(u.provider||"auto")+(u.model?" / "+u.model:"")+"…",{out:s,colors:a}),g=await Us(u,{fetchImpl:i,root:l});return g.ok?m.succeed("It works."):m.fail("The test didn't work."),ao(s,a,g,u.model||u.provider),g.ok?0:1}if(n==="pick"){let m=new kt({input:o,output:s,yes:!!t.yes,colors:a}),S={p:m,u:{c:a,line:f,ok:k=>f(a.green("✔")+" "+k),warn:k=>f(a.yellow("!")+" "+k),fail:k=>f(a.red("✖")+" "+k),info:k=>f(a.dim("·")+" "+k),note:k=>f(" "+a.dim(k)),cmd:k=>f(" "+a.cyan("$ "+k))},out:s,flags:t,env:r,fetchImpl:i,yes:!!t.yes,currentModel:u.model};try{let k=await Xc(S);return k?(t["no-test"]||await fr(S,k,k.model),await h(k),0):(f("Nothing changed."),1)}catch(k){if(k.cancelled)return f(a.red("✖")+" Cancelled; nothing changed."),130;throw k}finally{m.close()}}throw new Error("unknown: leftoff models "+n+" (list, cloud, pull <tag>, use <tag> [--cloud|--lmstudio], test, pick)")}Bf();xe();pi();kl();Ic();cs();Fn();cs();Ot();import bt from"node:fs";import Pt from"node:path";import{spawn as JA,spawnSync as su}from"node:child_process";import ph from"node:fs";import G0 from"node:os";import oa from"node:path";var mh=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"],YA=n=>String(n).replace(/\x1b\[[0-9;]*[A-Za-z]/g,"");function WA(n=process.env){return oa.join(n.XDG_CONFIG_HOME||oa.join(n.HOME||G0.homedir(),".config"),"checkpoints","logs")}function K0(n=process.env,e=new Date){let t=e.getFullYear()+"-"+String(e.getMonth()+1).padStart(2,"0")+"-"+String(e.getDate()).padStart(2,"0");return oa.join(WA(n),"upgrade-"+t+".log")}var gh=(n,e=process.env)=>{let t=e.HOME||G0.homedir();return t&&n.startsWith(t+oa.sep)?"~"+n.slice(t.length):n};function $0({out:n=process.stdout,quiet:e=!0,c:t,logFile:s,tty:o=!!n.isTTY,env:r=process.env}){try{ph.mkdirSync(oa.dirname(s),{recursive:!0})}catch{}let i=(h="")=>{try{ph.appendFileSync(s,YA(h)+`
362
362
  `)}catch{}},a=h=>n.write(h),l=null,c="",u=0,d=()=>{l&&(clearInterval(l),l=null,a("\r\x1B[2K"))};return{quiet:e,logFile:s,log:i,say:(h="")=>{i(h),e||a(h+`
363
363
  `)},print:(h="")=>{d(),i(h),a(h+`
364
- `)},start(h){c=h,i("== "+h),!(!e||!o)&&(d(),u=0,a(t.cyan(dh[0])+" "+h),l=setInterval(()=>{u=(u+1)%dh.length,a("\r\x1B[2K"+t.cyan(dh[u])+" "+c)},80),l.unref&&l.unref())},done(h){d(),i("✓ "+h),e&&a(t.green("✓")+" "+h+`
364
+ `)},start(h){c=h,i("== "+h),!(!e||!o)&&(d(),u=0,a(t.cyan(mh[0])+" "+h),l=setInterval(()=>{u=(u+1)%mh.length,a("\r\x1B[2K"+t.cyan(mh[u])+" "+c)},80),l.unref&&l.unref())},done(h){d(),i("✓ "+h),e&&a(t.green("✓")+" "+h+`
365
365
  `)},warn(h){d(),i("! "+h),a(t.yellow("!")+" "+h+`
366
366
  `)},fail(h,{lines:p=30}={}){if(d(),i("✗ "+h),a(t.red("✗")+" "+h+`
367
- `),e){let m=[];try{m=uh.readFileSync(s,"utf8").trimEnd().split(`
367
+ `),e){let m=[];try{m=ph.readFileSync(s,"utf8").trimEnd().split(`
368
368
  `).slice(-p)}catch{}m.length&&a(t.dim(m.map(g=>" │ "+g).join(`
369
369
  `))+`
370
- `)}a(t.dim("Full log: "+fh(s,r))+`
371
- `)},stop:d}}os();var eu=n=>new Promise(e=>setTimeout(e,n)),Ss=n=>n?String(n).slice(0,7):"unknown",GA=[{to:"0.3.0",note:'adds "updates": { "check": true }',run:n=>n.updates?n:{...n,updates:{check:!0,source:null}}}];function KA(n,e,t=X){let s=n&&typeof n=="object"?JSON.parse(JSON.stringify(n)):{},o=[];for(let r of GA)if((!e||qe(r.to,e)>0)&&qe(r.to,t)<=0){let i=r.run(s);JSON.stringify(i)!==JSON.stringify(s)&&o.push(r),s=i}return{cfg:s,applied:o}}function $A(n){let e=ci.map(s=>Dt.join(n,s)).filter(s=>St.existsSync(Dt.join(s,"package.json")));if(!e.length)return Dt.join(n,"checkpoints");let t=s=>{try{return JSON.parse(St.readFileSync(Dt.join(s,"package.json"),"utf8")).version||"0.0.0"}catch{return"0.0.0"}};return e.sort((s,o)=>qe(t(o),t(s)))[0]}function H0(){return process.platform==="win32"?"npm.cmd":"npm"}function U0(n=process.env,e="npm"){let t=e==="pnpm"?process.platform==="win32"?"pnpm.cmd":"pnpm":H0(),s=tu(t,["root","-g"],{encoding:"utf8",env:n});return s.status===0?s.stdout.trim():null}async function YA(n){try{return Number(St.readFileSync(Dt.join(n,".daemon.pid"),"utf8"))||null}catch{return null}}async function hh(n,e){try{let t=await fetch("http://127.0.0.1:"+n+"/v1/health",{headers:{authorization:"Bearer "+(e||"")},signal:AbortSignal.timeout(1500)});return t.ok?{up:!0,...await t.json()}:{up:t.status===401}}catch{return{up:!1}}}async function zA({root:n,bin:e,env:t=process.env,say:s=()=>{},dryRun:o=!1}){let r=Q(n),i=ve(n),a=Nt(),l=!!(o||t.CHECKPOINTS_SERVICE_DRY_RUN),c=gc({root:n,bin:e,dryRun:l});if(c&&s("Login service renamed: stopped "+c.from+", wrote "+c.to+(l?" (dry run: nothing stopped)":"")),St.existsSync(a)){let d=Ln({root:wf(St.readFileSync(a,"utf8"))||n,bin:e});if(St.readFileSync(a,"utf8")!==d&&(St.writeFileSync(a,d),s("Login service now points at "+e)),l)return s("Would restart the login service: "+Xs().map(f=>f.join(" ")).join(" && ")),{how:"service",dryRun:!0,moved:c};for(let f of Xs()){let h=tu(f[0],f.slice(1),{encoding:"utf8"});if(h.status!==0&&f[1]!=="unload")return{how:"service",ok:!1,error:(h.stderr||"").trim().split(`
372
- `)[0]||"exit "+h.status}}}else{let d=await hh(r.port,i),f=await YA(n),h=m=>{try{return process.kill(m,0),!0}catch{return!1}};if(d.up&&f&&h(f)){try{process.kill(f,"SIGTERM")}catch{}for(let m=0;m<50&&h(f);m++)await eu(200);if(h(f)){try{process.kill(f,"SIGKILL")}catch{}await eu(200)}}else if(d.up)return{how:"unknown",ok:!1,error:"a daemon is running that setup didn't start (stop it and run `leftoff serve`)"};for(let m=0;m<25&&(await hh(r.port,i)).up;m++)await eu(200);let{startDaemonDetached:p}=await Promise.resolve().then(()=>(eo(),Ic));p({root:n,bin:e,env:t})}let u={up:!1};for(let d=0;d<60&&(u=await hh(r.port,i),!(u.up&&(!u.version||u.version===X)));d++)await eu(250);return{how:St.existsSync(a)?"service":"detached",ok:u.up&&(!u.version||u.version===X),version:u.version,moved:c}}async function B0({flags:n={},out:e=process.stdout}={}){let t=hs(fs(e)),s=jo();return n.since&&(s=Fo(s,String(n.since).replace(/^v/,""),null)),n.latest&&(s=s.slice(0,1)),e.write(t.bold(Ge+" v"+X)+t.dim(" · what changed")+`
373
-
374
- `+yl(s,{colors:t})),0}function ph(n,e,{env:t,log:s,timeoutMs:o=3e5}){return new Promise(r=>{s("$ "+n+" "+e.join(" "));let i="",a="",l=BA(n,e,{env:t,stdio:["ignore","pipe","pipe"]}),c=setTimeout(()=>l.kill("SIGKILL"),o);l.stdout.on("data",u=>{a+=u,s(String(u).trimEnd())}),l.stderr.on("data",u=>{i+=u,s(String(u).trimEnd())}),l.on("error",u=>{clearTimeout(c),r({status:-1,stderr:u.message,stdout:a})}),l.on("close",u=>{clearTimeout(c),r({status:u,stderr:i,stdout:a})})})}async function G0({flags:n={},out:e=process.stdout,env:t=process.env}={}){let s=hs(fs(e)),o=!n.verbose&&!n.check&&!(n.finish&&!n.quiet),r=F0({out:e,quiet:o,c:s,logFile:n.log?String(n.log):j0(t),tty:n.finish?t.LEFTOFF_PROGRESS_TTY==="1":!!e.isTTY&&!t.CI,env:t}),i=r.say,a=v=>i(s.green("✔")+" "+v),l=v=>{o?r.warn(WA(v)):i(s.yellow("!")+" "+v)},c=v=>o?r.fail(v):i(s.red("✖")+" "+v);if(n.finish||r.log(`
375
- # leftoff upgrade, `+new Date().toISOString()+(o?"":" (--verbose)")),n.finish)return VA({flags:n,out:e,env:t,c:s,say:i,ok:a,warn:l,fail:c,P:r});let u=await ui({env:t,globalRoot:t.CHECKPOINTS_NPM_GLOBAL_ROOT||void 0}),d=Ho(n.source||t.CHECKPOINTS_UPDATE_SOURCE||(Q().updates||{}).source||u.source||li);u.kind==="npm"&&(d="npm:"+ot),i(s.bold(Ge+" v"+u.version)+s.dim((u.kind==="npm"?" · installed from npm with "+u.pm+" at ":" ("+Ss(u.sha)+") · "+{clone:"a git clone at ",global:"installed globally at ",npx:"an npx copy at ",local:"installed at "}[u.kind])+u.repoDir)),r.start("Checking for updates…");let f=await di({info:u,source:d,env:t});if(f.error&&u.kind==="npm")return c("Couldn't reach the npm registry ("+kl(t)+"): "+f.error),1;if(f.error)return i(s.dim(" (git uses your own credentials; for a private repo, check `git ls-remote "+d+'` works, or set "updates": { "source": "git@github.com:'+st+'.git" } in config.json)')),c("Couldn't reach "+d+": "+f.error),1;if(f.source&&f.source!==d&&(i(s.dim("· reached GitHub over SSH ("+f.source+")")),d=f.source),!(f.available||!u.sha&&f.remote.version&&qe(f.remote.version,u.version)>0)&&!n.force)return o?r.done("Up to date: "+Ge+" "+u.version):a("Up to date"+s.dim(" (remote "+Ss(f.remote.sha)+(f.remote.version?", v"+f.remote.version:"")+")")),0;let p=f.remote.version||Ss(f.remote.sha);if(r.done("Found "+p+" (you have "+u.version+")"),i(s.bold(s.cyan("Update available: "))+(f.remote.version?"v"+u.version+" → v"+f.remote.version:"")+(u.kind==="npm"?"":s.dim(" "+Ss(u.sha)+" → "+Ss(f.remote.sha)))),f.incoming.length?i(`
376
- `+yl(f.incoming,{colors:s})):f.fetchError&&i(s.dim(" (couldn't read the new changelog: "+f.fetchError+")")),n.check)return i(s.dim("Run `leftoff upgrade` to install it.")),0;r.start("Downloading "+p+"…");let m=u.repoDir;if(u.kind==="npm"){let v=Rd(u.pm);i(s.dim("$ "+v.join(" ")));let b=await ph(v[0],v.slice(1),{env:t,log:r.log});if(b.status!==0)return o||i((b.stderr||"").trim()),c(v.slice(0,3).join(" ")+" failed: "+((b.stderr||"").trim().split(`
377
- `).pop()||"exit "+b.status)),1;let y=t.CHECKPOINTS_NPM_GLOBAL_ROOT||U0(t,u.pm);m=y&&St.existsSync(Dt.join(y,ot,"package.json"))?Dt.join(y,ot):u.repoDir,a("Installed "+ot+"@"+(f.remote.version||"latest")+s.dim(" → "+m))}else if(u.kind==="clone"){u.source&&zs(u.source)!==u.source&&(await Et(["remote","set-url","origin",zs(u.source)],{cwd:u.repoDir,env:t})).code===0&&i(s.dim("· the repo is now "+st+"; pointed origin at "+zs(u.source))),i(s.dim("$ git -C "+u.repoDir+" pull --ff-only"));let v=await Et(["pull","--ff-only","-q"],{cwd:u.repoDir,env:t,timeoutMs:12e4});if(v.code!==0)return i(v.stderr.trim()),i(s.dim(" Local changes? Commit or stash them and run `leftoff upgrade` again.")),c("git pull failed: "+(v.stderr.trim().split(`
378
- `).pop()||"exit "+v.code)),1;a("Pulled "+Ss(f.remote.sha))}else{let v=d.match(/^(?:https:\/\/github\.com\/|git@github\.com:)([\w.-]+\/[\w.-]+?)(?:\.git)?$/),b=v?"github:"+v[1]:d;i(s.dim("$ npm install -g "+b));let y=await ph(H0(),["install","-g",b,"--no-fund","--no-audit","--loglevel=error"],{env:t,log:r.log});if(y.status!==0)return o||i((y.stderr||"").trim()),c("npm install -g failed: "+((y.stderr||"").trim().split(`
379
- `).pop()||"exit "+y.status)),1;let _=t.CHECKPOINTS_NPM_GLOBAL_ROOT||U0(t);m=_?$A(_):u.repoDir,a("Installed "+b+s.dim(" → "+m));try{St.existsSync(Dt.join(m,".install.json"))||Dd({sha:f.remote.sha,source:d,version:f.remote.version||void 0},m)}catch{}}r.done("Downloaded "+p),r.start("Updating the Claude Code plugin…");let g=null,S=Ec(t);if(u.kind==="npm")try{Df({from:m,env:t})}catch(v){l("Couldn't refresh the mod's files: "+v.message)}if(!S)l("`claude` isn't on PATH; update the mod later with: claude plugin marketplace update "+un+" && claude plugin update "+Me);else{let v=Oc(S,t);if(!v.installed&&v.legacyInstalled){i(s.dim("$ claude plugin marketplace update "+un));let b=jn(S,["plugin","marketplace","update",un],{env:t});b.code!==0&&l("`claude plugin marketplace update "+un+"` failed: "+((b.stderr||b.stdout).trim().split(`
380
- `).pop()||"exit "+b.code))}else if(!v.installed)i(s.dim("· The mod isn't installed through `claude plugin`; nothing to update there.")),g="skip";else{let b=!1;for(let y of[["plugin","marketplace","update",un],["plugin","update",Me]]){i(s.dim("$ claude "+y.join(" ")));let _=await ph(S,y,{env:{...t,CHECKPOINTS_INTERNAL:"1",LEFTOFF_INTERNAL:"1"},log:r.log,timeoutMs:12e4});if(_.status!==0){b=!0,l("`claude "+y.join(" ")+"` failed: "+((_.stderr||_.stdout).trim().split(`
381
- `).pop()||"exit "+_.status));break}}a("Mod updated"+s.dim(" · restart open Claude Code sessions to load it")),b||(g="ok")}}o&&(g==="ok"?r.done("Updated the Claude Code plugin"):g==="skip"?r.done("Claude Code plugin: not installed with `claude plugin`, nothing to update"):r.stop());let w=[Dt.join(m,"bin","checkpoints.js"),"upgrade","--finish","--from="+u.version,"--from-sha="+(u.sha||""),"--repo="+m];if(n["no-restart"]&&w.push("--no-restart"),!o){let v=tu(process.execPath,w,{env:t,stdio:"inherit"});return v.status==null?1:v.status}w.push("--quiet","--log="+r.logFile);let x=tu(process.execPath,w,{env:{...t,LEFTOFF_PROGRESS_TTY:e.isTTY&&!t.CI?"1":"0"},stdio:["ignore","inherit","pipe"],encoding:"utf8"});return x.stderr&&r.log(x.stderr.trimEnd()),x.status!==0?(r.fail("Finishing the upgrade failed (exit "+x.status+")"),x.status==null?1:x.status):0}var WA=n=>String(n).replace(/\x1b\[2m.*?\x1b\[22m/g,"").trim();function JA(n,e=3){let t=n.map(o=>{let r=o.sections.flatMap(a=>a.items),i=r.filter(a=>/^\*\*/.test(a));return i.length?i:r}),s=[];for(let o=0;s.length<e&&t.some(r=>r.length>o);o++)for(let r of t){if(s.length>=e||!r[o])continue;let i=r[o].match(/^\*\*(.+?)\*\*/),a=(i?i[1]:r[o]).replace(/[*`]/g,"").replace(/\s+/g," ").trim().replace(/[.:]$/,"");a.length>72&&(a=a.slice(0,71).replace(/\s+\S*$/,"")+"…"),s.push(a)}return s}function qA(n,e,t=X){let s=[];for(let[o,r]of Object.entries(Vs(n)))o!==e&&r.valid&&r.card.version&&qe(r.card.version,t)<0&&s.push(o+" ("+r.card.version+")");return s.sort()}async function VA({flags:n,out:e,env:t,c:s,say:o,ok:r,warn:i,fail:a,P:l}){let c=l.quiet,u=String(n.from||""),d=String(n.repo||Qn),f=await ui({repoDir:d,env:t}),h=Z(),p=yn(h);if(p){let{cfg:k,applied:w}=KA(p,u,X);if(w.length){St.writeFileSync(be(h).config,JSON.stringify(k,null,2)+`
382
- `);for(let x of w)r("Config: "+x.note)}}let m=St.existsSync(Dt.join(d,"bin","leftoff.js"))?Dt.join(d,"bin","leftoff.js"):Dt.join(d,"bin","checkpoints.js");try{if(Ni()){let k=St.readFileSync(cn(),"utf8");er(void 0,{bin:m}),k.includes($s)&&r("Settings hooks now run `leftoff`"+s.dim(" (replaced the checkpoints-hook entries)"))}}catch(k){i("Couldn't refresh the settings hooks: "+k.message)}let g=Ec(t);if(g){let k;try{k=Rf({bin:g,env:t})}catch(w){k={ok:!1,error:w.message}}k.migrated&&k.ok?r("Mod renamed: "+ps+" → "+Me+(k.enabled?"":s.dim(" (kept disabled, as it was)"))+s.dim(" · restart open Claude Code sessions to load it")):k.ok||i("Couldn't move the mod to its new name: "+k.error+s.dim(" · run: claude plugin install "+Me+" && claude plugin uninstall "+ps))}if(!n["no-restart"]){l.start("Restarting the daemon…");let k=await zA({root:h,bin:m,env:t,say:w=>o(s.dim("· "+w))});if(k.dryRun)l.stop();else if(k.ok){r("Daemon restarted"+s.dim(" ("+k.how+", v"+(k.version||X)+")")),l.done("Restarted the daemon");try{let w=JSON.parse(St.readFileSync(Dt.join(h,".cleanup."+de(Q(h)).replace(/[^\w.-]/g,"_")+".json"),"utf8"));w.lastArchived&&Date.now()-Date.parse(w.lastArchivedAt)<5*6e4&&r("Archived "+w.lastArchived+" empty "+(w.lastArchived===1?"session":"sessions")+" with no folder or process"+s.dim(" (kept on disk, marked archived; see leftoff doctor)"))}catch{}}else i("Restart the daemon yourself: "+(k.error||"it didn't come back")+s.dim(" · leftoff serve"))}if(c){let k=Fo(jo(),u||null,X);l.print(""),l.print(s.bold(Ge+" "+(u?u+" → ":"")+X)+" "+s.green("✓"));for(let x of JA(k))l.print(" • "+x);l.print(s.dim("Full notes: leftoff changelog (also What’s new on the board) · log: "+fh(l.logFile,t)));let w=qA(h,de(Q(h)));return w.length&&l.print(s.yellow("!")+" Upgrade your other Mac"+(w.length>1?"s":"")+" too: "+w.join(", ")),0}o(),o(s.bold(Ge+" "+(u?"v"+u+" → ":"")+"v"+X)+(n["from-sha"]||f.sha?s.dim(" "+Ss(n["from-sha"])+" → "+Ss(f.sha)):""));let S=Fo(jo(),u||null,X);if(S.length&&o(`
383
- `+yl(S,{colors:s})),f.kind==="clone"&&n["from-sha"]){let k=await Et(["log","--format=%h %s",String(n["from-sha"])+"..HEAD"],{cwd:d,env:t});if(k.code===0&&k.stdout.trim()){o(s.bold("Commits"));for(let w of k.stdout.trim().split(`
384
- `).slice(0,30))o(" "+s.dim(w.slice(0,7))+w.slice(7))}}return o(s.dim("The board shows these under What's new. `leftoff changelog` prints them again.")),0}xe();Jl();Ri();_t();import nu from"node:fs";import K0 from"node:path";import XA from"node:https";import{spawnSync as ZA}from"node:child_process";var mh="/Applications/Tailscale.app/Contents/MacOS/Tailscale";function q0({env:n=process.env,platform:e=process.platform,exists:t=s=>{try{return nu.accessSync(s,nu.constants.X_OK),nu.statSync(s).isFile()}catch{return!1}}}={}){if(n.CHECKPOINTS_TAILSCALE)return t(n.CHECKPOINTS_TAILSCALE)?n.CHECKPOINTS_TAILSCALE:null;let s=e==="win32"?["tailscale.exe"]:["tailscale"];for(let o of String(n.PATH||"").split(K0.delimiter).filter(Boolean))for(let r of s){let i=K0.join(o,r);if(t(i))return i}return e==="darwin"&&t(mh)?mh:null}function V0(n,e,{inherit:t=!1,timeoutMs:s=15e3}={}){let o=ZA(n,e,{encoding:"utf8",stdio:t?"inherit":["ignore","pipe","pipe"],timeout:t?void 0:s});return{code:o.status==null?o.error?127:1:o.status,stdout:o.stdout||"",stderr:(o.stderr||"")+(o.error?String(o.error.message):"")}}function gh(n){let e;try{e=typeof n=="string"?JSON.parse(n):n}catch{return null}if(!e||typeof e!="object")return null;let t=e.Self||{},s=String(t.DNSName||"").replace(/\.$/,"").toLowerCase()||null;return{state:e.BackendState||null,dnsName:s,hostName:t.HostName||null,online:t.Online!==!1,magicDNSSuffix:e.MagicDNSSuffix||null}}function yh(n,e,t=443){let s;try{s=typeof n=="string"?n.trim()?JSON.parse(n):{}:n}catch{return{parsed:!1}}s=s||{};let o=e+":"+t,r=(s.Web||{})[o],i=r&&r.Handlers&&r.Handlers["/"];return{parsed:!0,proxy:i&&i.Proxy||null,other:i?!i.Proxy:!1,funnel:!!(s.AllowFunnel&&s.AllowFunnel[o]),any:!!r}}var QA=n=>"http://127.0.0.1:"+n,wh=(n,e)=>!!n&&new RegExp("^(https?://)?(127\\.0\\.0\\.1|localhost):"+e+"/?$").test(n)||String(n)===String(e);async function Sh(n,e,t,s,o){try{let r=await fetch("http://127.0.0.1:"+n+s,{method:t,headers:{authorization:"Bearer "+(e||""),"content-type":"application/json"},body:o?JSON.stringify(o):void 0,signal:AbortSignal.timeout(3e3)}),i=await r.json().catch(()=>({}));return r.ok?{up:!0,ok:!0,body:i}:{up:!0,ok:!1,status:r.status,body:i}}catch{return{up:!1}}}async function $0({root:n,port:e,token:t,add:s=[],remove:o=[],requireToken:r}){let i=await Sh(e,t,"POST","/v1/remote/hosts",{add:s,remove:o,requireToken:r});if(i.ok)return{live:!0,hosts:i.body.hosts};let a=yn(n)||{},l=new Set(o.map(Ut)),c=(a.remoteHosts||[]).map(Ut).filter(d=>d&&!l.has(d));for(let d of s.map(Ut))d&&!c.includes(d)&&c.push(d);a.remoteHosts=c,typeof r=="boolean"&&(a.remoteRequireToken=r),po(a,n);let u=!1;return i.up&&(u=!!(await Sh(e,t,"POST","/v1/remote/reload")).ok),{live:u,daemonUp:!!i.up,status:i.status||null,hosts:c}}function Y0(n,{timeoutMs:e=45e3,env:t=process.env}={}){return new Promise(s=>{let o;try{o=new URL("v1/remote/ping",n)}catch{return s({ok:!1,kind:"bad_url",message:"not a URL: "+n})}let r=t.CHECKPOINTS_REMOTE_CHECK_ADDR?t.CHECKPOINTS_REMOTE_CHECK_ADDR.split(":"):null,i={method:"GET",hostname:o.hostname,port:o.port||443,path:o.pathname,servername:o.hostname,headers:{host:o.host,accept:"application/json"},timeout:e};if(r&&(i.hostname=r[0],i.port=Number(r[1])),t.CHECKPOINTS_REMOTE_CHECK_CA)try{i.ca=nu.readFileSync(t.CHECKPOINTS_REMOTE_CHECK_CA)}catch{}let a=Date.now(),l=XA.request(i,c=>{let u="";c.setEncoding("utf8"),c.on("data",d=>{u.length<2e4&&(u+=d)}),c.on("end",()=>{let d=null;try{d=JSON.parse(u)}catch{}let f=Date.now()-a;if(c.statusCode===200&&d&&d.app===Ge)return s({ok:!0,ms:f,version:d.version,via:d.via});if(c.statusCode===200&&d&&d.ok&&d.app)return s({ok:!1,kind:"other_app",message:"something else answers there ("+d.app+")"});let h=d&&d.error&&d.error.code||c.headers["x-leftoff-error"]||null;if(h==="bad_host")return s({ok:!1,kind:"bad_host",message:"the daemon refused the tailnet name (403 bad_host): it isn't in remoteHosts of the running daemon"});if(h==="funnel_refused")return s({ok:!1,kind:"funnel",message:"the request came through Tailscale Funnel"});if(c.statusCode===404&&h==="not_found")return s({ok:!1,kind:"old_daemon",message:"the running daemon is older than v0.4.6 (restart it: leftoff open)"});if(c.statusCode===502||c.statusCode===503)return s({ok:!1,kind:"daemon_down",message:"Tailscale answered but couldn't reach the daemon on this machine (HTTP "+c.statusCode+"): is it running? leftoff open"});s({ok:!1,kind:"http",message:"HTTP "+c.statusCode+(h?" "+h:"")})})});l.on("timeout",()=>{l.destroy(Object.assign(new Error("timed out after "+Math.round(e/1e3)+" s"),{code:"ETIMEDOUT"}))}),l.on("error",c=>{let u=String(c.code||"");if(u==="ENOTFOUND"||u==="EAI_AGAIN")return s({ok:!1,kind:"dns",message:"this Mac can't resolve "+o.hostname+' (MagicDNS off, or "Use Tailscale DNS" unticked in the Tailscale menu)'});if(/CERT|SELF_SIGNED|UNABLE_TO_VERIFY|ERR_TLS|EPROTO|ECONNRESET/.test(u)||/certificate|handshake|socket hang up/i.test(c.message))return s({ok:!1,kind:"tls",message:"no valid HTTPS certificate yet ("+(u||c.message)+"). Turn on HTTPS certificates for the tailnet (https://login.tailscale.com/admin/dns); the first one can take a minute to issue"});if(u==="ETIMEDOUT")return s({ok:!1,kind:"timeout",message:c.message+" (Tailscale may still be fetching the certificate; try leftoff tailscale status in a minute)"});if(u==="ECONNREFUSED")return s({ok:!1,kind:"refused",message:"nothing listens on "+o.host+" (tailscale serve isn't running)"});s({ok:!1,kind:"error",message:u?u+": "+c.message:c.message})}),l.end()})}function z0(n,e){return n.ok?"✓ checked "+e+" end to end from this machine: "+Ge+" "+(n.version?"v"+n.version+" ":"")+"answered in "+(n.ms<1e3?n.ms+" ms":(n.ms/1e3).toFixed(1)+" s")+" (MagicDNS, HTTPS certificate, Serve and the daemon are all fine)":"! couldn't open "+e+" from this machine: "+n.message}function W0(n={},e=process.stdout){return n["no-qr"]||n.qr==="false"||n.qr==="0"?!1:n.qr?!0:!!e.isTTY}function J0(n,{base:e,link:t,code:s,expiresAt:o,qr:r,color:i=!0}){let a=Math.max(1,Math.round((Date.parse(o)-Date.now())/6e4)),l=new Date(o).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"});n("Board on your tailnet: "+e),r?(n(""),n("Scan this with your iPhone camera (Tailscale has to be on there) to open the board signed in:"),n(""),n(bc(t,{color:i})),n("")):n("Open this once on your phone to sign that browser in:"),n(" "+t),s&&n("Home-screen app? It has its own cookies, so type this code in it instead: "+$l(s)),n("The link and the code each work once, until "+l+" ("+a+" min). Another device later: leftoff link")}var su="Tailscale Funnel (public internet) is intentionally unsupported: the board stays inside your tailnet.";function na(){return`leftoff tailscale — view the board from your phone or other machines on your tailnet
370
+ `)}a(t.dim("Full log: "+gh(s,r))+`
371
+ `)},stop:d}}as();var nu=n=>new Promise(e=>setTimeout(e,n)),vs=n=>n?String(n).slice(0,7):"unknown",qA=[{to:"0.3.0",note:'adds "updates": { "check": true }',run:n=>n.updates?n:{...n,updates:{check:!0,source:null}}}];function VA(n,e,t=X){let s=n&&typeof n=="object"?JSON.parse(JSON.stringify(n)):{},o=[];for(let r of qA)if((!e||Xe(r.to,e)>0)&&Xe(r.to,t)<=0){let i=r.run(s);JSON.stringify(i)!==JSON.stringify(s)&&o.push(r),s=i}return{cfg:s,applied:o}}function XA(n){let e=di.map(s=>Pt.join(n,s)).filter(s=>bt.existsSync(Pt.join(s,"package.json")));if(!e.length)return Pt.join(n,"checkpoints");let t=s=>{try{return JSON.parse(bt.readFileSync(Pt.join(s,"package.json"),"utf8")).version||"0.0.0"}catch{return"0.0.0"}};return e.sort((s,o)=>Xe(t(o),t(s)))[0]}function Y0(){return process.platform==="win32"?"npm.cmd":"npm"}function z0(n=process.env,e="npm"){let t=e==="pnpm"?process.platform==="win32"?"pnpm.cmd":"pnpm":Y0(),s=su(t,["root","-g"],{encoding:"utf8",env:n});return s.status===0?s.stdout.trim():null}async function ZA(n){try{return Number(bt.readFileSync(Pt.join(n,".daemon.pid"),"utf8"))||null}catch{return null}}async function yh(n,e){try{let t=await fetch("http://127.0.0.1:"+n+"/v1/health",{headers:{authorization:"Bearer "+(e||"")},signal:AbortSignal.timeout(1500)});return t.ok?{up:!0,...await t.json()}:{up:t.status===401}}catch{return{up:!1}}}async function QA({root:n,bin:e,env:t=process.env,say:s=()=>{},dryRun:o=!1}){let r=Q(n),i=ve(n),a=Lt(),l=!!(o||t.CHECKPOINTS_SERVICE_DRY_RUN),c=wc({root:n,bin:e,dryRun:l});if(c&&s("Login service renamed: stopped "+c.from+", wrote "+c.to+(l?" (dry run: nothing stopped)":"")),bt.existsSync(a)){let d=Rn({root:bf(bt.readFileSync(a,"utf8"))||n,bin:e});if(bt.readFileSync(a,"utf8")!==d&&(bt.writeFileSync(a,d),s("Login service now points at "+e)),l)return s("Would restart the login service: "+Qs().map(f=>f.join(" ")).join(" && ")),{how:"service",dryRun:!0,moved:c};for(let f of Qs()){let h=su(f[0],f.slice(1),{encoding:"utf8"});if(h.status!==0&&f[1]!=="unload")return{how:"service",ok:!1,error:(h.stderr||"").trim().split(`
372
+ `)[0]||"exit "+h.status}}}else{let d=await yh(r.port,i),f=await ZA(n),h=m=>{try{return process.kill(m,0),!0}catch{return!1}};if(d.up&&f&&h(f)){try{process.kill(f,"SIGTERM")}catch{}for(let m=0;m<50&&h(f);m++)await nu(200);if(h(f)){try{process.kill(f,"SIGKILL")}catch{}await nu(200)}}else if(d.up)return{how:"unknown",ok:!1,error:"a daemon is running that setup didn't start (stop it and run `leftoff serve`)"};for(let m=0;m<25&&(await yh(r.port,i)).up;m++)await nu(200);let{startDaemonDetached:p}=await Promise.resolve().then(()=>(no(),Mc));p({root:n,bin:e,env:t})}let u={up:!1};for(let d=0;d<60&&(u=await yh(r.port,i),!(u.up&&(!u.version||u.version===X)));d++)await nu(250);return{how:bt.existsSync(a)?"service":"detached",ok:u.up&&(!u.version||u.version===X),version:u.version,moved:c}}async function W0({flags:n={},out:e=process.stdout}={}){let t=gs(ms(e)),s=Uo();return n.since&&(s=Ho(s,String(n.since).replace(/^v/,""),null)),n.latest&&(s=s.slice(0,1)),e.write(t.bold(Ke+" v"+X)+t.dim(" · what changed")+`
373
+
374
+ `+Sl(s,{colors:t})),0}function wh(n,e,{env:t,log:s,timeoutMs:o=3e5}){return new Promise(r=>{s("$ "+n+" "+e.join(" "));let i="",a="",l=JA(n,e,{env:t,stdio:["ignore","pipe","pipe"]}),c=setTimeout(()=>l.kill("SIGKILL"),o);l.stdout.on("data",u=>{a+=u,s(String(u).trimEnd())}),l.stderr.on("data",u=>{i+=u,s(String(u).trimEnd())}),l.on("error",u=>{clearTimeout(c),r({status:-1,stderr:u.message,stdout:a})}),l.on("close",u=>{clearTimeout(c),r({status:u,stderr:i,stdout:a})})})}async function J0({flags:n={},out:e=process.stdout,env:t=process.env}={}){let s=gs(ms(e)),o=!n.verbose&&!n.check&&!(n.finish&&!n.quiet),r=$0({out:e,quiet:o,c:s,logFile:n.log?String(n.log):K0(t),tty:n.finish?t.LEFTOFF_PROGRESS_TTY==="1":!!e.isTTY&&!t.CI,env:t}),i=r.say,a=v=>i(s.green("✔")+" "+v),l=v=>{o?r.warn(eT(v)):i(s.yellow("!")+" "+v)},c=v=>o?r.fail(v):i(s.red("✖")+" "+v);if(n.finish||r.log(`
375
+ # leftoff upgrade, `+new Date().toISOString()+(o?"":" (--verbose)")),n.finish)return sT({flags:n,out:e,env:t,c:s,say:i,ok:a,warn:l,fail:c,P:r});let u=await fi({env:t,globalRoot:t.CHECKPOINTS_NPM_GLOBAL_ROOT||void 0}),d=Go(n.source||t.CHECKPOINTS_UPDATE_SOURCE||(Q().updates||{}).source||u.source||ui);u.kind==="npm"&&(d="npm:"+it),i(s.bold(Ke+" v"+u.version)+s.dim((u.kind==="npm"?" · installed from npm with "+u.pm+" at ":" ("+vs(u.sha)+") · "+{clone:"a git clone at ",global:"installed globally at ",npx:"an npx copy at ",local:"installed at "}[u.kind])+u.repoDir)),r.start("Checking for updates…");let f=await hi({info:u,source:d,env:t});if(f.error&&u.kind==="npm")return c("Couldn't reach the npm registry ("+vl(t)+"): "+f.error),1;if(f.error)return i(s.dim(" (git uses your own credentials; for a private repo, check `git ls-remote "+d+'` works, or set "updates": { "source": "git@github.com:'+rt+'.git" } in config.json)')),c("Couldn't reach "+d+": "+f.error),1;if(f.source&&f.source!==d&&(i(s.dim("· reached GitHub over SSH ("+f.source+")")),d=f.source),!(f.available||!u.sha&&f.remote.version&&Xe(f.remote.version,u.version)>0)&&!n.force)return o?r.done("Up to date: "+Ke+" "+u.version):a("Up to date"+s.dim(" (remote "+vs(f.remote.sha)+(f.remote.version?", v"+f.remote.version:"")+")")),0;let p=f.remote.version||vs(f.remote.sha);if(r.done("Found "+p+" (you have "+u.version+")"),i(s.bold(s.cyan("Update available: "))+(f.remote.version?"v"+u.version+" → v"+f.remote.version:"")+(u.kind==="npm"?"":s.dim(" "+vs(u.sha)+" → "+vs(f.remote.sha)))),f.incoming.length?i(`
376
+ `+Sl(f.incoming,{colors:s})):f.fetchError&&i(s.dim(" (couldn't read the new changelog: "+f.fetchError+")")),n.check)return i(s.dim("Run `leftoff upgrade` to install it.")),0;r.start("Downloading "+p+"…");let m=u.repoDir;if(u.kind==="npm"){let v=jd(u.pm);i(s.dim("$ "+v.join(" ")));let b=await wh(v[0],v.slice(1),{env:t,log:r.log});if(b.status!==0)return o||i((b.stderr||"").trim()),c(v.slice(0,3).join(" ")+" failed: "+((b.stderr||"").trim().split(`
377
+ `).pop()||"exit "+b.status)),1;let y=t.CHECKPOINTS_NPM_GLOBAL_ROOT||z0(t,u.pm);m=y&&bt.existsSync(Pt.join(y,it,"package.json"))?Pt.join(y,it):u.repoDir,a("Installed "+it+"@"+(f.remote.version||"latest")+s.dim(" → "+m))}else if(u.kind==="clone"){u.source&&Js(u.source)!==u.source&&(await Ct(["remote","set-url","origin",Js(u.source)],{cwd:u.repoDir,env:t})).code===0&&i(s.dim("· the repo is now "+rt+"; pointed origin at "+Js(u.source))),i(s.dim("$ git -C "+u.repoDir+" pull --ff-only"));let v=await Ct(["pull","--ff-only","-q"],{cwd:u.repoDir,env:t,timeoutMs:12e4});if(v.code!==0)return i(v.stderr.trim()),i(s.dim(" Local changes? Commit or stash them and run `leftoff upgrade` again.")),c("git pull failed: "+(v.stderr.trim().split(`
378
+ `).pop()||"exit "+v.code)),1;a("Pulled "+vs(f.remote.sha))}else{let v=d.match(/^(?:https:\/\/github\.com\/|git@github\.com:)([\w.-]+\/[\w.-]+?)(?:\.git)?$/),b=v?"github:"+v[1]:d;i(s.dim("$ npm install -g "+b));let y=await wh(Y0(),["install","-g",b,"--no-fund","--no-audit","--loglevel=error"],{env:t,log:r.log});if(y.status!==0)return o||i((y.stderr||"").trim()),c("npm install -g failed: "+((y.stderr||"").trim().split(`
379
+ `).pop()||"exit "+y.status)),1;let _=t.CHECKPOINTS_NPM_GLOBAL_ROOT||z0(t);m=_?XA(_):u.repoDir,a("Installed "+b+s.dim(" → "+m));try{bt.existsSync(Pt.join(m,".install.json"))||Pd({sha:f.remote.sha,source:d,version:f.remote.version||void 0},m)}catch{}}r.done("Downloaded "+p),r.start("Updating the Claude Code plugin…");let g=null,S=Cc(t);if(u.kind==="npm")try{Ff({from:m,env:t})}catch(v){l("Couldn't refresh the mod's files: "+v.message)}if(!S)l("`claude` isn't on PATH; update the mod later with: claude plugin marketplace update "+fn+" && claude plugin update "+Le);else{let v=Nc(S,t);if(!v.installed&&v.legacyInstalled){i(s.dim("$ claude plugin marketplace update "+fn));let b=Un(S,["plugin","marketplace","update",fn],{env:t});b.code!==0&&l("`claude plugin marketplace update "+fn+"` failed: "+((b.stderr||b.stdout).trim().split(`
380
+ `).pop()||"exit "+b.code))}else if(!v.installed)i(s.dim("· The mod isn't installed through `claude plugin`; nothing to update there.")),g="skip";else{let b=!1;for(let y of[["plugin","marketplace","update",fn],["plugin","update",Le]]){i(s.dim("$ claude "+y.join(" ")));let _=await wh(S,y,{env:{...t,CHECKPOINTS_INTERNAL:"1",LEFTOFF_INTERNAL:"1"},log:r.log,timeoutMs:12e4});if(_.status!==0){b=!0,l("`claude "+y.join(" ")+"` failed: "+((_.stderr||_.stdout).trim().split(`
381
+ `).pop()||"exit "+_.status));break}}a("Mod updated"+s.dim(" · restart open Claude Code sessions to load it")),b||(g="ok")}}o&&(g==="ok"?r.done("Updated the Claude Code plugin"):g==="skip"?r.done("Claude Code plugin: not installed with `claude plugin`, nothing to update"):r.stop());let w=[Pt.join(m,"bin","checkpoints.js"),"upgrade","--finish","--from="+u.version,"--from-sha="+(u.sha||""),"--repo="+m];if(n["no-restart"]&&w.push("--no-restart"),!o){let v=su(process.execPath,w,{env:t,stdio:"inherit"});return v.status==null?1:v.status}w.push("--quiet","--log="+r.logFile);let x=su(process.execPath,w,{env:{...t,LEFTOFF_PROGRESS_TTY:e.isTTY&&!t.CI?"1":"0"},stdio:["ignore","inherit","pipe"],encoding:"utf8"});return x.stderr&&r.log(x.stderr.trimEnd()),x.status!==0?(r.fail("Finishing the upgrade failed (exit "+x.status+")"),x.status==null?1:x.status):0}var eT=n=>String(n).replace(/\x1b\[2m.*?\x1b\[22m/g,"").trim();function tT(n,e=3){let t=n.map(o=>{let r=o.sections.flatMap(a=>a.items),i=r.filter(a=>/^\*\*/.test(a));return i.length?i:r}),s=[];for(let o=0;s.length<e&&t.some(r=>r.length>o);o++)for(let r of t){if(s.length>=e||!r[o])continue;let i=r[o].match(/^\*\*(.+?)\*\*/),a=(i?i[1]:r[o]).replace(/[*`]/g,"").replace(/\s+/g," ").trim().replace(/[.:]$/,"");a.length>72&&(a=a.slice(0,71).replace(/\s+\S*$/,"")+"…"),s.push(a)}return s}function nT(n,e,t=X){let s=[];for(let[o,r]of Object.entries(Zs(n)))o!==e&&r.valid&&r.card.version&&Xe(r.card.version,t)<0&&s.push(o+" ("+r.card.version+")");return s.sort()}async function sT({flags:n,out:e,env:t,c:s,say:o,ok:r,warn:i,fail:a,P:l}){let c=l.quiet,u=String(n.from||""),d=String(n.repo||ns),f=await fi({repoDir:d,env:t}),h=Z(),p=Sn(h);if(p){let{cfg:k,applied:w}=VA(p,u,X);if(w.length){bt.writeFileSync(be(h).config,JSON.stringify(k,null,2)+`
382
+ `);for(let x of w)r("Config: "+x.note)}}let m=bt.existsSync(Pt.join(d,"bin","leftoff.js"))?Pt.join(d,"bin","leftoff.js"):Pt.join(d,"bin","checkpoints.js");try{if(Di()){let k=bt.readFileSync(dn(),"utf8");tr(void 0,{bin:m}),k.includes(Ys)&&r("Settings hooks now run `leftoff`"+s.dim(" (replaced the checkpoints-hook entries)"))}}catch(k){i("Couldn't refresh the settings hooks: "+k.message)}let g=Cc(t);if(g){let k;try{k=Uf({bin:g,env:t})}catch(w){k={ok:!1,error:w.message}}k.migrated&&k.ok?r("Mod renamed: "+ys+" → "+Le+(k.enabled?"":s.dim(" (kept disabled, as it was)"))+s.dim(" · restart open Claude Code sessions to load it")):k.ok||i("Couldn't move the mod to its new name: "+k.error+s.dim(" · run: claude plugin install "+Le+" && claude plugin uninstall "+ys))}if(!n["no-restart"]){l.start("Restarting the daemon…");let k=await QA({root:h,bin:m,env:t,say:w=>o(s.dim("· "+w))});if(k.dryRun)l.stop();else if(k.ok){r("Daemon restarted"+s.dim(" ("+k.how+", v"+(k.version||X)+")")),l.done("Restarted the daemon");try{let w=JSON.parse(bt.readFileSync(Pt.join(h,".cleanup."+de(Q(h)).replace(/[^\w.-]/g,"_")+".json"),"utf8"));w.lastArchived&&Date.now()-Date.parse(w.lastArchivedAt)<5*6e4&&r("Archived "+w.lastArchived+" empty "+(w.lastArchived===1?"session":"sessions")+" with no folder or process"+s.dim(" (kept on disk, marked archived; see leftoff doctor)"))}catch{}}else i("Restart the daemon yourself: "+(k.error||"it didn't come back")+s.dim(" · leftoff serve"))}if(c){let k=Ho(Uo(),u||null,X);l.print(""),l.print(s.bold(Ke+" "+(u?u+" → ":"")+X)+" "+s.green("✓"));for(let x of tT(k))l.print(" • "+x);l.print(s.dim("Full notes: leftoff changelog (also What’s new on the board) · log: "+gh(l.logFile,t)));let w=nT(h,de(Q(h)));return w.length&&l.print(s.yellow("!")+" Upgrade your other Mac"+(w.length>1?"s":"")+" too: "+w.join(", ")),0}o(),o(s.bold(Ke+" "+(u?"v"+u+" → ":"")+"v"+X)+(n["from-sha"]||f.sha?s.dim(" "+vs(n["from-sha"])+" → "+vs(f.sha)):""));let S=Ho(Uo(),u||null,X);if(S.length&&o(`
383
+ `+Sl(S,{colors:s})),f.kind==="clone"&&n["from-sha"]){let k=await Ct(["log","--format=%h %s",String(n["from-sha"])+"..HEAD"],{cwd:d,env:t});if(k.code===0&&k.stdout.trim()){o(s.bold("Commits"));for(let w of k.stdout.trim().split(`
384
+ `).slice(0,30))o(" "+s.dim(w.slice(0,7))+w.slice(7))}}return o(s.dim("The board shows these under What's new. `leftoff changelog` prints them again.")),0}xe();Vl();Fi();Ot();import ou from"node:fs";import q0 from"node:path";import oT from"node:https";import{spawnSync as rT}from"node:child_process";var Sh="/Applications/Tailscale.app/Contents/MacOS/Tailscale";function tS({env:n=process.env,platform:e=process.platform,exists:t=s=>{try{return ou.accessSync(s,ou.constants.X_OK),ou.statSync(s).isFile()}catch{return!1}}}={}){if(n.CHECKPOINTS_TAILSCALE)return t(n.CHECKPOINTS_TAILSCALE)?n.CHECKPOINTS_TAILSCALE:null;let s=e==="win32"?["tailscale.exe"]:["tailscale"];for(let o of String(n.PATH||"").split(q0.delimiter).filter(Boolean))for(let r of s){let i=q0.join(o,r);if(t(i))return i}return e==="darwin"&&t(Sh)?Sh:null}function nS(n,e,{inherit:t=!1,timeoutMs:s=15e3}={}){let o=rT(n,e,{encoding:"utf8",stdio:t?"inherit":["ignore","pipe","pipe"],timeout:t?void 0:s});return{code:o.status==null?o.error?127:1:o.status,stdout:o.stdout||"",stderr:(o.stderr||"")+(o.error?String(o.error.message):"")}}function kh(n){let e;try{e=typeof n=="string"?JSON.parse(n):n}catch{return null}if(!e||typeof e!="object")return null;let t=e.Self||{},s=String(t.DNSName||"").replace(/\.$/,"").toLowerCase()||null;return{state:e.BackendState||null,dnsName:s,hostName:t.HostName||null,online:t.Online!==!1,magicDNSSuffix:e.MagicDNSSuffix||null}}function bh(n,e,t=443){let s;try{s=typeof n=="string"?n.trim()?JSON.parse(n):{}:n}catch{return{parsed:!1}}s=s||{};let o=e+":"+t,r=(s.Web||{})[o],i=r&&r.Handlers&&r.Handlers["/"];return{parsed:!0,proxy:i&&i.Proxy||null,other:i?!i.Proxy:!1,funnel:!!(s.AllowFunnel&&s.AllowFunnel[o]),any:!!r}}var iT=n=>"http://127.0.0.1:"+n,vh=(n,e)=>!!n&&new RegExp("^(https?://)?(127\\.0\\.0\\.1|localhost):"+e+"/?$").test(n)||String(n)===String(e);async function xh(n,e,t,s,o){try{let r=await fetch("http://127.0.0.1:"+n+s,{method:t,headers:{authorization:"Bearer "+(e||""),"content-type":"application/json"},body:o?JSON.stringify(o):void 0,signal:AbortSignal.timeout(3e3)}),i=await r.json().catch(()=>({}));return r.ok?{up:!0,ok:!0,body:i}:{up:!0,ok:!1,status:r.status,body:i}}catch{return{up:!1}}}async function V0({root:n,port:e,token:t,add:s=[],remove:o=[],requireToken:r}){let i=await xh(e,t,"POST","/v1/remote/hosts",{add:s,remove:o,requireToken:r});if(i.ok)return{live:!0,hosts:i.body.hosts};let a=Sn(n)||{},l=new Set(o.map(Bt)),c=(a.remoteHosts||[]).map(Bt).filter(d=>d&&!l.has(d));for(let d of s.map(Bt))d&&!c.includes(d)&&c.push(d);a.remoteHosts=c,typeof r=="boolean"&&(a.remoteRequireToken=r),go(a,n);let u=!1;return i.up&&(u=!!(await xh(e,t,"POST","/v1/remote/reload")).ok),{live:u,daemonUp:!!i.up,status:i.status||null,hosts:c}}function X0(n,{timeoutMs:e=45e3,env:t=process.env}={}){return new Promise(s=>{let o;try{o=new URL("v1/remote/ping",n)}catch{return s({ok:!1,kind:"bad_url",message:"not a URL: "+n})}let r=t.CHECKPOINTS_REMOTE_CHECK_ADDR?t.CHECKPOINTS_REMOTE_CHECK_ADDR.split(":"):null,i={method:"GET",hostname:o.hostname,port:o.port||443,path:o.pathname,servername:o.hostname,headers:{host:o.host,accept:"application/json"},timeout:e};if(r&&(i.hostname=r[0],i.port=Number(r[1])),t.CHECKPOINTS_REMOTE_CHECK_CA)try{i.ca=ou.readFileSync(t.CHECKPOINTS_REMOTE_CHECK_CA)}catch{}let a=Date.now(),l=oT.request(i,c=>{let u="";c.setEncoding("utf8"),c.on("data",d=>{u.length<2e4&&(u+=d)}),c.on("end",()=>{let d=null;try{d=JSON.parse(u)}catch{}let f=Date.now()-a;if(c.statusCode===200&&d&&d.app===Ke)return s({ok:!0,ms:f,version:d.version,via:d.via});if(c.statusCode===200&&d&&d.ok&&d.app)return s({ok:!1,kind:"other_app",message:"something else answers there ("+d.app+")"});let h=d&&d.error&&d.error.code||c.headers["x-leftoff-error"]||null;if(h==="bad_host")return s({ok:!1,kind:"bad_host",message:"the daemon refused the tailnet name (403 bad_host): it isn't in remoteHosts of the running daemon"});if(h==="funnel_refused")return s({ok:!1,kind:"funnel",message:"the request came through Tailscale Funnel"});if(c.statusCode===404&&h==="not_found")return s({ok:!1,kind:"old_daemon",message:"the running daemon is older than v0.4.6 (restart it: leftoff open)"});if(c.statusCode===502||c.statusCode===503)return s({ok:!1,kind:"daemon_down",message:"Tailscale answered but couldn't reach the daemon on this machine (HTTP "+c.statusCode+"): is it running? leftoff open"});s({ok:!1,kind:"http",message:"HTTP "+c.statusCode+(h?" "+h:"")})})});l.on("timeout",()=>{l.destroy(Object.assign(new Error("timed out after "+Math.round(e/1e3)+" s"),{code:"ETIMEDOUT"}))}),l.on("error",c=>{let u=String(c.code||"");if(u==="ENOTFOUND"||u==="EAI_AGAIN")return s({ok:!1,kind:"dns",message:"this Mac can't resolve "+o.hostname+' (MagicDNS off, or "Use Tailscale DNS" unticked in the Tailscale menu)'});if(/CERT|SELF_SIGNED|UNABLE_TO_VERIFY|ERR_TLS|EPROTO|ECONNRESET/.test(u)||/certificate|handshake|socket hang up/i.test(c.message))return s({ok:!1,kind:"tls",message:"no valid HTTPS certificate yet ("+(u||c.message)+"). Turn on HTTPS certificates for the tailnet (https://login.tailscale.com/admin/dns); the first one can take a minute to issue"});if(u==="ETIMEDOUT")return s({ok:!1,kind:"timeout",message:c.message+" (Tailscale may still be fetching the certificate; try leftoff tailscale status in a minute)"});if(u==="ECONNREFUSED")return s({ok:!1,kind:"refused",message:"nothing listens on "+o.host+" (tailscale serve isn't running)"});s({ok:!1,kind:"error",message:u?u+": "+c.message:c.message})}),l.end()})}function Z0(n,e){return n.ok?"✓ checked "+e+" end to end from this machine: "+Ke+" "+(n.version?"v"+n.version+" ":"")+"answered in "+(n.ms<1e3?n.ms+" ms":(n.ms/1e3).toFixed(1)+" s")+" (MagicDNS, HTTPS certificate, Serve and the daemon are all fine)":"! couldn't open "+e+" from this machine: "+n.message}function Q0(n={},e=process.stdout){return n["no-qr"]||n.qr==="false"||n.qr==="0"?!1:n.qr?!0:!!e.isTTY}function eS(n,{base:e,link:t,code:s,expiresAt:o,qr:r,color:i=!0}){let a=Math.max(1,Math.round((Date.parse(o)-Date.now())/6e4)),l=new Date(o).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"});n("Board on your tailnet: "+e),r?(n(""),n("Scan this with your iPhone camera (Tailscale has to be on there) to open the board signed in:"),n(""),n(xc(t,{color:i})),n("")):n("Open this once on your phone to sign that browser in:"),n(" "+t),s&&n("Home-screen app? It has its own cookies, so type this code in it instead: "+Yl(s)),n("The link and the code each work once, until "+l+" ("+a+" min). Another device later: leftoff link")}var ru="Tailscale Funnel (public internet) is intentionally unsupported: the board stays inside your tailnet.";function ra(){return`leftoff tailscale — view the board from your phone or other machines on your tailnet
385
385
 
386
386
  leftoff tailscale on [--https-port=443] [--force] [--no-login|--login]
387
387
  runs \`tailscale serve --bg --https=443 http://127.0.0.1:<port>\`, adds this
@@ -391,21 +391,21 @@ data: `+JSON.stringify(y)+`
391
391
  leftoff tailscale off removes the serve entry and the name, signs out remote browsers
392
392
  leftoff tailscale status tailscale, serve and Leftoff's side of it
393
393
  leftoff tailscale link a fresh sign-in: a QR code (one browser) and a typed code (the
394
- (or: leftoff link) home-screen app), each single-use, ${qs/6e4} minutes
394
+ (or: leftoff link) home-screen app), each single-use, ${Xs/6e4} minutes
395
395
  --qr / --no-qr print the QR code (default: only when the output is a terminal)
396
396
  --no-check on/status: skip opening the URL from this machine
397
397
  leftoff tailscale revoke sign out every remote browser
398
398
 
399
- ${su}
399
+ ${ru}
400
400
  No Tailscale? An SSH tunnel works too: ssh -N -L 7429:127.0.0.1:7429 you@this-machine
401
- `}function X0(n,e){return(n||[]).map(Gl).filter(t=>t&&t.name===e)}function kh({env:n=process.env,platform:e=process.platform,run:t=V0,port:s,cfgHosts:o=[]}={}){let r=q0({env:n,platform:e});if(!r)return{installed:!1};let i=t(r,["status","--json"],{timeoutMs:5e3}),a=i.code===0?gh(i.stdout):null,l={installed:!0,bin:r,state:a?a.state:null,dnsName:a?a.dnsName:null,error:i.code===0?null:(i.stderr||i.stdout).trim().split(`
402
- `)[0]||"tailscale status failed"};if(!a||!a.dnsName)return l;let c=X0(o,a.dnsName),u=c.length&&c[0].port||443,d=t(r,["serve","status","--json"],{timeoutMs:5e3}),f=d.code===0?yh(d.stdout,a.dnsName,u):{parsed:!1};return{...l,httpsPort:u,serve:f,serving:!!(f.proxy&&wh(f.proxy,s)),listed:c.length>0,url:"https://"+a.dnsName+(u===443?"":":"+u)+"/"}}function bh(n,{cfgHosts:e=[]}={}){return!n||!n.installed?e.length?"! remote access: remoteHosts is set but no tailscale CLI found":"· remote access: off (Tailscale not found; see leftoff tailscale)":n.dnsName?n.serving&&n.listed?"✓ remote access: "+n.url+" (tailscale serve → 127.0.0.1, tailnet only)":n.listed&&!n.serving?"! remote access: "+n.dnsName+" is in remoteHosts but tailscale serve isn't proxying to us (run: leftoff tailscale on)":n.serving&&!n.listed?"! remote access: tailscale serve points here but "+n.dnsName+" isn't in remoteHosts, so it gets 403 (run: leftoff tailscale on)":"· remote access: off (run: leftoff tailscale on, to open the board from your tailnet)":"· remote access: off (tailscale "+(n.state||"not connected")+")"}async function vh({sub:n="status",flags:e={},out:t=process.stdout,env:s=process.env,platform:o=process.platform,run:r=V0}={}){let i=(p="")=>t.write(p+`
403
- `);if(["help","--help","-h"].includes(n))return i(na()),0;if(n==="funnel")return i(su),i("Use: leftoff tailscale on (tailscale serve, tailnet only)"),1;let a=Z(),l=Q(a),c=Number(e.port||l.port),u=ve(a),d=l.remoteHosts||[];if(n==="revoke"){let p=xi(a);return i("Signed out "+p+" remote browser"+(p===1?"":"s")+". Unused login links are void too."),0}let f=q0({env:s,platform:o}),h=()=>(i("Tailscale CLI not found (looked on PATH"+(o==="darwin"?" and in "+mh:"")+")."),i("Install Tailscale from https://tailscale.com/download and sign in, then run this again."),i("Or, without Tailscale: ssh -N -L "+c+":127.0.0.1:"+c+" you@this-machine, then open http://localhost:"+c+"/"),1);if(n==="link"){if(!d.length)return i("Remote access is off. Run: leftoff tailscale on"),1;if(l.remoteRequireToken===!1)return i("remoteRequireToken is off, so no login is needed: "+d.map(g=>"https://"+Ut(g)+"/").join(" ")),0;Oe(a);let p=vi(a),m="https://"+Ut(d[0])+"/";J0(i,{base:m,link:m+"?t="+p.code,code:p.signInCode,expiresAt:p.expiresAt,qr:W0(e,t),color:!e["no-color"]&&!s.NO_COLOR});for(let g of d.slice(1))i(" also: https://"+Ut(g)+"/?t="+p.code);return 0}if(n==="status"){if(!f)return h(),i(""),i("remoteHosts: "+(d.length?d.join(", "):"(none)")),1;let p=kh({env:s,platform:o,run:r,port:c,cfgHosts:d}),m=r(f,["version"],{timeoutMs:5e3});if(i("tailscale "+f+(m.code===0?" ("+m.stdout.trim().split(`
404
- `)[0]+")":"")),i("state "+(p.state||p.error||"unknown")+(p.dnsName?" · "+p.dnsName:"")),p.dnsName){let k=p.serve||{};i("serve "+(k.parsed?k.proxy?"https://"+p.dnsName+(p.httpsPort===443?"":":"+p.httpsPort)+"/ → "+k.proxy+(p.serving?"":" (not this daemon, port "+c+")"):"nothing on https:"+p.httpsPort:"couldn't read tailscale serve status")),k.funnel&&i("! Funnel is ON for this port. Leftoff refuses Funnel traffic; turn it off: tailscale funnel --https="+p.httpsPort+" off")}i("remoteHosts "+(d.length?d.join(", "):"(none)")+" · sign-in "+(l.remoteRequireToken===!1?"not required":"required (QR code or typed code, leftoff link)"));let g=Wl(a);i("browsers "+g.sessions.length+" signed in (30 days from each one's last visit)"+(g.pendingLinks?", "+g.pendingLinks+" unused link"+(g.pendingLinks===1?"":"s"):"")+(g.pendingCodes?", "+g.pendingCodes+" unused code"+(g.pendingCodes===1?"":"s"):""));let S=await Sh(c,u,"GET","/v1/remote");return i("daemon "+(S.up?S.ok?"answers "+(S.body.hosts.length?S.body.hosts.join(", "):"loopback only"):"running but too old for remote access (restart it)":"not running")),i(bh(p,{cfgHosts:d})),p.serving&&p.listed&&!e["no-check"]&&s.CHECKPOINTS_REMOTE_CHECK!=="off"&&i(z0(await Y0(p.url,{timeoutMs:15e3,env:s}),p.url)),0}if(n==="on"){if(!f)return h();let p=r(f,["status","--json"],{timeoutMs:8e3}),m=p.code===0?gh(p.stdout):null;if(!m)return i("`tailscale status --json` failed: "+((p.stderr||p.stdout).trim()||"no output")+`
405
- Is Tailscale running and signed in?`),1;if(m.state&&m.state!=="Running")return i("Tailscale is "+m.state+", not connected. Open Tailscale (or run: tailscale up), then try again."),1;if(!m.dnsName)return i("This machine has no tailnet DNS name. Turn on MagicDNS and HTTPS certificates: https://login.tailscale.com/admin/dns"),1;let g=Number(e["https-port"]||443),S=r(f,["serve","status","--json"],{timeoutMs:8e3}),k=S.code===0?yh(S.stdout,m.dnsName,g):{parsed:!1},w=!!(k.proxy&&wh(k.proxy,c));if(k.funnel)return i("Funnel is ON for "+m.dnsName+":"+g+", which would put this port on the public internet. "+su+`
406
- Turn it off first: tailscale funnel --https=`+g+" off"),1;if((k.proxy||k.other)&&!w&&!e.force)return i("https://"+m.dnsName+(g===443?"":":"+g)+"/ is already served by tailscale ("+(k.proxy||"a file or text handler")+")."),i("Use another port (leftoff tailscale on --https-port=8443) or replace it (--force)."),1;let x=["serve","--bg","--https="+g,QA(c)];if(w)i("✓ tailscale serve already proxies https://"+m.dnsName+"/ to "+k.proxy);else{i("Running: tailscale "+x.join(" "));let A=r(f,x,{inherit:!0});if(A.code!==0)return i("tailscale serve failed (exit "+A.code+"). If it says HTTPS or Serve isn't enabled, turn on HTTPS certificates for your tailnet: https://login.tailscale.com/admin/dns"),1}let v=m.dnsName+(g===443?"":":"+g);Oe(a);let b=await $0({root:a,port:c,token:u,add:[v],requireToken:e["no-login"]?!1:e.login?!0:void 0});i((b.live||!b.daemonUp?"✓ ":"! ")+v+" added to remoteHosts"+(b.live?" (live in the running daemon)":b.daemonUp?" in config.json, but the running daemon didn't take it (HTTP "+(b.status||"?")+(b.status===401?": it uses a different folder or token than this shell; check CHECKPOINTS_DIR / LEFTOFF_DIR":"")+"). Restart it: leftoff open":" (the daemon isn't running; start it with: leftoff open)"));let y="https://"+v+"/",_=e["no-login"]?!1:Q(a).remoteRequireToken!==!1;if(!e["no-check"]&&s.CHECKPOINTS_REMOTE_CHECK!=="off"){i("Checking "+y+" from this machine (the first HTTPS request can take up to a minute while Tailscale gets the certificate)…");let A=await Y0(y,{env:s});i(z0(A,y)),!A.ok&&A.kind==="bad_host"&&!b.live&&i(" The running daemon didn't take the new name. Restart it: leftoff open (or log out and in), then: leftoff tailscale status")}if(i(""),_){let A=vi(a);J0(i,{base:y,link:y+"?t="+A.code,code:A.signInCode,expiresAt:A.expiresAt,qr:W0(e,t),color:!e["no-color"]&&!s.NO_COLOR})}else i("Board on your tailnet: "+y),i("remoteRequireToken is off: anyone on your tailnet who can reach this URL gets the board.");return i("Turn it off: leftoff tailscale off · "+su),0}if(n==="off"){let p=null;if(f){let w=r(f,["status","--json"],{timeoutMs:8e3}),x=w.code===0?gh(w.stdout):null;p=x&&x.dnsName}let m=p?X0(d,p):[],g=m.length?m.map(w=>w.port||443):[Number(e["https-port"]||443)];if(f&&p)for(let w of g){let x=r(f,["serve","status","--json"],{timeoutMs:8e3}),v=x.code===0?yh(x.stdout,p,w):{parsed:!1};if(v.proxy&&wh(v.proxy,c)){let b=["serve","--https="+w,"off"];i("Running: tailscale "+b.join(" "));let y=r(f,b,{inherit:!0});y.code!==0&&i("! tailscale serve off failed (exit "+y.code+"); check: tailscale serve status")}else v.proxy&&i("· leaving https:"+w+" alone: it proxies to "+v.proxy+", not this daemon")}else f||i("· tailscale CLI not found; removing Leftoff's side only");let S=e.all?d:m.length?m.map(w=>w.name+(w.port&&w.port!==443?":"+w.port:"")):d;await $0({root:a,port:c,token:u,remove:S});let k=xi(a);return i("✓ remote access off: "+(S.length?S.join(", ")+" removed from remoteHosts":"no remoteHosts to remove")+(k?", "+k+" remote browser"+(k===1?"":"s")+" signed out":"")),0}return i(na()),1}Gs();Ti();Vo();Pn();Te();function sa(n,e=Date.now()){let t=Date.parse(n||"");if(isNaN(t))return"";let s=Math.max(0,Math.round((e-t)/1e3));return s<60?"just now":s<3600?Math.round(s/60)+"m ago":s<86400?Math.round(s/3600)+"h ago":Math.round(s/86400)+"d ago"}function ks(n){return n.title||"(untitled)"}function xh(n,e){return[n.repoLabel||n.repoName,n.repo&&n.repo.branch,n.status,n.idleSec!=null?"idle "+sa(n.lastActivityAt,e).replace(" ago",""):null,"cp:"+(n.shortId||ze(n.id)),n.terminal?n.terminal.label:null].filter(Boolean).join(" · ")}function eT(n){return n.filter(e=>e.isLocal!==!1&&e.live!=="closed"&&!e.endedAt)}async function Z0({pos:n=[],flags:e={},api:t,out:s=process.stdout,err:o=process.stderr,input:r=process.stdin,jumpRunner:i=nc,now:a=Date.now(),env:l=process.env}={}){let c=w=>s.write(w+`
401
+ `}function sS(n,e){return(n||[]).map($l).filter(t=>t&&t.name===e)}function Ah({env:n=process.env,platform:e=process.platform,run:t=nS,port:s,cfgHosts:o=[]}={}){let r=tS({env:n,platform:e});if(!r)return{installed:!1};let i=t(r,["status","--json"],{timeoutMs:5e3}),a=i.code===0?kh(i.stdout):null,l={installed:!0,bin:r,state:a?a.state:null,dnsName:a?a.dnsName:null,error:i.code===0?null:(i.stderr||i.stdout).trim().split(`
402
+ `)[0]||"tailscale status failed"};if(!a||!a.dnsName)return l;let c=sS(o,a.dnsName),u=c.length&&c[0].port||443,d=t(r,["serve","status","--json"],{timeoutMs:5e3}),f=d.code===0?bh(d.stdout,a.dnsName,u):{parsed:!1};return{...l,httpsPort:u,serve:f,serving:!!(f.proxy&&vh(f.proxy,s)),listed:c.length>0,url:"https://"+a.dnsName+(u===443?"":":"+u)+"/"}}function Th(n,{cfgHosts:e=[]}={}){return!n||!n.installed?e.length?"! remote access: remoteHosts is set but no tailscale CLI found":"· remote access: off (Tailscale not found; see leftoff tailscale)":n.dnsName?n.serving&&n.listed?"✓ remote access: "+n.url+" (tailscale serve → 127.0.0.1, tailnet only)":n.listed&&!n.serving?"! remote access: "+n.dnsName+" is in remoteHosts but tailscale serve isn't proxying to us (run: leftoff tailscale on)":n.serving&&!n.listed?"! remote access: tailscale serve points here but "+n.dnsName+" isn't in remoteHosts, so it gets 403 (run: leftoff tailscale on)":"· remote access: off (run: leftoff tailscale on, to open the board from your tailnet)":"· remote access: off (tailscale "+(n.state||"not connected")+")"}async function _h({sub:n="status",flags:e={},out:t=process.stdout,env:s=process.env,platform:o=process.platform,run:r=nS}={}){let i=(p="")=>t.write(p+`
403
+ `);if(["help","--help","-h"].includes(n))return i(ra()),0;if(n==="funnel")return i(ru),i("Use: leftoff tailscale on (tailscale serve, tailnet only)"),1;let a=Z(),l=Q(a),c=Number(e.port||l.port),u=ve(a),d=l.remoteHosts||[];if(n==="revoke"){let p=_i(a);return i("Signed out "+p+" remote browser"+(p===1?"":"s")+". Unused login links are void too."),0}let f=tS({env:s,platform:o}),h=()=>(i("Tailscale CLI not found (looked on PATH"+(o==="darwin"?" and in "+Sh:"")+")."),i("Install Tailscale from https://tailscale.com/download and sign in, then run this again."),i("Or, without Tailscale: ssh -N -L "+c+":127.0.0.1:"+c+" you@this-machine, then open http://localhost:"+c+"/"),1);if(n==="link"){if(!d.length)return i("Remote access is off. Run: leftoff tailscale on"),1;if(l.remoteRequireToken===!1)return i("remoteRequireToken is off, so no login is needed: "+d.map(g=>"https://"+Bt(g)+"/").join(" ")),0;Oe(a);let p=Ti(a),m="https://"+Bt(d[0])+"/";eS(i,{base:m,link:m+"?t="+p.code,code:p.signInCode,expiresAt:p.expiresAt,qr:Q0(e,t),color:!e["no-color"]&&!s.NO_COLOR});for(let g of d.slice(1))i(" also: https://"+Bt(g)+"/?t="+p.code);return 0}if(n==="status"){if(!f)return h(),i(""),i("remoteHosts: "+(d.length?d.join(", "):"(none)")),1;let p=Ah({env:s,platform:o,run:r,port:c,cfgHosts:d}),m=r(f,["version"],{timeoutMs:5e3});if(i("tailscale "+f+(m.code===0?" ("+m.stdout.trim().split(`
404
+ `)[0]+")":"")),i("state "+(p.state||p.error||"unknown")+(p.dnsName?" · "+p.dnsName:"")),p.dnsName){let k=p.serve||{};i("serve "+(k.parsed?k.proxy?"https://"+p.dnsName+(p.httpsPort===443?"":":"+p.httpsPort)+"/ → "+k.proxy+(p.serving?"":" (not this daemon, port "+c+")"):"nothing on https:"+p.httpsPort:"couldn't read tailscale serve status")),k.funnel&&i("! Funnel is ON for this port. Leftoff refuses Funnel traffic; turn it off: tailscale funnel --https="+p.httpsPort+" off")}i("remoteHosts "+(d.length?d.join(", "):"(none)")+" · sign-in "+(l.remoteRequireToken===!1?"not required":"required (QR code or typed code, leftoff link)"));let g=ql(a);i("browsers "+g.sessions.length+" signed in (30 days from each one's last visit)"+(g.pendingLinks?", "+g.pendingLinks+" unused link"+(g.pendingLinks===1?"":"s"):"")+(g.pendingCodes?", "+g.pendingCodes+" unused code"+(g.pendingCodes===1?"":"s"):""));let S=await xh(c,u,"GET","/v1/remote");return i("daemon "+(S.up?S.ok?"answers "+(S.body.hosts.length?S.body.hosts.join(", "):"loopback only"):"running but too old for remote access (restart it)":"not running")),i(Th(p,{cfgHosts:d})),p.serving&&p.listed&&!e["no-check"]&&s.CHECKPOINTS_REMOTE_CHECK!=="off"&&i(Z0(await X0(p.url,{timeoutMs:15e3,env:s}),p.url)),0}if(n==="on"){if(!f)return h();let p=r(f,["status","--json"],{timeoutMs:8e3}),m=p.code===0?kh(p.stdout):null;if(!m)return i("`tailscale status --json` failed: "+((p.stderr||p.stdout).trim()||"no output")+`
405
+ Is Tailscale running and signed in?`),1;if(m.state&&m.state!=="Running")return i("Tailscale is "+m.state+", not connected. Open Tailscale (or run: tailscale up), then try again."),1;if(!m.dnsName)return i("This machine has no tailnet DNS name. Turn on MagicDNS and HTTPS certificates: https://login.tailscale.com/admin/dns"),1;let g=Number(e["https-port"]||443),S=r(f,["serve","status","--json"],{timeoutMs:8e3}),k=S.code===0?bh(S.stdout,m.dnsName,g):{parsed:!1},w=!!(k.proxy&&vh(k.proxy,c));if(k.funnel)return i("Funnel is ON for "+m.dnsName+":"+g+", which would put this port on the public internet. "+ru+`
406
+ Turn it off first: tailscale funnel --https=`+g+" off"),1;if((k.proxy||k.other)&&!w&&!e.force)return i("https://"+m.dnsName+(g===443?"":":"+g)+"/ is already served by tailscale ("+(k.proxy||"a file or text handler")+")."),i("Use another port (leftoff tailscale on --https-port=8443) or replace it (--force)."),1;let x=["serve","--bg","--https="+g,iT(c)];if(w)i("✓ tailscale serve already proxies https://"+m.dnsName+"/ to "+k.proxy);else{i("Running: tailscale "+x.join(" "));let A=r(f,x,{inherit:!0});if(A.code!==0)return i("tailscale serve failed (exit "+A.code+"). If it says HTTPS or Serve isn't enabled, turn on HTTPS certificates for your tailnet: https://login.tailscale.com/admin/dns"),1}let v=m.dnsName+(g===443?"":":"+g);Oe(a);let b=await V0({root:a,port:c,token:u,add:[v],requireToken:e["no-login"]?!1:e.login?!0:void 0});i((b.live||!b.daemonUp?"✓ ":"! ")+v+" added to remoteHosts"+(b.live?" (live in the running daemon)":b.daemonUp?" in config.json, but the running daemon didn't take it (HTTP "+(b.status||"?")+(b.status===401?": it uses a different folder or token than this shell; check CHECKPOINTS_DIR / LEFTOFF_DIR":"")+"). Restart it: leftoff open":" (the daemon isn't running; start it with: leftoff open)"));let y="https://"+v+"/",_=e["no-login"]?!1:Q(a).remoteRequireToken!==!1;if(!e["no-check"]&&s.CHECKPOINTS_REMOTE_CHECK!=="off"){i("Checking "+y+" from this machine (the first HTTPS request can take up to a minute while Tailscale gets the certificate)…");let A=await X0(y,{env:s});i(Z0(A,y)),!A.ok&&A.kind==="bad_host"&&!b.live&&i(" The running daemon didn't take the new name. Restart it: leftoff open (or log out and in), then: leftoff tailscale status")}if(i(""),_){let A=Ti(a);eS(i,{base:y,link:y+"?t="+A.code,code:A.signInCode,expiresAt:A.expiresAt,qr:Q0(e,t),color:!e["no-color"]&&!s.NO_COLOR})}else i("Board on your tailnet: "+y),i("remoteRequireToken is off: anyone on your tailnet who can reach this URL gets the board.");return i("Turn it off: leftoff tailscale off · "+ru),0}if(n==="off"){let p=null;if(f){let w=r(f,["status","--json"],{timeoutMs:8e3}),x=w.code===0?kh(w.stdout):null;p=x&&x.dnsName}let m=p?sS(d,p):[],g=m.length?m.map(w=>w.port||443):[Number(e["https-port"]||443)];if(f&&p)for(let w of g){let x=r(f,["serve","status","--json"],{timeoutMs:8e3}),v=x.code===0?bh(x.stdout,p,w):{parsed:!1};if(v.proxy&&vh(v.proxy,c)){let b=["serve","--https="+w,"off"];i("Running: tailscale "+b.join(" "));let y=r(f,b,{inherit:!0});y.code!==0&&i("! tailscale serve off failed (exit "+y.code+"); check: tailscale serve status")}else v.proxy&&i("· leaving https:"+w+" alone: it proxies to "+v.proxy+", not this daemon")}else f||i("· tailscale CLI not found; removing Leftoff's side only");let S=e.all?d:m.length?m.map(w=>w.name+(w.port&&w.port!==443?":"+w.port:"")):d;await V0({root:a,port:c,token:u,remove:S});let k=_i(a);return i("✓ remote access off: "+(S.length?S.join(", ")+" removed from remoteHosts":"no remoteHosts to remove")+(k?", "+k+" remote browser"+(k===1?"":"s")+" signed out":"")),0}return i(ra()),1}$s();Oi();Xo();Fn();Te();function ia(n,e=Date.now()){let t=Date.parse(n||"");if(isNaN(t))return"";let s=Math.max(0,Math.round((e-t)/1e3));return s<60?"just now":s<3600?Math.round(s/60)+"m ago":s<86400?Math.round(s/3600)+"h ago":Math.round(s/86400)+"d ago"}function xs(n){return n.title||"(untitled)"}function Eh(n,e){return[n.repoLabel||n.repoName,n.repo&&n.repo.branch,n.status,n.idleSec!=null?"idle "+ia(n.lastActivityAt,e).replace(" ago",""):null,"cp:"+(n.shortId||We(n.id)),n.terminal?n.terminal.label:null].filter(Boolean).join(" · ")}function aT(n){return n.filter(e=>e.isLocal!==!1&&e.live!=="closed"&&!e.endedAt)}async function oS({pos:n=[],flags:e={},api:t,out:s=process.stdout,err:o=process.stderr,input:r=process.stdin,jumpRunner:i=oc,now:a=Date.now(),env:l=process.env}={}){let c=w=>s.write(w+`
407
407
  `);e.id===!0&&(e={...e,id:n.shift()});let u;try{e.id?u=[await t("GET","/v1/sessions/"+encodeURIComponent(e.id))]:u=(await t("GET","/v1/sessions?limit=300")).sessions||[]}catch(w){let x=/fetch failed|ECONNREFUSED|abort|timed? ?out/i.test(String(w.message))?"The Leftoff daemon isn't running. Start it with: leftoff serve (or leftoff status)":w.message;return o.write((e.plain?"":ee.red("✖ "))+x+`
408
- `),e.plain&&c(x),1}let d=e.id?u:eT(u);if(e.repo===!0&&(e={...e,repo:n.shift()}),typeof e.repo=="string"&&!e.id){let w=d;if(d=e.repo.split(",").flatMap(x=>ol(w,x.trim())).filter((x,v,b)=>b.indexOf(x)===v),!d.length)return c('No open session in a repo matching "'+e.repo+'".'+(w.length?" Open repos: "+[...new Set(w.map(x=>x.repoLabel||x.repoName))].join(", "):"")),2}if(e.list){d.length||c("No open sessions on this machine.");for(let w of d)c((w.live==="open"?ee.green("●"):ee.dim("○"))+" "+ks(w)+" "+ee.dim(xh(w,a)));return 0}let f=n.join(" ").trim(),h=null;if(e.id)h=d[0];else{let w=f?Dy(f,d):d;if(!w.length)return c(f?'No open session matches "'+f+'".'+(d.length?" Open: "+d.slice(0,5).map(ks).join(", "):""):"No open sessions on this machine."),2;if(w.length===1)h=w[0];else if(r.isTTY&&s.isTTY){let x=await vw(f?'Sessions matching "'+f+'"':"Open Claude Code sessions",w.map(v=>({label:ks(v),hint:xh(v,a),value:v.id,search:[v.title,v.repoLabel,v.repoName,v.repoKey,v.repo&&v.repo.branch,v.cwd,v.id].filter(Boolean).join(" ")})),{input:r,output:s});if(!x)return 2;h=w.find(v=>v.id===x)}else{c((f?w.length+' sessions match "'+f+'"':w.length+" open sessions")+"; be more specific, or use the id:");for(let x of w.slice(0,10))c(" "+ks(x)+" "+ee.dim(xh(x,a)));return 2}}if(!h)return c("No such session."),2;if(h.live==="closed")return c('"'+F(ks(h),60)+'" is closed. Resume it with: '+h.resumeCommand),3;let p=h.terminal&&h.terminal.label||h.term&&h.term.label||"its terminal";e.plain||c(ee.dim("Switching to “"+F(ks(h),60)+"” in "+p+"…"));let m=Date.now(),g;if(e.daemon)try{g=await t("POST","/v1/sessions/"+encodeURIComponent(h.id)+"/go",{})}catch(w){g={ok:!1,message:w.message}}else{let w;try{let v=await t("GET","/v1/sessions/"+encodeURIComponent(h.id)+"/where");v.isLocal?v.term?w=Xo(v):w={supported:!1,reason:"No terminal was recorded for this session (it started before v0.4, or without the mod). Restart it to get Go."}:w={supported:!1,reason:"This session runs on "+v.machine+"; Go only works there"}}catch(v){w={supported:!1,reason:v.message}}let x=Bn(l);g=await i(w,{ghostty:{activate:x.kind==="ghostty"?"never":"auto"}})}let S=Date.now()-m,k=" in "+(S<1e4?(S/1e3).toFixed(2):Math.round(S/1e3))+" s";if(e.debug){c(ee.dim(" total "+S+" ms"+(g.elapsedMs!=null?" (jump "+g.elapsedMs+" ms"+(e.daemon?", inside the daemon":"")+")":"")));for(let w of g.steps||[])c(ee.dim(" · "+w.step+": "+w.ms+" ms"+(w.result?" → "+w.result:"")))}return e.plain?(c(g.ok?"Switched to “"+F(ks(h),50)+"”"+k:g.message),g.ok?0:3):g.ok?(c(ee.green("✔")+" "+ks(h)+ee.dim(" ("+String(g.message||"").replace(/^Switched to /,"")+k+")")),0):(c(ee.red("✖")+" "+g.message+ee.dim(k+(g.steps&&g.steps.length&&!e.debug?" · "+Ql(g.steps):""))),3)}function Q0(n=process.env){let e=Bn(n),t=Xo({id:"x",term:e,tty:"/dev/ttys000",cwd:"/"});return{term:e,supported:t.supported,reason:t.reason||null}}Pn();Te();Ti();xf();Vo();var eS=`\`leftoff restore\` (v0.4.7): reopen the Claude Code sessions that were open
408
+ `),e.plain&&c(x),1}let d=e.id?u:aT(u);if(e.repo===!0&&(e={...e,repo:n.shift()}),typeof e.repo=="string"&&!e.id){let w=d;if(d=e.repo.split(",").flatMap(x=>il(w,x.trim())).filter((x,v,b)=>b.indexOf(x)===v),!d.length)return c('No open session in a repo matching "'+e.repo+'".'+(w.length?" Open repos: "+[...new Set(w.map(x=>x.repoLabel||x.repoName))].join(", "):"")),2}if(e.list){d.length||c("No open sessions on this machine.");for(let w of d)c((w.live==="open"?ee.green("●"):ee.dim("○"))+" "+xs(w)+" "+ee.dim(Eh(w,a)));return 0}let f=n.join(" ").trim(),h=null;if(e.id)h=d[0];else{let w=f?Uy(f,d):d;if(!w.length)return c(f?'No open session matches "'+f+'".'+(d.length?" Open: "+d.slice(0,5).map(xs).join(", "):""):"No open sessions on this machine."),2;if(w.length===1)h=w[0];else if(r.isTTY&&s.isTTY){let x=await Ow(f?'Sessions matching "'+f+'"':"Open Claude Code sessions",w.map(v=>({label:xs(v),hint:Eh(v,a),value:v.id,search:[v.title,v.repoLabel,v.repoName,v.repoKey,v.repo&&v.repo.branch,v.cwd,v.id].filter(Boolean).join(" ")})),{input:r,output:s});if(!x)return 2;h=w.find(v=>v.id===x)}else{c((f?w.length+' sessions match "'+f+'"':w.length+" open sessions")+"; be more specific, or use the id:");for(let x of w.slice(0,10))c(" "+xs(x)+" "+ee.dim(Eh(x,a)));return 2}}if(!h)return c("No such session."),2;if(h.live==="closed")return c('"'+F(xs(h),60)+'" is closed. Resume it with: '+h.resumeCommand),3;let p=h.terminal&&h.terminal.label||h.term&&h.term.label||"its terminal";e.plain||c(ee.dim("Switching to “"+F(xs(h),60)+"” in "+p+"…"));let m=Date.now(),g;if(e.daemon)try{g=await t("POST","/v1/sessions/"+encodeURIComponent(h.id)+"/go",{})}catch(w){g={ok:!1,message:w.message}}else{let w;try{let v=await t("GET","/v1/sessions/"+encodeURIComponent(h.id)+"/where");v.isLocal?v.term?w=Zo(v):w={supported:!1,reason:"No terminal was recorded for this session (it started before v0.4, or without the mod). Restart it to get Go."}:w={supported:!1,reason:"This session runs on "+v.machine+"; Go only works there"}}catch(v){w={supported:!1,reason:v.message}}let x=Kn(l);g=await i(w,{ghostty:{activate:x.kind==="ghostty"?"never":"auto"}})}let S=Date.now()-m,k=" in "+(S<1e4?(S/1e3).toFixed(2):Math.round(S/1e3))+" s";if(e.debug){c(ee.dim(" total "+S+" ms"+(g.elapsedMs!=null?" (jump "+g.elapsedMs+" ms"+(e.daemon?", inside the daemon":"")+")":"")));for(let w of g.steps||[])c(ee.dim(" · "+w.step+": "+w.ms+" ms"+(w.result?" → "+w.result:"")))}return e.plain?(c(g.ok?"Switched to “"+F(xs(h),50)+"”"+k:g.message),g.ok?0:3):g.ok?(c(ee.green("✔")+" "+xs(h)+ee.dim(" ("+String(g.message||"").replace(/^Switched to /,"")+k+")")),0):(c(ee.red("✖")+" "+g.message+ee.dim(k+(g.steps&&g.steps.length&&!e.debug?" · "+tc(g.steps):""))),3)}function rS(n=process.env){let e=Kn(n),t=Zo({id:"x",term:e,tty:"/dev/ttys000",cwd:"/"});return{term:e,supported:t.supported,reason:t.reason||null}}Fn();Te();Oi();_f();Xo();var iS=`\`leftoff restore\` (v0.4.7): reopen the Claude Code sessions that were open
409
409
  before this Mac restarted, each in a new Ghostty tab in its folder, resumed
410
410
  by id and with its name back.
411
411
  leftoff restore pick from the list (all checked; space toggles, enter goes)
@@ -418,11 +418,11 @@ by id and with its name back.
418
418
  The tabs open from this terminal (macOS asks once whether it may control
419
419
  Ghostty), like \`leftoff go\`. Without Ghostty's AppleScript (Ghostty 1.3.0+,
420
420
  macOS) it prints one \`cd … && claude --resume …\` line per session.
421
- Exit codes: 0 done (or nothing to do), 1 error, 2 cancelled, 3 some didn't open.`;function mr(n){return n.title||"(untitled)"}function Ah(n,e){return[n.repoLabel,n.branch,n.lastAliveAt?"last seen "+sa(n.lastAliveAt,e):null,"cp:"+(n.shortId||ze(n.id))].filter(Boolean).join(" · ")}var Th=n=>{let e=new Date(n);return isNaN(e)?"":e.toLocaleString(void 0,{weekday:"short",hour:"numeric",minute:"2-digit"})};async function tS({pos:n=[],flags:e={},api:t,out:s=process.stdout,err:o=process.stderr,input:r=process.stdin,exec:i=qt,platform:a=process.platform,now:l=Date.now()}={}){let c=v=>s.write(v+`
421
+ Exit codes: 0 done (or nothing to do), 1 error, 2 cancelled, 3 some didn't open.`;function mr(n){return n.title||"(untitled)"}function Oh(n,e){return[n.repoLabel,n.branch,n.lastAliveAt?"last seen "+ia(n.lastAliveAt,e):null,"cp:"+(n.shortId||We(n.id))].filter(Boolean).join(" · ")}var Ch=n=>{let e=new Date(n);return isNaN(e)?"":e.toLocaleString(void 0,{weekday:"short",hour:"numeric",minute:"2-digit"})};async function aS({pos:n=[],flags:e={},api:t,out:s=process.stdout,err:o=process.stderr,input:r=process.stdin,exec:i=Xt,platform:a=process.platform,now:l=Date.now()}={}){let c=v=>s.write(v+`
422
422
  `),u=v=>(o.write(ee.red("✖ ")+v+`
423
- `),1),d=new URLSearchParams;e.since&&e.since!==!0&&d.set("since",String(e.since)),e.repo&&e.repo!==!0&&d.set("repo",String(e.repo));let f;try{f=await t("GET","/v1/restore"+(d.toString()?"?"+d:""))}catch(v){return u(/fetch failed|ECONNREFUSED|abort|timed? ?out/i.test(String(v.message))?"The Leftoff daemon isn't running. Start it with: leftoff serve (or leftoff status)":v.message)}let h=f.sessions||[],p=n.join(" ").trim();if(p){let v;try{v=(await t("GET","/v1/sessions?limit=500&archived=1")).sessions||[]}catch(N){return u(N.message)}let b=v.filter(N=>N.isLocal!==!1),y=b.find(N=>N.id===p||(N.shortId||ze(N.id))===p.replace(/^cp:/,"")),_=y?[{s:y,score:1/0}]:b.map(N=>({s:N,score:cf(p,N)})).filter(N=>N.score>0).sort((N,M)=>M.score-N.score||Date.parse(M.s.lastActivityAt||0)-Date.parse(N.s.lastActivityAt||0));if(!_.length)return u("No session on this machine matches “"+p+"”.");let A=_[0].s,O=_.filter(N=>N.score===_[0].score);if(O.length>1){c("More than one session matches “"+p+"”:");for(let N of O.slice(0,8))c(" "+mr(N.s)+ee.dim(" "+[N.s.repoLabel,N.s.live==="open"?"running":null,"cp:"+(N.s.shortId||ze(N.s.id))].filter(Boolean).join(" · ")));return c("Use the id: leftoff restore <id>"),1}if(A.live==="open")return c("“"+F(mr(A),60)+"” is already running. Jump to it: leftoff go "+(A.shortId||ze(A.id))),0;h=[h.find(N=>N.id===A.id)||{id:A.id,shortId:A.shortId,title:A.title,repoLabel:A.repoLabel,cwd:A.cwd,lastAliveAt:A.lastActivityAt}]}if(!h.length){let v=f.restartAt?f.restartKind==="boot"?"this Mac restarted ("+Th(f.restartAt)+")":"the daemon started ("+Th(f.restartAt)+")":"the last restart";return c("Nothing to restore: no session on "+(f.machine||"this machine")+" was open when "+v+" that isn't running again."+(e.since?"":" Look further back with --since=48h.")),0}let m=h,g=!p&&!e.all&&!e.yes&&!e["dry-run"]&&!e.print&&r.isTTY&&s.isTTY;if(!p&&!g&&!e.all&&!e.yes&&!e["dry-run"]&&!e.print){c(h.length+" session"+(h.length===1?" was":"s were")+" open before the restart:");for(let v of h)c(" "+mr(v)+ee.dim(" "+Ah(v,l)));return c("Restore them all: leftoff restore --all (or one: leftoff restore <name|id>)"),0}if(g){let v=h.length+" session"+(h.length===1?" was":"s were")+" open before "+(f.restartKind==="boot"?"your Mac restarted":"Leftoff restarted")+(f.restartAt?" ("+Th(f.restartAt)+")":"");if(m=await xw(v,h.map(b=>({label:F(mr(b),60),hint:Ah(b,l),value:b})),{input:r,output:s}),!m)return c(ee.dim("Cancelled.")),2;if(!m.length)return c(ee.dim("Nothing picked.")),2}if(e["dry-run"]){c("Would restore "+m.length+" session"+(m.length===1?"":"s")+" (nothing opened):");for(let v of m)c(" "+mr(v)+ee.dim(" "+Ah(v,l))+`
424
- `+v.command);return 0}let S=v=>{v&&c(ee.yellow("!")+" Couldn't open Ghostty tabs: "+v+". Run these yourself, one per tab:");for(let b of m)c(b.command)};if(e.print)return S(null),0;if(!m.every(v=>v.command))try{m=(await t("POST","/v1/restore",{ids:m.map(b=>b.id),print:!0})).sessions}catch(v){return u(v.message)}let k=m.filter(v=>!v.blocked),w=new Map;if(k.length){c(ee.dim("Opening "+k.length+" Ghostty tab"+(k.length===1?"":"s")+"…"));let v=await Sc(k.map(b=>({cwd:b.cwd,claude:b.claude})),{exec:i,platform:a});if(v.how==="print")return S(v.message),3;k.forEach((b,y)=>w.set(b.id,v.results[y]||{}))}let x=m.filter(v=>(w.get(v.id)||{}).ok).map(v=>v.id);if(x.length)try{await t("POST","/v1/restore/launched",{ids:x})}catch{}return m.forEach(v=>{let b=v.blocked?{ok:!1,error:v.error}:w.get(v.id)||{};c((b.ok?ee.green("✔"):ee.red("✖"))+" "+mr(v)+ee.dim(" "+[v.repoLabel,"cp:"+(v.shortId||ze(v.id))].filter(Boolean).join(" · "))+(b.ok?"":" "+b.error+(v.blocked?"":`
425
- `+v.command)))}),c(x.length===m.length?ee.green("Restored "+x.length+".")+ee.dim(" Each tab resumes its session by id, in its folder."):ee.yellow(x.length+" of "+m.length+" opened.")),x.length===m.length?0:3}xe();xn();Ls();pl();gl();ns();Te();var tT=new Set(["since","to","repo","machine","length"]),nT={r:"repo",m:"machine",s:"since"};function sT(n=[]){let e={repos:[],machines:[],rest:[]};for(let t=0;t<n.length;t++){let s=n[t],o=s.match(/^--?([A-Za-z-]+)(?:=(.*))?$/);if(!o){e.rest.push(s);continue}let r=nT[o[1]]||o[1],i=o[2];if(tT.has(r)&&i===void 0)if(t+1<n.length&&!/^--/.test(n[t+1]))i=n[++t];else throw new Error("--"+r+" needs a value");r==="repo"?e.repos.push(...String(i).split(",").map(a=>a.trim()).filter(Boolean)):r==="machine"?e.machines.push(...String(i).split(",").map(a=>a.trim()).filter(Boolean)):r==="short"||r==="long"||r==="informative"?e.length=r:e[r]=i===void 0?!0:i}return e.all&&(e.repos=[]),e}function nS(){return`leftoff catchup: one recap of what happened across your sessions
423
+ `),1),d=new URLSearchParams;e.since&&e.since!==!0&&d.set("since",String(e.since)),e.repo&&e.repo!==!0&&d.set("repo",String(e.repo));let f;try{f=await t("GET","/v1/restore"+(d.toString()?"?"+d:""))}catch(v){return u(/fetch failed|ECONNREFUSED|abort|timed? ?out/i.test(String(v.message))?"The Leftoff daemon isn't running. Start it with: leftoff serve (or leftoff status)":v.message)}let h=f.sessions||[],p=n.join(" ").trim();if(p){let v;try{v=(await t("GET","/v1/sessions?limit=500&archived=1")).sessions||[]}catch(I){return u(I.message)}let b=v.filter(I=>I.isLocal!==!1),y=b.find(I=>I.id===p||(I.shortId||We(I.id))===p.replace(/^cp:/,"")),_=y?[{s:y,score:1/0}]:b.map(I=>({s:I,score:ff(p,I)})).filter(I=>I.score>0).sort((I,M)=>M.score-I.score||Date.parse(M.s.lastActivityAt||0)-Date.parse(I.s.lastActivityAt||0));if(!_.length)return u("No session on this machine matches “"+p+"”.");let A=_[0].s,O=_.filter(I=>I.score===_[0].score);if(O.length>1){c("More than one session matches “"+p+"”:");for(let I of O.slice(0,8))c(" "+mr(I.s)+ee.dim(" "+[I.s.repoLabel,I.s.live==="open"?"running":null,"cp:"+(I.s.shortId||We(I.s.id))].filter(Boolean).join(" · ")));return c("Use the id: leftoff restore <id>"),1}if(A.live==="open")return c("“"+F(mr(A),60)+"” is already running. Jump to it: leftoff go "+(A.shortId||We(A.id))),0;h=[h.find(I=>I.id===A.id)||{id:A.id,shortId:A.shortId,title:A.title,repoLabel:A.repoLabel,cwd:A.cwd,lastAliveAt:A.lastActivityAt}]}if(!h.length){let v=f.restartAt?f.restartKind==="boot"?"this Mac restarted ("+Ch(f.restartAt)+")":"the daemon started ("+Ch(f.restartAt)+")":"the last restart";return c("Nothing to restore: no session on "+(f.machine||"this machine")+" was open when "+v+" that isn't running again."+(e.since?"":" Look further back with --since=48h.")),0}let m=h,g=!p&&!e.all&&!e.yes&&!e["dry-run"]&&!e.print&&r.isTTY&&s.isTTY;if(!p&&!g&&!e.all&&!e.yes&&!e["dry-run"]&&!e.print){c(h.length+" session"+(h.length===1?" was":"s were")+" open before the restart:");for(let v of h)c(" "+mr(v)+ee.dim(" "+Oh(v,l)));return c("Restore them all: leftoff restore --all (or one: leftoff restore <name|id>)"),0}if(g){let v=h.length+" session"+(h.length===1?" was":"s were")+" open before "+(f.restartKind==="boot"?"your Mac restarted":"Leftoff restarted")+(f.restartAt?" ("+Ch(f.restartAt)+")":"");if(m=await Cw(v,h.map(b=>({label:F(mr(b),60),hint:Oh(b,l),value:b})),{input:r,output:s}),!m)return c(ee.dim("Cancelled.")),2;if(!m.length)return c(ee.dim("Nothing picked.")),2}if(e["dry-run"]){c("Would restore "+m.length+" session"+(m.length===1?"":"s")+" (nothing opened):");for(let v of m)c(" "+mr(v)+ee.dim(" "+Oh(v,l))+`
424
+ `+v.command);return 0}let S=v=>{v&&c(ee.yellow("!")+" Couldn't open Ghostty tabs: "+v+". Run these yourself, one per tab:");for(let b of m)c(b.command)};if(e.print)return S(null),0;if(!m.every(v=>v.command))try{m=(await t("POST","/v1/restore",{ids:m.map(b=>b.id),print:!0})).sessions}catch(v){return u(v.message)}let k=m.filter(v=>!v.blocked),w=new Map;if(k.length){c(ee.dim("Opening "+k.length+" Ghostty tab"+(k.length===1?"":"s")+"…"));let v=await bc(k.map(b=>({cwd:b.cwd,claude:b.claude})),{exec:i,platform:a});if(v.how==="print")return S(v.message),3;k.forEach((b,y)=>w.set(b.id,v.results[y]||{}))}let x=m.filter(v=>(w.get(v.id)||{}).ok).map(v=>v.id);if(x.length)try{await t("POST","/v1/restore/launched",{ids:x})}catch{}return m.forEach(v=>{let b=v.blocked?{ok:!1,error:v.error}:w.get(v.id)||{};c((b.ok?ee.green("✔"):ee.red("✖"))+" "+mr(v)+ee.dim(" "+[v.repoLabel,"cp:"+(v.shortId||We(v.id))].filter(Boolean).join(" · "))+(b.ok?"":" "+b.error+(v.blocked?"":`
425
+ `+v.command)))}),c(x.length===m.length?ee.green("Restored "+x.length+".")+ee.dim(" Each tab resumes its session by id, in its folder."):ee.yellow(x.length+" of "+m.length+" opened.")),x.length===m.length?0:3}xe();Tn();Ps();gl();wl();rs();Te();var lT=new Set(["since","to","repo","machine","length"]),cT={r:"repo",m:"machine",s:"since"};function uT(n=[]){let e={repos:[],machines:[],rest:[]};for(let t=0;t<n.length;t++){let s=n[t],o=s.match(/^--?([A-Za-z-]+)(?:=(.*))?$/);if(!o){e.rest.push(s);continue}let r=cT[o[1]]||o[1],i=o[2];if(lT.has(r)&&i===void 0)if(t+1<n.length&&!/^--/.test(n[t+1]))i=n[++t];else throw new Error("--"+r+" needs a value");r==="repo"?e.repos.push(...String(i).split(",").map(a=>a.trim()).filter(Boolean)):r==="machine"?e.machines.push(...String(i).split(",").map(a=>a.trim()).filter(Boolean)):r==="short"||r==="long"||r==="informative"?e.length=r:e[r]=i===void 0?!0:i}return e.all&&(e.repos=[]),e}function lS(){return`leftoff catchup: one recap of what happened across your sessions
426
426
 
427
427
  leftoff catchup since your last catch-up (else the last 24 hours), every repo
428
428
  leftoff catchup --since 3d a duration: 90m, 12h, 3d, 1w
@@ -436,51 +436,51 @@ Exit codes: 0 done (or nothing to do), 1 error, 2 cancelled, 3 some didn't open.
436
436
  leftoff catchup --short | --long how much each session says (default: config summary.length)
437
437
  leftoff catchup --list the saved catch-ups, newest first
438
438
 
439
- Saved as catchups/YYYY-MM-DDTHHMM.md in your Leftoff folder; the board lists them under Catch me up.`}function oT(n){return Ye(n).body.replace(/^\n+/,"")}var sS=n=>/fetch failed|ECONNREFUSED|aborted|timeout|ECONNRESET/i.test(String(n&&n.message)+(n&&n.cause?n.cause.code:""));async function oS({argv:n=[],api:e,out:t=o=>process.stdout.write(o),err:s=o=>process.stderr.write(o)}={}){let o;try{o=sT(n)}catch(c){return s("leftoff catchup: "+c.message+`
439
+ Saved as catchups/YYYY-MM-DDTHHMM.md in your Leftoff folder; the board lists them under Catch me up.`}function dT(n){return Ye(n).body.replace(/^\n+/,"")}var cS=n=>/fetch failed|ECONNREFUSED|aborted|timeout|ECONNRESET/i.test(String(n&&n.message)+(n&&n.cause?n.cause.code:""));async function uS({argv:n=[],api:e,out:t=o=>process.stdout.write(o),err:s=o=>process.stderr.write(o)}={}){let o;try{o=uT(n)}catch(c){return s("leftoff catchup: "+c.message+`
440
440
 
441
- `+nS()+`
442
- `),2}if(o.help||o.h)return t(nS()+`
443
- `),0;o.rest.length&&!o.since&&(o.since=o.rest.join(" "));let r={since:o.since===!0?void 0:o.since,to:o.to||null,repos:o.repos,machines:o.machines,save:!o.print,length:o.length};if(o.list){let c=null;if(!o.offline)try{c=(await e("GET","/v1/catchups")).catchups}catch(u){if(!sS(u))throw u}if(!c){let u=Z();c=new xt(u,de(Q(u))).listCatchups()}if(o.json)return t(JSON.stringify(c,null,2)+`
441
+ `+lS()+`
442
+ `),2}if(o.help||o.h)return t(lS()+`
443
+ `),0;o.rest.length&&!o.since&&(o.since=o.rest.join(" "));let r={since:o.since===!0?void 0:o.since,to:o.to||null,repos:o.repos,machines:o.machines,save:!o.print,length:o.length};if(o.list){let c=null;if(!o.offline)try{c=(await e("GET","/v1/catchups")).catchups}catch(u){if(!cS(u))throw u}if(!c){let u=Z();c=new Tt(u,de(Q(u))).listCatchups()}if(o.json)return t(JSON.stringify(c,null,2)+`
444
444
  `),0;if(!c.length)return t(`No catch-ups yet. Make one: leftoff catchup
445
445
  `),0;for(let u of c)t(u.name.padEnd(18)+" "+String(u.label||"").padEnd(26)+" "+String(u.sessions==null?"?":u.sessions).padStart(3)+" sessions"+(u.needsYou?" · "+u.needsYou+" need you":"")+(u.machine?" · "+u.machine:"")+`
446
- `);return 0}let i=null,a="daemon";if(!o.offline)try{i=await e("POST","/v1/catchups",r)}catch(c){if(!sS(c))return s("leftoff catchup: "+c.message+`
447
- `),/window|can't read/.test(c.message)?2:1}if(!i){a="folder";let c=Z();Oe(c);let u=Q(c),d=de(u),f=new xt(c,d),h;try{let m=(()=>{try{return new Ct(c,d).view({fresh:!0})}catch{return{agents:[]}}})();h=await ii(f.loadAll().concat(Pl(m)),{...r,cfg:u,machine:d,lastAt:f.lastCatchupAt(),summarizer:new At(u,{root:c}),lists:new Zn(c,d).all({includeArchived:!1}).map(g=>gt(g)).concat(Rl(m))})}catch(m){return s("leftoff catchup: "+m.message+`
446
+ `);return 0}let i=null,a="daemon";if(!o.offline)try{i=await e("POST","/v1/catchups",r)}catch(c){if(!cS(c))return s("leftoff catchup: "+c.message+`
447
+ `),/window|can't read/.test(c.message)?2:1}if(!i){a="folder";let c=Z();Oe(c);let u=Q(c),d=de(u),f=new Tt(c,d),h;try{let m=(()=>{try{return new It(c,d).view({fresh:!0})}catch{return{agents:[]}}})();h=await li(f.loadAll().concat(Fl(m)),{...r,cfg:u,machine:d,lastAt:f.lastCatchupAt(),summarizer:new _t(u,{root:c}),lists:new ts(c,d).all({includeArchived:!1}).map(g=>wt(g)).concat(jl(m))})}catch(m){return s("leftoff catchup: "+m.message+`
448
448
  `),m.badWindow?2:1}let p=r.save?f.writeCatchup(h.fileName,h.markdown):null;i={name:p?p.name:null,path:p?"catchups/"+p.name+".md":null,saved:!!p,recap:h.recap,summary:h.summary,markdown:h.markdown}}if(o.json)return t(JSON.stringify({...i,via:a},null,2)+`
449
- `),0;t(oT(i.markdown));let l=i.recap.selection&&i.recap.selection.unmatched;return l&&l.length&&s("! no repo with activity in this window matches: "+l.join(", ")+`
449
+ `),0;t(dT(i.markdown));let l=i.recap.selection&&i.recap.selection.unmatched;return l&&l.length&&s("! no repo with activity in this window matches: "+l.join(", ")+`
450
450
  `),i.recap.window.fallback==="no earlier catch-up"&&s(`· no earlier catch-up, so this covers the last 24 hours
451
451
  `),i.saved&&s(`
452
452
  Saved `+i.path+(a==="folder"?" (daemon not running: built from the folder)":"")+` · on the board: Catch me up
453
- `),0}xe();gl();xn();Gu();Ur();Te();Pn();var rT=n=>/fetch failed|ECONNREFUSED|abort|timed? ?out/i.test(String(n&&n.message+(n.cause?" "+n.cause.code:"")));function iT(n){let e={sub:null,args:[],all:!1,json:!1,repo:null,pick:null,name:null,offline:!1};for(let s=0;s<n.length;s++){let o=n[s],r;if(o==="--all")e.all=!0;else if(o==="--json")e.json=!0;else if(o==="--offline")e.offline=!0;else if(r=o.match(/^--repo(?:=(.*))?$/))e.repo=r[1]!=null?r[1]:n[++s];else if(r=o.match(/^--name(?:=(.*))?$/))e.name=r[1]!=null?r[1]:n[++s];else if(r=o.match(/^--pick(?:=(\d+))?$/))r[1]?e.pick=Number(r[1]):/^\d+$/.test(n[s+1]||"")?e.pick=Number(n[++s]):e.pick=!0;else if(o==="--"){e.args.push(...n.slice(s+1));break}else!e.sub&&!e.args.length?e.sub=o:e.args.push(o)}let t=["ls","list","show","done","tick","check","undo","uncheck","add","rename","archive","unarchive","new","capture","help"];return e.sub&&!t.includes(e.sub)&&(e.args.unshift(e.sub),e.sub="show"),e.sub||(e.sub="ls"),e}function ou(n,{color:e=ee,number:t=null}={}){let s=[],o=(t?t+". ":"")+e.bold(n.name)+" "+n.progress+(n.archived?" · archived":"")+" "+e.dim(n.ref+(n.repo,""));s.push(o);for(let r of n.items||[])s.push(" "+String(r.n).padStart(2)+" "+(r.done?e.green("[x]"):"[ ]")+" "+(r.done?e.dim(r.text):r.text));return s.join(`
454
- `)}async function rS({argv:n=[],api:e,out:t=r=>process.stdout.write(r),err:s=r=>process.stderr.write(r),root:o}={}){let r=iT(n),i=(p,m=1)=>(s("leftoff todo: "+p+`
455
- `),m),a=null,l=()=>{if(a)return a;let p=o||Z();return Oe(p),a=new Zn(p,de(Q(p))),a},c=async(p,m,g)=>{if(r.offline||!e)return null;try{return await e(p,m,g)}catch(S){if(rT(S))return null;throw S}},u=p=>gt(l().resolve(p)),d=async p=>{let m=await c("GET","/v1/lists/resolve?ref="+encodeURIComponent(p));return m?m.list:u(p)},f=async(p,m)=>{let g=await d(p),S=await c("POST","/v1/lists/"+g.ref+"/change",m);if(S)return S;let k=l(),w={};if(m.text){let x=k.findItem(k.resolve(g.ref),m.text);if(!x)throw new Error('no item on "'+g.name+'" matches "'+m.text+'"; give its number');m={[m.undo?"undone":"done"]:[x.id]}}if(m.done||m.undone){let x=k.setDone(g.ref,[].concat(m.done||m.undone),!!m.done);w.missing=x.missing}return m.add&&(w.added=k.addItem(g.ref,m.add).item.id),m.name&&k.rename(g.ref,m.name),m.archived!=null&&k.archive(g.ref,m.archived),{ok:!0,list:u(g.ref),...w}},h=p=>p.flatMap(m=>String(m).split(",")).map(m=>m.replace(/^#/,"")).filter(Boolean);try{switch(r.sub){case"help":return t(lT),0;case"ls":case"list":{let p=null,m=await c("GET","/v1/lists"+(r.all?"?all=1":""));if(p=m?m.lists:l().all({includeArchived:r.all}).map(S=>gt(S)),r.repo&&(p=p.filter(S=>S.repoDir===r.repo||S.repo===r.repo||String(S.repo||"").toLowerCase().includes(String(r.repo).toLowerCase()))),r.all||(p=p.filter(S=>!S.archived)),r.json)return t(JSON.stringify(p,null,2)+`
453
+ `),0}xe();wl();Tn();$u();Hr();Te();Fn();var fT=n=>/fetch failed|ECONNREFUSED|abort|timed? ?out/i.test(String(n&&n.message+(n.cause?" "+n.cause.code:"")));function hT(n){let e={sub:null,args:[],all:!1,json:!1,repo:null,pick:null,name:null,offline:!1};for(let s=0;s<n.length;s++){let o=n[s],r;if(o==="--all")e.all=!0;else if(o==="--json")e.json=!0;else if(o==="--offline")e.offline=!0;else if(r=o.match(/^--repo(?:=(.*))?$/))e.repo=r[1]!=null?r[1]:n[++s];else if(r=o.match(/^--name(?:=(.*))?$/))e.name=r[1]!=null?r[1]:n[++s];else if(r=o.match(/^--pick(?:=(\d+))?$/))r[1]?e.pick=Number(r[1]):/^\d+$/.test(n[s+1]||"")?e.pick=Number(n[++s]):e.pick=!0;else if(o==="--"){e.args.push(...n.slice(s+1));break}else!e.sub&&!e.args.length?e.sub=o:e.args.push(o)}let t=["ls","list","show","done","tick","check","undo","uncheck","add","rename","archive","unarchive","new","capture","help"];return e.sub&&!t.includes(e.sub)&&(e.args.unshift(e.sub),e.sub="show"),e.sub||(e.sub="ls"),e}function iu(n,{color:e=ee,number:t=null}={}){let s=[],o=(t?t+". ":"")+e.bold(n.name)+" "+n.progress+(n.archived?" · archived":"")+" "+e.dim(n.ref+(n.repo,""));s.push(o);for(let r of n.items||[])s.push(" "+String(r.n).padStart(2)+" "+(r.done?e.green("[x]"):"[ ]")+" "+(r.done?e.dim(r.text):r.text));return s.join(`
454
+ `)}async function dS({argv:n=[],api:e,out:t=r=>process.stdout.write(r),err:s=r=>process.stderr.write(r),root:o}={}){let r=hT(n),i=(p,m=1)=>(s("leftoff todo: "+p+`
455
+ `),m),a=null,l=()=>{if(a)return a;let p=o||Z();return Oe(p),a=new ts(p,de(Q(p))),a},c=async(p,m,g)=>{if(r.offline||!e)return null;try{return await e(p,m,g)}catch(S){if(fT(S))return null;throw S}},u=p=>wt(l().resolve(p)),d=async p=>{let m=await c("GET","/v1/lists/resolve?ref="+encodeURIComponent(p));return m?m.list:u(p)},f=async(p,m)=>{let g=await d(p),S=await c("POST","/v1/lists/"+g.ref+"/change",m);if(S)return S;let k=l(),w={};if(m.text){let x=k.findItem(k.resolve(g.ref),m.text);if(!x)throw new Error('no item on "'+g.name+'" matches "'+m.text+'"; give its number');m={[m.undo?"undone":"done"]:[x.id]}}if(m.done||m.undone){let x=k.setDone(g.ref,[].concat(m.done||m.undone),!!m.done);w.missing=x.missing}return m.add&&(w.added=k.addItem(g.ref,m.add).item.id),m.name&&k.rename(g.ref,m.name),m.archived!=null&&k.archive(g.ref,m.archived),{ok:!0,list:u(g.ref),...w}},h=p=>p.flatMap(m=>String(m).split(",")).map(m=>m.replace(/^#/,"")).filter(Boolean);try{switch(r.sub){case"help":return t(mT),0;case"ls":case"list":{let p=null,m=await c("GET","/v1/lists"+(r.all?"?all=1":""));if(p=m?m.lists:l().all({includeArchived:r.all}).map(S=>wt(S)),r.repo&&(p=p.filter(S=>S.repoDir===r.repo||S.repo===r.repo||String(S.repo||"").toLowerCase().includes(String(r.repo).toLowerCase()))),r.all||(p=p.filter(S=>!S.archived)),r.json)return t(JSON.stringify(p,null,2)+`
456
456
  `),0;if(!p.length)return t("No "+(r.all?"":"open ")+`lists yet. When Claude writes you a checklist, save it with /cp-todo in the session, or Save on the board.
457
457
  `),0;let g=null;return p.forEach((S,k)=>{let w=S.repo||"no repo";w!==g&&(t((k?`
458
458
  `:"")+ee.dim("── "+w)+`
459
- `),g=w),t(ou(S,{number:k+1})+`
459
+ `),g=w),t(iu(S,{number:k+1})+`
460
460
  `)}),t(ee.dim(`
461
461
  Tick one: leftoff todo done <list> <n> · on the board: Lists`)+`
462
462
  `),0}case"show":{if(!r.args.length)return i("which list? leftoff todo show <list>",2);let p=await d(r.args.join(" "));return r.json?(t(JSON.stringify(p,null,2)+`
463
- `),0):(t(ou(p)+`
464
- `),0)}case"done":case"tick":case"check":case"undo":case"uncheck":{let p=r.sub==="undo"||r.sub==="uncheck",[m,...g]=r.args;if(!m)return i("usage: leftoff todo "+r.sub+" <list> <n…>",2);if(!g.length&&/^#?\d+(,\d+)*$/.test(m)){let b=await c("GET","/v1/lists"),y=(b?b.lists:l().all().map(_=>gt(_))).filter(_=>!_.archived);if(y.length!==1)return i("which list? leftoff todo "+r.sub+" <list> "+m+(y.length?" (open: "+y.map(_=>_.ref).join(", ")+")":""),2);g=[m],m=y[0].ref}if(!g.length)return i("which item? leftoff todo "+r.sub+" "+m+" <n>",2);let S=h(g),k=S.every(b=>/^\d+$/.test(b))?{[p?"undone":"done"]:S.map(Number)}:{text:g.join(" "),undo:p},w=await f(m,k),x=w.list;if(r.json)return t(JSON.stringify(w,null,2)+`
463
+ `),0):(t(iu(p)+`
464
+ `),0)}case"done":case"tick":case"check":case"undo":case"uncheck":{let p=r.sub==="undo"||r.sub==="uncheck",[m,...g]=r.args;if(!m)return i("usage: leftoff todo "+r.sub+" <list> <n…>",2);if(!g.length&&/^#?\d+(,\d+)*$/.test(m)){let b=await c("GET","/v1/lists"),y=(b?b.lists:l().all().map(_=>wt(_))).filter(_=>!_.archived);if(y.length!==1)return i("which list? leftoff todo "+r.sub+" <list> "+m+(y.length?" (open: "+y.map(_=>_.ref).join(", ")+")":""),2);g=[m],m=y[0].ref}if(!g.length)return i("which item? leftoff todo "+r.sub+" "+m+" <n>",2);let S=h(g),k=S.every(b=>/^\d+$/.test(b))?{[p?"undone":"done"]:S.map(Number)}:{text:g.join(" "),undo:p},w=await f(m,k),x=w.list;if(r.json)return t(JSON.stringify(w,null,2)+`
465
465
  `),0;let v=k.text?x.items.filter(b=>b.done!==p&&b.text.toLowerCase().includes(String(k.text).toLowerCase().split(" ")[0])).slice(0,1):x.items.filter(b=>(k.done||k.undone).includes(b.n));return t((p?"↺ ":"✓ ")+x.name+": "+v.map(b=>"#"+b.n+" "+F(b.text,70)).join("; ")+(p?" unticked":"")+" · "+x.progress+` done
466
466
  `),w.missing&&w.missing.length&&s("! no item "+w.missing.join(", ")+` on that list
467
467
  `),0}case"add":{let[p,...m]=r.args;if(!p||!m.length)return i("usage: leftoff todo add <list> <text>",2);let g=await f(p,{add:m.join(" ")});return r.json?(t(JSON.stringify(g,null,2)+`
468
468
  `),0):(t("+ "+g.list.name+": #"+g.added+" "+F(m.join(" "),80)+" · "+g.list.progress+`
469
469
  `),0)}case"rename":{let[p,...m]=r.args;if(!p||!m.length)return i("usage: leftoff todo rename <list> <new name>",2);let g=await f(p,{name:m.join(" ")});return t("Renamed: "+g.list.name+" ("+g.list.ref+`)
470
470
  `),0}case"archive":case"unarchive":{if(!r.args.length)return i("usage: leftoff todo "+r.sub+" <list>",2);let p=await f(r.args.join(" "),{archived:r.sub==="archive"});return t((r.sub==="archive"?"Archived: ":"Back on the board: ")+p.list.name+`
471
- `),0}case"new":{let[p,...m]=r.args;if(!p||!m.length)return i('usage: leftoff todo new "<name>" [--repo NAME] "<item>" ["<item>"…]',2);let g={name:p,items:m.map(k=>({text:k})),repo:r.repo||null},S=await c("POST","/v1/lists",g);return S||(S={list:gt(l().create({name:p,items:g.items,repo:r.repo||null}))}),r.json?(t(JSON.stringify(S.list,null,2)+`
472
- `),0):(t("Saved: "+ou(S.list)+`
473
- `),0)}case"capture":return await aT(r,{call:c,local:l,out:t,err:s,fail:i})}}catch(p){return i(p.message,/no list|no item|matches \d+ lists|which/.test(p.message)?3:1)}return i("unknown command "+r.sub,2)}async function aT(n,{call:e,local:t,out:s,err:o,fail:r}){let i=n.args.join(" ").trim();if(!i)return r("usage: leftoff todo capture <session name|id> [--pick [N]] [--name NAME]",2);let a=null,l=await e("GET","/v1/sessions?limit=500&archived=1");if(l)a=l.sessions;else{let S=t();a=new xt(S.root,S.machine).loadAll().map(({session:k})=>({...k,repoName:me(k),shortId:ze(k.id)}))}let c=i.toLowerCase(),u=[S=>S.id===i,S=>S.id.startsWith(c)||S.shortId&&S.shortId===c.replace(/^cp:/,""),S=>String(S.title||"").toLowerCase()===c,S=>String(S.title||"").toLowerCase().includes(c)],d=null;for(let S of u){let k=a.filter(S);if(k.length===1){d=k[0];break}if(k.length>1)return r('"'+i+'" matches '+k.length+" sessions: "+k.slice(0,6).map(w=>(w.title||"untitled")+" ("+w.id.slice(0,8)+")").join(", ")+". Use the id.",3)}if(!d)return r('no session matches "'+i+'" (give its name or id; `leftoff status` lists them)',3);let f,h=await e("GET","/v1/sessions/"+encodeURIComponent(d.id)+"/lists/find");if(h)f=h.candidates;else{let S=d.transcriptPath||vo(d.id);if(!S)return r(`Claude Code's transcript for "`+(d.title||d.id)+`" isn't on this machine (looked in ~/.claude/projects)`,1);f=pa(nn(S,{maxBytes:32*1024*1024}).messages,{fallback:d.title}).reverse().map(k=>({...k,count:k.items.length}))}let p=d.title||"untitled session";if(!f.length)return s('No checklists in "'+p+`". Leftoff looks for "- [ ]" checklists and numbered or bulleted lists under a heading like "Next steps".
471
+ `),0}case"new":{let[p,...m]=r.args;if(!p||!m.length)return i('usage: leftoff todo new "<name>" [--repo NAME] "<item>" ["<item>"…]',2);let g={name:p,items:m.map(k=>({text:k})),repo:r.repo||null},S=await c("POST","/v1/lists",g);return S||(S={list:wt(l().create({name:p,items:g.items,repo:r.repo||null}))}),r.json?(t(JSON.stringify(S.list,null,2)+`
472
+ `),0):(t("Saved: "+iu(S.list)+`
473
+ `),0)}case"capture":return await pT(r,{call:c,local:l,out:t,err:s,fail:i})}}catch(p){return i(p.message,/no list|no item|matches \d+ lists|which/.test(p.message)?3:1)}return i("unknown command "+r.sub,2)}async function pT(n,{call:e,local:t,out:s,err:o,fail:r}){let i=n.args.join(" ").trim();if(!i)return r("usage: leftoff todo capture <session name|id> [--pick [N]] [--name NAME]",2);let a=null,l=await e("GET","/v1/sessions?limit=500&archived=1");if(l)a=l.sessions;else{let S=t();a=new Tt(S.root,S.machine).loadAll().map(({session:k})=>({...k,repoName:ge(k),shortId:We(k.id)}))}let c=i.toLowerCase(),u=[S=>S.id===i,S=>S.id.startsWith(c)||S.shortId&&S.shortId===c.replace(/^cp:/,""),S=>String(S.title||"").toLowerCase()===c,S=>String(S.title||"").toLowerCase().includes(c)],d=null;for(let S of u){let k=a.filter(S);if(k.length===1){d=k[0];break}if(k.length>1)return r('"'+i+'" matches '+k.length+" sessions: "+k.slice(0,6).map(w=>(w.title||"untitled")+" ("+w.id.slice(0,8)+")").join(", ")+". Use the id.",3)}if(!d)return r('no session matches "'+i+'" (give its name or id; `leftoff status` lists them)',3);let f,h=await e("GET","/v1/sessions/"+encodeURIComponent(d.id)+"/lists/find");if(h)f=h.candidates;else{let S=d.transcriptPath||Ao(d.id);if(!S)return r(`Claude Code's transcript for "`+(d.title||d.id)+`" isn't on this machine (looked in ~/.claude/projects)`,1);f=ya(on(S,{maxBytes:32*1024*1024}).messages,{fallback:d.title}).reverse().map(k=>({...k,count:k.items.length}))}let p=d.title||"untitled session";if(!f.length)return s('No checklists in "'+p+`". Leftoff looks for "- [ ]" checklists and numbered or bulleted lists under a heading like "Next steps".
474
474
  `),4;if(n.pick===!0){s('Lists Claude wrote in "'+p+`" (newest first):
475
475
  `);for(let S of f)s(" "+String(S.n).padStart(2)+". "+S.name+" · "+S.count+" items"+(S.savedAs?ee.dim(" · saved as "+S.savedAs.ref):"")+`
476
476
  `+S.items.slice(0,3).map(k=>ee.dim(" "+F(k.text,70))).join(`
477
477
  `)+`
478
478
  `);return s(ee.dim("Save one: leftoff todo capture "+JSON.stringify(i)+' --pick <n> [--name "…"]')+`
479
- `),0}let m=n.pick?f.find(S=>S.n===n.pick):f[0];if(!m)return r("no list "+n.pick+" in that session (--pick lists them)",3);let g=await e("POST","/v1/lists",{sessionId:d.id,fromTranscript:m.n,name:n.name||void 0});if(!g){let S=t();g={list:gt(S.create({name:n.name||m.name,items:m.items,repo:d.repoName&&d.repoName!=="no-repo"?d.repoName:null,sourceSession:d.id,sourceMachine:d.machine||S.machine,sourceTitle:d.title||null}))}}return n.json?(s(JSON.stringify(g.list,null,2)+`
479
+ `),0}let m=n.pick?f.find(S=>S.n===n.pick):f[0];if(!m)return r("no list "+n.pick+" in that session (--pick lists them)",3);let g=await e("POST","/v1/lists",{sessionId:d.id,fromTranscript:m.n,name:n.name||void 0});if(!g){let S=t();g={list:wt(S.create({name:n.name||m.name,items:m.items,repo:d.repoName&&d.repoName!=="no-repo"?d.repoName:null,sourceSession:d.id,sourceMachine:d.machine||S.machine,sourceTitle:d.title||null}))}}return n.json?(s(JSON.stringify(g.list,null,2)+`
480
480
  `),0):(s('Saved from "'+p+`":
481
- `+ou(g.list)+`
481
+ `+iu(g.list)+`
482
482
  `),s(ee.dim("Tick: leftoff todo done "+g.list.ref+" <n> · in the session: /cp-done <n>")+`
483
- `),0)}var lT=`leftoff todo: the checklists Claude writes for you, saved as Markdown in lists/<repo>/ in your folder.
483
+ `),0)}var mT=`leftoff todo: the checklists Claude writes for you, saved as Markdown in lists/<repo>/ in your folder.
484
484
 
485
485
  leftoff todo [--all] [--repo NAME] open lists with their items
486
486
  leftoff todo show <list>
@@ -496,14 +496,14 @@ Tick one: leftoff todo done <list> <n> · on the board: Lists`)+`
496
496
 
497
497
  <list>: its id (repo/slug), its name or the start of it, or its number in \`leftoff todo\`.
498
498
  Item numbers never change. In Claude Code: /cp-todo [name] saves, /cp-done <n> ticks.
499
- `;xe();ns();_t();import ru from"node:fs";import cS from"node:crypto";var cT=n=>/fetch failed|ECONNREFUSED|abort|timed? ?out/i.test(String(n&&n.message+(n.cause?" "+n.cause.code:"")));function uT(n){let e={sub:null,args:[],key:null,json:!1,purge:!1,offline:!1,url:null,writeKey:null,dryRun:!1};for(let t=0;t<n.length;t++){let s=n[t],o;s==="--json"?e.json=!0:s==="--purge"?e.purge=!0:s==="--offline"?e.offline=!0:(o=s.match(/^--key=(.*)$/))?e.key=o[1]:s==="--key"?e.key=n[++t]==null?"":n[t]:(o=s.match(/^--url=(.*)$/))?e.url=o[1]:s==="--url"?e.url=n[++t]==null?"":n[t]:(o=s.match(/^--write-key=(.*)$/))?e.writeKey=o[1]:s==="--write-key"?e.writeKey=n[++t]==null?"":n[t]:s==="--dry-run"||s==="-n"?e.dryRun=!0:e.sub?e.args.push(s):e.sub=s}return e.sub||(e.sub="list"),e.sub==="ls"&&(e.sub="list"),(e.sub==="rm"||e.sub==="delete")&&(e.sub="remove"),e.sub==="pull"&&(e.sub="refresh"),e.sub==="keys"&&(e.sub="keygen"),e.sub==="push"&&(e.sub="publish"),e.sub==="check"&&(e.sub="validate"),e}var iS=n=>cS.createHash("sha256").update(String(n),"utf8").digest("hex");function dT(n=cS.randomBytes){let e="lfr_"+n(24).toString("base64url"),t="lfw_"+n(24).toString("base64url");return{read:e,write:t,readSha256:iS(e),writeSha256:iS(t)}}function fT(n){return["Two new keys for a private Leftoff agent feed. They are shown once; save them now (a password manager is a good place).","","Read key: give this to Leftoff on the Mac that pulls the feed"," "+n.read," leftoff agents add <name> <feed url> --key - (paste it on stdin)","","Write key: for the agent or script that publishes the feed. Keep it there"," "+n.write," leftoff agents publish feed.json --url <feed url> --write-key -","","For wrangler.toml (templates/agent-feed-worker), under [vars]. Hashes only, safe to commit:",' READ_KEY_SHA256 = "'+n.readSha256+'"',' WRITE_KEY_SHA256 = "'+n.writeSha256+'"',"","Guide: https://github.com/"+st+"/blob/main/docs/agent-feed-hosting.md",""].join(`
500
- `)}function hT(n,{stdinUsed:e}){if(n==="-"){if(e)return{error:"the feed and the write key can't both come from stdin; pass the feed as a file"};try{return{body:ru.readFileSync(0)}}catch(t){return{error:"couldn't read stdin: "+t.message}}}try{return ru.statSync(n).isFile()?{body:ru.readFileSync(n)}:{error:n+" is not a file"}}catch(t){return{error:"couldn't read "+n+": "+(t.code==="ENOENT"?"no such file":t.message)}}}async function pT({body:n,url:e,writeKey:t,fetchImpl:s=globalThis.fetch,timeoutMs:o=2e4,userAgent:r="Leftoff"}){let i=Ll(n);if(!i.ok)return{ok:!1,stage:"validate",errors:i.errors};let a=Gd(e);if(!a.ok)return{ok:!1,stage:"url",errors:[a.message]};if(!t)return{ok:!1,stage:"key",errors:["no write key (--write-key <key>, --write-key - for stdin, or LEFTOFF_AGENT_WRITE_KEY)"]};let l=new AbortController,c=setTimeout(()=>l.abort(),o),u;try{u=await s(a.url,{method:"PUT",body:n,redirect:"manual",signal:l.signal,headers:{authorization:"Bearer "+t,"content-type":"application/json","user-agent":r}})}catch(h){clearTimeout(c);let p=h.name==="AbortError"?"timed out after "+Math.round(o/1e3)+"s":String(h.cause&&(h.cause.code||h.cause.message)||h.message);return{ok:!1,stage:"put",errors:["couldn't reach "+new URL(a.url).host+": "+p.split(t).join("…")]}}clearTimeout(c);let d="";try{d=(await u.text()).slice(0,300)}catch{}if(d=d.split(t).join("…").replace(/\s+/g," ").trim(),u.status>=200&&u.status<300){let h=u.headers.get("etag")||null;try{let p=JSON.parse(d);p&&typeof p.etag=="string"&&(h=p.etag)}catch{}return{ok:!0,status:u.status,etag:h,sessions:i.feed.sessions.length,bytes:i.bytes,agent:i.feed.agent.name,url:a.url}}let f=u.status===401||u.status===403?"the server refused the write key ("+u.status+")":u.status>=300&&u.status<400?"the server redirected ("+u.status+(u.headers.get("location")?" to "+u.headers.get("location"):"")+"); use the final URL, the key isn't sent on":u.status===413?"the server says the feed is too big (413)":"the server answered "+u.status+(d?": "+d:"");return{ok:!1,stage:"put",status:u.status,errors:[f]}}var aS=(n,e=Date.now())=>{if(!n)return"never";let t=Math.max(0,Math.round((e-Date.parse(n))/1e3));return t<60?t+"s ago":t<3600?Math.round(t/60)+"m ago":t<86400?Math.round(t/3600)+"h ago":Math.round(t/86400)+"d ago"};function _h(n,{now:e=Date.now()}={}){let t=n.status||{},s=n.pullsHere?"pulled by this Mac":n.puller?"pulled by "+n.puller:"not pulled",o=(n.icon?n.icon+" ":"")+n.display+(n.display!==n.name?" ("+n.name+")":"");return t.error?{ok:!1,line:o+": "+t.error.message+" · last success "+aS(t.lastSuccessAt,e)+" · "+s,details:t.error.details||[]}:t.ok?{ok:!0,line:o+": "+n.sessions+" session"+(n.sessions===1?"":"s")+(n.needsYou?", "+n.needsYou+" waiting on you":"")+" · pulled "+aS(t.lastSuccessAt,e)+" · "+s,details:[]}:{ok:null,line:o+": not pulled yet · "+s+(n.pullsHere||n.hasKey||!n.puller?"":" (its key is on that Mac)"),details:[]}}function iu(n){let e=n.view({fresh:!0});return{machine:n.machine,agents:e.agents.map(t=>({name:t.name,display:t.display,icon:t.icon,url:t.url,feedUrl:t.source.url,puller:t.puller,pullsHere:t.pullsHere,hasKey:t.hasKey,status:t.status,sessions:t.records.length,needsYou:t.records.reduce((s,o)=>s+(o.inFeed&&o.session.status!=="done"?o.session.needsYou.length:0),0)}))}}function lS(){try{return ru.readFileSync(0,"utf8").trim()}catch{return""}}async function uS({argv:n=[],api:e,out:t=i=>process.stdout.write(i),err:s=i=>process.stderr.write(i),env:o=process.env,fetchImpl:r=null}={}){let i=uT(n),a=Z(),l=()=>(Oe(a),new Ct(a,de(Q(a)),{env:o,fetchImpl:r,userAgent:"Leftoff/"+X})),c=async u=>{if(i.offline||!e)return{offline:!0};try{return{r:await u()}}catch(d){if(cT(d))return{offline:!0};throw d}};try{if(i.sub==="list"){let u=(await c(()=>e("GET","/v1/agents"))).r;if(u||(u=iu(l())),i.json)return t(JSON.stringify(u,null,2)+`
499
+ `;xe();rs();Ot();import au from"node:fs";import mS from"node:crypto";var gT=n=>/fetch failed|ECONNREFUSED|abort|timed? ?out/i.test(String(n&&n.message+(n.cause?" "+n.cause.code:"")));function yT(n){let e={sub:null,args:[],key:null,json:!1,purge:!1,offline:!1,url:null,writeKey:null,dryRun:!1};for(let t=0;t<n.length;t++){let s=n[t],o;s==="--json"?e.json=!0:s==="--purge"?e.purge=!0:s==="--offline"?e.offline=!0:(o=s.match(/^--key=(.*)$/))?e.key=o[1]:s==="--key"?e.key=n[++t]==null?"":n[t]:(o=s.match(/^--url=(.*)$/))?e.url=o[1]:s==="--url"?e.url=n[++t]==null?"":n[t]:(o=s.match(/^--write-key=(.*)$/))?e.writeKey=o[1]:s==="--write-key"?e.writeKey=n[++t]==null?"":n[t]:s==="--dry-run"||s==="-n"?e.dryRun=!0:e.sub?e.args.push(s):e.sub=s}return e.sub||(e.sub="list"),e.sub==="ls"&&(e.sub="list"),(e.sub==="rm"||e.sub==="delete")&&(e.sub="remove"),e.sub==="pull"&&(e.sub="refresh"),e.sub==="keys"&&(e.sub="keygen"),e.sub==="push"&&(e.sub="publish"),e.sub==="check"&&(e.sub="validate"),e}var fS=n=>mS.createHash("sha256").update(String(n),"utf8").digest("hex");function wT(n=mS.randomBytes){let e="lfr_"+n(24).toString("base64url"),t="lfw_"+n(24).toString("base64url");return{read:e,write:t,readSha256:fS(e),writeSha256:fS(t)}}function ST(n){return["Two new keys for a private Leftoff agent feed. They are shown once; save them now (a password manager is a good place).","","Read key: give this to Leftoff on the Mac that pulls the feed"," "+n.read," leftoff agents add <name> <feed url> --key - (paste it on stdin)","","Write key: for the agent or script that publishes the feed. Keep it there"," "+n.write," leftoff agents publish feed.json --url <feed url> --write-key -","","For wrangler.toml (templates/agent-feed-worker), under [vars]. Hashes only, safe to commit:",' READ_KEY_SHA256 = "'+n.readSha256+'"',' WRITE_KEY_SHA256 = "'+n.writeSha256+'"',"","Guide: https://github.com/"+rt+"/blob/main/docs/agent-feed-hosting.md",""].join(`
500
+ `)}function kT(n,{stdinUsed:e}){if(n==="-"){if(e)return{error:"the feed and the write key can't both come from stdin; pass the feed as a file"};try{return{body:au.readFileSync(0)}}catch(t){return{error:"couldn't read stdin: "+t.message}}}try{return au.statSync(n).isFile()?{body:au.readFileSync(n)}:{error:n+" is not a file"}}catch(t){return{error:"couldn't read "+n+": "+(t.code==="ENOENT"?"no such file":t.message)}}}async function bT({body:n,url:e,writeKey:t,fetchImpl:s=globalThis.fetch,timeoutMs:o=2e4,userAgent:r="Leftoff"}){let i=Rl(n);if(!i.ok)return{ok:!1,stage:"validate",errors:i.errors};let a=zd(e);if(!a.ok)return{ok:!1,stage:"url",errors:[a.message]};if(!t)return{ok:!1,stage:"key",errors:["no write key (--write-key <key>, --write-key - for stdin, or LEFTOFF_AGENT_WRITE_KEY)"]};let l=new AbortController,c=setTimeout(()=>l.abort(),o),u;try{u=await s(a.url,{method:"PUT",body:n,redirect:"manual",signal:l.signal,headers:{authorization:"Bearer "+t,"content-type":"application/json","user-agent":r}})}catch(h){clearTimeout(c);let p=h.name==="AbortError"?"timed out after "+Math.round(o/1e3)+"s":String(h.cause&&(h.cause.code||h.cause.message)||h.message);return{ok:!1,stage:"put",errors:["couldn't reach "+new URL(a.url).host+": "+p.split(t).join("…")]}}clearTimeout(c);let d="";try{d=(await u.text()).slice(0,300)}catch{}if(d=d.split(t).join("…").replace(/\s+/g," ").trim(),u.status>=200&&u.status<300){let h=u.headers.get("etag")||null;try{let p=JSON.parse(d);p&&typeof p.etag=="string"&&(h=p.etag)}catch{}return{ok:!0,status:u.status,etag:h,sessions:i.feed.sessions.length,bytes:i.bytes,agent:i.feed.agent.name,url:a.url}}let f=u.status===401||u.status===403?"the server refused the write key ("+u.status+")":u.status>=300&&u.status<400?"the server redirected ("+u.status+(u.headers.get("location")?" to "+u.headers.get("location"):"")+"); use the final URL, the key isn't sent on":u.status===413?"the server says the feed is too big (413)":"the server answered "+u.status+(d?": "+d:"");return{ok:!1,stage:"put",status:u.status,errors:[f]}}var hS=(n,e=Date.now())=>{if(!n)return"never";let t=Math.max(0,Math.round((e-Date.parse(n))/1e3));return t<60?t+"s ago":t<3600?Math.round(t/60)+"m ago":t<86400?Math.round(t/3600)+"h ago":Math.round(t/86400)+"d ago"};function Nh(n,{now:e=Date.now()}={}){let t=n.status||{},s=n.pullsHere?"pulled by this Mac":n.puller?"pulled by "+n.puller:"not pulled",o=(n.icon?n.icon+" ":"")+n.display+(n.display!==n.name?" ("+n.name+")":"");return t.error?{ok:!1,line:o+": "+t.error.message+" · last success "+hS(t.lastSuccessAt,e)+" · "+s,details:t.error.details||[]}:t.ok?{ok:!0,line:o+": "+n.sessions+" session"+(n.sessions===1?"":"s")+(n.needsYou?", "+n.needsYou+" waiting on you":"")+" · pulled "+hS(t.lastSuccessAt,e)+" · "+s,details:[]}:{ok:null,line:o+": not pulled yet · "+s+(n.pullsHere||n.hasKey||!n.puller?"":" (its key is on that Mac)"),details:[]}}function lu(n){let e=n.view({fresh:!0});return{machine:n.machine,agents:e.agents.map(t=>({name:t.name,display:t.display,icon:t.icon,url:t.url,feedUrl:t.source.url,puller:t.puller,pullsHere:t.pullsHere,hasKey:t.hasKey,status:t.status,sessions:t.records.length,needsYou:t.records.reduce((s,o)=>s+(o.inFeed&&o.session.status!=="done"?o.session.needsYou.length:0),0)}))}}function pS(){try{return au.readFileSync(0,"utf8").trim()}catch{return""}}async function gS({argv:n=[],api:e,out:t=i=>process.stdout.write(i),err:s=i=>process.stderr.write(i),env:o=process.env,fetchImpl:r=null}={}){let i=yT(n),a=Z(),l=()=>(Oe(a),new It(a,de(Q(a)),{env:o,fetchImpl:r,userAgent:"Leftoff/"+X})),c=async u=>{if(i.offline||!e)return{offline:!0};try{return{r:await u()}}catch(d){if(gT(d))return{offline:!0};throw d}};try{if(i.sub==="list"){let u=(await c(()=>e("GET","/v1/agents"))).r;if(u||(u=lu(l())),i.json)return t(JSON.stringify(u,null,2)+`
501
501
  `),0;if(!u.agents.length)return t(`No agent feeds yet. Add one: leftoff agents add <name> <url> --key <key>
502
- `),0;for(let d of u.agents){let f=_h(d);t((f.ok===!1?"✗ ":f.ok?"✓ ":"· ")+f.line+`
502
+ `),0;for(let d of u.agents){let f=Nh(d);t((f.ok===!1?"✗ ":f.ok?"✓ ":"· ")+f.line+`
503
503
  `),t(" "+(d.feedUrl||"")+`
504
504
  `);for(let h of f.details.slice(0,5))t(" - "+h+`
505
505
  `)}return u.agents.some(d=>d.status&&d.status.error)?1:0}if(i.sub==="add"){let[u,d]=i.args;if(!u||!d)return s(`usage: leftoff agents add <name> <url> --key <key> (--key - reads it from stdin)
506
- `),2;let f=i.key;f==="-"&&(f=lS()),f==null&&o.LEFTOFF_AGENT_KEY&&(f=o.LEFTOFF_AGENT_KEY);let h={name:u.toLowerCase(),url:d,key:f||null},p=(await c(()=>e("POST","/v1/agents",h))).r;if(!p){let g=l();p=g.add(h);try{p.pull=await g.pull(p.source.name,{force:!0})}catch(S){p.pull={result:"error",error:{message:S.message}}}}t((p.replaced?"Updated ":"Added ")+p.source.name+" ("+p.source.url+")."+(p.hasKey?" Key saved on this Mac only.":" No key: the feed is pulled without one.")+`
506
+ `),2;let f=i.key;f==="-"&&(f=pS()),f==null&&o.LEFTOFF_AGENT_KEY&&(f=o.LEFTOFF_AGENT_KEY);let h={name:u.toLowerCase(),url:d,key:f||null},p=(await c(()=>e("POST","/v1/agents",h))).r;if(!p){let g=l();p=g.add(h);try{p.pull=await g.pull(p.source.name,{force:!0})}catch(S){p.pull={result:"error",error:{message:S.message}}}}t((p.replaced?"Updated ":"Added ")+p.source.name+" ("+p.source.url+")."+(p.hasKey?" Key saved on this Mac only.":" No key: the feed is pulled without one.")+`
507
507
  `),p.previousPuller&&t("This Mac pulls it now (was "+p.previousPuller+`).
508
508
  `);let m=p.pull||{};if(m.result==="ok")t("Pulled: "+(m.applied?m.applied.sessions+" session"+(m.applied.sessions===1?"":"s"):"ok")+". It shows on the board as "+(m.status&&m.status.agent&&m.status.agent.name||p.source.name)+`.
509
509
  `);else if(m.result==="unchanged")t(`Pulled: nothing new.
@@ -511,38 +511,38 @@ Item numbers never change. In Claude Code: /cp-todo [name] saves, /cp-done <n> t
511
511
  `);for(let g of(m.error&&m.error.details||[]).slice(0,5))s(" - "+g+`
512
512
  `);return 1}return 0}if(i.sub==="remove"){let[u]=i.args;if(!u)return s(`usage: leftoff agents remove <name> [--purge]
513
513
  `),2;let d=(await c(()=>e("DELETE","/v1/agents/"+encodeURIComponent(u)+(i.purge?"?purge=1":"")))).r;return d||(d=l().remove(u,{purge:i.purge})),t("Removed "+u+". "+(d.movedTo?"Its history is in "+d.movedTo+" (nothing deleted).":"Its files are deleted.")+(d.keyDeleted?" This Mac forgot its key.":"")+`
514
- `),0}if(i.sub==="refresh"){let u=i.args.length?i.args:null,d=(await c(()=>e("GET","/v1/agents"))).r,f=!!d;d||(d=iu(l()));let h=d.agents.filter(g=>u?u.includes(g.name):g.pullsHere);if(u){for(let g of u)if(!d.agents.some(S=>S.name===g))return s("No agent feed named "+g+`
514
+ `),0}if(i.sub==="refresh"){let u=i.args.length?i.args:null,d=(await c(()=>e("GET","/v1/agents"))).r,f=!!d;d||(d=lu(l()));let h=d.agents.filter(g=>u?u.includes(g.name):g.pullsHere);if(u){for(let g of u)if(!d.agents.some(S=>S.name===g))return s("No agent feed named "+g+`
515
515
  `),1}if(!h.length)return t("Nothing to pull on this Mac"+(d.agents.length?" (each feed is pulled by the Mac that holds its key)":"")+`.
516
516
  `),0;let p=0,m=f?null:l();for(let g of h){let S;try{S=f?await e("POST","/v1/agents/"+encodeURIComponent(g.name)+"/refresh"):await m.pull(g.name,{force:!0})}catch(k){S={result:"error",error:{message:k.message}}}if(S.result==="error"){p++,s("✗ "+g.name+": "+(S.error&&S.error.message||"error")+`
517
517
  `);for(let k of(S.error&&S.error.details||[]).slice(0,5))s(" - "+k+`
518
518
  `)}else t("✓ "+g.name+": "+(S.result==="unchanged"?"nothing new":S.applied?S.applied.sessions+" session"+(S.applied.sessions===1?"":"s")+", "+S.applied.written+" file"+(S.applied.written===1?"":"s")+" updated":"pulled")+`
519
- `)}return p?1:0}if(i.sub==="keygen"){let u=dT();return t(i.json?JSON.stringify(u,null,2)+`
520
- `:fT(u)),0}if(i.sub==="validate"||i.sub==="publish"){let[u]=i.args,d=i.sub==="validate"?"usage: leftoff agents validate <file>":"usage: leftoff agents publish <file> --url <url> --write-key <key|-> (--dry-run validates only)";if(!u)return s(d+`
521
- `),2;let f=i.writeKey,h=f==="-",p=hT(u,{stdinUsed:h});if(p.error)return s(p.error+`
522
- `),1;let m=Ll(p.body);if(!m.ok){s("✗ "+(u==="-"?"the feed":u)+" is not a valid leftoff-agent-feed/1 feed"+(i.sub==="publish"?"; nothing was published":"")+`:
519
+ `)}return p?1:0}if(i.sub==="keygen"){let u=wT();return t(i.json?JSON.stringify(u,null,2)+`
520
+ `:ST(u)),0}if(i.sub==="validate"||i.sub==="publish"){let[u]=i.args,d=i.sub==="validate"?"usage: leftoff agents validate <file>":"usage: leftoff agents publish <file> --url <url> --write-key <key|-> (--dry-run validates only)";if(!u)return s(d+`
521
+ `),2;let f=i.writeKey,h=f==="-",p=kT(u,{stdinUsed:h});if(p.error)return s(p.error+`
522
+ `),1;let m=Rl(p.body);if(!m.ok){s("✗ "+(u==="-"?"the feed":u)+" is not a valid leftoff-agent-feed/1 feed"+(i.sub==="publish"?"; nothing was published":"")+`:
523
523
  `);for(let k of m.errors.slice(0,20))s(" - "+k+`
524
524
  `);return m.errors.length>20&&s(" … and "+(m.errors.length-20)+` more
525
525
  `),1}let g=m.feed.agent.name+", "+m.feed.sessions.length+" session"+(m.feed.sessions.length===1?"":"s")+", "+m.bytes+" bytes";if(i.sub==="validate"||i.dryRun)return t("✓ Valid ("+g+")."+(i.sub==="publish"?" Not published (--dry-run).":"")+`
526
526
  `),0;if(!i.url)return s(d+`
527
- `),2;h&&(f=lS()),!f&&o.LEFTOFF_AGENT_WRITE_KEY&&(f=o.LEFTOFF_AGENT_WRITE_KEY);let S=await pT({body:p.body,url:i.url,writeKey:f,fetchImpl:r||globalThis.fetch,userAgent:"Leftoff/"+X});return S.ok?(t("✓ Published to "+S.url+" ("+g+(S.etag?", ETag "+S.etag:"")+`).
527
+ `),2;h&&(f=pS()),!f&&o.LEFTOFF_AGENT_WRITE_KEY&&(f=o.LEFTOFF_AGENT_WRITE_KEY);let S=await bT({body:p.body,url:i.url,writeKey:f,fetchImpl:r||globalThis.fetch,userAgent:"Leftoff/"+X});return S.ok?(t("✓ Published to "+S.url+" ("+g+(S.etag?", ETag "+S.etag:"")+`).
528
528
  `),0):(s("✗ Not published: "+S.errors.join("; ")+`
529
529
  `),1)}return s("unknown: leftoff agents "+i.sub+`
530
530
  usage: leftoff agents [list|add|remove|refresh|keygen|publish|validate]
531
531
  `),2}catch(u){return s(u&&u.message||String(u)),s(`
532
- `),1}}ns();Zi();xe();xn();ns();Ns();Vd();si();import{spawnSync as mT}from"node:child_process";function fS(n){let e={},t=[];for(let s=0;s<n.length;s++){let o=n[s],r=o.match(/^--([^=]+)(?:=(.*))?$/);if(!r){t.push(o);continue}r[2]!==void 0?e[r[1]]=r[2]:["space","date"].includes(r[1])&&n[s+1]&&!n[s+1].startsWith("--")?e[r[1]]=n[++s]:e[r[1]]=!0}return{flags:e,pos:t}}function hS(){let n=Z();Oe(n);let e=Q(n),t=de(e);return{root:n,cfg:e,machine:t,folder:new xt(n,t)}}function Eh(n,e){let t=new Set(n.folder.machines());for(let s of n.folder.aliveMachines())t.add(s.machine);for(let s of Object.keys(jt(e,"machines")))t.add(s);return[...t].filter(Boolean).sort()}var dS=`leftoff spaces Macs, their default space, and overrides
532
+ `),1}}rs();ta();xe();Tn();rs();Ds();Qd();ri();import{spawnSync as vT}from"node:child_process";function wS(n){let e={},t=[];for(let s=0;s<n.length;s++){let o=n[s],r=o.match(/^--([^=]+)(?:=(.*))?$/);if(!r){t.push(o);continue}r[2]!==void 0?e[r[1]]=r[2]:["space","date"].includes(r[1])&&n[s+1]&&!n[s+1].startsWith("--")?e[r[1]]=n[++s]:e[r[1]]=!0}return{flags:e,pos:t}}function SS(){let n=Z();Oe(n);let e=Q(n),t=de(e);return{root:n,cfg:e,machine:t,folder:new Tt(n,t)}}function Ih(n,e){let t=new Set(n.folder.machines());for(let s of n.folder.aliveMachines())t.add(s.machine);for(let s of Object.keys(Ut(e,"machines")))t.add(s);return[...t].filter(Boolean).sort()}var yS=`leftoff spaces Macs, their default space, and overrides
533
533
  leftoff spaces set-machine [mac] <space> a Mac's default space (default: this Mac)
534
534
  leftoff spaces set-repo <repo> <space> a repo counts as <space> on every Mac
535
535
  leftoff spaces set-session <id> <space> one session
536
536
  leftoff spaces set-agent <name> <space> an agent feed (default: the space of the Mac that pulls it)
537
537
  leftoff spaces unset machine|repo|session|agent <key>
538
538
  A session's space: its own override, else its repo's, else its Mac's default.
539
- Quick picks: Work, Personal (or any name).`;function pS({argv:n=[],out:e=console.log}={}){let{flags:t,pos:s}=fS(n),o=hS(),r=s[0]||"list",i=a=>e(a);try{if(r==="help"||t.help)return i(dS),0;if(r==="list"||r==="ls"){let{map:c,conflicts:u}=Is(o.root),d=Eh(o,c);if(t.json)return i(JSON.stringify({machine:o.machine,machines:d.map(f=>({machine:f,space:Jn(c,f),set:!!(c.machines[f]&&c.machines[f].space!=null)})),repos:jt(c,"repos"),sessions:jt(c,"sessions"),agents:jt(c,"agents"),spaces:sn(c,d)},null,2)),0;i("Spaces: "+(sn(c,d).join(", ")||"(none)")),i("Macs (default space):");for(let f of d){let h=c.machines[f]&&c.machines[f].space!=null;i(" "+f.padEnd(18)+Jn(c,f)+(h?"":" (not chosen yet: leftoff spaces set-machine "+f+" Work|Personal)")+(f===o.machine?" ← this Mac":""))}for(let[f,h]of[["repos","Repo overrides"],["sessions","Session overrides"],["agents","Agent feeds"]]){let p=jt(c,f);if(Object.keys(p).length){i(h+":");for(let[m,g]of Object.entries(p).sort(([S],[k])=>S.localeCompare(k)))i(" "+m.padEnd(28)+g)}}return u.length&&i("("+u.length+" Syncthing conflict cop"+(u.length===1?"y":"ies")+" of spaces.json merged; the next change folds "+(u.length===1?"it":"them")+" in)"),0}let a=(c,u,d)=>Ku(o.root,(f,h)=>{let p=Gr(c),m=$p(p,u),g=d==null?null:$r(d);return g&&(g=[...sn(f,Eh(o,f)),...xo].find(S=>S.toLowerCase()===g.toLowerCase())||g),f[p][m]={space:g,at:h.at,by:h.by},g},{machine:o.machine});if(r==="set-machine"){let[c,u]=s.length>=3?[s[1],s.slice(2).join(" ")]:[o.machine,s[1]];if(!u)throw new Error("usage: leftoff spaces set-machine [mac] <space>");let d=a("machine",c,u);return i("✓ "+c+" is in "+d+" (its sessions count as "+d+" unless a repo or session says otherwise)"),0}let l={"set-repo":"repo","set-session":"session","set-agent":"agent"}[r];if(l){if(!s[1]||!s[2])throw new Error("usage: leftoff spaces "+r+" <"+l+"> <space>");let c=a(l,s[1],s.slice(2).join(" "));return i("✓ "+l+" "+s[1]+" → "+c),0}if(r==="unset"||r==="rm"||r==="remove"){if(!s[1]||!s[2])throw new Error("usage: leftoff spaces unset machine|repo|session|agent <key>");a(s[1],s.slice(2).join(" "),null);let c=Gr(s[1]);return i("✓ "+s[1]+" "+s.slice(2).join(" ")+(c==="machines"?" is back to its own name as its space":" override removed")),0}return i(dS),1}catch(a){return console.error("✗ "+a.message),1}}function gT(n,{env:e=process.env,platform:t=process.platform}={}){let s=e.LEFTOFF_CLIPBOARD_CMD,o=s?[["sh",["-c",s]]]:t==="darwin"?[["pbcopy",[]]]:t==="win32"?[["clip",[]]]:[["wl-copy",[]],["xclip",["-selection","clipboard"]],["xsel",["--clipboard","--input"]]];for(let[r,i]of o){let a=mT(r,i,{input:n,stdio:["pipe","ignore","ignore"],timeout:5e3});if(!a.error&&a.status===0)return r}return null}async function mS({argv:n=[],api:e=null,out:t=s=>process.stdout.write(s)}={}){let{flags:s}=fS(n),o=hS(),{map:r}=Is(o.root),i=typeof s.space=="string"?s.space:Jn(r,o.machine);(s.all||/^all$/i.test(i))&&(i=null);let a=typeof s.date=="string"?s.date:null;if(a&&!/^\d{4}-\d{2}-\d{2}$/.test(a))return console.error("✗ --date is YYYY-MM-DD"),1;let l=null;if(e&&!s.offline)try{l=await e("GET","/v1/standup?"+new URLSearchParams({...i?{space:i}:{space:"all"},...a?{date:a}:{}}))}catch(c){if(!/fetch failed|ECONNREFUSED|aborted|timeout/i.test(c.message+(c.cause?c.cause.code:"")))return console.error("✗ "+c.message),1}if(!l){let c=il(o.cfg.digest,$t()),u=Date.now(),d=null;try{d=new Ct(o.root,o.machine).view({fresh:!0})}catch{}let f=i?sn(r,Eh(o,r)).find(h=>h.toLowerCase()===i.toLowerCase())||i:null;l={...Ul(o.folder.loadAll(),{now:u,date:a||Do(u,c).date,space:f,spaces:r,cfg:o.cfg,tz:c.tz,digestTime:c.time||"07:00",agents:d}),date:a||Do(u,c).date}}if(s.json)return t(JSON.stringify(l,null,2)+`
539
+ Quick picks: Work, Personal (or any name).`;function kS({argv:n=[],out:e=console.log}={}){let{flags:t,pos:s}=wS(n),o=SS(),r=s[0]||"list",i=a=>e(a);try{if(r==="help"||t.help)return i(yS),0;if(r==="list"||r==="ls"){let{map:c,conflicts:u}=Ls(o.root),d=Ih(o,c);if(t.json)return i(JSON.stringify({machine:o.machine,machines:d.map(f=>({machine:f,space:Vn(c,f),set:!!(c.machines[f]&&c.machines[f].space!=null)})),repos:Ut(c,"repos"),sessions:Ut(c,"sessions"),agents:Ut(c,"agents"),spaces:rn(c,d)},null,2)),0;i("Spaces: "+(rn(c,d).join(", ")||"(none)")),i("Macs (default space):");for(let f of d){let h=c.machines[f]&&c.machines[f].space!=null;i(" "+f.padEnd(18)+Vn(c,f)+(h?"":" (not chosen yet: leftoff spaces set-machine "+f+" Work|Personal)")+(f===o.machine?" ← this Mac":""))}for(let[f,h]of[["repos","Repo overrides"],["sessions","Session overrides"],["agents","Agent feeds"]]){let p=Ut(c,f);if(Object.keys(p).length){i(h+":");for(let[m,g]of Object.entries(p).sort(([S],[k])=>S.localeCompare(k)))i(" "+m.padEnd(28)+g)}}return u.length&&i("("+u.length+" Syncthing conflict cop"+(u.length===1?"y":"ies")+" of spaces.json merged; the next change folds "+(u.length===1?"it":"them")+" in)"),0}let a=(c,u,d)=>zu(o.root,(f,h)=>{let p=Kr(c),m=Jp(p,u),g=d==null?null:zr(d);return g&&(g=[...rn(f,Ih(o,f)),...To].find(S=>S.toLowerCase()===g.toLowerCase())||g),f[p][m]={space:g,at:h.at,by:h.by},g},{machine:o.machine});if(r==="set-machine"){let[c,u]=s.length>=3?[s[1],s.slice(2).join(" ")]:[o.machine,s[1]];if(!u)throw new Error("usage: leftoff spaces set-machine [mac] <space>");let d=a("machine",c,u);return i("✓ "+c+" is in "+d+" (its sessions count as "+d+" unless a repo or session says otherwise)"),0}let l={"set-repo":"repo","set-session":"session","set-agent":"agent"}[r];if(l){if(!s[1]||!s[2])throw new Error("usage: leftoff spaces "+r+" <"+l+"> <space>");let c=a(l,s[1],s.slice(2).join(" "));return i("✓ "+l+" "+s[1]+" → "+c),0}if(r==="unset"||r==="rm"||r==="remove"){if(!s[1]||!s[2])throw new Error("usage: leftoff spaces unset machine|repo|session|agent <key>");a(s[1],s.slice(2).join(" "),null);let c=Kr(s[1]);return i("✓ "+s[1]+" "+s.slice(2).join(" ")+(c==="machines"?" is back to its own name as its space":" override removed")),0}return i(yS),1}catch(a){return console.error("✗ "+a.message),1}}function xT(n,{env:e=process.env,platform:t=process.platform}={}){let s=e.LEFTOFF_CLIPBOARD_CMD,o=s?[["sh",["-c",s]]]:t==="darwin"?[["pbcopy",[]]]:t==="win32"?[["clip",[]]]:[["wl-copy",[]],["xclip",["-selection","clipboard"]],["xsel",["--clipboard","--input"]]];for(let[r,i]of o){let a=vT(r,i,{input:n,stdio:["pipe","ignore","ignore"],timeout:5e3});if(!a.error&&a.status===0)return r}return null}async function bS({argv:n=[],api:e=null,out:t=s=>process.stdout.write(s)}={}){let{flags:s}=wS(n),o=SS(),{map:r}=Ls(o.root),i=typeof s.space=="string"?s.space:Vn(r,o.machine);(s.all||/^all$/i.test(i))&&(i=null);let a=typeof s.date=="string"?s.date:null;if(a&&!/^\d{4}-\d{2}-\d{2}$/.test(a))return console.error("✗ --date is YYYY-MM-DD"),1;let l=null;if(e&&!s.offline)try{l=await e("GET","/v1/standup?"+new URLSearchParams({...i?{space:i}:{space:"all"},...a?{date:a}:{}}))}catch(c){if(!/fetch failed|ECONNREFUSED|aborted|timeout/i.test(c.message+(c.cause?c.cause.code:"")))return console.error("✗ "+c.message),1}if(!l){let c=ll(o.cfg.digest,Yt()),u=Date.now(),d=null;try{d=new It(o.root,o.machine).view({fresh:!0})}catch{}let f=i?rn(r,Ih(o,r)).find(h=>h.toLowerCase()===i.toLowerCase())||i:null;l={...Bl(o.folder.loadAll(),{now:u,date:a||Po(u,c).date,space:f,spaces:r,cfg:o.cfg,tz:c.tz,digestTime:c.time||"07:00",agents:d}),date:a||Po(u,c).date}}if(s.json)return t(JSON.stringify(l,null,2)+`
540
540
  `),0;if(t(l.text+`
541
- `),s.copy){let c=gT(l.text);console.error(c?"✓ Copied "+(l.space||"all spaces")+" standup to the clipboard":"· No clipboard tool found (pbcopy, wl-copy, xclip, xsel); copy the text above")}return 0}Kc();xe();os();Kc();Ki();Ri();Pc();function yT(n){let e={},t=[];for(let s of n){let o=s.match(/^--([^=]+)(?:=(.*))?$/);o?e[o[1]]=o[2]===void 0?!0:o[2]:t.push(s)}return{flags:e,pos:t}}var au=n=>{let e=Date.parse(n||"");if(!e)return"never";let t=Math.round((Date.now()-e)/6e4);return t<2?"just now":t<60?t+" min ago":t<2160?Math.round(t/60)+" h ago":Math.round(t/1440)+" days ago"};function gS(n){let e=[];if(!n.user)return e.push("Use from anywhere: off (no account yet)"),e.push(" Claim your name: leftoff relay signup <name> (yourname.leftoff.dev, an email link, free in the beta)"),e;e.push("Use from anywhere: "+(n.enabled?"on":"off")+" · "+n.host+(n.enabled?"":" (leftoff relay enable)")),n.enabled&&e.push(" Relay: "+(n.connected?"connected"+(n.latencyMs!=null?", "+n.latencyMs+" ms":""):"not connected"+(n.problem?": "+n.problem:""))),n.relayMachines&&n.relayMachines.length&&e.push(" Macs on it: "+n.relayMachines.join(", "));let t=n.list||[];e.push(" Paired devices: "+(t.length?t.length:"none yet (leftoff relay pair)"));for(let s of t)e.push(" "+s.name+" · "+s.id+" · paired "+au(s.createdAt)+" on "+s.pairedOn+(s.lastSeenAt?" · last used "+au(s.lastSeenAt):""));return n.enabled&&e.push(" Board: "+n.boardUrl),e}async function wT(n){let e=n["admin-token"]===!0?"-":n["admin-token"]||process.env.LEFTOFF_RELAY_ADMIN_TOKEN||"";if(e==="-"||!e&&!process.stdin.isTTY){let t=[];for await(let s of process.stdin)t.push(s);e=Buffer.concat(t).toString("utf8").trim()}else e||(process.stdout.write("Admin token (the ADMIN_TOKEN secret you set with wrangler): "),e=await new Promise(t=>{let s=process.stdin,o="";s.setRawMode&&s.setRawMode(!0),s.resume(),s.setEncoding("utf8");let r=i=>{i==="\r"||i===`
541
+ `),s.copy){let c=xT(l.text);console.error(c?"✓ Copied "+(l.space||"all spaces")+" standup to the clipboard":"· No clipboard tool found (pbcopy, wl-copy, xclip, xsel); copy the text above")}return 0}zc();xe();as();zc();Yi();Fi();Fc();function AT(n){let e={},t=[];for(let s of n){let o=s.match(/^--([^=]+)(?:=(.*))?$/);o?e[o[1]]=o[2]===void 0?!0:o[2]:t.push(s)}return{flags:e,pos:t}}var cu=n=>{let e=Date.parse(n||"");if(!e)return"never";let t=Math.round((Date.now()-e)/6e4);return t<2?"just now":t<60?t+" min ago":t<2160?Math.round(t/60)+" h ago":Math.round(t/1440)+" days ago"};function vS(n){let e=[];if(!n.user)return e.push("Use from anywhere: off (no account yet)"),e.push(" Claim your name: leftoff relay signup <name> (yourname.leftoff.dev, an email link, free in the beta)"),e;e.push("Use from anywhere: "+(n.enabled?"on":"off")+" · "+n.host+(n.enabled?"":" (leftoff relay enable)")),n.enabled&&e.push(" Relay: "+(n.connected?"connected"+(n.latencyMs!=null?", "+n.latencyMs+" ms":""):"not connected"+(n.problem?": "+n.problem:""))),n.relayMachines&&n.relayMachines.length&&e.push(" Macs on it: "+n.relayMachines.join(", "));let t=n.list||[];e.push(" Paired devices: "+(t.length?t.length:"none yet (leftoff relay pair)"));for(let s of t)e.push(" "+s.name+" · "+s.id+" · paired "+cu(s.createdAt)+" on "+s.pairedOn+(s.lastSeenAt?" · last used "+cu(s.lastSeenAt):""));return n.enabled&&e.push(" Board: "+n.boardUrl),e}async function TT(n){let e=n["admin-token"]===!0?"-":n["admin-token"]||process.env.LEFTOFF_RELAY_ADMIN_TOKEN||"";if(e==="-"||!e&&!process.stdin.isTTY){let t=[];for await(let s of process.stdin)t.push(s);e=Buffer.concat(t).toString("utf8").trim()}else e||(process.stdout.write("Admin token (the ADMIN_TOKEN secret you set with wrangler): "),e=await new Promise(t=>{let s=process.stdin,o="";s.setRawMode&&s.setRawMode(!0),s.resume(),s.setEncoding("utf8");let r=i=>{i==="\r"||i===`
542
542
  `||i===""?(s.setRawMode&&s.setRawMode(!1),s.pause(),s.off("data",r),process.stdout.write(`
543
543
  `),t(o)):i===""?(process.stdout.write(`
544
- `),process.exit(130)):i===""?o=o.slice(0,-1):o+=i};s.on("data",r)}));return String(e).trim()}function ST(){let n=null,e=async t=>(n||(n=(await import("node:readline")).createInterface({input:process.stdin,output:process.stdout})),new Promise(s=>n.question(t,s)));return e.close=()=>{n&&n.close(),n=null},e}async function yS({argv:n=[],api:e,out:t=o=>console.log(o),err:s=o=>console.error(o)}={}){let{flags:o,pos:r}=yT(n),i=r[0]||"status",a=Z(),l=async(c,u,d)=>{try{return await e(c,u,d)}catch(f){throw/fetch failed|ECONNREFUSED/.test(String(f.message)+String(f.cause&&f.cause.code))?Object.assign(new Error("The Leftoff daemon isn’t running. Start it (leftoff setup), then try again."),{offline:!0}):f}};try{if(i==="signup"){Oe(a);let c=Q(a),u=de(c),d=Hn(a),f=ST(),h=String(r[1]||"").trim().toLowerCase();if(!h){if(!process.stdin.isTTY)return s("Usage: leftoff relay signup <name> --email=you@example.com"),2;h=(await f("Your name (for yourname."+(d.domain||Un)+"): ")).trim().toLowerCase()}let p=Fn(h);if(!p.ok)return f.close(),s(p.code==="reserved"?"“"+h+"” is reserved (www, app, api, docs, feed, relay, admin, status, mail). Pick another.":"A name is 2–32 lowercase letters, digits or hyphens, e.g. yourname for yourname.leftoff.dev."),2;let m={...d,user:p.user,domain:String(o.domain||d.domain||Un)},g=Nn(u),S=await Yi({s:m,name:p.user,sign:g.signX});if(!S.ok&&S.code!=="legacy")return f.close(),s(S.message),S.code==="unreachable"||S.code==="closed"?1:2;if(S.ok&&S.status==="yours"){if(f.close(),t("✓ "+S.message),!(d.enabled&&d.user===p.user)){ys(a,{enabled:!0,user:p.user,domain:m.domain,registeredAt:d.registeredAt||new Date().toISOString(),via:d.via||"signup"});try{await e("POST","/v1/relay/enable",{user:p.user,domain:m.domain})}catch{}}return t(" Pair your phone: leftoff relay pair"),0}if(S.ok&&(S.status==="taken"||S.status==="pending"))return f.close(),s(S.message),s(" Yours on another Mac? leftoff relay enable "+p.user),1;S.ok&&S.claimed&&t(" "+S.host+" was claimed on leftoff.dev. Use the email you claimed it with.");let k=typeof o.email=="string"?o.email.trim():process.env.LEFTOFF_SIGNUP_EMAIL||"";if(!k){if(!process.stdin.isTTY)return f.close(),s("Add your email: leftoff relay signup "+p.user+" --email=you@example.com"),2;k=(await f("Email (for the confirmation link): ")).trim()}if(f.close(),!Dc(k))return s("That doesn’t look like an email address."),2;let w=await zi({s:m,name:p.user,email:k,machine:u,sign:g.signX});if(!w.ok)return s(w.message),1;if(w.already)t("✓ This Mac already has "+Bt(m)+".");else{if(t("✉ Sent a link to "+w.email+". Open it and press Confirm (it works for 30 minutes)."),w.claimed&&t(" Your leftoff.dev claim for "+p.user+" carries over."),o["no-wait"])return t(" Not waiting (--no-wait). After you confirm: leftoff relay enable "+p.user),0;t(" Waiting for you to confirm… (Ctrl-C stops waiting; the link still works, then run: leftoff relay enable "+p.user+")");let x=!1,v=()=>{x=!0};process.once("SIGINT",v);let b=await Kf({s:m,user:p.user,poll:w.poll,intervalMs:Number(process.env.LEFTOFF_SIGNUP_POLL_MS)||void 0,stop:()=>x});if(process.removeListener("SIGINT",v),b==="stopped")return t(`
545
- Stopped waiting. Once you press Confirm: leftoff relay enable `+p.user),130;if(b==="expired"||b==="timeout")return s("The link expired before it was confirmed. Run `leftoff relay signup "+p.user+"` again for a fresh one."),1;if(b==="replaced")return s("A newer link was sent for "+p.user+"; this one no longer counts. Use the newest email (or run signup again)."),1;if(b!=="confirmed")return s("Lost track of the signup ("+b+"). Run `leftoff relay signup "+p.user+"` again."),1}ys(a,{enabled:!0,user:p.user,domain:m.domain,registeredAt:new Date().toISOString(),via:"signup"}),t("✓ "+Bt(m)+" is yours. Pair your phone: leftoff relay pair");try{await e("POST","/v1/relay/enable",{user:p.user,domain:m.domain}),t(" Use from anywhere is on; the daemon is connecting.")}catch{t(" Start the daemon (leftoff setup) and it connects on its own.")}return t(" Your other Macs: leftoff relay enable "+p.user+" (while this Mac is awake, it vouches for them)"),0}if(i==="register"){Oe(a);let c=Q(a),u=de(c),d=Fn(r[1]||Hn(a).user||"");if(!d.ok)return s(r[1]?d.code==="reserved"?"“"+r[1]+"” is reserved (www, app, api, docs, feed, relay, admin, status, mail).":"A name is 2–32 lowercase letters, digits or hyphens.":"Usage: leftoff relay register <name> (your subdomain, e.g. yourname for yourname.leftoff.dev)"),2;let f={...Hn(a),user:d.user,domain:String(o.domain||Hn(a).domain||Un)},h=await wT(o);if(!h)return s("The admin token is needed: the ADMIN_TOKEN secret you set with `pnpm exec wrangler secret put ADMIN_TOKEN` (docs/relay.md). No token? Use `leftoff relay signup "+d.user+"` instead."),2;let p=Nn(u),m;try{m=await $i(f,"/_admin/register",{machine:u,sign:p.signX,replace:!!o.replace},h)}catch(g){return s("Couldn’t reach "+Bt(f)+": "+g.message+". Is the relay deployed and the DNS record in place? (docs/relay.md)"),1}if(m.status===401)return s("The relay refused the admin token."),1;if(m.status===409)return s(m.json.message||"This Mac is registered with another key. Add --replace to swap it."),1;if(m.status!==200)return s("The relay answered "+m.status+": "+(m.json.message||m.json.code||m.text.slice(0,200))),1;ys(a,{enabled:!0,user:d.user,domain:f.domain,registeredAt:new Date().toISOString()}),t("✓ "+u+" is on "+Bt(f)+" (Macs there: "+m.json.machines.join(", ")+")");try{await e("POST","/v1/relay/enable",{user:d.user,domain:f.domain}),t("✓ Use from anywhere is on; the daemon is connecting")}catch{t(" Start the daemon (leftoff setup) and it connects on its own.")}return t(" Next: leftoff relay pair (pair your phone), and turn on Use from anywhere on your other Mac: it joins on its own."),0}if(i==="status"){let c;try{c=await l("GET","/v1/relay")}catch(u){if(!u.offline)throw u;let d=Hn(a);c={...d,host:d.user?Bt(d):null,boardUrl:d.user?Vi(d):null,connected:!1,problem:"the daemon isn’t running",list:[]}}if(o.json)return t(JSON.stringify(c,null,2)),0;for(let u of gS(c))t(u);return 0}if(i==="enable"){let c=r[1]||Hn(a).user;if(!c)return s("Usage: leftoff relay enable <name> (the first time, on one Mac: leftoff relay signup <name>)"),2;let u=await l("POST","/v1/relay/enable",{user:c,...o.domain?{domain:o.domain}:{}});t("✓ Use from anywhere is on for this Mac: "+u.host),await new Promise(d=>setTimeout(d,1500));for(let d of gS(await l("GET","/v1/relay")))t(d);return 0}if(i==="disable")return await l("POST","/v1/relay/disable"),t("✓ Use from anywhere is off for this Mac (paired phones stay paired; they reach your other Macs)"),0;if(i==="pair"){let c=await l("POST","/v1/relay/pair");if(t("Scan this with your phone’s camera, or open "+c.link.split("#")[0]+" and type the code."),!o["no-qr"])try{t(bc(c.link))}catch{}return t(" Link: "+c.link),t(" Code: "+c.code+" (works once, for 10 minutes)"),0}if(i==="devices"){let c=await l("GET","/v1/relay");if(o.json)return t(JSON.stringify(c.list,null,2)),0;if(!c.list.length)return t("No paired devices. leftoff relay pair"),0;for(let u of c.list)t(u.id+" "+u.name+" · paired "+au(u.createdAt)+" on "+u.pairedOn+(u.lastSeenAt?" · last used "+au(u.lastSeenAt):"")+" · reaches "+u.macs.join(", "));return 0}if(i==="revoke"){if(!r[1])return s("Usage: leftoff relay revoke <device id> (ids: leftoff relay devices)"),2;let u=(await l("GET","/v1/relay")).list.find(f=>f.id===r[1]||f.id.startsWith(r[1])||f.name.toLowerCase()===r[1].toLowerCase());if(!u)return s("No paired device matches "+r[1]),1;let d=await l("POST","/v1/relay/devices/"+u.id+"/revoke");return t("✓ Revoked "+u.name+(d.relay==="done"?" (the relay refuses it now)":" (the relay hears it when this Mac reconnects; your Macs refuse it already)")),0}return s("Usage: leftoff relay [status|signup [name] [--email=you@x]|enable [name]|disable|pair|devices|revoke <id>|register <name>]"),2}catch(c){return s(c.message),1}}Ns();lh();ko();Ti();Vo();Gs();Hf();var Ch=bs.resolve(bs.dirname(kT(import.meta.url)),".."),Zt=process.argv.slice(2),OS=Zt[0],fe={},dt=[];for(let n of Zt.slice(1)){let e=n.match(/^--([^=]+)(?:=(.*))?$/);e?fe[e[1]]=e[2]===void 0?!0:e[2]:dt.push(n)}async function Le(n,e,t){let s=Q(),o=await fetch("http://127.0.0.1:"+s.port+e,{method:n,headers:{authorization:"Bearer "+(ve()||""),"content-type":"application/json"},body:t?JSON.stringify(t):void 0,signal:AbortSignal.timeout(n==="GET"?2e3:9e4)}),r=await o.json().catch(()=>({}));if(!o.ok)throw new Error(r.error&&r.error.message||"HTTP "+o.status);return r}async function bT(){let n=Z();Oe(n);let e=Q(n);fe.port&&(e.port=Number(fe.port));let t=i=>console.log(new Date().toISOString()+" "+i);for(let i of yp(n))t("moved "+i+" out of the folder to "+bs.join(en(),i)+" (keys stay on this Mac)");let s=new kc({root:n,cfg:e,log:t}).load().start();s.relayHost=new Xi({engine:s,root:n,port:e.port,log:t}),await s.startPeers();let o=N0(s,{root:n,port:e.port});try{await o.listen()}catch(i){console.error(i.code==="EADDRINUSE"?"Port "+e.port+" is in use. Is the daemon already running? Try: leftoff status":i.message),process.exit(1)}t("leftoff "+X+" listening on http://127.0.0.1:"+e.port+" · folder "+n+" · summarizer "+s.summarizer.mode),await s.relayHost.start().catch(i=>t("relay: "+i.message));let r=async()=>{t("shutting down"),s.stop(),s.relayHost.stop(),await s.stopPeers().catch(()=>{}),await o.close(),process.exit(0)};process.on("SIGINT",r),process.on("SIGTERM",r)}async function vT(){let n=Oe();if(console.log("✓ folder ready: "+n.root+" (config.json, token)"),fe.hooks){let e=er();console.log("✓ settings hooks added to "+e+" (a backup sits next to it)")}if(fe.service){let e=Nt();Xt.mkdirSync(bs.dirname(e),{recursive:!0}),Xt.writeFileSync(e,Ln({root:Z()})),console.log("✓ login service written: "+e),console.log(" enable it with: "+Qy())}console.log(""),console.log("Next:"),fe.service||console.log(" 1. start the daemon: leftoff serve (or re-run init with --service)"),console.log(" 2. load the mod: claude --plugin-dir "+bs.join(Ch,"mod")+" (one session)"),console.log(" or install it: claude plugin marketplace add "+Ch+" && claude plugin install leftoff@checkpoints"),fe.hooks||console.log(" no mods allowed? leftoff init --hooks (settings-hook fallback)"),console.log(" 3. open the UI: leftoff open (http://127.0.0.1:"+Q().port+"/)")}async function xT(){try{let n=await Le("GET","/v1/health"),e=typeof fe.repo=="string"?fe.repo:fe.repo===!0?dt[0]:null,{sessions:t}=await Le("GET","/v1/sessions?limit="+(e?300:30));e&&(t=e.split(",").flatMap(r=>ol(t,r.trim())).filter((r,i,a)=>a.indexOf(r)===i).slice(0,30)),console.log("daemon up · v"+n.version+" · "+n.sessionsOpen+" open · spool "+n.spoolPending+" · summarizer "+n.summarizer);let s=Q(),o=await Bi({port:s.port,prefer:process.env.CHECKPOINTS_OPEN_HOST||s.openHost||"auto",token:ve()});console.log("board "+o.url+(o.pretty?" (or http://127.0.0.1:"+s.port+"/)":"")+" · reopen it with: leftoff open"),n.remote&&n.remote.enabled?console.log("remote "+n.remote.urls.join(" ")+" (tailnet"+(n.remote.requireToken?", one-time login links":", no login")+") · leftoff tailscale status"):console.log("remote off · to open the board from your phone or other machines: leftoff tailscale on"),n.update&&n.update.available&&console.log("! update available: "+(n.update.remoteVersion?"v"+n.update.remoteVersion:n.update.remoteSha)+" · run: leftoff upgrade"),n.mod&&n.mod.failed&&n.mod.failed.length&&console.log("! mod commands failed to register: "+n.mod.failed.map(r=>"/"+r.want).join(", ")+" (see: leftoff doctor)"),e&&console.log(t.length?"repo "+[...new Set(t.map(r=>r.repoLabel||r.repoName))].join(", ")+" · "+t.length+" session"+(t.length===1?"":"s"):'no sessions in a repo matching "'+e+'"');for(let r of t){let i=r.live==="open"?"●":r.live==="closed"?"○":"·",a=r.live==="closed"?"closed "+sa(r.closedAt):r.terminal?r.terminal.label:"";console.log(" "+i+" "+r.status.padEnd(11)+" "+(r.title||"(untitled)").slice(0,44).padEnd(44)+" "+String(r.repoLabel||r.repoName).padEnd(16)+" cp:"+(r.shortId||r.id.slice(0,6))+(a?" "+a:""))}t.some(r=>r.live==="open"&&r.jump&&r.jump.supported)&&console.log("jump to one: leftoff go [name]");try{let r=await Le("GET","/v1/restore");r.sessions&&r.sessions.length&&console.log(r.sessions.length+" session"+(r.sessions.length===1?" was":"s were")+" open before the restart · put them back: leftoff restore")}catch{}}catch(n){console.log("daemon not reachable ("+n.message+"). Start it with: leftoff serve"),process.exitCode=1}}function AT(n){let e=s=>s?new Date(s).toLocaleString([],{weekday:"short",hour:"numeric",minute:"2-digit"}):"?";if(n.lastError)return"✗ digest: the last one failed "+e(n.lastError.at)+" ("+n.lastError.message+"); retried in 10 min, or run: leftoff digest";let t=n.today?"today's made "+e(n.today.generatedAt)+(n.today.by?" by "+n.today.by:"")+" ("+(n.today.sessions!=null?n.today.sessions+" session"+(n.today.sessions===1?"":"s"):"?")+")":n.todayNote&&/^waiting for /.test(n.todayNote)?"none yet today; "+n.first+" makes it, this Mac at "+e(n.nextAt)+" if not":"none yet today";return(n.today?"✓ ":"· ")+"digest: "+t+(n.time?" · next automatic "+e(n.nextAt):" · automatic digests off (digest.time is null)")}async function TT(){let n=typeof fe.date=="string"?fe.date:void 0;if(!fe.offline)try{let a=await Le("POST","/v1/digests",{date:n});process.stdout.write(a.markdown);return}catch(a){if(!/fetch failed|ECONNREFUSED|aborted|timeout/i.test(a.message+(a.cause?a.cause.code:"")))throw a}let e=Z();Oe(e);let t=Q(e),s=new xt(e,de(t)),o=(()=>{try{return new Ct(e,de(t)).view({fresh:!0})}catch{return null}})(),{meta:r,markdown:i}=await jl(s.loadAll(),{cfg:t,date:n,machine:de(t),summarizer:new At(t),agents:o,spaces:Is(e).map});s.writeDigest(r.date,i),process.stdout.write(i)}async function wS(){process.exitCode=pS({argv:Zt.slice(1)})}async function _T(){process.exitCode=await mS({argv:Zt.slice(1),api:Le})}async function SS(){process.exitCode=await yS({argv:Zt.slice(1),api:Le})}async function kS(){process.exitCode=await oS({argv:Zt.slice(1),api:Le})}async function bS(){process.exitCode=await x0({argv:Zt.slice(1),api:Le})}async function ET(){process.exitCode=await A0({argv:Zt.slice(1)})}async function OT(){process.exitCode=await nh({argv:Zt.slice(1)})}async function vS(){let n=dt[0];if(n==="allow"||n==="deny"){let t=await Le("POST","/v1/peers/allow",{allow:n==="allow"});console.log((t.allowIncoming?"✓ Other machines may now answer and Go on ":"✓ Other machines may no longer answer or Go on ")+t.machine+(t.listening?" (listening on port "+t.listening+")":""));return}if(n==="trust"){if(!dt[1])throw new Error("usage: leftoff machines trust <machine>");await Le("POST","/v1/peers/"+encodeURIComponent(dt[1])+"/trust"),console.log("✓ Trusting "+dt[1]+"’s new key");return}let e=await Le("POST","/v1/peers/check");if(fe.json){console.log(JSON.stringify(e,null,2));return}console.log("This machine: "+e.machine+" key "+e.fingerprint+" · other machines may answer and Go here: "+(e.allowIncoming?"yes":"no (leftoff machines allow)")),e.relay&&console.log("Relay: "+e.relay.url+(e.relay.connected?" (connected)":" (not connected)")),e.peers.length||console.log("No other machines in this folder yet. Run `leftoff move <synced folder>` here and on the other Mac.");for(let t of e.peers)console.log(" "+t.machine.padEnd(16)+(t.usable.ok?"✓ can answer and Go there ("+t.usable.via+")":"· "+t.usable.reason)+" key "+t.fingerprint+(t.trust==="key_changed"?" KEY CHANGED: leftoff machines trust "+t.machine:""))}async function xS(){process.exitCode=await uS({argv:Zt.slice(1),api:Le})}async function AS(){process.exitCode=await rS({argv:Zt.slice(1),api:Le})}async function CT(){let n=dt[0];if(!n)throw new Error("usage: leftoff checkpoint <session-id> [--short|--informative|--long] [note]");let e=fe.short?"short":fe.long?"long":fe.informative?"informative":fe.length||void 0,t=await Le("POST","/v1/sessions/"+encodeURIComponent(n)+"/checkpoints",{trigger:"manual",generate:!0,length:e,userNote:dt.slice(1).join(" ")||void 0});console.log(Nc(t))}async function IT(){let n=Z(),e=be(n),t=Q(n),s=(c,u)=>console.log((c?"✓ ":"✗ ")+u);s(Xt.existsSync(e.root),"folder "+e.root),s(!!ve(n),"token "+e.token);let o=null;try{o=await Le("GET","/v1/health"),s(!0,"daemon on 127.0.0.1:"+t.port+" (v"+o.version+", summarizer "+o.summarizer+", spool "+o.spoolPending+")")}catch(c){s(!1,"daemon on 127.0.0.1:"+t.port+": "+c.message)}let r=o&&o.mod;if(!o)try{r=JSON.parse(Xt.readFileSync(bs.join(n,".mod-health."+de(t).replace(/[^\w.-]/g,"_")+".json"),"utf8"))}catch{}for(let c of PT(r))console.log(c);if(o&&o.update){let c=o.update;c.available?console.log("! update available: v"+X+" → "+(c.remoteVersion?"v"+c.remoteVersion:c.remoteSha)+" · run: leftoff upgrade"):c.error?console.log("· update check: couldn't reach the repo ("+c.error+")"):c.checkedAt?console.log("✓ up to date (v"+X+", checked "+c.checkedAt.replace("T"," ").slice(0,16)+" UTC)"):console.log("· update check: "+(c.enabled===!1?"off (Settings)":"not run yet"))}let i="";try{i=Xt.readFileSync(cn(),"utf8")}catch{}let a=i.includes(ai)||i.includes($s);if(console.log((a?"✓ ":"· ")+"settings hooks "+(a?"installed":"not installed (fine if the mod is loaded)")),i.includes($s)&&console.log("! settings hooks are from before v0.4.5 (they still work); refresh them with: leftoff upgrade --force"),Xt.existsSync(tr())&&console.log("! the login service still has its old name ("+tr().replace(Oh.homedir(),"~")+"); move it with: leftoff upgrade --force"),console.log('· mod: run /plugin in Claude Code and look for "'+Me+'" (before v0.4.5: checkpoints@checkpoints; leftoff upgrade moves it); in a session type /cp, /cp-name or /leftoff'),console.log("· summarizer mode: "+new At(t).mode),o&&o.digest&&console.log(AT(o.digest)),o&&o.sessions)for(let c of await NT(o.sessions))console.log(c);console.log(bh(kh({port:t.port,cfgHosts:t.remoteHosts||[]}),{cfgHosts:t.remoteHosts||[]}));try{let c={ok:"✓ ",warn:"! ",info:"· "};for(let[u,d]of ah(await tn({root:n}),$n(Pt())))console.log(c[u]+d)}catch(c){console.log("· Syncthing: couldn't check ("+c.message+")")}let l=(()=>{try{return Xt.readdirSync(e.secret).filter(c=>/-api-key$/.test(c))}catch{return[]}})();try{let c=null;if(o)try{c=await Le("GET","/v1/agents")}catch{}c||(c=iu(new Ct(n,de(t)))),c.agents.length||console.log("· agents: none (add a feed: leftoff agents add <name> <url> --key <key>)");for(let u of c.agents){let d=_h(u);console.log((d.ok===!1?"✗ ":d.ok?"✓ ":"· ")+"agent "+d.line);for(let f of d.details.slice(0,3))console.log(" - "+f)}}catch(c){console.log("· agents: couldn't check ("+c.message+")")}l.length&&console.log("! API key files inside the folder ("+l.join(", ")+"): the daemon moves them to "+en().replace(Oh.homedir(),"~")+" at its next start");for(let c of await MT(o))console.log(c)}async function NT(n){let e=[];if(n.ghosts){let t="";try{let{sessions:s}=await Le("GET","/v1/sessions?limit=500");t=s.filter(o=>o.ghost).slice(0,5).map(o=>o.shortId+" ("+(o.live==="open"?"open, heartbeating":o.live)+")").join(", ")}catch{}e.push("! "+n.ghosts+(n.ghosts===1?" session is":" sessions are")+" known only by id (no folder, no process, no turns)"+(t?": "+t:"")+`. Usually one that was open with no prompt yet when an older daemon restarted. The daemon keeps looking it up; it's folded under "empty sessions" on the board and shows as itself after its next prompt.`)}return n.empty-(n.ghosts||0)>0&&e.push("· empty sessions (open, no prompt yet): "+(n.empty-(n.ghosts||0))+", folded into one row on the board"),n.healed&&e.push("✓ filled in "+n.healed+(n.healed===1?" session":" sessions")+" the daemon knew only by id (folder, process or terminal)"),n.archivedGhosts&&e.push("✓ archived "+n.archivedGhosts+" empty "+(n.archivedGhosts===1?"session":"sessions")+" with no folder or process (v0.4.8 cleanup"+(n.lastArchivedAt?", "+n.lastArchivedAt.replace("T"," ").slice(0,16)+" UTC":"")+"); their files are kept, marked archived, not deleted"),e.length||e.push("✓ no empty or id-only sessions"),e}async function MT(n){let e=[],t=Q0(process.env),s=t.term;if(e.push((t.supported?"✓ ":"· ")+"terminal: "+(s.label||s.kind)+(t.supported?" (Go can focus its tabs)":" (Go: "+t.reason+")")),DT()===!1&&e.push("· tab titles: off (config tabTitle: false), so Go matches Ghostty tabs by folder only"),(process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform)!=="darwin"||!["ghostty","iterm2","apple-terminal"].includes(s.kind==="tmux"?s.host:s.kind))return e;let r=s.kind==="tmux"?s.host:s.kind;if(fe["no-prompt"])return e;let i=await sc(r,{timeoutMs:8e3});if(e.push((i.ok?"✓ ":"✗ ")+"automation from this terminal: "+i.message),n)try{let a=await Le("GET","/v1/jump/check?kind="+r);e.push((a.ok?"✓ ":"✗ ")+"automation from the daemon (the board's Go button): "+a.message)}catch(a){e.push("· automation from the daemon: "+a.message)}if(r==="ghostty"&&e.push(...await LT(n,s)),process.platform==="darwin"){let a=Nt(),l=null;try{l=Xt.readFileSync(a,"utf8")}catch{}l!=null&&!/<key>ProcessType<\/key>\s*<string>Interactive<\/string>/.test(l)&&e.push("! login service is from before v0.4.1, so macOS throttles the daemon and the board's Go is slow. Fix: leftoff upgrade --force (rewrites "+a.replace(Oh.homedir(),"~")+" and restarts it)")}return e}async function LT(n,e){let t=[],s=i=>i.steps.map(a=>a.step+" "+a.ms+" ms"+(a.result&&a.result!=="ok"?" ("+a.result+")":"")).join(" · "),o=i=>i?(i.tty?" · tty lookup on":" · tty lookup off")+(i.version?" (Ghostty "+i.version+")":""):"",r=await Zl({hint:{resourcesDir:e.resourcesDir,version:e.version}});if(t.push((r.ok?"✓ ":"✗ ")+"Go speed from this terminal: "+s(r)+o(r.caps)+(r.ok?"":" · "+(r.stderr||r.code))),n)try{let i=await Le("GET","/v1/jump/speed"),a=i.steps.some(l=>l.ms>1500);t.push((i.ok&&!a?"✓ ":i.ok?"! ":"✗ ")+"Go speed from the daemon: "+s(i)+(i.cache?" · remembers "+i.cache.remembered+" of "+i.cache.sessions+" Ghostty sessions":"")+(i.ok?"":" · "+(i.stderr||i.code))),a&&t.push(" the daemon is much slower than this terminal: see the login service line below, or run the daemon in a terminal (`leftoff serve`) to compare")}catch(i){t.push("· Go speed from the daemon: "+i.message)}return t}function DT(){try{return Q().tabTitle}catch{return!0}}async function TS(){process.exitCode=await Z0({pos:dt,flags:fe,api:Le})}async function RT(){if(fe.help){console.log(eS);return}process.exitCode=await tS({pos:dt,flags:fe,api:Le,platform:process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform})}function PT(n){if(!n||!Array.isArray(n.commands))return["· mod commands: no report yet (start a Claude Code session with the mod loaded)"];let e=n.commands.filter(r=>r.ok).map(r=>"/"+r.name),t=n.commands.filter(r=>!r.ok),s=n.at?" (session "+String(n.sessionId||"").slice(0,8)+", "+n.at.replace("T"," ").slice(0,16)+" UTC)":"",o=[];e.length&&o.push("✓ mod commands registered: "+e.join(", ")+s);for(let r of t)o.push("✗ mod command /"+r.want+" failed to register: "+(r.error||"unknown error"));return o}async function jT(){let n=mc();console.log("removed "+n+" settings hook(s) from "+cn());for(let e of[Nt(),tr()])Xt.existsSync(e)&&(Xt.unlinkSync(e),console.log("removed "+e+" (stop it: "+(process.platform==="darwin"?"launchctl unload "+e:"systemctl --user disable --now "+bs.basename(e))+")"));console.log("mod: claude plugin uninstall "+Me),fe.purge?(Xt.rmSync(Z(),{recursive:!0,force:!0}),console.log("deleted "+Z())):console.log("kept your folder: "+Z()+" (pass --purge to delete it)")}function lu(){let n=globalThis.LEFTOFF_INVOKED_AS==="checkpoints"?"\n(`checkpoints` is the old name of `leftoff`; both commands do the same thing.)\n":"";console.log(`leftoff ${X} · WTF was I working on? Track every Claude Code session, checkpoint the quiet ones
544
+ `),process.exit(130)):i===""?o=o.slice(0,-1):o+=i};s.on("data",r)}));return String(e).trim()}function _T(){let n=null,e=async t=>(n||(n=(await import("node:readline")).createInterface({input:process.stdin,output:process.stdout})),new Promise(s=>n.question(t,s)));return e.close=()=>{n&&n.close(),n=null},e}async function xS({argv:n=[],api:e,out:t=o=>console.log(o),err:s=o=>console.error(o)}={}){let{flags:o,pos:r}=AT(n),i=r[0]||"status",a=Z(),l=async(c,u,d)=>{try{return await e(c,u,d)}catch(f){throw/fetch failed|ECONNREFUSED/.test(String(f.message)+String(f.cause&&f.cause.code))?Object.assign(new Error("The Leftoff daemon isn’t running. Start it (leftoff setup), then try again."),{offline:!0}):f}};try{if(i==="signup"){Oe(a);let c=Q(a),u=de(c),d=Gn(a),f=_T(),h=String(r[1]||"").trim().toLowerCase();if(!h){if(!process.stdin.isTTY)return s("Usage: leftoff relay signup <name> --email=you@example.com"),2;h=(await f("Your name (for yourname."+(d.domain||Bn)+"): ")).trim().toLowerCase()}let p=Hn(h);if(!p.ok)return f.close(),s(p.code==="reserved"?"“"+h+"” is reserved (www, app, api, docs, feed, relay, admin, status, mail). Pick another.":"A name is 2–32 lowercase letters, digits or hyphens, e.g. yourname for yourname.leftoff.dev."),2;let m={...d,user:p.user,domain:String(o.domain||d.domain||Bn)},g=Ln(u),S=await Ji({s:m,name:p.user,sign:g.signX});if(!S.ok&&S.code!=="legacy")return f.close(),s(S.message),S.code==="unreachable"||S.code==="closed"?1:2;if(S.ok&&S.status==="yours"){if(f.close(),t("✓ "+S.message),!(d.enabled&&d.user===p.user)){ks(a,{enabled:!0,user:p.user,domain:m.domain,registeredAt:d.registeredAt||new Date().toISOString(),via:d.via||"signup"});try{await e("POST","/v1/relay/enable",{user:p.user,domain:m.domain})}catch{}}return t(" Pair your phone: leftoff relay pair"),0}if(S.ok&&(S.status==="taken"||S.status==="pending"))return f.close(),s(S.message),s(" Yours on another Mac? leftoff relay enable "+p.user),1;S.ok&&S.claimed&&t(" "+S.host+" was claimed on leftoff.dev. Use the email you claimed it with.");let k=typeof o.email=="string"?o.email.trim():process.env.LEFTOFF_SIGNUP_EMAIL||"";if(!k){if(!process.stdin.isTTY)return f.close(),s("Add your email: leftoff relay signup "+p.user+" --email=you@example.com"),2;k=(await f("Email (for the confirmation link): ")).trim()}if(f.close(),!Pc(k))return s("That doesn’t look like an email address."),2;let w=await qi({s:m,name:p.user,email:k,machine:u,sign:g.signX});if(!w.ok)return s(w.message),1;if(w.already)t("✓ This Mac already has "+Kt(m)+".");else{if(t("✉ Sent a link to "+w.email+". Open it and press Confirm (it works for 30 minutes)."),w.claimed&&t(" Your leftoff.dev claim for "+p.user+" carries over."),o["no-wait"])return t(" Not waiting (--no-wait). After you confirm: leftoff relay enable "+p.user),0;t(" Waiting for you to confirm… (Ctrl-C stops waiting; the link still works, then run: leftoff relay enable "+p.user+")");let x=!1,v=()=>{x=!0};process.once("SIGINT",v);let b=await Wf({s:m,user:p.user,poll:w.poll,intervalMs:Number(process.env.LEFTOFF_SIGNUP_POLL_MS)||void 0,stop:()=>x});if(process.removeListener("SIGINT",v),b==="stopped")return t(`
545
+ Stopped waiting. Once you press Confirm: leftoff relay enable `+p.user),130;if(b==="expired"||b==="timeout")return s("The link expired before it was confirmed. Run `leftoff relay signup "+p.user+"` again for a fresh one."),1;if(b==="replaced")return s("A newer link was sent for "+p.user+"; this one no longer counts. Use the newest email (or run signup again)."),1;if(b!=="confirmed")return s("Lost track of the signup ("+b+"). Run `leftoff relay signup "+p.user+"` again."),1}ks(a,{enabled:!0,user:p.user,domain:m.domain,registeredAt:new Date().toISOString(),via:"signup"}),t("✓ "+Kt(m)+" is yours. Pair your phone: leftoff relay pair");try{await e("POST","/v1/relay/enable",{user:p.user,domain:m.domain}),t(" Use from anywhere is on; the daemon is connecting.")}catch{t(" Start the daemon (leftoff setup) and it connects on its own.")}return t(" Your other Macs: leftoff relay enable "+p.user+" (while this Mac is awake, it vouches for them)"),0}if(i==="register"){Oe(a);let c=Q(a),u=de(c),d=Hn(r[1]||Gn(a).user||"");if(!d.ok)return s(r[1]?d.code==="reserved"?"“"+r[1]+"” is reserved (www, app, api, docs, feed, relay, admin, status, mail).":"A name is 2–32 lowercase letters, digits or hyphens.":"Usage: leftoff relay register <name> (your subdomain, e.g. yourname for yourname.leftoff.dev)"),2;let f={...Gn(a),user:d.user,domain:String(o.domain||Gn(a).domain||Bn)},h=await TT(o);if(!h)return s("The admin token is needed: the ADMIN_TOKEN secret you set with `pnpm exec wrangler secret put ADMIN_TOKEN` (docs/relay.md). No token? Use `leftoff relay signup "+d.user+"` instead."),2;let p=Ln(u),m;try{m=await Wi(f,"/_admin/register",{machine:u,sign:p.signX,replace:!!o.replace},h)}catch(g){return s("Couldn’t reach "+Kt(f)+": "+g.message+". Is the relay deployed and the DNS record in place? (docs/relay.md)"),1}if(m.status===401)return s("The relay refused the admin token."),1;if(m.status===409)return s(m.json.message||"This Mac is registered with another key. Add --replace to swap it."),1;if(m.status!==200)return s("The relay answered "+m.status+": "+(m.json.message||m.json.code||m.text.slice(0,200))),1;ks(a,{enabled:!0,user:d.user,domain:f.domain,registeredAt:new Date().toISOString()}),t("✓ "+u+" is on "+Kt(f)+" (Macs there: "+m.json.machines.join(", ")+")");try{await e("POST","/v1/relay/enable",{user:d.user,domain:f.domain}),t("✓ Use from anywhere is on; the daemon is connecting")}catch{t(" Start the daemon (leftoff setup) and it connects on its own.")}return t(" Next: leftoff relay pair (pair your phone), and turn on Use from anywhere on your other Mac: it joins on its own."),0}if(i==="status"){let c;try{c=await l("GET","/v1/relay")}catch(u){if(!u.offline)throw u;let d=Gn(a);c={...d,host:d.user?Kt(d):null,boardUrl:d.user?Qi(d):null,connected:!1,problem:"the daemon isn’t running",list:[]}}if(o.json)return t(JSON.stringify(c,null,2)),0;for(let u of vS(c))t(u);return 0}if(i==="enable"){let c=r[1]||Gn(a).user;if(!c)return s("Usage: leftoff relay enable <name> (the first time, on one Mac: leftoff relay signup <name>)"),2;let u=await l("POST","/v1/relay/enable",{user:c,...o.domain?{domain:o.domain}:{}});t("✓ Use from anywhere is on for this Mac: "+u.host),await new Promise(d=>setTimeout(d,1500));for(let d of vS(await l("GET","/v1/relay")))t(d);return 0}if(i==="disable")return await l("POST","/v1/relay/disable"),t("✓ Use from anywhere is off for this Mac (paired phones stay paired; they reach your other Macs)"),0;if(i==="pair"){let c=await l("POST","/v1/relay/pair");if(t("Scan this with your phone’s camera, or open "+c.link.split("#")[0]+" and type the code."),!o["no-qr"])try{t(xc(c.link))}catch{}return t(" Link: "+c.link),t(" Code: "+c.code+" (works once, for 10 minutes)"),0}if(i==="devices"){let c=await l("GET","/v1/relay");if(o.json)return t(JSON.stringify(c.list,null,2)),0;if(!c.list.length)return t("No paired devices. leftoff relay pair"),0;for(let u of c.list)t(u.id+" "+u.name+" · paired "+cu(u.createdAt)+" on "+u.pairedOn+(u.lastSeenAt?" · last used "+cu(u.lastSeenAt):"")+" · reaches "+u.macs.join(", "));return 0}if(i==="revoke"){if(!r[1])return s("Usage: leftoff relay revoke <device id> (ids: leftoff relay devices)"),2;let u=(await l("GET","/v1/relay")).list.find(f=>f.id===r[1]||f.id.startsWith(r[1])||f.name.toLowerCase()===r[1].toLowerCase());if(!u)return s("No paired device matches "+r[1]),1;let d=await l("POST","/v1/relay/devices/"+u.id+"/revoke");return t("✓ Revoked "+u.name+(d.relay==="done"?" (the relay refuses it now)":" (the relay hears it when this Mac reconnects; your Macs refuse it already)")),0}return s("Usage: leftoff relay [status|signup [name] [--email=you@x]|enable [name]|disable|pair|devices|revoke <id>|register <name>]"),2}catch(c){return s(c.message),1}}Ds();fh();vo();Oi();Xo();$s();$f();var Lh=As.resolve(As.dirname(ET(import.meta.url)),".."),en=process.argv.slice(2),DS=en[0],fe={},ht=[];for(let n of en.slice(1)){let e=n.match(/^--([^=]+)(?:=(.*))?$/);e?fe[e[1]]=e[2]===void 0?!0:e[2]:ht.push(n)}async function De(n,e,t){let s=Q(),o=await fetch("http://127.0.0.1:"+s.port+e,{method:n,headers:{authorization:"Bearer "+(ve()||""),"content-type":"application/json"},body:t?JSON.stringify(t):void 0,signal:AbortSignal.timeout(n==="GET"?2e3:9e4)}),r=await o.json().catch(()=>({}));if(!o.ok)throw new Error(r.error&&r.error.message||"HTTP "+o.status);return r}async function OT(){let n=Z();Oe(n);let e=Q(n);fe.port&&(e.port=Number(fe.port));let t=i=>console.log(new Date().toISOString()+" "+i);for(let i of bp(n))t("moved "+i+" out of the folder to "+As.join(nn(),i)+" (keys stay on this Mac)");let s=new vc({root:n,cfg:e,log:t}).load().start();s.relayHost=new ea({engine:s,root:n,port:e.port,log:t}),await s.startPeers();let o=j0(s,{root:n,port:e.port});try{await o.listen()}catch(i){console.error(i.code==="EADDRINUSE"?"Port "+e.port+" is in use. Is the daemon already running? Try: leftoff status":i.message),process.exit(1)}t("leftoff "+X+" listening on http://127.0.0.1:"+e.port+" · folder "+n+" · summarizer "+s.summarizer.mode),await s.relayHost.start().catch(i=>t("relay: "+i.message));let r=async()=>{t("shutting down"),s.stop(),s.relayHost.stop(),await s.stopPeers().catch(()=>{}),await o.close(),process.exit(0)};process.on("SIGINT",r),process.on("SIGTERM",r)}async function CT(){let n=Oe();if(console.log("✓ folder ready: "+n.root+" (config.json, token)"),fe.hooks){let e=tr();console.log("✓ settings hooks added to "+e+" (a backup sits next to it)")}if(fe.service){let e=Lt();Qt.mkdirSync(As.dirname(e),{recursive:!0}),Qt.writeFileSync(e,Rn({root:Z()})),console.log("✓ login service written: "+e),console.log(" enable it with: "+ow())}console.log(""),console.log("Next:"),fe.service||console.log(" 1. start the daemon: leftoff serve (or re-run init with --service)"),console.log(" 2. load the mod: claude --plugin-dir "+As.join(Lh,"mod")+" (one session)"),console.log(" or install it: claude plugin marketplace add "+Lh+" && claude plugin install leftoff@checkpoints"),fe.hooks||console.log(" no mods allowed? leftoff init --hooks (settings-hook fallback)"),console.log(" 3. open the UI: leftoff open (http://127.0.0.1:"+Q().port+"/)")}async function NT(){try{let n=await De("GET","/v1/health"),e=typeof fe.repo=="string"?fe.repo:fe.repo===!0?ht[0]:null,{sessions:t}=await De("GET","/v1/sessions?limit="+(e?300:30));e&&(t=e.split(",").flatMap(r=>il(t,r.trim())).filter((r,i,a)=>a.indexOf(r)===i).slice(0,30)),console.log("daemon up · v"+n.version+" · "+n.sessionsOpen+" open · spool "+n.spoolPending+" · summarizer "+n.summarizer);let s=Q(),o=await $i({port:s.port,prefer:process.env.CHECKPOINTS_OPEN_HOST||s.openHost||"auto",token:ve()});console.log("board "+o.url+(o.pretty?" (or http://127.0.0.1:"+s.port+"/)":"")+" · reopen it with: leftoff open"),n.remote&&n.remote.enabled?console.log("remote "+n.remote.urls.join(" ")+" (tailnet"+(n.remote.requireToken?", one-time login links":", no login")+") · leftoff tailscale status"):console.log("remote off · to open the board from your phone or other machines: leftoff tailscale on"),n.update&&n.update.available&&console.log("! update available: "+(n.update.remoteVersion?"v"+n.update.remoteVersion:n.update.remoteSha)+" · run: leftoff upgrade"),n.mod&&n.mod.failed&&n.mod.failed.length&&console.log("! mod commands failed to register: "+n.mod.failed.map(r=>"/"+r.want).join(", ")+" (see: leftoff doctor)"),e&&console.log(t.length?"repo "+[...new Set(t.map(r=>r.repoLabel||r.repoName))].join(", ")+" · "+t.length+" session"+(t.length===1?"":"s"):'no sessions in a repo matching "'+e+'"');for(let r of t){let i=r.live==="open"?"●":r.live==="closed"?"○":"·",a=r.live==="closed"?"closed "+ia(r.closedAt):r.terminal?r.terminal.label:"";console.log(" "+i+" "+r.status.padEnd(11)+" "+(r.title||"(untitled)").slice(0,44).padEnd(44)+" "+String(r.repoLabel||r.repoName).padEnd(16)+" cp:"+(r.shortId||r.id.slice(0,6))+(a?" "+a:""))}t.some(r=>r.live==="open"&&r.jump&&r.jump.supported)&&console.log("jump to one: leftoff go [name]");try{let r=await De("GET","/v1/restore");r.sessions&&r.sessions.length&&console.log(r.sessions.length+" session"+(r.sessions.length===1?" was":"s were")+" open before the restart · put them back: leftoff restore")}catch{}}catch(n){console.log("daemon not reachable ("+n.message+"). Start it with: leftoff serve"),process.exitCode=1}}function IT(n){let e=s=>s?new Date(s).toLocaleString([],{weekday:"short",hour:"numeric",minute:"2-digit"}):"?";if(n.lastError)return"✗ digest: the last one failed "+e(n.lastError.at)+" ("+n.lastError.message+"); retried in 10 min, or run: leftoff digest";let t=n.today?"today's made "+e(n.today.generatedAt)+(n.today.by?" by "+n.today.by:"")+" ("+(n.today.sessions!=null?n.today.sessions+" session"+(n.today.sessions===1?"":"s"):"?")+")":n.todayNote&&/^waiting for /.test(n.todayNote)?"none yet today; "+n.first+" makes it, this Mac at "+e(n.nextAt)+" if not":"none yet today";return(n.today?"✓ ":"· ")+"digest: "+t+(n.time?" · next automatic "+e(n.nextAt):" · automatic digests off (digest.time is null)")}async function MT(){let n=typeof fe.date=="string"?fe.date:void 0;if(!fe.offline)try{let a=await De("POST","/v1/digests",{date:n});process.stdout.write(a.markdown);return}catch(a){if(!/fetch failed|ECONNREFUSED|aborted|timeout/i.test(a.message+(a.cause?a.cause.code:"")))throw a}let e=Z();Oe(e);let t=Q(e),s=new Tt(e,de(t)),o=(()=>{try{return new It(e,de(t)).view({fresh:!0})}catch{return null}})(),{meta:r,markdown:i}=await Ul(s.loadAll(),{cfg:t,date:n,machine:de(t),summarizer:new _t(t),agents:o,spaces:Ls(e).map});s.writeDigest(r.date,i),process.stdout.write(i)}async function AS(){process.exitCode=kS({argv:en.slice(1)})}async function LT(){process.exitCode=await bS({argv:en.slice(1),api:De})}async function TS(){process.exitCode=await xS({argv:en.slice(1),api:De})}async function _S(){process.exitCode=await uS({argv:en.slice(1),api:De})}async function ES(){process.exitCode=await C0({argv:en.slice(1),api:De})}async function DT(){process.exitCode=await N0({argv:en.slice(1)})}async function RT(){process.exitCode=await ih({argv:en.slice(1)})}async function OS(){let n=ht[0];if(n==="allow"||n==="deny"){let t=await De("POST","/v1/peers/allow",{allow:n==="allow"});console.log((t.allowIncoming?"✓ Other machines may now answer and Go on ":"✓ Other machines may no longer answer or Go on ")+t.machine+(t.listening?" (listening on port "+t.listening+")":""));return}if(n==="trust"){if(!ht[1])throw new Error("usage: leftoff machines trust <machine>");await De("POST","/v1/peers/"+encodeURIComponent(ht[1])+"/trust"),console.log("✓ Trusting "+ht[1]+"’s new key");return}let e=await De("POST","/v1/peers/check");if(fe.json){console.log(JSON.stringify(e,null,2));return}console.log("This machine: "+e.machine+" key "+e.fingerprint+" · other machines may answer and Go here: "+(e.allowIncoming?"yes":"no (leftoff machines allow)")),e.relay&&console.log("Relay: "+e.relay.url+(e.relay.connected?" (connected)":" (not connected)")),e.peers.length||console.log("No other machines in this folder yet. Run `leftoff move <synced folder>` here and on the other Mac.");for(let t of e.peers)console.log(" "+t.machine.padEnd(16)+(t.usable.ok?"✓ can answer and Go there ("+t.usable.via+")":"· "+t.usable.reason)+" key "+t.fingerprint+(t.trust==="key_changed"?" KEY CHANGED: leftoff machines trust "+t.machine:""))}async function CS(){process.exitCode=await gS({argv:en.slice(1),api:De})}async function NS(){process.exitCode=await dS({argv:en.slice(1),api:De})}async function PT(){let n=ht[0];if(!n)throw new Error("usage: leftoff checkpoint <session-id> [--short|--informative|--long] [note]");let e=fe.short?"short":fe.long?"long":fe.informative?"informative":fe.length||void 0,t=await De("POST","/v1/sessions/"+encodeURIComponent(n)+"/checkpoints",{trigger:"manual",generate:!0,length:e,userNote:ht.slice(1).join(" ")||void 0});console.log(Lc(t))}async function jT(){let n=Z(),e=be(n),t=Q(n),s=(c,u)=>console.log((c?"✓ ":"✗ ")+u);s(Qt.existsSync(e.root),"folder "+e.root),s(!!ve(n),"token "+e.token);let o=null;try{o=await De("GET","/v1/health"),s(!0,"daemon on 127.0.0.1:"+t.port+" (v"+o.version+", summarizer "+o.summarizer+", spool "+o.spoolPending+")")}catch(c){s(!1,"daemon on 127.0.0.1:"+t.port+": "+c.message)}let r=o&&o.mod;if(!o)try{r=JSON.parse(Qt.readFileSync(As.join(n,".mod-health."+de(t).replace(/[^\w.-]/g,"_")+".json"),"utf8"))}catch{}for(let c of KT(r))console.log(c);if(o&&o.update){let c=o.update;c.available?console.log("! update available: v"+X+" → "+(c.remoteVersion?"v"+c.remoteVersion:c.remoteSha)+" · run: leftoff upgrade"):c.error?console.log("· update check: couldn't reach the repo ("+c.error+")"):c.checkedAt?console.log("✓ up to date (v"+X+", checked "+c.checkedAt.replace("T"," ").slice(0,16)+" UTC)"):console.log("· update check: "+(c.enabled===!1?"off (Settings)":"not run yet"))}let i="";try{i=Qt.readFileSync(dn(),"utf8")}catch{}let a=i.includes(ci)||i.includes(Ys);if(console.log((a?"✓ ":"· ")+"settings hooks "+(a?"installed":"not installed (fine if the mod is loaded)")),i.includes(Ys)&&console.log("! settings hooks are from before v0.4.5 (they still work); refresh them with: leftoff upgrade --force"),Qt.existsSync(nr())&&console.log("! the login service still has its old name ("+nr().replace(Mh.homedir(),"~")+"); move it with: leftoff upgrade --force"),console.log('· mod: run /plugin in Claude Code and look for "'+Le+'" (before v0.4.5: checkpoints@checkpoints; leftoff upgrade moves it); in a session type /cp, /cp-name or /leftoff'),console.log("· summarizer mode: "+new _t(t).mode),o&&o.digest&&console.log(IT(o.digest)),o&&o.sessions)for(let c of await FT(o.sessions))console.log(c);console.log(Th(Ah({port:t.port,cfgHosts:t.remoteHosts||[]}),{cfgHosts:t.remoteHosts||[]}));try{let c={ok:"✓ ",warn:"! ",info:"· "};for(let[u,d]of dh(await sn({root:n}),Yn(Ft())))console.log(c[u]+d)}catch(c){console.log("· Syncthing: couldn't check ("+c.message+")")}let l=(()=>{try{return Qt.readdirSync(e.secret).filter(c=>/-api-key$/.test(c))}catch{return[]}})();try{let c=null;if(o)try{c=await De("GET","/v1/agents")}catch{}c||(c=lu(new It(n,de(t)))),c.agents.length||console.log("· agents: none (add a feed: leftoff agents add <name> <url> --key <key>)");for(let u of c.agents){let d=Nh(u);console.log((d.ok===!1?"✗ ":d.ok?"✓ ":"· ")+"agent "+d.line);for(let f of d.details.slice(0,3))console.log(" - "+f)}}catch(c){console.log("· agents: couldn't check ("+c.message+")")}l.length&&console.log("! API key files inside the folder ("+l.join(", ")+"): the daemon moves them to "+nn().replace(Mh.homedir(),"~")+" at its next start");for(let c of await UT(o))console.log(c)}async function FT(n){let e=[];if(n.ghosts){let t="";try{let{sessions:s}=await De("GET","/v1/sessions?limit=500");t=s.filter(o=>o.ghost).slice(0,5).map(o=>o.shortId+" ("+(o.live==="open"?"open, heartbeating":o.live)+")").join(", ")}catch{}e.push("! "+n.ghosts+(n.ghosts===1?" session is":" sessions are")+" known only by id (no folder, no process, no turns)"+(t?": "+t:"")+`. Usually one that was open with no prompt yet when an older daemon restarted. The daemon keeps looking it up; it's folded under "empty sessions" on the board and shows as itself after its next prompt.`)}return n.empty-(n.ghosts||0)>0&&e.push("· empty sessions (open, no prompt yet): "+(n.empty-(n.ghosts||0))+", folded into one row on the board"),n.healed&&e.push("✓ filled in "+n.healed+(n.healed===1?" session":" sessions")+" the daemon knew only by id (folder, process or terminal)"),n.archivedGhosts&&e.push("✓ archived "+n.archivedGhosts+" empty "+(n.archivedGhosts===1?"session":"sessions")+" with no folder or process (v0.4.8 cleanup"+(n.lastArchivedAt?", "+n.lastArchivedAt.replace("T"," ").slice(0,16)+" UTC":"")+"); their files are kept, marked archived, not deleted"),e.length||e.push("✓ no empty or id-only sessions"),e}async function UT(n){let e=[],t=rS(process.env),s=t.term;if(e.push((t.supported?"✓ ":"· ")+"terminal: "+(s.label||s.kind)+(t.supported?" (Go can focus its tabs)":" (Go: "+t.reason+")")),BT()===!1&&e.push("· tab titles: off (config tabTitle: false), so Go matches Ghostty tabs by folder only"),(process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform)!=="darwin"||!["ghostty","iterm2","apple-terminal"].includes(s.kind==="tmux"?s.host:s.kind))return e;let r=s.kind==="tmux"?s.host:s.kind;if(fe["no-prompt"])return e;let i=await rc(r,{timeoutMs:8e3});if(e.push((i.ok?"✓ ":"✗ ")+"automation from this terminal: "+i.message),n)try{let a=await De("GET","/v1/jump/check?kind="+r);e.push((a.ok?"✓ ":"✗ ")+"automation from the daemon (the board's Go button): "+a.message)}catch(a){e.push("· automation from the daemon: "+a.message)}if(r==="ghostty"&&e.push(...await HT(n,s)),process.platform==="darwin"){let a=Lt(),l=null;try{l=Qt.readFileSync(a,"utf8")}catch{}l!=null&&!/<key>ProcessType<\/key>\s*<string>Interactive<\/string>/.test(l)&&e.push("! login service is from before v0.4.1, so macOS throttles the daemon and the board's Go is slow. Fix: leftoff upgrade --force (rewrites "+a.replace(Mh.homedir(),"~")+" and restarts it)")}return e}async function HT(n,e){let t=[],s=i=>i.steps.map(a=>a.step+" "+a.ms+" ms"+(a.result&&a.result!=="ok"?" ("+a.result+")":"")).join(" · "),o=i=>i?(i.tty?" · tty lookup on":" · tty lookup off")+(i.version?" (Ghostty "+i.version+")":""):"",r=await ec({hint:{resourcesDir:e.resourcesDir,version:e.version}});if(t.push((r.ok?"✓ ":"✗ ")+"Go speed from this terminal: "+s(r)+o(r.caps)+(r.ok?"":" · "+(r.stderr||r.code))),n)try{let i=await De("GET","/v1/jump/speed"),a=i.steps.some(l=>l.ms>1500);t.push((i.ok&&!a?"✓ ":i.ok?"! ":"✗ ")+"Go speed from the daemon: "+s(i)+(i.cache?" · remembers "+i.cache.remembered+" of "+i.cache.sessions+" Ghostty sessions":"")+(i.ok?"":" · "+(i.stderr||i.code))),a&&t.push(" the daemon is much slower than this terminal: see the login service line below, or run the daemon in a terminal (`leftoff serve`) to compare")}catch(i){t.push("· Go speed from the daemon: "+i.message)}return t}function BT(){try{return Q().tabTitle}catch{return!0}}async function IS(){process.exitCode=await oS({pos:ht,flags:fe,api:De})}async function GT(){if(fe.help){console.log(iS);return}process.exitCode=await aS({pos:ht,flags:fe,api:De,platform:process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform})}function KT(n){if(!n||!Array.isArray(n.commands))return["· mod commands: no report yet (start a Claude Code session with the mod loaded)"];let e=n.commands.filter(r=>r.ok).map(r=>"/"+r.name),t=n.commands.filter(r=>!r.ok),s=n.at?" (session "+String(n.sessionId||"").slice(0,8)+", "+n.at.replace("T"," ").slice(0,16)+" UTC)":"",o=[];e.length&&o.push("✓ mod commands registered: "+e.join(", ")+s);for(let r of t)o.push("✗ mod command /"+r.want+" failed to register: "+(r.error||"unknown error"));return o}async function $T(){let n=yc();console.log("removed "+n+" settings hook(s) from "+dn());for(let e of[Lt(),nr()])Qt.existsSync(e)&&(Qt.unlinkSync(e),console.log("removed "+e+" (stop it: "+(process.platform==="darwin"?"launchctl unload "+e:"systemctl --user disable --now "+As.basename(e))+")"));console.log("mod: claude plugin uninstall "+Le),fe.purge?(Qt.rmSync(Z(),{recursive:!0,force:!0}),console.log("deleted "+Z())):console.log("kept your folder: "+Z()+" (pass --purge to delete it)")}function uu(){let n=globalThis.LEFTOFF_INVOKED_AS==="checkpoints"?"\n(`checkpoints` is the old name of `leftoff`; both commands do the same thing.)\n":"";console.log(`leftoff ${X} · WTF was I working on? Track every Claude Code session, checkpoint the quiet ones
546
546
  ${n}
547
547
  leftoff setup guided install: the mod, a summarizer (local models too), the daemon
548
548
  [--yes] [--dir=~/checkpoints] [--machine=NAME] [--capture=mod|hooks|none]
@@ -610,4 +610,4 @@ ${n}
610
610
  leftoff hook <Event> settings-hook entry point (reads stdin)
611
611
  leftoff doctor
612
612
  leftoff uninstall [--purge]
613
- `)}async function FT(){let n=await th({flags:fe});n.ok||(process.exitCode=n.cancelled?130:1)}async function _S(){process.exitCode=await Nw({flags:fe,bin:bs.join(Ch,"bin","leftoff.js")})}async function UT(){process.exitCode=await G0({flags:fe})}async function ES(){process.exitCode=await B0({flags:fe})}async function HT(){if(fe.help){console.log(na());return}process.exitCode=await vh({sub:"link",flags:fe})}async function BT(){if(fe.help){console.log(na());return}process.exitCode=await vh({sub:dt[0]||"status",flags:fe})}async function GT(){process.exitCode=await R0({sub:dt[0],args:dt.slice(1),flags:fe})}var KT={setup:FT,models:GT,go:TS,jump:TS,restore:RT,tailscale:BT,link:HT,upgrade:UT,changelog:ES,"whats-new":ES,open:_S,board:_S,serve:bT,init:vT,status:xT,digest:TT,catchup:kS,"catch-up":kS,todo:AS,todos:AS,spaces:wS,space:wS,standup:_T,relay:SS,anywhere:SS,agents:xS,agent:xS,move:OT,sync:bS,syncthing:bS,join:ET,machines:vS,peers:vS,checkpoint:CT,doctor:IT,uninstall:jT,hook:async()=>{dt[0]==="PermissionRequest"?(await D0(),process.exit(0)):await L0(dt[0])},help:lu,"--help":lu,"-h":lu,version:()=>console.log(X),"--version":()=>console.log(X)},$T=KT[OS]||lu;Promise.resolve($T()).catch(n=>{console.error((globalThis.LEFTOFF_INVOKED_AS||"leftoff")+": "+n.message),process.exit(OS==="hook"?0:1)});
613
+ `)}async function zT(){let n=await rh({flags:fe});n.ok||(process.exitCode=n.cancelled?130:1)}async function MS(){process.exitCode=await jw({flags:fe,bin:As.join(Lh,"bin","leftoff.js")})}async function YT(){process.exitCode=await J0({flags:fe})}async function LS(){process.exitCode=await W0({flags:fe})}async function WT(){if(fe.help){console.log(ra());return}process.exitCode=await _h({sub:"link",flags:fe})}async function JT(){if(fe.help){console.log(ra());return}process.exitCode=await _h({sub:ht[0]||"status",flags:fe})}async function qT(){process.exitCode=await B0({sub:ht[0],args:ht.slice(1),flags:fe})}var VT={setup:zT,models:qT,go:IS,jump:IS,restore:GT,tailscale:JT,link:WT,upgrade:YT,changelog:LS,"whats-new":LS,open:MS,board:MS,serve:OT,init:CT,status:NT,digest:MT,catchup:_S,"catch-up":_S,todo:NS,todos:NS,spaces:AS,space:AS,standup:LT,relay:TS,anywhere:TS,agents:CS,agent:CS,move:RT,sync:ES,syncthing:ES,join:DT,machines:OS,peers:OS,checkpoint:PT,doctor:jT,uninstall:$T,hook:async()=>{ht[0]==="PermissionRequest"?(await H0(),process.exit(0)):await U0(ht[0])},help:uu,"--help":uu,"-h":uu,version:()=>console.log(X),"--version":()=>console.log(X)},XT=VT[DS]||uu;Promise.resolve(XT()).catch(n=>{console.error((globalThis.LEFTOFF_INVOKED_AS||"leftoff")+": "+n.message),process.exit(DS==="hook"?0:1)});