@leftoff/cli 0.6.5 → 0.6.9

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 t0=Object.defineProperty;var z=(n,e)=>()=>(n&&(e=n(n=0)),e);var Jt=(n,e)=>{for(var t in e)t0(n,t,{get:e[t],enumerable:!0})};function Xs(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]=Xs(o,r):t[s]=r}return t}function ur(n){return String(n||"").replace(/[^A-Za-z0-9]/g,"").slice(0,8).toLowerCase()||"unknown0"}function Pc(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 Yi(n){let e=String(n||"").split(/[\\/]+/).filter(Boolean);return e.length?e[e.length-1]:""}function fe(n){let e=n&&n.repo;return e&&e.name?String(e.name).split("/").pop():e&&e.root?Yi(e.root):Yi(n&&n.cwd)||"no-repo"}function U(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 P(n){return n==null||n===""?NaN:typeof n=="number"?n:Date.parse(n)}function s0(n){return G(n).replace(/[-:]/g,"")}function o0(n){let e=G(n);return e.slice(0,10)+"T"+e.slice(11,19).replace(/:/g,"")+"Z"}function Ji(n,e,t=0){return"cp_"+s0(e)+"_"+ur(n)+(t?"-"+(t+1):"")}function Bc(n,e=0){return o0(n)+(e?"-"+(e+1):"")+".md"}function Yf(n){return[(G(n.startedAt||Date.now())||"").slice(0,10),Pc(fe(n),24),Pc(n.title,40),ur(n.id)].join("_")}function ir(n){return Pc(String(n||"").replace(/\.local$/i,""),40)||"unknown"}function qi({env:n,hostname:e,cfg:t={}}={}){if(n)return ir(n);let s=t.machineNames||{};return e&&s[e]?ir(s[e]):t.machine?ir(t.machine):ir(e||"unknown")}function jc(n){return n.replace(/[A-Z]/g,e=>"_"+e.toLowerCase())}function Wf(n){return n.replace(/_([a-z0-9])/g,(e,t)=>t.toUpperCase())}function Wi(n,e,t){if(Array.isArray(n))return n.map(o=>t?Wi(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"?Wi(r,e,t):r;return s}function i0(n){return!!(n===""||r0.test(n)||qf.test(n)&&/\d/.test(n)||/^[\s\-?:,\[\]{}#&*!|>'"%@`]/.test(n)||/\s$/.test(n)||/:(\s|$)|\s#|[,\[\]{}\n\r\t"]/.test(n))}function Fc(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(Fc).join(", ")+"]";if(typeof n=="object")return"{"+Object.entries(n).filter(([,s])=>s!==void 0).map(([s,o])=>s+": "+Fc(o)).join(", ")+"}";let e=String(n);return i0(e)?JSON.stringify(e):e}function ys(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+": "+Fc(n[o]));return s.push("---"),s.join(`
2
+ var B0=Object.defineProperty;var J=(n,e)=>()=>(n&&(e=n(n=0)),e);var Qt=(n,e)=>{for(var t in e)B0(n,t,{get:e[t],enumerable:!0})};function ao(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]=ao(o,r):t[s]=r}return t}function Sr(n){return String(n||"").replace(/[^A-Za-z0-9]/g,"").slice(0,8).toLowerCase()||"unknown0"}function Qc(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 ta(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?ta(e.root):ta(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 K(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 K0(n){return K(n).replace(/[-:]/g,"")}function $0(n){let e=K(n);return e.slice(0,10)+"T"+e.slice(11,19).replace(/:/g,"")+"Z"}function oa(n,e,t=0){return"cp_"+K0(e)+"_"+Sr(n)+(t?"-"+(t+1):"")}function ou(n,e=0){return $0(n)+(e?"-"+(e+1):"")+".md"}function gh(n){return[(K(n.startedAt||Date.now())||"").slice(0,10),Qc(me(n),24),Qc(n.title,40),Sr(n.id)].join("_")}function pr(n){return Qc(String(n||"").replace(/\.local$/i,""),40)||"unknown"}function ra({env:n,hostname:e,cfg:t={}}={}){if(n)return pr(n);let s=t.machineNames||{};return e&&s[e]?pr(s[e]):t.machine?pr(t.machine):pr(e||"unknown")}function eu(n){return n.replace(/[A-Z]/g,e=>"_"+e.toLowerCase())}function yh(n){return n.replace(/_([a-z0-9])/g,(e,t)=>t.toUpperCase())}function na(n,e,t){if(Array.isArray(n))return n.map(o=>t?na(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"?na(r,e,t):r;return s}function W0(n){return!!(n===""||Y0.test(n)||kh.test(n)&&/\d/.test(n)||/^[\s\-?:,\[\]{}#&*!|>'"%@`]/.test(n)||/\s$/.test(n)||/:(\s|$)|\s#|[,\[\]{}\n\r\t"]/.test(n))}function tu(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(tu).join(", ")+"]";if(typeof n=="object")return"{"+Object.entries(n).filter(([,s])=>s!==void 0).map(([s,o])=>s+": "+tu(o)).join(", ")+"}";let e=String(n);return W0(e)?JSON.stringify(e):e}function vs(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+": "+tu(n[o]));return s.push("---"),s.join(`
3
3
  `)+`
4
- `}function Uf(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 a0(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 ar(n){let e=a0(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 Uf(e.slice(1,e.lastIndexOf("]"))).map(ar);if(e[0]==="{"){let t={};for(let s of Uf(e.slice(1,e.lastIndexOf("}")))){let o=s.match(/^("(?:[^"\\]|\\.)*"|'[^']*'|[^:]+?)\s*:\s*(.*)$/);o&&(t[String(ar(o[1]))]=ar(o[2]))}return t}if(qf.test(e)&&/\d/.test(e)){let t=Number(e.replace(/_/g,""));if(!isNaN(t))return t}return e}function Be(n){let e=String(n||"").replace(/^\uFEFF/,"").replace(/\r\n/g,`
4
+ `}function uh(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 z0(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 mr(n){let e=z0(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 uh(e.slice(1,e.lastIndexOf("]"))).map(mr);if(e[0]==="{"){let t={};for(let s of uh(e.slice(1,e.lastIndexOf("}")))){let o=s.match(/^("(?:[^"\\]|\\.)*"|'[^']*'|[^:]+?)\s*:\s*(.*)$/);o&&(t[String(mr(o[1]))]=mr(o[2]))}return t}if(kh.test(e)&&/\d/.test(e)){let t=Number(e.replace(/_/g,""));if(!isNaN(t))return t}return e}function $e(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(ar(i[++a].replace(/^\s+-\s+/,"")));r[u]=f.length?f:null}else r[u]=ar(d)}return{data:r,body:o}}function Qs(n){let e=n.id||n.sessionId,t=n.cwd;return(t?"cd "+(o=>/^[A-Za-z0-9_./~@+-]+$/.test(o)?o:"'"+String(o).replace(/'/g,"'\\''")+"'")(t)+" && ":"")+"claude --resume "+e}function Vf(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&&pr[n.length]?n.length:void 0,generator:n.generator?zf(n.generator):{by:"unknown"},schema:1},t=["**Doing:** "+un(n.doing),"**Stopped at:** "+un(n.stoppedAt),"**Next:** "+un(n.next)];n.openQuestion&&t.push("**Open question:** "+un(n.openQuestion)),n.userNote&&t.push("**Note:** "+un(n.userNote));let s=r=>"- "+un(r).replace(/^\*+/,"");for(let r of A0){let i=n[r];if(r==="recap"){i&&t.push("","### "+Kf[r],un(i).replace(/^\*+/,""));continue}if(!(!Array.isArray(i)||!i.length))if(t.push("","### "+Kf[r]),r==="files")for(let a of i)t.push(s(a.path?"`"+String(a.path).replace(/`/g,"")+"`"+(a.why?" — "+un(a.why):""):a.why));else if(r==="nextSteps")i.forEach((a,l)=>t.push(l+1+". "+un(a).replace(/^\*+/,"")));else for(let a of i)t.push(s(a))}let o=Qs({id:n.sessionId,cwd:n.cwd});return ys(e,l0)+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(mr(i[++a].replace(/^\s+-\s+/,"")));r[u]=f.length?f:null}else r[u]=mr(d)}return{data:r,body:o}}function lo(n){let e=n.id||n.sessionId,t=n.cwd;return(t?"cd "+ro(t)+" && ":"")+ru(n,["--resume",e])}function ro(n){let e=String(n);return/^[A-Za-z0-9_./~@%+=:,-]+$/.test(e)?e:"'"+e.replace(/'/g,"'\\''")+"'"}function io(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 ia(n){try{let e=new URL(String(n).trim());return/^https?:$/.test(e.protocol)?e:null}catch{return null}}function V0(n){return/(^|\.)anthropic\.com$/i.test(String(n||""))}function Z0(n={},e=null){if(e&&!io(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()&&!io(o))return String(o).trim();return null}function vh(n={},{model:e=null}={}){let t=n.ANTHROPIC_BASE_URL?ia(n.ANTHROPIC_BASE_URL):null;if(!t||V0(t.hostname))return null;let s=t.hostname.toLowerCase(),o=Z0(n,e);return t.port===bh||/(^|\.)ollama\.com$/.test(s)||n.ANTHROPIC_AUTH_TOKEN==="ollama"?wr({kind:"ollama",model:o,baseUrl:t.origin}):wr({kind:"custom",host:t.host,model:o})}function xh(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?ia(n.baseUrl):null;return wr({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 wr({kind:"custom",host:t,model:e,inferred:n.inferred?!0:null})}return null}function Ah(n){if(!n||io(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 Th(n){let e=n&&n.backend;if(!e)return n&&n.model||null;let t=e.model||(n.model&&!io(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 _h(n){return!!(n&&n.kind==="ollama"&&!Eh(n.baseUrl))}function Eh(n){let e=n?ia(n):null;return!!(e&&/(^|\.)ollama\.com$/i.test(e.hostname))}function ru(n,e=[]){let t=n&&n.backend,s=e.map(ro).join(" ");if(t&&t.kind==="ollama"){if(Eh(t.baseUrl))return"ANTHROPIC_BASE_URL="+ro(t.baseUrl)+" ANTHROPIC_API_KEY= claude"+(t.model?" --model "+ro(t.model):"")+(s?" "+s:"");let o=t.baseUrl?ia(t.baseUrl):null;return(o&&!(q0.has(o.hostname)&&o.port===bh&&o.protocol==="http:")?"OLLAMA_HOST="+ro(o.origin)+" ":"")+"ollama launch claude"+(t.model?" --model "+ro(t.model):"")+" --"+(s?" "+s:"")}return"claude"+(s?" "+s:"")}function Oh(n){let e={id:n.id,session_id:n.sessionId,session_title:n.sessionTitle||null,created_at:K(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&&xr[n.length]?n.length:void 0,generator:n.generator?wh(n.generator):{by:"unknown"},schema:1},t=["**Doing:** "+pn(n.doing),"**Stopped at:** "+pn(n.stoppedAt),"**Next:** "+pn(n.next)];n.openQuestion&&t.push("**Open question:** "+pn(n.openQuestion)),n.userNote&&t.push("**Note:** "+pn(n.userNote));let s=r=>"- "+pn(r).replace(/^\*+/,"");for(let r of fS){let i=n[r];if(r==="recap"){i&&t.push("","### "+hh[r],pn(i).replace(/^\*+/,""));continue}if(!(!Array.isArray(i)||!i.length))if(t.push("","### "+hh[r]),r==="files")for(let a of i)t.push(s(a.path?"`"+String(a.path).replace(/`/g,"")+"`"+(a.why?" — "+pn(a.why):""):a.why));else if(r==="nextSteps")i.forEach((a,l)=>t.push(l+1+". "+pn(a).replace(/^\*+/,"")));else for(let a of i)t.push(s(a))}let o=lo({id:n.sessionId,cwd:n.cwd,backend:n.backend});return vs(e,J0)+t.join(`
9
9
  `)+`
10
10
 
11
- Resume: \``+o+"`\n"}function un(n){return String(n??"").replace(/\s*\n\s*/g," ").trim()}function Zf(n){let{data:e,body:t}=Be(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"?Jf(e.generator):{by:"unknown"},length:e.length&&pr[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=c0[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,rh(i)),s}function Xf(n,e=[]){let t={};for(let a of f0)t[jc(a)]=n[a]===void 0?null:n[a];t.repo&&typeof t.repo=="object"&&(t.repo=zf(t.repo));for(let a of d0)a==="folder"||a==="statusChanges"||n[a]!==void 0&&n[a]!==null&&n[a]!==""&&n[a]!==0&&n[a]!==!1&&(t[jc(a)]=n[a]);t.schema=1;let s=e.slice().sort((a,l)=>P(l.createdAt)-P(a.createdAt)),o=["# "+(n.title||"Untitled session"),""],r=fe(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: `"+Qs(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||Bc(a.createdAt))+") — Next: "+U(a.next,160))}return ys(t,u0)+o.join(`
11
+ Resume: \``+o+"`\n"}function pn(n){return String(n??"").replace(/\s*\n\s*/g," ").trim()}function Ch(n){let{data:e,body:t}=$e(n),s={id:e.id||null,sessionId:e.session_id||null,sessionTitle:e.session_title||null,createdAt:e.created_at?K(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"?Sh(e.generator):{by:"unknown"},length:e.length&&xr[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=X0[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,jh(i)),s}function Nh(n,e=[]){let t={};for(let a of tS)t[eu(a)]=n[a]===void 0?null:n[a];t.repo&&typeof t.repo=="object"&&(t.repo=wh(t.repo));for(let a of eS)a==="folder"||a==="statusChanges"||n[a]!==void 0&&n[a]!==null&&n[a]!==""&&n[a]!==0&&n[a]!==!1&&(t[eu(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 "+(K(n.startedAt)||"?")),o.push(""),o.push("Resume: `"+lo(n)+"`"),o.push(""),o.push("## Checkpoints"),o.push(""),s.length||o.push("_None yet._");for(let a of s){let l=K(a.createdAt).replace("T"," ").slice(0,16)+"Z";o.push("- ["+l+" · "+a.trigger+"](checkpoints/"+(a.file||ou(a.createdAt))+") — Next: "+F(a.next,160))}return vs(t,Q0)+o.join(`
13
13
  `)+`
14
- `}function Qf(n){let{data:e}=Be(n),t={};for(let[s,o]of Object.entries(e))s!=="schema"&&(t[Wf(s)]=o);t.repo&&typeof t.repo=="object"&&(t.repo=Jf(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]));return 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}function Ke(n){return ur(n).slice(0,6)}function dr(n){return"cp:"+Ke(n)}function fr(n){let e=String(n??""),t=e.match(h0);return t?{name:e.slice(0,t.index).trim(),id:t[1].toLowerCase()}:{name:e.trim(),id:null}}function ms(n){return fr(n).name}function p0(n,e){if(!n||!e)return!1;if(fr(n).id)return!0;let t=e.title||null;return!!t&&String(n).trim()===Gc("{name}",{name:t,id:e.id})}function m0(n){return typeof n!="string"||!n.trim()||n===Kc||!/\{name\}|\{repo\}|\{id\}/.test(n)?ye.tabTitleFormat:n}function Gc(n,{name:e,id:t,repo:s,marker:o=!1}={}){let r=m0(n),i=U(ms(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,Ke(t)).replace(/\s+/g," ").trim();return o&&t&&!r.includes("{id}")&&(a+=" · "+dr(t)),a}function g0(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 Rn(n={}){let e=n||{},t={kind:"unknown",label:"",program:e.TERM_PROGRAM||null,version:e.TERM_PROGRAM_VERSION||null},s=g0(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 "+Hf[t.host]:""),{...t,...o}):(t.kind=s,t.label=s==="unknown"?e.TERM_PROGRAM||e.TERM||"unknown terminal":Hf[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 hr(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 $c(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 th(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=hr(t[2]);return{pid:s,tty:$c(t[1]),pidStartedAt:o==null?null:G(e-o*1e3)}}function Pt(n,e,t=ye){if(n.endedAt)return"ended";let s=P(n.lastActivityAt||n.startedAt),o=isNaN(s)?0:e-s,r=t.dormantAfterMin*Gi;if(n.capture==="mod"){let i=P(n.lastHeartbeatAt||n.lastEventAt||n.startedAt),a=P(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 Yc(n,e,t=ye){if(n.ignored)return null;let s=Pt(n,e,t),o=P(n.lastCheckpointAt),r=P(n.lastActivityAt||n.startedAt),i=isNaN(o)||e-o>=t.minGapMin*Gi,a=(n.turnsSinceCheckpoint||0)>0||!isNaN(r)&&(isNaN(o)?!!n.lastActivityAt:r>o);if(s==="ended"||s==="stale")return n.finalCheckpointDone||!a||!i||!n.lastActivityAt?null:n.endReason==="clear"?"clear":"session_end";if(e-r<t.dormantAfterMin*Gi){if(t.longRunCheckpointMin&&s==="active"&&n.activeSince){let c=Math.max(P(n.activeSince),isNaN(o)?0:o);if(e-c>=t.longRunCheckpointMin*Gi&&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&&!(Number(e)>0)&&!n.lastCheckpointId&&!(Number(n.historyTurns)>0)&&n.startSource!=="resume":!0}function eo(n,e=0){return pt(n,e)&&!n.cwd&&!(Number(n.pid)>1)}function nh(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=ir(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)};switch(s.cwd&&(n.cwd=s.cwd),s.transcriptPath&&(n.transcriptPath=s.transcriptPath),s.model&&(n.model=s.model),s.surface&&(n.surface=s.surface),s.repo&&typeof s.repo=="object"&&(n.repo={...n.repo||{},...y0(s.repo)}),s.sessionTitle&&!fr(s.sessionTitle).id&&s.sessionTitle!==n.title&&!p0(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=Rn(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=U(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=U(s.lastAssistantPreview,280)),n.question=s.question?U(s.question,400):null,n.questionAt=s.question?t:null;break;case"notification":n.lastNotification=s.notificationType||null,n.lastNotificationMessage=s.message?U(s.message,200):null,(n0.includes(s.notificationType)||/^elicitation/.test(s.notificationType||""))&&(n.needsInput||(n.needsInputAt=t),n.needsInput=!0);break;case"title_changed":s.title&&ms(s.title)&&(n.title=ms(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 Wc(n){let e=String(n??"").replace(/\r\n?/g,`
14
+ `}function Ih(n){let{data:e}=$e(n),t={};for(let[s,o]of Object.entries(e))s!=="schema"&&(t[yh(s)]=o);t.repo&&typeof t.repo=="object"&&(t.repo=Sh(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]=K(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=xh(t.backend);s?t.backend=s:delete t.backend}return t}function Ye(n){return Sr(n).slice(0,6)}function kr(n){return"cp:"+Ye(n)}function br(n){let e=String(n??""),t=e.match(nS);return t?{name:e.slice(0,t.index).trim(),id:t[1].toLowerCase()}:{name:e.trim(),id:null}}function ks(n){return br(n).name}function sS(n,e){if(!n||!e)return!1;if(br(n).id)return!0;let t=e.title||null;return!!t&&String(n).trim()===au("{name}",{name:t,id:e.id})}function oS(n){return typeof n!="string"||!n.trim()||n===iu||!/\{name\}|\{repo\}|\{id\}/.test(n)?Se.tabTitleFormat:n}function au(n,{name:e,id:t,repo:s,marker:o=!1}={}){let r=oS(n),i=F(ks(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,Ye(t)).replace(/\s+/g," ").trim();return o&&t&&!r.includes("{id}")&&(a+=" · "+kr(t)),a}function rS(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 Un(n={}){let e=n||{},t={kind:"unknown",label:"",program:e.TERM_PROGRAM||null,version:e.TERM_PROGRAM_VERSION||null},s=rS(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 "+dh[t.host]:""),{...t,...o}):(t.kind=s,t.label=s==="unknown"?e.TERM_PROGRAM||e.TERM||"unknown terminal":dh[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 vr(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 lu(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 Lh(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=vr(t[2]);return{pid:s,tty:lu(t[1]),pidStartedAt:o==null?null:K(e-o*1e3)}}function kt(n,e,t=Se){if(n.endedAt)return"ended";let s=R(n.lastActivityAt||n.startedAt),o=isNaN(s)?0:e-s,r=t.dormantAfterMin*Qi;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 cu(n,e,t=Se){if(n.ignored)return null;let s=kt(n,e,t),o=R(n.lastCheckpointAt),r=R(n.lastActivityAt||n.startedAt),i=isNaN(o)||e-o>=t.minGapMin*Qi,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*Qi){if(t.longRunCheckpointMin&&s==="active"&&n.activeSince){let c=Math.max(R(n.activeSince),isNaN(o)?0:o);if(e-c>=t.longRunCheckpointMin*Qi&&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 dt(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 co(n,e=0){return dt(n,e)&&!n.cwd&&!(Number(n.pid)>1)}function Dh(n,e){let t=K(e.at)||K(Date.now()),s=e.data||{};n.id=n.id||e.sessionId,n.capture=e.capture||n.capture||"hooks",e.machine&&!n.machine&&(n.machine=pr(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=xh(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||{},...wr(s.repo)}),s.sessionTitle&&!br(s.sessionTitle).id&&s.sessionTitle!==n.title&&!sS(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=Un(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,(G0.includes(s.notificationType)||/^elicitation/.test(s.notificationType||""))&&(n.needsInput||(n.needsInputAt=t),n.needsInput=!0);break;case"title_changed":s.title&&ks(s.title)&&(n.title=ks(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 uu(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 y0(n){let e={};for(let[t,s]of Object.entries(n))s!=null&&(e[t]=s);return e}function w0(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 Vi(n,e){let t=String(n??"");for(let s of w0(e))t=t.replace(s,"[REDACTED]");return t}function lr(n,e){let t=String(n||"");return e&&t.startsWith(e.replace(/\/+$/,"")+"/")?t.slice(e.replace(/\/+$/,"").length+1):t}function zi(n,e={},t){let s=e||{};return s.file_path?lr(s.file_path,t):s.notebook_path?lr(s.notebook_path,t):s.command?U(s.command,80):s.pattern?U(s.pattern,60):s.url?U(s.url,80):s.path?lr(s.path,t):s.description?U(s.description,60):""}function S0(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(sh.includes(r)&&i){let a=lr(i,e);t.includes(a)||t.push(a)}}return t}function k0(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: "+U(u.text,500)),r=!0)}}else{a.text&&a.text.trim()&&l.push("ASSISTANT: "+U(a.text,1500));for(let u of a.toolUses||[]){let d=u.tool||u.name,f=" -> "+d+(zi(d,u.input,t)?" "+zi(d,u.input,t):"");u.isError&&(f+=" -> error",!r&&u.text&&(f+=": "+U(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 wr(n){let e={};for(let[t,s]of Object.entries(n))s!=null&&(e[t]=s);return e}function iS(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 aa(n,e){let t=String(n??"");for(let s of iS(e))t=t.replace(s,"[REDACTED]");return t}function gr(n,e){let t=String(n||"");return e&&t.startsWith(e.replace(/\/+$/,"")+"/")?t.slice(e.replace(/\/+$/,"").length+1):t}function sa(n,e={},t){let s=e||{};return s.file_path?gr(s.file_path,t):s.notebook_path?gr(s.notebook_path,t):s.command?F(s.command,80):s.pattern?F(s.pattern,60):s.url?F(s.url,80):s.path?gr(s.path,t):s.description?F(s.description,60):""}function aS(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(Rh.includes(r)&&i){let a=gr(i,e);t.includes(a)||t.push(a)}}return t}function lS(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+(sa(d,u.input,t)?" "+sa(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 dn(n,{strict:e=!1}={}){let t=String(n??"").trim().toLowerCase();if(pr[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:b0)}function zc(n=ye,e){if(e){let t=dn(e,{strict:!0});if(t)return t}return dn(n&&n.summary&&n.summary.length)}function Zi({session:n,previous:e,messages:t,cfg:s=ye,wantTitle:o=!1,userNote:r,length:i}){let a=zc(s,i),l=s.summarizer&&s.summarizer.maxInputChars||24e3,c=n.cwd,u=S0(t,c),d=n.repo&&n.repo.branch,f=e?["Doing: "+e.doing,"Stopped at: "+e.stoppedAt,"Next: "+e.next].join(`
19
- `):"none",p=["Repo: "+fe(n)+(d?" (branch "+d+")":"")+" · cwd "+(c||"?"),"Title: "+(n.title||"(none yet)"),"Previous checkpoint: "+f,"Files touched since then: "+(u.length?u.join(", "):"none")];r&&p.push("Note from the developer: "+r),n.needsInput&&p.push("State: the session is waiting on the developer ("+(n.lastNotification||"a prompt")+")."),o&&p.push("Also write the line: Title: <max 6 words>.");let h=k0(t,{maxInputChars:l,cwd:c}),m=s.redact&&s.redact.patterns||[],y=Vi(p.join(`
18
+ `)}function mn(n,{strict:e=!1}={}){let t=String(n??"").trim().toLowerCase();if(xr[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:cS)}function du(n=Se,e){if(e){let t=mn(e,{strict:!0});if(t)return t}return mn(n&&n.summary&&n.summary.length)}function la({session:n,previous:e,messages:t,cfg:s=Se,wantTitle:o=!1,userNote:r,length:i}){let a=du(s,i),l=s.summarizer&&s.summarizer.maxInputChars||24e3,c=n.cwd,u=aS(t,c),d=n.repo&&n.repo.branch,f=e?["Doing: "+e.doing,"Stopped at: "+e.stoppedAt,"Next: "+e.next].join(`
19
+ `):"none",p=["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&&p.push("Note from the developer: "+r),n.needsInput&&p.push("State: the session is waiting on the developer ("+(n.lastNotification||"a prompt")+")."),o&&p.push("Also write the line: Title: <max 6 words>.");let h=lS(t,{maxInputChars:l,cwd:c}),m=s.redact&&s.redact.patterns||[],y=aa(p.join(`
20
20
  `)+`
21
21
  Transcript (oldest→newest, trimmed):
22
- `+(h||"(empty)"),m);return{system:oh[a],prompt:y,filesTouched:u,inputChars:y.length,length:a,maxTokens:pr[a].maxTokens}}function _0(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 Jc(){return{decisions:[],questions:[],files:[],didntWork:[],recap:"",nextSteps:[]}}function rh(n){let e=Jc(),t=null,s=(o,r)=>{if(r=String(r||"").replace(/\*\*/g,"").trim(),!(!r||T0.test(r))){if(o==="recap"){e.recap=e.recap?e.recap+" "+r:r;return}if(o==="files"){let i=_0(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?x0[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(cr))e[o]=e[o].slice(0,cr[o]);return e.recap=U(e.recap,1600),e}function ih(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:U(n,600)||"(empty reply)",stoppedAt:e.stoppedAt||"",next:e.next||"",title:e.title,openQuestion:null,parseOk:!1,...Jc()}:(e.title&&(e.title=U(e.title.replace(/^["']|["']$/g,""),60)),{...e,...rh(s)})}function qc(n,e={},{length:t="short",cwd:s}={}){let o=n||[],r=[...o].reverse().find(k=>k.role==="user"&&k.text&&k.text.trim()),i=[...o].reverse().find(k=>k.role==="assistant"&&k.text&&k.text.trim()),a=null;for(let k=o.length-1;k>=0&&!a;k--){let v=o[k],T=(v.toolResults||[]).find(g=>g.isError),b=(v.toolUses||[]).find(g=>g.isError);if(T?a=T.text:b&&(a=b.text||b.tool+" failed"),v===i&&!a)break}let l=k=>{let v=U(k,400),T=v.match(/^(.{20,240}?[.!?])(\s|$)/);return T?T[1]:U(v,240)},c=r?"Working on: “"+U(r.text.split(`
24
- `)[0],140)+"”":e.firstPrompt?"Working on: “"+U(e.firstPrompt,140)+"”":"No prompt captured yet.",u=a?"Last error: "+U(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.",p=(i?U(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.":p?d="YOU: "+U(p[1],180):a&&(d="Look at the last error and decide whether to retry or change approach.");let h={doing:c,stoppedAt:u,next:d,title:null,openQuestion:null,parseOk:!0},m=dn(t);if(m==="short")return h;let y=Jc(),w=s||e.cwd,S=new Map;for(let k of o)for(let v of k.toolUses||[]){let T=v.tool||v.name,b=v.input&&(v.input.file_path||v.input.notebook_path);if(sh.includes(T)&&b){let g=lr(b,w);S.has(g)||S.set(g,T==="Write"?"written":"edited")}v.isError&&y.didntWork.length<cr.didntWork&&y.didntWork.push(U(T+(zi(T,v.input,w)?" "+zi(T,v.input,w):"")+" failed"+(v.text?": "+v.text.split(`
25
- `)[0]:""),200))}y.files=[...S].slice(-cr.files).map(([k,v])=>({path:k,why:v}));for(let k of o)if(k.role==="assistant"&&k.text){let v=Wc(k.text);v&&!y.questions.includes(v)&&y.questions.push(U(v,200))}if(y.questions=y.questions.slice(-cr.questions),m==="long"){let k=o.find(T=>T.role==="user"&&T.text&&T.text.trim()),v=o.filter(T=>T.role==="user"&&T.text&&T.text.trim()).length;y.recap=[k?"The session started with: “"+U(k.text.split(`
26
- `)[0],160)+"”.":"",v>1?"There were "+v+" prompts since.":"",S.size?S.size+" file"+(S.size===1?" was":"s were")+" changed.":"",a?"The last tool call failed.":"","Written without a model, from the transcript alone."].filter(Boolean).join(" "),y.nextSteps=[d]}return{...h,...y}}function ah(n){let e=String(n.firstPrompt||"").replace(/\s+/g," ").trim().split(" ").filter(Boolean).slice(0,6).join(" "),t=n.repo&&n.repo.branch;return fe(n)+(t?"@"+t:"")+(e?" · "+e:"")}function ch(n){let e=String(n||"").split(`
27
- `).map(t=>t.trim()).find(Boolean)||"";return U(e.replace(/^title\s*:\s*/i,"").replace(/^["'`*]+|["'`*.]+$/g,""),60)}function uh(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 $i(n){return String(n||"").replace(/\*\*(.+?)\*\*/g,"$1").replace(/__(.+?)__/g,"$1").replace(/\s*<!--.*?-->\s*$/,"").replace(/\s+/g," ").trim()}function Rc(n){let e=n.match(E0);return e?{indent:e[1].replace(/\t/g," ").length,done:e[2]!==" ",checkbox:!0,text:e[3]}:(e=n.match(C0),e?{indent:e[1].replace(/\t/g," ").length,done:!1,checkbox:!1,numbered:!0,n:Number(e[2]),text:e[3]}:(e=n.match(O0),e&&!/^[-*_]{3,}$/.test(n.trim())?{indent:e[1].replace(/\t/g," ").length,done:!1,checkbox:!1,text:e[2]}:null))}function gs(n){return String(n||"").replace(/^#+\s*/,"").replace(/\*\*|__/g,"").replace(/[::]\s*$/,"").replace(/\s+/g," ").trim()}function dh(n,{minItems:e=2}={}){let t=String(n||"").replace(/\r\n/g,`
22
+ `+(h||"(empty)"),m);return{system:Ph[a],prompt:y,filesTouched:u,inputChars:y.length,length:a,maxTokens:xr[a].maxTokens}}function pS(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 fu(){return{decisions:[],questions:[],files:[],didntWork:[],recap:"",nextSteps:[]}}function jh(n){let e=fu(),t=null,s=(o,r)=>{if(r=String(r||"").replace(/\*\*/g,"").trim(),!(!r||hS.test(r))){if(o==="recap"){e.recap=e.recap?e.recap+" "+r:r;return}if(o==="files"){let i=pS(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?dS[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(yr))e[o]=e[o].slice(0,yr[o]);return e.recap=F(e.recap,1600),e}function Fh(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,...fu()}:(e.title&&(e.title=F(e.title.replace(/^["']|["']$/g,""),60)),{...e,...jh(s)})}function hu(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 A=o[w],v=(A.toolResults||[]).find(g=>g.isError),k=(A.toolUses||[]).find(g=>g.isError);if(v?a=v.text:k&&(a=k.text||k.tool+" failed"),A===i&&!a)break}let l=w=>{let A=F(w,400),v=A.match(/^(.{20,240}?[.!?])(\s|$)/);return v?v[1]:F(A,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.",p=(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.":p?d="YOU: "+F(p[1],180):a&&(d="Look at the last error and decide whether to retry or change approach.");let h={doing:c,stoppedAt:u,next:d,title:null,openQuestion:null,parseOk:!0},m=mn(t);if(m==="short")return h;let y=fu(),S=s||e.cwd,b=new Map;for(let w of o)for(let A of w.toolUses||[]){let v=A.tool||A.name,k=A.input&&(A.input.file_path||A.input.notebook_path);if(Rh.includes(v)&&k){let g=gr(k,S);b.has(g)||b.set(g,v==="Write"?"written":"edited")}A.isError&&y.didntWork.length<yr.didntWork&&y.didntWork.push(F(v+(sa(v,A.input,S)?" "+sa(v,A.input,S):"")+" failed"+(A.text?": "+A.text.split(`
25
+ `)[0]:""),200))}y.files=[...b].slice(-yr.files).map(([w,A])=>({path:w,why:A}));for(let w of o)if(w.role==="assistant"&&w.text){let A=uu(w.text);A&&!y.questions.includes(A)&&y.questions.push(F(A,200))}if(y.questions=y.questions.slice(-yr.questions),m==="long"){let w=o.find(v=>v.role==="user"&&v.text&&v.text.trim()),A=o.filter(v=>v.role==="user"&&v.text&&v.text.trim()).length;y.recap=[w?"The session started with: “"+F(w.text.split(`
26
+ `)[0],160)+"”.":"",A>1?"There were "+A+" prompts since.":"",b.size?b.size+" file"+(b.size===1?" was":"s were")+" changed.":"",a?"The last tool call failed.":"","Written without a model, from the transcript alone."].filter(Boolean).join(" "),y.nextSteps=[d]}return{...h,...y}}function Uh(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 Bh(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 Gh(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 ea(n){return String(n||"").replace(/\*\*(.+?)\*\*/g,"$1").replace(/__(.+?)__/g,"$1").replace(/\s*<!--.*?-->\s*$/,"").replace(/\s+/g," ").trim()}function Xc(n){let e=n.match(mS);return e?{indent:e[1].replace(/\t/g," ").length,done:e[2]!==" ",checkbox:!0,text:e[3]}:(e=n.match(gS),e?{indent:e[1].replace(/\t/g," ").length,done:!1,checkbox:!1,numbered:!0,n:Number(e[2]),text:e[3]}:(e=n.match(yS),e&&!/^[-*_]{3,}$/.test(n.trim())?{indent:e[1].replace(/\t/g," ").length,done:!1,checkbox:!1,text:e[2]}:null))}function bs(n){return String(n||"").replace(/^#+\s*/,"").replace(/\*\*|__/g,"").replace(/[::]\s*$/,"").replace(/\s+/g," ").trim()}function Kh(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(Gf.test(l)){o=!o;continue}if(o||!l.trim())continue;let c=Rc(l);if(!c){let S=l.match(I0);S?(r=gs(S[1]||S[2]||S[3]||S[4]),i=a):r=null;continue}let u=c.indent,d=[{text:$i(c.text),done:c.done,checkbox:c.checkbox,numbered:!!c.numbered}],f=a+1;for(;f<t.length;f++){let S=t[f];if(Gf.test(S))break;if(!S.trim()){let T=f+1;for(;T<t.length&&!t[T].trim();)T++;let b=T<t.length?Rc(t[T]):null;if(b&&b.indent<=u+1){f=T-1;continue}break}let k=Rc(S),v=S.match(/^\s*/)[0].replace(/\t/g," ").length;if(k&&k.indent<=u+1){d.push({text:$i(k.text),done:k.done,checkbox:k.checkbox,numbered:!!k.numbered});continue}if(v>u){let T=d[d.length-1],b=$i(k?k.text:S);T&&b&&(T.text=k?T.sub?T.text+"; "+b:T.text+" — "+b:T.text+" "+b),T&&k&&(T.sub=!0);continue}break}let p=d.filter(S=>S.checkbox).length,h=r&&i>=a-3?r:null,m=p>=Math.ceil(d.length/2)?"checklist":d.every(S=>S.numbered)?"numbered":"bulleted",y=m==="checklist"||h&&N0.test(h),w=d.filter(S=>S.text);y&&w.length>=e&&s.push({kind:m,heading:h,items:w.map(S=>({text:S.text,done:!!S.done})),line:a}),r=null,a=f-1}return s}function Xi(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 dh(o.text,{minItems:e}))s.push({...i,message:r,at:o.at||null})}),s.map((o,r)=>({...o,n:r+1,name:mr(o,{fallback:t})}))}function Qi(n,e){let t=(n||[]).map(r=>Uc(r.text||r)).filter(Boolean),s=new Set((e||[]).map(r=>Uc(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 M0(n){let e=2166136261;for(let t of(n||[]).map(s=>Uc(s.text||s)).join("|"))e^=t.charCodeAt(0),e=Math.imul(e,16777619)>>>0;return e.toString(16).padStart(8,"0")}function mr(n,{fallback:e=null}={}){let t=gs(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 U(t.charAt(0).toUpperCase()+t.slice(1),60);let s=gs(e);return s?U(s,48)+" checklist":"Checklist"}function fh(n,{minItems:e=3,maxItems:t=60,fallback:s=null}={}){let o=dh(n,{minItems:e});if(!o.length)return null;let r=o[o.length-1],i=r.items.slice(0,t).map(a=>({text:U(a.text,300),done:!!a.done}));return{count:r.items.length,heading:r.heading||null,kind:r.kind,name:mr(r,{fallback:s}),items:i,fp:M0(i)}}function ea(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 Vc(n){let{data:e,body:t}=Be(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(ph.test(l)){o=!o;continue}if(o||!l.trim())continue;let c=Xc(l);if(!c){let b=l.match(wS);b?(r=bs(b[1]||b[2]||b[3]||b[4]),i=a):r=null;continue}let u=c.indent,d=[{text:ea(c.text),done:c.done,checkbox:c.checkbox,numbered:!!c.numbered}],f=a+1;for(;f<t.length;f++){let b=t[f];if(ph.test(b))break;if(!b.trim()){let v=f+1;for(;v<t.length&&!t[v].trim();)v++;let k=v<t.length?Xc(t[v]):null;if(k&&k.indent<=u+1){f=v-1;continue}break}let w=Xc(b),A=b.match(/^\s*/)[0].replace(/\t/g," ").length;if(w&&w.indent<=u+1){d.push({text:ea(w.text),done:w.done,checkbox:w.checkbox,numbered:!!w.numbered});continue}if(A>u){let v=d[d.length-1],k=ea(w?w.text:b);v&&k&&(v.text=w?v.sub?v.text+"; "+k:v.text+" — "+k:v.text+" "+k),v&&w&&(v.sub=!0);continue}break}let p=d.filter(b=>b.checkbox).length,h=r&&i>=a-3?r:null,m=p>=Math.ceil(d.length/2)?"checklist":d.every(b=>b.numbered)?"numbered":"bulleted",y=m==="checklist"||h&&SS.test(h),S=d.filter(b=>b.text);y&&S.length>=e&&s.push({kind:m,heading:h,items:S.map(b=>({text:b.text,done:!!b.done})),line:a}),r=null,a=f-1}return s}function ca(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 Kh(o.text,{minItems:e}))s.push({...i,message:r,at:o.at||null})}),s.map((o,r)=>({...o,n:r+1,name:Ar(o,{fallback:t})}))}function ua(n,e){let t=(n||[]).map(r=>nu(r.text||r)).filter(Boolean),s=new Set((e||[]).map(r=>nu(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 kS(n){let e=2166136261;for(let t of(n||[]).map(s=>nu(s.text||s)).join("|"))e^=t.charCodeAt(0),e=Math.imul(e,16777619)>>>0;return e.toString(16).padStart(8,"0")}function Ar(n,{fallback:e=null}={}){let t=bs(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=bs(e);return s?F(s,48)+" checklist":"Checklist"}function $h(n,{minItems:e=3,maxItems:t=60,fallback:s=null}={}){let o=Kh(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:Ar(r,{fallback:s}),items:i,fp:kS(i)}}function da(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 pu(n){let{data:e,body:t}=$e(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?K(e.created):null,updated:e.updated?K(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(D0);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 to(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 Zc(n){to(n);let e=n.meta,t=ys({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},L0),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(vS);if(l&&!l[1]){let c={id:l[4]?Number(l[4]):null,text:l[3].trim(),done:l[2]!==" ",at:l[5]?K(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 uo(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 mu(n){uo(n);let e=n.meta,t=vs({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},bS),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 hh({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:gs(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:$i(c.text),done:!!c.done,at:c.done?G(a):null});return to(l)}function ph(n,e){let t=(P(e.meta.updated)||0)>(P(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)&&!R0(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}(P(c.at)||0)>(P(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)),to(r)}function R0(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 mh(n){let e=n.items.length,t=n.items.filter(s=>s.done).length;return{done:t,total:e,label:t+"/"+e}}var ye,Hc,$f,n0,zf,Jf,r0,qf,l0,c0,u0,d0,f0,h0,Kc,eh,Hf,Gi,sh,pr,Bx,b0,Dc,Bf,v0,oh,Kx,x0,Kf,A0,T0,cr,lh,Gf,E0,C0,O0,I0,N0,Uc,L0,D0,ve=z(()=>{ye={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"}},Hc=["dormant","manual","session_end","clear","long_run"],$f=["session.opened","session.started","activity","turn_started","turn_completed","notification","title_changed","heartbeat","session.ended"],n0=["permission_prompt","elicitation_dialog","agent_needs_input"];zf=n=>Wi(n,jc,!0),Jf=n=>Wi(n,Wf,!0),r0=/^(null|~|true|false|yes|no|on|off|y|n)$/i,qf=/^[-+]?(\d[\d_]*)?(\.\d+)?([eE][-+]?\d+)?$/;l0=["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"];c0={doing:"doing","stopped at":"stoppedAt",next:"next","open question":"openQuestion",note:"userNote"};u0=["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"],d0=["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"],f0=["id","title","titleSource","machine","cwd","repo","transcriptPath","model","surface","capture","status","startedAt","lastActivityAt","lastHeartbeatAt","endedAt","endReason","turns","lastCheckpointId","lastCheckpointAt","parentSessionId","pinned","archived","ignored"];h0=/\s*(?:·|-|\|)?\s*cp:([a-z0-9]{6})\s*$/i;Kc="{name} · cp:{id}";eh=["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"],Hf={ghostty:"Ghostty",iterm2:"iTerm2",wezterm:"WezTerm",kitty:"kitty","apple-terminal":"Terminal.app",tmux:"tmux",vscode:"VS Code",unknown:"unknown terminal"};Gi=6e4;sh=["Edit","Write","MultiEdit","NotebookEdit"];pr={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."}},Bx=Object.keys(pr),b0="informative";Dc=["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:".'],Bf=['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>"],v0=["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>"],oh={short:[...Dc,"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:[...Dc,...Bf,'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:[...Dc,...Bf,...v0,'Optionally a final line "Title: <max 6 words>" if asked for a title.',"No preamble. No other markdown. Under 600 words total."].join(`
37
- `)},Kx=oh.short;x0={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"},Kf={decisions:"Key decisions",questions:"Open questions",files:"Files touched",didntWork:"What didn't work",recap:"Recap",nextSteps:"Next steps"},A0=["decisions","questions","files","didntWork","recap","nextSteps"],T0=/^(none|n\/a|nothing|-|—|none yet|no(ne)? (open )?questions?)\.?$/i,cr={decisions:6,questions:5,files:10,didntWork:5,nextSteps:5};lh="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.";Gf=/^\s*(```|~~~)/,E0=/^(\s*)(?:[-*+]|\d{1,3}[.)])\s+\[( |x|X)\]\s+(.*\S)\s*$/,C0=/^(\s*)(\d{1,3})[.)]\s+(.*\S)\s*$/,O0=/^(\s*)[-*+•]\s+(.*\S)\s*$/,I0=/^\s*(?:#{1,6}\s+(.+?)\s*#*|\*\*(.+?)\*\*\s*:?|__(.+?)__\s*:?|(\S[^\n]{0,100}?)\s*:)\s*$/,N0=/\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;Uc=n=>String(n||"").toLowerCase().replace(/[`*_~]/g,"").replace(/[^a-z0-9]+/g," ").trim();L0=["name","repo","repo_key","source_session","source_machine","source_title","created","updated","archived","next_id","schema"],D0=/^(\s*)[-*+]\s+\[( |x|X)\]\s+(.*?)\s*(?:<!--\s*#(\d+)(?:\s+(\S+))?\s*-->)?\s*$/});import ae from"node:fs";import ta from"node:os";import xe from"node:path";import P0 from"node:crypto";function na(){return xe.join(ta.homedir(),"checkpoints")}function gh(){return xe.join(process.env.XDG_CONFIG_HOME||xe.join(ta.homedir(),".config"),"checkpoints","folder")}function j0(){try{return ae.readFileSync(gh(),"utf8").trim()||null}catch{return null}}function yh(n){let e=gh();if(xe.resolve(n)===na()){try{ae.unlinkSync(e)}catch{}return null}return ae.mkdirSync(xe.dirname(e),{recursive:!0}),ae.writeFileSync(e,xe.resolve(n)+`
38
- `),e}function V(){return process.env.LEFTOFF_DIR||process.env.CHECKPOINTS_DIR||j0()||na()}function le(n){return qi({env:process.env.CHECKPOINTS_MACHINE,hostname:ta.hostname(),cfg:n||{}})}function we(n=V()){return{root:n,config:xe.join(n,"config.json"),readme:xe.join(n,"README.md"),sessions:xe.join(n,"sessions"),digests:xe.join(n,"digests"),catchups:xe.join(n,"catchups"),spool:xe.join(n,".spool"),secret:xe.join(n,".secret"),token:xe.join(n,".secret","token")}}function Te(n=V()){let e=we(n);for(let t of[e.root,e.sessions,e.digests,e.spool])ae.mkdirSync(t,{recursive:!0});ae.mkdirSync(e.secret,{recursive:!0,mode:448}),ae.existsSync(e.readme)||ae.writeFileSync(e.readme,F0),ae.existsSync(e.config)||ae.writeFileSync(e.config,JSON.stringify(ye,null,2)+`
39
- `),ae.existsSync(e.token)||ae.writeFileSync(e.token,P0.randomBytes(32).toString("hex")+`
40
- `,{mode:384});try{ae.chmodSync(e.token,384)}catch{}return e}function X(n=V()){let e=we(n),t={};try{t=JSON.parse(ae.readFileSync(e.config,"utf8"))}catch{}t.summarizer&&t.summarizer.mode&&!t.summarizer.provider&&(t.summarizer={...t.summarizer,provider:t.summarizer.mode}),t.tabTitleFormat===Kc&&delete t.tabTitleFormat;let s=Xs(ye,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=Xs(s,{summarizer:{provider:process.env.CHECKPOINTS_SUMMARIZER}})),s}function no(n,e=V()){let t=we(e),s=t.config+".tmp-"+process.pid;ae.writeFileSync(s,JSON.stringify(n,null,2)+`
41
- `),ae.renameSync(s,t.config)}function Se(n=V()){try{return ae.readFileSync(we(n).token,"utf8").trim()}catch{return null}}function fn(n=V()){try{return JSON.parse(ae.readFileSync(we(n).config,"utf8"))}catch{return null}}function qt(n=process.env){return xe.join(n.XDG_CONFIG_HOME||xe.join(n.HOME||ta.homedir(),".config"),"checkpoints","secrets")}function wh(n,e=process.env){let t=String(n||"").match(/^\.secret\/([^/]+)$/);return t?xe.join(qt(e),t[1]):null}function sa(n,e,t=V(),s=process.env){let o=qt(s);ae.mkdirSync(o,{recursive:!0,mode:448});try{ae.chmodSync(o,448)}catch{}let r=xe.join(o,n);ae.writeFileSync(r,String(e).trim()+`
42
- `,{mode:384});try{ae.chmodSync(r,384)}catch{}try{ae.unlinkSync(xe.join(we(t).secret,n))}catch{}return xe.join(".secret",n)}function Sh(n=V(),e=process.env){let t=[],s=[];try{s=ae.readdirSync(we(n).secret)}catch{return t}for(let o of s){if(!/-api-key$/.test(o))continue;let r=xe.join(we(n).secret,o),i=xe.join(qt(e),o);try{if(!ae.existsSync(i)&&(ae.mkdirSync(xe.dirname(i),{recursive:!0,mode:448}),ae.copyFileSync(r,i),ae.chmodSync(i,384),ae.readFileSync(i,"utf8")!==ae.readFileSync(r,"utf8")))throw new Error("copy differs");ae.unlinkSync(r),t.push(o)}catch{}}return t}var q,F0,ke=z(()=>{ve();q=(()=>{try{return JSON.parse(ae.readFileSync(new URL("../package.json",import.meta.url),"utf8")).version}catch{return"0.0.0"}})();F0=`# Leftoff
34
+ `}function Yh({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:bs(n)||"List",repo:t,repoKey:s,sourceSession:o,sourceMachine:r,sourceTitle:i,created:K(a),updated:K(a),archived:!1,nextId:0},items:[],segs:null};for(let c of e)c&&String(c.text||"").trim()&&l.items.push({id:null,text:ea(c.text),done:!!c.done,at:c.done?K(a):null});return uo(l)}function Wh(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)&&!xS(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)),uo(r)}function xS(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 zh(n){let e=n.items.length,t=n.items.filter(s=>s.done).length;return{done:t,total:e,label:t+"/"+e}}var Se,su,mh,G0,wh,Sh,Y0,kh,J0,bh,q0,X0,Q0,eS,tS,nS,iu,Mh,dh,Qi,Rh,xr,LA,cS,Zc,fh,uS,Ph,DA,dS,hh,fS,hS,yr,Hh,ph,mS,gS,yS,wS,SS,nu,bS,vS,Ae=J(()=>{Se={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"}},su=["dormant","manual","session_end","clear","long_run"],mh=["session.opened","session.started","activity","turn_started","turn_completed","notification","title_changed","heartbeat","session.ended"],G0=["permission_prompt","elicitation_dialog","agent_needs_input"];wh=n=>na(n,eu,!0),Sh=n=>na(n,yh,!0),Y0=/^(null|~|true|false|yes|no|on|off|y|n)$/i,kh=/^[-+]?(\d[\d_]*)?(\.\d+)?([eE][-+]?\d+)?$/;J0=["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",q0=new Set(["127.0.0.1","localhost","[::1]","::1","0.0.0.0"]);X0={doing:"doing","stopped at":"stoppedAt",next:"next","open question":"openQuestion",note:"userNote"};Q0=["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"],eS=["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"],tS=["id","title","titleSource","machine","cwd","repo","transcriptPath","model","surface","capture","status","startedAt","lastActivityAt","lastHeartbeatAt","endedAt","endReason","turns","lastCheckpointId","lastCheckpointAt","parentSessionId","pinned","archived","ignored"];nS=/\s*(?:·|-|\|)?\s*cp:([a-z0-9]{6})\s*$/i;iu="{name} · cp:{id}";Mh=["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"],dh={ghostty:"Ghostty",iterm2:"iTerm2",wezterm:"WezTerm",kitty:"kitty","apple-terminal":"Terminal.app",tmux:"tmux",vscode:"VS Code",unknown:"unknown terminal"};Qi=6e4;Rh=["Edit","Write","MultiEdit","NotebookEdit"];xr={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."}},LA=Object.keys(xr),cS="informative";Zc=["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:".'],fh=['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>"],uS=["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>"],Ph={short:[...Zc,"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:[...Zc,...fh,'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:[...Zc,...fh,...uS,'Optionally a final line "Title: <max 6 words>" if asked for a title.',"No preamble. No other markdown. Under 600 words total."].join(`
37
+ `)},DA=Ph.short;dS={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"},hh={decisions:"Key decisions",questions:"Open questions",files:"Files touched",didntWork:"What didn't work",recap:"Recap",nextSteps:"Next steps"},fS=["decisions","questions","files","didntWork","recap","nextSteps"],hS=/^(none|n\/a|nothing|-|—|none yet|no(ne)? (open )?questions?)\.?$/i,yr={decisions:6,questions:5,files:10,didntWork:5,nextSteps:5};Hh="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.";ph=/^\s*(```|~~~)/,mS=/^(\s*)(?:[-*+]|\d{1,3}[.)])\s+\[( |x|X)\]\s+(.*\S)\s*$/,gS=/^(\s*)(\d{1,3})[.)]\s+(.*\S)\s*$/,yS=/^(\s*)[-*+•]\s+(.*\S)\s*$/,wS=/^\s*(?:#{1,6}\s+(.+?)\s*#*|\*\*(.+?)\*\*\s*:?|__(.+?)__\s*:?|(\S[^\n]{0,100}?)\s*:)\s*$/,SS=/\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;nu=n=>String(n||"").toLowerCase().replace(/[`*_~]/g,"").replace(/[^a-z0-9]+/g," ").trim();bS=["name","repo","repo_key","source_session","source_machine","source_title","created","updated","archived","next_id","schema"],vS=/^(\s*)[-*+]\s+\[( |x|X)\]\s+(.*?)\s*(?:<!--\s*#(\d+)(?:\s+(\S+))?\s*-->)?\s*$/});import ue from"node:fs";import fa from"node:os";import Te from"node:path";import AS from"node:crypto";function ha(){return Te.join(fa.homedir(),"checkpoints")}function Jh(){return Te.join(process.env.XDG_CONFIG_HOME||Te.join(fa.homedir(),".config"),"checkpoints","folder")}function TS(){try{return ue.readFileSync(Jh(),"utf8").trim()||null}catch{return null}}function qh(n){let e=Jh();if(Te.resolve(n)===ha()){try{ue.unlinkSync(e)}catch{}return null}return ue.mkdirSync(Te.dirname(e),{recursive:!0}),ue.writeFileSync(e,Te.resolve(n)+`
38
+ `),e}function Z(){return process.env.LEFTOFF_DIR||process.env.CHECKPOINTS_DIR||TS()||ha()}function de(n){return ra({env:process.env.CHECKPOINTS_MACHINE,hostname:fa.hostname(),cfg:n||{}})}function ke(n=Z()){return{root:n,config:Te.join(n,"config.json"),readme:Te.join(n,"README.md"),sessions:Te.join(n,"sessions"),digests:Te.join(n,"digests"),catchups:Te.join(n,"catchups"),spool:Te.join(n,".spool"),secret:Te.join(n,".secret"),token:Te.join(n,".secret","token")}}function Ee(n=Z()){let e=ke(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,_S),ue.existsSync(e.config)||ue.writeFileSync(e.config,JSON.stringify(Se,null,2)+`
39
+ `),ue.existsSync(e.token)||ue.writeFileSync(e.token,AS.randomBytes(32).toString("hex")+`
40
+ `,{mode:384});try{ue.chmodSync(e.token,384)}catch{}return e}function X(n=Z()){let e=ke(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===iu&&delete t.tabTitleFormat;let s=ao(Se,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=ao(s,{summarizer:{provider:process.env.CHECKPOINTS_SUMMARIZER}})),s}function fo(n,e=Z()){let t=ke(e),s=t.config+".tmp-"+process.pid;ue.writeFileSync(s,JSON.stringify(n,null,2)+`
41
+ `),ue.renameSync(s,t.config)}function be(n=Z()){try{return ue.readFileSync(ke(n).token,"utf8").trim()}catch{return null}}function gn(n=Z()){try{return JSON.parse(ue.readFileSync(ke(n).config,"utf8"))}catch{return null}}function en(n=process.env){return Te.join(n.XDG_CONFIG_HOME||Te.join(n.HOME||fa.homedir(),".config"),"checkpoints","secrets")}function Vh(n,e=process.env){let t=String(n||"").match(/^\.secret\/([^/]+)$/);return t?Te.join(en(e),t[1]):null}function pa(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=Te.join(o,n);ue.writeFileSync(r,String(e).trim()+`
42
+ `,{mode:384});try{ue.chmodSync(r,384)}catch{}try{ue.unlinkSync(Te.join(ke(t).secret,n))}catch{}return Te.join(".secret",n)}function Zh(n=Z(),e=process.env){let t=[],s=[];try{s=ue.readdirSync(ke(n).secret)}catch{return t}for(let o of s){if(!/-api-key$/.test(o))continue;let r=Te.join(ke(n).secret,o),i=Te.join(en(e),o);try{if(!ue.existsSync(i)&&(ue.mkdirSync(Te.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 V,_S,ve=J(()=>{Ae();V=(()=>{try{return JSON.parse(ue.readFileSync(new URL("../package.json",import.meta.url),"utf8")).version}catch{return"0.0.0"}})();_S=`# 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,78 +56,81 @@ 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 Fh={};Jt(Fh,{BREW_URL:()=>Th,CODE_PREFIX:()=>gr,DOWNLOAD_URL:()=>Ah,FOLDER_ID:()=>Ae,FOLDER_LABEL:()=>vh,INSTALL_COMMANDS:()=>eu,INSTALL_LINE:()=>yr,PAIR_TTL_MS:()=>xh,PROOF_RE:()=>ru,PairCodeError:()=>hn,STIGNORE_LINES:()=>ia,SyncthingClient:()=>ra,SyncthingError:()=>Ze,acceptKnocked:()=>_r,armPairing:()=>xr,autoAcceptTick:()=>Ar,brewPath:()=>la,cancelPairing:()=>au,codeDigest:()=>ou,connect:()=>at,decodePairCode:()=>su,encodePairCode:()=>Qc,ensureDevice:()=>ua,ensureFolder:()=>ca,explainBrewFailure:()=>Mh,findSyncthingConfig:()=>Ih,guiUrl:()=>Oh,installPlan:()=>Ss,knockedDevices:()=>Tr,localDir:()=>oo,nameWithoutProof:()=>ro,nestedIn:()=>bs,pairProof:()=>da,parseSyncthingConfig:()=>Ch,peerAccepted:()=>fa,progressLine:()=>cu,proofName:()=>iu,publicPairing:()=>Fn,readManaged:()=>gn,readPairing:()=>It,readRunStatus:()=>nu,runInstall:()=>Sr,sameDevice:()=>_e,shortId:()=>Pn,startSyncthing:()=>kr,statusFile:()=>tu,stopSyncing:()=>vr,stuckSteps:()=>lu,suggestPath:()=>br,syncthingConfigCandidates:()=>Eh,syncthingInstall:()=>mn,syncthingStatus:()=>Vt,tilde:()=>Ee,untilde:()=>aa,waitForSyncthing:()=>ks,writeManaged:()=>jn,writeRunStatus:()=>ao,writeStignore:()=>Dh});import Me from"node:fs";import U0 from"node:os";import ie from"node:path";import H0 from"node:http";import B0 from"node:https";import io from"node:crypto";import{spawn as bh,spawnSync as kh}from"node:child_process";function oo(n=process.env){return ie.join(n.XDG_CONFIG_HOME||ie.join(mt(n),".config"),"checkpoints")}function wr(n){try{return JSON.parse(Me.readFileSync(n,"utf8"))}catch{return null}}function ws(n,e){Me.mkdirSync(ie.dirname(n),{recursive:!0,mode:448});let t=n+".tmp-"+process.pid;Me.writeFileSync(t,JSON.stringify(e,null,2)+`
60
- `,{mode:384}),Me.renameSync(t,n);try{Me.chmodSync(n,384)}catch{}}function Eh({env:n=process.env,platform:e=process.platform}={}){let t=mt(n),s=[];for(let i of[n.LEFTOFF_SYNCTHING_HOME,n.STHOMEDIR])i&&s.push(ie.join(i,"config.xml"));let o=ie.join(t,"Library","Application Support","Syncthing","config.xml"),r=[ie.join(n.XDG_STATE_HOME||ie.join(t,".local","state"),"syncthing","config.xml"),ie.join(n.XDG_CONFIG_HOME||ie.join(t,".config"),"syncthing","config.xml")];return e==="darwin"?s.push(o,...r):s.push(...r,o),[...new Set(s)]}function Ch(n,{home:e=mt()}={}){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:oa(o),tls:i,apiKey:oa(r)||null,url:Oh(oa(o),i)}}for(let o of String(n).matchAll(/<folder\b([^>]*?)(\/>|>([\s\S]*?)<\/folder>)/g)){let r=o[1],i=so(r,"path");if(!i)continue;i=ie.resolve(i.replace(/^~(?=\/|$)/,e));let a=[...String(o[3]||"").matchAll(/<device\b[^>]*\bid="([^"]+)"/g)].map(l=>l[1]);t.folders.push({id:so(r,"id")||"",label:so(r,"label")||"",path:i,type:so(r,"type")||"sendreceive",devices:a})}for(let o of String(n).matchAll(/<device\b([^>]*\bname="[^"]*"[^>]*)>/g))t.devices.push({id:so(o[1],"id"),name:so(o[1],"name")});return t}function Oh(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 Ih(n={}){let e=n.env||process.env;for(let t of Eh(n)){let s;try{s=Me.readFileSync(t,"utf8")}catch{continue}return{file:t,...Ch(s,{home:mt(e)})}}return null}function at(n={}){let e=n.env||process.env,t=Ih({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:ra.fromConfig(t,{managedDevices:()=>gn(e).devices,...n.client})}}catch(s){return{ok:!1,cfg:t,code:s.code,reason:s.message}}}function la(n=process.env){return n.LEFTOFF_NO_BREW?null:Nh("brew",n)||["/opt/homebrew/bin/brew","/usr/local/bin/brew","/home/linuxbrew/.linuxbrew/bin/brew"].find(e=>{try{return Me.accessSync(e,Me.constants.X_OK),!0}catch{return!1}})||null}function mn(n=process.env){let e=la(n);if(e){let s=ie.join(ie.dirname(e),"syncthing");if(K0(s))return{installed:!0,via:"brew",path:s,brew:e}}let t=Nh("syncthing",n);if(t)return{installed:!0,via:e&&t.startsWith(ie.dirname(ie.dirname(e)))?"brew":"path",path:t,brew:e};for(let s of["/Applications/Syncthing.app",ie.join(mt(n),"Applications","Syncthing.app")])if(G0(s))return{installed:!0,via:"app",path:s,brew:e};return{installed:!1,via:null,path:null,brew:e}}function Ss(n=process.env){return la(n)?{brew:!0,command:yr,steps:eu.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:Th,download:Ah}}}function Mh(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 Sr({env:n=process.env,onLine:e=()=>{},onStage:t=()=>{}}={}){let s=la(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 eu){let a=["brew",...i].join(" ");t(a),e("$ "+a);let l=await new Promise(c=>{let u=bh(s,i,{env:r,stdio:["ignore","pipe","pipe"]}),d="",f=p=>{d+=p;let h;for(;(h=d.search(/[\r\n]/))>=0;){let m=d.slice(0,h);d=d.slice(h+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",p=>{o.push(p.message),e(p.message),c(127)}),u.on("close",p=>{d.trim()&&(o.push(d),e(d)),c(p??1)})});if(l!==0)return{ok:!1,code:"brew_failed",exit:l,stage:a,error:Mh(o,l),lines:o}}return{ok:!0,lines:o}}function kr(n=process.env){let e=mn(n);if(!e.installed)return{ok:!1,code:"not_installed",error:"Syncthing isn’t installed"};if(e.via==="brew"){let o=kh(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 kh("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"};Me.mkdirSync(oo(n),{recursive:!0});let t=Me.openSync(ie.join(oo(n),"syncthing.log"),"a"),s=bh(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 ks({env:n=process.env,timeoutMs:e=9e4,every:t=500}={}){let s=Date.now(),o=null;for(;Date.now()-s<e;){let r=at({env:n});if(r.ok)try{return await r.client.ping(),{ok:!0,...r}}catch(i){o=i.message}else o=r.reason;await _h(t)}return{ok:!1,code:"not_running",error:"Syncthing didn’t come up"+(o?" ("+o+")":"")}}function gn(n=process.env){let e=wr(Lh(n))||{};return{devices:e.devices||[],originalName:e.originalName||null,usedCodes:e.usedCodes||[],folderPath:e.folderPath||null}}function jn(n,e=process.env){ws(Lh(e),{...gn(e),...n})}function $0(n,e){let t=gn(e);t.devices.some(s=>_e(s,n))||jn({devices:[...t.devices,n]},e)}function nu(n=process.env){return wr(tu(n))}function ao(n,e=process.env){ws(tu(e),{...n,at:new Date().toISOString()})}function bs(n,e,t=Ae){let s=ie.resolve(n);for(let o of e||[]){if(o.id===t)continue;let r=ie.resolve(o.path);if(s===r||s.startsWith(r+ie.sep))return{id:o.id,label:o.label||o.id,path:r,how:"inside"};if(r.startsWith(s+ie.sep))return{id:o.id,label:o.label||o.id,path:r,how:"contains"}}return null}function br(n,e=process.env){let t=(n||[]).find(s=>s.id===Ae);if(t)return t.path;for(let s of[ie.join(mt(e),"Sync","Leftoff"),ie.join(mt(e),"Leftoff"),ie.join(mt(e),"Leftoff-Sync")])if(!bs(s,n))return s;return ie.join(mt(e),"Leftoff-Sync")}async function ca(n,{path:e,peers:t=[],myID:s}){let o=await n.folders(),r=bs(e,o);if(r)throw new Ze(Ee(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===Ae);if(i){if(ie.resolve(i.path.replace(/^~(?=\/|$)/,mt()))!==ie.resolve(e))throw new Ze("Syncthing already has a Leftoff folder at "+Ee(i.path)+". Use that one, or press Stop syncing first.",{code:"folder_elsewhere"});let c=new Set((i.devices||[]).map(d=>Ot(d.deviceID))),u=t.filter(d=>!c.has(Ot(d)));return u.length&&await n.patchFolder(Ae,{devices:[...i.devices||[],...u.map(d=>({deviceID:d,introducedBy:"",encryptionPassword:""}))]}),await n.setIgnores(Ae,ia).catch(()=>{}),{created:!1,folder:{...i,path:e}}}Me.mkdirSync(e,{recursive:!0}),Dh(e);let l={...await n.defaultsFolder()||{},id:Ae,label:vh,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(Ae,ia).catch(()=>{}),{created:!0,folder:l}}function Dh(n){let e=ie.join(n,".stignore"),t="";try{t=Me.readFileSync(e,"utf8")}catch{}return t.includes("Written by Leftoff")?!1:(Me.writeFileSync(e,(t?t.replace(/\n?$/,`
63
- `):"")+ia.join(`
59
+ `});var gp={};Qt(gp,{BREW_URL:()=>sp,CODE_PREFIX:()=>Tr,DOWNLOAD_URL:()=>np,FOLDER_ID:()=>_e,FOLDER_LABEL:()=>ep,INSTALL_COMMANDS:()=>wu,INSTALL_LINE:()=>_r,PAIR_TTL_MS:()=>tp,PROOF_RE:()=>xu,PairCodeError:()=>yn,STIGNORE_LINES:()=>ya,SyncthingClient:()=>ga,SyncthingError:()=>et,acceptKnocked:()=>Rr,armPairing:()=>Mr,autoAcceptTick:()=>Lr,brewPath:()=>Sa,cancelPairing:()=>Tu,codeDigest:()=>vu,connect:()=>ft,decodePairCode:()=>bu,encodePairCode:()=>yu,ensureDevice:()=>ba,ensureFolder:()=>ka,explainBrewFailure:()=>up,findSyncthingConfig:()=>lp,guiUrl:()=>ap,installPlan:()=>As,knockedDevices:()=>Dr,localDir:()=>po,nameWithoutProof:()=>mo,nestedIn:()=>_s,pairProof:()=>va,parseSyncthingConfig:()=>ip,peerAccepted:()=>xa,progressLine:()=>Eu,proofName:()=>Au,publicPairing:()=>Gn,readManaged:()=>kn,readPairing:()=>Rt,readRunStatus:()=>ku,runInstall:()=>Or,sameDevice:()=>Oe,shortId:()=>Hn,startSyncthing:()=>Cr,statusFile:()=>Su,stopSyncing:()=>Ir,stuckSteps:()=>_u,suggestPath:()=>Nr,syncthingConfigCandidates:()=>rp,syncthingInstall:()=>Sn,syncthingStatus:()=>tn,tilde:()=>Ce,untilde:()=>wa,waitForSyncthing:()=>Ts,writeManaged:()=>Bn,writeRunStatus:()=>yo,writeStignore:()=>fp});import Re from"node:fs";import ES from"node:os";import le from"node:path";import OS from"node:http";import CS from"node:https";import go from"node:crypto";import{spawn as Qh,spawnSync as Xh}from"node:child_process";function po(n=process.env){return le.join(n.XDG_CONFIG_HOME||le.join(bt(n),".config"),"checkpoints")}function Er(n){try{return JSON.parse(Re.readFileSync(n,"utf8"))}catch{return null}}function xs(n,e){Re.mkdirSync(le.dirname(n),{recursive:!0,mode:448});let t=n+".tmp-"+process.pid;Re.writeFileSync(t,JSON.stringify(e,null,2)+`
60
+ `,{mode:384}),Re.renameSync(t,n);try{Re.chmodSync(n,384)}catch{}}function rp({env:n=process.env,platform:e=process.platform}={}){let t=bt(n),s=[];for(let i of[n.LEFTOFF_SYNCTHING_HOME,n.STHOMEDIR])i&&s.push(le.join(i,"config.xml"));let o=le.join(t,"Library","Application Support","Syncthing","config.xml"),r=[le.join(n.XDG_STATE_HOME||le.join(t,".local","state"),"syncthing","config.xml"),le.join(n.XDG_CONFIG_HOME||le.join(t,".config"),"syncthing","config.xml")];return e==="darwin"?s.push(o,...r):s.push(...r,o),[...new Set(s)]}function ip(n,{home:e=bt()}={}){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:ma(o),tls:i,apiKey:ma(r)||null,url:ap(ma(o),i)}}for(let o of String(n).matchAll(/<folder\b([^>]*?)(\/>|>([\s\S]*?)<\/folder>)/g)){let r=o[1],i=ho(r,"path");if(!i)continue;i=le.resolve(i.replace(/^~(?=\/|$)/,e));let a=[...String(o[3]||"").matchAll(/<device\b[^>]*\bid="([^"]+)"/g)].map(l=>l[1]);t.folders.push({id:ho(r,"id")||"",label:ho(r,"label")||"",path:i,type:ho(r,"type")||"sendreceive",devices:a})}for(let o of String(n).matchAll(/<device\b([^>]*\bname="[^"]*"[^>]*)>/g))t.devices.push({id:ho(o[1],"id"),name:ho(o[1],"name")});return t}function ap(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 lp(n={}){let e=n.env||process.env;for(let t of rp(n)){let s;try{s=Re.readFileSync(t,"utf8")}catch{continue}return{file:t,...ip(s,{home:bt(e)})}}return null}function ft(n={}){let e=n.env||process.env,t=lp({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:ga.fromConfig(t,{managedDevices:()=>kn(e).devices,...n.client})}}catch(s){return{ok:!1,cfg:t,code:s.code,reason:s.message}}}function Sa(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 Re.accessSync(e,Re.constants.X_OK),!0}catch{return!1}})||null}function Sn(n=process.env){let e=Sa(n);if(e){let s=le.join(le.dirname(e),"syncthing");if(NS(s))return{installed:!0,via:"brew",path:s,brew:e}}let t=cp("syncthing",n);if(t)return{installed:!0,via:e&&t.startsWith(le.dirname(le.dirname(e)))?"brew":"path",path:t,brew:e};for(let s of["/Applications/Syncthing.app",le.join(bt(n),"Applications","Syncthing.app")])if(IS(s))return{installed:!0,via:"app",path:s,brew:e};return{installed:!1,via:null,path:null,brew:e}}function As(n=process.env){return Sa(n)?{brew:!0,command:_r,steps:wu.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:sp,download:np}}}function up(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 Or({env:n=process.env,onLine:e=()=>{},onStage:t=()=>{}}={}){let s=Sa(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 wu){let a=["brew",...i].join(" ");t(a),e("$ "+a);let l=await new Promise(c=>{let u=Qh(s,i,{env:r,stdio:["ignore","pipe","pipe"]}),d="",f=p=>{d+=p;let h;for(;(h=d.search(/[\r\n]/))>=0;){let m=d.slice(0,h);d=d.slice(h+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",p=>{o.push(p.message),e(p.message),c(127)}),u.on("close",p=>{d.trim()&&(o.push(d),e(d)),c(p??1)})});if(l!==0)return{ok:!1,code:"brew_failed",exit:l,stage:a,error:up(o,l),lines:o}}return{ok:!0,lines:o}}function Cr(n=process.env){let e=Sn(n);if(!e.installed)return{ok:!1,code:"not_installed",error:"Syncthing isn’t installed"};if(e.via==="brew"){let o=Xh(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 Xh("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"};Re.mkdirSync(po(n),{recursive:!0});let t=Re.openSync(le.join(po(n),"syncthing.log"),"a"),s=Qh(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 Ts({env:n=process.env,timeoutMs:e=9e4,every:t=500}={}){let s=Date.now(),o=null;for(;Date.now()-s<e;){let r=ft({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 kn(n=process.env){let e=Er(dp(n))||{};return{devices:e.devices||[],originalName:e.originalName||null,usedCodes:e.usedCodes||[],folderPath:e.folderPath||null}}function Bn(n,e=process.env){xs(dp(e),{...kn(e),...n})}function MS(n,e){let t=kn(e);t.devices.some(s=>Oe(s,n))||Bn({devices:[...t.devices,n]},e)}function ku(n=process.env){return Er(Su(n))}function yo(n,e=process.env){xs(Su(e),{...n,at:new Date().toISOString()})}function _s(n,e,t=_e){let s=le.resolve(n);for(let o of e||[]){if(o.id===t)continue;let r=le.resolve(o.path);if(s===r||s.startsWith(r+le.sep))return{id:o.id,label:o.label||o.id,path:r,how:"inside"};if(r.startsWith(s+le.sep))return{id:o.id,label:o.label||o.id,path:r,how:"contains"}}return null}function Nr(n,e=process.env){let t=(n||[]).find(s=>s.id===_e);if(t)return t.path;for(let s of[le.join(bt(e),"Sync","Leftoff"),le.join(bt(e),"Leftoff"),le.join(bt(e),"Leftoff-Sync")])if(!_s(s,n))return s;return le.join(bt(e),"Leftoff-Sync")}async function ka(n,{path:e,peers:t=[],myID:s}){let o=await n.folders(),r=_s(e,o);if(r)throw new et(Ce(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===_e);if(i){if(le.resolve(i.path.replace(/^~(?=\/|$)/,bt()))!==le.resolve(e))throw new et("Syncthing already has a Leftoff folder at "+Ce(i.path)+". Use that one, or press Stop syncing first.",{code:"folder_elsewhere"});let c=new Set((i.devices||[]).map(d=>Dt(d.deviceID))),u=t.filter(d=>!c.has(Dt(d)));return u.length&&await n.patchFolder(_e,{devices:[...i.devices||[],...u.map(d=>({deviceID:d,introducedBy:"",encryptionPassword:""}))]}),await n.setIgnores(_e,ya).catch(()=>{}),{created:!1,folder:{...i,path:e}}}Re.mkdirSync(e,{recursive:!0}),fp(e);let l={...await n.defaultsFolder()||{},id:_e,label:ep,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(_e,ya).catch(()=>{}),{created:!0,folder:l}}function fp(n){let e=le.join(n,".stignore"),t="";try{t=Re.readFileSync(e,"utf8")}catch{}return t.includes("Written by Leftoff")?!1:(Re.writeFileSync(e,(t?t.replace(/\n?$/,`
63
+ `):"")+ya.join(`
64
64
  `)+`
65
- `),!0)}async function ua(n,{id:e,name:t,env:s=process.env}){let r=(await n.devices()).find(l=>_e(l.deviceID,e));if(r)return{created:!1,device:r};$0(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 vr(n){let e=await n.folder(Ae);return e?(await n.deleteFolder(Ae),{removed:!0,path:e.path}):{removed:!1,reason:"no Leftoff folder in Syncthing"}}function Rh(n){let e=0,t=0,s="";for(let o of n)for(t=t<<8|o,e+=8;e>=5;)s+=Xc[t>>>e-5&31],e-=5;return e>0&&(s+=Xc[t<<5-e&31]),s}function Y0(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=Xc.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 W0(n){let e=Ot(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|Ph.indexOf(r))&65535,t+=5,t>=8&&(o.push(s>>>t-8&255),t-=8);return Buffer.from(o)}function z0(n){let e=0,t=0,s="";for(let o of n)for(t=(t<<8|o)&65535,e+=8;e>=5;)s+=Ph[t>>>e-5&31],e-=5;return s.match(/.{7}/g).join("-")}function Qc({deviceId:n,folderId:e=Ae,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,W0(n),Buffer.from([i.length]),i,Buffer.from([r.length]),r]),c=io.createHash("sha256").update(l).digest().subarray(0,4);return gr+"-"+Rh(Buffer.concat([l,c])).match(/.{1,5}/g).join("-")}function su(n,{now:e=Date.now()}={}){let t=String(n||"").trim().replace(/^.*?\bjoin\s+/i,"").replace(/\s+/g,"");if(!new RegExp("^"+gr+"-?","i").test(t))throw new hn("That isn’t a Leftoff pairing code (it starts with "+gr+"-).","bad_code");t=t.replace(new RegExp("^"+gr+"-?","i"),"");let s;try{s=Y0(t)}catch{throw new hn("That code has a character that doesn’t belong; copy it again.","bad_code")}if(s.length<62)throw new hn("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(!io.createHash("sha256").update(o).digest().subarray(0,4).equals(r))throw new hn("That code has a typo or is cut short; copy it again.","bad_code");if(o[0]!==1)throw new hn("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=z0(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++],p=o.subarray(i,i+f).toString("utf8");if(e>a)throw new hn("That code expired. Make a new one on "+(p||"the other Mac")+" (Settings → Sync → Pair another Mac).","expired");return{deviceId:c,folderId:d,machine:p,secret:l,expiresAt:a}}function da(n,{joinerId:e,firstId:t,folderId:s=Ae}){let o=io.createHmac("sha256",n).update(`leftoff-pair-v1
66
- `+Ot(e)+`
67
- `+Ot(t)+`
68
- `+s).digest();return Rh(o).slice(0,16)}function It(n=process.env,e=Date.now()){let t=wr(pn(n));return t?{...t,live:!t.used&&e<t.expiresAt}:null}function Fn(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 xr({deviceId:n,machine:e,folderId:t=Ae,ttlMs:s=xh,env:o=process.env,now:r=Date.now(),reuse:i=!1}){let a=It(o,r);if(i&&a&&a.live&&_e(a.deviceId,n))return{...a,code:Qc({deviceId:n,folderId:a.folderId,machine:a.machine,secret:Buffer.from(a.secret,"base64"),expiresAt:a.expiresAt})};let l=io.randomBytes(16),c={deviceId:n,folderId:t,machine:e,secret:l.toString("base64"),createdAt:r,expiresAt:r+s,used:!1,paired:null,ignored:[]};return ws(pn(o),c),{...c,live:!0,code:Qc({deviceId:n,folderId:t,machine:e,secret:l,expiresAt:c.expiresAt})}}function au(n=process.env){try{Me.unlinkSync(pn(n))}catch{}}async function Ar({client:n,env:e=process.env,now:t=Date.now(),log:s=()=>{}}){let o=It(e,t);if(!o)return{state:"idle"};if(o.used)return{state:"paired",paired:o.paired};if(t>=o.expiresAt)return o.expiredLogged||(ws(pn(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(Ot(d)))continue;let p=String(f&&f.name||""),h=p.match(ru);if(!(h&&io.timingSafeEqual(Buffer.from(h[1]),Buffer.from(da(r,{joinerId:d,firstId:o.deviceId,folderId:o.folderId}))))){let S=ro(p).slice(0,80),k=c.find(v=>_e(v.deviceId,d));if(k?(k.name!==S||k.at!==(f&&f.time||k.at))&&(k.name=S,k.at=f&&f.time||k.at,k.seen=new Date(t).toISOString(),u=!0):(c.push({deviceId:d,name:S,at:f&&f.time||new Date(t).toISOString(),seen:new Date(t).toISOString()}),u=!0),a.has(Ot(d))||(a.add(Ot(d)),u=!0,s("syncthing: a device named “"+S+"” ("+Pn(d)+") asked to connect without this pairing code’s proof; not accepted")),!l.has(Ot(d))){l.add(Ot(d)),u=!0;try{await n.dismissPendingDevice(d),s("syncthing: cleared Syncthing’s pending record of "+Pn(d)+" once, so it can knock again with its current name")}catch{}}continue}let y=ro(p)||"other Mac",w={deviceId:d,name:y,at:new Date(t).toISOString(),how:"code"};return ws(pn(e),{...o,live:void 0,secret:null,used:!0,paired:w,ignored:[...a],dismissed:[...l],knocked:c.filter(S=>!_e(S.deviceId,d))}),await jh(n,{id:d,name:y,folderId:o.folderId,env:e}),s("syncthing: paired with "+y+" ("+Pn(d)+") by code; sharing the Leftoff folder with it"),{state:"accepted",paired:w}}return u&&ws(pn(e),{...o,live:void 0,ignored:[...a],dismissed:[...l],knocked:c}),{state:"waiting",knocked:c.length}}async function jh(n,{id:e,name:t,folderId:s=Ae,env:o}){await ua(n,{id:e,name:t,env:o});let r=await n.folder(s);r&&!(r.devices||[]).some(i=>_e(i.deviceID,e))&&await n.patchFolder(s,{devices:[...r.devices||[],{deviceID:e,introducedBy:"",encryptionPassword:""}]})}async function Tr(n,{env:e=process.env}={}){let t=wr(pn(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=>_e(c.deviceID,i.deviceId))||t.paired&&_e(t.paired.deviceId,i.deviceId))continue;let a=Object.entries(o||{}).find(([c])=>_e(c,i.deviceId));if(!a&&!(Date.parse(i.seen||i.at)>Date.now()-30*6e4))continue;let l=a&&ro(a[1]&&a[1].name).slice(0,80)||i.name;r.push({deviceId:i.deviceId,short:Pn(i.deviceId),name:l||Pn(i.deviceId),at:a&&a[1]&&a[1].time||i.at,pending:!!a})}return r}async function _r(n,{deviceId:e,env:t=process.env,now:s=Date.now(),log:o=()=>{}}){let i=(await Tr(n,{env:t})).find(c=>_e(c.deviceId,e)||e&&c.name===e);if(!i)throw new Ze("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=wr(pn(t))||{},l={deviceId:i.deviceId,name:i.name,at:new Date(s).toISOString(),how:"manual"};return ws(pn(t),{...a,live:void 0,secret:null,used:!0,paired:l,knocked:(a.knocked||[]).filter(c=>!_e(c.deviceId,i.deviceId))}),await jh(n,{id:i.deviceId,name:i.name,folderId:a.folderId||Ae,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 fa(n,{peerId:e,folderId:t=Ae}){let s=await n.connections().catch(()=>({})),o=Object.entries(s||{}).find(([l])=>_e(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 lu({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 J0(n,e){let t=n&&n[Ae];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 Vt({env:n=process.env,root:e=null,platform:t}={}){let s=mn(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=at({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!==Ae).length,o.suggestedPath=br(c,n),e&&(o.nested=bs(e,c));let d=c.find(f=>f.id===Ae);if(d){let[f,p,h,m,y]=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:Ee(d.path,n),isCurrent:!!e&&ie.resolve(d.path)===ie.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:[...(p||[]).map(w=>({path:w.path,error:w.error})),...f&&f.error?[{path:"",error:f.error}]:[]].slice(0,10),lastSync:J0(h,f)};for(let w of d.devices||[]){if(_e(w.deviceID,l.myID))continue;let S=u.find(g=>_e(g.deviceID,w.deviceID))||{},k=m[w.deviceID]||{},v=null,T=null;if(k.connected)try{let g=await i.completion(w.deviceID);v=g&&g.completion!=null?Math.max(0,Math.min(100,Math.round(g.completion))):null,T=g&&g.remoteState||null}catch{}let b=!T||T==="valid";o.peers.push({name:S.name||Pn(w.deviceID),short:Pn(w.deviceID),connected:!!k.connected&&b,notSharing:!!k.connected&&!b,lastSeen:(y[w.deviceID]||{}).lastSeen||null,completion:b?v:null})}o.knocked=await Tr(i,{env:n}).catch(()=>[])}}catch(a){o.reason=a.message}return o}function cu(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 Ae,vh,xh,eu,yr,Ah,Th,mt,K0,G0,_h,Ee,aa,oa,so,Ze,Ot,_e,Pn,ra,Nh,Lh,tu,ia,Xc,Ph,gr,hn,ou,ru,iu,ro,pn,lo=z(()=>{Ae="leftoff",vh="Leftoff",xh=900*1e3,eu=[["install","syncthing"],["services","start","syncthing"]],yr="brew install syncthing && brew services start syncthing",Ah="https://syncthing.net/downloads/",Th="https://brew.sh",mt=(n=process.env)=>n.HOME||U0.homedir(),K0=n=>{try{return Me.statSync(n),!0}catch{return!1}},G0=n=>{try{return Me.statSync(n).isDirectory()}catch{return!1}},_h=n=>new Promise(e=>setTimeout(e,n)),Ee=(n,e=process.env)=>{let t=mt(e);return n&&(n===t?"~":n.startsWith(t+ie.sep)?"~"+n.slice(t.length):n)},aa=(n,e=process.env)=>n==="~"?mt(e):String(n).startsWith("~/")?ie.join(mt(e),String(n).slice(2)):String(n);oa=n=>String(n).replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&apos;/g,"'").replace(/&amp;/g,"&"),so=(n,e)=>{let t=n.match(new RegExp("\\b"+e+'="([^"]*)"'));return t?oa(t[1]):null};Ze=class extends Error{constructor(e,{status:t=0,code:s="syncthing"}={}){super(e),this.status=t,this.code=s}},Ot=n=>String(n||"").toUpperCase().replace(/[^A-Z2-7]/g,""),_e=(n,e)=>!!n&&!!e&&Ot(n)===Ot(e),Pn=n=>String(n||"").split("-")[0]||"",ra=class n{constructor({url:e,apiKey:t,timeoutMs:s=6e3,folderId:o=Ae,managedDevices:r=()=>[],log:i=null}={}){if(!e)throw new Ze("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 Ze("Syncthing hasn’t written its settings yet",{code:"no_config"});if(!e.gui.apiKey)throw new Ze("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:"?B0:H0,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",p=>f.push(p)),d.on("end",()=>{let p=Buffer.concat(f).toString("utf8");if(d.statusCode>=400){let h=d.statusCode===401||d.statusCode===403?"Syncthing refused Leftoff’s request (its API key changed? Restart Syncthing and try again)":"Syncthing answered "+d.statusCode+(p?": "+p.trim().slice(0,160):"");return c(new Ze(h,{status:d.statusCode,code:d.statusCode===403||d.statusCode===401?"syncthing_forbidden":d.statusCode===404?"syncthing_not_found":"syncthing_error"}))}if(!p)return l(null);try{l(JSON.parse(p))}catch{l(p)}})});u.on("timeout",()=>u.destroy(new Ze("Syncthing didn’t answer in time",{code:"syncthing_timeout"}))),u.on("error",d=>c(d instanceof Ze?d:new Ze(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 Ze("Leftoff never changes another Syncthing folder ("+e+")",{code:"not_ours"})}assertOurDevice(e,{self:t=null}={}){if(!(t&&_e(e,t))&&!this.managedDevices().some(s=>_e(s,e)))throw new Ze("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&&_e(e,t)||this.assertOurDevice(e);let s="?device="+encodeURIComponent(e);return await this.req("POST","/rest/system/pause"+s),await _h(150),this.req("POST","/rest/system/resume"+s)}dismissPendingDevice(e){return this.req("DELETE","/rest/cluster/pending/devices?device="+encodeURIComponent(e))}};Nh=(n,e)=>{for(let t of String(e.PATH||"").split(ie.delimiter)){if(!t)continue;let s=ie.join(t,n);try{if(Me.accessSync(s,Me.constants.X_OK),Me.statSync(s).isFile())return s}catch{}}return null};Lh=n=>ie.join(oo(n),"syncthing.json");tu=(n=process.env)=>ie.join(oo(n),"syncthing-status.json");ia=["// 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*"];Xc="0123456789ABCDEFGHJKMNPQRSTVWXYZ";Ph="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";gr="LO1";hn=class extends Error{constructor(e,t){super(e),this.code=t}};ou=n=>io.createHash("sha256").update(String(n).toUpperCase().replace(/[^0-9A-Z]/g,"")).digest("hex").slice(0,24);ru=/\(leftoff-pair ([0-9A-Z]{16})\)\s*$/,iu=(n,e)=>String(n||"Mac").slice(0,40)+" (leftoff-pair "+e+")",ro=n=>String(n||"").replace(ru,"").trim(),pn=n=>ie.join(oo(n),"syncthing-pairing.json")});import Er from"node:fs";function Uh(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 co(n){let e=[],t={cwd:null,gitBranch:null,sessionId:null,customTitle:null,summary:null,lines:0,skipped:0},s=new Map;for(let o of String(n||"").split(`
70
- `)){if(!o.trim())continue;t.lines++;let r;try{r=JSON.parse(o)}catch{t.skipped++;continue}if(!r||typeof r!="object"){t.skipped++;continue}if(r.cwd&&(t.cwd=r.cwd),r.gitBranch&&(t.gitBranch=r.gitBranch),r.sessionId&&(t.sessionId=r.sessionId),r.type==="summary"&&r.summary){t.summary=r.summary;continue}if((r.type==="custom-title"||r.type==="custom_title")&&(r.customTitle||r.title)){t.customTitle=r.customTitle||r.title;continue}if(r.isSidechain||r.isMeta)continue;let i=r.message;if(!i||r.type!=="user"&&r.type!=="assistant"){r.type!=="system"&&t.skipped++;continue}let a=i.role||r.type,l=i.content;if(a==="user"){let c=[],u="";if(typeof l=="string")u=l;else if(Array.isArray(l)){for(let d of l)if(d)if(d.type==="tool_result"){let f={tool_use_id:d.tool_use_id,text:Uh(d.content),isError:!!d.is_error};c.push(f);let p=s.get(d.tool_use_id);p&&(p.isError=f.isError,p.text=f.text)}else d.type==="text"&&(u+=(u?`
71
- `:"")+(d.text||""))}if(/^\s*<(local-command-stdout|command-name|local-command-caveat)/.test(u)&&(u=""),!u&&!c.length)continue;e.push({role:"user",text:u,toolUses:[],toolResults:c})}else{let c=[],u="";for(let f of Array.isArray(l)?l:[{type:"text",text:Uh(l)}])if(f){if(f.type==="text"&&f.text)u+=(u?`
65
+ `),!0)}async function ba(n,{id:e,name:t,env:s=process.env}){let r=(await n.devices()).find(l=>Oe(l.deviceID,e));if(r)return{created:!1,device:r};MS(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 Ir(n){let e=await n.folder(_e);return e?(await n.deleteFolder(_e),{removed:!0,path:e.path}):{removed:!1,reason:"no Leftoff folder in Syncthing"}}function hp(n){let e=0,t=0,s="";for(let o of n)for(t=t<<8|o,e+=8;e>=5;)s+=gu[t>>>e-5&31],e-=5;return e>0&&(s+=gu[t<<5-e&31]),s}function LS(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=gu.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 DS(n){let e=Dt(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|pp.indexOf(r))&65535,t+=5,t>=8&&(o.push(s>>>t-8&255),t-=8);return Buffer.from(o)}function RS(n){let e=0,t=0,s="";for(let o of n)for(t=(t<<8|o)&65535,e+=8;e>=5;)s+=pp[t>>>e-5&31],e-=5;return s.match(/.{7}/g).join("-")}function yu({deviceId:n,folderId:e=_e,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,DS(n),Buffer.from([i.length]),i,Buffer.from([r.length]),r]),c=go.createHash("sha256").update(l).digest().subarray(0,4);return Tr+"-"+hp(Buffer.concat([l,c])).match(/.{1,5}/g).join("-")}function bu(n,{now:e=Date.now()}={}){let t=String(n||"").trim().replace(/^.*?\bjoin\s+/i,"").replace(/\s+/g,"");if(!new RegExp("^"+Tr+"-?","i").test(t))throw new yn("That isn’t a Leftoff pairing code (it starts with "+Tr+"-).","bad_code");t=t.replace(new RegExp("^"+Tr+"-?","i"),"");let s;try{s=LS(t)}catch{throw new yn("That code has a character that doesn’t belong; copy it again.","bad_code")}if(s.length<62)throw new yn("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(!go.createHash("sha256").update(o).digest().subarray(0,4).equals(r))throw new yn("That code has a typo or is cut short; copy it again.","bad_code");if(o[0]!==1)throw new yn("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=RS(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++],p=o.subarray(i,i+f).toString("utf8");if(e>a)throw new yn("That code expired. Make a new one on "+(p||"the other Mac")+" (Settings → Sync → Pair another Mac).","expired");return{deviceId:c,folderId:d,machine:p,secret:l,expiresAt:a}}function va(n,{joinerId:e,firstId:t,folderId:s=_e}){let o=go.createHmac("sha256",n).update(`leftoff-pair-v1
66
+ `+Dt(e)+`
67
+ `+Dt(t)+`
68
+ `+s).digest();return hp(o).slice(0,16)}function Rt(n=process.env,e=Date.now()){let t=Er(wn(n));return t?{...t,live:!t.used&&e<t.expiresAt}:null}function Gn(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 Mr({deviceId:n,machine:e,folderId:t=_e,ttlMs:s=tp,env:o=process.env,now:r=Date.now(),reuse:i=!1}){let a=Rt(o,r);if(i&&a&&a.live&&Oe(a.deviceId,n))return{...a,code:yu({deviceId:n,folderId:a.folderId,machine:a.machine,secret:Buffer.from(a.secret,"base64"),expiresAt:a.expiresAt})};let l=go.randomBytes(16),c={deviceId:n,folderId:t,machine:e,secret:l.toString("base64"),createdAt:r,expiresAt:r+s,used:!1,paired:null,ignored:[]};return xs(wn(o),c),{...c,live:!0,code:yu({deviceId:n,folderId:t,machine:e,secret:l,expiresAt:c.expiresAt})}}function Tu(n=process.env){try{Re.unlinkSync(wn(n))}catch{}}async function Lr({client:n,env:e=process.env,now:t=Date.now(),log:s=()=>{}}){let o=Rt(e,t);if(!o)return{state:"idle"};if(o.used)return{state:"paired",paired:o.paired};if(t>=o.expiresAt)return o.expiredLogged||(xs(wn(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(Dt(d)))continue;let p=String(f&&f.name||""),h=p.match(xu);if(!(h&&go.timingSafeEqual(Buffer.from(h[1]),Buffer.from(va(r,{joinerId:d,firstId:o.deviceId,folderId:o.folderId}))))){let b=mo(p).slice(0,80),w=c.find(A=>Oe(A.deviceId,d));if(w?(w.name!==b||w.at!==(f&&f.time||w.at))&&(w.name=b,w.at=f&&f.time||w.at,w.seen=new Date(t).toISOString(),u=!0):(c.push({deviceId:d,name:b,at:f&&f.time||new Date(t).toISOString(),seen:new Date(t).toISOString()}),u=!0),a.has(Dt(d))||(a.add(Dt(d)),u=!0,s("syncthing: a device named “"+b+"” ("+Hn(d)+") asked to connect without this pairing code’s proof; not accepted")),!l.has(Dt(d))){l.add(Dt(d)),u=!0;try{await n.dismissPendingDevice(d),s("syncthing: cleared Syncthing’s pending record of "+Hn(d)+" once, so it can knock again with its current name")}catch{}}continue}let y=mo(p)||"other Mac",S={deviceId:d,name:y,at:new Date(t).toISOString(),how:"code"};return xs(wn(e),{...o,live:void 0,secret:null,used:!0,paired:S,ignored:[...a],dismissed:[...l],knocked:c.filter(b=>!Oe(b.deviceId,d))}),await mp(n,{id:d,name:y,folderId:o.folderId,env:e}),s("syncthing: paired with "+y+" ("+Hn(d)+") by code; sharing the Leftoff folder with it"),{state:"accepted",paired:S}}return u&&xs(wn(e),{...o,live:void 0,ignored:[...a],dismissed:[...l],knocked:c}),{state:"waiting",knocked:c.length}}async function mp(n,{id:e,name:t,folderId:s=_e,env:o}){await ba(n,{id:e,name:t,env:o});let r=await n.folder(s);r&&!(r.devices||[]).some(i=>Oe(i.deviceID,e))&&await n.patchFolder(s,{devices:[...r.devices||[],{deviceID:e,introducedBy:"",encryptionPassword:""}]})}async function Dr(n,{env:e=process.env}={}){let t=Er(wn(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=>Oe(c.deviceID,i.deviceId))||t.paired&&Oe(t.paired.deviceId,i.deviceId))continue;let a=Object.entries(o||{}).find(([c])=>Oe(c,i.deviceId));if(!a&&!(Date.parse(i.seen||i.at)>Date.now()-30*6e4))continue;let l=a&&mo(a[1]&&a[1].name).slice(0,80)||i.name;r.push({deviceId:i.deviceId,short:Hn(i.deviceId),name:l||Hn(i.deviceId),at:a&&a[1]&&a[1].time||i.at,pending:!!a})}return r}async function Rr(n,{deviceId:e,env:t=process.env,now:s=Date.now(),log:o=()=>{}}){let i=(await Dr(n,{env:t})).find(c=>Oe(c.deviceId,e)||e&&c.name===e);if(!i)throw new et("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=Er(wn(t))||{},l={deviceId:i.deviceId,name:i.name,at:new Date(s).toISOString(),how:"manual"};return xs(wn(t),{...a,live:void 0,secret:null,used:!0,paired:l,knocked:(a.knocked||[]).filter(c=>!Oe(c.deviceId,i.deviceId))}),await mp(n,{id:i.deviceId,name:i.name,folderId:a.folderId||_e,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 xa(n,{peerId:e,folderId:t=_e}){let s=await n.connections().catch(()=>({})),o=Object.entries(s||{}).find(([l])=>Oe(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 _u({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 PS(n,e){let t=n&&n[_e];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=Sn(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=ft({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!==_e).length,o.suggestedPath=Nr(c,n),e&&(o.nested=_s(e,c));let d=c.find(f=>f.id===_e);if(d){let[f,p,h,m,y]=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:Ce(d.path,n),isCurrent:!!e&&le.resolve(d.path)===le.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:[...(p||[]).map(S=>({path:S.path,error:S.error})),...f&&f.error?[{path:"",error:f.error}]:[]].slice(0,10),lastSync:PS(h,f)};for(let S of d.devices||[]){if(Oe(S.deviceID,l.myID))continue;let b=u.find(g=>Oe(g.deviceID,S.deviceID))||{},w=m[S.deviceID]||{},A=null,v=null;if(w.connected)try{let g=await i.completion(S.deviceID);A=g&&g.completion!=null?Math.max(0,Math.min(100,Math.round(g.completion))):null,v=g&&g.remoteState||null}catch{}let k=!v||v==="valid";o.peers.push({name:b.name||Hn(S.deviceID),short:Hn(S.deviceID),connected:!!w.connected&&k,notSharing:!!w.connected&&!k,lastSeen:(y[S.deviceID]||{}).lastSeen||null,completion:k?A:null})}o.knocked=await Dr(i,{env:n}).catch(()=>[])}}catch(a){o.reason=a.message}return o}function Eu(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 _e,ep,tp,wu,_r,np,sp,bt,NS,IS,op,Ce,wa,ma,ho,et,Dt,Oe,Hn,ga,cp,dp,Su,ya,gu,pp,Tr,yn,vu,xu,Au,mo,wn,wo=J(()=>{_e="leftoff",ep="Leftoff",tp=900*1e3,wu=[["install","syncthing"],["services","start","syncthing"]],_r="brew install syncthing && brew services start syncthing",np="https://syncthing.net/downloads/",sp="https://brew.sh",bt=(n=process.env)=>n.HOME||ES.homedir(),NS=n=>{try{return Re.statSync(n),!0}catch{return!1}},IS=n=>{try{return Re.statSync(n).isDirectory()}catch{return!1}},op=n=>new Promise(e=>setTimeout(e,n)),Ce=(n,e=process.env)=>{let t=bt(e);return n&&(n===t?"~":n.startsWith(t+le.sep)?"~"+n.slice(t.length):n)},wa=(n,e=process.env)=>n==="~"?bt(e):String(n).startsWith("~/")?le.join(bt(e),String(n).slice(2)):String(n);ma=n=>String(n).replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&apos;/g,"'").replace(/&amp;/g,"&"),ho=(n,e)=>{let t=n.match(new RegExp("\\b"+e+'="([^"]*)"'));return t?ma(t[1]):null};et=class extends Error{constructor(e,{status:t=0,code:s="syncthing"}={}){super(e),this.status=t,this.code=s}},Dt=n=>String(n||"").toUpperCase().replace(/[^A-Z2-7]/g,""),Oe=(n,e)=>!!n&&!!e&&Dt(n)===Dt(e),Hn=n=>String(n||"").split("-")[0]||"",ga=class n{constructor({url:e,apiKey:t,timeoutMs:s=6e3,folderId:o=_e,managedDevices:r=()=>[],log:i=null}={}){if(!e)throw new et("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 et("Syncthing hasn’t written its settings yet",{code:"no_config"});if(!e.gui.apiKey)throw new et("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:"?CS:OS,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",p=>f.push(p)),d.on("end",()=>{let p=Buffer.concat(f).toString("utf8");if(d.statusCode>=400){let h=d.statusCode===401||d.statusCode===403?"Syncthing refused Leftoff’s request (its API key changed? Restart Syncthing and try again)":"Syncthing answered "+d.statusCode+(p?": "+p.trim().slice(0,160):"");return c(new et(h,{status:d.statusCode,code:d.statusCode===403||d.statusCode===401?"syncthing_forbidden":d.statusCode===404?"syncthing_not_found":"syncthing_error"}))}if(!p)return l(null);try{l(JSON.parse(p))}catch{l(p)}})});u.on("timeout",()=>u.destroy(new et("Syncthing didn’t answer in time",{code:"syncthing_timeout"}))),u.on("error",d=>c(d instanceof et?d:new et(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 et("Leftoff never changes another Syncthing folder ("+e+")",{code:"not_ours"})}assertOurDevice(e,{self:t=null}={}){if(!(t&&Oe(e,t))&&!this.managedDevices().some(s=>Oe(s,e)))throw new et("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&&Oe(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))}};cp=(n,e)=>{for(let t of String(e.PATH||"").split(le.delimiter)){if(!t)continue;let s=le.join(t,n);try{if(Re.accessSync(s,Re.constants.X_OK),Re.statSync(s).isFile())return s}catch{}}return null};dp=n=>le.join(po(n),"syncthing.json");Su=(n=process.env)=>le.join(po(n),"syncthing-status.json");ya=["// 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*"];gu="0123456789ABCDEFGHJKMNPQRSTVWXYZ";pp="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";Tr="LO1";yn=class extends Error{constructor(e,t){super(e),this.code=t}};vu=n=>go.createHash("sha256").update(String(n).toUpperCase().replace(/[^0-9A-Z]/g,"")).digest("hex").slice(0,24);xu=/\(leftoff-pair ([0-9A-Z]{16})\)\s*$/,Au=(n,e)=>String(n||"Mac").slice(0,40)+" (leftoff-pair "+e+")",mo=n=>String(n||"").replace(xu,"").trim(),wn=n=>le.join(po(n),"syncthing-pairing.json")});import Pr from"node:fs";function yp(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 So(n){let e=[],t={cwd:null,gitBranch:null,sessionId:null,customTitle:null,summary:null,model:null,lines:0,skipped:0},s=new Map;for(let o of String(n||"").split(`
70
+ `)){if(!o.trim())continue;t.lines++;let r;try{r=JSON.parse(o)}catch{t.skipped++;continue}if(!r||typeof r!="object"){t.skipped++;continue}if(r.cwd&&(t.cwd=r.cwd),r.gitBranch&&(t.gitBranch=r.gitBranch),r.sessionId&&(t.sessionId=r.sessionId),r.type==="summary"&&r.summary){t.summary=r.summary;continue}if((r.type==="custom-title"||r.type==="custom_title")&&(r.customTitle||r.title)){t.customTitle=r.customTitle||r.title;continue}if(r.isSidechain||r.isMeta)continue;let i=r.message;if(!i||r.type!=="user"&&r.type!=="assistant"){r.type!=="system"&&t.skipped++;continue}let a=i.role||r.type;a==="assistant"&&typeof i.model=="string"&&i.model&&i.model!=="<synthetic>"&&(t.model=i.model);let l=i.content;if(a==="user"){let c=[],u="";if(typeof l=="string")u=l;else if(Array.isArray(l)){for(let d of l)if(d)if(d.type==="tool_result"){let f={tool_use_id:d.tool_use_id,text:yp(d.content),isError:!!d.is_error};c.push(f);let p=s.get(d.tool_use_id);p&&(p.isError=f.isError,p.text=f.text)}else d.type==="text"&&(u+=(u?`
71
+ `:"")+(d.text||""))}if(/^\s*<(local-command-stdout|command-name|local-command-caveat)/.test(u)&&(u=""),!u&&!c.length)continue;e.push({role:"user",text:u,toolUses:[],toolResults:c})}else{let c=[],u="";for(let f of Array.isArray(l)?l:[{type:"text",text:yp(l)}])if(f){if(f.type==="text"&&f.text)u+=(u?`
72
72
  `:"")+f.text;else if(f.type==="tool_use"){let p={tool_use_id:f.id,tool:f.name,input:f.input||{},isError:!1,text:void 0};c.push(p),f.id&&s.set(f.id,p)}}if(!u&&!c.length)continue;let d=e[e.length-1];if(d&&d.role==="assistant"&&d._mid&&d._mid===i.id){u&&(d.text+=(d.text?`
73
- `:"")+u),d.toolUses.push(...c);continue}e.push({role:"assistant",text:u,toolUses:c,_mid:i.id||null})}}for(let o of e)delete o._mid;return{messages:e,meta:t}}function vs(n,{maxBytes:e=8*1024*1024}={}){try{let t=Er.statSync(n),s;if(t.size>e){let o=Er.openSync(n,"r"),r=Buffer.alloc(e);Er.readSync(o,r,0,e,t.size-e),Er.closeSync(o),s=r.toString("utf8"),s=s.slice(s.indexOf(`
74
- `)+1)}else s=Er.readFileSync(n,"utf8");return co(s)}catch{return{messages:[],meta:{missing:!0}}}}var Cr=z(()=>{});import Un from"node:fs";import q0 from"node:os";import uu from"node:path";import{execFile as V0}from"node:child_process";function Q0(n=process.env){return n.CLAUDE_CONFIG_DIR||uu.join(n.HOME||q0.homedir(),".claude")}function uo(n,{env:e=process.env,dirs:t}={}){if(!n||!X0.test(n))return null;let s=t||[uu.join(Q0(e),"projects")],o=null;for(let r of s){let i=[];try{i=Un.readdirSync(r,{withFileTypes:!0}).filter(a=>a.isDirectory()).map(a=>a.name)}catch{continue}for(let a of i){let l=uu.join(r,a,n+".jsonl");try{let c=Un.statSync(l);(!o||c.mtimeMs>o.mtimeMs)&&(o={file:l,mtimeMs:c.mtimeMs,size:c.size})}catch{}}}return o?o.file:null}function du(n,{maxBytes:e=Z0}={}){try{let t=Un.statSync(n).size,s;if(t>e){let a=Un.openSync(n,"r"),l=Buffer.alloc(256*1024),c=Un.readSync(a,l,0,l.length,0);Un.closeSync(a),s=l.subarray(0,c).toString("utf8");let{meta:u}=co(s);return{cwd:u.cwd||null,prompts:1,customTitle:u.customTitle||null,gitBranch:u.gitBranch||null,truncated:!0}}s=Un.readFileSync(n,"utf8");let{messages:o,meta:r}=co(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}}catch{return null}}function Bh(n,e,t=5e3){return new Promise(s=>{V0(n,e,{env:{...process.env,LC_ALL:"C"},timeout:t,maxBuffer:8*1024*1024},(o,r)=>s(o&&!r?"":String(r||"")))})}function eS(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 tS(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 Kh(n,e){let t=ha(e||"").toLowerCase(),s=ha(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)&&(ha(o[1])==="claude"||/@anthropic-ai\/claude-code\/cli\.m?js$/.test(o[1]||""))}function nS(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)&&Kh(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 Hh(n,{platform:e=process.platform,runner:t=Bh}={}){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,Un.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 oS(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=ha(t.get(o.pid)||String(o.args).split(/\s+/)[0]);for(let[a,l]of sS)if(a.test(i))return{...Rn(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 Gh(n,{cwd:e=null,claimedPids:t=new Set,env:s=process.env,platform:o=process.platform,runner:r=Bh,now:i=Date.now(),transcriptDirs:a}={}){let l={via:[]},c=uo(n,{env:s,dirs:a});if(c){l.transcriptPath=c;let h=du(c);h&&(h.cwd&&(l.cwd=h.cwd),l.prompts=h.prompts,h.customTitle&&(l.customTitle=h.customTitle),l.via.push("transcript"))}let u=e||l.cwd||null,d=eS(await r("ps",["-axo","pid=,ppid=,tty=,etime=,command="]));if(!d.length)return l;let f=tS(await r("ps",["-axo","pid=,comm="])),p=nS(n,d,f);if(p)l.via.push("ps-args");else if(u){let h=d.filter(S=>Kh(S,f.get(S.pid))&&!t.has(S.pid)),m=h.filter(S=>!h.some(k=>k.ppid===S.pid)),y=await Hh(m.map(S=>S.pid),{platform:o,runner:r}),w=m.filter(S=>y.get(S.pid)===u);w.length===1&&(p=w[0],l.via.push("ps-cwd"))}if(p){let h=hr(p.etime);l.proc={pid:p.pid,tty:$c(p.tty),pidStartedAt:h==null?null:G(i-h*1e3),source:"daemon"};let m=oS(p.pid,d,f);if(m&&(l.term=m,l.via.push("ps-term")),!l.cwd&&!e){let y=(await Hh([p.pid],{platform:o,runner:r})).get(p.pid);y&&(l.cwd=y)}}return l}var Z0,X0,ha,sS,fu=z(()=>{Cr();ve();Z0=32*1024*1024,X0=/^[0-9A-Za-z][0-9A-Za-z_-]{6,80}$/;ha=n=>String(n||"").split("/").pop();sS=[[/^ghostty$/i,"ghostty"],[/^iterm2?$/i,"iTerm.app"],[/^terminal$/i,"Apple_Terminal"],[/^wezterm(-gui)?$/i,"WezTerm"],[/^kitty$/i,"kitty"]]});import Bn from"node:fs";import Hn from"node:path";import rS from"node:crypto";function Nr(n){let e=iS[n]||n;if(!Ir.includes(e))throw Object.assign(new Error("kind is machine, repo, session or agent"),{code:"bad_kind"});return e}function Mr(n){return Yh.test(String(n))}function ma(n){return String(n).replace(Yh,"")}function Lr(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 Wh(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 xs(){return{schema:1,machines:{},repos:{},sessions:{},agents:{}}}function aS(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 zh(n){let e=xs();if(!n||typeof n!="object")return e;for(let t of Ir){let s=n[t];if(!(!s||typeof s!="object"))for(let[o,r]of Object.entries(s)){let i=aS(r);i&&(e[t][o]=i)}}return e}function lS(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 cS(...n){let e=xs();for(let t of n){let s=zh(t);for(let o of Ir)for(let[r,i]of Object.entries(s[o]))e[o][r]=lS(e[o][r],i)}return e}function As(n){let e=[],t=[];try{t=Bn.readdirSync(n)}catch{}for(let r of t)(r===Or||r.startsWith("spaces.")&&r.endsWith(".json")&&Mr(r)&&ma(r)===Or)&&e.push(r);let s=[],o=[];for(let r of e.sort())try{s.push(JSON.parse(Bn.readFileSync(Hn.join(n,r),"utf8"))),r!==Or&&o.push(r)}catch{}return{map:cS(...s),conflicts:o}}function ga(n,e){Bn.mkdirSync(Hn.dirname(n),{recursive:!0});let t=Hn.join(Hn.dirname(n),".syncthing."+Hn.basename(n)+"."+process.pid+"."+rS.randomBytes(4).toString("hex")+".tmp");Bn.writeFileSync(t,e),Bn.renameSync(t,n)}function uS(n,e,{removeConflicts:t=[]}={}){let s=zh(e),o={schema:1};for(let r of Ir)o[r]=Object.fromEntries(Object.entries(s[r]).sort(([i],[a])=>i.localeCompare(a)));ga(Hn.join(n,Or),JSON.stringify(o,null,2)+`
78
- `);for(let r of t)try{Bn.unlinkSync(Hn.join(n,r))}catch{}}function hu(n,e,{machine:t="",now:s=Date.now()}={}){let{map:o,conflicts:r}=As(n),i={at:new Date(s).toISOString(),by:t},a=e(o,i);return uS(n,o,{removeConflicts:r}),a===void 0?o:a}function Nt(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 Zt(n,e=[]){let t=new Set;for(let s of Ir)for(let o of Object.values(Nt(n,s)))t.add(o);for(let s of e)t.add(Kn(n,s));return ho([...t])}function ho(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 Kn(n,e){let t=n&&n.machines&&n.machines[e];return t&&t.space!=null?t.space:e||"Unsorted"}function fS(n,{repoKey:e,repoName:t,repoLabel:s}={}){let o=Nt(n,"repos");if(e&&o[e]!=null)return{key:e,space:o[e]};let r=[s,t,dS(e)].filter(Boolean).map($h);for(let i of r)for(let[a,l]of Object.entries(o))if($h(a)===i)return{key:a,space:l};return null}function Gn(n,e,{repoKey:t,repoName:s,repoLabel:o,machine:r}={}){let i=Nt(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=Nt(n,"agents");if(d[a]!=null)return{space:d[a],source:"agent",key:a}}let l=!a&&fS(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 Jh(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 Or,fo,Ir,iS,Yh,$h,dS,pa,Ts=z(()=>{Or="spaces.json",fo=["Work","Personal"],Ir=["machines","repos","sessions","agents"],iS={machine:"machines",repo:"repos",session:"sessions",agent:"agents"};Yh=/\.sync-conflict-(\d{8})-(\d{6})-([A-Za-z0-9]{1,16})(?=\.[^.]+$|$)/;$h=n=>String(n||"").toLowerCase(),dS=n=>{let e=String(n||"").split(/[\\/]+/).filter(Boolean);return e.length?e[e.length-1]:""};pa=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=Bn.readdirSync(this.root).filter(t=>t===Or||t.startsWith("spaces.")&&Mr(t))}catch{}return e.sort().map(t=>{try{let s=Bn.statSync(Hn.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}=As(this.root);return this.cache={sig:s,map:o,conflicts:r},o}update(e){let t=hu(this.root,e,{machine:this.machine,now:this.now()});return this.cache=null,t}resolve(e,t){return Gn(this.map(),e,t)}machineSpace(e=this.machine){return Kn(this.map(),e)}}});import oe from"node:fs";import ne from"node:path";import pu from"node:crypto";function $n(n,e){oe.mkdirSync(ne.dirname(n),{recursive:!0});let t=ne.join(ne.dirname(n),"."+ne.basename(n)+"."+process.pid+"."+pu.randomBytes(4).toString("hex")+".tmp");oe.writeFileSync(t,e),oe.renameSync(t,n)}function yn(n){try{return oe.readdirSync(n,{withFileTypes:!0}).filter(e=>e.isDirectory()&&!e.name.startsWith(".")).map(e=>e.name)}catch{return[]}}function qh(n){return oe.existsSync(ne.join(n,"session.md"))||oe.existsSync(ne.join(n,"checkpoints"))}function Vh(n,e,t,s){let o=ne.join(we(n).spool,e,String(t||"unknown").replace(/[^A-Za-z0-9_-]/g,"_"));oe.mkdirSync(o,{recursive:!0});let r=ne.join(o,Date.now()+"-"+pu.randomBytes(4).toString("hex")+".json");return oe.writeFileSync(r,JSON.stringify(s)+`
79
- `,{flag:"wx"}),r}var gt,wn=z(()=>{ve();ke();Ts();gt=class{constructor(e,t="unknown"){this.root=e,this.machine=t,this.p=we(e)}get mySessions(){return ne.join(this.p.sessions,this.machine)}get mySpool(){return ne.join(this.p.spool,this.machine)}get myDigests(){return ne.join(this.p.digests,this.machine)}sessionDir(e){return e.folder||(e.folder=this.findFolderFor(e.id)||this.machine+"/"+Yf(e)),ne.join(this.p.sessions,e.folder)}findFolderFor(e){let t="_"+ur(e);for(let s of yn(this.mySessions)){if(!s.endsWith(t))continue;let o=ne.join(this.mySessions,s,"session.md");if(!oe.existsSync(o))return this.machine+"/"+s;try{if(Be(oe.readFileSync(o,"utf8")).data.id===e)return this.machine+"/"+s}catch{}}return null}writeSession(e,t){let s=ne.join(this.sessionDir(e),"session.md");return $n(s,Xf(e,t)),s}writeCheckpoint(e,t){let s=ne.join(this.sessionDir(e),"checkpoints");oe.mkdirSync(s,{recursive:!0});for(let o=0;o<50;o++){let r=ne.join(s,Bc(t.createdAt,o)),i={...t,machine:t.machine||e.machine||this.machine,id:t.id&&o===0?t.id:Ji(t.sessionId,t.createdAt,o),file:ne.basename(r)};try{return oe.writeFileSync(r,Vf(i),{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 oe.readdirSync(ne.join(e,"checkpoints")).filter(a=>a.endsWith(".md")).sort())try{let a=Zf(oe.readFileSync(ne.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=Qf(oe.readFileSync(ne.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 yn(this.p.sessions)){let o=ne.join(this.p.sessions,s);if(qh(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 yn(o)){let i=this.loadSessionDir(ne.join(o,r),s+"/"+r,s);i&&t.push(i)}}return t}machines(){let e=new Set([this.machine]);for(let t of yn(this.p.sessions))qh(ne.join(this.p.sessions,t))||e.add(t);for(let t of yn(this.p.digests))e.add(t);return[...e].sort()}writeDigest(e,t){let s=ne.join(this.p.digests,e+".md");ga(s,t);for(let o of this.digestCopies(e))if(o!==e+".md")try{oe.unlinkSync(ne.join(this.p.digests,o))}catch{}return s}writeMachineDigest(e,t){let s=ne.join(this.myDigests,e+".md");return $n(s,t),s}digestCopies(e){let t=[];try{t=oe.readdirSync(this.p.digests)}catch{}return t.filter(s=>s===e+".md"||s.startsWith(e+".")&&s.endsWith(".md")&&Mr(s)&&ma(s)===e+".md")}readSharedDigest(e){let t=null,s=this.digestCopies(e);for(let o of s)try{let r=ne.join(this.p.digests,o),i=oe.readFileSync(r,"utf8"),a=Be(i).data||{},l=Date.parse(a.generated_at)||oe.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:oe.readFileSync(ne.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,...yn(this.p.digests).filter(r=>r!==this.machine)])try{return{machine:o,shared:!1,markdown:oe.readFileSync(ne.join(this.p.digests,o,e+".md"),"utf8")}}catch{}return null}listDigests(){let e=[],t=new Set;try{for(let o of oe.readdirSync(this.p.digests)){let r=/^(\d{4}-\d{2}-\d{2})\.(?:.*\.)?md$/.exec(o);r&&(o===r[1]+".md"||ma(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 yn(this.p.digests))try{for(let r of oe.readdirSync(ne.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=ne.join(this.p.digests,".machines",this.machine.replace(/[^\w.-]/g,"_")+".json");ga(s,JSON.stringify({machine:this.machine,seenAt:new Date(t).toISOString(),...e},null,2)+`
80
- `)}aliveMachines(){let e=[],t=ne.join(this.p.digests,".machines"),s=[];try{s=oe.readdirSync(t).filter(o=>o.endsWith(".json")&&!o.startsWith(".")&&!Mr(o))}catch{}for(let o of s)try{let r=JSON.parse(oe.readFileSync(ne.join(t,o),"utf8"));r&&r.machine&&e.push(r)}catch{}return e}writeCatchup(e,t){oe.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=ne.join(this.p.catchups,r+".md");try{let a=ne.join(this.p.catchups,"."+r+"."+process.pid+"."+pu.randomBytes(4).toString("hex")+".tmp");oe.writeFileSync(a,t);try{try{oe.linkSync(a,i)}catch(l){if(l.code==="EEXIST")throw l;oe.writeFileSync(i,t,{flag:"wx"})}}finally{try{oe.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:oe.readFileSync(ne.join(this.p.catchups,e+".md"),"utf8")}}catch{return null}}listCatchups(){let e=[];try{e=oe.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=oe.openSync(ne.join(this.p.catchups,o),"r");try{let a=Buffer.alloc(8192),l=oe.readSync(i,a,0,a.length,0);r=Be(a.slice(0,l).toString("utf8")).data||{}}finally{oe.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 yn(this.mySpool)){let s=ne.join(this.mySpool,t),o=[];try{o=oe.readdirSync(s)}catch{}for(let r of o){if(!r.endsWith(".json"))continue;let i=ne.join(s,r),a=null;try{a=oe.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 yn(this.mySpool))try{e+=oe.readdirSync(ne.join(this.mySpool,t)).filter(s=>s.endsWith(".json")).length}catch{}return e}}});import hS from"node:path";function Zh(n){let e=[],t=!1;for(let s of mu(n).split(`
73
+ `:"")+u),d.toolUses.push(...c);continue}e.push({role:"assistant",text:u,toolUses:c,_mid:i.id||null})}}for(let o of e)delete o._mid;return{messages:e,meta:t}}function Kn(n,{maxBytes:e=8*1024*1024}={}){try{let t=Pr.statSync(n),s;if(t.size>e){let o=Pr.openSync(n,"r"),r=Buffer.alloc(e);Pr.readSync(o,r,0,e,t.size-e),Pr.closeSync(o),s=r.toString("utf8"),s=s.slice(s.indexOf(`
74
+ `)+1)}else s=Pr.readFileSync(n,"utf8");return So(s)}catch{return{messages:[],meta:{missing:!0}}}}var jr=J(()=>{});import $n from"node:fs";import jS from"node:os";import Ou from"node:path";import{execFile as FS}from"node:child_process";function BS(n=process.env){return n.CLAUDE_CONFIG_DIR||Ou.join(n.HOME||jS.homedir(),".claude")}function ko(n,{env:e=process.env,dirs:t}={}){if(!n||!HS.test(n))return null;let s=t||[Ou.join(BS(e),"projects")],o=null;for(let r of s){let i=[];try{i=$n.readdirSync(r,{withFileTypes:!0}).filter(a=>a.isDirectory()).map(a=>a.name)}catch{continue}for(let a of i){let l=Ou.join(r,a,n+".jsonl");try{let c=$n.statSync(l);(!o||c.mtimeMs>o.mtimeMs)&&(o={file:l,mtimeMs:c.mtimeMs,size:c.size})}catch{}}}return o?o.file:null}function Cu(n,{maxBytes:e=US}={}){try{let t=$n.statSync(n).size,s;if(t>e){let a=$n.openSync(n,"r"),l=Buffer.alloc(256*1024),c=$n.readSync(a,l,0,l.length,0);$n.closeSync(a),s=l.subarray(0,c).toString("utf8");let{meta:u}=So(s);return{cwd:u.cwd||null,prompts:1,customTitle:u.customTitle||null,gitBranch:u.gitBranch||null,truncated:!0}}s=$n.readFileSync(n,"utf8");let{messages:o,meta:r}=So(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 Sp(n,e,t=5e3){return new Promise(s=>{FS(n,e,{env:{...process.env,LC_ALL:"C"},timeout:t,maxBuffer:8*1024*1024},(o,r)=>s(o&&!r?"":String(r||"")))})}function GS(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 KS(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 kp(n,e){let t=Aa(e||"").toLowerCase(),s=Aa(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)&&(Aa(o[1])==="claude"||/@anthropic-ai\/claude-code\/cli\.m?js$/.test(o[1]||""))}function $S(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)&&kp(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 wp(n,{platform:e=process.platform,runner:t=Sp}={}){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,$n.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 WS(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=Aa(t.get(o.pid)||String(o.args).split(/\s+/)[0]);for(let[a,l]of YS)if(a.test(i))return{...Un(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=Sp,now:i=Date.now(),transcriptDirs:a}={}){let l={via:[]},c=ko(n,{env:s,dirs:a});if(c){l.transcriptPath=c;let h=Cu(c);h&&(h.cwd&&(l.cwd=h.cwd),l.prompts=h.prompts,h.customTitle&&(l.customTitle=h.customTitle),l.via.push("transcript"))}let u=e||l.cwd||null,d=GS(await r("ps",["-axo","pid=,ppid=,tty=,etime=,command="]));if(!d.length)return l;let f=KS(await r("ps",["-axo","pid=,comm="])),p=$S(n,d,f);if(p)l.via.push("ps-args");else if(u){let h=d.filter(b=>kp(b,f.get(b.pid))&&!t.has(b.pid)),m=h.filter(b=>!h.some(w=>w.ppid===b.pid)),y=await wp(m.map(b=>b.pid),{platform:o,runner:r}),S=m.filter(b=>y.get(b.pid)===u);S.length===1&&(p=S[0],l.via.push("ps-cwd"))}if(p){let h=vr(p.etime);l.proc={pid:p.pid,tty:lu(p.tty),pidStartedAt:h==null?null:K(i-h*1e3),source:"daemon"};let m=WS(p.pid,d,f);if(m&&(l.term=m,l.via.push("ps-term")),!l.cwd&&!e){let y=(await wp([p.pid],{platform:o,runner:r})).get(p.pid);y&&(l.cwd=y)}}return l}var US,HS,Aa,YS,Nu=J(()=>{jr();Ae();US=32*1024*1024,HS=/^[0-9A-Za-z][0-9A-Za-z_-]{6,80}$/;Aa=n=>String(n||"").split("/").pop();YS=[[/^ghostty$/i,"ghostty"],[/^iterm2?$/i,"iTerm.app"],[/^terminal$/i,"Apple_Terminal"],[/^wezterm(-gui)?$/i,"WezTerm"],[/^kitty$/i,"kitty"]]});import Wn from"node:fs";import Yn from"node:path";import zS from"node:crypto";function Hr(n){let e=JS[n]||n;if(!Ur.includes(e))throw Object.assign(new Error("kind is machine, repo, session or agent"),{code:"bad_kind"});return e}function Br(n){return xp.test(String(n))}function _a(n){return String(n).replace(xp,"")}function Gr(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 Ap(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 Es(){return{schema:1,machines:{},repos:{},sessions:{},agents:{}}}function qS(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 Tp(n){let e=Es();if(!n||typeof n!="object")return e;for(let t of Ur){let s=n[t];if(!(!s||typeof s!="object"))for(let[o,r]of Object.entries(s)){let i=qS(r);i&&(e[t][o]=i)}}return e}function VS(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 ZS(...n){let e=Es();for(let t of n){let s=Tp(t);for(let o of Ur)for(let[r,i]of Object.entries(s[o]))e[o][r]=VS(e[o][r],i)}return e}function Os(n){let e=[],t=[];try{t=Wn.readdirSync(n)}catch{}for(let r of t)(r===Fr||r.startsWith("spaces.")&&r.endsWith(".json")&&Br(r)&&_a(r)===Fr)&&e.push(r);let s=[],o=[];for(let r of e.sort())try{s.push(JSON.parse(Wn.readFileSync(Yn.join(n,r),"utf8"))),r!==Fr&&o.push(r)}catch{}return{map:ZS(...s),conflicts:o}}function Ea(n,e){Wn.mkdirSync(Yn.dirname(n),{recursive:!0});let t=Yn.join(Yn.dirname(n),".syncthing."+Yn.basename(n)+"."+process.pid+"."+zS.randomBytes(4).toString("hex")+".tmp");Wn.writeFileSync(t,e),Wn.renameSync(t,n)}function XS(n,e,{removeConflicts:t=[]}={}){let s=Tp(e),o={schema:1};for(let r of Ur)o[r]=Object.fromEntries(Object.entries(s[r]).sort(([i],[a])=>i.localeCompare(a)));Ea(Yn.join(n,Fr),JSON.stringify(o,null,2)+`
78
+ `);for(let r of t)try{Wn.unlinkSync(Yn.join(n,r))}catch{}}function Iu(n,e,{machine:t="",now:s=Date.now()}={}){let{map:o,conflicts:r}=Os(n),i={at:new Date(s).toISOString(),by:t},a=e(o,i);return XS(n,o,{removeConflicts:r}),a===void 0?o:a}function Pt(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 nn(n,e=[]){let t=new Set;for(let s of Ur)for(let o of Object.values(Pt(n,s)))t.add(o);for(let s of e)t.add(zn(n,s));return vo([...t])}function vo(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 zn(n,e){let t=n&&n.machines&&n.machines[e];return t&&t.space!=null?t.space:e||"Unsorted"}function ek(n,{repoKey:e,repoName:t,repoLabel:s}={}){let o=Pt(n,"repos");if(e&&o[e]!=null)return{key:e,space:o[e]};let r=[s,t,QS(e)].filter(Boolean).map(vp);for(let i of r)for(let[a,l]of Object.entries(o))if(vp(a)===i)return{key:a,space:l};return null}function Jn(n,e,{repoKey:t,repoName:s,repoLabel:o,machine:r}={}){let i=Pt(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=Pt(n,"agents");if(d[a]!=null)return{space:d[a],source:"agent",key:a}}let l=!a&&ek(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 _p(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 Fr,bo,Ur,JS,xp,vp,QS,Ta,Cs=J(()=>{Fr="spaces.json",bo=["Work","Personal"],Ur=["machines","repos","sessions","agents"],JS={machine:"machines",repo:"repos",session:"sessions",agent:"agents"};xp=/\.sync-conflict-(\d{8})-(\d{6})-([A-Za-z0-9]{1,16})(?=\.[^.]+$|$)/;vp=n=>String(n||"").toLowerCase(),QS=n=>{let e=String(n||"").split(/[\\/]+/).filter(Boolean);return e.length?e[e.length-1]:""};Ta=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===Fr||t.startsWith("spaces.")&&Br(t))}catch{}return e.sort().map(t=>{try{let s=Wn.statSync(Yn.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}=Os(this.root);return this.cache={sig:s,map:o,conflicts:r},o}update(e){let t=Iu(this.root,e,{machine:this.machine,now:this.now()});return this.cache=null,t}resolve(e,t){return Jn(this.map(),e,t)}machineSpace(e=this.machine){return zn(this.map(),e)}}});import ie from"node:fs";import oe from"node:path";import Mu from"node:crypto";function qn(n,e){ie.mkdirSync(oe.dirname(n),{recursive:!0});let t=oe.join(oe.dirname(n),"."+oe.basename(n)+"."+process.pid+"."+Mu.randomBytes(4).toString("hex")+".tmp");ie.writeFileSync(t,e),ie.renameSync(t,n)}function bn(n){try{return ie.readdirSync(n,{withFileTypes:!0}).filter(e=>e.isDirectory()&&!e.name.startsWith(".")).map(e=>e.name)}catch{return[]}}function Ep(n){return ie.existsSync(oe.join(n,"session.md"))||ie.existsSync(oe.join(n,"checkpoints"))}function Op(n,e,t,s){let o=oe.join(ke(n).spool,e,String(t||"unknown").replace(/[^A-Za-z0-9_-]/g,"_"));ie.mkdirSync(o,{recursive:!0});let r=oe.join(o,Date.now()+"-"+Mu.randomBytes(4).toString("hex")+".json");return ie.writeFileSync(r,JSON.stringify(s)+`
79
+ `,{flag:"wx"}),r}var vt,vn=J(()=>{Ae();ve();Cs();vt=class{constructor(e,t="unknown"){this.root=e,this.machine=t,this.p=ke(e)}get mySessions(){return oe.join(this.p.sessions,this.machine)}get mySpool(){return oe.join(this.p.spool,this.machine)}get myDigests(){return oe.join(this.p.digests,this.machine)}sessionDir(e){return e.folder||(e.folder=this.findFolderFor(e.id)||this.machine+"/"+gh(e)),oe.join(this.p.sessions,e.folder)}findFolderFor(e){let t="_"+Sr(e);for(let s of bn(this.mySessions)){if(!s.endsWith(t))continue;let o=oe.join(this.mySessions,s,"session.md");if(!ie.existsSync(o))return this.machine+"/"+s;try{if($e(ie.readFileSync(o,"utf8")).data.id===e)return this.machine+"/"+s}catch{}}return null}writeSession(e,t){let s=oe.join(this.sessionDir(e),"session.md");return qn(s,Nh(e,t)),s}writeCheckpoint(e,t){let s=oe.join(this.sessionDir(e),"checkpoints");ie.mkdirSync(s,{recursive:!0});for(let o=0;o<50;o++){let r=oe.join(s,ou(t.createdAt,o)),i={...t,machine:t.machine||e.machine||this.machine,id:t.id&&o===0?t.id:oa(t.sessionId,t.createdAt,o),file:oe.basename(r)};try{return ie.writeFileSync(r,Oh({...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 ie.readdirSync(oe.join(e,"checkpoints")).filter(a=>a.endsWith(".md")).sort())try{let a=Ch(ie.readFileSync(oe.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=Ih(ie.readFileSync(oe.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 bn(this.p.sessions)){let o=oe.join(this.p.sessions,s);if(Ep(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 bn(o)){let i=this.loadSessionDir(oe.join(o,r),s+"/"+r,s);i&&t.push(i)}}return t}machines(){let e=new Set([this.machine]);for(let t of bn(this.p.sessions))Ep(oe.join(this.p.sessions,t))||e.add(t);for(let t of bn(this.p.digests))e.add(t);return[...e].sort()}writeDigest(e,t){let s=oe.join(this.p.digests,e+".md");Ea(s,t);for(let o of this.digestCopies(e))if(o!==e+".md")try{ie.unlinkSync(oe.join(this.p.digests,o))}catch{}return s}writeMachineDigest(e,t){let s=oe.join(this.myDigests,e+".md");return qn(s,t),s}digestCopies(e){let t=[];try{t=ie.readdirSync(this.p.digests)}catch{}return t.filter(s=>s===e+".md"||s.startsWith(e+".")&&s.endsWith(".md")&&Br(s)&&_a(s)===e+".md")}readSharedDigest(e){let t=null,s=this.digestCopies(e);for(let o of s)try{let r=oe.join(this.p.digests,o),i=ie.readFileSync(r,"utf8"),a=$e(i).data||{},l=Date.parse(a.generated_at)||ie.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:ie.readFileSync(oe.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,...bn(this.p.digests).filter(r=>r!==this.machine)])try{return{machine:o,shared:!1,markdown:ie.readFileSync(oe.join(this.p.digests,o,e+".md"),"utf8")}}catch{}return null}listDigests(){let e=[],t=new Set;try{for(let o of ie.readdirSync(this.p.digests)){let r=/^(\d{4}-\d{2}-\d{2})\.(?:.*\.)?md$/.exec(o);r&&(o===r[1]+".md"||_a(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 bn(this.p.digests))try{for(let r of ie.readdirSync(oe.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=oe.join(this.p.digests,".machines",this.machine.replace(/[^\w.-]/g,"_")+".json");Ea(s,JSON.stringify({machine:this.machine,seenAt:new Date(t).toISOString(),...e},null,2)+`
80
+ `)}aliveMachines(){let e=[],t=oe.join(this.p.digests,".machines"),s=[];try{s=ie.readdirSync(t).filter(o=>o.endsWith(".json")&&!o.startsWith(".")&&!Br(o))}catch{}for(let o of s)try{let r=JSON.parse(ie.readFileSync(oe.join(t,o),"utf8"));r&&r.machine&&e.push(r)}catch{}return e}writeCatchup(e,t){ie.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=oe.join(this.p.catchups,r+".md");try{let a=oe.join(this.p.catchups,"."+r+"."+process.pid+"."+Mu.randomBytes(4).toString("hex")+".tmp");ie.writeFileSync(a,t);try{try{ie.linkSync(a,i)}catch(l){if(l.code==="EEXIST")throw l;ie.writeFileSync(i,t,{flag:"wx"})}}finally{try{ie.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:ie.readFileSync(oe.join(this.p.catchups,e+".md"),"utf8")}}catch{return null}}listCatchups(){let e=[];try{e=ie.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=ie.openSync(oe.join(this.p.catchups,o),"r");try{let a=Buffer.alloc(8192),l=ie.readSync(i,a,0,a.length,0);r=$e(a.slice(0,l).toString("utf8")).data||{}}finally{ie.closeSync(i)}}catch{}t.push({name:o.slice(0,-3),generatedAt:r.generated_at?K(r.generated_at):null,from:r.window_from?K(r.window_from):null,to:r.window_to?K(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 bn(this.mySpool)){let s=oe.join(this.mySpool,t),o=[];try{o=ie.readdirSync(s)}catch{}for(let r of o){if(!r.endsWith(".json"))continue;let i=oe.join(s,r),a=null;try{a=ie.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 bn(this.mySpool))try{e+=ie.readdirSync(oe.join(this.mySpool,t)).filter(s=>s.endsWith(".json")).length}catch{}return e}}});import tk from"node:path";function Cp(n){let e=[],t=!1;for(let s of Lu(n).split(`
81
81
  `)){if(/^\s*(```|~~~)/.test(s)){t=!t;continue}t||e.push(s)}return e.join(`
82
- `)}function pS(n,e){let t=mu(n).trimEnd(),s=_s(e).replace(/[*_`]+/g,"");if(!s)return t;let o=s.split(`
82
+ `)}function nk(n,e){let t=Lu(n).trimEnd(),s=Ns(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 Xh(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(mS(l))break;if(!l||l.role!=="assistant"||!l.text||!l.text.trim())continue;let c=l.text;i&&(r=!e||gu.test(e)?yu(c):[],e&&(c=pS(c,e)),i=!1);let u=Zh(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(Xt(u[d].replace(/^#{1,6}\s+/,""),s))}return!o.length&&!r.length?null:{lines:o,options:r}}function yu(n){let e=Zh(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=>Xt(a.key+". "+a.text.replace(/\*\*/g,"").replace(/\s+[—–-]\s+.*$/,"").replace(/[:.]$/,"").trim(),60)):[]}function wS(n={},{cwd:e}={}){let t=n.input||{},s=String(n.tool||"tool"),o=gS(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=_s(t.command);return yS.test(r)?"Ran tests":/^git\s+commit\b/.test(r)?"Committed":/^git\s+push\b/.test(r)?"Pushed":"Ran "+Xt(t.description?_s(t.description):r||"a command",48)}case"Grep":return"Searched “"+Xt(_s(t.pattern),32)+"”";case"Glob":return"Listed "+Xt(_s(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 “"+Xt(_s(t.query),32)+"”";case"TodoWrite":return"Updated the to-do list";case"Task":case"Agent":return"Ran an agent"+(t.description?": "+Xt(_s(t.description),36):"");case"AskUserQuestion":return"Asked you";case"ExitPlanMode":return"Proposed a plan";default:return"Used "+Xt(s.replace(/^mcp__/,"").replace(/__/g," "),40)}}function Qh(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:Xt(r.text.trim(),s),chips:[]});continue}let i=(r.text||"").trim(),a=(r.toolUses||[]).map(c=>({label:wS(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=Xt(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 Xt,mu,_s,gu,mS,gS,yS,ep=z(()=>{Xt=(n,e)=>(n=String(n??""),n.length>e?n.slice(0,e-1).trimEnd()+"…":n),mu=n=>String(n??"").replace(/\r\n?/g,`
90
- `),_s=n=>mu(n).replace(/\s+/g," ").trim();gu=/\b(which|or|options?|prefer|pick|choose|one of)\b/i,mS=n=>n&&n.role==="user"&&n.text&&n.text.trim();gS=(n,e)=>{let t=String(n||"");return t?e&&t.startsWith(String(e).replace(/\/$/,"")+"/")?t.slice(String(e).replace(/\/$/,"").length+1):t.includes("/")?hS.basename(t):t:""},yS=/(^|[\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 SS,execFileSync as kS}from"node:child_process";import bS from"node:fs";import wu from"node:path";function TS(){if(process.env.CHECKPOINTS_NO_CLAUDE)return!1;try{return kS(process.platform==="win32"?"where":"which",["claude"],{stdio:"ignore"}),!0}catch{return!1}}function _S(n){let e=n.summarizer||{},t=String(e.provider||e.mode||"auto").toLowerCase();return np[t]?np[t]:TS()?"claude":process.env.ANTHROPIC_API_KEY?"anthropic":"heuristic"}function ES({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=SS("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 Su(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 CS(n={},e=process.env){return Sn("anthropic",n,e)}function Sn(n,{apiKeyEnv:e,apiKeyFile:t,root:s}={},o=process.env){let r=wa[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=wu.isAbsolute(a)?null:wh(a,o.HOME||o.XDG_CONFIG_HOME?o:process.env);for(let c of[l,wu.isAbsolute(a)?a:wu.join(s||V(),a)])if(c)try{let u=bS.readFileSync(c,"utf8").trim();if(u)return u}catch{}}return null}async function OS(n,{system:e,prompt:t,model:s,maxTokens:o,apiKeyEnv:r,apiKeyFile:i,root:a}){let l=CS({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 Su(n,c+"/v1/messages",{"x-api-key":l,"anthropic-version":"2023-06-01"},{model:tp[s]||s||tp.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 sp(n,{system:e,prompt:t,model:s,maxTokens:o,baseUrl:r,key:i,cloud:a}){let l=await Su(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 IS(n,{system:e,prompt:t,model:s,maxTokens:o,baseUrl:r,apiKeyEnv:i}){let a=i?process.env[i]:null,l=await Su(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 tp,vS,xS,AS,ya,wa,np,yt,Es=z(()=>{ke();ve();tp={haiku:"claude-haiku-5-5",sonnet:"claude-sonnet-5-5",opus:"claude-opus-5-5"},vS=["haiku","sonnet","opus",""],xS="https://ollama.com",AS="gpt-oss:20b",ya={ollama:{baseUrl:"http://localhost:11434",model:"llama3.2"},"ollama-cloud":{baseUrl:xS,model:AS},"openai-compatible":{baseUrl:"http://localhost:1234/v1",model:"local-model"}},wa={anthropic:{env:"ANTHROPIC_API_KEY",file:".secret/anthropic-api-key"},"ollama-cloud":{env:"OLLAMA_API_KEY",file:".secret/ollama-api-key"}},np={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"};yt=class{constructor(e,{fetchImpl:t,root:s}={}){this.cfg=e,this.root=s||null,this.fetchImpl=t||((...o)=>fetch(...o)),this.mode=_S(e)}get model(){let e=String(this.cfg.summarizer&&this.cfg.summarizer.model||"");return ya[this.mode]&&vS.includes(e)?ya[this.mode].model:e||"haiku"}get baseUrl(){return this.cfg.summarizer&&this.cfg.summarizer.baseUrl||ya[this.mode]&&ya[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 ES(r);if(this.mode==="anthropic")return OS(this.fetchImpl,r);if(this.mode==="ollama")return sp(this.fetchImpl,r);if(this.mode==="ollama-cloud"){let i=Sn("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 sp(this.fetchImpl,{...r,key:i,cloud:!0})}if(this.mode==="openai-compatible")return IS(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=ih(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{...qc(s,o,{length:i}),generator:{by:"heuristic",fallbackFrom:this.mode,error:String(a.message||a).slice(0,160)}}}return{...qc(s,o,{length:i}),generator:{by:"heuristic"}}}async title(e){if(this.mode!=="heuristic"&&e.firstPrompt)try{let t=await this.complete({system:lh,prompt:`First prompt:
92
- `+e.firstPrompt,maxTokens:30}),s=ch(t.text);if(s)return{title:s,titleSource:"generated"}}catch{}return{title:ah(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 po from"node:fs";import NS from"node:os";import Sa from"node:path";import{execFileSync as RA,spawn as MS,spawnSync as LS}from"node:child_process";import{fileURLToPath as DS}from"node:url";async function Pr({baseUrl:n=process.env.CHECKPOINTS_CLOUD_URL||Cs,fetchImpl:e=fetch,timeoutMs:t=5e3}={}){let s=n.replace(/\/$/,"");try{let o=await ba(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 va(n=[],{curated:e=Rr,current:t=null}={}){let s=r=>n.some(i=>Ge(i,r)),o=e.map(r=>({...r,curated:!0,available:n.length?s(r.tag):null,current:!!t&&Ge(t,r.tag)}));for(let r of n)o.some(i=>Ge(i.tag,r))||o.push({tag:r,name:r,curated:!1,available:!0,recommended:!1,current:!!t&&Ge(t,r)});return o}function ka(n){let e=String(n||"").trim().toLowerCase();return e?e.includes(":")?e:e+":latest":""}function Ge(n,e){return ka(n)===ka(e)}function go(n=[],{curated:e=Dr,current:t=null}={}){let s=n.map(i=>typeof i=="string"?{name:i}:i),o=i=>s.some(a=>Ge(a.name||a.model,i)),r=e.map(i=>({...i,curated:!0,installed:o(i.tag),current:!!t&&Ge(t,i.tag)}));for(let i of s){let a=i.name||i.model;!a||r.some(l=>Ge(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&&Ge(t,a)})}return r}function rp(n,{totalMemBytes:e=NS.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 xa(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 yo(n,e=process.env){let t=String(e.PATH||"").split(Sa.delimiter).filter(Boolean),s=process.platform==="win32"?[".exe",".cmd",".bat",""]:[""];for(let o of t)for(let r of s){let i=Sa.join(o,n+r);try{if(po.statSync(i).isFile())return po.accessSync(i,po.constants.X_OK),i}catch{}}return null}function jr(n=process.env){let e=yo("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(po.existsSync(t))return t;return null}async function ba(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 jt({baseUrl:n=lt,fetchImpl:e=fetch,env:t=process.env}={}){let s=n.replace(/\/$/,""),o={kind:"ollama",running:!1,baseUrl:s,version:null,models:[],binary:jr(t),error:null};try{let r=await ba(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 ba(e,s+"/api/version")).version||null}catch{}}catch(r){o.error=r.cause&&r.cause.code||r.name||r.message}return o}async function kn({baseUrl:n=Qt,fetchImpl:e=fetch}={}){let t=n.replace(/\/$/,""),s={kind:"openai-compatible",running:!1,baseUrl:t,models:[],error:null};try{let o=await ba(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 wo({baseUrl:n=lt,fetchImpl:e=fetch,timeoutMs:t=2e4}={}){let s=Date.now()+t;for(;Date.now()<s;){let o=await jt({baseUrl:n,fetchImpl:e});if(o.running)return o;await PS(500)}return jt({baseUrl:n,fetchImpl:e})}function jS(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 So({tag:n,baseUrl:e=lt,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=jS(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 Os(n,{fetchImpl:e,transcriptFile:t=RS,root:s}={}){let o=Xs(ye,{summarizer:n}),{messages:r,meta:i}=co(po.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=Zi({session:a,messages:r,cfg:o}),c=new yt(o,{...e?{fetchImpl:e}:{},...s?{root:s}:{}}),u=Date.now(),d=await c.checkpoint({...l,messages:r,session:a}),f=Date.now()-u,p=d.generator&&d.generator.by==="heuristic"&&d.generator.fallbackFrom;return{ok:!p,doing:d.doing,stoppedAt:d.stoppedAt,next:d.next,parseOk:d.parseOk!==!1,ms:f,generator:d.generator,error:p?d.generator.error:null}}function ip(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 yo("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 Fr(n=process.platform,e=jr()){return n==="darwin"&&po.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 Aa({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?Fr(n,e):null}function ap(n,{spawnImpl:e=MS,spawnSyncImpl:t=LS}={}){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 lt,Qt,RS,mo,Dr,Cs,op,Rr,PS,Is=z(()=>{Cr();ve();Es();lt="http://localhost:11434",Qt="http://localhost:1234/v1",RS=Sa.resolve(Sa.dirname(DS(import.meta.url)),"..","assets","sample-transcript.jsonl"),mo="2026-10-09",Dr=[{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"'}],Cs="https://ollama.com",op="2026-10-09",Rr=[{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"}];PS=n=>new Promise(e=>setTimeout(e,n))});import lp from"node:fs";import{execFile as FS}from"node:child_process";function up(n,e){let t=yo(n,e);if(t)return t;if(e.CHECKPOINTS_TEST_NO_SYSTEM_BINS)return null;for(let s of US){let o=s+"/"+n;try{return lp.accessSync(o,lp.constants.X_OK),o}catch{}}return null}function Ur(n=process.env){return n.LEFTOFF_GH||up("gh",n)}function ku(n=process.env){return n.LEFTOFF_BREW||up("brew",n)}function cp(n,e,{env:t,timeoutMs:s}){return new Promise(o=>{FS(n,e,{env:HS(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 BS(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 KS(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:Ta,text:"Install gh from "+Ta+", then run gh auth login"}:n==="signed-out"?{cmd:"gh auth login",text:"Sign in to GitHub"}:null}async function Hr({env:n=process.env,timeoutMs:e=4e3}={}){let t=!!ku(n),s={version:null,account:null,host:null,brew:t,checkedAt:new Date().toISOString()},o=Ur(n),r=(u,d={})=>({status:u,...s,...d,fix:KS(u,{brew:t})});if(!o)return r("missing");let i=await cp(o,["--version"],{env:n,timeoutMs:e});if(!i.ok)return r("missing");let a=(i.out.match(/gh version (\S+)/)||[])[1]||null,l=await cp(o,["auth","status"],{env:n,timeoutMs:e}),c=BS(l.out);return l.ok||l.code==="timeout"&&c?r("ready",{version:a,...c||{}}):r("signed-out",{version:a})}async function dp({env:n=process.env,fresh:e=!1,now:t=Date.now,read:s=Hr}={}){let o=t(),r=Mt.value&&Mt.value.status==="ready"?10*6e4:3e4;return!e&&Mt.value&&o-Mt.at<r?Mt.value:(Mt.pending||(Mt.pending=Promise.resolve().then(()=>s({env:n})).then(i=>(Mt.value=i,Mt.at=t(),i)).finally(()=>{Mt.pending=null})),Mt.pending)}var Ta,US,HS,Mt,_a=z(()=>{Is();Ta="https://cli.github.com",US=["/opt/homebrew/bin","/usr/local/bin"];HS=(n=process.env)=>({...n,GH_PROMPT_DISABLED:"1",GH_NO_UPDATE_NOTIFIER:"1",NO_COLOR:"1",CLICOLOR:"0"});Mt={at:0,value:null,pending:null}});import{execFile as GS}from"node:child_process";import $S from"node:fs";function JS(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();WS.has(l)?s++:zS.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 qS(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:JS(e.statusCheckRollup)}}function VS({cwd:n,branch:e,gh:t,timeoutMs:s=6e3}={}){return t=t||Ur()||"gh",new Promise(o=>{if(!n||!e)return o(null);try{if(!$S.statSync(n).isDirectory())return o(null)}catch{return o(null)}GS(t,["pr","view",e,"--json",YS],{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(qS(String(i)))})})}var YS,WS,zS,Ea,fp=z(()=>{_a();YS="number,title,state,url,isDraft,headRefName,statusCheckRollup",WS=new Set(["FAILURE","ERROR","TIMED_OUT","CANCELLED","ACTION_REQUIRED","STARTUP_FAILURE","STALE"]),zS=new Set(["SUCCESS","NEUTRAL","SKIPPED"]);Ea=class{constructor({ttlMs:e=5*6e4,missTtlMs:t=10*6e4,view:s=VS,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 Ca from"node:fs";import hp from"node:path";import ZS from"node:crypto";function Ns(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(`
94
- `):t=String(e.kind)+":"+(e.since||""),ZS.createHash("sha256").update(String(n)+`
95
- `+t).digest("hex").slice(0,20)}var XS,QS,pp,Oa,mp=z(()=>{XS=30;QS=/^\.needs-dismissed\..+\.json$/,pp=n=>{try{let e=JSON.parse(Ca.readFileSync(n,"utf8"));return e&&typeof e=="object"?e:null}catch{return null}},Oa=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 hp.join(this.root,".needs-dismissed."+String(this.machine).replace(/[^\w.-]/g,"_")+".json")}own(){let e=pp(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=Ca.readdirSync(this.root).filter(i=>QS.test(i))}catch{}for(let i of o){let a=pp(hp.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()-XS*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;Ca.writeFileSync(o,JSON.stringify(e,null,2)+`
96
- `),Ca.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}}});function bu(n){return String(n||"").split(/[\\/]+/).filter(Boolean)}function ek(n){let e=bu(n);return e.length?e[e.length-1]:""}function vu(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 en(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=bu(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)<bu(l).length&&(t.set(l,t.get(l)+1),i=!0);if(!i)break}return new Map(e.map(o=>[o,s(o)]))}function ko(n,e,{loose:t=!1}={}){let s=String(e||"").trim().toLowerCase().replace(/\/+$/,"");if(!s)return!1;let o=vu(n),r=[o,n.repoLabel,n.repoName,ek(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 Ia(n,e){if(!e)return n;let t=n.filter(s=>ko(s,e));return t.length?t:n.filter(s=>ko(s,e,{loose:!0}))}var Ms=z(()=>{});function Ft(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||"UTC"}catch{return"UTC"}}function bo(n){if(!n||typeof n!="string")return!1;try{return new Intl.DateTimeFormat("en-US",{timeZone:n}),!0}catch{return!1}}function nk(n){return xu.has(n)||xu.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"})),xu.get(n)}function Ut(n,e){let t={};for(let c of nk(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=tn.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:tn[l],date:s+"-"+String(o).padStart(2,"0")+"-"+String(r).padStart(2,"0")}}function gp(n,e){let t=Ut(n,e);return Date.UTC(t.y,t.mo-1,t.d,t.hh,t.mm,t.ss)-Math.floor(n/1e3)*1e3}function Ht(n,e,t,s,o,r){let i=Date.UTC(n,e-1,t,s,o),a=gp(i,r),l=i-a,c=gp(l,r);return c!==a&&(l=i-c),l}function Au(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 vo(n){if(!Array.isArray(n))return tn.slice();let e=tn.filter(t=>n.map(s=>String(s).toLowerCase().slice(0,3)).includes(t));return e.length?e:tn.slice()}function Ma(n={},e=Ft()){let t=n||{},s=t.time===null?null:/^\d{1,2}:\d{2}$/.test(String(t.time||""))?String(t.time):"07:00",o=bo(t.timeZone)?t.timeZone:e;return{time:s,tz:o,days:vo(t.days),skipEmpty:t.skipEmpty!==!1,zoneOverride:bo(t.timeZone)?t.timeZone:null}}function xo(n,e){let t=Ut(n,e.tz),s=e.days.includes(t.day),o=e.time?Ht(t.y,t.mo,t.d,Math.floor(Na(e.time)/60),Na(e.time)%60,e.tz):null;return{date:t.date,day:t.day,isDay:s,at:o,past:o!=null&&n>=o}}function yp(n,e,{doneToday:t=!1}={}){if(!e.time)return null;let s=Ut(n,e.tz),o=Math.floor(Na(e.time)/60),r=Na(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=tn[a.getUTCDay()];if(!e.days.includes(l))continue;let c=Ht(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 wp(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=Au(t,e);return r+" · "+e+(i&&i!==e?" ("+i+")":"")}function Sp(n){let e=vo(n);return e.length===7?"Every day":e.length===5&&Ls.every(t=>e.includes(t))?"Weekdays":e.length===2&&e.includes("sat")&&e.includes("sun")?"Weekends":e.map(t=>tk[t]).join(", ")}function kp({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 tn,tk,Ls,xu,Na,Br=z(()=>{tn=["sun","mon","tue","wed","thu","fri","sat"],tk={sun:"Sun",mon:"Mon",tue:"Tue",wed:"Wed",thu:"Thu",fri:"Fri",sat:"Sat"},Ls=["mon","tue","wed","thu","fri"];xu=new Map;Na=n=>{let[e,t]=String(n).split(":").map(Number);return(e||0)*60+(t||0)}});var Fp={};Jt(Fp,{CATCHUP_LENGTHS:()=>Cu,CATCHUP_NAME_RE:()=>sk,CATCHUP_SCHEMA:()=>xp,CATCHUP_SYSTEM:()=>Np,availableFrom:()=>Lu,buildRecap:()=>Op,catchupFileName:()=>Pp,catchupLength:()=>Ou,catchupOptions:()=>Ua,catchupZone:()=>ja,collectSessions:()=>Mu,createCatchup:()=>$r,heuristicSummary:()=>Kr,lastWorkday:()=>Nu,modelLabel:()=>Rp,parseBullets:()=>Lp,parseCatchupMd:()=>Du,parseDuration:()=>Ap,parseWhen:()=>Eu,pickRepos:()=>Ep,recapSession:()=>Cp,renderCatchupMd:()=>jp,resolveWindow:()=>Fa,stillOpenLists:()=>Ip,summarize:()=>Dp,summaryPrompt:()=>Mp,windowChoices:()=>Tp,workdayLabel:()=>rk,workdaysOf:()=>Iu});function Ou(n=ye,e){let t=zc(n||ye,e);return{key:t,...Cu[t]}}function ja(n=ye){let e=n&&n.digest&&n.digest.timeZone;return bo(e)?e:Ft()}function Iu(n=ye){let e=n&&n.catchup&&n.catchup.workdays,t=Array.isArray(e)?vo(e):null;return t&&t.length?t:Ls}function Tu(n){let e=new Error(n);return e.badWindow=!0,e}function Ra(n,e,t){let s=Ut(n,t),o=new Date(Date.UTC(s.y,s.mo-1,s.d-e));return Ht(o.getUTCFullYear(),o.getUTCMonth()+1,o.getUTCDate(),0,0,t)}function Nu(n,e,t=Ls){for(let s=1;s<=7;s++){let o=Ra(n,s,e),r=Ut(o+2*Gr,e);if(t.includes(r.day))return{from:o,day:r.day,daysAgo:s}}return{from:Ra(n,1,e),day:Ut(n-Da,e).day,daysAgo:1}}function rk(n,e,t=Ls){return"Since "+La[Nu(n,e,t).day]}function ik(n){let e=n/Gr;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 Ap(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"?Gr:s==="d"?Da:7*Da);return o>0?o:null}function Eu(n,e){let t=String(n??"").trim(),s=t.match(/^(\d{4})-(\d{2})-(\d{2})$/);if(s)return Ht(+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 Ht(+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 Fa(n,{now:e=Date.now(),tz:t=Ft(),lastAt:s=null,to:o=null,workdays:r=Ls}={}){let i=o!=null&&o!==""?typeof o=="number"?o:Eu(o,t):e;if(i==null||isNaN(i))throw Tu(`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=(p,h,m,y={})=>{if(!(p<i))throw Tu("the window starts after it ends ("+new Date(p).toISOString()+" → "+new Date(i).toISOString()+")");return{from:p,to:i,spec:a||"last",kind:h,label:m,...y}};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-Da,"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 p=Nu(i,t,r);return c(p.from,"workday","Since "+La[p.day])}if(l==="today")return c(ok(i,t),"day","Today");if(l==="yesterday")return c(Ra(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)&&tn.includes(u)&&La[u].toLowerCase().startsWith(l.replace(/^since-/,"")))for(let p=1;p<=7;p++){let h=Ra(i,p,t);if(Ut(h+2*Gr,t).day===u)return c(h,"day","Since "+La[u])}let d=Ap(l);if(d)return c(i-d,"duration",ik(d).replace(/^l/,"L"));let f=Eu(a,t);if(f!=null)return c(f,"custom","Since "+Pa(f,t));throw Tu(`can't read the window "`+a+'". Try: 24h, 3d, 1w, workday, yesterday, friday, or a date like 2026-10-09')}function Tp({now:n=Date.now(),tz:e=Ft(),lastAt:t=null,workdays:s=Ls}={}){let o=c=>{try{return Fa(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 Mu(n,e={}){let{from:t,to:s,now:o=Date.now(),cfg:r=ye,machine:i=null}=e,a=e.repoKeyFor||(c=>vu(c)),l=[];for(let{session:c,checkpoints:u=[]}of n){if(!c||!c.id||c.archived||c.ignored||!c.agent&&(pt(c,u.length)||eo(c,u.length)))continue;let d=u.filter(v=>v&&v.createdAt).slice().sort((v,T)=>P(v.createdAt)-P(T.createdAt)),f=d.filter(v=>bp(P(v.createdAt),t,s)),p=i&&c.machine&&c.machine!==i,h=c.agent?c.agentStatus:Pt(c,o,p?{...r,staleAfterSec:Math.max(r.staleAfterSec||180,900)}:r),m=[c.lastActivityAt,c.startedAt,c.endedAt].some(v=>bp(P(v),t,s)),y=c.agent?ak(c):(e.needsOf?e.needsOf(c,h):lk(c,h))||null,w=d[d.length-1]||null,S=w&&/^YOU:/i.test(w.next||"")&&h!=="ended"&&h!=="stale"&&!c.endedAt,k=s>=o-6e4;!f.length&&!m&&!(k&&(y||S))||l.push({s:c,cps:d,inWin:f,latest:w,status:h,needs:y,youNext:S,repoKey:a(c)})}return l}function ak(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 lk(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 Lu(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,_p(i)),t.set(i.repoKey,a)}let s=e||en(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)||(P(a.last)||0)-(P(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 Ep(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||en(n.map(a=>a.repoKey));for(let a of s){let l=u=>n.filter(d=>ko({...d.s,repoKey:d.repoKey,repoLabel:i.get(d.repoKey),repoName:fe(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 uk(n){return n.lastPreview?"no checkpoint yet; last reply: "+U(n.lastPreview,220):n.firstPrompt?"no checkpoint yet; started with “"+U(n.firstPrompt,200)+"”":"no checkpoint yet"+(n.turns?" ("+n.turns+" turn"+(n.turns===1?"":"s")+")":"")}function dk(n,e){let t=n.s;return n.needs?"needs you":n.status==="ended"?"closed"+(t.endedAt?" "+Pa(P(t.endedAt),e):""):n.status==="stale"?"gone quiet":n.status==="dormant"?"idle":n.status==="needs_input"?"needs you":n.status}function Cp(n,{length:e,tz:t,patterns:s,labels:o}={}){let r=e||Cu.informative,i=m=>m==null||m===""?m:Vi(String(m),s),a=n.s,l=n.latest,c=_u([...n.inWin.map(m=>m.doing),...r.done>4&&l&&l.recap&&n.inWin.includes(l)?[l.recap]:[]]).slice(-r.done),u=l&&(P(a.lastActivityAt)||0)>P(l.createdAt)+5*6e4,d=To(l&&l.next).toLowerCase().replace(/[.!?…]+$/,""),f=_u([l&&l.openQuestion,...l&&l.questions||[]]).filter(m=>To(m).toLowerCase().replace(/[.!?…]+$/,"")!==d).slice(0,r.questions),p=_u((l&&l.nextSteps||[]).map(To)).filter(m=>m.toLowerCase()!==To(l&&l.next).toLowerCase()).slice(0,Math.max(0,r.steps-1)),h=a.repo&&a.repo.branch;return{id:a.id,title:i(a.title||"Untitled session"),repoKey:n.repoKey,repo:o&&o.get(n.repoKey)||fe(a),branch:h||null,machine:a.machine||null,cwd:a.cwd||null,status:n.status,state:dk(n,t),lastActivityAt:G(_p(n))||null,checkpoints:n.inWin.length,done:c.map(m=>i(U(m,240))),stoppedAt:i(l?U(l.stoppedAt,280):uk(a)),next:l?i(U(l.next,240)):null,nextSteps:p.map(m=>i(U(m,200))),questions:f.map(m=>i(U(m,240))),needsYou:n.needs?i(U(n.needs.message||n.needs.question||"waiting on you",200)):n.youNext?i(U(To(l.next),200)):null,needsKind:n.needs?n.needs.kind||"terminal":n.youNext?"next":null,behind:!!u,lastReply:u&&a.lastPreview&&!ck(a.lastPreview,l&&l.stoppedAt)?i(U(a.lastPreview,200)):null,resume:a.agent?null:Qs(a),agent:a.agent?{name:a.agent.name,display:a.agent.display,url:a.agent.url||null,reply:a.agent.reply||null}:null}}function Op(n,e={}){let{now:t=Date.now(),cfg:s=ye,machine:o=null}=e,r=e.tz||ja(s),i=e.window,a=Ou(s,e.lengthOverride),l=s.redact&&s.redact.patterns||[],c=Mu(n,{...e,from:i.from,to:i.to,now:t,cfg:s,machine:o}),u=(e.machines||[]).flatMap(k=>String(k).split(",")).map(k=>k.trim()).filter(Boolean);u.length&&(c=c.filter(k=>u.includes(k.s.machine||o)));let d=en(c.map(k=>k.repoKey)),f=Lu(c,{labels:d}),{keys:p,unmatched:h}=Ep(c,e.repos,d);p&&(c=c.filter(k=>p.has(k.repoKey)));let m=c.map(k=>Cp(k,{length:a,tz:r,patterns:l,labels:d})).sort((k,v)=>!!v.needsYou-!!k.needsYou||(P(v.lastActivityAt)||0)-(P(k.lastActivityAt)||0)),y=new Map;for(let k of m)y.has(k.repoKey)||y.set(k.repoKey,{key:k.repoKey,label:k.repo,sessions:[]}),y.get(k.repoKey).sessions.push(k);let w=[...y.values()],S=[...new Set(m.map(k=>k.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:w,sessions:m,needsYou:m.filter(k=>k.needsYou),stillOpen:Ip(e.lists,{keys:p,labels:w.map(k=>k.label),from:P(i.from),to:P(i.to)}),machines:S,selection:{repos:p?w.map(k=>k.label):"all",repoKeys:p?[...p]:null,machines:u.length?u:"all",unmatched:h},available:f,madeOn:o}}function Ip(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&&P(c.at)>=s&&P(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)=>(P(a.updated)||0)-(P(i.updated)||0)).slice(0,20)}function Mp(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(bn).join("; ")+".":"")+(r.stoppedAt?" Stopped at: "+bn(r.stoppedAt)+".":"")+(r.next?" Next: "+bn(r.next)+".":"")+(r.questions.length?" Open: "+r.questions.map(bn).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=>bn(r.text)).join("; ")+".");let s=t.join(`
85
+ `).trimEnd()}function Np(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(sk(l))break;if(!l||l.role!=="assistant"||!l.text||!l.text.trim())continue;let c=l.text;i&&(r=!e||Du.test(e)?Ru(c):[],e&&(c=nk(c,e)),i=!1);let u=Cp(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(sn(u[d].replace(/^#{1,6}\s+/,""),s))}return!o.length&&!r.length?null:{lines:o,options:r}}function Ru(n){let e=Cp(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=>sn(a.key+". "+a.text.replace(/\*\*/g,"").replace(/\s+[—–-]\s+.*$/,"").replace(/[:.]$/,"").trim(),60)):[]}function ik(n={},{cwd:e}={}){let t=n.input||{},s=String(n.tool||"tool"),o=ok(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=Ns(t.command);return rk.test(r)?"Ran tests":/^git\s+commit\b/.test(r)?"Committed":/^git\s+push\b/.test(r)?"Pushed":"Ran "+sn(t.description?Ns(t.description):r||"a command",48)}case"Grep":return"Searched “"+sn(Ns(t.pattern),32)+"”";case"Glob":return"Listed "+sn(Ns(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 “"+sn(Ns(t.query),32)+"”";case"TodoWrite":return"Updated the to-do list";case"Task":case"Agent":return"Ran an agent"+(t.description?": "+sn(Ns(t.description),36):"");case"AskUserQuestion":return"Asked you";case"ExitPlanMode":return"Proposed a plan";default:return"Used "+sn(s.replace(/^mcp__/,"").replace(/__/g," "),40)}}function Ip(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:sn(r.text.trim(),s),chips:[]});continue}let i=(r.text||"").trim(),a=(r.toolUses||[]).map(c=>({label:ik(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=sn(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 sn,Lu,Ns,Du,sk,ok,rk,Mp=J(()=>{sn=(n,e)=>(n=String(n??""),n.length>e?n.slice(0,e-1).trimEnd()+"…":n),Lu=n=>String(n??"").replace(/\r\n?/g,`
90
+ `),Ns=n=>Lu(n).replace(/\s+/g," ").trim();Du=/\b(which|or|options?|prefer|pick|choose|one of)\b/i,sk=n=>n&&n.role==="user"&&n.text&&n.text.trim();ok=(n,e)=>{let t=String(n||"");return t?e&&t.startsWith(String(e).replace(/\/$/,"")+"/")?t.slice(String(e).replace(/\/$/,"").length+1):t.includes("/")?tk.basename(t):t:""},rk=/(^|[\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 ak,execFileSync as lk}from"node:child_process";import ck from"node:fs";import Pu from"node:path";function hk(){if(process.env.CHECKPOINTS_NO_CLAUDE)return!1;try{return lk(process.platform==="win32"?"where":"which",["claude"],{stdio:"ignore"}),!0}catch{return!1}}function pk(n){let e=n.summarizer||{},t=String(e.provider||e.mode||"auto").toLowerCase();return Dp[t]?Dp[t]:hk()?"claude":process.env.ANTHROPIC_API_KEY?"anthropic":"heuristic"}function mk({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=ak("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 ju(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 gk(n={},e=process.env){return xn("anthropic",n,e)}function xn(n,{apiKeyEnv:e,apiKeyFile:t,root:s}={},o=process.env){let r=Ca[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=Pu.isAbsolute(a)?null:Vh(a,o.HOME||o.XDG_CONFIG_HOME?o:process.env);for(let c of[l,Pu.isAbsolute(a)?a:Pu.join(s||Z(),a)])if(c)try{let u=ck.readFileSync(c,"utf8").trim();if(u)return u}catch{}}return null}async function yk(n,{system:e,prompt:t,model:s,maxTokens:o,apiKeyEnv:r,apiKeyFile:i,root:a}){let l=gk({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 ju(n,c+"/v1/messages",{"x-api-key":l,"anthropic-version":"2023-06-01"},{model:Lp[s]||s||Lp.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 Rp(n,{system:e,prompt:t,model:s,maxTokens:o,baseUrl:r,key:i,cloud:a}){let l=await ju(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 wk(n,{system:e,prompt:t,model:s,maxTokens:o,baseUrl:r,apiKeyEnv:i}){let a=i?process.env[i]:null,l=await ju(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 Lp,uk,dk,fk,Oa,Ca,Dp,xt,Is=J(()=>{ve();Ae();Lp={haiku:"claude-haiku-5-5",sonnet:"claude-sonnet-5-5",opus:"claude-opus-5-5"},uk=["haiku","sonnet","opus",""],dk="https://ollama.com",fk="gpt-oss:20b",Oa={ollama:{baseUrl:"http://localhost:11434",model:"llama3.2"},"ollama-cloud":{baseUrl:dk,model:fk},"openai-compatible":{baseUrl:"http://localhost:1234/v1",model:"local-model"}},Ca={anthropic:{env:"ANTHROPIC_API_KEY",file:".secret/anthropic-api-key"},"ollama-cloud":{env:"OLLAMA_API_KEY",file:".secret/ollama-api-key"}},Dp={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"};xt=class{constructor(e,{fetchImpl:t,root:s}={}){this.cfg=e,this.root=s||null,this.fetchImpl=t||((...o)=>fetch(...o)),this.mode=pk(e)}get model(){let e=String(this.cfg.summarizer&&this.cfg.summarizer.model||"");return Oa[this.mode]&&uk.includes(e)?Oa[this.mode].model:e||"haiku"}get baseUrl(){return this.cfg.summarizer&&this.cfg.summarizer.baseUrl||Oa[this.mode]&&Oa[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 mk(r);if(this.mode==="anthropic")return yk(this.fetchImpl,r);if(this.mode==="ollama")return Rp(this.fetchImpl,r);if(this.mode==="ollama-cloud"){let i=xn("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 Rp(this.fetchImpl,{...r,key:i,cloud:!0})}if(this.mode==="openai-compatible")return wk(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=Fh(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{...hu(s,o,{length:i}),generator:{by:"heuristic",fallbackFrom:this.mode,error:String(a.message||a).slice(0,160)}}}return{...hu(s,o,{length:i}),generator:{by:"heuristic"}}}async title(e){if(this.mode!=="heuristic"&&e.firstPrompt)try{let t=await this.complete({system:Hh,prompt:`First prompt:
92
+ `+e.firstPrompt,maxTokens:30}),s=Bh(t.text);if(s)return{title:s,titleSource:"generated"}}catch{}return{title:Uh(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 xo from"node:fs";import Sk from"node:os";import Na from"node:path";import{execFileSync as ET,spawn as kk,spawnSync as bk}from"node:child_process";import{fileURLToPath as vk}from"node:url";async function Yr({baseUrl:n=process.env.CHECKPOINTS_CLOUD_URL||Ms,fetchImpl:e=fetch,timeoutMs:t=5e3}={}){let s=n.replace(/\/$/,"");try{let o=await Ma(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 La(n=[],{curated:e=$r,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 Ia(n){let e=String(n||"").trim().toLowerCase();return e?e.includes(":")?e:e+":latest":""}function We(n,e){return Ia(n)===Ia(e)}function To(n=[],{curated:e=Kr,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 jp(n,{totalMemBytes:e=Sk.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 Da(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 _o(n,e=process.env){let t=String(e.PATH||"").split(Na.delimiter).filter(Boolean),s=process.platform==="win32"?[".exe",".cmd",".bat",""]:[""];for(let o of t)for(let r of s){let i=Na.join(o,n+r);try{if(xo.statSync(i).isFile())return xo.accessSync(i,xo.constants.X_OK),i}catch{}}return null}function Ls(n=process.env){let e=_o("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(xo.existsSync(t))return t;return null}async function Ma(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 Bt({baseUrl:n=ht,fetchImpl:e=fetch,env:t=process.env}={}){let s=n.replace(/\/$/,""),o={kind:"ollama",running:!1,baseUrl:s,version:null,models:[],binary:Ls(t),error:null};try{let r=await Ma(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 Ma(e,s+"/api/version")).version||null}catch{}}catch(r){o.error=r.cause&&r.cause.code||r.name||r.message}return o}async function An({baseUrl:n=on,fetchImpl:e=fetch}={}){let t=n.replace(/\/$/,""),s={kind:"openai-compatible",running:!1,baseUrl:t,models:[],error:null};try{let o=await Ma(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 Eo({baseUrl:n=ht,fetchImpl:e=fetch,timeoutMs:t=2e4}={}){let s=Date.now()+t;for(;Date.now()<s;){let o=await Bt({baseUrl:n,fetchImpl:e});if(o.running)return o;await Ak(500)}return Bt({baseUrl:n,fetchImpl:e})}function Tk(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 Oo({tag:n,baseUrl:e=ht,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=Tk(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 Ds(n,{fetchImpl:e,transcriptFile:t=xk,root:s}={}){let o=ao(Se,{summarizer:n}),{messages:r,meta:i}=So(xo.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=la({session:a,messages:r,cfg:o}),c=new xt(o,{...e?{fetchImpl:e}:{},...s?{root:s}:{}}),u=Date.now(),d=await c.checkpoint({...l,messages:r,session:a}),f=Date.now()-u,p=d.generator&&d.generator.by==="heuristic"&&d.generator.fallbackFrom;return{ok:!p,doing:d.doing,stoppedAt:d.stoppedAt,next:d.next,parseOk:d.parseOk!==!1,ms:f,generator:d.generator,error:p?d.generator.error:null}}function Fp(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 _o("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 Wr(n=process.platform,e=Ls()){return n==="darwin"&&xo.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 Ra({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?Wr(n,e):null}function Up(n,{spawnImpl:e=kk,spawnSyncImpl:t=bk}={}){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 ht,on,xk,Ao,Kr,Ms,Pp,$r,Ak,Rs=J(()=>{jr();Ae();Is();ht="http://localhost:11434",on="http://localhost:1234/v1",xk=Na.resolve(Na.dirname(vk(import.meta.url)),"..","assets","sample-transcript.jsonl"),Ao="2026-10-09",Kr=[{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"'}],Ms="https://ollama.com",Pp="2026-10-09",$r=[{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"}];Ak=n=>new Promise(e=>setTimeout(e,n))});import Hp from"node:fs";import{execFile as _k}from"node:child_process";function Gp(n,e){let t=_o(n,e);if(t)return t;if(e.CHECKPOINTS_TEST_NO_SYSTEM_BINS)return null;for(let s of Ek){let o=s+"/"+n;try{return Hp.accessSync(o,Hp.constants.X_OK),o}catch{}}return null}function Ps(n=process.env){return n.LEFTOFF_GH||Gp("gh",n)}function Fu(n=process.env){return n.LEFTOFF_BREW||Gp("brew",n)}function Bp(n,e,{env:t,timeoutMs:s}){return new Promise(o=>{_k(n,e,{env:Uu(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 Ok(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 Ck(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:Pa,text:"Install gh from "+Pa+", then run gh auth login"}:n==="signed-out"?{cmd:"gh auth login",text:"Sign in to GitHub"}:null}async function zr({env:n=process.env,timeoutMs:e=4e3}={}){let t=!!Fu(n),s={version:null,account:null,host:null,brew:t,checkedAt:new Date().toISOString()},o=Ps(n),r=(u,d={})=>({status:u,...s,...d,fix:Ck(u,{brew:t})});if(!o)return r("missing");let i=await Bp(o,["--version"],{env:n,timeoutMs:e});if(!i.ok)return r("missing");let a=(i.out.match(/gh version (\S+)/)||[])[1]||null,l=await Bp(o,["auth","status"],{env:n,timeoutMs:e}),c=Ok(l.out);return l.ok||l.code==="timeout"&&c?r("ready",{version:a,...c||{}}):r("signed-out",{version:a})}async function ja({env:n=process.env,fresh:e=!1,now:t=Date.now,read:s=zr}={}){let o=t(),r=jt.value&&jt.value.status==="ready"?10*6e4:3e4;return!e&&jt.value&&o-jt.at<r?jt.value:(jt.pending||(jt.pending=Promise.resolve().then(()=>s({env:n})).then(i=>(jt.value=i,jt.at=t(),i)).finally(()=>{jt.pending=null})),jt.pending)}var Pa,Ek,Uu,jt,Co=J(()=>{Rs();Pa="https://cli.github.com",Ek=["/opt/homebrew/bin","/usr/local/bin"];Uu=(n=process.env)=>({...n,GH_PROMPT_DISABLED:"1",GH_NO_UPDATE_NOTIFIER:"1",NO_COLOR:"1",CLICOLOR:"0"});jt={at:0,value:null,pending:null}});import{execFile as Nk}from"node:child_process";import Ik from"node:fs";function Hu(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();Lk.has(l)?s++:Dk.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 Rk(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:Hu(e.statusCheckRollup)}}function Pk({cwd:n,branch:e,gh:t,timeoutMs:s=6e3}={}){return t=t||Ps()||"gh",new Promise(o=>{if(!n||!e)return o(null);try{if(!Ik.statSync(n).isDirectory())return o(null)}catch{return o(null)}Nk(t,["pr","view",e,"--json",Mk],{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(Rk(String(i)))})})}var Mk,Lk,Dk,Fa,Bu=J(()=>{Co();Mk="number,title,state,url,isDraft,headRefName,statusCheckRollup",Lk=new Set(["FAILURE","ERROR","TIMED_OUT","CANCELLED","ACTION_REQUIRED","STARTUP_FAILURE","STALE"]),Dk=new Set(["SUCCESS","NEUTRAL","SKIPPED"]);Fa=class{constructor({ttlMs:e=5*6e4,missTtlMs:t=10*6e4,view:s=Pk,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 Ua from"node:fs";import{execFile as jk}from"node:child_process";function js(n){let e=new RegExp(Yp.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 Ha(n){let e=[],t=String(n||""),s=new RegExp(Yp.source,"g"),o;for(;o=s.exec(t);){let r=js(o[0]);r&&!e.some(i=>i.url===r.url)&&e.push(r)}return e}function zp(n,{repo:e}={}){let t=String(n??"").trim();if(!t)return{error:"empty"};let s=js(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 Ka(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 Jp(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"?Jp(e.content):"":typeof e=="string"?e:"").join(`
94
+ `):""}function qp(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 Ha(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 Ha(l.text))s(c,"mentioned");else if(l.type==="tool_use"){let c=l.input&&typeof l.input.command=="string"?l.input.command:"";if(Fk.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 Ha(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=Jp(l.content)+`
96
+ `+(r.toolUseResult&&typeof r.toolUseResult=="object"?String(r.toolUseResult.stdout||""):""),u=Ha(c);u.length&&s(u[u.length-1],"created")}}}}return[...t.values()]}function Vp(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>Wp;){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=js(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,Wp)}function Vr(n){return Array.isArray(n)?[...new Set(n.map(e=>(js(e)||{}).url).filter(Boolean))].slice(0,100):[]}function $p(n){if(!n||typeof n!="object"||!Number.isInteger(n.number)||n.number<=0)return null;let e=js(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=>Jr(i,80)):[],r=String(n.reviewDecision||"").toUpperCase();return{repo:e.repo,number:n.number,url:e.url,title:Jr(n.title)||"Pull request #"+n.number,state:s,branch:n.headRefName?Jr(n.headRefName):null,base:n.baseRefName?Jr(n.baseRefName):null,author:n.author&&n.author.login?Jr(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:Hu(n.statusCheckRollup)}}function Gu(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 Zp(n,{now:e=Date.now(),me:t=null}={}){let s={attention:[],open:[],draft:[],merged:[]};for(let r of n){let i=r.pr,a=Gu(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*Ba&&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 Xp(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 Ku(n,{gh:e,timeoutMs:t=1e4,env:s=process.env}={}){let o=e||Ps(s);return new Promise(r=>{if(!o)return r({ok:!1,code:"ENOENT",out:""});jk(o,n,{env:Uu(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 Qp(n,e,{maxBytes:t=16<<20}={}){let s=Ua.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=Ua.openSync(n,"r");try{let i=Buffer.alloc(s.size-o);Ua.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{Ua.closeSync(r)}}var Kp,Yp,Wp,Fk,Jr,Ba,Ga,$u=J(()=>{Co();Bu();Kp="number,title,state,url,isDraft,headRefName,baseRefName,author,createdAt,updatedAt,mergedAt,reviewDecision,reviewRequests,statusCheckRollup",Yp=/https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)\/pull\/(\d{1,7})(?![\d])/g,Wp=24;Fk=/\bgh\s+pr\s+create\b/;Jr=(n,e=200)=>String(n??"").replace(/[\u0000-\u001F\u007F]+/g," ").trim().slice(0,e);Ba=864e5;Ga=class{constructor({run:e=Ku,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*Ba,ok:!0});for(let s of t)this.views.set(s.url,{pr:s,at:this.now()+365*Ba})}seedView(e){this.views.set(e.url,{pr:e,at:this.now()+365*Ba})}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",Kp]:["pr","list","--repo",t,"--state","open","--limit","100","--json",Kp],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?$p(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($p).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 $a from"node:fs";import em from"node:path";import Uk from"node:crypto";function Fs(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||""),Uk.createHash("sha256").update(String(n)+`
98
+ `+t).digest("hex").slice(0,20)}var Hk,Bk,tm,Ya,nm=J(()=>{Hk=30;Bk=/^\.needs-dismissed\..+\.json$/,tm=n=>{try{let e=JSON.parse($a.readFileSync(n,"utf8"));return e&&typeof e=="object"?e:null}catch{return null}},Ya=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 em.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=$a.readdirSync(this.root).filter(i=>Bk.test(i))}catch{}for(let i of o){let a=tm(em.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()-Hk*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;$a.writeFileSync(o,JSON.stringify(e,null,2)+`
99
+ `),$a.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}}});function Wu(n){return String(n||"").split(/[\\/]+/).filter(Boolean)}function Gk(n){let e=Wu(n);return e.length?e[e.length-1]:""}function Zr(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 Gt(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=Wu(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)<Wu(l).length&&(t.set(l,t.get(l)+1),i=!0);if(!i)break}return new Map(e.map(o=>[o,s(o)]))}function sm(n,{countIn:e}={}){let t=new Map;for(let o of n){let r=Zr(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,Kk(o)),!i.label&&o.repoLabel&&(i.label=o.repoLabel),t.set(r,i)}if(e)for(let o of e){let r=t.get(Zr(o));r&&r.count++}else for(let o of t.values())o.count=o.total;let s=Gt(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 Us(n,e,{loose:t=!1}={}){let s=String(e||"").trim().toLowerCase().replace(/\/+$/,"");if(!s)return!1;let o=Zr(n),r=[o,n.repoLabel,n.repoName,Gk(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 Wa(n,e){if(!e)return n;let t=n.filter(s=>Us(s,e));return t.length?t:n.filter(s=>Us(s,e,{loose:!0}))}var Yu,Kk,Hs=J(()=>{Yu=n=>{let e=Date.parse(n||"");return isNaN(e)?0:e},Kk=n=>Math.max(Yu(n.lastActivityAt),Yu(n.endedAt),Yu(n.startedAt))});function Kt(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||"UTC"}catch{return"UTC"}}function No(n){if(!n||typeof n!="string")return!1;try{return new Intl.DateTimeFormat("en-US",{timeZone:n}),!0}catch{return!1}}function Yk(n){return zu.has(n)||zu.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"})),zu.get(n)}function $t(n,e){let t={};for(let c of Yk(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=rn.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:rn[l],date:s+"-"+String(o).padStart(2,"0")+"-"+String(r).padStart(2,"0")}}function om(n,e){let t=$t(n,e);return Date.UTC(t.y,t.mo-1,t.d,t.hh,t.mm,t.ss)-Math.floor(n/1e3)*1e3}function Yt(n,e,t,s,o,r){let i=Date.UTC(n,e-1,t,s,o),a=om(i,r),l=i-a,c=om(l,r);return c!==a&&(l=i-c),l}function Ju(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 Io(n){if(!Array.isArray(n))return rn.slice();let e=rn.filter(t=>n.map(s=>String(s).toLowerCase().slice(0,3)).includes(t));return e.length?e:rn.slice()}function Ja(n={},e=Kt()){let t=n||{},s=t.time===null?null:/^\d{1,2}:\d{2}$/.test(String(t.time||""))?String(t.time):"07:00",o=No(t.timeZone)?t.timeZone:e;return{time:s,tz:o,days:Io(t.days),skipEmpty:t.skipEmpty!==!1,zoneOverride:No(t.timeZone)?t.timeZone:null}}function Mo(n,e){let t=$t(n,e.tz),s=e.days.includes(t.day),o=e.time?Yt(t.y,t.mo,t.d,Math.floor(za(e.time)/60),za(e.time)%60,e.tz):null;return{date:t.date,day:t.day,isDay:s,at:o,past:o!=null&&n>=o}}function rm(n,e,{doneToday:t=!1}={}){if(!e.time)return null;let s=$t(n,e.tz),o=Math.floor(za(e.time)/60),r=za(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=rn[a.getUTCDay()];if(!e.days.includes(l))continue;let c=Yt(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 im(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=Ju(t,e);return r+" · "+e+(i&&i!==e?" ("+i+")":"")}function am(n){let e=Io(n);return e.length===7?"Every day":e.length===5&&Bs.every(t=>e.includes(t))?"Weekdays":e.length===2&&e.includes("sat")&&e.includes("sun")?"Weekends":e.map(t=>$k[t]).join(", ")}function lm({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 rn,$k,Bs,zu,za,Xr=J(()=>{rn=["sun","mon","tue","wed","thu","fri","sat"],$k={sun:"Sun",mon:"Mon",tue:"Tue",wed:"Wed",thu:"Thu",fri:"Fri",sat:"Sat"},Bs=["mon","tue","wed","thu","fri"];zu=new Map;za=n=>{let[e,t]=String(n).split(":").map(Number);return(e||0)*60+(t||0)}});var _m={};Qt(_m,{CATCHUP_LENGTHS:()=>Xu,CATCHUP_NAME_RE:()=>Wk,CATCHUP_SCHEMA:()=>dm,CATCHUP_SYSTEM:()=>Sm,availableFrom:()=>sd,buildRecap:()=>ym,catchupFileName:()=>Am,catchupLength:()=>Qu,catchupOptions:()=>tl,catchupZone:()=>Qa,collectSessions:()=>nd,createCatchup:()=>ti,heuristicSummary:()=>Qr,lastWorkday:()=>td,modelLabel:()=>xm,parseBullets:()=>bm,parseCatchupMd:()=>od,parseDuration:()=>fm,parseWhen:()=>Zu,pickRepos:()=>mm,recapSession:()=>gm,renderCatchupMd:()=>Tm,resolveWindow:()=>el,stillOpenLists:()=>wm,summarize:()=>vm,summaryPrompt:()=>km,windowChoices:()=>hm,workdayLabel:()=>Jk,workdaysOf:()=>ed});function Qu(n=Se,e){let t=du(n||Se,e);return{key:t,...Xu[t]}}function Qa(n=Se){let e=n&&n.digest&&n.digest.timeZone;return No(e)?e:Kt()}function ed(n=Se){let e=n&&n.catchup&&n.catchup.workdays,t=Array.isArray(e)?Io(e):null;return t&&t.length?t:Bs}function qu(n){let e=new Error(n);return e.badWindow=!0,e}function Za(n,e,t){let s=$t(n,t),o=new Date(Date.UTC(s.y,s.mo-1,s.d-e));return Yt(o.getUTCFullYear(),o.getUTCMonth()+1,o.getUTCDate(),0,0,t)}function td(n,e,t=Bs){for(let s=1;s<=7;s++){let o=Za(n,s,e),r=$t(o+2*ei,e);if(t.includes(r.day))return{from:o,day:r.day,daysAgo:s}}return{from:Za(n,1,e),day:$t(n-Va,e).day,daysAgo:1}}function Jk(n,e,t=Bs){return"Since "+qa[td(n,e,t).day]}function qk(n){let e=n/ei;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 fm(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"?ei:s==="d"?Va:7*Va);return o>0?o:null}function Zu(n,e){let t=String(n??"").trim(),s=t.match(/^(\d{4})-(\d{2})-(\d{2})$/);if(s)return Yt(+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 Yt(+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 el(n,{now:e=Date.now(),tz:t=Kt(),lastAt:s=null,to:o=null,workdays:r=Bs}={}){let i=o!=null&&o!==""?typeof o=="number"?o:Zu(o,t):e;if(i==null||isNaN(i))throw qu(`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=(p,h,m,y={})=>{if(!(p<i))throw qu("the window starts after it ends ("+new Date(p).toISOString()+" → "+new Date(i).toISOString()+")");return{from:p,to:i,spec:a||"last",kind:h,label:m,...y}};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-Va,"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 p=td(i,t,r);return c(p.from,"workday","Since "+qa[p.day])}if(l==="today")return c(zk(i,t),"day","Today");if(l==="yesterday")return c(Za(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)&&rn.includes(u)&&qa[u].toLowerCase().startsWith(l.replace(/^since-/,"")))for(let p=1;p<=7;p++){let h=Za(i,p,t);if($t(h+2*ei,t).day===u)return c(h,"day","Since "+qa[u])}let d=fm(l);if(d)return c(i-d,"duration",qk(d).replace(/^l/,"L"));let f=Zu(a,t);if(f!=null)return c(f,"custom","Since "+Xa(f,t));throw qu(`can't read the window "`+a+'". Try: 24h, 3d, 1w, workday, yesterday, friday, or a date like 2026-10-09')}function hm({now:n=Date.now(),tz:e=Kt(),lastAt:t=null,workdays:s=Bs}={}){let o=c=>{try{return el(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&&K(r.from)},{key:"24h",label:"Last 24 hours",from:i&&K(i.from)},{key:"workday",label:a?a.label:"Since the last workday",from:a&&K(a.from)},{key:"7d",label:"Last 7 days",from:l&&K(l.from)},{key:"custom",label:"Custom…",from:null}]}function nd(n,e={}){let{from:t,to:s,now:o=Date.now(),cfg:r=Se,machine:i=null}=e,a=e.repoKeyFor||(c=>Zr(c)),l=[];for(let{session:c,checkpoints:u=[]}of n){if(!c||!c.id||c.archived||c.ignored||!c.agent&&(dt(c,u.length)||co(c,u.length)))continue;let d=u.filter(A=>A&&A.createdAt).slice().sort((A,v)=>R(A.createdAt)-R(v.createdAt)),f=d.filter(A=>cm(R(A.createdAt),t,s)),p=i&&c.machine&&c.machine!==i,h=c.agent?c.agentStatus:kt(c,o,p?{...r,staleAfterSec:Math.max(r.staleAfterSec||180,900)}:r),m=[c.lastActivityAt,c.startedAt,c.endedAt].some(A=>cm(R(A),t,s)),y=c.agent?Vk(c):(e.needsOf?e.needsOf(c,h):Zk(c,h))||null,S=d[d.length-1]||null,b=S&&/^YOU:/i.test(S.next||"")&&h!=="ended"&&h!=="stale"&&!c.endedAt,w=s>=o-6e4;!f.length&&!m&&!(w&&(y||b))||l.push({s:c,cps:d,inWin:f,latest:S,status:h,needs:y,youNext:b,repoKey:a(c)})}return l}function Vk(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 Zk(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 sd(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,pm(i)),t.set(i.repoKey,a)}let s=e||Gt(t.keys()),o=[...t.values()].map(i=>({...i,label:s.get(i.key)||i.key,last:i.last?K(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 mm(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||Gt(n.map(a=>a.repoKey));for(let a of s){let l=u=>n.filter(d=>Us({...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 Qk(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 eb(n,e){let t=n.s;return n.needs?"needs you":n.status==="ended"?"closed"+(t.endedAt?" "+Xa(R(t.endedAt),e):""):n.status==="stale"?"gone quiet":n.status==="dormant"?"idle":n.status==="needs_input"?"needs you":n.status}function gm(n,{length:e,tz:t,patterns:s,labels:o}={}){let r=e||Xu.informative,i=m=>m==null||m===""?m:aa(String(m),s),a=n.s,l=n.latest,c=Vu([...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=Do(l&&l.next).toLowerCase().replace(/[.!?…]+$/,""),f=Vu([l&&l.openQuestion,...l&&l.questions||[]]).filter(m=>Do(m).toLowerCase().replace(/[.!?…]+$/,"")!==d).slice(0,r.questions),p=Vu((l&&l.nextSteps||[]).map(Do)).filter(m=>m.toLowerCase()!==Do(l&&l.next).toLowerCase()).slice(0,Math.max(0,r.steps-1)),h=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:h||null,machine:a.machine||null,cwd:a.cwd||null,status:n.status,state:eb(n,t),lastActivityAt:K(pm(n))||null,checkpoints:n.inWin.length,done:c.map(m=>i(F(m,240))),stoppedAt:i(l?F(l.stoppedAt,280):Qk(a)),next:l?i(F(l.next,240)):null,nextSteps:p.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(Do(l.next),200)):null,needsKind:n.needs?n.needs.kind||"terminal":n.youNext?"next":null,behind:!!u,lastReply:u&&a.lastPreview&&!Xk(a.lastPreview,l&&l.stoppedAt)?i(F(a.lastPreview,200)):null,resume:a.agent?null:lo(a),agent:a.agent?{name:a.agent.name,display:a.agent.display,url:a.agent.url||null,reply:a.agent.reply||null}:null}}function ym(n,e={}){let{now:t=Date.now(),cfg:s=Se,machine:o=null}=e,r=e.tz||Qa(s),i=e.window,a=Qu(s,e.lengthOverride),l=s.redact&&s.redact.patterns||[],c=nd(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=Gt(c.map(w=>w.repoKey)),f=sd(c,{labels:d}),{keys:p,unmatched:h}=mm(c,e.repos,d);p&&(c=c.filter(w=>p.has(w.repoKey)));let m=c.map(w=>gm(w,{length:a,tz:r,patterns:l,labels:d})).sort((w,A)=>!!A.needsYou-!!w.needsYou||(R(A.lastActivityAt)||0)-(R(w.lastActivityAt)||0)),y=new Map;for(let w of m)y.has(w.repoKey)||y.set(w.repoKey,{key:w.repoKey,label:w.repo,sessions:[]}),y.get(w.repoKey).sessions.push(w);let S=[...y.values()],b=[...new Set(m.map(w=>w.machine).filter(Boolean))].sort();return{window:{from:K(i.from),to:K(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:wm(e.lists,{keys:p,labels:S.map(w=>w.label),from:R(i.from),to:R(i.to)}),machines:b,selection:{repos:p?S.map(w=>w.label):"all",repoKeys:p?[...p]:null,machines:u.length?u:"all",unmatched:h},available:f,madeOn:o}}function wm(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 km(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(Tn).join("; ")+".":"")+(r.stoppedAt?" Stopped at: "+Tn(r.stoppedAt)+".":"")+(r.next?" Next: "+Tn(r.next)+".":"")+(r.questions.length?" Open: "+r.questions.map(Tn).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=>Tn(r.text)).join("; ")+".");let s=t.join(`
97
100
  `);return s.length>e&&(s=s.slice(0,e)+`
98
- …`),s}function Kr(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: "+Ao(r[0].title)+" ("+r[0].repo+"), "+bn(r[0].needsYou)+"."):r.length>1&&s.push(r.length+" sessions are waiting on you: "+r.slice(0,3).map(d=>Ao(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+", "+Ao(d.title)+": "+bn(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: "+Ao(a.title)+", next: "+bn(To(a.next))+".");let l=t.filter(d=>["active","background"].includes(d.status));l.length&&s.push("Still running: "+l.slice(0,3).map(d=>Ao(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=>Ao(d.name)+" ("+d.done+"/"+d.total+", next: "+bn(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 Dp(n,{summarizer:e=null,cfg:t=ye,given:s=null}={}){let o=Ou(t,n.length),r=t.redact&&t.redact.patterns||[],i=a=>a.map(l=>Vi(U(l,400),r));if(!n.sessions.length)return{bullets:Kr(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:Np.replace("{n}",String(o.bullets)),prompt:Mp(n,{maxChars:a}),maxTokens:o.maxTokens}),c=Lp(l&&l.text,Math.max(o.bullets,3));return c?{bullets:i(c),by:{by:"model",provider:e.mode,model:e.model||null}}:{bullets:Kr(n,o.bullets),by:{by:"heuristic",fallbackFrom:e.mode,error:"the model reply had no bullets"}}}catch(a){return{bullets:Kr(n,o.bullets),by:{by:"heuristic",fallbackFrom:e.mode,error:String(a&&a.message||a).slice(0,160)}}}return{bullets:Kr(n,o.bullets),by:{by:"heuristic"}}}function Rp(n){return!n||n.by!=="model"?"heuristic":(n.provider||"model")+(n.model?"/"+n.model:"")}function Pp(n,e,t=0){let s=Ut(n,e);return s.date+"T"+String(s.hh).padStart(2,"0")+String(s.mm).padStart(2,"0")+(t?"-"+(t+1):"")+".md"}function jp(n,e,{now:t=Date.now()}={}){let s=n.window.tz||Ft(),o=P(n.window.from),r=P(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:Rp(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:xp},a=n.machines.length>1,l=[];l.push("# Catch me up: "+n.window.label.replace(/^\w/,c=>c.toLowerCase()).replace(/^since/,"since"),""),l.push("_"+Pa(o,s)+" → "+Pa(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("- "+Le(c));if(l.push(""),n.needsYou.length){l.push("## Needs you ("+n.needsYou.length+")","");for(let c of n.needsYou)l.push("- **"+Le(c.title)+"** ("+c.repo+(a&&c.machine?" · "+c.machine:"")+") — "+Le(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("- **"+Le(c.name)+"** ("+Le(c.repo)+" · "+c.done+"/"+c.total+" done) — next: "+c.next.map(u=>"#"+u.n+" "+Le(u.text)).join("; ")+" <!-- list: "+c.ref+" -->"),c.ticked.length&&l.push(" - Ticked off in this window: "+c.ticked.map(u=>"#"+u.n+" "+Le(u.text)).join("; "));l.push("")}for(let c of n.repos){l.push("## "+Le(c.label)+" ("+c.sessions.length+")","");for(let u of c.sessions){if(l.push("### "+Le(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:** "+Le(u.needsYou)),u.done.length===1)l.push("**Done:** "+Le(u.done[0]));else if(u.done.length){l.push("**Done:**");for(let d of u.done)l.push("- "+Le(d))}u.stoppedAt&&l.push((u.agent?"**Latest:** ":"**Stopped at:** ")+Le(u.stoppedAt)),u.lastReply&&l.push("**Since then:** "+Le(u.lastReply)),u.next&&l.push("**Next:** "+Le(u.next));for(let d of u.nextSteps)l.push("- "+Le(d));if(u.questions.length){l.push("**Open questions:**");for(let d of u.questions)l.push("- "+Le(d))}u.agent?l.push("","From "+Le(u.agent.display)+"’s feed (read-only)"+(u.agent.reply&&/^[^<>\s]+$/.test(u.agent.reply.url)?" · "+Le(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._",""),ys(i,fk)+l.join(`
101
+ …`),s}function Qr(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: "+Lo(r[0].title)+" ("+r[0].repo+"), "+Tn(r[0].needsYou)+"."):r.length>1&&s.push(r.length+" sessions are waiting on you: "+r.slice(0,3).map(d=>Lo(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+", "+Lo(d.title)+": "+Tn(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: "+Lo(a.title)+", next: "+Tn(Do(a.next))+".");let l=t.filter(d=>["active","background"].includes(d.status));l.length&&s.push("Still running: "+l.slice(0,3).map(d=>Lo(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=>Lo(d.name)+" ("+d.done+"/"+d.total+", next: "+Tn(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 vm(n,{summarizer:e=null,cfg:t=Se,given:s=null}={}){let o=Qu(t,n.length),r=t.redact&&t.redact.patterns||[],i=a=>a.map(l=>aa(F(l,400),r));if(!n.sessions.length)return{bullets:Qr(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:Sm.replace("{n}",String(o.bullets)),prompt:km(n,{maxChars:a}),maxTokens:o.maxTokens}),c=bm(l&&l.text,Math.max(o.bullets,3));return c?{bullets:i(c),by:{by:"model",provider:e.mode,model:e.model||null}}:{bullets:Qr(n,o.bullets),by:{by:"heuristic",fallbackFrom:e.mode,error:"the model reply had no bullets"}}}catch(a){return{bullets:Qr(n,o.bullets),by:{by:"heuristic",fallbackFrom:e.mode,error:String(a&&a.message||a).slice(0,160)}}}return{bullets:Qr(n,o.bullets),by:{by:"heuristic"}}}function xm(n){return!n||n.by!=="model"?"heuristic":(n.provider||"model")+(n.model?"/"+n.model:"")}function Am(n,e,t=0){let s=$t(n,e);return s.date+"T"+String(s.hh).padStart(2,"0")+String(s.mm).padStart(2,"0")+(t?"-"+(t+1):"")+".md"}function Tm(n,e,{now:t=Date.now()}={}){let s=n.window.tz||Kt(),o=R(n.window.from),r=R(n.window.to),i={kind:"catchup",generated_at:K(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:xm(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:dm},a=n.machines.length>1,l=[];l.push("# Catch me up: "+n.window.label.replace(/^\w/,c=>c.toLowerCase()).replace(/^since/,"since"),""),l.push("_"+Xa(o,s)+" → "+Xa(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("- "+Pe(c));if(l.push(""),n.needsYou.length){l.push("## Needs you ("+n.needsYou.length+")","");for(let c of n.needsYou)l.push("- **"+Pe(c.title)+"** ("+c.repo+(a&&c.machine?" · "+c.machine:"")+") — "+Pe(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("- **"+Pe(c.name)+"** ("+Pe(c.repo)+" · "+c.done+"/"+c.total+" done) — next: "+c.next.map(u=>"#"+u.n+" "+Pe(u.text)).join("; ")+" <!-- list: "+c.ref+" -->"),c.ticked.length&&l.push(" - Ticked off in this window: "+c.ticked.map(u=>"#"+u.n+" "+Pe(u.text)).join("; "));l.push("")}for(let c of n.repos){l.push("## "+Pe(c.label)+" ("+c.sessions.length+")","");for(let u of c.sessions){if(l.push("### "+Pe(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:** "+Pe(u.needsYou)),u.done.length===1)l.push("**Done:** "+Pe(u.done[0]));else if(u.done.length){l.push("**Done:**");for(let d of u.done)l.push("- "+Pe(d))}u.stoppedAt&&l.push((u.agent?"**Latest:** ":"**Stopped at:** ")+Pe(u.stoppedAt)),u.lastReply&&l.push("**Since then:** "+Pe(u.lastReply)),u.next&&l.push("**Next:** "+Pe(u.next));for(let d of u.nextSteps)l.push("- "+Pe(d));if(u.questions.length){l.push("**Open questions:**");for(let d of u.questions)l.push("- "+Pe(d))}u.agent?l.push("","From "+Pe(u.agent.display)+"’s feed (read-only)"+(u.agent.reply&&/^[^<>\s]+$/.test(u.agent.reply.url)?" · "+Pe(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._",""),vs(i,tb)+l.join(`
99
102
  `).replace(/\n+$/,"")+`
100
- `}function Du(n){let{data:e,body:t}=Be(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(`
101
- `),r=[],i=[],a=[],l=[],c=null,u=null,d=null,f=null,p=h=>{let m=h.match(/<!--\s*session:\s*([^\s>]+)\s*-->/);return m?m[1]:null};for(let h of o){let m=h.replace(/\s+$/,""),y;if(y=m.match(/^#\s+(.*)$/)){s.title=y[1];continue}if(y=m.match(/^##\s+(.*?)(?:\s+\((\d+)\))?$/)){d=null,f=null,/^summary$/i.test(y[1])?c="summary":/^needs you$/i.test(y[1])?c="needs":/^still open$/i.test(y[1])?c="open":(c="repo",u={label:y[1],sessions:[]},a.push(u));continue}if(c==="summary"){(y=m.match(/^-\s+(.*)$/))&&r.push(y[1]);continue}if(c==="needs"){(y=m.match(/^-\s+\*\*(.+?)\*\*\s+\((.*?)\)\s+—\s+(.*?)(?:\s*<!--.*-->)?$/))&&i.push({id:p(m),title:y[1],repo:y[2].split(" · ")[0],needsYou:y[3]});continue}if(c==="open"){if(y=m.match(/^-\s+\*\*(.+?)\*\*\s+\((.*?) · (\d+)\/(\d+) done\)\s+—\s+next:\s*(.*?)(?:\s*<!--\s*list:\s*(\S+)\s*-->)?$/)){let w=S=>S.split(/;\s+(?=#\d+ )/).map(k=>{let v=k.match(/^#(\d+)\s+(.*)$/);return v?{n:Number(v[1]),text:v[2]}:null}).filter(Boolean);l.push({name:y[1],repo:y[2],done:Number(y[3]),total:Number(y[4]),next:w(y[5]),ref:y[6]||null,ticked:[]})}else(y=m.match(/^\s+-\s+Ticked off in this window:\s*(.*)$/))&&l.length&&(l[l.length-1].ticked=y[1].split(/;\s+(?=#\d+ )/).map(w=>{let S=w.match(/^#(\d+)\s+(.*)$/);return S?{n:Number(S[1]),text:S[2]}:null}).filter(Boolean));continue}if(c==="repo"){if(y=m.match(/^###\s+(.*)$/)){d={id:null,title:y[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(p(m)&&!d.id){d.id=p(m);continue}if(!d.line&&(y=m.match(/^`([^`]+)`$/))){d.line=y[1];continue}if(y=m.match(/^Resume:\s+`(.*)`$/)){d.resume=y[1],f=null;continue}if(y=m.match(/^From (.+?)’s feed \(read-only\) · ([^<>→]{1,60}?) → <([^>\s]+)>$/)){d.agent={display:y[1],url:null,reply:{label:y[2],url:y[3]}},f=null;continue}if(y=m.match(/^From (.+?)’s feed \(read-only\)(?:\. Reply: <(https:\/\/[^>\s]+)>)?$/)){d.agent={display:y[1],url:y[2]||null},f=null;continue}if(y=m.match(/^\*\*([A-Za-z ]+):\*\*\s*(.*)$/)){let w=y[1].toLowerCase(),S=y[2].trim();f=null,w==="done"?S?d.done.push(S):f=d.done:w==="stopped at"?d.stoppedAt=S:w==="latest"?(d.stoppedAt=S,d.agent=d.agent||{display:null,url:null}):w==="next"?(d.next=S,f=d.nextSteps):w==="open questions"?f=d.questions:w==="needs you"?d.needsYou=S:w==="since then"&&(d.lastReply=S);continue}if((y=m.match(/^-\s+(.*)$/))&&f){f.push(y[1]);continue}m.trim()}}}return{meta:s,summary:r,needsYou:i,stillOpen:l,repos:a}}async function $r(n,e={}){let t=e.now!=null?e.now:Date.now(),s=e.cfg||ye,o=ja(s),r=Fa(e.since,{now:t,tz:o,lastAt:e.lastAt==null?null:e.lastAt,to:e.to||null,workdays:Iu(s)}),i=Op(n,{...e,now:t,cfg:s,tz:o,window:r,lengthOverride:e.length}),a=await Dp(i,{summarizer:e.summarizer||null,cfg:s,given:e.given||null}),l=jp(i,a,{now:t});return{recap:i,summary:a,markdown:l,fileName:Pp(t,o)}}function Ua(n,e={}){let t=e.now!=null?e.now:Date.now(),s=e.cfg||ye,o=ja(s),r=Iu(s),i=e.lastAt==null?null:e.lastAt,a=Fa(e.since,{now:t,tz:o,lastAt:i,to:e.to||null,workdays:r}),l=Mu(n,{...e,from:a.from,to:a.to,now:t,cfg:s}),c=Lu(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:Tp({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 xp,sk,Gr,Da,La,Cu,ok,Pa,bp,_p,_u,vp,ck,To,bn,Np,Lp,Ao,fk,Le,Ha=z(()=>{ve();Ms();Br();xp=1,sk=/^\d{4}-\d{2}-\d{2}T\d{4}(?:-\d+)?$/,Gr=36e5,Da=24*Gr,La={sun:"Sunday",mon:"Monday",tue:"Tuesday",wed:"Wednesday",thu:"Thursday",fri:"Friday",sat:"Saturday"},Cu={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}};ok=(n,e)=>{let t=Ut(n,e);return Ht(t.y,t.mo,t.d,0,0,e)};Pa=(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)/,"");bp=(n,e,t)=>!isNaN(n)&&n>=e&&n<=t;_p=n=>Math.max(P(n.s.lastActivityAt)||0,P(n.s.endedAt)||0,n.latest?P(n.latest.createdAt):0,P(n.s.startedAt)||0);_u=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)})},vp=n=>String(n||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim(),ck=(n,e)=>{let t=vp(n),s=vp(e);return!!t&&!!s&&(t.includes(s)||s.includes(t))},To=n=>String(n||"").replace(/^YOU:\s*/i,""),bn=n=>String(n||"").replace(/[.!?…]+$/,"");Np='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 "- ".';Lp=uh,Ao=n=>"“"+n+"”";fk=["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"],Le=n=>String(n??"").replace(/\s*\n\s*/g," ").trim()});import ct from"node:fs";import be from"node:path";function Ru(n){let e=be.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 gk(n){return ea(n||"no-repo",60)||"no-repo"}function Ba(n){try{return ct.accessSync(n),!0}catch{return!1}}function ut(n,{items:e=!0}={}){let t=mh(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 Pu(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":" ")+"] "+U(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(`
102
- `)}var hk,pk,mk,Yn,Ka=z(()=>{ve();wn();hk=1e4,pk=4e3,mk=n=>{try{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,n)}catch{}};Yn=class{constructor(e,t="unknown",{now:s}={}){this.root=e,this.machine=t,this.now=s||(()=>Date.now()),this.dir=be.join(e,"lists"),this.lockDir=be.join(e,".locks",String(t).replace(/[^\w.-]/g,"_"))}files(){let e=[],t=[];try{t=ct.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=ct.readdirSync(be.join(this.dir,s)).filter(r=>r.endsWith(".md")&&!r.startsWith("."))}catch{}for(let r of o)e.push(be.join(this.dir,s,r))}return e}refOf(e){return be.basename(be.dirname(e))+"/"+be.basename(e,".md")}fileOf(e){let[t,s]=String(e).split("/");return be.join(this.dir,t,s+".md")}read(e){let t;try{t=ct.readFileSync(e,"utf8")}catch{return null}let s=Vc(t);return to(s),this.decorate(s,e)}decorate(e,t){return e.file=t,e.ref=this.refOf(t),e.slug=be.basename(t,".md"),e.repoDir=be.basename(be.dirname(t)),e}healConflicts(){let e=0;for(let t of this.files()){let s=Ru(t);if(!s)continue;let o=be.join(be.dirname(t),s);if(Ba(o))try{this.update(this.refOf(o),i=>{let a=Vc(ct.readFileSync(t,"utf8")),l=ph(i,a);i.meta=l.meta,i.items=l.items,i.segs=l.segs},{touch:!1});let r=be.join(this.dir,".merged",be.basename(be.dirname(t)));ct.mkdirSync(r,{recursive:!0}),ct.renameSync(t,be.join(r,be.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(Ru(r)&&Ba(be.join(be.dirname(r),Ru(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)=>(P(i.meta.updated)||0)-(P(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)&&Ba(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=ea(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){ct.mkdirSync(this.lockDir,{recursive:!0});let t=be.join(this.lockDir,e.replace(/[^\w.-]+/g,"_")+".lock"),s=Date.now();for(;;){try{return ct.writeFileSync(t,String(process.pid),{flag:"wx"}),()=>{try{ct.unlinkSync(t)}catch{}}}catch(o){if(o.code!=="EEXIST")throw o}try{if(Date.now()-ct.statSync(t).mtimeMs>hk){ct.unlinkSync(t);continue}}catch{continue}if(Date.now()-s>pk)throw Object.assign(new Error("the list is busy (locked by another writer); try again"),{code:"list_busy"});mk(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(!Ba(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())),$n(o,Zc(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=gs(e)||"Checklist",c=hh({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=be.join(this.dir,gk(s));ct.mkdirSync(u,{recursive:!0});let d=ea(l);for(let f=1;f<200;f++){let p=be.join(u,(f===1?d:d+"-"+f)+".md");try{return ct.writeFileSync(p,Zc(c),{flag:"wx"}),this.read(p)}catch(h){if(h.code!=="EEXIST")throw h}}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:U(s,500),done:!1,at:G(this.now())},i.items.push(o),to(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=U(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=gs(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 Up(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 Hp=z(()=>{});import yk from"node:fs";import Bp from"node:path";import{fileURLToPath as wk}from"node:url";function Kp(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 Ye(n,e){let t=Kp(n),s=Kp(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 ju(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:Up(r.join(`
103
+ `}function od(n){let{data:e,body:t}=$e(n),s={generatedAt:e.generated_at?K(e.generated_at):null,window:{from:e.window_from?K(e.window_from):null,to:e.window_to?K(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(`
104
+ `),r=[],i=[],a=[],l=[],c=null,u=null,d=null,f=null,p=h=>{let m=h.match(/<!--\s*session:\s*([^\s>]+)\s*-->/);return m?m[1]:null};for(let h of o){let m=h.replace(/\s+$/,""),y;if(y=m.match(/^#\s+(.*)$/)){s.title=y[1];continue}if(y=m.match(/^##\s+(.*?)(?:\s+\((\d+)\))?$/)){d=null,f=null,/^summary$/i.test(y[1])?c="summary":/^needs you$/i.test(y[1])?c="needs":/^still open$/i.test(y[1])?c="open":(c="repo",u={label:y[1],sessions:[]},a.push(u));continue}if(c==="summary"){(y=m.match(/^-\s+(.*)$/))&&r.push(y[1]);continue}if(c==="needs"){(y=m.match(/^-\s+\*\*(.+?)\*\*\s+\((.*?)\)\s+—\s+(.*?)(?:\s*<!--.*-->)?$/))&&i.push({id:p(m),title:y[1],repo:y[2].split(" · ")[0],needsYou:y[3]});continue}if(c==="open"){if(y=m.match(/^-\s+\*\*(.+?)\*\*\s+\((.*?) · (\d+)\/(\d+) done\)\s+—\s+next:\s*(.*?)(?:\s*<!--\s*list:\s*(\S+)\s*-->)?$/)){let S=b=>b.split(/;\s+(?=#\d+ )/).map(w=>{let A=w.match(/^#(\d+)\s+(.*)$/);return A?{n:Number(A[1]),text:A[2]}:null}).filter(Boolean);l.push({name:y[1],repo:y[2],done:Number(y[3]),total:Number(y[4]),next:S(y[5]),ref:y[6]||null,ticked:[]})}else(y=m.match(/^\s+-\s+Ticked off in this window:\s*(.*)$/))&&l.length&&(l[l.length-1].ticked=y[1].split(/;\s+(?=#\d+ )/).map(S=>{let b=S.match(/^#(\d+)\s+(.*)$/);return b?{n:Number(b[1]),text:b[2]}:null}).filter(Boolean));continue}if(c==="repo"){if(y=m.match(/^###\s+(.*)$/)){d={id:null,title:y[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(p(m)&&!d.id){d.id=p(m);continue}if(!d.line&&(y=m.match(/^`([^`]+)`$/))){d.line=y[1];continue}if(y=m.match(/^Resume:\s+`(.*)`$/)){d.resume=y[1],f=null;continue}if(y=m.match(/^From (.+?)’s feed \(read-only\) · ([^<>→]{1,60}?) → <([^>\s]+)>$/)){d.agent={display:y[1],url:null,reply:{label:y[2],url:y[3]}},f=null;continue}if(y=m.match(/^From (.+?)’s feed \(read-only\)(?:\. Reply: <(https:\/\/[^>\s]+)>)?$/)){d.agent={display:y[1],url:y[2]||null},f=null;continue}if(y=m.match(/^\*\*([A-Za-z ]+):\*\*\s*(.*)$/)){let S=y[1].toLowerCase(),b=y[2].trim();f=null,S==="done"?b?d.done.push(b):f=d.done:S==="stopped at"?d.stoppedAt=b:S==="latest"?(d.stoppedAt=b,d.agent=d.agent||{display:null,url:null}):S==="next"?(d.next=b,f=d.nextSteps):S==="open questions"?f=d.questions:S==="needs you"?d.needsYou=b:S==="since then"&&(d.lastReply=b);continue}if((y=m.match(/^-\s+(.*)$/))&&f){f.push(y[1]);continue}m.trim()}}}return{meta:s,summary:r,needsYou:i,stillOpen:l,repos:a}}async function ti(n,e={}){let t=e.now!=null?e.now:Date.now(),s=e.cfg||Se,o=Qa(s),r=el(e.since,{now:t,tz:o,lastAt:e.lastAt==null?null:e.lastAt,to:e.to||null,workdays:ed(s)}),i=ym(n,{...e,now:t,cfg:s,tz:o,window:r,lengthOverride:e.length}),a=await vm(i,{summarizer:e.summarizer||null,cfg:s,given:e.given||null}),l=Tm(i,a,{now:t});return{recap:i,summary:a,markdown:l,fileName:Am(t,o)}}function tl(n,e={}){let t=e.now!=null?e.now:Date.now(),s=e.cfg||Se,o=Qa(s),r=ed(s),i=e.lastAt==null?null:e.lastAt,a=el(e.since,{now:t,tz:o,lastAt:i,to:e.to||null,workdays:r}),l=nd(n,{...e,from:a.from,to:a.to,now:t,cfg:s}),c=sd(l),u=[...new Set(n.map(d=>d.session&&d.session.machine).filter(Boolean))].sort();return{tz:o,now:K(t),window:{from:K(a.from),to:K(a.to),spec:a.spec,kind:a.kind,label:a.label,fallback:a.fallback||null},windows:hm({now:t,tz:o,lastAt:i,workdays:r}),lastAt:i!=null?K(i):null,repos:c.repos,machines:c.machines,allMachines:u,sessions:l.length}}var dm,Wk,ei,Va,qa,Xu,zk,Xa,cm,pm,Vu,um,Xk,Do,Tn,Sm,bm,Lo,tb,Pe,nl=J(()=>{Ae();Hs();Xr();dm=1,Wk=/^\d{4}-\d{2}-\d{2}T\d{4}(?:-\d+)?$/,ei=36e5,Va=24*ei,qa={sun:"Sunday",mon:"Monday",tue:"Tuesday",wed:"Wednesday",thu:"Thursday",fri:"Friday",sat:"Saturday"},Xu={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}};zk=(n,e)=>{let t=$t(n,e);return Yt(t.y,t.mo,t.d,0,0,e)};Xa=(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)/,"");cm=(n,e,t)=>!isNaN(n)&&n>=e&&n<=t;pm=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);Vu=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)})},um=n=>String(n||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim(),Xk=(n,e)=>{let t=um(n),s=um(e);return!!t&&!!s&&(t.includes(s)||s.includes(t))},Do=n=>String(n||"").replace(/^YOU:\s*/i,""),Tn=n=>String(n||"").replace(/[.!?…]+$/,"");Sm='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 "- ".';bm=Gh,Lo=n=>"“"+n+"”";tb=["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"],Pe=n=>String(n??"").replace(/\s*\n\s*/g," ").trim()});import pt from"node:fs";import xe from"node:path";function rd(n){let e=xe.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 rb(n){return da(n||"no-repo",60)||"no-repo"}function sl(n){try{return pt.accessSync(n),!0}catch{return!1}}function mt(n,{items:e=!0}={}){let t=zh(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 id(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(`
105
+ `)}var nb,sb,ob,Vn,ol=J(()=>{Ae();vn();nb=1e4,sb=4e3,ob=n=>{try{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,n)}catch{}};Vn=class{constructor(e,t="unknown",{now:s}={}){this.root=e,this.machine=t,this.now=s||(()=>Date.now()),this.dir=xe.join(e,"lists"),this.lockDir=xe.join(e,".locks",String(t).replace(/[^\w.-]/g,"_"))}files(){let e=[],t=[];try{t=pt.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=pt.readdirSync(xe.join(this.dir,s)).filter(r=>r.endsWith(".md")&&!r.startsWith("."))}catch{}for(let r of o)e.push(xe.join(this.dir,s,r))}return e}refOf(e){return xe.basename(xe.dirname(e))+"/"+xe.basename(e,".md")}fileOf(e){let[t,s]=String(e).split("/");return xe.join(this.dir,t,s+".md")}read(e){let t;try{t=pt.readFileSync(e,"utf8")}catch{return null}let s=pu(t);return uo(s),this.decorate(s,e)}decorate(e,t){return e.file=t,e.ref=this.refOf(t),e.slug=xe.basename(t,".md"),e.repoDir=xe.basename(xe.dirname(t)),e}healConflicts(){let e=0;for(let t of this.files()){let s=rd(t);if(!s)continue;let o=xe.join(xe.dirname(t),s);if(sl(o))try{this.update(this.refOf(o),i=>{let a=pu(pt.readFileSync(t,"utf8")),l=Wh(i,a);i.meta=l.meta,i.items=l.items,i.segs=l.segs},{touch:!1});let r=xe.join(this.dir,".merged",xe.basename(xe.dirname(t)));pt.mkdirSync(r,{recursive:!0}),pt.renameSync(t,xe.join(r,xe.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(rd(r)&&sl(xe.join(xe.dirname(r),rd(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)&&sl(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=da(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){pt.mkdirSync(this.lockDir,{recursive:!0});let t=xe.join(this.lockDir,e.replace(/[^\w.-]+/g,"_")+".lock"),s=Date.now();for(;;){try{return pt.writeFileSync(t,String(process.pid),{flag:"wx"}),()=>{try{pt.unlinkSync(t)}catch{}}}catch(o){if(o.code!=="EEXIST")throw o}try{if(Date.now()-pt.statSync(t).mtimeMs>nb){pt.unlinkSync(t);continue}}catch{continue}if(Date.now()-s>sb)throw Object.assign(new Error("the list is busy (locked by another writer); try again"),{code:"list_busy"});ob(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(!sl(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=K(this.now())),qn(o,mu(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=bs(e)||"Checklist",c=Yh({name:l,items:t,repo:s,repoKey:o,sourceSession:r,sourceMachine:i||(r?this.machine:null),sourceTitle:a,now:K(this.now())});if(!c.items.length)throw Object.assign(new Error("a list needs at least one item"),{code:"empty_list"});let u=xe.join(this.dir,rb(s));pt.mkdirSync(u,{recursive:!0});let d=da(l);for(let f=1;f<200;f++){let p=xe.join(u,(f===1?d:d+"-"+f)+".md");try{return pt.writeFileSync(p,mu(c),{flag:"wx"}),this.read(p)}catch(h){if(h.code!=="EEXIST")throw h}}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=K(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:K(this.now())},i.items.push(o),uo(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=K(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=bs(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 Em(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 Om=J(()=>{});import ib from"node:fs";import Cm from"node:path";import{fileURLToPath as ab}from"node:url";function Nm(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 Je(n,e){let t=Nm(n),s=Nm(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 ad(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:Em(r.join(`
103
106
  `))});return e.map(({lines:r,...i})=>({...i,sections:i.sections.map(o),markdown:r.join(`
104
- `).trim()}))}function _o(n=Sk){try{return ju(yk.readFileSync(n,"utf8"))}catch{return[]}}function Eo(n,e,t){return n.filter(s=>(!e||Ye(s.version,e)>0)&&(!t||Ye(s.version,t)<=0))}function Gp(n,e,t){return n.map(s=>({...s,isNew:(!t||Ye(s.version,t)<=0)&&(!e||Ye(s.version,e)>0)}))}function Ga(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(`
107
+ `).trim()}))}function Ro(n=lb){try{return ad(ib.readFileSync(n,"utf8"))}catch{return[]}}function Po(n,e,t){return n.filter(s=>(!e||Je(s.version,e)>0)&&(!t||Je(s.version,t)<=0))}function Im(n,e,t){return n.map(s=>({...s,isNew:(!t||Je(s.version,t)<=0)&&(!e||Je(s.version,e)>0)}))}function rl(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(`
105
108
  `))}o.push("")}return o.join(`
106
- `)}var Sk,$a=z(()=>{Hp();Sk=Bp.resolve(Bp.dirname(wk(import.meta.url)),"..","CHANGELOG.md")});var je,J,Xe,$p,Yr,Ds,Fu,Yp,Rs,Uu,Wp,zp,Jp,wt,qp,St=z(()=>{je="Leftoff",J="leftoff",Xe="OneSillyApp/leftoff",$p=["joelash/checkpoints","joelash/leftoff"],Yr="leftoff-hook",Ds="checkpoints-hook",Fu="dev.leftoff.daemon",Yp="dev.checkpoints.daemon",Rs="leftoff.service",Uu="checkpoints.service",Wp="checkpoints",zp="leftoff",Jp="checkpoints",wt="leftoff.localhost",qp="checkpoints.localhost"});var im={};Jt(im,{CHECK_EVERY_MS:()=>Gu,DEFAULT_SOURCE:()=>Wr,GITHUB_REPO:()=>Xe,NPM_PACKAGE:()=>Qe,PACKAGE_DIRS:()=>zr,REPO_DIR:()=>Wn,STAMP_FILE:()=>Ku,checkForUpdate:()=>qr,defaultCacheDir:()=>Wu,fetchRemoteFiles:()=>rm,installInfo:()=>Jr,isNpmBuild:()=>em,isNpxCacheDir:()=>Bu,lsRemote:()=>om,normalizeSource:()=>Oo,npmLatest:()=>nm,npmPackageUrl:()=>Wa,npmRegistry:()=>Ya,npmUpgradeCommand:()=>Yu,packageManagerOf:()=>tm,readPkg:()=>Qp,readStamp:()=>Zp,registryPath:()=>Xp,renamedSource:()=>Ps,runGit:()=>kt,sshFallback:()=>sm,writeStamp:()=>$u});import Co from"node:fs";import kk from"node:os";import vn from"node:path";import{execFile as bk}from"node:child_process";import{fileURLToPath as vk}from"node:url";function Oo(n){let e=String(n||"").trim();if(!e)return Wr;let t=e.match(/^(?:github:)?([\w.-]+)\/([\w.-]+?)(?:\.git)?(?:#.*)?$/);return t&&!e.includes("://")&&!e.startsWith("/")&&!e.startsWith(".")?Ps("https://github.com/"+t[1]+"/"+t[2]+".git"):Ps(e)}function Ps(n){let e=String(n||"");for(let t of $p){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]+Xe+".git"}return e}function kt(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"};bk(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 Zp(n=Wn){try{return JSON.parse(Co.readFileSync(vn.join(n,Ku),"utf8"))}catch{return null}}function $u(n,e=Wn){let t={version:n.version||q,sha:n.sha||null,source:n.source||null,at:n.at||new Date().toISOString()};return Co.writeFileSync(vn.join(e,Ku),JSON.stringify(t,null,2)+`
107
- `),t}function Bu(n){return/[\\/]_npx[\\/]/.test(n)||/[\\/]\.npm[\\/]/.test(n)}function Xp(n=Qe){return n.startsWith("@")?"@"+n.slice(1).split("/").map(encodeURIComponent).join("%2f"):encodeURIComponent(n)}function Wa(n=process.env,e=Qe){return Ya(n)+"/"+Xp(e)}function Qp(n=Wn){try{return JSON.parse(Co.readFileSync(vn.join(n,"package.json"),"utf8"))}catch{return null}}function em(n=Wn){let e=Qp(n);return!!(e&&e.leftoffBuild==="npm")}function tm(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 Yu(n,e=Qe+"@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 Ya(n=process.env){return String(n.LEFTOFF_NPM_REGISTRY||n.npm_config_registry||"https://registry.npmjs.org").replace(/\/+$/,"")}async function nm({env:n=process.env,fetchImpl:e=fetch,timeoutMs:t=15e3}={}){let s=Wa(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:Qe+" isn’t on "+Ya(n)+" (404)"};if(!o.ok)return{ok:!1,version:null,error:Ya(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 Jr({repoDir:n=Wn,env:e=process.env,globalRoot:t}={}){let s={version:q,sha:null,kind:"local",source:null,repoDir:n};if(Co.existsSync(vn.join(n,".git"))){s.kind="clone";let r=await kt(["rev-parse","HEAD"],{cwd:n,env:e});r.code===0&&(s.sha=r.stdout.trim());let i=await kt(["remote","get-url","origin"],{cwd:n,env:e});return s.source=i.code===0?i.stdout.trim():null,s}if(em(n))return s.kind="npm",s.source="npm:"+Qe,s.pm=tm(n,e),s.npx=Bu(n),s;let o=Zp(n);return o&&(s.sha=o.sha||null,s.source=o.source||null),Bu(n)?s.kind="npx":(t?n.startsWith(t):/[\\/]node_modules[\\/](checkpoints|leftoff|@leftoff[\\/]cli)$/.test(n))&&(s.kind="global"),s}function sm(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 Vp(n,{env:e,timeoutMs:t}){let s=await kt(["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:Hu(s.stderr)||"git ls-remote exited "+s.code,source:n}}async function om(n,{env:e=process.env,timeoutMs:t=15e3}={}){let s=Oo(n),o=await Vp(s,{env:e,timeoutMs:t});if(o.ok)return o;let r=sm(s);if(!r)return o;let i=await Vp(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 rm(n,{cacheDir:e=Wu(),env:t=process.env,timeoutMs:s=3e4}={}){try{if(Co.mkdirSync(e,{recursive:!0}),!Co.existsSync(vn.join(e,"HEAD"))){let c=await kt(["init","--bare","-q",e],{env:t});if(c.code!==0)return{ok:!1,error:Hu(c.stderr)}}let o=await kt(["fetch","-q","--depth=1",Oo(n),"HEAD"],{cwd:e,env:t,timeoutMs:s});if(o.code!==0)return{ok:!1,error:Hu(o.stderr)||"git fetch exited "+o.code};let r=(await kt(["rev-parse","FETCH_HEAD"],{cwd:e,env:t})).stdout.trim()||null,i=await kt(["show","FETCH_HEAD:CHANGELOG.md"],{cwd:e,env:t}),a=await kt(["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 Wu(n=process.env){return vn.join(n.XDG_CACHE_HOME||vn.join(kk.homedir(),".cache"),"checkpoints","remote.git")}async function qr({info:n,source:e,env:t=process.env,cacheDir:s,now:o=Date.now(),fetchFiles:r=!0}={}){let i=n||await Jr({env:t});if(i.kind==="npm"){let d={checkedAt:new Date(o).toISOString(),source:"npm:"+Qe,current:{version:i.version,sha:null},remote:{sha:null,version:null},available:!1,incoming:[],error:null},f=await nm({env:t});return f.ok?(d.remote.version=f.version,d.available=Ye(f.version,i.version)===1,d):(d.error=f.error,d)}let a=Oo(e||i.source||Wr),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 om(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 rm(l.source,{cacheDir:s||Wu(t),env:t});d.ok?(l.remote.version=d.version,d.changelog&&(l.incoming=Eo(ju(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?Ye(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 Wn,Wr,Ku,Gu,Hu,Qe,zr,Vr=z(()=>{St();ke();$a();Wn=vn.resolve(vn.dirname(vk(import.meta.url)),".."),Wr="https://github.com/"+Xe+".git",Ku=".install.json",Gu=24*3600*1e3;Hu=n=>String(n||"").trim().split(`
108
- `).filter(Boolean).pop()||"";Qe="@leftoff/cli",zr=[Qe,"leftoff","checkpoints"]});import cm from"node:crypto";function dm(){return"rq_"+cm.randomBytes(9).toString("base64url")}function Zr(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 cm.createHash("sha256").update(String(n||"")+`
109
- `+t(e||{})).digest("hex").slice(0,16)}function Ak(n,e={}){let t=e&&typeof e=="object"?e:{};switch(n){case"Bash":return{verb:"Run",detail:We(t.command,400),note:t.description?We(t.description,160):null};case"Edit":case"MultiEdit":return{verb:"Edit",detail:We(t.file_path,300),note:null};case"Write":return{verb:"Write",detail:We(t.file_path,300),note:t.content?We(String(t.content).split(`
110
- `).length+" lines",40):null};case"NotebookEdit":return{verb:"Edit notebook",detail:We(t.notebook_path,300),note:null};case"Read":return{verb:"Read",detail:We(t.file_path,300),note:null};case"WebFetch":return{verb:"Fetch",detail:We(t.url,300),note:null};case"WebSearch":return{verb:"Search the web for",detail:We(t.query,300),note:null};default:{let s=Object.entries(t).find(([,o])=>typeof o=="string");return{verb:"Use "+n,detail:s?We(s[1],300):"",note:null}}}}function Tk(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 _k(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 Ek(n,e={}){let t=r=>{let i=new Error(r);throw i.status=400,i.code="bad_answer",i},s=e.action;am.includes(s)||t("action is one of "+am.join(", "));let o=e.message?We(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:_k(n.tool,n.suggestions,n.input)}:{behavior:"allow"}}function fm(n){return n?{hookSpecificOutput:{hookEventName:"PermissionRequest",decision:n}}:{}}function Xr(n,e={}){if(e.action==="deny")return"Denied"+(e.message?" (“"+We(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])=>"“"+We(s,80)+"” → "+We(Array.isArray(o)?o.join(", "):o,120)).join("; ")}return e.action==="reply"?"Replied “"+We(e.text,200)+"”":String(e.action||"")}function zu({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 hm({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 am,xk,um,za,lm,We,Ja,qa,Qr=z(()=>{am=["allow","allow_session","deny","answer"],xk=9.5*60*1e3,um=45*1e3,za=25*1e3,lm=4e3;We=(n,e)=>{let t=String(n??"").replace(/\s+/g," ").trim();return t.length>e?t.slice(0,e-1)+"…":t};Ja=class{constructor({now:e=()=>Date.now(),timeoutMs:t=xk,noListenerMs:s=um,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=Zr(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:dm(),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"?Tk(s):null,describe:u==="permission"?Ak(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=za,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,za,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=Ek(o,t);o.state="answered",o.decision=r,o.answer={action:t.action,message:t.message?We(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?Xr(e,e.answer):null,capture:[...e.captures].includes("mod")?"mod":"hooks"}:null}},qa=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,`
111
- `).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>lm&&s("the reply is over "+lm+" characters"),t}listening(e){return(this.waiters.get(e)||new Set).size>0||this.now()-(this.lastPoll.get(e)||0)<um}put(e,t,s={}){let o={id:dm().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=za,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,za));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 pm(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)),[{...r,type:"session.started",data:{...i,source:e.source||"startup",...l}}]}case"UserPromptSubmit":return[{...r,type:"activity",data:{...i,kind:"prompt",prompt:U(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?Zr(e.tool_name,e.tool_input):null}}];case"Stop":return[{...r,type:"turn_completed",data:{...i,lastAssistantPreview:U(e.last_assistant_message||"",280),question:Wc(e.last_assistant_message||""),...(()=>{let l=fh(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:U(e.message||"",200)}}];case"SessionEnd":return[{...r,type:"session.ended",data:{...i,reason:e.reason||"other"}}];default:return[]}}var mm=z(()=>{ve();Qr()});import{execFile as Ck}from"node:child_process";import ei from"node:fs";import js from"node:path";function gm(n,e){return new Promise(t=>{Ck("git",["-C",n,...e],{timeout:1500},(s,o)=>t(s?null:String(o).trim()))})}async function ym(n){if(!n||!ei.existsSync(n))return null;let e=await gm(n,["rev-parse","--show-toplevel"]);if(!e)return null;let t=await gm(n,["symbolic-ref","--short","-q","HEAD"]);return{root:e,branch:t||null,name:js.basename(e)}}function wm(n,e){if(!n)return!1;if((e.ignoreRepos||[]).includes(js.basename(n))||(e.ignoreRepos||[]).includes(n))return!0;try{return ei.existsSync(js.join(n,".leftoff-ignore"))||ei.existsSync(js.join(n,".checkpoints-ignore"))}catch{return!1}}function Sm(n){if(!n)return null;let e=js.resolve(String(n));try{if(!ei.statSync(e).isDirectory())return null}catch{return null}for(let t=0;t<64;t++){try{if(ei.existsSync(js.join(e,".git")))return e}catch{}let s=js.dirname(e);if(s===e)return null;e=s}return null}var km=z(()=>{});import ze from"node:fs";import Ok from"node:os";import dt from"node:path";import Am from"node:crypto";function nl(n,{multiline:e=!1}={}){let t=String(n).replace(/\r\n?/g,`
112
- `).replace(Mk,"");return t=e?t.replace(/[\u0000-\u0008\u000B-\u001F\u007F]/g,""):t.replace(/[\u0000-\u001F\u007F]+/g," "),t.replace(/[ \t]+\n/g,`
109
+ `)}var lb,il=J(()=>{Om();lb=Cm.resolve(Cm.dirname(ab(import.meta.url)),"..","CHANGELOG.md")});var He,q,tt,Mm,ni,Gs,ld,Lm,Ks,cd,Dm,Rm,Pm,At,jm,Tt=J(()=>{He="Leftoff",q="leftoff",tt="OneSillyApp/leftoff",Mm=["joelash/checkpoints","joelash/leftoff"],ni="leftoff-hook",Gs="checkpoints-hook",ld="dev.leftoff.daemon",Lm="dev.checkpoints.daemon",Ks="leftoff.service",cd="checkpoints.service",Dm="checkpoints",Rm="leftoff",Pm="checkpoints",At="leftoff.localhost",jm="checkpoints.localhost"});var Jm={};Qt(Jm,{CHECK_EVERY_MS:()=>hd,DEFAULT_SOURCE:()=>si,GITHUB_REPO:()=>tt,NPM_PACKAGE:()=>nt,PACKAGE_DIRS:()=>oi,REPO_DIR:()=>Zn,STAMP_FILE:()=>fd,checkForUpdate:()=>ii,defaultCacheDir:()=>gd,fetchRemoteFiles:()=>zm,installInfo:()=>ri,isNpmBuild:()=>Gm,isNpxCacheDir:()=>dd,lsRemote:()=>Wm,normalizeSource:()=>Fo,npmLatest:()=>$m,npmPackageUrl:()=>ll,npmRegistry:()=>al,npmUpgradeCommand:()=>md,packageManagerOf:()=>Km,readPkg:()=>Bm,readStamp:()=>Um,registryPath:()=>Hm,renamedSource:()=>$s,runGit:()=>_t,sshFallback:()=>Ym,writeStamp:()=>pd});import jo from"node:fs";import cb from"node:os";import _n from"node:path";import{execFile as ub}from"node:child_process";import{fileURLToPath as db}from"node:url";function Fo(n){let e=String(n||"").trim();if(!e)return si;let t=e.match(/^(?:github:)?([\w.-]+)\/([\w.-]+?)(?:\.git)?(?:#.*)?$/);return t&&!e.includes("://")&&!e.startsWith("/")&&!e.startsWith(".")?$s("https://github.com/"+t[1]+"/"+t[2]+".git"):$s(e)}function $s(n){let e=String(n||"");for(let t of Mm){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]+tt+".git"}return e}function _t(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"};ub(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 Um(n=Zn){try{return JSON.parse(jo.readFileSync(_n.join(n,fd),"utf8"))}catch{return null}}function pd(n,e=Zn){let t={version:n.version||V,sha:n.sha||null,source:n.source||null,at:n.at||new Date().toISOString()};return jo.writeFileSync(_n.join(e,fd),JSON.stringify(t,null,2)+`
110
+ `),t}function dd(n){return/[\\/]_npx[\\/]/.test(n)||/[\\/]\.npm[\\/]/.test(n)}function Hm(n=nt){return n.startsWith("@")?"@"+n.slice(1).split("/").map(encodeURIComponent).join("%2f"):encodeURIComponent(n)}function ll(n=process.env,e=nt){return al(n)+"/"+Hm(e)}function Bm(n=Zn){try{return JSON.parse(jo.readFileSync(_n.join(n,"package.json"),"utf8"))}catch{return null}}function Gm(n=Zn){let e=Bm(n);return!!(e&&e.leftoffBuild==="npm")}function Km(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 md(n,e=nt+"@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 al(n=process.env){return String(n.LEFTOFF_NPM_REGISTRY||n.npm_config_registry||"https://registry.npmjs.org").replace(/\/+$/,"")}async function $m({env:n=process.env,fetchImpl:e=fetch,timeoutMs:t=15e3}={}){let s=ll(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:nt+" isn’t on "+al(n)+" (404)"};if(!o.ok)return{ok:!1,version:null,error:al(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 ri({repoDir:n=Zn,env:e=process.env,globalRoot:t}={}){let s={version:V,sha:null,kind:"local",source:null,repoDir:n};if(jo.existsSync(_n.join(n,".git"))){s.kind="clone";let r=await _t(["rev-parse","HEAD"],{cwd:n,env:e});r.code===0&&(s.sha=r.stdout.trim());let i=await _t(["remote","get-url","origin"],{cwd:n,env:e});return s.source=i.code===0?i.stdout.trim():null,s}if(Gm(n))return s.kind="npm",s.source="npm:"+nt,s.pm=Km(n,e),s.npx=dd(n),s;let o=Um(n);return o&&(s.sha=o.sha||null,s.source=o.source||null),dd(n)?s.kind="npx":(t?n.startsWith(t):/[\\/]node_modules[\\/](checkpoints|leftoff|@leftoff[\\/]cli)$/.test(n))&&(s.kind="global"),s}function Ym(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 Fm(n,{env:e,timeoutMs:t}){let s=await _t(["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:ud(s.stderr)||"git ls-remote exited "+s.code,source:n}}async function Wm(n,{env:e=process.env,timeoutMs:t=15e3}={}){let s=Fo(n),o=await Fm(s,{env:e,timeoutMs:t});if(o.ok)return o;let r=Ym(s);if(!r)return o;let i=await Fm(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 zm(n,{cacheDir:e=gd(),env:t=process.env,timeoutMs:s=3e4}={}){try{if(jo.mkdirSync(e,{recursive:!0}),!jo.existsSync(_n.join(e,"HEAD"))){let c=await _t(["init","--bare","-q",e],{env:t});if(c.code!==0)return{ok:!1,error:ud(c.stderr)}}let o=await _t(["fetch","-q","--depth=1",Fo(n),"HEAD"],{cwd:e,env:t,timeoutMs:s});if(o.code!==0)return{ok:!1,error:ud(o.stderr)||"git fetch exited "+o.code};let r=(await _t(["rev-parse","FETCH_HEAD"],{cwd:e,env:t})).stdout.trim()||null,i=await _t(["show","FETCH_HEAD:CHANGELOG.md"],{cwd:e,env:t}),a=await _t(["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 gd(n=process.env){return _n.join(n.XDG_CACHE_HOME||_n.join(cb.homedir(),".cache"),"checkpoints","remote.git")}async function ii({info:n,source:e,env:t=process.env,cacheDir:s,now:o=Date.now(),fetchFiles:r=!0}={}){let i=n||await ri({env:t});if(i.kind==="npm"){let d={checkedAt:new Date(o).toISOString(),source:"npm:"+nt,current:{version:i.version,sha:null},remote:{sha:null,version:null},available:!1,incoming:[],error:null},f=await $m({env:t});return f.ok?(d.remote.version=f.version,d.available=Je(f.version,i.version)===1,d):(d.error=f.error,d)}let a=Fo(e||i.source||si),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 Wm(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 zm(l.source,{cacheDir:s||gd(t),env:t});d.ok?(l.remote.version=d.version,d.changelog&&(l.incoming=Po(ad(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?Je(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 Zn,si,fd,hd,ud,nt,oi,ai=J(()=>{Tt();ve();il();Zn=_n.resolve(_n.dirname(db(import.meta.url)),".."),si="https://github.com/"+tt+".git",fd=".install.json",hd=24*3600*1e3;ud=n=>String(n||"").trim().split(`
111
+ `).filter(Boolean).pop()||"";nt="@leftoff/cli",oi=[nt,"leftoff","checkpoints"]});import Zm from"node:crypto";function Qm(){return"rq_"+Zm.randomBytes(9).toString("base64url")}function li(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 Zm.createHash("sha256").update(String(n||"")+`
112
+ `+t(e||{})).digest("hex").slice(0,16)}function hb(n,e={}){let t=e&&typeof e=="object"?e:{};switch(n){case"Bash":return{verb:"Run",detail:qe(t.command,400),note:t.description?qe(t.description,160):null};case"Edit":case"MultiEdit":return{verb:"Edit",detail:qe(t.file_path,300),note:null};case"Write":return{verb:"Write",detail:qe(t.file_path,300),note:t.content?qe(String(t.content).split(`
113
+ `).length+" lines",40):null};case"NotebookEdit":return{verb:"Edit notebook",detail:qe(t.notebook_path,300),note:null};case"Read":return{verb:"Read",detail:qe(t.file_path,300),note:null};case"WebFetch":return{verb:"Fetch",detail:qe(t.url,300),note:null};case"WebSearch":return{verb:"Search the web for",detail:qe(t.query,300),note:null};default:{let s=Object.entries(t).find(([,o])=>typeof o=="string");return{verb:"Use "+n,detail:s?qe(s[1],300):"",note:null}}}}function pb(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 mb(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 gb(n,e={}){let t=r=>{let i=new Error(r);throw i.status=400,i.code="bad_answer",i},s=e.action;qm.includes(s)||t("action is one of "+qm.join(", "));let o=e.message?qe(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:mb(n.tool,n.suggestions,n.input)}:{behavior:"allow"}}function eg(n){return n?{hookSpecificOutput:{hookEventName:"PermissionRequest",decision:n}}:{}}function ci(n,e={}){if(e.action==="deny")return"Denied"+(e.message?" (“"+qe(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])=>"“"+qe(s,80)+"” → "+qe(Array.isArray(o)?o.join(", "):o,120)).join("; ")}return e.action==="reply"?"Replied “"+qe(e.text,200)+"”":String(e.action||"")}function yd({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 tg({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 qm,fb,Xm,cl,Vm,qe,ul,dl,ui=J(()=>{qm=["allow","allow_session","deny","answer"],fb=9.5*60*1e3,Xm=45*1e3,cl=25*1e3,Vm=4e3;qe=(n,e)=>{let t=String(n??"").replace(/\s+/g," ").trim();return t.length>e?t.slice(0,e-1)+"…":t};ul=class{constructor({now:e=()=>Date.now(),timeoutMs:t=fb,noListenerMs:s=Xm,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=li(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:Qm(),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"?pb(s):null,describe:u==="permission"?hb(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=cl,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,cl,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=gb(o,t);o.state="answered",o.decision=r,o.answer={action:t.action,message:t.message?qe(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?ci(e,e.answer):null,capture:[...e.captures].includes("mod")?"mod":"hooks"}:null}},dl=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,`
114
+ `).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>Vm&&s("the reply is over "+Vm+" characters"),t}listening(e){return(this.waiters.get(e)||new Set).size>0||this.now()-(this.lastPoll.get(e)||0)<Xm}put(e,t,s={}){let o={id:Qm().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=cl,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,cl));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 ng(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?li(e.tool_name,e.tool_input):null}}];case"Stop":return[{...r,type:"turn_completed",data:{...i,lastAssistantPreview:F(e.last_assistant_message||"",280),question:uu(e.last_assistant_message||""),...(()=>{let l=$h(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 sg=J(()=>{Ae();ui()});import{execFile as yb}from"node:child_process";import Uo from"node:fs";import Ys from"node:path";function fl(n,e){return new Promise(t=>{yb("git",["-C",n,...e],{timeout:1500},(s,o)=>t(s?null:String(o).trim()))})}async function og(n){if(!n||!Uo.existsSync(n))return null;let e=await fl(n,["rev-parse","--show-toplevel"]);if(!e)return null;let t=await fl(n,["symbolic-ref","--short","-q","HEAD"]),s=Ka(await fl(e,["config","--get","remote.origin.url"]));return{root:e,branch:t||null,name:Ys.basename(e),...s?{github:s}:{}}}async function rg(n){return!n||!Uo.existsSync(n)?null:fl(n,["config","--get","remote.origin.url"])}function ig(n,e){if(!n)return!1;if((e.ignoreRepos||[]).includes(Ys.basename(n))||(e.ignoreRepos||[]).includes(n))return!0;try{return Uo.existsSync(Ys.join(n,".leftoff-ignore"))||Uo.existsSync(Ys.join(n,".checkpoints-ignore"))}catch{return!1}}function ag(n){if(!n)return null;let e=Ys.resolve(String(n));try{if(!Uo.statSync(e).isDirectory())return null}catch{return null}for(let t=0;t<64;t++){try{if(Uo.existsSync(Ys.join(e,".git")))return e}catch{}let s=Ys.dirname(e);if(s===e)return null;e=s}return null}var lg=J(()=>{$u()});import Ve from"node:fs";import wb from"node:os";import gt from"node:path";import fg from"node:crypto";function Sl(n,{multiline:e=!1}={}){let t=String(n).replace(/\r\n?/g,`
115
+ `).replace(bb,"");return t=e?t.replace(/[\u0000-\u0008\u000B-\u001F\u007F]/g,""):t.replace(/[\u0000-\u001F\u007F]+/g," "),t.replace(/[ \t]+\n/g,`
113
116
  `).replace(/\n{3,}/g,`
114
117
 
115
- `).trim()}function Em(n){if(typeof n!="string"||!n||n.length>de.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 qu(n){if(typeof n!="string"||!n||n.length>de.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(!Lk.includes(t))return null;if(t==="https:")return Em(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 Xu(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:"&&Dk.has(e.hostname)?{ok:!0,url:e.href,loopback:!0}:{ok:!1,message:"the feed must be https (plain http only on localhost)"}}function xn(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 Bt(n){return n&&typeof n=="object"&&!Array.isArray(n)}function bt(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 Ju(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>de.id||!Ik.test(r)?(s.add(t+"."+e,"letters, digits and ._:@#/- only, up to "+de.id),null):r}function ti(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"||!Nk.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 No(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 Cm(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=Em(r);return i||s.add(t+"."+e,"must be an https:// link"),i}function Om(n,e,t){if(!Bt(n))return t.add(e,"must be an object"),null;xn(n,["id","title","project","status","startedAt","updatedAt","checkpoints","needsYou","lists","links"],e,t);let s={id:Ju(n,"id",e,t),title:bt(n,"title",e,t,{required:!0,max:de.title}),project:bt(n,"project",e,t,{max:de.project}),status:null,startedAt:ti(n,"startedAt",e,t,{required:!0}),updatedAt:ti(n,"updatedAt",e,t,{required:!0}),checkpoints:[],needsYou:[],lists:[],links:[]};bm.includes(n.status)?s.status=n.status:t.add(e+".status","must be one of "+bm.join(", "));let o=new Set;No(n,"checkpoints",e,t,de.checkpoints).forEach((a,l)=>{let c=e+".checkpoints["+l+"]";if(!Bt(a))return t.add(c,"must be an object");xn(a,["id","at","summary","done","next"],c,t);let u={id:Ju(a,"id",c,t),at:ti(a,"at",c,t,{required:!0}),summary:bt(a,"summary",c,t,{required:!0,max:de.summary,multiline:!0}),done:bt(a,"done",c,t,{max:de.summary,multiline:!0}),next:bt(a,"next",c,t,{max:de.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;No(n,"needsYou",e,t,de.needsYou).forEach((a,l)=>{let c=e+".needsYou["+l+"]";if(!Bt(a))return t.add(c,"must be an object");xn(a,["id","text","since"],c,t);let u={id:Ju(a,"id",c,t),text:bt(a,"text",c,t,{required:!0,max:de.text,multiline:!0}),since:ti(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 No(n,"lists",e,t,de.lists).forEach((a,l)=>{let c=e+".lists["+l+"]";if(!Bt(a))return t.add(c,"must be an object");xn(a,["name","items"],c,t);let u=bt(a,"name",c,t,{required:!0,max:de.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)"),No(a,"items",c,t,de.items).forEach((p,h)=>{let m=c+".items["+h+"]";if(!Bt(p))return t.add(m,"must be an object");xn(p,["n","text","done"],m,t),!Number.isInteger(p.n)||p.n<1||p.n>de.n?t.add(m+".n","must be a whole number from 1 (stable: the same item keeps its n)"):f.has(p.n)&&t.add(m+".n","duplicate "+p.n),f.add(p.n),p.done!=null&&typeof p.done!="boolean"&&t.add(m+".done","must be true or false"),d.push({n:p.n,text:bt(p,"text",m,t,{required:!0,max:de.text}),done:p.done===!0})}),s.lists.push({name:u,items:d})}),No(n,"links",e,t,de.links).forEach((a,l)=>{let c=e+".links["+l+"]";if(!Bt(a))return t.add(c,"must be an object");xn(a,["label","url"],c,t),s.links.push({label:bt(a,"label",c,t,{required:!0,max:de.label}),url:Cm(a,"url",c,t,{required:!0})})}),s}function Rk(n){let e=new sl;if(!Bt(n))return{ok:!1,errors:["feed: must be a JSON object"],feed:null};xn(n,["version","agent","generatedAt","sessions"],"feed",e),n.version!==Qa&&e.add("feed.version",'must be "'+Qa+'"');let t=null;if(!Bt(n.agent))e.add("feed.agent","required ({ name, icon, url })");else{xn(n.agent,["name","icon","url","reply","replyHint"],"feed.agent",e),t={name:bt(n.agent,"name","feed.agent",e,{required:!0,max:de.agentName}),icon:bt(n.agent,"icon","feed.agent",e,{max:de.icon}),url:Cm(n.agent,"url","feed.agent",e)};let i=n.agent.reply;if(i!=null)if(!Bt(i))e.add("feed.agent.reply","must be an object ({ label, url })");else{xn(i,["label","url"],"feed.agent.reply",e);let l=bt(i,"label","feed.agent.reply",e,{required:!0,max:de.replyLabel}),c=null;i.url==null||i.url===""?e.add("feed.agent.reply.url","required"):(c=qu(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=bt(n.agent,"replyHint","feed.agent",e,{max:de.replyHint});a&&(t.replyHint=a)}let s=ti(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 No(n,"sessions","feed",e,de.sessions).forEach((i,a)=>{let l=Om(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:Qa,agent:t,generatedAt:s,sessions:o}}}function ol(n,{maxBytes:e=de.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=Rk(s);return o.ok?{ok:!0,feed:o.feed,bytes:t.length}:{ok:!1,code:"invalid",errors:o.errors}}async function Pk(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 jk(n,e){let t=new URL(n);return t.searchParams.set("key",e),t.href}async function Fk({url:n,key:e=null,etag:t=null,auth:s="bearer",fetchImpl:o=globalThis.fetch,timeoutMs:r=15e3,maxBytes:i=de.bytes,userAgent:a="Leftoff"}={}){let l=e&&s==="query"?["query"]:e?["bearer","query"]:["none"],c=null;for(let u of l){let d=await Uk({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 Uk({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"?jk(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 p=0;p<4;p++){let h;try{h=await o(c,{headers:u,redirect:"manual",signal:d.signal})}catch(w){let S=d.signal.aborted?"no answer in "+Math.round(r/1e3)+" s":w&&w.cause&&(w.cause.code||w.cause.message)||w&&w.message||"network error";return{result:"error",error:{code:"unreachable",message:Va("can’t reach the feed: "+S,e)}}}if([301,302,303,307,308].includes(h.status)){let w=h.headers.get("location"),S=null;try{S=w&&new URL(w,c)}catch{}if(!S||S.origin!==l)return{result:"error",httpStatus:h.status,error:{code:"redirect",message:"the feed redirects to another site; Leftoff won’t send the key there. Use the final URL."}};s==="query"&&!S.searchParams.has("key")&&S.searchParams.set("key",e),c=S.href;continue}if(h.status===304)return{result:"unchanged",etag:t,httpStatus:304};if(h.status===401||h.status===403)return{result:"error",httpStatus:h.status,error:{code:"unauthorized",message:"the feed refused the key (HTTP "+h.status+")"}};if(h.status===404)return{result:"error",httpStatus:404,error:{code:"not_found",message:"no feed at that URL (HTTP 404)"}};if(h.status<200||h.status>=300)return{result:"error",httpStatus:h.status,error:{code:"http",message:"the feed answered HTTP "+h.status}};let m=await Pk(h,i);if(m.tooLarge)return{result:"error",httpStatus:h.status,error:{code:"too_large",message:"the feed is bigger than "+Math.round(i/1024)+" KB"}};let y=ol(m.buf,{maxBytes:i});return y.ok?{result:"ok",feed:y.feed,bytes:y.bytes,etag:h.headers.get("etag")||null,httpStatus:h.status}:{result:"error",httpStatus:h.status,error:{code:y.code,message:y.code==="invalid"?"the feed doesn’t match "+Qa+" ("+y.errors.length+" problem"+(y.errors.length===1?"":"s")+")":Va(y.errors[0],e),details:y.errors.slice(0,10).map(w=>Va(w,e))}}}return{result:"error",error:{code:"redirect",message:"too many redirects"}}}catch(p){return{result:"error",error:{code:"unreachable",message:Va("can’t read the feed: "+(p&&p.message||p),e)}}}finally{clearTimeout(f)}}function Im(n=process.env){return dt.join(n.XDG_CONFIG_HOME||dt.join(n.HOME||Ok.homedir(),".config"),"checkpoints","agents")}function Qu(n,e=process.env){return tl(n),dt.join(Im(e),n+".key")}function el(n,e=process.env){try{return ze.readFileSync(Qu(n,e),"utf8").trim()||null}catch{return null}}function Hk(n,e,t=process.env){let s=Im(t);ze.mkdirSync(s,{recursive:!0,mode:448});try{ze.chmodSync(s,448)}catch{}let o=Qu(n,t),r=o+"."+process.pid+".tmp";return ze.writeFileSync(r,String(e).trim()+`
116
- `,{mode:384}),ze.chmodSync(r,384),ze.renameSync(r,o),o}function Bk(n,e=process.env){try{return ze.unlinkSync(Qu(n,e)),!0}catch{return!1}}function tl(n){if(typeof n!="string"||!_m.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 Io(n,...e){let t=dt.resolve(n),s=dt.resolve(t,...e);if(s!==t&&!s.startsWith(t+dt.sep))throw new Error("refusing to write outside "+t);return s}function Kk(n){return(String(n).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,48)||"session")+"-"+Am.createHash("sha256").update(String(n)).digest("hex").slice(0,8)+".md"}function Gk(n){return(String(n).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40)||"list")+"-"+Am.createHash("sha256").update(String(n)).digest("hex").slice(0,6)}function $k(n){let e=n.session,t=["---","kind: leftoff-agent-session","schema: 1","agent: "+Za(n.agent),"id: "+Za(e.id),"title: "+Za(e.title)];if(e.project&&t.push("project: "+Za(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(Xa(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,"",Xa(o.summary)),o.done&&t.push(">",Xa("Done: "+o.done)),o.next&&t.push(">",Xa("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. -->",Vu,JSON.stringify(s,null,1),"```",""),t.join(`
117
- `)}function Yk(n){let e=String(n||""),t=e.lastIndexOf(`
118
- `+Vu+`
119
- `);if(t<0)return null;let s=e.slice(t+Vu.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(!Bt(r)||!Bt(r.session))return null;let i=new sl,a=Om(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,de.agentName):null,inFeed:r.inFeed!==!1,firstSeenAt:r.firstSeenAt||null,goneAt:r.goneAt||null,pulledAt:r.pulledAt||null,session:a}}function Wk(n,e){if(!n)return{...e,checkpoints:e.checkpoints.slice().sort((o,r)=>o.at.localeCompare(r.at)).slice(-de.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(-de.stored);return{...e,checkpoints:s}}function xm(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 zk(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"&&qu(t.url)){let s=nl(t.label).slice(0,de.replyLabel);s&&(e.reply={label:s,url:qu(t.url)})}return typeof n.replyHint=="string"&&(e.replyHint=nl(n.replyHint).slice(0,de.replyHint)||null),e}function ni(n){let e=n.session.status;return!n.inFeed||e==="done"?"ended":e==="waiting"?"waiting":"active"}function td(n,e){return"agents/"+n.name+"/"+(e.project?e.project.toLowerCase().replace(/[\\/]+/g,"-"):n.name)}function Zu(n,e,t){return{id:t.id,sessionId:rl(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 Jn(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)=>zn(i.since)<zn(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-zn(o.since))/1e3))}}function nd(n,e,t=Date.now(),{full:s=!1}={}){let o=e.session,r=ni(e),i=o.checkpoints,a=i[i.length-1]||null,l=td(n,o),c={id:rl(n.name,o.id),feedId:o.id,title:o.title,titleSource:"agent",agent:{...ed(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-zn(o.updatedAt))/1e3)),checkpointCount:i.length,latestCheckpoint:a?Zu(n,e,a):null,resumeCommand:null,jump:null,live:null,peer:null,pinned:!1,archived:!1,ignored:!1,empty:!1,ghost:!1,needsYou:Jn(n,e,t),lists:o.lists.map(u=>{let d=u.items.filter(f=>f.done).length;return{ref:Nm(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=>({...Zu(n,e,u)}))),c}function Nm(n,e,t){return"~"+n+"/"+Gk(e+"\0"+t)}function il(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:Nm(t.name,s.session.id,o.name),name:o.name,repo:s.session.project||t.display,repoKey:td(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:ed(t),sourceSession:rl(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 al(n){let e=[];for(let t of n.agents)for(let s of t.records){let o=s.session,r=ni(s);e.push({session:{id:rl(t.name,o.id),title:o.title,machine:t.display,capture:"agent",repo:{name:o.project||t.display,root:td(t,o)},startedAt:o.startedAt,lastActivityAt:o.updatedAt,endedAt:r==="ended"?s.goneAt||o.updatedAt:null,turns:o.checkpoints.length,agent:ed(t),agentStatus:r,agentNeeds:Jn(t,s)},checkpoints:o.checkpoints.map(i=>Zu(t,s,i))})}return e}function sd(n,e,t,{heading:s="##"}={}){let o=[];for(let a of n.agents)for(let l of a.records){let c=l.session,u=Jn(a,l);!(zn(c.updatedAt)>=e&&zn(c.updatedAt)<=t)&&!u||o.push({a,rec:l,n:u})}if(!o.length)return[];o.sort((a,l)=>!!l.n-!!a.n||zn(l.rec.session.updatedAt)-zn(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":ni(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 Qa,Tm,de,bm,_m,Ik,Nk,Mk,Lk,Dk,sl,Va,Za,Xa,Vu,vm,vt,zn,ed,rl,Fs,qn=z(()=>{wn();Qa="leftoff-agent-feed/1",Tm=12e4,de={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},bm=["active","waiting","done"],_m=/^[a-z0-9][a-z0-9-]{0,30}$/,Ik=/^[A-Za-z0-9][A-Za-z0-9._:@#\/-]*$/,Nk=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?(Z|[+-]\d{2}:?\d{2})$/,Mk=/[\u202A-\u202E\u2066-\u2069\u200E\u200F]/g;Lk=["https:","sms:","imessage:","mailto:","tel:"];Dk=new Set(["127.0.0.1","localhost","[::1]","::1"]);sl=class{constructor(){this.list=[]}add(e,t){this.list.length<50&&this.list.push(e+": "+t)}};Va=(n,e)=>e?String(n).split(e).join("…"):String(n);Za=n=>n==null?"":JSON.stringify(String(n)),Xa=n=>String(n).split(`
118
+ `).trim()}function mg(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 Sd(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(!vb.includes(t))return null;if(t==="https:")return mg(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 vd(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:"&&xb.has(e.hostname)?{ok:!0,url:e.href,loopback:!0}:{ok:!1,message:"the feed must be https (plain http only on localhost)"}}function En(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 Et(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=Sl(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 wd(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||!Sb.test(r)?(s.add(t+"."+e,"letters, digits and ._:@#/- only, up to "+he.id),null):r}function di(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"||!kb.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 Bo(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 gg(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=mg(r);return i||s.add(t+"."+e,"must be an https:// link"),i}function yg(n,e,t){if(!Wt(n))return t.add(e,"must be an object"),null;En(n,["id","title","project","status","startedAt","updatedAt","checkpoints","needsYou","lists","links"],e,t);let s={id:wd(n,"id",e,t),title:Et(n,"title",e,t,{required:!0,max:he.title}),project:Et(n,"project",e,t,{max:he.project}),status:null,startedAt:di(n,"startedAt",e,t,{required:!0}),updatedAt:di(n,"updatedAt",e,t,{required:!0}),checkpoints:[],needsYou:[],lists:[],links:[]};cg.includes(n.status)?s.status=n.status:t.add(e+".status","must be one of "+cg.join(", "));let o=new Set;Bo(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");En(a,["id","at","summary","done","next"],c,t);let u={id:wd(a,"id",c,t),at:di(a,"at",c,t,{required:!0}),summary:Et(a,"summary",c,t,{required:!0,max:he.summary,multiline:!0}),done:Et(a,"done",c,t,{max:he.summary,multiline:!0}),next:Et(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;Bo(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");En(a,["id","text","since"],c,t);let u={id:wd(a,"id",c,t),text:Et(a,"text",c,t,{required:!0,max:he.text,multiline:!0}),since:di(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 Bo(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");En(a,["name","items"],c,t);let u=Et(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)"),Bo(a,"items",c,t,he.items).forEach((p,h)=>{let m=c+".items["+h+"]";if(!Wt(p))return t.add(m,"must be an object");En(p,["n","text","done"],m,t),!Number.isInteger(p.n)||p.n<1||p.n>he.n?t.add(m+".n","must be a whole number from 1 (stable: the same item keeps its n)"):f.has(p.n)&&t.add(m+".n","duplicate "+p.n),f.add(p.n),p.done!=null&&typeof p.done!="boolean"&&t.add(m+".done","must be true or false"),d.push({n:p.n,text:Et(p,"text",m,t,{required:!0,max:he.text}),done:p.done===!0})}),s.lists.push({name:u,items:d})}),Bo(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");En(a,["label","url"],c,t),s.links.push({label:Et(a,"label",c,t,{required:!0,max:he.label}),url:gg(a,"url",c,t,{required:!0})})}),s}function Ab(n){let e=new kl;if(!Wt(n))return{ok:!1,errors:["feed: must be a JSON object"],feed:null};En(n,["version","agent","generatedAt","sessions"],"feed",e),n.version!==gl&&e.add("feed.version",'must be "'+gl+'"');let t=null;if(!Wt(n.agent))e.add("feed.agent","required ({ name, icon, url })");else{En(n.agent,["name","icon","url","reply","replyHint"],"feed.agent",e),t={name:Et(n.agent,"name","feed.agent",e,{required:!0,max:he.agentName}),icon:Et(n.agent,"icon","feed.agent",e,{max:he.icon}),url:gg(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{En(i,["label","url"],"feed.agent.reply",e);let l=Et(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=Sd(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=Et(n.agent,"replyHint","feed.agent",e,{max:he.replyHint});a&&(t.replyHint=a)}let s=di(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 Bo(n,"sessions","feed",e,he.sessions).forEach((i,a)=>{let l=yg(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:gl,agent:t,generatedAt:s,sessions:o}}}function bl(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=Ab(s);return o.ok?{ok:!0,feed:o.feed,bytes:t.length}:{ok:!1,code:"invalid",errors:o.errors}}async function Tb(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 _b(n,e){let t=new URL(n);return t.searchParams.set("key",e),t.href}async function Eb({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 Ob({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 Ob({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"?_b(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 p=0;p<4;p++){let h;try{h=await o(c,{headers:u,redirect:"manual",signal:d.signal})}catch(S){let b=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:hl("can’t reach the feed: "+b,e)}}}if([301,302,303,307,308].includes(h.status)){let S=h.headers.get("location"),b=null;try{b=S&&new URL(S,c)}catch{}if(!b||b.origin!==l)return{result:"error",httpStatus:h.status,error:{code:"redirect",message:"the feed redirects to another site; Leftoff won’t send the key there. Use the final URL."}};s==="query"&&!b.searchParams.has("key")&&b.searchParams.set("key",e),c=b.href;continue}if(h.status===304)return{result:"unchanged",etag:t,httpStatus:304};if(h.status===401||h.status===403)return{result:"error",httpStatus:h.status,error:{code:"unauthorized",message:"the feed refused the key (HTTP "+h.status+")"}};if(h.status===404)return{result:"error",httpStatus:404,error:{code:"not_found",message:"no feed at that URL (HTTP 404)"}};if(h.status<200||h.status>=300)return{result:"error",httpStatus:h.status,error:{code:"http",message:"the feed answered HTTP "+h.status}};let m=await Tb(h,i);if(m.tooLarge)return{result:"error",httpStatus:h.status,error:{code:"too_large",message:"the feed is bigger than "+Math.round(i/1024)+" KB"}};let y=bl(m.buf,{maxBytes:i});return y.ok?{result:"ok",feed:y.feed,bytes:y.bytes,etag:h.headers.get("etag")||null,httpStatus:h.status}:{result:"error",httpStatus:h.status,error:{code:y.code,message:y.code==="invalid"?"the feed doesn’t match "+gl+" ("+y.errors.length+" problem"+(y.errors.length===1?"":"s")+")":hl(y.errors[0],e),details:y.errors.slice(0,10).map(S=>hl(S,e))}}}return{result:"error",error:{code:"redirect",message:"too many redirects"}}}catch(p){return{result:"error",error:{code:"unreachable",message:hl("can’t read the feed: "+(p&&p.message||p),e)}}}finally{clearTimeout(f)}}function wg(n=process.env){return gt.join(n.XDG_CONFIG_HOME||gt.join(n.HOME||wb.homedir(),".config"),"checkpoints","agents")}function xd(n,e=process.env){return wl(n),gt.join(wg(e),n+".key")}function yl(n,e=process.env){try{return Ve.readFileSync(xd(n,e),"utf8").trim()||null}catch{return null}}function Cb(n,e,t=process.env){let s=wg(t);Ve.mkdirSync(s,{recursive:!0,mode:448});try{Ve.chmodSync(s,448)}catch{}let o=xd(n,t),r=o+"."+process.pid+".tmp";return Ve.writeFileSync(r,String(e).trim()+`
119
+ `,{mode:384}),Ve.chmodSync(r,384),Ve.renameSync(r,o),o}function Nb(n,e=process.env){try{return Ve.unlinkSync(xd(n,e)),!0}catch{return!1}}function wl(n){if(typeof n!="string"||!pg.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 Ho(n,...e){let t=gt.resolve(n),s=gt.resolve(t,...e);if(s!==t&&!s.startsWith(t+gt.sep))throw new Error("refusing to write outside "+t);return s}function Ib(n){return(String(n).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,48)||"session")+"-"+fg.createHash("sha256").update(String(n)).digest("hex").slice(0,8)+".md"}function Mb(n){return(String(n).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40)||"list")+"-"+fg.createHash("sha256").update(String(n)).digest("hex").slice(0,6)}function Lb(n){let e=n.session,t=["---","kind: leftoff-agent-session","schema: 1","agent: "+pl(n.agent),"id: "+pl(e.id),"title: "+pl(e.title)];if(e.project&&t.push("project: "+pl(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(ml(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,"",ml(o.summary)),o.done&&t.push(">",ml("Done: "+o.done)),o.next&&t.push(">",ml("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(`
120
+ `)}function Db(n){let e=String(n||""),t=e.lastIndexOf(`
121
+ `+kd+`
122
+ `);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(!Wt(r)||!Wt(r.session))return null;let i=new kl,a=yg(r.session,"session",i);return i.list.length||!a?null:{agent:typeof r.agent=="string"?r.agent:null,agentName:typeof r.agentName=="string"?Sl(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 Rb(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 dg(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 Pb(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"&&Sd(t.url)){let s=Sl(t.label).slice(0,he.replyLabel);s&&(e.reply={label:s,url:Sd(t.url)})}return typeof n.replyHint=="string"&&(e.replyHint=Sl(n.replyHint).slice(0,he.replyHint)||null),e}function fi(n){let e=n.session.status;return!n.inFeed||e==="done"?"ended":e==="waiting"?"waiting":"active"}function Td(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:vl(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 Qn(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)=>Xn(i.since)<Xn(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-Xn(o.since))/1e3))}}function _d(n,e,t=Date.now(),{full:s=!1}={}){let o=e.session,r=fi(e),i=o.checkpoints,a=i[i.length-1]||null,l=Td(n,o),c={id:vl(n.name,o.id),feedId:o.id,title:o.title,titleSource:"agent",agent:{...Ad(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-Xn(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:Qn(n,e,t),lists:o.lists.map(u=>{let d=u.items.filter(f=>f.done).length;return{ref:Sg(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 Sg(n,e,t){return"~"+n+"/"+Mb(e+"\0"+t)}function xl(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:Sg(t.name,s.session.id,o.name),name:o.name,repo:s.session.project||t.display,repoKey:Td(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:Ad(t),sourceSession:vl(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 Al(n){let e=[];for(let t of n.agents)for(let s of t.records){let o=s.session,r=fi(s);e.push({session:{id:vl(t.name,o.id),title:o.title,machine:t.display,capture:"agent",repo:{name:o.project||t.display,root:Td(t,o)},startedAt:o.startedAt,lastActivityAt:o.updatedAt,endedAt:r==="ended"?s.goneAt||o.updatedAt:null,turns:o.checkpoints.length,agent:Ad(t),agentStatus:r,agentNeeds:Qn(t,s)},checkpoints:o.checkpoints.map(i=>bd(t,s,i))})}return e}function Ed(n,e,t,{heading:s="##"}={}){let o=[];for(let a of n.agents)for(let l of a.records){let c=l.session,u=Qn(a,l);!(Xn(c.updatedAt)>=e&&Xn(c.updatedAt)<=t)&&!u||o.push({a,rec:l,n:u})}if(!o.length)return[];o.sort((a,l)=>!!l.n-!!a.n||Xn(l.rec.session.updatedAt)-Xn(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":fi(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 gl,hg,he,cg,pg,Sb,kb,bb,vb,xb,kl,hl,pl,ml,kd,ug,Ot,Xn,Ad,vl,zt,es=J(()=>{vn();gl="leftoff-agent-feed/1",hg=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},cg=["active","waiting","done"],pg=/^[a-z0-9][a-z0-9-]{0,30}$/,Sb=/^[A-Za-z0-9][A-Za-z0-9._:@#\/-]*$/,kb=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?(Z|[+-]\d{2}:?\d{2})$/,bb=/[\u202A-\u202E\u2066-\u2069\u200E\u200F]/g;vb=["https:","sms:","imessage:","mailto:","tel:"];xb=new Set(["127.0.0.1","localhost","[::1]","::1"]);kl=class{constructor(){this.list=[]}add(e,t){this.list.length<50&&this.list.push(e+": "+t)}};hl=(n,e)=>e?String(n).split(e).join("…"):String(n);pl=n=>n==null?"":JSON.stringify(String(n)),ml=n=>String(n).split(`
120
123
  `).map(e=>"> "+e).join(`
121
- `),Vu="```json leftoff-agent-session";vm=n=>{try{return JSON.parse(ze.readFileSync(n,"utf8"))}catch{return null}},vt=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 dt.join(this.root,"agents")}agentDir(e){return Io(this.dir,tl(e))}sources(){let e=[];try{e=ze.readdirSync(this.dir,{withFileTypes:!0}).filter(s=>s.isDirectory()&&_m.test(s.name)).map(s=>s.name).sort()}catch{}let t=[];for(let s of e){let o=vm(dt.join(this.dir,s,"source.json"));o&&o.name===s&&typeof o.url=="string"&&t.push(o)}return t}source(e){return tl(e),this.sources().find(t=>t.name===e)||null}status(e){return vm(dt.join(this.agentDir(e),"status.json"))||{}}writeStatus(e,t){$n(Io(this.agentDir(e),"status.json"),JSON.stringify(t,null,2)+`
122
- `),this.cache=null}add({name:e,url:t,key:s}){tl(e);let o=Xu(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($n(Io(this.agentDir(e),"source.json"),JSON.stringify(a,null,2)+`
123
- `),r&&Hk(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:!!el(e,this.env)}}remove(e,{purge:t=!1}={}){let s=this.agentDir(e);if(!ze.existsSync(s)){let i=new Error("no agent feed named "+e);throw i.code="not_found",i}let o=null;if(t)ze.rmSync(s,{recursive:!0,force:!0});else{let i=new Date(this.now()).toISOString().replace(/[:.]/g,"-"),a=Io(dt.join(this.dir,".removed"),e+"-"+i);ze.mkdirSync(dt.dirname(a),{recursive:!0}),ze.renameSync(s,a),o=dt.relative(this.root,a)}let r=Bk(e,this.env);return this.cache=null,{removed:e,movedTo:o,purged:!!t,keyDeleted:r}}hasKey(e){return!!el(e,this.env)}pullsHere(e){return e&&e.puller===this.machine&&this.hasKey(e.name)}loadRecords(e){let t=dt.join(this.agentDir(e),"sessions"),s=[],o=[];try{o=ze.readdirSync(t).filter(r=>r.endsWith(".md")&&!r.startsWith("."))}catch{}for(let r of o){let i=null;try{i=Yk(ze.readFileSync(dt.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=Io(s,"sessions"),r=new Date(this.now()).toISOString(),i=new Map(this.loadRecords(e).map(p=>[p.session.id,p])),a=0,l=0,c=0,u=t.agent&&t.agent.name||e,d=p=>{let h=Io(o,Kk(p.session.id)),m=$k(p),y=null;try{y=ze.readFileSync(h,"utf8")}catch{}y!==m&&($n(h,m),a++)},f=new Set;for(let p of t.sessions){f.add(p.id);let h=i.get(p.id);h||l++;let m=Wk(h&&h.session,p),y=h&&h.inFeed&&JSON.stringify(h.session)===JSON.stringify(m)&&h.agentName===u;d({agent:e,agentName:u,inFeed:!0,firstSeenAt:h&&h.firstSeenAt||r,goneAt:null,pulledAt:y?h.pulledAt:r,session:m})}for(let[p,h]of i)f.has(p)||!h.inFeed||(c++,d({...h,inFeed:!1,goneAt:r,session:{...h.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=el(e,this.env),r=this.status(e),i=new Date(this.now()).toISOString(),a=await Fk({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:xm(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,...zk(r.agent),source:o,puller:o.puller,pullsHere:this.pullsHere(o),hasKey:this.hasKey(o.name),status:xm(r),records:i})}let s={agents:t};return this.cache={at:this.now(),v:s},s}};zn=n=>{let e=Date.parse(n||"");return isNaN(e)?0:e};ed=n=>({name:n.name,display:n.display,icon:n.icon||null,url:n.url||null,reply:n.reply||null,replyHint:n.replyHint||null}),rl=(n,e)=>"agent:"+n+":"+e,Fs=n=>typeof n=="string"&&n.startsWith("agent:")});function Jk(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 qk(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 Mm(n){return new Date(n).toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"})}function od(n,e,t=ye,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((h,m)=>P(m.createdAt)-P(h.createdAt))[0]||null,u=o&&a.machine&&a.machine!==o,d=Pt(a,e,u?{...t,staleAfterSec:Math.max(t.staleAfterSec,900)}:t),f=Math.max(P(a.lastActivityAt)||0,P(a.endedAt)||0,c?P(c.createdAt):0,P(a.startedAt)||0);if(d==="ended"&&f<s||d!=="ended"&&f<s&&!c)continue;let p={session:a,latest:c,status:d};d==="needs_input"||c&&/^YOU:/i.test(c.next||"")&&d!=="ended"?r.needsYou.push(p):d==="ended"?r.finished.push(p):d==="dormant"||d==="stale"?r.dormant.push(p):r.active.push(p)}let i=(a,l)=>(P(l.session.lastActivityAt)||0)-(P(a.session.lastActivityAt)||0);for(let a of Object.values(r))a.sort(i);return r}function Zk(n,e,t,s,o){let r=n.session,i=n.latest,a=r.repo&&r.repo.branch,l=(o&&o.get(Mo(r))||fe(r))+(a?" · "+a:"")+(s&&r.machine?" · "+r.machine:""),c;t==="needsYou"?c=r.needsInput?"waiting on "+String(r.lastNotification||"input").replace(/_/g," ")+" since "+Mm(r.lastEventAt||r.lastActivityAt):"blocked on you":t==="finished"?c="closed "+Mm(r.endedAt):t==="active"?c=n.status:c="idle "+qk(e-(P(r.lastActivityAt)||e));let u=["- **"+(r.title||"Untitled session")+"** ("+l+") — "+c];return i?t==="dormant"||t==="finished"?u.push(" Stopped at: "+U(i.stoppedAt,220)+" Next: "+U(i.next,200)):u.push(" Next: "+U(i.next,220)):r.lastPreview&&u.push(" Last reply: "+U(r.lastPreview,200)),u}function Xk(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: "+U(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 Qk(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&&P(c.at)>=e&&P(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("- **"+U(a.name,80)+"** ("+(a.repo||"no repo")+"): "+a.done+"/"+a.total+" done"+(l.length?". Done: "+l.slice(0,4).map(u=>U(u.text,80)).join("; ")+(l.length>4?" +"+(l.length-4):""):"")+(c.length?". Next: "+U(c[0].text,100):". All done."))}return s.ticked=o,s}function rd(n,e){return Gn(n||xs(),e,{repoKey:Mo(e),repoName:fe(e)}).space}async function ll(n,{now:e=Date.now(),cfg:t=ye,date:s,summarizer:o,machine:r,lists:i=[],agents:a=null,spaces:l=null,agentSpace:c=null}={}){let u=(t.digest&&t.digest.lookbackHours||24)*36e5,d=e-u,f=od(n,e,t,d,r),p=[...new Set(n.map(O=>O.session.machine).filter(Boolean))].sort(),h=s||Jk(e),m=new Date(h+"T12:00:00").toLocaleDateString("en-US",{weekday:"short",month:"short",day:"numeric"}).replace(",",""),y=f.needsYou.length+f.active.length+f.dormant.length+f.finished.length,w={date:h,generatedAt:G(e),window:{from:G(d),to:G(e)},sessions:y,needsYou:f.needsYou.map(O=>O.session.id),path:"digests/"+h+".md",machine:r||null,shared:!0,machines:p},S=en(Object.values(f).flat().map(O=>Mo(O.session))),k=new Map,v=O=>(k.has(O)||k.set(O,{needsYou:[],active:[],dormant:[],finished:[],agents:[]}),k.get(O));for(let[O,F]of Object.entries(f))for(let $ of F)v(rd(l,$.session))[O].push($);let T=new Map;if(a&&a.agents)for(let O of a.agents){let F=c?c(O):Gn(l||xs(),{id:"agent:"+O.name+":x",agent:{name:O.name,puller:O.puller}}).space;sd({agents:[O]},d,e,{heading:"###"}).length&&(v(F),T.set(F,[...T.get(F)||[],O]))}let b=ho([...k.keys()]);w.spaces=b;let g=[];g.push("# Where you left off — "+m,"");let _=0;for(let O of b){let F=k.get(O),$=F.needsYou.length+F.active.length+F.dormant.length+F.finished.length,j=new Set(Object.values(F).flat().map(Y=>Y.session&&Y.session.machine).filter(Boolean)).size>1;g.push("## "+O,"");let W=(Y,Z,te)=>{if(Z.length){g.push("### "+Y+" ("+Z.length+")");for(let B of Z)g.push(...Zk(B,e,te,j,S));g.push("")}};W("Needs you",F.needsYou,"needsYou"),W("Still running",F.active,"active"),W("Dormant, ready to pick up",F.dormant,"dormant"),W("Finished or closed",F.finished,"finished");let ue=T.get(O);if(ue){let Y=sd({agents:ue},d,e,{heading:"###"});_+=Y.filter(Z=>Z.startsWith("- ")).length,g.push(...Y,"")}else $||g.push("_Nothing here in the last "+Math.round(u/36e5)+" hours._","")}y||g.push("_No Claude Code sessions in the last "+Math.round(u/36e5)+" hours._",""),w.agentSessions=_;let x=Qk(i,d,e);x.length&&g.push(...x,""),w.listsTicked=x.ticked||0;let C=null,E=null;if(y&&(!t.digest||t.digest.topOfMind!==!1)){if(o){let O=[...f.needsYou,...f.dormant,...f.active].slice(0,15).map(F=>"- "+(F.session.title||"Untitled")+" ["+F.status+"]"+(F.latest?" Doing: "+F.latest.doing+" Stopped at: "+F.latest.stoppedAt+" Next: "+F.latest.next:""));C=await o.paragraph(Vk,O.join(`
124
- `)),C&&(E="model")}C||(C=Xk(f),E="heuristic"),g.push("## Top of mind","",C,"")}w.topOfMindBy=E;let D=ys({date:h,machine:r||null,shared:!0,machines:p,spaces:b,generated_at:w.generatedAt,window_from:w.window.from,window_to:w.window.to,sessions:y,needs_you:w.needsYou,schema:2});return{meta:w,markdown:D+g.join(`
124
+ `),kd="```json leftoff-agent-session";ug=n=>{try{return JSON.parse(Ve.readFileSync(n,"utf8"))}catch{return null}},Ot=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 gt.join(this.root,"agents")}agentDir(e){return Ho(this.dir,wl(e))}sources(){let e=[];try{e=Ve.readdirSync(this.dir,{withFileTypes:!0}).filter(s=>s.isDirectory()&&pg.test(s.name)).map(s=>s.name).sort()}catch{}let t=[];for(let s of e){let o=ug(gt.join(this.dir,s,"source.json"));o&&o.name===s&&typeof o.url=="string"&&t.push(o)}return t}source(e){return wl(e),this.sources().find(t=>t.name===e)||null}status(e){return ug(gt.join(this.agentDir(e),"status.json"))||{}}writeStatus(e,t){qn(Ho(this.agentDir(e),"status.json"),JSON.stringify(t,null,2)+`
125
+ `),this.cache=null}add({name:e,url:t,key:s}){wl(e);let o=vd(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(qn(Ho(this.agentDir(e),"source.json"),JSON.stringify(a,null,2)+`
126
+ `),r&&Cb(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:!!yl(e,this.env)}}remove(e,{purge:t=!1}={}){let s=this.agentDir(e);if(!Ve.existsSync(s)){let i=new Error("no agent feed named "+e);throw i.code="not_found",i}let o=null;if(t)Ve.rmSync(s,{recursive:!0,force:!0});else{let i=new Date(this.now()).toISOString().replace(/[:.]/g,"-"),a=Ho(gt.join(this.dir,".removed"),e+"-"+i);Ve.mkdirSync(gt.dirname(a),{recursive:!0}),Ve.renameSync(s,a),o=gt.relative(this.root,a)}let r=Nb(e,this.env);return this.cache=null,{removed:e,movedTo:o,purged:!!t,keyDeleted:r}}hasKey(e){return!!yl(e,this.env)}pullsHere(e){return e&&e.puller===this.machine&&this.hasKey(e.name)}loadRecords(e){let t=gt.join(this.agentDir(e),"sessions"),s=[],o=[];try{o=Ve.readdirSync(t).filter(r=>r.endsWith(".md")&&!r.startsWith("."))}catch{}for(let r of o){let i=null;try{i=Db(Ve.readFileSync(gt.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=Ho(s,"sessions"),r=new Date(this.now()).toISOString(),i=new Map(this.loadRecords(e).map(p=>[p.session.id,p])),a=0,l=0,c=0,u=t.agent&&t.agent.name||e,d=p=>{let h=Ho(o,Ib(p.session.id)),m=Lb(p),y=null;try{y=Ve.readFileSync(h,"utf8")}catch{}y!==m&&(qn(h,m),a++)},f=new Set;for(let p of t.sessions){f.add(p.id);let h=i.get(p.id);h||l++;let m=Rb(h&&h.session,p),y=h&&h.inFeed&&JSON.stringify(h.session)===JSON.stringify(m)&&h.agentName===u;d({agent:e,agentName:u,inFeed:!0,firstSeenAt:h&&h.firstSeenAt||r,goneAt:null,pulledAt:y?h.pulledAt:r,session:m})}for(let[p,h]of i)f.has(p)||!h.inFeed||(c++,d({...h,inFeed:!1,goneAt:r,session:{...h.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=yl(e,this.env),r=this.status(e),i=new Date(this.now()).toISOString(),a=await Eb({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:dg(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,...Pb(r.agent),source:o,puller:o.puller,pullsHere:this.pullsHere(o),hasKey:this.hasKey(o.name),status:dg(r),records:i})}let s={agents:t};return this.cache={at:this.now(),v:s},s}};Xn=n=>{let e=Date.parse(n||"");return isNaN(e)?0:e};Ad=n=>({name:n.name,display:n.display,icon:n.icon||null,url:n.url||null,reply:n.reply||null,replyHint:n.replyHint||null}),vl=(n,e)=>"agent:"+n+":"+e,zt=n=>typeof n=="string"&&n.startsWith("agent:")});function jb(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 Fb(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 kg(n){return new Date(n).toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"})}function Od(n,e,t=Se,s,o){let r={needsYou:[],active:[],dormant:[],finished:[]};for(let{session:a,checkpoints:l}of n){if(a.archived||dt(a,l.length))continue;let c=l.slice().sort((h,m)=>R(m.createdAt)-R(h.createdAt))[0]||null,u=o&&a.machine&&a.machine!==o,d=kt(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 p={session:a,latest:c,status:d};d==="needs_input"||c&&/^YOU:/i.test(c.next||"")&&d!=="ended"?r.needsYou.push(p):d==="ended"?r.finished.push(p):d==="dormant"||d==="stale"?r.dormant.push(p):r.active.push(p)}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 Hb(n,e,t,s,o){let r=n.session,i=n.latest,a=r.repo&&r.repo.branch,l=(o&&o.get(Go(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 "+kg(r.lastEventAt||r.lastActivityAt):"blocked on you":t==="finished"?c="closed "+kg(r.endedAt):t==="active"?c=n.status:c="idle "+Fb(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 Bb(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 Gb(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 Cd(n,e){return Jn(n||Es(),e,{repoKey:Go(e),repoName:me(e)}).space}async function Tl(n,{now:e=Date.now(),cfg:t=Se,date:s,summarizer:o,machine:r,lists:i=[],agents:a=null,spaces:l=null,agentSpace:c=null}={}){let u=(t.digest&&t.digest.lookbackHours||24)*36e5,d=e-u,f=Od(n,e,t,d,r),p=[...new Set(n.map(C=>C.session.machine).filter(Boolean))].sort(),h=s||jb(e),m=new Date(h+"T12:00:00").toLocaleDateString("en-US",{weekday:"short",month:"short",day:"numeric"}).replace(",",""),y=f.needsYou.length+f.active.length+f.dormant.length+f.finished.length,S={date:h,generatedAt:K(e),window:{from:K(d),to:K(e)},sessions:y,needsYou:f.needsYou.map(C=>C.session.id),path:"digests/"+h+".md",machine:r||null,shared:!0,machines:p},b=Gt(Object.values(f).flat().map(C=>Go(C.session))),w=new Map,A=C=>(w.has(C)||w.set(C,{needsYou:[],active:[],dormant:[],finished:[],agents:[]}),w.get(C));for(let[C,U]of Object.entries(f))for(let W of U)A(Cd(l,W.session))[C].push(W);let v=new Map;if(a&&a.agents)for(let C of a.agents){let U=c?c(C):Jn(l||Es(),{id:"agent:"+C.name+":x",agent:{name:C.name,puller:C.puller}}).space;Ed({agents:[C]},d,e,{heading:"###"}).length&&(A(U),v.set(U,[...v.get(U)||[],C]))}let k=vo([...w.keys()]);S.spaces=k;let g=[];g.push("# Where you left off — "+m,"");let _=0;for(let C of k){let U=w.get(C),W=U.needsYou.length+U.active.length+U.dormant.length+U.finished.length,P=new Set(Object.values(U).flat().map($=>$.session&&$.session.machine).filter(Boolean)).size>1;g.push("## "+C,"");let B=($,ge,Qe)=>{if(ge.length){g.push("### "+$+" ("+ge.length+")");for(let Me of ge)g.push(...Hb(Me,e,Qe,P,b));g.push("")}};B("Needs you",U.needsYou,"needsYou"),B("Still running",U.active,"active"),B("Dormant, ready to pick up",U.dormant,"dormant"),B("Finished or closed",U.finished,"finished");let ce=v.get(C);if(ce){let $=Ed({agents:ce},d,e,{heading:"###"});_+=$.filter(ge=>ge.startsWith("- ")).length,g.push(...$,"")}else W||g.push("_Nothing here in the last "+Math.round(u/36e5)+" hours._","")}y||g.push("_No Claude Code sessions in the last "+Math.round(u/36e5)+" hours._",""),S.agentSessions=_;let x=Gb(i,d,e);x.length&&g.push(...x,""),S.listsTicked=x.ticked||0;let E=null,O=null;if(y&&(!t.digest||t.digest.topOfMind!==!1)){if(o){let C=[...f.needsYou,...f.dormant,...f.active].slice(0,15).map(U=>"- "+(U.session.title||"Untitled")+" ["+U.status+"]"+(U.latest?" Doing: "+U.latest.doing+" Stopped at: "+U.latest.stoppedAt+" Next: "+U.latest.next:""));E=await o.paragraph(Ub,C.join(`
127
+ `)),E&&(O="model")}E||(E=Bb(f),O="heuristic"),g.push("## Top of mind","",E,"")}S.topOfMindBy=O;let M=vs({date:h,machine:r||null,shared:!0,machines:p,spaces:k,generated_at:S.generatedAt,window_from:S.window.from,window_to:S.window.to,sessions:y,needs_you:S.needsYou,schema:2});return{meta:S,markdown:M+g.join(`
125
128
  `).replace(/\n+$/,"")+`
126
- `}}var Vk,Mo,cl=z(()=>{ve();Ms();qn();Ts();Vk="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.";Mo=n=>n.repo&&n.repo.root||n.cwd||fe(n)});function tb(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),!eb.includes(tn[o.getUTCDay()]));r++);return o.toISOString().slice(0,10)}function nb({date:n,now:e,tz:t="UTC",digestTime:s="07:00",prevDigestAt:o=null}){let r=tb(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=Ht(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=nb({date:t,now:e,tz:i,digestTime:a,prevDigestAt:l}),p=n.filter(g=>!s||rd(o,g.session)===s),h=en(p.map(g=>Mo(g.session))),m=g=>"`"+(h.get(Mo(g))||fe(g)||"no repo").replace(/`/g,"'")+"`",y=g=>Kt(g.title||"Untitled session",80),w=[],S=[],k=[],v=[];for(let{session:g,checkpoints:_}of p){if(g.archived||pt(g,_.length))continue;let x=O=>O&&O>=f.from&&O<=f.to,C=_.filter(O=>x(P(O.createdAt))).sort((O,F)=>P(F.createdAt)-P(O.createdAt)),E=x(P(g.endedAt));if(!C.length&&!E&&!x(P(g.lastActivityAt)))continue;let D=E?"done":C[0]?Kt(C[0].stoppedAt||C[0].doing,110):"worked on it";v.push({t:Math.max(P(g.lastActivityAt)||0,P(g.endedAt)||0),line:"• "+m(g)+" "+y(g)+(D==="done"?" (done)":" — "+D)})}v.sort((g,_)=>_.t-g.t),w.push(...v.slice(0,8).map(g=>g.line)),v.length>8&&w.push("• +"+(v.length-8)+" more");let T=od(p,e,r&&r.staleAfterSec?r:{...r,staleAfterSec:120},f.from);for(let g of T.needsYou){let _=g.session,x=g.latest,C=x&&/^YOU:/i.test(x.next||"")?Kt(x.next.replace(/^YOU:\s*/i,""),110):_.needsInput?"waiting on "+String(_.lastNotification||"input").replace(/_/g," "):"waiting on me";k.push("• "+m(_)+" "+y(_)+" — "+C)}for(let g of[...T.active,...T.dormant.slice(0,d)]){let _=g.session,x=g.latest;S.push("• "+m(_)+" "+y(_)+(x&&x.next?" — next: "+Kt(x.next,110):""))}if(c&&c.agents)for(let g of c.agents){let _=u?u(g):Gn(o||xs(),{id:"agent:"+g.name+":x",agent:{name:g.name,puller:g.puller}}).space;if(!(s&&_!==s))for(let x of g.records||[]){let C=x.session,E="`"+Kt(C.project||g.display,40)+"`",D=C.checkpoints&&C.checkpoints[C.checkpoints.length-1],O=Jn(g,x,e),F=!!(O&&O.items&&O.items.length),$=ni(x),N=Date.parse(C.updatedAt);F?k.push("• "+E+" "+Kt(C.title,80)+" — "+Kt(O.items[0].text,110)):$!=="ended"&&S.push("• "+E+" "+Kt(C.title,80)+(D&&D.next?" — next: "+Kt(D.next,110):"")),N>=f.from&&N<=f.to&&!F&&w.push("• "+E+" "+Kt(C.title,80)+($==="ended"?" (done)":D?" — "+Kt(D.summary,110):""))}}return{text:["Yesterday:",...w.length?w:["• Nothing logged"],"Today:",...S.length?S:["• Nothing open"],"Blocked:",...k.length?k:["• None"]].join(`
127
- `),space:s||null,counts:{yesterday:v.length,today:S.length,blocked:k.length},window:{from:new Date(f.from).toISOString(),to:new Date(f.to).toISOString(),previousWorkday:f.prev}}}var eb,Kt,id=z(()=>{ve();Ms();cl();Ts();qn();Br();eb=["mon","tue","wed","thu","fri"];Kt=(n,e)=>U(String(n||"").replace(/\s*\n\s*/g," ").replace(/`/g,"'").trim(),e).replace(/[\s.]+$/,"")});function ib(n){return n?n>=1e9?(n/1e9).toFixed(1)+" GB":Math.round(n/1e6)+" MB":""}async function Lm(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 jt({baseUrl:t||lt,fetchImpl:s,env:r}),c=go(l.models,{current:e}).map(f=>({tag:f.tag,name:f.name||f.tag,note:f.note||(f.curated?"":"already on this machine"),hint:[ib(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:Aa({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:mo}}if(a==="ollama-cloud")return{provider:a,choices:Rr.map(l=>dl({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:op};if(a==="anthropic"){let l={haiku:"claude-haiku-5-5",sonnet:"claude-sonnet-5-5"}[e]||e;return{provider:a,choices:ob.map(c=>dl({tag:c.tag,name:c.name,note:c.note,hint:c.cost,recommended:!!c.recommended},l)),status:null,checked:sb}}if(a==="claude"||a==="auto")return{provider:a,choices:rb.map(l=>dl({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 kn({baseUrl:t||Qt,fetchImpl:s});return{provider:a,choices:l.models.map(c=>dl({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 ab(n){return Dm.some(e=>Ge(e,n))}function lb(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 sb,ob,rb,dl,Dm,fl,Rm=z(()=>{Is();sb="2026-10-10",ob=[{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"}],rb=[{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"}];dl=(n,e)=>({...n,current:!!e&&(Ge(e,n.tag)||e===n.tag)});Dm=Dr.map(n=>n.tag);fl=class{constructor({baseUrl:e=lt,fetchImpl:t=fetch,onChange:s=()=>{},pull:o=So,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(!ab(e)){let l=new Error("Only the suggested models can be downloaded from the board: "+Dm.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=lb(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 si from"node:fs";import Pm from"node:path";import Lo from"node:crypto";function Fm(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 hl(n){let e=Fm(String(n||"").replace(/^https?:\/\//i,"").replace(/\/.*$/,""));return!e||cd.includes(e.name)||!/^[a-z0-9.-]+$/.test(e.name)||!e.name.includes(".")?null:e}function Lt(n){let e=hl(n);return e?e.name+(e.port&&e.port!==443?":"+e.port:""):null}function oi(n,e,t=[]){let s=Fm(n);if(!s)return null;if(cd.includes(s.name))return s.port===Number(e)?"loopback":null;for(let o of t||[]){let r=hl(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 Um(n,e,t=[]){if(!n)return!0;let s=String(n).toLowerCase(),o=s.match(/^http:\/\/([^/]+)$/);return o?oi(o[1],e,[])==="loopback":(o=s.match(/^https:\/\/([^/]+)$/),o?oi(o[1],e,t)==="remote":!1)}function pl(n=[]){return(n||[]).map(Lt).filter(Boolean).map(e=>"https://"+e+"/")}function Hm(n){return Pm.join(we(n).secret,"remote.json")}function Po(n){try{let e=JSON.parse(si.readFileSync(Hm(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 Ro(n,e){let t=Hm(n);si.mkdirSync(Pm.dirname(t),{recursive:!0,mode:448});let s=t+".tmp-"+process.pid;si.writeFileSync(s,JSON.stringify(e,null,2)+`
128
- `,{mode:384}),si.renameSync(s,t);try{si.chmodSync(t,384)}catch{}}function ub(n){let e=String(n||"").toUpperCase().replace(/[\s-]+/g,"");if(e.length!==jm)return null;for(let t of e)if(!ld.includes(t))return null;return e}function db(){return Array.from(Lo.randomBytes(jm),n=>ld[n%ld.length]).join("")}function ai(n,{now:e=Date.now(),ttlMs:t=Hs,note:s=null,withCode:o=!0,codeOnly:r=!1}={}){let i=Po(n),a=new Date(e+t).toISOString();i.links=i.links.filter(u=>Us(u,e));let l=null,c=null;return r||(l=Lo.randomBytes(24).toString("base64url"),i.links.push({kind:"link",hash:Do(l),createdAt:new Date(e).toISOString(),expiresAt:a,note:s})),(o||r)&&(c=db(),i.links.push({kind:"code",hash:Do(c),createdAt:new Date(e).toISOString(),expiresAt:a,note:s})),Ro(n,i),{code:l,signInCode:c,expiresAt:a}}function Bm(n,{now:e,userAgent:t,who:s,how:o}){let r=Lo.randomBytes(32).toString("base64url"),i=Lo.randomBytes(6).toString("hex"),a=new Date(e).toISOString();return n.sessions.push({id:i,hash:Do(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 Km(n,e,{now:t=Date.now(),userAgent:s="",who:o=null}={}){if(!e||String(e).length>200)return null;let r=Po(n),i=Do(e),a=r.links.find(c=>c.hash===i&&c.kind!=="code");if(!a||!Us(a,t))return null;a.usedAt=new Date(t).toISOString();let l=Bm(r,{now:t,userAgent:s,who:o,how:"link"});return r.links=r.links.filter(c=>Us(c,t)),r.sessions=r.sessions.filter(c=>ri(c,t)),Ro(n,r),l}function Gm(n,e,{now:t=Date.now(),userAgent:s="",who:o=null}={}){let r=Po(n);if(r.failures=(r.failures||[]).filter(u=>t-Date.parse(u)<Hs),r.failures.length>=ad)return{ok:!1,reason:"locked"};let i=ub(e),a=i?Do(i):null,l=a?r.links.find(u=>u.kind==="code"&&u.hash===a&&Us(u,t)):null;if(!l)return r.failures.push(new Date(t).toISOString()),r.failures.length>=ad&&(r.links=r.links.filter(u=>u.kind!=="code")),Ro(n,r),{ok:!1,reason:r.failures.length>=ad?"locked":"bad_code"};l.usedAt=new Date(t).toISOString();let c=Bm(r,{now:t,userAgent:s,who:o,how:"code"});return r.links=r.links.filter(u=>Us(u,t)),r.sessions=r.sessions.filter(u=>ri(u,t)),Ro(n,r),{ok:!0,...c}}function ri(n,e){return n.lastUsedAt?e-Date.parse(n.lastUsedAt)<ud*1e3:!0}function gl(n,e,{now:t=Date.now(),touch:s=!1}={}){if(!e)return null;let o=Buffer.from(Do(e)),r=Po(n),i=r.sessions.find(c=>{let u=Buffer.from(c.hash);return u.length===o.length&&Lo.timingSafeEqual(u,o)});if(!i||!ri(i,t))return null;if(s&&(!i.lastUsedAt||t-Date.parse(i.lastUsedAt)>cb)){i.lastUsedAt=new Date(t).toISOString(),r.sessions=r.sessions.filter(c=>ri(c,t));try{Ro(n,r)}catch{}}let{hash:a,...l}=i;return{...l,expiresAt:new Date(Date.parse(i.lastUsedAt||new Date(t).toISOString())+ud*1e3).toISOString()}}function yl(n,{maxAgeSec:e=ud}={}){return ii+"="+n+"; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age="+e}function li(n){let t=Po(n).sessions.length;return Ro(n,{links:[],sessions:[],failures:[]}),t}function wl(n){let e=Po(n),t=Date.now();return{sessions:e.sessions.filter(s=>ri(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=>Us(s,t)&&s.kind!=="code").length,pendingCodes:e.links.filter(s=>Us(s,t)&&s.kind==="code").length}}var cd,Hs,ii,ud,cb,ld,jm,ad,Do,ml,Us,Sl=z(()=>{ke();St();cd=["127.0.0.1","[::1]","localhost",wt,qp],Hs=900*1e3,ii="cp_remote",ud=720*3600,cb=3600*1e3,ld="ABCDEFGHJKLMNPQRSTUVWXYZ23456789",jm=6,ad=10;Do=n=>Lo.createHash("sha256").update(String(n)).digest("hex");ml=n=>n&&n.slice(0,3)+"-"+n.slice(3);Us=(n,e)=>!n.usedAt&&Date.parse(n.expiresAt)>e});import{execFile as fb}from"node:child_process";function pb(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 mb(n,{run:e=gb,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(`
129
- `)){let[a,l,c]=i.trim().split(/\s+/);if(a&&/^Z/.test(l||"")){r.set(Number(a),-1);continue}let u=hr(c);a&&u!=null&&r.set(Number(a),t-u*1e3)}return r}).catch(()=>new Map):Promise.resolve(new Map)}function gb(n){return new Promise((e,t)=>{fb("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 $m(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=>pb(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 mb(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=P(c.pidStartedAt);u!=null&&!isNaN(d)&&Math.abs(u-d)>hb?o.set(c.id,{alive:!1,reason:"pid_reused"}):o.set(c.id,{alive:!0,reason:"running"})}return o}var hb,Ym=z(()=>{ve();hb=15e3});import dd from"node:fs";import vl from"node:path";import Zm from"node:os";import{execFile as yb}from"node:child_process";function fd({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(`
130
- `)}function hd(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"+Wm)&&e!=="ok")return{state:"bad",terminals:[]};let t=[];for(let s of e.slice(3).split(Wm)){if(!s)continue;let o=s.split(Sb);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 zm(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=vl.join(Zm.homedir(),t.slice(1))),t.length>1&&(t=t.replace(/\/+$/,"")),e)try{t=dd.realpathSync.native(t)}catch{}return t}function Qm(n){return/^\s*[\u2733\u273b\u273d\u2736\u2722\u00b7*\u2800-\u28ff]\s/.test(String(n||""))}function eg(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?dr(s.id):"");if(r){let c=o.find(u=>String(u.name||"").includes(r));if(c)return{id:c.id,how:"title"}}let i=Jm(s.name),a=i?o.filter(c=>Jm(c.name)===i):[];if(a.length===1)return{id:a[0].id,how:"name"};let l=zm(s.cwd,{realpath:t});if(l){let c=o.filter(u=>u.cwd&&zm(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=>Qm(d.name));return u.length===1?{id:u[0].id,how:"cwd+claude-title"}:{ambiguous:c.length}}}return null}function Jm(n){let e=String(n||"").trim();return Qm(e)&&(e=e.replace(/^\s*\S+\s+/,"")),ms(e).replace(/\s+/g," ").trim().toLowerCase()}function kl(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 qm({resourcesDir:n,home:e=Zm.homedir()}={}){let t=[];if(n){let s=String(n).match(/^(.*?\.app)(?:\/|$)/);s&&t.push(s[1])}return t.push("/Applications/Ghostty.app",vl.join(e,"Applications","Ghostty.app")),[...new Set(t)]}function Vm(n,e=t=>dd.readFileSync(t,"utf8")){try{let s=e(vl.join(n,"Contents","Info.plist")).match(/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/);return s?s[1].trim():null}catch{return null}}function pd({resourcesDir:n,version:e,home:t,readFile:s=o=>dd.readFileSync(o,"utf8")}={}){for(let o of qm({resourcesDir:n,home:t})){let r=null;try{r=s(vl.join(o,"Contents","Resources","Ghostty.sdef"))}catch{}if(r&&/class name="terminal"/.test(r))return{tty:/code="Gtty"/.test(r),version:Vm(o,s)||e||null,source:"sdef",bundle:o}}for(let o of qm({resourcesDir:n,home:t})){let r=Vm(o,s);if(r)return{tty:kl(r)===!0,version:r,source:"Info.plist",bundle:o}}return e?{tty:kl(e)===!0,version:e,source:"TERM_PROGRAM_VERSION"}:{tty:!1,version:null,source:"unknown"}}function xb(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 Gt(n,{env:e,timeoutMs:t=jo}={}){let s=Date.now(),o=e?{...process.env,...e}:process.env;return/(^|\/)osascript$/.test(String(n[0]))&&(o=xb(o)),new Promise(r=>{yb(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 md(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 Tb(n){return Ab[n]||n}async function tg(n,{exec:e=Gt,caps:t,capsFn:s=pd,timeoutMs:o=jo,env:r,explain:i,onFound:a,onStale:l,activate:c="auto",realpath:u=!0}={}){let d=n.ghostty||{},f=Date.now(),p=[],h=async(g,_,x={})=>{let C=Date.now(),E=await e(_,{env:r,timeoutMs:x.timeoutMs||o}),D={step:g,ms:E.ms!=null?E.ms:Date.now()-C,result:E.code===0?String(E.stdout).trim().split(/[\x1e\n]/)[0]||"ok":String(E.code)};return p.push(D),{...E,step:D}},m=g=>({...g,steps:p,elapsedMs:Date.now()-f}),y=g=>g.code==="timeout"?m(md("Ghostty",o,g.step.step)):g.missing?m({ok:!1,code:"missing_osascript",message:"Couldn't run `osascript`."}):m({ok:!1,...i?i(g.stderr,"Ghostty"):{code:"script_failed",message:String(g.stderr).trim()}}),w=()=>m({ok:!1,code:"not_running",message:"Ghostty isn't running."}),S=async g=>{let _=await h("focus by id",["osascript","-e",kb,g]);if(_.code!==0)return{fail:y(_)};let x=_.stdout.trim();return x==="ok"?{ok:!0}:x==="notrunning"?{fail:w()}:x.startsWith("stale")?(_.step.result="stale",{stale:!0}):{fail:m({ok:!1,code:"script_failed",message:'Ghostty answered "'+x.slice(0,80)+'"'})}},k=d.terminalId||null,v=null;if(k){let g=await S(k);if(g.fail)return g.fail;g.ok?v="cached id":(l&&l(k),k=null)}if(!v){let g=t||s({resourcesDir:d.resourcesDir,version:d.version}),_=null;for(let E=0;E<2;E++){let D=await h("list terminals"+(g.tty?" (with tty)":""),["osascript","-e",fd({tty:!!g.tty})]);if(D.code!==0)return y(D);if(_=hd(D.stdout),D.step.result=_.state==="ok"?Xm(_.terminals.length):_.state,_.state!=="changed")break}if(_.state==="notrunning")return w();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 x=eg({id:d.sessionId,marker:d.marker,name:d.name,cwd:d.cwd,tty:g.tty?d.tty:null},_.terminals,{realpath:u});if(!x)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(x.ambiguous)return m({ok:!1,code:"ambiguous",message:x.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(x.id,x.how,_.terminals);let C=await S(x.id);if(C.fail)return C.fail;if(C.stale)return m({ok:!1,code:"not_found",message:"The Ghostty tab closed while switching."});k=x.id,v=x.how}let T=null,b=c||"auto";if(b==="auto"){await wb(150);let g=await h("frontmost check",["osascript","-e",vb],{timeoutMs:3e3});T=g.code===0?g.stdout.trim()==="true":null,T===!1&&(await h("open -a Ghostty",["open","-a","Ghostty"],{timeoutMs:3e3})).code===0&&(T=null)}else b==="open"?await h("open -a Ghostty",["open","-a","Ghostty"],{timeoutMs:3e3}):(b==="applescript"||b==="always")&&await h("activate",["osascript","-e",bb],{timeoutMs:3e3});return m({ok:!0,how:v,terminalId:k,message:"Switched to Ghostty ("+(v==="cached id"?"remembered tab":"matched by "+Tb(v))+")"})}async function xl({exec:n=Gt,caps:e,capsFn:t=pd,timeoutMs:s=jo,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",fd({tty:!!r.tty})],{timeoutMs:s}),c=l.code===0?hd(l.stdout):null;i.push({step:"list terminals"+(r.tty?" (with tty)":""),ms:l.ms,result:c?c.state==="ok"?Xm(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 Al(n){return(n||[]).map(e=>e.step+" "+e.ms+" ms"+(e.result&&e.result!=="ok"?" ("+e.result+")":"")).join(" · ")}var jo,wb,Xm,Wm,Sb,kb,bb,vb,Ab,_b,bl,Fo=z(()=>{ve();jo=Number(process.env.CHECKPOINTS_JUMP_STEP_TIMEOUT_MS)||8e3,wb=n=>new Promise(e=>setTimeout(e,n)),Xm=n=>n+(n===1?" terminal":" terminals"),Wm="",Sb="";kb=`on run argv
129
+ `}}var Ub,Go,_l=J(()=>{Ae();Hs();es();Cs();Ub="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.";Go=n=>n.repo&&n.repo.root||n.cwd||me(n)});function $b(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),!Kb.includes(rn[o.getUTCDay()]));r++);return o.toISOString().slice(0,10)}function Yb({date:n,now:e,tz:t="UTC",digestTime:s="07:00",prevDigestAt:o=null}){let r=$b(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=Yt(a,l,c,u||0,d||0,t)}return{prev:r,from:i,to:e}}function El(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=Yb({date:t,now:e,tz:i,digestTime:a,prevDigestAt:l}),p=n.filter(g=>!s||Cd(o,g.session)===s),h=Gt(p.map(g=>Go(g.session))),m=g=>"`"+(h.get(Go(g))||me(g)||"no repo").replace(/`/g,"'")+"`",y=g=>Jt(g.title||"Untitled session",80),S=[],b=[],w=[],A=[];for(let{session:g,checkpoints:_}of p){if(g.archived||dt(g,_.length))continue;let x=C=>C&&C>=f.from&&C<=f.to,E=_.filter(C=>x(R(C.createdAt))).sort((C,U)=>R(U.createdAt)-R(C.createdAt)),O=x(R(g.endedAt));if(!E.length&&!O&&!x(R(g.lastActivityAt)))continue;let M=O?"done":E[0]?Jt(E[0].stoppedAt||E[0].doing,110):"worked on it";A.push({t:Math.max(R(g.lastActivityAt)||0,R(g.endedAt)||0),line:"• "+m(g)+" "+y(g)+(M==="done"?" (done)":" — "+M)})}A.sort((g,_)=>_.t-g.t),S.push(...A.slice(0,8).map(g=>g.line)),A.length>8&&S.push("• +"+(A.length-8)+" more");let v=Od(p,e,r&&r.staleAfterSec?r:{...r,staleAfterSec:120},f.from);for(let g of v.needsYou){let _=g.session,x=g.latest,E=x&&/^YOU:/i.test(x.next||"")?Jt(x.next.replace(/^YOU:\s*/i,""),110):_.needsInput?"waiting on "+String(_.lastNotification||"input").replace(/_/g," "):"waiting on me";w.push("• "+m(_)+" "+y(_)+" — "+E)}for(let g of[...v.active,...v.dormant.slice(0,d)]){let _=g.session,x=g.latest;b.push("• "+m(_)+" "+y(_)+(x&&x.next?" — next: "+Jt(x.next,110):""))}if(c&&c.agents)for(let g of c.agents){let _=u?u(g):Jn(o||Es(),{id:"agent:"+g.name+":x",agent:{name:g.name,puller:g.puller}}).space;if(!(s&&_!==s))for(let x of g.records||[]){let E=x.session,O="`"+Jt(E.project||g.display,40)+"`",M=E.checkpoints&&E.checkpoints[E.checkpoints.length-1],C=Qn(g,x,e),U=!!(C&&C.items&&C.items.length),W=fi(x),I=Date.parse(E.updatedAt);U?w.push("• "+O+" "+Jt(E.title,80)+" — "+Jt(C.items[0].text,110)):W!=="ended"&&b.push("• "+O+" "+Jt(E.title,80)+(M&&M.next?" — next: "+Jt(M.next,110):"")),I>=f.from&&I<=f.to&&!U&&S.push("• "+O+" "+Jt(E.title,80)+(W==="ended"?" (done)":M?" — "+Jt(M.summary,110):""))}}return{text:["Yesterday:",...S.length?S:["• Nothing logged"],"Today:",...b.length?b:["• Nothing open"],"Blocked:",...w.length?w:["• None"]].join(`
130
+ `),space:s||null,counts:{yesterday:A.length,today:b.length,blocked:w.length},window:{from:new Date(f.from).toISOString(),to:new Date(f.to).toISOString(),previousWorkday:f.prev}}}var Kb,Jt,Nd=J(()=>{Ae();Hs();_l();Cs();es();Xr();Kb=["mon","tue","wed","thu","fri"];Jt=(n,e)=>F(String(n||"").replace(/\s*\n\s*/g," ").replace(/`/g,"'").trim(),e).replace(/[\s.]+$/,"")});function qb(n){return n?n>=1e9?(n/1e9).toFixed(1)+" GB":Math.round(n/1e6)+" MB":""}async function bg(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 Bt({baseUrl:t||ht,fetchImpl:s,env:r}),c=To(l.models,{current:e}).map(f=>({tag:f.tag,name:f.name||f.tag,note:f.note||(f.curated?"":"already on this machine"),hint:[qb(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:Ra({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:Ao}}if(a==="ollama-cloud")return{provider:a,choices:$r.map(l=>Ol({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:Pp};if(a==="anthropic"){let l={haiku:"claude-haiku-5-5",sonnet:"claude-sonnet-5-5"}[e]||e;return{provider:a,choices:zb.map(c=>Ol({tag:c.tag,name:c.name,note:c.note,hint:c.cost,recommended:!!c.recommended},l)),status:null,checked:Wb}}if(a==="claude"||a==="auto")return{provider:a,choices:Jb.map(l=>Ol({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 An({baseUrl:t||on,fetchImpl:s});return{provider:a,choices:l.models.map(c=>Ol({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 Vb(n){return vg.some(e=>We(e,n))}function Zb(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 Wb,zb,Jb,Ol,vg,Cl,xg=J(()=>{Rs();Wb="2026-10-10",zb=[{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"}],Jb=[{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"}];Ol=(n,e)=>({...n,current:!!e&&(We(e,n.tag)||e===n.tag)});vg=Kr.map(n=>n.tag);Cl=class{constructor({baseUrl:e=ht,fetchImpl:t=fetch,onChange:s=()=>{},pull:o=Oo,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(!Vb(e)){let l=new Error("Only the suggested models can be downloaded from the board: "+vg.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=Zb(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 hi from"node:fs";import Ag from"node:path";import Ko from"node:crypto";function _g(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 Nl(n){let e=_g(String(n||"").replace(/^https?:\/\//i,"").replace(/\/.*$/,""));return!e||Ld.includes(e.name)||!/^[a-z0-9.-]+$/.test(e.name)||!e.name.includes(".")?null:e}function Ft(n){let e=Nl(n);return e?e.name+(e.port&&e.port!==443?":"+e.port:""):null}function pi(n,e,t=[]){let s=_g(n);if(!s)return null;if(Ld.includes(s.name))return s.port===Number(e)?"loopback":null;for(let o of t||[]){let r=Nl(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 Eg(n,e,t=[]){if(!n)return!0;let s=String(n).toLowerCase(),o=s.match(/^http:\/\/([^/]+)$/);return o?pi(o[1],e,[])==="loopback":(o=s.match(/^https:\/\/([^/]+)$/),o?pi(o[1],e,t)==="remote":!1)}function Il(n=[]){return(n||[]).map(Ft).filter(Boolean).map(e=>"https://"+e+"/")}function Og(n){return Ag.join(ke(n).secret,"remote.json")}function Wo(n){try{let e=JSON.parse(hi.readFileSync(Og(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 Yo(n,e){let t=Og(n);hi.mkdirSync(Ag.dirname(t),{recursive:!0,mode:448});let s=t+".tmp-"+process.pid;hi.writeFileSync(s,JSON.stringify(e,null,2)+`
131
+ `,{mode:384}),hi.renameSync(s,t);try{hi.chmodSync(t,384)}catch{}}function Qb(n){let e=String(n||"").toUpperCase().replace(/[\s-]+/g,"");if(e.length!==Tg)return null;for(let t of e)if(!Md.includes(t))return null;return e}function ev(){return Array.from(Ko.randomBytes(Tg),n=>Md[n%Md.length]).join("")}function yi(n,{now:e=Date.now(),ttlMs:t=zs,note:s=null,withCode:o=!0,codeOnly:r=!1}={}){let i=Wo(n),a=new Date(e+t).toISOString();i.links=i.links.filter(u=>Ws(u,e));let l=null,c=null;return r||(l=Ko.randomBytes(24).toString("base64url"),i.links.push({kind:"link",hash:$o(l),createdAt:new Date(e).toISOString(),expiresAt:a,note:s})),(o||r)&&(c=ev(),i.links.push({kind:"code",hash:$o(c),createdAt:new Date(e).toISOString(),expiresAt:a,note:s})),Yo(n,i),{code:l,signInCode:c,expiresAt:a}}function Cg(n,{now:e,userAgent:t,who:s,how:o}){let r=Ko.randomBytes(32).toString("base64url"),i=Ko.randomBytes(6).toString("hex"),a=new Date(e).toISOString();return n.sessions.push({id:i,hash:$o(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 Ng(n,e,{now:t=Date.now(),userAgent:s="",who:o=null}={}){if(!e||String(e).length>200)return null;let r=Wo(n),i=$o(e),a=r.links.find(c=>c.hash===i&&c.kind!=="code");if(!a||!Ws(a,t))return null;a.usedAt=new Date(t).toISOString();let l=Cg(r,{now:t,userAgent:s,who:o,how:"link"});return r.links=r.links.filter(c=>Ws(c,t)),r.sessions=r.sessions.filter(c=>mi(c,t)),Yo(n,r),l}function Ig(n,e,{now:t=Date.now(),userAgent:s="",who:o=null}={}){let r=Wo(n);if(r.failures=(r.failures||[]).filter(u=>t-Date.parse(u)<zs),r.failures.length>=Id)return{ok:!1,reason:"locked"};let i=Qb(e),a=i?$o(i):null,l=a?r.links.find(u=>u.kind==="code"&&u.hash===a&&Ws(u,t)):null;if(!l)return r.failures.push(new Date(t).toISOString()),r.failures.length>=Id&&(r.links=r.links.filter(u=>u.kind!=="code")),Yo(n,r),{ok:!1,reason:r.failures.length>=Id?"locked":"bad_code"};l.usedAt=new Date(t).toISOString();let c=Cg(r,{now:t,userAgent:s,who:o,how:"code"});return r.links=r.links.filter(u=>Ws(u,t)),r.sessions=r.sessions.filter(u=>mi(u,t)),Yo(n,r),{ok:!0,...c}}function mi(n,e){return n.lastUsedAt?e-Date.parse(n.lastUsedAt)<Dd*1e3:!0}function Ll(n,e,{now:t=Date.now(),touch:s=!1}={}){if(!e)return null;let o=Buffer.from($o(e)),r=Wo(n),i=r.sessions.find(c=>{let u=Buffer.from(c.hash);return u.length===o.length&&Ko.timingSafeEqual(u,o)});if(!i||!mi(i,t))return null;if(s&&(!i.lastUsedAt||t-Date.parse(i.lastUsedAt)>Xb)){i.lastUsedAt=new Date(t).toISOString(),r.sessions=r.sessions.filter(c=>mi(c,t));try{Yo(n,r)}catch{}}let{hash:a,...l}=i;return{...l,expiresAt:new Date(Date.parse(i.lastUsedAt||new Date(t).toISOString())+Dd*1e3).toISOString()}}function Dl(n,{maxAgeSec:e=Dd}={}){return gi+"="+n+"; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age="+e}function wi(n){let t=Wo(n).sessions.length;return Yo(n,{links:[],sessions:[],failures:[]}),t}function Rl(n){let e=Wo(n),t=Date.now();return{sessions:e.sessions.filter(s=>mi(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=>Ws(s,t)&&s.kind!=="code").length,pendingCodes:e.links.filter(s=>Ws(s,t)&&s.kind==="code").length}}var Ld,zs,gi,Dd,Xb,Md,Tg,Id,$o,Ml,Ws,Pl=J(()=>{ve();Tt();Ld=["127.0.0.1","[::1]","localhost",At,jm],zs=900*1e3,gi="cp_remote",Dd=720*3600,Xb=3600*1e3,Md="ABCDEFGHJKLMNPQRSTUVWXYZ23456789",Tg=6,Id=10;$o=n=>Ko.createHash("sha256").update(String(n)).digest("hex");Ml=n=>n&&n.slice(0,3)+"-"+n.slice(3);Ws=(n,e)=>!n.usedAt&&Date.parse(n.expiresAt)>e});import{execFile as tv}from"node:child_process";function sv(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 ov(n,{run:e=rv,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(`
132
+ `)){let[a,l,c]=i.trim().split(/\s+/);if(a&&/^Z/.test(l||"")){r.set(Number(a),-1);continue}let u=vr(c);a&&u!=null&&r.set(Number(a),t-u*1e3)}return r}).catch(()=>new Map):Promise.resolve(new Map)}function rv(n){return new Promise((e,t)=>{tv("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 Mg(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=>sv(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 ov(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)>nv?o.set(c.id,{alive:!1,reason:"pid_reused"}):o.set(c.id,{alive:!0,reason:"running"})}return o}var nv,Lg=J(()=>{Ae();nv=15e3});import Rd from"node:fs";import Ul from"node:path";import Ug from"node:os";import{execFile as iv}from"node:child_process";function Pd({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(`
133
+ `)}function jd(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"+Dg)&&e!=="ok")return{state:"bad",terminals:[]};let t=[];for(let s of e.slice(3).split(Dg)){if(!s)continue;let o=s.split(lv);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 Rg(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=Ul.join(Ug.homedir(),t.slice(1))),t.length>1&&(t=t.replace(/\/+$/,"")),e)try{t=Rd.realpathSync.native(t)}catch{}return t}function Bg(n){return/^\s*[\u2733\u273b\u273d\u2736\u2722\u00b7*\u2800-\u28ff]\s/.test(String(n||""))}function Gg(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?kr(s.id):"");if(r){let c=o.find(u=>String(u.name||"").includes(r));if(c)return{id:c.id,how:"title"}}let i=Pg(s.name),a=i?o.filter(c=>Pg(c.name)===i):[];if(a.length===1)return{id:a[0].id,how:"name"};let l=Rg(s.cwd,{realpath:t});if(l){let c=o.filter(u=>u.cwd&&Rg(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=>Bg(d.name));return u.length===1?{id:u[0].id,how:"cwd+claude-title"}:{ambiguous:c.length}}}return null}function Pg(n){let e=String(n||"").trim();return Bg(e)&&(e=e.replace(/^\s*\S+\s+/,"")),ks(e).replace(/\s+/g," ").trim().toLowerCase()}function jl(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 jg({resourcesDir:n,home:e=Ug.homedir()}={}){let t=[];if(n){let s=String(n).match(/^(.*?\.app)(?:\/|$)/);s&&t.push(s[1])}return t.push("/Applications/Ghostty.app",Ul.join(e,"Applications","Ghostty.app")),[...new Set(t)]}function Fg(n,e=t=>Rd.readFileSync(t,"utf8")){try{let s=e(Ul.join(n,"Contents","Info.plist")).match(/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/);return s?s[1].trim():null}catch{return null}}function Fd({resourcesDir:n,version:e,home:t,readFile:s=o=>Rd.readFileSync(o,"utf8")}={}){for(let o of jg({resourcesDir:n,home:t})){let r=null;try{r=s(Ul.join(o,"Contents","Resources","Ghostty.sdef"))}catch{}if(r&&/class name="terminal"/.test(r))return{tty:/code="Gtty"/.test(r),version:Fg(o,s)||e||null,source:"sdef",bundle:o}}for(let o of jg({resourcesDir:n,home:t})){let r=Fg(o,s);if(r)return{tty:jl(r)===!0,version:r,source:"Info.plist",bundle:o}}return e?{tty:jl(e)===!0,version:e,source:"TERM_PROGRAM_VERSION"}:{tty:!1,version:null,source:"unknown"}}function fv(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=zo}={}){let s=Date.now(),o=e?{...process.env,...e}:process.env;return/(^|\/)osascript$/.test(String(n[0]))&&(o=fv(o)),new Promise(r=>{iv(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 Ud(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 pv(n){return hv[n]||n}async function Kg(n,{exec:e=qt,caps:t,capsFn:s=Fd,timeoutMs:o=zo,env:r,explain:i,onFound:a,onStale:l,activate:c="auto",realpath:u=!0}={}){let d=n.ghostty||{},f=Date.now(),p=[],h=async(g,_,x={})=>{let E=Date.now(),O=await e(_,{env:r,timeoutMs:x.timeoutMs||o}),M={step:g,ms:O.ms!=null?O.ms:Date.now()-E,result:O.code===0?String(O.stdout).trim().split(/[\x1e\n]/)[0]||"ok":String(O.code)};return p.push(M),{...O,step:M}},m=g=>({...g,steps:p,elapsedMs:Date.now()-f}),y=g=>g.code==="timeout"?m(Ud("Ghostty",o,g.step.step)):g.missing?m({ok:!1,code:"missing_osascript",message:"Couldn't run `osascript`."}):m({ok:!1,...i?i(g.stderr,"Ghostty"):{code:"script_failed",message:String(g.stderr).trim()}}),S=()=>m({ok:!1,code:"not_running",message:"Ghostty isn't running."}),b=async g=>{let _=await h("focus by id",["osascript","-e",cv,g]);if(_.code!==0)return{fail:y(_)};let x=_.stdout.trim();return x==="ok"?{ok:!0}:x==="notrunning"?{fail:S()}:x.startsWith("stale")?(_.step.result="stale",{stale:!0}):{fail:m({ok:!1,code:"script_failed",message:'Ghostty answered "'+x.slice(0,80)+'"'})}},w=d.terminalId||null,A=null;if(w){let g=await b(w);if(g.fail)return g.fail;g.ok?A="cached id":(l&&l(w),w=null)}if(!A){let g=t||s({resourcesDir:d.resourcesDir,version:d.version}),_=null;for(let O=0;O<2;O++){let M=await h("list terminals"+(g.tty?" (with tty)":""),["osascript","-e",Pd({tty:!!g.tty})]);if(M.code!==0)return y(M);if(_=jd(M.stdout),M.step.result=_.state==="ok"?Hg(_.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 x=Gg({id:d.sessionId,marker:d.marker,name:d.name,cwd:d.cwd,tty:g.tty?d.tty:null},_.terminals,{realpath:u});if(!x)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(x.ambiguous)return m({ok:!1,code:"ambiguous",message:x.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(x.id,x.how,_.terminals);let E=await b(x.id);if(E.fail)return E.fail;if(E.stale)return m({ok:!1,code:"not_found",message:"The Ghostty tab closed while switching."});w=x.id,A=x.how}let v=null,k=c||"auto";if(k==="auto"){await av(150);let g=await h("frontmost check",["osascript","-e",dv],{timeoutMs:3e3});v=g.code===0?g.stdout.trim()==="true":null,v===!1&&(await h("open -a Ghostty",["open","-a","Ghostty"],{timeoutMs:3e3})).code===0&&(v=null)}else k==="open"?await h("open -a Ghostty",["open","-a","Ghostty"],{timeoutMs:3e3}):(k==="applescript"||k==="always")&&await h("activate",["osascript","-e",uv],{timeoutMs:3e3});return m({ok:!0,how:A,terminalId:w,message:"Switched to Ghostty ("+(A==="cached id"?"remembered tab":"matched by "+pv(A))+")"})}async function Hl({exec:n=qt,caps:e,capsFn:t=Fd,timeoutMs:s=zo,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",Pd({tty:!!r.tty})],{timeoutMs:s}),c=l.code===0?jd(l.stdout):null;i.push({step:"list terminals"+(r.tty?" (with tty)":""),ms:l.ms,result:c?c.state==="ok"?Hg(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 Bl(n){return(n||[]).map(e=>e.step+" "+e.ms+" ms"+(e.result&&e.result!=="ok"?" ("+e.result+")":"")).join(" · ")}var zo,av,Hg,Dg,lv,cv,uv,dv,hv,mv,Fl,Jo=J(()=>{Ae();zo=Number(process.env.CHECKPOINTS_JUMP_STEP_TIMEOUT_MS)||8e3,av=n=>new Promise(e=>setTimeout(e,n)),Hg=n=>n+(n===1?" terminal":" terminals"),Dg="",lv="";cv=`on run argv
131
134
  set termId to item 1 of argv
132
135
  if application "Ghostty" is not running then return "notrunning"
133
136
  tell application "Ghostty"
@@ -139,10 +142,10 @@ grep and sync. The daemon rebuilds its index from here at start.
139
142
  end try
140
143
  end tell
141
144
  return "ok"
142
- end run`,bb='tell application "Ghostty" to activate',vb=`if application "Ghostty" is not running then return "notrunning"
143
- tell application "Ghostty" to return frontmost as text`;Ab={"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"};_b=600*1e3,bl=class{constructor({exec:e=Gt,log:t=()=>{},now:s=()=>Date.now(),capsFn:o=pd,timeoutMs:r=jo,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>_b)&&(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=eg({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",fd({tty:!!r.tty})],{timeoutMs:this.timeoutMs}),!(a.code!==0||(i=hd(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(`
144
- `).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 Tl from"node:fs";import ci from"node:path";function Cb(n){return'"'+String(n??"").replace(/[\x00-\x08\x0b-\x1f\x7f]/g," ").replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\r?\n/g,"\\n")+'"'}function _l(n){let e=[];return n.cwd&&e.push(n.cwd),n.tty&&e.push(n.tty),e.join(" on ")}function Uo(n,{platform:e=yd()}={}){let t=n||{},s=t.term||t.terminal||Rn({}),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?dr(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(Ob,[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(Ib,[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&&sg[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"+(_l(t)?". It's in "+_l(t):""));default:return a("jumping isn't supported for "+(r||"this terminal")+" yet"+(_l(t)?". It's in "+_l(t):""))}}function gd(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: "+U(t.trim().split(`
145
- `).pop(),200)}}function Nb(n,{env:e=process.env,platform:t=process.platform}={}){for(let s of String(e.PATH||"").split(ci.delimiter).filter(Boolean)){let o=ci.join(s,n);try{return Tl.accessSync(o,Tl.constants.X_OK),o}catch{}}if(t==="darwin")for(let s of Eb[n]||[])try{return Tl.accessSync(s,Tl.constants.X_OK),s}catch{}return null}function og(n,e={}){return Gt(n,e)}async function El(n,{exec:e=og,env:t=process.env,platform:s=yd(),timeoutMs:o=jo,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 tg(n,{exec:e,timeoutMs:o,explain:gd,...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 h=Nb(u.bin,{env:t,platform:s});if(!h&&!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)":"")+"."});h&&(d=[h,...d.slice(1)])}let f=Date.now(),p=await e(d,{env:u.env,timeoutMs:o});if(a.push({step:u.name||(ci.basename(d[0])+(d[0].endsWith("osascript")?"":" "+(d[1]||""))).trim(),ms:p.ms!=null?p.ms:Date.now()-f,result:p.code===0?"ok":String(p.code)}),!u.optional){if(p.code==="timeout")return l(md(n.app||n.label||"The terminal",o));if(p.missing)return l({ok:!1,code:"missing_"+d[0],message:"Couldn't run `"+ci.basename(d[0])+"`."});if(u.parse==="osascript"){if(p.code!==0)return l({ok:!1,...gd(p.stderr,n.app)});let h=p.stdout.trim();if(h.startsWith("ok")){c=h.slice(2).trim()||null;continue}return h.startsWith("ambiguous")?l({ok:!1,code:"ambiguous",message:h.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(p.code!==0)return l({ok:!1,code:"command_failed",message:"`"+[ci.basename(d[0]),...d.slice(1,3)].join(" ")+"` failed: "+U((p.stderr||p.stdout).trim(),200)})}}return l({ok:!0,how:c,message:"Switched to "+(n.label||n.kind)+(c?" (matched by "+c+")":"")})}function ng(n){return String(n||"").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean)}function Mb(n,e){let t=0;for(let s of e)s===n[t]&&t++;return t===n.length}function wd(n,e){let t=String(n||"").trim().toLowerCase();if(!t)return 1;let s=String(e.id||"").toLowerCase(),o=Ke(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],[Yi(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();ng(d).includes(a)?l=Math.max(l,u*4):ng(d).some(f=>f.startsWith(a))?l=Math.max(l,u*3):d.includes(a)?l=Math.max(l,u*2):a.length>=2&&Mb(a,d.replace(/[^a-z0-9]/g,""))&&(l=Math.max(l,u))}if(!l)return 0;i+=l}return i}function rg(n,e){return e.map(t=>({s:t,score:wd(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 Lb(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 Cl(n,{exec:e=og,platform:t=yd(),timeoutMs:s=8e3}={}){let o=sg[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 "+Cb(o)+" to get version"],{timeoutMs:s});if(r.code===0){let a=r.stdout.trim();return n==="ghostty"&&!Lb(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=gd(r.stderr,o);return{kind:n,ok:!1,code:i.code,message:i.message}}var sg,Eb,Ob,Ib,yd,ui=z(()=>{ve();Fo();sg={ghostty:"Ghostty",iterm2:"iTerm2","apple-terminal":"Terminal",wezterm:"WezTerm",kitty:"kitty"},Eb={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"]};Ob=`on run argv
145
+ end run`,uv='tell application "Ghostty" to activate',dv=`if application "Ghostty" is not running then return "notrunning"
146
+ tell application "Ghostty" to return frontmost as text`;hv={"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"};mv=600*1e3,Fl=class{constructor({exec:e=qt,log:t=()=>{},now:s=()=>Date.now(),capsFn:o=Fd,timeoutMs:r=zo,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>mv)&&(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=Gg({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",Pd({tty:!!r.tty})],{timeoutMs:this.timeoutMs}),!(a.code!==0||(i=jd(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(`
147
+ `).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 Gl from"node:fs";import Si from"node:path";function yv(n){return'"'+String(n??"").replace(/[\x00-\x08\x0b-\x1f\x7f]/g," ").replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\r?\n/g,"\\n")+'"'}function Kl(n){let e=[];return n.cwd&&e.push(n.cwd),n.tty&&e.push(n.tty),e.join(" on ")}function qo(n,{platform:e=Bd()}={}){let t=n||{},s=t.term||t.terminal||Un({}),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?kr(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(wv,[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(Sv,[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&&Yg[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"+(Kl(t)?". It's in "+Kl(t):""));default:return a("jumping isn't supported for "+(r||"this terminal")+" yet"+(Kl(t)?". It's in "+Kl(t):""))}}function Hd(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(`
148
+ `).pop(),200)}}function kv(n,{env:e=process.env,platform:t=process.platform}={}){for(let s of String(e.PATH||"").split(Si.delimiter).filter(Boolean)){let o=Si.join(s,n);try{return Gl.accessSync(o,Gl.constants.X_OK),o}catch{}}if(t==="darwin")for(let s of gv[n]||[])try{return Gl.accessSync(s,Gl.constants.X_OK),s}catch{}return null}function Wg(n,e={}){return qt(n,e)}async function $l(n,{exec:e=Wg,env:t=process.env,platform:s=Bd(),timeoutMs:o=zo,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 Kg(n,{exec:e,timeoutMs:o,explain:Hd,...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 h=kv(u.bin,{env:t,platform:s});if(!h&&!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)":"")+"."});h&&(d=[h,...d.slice(1)])}let f=Date.now(),p=await e(d,{env:u.env,timeoutMs:o});if(a.push({step:u.name||(Si.basename(d[0])+(d[0].endsWith("osascript")?"":" "+(d[1]||""))).trim(),ms:p.ms!=null?p.ms:Date.now()-f,result:p.code===0?"ok":String(p.code)}),!u.optional){if(p.code==="timeout")return l(Ud(n.app||n.label||"The terminal",o));if(p.missing)return l({ok:!1,code:"missing_"+d[0],message:"Couldn't run `"+Si.basename(d[0])+"`."});if(u.parse==="osascript"){if(p.code!==0)return l({ok:!1,...Hd(p.stderr,n.app)});let h=p.stdout.trim();if(h.startsWith("ok")){c=h.slice(2).trim()||null;continue}return h.startsWith("ambiguous")?l({ok:!1,code:"ambiguous",message:h.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(p.code!==0)return l({ok:!1,code:"command_failed",message:"`"+[Si.basename(d[0]),...d.slice(1,3)].join(" ")+"` failed: "+F((p.stderr||p.stdout).trim(),200)})}}return l({ok:!0,how:c,message:"Switched to "+(n.label||n.kind)+(c?" (matched by "+c+")":"")})}function $g(n){return String(n||"").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean)}function bv(n,e){let t=0;for(let s of e)s===n[t]&&t++;return t===n.length}function Gd(n,e){let t=String(n||"").trim().toLowerCase();if(!t)return 1;let s=String(e.id||"").toLowerCase(),o=Ye(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],[ta(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();$g(d).includes(a)?l=Math.max(l,u*4):$g(d).some(f=>f.startsWith(a))?l=Math.max(l,u*3):d.includes(a)?l=Math.max(l,u*2):a.length>=2&&bv(a,d.replace(/[^a-z0-9]/g,""))&&(l=Math.max(l,u))}if(!l)return 0;i+=l}return i}function zg(n,e){return e.map(t=>({s:t,score:Gd(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 vv(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 Yl(n,{exec:e=Wg,platform:t=Bd(),timeoutMs:s=8e3}={}){let o=Yg[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 "+yv(o)+" to get version"],{timeoutMs:s});if(r.code===0){let a=r.stdout.trim();return n==="ghostty"&&!vv(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=Hd(r.stderr,o);return{kind:n,ok:!1,code:i.code,message:i.message}}var Yg,gv,wv,Sv,Bd,ki=J(()=>{Ae();Jo();Yg={ghostty:"Ghostty",iterm2:"iTerm2","apple-terminal":"Terminal",wezterm:"WezTerm",kitty:"kitty"},gv={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"]};wv=`on run argv
146
149
  set sid to item 1 of argv
147
150
  tell application "iTerm2"
148
151
  repeat with w in windows
@@ -160,7 +163,7 @@ tell application "Ghostty" to return frontmost as text`;Ab={"cached id":"its rem
160
163
  end repeat
161
164
  end tell
162
165
  return "notfound"
163
- end run`,Ib=`on run argv
166
+ end run`,Sv=`on run argv
164
167
  set ttyPath to item 1 of argv
165
168
  tell application "Terminal"
166
169
  repeat with w in windows
@@ -175,49 +178,49 @@ end run`,Ib=`on run argv
175
178
  end repeat
176
179
  end tell
177
180
  return "notfound"
178
- end run`;yd=()=>process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform});import Db from"node:http";import Rb from"node:https";import di from"node:crypto";import{EventEmitter as ag}from"node:events";function ig(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=di.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 Fb(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=di.randomBytes(16).toString("base64"),l=(i?Rb:Db).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=di.createHash("sha1").update(a+Pb).digest("base64");if(c.headers["sec-websocket-accept"]!==f)return u.destroy(),o(new Error("bad websocket handshake"));u.setNoDelay(!0),s(new kd(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 Pb,jb,Sd,kd,Ho,bd=z(()=>{Pb="258EAFA5-E914-47DA-95CA-C5AB0DC85B11",jb=1024*1024;Sd=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>jb)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 p=0;p<a;p++)f[p]=u[p]^d[p&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})}}},kd=class extends ag{constructor(e,{client:t,head:s=null}){super(),this.socket=e,this.client=t,this.open=!0;let o=new Sd(({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(ig(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(ig(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")}}};Ho=class extends ag{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 Fb(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+"."+di.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=di.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 _d={};Jt(_d,{ACTIONS:()=>Bb,MAX_TTL_MS:()=>cg,PROTOCOL:()=>Bo,PeerError:()=>me,PeerHub:()=>hi,ReplayGuard:()=>Dl,SKEW_MS:()=>ug,TTL_MS:()=>lg,canonical:()=>xt,cardFile:()=>hg,fingerprint:()=>Ad,identityDir:()=>Rl,loadIdentity:()=>Tn,localAddresses:()=>yg,machinesDir:()=>Nl,makeCard:()=>pg,open:()=>vd,publishCard:()=>mg,readCards:()=>Bs,saveIdentity:()=>Ol,seal:()=>Il,trustOf:()=>An,verifyCard:()=>Td});import $t from"node:fs";import xd from"node:os";import nn from"node:path";import Ub from"node:http";import De from"node:crypto";import{EventEmitter as Hb}from"node:events";function xt(n){return n===null||typeof n!="object"?JSON.stringify(n===void 0?null:n):Array.isArray(n)?"["+n.map(xt).join(",")+"]":"{"+Object.keys(n).filter(e=>n[e]!==void 0).sort().map(e=>JSON.stringify(e)+":"+xt(n[e])).join(",")+"}"}function Rl(n=process.env){return n.LEFTOFF_IDENTITY_DIR?n.LEFTOFF_IDENTITY_DIR:nn.join(n.XDG_CONFIG_HOME||nn.join(n.HOME||xd.homedir(),".config"),"checkpoints","identity")}function Ad(n,e){return De.createHash("sha256").update(Vn(n)).update(Vn(e)).digest("hex").slice(0,16).toUpperCase().match(/.{4}/g).join("-")}function Kb(){let n=De.generateKeyPairSync("ed25519"),e=De.generateKeyPairSync("x25519");return{sign:n.privateKey.export({format:"jwk"}),box:e.privateKey.export({format:"jwk"})}}function Gb(n,e){let t=De.createPrivateKey({key:n.sign,format:"jwk"}),s=De.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:Ad(n.sign.x,n.box.x),get allowIncoming(){return!!n.allowIncoming},get pins(){return n.pins||(n.pins={})}}}function Tn(n,{dir:e=Rl()}={}){let t=nn.join(e,dg(n)+".json"),s=null;try{s=JSON.parse($t.readFileSync(t,"utf8"))}catch{}(!s||!s.sign||!s.box)&&(s={v:1,machine:n,created:new Date().toISOString(),...Kb(),allowIncoming:!1,pins:{}},$t.mkdirSync(e,{recursive:!0,mode:448}),fg(t,s));try{$t.chmodSync(t,384)}catch{}return Gb(s,t)}function fg(n,e){let t=n+"."+process.pid+".tmp";$t.writeFileSync(t,JSON.stringify(e,null,2)+`
179
- `,{mode:384}),$t.renameSync(t,n)}function Ol(n){fg(n.file,n.state)}function Nl(n){return nn.join(n,"machines")}function hg(n,e){return nn.join(Nl(n),dg(e)+".json")}function pg(n,{addresses:e=[],relay:t=!1,version:s=null,hostname:o=xd.hostname(),now:r=Date.now(),anywhere:i=null}={}){let a={v:Bo,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=fi(De.sign(null,Buffer.from(xt(a)),n.signPriv)),a}function Td(n){if(!n||typeof n!="object"||!n.sign||!n.box||!n.sig||!n.machine)return!1;try{let{sig:e,...t}=n,s=De.createPublicKey({key:{kty:"OKP",crv:"Ed25519",x:n.sign},format:"jwk"});return De.verify(null,Buffer.from(xt(t)),s,Vn(e))&&Ad(n.sign,n.box)===n.fingerprint}catch{return!1}}function mg(n,e){let t=hg(n,e.machine);try{let o=JSON.parse($t.readFileSync(t,"utf8")),r=i=>{let{updatedAt:a,sig:l,...c}=i;return xt(c)};if(r(o)===r(e)&&Td(o))return{file:t,changed:!1}}catch{}$t.mkdirSync(nn.dirname(t),{recursive:!0});let s=nn.join(nn.dirname(t),"."+nn.basename(t)+"."+process.pid+".tmp");return $t.writeFileSync(s,JSON.stringify(e,null,2)+`
180
- `),$t.renameSync(s,t),{file:t,changed:!0}}function Bs(n){let e={},t=[];try{t=$t.readdirSync(Nl(n)).filter(s=>s.endsWith(".json")&&!s.startsWith("."))}catch{return e}for(let s of t)try{let o=JSON.parse($t.readFileSync(nn.join(Nl(n),s),"utf8"));e[o.machine||s.slice(0,-5)]={card:o,valid:Td(o)}}catch{}return e}function An(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 gg(n,e,t,s){let o=De.createPublicKey({key:{kty:"OKP",crv:"X25519",x:e},format:"jwk"}),r=De.diffieHellman({privateKey:n,publicKey:o});return Buffer.from(De.hkdfSync("sha256",r,t,Buffer.from(s),32))}function Il({me:n,to:e,kind:t="req",body:s,re:o=null,ttlMs:r=lg,now:i=Date.now()}){if(!e||!e.box||!e.machine)throw new me("unknown_target","no published key for that machine",400);let a=De.generateKeyPairSync("x25519"),l=fi(De.randomBytes(16)),c=De.randomBytes(12),u={v:Bo,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:fi(c)},d=gg(a.privateKey,e.box,Vn(l),"leftoff-peer-v1|"+u.from+"|"+u.to),f=De.createCipheriv("aes-256-gcm",d,c);f.setAAD(Buffer.from(xt(Ll(u,Ml))));let p=Buffer.concat([f.update(Buffer.from(JSON.stringify(s))),f.final(),f.getAuthTag()]);return u.ct=fi(p),u.sig=fi(De.sign(null,Buffer.from(xt(Ll(u,[...Ml,"ct"]))),n.signPriv)),u}function Ll(n,e){let t={};for(let s of e)n[s]!==void 0&&(t[s]=n[s]);return t}function vd(n,{me:e,cardOf:t,replay:s,now:o=Date.now(),expectKind:r=null,expectRe:i=null}){if(!n||typeof n!="object"||n.v!==Bo||!n.id||!n.from||!n.ct||!n.sig||!n.epk||!n.iv)throw new me("bad_envelope","not a Leftoff peer envelope",400);if(r&&n.kind!==r)throw new me("bad_envelope","unexpected envelope kind",400);if(n.to!==e.machine)throw new me("wrong_target","this envelope is for "+n.to+", not "+e.machine,400);let a=t(n.from);if(!a||!a.card)throw new me("unknown_sender",n.from+" has no published key in this folder (has the sync caught up?)");if(a.trust==="key_changed")throw new me("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 me("unknown_sender",n.from+"’s published key file doesn’t verify");let l=!1;try{let u=De.createPublicKey({key:{kty:"OKP",crv:"Ed25519",x:a.card.sign},format:"jwk"});l=De.verify(null,Buffer.from(xt(Ll(n,[...Ml,"ct"]))),u,Vn(n.sig))}catch{}if(!l)throw new me("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>cg||n.exp<n.iat)throw new me("bad_envelope","bad lifetime",400);if(o>n.exp)throw new me("expired","the command expired before it arrived ("+Math.round((o-n.exp)/1e3)+" s late; are both clocks right?)");if(n.iat>o+ug)throw new me("not_yet_valid","the command is from the future (check both Macs’ clocks)");if(i&&n.re!==i)throw new me("bad_envelope","reply to a different request",400);if(!s.check(n.from,n.id,n.exp,o))throw new me("replayed","this command was already used once");let c;try{let u=gg(e.boxPriv,n.epk,Vn(n.id),"leftoff-peer-v1|"+n.from+"|"+n.to),d=Vn(n.ct),f=De.createDecipheriv("aes-256-gcm",u,Vn(n.iv));f.setAAD(Buffer.from(xt(Ll(n,Ml)))),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 me("decrypt_failed","couldn’t decrypt (wrong key?)",400)}return{env:n,body:c,sender:a}}function yg(n,e=xd.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 Bo,lg,cg,ug,Bb,fi,Vn,me,dg,Ml,Dl,hi,Zn=z(()=>{bd();Bo=1,lg=60*1e3,cg=120*1e3,ug=30*1e3,Bb=["ping","needs.list","ask.answer","session.reply","session.go","session.turns"],fi=n=>Buffer.from(n).toString("base64url"),Vn=n=>Buffer.from(String(n||""),"base64url"),me=class extends Error{constructor(e,t,s=403){super(t||e),this.code=e,this.status=s}};dg=n=>String(n).replace(/[^\w.-]/g,"_");Ml=["v","kind","from","to","id","iat","exp","re","epk","iv"];Dl=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)}};hi=class extends Hb{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||Tn(s,{dir:i}),this.replay=new Dl,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"})):yg(t)}publish(){let e=pg(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=mg(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,Ol(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 me("unknown_target","no valid key published for "+e,404);return this.id.pins[e]=t.card.fingerprint,Ol(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=Bs(this.root),this.cardsAt=this.now();let t=!1;for(let[s,o]of Object.entries(this.cards))s!==this.machine&&An(this.id,o)==="new"&&(t=!0);if(t)try{Ol(this.id)}catch{}return this.cards}cardOf(e){let t=this.readCards()[e];return t?{card:t.card,trust:An(this.id,t,{pinNew:!1})==="unpinned"?"new":An(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=An(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=vd(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 Il({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:Bo,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)),Il({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});default:throw new me("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=Ub.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:Bo,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 Ho({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 me("no_key",e+" hasn’t published a machine key yet",409);if(i.trust==="key_changed")throw new me("key_changed",e+"’s key changed; confirm it in Settings → Privacy & security",409);if(i.trust==="invalid")throw new me("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 me("no_route","no way to reach "+e+" (no address, no relay)",503);for(let c of l){let u=Il({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(p){a.push(c.via+": "+(p.code||p.cause&&p.cause.code||p.name||p.message));continue}if(d&&d.kind==="err")throw this.mark(e,!0,c.via,null),new me(d.code||"refused",e+" refused the command: "+(d.message||d.code),403);let f=vd(d,{me:this.id,cardOf:p=>this.cardOf(p),replay:this.replay,now:this.now(),expectKind:"res",expectRe:u.id});if(this.mark(e,!0,c.via,null),!f.body.ok){let p=f.body.error||{};throw new me(p.code||"error",p.message||"failed on "+e,p.status||409)}return{result:f.body.result,via:c.via}}throw this.mark(e,!1,null,a.join("; ")),new me("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||An(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 $b,execFileSync as Yb}from"node:child_process";import Pl from"node:process";function Wb(){if(pi!==void 0)return pi;try{pi=Yb("/bin/sh",["-c","command -v terminal-notifier"],{encoding:"utf8",timeout:1500}).trim()||null}catch{pi=null}return pi}function wg({title:n,subtitle:e,message:t,sessionId:s,goBin:o,notifier:r=null}={}){let i=jl(n||"Leftoff",80),a=jl(e,120),l=jl(t,240);if(r){let c=["-title",i,"-subtitle",a,"-message",l||" ","-group","leftoff-"+jl(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 Sg(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 kg(n,{platform:e=Pl.platform,run:t=$b}={}){if(e!=="darwin"&&!Pl.env.CHECKPOINTS_NOTIFY_CMD)return{ok:!1,skipped:"not macOS"};let s;Pl.env.CHECKPOINTS_NOTIFY_CMD?s={cmd:Pl.env.CHECKPOINTS_NOTIFY_CMD,args:wg({...n}).args}:s=wg({...n,notifier:n.notifier!==void 0?n.notifier:Wb()});try{t(s.cmd,s.args,{timeout:5e3},()=>{})}catch(o){return{ok:!1,error:o.message}}return{ok:!0,via:s.cmd}}var pi,jl,bg=z(()=>{jl=(n,e)=>String(n??"").replace(/[\u0000-\u001f]+/g," ").replace(/\s+/g," ").trim().slice(0,e)});import Re from"node:fs";import Xn from"node:os";import Ie from"node:path";import{fileURLToPath as zb}from"node:url";import{spawnSync as Jb}from"node:child_process";function sn(){return process.env.CLAUDE_SETTINGS_PATH||Ie.join(process.env.CLAUDE_CONFIG_DIR||Ie.join(Xn.homedir(),".claude"),"settings.json")}function Fl(n){return/^[A-Za-z0-9_./-]+$/.test(n)?n:JSON.stringify(n)}function Qb(n,e=Fe){return Fl(process.execPath)+" "+Fl(e)+" hook "+n+" --tag="+Zb}function ev(n=Fe){let e={};for(let t of qb)e[t]=[{hooks:[{type:"command",command:Qb(t,n),timeout:5,...Vb[t]||{}}]}];return e}function Ul(n=sn()){try{let e=Re.readFileSync(n,"utf8");return e.trim()?JSON.parse(e):{}}catch{return null}}function gi(n=sn()){let e=Ul(n),t=e?JSON.stringify(e.hooks||{}):"";return vg.some(s=>t.includes(s))}function yi(n,e,t=sn()){let s=Ul(t),o=s||{},r=o.env?o.env[n]:void 0;return e==null&&r===void 0||r===e?!1:(s&&Re.existsSync(t)?Re.writeFileSync(t+".leftoff-backup-"+new Date().toISOString().replace(/[:.]/g,"-"),Re.readFileSync(t,"utf8")):Re.mkdirSync(Ie.dirname(t),{recursive:!0}),e==null?(delete o.env[n],Object.keys(o.env).length||delete o.env):o.env={...o.env||{},[n]:e},Re.writeFileSync(t,JSON.stringify(o,null,2)+`
181
- `),!0)}function Ko(n=sn(),{bin:e=Fe}={}){let t={};if(Re.existsSync(n)){let s=Re.readFileSync(n,"utf8");t=s.trim()?JSON.parse(s):{};let o=n+".leftoff-backup-"+new Date().toISOString().replace(/[:.]/g,"-");Re.writeFileSync(o,s)}else Re.mkdirSync(Ie.dirname(n),{recursive:!0});xg(t),t.hooks=t.hooks||{};for(let[s,o]of Object.entries(ev(e)))t.hooks[s]=[...t.hooks[s]||[],...o];return Re.writeFileSync(n,JSON.stringify(t,null,2)+`
182
- `),n}function xg(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&&Xb(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 Hl(n=sn()){if(!Re.existsSync(n))return 0;let e=JSON.parse(Re.readFileSync(n,"utf8")||"{}"),t=xg(e);return t&&Re.writeFileSync(n,JSON.stringify(e,null,2)+`
183
- `),t}function _n(){return process.env.CHECKPOINTS_SERVICE_PLATFORM||process.platform}function At(n=_n()){return n==="darwin"?Ie.join(Xn.homedir(),"Library","LaunchAgents",Fu+".plist"):Ie.join(process.env.XDG_CONFIG_HOME||Ie.join(Xn.homedir(),".config"),"systemd","user",Rs)}function Go(n=_n()){return n==="darwin"?Ie.join(Xn.homedir(),"Library","LaunchAgents",Yp+".plist"):Ie.join(process.env.XDG_CONFIG_HOME||Ie.join(Xn.homedir(),".config"),"systemd","user",Uu)}function mi(n){return String(n).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function En({root:n=Ie.join(Xn.homedir(),"checkpoints"),bin:e=Fe,platform:t=_n()}={}){let s=Ie.resolve(n)!==Ie.join(Xn.homedir(),"checkpoints"),o=Ie.join(n,".daemon.log");if(t==="darwin"){let r=s?` <key>EnvironmentVariables</key><dict><key>CHECKPOINTS_DIR</key><string>${mi(n)}</string></dict>
181
+ end run`;Bd=()=>process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform});import xv from"node:http";import Av from"node:https";import bi from"node:crypto";import{EventEmitter as qg}from"node:events";function Jg(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=bi.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 Ev(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=bi.randomBytes(16).toString("base64"),l=(i?Av:xv).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=bi.createHash("sha1").update(a+Tv).digest("base64");if(c.headers["sec-websocket-accept"]!==f)return u.destroy(),o(new Error("bad websocket handshake"));u.setNoDelay(!0),s(new $d(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 Tv,_v,Kd,$d,Vo,Yd=J(()=>{Tv="258EAFA5-E914-47DA-95CA-C5AB0DC85B11",_v=1024*1024;Kd=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>_v)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 p=0;p<a;p++)f[p]=u[p]^d[p&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})}}},$d=class extends qg{constructor(e,{client:t,head:s=null}){super(),this.socket=e,this.client=t,this.open=!0;let o=new Kd(({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(Jg(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(Jg(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")}}};Vo=class extends qg{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 Ev(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+"."+bi.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=bi.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 Vd={};Qt(Vd,{ACTIONS:()=>Nv,MAX_TTL_MS:()=>Zg,PROTOCOL:()=>Zo,PeerError:()=>ye,PeerHub:()=>xi,ReplayGuard:()=>Zl,SKEW_MS:()=>Xg,TTL_MS:()=>Vg,canonical:()=>Ct,cardFile:()=>ty,fingerprint:()=>Jd,identityDir:()=>Xl,loadIdentity:()=>Cn,localAddresses:()=>ry,machinesDir:()=>Jl,makeCard:()=>ny,open:()=>Wd,publishCard:()=>sy,readCards:()=>Js,saveIdentity:()=>Wl,seal:()=>zl,trustOf:()=>On,verifyCard:()=>qd});import Vt from"node:fs";import zd from"node:os";import an from"node:path";import Ov from"node:http";import je from"node:crypto";import{EventEmitter as Cv}from"node:events";function Ct(n){return n===null||typeof n!="object"?JSON.stringify(n===void 0?null:n):Array.isArray(n)?"["+n.map(Ct).join(",")+"]":"{"+Object.keys(n).filter(e=>n[e]!==void 0).sort().map(e=>JSON.stringify(e)+":"+Ct(n[e])).join(",")+"}"}function Xl(n=process.env){return n.LEFTOFF_IDENTITY_DIR?n.LEFTOFF_IDENTITY_DIR:an.join(n.XDG_CONFIG_HOME||an.join(n.HOME||zd.homedir(),".config"),"checkpoints","identity")}function Jd(n,e){return je.createHash("sha256").update(ts(n)).update(ts(e)).digest("hex").slice(0,16).toUpperCase().match(/.{4}/g).join("-")}function Iv(){let n=je.generateKeyPairSync("ed25519"),e=je.generateKeyPairSync("x25519");return{sign:n.privateKey.export({format:"jwk"}),box:e.privateKey.export({format:"jwk"})}}function Mv(n,e){let t=je.createPrivateKey({key:n.sign,format:"jwk"}),s=je.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:Jd(n.sign.x,n.box.x),get allowIncoming(){return!!n.allowIncoming},get pins(){return n.pins||(n.pins={})}}}function Cn(n,{dir:e=Xl()}={}){let t=an.join(e,Qg(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(),...Iv(),allowIncoming:!1,pins:{}},Vt.mkdirSync(e,{recursive:!0,mode:448}),ey(t,s));try{Vt.chmodSync(t,384)}catch{}return Mv(s,t)}function ey(n,e){let t=n+"."+process.pid+".tmp";Vt.writeFileSync(t,JSON.stringify(e,null,2)+`
182
+ `,{mode:384}),Vt.renameSync(t,n)}function Wl(n){ey(n.file,n.state)}function Jl(n){return an.join(n,"machines")}function ty(n,e){return an.join(Jl(n),Qg(e)+".json")}function ny(n,{addresses:e=[],relay:t=!1,version:s=null,hostname:o=zd.hostname(),now:r=Date.now(),anywhere:i=null}={}){let a={v:Zo,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=vi(je.sign(null,Buffer.from(Ct(a)),n.signPriv)),a}function qd(n){if(!n||typeof n!="object"||!n.sign||!n.box||!n.sig||!n.machine)return!1;try{let{sig:e,...t}=n,s=je.createPublicKey({key:{kty:"OKP",crv:"Ed25519",x:n.sign},format:"jwk"});return je.verify(null,Buffer.from(Ct(t)),s,ts(e))&&Jd(n.sign,n.box)===n.fingerprint}catch{return!1}}function sy(n,e){let t=ty(n,e.machine);try{let o=JSON.parse(Vt.readFileSync(t,"utf8")),r=i=>{let{updatedAt:a,sig:l,...c}=i;return Ct(c)};if(r(o)===r(e)&&qd(o))return{file:t,changed:!1}}catch{}Vt.mkdirSync(an.dirname(t),{recursive:!0});let s=an.join(an.dirname(t),"."+an.basename(t)+"."+process.pid+".tmp");return Vt.writeFileSync(s,JSON.stringify(e,null,2)+`
183
+ `),Vt.renameSync(s,t),{file:t,changed:!0}}function Js(n){let e={},t=[];try{t=Vt.readdirSync(Jl(n)).filter(s=>s.endsWith(".json")&&!s.startsWith("."))}catch{return e}for(let s of t)try{let o=JSON.parse(Vt.readFileSync(an.join(Jl(n),s),"utf8"));e[o.machine||s.slice(0,-5)]={card:o,valid:qd(o)}}catch{}return e}function On(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 oy(n,e,t,s){let o=je.createPublicKey({key:{kty:"OKP",crv:"X25519",x:e},format:"jwk"}),r=je.diffieHellman({privateKey:n,publicKey:o});return Buffer.from(je.hkdfSync("sha256",r,t,Buffer.from(s),32))}function zl({me:n,to:e,kind:t="req",body:s,re:o=null,ttlMs:r=Vg,now:i=Date.now()}){if(!e||!e.box||!e.machine)throw new ye("unknown_target","no published key for that machine",400);let a=je.generateKeyPairSync("x25519"),l=vi(je.randomBytes(16)),c=je.randomBytes(12),u={v:Zo,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:vi(c)},d=oy(a.privateKey,e.box,ts(l),"leftoff-peer-v1|"+u.from+"|"+u.to),f=je.createCipheriv("aes-256-gcm",d,c);f.setAAD(Buffer.from(Ct(Vl(u,ql))));let p=Buffer.concat([f.update(Buffer.from(JSON.stringify(s))),f.final(),f.getAuthTag()]);return u.ct=vi(p),u.sig=vi(je.sign(null,Buffer.from(Ct(Vl(u,[...ql,"ct"]))),n.signPriv)),u}function Vl(n,e){let t={};for(let s of e)n[s]!==void 0&&(t[s]=n[s]);return t}function Wd(n,{me:e,cardOf:t,replay:s,now:o=Date.now(),expectKind:r=null,expectRe:i=null}){if(!n||typeof n!="object"||n.v!==Zo||!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=je.createPublicKey({key:{kty:"OKP",crv:"Ed25519",x:a.card.sign},format:"jwk"});l=je.verify(null,Buffer.from(Ct(Vl(n,[...ql,"ct"]))),u,ts(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>Zg||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+Xg)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=oy(e.boxPriv,n.epk,ts(n.id),"leftoff-peer-v1|"+n.from+"|"+n.to),d=ts(n.ct),f=je.createDecipheriv("aes-256-gcm",u,ts(n.iv));f.setAAD(Buffer.from(Ct(Vl(n,ql)))),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 ry(n,e=zd.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 Zo,Vg,Zg,Xg,Nv,vi,ts,ye,Qg,ql,Zl,xi,ns=J(()=>{Yd();Zo=1,Vg=60*1e3,Zg=120*1e3,Xg=30*1e3,Nv=["ping","needs.list","ask.answer","session.reply","session.go","session.turns","session.resume","prs.list","session.prlink"],vi=n=>Buffer.from(n).toString("base64url"),ts=n=>Buffer.from(String(n||""),"base64url"),ye=class extends Error{constructor(e,t,s=403){super(t||e),this.code=e,this.status=s}};Qg=n=>String(n).replace(/[^\w.-]/g,"_");ql=["v","kind","from","to","id","iat","exp","re","epk","iv"];Zl=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)}};xi=class extends Cv{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||Cn(s,{dir:i}),this.replay=new Zl,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"})):ry(t)}publish(){let e=ny(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=sy(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,Wl(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,Wl(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=Js(this.root),this.cardsAt=this.now();let t=!1;for(let[s,o]of Object.entries(this.cards))s!==this.machine&&On(this.id,o)==="new"&&(t=!0);if(t)try{Wl(this.id)}catch{}return this.cards}cardOf(e){let t=this.readCards()[e];return t?{card:t.card,trust:On(this.id,t,{pinNew:!1})==="unpinned"?"new":On(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=On(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=Wd(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 zl({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:Zo,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)),zl({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});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=Ov.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:Zo,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 Vo({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=zl({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(p){a.push(c.via+": "+(p.code||p.cause&&p.cause.code||p.name||p.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=Wd(d,{me:this.id,cardOf:p=>this.cardOf(p),replay:this.replay,now:this.now(),expectKind:"res",expectRe:u.id});if(this.mark(e,!0,c.via,null),!f.body.ok){let p=f.body.error||{};throw new ye(p.code||"error",p.message||"failed on "+e,p.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||On(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 Lv,execFileSync as Dv}from"node:child_process";import Ql from"node:process";function Rv(){if(Ai!==void 0)return Ai;try{Ai=Dv("/bin/sh",["-c","command -v terminal-notifier"],{encoding:"utf8",timeout:1500}).trim()||null}catch{Ai=null}return Ai}function iy({title:n,subtitle:e,message:t,sessionId:s,goBin:o,notifier:r=null}={}){let i=ec(n||"Leftoff",80),a=ec(e,120),l=ec(t,240);if(r){let c=["-title",i,"-subtitle",a,"-message",l||" ","-group","leftoff-"+ec(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 ay(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 ly(n,{platform:e=Ql.platform,run:t=Lv}={}){if(e!=="darwin"&&!Ql.env.CHECKPOINTS_NOTIFY_CMD)return{ok:!1,skipped:"not macOS"};let s;Ql.env.CHECKPOINTS_NOTIFY_CMD?s={cmd:Ql.env.CHECKPOINTS_NOTIFY_CMD,args:iy({...n}).args}:s=iy({...n,notifier:n.notifier!==void 0?n.notifier:Rv()});try{t(s.cmd,s.args,{timeout:5e3},()=>{})}catch(o){return{ok:!1,error:o.message}}return{ok:!0,via:s.cmd}}var Ai,ec,cy=J(()=>{ec=(n,e)=>String(n??"").replace(/[\u0000-\u001f]+/g," ").replace(/\s+/g," ").trim().slice(0,e)});import Fe from"node:fs";import ss from"node:os";import Le from"node:path";import{fileURLToPath as Pv}from"node:url";import{spawnSync as jv}from"node:child_process";function ln(){return process.env.CLAUDE_SETTINGS_PATH||Le.join(process.env.CLAUDE_CONFIG_DIR||Le.join(ss.homedir(),".claude"),"settings.json")}function tc(n){return/^[A-Za-z0-9_./-]+$/.test(n)?n:JSON.stringify(n)}function Gv(n,e=Be){return tc(process.execPath)+" "+tc(e)+" hook "+n+" --tag="+Hv}function Kv(n=Be){let e={};for(let t of Fv)e[t]=[{hooks:[{type:"command",command:Gv(t,n),timeout:5,...Uv[t]||{}}]}];return e}function nc(n=ln()){try{let e=Fe.readFileSync(n,"utf8");return e.trim()?JSON.parse(e):{}}catch{return null}}function _i(n=ln()){let e=nc(n),t=e?JSON.stringify(e.hooks||{}):"";return uy.some(s=>t.includes(s))}function Ei(n,e,t=ln()){let s=nc(t),o=s||{},r=o.env?o.env[n]:void 0;return e==null&&r===void 0||r===e?!1:(s&&Fe.existsSync(t)?Fe.writeFileSync(t+".leftoff-backup-"+new Date().toISOString().replace(/[:.]/g,"-"),Fe.readFileSync(t,"utf8")):Fe.mkdirSync(Le.dirname(t),{recursive:!0}),e==null?(delete o.env[n],Object.keys(o.env).length||delete o.env):o.env={...o.env||{},[n]:e},Fe.writeFileSync(t,JSON.stringify(o,null,2)+`
184
+ `),!0)}function Xo(n=ln(),{bin:e=Be}={}){let t={};if(Fe.existsSync(n)){let s=Fe.readFileSync(n,"utf8");t=s.trim()?JSON.parse(s):{};let o=n+".leftoff-backup-"+new Date().toISOString().replace(/[:.]/g,"-");Fe.writeFileSync(o,s)}else Fe.mkdirSync(Le.dirname(n),{recursive:!0});dy(t),t.hooks=t.hooks||{};for(let[s,o]of Object.entries(Kv(e)))t.hooks[s]=[...t.hooks[s]||[],...o];return Fe.writeFileSync(n,JSON.stringify(t,null,2)+`
185
+ `),n}function dy(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&&Bv(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 sc(n=ln()){if(!Fe.existsSync(n))return 0;let e=JSON.parse(Fe.readFileSync(n,"utf8")||"{}"),t=dy(e);return t&&Fe.writeFileSync(n,JSON.stringify(e,null,2)+`
186
+ `),t}function Nn(){return process.env.CHECKPOINTS_SERVICE_PLATFORM||process.platform}function Nt(n=Nn()){return n==="darwin"?Le.join(ss.homedir(),"Library","LaunchAgents",ld+".plist"):Le.join(process.env.XDG_CONFIG_HOME||Le.join(ss.homedir(),".config"),"systemd","user",Ks)}function Qo(n=Nn()){return n==="darwin"?Le.join(ss.homedir(),"Library","LaunchAgents",Lm+".plist"):Le.join(process.env.XDG_CONFIG_HOME||Le.join(ss.homedir(),".config"),"systemd","user",cd)}function Ti(n){return String(n).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function In({root:n=Le.join(ss.homedir(),"checkpoints"),bin:e=Be,platform:t=Nn()}={}){let s=Le.resolve(n)!==Le.join(ss.homedir(),"checkpoints"),o=Le.join(n,".daemon.log");if(t==="darwin"){let r=s?` <key>EnvironmentVariables</key><dict><key>CHECKPOINTS_DIR</key><string>${Ti(n)}</string></dict>
184
187
  `:"";return`<?xml version="1.0" encoding="UTF-8"?>
185
188
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
186
189
  <plist version="1.0"><dict>
187
- <key>Label</key><string>${Fu}</string>
188
- <key>ProgramArguments</key><array><string>${mi(process.execPath)}</string><string>${mi(e)}</string><string>serve</string></array>
190
+ <key>Label</key><string>${ld}</string>
191
+ <key>ProgramArguments</key><array><string>${Ti(process.execPath)}</string><string>${Ti(e)}</string><string>serve</string></array>
189
192
  ${r} <key>ProcessType</key><string>Interactive</string>
190
193
  <key>RunAtLoad</key><true/>
191
194
  <key>KeepAlive</key><true/>
192
- <key>StandardOutPath</key><string>${mi(o)}</string>
193
- <key>StandardErrorPath</key><string>${mi(o)}</string>
195
+ <key>StandardOutPath</key><string>${Ti(o)}</string>
196
+ <key>StandardErrorPath</key><string>${Ti(o)}</string>
194
197
  </dict></plist>
195
198
  `}return`[Unit]
196
199
  Description=Leftoff daemon (Claude Code session tracker)
197
200
 
198
201
  [Service]
199
- ExecStart=${Fl(process.execPath)} ${Fl(e)} serve
202
+ ExecStart=${tc(process.execPath)} ${tc(e)} serve
200
203
  ${s?"Environment="+JSON.stringify("CHECKPOINTS_DIR="+n)+`
201
204
  `:""}Restart=on-failure
202
205
 
203
206
  [Install]
204
207
  WantedBy=default.target
205
- `}function Ag(){return _n()==="darwin"?"launchctl load -w "+At():"systemctl --user daemon-reload && systemctl --user enable --now "+Rs}function Ks(n=_n()){let e=At(n);return n==="darwin"?[["launchctl","unload",e],["launchctl","load","-w",e]]:[["systemctl","--user","daemon-reload"],["systemctl","--user","enable",Rs],["systemctl","--user","restart",Rs]]}function tv(n=_n()){return n==="darwin"?[["launchctl","unload","-w",Go(n)]]:[["systemctl","--user","disable","--now",Uu]]}function Ed(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 Bl({root:n,bin:e=Fe,platform:t=_n(),dryRun:s=!1,run:o=Jb}={}){let r=Go(t);if(!Re.existsSync(r))return null;let i=At(t),a=Re.readFileSync(r,"utf8"),l=Ed(a)||n||Ie.join(Xn.homedir(),"checkpoints"),c=[];if(!s)for(let u of tv(t)){let d=o(u[0],u.slice(1),{encoding:"utf8"});c.push({cmd:u,code:d.status,stderr:String(d.stderr||"").trim()})}return Re.mkdirSync(Ie.dirname(i),{recursive:!0}),Re.writeFileSync(i,En({root:l,bin:e,platform:t})),Re.unlinkSync(r),{from:r,to:i,root:l,steps:c,dryRun:s}}var Fe,H_,qb,Vb,Zb,vg,Xb,Qn=z(()=>{St();Fe=Ie.resolve(Ie.dirname(zb(import.meta.url)),"..","bin","leftoff.js"),H_=Ie.join(Ie.dirname(Fe),"checkpoints.js"),qb=["SessionStart","UserPromptSubmit","Stop","Notification","SessionEnd","PermissionRequest","PostToolUse"],Vb={SessionEnd:{timeout:2},PermissionRequest:{timeout:600},PostToolUse:{timeout:5,async:!0}},Zb=Yr,vg=[Yr,Ds],Xb=n=>vg.some(e=>String(n).includes(e))});import Kl from"node:fs";import _g from"node:path";import{execFileSync as nv}from"node:child_process";function Gl(n){return Math.max(es(n.lastHeartbeatAt),es(n.lastEventAt),es(n.lastActivityAt),es(n.pidAliveAt),es(n.startedAt))}function av(n,e){return Math.max(0,...[n.lastHeartbeatAt,n.lastEventAt,n.lastActivityAt,n.pidAliveAt,n.startedAt].map(es).filter(t=>t&&t<=e))}function Eg(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 Cg({platform:n=process.platform,exec:e=nv,readFile:t=Kl.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 Nd(n,e){try{return(JSON.parse(Kl.readFileSync(Og(n,e),"utf8")).starts||[]).map(es).filter(Boolean).sort((s,o)=>s-o)}catch{return[]}}function Ig(n,e,t=Date.now()){let s=[...Nd(n,e),t].sort((r,i)=>r-i).slice(-20),o=Og(n,e);try{Kl.mkdirSync(_g.dirname(o),{recursive:!0}),Kl.writeFileSync(o,JSON.stringify({machine:e,starts:s.map(r=>G(r))},null,2)+`
206
- `)}catch{}return s}function Ng({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 Mg(n,{machine:e,moments:t=[],sinceHours:s=Id,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 p=l.map(w=>av(w,f.restartAt)).filter(Boolean),h=p.length?Math.max(...p):f.restartAt,m=Math.min(h,f.restartAt)-sv,y=f.upAt+ov;for(let w of l){if(c.has(w.id)||w.archived||w.ignored||!w.cwd||pt(w)||w.surface&&w.surface!=="terminal"||o(w))continue;let S=r.get(w.id);if(S&&i-S<rv)continue;let k=Gl(w);if(!k||k<f.restartAt-s*36e5||k>f.upAt+12e4)continue;let v=es(w.endedAt),T;if(!v)T="never ended";else{if(w.endReason&&iv.has(w.endReason)||v<m||v>y)continue;T=w.endReason==="closed"?"process gone after the restart":w.endReason?"ended ("+w.endReason+") as the Mac shut down":"ended at the restart"}a&&!a(w)||(c.set(w.id,{s:w,lastAliveAt:k,endedAt:v||null,why:T,moment:f.kind}),u=u||f)}}let d=[...c.values()].sort((f,p)=>p.lastAliveAt-f.lastAliveAt);return{moment:u||t[0]||null,sessions:d}}function Cd(n){let e=String(n);return/^[A-Za-z0-9_./~@%+=:,-]+$/.test(e)?e:"'"+e.replace(/'/g,"'\\''")+"'"}function lv(n,e={}){let t=n.title&&n.titleSource!=="none"?n.title:null;return e.tabTitle===!1?t:Gc(e.tabTitleFormat,{name:t,id:n.id,repo:n.repo&&n.repo.name||null,marker:!!e.tabMarker})}function Lg(n,e={}){let t=lv(n,e),s="claude --resume "+Cd(n.id)+(t?" --name "+Cd(t):"");return{cwd:n.cwd,name:t,claude:s,line:"cd "+Cd(n.cwd)+" && "+s}}function cv(){return["on u8(t)",' if t does not start with "'+Od+'" then return t'," set h to text "+(Od.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(`
207
- `)}async function $l(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",cv(),...n.flatMap(c=>[Tg(c.cwd),Tg(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 Id,sv,ov,rv,iv,es,Og,Od,Tg,Md=z(()=>{ve();ke();Id=24,sv=900*1e3,ov=600*1e3,rv=600*1e3,iv=new Set(["prompt_input_exit","clear","logout","bypass_permissions_disabled","resume"]),es=n=>{let e=P(n);return isNaN(e)?0:e};Og=(n,e)=>_g.join(we(n).secret,"daemon-starts-"+String(e||"this").replace(/[^A-Za-z0-9._-]/g,"_")+".json");Od="u8:",Tg=n=>Od+Buffer.from(String(n),"utf8").toString("hex").toUpperCase()});import{EventEmitter as uv}from"node:events";import Je from"node:fs";import dv from"node:os";import $o from"node:path";function hv(n){try{return n()}catch(e){throw e&&e.badWindow?I(400,"bad_window",e.message):e}}async function pv(n){try{return await n()}catch(e){throw e&&e.badWindow?I(400,"bad_window",e.message):e}}function mv(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 Rg(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:dn(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 I(n,e,t){let s=new Error(t);return s.status=n,s.code=e,s}var fv,Dg,Yl,Ld=z(()=>{lo();ve();fu();wn();Cr();ep();fp();mp();Es();Ha();Ka();Is();$a();Vr();mm();km();Ms();cl();Ts();id();Rm();Br();ke();Sl();Ym();ui();Fo();Qr();Zn();qn();bg();Qn();Md();fv=18e4;Dg=(n,e)=>"agent:"+n.name+":"+e.session.id;Yl=class extends uv{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:p,liveChecker:h,jumpRunner:m,liveCheckMs:y,jumpExec:w,ghosttyRefreshMs:S,notifier:k,askTimeoutMs:v,askNoListenerMs:T,bootTime:b,restoreExec:g,platform:_,healer:x,agentFetch:C,agentEnv:E,agentPollMs:D}={}){super(),this.root=e,this.cfg=t||X(e),this.machine=d||le(this.cfg),this.folder=new gt(e,this.machine),this.lastRemoteScan=0,this.updateChecker=f||null,this.updateStartDelayMs=p,this.summarizer=s||new yt(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=g||w||null,this.platform=_||process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform,this.restored=new Map,this.restoring=!1,this.timers=[],this.liveChecker=h||$m,this.jumpRunner=m||El,this.jumpExec=w||null,this.ghostty=new bl({exec:w||Gt,log:O=>this.log(O)}),this.ghosttyRefreshMs=S||Number(process.env.CHECKPOINTS_GHOSTTY_REFRESH_MS)||6e4,this.boardClients=0,this.lastGoAt=0,this.jumping=null,this.liveCheckMs=y||Number(process.env.CHECKPOINTS_LIVE_CHECK_MS)||(Number(this.cfg.liveCheckSec)||30)*1e3,this.asks=new Ja({now:this.now,onChange:(O,F)=>this.onAskChange(O,F),...v||process.env.CHECKPOINTS_ASK_TIMEOUT_MS?{timeoutMs:v||Number(process.env.CHECKPOINTS_ASK_TIMEOUT_MS)}:{},...T||process.env.CHECKPOINTS_ASK_NO_LISTENER_MS?{noListenerMs:T||Number(process.env.CHECKPOINTS_ASK_NO_LISTENER_MS)}:{}}),this.replies=new qa({now:this.now}),this.notifier=k||kg,this.notified=new Map,this.healer=x||(process.env.CHECKPOINTS_HEAL==="off"?null:Gh),this.healTries=new Map,this.healed=0,this.openStateTimer=null,this.lists=new Yn(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 Ea({now:this.now,onUpdate:({cwd:O,branch:F})=>{for(let $ of this.sessions.values())this.prKey($)&&this.prKey($).cwd===O&&this.prKey($).branch===F&&this.emit("session.updated",this.publicSession($));this.emit("needs.updated",{what:"pr"})}}),this.agents=new vt(this.root,this.machine,{now:this.now,log:O=>this.log(O),fetchImpl:C||null,env:E||process.env,userAgent:"Leftoff/"+q}),this.agentPollMs=D||Number(process.env.LEFTOFF_AGENTS_POLL_MS)||Tm,this.spaces=new pa(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)=>P(l.createdAt)-P(c.createdAt)));let a=r[r.length-1];a&&(!o.lastCheckpointAt||P(a.createdAt)>P(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 $o.join(this.root,"."+e+"."+this.machine.replace(/[^\w.-]/g,"_")+".json")}readState(e){try{return JSON.parse(Je.readFileSync(this.stateFile(e),"utf8"))}catch{return null}}writeState(e,t){try{Je.writeFileSync(this.stateFile(e),JSON.stringify(t,null,2)+`
208
- `)}catch{}}loadUiState(){let e=this.readState("ui-state"),t=e;if(t)t.lastRunVersion!==q&&(t={...t,upgradedFrom:t.lastRunVersion||null,upgradeNoticeFor:q,lastRunVersion:q});else{let s=this.sessions.size===0;t={lastRunVersion:q,lastSeenVersion:s?q:null,upgradedFrom:s?null:"an earlier version",upgradeNoticeFor:s?null:q}}this.uiState=t,JSON.stringify(e)!==JSON.stringify(t)&&this.writeState("ui-state",t)}changelog(){let e=this.uiState||{},t=Gp(_o(),e.lastSeenVersion,q);return{current:q,lastSeen:e.lastSeenVersion||null,upgradedFrom:e.upgradedFrom||null,upgradeNotice:e.upgradeNoticeFor===q,entries:t}}markChangelogSeen(e){let t=e||q,s={...this.uiState||{}};return(!s.lastSeenVersion||Ye(t,s.lastSeenVersion)>0)&&(s.lastSeenVersion=Ye(t,q)>0?q: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?Gu-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||qr)({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!==q;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=Ig(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 I(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=nd(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(Fs(e))throw I(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=It();if(!(!e||e.used||e.expiredLogged)){this.stBusy=!0;try{let t=at();if(!t.ok)return;await Ar({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"||kl(s.version)===!0)return!1;let o=c=>ms(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)+`
209
- `;if(t!==JSON.stringify(e)){this.lastOpenState=JSON.stringify(e);try{Je.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(P(o.lastHeartbeatAt)||0,P(o.lastEventAt)||0,P(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||!eo(s,(this.checkpoints.get(s.id)||[]).length))continue;let o=P(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++,eo(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=du(e.transcriptPath);t&&(e.historyTurns=t.prompts||0,e.historyTurns>0&&this.persist(e))}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&&!fr(o.customTitle).id&&(e.title=U(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?P(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),Je.unlinkSync(t.file),e++}catch(s){this.log("spool item skipped: "+t.file+" "+s.message);try{Je.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||!$f.includes(u.type))throw I(400,"bad_event","each event needs sessionId and a known type");if(Fs(String(u.sessionId)))throw I(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 p=this.sessions.get(u.sessionId),h=!p;p||(p={id:u.sessionId,machine:this.machine,turns:0,turnsSinceCheckpoint:0,backgroundTasks:0,pinned:!1,archived:!1,statusChanges:[]},this.sessions.set(p.id,p),this.checkpoints.set(p.id,[]));let m=p.title,y=p.turns;nh(p,{...u,at:u.at||G(t)}),(h||u.data&&u.data.cwd&&!(p.repo&&p.repo.root))&&this.enrichRepo(p),u.type==="session.started"&&p.startSource==="resume"&&p.historyTurns==null&&p.transcriptPath&&this.noteHistory(p),!p.cwd&&p.capture==="mod"&&this.isMine(p)&&this.scheduleHeal(p),p.turns===1&&y===0&&!p.title&&this.autoTitle(p),p.title!==m&&(p.titleChangedAt=G(t)),u.type==="session.ended"&&this.onEnded(p),u.type==="turn_completed"&&u.data&&u.data.listHint&&typeof u.data.listHint=="object"&&this.noteListHint(p,u.data.listHint,u.at||G(t)),this.settleAsksFor(p,u),p.term&&p.term.kind==="ghostty"&&this.isMine(p)&&(u.type==="session.opened"?this.scheduleGhosttyRefresh("session started",1500):p.title!==m&&this.scheduleGhosttyRefresh("renamed",3e3)),s++,r.add(p)}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=Yc(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:fe(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=pm(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 ym(e.cwd);t&&(e.repo={...t,...e.repo||{},root:e.repo&&e.repo.root||t.root,branch:e.repo&&e.repo.branch||t.branch}),wm(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)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)=>P(r.createdAt)-P(i.createdAt))),this.refreshStatus(e),this.emit("session.updated",this.publicSession(e))}}refreshStatus(e,t=this.now()){let s=Pt(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:Cg()),this.bootTimeCache)}isRunningNow(e,t=this.now()){return this.liveInfo(e,Pt(e,t,this.cfgFor(e)),t).live==="open"}restoreView(e,t,s,o=[]){let r=this.publicSession(e,s),i=Lg(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():Gl(e)?new Date(Gl(e)).toISOString():null,endedAt:e.endedAt||null,why:t?t.why:null,name:i.name,command:i.line,claude:i.claude}}restoreCandidates({since:e,repo:t}={},s=this.now()){let o=Eg(e)||Id,r=this.bootTime(),i=Ng({bootAt:r,starts:this.daemonStarts||Nd(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=Mg([...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 I(404,"not_found","no such session: "+o);if(!this.isMine(r))throw I(409,"other_machine",'"'+(r.title||o)+'" ran on '+(r.machine||"another machine")+"; restore it there");if(!r.cwd)throw I(409,"no_folder",'"'+(r.title||o)+'" has no folder on record');if(this.isRunningNow(r,t))throw I(409,"running",'"'+(r.title||o)+'" is already running (use Go)');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 I(400,"nothing","pick at least one session");if(this.restoring)throw I(409,"busy","a restore is already running");this.restoring=!0;try{let r=o.map(c=>this.restoreView(c,null,s,o));if(t)return{ok:!1,how:"print",opened:0,failed:0,sessions:r,message:"the commands, to run yourself"};let i=await $l(r.map(c=>({cwd:c.cwd,claude:c.claude})),{exec:this.restoreExec||Gt,platform:this.platform}),a=r.map((c,u)=>({...c,ok:i.how==="ghostty"?!!(i.results[u]&&i.results[u].ok):!1,error:i.results[u]&&i.results[u].error?i.results[u].error:null})),l=a.filter(c=>c.ok);return l.length&&this.markRestored(l.map(c=>c.id),s),this.log("restore: "+i.message+(i.how==="print"?" ("+o.length+" commands printed)":"")),{ok:i.ok,how:i.how,opened:l.length,failed:a.length-l.length,sessions:a,message:i.message}}finally{this.restoring=!1}}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=Uo(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 I(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 I(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=Uo({...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?" ["+Al(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 xl({exec:this.jumpExec||Gt,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 Cl(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 I(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 I(404,"not_found","no such session");return o.title=U(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 I(404,"not_found","no such session");if(this.assertMine(s),t.title!=null){if(typeof t.title!="string"||!t.title.trim())throw I(400,"bad_title","title must be a non-empty string");s.title=U(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 I(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(!Hc.includes(r))throw I(400,"bad_trigger","trigger must be one of "+Hc.join(", "));for(let u of["doing","stoppedAt","next"])if(typeof t[u]!="string")throw I(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||Ji(e,a),sessionId:e,machine:o.machine||this.machine,createdAt:a,trigger:r,sessionTitle:o.title||null,repo:o.repo?fe(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"},...Rg(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 I(404,"not_found","no such session");if(this.assertMine(i),this.generating.has(e))throw I(409,"busy","a checkpoint is already being generated for this session");this.generating.add(e);try{let{messages:a,meta:l}=i.transcriptPath?vs(i.transcriptPath):{messages:[],meta:{}};l&&l.gitBranch&&!(i.repo&&i.repo.branch)&&(i.repo={...i.repo||{},branch:l.gitBranch});let c=this.checkpoints.get(e)||[],u=c[c.length-1]||null,d=Zi({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 p={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,p,{by:"daemon"}):{...p,...Rg(p),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=Yc(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<fv)))try{await this.generateCheckpoint(t.id,s)}catch(r){this.log("checkpoint failed for "+t.id+": "+r.message)}}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 ll(this.folder.loadAll(),{now:s,cfg:this.cfg,machine:this.machine,date:e&&e!=="today"?e:this.digestDate(s),summarizer:this.summarizer,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 I(400,"bad_date","date is YYYY-MM-DD");let a=s;if(i!==r){let f=this.folder.readDigest(i),p=f?Be(f.markdown).data||{}:{},[h,m,y]=i.split("-").map(Number),[w,S]=String(o.time||"07:00").split(":").map(Number);a=Date.parse(p.generated_at)||Ht(h,m,y,w,S,o.tz)}let l=this.spaces.map({fresh:!0}),c=null;if(e&&e!=="all")try{c=Zt(l,this.knownMachines()).find(f=>f.toLowerCase()===String(e).toLowerCase())||Lr(e)}catch(f){throw I(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 Je.readdirSync($o.join(this.root,"machines")))if(t.endsWith(".json")&&!t.startsWith("."))try{let s=JSON.parse(Je.readFileSync($o.join(this.root,"machines",t),"utf8"));s&&s.machine&&e.add(s.machine)}catch{}}catch{}for(let t of Object.keys(Nt(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=Jh([...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)||fe(a),at:a.lastActivityAt||a.startedAt}}),{home:dv.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:Gn(e,{id:"agent:"+a.name+":x",agent:{name:a.name,puller:a.puller}}).space}))}catch{}return{machine:this.machine,quickPicks:fo,spaces:ho([...Zt(e,t),...fo]),inUse:Zt(e,t),machines:t.map(a=>{let l=e.machines[a];return{machine:a,space:Kn(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=Nr(e.kind)}catch(r){throw I(400,"bad_kind",r.message)}let s=String(e.key==null?"":e.key).trim();if(!s)throw I(400,"bad_key","which "+t.replace(/s$/,"")+"?");let o=e.space;if((o===""||o===void 0)&&(o=null),o!=null)try{o=Lr(o)}catch(r){throw I(400,"bad_space",r.message)}try{this.spaces.update((r,i)=>{r[t][s]={space:o==null?null:Zt(r,this.knownMachines()).concat(fo).find(a=>a.toLowerCase()===o.toLowerCase())||o,at:i.at,by:i.by}})}catch(r){throw I(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=>ut(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)=>(P(o[1].at)||0)-(P(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:U(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=>Qi(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:U(String(t.name||mr({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 I(404,"not_found","no such session");let s=(t.transcriptPath&&Je.existsSync(t.transcriptPath)?t.transcriptPath:null)||uo(e,this.transcriptDirs?{dirs:this.transcriptDirs}:{});if(!s)throw this.isMine(t)?I(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."):I(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}=vs(s,{maxBytes:32*1024*1024}),r=Xi(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=>Qi(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 I(403,"lists_remote_off","Changing lists from another device follows the same switch as answering. On the machine running Leftoff, turn on Settings → Privacy & security → “Allow answering permission prompts and questions from other devices”.")}listError(e){if(e&&e.status)return e;let t=e&&e.code;return t==="no_list"?I(404,"no_list",e.message):t==="no_item"?I(404,"no_item",e.message):t==="ambiguous_list"?I(409,"ambiguous_list",e.message):t==="list_busy"?I(503,"list_busy",e.message):t&&/^empty_/.test(t)?I(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 I(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 I(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 I(404,"no_candidate","no list "+e.fromTranscript+" in that session's transcript");o=u.items,r=r||u.name}if(!o||!o.length)throw I(400,"empty_list","a list needs items (or fromHint / fromTranscript with a sessionId)");let a=s?fe(s):e.repo||null,l=s?this.repoKeyFor(s):e.repo||null,c;try{c=this.lists.create({name:r||mr({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||Qi(o,u.items)>=.6)&&(u.savedRef=c.ref,this.saveListHints())}return this.log("list saved: "+c.ref+" ("+c.items.length+" items"+(s?", from session "+Ke(s.id):"")+")"),this.listsChanged(c,"created"),s&&this.emit("session.updated",this.publicSession(s)),{ok:!0,list:ut(c),context:Pu([ut(c)])}}getList(e){if(String(e||"").startsWith("~")){let t=this.agentListIndex().find(s=>s.ref===e);if(!t)throw I(404,"not_found","no such list");return t}try{return ut(this.lists.resolve(e))}catch(t){throw this.listError(t)}}changeList(e,t={},s={}){if(String(e||"").startsWith("~"))throw I(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 I(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:ut(o),...r}}agentListIndex(){try{return il(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)||(P(a.updated)||0)-(P(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(Fs(e))return{lists:this.agentListIndex().filter(i=>i.sourceSession===e),hint:null,context:null,signature:null,readOnly:!0};if(!this.sessions.get(e))throw I(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:Pu(s),signature:this.listSignature(e)}}catchupEntries(){for(let t of this.sessions.values())this.persistNow(t);let e=[];try{e=al(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 hv(()=>Ua(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 pv(()=>$r(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:Du(t.markdown)}:null}digestSchedule(){return Ma(this.cfg.digest,Ft())}digestTime(){return this.digestSchedule().time}digestDate(e=this.now()){return xo(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=xo(e,t);return{slot:s,...kp({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=yp(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(P(s.lastActivityAt)||0,P(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=Be(r.markdown).data||{};return(!i.machine||i.machine===this.machine||!s.order.includes(i.machine))&&Number(i.sessions)===0&&e-(P(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:q,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-P(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?Be(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:Ft(),zoneAbbr:Au(e,s.tz),label:wp(s.time,s.tz,e),days:s.days,daysLabel:Sp(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,Sm(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(`
210
- `);return(!this.repoLabelMemo||this.repoLabelMemo.sig!==t)&&(this.repoLabelMemo={sig:t,map:en(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=Pt(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:fe(e),repoKey:s,repoLabel:this.repoLabels().get(s)||fe(e),transcriptPath:e.transcriptPath||null,model:e.model||null,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-P(e.lastActivityAt))/1e3)):null,checkpointCount:o.length,latestCheckpoint:r,resumeCommand:Qs(e),statusChanges:e.statusChanges||[],folder:e.folder||null,empty:pt(e,o.length),ghost:eo(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),...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:fe(e),repoLabel:this.repoLabels().get(s)||fe(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:Ke(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=>ko(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||(P(d.lastActivityAt||d.startedAt)||0)-(P(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];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(Fs(e)){let o=this.agentSession(e);if(!o)throw I(404,"not_found","no such session");return o}let t=this.sessions.get(e);if(!t)throw I(404,"not_found","no such session");let s=this.checkpoints.get(e)||[];return{...this.publicSession(t),checkpoints:s.slice(-20).reverse()}}sessionCheckpoints(e,{before:t,limit:s=50}={}){if(Fs(e)){let r=this.agentSession(e);if(!r)throw I(404,"not_found","no such session");return r.checkpoints.filter(i=>!t||P(i.createdAt)<P(t)).slice(0,Math.min(Number(s)||50,500))}if(!this.sessions.has(e))throw I(404,"not_found","no such session");let o=(this.checkpoints.get(e)||[]).slice().reverse();return t&&(o=o.filter(r=>P(r.createdAt)<P(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=>P(i.createdAt)>=P(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)=>P(a.createdAt)-P(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(Pt(t,this.now(),this.cfgFor(t)))||e++;return{ok:!0,version:q,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 hi({engine:this,root:this.root,machine:this.machine,log:this.log,version:q,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 I(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 I(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 I(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=Ns(e.id,o),this.dismissals.has(o.key)?null:o):null}agentNeed(e,t,s=this.now()){let o=Jn(e,t,s);return o?(o.key=Ns(Dg(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 Oa({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 I(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:mv(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(Ns(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(Ns(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||Pt(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-P(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-P(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&&gu.test(o)?yu(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}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&&Je.existsSync(e.transcriptPath))return e.transcriptPath;let t=this.transcriptHits.get(e.id);if(t&&t.dirs===this.transcriptDirs&&(t.file?Je.existsSync(t.file):this.now()-t.at<15e3))return t.file;let s=null;try{s=uo(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=Je.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}=vs(s,{maxBytes:512*1024}),l=Xh(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(Fs(e))throw I(404,"no_transcript","External agents don’t share their transcripts.");let o=this.sessions.get(e);if(!o)throw I(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 I(404,"no_transcript","Claude Code’s transcript for this session isn’t on this machine.");let{messages:i,meta:a}=vs(r,{maxBytes:4*1024*1024});return{sessionId:e,machine:this.machine,branch:o.repo&&o.repo.branch||a.gitBranch||null,turns:Qh(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=Ns(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=Jn(o,r,s);if(!i)continue;i.key=Ns(Dg(o,r),i);let a=nd(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"?Xr(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||fe(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"||P(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||fe(r),repoLabel:this.publicSession(r).repoLabel}});return s.sort((r,i)=>P(i.at)-P(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 I(403,"answers_remote_off","Answering from another device is off. On the machine running Leftoff, turn on Settings → Privacy & security → “Allow answering permission prompts and questions from other devices”.");if(e.proxied&&e.via!=="remote")throw I(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 I(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 I(409,"other_machine","That session runs on "+r.machine);let i=zu(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=Xr(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 I(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 I(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 I(409,"closed","That session is closed.");if(!this.replies.listening(o.id))throw I(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=zu(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:Xr(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=$o.join(this.folder.sessionDir(s),"answers.md"),r=Je.existsSync(o)?"":`# Answered from the Leftoff board
208
+ `}function fy(){return Nn()==="darwin"?"launchctl load -w "+Nt():"systemctl --user daemon-reload && systemctl --user enable --now "+Ks}function qs(n=Nn()){let e=Nt(n);return n==="darwin"?[["launchctl","unload",e],["launchctl","load","-w",e]]:[["systemctl","--user","daemon-reload"],["systemctl","--user","enable",Ks],["systemctl","--user","restart",Ks]]}function $v(n=Nn()){return n==="darwin"?[["launchctl","unload","-w",Qo(n)]]:[["systemctl","--user","disable","--now",cd]]}function Zd(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 oc({root:n,bin:e=Be,platform:t=Nn(),dryRun:s=!1,run:o=jv}={}){let r=Qo(t);if(!Fe.existsSync(r))return null;let i=Nt(t),a=Fe.readFileSync(r,"utf8"),l=Zd(a)||n||Le.join(ss.homedir(),"checkpoints"),c=[];if(!s)for(let u of $v(t)){let d=o(u[0],u.slice(1),{encoding:"utf8"});c.push({cmd:u,code:d.status,stderr:String(d.stderr||"").trim()})}return Fe.mkdirSync(Le.dirname(i),{recursive:!0}),Fe.writeFileSync(i,In({root:l,bin:e,platform:t})),Fe.unlinkSync(r),{from:r,to:i,root:l,steps:c,dryRun:s}}var Be,FE,Fv,Uv,Hv,uy,Bv,os=J(()=>{Tt();Be=Le.resolve(Le.dirname(Pv(import.meta.url)),"..","bin","leftoff.js"),FE=Le.join(Le.dirname(Be),"checkpoints.js"),Fv=["SessionStart","UserPromptSubmit","Stop","Notification","SessionEnd","PermissionRequest","PostToolUse"],Uv={SessionEnd:{timeout:2},PermissionRequest:{timeout:600},PostToolUse:{timeout:5,async:!0}},Hv=ni,uy=[ni,Gs],Bv=n=>uy.some(e=>String(n).includes(e))});import rc from"node:fs";import py from"node:path";import{execFileSync as Yv}from"node:child_process";function ic(n){return Math.max(rs(n.lastHeartbeatAt),rs(n.lastEventAt),rs(n.lastActivityAt),rs(n.pidAliveAt),rs(n.startedAt))}function Vv(n,e){return Math.max(0,...[n.lastHeartbeatAt,n.lastEventAt,n.lastActivityAt,n.pidAliveAt,n.startedAt].map(rs).filter(t=>t&&t<=e))}function my(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 gy({platform:n=process.platform,exec:e=Yv,readFile:t=rc.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 ef(n,e){try{return(JSON.parse(rc.readFileSync(yy(n,e),"utf8")).starts||[]).map(rs).filter(Boolean).sort((s,o)=>s-o)}catch{return[]}}function wy(n,e,t=Date.now()){let s=[...ef(n,e),t].sort((r,i)=>r-i).slice(-20),o=yy(n,e);try{rc.mkdirSync(py.dirname(o),{recursive:!0}),rc.writeFileSync(o,JSON.stringify({machine:e,starts:s.map(r=>K(r))},null,2)+`
209
+ `)}catch{}return s}function Sy({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 ky(n,{machine:e,moments:t=[],sinceHours:s=Qd,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 p=l.map(S=>Vv(S,f.restartAt)).filter(Boolean),h=p.length?Math.max(...p):f.restartAt,m=Math.min(h,f.restartAt)-Wv,y=f.upAt+zv;for(let S of l){if(c.has(S.id)||S.archived||S.ignored||!S.cwd||dt(S)||S.surface&&S.surface!=="terminal"||o(S))continue;let b=r.get(S.id);if(b&&i-b<Jv)continue;let w=ic(S);if(!w||w<f.restartAt-s*36e5||w>f.upAt+12e4)continue;let A=rs(S.endedAt),v;if(!A)v="never ended";else{if(S.endReason&&qv.has(S.endReason)||A<m||A>y)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:A||null,why:v,moment:f.kind}),u=u||f)}}let d=[...c.values()].sort((f,p)=>p.lastAliveAt-f.lastAliveAt);return{moment:u||t[0]||null,sessions:d}}function Zv(n){let e=String(n);return/^[A-Za-z0-9_./~@%+=:,-]+$/.test(e)?e:"'"+e.replace(/'/g,"'\\''")+"'"}function Xv(n,e={}){let t=n.title&&n.titleSource!=="none"?n.title:null;return e.tabTitle===!1?t:au(e.tabTitleFormat,{name:t,id:n.id,repo:n.repo&&n.repo.name||null,marker:!!e.tabMarker})}function tf(n,e={}){let t=Xv(n,e),s=ru(n,["--resume",n.id,...t?["--name",t]:[]]);return{cwd:n.cwd,name:t,claude:s,line:"cd "+Zv(n.cwd)+" && "+s,ollama:_h(n.backend)}}function Qv(){return["on u8(t)",' if t does not start with "'+Xd+'" then return t'," set h to text "+(Xd.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(`
210
+ `)}async function ac(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",Qv(),...n.flatMap(c=>[hy(c.cwd),hy(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 Qd,Wv,zv,Jv,qv,rs,yy,Xd,hy,nf=J(()=>{Ae();ve();Qd=24,Wv=900*1e3,zv=600*1e3,Jv=600*1e3,qv=new Set(["prompt_input_exit","clear","logout","bypass_permissions_disabled","resume"]),rs=n=>{let e=R(n);return isNaN(e)?0:e};yy=(n,e)=>py.join(ke(n).secret,"daemon-starts-"+String(e||"this").replace(/[^A-Za-z0-9._-]/g,"_")+".json");Xd="u8:",hy=n=>Xd+Buffer.from(String(n),"utf8").toString("hex").toUpperCase()});import{EventEmitter as e1}from"node:events";import Ge from"node:fs";import t1 from"node:os";import er from"node:path";function o1(n){try{return n()}catch(e){throw e&&e.badWindow?N(400,"bad_window",e.message):e}}async function r1(n){try{return await n()}catch(e){throw e&&e.badWindow?N(400,"bad_window",e.message):e}}function i1(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 vy(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:mn(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 N(n,e,t){let s=new Error(t);return s.status=n,s.code=e,s}var n1,s1,by,sf,lc,of=J(()=>{wo();Ae();Nu();vn();jr();Mp();Bu();$u();Co();nm();Is();nl();ol();Rs();il();ai();sg();lg();Hs();_l();Cs();Nd();xg();Xr();ve();Pl();Lg();ki();Jo();ui();ns();es();cy();os();nf();n1=async()=>({ok:!1,code:"ENOENT",out:""}),s1=18e4;by=(n,e)=>"agent:"+n.name+":"+e.session.id;sf="Can’t resume through Ollama: ollama isn’t installed on this Mac (ollama.com/download).",lc=class extends e1{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:p,liveChecker:h,jumpRunner:m,liveCheckMs:y,jumpExec:S,ghosttyRefreshMs:b,notifier:w,askTimeoutMs:A,askNoListenerMs:v,bootTime:k,restoreExec:g,platform:_,healer:x,agentFetch:E,agentEnv:O,agentPollMs:M,dirExists:C,findOllama:U,prRun:W,prGhStatus:I,gitRemote:P}={}){super(),this.root=e,this.cfg=t||X(e),this.machine=d||de(this.cfg),this.folder=new vt(e,this.machine),this.lastRemoteScan=0,this.updateChecker=f||null,this.updateStartDelayMs=p,this.summarizer=s||new xt(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=k,this.restoreExec=g||S||null,this.dirExists=C||(B=>{try{return Ge.statSync(B).isDirectory()}catch{return!1}}),this.findOllama=U||(()=>Ls()),this.backendChecked=new Set,this.platform=_||process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform,this.restored=new Map,this.restoring=!1,this.timers=[],this.liveChecker=h||Mg,this.jumpRunner=m||$l,this.jumpExec=S||null,this.ghostty=new Fl({exec:S||qt,log:B=>this.log(B)}),this.ghosttyRefreshMs=b||Number(process.env.CHECKPOINTS_GHOSTTY_REFRESH_MS)||6e4,this.boardClients=0,this.lastGoAt=0,this.jumping=null,this.liveCheckMs=y||Number(process.env.CHECKPOINTS_LIVE_CHECK_MS)||(Number(this.cfg.liveCheckSec)||30)*1e3,this.asks=new ul({now:this.now,onChange:(B,ce)=>this.onAskChange(B,ce),...A||process.env.CHECKPOINTS_ASK_TIMEOUT_MS?{timeoutMs:A||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 dl({now:this.now}),this.notifier=w||ly,this.notified=new Map,this.healer=x||(process.env.CHECKPOINTS_HEAL==="off"?null:bp),this.healTries=new Map,this.healed=0,this.openStateTimer=null,this.lists=new Vn(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 Fa({now:this.now,onUpdate:({cwd:B,branch:ce})=>{for(let $ of this.sessions.values())this.prKey($)&&this.prKey($).cwd===B&&this.prKey($).branch===ce&&this.emit("session.updated",this.publicSession($));this.emit("needs.updated",{what:"pr"})}}),this.prStore=new Ga({run:W||(process.env.NODE_TEST_CONTEXT?n1:Ku),now:this.now,onUpdate:()=>this.prsChanged()}),this.prGhStatus=I||(process.env.NODE_TEST_CONTEXT?async()=>({status:"missing"}):B=>ja(B)),this.gitRemote=P||null,this.prScans=new Map,this.githubRepos=new Map,this.prPeer=null,this.agents=new Ot(this.root,this.machine,{now:this.now,log:B=>this.log(B),fetchImpl:E||null,env:O||process.env,userAgent:"Leftoff/"+V}),this.agentPollMs=M||Number(process.env.LEFTOFF_AGENTS_POLL_MS)||hg,this.spaces=new Ta(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 er.join(this.root,"."+e+"."+this.machine.replace(/[^\w.-]/g,"_")+".json")}readState(e){try{return JSON.parse(Ge.readFileSync(this.stateFile(e),"utf8"))}catch{return null}}writeState(e,t){try{Ge.writeFileSync(this.stateFile(e),JSON.stringify(t,null,2)+`
211
+ `)}catch{}}loadUiState(){let e=this.readState("ui-state"),t=e;if(t)t.lastRunVersion!==V&&(t={...t,upgradedFrom:t.lastRunVersion||null,upgradeNoticeFor:V,lastRunVersion:V});else{let s=this.sessions.size===0;t={lastRunVersion:V,lastSeenVersion:s?V:null,upgradedFrom:s?null:"an earlier version",upgradeNoticeFor:s?null:V}}this.uiState=t,JSON.stringify(e)!==JSON.stringify(t)&&this.writeState("ui-state",t)}changelog(){let e=this.uiState||{},t=Im(Ro(),e.lastSeenVersion,V);return{current:V,lastSeen:e.lastSeenVersion||null,upgradedFrom:e.upgradedFrom||null,upgradeNotice:e.upgradeNoticeFor===V,entries:t}}markChangelogSeen(e){let t=e||V,s={...this.uiState||{}};return(!s.lastSeenVersion||Je(t,s.lastSeenVersion)>0)&&(s.lastSeenVersion=Je(t,V)>0?V: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?hd-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||ii)({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!==V;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=wy(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 N(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=_d(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(zt(e))throw N(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=Rt();if(!(!e||e.used||e.expiredLogged)){this.stBusy=!0;try{let t=ft();if(!t.ok)return;await Lr({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"||jl(s.version)===!0)return!1;let o=c=>ks(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:K(Date.now()),sessions:e},null,2)+`
212
+ `;if(t!==JSON.stringify(e)){this.lastOpenState=JSON.stringify(e);try{Ge.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||!co(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:K(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;dt(o,r)&&(e++,co(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=Cu(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||io(t))return!1;if(!e.backend){let s=Ah(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}=Kn(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&&!br(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=K(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),Ge.unlinkSync(t.file),e++}catch(s){this.log("spool item skipped: "+t.file+" "+s.message);try{Ge.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||!mh.includes(u.type))throw N(400,"bad_event","each event needs sessionId and a known type");if(zt(String(u.sessionId)))throw N(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 p=this.sessions.get(u.sessionId),h=!p;p||(p={id:u.sessionId,machine:this.machine,turns:0,turnsSinceCheckpoint:0,backgroundTasks:0,pinned:!1,archived:!1,statusChanges:[]},this.sessions.set(p.id,p),this.checkpoints.set(p.id,[]));let m=p.title,y=p.turns;Dh(p,{...u,at:u.at||K(t)}),(h||u.data&&u.data.cwd&&!(p.repo&&p.repo.root))&&this.enrichRepo(p),u.type==="session.started"&&p.startSource==="resume"&&p.historyTurns==null&&p.transcriptPath&&this.noteHistory(p),!p.cwd&&p.capture==="mod"&&this.isMine(p)&&this.scheduleHeal(p),p.turns===1&&y===0&&!p.title&&this.autoTitle(p),p.title!==m&&(p.titleChangedAt=K(t)),u.type==="session.ended"&&this.onEnded(p),u.type==="turn_completed"&&u.data&&u.data.listHint&&typeof u.data.listHint=="object"&&this.noteListHint(p,u.data.listHint,u.at||K(t)),this.settleAsksFor(p,u),p.term&&p.term.kind==="ghostty"&&this.isMine(p)&&(u.type==="session.opened"?this.scheduleGhosttyRefresh("session started",1500):p.title!==m&&this.scheduleGhosttyRefresh("renamed",3e3)),s++,r.add(p)}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=cu(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=ng(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 og(e.cwd);t&&(e.repo={...t,...e.repo||{},root:e.repo&&e.repo.root||t.root,branch:e.repo&&e.repo.branch||t.branch}),ig(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))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=kt(e,t,this.cfgFor(e));return s!==e.status?(e.status=s,e.statusChanges=[...e.statusChanges||[],{at:K(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:gy()),this.bootTimeCache)}isRunningNow(e,t=this.now()){return this.liveInfo(e,kt(e,t,this.cfgFor(e)),t).live==="open"}restoreView(e,t,s,o=[]){let r=this.publicSession(e,s),i=tf(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():ic(e)?new Date(ic(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:sf}:{}}}restoreCandidates({since:e,repo:t}={},s=this.now()){let o=my(e)||Qd,r=this.bootTime(),i=Sy({bootAt:r,starts:this.daemonStarts||ef(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=ky([...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 N(404,"not_found","no such session: "+o);if(!this.isMine(r))throw N(409,"other_machine",'"'+(r.title||o)+'" ran on '+(r.machine||"another machine")+"; restore it there");if(!r.cwd)throw N(409,"no_folder",'"'+(r.title||o)+'" has no folder on record');if(this.isRunningNow(r,t))throw N(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 N(400,"nothing","pick at least one session");if(this.restoring)throw N(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 ac(i.map(d=>({cwd:d.cwd,claude:d.claude})),{exec:this.restoreExec||qt,platform:this.platform}):{ok:!1,how:"ghostty",results:[],message:sf},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 N(404,"not_found","no such session");if(!this.isMine(o)){if(t)throw N(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 N(409,"folder_gone","Can’t resume: "+r.cwd+" isn’t there any more.");if(i.ollama&&!this.findOllama())throw N(409,"no_ollama",sf);let a=await this.restore([e],{},s),l=a.sessions[0]||{};if(a.how!=="ghostty")throw N(409,"no_ghostty","Couldn’t open Ghostty: "+a.message+". Use Copy resume.");if(!l.ok)throw N(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,limit:r=30,offset:i=0}={},a=this.now()){let l=e?String(e).toLowerCase().trim():null,c=[];for(let h of this.sessions.values()){if(h.archived||h.ignored||!h.endedAt||kt(h,a,this.cfgFor(h))!=="ended"||t&&(h.machine||this.machine)!==t)continue;let m=this.publicSession(h,a);m.empty||o&&m.space!==o||l&&!this.matches(m,l)||c.push(m)}c.sort((h,m)=>(R(m.endedAt)||0)-(R(h.endedAt)||0));let u=sm(c);if(s){let h=String(s).split(",").map(m=>m.trim()).filter(Boolean);c=c.filter(m=>h.some(y=>Us(m,y)))}let d=Math.max(0,Number(i)||0),f=Math.max(1,Math.min(Number(r)||30,200));return{sessions:c.slice(d,d+f).map(h=>{let m=this.sessions.get(h.id);return m&&this.inferBackend(m)?this.publicSession(m,a):h}),total:c.length,more:c.length>d+f,repos:u}}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||kt(o,t,this.cfgFor(o))==="ended"&&(dt(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=K(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=qo(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 N(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 N(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=qo({...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?" ["+Bl(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 Hl({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 Yl(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 N(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&&!dt(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 N(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 N(404,"not_found","no such session");if(this.assertMine(s),t.title!=null){if(typeof t.title!="string"||!t.title.trim())throw N(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 N(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||K(this.now()),statusChanges:[]},this.sessions.set(e,o),this.checkpoints.set(e,[]));let r=t.trigger||"manual";if(!su.includes(r))throw N(400,"bad_trigger","trigger must be one of "+su.join(", "));for(let u of["doing","stoppedAt","next"])if(typeof t[u]!="string")throw N(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=K(t.createdAt||this.now()),l={id:t.id||oa(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"},...vy(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 N(404,"not_found","no such session");if(this.assertMine(i),this.generating.has(e))throw N(409,"busy","a checkpoint is already being generated for this session");this.generating.add(e);try{let{messages:a,meta:l}=i.transcriptPath?Kn(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=la({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 p={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,p,{by:"daemon"}):{...p,...vy(p),sessionId:e,preview:!0,sessionTitle:i.title||null,createdAt:K(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=cu(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<s1)))try{await this.generateCheckpoint(t.id,s)}catch(r){this.log("checkpoint failed for "+t.id+": "+r.message)}}this.scanPrLinksTick(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 Tl(this.folder.loadAll(),{now:s,cfg:this.cfg,machine:this.machine,date:e&&e!=="today"?e:this.digestDate(s),summarizer:this.summarizer,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:K(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:K(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 N(400,"bad_date","date is YYYY-MM-DD");let a=s;if(i!==r){let f=this.folder.readDigest(i),p=f?$e(f.markdown).data||{}:{},[h,m,y]=i.split("-").map(Number),[S,b]=String(o.time||"07:00").split(":").map(Number);a=Date.parse(p.generated_at)||Yt(h,m,y,S,b,o.tz)}let l=this.spaces.map({fresh:!0}),c=null;if(e&&e!=="all")try{c=nn(l,this.knownMachines()).find(f=>f.toLowerCase()===String(e).toLowerCase())||Gr(e)}catch(f){throw N(400,"bad_space",f.message)}let u=null;try{u=this.agents.view({fresh:!0})}catch{}return{...El(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 Ge.readdirSync(er.join(this.root,"machines")))if(t.endsWith(".json")&&!t.startsWith("."))try{let s=JSON.parse(Ge.readFileSync(er.join(this.root,"machines",t),"utf8"));s&&s.machine&&e.add(s.machine)}catch{}}catch{}for(let t of Object.keys(Pt(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=_p([...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:t1.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:Jn(e,{id:"agent:"+a.name+":x",agent:{name:a.name,puller:a.puller}}).space}))}catch{}return{machine:this.machine,quickPicks:bo,spaces:vo([...nn(e,t),...bo]),inUse:nn(e,t),machines:t.map(a=>{let l=e.machines[a];return{machine:a,space:zn(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=Hr(e.kind)}catch(r){throw N(400,"bad_kind",r.message)}let s=String(e.key==null?"":e.key).trim();if(!s)throw N(400,"bad_key","which "+t.replace(/s$/,"")+"?");let o=e.space;if((o===""||o===void 0)&&(o=null),o!=null)try{o=Gr(o)}catch(r){throw N(400,"bad_space",r.message)}try{this.spaces.update((r,i)=>{r[t][s]={space:o==null?null:nn(r,this.knownMachines()).concat(bo).find(a=>a.toLowerCase()===o.toLowerCase())||o,at:i.at,by:i.by}})}catch(r){throw N(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=>mt(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=>ua(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||Ar({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 N(404,"not_found","no such session");let s=(t.transcriptPath&&Ge.existsSync(t.transcriptPath)?t.transcriptPath:null)||ko(e,this.transcriptDirs?{dirs:this.transcriptDirs}:{});if(!s)throw this.isMine(t)?N(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."):N(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}=Kn(s,{maxBytes:32*1024*1024}),r=ca(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=>ua(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 N(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"?N(404,"no_list",e.message):t==="no_item"?N(404,"no_item",e.message):t==="ambiguous_list"?N(409,"ambiguous_list",e.message):t==="list_busy"?N(503,"list_busy",e.message):t&&/^empty_/.test(t)?N(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 N(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 N(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 N(404,"no_candidate","no list "+e.fromTranscript+" in that session's transcript");o=u.items,r=r||u.name}if(!o||!o.length)throw N(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||Ar({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||ua(o,u.items)>=.6)&&(u.savedRef=c.ref,this.saveListHints())}return this.log("list saved: "+c.ref+" ("+c.items.length+" items"+(s?", from session "+Ye(s.id):"")+")"),this.listsChanged(c,"created"),s&&this.emit("session.updated",this.publicSession(s)),{ok:!0,list:mt(c),context:id([mt(c)])}}getList(e){if(String(e||"").startsWith("~")){let t=this.agentListIndex().find(s=>s.ref===e);if(!t)throw N(404,"not_found","no such list");return t}try{return mt(this.lists.resolve(e))}catch(t){throw this.listError(t)}}changeList(e,t={},s={}){if(String(e||"").startsWith("~"))throw N(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 N(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:mt(o),...r}}agentListIndex(){try{return xl(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(zt(e))return{lists:this.agentListIndex().filter(i=>i.sourceSession===e),hint:null,context:null,signature:null,readOnly:!0};if(!this.sessions.get(e))throw N(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:id(s),signature:this.listSignature(e)}}catchupEntries(){for(let t of this.sessions.values())this.persistNow(t);let e=[];try{e=Al(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 o1(()=>tl(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 r1(()=>ti(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:od(t.markdown)}:null}digestSchedule(){return Ja(this.cfg.digest,Kt())}digestTime(){return this.digestSchedule().time}digestDate(e=this.now()){return Mo(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=Mo(e,t);return{slot:s,...lm({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 K(s.at);let a=rm(e,t,{doneToday:r&&!i||o.isDay&&o.past&&!i});return a==null?null:K(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=$e(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:V,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?$e(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:Kt(),zoneAbbr:Ju(e,s.tz),label:im(s.time,s.tz,e),days:s.days,daysLabel:am(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,ag(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(`
213
+ `);return(!this.repoLabelMemo||this.repoLabelMemo.sig!==t)&&(this.repoLabelMemo={sig:t,map:Gt(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=kt(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:Th(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:lo(e),statusChanges:e.statusChanges||[],folder:e.folder||null,empty:dt(e,o.length),ghost:co(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.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:Ye(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=>Us(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];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(zt(e)){let o=this.agentSession(e);if(!o)throw N(404,"not_found","no such session");return o}let t=this.sessions.get(e);if(!t)throw N(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(zt(e)){let r=this.agentSession(e);if(!r)throw N(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 N(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(kt(t,this.now(),this.cfgFor(t)))||e++;return{ok:!0,version:V,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 xi({engine:this,root:this.root,machine:this.machine,log:this.log,version:V,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 N(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 N(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 N(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=Fs(e.id,o),this.dismissals.has(o.key)?null:o):null}agentNeed(e,t,s=this.now()){let o=Qn(e,t,s);return o?(o.key=Fs(by(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 Ya({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 N(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:i1(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(Fs(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(Fs(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||kt(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&&Du.test(o)?Ru(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):rg(t)).catch(()=>null).then(r=>{let i=Ka(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||zt(e.id))return[];let t=new Set(Vr(e.prUnlinked)),s=[];for(let i of qr(e.prLinks))t.has(i.url)||s.push({...js(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?Xp(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||zt(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=Ge.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=Qp(t,o.offset);if(o.offset=r.next,this.prScans.set(e.id,o),!r.text)return!0;let i=qp(r.text,{pending:o.pending});if(!i.length)return!0;let a=Vp(qr(e.prLinks),i,Vr(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}async linkPr(e,t={},{fromPeer:s=!1}={}){if(zt(e))throw N(400,"agent_session","External agents don’t link PRs.");let o=this.sessions.get(e);if(!o)throw N(404,"not_found","no such session");let r=!!t.unlink;if(!this.isMine(o)){if(s)throw N(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=Vr(f.prUnlinked));let p=this.publicSession(o);return this.emit("session.updated",p),this.prsChanged(),{...f,prs:p.prs,peer:o.machine}}let i=await this.ensureGithub(o),a=zp(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 N(400,a.error==="no_repo"?"no_repo":"bad_ref",d)}let l=qr(o.prLinks),c=Vr(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=K(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&&!zt(w.id)),c=new Map;for(let w of l)if(w.repo&&!w.repo.github&&(w.repo.root||w.cwd)){let A=w.repo.root||w.cwd;c.has(A)||c.set(A,[]),c.get(A).push(w)}await Promise.all([...c.values()].slice(0,60).map(async w=>{let A=await this.ensureGithub(w[0]);if(A)for(let v of w.slice(1))v.repo&&!v.repo.github&&(v.repo={...v.repo,github:A},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}),p=new Map,h=(w,A)=>(R(A.lastActivityAt)||0)-(R(w.lastActivityAt)||0);for(let w of l.slice().sort(h))for(let A of this.sessionPrRefs(w)){let v=p.get(A.url);v||p.set(A.url,v={ref:A,via:new Set,sessionIds:[]}),v.via.add(A.via),v.sessionIds.includes(w.id)||v.sessionIds.push(w.id)}let m=r.account||null,y,S=0;if(e==="mine"){f=await this.prStore.settle({urls:[...p.keys()],waitMs:o,fresh:t})&&f;let w=[];for(let[A,v]of p){let k=this.prStore.views.get(A);if(!k||!k.pr){S++;continue}w.push({pr:k.pr,via:[...v.via],sessionIds:v.sessionIds})}y=Zp(w,{now:this.now(),me:m})}else y=u.map(w=>{let A=this.prStore.lists.get(w),v=A&&A.prs;return{key:w,label:w,failed:!v,rows:(v||[]).slice().sort((k,g)=>String(g.updatedAt||"").localeCompare(String(k.updatedAt||""))).map(k=>{let g=p.get(k.url);return{pr:k,attention:Gu(k,m),via:g?[...g.via]:[],sessionIds:g?g.sessionIds:[]}})}});let b={view:e,gh:i,me:m,machine:this.machine,servedBy:this.machine,repos:u,groups:y,unread:S,loading:!f||this.prStore.busy(),updatedAt:a};return s?b:this.hydratePrs(b)}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&&Ge.existsSync(e.transcriptPath))return e.transcriptPath;let t=this.transcriptHits.get(e.id);if(t&&t.dirs===this.transcriptDirs&&(t.file?Ge.existsSync(t.file):this.now()-t.at<15e3))return t.file;let s=null;try{s=ko(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=Ge.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}=Kn(s,{maxBytes:512*1024}),l=Np(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(zt(e))throw N(404,"no_transcript","External agents don’t share their transcripts.");let o=this.sessions.get(e);if(!o)throw N(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 N(404,"no_transcript","Claude Code’s transcript for this session isn’t on this machine.");let{messages:i,meta:a}=Kn(r,{maxBytes:4*1024*1024});return{sessionId:e,machine:this.machine,branch:o.repo&&o.repo.branch||a.gitBranch||null,turns:Ip(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=Fs(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=Qn(o,r,s);if(!i)continue;i.key=Fs(by(o,r),i);let a=_d(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"?ci(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 N(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 N(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 N(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 N(409,"other_machine","That session runs on "+r.machine);let i=yd(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=ci(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 N(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 N(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 N(409,"closed","That session is closed.");if(!this.replies.listening(o.id))throw N(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=yd(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:ci(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=er.join(this.folder.sessionDir(s),"answers.md"),r=Ge.existsSync(o)?"":`# Answered from the Leftoff board
211
214
 
212
215
  Every answer given from the board for this session: when, what was asked, the answer, and where it came from.
213
216
 
214
217
  `,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":"")+`
215
- `;Je.mkdirSync($o.dirname(o),{recursive:!0}),Je.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=Sg(i,e.title||fe(e)),c=this.notifier({title:"Leftoff",subtitle:l.subtitle,message:l.message,sessionId:e.id,goBin:Fe});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(Lt).filter(Boolean);return{enabled:e.length>0,hosts:e,urls:pl(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(Lt).filter(Boolean),r=new Set(o(t)),i=(this.cfg.remoteHosts||[]).map(Lt).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),no(this.cfg,this.root),this.remoteHealth()}reloadRemote(){let e=X(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 $o.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 I(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{Je.writeFileSync(this.modHealthFile(),JSON.stringify(this._modHealth,null,2)+`
216
- `)}catch{}return this.emit("session.updated",{mod:!0}),{ok:!0}}modHealth(){if(this._modHealth===void 0)try{this._modHealth=JSON.parse(Je.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(wa)){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=!!Sn(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 fl({...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 Lm(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||lt,s=await jt({baseUrl:t,...this.pullFetch?{fetchImpl:this.pullFetch}:{}});if(s.running)return{ok:!0,already:!0,models:await this.modelChoices({provider:"ollama"})};let o=Aa({binary:s.binary});if(!o)throw I(409,"no_ollama","Ollama isn’t installed here. Install it from ollama.com first.");let r=ap(o);if(!r.started)throw I(500,"start_failed","Couldn’t start Ollama ("+(r.error||o.shell)+")");return this.log("ollama: started from the board ("+o.shell+")"),{ok:!!(await wo({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:Dr.map(e=>e.tag),"ollama-cloud":Rr.map(e=>e.tag)}}}saveKey(e,t){let s=wa[e];if(!s)throw I(400,"bad_provider","no key needed for "+e);let o=String(t||"").trim();if(!o||o.length>400||/\s/.test(o))throw I(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},no(this.cfg,this.root)),this.summarizer=new yt(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 I(400,"bad_config","cannot change "+o+" here");if(e.remote!==void 0&&t==="remote")throw I(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 I(400,"bad_config","remote is an object");let o={};for(let r of Object.keys(e.remote)){if(r!=="allowAnswers")throw I(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: answering from other devices "+(o.allowAnswers?"ALLOWED":"off"))}if(e.notify!==void 0){if(!e.notify||typeof e.notify!="object")throw I(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 I(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 I(400,"bad_config","summary is an object");let o={};for(let r of Object.keys(e.summary)){if(r!=="length")throw I(400,"bad_config","cannot change summary."+r+" here");if(o.length=dn(e.summary.length,{strict:!0}),!o.length)throw I(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 I(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 I(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 I(400,"bad_config","digest.days needs at least one day");o.days=vo(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(!bo(o.timeZone))throw I(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 no(this.cfg,this.root),e.summarizer&&(this.summarizer=new yt(this.cfg,{root:this.root})),e.updates&&!this.stopped&&this.timers.length&&this.scheduleUpdateCheck(),this.publicConfig()}}});var Yo,qe,wi,ts,ns,ss,Si,Pg,jg,gv,yv,wv,Sv,Fg,kv,bv,vv,xv,Ug,RE,Hg=z(()=>{Yo=function(n,e){let o=n,r=wi[e],i=null,a=0,l=null,c=[],u={},d=function(g,_){a=o*4+17,i=(function(x){let C=new Array(x);for(let E=0;E<x;E+=1){C[E]=new Array(x);for(let D=0;D<x;D+=1)C[E][D]=null}return C})(a),f(0,0),f(a-7,0),f(0,a-7),m(),h(),w(g,_),o>=7&&y(g),l==null&&(l=v(o,r,c)),S(l,_)},f=function(g,_){for(let x=-1;x<=7;x+=1)if(!(g+x<=-1||a<=g+x))for(let C=-1;C<=7;C+=1)_+C<=-1||a<=_+C||(0<=x&&x<=6&&(C==0||C==6)||0<=C&&C<=6&&(x==0||x==6)||2<=x&&x<=4&&2<=C&&C<=4?i[g+x][_+C]=!0:i[g+x][_+C]=!1)},p=function(){let g=0,_=0;for(let x=0;x<8;x+=1){d(!0,x);let C=ns.getLostPoint(u);(x==0||g>C)&&(g=C,_=x)}return _},h=function(){for(let g=8;g<a-8;g+=1)i[g][6]==null&&(i[g][6]=g%2==0);for(let g=8;g<a-8;g+=1)i[6][g]==null&&(i[6][g]=g%2==0)},m=function(){let g=ns.getPatternPosition(o);for(let _=0;_<g.length;_+=1)for(let x=0;x<g.length;x+=1){let C=g[_],E=g[x];if(i[C][E]==null)for(let D=-2;D<=2;D+=1)for(let O=-2;O<=2;O+=1)D==-2||D==2||O==-2||O==2||D==0&&O==0?i[C+D][E+O]=!0:i[C+D][E+O]=!1}},y=function(g){let _=ns.getBCHTypeNumber(o);for(let x=0;x<18;x+=1){let C=!g&&(_>>x&1)==1;i[Math.floor(x/3)][x%3+a-8-3]=C}for(let x=0;x<18;x+=1){let C=!g&&(_>>x&1)==1;i[x%3+a-8-3][Math.floor(x/3)]=C}},w=function(g,_){let x=r<<3|_,C=ns.getBCHTypeInfo(x);for(let E=0;E<15;E+=1){let D=!g&&(C>>E&1)==1;E<6?i[E][8]=D:E<8?i[E+1][8]=D:i[a-15+E][8]=D}for(let E=0;E<15;E+=1){let D=!g&&(C>>E&1)==1;E<8?i[8][a-E-1]=D:E<9?i[8][15-E-1+1]=D:i[8][15-E-1]=D}i[a-8][8]=!g},S=function(g,_){let x=-1,C=a-1,E=7,D=0,O=ns.getMaskFunction(_);for(let F=a-1;F>0;F-=2)for(F==6&&(F-=1);;){for(let $=0;$<2;$+=1)if(i[C][F-$]==null){let N=!1;D<g.length&&(N=(g[D]>>>E&1)==1),O(C,F-$)&&(N=!N),i[C][F-$]=N,E-=1,E==-1&&(D+=1,E=7)}if(C+=x,C<0||a<=C){C-=x,x=-x;break}}},k=function(g,_){let x=0,C=0,E=0,D=new Array(_.length),O=new Array(_.length);for(let j=0;j<_.length;j+=1){let W=_[j].dataCount,ue=_[j].totalCount-W;C=Math.max(C,W),E=Math.max(E,ue),D[j]=new Array(W);for(let B=0;B<D[j].length;B+=1)D[j][B]=255&g.getBuffer()[B+x];x+=W;let Y=ns.getErrorCorrectPolynomial(ue),te=Si(D[j],Y.getLength()-1).mod(Y);O[j]=new Array(Y.getLength()-1);for(let B=0;B<O[j].length;B+=1){let Ct=B+te.getLength()-O[j].length;O[j][B]=Ct>=0?te.getAt(Ct):0}}let F=0;for(let j=0;j<_.length;j+=1)F+=_[j].totalCount;let $=new Array(F),N=0;for(let j=0;j<C;j+=1)for(let W=0;W<_.length;W+=1)j<D[W].length&&($[N]=D[W][j],N+=1);for(let j=0;j<E;j+=1)for(let W=0;W<_.length;W+=1)j<O[W].length&&($[N]=O[W][j],N+=1);return $},v=function(g,_,x){let C=Pg.getRSBlocks(g,_),E=jg();for(let O=0;O<x.length;O+=1){let F=x[O];E.put(F.getMode(),4),E.put(F.getLength(),ns.getLengthInBits(F.getMode(),g)),F.write(E)}let D=0;for(let O=0;O<C.length;O+=1)D+=C[O].dataCount;if(E.getLengthInBits()>D*8)throw"code length overflow. ("+E.getLengthInBits()+">"+D*8+")";for(E.getLengthInBits()+4<=D*8&&E.put(0,4);E.getLengthInBits()%8!=0;)E.putBit(!1);for(;!(E.getLengthInBits()>=D*8||(E.put(236,8),E.getLengthInBits()>=D*8));)E.put(17,8);return k(E,C)};u.addData=function(g,_){_=_||"Byte";let x=null;switch(_){case"Numeric":x=gv(g);break;case"Alphanumeric":x=yv(g);break;case"Byte":x=wv(g);break;case"Kanji":x=Sv(g);break;default:throw"mode:"+_}c.push(x),l=null},u.isDark=function(g,_){if(g<0||a<=g||_<0||a<=_)throw g+","+_;return i[g][_]},u.getModuleCount=function(){return a},u.make=function(){if(o<1){let g=1;for(;g<40;g++){let _=Pg.getRSBlocks(g,r),x=jg();for(let E=0;E<c.length;E++){let D=c[E];x.put(D.getMode(),4),x.put(D.getLength(),ns.getLengthInBits(D.getMode(),g)),D.write(x)}let C=0;for(let E=0;E<_.length;E++)C+=_[E].dataCount;if(x.getLengthInBits()<=C*8)break}o=g}d(!1,p())},u.createTableTag=function(g,_){g=g||2,_=typeof _>"u"?g*4:_;let x="";x+='<table style="',x+=" border-width: 0px; border-style: none;",x+=" border-collapse: collapse;",x+=" padding: 0px; margin: "+_+"px;",x+='">',x+="<tbody>";for(let C=0;C<u.getModuleCount();C+=1){x+="<tr>";for(let E=0;E<u.getModuleCount();E+=1)x+='<td style="',x+=" border-width: 0px; border-style: none;",x+=" border-collapse: collapse;",x+=" padding: 0px; margin: 0px;",x+=" width: "+g+"px;",x+=" height: "+g+"px;",x+=" background-color: ",x+=u.isDark(C,E)?"#000000":"#ffffff",x+=";",x+='"/>';x+="</tr>"}return x+="</tbody>",x+="</table>",x},u.createSvgTag=function(g,_,x,C){let E={};typeof arguments[0]=="object"&&(E=arguments[0],g=E.cellSize,_=E.margin,x=E.alt,C=E.title),g=g||2,_=typeof _>"u"?g*4:_,x=typeof x=="string"?{text:x}:x||{},x.text=x.text||null,x.id=x.text?x.id||"qrcode-description":null,C=typeof C=="string"?{text:C}:C||{},C.text=C.text||null,C.id=C.text?C.id||"qrcode-title":null;let D=u.getModuleCount()*g+_*2,O,F,$,N,j="",W;for(W="l"+g+",0 0,"+g+" -"+g+",0 0,-"+g+"z ",j+='<svg version="1.1" xmlns="http://www.w3.org/2000/svg"',j+=E.scalable?"":' width="'+D+'px" height="'+D+'px"',j+=' viewBox="0 0 '+D+" "+D+'" ',j+=' preserveAspectRatio="xMinYMin meet"',j+=C.text||x.text?' role="img" aria-labelledby="'+T([C.id,x.id].join(" ").trim())+'"':"",j+=">",j+=C.text?'<title id="'+T(C.id)+'">'+T(C.text)+"</title>":"",j+=x.text?'<description id="'+T(x.id)+'">'+T(x.text)+"</description>":"",j+='<rect width="100%" height="100%" fill="white" cx="0" cy="0"/>',j+='<path d="',$=0;$<u.getModuleCount();$+=1)for(N=$*g+_,O=0;O<u.getModuleCount();O+=1)u.isDark($,O)&&(F=O*g+_,j+="M"+F+","+N+W);return j+='" stroke="transparent" fill="black"/>',j+="</svg>",j},u.createDataURL=function(g,_){g=g||2,_=typeof _>"u"?g*4:_;let x=u.getModuleCount()*g+_*2,C=_,E=x-_;return xv(x,x,function(D,O){if(C<=D&&D<E&&C<=O&&O<E){let F=Math.floor((D-C)/g),$=Math.floor((O-C)/g);return u.isDark($,F)?0:1}else return 1})},u.createImgTag=function(g,_,x){g=g||2,_=typeof _>"u"?g*4:_;let C=u.getModuleCount()*g+_*2,E="";return E+="<img",E+=' src="',E+=u.createDataURL(g,_),E+='"',E+=' width="',E+=C,E+='"',E+=' height="',E+=C,E+='"',x&&(E+=' alt="',E+=T(x),E+='"'),E+="/>",E};let T=function(g){let _="";for(let x=0;x<g.length;x+=1){let C=g.charAt(x);switch(C){case"<":_+="&lt;";break;case">":_+="&gt;";break;case"&":_+="&amp;";break;case'"':_+="&quot;";break;default:_+=C;break}}return _},b=function(g){g=typeof g>"u"?2:g;let x=u.getModuleCount()*1+g*2,C=g,E=x-g,D,O,F,$,N,j={"██":"█","█ ":"▀"," █":"▄"," ":" "},W={"██":"▀","█ ":"▀"," █":" "," ":" "},ue="";for(D=0;D<x;D+=2){for(F=Math.floor((D-C)/1),$=Math.floor((D+1-C)/1),O=0;O<x;O+=1)N="█",C<=O&&O<E&&C<=D&&D<E&&u.isDark(F,Math.floor((O-C)/1))&&(N=" "),C<=O&&O<E&&C<=D+1&&D+1<E&&u.isDark($,Math.floor((O-C)/1))?N+=" ":N+="█",ue+=g<1&&D+1>=E?W[N]:j[N];ue+=`
217
- `}return x%2&&g>0?ue.substring(0,ue.length-x-1)+Array(x+1).join("▀"):ue.substring(0,ue.length-1)};return u.createASCII=function(g,_){if(g=g||1,g<2)return b(_);g-=1,_=typeof _>"u"?g*2:_;let x=u.getModuleCount()*g+_*2,C=_,E=x-_,D,O,F,$,N=Array(g+1).join("██"),j=Array(g+1).join(" "),W="",ue="";for(D=0;D<x;D+=1){for(F=Math.floor((D-C)/g),ue="",O=0;O<x;O+=1)$=1,C<=O&&O<E&&C<=D&&D<E&&u.isDark(F,Math.floor((O-C)/g))&&($=0),ue+=$?N:j;for(F=0;F<g;F+=1)W+=ue+`
218
- `}return W.substring(0,W.length-1)},u.renderTo2dContext=function(g,_){_=_||2;let x=u.getModuleCount();for(let C=0;C<x;C++)for(let E=0;E<x;E++)g.fillStyle=u.isDark(C,E)?"black":"white",g.fillRect(E*_,C*_,_,_)},u};Yo.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};Yo.createStringToBytes=function(n,e){let t=(function(){let o=bv(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),p=u<<8|d;a[f]=p,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},wi={L:1,M:0,Q:3,H:2},ts={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7},ns=(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 ts.PATTERN000:return function(a,l){return(a+l)%2==0};case ts.PATTERN001:return function(a,l){return a%2==0};case ts.PATTERN010:return function(a,l){return l%3==0};case ts.PATTERN011:return function(a,l){return(a+l)%3==0};case ts.PATTERN100:return function(a,l){return(Math.floor(a/2)+Math.floor(l/3))%2==0};case ts.PATTERN101:return function(a,l){return a*l%2+a*l%3==0};case ts.PATTERN110:return function(a,l){return(a*l%2+a*l%3)%2==0};case ts.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=Si([1],0);for(let l=0;l<i;l+=1)a=a.multiply(Si([1,ss.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 p=0,h=i.isDark(d,f);for(let m=-1;m<=1;m+=1)if(!(d+m<0||a<=d+m))for(let y=-1;y<=1;y+=1)f+y<0||a<=f+y||m==0&&y==0||h==i.isDark(d+m,f+y)&&(p+=1);p>5&&(l+=3+p-5)}for(let d=0;d<a-1;d+=1)for(let f=0;f<a-1;f+=1){let p=0;i.isDark(d,f)&&(p+=1),i.isDark(d+1,f)&&(p+=1),i.isDark(d,f+1)&&(p+=1),i.isDark(d+1,f+1)&&(p+=1),(p==0||p==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})(),ss=(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})(),Si=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]^=ss.gexp(ss.glog(s.getAt(i))+ss.glog(o.getAt(a)));return Si(r,0)},s.mod=function(o){if(s.getLength()-o.getLength()<0)return s;let r=ss.glog(s.getAt(0))-ss.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]^=ss.gexp(ss.glog(o.getAt(a))+r);return Si(i,0).mod(o)},s},Pg=(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 wi.L:return n[(o-1)*4+0];case wi.M:return n[(o-1)*4+1];case wi.Q:return n[(o-1)*4+2];case wi.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 p=0;p<u;p+=1)l.push(e(d,f))}return l},t})(),jg=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},gv=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},yv=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},wv=function(n){let e=qe.MODE_8BIT_BYTE,t=n,s=Yo.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},Sv=function(n){let e=qe.MODE_KANJI,t=n,s=Yo.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},Fg=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},kv=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},bv=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},vv=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,p={};return p.write=function(h,m){if(h>>>m)throw"length over";for(;d+m>=8;)u.writeByte(255&(h<<d|f)),m-=8-d,h>>>=8-d,f=0,d=0;f=h<<d|f,d=d+m},p.flush=function(){d>0&&u.writeByte(f)},p},a=function(c){let u=1<<c,d=(1<<c)+1,f=c+1,p=l();for(let S=0;S<u;S+=1)p.add(String.fromCharCode(S));p.add(String.fromCharCode(u)),p.add(String.fromCharCode(d));let h=Fg(),m=i(h);m.write(u,f);let y=0,w=String.fromCharCode(o[y]);for(y+=1;y<o.length;){let S=String.fromCharCode(o[y]);y+=1,p.contains(w+S)?w=w+S:(m.write(p.indexOf(w),f),p.size()<4095&&(p.size()==1<<f&&(f+=1),p.add(w+S)),w=S)}return m.write(p.indexOf(w),f),m.write(d,f),m.flush(),h.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},xv=function(n,e,t){let s=vv(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=Fg();s.write(o);let r=kv(),i=o.toByteArray();for(let a=0;a<i.length;a+=1)r.writeByte(i[a]);return r.flush(),"data:image/gif;base64,"+r},Ug=Yo,RE=Yo.stringToBytes});function Bg(n,{ecc:e="M"}={}){let t=Ug(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 Av(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 Wl(n,{quiet:e=2,color:t=!0,invert:s=!0}={}){let o=Av(Bg(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(`
219
- `)}function zl(n,{quiet:e=4,dark:t="#000",light:s="#fff"}={}){let o=Bg(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 ki=z(()=>{Hg()});var Yg={};Jt(Yg,{PROVIDERS:()=>Gg,backupPath:()=>Fd,deleteBackup:()=>vi,detectSyncFolders:()=>Wo,executeMove:()=>Ud,findBackups:()=>Jo,isLeftoffFolder:()=>On,joinLine:()=>qo,moveStatusFile:()=>bi,ownerOf:()=>Dd,planMove:()=>zo,readMoveStatus:()=>Gs,sha256:()=>Cn,syncedBy:()=>$g,tilde:()=>ge,walk:()=>Jl,writeMoveStatus:()=>jd});import re from"node:fs";import ql from"node:os";import K from"node:path";import Kg from"node:crypto";function bi(n=process.env){return n.LEFTOFF_MOVE_STATUS?n.LEFTOFF_MOVE_STATUS:K.join(n.XDG_CONFIG_HOME||K.join(n.HOME||ql.homedir(),".config"),"checkpoints","move-status.json")}function Gs(n=process.env){try{return JSON.parse(re.readFileSync(bi(n),"utf8"))}catch{return null}}function jd(n,e=process.env){let t=bi(e);re.mkdirSync(K.dirname(t),{recursive:!0});let s=t+"."+process.pid+".tmp";re.writeFileSync(s,JSON.stringify({...n,at:new Date().toISOString()},null,2)+`
220
- `),re.renameSync(s,t)}function Tv(n){let e=[],t=[K.join(n,"Library","Application Support","Syncthing","config.xml"),K.join(n,".config","syncthing","config.xml"),K.join(n,".local","state","syncthing","config.xml")];for(let s of t){let o="";try{o=re.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:K.resolve(c),label:l||K.basename(c),source:s})}}return!e.length&&os(K.join(n,"Sync"))&&e.push({provider:"syncthing",path:K.join(n,"Sync"),label:"Sync",source:"default folder"}),e}function Wo({home:n=Pd(),current:e=null}={}){let t=[];t.push(...Tv(n));let s=K.join(n,"Library","CloudStorage");try{for(let a of re.readdirSync(s))/^Dropbox/i.test(a)&&t.push({provider:"dropbox",path:K.join(s,a),label:a.replace(/^Dropbox-?/,"Dropbox ").trim()})}catch{}os(K.join(n,"Dropbox"))&&!t.some(a=>a.provider==="dropbox")&&t.push({provider:"dropbox",path:K.join(n,"Dropbox"),label:"Dropbox"});let o=K.join(n,"Library","Mobile Documents","com~apple~CloudDocs");os(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=K.join(a.path,"Leftoff");i.push({...a,suggested:l,display:ge(l),hasLeftoff:On(l),...Gg[a.provider]})}return i.sort((a,l)=>(l.recommended?1:0)-(a.recommended?1:0)),{candidates:i,current:e?{path:e,display:ge(e),syncedBy:$g(e,i)}:null}}function $g(n,e){let t=K.resolve(n);for(let s of e)if(t===s.path||t.startsWith(s.path+K.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 On(n){return et(K.join(n,"config.json"))&&(os(K.join(n,"sessions"))||et(K.join(n,"README.md")))}function Jl(n){let e=[],t=(s,o)=>{let r=[];try{r=re.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(K.join(s,i.name),a):i.isFile()&&!_v(a)&&e.push(a))}};return t(n,""),e.sort()}function Cn(n){return Kg.createHash("sha256").update(re.readFileSync(n)).digest("hex")}function Dd(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 Ev(n){return n==="config.json"?"config":n==="README.md"?"readme":n.startsWith(".secret/")?"secret":n.startsWith("lists/")?"list":n.startsWith("catchups/")?"catchup":"file"}function zo(n,e,{machine:t,hostname:s=ql.hostname(),identity:o=null}={}){n=K.resolve(n),e=K.resolve(e);let r=[],i=[],a=[];n===e&&r.push({code:"same_folder",message:"That is already the folder Leftoff uses."}),e.startsWith(n+K.sep)&&r.push({code:"inside_old",message:"The new folder can’t be inside the current one."}),n.startsWith(e+K.sep)&&r.push({code:"contains_old",message:"The new folder can’t contain the current one; pick a subfolder such as "+ge(K.join(e,"Leftoff"))+"."}),os(n)||r.push({code:"no_source",message:"The current folder "+ge(n)+" doesn’t exist."});let l=!1;if(et(e))if(!os(e))r.push({code:"not_a_folder",message:ge(e)+" is a file, not a folder."});else{let f=re.readdirSync(e).filter(p=>!/^\.DS_Store$|^\.stfolder$|^\.stignore$|^Icon\r?$/.test(p));On(e)||f.some(p=>["sessions","machines","lists","catchups","digests"].includes(p))?l=!0:f.length&&r.push({code:"not_empty",message:ge(e)+" already has other files in it. Pick an empty folder, or a new one such as "+ge(K.join(e,"Leftoff"))+"."})}else{let f=K.dirname(e);for(;!et(f)&&f!==K.dirname(f);)f=K.dirname(f);try{re.accessSync(f,re.constants.W_OK)}catch{r.push({code:"not_writable",message:"Can’t write to "+ge(f)+"."})}}if(l&&t){try{let f=JSON.parse(re.readFileSync(K.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&&os(K.join(e,"sessions",t))){let f=new Set(re.readdirSync(K.join(e,"sessions",t))),p=os(K.join(n,"sessions",t))?new Set(re.readdirSync(K.join(n,"sessions",t))):new Set,h=[...f].filter(m=>!m.startsWith(".")&&!p.has(m));h.length&&a.push({machine:t,code:"machine_name_maybe_taken",message:"The folder already has "+h.length+" session"+(h.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 Jl(n)){let p=K.join(n,f),h=K.join(e,f),m=re.statSync(p).size;d.total++;let y="copy",w=null,S=f;if(l&&et(h))if(Cn(p)===Cn(h))y="same";else{let k=Dd(f),v=Ev(f);k&&k===t?(y="copy",w="this machine’s own file: ours is newer"):v==="list"?(y="conflict-copy",S=f.replace(/\.md$/,"")+".sync-conflict-"+Rd(new Date,!0)+"-LEFTOFF.md",w="the list changed on both sides: merged item by item"):v==="catchup"?(y="rename-copy",S=Cv(e,f),w="same minute, different catch-up"):(y="keep-target",w=v==="config"?"the joined folder’s settings win":k?k+"’s own file: the joined folder has its latest":"the joined folder’s copy wins")}(y==="copy"||y==="conflict-copy"||y==="rename-copy")&&(u+=m),d[{copy:"copy",same:"same","keep-target":"keepTarget","conflict-copy":"conflictCopy","rename-copy":"renameCopy"}[y]]++,c.push({rel:f,dest:S,action:y,why:w,size:m})}if(l){let f=new Set;for(let p of Jl(e)){let h=Dd(p);h&&h!==t&&f.add(h)}f.size&&i.push({code:"merging",message:"Joining a folder that already has "+[...f].sort().join(", ")+"’s data. Nothing of theirs is overwritten."}),et(K.join(e,"config.json"))&&et(K.join(n,"config.json"))&&Cn(K.join(e,"config.json"))!==Cn(K.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:Fd(n)}}function Rd(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 Cv(n,e){let t=K.extname(e),s=e.slice(0,-t.length);for(let o=2;o<100;o++){let r=s+"-"+o+t;if(!et(K.join(n,r)))return r}return s+"-"+Kg.randomBytes(3).toString("hex")+t}function Fd(n,e=new Date){let t=n+".leftoff-backup-"+Rd(e);for(let s=2;et(t);s++)t=n+".leftoff-backup-"+Rd(e)+"-"+s;return t}async function Ud(n,{hooks:e={},machine:t,hostname:s=ql.hostname(),say:o=()=>{},copyFile:r=re.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 y=[],w=m;for(;!et(w);)y.push(w),w=K.dirname(w);for(let S of y.reverse())re.mkdirSync(S),l.push(S)},d=(m,y)=>{let w=K.join(n.to,y);u(K.dirname(w));let S=et(w),k=K.join(K.dirname(w),"."+K.basename(w)+".move-"+process.pid+".tmp");r(m,k);try{re.chmodSync(k,re.statSync(m).mode&511)}catch{}return re.renameSync(k,w),S||a.push(w),w},f=()=>{for(let m of a.reverse())try{re.unlinkSync(m)}catch{}for(let m of l.reverse())try{re.rmdirSync(m)}catch{}},p=!1;try{o("Stopping the daemon so nothing writes mid-move"),e.stopWriters&&(await e.stopWriters(),p=!0),u(n.to),o("Copying "+n.counts.total+" files");let m=[];for(let S of n.files){if(S.action==="same"){c.verified++;continue}if(S.action==="keep-target"){c.kept++;continue}let k=d(K.join(n.from,S.rel),S.dest);(S.action==="conflict-copy"||S.action==="rename-copy")&&c.conflicts++,c.copied++,m.push([K.join(n.from,S.rel),k,S.rel])}n.files.find(S=>S.rel==="config.json"&&S.action==="keep-target")&&Ov(n.from,n.to,s),o("Verifying "+m.length+" copies");for(let[S,k,v]of m){if(Cn(S)!==Cn(k))throw Object.assign(new Error("verify failed: "+v+" 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 w=new Set(n.files.map(S=>S.rel));for(let S of Jl(n.from)){if(w.has(S)||et(K.join(n.to,S)))continue;let k=d(K.join(n.from,S),S);if(Cn(K.join(n.from,S))!==Cn(k))throw Object.assign(new Error("verify failed: "+S),{code:"verify_failed"});c.stragglers++}}catch(m){if(f(),p&&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 h=Fd(n.from,i);try{re.renameSync(n.from,h),re.writeFileSync(K.join(h,"MOVED-HERE.md"),`# This is a Leftoff backup
218
+ `;Ge.mkdirSync(er.dirname(o),{recursive:!0}),Ge.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=ay(i,e.title||me(e)),c=this.notifier({title:"Leftoff",subtitle:l.subtitle,message:l.message,sessionId:e.id,goBin:Be});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(Ft).filter(Boolean);return{enabled:e.length>0,hosts:e,urls:Il(e),requireToken:this.cfg.remoteRequireToken!==!1}}remoteState(e){return{...this.remoteHealth(),via:e||null,...Rl(this.root)}}setRemoteHosts({add:e=[],remove:t=[],requireToken:s}={}){let o=a=>(Array.isArray(a)?a:[a]).map(Ft).filter(Boolean),r=new Set(o(t)),i=(this.cfg.remoteHosts||[]).map(Ft).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),fo(this.cfg,this.root),this.remoteHealth()}reloadRemote(){let e=X(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 er.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 N(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{Ge.writeFileSync(this.modHealthFile(),JSON.stringify(this._modHealth,null,2)+`
219
+ `)}catch{}return this.emit("session.updated",{mod:!0}),{ok:!0}}modHealth(){if(this._modHealth===void 0)try{this._modHealth=JSON.parse(Ge.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(Ca)){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=!!xn(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 Cl({...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 bg(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||ht,s=await Bt({baseUrl:t,...this.pullFetch?{fetchImpl:this.pullFetch}:{}});if(s.running)return{ok:!0,already:!0,models:await this.modelChoices({provider:"ollama"})};let o=Ra({binary:s.binary});if(!o)throw N(409,"no_ollama","Ollama isn’t installed here. Install it from ollama.com first.");let r=Up(o);if(!r.started)throw N(500,"start_failed","Couldn’t start Ollama ("+(r.error||o.shell)+")");return this.log("ollama: started from the board ("+o.shell+")"),{ok:!!(await Eo({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:Kr.map(e=>e.tag),"ollama-cloud":$r.map(e=>e.tag)}}}saveKey(e,t){let s=Ca[e];if(!s)throw N(400,"bad_provider","no key needed for "+e);let o=String(t||"").trim();if(!o||o.length>400||/\s/.test(o))throw N(400,"bad_key","that doesn't look like an API key");let r=pa(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},fo(this.cfg,this.root)),this.summarizer=new xt(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 N(400,"bad_config","cannot change "+o+" here");if(e.remote!==void 0&&t==="remote")throw N(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 N(400,"bad_config","remote is an object");let o={};for(let r of Object.keys(e.remote)){if(r!=="allowAnswers")throw N(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 N(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 N(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 N(400,"bad_config","summary is an object");let o={};for(let r of Object.keys(e.summary)){if(r!=="length")throw N(400,"bad_config","cannot change summary."+r+" here");if(o.length=mn(e.summary.length,{strict:!0}),!o.length)throw N(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 N(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 N(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 N(400,"bad_config","digest.days needs at least one day");o.days=Io(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(!No(o.timeZone))throw N(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 fo(this.cfg,this.root),e.summarizer&&(this.summarizer=new xt(this.cfg,{root:this.root})),e.updates&&!this.stopped&&this.timers.length&&this.scheduleUpdateCheck(),this.publicConfig()}}});var tr,Ze,Oi,is,as,ls,Ci,xy,Ay,a1,l1,c1,u1,Ty,d1,f1,h1,p1,_y,PO,Ey=J(()=>{tr=function(n,e){let o=n,r=Oi[e],i=null,a=0,l=null,c=[],u={},d=function(g,_){a=o*4+17,i=(function(x){let E=new Array(x);for(let O=0;O<x;O+=1){E[O]=new Array(x);for(let M=0;M<x;M+=1)E[O][M]=null}return E})(a),f(0,0),f(a-7,0),f(0,a-7),m(),h(),S(g,_),o>=7&&y(g),l==null&&(l=A(o,r,c)),b(l,_)},f=function(g,_){for(let x=-1;x<=7;x+=1)if(!(g+x<=-1||a<=g+x))for(let E=-1;E<=7;E+=1)_+E<=-1||a<=_+E||(0<=x&&x<=6&&(E==0||E==6)||0<=E&&E<=6&&(x==0||x==6)||2<=x&&x<=4&&2<=E&&E<=4?i[g+x][_+E]=!0:i[g+x][_+E]=!1)},p=function(){let g=0,_=0;for(let x=0;x<8;x+=1){d(!0,x);let E=as.getLostPoint(u);(x==0||g>E)&&(g=E,_=x)}return _},h=function(){for(let g=8;g<a-8;g+=1)i[g][6]==null&&(i[g][6]=g%2==0);for(let g=8;g<a-8;g+=1)i[6][g]==null&&(i[6][g]=g%2==0)},m=function(){let g=as.getPatternPosition(o);for(let _=0;_<g.length;_+=1)for(let x=0;x<g.length;x+=1){let E=g[_],O=g[x];if(i[E][O]==null)for(let M=-2;M<=2;M+=1)for(let C=-2;C<=2;C+=1)M==-2||M==2||C==-2||C==2||M==0&&C==0?i[E+M][O+C]=!0:i[E+M][O+C]=!1}},y=function(g){let _=as.getBCHTypeNumber(o);for(let x=0;x<18;x+=1){let E=!g&&(_>>x&1)==1;i[Math.floor(x/3)][x%3+a-8-3]=E}for(let x=0;x<18;x+=1){let E=!g&&(_>>x&1)==1;i[x%3+a-8-3][Math.floor(x/3)]=E}},S=function(g,_){let x=r<<3|_,E=as.getBCHTypeInfo(x);for(let O=0;O<15;O+=1){let M=!g&&(E>>O&1)==1;O<6?i[O][8]=M:O<8?i[O+1][8]=M:i[a-15+O][8]=M}for(let O=0;O<15;O+=1){let M=!g&&(E>>O&1)==1;O<8?i[8][a-O-1]=M:O<9?i[8][15-O-1+1]=M:i[8][15-O-1]=M}i[a-8][8]=!g},b=function(g,_){let x=-1,E=a-1,O=7,M=0,C=as.getMaskFunction(_);for(let U=a-1;U>0;U-=2)for(U==6&&(U-=1);;){for(let W=0;W<2;W+=1)if(i[E][U-W]==null){let I=!1;M<g.length&&(I=(g[M]>>>O&1)==1),C(E,U-W)&&(I=!I),i[E][U-W]=I,O-=1,O==-1&&(M+=1,O=7)}if(E+=x,E<0||a<=E){E-=x,x=-x;break}}},w=function(g,_){let x=0,E=0,O=0,M=new Array(_.length),C=new Array(_.length);for(let P=0;P<_.length;P+=1){let B=_[P].dataCount,ce=_[P].totalCount-B;E=Math.max(E,B),O=Math.max(O,ce),M[P]=new Array(B);for(let Me=0;Me<M[P].length;Me+=1)M[P][Me]=255&g.getBuffer()[Me+x];x+=B;let $=as.getErrorCorrectPolynomial(ce),Qe=Ci(M[P],$.getLength()-1).mod($);C[P]=new Array($.getLength()-1);for(let Me=0;Me<C[P].length;Me+=1){let Q=Me+Qe.getLength()-C[P].length;C[P][Me]=Q>=0?Qe.getAt(Q):0}}let U=0;for(let P=0;P<_.length;P+=1)U+=_[P].totalCount;let W=new Array(U),I=0;for(let P=0;P<E;P+=1)for(let B=0;B<_.length;B+=1)P<M[B].length&&(W[I]=M[B][P],I+=1);for(let P=0;P<O;P+=1)for(let B=0;B<_.length;B+=1)P<C[B].length&&(W[I]=C[B][P],I+=1);return W},A=function(g,_,x){let E=xy.getRSBlocks(g,_),O=Ay();for(let C=0;C<x.length;C+=1){let U=x[C];O.put(U.getMode(),4),O.put(U.getLength(),as.getLengthInBits(U.getMode(),g)),U.write(O)}let M=0;for(let C=0;C<E.length;C+=1)M+=E[C].dataCount;if(O.getLengthInBits()>M*8)throw"code length overflow. ("+O.getLengthInBits()+">"+M*8+")";for(O.getLengthInBits()+4<=M*8&&O.put(0,4);O.getLengthInBits()%8!=0;)O.putBit(!1);for(;!(O.getLengthInBits()>=M*8||(O.put(236,8),O.getLengthInBits()>=M*8));)O.put(17,8);return w(O,E)};u.addData=function(g,_){_=_||"Byte";let x=null;switch(_){case"Numeric":x=a1(g);break;case"Alphanumeric":x=l1(g);break;case"Byte":x=c1(g);break;case"Kanji":x=u1(g);break;default:throw"mode:"+_}c.push(x),l=null},u.isDark=function(g,_){if(g<0||a<=g||_<0||a<=_)throw g+","+_;return i[g][_]},u.getModuleCount=function(){return a},u.make=function(){if(o<1){let g=1;for(;g<40;g++){let _=xy.getRSBlocks(g,r),x=Ay();for(let O=0;O<c.length;O++){let M=c[O];x.put(M.getMode(),4),x.put(M.getLength(),as.getLengthInBits(M.getMode(),g)),M.write(x)}let E=0;for(let O=0;O<_.length;O++)E+=_[O].dataCount;if(x.getLengthInBits()<=E*8)break}o=g}d(!1,p())},u.createTableTag=function(g,_){g=g||2,_=typeof _>"u"?g*4:_;let x="";x+='<table style="',x+=" border-width: 0px; border-style: none;",x+=" border-collapse: collapse;",x+=" padding: 0px; margin: "+_+"px;",x+='">',x+="<tbody>";for(let E=0;E<u.getModuleCount();E+=1){x+="<tr>";for(let O=0;O<u.getModuleCount();O+=1)x+='<td style="',x+=" border-width: 0px; border-style: none;",x+=" border-collapse: collapse;",x+=" padding: 0px; margin: 0px;",x+=" width: "+g+"px;",x+=" height: "+g+"px;",x+=" background-color: ",x+=u.isDark(E,O)?"#000000":"#ffffff",x+=";",x+='"/>';x+="</tr>"}return x+="</tbody>",x+="</table>",x},u.createSvgTag=function(g,_,x,E){let O={};typeof arguments[0]=="object"&&(O=arguments[0],g=O.cellSize,_=O.margin,x=O.alt,E=O.title),g=g||2,_=typeof _>"u"?g*4:_,x=typeof x=="string"?{text:x}:x||{},x.text=x.text||null,x.id=x.text?x.id||"qrcode-description":null,E=typeof E=="string"?{text:E}:E||{},E.text=E.text||null,E.id=E.text?E.id||"qrcode-title":null;let M=u.getModuleCount()*g+_*2,C,U,W,I,P="",B;for(B="l"+g+",0 0,"+g+" -"+g+",0 0,-"+g+"z ",P+='<svg version="1.1" xmlns="http://www.w3.org/2000/svg"',P+=O.scalable?"":' width="'+M+'px" height="'+M+'px"',P+=' viewBox="0 0 '+M+" "+M+'" ',P+=' preserveAspectRatio="xMinYMin meet"',P+=E.text||x.text?' role="img" aria-labelledby="'+v([E.id,x.id].join(" ").trim())+'"':"",P+=">",P+=E.text?'<title id="'+v(E.id)+'">'+v(E.text)+"</title>":"",P+=x.text?'<description id="'+v(x.id)+'">'+v(x.text)+"</description>":"",P+='<rect width="100%" height="100%" fill="white" cx="0" cy="0"/>',P+='<path d="',W=0;W<u.getModuleCount();W+=1)for(I=W*g+_,C=0;C<u.getModuleCount();C+=1)u.isDark(W,C)&&(U=C*g+_,P+="M"+U+","+I+B);return P+='" stroke="transparent" fill="black"/>',P+="</svg>",P},u.createDataURL=function(g,_){g=g||2,_=typeof _>"u"?g*4:_;let x=u.getModuleCount()*g+_*2,E=_,O=x-_;return p1(x,x,function(M,C){if(E<=M&&M<O&&E<=C&&C<O){let U=Math.floor((M-E)/g),W=Math.floor((C-E)/g);return u.isDark(W,U)?0:1}else return 1})},u.createImgTag=function(g,_,x){g=g||2,_=typeof _>"u"?g*4:_;let E=u.getModuleCount()*g+_*2,O="";return O+="<img",O+=' src="',O+=u.createDataURL(g,_),O+='"',O+=' width="',O+=E,O+='"',O+=' height="',O+=E,O+='"',x&&(O+=' alt="',O+=v(x),O+='"'),O+="/>",O};let v=function(g){let _="";for(let x=0;x<g.length;x+=1){let E=g.charAt(x);switch(E){case"<":_+="&lt;";break;case">":_+="&gt;";break;case"&":_+="&amp;";break;case'"':_+="&quot;";break;default:_+=E;break}}return _},k=function(g){g=typeof g>"u"?2:g;let x=u.getModuleCount()*1+g*2,E=g,O=x-g,M,C,U,W,I,P={"██":"█","█ ":"▀"," █":"▄"," ":" "},B={"██":"▀","█ ":"▀"," █":" "," ":" "},ce="";for(M=0;M<x;M+=2){for(U=Math.floor((M-E)/1),W=Math.floor((M+1-E)/1),C=0;C<x;C+=1)I="█",E<=C&&C<O&&E<=M&&M<O&&u.isDark(U,Math.floor((C-E)/1))&&(I=" "),E<=C&&C<O&&E<=M+1&&M+1<O&&u.isDark(W,Math.floor((C-E)/1))?I+=" ":I+="█",ce+=g<1&&M+1>=O?B[I]:P[I];ce+=`
220
+ `}return x%2&&g>0?ce.substring(0,ce.length-x-1)+Array(x+1).join("▀"):ce.substring(0,ce.length-1)};return u.createASCII=function(g,_){if(g=g||1,g<2)return k(_);g-=1,_=typeof _>"u"?g*2:_;let x=u.getModuleCount()*g+_*2,E=_,O=x-_,M,C,U,W,I=Array(g+1).join("██"),P=Array(g+1).join(" "),B="",ce="";for(M=0;M<x;M+=1){for(U=Math.floor((M-E)/g),ce="",C=0;C<x;C+=1)W=1,E<=C&&C<O&&E<=M&&M<O&&u.isDark(U,Math.floor((C-E)/g))&&(W=0),ce+=W?I:P;for(U=0;U<g;U+=1)B+=ce+`
221
+ `}return B.substring(0,B.length-1)},u.renderTo2dContext=function(g,_){_=_||2;let x=u.getModuleCount();for(let E=0;E<x;E++)for(let O=0;O<x;O++)g.fillStyle=u.isDark(E,O)?"black":"white",g.fillRect(O*_,E*_,_,_)},u};tr.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};tr.createStringToBytes=function(n,e){let t=(function(){let o=f1(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),p=u<<8|d;a[f]=p,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}};Ze={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8},Oi={L:1,M:0,Q:3,H:2},is={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7},as=(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 is.PATTERN000:return function(a,l){return(a+l)%2==0};case is.PATTERN001:return function(a,l){return a%2==0};case is.PATTERN010:return function(a,l){return l%3==0};case is.PATTERN011:return function(a,l){return(a+l)%3==0};case is.PATTERN100:return function(a,l){return(Math.floor(a/2)+Math.floor(l/3))%2==0};case is.PATTERN101:return function(a,l){return a*l%2+a*l%3==0};case is.PATTERN110:return function(a,l){return(a*l%2+a*l%3)%2==0};case is.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=Ci([1],0);for(let l=0;l<i;l+=1)a=a.multiply(Ci([1,ls.gexp(l)],0));return a},o.getLengthInBits=function(i,a){if(1<=a&&a<10)switch(i){case Ze.MODE_NUMBER:return 10;case Ze.MODE_ALPHA_NUM:return 9;case Ze.MODE_8BIT_BYTE:return 8;case Ze.MODE_KANJI:return 8;default:throw"mode:"+i}else if(a<27)switch(i){case Ze.MODE_NUMBER:return 12;case Ze.MODE_ALPHA_NUM:return 11;case Ze.MODE_8BIT_BYTE:return 16;case Ze.MODE_KANJI:return 10;default:throw"mode:"+i}else if(a<41)switch(i){case Ze.MODE_NUMBER:return 14;case Ze.MODE_ALPHA_NUM:return 13;case Ze.MODE_8BIT_BYTE:return 16;case Ze.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 p=0,h=i.isDark(d,f);for(let m=-1;m<=1;m+=1)if(!(d+m<0||a<=d+m))for(let y=-1;y<=1;y+=1)f+y<0||a<=f+y||m==0&&y==0||h==i.isDark(d+m,f+y)&&(p+=1);p>5&&(l+=3+p-5)}for(let d=0;d<a-1;d+=1)for(let f=0;f<a-1;f+=1){let p=0;i.isDark(d,f)&&(p+=1),i.isDark(d+1,f)&&(p+=1),i.isDark(d,f+1)&&(p+=1),i.isDark(d+1,f+1)&&(p+=1),(p==0||p==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})(),ls=(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})(),Ci=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]^=ls.gexp(ls.glog(s.getAt(i))+ls.glog(o.getAt(a)));return Ci(r,0)},s.mod=function(o){if(s.getLength()-o.getLength()<0)return s;let r=ls.glog(s.getAt(0))-ls.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]^=ls.gexp(ls.glog(o.getAt(a))+r);return Ci(i,0).mod(o)},s},xy=(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 Oi.L:return n[(o-1)*4+0];case Oi.M:return n[(o-1)*4+1];case Oi.Q:return n[(o-1)*4+2];case Oi.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 p=0;p<u;p+=1)l.push(e(d,f))}return l},t})(),Ay=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},a1=function(n){let e=Ze.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},l1=function(n){let e=Ze.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},c1=function(n){let e=Ze.MODE_8BIT_BYTE,t=n,s=tr.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},u1=function(n){let e=Ze.MODE_KANJI,t=n,s=tr.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},Ty=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},d1=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},f1=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},h1=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,p={};return p.write=function(h,m){if(h>>>m)throw"length over";for(;d+m>=8;)u.writeByte(255&(h<<d|f)),m-=8-d,h>>>=8-d,f=0,d=0;f=h<<d|f,d=d+m},p.flush=function(){d>0&&u.writeByte(f)},p},a=function(c){let u=1<<c,d=(1<<c)+1,f=c+1,p=l();for(let b=0;b<u;b+=1)p.add(String.fromCharCode(b));p.add(String.fromCharCode(u)),p.add(String.fromCharCode(d));let h=Ty(),m=i(h);m.write(u,f);let y=0,S=String.fromCharCode(o[y]);for(y+=1;y<o.length;){let b=String.fromCharCode(o[y]);y+=1,p.contains(S+b)?S=S+b:(m.write(p.indexOf(S),f),p.size()<4095&&(p.size()==1<<f&&(f+=1),p.add(S+b)),S=b)}return m.write(p.indexOf(S),f),m.write(d,f),m.flush(),h.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},p1=function(n,e,t){let s=h1(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=Ty();s.write(o);let r=d1(),i=o.toByteArray();for(let a=0;a<i.length;a+=1)r.writeByte(i[a]);return r.flush(),"data:image/gif;base64,"+r},_y=tr,PO=tr.stringToBytes});function Oy(n,{ecc:e="M"}={}){let t=_y(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 m1(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 cc(n,{quiet:e=2,color:t=!0,invert:s=!0}={}){let o=m1(Oy(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(`
222
+ `)}function uc(n,{quiet:e=4,dark:t="#000",light:s="#fff"}={}){let o=Oy(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 Ni=J(()=>{Ey()});var My={};Qt(My,{PROVIDERS:()=>Ny,backupPath:()=>uf,deleteBackup:()=>Mi,detectSyncFolders:()=>nr,executeMove:()=>df,findBackups:()=>or,isLeftoffFolder:()=>Ln,joinLine:()=>rr,moveStatusFile:()=>Ii,ownerOf:()=>rf,planMove:()=>sr,readMoveStatus:()=>Vs,sha256:()=>Mn,syncedBy:()=>Iy,tilde:()=>we,walk:()=>dc,writeMoveStatus:()=>cf});import ae from"node:fs";import fc from"node:os";import H from"node:path";import Cy from"node:crypto";function Ii(n=process.env){return n.LEFTOFF_MOVE_STATUS?n.LEFTOFF_MOVE_STATUS:H.join(n.XDG_CONFIG_HOME||H.join(n.HOME||fc.homedir(),".config"),"checkpoints","move-status.json")}function Vs(n=process.env){try{return JSON.parse(ae.readFileSync(Ii(n),"utf8"))}catch{return null}}function cf(n,e=process.env){let t=Ii(e);ae.mkdirSync(H.dirname(t),{recursive:!0});let s=t+"."+process.pid+".tmp";ae.writeFileSync(s,JSON.stringify({...n,at:new Date().toISOString()},null,2)+`
223
+ `),ae.renameSync(s,t)}function g1(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=ae.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&&cs(H.join(n,"Sync"))&&e.push({provider:"syncthing",path:H.join(n,"Sync"),label:"Sync",source:"default folder"}),e}function nr({home:n=lf(),current:e=null}={}){let t=[];t.push(...g1(n));let s=H.join(n,"Library","CloudStorage");try{for(let a of ae.readdirSync(s))/^Dropbox/i.test(a)&&t.push({provider:"dropbox",path:H.join(s,a),label:a.replace(/^Dropbox-?/,"Dropbox ").trim()})}catch{}cs(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");cs(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:Ln(l),...Ny[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:Iy(e,i)}:null}}function Iy(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 Ln(n){return st(H.join(n,"config.json"))&&(cs(H.join(n,"sessions"))||st(H.join(n,"README.md")))}function dc(n){let e=[],t=(s,o)=>{let r=[];try{r=ae.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()&&!y1(a)&&e.push(a))}};return t(n,""),e.sort()}function Mn(n){return Cy.createHash("sha256").update(ae.readFileSync(n)).digest("hex")}function rf(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 w1(n){return n==="config.json"?"config":n==="README.md"?"readme":n.startsWith(".secret/")?"secret":n.startsWith("lists/")?"list":n.startsWith("catchups/")?"catchup":"file"}function sr(n,e,{machine:t,hostname:s=fc.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"))+"."}),cs(n)||r.push({code:"no_source",message:"The current folder "+we(n)+" doesn’t exist."});let l=!1;if(st(e))if(!cs(e))r.push({code:"not_a_folder",message:we(e)+" is a file, not a folder."});else{let f=ae.readdirSync(e).filter(p=>!/^\.DS_Store$|^\.stfolder$|^\.stignore$|^Icon\r?$/.test(p));Ln(e)||f.some(p=>["sessions","machines","lists","catchups","digests"].includes(p))?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(;!st(f)&&f!==H.dirname(f);)f=H.dirname(f);try{ae.accessSync(f,ae.constants.W_OK)}catch{r.push({code:"not_writable",message:"Can’t write to "+we(f)+"."})}}if(l&&t){try{let f=JSON.parse(ae.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&&cs(H.join(e,"sessions",t))){let f=new Set(ae.readdirSync(H.join(e,"sessions",t))),p=cs(H.join(n,"sessions",t))?new Set(ae.readdirSync(H.join(n,"sessions",t))):new Set,h=[...f].filter(m=>!m.startsWith(".")&&!p.has(m));h.length&&a.push({machine:t,code:"machine_name_maybe_taken",message:"The folder already has "+h.length+" session"+(h.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 dc(n)){let p=H.join(n,f),h=H.join(e,f),m=ae.statSync(p).size;d.total++;let y="copy",S=null,b=f;if(l&&st(h))if(Mn(p)===Mn(h))y="same";else{let w=rf(f),A=w1(f);w&&w===t?(y="copy",S="this machine’s own file: ours is newer"):A==="list"?(y="conflict-copy",b=f.replace(/\.md$/,"")+".sync-conflict-"+af(new Date,!0)+"-LEFTOFF.md",S="the list changed on both sides: merged item by item"):A==="catchup"?(y="rename-copy",b=S1(e,f),S="same minute, different catch-up"):(y="keep-target",S=A==="config"?"the joined folder’s settings win":w?w+"’s own file: the joined folder has its latest":"the joined folder’s copy wins")}(y==="copy"||y==="conflict-copy"||y==="rename-copy")&&(u+=m),d[{copy:"copy",same:"same","keep-target":"keepTarget","conflict-copy":"conflictCopy","rename-copy":"renameCopy"}[y]]++,c.push({rel:f,dest:b,action:y,why:S,size:m})}if(l){let f=new Set;for(let p of dc(e)){let h=rf(p);h&&h!==t&&f.add(h)}f.size&&i.push({code:"merging",message:"Joining a folder that already has "+[...f].sort().join(", ")+"’s data. Nothing of theirs is overwritten."}),st(H.join(e,"config.json"))&&st(H.join(n,"config.json"))&&Mn(H.join(e,"config.json"))!==Mn(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:uf(n)}}function af(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 S1(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(!st(H.join(n,r)))return r}return s+"-"+Cy.randomBytes(3).toString("hex")+t}function uf(n,e=new Date){let t=n+".leftoff-backup-"+af(e);for(let s=2;st(t);s++)t=n+".leftoff-backup-"+af(e)+"-"+s;return t}async function df(n,{hooks:e={},machine:t,hostname:s=fc.hostname(),say:o=()=>{},copyFile:r=ae.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 y=[],S=m;for(;!st(S);)y.push(S),S=H.dirname(S);for(let b of y.reverse())ae.mkdirSync(b),l.push(b)},d=(m,y)=>{let S=H.join(n.to,y);u(H.dirname(S));let b=st(S),w=H.join(H.dirname(S),"."+H.basename(S)+".move-"+process.pid+".tmp");r(m,w);try{ae.chmodSync(w,ae.statSync(m).mode&511)}catch{}return ae.renameSync(w,S),b||a.push(S),S},f=()=>{for(let m of a.reverse())try{ae.unlinkSync(m)}catch{}for(let m of l.reverse())try{ae.rmdirSync(m)}catch{}},p=!1;try{o("Stopping the daemon so nothing writes mid-move"),e.stopWriters&&(await e.stopWriters(),p=!0),u(n.to),o("Copying "+n.counts.total+" files");let m=[];for(let b of n.files){if(b.action==="same"){c.verified++;continue}if(b.action==="keep-target"){c.kept++;continue}let w=d(H.join(n.from,b.rel),b.dest);(b.action==="conflict-copy"||b.action==="rename-copy")&&c.conflicts++,c.copied++,m.push([H.join(n.from,b.rel),w,b.rel])}n.files.find(b=>b.rel==="config.json"&&b.action==="keep-target")&&k1(n.from,n.to,s),o("Verifying "+m.length+" copies");for(let[b,w,A]of m){if(Mn(b)!==Mn(w))throw Object.assign(new Error("verify failed: "+A+" 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(b=>b.rel));for(let b of dc(n.from)){if(S.has(b)||st(H.join(n.to,b)))continue;let w=d(H.join(n.from,b),b);if(Mn(H.join(n.from,b))!==Mn(w))throw Object.assign(new Error("verify failed: "+b),{code:"verify_failed"});c.stragglers++}}catch(m){if(f(),p&&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 h=uf(n.from,i);try{ae.renameSync(n.from,h),ae.writeFileSync(H.join(h,"MOVED-HERE.md"),`# This is a Leftoff backup
221
224
 
222
225
  On `+i.toISOString()+` Leftoff moved this folder to:
223
226
 
@@ -225,21 +228,21 @@ On `+i.toISOString()+` Leftoff moved this folder to:
225
228
 
226
229
  Every file was copied and checked (sha-256). This copy is kept until you delete it:
227
230
  Settings → Sync on the board, or \`leftoff move --delete-backup\`. Leftoff never deletes it on its own.
228
- `),c.backup=h,e.switchTo&&await e.switchTo(n.to)}catch(m){try{!et(n.from)&&et(h)&&re.renameSync(h,n.from)}catch{}try{re.unlinkSync(K.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(y=>({ok:!1,error:y.message}));if(!m||m.ok===!1){try{e.stopWriters&&await e.stopWriters()}catch{}try{re.unlinkSync(K.join(h,"MOVED-HERE.md"))}catch{}try{re.renameSync(h,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 Ov(n,e,t){try{let s=JSON.parse(re.readFileSync(K.join(n,"config.json"),"utf8")),o=JSON.parse(re.readFileSync(K.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=K.join(e,".config.json.move.tmp");re.writeFileSync(i,JSON.stringify(o,null,2)+`
229
- `),re.renameSync(i,K.join(e,"config.json"))}catch{}}function Jo(n,{extra:e=[]}={}){let t=[],s=new Set,o=new Set([K.dirname(n),Pd()]),r=Gs(),i=[];for(let a of[...e,r&&r.backup])a&&i.push(K.resolve(a));for(let a of o){let l=[];try{l=re.readdirSync(a)}catch{continue}for(let c of l)i.push(K.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(K.basename(a))))continue;let l="";try{l=re.readFileSync(K.join(a,"MOVED-HERE.md"),"utf8")}catch{continue}let c=(l.match(/^ {4}(.+)$/m)||[])[1]||null;c&&K.resolve(c)!==K.resolve(n)||t.push({path:a,display:ge(a),movedTo:c,at:(l.match(/^On (\S+)/m)||[])[1]||null})}return t}function vi(n){let e=K.resolve(n);if(!/\.leftoff-backup-/.test(K.basename(e))||!et(K.join(e,"MOVED-HERE.md")))throw Object.assign(new Error("not a Leftoff backup folder: "+n),{code:"not_a_backup"});return re.rmSync(e,{recursive:!0,force:!0}),{deleted:e}}function qo(n,{cli:e="leftoff"}={}){let t=ge(n),s=/\s/.test(t)?'"'+t.replace(/^~\//,"$HOME/")+'"':t;return{installed:e+" move "+s,fresh:"npx -y github:"+Xe+" setup --dir "+s}}var Pd,et,os,ge,Gg,_v,$s=z(()=>{St();Pd=()=>process.env.HOME||ql.homedir(),et=n=>{try{return re.statSync(n),!0}catch{return!1}},os=n=>{try{return re.statSync(n).isDirectory()}catch{return!1}},ge=n=>{let e=Pd();return n===e?"~":n.startsWith(e+K.sep)?"~"+n.slice(e.length):n};Gg={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."}};_v=n=>/(^|\/)\.[^/]*\.tmp$|\.tmp(-\d+)?$|\.lock$|(^|\/)\.lock(\/|$)|^\.daemon\.pid$|(^|\/)\.DS_Store$|^MOVED-HERE\.md$/.test(n)});import Iv from"node:readline";function rs(n=process.stdout){return process.env.NO_COLOR?!1:process.env.FORCE_COLOR&&process.env.FORCE_COLOR!=="0"?!0:!!(n&&n.isTTY)}function is(n){let e={};for(let[t,[s,o]]of Object.entries(Nv))e[t]=r=>n?`\x1B[${s}m${r}\x1B[${o}m`:String(r);return e}function Dt(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 Ai(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 Mv(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 tt(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(Wg[o++%Wg.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)+`
230
- `)};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 Vl(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=Mv(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+`
231
+ `),c.backup=h,e.switchTo&&await e.switchTo(n.to)}catch(m){try{!st(n.from)&&st(h)&&ae.renameSync(h,n.from)}catch{}try{ae.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(y=>({ok:!1,error:y.message}));if(!m||m.ok===!1){try{e.stopWriters&&await e.stopWriters()}catch{}try{ae.unlinkSync(H.join(h,"MOVED-HERE.md"))}catch{}try{ae.renameSync(h,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 k1(n,e,t){try{let s=JSON.parse(ae.readFileSync(H.join(n,"config.json"),"utf8")),o=JSON.parse(ae.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");ae.writeFileSync(i,JSON.stringify(o,null,2)+`
232
+ `),ae.renameSync(i,H.join(e,"config.json"))}catch{}}function or(n,{extra:e=[]}={}){let t=[],s=new Set,o=new Set([H.dirname(n),lf()]),r=Vs(),i=[];for(let a of[...e,r&&r.backup])a&&i.push(H.resolve(a));for(let a of o){let l=[];try{l=ae.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=ae.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 Mi(n){let e=H.resolve(n);if(!/\.leftoff-backup-/.test(H.basename(e))||!st(H.join(e,"MOVED-HERE.md")))throw Object.assign(new Error("not a Leftoff backup folder: "+n),{code:"not_a_backup"});return ae.rmSync(e,{recursive:!0,force:!0}),{deleted:e}}function rr(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:"+tt+" setup --dir "+s}}var lf,st,cs,we,Ny,y1,Zs=J(()=>{Tt();lf=()=>process.env.HOME||fc.homedir(),st=n=>{try{return ae.statSync(n),!0}catch{return!1}},cs=n=>{try{return ae.statSync(n).isDirectory()}catch{return!1}},we=n=>{let e=lf();return n===e?"~":n.startsWith(e+H.sep)?"~"+n.slice(e.length):n};Ny={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."}};y1=n=>/(^|\/)\.[^/]*\.tmp$|\.tmp(-\d+)?$|\.lock$|(^|\/)\.lock(\/|$)|^\.daemon\.pid$|(^|\/)\.DS_Store$|^MOVED-HERE\.md$/.test(n)});import b1 from"node:readline";function us(n=process.stdout){return process.env.NO_COLOR?!1:process.env.FORCE_COLOR&&process.env.FORCE_COLOR!=="0"?!0:!!(n&&n.isTTY)}function ds(n){let e={};for(let[t,[s,o]]of Object.entries(v1))e[t]=r=>n?`\x1B[${s}m${r}\x1B[${o}m`:String(r);return e}function Ut(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 Di(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 x1(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 ot(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(Ly[o++%Ly.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)+`
233
+ `)};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 hc(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=x1(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+`
231
234
  `))},done(a){e.write((o?"\r\x1B[2K":"")+t.green("✔")+" "+a+`
232
235
  `)},fail(a){e.write((o?"\r\x1B[2K":"")+t.red("✖")+" "+a+`
233
- `)}}}function zg(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 p=a.toLowerCase();return p?e.filter(h=>(h.search||h.label+" "+(h.hint||"")).toLowerCase().includes(p)):e},u=()=>{let p=c();i>=p.length&&(i=Math.max(0,p.length-1)),l&&s.write("\x1B["+l+"A\x1B[0J");let h=[o.cyan("?")+" "+o.bold(n)+" "+o.dim(a?"filter: "+a:"↑↓ move · enter go · type to filter · esc quit")];p.slice(0,15).forEach((m,y)=>{let w=y===i;h.push((w?o.cyan(" › "):" ")+o.dim((y<9?String(y+1):" ")+" ")+(w?o.bold(m.label):m.label)+(m.hint?" "+o.dim(m.hint):"")),m.detail&&h.push(" "+o.dim(m.detail))}),p.length||h.push(o.dim(" nothing matches")),s.write(h.join(`
236
+ `)}}}function Dy(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 p=a.toLowerCase();return p?e.filter(h=>(h.search||h.label+" "+(h.hint||"")).toLowerCase().includes(p)):e},u=()=>{let p=c();i>=p.length&&(i=Math.max(0,p.length-1)),l&&s.write("\x1B["+l+"A\x1B[0J");let h=[o.cyan("?")+" "+o.bold(n)+" "+o.dim(a?"filter: "+a:"↑↓ move · enter go · type to filter · esc quit")];p.slice(0,15).forEach((m,y)=>{let S=y===i;h.push((S?o.cyan(" › "):" ")+o.dim((y<9?String(y+1):" ")+" ")+(S?o.bold(m.label):m.label)+(m.hint?" "+o.dim(m.hint):"")),m.detail&&h.push(" "+o.dim(m.detail))}),p.length||h.push(o.dim(" nothing matches")),s.write(h.join(`
234
237
  `)+`
235
238
  `),l=h.length},d=p=>{t.removeListener("data",f),t.setRawMode&&t.setRawMode(!1),t.pause(),s.write("\x1B[?25h"),r(p)},f=p=>{let h=String(p),m=c();if(h===""||h==="\x1B"||h==="q"&&!a)return d(null);if(h==="\r"||h===`
236
- `)return d(m[i]?m[i].value:null);if(h==="\x1B[A"||h==="k"&&!a)i=Math.max(0,i-1);else if(h==="\x1B[B"||h==="j"&&!a)i=Math.min(m.length-1,i+1);else{if(/^[1-9]$/.test(h)&&!a&&m[Number(h)-1])return d(m[Number(h)-1].value);h===""||h==="\b"?a=a.slice(0,-1):/^[\x20-\x7e]$/.test(h)&&(a+=h,i=0)}u()};t.setRawMode&&t.setRawMode(!0),t.resume(),t.on("data",f),s.write("\x1B[?25l"),u()})}function Jg(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,p=[o.cyan("?")+" "+o.bold(n)+" "+o.dim("space toggle · a all/none · enter restore "+f+" · esc quit")],h=Math.max(0,Math.min(i-7,e.length-15));e.slice(h,h+15).forEach((m,y)=>{let w=h+y,S=w===i;p.push((S?o.cyan(" › "):" ")+(l[w]?o.green("[x]"):o.dim("[ ]"))+" "+(S?o.bold(m.label):m.label)+(m.hint?" "+o.dim(m.hint):""))}),e.length>15&&p.push(o.dim(" … "+e.length+" in all")),s.write(p.join(`
239
+ `)return d(m[i]?m[i].value:null);if(h==="\x1B[A"||h==="k"&&!a)i=Math.max(0,i-1);else if(h==="\x1B[B"||h==="j"&&!a)i=Math.min(m.length-1,i+1);else{if(/^[1-9]$/.test(h)&&!a&&m[Number(h)-1])return d(m[Number(h)-1].value);h===""||h==="\b"?a=a.slice(0,-1):/^[\x20-\x7e]$/.test(h)&&(a+=h,i=0)}u()};t.setRawMode&&t.setRawMode(!0),t.resume(),t.on("data",f),s.write("\x1B[?25l"),u()})}function Ry(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,p=[o.cyan("?")+" "+o.bold(n)+" "+o.dim("space toggle · a all/none · enter restore "+f+" · esc quit")],h=Math.max(0,Math.min(i-7,e.length-15));e.slice(h,h+15).forEach((m,y)=>{let S=h+y,b=S===i;p.push((b?o.cyan(" › "):" ")+(l[S]?o.green("[x]"):o.dim("[ ]"))+" "+(b?o.bold(m.label):m.label)+(m.hint?" "+o.dim(m.hint):""))}),e.length>15&&p.push(o.dim(" … "+e.length+" in all")),s.write(p.join(`
237
240
  `)+`
238
241
  `),a=p.length},u=f=>{t.removeListener("data",d),t.setRawMode&&t.setRawMode(!1),t.pause(),s.write("\x1B[?25h"),r(f)},d=f=>{let p=String(f);if(p===""||p==="\x1B"||p==="q")return u(null);if(p==="\r"||p===`
239
- `)return u(e.filter((h,m)=>l[m]).map(h=>h.value));if(p==="\x1B[A"||p==="k")i=Math.max(0,i-1);else if(p==="\x1B[B"||p==="j")i=Math.min(e.length-1,i+1);else if(p===" ")l[i]=!l[i];else if(p==="a"){let h=l.every(Boolean);l.fill(!h)}c()};t.setRawMode&&t.setRawMode(!0),t.resume(),t.on("data",d),s.write("\x1B[?25l"),c()})}var xi,Nv,ee,Wg,ft,In=z(()=>{xi=class extends Error{constructor(e="cancelled"){super(e),this.cancelled=!0}};Nv={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=is(rs());Wg=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];ft=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 xi("cancelled (Ctrl-C)"):r}finally{this.interruptHandlers.delete(s),process.removeListener("SIGINT",o)}}_open(){if(this.rl||this.yes)return;this.rl=Iv.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(`
242
+ `)return u(e.filter((h,m)=>l[m]).map(h=>h.value));if(p==="\x1B[A"||p==="k")i=Math.max(0,i-1);else if(p==="\x1B[B"||p==="j")i=Math.min(e.length-1,i+1);else if(p===" ")l[i]=!l[i];else if(p==="a"){let h=l.every(Boolean);l.fill(!h)}c()};t.setRawMode&&t.setRawMode(!0),t.resume(),t.on("data",d),s.write("\x1B[?25l"),c()})}var Li,v1,ee,Ly,yt,Dn=J(()=>{Li=class extends Error{constructor(e="cancelled"){super(e),this.cancelled=!0}};v1={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=ds(us());Ly=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];yt=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 Li("cancelled (Ctrl-C)"):r}finally{this.interruptHandlers.delete(s),process.removeListener("SIGINT",o)}}_open(){if(this.rl||this.yes)return;this.rl=b1.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(`
240
243
  `):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)+`
241
- `),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 xi(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)+`
242
- `),Promise.resolve(t)}return this.pendingCancel||this.closed?Promise.reject(new xi(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)+`
244
+ `),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 Li(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)+`
245
+ `),Promise.resolve(t)}return this.pendingCancel||this.closed?Promise.reject(new Li(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)+`
243
246
  `)}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(`
244
247
  `);let l=a===""?t:a,c=o?o(l):null;if(!c)return l;this.out.write(" "+this.c.red(c)+`
245
248
  `)}}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.")+`
@@ -247,12 +250,12 @@ Settings → Sync on the board, or \`leftoff move --delete-backup\`. Leftoff nev
247
250
  `),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):"")+`
248
251
  `),r.detail&&this.out.write(" "+this.c.dim(r.detail)+`
249
252
  `)});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+".")+`
250
- `)}}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 Tt from"node:fs";import Lv from"node:os";import Ue from"node:path";import{spawnSync as Dv}from"node:child_process";import{fileURLToPath as Rv}from"node:url";function Xl(n=process.env){return n.CHECKPOINTS_CLAUDE_BIN?n.CHECKPOINTS_CLAUDE_BIN:yo("claude",n)}function Nn(n,e,{env:t=process.env,timeoutMs:s=12e4}={}){let o=Dv(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 jv(n){let e=String(n||"").match(/(\d+)\.(\d+)\.(\d+)/);return e?e[0]:null}function Fv(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 Vg(n=process.env){let e=Xl(n);if(!e)return{found:!1,bin:null,version:null,modsSupported:!1};let t=Nn(e,["--version"],{env:n,timeoutMs:15e3}),s=jv(t.stdout+" "+t.stderr);return{found:!0,bin:e,version:s,modsSupported:!!s&&Fv(s,Ti)>=0}}function Hd(n){return/[\\/]_npx[\\/]/.test(n)||/[\\/]\.npm[\\/]/.test(n)}function Bd(n=Vo){try{return JSON.parse(Tt.readFileSync(Ue.join(n,"package.json"),"utf8")).leftoffBuild==="npm"}catch{return!1}}function Uv(n=process.env){return n.LEFTOFF_PLUGIN_DIR?n.LEFTOFF_PLUGIN_DIR:Ue.join(n.XDG_CONFIG_HOME||Ue.join(n.HOME||Lv.homedir(),".config"),"checkpoints","plugin")}function Kd({from:n=Vo,env:e=process.env}={}){let t=Uv(e);if(!Tt.existsSync(Ue.join(n,".claude-plugin","marketplace.json")))throw new Error("no .claude-plugin/marketplace.json in "+n);let s=t+"."+process.pid+".tmp";Tt.rmSync(s,{recursive:!0,force:!0}),Tt.mkdirSync(Ue.join(s,".claude-plugin"),{recursive:!0}),Tt.copyFileSync(Ue.join(n,".claude-plugin","marketplace.json"),Ue.join(s,".claude-plugin","marketplace.json"));let o=r=>/^(tests|tsconfig\.json)$/.test(r)||/(^|[\\/])types([\\/]|$)/.test(r);return Tt.cpSync(Ue.join(n,"mod"),Ue.join(s,"mod"),{recursive:!0,filter:r=>!o(Ue.relative(Ue.join(n,"mod"),r))}),Tt.writeFileSync(Ue.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"),Tt.rmSync(t,{recursive:!0,force:!0}),Tt.mkdirSync(Ue.dirname(t),{recursive:!0}),Tt.renameSync(s,t),t}function Zg({repoDir:n=Vo,override:e,env:t=process.env,copy:s=Kd}={}){if(e)return e;if(!Hd(n)&&Tt.existsSync(Ue.join(n,".git"))&&Tt.existsSync(Ue.join(n,".claude-plugin","marketplace.json")))return n;if(Bd(n)&&Tt.existsSync(Ue.join(n,".claude-plugin","marketplace.json")))try{return s({from:n,env:t})}catch{}return Zl}function qg(n,e=Ce){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 Hv(n){return new RegExp("(^|[^\\w-])"+on+"([^\\w-]|$)","m").test(String(n||""))}function Ql(n,e){let t=Nn(n,["plugin","list"],{env:e,timeoutMs:3e4}),s=Nn(n,["plugin","marketplace","list"],{env:e,timeoutMs:3e4}),o=qg(t.stdout,as);return{...qg(t.stdout),legacyInstalled:o.installed,legacyEnabled:o.enabled,marketplaceAdded:s.code===0&&Hv(s.stdout),listOk:t.code===0}}function Gd({bin:n,env:e=process.env,state:t=null,onStep:s=()=>{}}){let o=t||Ql(n,e);if(!o.legacyInstalled)return{ok:!0,migrated:!1,steps:[]};let r=[],i=c=>{s("claude "+c.join(" "));let u=Nn(n,c,{env:e});return r.push(u),u},a=c=>(c.stderr||c.stdout).trim().split(`
251
- `).pop()||"exit "+c.code;if(!o.installed){i(["plugin","marketplace","update",on]);let c=i(["plugin","install",Ce]);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",Ce])}let l=i(["plugin","uninstall",as]);return l.code!==0?{ok:!1,migrated:!0,steps:r,error:"installed "+Ce+" but couldn't uninstall "+as+": "+a(l)}:{ok:!0,migrated:!0,enabled:o.legacyEnabled!==!1,steps:r}}function $d({bin:n,source:e,env:t=process.env,onStep:s=()=>{}}){let o=[],r=Ql(n,t);if(r.legacyInstalled){let l=Gd({bin:n,env:t,state:r,onStep:s});if(!l.ok)return{ok:!1,steps:l.steps,state:r,error:l.error};let c=$d({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 "+Ce);let l=Nn(n,["plugin","enable",Ce],{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=Nn(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 "+Ce);let i=Nn(n,["plugin","install",Ce],{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 Xg(n){return!!(n&&n.disableAllHooks===!0)}var Vo,Zl,on,Pv,Ce,as,Ti,ec=z(()=>{Is();St();Vo=Ue.resolve(Ue.dirname(Rv(import.meta.url)),".."),Zl=Xe,on=Wp,Pv=zp,Ce=Pv+"@"+on,as=Jp+"@"+on,Ti="2.1.287"});import Bv from"node:dns/promises";import Kv from"node:http";import Gv from"node:fs";import Qg from"node:path";import{spawn as $v}from"node:child_process";function Yv(n){return{pretty:"http://"+wt+":"+n+"/",plain:"http://127.0.0.1:"+n+"/"}}async function zv(n=wt,e=t=>Bv.lookup(t,{all:!0})){try{let t=await Promise.race([e(n),ty(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(Wv)}catch{return!1}}function Jv(n,e,t){return new Promise(s=>{let o=Kv.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=zv,probe:o=Jv}={}){let r=Yv(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"}:[wt,"pretty","name"].includes(i)?{url:r.pretty,pretty:!0,reason:"asked for "+wt}:await s(wt)?await o(n,wt+":"+n,t)==="refused"?{url:r.plain,pretty:!1,reason:"the running daemon predates "+wt+" support (restart it)"}:{url:r.pretty,pretty:!0,reason:wt+" resolves to loopback"}:{url:r.plain,pretty:!1,reason:wt+" doesn't resolve to loopback here"}}function qv(n,{platform:e=process.platform,spawnImpl:t=$v}={}){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 ey(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 ny({flags:n={},out:e=process.stdout,bin:t,env:s=process.env,openImpl:o=qv,startImpl:r}={}){let i=V(),a=X(i),l=Number(n.port||a.port),c=Se(i),u=m=>{n.quiet||e.write(m+`
252
- `)},d=await ey(l,c);if(!d.up&&!n["no-start"]){if(!Gv.existsSync(Qg.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(()=>(Ys(),tc))).startDaemonDetached)({root:i,bin:t,env:s});for(let y=0;y<40&&!(d=await ey(l,c)).up;y++)await ty(250);if(!d.up)return u("The daemon didn't start; see "+Qg.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",p=await _i({port:l,prefer:f,token:c});if(n.print||n["no-browser"])return e.write(p.url+`
253
- `),0;let h=o(p.url);return u((h?"Opened ":"Open ")+p.url+(p.pretty?"":" ("+p.reason+")")),0}var ty,Wv,Yd=z(()=>{ke();Wd();ty=n=>new Promise(e=>setTimeout(e,n));Wv=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(`
254
- `)}var zd,sy,Vv,oy,ry,Jd=z(()=>{zd={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."},sy={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."]},Vv={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."]},oy={short:{...zd},informative:{...zd,...sy},long:{...zd,...sy,...Vv}},ry=[{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 iy={};Jt(iy,{RESERVED:()=>Ei,checkUserName:()=>Mn,domainsOf:()=>Qv,isReserved:()=>Vd,passthroughOf:()=>Zv,userFromHost:()=>Xv,validUser:()=>qd});function qd(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 Vd(n){return Ei.includes(String(n||"").toLowerCase())}function Zv(n){return String(n&&n.PASSTHROUGH||"").toLowerCase().split(/[\s,]+/).filter(Boolean)}function Mn(n){let e=String(n||"").trim().toLowerCase();return qd(e)?Vd(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 Xv(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(".")||!qd(r)||Vd(r)||t.includes(r)?null:r}return null}function Qv(n){let e=[String(n.DOMAIN||"leftoff.dev").toLowerCase()];return n.DEV==="1"&&e.push("localhost"),e}var Ei,Ci=z(()=>{Ei=["www","app","api","docs","feed","relay","admin","status","mail"]});var fy={};Jt(fy,{BAD_NAME_MESSAGE:()=>oc,POLL_MS:()=>ay,SIGNUP_TTL_MS:()=>ly,checkName:()=>Ii,relayGet:()=>cy,relayPost:()=>Oi,reservedMessage:()=>sc,signupProblem:()=>dy,signupStatus:()=>ic,startSignup:()=>Ni,validEmail:()=>rc,waitForConfirm:()=>Zd});import e1 from"node:http";import t1 from"node:https";function rc(n){let e=String(n||"").trim();return e.length<=254&&/^[^\s@<>()",;:]+@[^\s@<>()",;:]+\.[a-z]{2,}$/i.test(e)}function Oi(n,e,t,s=null){return uy(n,"POST",e,t,s)}function cy(n,e){return uy(n,"GET",e)}function uy(n,e,t,s,o=null){let r=n.origin?new URL(n.origin):new URL("https://"+Zo(n)),i=e==="GET"?null:Buffer.from(JSON.stringify(s)),a=r.protocol==="https:"?t1:e1;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:Zo(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:Zo(n)}:{}},d=>{let f="";d.on("data",p=>f+=p),d.on("end",()=>{let p={};try{p=JSON.parse(f)}catch{}l({status:d.statusCode,json:p,text:f})})});u.on("timeout",()=>u.destroy(new Error("timed out"))),u.on("error",c),u.end(i||void 0)})}function dy(n,e){let t=n&&n.json||{};return n.status===404&&!t.code?Zo(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 Ii({s:n,name:e,sign:t}){let s=Mn(e);if(!s.ok)return{ok:!1,code:s.code,message:s.code==="reserved"?sc(String(e).trim().toLowerCase()):oc};let o={...n,user:s.user},r=Zo(o),i;try{i=await cy(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:sc(s.user)};if(a.status==="invalid")return{ok:!1,code:"bad_name",message:oc};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 Ni({s:n,name:e,email:t,machine:s,sign:o}){let r=Mn(e);if(!r.ok)return{ok:!1,status:400,code:r.code,message:r.code==="reserved"?sc(e):oc};if(!rc(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 Oi(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 "+Zo(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:dy(a,i),retryAfter:a.json.retryAfter||null}}async function ic({s:n,user:e,poll:t}){try{let s=await Oi({...n,user:e},"/_signup/status",{poll:t});return s.json.status||(s.status===404?"unknown":"error")}catch{return"error"}}async function Zd({s:n,user:e,poll:t,intervalMs:s=ay,timeoutMs:o=ly+6e4,stop:r=()=>!1,onTick:i=()=>{}}){let a=Date.now();for(;;){if(r())return"stopped";let l=await ic({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 Zo,ay,ly,sc,oc,ac=z(()=>{Ci();Zo=n=>n.user+"."+n.domain,ay=2e3,ly=1800*1e3;sc=n=>"“"+n+"” is reserved (www, app, api, docs, feed, relay, admin, status, mail). Pick another.",oc="A name is 2–32 lowercase letters, digits or hyphens."});function Mi(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 Li(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 Qd(n){return py.getRandomValues(new Uint8Array(n))}function gy(n=16){return Mi(Qd(n))}function yy(){let n=Qd(16),e="";for(let t=0;t<16;t++)e+=my[n[t]&31];return e.match(/.{4}/g).join("-")}function wy(n){let e=String(n||"").toUpperCase().replace(/[\s-]/g,"").replace(/O/g,"0").replace(/[IL]/g,"1");return e.length!==16||[...e].some(t=>!my.includes(t))?null:e.match(/.{4}/g).join("-")}async function Sy(n){return _t.importKey("raw",ls.encode(wy(n)||""),{name:"HMAC",hash:"SHA-256"},!1,["sign","verify"])}async function ky(n,e){let t=await Sy(n);return Mi(await _t.sign("HMAC",t,ls.encode("leftoff-pair-v1|"+e.join("|"))))}async function by(n,e,t){let s=await Sy(n);try{return await _t.verify("HMAC",s,Li(t),ls.encode("leftoff-pair-v1|"+e.join("|")))}catch{return!1}}async function vy({extractable:n=!1}={}){let e=await _t.generateKey({name:"ECDH",namedCurve:"P-256"},n,["deriveBits"]),t=Mi(await _t.exportKey("raw",e.publicKey));return{privateKey:e.privateKey,pub:t}}async function xy({privateKey:n,peerPub:e,code:t,device:s,extractable:o=!1}){let r=await _t.importKey("raw",Li(e),{name:"ECDH",namedCurve:"P-256"},!1,[]),i=await _t.deriveBits({name:"ECDH",public:r},n,256),a=await _t.importKey("raw",i,"HKDF",!1,["deriveKey","deriveBits"]),l={name:"HKDF",hash:"SHA-256",salt:ls.encode(wy(t)),info:ls.encode("leftoff-relay-device-v1|"+s)};if(o){let c=new Uint8Array(await _t.deriveBits(l,a,256));return{raw:c,key:await cc(c)}}return{key:await _t.deriveKey(l,a,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}}function cc(n){return _t.importKey("raw",n instanceof Uint8Array?n:Li(n),{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}async function Ay(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 rn("bad_envelope");if(n.to!==t)throw new rn("wrong_target","sealed for "+n.to);if(Math.abs(o-n.ts)>hy)throw new rn("expired","the request is too old (or a clock is off)");let r=await e(n.d);if(!r)throw new rn("unknown_device","this browser isn’t paired with this Mac, or it was revoked");if(s&&s.has(n.d+"|"+n.id))throw new rn("replayed");let i;try{i=JSON.parse(n1.decode(await _t.decrypt({name:"AES-GCM",iv:Li(n.iv),additionalData:s1(n)},r,Li(n.ct))))}catch{throw new rn("decrypt_failed")}return s&&s.add(n.d+"|"+n.id,n.ts+hy),i}async function Ty(n,e,t){let s=Qd(12),o=await _t.encrypt({name:"AES-GCM",iv:s,additionalData:o1(null,e)},n,ls.encode(JSON.stringify(t)));return{v:1,re:e.id,iv:Mi(s),ct:Mi(o)}}var py,_t,ls,n1,my,Xd,hy,s1,o1,rn,lc,_y=z(()=>{py=globalThis.crypto,_t=py.subtle,ls=new TextEncoder,n1=new TextDecoder,my="0123456789ABCDEFGHJKMNPQRSTVWXYZ",Xd=600*1e3,hy=120*1e3;s1=n=>ls.encode(["leftoff-tunnel-v1","req",n.d,n.to,n.id,n.ts].join("|")),o1=(n,e)=>ls.encode(["leftoff-tunnel-v1","res",e.d,e.to,e.id].join("|")),rn=class extends Error{constructor(e,t){super(t||e),this.code=e}};lc=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 Iy={};Jt(Iy,{DEFAULT_DOMAIN:()=>Ln,RESERVED:()=>Ei,RelayHost:()=>Pi,boardUrlFor:()=>Ri,connectTarget:()=>tf,devicesDir:()=>Ws,hostFor:()=>Rt,localRelayFile:()=>fc,problemText:()=>of,readRelaySettings:()=>Dn,sharedAccountFile:()=>hc,unwrapKey:()=>sf,wrapKey:()=>nf,writeRelaySettings:()=>us});import cs from"node:fs";import Pe from"node:path";import r1 from"node:http";import nt from"node:crypto";import{EventEmitter as i1}from"node:events";import{fileURLToPath as a1}from"node:url";function fc(n=process.env){return Pe.join(Pe.dirname(qt(n)),"relay.json")}function hc(n){return Pe.join(n,"relay","account.json")}function Ws(n){return Pe.join(n,"relay","devices")}function zs(n){try{return JSON.parse(cs.readFileSync(n,"utf8"))}catch{return null}}function Di(n,e,t){cs.mkdirSync(Pe.dirname(n),{recursive:!0});let s=Pe.join(Pe.dirname(n),"."+Pe.basename(n)+"."+process.pid+".tmp");cs.writeFileSync(s,JSON.stringify(e,null,2)+`
255
- `,t?{mode:t}:void 0),cs.renameSync(s,n)}function Dn(n,e=process.env){let t=zs(fc(e))||{},s=zs(hc(n))||{},o=String(e.LEFTOFF_RELAY_USER||t.user||s.user||"").toLowerCase()||null,r=String(e.LEFTOFF_RELAY_DOMAIN||t.domain||s.domain||Ln).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 us(n,e,t=process.env){let o={...zs(fc(t))||{},...e};if(Di(fc(t),o,384),e.user){let r=zs(hc(n))||{};(r.user!==e.user||e.domain&&r.domain!==e.domain)&&Di(hc(n),{user:e.user,domain:e.domain||r.domain||Ln,updatedAt:new Date().toISOString()})}return o}function Rt(n){return n.user+"."+n.domain}function Ri(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://"+Rt(n)}function tf(n){if(n.origin){let e=new URL(n.origin);return{url:(e.protocol==="https:"?"wss://":"ws://")+e.host+"/_connect",hostHeader:Rt(n)+(e.port?":"+e.port:"")}}return{url:"wss://"+Rt(n)+"/_connect",hostHeader:null}}function nf(n,{device:e,machine:t,box:s}){let o=nt.generateKeyPairSync("x25519"),r=nt.createPublicKey({key:{kty:"OKP",crv:"X25519",x:s},format:"jwk"}),i=nt.diffieHellman({privateKey:o.privateKey,publicKey:r}),a=nt.randomBytes(16),l=Buffer.from(nt.hkdfSync("sha256",i,a,"leftoff-relay-wrap-v1|"+t+"|"+e,32)),c=nt.randomBytes(12),u=nt.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 sf(n,{device:e,machine:t,boxPriv:s}){let o=nt.createPublicKey({key:{kty:"OKP",crv:"X25519",x:n.epk},format:"jwk"}),r=nt.diffieHellman({privateKey:s,publicKey:o}),i=Buffer.from(nt.hkdfSync("sha256",r,dc(n.salt),"leftoff-relay-wrap-v1|"+t+"|"+e,32)),a=dc(n.ct),l=nt.createDecipheriv("aes-256-gcm",i,dc(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 ef(n,e){let{sig:t,...s}=n;return s.by=e.machine,{...s,sig:uc(nt.sign(null,Buffer.from(xt(s)),e.signPriv))}}function c1(n,e){try{let{sig:t,...s}=n;return nt.verify(null,Buffer.from(xt(s)),nt.createPublicKey({key:{kty:"OKP",crv:"Ed25519",x:e},format:"jwk"}),dc(t))}catch{return!1}}function Oy(n){return String(n||"Browser").replace(/[\u0000-\u001f<>]/g,"").trim().slice(0,60)||"Browser"}function of(n,e={}){return n?n==="not_registered"?"this Mac isn’t on "+(e.user||"?")+"."+(e.domain||Ln)+" 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||Ln)+" (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 Ln,Ey,l1,Cy,uc,dc,Pi,pc=z(()=>{bd();Zn();ke();ki();ac();Ci();_y();Ln="leftoff.dev",Ey=Pe.join(Pe.dirname(a1(import.meta.url)),"..","ui"),l1={".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"},Cy=4*1024*1024,uc=n=>Buffer.from(n).toString("base64url"),dc=n=>Buffer.from(String(n||""),"base64url");Pi=class extends i1{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 lc,this.innerSecret=nt.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=Tn(this.machine))}settings(){return Dn(this.root,this.env)}active(){let e=this.settings();return!!(e.enabled&&e.user)}url(){return this.active()?tf(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?Ri(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=tf(e),s=new Ho({url:t.url,hostHeader:t.hostHeader,id:this.id,user:e.user,log:this.log,helloExtra:()=>({version:q,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 "+Rt(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=Mn(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 us(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 us(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 Ii({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 Ni({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 ic({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();us(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?Rt(e):null,boardUrl:e.user?Ri(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?of(t.lastError,e):null,relayMachines:t&&t.relayMachines?t.relayMachines:null,devices:this.devices().length,reserved:Ei,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=Pe.join(Ey,Pe.normalize(t).replace(/^(\.\.[/\\])+/,""));return!s.startsWith(Ey+Pe.sep)||/(^|[/\\])\./.test(t)||!cs.existsSync(s)||cs.statSync(s).isDirectory()?{t:"static.res",id:e.id,status:404}:{t:"static.res",id:e.id,status:200,type:l1[Pe.extname(s)]||"application/octet-stream",b64:cs.readFileSync(s).toString("base64")}}async tunnel(e){let t;try{t=await Ay(e,{keyFor:i=>this.keyFor(i),machine:this.machine,seen:this.seen})}catch(i){return{error:{code:i instanceof rn?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 Ty(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=r1.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<=Cy&&c.push(d)}),l.on("end",()=>o({s:l.statusCode,h:{"content-type":l.headers["content-type"]||"application/json"},b:u>Cy?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?" ("+of(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=yy();while(this.pairs.has(t.slice(0,4)));let s=t.slice(0,4),o=e+Xd,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(Xd/1e3),qrSvg:zl(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 by(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=gy(12),r=await vy(),i;try{i=(await xy({privateKey:r.privateKey,peerPub:e.pub,code:s.code,device:o,extractable:!0})).raw}catch{return{ok:!1,code:"bad_key"}}let a=await ky(s.code,["mac",e.pair,e.pub,r.pub,o]);return this.saveDevice({id:o,name:Oy(e.name),createdAt:new Date(t).toISOString(),pairedOn:this.machine},i),this.keys.set(o,{key:await cc(i),at:Date.now()}),this.log("relay: paired a new browser, "+Oy(e.name)+" ("+o.slice(0,6)+"…)"),this.changed(),{ok:!0,device:o,pub:r.pub,mac:a}}trustedCards(){let e=Bs(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}An(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]=nf(t,{device:e.id,machine:o,box:r.box}));Di(Pe.join(Ws(this.root),e.id+".json"),ef({v:1,...e,wraps:s},this.id))}deviceFiles(){let e=[];try{e=cs.readdirSync(Ws(this.root)).filter(t=>/^[\w-]+\.json$/.test(t))}catch{return[]}return e.map(t=>zs(Pe.join(Ws(this.root),t))).filter(t=>t&&t.id)}verified(e){let s=this.trustedCards()[e.by];return!!(s&&c1(e,s.sign))}deviceRecord(e){if(!/^[\w-]{8,64}$/.test(String(e||"")))return null;let t=zs(Pe.join(Ws(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 cc(sf(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=Pe.join(Ws(this.root),e+".json"),s=zs(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;Di(t,ef({...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=Bs(this.root);for(let[o,r]of Object.entries(s))if(!(o===this.machine||t.has(o)||!r.valid||r.card.anywhere!==e.user)&&An(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=sf(t.wraps[this.machine],{device:t.id,machine:this.machine,boxPriv:this.id.boxPriv}),r={...t.wraps};for(let i of s)r[i]=nf(o,{device:t.id,machine:i,box:e[i].box});Di(Pe.join(Ws(this.root),t.id+".json"),ef({...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 tc={};Jt(tc,{SUMMARIZER_FLAG:()=>af,daemonHealth:()=>Qo,flagBool:()=>ot,ghSetupStep:()=>Fy,globalBinPath:()=>lf,lengthFlow:()=>Py,linkFolder:()=>Sc,localModelFlow:()=>kc,mergeSetupConfig:()=>yc,ollamaModelFlow:()=>Ry,patchDaemonConfig:()=>mc,relaySignupStep:()=>jy,runSetup:()=>uf,showTestResult:()=>Vs,startDaemonDetached:()=>gc,testFlow:()=>er,writeConfigFile:()=>wc});import Ve from"node:fs";import qs from"node:os";import Ne from"node:path";import{spawn as Xo,spawnSync as Js}from"node:child_process";function ot(n,e){if(n["no-"+e])return!1;if(n[e]!==void 0)return d1(n[e])?!1:u1(n[e])||n[e]===""?!0:!!n[e]}function f1(n,e){let t=e;return{c:t,line:(s="")=>n.write(s+`
253
+ `)}}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 It from"node:fs";import A1 from"node:os";import Ke from"node:path";import{spawnSync as T1}from"node:child_process";import{fileURLToPath as _1}from"node:url";function mc(n=process.env){return n.CHECKPOINTS_CLAUDE_BIN?n.CHECKPOINTS_CLAUDE_BIN:_o("claude",n)}function Rn(n,e,{env:t=process.env,timeoutMs:s=12e4}={}){let o=T1(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 O1(n){let e=String(n||"").match(/(\d+)\.(\d+)\.(\d+)/);return e?e[0]:null}function C1(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 jy(n=process.env){let e=mc(n);if(!e)return{found:!1,bin:null,version:null,modsSupported:!1};let t=Rn(e,["--version"],{env:n,timeoutMs:15e3}),s=O1(t.stdout+" "+t.stderr);return{found:!0,bin:e,version:s,modsSupported:!!s&&C1(s,Ri)>=0}}function ff(n){return/[\\/]_npx[\\/]/.test(n)||/[\\/]\.npm[\\/]/.test(n)}function hf(n=ir){try{return JSON.parse(It.readFileSync(Ke.join(n,"package.json"),"utf8")).leftoffBuild==="npm"}catch{return!1}}function N1(n=process.env){return n.LEFTOFF_PLUGIN_DIR?n.LEFTOFF_PLUGIN_DIR:Ke.join(n.XDG_CONFIG_HOME||Ke.join(n.HOME||A1.homedir(),".config"),"checkpoints","plugin")}function pf({from:n=ir,env:e=process.env}={}){let t=N1(e);if(!It.existsSync(Ke.join(n,".claude-plugin","marketplace.json")))throw new Error("no .claude-plugin/marketplace.json in "+n);let s=t+"."+process.pid+".tmp";It.rmSync(s,{recursive:!0,force:!0}),It.mkdirSync(Ke.join(s,".claude-plugin"),{recursive:!0}),It.copyFileSync(Ke.join(n,".claude-plugin","marketplace.json"),Ke.join(s,".claude-plugin","marketplace.json"));let o=r=>/^(tests|tsconfig\.json)$/.test(r)||/(^|[\\/])types([\\/]|$)/.test(r);return It.cpSync(Ke.join(n,"mod"),Ke.join(s,"mod"),{recursive:!0,filter:r=>!o(Ke.relative(Ke.join(n,"mod"),r))}),It.writeFileSync(Ke.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"),It.rmSync(t,{recursive:!0,force:!0}),It.mkdirSync(Ke.dirname(t),{recursive:!0}),It.renameSync(s,t),t}function Fy({repoDir:n=ir,override:e,env:t=process.env,copy:s=pf}={}){if(e)return e;if(!ff(n)&&It.existsSync(Ke.join(n,".git"))&&It.existsSync(Ke.join(n,".claude-plugin","marketplace.json")))return n;if(hf(n)&&It.existsSync(Ke.join(n,".claude-plugin","marketplace.json")))try{return s({from:n,env:t})}catch{}return pc}function Py(n,e=Ne){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 I1(n){return new RegExp("(^|[^\\w-])"+cn+"([^\\w-]|$)","m").test(String(n||""))}function gc(n,e){let t=Rn(n,["plugin","list"],{env:e,timeoutMs:3e4}),s=Rn(n,["plugin","marketplace","list"],{env:e,timeoutMs:3e4}),o=Py(t.stdout,fs);return{...Py(t.stdout),legacyInstalled:o.installed,legacyEnabled:o.enabled,marketplaceAdded:s.code===0&&I1(s.stdout),listOk:t.code===0}}function mf({bin:n,env:e=process.env,state:t=null,onStep:s=()=>{}}){let o=t||gc(n,e);if(!o.legacyInstalled)return{ok:!0,migrated:!1,steps:[]};let r=[],i=c=>{s("claude "+c.join(" "));let u=Rn(n,c,{env:e});return r.push(u),u},a=c=>(c.stderr||c.stdout).trim().split(`
254
+ `).pop()||"exit "+c.code;if(!o.installed){i(["plugin","marketplace","update",cn]);let c=i(["plugin","install",Ne]);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",Ne])}let l=i(["plugin","uninstall",fs]);return l.code!==0?{ok:!1,migrated:!0,steps:r,error:"installed "+Ne+" but couldn't uninstall "+fs+": "+a(l)}:{ok:!0,migrated:!0,enabled:o.legacyEnabled!==!1,steps:r}}function gf({bin:n,source:e,env:t=process.env,onStep:s=()=>{}}){let o=[],r=gc(n,t);if(r.legacyInstalled){let l=mf({bin:n,env:t,state:r,onStep:s});if(!l.ok)return{ok:!1,steps:l.steps,state:r,error:l.error};let c=gf({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 "+Ne);let l=Rn(n,["plugin","enable",Ne],{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=Rn(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 "+Ne);let i=Rn(n,["plugin","install",Ne],{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 Uy(n){return!!(n&&n.disableAllHooks===!0)}var ir,pc,cn,E1,Ne,fs,Ri,yc=J(()=>{Rs();Tt();ir=Ke.resolve(Ke.dirname(_1(import.meta.url)),".."),pc=tt,cn=Dm,E1=Rm,Ne=E1+"@"+cn,fs=Pm+"@"+cn,Ri="2.1.287"});import M1 from"node:dns/promises";import L1 from"node:http";import D1 from"node:fs";import Hy from"node:path";import{spawn as R1}from"node:child_process";function P1(n){return{pretty:"http://"+At+":"+n+"/",plain:"http://127.0.0.1:"+n+"/"}}async function F1(n=At,e=t=>M1.lookup(t,{all:!0})){try{let t=await Promise.race([e(n),Gy(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(j1)}catch{return!1}}function U1(n,e,t){return new Promise(s=>{let o=L1.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 Pi({port:n,prefer:e="auto",token:t=null,resolve:s=F1,probe:o=U1}={}){let r=P1(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"}:[At,"pretty","name"].includes(i)?{url:r.pretty,pretty:!0,reason:"asked for "+At}:await s(At)?await o(n,At+":"+n,t)==="refused"?{url:r.plain,pretty:!1,reason:"the running daemon predates "+At+" support (restart it)"}:{url:r.pretty,pretty:!0,reason:At+" resolves to loopback"}:{url:r.plain,pretty:!1,reason:At+" doesn't resolve to loopback here"}}function H1(n,{platform:e=process.platform,spawnImpl:t=R1}={}){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 By(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 Ky({flags:n={},out:e=process.stdout,bin:t,env:s=process.env,openImpl:o=H1,startImpl:r}={}){let i=Z(),a=X(i),l=Number(n.port||a.port),c=be(i),u=m=>{n.quiet||e.write(m+`
255
+ `)},d=await By(l,c);if(!d.up&&!n["no-start"]){if(!D1.existsSync(Hy.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(()=>(Xs(),wc))).startDaemonDetached)({root:i,bin:t,env:s});for(let y=0;y<40&&!(d=await By(l,c)).up;y++)await Gy(250);if(!d.up)return u("The daemon didn't start; see "+Hy.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",p=await Pi({port:l,prefer:f,token:c});if(n.print||n["no-browser"])return e.write(p.url+`
256
+ `),0;let h=o(p.url);return u((h?"Opened ":"Open ")+p.url+(p.pretty?"":" ("+p.reason+")")),0}var Gy,j1,yf=J(()=>{ve();wf();Gy=n=>new Promise(e=>setTimeout(e,n));j1=n=>/^127\./.test(n)||n==="::1"||n==="0:0:0:0:0:0:0:1"});function Sc(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(`
257
+ `)}var Sf,$y,B1,Yy,Wy,kf=J(()=>{Sf={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."},$y={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."]},B1={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."]},Yy={short:{...Sf},informative:{...Sf,...$y},long:{...Sf,...$y,...B1}},Wy=[{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 zy={};Qt(zy,{RESERVED:()=>ji,checkUserName:()=>Pn,domainsOf:()=>$1,isReserved:()=>vf,passthroughOf:()=>G1,userFromHost:()=>K1,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 vf(n){return ji.includes(String(n||"").toLowerCase())}function G1(n){return String(n&&n.PASSTHROUGH||"").toLowerCase().split(/[\s,]+/).filter(Boolean)}function Pn(n){let e=String(n||"").trim().toLowerCase();return bf(e)?vf(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 K1(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)||vf(r)||t.includes(r)?null:r}return null}function $1(n){let e=[String(n.DOMAIN||"leftoff.dev").toLowerCase()];return n.DEV==="1"&&e.push("localhost"),e}var ji,Fi=J(()=>{ji=["www","app","api","docs","feed","relay","admin","status","mail"]});var Qy={};Qt(Qy,{BAD_NAME_MESSAGE:()=>bc,POLL_MS:()=>Jy,SIGNUP_TTL_MS:()=>qy,checkName:()=>Hi,relayGet:()=>Vy,relayPost:()=>Ui,reservedMessage:()=>kc,signupProblem:()=>Xy,signupStatus:()=>xc,startSignup:()=>Bi,validEmail:()=>vc,waitForConfirm:()=>xf});import Y1 from"node:http";import W1 from"node:https";function vc(n){let e=String(n||"").trim();return e.length<=254&&/^[^\s@<>()",;:]+@[^\s@<>()",;:]+\.[a-z]{2,}$/i.test(e)}function Ui(n,e,t,s=null){return Zy(n,"POST",e,t,s)}function Vy(n,e){return Zy(n,"GET",e)}function Zy(n,e,t,s,o=null){let r=n.origin?new URL(n.origin):new URL("https://"+ar(n)),i=e==="GET"?null:Buffer.from(JSON.stringify(s)),a=r.protocol==="https:"?W1:Y1;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:ar(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:ar(n)}:{}},d=>{let f="";d.on("data",p=>f+=p),d.on("end",()=>{let p={};try{p=JSON.parse(f)}catch{}l({status:d.statusCode,json:p,text:f})})});u.on("timeout",()=>u.destroy(new Error("timed out"))),u.on("error",c),u.end(i||void 0)})}function Xy(n,e){let t=n&&n.json||{};return n.status===404&&!t.code?ar(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 Hi({s:n,name:e,sign:t}){let s=Pn(e);if(!s.ok)return{ok:!1,code:s.code,message:s.code==="reserved"?kc(String(e).trim().toLowerCase()):bc};let o={...n,user:s.user},r=ar(o),i;try{i=await Vy(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:kc(s.user)};if(a.status==="invalid")return{ok:!1,code:"bad_name",message:bc};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 Bi({s:n,name:e,email:t,machine:s,sign:o}){let r=Pn(e);if(!r.ok)return{ok:!1,status:400,code:r.code,message:r.code==="reserved"?kc(e):bc};if(!vc(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 Ui(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 "+ar(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:Xy(a,i),retryAfter:a.json.retryAfter||null}}async function xc({s:n,user:e,poll:t}){try{let s=await Ui({...n,user:e},"/_signup/status",{poll:t});return s.json.status||(s.status===404?"unknown":"error")}catch{return"error"}}async function xf({s:n,user:e,poll:t,intervalMs:s=Jy,timeoutMs:o=qy+6e4,stop:r=()=>!1,onTick:i=()=>{}}){let a=Date.now();for(;;){if(r())return"stopped";let l=await xc({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 ar,Jy,qy,kc,bc,Ac=J(()=>{Fi();ar=n=>n.user+"."+n.domain,Jy=2e3,qy=1800*1e3;kc=n=>"“"+n+"” is reserved (www, app, api, docs, feed, relay, admin, status, mail). Pick another.",bc="A name is 2–32 lowercase letters, digits or hyphens."});function Gi(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 Ki(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 Tf(n){return tw.getRandomValues(new Uint8Array(n))}function sw(n=16){return Gi(Tf(n))}function ow(){let n=Tf(16),e="";for(let t=0;t<16;t++)e+=nw[n[t]&31];return e.match(/.{4}/g).join("-")}function rw(n){let e=String(n||"").toUpperCase().replace(/[\s-]/g,"").replace(/O/g,"0").replace(/[IL]/g,"1");return e.length!==16||[...e].some(t=>!nw.includes(t))?null:e.match(/.{4}/g).join("-")}async function iw(n){return Mt.importKey("raw",hs.encode(rw(n)||""),{name:"HMAC",hash:"SHA-256"},!1,["sign","verify"])}async function aw(n,e){let t=await iw(n);return Gi(await Mt.sign("HMAC",t,hs.encode("leftoff-pair-v1|"+e.join("|"))))}async function lw(n,e,t){let s=await iw(n);try{return await Mt.verify("HMAC",s,Ki(t),hs.encode("leftoff-pair-v1|"+e.join("|")))}catch{return!1}}async function cw({extractable:n=!1}={}){let e=await Mt.generateKey({name:"ECDH",namedCurve:"P-256"},n,["deriveBits"]),t=Gi(await Mt.exportKey("raw",e.publicKey));return{privateKey:e.privateKey,pub:t}}async function uw({privateKey:n,peerPub:e,code:t,device:s,extractable:o=!1}){let r=await Mt.importKey("raw",Ki(e),{name:"ECDH",namedCurve:"P-256"},!1,[]),i=await Mt.deriveBits({name:"ECDH",public:r},n,256),a=await Mt.importKey("raw",i,"HKDF",!1,["deriveKey","deriveBits"]),l={name:"HKDF",hash:"SHA-256",salt:hs.encode(rw(t)),info:hs.encode("leftoff-relay-device-v1|"+s)};if(o){let c=new Uint8Array(await Mt.deriveBits(l,a,256));return{raw:c,key:await _c(c)}}return{key:await Mt.deriveKey(l,a,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}}function _c(n){return Mt.importKey("raw",n instanceof Uint8Array?n:Ki(n),{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}async function dw(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 un("bad_envelope");if(n.to!==t)throw new un("wrong_target","sealed for "+n.to);if(Math.abs(o-n.ts)>ew)throw new un("expired","the request is too old (or a clock is off)");let r=await e(n.d);if(!r)throw new un("unknown_device","this browser isn’t paired with this Mac, or it was revoked");if(s&&s.has(n.d+"|"+n.id))throw new un("replayed");let i;try{i=JSON.parse(z1.decode(await Mt.decrypt({name:"AES-GCM",iv:Ki(n.iv),additionalData:J1(n)},r,Ki(n.ct))))}catch{throw new un("decrypt_failed")}return s&&s.add(n.d+"|"+n.id,n.ts+ew),i}async function fw(n,e,t){let s=Tf(12),o=await Mt.encrypt({name:"AES-GCM",iv:s,additionalData:q1(null,e)},n,hs.encode(JSON.stringify(t)));return{v:1,re:e.id,iv:Gi(s),ct:Gi(o)}}var tw,Mt,hs,z1,nw,Af,ew,J1,q1,un,Tc,hw=J(()=>{tw=globalThis.crypto,Mt=tw.subtle,hs=new TextEncoder,z1=new TextDecoder,nw="0123456789ABCDEFGHJKMNPQRSTVWXYZ",Af=600*1e3,ew=120*1e3;J1=n=>hs.encode(["leftoff-tunnel-v1","req",n.d,n.to,n.id,n.ts].join("|")),q1=(n,e)=>hs.encode(["leftoff-tunnel-v1","res",e.d,e.to,e.id].join("|")),un=class extends Error{constructor(e,t){super(t||e),this.code=e}};Tc=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 yw={};Qt(yw,{DEFAULT_DOMAIN:()=>jn,RESERVED:()=>ji,RelayHost:()=>Wi,boardUrlFor:()=>Yi,connectTarget:()=>Ef,devicesDir:()=>Qs,hostFor:()=>Ht,localRelayFile:()=>Cc,problemText:()=>Nf,readRelaySettings:()=>Fn,sharedAccountFile:()=>Nc,unwrapKey:()=>Cf,wrapKey:()=>Of,writeRelaySettings:()=>ms});import ps from"node:fs";import Ue from"node:path";import V1 from"node:http";import rt from"node:crypto";import{EventEmitter as Z1}from"node:events";import{fileURLToPath as X1}from"node:url";function Cc(n=process.env){return Ue.join(Ue.dirname(en(n)),"relay.json")}function Nc(n){return Ue.join(n,"relay","account.json")}function Qs(n){return Ue.join(n,"relay","devices")}function eo(n){try{return JSON.parse(ps.readFileSync(n,"utf8"))}catch{return null}}function $i(n,e,t){ps.mkdirSync(Ue.dirname(n),{recursive:!0});let s=Ue.join(Ue.dirname(n),"."+Ue.basename(n)+"."+process.pid+".tmp");ps.writeFileSync(s,JSON.stringify(e,null,2)+`
258
+ `,t?{mode:t}:void 0),ps.renameSync(s,n)}function Fn(n,e=process.env){let t=eo(Cc(e))||{},s=eo(Nc(n))||{},o=String(e.LEFTOFF_RELAY_USER||t.user||s.user||"").toLowerCase()||null,r=String(e.LEFTOFF_RELAY_DOMAIN||t.domain||s.domain||jn).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 ms(n,e,t=process.env){let o={...eo(Cc(t))||{},...e};if($i(Cc(t),o,384),e.user){let r=eo(Nc(n))||{};(r.user!==e.user||e.domain&&r.domain!==e.domain)&&$i(Nc(n),{user:e.user,domain:e.domain||r.domain||jn,updatedAt:new Date().toISOString()})}return o}function Ht(n){return n.user+"."+n.domain}function Yi(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://"+Ht(n)}function Ef(n){if(n.origin){let e=new URL(n.origin);return{url:(e.protocol==="https:"?"wss://":"ws://")+e.host+"/_connect",hostHeader:Ht(n)+(e.port?":"+e.port:"")}}return{url:"wss://"+Ht(n)+"/_connect",hostHeader:null}}function Of(n,{device:e,machine:t,box:s}){let o=rt.generateKeyPairSync("x25519"),r=rt.createPublicKey({key:{kty:"OKP",crv:"X25519",x:s},format:"jwk"}),i=rt.diffieHellman({privateKey:o.privateKey,publicKey:r}),a=rt.randomBytes(16),l=Buffer.from(rt.hkdfSync("sha256",i,a,"leftoff-relay-wrap-v1|"+t+"|"+e,32)),c=rt.randomBytes(12),u=rt.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:Ec(a),iv:Ec(c),ct:Ec(d)}}function Cf(n,{device:e,machine:t,boxPriv:s}){let o=rt.createPublicKey({key:{kty:"OKP",crv:"X25519",x:n.epk},format:"jwk"}),r=rt.diffieHellman({privateKey:s,publicKey:o}),i=Buffer.from(rt.hkdfSync("sha256",r,Oc(n.salt),"leftoff-relay-wrap-v1|"+t+"|"+e,32)),a=Oc(n.ct),l=rt.createDecipheriv("aes-256-gcm",i,Oc(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 _f(n,e){let{sig:t,...s}=n;return s.by=e.machine,{...s,sig:Ec(rt.sign(null,Buffer.from(Ct(s)),e.signPriv))}}function ex(n,e){try{let{sig:t,...s}=n;return rt.verify(null,Buffer.from(Ct(s)),rt.createPublicKey({key:{kty:"OKP",crv:"Ed25519",x:e},format:"jwk"}),Oc(t))}catch{return!1}}function gw(n){return String(n||"Browser").replace(/[\u0000-\u001f<>]/g,"").trim().slice(0,60)||"Browser"}function Nf(n,e={}){return n?n==="not_registered"?"this Mac isn’t on "+(e.user||"?")+"."+(e.domain||jn)+" 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||jn)+" (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 jn,pw,Q1,mw,Ec,Oc,Wi,Ic=J(()=>{Yd();ns();ve();Ni();Ac();Fi();hw();jn="leftoff.dev",pw=Ue.join(Ue.dirname(X1(import.meta.url)),"..","ui"),Q1={".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"},mw=4*1024*1024,Ec=n=>Buffer.from(n).toString("base64url"),Oc=n=>Buffer.from(String(n||""),"base64url");Wi=class extends Z1{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 Tc,this.innerSecret=rt.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=Cn(this.machine))}settings(){return Fn(this.root,this.env)}active(){let e=this.settings();return!!(e.enabled&&e.user)}url(){return this.active()?Ef(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?Yi(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=Ef(e),s=new Vo({url:t.url,hostHeader:t.hostHeader,id:this.id,user:e.user,log:this.log,helloExtra:()=>({version:V,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 "+Ht(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=Pn(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 ms(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 ms(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 Hi({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 Bi({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 xc({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();ms(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?Ht(e):null,boardUrl:e.user?Yi(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?Nf(t.lastError,e):null,relayMachines:t&&t.relayMachines?t.relayMachines:null,devices:this.devices().length,reserved:ji,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=Ue.join(pw,Ue.normalize(t).replace(/^(\.\.[/\\])+/,""));return!s.startsWith(pw+Ue.sep)||/(^|[/\\])\./.test(t)||!ps.existsSync(s)||ps.statSync(s).isDirectory()?{t:"static.res",id:e.id,status:404}:{t:"static.res",id:e.id,status:200,type:Q1[Ue.extname(s)]||"application/octet-stream",b64:ps.readFileSync(s).toString("base64")}}async tunnel(e){let t;try{t=await dw(e,{keyFor:i=>this.keyFor(i),machine:this.machine,seen:this.seen})}catch(i){return{error:{code:i instanceof un?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 fw(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=V1.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<=mw&&c.push(d)}),l.on("end",()=>o({s:l.statusCode,h:{"content-type":l.headers["content-type"]||"application/json"},b:u>mw?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?" ("+Nf(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=ow();while(this.pairs.has(t.slice(0,4)));let s=t.slice(0,4),o=e+Af,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(Af/1e3),qrSvg:uc(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 lw(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=sw(12),r=await cw(),i;try{i=(await uw({privateKey:r.privateKey,peerPub:e.pub,code:s.code,device:o,extractable:!0})).raw}catch{return{ok:!1,code:"bad_key"}}let a=await aw(s.code,["mac",e.pair,e.pub,r.pub,o]);return this.saveDevice({id:o,name:gw(e.name),createdAt:new Date(t).toISOString(),pairedOn:this.machine},i),this.keys.set(o,{key:await _c(i),at:Date.now()}),this.log("relay: paired a new browser, "+gw(e.name)+" ("+o.slice(0,6)+"…)"),this.changed(),{ok:!0,device:o,pub:r.pub,mac:a}}trustedCards(){let e=Js(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}On(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]=Of(t,{device:e.id,machine:o,box:r.box}));$i(Ue.join(Qs(this.root),e.id+".json"),_f({v:1,...e,wraps:s},this.id))}deviceFiles(){let e=[];try{e=ps.readdirSync(Qs(this.root)).filter(t=>/^[\w-]+\.json$/.test(t))}catch{return[]}return e.map(t=>eo(Ue.join(Qs(this.root),t))).filter(t=>t&&t.id)}verified(e){let s=this.trustedCards()[e.by];return!!(s&&ex(e,s.sign))}deviceRecord(e){if(!/^[\w-]{8,64}$/.test(String(e||"")))return null;let t=eo(Ue.join(Qs(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 _c(Cf(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=Ue.join(Qs(this.root),e+".json"),s=eo(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;$i(t,_f({...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=Js(this.root);for(let[o,r]of Object.entries(s))if(!(o===this.machine||t.has(o)||!r.valid||r.card.anywhere!==e.user)&&On(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=Cf(t.wraps[this.machine],{device:t.id,machine:this.machine,boxPriv:this.id.boxPriv}),r={...t.wraps};for(let i of s)r[i]=Of(o,{device:t.id,machine:i,box:e[i].box});$i(Ue.join(Qs(this.root),t.id+".json"),_f({...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 wc={};Qt(wc,{SUMMARIZER_FLAG:()=>Mf,daemonHealth:()=>cr,flagBool:()=>at,ghSetupStep:()=>Tw,globalBinPath:()=>Lf,lengthFlow:()=>xw,linkFolder:()=>Pc,localModelFlow:()=>jc,mergeSetupConfig:()=>Dc,ollamaModelFlow:()=>vw,patchDaemonConfig:()=>Mc,relaySignupStep:()=>Aw,runSetup:()=>Rf,showTestResult:()=>so,startDaemonDetached:()=>Lc,testFlow:()=>ur,writeConfigFile:()=>Rc});import Xe from"node:fs";import no from"node:os";import De from"node:path";import{spawn as lr,spawnSync as to}from"node:child_process";function at(n,e){if(n["no-"+e])return!1;if(n[e]!==void 0)return nx(n[e])?!1:tx(n[e])||n[e]===""?!0:!!n[e]}function sx(n,e){let t=e;return{c:t,line:(s="")=>n.write(s+`
256
259
  `),ok:s=>n.write(t.green("✔")+" "+s+`
257
260
  `),warn:s=>n.write(t.yellow("!")+" "+s+`
258
261
  `),fail:s=>n.write(t.red("✖")+" "+s+`
@@ -261,115 +264,115 @@ Settings → Sync on the board, or \`leftoff move --delete-backup\`. Leftoff nev
261
264
  `),step:(s,o,r)=>n.write(`
262
265
  `+t.bold(t.magenta(`[${s}/${o}]`))+" "+t.bold(r)+`
263
266
  `),cmd:s=>n.write(" "+t.cyan("$ "+s)+`
264
- `)}}function cf(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))+"╮")+`
267
+ `)}}function Df(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))+"╮")+`
265
268
  `);for(let i of s)n.write(e.dim("│")+" "+i+" ".repeat(Math.max(0,r-o(i).length-2))+e.dim("│")+`
266
269
  `);n.write(e.dim("╰"+"─".repeat(r-1)+"╯")+`
267
- `)}function Dy(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 Vs(n,e,t,s){if(!t.ok){n.write(e.red("✖")+" Test checkpoint failed with "+s+": "+(t.error||"no answer")+`
268
- `);return}let o=[];for(let[a,l]of[["Doing",t.doing],["Stopped at",t.stoppedAt],["Next",t.next]]){let c=Dy(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":"";cf(n,e,"Sample checkpoint · "+s+" · "+Ai(t.ms)+i,o),t.parseOk||n.write(e.yellow("!")+` The model didn't follow the 3-line format; a bigger model usually does.
269
- `)}async function Qo(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 mc(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 gc({root:n,bin:e=Fe,env:t=process.env}){let s=we(n);Ve.mkdirSync(s.root,{recursive:!0});let o=Ve.openSync(Ne.join(n,".daemon.log"),"a"),r=Xo(process.execPath,[e,"serve"],{detached:!0,stdio:["ignore",o,o],env:{...t,CHECKPOINTS_DIR:n}});return r.unref(),Ve.closeSync(o),Ve.writeFileSync(Ne.join(n,".daemon.pid"),String(r.pid)+`
270
- `),r.pid}function h1(n){let e=process.platform==="darwin"?["open",[n]]:process.platform==="win32"?["cmd",["/c","start","",n]]:["xdg-open",[n]];try{let t=Xo(e[0],e[1],{detached:!0,stdio:"ignore"});return t.on("error",()=>{}),t.unref(),!0}catch{return!1}}function yc(n,{summarizer:e,machine:t,hostname:s=qs.hostname(),port:o,summaryLength:r}={}){let i=qi({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=we(n),s=t.config+".tmp-"+process.pid;return Ve.writeFileSync(s,JSON.stringify(e,null,2)+`
271
- `),Ve.renameSync(s,t.config),t.config}function Sc(n){let e=na(),t=yh(n);if(Ne.resolve(n)===e)return yi("CHECKPOINTS_DIR",null),{how:"default",pointer:t};let s=null;try{s=Ve.lstatSync(e)}catch{}if(s&&s.isSymbolicLink()){if(Ne.resolve(Ne.dirname(e),Ve.readlinkSync(e))===Ne.resolve(n))return yi("CHECKPOINTS_DIR",null),{how:"symlink",pointer:t,already:!0};Ve.unlinkSync(e),s=null}return s?(yi("CHECKPOINTS_DIR",n),{how:"settings-env",pointer:t}):(Ve.symlinkSync(n,e,"dir"),yi("CHECKPOINTS_DIR",null),{how:"symlink",pointer:t})}function lf(n=process.env){let e=Js(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"?Ne.join(t,"node_modules"):Ne.join(t,"lib","node_modules");for(let o of zr)for(let r of["leftoff.js","checkpoints.js"]){let i=Ne.join(s,o,"bin",r);if(Ve.existsSync(i))return i}return null}function p1(n,e,t,s){t.close(),s.write(`
272
- `);let o=Js(n,e,{stdio:"inherit"});return s.write(`
273
- `),o.status===0}async function kc(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)||lt).replace(/\/$/,""),c=(o["lmstudio-url"]||(o["base-url"]&&n.wantProvider==="openai-compatible"?o["base-url"]:null)||Qt).replace(/\/$/,""),u=tt("Looking for local model servers…",{out:s,colors:a}),[d,f]=await Promise.all([jt({baseUrl:l,fetchImpl:r,env:i}),n.wantProvider==="ollama"?{running:!1,models:[]}:kn({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 ("+st(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 p;if(n.wantProvider==="openai-compatible"?p="openai-compatible":n.wantProvider==="ollama"?p="ollama":d.running&&f.running?p=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?p="openai-compatible":p="ollama",p==="openai-compatible")return My(n,f,c);if(!d.running){if(d.binary){let h=Fr(process.platform,d.binary);if(h&&await e.confirm("Start Ollama now? ("+h.shell+")",!0)){if(h.detached){let y=Xo(h.cmd[0],h.cmd.slice(1),{detached:!0,stdio:"ignore"});y.on("error",()=>{}),y.unref()}else Js(h.cmd[0],h.cmd.slice(1),{stdio:"ignore"});let m=tt("Waiting for Ollama to come up…",{out:s,colors:a});d=await wo({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&&!ot(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 h=ip(process.platform,i),m=await e.select("Install Ollama?",[...h.map(S=>({label:S.label,value:S.id,hint:S.shell+(S.note?" · "+S.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 My(n,f,c);let y=h.find(S=>S.id===m);if(y.cmd){if(t.line(" This runs:"),t.cmd(y.shell),!await e.confirm("Run it now?",!0))return null;if(!p1(y.cmd[0],y.cmd.slice(1),e,s))return t.fail("The Ollama installer exited with an error (see above)."),null;if(t.ok("Ollama installed"),y.id==="brew"){let S=Fr(process.platform,jr(i));if(S){let k=Xo(S.cmd[0],S.cmd.slice(1),{detached:S.detached,stdio:"ignore"});k.on("error",()=>{}),k.unref()}}}else{if(t.line(" Install it with:"),t.cmd(y.shell),y.url&&t.note("or download it from "+y.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 w=tt("Waiting for Ollama…",{out:s,colors:a});if(d=await wo({baseUrl:l,fetchImpl:r,timeoutMs:3e4}),!d.running&&d.binary){let S=Fr(process.platform,d.binary);if(S){let k=Xo(S.cmd[0],S.cmd.slice(1),{detached:!0,stdio:"ignore"});k.on("error",()=>{}),k.unref(),d=await wo({baseUrl:l,fetchImpl:r,timeoutMs:2e4})}}d.running?w.succeed("Ollama is up at "+d.baseUrl):w.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 Ry(n,d)}async function Ry(n,e){let{p:t,u:s,out:o,flags:r,fetchImpl:i}=n,a=s.c,l=go(e.models,{current:n.currentModel}),c=r.model?ka(r.model):rp(l);s.line(),s.line(" "+a.bold("Models for 3-line checkpoints")+a.dim(" (sizes from ollama.com/library, "+mo+")"));let u=l.map(h=>({label:h.tag.padEnd(13)+" "+a.dim((h.curated?xa(h,Dt):(h.sizeBytes?Dt(h.sizeBytes)+" · ":"")+"on this machine").padEnd(30))+" "+(h.recommended?a.yellow("★ recommended")+" ":"")+(h.installed?a.green("✔ pulled"):""),detail:h.curated?h.note:null,value:h.tag}));u.push({label:"Other tag…",value:"__other__",hint:"any ollama.com/library tag, e.g. phi4-mini"});let d=u.findIndex(h=>Ge(h.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:h=>/^[\w.\-/]+(:[\w.\-]+)?$/.test(h)?null:"Use a tag like llama3.2:3b"})));let p=l.find(h=>Ge(h.tag,f));if(!p||!p.installed){let h=p&&p.sizeBytes?" ("+Dt(p.sizeBytes)+")":"";if(ot(r,"pull")===!1)throw new Error(f+" isn't pulled and --no-pull was given");if(!await t.confirm("Download "+f+h+" now?",!0))return null;let m=Vl("pull "+f,{out:o,colors:a}),y=Date.now();o.write(a.dim(" pulling "+f+" from ollama.com · Ctrl-C to stop (a re-run resumes)")+`
274
- `);try{await t.interruptible(w=>So({tag:f,baseUrl:e.baseUrl,fetchImpl:i,signal:w,onProgress:S=>{let k=(Date.now()-y)/1e3,v=k>.5&&S.completed?Dt(S.completed/k)+"/s":"",T=S.total?Dt(S.completed)+" / "+Dt(S.total)+(v?" · "+v:""):S.status;m.update(S.fraction,T)}}))}catch(w){if(w.cancelled||(m.fail(w.message),n.yes))throw w;return null}m.done("Pulled "+f+a.dim(" in "+Ai(Date.now()-y)))}else s.ok(f+" is already pulled");return{provider:"ollama",model:f,baseUrl:e.baseUrl===lt?null:e.baseUrl}}async function My(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 kn({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 kn({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===Qt?null:e.baseUrl}}async function er(n,e,t){let{p:s,u:o,out:r,fetchImpl:i}=n,a=tt("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 Os(e,{fetchImpl:i});return l.ok?a.succeed("It works."):a.fail("The test didn't work."),Vs(r,o.c,l,t),l}async function Ly(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:S=>/^[A-Za-z_][A-Za-z0-9_]*$/.test(S)?null:"That isn't an env var name"});e.apiKeyEnv=f;let p=".secret/"+s,h=!!c[f],m=Sn(e.provider,{apiKeyEnv:"__none__",apiKeyFile:d&&u.apiKeyFile?u.apiKeyFile:p,root:n.root},{});h?a.ok("$"+f+" is set in this shell"):a.warn("$"+f+" isn't set in this shell");let y=h?c[f]:null,w=S=>{e.apiKeyFile=p,(n.pendingSecrets=n.pendingSecrets||[]).push({name:s,value:S})};if(!y&&m?(a.ok("A saved key is already in "+p),e.apiKeyFile=d&&u.apiKeyFile||p,y=m):!y&&!n.yes&&(y=await i.ask("Paste the key (hidden; Enter to skip)","",{secret:!0}),y&&w(y)),y&&h&&!n.yes&&await i.confirm("Also save it to "+p+" (mode 0600) so the background service can use it?",!0)&&w(y),y||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."),y&&ot(l,"test")!==!1&&(n.yes||await i.confirm(o,!0))){process.env.__CHECKPOINTS_TEST_KEY=y;try{await er(n,{...e,apiKeyFile:void 0,apiKeyEnv:"__CHECKPOINTS_TEST_KEY"},r)}finally{delete process.env.__CHECKPOINTS_TEST_KEY}}return y}async function m1(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||Cs),c=n.yes?null:tt("Fetching the cloud model list from "+l.replace(/^https?:\/\//,"")+"…",{out:n.out,colors:a}),u=await Pr({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=va(u.models,{current:e}),f=d.find(w=>w.current&&w.available!==!1)||d.find(w=>w.recommended&&w.available!==!1)||d.find(w=>w.available!==!1)||d[0];if(n.yes)return f.tag;let p=d.filter(w=>w.curated||w.current),h=p.map(w=>({label:w.tag+(w.recommended?" ★":""),value:w.tag,hint:(w.note||"")+(w.available===!1?" · not in today's list":"")})),m=d.filter(w=>!p.includes(w)).map(w=>w.tag);h.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 y=await t.select("Which cloud model?",h,Math.max(0,h.findIndex(w=>w.value===f.tag)));return y!=="__other"?y:t.ask("Model name (as ollama.com/api/tags lists it)",e||f.tag,{validate:w=>/^[\w.:/-]+$/.test(w)?null:"That doesn't look like a model tag"})}async function g1(n){let{p:e,u:t,flags:s,env:o}=n,r=t.c,i=n.currentSummarizer||{},a=af[String(i.provider||"auto").toLowerCase()]||"claude",l=s.summarizer?af[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(h=>h.value===d)));if(f==="claude"){let h={provider:"claude",model:"haiku"};return n.claude.found&&(ot(s,"test")===!0||!n.yes&&await e.confirm("Run a test checkpoint with `claude -p` now? (~10 s, uses your Claude login)",!1))?await er(n,h,"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.'),h}if(f==="heuristic")return{provider:"heuristic",model:null};if(f==="anthropic"){let h={provider:"anthropic",model:s.model||"haiku"};return await Ly(n,h,{defaultEnv:"ANTHROPIC_API_KEY",secretName:"anthropic-api-key",testPrompt:"Test it now? (one Haiku call, well under a cent)",testLabel:"Claude Haiku (API)"}),h}if(f==="ollama-cloud"){let h=await m1(n,i.provider==="ollama-cloud"?i.model:null),m={provider:"ollama-cloud",model:h,...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 Ly(n,m,{defaultEnv:"OLLAMA_API_KEY",secretName:"ollama-api-key",testPrompt:"Test it now? (one call to "+h+" on ollama.com)",testLabel:h+" (Ollama cloud)"}),m}let p=await kc({...n,wantProvider:l==="ollama"||l==="openai-compatible"?l:null});if(!p){if(n.yes)throw new Error("local model setup didn't finish");continue}if(ot(s,"test")!==!1){let h=await er(n,p,p.model);if(!h.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(!h.ok&&n.yes)throw new Error("test checkpoint failed: "+h.error)}return p}}async function Py(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=dn(i,{strict:!0});if(!u)throw new Error("unknown --summary-length="+i+" (short, informative, long)");return s.ok("Checkpoint length: "+u),u}let a=dn(e.summary&&e.summary.length),l=ry.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(oy[u]).split(`
275
- `).flatMap(f=>Dy(f,70));if(cf(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 uf({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=is(rs(t)),a=f1(t,i),l=i,c=new ft({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(`
276
- `),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"+q)),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 h=Vg(s);u.claude=h,h.found?h.modsSupported?a.ok("Claude Code "+(h.version||"?")+l.dim(" · "+st(h.bin))):a.warn("Claude Code "+(h.version||"(version unknown)")+" · mods need "+Ti+"+ (update with `claude update`); settings hooks work on any version"):a.warn("Claude Code's `claude` command isn't on your PATH");let m=Fe;if(Hd(Vo)){let A=lf(s);if(A)m=A,a.ok("leftoff is installed globally"+l.dim(" · "+st(A)));else{a.warn("Running from a temporary npx copy.");let M=ot(n,"global"),R=Bd(Vo)?Qe:"github:"+Zl;if(M!==!1&&(M===!0||!r&&await c.confirm("Install the `leftoff` command globally (npm install -g "+R+")?",!0))){let H=tt("npm install -g "+R,{out:t,colors:l}),Q=Js(process.platform==="win32"?"npm.cmd":"npm",["install","-g",R],{encoding:"utf8",env:s}),he=Q.status===0?lf(s):null;he?(m=he,H.succeed("Installed `leftoff` globally")):H.fail("Global install failed: "+((Q.stderr||"").trim().split(`
277
- `).pop()||"exit "+Q.status)+" (continuing from the npx copy)")}m===Fe&&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:A,lsRemote:M,writeStamp:R,DEFAULT_SOURCE:H}=await Promise.resolve().then(()=>(Vr(),im)),Q=await A({env:s});if(Q.kind!=="clone"&&Q.kind!=="npm"&&!Q.sha){let he=await M(H,{env:s,timeoutMs:8e3});he.ok&&R({sha:he.sha,source:H})}}catch{}let y=V(),w=Ve.existsSync(we(y).config);w&&a.info("Found an existing setup in "+st(y)+l.dim(" · your answers from last time are the defaults")),w||a.note("Tip: a synced folder (iCloud Drive, Dropbox, Syncthing) puts every machine on one board.");let S=await c.ask("Where should checkpoints live?",st(rf(n.dir||y)),{validate:A=>A&&Ne.isAbsolute(rf(A))?null:"Use an absolute path or ~/…"}),k=Ne.resolve(rf(S));u.root=k;let v=fn(k)||(w&&k===y?fn(y):null)||{},T=X(k),b=qs.hostname(),g=await c.ask("Name for this machine",n.machine||le(T),{hint:"(shown on each card)",validate:A=>/^[\w .-]{1,40}$/.test(A)?null:"Letters, numbers, spaces, dots and dashes; up to 40"});if(Ne.resolve(k)!==Ne.resolve(y)&&On(y)&&!n["no-move"]){let{planMove:A,executeMove:M,tilde:R}=await Promise.resolve().then(()=>($s(),Yg)),{moveHooks:H}=await Promise.resolve().then(()=>(ji(),bc)),{loadIdentity:Q}=await Promise.resolve().then(()=>(Zn(),_d)),he=null;try{he=Q(g)}catch{}let pe=A(y,k,{machine:g,identity:he});if(!pe.ok){for(let or of[...pe.errors,...pe.collisions])a.warn(or.message);throw new Error("Can’t "+(pe.merge?"join ":"move to ")+R(k)+" yet (see above). Nothing changed.")}if(a.info((pe.merge?"That folder already has Leftoff data"+(pe.warnings.length?" ("+pe.warnings.map(or=>or.message).join(" ")+")":"")+". ":"")+pe.counts.total+" files in "+R(y)+"."),await c.confirm((pe.merge?"Merge":"Move")+" your sessions into "+R(k)+"? (copied and checked; the old folder is kept as "+R(pe.backup)+")",!0)){let or=X(y),rr=await M(pe,{hooks:H({from:y,cfg:or,token:Se(y),noRestart:!0,env:s}),machine:g,say:e0=>a.line(" "+l.dim("· "+e0))});if(!rr.ok)throw new Error("The move stopped ("+rr.error+"); put back, still using "+R(y)+".");a.ok((pe.merge?"Merged":"Moved")+": "+rr.copied+" copied, "+rr.verified+" verified. Backup: "+R(rr.backup))}}a.step(2,f,"Claude Code mod");let _=Ul();Xg(_)&&a.warn('"disableAllHooks": true is set in your Claude settings, so neither mods nor settings hooks run until you remove it.');let x=n.capture?String(n.capture):ot(n,"hooks")===!0?"hooks":ot(n,"mod")===!1?"none":null,C=[];h.found&&C.push({label:"Install the mod",value:"mod",hint:h.modsSupported?"recommended · /cp, /cp-name and the /leftoff pane":"needs Claude Code "+Ti+"+"}),C.push({label:"Settings hooks only",value:"hooks",hint:"works on any version; no slash commands or pane"+(h.found?"":" · start working once Claude Code is installed")}),C.push({label:"Skip for now",value:"none"});let E=x||(h.found&&h.modsSupported?"mod":"hooks");h.found||(a.line(" Install Claude Code first ("+l.underline("https://code.claude.com/docs")+"), then re-run setup to add the mod."),E==="mod"&&(E="hooks")),E=await c.select("How should Leftoff see your sessions?",C,Math.max(0,C.findIndex(A=>A.value===E)));let D=!1;if(E==="mod"){let A=Zg({override:n.marketplace}),M="",R=tt("Checking installed plugins…",{out:t,colors:l}),H=$d({bin:h.bin,source:A,env:s,onStep:Q=>{M=Q,R.update("Running "+l.cyan(Q))}});if(H.ok&&H.migrated)R.succeed("Mod moved to its new name: "+Ce+" (was "+as+")");else if(H.ok&&H.already&&H.state&&!H.state.enabled)R.succeed("Mod was installed but disabled; enabled it ("+Ce+")");else if(H.ok&&H.already)R.succeed("Mod already installed and enabled ("+Ce+")");else if(H.ok){R.succeed("Mod installed: "+Ce);for(let Q of H.steps)a.cmd("claude "+Q.args.map(he=>st(he)).join(" "))}else R.fail("`"+M+"` failed: "+String(H.error).split(`
278
- `).slice(-2).join(" "));D=H.ok,H.ok?h.modsSupported||a.warn("The mod is installed but loads only on Claude Code "+Ti+"+."):(/github|clone|auth|permission|denied|not found/i.test(String(H.error))&&A===Zl&&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."),E=await c.confirm("Use settings hooks instead? (same tracking, no slash commands)",!0)?"hooks":"none"),D&&gi()&&await c.confirm("Remove the older settings hooks? The mod covers them.",!0)&&(Hl(),a.ok("Removed the settings hooks"))}if(E==="hooks"){let A=gi(),M=Ko(void 0,{bin:m});a.ok((A?"Settings hooks refreshed in ":"Settings hooks added to ")+st(M)+l.dim(" (backup saved next to it)"))}E==="none"&&a.info("Skipped. Add it later with `leftoff setup`."),a.step(3,f,"Summarizer"),u.currentSummarizer=T.summarizer,u.currentModel=T.summarizer&&T.summarizer.model;let O=await g1(u),F=await Py(u,T);a.step(4,f,"Save and start"),Te(k);for(let A of u.pendingSecrets||[])sa(A.name,A.value,k,s),a.ok("Key saved to "+st(Ne.join(qt(s),A.name))+l.dim(" (0600, on this Mac only)"));let $=yc(v,{summarizer:O,machine:g,hostname:b,port:n.port,summaryLength:F});wc(k,$),a.ok("Config saved: "+st(we(k).config)+l.dim(" · summarizer "+O.provider+(O.model?" / "+O.model:"")+" · "+F+" checkpoints · machine "+g));let N=Sc(k);N.how==="symlink"&&!N.already?a.ok("Linked ~/checkpoints → "+st(k)+l.dim(" (so the mod and hooks find it)")):N.how==="settings-env"&&a.ok("Set CHECKPOINTS_DIR in your Claude settings so the mod and hooks find "+st(k));let j=X(k),W=j.port,ue=Se(k),Y={...s,CHECKPOINTS_DIR:k},Z=await Qo(W,ue,o),te=ot(n,"service"),B=te!==void 0?te:!r&&process.platform!=="win32"?await c.confirm("Start Leftoff automatically when you log in?",!0):!1;if(B){let A=At(),M=Bl({root:k,bin:m,dryRun:!!n["service-dry-run"]});M&&a.ok("Moved the login service to its new name: "+st(M.to)+l.dim(" (stopped and removed "+st(M.from)+")")),Ve.mkdirSync(Ne.dirname(A),{recursive:!0});let R=En({root:k,bin:m}),H=!Ve.existsSync(A)||Ve.readFileSync(A,"utf8")!==R;Ve.writeFileSync(A,R),a.ok((H?"Login service written: ":"Login service up to date: ")+st(A));let Q=!0;if(!n["service-dry-run"])for(let he of Ks()){let pe=Js(he[0],he.slice(1),{encoding:"utf8"});if(pe.status!==0&&he[1]!=="unload"){Q=!1,a.warn("`"+he.join(" ")+"` failed: "+((pe.stderr||pe.error&&pe.error.message||"").trim().split(`
279
- `)[0]||"exit "+pe.status));break}}if(Q?a.ok("Service started"):a.note("Starting the daemon directly instead; the service file stays for next login."),Q)for(let he=0;he<20&&!(Z=await Qo(W,ue,o)).up;he++)await Ny(250)}if(ot(n,"start")===!1)a.info("Not starting the daemon (--no-start). Start it with `leftoff serve`.");else if(Z.up&&Z.auth){try{await mc(W,ue,{summarizer:$.summarizer,machineNames:$.machineNames||{},summary:{length:F}},o),a.ok("Daemon already running on 127.0.0.1:"+W+l.dim(" · config updated live"))}catch(A){a.warn("Daemon is running but didn't take the new config ("+A.message+"); restart it to apply.")}Z.machine&&Z.machine!==g&&a.note("Restart the daemon to show the new machine name.")}else if(Z.up&&!Z.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 A=tt("Starting the daemon…",{out:t,colors:l});gc({root:k,bin:m,env:Y});for(let M=0;M<40&&!(Z=await Qo(W,ue,o)).up;M++)await Ny(250);Z.up?(A.succeed("Daemon running on 127.0.0.1:"+W),B||a.note("Runs in the background until reboot; `leftoff setup --service` starts it at login.")):A.fail("The daemon didn't start; see "+st(Ne.join(k,".daemon.log")))}if(a.step(5,f,"Check"),ot(n,"doctor")!==!1){let A=Js(process.execPath,[m,"doctor"],{encoding:"utf8",env:{...Y,NO_COLOR:"1"}});for(let M of(A.stdout||"").trim().split(`
280
- `).filter(Boolean).map(R=>R.split(qs.homedir()+Ne.sep).join("~"+Ne.sep))){let R=M.match(/^([✓✗·])\s(.*)$/);R?R[1]==="✓"?a.ok(R[2]):R[1]==="✗"?a.fail(R[2]):a.info(R[2]):a.line(" "+M)}}let Ct=await jy(u,{root:k,machine:g,port:W,token:ue,up:!!Z.up});await Fy({...u,input:e}).catch(A=>a.info("PR cards: couldn't check gh ("+A.message+")"));let $e=Z.up?(await _i({port:W,prefer:s.CHECKPOINTS_OPEN_HOST||j.openHost||"auto",token:ue})).url:"http://127.0.0.1:"+W+"/",zt=ot(n,"open");Z.up&&(zt===!0||zt===void 0&&!r&&t.isTTY&&await c.confirm("Open the board in your browser?",!0))&&(h1($e)?a.ok("Opened "+$e):a.info("Open "+$e)),a.line();let He=[l.bold("Board")+" "+l.cyan($e)];if(E==="mod"){let A=(M,R,H,Q)=>He.push(M+" ".repeat(15-M.replace(/\x1b\[[0-9;]*m/g,"").length)+l.cyan(R)+(H?" "+H:"")+" ".repeat(Math.max(1,21-R.length-(H?H.length+1:0)))+Q);A(l.bold("In any session"),"/cp","[note]","checkpoint now"),A("","/cp-name","<title>","name the session"),A("","/leftoff","","every open session"),He.push(l.dim("Already-open sessions pick the mod up when they restart."))}else E==="hooks"?(He.push(l.bold("Sessions")+" tracked through settings hooks;"),He.push(" checkpoints happen on their own after 15 min idle")):He.push(l.bold("Sessions")+" not hooked up yet: `leftoff setup`");return He.push(l.bold("Models")+" "+l.cyan("leftoff models")+" list, pull, switch"),He.push(l.bold("Digest")+" "+l.cyan("leftoff digest")+" where you left off"),He.push(l.bold("Reopen")+" "+l.cyan("leftoff open")+" the board, any time"),Ct&&Ct.host?He.push(l.bold("Anywhere")+" "+l.cyan("https://"+Ct.host)+l.dim(" (leftoff relay pair)")):He.push(l.bold("Anywhere")+" "+l.cyan("leftoff relay signup")+" yourname"+l.dim(" (your board on your phone)")),cf(t,l,"You're set",He),c.close(),r&&process.removeListener("SIGINT",d),{ok:!0,root:k,capture:E,summarizer:O,port:W,daemonUp:!!Z.up,anywhere:Ct}}catch(p){if(c.close(),r&&process.removeListener("SIGINT",d),p&&p.cancelled)return t.write(`
281
- `),a.fail("Setup cancelled ("+p.message.replace(/^cancelled \(?|\)$/g,"")+"). Re-run `leftoff setup` any time; finished steps are skipped."),{ok:!1,cancelled:!0};throw p}}async function jy(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:p,writeRelaySettings:h,hostFor:m,DEFAULT_DOMAIN:y}=await Promise.resolve().then(()=>(pc(),Iy)),{startSignup:w,waitForConfirm:S,validEmail:k,checkName:v}=await Promise.resolve().then(()=>(ac(),fy)),{checkUserName:T}=await Promise.resolve().then(()=>(Ci(),iy)),{loadIdentity:b}=await Promise.resolve().then(()=>(Zn(),_d)),g=p(e,c);if(g.user)return a.ok("Use from anywhere: "+m(g)+(g.enabled?"":f.dim(" (off on this Mac: leftoff relay enable "+g.user+")"))),{host:m(g),existing:!0};if(ot(l,"signup")===!1)return{skipped:!0};let _=String(l.domain||g.domain||y),x=typeof l.signup=="string"?l.signup.trim().toLowerCase():"",C=typeof l.email=="string"?l.email.trim():"";if(u&&!(x&&C))return{skipped:!0};let E=!1;a.line();let D=$=>$&&$.cancelled&&/input closed/.test($.message);try{if(!u&&!x&&!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 $=!!x;for(let N=0;;N++){x||(x=(await i.ask("Your name","",{hint:"(for yourname."+_+")",validate:ue=>{let Y=T(String(ue).toLowerCase());return Y.ok?null:Y.code==="reserved"?"That one’s reserved; pick another":"2–32 lowercase letters, digits or hyphens"}})).toLowerCase());let j=await v({s:{...g,user:x,domain:_},name:x,sign:b(t).signX});if(j.ok&&j.status==="yours"){E=!0;break}let W=j.ok?j.status==="available"?null:j.message:j.code==="legacy"?null:j.message;if(!W){j.ok&&j.claimed&&a.note(j.host+" was claimed on leftoff.dev. Use the email you claimed it with.");break}if(a.warn(W),$||u||N>=2||j.code==="unreachable"||j.code==="closed")return a.info("Try again any time: "+f.cyan("leftoff relay signup yourname")),{skipped:!0,error:W};x=""}!E&&!C&&(C=await i.ask("Email","",{hint:"(for the confirmation link)",validate:N=>k(N)?null:"That doesn’t look like an email address"}))}catch($){if(!D($))throw $;return a.line(),a.info("Skipped. Later: "+f.cyan("leftoff relay signup yourname")),{skipped:!0}}let O={...g,user:x,domain:_},F=E?{ok:!0,already:!0}:await w({s:O,name:x,email:C,machine:t,sign:b(t).signX});if(!F.ok)return a.warn(F.message),a.info("Try again any time: "+f.cyan("leftoff relay signup "+x)),{skipped:!0,error:F.message};if(F.already)a.ok("This Mac already has "+m(O)+". Pair your phone: "+f.cyan("leftoff relay pair"));else{a.ok("Sent a link to "+F.email+". Open it and press "+f.bold("Confirm "+m(O))+f.dim(" (30 minutes)")),F.claimed&&a.note("Your leftoff.dev claim for "+x+" carries over.");let $=tt("Waiting for you to confirm… "+f.dim("(Ctrl-C skips; finish later with leftoff relay enable "+x+")"),{out:n.out,colors:f}),N=!1,j=()=>{N=!0};i.interruptHandlers.add(j),process.on("SIGINT",j);let W=await S({s:O,user:x,poll:F.poll,intervalMs:Number(c.LEFTOFF_SIGNUP_POLL_MS)||void 0,timeoutMs:Number(c.LEFTOFF_SIGNUP_WAIT_MS)||void 0,stop:()=>N});if(i.interruptHandlers.delete(j),process.removeListener("SIGINT",j),W!=="confirmed")return $.warn(W==="stopped"?"Stopped waiting. The link still works; then run "+f.cyan("leftoff relay enable "+x):W==="expired"||W==="timeout"?"The link expired. Run "+f.cyan("leftoff relay signup "+x)+" for a fresh one.":"The signup didn’t finish ("+W+"). Run "+f.cyan("leftoff relay signup "+x)+" again."),{skipped:!0,status:W};$.succeed(m(O)+" is yours. Pair your phone: "+f.cyan("leftoff relay pair"))}if(h(e,{enabled:!0,user:x,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:x,domain:_})})).ok&&a.ok("Use from anywhere is on; the daemon is connecting")}catch{}return{host:m(O)}}async function Fy(n){let{p:e,u:t,flags:s={},env:o=process.env,yes:r}=n,i=t.c;if(ot(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 Hr({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 "+Ta+", 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=ku(o),p=tt("Installing gh with Homebrew…",{out:n.out,colors:i}),h=await new Promise(m=>{let y="",w;try{w=Xo(f,["install","gh"],{env:{...o,HOMEBREW_NO_ENV_HINTS:"1"},stdio:["ignore","pipe","pipe"]})}catch(S){return m({ok:!1,log:S.message})}w.stdout.on("data",S=>y+=S),w.stderr.on("data",S=>y+=S),w.on("error",S=>m({ok:!1,log:y+S.message})),w.on("close",S=>m({ok:S===0,log:y}))});if(!h.ok){p.fail("Homebrew couldn't install gh:");for(let m of h.log.trim().split(`
282
- `).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(p.succeed("Installed gh"),c=await Hr({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=Ur(o);return e.close(),n.out.write(`
283
- `),Js(d,["auth","login"],{stdio:l,env:o}),n.out.write(`
284
- `),c=await Hr({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 Ny,st,rf,u1,d1,af,Ys=z(()=>{In();ke();Qn();ec();Is();Es();Vr();$s();Yd();_a();ve();Jd();Ny=n=>new Promise(e=>setTimeout(e,n)),st=n=>n&&n.startsWith(qs.homedir()+Ne.sep)?"~"+n.slice(qs.homedir().length):n,rf=n=>n==="~"?qs.homedir():n&&n.startsWith("~/")?Ne.join(qs.homedir(),n.slice(2)):n,u1=n=>n===!0||n==="true"||n==="1"||n==="yes",d1=n=>n===!1||n==="false"||n==="0"||n==="no";af={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 bc={};Jt(bc,{moveHooks:()=>$y,parseMoveArgs:()=>Gy,runMove:()=>df});import Fi from"node:fs";import Uy from"node:os";import tr from"node:path";import{spawnSync as Hy}from"node:child_process";async function xc(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 Gy(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 $y({from:n,cfg:e,token:t,daemonPid:s=null,noRestart:o=!1,say:r=()=>{},env:i=process.env}){let a=At(),l=Fi.existsSync(a)&&!i.CHECKPOINTS_SERVICE_DRY_RUN,c=e.port,u=!1;return{async stopWriters(){let d=await xc(c,t);if(u=d.up,!!d.up){if(l){let f=_n()==="darwin"?["launchctl","unload",a]:["systemctl","--user","stop",Rs];Hy(f[0],f.slice(1),{encoding:"utf8"})}else{let f=s||d.pid||null;if(!f)try{f=Number(Fi.readFileSync(tr.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 p=0;p<50&&Ky(f);p++)await vc(100);if(Ky(f)){try{process.kill(f,"SIGKILL")}catch{}await vc(200)}}for(let f=0;f<40&&(await xc(c,t)).up;f++)await vc(100);if((await xc(c,t)).up)throw new Error("the daemon didn’t stop");r("Daemon stopped")}},async switchTo(d){if(Sc(d),Fi.existsSync(a)){let f=En({root:d,bin:Fe});Fi.readFileSync(a,"utf8")!==f&&Fi.writeFileSync(a,f)}},async startWriters(d){if(o||!u)return{ok:!0,skipped:!0};let f=Se(d),p=X(d).port;if(l)for(let h of Ks())Hy(h[0],h.slice(1),{encoding:"utf8"});else{let{LEFTOFF_DIR:h,CHECKPOINTS_DIR:m,...y}=i;gc({root:d,bin:Fe,env:{...y,CHECKPOINTS_DIR:d}})}for(let h=0;h<80;h++){let m=await xc(p,f);if(m.up&&m.folder&&tr.resolve(m.folder)===tr.resolve(d))return{ok:!0,port:p};if(m.up&&m.status===401)return{ok:!1,error:"something else is answering on port "+p};await vc(150)}return{ok:!1,error:"no answer on port "+p}}}}async function df({argv:n=[],out:e=process.stdout,input:t=process.stdin,env:s=process.env}={}){let o=ee,r=Gy(n),i=k=>{r.json||e.write(k+`
285
- `)},a=V();if(r.help)return e.write(y1),0;if(r.backups||r.deleteBackup){let k=Jo(a);if(r.backups){if(r.json)return e.write(JSON.stringify({backups:k})+`
286
- `),0;k.length||i("No backups from a move next to "+ge(a)+".");for(let T of k)i(" "+T.display+o.dim(" (moved "+(T.at||"?")+")"));return 0}let v=r.deleteBackup===!0?k.length===1?k[0].path:null:tr.resolve(By(r.deleteBackup));if(!v)return i(k.length?"Several backups; name one: "+J+" move --delete-backup <path>":"No backups to delete."),1;if(!r.yes){let T=new ft({input:t,output:e});if(!await T.confirm("Delete "+ge(v)+" for good?",!1).finally(()=>T.close&&T.close()))return i("Kept it."),0}try{return vi(v),i(o.green("✓")+" Deleted "+ge(v)),0}catch(T){return i(o.red("✗")+" "+T.message),1}}if(!r.to){let k=Wo({current:a});if(i("usage: "+J+` move <folder> [--dry-run] [--yes]
287
-
288
- Now: `+ge(a)+(k.current.syncedBy?" (in "+k.current.syncedBy.label+")":" (not in a synced folder)")),k.candidates.length){i(`
289
- Synced folders on this Mac:`);for(let v of k.candidates)i(" "+v.label.padEnd(14)+" "+v.display+(v.hasLeftoff?o.dim(" · already has Leftoff data (joins it)"):"")+`
290
- `+" ".repeat(15)+o.dim(v.tradeoff))}return 1}let l=tr.resolve(By(r.to)),c=X(a),u=le(c),d=null;try{d=Tn(u,{dir:Rl(s)})}catch{}let f=zo(a,l,{machine:u,identity:d});r.force&&(f.collisions=f.collisions.filter(k=>k.code!=="machine_name_maybe_taken"),f.ok=!f.errors.length&&!f.collisions.length);let p=k=>{try{jd({from:a,to:l,...k},s)}catch{}};if(r.json&&r.dryRun){let{files:k,...v}=f;return e.write(JSON.stringify({...v,sample:k.filter(T=>T.action!=="same").slice(0,20)})+`
291
- `),f.ok?0:1}i(o.bold((f.merge?"Join ":"Move to ")+ge(l))+o.dim(" (from "+ge(a)+")"));for(let k of[...f.errors,...f.collisions])i(" "+o.red("✗")+" "+k.message);if(!f.ok)return p({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})+`
292
- `),1;let h=f.counts;i(" "+h.total+" files: "+h.copy+" to copy"+(h.same?", "+h.same+" already there":"")+(h.keepTarget?", "+h.keepTarget+" kept from the joined folder":"")+(h.conflictCopy?", "+h.conflictCopy+" list"+(h.conflictCopy===1?"":"s")+" merged":"")+(h.renameCopy?", "+h.renameCopy+" renamed":""));for(let k of f.warnings)i(" "+o.yellow("!")+" "+k.message);if(i(" The old folder stays, renamed to "+ge(f.backup)+", until you delete it."),r.dryRun)return i(o.dim("Dry run: nothing changed.")),0;if(!r.yes){let k=new ft({input:t,output:e});if(!await k.confirm(f.merge?"Join it?":"Move it?",!0).finally(()=>k.close&&k.close()))return i("Nothing changed."),0}p({state:"moving",ok:null,message:"Copying "+h.total+" files"});let m=$y({from:a,cfg:c,token:Se(a),daemonPid:r.daemonPid,noRestart:r.noRestart,say:k=>i(" "+o.dim("· "+k)),env:s}),y=await Ud(f,{hooks:m,machine:u,say:k=>{i(" "+o.dim("· "+k)),p({state:"moving",ok:null,message:k})}});if(!y.ok)return i(" "+o.red("✗")+" "+y.error+(y.rolledBack?o.dim(" · put back: still using "+ge(a)):"")),p({state:"failed",ok:!1,error:y.error,stage:y.stage,rolledBack:!!y.rolledBack}),r.json&&e.write(JSON.stringify(y)+`
293
- `),1;let w=qo(l,{cli:J}),S=Wo({current:l});return p({state:"done",ok:!0,copied:y.copied,verified:y.verified,kept:y.kept,conflicts:y.conflicts,backup:y.backup,merge:f.merge,syncedBy:S.current.syncedBy,join:w}),i(" "+o.green("✓")+" "+(f.merge?"Joined":"Moved")+": "+y.copied+" copied, "+y.verified+" verified"+(y.kept?", "+y.kept+" kept":"")+". Leftoff now uses "+ge(l)),i(" "+o.dim("Backup: "+ge(y.backup)+" (delete it when you’re happy: "+J+" move --delete-backup)")),i(`
270
+ `)}function bw(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 so(n,e,t,s){if(!t.ok){n.write(e.red("✖")+" Test checkpoint failed with "+s+": "+(t.error||"no answer")+`
271
+ `);return}let o=[];for(let[a,l]of[["Doing",t.doing],["Stopped at",t.stoppedAt],["Next",t.next]]){let c=bw(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":"";Df(n,e,"Sample checkpoint · "+s+" · "+Di(t.ms)+i,o),t.parseOk||n.write(e.yellow("!")+` The model didn't follow the 3-line format; a bigger model usually does.
272
+ `)}async function cr(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 Mc(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 Lc({root:n,bin:e=Be,env:t=process.env}){let s=ke(n);Xe.mkdirSync(s.root,{recursive:!0});let o=Xe.openSync(De.join(n,".daemon.log"),"a"),r=lr(process.execPath,[e,"serve"],{detached:!0,stdio:["ignore",o,o],env:{...t,CHECKPOINTS_DIR:n}});return r.unref(),Xe.closeSync(o),Xe.writeFileSync(De.join(n,".daemon.pid"),String(r.pid)+`
273
+ `),r.pid}function ox(n){let e=process.platform==="darwin"?["open",[n]]:process.platform==="win32"?["cmd",["/c","start","",n]]:["xdg-open",[n]];try{let t=lr(e[0],e[1],{detached:!0,stdio:"ignore"});return t.on("error",()=>{}),t.unref(),!0}catch{return!1}}function Dc(n,{summarizer:e,machine:t,hostname:s=no.hostname(),port:o,summaryLength:r}={}){let i=ra({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 Rc(n,e){let t=ke(n),s=t.config+".tmp-"+process.pid;return Xe.writeFileSync(s,JSON.stringify(e,null,2)+`
274
+ `),Xe.renameSync(s,t.config),t.config}function Pc(n){let e=ha(),t=qh(n);if(De.resolve(n)===e)return Ei("CHECKPOINTS_DIR",null),{how:"default",pointer:t};let s=null;try{s=Xe.lstatSync(e)}catch{}if(s&&s.isSymbolicLink()){if(De.resolve(De.dirname(e),Xe.readlinkSync(e))===De.resolve(n))return Ei("CHECKPOINTS_DIR",null),{how:"symlink",pointer:t,already:!0};Xe.unlinkSync(e),s=null}return s?(Ei("CHECKPOINTS_DIR",n),{how:"settings-env",pointer:t}):(Xe.symlinkSync(n,e,"dir"),Ei("CHECKPOINTS_DIR",null),{how:"symlink",pointer:t})}function Lf(n=process.env){let e=to(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"?De.join(t,"node_modules"):De.join(t,"lib","node_modules");for(let o of oi)for(let r of["leftoff.js","checkpoints.js"]){let i=De.join(s,o,"bin",r);if(Xe.existsSync(i))return i}return null}function rx(n,e,t,s){t.close(),s.write(`
275
+ `);let o=to(n,e,{stdio:"inherit"});return s.write(`
276
+ `),o.status===0}async function jc(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)||ht).replace(/\/$/,""),c=(o["lmstudio-url"]||(o["base-url"]&&n.wantProvider==="openai-compatible"?o["base-url"]:null)||on).replace(/\/$/,""),u=ot("Looking for local model servers…",{out:s,colors:a}),[d,f]=await Promise.all([Bt({baseUrl:l,fetchImpl:r,env:i}),n.wantProvider==="ollama"?{running:!1,models:[]}:An({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 ("+it(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 p;if(n.wantProvider==="openai-compatible"?p="openai-compatible":n.wantProvider==="ollama"?p="ollama":d.running&&f.running?p=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?p="openai-compatible":p="ollama",p==="openai-compatible")return Sw(n,f,c);if(!d.running){if(d.binary){let h=Wr(process.platform,d.binary);if(h&&await e.confirm("Start Ollama now? ("+h.shell+")",!0)){if(h.detached){let y=lr(h.cmd[0],h.cmd.slice(1),{detached:!0,stdio:"ignore"});y.on("error",()=>{}),y.unref()}else to(h.cmd[0],h.cmd.slice(1),{stdio:"ignore"});let m=ot("Waiting for Ollama to come up…",{out:s,colors:a});d=await Eo({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&&!at(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 h=Fp(process.platform,i),m=await e.select("Install Ollama?",[...h.map(b=>({label:b.label,value:b.id,hint:b.shell+(b.note?" · "+b.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 Sw(n,f,c);let y=h.find(b=>b.id===m);if(y.cmd){if(t.line(" This runs:"),t.cmd(y.shell),!await e.confirm("Run it now?",!0))return null;if(!rx(y.cmd[0],y.cmd.slice(1),e,s))return t.fail("The Ollama installer exited with an error (see above)."),null;if(t.ok("Ollama installed"),y.id==="brew"){let b=Wr(process.platform,Ls(i));if(b){let w=lr(b.cmd[0],b.cmd.slice(1),{detached:b.detached,stdio:"ignore"});w.on("error",()=>{}),w.unref()}}}else{if(t.line(" Install it with:"),t.cmd(y.shell),y.url&&t.note("or download it from "+y.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=ot("Waiting for Ollama…",{out:s,colors:a});if(d=await Eo({baseUrl:l,fetchImpl:r,timeoutMs:3e4}),!d.running&&d.binary){let b=Wr(process.platform,d.binary);if(b){let w=lr(b.cmd[0],b.cmd.slice(1),{detached:!0,stdio:"ignore"});w.on("error",()=>{}),w.unref(),d=await Eo({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 vw(n,d)}async function vw(n,e){let{p:t,u:s,out:o,flags:r,fetchImpl:i}=n,a=s.c,l=To(e.models,{current:n.currentModel}),c=r.model?Ia(r.model):jp(l);s.line(),s.line(" "+a.bold("Models for 3-line checkpoints")+a.dim(" (sizes from ollama.com/library, "+Ao+")"));let u=l.map(h=>({label:h.tag.padEnd(13)+" "+a.dim((h.curated?Da(h,Ut):(h.sizeBytes?Ut(h.sizeBytes)+" · ":"")+"on this machine").padEnd(30))+" "+(h.recommended?a.yellow("★ recommended")+" ":"")+(h.installed?a.green("✔ pulled"):""),detail:h.curated?h.note:null,value:h.tag}));u.push({label:"Other tag…",value:"__other__",hint:"any ollama.com/library tag, e.g. phi4-mini"});let d=u.findIndex(h=>We(h.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:h=>/^[\w.\-/]+(:[\w.\-]+)?$/.test(h)?null:"Use a tag like llama3.2:3b"})));let p=l.find(h=>We(h.tag,f));if(!p||!p.installed){let h=p&&p.sizeBytes?" ("+Ut(p.sizeBytes)+")":"";if(at(r,"pull")===!1)throw new Error(f+" isn't pulled and --no-pull was given");if(!await t.confirm("Download "+f+h+" now?",!0))return null;let m=hc("pull "+f,{out:o,colors:a}),y=Date.now();o.write(a.dim(" pulling "+f+" from ollama.com · Ctrl-C to stop (a re-run resumes)")+`
277
+ `);try{await t.interruptible(S=>Oo({tag:f,baseUrl:e.baseUrl,fetchImpl:i,signal:S,onProgress:b=>{let w=(Date.now()-y)/1e3,A=w>.5&&b.completed?Ut(b.completed/w)+"/s":"",v=b.total?Ut(b.completed)+" / "+Ut(b.total)+(A?" · "+A:""):b.status;m.update(b.fraction,v)}}))}catch(S){if(S.cancelled||(m.fail(S.message),n.yes))throw S;return null}m.done("Pulled "+f+a.dim(" in "+Di(Date.now()-y)))}else s.ok(f+" is already pulled");return{provider:"ollama",model:f,baseUrl:e.baseUrl===ht?null:e.baseUrl}}async function Sw(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 An({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 An({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===on?null:e.baseUrl}}async function ur(n,e,t){let{p:s,u:o,out:r,fetchImpl:i}=n,a=ot("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 Ds(e,{fetchImpl:i});return l.ok?a.succeed("It works."):a.fail("The test didn't work."),so(r,o.c,l,t),l}async function kw(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:b=>/^[A-Za-z_][A-Za-z0-9_]*$/.test(b)?null:"That isn't an env var name"});e.apiKeyEnv=f;let p=".secret/"+s,h=!!c[f],m=xn(e.provider,{apiKeyEnv:"__none__",apiKeyFile:d&&u.apiKeyFile?u.apiKeyFile:p,root:n.root},{});h?a.ok("$"+f+" is set in this shell"):a.warn("$"+f+" isn't set in this shell");let y=h?c[f]:null,S=b=>{e.apiKeyFile=p,(n.pendingSecrets=n.pendingSecrets||[]).push({name:s,value:b})};if(!y&&m?(a.ok("A saved key is already in "+p),e.apiKeyFile=d&&u.apiKeyFile||p,y=m):!y&&!n.yes&&(y=await i.ask("Paste the key (hidden; Enter to skip)","",{secret:!0}),y&&S(y)),y&&h&&!n.yes&&await i.confirm("Also save it to "+p+" (mode 0600) so the background service can use it?",!0)&&S(y),y||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."),y&&at(l,"test")!==!1&&(n.yes||await i.confirm(o,!0))){process.env.__CHECKPOINTS_TEST_KEY=y;try{await ur(n,{...e,apiKeyFile:void 0,apiKeyEnv:"__CHECKPOINTS_TEST_KEY"},r)}finally{delete process.env.__CHECKPOINTS_TEST_KEY}}return y}async function ix(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||Ms),c=n.yes?null:ot("Fetching the cloud model list from "+l.replace(/^https?:\/\//,"")+"…",{out:n.out,colors:a}),u=await Yr({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=La(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 p=d.filter(S=>S.curated||S.current),h=p.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=>!p.includes(S)).map(S=>S.tag);h.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 y=await t.select("Which cloud model?",h,Math.max(0,h.findIndex(S=>S.value===f.tag)));return y!=="__other"?y: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 ax(n){let{p:e,u:t,flags:s,env:o}=n,r=t.c,i=n.currentSummarizer||{},a=Mf[String(i.provider||"auto").toLowerCase()]||"claude",l=s.summarizer?Mf[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(h=>h.value===d)));if(f==="claude"){let h={provider:"claude",model:"haiku"};return n.claude.found&&(at(s,"test")===!0||!n.yes&&await e.confirm("Run a test checkpoint with `claude -p` now? (~10 s, uses your Claude login)",!1))?await ur(n,h,"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.'),h}if(f==="heuristic")return{provider:"heuristic",model:null};if(f==="anthropic"){let h={provider:"anthropic",model:s.model||"haiku"};return await kw(n,h,{defaultEnv:"ANTHROPIC_API_KEY",secretName:"anthropic-api-key",testPrompt:"Test it now? (one Haiku call, well under a cent)",testLabel:"Claude Haiku (API)"}),h}if(f==="ollama-cloud"){let h=await ix(n,i.provider==="ollama-cloud"?i.model:null),m={provider:"ollama-cloud",model:h,...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 kw(n,m,{defaultEnv:"OLLAMA_API_KEY",secretName:"ollama-api-key",testPrompt:"Test it now? (one call to "+h+" on ollama.com)",testLabel:h+" (Ollama cloud)"}),m}let p=await jc({...n,wantProvider:l==="ollama"||l==="openai-compatible"?l:null});if(!p){if(n.yes)throw new Error("local model setup didn't finish");continue}if(at(s,"test")!==!1){let h=await ur(n,p,p.model);if(!h.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(!h.ok&&n.yes)throw new Error("test checkpoint failed: "+h.error)}return p}}async function xw(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=mn(i,{strict:!0});if(!u)throw new Error("unknown --summary-length="+i+" (short, informative, long)");return s.ok("Checkpoint length: "+u),u}let a=mn(e.summary&&e.summary.length),l=Wy.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=Sc(Yy[u]).split(`
278
+ `).flatMap(f=>bw(f,70));if(Df(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 Rf({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=ds(us(t)),a=sx(t,i),l=i,c=new yt({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(`
279
+ `),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"+V)),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 h=jy(s);u.claude=h,h.found?h.modsSupported?a.ok("Claude Code "+(h.version||"?")+l.dim(" · "+it(h.bin))):a.warn("Claude Code "+(h.version||"(version unknown)")+" · mods need "+Ri+"+ (update with `claude update`); settings hooks work on any version"):a.warn("Claude Code's `claude` command isn't on your PATH");let m=Be;if(ff(ir)){let z=Lf(s);if(z)m=z,a.ok("leftoff is installed globally"+l.dim(" · "+it(z)));else{a.warn("Running from a temporary npx copy.");let te=at(n,"global"),ne=hf(ir)?nt:"github:"+pc;if(te!==!1&&(te===!0||!r&&await c.confirm("Install the `leftoff` command globally (npm install -g "+ne+")?",!0))){let T=ot("npm install -g "+ne,{out:t,colors:l}),L=to(process.platform==="win32"?"npm.cmd":"npm",["install","-g",ne],{encoding:"utf8",env:s}),j=L.status===0?Lf(s):null;j?(m=j,T.succeed("Installed `leftoff` globally")):T.fail("Global install failed: "+((L.stderr||"").trim().split(`
280
+ `).pop()||"exit "+L.status)+" (continuing from the npx copy)")}m===Be&&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:te,writeStamp:ne,DEFAULT_SOURCE:T}=await Promise.resolve().then(()=>(ai(),Jm)),L=await z({env:s});if(L.kind!=="clone"&&L.kind!=="npm"&&!L.sha){let j=await te(T,{env:s,timeoutMs:8e3});j.ok&&ne({sha:j.sha,source:T})}}catch{}let y=Z(),S=Xe.existsSync(ke(y).config);S&&a.info("Found an existing setup in "+it(y)+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 b=await c.ask("Where should checkpoints live?",it(If(n.dir||y)),{validate:z=>z&&De.isAbsolute(If(z))?null:"Use an absolute path or ~/…"}),w=De.resolve(If(b));u.root=w;let A=gn(w)||(S&&w===y?gn(y):null)||{},v=X(w),k=no.hostname(),g=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(De.resolve(w)!==De.resolve(y)&&Ln(y)&&!n["no-move"]){let{planMove:z,executeMove:te,tilde:ne}=await Promise.resolve().then(()=>(Zs(),My)),{moveHooks:T}=await Promise.resolve().then(()=>(zi(),Fc)),{loadIdentity:L}=await Promise.resolve().then(()=>(ns(),Vd)),j=null;try{j=L(g)}catch{}let Y=z(y,w,{machine:g,identity:j});if(!Y.ok){for(let pe of[...Y.errors,...Y.collisions])a.warn(pe.message);throw new Error("Can’t "+(Y.merge?"join ":"move to ")+ne(w)+" yet (see above). Nothing changed.")}if(a.info((Y.merge?"That folder already has Leftoff data"+(Y.warnings.length?" ("+Y.warnings.map(pe=>pe.message).join(" ")+")":"")+". ":"")+Y.counts.total+" files in "+ne(y)+"."),await c.confirm((Y.merge?"Merge":"Move")+" your sessions into "+ne(w)+"? (copied and checked; the old folder is kept as "+ne(Y.backup)+")",!0)){let pe=X(y),St=await te(Y,{hooks:T({from:y,cfg:pe,token:be(y),noRestart:!0,env:s}),machine:g,say:hn=>a.line(" "+l.dim("· "+hn))});if(!St.ok)throw new Error("The move stopped ("+St.error+"); put back, still using "+ne(y)+".");a.ok((Y.merge?"Merged":"Moved")+": "+St.copied+" copied, "+St.verified+" verified. Backup: "+ne(St.backup))}}a.step(2,f,"Claude Code mod");let _=nc();Uy(_)&&a.warn('"disableAllHooks": true is set in your Claude settings, so neither mods nor settings hooks run until you remove it.');let x=n.capture?String(n.capture):at(n,"hooks")===!0?"hooks":at(n,"mod")===!1?"none":null,E=[];h.found&&E.push({label:"Install the mod",value:"mod",hint:h.modsSupported?"recommended · /cp, /cp-name and the /leftoff pane":"needs Claude Code "+Ri+"+"}),E.push({label:"Settings hooks only",value:"hooks",hint:"works on any version; no slash commands or pane"+(h.found?"":" · start working once Claude Code is installed")}),E.push({label:"Skip for now",value:"none"});let O=x||(h.found&&h.modsSupported?"mod":"hooks");h.found||(a.line(" Install Claude Code first ("+l.underline("https://code.claude.com/docs")+"), then re-run setup to add the mod."),O==="mod"&&(O="hooks")),O=await c.select("How should Leftoff see your sessions?",E,Math.max(0,E.findIndex(z=>z.value===O)));let M=!1;if(O==="mod"){let z=Fy({override:n.marketplace}),te="",ne=ot("Checking installed plugins…",{out:t,colors:l}),T=gf({bin:h.bin,source:z,env:s,onStep:L=>{te=L,ne.update("Running "+l.cyan(L))}});if(T.ok&&T.migrated)ne.succeed("Mod moved to its new name: "+Ne+" (was "+fs+")");else if(T.ok&&T.already&&T.state&&!T.state.enabled)ne.succeed("Mod was installed but disabled; enabled it ("+Ne+")");else if(T.ok&&T.already)ne.succeed("Mod already installed and enabled ("+Ne+")");else if(T.ok){ne.succeed("Mod installed: "+Ne);for(let L of T.steps)a.cmd("claude "+L.args.map(j=>it(j)).join(" "))}else ne.fail("`"+te+"` failed: "+String(T.error).split(`
281
+ `).slice(-2).join(" "));M=T.ok,T.ok?h.modsSupported||a.warn("The mod is installed but loads only on Claude Code "+Ri+"+."):(/github|clone|auth|permission|denied|not found/i.test(String(T.error))&&z===pc&&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."),O=await c.confirm("Use settings hooks instead? (same tracking, no slash commands)",!0)?"hooks":"none"),M&&_i()&&await c.confirm("Remove the older settings hooks? The mod covers them.",!0)&&(sc(),a.ok("Removed the settings hooks"))}if(O==="hooks"){let z=_i(),te=Xo(void 0,{bin:m});a.ok((z?"Settings hooks refreshed in ":"Settings hooks added to ")+it(te)+l.dim(" (backup saved next to it)"))}O==="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 C=await ax(u),U=await xw(u,v);a.step(4,f,"Save and start"),Ee(w);for(let z of u.pendingSecrets||[])pa(z.name,z.value,w,s),a.ok("Key saved to "+it(De.join(en(s),z.name))+l.dim(" (0600, on this Mac only)"));let W=Dc(A,{summarizer:C,machine:g,hostname:k,port:n.port,summaryLength:U});Rc(w,W),a.ok("Config saved: "+it(ke(w).config)+l.dim(" · summarizer "+C.provider+(C.model?" / "+C.model:"")+" · "+U+" checkpoints · machine "+g));let I=Pc(w);I.how==="symlink"&&!I.already?a.ok("Linked ~/checkpoints → "+it(w)+l.dim(" (so the mod and hooks find it)")):I.how==="settings-env"&&a.ok("Set CHECKPOINTS_DIR in your Claude settings so the mod and hooks find "+it(w));let P=X(w),B=P.port,ce=be(w),$={...s,CHECKPOINTS_DIR:w},ge=await cr(B,ce,o),Qe=at(n,"service"),Me=Qe!==void 0?Qe:!r&&process.platform!=="win32"?await c.confirm("Start Leftoff automatically when you log in?",!0):!1;if(Me){let z=Nt(),te=oc({root:w,bin:m,dryRun:!!n["service-dry-run"]});te&&a.ok("Moved the login service to its new name: "+it(te.to)+l.dim(" (stopped and removed "+it(te.from)+")")),Xe.mkdirSync(De.dirname(z),{recursive:!0});let ne=In({root:w,bin:m}),T=!Xe.existsSync(z)||Xe.readFileSync(z,"utf8")!==ne;Xe.writeFileSync(z,ne),a.ok((T?"Login service written: ":"Login service up to date: ")+it(z));let L=!0;if(!n["service-dry-run"])for(let j of qs()){let Y=to(j[0],j.slice(1),{encoding:"utf8"});if(Y.status!==0&&j[1]!=="unload"){L=!1,a.warn("`"+j.join(" ")+"` failed: "+((Y.stderr||Y.error&&Y.error.message||"").trim().split(`
282
+ `)[0]||"exit "+Y.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&&!(ge=await cr(B,ce,o)).up;j++)await ww(250)}if(at(n,"start")===!1)a.info("Not starting the daemon (--no-start). Start it with `leftoff serve`.");else if(ge.up&&ge.auth){try{await Mc(B,ce,{summarizer:W.summarizer,machineNames:W.machineNames||{},summary:{length:U}},o),a.ok("Daemon already running on 127.0.0.1:"+B+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.")}ge.machine&&ge.machine!==g&&a.note("Restart the daemon to show the new machine name.")}else if(ge.up&&!ge.auth)a.warn("Something else (another Leftoff folder?) is on port "+B+'. Stop it, or set "port" in config.json, then run `leftoff serve`.');else{let z=ot("Starting the daemon…",{out:t,colors:l});Lc({root:w,bin:m,env:$});for(let te=0;te<40&&!(ge=await cr(B,ce,o)).up;te++)await ww(250);ge.up?(z.succeed("Daemon running on 127.0.0.1:"+B),Me||a.note("Runs in the background until reboot; `leftoff setup --service` starts it at login.")):z.fail("The daemon didn't start; see "+it(De.join(w,".daemon.log")))}if(a.step(5,f,"Check"),at(n,"doctor")!==!1){let z=to(process.execPath,[m,"doctor"],{encoding:"utf8",env:{...$,NO_COLOR:"1"}});for(let te of(z.stdout||"").trim().split(`
283
+ `).filter(Boolean).map(ne=>ne.split(no.homedir()+De.sep).join("~"+De.sep))){let ne=te.match(/^([✓✗·])\s(.*)$/);ne?ne[1]==="✓"?a.ok(ne[2]):ne[1]==="✗"?a.fail(ne[2]):a.info(ne[2]):a.line(" "+te)}}let Q=await Aw(u,{root:w,machine:g,port:B,token:ce,up:!!ge.up});await Tw({...u,input:e}).catch(z=>a.info("PR cards: couldn't check gh ("+z.message+")"));let re=ge.up?(await Pi({port:B,prefer:s.CHECKPOINTS_OPEN_HOST||P.openHost||"auto",token:ce})).url:"http://127.0.0.1:"+B+"/",G=at(n,"open");ge.up&&(G===!0||G===void 0&&!r&&t.isTTY&&await c.confirm("Open the board in your browser?",!0))&&(ox(re)?a.ok("Opened "+re):a.info("Open "+re)),a.line();let ze=[l.bold("Board")+" "+l.cyan(re)];if(O==="mod"){let z=(te,ne,T,L)=>ze.push(te+" ".repeat(15-te.replace(/\x1b\[[0-9;]*m/g,"").length)+l.cyan(ne)+(T?" "+T:"")+" ".repeat(Math.max(1,21-ne.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"),ze.push(l.dim("Already-open sessions pick the mod up when they restart."))}else O==="hooks"?(ze.push(l.bold("Sessions")+" tracked through settings hooks;"),ze.push(" checkpoints happen on their own after 15 min idle")):ze.push(l.bold("Sessions")+" not hooked up yet: `leftoff setup`");return ze.push(l.bold("Models")+" "+l.cyan("leftoff models")+" list, pull, switch"),ze.push(l.bold("Digest")+" "+l.cyan("leftoff digest")+" where you left off"),ze.push(l.bold("Reopen")+" "+l.cyan("leftoff open")+" the board, any time"),Q&&Q.host?ze.push(l.bold("Anywhere")+" "+l.cyan("https://"+Q.host)+l.dim(" (leftoff relay pair)")):ze.push(l.bold("Anywhere")+" "+l.cyan("leftoff relay signup")+" yourname"+l.dim(" (your board on your phone)")),Df(t,l,"You're set",ze),c.close(),r&&process.removeListener("SIGINT",d),{ok:!0,root:w,capture:O,summarizer:C,port:B,daemonUp:!!ge.up,anywhere:Q}}catch(p){if(c.close(),r&&process.removeListener("SIGINT",d),p&&p.cancelled)return t.write(`
284
+ `),a.fail("Setup cancelled ("+p.message.replace(/^cancelled \(?|\)$/g,"")+"). Re-run `leftoff setup` any time; finished steps are skipped."),{ok:!1,cancelled:!0};throw p}}async function Aw(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:p,writeRelaySettings:h,hostFor:m,DEFAULT_DOMAIN:y}=await Promise.resolve().then(()=>(Ic(),yw)),{startSignup:S,waitForConfirm:b,validEmail:w,checkName:A}=await Promise.resolve().then(()=>(Ac(),Qy)),{checkUserName:v}=await Promise.resolve().then(()=>(Fi(),zy)),{loadIdentity:k}=await Promise.resolve().then(()=>(ns(),Vd)),g=p(e,c);if(g.user)return a.ok("Use from anywhere: "+m(g)+(g.enabled?"":f.dim(" (off on this Mac: leftoff relay enable "+g.user+")"))),{host:m(g),existing:!0};if(at(l,"signup")===!1)return{skipped:!0};let _=String(l.domain||g.domain||y),x=typeof l.signup=="string"?l.signup.trim().toLowerCase():"",E=typeof l.email=="string"?l.email.trim():"";if(u&&!(x&&E))return{skipped:!0};let O=!1;a.line();let M=W=>W&&W.cancelled&&/input closed/.test(W.message);try{if(!u&&!x&&!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 W=!!x;for(let I=0;;I++){x||(x=(await i.ask("Your name","",{hint:"(for yourname."+_+")",validate:ce=>{let $=v(String(ce).toLowerCase());return $.ok?null:$.code==="reserved"?"That one’s reserved; pick another":"2–32 lowercase letters, digits or hyphens"}})).toLowerCase());let P=await A({s:{...g,user:x,domain:_},name:x,sign:k(t).signX});if(P.ok&&P.status==="yours"){O=!0;break}let B=P.ok?P.status==="available"?null:P.message:P.code==="legacy"?null:P.message;if(!B){P.ok&&P.claimed&&a.note(P.host+" was claimed on leftoff.dev. Use the email you claimed it with.");break}if(a.warn(B),W||u||I>=2||P.code==="unreachable"||P.code==="closed")return a.info("Try again any time: "+f.cyan("leftoff relay signup yourname")),{skipped:!0,error:B};x=""}!O&&!E&&(E=await i.ask("Email","",{hint:"(for the confirmation link)",validate:I=>w(I)?null:"That doesn’t look like an email address"}))}catch(W){if(!M(W))throw W;return a.line(),a.info("Skipped. Later: "+f.cyan("leftoff relay signup yourname")),{skipped:!0}}let C={...g,user:x,domain:_},U=O?{ok:!0,already:!0}:await S({s:C,name:x,email:E,machine:t,sign:k(t).signX});if(!U.ok)return a.warn(U.message),a.info("Try again any time: "+f.cyan("leftoff relay signup "+x)),{skipped:!0,error:U.message};if(U.already)a.ok("This Mac already has "+m(C)+". Pair your phone: "+f.cyan("leftoff relay pair"));else{a.ok("Sent a link to "+U.email+". Open it and press "+f.bold("Confirm "+m(C))+f.dim(" (30 minutes)")),U.claimed&&a.note("Your leftoff.dev claim for "+x+" carries over.");let W=ot("Waiting for you to confirm… "+f.dim("(Ctrl-C skips; finish later with leftoff relay enable "+x+")"),{out:n.out,colors:f}),I=!1,P=()=>{I=!0};i.interruptHandlers.add(P),process.on("SIGINT",P);let B=await b({s:C,user:x,poll:U.poll,intervalMs:Number(c.LEFTOFF_SIGNUP_POLL_MS)||void 0,timeoutMs:Number(c.LEFTOFF_SIGNUP_WAIT_MS)||void 0,stop:()=>I});if(i.interruptHandlers.delete(P),process.removeListener("SIGINT",P),B!=="confirmed")return W.warn(B==="stopped"?"Stopped waiting. The link still works; then run "+f.cyan("leftoff relay enable "+x):B==="expired"||B==="timeout"?"The link expired. Run "+f.cyan("leftoff relay signup "+x)+" for a fresh one.":"The signup didn’t finish ("+B+"). Run "+f.cyan("leftoff relay signup "+x)+" again."),{skipped:!0,status:B};W.succeed(m(C)+" is yours. Pair your phone: "+f.cyan("leftoff relay pair"))}if(h(e,{enabled:!0,user:x,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:x,domain:_})})).ok&&a.ok("Use from anywhere is on; the daemon is connecting")}catch{}return{host:m(C)}}async function Tw(n){let{p:e,u:t,flags:s={},env:o=process.env,yes:r}=n,i=t.c;if(at(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 zr({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 "+Pa+", 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=Fu(o),p=ot("Installing gh with Homebrew…",{out:n.out,colors:i}),h=await new Promise(m=>{let y="",S;try{S=lr(f,["install","gh"],{env:{...o,HOMEBREW_NO_ENV_HINTS:"1"},stdio:["ignore","pipe","pipe"]})}catch(b){return m({ok:!1,log:b.message})}S.stdout.on("data",b=>y+=b),S.stderr.on("data",b=>y+=b),S.on("error",b=>m({ok:!1,log:y+b.message})),S.on("close",b=>m({ok:b===0,log:y}))});if(!h.ok){p.fail("Homebrew couldn't install gh:");for(let m of h.log.trim().split(`
285
+ `).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(p.succeed("Installed gh"),c=await zr({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=Ps(o);return e.close(),n.out.write(`
286
+ `),to(d,["auth","login"],{stdio:l,env:o}),n.out.write(`
287
+ `),c=await zr({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 ww,it,If,tx,nx,Mf,Xs=J(()=>{Dn();ve();os();yc();Rs();Is();ai();Zs();yf();Co();Ae();kf();ww=n=>new Promise(e=>setTimeout(e,n)),it=n=>n&&n.startsWith(no.homedir()+De.sep)?"~"+n.slice(no.homedir().length):n,If=n=>n==="~"?no.homedir():n&&n.startsWith("~/")?De.join(no.homedir(),n.slice(2)):n,tx=n=>n===!0||n==="true"||n==="1"||n==="yes",nx=n=>n===!1||n==="false"||n==="0"||n==="no";Mf={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 Fc={};Qt(Fc,{moveHooks:()=>Iw,parseMoveArgs:()=>Nw,runMove:()=>Pf});import Ji from"node:fs";import _w from"node:os";import dr from"node:path";import{spawnSync as Ew}from"node:child_process";async function Hc(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 Nw(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 Iw({from:n,cfg:e,token:t,daemonPid:s=null,noRestart:o=!1,say:r=()=>{},env:i=process.env}){let a=Nt(),l=Ji.existsSync(a)&&!i.CHECKPOINTS_SERVICE_DRY_RUN,c=e.port,u=!1;return{async stopWriters(){let d=await Hc(c,t);if(u=d.up,!!d.up){if(l){let f=Nn()==="darwin"?["launchctl","unload",a]:["systemctl","--user","stop",Ks];Ew(f[0],f.slice(1),{encoding:"utf8"})}else{let f=s||d.pid||null;if(!f)try{f=Number(Ji.readFileSync(dr.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 p=0;p<50&&Cw(f);p++)await Uc(100);if(Cw(f)){try{process.kill(f,"SIGKILL")}catch{}await Uc(200)}}for(let f=0;f<40&&(await Hc(c,t)).up;f++)await Uc(100);if((await Hc(c,t)).up)throw new Error("the daemon didn’t stop");r("Daemon stopped")}},async switchTo(d){if(Pc(d),Ji.existsSync(a)){let f=In({root:d,bin:Be});Ji.readFileSync(a,"utf8")!==f&&Ji.writeFileSync(a,f)}},async startWriters(d){if(o||!u)return{ok:!0,skipped:!0};let f=be(d),p=X(d).port;if(l)for(let h of qs())Ew(h[0],h.slice(1),{encoding:"utf8"});else{let{LEFTOFF_DIR:h,CHECKPOINTS_DIR:m,...y}=i;Lc({root:d,bin:Be,env:{...y,CHECKPOINTS_DIR:d}})}for(let h=0;h<80;h++){let m=await Hc(p,f);if(m.up&&m.folder&&dr.resolve(m.folder)===dr.resolve(d))return{ok:!0,port:p};if(m.up&&m.status===401)return{ok:!1,error:"something else is answering on port "+p};await Uc(150)}return{ok:!1,error:"no answer on port "+p}}}}async function Pf({argv:n=[],out:e=process.stdout,input:t=process.stdin,env:s=process.env}={}){let o=ee,r=Nw(n),i=w=>{r.json||e.write(w+`
288
+ `)},a=Z();if(r.help)return e.write(lx),0;if(r.backups||r.deleteBackup){let w=or(a);if(r.backups){if(r.json)return e.write(JSON.stringify({backups:w})+`
289
+ `),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 A=r.deleteBackup===!0?w.length===1?w[0].path:null:dr.resolve(Ow(r.deleteBackup));if(!A)return i(w.length?"Several backups; name one: "+q+" move --delete-backup <path>":"No backups to delete."),1;if(!r.yes){let v=new yt({input:t,output:e});if(!await v.confirm("Delete "+we(A)+" for good?",!1).finally(()=>v.close&&v.close()))return i("Kept it."),0}try{return Mi(A),i(o.green("✓")+" Deleted "+we(A)),0}catch(v){return i(o.red("✗")+" "+v.message),1}}if(!r.to){let w=nr({current:a});if(i("usage: "+q+` move <folder> [--dry-run] [--yes]
290
+
291
+ Now: `+we(a)+(w.current.syncedBy?" (in "+w.current.syncedBy.label+")":" (not in a synced folder)")),w.candidates.length){i(`
292
+ Synced folders on this Mac:`);for(let A of w.candidates)i(" "+A.label.padEnd(14)+" "+A.display+(A.hasLeftoff?o.dim(" · already has Leftoff data (joins it)"):"")+`
293
+ `+" ".repeat(15)+o.dim(A.tradeoff))}return 1}let l=dr.resolve(Ow(r.to)),c=X(a),u=de(c),d=null;try{d=Cn(u,{dir:Xl(s)})}catch{}let f=sr(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 p=w=>{try{cf({from:a,to:l,...w},s)}catch{}};if(r.json&&r.dryRun){let{files:w,...A}=f;return e.write(JSON.stringify({...A,sample:w.filter(v=>v.action!=="same").slice(0,20)})+`
294
+ `),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 p({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})+`
295
+ `),1;let h=f.counts;i(" "+h.total+" files: "+h.copy+" to copy"+(h.same?", "+h.same+" already there":"")+(h.keepTarget?", "+h.keepTarget+" kept from the joined folder":"")+(h.conflictCopy?", "+h.conflictCopy+" list"+(h.conflictCopy===1?"":"s")+" merged":"")+(h.renameCopy?", "+h.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 yt({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}p({state:"moving",ok:null,message:"Copying "+h.total+" files"});let m=Iw({from:a,cfg:c,token:be(a),daemonPid:r.daemonPid,noRestart:r.noRestart,say:w=>i(" "+o.dim("· "+w)),env:s}),y=await df(f,{hooks:m,machine:u,say:w=>{i(" "+o.dim("· "+w)),p({state:"moving",ok:null,message:w})}});if(!y.ok)return i(" "+o.red("✗")+" "+y.error+(y.rolledBack?o.dim(" · put back: still using "+we(a)):"")),p({state:"failed",ok:!1,error:y.error,stage:y.stage,rolledBack:!!y.rolledBack}),r.json&&e.write(JSON.stringify(y)+`
296
+ `),1;let S=rr(l,{cli:q}),b=nr({current:l});return p({state:"done",ok:!0,copied:y.copied,verified:y.verified,kept:y.kept,conflicts:y.conflicts,backup:y.backup,merge:f.merge,syncedBy:b.current.syncedBy,join:S}),i(" "+o.green("✓")+" "+(f.merge?"Joined":"Moved")+": "+y.copied+" copied, "+y.verified+" verified"+(y.kept?", "+y.kept+" kept":"")+". Leftoff now uses "+we(l)),i(" "+o.dim("Backup: "+we(y.backup)+" (delete it when you’re happy: "+q+" move --delete-backup)")),i(`
294
297
  On each other Mac, once the folder has synced there:
295
- `+o.cyan(w.installed)+o.dim(" (Leftoff installed)")+`
296
- `+o.cyan(w.fresh)+o.dim(" (not yet)")),r.json&&e.write(JSON.stringify({...y,join:w})+`
297
- `),0}var y1,vc,By,Ky,ji=z(()=>{ke();$s();Qn();Ys();Zn();In();St();y1=`${J} move: move the Leftoff folder, or join a synced one another Mac uses
298
+ `+o.cyan(S.installed)+o.dim(" (Leftoff installed)")+`
299
+ `+o.cyan(S.fresh)+o.dim(" (not yet)")),r.json&&e.write(JSON.stringify({...y,join:S})+`
300
+ `),0}var lx,Uc,Ow,Cw,zi=J(()=>{ve();Zs();os();Xs();ns();Dn();Tt();lx=`${q} move: move the Leftoff folder, or join a synced one another Mac uses
298
301
 
299
- ${J} move ~/Sync/Leftoff move (or join) it: copy, verify, switch, keep a backup
300
- ${J} move ~/Sync/Leftoff --dry-run say what it would do; change nothing
301
- ${J} move ... --yes don't ask first
302
- ${J} move ... --force join even though the folder has sessions under this Mac's name
303
- ${J} move --backups the backups earlier moves left
304
- ${J} move --delete-backup [PATH] delete one (asks first)
302
+ ${q} move ~/Sync/Leftoff move (or join) it: copy, verify, switch, keep a backup
303
+ ${q} move ~/Sync/Leftoff --dry-run say what it would do; change nothing
304
+ ${q} move ... --yes don't ask first
305
+ ${q} move ... --force join even though the folder has sessions under this Mac's name
306
+ ${q} move --backups the backups earlier moves left
307
+ ${q} move --delete-backup [PATH] delete one (asks first)
305
308
 
306
309
  The old folder is renamed to <name>.leftoff-backup-<date> and kept until you delete it.
307
310
  The board does the same from Settings → Sync → Set up syncing.
308
- `,vc=n=>new Promise(e=>setTimeout(e,n)),By=n=>n==="~"?Uy.homedir():String(n).startsWith("~/")?tr.join(process.env.HOME||Uy.homedir(),String(n).slice(2)):n;Ky=n=>{try{return process.kill(n,0),!0}catch{return!1}}});import Yy from"node:fs";import nr from"node:path";function Wy(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 zy({env:n=process.env,say:e=()=>{},confirm:t=async()=>!1,onLine:s=()=>{}}={}){if(mn(n).installed)return{ok:!0,already:!0};let r=Ss(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 Sr({env:n,onLine:s});return i.ok?(e("Waiting for Syncthing to start…"),(await ks({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 mf({env:n=process.env,say:e=()=>{}}={}){let t=at({env:n});if(t.ok)try{return await t.client.ping(),{ok:!0,client:t.client}}catch{}if(!mn(n).installed&&!t.cfg)return{ok:!1,code:"not_installed",error:"Syncthing isn’t installed on this Mac."};e("Starting Syncthing…");let o=kr(n);if(!o.ok)return{ok:!1,code:o.code,error:"Couldn’t start Syncthing: "+o.error};let r=await ks({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 w1({env:n=process.env,target:e=null,daemonPid:t=null,say:s=()=>{},status:o=()=>{}}={}){let r=await mf({env:n,say:s});if(!r.ok)return r;let i=r.client,a=await i.systemStatus(),l=await i.folders(),c=V(),u=l.find(h=>h.id===Ae),d=nr.resolve(aa(e||u&&u.path||br(l,n),n)),f=bs(d,l);if(f)return{ok:!1,code:"nested",error:Ee(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&&nr.resolve(u.path)!==d)return{ok:!1,code:"folder_elsewhere",error:"Syncthing already has a Leftoff folder at "+Ee(u.path,n)+". Use that path, or Stop syncing first."};if(nr.resolve(c)!==d){s("Moving your Leftoff folder to "+Ee(d,n)+" (copy, verify, keep a backup)…"),o({state:"moving",message:"Moving your folder to "+Ee(d,n)+"…"});let{runMove:h}=await Promise.resolve().then(()=>(ji(),bc)),m={write:()=>!0};if(await h({argv:[d,"--yes","--json",...t?["--daemon-pid",String(t)]:[]],out:m,env:n})!==0)return{ok:!1,code:"move_failed",error:"The move stopped: "+((Gs(n)||{}).error||"unknown error")+". Nothing changed; Leftoff still uses "+Ee(c,n)+"."}}s("Adding the Leftoff folder to Syncthing…"),o({state:"configuring",message:"Adding the folder to Syncthing…"});let p=await ca(i,{path:d,myID:a.myID});return jn({folderPath:d},n),{ok:!0,path:d,created:p.created,myID:a.myID}}async function S1(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:p=30*6e4}={}){let h;try{h=su(n,{now:d()})}catch(j){return{ok:!1,code:j.code||"bad_code",error:j.message}}let m=ou(n);if(gn(e).usedCodes.includes(m))return{ok:!1,code:"used",error:"This code was already used on this Mac. Make a new one on "+(h.machine||"the other Mac")+"."};let y=h.machine||"the other Mac";if(i({state:"starting",message:"Checking Syncthing…",with:h.machine}),!mn(e).installed&&!at({env:e}).cfg){if(!a)return{ok:!1,code:"needs_install",error:"Syncthing isn’t installed on this Mac yet.",plan:Ss(e)};i({state:"installing",message:"Installing Syncthing…"});let j=await zy({env:e,say:r,confirm:a,onLine:l});if(!j.ok)return j}let w=await mf({env:e,say:r});if(!w.ok)return w;let S=w.client,k=await S.systemStatus();if(_e(k.myID,h.deviceId))return{ok:!1,code:"same_mac",error:"This is the Mac that made the code. Paste it on your other Mac."};let v=V(),T=await S.folders(),b=T.find(j=>j.id===h.folderId),g=nr.resolve(aa(t||b&&b.path||br(T,e),e)),_=bs(g,T);if(_)return{ok:!1,code:"nested",error:Ee(g,e)+" is inside your Syncthing folder “"+_.label+"”. Pick another folder (--path); Leftoff won’t change that folder."};if(Yy.existsSync(g)&&nr.resolve(v)!==g&&!On(g)&&Yy.readdirSync(g).filter(W=>!/^\.DS_Store$|^\.stfolder$|^\.stignore$/.test(W)).length)return{ok:!1,code:"not_empty",error:Ee(g,e)+" already has other files in it. Pick an empty folder (--path)."};let C=(await S.devices()).find(j=>_e(j.deviceID,k.myID))||{},E=gn(e),D=E.originalName||ro(C.name)||le(X(v));E.originalName||jn({originalName:D},e);let O=async()=>{try{gn(e).originalName&&(await S.patchDevice(k.myID,{name:D},{self:k.myID}),jn({originalName:null},e))}catch{}},F=da(h.secret,{joinerId:k.myID,firstId:h.deviceId,folderId:h.folderId}),$=iu(D,F);await S.patchDevice(k.myID,{name:$},{self:k.myID});try{r("Adding "+y+" to Syncthing and sharing the Leftoff folder at "+Ee(g,e)+"…");let j=await ua(S,{id:h.deviceId,name:h.machine?h.machine+" (Leftoff)":"Leftoff Mac",env:e});await ca(S,{path:g,peers:[h.deviceId],myID:k.myID}),jn({folderPath:g},e),j.created||j.device.paused||await S.reconnectDevice(h.deviceId,{allow:h.deviceId}).catch(()=>{});let W=d(),ue=!1,Y="",Z=Number(e.LEFTOFF_JOIN_STUCK_MS)||6e4,te={first:y,name:D,url:"http://127.0.0.1:8384",steps:lu({first:y,name:D}),board:"Or on "+y+"’s Leftoff board: Settings → Sync → Accept "+D+"."},B=(A,M={})=>i({state:"waiting",message:A,with:h.machine,...ue?{stuck:te}:{},...M}),Ct=async({again:A=!1}={})=>{let M=0,R=d();for(B(A?"Lost the connection to "+y+". Waiting for it to come back…":"Waiting for "+y+" to accept this Mac…");;){let H=await fa(S,{peerId:h.deviceId,folderId:h.folderId});if(M=H.accepted?M+1:0,M>=2)return!0;let Q=H.connected&&H.state==="notSharing"?y+" added this Mac but isn’t sharing the Leftoff folder with it yet. On "+y+", tick Leftoff on this Mac’s Sharing tab in Syncthing.":"";if(Q&&Q!==Y&&(r(Q),B(Q)),Y=Q,!A&&!ue&&d()-W>=Z&&(ue=!0,r("If it’s stuck: "+te.steps.join(" ")+" "+te.board),B("Still waiting for "+y+" to accept this Mac…")),!A&&d()>h.expiresAt+6e4||A&&d()-R>p)return!1;await ff(f)}};if(r("Waiting for "+y+" to accept this Mac…"),!await Ct())return{ok:!1,code:"expired",error:y+" 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: "+te.steps.join(" ")};await O(),jn({usedCodes:[...gn(e).usedCodes,m].slice(-50)},e),r(y+" accepted this Mac. Syncing…");let $e="",zt=0,He=d();for(;;){let A=await fa(S,{peerId:h.deviceId,folderId:h.folderId}),M=await S.dbStatus(h.folderId).catch(()=>null),R=M&&M.globalFiles>0&&!M.needFiles&&M.state==="idle"&&On(g);if(!A.accepted&&!R){if(r("Lost the connection to "+y+". Waiting for it to come back…"),$e="",zt=0,!await Ct({again:!0}))return{ok:!1,code:"sync_timeout",error:"Lost the connection to "+y+" and it didn’t come back. Syncthing keeps trying; run `"+J+" move "+Ee(g,e)+"` once the files are here."};r("Connected to "+y+" again. Syncing…");continue}let H=cu(M);if(H!==$e&&($e=H,r(H),i({state:"syncing",message:H,with:h.machine,have:M?M.inSyncFiles:null,total:M?M.globalFiles:null})),zt=R?zt+1:0,zt>=2)break;if(d()-He>p)return{ok:!1,code:"sync_timeout",error:"The first sync is taking a long time ("+H+"). It keeps going in Syncthing; run `"+J+" join` again later, or `"+J+" move "+Ee(g,e)+"` once it’s done."};await ff(f)}}finally{await O()}if(i({state:"merging",message:"Joining the folder…",with:h.machine}),nr.resolve(v)===g)return{ok:!0,path:g,merged:!1,with:h.machine};if(On(v)){r("Joining: this Mac’s Leftoff data goes into "+Ee(g,e)+" (nothing of "+y+"’s is overwritten)…");let{runMove:j}=await Promise.resolve().then(()=>(ji(),bc));return await j({argv:[g,"--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: "+((Gs(e)||{}).error||"unknown error")+". Run `"+J+" move "+Ee(g,e)+"` to try again."}:{ok:!0,path:g,merged:!0,with:h.machine}}r("Synced. Setting up Leftoff on this Mac with that folder…");let{runSetup:N}=await Promise.resolve().then(()=>(Ys(),tc));return await N({flags:{dir:g,...o?{yes:!0}:{}},input:c,out:u,env:e}),{ok:!0,path:g,merged:!1,fresh:!0,with:h.machine}}function pf(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 gf(n,e=null){let t=[];if(!n.installed)return t.push(["info","Syncthing: not installed"+(n.brew?" · "+J+" 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+")":"")+" · "+J+" 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 · "+J+" 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; "+J+" sync setup)")+" · "+(s.state||"?")+(s.globalFiles!=null?" · "+(s.inSyncFiles!=null?s.inSyncFiles+"/":"")+s.globalFiles+" files":"")+" · last sync "+pf(s.lastSync)]),n.peers.length||t.push(["info","Peers: none yet · "+J+" 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 "+pf(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 "+J+' sync accept "'+o.name+'"']);return e&&e.armed&&t.push(["info","Pairing code: live until "+new Date(e.expiresAt).toLocaleTimeString()]),t}async function Jy({argv:n=[],api:e=null,env:t=process.env,out:s=process.stdout,input:o=process.stdin}={}){let r=Wy(n),i=ee,a=u=>{r.json||s.write(u+`
309
- `)},l=r.pos[0]||"status";if(r.help||l==="help")return s.write(hf),0;let c={ok:i.green("✓"),warn:i.yellow("!"),info:i.dim("·")};if(l==="status"){let u=await Vt({env:t,root:V()}),d=Fn(It(t));if(r.json)return s.write(JSON.stringify({...u,pairing:d})+`
310
- `),0;for(let[f,p]of gf(u,d))a(c[f]+" "+p);return 0}if(l==="install"){let u=new ft({input:o,output:s,yes:r.yes}),d=await zy({env:t,say:a,onLine:f=>a(i.dim(" "+f)),confirm:async f=>(a(`Leftoff will run:
311
+ `,Uc=n=>new Promise(e=>setTimeout(e,n)),Ow=n=>n==="~"?_w.homedir():String(n).startsWith("~/")?dr.join(process.env.HOME||_w.homedir(),String(n).slice(2)):n;Cw=n=>{try{return process.kill(n,0),!0}catch{return!1}}});import Mw from"node:fs";import fr from"node:path";function Lw(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 Dw({env:n=process.env,say:e=()=>{},confirm:t=async()=>!1,onLine:s=()=>{}}={}){if(Sn(n).installed)return{ok:!0,already:!0};let r=As(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 Or({env:n,onLine:s});return i.ok?(e("Waiting for Syncthing to start…"),(await Ts({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 Hf({env:n=process.env,say:e=()=>{}}={}){let t=ft({env:n});if(t.ok)try{return await t.client.ping(),{ok:!0,client:t.client}}catch{}if(!Sn(n).installed&&!t.cfg)return{ok:!1,code:"not_installed",error:"Syncthing isn’t installed on this Mac."};e("Starting Syncthing…");let o=Cr(n);if(!o.ok)return{ok:!1,code:o.code,error:"Couldn’t start Syncthing: "+o.error};let r=await Ts({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 cx({env:n=process.env,target:e=null,daemonPid:t=null,say:s=()=>{},status:o=()=>{}}={}){let r=await Hf({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(h=>h.id===_e),d=fr.resolve(wa(e||u&&u.path||Nr(l,n),n)),f=_s(d,l);if(f)return{ok:!1,code:"nested",error:Ce(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&&fr.resolve(u.path)!==d)return{ok:!1,code:"folder_elsewhere",error:"Syncthing already has a Leftoff folder at "+Ce(u.path,n)+". Use that path, or Stop syncing first."};if(fr.resolve(c)!==d){s("Moving your Leftoff folder to "+Ce(d,n)+" (copy, verify, keep a backup)…"),o({state:"moving",message:"Moving your folder to "+Ce(d,n)+"…"});let{runMove:h}=await Promise.resolve().then(()=>(zi(),Fc)),m={write:()=>!0};if(await h({argv:[d,"--yes","--json",...t?["--daemon-pid",String(t)]:[]],out:m,env:n})!==0)return{ok:!1,code:"move_failed",error:"The move stopped: "+((Vs(n)||{}).error||"unknown error")+". Nothing changed; Leftoff still uses "+Ce(c,n)+"."}}s("Adding the Leftoff folder to Syncthing…"),o({state:"configuring",message:"Adding the folder to Syncthing…"});let p=await ka(i,{path:d,myID:a.myID});return Bn({folderPath:d},n),{ok:!0,path:d,created:p.created,myID:a.myID}}async function ux(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:p=30*6e4}={}){let h;try{h=bu(n,{now:d()})}catch(P){return{ok:!1,code:P.code||"bad_code",error:P.message}}let m=vu(n);if(kn(e).usedCodes.includes(m))return{ok:!1,code:"used",error:"This code was already used on this Mac. Make a new one on "+(h.machine||"the other Mac")+"."};let y=h.machine||"the other Mac";if(i({state:"starting",message:"Checking Syncthing…",with:h.machine}),!Sn(e).installed&&!ft({env:e}).cfg){if(!a)return{ok:!1,code:"needs_install",error:"Syncthing isn’t installed on this Mac yet.",plan:As(e)};i({state:"installing",message:"Installing Syncthing…"});let P=await Dw({env:e,say:r,confirm:a,onLine:l});if(!P.ok)return P}let S=await Hf({env:e,say:r});if(!S.ok)return S;let b=S.client,w=await b.systemStatus();if(Oe(w.myID,h.deviceId))return{ok:!1,code:"same_mac",error:"This is the Mac that made the code. Paste it on your other Mac."};let A=Z(),v=await b.folders(),k=v.find(P=>P.id===h.folderId),g=fr.resolve(wa(t||k&&k.path||Nr(v,e),e)),_=_s(g,v);if(_)return{ok:!1,code:"nested",error:Ce(g,e)+" is inside your Syncthing folder “"+_.label+"”. Pick another folder (--path); Leftoff won’t change that folder."};if(Mw.existsSync(g)&&fr.resolve(A)!==g&&!Ln(g)&&Mw.readdirSync(g).filter(B=>!/^\.DS_Store$|^\.stfolder$|^\.stignore$/.test(B)).length)return{ok:!1,code:"not_empty",error:Ce(g,e)+" already has other files in it. Pick an empty folder (--path)."};let E=(await b.devices()).find(P=>Oe(P.deviceID,w.myID))||{},O=kn(e),M=O.originalName||mo(E.name)||de(X(A));O.originalName||Bn({originalName:M},e);let C=async()=>{try{kn(e).originalName&&(await b.patchDevice(w.myID,{name:M},{self:w.myID}),Bn({originalName:null},e))}catch{}},U=va(h.secret,{joinerId:w.myID,firstId:h.deviceId,folderId:h.folderId}),W=Au(M,U);await b.patchDevice(w.myID,{name:W},{self:w.myID});try{r("Adding "+y+" to Syncthing and sharing the Leftoff folder at "+Ce(g,e)+"…");let P=await ba(b,{id:h.deviceId,name:h.machine?h.machine+" (Leftoff)":"Leftoff Mac",env:e});await ka(b,{path:g,peers:[h.deviceId],myID:w.myID}),Bn({folderPath:g},e),P.created||P.device.paused||await b.reconnectDevice(h.deviceId,{allow:h.deviceId}).catch(()=>{});let B=d(),ce=!1,$="",ge=Number(e.LEFTOFF_JOIN_STUCK_MS)||6e4,Qe={first:y,name:M,url:"http://127.0.0.1:8384",steps:_u({first:y,name:M}),board:"Or on "+y+"’s Leftoff board: Settings → Sync → Accept "+M+"."},Me=(z,te={})=>i({state:"waiting",message:z,with:h.machine,...ce?{stuck:Qe}:{},...te}),Q=async({again:z=!1}={})=>{let te=0,ne=d();for(Me(z?"Lost the connection to "+y+". Waiting for it to come back…":"Waiting for "+y+" to accept this Mac…");;){let T=await xa(b,{peerId:h.deviceId,folderId:h.folderId});if(te=T.accepted?te+1:0,te>=2)return!0;let L=T.connected&&T.state==="notSharing"?y+" added this Mac but isn’t sharing the Leftoff folder with it yet. On "+y+", tick Leftoff on this Mac’s Sharing tab in Syncthing.":"";if(L&&L!==$&&(r(L),Me(L)),$=L,!z&&!ce&&d()-B>=ge&&(ce=!0,r("If it’s stuck: "+Qe.steps.join(" ")+" "+Qe.board),Me("Still waiting for "+y+" to accept this Mac…")),!z&&d()>h.expiresAt+6e4||z&&d()-ne>p)return!1;await jf(f)}};if(r("Waiting for "+y+" to accept this Mac…"),!await Q())return{ok:!1,code:"expired",error:y+" 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: "+Qe.steps.join(" ")};await C(),Bn({usedCodes:[...kn(e).usedCodes,m].slice(-50)},e),r(y+" accepted this Mac. Syncing…");let re="",G=0,ze=d();for(;;){let z=await xa(b,{peerId:h.deviceId,folderId:h.folderId}),te=await b.dbStatus(h.folderId).catch(()=>null),ne=te&&te.globalFiles>0&&!te.needFiles&&te.state==="idle"&&Ln(g);if(!z.accepted&&!ne){if(r("Lost the connection to "+y+". Waiting for it to come back…"),re="",G=0,!await Q({again:!0}))return{ok:!1,code:"sync_timeout",error:"Lost the connection to "+y+" and it didn’t come back. Syncthing keeps trying; run `"+q+" move "+Ce(g,e)+"` once the files are here."};r("Connected to "+y+" again. Syncing…");continue}let T=Eu(te);if(T!==re&&(re=T,r(T),i({state:"syncing",message:T,with:h.machine,have:te?te.inSyncFiles:null,total:te?te.globalFiles:null})),G=ne?G+1:0,G>=2)break;if(d()-ze>p)return{ok:!1,code:"sync_timeout",error:"The first sync is taking a long time ("+T+"). It keeps going in Syncthing; run `"+q+" join` again later, or `"+q+" move "+Ce(g,e)+"` once it’s done."};await jf(f)}}finally{await C()}if(i({state:"merging",message:"Joining the folder…",with:h.machine}),fr.resolve(A)===g)return{ok:!0,path:g,merged:!1,with:h.machine};if(Ln(A)){r("Joining: this Mac’s Leftoff data goes into "+Ce(g,e)+" (nothing of "+y+"’s is overwritten)…");let{runMove:P}=await Promise.resolve().then(()=>(zi(),Fc));return await P({argv:[g,"--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: "+((Vs(e)||{}).error||"unknown error")+". Run `"+q+" move "+Ce(g,e)+"` to try again."}:{ok:!0,path:g,merged:!0,with:h.machine}}r("Synced. Setting up Leftoff on this Mac with that folder…");let{runSetup:I}=await Promise.resolve().then(()=>(Xs(),wc));return await I({flags:{dir:g,...o?{yes:!0}:{}},input:c,out:u,env:e}),{ok:!0,path:g,merged:!1,fresh:!0,with:h.machine}}function Uf(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 Bf(n,e=null){let t=[];if(!n.installed)return t.push(["info","Syncthing: not installed"+(n.brew?" · "+q+" 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+")":"")+" · "+q+" 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 · "+q+" 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; "+q+" sync setup)")+" · "+(s.state||"?")+(s.globalFiles!=null?" · "+(s.inSyncFiles!=null?s.inSyncFiles+"/":"")+s.globalFiles+" files":"")+" · last sync "+Uf(s.lastSync)]),n.peers.length||t.push(["info","Peers: none yet · "+q+" 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 "+Uf(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 "+q+' sync accept "'+o.name+'"']);return e&&e.armed&&t.push(["info","Pairing code: live until "+new Date(e.expiresAt).toLocaleTimeString()]),t}async function Rw({argv:n=[],api:e=null,env:t=process.env,out:s=process.stdout,input:o=process.stdin}={}){let r=Lw(n),i=ee,a=u=>{r.json||s.write(u+`
312
+ `)},l=r.pos[0]||"status";if(r.help||l==="help")return s.write(Ff),0;let c={ok:i.green("✓"),warn:i.yellow("!"),info:i.dim("·")};if(l==="status"){let u=await tn({env:t,root:Z()}),d=Gn(Rt(t));if(r.json)return s.write(JSON.stringify({...u,pairing:d})+`
313
+ `),0;for(let[f,p]of Bf(u,d))a(c[f]+" "+p);return 0}if(l==="install"){let u=new yt({input:o,output:s,yes:r.yes}),d=await Dw({env:t,say:a,onLine:f=>a(i.dim(" "+f)),confirm:async f=>(a(`Leftoff will run:
311
314
  `+i.cyan(f)+`
312
- `+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: "+J+" sync setup")),0):!d.ok&&d.code==="no_brew"?(a(c.warn+" "+d.error+`
315
+ `+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: "+q+" sync setup")),0):!d.ok&&d.code==="no_brew"?(a(c.warn+" "+d.error+`
313
316
  `),d.directions.forEach((f,p)=>a(" "+(p+1)+". "+f)),a(`
314
- Then run `+i.cyan(J+" sync install")+" again."),1):d.ok?(a(c.ok+" Syncthing is installed and running"+i.dim(" · next: "+J+" sync setup")),0):(a((d.code==="declined"?c.info:i.red("✗"))+" "+d.error),d.code==="declined"?0:1)}if(l==="start"){let u=await mf({env:t,say:a});return a(u.ok?c.ok+" Syncthing is running":i.red("✗")+" "+u.error+(u.code==="not_installed"?" · "+J+" sync install":"")),u.ok?0:1}if(l==="setup"){let u=r.path||r.pos[1]||null,d=p=>{try{ao({kind:"setup",...p},t)}catch{}};d({state:"starting",message:"Checking Syncthing…"});let f;try{f=await w1({env:t,target:u,daemonPid:r.daemonPid,say:a,status:d})}catch(p){f={ok:!1,code:p.code||"error",error:p.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 "+Ee(f.path,t)+i.dim(" · next: "+J+" sync pair, to add your other Mac")),r.json&&s.write(JSON.stringify(f)+`
317
+ Then run `+i.cyan(q+" sync install")+" again."),1):d.ok?(a(c.ok+" Syncthing is installed and running"+i.dim(" · next: "+q+" sync setup")),0):(a((d.code==="declined"?c.info:i.red("✗"))+" "+d.error),d.code==="declined"?0:1)}if(l==="start"){let u=await Hf({env:t,say:a});return a(u.ok?c.ok+" Syncthing is running":i.red("✗")+" "+u.error+(u.code==="not_installed"?" · "+q+" sync install":"")),u.ok?0:1}if(l==="setup"){let u=r.path||r.pos[1]||null,d=p=>{try{yo({kind:"setup",...p},t)}catch{}};d({state:"starting",message:"Checking Syncthing…"});let f;try{f=await cx({env:t,target:u,daemonPid:r.daemonPid,say:a,status:d})}catch(p){f={ok:!1,code:p.code||"error",error:p.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 "+Ce(f.path,t)+i.dim(" · next: "+q+" sync pair, to add your other Mac")),r.json&&s.write(JSON.stringify(f)+`
315
318
  `),0):(d({state:"failed",error:f.error,code:f.code}),a(i.red("✗")+" "+f.error),r.json&&s.write(JSON.stringify(f)+`
316
- `),1)}if(l==="pair"){let u=at({env:t});if(!u.ok)return a(i.red("✗")+" "+u.reason+" · "+J+" sync install"),1;let d;if(e)try{d=await e("POST","/v1/syncthing/pair",{})}catch(w){if(d=null,!/ECONNREFUSED|fetch failed/i.test(w.message))return a(i.red("✗")+" "+w.message),1}let f=!!d;if(!d){let w=await Vt({env:t,root:V()});if(!w.running)return a(i.red("✗")+" Syncthing isn’t running · "+J+" sync start"),1;if(!w.folder)return a(i.red("✗")+" The Leftoff folder isn’t in Syncthing yet · "+J+" sync setup"),1;let S=xr({deviceId:w.myID,machine:le(X()),env:t});d={code:S.code,expiresAt:new Date(S.expiresAt).toISOString()}}if(r.json)return s.write(JSON.stringify({code:d.code,expiresAt:d.expiresAt,command:Zs(d.code),fresh:Zs(d.code,{installed:!1})})+`
319
+ `),1)}if(l==="pair"){let u=ft({env:t});if(!u.ok)return a(i.red("✗")+" "+u.reason+" · "+q+" 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 · "+q+" sync start"),1;if(!S.folder)return a(i.red("✗")+" The Leftoff folder isn’t in Syncthing yet · "+q+" sync setup"),1;let b=Mr({deviceId:S.myID,machine:de(X()),env:t});d={code:b.code,expiresAt:new Date(b.expiresAt).toISOString()}}if(r.json)return s.write(JSON.stringify({code:d.code,expiresAt:d.expiresAt,command:oo(d.code),fresh:oo(d.code,{installed:!1})})+`
317
320
  `),0;a(`On your other Mac, run:
318
321
 
319
- `+i.cyan(Zs(d.code))+`
322
+ `+i.cyan(oo(d.code))+`
320
323
 
321
- `+i.dim(" Leftoff not installed there yet: ")+Zs(d.code,{installed:!1})+`
324
+ `+i.dim(" Leftoff not installed there yet: ")+oo(d.code,{installed:!1})+`
322
325
  `+i.dim(" Or on its board: Settings → Sync → Join another Mac, and paste the code.")+`
323
326
 
324
- `+i.dim(" Works once, for 15 minutes. Only the Mac that uses this code is accepted."));let p="",h=new Set,m=async()=>{f||await Ar({client:u.client,env:t,log:()=>{}}).catch(()=>{});let w=Fn(It(t)),S=await Vt({env:t,root:V()}),k=w.paired?S.peers.find(T=>T.name===w.paired.name):null,v=w.expired?"The code expired; nothing was accepted. Run "+J+" sync pair for a new one.":w.paired?!k||!k.connected?"Paired with "+w.paired.name+" · connecting…":k.completion===100?"Done: "+w.paired.name+" is in sync.":"Paired with "+w.paired.name+" · syncing ("+(k.completion!=null?k.completion+"%":"…")+")":"Waiting for your other Mac…";v!==p&&(p=v,a(c.info+" "+v));for(let T of S.knocked||[])h.has(T.deviceId)||(h.add(T.deviceId),a(c.warn+" A Mac named “"+T.name+"” asked to connect without the pairing proof, so it wasn’t accepted. If that’s your Mac, run: "+J+' sync accept "'+T.name+'"'));return/^Done|expired/.test(v)},y=Date.now()+(r.timeoutMs||16*6e4);for(;Date.now()<y;){if(await m())return/^Done/.test(p)?0:1;await ff(Number(t.LEFTOFF_SYNCTHING_POLL_MS)||1500)}return 1}if(l==="accept"){let u=at({env:t});if(!u.ok)return a(i.red("✗")+" "+u.reason),1;let d=await Tr(u.client,{env:t}).catch(()=>[]),f=r.pos.slice(1).join(" "),p=f?d.find(h=>h.name===f||_e(h.deviceId,f)||h.short===f.toUpperCase()):d.length===1?d[0]:null;if(!p){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 h of d)a(" "+J+' sync accept "'+h.name+'" ('+h.short+", "+pf(h.at)+")")}return 1}if(!r.yes){let h=new ft({input:o,output:s});if(!await h.confirm("Add “"+p.name+"” ("+p.short+") to Syncthing and share the Leftoff folder with it? Only do this if it’s your Mac.",!1).finally(()=>h.close&&h.close()))return a("Nothing changed."),0}try{await _r(u.client,{deviceId:p.deviceId,env:t})}catch(h){return a(i.red("✗")+" "+h.message),1}return a(c.ok+" Added "+p.name+" and shared the Leftoff folder with it. Its join finishes on its own once the files arrive."),0}if(l==="stop"){let u=at({env:t});if(!u.ok)return a(i.red("✗")+" "+u.reason),1;let d=await u.client.folder(Ae).catch(()=>null);if(!d)return a(c.info+" The Leftoff folder isn’t in Syncthing; nothing to stop."),0;if(!r.yes){let p=new ft({input:o,output:s});if(!await p.confirm("Stop syncing "+Ee(d.path,t)+"? The files stay; other Macs stop getting updates.",!1).finally(()=>p.close&&p.close()))return a("Nothing changed."),0}let f=await vr(u.client);return a(c.ok+" Stopped syncing. "+Ee(f.path,t)+" and its files are still here; Leftoff keeps using it."),0}return s.write(hf),1}async function qy({argv:n=[],env:e=process.env,out:t=process.stdout,input:s=process.stdin}={}){let o=Wy(n),r=ee,i=f=>{o.json||t.write(f+`
325
- `)};if(o.help)return t.write(hf),0;let a=o.pos.join("")||e.LEFTOFF_JOIN_CODE||"";if(!a)return i("usage: "+J+" join <code> (the code from Settings → Sync → Pair another Mac, or `"+J+" sync pair`, on your first Mac)"),1;let l=f=>{try{ao({kind:"join",...f},e)}catch{}},c=new ft({input:s,output:t,yes:o.yes}),u=async f=>(i(`Syncthing isn’t installed here. Leftoff will run:
326
- `+r.cyan(f)),c.confirm("Install Syncthing?",!0)),d;try{d=await S1(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 "+Ee(d.path,e)+" through Syncthing"+(d.merged?"; this Mac’s sessions were merged in.":".")),o.json&&t.write(JSON.stringify(d)+`
327
+ `+i.dim(" Works once, for 15 minutes. Only the Mac that uses this code is accepted."));let p="",h=new Set,m=async()=>{f||await Lr({client:u.client,env:t,log:()=>{}}).catch(()=>{});let S=Gn(Rt(t)),b=await tn({env:t,root:Z()}),w=S.paired?b.peers.find(v=>v.name===S.paired.name):null,A=S.expired?"The code expired; nothing was accepted. Run "+q+" 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…";A!==p&&(p=A,a(c.info+" "+A));for(let v of b.knocked||[])h.has(v.deviceId)||(h.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: "+q+' sync accept "'+v.name+'"'));return/^Done|expired/.test(A)},y=Date.now()+(r.timeoutMs||16*6e4);for(;Date.now()<y;){if(await m())return/^Done/.test(p)?0:1;await jf(Number(t.LEFTOFF_SYNCTHING_POLL_MS)||1500)}return 1}if(l==="accept"){let u=ft({env:t});if(!u.ok)return a(i.red("✗")+" "+u.reason),1;let d=await Dr(u.client,{env:t}).catch(()=>[]),f=r.pos.slice(1).join(" "),p=f?d.find(h=>h.name===f||Oe(h.deviceId,f)||h.short===f.toUpperCase()):d.length===1?d[0]:null;if(!p){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 h of d)a(" "+q+' sync accept "'+h.name+'" ('+h.short+", "+Uf(h.at)+")")}return 1}if(!r.yes){let h=new yt({input:o,output:s});if(!await h.confirm("Add “"+p.name+"” ("+p.short+") to Syncthing and share the Leftoff folder with it? Only do this if it’s your Mac.",!1).finally(()=>h.close&&h.close()))return a("Nothing changed."),0}try{await Rr(u.client,{deviceId:p.deviceId,env:t})}catch(h){return a(i.red("✗")+" "+h.message),1}return a(c.ok+" Added "+p.name+" and shared the Leftoff folder with it. Its join finishes on its own once the files arrive."),0}if(l==="stop"){let u=ft({env:t});if(!u.ok)return a(i.red("✗")+" "+u.reason),1;let d=await u.client.folder(_e).catch(()=>null);if(!d)return a(c.info+" The Leftoff folder isn’t in Syncthing; nothing to stop."),0;if(!r.yes){let p=new yt({input:o,output:s});if(!await p.confirm("Stop syncing "+Ce(d.path,t)+"? The files stay; other Macs stop getting updates.",!1).finally(()=>p.close&&p.close()))return a("Nothing changed."),0}let f=await Ir(u.client);return a(c.ok+" Stopped syncing. "+Ce(f.path,t)+" and its files are still here; Leftoff keeps using it."),0}return s.write(Ff),1}async function Pw({argv:n=[],env:e=process.env,out:t=process.stdout,input:s=process.stdin}={}){let o=Lw(n),r=ee,i=f=>{o.json||t.write(f+`
328
+ `)};if(o.help)return t.write(Ff),0;let a=o.pos.join("")||e.LEFTOFF_JOIN_CODE||"";if(!a)return i("usage: "+q+" join <code> (the code from Settings → Sync → Pair another Mac, or `"+q+" sync pair`, on your first Mac)"),1;let l=f=>{try{yo({kind:"join",...f},e)}catch{}},c=new yt({input:s,output:t,yes:o.yes}),u=async f=>(i(`Syncthing isn’t installed here. Leftoff will run:
329
+ `+r.cyan(f)),c.confirm("Install Syncthing?",!0)),d;try{d=await ux(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 "+Ce(d.path,e)+" through Syncthing"+(d.merged?"; this Mac’s sessions were merged in.":".")),o.json&&t.write(JSON.stringify(d)+`
327
330
  `),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,p)=>i(" "+(p+1)+". "+f)),o.json&&t.write(JSON.stringify({ok:!1,code:d.code,error:d.error})+`
328
- `),1)}var ff,Zs,hf,yf=z(()=>{ke();$s();$s();lo();In();St();ff=n=>new Promise(e=>setTimeout(e,n)),Zs=(n,{installed:e=!0}={})=>(e?J+" join ":"npx -y github:"+Xe+" join ")+n,hf=`${J} sync: Syncthing for Leftoff, without opening Syncthing
331
+ `),1)}var jf,oo,Ff,Gf=J(()=>{ve();Zs();Zs();wo();Dn();Tt();jf=n=>new Promise(e=>setTimeout(e,n)),oo=(n,{installed:e=!0}={})=>(e?q+" join ":"npx -y github:"+tt+" join ")+n,Ff=`${q} sync: Syncthing for Leftoff, without opening Syncthing
329
332
 
330
- ${J} sync state: running, peers, last sync, folder errors
331
- ${J} sync install install Syncthing with Homebrew (shows the command, asks first)
332
- ${J} sync start start Syncthing if it's installed but not running
333
- ${J} sync setup [PATH] put the Leftoff folder in Syncthing (default ~/Sync/Leftoff)
334
- ${J} sync pair show a code for your other Mac (15 minutes, works once)
335
- ${J} sync stop stop syncing: removes the Leftoff folder from Syncthing, keeps the files
336
- ${J} sync accept [NAME] add a Mac that asked to connect during pairing without the proof (asks first)
337
- ${J} join <code> on the other Mac: set up Syncthing, pair, sync, then join the folder
333
+ ${q} sync state: running, peers, last sync, folder errors
334
+ ${q} sync install install Syncthing with Homebrew (shows the command, asks first)
335
+ ${q} sync start start Syncthing if it's installed but not running
336
+ ${q} sync setup [PATH] put the Leftoff folder in Syncthing (default ~/Sync/Leftoff)
337
+ ${q} sync pair show a code for your other Mac (15 minutes, works once)
338
+ ${q} sync stop stop syncing: removes the Leftoff folder from Syncthing, keeps the files
339
+ ${q} sync accept [NAME] add a Mac that asked to connect during pairing without the proof (asks first)
340
+ ${q} join <code> on the other Mac: set up Syncthing, pair, sync, then join the folder
338
341
 
339
342
  The board does the same from Settings → Sync.
340
- `});import k1 from"node:http";import wf from"node:fs";import ds from"node:path";import tw from"node:crypto";import{fileURLToPath as b1}from"node:url";import{spawn as Vy}from"node:child_process";function v1(n){return rt.state==="running"||(Object.assign(rt,{state:"running",lines:[],error:null,code:null,startedAt:new Date().toISOString(),finishedAt:null,stage:null}),n.log("syncthing: installing with Homebrew ("+yr+"), confirmed on the board"),(async()=>{let e=await Sr({onLine:s=>{rt.lines.push(String(s).slice(0,300)),rt.lines.length>200&&rt.lines.shift()},onStage:s=>{rt.stage=s}});if(!e.ok){Object.assign(rt,{state:"failed",error:e.error,code:e.code,finishedAt:new Date().toISOString()}),n.log("syncthing: install failed: "+e.error);return}if(rt.stage="Starting Syncthing…",!(await ks({timeoutMs:Number(process.env.LEFTOFF_SYNCTHING_START_MS)||9e4})).ok){Object.assign(rt,{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(rt,{state:"done",finishedAt:new Date().toISOString()}),n.log("syncthing: installed and running")})().catch(e=>Object.assign(rt,{state:"failed",error:e.message,finishedAt:new Date().toISOString()}))),rt}function x1(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&&tw.timingSafeEqual(r,i)}function A1(n){try{return decodeURIComponent(String(n||""))||null}catch{return null}}function Xy(n){return n.method==="GET"&&/text\/html/.test(String(n.headers.accept||""))}function Qy(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>'+cn(je)+": "+cn(s)+'</title><meta name="color-scheme" content="light dark"></head><body><main><h1>'+cn(s)+"</h1>"+o.map(i=>"<p>"+i+"</p>").join("")+"<p><small>"+cn(je)+" "+cn(q)+" · "+cn(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 L(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 an(n,e,t,s){L(n,e,{error:{code:t,message:s}})}function se(n){return new Promise((e,t)=>{let s=0,o=[];n.on("data",r=>{if(s+=r.length,s>T1){t(I(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(I(400,"bad_json","body is not valid JSON"))}}),n.on("error",t)})}function C1(n,e){let t=Buffer.from(String(n||"")),s=Buffer.from(String(e||""));return t.length===s.length&&tw.timingSafeEqual(t,s)}function Ac(n,e){let t=String(n.headers.cookie||"").match(new RegExp("(?:^|;\\s*)"+e+"=([^;]+)"));return t?decodeURIComponent(t[1]):null}function ln(n){return O1.some(e=>n.headers[e]!==void 0)}function Ui({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 nw(n,{root:e,port:t,host:s="127.0.0.1",goLimit:o,answerLimit:r}={}){e=e||n.root;let i=Se(e),a=new Set,l=Ui(o||{}),c=Ui({burst:2,everyMs:15e3}),u=Ui({burst:2,everyMs:1e4}),d=Ui({burst:6,everyMs:5e3}),f=new Map,p={take:b=>(f.has(b)||(f.size>500&&f.clear(),f.set(b,Ui({burst:5,everyMs:12e3}))),f.get(b).take())},h=b=>String(b.headers["x-forwarded-for"]||b.socket.remoteAddress||"").split(",")[0].trim(),m=hm(r||(process.env.CHECKPOINTS_ANSWER_LIMIT?{max:Number(process.env.CHECKPOINTS_ANSWER_LIMIT)}:{})),y=[],w=0,S=new Set,k=(b,g)=>{let _="event: "+b+`
343
+ `});import dx from"node:http";import Kf from"node:fs";import gs from"node:path";import Gw from"node:crypto";import{fileURLToPath as fx}from"node:url";import{spawn as jw}from"node:child_process";function hx(n){return ct.state==="running"||(Object.assign(ct,{state:"running",lines:[],error:null,code:null,startedAt:new Date().toISOString(),finishedAt:null,stage:null}),n.log("syncthing: installing with Homebrew ("+_r+"), confirmed on the board"),(async()=>{let e=await Or({onLine:s=>{ct.lines.push(String(s).slice(0,300)),ct.lines.length>200&&ct.lines.shift()},onStage:s=>{ct.stage=s}});if(!e.ok){Object.assign(ct,{state:"failed",error:e.error,code:e.code,finishedAt:new Date().toISOString()}),n.log("syncthing: install failed: "+e.error);return}if(ct.stage="Starting Syncthing…",!(await Ts({timeoutMs:Number(process.env.LEFTOFF_SYNCTHING_START_MS)||9e4})).ok){Object.assign(ct,{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(ct,{state:"done",finishedAt:new Date().toISOString()}),n.log("syncthing: installed and running")})().catch(e=>Object.assign(ct,{state:"failed",error:e.message,finishedAt:new Date().toISOString()}))),ct}function px(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&&Gw.timingSafeEqual(r,i)}function mx(n){try{return decodeURIComponent(String(n||""))||null}catch{return null}}function Uw(n){return n.method==="GET"&&/text\/html/.test(String(n.headers.accept||""))}function Hw(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>'+fn(He)+": "+fn(s)+'</title><meta name="color-scheme" content="light dark"></head><body><main><h1>'+fn(s)+"</h1>"+o.map(i=>"<p>"+i+"</p>").join("")+"<p><small>"+fn(He)+" "+fn(V)+" · "+fn(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 dn(n,e,t,s){D(n,e,{error:{code:t,message:s}})}function se(n){return new Promise((e,t)=>{let s=0,o=[];n.on("data",r=>{if(s+=r.length,s>gx){t(N(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(N(400,"bad_json","body is not valid JSON"))}}),n.on("error",t)})}function Sx(n,e){let t=Buffer.from(String(n||"")),s=Buffer.from(String(e||""));return t.length===s.length&&Gw.timingSafeEqual(t,s)}function Bc(n,e){let t=String(n.headers.cookie||"").match(new RegExp("(?:^|;\\s*)"+e+"=([^;]+)"));return t?decodeURIComponent(t[1]):null}function lt(n){return kx.some(e=>n.headers[e]!==void 0)}function qi({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 Kw(n,{root:e,port:t,host:s="127.0.0.1",goLimit:o,answerLimit:r}={}){e=e||n.root;let i=be(e),a=new Set,l=qi(o||{}),c=qi({burst:2,everyMs:15e3}),u=qi({burst:2,everyMs:1e4}),d=qi({burst:6,everyMs:5e3}),f=new Map,p={take:k=>(f.has(k)||(f.size>500&&f.clear(),f.set(k,qi({burst:5,everyMs:12e3}))),f.get(k).take())},h=k=>String(k.headers["x-forwarded-for"]||k.socket.remoteAddress||"").split(",")[0].trim(),m=tg(r||(process.env.CHECKPOINTS_ANSWER_LIMIT?{max:Number(process.env.CHECKPOINTS_ANSWER_LIMIT)}:{})),y=[],S=0,b=new Set,w=(k,g)=>{let _="event: "+k+`
341
344
  data: `+JSON.stringify(g)+`
342
345
 
343
- `;for(let x of a)x.write(_);y.push({seq:++w,event:b,data:g}),y.length>200&&y.shift();for(let x of S)x()};n.on("session.updated",b=>k("session.updated",b)),n.on("checkpoint.created",b=>k("checkpoint.created",b)),n.on("digest.created",b=>k("digest.created",b)),n.on("spaces.updated",b=>k("spaces.updated",b)),n.on("catchup.created",b=>k("catchup.created",b)),n.on("lists.updated",b=>k("lists.updated",b)),n.on("model.pull",b=>k("model.pull",b)),n.on("needs.updated",b=>k("needs.updated",b)),n.on("restore.updated",b=>k("restore.updated",b)),n.on("peers.updated",b=>k("peers.updated",b)),n.on("agents.updated",b=>k("agents.updated",b)),n.on("relay.updated",b=>k("relay.updated",b));let v=setInterval(()=>{for(let b of a)b.write(`: ping
346
+ `;for(let x of a)x.write(_);y.push({seq:++S,event:k,data:g}),y.length>200&&y.shift();for(let x of b)x()};n.on("session.updated",k=>w("session.updated",k)),n.on("prs.updated",k=>w("prs.updated",k)),n.on("checkpoint.created",k=>w("checkpoint.created",k)),n.on("digest.created",k=>w("digest.created",k)),n.on("spaces.updated",k=>w("spaces.updated",k)),n.on("catchup.created",k=>w("catchup.created",k)),n.on("lists.updated",k=>w("lists.updated",k)),n.on("model.pull",k=>w("model.pull",k)),n.on("needs.updated",k=>w("needs.updated",k)),n.on("restore.updated",k=>w("restore.updated",k)),n.on("peers.updated",k=>w("peers.updated",k)),n.on("agents.updated",k=>w("agents.updated",k)),n.on("relay.updated",k=>w("relay.updated",k));let A=setInterval(()=>{for(let k of a)k.write(`: ping
344
347
 
345
- `)},25e3);v.unref();let T=k1.createServer(async(b,g)=>{try{let _=n.cfg&&n.cfg.remoteHosts||[],x=oi(b.headers.host,t,_),C=x1(b,n);C&&(x="remote");let E=b.headers["x-forwarded-host"]?String(b.headers["x-forwarded-host"]).split(",")[0].trim():"";if(x==="loopback"&&E&&oi(E,t,_)==="remote"&&(x="remote"),!x)return Xy(b)?Qy(g,403,"bad_host","This address isn’t on "+je+"’s list",[cn(je)+" only answers to its own names, so other sites can’t reach it through your browser. <code>"+cn(String(b.headers.host||"(none)"))+"</code> isn’t one of them.","On the machine running "+cn(je)+", run <code>"+J+" tailscale on</code>: it adds this machine’s tailnet name to the list and prints a QR code that signs your phone in."]):an(g,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 Xy(b)?Qy(g,403,"funnel_refused","Not on the public internet",[cn(je)+" is not served over Tailscale Funnel. Use your tailnet: <code>"+J+" tailscale on</code> (tailscale serve)."]):an(g,403,"funnel_refused","Leftoff is not served over Tailscale Funnel; use tailscale serve (tailnet only)");let D=b.headers.origin;if(!Um(D,t,_))return an(g,403,"bad_origin","cross-origin requests are refused");if(x==="loopback"&&ln(b))return an(g,403,"bad_host","a proxied request must use a name in config remoteHosts, not a loopback Host");let O=x==="remote",F=O&&!C&&n.cfg.remoteRequireToken!==!1,$=new URL(b.url,"http://"+b.headers.host),N=$.pathname;if(b.method==="GET"&&!N.startsWith("/v1/")&&!C){let A=N==="/"||N==="/index.html"?"index.html":N.slice(1),M=ds.join(Zy,ds.normalize(A).replace(/^(\.\.[/\\])+/,""));if(!M.startsWith(Zy)||!wf.existsSync(M)||wf.statSync(M).isDirectory())return an(g,404,"not_found","no such page");let R={"content-type":_1[ds.extname(M)]||"application/octet-stream","cache-control":"no-cache","x-content-type-options":"nosniff","x-frame-options":"DENY","content-security-policy":E1};if(R["referrer-policy"]="no-referrer",A==="index.html")if(!F)R["set-cookie"]="cp_token="+encodeURIComponent(i)+"; HttpOnly; SameSite=Strict; Path=/"+(O?"; Secure":"");else if($.searchParams.has("t")){let H=b.headers["tailscale-user-login"]||null,Q=Km(e,$.searchParams.get("t"),{userAgent:b.headers["user-agent"],who:H});return Q?(n.log("remote login: new browser session "+Q.id+" (link)"+(H?" ("+H+")":"")+" via "+b.headers.host),g.writeHead(302,{location:"/","cache-control":"no-store","referrer-policy":"no-referrer","set-cookie":yl(Q.secret)}),g.end()):(g.writeHead(302,{location:"/?login=expired","cache-control":"no-store","referrer-policy":"no-referrer"}),g.end())}else{let H=Ac(b,ii);H&&gl(e,H,{touch:!0})&&(R["set-cookie"]=yl(H))}return A==="sw.js"&&(R["service-worker-allowed"]="/"),g.writeHead(200,R),wf.createReadStream(M).pipe(g)}if(b.method==="GET"&&N==="/v1/remote/ping"){let A=!F||O&&!!gl(e,Ac(b,ii));return L(g,200,{ok:!0,app:je,version:q,via:x,proxied:ln(b),loginRequired:F,signedIn:A})}if(b.method==="POST"&&N==="/v1/remote/signin"){if(!O)throw I(400,"not_remote","sign-in codes are for other devices; this machine needs none");if(!F)return L(g,200,{ok:!0,needed:!1});if(!/application\/json/.test(String(b.headers["content-type"]||"")))throw I(415,"json_only","send JSON");if(!p.take(h(b)))throw I(429,"slow_down","Too many tries. Wait a minute, then try again.");let A=await se(b),M=b.headers["tailscale-user-login"]||null,R=Gm(e,A.code,{userAgent:b.headers["user-agent"],who:M});if(!R.ok)throw n.log("remote login: wrong sign-in code via "+b.headers.host+(R.reason==="locked"?" (too many: pending codes voided)":"")),R.reason==="locked"?I(429,"locked","Too many wrong codes, so every pending code was cancelled. Get a new one: "+J+" link, on the machine running "+je+"."):I(401,"bad_code","That code isn’t right, or it was already used or has expired. Codes work once, for "+Hs/6e4+" minutes.");return n.log("remote login: new browser session "+R.id+" (code)"+(M?" ("+M+")":"")+" via "+b.headers.host),L(g,200,{ok:!0},{"set-cookie":yl(R.secret)})}let j=String(b.headers.authorization||""),W=j.startsWith("Bearer ")?j.slice(7).trim():Ac(b,"cp_token");if(!(C||i&&C1(W,i)||O&&!!gl(e,Ac(b,ii))))return F?an(g,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"):an(g,401,"unauthorized","missing or wrong token (see ~/checkpoints/.secret/token)");let Y=()=>{if(O||ln(b))throw I(403,"loopback_only","only from the machine running Leftoff")},Z=Object.fromEntries($.searchParams),te=A=>N.match(A),B,Ct=C?A1(b.headers["x-leftoff-device-name"]):null,$e=()=>({via:O?"remote":"loopback",proxied:ln(b),ip:C?null:b.socket.remoteAddress||null,host:C?"relay":b.headers.host||null,who:C?(Ct||"a paired phone")+" (through the relay)":b.headers["tailscale-user-login"]||null,userAgent:b.headers["user-agent"]||null,relay:C||void 0}),zt=()=>{if(!m.take(C?"relay:"+b.headers["x-leftoff-device"]:O?"remote:"+(b.headers["x-forwarded-for"]||b.socket.remoteAddress):"local"))throw I(429,"rate_limited","Too many answers in a minute; wait a moment.")},He=()=>{let A=new AbortController;return g.on("close",()=>{g.writableEnded||A.abort()}),A.signal};if(b.method==="GET"&&N==="/v1/needs")return L(g,200,{items:n.needsList(),recent:n.recentAnswered(Math.min(50,Number(Z.recent)||20)),answering:n.answeringInfo(O?"remote":"loopback"),dismissed:n.dismissedNeeds()});if(b.method==="POST"&&N==="/v1/needs/dismiss"){let A=await se(b);if(typeof A.key!="string"||!/^[0-9a-f]{8,64}$/.test(A.key))throw I(400,"bad_request","key is required");return L(g,200,n.dismissNeed(A.key))}if(b.method==="POST"&&N==="/v1/needs/restore"){let A=await se(b),M=A.all===!0?n.dismissedNeeds().map(R=>R.key):(Array.isArray(A.keys)?A.keys:[A.key]).filter(R=>typeof R=="string"&&/^[0-9a-f]{8,64}$/.test(R)).slice(0,500);if(!M.length&&A.all!==!0)throw I(400,"bad_request","key or keys is required");return L(g,200,n.restoreNeeds(M))}if(b.method==="POST"&&N==="/v1/asks")return Y(),L(g,200,n.openAsk(await se(b)));if(b.method==="POST"&&N==="/v1/asks/settle"){Y();let A=await se(b);if(!A.sessionId||!A.tool)throw I(400,"bad_settle","sessionId and tool are required");let M=n.asks.settleSession(A.sessionId,String(A.reason||"answered_in_terminal"),A.all?{}:{key:Zr(A.tool,A.input)});return L(g,200,{ok:!0,settled:M.length})}if(b.method==="GET"&&(B=te(/^\/v1\/asks\/([\w-]+)\/wait$/))){Y();let A=await n.asks.wait(B[1],Number(Z.ms)||2e4,He());return A===null?void 0:L(g,200,A)}if(b.method==="POST"&&(B=te(/^\/v1\/asks\/([\w-]+)\/cancel$/))){Y();let A=n.asks.settle(B[1],String((await se(b)).reason||"cancelled_in_terminal"));return L(g,200,A?{ok:!0,state:A.state}:{ok:!1,state:"gone"})}if(b.method==="GET"&&(B=te(/^\/v1\/asks\/([\w-]+)$/))){let A=n.asks.get(B[1]);if(!A)throw I(404,"not_found","no such request");return L(g,200,n.asks.publicAsk(A))}if(b.method==="POST"&&(B=te(/^\/v1\/asks\/([\w-]+)\/answer$/)))return n.assertMayAnswer($e()),zt(),L(g,200,await n.answerAsk(B[1],await se(b),$e()));if(b.method==="POST"&&(B=te(/^\/v1\/sessions\/([^/]+)\/reply$/)))return n.assertMayAnswer($e()),zt(),L(g,200,await n.postReply(decodeURIComponent(B[1]),(await se(b)).text,$e()));if(b.method==="GET"&&(B=te(/^\/v1\/sessions\/([^/]+)\/replies\/next$/))){Y();let A=He(),M=await n.takeReply(decodeURIComponent(B[1]),Number(Z.ms)||2e4,A);return A.aborted?void 0:L(g,200,M)}if(b.method==="GET"&&N==="/v1/sync"){let A=Wo({current:e});return L(g,200,{...A,machine:n.machine,backups:Jo(e),lastMove:Gs(),join:qo(e,{cli:J}),viewer:{local:!O&&!ln(b)}})}if(b.method==="POST"&&N==="/v1/sync/plan"){Y();let A=await se(b);if(!A.to)throw I(400,"bad_request","to: the folder to move to");let M=zo(e,ds.resolve(ew(String(A.to))),{machine:n.machine,identity:n.peers?n.peers.id:null});A.force&&(M.collisions=M.collisions.filter(Q=>Q.code!=="machine_name_maybe_taken"),M.ok=!M.errors.length&&!M.collisions.length);let{files:R,...H}=M;return L(g,200,{...H,toDisplay:ge(M.to),backupDisplay:ge(M.backup),sample:R.filter(Q=>Q.action!=="same").slice(0,12),join:qo(M.to,{cli:J})})}if(b.method==="POST"&&N==="/v1/sync/move"){Y();let A=await se(b);if(!A.to)throw I(400,"bad_request","to: the folder to move to");let M=ds.resolve(ew(String(A.to))),R=zo(e,M,{machine:n.machine,identity:n.peers?n.peers.id:null}),H=R.collisions.filter(pe=>!(A.force&&pe.code==="machine_name_maybe_taken"));if(R.errors.length||H.length)throw I(409,(R.errors[0]||H[0]).code,(R.errors[0]||H[0]).message);let Q=[Fe,"move",M,"--yes","--json","--daemon-pid",String(process.pid)];A.force&&Q.push("--force");let he=Vy(process.execPath,Q,{detached:!0,stdio:"ignore",env:{...process.env}});return he.unref(),n.log("sync: moving the folder to "+M+" (leftoff move, pid "+he.pid+")"),L(g,202,{started:!0,to:M,statusFile:bi()})}if(b.method==="POST"&&N==="/v1/sync/backup/delete"){Y();let A=await se(b);if(A.confirm!==!0)throw I(400,"confirm","send { path, confirm: true } to delete a backup");let M=Jo(e).find(R=>R.path===A.path);if(!M)throw I(404,"not_found","no such Leftoff backup next to this folder");return L(g,200,vi(M.path))}if(b.method==="GET"&&N==="/v1/syncthing"){let A=!O&&!ln(b),M=await Vt({root:e}),R=nu(),H={...M,myID:void 0,pairing:Fn(It()),run:R&&Date.now()-new Date(R.at).getTime()<6*36e5?R:null,viewer:{local:A}};return A?H.install={...Ss(),job:{...rt,lines:rt.lines.slice(-60)}}:(H.folder&&(H.folder={...H.folder,path:void 0}),H.knocked=(H.knocked||[]).map(Q=>({...Q,deviceId:void 0}))),L(g,200,H)}if(b.method==="POST"&&N==="/v1/syncthing/install"){if(Y(),(await se(b)).confirm!==!0)throw I(400,"confirm","send { confirm: true } after showing the command: "+yr);if(mn().installed)return L(g,200,{already:!0});if(!Ss().brew)throw I(409,"no_brew","Homebrew isn’t installed on this Mac");return L(g,202,{started:!0,job:v1(n)})}if(b.method==="POST"&&N==="/v1/syncthing/start"){Y();let A=kr();if(!A.ok)throw I(409,A.code||"start_failed",A.error);let M=await ks({timeoutMs:2e4});if(!M.ok)throw I(504,"not_running",M.error);return n.log("syncthing: started ("+A.how+")"),L(g,200,{ok:!0})}if(b.method==="POST"&&(N==="/v1/syncthing/setup"||N==="/v1/syncthing/join")){Y();let A=await se(b),M=N.endsWith("/join");if(M&&!A.code)throw I(400,"bad_request","code: the pairing code from your other Mac");if(M)try{(await Promise.resolve().then(()=>(lo(),Fh))).decodePairCode(String(A.code))}catch(Q){throw I(400,Q.code||"bad_code",Q.message)}let R=[Fe,M?"join":"sync",...M?[]:["setup"],"--yes","--json","--daemon-pid",String(process.pid),...A.path?["--path",String(A.path)]:[]];ao({kind:M?"join":"setup",state:"starting",message:"Starting…"});let H=Vy(process.execPath,R,{detached:!0,stdio:"ignore",env:{...process.env,...M?{LEFTOFF_JOIN_CODE:String(A.code)}:{}}});return H.unref(),n.log("syncthing: "+(M?"joining another Mac by code":"putting the folder in Syncthing")+" (pid "+H.pid+")"),L(g,202,{started:!0})}if(b.method==="POST"&&N==="/v1/syncthing/pair"){Y();let A=await Vt({root:e});if(!A.running)throw I(409,"not_running","Syncthing isn’t running");if(!A.folder)throw I(409,"no_folder","The Leftoff folder isn’t in Syncthing yet");let M=await se(b),R=xr({deviceId:A.myID,machine:n.machine,reuse:M.fresh!==!0});return n.log("syncthing: pairing code made; accepting the matching Mac until "+new Date(R.expiresAt).toISOString()),L(g,200,{code:R.code,command:Zs(R.code),fresh:Zs(R.code,{installed:!1}),expiresAt:new Date(R.expiresAt).toISOString()},{"cache-control":"no-store"})}if(b.method==="POST"&&N==="/v1/syncthing/pair/cancel")return Y(),au(),n.log("syncthing: pairing code cancelled"),L(g,200,{ok:!0});if(b.method==="POST"&&N==="/v1/syncthing/accept"){Y();let A=await se(b);if(A.confirm!==!0||!A.deviceId)throw I(400,"confirm","send { deviceId, confirm: true } after the person clicked Accept");let M=at();if(!M.ok)throw I(409,M.code||"no_syncthing",M.reason);let R;try{R=await _r(M.client,{deviceId:String(A.deviceId),log:H=>n.log(H)})}catch(H){throw I(H.code==="not_knocked"?404:409,H.code||"accept_failed",H.message)}return L(g,200,R)}if(b.method==="POST"&&N==="/v1/syncthing/stop"){if(Y(),(await se(b)).confirm!==!0)throw I(400,"confirm","send { confirm: true } to stop syncing");let M=at();if(!M.ok)throw I(409,M.code||"no_syncthing",M.reason);let R=await vr(M.client);return n.log("syncthing: stopped syncing the Leftoff folder (files kept)"),L(g,200,R)}if(b.method==="GET"&&N==="/v1/agents"){let A=!O&&!ln(b);return L(g,200,{...n.agentsOverview({local:A}),viewer:{local:A}})}if(b.method==="POST"&&N==="/v1/agents")return Y(),L(g,200,await n.addAgent(await se(b)));if(b.method==="POST"&&(B=te(/^\/v1\/agents\/([a-z0-9-]+)\/refresh$/))){if(!d.take())throw I(429,"slow_down","too many refreshes; wait a moment");return L(g,200,await n.pullAgent(B[1],{force:!0}))}if(b.method==="DELETE"&&(B=te(/^\/v1\/agents\/([a-z0-9-]+)$/)))return Y(),L(g,200,n.removeAgent(B[1],{purge:Z.purge==="1"}));if(b.method==="GET"&&N==="/v1/peers")return L(g,200,{...n.peersOverview(),viewer:{local:!O&&!ln(b)}});if(b.method==="POST"&&N==="/v1/peers/allow"){if(Y(),!n.peers)throw I(409,"peers_off","cross-machine actions are off on this machine (LEFTOFF_PEERS=0 or peers.enabled false)");return L(g,200,await n.peers.setAllowIncoming((await se(b)).allow===!0))}if(b.method==="POST"&&(B=te(/^\/v1\/peers\/([\w.-]+)\/trust$/))){if(Y(),!n.peers)throw I(409,"peers_off","cross-machine actions are off on this machine");return L(g,200,n.peers.trust(decodeURIComponent(B[1])))}if(b.method==="POST"&&N==="/v1/peers/check")return n.peers&&await n.peers.poll(),L(g,200,n.peersOverview());if(b.method==="GET"&&N==="/v1/health")return L(g,200,{...n.health(),answering:n.answeringInfo(O?"remote":"loopback")});if(b.method==="POST"&&N==="/v1/mod/health")return L(g,200,n.recordModHealth(await se(b)));if(b.method==="GET"&&N==="/v1/changelog")return L(g,200,n.changelog());if(b.method==="POST"&&N==="/v1/changelog/seen")return L(g,200,n.markChangelogSeen((await se(b)).version));if(b.method==="POST"&&N==="/v1/changelog/dismiss")return L(g,200,n.dismissUpgradeNotice());if(b.method==="GET"&&N==="/v1/updates")return L(g,200,n.updateState());if(b.method==="POST"&&N==="/v1/updates/check")return L(g,200,await n.runUpdateCheck({force:!0}));if(b.method==="POST"&&N==="/v1/events"){let A=await se(b);if(!Array.isArray(A.events))throw I(400,"bad_request","body must be { events: Event[] }");return L(g,200,n.ingest(A.events))}if(b.method==="POST"&&(B=te(/^\/v1\/hooks\/([A-Za-z]+)$/))){let A=await se(b);return L(g,200,n.ingestHook(B[1],A))}if(b.method==="POST"&&(B=te(/^\/v1\/sessions\/([^/]+)\/checkpoints$/))){let A=decodeURIComponent(B[1]),M=await se(b);if(M.generate){let R=await n.generateCheckpoint(A,M.trigger||"manual",{userNote:M.userNote,length:M.length||void 0});return L(g,201,R)}return L(g,201,n.addCheckpoint(A,M))}if(b.method==="POST"&&N==="/v1/checkpoints/preview"){let A=await se(b);if(!A.sessionId)throw I(400,"bad_request","sessionId is required");return L(g,200,await n.generateCheckpoint(String(A.sessionId),"manual",{length:A.length||void 0,save:!1}))}if(b.method==="GET"&&N==="/v1/sessions")return L(g,200,{sessions:n.listSessions({...Z,includeArchived:Z.archived==="1"}),viewer:{local:!O,mayAnswer:n.answeringInfo(O?"remote":"loopback").allowedHere}});if(b.method==="GET"&&N==="/v1/gh"){let A=await dp({fresh:Z.fresh==="1"});return A.status==="ready"&&n.prs&&(n.prs.offUntil=0),L(g,200,A)}if(b.method==="GET"&&(B=te(/^\/v1\/sessions\/([^/]+)\/turns$/)))return L(g,200,await n.sessionTurns(decodeURIComponent(B[1]),{limit:Z.limit}));if(b.method==="GET"&&N==="/v1/restore")return L(g,200,{...n.restoreCandidates({since:Z.since,repo:Z.repo}),viewer:{local:!O&&!ln(b)}});if(b.method==="POST"&&N==="/v1/restore"){Y();let A=await se(b);if(!Array.isArray(A.ids))throw I(400,"bad_request","ids: the session ids to restore");if(A.print!==!0&&!u.take())throw I(429,"slow_down","too many restore requests; wait a moment");return L(g,200,await n.restore(A.ids,{print:A.print===!0}))}if(b.method==="POST"&&N==="/v1/restore/launched"){Y();let A=await se(b),M=n.restoreTargets(Array.isArray(A.ids)?A.ids:[]).map(R=>R.id);return n.markRestored(M),L(g,200,{ok:!0,marked:M.length})}if(b.method==="POST"&&(B=te(/^\/v1\/sessions\/([^/]+)\/go$/))){if(Y(),!l.take())throw I(429,"slow_down","too many Go requests; wait a moment");return L(g,200,await n.jump(decodeURIComponent(B[1])))}if(b.method==="GET"&&(B=te(/^\/v1\/sessions\/([^/]+)\/where$/)))return Y(),L(g,200,n.where(decodeURIComponent(B[1])));if(b.method==="GET"&&N==="/v1/jump/speed")return Y(),L(g,200,await n.jumpSpeed());if(b.method==="GET"&&N==="/v1/jump/check")return Y(),L(g,200,await n.jumpCheck(Z.kind||"ghostty"));if(b.method==="GET"&&N==="/v1/lists")return L(g,200,{...n.listsOverview({all:Z.all==="1",repo:Z.repo||null}),mayEdit:n.answeringInfo(O?"remote":"loopback").allowedHere});if(b.method==="GET"&&N==="/v1/lists/resolve")return L(g,200,{list:n.getList(Z.ref)});if(b.method==="POST"&&N==="/v1/lists")return L(g,200,n.saveList(await se(b),$e()));if(b.method==="GET"&&(B=te(/^\/v1\/lists\/(~?[\w.-]+\/[\w.-]+)$/)))return L(g,200,{list:n.getList(B[1]),mayEdit:n.answeringInfo(O?"remote":"loopback").allowedHere});if(b.method==="POST"&&(B=te(/^\/v1\/lists\/(~?[\w.-]+\/[\w.-]+)\/change$/)))return L(g,200,n.changeList(B[1],await se(b),$e()));if(b.method==="GET"&&(B=te(/^\/v1\/sessions\/([^/]+)\/lists$/)))return L(g,200,n.sessionLists(decodeURIComponent(B[1])));if(b.method==="GET"&&(B=te(/^\/v1\/sessions\/([^/]+)\/lists\/find$/)))return L(g,200,n.findListsInSession(decodeURIComponent(B[1])));if(b.method==="POST"&&(B=te(/^\/v1\/sessions\/([^/]+)\/list-hint\/dismiss$/)))return n.assertMayEditLists($e()),L(g,200,n.dismissListHint(decodeURIComponent(B[1])));if(b.method==="GET"&&(B=te(/^\/v1\/sessions\/([^/]+)$/)))return L(g,200,n.getSession(decodeURIComponent(B[1])));if(b.method==="PATCH"&&(B=te(/^\/v1\/sessions\/([^/]+)$/)))return L(g,200,n.patchSession(decodeURIComponent(B[1]),await se(b)));if(b.method==="GET"&&(B=te(/^\/v1\/sessions\/([^/]+)\/checkpoints$/)))return L(g,200,{checkpoints:n.sessionCheckpoints(decodeURIComponent(B[1]),Z)});if(b.method==="GET"&&N==="/v1/checkpoints")return L(g,200,{checkpoints:n.timeline(Z)});if(b.method==="GET"&&N==="/v1/search")return L(g,200,n.search(Z.q));if(b.method==="GET"&&N==="/v1/catchups")return L(g,200,{catchups:n.folder.listCatchups(),lastAt:(()=>{let A=n.folder.lastCatchupAt();return A==null?null:new Date(A).toISOString()})()});if(b.method==="GET"&&N==="/v1/catchups/options")return L(g,200,n.catchupOptions({since:Z.since,to:Z.to}));if(b.method==="POST"&&N==="/v1/catchups/prepare"){let A=await se(b),M=await n.makeCatchup({...A,save:!1,useModel:!1}),{catchupLength:R,CATCHUP_SYSTEM:H,summaryPrompt:Q}=await Promise.resolve().then(()=>(Ha(),Fp)),he=R(n.cfg,M.recap.length);return L(g,200,{window:M.recap.window,sessions:M.recap.sessions.length,needsYou:M.recap.needsYou.length,prompt:M.recap.sessions.length?{system:H.replace("{n}",String(he.bullets)),prompt:Q(M.recap,{maxChars:n.cfg.summarizer&&n.cfg.summarizer.maxInputChars||24e3}),maxTokens:he.maxTokens}:null})}if(b.method==="POST"&&N==="/v1/catchups"){let A=await se(b);return L(g,200,await n.makeCatchup(A))}if(b.method==="GET"&&(B=te(/^\/v1\/catchups\/(\d{4}-\d{2}-\d{2}T\d{4}(?:-\d+)?)$/))){let A=n.readCatchup(B[1]);if(!A)throw I(404,"not_found","no catch-up named "+B[1]);return L(g,200,A)}if(b.method==="GET"&&N==="/v1/digests")return L(g,200,{digests:n.folder.listDigests()});if(b.method==="GET"&&(B=te(/^\/v1\/digests\/(\d{4}-\d{2}-\d{2})$/))){let A=n.folder.readDigest(B[1],Z.machine||null);if(!A)throw I(404,"not_found","no digest for "+B[1]);let M=n.folder.listDigests().filter(R=>R.date===B[1]&&!(R.shared&&A.shared)&&R.machine!==A.machine);return L(g,200,{meta:Be(A.markdown).data,markdown:A.markdown,machine:A.machine,shared:!!A.shared,by:A.by||null,duplicates:A.duplicates||0,others:M})}if(b.method==="GET"&&N==="/v1/spaces")return L(g,200,n.spacesView());if(b.method==="POST"&&N==="/v1/spaces")return L(g,200,n.setSpace(await se(b)));if(b.method==="GET"&&N==="/v1/standup")return L(g,200,n.standup({space:Z.space||null,date:Z.date||null}));if(b.method==="POST"&&N==="/v1/digests"){let A=await se(b),M=await n.makeDigest({date:A.date});return L(g,201,M)}if(b.method==="GET"&&N==="/v1/models")return L(g,200,await n.modelChoices({provider:Z.provider,baseUrl:Z.baseUrl}));if(b.method==="POST"&&N==="/v1/models/ollama/start"){if(Y(),!c.take())throw I(429,"slow_down","Starting Ollama already; wait a moment");return L(g,200,await n.startOllama())}if(b.method==="GET"&&N==="/v1/models/pulls")return L(g,200,{pulls:n.pulls.list()});if(b.method==="POST"&&N==="/v1/models/pull"){Y();let A=await se(b);return L(g,202,n.startPull({tag:A.tag,use:!!A.use}))}if(b.method==="DELETE"&&(B=te(/^\/v1\/models\/pull\/(.+)$/))){Y();let A=n.pulls.cancel(decodeURIComponent(B[1]));if(!A)throw I(404,"not_found","no download of that model");return L(g,200,A)}if(b.method==="GET"&&N==="/v1/remote")return L(g,200,n.remoteState(x));if(b.method==="POST"&&N==="/v1/remote/hosts")return Y(),L(g,200,n.setRemoteHosts(await se(b)));if(b.method==="POST"&&N==="/v1/remote/reload")return Y(),L(g,200,n.reloadRemote());if(b.method==="POST"&&N==="/v1/remote/revoke")return Y(),L(g,200,{revoked:li(e),...n.remoteState(x)});if(b.method==="POST"&&N==="/v1/remote/link"){let A=pl(n.cfg.remoteHosts);if(!A.length)throw I(409,"remote_off","remote access is off: run leftoff tailscale on");if(n.cfg.remoteRequireToken===!1)return L(g,200,{needed:!1,urls:A});if(O&&!p.take("mint:"+h(b)))throw I(429,"slow_down","Too many codes in a row; wait a minute.");let M=ai(e,{codeOnly:O,note:O?"from a signed-in remote browser":null}),R={needed:!0,base:A[0],code:ml(M.signInCode),expiresAt:M.expiresAt,ttlMin:Hs/6e4};return M.code&&Object.assign(R,{urls:A.map(H=>H+"?t="+M.code),url:A[0]+"?t="+M.code,qrSvg:zl(A[0]+"?t="+M.code)}),L(g,200,R)}if(N==="/v1/relay"||N.startsWith("/v1/relay/")){let A=n.relayHost;if(!A){if(b.method==="GET"&&N==="/v1/relay")return L(g,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"&&N==="/v1/relay/devices")return L(g,200,{devices:[]});throw I(409,"relay_unavailable","the relay isn’t available in this process")}if(b.method==="GET"&&N==="/v1/relay")return await A.refreshRelayDevices().catch(()=>{}),L(g,200,{...A.status(),list:A.devices(),viewer:{relay:C,device:C?b.headers["x-leftoff-device"]:null,local:!O&&!ln(b)}});if(b.method==="POST"&&N==="/v1/relay/enable")return Y(),L(g,200,await A.enable(await se(b)));if(b.method==="POST"&&N==="/v1/relay/signup")return Y(),L(g,200,await A.signupStart(await se(b)));if(b.method==="GET"&&N==="/v1/relay/signup")return Y(),L(g,200,await A.signupCheck());if(b.method==="POST"&&N==="/v1/relay/signup/cancel")return Y(),L(g,200,A.signupCancel());if(b.method==="POST"&&N==="/v1/relay/disable")return Y(),L(g,200,await A.disable());if(b.method==="POST"&&N==="/v1/relay/pair")return Y(),L(g,200,await A.createPair());if(b.method==="POST"&&N==="/v1/relay/pair/cancel")return Y(),await A.cancelPairs(),L(g,200,{ok:!0});if(b.method==="GET"&&N==="/v1/relay/devices")return await A.refreshRelayDevices().catch(()=>{}),L(g,200,{devices:A.devices()});if(b.method==="POST"&&(B=te(/^\/v1\/relay\/devices\/([\w-]+)\/revoke$/)))return Y(),L(g,200,await A.revoke(B[1]));throw I(404,"not_found","no route for "+b.method+" "+N)}if(b.method==="GET"&&N==="/v1/events"){let A=Number(Z.after),M=()=>y.filter(H=>H.seq>A);if(!Number.isFinite(A)||A<0||A>w)return L(g,200,{seq:w,events:[],reset:Number.isFinite(A)&&A>w});let R=M();if(!R.length){let H=He();if(await new Promise(Q=>{let he=setTimeout(pe,Math.min(25e3,Number(Z.ms)||2e4));function pe(){clearTimeout(he),S.delete(pe),H.removeEventListener("abort",pe),Q()}S.add(pe),H.addEventListener("abort",pe)}),H.aborted)return;R=M()}return n.boardOpened&&R.length,L(g,200,{seq:w,events:R})}if(b.method==="GET"&&N==="/v1/config")return L(g,200,n.publicConfig());if(b.method==="PATCH"&&N==="/v1/config")return L(g,200,n.updateConfig(await se(b),{via:O?"remote":"loopback"}));if(b.method==="PUT"&&(B=te(/^\/v1\/config\/keys\/([a-z-]+)$/)))return L(g,200,n.saveKey(B[1],(await se(b)).key));if(b.method==="GET"&&N==="/v1/stream"){g.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-store",connection:"keep-alive"}),g.write(`retry: 3000
348
+ `)},25e3);A.unref();let v=dx.createServer(async(k,g)=>{try{let _=n.cfg&&n.cfg.remoteHosts||[],x=pi(k.headers.host,t,_),E=px(k,n);E&&(x="remote");let O=k.headers["x-forwarded-host"]?String(k.headers["x-forwarded-host"]).split(",")[0].trim():"";if(x==="loopback"&&O&&pi(O,t,_)==="remote"&&(x="remote"),!x)return Uw(k)?Hw(g,403,"bad_host","This address isn’t on "+He+"’s list",[fn(He)+" only answers to its own names, so other sites can’t reach it through your browser. <code>"+fn(String(k.headers.host||"(none)"))+"</code> isn’t one of them.","On the machine running "+fn(He)+", run <code>"+q+" tailscale on</code>: it adds this machine’s tailnet name to the list and prints a QR code that signs your phone in."]):dn(g,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(k.headers["tailscale-funnel-request"])return Uw(k)?Hw(g,403,"funnel_refused","Not on the public internet",[fn(He)+" is not served over Tailscale Funnel. Use your tailnet: <code>"+q+" tailscale on</code> (tailscale serve)."]):dn(g,403,"funnel_refused","Leftoff is not served over Tailscale Funnel; use tailscale serve (tailnet only)");let M=k.headers.origin;if(!Eg(M,t,_))return dn(g,403,"bad_origin","cross-origin requests are refused");if(x==="loopback"&&lt(k))return dn(g,403,"bad_host","a proxied request must use a name in config remoteHosts, not a loopback Host");let C=x==="remote",U=C&&!E&&n.cfg.remoteRequireToken!==!1,W=new URL(k.url,"http://"+k.headers.host),I=W.pathname;if(k.method==="GET"&&!I.startsWith("/v1/")&&!E){let T=I==="/"||I==="/index.html"?"index.html":I.slice(1),L=gs.join(Fw,gs.normalize(T).replace(/^(\.\.[/\\])+/,""));if(!L.startsWith(Fw)||!Kf.existsSync(L)||Kf.statSync(L).isDirectory())return dn(g,404,"not_found","no such page");let j={"content-type":yx[gs.extname(L)]||"application/octet-stream","cache-control":"no-cache","x-content-type-options":"nosniff","x-frame-options":"DENY","content-security-policy":wx};if(j["referrer-policy"]="no-referrer",T==="index.html")if(!U)j["set-cookie"]="cp_token="+encodeURIComponent(i)+"; HttpOnly; SameSite=Strict; Path=/"+(C?"; Secure":"");else if(W.searchParams.has("t")){let Y=k.headers["tailscale-user-login"]||null,pe=Ng(e,W.searchParams.get("t"),{userAgent:k.headers["user-agent"],who:Y});return pe?(n.log("remote login: new browser session "+pe.id+" (link)"+(Y?" ("+Y+")":"")+" via "+k.headers.host),g.writeHead(302,{location:"/","cache-control":"no-store","referrer-policy":"no-referrer","set-cookie":Dl(pe.secret)}),g.end()):(g.writeHead(302,{location:"/?login=expired","cache-control":"no-store","referrer-policy":"no-referrer"}),g.end())}else{let Y=Bc(k,gi);Y&&Ll(e,Y,{touch:!0})&&(j["set-cookie"]=Dl(Y))}return T==="sw.js"&&(j["service-worker-allowed"]="/"),g.writeHead(200,j),Kf.createReadStream(L).pipe(g)}if(k.method==="GET"&&I==="/v1/remote/ping"){let T=!U||C&&!!Ll(e,Bc(k,gi));return D(g,200,{ok:!0,app:He,version:V,via:x,proxied:lt(k),loginRequired:U,signedIn:T})}if(k.method==="POST"&&I==="/v1/remote/signin"){if(!C)throw N(400,"not_remote","sign-in codes are for other devices; this machine needs none");if(!U)return D(g,200,{ok:!0,needed:!1});if(!/application\/json/.test(String(k.headers["content-type"]||"")))throw N(415,"json_only","send JSON");if(!p.take(h(k)))throw N(429,"slow_down","Too many tries. Wait a minute, then try again.");let T=await se(k),L=k.headers["tailscale-user-login"]||null,j=Ig(e,T.code,{userAgent:k.headers["user-agent"],who:L});if(!j.ok)throw n.log("remote login: wrong sign-in code via "+k.headers.host+(j.reason==="locked"?" (too many: pending codes voided)":"")),j.reason==="locked"?N(429,"locked","Too many wrong codes, so every pending code was cancelled. Get a new one: "+q+" link, on the machine running "+He+"."):N(401,"bad_code","That code isn’t right, or it was already used or has expired. Codes work once, for "+zs/6e4+" minutes.");return n.log("remote login: new browser session "+j.id+" (code)"+(L?" ("+L+")":"")+" via "+k.headers.host),D(g,200,{ok:!0},{"set-cookie":Dl(j.secret)})}let P=String(k.headers.authorization||""),B=P.startsWith("Bearer ")?P.slice(7).trim():Bc(k,"cp_token");if(!(E||i&&Sx(B,i)||C&&!!Ll(e,Bc(k,gi))))return U?dn(g,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"):dn(g,401,"unauthorized","missing or wrong token (see ~/checkpoints/.secret/token)");let $=()=>{if(C||lt(k))throw N(403,"loopback_only","only from the machine running Leftoff")},ge=!C&&!lt(k)?!0:C&&n.answeringInfo("remote").remoteAllowed,Qe=T=>{if(!ge)throw C||$(),N(403,"trust_off",T+" from another device is off. On a Mac, turn on Settings → Privacy & security → Trust my paired devices.")},Me=()=>{!C&&lt(k)&&$()},Q=Object.fromEntries(W.searchParams),re=T=>I.match(T),G,ze=E?mx(k.headers["x-leftoff-device-name"]):null,z=()=>({via:C?"remote":"loopback",proxied:lt(k),ip:E?null:k.socket.remoteAddress||null,host:E?"relay":k.headers.host||null,who:E?(ze||"a paired phone")+" (through the relay)":k.headers["tailscale-user-login"]||null,userAgent:k.headers["user-agent"]||null,relay:E||void 0}),te=()=>{if(!m.take(E?"relay:"+k.headers["x-leftoff-device"]:C?"remote:"+(k.headers["x-forwarded-for"]||k.socket.remoteAddress):"local"))throw N(429,"rate_limited","Too many answers in a minute; wait a moment.")},ne=()=>{let T=new AbortController;return g.on("close",()=>{g.writableEnded||T.abort()}),T.signal};if(k.method==="GET"&&I==="/v1/needs")return D(g,200,{items:n.needsList(),recent:n.recentAnswered(Math.min(50,Number(Q.recent)||20)),answering:n.answeringInfo(C?"remote":"loopback"),dismissed:n.dismissedNeeds()});if(k.method==="POST"&&I==="/v1/needs/dismiss"){let T=await se(k);if(typeof T.key!="string"||!/^[0-9a-f]{8,64}$/.test(T.key))throw N(400,"bad_request","key is required");return D(g,200,n.dismissNeed(T.key))}if(k.method==="POST"&&I==="/v1/needs/restore"){let T=await se(k),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 N(400,"bad_request","key or keys is required");return D(g,200,n.restoreNeeds(L))}if(k.method==="POST"&&I==="/v1/asks")return $(),D(g,200,n.openAsk(await se(k)));if(k.method==="POST"&&I==="/v1/asks/settle"){$();let T=await se(k);if(!T.sessionId||!T.tool)throw N(400,"bad_settle","sessionId and tool are required");let L=n.asks.settleSession(T.sessionId,String(T.reason||"answered_in_terminal"),T.all?{}:{key:li(T.tool,T.input)});return D(g,200,{ok:!0,settled:L.length})}if(k.method==="GET"&&(G=re(/^\/v1\/asks\/([\w-]+)\/wait$/))){$();let T=await n.asks.wait(G[1],Number(Q.ms)||2e4,ne());return T===null?void 0:D(g,200,T)}if(k.method==="POST"&&(G=re(/^\/v1\/asks\/([\w-]+)\/cancel$/))){$();let T=n.asks.settle(G[1],String((await se(k)).reason||"cancelled_in_terminal"));return D(g,200,T?{ok:!0,state:T.state}:{ok:!1,state:"gone"})}if(k.method==="GET"&&(G=re(/^\/v1\/asks\/([\w-]+)$/))){let T=n.asks.get(G[1]);if(!T)throw N(404,"not_found","no such request");return D(g,200,n.asks.publicAsk(T))}if(k.method==="POST"&&(G=re(/^\/v1\/asks\/([\w-]+)\/answer$/)))return n.assertMayAnswer(z()),te(),D(g,200,await n.answerAsk(G[1],await se(k),z()));if(k.method==="POST"&&(G=re(/^\/v1\/sessions\/([^/]+)\/reply$/)))return n.assertMayAnswer(z()),te(),D(g,200,await n.postReply(decodeURIComponent(G[1]),(await se(k)).text,z()));if(k.method==="GET"&&(G=re(/^\/v1\/sessions\/([^/]+)\/replies\/next$/))){$();let T=ne(),L=await n.takeReply(decodeURIComponent(G[1]),Number(Q.ms)||2e4,T);return T.aborted?void 0:D(g,200,L)}if(k.method==="GET"&&I==="/v1/sync"){let T=nr({current:e});return D(g,200,{...T,machine:n.machine,backups:or(e),lastMove:Vs(),join:rr(e,{cli:q}),viewer:{local:!C&&!lt(k)}})}if(k.method==="POST"&&I==="/v1/sync/plan"){$();let T=await se(k);if(!T.to)throw N(400,"bad_request","to: the folder to move to");let L=sr(e,gs.resolve(Bw(String(T.to))),{machine:n.machine,identity:n.peers?n.peers.id:null});T.force&&(L.collisions=L.collisions.filter(pe=>pe.code!=="machine_name_maybe_taken"),L.ok=!L.errors.length&&!L.collisions.length);let{files:j,...Y}=L;return D(g,200,{...Y,toDisplay:we(L.to),backupDisplay:we(L.backup),sample:j.filter(pe=>pe.action!=="same").slice(0,12),join:rr(L.to,{cli:q})})}if(k.method==="POST"&&I==="/v1/sync/move"){$();let T=await se(k);if(!T.to)throw N(400,"bad_request","to: the folder to move to");let L=gs.resolve(Bw(String(T.to))),j=sr(e,L,{machine:n.machine,identity:n.peers?n.peers.id:null}),Y=j.collisions.filter(hn=>!(T.force&&hn.code==="machine_name_maybe_taken"));if(j.errors.length||Y.length)throw N(409,(j.errors[0]||Y[0]).code,(j.errors[0]||Y[0]).message);let pe=[Be,"move",L,"--yes","--json","--daemon-pid",String(process.pid)];T.force&&pe.push("--force");let St=jw(process.execPath,pe,{detached:!0,stdio:"ignore",env:{...process.env}});return St.unref(),n.log("sync: moving the folder to "+L+" (leftoff move, pid "+St.pid+")"),D(g,202,{started:!0,to:L,statusFile:Ii()})}if(k.method==="POST"&&I==="/v1/sync/backup/delete"){$();let T=await se(k);if(T.confirm!==!0)throw N(400,"confirm","send { path, confirm: true } to delete a backup");let L=or(e).find(j=>j.path===T.path);if(!L)throw N(404,"not_found","no such Leftoff backup next to this folder");return D(g,200,Mi(L.path))}if(k.method==="GET"&&I==="/v1/syncthing"){let T=!C&&!lt(k),L=await tn({root:e}),j=ku(),Y={...L,myID:void 0,pairing:Gn(Rt()),run:j&&Date.now()-new Date(j.at).getTime()<6*36e5?j:null,viewer:{local:T}};return T?Y.install={...As(),job:{...ct,lines:ct.lines.slice(-60)}}:(Y.folder&&(Y.folder={...Y.folder,path:void 0}),Y.knocked=(Y.knocked||[]).map(pe=>({...pe,deviceId:void 0}))),D(g,200,Y)}if(k.method==="POST"&&I==="/v1/syncthing/install"){if($(),(await se(k)).confirm!==!0)throw N(400,"confirm","send { confirm: true } after showing the command: "+_r);if(Sn().installed)return D(g,200,{already:!0});if(!As().brew)throw N(409,"no_brew","Homebrew isn’t installed on this Mac");return D(g,202,{started:!0,job:hx(n)})}if(k.method==="POST"&&I==="/v1/syncthing/start"){$();let T=Cr();if(!T.ok)throw N(409,T.code||"start_failed",T.error);let L=await Ts({timeoutMs:2e4});if(!L.ok)throw N(504,"not_running",L.error);return n.log("syncthing: started ("+T.how+")"),D(g,200,{ok:!0})}if(k.method==="POST"&&(I==="/v1/syncthing/setup"||I==="/v1/syncthing/join")){$();let T=await se(k),L=I.endsWith("/join");if(L&&!T.code)throw N(400,"bad_request","code: the pairing code from your other Mac");if(L)try{(await Promise.resolve().then(()=>(wo(),gp))).decodePairCode(String(T.code))}catch(pe){throw N(400,pe.code||"bad_code",pe.message)}let j=[Be,L?"join":"sync",...L?[]:["setup"],"--yes","--json","--daemon-pid",String(process.pid),...T.path?["--path",String(T.path)]:[]];yo({kind:L?"join":"setup",state:"starting",message:"Starting…"});let Y=jw(process.execPath,j,{detached:!0,stdio:"ignore",env:{...process.env,...L?{LEFTOFF_JOIN_CODE:String(T.code)}:{}}});return Y.unref(),n.log("syncthing: "+(L?"joining another Mac by code":"putting the folder in Syncthing")+" (pid "+Y.pid+")"),D(g,202,{started:!0})}if(k.method==="POST"&&I==="/v1/syncthing/pair"){$();let T=await tn({root:e});if(!T.running)throw N(409,"not_running","Syncthing isn’t running");if(!T.folder)throw N(409,"no_folder","The Leftoff folder isn’t in Syncthing yet");let L=await se(k),j=Mr({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(g,200,{code:j.code,command:oo(j.code),fresh:oo(j.code,{installed:!1}),expiresAt:new Date(j.expiresAt).toISOString()},{"cache-control":"no-store"})}if(k.method==="POST"&&I==="/v1/syncthing/pair/cancel")return $(),Tu(),n.log("syncthing: pairing code cancelled"),D(g,200,{ok:!0});if(k.method==="POST"&&I==="/v1/syncthing/accept"){$();let T=await se(k);if(T.confirm!==!0||!T.deviceId)throw N(400,"confirm","send { deviceId, confirm: true } after the person clicked Accept");let L=ft();if(!L.ok)throw N(409,L.code||"no_syncthing",L.reason);let j;try{j=await Rr(L.client,{deviceId:String(T.deviceId),log:Y=>n.log(Y)})}catch(Y){throw N(Y.code==="not_knocked"?404:409,Y.code||"accept_failed",Y.message)}return D(g,200,j)}if(k.method==="POST"&&I==="/v1/syncthing/stop"){if($(),(await se(k)).confirm!==!0)throw N(400,"confirm","send { confirm: true } to stop syncing");let L=ft();if(!L.ok)throw N(409,L.code||"no_syncthing",L.reason);let j=await Ir(L.client);return n.log("syncthing: stopped syncing the Leftoff folder (files kept)"),D(g,200,j)}if(k.method==="GET"&&I==="/v1/agents"){let T=!C&&!lt(k);return D(g,200,{...n.agentsOverview({local:T}),viewer:{local:T}})}if(k.method==="POST"&&I==="/v1/agents")return $(),D(g,200,await n.addAgent(await se(k)));if(k.method==="POST"&&(G=re(/^\/v1\/agents\/([a-z0-9-]+)\/refresh$/))){if(!d.take())throw N(429,"slow_down","too many refreshes; wait a moment");return D(g,200,await n.pullAgent(G[1],{force:!0}))}if(k.method==="DELETE"&&(G=re(/^\/v1\/agents\/([a-z0-9-]+)$/)))return $(),D(g,200,n.removeAgent(G[1],{purge:Q.purge==="1"}));if(k.method==="GET"&&I==="/v1/peers")return D(g,200,{...n.peersOverview(),viewer:{local:!C&&!lt(k)}});if(k.method==="POST"&&I==="/v1/peers/allow"){if($(),!n.peers)throw N(409,"peers_off","cross-machine actions are off on this machine (LEFTOFF_PEERS=0 or peers.enabled false)");return D(g,200,await n.peers.setAllowIncoming((await se(k)).allow===!0))}if(k.method==="POST"&&(G=re(/^\/v1\/peers\/([\w.-]+)\/trust$/))){if($(),!n.peers)throw N(409,"peers_off","cross-machine actions are off on this machine");return D(g,200,n.peers.trust(decodeURIComponent(G[1])))}if(k.method==="POST"&&I==="/v1/peers/check")return n.peers&&await n.peers.poll(),D(g,200,n.peersOverview());if(k.method==="GET"&&I==="/v1/health")return D(g,200,{...n.health(),answering:n.answeringInfo(C?"remote":"loopback")});if(k.method==="POST"&&I==="/v1/mod/health")return D(g,200,n.recordModHealth(await se(k)));if(k.method==="GET"&&I==="/v1/changelog")return D(g,200,n.changelog());if(k.method==="POST"&&I==="/v1/changelog/seen")return D(g,200,n.markChangelogSeen((await se(k)).version));if(k.method==="POST"&&I==="/v1/changelog/dismiss")return D(g,200,n.dismissUpgradeNotice());if(k.method==="GET"&&I==="/v1/updates")return D(g,200,n.updateState());if(k.method==="POST"&&I==="/v1/updates/check")return D(g,200,await n.runUpdateCheck({force:!0}));if(k.method==="POST"&&I==="/v1/events"){let T=await se(k);if(!Array.isArray(T.events))throw N(400,"bad_request","body must be { events: Event[] }");return D(g,200,n.ingest(T.events))}if(k.method==="POST"&&(G=re(/^\/v1\/hooks\/([A-Za-z]+)$/))){let T=await se(k);return D(g,200,n.ingestHook(G[1],T))}if(k.method==="POST"&&(G=re(/^\/v1\/sessions\/([^/]+)\/checkpoints$/))){let T=decodeURIComponent(G[1]),L=await se(k);if(L.generate){let j=await n.generateCheckpoint(T,L.trigger||"manual",{userNote:L.userNote,length:L.length||void 0});return D(g,201,j)}return D(g,201,n.addCheckpoint(T,L))}if(k.method==="POST"&&I==="/v1/checkpoints/preview"){let T=await se(k);if(!T.sessionId)throw N(400,"bad_request","sessionId is required");return D(g,200,await n.generateCheckpoint(String(T.sessionId),"manual",{length:T.length||void 0,save:!1}))}if(k.method==="GET"&&I==="/v1/sessions")return D(g,200,{sessions:n.listSessions({...Q,includeArchived:Q.archived==="1"}),closedCount:n.closedCount({machine:Q.machine}),viewer:{local:!C,ghostty:n.platform==="darwin",mayAnswer:n.answeringInfo(C?"remote":"loopback").allowedHere}});if(k.method==="GET"&&I==="/v1/sessions/closed")return D(g,200,{...n.closedSessions({q:Q.q,machine:Q.machine,repo:Q.repo,space:Q.space,limit:Q.limit,offset:Q.offset}),viewer:{local:!C&&!lt(k),ghostty:n.platform==="darwin"}});if(k.method==="GET"&&I==="/v1/gh"){let T=await ja({fresh:Q.fresh==="1"});return T.status==="ready"&&n.prs&&(n.prs.offUntil=0),D(g,200,T)}if(k.method==="GET"&&I==="/v1/prs")return D(g,200,await n.prsOverview({view:Q.view,fresh:Q.fresh==="1"}));if(k.method==="POST"&&(G=re(/^\/v1\/sessions\/([^/]+)\/prs$/)))return D(g,200,await n.linkPr(decodeURIComponent(G[1]),await se(k)));if(k.method==="GET"&&(G=re(/^\/v1\/sessions\/([^/]+)\/turns$/)))return D(g,200,await n.sessionTurns(decodeURIComponent(G[1]),{limit:Q.limit}));if(k.method==="GET"&&I==="/v1/restore")return D(g,200,{...n.restoreCandidates({since:Q.since,repo:Q.repo}),viewer:{local:!C&&!lt(k)}});if(k.method==="POST"&&I==="/v1/restore"){$();let T=await se(k);if(!Array.isArray(T.ids))throw N(400,"bad_request","ids: the session ids to restore");if(T.print!==!0&&!u.take())throw N(429,"slow_down","too many restore requests; wait a moment");return D(g,200,await n.restore(T.ids,{print:T.print===!0}))}if(k.method==="POST"&&I==="/v1/restore/launched"){$();let T=await se(k),L=n.restoreTargets(Array.isArray(T.ids)?T.ids:[]).map(j=>j.id);return n.markRestored(L),D(g,200,{ok:!0,marked:L.length})}if(k.method==="POST"&&(G=re(/^\/v1\/sessions\/([^/]+)\/resume$/))){if($(),!u.take())throw N(429,"slow_down","too many resume requests; wait a moment");return D(g,200,await n.resume(decodeURIComponent(G[1])))}if(k.method==="POST"&&(G=re(/^\/v1\/sessions\/([^/]+)\/go$/))){if($(),!l.take())throw N(429,"slow_down","too many Go requests; wait a moment");return D(g,200,await n.jump(decodeURIComponent(G[1])))}if(k.method==="GET"&&(G=re(/^\/v1\/sessions\/([^/]+)\/where$/)))return $(),D(g,200,n.where(decodeURIComponent(G[1])));if(k.method==="GET"&&I==="/v1/jump/speed")return $(),D(g,200,await n.jumpSpeed());if(k.method==="GET"&&I==="/v1/jump/check")return $(),D(g,200,await n.jumpCheck(Q.kind||"ghostty"));if(k.method==="GET"&&I==="/v1/lists")return D(g,200,{...n.listsOverview({all:Q.all==="1",repo:Q.repo||null}),mayEdit:n.answeringInfo(C?"remote":"loopback").allowedHere});if(k.method==="GET"&&I==="/v1/lists/resolve")return D(g,200,{list:n.getList(Q.ref)});if(k.method==="POST"&&I==="/v1/lists")return D(g,200,n.saveList(await se(k),z()));if(k.method==="GET"&&(G=re(/^\/v1\/lists\/(~?[\w.-]+\/[\w.-]+)$/)))return D(g,200,{list:n.getList(G[1]),mayEdit:n.answeringInfo(C?"remote":"loopback").allowedHere});if(k.method==="POST"&&(G=re(/^\/v1\/lists\/(~?[\w.-]+\/[\w.-]+)\/change$/)))return D(g,200,n.changeList(G[1],await se(k),z()));if(k.method==="GET"&&(G=re(/^\/v1\/sessions\/([^/]+)\/lists$/)))return D(g,200,n.sessionLists(decodeURIComponent(G[1])));if(k.method==="GET"&&(G=re(/^\/v1\/sessions\/([^/]+)\/lists\/find$/)))return D(g,200,n.findListsInSession(decodeURIComponent(G[1])));if(k.method==="POST"&&(G=re(/^\/v1\/sessions\/([^/]+)\/list-hint\/dismiss$/)))return n.assertMayEditLists(z()),D(g,200,n.dismissListHint(decodeURIComponent(G[1])));if(k.method==="GET"&&(G=re(/^\/v1\/sessions\/([^/]+)$/)))return D(g,200,{...n.getSession(decodeURIComponent(G[1])),viewer:{local:!C&&!lt(k),ghostty:n.platform==="darwin"}});if(k.method==="PATCH"&&(G=re(/^\/v1\/sessions\/([^/]+)$/)))return D(g,200,n.patchSession(decodeURIComponent(G[1]),await se(k)));if(k.method==="GET"&&(G=re(/^\/v1\/sessions\/([^/]+)\/checkpoints$/)))return D(g,200,{checkpoints:n.sessionCheckpoints(decodeURIComponent(G[1]),Q)});if(k.method==="GET"&&I==="/v1/checkpoints")return D(g,200,{checkpoints:n.timeline(Q)});if(k.method==="GET"&&I==="/v1/search")return D(g,200,n.search(Q.q));if(k.method==="GET"&&I==="/v1/catchups")return D(g,200,{catchups:n.folder.listCatchups(),lastAt:(()=>{let T=n.folder.lastCatchupAt();return T==null?null:new Date(T).toISOString()})()});if(k.method==="GET"&&I==="/v1/catchups/options")return D(g,200,n.catchupOptions({since:Q.since,to:Q.to}));if(k.method==="POST"&&I==="/v1/catchups/prepare"){let T=await se(k),L=await n.makeCatchup({...T,save:!1,useModel:!1}),{catchupLength:j,CATCHUP_SYSTEM:Y,summaryPrompt:pe}=await Promise.resolve().then(()=>(nl(),_m)),St=j(n.cfg,L.recap.length);return D(g,200,{window:L.recap.window,sessions:L.recap.sessions.length,needsYou:L.recap.needsYou.length,prompt:L.recap.sessions.length?{system:Y.replace("{n}",String(St.bullets)),prompt:pe(L.recap,{maxChars:n.cfg.summarizer&&n.cfg.summarizer.maxInputChars||24e3}),maxTokens:St.maxTokens}:null})}if(k.method==="POST"&&I==="/v1/catchups"){let T=await se(k);return D(g,200,await n.makeCatchup(T))}if(k.method==="GET"&&(G=re(/^\/v1\/catchups\/(\d{4}-\d{2}-\d{2}T\d{4}(?:-\d+)?)$/))){let T=n.readCatchup(G[1]);if(!T)throw N(404,"not_found","no catch-up named "+G[1]);return D(g,200,T)}if(k.method==="GET"&&I==="/v1/digests")return D(g,200,{digests:n.folder.listDigests()});if(k.method==="GET"&&(G=re(/^\/v1\/digests\/(\d{4}-\d{2}-\d{2})$/))){let T=n.folder.readDigest(G[1],Q.machine||null);if(!T)throw N(404,"not_found","no digest for "+G[1]);let L=n.folder.listDigests().filter(j=>j.date===G[1]&&!(j.shared&&T.shared)&&j.machine!==T.machine);return D(g,200,{meta:$e(T.markdown).data,markdown:T.markdown,machine:T.machine,shared:!!T.shared,by:T.by||null,duplicates:T.duplicates||0,others:L})}if(k.method==="GET"&&I==="/v1/spaces")return D(g,200,n.spacesView());if(k.method==="POST"&&I==="/v1/spaces")return D(g,200,n.setSpace(await se(k)));if(k.method==="GET"&&I==="/v1/standup")return D(g,200,n.standup({space:Q.space||null,date:Q.date||null}));if(k.method==="POST"&&I==="/v1/digests"){let T=await se(k),L=await n.makeDigest({date:T.date});return D(g,201,L)}if(k.method==="GET"&&I==="/v1/models")return D(g,200,await n.modelChoices({provider:Q.provider,baseUrl:Q.baseUrl}));if(k.method==="POST"&&I==="/v1/models/ollama/start"){if($(),!c.take())throw N(429,"slow_down","Starting Ollama already; wait a moment");return D(g,200,await n.startOllama())}if(k.method==="GET"&&I==="/v1/models/pulls")return D(g,200,{pulls:n.pulls.list()});if(k.method==="POST"&&I==="/v1/models/pull"){$();let T=await se(k);return D(g,202,n.startPull({tag:T.tag,use:!!T.use}))}if(k.method==="DELETE"&&(G=re(/^\/v1\/models\/pull\/(.+)$/))){$();let T=n.pulls.cancel(decodeURIComponent(G[1]));if(!T)throw N(404,"not_found","no download of that model");return D(g,200,T)}if(k.method==="GET"&&I==="/v1/remote")return D(g,200,n.remoteState(x));if(k.method==="POST"&&I==="/v1/remote/hosts")return $(),D(g,200,n.setRemoteHosts(await se(k)));if(k.method==="POST"&&I==="/v1/remote/reload")return $(),D(g,200,n.reloadRemote());if(k.method==="POST"&&I==="/v1/remote/revoke")return $(),D(g,200,{revoked:wi(e),...n.remoteState(x)});if(k.method==="POST"&&I==="/v1/remote/link"){let T=Il(n.cfg.remoteHosts);if(!T.length)throw N(409,"remote_off","remote access is off: run leftoff tailscale on");if(n.cfg.remoteRequireToken===!1)return D(g,200,{needed:!1,urls:T});if(C&&!p.take("mint:"+h(k)))throw N(429,"slow_down","Too many codes in a row; wait a minute.");let L=!C||ge&&(await se(k).catch(()=>null)||{}).full===!0,j=yi(e,{codeOnly:!L,note:C?L?"from a trusted remote browser":"from a signed-in remote browser":null}),Y={needed:!0,base:T[0],code:Ml(j.signInCode),expiresAt:j.expiresAt,ttlMin:zs/6e4};return j.code&&Object.assign(Y,{urls:T.map(pe=>pe+"?t="+j.code),url:T[0]+"?t="+j.code,qrSvg:uc(T[0]+"?t="+j.code)}),D(g,200,Y)}if(I==="/v1/relay"||I.startsWith("/v1/relay/")){let T=n.relayHost;if(!T){if(k.method==="GET"&&I==="/v1/relay")return D(g,200,{enabled:!1,configured:!1,available:!1,user:null,host:null,boardUrl:null,connected:!1,connecting:!1,latencyMs:null,devices:0,list:[]});if(k.method==="GET"&&I==="/v1/relay/devices")return D(g,200,{devices:[]});throw N(409,"relay_unavailable","the relay isn’t available in this process")}if(k.method==="GET"&&I==="/v1/relay")return await T.refreshRelayDevices().catch(()=>{}),D(g,200,{...T.status(),list:T.devices(),viewer:{relay:E,device:E?k.headers["x-leftoff-device"]:null,local:!C&&!lt(k),mayPair:ge,mayRevoke:C||!lt(k)}});if(k.method==="POST"&&I==="/v1/relay/enable")return $(),D(g,200,await T.enable(await se(k)));if(k.method==="POST"&&I==="/v1/relay/signup")return $(),D(g,200,await T.signupStart(await se(k)));if(k.method==="GET"&&I==="/v1/relay/signup")return $(),D(g,200,await T.signupCheck());if(k.method==="POST"&&I==="/v1/relay/signup/cancel")return $(),D(g,200,T.signupCancel());if(k.method==="POST"&&I==="/v1/relay/disable")return $(),D(g,200,await T.disable());if(k.method==="POST"&&I==="/v1/relay/pair"){Qe("Pairing");let L=await T.createPair();return C&&n.log("relay: pairing code made from "+(ze||"a remote browser")),D(g,200,L)}if(k.method==="POST"&&I==="/v1/relay/pair/cancel")return Qe("Pairing"),await T.cancelPairs(),D(g,200,{ok:!0});if(k.method==="GET"&&I==="/v1/relay/devices")return await T.refreshRelayDevices().catch(()=>{}),D(g,200,{devices:T.devices()});if(k.method==="POST"&&(G=re(/^\/v1\/relay\/devices\/([\w-]+)\/revoke$/))){Me();let L=E&&k.headers["x-leftoff-device"]===G[1];return D(g,200,{...await T.revoke(G[1]),self:L})}throw N(404,"not_found","no route for "+k.method+" "+I)}if(k.method==="GET"&&I==="/v1/events"){let T=Number(Q.after),L=()=>y.filter(Y=>Y.seq>T);if(!Number.isFinite(T)||T<0||T>S)return D(g,200,{seq:S,events:[],reset:Number.isFinite(T)&&T>S});let j=L();if(!j.length){let Y=ne();if(await new Promise(pe=>{let St=setTimeout(hn,Math.min(25e3,Number(Q.ms)||2e4));function hn(){clearTimeout(St),b.delete(hn),Y.removeEventListener("abort",hn),pe()}b.add(hn),Y.addEventListener("abort",hn)}),Y.aborted)return;j=L()}return n.boardOpened&&j.length,D(g,200,{seq:S,events:j})}if(k.method==="GET"&&I==="/v1/config")return D(g,200,n.publicConfig());if(k.method==="PATCH"&&I==="/v1/config")return D(g,200,n.updateConfig(await se(k),{via:C?"remote":"loopback"}));if(k.method==="PUT"&&(G=re(/^\/v1\/config\/keys\/([a-z-]+)$/)))return D(g,200,n.saveKey(G[1],(await se(k)).key));if(k.method==="GET"&&I==="/v1/stream"){g.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-store",connection:"keep-alive"}),g.write(`retry: 3000
346
349
 
347
- `),a.add(g),n.boardOpened&&n.boardOpened(),b.on("close",()=>{a.delete(g),n.boardClosed&&n.boardClosed()});return}return an(g,404,"not_found","no route for "+b.method+" "+N)}catch(_){return _.status?an(g,_.status,_.code||"error",_.message):(n.log("500: "+(_.stack||_.message)),an(g,500,"internal",_.message))}});return T.on("close",()=>{clearInterval(v);for(let b of a)b.end()}),{server:T,listen:()=>new Promise((b,g)=>{T.once("error",g),T.listen(t,s,()=>b(T))}),close:()=>new Promise(b=>{for(let g of a)g.end();T.close(()=>b()),T.closeAllConnections&&T.closeAllConnections()})}}var rt,Zy,T1,_1,E1,cn,ew,O1,Wd=z(()=>{ke();Qr();Ld();ve();Sl();ki();_a();ke();St();$s();Qn();lo();yf();rt={state:"idle",lines:[],error:null,startedAt:null,finishedAt:null,stage:null};Zy=ds.join(ds.dirname(b1(import.meta.url)),"..","ui"),T1=1024*1024,_1={".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"},E1="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'";cn=n=>String(n).replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[e]);ew=n=>n==="~"?process.env.HOME||"":n.startsWith("~/")?ds.join(process.env.HOME||"",n.slice(2)):n;O1=["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);ke();Ld();Wd();import Yt from"node:fs";import jf from"node:os";import ps from"node:path";import{fileURLToPath as mx}from"node:url";ke();wn();ve();Qr();import{execFileSync as I1}from"node:child_process";function N1({start:n=process.ppid,ps:e=M1,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 th(o+" "+i.tty+" "+i.etime,s);o=i.ppid}return null}function M1(n){try{let t=I1("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 L1(n=process.env){let e={};for(let t of eh)n[t]&&(e[t]=n[t]);return e}async function sw(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 ow(n,{stdinText:e,timeoutMs:t=800}={}){if(process.env.CHECKPOINTS_INTERNAL)return{skipped:"internal"};let s=V(),o=e??await sw(),r;try{r=JSON.parse(o||"{}")}catch{return{skipped:"bad_json"}}let i=n||r.hook_event_name;if(!i||!r.session_id)return{skipped:"no_event"};if(i==="SessionStart"&&!r._checkpoints){let u=null;try{u=N1()}catch{}r._checkpoints={proc:u,termEnv:L1()}}let a=X(s),l=Se(s),c=new Date().toISOString();try{let u=await fetch("http://127.0.0.1:"+a.port+"/v1/hooks/"+encodeURIComponent(i),{method:"POST",headers:{"content-type":"application/json",authorization:"Bearer "+(l||"")},body:JSON.stringify(r),signal:AbortSignal.timeout(t)});if(u.ok)return{posted:!0};if(u.status===400||u.status===404)return{rejected:u.status};throw new Error("status "+u.status)}catch{try{return Vh(s,le(a),r.session_id,{hook:i,payload:r,at:c}),{spooled:!0}}catch(u){return{lost:u.message}}}}async function rw({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=V(),i=n??await sw(),a;try{a=JSON.parse(i||"{}")}catch{return{skipped:"bad_json"}}if(!a.session_id||!a.tool_name)return{skipped:"no_event"};let l=X(r);if(l.answers&&l.answers.enabled===!1)return{skipped:"off"};let c=Se(r),u="http://127.0.0.1:"+l.port,d={"content-type":"application/json",authorization:"Bearer "+(c||"")},f=async(w,S,k,v)=>{let T=await s(u+S,{method:w,headers:d,body:k===void 0?void 0:JSON.stringify(k),signal:AbortSignal.timeout(v)});if(!T.ok)throw new Error("status "+T.status);return T.json()},p;try{p=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(!p||!p.id||p.state!=="waiting")return{skipped:p&&p.reason?p.reason:"not_waiting"};let h=!1;o(async()=>{h=!0;try{await f("POST","/v1/asks/"+p.id+"/cancel",{reason:"cancelled_in_terminal"},800)}catch{}process.exit(0)});let m=Date.now(),y=0;for(;!h&&Date.now()-m<t;){let w;try{w=await f("GET","/v1/asks/"+p.id+"/wait?ms=20000",void 0,25e3),y=0}catch{if(++y>=3)return{gaveUp:"daemon_unreachable"};await new Promise(S=>setTimeout(S,1e3));continue}if(w.state==="answered"&&w.decision)return e.write(JSON.stringify(fm(w.decision))+`
348
- `),{answered:w.decision.behavior};if(w.state!=="waiting")return{settled:w.state,reason:w.reason}}return{gaveUp:"timeout"}}cl();wn();Es();Qn();ec();St();Ys();In();ke();Is();Es();Ys();async function iw({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=is(rs(s)),l=V(),c=X(l),u=c.summarizer||{},d=(t["ollama-url"]||u.provider==="ollama"&&u.baseUrl||lt).replace(/\/$/,""),f=(m="")=>s.write(m+`
349
- `),p=async m=>{Te(l);let y=yc(fn(l)||{},{summarizer:m});wc(l,y),f(a.green("✔")+" Summarizer is now "+a.bold(m.provider+(m.model?" / "+m.model:"")));let w=await Qo(c.port,Se(l),i);if(w.up&&w.auth)try{await mc(c.port,Se(l),{summarizer:y.summarizer},i),f(a.green("✔")+" Running daemon switched over")}catch(S){f(a.yellow("!")+" Daemon didn't take it ("+S.message+"); restart it")}},h=async()=>{let m=String(t["cloud-url"]||u.provider==="ollama-cloud"&&u.baseUrl||r.CHECKPOINTS_CLOUD_URL||Cs).replace(/\/$/,""),y=await Pr({baseUrl:m,fetchImpl:i}),w=Sn("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+" · "+(w?"API key found":"no key: set OLLAMA_API_KEY (ollama.com/settings/keys)"))),!y.ok){f(a.dim(" couldn't fetch the model list ("+y.error+")"));return}let S=u.provider==="ollama-cloud"&&u.model||null;for(let k of va(y.models,{current:S})){let v=k.current?a.cyan("●"):k.available===!1?a.dim("×"):a.dim("○");f(" "+v+" "+k.tag.padEnd(22)+a.dim(k.note||"")+(k.recommended?a.yellow(" ★"):"")+(k.current?a.cyan(" (in use)"):"")+(k.available===!1?a.dim(" (not offered today)"):""))}f(a.dim(" `leftoff models use <tag> --cloud` switches · nothing to download"))};if(n==="cloud")return await h(),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 jt({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, "+mo));let w=u.provider==="ollama"?u.model:null;for(let S of go(m.models,{current:w})){let k=S.current?a.cyan("●"):S.installed?a.green("✔"):a.dim("○"),v=(S.recommended?a.yellow(" ★"):"")+(S.current?a.cyan(" (in use)"):""),T=S.curated?xa(S,Dt)+" · "+S.note:S.sizeBytes?Dt(S.sizeBytes):"";f(" "+k+" "+S.tag.padEnd(16)+a.dim(T)+v)}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 y=await kn({baseUrl:t["lmstudio-url"]||u.provider==="openai-compatible"&&u.baseUrl||Qt,fetchImpl:i});if(f(),y.running){f(a.bold("LM Studio / OpenAI-compatible")+a.dim(" · "+y.baseUrl));for(let w of y.models)f(" "+(u.provider==="openai-compatible"&&u.model===w?a.cyan("●"):a.green("✔"))+" "+w);y.models.length||f(a.dim(" no model loaded"))}else f(a.bold("LM Studio")+": not running at "+y.baseUrl);return t.local||(f(),await h()),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 S=String(t["base-url"]||Cs).replace(/\/$/,""),k=await Pr({baseUrl:S,fetchImpl:i});k.ok&&!k.models.some(g=>Ge(g,m))&&f(a.yellow("!")+" "+m+" isn't in ollama.com's cloud list right now ("+k.models.slice(0,5).join(", ")+", …); using it anyway");let v=u.provider==="ollama-cloud"?u:{},T={provider:"ollama-cloud",model:m,baseUrl:S===Cs?null:S,apiKeyEnv:v.apiKeyEnv||(t["api-key-env"]?String(t["api-key-env"]):null),...v.apiKeyFile?{apiKeyFile:v.apiKeyFile}:{}},b=Sn("ollama-cloud",{apiKeyEnv:T.apiKeyEnv,apiKeyFile:T.apiKeyFile,root:l},r);if(b||f(a.yellow("!")+" No key found: export "+(T.apiKeyEnv||"OLLAMA_API_KEY")+" (from ollama.com/settings/keys) or run `leftoff setup` to save one"),await p(T),!t["no-test"]&&b){let g=tt("Writing a sample checkpoint with "+m+" on ollama.com…",{out:s,colors:a}),_=await Os(T,{fetchImpl:i,root:l});return _.ok?g.succeed("It works."):g.fail("The test didn't work."),Vs(s,a,_,m+" (cloud)"),_.ok?0:1}return 0}if(n==="use"&&(t.lmstudio||t.provider==="openai-compatible")){let S=(t["base-url"]||Qt).replace(/\/$/,""),k=await kn({baseUrl:S,fetchImpl:i});if(!k.running)throw new Error("nothing answers at "+S+"/models");if(!k.models.includes(m))throw new Error(m+" isn't loaded there (loaded: "+(k.models.join(", ")||"none")+")");let v={provider:"openai-compatible",model:m,baseUrl:S===Qt?null:S};return await p(v),t["no-test"]||Vs(s,a,await Os(v,{fetchImpl:i}),m),0}let y=await jt({baseUrl:d,fetchImpl:i,env:r});if(!y.running)throw new Error("Ollama isn't running at "+d+" (start it with `ollama serve`)");if(y.models.some(S=>Ge(S.name,m)))f(a.green("✔")+" "+m+" is already pulled");else{let S=Vl("pull "+m,{out:s,colors:a}),k=Date.now();try{await So({tag:m,baseUrl:y.baseUrl,fetchImpl:i,onProgress:v=>S.update(v.fraction,v.total?Dt(v.completed)+" / "+Dt(v.total):v.status)})}catch(v){return S.fail(v.message),1}S.done("Pulled "+m+a.dim(" in "+Ai(Date.now()-k)))}if(n==="use"){let S={provider:"ollama",model:m,baseUrl:y.baseUrl===lt?null:y.baseUrl};if(await p(S),!t["no-test"]){let k=tt("Writing a sample checkpoint with "+m+"…",{out:s,colors:a}),v=await Os(S,{fetchImpl:i});return v.ok?k.succeed("It works."):k.fail("The test didn't work."),Vs(s,a,v,m),v.ok?0:1}}return 0}if(n==="test"){let m=tt("Writing a sample checkpoint with "+(u.provider||"auto")+(u.model?" / "+u.model:"")+"…",{out:s,colors:a}),y=await Os(u,{fetchImpl:i,root:l});return y.ok?m.succeed("It works."):m.fail("The test didn't work."),Vs(s,a,y,u.model||u.provider),y.ok?0:1}if(n==="pick"){let m=new ft({input:o,output:s,yes:!!t.yes,colors:a}),w={p:m,u:{c:a,line:f,ok:S=>f(a.green("✔")+" "+S),warn:S=>f(a.yellow("!")+" "+S),fail:S=>f(a.red("✖")+" "+S),info:S=>f(a.dim("·")+" "+S),note:S=>f(" "+a.dim(S)),cmd:S=>f(" "+a.cyan("$ "+S))},out:s,flags:t,env:r,fetchImpl:i,yes:!!t.yes,currentModel:u.model};try{let S=await kc(w);return S?(t["no-test"]||await er(w,S,S.model),await p(S),0):(f("Nothing changed."),1)}catch(S){if(S.cancelled)return f(a.red("✖")+" Cancelled; nothing changed."),130;throw S}finally{m.close()}}throw new Error("unknown: leftoff models "+n+" (list, cloud, pull <tag>, use <tag> [--cloud|--lmstudio], test, pick)")}Yd();ke();Vr();$a();ec();Qn();In();Qn();St();import ht from"node:fs";import Et from"node:path";import{spawn as P1,spawnSync as _c}from"node:child_process";import Sf from"node:fs";import aw from"node:os";import Hi from"node:path";var kf=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"],D1=n=>String(n).replace(/\x1b\[[0-9;]*[A-Za-z]/g,"");function R1(n=process.env){return Hi.join(n.XDG_CONFIG_HOME||Hi.join(n.HOME||aw.homedir(),".config"),"checkpoints","logs")}function lw(n=process.env,e=new Date){let t=e.getFullYear()+"-"+String(e.getMonth()+1).padStart(2,"0")+"-"+String(e.getDate()).padStart(2,"0");return Hi.join(R1(n),"upgrade-"+t+".log")}var bf=(n,e=process.env)=>{let t=e.HOME||aw.homedir();return t&&n.startsWith(t+Hi.sep)?"~"+n.slice(t.length):n};function cw({out:n=process.stdout,quiet:e=!0,c:t,logFile:s,tty:o=!!n.isTTY,env:r=process.env}){try{Sf.mkdirSync(Hi.dirname(s),{recursive:!0})}catch{}let i=(p="")=>{try{Sf.appendFileSync(s,D1(p)+`
350
+ `),a.add(g),n.boardOpened&&n.boardOpened(),k.on("close",()=>{a.delete(g),n.boardClosed&&n.boardClosed()});return}return dn(g,404,"not_found","no route for "+k.method+" "+I)}catch(_){return _.status?dn(g,_.status,_.code||"error",_.message):(n.log("500: "+(_.stack||_.message)),dn(g,500,"internal",_.message))}});return v.on("close",()=>{clearInterval(A);for(let k of a)k.end()}),{server:v,listen:()=>new Promise((k,g)=>{v.once("error",g),v.listen(t,s,()=>k(v))}),close:()=>new Promise(k=>{for(let g of a)g.end();v.close(()=>k()),v.closeAllConnections&&v.closeAllConnections()})}}var ct,Fw,gx,yx,wx,fn,Bw,kx,wf=J(()=>{ve();ui();of();Ae();Pl();Ni();Co();ve();Tt();Zs();os();wo();Gf();ct={state:"idle",lines:[],error:null,startedAt:null,finishedAt:null,stage:null};Fw=gs.join(gs.dirname(fx(import.meta.url)),"..","ui"),gx=1024*1024,yx={".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"},wx="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'";fn=n=>String(n).replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[e]);Bw=n=>n==="~"?process.env.HOME||"":n.startsWith("~/")?gs.join(process.env.HOME||"",n.slice(2)):n;kx=["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);ve();of();wf();import Zt from"node:fs";import lh from"node:os";import Ss from"node:path";import{fileURLToPath as aA}from"node:url";ve();vn();Ae();ui();import{execFileSync as bx}from"node:child_process";function vx({start:n=process.ppid,ps:e=xx,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 Lh(o+" "+i.tty+" "+i.etime,s);o=i.ppid}return null}function xx(n){try{let t=bx("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 Ax(n=process.env){let e={};for(let t of Mh)n[t]&&(e[t]=n[t]);return e}function Tx(n={},e=process.env){let t=typeof n.model=="string"?n.model:n.model&&n.model.id||null;return{termEnv:Ax(e),backend:vh(e,{model:t})}}async function $w(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 Yw(n,{stdinText:e,timeoutMs:t=800,env:s=process.env}={}){if(s.CHECKPOINTS_INTERNAL)return{skipped:"internal"};let o=Z(),r=e??await $w(),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=vx()}catch{}i._checkpoints={proc:d,...Tx(i,s)}}let l=X(o),c=be(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 Op(o,de(l),i.session_id,{hook:a,payload:i,at:u}),{spooled:!0}}catch(d){return{lost:d.message}}}}async function Ww({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 $w(),a;try{a=JSON.parse(i||"{}")}catch{return{skipped:"bad_json"}}if(!a.session_id||!a.tool_name)return{skipped:"no_event"};let l=X(r);if(l.answers&&l.answers.enabled===!1)return{skipped:"off"};let c=be(r),u="http://127.0.0.1:"+l.port,d={"content-type":"application/json",authorization:"Bearer "+(c||"")},f=async(S,b,w,A)=>{let v=await s(u+b,{method:S,headers:d,body:w===void 0?void 0:JSON.stringify(w),signal:AbortSignal.timeout(A)});if(!v.ok)throw new Error("status "+v.status);return v.json()},p;try{p=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(!p||!p.id||p.state!=="waiting")return{skipped:p&&p.reason?p.reason:"not_waiting"};let h=!1;o(async()=>{h=!0;try{await f("POST","/v1/asks/"+p.id+"/cancel",{reason:"cancelled_in_terminal"},800)}catch{}process.exit(0)});let m=Date.now(),y=0;for(;!h&&Date.now()-m<t;){let S;try{S=await f("GET","/v1/asks/"+p.id+"/wait?ms=20000",void 0,25e3),y=0}catch{if(++y>=3)return{gaveUp:"daemon_unreachable"};await new Promise(b=>setTimeout(b,1e3));continue}if(S.state==="answered"&&S.decision)return e.write(JSON.stringify(eg(S.decision))+`
351
+ `),{answered:S.decision.behavior};if(S.state!=="waiting")return{settled:S.state,reason:S.reason}}return{gaveUp:"timeout"}}_l();vn();Is();os();yc();Tt();Xs();Dn();ve();Rs();Is();Xs();async function zw({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=ds(us(s)),l=Z(),c=X(l),u=c.summarizer||{},d=(t["ollama-url"]||u.provider==="ollama"&&u.baseUrl||ht).replace(/\/$/,""),f=(m="")=>s.write(m+`
352
+ `),p=async m=>{Ee(l);let y=Dc(gn(l)||{},{summarizer:m});Rc(l,y),f(a.green("✔")+" Summarizer is now "+a.bold(m.provider+(m.model?" / "+m.model:"")));let S=await cr(c.port,be(l),i);if(S.up&&S.auth)try{await Mc(c.port,be(l),{summarizer:y.summarizer},i),f(a.green("✔")+" Running daemon switched over")}catch(b){f(a.yellow("!")+" Daemon didn't take it ("+b.message+"); restart it")}},h=async()=>{let m=String(t["cloud-url"]||u.provider==="ollama-cloud"&&u.baseUrl||r.CHECKPOINTS_CLOUD_URL||Ms).replace(/\/$/,""),y=await Yr({baseUrl:m,fetchImpl:i}),S=xn("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)"))),!y.ok){f(a.dim(" couldn't fetch the model list ("+y.error+")"));return}let b=u.provider==="ollama-cloud"&&u.model||null;for(let w of La(y.models,{current:b})){let A=w.current?a.cyan("●"):w.available===!1?a.dim("×"):a.dim("○");f(" "+A+" "+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 h(),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 Bt({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, "+Ao));let S=u.provider==="ollama"?u.model:null;for(let b of To(m.models,{current:S})){let w=b.current?a.cyan("●"):b.installed?a.green("✔"):a.dim("○"),A=(b.recommended?a.yellow(" ★"):"")+(b.current?a.cyan(" (in use)"):""),v=b.curated?Da(b,Ut)+" · "+b.note:b.sizeBytes?Ut(b.sizeBytes):"";f(" "+w+" "+b.tag.padEnd(16)+a.dim(v)+A)}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 y=await An({baseUrl:t["lmstudio-url"]||u.provider==="openai-compatible"&&u.baseUrl||on,fetchImpl:i});if(f(),y.running){f(a.bold("LM Studio / OpenAI-compatible")+a.dim(" · "+y.baseUrl));for(let S of y.models)f(" "+(u.provider==="openai-compatible"&&u.model===S?a.cyan("●"):a.green("✔"))+" "+S);y.models.length||f(a.dim(" no model loaded"))}else f(a.bold("LM Studio")+": not running at "+y.baseUrl);return t.local||(f(),await h()),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 b=String(t["base-url"]||Ms).replace(/\/$/,""),w=await Yr({baseUrl:b,fetchImpl:i});w.ok&&!w.models.some(g=>We(g,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 A=u.provider==="ollama-cloud"?u:{},v={provider:"ollama-cloud",model:m,baseUrl:b===Ms?null:b,apiKeyEnv:A.apiKeyEnv||(t["api-key-env"]?String(t["api-key-env"]):null),...A.apiKeyFile?{apiKeyFile:A.apiKeyFile}:{}},k=xn("ollama-cloud",{apiKeyEnv:v.apiKeyEnv,apiKeyFile:v.apiKeyFile,root:l},r);if(k||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 p(v),!t["no-test"]&&k){let g=ot("Writing a sample checkpoint with "+m+" on ollama.com…",{out:s,colors:a}),_=await Ds(v,{fetchImpl:i,root:l});return _.ok?g.succeed("It works."):g.fail("The test didn't work."),so(s,a,_,m+" (cloud)"),_.ok?0:1}return 0}if(n==="use"&&(t.lmstudio||t.provider==="openai-compatible")){let b=(t["base-url"]||on).replace(/\/$/,""),w=await An({baseUrl:b,fetchImpl:i});if(!w.running)throw new Error("nothing answers at "+b+"/models");if(!w.models.includes(m))throw new Error(m+" isn't loaded there (loaded: "+(w.models.join(", ")||"none")+")");let A={provider:"openai-compatible",model:m,baseUrl:b===on?null:b};return await p(A),t["no-test"]||so(s,a,await Ds(A,{fetchImpl:i}),m),0}let y=await Bt({baseUrl:d,fetchImpl:i,env:r});if(!y.running)throw new Error("Ollama isn't running at "+d+" (start it with `ollama serve`)");if(y.models.some(b=>We(b.name,m)))f(a.green("✔")+" "+m+" is already pulled");else{let b=hc("pull "+m,{out:s,colors:a}),w=Date.now();try{await Oo({tag:m,baseUrl:y.baseUrl,fetchImpl:i,onProgress:A=>b.update(A.fraction,A.total?Ut(A.completed)+" / "+Ut(A.total):A.status)})}catch(A){return b.fail(A.message),1}b.done("Pulled "+m+a.dim(" in "+Di(Date.now()-w)))}if(n==="use"){let b={provider:"ollama",model:m,baseUrl:y.baseUrl===ht?null:y.baseUrl};if(await p(b),!t["no-test"]){let w=ot("Writing a sample checkpoint with "+m+"…",{out:s,colors:a}),A=await Ds(b,{fetchImpl:i});return A.ok?w.succeed("It works."):w.fail("The test didn't work."),so(s,a,A,m),A.ok?0:1}}return 0}if(n==="test"){let m=ot("Writing a sample checkpoint with "+(u.provider||"auto")+(u.model?" / "+u.model:"")+"…",{out:s,colors:a}),y=await Ds(u,{fetchImpl:i,root:l});return y.ok?m.succeed("It works."):m.fail("The test didn't work."),so(s,a,y,u.model||u.provider),y.ok?0:1}if(n==="pick"){let m=new yt({input:o,output:s,yes:!!t.yes,colors:a}),S={p:m,u:{c:a,line:f,ok:b=>f(a.green("✔")+" "+b),warn:b=>f(a.yellow("!")+" "+b),fail:b=>f(a.red("✖")+" "+b),info:b=>f(a.dim("·")+" "+b),note:b=>f(" "+a.dim(b)),cmd:b=>f(" "+a.cyan("$ "+b))},out:s,flags:t,env:r,fetchImpl:i,yes:!!t.yes,currentModel:u.model};try{let b=await jc(S);return b?(t["no-test"]||await ur(S,b,b.model),await p(b),0):(f("Nothing changed."),1)}catch(b){if(b.cancelled)return f(a.red("✖")+" Cancelled; nothing changed."),130;throw b}finally{m.close()}}throw new Error("unknown: leftoff models "+n+" (list, cloud, pull <tag>, use <tag> [--cloud|--lmstudio], test, pick)")}yf();ve();ai();il();yc();os();Dn();os();Tt();import wt from"node:fs";import Lt from"node:path";import{spawn as Ox,spawnSync as Kc}from"node:child_process";import $f from"node:fs";import Jw from"node:os";import Vi from"node:path";var Yf=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"],_x=n=>String(n).replace(/\x1b\[[0-9;]*[A-Za-z]/g,"");function Ex(n=process.env){return Vi.join(n.XDG_CONFIG_HOME||Vi.join(n.HOME||Jw.homedir(),".config"),"checkpoints","logs")}function qw(n=process.env,e=new Date){let t=e.getFullYear()+"-"+String(e.getMonth()+1).padStart(2,"0")+"-"+String(e.getDate()).padStart(2,"0");return Vi.join(Ex(n),"upgrade-"+t+".log")}var Wf=(n,e=process.env)=>{let t=e.HOME||Jw.homedir();return t&&n.startsWith(t+Vi.sep)?"~"+n.slice(t.length):n};function Vw({out:n=process.stdout,quiet:e=!0,c:t,logFile:s,tty:o=!!n.isTTY,env:r=process.env}){try{$f.mkdirSync(Vi.dirname(s),{recursive:!0})}catch{}let i=(p="")=>{try{$f.appendFileSync(s,_x(p)+`
350
353
  `)}catch{}},a=p=>n.write(p),l=null,c="",u=0,d=()=>{l&&(clearInterval(l),l=null,a("\r\x1B[2K"))};return{quiet:e,logFile:s,log:i,say:(p="")=>{i(p),e||a(p+`
351
354
  `)},print:(p="")=>{d(),i(p),a(p+`
352
- `)},start(p){c=p,i("== "+p),!(!e||!o)&&(d(),u=0,a(t.cyan(kf[0])+" "+p),l=setInterval(()=>{u=(u+1)%kf.length,a("\r\x1B[2K"+t.cyan(kf[u])+" "+c)},80),l.unref&&l.unref())},done(p){d(),i("✓ "+p),e&&a(t.green("✓")+" "+p+`
355
+ `)},start(p){c=p,i("== "+p),!(!e||!o)&&(d(),u=0,a(t.cyan(Yf[0])+" "+p),l=setInterval(()=>{u=(u+1)%Yf.length,a("\r\x1B[2K"+t.cyan(Yf[u])+" "+c)},80),l.unref&&l.unref())},done(p){d(),i("✓ "+p),e&&a(t.green("✓")+" "+p+`
353
356
  `)},warn(p){d(),i("! "+p),a(t.yellow("!")+" "+p+`
354
357
  `)},fail(p,{lines:h=30}={}){if(d(),i("✗ "+p),a(t.red("✗")+" "+p+`
355
- `),e){let m=[];try{m=Sf.readFileSync(s,"utf8").trimEnd().split(`
358
+ `),e){let m=[];try{m=$f.readFileSync(s,"utf8").trimEnd().split(`
356
359
  `).slice(-h)}catch{}m.length&&a(t.dim(m.map(y=>" │ "+y).join(`
357
360
  `))+`
358
- `)}a(t.dim("Full log: "+bf(s,r))+`
359
- `)},stop:d}}Zn();var Tc=n=>new Promise(e=>setTimeout(e,n)),fs=n=>n?String(n).slice(0,7):"unknown",j1=[{to:"0.3.0",note:'adds "updates": { "check": true }',run:n=>n.updates?n:{...n,updates:{check:!0,source:null}}}];function F1(n,e,t=q){let s=n&&typeof n=="object"?JSON.parse(JSON.stringify(n)):{},o=[];for(let r of j1)if((!e||Ye(r.to,e)>0)&&Ye(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 U1(n){let e=zr.map(s=>Et.join(n,s)).filter(s=>ht.existsSync(Et.join(s,"package.json")));if(!e.length)return Et.join(n,"checkpoints");let t=s=>{try{return JSON.parse(ht.readFileSync(Et.join(s,"package.json"),"utf8")).version||"0.0.0"}catch{return"0.0.0"}};return e.sort((s,o)=>Ye(t(o),t(s)))[0]}function dw(){return process.platform==="win32"?"npm.cmd":"npm"}function uw(n=process.env,e="npm"){let t=e==="pnpm"?process.platform==="win32"?"pnpm.cmd":"pnpm":dw(),s=_c(t,["root","-g"],{encoding:"utf8",env:n});return s.status===0?s.stdout.trim():null}async function H1(n){try{return Number(ht.readFileSync(Et.join(n,".daemon.pid"),"utf8"))||null}catch{return null}}async function vf(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 B1({root:n,bin:e,env:t=process.env,say:s=()=>{},dryRun:o=!1}){let r=X(n),i=Se(n),a=At(),l=!!(o||t.CHECKPOINTS_SERVICE_DRY_RUN),c=Bl({root:n,bin:e,dryRun:l});if(c&&s("Login service renamed: stopped "+c.from+", wrote "+c.to+(l?" (dry run: nothing stopped)":"")),ht.existsSync(a)){let d=En({root:Ed(ht.readFileSync(a,"utf8"))||n,bin:e});if(ht.readFileSync(a,"utf8")!==d&&(ht.writeFileSync(a,d),s("Login service now points at "+e)),l)return s("Would restart the login service: "+Ks().map(f=>f.join(" ")).join(" && ")),{how:"service",dryRun:!0,moved:c};for(let f of Ks()){let p=_c(f[0],f.slice(1),{encoding:"utf8"});if(p.status!==0&&f[1]!=="unload")return{how:"service",ok:!1,error:(p.stderr||"").trim().split(`
360
- `)[0]||"exit "+p.status}}}else{let d=await vf(r.port,i),f=await H1(n),p=m=>{try{return process.kill(m,0),!0}catch{return!1}};if(d.up&&f&&p(f)){try{process.kill(f,"SIGTERM")}catch{}for(let m=0;m<50&&p(f);m++)await Tc(200);if(p(f)){try{process.kill(f,"SIGKILL")}catch{}await Tc(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 vf(r.port,i)).up;m++)await Tc(200);let{startDaemonDetached:h}=await Promise.resolve().then(()=>(Ys(),tc));h({root:n,bin:e,env:t})}let u={up:!1};for(let d=0;d<60&&(u=await vf(r.port,i),!(u.up&&(!u.version||u.version===q)));d++)await Tc(250);return{how:ht.existsSync(a)?"service":"detached",ok:u.up&&(!u.version||u.version===q),version:u.version,moved:c}}async function fw({flags:n={},out:e=process.stdout}={}){let t=is(rs(e)),s=_o();return n.since&&(s=Eo(s,String(n.since).replace(/^v/,""),null)),n.latest&&(s=s.slice(0,1)),e.write(t.bold(je+" v"+q)+t.dim(" · what changed")+`
361
-
362
- `+Ga(s,{colors:t})),0}function xf(n,e,{env:t,log:s,timeoutMs:o=3e5}){return new Promise(r=>{s("$ "+n+" "+e.join(" "));let i="",a="",l=P1(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 hw({flags:n={},out:e=process.stdout,env:t=process.env}={}){let s=is(rs(e)),o=!n.verbose&&!n.check&&!(n.finish&&!n.quiet),r=cw({out:e,quiet:o,c:s,logFile:n.log?String(n.log):lw(t),tty:n.finish?t.LEFTOFF_PROGRESS_TTY==="1":!!e.isTTY&&!t.CI,env:t}),i=r.say,a=T=>i(s.green("✔")+" "+T),l=T=>{o?r.warn(K1(T)):i(s.yellow("!")+" "+T)},c=T=>o?r.fail(T):i(s.red("✖")+" "+T);if(n.finish||r.log(`
363
- # leftoff upgrade, `+new Date().toISOString()+(o?"":" (--verbose)")),n.finish)return Y1({flags:n,out:e,env:t,c:s,say:i,ok:a,warn:l,fail:c,P:r});let u=await Jr({env:t,globalRoot:t.CHECKPOINTS_NPM_GLOBAL_ROOT||void 0}),d=Oo(n.source||t.CHECKPOINTS_UPDATE_SOURCE||(X().updates||{}).source||u.source||Wr);u.kind==="npm"&&(d="npm:"+Qe),i(s.bold(je+" v"+u.version)+s.dim((u.kind==="npm"?" · installed from npm with "+u.pm+" at ":" ("+fs(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 qr({info:u,source:d,env:t});if(f.error&&u.kind==="npm")return c("Couldn't reach the npm registry ("+Wa(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:'+Xe+'.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&&Ye(f.remote.version,u.version)>0)&&!n.force)return o?r.done("Up to date: "+je+" "+u.version):a("Up to date"+s.dim(" (remote "+fs(f.remote.sha)+(f.remote.version?", v"+f.remote.version:"")+")")),0;let h=f.remote.version||fs(f.remote.sha);if(r.done("Found "+h+" (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(" "+fs(u.sha)+" → "+fs(f.remote.sha)))),f.incoming.length?i(`
364
- `+Ga(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 "+h+"…");let m=u.repoDir;if(u.kind==="npm"){let T=Yu(u.pm);i(s.dim("$ "+T.join(" ")));let b=await xf(T[0],T.slice(1),{env:t,log:r.log});if(b.status!==0)return o||i((b.stderr||"").trim()),c(T.slice(0,3).join(" ")+" failed: "+((b.stderr||"").trim().split(`
365
- `).pop()||"exit "+b.status)),1;let g=t.CHECKPOINTS_NPM_GLOBAL_ROOT||uw(t,u.pm);m=g&&ht.existsSync(Et.join(g,Qe,"package.json"))?Et.join(g,Qe):u.repoDir,a("Installed "+Qe+"@"+(f.remote.version||"latest")+s.dim(" → "+m))}else if(u.kind==="clone"){u.source&&Ps(u.source)!==u.source&&(await kt(["remote","set-url","origin",Ps(u.source)],{cwd:u.repoDir,env:t})).code===0&&i(s.dim("· the repo is now "+Xe+"; pointed origin at "+Ps(u.source))),i(s.dim("$ git -C "+u.repoDir+" pull --ff-only"));let T=await kt(["pull","--ff-only","-q"],{cwd:u.repoDir,env:t,timeoutMs:12e4});if(T.code!==0)return i(T.stderr.trim()),i(s.dim(" Local changes? Commit or stash them and run `leftoff upgrade` again.")),c("git pull failed: "+(T.stderr.trim().split(`
366
- `).pop()||"exit "+T.code)),1;a("Pulled "+fs(f.remote.sha))}else{let T=d.match(/^(?:https:\/\/github\.com\/|git@github\.com:)([\w.-]+\/[\w.-]+?)(?:\.git)?$/),b=T?"github:"+T[1]:d;i(s.dim("$ npm install -g "+b));let g=await xf(dw(),["install","-g",b,"--no-fund","--no-audit","--loglevel=error"],{env:t,log:r.log});if(g.status!==0)return o||i((g.stderr||"").trim()),c("npm install -g failed: "+((g.stderr||"").trim().split(`
367
- `).pop()||"exit "+g.status)),1;let _=t.CHECKPOINTS_NPM_GLOBAL_ROOT||uw(t);m=_?U1(_):u.repoDir,a("Installed "+b+s.dim(" → "+m));try{ht.existsSync(Et.join(m,".install.json"))||$u({sha:f.remote.sha,source:d,version:f.remote.version||void 0},m)}catch{}}r.done("Downloaded "+h),r.start("Updating the Claude Code plugin…");let y=null,w=Xl(t);if(u.kind==="npm")try{Kd({from:m,env:t})}catch(T){l("Couldn't refresh the mod's files: "+T.message)}if(!w)l("`claude` isn't on PATH; update the mod later with: claude plugin marketplace update "+on+" && claude plugin update "+Ce);else{let T=Ql(w,t);if(!T.installed&&T.legacyInstalled){i(s.dim("$ claude plugin marketplace update "+on));let b=Nn(w,["plugin","marketplace","update",on],{env:t});b.code!==0&&l("`claude plugin marketplace update "+on+"` failed: "+((b.stderr||b.stdout).trim().split(`
368
- `).pop()||"exit "+b.code))}else if(!T.installed)i(s.dim("· The mod isn't installed through `claude plugin`; nothing to update there.")),y="skip";else{let b=!1;for(let g of[["plugin","marketplace","update",on],["plugin","update",Ce]]){i(s.dim("$ claude "+g.join(" ")));let _=await xf(w,g,{env:{...t,CHECKPOINTS_INTERNAL:"1",LEFTOFF_INTERNAL:"1"},log:r.log,timeoutMs:12e4});if(_.status!==0){b=!0,l("`claude "+g.join(" ")+"` failed: "+((_.stderr||_.stdout).trim().split(`
369
- `).pop()||"exit "+_.status));break}}a("Mod updated"+s.dim(" · restart open Claude Code sessions to load it")),b||(y="ok")}}o&&(y==="ok"?r.done("Updated the Claude Code plugin"):y==="skip"?r.done("Claude Code plugin: not installed with `claude plugin`, nothing to update"):r.stop());let k=[Et.join(m,"bin","checkpoints.js"),"upgrade","--finish","--from="+u.version,"--from-sha="+(u.sha||""),"--repo="+m];if(n["no-restart"]&&k.push("--no-restart"),!o){let T=_c(process.execPath,k,{env:t,stdio:"inherit"});return T.status==null?1:T.status}k.push("--quiet","--log="+r.logFile);let v=_c(process.execPath,k,{env:{...t,LEFTOFF_PROGRESS_TTY:e.isTTY&&!t.CI?"1":"0"},stdio:["ignore","inherit","pipe"],encoding:"utf8"});return v.stderr&&r.log(v.stderr.trimEnd()),v.status!==0?(r.fail("Finishing the upgrade failed (exit "+v.status+")"),v.status==null?1:v.status):0}var K1=n=>String(n).replace(/\x1b\[2m.*?\x1b\[22m/g,"").trim();function G1(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 $1(n,e,t=q){let s=[];for(let[o,r]of Object.entries(Bs(n)))o!==e&&r.valid&&r.card.version&&Ye(r.card.version,t)<0&&s.push(o+" ("+r.card.version+")");return s.sort()}async function Y1({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||Wn),f=await Jr({repoDir:d,env:t}),p=V(),h=fn(p);if(h){let{cfg:S,applied:k}=F1(h,u,q);if(k.length){ht.writeFileSync(we(p).config,JSON.stringify(S,null,2)+`
370
- `);for(let v of k)r("Config: "+v.note)}}let m=ht.existsSync(Et.join(d,"bin","leftoff.js"))?Et.join(d,"bin","leftoff.js"):Et.join(d,"bin","checkpoints.js");try{if(gi()){let S=ht.readFileSync(sn(),"utf8");Ko(void 0,{bin:m}),S.includes(Ds)&&r("Settings hooks now run `leftoff`"+s.dim(" (replaced the checkpoints-hook entries)"))}}catch(S){i("Couldn't refresh the settings hooks: "+S.message)}let y=Xl(t);if(y){let S;try{S=Gd({bin:y,env:t})}catch(k){S={ok:!1,error:k.message}}S.migrated&&S.ok?r("Mod renamed: "+as+" → "+Ce+(S.enabled?"":s.dim(" (kept disabled, as it was)"))+s.dim(" · restart open Claude Code sessions to load it")):S.ok||i("Couldn't move the mod to its new name: "+S.error+s.dim(" · run: claude plugin install "+Ce+" && claude plugin uninstall "+as))}if(!n["no-restart"]){l.start("Restarting the daemon…");let S=await B1({root:p,bin:m,env:t,say:k=>o(s.dim("· "+k))});if(S.dryRun)l.stop();else if(S.ok){r("Daemon restarted"+s.dim(" ("+S.how+", v"+(S.version||q)+")")),l.done("Restarted the daemon");try{let k=JSON.parse(ht.readFileSync(Et.join(p,".cleanup."+le(X(p)).replace(/[^\w.-]/g,"_")+".json"),"utf8"));k.lastArchived&&Date.now()-Date.parse(k.lastArchivedAt)<5*6e4&&r("Archived "+k.lastArchived+" empty "+(k.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: "+(S.error||"it didn't come back")+s.dim(" · leftoff serve"))}if(c){let S=Eo(_o(),u||null,q);l.print(""),l.print(s.bold(je+" "+(u?u+" → ":"")+q)+" "+s.green("✓"));for(let v of G1(S))l.print(" • "+v);l.print(s.dim("Full notes: leftoff changelog (also What’s new on the board) · log: "+bf(l.logFile,t)));let k=$1(p,le(X(p)));return k.length&&l.print(s.yellow("!")+" Upgrade your other Mac"+(k.length>1?"s":"")+" too: "+k.join(", ")),0}o(),o(s.bold(je+" "+(u?"v"+u+" → ":"")+"v"+q)+(n["from-sha"]||f.sha?s.dim(" "+fs(n["from-sha"])+" → "+fs(f.sha)):""));let w=Eo(_o(),u||null,q);if(w.length&&o(`
371
- `+Ga(w,{colors:s})),f.kind==="clone"&&n["from-sha"]){let S=await kt(["log","--format=%h %s",String(n["from-sha"])+"..HEAD"],{cwd:d,env:t});if(S.code===0&&S.stdout.trim()){o(s.bold("Commits"));for(let k of S.stdout.trim().split(`
372
- `).slice(0,30))o(" "+s.dim(k.slice(0,7))+k.slice(7))}}return o(s.dim("The board shows these under What's new. `leftoff changelog` prints them again.")),0}ke();Sl();ki();St();import Ec from"node:fs";import pw from"node:path";import W1 from"node:https";import{spawnSync as z1}from"node:child_process";var Af="/Applications/Tailscale.app/Contents/MacOS/Tailscale";function kw({env:n=process.env,platform:e=process.platform,exists:t=s=>{try{return Ec.accessSync(s,Ec.constants.X_OK),Ec.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(pw.delimiter).filter(Boolean))for(let r of s){let i=pw.join(o,r);if(t(i))return i}return e==="darwin"&&t(Af)?Af:null}function bw(n,e,{inherit:t=!1,timeoutMs:s=15e3}={}){let o=z1(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 Tf(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 _f(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 J1=n=>"http://127.0.0.1:"+n,Ef=(n,e)=>!!n&&new RegExp("^(https?://)?(127\\.0\\.0\\.1|localhost):"+e+"/?$").test(n)||String(n)===String(e);async function Cf(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 mw({root:n,port:e,token:t,add:s=[],remove:o=[],requireToken:r}){let i=await Cf(e,t,"POST","/v1/remote/hosts",{add:s,remove:o,requireToken:r});if(i.ok)return{live:!0,hosts:i.body.hosts};let a=fn(n)||{},l=new Set(o.map(Lt)),c=(a.remoteHosts||[]).map(Lt).filter(d=>d&&!l.has(d));for(let d of s.map(Lt))d&&!c.includes(d)&&c.push(d);a.remoteHosts=c,typeof r=="boolean"&&(a.remoteRequireToken=r),no(a,n);let u=!1;return i.up&&(u=!!(await Cf(e,t,"POST","/v1/remote/reload")).ok),{live:u,daemonUp:!!i.up,status:i.status||null,hosts:c}}function gw(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=Ec.readFileSync(t.CHECKPOINTS_REMOTE_CHECK_CA)}catch{}let a=Date.now(),l=W1.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===je)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 p=d&&d.error&&d.error.code||c.headers["x-leftoff-error"]||null;if(p==="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(p==="funnel_refused")return s({ok:!1,kind:"funnel",message:"the request came through Tailscale Funnel"});if(c.statusCode===404&&p==="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+(p?" "+p:"")})})});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 yw(n,e){return n.ok?"✓ checked "+e+" end to end from this machine: "+je+" "+(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 ww(n={},e=process.stdout){return n["no-qr"]||n.qr==="false"||n.qr==="0"?!1:n.qr?!0:!!e.isTTY}function Sw(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(Wl(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: "+ml(s)),n("The link and the code each work once, until "+l+" ("+a+" min). Another device later: leftoff link")}var Cc="Tailscale Funnel (public internet) is intentionally unsupported: the board stays inside your tailnet.";function Bi(){return`leftoff tailscale — view the board from your phone or other machines on your tailnet
361
+ `)}a(t.dim("Full log: "+Wf(s,r))+`
362
+ `)},stop:d}}ns();var Gc=n=>new Promise(e=>setTimeout(e,n)),ys=n=>n?String(n).slice(0,7):"unknown",Cx=[{to:"0.3.0",note:'adds "updates": { "check": true }',run:n=>n.updates?n:{...n,updates:{check:!0,source:null}}}];function Nx(n,e,t=V){let s=n&&typeof n=="object"?JSON.parse(JSON.stringify(n)):{},o=[];for(let r of Cx)if((!e||Je(r.to,e)>0)&&Je(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 Ix(n){let e=oi.map(s=>Lt.join(n,s)).filter(s=>wt.existsSync(Lt.join(s,"package.json")));if(!e.length)return Lt.join(n,"checkpoints");let t=s=>{try{return JSON.parse(wt.readFileSync(Lt.join(s,"package.json"),"utf8")).version||"0.0.0"}catch{return"0.0.0"}};return e.sort((s,o)=>Je(t(o),t(s)))[0]}function Xw(){return process.platform==="win32"?"npm.cmd":"npm"}function Zw(n=process.env,e="npm"){let t=e==="pnpm"?process.platform==="win32"?"pnpm.cmd":"pnpm":Xw(),s=Kc(t,["root","-g"],{encoding:"utf8",env:n});return s.status===0?s.stdout.trim():null}async function Mx(n){try{return Number(wt.readFileSync(Lt.join(n,".daemon.pid"),"utf8"))||null}catch{return null}}async function zf(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 Lx({root:n,bin:e,env:t=process.env,say:s=()=>{},dryRun:o=!1}){let r=X(n),i=be(n),a=Nt(),l=!!(o||t.CHECKPOINTS_SERVICE_DRY_RUN),c=oc({root:n,bin:e,dryRun:l});if(c&&s("Login service renamed: stopped "+c.from+", wrote "+c.to+(l?" (dry run: nothing stopped)":"")),wt.existsSync(a)){let d=In({root:Zd(wt.readFileSync(a,"utf8"))||n,bin:e});if(wt.readFileSync(a,"utf8")!==d&&(wt.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 p=Kc(f[0],f.slice(1),{encoding:"utf8"});if(p.status!==0&&f[1]!=="unload")return{how:"service",ok:!1,error:(p.stderr||"").trim().split(`
363
+ `)[0]||"exit "+p.status}}}else{let d=await zf(r.port,i),f=await Mx(n),p=m=>{try{return process.kill(m,0),!0}catch{return!1}};if(d.up&&f&&p(f)){try{process.kill(f,"SIGTERM")}catch{}for(let m=0;m<50&&p(f);m++)await Gc(200);if(p(f)){try{process.kill(f,"SIGKILL")}catch{}await Gc(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 zf(r.port,i)).up;m++)await Gc(200);let{startDaemonDetached:h}=await Promise.resolve().then(()=>(Xs(),wc));h({root:n,bin:e,env:t})}let u={up:!1};for(let d=0;d<60&&(u=await zf(r.port,i),!(u.up&&(!u.version||u.version===V)));d++)await Gc(250);return{how:wt.existsSync(a)?"service":"detached",ok:u.up&&(!u.version||u.version===V),version:u.version,moved:c}}async function Qw({flags:n={},out:e=process.stdout}={}){let t=ds(us(e)),s=Ro();return n.since&&(s=Po(s,String(n.since).replace(/^v/,""),null)),n.latest&&(s=s.slice(0,1)),e.write(t.bold(He+" v"+V)+t.dim(" · what changed")+`
364
+
365
+ `+rl(s,{colors:t})),0}function Jf(n,e,{env:t,log:s,timeoutMs:o=3e5}){return new Promise(r=>{s("$ "+n+" "+e.join(" "));let i="",a="",l=Ox(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 e0({flags:n={},out:e=process.stdout,env:t=process.env}={}){let s=ds(us(e)),o=!n.verbose&&!n.check&&!(n.finish&&!n.quiet),r=Vw({out:e,quiet:o,c:s,logFile:n.log?String(n.log):qw(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(Dx(v)):i(s.yellow("!")+" "+v)},c=v=>o?r.fail(v):i(s.red("✖")+" "+v);if(n.finish||r.log(`
366
+ # leftoff upgrade, `+new Date().toISOString()+(o?"":" (--verbose)")),n.finish)return jx({flags:n,out:e,env:t,c:s,say:i,ok:a,warn:l,fail:c,P:r});let u=await ri({env:t,globalRoot:t.CHECKPOINTS_NPM_GLOBAL_ROOT||void 0}),d=Fo(n.source||t.CHECKPOINTS_UPDATE_SOURCE||(X().updates||{}).source||u.source||si);u.kind==="npm"&&(d="npm:"+nt),i(s.bold(He+" v"+u.version)+s.dim((u.kind==="npm"?" · installed from npm with "+u.pm+" at ":" ("+ys(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 ii({info:u,source:d,env:t});if(f.error&&u.kind==="npm")return c("Couldn't reach the npm registry ("+ll(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:'+tt+'.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&&Je(f.remote.version,u.version)>0)&&!n.force)return o?r.done("Up to date: "+He+" "+u.version):a("Up to date"+s.dim(" (remote "+ys(f.remote.sha)+(f.remote.version?", v"+f.remote.version:"")+")")),0;let h=f.remote.version||ys(f.remote.sha);if(r.done("Found "+h+" (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(" "+ys(u.sha)+" → "+ys(f.remote.sha)))),f.incoming.length?i(`
367
+ `+rl(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 "+h+"…");let m=u.repoDir;if(u.kind==="npm"){let v=md(u.pm);i(s.dim("$ "+v.join(" ")));let k=await Jf(v[0],v.slice(1),{env:t,log:r.log});if(k.status!==0)return o||i((k.stderr||"").trim()),c(v.slice(0,3).join(" ")+" failed: "+((k.stderr||"").trim().split(`
368
+ `).pop()||"exit "+k.status)),1;let g=t.CHECKPOINTS_NPM_GLOBAL_ROOT||Zw(t,u.pm);m=g&&wt.existsSync(Lt.join(g,nt,"package.json"))?Lt.join(g,nt):u.repoDir,a("Installed "+nt+"@"+(f.remote.version||"latest")+s.dim(" → "+m))}else if(u.kind==="clone"){u.source&&$s(u.source)!==u.source&&(await _t(["remote","set-url","origin",$s(u.source)],{cwd:u.repoDir,env:t})).code===0&&i(s.dim("· the repo is now "+tt+"; pointed origin at "+$s(u.source))),i(s.dim("$ git -C "+u.repoDir+" pull --ff-only"));let v=await _t(["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(`
369
+ `).pop()||"exit "+v.code)),1;a("Pulled "+ys(f.remote.sha))}else{let v=d.match(/^(?:https:\/\/github\.com\/|git@github\.com:)([\w.-]+\/[\w.-]+?)(?:\.git)?$/),k=v?"github:"+v[1]:d;i(s.dim("$ npm install -g "+k));let g=await Jf(Xw(),["install","-g",k,"--no-fund","--no-audit","--loglevel=error"],{env:t,log:r.log});if(g.status!==0)return o||i((g.stderr||"").trim()),c("npm install -g failed: "+((g.stderr||"").trim().split(`
370
+ `).pop()||"exit "+g.status)),1;let _=t.CHECKPOINTS_NPM_GLOBAL_ROOT||Zw(t);m=_?Ix(_):u.repoDir,a("Installed "+k+s.dim(" → "+m));try{wt.existsSync(Lt.join(m,".install.json"))||pd({sha:f.remote.sha,source:d,version:f.remote.version||void 0},m)}catch{}}r.done("Downloaded "+h),r.start("Updating the Claude Code plugin…");let y=null,S=mc(t);if(u.kind==="npm")try{pf({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 "+cn+" && claude plugin update "+Ne);else{let v=gc(S,t);if(!v.installed&&v.legacyInstalled){i(s.dim("$ claude plugin marketplace update "+cn));let k=Rn(S,["plugin","marketplace","update",cn],{env:t});k.code!==0&&l("`claude plugin marketplace update "+cn+"` failed: "+((k.stderr||k.stdout).trim().split(`
371
+ `).pop()||"exit "+k.code))}else if(!v.installed)i(s.dim("· The mod isn't installed through `claude plugin`; nothing to update there.")),y="skip";else{let k=!1;for(let g of[["plugin","marketplace","update",cn],["plugin","update",Ne]]){i(s.dim("$ claude "+g.join(" ")));let _=await Jf(S,g,{env:{...t,CHECKPOINTS_INTERNAL:"1",LEFTOFF_INTERNAL:"1"},log:r.log,timeoutMs:12e4});if(_.status!==0){k=!0,l("`claude "+g.join(" ")+"` failed: "+((_.stderr||_.stdout).trim().split(`
372
+ `).pop()||"exit "+_.status));break}}a("Mod updated"+s.dim(" · restart open Claude Code sessions to load it")),k||(y="ok")}}o&&(y==="ok"?r.done("Updated the Claude Code plugin"):y==="skip"?r.done("Claude Code plugin: not installed with `claude plugin`, nothing to update"):r.stop());let w=[Lt.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=Kc(process.execPath,w,{env:t,stdio:"inherit"});return v.status==null?1:v.status}w.push("--quiet","--log="+r.logFile);let A=Kc(process.execPath,w,{env:{...t,LEFTOFF_PROGRESS_TTY:e.isTTY&&!t.CI?"1":"0"},stdio:["ignore","inherit","pipe"],encoding:"utf8"});return A.stderr&&r.log(A.stderr.trimEnd()),A.status!==0?(r.fail("Finishing the upgrade failed (exit "+A.status+")"),A.status==null?1:A.status):0}var Dx=n=>String(n).replace(/\x1b\[2m.*?\x1b\[22m/g,"").trim();function Rx(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 Px(n,e,t=V){let s=[];for(let[o,r]of Object.entries(Js(n)))o!==e&&r.valid&&r.card.version&&Je(r.card.version,t)<0&&s.push(o+" ("+r.card.version+")");return s.sort()}async function jx({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||Zn),f=await ri({repoDir:d,env:t}),p=Z(),h=gn(p);if(h){let{cfg:b,applied:w}=Nx(h,u,V);if(w.length){wt.writeFileSync(ke(p).config,JSON.stringify(b,null,2)+`
373
+ `);for(let A of w)r("Config: "+A.note)}}let m=wt.existsSync(Lt.join(d,"bin","leftoff.js"))?Lt.join(d,"bin","leftoff.js"):Lt.join(d,"bin","checkpoints.js");try{if(_i()){let b=wt.readFileSync(ln(),"utf8");Xo(void 0,{bin:m}),b.includes(Gs)&&r("Settings hooks now run `leftoff`"+s.dim(" (replaced the checkpoints-hook entries)"))}}catch(b){i("Couldn't refresh the settings hooks: "+b.message)}let y=mc(t);if(y){let b;try{b=mf({bin:y,env:t})}catch(w){b={ok:!1,error:w.message}}b.migrated&&b.ok?r("Mod renamed: "+fs+" → "+Ne+(b.enabled?"":s.dim(" (kept disabled, as it was)"))+s.dim(" · restart open Claude Code sessions to load it")):b.ok||i("Couldn't move the mod to its new name: "+b.error+s.dim(" · run: claude plugin install "+Ne+" && claude plugin uninstall "+fs))}if(!n["no-restart"]){l.start("Restarting the daemon…");let b=await Lx({root:p,bin:m,env:t,say:w=>o(s.dim("· "+w))});if(b.dryRun)l.stop();else if(b.ok){r("Daemon restarted"+s.dim(" ("+b.how+", v"+(b.version||V)+")")),l.done("Restarted the daemon");try{let w=JSON.parse(wt.readFileSync(Lt.join(p,".cleanup."+de(X(p)).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: "+(b.error||"it didn't come back")+s.dim(" · leftoff serve"))}if(c){let b=Po(Ro(),u||null,V);l.print(""),l.print(s.bold(He+" "+(u?u+" → ":"")+V)+" "+s.green("✓"));for(let A of Rx(b))l.print(" • "+A);l.print(s.dim("Full notes: leftoff changelog (also What’s new on the board) · log: "+Wf(l.logFile,t)));let w=Px(p,de(X(p)));return w.length&&l.print(s.yellow("!")+" Upgrade your other Mac"+(w.length>1?"s":"")+" too: "+w.join(", ")),0}o(),o(s.bold(He+" "+(u?"v"+u+" → ":"")+"v"+V)+(n["from-sha"]||f.sha?s.dim(" "+ys(n["from-sha"])+" → "+ys(f.sha)):""));let S=Po(Ro(),u||null,V);if(S.length&&o(`
374
+ `+rl(S,{colors:s})),f.kind==="clone"&&n["from-sha"]){let b=await _t(["log","--format=%h %s",String(n["from-sha"])+"..HEAD"],{cwd:d,env:t});if(b.code===0&&b.stdout.trim()){o(s.bold("Commits"));for(let w of b.stdout.trim().split(`
375
+ `).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}ve();Pl();Ni();Tt();import $c from"node:fs";import t0 from"node:path";import Fx from"node:https";import{spawnSync as Ux}from"node:child_process";var qf="/Applications/Tailscale.app/Contents/MacOS/Tailscale";function a0({env:n=process.env,platform:e=process.platform,exists:t=s=>{try{return $c.accessSync(s,$c.constants.X_OK),$c.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(t0.delimiter).filter(Boolean))for(let r of s){let i=t0.join(o,r);if(t(i))return i}return e==="darwin"&&t(qf)?qf:null}function l0(n,e,{inherit:t=!1,timeoutMs:s=15e3}={}){let o=Ux(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 Vf(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 Zf(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 Hx=n=>"http://127.0.0.1:"+n,Xf=(n,e)=>!!n&&new RegExp("^(https?://)?(127\\.0\\.0\\.1|localhost):"+e+"/?$").test(n)||String(n)===String(e);async function Qf(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 n0({root:n,port:e,token:t,add:s=[],remove:o=[],requireToken:r}){let i=await Qf(e,t,"POST","/v1/remote/hosts",{add:s,remove:o,requireToken:r});if(i.ok)return{live:!0,hosts:i.body.hosts};let a=gn(n)||{},l=new Set(o.map(Ft)),c=(a.remoteHosts||[]).map(Ft).filter(d=>d&&!l.has(d));for(let d of s.map(Ft))d&&!c.includes(d)&&c.push(d);a.remoteHosts=c,typeof r=="boolean"&&(a.remoteRequireToken=r),fo(a,n);let u=!1;return i.up&&(u=!!(await Qf(e,t,"POST","/v1/remote/reload")).ok),{live:u,daemonUp:!!i.up,status:i.status||null,hosts:c}}function s0(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=$c.readFileSync(t.CHECKPOINTS_REMOTE_CHECK_CA)}catch{}let a=Date.now(),l=Fx.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===He)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 p=d&&d.error&&d.error.code||c.headers["x-leftoff-error"]||null;if(p==="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(p==="funnel_refused")return s({ok:!1,kind:"funnel",message:"the request came through Tailscale Funnel"});if(c.statusCode===404&&p==="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+(p?" "+p:"")})})});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 o0(n,e){return n.ok?"✓ checked "+e+" end to end from this machine: "+He+" "+(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 r0(n={},e=process.stdout){return n["no-qr"]||n.qr==="false"||n.qr==="0"?!1:n.qr?!0:!!e.isTTY}function i0(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(cc(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: "+Ml(s)),n("The link and the code each work once, until "+l+" ("+a+" min). Another device later: leftoff link")}var Yc="Tailscale Funnel (public internet) is intentionally unsupported: the board stays inside your tailnet.";function Zi(){return`leftoff tailscale — view the board from your phone or other machines on your tailnet
373
376
 
374
377
  leftoff tailscale on [--https-port=443] [--force] [--no-login|--login]
375
378
  runs \`tailscale serve --bg --https=443 http://127.0.0.1:<port>\`, adds this
@@ -379,21 +382,21 @@ data: `+JSON.stringify(g)+`
379
382
  leftoff tailscale off removes the serve entry and the name, signs out remote browsers
380
383
  leftoff tailscale status tailscale, serve and Leftoff's side of it
381
384
  leftoff tailscale link a fresh sign-in: a QR code (one browser) and a typed code (the
382
- (or: leftoff link) home-screen app), each single-use, ${Hs/6e4} minutes
385
+ (or: leftoff link) home-screen app), each single-use, ${zs/6e4} minutes
383
386
  --qr / --no-qr print the QR code (default: only when the output is a terminal)
384
387
  --no-check on/status: skip opening the URL from this machine
385
388
  leftoff tailscale revoke sign out every remote browser
386
389
 
387
- ${Cc}
390
+ ${Yc}
388
391
  No Tailscale? An SSH tunnel works too: ssh -N -L 7429:127.0.0.1:7429 you@this-machine
389
- `}function vw(n,e){return(n||[]).map(hl).filter(t=>t&&t.name===e)}function Of({env:n=process.env,platform:e=process.platform,run:t=bw,port:s,cfgHosts:o=[]}={}){let r=kw({env:n,platform:e});if(!r)return{installed:!1};let i=t(r,["status","--json"],{timeoutMs:5e3}),a=i.code===0?Tf(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(`
390
- `)[0]||"tailscale status failed"};if(!a||!a.dnsName)return l;let c=vw(o,a.dnsName),u=c.length&&c[0].port||443,d=t(r,["serve","status","--json"],{timeoutMs:5e3}),f=d.code===0?_f(d.stdout,a.dnsName,u):{parsed:!1};return{...l,httpsPort:u,serve:f,serving:!!(f.proxy&&Ef(f.proxy,s)),listed:c.length>0,url:"https://"+a.dnsName+(u===443?"":":"+u)+"/"}}function If(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 Nf({sub:n="status",flags:e={},out:t=process.stdout,env:s=process.env,platform:o=process.platform,run:r=bw}={}){let i=(h="")=>t.write(h+`
391
- `);if(["help","--help","-h"].includes(n))return i(Bi()),0;if(n==="funnel")return i(Cc),i("Use: leftoff tailscale on (tailscale serve, tailnet only)"),1;let a=V(),l=X(a),c=Number(e.port||l.port),u=Se(a),d=l.remoteHosts||[];if(n==="revoke"){let h=li(a);return i("Signed out "+h+" remote browser"+(h===1?"":"s")+". Unused login links are void too."),0}let f=kw({env:s,platform:o}),p=()=>(i("Tailscale CLI not found (looked on PATH"+(o==="darwin"?" and in "+Af:"")+")."),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(y=>"https://"+Lt(y)+"/").join(" ")),0;Te(a);let h=ai(a),m="https://"+Lt(d[0])+"/";Sw(i,{base:m,link:m+"?t="+h.code,code:h.signInCode,expiresAt:h.expiresAt,qr:ww(e,t),color:!e["no-color"]&&!s.NO_COLOR});for(let y of d.slice(1))i(" also: https://"+Lt(y)+"/?t="+h.code);return 0}if(n==="status"){if(!f)return p(),i(""),i("remoteHosts: "+(d.length?d.join(", "):"(none)")),1;let h=Of({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(`
392
- `)[0]+")":"")),i("state "+(h.state||h.error||"unknown")+(h.dnsName?" · "+h.dnsName:"")),h.dnsName){let S=h.serve||{};i("serve "+(S.parsed?S.proxy?"https://"+h.dnsName+(h.httpsPort===443?"":":"+h.httpsPort)+"/ → "+S.proxy+(h.serving?"":" (not this daemon, port "+c+")"):"nothing on https:"+h.httpsPort:"couldn't read tailscale serve status")),S.funnel&&i("! Funnel is ON for this port. Leftoff refuses Funnel traffic; turn it off: tailscale funnel --https="+h.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 y=wl(a);i("browsers "+y.sessions.length+" signed in (30 days from each one's last visit)"+(y.pendingLinks?", "+y.pendingLinks+" unused link"+(y.pendingLinks===1?"":"s"):"")+(y.pendingCodes?", "+y.pendingCodes+" unused code"+(y.pendingCodes===1?"":"s"):""));let w=await Cf(c,u,"GET","/v1/remote");return i("daemon "+(w.up?w.ok?"answers "+(w.body.hosts.length?w.body.hosts.join(", "):"loopback only"):"running but too old for remote access (restart it)":"not running")),i(If(h,{cfgHosts:d})),h.serving&&h.listed&&!e["no-check"]&&s.CHECKPOINTS_REMOTE_CHECK!=="off"&&i(yw(await gw(h.url,{timeoutMs:15e3,env:s}),h.url)),0}if(n==="on"){if(!f)return p();let h=r(f,["status","--json"],{timeoutMs:8e3}),m=h.code===0?Tf(h.stdout):null;if(!m)return i("`tailscale status --json` failed: "+((h.stderr||h.stdout).trim()||"no output")+`
393
- 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 y=Number(e["https-port"]||443),w=r(f,["serve","status","--json"],{timeoutMs:8e3}),S=w.code===0?_f(w.stdout,m.dnsName,y):{parsed:!1},k=!!(S.proxy&&Ef(S.proxy,c));if(S.funnel)return i("Funnel is ON for "+m.dnsName+":"+y+", which would put this port on the public internet. "+Cc+`
394
- Turn it off first: tailscale funnel --https=`+y+" off"),1;if((S.proxy||S.other)&&!k&&!e.force)return i("https://"+m.dnsName+(y===443?"":":"+y)+"/ is already served by tailscale ("+(S.proxy||"a file or text handler")+")."),i("Use another port (leftoff tailscale on --https-port=8443) or replace it (--force)."),1;let v=["serve","--bg","--https="+y,J1(c)];if(k)i("✓ tailscale serve already proxies https://"+m.dnsName+"/ to "+S.proxy);else{i("Running: tailscale "+v.join(" "));let x=r(f,v,{inherit:!0});if(x.code!==0)return i("tailscale serve failed (exit "+x.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 T=m.dnsName+(y===443?"":":"+y);Te(a);let b=await mw({root:a,port:c,token:u,add:[T],requireToken:e["no-login"]?!1:e.login?!0:void 0});i((b.live||!b.daemonUp?"✓ ":"! ")+T+" 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 g="https://"+T+"/",_=e["no-login"]?!1:X(a).remoteRequireToken!==!1;if(!e["no-check"]&&s.CHECKPOINTS_REMOTE_CHECK!=="off"){i("Checking "+g+" from this machine (the first HTTPS request can take up to a minute while Tailscale gets the certificate)…");let x=await gw(g,{env:s});i(yw(x,g)),!x.ok&&x.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 x=ai(a);Sw(i,{base:g,link:g+"?t="+x.code,code:x.signInCode,expiresAt:x.expiresAt,qr:ww(e,t),color:!e["no-color"]&&!s.NO_COLOR})}else i("Board on your tailnet: "+g),i("remoteRequireToken is off: anyone on your tailnet who can reach this URL gets the board.");return i("Turn it off: leftoff tailscale off · "+Cc),0}if(n==="off"){let h=null;if(f){let k=r(f,["status","--json"],{timeoutMs:8e3}),v=k.code===0?Tf(k.stdout):null;h=v&&v.dnsName}let m=h?vw(d,h):[],y=m.length?m.map(k=>k.port||443):[Number(e["https-port"]||443)];if(f&&h)for(let k of y){let v=r(f,["serve","status","--json"],{timeoutMs:8e3}),T=v.code===0?_f(v.stdout,h,k):{parsed:!1};if(T.proxy&&Ef(T.proxy,c)){let b=["serve","--https="+k,"off"];i("Running: tailscale "+b.join(" "));let g=r(f,b,{inherit:!0});g.code!==0&&i("! tailscale serve off failed (exit "+g.code+"); check: tailscale serve status")}else T.proxy&&i("· leaving https:"+k+" alone: it proxies to "+T.proxy+", not this daemon")}else f||i("· tailscale CLI not found; removing Leftoff's side only");let w=e.all?d:m.length?m.map(k=>k.name+(k.port&&k.port!==443?":"+k.port:"")):d;await mw({root:a,port:c,token:u,remove:w});let S=li(a);return i("✓ remote access off: "+(w.length?w.join(", ")+" removed from remoteHosts":"no remoteHosts to remove")+(S?", "+S+" remote browser"+(S===1?"":"s")+" signed out":"")),0}return i(Bi()),1}Ms();ui();Fo();In();ve();function Ki(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 hs(n){return n.title||"(untitled)"}function Mf(n,e){return[n.repoLabel||n.repoName,n.repo&&n.repo.branch,n.status,n.idleSec!=null?"idle "+Ki(n.lastActivityAt,e).replace(" ago",""):null,"cp:"+(n.shortId||Ke(n.id)),n.terminal?n.terminal.label:null].filter(Boolean).join(" · ")}function q1(n){return n.filter(e=>e.isLocal!==!1&&e.live!=="closed"&&!e.endedAt)}async function xw({pos:n=[],flags:e={},api:t,out:s=process.stdout,err:o=process.stderr,input:r=process.stdin,jumpRunner:i=El,now:a=Date.now(),env:l=process.env}={}){let c=k=>s.write(k+`
395
- `);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(k){let v=/fetch failed|ECONNREFUSED|abort|timed? ?out/i.test(String(k.message))?"The Leftoff daemon isn't running. Start it with: leftoff serve (or leftoff status)":k.message;return o.write((e.plain?"":ee.red("✖ "))+v+`
396
- `),e.plain&&c(v),1}let d=e.id?u:q1(u);if(e.repo===!0&&(e={...e,repo:n.shift()}),typeof e.repo=="string"&&!e.id){let k=d;if(d=e.repo.split(",").flatMap(v=>Ia(k,v.trim())).filter((v,T,b)=>b.indexOf(v)===T),!d.length)return c('No open session in a repo matching "'+e.repo+'".'+(k.length?" Open repos: "+[...new Set(k.map(v=>v.repoLabel||v.repoName))].join(", "):"")),2}if(e.list){d.length||c("No open sessions on this machine.");for(let k of d)c((k.live==="open"?ee.green("●"):ee.dim("○"))+" "+hs(k)+" "+ee.dim(Mf(k,a)));return 0}let f=n.join(" ").trim(),p=null;if(e.id)p=d[0];else{let k=f?rg(f,d):d;if(!k.length)return c(f?'No open session matches "'+f+'".'+(d.length?" Open: "+d.slice(0,5).map(hs).join(", "):""):"No open sessions on this machine."),2;if(k.length===1)p=k[0];else if(r.isTTY&&s.isTTY){let v=await zg(f?'Sessions matching "'+f+'"':"Open Claude Code sessions",k.map(T=>({label:hs(T),hint:Mf(T,a),value:T.id,search:[T.title,T.repoLabel,T.repoName,T.repoKey,T.repo&&T.repo.branch,T.cwd,T.id].filter(Boolean).join(" ")})),{input:r,output:s});if(!v)return 2;p=k.find(T=>T.id===v)}else{c((f?k.length+' sessions match "'+f+'"':k.length+" open sessions")+"; be more specific, or use the id:");for(let v of k.slice(0,10))c(" "+hs(v)+" "+ee.dim(Mf(v,a)));return 2}}if(!p)return c("No such session."),2;if(p.live==="closed")return c('"'+U(hs(p),60)+'" is closed. Resume it with: '+p.resumeCommand),3;let h=p.terminal&&p.terminal.label||p.term&&p.term.label||"its terminal";e.plain||c(ee.dim("Switching to “"+U(hs(p),60)+"” in "+h+"…"));let m=Date.now(),y;if(e.daemon)try{y=await t("POST","/v1/sessions/"+encodeURIComponent(p.id)+"/go",{})}catch(k){y={ok:!1,message:k.message}}else{let k;try{let T=await t("GET","/v1/sessions/"+encodeURIComponent(p.id)+"/where");T.isLocal?T.term?k=Uo(T):k={supported:!1,reason:"No terminal was recorded for this session (it started before v0.4, or without the mod). Restart it to get Go."}:k={supported:!1,reason:"This session runs on "+T.machine+"; Go only works there"}}catch(T){k={supported:!1,reason:T.message}}let v=Rn(l);y=await i(k,{ghostty:{activate:v.kind==="ghostty"?"never":"auto"}})}let w=Date.now()-m,S=" in "+(w<1e4?(w/1e3).toFixed(2):Math.round(w/1e3))+" s";if(e.debug){c(ee.dim(" total "+w+" ms"+(y.elapsedMs!=null?" (jump "+y.elapsedMs+" ms"+(e.daemon?", inside the daemon":"")+")":"")));for(let k of y.steps||[])c(ee.dim(" · "+k.step+": "+k.ms+" ms"+(k.result?" → "+k.result:"")))}return e.plain?(c(y.ok?"Switched to “"+U(hs(p),50)+"”"+S:y.message),y.ok?0:3):y.ok?(c(ee.green("✔")+" "+hs(p)+ee.dim(" ("+String(y.message||"").replace(/^Switched to /,"")+S+")")),0):(c(ee.red("✖")+" "+y.message+ee.dim(S+(y.steps&&y.steps.length&&!e.debug?" · "+Al(y.steps):""))),3)}function Aw(n=process.env){let e=Rn(n),t=Uo({id:"x",term:e,tty:"/dev/ttys000",cwd:"/"});return{term:e,supported:t.supported,reason:t.reason||null}}In();ve();ui();Md();Fo();var Tw=`\`leftoff restore\` (v0.4.7): reopen the Claude Code sessions that were open
392
+ `}function c0(n,e){return(n||[]).map(Nl).filter(t=>t&&t.name===e)}function eh({env:n=process.env,platform:e=process.platform,run:t=l0,port:s,cfgHosts:o=[]}={}){let r=a0({env:n,platform:e});if(!r)return{installed:!1};let i=t(r,["status","--json"],{timeoutMs:5e3}),a=i.code===0?Vf(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(`
393
+ `)[0]||"tailscale status failed"};if(!a||!a.dnsName)return l;let c=c0(o,a.dnsName),u=c.length&&c[0].port||443,d=t(r,["serve","status","--json"],{timeoutMs:5e3}),f=d.code===0?Zf(d.stdout,a.dnsName,u):{parsed:!1};return{...l,httpsPort:u,serve:f,serving:!!(f.proxy&&Xf(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 nh({sub:n="status",flags:e={},out:t=process.stdout,env:s=process.env,platform:o=process.platform,run:r=l0}={}){let i=(h="")=>t.write(h+`
394
+ `);if(["help","--help","-h"].includes(n))return i(Zi()),0;if(n==="funnel")return i(Yc),i("Use: leftoff tailscale on (tailscale serve, tailnet only)"),1;let a=Z(),l=X(a),c=Number(e.port||l.port),u=be(a),d=l.remoteHosts||[];if(n==="revoke"){let h=wi(a);return i("Signed out "+h+" remote browser"+(h===1?"":"s")+". Unused login links are void too."),0}let f=a0({env:s,platform:o}),p=()=>(i("Tailscale CLI not found (looked on PATH"+(o==="darwin"?" and in "+qf:"")+")."),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(y=>"https://"+Ft(y)+"/").join(" ")),0;Ee(a);let h=yi(a),m="https://"+Ft(d[0])+"/";i0(i,{base:m,link:m+"?t="+h.code,code:h.signInCode,expiresAt:h.expiresAt,qr:r0(e,t),color:!e["no-color"]&&!s.NO_COLOR});for(let y of d.slice(1))i(" also: https://"+Ft(y)+"/?t="+h.code);return 0}if(n==="status"){if(!f)return p(),i(""),i("remoteHosts: "+(d.length?d.join(", "):"(none)")),1;let h=eh({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(`
395
+ `)[0]+")":"")),i("state "+(h.state||h.error||"unknown")+(h.dnsName?" · "+h.dnsName:"")),h.dnsName){let b=h.serve||{};i("serve "+(b.parsed?b.proxy?"https://"+h.dnsName+(h.httpsPort===443?"":":"+h.httpsPort)+"/ → "+b.proxy+(h.serving?"":" (not this daemon, port "+c+")"):"nothing on https:"+h.httpsPort:"couldn't read tailscale serve status")),b.funnel&&i("! Funnel is ON for this port. Leftoff refuses Funnel traffic; turn it off: tailscale funnel --https="+h.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 y=Rl(a);i("browsers "+y.sessions.length+" signed in (30 days from each one's last visit)"+(y.pendingLinks?", "+y.pendingLinks+" unused link"+(y.pendingLinks===1?"":"s"):"")+(y.pendingCodes?", "+y.pendingCodes+" unused code"+(y.pendingCodes===1?"":"s"):""));let S=await Qf(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(h,{cfgHosts:d})),h.serving&&h.listed&&!e["no-check"]&&s.CHECKPOINTS_REMOTE_CHECK!=="off"&&i(o0(await s0(h.url,{timeoutMs:15e3,env:s}),h.url)),0}if(n==="on"){if(!f)return p();let h=r(f,["status","--json"],{timeoutMs:8e3}),m=h.code===0?Vf(h.stdout):null;if(!m)return i("`tailscale status --json` failed: "+((h.stderr||h.stdout).trim()||"no output")+`
396
+ 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 y=Number(e["https-port"]||443),S=r(f,["serve","status","--json"],{timeoutMs:8e3}),b=S.code===0?Zf(S.stdout,m.dnsName,y):{parsed:!1},w=!!(b.proxy&&Xf(b.proxy,c));if(b.funnel)return i("Funnel is ON for "+m.dnsName+":"+y+", which would put this port on the public internet. "+Yc+`
397
+ Turn it off first: tailscale funnel --https=`+y+" off"),1;if((b.proxy||b.other)&&!w&&!e.force)return i("https://"+m.dnsName+(y===443?"":":"+y)+"/ is already served by tailscale ("+(b.proxy||"a file or text handler")+")."),i("Use another port (leftoff tailscale on --https-port=8443) or replace it (--force)."),1;let A=["serve","--bg","--https="+y,Hx(c)];if(w)i("✓ tailscale serve already proxies https://"+m.dnsName+"/ to "+b.proxy);else{i("Running: tailscale "+A.join(" "));let x=r(f,A,{inherit:!0});if(x.code!==0)return i("tailscale serve failed (exit "+x.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+(y===443?"":":"+y);Ee(a);let k=await n0({root:a,port:c,token:u,add:[v],requireToken:e["no-login"]?!1:e.login?!0:void 0});i((k.live||!k.daemonUp?"✓ ":"! ")+v+" added to remoteHosts"+(k.live?" (live in the running daemon)":k.daemonUp?" in config.json, but the running daemon didn't take it (HTTP "+(k.status||"?")+(k.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 g="https://"+v+"/",_=e["no-login"]?!1:X(a).remoteRequireToken!==!1;if(!e["no-check"]&&s.CHECKPOINTS_REMOTE_CHECK!=="off"){i("Checking "+g+" from this machine (the first HTTPS request can take up to a minute while Tailscale gets the certificate)…");let x=await s0(g,{env:s});i(o0(x,g)),!x.ok&&x.kind==="bad_host"&&!k.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 x=yi(a);i0(i,{base:g,link:g+"?t="+x.code,code:x.signInCode,expiresAt:x.expiresAt,qr:r0(e,t),color:!e["no-color"]&&!s.NO_COLOR})}else i("Board on your tailnet: "+g),i("remoteRequireToken is off: anyone on your tailnet who can reach this URL gets the board.");return i("Turn it off: leftoff tailscale off · "+Yc),0}if(n==="off"){let h=null;if(f){let w=r(f,["status","--json"],{timeoutMs:8e3}),A=w.code===0?Vf(w.stdout):null;h=A&&A.dnsName}let m=h?c0(d,h):[],y=m.length?m.map(w=>w.port||443):[Number(e["https-port"]||443)];if(f&&h)for(let w of y){let A=r(f,["serve","status","--json"],{timeoutMs:8e3}),v=A.code===0?Zf(A.stdout,h,w):{parsed:!1};if(v.proxy&&Xf(v.proxy,c)){let k=["serve","--https="+w,"off"];i("Running: tailscale "+k.join(" "));let g=r(f,k,{inherit:!0});g.code!==0&&i("! tailscale serve off failed (exit "+g.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 n0({root:a,port:c,token:u,remove:S});let b=wi(a);return i("✓ remote access off: "+(S.length?S.join(", ")+" removed from remoteHosts":"no remoteHosts to remove")+(b?", "+b+" remote browser"+(b===1?"":"s")+" signed out":"")),0}return i(Zi()),1}Hs();ki();Jo();Dn();Ae();function Xi(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 ws(n){return n.title||"(untitled)"}function sh(n,e){return[n.repoLabel||n.repoName,n.repo&&n.repo.branch,n.status,n.idleSec!=null?"idle "+Xi(n.lastActivityAt,e).replace(" ago",""):null,"cp:"+(n.shortId||Ye(n.id)),n.terminal?n.terminal.label:null].filter(Boolean).join(" · ")}function Bx(n){return n.filter(e=>e.isLocal!==!1&&e.live!=="closed"&&!e.endedAt)}async function u0({pos:n=[],flags:e={},api:t,out:s=process.stdout,err:o=process.stderr,input:r=process.stdin,jumpRunner:i=$l,now:a=Date.now(),env:l=process.env}={}){let c=w=>s.write(w+`
398
+ `);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 A=/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("✖ "))+A+`
399
+ `),e.plain&&c(A),1}let d=e.id?u:Bx(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(A=>Wa(w,A.trim())).filter((A,v,k)=>k.indexOf(A)===v),!d.length)return c('No open session in a repo matching "'+e.repo+'".'+(w.length?" Open repos: "+[...new Set(w.map(A=>A.repoLabel||A.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("○"))+" "+ws(w)+" "+ee.dim(sh(w,a)));return 0}let f=n.join(" ").trim(),p=null;if(e.id)p=d[0];else{let w=f?zg(f,d):d;if(!w.length)return c(f?'No open session matches "'+f+'".'+(d.length?" Open: "+d.slice(0,5).map(ws).join(", "):""):"No open sessions on this machine."),2;if(w.length===1)p=w[0];else if(r.isTTY&&s.isTTY){let A=await Dy(f?'Sessions matching "'+f+'"':"Open Claude Code sessions",w.map(v=>({label:ws(v),hint:sh(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(!A)return 2;p=w.find(v=>v.id===A)}else{c((f?w.length+' sessions match "'+f+'"':w.length+" open sessions")+"; be more specific, or use the id:");for(let A of w.slice(0,10))c(" "+ws(A)+" "+ee.dim(sh(A,a)));return 2}}if(!p)return c("No such session."),2;if(p.live==="closed")return c('"'+F(ws(p),60)+'" is closed. Resume it with: '+p.resumeCommand),3;let h=p.terminal&&p.terminal.label||p.term&&p.term.label||"its terminal";e.plain||c(ee.dim("Switching to “"+F(ws(p),60)+"” in "+h+"…"));let m=Date.now(),y;if(e.daemon)try{y=await t("POST","/v1/sessions/"+encodeURIComponent(p.id)+"/go",{})}catch(w){y={ok:!1,message:w.message}}else{let w;try{let v=await t("GET","/v1/sessions/"+encodeURIComponent(p.id)+"/where");v.isLocal?v.term?w=qo(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 A=Un(l);y=await i(w,{ghostty:{activate:A.kind==="ghostty"?"never":"auto"}})}let S=Date.now()-m,b=" in "+(S<1e4?(S/1e3).toFixed(2):Math.round(S/1e3))+" s";if(e.debug){c(ee.dim(" total "+S+" ms"+(y.elapsedMs!=null?" (jump "+y.elapsedMs+" ms"+(e.daemon?", inside the daemon":"")+")":"")));for(let w of y.steps||[])c(ee.dim(" · "+w.step+": "+w.ms+" ms"+(w.result?" → "+w.result:"")))}return e.plain?(c(y.ok?"Switched to “"+F(ws(p),50)+"”"+b:y.message),y.ok?0:3):y.ok?(c(ee.green("✔")+" "+ws(p)+ee.dim(" ("+String(y.message||"").replace(/^Switched to /,"")+b+")")),0):(c(ee.red("✖")+" "+y.message+ee.dim(b+(y.steps&&y.steps.length&&!e.debug?" · "+Bl(y.steps):""))),3)}function d0(n=process.env){let e=Un(n),t=qo({id:"x",term:e,tty:"/dev/ttys000",cwd:"/"});return{term:e,supported:t.supported,reason:t.reason||null}}Dn();Ae();ki();nf();Jo();var f0=`\`leftoff restore\` (v0.4.7): reopen the Claude Code sessions that were open
397
400
  before this Mac restarted, each in a new Ghostty tab in its folder, resumed
398
401
  by id and with its name back.
399
402
  leftoff restore pick from the list (all checked; space toggles, enter goes)
@@ -406,11 +409,11 @@ by id and with its name back.
406
409
  The tabs open from this terminal (macOS asks once whether it may control
407
410
  Ghostty), like \`leftoff go\`. Without Ghostty's AppleScript (Ghostty 1.3.0+,
408
411
  macOS) it prints one \`cd … && claude --resume …\` line per session.
409
- Exit codes: 0 done (or nothing to do), 1 error, 2 cancelled, 3 some didn't open.`;function sr(n){return n.title||"(untitled)"}function Lf(n,e){return[n.repoLabel,n.branch,n.lastAliveAt?"last seen "+Ki(n.lastAliveAt,e):null,"cp:"+(n.shortId||Ke(n.id))].filter(Boolean).join(" · ")}var Df=n=>{let e=new Date(n);return isNaN(e)?"":e.toLocaleString(void 0,{weekday:"short",hour:"numeric",minute:"2-digit"})};async function _w({pos:n=[],flags:e={},api:t,out:s=process.stdout,err:o=process.stderr,input:r=process.stdin,exec:i=Gt,platform:a=process.platform,now:l=Date.now()}={}){let c=v=>s.write(v+`
412
+ Exit codes: 0 done (or nothing to do), 1 error, 2 cancelled, 3 some didn't open.`;function hr(n){return n.title||"(untitled)"}function oh(n,e){return[n.repoLabel,n.branch,n.lastAliveAt?"last seen "+Xi(n.lastAliveAt,e):null,"cp:"+(n.shortId||Ye(n.id))].filter(Boolean).join(" · ")}var rh=n=>{let e=new Date(n);return isNaN(e)?"":e.toLocaleString(void 0,{weekday:"short",hour:"numeric",minute:"2-digit"})};async function h0({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+`
410
413
  `),u=v=>(o.write(ee.red("✖ ")+v+`
411
- `),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 p=f.sessions||[],h=n.join(" ").trim();if(h){let v;try{v=(await t("GET","/v1/sessions?limit=500&archived=1")).sessions||[]}catch(E){return u(E.message)}let T=v.filter(E=>E.isLocal!==!1),b=T.find(E=>E.id===h||(E.shortId||Ke(E.id))===h.replace(/^cp:/,"")),g=b?[{s:b,score:1/0}]:T.map(E=>({s:E,score:wd(h,E)})).filter(E=>E.score>0).sort((E,D)=>D.score-E.score||Date.parse(D.s.lastActivityAt||0)-Date.parse(E.s.lastActivityAt||0));if(!g.length)return u("No session on this machine matches “"+h+"”.");let _=g[0].s,x=g.filter(E=>E.score===g[0].score);if(x.length>1){c("More than one session matches “"+h+"”:");for(let E of x.slice(0,8))c(" "+sr(E.s)+ee.dim(" "+[E.s.repoLabel,E.s.live==="open"?"running":null,"cp:"+(E.s.shortId||Ke(E.s.id))].filter(Boolean).join(" · ")));return c("Use the id: leftoff restore <id>"),1}if(_.live==="open")return c("“"+U(sr(_),60)+"” is already running. Jump to it: leftoff go "+(_.shortId||Ke(_.id))),0;p=[p.find(E=>E.id===_.id)||{id:_.id,shortId:_.shortId,title:_.title,repoLabel:_.repoLabel,cwd:_.cwd,lastAliveAt:_.lastActivityAt}]}if(!p.length){let v=f.restartAt?f.restartKind==="boot"?"this Mac restarted ("+Df(f.restartAt)+")":"the daemon started ("+Df(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=p,y=!h&&!e.all&&!e.yes&&!e["dry-run"]&&!e.print&&r.isTTY&&s.isTTY;if(!h&&!y&&!e.all&&!e.yes&&!e["dry-run"]&&!e.print){c(p.length+" session"+(p.length===1?" was":"s were")+" open before the restart:");for(let v of p)c(" "+sr(v)+ee.dim(" "+Lf(v,l)));return c("Restore them all: leftoff restore --all (or one: leftoff restore <name|id>)"),0}if(y){let v=p.length+" session"+(p.length===1?" was":"s were")+" open before "+(f.restartKind==="boot"?"your Mac restarted":"Leftoff restarted")+(f.restartAt?" ("+Df(f.restartAt)+")":"");if(m=await Jg(v,p.map(T=>({label:U(sr(T),60),hint:Lf(T,l),value:T})),{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(" "+sr(v)+ee.dim(" "+Lf(v,l))+`
412
- `+v.command);return 0}let w=v=>{v&&c(ee.yellow("!")+" Couldn't open Ghostty tabs: "+v+". Run these yourself, one per tab:");for(let T of m)c(T.command)};if(e.print)return w(null),0;if(!m.every(v=>v.command))try{m=(await t("POST","/v1/restore",{ids:m.map(T=>T.id),print:!0})).sessions}catch(v){return u(v.message)}c(ee.dim("Opening "+m.length+" Ghostty tab"+(m.length===1?"":"s")+"…"));let S=await $l(m.map(v=>({cwd:v.cwd,claude:v.claude})),{exec:i,platform:a});if(S.how==="print")return w(S.message),3;let k=m.filter((v,T)=>S.results[T]&&S.results[T].ok).map(v=>v.id);if(k.length)try{await t("POST","/v1/restore/launched",{ids:k})}catch{}return m.forEach((v,T)=>{let b=S.results[T]||{};c((b.ok?ee.green("✔"):ee.red("✖"))+" "+sr(v)+ee.dim(" "+[v.repoLabel,"cp:"+(v.shortId||Ke(v.id))].filter(Boolean).join(" · "))+(b.ok?"":" "+b.error+`
413
- `+v.command))}),c(k.length===m.length?ee.green("Restored "+k.length+".")+ee.dim(" Each tab runs claude --resume with the session id, in its folder."):ee.yellow(k.length+" of "+m.length+" opened.")),k.length===m.length?0:3}ke();wn();Es();Ha();Ka();qn();ve();var V1=new Set(["since","to","repo","machine","length"]),Z1={r:"repo",m:"machine",s:"since"};function X1(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=Z1[o[1]]||o[1],i=o[2];if(V1.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 Ew(){return`leftoff catchup: one recap of what happened across your sessions
414
+ `),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 p=f.sessions||[],h=n.join(" ").trim();if(h){let v;try{v=(await t("GET","/v1/sessions?limit=500&archived=1")).sessions||[]}catch(M){return u(M.message)}let k=v.filter(M=>M.isLocal!==!1),g=k.find(M=>M.id===h||(M.shortId||Ye(M.id))===h.replace(/^cp:/,"")),_=g?[{s:g,score:1/0}]:k.map(M=>({s:M,score:Gd(h,M)})).filter(M=>M.score>0).sort((M,C)=>C.score-M.score||Date.parse(C.s.lastActivityAt||0)-Date.parse(M.s.lastActivityAt||0));if(!_.length)return u("No session on this machine matches “"+h+"”.");let x=_[0].s,E=_.filter(M=>M.score===_[0].score);if(E.length>1){c("More than one session matches “"+h+"”:");for(let M of E.slice(0,8))c(" "+hr(M.s)+ee.dim(" "+[M.s.repoLabel,M.s.live==="open"?"running":null,"cp:"+(M.s.shortId||Ye(M.s.id))].filter(Boolean).join(" · ")));return c("Use the id: leftoff restore <id>"),1}if(x.live==="open")return c("“"+F(hr(x),60)+"” is already running. Jump to it: leftoff go "+(x.shortId||Ye(x.id))),0;p=[p.find(M=>M.id===x.id)||{id:x.id,shortId:x.shortId,title:x.title,repoLabel:x.repoLabel,cwd:x.cwd,lastAliveAt:x.lastActivityAt}]}if(!p.length){let v=f.restartAt?f.restartKind==="boot"?"this Mac restarted ("+rh(f.restartAt)+")":"the daemon started ("+rh(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=p,y=!h&&!e.all&&!e.yes&&!e["dry-run"]&&!e.print&&r.isTTY&&s.isTTY;if(!h&&!y&&!e.all&&!e.yes&&!e["dry-run"]&&!e.print){c(p.length+" session"+(p.length===1?" was":"s were")+" open before the restart:");for(let v of p)c(" "+hr(v)+ee.dim(" "+oh(v,l)));return c("Restore them all: leftoff restore --all (or one: leftoff restore <name|id>)"),0}if(y){let v=p.length+" session"+(p.length===1?" was":"s were")+" open before "+(f.restartKind==="boot"?"your Mac restarted":"Leftoff restarted")+(f.restartAt?" ("+rh(f.restartAt)+")":"");if(m=await Ry(v,p.map(k=>({label:F(hr(k),60),hint:oh(k,l),value:k})),{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(" "+hr(v)+ee.dim(" "+oh(v,l))+`
415
+ `+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 k of m)c(k.command)};if(e.print)return S(null),0;if(!m.every(v=>v.command))try{m=(await t("POST","/v1/restore",{ids:m.map(k=>k.id),print:!0})).sessions}catch(v){return u(v.message)}let b=m.filter(v=>!v.blocked),w=new Map;if(b.length){c(ee.dim("Opening "+b.length+" Ghostty tab"+(b.length===1?"":"s")+"…"));let v=await ac(b.map(k=>({cwd:k.cwd,claude:k.claude})),{exec:i,platform:a});if(v.how==="print")return S(v.message),3;b.forEach((k,g)=>w.set(k.id,v.results[g]||{}))}let A=m.filter(v=>(w.get(v.id)||{}).ok).map(v=>v.id);if(A.length)try{await t("POST","/v1/restore/launched",{ids:A})}catch{}return m.forEach(v=>{let k=v.blocked?{ok:!1,error:v.error}:w.get(v.id)||{};c((k.ok?ee.green("✔"):ee.red("✖"))+" "+hr(v)+ee.dim(" "+[v.repoLabel,"cp:"+(v.shortId||Ye(v.id))].filter(Boolean).join(" · "))+(k.ok?"":" "+k.error+(v.blocked?"":`
416
+ `+v.command)))}),c(A.length===m.length?ee.green("Restored "+A.length+".")+ee.dim(" Each tab resumes its session by id, in its folder."):ee.yellow(A.length+" of "+m.length+" opened.")),A.length===m.length?0:3}ve();vn();Is();nl();ol();es();Ae();var Gx=new Set(["since","to","repo","machine","length"]),Kx={r:"repo",m:"machine",s:"since"};function $x(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=Kx[o[1]]||o[1],i=o[2];if(Gx.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 p0(){return`leftoff catchup: one recap of what happened across your sessions
414
417
 
415
418
  leftoff catchup since your last catch-up (else the last 24 hours), every repo
416
419
  leftoff catchup --since 3d a duration: 90m, 12h, 3d, 1w
@@ -424,51 +427,51 @@ Exit codes: 0 done (or nothing to do), 1 error, 2 cancelled, 3 some didn't open.
424
427
  leftoff catchup --short | --long how much each session says (default: config summary.length)
425
428
  leftoff catchup --list the saved catch-ups, newest first
426
429
 
427
- Saved as catchups/YYYY-MM-DDTHHMM.md in your Leftoff folder; the board lists them under Catch me up.`}function Q1(n){return Be(n).body.replace(/^\n+/,"")}var Cw=n=>/fetch failed|ECONNREFUSED|aborted|timeout|ECONNRESET/i.test(String(n&&n.message)+(n&&n.cause?n.cause.code:""));async function Ow({argv:n=[],api:e,out:t=o=>process.stdout.write(o),err:s=o=>process.stderr.write(o)}={}){let o;try{o=X1(n)}catch(c){return s("leftoff catchup: "+c.message+`
430
+ Saved as catchups/YYYY-MM-DDTHHMM.md in your Leftoff folder; the board lists them under Catch me up.`}function Yx(n){return $e(n).body.replace(/^\n+/,"")}var m0=n=>/fetch failed|ECONNREFUSED|aborted|timeout|ECONNRESET/i.test(String(n&&n.message)+(n&&n.cause?n.cause.code:""));async function g0({argv:n=[],api:e,out:t=o=>process.stdout.write(o),err:s=o=>process.stderr.write(o)}={}){let o;try{o=$x(n)}catch(c){return s("leftoff catchup: "+c.message+`
428
431
 
429
- `+Ew()+`
430
- `),2}if(o.help||o.h)return t(Ew()+`
431
- `),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(!Cw(u))throw u}if(!c){let u=V();c=new gt(u,le(X(u))).listCatchups()}if(o.json)return t(JSON.stringify(c,null,2)+`
432
+ `+p0()+`
433
+ `),2}if(o.help||o.h)return t(p0()+`
434
+ `),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(!m0(u))throw u}if(!c){let u=Z();c=new vt(u,de(X(u))).listCatchups()}if(o.json)return t(JSON.stringify(c,null,2)+`
432
435
  `),0;if(!c.length)return t(`No catch-ups yet. Make one: leftoff catchup
433
436
  `),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:"")+`
434
- `);return 0}let i=null,a="daemon";if(!o.offline)try{i=await e("POST","/v1/catchups",r)}catch(c){if(!Cw(c))return s("leftoff catchup: "+c.message+`
435
- `),/window|can't read/.test(c.message)?2:1}if(!i){a="folder";let c=V();Te(c);let u=X(c),d=le(u),f=new gt(c,d),p;try{let m=(()=>{try{return new vt(c,d).view({fresh:!0})}catch{return{agents:[]}}})();p=await $r(f.loadAll().concat(al(m)),{...r,cfg:u,machine:d,lastAt:f.lastCatchupAt(),summarizer:new yt(u,{root:c}),lists:new Yn(c,d).all({includeArchived:!1}).map(y=>ut(y)).concat(il(m))})}catch(m){return s("leftoff catchup: "+m.message+`
437
+ `);return 0}let i=null,a="daemon";if(!o.offline)try{i=await e("POST","/v1/catchups",r)}catch(c){if(!m0(c))return s("leftoff catchup: "+c.message+`
438
+ `),/window|can't read/.test(c.message)?2:1}if(!i){a="folder";let c=Z();Ee(c);let u=X(c),d=de(u),f=new vt(c,d),p;try{let m=(()=>{try{return new Ot(c,d).view({fresh:!0})}catch{return{agents:[]}}})();p=await ti(f.loadAll().concat(Al(m)),{...r,cfg:u,machine:d,lastAt:f.lastCatchupAt(),summarizer:new xt(u,{root:c}),lists:new Vn(c,d).all({includeArchived:!1}).map(y=>mt(y)).concat(xl(m))})}catch(m){return s("leftoff catchup: "+m.message+`
436
439
  `),m.badWindow?2:1}let h=r.save?f.writeCatchup(p.fileName,p.markdown):null;i={name:h?h.name:null,path:h?"catchups/"+h.name+".md":null,saved:!!h,recap:p.recap,summary:p.summary,markdown:p.markdown}}if(o.json)return t(JSON.stringify({...i,via:a},null,2)+`
437
- `),0;t(Q1(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(", ")+`
440
+ `),0;t(Yx(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(", ")+`
438
441
  `),i.recap.window.fallback==="no earlier catch-up"&&s(`· no earlier catch-up, so this covers the last 24 hours
439
442
  `),i.saved&&s(`
440
443
  Saved `+i.path+(a==="folder"?" (daemon not running: built from the folder)":"")+` · on the board: Catch me up
441
- `),0}ke();Ka();wn();fu();Cr();ve();In();var ex=n=>/fetch failed|ECONNREFUSED|abort|timed? ?out/i.test(String(n&&n.message+(n.cause?" "+n.cause.code:"")));function tx(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 Oc(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(`
442
- `)}async function Iw({argv:n=[],api:e,out:t=r=>process.stdout.write(r),err:s=r=>process.stderr.write(r),root:o}={}){let r=tx(n),i=(h,m=1)=>(s("leftoff todo: "+h+`
443
- `),m),a=null,l=()=>{if(a)return a;let h=o||V();return Te(h),a=new Yn(h,le(X(h))),a},c=async(h,m,y)=>{if(r.offline||!e)return null;try{return await e(h,m,y)}catch(w){if(ex(w))return null;throw w}},u=h=>ut(l().resolve(h)),d=async h=>{let m=await c("GET","/v1/lists/resolve?ref="+encodeURIComponent(h));return m?m.list:u(h)},f=async(h,m)=>{let y=await d(h),w=await c("POST","/v1/lists/"+y.ref+"/change",m);if(w)return w;let S=l(),k={};if(m.text){let v=S.findItem(S.resolve(y.ref),m.text);if(!v)throw new Error('no item on "'+y.name+'" matches "'+m.text+'"; give its number');m={[m.undo?"undone":"done"]:[v.id]}}if(m.done||m.undone){let v=S.setDone(y.ref,[].concat(m.done||m.undone),!!m.done);k.missing=v.missing}return m.add&&(k.added=S.addItem(y.ref,m.add).item.id),m.name&&S.rename(y.ref,m.name),m.archived!=null&&S.archive(y.ref,m.archived),{ok:!0,list:u(y.ref),...k}},p=h=>h.flatMap(m=>String(m).split(",")).map(m=>m.replace(/^#/,"")).filter(Boolean);try{switch(r.sub){case"help":return t(sx),0;case"ls":case"list":{let h=null,m=await c("GET","/v1/lists"+(r.all?"?all=1":""));if(h=m?m.lists:l().all({includeArchived:r.all}).map(w=>ut(w)),r.repo&&(h=h.filter(w=>w.repoDir===r.repo||w.repo===r.repo||String(w.repo||"").toLowerCase().includes(String(r.repo).toLowerCase()))),r.all||(h=h.filter(w=>!w.archived)),r.json)return t(JSON.stringify(h,null,2)+`
444
+ `),0}ve();ol();vn();Nu();jr();Ae();Dn();var Wx=n=>/fetch failed|ECONNREFUSED|abort|timed? ?out/i.test(String(n&&n.message+(n.cause?" "+n.cause.code:"")));function zx(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 Wc(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(`
445
+ `)}async function y0({argv:n=[],api:e,out:t=r=>process.stdout.write(r),err:s=r=>process.stderr.write(r),root:o}={}){let r=zx(n),i=(h,m=1)=>(s("leftoff todo: "+h+`
446
+ `),m),a=null,l=()=>{if(a)return a;let h=o||Z();return Ee(h),a=new Vn(h,de(X(h))),a},c=async(h,m,y)=>{if(r.offline||!e)return null;try{return await e(h,m,y)}catch(S){if(Wx(S))return null;throw S}},u=h=>mt(l().resolve(h)),d=async h=>{let m=await c("GET","/v1/lists/resolve?ref="+encodeURIComponent(h));return m?m.list:u(h)},f=async(h,m)=>{let y=await d(h),S=await c("POST","/v1/lists/"+y.ref+"/change",m);if(S)return S;let b=l(),w={};if(m.text){let A=b.findItem(b.resolve(y.ref),m.text);if(!A)throw new Error('no item on "'+y.name+'" matches "'+m.text+'"; give its number');m={[m.undo?"undone":"done"]:[A.id]}}if(m.done||m.undone){let A=b.setDone(y.ref,[].concat(m.done||m.undone),!!m.done);w.missing=A.missing}return m.add&&(w.added=b.addItem(y.ref,m.add).item.id),m.name&&b.rename(y.ref,m.name),m.archived!=null&&b.archive(y.ref,m.archived),{ok:!0,list:u(y.ref),...w}},p=h=>h.flatMap(m=>String(m).split(",")).map(m=>m.replace(/^#/,"")).filter(Boolean);try{switch(r.sub){case"help":return t(qx),0;case"ls":case"list":{let h=null,m=await c("GET","/v1/lists"+(r.all?"?all=1":""));if(h=m?m.lists:l().all({includeArchived:r.all}).map(S=>mt(S)),r.repo&&(h=h.filter(S=>S.repoDir===r.repo||S.repo===r.repo||String(S.repo||"").toLowerCase().includes(String(r.repo).toLowerCase()))),r.all||(h=h.filter(S=>!S.archived)),r.json)return t(JSON.stringify(h,null,2)+`
444
447
  `),0;if(!h.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.
445
- `),0;let y=null;return h.forEach((w,S)=>{let k=w.repo||"no repo";k!==y&&(t((S?`
446
- `:"")+ee.dim("── "+k)+`
447
- `),y=k),t(Oc(w,{number:S+1})+`
448
+ `),0;let y=null;return h.forEach((S,b)=>{let w=S.repo||"no repo";w!==y&&(t((b?`
449
+ `:"")+ee.dim("── "+w)+`
450
+ `),y=w),t(Wc(S,{number:b+1})+`
448
451
  `)}),t(ee.dim(`
449
452
  Tick one: leftoff todo done <list> <n> · on the board: Lists`)+`
450
453
  `),0}case"show":{if(!r.args.length)return i("which list? leftoff todo show <list>",2);let h=await d(r.args.join(" "));return r.json?(t(JSON.stringify(h,null,2)+`
451
- `),0):(t(Oc(h)+`
452
- `),0)}case"done":case"tick":case"check":case"undo":case"uncheck":{let h=r.sub==="undo"||r.sub==="uncheck",[m,...y]=r.args;if(!m)return i("usage: leftoff todo "+r.sub+" <list> <n…>",2);if(!y.length&&/^#?\d+(,\d+)*$/.test(m)){let b=await c("GET","/v1/lists"),g=(b?b.lists:l().all().map(_=>ut(_))).filter(_=>!_.archived);if(g.length!==1)return i("which list? leftoff todo "+r.sub+" <list> "+m+(g.length?" (open: "+g.map(_=>_.ref).join(", ")+")":""),2);y=[m],m=g[0].ref}if(!y.length)return i("which item? leftoff todo "+r.sub+" "+m+" <n>",2);let w=p(y),S=w.every(b=>/^\d+$/.test(b))?{[h?"undone":"done"]:w.map(Number)}:{text:y.join(" "),undo:h},k=await f(m,S),v=k.list;if(r.json)return t(JSON.stringify(k,null,2)+`
453
- `),0;let T=S.text?v.items.filter(b=>b.done!==h&&b.text.toLowerCase().includes(String(S.text).toLowerCase().split(" ")[0])).slice(0,1):v.items.filter(b=>(S.done||S.undone).includes(b.n));return t((h?"↺ ":"✓ ")+v.name+": "+T.map(b=>"#"+b.n+" "+U(b.text,70)).join("; ")+(h?" unticked":"")+" · "+v.progress+` done
454
- `),k.missing&&k.missing.length&&s("! no item "+k.missing.join(", ")+` on that list
454
+ `),0):(t(Wc(h)+`
455
+ `),0)}case"done":case"tick":case"check":case"undo":case"uncheck":{let h=r.sub==="undo"||r.sub==="uncheck",[m,...y]=r.args;if(!m)return i("usage: leftoff todo "+r.sub+" <list> <n…>",2);if(!y.length&&/^#?\d+(,\d+)*$/.test(m)){let k=await c("GET","/v1/lists"),g=(k?k.lists:l().all().map(_=>mt(_))).filter(_=>!_.archived);if(g.length!==1)return i("which list? leftoff todo "+r.sub+" <list> "+m+(g.length?" (open: "+g.map(_=>_.ref).join(", ")+")":""),2);y=[m],m=g[0].ref}if(!y.length)return i("which item? leftoff todo "+r.sub+" "+m+" <n>",2);let S=p(y),b=S.every(k=>/^\d+$/.test(k))?{[h?"undone":"done"]:S.map(Number)}:{text:y.join(" "),undo:h},w=await f(m,b),A=w.list;if(r.json)return t(JSON.stringify(w,null,2)+`
456
+ `),0;let v=b.text?A.items.filter(k=>k.done!==h&&k.text.toLowerCase().includes(String(b.text).toLowerCase().split(" ")[0])).slice(0,1):A.items.filter(k=>(b.done||b.undone).includes(k.n));return t((h?"↺ ":"✓ ")+A.name+": "+v.map(k=>"#"+k.n+" "+F(k.text,70)).join("; ")+(h?" unticked":"")+" · "+A.progress+` done
457
+ `),w.missing&&w.missing.length&&s("! no item "+w.missing.join(", ")+` on that list
455
458
  `),0}case"add":{let[h,...m]=r.args;if(!h||!m.length)return i("usage: leftoff todo add <list> <text>",2);let y=await f(h,{add:m.join(" ")});return r.json?(t(JSON.stringify(y,null,2)+`
456
- `),0):(t("+ "+y.list.name+": #"+y.added+" "+U(m.join(" "),80)+" · "+y.list.progress+`
459
+ `),0):(t("+ "+y.list.name+": #"+y.added+" "+F(m.join(" "),80)+" · "+y.list.progress+`
457
460
  `),0)}case"rename":{let[h,...m]=r.args;if(!h||!m.length)return i("usage: leftoff todo rename <list> <new name>",2);let y=await f(h,{name:m.join(" ")});return t("Renamed: "+y.list.name+" ("+y.list.ref+`)
458
461
  `),0}case"archive":case"unarchive":{if(!r.args.length)return i("usage: leftoff todo "+r.sub+" <list>",2);let h=await f(r.args.join(" "),{archived:r.sub==="archive"});return t((r.sub==="archive"?"Archived: ":"Back on the board: ")+h.list.name+`
459
- `),0}case"new":{let[h,...m]=r.args;if(!h||!m.length)return i('usage: leftoff todo new "<name>" [--repo NAME] "<item>" ["<item>"…]',2);let y={name:h,items:m.map(S=>({text:S})),repo:r.repo||null},w=await c("POST","/v1/lists",y);return w||(w={list:ut(l().create({name:h,items:y.items,repo:r.repo||null}))}),r.json?(t(JSON.stringify(w.list,null,2)+`
460
- `),0):(t("Saved: "+Oc(w.list)+`
461
- `),0)}case"capture":return await nx(r,{call:c,local:l,out:t,err:s,fail:i})}}catch(h){return i(h.message,/no list|no item|matches \d+ lists|which/.test(h.message)?3:1)}return i("unknown command "+r.sub,2)}async function nx(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 w=t();a=new gt(w.root,w.machine).loadAll().map(({session:S})=>({...S,repoName:fe(S),shortId:Ke(S.id)}))}let c=i.toLowerCase(),u=[w=>w.id===i,w=>w.id.startsWith(c)||w.shortId&&w.shortId===c.replace(/^cp:/,""),w=>String(w.title||"").toLowerCase()===c,w=>String(w.title||"").toLowerCase().includes(c)],d=null;for(let w of u){let S=a.filter(w);if(S.length===1){d=S[0];break}if(S.length>1)return r('"'+i+'" matches '+S.length+" sessions: "+S.slice(0,6).map(k=>(k.title||"untitled")+" ("+k.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,p=await e("GET","/v1/sessions/"+encodeURIComponent(d.id)+"/lists/find");if(p)f=p.candidates;else{let w=d.transcriptPath||uo(d.id);if(!w)return r(`Claude Code's transcript for "`+(d.title||d.id)+`" isn't on this machine (looked in ~/.claude/projects)`,1);f=Xi(vs(w,{maxBytes:32*1024*1024}).messages,{fallback:d.title}).reverse().map(S=>({...S,count:S.items.length}))}let h=d.title||"untitled session";if(!f.length)return s('No checklists in "'+h+`". Leftoff looks for "- [ ]" checklists and numbered or bulleted lists under a heading like "Next steps".
462
+ `),0}case"new":{let[h,...m]=r.args;if(!h||!m.length)return i('usage: leftoff todo new "<name>" [--repo NAME] "<item>" ["<item>"…]',2);let y={name:h,items:m.map(b=>({text:b})),repo:r.repo||null},S=await c("POST","/v1/lists",y);return S||(S={list:mt(l().create({name:h,items:y.items,repo:r.repo||null}))}),r.json?(t(JSON.stringify(S.list,null,2)+`
463
+ `),0):(t("Saved: "+Wc(S.list)+`
464
+ `),0)}case"capture":return await Jx(r,{call:c,local:l,out:t,err:s,fail:i})}}catch(h){return i(h.message,/no list|no item|matches \d+ lists|which/.test(h.message)?3:1)}return i("unknown command "+r.sub,2)}async function Jx(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 vt(S.root,S.machine).loadAll().map(({session:b})=>({...b,repoName:me(b),shortId:Ye(b.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 b=a.filter(S);if(b.length===1){d=b[0];break}if(b.length>1)return r('"'+i+'" matches '+b.length+" sessions: "+b.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,p=await e("GET","/v1/sessions/"+encodeURIComponent(d.id)+"/lists/find");if(p)f=p.candidates;else{let S=d.transcriptPath||ko(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=ca(Kn(S,{maxBytes:32*1024*1024}).messages,{fallback:d.title}).reverse().map(b=>({...b,count:b.items.length}))}let h=d.title||"untitled session";if(!f.length)return s('No checklists in "'+h+`". Leftoff looks for "- [ ]" checklists and numbered or bulleted lists under a heading like "Next steps".
462
465
  `),4;if(n.pick===!0){s('Lists Claude wrote in "'+h+`" (newest first):
463
- `);for(let w of f)s(" "+String(w.n).padStart(2)+". "+w.name+" · "+w.count+" items"+(w.savedAs?ee.dim(" · saved as "+w.savedAs.ref):"")+`
464
- `+w.items.slice(0,3).map(S=>ee.dim(" "+U(S.text,70))).join(`
466
+ `);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):"")+`
467
+ `+S.items.slice(0,3).map(b=>ee.dim(" "+F(b.text,70))).join(`
465
468
  `)+`
466
469
  `);return s(ee.dim("Save one: leftoff todo capture "+JSON.stringify(i)+' --pick <n> [--name "…"]')+`
467
- `),0}let m=n.pick?f.find(w=>w.n===n.pick):f[0];if(!m)return r("no list "+n.pick+" in that session (--pick lists them)",3);let y=await e("POST","/v1/lists",{sessionId:d.id,fromTranscript:m.n,name:n.name||void 0});if(!y){let w=t();y={list:ut(w.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||w.machine,sourceTitle:d.title||null}))}}return n.json?(s(JSON.stringify(y.list,null,2)+`
470
+ `),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 y=await e("POST","/v1/lists",{sessionId:d.id,fromTranscript:m.n,name:n.name||void 0});if(!y){let S=t();y={list:mt(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(y.list,null,2)+`
468
471
  `),0):(s('Saved from "'+h+`":
469
- `+Oc(y.list)+`
472
+ `+Wc(y.list)+`
470
473
  `),s(ee.dim("Tick: leftoff todo done "+y.list.ref+" <n> · in the session: /cp-done <n>")+`
471
- `),0)}var sx=`leftoff todo: the checklists Claude writes for you, saved as Markdown in lists/<repo>/ in your folder.
474
+ `),0)}var qx=`leftoff todo: the checklists Claude writes for you, saved as Markdown in lists/<repo>/ in your folder.
472
475
 
473
476
  leftoff todo [--all] [--repo NAME] open lists with their items
474
477
  leftoff todo show <list>
@@ -484,14 +487,14 @@ Tick one: leftoff todo done <list> <n> · on the board: Lists`)+`
484
487
 
485
488
  <list>: its id (repo/slug), its name or the start of it, or its number in \`leftoff todo\`.
486
489
  Item numbers never change. In Claude Code: /cp-todo [name] saves, /cp-done <n> ticks.
487
- `;ke();qn();St();import Ic from"node:fs";import Dw from"node:crypto";var ox=n=>/fetch failed|ECONNREFUSED|abort|timed? ?out/i.test(String(n&&n.message+(n.cause?" "+n.cause.code:"")));function rx(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 Nw=n=>Dw.createHash("sha256").update(String(n),"utf8").digest("hex");function ix(n=Dw.randomBytes){let e="lfr_"+n(24).toString("base64url"),t="lfw_"+n(24).toString("base64url");return{read:e,write:t,readSha256:Nw(e),writeSha256:Nw(t)}}function ax(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/"+Xe+"/blob/main/docs/agent-feed-hosting.md",""].join(`
488
- `)}function lx(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:Ic.readFileSync(0)}}catch(t){return{error:"couldn't read stdin: "+t.message}}}try{return Ic.statSync(n).isFile()?{body:Ic.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 cx({body:n,url:e,writeKey:t,fetchImpl:s=globalThis.fetch,timeoutMs:o=2e4,userAgent:r="Leftoff"}){let i=ol(n);if(!i.ok)return{ok:!1,stage:"validate",errors:i.errors};let a=Xu(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(p){clearTimeout(c);let h=p.name==="AbortError"?"timed out after "+Math.round(o/1e3)+"s":String(p.cause&&(p.cause.code||p.cause.message)||p.message);return{ok:!1,stage:"put",errors:["couldn't reach "+new URL(a.url).host+": "+h.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 p=u.headers.get("etag")||null;try{let h=JSON.parse(d);h&&typeof h.etag=="string"&&(p=h.etag)}catch{}return{ok:!0,status:u.status,etag:p,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 Mw=(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 Rf(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 "+Mw(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 "+Mw(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 Nc(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 Lw(){try{return Ic.readFileSync(0,"utf8").trim()}catch{return""}}async function Rw({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=rx(n),a=V(),l=()=>(Te(a),new vt(a,le(X(a)),{env:o,fetchImpl:r,userAgent:"Leftoff/"+q})),c=async u=>{if(i.offline||!e)return{offline:!0};try{return{r:await u()}}catch(d){if(ox(d))return{offline:!0};throw d}};try{if(i.sub==="list"){let u=(await c(()=>e("GET","/v1/agents"))).r;if(u||(u=Nc(l())),i.json)return t(JSON.stringify(u,null,2)+`
490
+ `;ve();es();Tt();import zc from"node:fs";import b0 from"node:crypto";var Vx=n=>/fetch failed|ECONNREFUSED|abort|timed? ?out/i.test(String(n&&n.message+(n.cause?" "+n.cause.code:"")));function Zx(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 w0=n=>b0.createHash("sha256").update(String(n),"utf8").digest("hex");function Xx(n=b0.randomBytes){let e="lfr_"+n(24).toString("base64url"),t="lfw_"+n(24).toString("base64url");return{read:e,write:t,readSha256:w0(e),writeSha256:w0(t)}}function Qx(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/"+tt+"/blob/main/docs/agent-feed-hosting.md",""].join(`
491
+ `)}function eA(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:zc.readFileSync(0)}}catch(t){return{error:"couldn't read stdin: "+t.message}}}try{return zc.statSync(n).isFile()?{body:zc.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 tA({body:n,url:e,writeKey:t,fetchImpl:s=globalThis.fetch,timeoutMs:o=2e4,userAgent:r="Leftoff"}){let i=bl(n);if(!i.ok)return{ok:!1,stage:"validate",errors:i.errors};let a=vd(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(p){clearTimeout(c);let h=p.name==="AbortError"?"timed out after "+Math.round(o/1e3)+"s":String(p.cause&&(p.cause.code||p.cause.message)||p.message);return{ok:!1,stage:"put",errors:["couldn't reach "+new URL(a.url).host+": "+h.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 p=u.headers.get("etag")||null;try{let h=JSON.parse(d);h&&typeof h.etag=="string"&&(p=h.etag)}catch{}return{ok:!0,status:u.status,etag:p,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 S0=(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 ih(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 "+S0(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 "+S0(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 Jc(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 k0(){try{return zc.readFileSync(0,"utf8").trim()}catch{return""}}async function v0({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=Zx(n),a=Z(),l=()=>(Ee(a),new Ot(a,de(X(a)),{env:o,fetchImpl:r,userAgent:"Leftoff/"+V})),c=async u=>{if(i.offline||!e)return{offline:!0};try{return{r:await u()}}catch(d){if(Vx(d))return{offline:!0};throw d}};try{if(i.sub==="list"){let u=(await c(()=>e("GET","/v1/agents"))).r;if(u||(u=Jc(l())),i.json)return t(JSON.stringify(u,null,2)+`
489
492
  `),0;if(!u.agents.length)return t(`No agent feeds yet. Add one: leftoff agents add <name> <url> --key <key>
490
- `),0;for(let d of u.agents){let f=Rf(d);t((f.ok===!1?"✗ ":f.ok?"✓ ":"· ")+f.line+`
493
+ `),0;for(let d of u.agents){let f=ih(d);t((f.ok===!1?"✗ ":f.ok?"✓ ":"· ")+f.line+`
491
494
  `),t(" "+(d.feedUrl||"")+`
492
495
  `);for(let p of f.details.slice(0,5))t(" - "+p+`
493
496
  `)}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)
494
- `),2;let f=i.key;f==="-"&&(f=Lw()),f==null&&o.LEFTOFF_AGENT_KEY&&(f=o.LEFTOFF_AGENT_KEY);let p={name:u.toLowerCase(),url:d,key:f||null},h=(await c(()=>e("POST","/v1/agents",p))).r;if(!h){let y=l();h=y.add(p);try{h.pull=await y.pull(h.source.name,{force:!0})}catch(w){h.pull={result:"error",error:{message:w.message}}}}t((h.replaced?"Updated ":"Added ")+h.source.name+" ("+h.source.url+")."+(h.hasKey?" Key saved on this Mac only.":" No key: the feed is pulled without one.")+`
497
+ `),2;let f=i.key;f==="-"&&(f=k0()),f==null&&o.LEFTOFF_AGENT_KEY&&(f=o.LEFTOFF_AGENT_KEY);let p={name:u.toLowerCase(),url:d,key:f||null},h=(await c(()=>e("POST","/v1/agents",p))).r;if(!h){let y=l();h=y.add(p);try{h.pull=await y.pull(h.source.name,{force:!0})}catch(S){h.pull={result:"error",error:{message:S.message}}}}t((h.replaced?"Updated ":"Added ")+h.source.name+" ("+h.source.url+")."+(h.hasKey?" Key saved on this Mac only.":" No key: the feed is pulled without one.")+`
495
498
  `),h.previousPuller&&t("This Mac pulls it now (was "+h.previousPuller+`).
496
499
  `);let m=h.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||h.source.name)+`.
497
500
  `);else if(m.result==="unchanged")t(`Pulled: nothing new.
@@ -499,38 +502,38 @@ Item numbers never change. In Claude Code: /cp-todo [name] saves, /cp-done <n> t
499
502
  `);for(let y of(m.error&&m.error.details||[]).slice(0,5))s(" - "+y+`
500
503
  `);return 1}return 0}if(i.sub==="remove"){let[u]=i.args;if(!u)return s(`usage: leftoff agents remove <name> [--purge]
501
504
  `),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.":"")+`
502
- `),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=Nc(l()));let p=d.agents.filter(y=>u?u.includes(y.name):y.pullsHere);if(u){for(let y of u)if(!d.agents.some(w=>w.name===y))return s("No agent feed named "+y+`
505
+ `),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=Jc(l()));let p=d.agents.filter(y=>u?u.includes(y.name):y.pullsHere);if(u){for(let y of u)if(!d.agents.some(S=>S.name===y))return s("No agent feed named "+y+`
503
506
  `),1}if(!p.length)return t("Nothing to pull on this Mac"+(d.agents.length?" (each feed is pulled by the Mac that holds its key)":"")+`.
504
- `),0;let h=0,m=f?null:l();for(let y of p){let w;try{w=f?await e("POST","/v1/agents/"+encodeURIComponent(y.name)+"/refresh"):await m.pull(y.name,{force:!0})}catch(S){w={result:"error",error:{message:S.message}}}if(w.result==="error"){h++,s("✗ "+y.name+": "+(w.error&&w.error.message||"error")+`
505
- `);for(let S of(w.error&&w.error.details||[]).slice(0,5))s(" - "+S+`
506
- `)}else t("✓ "+y.name+": "+(w.result==="unchanged"?"nothing new":w.applied?w.applied.sessions+" session"+(w.applied.sessions===1?"":"s")+", "+w.applied.written+" file"+(w.applied.written===1?"":"s")+" updated":"pulled")+`
507
- `)}return h?1:0}if(i.sub==="keygen"){let u=ix();return t(i.json?JSON.stringify(u,null,2)+`
508
- `:ax(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+`
509
- `),2;let f=i.writeKey,p=f==="-",h=lx(u,{stdinUsed:p});if(h.error)return s(h.error+`
510
- `),1;let m=ol(h.body);if(!m.ok){s("✗ "+(u==="-"?"the feed":u)+" is not a valid leftoff-agent-feed/1 feed"+(i.sub==="publish"?"; nothing was published":"")+`:
511
- `);for(let S of m.errors.slice(0,20))s(" - "+S+`
507
+ `),0;let h=0,m=f?null:l();for(let y of p){let S;try{S=f?await e("POST","/v1/agents/"+encodeURIComponent(y.name)+"/refresh"):await m.pull(y.name,{force:!0})}catch(b){S={result:"error",error:{message:b.message}}}if(S.result==="error"){h++,s("✗ "+y.name+": "+(S.error&&S.error.message||"error")+`
508
+ `);for(let b of(S.error&&S.error.details||[]).slice(0,5))s(" - "+b+`
509
+ `)}else t("✓ "+y.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")+`
510
+ `)}return h?1:0}if(i.sub==="keygen"){let u=Xx();return t(i.json?JSON.stringify(u,null,2)+`
511
+ `:Qx(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+`
512
+ `),2;let f=i.writeKey,p=f==="-",h=eA(u,{stdinUsed:p});if(h.error)return s(h.error+`
513
+ `),1;let m=bl(h.body);if(!m.ok){s("✗ "+(u==="-"?"the feed":u)+" is not a valid leftoff-agent-feed/1 feed"+(i.sub==="publish"?"; nothing was published":"")+`:
514
+ `);for(let b of m.errors.slice(0,20))s(" - "+b+`
512
515
  `);return m.errors.length>20&&s(" … and "+(m.errors.length-20)+` more
513
516
  `),1}let y=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 ("+y+")."+(i.sub==="publish"?" Not published (--dry-run).":"")+`
514
517
  `),0;if(!i.url)return s(d+`
515
- `),2;p&&(f=Lw()),!f&&o.LEFTOFF_AGENT_WRITE_KEY&&(f=o.LEFTOFF_AGENT_WRITE_KEY);let w=await cx({body:h.body,url:i.url,writeKey:f,fetchImpl:r||globalThis.fetch,userAgent:"Leftoff/"+q});return w.ok?(t("✓ Published to "+w.url+" ("+y+(w.etag?", ETag "+w.etag:"")+`).
516
- `),0):(s("✗ Not published: "+w.errors.join("; ")+`
518
+ `),2;p&&(f=k0()),!f&&o.LEFTOFF_AGENT_WRITE_KEY&&(f=o.LEFTOFF_AGENT_WRITE_KEY);let S=await tA({body:h.body,url:i.url,writeKey:f,fetchImpl:r||globalThis.fetch,userAgent:"Leftoff/"+V});return S.ok?(t("✓ Published to "+S.url+" ("+y+(S.etag?", ETag "+S.etag:"")+`).
519
+ `),0):(s("✗ Not published: "+S.errors.join("; ")+`
517
520
  `),1)}return s("unknown: leftoff agents "+i.sub+`
518
521
  usage: leftoff agents [list|add|remove|refresh|keygen|publish|validate]
519
522
  `),2}catch(u){return s(u&&u.message||String(u)),s(`
520
- `),1}}qn();ji();ke();wn();qn();Ts();id();Br();import{spawnSync as ux}from"node:child_process";function jw(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 Fw(){let n=V();Te(n);let e=X(n),t=le(e);return{root:n,cfg:e,machine:t,folder:new gt(n,t)}}function Pf(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(Nt(e,"machines")))t.add(s);return[...t].filter(Boolean).sort()}var Pw=`leftoff spaces Macs, their default space, and overrides
523
+ `),1}}es();zi();ve();vn();es();Cs();Nd();Xr();import{spawnSync as nA}from"node:child_process";function A0(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 T0(){let n=Z();Ee(n);let e=X(n),t=de(e);return{root:n,cfg:e,machine:t,folder:new vt(n,t)}}function ah(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(Pt(e,"machines")))t.add(s);return[...t].filter(Boolean).sort()}var x0=`leftoff spaces Macs, their default space, and overrides
521
524
  leftoff spaces set-machine [mac] <space> a Mac's default space (default: this Mac)
522
525
  leftoff spaces set-repo <repo> <space> a repo counts as <space> on every Mac
523
526
  leftoff spaces set-session <id> <space> one session
524
527
  leftoff spaces set-agent <name> <space> an agent feed (default: the space of the Mac that pulls it)
525
528
  leftoff spaces unset machine|repo|session|agent <key>
526
529
  A session's space: its own override, else its repo's, else its Mac's default.
527
- Quick picks: Work, Personal (or any name).`;function Uw({argv:n=[],out:e=console.log}={}){let{flags:t,pos:s}=jw(n),o=Fw(),r=s[0]||"list",i=a=>e(a);try{if(r==="help"||t.help)return i(Pw),0;if(r==="list"||r==="ls"){let{map:c,conflicts:u}=As(o.root),d=Pf(o,c);if(t.json)return i(JSON.stringify({machine:o.machine,machines:d.map(f=>({machine:f,space:Kn(c,f),set:!!(c.machines[f]&&c.machines[f].space!=null)})),repos:Nt(c,"repos"),sessions:Nt(c,"sessions"),agents:Nt(c,"agents"),spaces:Zt(c,d)},null,2)),0;i("Spaces: "+(Zt(c,d).join(", ")||"(none)")),i("Macs (default space):");for(let f of d){let p=c.machines[f]&&c.machines[f].space!=null;i(" "+f.padEnd(18)+Kn(c,f)+(p?"":" (not chosen yet: leftoff spaces set-machine "+f+" Work|Personal)")+(f===o.machine?" ← this Mac":""))}for(let[f,p]of[["repos","Repo overrides"],["sessions","Session overrides"],["agents","Agent feeds"]]){let h=Nt(c,f);if(Object.keys(h).length){i(p+":");for(let[m,y]of Object.entries(h).sort(([w],[S])=>w.localeCompare(S)))i(" "+m.padEnd(28)+y)}}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)=>hu(o.root,(f,p)=>{let h=Nr(c),m=Wh(h,u),y=d==null?null:Lr(d);return y&&(y=[...Zt(f,Pf(o,f)),...fo].find(w=>w.toLowerCase()===y.toLowerCase())||y),f[h][m]={space:y,at:p.at,by:p.by},y},{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=Nr(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(Pw),1}catch(a){return console.error("✗ "+a.message),1}}function dx(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=ux(r,i,{input:n,stdio:["pipe","ignore","ignore"],timeout:5e3});if(!a.error&&a.status===0)return r}return null}async function Hw({argv:n=[],api:e=null,out:t=s=>process.stdout.write(s)}={}){let{flags:s}=jw(n),o=Fw(),{map:r}=As(o.root),i=typeof s.space=="string"?s.space:Kn(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=Ma(o.cfg.digest,Ft()),u=Date.now(),d=null;try{d=new vt(o.root,o.machine).view({fresh:!0})}catch{}let f=i?Zt(r,Pf(o,r)).find(p=>p.toLowerCase()===i.toLowerCase())||i:null;l={...ul(o.folder.loadAll(),{now:u,date:a||xo(u,c).date,space:f,spaces:r,cfg:o.cfg,tz:c.tz,digestTime:c.time||"07:00",agents:d}),date:a||xo(u,c).date}}if(s.json)return t(JSON.stringify(l,null,2)+`
530
+ Quick picks: Work, Personal (or any name).`;function _0({argv:n=[],out:e=console.log}={}){let{flags:t,pos:s}=A0(n),o=T0(),r=s[0]||"list",i=a=>e(a);try{if(r==="help"||t.help)return i(x0),0;if(r==="list"||r==="ls"){let{map:c,conflicts:u}=Os(o.root),d=ah(o,c);if(t.json)return i(JSON.stringify({machine:o.machine,machines:d.map(f=>({machine:f,space:zn(c,f),set:!!(c.machines[f]&&c.machines[f].space!=null)})),repos:Pt(c,"repos"),sessions:Pt(c,"sessions"),agents:Pt(c,"agents"),spaces:nn(c,d)},null,2)),0;i("Spaces: "+(nn(c,d).join(", ")||"(none)")),i("Macs (default space):");for(let f of d){let p=c.machines[f]&&c.machines[f].space!=null;i(" "+f.padEnd(18)+zn(c,f)+(p?"":" (not chosen yet: leftoff spaces set-machine "+f+" Work|Personal)")+(f===o.machine?" ← this Mac":""))}for(let[f,p]of[["repos","Repo overrides"],["sessions","Session overrides"],["agents","Agent feeds"]]){let h=Pt(c,f);if(Object.keys(h).length){i(p+":");for(let[m,y]of Object.entries(h).sort(([S],[b])=>S.localeCompare(b)))i(" "+m.padEnd(28)+y)}}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)=>Iu(o.root,(f,p)=>{let h=Hr(c),m=Ap(h,u),y=d==null?null:Gr(d);return y&&(y=[...nn(f,ah(o,f)),...bo].find(S=>S.toLowerCase()===y.toLowerCase())||y),f[h][m]={space:y,at:p.at,by:p.by},y},{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=Hr(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(x0),1}catch(a){return console.error("✗ "+a.message),1}}function sA(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=nA(r,i,{input:n,stdio:["pipe","ignore","ignore"],timeout:5e3});if(!a.error&&a.status===0)return r}return null}async function E0({argv:n=[],api:e=null,out:t=s=>process.stdout.write(s)}={}){let{flags:s}=A0(n),o=T0(),{map:r}=Os(o.root),i=typeof s.space=="string"?s.space:zn(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=Ja(o.cfg.digest,Kt()),u=Date.now(),d=null;try{d=new Ot(o.root,o.machine).view({fresh:!0})}catch{}let f=i?nn(r,ah(o,r)).find(p=>p.toLowerCase()===i.toLowerCase())||i:null;l={...El(o.folder.loadAll(),{now:u,date:a||Mo(u,c).date,space:f,spaces:r,cfg:o.cfg,tz:c.tz,digestTime:c.time||"07:00",agents:d}),date:a||Mo(u,c).date}}if(s.json)return t(JSON.stringify(l,null,2)+`
528
531
  `),0;if(t(l.text+`
529
- `),s.copy){let c=dx(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}pc();ke();Zn();pc();Ci();ki();ac();function fx(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 Mc=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 Bw(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 "+Mc(s.createdAt)+" on "+s.pairedOn+(s.lastSeenAt?" · last used "+Mc(s.lastSeenAt):""));return n.enabled&&e.push(" Board: "+n.boardUrl),e}async function hx(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===`
532
+ `),s.copy){let c=sA(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}Ic();ve();ns();Ic();Fi();Ni();Ac();function oA(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 qc=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 O0(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 "+qc(s.createdAt)+" on "+s.pairedOn+(s.lastSeenAt?" · last used "+qc(s.lastSeenAt):""));return n.enabled&&e.push(" Board: "+n.boardUrl),e}async function rA(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===`
530
533
  `||i===""?(s.setRawMode&&s.setRawMode(!1),s.pause(),s.off("data",r),process.stdout.write(`
531
534
  `),t(o)):i===""?(process.stdout.write(`
532
- `),process.exit(130)):i===""?o=o.slice(0,-1):o+=i};s.on("data",r)}));return String(e).trim()}function px(){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 Kw({argv:n=[],api:e,out:t=o=>console.log(o),err:s=o=>console.error(o)}={}){let{flags:o,pos:r}=fx(n),i=r[0]||"status",a=V(),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"){Te(a);let c=X(a),u=le(c),d=Dn(a),f=px(),p=String(r[1]||"").trim().toLowerCase();if(!p){if(!process.stdin.isTTY)return s("Usage: leftoff relay signup <name> --email=you@example.com"),2;p=(await f("Your name (for yourname."+(d.domain||Ln)+"): ")).trim().toLowerCase()}let h=Mn(p);if(!h.ok)return f.close(),s(h.code==="reserved"?"“"+p+"” 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:h.user,domain:String(o.domain||d.domain||Ln)},y=Tn(u),w=await Ii({s:m,name:h.user,sign:y.signX});if(!w.ok&&w.code!=="legacy")return f.close(),s(w.message),w.code==="unreachable"||w.code==="closed"?1:2;if(w.ok&&w.status==="yours"){if(f.close(),t("✓ "+w.message),!(d.enabled&&d.user===h.user)){us(a,{enabled:!0,user:h.user,domain:m.domain,registeredAt:d.registeredAt||new Date().toISOString(),via:d.via||"signup"});try{await e("POST","/v1/relay/enable",{user:h.user,domain:m.domain})}catch{}}return t(" Pair your phone: leftoff relay pair"),0}if(w.ok&&(w.status==="taken"||w.status==="pending"))return f.close(),s(w.message),s(" Yours on another Mac? leftoff relay enable "+h.user),1;w.ok&&w.claimed&&t(" "+w.host+" was claimed on leftoff.dev. Use the email you claimed it with.");let S=typeof o.email=="string"?o.email.trim():process.env.LEFTOFF_SIGNUP_EMAIL||"";if(!S){if(!process.stdin.isTTY)return f.close(),s("Add your email: leftoff relay signup "+h.user+" --email=you@example.com"),2;S=(await f("Email (for the confirmation link): ")).trim()}if(f.close(),!rc(S))return s("That doesn’t look like an email address."),2;let k=await Ni({s:m,name:h.user,email:S,machine:u,sign:y.signX});if(!k.ok)return s(k.message),1;if(k.already)t("✓ This Mac already has "+Rt(m)+".");else{if(t("✉ Sent a link to "+k.email+". Open it and press Confirm (it works for 30 minutes)."),k.claimed&&t(" Your leftoff.dev claim for "+h.user+" carries over."),o["no-wait"])return t(" Not waiting (--no-wait). After you confirm: leftoff relay enable "+h.user),0;t(" Waiting for you to confirm… (Ctrl-C stops waiting; the link still works, then run: leftoff relay enable "+h.user+")");let v=!1,T=()=>{v=!0};process.once("SIGINT",T);let b=await Zd({s:m,user:h.user,poll:k.poll,intervalMs:Number(process.env.LEFTOFF_SIGNUP_POLL_MS)||void 0,stop:()=>v});if(process.removeListener("SIGINT",T),b==="stopped")return t(`
533
- Stopped waiting. Once you press Confirm: leftoff relay enable `+h.user),130;if(b==="expired"||b==="timeout")return s("The link expired before it was confirmed. Run `leftoff relay signup "+h.user+"` again for a fresh one."),1;if(b==="replaced")return s("A newer link was sent for "+h.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 "+h.user+"` again."),1}us(a,{enabled:!0,user:h.user,domain:m.domain,registeredAt:new Date().toISOString(),via:"signup"}),t("✓ "+Rt(m)+" is yours. Pair your phone: leftoff relay pair");try{await e("POST","/v1/relay/enable",{user:h.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 "+h.user+" (while this Mac is awake, it vouches for them)"),0}if(i==="register"){Te(a);let c=X(a),u=le(c),d=Mn(r[1]||Dn(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={...Dn(a),user:d.user,domain:String(o.domain||Dn(a).domain||Ln)},p=await hx(o);if(!p)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 h=Tn(u),m;try{m=await Oi(f,"/_admin/register",{machine:u,sign:h.signX,replace:!!o.replace},p)}catch(y){return s("Couldn’t reach "+Rt(f)+": "+y.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;us(a,{enabled:!0,user:d.user,domain:f.domain,registeredAt:new Date().toISOString()}),t("✓ "+u+" is on "+Rt(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=Dn(a);c={...d,host:d.user?Rt(d):null,boardUrl:d.user?Ri(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 Bw(c))t(u);return 0}if(i==="enable"){let c=r[1]||Dn(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 Bw(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(Wl(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 "+Mc(u.createdAt)+" on "+u.pairedOn+(u.lastSeenAt?" · last used "+Mc(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}}Ts();yf();lo();ui();Fo();Ms();Jd();var Ff=ps.resolve(ps.dirname(mx(import.meta.url)),".."),Wt=process.argv.slice(2),Qw=Wt[0],ce={},it=[];for(let n of Wt.slice(1)){let e=n.match(/^--([^=]+)(?:=(.*))?$/);e?ce[e[1]]=e[2]===void 0?!0:e[2]:it.push(n)}async function Oe(n,e,t){let s=X(),o=await fetch("http://127.0.0.1:"+s.port+e,{method:n,headers:{authorization:"Bearer "+(Se()||""),"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 gx(){let n=V();Te(n);let e=X(n);ce.port&&(e.port=Number(ce.port));let t=i=>console.log(new Date().toISOString()+" "+i);for(let i of Sh(n))t("moved "+i+" out of the folder to "+ps.join(qt(),i)+" (keys stay on this Mac)");let s=new Yl({root:n,cfg:e,log:t}).load().start();s.relayHost=new Pi({engine:s,root:n,port:e.port,log:t}),await s.startPeers();let o=nw(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 "+q+" 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 yx(){let n=Te();if(console.log("✓ folder ready: "+n.root+" (config.json, token)"),ce.hooks){let e=Ko();console.log("✓ settings hooks added to "+e+" (a backup sits next to it)")}if(ce.service){let e=At();Yt.mkdirSync(ps.dirname(e),{recursive:!0}),Yt.writeFileSync(e,En({root:V()})),console.log("✓ login service written: "+e),console.log(" enable it with: "+Ag())}console.log(""),console.log("Next:"),ce.service||console.log(" 1. start the daemon: leftoff serve (or re-run init with --service)"),console.log(" 2. load the mod: claude --plugin-dir "+ps.join(Ff,"mod")+" (one session)"),console.log(" or install it: claude plugin marketplace add "+Ff+" && claude plugin install leftoff@checkpoints"),ce.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:"+X().port+"/)")}async function wx(){try{let n=await Oe("GET","/v1/health"),e=typeof ce.repo=="string"?ce.repo:ce.repo===!0?it[0]:null,{sessions:t}=await Oe("GET","/v1/sessions?limit="+(e?300:30));e&&(t=e.split(",").flatMap(r=>Ia(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=X(),o=await _i({port:s.port,prefer:process.env.CHECKPOINTS_OPEN_HOST||s.openHost||"auto",token:Se()});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 "+Ki(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 Oe("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 Sx(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 kx(){let n=typeof ce.date=="string"?ce.date:void 0;if(!ce.offline)try{let a=await Oe("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=V();Te(e);let t=X(e),s=new gt(e,le(t)),o=(()=>{try{return new vt(e,le(t)).view({fresh:!0})}catch{return null}})(),{meta:r,markdown:i}=await ll(s.loadAll(),{cfg:t,date:n,machine:le(t),summarizer:new yt(t),agents:o,spaces:As(e).map});s.writeDigest(r.date,i),process.stdout.write(i)}async function Gw(){process.exitCode=Uw({argv:Wt.slice(1)})}async function bx(){process.exitCode=await Hw({argv:Wt.slice(1),api:Oe})}async function $w(){process.exitCode=await Kw({argv:Wt.slice(1),api:Oe})}async function Yw(){process.exitCode=await Ow({argv:Wt.slice(1),api:Oe})}async function Ww(){process.exitCode=await Jy({argv:Wt.slice(1),api:Oe})}async function vx(){process.exitCode=await qy({argv:Wt.slice(1)})}async function xx(){process.exitCode=await df({argv:Wt.slice(1)})}async function zw(){let n=it[0];if(n==="allow"||n==="deny"){let t=await Oe("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(!it[1])throw new Error("usage: leftoff machines trust <machine>");await Oe("POST","/v1/peers/"+encodeURIComponent(it[1])+"/trust"),console.log("✓ Trusting "+it[1]+"’s new key");return}let e=await Oe("POST","/v1/peers/check");if(ce.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 Jw(){process.exitCode=await Rw({argv:Wt.slice(1),api:Oe})}async function qw(){process.exitCode=await Iw({argv:Wt.slice(1),api:Oe})}async function Ax(){let n=it[0];if(!n)throw new Error("usage: leftoff checkpoint <session-id> [--short|--informative|--long] [note]");let e=ce.short?"short":ce.long?"long":ce.informative?"informative":ce.length||void 0,t=await Oe("POST","/v1/sessions/"+encodeURIComponent(n)+"/checkpoints",{trigger:"manual",generate:!0,length:e,userNote:it.slice(1).join(" ")||void 0});console.log(nc(t))}async function Tx(){let n=V(),e=we(n),t=X(n),s=(c,u)=>console.log((c?"✓ ":"✗ ")+u);s(Yt.existsSync(e.root),"folder "+e.root),s(!!Se(n),"token "+e.token);let o=null;try{o=await Oe("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(Yt.readFileSync(ps.join(n,".mod-health."+le(t).replace(/[^\w.-]/g,"_")+".json"),"utf8"))}catch{}for(let c of Nx(r))console.log(c);if(o&&o.update){let c=o.update;c.available?console.log("! update available: v"+q+" → "+(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"+q+", 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=Yt.readFileSync(sn(),"utf8")}catch{}let a=i.includes(Yr)||i.includes(Ds);if(console.log((a?"✓ ":"· ")+"settings hooks "+(a?"installed":"not installed (fine if the mod is loaded)")),i.includes(Ds)&&console.log("! settings hooks are from before v0.4.5 (they still work); refresh them with: leftoff upgrade --force"),Yt.existsSync(Go())&&console.log("! the login service still has its old name ("+Go().replace(jf.homedir(),"~")+"); move it with: leftoff upgrade --force"),console.log('· mod: run /plugin in Claude Code and look for "'+Ce+'" (before v0.4.5: checkpoints@checkpoints; leftoff upgrade moves it); in a session type /cp, /cp-name or /leftoff'),console.log("· summarizer mode: "+new yt(t).mode),o&&o.digest&&console.log(Sx(o.digest)),o&&o.sessions)for(let c of await _x(o.sessions))console.log(c);console.log(If(Of({port:t.port,cfgHosts:t.remoteHosts||[]}),{cfgHosts:t.remoteHosts||[]}));try{let c={ok:"✓ ",warn:"! ",info:"· "};for(let[u,d]of gf(await Vt({root:n}),Fn(It())))console.log(c[u]+d)}catch(c){console.log("· Syncthing: couldn't check ("+c.message+")")}let l=(()=>{try{return Yt.readdirSync(e.secret).filter(c=>/-api-key$/.test(c))}catch{return[]}})();try{let c=null;if(o)try{c=await Oe("GET","/v1/agents")}catch{}c||(c=Nc(new vt(n,le(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=Rf(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 "+qt().replace(jf.homedir(),"~")+" at its next start");for(let c of await Ex(o))console.log(c)}async function _x(n){let e=[];if(n.ghosts){let t="";try{let{sessions:s}=await Oe("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 Ex(n){let e=[],t=Aw(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+")")),Ox()===!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(ce["no-prompt"])return e;let i=await Cl(r,{timeoutMs:8e3});if(e.push((i.ok?"✓ ":"✗ ")+"automation from this terminal: "+i.message),n)try{let a=await Oe("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 Cx(n,s)),process.platform==="darwin"){let a=At(),l=null;try{l=Yt.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(jf.homedir(),"~")+" and restarts it)")}return e}async function Cx(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 xl({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 Oe("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 Ox(){try{return X().tabTitle}catch{return!0}}async function Vw(){process.exitCode=await xw({pos:it,flags:ce,api:Oe})}async function Ix(){if(ce.help){console.log(Tw);return}process.exitCode=await _w({pos:it,flags:ce,api:Oe,platform:process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform})}function Nx(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 Mx(){let n=Hl();console.log("removed "+n+" settings hook(s) from "+sn());for(let e of[At(),Go()])Yt.existsSync(e)&&(Yt.unlinkSync(e),console.log("removed "+e+" (stop it: "+(process.platform==="darwin"?"launchctl unload "+e:"systemctl --user disable --now "+ps.basename(e))+")"));console.log("mod: claude plugin uninstall "+Ce),ce.purge?(Yt.rmSync(V(),{recursive:!0,force:!0}),console.log("deleted "+V())):console.log("kept your folder: "+V()+" (pass --purge to delete it)")}function Lc(){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 ${q} · WTF was I working on? Track every Claude Code session, checkpoint the quiet ones
535
+ `),process.exit(130)):i===""?o=o.slice(0,-1):o+=i};s.on("data",r)}));return String(e).trim()}function iA(){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 C0({argv:n=[],api:e,out:t=o=>console.log(o),err:s=o=>console.error(o)}={}){let{flags:o,pos:r}=oA(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"){Ee(a);let c=X(a),u=de(c),d=Fn(a),f=iA(),p=String(r[1]||"").trim().toLowerCase();if(!p){if(!process.stdin.isTTY)return s("Usage: leftoff relay signup <name> --email=you@example.com"),2;p=(await f("Your name (for yourname."+(d.domain||jn)+"): ")).trim().toLowerCase()}let h=Pn(p);if(!h.ok)return f.close(),s(h.code==="reserved"?"“"+p+"” 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:h.user,domain:String(o.domain||d.domain||jn)},y=Cn(u),S=await Hi({s:m,name:h.user,sign:y.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===h.user)){ms(a,{enabled:!0,user:h.user,domain:m.domain,registeredAt:d.registeredAt||new Date().toISOString(),via:d.via||"signup"});try{await e("POST","/v1/relay/enable",{user:h.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 "+h.user),1;S.ok&&S.claimed&&t(" "+S.host+" was claimed on leftoff.dev. Use the email you claimed it with.");let b=typeof o.email=="string"?o.email.trim():process.env.LEFTOFF_SIGNUP_EMAIL||"";if(!b){if(!process.stdin.isTTY)return f.close(),s("Add your email: leftoff relay signup "+h.user+" --email=you@example.com"),2;b=(await f("Email (for the confirmation link): ")).trim()}if(f.close(),!vc(b))return s("That doesn’t look like an email address."),2;let w=await Bi({s:m,name:h.user,email:b,machine:u,sign:y.signX});if(!w.ok)return s(w.message),1;if(w.already)t("✓ This Mac already has "+Ht(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 "+h.user+" carries over."),o["no-wait"])return t(" Not waiting (--no-wait). After you confirm: leftoff relay enable "+h.user),0;t(" Waiting for you to confirm… (Ctrl-C stops waiting; the link still works, then run: leftoff relay enable "+h.user+")");let A=!1,v=()=>{A=!0};process.once("SIGINT",v);let k=await xf({s:m,user:h.user,poll:w.poll,intervalMs:Number(process.env.LEFTOFF_SIGNUP_POLL_MS)||void 0,stop:()=>A});if(process.removeListener("SIGINT",v),k==="stopped")return t(`
536
+ Stopped waiting. Once you press Confirm: leftoff relay enable `+h.user),130;if(k==="expired"||k==="timeout")return s("The link expired before it was confirmed. Run `leftoff relay signup "+h.user+"` again for a fresh one."),1;if(k==="replaced")return s("A newer link was sent for "+h.user+"; this one no longer counts. Use the newest email (or run signup again)."),1;if(k!=="confirmed")return s("Lost track of the signup ("+k+"). Run `leftoff relay signup "+h.user+"` again."),1}ms(a,{enabled:!0,user:h.user,domain:m.domain,registeredAt:new Date().toISOString(),via:"signup"}),t("✓ "+Ht(m)+" is yours. Pair your phone: leftoff relay pair");try{await e("POST","/v1/relay/enable",{user:h.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 "+h.user+" (while this Mac is awake, it vouches for them)"),0}if(i==="register"){Ee(a);let c=X(a),u=de(c),d=Pn(r[1]||Fn(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={...Fn(a),user:d.user,domain:String(o.domain||Fn(a).domain||jn)},p=await rA(o);if(!p)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 h=Cn(u),m;try{m=await Ui(f,"/_admin/register",{machine:u,sign:h.signX,replace:!!o.replace},p)}catch(y){return s("Couldn’t reach "+Ht(f)+": "+y.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;ms(a,{enabled:!0,user:d.user,domain:f.domain,registeredAt:new Date().toISOString()}),t("✓ "+u+" is on "+Ht(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=Fn(a);c={...d,host:d.user?Ht(d):null,boardUrl:d.user?Yi(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 O0(c))t(u);return 0}if(i==="enable"){let c=r[1]||Fn(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 O0(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(cc(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 "+qc(u.createdAt)+" on "+u.pairedOn+(u.lastSeenAt?" · last used "+qc(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}}Cs();Gf();wo();ki();Jo();Hs();kf();var ch=Ss.resolve(Ss.dirname(aA(import.meta.url)),".."),Xt=process.argv.slice(2),H0=Xt[0],fe={},ut=[];for(let n of Xt.slice(1)){let e=n.match(/^--([^=]+)(?:=(.*))?$/);e?fe[e[1]]=e[2]===void 0?!0:e[2]:ut.push(n)}async function Ie(n,e,t){let s=X(),o=await fetch("http://127.0.0.1:"+s.port+e,{method:n,headers:{authorization:"Bearer "+(be()||""),"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 lA(){let n=Z();Ee(n);let e=X(n);fe.port&&(e.port=Number(fe.port));let t=i=>console.log(new Date().toISOString()+" "+i);for(let i of Zh(n))t("moved "+i+" out of the folder to "+Ss.join(en(),i)+" (keys stay on this Mac)");let s=new lc({root:n,cfg:e,log:t}).load().start();s.relayHost=new Wi({engine:s,root:n,port:e.port,log:t}),await s.startPeers();let o=Kw(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 "+V+" 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 cA(){let n=Ee();if(console.log("✓ folder ready: "+n.root+" (config.json, token)"),fe.hooks){let e=Xo();console.log("✓ settings hooks added to "+e+" (a backup sits next to it)")}if(fe.service){let e=Nt();Zt.mkdirSync(Ss.dirname(e),{recursive:!0}),Zt.writeFileSync(e,In({root:Z()})),console.log("✓ login service written: "+e),console.log(" enable it with: "+fy())}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 "+Ss.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:"+X().port+"/)")}async function uA(){try{let n=await Ie("GET","/v1/health"),e=typeof fe.repo=="string"?fe.repo:fe.repo===!0?ut[0]:null,{sessions:t}=await Ie("GET","/v1/sessions?limit="+(e?300:30));e&&(t=e.split(",").flatMap(r=>Wa(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=X(),o=await Pi({port:s.port,prefer:process.env.CHECKPOINTS_OPEN_HOST||s.openHost||"auto",token:be()});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 "+Xi(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 Ie("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 dA(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 fA(){let n=typeof fe.date=="string"?fe.date:void 0;if(!fe.offline)try{let a=await Ie("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();Ee(e);let t=X(e),s=new vt(e,de(t)),o=(()=>{try{return new Ot(e,de(t)).view({fresh:!0})}catch{return null}})(),{meta:r,markdown:i}=await Tl(s.loadAll(),{cfg:t,date:n,machine:de(t),summarizer:new xt(t),agents:o,spaces:Os(e).map});s.writeDigest(r.date,i),process.stdout.write(i)}async function N0(){process.exitCode=_0({argv:Xt.slice(1)})}async function hA(){process.exitCode=await E0({argv:Xt.slice(1),api:Ie})}async function I0(){process.exitCode=await C0({argv:Xt.slice(1),api:Ie})}async function M0(){process.exitCode=await g0({argv:Xt.slice(1),api:Ie})}async function L0(){process.exitCode=await Rw({argv:Xt.slice(1),api:Ie})}async function pA(){process.exitCode=await Pw({argv:Xt.slice(1)})}async function mA(){process.exitCode=await Pf({argv:Xt.slice(1)})}async function D0(){let n=ut[0];if(n==="allow"||n==="deny"){let t=await Ie("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(!ut[1])throw new Error("usage: leftoff machines trust <machine>");await Ie("POST","/v1/peers/"+encodeURIComponent(ut[1])+"/trust"),console.log("✓ Trusting "+ut[1]+"’s new key");return}let e=await Ie("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 R0(){process.exitCode=await v0({argv:Xt.slice(1),api:Ie})}async function P0(){process.exitCode=await y0({argv:Xt.slice(1),api:Ie})}async function gA(){let n=ut[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 Ie("POST","/v1/sessions/"+encodeURIComponent(n)+"/checkpoints",{trigger:"manual",generate:!0,length:e,userNote:ut.slice(1).join(" ")||void 0});console.log(Sc(t))}async function yA(){let n=Z(),e=ke(n),t=X(n),s=(c,u)=>console.log((c?"✓ ":"✗ ")+u);s(Zt.existsSync(e.root),"folder "+e.root),s(!!be(n),"token "+e.token);let o=null;try{o=await Ie("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(Zt.readFileSync(Ss.join(n,".mod-health."+de(t).replace(/[^\w.-]/g,"_")+".json"),"utf8"))}catch{}for(let c of xA(r))console.log(c);if(o&&o.update){let c=o.update;c.available?console.log("! update available: v"+V+" → "+(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"+V+", 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=Zt.readFileSync(ln(),"utf8")}catch{}let a=i.includes(ni)||i.includes(Gs);if(console.log((a?"✓ ":"· ")+"settings hooks "+(a?"installed":"not installed (fine if the mod is loaded)")),i.includes(Gs)&&console.log("! settings hooks are from before v0.4.5 (they still work); refresh them with: leftoff upgrade --force"),Zt.existsSync(Qo())&&console.log("! the login service still has its old name ("+Qo().replace(lh.homedir(),"~")+"); move it with: leftoff upgrade --force"),console.log('· mod: run /plugin in Claude Code and look for "'+Ne+'" (before v0.4.5: checkpoints@checkpoints; leftoff upgrade moves it); in a session type /cp, /cp-name or /leftoff'),console.log("· summarizer mode: "+new xt(t).mode),o&&o.digest&&console.log(dA(o.digest)),o&&o.sessions)for(let c of await wA(o.sessions))console.log(c);console.log(th(eh({port:t.port,cfgHosts:t.remoteHosts||[]}),{cfgHosts:t.remoteHosts||[]}));try{let c={ok:"✓ ",warn:"! ",info:"· "};for(let[u,d]of Bf(await tn({root:n}),Gn(Rt())))console.log(c[u]+d)}catch(c){console.log("· Syncthing: couldn't check ("+c.message+")")}let l=(()=>{try{return Zt.readdirSync(e.secret).filter(c=>/-api-key$/.test(c))}catch{return[]}})();try{let c=null;if(o)try{c=await Ie("GET","/v1/agents")}catch{}c||(c=Jc(new Ot(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=ih(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(lh.homedir(),"~")+" at its next start");for(let c of await SA(o))console.log(c)}async function wA(n){let e=[];if(n.ghosts){let t="";try{let{sessions:s}=await Ie("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 SA(n){let e=[],t=d0(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+")")),bA()===!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 Yl(r,{timeoutMs:8e3});if(e.push((i.ok?"✓ ":"✗ ")+"automation from this terminal: "+i.message),n)try{let a=await Ie("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 kA(n,s)),process.platform==="darwin"){let a=Nt(),l=null;try{l=Zt.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(lh.homedir(),"~")+" and restarts it)")}return e}async function kA(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 Hl({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 Ie("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 bA(){try{return X().tabTitle}catch{return!0}}async function j0(){process.exitCode=await u0({pos:ut,flags:fe,api:Ie})}async function vA(){if(fe.help){console.log(f0);return}process.exitCode=await h0({pos:ut,flags:fe,api:Ie,platform:process.env.CHECKPOINTS_JUMP_PLATFORM||process.platform})}function xA(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 AA(){let n=sc();console.log("removed "+n+" settings hook(s) from "+ln());for(let e of[Nt(),Qo()])Zt.existsSync(e)&&(Zt.unlinkSync(e),console.log("removed "+e+" (stop it: "+(process.platform==="darwin"?"launchctl unload "+e:"systemctl --user disable --now "+Ss.basename(e))+")"));console.log("mod: claude plugin uninstall "+Ne),fe.purge?(Zt.rmSync(Z(),{recursive:!0,force:!0}),console.log("deleted "+Z())):console.log("kept your folder: "+Z()+" (pass --purge to delete it)")}function Vc(){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 ${V} · WTF was I working on? Track every Claude Code session, checkpoint the quiet ones
534
537
  ${n}
535
538
  leftoff setup guided install: the mod, a summarizer (local models too), the daemon
536
539
  [--yes] [--dir=~/checkpoints] [--machine=NAME] [--capture=mod|hooks|none]
@@ -598,4 +601,4 @@ ${n}
598
601
  leftoff hook <Event> settings-hook entry point (reads stdin)
599
602
  leftoff doctor
600
603
  leftoff uninstall [--purge]
601
- `)}async function Lx(){let n=await uf({flags:ce});n.ok||(process.exitCode=n.cancelled?130:1)}async function Zw(){process.exitCode=await ny({flags:ce,bin:ps.join(Ff,"bin","leftoff.js")})}async function Dx(){process.exitCode=await hw({flags:ce})}async function Xw(){process.exitCode=await fw({flags:ce})}async function Rx(){if(ce.help){console.log(Bi());return}process.exitCode=await Nf({sub:"link",flags:ce})}async function Px(){if(ce.help){console.log(Bi());return}process.exitCode=await Nf({sub:it[0]||"status",flags:ce})}async function jx(){process.exitCode=await iw({sub:it[0],args:it.slice(1),flags:ce})}var Fx={setup:Lx,models:jx,go:Vw,jump:Vw,restore:Ix,tailscale:Px,link:Rx,upgrade:Dx,changelog:Xw,"whats-new":Xw,open:Zw,board:Zw,serve:gx,init:yx,status:wx,digest:kx,catchup:Yw,"catch-up":Yw,todo:qw,todos:qw,spaces:Gw,space:Gw,standup:bx,relay:$w,anywhere:$w,agents:Jw,agent:Jw,move:xx,sync:Ww,syncthing:Ww,join:vx,machines:zw,peers:zw,checkpoint:Ax,doctor:Tx,uninstall:Mx,hook:async()=>{it[0]==="PermissionRequest"?(await rw(),process.exit(0)):await ow(it[0])},help:Lc,"--help":Lc,"-h":Lc,version:()=>console.log(q),"--version":()=>console.log(q)},Ux=Fx[Qw]||Lc;Promise.resolve(Ux()).catch(n=>{console.error((globalThis.LEFTOFF_INVOKED_AS||"leftoff")+": "+n.message),process.exit(Qw==="hook"?0:1)});
604
+ `)}async function TA(){let n=await Rf({flags:fe});n.ok||(process.exitCode=n.cancelled?130:1)}async function F0(){process.exitCode=await Ky({flags:fe,bin:Ss.join(ch,"bin","leftoff.js")})}async function _A(){process.exitCode=await e0({flags:fe})}async function U0(){process.exitCode=await Qw({flags:fe})}async function EA(){if(fe.help){console.log(Zi());return}process.exitCode=await nh({sub:"link",flags:fe})}async function OA(){if(fe.help){console.log(Zi());return}process.exitCode=await nh({sub:ut[0]||"status",flags:fe})}async function CA(){process.exitCode=await zw({sub:ut[0],args:ut.slice(1),flags:fe})}var NA={setup:TA,models:CA,go:j0,jump:j0,restore:vA,tailscale:OA,link:EA,upgrade:_A,changelog:U0,"whats-new":U0,open:F0,board:F0,serve:lA,init:cA,status:uA,digest:fA,catchup:M0,"catch-up":M0,todo:P0,todos:P0,spaces:N0,space:N0,standup:hA,relay:I0,anywhere:I0,agents:R0,agent:R0,move:mA,sync:L0,syncthing:L0,join:pA,machines:D0,peers:D0,checkpoint:gA,doctor:yA,uninstall:AA,hook:async()=>{ut[0]==="PermissionRequest"?(await Ww(),process.exit(0)):await Yw(ut[0])},help:Vc,"--help":Vc,"-h":Vc,version:()=>console.log(V),"--version":()=>console.log(V)},IA=NA[H0]||Vc;Promise.resolve(IA()).catch(n=>{console.error((globalThis.LEFTOFF_INVOKED_AS||"leftoff")+": "+n.message),process.exit(H0==="hook"?0:1)});