@north-light/cli 0.3.505 → 0.3.507

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.
@@ -1,2 +1,2 @@
1
- var Q=Object.defineProperty;var d=(s,f)=>Q(s,"name",{value:f,configurable:!0});import{mkdtempSync as V,readFileSync as q,rmSync as E,existsSync as Y}from"node:fs";import{tmpdir as Z}from"node:os";import{join as F}from"node:path";import{cliClient as ee}from"../../commands/api-client.js";import{loadPins as re,savePins as ne}from"../../core/canvas/browse/pins.js";import{detectColorCaps as oe}from"../../core/tui/draw.js";import{getTerminalSize as ie,parseKeypress as te,restoreTerminal as se,setupTerminal as de}from"../../core/tui/terminal.js";import{getPaneOption as le,runShellInBackground as ce,setPaneOption as C,shellQuote as h}from"../../core/runtime/placement-tmux.js";import{matchesTerminalInput as ae,resolveUserKeybindings as fe}from"../../core/keybindings/index.js";import{VIEWER_OWNER_OPTION as S}from"../attach/session/pane-tag.js";import{buildLines as ue,nearestSelectable as M,placements as pe,rootOf as b,stepSelectable as D}from"./model.js";import{listHeight as me,renderDashboard as he}from"./render.js";const ge=2e3,we=250,be=6e3;function P(s){return s.kind==="heading"?null:s.kind==="root"?`root:${s.id}`:`child:${s.rootId}:${s.id}`}d(P,"lineKey");async function Oe(s){const f=process.env.TMUX_PANE;if(f===void 0||f==="")throw new Error("the dashboard runs in a tmux pane (TMUX_PANE is unset)");const m=ee(),R=fe(),j=oe(),c=d((r,n)=>ae(R,r,n),"matches"),k=String(process.pid);C(f,S,k);for(const r of["@crtr_node","@crtr_attach","@crtr_viewer_at"])C(f,r,"");const K=V(F(Z(),"crtr-dashboard-"));let I=!1;const L=d(()=>{if(!I){I=!0;try{se()}catch{}le(f,S)===k&&C(f,S,"");try{E(K,{recursive:!0,force:!0})}catch{}}},"cleanup");process.once("exit",L);for(const r of["SIGTERM","SIGHUP"])process.once(r,()=>{L(),process.exit(0)});const e={rows:[],placed:new Map,pinned:re(),expanded:new Set,lines:[],cursorKey:null,cursor:0,scroll:0,scope:s.profileId===void 0?"all":"profile",profileLabel:s.profileId??"",loaded:!1,help:!1,inputFocused:!1,inputText:"",dirs:[s.cwd],dirIndex:0,dirsLoaded:!1,rename:null,renameTarget:null,confirmClose:null,notice:null,opening:null},x=new Set;let $;const u=d(r=>{e.notice=r,$!==void 0&&clearTimeout($),$=setTimeout(()=>{e.notice=null,l()},be)},"notify"),g=d(()=>{const r=e.rows.filter(t=>!x.has(t.node_id));e.lines=ue(r,e.placed,e.pinned,e.expanded);const n=e.cursorKey===null?-1:e.lines.findIndex(t=>P(t)===e.cursorKey);e.cursor=n>=0?n:M(e.lines,e.cursor),e.cursor<0&&(e.cursor=0);const o=e.lines[e.cursor];e.cursorKey=o===void 0?null:P(o)},"rebuild"),l=d(()=>{if(I)return;const r=ie(),n=me(r.rows)-1;e.cursor<e.scroll&&(e.scroll=e.cursor),e.cursor>=e.scroll+n&&(e.scroll=e.cursor-n+1),e.scroll<0&&(e.scroll=0),process.stdout.write(he({lines:e.lines,cursor:e.cursor,scroll:e.scroll,pinned:e.pinned,scope:e.scope,profileLabel:e.profileLabel,rename:e.rename,inputText:e.inputText,inputFocused:e.inputFocused,chipDir:e.dirs[e.dirIndex]??s.cwd,confirmName:e.confirmClose===null?null:e.rows.find(o=>o.node_id===e.confirmClose)?.name??e.confirmClose,notice:e.notice,opening:e.opening!==null,help:e.help,loaded:e.loaded},r,j))},"paint");let v=!1,y=!1;const p=d(async()=>{if(v){y=!0;return}v=!0;try{const r=await m.liveRoots(e.scope==="profile"&&s.profileId!==void 0?{profile:s.profileId}:{});e.rows=r.rows,e.placed=pe(r.rows,e.placed),e.loaded=!0,g()}catch(r){u(`refresh failed: ${r.message}`)}finally{v=!1}l(),y&&(y=!1,p())},"poll");s.profileId!==void 0&&m.getProfile(s.profileId).then(r=>{e.profileLabel=r.name,l()}).catch(()=>{});const U=d(()=>e.lines[e.cursor],"current"),z=d(r=>{if(e.opening!==null||r.kind==="heading")return;const o=(r.kind==="root"?r.row:e.rows.find(_=>_.node_id===r.rootId)?.children.find(_=>_.node_id===r.id))?.name??r.id,t=r.kind==="root"&&r.row.asks>0?r.row.first_ask_ticket_id:null,i=F(K,`open-${Date.now()}`),T=`${["crtr","surface","node","focus",r.id,"--pane",f,"--in-place","--keep-other-viewers",...t===null?[]:["--open-ticket",t],"--json"].map(h).join(" ")} >${h(`${i}.out`)} 2>${h(`${i}.err`)}; echo $? >${h(`${i}.code`)}; mv ${h(`${i}.code`)} ${h(i)}`;if(!ce(T)){u(`could not open ${o}`),l();return}e.opening={file:i,name:o},l()},"open"),A=d(()=>{const r=e.opening;if(r===null||!Y(r.file))return;const n=d(a=>{try{return q(a,"utf8").trim()}catch{return""}},"read"),o=n(r.file),t=n(`${r.file}.out`),i=n(`${r.file}.err`);for(const a of[r.file,`${r.file}.out`,`${r.file}.err`])E(a,{force:!0});if(e.opening=null,o!=="0")u(i===""?`could not open ${r.name}`:i.split(`
2
- `)[0]);else{let a=!1;try{a=JSON.parse(t).focused===!0}catch{a=!1}a||u(`could not open ${r.name}`)}l(),p()},"finishOpen"),G=d(async()=>{const r=e.inputText.trim();if(r!==""){try{await m.createNode({root:!0,prompt:r,cwd:e.dirs[e.dirIndex]??s.cwd,...s.profileId===void 0?{}:{profile:s.profileId}}),e.inputText="",e.inputFocused=!1,p()}catch(n){u(`could not start: ${n.message}`)}l()}},"startConversation"),H=d(()=>{e.dirsLoaded||s.profileId===void 0||(e.dirsLoaded=!0,m.getProfile(s.profileId).then(r=>{e.dirs=[s.cwd,...r.projects.map(n=>n.path).filter(n=>n!==s.cwd)],l()}).catch(r=>{u(`could not list directories: ${r.message}`),l()}))},"loadDirs"),O=d(async r=>{e.confirmClose=null,x.add(r),g(),l();try{await m.closeNode(r,{finish:!0})}catch(n){u(`could not close: ${n.message}`)}finally{x.delete(r)}p(),l()},"doClose"),W=d(async()=>{const r=e.renameTarget,n=(e.rename??"").trim();if(e.rename=null,e.renameTarget=null,r!==null&&n!==""){try{await m.patchConfig(r,{name:n})}catch(o){u(`could not rename: ${o.message}`)}p()}l()},"commitRename"),X=d(r=>{e.pinned.has(r)?e.pinned.delete(r):e.pinned.add(r),ne(e.pinned),g()},"togglePin"),w=d(r=>{const n=M(e.lines,r);n<0||(e.cursor=n,e.cursorKey=P(e.lines[n]))},"moveTo"),B=d(r=>[...r].filter(n=>n>=" "&&n!=="\x7F").join(""),"printable"),N=d((r,n,o,t)=>{if(n.backspace)return t(o().slice(0,-1)),!0;if(n.ctrl||n.meta)return!0;const i=B(r);return i!==""&&t(o()+i),!0},"onText"),J=d((r,n)=>{const o={input:r,...n};if(e.help){e.help=!1,l();return}if(e.rename!==null){if(n.escape)e.rename=null,e.renameTarget=null;else if(n.return){W();return}else N(r,n,()=>e.rename??"",i=>{e.rename=i});l();return}if(e.inputFocused){if(c("crtr.dashboard.input-cancel",o))e.inputFocused=!1;else if(c("crtr.dashboard.input-submit",o)){G();return}else c("crtr.dashboard.directory-next",o)?e.dirIndex=(e.dirIndex+1)%e.dirs.length:c("crtr.dashboard.directory-previous",o)?e.dirIndex=(e.dirIndex-1+e.dirs.length)%e.dirs.length:N(r,n,()=>e.inputText,i=>{e.inputText=i});l();return}if(e.confirmClose!==null){const i=e.confirmClose;if(c("crtr.dashboard.confirm-close",o)){O(i);return}c("crtr.dashboard.cancel-close",o)&&(e.confirmClose=null,l());return}const t=U();if(c("crtr.dashboard.down",o))w(D(e.lines,e.cursor,1));else if(c("crtr.dashboard.up",o))w(D(e.lines,e.cursor,-1));else if(c("crtr.dashboard.first",o))w(0);else if(c("crtr.dashboard.last",o))w(e.lines.length-1);else if(c("crtr.dashboard.expand",o))t?.kind==="root"&&t.row.children.length>0&&(e.expanded.add(t.id),g());else if(c("crtr.dashboard.collapse",o)){if(t!==void 0&&t.kind!=="heading"){const i=b(t);e.expanded.delete(i),e.cursorKey=`root:${i}`,g()}}else if(c("crtr.dashboard.open",o)){t!==void 0&&z(t);return}else if(c("crtr.dashboard.pin",o)){const i=t===void 0?null:b(t);i!==null&&X(i)}else if(c("crtr.dashboard.rename",o)){const i=t===void 0?null:b(t);i!==null&&(e.renameTarget=i,e.rename=e.rows.find(a=>a.node_id===i)?.name??"")}else if(c("crtr.dashboard.close",o)){const i=t===void 0?null:b(t),a=i===null?void 0:e.rows.find(T=>T.node_id===i);if(a!==void 0)if(a.running_count>0)e.confirmClose=a.node_id;else{O(a.node_id);return}}else c("crtr.dashboard.profile",o)?s.profileId!==void 0&&(e.scope=e.scope==="profile"?"all":"profile",p()):c("crtr.dashboard.input",o)?(e.inputFocused=!0,H()):c("crtr.dashboard.help",o)&&(e.help=!0);l()},"onKey");de(),l(),p(),setInterval(()=>{p()},ge),setInterval(A,we),process.stdout.on("resize",l),await new Promise(()=>{process.stdin.on("data",r=>{try{const{input:n,key:o}=te(r);J(n,o)}catch(n){u(`internal error: ${n.message}`),l()}})})}d(Oe,"runDashboard");export{Oe as runDashboard};
1
+ var Q=Object.defineProperty;var d=(s,f)=>Q(s,"name",{value:f,configurable:!0});import{mkdtempSync as V,readFileSync as q,rmSync as E,existsSync as Y}from"node:fs";import{tmpdir as Z}from"node:os";import{join as F}from"node:path";import{cliClient as ee}from"../../commands/api-client.js";import{loadPins as re,savePins as ne}from"../../core/canvas/browse/pins.js";import{detectColorCaps as oe}from"../../core/tui/draw.js";import{getTerminalSize as ie,parseKeypress as te,restoreTerminal as se,setupTerminal as de}from"../../core/tui/terminal.js";import{getPaneOption as le,runShellInBackground as ce,setPaneOption as C,shellQuote as h}from"../../core/runtime/placement-tmux.js";import{matchesTerminalInput as ae,resolveUserKeybindings as fe}from"../../core/keybindings/index.js";import{VIEWER_OWNER_OPTION as S}from"../attach/session/pane-tag.js";import{buildLines as ue,nearestSelectable as M,placements as pe,rootOf as b,stepSelectable as D}from"./model.js";import{listHeight as me,renderDashboard as he}from"./render.js";const ge=2e3,we=250,be=6e3;function P(s){return s.kind==="heading"?null:s.kind==="root"?`root:${s.id}`:`child:${s.rootId}:${s.id}`}d(P,"lineKey");async function Oe(s){const f=process.env.TMUX_PANE;if(f===void 0||f==="")throw new Error("the dashboard runs in a tmux pane (TMUX_PANE is unset)");const m=ee(),R=fe(),j=oe(),c=d((r,n)=>ae(R,r,n),"matches"),k=String(process.pid);C(f,S,k);for(const r of["@crtr_node","@crtr_attach","@crtr_viewer_at"])C(f,r,"");const K=V(F(Z(),"crtr-dashboard-"));let x=!1;const L=d(()=>{if(!x){x=!0;try{se()}catch{}le(f,S)===k&&C(f,S,"");try{E(K,{recursive:!0,force:!0})}catch{}}},"cleanup");process.once("exit",L);for(const r of["SIGTERM","SIGHUP"])process.once(r,()=>{L(),process.exit(0)});const e={rows:[],placed:new Map,pinned:re(),expanded:new Set,lines:[],cursorKey:null,cursor:0,scroll:0,scope:s.profileId===void 0?"all":"profile",profileLabel:s.profileId??"",loaded:!1,help:!1,inputFocused:!1,inputText:"",dirs:[s.cwd],dirIndex:0,dirsLoaded:!1,rename:null,renameTarget:null,confirmClose:null,notice:null,opening:null},I=new Set;let v;const u=d(r=>{e.notice=r,v!==void 0&&clearTimeout(v),v=setTimeout(()=>{e.notice=null,l()},be)},"notify"),g=d(()=>{const r=e.rows.filter(t=>!I.has(t.node_id));e.lines=ue(r,e.placed,e.pinned,e.expanded);const n=e.cursorKey===null?-1:e.lines.findIndex(t=>P(t)===e.cursorKey);e.cursor=n>=0?n:M(e.lines,e.cursor),e.cursor<0&&(e.cursor=0);const o=e.lines[e.cursor];e.cursorKey=o===void 0?null:P(o)},"rebuild"),l=d(()=>{if(x)return;const r=ie(),n=me(r.rows)-1;e.cursor<e.scroll&&(e.scroll=e.cursor),e.cursor>=e.scroll+n&&(e.scroll=e.cursor-n+1),e.scroll<0&&(e.scroll=0),process.stdout.write(he({lines:e.lines,cursor:e.cursor,scroll:e.scroll,pinned:e.pinned,scope:e.scope,profileLabel:e.profileLabel,rename:e.rename,inputText:e.inputText,inputFocused:e.inputFocused,chipDir:e.dirs[e.dirIndex]??s.cwd,confirmName:e.confirmClose===null?null:e.rows.find(o=>o.node_id===e.confirmClose)?.name??e.confirmClose,notice:e.notice,opening:e.opening!==null,help:e.help,loaded:e.loaded},r,j))},"paint");let $=!1,y=!1;const p=d(async()=>{if($){y=!0;return}$=!0;try{const r=await m.liveRoots(e.scope==="profile"&&s.profileId!==void 0?{profile:s.profileId}:{});e.rows=r.rows,e.placed=pe(r.rows,e.placed),e.loaded=!0,g()}catch(r){u(`refresh failed: ${r.message}`)}finally{$=!1}l(),y&&(y=!1,p())},"poll");s.profileId!==void 0&&m.getProfile(s.profileId).then(r=>{e.profileLabel=r.name,l()}).catch(()=>{});const U=d(()=>e.lines[e.cursor],"current"),z=d(r=>{if(e.opening!==null||r.kind==="heading")return;const o=(r.kind==="root"?r.row:e.rows.find(_=>_.node_id===r.rootId)?.children.find(_=>_.node_id===r.id))?.name??r.id,t=r.kind==="root"&&r.row.asks>0?r.row.first_ask_ticket_id:null,i=F(K,`open-${Date.now()}`),T=`{ ${["crtr","surface","node","focus",r.id,"--pane",f,"--in-place","--keep-other-viewers",...t===null?[]:["--open-ticket",t],"--json"].map(h).join(" ")} >${h(`${i}.out`)} 2>${h(`${i}.err`)}; echo $? >${h(`${i}.code`)} && mv ${h(`${i}.code`)} ${h(i)}; } 2>/dev/null; exit 0`;if(!ce(T)){u(`could not open ${o}`),l();return}e.opening={file:i,name:o},l()},"open"),A=d(()=>{const r=e.opening;if(r===null||!Y(r.file))return;const n=d(a=>{try{return q(a,"utf8").trim()}catch{return""}},"read"),o=n(r.file),t=n(`${r.file}.out`),i=n(`${r.file}.err`);for(const a of[r.file,`${r.file}.out`,`${r.file}.err`])E(a,{force:!0});if(e.opening=null,o!=="0")u(i===""?`could not open ${r.name}`:i.split(`
2
+ `)[0]);else{let a=!1;try{a=JSON.parse(t).focused===!0}catch{a=!1}a||u(`could not open ${r.name}`)}l(),p()},"finishOpen"),G=d(async()=>{const r=e.inputText.trim();if(r!==""){try{await m.createNode({root:!0,prompt:r,cwd:e.dirs[e.dirIndex]??s.cwd,...s.profileId===void 0?{}:{profile:s.profileId}}),e.inputText="",e.inputFocused=!1,p()}catch(n){u(`could not start: ${n.message}`)}l()}},"startConversation"),H=d(()=>{e.dirsLoaded||s.profileId===void 0||(e.dirsLoaded=!0,m.getProfile(s.profileId).then(r=>{e.dirs=[s.cwd,...r.projects.map(n=>n.path).filter(n=>n!==s.cwd)],l()}).catch(r=>{u(`could not list directories: ${r.message}`),l()}))},"loadDirs"),O=d(async r=>{e.confirmClose=null,I.add(r),g(),l();try{await m.closeNode(r,{finish:!0})}catch(n){u(`could not close: ${n.message}`)}finally{I.delete(r)}p(),l()},"doClose"),W=d(async()=>{const r=e.renameTarget,n=(e.rename??"").trim();if(e.rename=null,e.renameTarget=null,r!==null&&n!==""){try{await m.patchConfig(r,{name:n})}catch(o){u(`could not rename: ${o.message}`)}p()}l()},"commitRename"),X=d(r=>{e.pinned.has(r)?e.pinned.delete(r):e.pinned.add(r),ne(e.pinned),g()},"togglePin"),w=d(r=>{const n=M(e.lines,r);n<0||(e.cursor=n,e.cursorKey=P(e.lines[n]))},"moveTo"),B=d(r=>[...r].filter(n=>n>=" "&&n!=="\x7F").join(""),"printable"),N=d((r,n,o,t)=>{if(n.backspace)return t(o().slice(0,-1)),!0;if(n.ctrl||n.meta)return!0;const i=B(r);return i!==""&&t(o()+i),!0},"onText"),J=d((r,n)=>{const o={input:r,...n};if(e.help){e.help=!1,l();return}if(e.rename!==null){if(n.escape)e.rename=null,e.renameTarget=null;else if(n.return){W();return}else N(r,n,()=>e.rename??"",i=>{e.rename=i});l();return}if(e.inputFocused){if(c("crtr.dashboard.input-cancel",o))e.inputFocused=!1;else if(c("crtr.dashboard.input-submit",o)){G();return}else c("crtr.dashboard.directory-next",o)?e.dirIndex=(e.dirIndex+1)%e.dirs.length:c("crtr.dashboard.directory-previous",o)?e.dirIndex=(e.dirIndex-1+e.dirs.length)%e.dirs.length:N(r,n,()=>e.inputText,i=>{e.inputText=i});l();return}if(e.confirmClose!==null){const i=e.confirmClose;if(c("crtr.dashboard.confirm-close",o)){O(i);return}c("crtr.dashboard.cancel-close",o)&&(e.confirmClose=null,l());return}const t=U();if(c("crtr.dashboard.down",o))w(D(e.lines,e.cursor,1));else if(c("crtr.dashboard.up",o))w(D(e.lines,e.cursor,-1));else if(c("crtr.dashboard.first",o))w(0);else if(c("crtr.dashboard.last",o))w(e.lines.length-1);else if(c("crtr.dashboard.expand",o))t?.kind==="root"&&t.row.children.length>0&&(e.expanded.add(t.id),g());else if(c("crtr.dashboard.collapse",o)){if(t!==void 0&&t.kind!=="heading"){const i=b(t);e.expanded.delete(i),e.cursorKey=`root:${i}`,g()}}else if(c("crtr.dashboard.open",o)){t!==void 0&&z(t);return}else if(c("crtr.dashboard.pin",o)){const i=t===void 0?null:b(t);i!==null&&X(i)}else if(c("crtr.dashboard.rename",o)){const i=t===void 0?null:b(t);i!==null&&(e.renameTarget=i,e.rename=e.rows.find(a=>a.node_id===i)?.name??"")}else if(c("crtr.dashboard.close",o)){const i=t===void 0?null:b(t),a=i===null?void 0:e.rows.find(T=>T.node_id===i);if(a!==void 0)if(a.running_count>0)e.confirmClose=a.node_id;else{O(a.node_id);return}}else c("crtr.dashboard.profile",o)?s.profileId!==void 0&&(e.scope=e.scope==="profile"?"all":"profile",p()):c("crtr.dashboard.input",o)?(e.inputFocused=!0,H()):c("crtr.dashboard.help",o)&&(e.help=!0);l()},"onKey");de(),l(),p(),setInterval(()=>{p()},ge),setInterval(A,we),process.stdout.on("resize",l),await new Promise(()=>{process.stdin.on("data",r=>{try{const{input:n,key:o}=te(r);J(n,o)}catch(n){u(`internal error: ${n.message}`),l()}})})}d(Oe,"runDashboard");export{Oe as runDashboard};
@@ -1,3 +1,3 @@
1
- var E=Object.defineProperty;var s=(t,a)=>E(t,"name",{value:a,configurable:!0});import{defineLeaf as _}from"../core/command.js";import{envProfileId as p}from"../shared/env.js";import{getDefaultProfileId as f}from"../core/profiles/default-binding.js";import{SECTION_ORDER as k,SECTION_TITLE as x,sectionOf as R}from"../clients/dashboard/model.js";import{closeWindow as I,currentTmux as T,displayMessage as v,getPaneOption as P,openNodeWindow as N,paneCurrentCommand as C,paneCurrentPath as D,paneLocation as O,paneStartCommand as U,renameWindow as W,respawnPaneSync as L,shellQuote as S,swapPanes as A,viewerSplitEnv as M,windowOfPane as X}from"../core/runtime/placement-tmux.js";import{cliClient as $}from"./api-client.js";const h="CRTR_DASHBOARD_RUN";async function j(t,a,e){if(t!==void 0&&t!=="")return t;const o=a===void 0?void 0:P(a,"@crtr_node");if(o!==void 0&&o!=="")try{const r=(await $().getNode(o)).profile_id;if(r!=null)return r}catch{}return p()??f(e)??void 0}s(j,"profileFor");async function q(t){const a=await $().liveRoots(t===void 0?{}:{profile:t}),e=[];for(const o of k){const r=a.rows.filter(n=>R(n)===o);if(r.length!==0){e.push(`${x[o]} (${r.length})`);for(const n of r){const i=[n.running_count>0?`\u27F3 ${n.running_count}`:"",n.asks>0?`? ${n.asks}`:"",n.fault!==null?"\u26A0":""].filter(d=>d!=="").join(" ");e.push(` ${n.name} ${n.state_line??n.goal??""}${i===""?"":` ${i}`}`)}}}process.stdout.write(`${e.length===0?"no live conversations":e.join(`
1
+ var R=Object.defineProperty;var i=(r,s)=>R(r,"name",{value:s,configurable:!0});import{defineLeaf as N}from"../core/command.js";import{envProfileId as f}from"../shared/env.js";import{getDefaultProfileId as l}from"../core/profiles/default-binding.js";import{SECTION_ORDER as O,SECTION_TITLE as P,sectionOf as T}from"../clients/dashboard/model.js";import{closeWindow as I,currentTmux as C,displayMessage as w,getPaneOption as m,openNodeWindow as D,paneCurrentCommand as U,paneCurrentPath as W,paneLocation as A,paneStartCommand as L,renameWindow as S,respawnPaneSync as M,setPaneOption as X,shellQuote as j,swapPanes as q,viewerSplitEnv as F,windowOfPane as V}from"../core/runtime/placement-tmux.js";import{cliClient as g}from"./api-client.js";const b="CRTR_DASHBOARD_RUN",x="@crtr_dashboard_return";async function B(r,s,e){if(r!==void 0&&r!=="")return r;const n=s===void 0?void 0:m(s,"@crtr_node");if(n!==void 0&&n!=="")try{const a=(await g().getNode(n)).profile_id;if(a!=null)return a}catch{}return f()??l(e)??void 0}i(B,"profileFor");async function H(r){const s=await g().liveRoots(r===void 0?{}:{profile:r}),e=[];for(const n of O){const a=s.rows.filter(o=>T(o)===n);if(a.length!==0){e.push(`${P[n]} (${a.length})`);for(const o of a){const d=[o.running_count>0?`\u27F3 ${o.running_count}`:"",o.asks>0?`? ${o.asks}`:"",o.fault!==null?"\u26A0":""].filter(c=>c!=="").join(" ");e.push(` ${o.name} ${o.state_line??o.goal??""}${d===""?"":` ${d}`}`)}}}process.stdout.write(`${e.length===0?"no live conversations":e.join(`
2
2
  `)}
3
- `)}s(q,"printListing");const G=_({name:"dashboard",description:"show the profile\u2019s live root conversations in a tmux pane \u2014 which need the person, which run, which are idle",whenToUse:"the person asks to see all their running conversations in one place, or you are wiring a key to it. It is a screen for the person, not a read: use `canvas dashboard` for a one-shot tree you can pipe",help:{name:"surface dashboard",summary:"put the dashboard in a tmux pane: live root conversations in Waiting for you / Running / Idle, Enter opens one in that pane, n starts a new background conversation. Outside tmux it prints the same listing as text",params:[{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane that shows the dashboard. Default: $TMUX_PANE. A pane running a crouter viewer or a dashboard is respawned; any other pane (a shell, an editor) is swapped for a new window that runs the dashboard, and its program keeps running in the background window."},{kind:"flag",name:"profile-id",type:"string",required:!1,constraint:"Exact profile id to list. Default: the profile of the conversation shown in the pane, then $CRTR_PROFILE_ID, then the pane directory\u2019s pinned default profile; absent means all profiles."}],output:[],outputKind:"object",effects:["Respawns the target pane onto the dashboard (a crouter viewer\u2019s engine keeps running), or opens a new tmux window with the dashboard and swaps it into the pane\u2019s place.","Never kills a pane that is not running a crouter viewer or dashboard.","Inside the dashboard, Enter runs `surface node focus` for the chosen conversation, `n` creates a root conversation, `x` finishes one."]},run:s(async t=>{const a=t.profileId;if(process.env[h]==="1"){delete process.env[h];const{runDashboard:c}=await import("../clients/dashboard/app.js"),y=a??p()??f(process.cwd())??void 0;await c({profileId:y,cwd:process.cwd()});return}if(process.env.TMUX===void 0||process.env.TMUX===""){await q(a??p()??f(process.cwd())??void 0);return}const e=t.pane??process.env.TMUX_PANE??T()?.pane;if(e===void 0||e==="")throw v("crtr: no pane to show the dashboard in"),new Error("no tmux pane to show the dashboard in \u2014 pass --pane <id>");const o=s(c=>{throw v(`crtr: ${c}`,e),new Error(c)},"fail"),r=U(e)?.replace(/^"|"$/g,"");if(r===void 0)return o(`pane ${e} is not reachable`);if(r.startsWith("crtr surface dashboard"))return;const n=D(e)??process.cwd(),i=await j(a,e,n),d=`crtr surface dashboard${i===void 0?"":` --profile-id ${S(i)}`}`,l={...M(),[h]:"1"};if(r.startsWith("crtr surface attach to ")){L({pane:e,cwd:n,env:l,command:d})||o(`tmux could not respawn pane ${e}`);return}const w=O(e);if(w===null)return o(`pane ${e} is not in a tmux window`);const u=N({session:w.session,name:"dashboard",cwd:n,env:l,command:d});if(u===null)return o("tmux could not open a window for the dashboard");const m=C(e),g=A(u.pane,e);if(g!==null)return I(u.window),o(`could not show the dashboard: ${g}`);const b=X(e);b!==null&&m!==void 0&&W(b,m)},"run"),render:s(()=>"","render")});export{G as surfaceDashboardLeaf};
3
+ `)}i(H,"printListing");const ne=N({name:"dashboard",description:"show the profile\u2019s live root conversations in a tmux pane \u2014 which need the person, which run, which are idle",whenToUse:"the person asks to see all their running conversations in one place, or you are wiring a key to it. It is a screen for the person, not a read: use `canvas dashboard` for a one-shot tree you can pipe",help:{name:"surface dashboard",summary:"put the dashboard in a tmux pane: live root conversations in Waiting for you / Running / Idle, Enter opens one in that pane, n starts a new background conversation. Outside tmux it prints the same listing as text",params:[{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane that shows the dashboard. Default: $TMUX_PANE. A pane running a crouter viewer is respawned; any other pane (a shell, an editor) is swapped for a new window that runs the dashboard, and its program keeps running in the background window. A pane already showing the dashboard returns to the conversation it showed before."},{kind:"flag",name:"profile-id",type:"string",required:!1,constraint:"Exact profile id to list. Default: the profile of the conversation shown in the pane, then $CRTR_PROFILE_ID, then the pane directory\u2019s pinned default profile; absent means all profiles."}],output:[],outputKind:"object",effects:["Respawns the target pane onto the dashboard (a crouter viewer\u2019s engine keeps running), or opens a new tmux window with the dashboard and swaps it into the pane\u2019s place.","On a pane already showing the dashboard: opens the conversation that pane showed before the dashboard, in that pane; with none recorded, shows a tmux message and changes nothing.","Never kills a pane that is not running a crouter viewer or dashboard.","Inside the dashboard, Enter runs `surface node focus` for the chosen conversation, `n` creates a root conversation, `x` finishes one."]},run:i(async r=>{const s=r.profileId;if(process.env[b]==="1"){delete process.env[b];const{runDashboard:t}=await import("../clients/dashboard/app.js"),u=s??f()??l(process.cwd())??void 0;await t({profileId:u,cwd:process.cwd()});return}if(process.env.TMUX===void 0||process.env.TMUX===""){await H(s??f()??l(process.cwd())??void 0);return}const e=r.pane??process.env.TMUX_PANE??C()?.pane;if(e===void 0||e==="")throw w("crtr: no pane to show the dashboard in"),new Error("no tmux pane to show the dashboard in \u2014 pass --pane <id>");const n=i(t=>{throw w(`crtr: ${t}`,e),new Error(t)},"fail"),a=L(e)?.replace(/^"|"$/g,"");if(a===void 0)return n(`pane ${e} is not reachable`);if(a.startsWith("crtr surface dashboard")){const t=m(e,x);if(t===void 0||t===""){w("crtr: no conversation to return to from this dashboard",e);return}const u=g(),p=await u.getNode(t).catch(()=>null);if(p===null)return n("the conversation this pane showed before no longer exists");const{focusNode:E}=await import("../core/runtime/focus-viewer.js");(await E(u,p,{pane:e,inPlace:!0,keepOtherViewers:!0})).focused||n(`could not return to ${p.name}`);return}const o=W(e)??process.cwd(),d=await B(s,e,o),c=`crtr surface dashboard${d===void 0?"":` --profile-id ${j(d)}`}`,v={...F(),[b]:"1"};if(a.startsWith("crtr surface attach to ")){const t=m(e,"@crtr_node");t!==void 0&&t!==""&&X(e,x,t),M({pane:e,cwd:o,env:v,command:c})||n(`tmux could not respawn pane ${e}`);return}const $=A(e);if($===null)return n(`pane ${e} is not in a tmux window`);const h=D({session:$.session,name:"dashboard",cwd:o,env:v,command:c});if(h===null)return n("tmux could not open a window for the dashboard");const _=U(e),y=q(h.pane,e);if(y!==null)return I(h.window),n(`could not show the dashboard: ${y}`);const k=V(e);k!==null&&_!==void 0&&S(k,_)},"run"),render:i(()=>"","render")});export{ne as surfaceDashboardLeaf};
@@ -33,3 +33,7 @@ export interface ResolveContext {
33
33
  readable: (o: ObjectRow) => boolean;
34
34
  }
35
35
  export declare function resolveName(db: DatabaseSync, reader: Reader, raw: string, context?: ResolveContext): Resolution;
36
+ /** The stub of another machine's object that `raw` (`<owner handle>/<name>`, `<type>:<owner handle>/<name>` or a bare name) names, or null.
37
+ * `resolveName` never answers a stub, so a stub cannot shadow a local object; this is the lookup a forwarding handler makes only after
38
+ * `resolveName` found no local object, to learn that the name belongs to an object another machine homes. */
39
+ export declare function stubsNamed(db: DatabaseSync, reader: Reader, raw: string): ObjectRow[];
@@ -1,3 +1,3 @@
1
- var j=Object.defineProperty;var m=(t,e)=>j(t,"name",{value:e,configurable:!0});import{prepared as R}from"../sqlite-statements.js";import{isSafeNodeId as I}from"../canvas/paths.js";import{canRead as y,toolApps as g}from"./access.js";import{getById as T}from"./objects.js";function _(t,e){const s=R(t,"SELECT * FROM objects WHERE type = 'person' AND name = 'user' AND home_machine IS NULL").get(),l=g(t,e),o=e.class==="node"?[e.node,...e.repos,...e.projectRepos,e.profile,e.app]:e.class==="app"?[e.app]:e.class==="object"?[e.owner,...e.repos,e.app]:[],a=new Set;return[...o,s,...l].filter(r=>!!r&&!a.has(r.rowid)&&!!a.add(r.rowid))}m(_,"viewOf");function E(t,e,s,l){return t.prepare(`SELECT * FROM objects WHERE (type = 'document' OR instr(type, '.') > 0) AND owner = ? AND name = ? AND home_machine IS NULL
2
- AND deleted_at IS ${l?"NOT NULL ORDER BY deleted_at DESC":"NULL"}`).all(e.rowid,s)}m(E,"namedObjects");const F=["person","app","repo","profile"],h=/^(person|app|repo|profile|node):([^/]+)\/(.+)$/;function x(t,e,s){return s==="node"?R(t,"SELECT * FROM objects WHERE type = 'node' AND id = ?").all(e):s?R(t,"SELECT * FROM objects WHERE type = ? AND (name = ? OR id = ?) AND home_machine IS NULL").all(s,e,e):R(t,`SELECT * FROM objects WHERE (type = 'node' AND id = ?)
3
- OR (type IN ('person','app','repo','profile') AND (name = ? OR id = ?) AND home_machine IS NULL)`).all(e,e,e)}m(x,"ownersByHandle");function W(t,e,s,l){let o=s.trim().replace(/^\[\[(.*)\]\]$/,"$1").trim();const a=/^([^:/]+):reports\/(.+?)(?:\.md)?$/.exec(o);a&&!h.test(o)&&(o=`${a[1]}/reports/${a[2]}`);const r=l?.readable??(c=>y(t,e,c)),u=T(t,o);if(u&&r(u))return u.deleted_at?{deleted:u}:{object:u};const L=l?.view??_(t,e);for(const c of L){const i=E(t,c,o,!1).find(r);if(i)return{object:i}}const p=h.exec(o),w=o.indexOf("/");if(p||w>0){const c=p?p[2]:o.slice(0,w),i=p?p[3]:o.slice(w+1),d=x(t,c,p?.[1]),D=m(n=>{const f=L.findIndex(O=>O.rowid===n.rowid);return f<0?1/0:f},"rank"),A=d.filter(n=>D(n)<1/0).sort((n,f)=>D(n)-D(f));for(const n of A){const f=E(t,n,i,!1).find(r);if(f)return{object:f}}const N=d.flatMap(n=>E(t,n,i,!1)).filter(r);if(N.length===1)return{object:N[0]};if(N.length>1)return{ambiguous:N};const S=d.flatMap(n=>E(t,n,i,!0)).find(r);return S?{deleted:S}:!p&&d.length===0&&I(c)?{remote:o}:{missing:!0}}for(const c of L){const i=E(t,c,o,!0).find(r);if(i)return{deleted:i}}return{missing:!0}}m(W,"resolveName");export{F as HANDLE_TYPES,x as ownersByHandle,W as resolveName,_ as viewOf};
1
+ var g=Object.defineProperty;var m=(t,e)=>g(t,"name",{value:e,configurable:!0});import{prepared as R}from"../sqlite-statements.js";import{isSafeNodeId as x}from"../canvas/paths.js";import{canRead as S,toolApps as $}from"./access.js";import{getById as T}from"./objects.js";function A(t,e){const i=R(t,"SELECT * FROM objects WHERE type = 'person' AND name = 'user' AND home_machine IS NULL").get(),s=$(t,e),o=e.class==="node"?[e.node,...e.repos,...e.projectRepos,e.profile,e.app]:e.class==="app"?[e.app]:e.class==="object"?[e.owner,...e.repos,e.app]:[],p=new Set;return[...o,i,...s].filter(r=>!!r&&!p.has(r.rowid)&&!!p.add(r.rowid))}m(A,"viewOf");function E(t,e,i,s,o=!1){return t.prepare(`SELECT * FROM objects WHERE (type = 'document' OR instr(type, '.') > 0) AND owner = ? AND name = ? AND home_machine IS ${o?"NOT NULL":"NULL"}
2
+ AND deleted_at IS ${s?"NOT NULL ORDER BY deleted_at DESC":"NULL"}`).all(e.rowid,i)}m(E,"namedObjects");const v=["person","app","repo","profile"],D=/^(person|app|repo|profile|node):([^/]+)\/(.+)$/;function j(t,e,i){return i==="node"?R(t,"SELECT * FROM objects WHERE type = 'node' AND id = ?").all(e):i?R(t,"SELECT * FROM objects WHERE type = ? AND (name = ? OR id = ?) AND home_machine IS NULL").all(i,e,e):R(t,`SELECT * FROM objects WHERE (type = 'node' AND id = ?)
3
+ OR (type IN ('person','app','repo','profile') AND (name = ? OR id = ?) AND home_machine IS NULL)`).all(e,e,e)}m(j,"ownersByHandle");function F(t,e,i,s){let o=i.trim().replace(/^\[\[(.*)\]\]$/,"$1").trim();const p=/^([^:/]+):reports\/(.+?)(?:\.md)?$/.exec(o);p&&!D.test(o)&&(o=`${p[1]}/reports/${p[2]}`);const r=s?.readable??(f=>S(t,e,f)),a=T(t,o);if(a&&r(a))return a.deleted_at?{deleted:a}:{object:a};const N=s?.view??A(t,e);for(const f of N){const n=E(t,f,o,!1).find(r);if(n)return{object:n}}const l=D.exec(o),d=o.indexOf("/");if(l||d>0){const f=l?l[2]:o.slice(0,d),n=l?l[3]:o.slice(d+1),w=j(t,f,l?.[1]),h=m(c=>{const u=N.findIndex(I=>I.rowid===c.rowid);return u<0?1/0:u},"rank"),y=w.filter(c=>h(c)<1/0).sort((c,u)=>h(c)-h(u));for(const c of y){const u=E(t,c,n,!1).find(r);if(u)return{object:u}}const L=w.flatMap(c=>E(t,c,n,!1)).filter(r);if(L.length===1)return{object:L[0]};if(L.length>1)return{ambiguous:L};const O=w.flatMap(c=>E(t,c,n,!0)).find(r);return O?{deleted:O}:!l&&w.length===0&&x(f)?{remote:o}:{missing:!0}}for(const f of N){const n=E(t,f,o,!0).find(r);if(n)return{deleted:n}}return{missing:!0}}m(F,"resolveName");function W(t,e,i){const s=i.trim().replace(/^\[\[(.*)\]\]$/,"$1").trim(),o=D.exec(s),p=s.indexOf("/"),r=A(t,e),a=m((d,f)=>d.flatMap(n=>E(t,n,f,!1,!0)).filter(n=>S(t,e,n)),"found"),N=a(r,s);if(N.length)return N;if(!o&&p<=0)return[];const l=j(t,o?o[2]:s.slice(0,p),o?.[1]);return a(l,o?o[3]:s.slice(p+1))}m(W,"stubsNamed");export{v as HANDLE_TYPES,j as ownersByHandle,F as resolveName,W as stubsNamed,A as viewOf};
@@ -1,3 +1,3 @@
1
- var ye=Object.defineProperty;var u=(e,t)=>ye(e,"name",{value:t,configurable:!0});import{randomUUID as ge}from"node:crypto";import{streamSimple as be}from"@earendil-works/pi-ai/compat";import{expandModelCandidates as re,parseModelSpec as se,probeRouteAvailability as ve}from"../model-routes.js";import{readMergedLaunchConfig as ie}from"../config.js";import{openModelRegistry as we}from"../runtime/model-registry.js";import{coolingUntilFor as ae,isManagedProvider as H,markSubscriptionRateLimited as xe,markSubscriptionSuccess as ke,readSubscriptionPool as je,retryAfterMsFromHeaders as K,retryAfterMsFromError as qe,refreshSubscriptionCredential as Ae,subscriptionNeedsSuccessWrite as Me}from"../subscription-state.js";import{classifyRateLimit as $e,describeRateLimitScope as de,rateLimitHeaders as Re}from"../rate-limit-scope.js";import{RunError as s}from"../runs/operations.js";import{recognizeProviderError as le}from"../provider-error-recognition.js";import{isSafetyRefusal as Ie}from"./refusal.js";import{assertUnderCap as Se,recordModelUsage as ce}from"../usage/ledger.js";import{outboundToken as Ce}from"../providers/mint.js";import{discardFile as Le}from"../providers/token-cache.js";import{ProviderError as Oe}from"../providers/errors.js";import{appCandidates as Fe}from"./app-rungs.js";function Ne(e){return typeof e=="string"||e===null||Array.isArray(e)&&e.every(t=>t&&typeof t=="object"&&(t.type==="text"&&typeof t.text=="string"||t.type==="image_url"&&typeof t.image_url?.url=="string"&&/^data:image\/(png|jpeg|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/.test(t.image_url.url)))}u(Ne,"validContent");function ue(e){return Array.isArray(e)?e.map(t=>{if(t.type==="text")return t;const[,n,r]=/^data:(image\/(?:png|jpeg|gif|webp));base64,(.*)$/.exec(t.image_url.url);return{type:"image",mimeType:n,data:r}}):e??""}u(ue,"imageContent");function rt(e){if(!e||typeof e!="object"||Array.isArray(e))throw new s(400,"invalid_request","body must be an object");const t=e;if(!Array.isArray(t.messages)||!t.messages.every(n=>n&&typeof n=="object"&&["system","user","assistant","tool"].includes(n.role)&&Ne(n.content)))throw new s(400,"invalid_request","messages must be chat messages","messages");if(t.messages.some(n=>["system","assistant"].includes(n.role)&&typeof n.content!="string"&&n.content!==null))throw new s(400,"invalid_request","system and assistant content must be text","messages");if(t.model!==void 0&&typeof t.model!="string")throw new s(400,"invalid_request","model must be a string","model");if(t.model_policy!==void 0&&!["exact","substitute"].includes(t.model_policy))throw new s(400,"invalid_request","invalid model_policy","model_policy");if(t.payer!==void 0&&!["user","app"].includes(t.payer))throw new s(400,"invalid_request","invalid payer","payer");if(t.reasoning_effort!==void 0&&!["minimal","low","medium","high","xhigh","max"].includes(t.reasoning_effort))throw new s(400,"invalid_request","invalid reasoning_effort","reasoning_effort");if(t.stream!==void 0&&typeof t.stream!="boolean")throw new s(400,"invalid_request","stream must be boolean","stream");if(t.tools!==void 0&&(!Array.isArray(t.tools)||!t.tools.every(n=>n?.type==="function"&&typeof n.function?.name=="string"&&n.function.name.length>0&&(n.function.description===void 0||typeof n.function.description=="string")&&(n.function.parameters===void 0||n.function.parameters!==null&&typeof n.function.parameters=="object"&&!Array.isArray(n.function.parameters)&&n.function.parameters.type==="object"))))throw new s(400,"invalid_request","invalid tools","tools");if(t.tool_choice!==void 0&&t.tool_choice!=="auto"&&t.tool_choice!=="none"&&t.tool_choice!=="required"&&(typeof t.tool_choice!="object"||t.tool_choice===null||t.tool_choice.type!=="function"||typeof t.tool_choice.function?.name!="string"))throw new s(400,"invalid_request","invalid tool_choice","tool_choice");if(t.messages.some(n=>n.role==="assistant"&&n.tool_calls!==void 0&&(!Array.isArray(n.tool_calls)||!n.tool_calls.every(r=>r&&typeof r.id=="string"&&typeof r.function?.name=="string"&&typeof r.function.arguments=="string"&&De(r.function.arguments)))))throw new s(400,"invalid_request","invalid assistant tool_calls","messages");if(t.stop!==void 0&&!(typeof t.stop=="string"||Array.isArray(t.stop)&&t.stop.every(n=>typeof n=="string")))throw new s(400,"invalid_request","invalid stop","stop");if(t.seed!==void 0&&!Number.isSafeInteger(t.seed))throw new s(400,"invalid_request","invalid seed","seed");for(const[n,r]of[["temperature",t.temperature],["top_p",t.top_p],["max_tokens",t.max_tokens]])if(r!==void 0&&(typeof r!="number"||!Number.isFinite(r)||n==="temperature"&&(r<0||r>2)||n==="top_p"&&(r<0||r>1)||n==="max_tokens"&&(!Number.isSafeInteger(r)||r<1)))throw new s(400,"invalid_request",`invalid ${n}`,n);if(t.response_format!==void 0){const n=t.response_format;if(!n||typeof n!="object"||Array.isArray(n)||!["text","json_object","json_schema"].includes(n.type)||n.type==="json_schema"&&(typeof n.json_schema?.name!="string"||!n.json_schema.name||!n.json_schema.schema||typeof n.json_schema.schema!="object"||Array.isArray(n.json_schema.schema)))throw new s(400,"invalid_request","invalid response_format","response_format")}if(t.metadata!==void 0&&Buffer.byteLength(JSON.stringify(t.metadata)??"")>4096)throw new s(400,"invalid_request","metadata exceeds 4 KB","metadata");return t}u(rt,"parseLlmRequest");function De(e){try{const t=JSON.parse(e);return t!==null&&typeof t=="object"&&!Array.isArray(t)}catch{return!1}}u(De,"isJsonObject");function Pe(e){const t=Date.now(),n=e.messages.map(i=>i.role==="system"?{role:"system",content:i.content??"",timestamp:t}:i.role==="tool"?{role:"toolResult",toolCallId:i.tool_call_id??"",toolName:"",content:typeof i.content=="string"||i.content===null?[{type:"text",text:i.content??""}]:ue(i.content),isError:!1,timestamp:t}:i.role==="assistant"?{role:"assistant",content:[...i.content?[{type:"text",text:i.content}]:[],...(i.tool_calls??[]).map(g=>({type:"toolCall",id:g.id,name:g.function.name,arguments:JSON.parse(g.function.arguments)}))],api:"openai-completions",provider:"openai",model:e.model??"",usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:t}:{role:"user",content:ue(i.content),timestamp:t}),r=n.findIndex(i=>i.role!=="system"),w=n.slice(0,r<0?n.length:r).map(i=>i.content),y=w.join(`
1
+ var ge=Object.defineProperty;var u=(e,t)=>ge(e,"name",{value:t,configurable:!0});import{randomUUID as be}from"node:crypto";import{streamSimple as ve}from"@earendil-works/pi-ai/compat";import{expandModelCandidates as re,parseModelSpec as se,probeRouteAvailability as we}from"../model-routes.js";import{readMergedLaunchConfig as ie}from"../config.js";import{openModelRegistry as xe}from"../runtime/model-registry.js";import{MAX_RATE_LIMIT_COOLDOWN_MS as ae,coolingUntilFor as de,isManagedProvider as H,markSubscriptionRateLimited as ke,markSubscriptionSuccess as je,readSubscriptionPool as Me,retryAfterMsFromHeaders as K,retryAfterMsFromError as qe,refreshSubscriptionCredential as Ae,subscriptionNeedsSuccessWrite as $e}from"../subscription-state.js";import{classifyRateLimit as Ie,describeRateLimitScope as le,rateLimitHeaders as Re}from"../rate-limit-scope.js";import{RunError as s}from"../runs/operations.js";import{recognizeProviderError as ce}from"../provider-error-recognition.js";import{isSafetyRefusal as Se}from"./refusal.js";import{assertUnderCap as Ce,recordModelUsage as ue}from"../usage/ledger.js";import{outboundToken as Le}from"../providers/mint.js";import{discardFile as Oe}from"../providers/token-cache.js";import{ProviderError as Fe}from"../providers/errors.js";import{appCandidates as Ne}from"./app-rungs.js";function De(e){return typeof e=="string"||e===null||Array.isArray(e)&&e.every(t=>t&&typeof t=="object"&&(t.type==="text"&&typeof t.text=="string"||t.type==="image_url"&&typeof t.image_url?.url=="string"&&/^data:image\/(png|jpeg|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/.test(t.image_url.url)))}u(De,"validContent");function fe(e){return Array.isArray(e)?e.map(t=>{if(t.type==="text")return t;const[,n,r]=/^data:(image\/(?:png|jpeg|gif|webp));base64,(.*)$/.exec(t.image_url.url);return{type:"image",mimeType:n,data:r}}):e??""}u(fe,"imageContent");function st(e){if(!e||typeof e!="object"||Array.isArray(e))throw new s(400,"invalid_request","body must be an object");const t=e;if(!Array.isArray(t.messages)||!t.messages.every(n=>n&&typeof n=="object"&&["system","user","assistant","tool"].includes(n.role)&&De(n.content)))throw new s(400,"invalid_request","messages must be chat messages","messages");if(t.messages.some(n=>["system","assistant"].includes(n.role)&&typeof n.content!="string"&&n.content!==null))throw new s(400,"invalid_request","system and assistant content must be text","messages");if(t.model!==void 0&&typeof t.model!="string")throw new s(400,"invalid_request","model must be a string","model");if(t.model_policy!==void 0&&!["exact","substitute"].includes(t.model_policy))throw new s(400,"invalid_request","invalid model_policy","model_policy");if(t.payer!==void 0&&!["user","app"].includes(t.payer))throw new s(400,"invalid_request","invalid payer","payer");if(t.reasoning_effort!==void 0&&!["minimal","low","medium","high","xhigh","max"].includes(t.reasoning_effort))throw new s(400,"invalid_request","invalid reasoning_effort","reasoning_effort");if(t.stream!==void 0&&typeof t.stream!="boolean")throw new s(400,"invalid_request","stream must be boolean","stream");if(t.tools!==void 0&&(!Array.isArray(t.tools)||!t.tools.every(n=>n?.type==="function"&&typeof n.function?.name=="string"&&n.function.name.length>0&&(n.function.description===void 0||typeof n.function.description=="string")&&(n.function.parameters===void 0||n.function.parameters!==null&&typeof n.function.parameters=="object"&&!Array.isArray(n.function.parameters)&&n.function.parameters.type==="object"))))throw new s(400,"invalid_request","invalid tools","tools");if(t.tool_choice!==void 0&&t.tool_choice!=="auto"&&t.tool_choice!=="none"&&t.tool_choice!=="required"&&(typeof t.tool_choice!="object"||t.tool_choice===null||t.tool_choice.type!=="function"||typeof t.tool_choice.function?.name!="string"))throw new s(400,"invalid_request","invalid tool_choice","tool_choice");if(t.messages.some(n=>n.role==="assistant"&&n.tool_calls!==void 0&&(!Array.isArray(n.tool_calls)||!n.tool_calls.every(r=>r&&typeof r.id=="string"&&typeof r.function?.name=="string"&&typeof r.function.arguments=="string"&&Ee(r.function.arguments)))))throw new s(400,"invalid_request","invalid assistant tool_calls","messages");if(t.stop!==void 0&&!(typeof t.stop=="string"||Array.isArray(t.stop)&&t.stop.every(n=>typeof n=="string")))throw new s(400,"invalid_request","invalid stop","stop");if(t.seed!==void 0&&!Number.isSafeInteger(t.seed))throw new s(400,"invalid_request","invalid seed","seed");for(const[n,r]of[["temperature",t.temperature],["top_p",t.top_p],["max_tokens",t.max_tokens]])if(r!==void 0&&(typeof r!="number"||!Number.isFinite(r)||n==="temperature"&&(r<0||r>2)||n==="top_p"&&(r<0||r>1)||n==="max_tokens"&&(!Number.isSafeInteger(r)||r<1)))throw new s(400,"invalid_request",`invalid ${n}`,n);if(t.response_format!==void 0){const n=t.response_format;if(!n||typeof n!="object"||Array.isArray(n)||!["text","json_object","json_schema"].includes(n.type)||n.type==="json_schema"&&(typeof n.json_schema?.name!="string"||!n.json_schema.name||!n.json_schema.schema||typeof n.json_schema.schema!="object"||Array.isArray(n.json_schema.schema)))throw new s(400,"invalid_request","invalid response_format","response_format")}if(t.metadata!==void 0&&Buffer.byteLength(JSON.stringify(t.metadata)??"")>4096)throw new s(400,"invalid_request","metadata exceeds 4 KB","metadata");return t}u(st,"parseLlmRequest");function Ee(e){try{const t=JSON.parse(e);return t!==null&&typeof t=="object"&&!Array.isArray(t)}catch{return!1}}u(Ee,"isJsonObject");function Pe(e){const t=Date.now(),n=e.messages.map(i=>i.role==="system"?{role:"system",content:i.content??"",timestamp:t}:i.role==="tool"?{role:"toolResult",toolCallId:i.tool_call_id??"",toolName:"",content:typeof i.content=="string"||i.content===null?[{type:"text",text:i.content??""}]:fe(i.content),isError:!1,timestamp:t}:i.role==="assistant"?{role:"assistant",content:[...i.content?[{type:"text",text:i.content}]:[],...(i.tool_calls??[]).map(g=>({type:"toolCall",id:g.id,name:g.function.name,arguments:JSON.parse(g.function.arguments)}))],api:"openai-completions",provider:"openai",model:e.model??"",usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:t}:{role:"user",content:fe(i.content),timestamp:t}),r=n.findIndex(i=>i.role!=="system"),w=n.slice(0,r<0?n.length:r).map(i=>i.content),y=w.join(`
2
2
 
3
- `),l=e.tools?.map(({function:i})=>({name:i.name,description:i.description??"",parameters:i.parameters??{type:"object",properties:{}}}));return{...y?{systemPrompt:y}:{},messages:n.slice(w.length),...l?{tools:l}:{}}}u(Pe,"contextOf");function fe(e,t,n,r){if(!e){const y=ie(n,r),l=Object.values({...y.modelRoutes??{},...y.legacyModelRoutes??{}}),i=l.length?[y.modelLadders.defaultProvider,...y.modelRouting?.familyOrder??[],...l.map(g=>g.family)].find(g=>l.some(F=>F.family===g)):y.modelLadders.defaultProvider??"anthropic";return{kind:"logical",family:i,strength:"strong",spec:`${i}/strong`}}const w=/^([^/]+)\/(ultra|strong|medium|light)$/.exec(e);return w?{kind:"logical",family:w[1],strength:w[2],spec:e}:{kind:t==="exact"?"exact":"concrete",spec:e}}u(fe,"requestFor");function Ee(e,t){const{input:n,output:r,cacheRead:w,cacheWrite:y}=e.cost;return n===0&&r===0&&w===0&&y===0?null:Number.isFinite(t)&&t>=0?t:null}u(Ee,"usdEquivalent");function Te(e,t){return e.compat?.allowedFallbackModels?.find(r=>r.provider===e.provider&&r.model===t)}u(Te,"allowedFallback");function st(e){return e?{payer:e.payer??"user",sub:e.sub,grantee:e.grantee,inference_endpoint:e.inference_endpoint??null}:void 0}u(st,"llmCallerOf");function Ue(e){return e instanceof Oe?e.code==="server_error"?new s(503,"upstream_model_unavailable",e.message):new s(404,"model_unavailable",e.message):e}u(Ue,"mintFailure");async function it(e,t,n,r,w,y){const l=y?.payer==="app"?y:void 0;if(e.payer==="app"&&!l)throw new s(403,"payer_not_app","this grant is not app-paid");if(e.payer==="user"&&l)throw new s(400,"invalid_request","this grant is app-paid; payer cannot be user","payer");if(l&&!l.inference_endpoint)throw new s(404,"model_unavailable",`${l.grantee} is app-paid but its grant carries no inference endpoint`);const i=l?.inference_endpoint??void 0;l||Se(w);const g=await we(),F=u(c=>c.routeId!=="concrete"&&c.routeId!=="exact","configured"),Y=u(c=>{const p=g.find(c.providerId,c.modelId);return p!==void 0&&(!e.reasoning_effort||p.reasoning)&&(e.seed===void 0&&e.response_format===void 0||p.api==="openai-completions")&&(e.stop===void 0||["openai-completions","anthropic-messages"].includes(p.api))},"matchesFeatures"),me=u(()=>{const c=fe(e.model,e.model_policy,r?.cwd,r?.profileId),p=re(c.kind==="exact"?{kind:"concrete",spec:c.spec}:c,r?.cwd,r?.profileId).filter(F);let a;if(c.kind==="logical")a=e.model_policy==="exact"?p.filter(m=>m.family===c.family&&m.strength===c.strength).slice(0,1):p;else{const m=se(c.spec),M=p.filter(j=>j.providerId===m?.providerId&&j.modelId===m.modelId&&(e.model_policy!=="exact"||j.thinkingLevel===m.thinkingLevel)&&Y(j));if(e.model_policy==="exact")a=M.slice(0,1);else if(M.length)a=M;else{const j=ie(r?.cwd,r?.profileId),x=[...Object.values(j.modelRoutes??{}).flatMap(v=>Object.entries(v.models)),...[j.modelLadders.anthropic,j.modelLadders.openai].flatMap(v=>Object.entries(v))].find(([,v])=>v===c.spec)?.[0],f=fe(void 0,"substitute",r?.cwd,r?.profileId);a=x&&f.kind==="logical"?re({...f,strength:x,spec:c.spec},r?.cwd,r?.profileId).filter(v=>F(v)&&v.strength===x):[]}}return a},"ownerCandidates");let R;if(i?R=Fe(i,e.model,e.model_policy==="exact").flatMap((c,p)=>{const a=se(c.model);return a?[{routeId:"app",credentialSource:"app",family:"app",strength:c.strength,...a,rank:p}]:[]}):R=me(),R=R.filter(Y),R.length===0)throw e.model_policy==="exact"?new s(400,"substitution_refused","no eligible model route is configured"):new s(404,"model_unavailable","no eligible model route is configured");const ee=`chatcmpl-${ge()}`;let I=1/0,b,z;for(const c of R){if(!l&&!ve(c,g).authenticated)continue;const p=g.find(c.providerId,c.modelId);if(!p)continue;const a=i?{...p,baseUrl:`${i.base_url}/${p.provider}`}:p,m=c.providerId,M=!l&&H(m)?je(m).filter(x=>!x.authFailure):[],j=M.filter(x=>ae(x,a.id)<=Date.now());if(M.length&&!j.length){const x=Math.min(...M.map(f=>ae(f,a.id)));x<I&&(I=x,z=`${a.provider}/${a.id}`);continue}const te=l?[void 0,void 0]:M.length?j:[void 0];for(const[x,f]of te.entries()){const v=Date.now();let oe=f?.access;if(f&&f.expires<=Date.now()&&H(m)){const o=g.getProvider(m)?.auth.oauth;if(!o)throw new s(503,"upstream_model_unavailable",`no OAuth refresh for ${m}`,void 0,{served_model:`${a.provider}/${a.id}`});oe=(await Ae(m,f.label,async(k,ne)=>{const W=await o.refresh({type:"oauth",refresh:k,access:"",expires:0,...ne?{clientId:ne}:{}},t??new AbortController().signal),X=W.clientId;return{refresh:W.refresh,access:W.access,expires:W.expires,...typeof X=="string"&&X?{clientId:X}:{}}})).access}const pe=Pe(e);let N;if(l&&i)try{N=await Ce({audience:i.base_url,sub:l.sub,grantee:l.grantee,initiating_app:l.grantee,scope:["crtr:llm"]},{signal:t})}catch(o){throw Ue(o)}const S=f||l?void 0:await g.getApiKeyAndHeaders(a);if(S&&!S.ok)continue;const h=`${a.provider}/${a.id}`,C=e.model??h;let A=!1,O,q=3e4,$;const B=[],Z=[],D=[];let d,P;const E=u((o,_=null)=>({id:ee,object:"chat.completion.chunk",created:Math.floor(v/1e3),model:h,served_model:h,requested_model:C,substituted:C!==h&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(C),choices:[{index:0,delta:o,finish_reason:_}]}),"chunk"),T=u(o=>{B.push(o),n?.(o)},"emit"),he=be(a,pe,{apiKey:N?.access_token??oe??(S?.ok?S.apiKey:void 0),...S?.ok?{headers:S.headers}:{},signal:t,reasoning:e.reasoning_effort??c.thinkingLevel,...e.temperature!==void 0?{temperature:e.temperature}:{},...e.max_tokens!==void 0?{maxTokens:e.max_tokens}:{},...e.top_p!==void 0?{samplingParams:{top_p:e.top_p}}:{},...e.tool_choice==="none"?{toolChoice:"none"}:{},onPayload:u(o=>{if(!o||typeof o!="object")return;const _=e.tool_choice,k=_==="required"?a.api==="anthropic-messages"?{type:"any"}:"required":typeof _=="object"&&_!==null?a.api==="anthropic-messages"?{type:"tool",name:_.function.name}:_:void 0;return{...o,...k?{tool_choice:k}:{},...e.stop!==void 0?{[a.api==="anthropic-messages"?"stop_sequences":"stop"]:typeof e.stop=="string"?[e.stop]:e.stop}:{},...e.seed!==void 0?{seed:e.seed}:{},...e.response_format!==void 0?{response_format:e.response_format}:{}}},"onPayload"),fetch:u(async(o,_)=>{const k=await fetch(o,_);return O=k.status,k.status===429&&(A=!0,$=k.headers,q=K(k.headers)??15e3),k},"fetch"),onResponse:u(o=>{o.status===429&&(A=!0,$=o.headers,q=K(o.headers)??15e3)},"onResponse")});try{for await(const o of he)o.type==="text_delta"&&(Z.push(o.delta),T(E({content:o.delta}))),o.type==="thinking_delta"&&(D.push(o.delta),T(E({reasoning_content:o.delta}))),o.type==="done"&&(d=o.message),o.type==="error"&&(d=o.error)}catch(o){const _=qe(o);$===void 0&&o&&typeof o=="object"&&"headers"in o&&Object.keys(Re(o.headers)).length&&($=o.headers);const k=le({status:o&&typeof o=="object"&&"status"in o?o.status:void 0,message:o instanceof Error?o.message:String(o)});if(l)P=o;else{if(!A&&k.kind!=="rate-limit")throw B.length?new s(503,"upstream_model_unavailable",o instanceof Error?o.message:String(o),void 0,{served_model:h}):o;A=!0,q=_??q}}if(l){if(O===401){if(N&&Le(N.key),x===0)continue;throw new s(404,"model_unavailable",`${i.base_url} refuses this runtime's tokens`,void 0,{served_model:h})}if(A&&K($)===void 0)throw new s(503,"upstream_model_unavailable",`${h} was rate-limited without a retry-after`,void 0,{served_model:h});if(!A&&(P!==void 0||O!==void 0&&O>=500))throw new s(503,"upstream_model_unavailable",P instanceof Error?P.message:d?.errorMessage??"inference endpoint failed",void 0,{served_model:h})}if(!A&&d?.stopReason==="error"&&le({message:d.errorMessage}).kind==="rate-limit"&&(A=!0,q=15e3),A){const o=$e($,a.id);if(K($)===void 0&&o.resetAt!==void 0&&(q=Math.max(q,o.resetAt-Date.now())),b={provider:m,model:a.id,...f?{account:f.label}:{},observation:o},console.error(`[llm] 429 from ${h}${f?` account "${f.label}"`:""}: scope=${de(o.scope)} retry_after_ms=${q} headers=${JSON.stringify(o.headers)}`),f&&H(m)&&await xe(m,f.label,q,v,Date.now(),o.scope),Date.now()+q<I&&(I=Date.now()+q,z=h),Z.length||D.length)throw new s(503,"service_unavailable","vendor rate limit after output began",void 0,{served_model:h});if(l)break;continue}const U=Ie(d);if(O===400&&e.tool_choice!==void 0&&e.tool_choice!=="auto"&&e.tool_choice!=="none"&&/\btool[_ ]choice\b/i.test(d?.errorMessage??"")&&/not supported|does not support|unsupported|cannot|must be auto/i.test(d?.errorMessage??""))throw new s(400,"invalid_request",d.errorMessage,"tool_choice",{served_model:h});if(!d||d.stopReason==="error"&&!U||d.stopReason==="aborted")throw new s(503,"upstream_model_unavailable",d?.errorMessage??"model request failed",void 0,{served_model:h});f&&H(m)&&Me(f,a.id)&&await ke(m,f.label,v,a.id);const G=d.content.filter(o=>o.type==="toolCall").map(o=>({id:o.id,type:"function",function:{name:o.name,arguments:JSON.stringify(o.arguments)}})),Q=U?"content_filter":d.stopReason==="toolUse"?"tool_calls":d.stopReason==="length"?"length":"stop",V=d.responseModel?Te(a,d.responseModel):void 0,L=V?`${a.provider}/${V.model}`:h,J={prompt_tokens:d.usage.input,completion_tokens:d.usage.output,total_tokens:d.usage.totalTokens,cache_read_tokens:d.usage.cacheRead,cache_write_tokens:d.usage.cacheWrite,usd_equivalent:Ee(V??a,d.usage.cost.total),served_model:L};l?ce(l.grantee,"app",L,d.usage,J.usd_equivalent):w&&ce(w.grantee,"user",L,d.usage,J.usd_equivalent);const _e={id:ee,object:"chat.completion",created:Math.floor(v/1e3),model:L,served_model:L,requested_model:C,substituted:C!==L&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(C),choices:[{index:0,message:{role:"assistant",content:Z.join("")||null,...D.length?{reasoning_content:D.join("")}:{},...G.length?{tool_calls:G}:{},...U?{refusal:d.errorMessage??"Content filtered"}:{}},finish_reason:Q}],usage:J,...e.metadata!==void 0?{metadata:e.metadata}:{}};for(const[o,_]of G.entries())T(E({tool_calls:[{index:o,..._}]}));return T({...E({},Q),...U?{choices:[{index:0,delta:{},finish_reason:Q,message:{refusal:d.errorMessage??"Content filtered"}}]}:{},usage:J}),{completion:_e,chunks:B}}}throw I!==1/0?new s(429,"all_routes_exhausted","all eligible model routes are rate-limited",void 0,{retry_after_s:Math.max(1,Math.ceil((I-Date.now())/1e3)),served_model:b?`${b.provider}/${b.model}`:z,...b?{rate_limit:{provider:b.provider,model:b.model,...b.account?{account:b.account}:{},scope:de(b.observation.scope),...b.observation.claim?{claim:b.observation.claim}:{},headers:b.observation.headers}}:{}},void 0,!0):e.model_policy==="exact"?new s(400,"substitution_refused","the requested model is unavailable"):new s(404,"model_unavailable","no usable model route is available")}u(it,"runLlm");export{st as llmCallerOf,rt as parseLlmRequest,it as runLlm};
3
+ `),l=e.tools?.map(({function:i})=>({name:i.name,description:i.description??"",parameters:i.parameters??{type:"object",properties:{}}}));return{...y?{systemPrompt:y}:{},messages:n.slice(w.length),...l?{tools:l}:{}}}u(Pe,"contextOf");function me(e,t,n,r){if(!e){const y=ie(n,r),l=Object.values({...y.modelRoutes??{},...y.legacyModelRoutes??{}}),i=l.length?[y.modelLadders.defaultProvider,...y.modelRouting?.familyOrder??[],...l.map(g=>g.family)].find(g=>l.some(F=>F.family===g)):y.modelLadders.defaultProvider??"anthropic";return{kind:"logical",family:i,strength:"strong",spec:`${i}/strong`}}const w=/^([^/]+)\/(ultra|strong|medium|light)$/.exec(e);return w?{kind:"logical",family:w[1],strength:w[2],spec:e}:{kind:t==="exact"?"exact":"concrete",spec:e}}u(me,"requestFor");function Te(e,t){const{input:n,output:r,cacheRead:w,cacheWrite:y}=e.cost;return n===0&&r===0&&w===0&&y===0?null:Number.isFinite(t)&&t>=0?t:null}u(Te,"usdEquivalent");function Ue(e,t){return e.compat?.allowedFallbackModels?.find(r=>r.provider===e.provider&&r.model===t)}u(Ue,"allowedFallback");function it(e){return e?{payer:e.payer??"user",sub:e.sub,grantee:e.grantee,inference_endpoint:e.inference_endpoint??null}:void 0}u(it,"llmCallerOf");function Je(e){return e instanceof Fe?e.code==="server_error"?new s(503,"upstream_model_unavailable",e.message):new s(404,"model_unavailable",e.message):e}u(Je,"mintFailure");async function at(e,t,n,r,w,y){const l=y?.payer==="app"?y:void 0;if(e.payer==="app"&&!l)throw new s(403,"payer_not_app","this grant is not app-paid");if(e.payer==="user"&&l)throw new s(400,"invalid_request","this grant is app-paid; payer cannot be user","payer");if(l&&!l.inference_endpoint)throw new s(404,"model_unavailable",`${l.grantee} is app-paid but its grant carries no inference endpoint`);const i=l?.inference_endpoint??void 0;l||Ce(w);const g=await xe(),F=u(c=>c.routeId!=="concrete"&&c.routeId!=="exact","configured"),Y=u(c=>{const p=g.find(c.providerId,c.modelId);return p!==void 0&&(!e.reasoning_effort||p.reasoning)&&(e.seed===void 0&&e.response_format===void 0||p.api==="openai-completions")&&(e.stop===void 0||["openai-completions","anthropic-messages"].includes(p.api))},"matchesFeatures"),pe=u(()=>{const c=me(e.model,e.model_policy,r?.cwd,r?.profileId),p=re(c.kind==="exact"?{kind:"concrete",spec:c.spec}:c,r?.cwd,r?.profileId).filter(F);let a;if(c.kind==="logical")a=e.model_policy==="exact"?p.filter(m=>m.family===c.family&&m.strength===c.strength).slice(0,1):p;else{const m=se(c.spec),A=p.filter(j=>j.providerId===m?.providerId&&j.modelId===m.modelId&&(e.model_policy!=="exact"||j.thinkingLevel===m.thinkingLevel)&&Y(j));if(e.model_policy==="exact")a=A.slice(0,1);else if(A.length)a=A;else{const j=ie(r?.cwd,r?.profileId),x=[...Object.values(j.modelRoutes??{}).flatMap(v=>Object.entries(v.models)),...[j.modelLadders.anthropic,j.modelLadders.openai].flatMap(v=>Object.entries(v))].find(([,v])=>v===c.spec)?.[0],f=me(void 0,"substitute",r?.cwd,r?.profileId);a=x&&f.kind==="logical"?re({...f,strength:x,spec:c.spec},r?.cwd,r?.profileId).filter(v=>F(v)&&v.strength===x):[]}}return a},"ownerCandidates");let I;if(i?I=Ne(i,e.model,e.model_policy==="exact").flatMap((c,p)=>{const a=se(c.model);return a?[{routeId:"app",credentialSource:"app",family:"app",strength:c.strength,...a,rank:p}]:[]}):I=pe(),I=I.filter(Y),I.length===0)throw e.model_policy==="exact"?new s(400,"substitution_refused","no eligible model route is configured"):new s(404,"model_unavailable","no eligible model route is configured");const ee=`chatcmpl-${be()}`;let R=1/0,b,z;for(const c of I){if(!l&&!we(c,g).authenticated)continue;const p=g.find(c.providerId,c.modelId);if(!p)continue;const a=i?{...p,baseUrl:`${i.base_url}/${p.provider}`}:p,m=c.providerId,A=!l&&H(m)?Me(m).filter(x=>!x.authFailure):[],j=A.filter(x=>de(x,a.id)<=Date.now());if(A.length&&!j.length){const x=Math.min(...A.map(f=>de(f,a.id)));x<R&&(R=x,z=`${a.provider}/${a.id}`);continue}const te=l?[void 0,void 0]:A.length?j:[void 0];for(const[x,f]of te.entries()){const v=Date.now();let oe=f?.access;if(f&&f.expires<=Date.now()&&H(m)){const o=g.getProvider(m)?.auth.oauth;if(!o)throw new s(503,"upstream_model_unavailable",`no OAuth refresh for ${m}`,void 0,{served_model:`${a.provider}/${a.id}`});oe=(await Ae(m,f.label,async(k,ne)=>{const W=await o.refresh({type:"oauth",refresh:k,access:"",expires:0,...ne?{clientId:ne}:{}},t??new AbortController().signal),V=W.clientId;return{refresh:W.refresh,access:W.access,expires:W.expires,...typeof V=="string"&&V?{clientId:V}:{}}})).access}const he=Pe(e);let N;if(l&&i)try{N=await Le({audience:i.base_url,sub:l.sub,grantee:l.grantee,initiating_app:l.grantee,scope:["crtr:llm"]},{signal:t})}catch(o){throw Je(o)}const S=f||l?void 0:await g.getApiKeyAndHeaders(a);if(S&&!S.ok)continue;const h=`${a.provider}/${a.id}`,C=e.model??h;let q=!1,O,M=3e4,$;const B=[],X=[],D=[];let d,E;const P=u((o,_=null)=>({id:ee,object:"chat.completion.chunk",created:Math.floor(v/1e3),model:h,served_model:h,requested_model:C,substituted:C!==h&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(C),choices:[{index:0,delta:o,finish_reason:_}]}),"chunk"),T=u(o=>{B.push(o),n?.(o)},"emit"),_e=ve(a,he,{apiKey:N?.access_token??oe??(S?.ok?S.apiKey:void 0),...S?.ok?{headers:S.headers}:{},signal:t,reasoning:e.reasoning_effort??c.thinkingLevel,...e.temperature!==void 0?{temperature:e.temperature}:{},...e.max_tokens!==void 0?{maxTokens:e.max_tokens}:{},...e.top_p!==void 0?{samplingParams:{top_p:e.top_p}}:{},...e.tool_choice==="none"?{toolChoice:"none"}:{},onPayload:u(o=>{if(!o||typeof o!="object")return;const _=e.tool_choice,k=_==="required"?a.api==="anthropic-messages"?{type:"any"}:"required":typeof _=="object"&&_!==null?a.api==="anthropic-messages"?{type:"tool",name:_.function.name}:_:void 0;return{...o,...k?{tool_choice:k}:{},...e.stop!==void 0?{[a.api==="anthropic-messages"?"stop_sequences":"stop"]:typeof e.stop=="string"?[e.stop]:e.stop}:{},...e.seed!==void 0?{seed:e.seed}:{},...e.response_format!==void 0?{response_format:e.response_format}:{}}},"onPayload"),fetch:u(async(o,_)=>{const k=await fetch(o,_);return O=k.status,k.status===429&&(q=!0,$=k.headers,M=K(k.headers)??15e3),k},"fetch"),onResponse:u(o=>{o.status===429&&(q=!0,$=o.headers,M=K(o.headers)??15e3)},"onResponse")});try{for await(const o of _e)o.type==="text_delta"&&(X.push(o.delta),T(P({content:o.delta}))),o.type==="thinking_delta"&&(D.push(o.delta),T(P({reasoning_content:o.delta}))),o.type==="done"&&(d=o.message),o.type==="error"&&(d=o.error)}catch(o){const _=qe(o);$===void 0&&o&&typeof o=="object"&&"headers"in o&&Object.keys(Re(o.headers)).length&&($=o.headers);const k=ce({status:o&&typeof o=="object"&&"status"in o?o.status:void 0,message:o instanceof Error?o.message:String(o)});if(l)E=o;else{if(!q&&k.kind!=="rate-limit")throw B.length?new s(503,"upstream_model_unavailable",o instanceof Error?o.message:String(o),void 0,{served_model:h}):o;q=!0,M=_??M}}if(l){if(O===401){if(N&&Oe(N.key),x===0)continue;throw new s(404,"model_unavailable",`${i.base_url} refuses this runtime's tokens`,void 0,{served_model:h})}if(q&&K($)===void 0)throw new s(503,"upstream_model_unavailable",`${h} was rate-limited without a retry-after`,void 0,{served_model:h});if(!q&&(E!==void 0||O!==void 0&&O>=500))throw new s(503,"upstream_model_unavailable",E instanceof Error?E.message:d?.errorMessage??"inference endpoint failed",void 0,{served_model:h})}if(!q&&d?.stopReason==="error"&&ce({message:d.errorMessage}).kind==="rate-limit"&&(q=!0,M=15e3),q){const o=Ie($,a.id);if(K($)===void 0&&o.resetAt!==void 0&&(M=Math.max(M,o.resetAt-Date.now())),b={provider:m,model:a.id,...f?{account:f.label}:{},observation:o},console.error(`[llm] 429 from ${h}${f?` account "${f.label}"`:""}: scope=${le(o.scope)} retry_after_ms=${M} headers=${JSON.stringify(o.headers)}`),f&&H(m)&&await ke(m,f.label,M,v,Date.now(),o.scope),Date.now()+Math.min(M,ae)<R&&(R=Date.now()+Math.min(M,ae),z=h),X.length||D.length)throw new s(503,"service_unavailable","vendor rate limit after output began",void 0,{served_model:h});if(l)break;continue}const U=Se(d);if(O===400&&e.tool_choice!==void 0&&e.tool_choice!=="auto"&&e.tool_choice!=="none"&&/\btool[_ ]choice\b/i.test(d?.errorMessage??"")&&/not supported|does not support|unsupported|cannot|must be auto/i.test(d?.errorMessage??""))throw new s(400,"invalid_request",d.errorMessage,"tool_choice",{served_model:h});if(!d||d.stopReason==="error"&&!U||d.stopReason==="aborted")throw new s(503,"upstream_model_unavailable",d?.errorMessage??"model request failed",void 0,{served_model:h});f&&H(m)&&$e(f,a.id)&&await je(m,f.label,v,a.id);const Z=d.content.filter(o=>o.type==="toolCall").map(o=>({id:o.id,type:"function",function:{name:o.name,arguments:JSON.stringify(o.arguments)}})),G=U?"content_filter":d.stopReason==="toolUse"?"tool_calls":d.stopReason==="length"?"length":"stop",Q=d.responseModel?Ue(a,d.responseModel):void 0,L=Q?`${a.provider}/${Q.model}`:h,J={prompt_tokens:d.usage.input,completion_tokens:d.usage.output,total_tokens:d.usage.totalTokens,cache_read_tokens:d.usage.cacheRead,cache_write_tokens:d.usage.cacheWrite,usd_equivalent:Te(Q??a,d.usage.cost.total),served_model:L};l?ue(l.grantee,"app",L,d.usage,J.usd_equivalent):w&&ue(w.grantee,"user",L,d.usage,J.usd_equivalent);const ye={id:ee,object:"chat.completion",created:Math.floor(v/1e3),model:L,served_model:L,requested_model:C,substituted:C!==L&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(C),choices:[{index:0,message:{role:"assistant",content:X.join("")||null,...D.length?{reasoning_content:D.join("")}:{},...Z.length?{tool_calls:Z}:{},...U?{refusal:d.errorMessage??"Content filtered"}:{}},finish_reason:G}],usage:J,...e.metadata!==void 0?{metadata:e.metadata}:{}};for(const[o,_]of Z.entries())T(P({tool_calls:[{index:o,..._}]}));return T({...P({},G),...U?{choices:[{index:0,delta:{},finish_reason:G,message:{refusal:d.errorMessage??"Content filtered"}}]}:{},usage:J}),{completion:ye,chunks:B}}}throw R!==1/0?new s(429,"all_routes_exhausted","all eligible model routes are rate-limited",void 0,{retry_after_s:Math.max(1,Math.ceil((R-Date.now())/1e3)),served_model:b?`${b.provider}/${b.model}`:z,...b?{rate_limit:{provider:b.provider,model:b.model,...b.account?{account:b.account}:{},scope:le(b.observation.scope),...b.observation.claim?{claim:b.observation.claim}:{},headers:b.observation.headers}}:{}},void 0,!0):e.model_policy==="exact"?new s(400,"substitution_refused","the requested model is unavailable"):new s(404,"model_unavailable","no usable model route is available")}u(at,"runLlm");export{it as llmCallerOf,st as parseLlmRequest,at as runLlm};
@@ -3,6 +3,8 @@ export declare const ANTHROPIC_PROVIDER_ID = "anthropic";
3
3
  export declare const OPENAI_CODEX_PROVIDER_ID = "openai-codex";
4
4
  /** pi's built-in `openai` provider signed in with a ChatGPT account (dynamic OAuth client). Distinct from Codex: its own pool, credential file and sign-in. */
5
5
  export declare const OPENAI_CHATGPT_PROVIDER_ID = "openai";
6
+ /** Longest a recorded 429 keeps an account or model family cooling, whatever the provider's Retry-After or window reset says: after this the next real call probes the provider, and a success clears the row. */
7
+ export declare const MAX_RATE_LIMIT_COOLDOWN_MS: number;
6
8
  export declare const INVALID_REFRESH_TOKEN_BACKOFF_MS: number;
7
9
  export type ManagedProviderId = typeof ANTHROPIC_PROVIDER_ID | typeof OPENAI_CODEX_PROVIDER_ID | typeof OPENAI_CHATGPT_PROVIDER_ID;
8
10
  export declare const MANAGED_PROVIDER_IDS: readonly ManagedProviderId[];
@@ -1,4 +1,4 @@
1
- var rt=Object.defineProperty;var o=(t,n)=>rt(t,"name",{value:n,configurable:!0});import{prepared as g}from"./sqlite-statements.js";import{createHash as ot,randomUUID as U}from"node:crypto";import{mkdirSync as st,readFileSync as ct,writeFileSync as lt}from"node:fs";import{join as q}from"node:path";import{brokerConfigSnapshot as ut}from"./config.js";import{LockedJsonFile as dt}from"./locked-file.js";import{secretsDir as mt}from"./layout.js";import{openScopedStore as ft}from"./scoped-state/db.js";import{providerStore as ht}from"./scoped-state/paths.js";import{providerModelCooldowns as T,providerModelCooldownsFor as bt,providerRowByReference as P,providerRows as pt,providerWrite as A}from"./scoped-state/providers.js";import{modelCooldownFamily as j}from"./rate-limit-scope.js";const H="anthropic",z="openai-codex",G="openai",At=720*60*60*1e3,qt=[H,z,G],J="strong",Q={anthropic:{label:"Claude",credentials:"anthropic-credentials.json"},"openai-codex":{label:"OpenAI Codex",credentials:"openai-codex-credentials.json"},openai:{label:"ChatGPT",credentials:"openai-credentials.json"}};function I(t){return{refresh:t.refresh,access:t.access,expires:t.expires,...t.accountId?{accountId:t.accountId}:{},...t.clientId?{clientId:t.clientId}:{}}}o(I,"stored");function W(t){return typeof t=="string"&&t.trim()?t.trim():void 0}o(W,"optionalString");function h(){return mt()}o(h,"agentDir");function K(t){return q(h(),Q[t].credentials)}o(K,"credentialFile");function X(t){return t==="ultra"||t==="strong"||t==="medium"||t==="light"?t:void 0}o(X,"normalizeStrength");function f(t){return t.toLowerCase()}o(f,"key");function yt(t){const n=typeof t.label=="string"?t.label.trim():"",e=typeof t.refresh=="string"?t.refresh.trim():"",a=typeof t.access=="string"?t.access.trim():"",i=Number(t.expires);if(!n||!e||!a||!Number.isFinite(i))return;const r=W(t.accountId),s=W(t.clientId);return{label:n,refresh:e,access:a,expires:i,rateLimitedUntil:Number.isFinite(Number(t.rateLimitedUntil))?Number(t.rateLimitedUntil):0,lastAttemptAt:Number.isFinite(Number(t.lastAttemptAt))?Number(t.lastAttemptAt):0,lastRateLimitedAt:Number.isFinite(Number(t.lastRateLimitedAt))?Number(t.lastRateLimitedAt):0,...t.authFailure==="invalid_grant"?{authFailure:"invalid_grant"}:{},...r?{accountId:r}:{},...s?{clientId:s}:{},...Y(t.modelCooldowns)?{modelCooldowns:Y(t.modelCooldowns)}:{}}}o(yt,"credential");function Y(t){if(!t||typeof t!="object"||Array.isArray(t))return;const n=Object.entries(t).filter(([e,a])=>e.trim()!==""&&Number.isFinite(Number(a))).map(([e,a])=>[e,Number(a)]);return n.length?Object.fromEntries(n):void 0}o(Y,"modelCooldowns");function N(t,n){if(!n.label?.trim())throw new Error(`${b(t)} subscription label cannot be empty`);const e=yt(n);if(!e)throw new Error(`Refusing to persist incomplete ${b(t)} subscription "${n.label.trim()}"; pool rows require both OAuth tokens.`);return e}o(N,"assertCredential");function Z(t,n){if(!n||n.trim()==="")return{version:1,accounts:{}};let e;try{e=JSON.parse(n)}catch(a){throw new Error(`managed credential file ${t} is corrupt: ${a instanceof Error?a.message:String(a)}`)}if(typeof e!="object"||e===null||Array.isArray(e)||e.version!==1||typeof e.accounts!="object"||e.accounts===null||Array.isArray(e.accounts))throw new Error(`managed credential file ${t} is corrupt`);return e}o(Z,"parseCredentials");function _t(t,n,e=[]){const a=n.accounts[t.id];if(!a||typeof a.refresh!="string"||typeof a.access!="string"||!Number.isFinite(a.expires))throw new Error(`managed ${t.providerId} account ${t.id} has no credential record`);return{label:t.label,refresh:a.refresh,access:a.access,expires:a.expires,rateLimitedUntil:t.rateLimitedUntil,lastAttemptAt:t.lastAttemptAt,lastRateLimitedAt:t.lastRateLimitedAt,...t.authFailure?{authFailure:t.authFailure}:{},...a.accountId?{accountId:a.accountId}:{},...a.clientId?{clientId:a.clientId}:{},...e.length?{modelCooldowns:Object.fromEntries(e.map(i=>[i.family,i.rateLimitedUntil]))}:{}}}o(_t,"toPublic");function V(t,n){const e=bt(h(),t);return pt(h(),t).map(a=>({row:a,entry:_t(a,n,e.get(a.id))}))}o(V,"rowsWithCredentials");function v(t){return new dt(K(t),!0,`{"version":1,"accounts":{}}
2
- `)}o(v,"managedCredentialFile");function gt(t){return Z(K(t),v(t).readUnlocked())}o(gt,"readFile");function tt(t,n){const e=t.accountId?.trim(),a=n.accountId?.trim();return!!(e&&a&&e!==a)}o(tt,"differentAccount");function _(t){return t.accountId?.trim()||(t.refresh.trim()?`refresh:${ot("sha256").update(t.refresh.trim()).digest("hex")}`:void 0)}o(_,"identity");function et(t,n){const e=W(n);return e?{...t,clientId:e}:t}o(et,"withClientId");function nt(t,n,e){const a=e.refresh?.trim()??"",i=e.access?.trim()??"",r=Number(e.expires);if(!a||!i||!Number.isFinite(r))throw new Error(`${b(t)} subscription "${n}" returned an incomplete credential; the stored credential was left unchanged.`);return{refresh:a,access:i,expires:r}}o(nt,"assertTokens");function Et(t,n){const e=j(n);return Math.max(t.rateLimitedUntil||0,e===void 0?0:t.modelCooldowns?.[e]??0)}o(Et,"coolingUntilFor");function wt(t,n=Date.now()){return[...(t.rateLimitedUntil||0)>n?[{scope:"account",until:t.rateLimitedUntil}]:[],...Object.entries(t.modelCooldowns??{}).filter(([,e])=>e>n).map(([e,a])=>({scope:e,until:a}))]}o(wt,"activeCooldowns");function zt(t,n=Date.now()){const e=wt(t,n).map(({scope:a,until:i})=>`${a==="account"?"":`${a} `}cooldown ${xt(i-n)}`);return e.length?e.join(", "):void 0}o(zt,"formatCooldowns");function xt(t){const n=Math.max(1,Math.ceil(t/6e4));if(n<60)return`${n}m`;const e=Math.floor(n/60);return e<24?`${e}h${n%60?` ${n%60}m`:""}`:`${Math.floor(e/24)}d${e%24?` ${e%24}h`:""}`}o(xt,"formatWait");function Lt(t,n){const e=j(n);return e!==void 0&&t.modelCooldowns?.[e]!==void 0}o(Lt,"hasModelCooldownRow");function Gt(t,n){return!!(t.rateLimitedUntil||t.authFailure)||Lt(t,n)}o(Gt,"subscriptionNeedsSuccessWrite");function Jt(t){return t===H||t===z||t===G}o(Jt,"isManagedProvider");function Qt(){const t=q(h(),"openai-device-id");try{const e=ct(t,"utf8").trim();if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))return e.toLowerCase()}catch{}const n=U();return st(h(),{recursive:!0,mode:448}),lt(t,`${n}
3
- `,{mode:384}),n}o(Qt,"openAIDeviceId");function b(t){return Q[t].label}o(b,"getProviderLabel");function Xt(t,n=Date.now(),e={}){const a=[],i=[];for(let r=0;r<t.length;r++){const s=t[r],c=s.registered?s.automaticFallback===!1?"non-managed-fallback":s.authenticated?(s.coolingUntil??0)>n?"cooling":void 0:"unauthenticated":"unregistered";if(!c)a.push(s);else if(i.push({candidate:s,reason:c}),e.pinnedToFirst&&r===0&&c!=="cooling")break}return{candidates:a,rejected:i}}o(Xt,"resolveProviderCandidates");class R extends Error{static{o(this,"DuplicateSubscriptionError")}constructor(n){super(n),this.name="DuplicateSubscriptionError"}}function D(t){return V(t,gt(t)).map(({entry:n})=>n)}o(D,"readSubscriptionPool");async function E(t,n){const e=K(t);return v(t).withLockAsync(async(a,i,r)=>{const s=Z(e,a);return{result:await n(s,V(t,s),c=>r(`${JSON.stringify(c)}
4
- `))}})}o(E,"locked");function M(t,n,e){g(t,"INSERT INTO managed_accounts(provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure) VALUES(?,?,?,?,?,?,?,?,?)").run(n,e.id,e.label,e.labelKey,e.position,e.lastAttemptAt,e.lastRateLimitedAt,e.rateLimitedUntil,e.authFailure??null)}o(M,"insert");function x(t,n){g(t,"UPDATE managed_accounts SET label=?,label_key=?,position=?,last_attempt_at=?,last_rate_limited_at=?,rate_limited_until=?,auth_failure=? WHERE provider_id=? AND id=?").run(n.label,n.labelKey,n.position,n.lastAttemptAt,n.lastRateLimitedAt,n.rateLimitedUntil,n.authFailure??null,n.providerId,n.id)}o(x,"update");function at(t,n,e,a,i,r){g(t,"INSERT INTO managed_account_model_cooldowns(provider_id,account_id,family,rate_limited_until,last_rate_limited_at) VALUES(?,?,?,?,?) ON CONFLICT(provider_id,account_id,family) DO UPDATE SET rate_limited_until=excluded.rate_limited_until,last_rate_limited_at=excluded.last_rate_limited_at").run(n,e,a,i,r)}o(at,"upsertModelCooldown");function C(t,n,e){e===void 0?g(t,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=?").run(n):g(t,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=? AND account_id=?").run(n,e)}o(C,"deleteModelCooldowns");function w(t,n){return V(t,n).map(({entry:e})=>e)}o(w,"publicPool");function $(t,n){try{n()}catch(e){throw new Error(`managed ${b(t)} credential file was published, but its SQL membership update failed; SQL membership remains authoritative: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}o($,"writeMembershipAfterCredentialPublish");async function Yt(t,n){return E(t,async(e,a,i)=>{const r=n.map(u=>N(t,u)),s=r.map((u,l)=>({entry:u,id:a[l]?.row.id??U()})),c={};for(const{entry:u,id:l}of s)c[l]=I(u);return i({version:1,accounts:c}),$(t,()=>A(h(),u=>{const l=T(u,t);g(u,"DELETE FROM managed_accounts WHERE provider_id=?").run(t),g(u,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=?").run(t),s.forEach(({entry:m,id:d},y)=>{M(u,t,{providerId:t,id:d,label:m.label,labelKey:f(m.label),position:y,lastAttemptAt:m.lastAttemptAt,lastRateLimitedAt:m.lastRateLimitedAt,rateLimitedUntil:m.rateLimitedUntil,...m.authFailure?{authFailure:m.authFailure}:{}});for(const[F,k]of Object.entries(m.modelCooldowns??{}))at(u,t,d,F,k,l.get(d)?.find(p=>p.family===F)?.lastRateLimitedAt??m.lastRateLimitedAt)})})),r})}o(Yt,"writeSubscriptionPool");function O(t,n){const e=n.trim().toLowerCase(),a=Number.parseInt(e,10);return Number.isFinite(a)&&String(a)===e&&a>=1&&a<=t.length?a-1:t.findIndex(({entry:i})=>f(i.label)===e)}o(O,"resolve");async function Rt(t,n){const e=N(t,n);return E(t,async(a,i,r)=>{if(i.some(({entry:l})=>f(l.label)===f(e.label)))throw new R(`${b(t)} subscription already exists: ${e.label}`);const s=_(e),c=i.find(({entry:l})=>_(l)===s);if(s&&c)throw new R(`This ${b(t)} account is already added as \u201C${c.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const u=U();return a.accounts[u]=I(e),r(a),$(t,()=>A(h(),l=>M(l,t,{providerId:t,id:u,label:e.label,labelKey:f(e.label),position:i.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}}))),w(t,a)})}o(Rt,"addSubscription");async function Zt(t,n){const e=N(t,n);return D(t).find(i=>f(i.label)===f(e.label))?It(t,e):Rt(t,e)}o(Zt,"upsertSubscription");async function It(t,n){const e=N(t,n);return E(t,async(a,i,r)=>{const s=i.findIndex(({entry:d})=>f(d.label)===f(e.label)),c=i[s],u=i.find(({entry:d},y)=>y!==s&&_(d)===_(e));if(_(e)&&u)throw new R(`This ${b(t)} account is already added as \u201C${u.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);if(!c){const d=U();return a.accounts[d]=I(e),r(a),$(t,()=>A(h(),y=>M(y,t,{providerId:t,id:d,label:e.label,labelKey:f(e.label),position:i.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil}))),w(t,a)}const l=c.row,m=e.lastRateLimitedAt>=l.lastRateLimitedAt;return a.accounts[l.id]=I(e),r(a),$(t,()=>A(h(),d=>{x(d,{...l,label:e.label,labelKey:f(e.label),lastAttemptAt:Math.max(l.lastAttemptAt,e.lastAttemptAt),lastRateLimitedAt:m?e.lastRateLimitedAt:l.lastRateLimitedAt,rateLimitedUntil:m?e.rateLimitedUntil:l.rateLimitedUntil}),tt(c.entry,e)&&C(d,t,l.id)})),w(t,a)})}o(It,"upsertSubscriptionWithUniqueAccount");async function vt(t,n,e){return E(t,async(a,i)=>{const r=O(i,n);if(r<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const s=e.trim();if(!s)throw new Error(`${b(t)} subscription label cannot be empty`);if(i.some((c,u)=>u!==r&&f(c.entry.label)===f(s)))throw new R(`${b(t)} subscription already exists: ${s}`);return A(h(),c=>x(c,{...i[r].row,label:s,labelKey:f(s)})),w(t,a)})}o(vt,"renameSubscription");async function B(t,n){return E(t,async(e,a,i)=>{const r=O(a,n);if(r<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const s=a[r].row;return A(h(),c=>{g(c,"DELETE FROM managed_accounts WHERE provider_id=? AND id=?").run(t,s.id),C(c,t,s.id)}),delete e.accounts[s.id],i(e),w(t,e)})}o(B,"remove");async function te(t,n){return B(t,n)}o(te,"removeManagedAccount");async function ee(t,n){return B(t,n)}o(ee,"removeSubscription");async function $t(t){return E(t,async(n,e,a)=>{A(h(),i=>{g(i,"DELETE FROM managed_accounts WHERE provider_id=?").run(t),C(i,t)});for(const{row:i}of e)delete n.accounts[i.id];return a(n),e.length})}o($t,"removeAllManagedAccounts");async function ne(t,n){return n===void 0?(await $t(t),[]):B(t,n)}o(ne,"removeManagedProvider");async function ae(t,n){return E(t,async(e,a)=>{const i=O(a,n);if(i<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);return A(h(),r=>{[a[i],...a.filter((c,u)=>u!==i)].forEach((c,u)=>x(r,{...c.row,position:u}))}),w(t,e)})}o(ae,"promoteSubscription");async function it(t,n,e){return A(h(),a=>{const i=P(a,t,n);if(!i)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const r=e(i);r&&x(a,r)}),D(t)}o(it,"mutateHealth");async function ie(t,n,e=Date.now()){return it(t,n,a=>({...a,lastAttemptAt:Math.max(a.lastAttemptAt,e)}))}o(ie,"markSubscriptionAttempt");async function re(t,n,e,a,i=Date.now()){return E(t,async(r,s)=>{const c=s.find(({entry:u})=>f(u.label)===f(n));if(!c)throw new Error(`Unknown ${b(t)} subscription: ${n}`);return c.entry.refresh!==e.refresh||c.entry.access!==e.access||A(h(),u=>x(u,{...c.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(c.row.lastAttemptAt,i),rateLimitedUntil:Math.max(c.row.rateLimitedUntil,i+Math.max(0,a))})),w(t,r)})}o(re,"markSubscriptionAuthenticationFailed");async function oe(t,n,e,a=Date.now(),i=Date.now(),r={kind:"account"}){return r.kind==="account"?it(t,n,s=>i<s.lastRateLimitedAt?null:{...s,lastAttemptAt:Math.max(s.lastAttemptAt,a),lastRateLimitedAt:i,rateLimitedUntil:i+Math.max(0,e)}):(A(h(),s=>{const c=P(s,t,n);if(!c)throw new Error(`Unknown ${b(t)} subscription: ${n}`);x(s,{...c,lastAttemptAt:Math.max(c.lastAttemptAt,a)});const u=T(s,t).get(c.id)?.find(l=>l.family===r.family);u&&i<u.lastRateLimitedAt||at(s,t,c.id,r.family,i+Math.max(0,e),i)}),D(t))}o(oe,"markSubscriptionRateLimited");async function se(t,n,e=Date.now(),a){const i=j(a);return A(h(),r=>{const s=P(r,t,n);if(!s)throw new Error(`Unknown ${b(t)} subscription: ${n}`);if(e>=s.lastRateLimitedAt&&x(r,{...s,authFailure:void 0,lastAttemptAt:Math.max(s.lastAttemptAt,e),rateLimitedUntil:0}),i===void 0)return;const c=T(r,t).get(s.id)?.find(u=>u.family===i);c&&e>=c.lastRateLimitedAt&&g(r,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=? AND account_id=? AND family=?").run(t,s.id,i)}),D(t)}o(se,"markSubscriptionSuccess");async function ce(t,n,e){return E(t,async(a,i,r)=>{const s=O(i,n);if(s<0)throw new Error(`${b(t)} subscription "${n}" no longer exists`);const c=i[s];if(c.entry.expires>Date.now())return c.entry;const u=await e(c.entry.refresh,c.entry.clientId),l=et(nt(t,c.entry.label,u),u.clientId??c.entry.clientId);return a.accounts[c.row.id]={...a.accounts[c.row.id],...l},r(a),{...c.entry,...l}})}o(ce,"refreshSubscriptionCredential");function Ft(t){const n=t,e=[n?.errorMessage,n?.message,t instanceof Error?t.message:""].filter(i=>typeof i=="string").join(" ");return(typeof n?.status=="number"?n.status:Number(e.match(/\bstatus[=:]\s*(\d+)/i)?.[1]))===400&&/invalid_grant|refresh token .*invalid|refresh token .*not found/i.test(e)}o(Ft,"isInvalidRefreshTokenError");async function le(t,n,e){return E(t,async(a,i,r)=>{const s=new Set;let c;for(;s.size<i.length;){const u=n(i.filter(({entry:m})=>!s.has(m.label)).map(({entry:m})=>m));if(!u)break;const l=i.find(({entry:m})=>m.label===u.label);s.add(l.entry.label);try{const m=await e(l.entry);if(m){const d=et(nt(t,l.entry.label,m),m.clientId??l.entry.clientId);return a.accounts[l.row.id]={...a.accounts[l.row.id],...d},r(a),{...l.entry,...d}}return l.entry}catch(m){if(!Ft(m))throw m;c=m;const d=Date.now();A(h(),y=>x(y,{...l.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(l.row.lastAttemptAt,d),rateLimitedUntil:Math.max(l.row.rateLimitedUntil,d+At)}))}}if(c)throw c})}o(le,"modifyElectedSubscription");async function St(t,n){const e=n.accountId?.trim(),a=n.email?.trim();if(t!==H)return{...e?{accountId:e}:{},...a?{email:a}:{}};try{const i=await fetch("https://api.anthropic.com/api/oauth/profile",{headers:{Authorization:`Bearer ${n.access}`,Accept:"application/json"},signal:AbortSignal.timeout(3e3)});if(!i.ok)return e?{accountId:e}:{};const r=await i.json(),s=typeof r.account?.uuid=="string"&&r.account.uuid.trim()?r.account.uuid.trim():void 0,c=typeof r.account?.email=="string"&&r.account.email.trim()?r.account.email.trim():void 0;return{...s?{accountId:s}:{},...c?{email:c}:{}}}catch{return e?{accountId:e}:{}}}o(St,"enrichManagedLoginIdentity");async function ue(t,n,e){const a=await St(t,e);return E(t,async(i,r,s)=>{const c=e.autoLabel?void 0:n?.trim(),u=_({refresh:e.refresh,accountId:a.accountId}),l=(c?r.find(({entry:p})=>f(p.label)===f(c)):void 0)??r.find(({entry:p})=>_(p)===u);let m=l?.entry.label??c;if(!m&&e.autoLabel){const p=a.email||n?.trim()||`Account ${r.length+1}`;m=p;for(let S=2;r.some(({entry:L})=>f(L.label)===f(m));S++)m=`${p} (${S})`}if(!m)throw new Error(`${b(t)} subscription label cannot be empty`);const d=N(t,{label:m,refresh:e.refresh,access:e.access,expires:e.expires,...a.accountId?{accountId:a.accountId}:{},...e.clientId?{clientId:e.clientId}:{},rateLimitedUntil:0,lastAttemptAt:Date.now(),lastRateLimitedAt:e.lastRateLimitedAt});if(!l){if(r.some(({entry:L})=>f(L.label)===f(d.label)))throw new R(`${b(t)} subscription already exists: ${d.label}`);const p=r.find(({entry:L})=>_(L)===_(d));if(_(d)&&p)throw new R(`This ${b(t)} account is already added as \u201C${p.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const S=U();return i.accounts[S]=I(d),s(i),$(t,()=>A(h(),L=>M(L,t,{providerId:t,id:S,label:d.label,labelKey:f(d.label),position:r.length,lastAttemptAt:d.lastAttemptAt,lastRateLimitedAt:d.lastRateLimitedAt,rateLimitedUntil:d.rateLimitedUntil}))),w(t,i)}const y=l.row,F=r.find(({entry:p})=>p.label!==l.entry.label&&_(p)===_(d));if(_(d)&&F)throw new R(`This ${b(t)} account is already added as \u201C${F.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);i.accounts[y.id]=I(d),s(i);const k=d.lastRateLimitedAt>=y.lastRateLimitedAt;return $(t,()=>A(h(),p=>{x(p,{...y,authFailure:void 0,lastAttemptAt:Math.max(y.lastAttemptAt,d.lastAttemptAt),lastRateLimitedAt:k?d.lastRateLimitedAt:y.lastRateLimitedAt,rateLimitedUntil:k?0:y.rateLimitedUntil}),tt(l.entry,d)&&C(p,t,y.id)})),w(t,i)})}o(ue,"commitManagedLogin");function Ut(t,n=Date.now()){if(!t?.trim())return;const e=Number(t.trim());if(Number.isFinite(e))return Math.max(0,Math.round(e*1e3));const a=Date.parse(t);return Number.isFinite(a)?Math.max(0,a-n):void 0}o(Ut,"parseRetryAfterHeader");function Nt(t){if(!t)return;const n=o(a=>t instanceof Headers?t.get(a)??void 0:Object.entries(t).find(([i])=>i.toLowerCase()===a)?.[1],"get"),e=n("retry-after-ms");return e!==void 0&&e.trim()!==""&&Number.isFinite(Number(e))?Math.max(0,Number(e)):Ut(n("retry-after"))}o(Nt,"retryAfterMsFromHeaders");function de(t){if(!t||typeof t!="object"||!("headers"in t))return;const n=t.headers;if(!(!(n instanceof Headers)&&(!n||typeof n!="object"||Array.isArray(n))))return Nt(n)}o(de,"retryAfterMsFromError");function Dt(){const t=ut();if(t)return t.rotation;const n=g(ft(ht(h())),"SELECT default_fallback_strength,revert_when_available FROM rotation_settings WHERE singleton=1").get();return{defaultFallbackStrength:X(n?.default_fallback_strength)??J,revertWhenAvailable:n?.revert_when_available===void 0||n.revert_when_available===null?!0:!!n.revert_when_available}}o(Dt,"readRotationConfig");function me(t){const n=Dt(),e={defaultFallbackStrength:X(t.defaultFallbackStrength)??n.defaultFallbackStrength??J,revertWhenAvailable:t.revertWhenAvailable??n.revertWhenAvailable??!0};return A(h(),a=>g(a,"INSERT INTO rotation_settings(singleton,default_fallback_strength,revert_when_available) VALUES(1,?,?) ON CONFLICT(singleton) DO UPDATE SET default_fallback_strength=excluded.default_fallback_strength,revert_when_available=excluded.revert_when_available").run(e.defaultFallbackStrength,e.revertWhenAvailable?1:0)),e}o(me,"writeRotationConfig");function fe(t,n=Date.now(),e){const a=D(t),i=a.filter(s=>s.authFailure===void 0&&Et(s,e)<=n),r=b(t);return i.length===0?`${r} pool exhausted`:`${r} ${i[0]?.label??t} (${i.length}/${a.length} ready)`}o(fe,"formatStatusLine");export{H as ANTHROPIC_PROVIDER_ID,R as DuplicateSubscriptionError,At as INVALID_REFRESH_TOKEN_BACKOFF_MS,qt as MANAGED_PROVIDER_IDS,G as OPENAI_CHATGPT_PROVIDER_ID,z as OPENAI_CODEX_PROVIDER_ID,wt as activeCooldowns,Rt as addSubscription,ue as commitManagedLogin,Et as coolingUntilFor,St as enrichManagedLoginIdentity,zt as formatCooldowns,fe as formatStatusLine,b as getProviderLabel,Ft as isInvalidRefreshTokenError,Jt as isManagedProvider,ie as markSubscriptionAttempt,re as markSubscriptionAuthenticationFailed,oe as markSubscriptionRateLimited,se as markSubscriptionSuccess,le as modifyElectedSubscription,Qt as openAIDeviceId,Ut as parseRetryAfterHeader,ae as promoteSubscription,Dt as readRotationConfig,D as readSubscriptionPool,ce as refreshSubscriptionCredential,$t as removeAllManagedAccounts,te as removeManagedAccount,ne as removeManagedProvider,ee as removeSubscription,vt as renameSubscription,Xt as resolveProviderCandidates,de as retryAfterMsFromError,Nt as retryAfterMsFromHeaders,Gt as subscriptionNeedsSuccessWrite,Zt as upsertSubscription,It as upsertSubscriptionWithUniqueAccount,me as writeRotationConfig,Yt as writeSubscriptionPool};
1
+ var rt=Object.defineProperty;var o=(t,n)=>rt(t,"name",{value:n,configurable:!0});import{prepared as g}from"./sqlite-statements.js";import{createHash as ot,randomUUID as N}from"node:crypto";import{mkdirSync as st,readFileSync as ct,writeFileSync as lt}from"node:fs";import{join as q}from"node:path";import{brokerConfigSnapshot as ut}from"./config.js";import{LockedJsonFile as dt}from"./locked-file.js";import{secretsDir as mt}from"./layout.js";import{openScopedStore as ft}from"./scoped-state/db.js";import{providerStore as ht}from"./scoped-state/paths.js";import{providerModelCooldowns as T,providerModelCooldownsFor as bt,providerRowByReference as P,providerRows as pt,providerWrite as A}from"./scoped-state/providers.js";import{modelCooldownFamily as j}from"./rate-limit-scope.js";const W="anthropic",z="openai-codex",G="openai",At=600*1e3,yt=720*60*60*1e3,zt=[W,z,G],J="strong",Q={anthropic:{label:"Claude",credentials:"anthropic-credentials.json"},"openai-codex":{label:"OpenAI Codex",credentials:"openai-codex-credentials.json"},openai:{label:"ChatGPT",credentials:"openai-credentials.json"}};function I(t){return{refresh:t.refresh,access:t.access,expires:t.expires,...t.accountId?{accountId:t.accountId}:{},...t.clientId?{clientId:t.clientId}:{}}}o(I,"stored");function H(t){return typeof t=="string"&&t.trim()?t.trim():void 0}o(H,"optionalString");function h(){return mt()}o(h,"agentDir");function K(t){return q(h(),Q[t].credentials)}o(K,"credentialFile");function X(t){return t==="ultra"||t==="strong"||t==="medium"||t==="light"?t:void 0}o(X,"normalizeStrength");function f(t){return t.toLowerCase()}o(f,"key");function _t(t){const n=typeof t.label=="string"?t.label.trim():"",e=typeof t.refresh=="string"?t.refresh.trim():"",a=typeof t.access=="string"?t.access.trim():"",i=Number(t.expires);if(!n||!e||!a||!Number.isFinite(i))return;const r=H(t.accountId),s=H(t.clientId);return{label:n,refresh:e,access:a,expires:i,rateLimitedUntil:Number.isFinite(Number(t.rateLimitedUntil))?Number(t.rateLimitedUntil):0,lastAttemptAt:Number.isFinite(Number(t.lastAttemptAt))?Number(t.lastAttemptAt):0,lastRateLimitedAt:Number.isFinite(Number(t.lastRateLimitedAt))?Number(t.lastRateLimitedAt):0,...t.authFailure==="invalid_grant"?{authFailure:"invalid_grant"}:{},...r?{accountId:r}:{},...s?{clientId:s}:{},...Y(t.modelCooldowns)?{modelCooldowns:Y(t.modelCooldowns)}:{}}}o(_t,"credential");function Y(t){if(!t||typeof t!="object"||Array.isArray(t))return;const n=Object.entries(t).filter(([e,a])=>e.trim()!==""&&Number.isFinite(Number(a))).map(([e,a])=>[e,Number(a)]);return n.length?Object.fromEntries(n):void 0}o(Y,"modelCooldowns");function U(t,n){if(!n.label?.trim())throw new Error(`${b(t)} subscription label cannot be empty`);const e=_t(n);if(!e)throw new Error(`Refusing to persist incomplete ${b(t)} subscription "${n.label.trim()}"; pool rows require both OAuth tokens.`);return e}o(U,"assertCredential");function Z(t,n){if(!n||n.trim()==="")return{version:1,accounts:{}};let e;try{e=JSON.parse(n)}catch(a){throw new Error(`managed credential file ${t} is corrupt: ${a instanceof Error?a.message:String(a)}`)}if(typeof e!="object"||e===null||Array.isArray(e)||e.version!==1||typeof e.accounts!="object"||e.accounts===null||Array.isArray(e.accounts))throw new Error(`managed credential file ${t} is corrupt`);return e}o(Z,"parseCredentials");function gt(t,n,e=[]){const a=n.accounts[t.id];if(!a||typeof a.refresh!="string"||typeof a.access!="string"||!Number.isFinite(a.expires))throw new Error(`managed ${t.providerId} account ${t.id} has no credential record`);return{label:t.label,refresh:a.refresh,access:a.access,expires:a.expires,rateLimitedUntil:t.rateLimitedUntil,lastAttemptAt:t.lastAttemptAt,lastRateLimitedAt:t.lastRateLimitedAt,...t.authFailure?{authFailure:t.authFailure}:{},...a.accountId?{accountId:a.accountId}:{},...a.clientId?{clientId:a.clientId}:{},...e.length?{modelCooldowns:Object.fromEntries(e.map(i=>[i.family,i.rateLimitedUntil]))}:{}}}o(gt,"toPublic");function V(t,n){const e=bt(h(),t);return pt(h(),t).map(a=>({row:a,entry:gt(a,n,e.get(a.id))}))}o(V,"rowsWithCredentials");function v(t){return new dt(K(t),!0,`{"version":1,"accounts":{}}
2
+ `)}o(v,"managedCredentialFile");function Et(t){return Z(K(t),v(t).readUnlocked())}o(Et,"readFile");function tt(t,n){const e=t.accountId?.trim(),a=n.accountId?.trim();return!!(e&&a&&e!==a)}o(tt,"differentAccount");function _(t){return t.accountId?.trim()||(t.refresh.trim()?`refresh:${ot("sha256").update(t.refresh.trim()).digest("hex")}`:void 0)}o(_,"identity");function et(t,n){const e=H(n);return e?{...t,clientId:e}:t}o(et,"withClientId");function nt(t,n,e){const a=e.refresh?.trim()??"",i=e.access?.trim()??"",r=Number(e.expires);if(!a||!i||!Number.isFinite(r))throw new Error(`${b(t)} subscription "${n}" returned an incomplete credential; the stored credential was left unchanged.`);return{refresh:a,access:i,expires:r}}o(nt,"assertTokens");function wt(t,n){const e=j(n);return Math.max(t.rateLimitedUntil||0,e===void 0?0:t.modelCooldowns?.[e]??0)}o(wt,"coolingUntilFor");function xt(t,n=Date.now()){return[...(t.rateLimitedUntil||0)>n?[{scope:"account",until:t.rateLimitedUntil}]:[],...Object.entries(t.modelCooldowns??{}).filter(([,e])=>e>n).map(([e,a])=>({scope:e,until:a}))]}o(xt,"activeCooldowns");function Gt(t,n=Date.now()){const e=xt(t,n).map(({scope:a,until:i})=>`${a==="account"?"":`${a} `}cooldown ${Lt(i-n)}`);return e.length?e.join(", "):void 0}o(Gt,"formatCooldowns");function Lt(t){const n=Math.max(1,Math.ceil(t/6e4));if(n<60)return`${n}m`;const e=Math.floor(n/60);return e<24?`${e}h${n%60?` ${n%60}m`:""}`:`${Math.floor(e/24)}d${e%24?` ${e%24}h`:""}`}o(Lt,"formatWait");function Rt(t,n){const e=j(n);return e!==void 0&&t.modelCooldowns?.[e]!==void 0}o(Rt,"hasModelCooldownRow");function Jt(t,n){return!!(t.rateLimitedUntil||t.authFailure)||Rt(t,n)}o(Jt,"subscriptionNeedsSuccessWrite");function Qt(t){return t===W||t===z||t===G}o(Qt,"isManagedProvider");function Xt(){const t=q(h(),"openai-device-id");try{const e=ct(t,"utf8").trim();if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))return e.toLowerCase()}catch{}const n=N();return st(h(),{recursive:!0,mode:448}),lt(t,`${n}
3
+ `,{mode:384}),n}o(Xt,"openAIDeviceId");function b(t){return Q[t].label}o(b,"getProviderLabel");function Yt(t,n=Date.now(),e={}){const a=[],i=[];for(let r=0;r<t.length;r++){const s=t[r],c=s.registered?s.automaticFallback===!1?"non-managed-fallback":s.authenticated?(s.coolingUntil??0)>n?"cooling":void 0:"unauthenticated":"unregistered";if(!c)a.push(s);else if(i.push({candidate:s,reason:c}),e.pinnedToFirst&&r===0&&c!=="cooling")break}return{candidates:a,rejected:i}}o(Yt,"resolveProviderCandidates");class R extends Error{static{o(this,"DuplicateSubscriptionError")}constructor(n){super(n),this.name="DuplicateSubscriptionError"}}function D(t){return V(t,Et(t)).map(({entry:n})=>n)}o(D,"readSubscriptionPool");async function E(t,n){const e=K(t);return v(t).withLockAsync(async(a,i,r)=>{const s=Z(e,a);return{result:await n(s,V(t,s),c=>r(`${JSON.stringify(c)}
4
+ `))}})}o(E,"locked");function k(t,n,e){g(t,"INSERT INTO managed_accounts(provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure) VALUES(?,?,?,?,?,?,?,?,?)").run(n,e.id,e.label,e.labelKey,e.position,e.lastAttemptAt,e.lastRateLimitedAt,e.rateLimitedUntil,e.authFailure??null)}o(k,"insert");function x(t,n){g(t,"UPDATE managed_accounts SET label=?,label_key=?,position=?,last_attempt_at=?,last_rate_limited_at=?,rate_limited_until=?,auth_failure=? WHERE provider_id=? AND id=?").run(n.label,n.labelKey,n.position,n.lastAttemptAt,n.lastRateLimitedAt,n.rateLimitedUntil,n.authFailure??null,n.providerId,n.id)}o(x,"update");function at(t,n,e,a,i,r){g(t,"INSERT INTO managed_account_model_cooldowns(provider_id,account_id,family,rate_limited_until,last_rate_limited_at) VALUES(?,?,?,?,?) ON CONFLICT(provider_id,account_id,family) DO UPDATE SET rate_limited_until=excluded.rate_limited_until,last_rate_limited_at=excluded.last_rate_limited_at").run(n,e,a,i,r)}o(at,"upsertModelCooldown");function C(t,n,e){e===void 0?g(t,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=?").run(n):g(t,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=? AND account_id=?").run(n,e)}o(C,"deleteModelCooldowns");function w(t,n){return V(t,n).map(({entry:e})=>e)}o(w,"publicPool");function $(t,n){try{n()}catch(e){throw new Error(`managed ${b(t)} credential file was published, but its SQL membership update failed; SQL membership remains authoritative: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}o($,"writeMembershipAfterCredentialPublish");async function Zt(t,n){return E(t,async(e,a,i)=>{const r=n.map(u=>U(t,u)),s=r.map((u,l)=>({entry:u,id:a[l]?.row.id??N()})),c={};for(const{entry:u,id:l}of s)c[l]=I(u);return i({version:1,accounts:c}),$(t,()=>A(h(),u=>{const l=T(u,t);g(u,"DELETE FROM managed_accounts WHERE provider_id=?").run(t),g(u,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=?").run(t),s.forEach(({entry:m,id:d},y)=>{k(u,t,{providerId:t,id:d,label:m.label,labelKey:f(m.label),position:y,lastAttemptAt:m.lastAttemptAt,lastRateLimitedAt:m.lastRateLimitedAt,rateLimitedUntil:m.rateLimitedUntil,...m.authFailure?{authFailure:m.authFailure}:{}});for(const[F,M]of Object.entries(m.modelCooldowns??{}))at(u,t,d,F,M,l.get(d)?.find(p=>p.family===F)?.lastRateLimitedAt??m.lastRateLimitedAt)})})),r})}o(Zt,"writeSubscriptionPool");function O(t,n){const e=n.trim().toLowerCase(),a=Number.parseInt(e,10);return Number.isFinite(a)&&String(a)===e&&a>=1&&a<=t.length?a-1:t.findIndex(({entry:i})=>f(i.label)===e)}o(O,"resolve");async function It(t,n){const e=U(t,n);return E(t,async(a,i,r)=>{if(i.some(({entry:l})=>f(l.label)===f(e.label)))throw new R(`${b(t)} subscription already exists: ${e.label}`);const s=_(e),c=i.find(({entry:l})=>_(l)===s);if(s&&c)throw new R(`This ${b(t)} account is already added as \u201C${c.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const u=N();return a.accounts[u]=I(e),r(a),$(t,()=>A(h(),l=>k(l,t,{providerId:t,id:u,label:e.label,labelKey:f(e.label),position:i.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}}))),w(t,a)})}o(It,"addSubscription");async function vt(t,n){const e=U(t,n);return D(t).find(i=>f(i.label)===f(e.label))?$t(t,e):It(t,e)}o(vt,"upsertSubscription");async function $t(t,n){const e=U(t,n);return E(t,async(a,i,r)=>{const s=i.findIndex(({entry:d})=>f(d.label)===f(e.label)),c=i[s],u=i.find(({entry:d},y)=>y!==s&&_(d)===_(e));if(_(e)&&u)throw new R(`This ${b(t)} account is already added as \u201C${u.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);if(!c){const d=N();return a.accounts[d]=I(e),r(a),$(t,()=>A(h(),y=>k(y,t,{providerId:t,id:d,label:e.label,labelKey:f(e.label),position:i.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil}))),w(t,a)}const l=c.row,m=e.lastRateLimitedAt>=l.lastRateLimitedAt;return a.accounts[l.id]=I(e),r(a),$(t,()=>A(h(),d=>{x(d,{...l,label:e.label,labelKey:f(e.label),lastAttemptAt:Math.max(l.lastAttemptAt,e.lastAttemptAt),lastRateLimitedAt:m?e.lastRateLimitedAt:l.lastRateLimitedAt,rateLimitedUntil:m?e.rateLimitedUntil:l.rateLimitedUntil}),tt(c.entry,e)&&C(d,t,l.id)})),w(t,a)})}o($t,"upsertSubscriptionWithUniqueAccount");async function te(t,n,e){return E(t,async(a,i)=>{const r=O(i,n);if(r<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const s=e.trim();if(!s)throw new Error(`${b(t)} subscription label cannot be empty`);if(i.some((c,u)=>u!==r&&f(c.entry.label)===f(s)))throw new R(`${b(t)} subscription already exists: ${s}`);return A(h(),c=>x(c,{...i[r].row,label:s,labelKey:f(s)})),w(t,a)})}o(te,"renameSubscription");async function B(t,n){return E(t,async(e,a,i)=>{const r=O(a,n);if(r<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const s=a[r].row;return A(h(),c=>{g(c,"DELETE FROM managed_accounts WHERE provider_id=? AND id=?").run(t,s.id),C(c,t,s.id)}),delete e.accounts[s.id],i(e),w(t,e)})}o(B,"remove");async function ee(t,n){return B(t,n)}o(ee,"removeManagedAccount");async function ne(t,n){return B(t,n)}o(ne,"removeSubscription");async function Ft(t){return E(t,async(n,e,a)=>{A(h(),i=>{g(i,"DELETE FROM managed_accounts WHERE provider_id=?").run(t),C(i,t)});for(const{row:i}of e)delete n.accounts[i.id];return a(n),e.length})}o(Ft,"removeAllManagedAccounts");async function ae(t,n){return n===void 0?(await Ft(t),[]):B(t,n)}o(ae,"removeManagedProvider");async function ie(t,n){return E(t,async(e,a)=>{const i=O(a,n);if(i<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);return A(h(),r=>{[a[i],...a.filter((c,u)=>u!==i)].forEach((c,u)=>x(r,{...c.row,position:u}))}),w(t,e)})}o(ie,"promoteSubscription");async function it(t,n,e){return A(h(),a=>{const i=P(a,t,n);if(!i)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const r=e(i);r&&x(a,r)}),D(t)}o(it,"mutateHealth");async function re(t,n,e=Date.now()){return it(t,n,a=>({...a,lastAttemptAt:Math.max(a.lastAttemptAt,e)}))}o(re,"markSubscriptionAttempt");async function oe(t,n,e,a,i=Date.now()){return E(t,async(r,s)=>{const c=s.find(({entry:u})=>f(u.label)===f(n));if(!c)throw new Error(`Unknown ${b(t)} subscription: ${n}`);return c.entry.refresh!==e.refresh||c.entry.access!==e.access||A(h(),u=>x(u,{...c.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(c.row.lastAttemptAt,i),rateLimitedUntil:Math.max(c.row.rateLimitedUntil,i+Math.max(0,a))})),w(t,r)})}o(oe,"markSubscriptionAuthenticationFailed");async function se(t,n,e,a=Date.now(),i=Date.now(),r={kind:"account"}){return e=Math.min(Math.max(0,e),At),r.kind==="account"?it(t,n,s=>i<s.lastRateLimitedAt?null:{...s,lastAttemptAt:Math.max(s.lastAttemptAt,a),lastRateLimitedAt:i,rateLimitedUntil:i+e}):(A(h(),s=>{const c=P(s,t,n);if(!c)throw new Error(`Unknown ${b(t)} subscription: ${n}`);x(s,{...c,lastAttemptAt:Math.max(c.lastAttemptAt,a)});const u=T(s,t).get(c.id)?.find(l=>l.family===r.family);u&&i<u.lastRateLimitedAt||at(s,t,c.id,r.family,i+e,i)}),D(t))}o(se,"markSubscriptionRateLimited");async function ce(t,n,e=Date.now(),a){const i=j(a);return A(h(),r=>{const s=P(r,t,n);if(!s)throw new Error(`Unknown ${b(t)} subscription: ${n}`);if(e>=s.lastRateLimitedAt&&x(r,{...s,authFailure:void 0,lastAttemptAt:Math.max(s.lastAttemptAt,e),rateLimitedUntil:0}),i===void 0)return;const c=T(r,t).get(s.id)?.find(u=>u.family===i);c&&e>=c.lastRateLimitedAt&&g(r,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=? AND account_id=? AND family=?").run(t,s.id,i)}),D(t)}o(ce,"markSubscriptionSuccess");async function le(t,n,e){return E(t,async(a,i,r)=>{const s=O(i,n);if(s<0)throw new Error(`${b(t)} subscription "${n}" no longer exists`);const c=i[s];if(c.entry.expires>Date.now())return c.entry;const u=await e(c.entry.refresh,c.entry.clientId),l=et(nt(t,c.entry.label,u),u.clientId??c.entry.clientId);return a.accounts[c.row.id]={...a.accounts[c.row.id],...l},r(a),{...c.entry,...l}})}o(le,"refreshSubscriptionCredential");function St(t){const n=t,e=[n?.errorMessage,n?.message,t instanceof Error?t.message:""].filter(i=>typeof i=="string").join(" ");return(typeof n?.status=="number"?n.status:Number(e.match(/\bstatus[=:]\s*(\d+)/i)?.[1]))===400&&/invalid_grant|refresh token .*invalid|refresh token .*not found/i.test(e)}o(St,"isInvalidRefreshTokenError");async function ue(t,n,e){return E(t,async(a,i,r)=>{const s=new Set;let c;for(;s.size<i.length;){const u=n(i.filter(({entry:m})=>!s.has(m.label)).map(({entry:m})=>m));if(!u)break;const l=i.find(({entry:m})=>m.label===u.label);s.add(l.entry.label);try{const m=await e(l.entry);if(m){const d=et(nt(t,l.entry.label,m),m.clientId??l.entry.clientId);return a.accounts[l.row.id]={...a.accounts[l.row.id],...d},r(a),{...l.entry,...d}}return l.entry}catch(m){if(!St(m))throw m;c=m;const d=Date.now();A(h(),y=>x(y,{...l.row,authFailure:"invalid_grant",lastAttemptAt:Math.max(l.row.lastAttemptAt,d),rateLimitedUntil:Math.max(l.row.rateLimitedUntil,d+yt)}))}}if(c)throw c})}o(ue,"modifyElectedSubscription");async function Nt(t,n){const e=n.accountId?.trim(),a=n.email?.trim();if(t!==W)return{...e?{accountId:e}:{},...a?{email:a}:{}};try{const i=await fetch("https://api.anthropic.com/api/oauth/profile",{headers:{Authorization:`Bearer ${n.access}`,Accept:"application/json"},signal:AbortSignal.timeout(3e3)});if(!i.ok)return e?{accountId:e}:{};const r=await i.json(),s=typeof r.account?.uuid=="string"&&r.account.uuid.trim()?r.account.uuid.trim():void 0,c=typeof r.account?.email=="string"&&r.account.email.trim()?r.account.email.trim():void 0;return{...s?{accountId:s}:{},...c?{email:c}:{}}}catch{return e?{accountId:e}:{}}}o(Nt,"enrichManagedLoginIdentity");async function de(t,n,e){const a=await Nt(t,e);return E(t,async(i,r,s)=>{const c=e.autoLabel?void 0:n?.trim(),u=_({refresh:e.refresh,accountId:a.accountId}),l=(c?r.find(({entry:p})=>f(p.label)===f(c)):void 0)??r.find(({entry:p})=>_(p)===u);let m=l?.entry.label??c;if(!m&&e.autoLabel){const p=a.email||n?.trim()||`Account ${r.length+1}`;m=p;for(let S=2;r.some(({entry:L})=>f(L.label)===f(m));S++)m=`${p} (${S})`}if(!m)throw new Error(`${b(t)} subscription label cannot be empty`);const d=U(t,{label:m,refresh:e.refresh,access:e.access,expires:e.expires,...a.accountId?{accountId:a.accountId}:{},...e.clientId?{clientId:e.clientId}:{},rateLimitedUntil:0,lastAttemptAt:Date.now(),lastRateLimitedAt:e.lastRateLimitedAt});if(!l){if(r.some(({entry:L})=>f(L.label)===f(d.label)))throw new R(`${b(t)} subscription already exists: ${d.label}`);const p=r.find(({entry:L})=>_(L)===_(d));if(_(d)&&p)throw new R(`This ${b(t)} account is already added as \u201C${p.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const S=N();return i.accounts[S]=I(d),s(i),$(t,()=>A(h(),L=>k(L,t,{providerId:t,id:S,label:d.label,labelKey:f(d.label),position:r.length,lastAttemptAt:d.lastAttemptAt,lastRateLimitedAt:d.lastRateLimitedAt,rateLimitedUntil:d.rateLimitedUntil}))),w(t,i)}const y=l.row,F=r.find(({entry:p})=>p.label!==l.entry.label&&_(p)===_(d));if(_(d)&&F)throw new R(`This ${b(t)} account is already added as \u201C${F.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);i.accounts[y.id]=I(d),s(i);const M=d.lastRateLimitedAt>=y.lastRateLimitedAt;return $(t,()=>A(h(),p=>{x(p,{...y,authFailure:void 0,lastAttemptAt:Math.max(y.lastAttemptAt,d.lastAttemptAt),lastRateLimitedAt:M?d.lastRateLimitedAt:y.lastRateLimitedAt,rateLimitedUntil:M?0:y.rateLimitedUntil}),tt(l.entry,d)&&C(p,t,y.id)})),w(t,i)})}o(de,"commitManagedLogin");function Ut(t,n=Date.now()){if(!t?.trim())return;const e=Number(t.trim());if(Number.isFinite(e))return Math.max(0,Math.round(e*1e3));const a=Date.parse(t);return Number.isFinite(a)?Math.max(0,a-n):void 0}o(Ut,"parseRetryAfterHeader");function Dt(t){if(!t)return;const n=o(a=>t instanceof Headers?t.get(a)??void 0:Object.entries(t).find(([i])=>i.toLowerCase()===a)?.[1],"get"),e=n("retry-after-ms");return e!==void 0&&e.trim()!==""&&Number.isFinite(Number(e))?Math.max(0,Number(e)):Ut(n("retry-after"))}o(Dt,"retryAfterMsFromHeaders");function me(t){if(!t||typeof t!="object"||!("headers"in t))return;const n=t.headers;if(!(!(n instanceof Headers)&&(!n||typeof n!="object"||Array.isArray(n))))return Dt(n)}o(me,"retryAfterMsFromError");function Mt(){const t=ut();if(t)return t.rotation;const n=g(ft(ht(h())),"SELECT default_fallback_strength,revert_when_available FROM rotation_settings WHERE singleton=1").get();return{defaultFallbackStrength:X(n?.default_fallback_strength)??J,revertWhenAvailable:n?.revert_when_available===void 0||n.revert_when_available===null?!0:!!n.revert_when_available}}o(Mt,"readRotationConfig");function fe(t){const n=Mt(),e={defaultFallbackStrength:X(t.defaultFallbackStrength)??n.defaultFallbackStrength??J,revertWhenAvailable:t.revertWhenAvailable??n.revertWhenAvailable??!0};return A(h(),a=>g(a,"INSERT INTO rotation_settings(singleton,default_fallback_strength,revert_when_available) VALUES(1,?,?) ON CONFLICT(singleton) DO UPDATE SET default_fallback_strength=excluded.default_fallback_strength,revert_when_available=excluded.revert_when_available").run(e.defaultFallbackStrength,e.revertWhenAvailable?1:0)),e}o(fe,"writeRotationConfig");function he(t,n=Date.now(),e){const a=D(t),i=a.filter(s=>s.authFailure===void 0&&wt(s,e)<=n),r=b(t);return i.length===0?`${r} pool exhausted`:`${r} ${i[0]?.label??t} (${i.length}/${a.length} ready)`}o(he,"formatStatusLine");export{W as ANTHROPIC_PROVIDER_ID,R as DuplicateSubscriptionError,yt as INVALID_REFRESH_TOKEN_BACKOFF_MS,zt as MANAGED_PROVIDER_IDS,At as MAX_RATE_LIMIT_COOLDOWN_MS,G as OPENAI_CHATGPT_PROVIDER_ID,z as OPENAI_CODEX_PROVIDER_ID,xt as activeCooldowns,It as addSubscription,de as commitManagedLogin,wt as coolingUntilFor,Nt as enrichManagedLoginIdentity,Gt as formatCooldowns,he as formatStatusLine,b as getProviderLabel,St as isInvalidRefreshTokenError,Qt as isManagedProvider,re as markSubscriptionAttempt,oe as markSubscriptionAuthenticationFailed,se as markSubscriptionRateLimited,ce as markSubscriptionSuccess,ue as modifyElectedSubscription,Xt as openAIDeviceId,Ut as parseRetryAfterHeader,ie as promoteSubscription,Mt as readRotationConfig,D as readSubscriptionPool,le as refreshSubscriptionCredential,Ft as removeAllManagedAccounts,ee as removeManagedAccount,ae as removeManagedProvider,ne as removeSubscription,te as renameSubscription,Yt as resolveProviderCandidates,me as retryAfterMsFromError,Dt as retryAfterMsFromHeaders,Jt as subscriptionNeedsSuccessWrite,vt as upsertSubscription,$t as upsertSubscriptionWithUniqueAccount,fe as writeRotationConfig,Zt as writeSubscriptionPool};
@@ -1,6 +1,6 @@
1
- var Y=Object.defineProperty;var l=(t,e)=>Y(t,"name",{value:e,configurable:!0});import{gzipSync as V}from"node:zlib";import{browseNodes as F,graphNodes as X,listNodes as G,pruneNodes as Z,pruneToLimit as ee,rosterEdges as w,rosterNodes as te}from"../../../core/canvas/canvas.js";import{activeBackgroundBashJobs as ne}from"../../../core/bash-jobs.js";import{contextDir as se}from"../../../core/canvas/node-agent-paths.js";import{nowIso as $}from"../../../core/fs-utils.js";import{isPidPresent as oe}from"../../../core/canvas/pid.js";import{dashboardRowsAllFromSource as re,enrichRowsFromSource as ie}from"../../../core/canvas/render-source.js";import{localCanvasSource as P}from"../../../core/canvas/source.js";import{ticketCountsForNodes as q,ticketsAcrossCanvas as ae}from"../../../core/canvas/attention.js";import{buildCorpus as de,groupCorpus as le,resolveRef as ce}from"../../../core/canvas/history.js";import{listFocuses as D,reapEmptyNodes as ue}from"../../../core/runtime/placement.js";import{boundFleet as fe}from"../../../core/runtime/fleet.js";import{retentionHold as pe}from"../../reconcilers/live-obligation.js";import{notFound as me,usage as _}from"../../../core/errors.js";import{openDb as he}from"../../../core/canvas/db.js";import{prepared as _e}from"../../../core/sqlite-statements.js";import{view as ye}from"../../../core/canvas/tree.js";import{isFinishedWork as ge,isLiveStatus as be}from"../../../core/canvas/node-order.js";import{readNodeRecap as ve}from"../../../core/canvas/node-recap.js";import{readGoal as ke}from"../../../core/runtime/kickoff.js";import{faultSummary as Se}from"../../../core/canvas/status-glyph.js";import{listPendingAsks as we}from"../../../core/human/requests.js";import{toGraphNodeDTO as Ne}from"../map.js";import{actsAsPerson as Re,canReadNode as O,canReadRun as Ce}from"../router.js";import{ApiError as T}from"../../../api/errors.js";function N(t){return t.principal.class==="person"?null:new Set(G().filter(e=>O(t.principal,e.grantee??"app:terminal",e.node_id)).map(e=>e.node_id))}l(N,"visibleNodes");function H(t){return t.principal.class==="person"?null:new Set(G().filter(e=>e.run_id?Ce(t.principal,e.grantee??"app:terminal",e.node_id):O(t.principal,e.grantee??"app:terminal",e.node_id)).map(e=>e.node_id))}l(H,"visibleHistoryNodes");function Me(t){const e=N(t);return{status:200,body:{items:ae().filter(s=>e===null||e.has(s.node_id)).map(s=>({node_id:s.node_id,name:s.name,cwd:s.cwd,count:s.count,subjects:s.subjects}))}}}l(Me,"handleAttention");function Le(t){const e=t.body;if(e===null||typeof e!="object"||!Array.isArray(e.node_ids))throw _("attention count request requires node_ids: string[]");const n=e.node_ids;if(!n.every(o=>typeof o=="string"))throw _("attention count request node_ids must contain only strings");const s=N(t);return{status:200,body:{counts:q(n.filter(o=>s===null||s.has(o)))}}}l(Le,"handleAttentionCounts");function z(t){return Buffer.from(String(t),"utf8").toString("base64")}l(z,"encodeCursor");function x(t){const e=Number(Buffer.from(t,"base64").toString("utf8"));if(!Number.isInteger(e)||e<0)throw _(`invalid cursor: ${t}`);return e}l(x,"decodeCursor");function $e(t,e,n){const a=t.loadBody().split(`
2
- `);if(e.length>0){const c=a.findIndex(u=>{const f=u.toLowerCase();return e.some(i=>f.includes(i))});if(c!==-1)return a.slice(c,c+n).map(u=>u.trim()).filter(u=>u!=="").join(" \u23CE ")}const o=a.map(c=>c.trim()).filter(c=>c!=="").slice(0,n).join(" \u23CE ");return o!==""?o:t.title}l($e,"snippet");function Be(t,e,n){const s=[...t];return e==="relevance"&&n!==void 0?s.sort((a,o)=>(n.get(o.ref)??0)-(n.get(a.ref)??0)||o.tsMs-a.tsMs||a.ref.localeCompare(o.ref)):e==="oldest"?s.sort((a,o)=>a.tsMs-o.tsMs||a.ref.localeCompare(o.ref)):s.sort((a,o)=>o.tsMs-a.tsMs||a.ref.localeCompare(o.ref)),s}l(Be,"sortArtifacts");function Ee(t){if(t==null)return{};if(typeof t!="object")throw _("history search body must be a JSON object");return t}l(Ee,"parseSearchBody");function Ie(t){if(t===null||typeof t!="object")throw _("history grep body must be a JSON object");const e=t;if(typeof e.pattern!="string"||e.pattern==="")throw _("history grep body requires a non-empty pattern");return e}l(Ie,"parseGrepBody");function j(t,e){const n={cwd:t.cwd,allCwds:t.all_cwds===!0,under:t.under,nodes:t.nodes},s={types:t.types,kinds:t.kinds,statuses:t.statuses,sinceMs:t.since_ms,untilMs:t.until_ms,origins:t.origins,roles:t.roles,from:t.from},a=H(e);return de(n,s).filter(o=>a===null||a.has(o.nodeId))}l(j,"buildCorpusFromQuery");function Oe(t){if(t==null)return{};if(typeof t!="object")throw _("history stats body must be a JSON object");return t}l(Oe,"parseStatsBody");const J=["origin","node","kind","day","type","role"];function Te(t){const e=Oe(t.body),n=e.group_by??"type";if(!J.includes(n))throw _(`--group-by must be one of: ${J.join(", ")}; received: ${String(e.group_by)}`);const s=j(e,t),a=le(s,n);return{status:200,body:{group_by:n,groups:a,total:a.reduce((c,u)=>c+u.count,0),scanned:s.length}}}l(Te,"handleHistoryStats");function je(t){const e=Ie(t.body),n=Math.min(100,Math.max(1,e.limit??20)),s=j(e,t),a=e.cursor!==void 0?x(e.cursor):0;let o;try{o=new RegExp(e.pattern,"i")}catch{throw _(`invalid regex pattern: ${e.pattern}`)}const c=[];for(const r of s)r.loadBody().split(`
3
- `).forEach((y,b)=>{o.test(y)&&c.push({ref:r.ref,node:`${r.nodeName} (${r.nodeId})`,ts:r.ts,line:b+1,text:y.trim()})});c.sort((r,d)=>r.ref.localeCompare(d.ref)||r.line-d.line);const u=c.slice(a,a+n),f=a+n<c.length?z(a+n):null;return{status:200,body:{hits:u,next_cursor:f,total:c.length}}}l(je,"handleHistoryGrep");function Ae(t){const e=Ee(t.body),n=(e.query??"").trim(),s=e.weigh_body===!0,a=e.full===!0,o=Math.max(1,e.snippet_lines??1),c=Math.min(100,Math.max(1,e.limit??20)),u=e.per_node===!0;if(u&&(e.nodes===void 0||e.nodes.length===0))throw new T(400,"invalid_request","`per_node` requires a non-empty `nodes` list",{param:"nodes"});const f=j(e,t),i=e.cursor!==void 0?x(e.cursor):0,r=n.toLowerCase().split(/\s+/).filter(p=>p.length>0);let d=f,y;const b=e.sort??(r.length>0?"relevance":"recency");if(r.length>0&&b==="relevance"){y=new Map;const p=[];for(const h of f){const I=h.title.toLowerCase(),W=h.nodeName.toLowerCase(),Q=h.nodeDesc.toLowerCase(),A=s?h.loadBody().toLowerCase():null;let S=0;for(const L of r)I.includes(L)&&(S+=10),W.includes(L)&&(S+=5),Q.includes(L)&&(S+=3),A!==null&&A.includes(L)&&(S+=1);S>0&&(y.set(h.ref,S),p.push(h))}d=p}else r.length>0&&(d=f.filter(p=>{const h=`${p.title}
4
- ${p.nodeName}
5
- ${p.nodeDesc}${s?`
6
- `+p.loadBody():""}`.toLowerCase();return r.some(I=>h.includes(I))}));let g=Be(d,b,y);if(u){const p=new Set;g=g.filter(h=>!p.has(h.nodeId)&&p.add(h.nodeId))}const M=u?g:g.slice(i,i+c),k=!u&&i+c<g.length?z(i+c):null;return{status:200,body:{hits:M.map(p=>{const h={ref:p.ref,node:`${p.nodeName} (${p.nodeId})`,kind:p.nodeKind,source:p.source,ts:p.ts,...u?{node_id:p.nodeId}:{}};return b==="relevance"&&y!==void 0&&(h.score=y.get(p.ref)??0),a?h.body=p.loadBody():h.snippet=$e(p,r,o),h}),next_cursor:k,total:g.length}}}l(Ae,"handleHistorySearch");function Fe(t){const e=(t.query.get("ref")??"").trim();if(!e.includes(":"))throw _(`ref must be <node-id>:inbox/<n>, <node-id>:session, or <node-id>:session/<n>; received: ${e}`,{received:e,next:"Get a valid ref from `crtr canvas history search`. Reports and documents are `crtr canvas read <name>`."});const n=ce(e);if(n===null)throw me(`no inbox entry or message for ref: ${e}`,{ref:e,next:"Re-run `crtr canvas history search` for a current ref. Reports and documents are `crtr canvas read <name>`."});if(H(t)?.has(n.nodeId)===!1)throw new T(404,"not_found","artifact not found");const s=n.source,o=t.query.get("format")==="raw"&&n.raw!==null?n.raw:n.body;return{status:200,body:{ref:e,node:`${n.nodeName} (${n.nodeId})`,source:s,ts:n.ts,content:o}}}l(Fe,"handleHistoryRead");function Ge(t){for(const[s]of t.query)if(s!=="live")throw _(`unknown snapshot query parameter: ${s}`);const e=t.query.getAll("live");if(e.length>1)throw _("live must appear at most once");const n=e[0];if(n===void 0||n==="false"||n==="0")return!1;if(n==="true"||n==="1")return!0;throw _("live must be true or false")}l(Ge,"snapshotLiveOnly");async function Pe(t){const e=new Set(D().map(r=>r.node_id)),n=N(t),s=(await re(P,e,Ge(t))).filter(r=>n===null||n.has(r.node_id)),a=q(s.map(r=>r.node_id));await ie(P,s,a);const o=s.map(r=>{const d=r.srcRow,y=d?.host_kind??"tmux";return{node_id:r.node_id,name:r.name,title:r.handle??r.name,description:r.description??null,kind:r.kind,mode:r.mode,lifecycle:r.lifecycle??d?.lifecycle,status:r.status,cwd:r.cwd,profile_id:r.profile_id??null,parent:d?.parent??null,created:r.created,host_kind:y,enterable:y==="broker",attention_count:r.asks,...d?.cycles!=null?{cycles:d.cycles}:{},...r.mtimeMs!==void 0?{last_activity:new Date(r.mtimeMs).toISOString()}:{},ctx_tokens:r.ctx_tokens,streaming:r.streaming??!1,jobs_active:d!==void 0&&oe(d.pi_pid)?ne(se(r.node_id)).length:0,waiting_for:d?.waiting_for??null,hanging:r.hanging??null,viewed:r.viewed??!1,final_report:d?.final_report??null,finalized_at:d?.finalized_at??null}}),c=new Map;for(const r of w()){const d=c.get(r.from_id);d===void 0?c.set(r.from_id,[r.to_id]):d.push(r.to_id)}const u=new Set(s.map(r=>r.node_id)),f=Object.fromEntries(s.map(r=>[r.node_id,(c.get(r.node_id)??[]).filter(d=>u.has(d))]));return{status:200,body:{generated_at:$(),nodes:o,subscriptions:f}}}l(Pe,"handleSnapshot");function K(){const t=te().map(n=>{const s=n.host_kind??"tmux";return{node_id:n.node_id,name:n.name,kind:n.kind,mode:n.mode,lifecycle:n.lifecycle,status:n.status,cwd:n.cwd,host_kind:s,enterable:s==="broker",parent:n.parent,created:n.created}}),e=w().map(n=>({from_id:n.from_id,to_id:n.to_id,active:n.active,created:n.created}));return{generated_at:$(),nodes:t,edges:e}}l(K,"buildRoster");let R=null,B=null;function qe(){return R===null?(R=Promise.resolve().then(()=>K()).finally(()=>{R=null}),R):(B===null&&(B=R.then(()=>K()).finally(()=>{B=null})),B)}l(qe,"rosterCoalesced");async function De(t){const e=await qe(),n=N(t);return n===null?{status:200,body:e}:{status:200,body:{...e,nodes:e.nodes.filter(s=>n.has(s.node_id)),edges:e.edges.filter(s=>n.has(s.from_id)&&n.has(s.to_id))}}}l(De,"handleRoster");function U(t){if(t.name!==""&&t.name!==t.kind)return t.name;const e=(t.description??"").trim();return e!==""?e:t.name}l(U,"liveRootName");function He(){const t=F(),e=new Map(t.map(i=>[i.node_id,i])),n=new Map(_e(he(),"SELECT node_id, reply_at, read_at FROM nodes WHERE parent IS NULL").all().map(i=>[i.node_id,i])),s=t.filter(i=>i.parent===null&&i.kind!=="human"&&(be(i.status)||!ge({lifecycle:i.lifecycle,finalized:i.final_report!=null}))),a=we(),o=new Map;for(const i of a)o.set(i.requester_node_id,(o.get(i.requester_node_id)??0)+1);const c=new Map;for(const i of w())c.set(i.from_id,[...c.get(i.from_id)??[],i.to_id]);const u=l(i=>{const r=e.get(i)?.fault;return r&&r.link!=="broker\u2194crtrd"?Se(r):null},"faultOf"),f=s.map(i=>{const r=[i.node_id,...ye(i.node_id)].filter(m=>e.get(m)?.kind!=="human"&&e.has(m)),d=new Set(r),y=a.filter(m=>d.has(m.requester_node_id)),b=n.get(i.node_id),g=b?.reply_at??null,M=b?.read_at??null;let k=i.created;for(const m of r){const v=e.get(m).telemetry_updated_at;v!=null&&v>k&&(k=v)}return g!==null&&g>k&&(k=g),{node_id:i.node_id,name:U(i),cwd:i.cwd,profile_id:i.profile_id,status:i.status,state_line:(()=>{const m=ve(i.node_id)?.state.trim();return m||null})(),goal:ke(i.node_id)?.trim()||null,asks:y.length,first_ask_ticket_id:y[0]?.inbox_ticket_id??null,fault:r.map(u).find(m=>m!==null)??null,running_count:r.filter(m=>e.get(m).streaming===!0).length,unread:g!==null&&(M===null||g>M),last_activity:k,children:(c.get(i.node_id)??[]).filter(m=>e.has(m)&&e.get(m).kind!=="human").map(m=>{const v=e.get(m);return{node_id:m,name:U(v),status:v.status,streaming:v.streaming===!0,asks:o.get(m)??0,fault:u(m)}})}});return{generated_at:$(),rows:f}}l(He,"buildLiveRoots");let C=null,E=null;function ze(){const t=l(()=>Promise.resolve().then(He),"run");return C===null?(C=t().finally(()=>{C=null}),C):(E===null&&(E=C.then(t).finally(()=>{E=null})),E)}l(ze,"liveRootsCoalesced");async function xe(t){if(!Re(t.principal))throw new T(403,"permission_error","only the person reads the dashboard");const e=await ze(),n=t.query.get("profile");return{status:200,body:n===null?e:{...e,rows:e.rows.filter(s=>s.profile_id===n)}}}l(xe,"handleLiveRoots");function Je(t){const e=F().filter(o=>t.principal.class==="person"||O(t.principal,o.grantee??"app:terminal",o.node_id)).map(o=>({node_id:o.node_id,name:o.name,description:o.description,kind:o.kind,mode:o.mode,lifecycle:o.lifecycle,status:o.status,cwd:o.cwd,profile_id:o.profile_id,grantee:o.grantee??"app:terminal",parent:o.parent,created:o.created,frozen_at:o.frozen_at??null,terminal_reason:o.terminal_reason??null,final_report:o.final_report??null,telemetry_tokens_in:o.telemetry_tokens_in??null,telemetry_updated_at:o.telemetry_updated_at??null,fault:o.fault&&o.fault.link!=="broker\u2194crtrd"?{...o.fault,link:o.fault.link}:null,streaming:o.streaming??!1})),n=new Set(e.map(o=>o.node_id)),s=w().filter(o=>n.has(o.from_id)&&n.has(o.to_id));return{status:200,rawBody:V(JSON.stringify({nodes:e,edges:s}),{level:1}),headers:{"content-type":"application/gzip"}}}l(Je,"handleBrowse");function Ke(t){const e=N(t);return{status:200,body:{generated_at:$(),nodes:X().filter(s=>e===null||e.has(s.node_id)).map(Ne),edges:w().filter(s=>e===null||e.has(s.from_id)&&e.has(s.to_id)).map(s=>({from_id:s.from_id,to_id:s.to_id,active:s.active,created:s.created})),focused_node_ids:D().map(s=>s.node_id).filter(s=>e===null||e.has(s))}}}l(Ke,"handleGraph");function Ue(t){if(t==null)return{};if(typeof t!="object")throw _("prune body must be a JSON object");const e=t,n=l(r=>{const d=e[r];if(d!==void 0){if(typeof d!="number"||!Number.isFinite(d))throw _(`${r} must be a number`);return d}},"num"),s=l(r=>{const d=e[r];if(d!==void 0){if(typeof d!="boolean")throw _(`${r} must be a boolean`);return d}},"bool"),a={},o=n("limit");o!==void 0&&(a.limit=o);const c=n("ttl_days");c!==void 0&&(a.ttl_days=c);const u=s("include_stale");u!==void 0&&(a.include_stale=u);const f=s("empty");f!==void 0&&(a.empty=f);const i=s("dry_run");return i!==void 0&&(a.dry_run=i),a}l(Ue,"parsePruneBody");function We(t){const e=Ue(t.body),n=e.ttl_days??14,s=e.dry_run??!1,a=pe(fe());let o;return e.limit!==void 0?o=ee({maxNodes:e.limit,held:a,dryRun:s}).pruned.map(f=>({node_id:f.node_id,status:f.status,created:f.created})):e.empty===!0?o=ue({dryRun:s}).map(f=>({node_id:f,status:"empty",created:""})):o=Z({ttlDays:n,held:a,dryRun:s,includeStale:e.include_stale??!1}).pruned.map(f=>({node_id:f.node_id,status:f.status,created:f.created})),{status:200,body:{pruned:o,dry_run:s,ttl_days:n}}}l(We,"handlePrune");const kt=[{method:"GET",pattern:"/v1/canvas/attention",handler:Me},{method:"POST",pattern:"/v1/canvas/attention/counts",handler:Le},{method:"POST",pattern:"/v1/canvas/history/search",handler:Ae},{method:"POST",pattern:"/v1/canvas/history/grep",handler:je},{method:"GET",pattern:"/v1/canvas/history/read",handler:Fe},{method:"POST",pattern:"/v1/canvas/history/stats",handler:Te},{method:"GET",pattern:"/v1/canvas/snapshot",handler:Pe},{method:"GET",pattern:"/v1/canvas/roster",handler:De},{method:"GET",pattern:"/v1/canvas/live-roots",handler:xe},{method:"GET",pattern:"/v1/canvas/browse",handler:Je},{method:"GET",pattern:"/v1/canvas/graph",handler:Ke},{method:"POST",pattern:"/v1/canvas/prune",ownerOnly:!0,handler:We}];export{kt as canvasRoutes,Je as handleBrowse};
1
+ var Y=Object.defineProperty;var l=(t,e)=>Y(t,"name",{value:e,configurable:!0});import{gzipSync as V}from"node:zlib";import{browseNodes as P,graphNodes as X,listNodes as F,pruneNodes as Z,pruneToLimit as ee,rosterEdges as w,rosterNodes as te}from"../../../core/canvas/canvas.js";import{activeBackgroundBashJobs as ne}from"../../../core/bash-jobs.js";import{contextDir as se}from"../../../core/canvas/node-agent-paths.js";import{nowIso as L}from"../../../core/fs-utils.js";import{isPidPresent as oe}from"../../../core/canvas/pid.js";import{dashboardRowsAllFromSource as re,enrichRowsFromSource as ie}from"../../../core/canvas/render-source.js";import{localCanvasSource as G}from"../../../core/canvas/source.js";import{ticketCountsForNodes as q,ticketsAcrossCanvas as ae}from"../../../core/canvas/attention.js";import{buildCorpus as de,groupCorpus as le,resolveRef as ue}from"../../../core/canvas/history.js";import{listFocuses as D,reapEmptyNodes as ce}from"../../../core/runtime/placement.js";import{boundFleet as fe}from"../../../core/runtime/fleet.js";import{retentionHold as pe}from"../../reconcilers/live-obligation.js";import{notFound as me,usage as _}from"../../../core/errors.js";import{openDb as _e}from"../../../core/canvas/db.js";import{prepared as he}from"../../../core/sqlite-statements.js";import{view as ye}from"../../../core/canvas/tree.js";import{isLiveStatus as ge}from"../../../core/canvas/node-order.js";import{readNodeRecap as be}from"../../../core/canvas/node-recap.js";import{readGoal as ve}from"../../../core/runtime/kickoff.js";import{faultSummary as ke}from"../../../core/canvas/status-glyph.js";import{listPendingAsks as Se}from"../../../core/human/requests.js";import{toGraphNodeDTO as we}from"../map.js";import{actsAsPerson as Re,canReadNode as B,canReadRun as Ne}from"../router.js";import{ApiError as T}from"../../../api/errors.js";function R(t){return t.principal.class==="person"?null:new Set(F().filter(e=>B(t.principal,e.grantee??"app:terminal",e.node_id)).map(e=>e.node_id))}l(R,"visibleNodes");function H(t){return t.principal.class==="person"?null:new Set(F().filter(e=>e.run_id?Ne(t.principal,e.grantee??"app:terminal",e.node_id):B(t.principal,e.grantee??"app:terminal",e.node_id)).map(e=>e.node_id))}l(H,"visibleHistoryNodes");function Ce(t){const e=R(t);return{status:200,body:{items:ae().filter(s=>e===null||e.has(s.node_id)).map(s=>({node_id:s.node_id,name:s.name,cwd:s.cwd,count:s.count,subjects:s.subjects}))}}}l(Ce,"handleAttention");function Me(t){const e=t.body;if(e===null||typeof e!="object"||!Array.isArray(e.node_ids))throw _("attention count request requires node_ids: string[]");const n=e.node_ids;if(!n.every(r=>typeof r=="string"))throw _("attention count request node_ids must contain only strings");const s=R(t);return{status:200,body:{counts:q(n.filter(r=>s===null||s.has(r)))}}}l(Me,"handleAttentionCounts");function x(t){return Buffer.from(String(t),"utf8").toString("base64")}l(x,"encodeCursor");function z(t){const e=Number(Buffer.from(t,"base64").toString("utf8"));if(!Number.isInteger(e)||e<0)throw _(`invalid cursor: ${t}`);return e}l(z,"decodeCursor");function Le(t,e,n){const a=t.loadBody().split(`
2
+ `);if(e.length>0){const u=a.findIndex(p=>{const f=p.toLowerCase();return e.some(h=>f.includes(h))});if(u!==-1)return a.slice(u,u+n).map(p=>p.trim()).filter(p=>p!=="").join(" \u23CE ")}const r=a.map(u=>u.trim()).filter(u=>u!=="").slice(0,n).join(" \u23CE ");return r!==""?r:t.title}l(Le,"snippet");function Ee(t,e,n){const s=[...t];return e==="relevance"&&n!==void 0?s.sort((a,r)=>(n.get(r.ref)??0)-(n.get(a.ref)??0)||r.tsMs-a.tsMs||a.ref.localeCompare(r.ref)):e==="oldest"?s.sort((a,r)=>a.tsMs-r.tsMs||a.ref.localeCompare(r.ref)):s.sort((a,r)=>r.tsMs-a.tsMs||a.ref.localeCompare(r.ref)),s}l(Ee,"sortArtifacts");function Ie(t){if(t==null)return{};if(typeof t!="object")throw _("history search body must be a JSON object");return t}l(Ie,"parseSearchBody");function Oe(t){if(t===null||typeof t!="object")throw _("history grep body must be a JSON object");const e=t;if(typeof e.pattern!="string"||e.pattern==="")throw _("history grep body requires a non-empty pattern");return e}l(Oe,"parseGrepBody");function j(t,e){const n={cwd:t.cwd,allCwds:t.all_cwds===!0,under:t.under,nodes:t.nodes},s={types:t.types,kinds:t.kinds,statuses:t.statuses,sinceMs:t.since_ms,untilMs:t.until_ms,origins:t.origins,roles:t.roles,from:t.from},a=H(e);return de(n,s).filter(r=>a===null||a.has(r.nodeId))}l(j,"buildCorpusFromQuery");function $e(t){if(t==null)return{};if(typeof t!="object")throw _("history stats body must be a JSON object");return t}l($e,"parseStatsBody");const J=["origin","node","kind","day","type","role"];function Be(t){const e=$e(t.body),n=e.group_by??"type";if(!J.includes(n))throw _(`--group-by must be one of: ${J.join(", ")}; received: ${String(e.group_by)}`);const s=j(e,t),a=le(s,n);return{status:200,body:{group_by:n,groups:a,total:a.reduce((u,p)=>u+p.count,0),scanned:s.length}}}l(Be,"handleHistoryStats");function Te(t){const e=Oe(t.body),n=Math.min(100,Math.max(1,e.limit??20)),s=j(e,t),a=e.cursor!==void 0?z(e.cursor):0;let r;try{r=new RegExp(e.pattern,"i")}catch{throw _(`invalid regex pattern: ${e.pattern}`)}const u=[];for(const i of s)i.loadBody().split(`
3
+ `).forEach((c,b)=>{r.test(c)&&u.push({ref:i.ref,node:`${i.nodeName} (${i.nodeId})`,ts:i.ts,line:b+1,text:c.trim()})});u.sort((i,o)=>i.ref.localeCompare(o.ref)||i.line-o.line);const p=u.slice(a,a+n),f=a+n<u.length?x(a+n):null;return{status:200,body:{hits:p,next_cursor:f,total:u.length}}}l(Te,"handleHistoryGrep");function je(t){const e=Ie(t.body),n=(e.query??"").trim(),s=e.weigh_body===!0,a=e.full===!0,r=Math.max(1,e.snippet_lines??1),u=Math.min(100,Math.max(1,e.limit??20)),p=e.per_node===!0;if(p&&(e.nodes===void 0||e.nodes.length===0))throw new T(400,"invalid_request","`per_node` requires a non-empty `nodes` list",{param:"nodes"});const f=j(e,t),h=e.cursor!==void 0?z(e.cursor):0,i=n.toLowerCase().split(/\s+/).filter(d=>d.length>0);let o=f,c;const b=e.sort??(i.length>0?"relevance":"recency");if(i.length>0&&b==="relevance"){c=new Map;const d=[];for(const m of f){const $=m.title.toLowerCase(),Q=m.nodeName.toLowerCase(),W=m.nodeDesc.toLowerCase(),A=s?m.loadBody().toLowerCase():null;let S=0;for(const M of i)$.includes(M)&&(S+=10),Q.includes(M)&&(S+=5),W.includes(M)&&(S+=3),A!==null&&A.includes(M)&&(S+=1);S>0&&(c.set(m.ref,S),d.push(m))}o=d}else i.length>0&&(o=f.filter(d=>{const m=`${d.title}
4
+ ${d.nodeName}
5
+ ${d.nodeDesc}${s?`
6
+ `+d.loadBody():""}`.toLowerCase();return i.some($=>m.includes($))}));let g=Ee(o,b,c);if(p){const d=new Set;g=g.filter(m=>!d.has(m.nodeId)&&d.add(m.nodeId))}const v=p?g:g.slice(h,h+u),y=!p&&h+u<g.length?x(h+u):null;return{status:200,body:{hits:v.map(d=>{const m={ref:d.ref,node:`${d.nodeName} (${d.nodeId})`,kind:d.nodeKind,source:d.source,ts:d.ts,...p?{node_id:d.nodeId}:{}};return b==="relevance"&&c!==void 0&&(m.score=c.get(d.ref)??0),a?m.body=d.loadBody():m.snippet=Le(d,i,r),m}),next_cursor:y,total:g.length}}}l(je,"handleHistorySearch");function Ae(t){const e=(t.query.get("ref")??"").trim();if(!e.includes(":"))throw _(`ref must be <node-id>:inbox/<n>, <node-id>:session, or <node-id>:session/<n>; received: ${e}`,{received:e,next:"Get a valid ref from `crtr canvas history search`. Reports and documents are `crtr canvas read <name>`."});const n=ue(e);if(n===null)throw me(`no inbox entry or message for ref: ${e}`,{ref:e,next:"Re-run `crtr canvas history search` for a current ref. Reports and documents are `crtr canvas read <name>`."});if(H(t)?.has(n.nodeId)===!1)throw new T(404,"not_found","artifact not found");const s=n.source,r=t.query.get("format")==="raw"&&n.raw!==null?n.raw:n.body;return{status:200,body:{ref:e,node:`${n.nodeName} (${n.nodeId})`,source:s,ts:n.ts,content:r}}}l(Ae,"handleHistoryRead");function Pe(t){for(const[s]of t.query)if(s!=="live")throw _(`unknown snapshot query parameter: ${s}`);const e=t.query.getAll("live");if(e.length>1)throw _("live must appear at most once");const n=e[0];if(n===void 0||n==="false"||n==="0")return!1;if(n==="true"||n==="1")return!0;throw _("live must be true or false")}l(Pe,"snapshotLiveOnly");async function Fe(t){const e=new Set(D().map(i=>i.node_id)),n=R(t),s=(await re(G,e,Pe(t))).filter(i=>n===null||n.has(i.node_id)),a=q(s.map(i=>i.node_id));await ie(G,s,a);const r=s.map(i=>{const o=i.srcRow,c=o?.host_kind??"tmux";return{node_id:i.node_id,name:i.name,title:i.handle??i.name,description:i.description??null,kind:i.kind,mode:i.mode,lifecycle:i.lifecycle??o?.lifecycle,status:i.status,cwd:i.cwd,profile_id:i.profile_id??null,parent:o?.parent??null,created:i.created,host_kind:c,enterable:c==="broker",attention_count:i.asks,...o?.cycles!=null?{cycles:o.cycles}:{},...i.mtimeMs!==void 0?{last_activity:new Date(i.mtimeMs).toISOString()}:{},ctx_tokens:i.ctx_tokens,streaming:i.streaming??!1,jobs_active:o!==void 0&&oe(o.pi_pid)?ne(se(i.node_id)).length:0,waiting_for:o?.waiting_for??null,hanging:i.hanging??null,viewed:i.viewed??!1,final_report:o?.final_report??null,finalized_at:o?.finalized_at??null}}),u=new Map;for(const i of w()){const o=u.get(i.from_id);o===void 0?u.set(i.from_id,[i.to_id]):o.push(i.to_id)}const p=new Set(s.map(i=>i.node_id)),f=Object.fromEntries(s.map(i=>[i.node_id,(u.get(i.node_id)??[]).filter(o=>p.has(o))]));return{status:200,body:{generated_at:L(),nodes:r,subscriptions:f}}}l(Fe,"handleSnapshot");function K(){const t=te().map(n=>{const s=n.host_kind??"tmux";return{node_id:n.node_id,name:n.name,kind:n.kind,mode:n.mode,lifecycle:n.lifecycle,status:n.status,cwd:n.cwd,host_kind:s,enterable:s==="broker",parent:n.parent,created:n.created}}),e=w().map(n=>({from_id:n.from_id,to_id:n.to_id,active:n.active,created:n.created}));return{generated_at:L(),nodes:t,edges:e}}l(K,"buildRoster");let N=null,E=null;function Ge(){return N===null?(N=Promise.resolve().then(()=>K()).finally(()=>{N=null}),N):(E===null&&(E=N.then(()=>K()).finally(()=>{E=null})),E)}l(Ge,"rosterCoalesced");async function qe(t){const e=await Ge(),n=R(t);return n===null?{status:200,body:e}:{status:200,body:{...e,nodes:e.nodes.filter(s=>n.has(s.node_id)),edges:e.edges.filter(s=>n.has(s.from_id)&&n.has(s.to_id))}}}l(qe,"handleRoster");function U(t){if(t.name!==""&&t.name!==t.kind)return t.name;const e=(t.description??"").trim();return e!==""?e:t.name}l(U,"liveRootName");const De=1440*6e4;function He(){const t=P(),e=new Map(t.map(o=>[o.node_id,o])),n=new Map(he(_e(),"SELECT node_id, reply_at, read_at, intent FROM nodes WHERE parent IS NULL").all().map(o=>[o.node_id,o])),s=Se(),a=new Map;for(const o of s)a.set(o.requester_node_id,(a.get(o.requester_node_id)??0)+1);const r=new Date(Date.now()-De).toISOString(),u=l(o=>{const c=n.get(o.node_id);return c?.reply_at!=null&&(c.read_at==null||c.reply_at>c.read_at)||(a.get(o.node_id)??0)>0?!0:o.status!=="done"||c?.intent!=="parked"?!1:[o.created,o.telemetry_updated_at,c.reply_at].reduce((v,y)=>y!=null&&y>v?y:v,"")>=r},"keepFinished"),p=t.filter(o=>o.parent===null&&o.kind!=="human"&&(ge(o.status)||u(o))),f=new Map;for(const o of w())f.set(o.from_id,[...f.get(o.from_id)??[],o.to_id]);const h=l(o=>{const c=e.get(o)?.fault;return c&&c.link!=="broker\u2194crtrd"?ke(c):null},"faultOf"),i=p.map(o=>{const c=[o.node_id,...ye(o.node_id)].filter(d=>e.get(d)?.kind!=="human"&&e.has(d)),b=new Set(c),g=s.filter(d=>b.has(d.requester_node_id)),v=n.get(o.node_id),y=v?.reply_at??null,O=v?.read_at??null;let k=o.created;for(const d of c){const m=e.get(d).telemetry_updated_at;m!=null&&m>k&&(k=m)}return y!==null&&y>k&&(k=y),{node_id:o.node_id,name:U(o),cwd:o.cwd,profile_id:o.profile_id,status:o.status,state_line:(()=>{const d=be(o.node_id)?.state.trim();return d||null})(),goal:ve(o.node_id)?.trim()||null,asks:g.length,first_ask_ticket_id:g[0]?.inbox_ticket_id??null,fault:c.map(h).find(d=>d!==null)??null,running_count:c.filter(d=>e.get(d).streaming===!0).length,unread:y!==null&&(O===null||y>O),last_activity:k,children:(f.get(o.node_id)??[]).filter(d=>e.has(d)&&e.get(d).kind!=="human").map(d=>{const m=e.get(d);return{node_id:d,name:U(m),status:m.status,streaming:m.streaming===!0,asks:a.get(d)??0,fault:h(d)}})}});return{generated_at:L(),rows:i}}l(He,"buildLiveRoots");let C=null,I=null;function xe(){const t=l(()=>Promise.resolve().then(He),"run");return C===null?(C=t().finally(()=>{C=null}),C):(I===null&&(I=C.then(t).finally(()=>{I=null})),I)}l(xe,"liveRootsCoalesced");async function ze(t){if(!Re(t.principal))throw new T(403,"permission_error","only the person reads the dashboard");const e=await xe(),n=t.query.get("profile");return{status:200,body:n===null?e:{...e,rows:e.rows.filter(s=>s.profile_id===n)}}}l(ze,"handleLiveRoots");function Je(t){const e=P().filter(r=>t.principal.class==="person"||B(t.principal,r.grantee??"app:terminal",r.node_id)).map(r=>({node_id:r.node_id,name:r.name,description:r.description,kind:r.kind,mode:r.mode,lifecycle:r.lifecycle,status:r.status,cwd:r.cwd,profile_id:r.profile_id,grantee:r.grantee??"app:terminal",parent:r.parent,created:r.created,frozen_at:r.frozen_at??null,terminal_reason:r.terminal_reason??null,final_report:r.final_report??null,telemetry_tokens_in:r.telemetry_tokens_in??null,telemetry_updated_at:r.telemetry_updated_at??null,fault:r.fault&&r.fault.link!=="broker\u2194crtrd"?{...r.fault,link:r.fault.link}:null,streaming:r.streaming??!1})),n=new Set(e.map(r=>r.node_id)),s=w().filter(r=>n.has(r.from_id)&&n.has(r.to_id));return{status:200,rawBody:V(JSON.stringify({nodes:e,edges:s}),{level:1}),headers:{"content-type":"application/gzip"}}}l(Je,"handleBrowse");function Ke(t){const e=R(t);return{status:200,body:{generated_at:L(),nodes:X().filter(s=>e===null||e.has(s.node_id)).map(we),edges:w().filter(s=>e===null||e.has(s.from_id)&&e.has(s.to_id)).map(s=>({from_id:s.from_id,to_id:s.to_id,active:s.active,created:s.created})),focused_node_ids:D().map(s=>s.node_id).filter(s=>e===null||e.has(s))}}}l(Ke,"handleGraph");function Ue(t){if(t==null)return{};if(typeof t!="object")throw _("prune body must be a JSON object");const e=t,n=l(i=>{const o=e[i];if(o!==void 0){if(typeof o!="number"||!Number.isFinite(o))throw _(`${i} must be a number`);return o}},"num"),s=l(i=>{const o=e[i];if(o!==void 0){if(typeof o!="boolean")throw _(`${i} must be a boolean`);return o}},"bool"),a={},r=n("limit");r!==void 0&&(a.limit=r);const u=n("ttl_days");u!==void 0&&(a.ttl_days=u);const p=s("include_stale");p!==void 0&&(a.include_stale=p);const f=s("empty");f!==void 0&&(a.empty=f);const h=s("dry_run");return h!==void 0&&(a.dry_run=h),a}l(Ue,"parsePruneBody");function Qe(t){const e=Ue(t.body),n=e.ttl_days??14,s=e.dry_run??!1,a=pe(fe());let r;return e.limit!==void 0?r=ee({maxNodes:e.limit,held:a,dryRun:s}).pruned.map(f=>({node_id:f.node_id,status:f.status,created:f.created})):e.empty===!0?r=ce({dryRun:s}).map(f=>({node_id:f,status:"empty",created:""})):r=Z({ttlDays:n,held:a,dryRun:s,includeStale:e.include_stale??!1}).pruned.map(f=>({node_id:f.node_id,status:f.status,created:f.created})),{status:200,body:{pruned:r,dry_run:s,ttl_days:n}}}l(Qe,"handlePrune");const kt=[{method:"GET",pattern:"/v1/canvas/attention",handler:Ce},{method:"POST",pattern:"/v1/canvas/attention/counts",handler:Me},{method:"POST",pattern:"/v1/canvas/history/search",handler:je},{method:"POST",pattern:"/v1/canvas/history/grep",handler:Te},{method:"GET",pattern:"/v1/canvas/history/read",handler:Ae},{method:"POST",pattern:"/v1/canvas/history/stats",handler:Be},{method:"GET",pattern:"/v1/canvas/snapshot",handler:Fe},{method:"GET",pattern:"/v1/canvas/roster",handler:qe},{method:"GET",pattern:"/v1/canvas/live-roots",handler:ze},{method:"GET",pattern:"/v1/canvas/browse",handler:Je},{method:"GET",pattern:"/v1/canvas/graph",handler:Ke},{method:"POST",pattern:"/v1/canvas/prune",ownerOnly:!0,handler:Qe}];export{kt as canvasRoutes,Je as handleBrowse};