bostrat-node 1.10.5 → 1.10.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +8 -8
- package/dist/index.js +201 -198
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -87,8 +87,8 @@ bostrat never sees your Fly account: this wizard only runs your own \`fly\` CLI.
|
|
|
87
87
|
|
|
88
88
|
`);try{await ut(["version"],{exec:o})}catch{return n.write("\u2717 `fly` (flyctl) is not installed or not on PATH.\n install: https://fly.io/docs/flyctl/install/ \u2014 then re-run `bostrat-node fly`\n"),g(1)}try{let{stdout:b}=await ut(["auth","whoami"],{exec:o});n.write(`\u2713 signed in to Fly as ${b.trim()}
|
|
89
89
|
`)}catch{return n.write("\u2717 not signed in to Fly. Run `fly auth login` (or `fly auth signup`) and re-run `bostrat-node fly`.\n"),g(1)}let T=await Vo({ask:f,output:n,exec:o,flag:c.org}),p=c.app||await f("App name",`bostrat-node-${Wo().slice(0,4)}`),h=c.region||await f("Region (see `fly platform regions`)","iad"),D=c.sizeGb||Number(await f("Volume size GB (logins + repos live here)","10"))||10,A=c.name||p,k=c.planeUrl||qn,_=c.token||await f("Node token (minted in the bostrat setup chat \u2014 shown once)");if(!_)return n.write(`\u2717 a node token is required \u2014 mint one in the bostrat setup chat (Connect a node \u2192 Fly), then re-run with it.
|
|
90
|
-
`),g(1);let I=c.owner??await f("Owner userId (shown next to the token; empty = infer from token)",""),
|
|
91
|
-
wrote ${ye.join(K,"fly.toml")} \u2014 deploys the prebuilt node image ${
|
|
90
|
+
`),g(1);let I=c.owner??await f("Owner userId (shown next to the token; empty = infer from token)",""),j=c.image||Ut,{dir:K}=Xn({app:p,dest:s,toml:Yn({app:p,region:h,nodeName:A,planeUrl:k,image:j})});n.write(`
|
|
91
|
+
wrote ${ye.join(K,"fly.toml")} \u2014 deploys the prebuilt node image ${j}
|
|
92
92
|
(claude + cursor-agent + codex baked in, logged out; nothing builds on your machine)
|
|
93
93
|
|
|
94
94
|
Plan (your flyctl, your org, your bill):
|
|
@@ -112,8 +112,8 @@ Run it? (Y/n)`,"Y")).toLowerCase().startsWith("n"))return n.write(`aborted \u201
|
|
|
112
112
|
redeploy: fly deploy ${K} -c ${ye.join(K,"fly.toml")} -a ${p} --ha=false
|
|
113
113
|
Vendor logins (Claude/Codex) are NOT set up here \u2014 you type those later, inside
|
|
114
114
|
boxes, and they land on this machine's volume, never on bostrat (box-auth-policy).
|
|
115
|
-
`),g(0)}var qn,zo,Ut,ut,Qn=v(()=>{qn="wss://plane.bostrat.ai/v1/node",zo=Ko(import.meta.url)("../package.json").version,Ut=`ghcr.io/globocodes/bostrat-node:${zo}`;ut=(e,{stdin:t,exec:n}={})=>new Promise((o,s)=>{if(n)return n(e,{stdin:t}).then(o,s);let c=Ho("fly",e,{timeout:6e5,maxBuffer:16*1024*1024},(u,f,g)=>u?s(Object.assign(u,{stdout:f,stderr:g})):o({stdout:f,stderr:g}));t!=null&&c.stdin?.end(t)})});function er(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!Go.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 Jo(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(s=>({...s,fromMs:er(s.from,"from",n),toMs:er(s.to,"to",n)}))}return t}var Yo,Go,bc,Ac,tr=v(()=>{Yo={"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)."}]},Go=/^\d{4}-\d{2}-\d{2}$/;bc=Jo(Yo),Ac=Object.freeze({staleDays:90,leadDays:14,confirmDays:30})});var nr=v(()=>{tr()});import rr from"node:fs/promises";import ir from"node:path";import Xo from"node:os";function cr(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function lr(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function ur(e){let t=new Date(cr(e));return t.setDate(t.getDate()+7),t.getTime()}function dr(e){let t=new Date(lr(e));return t.setMonth(t.getMonth()+1),t.getTime()}function ns(e=Date.now()){for(let[t,n]of Object.entries(es)){let o=n.startOf(e);if(o!==dt[t]){dt[t]=o;for(let s of Ht.values())s[t]=0;for(let s of or)s.includes(`:${t}:`)&&!s.endsWith(`:${o}`)&&or.delete(s)}}}function rs(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 s={};for(let c of["weeklyUsd","monthlyUsd"]){let u=o?.[c];if(u==null||u==="")continue;let f=Number(u);if(!Number.isFinite(f)||f<=0)throw new Error(`${n}.${c} must be a positive number or null`);s[c]=f}Object.keys(s).length&&(t[n]=s)}return t}async function pr(e,{source:t="local",updatedAt:n=Date.now()}={}){ke={caps:rs(e),updatedAt:n,source:t};try{await rr.mkdir(ar,{recursive:!0}),await rr.writeFile(Zo(),JSON.stringify(ke,null,2))}catch{}return os()}function os(e=Date.now()){ns(e);let t=new Set([...Ht.keys(),...Object.keys(ke?.caps||{})]),n={};for(let o of t){let s=Ht.get(o);n[o]={weeklyUsd:sr(s?.weekly||0),monthlyUsd:sr(s?.monthly||0)}}return{caps:ke?.caps||{},configured:!!ke,seeded:ts,updatedAt:ke?.updatedAt??null,spent:n,windows:{weekly:{start:dt.weekly,resetsAt:ur(e)},monthly:{start:dt.monthly,resetsAt:dr(e)}},warnPct:Qo}}var ar,Zo,Qo,es,ke,ts,dt,Ht,or,sr,fr=v(()=>{at();nr();ar=process.env.BOSTRAT_STATE_DIR||ir.join(process.env.HOME||Xo.tmpdir(),".bostrat"),Zo=()=>ir.join(ar,"usage-caps.json"),Qo=.8;es={weekly:{startOf:cr,nextStart:ur,capKey:"weeklyUsd"},monthly:{startOf:lr,nextStart:dr,capKey:"monthlyUsd"}},ke=null,ts=!1,dt={weekly:0,monthly:0},Ht=new Map,or=new Set;sr=e=>Math.round(e*1e4)/1e4});var vr={};$t(vr,{REMOVED_MARKER:()=>qt,detectInstallKind:()=>Ee,detectProviderAuth:()=>_r,maskToken:()=>Vt,readRemovedMarker:()=>Tr,startPlaneLink:()=>gs,updateCommandFor:()=>qe,versionLt:()=>xe});import $e from"node:fs";import Ve from"node:fs/promises";import ss from"node:http";import F from"node:path";import pt from"node:os";import{createRequire as is}from"node:module";import Kt from"ws";function Vt(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function Tr(e=qt){try{let t=JSON.parse($e.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function Ee({env:e=process.env,pkgDir:t=F.resolve(F.dirname(new URL(import.meta.url).pathname),".."),exists:n=$e.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(F.join(t,"..",".git"))?"git-checkout":"dev"}function xe(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 s=0;s<Math.max(n.length,o.length);s++){let c=(n[s]||0)-(o[s]||0);if(c)return c<0}return!1}function qe(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 _r({home:e=process.env.HOME||pt.homedir(),env:t=process.env}={}){let n=o=>{try{return $e.statSync(o),!0}catch{return!1}};return{claude:n(F.join(e,".claude",".credentials.json")),codex:n(F.join(t.CODEX_HOME||F.join(e,".codex"),"auth.json"))}}function gs({url:e,token:t,nodeId:n=pt.hostname(),name:o=pt.hostname(),listTracks:s,detectAgents:c=null,onTracksTransferred:u=null,sampleMetrics:f=null,deliverySample:g=null,onHashKey:T=null,onViewBase:p=null,metricsMs:h=ys,onUpdateAvailable:D=null,onUpdatePolicy:A=null,onHelloRefused:k=null,updatePolicy:_=null,installKind:I=Ee(),selfUrl:C=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:K=null,removedMarkerFile:M=qt,onRemoved:b=null,onDegraded:V=null,onRecovered:Te=null,onConnected:Ye=null,ingestMode:Ne=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:en=fs,unackedRetryMs:Ge=ps,pingMs:Dr=ds,maxInflightEvents:Br=ms,httpTimeoutMs:Je=ws,stallBackoffMinMs:tn=hs,fetchImpl:nn=globalThis.fetch,log:w=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof s!="function")throw new Error("plane-link: listTracks is required");e=je(e);let Lr=C.replace(/^http/,"ws"),rn=new Set,le=new Map,ue=new Map,wt=F.join(zt,"plane-link","cursor.json"),yt=F.join(zt,"plane-link","dead-letter.ndjson"),on=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),de={},ne={},$=new Map,Xe=null,re=null,Q=0,Ie=0,sn=0,an=Promise.resolve(),E="ws",_e=0,q=!1,pe=0,gt=!1,W=ie,Se=new Set,oe=[],j=null,X=null,Y=!1,Tt=!1,Me=hr,Pe=null,ve=null,be=null,ee=null,te=null,_t=!1,Ze=0,De=!1,se=Tr(M);if(se)if(se.tokenPreview===Vt(t))De=!0,Nt({reason:se.reason??"removed",at:se.at??null,nodeId:se.nodeId??n}),w.error(`[plane-link] this node was REMOVED from the plane (${se.reason??"removed"}${se.at?` at ${new Date(se.at).toISOString()}`:""}) \u2014 not dialing. Re-provision it with a new node token, or delete ${M} to force a dial.`);else try{$e.unlinkSync(M),w.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function St({reason:i="removed",at:l=Date.now(),via:r="ctl"}={}){if(!De){De=!0,Nt({reason:i,at:l,nodeId:n,via:r});try{$e.mkdirSync(F.dirname(M),{recursive:!0}),$e.writeFileSync(M,JSON.stringify({at:l,reason:i,via:r,nodeId:n,tokenPreview:Vt(t)},null,2))}catch(a){w.error(`[plane-link] could not write ${M}: ${a?.message||a}`)}w.error(`[plane-link] the plane REMOVED this node (${i}, via ${r}) \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{b?.({reason:i,at:l,via:r})}catch{}wn({bye:!1})}}async function Cr(){try{de=JSON.parse(await Ve.readFile(wt,"utf8"))}catch{de={}}}function jr(){_t=!0,!be&&(be=setTimeout(async()=>{if(be=null,!!_t){_t=!1;try{await Ve.mkdir(F.dirname(wt),{recursive:!0}),await Ve.writeFile(wt,JSON.stringify(de))}catch(i){w.error("[plane-link] cursor flush failed:",i?.message||i)}}},us),be.unref?.())}function O(i){return!X||X.readyState!==Kt.OPEN?!1:(X.send(JSON.stringify(i)),!0)}function Be(i){let l=Date.now();for(let{trackId:r,event:a}of i){let d=$.get(a.id);$.set(a.id,{trackId:r,seq:a.seq,event:a,sentAt:l,firstSentAt:d?.firstSentAt??l,retryAt:l+Ge,tries:(d?.tries??0)+1,nacks:d?.nacks??0})}return E==="http"?(Kr(i),!0):O({v:R,ch:"events",type:"append",events:i})}function Fr(i){let l=null;for(let r of $.values())r.trackId===i&&(l==null||r.seq<l)&&(l=r.seq);return l}function vt(i){let l=ne[i];if(l==null)return;let r=Fr(i),a=r==null?l:Math.min(l,r-1);a>(de[i]??0)&&(de[i]=a,jr())}function Le(){if(Xe=Date.now(),re!=null){let i=Date.now()-re;w.log(`[plane-link] spine sync recovered after ${Math.round(i/1e3)}s degraded (${Q} stall redial(s))`);let l=re;re=null,Q=0,Ie=0,q=!1,pe=0;try{Te?.({since:l,downMs:i})}catch(r){w.error(`[plane-link] onRecovered failed: ${r?.message||r}`)}}}function bt(i){if(!Array.isArray(i)||!i.length)return;let l=new Set;for(let r of i){let a=$.get(r);a&&($.delete(r),(ne[a.trackId]??0)<a.seq&&(ne[a.trackId]=a.seq),l.add(a.trackId))}for(let r of l)vt(r);Le()}function cn(i,l){sn++;let r={at:Date.now(),trackId:i.trackId,seq:i.seq,id:i.event?.id??null,type:i.event?.type??null,reason:l,event:i.event??null};w.error(`[plane-link] DEAD-LETTER ${i.trackId} seq ${i.seq} (${r.type??"?"}): plane rejected it permanently (${l}) \u2014 kept in ${yt}`),an=an.then(async()=>{await Ve.mkdir(F.dirname(yt),{recursive:!0}),await Ve.appendFile(yt,JSON.stringify(r)+`
|
|
116
|
-
`)}).catch(a=>w.error(`[plane-link] dead-letter write failed: ${a?.message||a}`))}function ln(i,l,r,a=null){if(!Array.isArray(i)||!i.length)return;let d=new Set,m=Date.now();for(let y of i){let S=$.get(y);S&&(r?($.delete(y),cn(S,l),(ne[S.trackId]??0)<S.seq&&(ne[S.trackId]=S.seq),d.add(S.trackId)):(S.nacks++,S.retryAt=m+Math.max(1e3,Number(a)||Ge)))}for(let y of d)vt(y);Le()}function un(i=Date.now()){let l=1/0;for(let r of $.values())r.firstSentAt<l&&(l=r.firstSentAt);return l===1/0?0:i-l}function dn(){if(!Y||!$.size)return;let i=Date.now(),l=un(i);if(l<en||Xe!=null&&i-Xe<en||E==="http"&&q||Ie>i)return;Q++;let r=Math.min(tn*2**(Q-1),wr);if(re==null){re=i-l,w.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(l/1e3)}s with ${$.size} event(s) in flight (${E} door) \u2014 the plane is not answering appends`);try{V?.({since:re,unacked:$.size,oldestAgeMs:l,mode:E})}catch(a){w.error(`[plane-link] onDegraded failed: ${a?.message||a}`)}}if(E==="http"){q=!0,pe=i+r,w.error(`[plane-link] ingest circuit OPEN (stall #${Q}); probing /v1/ingest/health in ${Math.round(r/1e3)}s`);return}Ie=i+r,w.error(`[plane-link] closing the link and redialing in ${Math.round(r/1e3)}s (stall redial #${Q})`);try{X?.terminate()}catch{}}function At(){if(!Y)return;let i=Date.now();if(E==="http"&&q)return;let l=[...$.values()].filter(a=>a.event&&a.retryAt<=i&&!(E==="http"&&Se.has(a.trackId)));if(!l.length)return;l.sort((a,d)=>a.trackId===d.trackId?a.seq-d.seq:a.trackId<d.trackId?-1:1),w.log(`[plane-link] re-sending ${l.length} unacked event(s) the plane never confirmed`);let r=E==="http"?W:ie;if(E==="http"){let a=new Map;for(let d of l)(a.get(d.trackId)??a.set(d.trackId,[]).get(d.trackId)).push(d);for(let[,d]of a){if(_e>=Wt)return;Be(d.slice(0,r).map(m=>({trackId:m.trackId,event:m.event})))}return}for(let a=0;a<l.length;a+=r){let d=l.slice(a,a+r).map(m=>({trackId:m.trackId,event:m.event}));if(!Be(d))return}}async function Ur(i=null){for(;;){if(!Y)return!1;let l=$.size<Br,r=E!=="http"||_e<Wt&&!q&&(!i||!Se.has(i));if(l&&r)return!0;await new Promise(a=>setTimeout(a,100))}}function kt(){if(j=null,!Y||!oe.length)return;let i=oe;if(oe=[],E!=="http"){for(let r=0;r<i.length;r+=ie)Be(i.slice(r,r+ie));return}let l=new Map;for(let r of i)(l.get(r.trackId)??l.set(r.trackId,[]).get(r.trackId)).push(r);for(let[r,a]of l){if(Se.has(r)||_e>=Wt||q){oe.push(...a);continue}for(let d=0;d<a.length;d+=W)Be(a.slice(d,d+W))}oe.length&&!j&&(j=setTimeout(kt,yr),j.unref?.())}let Hr=i=>{let l=new Map;for(let{trackId:r,event:a}of i)(l.get(r)??l.set(r,[]).get(r)).push(a);return[...l].map(([r,a])=>({trackId:r,events:a}))},fe=(i,l,r,{progress:a})=>{let d=Date.now();for(let{event:m}of i){let y=$.get(m.id);y&&(y.nacks++,y.retryAt=d+Math.max(1e3,r||Ge))}a&&Le()};async function Kr(i){let l=new Set(i.map(r=>r.trackId));for(let r of l)Se.add(r);_e++;try{let r=await nn(`${on}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:Hr(i)}),signal:AbortSignal.timeout(Je)}),a=(Number(r.headers.get("retry-after"))||0)*1e3;if(r.status===200){let d=await r.json();W=ie;for(let m of d.results??[]){bt(m.acked??[]);let y=(S,L)=>{let z=new Map;for(let x of S??[])(z.get(x.reason)??z.set(x.reason,[]).get(x.reason)).push(x.id);for(let[x,yn]of z)ln(yn,x,L,d.retryAfterMs??null)};y(m.rejected,!0),y(m.deferred,!1)}bt(d.discarded??[]),Le();return}if(r.status===429){fe(i,"overloaded",a||5e3,{progress:!0});return}if(r.status===413&&i.length>1){W=Math.max(1,Math.floor(i.length/2)),w.error(`[plane-link] ingest request too large (${i.length} events) \u2014 halving batches to ${W}`),fe(i,"too_large",1e3,{progress:!0});return}if(r.status===413){let d=$.get(i[0].event.id);d&&($.delete(d.event.id),cn(d,"too_large"),(ne[d.trackId]??0)<d.seq&&(ne[d.trackId]=d.seq),vt(d.trackId)),Le();return}if(r.status===410){St({reason:"removed",via:"410"}),fe(i,"http_410",6e4,{progress:!1});return}if(r.status===401||r.status===403){let{tripped:d,holdMs:m}=st();if(d&&w.error(`[plane-link] holding every plane call for ${Math.round(m/6e4)} min \u2014 the ingest door refused this node's token three times in a row`),Date.now()-Ze>=gr){Ze=Date.now();let y="";try{y=(await r.json())?.error??""}catch{}w.error(`[plane-link] plane refused ingest (HTTP ${r.status}${y?`: ${y}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}fe(i,`http_${r.status}`,Math.max(6e4,He()),{progress:!1});return}if(r.status===400){w.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${i.length} event(s) for retry; this is a bug, not data`),fe(i,"http_400",6e4,{progress:!1});return}fe(i,`http_${r.status}`,a||5e3,{progress:!1})}catch(r){let a=r?.name==="TimeoutError"||r?.name==="AbortError";a&&i.length>1&&(W=Math.max(1,Math.floor(i.length/2)),w.error(`[plane-link] ingest request of ${i.length} event(s) outlived its ${Je} ms deadline \u2014 halving batches to ${W}`)),fe(i,a?"timeout":"network",5e3,{progress:!1})}finally{_e--;for(let r of l)Se.delete(r)}}async function Wr(){if(E!=="http"||!q||!Y||gt)return;let i=Date.now();if(!(pe>i||it(i))){gt=!0;try{let l=await nn(`${on}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(Je)});if(!l.ok)throw new Error(`HTTP ${l.status}`);w.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),q=!1,pe=0,Ie=Date.now()+Je+Ge+1e3;for(let r of $.values())r.retryAt=Math.min(r.retryAt,Date.now());At()}catch(l){Q++;let r=Math.min(tn*2**(Q-1),wr);pe=Date.now()+r,w.error(`[plane-link] ingest door still not answering (${l?.message||l}); next probe in ${Math.round(r/1e3)}s`)}finally{gt=!1}}}function pn(){let i=Date.now();return{mode:E,unacked:$.size,oldestUnackedAgeMs:un(i),lastProgressAt:Xe,degradedSince:re,stallRedials:Q,deadLettered:sn,http:E==="http"?{inFlight:_e,circuitOpen:q,nextProbeInMs:q?Math.max(0,pe-i):0,batchCap:W}:null}}async function fn(){if(!f)return;let i;try{i=await f()}catch(r){w.error("[plane-link] metrics sample failed:",r?.message||r);return}let l=null;try{l=g?.()??null}catch{}i&&O({v:R,ch:"ctl",type:"metrics",metrics:{...i,planeLink:pn(),...l?{delivery:l}:{}}})}function zr(){f&&(te&&clearInterval(te),fn(),te=setInterval(fn,h),te.unref?.())}async function Vr(){let i;try{i=await s()}catch(l){w.error("[plane-link] backfill listTracks failed:",l?.message||l);return}O({v:R,ch:"ctl",type:"tracks",ids:i.filter(l=>!l.transferredTo).map(l=>l.id).filter(l=>typeof l=="string")});for(let l of i){if(!Y)return;let r;try{r=await Dt(l)}catch{continue}let a=de[l.id]??0,d=r.filter(y=>y.seq>a&&typeof y.id=="string"),m=E==="http"?W:ie;for(let y=0;y<d.length;y+=m){if(!await Ur(l.id))return;let S=d.slice(y,y+Math.min(m,E==="http"?W:ie)).map(L=>({trackId:l.id,event:L}));if(!Be(S))return}}}function hn(i){!Y||!i?.trackId||typeof i.id!="string"||(oe.push({trackId:i.trackId,event:i}),j||(j=setTimeout(kt,yr),j.unref?.()))}let Ce=new Map;async function qr(i){let{reqId:l,method:r,path:a,headers:d={},body:m}=i,y=Object.fromEntries(Object.entries(d).filter(([,L])=>L!=null)),S=new AbortController;Ce.set(l,S);try{if(i.host){await Yr(i,y,S);return}K&&(y["x-bostrat-token"]=K);let L=await fetch(C+a,{method:r,headers:y,body:m?Buffer.from(m,"base64"):void 0,signal:S.signal}),z=Object.fromEntries(L.headers);for(let x of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete z[x];if(z["content-type"]||(z["content-type"]="application/octet-stream"),O({v:R,ch:"control",type:"http_head",reqId:l,status:L.status,headers:z}),L.body){for await(let x of L.body)if(!O({v:R,ch:"control",type:"http_data",reqId:l,data:Buffer.from(x).toString("base64")}))break}O({v:R,ch:"control",type:"http_end",reqId:l,error:null})}finally{Ce.delete(l)}}function Yr(i,l,r){let{reqId:a,method:d,path:m,body:y}=i,S=new URL(C);return new Promise(L=>{let z=ss.request({host:S.hostname,port:S.port,method:d,path:m,headers:{...l,host:i.host},signal:r.signal},x=>{let{connection:yn,"keep-alive":ai,"transfer-encoding":ci,upgrade:li,...Gr}=x.headers;O({v:R,ch:"control",type:"http_head",reqId:a,status:x.statusCode||502,headers:Gr}),x.on("data",Jr=>{O({v:R,ch:"control",type:"http_data",reqId:a,data:Buffer.from(Jr).toString("base64")})||x.destroy()}),x.on("end",()=>{O({v:R,ch:"control",type:"http_end",reqId:a,error:null}),L()}),x.on("error",()=>{O({v:R,ch:"control",type:"http_end",reqId:a,error:"view upstream error"}),L()})});z.on("error",x=>{O({v:R,ch:"control",type:"http_end",reqId:a,error:x?.message||"view request failed"}),L()}),y&&z.write(Buffer.from(y,"base64")),z.end()})}function mn(){if(Tt||De)return;X=new Kt(e,{headers:{authorization:`Bearer ${t}`}}),X.on("open",async()=>{let l=null;try{l=await c?.()||null}catch{l=null}O({v:R,ch:"ctl",type:"hello",nodeId:n,name:o,platform:process.platform,version:G,installKind:I,...typeof _=="function"?{updatePolicy:_()}:_?{updatePolicy:_}:{},providers:_r(),...l?{agents:l}:{}})}),X.on("message",l=>{let r;try{r=JSON.parse(l)}catch{return}if(r.ch==="ctl"&&r.type==="hello_ok"){Y=!0,Me=hr,Pe=null,$n(),w.log(`[plane-link] connected to plane as ${n}`);try{Ye?.()}catch{}if(typeof r.hashKey=="string"&&r.hashKey)try{T?.(r.hashKey)}catch{}try{p?.(typeof r.viewBase=="string"&&r.viewBase?r.viewBase:null)}catch{}r.minNodeVersion&&G&&xe(G,r.minNodeVersion)&&w.error(`[plane-link] this node is bostrat-node ${G}; the plane asks for \u2265${r.minNodeVersion} \u2014 update with: ${qe(I)}`);try{A?.(typeof r.updatePolicy=="string"?r.updatePolicy:null)}catch{}let a=typeof r.latestNodeVersion=="string"?r.latestNodeVersion:null;if(a&&G&&xe(G,a)&&!rn.has(a)){rn.add(a),w.log(`[plane-link] bostrat-node ${a} is available (this node runs ${G}) \u2014 ${qe(I)}`);try{D?.({current:G,latest:a,min:r.minNodeVersion??null,installKind:I,required:!!(r.minNodeVersion&&xe(G,r.minNodeVersion))})}catch(m){w.error(`[plane-link] update notice failed: ${m?.message||m}`)}}let d=r.ingest==="http";E=Ne==="ws"?"ws":Ne==="http"||d?"http":"ws",Ne==="http"&&!d&&w.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),E==="http"&&w.log("[plane-link] spine rides the HTTPS ingest door"),q=!1,pe=0,W=ie,ee&&clearInterval(ee),ee=setInterval(()=>{O({v:R,ch:"ctl",type:"ping"}),dn(),Wr().catch(()=>{}),At(),oe.length&&!j&&kt()},Dr),ee.unref?.(),zr(),Vr().catch(m=>w.error("[plane-link] backfill failed:",m?.message||m));return}if(r.ch==="ctl"&&r.type==="hello_refused"){let a=typeof r.minNodeVersion=="string"?r.minNodeVersion:null,d=typeof r.latestNodeVersion=="string"?r.latestNodeVersion:null;w.error(`[plane-link] plane REFUSED this node: bostrat-node ${G??"?"} is below its minimum ${a??"?"} \u2014 ${r.fix||qe(I)}`),Me=mr,Pe=0;try{k?.({current:G,min:a,latest:d,fix:r.fix||qe(I),installKind:I})}catch(m){w.error(`[plane-link] refusal handler failed: ${m?.message||m}`)}return}if(r.ch==="ctl"&&r.type==="removed"){O({v:R,ch:"ctl",type:"removed_ack",nodeId:n}),St({reason:typeof r.reason=="string"?r.reason:"removed",at:Number(r.at)||Date.now(),via:"ctl"});return}if(r.ch==="ctl"&&r.type==="update_policy"){try{A?.(typeof r.mode=="string"?r.mode:null)}catch{}return}if(r.ch==="ctl"&&r.type==="usage_caps"){pr(r.caps??{},{source:"plane",updatedAt:r.updatedAt??Date.now()}).then(a=>w.log(`[plane-link] spend caps ${Object.keys(a.caps).length?`updated (${Object.keys(a.caps).join(", ")})`:"cleared"} from plane`)).catch(a=>w.error("[plane-link] spend-caps push rejected:",a?.message||a));return}if(r.ch==="ctl"&&r.type==="transferred"&&Array.isArray(r.tracks)){Promise.resolve().then(()=>u?.(r.tracks)).catch(a=>w.error("[plane-link] transfer notice failed:",a?.message||a));return}if(r.ch==="events"&&r.type==="ack"&&Array.isArray(r.ids)){bt(r.ids);return}if(r.ch==="events"&&r.type==="nack"&&Array.isArray(r.ids)){ln(r.ids,r.reason,r.permanent===!0,r.retryAfterMs??null);return}if(r.ch==="control"&&r.type==="http_open"){qr(r).catch(a=>O({v:R,ch:"control",type:"http_end",reqId:r.reqId,error:a?.message||"tunnel error"}));return}if(r.ch==="control"&&r.type==="http_cancel"){Ce.get(r.reqId)?.abort();return}if(r.ch==="attach"&&r.type==="open"){let a=new Kt(Lr+r.path,Array.isArray(r.protocols)?r.protocols:[],r.host?{headers:{host:r.host}}:void 0);le.set(r.streamId,a),ue.set(r.streamId,[]),a.on("open",()=>{let m=ue.get(r.streamId)||[];ue.delete(r.streamId);for(let{buf:y,bin:S}of m)a.send(y,{binary:S})}),a.on("message",(m,y)=>{let S=y?m:Buffer.from(String(m));O({v:R,ch:"attach",type:"data",streamId:r.streamId,data:S.toString("base64"),...y?{bin:!0}:{}})});let d=()=>{ue.delete(r.streamId),le.delete(r.streamId)&&O({v:R,ch:"attach",type:"close",streamId:r.streamId})};a.on("close",d),a.on("error",d);return}if(r.ch==="attach"&&r.type==="data"){let a=le.get(r.streamId);if(!a)return;let d=Buffer.from(r.data,"base64"),m=r.bin===!0,y=ue.get(r.streamId);y?y.push({buf:d,bin:m}):a.send(d,{binary:m});return}if(r.ch==="attach"&&r.type==="close"){let a=le.get(r.streamId);le.delete(r.streamId),ue.delete(r.streamId),a?.close();return}});let i=()=>{Y&&w.log("[plane-link] disconnected from plane"),Y=!1,ee&&(clearInterval(ee),ee=null),te&&(clearInterval(te),te=null),$.clear(),ne={},oe=[],j&&(clearTimeout(j),j=null),Se.clear();for(let a of le.values())a.close();le.clear(),ue.clear();for(let a of Ce.values())a.abort();if(Ce.clear(),Tt||ve)return;Pe==null&&(Pe=Date.now());let l=Date.now()-Pe<as,r=Math.max(l?cs+Math.random()*ls:Me,Ie-Date.now(),He());ve=setTimeout(()=>{ve=null,mn()},r),ve.unref?.(),l||(Me=Math.min(Me*2,mr))};X.on("close",i),X.on("error",l=>{let r=/Unexpected server response: (\d+)/.exec(l?.message||"")?.[1];if(r==="410"){St({reason:"removed",via:"410"});return}if(r==="401"||r==="403"){let{tripped:a,holdMs:d}=st();a?w.error(`[plane-link] holding every plane call for ${Math.round(d/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(d/6e4)} min.`):Date.now()-Ze>=gr&&(Ze=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)`))}i()})}We.on("event",hn),Cr().then(mn);function wn({bye:i=!0}={}){Tt=!0,We.off("event",hn),ve&&clearTimeout(ve),be&&clearTimeout(be),ee&&clearInterval(ee),te&&clearInterval(te),j&&clearTimeout(j),i&&O({v:R,ch:"ctl",type:"bye",reason:"shutdown"}),X?.close()}return{stop:()=>wn(),status:()=>({connected:Y,nodeId:n,cursor:{...de},inflight:$.size,...pn(),auth:xn(),dormant:De,removed:It()}),_tick:()=>{dn(),At()}}}var G,zt,qt,R,hr,mr,as,cs,ls,ie,us,ds,ps,Sr,fs,hs,wr,ms,yr,ws,Wt,ys,gr,Yt=v(()=>{at();fr();Ot();Mt();G=(()=>{try{return is(import.meta.url)("../package.json").version||null}catch{return null}})(),zt=process.env.BOSTRAT_STATE_DIR||F.join(process.env.HOME||pt.tmpdir(),".bostrat"),qt=F.join(zt,"plane-link","removed.json");R=1,hr=1e3,mr=3e4,as=6e4,cs=1500,ls=500,ie=100,us=2e3,ds=25e3,ps=6e4,Sr=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},fs=Sr(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),hs=3e4,wr=10*6e4,ms=400,yr=250,ws=Sr(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),Wt=4,ys=6e4,gr=6e4});import{execFile as Ts}from"node:child_process";import{promisify as _s}from"node:util";function br(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}var Xc,Zc,Qc,Ar=v(()=>{Bt();Xc=_s(Ts),Zc=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Qc=Promise.resolve()});var Or={};$t(Or,{ACTIONABLE_KINDS:()=>mt,SELF_UPDATE_DIR:()=>Gt,createAutoUpdater:()=>Ms,inFlight:()=>Xt,readStatus:()=>Jt,selfUpdatePlan:()=>xr,settleSelfUpdate:()=>Is,startSelfUpdate:()=>Er,underService:()=>$r,updatePolicy:()=>xs});import kr from"node:fs";import ae from"node:fs/promises";import Ss from"node:os";import P from"node:path";import{spawn as vs}from"node:child_process";import{createRequire as bs}from"node:module";function xs({kind:e=Ee(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:mt.has(e)?e==="git-checkout"||$r(t)?"auto":"prompt":"off"}function $r(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}function xr({kind:e,to:t,from:n=ft,statusFile:o=ht,logFile:s=P.join(Gt,"last.log"),port:c=Number(process.env.PORT)||3362,npmPrefix:u=null,serviceName:f=process.platform==="darwin"?"com.bostrat.node":"bostrat-node.service",uid:g=typeof process.getuid=="function"?process.getuid():0,liveDir:T=process.env.BOSTRAT_LIVE_CHECKOUT||P.resolve(P.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:p=Date.now()}={}){if(!mt.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 h=(I,C="")=>`write_status ${U(I)} ${C}`,D=process.platform==="darwin"?`launchctl kickstart -k gui/${g}/${f}`:`systemctl --user restart ${U(f)}`,A=`
|
|
115
|
+
`),g(0)}var qn,zo,Ut,ut,Qn=v(()=>{qn="wss://plane.bostrat.ai/v1/node",zo=Ko(import.meta.url)("../package.json").version,Ut=`ghcr.io/globocodes/bostrat-node:${zo}`;ut=(e,{stdin:t,exec:n}={})=>new Promise((o,s)=>{if(n)return n(e,{stdin:t}).then(o,s);let c=Ho("fly",e,{timeout:6e5,maxBuffer:16*1024*1024},(u,f,g)=>u?s(Object.assign(u,{stdout:f,stderr:g})):o({stdout:f,stderr:g}));t!=null&&c.stdin?.end(t)})});function er(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!Go.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 Jo(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(s=>({...s,fromMs:er(s.from,"from",n),toMs:er(s.to,"to",n)}))}return t}var Yo,Go,bc,Ac,tr=v(()=>{Yo={"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)."}]},Go=/^\d{4}-\d{2}-\d{2}$/;bc=Jo(Yo),Ac=Object.freeze({staleDays:90,leadDays:14,confirmDays:30})});var nr=v(()=>{tr()});import rr from"node:fs/promises";import ir from"node:path";import Xo from"node:os";function cr(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function lr(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function ur(e){let t=new Date(cr(e));return t.setDate(t.getDate()+7),t.getTime()}function dr(e){let t=new Date(lr(e));return t.setMonth(t.getMonth()+1),t.getTime()}function ns(e=Date.now()){for(let[t,n]of Object.entries(es)){let o=n.startOf(e);if(o!==dt[t]){dt[t]=o;for(let s of Ht.values())s[t]=0;for(let s of or)s.includes(`:${t}:`)&&!s.endsWith(`:${o}`)&&or.delete(s)}}}function rs(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 s={};for(let c of["weeklyUsd","monthlyUsd"]){let u=o?.[c];if(u==null||u==="")continue;let f=Number(u);if(!Number.isFinite(f)||f<=0)throw new Error(`${n}.${c} must be a positive number or null`);s[c]=f}Object.keys(s).length&&(t[n]=s)}return t}async function pr(e,{source:t="local",updatedAt:n=Date.now()}={}){ke={caps:rs(e),updatedAt:n,source:t};try{await rr.mkdir(ar,{recursive:!0}),await rr.writeFile(Zo(),JSON.stringify(ke,null,2))}catch{}return os()}function os(e=Date.now()){ns(e);let t=new Set([...Ht.keys(),...Object.keys(ke?.caps||{})]),n={};for(let o of t){let s=Ht.get(o);n[o]={weeklyUsd:sr(s?.weekly||0),monthlyUsd:sr(s?.monthly||0)}}return{caps:ke?.caps||{},configured:!!ke,seeded:ts,updatedAt:ke?.updatedAt??null,spent:n,windows:{weekly:{start:dt.weekly,resetsAt:ur(e)},monthly:{start:dt.monthly,resetsAt:dr(e)}},warnPct:Qo}}var ar,Zo,Qo,es,ke,ts,dt,Ht,or,sr,fr=v(()=>{at();nr();ar=process.env.BOSTRAT_STATE_DIR||ir.join(process.env.HOME||Xo.tmpdir(),".bostrat"),Zo=()=>ir.join(ar,"usage-caps.json"),Qo=.8;es={weekly:{startOf:cr,nextStart:ur,capKey:"weeklyUsd"},monthly:{startOf:lr,nextStart:dr,capKey:"monthlyUsd"}},ke=null,ts=!1,dt={weekly:0,monthly:0},Ht=new Map,or=new Set;sr=e=>Math.round(e*1e4)/1e4});var vr={};$t(vr,{REMOVED_MARKER:()=>qt,detectInstallKind:()=>Ee,detectProviderAuth:()=>_r,maskToken:()=>Vt,readRemovedMarker:()=>Tr,startPlaneLink:()=>gs,updateCommandFor:()=>qe,versionLt:()=>xe});import $e from"node:fs";import Ve from"node:fs/promises";import ss from"node:http";import L from"node:path";import pt from"node:os";import{createRequire as is}from"node:module";import Kt from"ws";function Vt(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function Tr(e=qt){try{let t=JSON.parse($e.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function Ee({env:e=process.env,pkgDir:t=L.resolve(L.dirname(new URL(import.meta.url).pathname),".."),exists:n=$e.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(L.join(t,"..",".git"))?"git-checkout":"dev"}function xe(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 s=0;s<Math.max(n.length,o.length);s++){let c=(n[s]||0)-(o[s]||0);if(c)return c<0}return!1}function qe(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 _r({home:e=process.env.HOME||pt.homedir(),env:t=process.env}={}){let n=o=>{try{return $e.statSync(o),!0}catch{return!1}};return{claude:n(L.join(e,".claude",".credentials.json")),codex:n(L.join(t.CODEX_HOME||L.join(e,".codex"),"auth.json")),cursor:n(L.join(t.XDG_CONFIG_HOME||L.join(e,".config"),"cursor","auth.json"))}}function gs({url:e,token:t,nodeId:n=pt.hostname(),name:o=pt.hostname(),listTracks:s,detectAgents:c=null,onTracksTransferred:u=null,sampleMetrics:f=null,deliverySample:g=null,onHashKey:T=null,onViewBase:p=null,metricsMs:h=ys,onUpdateAvailable:D=null,onUpdatePolicy:A=null,onHelloRefused:k=null,updatePolicy:_=null,installKind:I=Ee(),selfUrl:j=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:K=null,removedMarkerFile:M=qt,onRemoved:b=null,onDegraded:V=null,onRecovered:Te=null,onConnected:Ye=null,ingestMode:Ne=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:en=fs,unackedRetryMs:Ge=ps,pingMs:Dr=ds,maxInflightEvents:Br=ms,httpTimeoutMs:Je=ws,stallBackoffMinMs:tn=hs,fetchImpl:nn=globalThis.fetch,log:w=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof s!="function")throw new Error("plane-link: listTracks is required");e=je(e);let Lr=j.replace(/^http/,"ws"),rn=new Set,le=new Map,ue=new Map,wt=L.join(zt,"plane-link","cursor.json"),yt=L.join(zt,"plane-link","dead-letter.ndjson"),on=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),de={},ne={},$=new Map,Xe=null,re=null,Q=0,Ie=0,sn=0,an=Promise.resolve(),E="ws",_e=0,q=!1,pe=0,gt=!1,W=ie,Se=new Set,oe=[],F=null,X=null,Y=!1,Tt=!1,Me=hr,Pe=null,ve=null,be=null,ee=null,te=null,_t=!1,Ze=0,De=!1,se=Tr(M);if(se)if(se.tokenPreview===Vt(t))De=!0,Nt({reason:se.reason??"removed",at:se.at??null,nodeId:se.nodeId??n}),w.error(`[plane-link] this node was REMOVED from the plane (${se.reason??"removed"}${se.at?` at ${new Date(se.at).toISOString()}`:""}) \u2014 not dialing. Re-provision it with a new node token, or delete ${M} to force a dial.`);else try{$e.unlinkSync(M),w.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function St({reason:i="removed",at:l=Date.now(),via:r="ctl"}={}){if(!De){De=!0,Nt({reason:i,at:l,nodeId:n,via:r});try{$e.mkdirSync(L.dirname(M),{recursive:!0}),$e.writeFileSync(M,JSON.stringify({at:l,reason:i,via:r,nodeId:n,tokenPreview:Vt(t)},null,2))}catch(a){w.error(`[plane-link] could not write ${M}: ${a?.message||a}`)}w.error(`[plane-link] the plane REMOVED this node (${i}, via ${r}) \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{b?.({reason:i,at:l,via:r})}catch{}wn({bye:!1})}}async function Cr(){try{de=JSON.parse(await Ve.readFile(wt,"utf8"))}catch{de={}}}function jr(){_t=!0,!be&&(be=setTimeout(async()=>{if(be=null,!!_t){_t=!1;try{await Ve.mkdir(L.dirname(wt),{recursive:!0}),await Ve.writeFile(wt,JSON.stringify(de))}catch(i){w.error("[plane-link] cursor flush failed:",i?.message||i)}}},us),be.unref?.())}function O(i){return!X||X.readyState!==Kt.OPEN?!1:(X.send(JSON.stringify(i)),!0)}function Be(i){let l=Date.now();for(let{trackId:r,event:a}of i){let d=$.get(a.id);$.set(a.id,{trackId:r,seq:a.seq,event:a,sentAt:l,firstSentAt:d?.firstSentAt??l,retryAt:l+Ge,tries:(d?.tries??0)+1,nacks:d?.nacks??0})}return E==="http"?(Kr(i),!0):O({v:R,ch:"events",type:"append",events:i})}function Fr(i){let l=null;for(let r of $.values())r.trackId===i&&(l==null||r.seq<l)&&(l=r.seq);return l}function vt(i){let l=ne[i];if(l==null)return;let r=Fr(i),a=r==null?l:Math.min(l,r-1);a>(de[i]??0)&&(de[i]=a,jr())}function Le(){if(Xe=Date.now(),re!=null){let i=Date.now()-re;w.log(`[plane-link] spine sync recovered after ${Math.round(i/1e3)}s degraded (${Q} stall redial(s))`);let l=re;re=null,Q=0,Ie=0,q=!1,pe=0;try{Te?.({since:l,downMs:i})}catch(r){w.error(`[plane-link] onRecovered failed: ${r?.message||r}`)}}}function bt(i){if(!Array.isArray(i)||!i.length)return;let l=new Set;for(let r of i){let a=$.get(r);a&&($.delete(r),(ne[a.trackId]??0)<a.seq&&(ne[a.trackId]=a.seq),l.add(a.trackId))}for(let r of l)vt(r);Le()}function cn(i,l){sn++;let r={at:Date.now(),trackId:i.trackId,seq:i.seq,id:i.event?.id??null,type:i.event?.type??null,reason:l,event:i.event??null};w.error(`[plane-link] DEAD-LETTER ${i.trackId} seq ${i.seq} (${r.type??"?"}): plane rejected it permanently (${l}) \u2014 kept in ${yt}`),an=an.then(async()=>{await Ve.mkdir(L.dirname(yt),{recursive:!0}),await Ve.appendFile(yt,JSON.stringify(r)+`
|
|
116
|
+
`)}).catch(a=>w.error(`[plane-link] dead-letter write failed: ${a?.message||a}`))}function ln(i,l,r,a=null){if(!Array.isArray(i)||!i.length)return;let d=new Set,m=Date.now();for(let y of i){let S=$.get(y);S&&(r?($.delete(y),cn(S,l),(ne[S.trackId]??0)<S.seq&&(ne[S.trackId]=S.seq),d.add(S.trackId)):(S.nacks++,S.retryAt=m+Math.max(1e3,Number(a)||Ge)))}for(let y of d)vt(y);Le()}function un(i=Date.now()){let l=1/0;for(let r of $.values())r.firstSentAt<l&&(l=r.firstSentAt);return l===1/0?0:i-l}function dn(){if(!Y||!$.size)return;let i=Date.now(),l=un(i);if(l<en||Xe!=null&&i-Xe<en||E==="http"&&q||Ie>i)return;Q++;let r=Math.min(tn*2**(Q-1),wr);if(re==null){re=i-l,w.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(l/1e3)}s with ${$.size} event(s) in flight (${E} door) \u2014 the plane is not answering appends`);try{V?.({since:re,unacked:$.size,oldestAgeMs:l,mode:E})}catch(a){w.error(`[plane-link] onDegraded failed: ${a?.message||a}`)}}if(E==="http"){q=!0,pe=i+r,w.error(`[plane-link] ingest circuit OPEN (stall #${Q}); probing /v1/ingest/health in ${Math.round(r/1e3)}s`);return}Ie=i+r,w.error(`[plane-link] closing the link and redialing in ${Math.round(r/1e3)}s (stall redial #${Q})`);try{X?.terminate()}catch{}}function At(){if(!Y)return;let i=Date.now();if(E==="http"&&q)return;let l=[...$.values()].filter(a=>a.event&&a.retryAt<=i&&!(E==="http"&&Se.has(a.trackId)));if(!l.length)return;l.sort((a,d)=>a.trackId===d.trackId?a.seq-d.seq:a.trackId<d.trackId?-1:1),w.log(`[plane-link] re-sending ${l.length} unacked event(s) the plane never confirmed`);let r=E==="http"?W:ie;if(E==="http"){let a=new Map;for(let d of l)(a.get(d.trackId)??a.set(d.trackId,[]).get(d.trackId)).push(d);for(let[,d]of a){if(_e>=Wt)return;Be(d.slice(0,r).map(m=>({trackId:m.trackId,event:m.event})))}return}for(let a=0;a<l.length;a+=r){let d=l.slice(a,a+r).map(m=>({trackId:m.trackId,event:m.event}));if(!Be(d))return}}async function Ur(i=null){for(;;){if(!Y)return!1;let l=$.size<Br,r=E!=="http"||_e<Wt&&!q&&(!i||!Se.has(i));if(l&&r)return!0;await new Promise(a=>setTimeout(a,100))}}function kt(){if(F=null,!Y||!oe.length)return;let i=oe;if(oe=[],E!=="http"){for(let r=0;r<i.length;r+=ie)Be(i.slice(r,r+ie));return}let l=new Map;for(let r of i)(l.get(r.trackId)??l.set(r.trackId,[]).get(r.trackId)).push(r);for(let[r,a]of l){if(Se.has(r)||_e>=Wt||q){oe.push(...a);continue}for(let d=0;d<a.length;d+=W)Be(a.slice(d,d+W))}oe.length&&!F&&(F=setTimeout(kt,yr),F.unref?.())}let Hr=i=>{let l=new Map;for(let{trackId:r,event:a}of i)(l.get(r)??l.set(r,[]).get(r)).push(a);return[...l].map(([r,a])=>({trackId:r,events:a}))},fe=(i,l,r,{progress:a})=>{let d=Date.now();for(let{event:m}of i){let y=$.get(m.id);y&&(y.nacks++,y.retryAt=d+Math.max(1e3,r||Ge))}a&&Le()};async function Kr(i){let l=new Set(i.map(r=>r.trackId));for(let r of l)Se.add(r);_e++;try{let r=await nn(`${on}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:Hr(i)}),signal:AbortSignal.timeout(Je)}),a=(Number(r.headers.get("retry-after"))||0)*1e3;if(r.status===200){let d=await r.json();W=ie;for(let m of d.results??[]){bt(m.acked??[]);let y=(S,C)=>{let z=new Map;for(let x of S??[])(z.get(x.reason)??z.set(x.reason,[]).get(x.reason)).push(x.id);for(let[x,yn]of z)ln(yn,x,C,d.retryAfterMs??null)};y(m.rejected,!0),y(m.deferred,!1)}bt(d.discarded??[]),Le();return}if(r.status===429){fe(i,"overloaded",a||5e3,{progress:!0});return}if(r.status===413&&i.length>1){W=Math.max(1,Math.floor(i.length/2)),w.error(`[plane-link] ingest request too large (${i.length} events) \u2014 halving batches to ${W}`),fe(i,"too_large",1e3,{progress:!0});return}if(r.status===413){let d=$.get(i[0].event.id);d&&($.delete(d.event.id),cn(d,"too_large"),(ne[d.trackId]??0)<d.seq&&(ne[d.trackId]=d.seq),vt(d.trackId)),Le();return}if(r.status===410){St({reason:"removed",via:"410"}),fe(i,"http_410",6e4,{progress:!1});return}if(r.status===401||r.status===403){let{tripped:d,holdMs:m}=st();if(d&&w.error(`[plane-link] holding every plane call for ${Math.round(m/6e4)} min \u2014 the ingest door refused this node's token three times in a row`),Date.now()-Ze>=gr){Ze=Date.now();let y="";try{y=(await r.json())?.error??""}catch{}w.error(`[plane-link] plane refused ingest (HTTP ${r.status}${y?`: ${y}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}fe(i,`http_${r.status}`,Math.max(6e4,He()),{progress:!1});return}if(r.status===400){w.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${i.length} event(s) for retry; this is a bug, not data`),fe(i,"http_400",6e4,{progress:!1});return}fe(i,`http_${r.status}`,a||5e3,{progress:!1})}catch(r){let a=r?.name==="TimeoutError"||r?.name==="AbortError";a&&i.length>1&&(W=Math.max(1,Math.floor(i.length/2)),w.error(`[plane-link] ingest request of ${i.length} event(s) outlived its ${Je} ms deadline \u2014 halving batches to ${W}`)),fe(i,a?"timeout":"network",5e3,{progress:!1})}finally{_e--;for(let r of l)Se.delete(r)}}async function Wr(){if(E!=="http"||!q||!Y||gt)return;let i=Date.now();if(!(pe>i||it(i))){gt=!0;try{let l=await nn(`${on}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(Je)});if(!l.ok)throw new Error(`HTTP ${l.status}`);w.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),q=!1,pe=0,Ie=Date.now()+Je+Ge+1e3;for(let r of $.values())r.retryAt=Math.min(r.retryAt,Date.now());At()}catch(l){Q++;let r=Math.min(tn*2**(Q-1),wr);pe=Date.now()+r,w.error(`[plane-link] ingest door still not answering (${l?.message||l}); next probe in ${Math.round(r/1e3)}s`)}finally{gt=!1}}}function pn(){let i=Date.now();return{mode:E,unacked:$.size,oldestUnackedAgeMs:un(i),lastProgressAt:Xe,degradedSince:re,stallRedials:Q,deadLettered:sn,http:E==="http"?{inFlight:_e,circuitOpen:q,nextProbeInMs:q?Math.max(0,pe-i):0,batchCap:W}:null}}async function fn(){if(!f)return;let i;try{i=await f()}catch(r){w.error("[plane-link] metrics sample failed:",r?.message||r);return}let l=null;try{l=g?.()??null}catch{}i&&O({v:R,ch:"ctl",type:"metrics",metrics:{...i,planeLink:pn(),...l?{delivery:l}:{}}})}function zr(){f&&(te&&clearInterval(te),fn(),te=setInterval(fn,h),te.unref?.())}async function Vr(){let i;try{i=await s()}catch(l){w.error("[plane-link] backfill listTracks failed:",l?.message||l);return}O({v:R,ch:"ctl",type:"tracks",ids:i.filter(l=>!l.transferredTo).map(l=>l.id).filter(l=>typeof l=="string")});for(let l of i){if(!Y)return;let r;try{r=await Dt(l)}catch{continue}let a=de[l.id]??0,d=r.filter(y=>y.seq>a&&typeof y.id=="string"),m=E==="http"?W:ie;for(let y=0;y<d.length;y+=m){if(!await Ur(l.id))return;let S=d.slice(y,y+Math.min(m,E==="http"?W:ie)).map(C=>({trackId:l.id,event:C}));if(!Be(S))return}}}function hn(i){!Y||!i?.trackId||typeof i.id!="string"||(oe.push({trackId:i.trackId,event:i}),F||(F=setTimeout(kt,yr),F.unref?.()))}let Ce=new Map;async function qr(i){let{reqId:l,method:r,path:a,headers:d={},body:m}=i,y=Object.fromEntries(Object.entries(d).filter(([,C])=>C!=null)),S=new AbortController;Ce.set(l,S);try{if(i.host){await Yr(i,y,S);return}K&&(y["x-bostrat-token"]=K);let C=await fetch(j+a,{method:r,headers:y,body:m?Buffer.from(m,"base64"):void 0,signal:S.signal}),z=Object.fromEntries(C.headers);for(let x of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete z[x];if(z["content-type"]||(z["content-type"]="application/octet-stream"),O({v:R,ch:"control",type:"http_head",reqId:l,status:C.status,headers:z}),C.body){for await(let x of C.body)if(!O({v:R,ch:"control",type:"http_data",reqId:l,data:Buffer.from(x).toString("base64")}))break}O({v:R,ch:"control",type:"http_end",reqId:l,error:null})}finally{Ce.delete(l)}}function Yr(i,l,r){let{reqId:a,method:d,path:m,body:y}=i,S=new URL(j);return new Promise(C=>{let z=ss.request({host:S.hostname,port:S.port,method:d,path:m,headers:{...l,host:i.host},signal:r.signal},x=>{let{connection:yn,"keep-alive":ai,"transfer-encoding":ci,upgrade:li,...Gr}=x.headers;O({v:R,ch:"control",type:"http_head",reqId:a,status:x.statusCode||502,headers:Gr}),x.on("data",Jr=>{O({v:R,ch:"control",type:"http_data",reqId:a,data:Buffer.from(Jr).toString("base64")})||x.destroy()}),x.on("end",()=>{O({v:R,ch:"control",type:"http_end",reqId:a,error:null}),C()}),x.on("error",()=>{O({v:R,ch:"control",type:"http_end",reqId:a,error:"view upstream error"}),C()})});z.on("error",x=>{O({v:R,ch:"control",type:"http_end",reqId:a,error:x?.message||"view request failed"}),C()}),y&&z.write(Buffer.from(y,"base64")),z.end()})}function mn(){if(Tt||De)return;X=new Kt(e,{headers:{authorization:`Bearer ${t}`}}),X.on("open",async()=>{let l=null;try{l=await c?.()||null}catch{l=null}O({v:R,ch:"ctl",type:"hello",nodeId:n,name:o,platform:process.platform,version:G,installKind:I,...typeof _=="function"?{updatePolicy:_()}:_?{updatePolicy:_}:{},providers:_r(),...l?{agents:l}:{}})}),X.on("message",l=>{let r;try{r=JSON.parse(l)}catch{return}if(r.ch==="ctl"&&r.type==="hello_ok"){Y=!0,Me=hr,Pe=null,$n(),w.log(`[plane-link] connected to plane as ${n}`);try{Ye?.()}catch{}if(typeof r.hashKey=="string"&&r.hashKey)try{T?.(r.hashKey)}catch{}try{p?.(typeof r.viewBase=="string"&&r.viewBase?r.viewBase:null)}catch{}r.minNodeVersion&&G&&xe(G,r.minNodeVersion)&&w.error(`[plane-link] this node is bostrat-node ${G}; the plane asks for \u2265${r.minNodeVersion} \u2014 update with: ${qe(I)}`);try{A?.(typeof r.updatePolicy=="string"?r.updatePolicy:null)}catch{}let a=typeof r.latestNodeVersion=="string"?r.latestNodeVersion:null;if(a&&G&&xe(G,a)&&!rn.has(a)){rn.add(a),w.log(`[plane-link] bostrat-node ${a} is available (this node runs ${G}) \u2014 ${qe(I)}`);try{D?.({current:G,latest:a,min:r.minNodeVersion??null,installKind:I,required:!!(r.minNodeVersion&&xe(G,r.minNodeVersion))})}catch(m){w.error(`[plane-link] update notice failed: ${m?.message||m}`)}}let d=r.ingest==="http";E=Ne==="ws"?"ws":Ne==="http"||d?"http":"ws",Ne==="http"&&!d&&w.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),E==="http"&&w.log("[plane-link] spine rides the HTTPS ingest door"),q=!1,pe=0,W=ie,ee&&clearInterval(ee),ee=setInterval(()=>{O({v:R,ch:"ctl",type:"ping"}),dn(),Wr().catch(()=>{}),At(),oe.length&&!F&&kt()},Dr),ee.unref?.(),zr(),Vr().catch(m=>w.error("[plane-link] backfill failed:",m?.message||m));return}if(r.ch==="ctl"&&r.type==="hello_refused"){let a=typeof r.minNodeVersion=="string"?r.minNodeVersion:null,d=typeof r.latestNodeVersion=="string"?r.latestNodeVersion:null;w.error(`[plane-link] plane REFUSED this node: bostrat-node ${G??"?"} is below its minimum ${a??"?"} \u2014 ${r.fix||qe(I)}`),Me=mr,Pe=0;try{k?.({current:G,min:a,latest:d,fix:r.fix||qe(I),installKind:I})}catch(m){w.error(`[plane-link] refusal handler failed: ${m?.message||m}`)}return}if(r.ch==="ctl"&&r.type==="removed"){O({v:R,ch:"ctl",type:"removed_ack",nodeId:n}),St({reason:typeof r.reason=="string"?r.reason:"removed",at:Number(r.at)||Date.now(),via:"ctl"});return}if(r.ch==="ctl"&&r.type==="update_policy"){try{A?.(typeof r.mode=="string"?r.mode:null)}catch{}return}if(r.ch==="ctl"&&r.type==="usage_caps"){pr(r.caps??{},{source:"plane",updatedAt:r.updatedAt??Date.now()}).then(a=>w.log(`[plane-link] spend caps ${Object.keys(a.caps).length?`updated (${Object.keys(a.caps).join(", ")})`:"cleared"} from plane`)).catch(a=>w.error("[plane-link] spend-caps push rejected:",a?.message||a));return}if(r.ch==="ctl"&&r.type==="transferred"&&Array.isArray(r.tracks)){Promise.resolve().then(()=>u?.(r.tracks)).catch(a=>w.error("[plane-link] transfer notice failed:",a?.message||a));return}if(r.ch==="events"&&r.type==="ack"&&Array.isArray(r.ids)){bt(r.ids);return}if(r.ch==="events"&&r.type==="nack"&&Array.isArray(r.ids)){ln(r.ids,r.reason,r.permanent===!0,r.retryAfterMs??null);return}if(r.ch==="control"&&r.type==="http_open"){qr(r).catch(a=>O({v:R,ch:"control",type:"http_end",reqId:r.reqId,error:a?.message||"tunnel error"}));return}if(r.ch==="control"&&r.type==="http_cancel"){Ce.get(r.reqId)?.abort();return}if(r.ch==="attach"&&r.type==="open"){let a=new Kt(Lr+r.path,Array.isArray(r.protocols)?r.protocols:[],r.host?{headers:{host:r.host}}:void 0);le.set(r.streamId,a),ue.set(r.streamId,[]),a.on("open",()=>{let m=ue.get(r.streamId)||[];ue.delete(r.streamId);for(let{buf:y,bin:S}of m)a.send(y,{binary:S})}),a.on("message",(m,y)=>{let S=y?m:Buffer.from(String(m));O({v:R,ch:"attach",type:"data",streamId:r.streamId,data:S.toString("base64"),...y?{bin:!0}:{}})});let d=()=>{ue.delete(r.streamId),le.delete(r.streamId)&&O({v:R,ch:"attach",type:"close",streamId:r.streamId})};a.on("close",d),a.on("error",d);return}if(r.ch==="attach"&&r.type==="data"){let a=le.get(r.streamId);if(!a)return;let d=Buffer.from(r.data,"base64"),m=r.bin===!0,y=ue.get(r.streamId);y?y.push({buf:d,bin:m}):a.send(d,{binary:m});return}if(r.ch==="attach"&&r.type==="close"){let a=le.get(r.streamId);le.delete(r.streamId),ue.delete(r.streamId),a?.close();return}});let i=()=>{Y&&w.log("[plane-link] disconnected from plane"),Y=!1,ee&&(clearInterval(ee),ee=null),te&&(clearInterval(te),te=null),$.clear(),ne={},oe=[],F&&(clearTimeout(F),F=null),Se.clear();for(let a of le.values())a.close();le.clear(),ue.clear();for(let a of Ce.values())a.abort();if(Ce.clear(),Tt||ve)return;Pe==null&&(Pe=Date.now());let l=Date.now()-Pe<as,r=Math.max(l?cs+Math.random()*ls:Me,Ie-Date.now(),He());ve=setTimeout(()=>{ve=null,mn()},r),ve.unref?.(),l||(Me=Math.min(Me*2,mr))};X.on("close",i),X.on("error",l=>{let r=/Unexpected server response: (\d+)/.exec(l?.message||"")?.[1];if(r==="410"){St({reason:"removed",via:"410"});return}if(r==="401"||r==="403"){let{tripped:a,holdMs:d}=st();a?w.error(`[plane-link] holding every plane call for ${Math.round(d/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(d/6e4)} min.`):Date.now()-Ze>=gr&&(Ze=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)`))}i()})}We.on("event",hn),Cr().then(mn);function wn({bye:i=!0}={}){Tt=!0,We.off("event",hn),ve&&clearTimeout(ve),be&&clearTimeout(be),ee&&clearInterval(ee),te&&clearInterval(te),F&&clearTimeout(F),i&&O({v:R,ch:"ctl",type:"bye",reason:"shutdown"}),X?.close()}return{stop:()=>wn(),status:()=>({connected:Y,nodeId:n,cursor:{...de},inflight:$.size,...pn(),auth:xn(),dormant:De,removed:It()}),_tick:()=>{dn(),At()}}}var G,zt,qt,R,hr,mr,as,cs,ls,ie,us,ds,ps,Sr,fs,hs,wr,ms,yr,ws,Wt,ys,gr,Yt=v(()=>{at();fr();Ot();Mt();G=(()=>{try{return is(import.meta.url)("../package.json").version||null}catch{return null}})(),zt=process.env.BOSTRAT_STATE_DIR||L.join(process.env.HOME||pt.tmpdir(),".bostrat"),qt=L.join(zt,"plane-link","removed.json");R=1,hr=1e3,mr=3e4,as=6e4,cs=1500,ls=500,ie=100,us=2e3,ds=25e3,ps=6e4,Sr=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},fs=Sr(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),hs=3e4,wr=10*6e4,ms=400,yr=250,ws=Sr(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),Wt=4,ys=6e4,gr=6e4});import{execFile as Ts}from"node:child_process";import{promisify as _s}from"node:util";function br(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}var Xc,Zc,Qc,Ar=v(()=>{Bt();Xc=_s(Ts),Zc=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Qc=Promise.resolve()});var Or={};$t(Or,{ACTIONABLE_KINDS:()=>mt,SELF_UPDATE_DIR:()=>Gt,createAutoUpdater:()=>Ms,inFlight:()=>Xt,readStatus:()=>Jt,selfUpdatePlan:()=>xr,settleSelfUpdate:()=>Is,startSelfUpdate:()=>Er,underService:()=>$r,updatePolicy:()=>xs});import kr from"node:fs";import ae from"node:fs/promises";import Ss from"node:os";import P from"node:path";import{spawn as vs}from"node:child_process";import{createRequire as bs}from"node:module";function xs({kind:e=Ee(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:mt.has(e)?e==="git-checkout"||$r(t)?"auto":"prompt":"off"}function $r(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}function xr({kind:e,to:t,from:n=ft,statusFile:o=ht,logFile:s=P.join(Gt,"last.log"),port:c=Number(process.env.PORT)||3362,npmPrefix:u=null,serviceName:f=process.platform==="darwin"?"com.bostrat.node":"bostrat-node.service",uid:g=typeof process.getuid=="function"?process.getuid():0,liveDir:T=process.env.BOSTRAT_LIVE_CHECKOUT||P.resolve(P.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:p=Date.now()}={}){if(!mt.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 h=(I,j="")=>`write_status ${U(I)} ${j}`,D=process.platform==="darwin"?`launchctl kickstart -k gui/${g}/${f}`:`systemctl --user restart ${U(f)}`,A=`
|
|
117
117
|
health() {
|
|
118
118
|
for i in $(seq 1 45); do
|
|
119
119
|
sleep 1
|
|
@@ -136,8 +136,8 @@ write_status() { # <status> [error]
|
|
|
136
136
|
"\${err:+,\\"error\\":\\"$err\\"}" > "$STATUS_FILE.tmp" && mv "$STATUS_FILE.tmp" "$STATUS_FILE"
|
|
137
137
|
}
|
|
138
138
|
${A}
|
|
139
|
-
`;if(e==="npm-global"){let I=u?P.join(u,"bin","npm"):"npm",
|
|
140
|
-
`:"",K=u?` --prefix ${U(u)}`:"",M=b=>`${U(I)} install -g "bostrat-node@${b}" --no-audit --no-fund${K}`;return k+
|
|
139
|
+
`;if(e==="npm-global"){let I=u?P.join(u,"bin","npm"):"npm",j=u?`export PATH=${U(P.join(u,"bin"))}:"$PATH"
|
|
140
|
+
`:"",K=u?` --prefix ${U(u)}`:"",M=b=>`${U(I)} install -g "bostrat-node@${b}" --no-audit --no-fund${K}`;return k+j+`
|
|
141
141
|
${h("updating",'"" ')}
|
|
142
142
|
if ! ${M("$TO")}; then
|
|
143
143
|
${h("failed",'"npm install failed"')}
|
|
@@ -197,8 +197,8 @@ injected per box, stored 0600 in node.env, never sent to the plane.
|
|
|
197
197
|
Bind host: unset = loopback-only while plane-linked; BOSTRAT_HOST=all restores bind-all (LAN).`}),n.write(`
|
|
198
198
|
wrote ${e}
|
|
199
199
|
|
|
200
|
-
`);let I={..._,...process.env},
|
|
201
|
-
`));return K&&n.write("\nnext: `bostrat-node run` (foreground) or `bostrat-node install-service`\n"),{ok:K,results:
|
|
200
|
+
`);let I={..._,...process.env},j=await Mr(I,{envFile:e}),K=Pr(j,M=>n.write(M+`
|
|
201
|
+
`));return K&&n.write("\nnext: `bostrat-node run` (foreground) or `bostrat-node install-service`\n"),{ok:K,results:j}}function ni({envFile:e=Ae()}={}){gn(e);let t=process.env.BOSTRAT_NODE_BIN||process.execPath,n=Ds(t,[Fs],{cwd:ge,stdio:"inherit",env:process.env});for(let o of["SIGINT","SIGTERM"])process.on(o,()=>n.kill(o));return n.on("exit",(o,s)=>process.exit(s?1:o??1)),n}function ri({agentDir:e=ge,nodeBin:t=process.execPath,envFile:n=Ae(),home:o=Re.homedir()}={}){let s=N.dirname(t),c=N.join(N.basename(Qt),"index.js");return`# bostrat node \u2014 written by \`bostrat-node install-service\`. Config lives in
|
|
202
202
|
# ${n} (edit + \`systemctl --user restart bostrat-node\`), not here.
|
|
203
203
|
[Unit]
|
|
204
204
|
Description=bostrat node \u2014 box runtime dialing out to the coordination plane (plane-p1-package-node-scope-2026-07-14.md)
|