bostrat-node 1.14.2 → 1.14.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +4 -4
  2. package/dist/index.js +231 -231
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -47,7 +47,7 @@ exit 0
47
47
  curl -sS -m 3 -X POST "$${Ee}/api/deps/refresh" \\
48
48
  -H 'content-type: application/json' -H "${Ke}: $${ie}" --data-binary '{}' >/dev/null 2>&1 || true
49
49
  exit 0
50
- `});import{execFile as os}from"node:child_process";import{promisify as ss}from"node:util";import fr from"node:fs";import is from"node:os";function pr(e){return`/tmp/tmux-${e}/default`}function cs({explicit:e,legacyExists:t,runtimeDir:n,uid:o}){if(e)return e;let i=pr(o);return t?i:n?`${n}/bostrat/tmux-default`:i}async function en(e,{loginShell:t=!1,failOpen:n=!1}={}){let o=await us(e,{loginShell:t});return o===hr?!0:o===Qt?!1:n}async function us(e,{loginShell:t=!1,timeoutMs:n=ls}={}){if(!e)return Qt;let o=t?process.env.SHELL||"/bin/sh":"sh",i=t?"-lic":"-c";try{return await as(o,[i,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),hr}catch(c){return c?.killed||c?.signal?dr:c?.code===1||c?.code===127?Qt:dr}}var as,Jc,ur,Zc,Qc,el,tl,nl,hr,Qt,dr,ls,mt,rl,ol,sl,il,al,cl,Wt=k(()=>{Vn();Yn();Gt();Xn();or();ar();Kt();ct();lr();as=ss(os),Jc=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";ur=typeof process.getuid=="function"?process.getuid():0,Zc=cs({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return fr.existsSync(pr(ur))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:ur}),Qc=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",el=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",tl=Object.freeze(["BOSTRAT_PLANE_URL","BOSTRAT_PLANE_TOKEN"]),nl=Object.freeze(["TMPDIR","CLAUDE_CODE_TMPDIR"]);hr="present",Qt="absent",dr="unknown",ls=5e3;mt={claude:"npm install -g @anthropic-ai/claude-code",codex:"npm install -g @openai/codex","cursor-agent":"curl https://cursor.com/install -fsS | bash",gh:"apt install gh (Debian: enable cli.github.com/packages) / brew install gh \u2014 see https://github.com/cli/cli#installation"},rl=(()=>{try{let e=is.homedir();if(e&&fr.existsSync(e))return e}catch{}return"/"})(),ol=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,sl=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,il=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,al=Number(process.env.BOSTRAT_BOX_COLS)||120,cl=Number(process.env.BOSTRAT_BOX_ROWS)||40});var Ar={};Ft(Ar,{NODE_IMAGE:()=>nn,commandPlan:()=>_r,flyToml:()=>Sr,flyWizard:()=>As,parseFlyFlags:()=>vr,stageConfig:()=>br});import tn from"node:fs";import _e from"node:path";import ys from"node:os";import gs from"node:readline/promises";import{execFile as Ts}from"node:child_process";import{createRequire as Ss}from"node:module";import{randomUUID as _s}from"node:crypto";function Sr({app:e,region:t,nodeName:n=e,planeUrl:o=Tr,image:i=nn}){return`# bostrat node on YOUR Fly org \u2014 written by \`bostrat-node fly\`.
50
+ `});import{execFile as os}from"node:child_process";import{promisify as ss}from"node:util";import fr from"node:fs";import is from"node:os";function pr(e){return`/tmp/tmux-${e}/default`}function cs({explicit:e,legacyExists:t,runtimeDir:n,uid:o}){if(e)return e;let i=pr(o);return t?i:n?`${n}/bostrat/tmux-default`:i}async function en(e,{loginShell:t=!1,failOpen:n=!1}={}){let o=await us(e,{loginShell:t});return o===hr?!0:o===Qt?!1:n}async function us(e,{loginShell:t=!1,timeoutMs:n=ls}={}){if(!e)return Qt;let o=t?process.env.SHELL||"/bin/sh":"sh",i=t?"-lic":"-c";try{return await as(o,[i,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),hr}catch(c){return c?.killed||c?.signal?dr:c?.code===1||c?.code===127?Qt:dr}}var as,Zc,ur,Qc,el,tl,nl,rl,hr,Qt,dr,ls,mt,ol,sl,il,al,cl,ll,Wt=k(()=>{Vn();Yn();Gt();Xn();or();ar();Kt();ct();lr();as=ss(os),Zc=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";ur=typeof process.getuid=="function"?process.getuid():0,Qc=cs({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return fr.existsSync(pr(ur))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:ur}),el=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",tl=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",nl=Object.freeze(["BOSTRAT_PLANE_URL","BOSTRAT_PLANE_TOKEN"]),rl=Object.freeze(["TMPDIR","CLAUDE_CODE_TMPDIR"]);hr="present",Qt="absent",dr="unknown",ls=5e3;mt={claude:"npm install -g @anthropic-ai/claude-code",codex:"npm install -g @openai/codex","cursor-agent":"curl https://cursor.com/install -fsS | bash",gh:"apt install gh (Debian: enable cli.github.com/packages) / brew install gh \u2014 see https://github.com/cli/cli#installation"},ol=(()=>{try{let e=is.homedir();if(e&&fr.existsSync(e))return e}catch{}return"/"})(),sl=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,il=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,al=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,cl=Number(process.env.BOSTRAT_BOX_COLS)||120,ll=Number(process.env.BOSTRAT_BOX_ROWS)||40});var Ar={};Ft(Ar,{NODE_IMAGE:()=>nn,commandPlan:()=>_r,flyToml:()=>Sr,flyWizard:()=>As,parseFlyFlags:()=>vr,stageConfig:()=>br});import tn from"node:fs";import _e from"node:path";import ys from"node:os";import gs from"node:readline/promises";import{execFile as Ts}from"node:child_process";import{createRequire as Ss}from"node:module";import{randomUUID as _s}from"node:crypto";function Sr({app:e,region:t,nodeName:n=e,planeUrl:o=Tr,image:i=nn}){return`# bostrat node on YOUR Fly org \u2014 written by \`bostrat-node fly\`.
51
51
  # Redeploy after edits: fly deploy <this dir> -c <this file> -a ${e} --ha=false
52
52
  # No [http_service] and none should be added: the node dials OUT to the plane;
53
53
  # nothing inbound, ever. The node token lives in \`fly secrets\`, not here.
@@ -120,7 +120,7 @@ Run it? (Y/n)`,"Y")).toLowerCase().startsWith("n"))return n.write(`aborted \u201
120
120
  redeploy: fly deploy ${E} -c ${_e.join(E,"fly.toml")} -a ${f} --ha=false
121
121
  Vendor logins (Claude/Codex) are NOT set up here \u2014 you type those later, inside
122
122
  boxes, and they land on this machine's volume, never on bostrat (box-auth-policy).
123
- `),h(0)}var Tr,vs,nn,wt,kr=k(()=>{Tr="wss://plane.bostrat.ai/v1/node",vs=Ss(import.meta.url)("../package.json").version,nn=`ghcr.io/globocodes/bostrat-node:${vs}`;wt=(e,{stdin:t,exec:n}={})=>new Promise((o,i)=>{if(n)return n(e,{stdin:t}).then(o,i);let c=Ts("fly",e,{timeout:6e5,maxBuffer:16*1024*1024},(d,m,h)=>d?i(Object.assign(d,{stdout:m,stderr:h})):o({stdout:m,stderr:h}));t!=null&&c.stdin?.end(t)})});function $r(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!$s.test(String(e)))throw new Error(`pricing.js: ${n} window ${t} must be YYYY-MM-DD, got ${JSON.stringify(e)}`);let o=Date.parse(`${e}T${t==="from"?"00:00:00.000":"23:59:59.999"}Z`);if(!Number.isFinite(o))throw new Error(`pricing.js: ${n} window ${t} is not a real date: ${e}`);return o}function xs(e){let t={};for(let[n,o]of Object.entries(e)){if(!Array.isArray(o)||o.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);t[n]=o.map(i=>({...i,fromMs:$r(i.from,"from",n),toMs:$r(i.to,"to",n)}))}return t}var ks,$s,_l,vl,xr=k(()=>{ks={"claude-opus-5-5":[{from:null,to:null,input:4,output:20,cacheWrite:8,cacheRead:.2,cacheReadX:.05,fast:{input:8,output:40,cacheWrite:16,cacheRead:.4},verifiedAt:"2026-09-28",note:"verified 2026-09-28 against platform.claude.com/docs/en/about-claude/pricing: $4/$20, 1h cache write $8 (2\xD7), cache hits $0.20 (0.05\xD7 \u2014 footnote 2), fast $8/$40; added before any fleet turn was recorded."}],"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5-1":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:.25,cacheReadX:.025,verifiedAt:"2026-09-07",note:"verified 2026-09-07 against platform.claude.com/docs/en/about-claude/pricing: $10/$50, 1h cache write $20 (2\xD7), cache hits $0.25 (0.025\xD7 \u2014 the 5.1-only multiplier; footnote 1 on the page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:"2026-08-23",input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL). Superseded 2026-08-24 by the vendor's promotional cut (window below)."},{from:"2026-08-24",to:null,input:4,output:20,cacheWrite:5,cacheRead:.4,verifiedAt:"2026-08-24",note:"promotional pricing verified 2026-08-24 (developers.openai.com pricing), 'at least through November 21, 2026'; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-6-astra":[{from:null,to:null,input:10,output:50,cacheWrite:12.5,cacheRead:1,fast:{input:20,output:100,cacheWrite:25,cacheRead:2},verifiedAt:"2026-09-07",note:"verified 2026-09-07 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input; fast tier $20/$100 recorded as the fast variant (codex reports no speed)."}]},$s=/^\d{4}-\d{2}-\d{2}$/;_l=xs(ks),vl=Object.freeze({staleDays:90,leadDays:14,confirmDays:30})});var Er=k(()=>{xr()});import Or from"node:fs/promises";import Ir from"node:path";import Es from"node:os";function Pr(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function Dr(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function Cr(e){let t=new Date(Pr(e));return t.setDate(t.getDate()+7),t.getTime()}function Lr(e){let t=new Date(Dr(e));return t.setMonth(t.getMonth()+1),t.getTime()}function Ms(e=Date.now()){for(let[t,n]of Object.entries(Ns)){let o=n.startOf(e);if(o!==yt[t]){yt[t]=o;for(let i of rn.values())i[t]=0;for(let i of Rr)i.includes(`:${t}:`)&&!i.endsWith(`:${o}`)&&Rr.delete(i)}}}function Ps(e){let t={};for(let[n,o]of Object.entries(e||{})){if(typeof n!="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(n))throw new Error(`bad provider key "${n}"`);let i={};for(let c of["weeklyUsd","monthlyUsd"]){let d=o?.[c];if(d==null||d==="")continue;let m=Number(d);if(!Number.isFinite(m)||m<=0)throw new Error(`${n}.${c} must be a positive number or null`);i[c]=m}Object.keys(i).length&&(t[n]=i)}return t}async function Br(e,{source:t="local",updatedAt:n=Date.now()}={}){Oe={caps:Ps(e),updatedAt:n,source:t};try{await Or.mkdir(Mr,{recursive:!0}),await Or.writeFile(Os(),JSON.stringify(Oe,null,2))}catch{}return Ds()}function Ds(e=Date.now()){Ms(e);let t=new Set([...rn.keys(),...Object.keys(Oe?.caps||{})]),n={};for(let o of t){let i=rn.get(o);n[o]={weeklyUsd:Nr(i?.weekly||0),monthlyUsd:Nr(i?.monthly||0)}}return{caps:Oe?.caps||{},configured:!!Oe,seeded:Is,updatedAt:Oe?.updatedAt??null,spent:n,windows:{weekly:{start:yt.weekly,resetsAt:Cr(e)},monthly:{start:yt.monthly,resetsAt:Lr(e)}},warnPct:Rs}}var Mr,Os,Rs,Ns,Oe,Is,yt,rn,Rr,Nr,jr=k(()=>{ht();Er();Mr=process.env.BOSTRAT_STATE_DIR||Ir.join(process.env.HOME||Es.tmpdir(),".bostrat"),Os=()=>Ir.join(Mr,"usage-caps.json"),Rs=.8;Ns={weekly:{startOf:Pr,nextStart:Cr,capKey:"weeklyUsd"},monthly:{startOf:Dr,nextStart:Lr,capKey:"monthlyUsd"}},Oe=null,Is=!1,yt={weekly:0,monthly:0},rn=new Map,Rr=new Set;Nr=e=>Math.round(e*1e4)/1e4});var Yr={};Ft(Yr,{REMOVED_MARKER:()=>un,detectInstallKind:()=>Ie,detectProviderAuth:()=>Gr,maskToken:()=>ln,readRemovedMarker:()=>zr,startPlaneLink:()=>Zs,updateCommandFor:()=>Je,versionLt:()=>Ne});import Re from"node:fs";import Xe from"node:fs/promises";import Cs from"node:http";import U from"node:path";import gt from"node:os";import{createRequire as Ls}from"node:module";import on from"ws";function ln(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function zr(e=un){try{let t=JSON.parse(Re.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function Ie({env:e=process.env,pkgDir:t=U.resolve(U.dirname(new URL(import.meta.url).pathname),".."),exists:n=Re.existsSync}={}){let o=String(e.BOSTRAT_INSTALL_KIND||"").trim();return["container","npm-global","git-checkout","dev"].includes(o)?o:/\/lib\/node_modules\/bostrat-node(?:\/|$)/.test(t.replace(/\\/g,"/"))?"npm-global":n(U.join(t,"..",".git"))?"git-checkout":"dev"}function Ne(e,t){let n=String(e).split(".").map(Number),o=String(t).split(".").map(Number);if(n.some(Number.isNaN)||o.some(Number.isNaN))return!1;for(let i=0;i<Math.max(n.length,o.length);i++){let c=(n[i]||0)-(o[i]||0);if(c)return c<0}return!1}function Je(e){switch(e){case"npm-global":return"npm install -g bostrat-node && restart the bostrat-node service";case"git-checkout":return"scripts/deploy-pi.sh (or The Bakery \u2192 Deploy to Pi \u2192 bostrat)";case"container":return"Links \u2192 Fly \u2192 Update (or the plane's auto-update while the node sleeps)";default:return"update this node's bostrat-node checkout/install"}}function Gr({home:e=process.env.HOME||gt.homedir(),env:t=process.env}={}){let n=o=>{try{return Re.statSync(o),!0}catch{return!1}};return{claude:n(U.join(e,".claude",".credentials.json")),codex:n(U.join(t.CODEX_HOME||U.join(e,".codex"),"auth.json")),cursor:n(U.join(t.XDG_CONFIG_HOME||U.join(e,".config"),"cursor","auth.json"))}}function Zs({url:e,token:t,nodeId:n=gt.hostname(),name:o=gt.hostname(),listTracks:i,detectAgents:c=null,onTracksTransferred:d=null,onTracksMoves:m=null,sampleMetrics:h=null,deliverySample:x=null,onHashKey:f=null,onViewBase:T=null,metricsMs:$=Js,onUpdateAvailable:_=null,onUpdatePolicy:v=null,onHelloRefused:M=null,updatePolicy:O=null,installKind:E=Ie(),selfUrl:b=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:S=null,removedMarkerFile:A=un,onRemoved:P=null,onDegraded:R=null,onRecovered:K=null,onRefused:kt=null,onConnected:co=null,ingestMode:$t=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:_n=zs,unackedRetryMs:Qe=Ws,pingMs:vn=Hs,deadLinkMs:xt=Math.round(vn*Ks),maxInflightEvents:lo=qs,httpTimeoutMs:et=Xs,stallBackoffMinMs:bn=Ys,fetchImpl:An=globalThis.fetch,log:w=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof i!="function")throw new Error("plane-link: listTracks is required");e=He(e);let uo=b.replace(/^http/,"ws"),kn=new Set,$n=s=>{let l=typeof s.latestNodeVersion=="string"?s.latestNodeVersion:null;if(!(!(l&&Z&&Ne(Z,l))||kn.has(l))){kn.add(l),w.log(`[plane-link] bostrat-node ${l} is available (this node runs ${Z}) \u2014 ${Je(E)}`);try{_?.({current:Z,latest:l,min:s.minNodeVersion??null,installKind:E,required:!!(s.minNodeVersion&&Ne(Z,s.minNodeVersion))})}catch(a){w.error(`[plane-link] update notice failed: ${a?.message||a}`)}}},pe=new Map,he=new Map,Et=U.join(cn,"plane-link","cursor.json"),Ot=U.join(cn,"plane-link","dead-letter.ndjson"),xn=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),ne={},ee={},N=new Map,tt=null,ae=null,re=0,De=0,En=0,nt={},On=new Set,Rn=Promise.resolve(),C="ws",be=0,J=!1,me=0,Rt=!1,V=ue,Ae=new Set,ce=[],z=null,Y=null,q=!1,Nt=!1,Ce=Fr,Le=null,ke=null,$e=null,oe=null,we=0,Nn=0,se=null,It=!1,rt=0,Be=!1,le=zr(A);if(le)if(le.tokenPreview===ln(t))Be=!0,Yt({reason:le.reason??"removed",at:le.at??null,nodeId:le.nodeId??n}),w.error(`[plane-link] this node was REMOVED from the plane (${le.reason??"removed"}${le.at?` at ${new Date(le.at).toISOString()}`:""}) \u2014 not dialing. Re-provision it with a new node token, or delete ${A} to force a dial.`);else try{Re.unlinkSync(A),w.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function Mt({reason:s="removed",at:l=Date.now(),via:a="ctl"}={}){if(!Be){Be=!0,Yt({reason:s,at:l,nodeId:n,via:a});try{Re.mkdirSync(U.dirname(A),{recursive:!0}),Re.writeFileSync(A,JSON.stringify({at:l,reason:s,via:a,nodeId:n,tokenPreview:ln(t)},null,2))}catch(r){w.error(`[plane-link] could not write ${A}: ${r?.message||r}`)}w.error(`[plane-link] the plane REMOVED this node (${s}, via ${a}) \u2014 going dormant: no more dials, ingest or broker calls. Re-provision with a new node token to reconnect; the box and its data are untouched.`);try{P?.({reason:s,at:l,via:a})}catch{}Hn({bye:!1})}}async function fo(){try{ne=JSON.parse(await Xe.readFile(Et,"utf8"))}catch{ne={}}}function In(){It=!0,!$e&&($e=setTimeout(async()=>{if($e=null,!!It){It=!1;try{await Xe.mkdir(U.dirname(Et),{recursive:!0}),await Xe.writeFile(Et,JSON.stringify(ne))}catch(s){w.error("[plane-link] cursor flush failed:",s?.message||s)}}},Us),$e.unref?.())}function L(s){return!Y||Y.readyState!==on.OPEN?!1:(Y.send(JSON.stringify(s)),!0)}function je(s){let l=Date.now();for(let{trackId:r,event:u}of s){let p=N.get(u.id);N.set(u.id,{trackId:r,seq:u.seq,event:u,sentAt:l,firstSentAt:p?.firstSentAt??l,retryAt:l+Qe,tries:(p?.tries??0)+1,nacks:p?.nacks??0})}let a=s.map(({trackId:r,event:u})=>({trackId:r,event:Vs(u)}));return C==="http"?(go(a),!0):L({v:B,ch:"events",type:"append",events:a})}function po(s){let l=null;for(let a of N.values())a.trackId===s&&(l==null||a.seq<l)&&(l=a.seq);return l}function Pt(s){let l=ee[s];if(l==null)return;let a=po(s),r=a==null?l:Math.min(l,a-1);r>(ne[s]??0)&&(ne[s]=r,In())}function Fe(){if(tt=Date.now(),ae!=null){let s=Date.now()-ae;w.log(`[plane-link] spine sync recovered after ${Math.round(s/1e3)}s degraded (${re} stall redial(s))`);let l=ae;ae=null,re=0,De=0,J=!1,me=0;try{K?.({since:l,downMs:s})}catch(a){w.error(`[plane-link] onRecovered failed: ${a?.message||a}`)}}}function Dt(s){if(!Array.isArray(s)||!s.length)return;let l=new Set;for(let a of s){let r=N.get(a);r&&(N.delete(a),(ee[r.trackId]??0)<r.seq&&(ee[r.trackId]=r.seq),l.add(r.trackId))}for(let a of l)Pt(a);Fe()}function Mn(s,l){En++;let a={at:Date.now(),trackId:s.trackId,seq:s.seq,id:s.event?.id??null,type:s.event?.type??null,reason:l,event:s.event??null};w.error(`[plane-link] DEAD-LETTER ${s.trackId} seq ${s.seq} (${a.type??"?"}): plane rejected it permanently (${l}) \u2014 kept in ${Ot}`),Rn=Rn.then(async()=>{await Xe.mkdir(U.dirname(Ot),{recursive:!0}),await Xe.appendFile(Ot,JSON.stringify(a)+`
123
+ `),h(0)}var Tr,vs,nn,wt,kr=k(()=>{Tr="wss://plane.bostrat.ai/v1/node",vs=Ss(import.meta.url)("../package.json").version,nn=`ghcr.io/globocodes/bostrat-node:${vs}`;wt=(e,{stdin:t,exec:n}={})=>new Promise((o,i)=>{if(n)return n(e,{stdin:t}).then(o,i);let c=Ts("fly",e,{timeout:6e5,maxBuffer:16*1024*1024},(d,m,h)=>d?i(Object.assign(d,{stdout:m,stderr:h})):o({stdout:m,stderr:h}));t!=null&&c.stdin?.end(t)})});function $r(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!$s.test(String(e)))throw new Error(`pricing.js: ${n} window ${t} must be YYYY-MM-DD, got ${JSON.stringify(e)}`);let o=Date.parse(`${e}T${t==="from"?"00:00:00.000":"23:59:59.999"}Z`);if(!Number.isFinite(o))throw new Error(`pricing.js: ${n} window ${t} is not a real date: ${e}`);return o}function xs(e){let t={};for(let[n,o]of Object.entries(e)){if(!Array.isArray(o)||o.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);t[n]=o.map(i=>({...i,fromMs:$r(i.from,"from",n),toMs:$r(i.to,"to",n)}))}return t}var ks,$s,vl,bl,xr=k(()=>{ks={"claude-opus-5-5":[{from:null,to:null,input:4,output:20,cacheWrite:8,cacheRead:.2,cacheReadX:.05,fast:{input:8,output:40,cacheWrite:16,cacheRead:.4},verifiedAt:"2026-09-28",note:"verified 2026-09-28 against platform.claude.com/docs/en/about-claude/pricing: $4/$20, 1h cache write $8 (2\xD7), cache hits $0.20 (0.05\xD7 \u2014 footnote 2), fast $8/$40; added before any fleet turn was recorded."}],"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5-1":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:.25,cacheReadX:.025,verifiedAt:"2026-09-07",note:"verified 2026-09-07 against platform.claude.com/docs/en/about-claude/pricing: $10/$50, 1h cache write $20 (2\xD7), cache hits $0.25 (0.025\xD7 \u2014 the 5.1-only multiplier; footnote 1 on the page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:"2026-08-23",input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL). Superseded 2026-08-24 by the vendor's promotional cut (window below)."},{from:"2026-08-24",to:null,input:4,output:20,cacheWrite:5,cacheRead:.4,verifiedAt:"2026-08-24",note:"promotional pricing verified 2026-08-24 (developers.openai.com pricing), 'at least through November 21, 2026'; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-6-astra":[{from:null,to:null,input:10,output:50,cacheWrite:12.5,cacheRead:1,fast:{input:20,output:100,cacheWrite:25,cacheRead:2},verifiedAt:"2026-09-07",note:"verified 2026-09-07 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input; fast tier $20/$100 recorded as the fast variant (codex reports no speed)."}]},$s=/^\d{4}-\d{2}-\d{2}$/;vl=xs(ks),bl=Object.freeze({staleDays:90,leadDays:14,confirmDays:30})});var Er=k(()=>{xr()});import Or from"node:fs/promises";import Ir from"node:path";import Es from"node:os";function Pr(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function Dr(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function Cr(e){let t=new Date(Pr(e));return t.setDate(t.getDate()+7),t.getTime()}function Lr(e){let t=new Date(Dr(e));return t.setMonth(t.getMonth()+1),t.getTime()}function Ms(e=Date.now()){for(let[t,n]of Object.entries(Ns)){let o=n.startOf(e);if(o!==yt[t]){yt[t]=o;for(let i of rn.values())i[t]=0;for(let i of Rr)i.includes(`:${t}:`)&&!i.endsWith(`:${o}`)&&Rr.delete(i)}}}function Ps(e){let t={};for(let[n,o]of Object.entries(e||{})){if(typeof n!="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(n))throw new Error(`bad provider key "${n}"`);let i={};for(let c of["weeklyUsd","monthlyUsd"]){let d=o?.[c];if(d==null||d==="")continue;let m=Number(d);if(!Number.isFinite(m)||m<=0)throw new Error(`${n}.${c} must be a positive number or null`);i[c]=m}Object.keys(i).length&&(t[n]=i)}return t}async function Br(e,{source:t="local",updatedAt:n=Date.now()}={}){Oe={caps:Ps(e),updatedAt:n,source:t};try{await Or.mkdir(Mr,{recursive:!0}),await Or.writeFile(Os(),JSON.stringify(Oe,null,2))}catch{}return Ds()}function Ds(e=Date.now()){Ms(e);let t=new Set([...rn.keys(),...Object.keys(Oe?.caps||{})]),n={};for(let o of t){let i=rn.get(o);n[o]={weeklyUsd:Nr(i?.weekly||0),monthlyUsd:Nr(i?.monthly||0)}}return{caps:Oe?.caps||{},configured:!!Oe,seeded:Is,updatedAt:Oe?.updatedAt??null,spent:n,windows:{weekly:{start:yt.weekly,resetsAt:Cr(e)},monthly:{start:yt.monthly,resetsAt:Lr(e)}},warnPct:Rs}}var Mr,Os,Rs,Ns,Oe,Is,yt,rn,Rr,Nr,jr=k(()=>{ht();Er();Mr=process.env.BOSTRAT_STATE_DIR||Ir.join(process.env.HOME||Es.tmpdir(),".bostrat"),Os=()=>Ir.join(Mr,"usage-caps.json"),Rs=.8;Ns={weekly:{startOf:Pr,nextStart:Cr,capKey:"weeklyUsd"},monthly:{startOf:Dr,nextStart:Lr,capKey:"monthlyUsd"}},Oe=null,Is=!1,yt={weekly:0,monthly:0},rn=new Map,Rr=new Set;Nr=e=>Math.round(e*1e4)/1e4});var Yr={};Ft(Yr,{REMOVED_MARKER:()=>un,detectInstallKind:()=>Ie,detectProviderAuth:()=>Gr,maskToken:()=>ln,readRemovedMarker:()=>zr,startPlaneLink:()=>Zs,updateCommandFor:()=>Je,versionLt:()=>Ne});import Re from"node:fs";import Xe from"node:fs/promises";import Cs from"node:http";import U from"node:path";import gt from"node:os";import{createRequire as Ls}from"node:module";import on from"ws";function ln(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function zr(e=un){try{let t=JSON.parse(Re.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function Ie({env:e=process.env,pkgDir:t=U.resolve(U.dirname(new URL(import.meta.url).pathname),".."),exists:n=Re.existsSync}={}){let o=String(e.BOSTRAT_INSTALL_KIND||"").trim();return["container","npm-global","git-checkout","dev"].includes(o)?o:/\/lib\/node_modules\/bostrat-node(?:\/|$)/.test(t.replace(/\\/g,"/"))?"npm-global":n(U.join(t,"..",".git"))?"git-checkout":"dev"}function Ne(e,t){let n=String(e).split(".").map(Number),o=String(t).split(".").map(Number);if(n.some(Number.isNaN)||o.some(Number.isNaN))return!1;for(let i=0;i<Math.max(n.length,o.length);i++){let c=(n[i]||0)-(o[i]||0);if(c)return c<0}return!1}function Je(e){switch(e){case"npm-global":return"npm install -g bostrat-node && restart the bostrat-node service";case"git-checkout":return"scripts/deploy-pi.sh (or The Bakery \u2192 Deploy to Pi \u2192 bostrat)";case"container":return"Links \u2192 Fly \u2192 Update (or the plane's auto-update while the node sleeps)";default:return"update this node's bostrat-node checkout/install"}}function Gr({home:e=process.env.HOME||gt.homedir(),env:t=process.env}={}){let n=o=>{try{return Re.statSync(o),!0}catch{return!1}};return{claude:n(U.join(e,".claude",".credentials.json")),codex:n(U.join(t.CODEX_HOME||U.join(e,".codex"),"auth.json")),cursor:n(U.join(t.XDG_CONFIG_HOME||U.join(e,".config"),"cursor","auth.json"))}}function Zs({url:e,token:t,nodeId:n=gt.hostname(),name:o=gt.hostname(),listTracks:i,detectAgents:c=null,onTracksTransferred:d=null,onTracksMoves:m=null,sampleMetrics:h=null,deliverySample:x=null,onHashKey:f=null,onViewBase:T=null,metricsMs:$=Js,onUpdateAvailable:_=null,onUpdatePolicy:v=null,onHelloRefused:M=null,updatePolicy:O=null,installKind:E=Ie(),selfUrl:b=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:S=null,removedMarkerFile:A=un,onRemoved:P=null,onDegraded:R=null,onRecovered:K=null,onRefused:kt=null,onConnected:co=null,ingestMode:$t=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:_n=zs,unackedRetryMs:Qe=Ws,pingMs:vn=Hs,deadLinkMs:xt=Math.round(vn*Ks),maxInflightEvents:lo=qs,httpTimeoutMs:et=Xs,stallBackoffMinMs:bn=Ys,fetchImpl:An=globalThis.fetch,log:w=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof i!="function")throw new Error("plane-link: listTracks is required");e=He(e);let uo=b.replace(/^http/,"ws"),kn=new Set,$n=s=>{let l=typeof s.latestNodeVersion=="string"?s.latestNodeVersion:null;if(!(!(l&&Z&&Ne(Z,l))||kn.has(l))){kn.add(l),w.log(`[plane-link] bostrat-node ${l} is available (this node runs ${Z}) \u2014 ${Je(E)}`);try{_?.({current:Z,latest:l,min:s.minNodeVersion??null,installKind:E,required:!!(s.minNodeVersion&&Ne(Z,s.minNodeVersion))})}catch(a){w.error(`[plane-link] update notice failed: ${a?.message||a}`)}}},pe=new Map,he=new Map,Et=U.join(cn,"plane-link","cursor.json"),Ot=U.join(cn,"plane-link","dead-letter.ndjson"),xn=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),ne={},ee={},N=new Map,tt=null,ae=null,re=0,De=0,En=0,nt={},On=new Set,Rn=Promise.resolve(),C="ws",be=0,J=!1,me=0,Rt=!1,V=ue,Ae=new Set,ce=[],z=null,Y=null,q=!1,Nt=!1,Ce=Fr,Le=null,ke=null,$e=null,oe=null,we=0,Nn=0,se=null,It=!1,rt=0,Be=!1,le=zr(A);if(le)if(le.tokenPreview===ln(t))Be=!0,Yt({reason:le.reason??"removed",at:le.at??null,nodeId:le.nodeId??n}),w.error(`[plane-link] this node was REMOVED from the plane (${le.reason??"removed"}${le.at?` at ${new Date(le.at).toISOString()}`:""}) \u2014 not dialing. Re-provision it with a new node token, or delete ${A} to force a dial.`);else try{Re.unlinkSync(A),w.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function Mt({reason:s="removed",at:l=Date.now(),via:a="ctl"}={}){if(!Be){Be=!0,Yt({reason:s,at:l,nodeId:n,via:a});try{Re.mkdirSync(U.dirname(A),{recursive:!0}),Re.writeFileSync(A,JSON.stringify({at:l,reason:s,via:a,nodeId:n,tokenPreview:ln(t)},null,2))}catch(r){w.error(`[plane-link] could not write ${A}: ${r?.message||r}`)}w.error(`[plane-link] the plane REMOVED this node (${s}, via ${a}) \u2014 going dormant: no more dials, ingest or broker calls. Re-provision with a new node token to reconnect; the box and its data are untouched.`);try{P?.({reason:s,at:l,via:a})}catch{}Hn({bye:!1})}}async function fo(){try{ne=JSON.parse(await Xe.readFile(Et,"utf8"))}catch{ne={}}}function In(){It=!0,!$e&&($e=setTimeout(async()=>{if($e=null,!!It){It=!1;try{await Xe.mkdir(U.dirname(Et),{recursive:!0}),await Xe.writeFile(Et,JSON.stringify(ne))}catch(s){w.error("[plane-link] cursor flush failed:",s?.message||s)}}},Us),$e.unref?.())}function L(s){return!Y||Y.readyState!==on.OPEN?!1:(Y.send(JSON.stringify(s)),!0)}function je(s){let l=Date.now();for(let{trackId:r,event:u}of s){let p=N.get(u.id);N.set(u.id,{trackId:r,seq:u.seq,event:u,sentAt:l,firstSentAt:p?.firstSentAt??l,retryAt:l+Qe,tries:(p?.tries??0)+1,nacks:p?.nacks??0})}let a=s.map(({trackId:r,event:u})=>({trackId:r,event:Vs(u)}));return C==="http"?(go(a),!0):L({v:B,ch:"events",type:"append",events:a})}function po(s){let l=null;for(let a of N.values())a.trackId===s&&(l==null||a.seq<l)&&(l=a.seq);return l}function Pt(s){let l=ee[s];if(l==null)return;let a=po(s),r=a==null?l:Math.min(l,a-1);r>(ne[s]??0)&&(ne[s]=r,In())}function Fe(){if(tt=Date.now(),ae!=null){let s=Date.now()-ae;w.log(`[plane-link] spine sync recovered after ${Math.round(s/1e3)}s degraded (${re} stall redial(s))`);let l=ae;ae=null,re=0,De=0,J=!1,me=0;try{K?.({since:l,downMs:s})}catch(a){w.error(`[plane-link] onRecovered failed: ${a?.message||a}`)}}}function Dt(s){if(!Array.isArray(s)||!s.length)return;let l=new Set;for(let a of s){let r=N.get(a);r&&(N.delete(a),(ee[r.trackId]??0)<r.seq&&(ee[r.trackId]=r.seq),l.add(r.trackId))}for(let a of l)Pt(a);Fe()}function Mn(s,l){En++;let a={at:Date.now(),trackId:s.trackId,seq:s.seq,id:s.event?.id??null,type:s.event?.type??null,reason:l,event:s.event??null};w.error(`[plane-link] DEAD-LETTER ${s.trackId} seq ${s.seq} (${a.type??"?"}): plane rejected it permanently (${l}) \u2014 kept in ${Ot}`),Rn=Rn.then(async()=>{await Xe.mkdir(U.dirname(Ot),{recursive:!0}),await Xe.appendFile(Ot,JSON.stringify(a)+`
124
124
  `)}).catch(r=>w.error(`[plane-link] dead-letter write failed: ${r?.message||r}`))}function ho(s,l){if(nt[s]=(nt[s]??0)+1,On.has(s))return;On.add(s);let a=Gs[s]??"see the plane's tenancy alarm ring (GET /plane/tenancy/alarms)";w.error(`[plane-link] REFUSED by the plane (${s}) on track ${l}: events are being dropped, not stored \u2014 ${a}`);try{kt?.({reason:s,trackId:l,count:nt[s],hint:a})}catch(r){w.error(`[plane-link] onRefused failed: ${r?.message||r}`)}}function Ct(s,l,a,r=null){if(!Array.isArray(s)||!s.length)return;let u=new Set,p=Date.now();for(let g of s){let y=N.get(g);y&&(a?(N.delete(g),Mn(y,l),String(l).startsWith(sn)&&ho(String(l).slice(sn.length),y.trackId),(ee[y.trackId]??0)<y.seq&&(ee[y.trackId]=y.seq),u.add(y.trackId)):(y.nacks++,y.retryAt=p+Math.max(1e3,Number(r)||Qe)))}for(let g of u)Pt(g);Fe()}function Pn(s=Date.now()){let l=1/0;for(let a of N.values())a.firstSentAt<l&&(l=a.firstSentAt);return l===1/0?0:s-l}function mo(s){if(!xt||!s||s!==Y||!we)return!1;let l=Date.now()-we;if(l<=xt)return!1;Nn++,w.error(`[plane-link] no frame from the plane for ${Math.round(l/1e3)}s \u2014 link is half-open; terminating and redialing`);try{s.terminate()}catch{}return!0}function Dn(){if(!q||!N.size)return;let s=Date.now(),l=Pn(s);if(l<_n||tt!=null&&s-tt<_n||C==="http"&&J||De>s)return;re++;let a=Math.min(bn*2**(re-1),Hr);if(ae==null){ae=s-l,w.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(l/1e3)}s with ${N.size} event(s) in flight (${C} door) \u2014 the plane is not answering appends`);try{R?.({since:ae,unacked:N.size,oldestAgeMs:l,mode:C})}catch(r){w.error(`[plane-link] onDegraded failed: ${r?.message||r}`)}}if(C==="http"){J=!0,me=s+a,w.error(`[plane-link] ingest circuit OPEN (stall #${re}); probing /v1/ingest/health in ${Math.round(a/1e3)}s`);return}De=s+a,w.error(`[plane-link] closing the link and redialing in ${Math.round(a/1e3)}s (stall redial #${re})`);try{Y?.terminate()}catch{}}function Lt(){if(!q)return;let s=Date.now();if(C==="http"&&J)return;let l=[...N.values()].filter(r=>r.event&&r.retryAt<=s&&!(C==="http"&&Ae.has(r.trackId)));if(!l.length)return;l.sort((r,u)=>r.trackId===u.trackId?r.seq-u.seq:r.trackId<u.trackId?-1:1),w.log(`[plane-link] re-sending ${l.length} unacked event(s) the plane never confirmed`);let a=C==="http"?V:ue;if(C==="http"){let r=new Map;for(let u of l)(r.get(u.trackId)??r.set(u.trackId,[]).get(u.trackId)).push(u);for(let[,u]of r){if(be>=an)return;je(u.slice(0,a).map(p=>({trackId:p.trackId,event:p.event})))}return}for(let r=0;r<l.length;r+=a){let u=l.slice(r,r+a).map(p=>({trackId:p.trackId,event:p.event}));if(!je(u))return}}async function wo(s=null){for(;;){if(!q)return!1;let l=N.size<lo,a=C!=="http"||be<an&&!J&&(!s||!Ae.has(s));if(l&&a)return!0;await new Promise(r=>setTimeout(r,100))}}function Bt(){if(z=null,!q||!ce.length)return;let s=ce;if(ce=[],C!=="http"){for(let a=0;a<s.length;a+=ue)je(s.slice(a,a+ue));return}let l=new Map;for(let a of s)(l.get(a.trackId)??l.set(a.trackId,[]).get(a.trackId)).push(a);for(let[a,r]of l){if(Ae.has(a)||be>=an||J){ce.push(...r);continue}for(let u=0;u<r.length;u+=V)je(r.slice(u,u+V))}ce.length&&!z&&(z=setTimeout(Bt,Kr),z.unref?.())}let yo=s=>{let l=new Map;for(let{trackId:a,event:r}of s)(l.get(a)??l.set(a,[]).get(a)).push(r);return[...l].map(([a,r])=>({trackId:a,events:r}))},ye=(s,l,a,{progress:r})=>{let u=Date.now();for(let{event:p}of s){let g=N.get(p.id);g&&(g.nacks++,g.retryAt=u+Math.max(1e3,a||Qe))}r&&Fe()};async function go(s){let l=new Set(s.map(a=>a.trackId));for(let a of l)Ae.add(a);be++;try{let a=await An(`${xn}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:yo(s)}),signal:AbortSignal.timeout(et)}),r=(Number(a.headers.get("retry-after"))||0)*1e3;if(a.status===200){let u=await a.json();V=ue;for(let y of u.results??[]){Dt(y.acked??[]);let D=(te,W)=>{let ot=new Map;for(let ge of te??[])(ot.get(ge.reason)??ot.set(ge.reason,[]).get(ge.reason)).push(ge.id);for(let[ge,Kn]of ot)Ct(Kn,ge,W,u.retryAfterMs??null)};D(y.rejected,!0),D(y.deferred,!1)}let p=new Set,g=new Map;for(let y of Array.isArray(u.refused)?u.refused:[])!y||typeof y.id!="string"||(p.add(y.id),(g.get(y.reason)??g.set(y.reason,[]).get(y.reason)).push(y.id));for(let[y,D]of g)Ct(D,`${sn}${y}`,!0);Dt((u.discarded??[]).filter(y=>!p.has(y))),Fe();return}if(a.status===429){ye(s,"overloaded",r||5e3,{progress:!0});return}if(a.status===413&&s.length>1){V=Math.max(1,Math.floor(s.length/2)),w.error(`[plane-link] ingest request too large (${s.length} events) \u2014 halving batches to ${V}`),ye(s,"too_large",1e3,{progress:!0});return}if(a.status===413){let u=N.get(s[0].event.id);u&&(N.delete(u.event.id),Mn(u,"too_large"),(ee[u.trackId]??0)<u.seq&&(ee[u.trackId]=u.seq),Pt(u.trackId)),Fe();return}if(a.status===410){Mt({reason:"removed",via:"410"}),ye(s,"http_410",6e4,{progress:!1});return}if(a.status===401||a.status===403){let{tripped:u,holdMs:p}=dt();if(u&&w.error(`[plane-link] holding every plane call for ${Math.round(p/6e4)} min \u2014 the ingest door refused this node's token three times in a row`),Date.now()-rt>=Wr){rt=Date.now();let g="";try{g=(await a.json())?.error??""}catch{}w.error(`[plane-link] plane refused ingest (HTTP ${a.status}${g?`: ${g}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}ye(s,`http_${a.status}`,Math.max(6e4,Ge()),{progress:!1});return}if(a.status===400){w.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${s.length} event(s) for retry; this is a bug, not data`),ye(s,"http_400",6e4,{progress:!1});return}ye(s,`http_${a.status}`,r||5e3,{progress:!1})}catch(a){let r=a?.name==="TimeoutError"||a?.name==="AbortError";r&&s.length>1&&(V=Math.max(1,Math.floor(s.length/2)),w.error(`[plane-link] ingest request of ${s.length} event(s) outlived its ${et} ms deadline \u2014 halving batches to ${V}`)),ye(s,r?"timeout":"network",5e3,{progress:!1})}finally{be--;for(let a of l)Ae.delete(a)}}async function To(){if(C!=="http"||!J||!q||Rt)return;let s=Date.now();if(!(me>s||ft(s))){Rt=!0;try{let l=await An(`${xn}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(et)});if(!l.ok)throw new Error(`HTTP ${l.status}`);w.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),J=!1,me=0,De=Date.now()+et+Qe+1e3;for(let a of N.values())a.retryAt=Math.min(a.retryAt,Date.now());Lt()}catch(l){re++;let a=Math.min(bn*2**(re-1),Hr);me=Date.now()+a,w.error(`[plane-link] ingest door still not answering (${l?.message||l}); next probe in ${Math.round(a/1e3)}s`)}finally{Rt=!1}}}function Cn(){let s=Date.now();return{mode:C,unacked:N.size,oldestUnackedAgeMs:Pn(s),lastProgressAt:tt,degradedSince:ae,stallRedials:re,deadLettered:En,http:C==="http"?{inFlight:be,circuitOpen:J,nextProbeInMs:J?Math.max(0,me-s):0,batchCap:V}:null}}async function Ln(){if(!h)return;let s;try{s=await h()}catch(a){w.error("[plane-link] metrics sample failed:",a?.message||a);return}let l=null;try{l=x?.()??null}catch{}s&&L({v:B,ch:"ctl",type:"metrics",metrics:{...s,planeLink:Cn(),...l?{delivery:l}:{}}})}function So(){h&&(se&&clearInterval(se),Ln(),se=setInterval(Ln,$),se.unref?.())}async function _o(){let s;try{s=await i()}catch(l){w.error("[plane-link] backfill listTracks failed:",l?.message||l);return}L({v:B,ch:"ctl",type:"tracks",ids:s.filter(l=>!l.transferredTo).map(l=>l.id).filter(l=>typeof l=="string")});for(let l of s)if(!q||!await Bn(l))return}async function Bn(s){let l;try{l=await pt(s)}catch{return!0}let a=ne[s.id]??0,r=l.filter(p=>p.seq>a&&typeof p.id=="string"),u=C==="http"?V:ue;for(let p=0;p<r.length;p+=u){if(!await wo(s.id))return!1;let g=r.slice(p,p+Math.min(u,C==="http"?V:ue)).map(y=>({trackId:s.id,event:y}));if(!je(g))return!1}return!0}let jt=new Map;async function jn(s,l){if(jt.get(s)===l)return!1;jt.set(s,l);let a=null;try{a=(await i()).find(g=>g?.id===s)??null}catch{return!1}if(!a)return!1;let r;try{r=await pt(a)}catch{return!1}let u=[...r].reverse().find(g=>(g?.type==="track.transferred"||g?.type==="track.recovered")&&g.payload?.toNode===n);if(!u||!Number.isFinite(u.seq))return!1;let p=u.seq-1;return(ne[s]??0)>p&&(ne[s]=p,In()),(ee[s]??0)>p&&(ee[s]=p),w.log(`[plane-link] re-sending ${s}'s move-commit (seq ${u.seq}, ${u.type}) \u2014 the plane's move record predates this node's adoption`),q?Bn(a):!1}function Fn(s){!q||!s?.trackId||typeof s.id!="string"||(ce.push({trackId:s.trackId,event:s}),z||(z=setTimeout(Bt,Kr),z.unref?.()))}let Ue=new Map;async function vo(s){let{reqId:l,method:a,path:r,headers:u={},body:p}=s,g=Object.fromEntries(Object.entries(u).filter(([,D])=>D!=null)),y=new AbortController;Ue.set(l,y);try{if(s.host){await bo(s,g,y);return}S&&(g["x-bostrat-token"]=S);let D=await fetch(b+r,{method:a,headers:g,body:p?Buffer.from(p,"base64"):void 0,signal:y.signal}),te=Object.fromEntries(D.headers);for(let W of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete te[W];if(te["content-type"]||(te["content-type"]="application/octet-stream"),L({v:B,ch:"control",type:"http_head",reqId:l,status:D.status,headers:te}),D.body){for await(let W of D.body)if(!L({v:B,ch:"control",type:"http_data",reqId:l,data:Buffer.from(W).toString("base64")}))break}L({v:B,ch:"control",type:"http_end",reqId:l,error:null})}finally{Ue.delete(l)}}function bo(s,l,a){let{reqId:r,method:u,path:p,body:g}=s,y=new URL(b);return new Promise(D=>{let te=Cs.request({host:y.hostname,port:y.port,method:u,path:p,headers:{...l,host:s.host},signal:a.signal},W=>{let{connection:ot,"keep-alive":ge,"transfer-encoding":Kn,upgrade:zi,...Ao}=W.headers;L({v:B,ch:"control",type:"http_head",reqId:r,status:W.statusCode||502,headers:Ao}),W.on("data",ko=>{L({v:B,ch:"control",type:"http_data",reqId:r,data:Buffer.from(ko).toString("base64")})||W.destroy()}),W.on("end",()=>{L({v:B,ch:"control",type:"http_end",reqId:r,error:null}),D()}),W.on("error",()=>{L({v:B,ch:"control",type:"http_end",reqId:r,error:"view upstream error"}),D()})});te.on("error",W=>{L({v:B,ch:"control",type:"http_end",reqId:r,error:W?.message||"view request failed"}),D()}),g&&te.write(Buffer.from(g,"base64")),te.end()})}function Un(){if(Nt||Be)return;Y=new on(e,{headers:{authorization:`Bearer ${t}`}});let s=Y;we=0,s.on("ping",()=>{we=Date.now()}),s.on("pong",()=>{we=Date.now()}),Y.on("open",async()=>{let a=null;try{a=await c?.()||null}catch{a=null}L({v:B,ch:"ctl",type:"hello",nodeId:n,name:o,platform:process.platform,version:Z,installKind:E,...typeof O=="function"?{updatePolicy:O()}:O?{updatePolicy:O}:{},providers:Gr(),...a?{agents:a}:{}})}),Y.on("message",a=>{we=Date.now();let r;try{r=JSON.parse(a)}catch{return}if(r.ch==="ctl"&&r.type==="hello_ok"){jt.clear(),q=!0,Ce=Fr,Le=null,Qn(),w.log(`[plane-link] connected to plane as ${n}`);try{co?.()}catch{}if(typeof r.hashKey=="string"&&r.hashKey)try{f?.(r.hashKey)}catch{}try{T?.(typeof r.viewBase=="string"&&r.viewBase?r.viewBase:null)}catch{}r.minNodeVersion&&Z&&Ne(Z,r.minNodeVersion)&&w.error(`[plane-link] this node is bostrat-node ${Z}; the plane asks for \u2265${r.minNodeVersion} \u2014 update with: ${Je(E)}`);try{v?.(typeof r.updatePolicy=="string"?r.updatePolicy:null)}catch{}$n(r);let u=r.ingest==="http";C=$t==="ws"?"ws":$t==="http"||u?"http":"ws",$t==="http"&&!u&&w.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),C==="http"&&w.log("[plane-link] spine rides the HTTPS ingest door"),J=!1,me=0,V=ue,oe&&clearInterval(oe),we=Date.now(),oe=setInterval(()=>{mo(s)||(L({v:B,ch:"ctl",type:"ping"}),Dn(),To().catch(()=>{}),Lt(),ce.length&&!z&&Bt())},vn),oe.unref?.(),So(),_o().catch(p=>w.error("[plane-link] backfill failed:",p?.message||p));return}if(r.ch==="ctl"&&r.type==="hello_refused"){let u=typeof r.minNodeVersion=="string"?r.minNodeVersion:null,p=typeof r.latestNodeVersion=="string"?r.latestNodeVersion:null;w.error(`[plane-link] plane REFUSED this node: bostrat-node ${Z??"?"} is below its minimum ${u??"?"} \u2014 ${r.fix||Je(E)}`),Ce=Ur,Le=0;try{M?.({current:Z,min:u,latest:p,fix:r.fix||Je(E),installKind:E})}catch(g){w.error(`[plane-link] refusal handler failed: ${g?.message||g}`)}return}if(r.ch==="ctl"&&r.type==="removed"){L({v:B,ch:"ctl",type:"removed_ack",nodeId:n}),Mt({reason:typeof r.reason=="string"?r.reason:"removed",at:Number(r.at)||Date.now(),via:"ctl"});return}if(r.ch==="ctl"&&r.type==="update_available"){$n(r);return}if(r.ch==="ctl"&&r.type==="update_policy"){try{v?.(typeof r.mode=="string"?r.mode:null)}catch{}return}if(r.ch==="ctl"&&r.type==="usage_caps"){Br(r.caps??{},{source:"plane",updatedAt:r.updatedAt??Date.now()}).then(u=>w.log(`[plane-link] spend caps ${Object.keys(u.caps).length?`updated (${Object.keys(u.caps).join(", ")})`:"cleared"} from plane`)).catch(u=>w.error("[plane-link] spend-caps push rejected:",u?.message||u));return}if(r.ch==="ctl"&&r.type==="transferred"&&Array.isArray(r.tracks)){Promise.resolve().then(()=>d?.(r.tracks)).then(async u=>{for(let p of Array.isArray(u)?u:[])p?.stale&&p.id&&await jn(p.id,p.since??null)}).catch(u=>w.error("[plane-link] transfer notice failed:",u?.message||u));return}if(r.ch==="ctl"&&r.type==="moves"&&Array.isArray(r.tracks)){Promise.resolve().then(()=>m?.(r.tracks)).then(async u=>{for(let p of Array.isArray(u)?u:[])p?.stale&&p.id&&await jn(p.id,p.since??null)}).catch(u=>w.error("[plane-link] move-record reconcile failed:",u?.message||u));return}if(r.ch==="events"&&r.type==="ack"&&Array.isArray(r.ids)){Dt(r.ids);return}if(r.ch==="events"&&r.type==="nack"&&Array.isArray(r.ids)){Ct(r.ids,r.reason,r.permanent===!0,r.retryAfterMs??null);return}if(r.ch==="control"&&r.type==="http_open"){vo(r).catch(u=>L({v:B,ch:"control",type:"http_end",reqId:r.reqId,error:u?.message||"tunnel error"}));return}if(r.ch==="control"&&r.type==="http_cancel"){Ue.get(r.reqId)?.abort();return}if(r.ch==="attach"&&r.type==="open"){let u=new on(uo+r.path,Array.isArray(r.protocols)?r.protocols:[],r.host?{headers:{host:r.host}}:void 0);pe.set(r.streamId,u),he.set(r.streamId,[]),u.on("open",()=>{let g=he.get(r.streamId)||[];he.delete(r.streamId);for(let{buf:y,bin:D}of g)u.send(y,{binary:D})}),u.on("message",(g,y)=>{let D=y?g:Buffer.from(String(g));L({v:B,ch:"attach",type:"data",streamId:r.streamId,data:D.toString("base64"),...y?{bin:!0}:{}})});let p=()=>{he.delete(r.streamId),pe.delete(r.streamId)&&L({v:B,ch:"attach",type:"close",streamId:r.streamId})};u.on("close",p),u.on("error",p);return}if(r.ch==="attach"&&r.type==="data"){let u=pe.get(r.streamId);if(!u)return;let p=Buffer.from(r.data,"base64"),g=r.bin===!0,y=he.get(r.streamId);y?y.push({buf:p,bin:g}):u.send(p,{binary:g});return}if(r.ch==="attach"&&r.type==="close"){let u=pe.get(r.streamId);pe.delete(r.streamId),he.delete(r.streamId),u?.close();return}});let l=()=>{q&&w.log("[plane-link] disconnected from plane"),q=!1,oe&&(clearInterval(oe),oe=null),se&&(clearInterval(se),se=null),N.clear(),ee={},ce=[],z&&(clearTimeout(z),z=null),Ae.clear();for(let u of pe.values())u.close();pe.clear(),he.clear();for(let u of Ue.values())u.abort();if(Ue.clear(),Nt||ke)return;Le==null&&(Le=Date.now());let a=Date.now()-Le<Bs,r=Math.max(a?js+Math.random()*Fs:Ce,De-Date.now(),Ge());ke=setTimeout(()=>{ke=null,Un()},r),ke.unref?.(),a||(Ce=Math.min(Ce*2,Ur))};Y.on("close",l),Y.on("error",a=>{let r=/Unexpected server response: (\d+)/.exec(a?.message||"")?.[1];if(r==="410"){Mt({reason:"removed",via:"410"});return}if(r==="401"||r==="403"){let{tripped:u,holdMs:p}=dt();u?w.error(`[plane-link] holding every plane call for ${Math.round(p/6e4)} min \u2014 three credential refusals in a row (HTTP ${r}) mean the token is revoked or unknown, not that the plane is down; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN. Next dial in ${Math.round(p/6e4)} min.`):Date.now()-rt>=Wr&&(rt=Date.now(),w.error(`[plane-link] plane refused this node's credentials (HTTP ${r}) \u2014 the token was revoked or is unknown to the plane; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN (still retrying)`))}l()})}Ye.on("event",Fn),fo().then(Un);function Hn({bye:s=!0}={}){Nt=!0,Ye.off("event",Fn),ke&&clearTimeout(ke),$e&&clearTimeout($e),oe&&clearInterval(oe),se&&clearInterval(se),z&&clearTimeout(z),s&&L({v:B,ch:"ctl",type:"bye",reason:"shutdown"}),Y?.close()}return{stop:()=>Hn(),status:()=>({connected:q,nodeId:n,cursor:{...ne},inflight:N.size,...Cn(),auth:er(),dormant:Be,removed:qt(),deadLinkMs:xt,deadLinkRedials:Nn,refused:{...nt}}),_tick:()=>{Dn(),Lt()}}}var Z,cn,un,B,Fr,Ur,Bs,js,Fs,ue,Us,Hs,Ks,Ws,Vr,zs,sn,Gs,Vs,Ys,Hr,qs,Kr,Xs,an,Js,Wr,dn=k(()=>{ht();jr();Ht();Xt();Z=(()=>{try{return Ls(import.meta.url)("../package.json").version||null}catch{return null}})(),cn=process.env.BOSTRAT_STATE_DIR||U.join(process.env.HOME||gt.tmpdir(),".bostrat"),un=U.join(cn,"plane-link","removed.json");B=1,Fr=1e3,Ur=3e4,Bs=6e4,js=1500,Fs=500,ue=100,Us=2e3,Hs=25e3,Ks=2.5,Ws=6e4,Vr=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},zs=Vr(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),sn="refused:",Gs={forgedUserId:"this node's events carry a userId the plane's token does not own \u2014 remove BOSTRAT_OWNER from node.env and the shell environment, then restart the node (current releases never stamp one)",foreignTrack:"the Track belongs to another user's node \u2014 this node must not append to it",squattedTrack:"the Track id is already claimed by another user \u2014 a local Track collided with theirs",transferredAway:"the Track moved to another node \u2014 this copy is stale; it should be demoted, not resumed",slugHijack:"a View slug belongs to another owner \u2014 pick a different slug"},Vs=e=>{if(!e||e.userId===void 0)return e;let{userId:t,...n}=e;return n},Ys=3e4,Hr=10*6e4,qs=400,Kr=250,Xs=Vr(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),an=4,Js=6e4,Wr=6e4});var qr=k(()=>{Gt()});var no={};Ft(no,{ACTIONABLE_KINDS:()=>bt,INSTALL_ATTEMPTS:()=>Tt,INSTALL_RETRY_STEP_S:()=>Xr,REGISTRY_RETRY_ATTEMPTS:()=>hn,REGISTRY_WAIT_POLLS:()=>fn,REGISTRY_WAIT_STEP_S:()=>pn,RESTART_SIGNAL:()=>Zr,SELF_UPDATE_DIR:()=>_t,SUPERVISOR_PID_FILE:()=>Jr,createAutoUpdater:()=>pi,inFlight:()=>gn,readStatus:()=>yn,restartCommand:()=>Qr,restartStrategy:()=>wn,selfUpdatePlan:()=>eo,settleSelfUpdate:()=>fi,startSelfUpdate:()=>to,underService:()=>mn,updatePolicy:()=>ii});import Ze from"node:fs";import de from"node:fs/promises";import Qs from"node:os";import j from"node:path";import{spawn as ei,execFileSync as ti}from"node:child_process";import{createRequire as ni}from"node:module";function ii({kind:e=Ie(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:bt.has(e)?e==="git-checkout"||mn(t)?"auto":"prompt":"off"}function mn(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}function wn({env:e=process.env,platform:t=process.platform,uid:n=typeof process.getuid=="function"?process.getuid():0,serviceName:o=t==="darwin"?"com.bostrat.node":"bostrat-node.service",pidFile:i=Jr,readFile:c=m=>Ze.readFileSync(m,"utf8"),alive:d=ai}={}){let m=t==="darwin"?{mode:"launchd",uid:n,serviceName:o}:{mode:"systemd",serviceName:o};if(mn(e))return m;let h=Number(e.BOSTRAT_RUN_SUPERVISOR_PID);if(!(Number.isInteger(h)&&h>0)){try{h=Number(String(c(i)).trim())}catch{h=NaN}Number.isInteger(h)&&h>0&&d(h)||(h=NaN)}return Number.isInteger(h)&&h>0?{mode:"supervisor",pid:h}:m}function ai(e){try{process.kill(e,0)}catch{return!1}try{let t=ti("ps",["-p",String(e),"-o","command="],{encoding:"utf8",stdio:["ignore","pipe","ignore"]});return/bostrat-node|\/cli\.js|node-cli\.js/.test(t)}catch{return!1}}function Qr(e){switch(e?.mode){case"systemd":return`systemctl --user restart ${X(e.serviceName||"bostrat-node.service")}`;case"launchd":return`launchctl kickstart -k gui/${Number(e.uid)||0}/${e.serviceName||"com.bostrat.node"}`;case"supervisor":return`kill -${Zr.replace(/^SIG/,"")} ${Number(e.pid)}`;default:throw new Error(`self-update: unknown restart strategy "${e?.mode}"`)}}function eo({kind:e,to:t,from:n=St,statusFile:o=vt,logFile:i=j.join(_t,"last.log"),port:c=Number(process.env.PORT)||3362,npmPrefix:d=null,serviceName:m=process.platform==="darwin"?"com.bostrat.node":"bostrat-node.service",uid:h=typeof process.getuid=="function"?process.getuid():0,restart:x=wn({env:{},platform:process.platform,uid:h,serviceName:m,readFile:()=>{throw new Error("no pidfile")}}),liveDir:f=process.env.BOSTRAT_LIVE_CHECKOUT||j.resolve(j.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:T=Date.now(),attempts:$=1}={}){if(!bt.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let _=(b,S="")=>`write_status ${X(b)} ${S}`,v=Qr(x),M=`
125
125
  health() {
126
126
  for i in $(seq 1 45); do
@@ -268,7 +268,7 @@ WantedBy=default.target
268
268
  <key>StandardErrorPath</key><string>${i}/node.err.log</string>
269
269
  </dict>
270
270
  </plist>
271
- `}function Ki(e=process.platform,t=Pe.homedir()){return e==="darwin"?{kind:"launchd",file:I.join(t,"Library","LaunchAgents","com.bostrat.node.plist")}:{kind:"systemd",file:I.join(t,".config","systemd","user","bostrat-node.service")}}async function Wi({uninstall:e=!1,start:t=!0,platform:n=process.platform,log:o=console.log,agentDir:i=ve}={}){let{kind:c,file:d}=Ki(n);if(!e&&i.split(I.sep).includes("_npx"))return o("\u2717 running from the npx cache \u2014 a service must point at a durable install."),o(" fix: npm install -g bostrat-node && bostrat-node install-service"),1;if(e){c==="systemd"?(await fe("systemctl",["--user","disable","--now","bostrat-node.service"]).catch(()=>{}),H.rmSync(d,{force:!0}),await fe("systemctl",["--user","daemon-reload"])):(await fe("launchctl",["unload","-w",d]).catch(()=>{}),H.rmSync(d,{force:!0})),o(`removed ${d}`);return}H.mkdirSync(I.dirname(d),{recursive:!0}),c==="systemd"?(H.writeFileSync(d,Ui()),await fe("systemctl",["--user","daemon-reload"]),t&&await fe("systemctl",["--user","enable","--now","bostrat-node.service"]),o(`installed ${d}${t?" (enabled + started)":""}`),o("logs: journalctl --user -u bostrat-node.service -f")):(H.mkdirSync(I.join(Pe.homedir(),"Library","Logs","bostrat"),{recursive:!0}),H.writeFileSync(d,Hi()),t&&await fe("launchctl",["load","-w",d]),o(`installed ${d}${t?" (loaded)":""}`),o("logs: ~/Library/Logs/bostrat/node.{out,err}.log"))}async function Tu(e=process.argv.slice(2)){let t=e[0],n=xe();if(t==="--version"||t==="-v"||t==="version")return console.log(Me),0;if(t==="init")return(await Ci({envFile:n})).ok?0:1;if(t==="doctor"){let o={...at(n),...process.env};return ao(await io(o,{envFile:n}))?0:1}if(t==="run"||t==="start")return Fi({envFile:n}),null;if(t==="install-service")return await Wi({uninstall:e.includes("--uninstall"),start:!e.includes("--no-start")})||0;if(t==="fly"){let{flyWizard:o}=await Promise.resolve().then(()=>(kr(),Ar));return await o({argv:e.slice(1)})}if(t==="self-update"){let{startSelfUpdate:o,readStatus:i,updatePolicy:c}=await Promise.resolve().then(()=>(ro(),no)),{detectInstallKind:d}=await Promise.resolve().then(()=>(dn(),Yr)),m=d(),h=e.indexOf("--to"),x=h>=0?String(e[h+1]||"").replace(/^v/,""):null;if(e.includes("--status"))return console.log(JSON.stringify({current:Me,installKind:m,policy:c({kind:m}),status:await i()},null,2)),0;if(!x)return console.error("bostrat-node self-update --to X.Y.Z (the release to move onto; the Nodes page / chat notice names the current one)"),1;let f=await o({to:x,kind:m,reason:"cli",force:!0});return f.ok?(console.log(`\u2713 self-update ${f.from??"?"} \u2192 ${f.to} started (${f.kind}) \u2014 status: ${f.statusFile}; the node restarts when the install completes`),0):(console.error(`\u2717 not started: ${f.reason}`),1)}return console.log(`bostrat-node \u2014 install and run a bostrat node (plane P1)
271
+ `}function Ki(e=process.platform,t=Pe.homedir()){return e==="darwin"?{kind:"launchd",file:I.join(t,"Library","LaunchAgents","com.bostrat.node.plist")}:{kind:"systemd",file:I.join(t,".config","systemd","user","bostrat-node.service")}}async function Wi({uninstall:e=!1,start:t=!0,platform:n=process.platform,log:o=console.log,agentDir:i=ve}={}){let{kind:c,file:d}=Ki(n);if(!e&&i.split(I.sep).includes("_npx"))return o("\u2717 running from the npx cache \u2014 a service must point at a durable install."),o(" fix: npm install -g bostrat-node && bostrat-node install-service"),1;if(e){c==="systemd"?(await fe("systemctl",["--user","disable","--now","bostrat-node.service"]).catch(()=>{}),H.rmSync(d,{force:!0}),await fe("systemctl",["--user","daemon-reload"])):(await fe("launchctl",["unload","-w",d]).catch(()=>{}),H.rmSync(d,{force:!0})),o(`removed ${d}`);return}H.mkdirSync(I.dirname(d),{recursive:!0}),c==="systemd"?(H.writeFileSync(d,Ui()),await fe("systemctl",["--user","daemon-reload"]),t&&await fe("systemctl",["--user","enable","--now","bostrat-node.service"]),o(`installed ${d}${t?" (enabled + started)":""}`),o("logs: journalctl --user -u bostrat-node.service -f")):(H.mkdirSync(I.join(Pe.homedir(),"Library","Logs","bostrat"),{recursive:!0}),H.writeFileSync(d,Hi()),t&&await fe("launchctl",["load","-w",d]),o(`installed ${d}${t?" (loaded)":""}`),o("logs: ~/Library/Logs/bostrat/node.{out,err}.log"))}async function Su(e=process.argv.slice(2)){let t=e[0],n=xe();if(t==="--version"||t==="-v"||t==="version")return console.log(Me),0;if(t==="init")return(await Ci({envFile:n})).ok?0:1;if(t==="doctor"){let o={...at(n),...process.env};return ao(await io(o,{envFile:n}))?0:1}if(t==="run"||t==="start")return Fi({envFile:n}),null;if(t==="install-service")return await Wi({uninstall:e.includes("--uninstall"),start:!e.includes("--no-start")})||0;if(t==="fly"){let{flyWizard:o}=await Promise.resolve().then(()=>(kr(),Ar));return await o({argv:e.slice(1)})}if(t==="self-update"){let{startSelfUpdate:o,readStatus:i,updatePolicy:c}=await Promise.resolve().then(()=>(ro(),no)),{detectInstallKind:d}=await Promise.resolve().then(()=>(dn(),Yr)),m=d(),h=e.indexOf("--to"),x=h>=0?String(e[h+1]||"").replace(/^v/,""):null;if(e.includes("--status"))return console.log(JSON.stringify({current:Me,installKind:m,policy:c({kind:m}),status:await i()},null,2)),0;if(!x)return console.error("bostrat-node self-update --to X.Y.Z (the release to move onto; the Nodes page / chat notice names the current one)"),1;let f=await o({to:x,kind:m,reason:"cli",force:!0});return f.ok?(console.log(`\u2713 self-update ${f.from??"?"} \u2192 ${f.to} started (${f.kind}) \u2014 status: ${f.statusFile}; the node restarts when the install completes`),0):(console.error(`\u2717 not started: ${f.reason}`),1)}return console.log(`bostrat-node \u2014 install and run a bostrat node (plane P1)
272
272
 
273
273
  usage: bostrat-node <init | doctor | run | install-service [--uninstall] [--no-start] | fly | self-update --to X.Y.Z | --version>
274
274
 
@@ -277,4 +277,4 @@ usage: bostrat-node <init | doctor | run | install-service [--uninstall] [--no-s
277
277
  run start the node in the foreground (sources node.env)
278
278
  install-service run at boot/login: systemd --user (Linux) / launchd (macOS)
279
279
  fly provision this node on YOUR OWN Fly.io org (uses your flyctl)
280
- self-update move this install onto a release (--to X.Y.Z; --status shows the last one)`),t?1:0}export{Li as RESTART_SIGNAL,Bi as ROLLBACK_GRACE_MS,At as SUPERVISOR_PID_FILE,ji as createRunSupervisor,Ci as init,Wi as installService,Hi as launchdPlist,Tu as main,ao as printDoctor,Fi as runAgent,io as runDoctor,Ki as servicePaths,Ui as systemdUnit};
280
+ self-update move this install onto a release (--to X.Y.Z; --status shows the last one)`),t?1:0}export{Li as RESTART_SIGNAL,Bi as ROLLBACK_GRACE_MS,At as SUPERVISOR_PID_FILE,ji as createRunSupervisor,Ci as init,Wi as installService,Hi as launchdPlist,Su as main,ao as printDoctor,Fi as runAgent,io as runDoctor,Ki as servicePaths,Ui as systemdUnit};