@north-light/cli 0.3.585 → 0.3.587

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.
@@ -9,7 +9,7 @@ when-and-why-to-read: When you need Release notes, this knowledge should be read
9
9
 
10
10
  Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
11
11
 
12
- ## 0.3.584
12
+ ## 0.3.586
13
13
 
14
14
  `GET /v1/nodes/{id}/jobs/{jobId}/output` reads a background bash job's stored output, last 256 KiB by default (`max_bytes` up to 4 MiB, or `tail_lines`), from the blob store, so it works after the job's sandbox is deleted. It answers `job_not_found` for a job the node does not own and an empty `output` for a job that has written nothing. `CrtrClient.readBashJobOutput` calls it. Nothing existing changes.
15
15
 
@@ -1,6 +1,6 @@
1
1
  import type { SurfaceHost } from '../../surfaces/host.js';
2
2
  import type { Picker } from './pickers.js';
3
- import { extractFilePaths } from '#core/human/visible-paths';
3
+ import { extractFilePaths } from '../../../core/human/visible-paths.js';
4
4
  export declare function buildFileReviewPicker(candidates: readonly string[], cwd: string, onChoose: (file: string) => void, close: () => void, notify: (message: string) => void): Picker;
5
5
  /** Open an inline review of a file path, or of a document given as `[[ref]]`. */
6
6
  export declare function launchFileReview(host: SurfaceHost, file: string, opts: {
@@ -1,2 +1,2 @@
1
- var v=Object.defineProperty;var c=(n,e)=>v(n,"name",{value:e,configurable:!0});import{homedir as a}from"node:os";import{isAbsolute as g,join as b,relative as S,resolve as y}from"node:path";import{Input as I,SelectList as d,getKeybindings as R}from"@earendil-works/pi-tui";import{getSelectListTheme as u}from"@earendil-works/pi-coding-agent";import{openReviewWindow as C}from"../../inbox/review/launch.js";import{createReviewClient as L,ReviewTerminalError as k,terminalRefusalNotice as x}from"../../inbox/review/review-client.js";import{checkReviewableFile as N,extractFilePaths as P}from"#core/human/visible-paths";import{docLinkText as F}from"../render/doc-links.js";const l=12;function h(n){const e=/^\[\[([^[\]\n]+)\]\]$/.exec(n.trim());return e===null?void 0:e[1].trim()}c(h,"documentRef");function m(n,e){const t=h(n);if(t!==void 0)return`${F(t)} (document)`;const i=S(e,n);if(i!==""&&!i.startsWith("..")&&!g(i))return i;const s=a();return n===s||n.startsWith(`${s}/`)?`~${n.slice(s.length)}`:n}c(m,"labelForPath");function f(n,e){const t=n==="~"?a():n.startsWith("~/")?b(a(),n.slice(2)):n;return y(e,t)}c(f,"resolveTypedPath");class W{static{c(this,"FileReviewPicker")}candidates;cwd;onChoose;close;notify;focused=!1;input=new I;list=new d([],l,u());constructor(e,t,i,s,o){this.candidates=e,this.cwd=t,this.onChoose=i,this.close=s,this.notify=o,this.input.onEscape=s,this.input.onSubmit=()=>this.chooseSelected(),this.refreshList()}render(e){this.input.focused=this.focused;const t=Math.max(1,e-8);return["Review a file or document named in this transcript",...this.input.render(t).map(i=>`Path: ${i}`),...this.list.render(e)]}handleInput(e){const t=R();if(t.matches(e,"tui.select.up")||t.matches(e,"tui.select.down")){this.list.handleInput(e);return}if(t.matches(e,"tui.select.confirm")||e===`
1
+ var v=Object.defineProperty;var c=(n,e)=>v(n,"name",{value:e,configurable:!0});import{homedir as a}from"node:os";import{isAbsolute as g,join as b,relative as S,resolve as y}from"node:path";import{Input as I,SelectList as d,getKeybindings as R}from"@earendil-works/pi-tui";import{getSelectListTheme as u}from"@earendil-works/pi-coding-agent";import{openReviewWindow as C}from"../../inbox/review/launch.js";import{createReviewClient as L,ReviewTerminalError as k,terminalRefusalNotice as x}from"../../inbox/review/review-client.js";import{checkReviewableFile as N,extractFilePaths as P}from"../../../core/human/visible-paths.js";import{docLinkText as F}from"../render/doc-links.js";const l=12;function h(n){const e=/^\[\[([^[\]\n]+)\]\]$/.exec(n.trim());return e===null?void 0:e[1].trim()}c(h,"documentRef");function m(n,e){const t=h(n);if(t!==void 0)return`${F(t)} (document)`;const i=S(e,n);if(i!==""&&!i.startsWith("..")&&!g(i))return i;const s=a();return n===s||n.startsWith(`${s}/`)?`~${n.slice(s.length)}`:n}c(m,"labelForPath");function f(n,e){const t=n==="~"?a():n.startsWith("~/")?b(a(),n.slice(2)):n;return y(e,t)}c(f,"resolveTypedPath");class W{static{c(this,"FileReviewPicker")}candidates;cwd;onChoose;close;notify;focused=!1;input=new I;list=new d([],l,u());constructor(e,t,i,s,o){this.candidates=e,this.cwd=t,this.onChoose=i,this.close=s,this.notify=o,this.input.onEscape=s,this.input.onSubmit=()=>this.chooseSelected(),this.refreshList()}render(e){this.input.focused=this.focused;const t=Math.max(1,e-8);return["Review a file or document named in this transcript",...this.input.render(t).map(i=>`Path: ${i}`),...this.list.render(e)]}handleInput(e){const t=R();if(t.matches(e,"tui.select.up")||t.matches(e,"tui.select.down")){this.list.handleInput(e);return}if(t.matches(e,"tui.select.confirm")||e===`
2
2
  `){this.chooseSelected();return}const i=this.input.getValue();this.input.handleInput(e),this.input.getValue()!==i&&this.refreshList()}invalidate(){this.input.invalidate(),this.list.invalidate()}refreshList(){const e=this.input.getValue(),t=e.toLowerCase(),i=this.candidates.filter(r=>{const w=m(r,this.cwd).toLowerCase();return t===""||w.includes(t)||r.toLowerCase().includes(t)}),s=e===""?void 0:h(e)!==void 0?e.trim():f(e,this.cwd),o=s!==void 0&&this.candidates.some(r=>r===s),p=[...i.map(r=>({value:r,label:m(r,this.cwd)})),...s!==void 0&&!o?[{value:s,label:`use ${JSON.stringify(e)}`,description:s}]:[]];this.list=new d(p,l,u()),this.list.onSelect=()=>this.chooseSelected(),this.list.onCancel=this.close}chooseSelected(){const e=this.input.getValue(),i=this.list.getSelectedItem()?.value??e;if(i==="")return;if(h(i)!==void 0){this.onChoose(i),this.close();return}const s=f(i,this.cwd),o=N(s);if(!o.ok){this.notify(o.reason);return}this.onChoose(s),this.close()}}function q(n,e,t,i,s){const o=new W(n,e,t,i,s);return{component:o,focus:o}}c(q,"buildFileReviewPicker");async function B(n,e,t){if(n.capabilities.remote){t.onNotice("The review is unavailable in a remote attach \u2014 the file and companion live on the host.");return}const i=h(e);let s;try{s=await L().createInline(i===void 0?{originNodeId:t.originNodeId,file:e}:{originNodeId:t.originNodeId,document:i})}catch(o){t.onNotice(o instanceof k?x(o):o instanceof Error?o.message:String(o));return}C(n,s)}c(B,"launchFileReview");export{q as buildFileReviewPicker,P as extractFilePaths,B as launchFileReview};
@@ -1,4 +1,4 @@
1
- var N=Object.defineProperty;var l=(h,t)=>N(h,"name",{value:t,configurable:!0});import{Container as v,Loader as V,Spacer as u,Text as w}from"@earendil-works/pi-tui";import{AssistantMessageComponent as z,BashExecutionComponent as P,BranchSummaryMessageComponent as Y,CompactionSummaryMessageComponent as K,CustomMessageComponent as _,parseSkillBlock as J,SkillInvocationMessageComponent as X,ToolExecutionComponent as Q,UserMessageComponent as B}from"@earendil-works/pi-coding-agent";import{attachMarkdownTheme as f}from"../config.js";import{ContextMessageComponent as j}from"./context-message.js";import{presentationFor as Z}from"./card-presentation.js";import{detectInlinePageResult as tt,PageBlockComponent as et}from"./page-block.js";import{CRTR_OUTPUT_CUSTOM_TYPE as st,CrtrOutputMessageComponent as F,createCrtrBashToolDefinition as it}from"./crtr-output.js";import{CRTR_CYCLE_DIVIDER_CUSTOM_TYPE as nt,endedByAbort as G}from"../../../core/runtime/session-cycles.js";import{isHumanWordsEntry as H,parseCard as ot}from"../../../shared/generated-context.js";import{assistantVisibleText as c,isGroupBoundaryKind as rt,isTrueUserMessage as y}from"../../../shared/tool-groups.js";import{messageKey as R,normalizeTurnReplyRecord as at,START_TURN_KEY as M,turnReplyText as W}from"../../../shared/turn-replies.js";import{isCycleDivider as ht}from"./condensed-history.js";import{transformDiagramFences as lt}from"./diagram.js";import{styleAttachAgentMarkdown as pt,styleAttachMarkdownSource as L,styleAttachMessageMarkdown as dt,styleAttachSummaryMarkdown as $}from"./markdown-source.js";import{createCrtrEditToolDefinition as ct}from"./edit-diff.js";import{GroupActivityRecorder as ut}from"./group-activity.js";import{GroupRecapComponent as ft}from"./group-recap.js";import{FoldedToolCallController as mt,bashCallBackgroundHintLines as gt,createCrtrPlainBashToolDefinition as yt,createCrtrReadToolDefinition as Tt,createCrtrWriteToolDefinition as Ct}from"./tool-calls.js";import{ringCompletionBell as kt}from"../chrome/completion-bell.js";import{extractFilePaths as bt}from"#core/human/visible-paths";import{extractDocRefs as wt}from"./doc-links.js";import{keepWhenCondensed as xt,liveRegionStart as St}from"./condensed-history.js";import{ViewerAssistantMessageComponent as x}from"./assistant-message.js";import{CompositeViewportSource as Rt,MeasuredContainer as E}from"./measured-container.js";import{readConfig as T,updateRawConfigAtomically as At}from"../../../core/config.js";import{DEFAULT_CONDENSED_HISTORY as Bt,DEFAULT_LIVE_CYCLES as Gt}from"../../../types.js";import{applySnapshot as Mt,applyToolGroupSummary as Et,applyWorkingActivity as It,bashEnd as Dt,bashOutput as Ot,bashStart as vt,initialConvState as Pt,reduce as _t}from"../../conversation/projection.js";const q=l(h=>`\x1B[2m${h}\x1B[22m`,"defaultDimStyle"),I=50;function jt(){try{return T("user").live_cycles}catch{return Gt}}l(jt,"readLiveCycles");function Ft(){try{return T("user").condensed_history}catch{return Bt}}l(Ft,"readCondensedHistory");function Ht(){try{return T("user").fold_finished_tools}catch{return!1}}l(Ht,"readFoldFinishedTools");function Wt(){try{return T("user").summarize_tool_calls}catch{return!1}}l(Wt,"readSummarizeToolCalls");function Lt(){try{return T("user").detailed_tool_recaps}catch{return!0}}l(Lt,"readDetailedToolRecaps");function $t(){try{return T("user").consolidate_replies}catch{return!1}}l($t,"readConsolidateReplies");async function qt(h){try{await At("user",t=>({...t,fold_finished_tools:h}))}catch{}}l(qt,"persistFoldFinishedTools");function Ut(h){return typeof h=="object"&&h!==null&&"setExpanded"in h&&typeof h.setExpanded=="function"}l(Ut,"isExpandable");const Nt=/\x1b\[[0-9;]*m/g,Vt=/\x1b\[([0-9;]*)m/g;function zt(h){return h.replace(Vt,(t,e)=>{const s=e.split(";"),i=[];for(let n=0;n<s.length;n++){const r=Number(s[n]===""?0:s[n]);if(r===38||r===48||r===58){const o=Number(s[n+1]),a=n+(o===5?2:o===2?4:1);r!==48&&i.push(...s.slice(n,a+1)),n=a;continue}r>=40&&r<=47||r>=100&&r<=107||i.push(String(r))}return i.length>0?`\x1B[${i.join(";")}m`:""})}l(zt,"stripBackground");const Yt=/<from\b[^>]*>[\s\S]*?<\/from>/g;class D extends u{static{l(this,"FoldedToolSeparator")}folded=!1;separatesToolGroup=!1;setFolded(t){this.folded=t,this.refresh()}setSeparatesToolGroup(t){this.separatesToolGroup=t,this.refresh()}refresh(){this.setLines(this.folded&&this.separatesToolGroup?1:0)}}class Kt extends v{static{l(this,"WorkingRow")}label=new w("",1,0);constructor(){super(),this.addChild(new u(1)),this.addChild(this.label)}setLabel(t){this.label.setText(t)}}function O(h,t,e){return`${h} ${h===1?t:e}`}l(O,"plural");class A extends Q{static{l(this,"MinimizableToolComponent")}activityCallId;minimized=!1;expandedState=!1;foldController;resultObserver;setToolView(t,e){this.foldController=t,this.resultObserver=e}updateResult(...t){this.resultObserver?.(t[0],t[1]??!1);const e=t[0],s=e.content;Array.isArray(s)&&/^elapsed \d+(?:h \d+)?m \d+s(?:\n|$)/.test(s.at(-1)?.text??"")?super.updateResult({...e,content:s.slice(0,-1)},t[1]):super.updateResult(...t)}setExpanded(t){this.expandedState=t,super.setExpanded(t)}setMinimized(t){!this.foldController||this.minimized===t||(this.minimized=t,this.foldController.setFolded(t),super.setExpanded(this.expandedState))}render(t){if(!this.minimized)return[...super.render(t),...gt(this.foldController?.callComponent(),t)];const e=this.foldController?.callComponent();return e?e.render(t).filter(s=>s.replace(Nt,"").trim().length>0).map(s=>zt(s).trimEnd()):super.render(t)}}const Jt=l(h=>`\x1B[31m${h}\x1B[39m`,"defaultErrorStyle"),Xt={accent:l(h=>`\x1B[33m${h}\x1B[39m`,"accent"),active:l(h=>`\x1B[36m${h}\x1B[39m`,"active"),info:l(h=>`\x1B[34m${h}\x1B[39m`,"info"),muted:q,faint:q,border:l(h=>h,"border"),surface:l(h=>h,"surface"),bold:l(h=>`\x1B[1m${h}\x1B[22m`,"bold"),error:Jt,warning:l(h=>`\x1B[33m${h}\x1B[39m`,"warning"),diffAdded:l(h=>`\x1B[32m${h}\x1B[39m`,"diffAdded"),diffRemoved:l(h=>`\x1B[91m${h}\x1B[39m`,"diffRemoved"),bashMode:l(h=>`\x1B[32m${h}\x1B[39m`,"bashMode"),bashModeAlt:l(h=>`\x1B[92m${h}\x1B[39m`,"bashModeAlt")};class Ae{static{l(this,"ChatView")}tui;container;historyContainer=new E;statusContainer=new E;bannerContainer;transcript;appendTarget=this.historyContainer;pageBlocks=new Map;inboxAnswers=new Set;inlinePageBlocks;pageOpenHint;backgroundBashHint;liveCycles;condensedHistory;cwd;showImages;imageWidthCells;hideThinking;hiddenThinkingLabel;toolOutputExpanded;foldSettledTools=Ht();summarizeToolCalls=Wt();detailedToolRecaps=Lt();showGroupSummaries=this.summarizeToolCalls&&this.foldSettledTools;groups=[];openGroup;consolidateReplies=$t();showWorking=!1;turnReplies=new Map;turns=[];openReplyTurn;carriedTurn={key:M,prose:[]};outsideTurns=0;turnHidden=new Set;groupSummaries=new Map;nodeRoster=new Map;streamingWasGroupMember=!1;onFooterEvent;onActivityChange;spinnerStyle;dimStyle;errorStyle;recapPalette;labelStyle=l(t=>`\x1B[1m${t}\x1B[22m`,"labelStyle");markdownTransformers=[(t,{messageType:e,availableWidth:s})=>e==="assistant"?lt(t,s):t,(t,{messageType:e})=>e==="user"?L(t):pt(t)];streamingComponent;streamingToolSeparator;foldedToolSeparatorContents=new Map;pendingTools=new Map;bashComponent;activityLoader;projection=Pt();workingActivity=this.projection.workingActivity;runActive=!1;applyingSnapshot=!1;snapshotGeneration=0;deferredFrames=[];lastAssistantText;transcriptFilePaths=[];transcriptDocRefs=[];copyBlockRecords=[];copyIndex=new Map;condensedCopyBlocks=new WeakMap;constructor(t,e,s={}){if(this.tui=t,this.container=e,this.cwd=s.cwd??process.cwd(),this.showImages=s.showImages??!0,this.imageWidthCells=s.imageWidthCells??60,this.hideThinking=s.hideThinking??!1,this.hiddenThinkingLabel=s.hiddenThinkingLabel??"Thinking...",this.toolOutputExpanded=s.toolOutputExpanded??!1,this.liveCycles=s.liveCycles??jt(),this.condensedHistory=s.condensedHistory??Ft(),this.onFooterEvent=s.onFooterEvent,this.onActivityChange=s.onActivityChange,this.inlinePageBlocks=s.inlinePageBlocks??!1,this.pageOpenHint=s.pageOpenHint??(()=>"crtr human list"),this.backgroundBashHint=s.backgroundBashHint??(()=>null),this.recapPalette=s.palette??Xt,this.spinnerStyle=this.recapPalette.active,this.dimStyle=this.recapPalette.muted,this.errorStyle=this.recapPalette.error,s.banner&&s.banner.length>0){const i=new E;i.addChild(new u(1));for(const n of s.banner)i.addChild(new w(n,1,0));i.addChild(new u(1)),this.bannerContainer=i,this.container.addChild(i)}this.container.addChild(this.historyContainer),this.onActivityChange||this.container.addChild(this.statusContainer),this.transcript=new Rt([...this.bannerContainer?[this.bannerContainer]:[],this.historyContainer,...this.onActivityChange?[]:[this.statusContainer]])}transcriptSource(){return this.transcript}async applySnapshot(t){const e=++this.snapshotGeneration;if(this.applyingSnapshot=!0,this.deferredFrames.length=0,this.resetChat(),this.projection=Mt(t,this.projection),this.workingActivity=this.projection.workingActivity,this.runActive=this.projection.isStreaming,this.seedToolGroupSummaries(this.projection.toolGroupSummaryDetails),this.turnReplies.clear(),this.consolidateReplies)for(const[o,a]of Object.entries(at(t.turnReplies)))this.turnReplies.set(o,a);const s=new Map,i=this.projection.messages,n=i.length-1,r=St(i,this.liveCycles);if(r>0){const o=await this.buildCondensedBlock(i.slice(0,r),e);if(e!==this.snapshotGeneration)return;o&&this.appendCondensedBlock(o)}for(let o=r;o<i.length;o++){if(!this.projection.isStreaming&&o>r&&(o-r)%I===0&&(await new Promise(p=>setImmediate(p)),e!==this.snapshotGeneration))return;const a=i[o];if(a.role==="assistant"){const p=c(a);p.trim()&&(this.lastAssistantText=p,this.pushTranscriptText(p));const d=o===n&&this.projection.isStreaming&&a.stopReason!=="aborted"&&a.stopReason!=="error",C=c(a).trim()!=="",U=this.assistantToolCalls(a),k=a.stopReason==="aborted"||a.stopReason==="error",b=G(a)?"Interrupted":a.errorMessage??"Error";if(k){for(const m of s.values()){const g={content:[{type:"text",text:b}],isError:!0};m.updateResult(g),this.registerCopyBlock(m,"tool",b),this.settleGroupTool(m,g,!0)}s.clear()}(C||k)&&this.closeOpenGroup();let S;d?(this.streamingComponent=new x(void 0,this.hideThinking,f(),this.hiddenThinkingLabel,this.markdownTransformers),this.appendAssistantPart(this.streamingComponent,!C),this.registerCopyBlock(this.streamingComponent,"assistant",""),this.streamingWasGroupMember=!C,this.streamingComponent.updateContent(a),this.registerCopyBlock(this.streamingComponent,"assistant",c(a)),S=this.streamingToolSeparator=this.appendAssistantSeparator(!C)):S=this.appendAssistantMessage(a,!C&&!k),S&&this.setAssistantSeparatorContent(S,a);for(const m of U){this.pushTranscriptValue(m.arguments);const g=this.appendToolComponent(m.name,m.id,m.arguments);a.stopReason==="aborted"||a.stopReason==="error"?(g.updateResult({content:[{type:"text",text:b}],isError:!0}),this.registerCopyBlock(g,"tool",b),this.settleGroupTool(g,{content:[{type:"text",text:b}],isError:!0},!0)):s.set(m.id,g)}k&&this.closeOpenGroup(),!d&&!k&&this.recordTurnProse(a,!1)}else if(a.role==="toolResult"){this.pushTranscriptText(this.userMessageText(a));const p=s.get(a.toolCallId);p&&(p.updateResult(a),this.registerCopyBlock(p,"tool",this.toolResultText(a)),this.historyContainer.markDirty(p),this.settleGroupTool(p,a,a.isError===!0),s.delete(a.toolCallId),this.maybeAppendPageBlock(a))}else this.addMessageToChat(a)}for(const[o,a]of s)this.pendingTools.set(o,a);for(const o of this.turns)this.refreshTurn(o);this.applyToolDisplay(),this.projection.isStreaming&&this.setActivity(this.makeLoader(this.workingActivity)),this.tui.requestRender(),this.applyingSnapshot=!1,this.replayDeferredFrames()}async buildCondensedBlock(t,e){const s=new v,i=this.appendTarget,n=[];this.appendTarget=s,this.condensedCopyBlocks.set(s,n);try{if(this.consolidateReplies)return await this.buildCondensedTurns(t,e)&&s.children.length>0?{component:s,copyBlocks:n}:void 0;let r=0;for(const o of t)if(xt(o,this.condensedHistory)){if(r>0&&r%I===0&&(await new Promise(a=>setImmediate(a)),e!==this.snapshotGeneration))return;r++,this.pushTranscriptText(this.userMessageText(o)),this.addMessageToChat(o)}}finally{this.appendTarget=i,this.condensedCopyBlocks.delete(s)}return s.children.length>0?{component:s,copyBlocks:n}:void 0}async buildCondensedTurns(t,e){const s=[{key:M,prose:[],dividers:[]}];for(const o of t){const a=s.at(-1);if(y(o))s.push({key:R(o)??`local-${s.length}`,user:o,prose:[],dividers:[]});else if(ht(o))a.dividers.push(o);else if(o.role==="assistant"&&o.stopReason!=="aborted"&&o.stopReason!=="error"){const p=c(o).trim(),d=R(o);p!==""&&d!==void 0&&a.prose.push({key:d,text:p})}}const i=s.pop();let n=0;const r=l(async(o,a)=>{if(n>0&&n%I===0&&(await new Promise(d=>setImmediate(d)),e!==this.snapshotGeneration))return!1;n++,o.user!==void 0&&(this.pushTranscriptText(this.userMessageText(o.user)),this.addMessageToChat(o.user));const p=a?W(o.prose,this.turnReplies.get(o.key)):"";if(p!==""){const d=new x(void 0,this.hideThinking,f(),this.hiddenThinkingLabel,this.markdownTransformers);d.updateContent(this.replyMessage(p)),this.append(d),this.registerCopyBlock(d,"assistant",p)}for(const d of o.dividers)this.addMessageToChat(d);return!0},"render");for(const o of s)if(!await r(o,!0))return!1;return await r(i,!1)?(this.carriedTurn={key:i.key,prose:i.prose},!0):!1}deferIfBuilding(t){return this.applyingSnapshot?(this.deferredFrames.push(t),!0):!1}replayDeferredFrames(){const t=this.deferredFrames.splice(0);for(const e of t)e()}setWorkingActivity(t){if(this.deferIfBuilding(()=>this.setWorkingActivity(t)))return;const e=this.projection.activity===this.workingActivity;this.projection=It(this.projection,t),this.workingActivity=this.projection.workingActivity,this.runActive&&e&&this.setActivity(this.makeLoader(this.workingActivity))}settleRun(){this.runActive=!1;for(const t of this.pendingTools.values())this.settleGroupTool(t);if(this.streamingComponent){this.historyContainer.removeChild(this.streamingComponent);for(const t of this.turns)t.members.delete(this.streamingComponent);this.turnHidden.delete(this.streamingComponent),this.unregisterCopyBlock(this.streamingComponent),this.streamingComponent=void 0}this.streamingToolSeparator=void 0,this.pendingTools.clear(),this.openReplyTurn&&this.refreshWorkingRow(this.openReplyTurn),this.applyToolDisplay(),this.setActivity(void 0),this.settleBashComponent()}dispose(){this.settleRun()}toggleToolsExpanded(){let t;return this.summarizeToolCalls&&this.foldSettledTools?(this.showGroupSummaries=!this.showGroupSummaries,this.toolOutputExpanded=!this.showGroupSummaries,t={kind:"summaries",shown:this.showGroupSummaries}):(this.toolOutputExpanded=!this.toolOutputExpanded,t={kind:"output",expanded:this.toolOutputExpanded}),this.applyToolDisplay(),t}toggleFoldSettledTools(){return this.foldSettledTools=!this.foldSettledTools,qt(this.foldSettledTools),this.summarizeToolCalls&&this.foldSettledTools&&(this.showGroupSummaries=!0),this.applyToolDisplay(),this.foldSettledTools}applyToolDisplay(){const t=new Set(this.pendingTools.values());for(const i of this.groups)for(const n of i.inFlight)t.add(n);for(const i of this.historyContainer.children)i instanceof A&&i.setMinimized(this.foldSettledTools&&!t.has(i)),Ut(i)&&i.setExpanded(this.toolOutputExpanded);const e=this.turnFoldedComponents(t),s=new Set;for(const i of this.groups){this.applyGroupDisplay(i,e),s.add(i.recap);for(const n of i.members)s.add(n)}for(const i of this.turnHidden)!e.has(i)&&!s.has(i)&&this.historyContainer.setHidden(i,!1);for(const i of e)!s.has(i)&&!this.historyContainer.hiddenChildren.has(i)&&this.historyContainer.setHidden(i,!0);this.turnHidden=e,this.applyInboxExchangeDisplay(),this.updateFoldedToolSeparators(t),this.historyContainer.markDirty(),this.tui.requestRender()}hidesInboxExchange(){return this.showGroupSummaries&&this.foldSettledTools}applyInboxExchangeDisplay(){const t=this.hidesInboxExchange();for(const e of this.inboxAnswers)this.historyContainer.setHidden(e,t);for(const e of this.pageBlocks.values())this.historyContainer.setHidden(e,t&&e.settled)}toggleShowWorking(){if(!this.consolidateReplies)return null;this.showWorking=!this.showWorking;for(const t of this.turns)this.refreshWorkingRow(t);return this.applyToolDisplay(),this.showWorking}applyTurnReply(t,e){if(!this.deferIfBuilding(()=>this.applyTurnReply(t,e))&&this.consolidateReplies){this.turnReplies.set(t,e);for(const s of this.turns)s.key===t&&this.refreshTurn(s);this.tui.requestRender()}}turnFoldedComponents(t){const e=new Set;if(!this.consolidateReplies||this.showWorking)return e;for(const s of this.turns){const i=s===this.openReplyTurn;for(const n of s.members)i&&(n===this.streamingComponent||n===this.streamingToolSeparator||n===this.bashComponent||t.has(n))||e.add(n)}return e}beginTurn(t,e=[]){const s=new x(void 0,this.hideThinking,f(),this.hiddenThinkingLabel,this.markdownTransformers),i=new Kt;this.outsideTurns++;try{this.append(i),this.append(s)}finally{this.outsideTurns--}this.registerCopyBlock(s,"assistant","");const n={key:t,reply:s,working:i,members:new Set,prose:e};return this.turns.push(n),this.openReplyTurn=n,this.refreshTurn(n),n}consolidatingHere(){return this.consolidateReplies&&this.appendTarget===this.historyContainer}currentTurn(){return this.openReplyTurn??this.beginTurn(this.carriedTurn.key,[...this.carriedTurn.prose])}recordTurnProse(t,e){if(!this.consolidatingHere()||t.role!=="assistant"||t.stopReason==="aborted"||t.stopReason==="error")return;const s=c(t).trim(),i=R(t);if(s===""||i===void 0)return;const n=this.currentTurn();n.prose.some(r=>r.key===i)||(n.prose.push({key:i,text:s}),e&&this.refreshTurn(n))}refreshTurn(t){const e=W(t.prose,this.turnReplies.get(t.key));t.reply.updateContent(this.replyMessage(e)),this.historyContainer.setHidden(t.reply,e===""),this.historyContainer.markDirty(t.reply),this.registerCopyBlock(t.reply,"assistant",e),e!==""&&t===this.turns.at(-1)&&(this.lastAssistantText=e),this.refreshWorkingRow(t)}refreshWorkingRow(t){let e=0,s=0;for(const a of t.members)a instanceof A?e++:(a instanceof j||a instanceof _||a instanceof F)&&s++;const i=t.prose.length,n=e>0||s>0||i>1,r=[i>0?O(i,"message","messages"):void 0,e>0?O(e,"tool call","tool calls"):void 0,s>0?O(s,"card","cards"):void 0].filter(a=>a!==void 0),o=this.runActive&&t===this.openReplyTurn;t.working.setLabel(this.dimStyle(`${this.showWorking?"\u25BE":"\u25B8"} ${o?"working \u2014 ":""}${r.join(" \xB7 ")}`)),this.historyContainer.setHidden(t.working,!n),this.historyContainer.markDirty(t.working)}replyMessage(t){return{role:"assistant",content:t===""?[]:[{type:"text",text:t}],stopReason:"stop"}}toggleThinking(){this.hideThinking=!this.hideThinking;for(const t of this.historyContainer.children)t instanceof z&&t.setHideThinkingBlock(this.hideThinking);for(const[t,e]of this.foldedToolSeparatorContents)t.setSeparatesToolGroup(e.hasText||!this.hideThinking&&e.hasThinking);return this.historyContainer.markDirty(),this.tui.requestRender(),this.hideThinking}handleEvent(t){if(!this.deferIfBuilding(()=>this.handleEvent(t))){switch(this.projection=_t(this.projection,t),t.type){case"agent_start":{this.runActive=this.projection.isStreaming;for(const e of this.pendingTools.values())this.settleGroupTool(e);this.pendingTools.clear(),this.openReplyTurn&&this.refreshWorkingRow(this.openReplyTurn),this.setActivity(this.makeLoader(this.workingActivity));break}case"agent_end":{this.settleRun(),kt();break}case"agent_settled":{this.settleRun();break}case"message_start":{if(t.message.role==="custom"||t.message.role==="user")this.addMessageToChat(t.message),this.consolidateReplies&&(this.openReplyTurn&&this.refreshWorkingRow(this.openReplyTurn),this.applyToolDisplay());else if(t.message.role==="assistant"){const e=c(t.message).trim()!=="";e&&this.closeOpenGroup(),this.streamingComponent=new x(void 0,this.hideThinking,f(),this.hiddenThinkingLabel,this.markdownTransformers),this.streamingWasGroupMember=!e,this.appendAssistantPart(this.streamingComponent,!e),this.registerCopyBlock(this.streamingComponent,"assistant",""),this.streamingComponent.updateContent(t.message),this.registerCopyBlock(this.streamingComponent,"assistant",c(t.message)),this.streamingToolSeparator=this.appendAssistantSeparator(!e),this.setAssistantSeparatorContent(this.streamingToolSeparator,t.message)}break}case"message_update":{if(this.streamingComponent&&t.message.role==="assistant"){this.promoteStreamingAssistantToBoundary(t.message),this.streamingComponent.updateContent(t.message),this.registerCopyBlock(this.streamingComponent,"assistant",c(t.message)),this.historyContainer.markDirty(this.streamingComponent);const e=this.assistantToolCalls(t.message);this.streamingToolSeparator&&this.setAssistantSeparatorContent(this.streamingToolSeparator,t.message);for(const s of e){this.pushTranscriptValue(s.arguments);const i=this.pendingTools.get(s.id);if(i)i.updateArgs(s.arguments),this.noteGroupTool(i,s.name,s.arguments),this.historyContainer.markDirty(i);else{const n=this.appendToolComponent(s.name,s.id,s.arguments);this.pendingTools.set(s.id,n)}}}break}case"message_end":{if(t.message.role!=="assistant")break;const e=t.message.stopReason==="aborted"||t.message.stopReason==="error";if(this.promoteStreamingAssistantToBoundary(t.message,e),this.streamingComponent&&this.registerCopyBlock(this.streamingComponent,"assistant",c(t.message)),t.message.stopReason!=="aborted"&&t.message.stopReason!=="error"){const s=c(t.message);s.trim()&&(this.lastAssistantText=s,this.pushTranscriptText(s))}if(this.streamingComponent){const s=t.message.stopReason;let i=t.message.errorMessage;if(G(t.message)&&(i="Interrupted",t.message.errorMessage=i,t.message.stopReason="aborted"),this.streamingComponent.updateContent(t.message),s==="aborted"||s==="error"){const n=i??"Error";for(const r of this.pendingTools.values())r.updateResult({content:[{type:"text",text:n}],isError:!0}),this.registerCopyBlock(r,"tool",n),this.settleGroupTool(r,{content:[{type:"text",text:n}],isError:!0},!0),this.historyContainer.markDirty(r);this.pendingTools.clear()}else for(const n of this.pendingTools.values())n.setArgsComplete(),this.historyContainer.markDirty(n);this.historyContainer.markDirty(this.streamingComponent),this.streamingComponent=void 0,this.streamingToolSeparator=void 0}e&&(this.closeOpenGroup(),this.applyToolDisplay()),this.consolidateReplies&&(this.recordTurnProse(t.message,!0),this.openReplyTurn&&this.refreshWorkingRow(this.openReplyTurn),this.applyToolDisplay());break}case"tool_execution_start":{this.pushTranscriptValue(t.args);let e=this.pendingTools.get(t.toolCallId);e?this.noteGroupTool(e,t.toolName,t.args):(e=this.appendToolComponent(t.toolName,t.toolCallId,t.args),this.pendingTools.set(t.toolCallId,e)),e.markExecutionStarted(),this.historyContainer.markDirty(e);break}case"tool_execution_update":{const e=this.projection.partialToolResults.get(t.toolCallId)??t.partialResult;this.pushTranscriptValue(e);const s=this.pendingTools.get(t.toolCallId);s&&(s.updateResult(e,!0),this.registerCopyBlock(s,"tool",this.toolResultText(e)),this.historyContainer.markDirty(s));break}case"tool_execution_end":{const e=this.projection.partialToolResults.get(t.toolCallId)??t.result;this.pushTranscriptValue(e);const s=this.pendingTools.get(t.toolCallId);s&&(s.updateResult(e),this.registerCopyBlock(s,"tool",this.toolResultText(e)),this.pendingTools.delete(t.toolCallId),this.settleGroupTool(s,e,t.isError),this.applyToolDisplay(),this.historyContainer.markDirty(s),this.maybeAppendPageBlock(e));break}case"compaction_start":{const e=t.reason==="manual"?"Compacting context...":"Auto-compacting...";this.setActivity(this.makeLoader(e));break}case"compaction_end":{this.setActivity(this.runActive?this.makeLoader(this.workingActivity):void 0),t.aborted?this.showStatus(t.reason==="manual"?"Compaction cancelled":"Auto-compaction cancelled"):t.result?this.addMessageToChat({role:"compactionSummary",summary:t.result.summary,tokensBefore:t.result.tokensBefore,timestamp:Date.now()}):t.errorMessage&&this.showError(t.errorMessage);break}case"auto_retry_start":{const e=Math.ceil(t.delayMs/1e3);this.setActivity(this.makeLoader(`Retrying (${t.attempt}/${t.maxAttempts}) in ${e}s...`));break}case"auto_retry_end":{this.setActivity(this.runActive?this.makeLoader(this.workingActivity):void 0),t.success||this.showError(`Retry failed after ${t.attempt} attempts: ${t.finalError??"Unknown error"}`);break}case"queue_update":case"session_info_changed":case"thinking_level_changed":this.onFooterEvent?.(t);break;default:break}this.tui.requestRender()}}bashStart(t,e){if(this.deferIfBuilding(()=>this.bashStart(t,e)))return;this.projection=vt(this.projection,t,e),this.closeOpenGroup(),this.settleBashComponent();let s;s=new P(t,this.componentTui(()=>s),e),this.bashComponent=s,this.append(s),this.tui.requestRender()}bashOutput(t){this.deferIfBuilding(()=>this.bashOutput(t))||(this.projection=Ot(this.projection,t),this.pushTranscriptText(t),this.bashComponent&&(this.bashComponent.appendOutput(t),this.historyContainer.markDirty(this.bashComponent)),this.tui.requestRender())}bashEnd(t){this.deferIfBuilding(()=>this.bashEnd(t))||(this.projection=Dt(this.projection,t),this.settleBashComponent(t),this.tui.requestRender())}addMessageToChat(t){const e=ot(t);if(e!==null&&(t.role!=="custom"||t.display===!0)){this.pushTranscriptText(e.body);const s=e.senders.flatMap(n=>n.entries).filter(H).map(n=>n.body.trim()).filter(n=>n!=="");if(!(s.length>0&&e.body.replace(Yt,"").trim()===""&&e.senders.every(n=>n.entries.every(H)))){const n=Z(e.kind),r=new j(e,n,this.toolOutputExpanded,this.dimStyle,o=>this.spinnerStyle(this.labelStyle(o)));rt(e.kind)?(this.closeOpenGroup(),this.append(r)):this.appendGroupMember(r)?.activity.noteCard(e,n)}for(const n of s)this.appendHumanAnswer(n);return}switch(t.role){case"bashExecution":{this.closeOpenGroup(),this.pushTranscriptText(t.output??"");const s=new P(t.command,this.tui,t.excludeFromContext);t.output&&s.appendOutput(t.output),s.setComplete(t.exitCode,t.cancelled,t.truncated?{truncated:!0}:void 0,t.fullOutputPath),this.append(s);break}case"custom":{if(t.display){if(this.pushTranscriptText(this.userMessageText(t)),t.customType===st){this.appendGroupMember(new F(this.userMessageText(t),this.toolOutputExpanded,this.crtrOutputTheme()));break}if(t.customType===nt){this.closeOpenGroup(),this.append(new u(1)),this.append(new w(this.dimStyle(`\u2500\u2500 ${this.userMessageText(t)} \u2500\u2500`),1,0)),this.append(new u(1));break}const s=new _(dt(t),void 0,f());s.setExpanded(this.toolOutputExpanded),this.appendGroupMember(s)}break}case"compactionSummary":{this.closeOpenGroup(),this.append(new u(1));const s=new K($(t),f());s.setExpanded(this.toolOutputExpanded),this.append(s);break}case"branchSummary":{this.closeOpenGroup(),this.append(new u(1));const s=new Y($(t),f());s.setExpanded(this.toolOutputExpanded),this.append(s),this.registerCopyBlock(s,"assistant",t.summary);break}case"user":{if(y(t)&&this.closeOpenGroup(),y(t)&&this.consolidatingHere()){!this.openReplyTurn&&this.carriedTurn.prose.length>0&&this.currentTurn(),this.outsideTurns++;try{this.appendUserMessage(t)}finally{this.outsideTurns--}this.beginTurn(R(t)??`local-${this.turns.length}`);break}this.appendUserMessage(t);break}case"assistant":{const s=c(t).trim()!=="";s&&this.closeOpenGroup(),this.appendAssistantMessage(t,!s);break}default:break}}appendUserMessage(t){const e=this.userMessageText(t);if(!e)return;this.pushTranscriptText(e),this.chatChildCount()>0&&this.appendUserPart(new u(1),!y(t));const s=J(e);if(s){const i=new X({...s,content:L(s.content)},f());if(i.setExpanded(this.toolOutputExpanded),this.appendUserPart(i,!y(t)),this.registerCopyBlock(i,"user",e),s.userMessage){const n=new B(s.userMessage,f(),void 0,this.markdownTransformers);this.appendUserPart(n,!y(t)),this.registerCopyBlock(n,"user",s.userMessage)}}else{const i=new B(e,f(),void 0,this.markdownTransformers);this.appendUserPart(i,!y(t)),this.registerCopyBlock(i,"user",e)}}appendAssistantMessage(t,e=!1){const s=new x(void 0,this.hideThinking,f(),this.hiddenThinkingLabel,this.markdownTransformers),i=G(t)?{...t,stopReason:"aborted",errorMessage:"Interrupted"}:t;s.updateContent(i),this.appendAssistantPart(s,e),this.registerCopyBlock(s,"assistant",c(t));const n=this.appendAssistantSeparator(e);return this.setAssistantSeparatorContent(n,t),n}appendAssistantSeparator(t=!1){const e=new D;return t?this.appendGroupMember(e):this.append(e),e}appendAssistantPart(t,e){e?this.appendGroupMember(t):this.append(t)}appendUserPart(t,e){e?this.appendGroupMember(t):this.append(t)}appendHumanAnswer(t){this.outsideTurns++;try{this.appendHumanAnswerParts(t)}finally{this.outsideTurns--}}appendHumanAnswerParts(t){this.closeOpenGroup();const e=this.appendTarget===this.historyContainer;if(this.chatChildCount()>0){const i=new u(1);this.append(i),e&&this.trackInboxAnswer(i)}const s=new B(t,f(),void 0,this.markdownTransformers);this.append(s),e&&this.trackInboxAnswer(s),this.registerCopyBlock(s,"user",t)}trackInboxAnswer(t){this.inboxAnswers.add(t),this.hidesInboxExchange()&&this.historyContainer.setHidden(t,!0)}isLiveGroupRegion(){return this.appendTarget===this.historyContainer}ensureGroup(){if(!this.isLiveGroupRegion())return;if(this.openGroup)return this.openGroup;const t=new ft,e={members:[],recap:t,activity:new ut(this.cwd,this.nodeRoster),toolCount:0,inFlight:new Set};return this.append(t),this.historyContainer.setHidden(t,!0),this.groups.push(e),this.openGroup=e,e}closeOpenGroup(){this.openGroup=void 0}appendGroupMember(t){const e=this.ensureGroup();return this.append(t),e&&e.members.push(t),e}removeGroupMember(t){for(const e of this.groups){const s=e.members.indexOf(t);s!==-1&&e.members.splice(s,1)}this.historyContainer.setHidden(t,!1)}appendToolComponent(t,e,s){const i=this.isLiveGroupRegion()&&!this.openGroup&&this.historyContainer.children.at(-1)instanceof D?this.historyContainer.children.at(-1):void 0;i&&this.historyContainer.removeChild(i);const n=this.ensureGroup();i&&(this.append(i),n?.members.push(i));const r=this.makeToolComponent(t,e,s);return r.activityCallId=e,this.appendGroupMember(r),this.registerCopyBlock(r,"tool",""),n&&(n.key??=e,n.toolCount++,n.inFlight.add(r),n.activity.noteCall(e,t,s)),r}noteGroupTool(t,e,s){if(t.activityCallId)for(const i of this.groups)i.members.includes(t)&&i.activity.noteCall(t.activityCallId,e,s)}settleGroupTool(t,e,s=!1){for(const i of this.groups)i.inFlight.delete(t),!(!t.activityCallId||!i.members.includes(t))&&(e===void 0?i.activity.resolveWithoutResult(t.activityCallId):i.activity.settle(t.activityCallId,e,s))}promoteStreamingAssistantToBoundary(t,e=!1){!this.streamingWasGroupMember||!e&&c(t).trim()===""||(this.streamingComponent&&this.removeGroupMember(this.streamingComponent),this.streamingToolSeparator&&this.removeGroupMember(this.streamingToolSeparator),this.streamingWasGroupMember=!1,this.closeOpenGroup())}seedToolGroupSummaries(t){this.groupSummaries.clear();for(const[e,s]of Object.entries(t))this.groupSummaries.set(e,s)}applyToolGroupSummary(t,e){if(this.deferIfBuilding(()=>this.applyToolGroupSummary(t,e)))return;this.projection=Et(this.projection,t,e);const s=this.projection.toolGroupSummaryDetails[t];s!==void 0&&(this.groupSummaries.set(t,s),this.applyToolDisplay())}applyGroupDisplay(t,e){const s=t.key===void 0?void 0:this.groupSummaries.get(t.key),i=this.foldSettledTools&&this.showGroupSummaries&&t.toolCount>0&&t.inFlight.size===0&&s!==void 0;t.recap.setRecap(i?s:void 0,t.toolCount,t.activity.activity(),this.detailedToolRecaps,this.recapPalette),this.historyContainer.setHidden(t.recap,!i||e.has(t.recap));for(const n of t.members)this.historyContainer.setHidden(n,i||e.has(n))}updateFoldedToolSeparators(t=new Set(this.pendingTools.values())){const e=this.historyContainer.children;for(let s=0;s<e.length;s++){const i=e[s];if(!(i instanceof D))continue;const n=e[s+1];i.setFolded(this.foldSettledTools&&!this.historyContainer.hiddenChildren.has(i)&&n instanceof A&&!this.historyContainer.hiddenChildren.has(n)&&!t.has(n)),this.historyContainer.markDirty(i)}}setAssistantSeparatorContent(t,e){const s=e.content,i=Array.isArray(s)?s:[],n=i.some(o=>typeof o=="object"&&o!==null&&o.type==="text"&&typeof o.text=="string"&&o.text.trim()!==""),r=i.some(o=>typeof o=="object"&&o!==null&&o.type==="thinking"&&typeof o.thinking=="string"&&o.thinking.trim()!=="");this.foldedToolSeparatorContents.set(t,{hasText:n,hasThinking:r}),t.setSeparatesToolGroup(n||!this.hideThinking&&r)}makeToolComponent(t,e,s){const i=new mt;let n,r;if(t==="bash"){const a=it(s,i);a===void 0?r=yt(this.cwd,i,this.backgroundBashHint):r=a.definition}else if(t==="edit"){const a=ct(this.cwd,i);r=a.definition,n=a.observeResult}else t==="read"?r=Tt(this.cwd,i):t==="write"&&(r=Ct(this.cwd,i));let o;return o=new A(t,e,s,{showImages:this.showImages,imageWidthCells:this.imageWidthCells},r,this.componentTui(()=>o),this.cwd),o.setToolView(r===void 0?void 0:i,n),o.setExpanded(this.toolOutputExpanded),o}append(t){if(this.outsideTurns===0&&this.consolidatingHere()){const e=this.currentTurn();this.appendTarget.addChild(t),e.members.add(t);return}this.appendTarget.addChild(t)}chatChildCount(){return this.appendTarget.children.length}resetChat(){this.setActivity(void 0),this.settleBashComponent(),this.streamingComponent=void 0,this.streamingToolSeparator=void 0,this.streamingWasGroupMember=!1,this.foldedToolSeparatorContents.clear(),this.groups.length=0,this.openGroup=void 0,this.turns.length=0,this.openReplyTurn=void 0,this.carriedTurn={key:M,prose:[]},this.turnHidden=new Set,this.groupSummaries.clear(),this.nodeRoster.clear(),this.pendingTools.clear(),this.lastAssistantText=void 0,this.copyBlockRecords.length=0,this.copyIndex.clear(),this.transcriptFilePaths.length=0,this.transcriptDocRefs.length=0,this.pageBlocks.clear(),this.inboxAnswers.clear(),this.historyContainer.clear()}maybeAppendPageBlock(t){if(!this.inlinePageBlocks||this.appendTarget!==this.historyContainer)return;const e=tt(t);if(e===void 0||this.pageBlocks.has(e.pageId))return;const s=new et(e.pageId,this.pageOpenHint);this.pageBlocks.set(e.pageId,s),this.outsideTurns++;try{this.append(s)}finally{this.outsideTurns--}this.loadPageBlock(s)}async loadPageBlock(t){await t.refresh()&&(this.historyContainer.setHidden(t,this.hidesInboxExchange()&&t.settled),this.historyContainer.markDirty(t),this.tui.requestRender())}refreshInFlightTools(){if(this.pendingTools.size!==0){for(const t of this.pendingTools.values())this.historyContainer.markDirty(t);this.tui.requestRender()}}async refreshPageBlocks(){const t=[...this.pageBlocks.values()];(await Promise.all(t.map(async s=>{const i=await s.refresh();return i&&(this.historyContainer.setHidden(s,this.hidesInboxExchange()&&s.settled),this.historyContainer.markDirty(s)),i}))).some(Boolean)&&this.tui.requestRender()}makeLoader(t){let e;const s={requestRender:l(i=>{e&&!this.onActivityChange&&this.statusContainer.markDirty(e),this.tui.requestRender(i)},"requestRender"),terminal:this.tui.terminal};return e=new V(s,this.spinnerStyle,this.dimStyle,t),e}componentTui(t){return{requestRender:l(e=>{this.historyContainer.markDirty(t()),this.tui.requestRender(e)},"requestRender"),terminal:this.tui.terminal}}settleBashComponent(t){if(!this.bashComponent)return;const e=this.bashComponent;e.setComplete(t?.exitCode,t?.cancelled??!0,t?.truncated?{truncated:!0}:void 0,t?.fullOutputPath),this.historyContainer.markDirty(e),this.bashComponent=void 0}setActivity(t){if(this.activityLoader?.stop(),this.activityLoader=t,this.onActivityChange){this.onActivityChange(t);return}this.statusContainer.clear(),t&&this.statusContainer.addChild(t)}showStatus(t){this.closeOpenGroup(),this.append(new u(1)),this.append(new w(this.dimStyle(t),1,0))}showError(t){if(!this.deferIfBuilding(()=>this.showError(t))){this.closeOpenGroup(),this.outsideTurns++;try{this.append(new u(1)),this.append(new w(this.errorStyle(t),1,0))}finally{this.outsideTurns--}this.tui.requestRender()}}crtrOutputTheme(){return{fg:l((t,e)=>t==="error"?this.errorStyle(e):t==="warning"||t==="accent"||t==="toolTitle"||t==="success"?this.spinnerStyle(e):t==="muted"?this.dimStyle(e):e,"fg"),bg:l((t,e)=>e,"bg"),bold:l(t=>this.labelStyle(t),"bold")}}assistantToolCalls(t){const e=t.content;return Array.isArray(e)?e.filter(s=>typeof s=="object"&&s!==null&&s.type==="toolCall"):[]}getTranscriptFilePathsNewestFirst(){return[...this.transcriptFilePaths]}getTranscriptDocRefsNewestFirst(){return[...this.transcriptDocRefs]}refreshMarkdown(){this.historyContainer.invalidate(),this.tui.requestRender()}getLastAssistantText(){const t=this.lastAssistantText?.trim();return t||void 0}blockText(t){const e=this.copyIndex.get(t);return e===void 0?void 0:this.copyBlockRecords[e]?.text}copyBlocks(){return this.copyBlockRecords}getLastToolOutputText(){for(let t=this.copyBlockRecords.length-1;t>=0;t--){const e=this.copyBlockRecords[t];if(e.kind==="tool")return e.text.trim()===""?void 0:e.text}}getTranscriptPlainText(){return this.copyBlockRecords.map(t=>t.text).filter(t=>t.trim()!=="").join(`
1
+ var N=Object.defineProperty;var l=(h,t)=>N(h,"name",{value:t,configurable:!0});import{Container as v,Loader as V,Spacer as u,Text as w}from"@earendil-works/pi-tui";import{AssistantMessageComponent as z,BashExecutionComponent as P,BranchSummaryMessageComponent as Y,CompactionSummaryMessageComponent as K,CustomMessageComponent as _,parseSkillBlock as J,SkillInvocationMessageComponent as X,ToolExecutionComponent as Q,UserMessageComponent as B}from"@earendil-works/pi-coding-agent";import{attachMarkdownTheme as f}from"../config.js";import{ContextMessageComponent as j}from"./context-message.js";import{presentationFor as Z}from"./card-presentation.js";import{detectInlinePageResult as tt,PageBlockComponent as et}from"./page-block.js";import{CRTR_OUTPUT_CUSTOM_TYPE as st,CrtrOutputMessageComponent as F,createCrtrBashToolDefinition as it}from"./crtr-output.js";import{CRTR_CYCLE_DIVIDER_CUSTOM_TYPE as nt,endedByAbort as G}from"../../../core/runtime/session-cycles.js";import{isHumanWordsEntry as H,parseCard as ot}from"../../../shared/generated-context.js";import{assistantVisibleText as c,isGroupBoundaryKind as rt,isTrueUserMessage as y}from"../../../shared/tool-groups.js";import{messageKey as R,normalizeTurnReplyRecord as at,START_TURN_KEY as M,turnReplyText as W}from"../../../shared/turn-replies.js";import{isCycleDivider as ht}from"./condensed-history.js";import{transformDiagramFences as lt}from"./diagram.js";import{styleAttachAgentMarkdown as pt,styleAttachMarkdownSource as L,styleAttachMessageMarkdown as dt,styleAttachSummaryMarkdown as $}from"./markdown-source.js";import{createCrtrEditToolDefinition as ct}from"./edit-diff.js";import{GroupActivityRecorder as ut}from"./group-activity.js";import{GroupRecapComponent as ft}from"./group-recap.js";import{FoldedToolCallController as mt,bashCallBackgroundHintLines as gt,createCrtrPlainBashToolDefinition as yt,createCrtrReadToolDefinition as Tt,createCrtrWriteToolDefinition as Ct}from"./tool-calls.js";import{ringCompletionBell as kt}from"../chrome/completion-bell.js";import{extractFilePaths as bt}from"../../../core/human/visible-paths.js";import{extractDocRefs as wt}from"./doc-links.js";import{keepWhenCondensed as xt,liveRegionStart as St}from"./condensed-history.js";import{ViewerAssistantMessageComponent as x}from"./assistant-message.js";import{CompositeViewportSource as Rt,MeasuredContainer as E}from"./measured-container.js";import{readConfig as T,updateRawConfigAtomically as At}from"../../../core/config.js";import{DEFAULT_CONDENSED_HISTORY as Bt,DEFAULT_LIVE_CYCLES as Gt}from"../../../types.js";import{applySnapshot as Mt,applyToolGroupSummary as Et,applyWorkingActivity as It,bashEnd as Dt,bashOutput as Ot,bashStart as vt,initialConvState as Pt,reduce as _t}from"../../conversation/projection.js";const q=l(h=>`\x1B[2m${h}\x1B[22m`,"defaultDimStyle"),I=50;function jt(){try{return T("user").live_cycles}catch{return Gt}}l(jt,"readLiveCycles");function Ft(){try{return T("user").condensed_history}catch{return Bt}}l(Ft,"readCondensedHistory");function Ht(){try{return T("user").fold_finished_tools}catch{return!1}}l(Ht,"readFoldFinishedTools");function Wt(){try{return T("user").summarize_tool_calls}catch{return!1}}l(Wt,"readSummarizeToolCalls");function Lt(){try{return T("user").detailed_tool_recaps}catch{return!0}}l(Lt,"readDetailedToolRecaps");function $t(){try{return T("user").consolidate_replies}catch{return!1}}l($t,"readConsolidateReplies");async function qt(h){try{await At("user",t=>({...t,fold_finished_tools:h}))}catch{}}l(qt,"persistFoldFinishedTools");function Ut(h){return typeof h=="object"&&h!==null&&"setExpanded"in h&&typeof h.setExpanded=="function"}l(Ut,"isExpandable");const Nt=/\x1b\[[0-9;]*m/g,Vt=/\x1b\[([0-9;]*)m/g;function zt(h){return h.replace(Vt,(t,e)=>{const s=e.split(";"),i=[];for(let n=0;n<s.length;n++){const r=Number(s[n]===""?0:s[n]);if(r===38||r===48||r===58){const o=Number(s[n+1]),a=n+(o===5?2:o===2?4:1);r!==48&&i.push(...s.slice(n,a+1)),n=a;continue}r>=40&&r<=47||r>=100&&r<=107||i.push(String(r))}return i.length>0?`\x1B[${i.join(";")}m`:""})}l(zt,"stripBackground");const Yt=/<from\b[^>]*>[\s\S]*?<\/from>/g;class D extends u{static{l(this,"FoldedToolSeparator")}folded=!1;separatesToolGroup=!1;setFolded(t){this.folded=t,this.refresh()}setSeparatesToolGroup(t){this.separatesToolGroup=t,this.refresh()}refresh(){this.setLines(this.folded&&this.separatesToolGroup?1:0)}}class Kt extends v{static{l(this,"WorkingRow")}label=new w("",1,0);constructor(){super(),this.addChild(new u(1)),this.addChild(this.label)}setLabel(t){this.label.setText(t)}}function O(h,t,e){return`${h} ${h===1?t:e}`}l(O,"plural");class A extends Q{static{l(this,"MinimizableToolComponent")}activityCallId;minimized=!1;expandedState=!1;foldController;resultObserver;setToolView(t,e){this.foldController=t,this.resultObserver=e}updateResult(...t){this.resultObserver?.(t[0],t[1]??!1);const e=t[0],s=e.content;Array.isArray(s)&&/^elapsed \d+(?:h \d+)?m \d+s(?:\n|$)/.test(s.at(-1)?.text??"")?super.updateResult({...e,content:s.slice(0,-1)},t[1]):super.updateResult(...t)}setExpanded(t){this.expandedState=t,super.setExpanded(t)}setMinimized(t){!this.foldController||this.minimized===t||(this.minimized=t,this.foldController.setFolded(t),super.setExpanded(this.expandedState))}render(t){if(!this.minimized)return[...super.render(t),...gt(this.foldController?.callComponent(),t)];const e=this.foldController?.callComponent();return e?e.render(t).filter(s=>s.replace(Nt,"").trim().length>0).map(s=>zt(s).trimEnd()):super.render(t)}}const Jt=l(h=>`\x1B[31m${h}\x1B[39m`,"defaultErrorStyle"),Xt={accent:l(h=>`\x1B[33m${h}\x1B[39m`,"accent"),active:l(h=>`\x1B[36m${h}\x1B[39m`,"active"),info:l(h=>`\x1B[34m${h}\x1B[39m`,"info"),muted:q,faint:q,border:l(h=>h,"border"),surface:l(h=>h,"surface"),bold:l(h=>`\x1B[1m${h}\x1B[22m`,"bold"),error:Jt,warning:l(h=>`\x1B[33m${h}\x1B[39m`,"warning"),diffAdded:l(h=>`\x1B[32m${h}\x1B[39m`,"diffAdded"),diffRemoved:l(h=>`\x1B[91m${h}\x1B[39m`,"diffRemoved"),bashMode:l(h=>`\x1B[32m${h}\x1B[39m`,"bashMode"),bashModeAlt:l(h=>`\x1B[92m${h}\x1B[39m`,"bashModeAlt")};class Ae{static{l(this,"ChatView")}tui;container;historyContainer=new E;statusContainer=new E;bannerContainer;transcript;appendTarget=this.historyContainer;pageBlocks=new Map;inboxAnswers=new Set;inlinePageBlocks;pageOpenHint;backgroundBashHint;liveCycles;condensedHistory;cwd;showImages;imageWidthCells;hideThinking;hiddenThinkingLabel;toolOutputExpanded;foldSettledTools=Ht();summarizeToolCalls=Wt();detailedToolRecaps=Lt();showGroupSummaries=this.summarizeToolCalls&&this.foldSettledTools;groups=[];openGroup;consolidateReplies=$t();showWorking=!1;turnReplies=new Map;turns=[];openReplyTurn;carriedTurn={key:M,prose:[]};outsideTurns=0;turnHidden=new Set;groupSummaries=new Map;nodeRoster=new Map;streamingWasGroupMember=!1;onFooterEvent;onActivityChange;spinnerStyle;dimStyle;errorStyle;recapPalette;labelStyle=l(t=>`\x1B[1m${t}\x1B[22m`,"labelStyle");markdownTransformers=[(t,{messageType:e,availableWidth:s})=>e==="assistant"?lt(t,s):t,(t,{messageType:e})=>e==="user"?L(t):pt(t)];streamingComponent;streamingToolSeparator;foldedToolSeparatorContents=new Map;pendingTools=new Map;bashComponent;activityLoader;projection=Pt();workingActivity=this.projection.workingActivity;runActive=!1;applyingSnapshot=!1;snapshotGeneration=0;deferredFrames=[];lastAssistantText;transcriptFilePaths=[];transcriptDocRefs=[];copyBlockRecords=[];copyIndex=new Map;condensedCopyBlocks=new WeakMap;constructor(t,e,s={}){if(this.tui=t,this.container=e,this.cwd=s.cwd??process.cwd(),this.showImages=s.showImages??!0,this.imageWidthCells=s.imageWidthCells??60,this.hideThinking=s.hideThinking??!1,this.hiddenThinkingLabel=s.hiddenThinkingLabel??"Thinking...",this.toolOutputExpanded=s.toolOutputExpanded??!1,this.liveCycles=s.liveCycles??jt(),this.condensedHistory=s.condensedHistory??Ft(),this.onFooterEvent=s.onFooterEvent,this.onActivityChange=s.onActivityChange,this.inlinePageBlocks=s.inlinePageBlocks??!1,this.pageOpenHint=s.pageOpenHint??(()=>"crtr human list"),this.backgroundBashHint=s.backgroundBashHint??(()=>null),this.recapPalette=s.palette??Xt,this.spinnerStyle=this.recapPalette.active,this.dimStyle=this.recapPalette.muted,this.errorStyle=this.recapPalette.error,s.banner&&s.banner.length>0){const i=new E;i.addChild(new u(1));for(const n of s.banner)i.addChild(new w(n,1,0));i.addChild(new u(1)),this.bannerContainer=i,this.container.addChild(i)}this.container.addChild(this.historyContainer),this.onActivityChange||this.container.addChild(this.statusContainer),this.transcript=new Rt([...this.bannerContainer?[this.bannerContainer]:[],this.historyContainer,...this.onActivityChange?[]:[this.statusContainer]])}transcriptSource(){return this.transcript}async applySnapshot(t){const e=++this.snapshotGeneration;if(this.applyingSnapshot=!0,this.deferredFrames.length=0,this.resetChat(),this.projection=Mt(t,this.projection),this.workingActivity=this.projection.workingActivity,this.runActive=this.projection.isStreaming,this.seedToolGroupSummaries(this.projection.toolGroupSummaryDetails),this.turnReplies.clear(),this.consolidateReplies)for(const[o,a]of Object.entries(at(t.turnReplies)))this.turnReplies.set(o,a);const s=new Map,i=this.projection.messages,n=i.length-1,r=St(i,this.liveCycles);if(r>0){const o=await this.buildCondensedBlock(i.slice(0,r),e);if(e!==this.snapshotGeneration)return;o&&this.appendCondensedBlock(o)}for(let o=r;o<i.length;o++){if(!this.projection.isStreaming&&o>r&&(o-r)%I===0&&(await new Promise(p=>setImmediate(p)),e!==this.snapshotGeneration))return;const a=i[o];if(a.role==="assistant"){const p=c(a);p.trim()&&(this.lastAssistantText=p,this.pushTranscriptText(p));const d=o===n&&this.projection.isStreaming&&a.stopReason!=="aborted"&&a.stopReason!=="error",C=c(a).trim()!=="",U=this.assistantToolCalls(a),k=a.stopReason==="aborted"||a.stopReason==="error",b=G(a)?"Interrupted":a.errorMessage??"Error";if(k){for(const m of s.values()){const g={content:[{type:"text",text:b}],isError:!0};m.updateResult(g),this.registerCopyBlock(m,"tool",b),this.settleGroupTool(m,g,!0)}s.clear()}(C||k)&&this.closeOpenGroup();let S;d?(this.streamingComponent=new x(void 0,this.hideThinking,f(),this.hiddenThinkingLabel,this.markdownTransformers),this.appendAssistantPart(this.streamingComponent,!C),this.registerCopyBlock(this.streamingComponent,"assistant",""),this.streamingWasGroupMember=!C,this.streamingComponent.updateContent(a),this.registerCopyBlock(this.streamingComponent,"assistant",c(a)),S=this.streamingToolSeparator=this.appendAssistantSeparator(!C)):S=this.appendAssistantMessage(a,!C&&!k),S&&this.setAssistantSeparatorContent(S,a);for(const m of U){this.pushTranscriptValue(m.arguments);const g=this.appendToolComponent(m.name,m.id,m.arguments);a.stopReason==="aborted"||a.stopReason==="error"?(g.updateResult({content:[{type:"text",text:b}],isError:!0}),this.registerCopyBlock(g,"tool",b),this.settleGroupTool(g,{content:[{type:"text",text:b}],isError:!0},!0)):s.set(m.id,g)}k&&this.closeOpenGroup(),!d&&!k&&this.recordTurnProse(a,!1)}else if(a.role==="toolResult"){this.pushTranscriptText(this.userMessageText(a));const p=s.get(a.toolCallId);p&&(p.updateResult(a),this.registerCopyBlock(p,"tool",this.toolResultText(a)),this.historyContainer.markDirty(p),this.settleGroupTool(p,a,a.isError===!0),s.delete(a.toolCallId),this.maybeAppendPageBlock(a))}else this.addMessageToChat(a)}for(const[o,a]of s)this.pendingTools.set(o,a);for(const o of this.turns)this.refreshTurn(o);this.applyToolDisplay(),this.projection.isStreaming&&this.setActivity(this.makeLoader(this.workingActivity)),this.tui.requestRender(),this.applyingSnapshot=!1,this.replayDeferredFrames()}async buildCondensedBlock(t,e){const s=new v,i=this.appendTarget,n=[];this.appendTarget=s,this.condensedCopyBlocks.set(s,n);try{if(this.consolidateReplies)return await this.buildCondensedTurns(t,e)&&s.children.length>0?{component:s,copyBlocks:n}:void 0;let r=0;for(const o of t)if(xt(o,this.condensedHistory)){if(r>0&&r%I===0&&(await new Promise(a=>setImmediate(a)),e!==this.snapshotGeneration))return;r++,this.pushTranscriptText(this.userMessageText(o)),this.addMessageToChat(o)}}finally{this.appendTarget=i,this.condensedCopyBlocks.delete(s)}return s.children.length>0?{component:s,copyBlocks:n}:void 0}async buildCondensedTurns(t,e){const s=[{key:M,prose:[],dividers:[]}];for(const o of t){const a=s.at(-1);if(y(o))s.push({key:R(o)??`local-${s.length}`,user:o,prose:[],dividers:[]});else if(ht(o))a.dividers.push(o);else if(o.role==="assistant"&&o.stopReason!=="aborted"&&o.stopReason!=="error"){const p=c(o).trim(),d=R(o);p!==""&&d!==void 0&&a.prose.push({key:d,text:p})}}const i=s.pop();let n=0;const r=l(async(o,a)=>{if(n>0&&n%I===0&&(await new Promise(d=>setImmediate(d)),e!==this.snapshotGeneration))return!1;n++,o.user!==void 0&&(this.pushTranscriptText(this.userMessageText(o.user)),this.addMessageToChat(o.user));const p=a?W(o.prose,this.turnReplies.get(o.key)):"";if(p!==""){const d=new x(void 0,this.hideThinking,f(),this.hiddenThinkingLabel,this.markdownTransformers);d.updateContent(this.replyMessage(p)),this.append(d),this.registerCopyBlock(d,"assistant",p)}for(const d of o.dividers)this.addMessageToChat(d);return!0},"render");for(const o of s)if(!await r(o,!0))return!1;return await r(i,!1)?(this.carriedTurn={key:i.key,prose:i.prose},!0):!1}deferIfBuilding(t){return this.applyingSnapshot?(this.deferredFrames.push(t),!0):!1}replayDeferredFrames(){const t=this.deferredFrames.splice(0);for(const e of t)e()}setWorkingActivity(t){if(this.deferIfBuilding(()=>this.setWorkingActivity(t)))return;const e=this.projection.activity===this.workingActivity;this.projection=It(this.projection,t),this.workingActivity=this.projection.workingActivity,this.runActive&&e&&this.setActivity(this.makeLoader(this.workingActivity))}settleRun(){this.runActive=!1;for(const t of this.pendingTools.values())this.settleGroupTool(t);if(this.streamingComponent){this.historyContainer.removeChild(this.streamingComponent);for(const t of this.turns)t.members.delete(this.streamingComponent);this.turnHidden.delete(this.streamingComponent),this.unregisterCopyBlock(this.streamingComponent),this.streamingComponent=void 0}this.streamingToolSeparator=void 0,this.pendingTools.clear(),this.openReplyTurn&&this.refreshWorkingRow(this.openReplyTurn),this.applyToolDisplay(),this.setActivity(void 0),this.settleBashComponent()}dispose(){this.settleRun()}toggleToolsExpanded(){let t;return this.summarizeToolCalls&&this.foldSettledTools?(this.showGroupSummaries=!this.showGroupSummaries,this.toolOutputExpanded=!this.showGroupSummaries,t={kind:"summaries",shown:this.showGroupSummaries}):(this.toolOutputExpanded=!this.toolOutputExpanded,t={kind:"output",expanded:this.toolOutputExpanded}),this.applyToolDisplay(),t}toggleFoldSettledTools(){return this.foldSettledTools=!this.foldSettledTools,qt(this.foldSettledTools),this.summarizeToolCalls&&this.foldSettledTools&&(this.showGroupSummaries=!0),this.applyToolDisplay(),this.foldSettledTools}applyToolDisplay(){const t=new Set(this.pendingTools.values());for(const i of this.groups)for(const n of i.inFlight)t.add(n);for(const i of this.historyContainer.children)i instanceof A&&i.setMinimized(this.foldSettledTools&&!t.has(i)),Ut(i)&&i.setExpanded(this.toolOutputExpanded);const e=this.turnFoldedComponents(t),s=new Set;for(const i of this.groups){this.applyGroupDisplay(i,e),s.add(i.recap);for(const n of i.members)s.add(n)}for(const i of this.turnHidden)!e.has(i)&&!s.has(i)&&this.historyContainer.setHidden(i,!1);for(const i of e)!s.has(i)&&!this.historyContainer.hiddenChildren.has(i)&&this.historyContainer.setHidden(i,!0);this.turnHidden=e,this.applyInboxExchangeDisplay(),this.updateFoldedToolSeparators(t),this.historyContainer.markDirty(),this.tui.requestRender()}hidesInboxExchange(){return this.showGroupSummaries&&this.foldSettledTools}applyInboxExchangeDisplay(){const t=this.hidesInboxExchange();for(const e of this.inboxAnswers)this.historyContainer.setHidden(e,t);for(const e of this.pageBlocks.values())this.historyContainer.setHidden(e,t&&e.settled)}toggleShowWorking(){if(!this.consolidateReplies)return null;this.showWorking=!this.showWorking;for(const t of this.turns)this.refreshWorkingRow(t);return this.applyToolDisplay(),this.showWorking}applyTurnReply(t,e){if(!this.deferIfBuilding(()=>this.applyTurnReply(t,e))&&this.consolidateReplies){this.turnReplies.set(t,e);for(const s of this.turns)s.key===t&&this.refreshTurn(s);this.tui.requestRender()}}turnFoldedComponents(t){const e=new Set;if(!this.consolidateReplies||this.showWorking)return e;for(const s of this.turns){const i=s===this.openReplyTurn;for(const n of s.members)i&&(n===this.streamingComponent||n===this.streamingToolSeparator||n===this.bashComponent||t.has(n))||e.add(n)}return e}beginTurn(t,e=[]){const s=new x(void 0,this.hideThinking,f(),this.hiddenThinkingLabel,this.markdownTransformers),i=new Kt;this.outsideTurns++;try{this.append(i),this.append(s)}finally{this.outsideTurns--}this.registerCopyBlock(s,"assistant","");const n={key:t,reply:s,working:i,members:new Set,prose:e};return this.turns.push(n),this.openReplyTurn=n,this.refreshTurn(n),n}consolidatingHere(){return this.consolidateReplies&&this.appendTarget===this.historyContainer}currentTurn(){return this.openReplyTurn??this.beginTurn(this.carriedTurn.key,[...this.carriedTurn.prose])}recordTurnProse(t,e){if(!this.consolidatingHere()||t.role!=="assistant"||t.stopReason==="aborted"||t.stopReason==="error")return;const s=c(t).trim(),i=R(t);if(s===""||i===void 0)return;const n=this.currentTurn();n.prose.some(r=>r.key===i)||(n.prose.push({key:i,text:s}),e&&this.refreshTurn(n))}refreshTurn(t){const e=W(t.prose,this.turnReplies.get(t.key));t.reply.updateContent(this.replyMessage(e)),this.historyContainer.setHidden(t.reply,e===""),this.historyContainer.markDirty(t.reply),this.registerCopyBlock(t.reply,"assistant",e),e!==""&&t===this.turns.at(-1)&&(this.lastAssistantText=e),this.refreshWorkingRow(t)}refreshWorkingRow(t){let e=0,s=0;for(const a of t.members)a instanceof A?e++:(a instanceof j||a instanceof _||a instanceof F)&&s++;const i=t.prose.length,n=e>0||s>0||i>1,r=[i>0?O(i,"message","messages"):void 0,e>0?O(e,"tool call","tool calls"):void 0,s>0?O(s,"card","cards"):void 0].filter(a=>a!==void 0),o=this.runActive&&t===this.openReplyTurn;t.working.setLabel(this.dimStyle(`${this.showWorking?"\u25BE":"\u25B8"} ${o?"working \u2014 ":""}${r.join(" \xB7 ")}`)),this.historyContainer.setHidden(t.working,!n),this.historyContainer.markDirty(t.working)}replyMessage(t){return{role:"assistant",content:t===""?[]:[{type:"text",text:t}],stopReason:"stop"}}toggleThinking(){this.hideThinking=!this.hideThinking;for(const t of this.historyContainer.children)t instanceof z&&t.setHideThinkingBlock(this.hideThinking);for(const[t,e]of this.foldedToolSeparatorContents)t.setSeparatesToolGroup(e.hasText||!this.hideThinking&&e.hasThinking);return this.historyContainer.markDirty(),this.tui.requestRender(),this.hideThinking}handleEvent(t){if(!this.deferIfBuilding(()=>this.handleEvent(t))){switch(this.projection=_t(this.projection,t),t.type){case"agent_start":{this.runActive=this.projection.isStreaming;for(const e of this.pendingTools.values())this.settleGroupTool(e);this.pendingTools.clear(),this.openReplyTurn&&this.refreshWorkingRow(this.openReplyTurn),this.setActivity(this.makeLoader(this.workingActivity));break}case"agent_end":{this.settleRun(),kt();break}case"agent_settled":{this.settleRun();break}case"message_start":{if(t.message.role==="custom"||t.message.role==="user")this.addMessageToChat(t.message),this.consolidateReplies&&(this.openReplyTurn&&this.refreshWorkingRow(this.openReplyTurn),this.applyToolDisplay());else if(t.message.role==="assistant"){const e=c(t.message).trim()!=="";e&&this.closeOpenGroup(),this.streamingComponent=new x(void 0,this.hideThinking,f(),this.hiddenThinkingLabel,this.markdownTransformers),this.streamingWasGroupMember=!e,this.appendAssistantPart(this.streamingComponent,!e),this.registerCopyBlock(this.streamingComponent,"assistant",""),this.streamingComponent.updateContent(t.message),this.registerCopyBlock(this.streamingComponent,"assistant",c(t.message)),this.streamingToolSeparator=this.appendAssistantSeparator(!e),this.setAssistantSeparatorContent(this.streamingToolSeparator,t.message)}break}case"message_update":{if(this.streamingComponent&&t.message.role==="assistant"){this.promoteStreamingAssistantToBoundary(t.message),this.streamingComponent.updateContent(t.message),this.registerCopyBlock(this.streamingComponent,"assistant",c(t.message)),this.historyContainer.markDirty(this.streamingComponent);const e=this.assistantToolCalls(t.message);this.streamingToolSeparator&&this.setAssistantSeparatorContent(this.streamingToolSeparator,t.message);for(const s of e){this.pushTranscriptValue(s.arguments);const i=this.pendingTools.get(s.id);if(i)i.updateArgs(s.arguments),this.noteGroupTool(i,s.name,s.arguments),this.historyContainer.markDirty(i);else{const n=this.appendToolComponent(s.name,s.id,s.arguments);this.pendingTools.set(s.id,n)}}}break}case"message_end":{if(t.message.role!=="assistant")break;const e=t.message.stopReason==="aborted"||t.message.stopReason==="error";if(this.promoteStreamingAssistantToBoundary(t.message,e),this.streamingComponent&&this.registerCopyBlock(this.streamingComponent,"assistant",c(t.message)),t.message.stopReason!=="aborted"&&t.message.stopReason!=="error"){const s=c(t.message);s.trim()&&(this.lastAssistantText=s,this.pushTranscriptText(s))}if(this.streamingComponent){const s=t.message.stopReason;let i=t.message.errorMessage;if(G(t.message)&&(i="Interrupted",t.message.errorMessage=i,t.message.stopReason="aborted"),this.streamingComponent.updateContent(t.message),s==="aborted"||s==="error"){const n=i??"Error";for(const r of this.pendingTools.values())r.updateResult({content:[{type:"text",text:n}],isError:!0}),this.registerCopyBlock(r,"tool",n),this.settleGroupTool(r,{content:[{type:"text",text:n}],isError:!0},!0),this.historyContainer.markDirty(r);this.pendingTools.clear()}else for(const n of this.pendingTools.values())n.setArgsComplete(),this.historyContainer.markDirty(n);this.historyContainer.markDirty(this.streamingComponent),this.streamingComponent=void 0,this.streamingToolSeparator=void 0}e&&(this.closeOpenGroup(),this.applyToolDisplay()),this.consolidateReplies&&(this.recordTurnProse(t.message,!0),this.openReplyTurn&&this.refreshWorkingRow(this.openReplyTurn),this.applyToolDisplay());break}case"tool_execution_start":{this.pushTranscriptValue(t.args);let e=this.pendingTools.get(t.toolCallId);e?this.noteGroupTool(e,t.toolName,t.args):(e=this.appendToolComponent(t.toolName,t.toolCallId,t.args),this.pendingTools.set(t.toolCallId,e)),e.markExecutionStarted(),this.historyContainer.markDirty(e);break}case"tool_execution_update":{const e=this.projection.partialToolResults.get(t.toolCallId)??t.partialResult;this.pushTranscriptValue(e);const s=this.pendingTools.get(t.toolCallId);s&&(s.updateResult(e,!0),this.registerCopyBlock(s,"tool",this.toolResultText(e)),this.historyContainer.markDirty(s));break}case"tool_execution_end":{const e=this.projection.partialToolResults.get(t.toolCallId)??t.result;this.pushTranscriptValue(e);const s=this.pendingTools.get(t.toolCallId);s&&(s.updateResult(e),this.registerCopyBlock(s,"tool",this.toolResultText(e)),this.pendingTools.delete(t.toolCallId),this.settleGroupTool(s,e,t.isError),this.applyToolDisplay(),this.historyContainer.markDirty(s),this.maybeAppendPageBlock(e));break}case"compaction_start":{const e=t.reason==="manual"?"Compacting context...":"Auto-compacting...";this.setActivity(this.makeLoader(e));break}case"compaction_end":{this.setActivity(this.runActive?this.makeLoader(this.workingActivity):void 0),t.aborted?this.showStatus(t.reason==="manual"?"Compaction cancelled":"Auto-compaction cancelled"):t.result?this.addMessageToChat({role:"compactionSummary",summary:t.result.summary,tokensBefore:t.result.tokensBefore,timestamp:Date.now()}):t.errorMessage&&this.showError(t.errorMessage);break}case"auto_retry_start":{const e=Math.ceil(t.delayMs/1e3);this.setActivity(this.makeLoader(`Retrying (${t.attempt}/${t.maxAttempts}) in ${e}s...`));break}case"auto_retry_end":{this.setActivity(this.runActive?this.makeLoader(this.workingActivity):void 0),t.success||this.showError(`Retry failed after ${t.attempt} attempts: ${t.finalError??"Unknown error"}`);break}case"queue_update":case"session_info_changed":case"thinking_level_changed":this.onFooterEvent?.(t);break;default:break}this.tui.requestRender()}}bashStart(t,e){if(this.deferIfBuilding(()=>this.bashStart(t,e)))return;this.projection=vt(this.projection,t,e),this.closeOpenGroup(),this.settleBashComponent();let s;s=new P(t,this.componentTui(()=>s),e),this.bashComponent=s,this.append(s),this.tui.requestRender()}bashOutput(t){this.deferIfBuilding(()=>this.bashOutput(t))||(this.projection=Ot(this.projection,t),this.pushTranscriptText(t),this.bashComponent&&(this.bashComponent.appendOutput(t),this.historyContainer.markDirty(this.bashComponent)),this.tui.requestRender())}bashEnd(t){this.deferIfBuilding(()=>this.bashEnd(t))||(this.projection=Dt(this.projection,t),this.settleBashComponent(t),this.tui.requestRender())}addMessageToChat(t){const e=ot(t);if(e!==null&&(t.role!=="custom"||t.display===!0)){this.pushTranscriptText(e.body);const s=e.senders.flatMap(n=>n.entries).filter(H).map(n=>n.body.trim()).filter(n=>n!=="");if(!(s.length>0&&e.body.replace(Yt,"").trim()===""&&e.senders.every(n=>n.entries.every(H)))){const n=Z(e.kind),r=new j(e,n,this.toolOutputExpanded,this.dimStyle,o=>this.spinnerStyle(this.labelStyle(o)));rt(e.kind)?(this.closeOpenGroup(),this.append(r)):this.appendGroupMember(r)?.activity.noteCard(e,n)}for(const n of s)this.appendHumanAnswer(n);return}switch(t.role){case"bashExecution":{this.closeOpenGroup(),this.pushTranscriptText(t.output??"");const s=new P(t.command,this.tui,t.excludeFromContext);t.output&&s.appendOutput(t.output),s.setComplete(t.exitCode,t.cancelled,t.truncated?{truncated:!0}:void 0,t.fullOutputPath),this.append(s);break}case"custom":{if(t.display){if(this.pushTranscriptText(this.userMessageText(t)),t.customType===st){this.appendGroupMember(new F(this.userMessageText(t),this.toolOutputExpanded,this.crtrOutputTheme()));break}if(t.customType===nt){this.closeOpenGroup(),this.append(new u(1)),this.append(new w(this.dimStyle(`\u2500\u2500 ${this.userMessageText(t)} \u2500\u2500`),1,0)),this.append(new u(1));break}const s=new _(dt(t),void 0,f());s.setExpanded(this.toolOutputExpanded),this.appendGroupMember(s)}break}case"compactionSummary":{this.closeOpenGroup(),this.append(new u(1));const s=new K($(t),f());s.setExpanded(this.toolOutputExpanded),this.append(s);break}case"branchSummary":{this.closeOpenGroup(),this.append(new u(1));const s=new Y($(t),f());s.setExpanded(this.toolOutputExpanded),this.append(s),this.registerCopyBlock(s,"assistant",t.summary);break}case"user":{if(y(t)&&this.closeOpenGroup(),y(t)&&this.consolidatingHere()){!this.openReplyTurn&&this.carriedTurn.prose.length>0&&this.currentTurn(),this.outsideTurns++;try{this.appendUserMessage(t)}finally{this.outsideTurns--}this.beginTurn(R(t)??`local-${this.turns.length}`);break}this.appendUserMessage(t);break}case"assistant":{const s=c(t).trim()!=="";s&&this.closeOpenGroup(),this.appendAssistantMessage(t,!s);break}default:break}}appendUserMessage(t){const e=this.userMessageText(t);if(!e)return;this.pushTranscriptText(e),this.chatChildCount()>0&&this.appendUserPart(new u(1),!y(t));const s=J(e);if(s){const i=new X({...s,content:L(s.content)},f());if(i.setExpanded(this.toolOutputExpanded),this.appendUserPart(i,!y(t)),this.registerCopyBlock(i,"user",e),s.userMessage){const n=new B(s.userMessage,f(),void 0,this.markdownTransformers);this.appendUserPart(n,!y(t)),this.registerCopyBlock(n,"user",s.userMessage)}}else{const i=new B(e,f(),void 0,this.markdownTransformers);this.appendUserPart(i,!y(t)),this.registerCopyBlock(i,"user",e)}}appendAssistantMessage(t,e=!1){const s=new x(void 0,this.hideThinking,f(),this.hiddenThinkingLabel,this.markdownTransformers),i=G(t)?{...t,stopReason:"aborted",errorMessage:"Interrupted"}:t;s.updateContent(i),this.appendAssistantPart(s,e),this.registerCopyBlock(s,"assistant",c(t));const n=this.appendAssistantSeparator(e);return this.setAssistantSeparatorContent(n,t),n}appendAssistantSeparator(t=!1){const e=new D;return t?this.appendGroupMember(e):this.append(e),e}appendAssistantPart(t,e){e?this.appendGroupMember(t):this.append(t)}appendUserPart(t,e){e?this.appendGroupMember(t):this.append(t)}appendHumanAnswer(t){this.outsideTurns++;try{this.appendHumanAnswerParts(t)}finally{this.outsideTurns--}}appendHumanAnswerParts(t){this.closeOpenGroup();const e=this.appendTarget===this.historyContainer;if(this.chatChildCount()>0){const i=new u(1);this.append(i),e&&this.trackInboxAnswer(i)}const s=new B(t,f(),void 0,this.markdownTransformers);this.append(s),e&&this.trackInboxAnswer(s),this.registerCopyBlock(s,"user",t)}trackInboxAnswer(t){this.inboxAnswers.add(t),this.hidesInboxExchange()&&this.historyContainer.setHidden(t,!0)}isLiveGroupRegion(){return this.appendTarget===this.historyContainer}ensureGroup(){if(!this.isLiveGroupRegion())return;if(this.openGroup)return this.openGroup;const t=new ft,e={members:[],recap:t,activity:new ut(this.cwd,this.nodeRoster),toolCount:0,inFlight:new Set};return this.append(t),this.historyContainer.setHidden(t,!0),this.groups.push(e),this.openGroup=e,e}closeOpenGroup(){this.openGroup=void 0}appendGroupMember(t){const e=this.ensureGroup();return this.append(t),e&&e.members.push(t),e}removeGroupMember(t){for(const e of this.groups){const s=e.members.indexOf(t);s!==-1&&e.members.splice(s,1)}this.historyContainer.setHidden(t,!1)}appendToolComponent(t,e,s){const i=this.isLiveGroupRegion()&&!this.openGroup&&this.historyContainer.children.at(-1)instanceof D?this.historyContainer.children.at(-1):void 0;i&&this.historyContainer.removeChild(i);const n=this.ensureGroup();i&&(this.append(i),n?.members.push(i));const r=this.makeToolComponent(t,e,s);return r.activityCallId=e,this.appendGroupMember(r),this.registerCopyBlock(r,"tool",""),n&&(n.key??=e,n.toolCount++,n.inFlight.add(r),n.activity.noteCall(e,t,s)),r}noteGroupTool(t,e,s){if(t.activityCallId)for(const i of this.groups)i.members.includes(t)&&i.activity.noteCall(t.activityCallId,e,s)}settleGroupTool(t,e,s=!1){for(const i of this.groups)i.inFlight.delete(t),!(!t.activityCallId||!i.members.includes(t))&&(e===void 0?i.activity.resolveWithoutResult(t.activityCallId):i.activity.settle(t.activityCallId,e,s))}promoteStreamingAssistantToBoundary(t,e=!1){!this.streamingWasGroupMember||!e&&c(t).trim()===""||(this.streamingComponent&&this.removeGroupMember(this.streamingComponent),this.streamingToolSeparator&&this.removeGroupMember(this.streamingToolSeparator),this.streamingWasGroupMember=!1,this.closeOpenGroup())}seedToolGroupSummaries(t){this.groupSummaries.clear();for(const[e,s]of Object.entries(t))this.groupSummaries.set(e,s)}applyToolGroupSummary(t,e){if(this.deferIfBuilding(()=>this.applyToolGroupSummary(t,e)))return;this.projection=Et(this.projection,t,e);const s=this.projection.toolGroupSummaryDetails[t];s!==void 0&&(this.groupSummaries.set(t,s),this.applyToolDisplay())}applyGroupDisplay(t,e){const s=t.key===void 0?void 0:this.groupSummaries.get(t.key),i=this.foldSettledTools&&this.showGroupSummaries&&t.toolCount>0&&t.inFlight.size===0&&s!==void 0;t.recap.setRecap(i?s:void 0,t.toolCount,t.activity.activity(),this.detailedToolRecaps,this.recapPalette),this.historyContainer.setHidden(t.recap,!i||e.has(t.recap));for(const n of t.members)this.historyContainer.setHidden(n,i||e.has(n))}updateFoldedToolSeparators(t=new Set(this.pendingTools.values())){const e=this.historyContainer.children;for(let s=0;s<e.length;s++){const i=e[s];if(!(i instanceof D))continue;const n=e[s+1];i.setFolded(this.foldSettledTools&&!this.historyContainer.hiddenChildren.has(i)&&n instanceof A&&!this.historyContainer.hiddenChildren.has(n)&&!t.has(n)),this.historyContainer.markDirty(i)}}setAssistantSeparatorContent(t,e){const s=e.content,i=Array.isArray(s)?s:[],n=i.some(o=>typeof o=="object"&&o!==null&&o.type==="text"&&typeof o.text=="string"&&o.text.trim()!==""),r=i.some(o=>typeof o=="object"&&o!==null&&o.type==="thinking"&&typeof o.thinking=="string"&&o.thinking.trim()!=="");this.foldedToolSeparatorContents.set(t,{hasText:n,hasThinking:r}),t.setSeparatesToolGroup(n||!this.hideThinking&&r)}makeToolComponent(t,e,s){const i=new mt;let n,r;if(t==="bash"){const a=it(s,i);a===void 0?r=yt(this.cwd,i,this.backgroundBashHint):r=a.definition}else if(t==="edit"){const a=ct(this.cwd,i);r=a.definition,n=a.observeResult}else t==="read"?r=Tt(this.cwd,i):t==="write"&&(r=Ct(this.cwd,i));let o;return o=new A(t,e,s,{showImages:this.showImages,imageWidthCells:this.imageWidthCells},r,this.componentTui(()=>o),this.cwd),o.setToolView(r===void 0?void 0:i,n),o.setExpanded(this.toolOutputExpanded),o}append(t){if(this.outsideTurns===0&&this.consolidatingHere()){const e=this.currentTurn();this.appendTarget.addChild(t),e.members.add(t);return}this.appendTarget.addChild(t)}chatChildCount(){return this.appendTarget.children.length}resetChat(){this.setActivity(void 0),this.settleBashComponent(),this.streamingComponent=void 0,this.streamingToolSeparator=void 0,this.streamingWasGroupMember=!1,this.foldedToolSeparatorContents.clear(),this.groups.length=0,this.openGroup=void 0,this.turns.length=0,this.openReplyTurn=void 0,this.carriedTurn={key:M,prose:[]},this.turnHidden=new Set,this.groupSummaries.clear(),this.nodeRoster.clear(),this.pendingTools.clear(),this.lastAssistantText=void 0,this.copyBlockRecords.length=0,this.copyIndex.clear(),this.transcriptFilePaths.length=0,this.transcriptDocRefs.length=0,this.pageBlocks.clear(),this.inboxAnswers.clear(),this.historyContainer.clear()}maybeAppendPageBlock(t){if(!this.inlinePageBlocks||this.appendTarget!==this.historyContainer)return;const e=tt(t);if(e===void 0||this.pageBlocks.has(e.pageId))return;const s=new et(e.pageId,this.pageOpenHint);this.pageBlocks.set(e.pageId,s),this.outsideTurns++;try{this.append(s)}finally{this.outsideTurns--}this.loadPageBlock(s)}async loadPageBlock(t){await t.refresh()&&(this.historyContainer.setHidden(t,this.hidesInboxExchange()&&t.settled),this.historyContainer.markDirty(t),this.tui.requestRender())}refreshInFlightTools(){if(this.pendingTools.size!==0){for(const t of this.pendingTools.values())this.historyContainer.markDirty(t);this.tui.requestRender()}}async refreshPageBlocks(){const t=[...this.pageBlocks.values()];(await Promise.all(t.map(async s=>{const i=await s.refresh();return i&&(this.historyContainer.setHidden(s,this.hidesInboxExchange()&&s.settled),this.historyContainer.markDirty(s)),i}))).some(Boolean)&&this.tui.requestRender()}makeLoader(t){let e;const s={requestRender:l(i=>{e&&!this.onActivityChange&&this.statusContainer.markDirty(e),this.tui.requestRender(i)},"requestRender"),terminal:this.tui.terminal};return e=new V(s,this.spinnerStyle,this.dimStyle,t),e}componentTui(t){return{requestRender:l(e=>{this.historyContainer.markDirty(t()),this.tui.requestRender(e)},"requestRender"),terminal:this.tui.terminal}}settleBashComponent(t){if(!this.bashComponent)return;const e=this.bashComponent;e.setComplete(t?.exitCode,t?.cancelled??!0,t?.truncated?{truncated:!0}:void 0,t?.fullOutputPath),this.historyContainer.markDirty(e),this.bashComponent=void 0}setActivity(t){if(this.activityLoader?.stop(),this.activityLoader=t,this.onActivityChange){this.onActivityChange(t);return}this.statusContainer.clear(),t&&this.statusContainer.addChild(t)}showStatus(t){this.closeOpenGroup(),this.append(new u(1)),this.append(new w(this.dimStyle(t),1,0))}showError(t){if(!this.deferIfBuilding(()=>this.showError(t))){this.closeOpenGroup(),this.outsideTurns++;try{this.append(new u(1)),this.append(new w(this.errorStyle(t),1,0))}finally{this.outsideTurns--}this.tui.requestRender()}}crtrOutputTheme(){return{fg:l((t,e)=>t==="error"?this.errorStyle(e):t==="warning"||t==="accent"||t==="toolTitle"||t==="success"?this.spinnerStyle(e):t==="muted"?this.dimStyle(e):e,"fg"),bg:l((t,e)=>e,"bg"),bold:l(t=>this.labelStyle(t),"bold")}}assistantToolCalls(t){const e=t.content;return Array.isArray(e)?e.filter(s=>typeof s=="object"&&s!==null&&s.type==="toolCall"):[]}getTranscriptFilePathsNewestFirst(){return[...this.transcriptFilePaths]}getTranscriptDocRefsNewestFirst(){return[...this.transcriptDocRefs]}refreshMarkdown(){this.historyContainer.invalidate(),this.tui.requestRender()}getLastAssistantText(){const t=this.lastAssistantText?.trim();return t||void 0}blockText(t){const e=this.copyIndex.get(t);return e===void 0?void 0:this.copyBlockRecords[e]?.text}copyBlocks(){return this.copyBlockRecords}getLastToolOutputText(){for(let t=this.copyBlockRecords.length-1;t>=0;t--){const e=this.copyBlockRecords[t];if(e.kind==="tool")return e.text.trim()===""?void 0:e.text}}getTranscriptPlainText(){return this.copyBlockRecords.map(t=>t.text).filter(t=>t.trim()!=="").join(`
2
2
 
3
3
  `)}registerCopyBlock(t,e,s){const i=this.condensedCopyBlocks.get(this.appendTarget);if(i){const r=i.findIndex(o=>o.block===t);r===-1?i.push({block:t,kind:e,text:s}):i[r].text=s;return}const n=this.copyIndex.get(t);n===void 0?(this.copyIndex.set(t,this.copyBlockRecords.length),this.copyBlockRecords.push({block:t,kind:e,text:s})):this.copyBlockRecords[n].text=s}unregisterCopyBlock(t){const e=this.condensedCopyBlocks.get(this.appendTarget);if(e){const i=e.findIndex(n=>n.block===t);i!==-1&&e.splice(i,1);return}const s=this.copyIndex.get(t);if(s!==void 0){this.copyBlockRecords.splice(s,1),this.copyIndex.delete(t);for(let i=s;i<this.copyBlockRecords.length;i++)this.copyIndex.set(this.copyBlockRecords[i].block,i)}}appendCondensedBlock({component:t,copyBlocks:e}){this.outsideTurns++;try{this.append(t)}finally{this.outsideTurns--}this.registerCopyBlock(t,"condensed",e.map(s=>s.text).filter(s=>s.trim()!=="").join(`
4
4
 
@@ -1 +1 @@
1
- var x=Object.defineProperty;var n=(t,e)=>x(t,"name",{value:e,configurable:!0});import{execFile as T}from"node:child_process";import{existsSync as f,mkdtempSync as $,readFileSync as g,rmSync as I}from"node:fs";import{homedir as O,tmpdir as P}from"node:os";import{join as d}from"node:path";import{promisify as L}from"node:util";import{defineBranch as M,defineLeaf as U}from"../../core/command.js";import{CrtrError as j,general as p,usage as C}from"../../core/errors.js";import{atomicWriteJson as E}from"../../core/fs-utils.js";import{northlightUnreachable as w}from"../../core/directory/reach.js";import{notSignedIn as y,readPersonCredential as k,personAccount as F}from"../../core/directory/person-credential.js";import{readMachineConfig as R}from"../../daemon/machine-link/config.js";import{ExitCode as B}from"../../types.js";const D=L(T),S="https://downloads.northlight.ai/desktop/mac/Northlight-Desktop.dmg",_="BRL7SHLNLU",N="Northlight Desktop.app",J="/Applications",m=d(O(),"Library","Application Support","Northlight Desktop"),A=3e4,K=12e4,W=/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,b=n(t=>new Promise(e=>setTimeout(e,t)),"sleep"),h=n(()=>d(J,N),"appPath"),l=n(async(t,e)=>(await D(t,e,{maxBuffer:16*1024*1024})).stdout,"sh");async function q(){try{return await l("pgrep",["-f",`${h()}/Contents/MacOS/`]),!0}catch{return!1}}n(q,"desktopRunning");function v(t){try{return JSON.parse(g(d(m,"machine.json"),"utf8"))?.owner===t}catch{return!1}}n(v,"desktopPaired");async function G(t){const e=d(t,"Northlight-Desktop.dmg");try{await l("curl",["-fsSL","--max-time","600","-o",e,S])}catch(o){const a=String(o.stderr??"");throw/Could not resolve|Failed to connect|timed out|SSL|Connection|resolve host/i.test(a)?w(o,S):p(`the Desktop download failed: ${a.trim()||o.message}`)}return e}n(G,"download");async function H(t,e){await l("hdiutil",["attach","-nobrowse","-readonly","-mountpoint",e,t]);try{const o=d(e,N);try{await l("codesign",["--verify","--deep","--strict",o])}catch(i){throw p(`the downloaded Desktop failed its signature check, so it was not installed: ${String(i.stderr??i.message).trim()}`)}const a=(await D("codesign",["-dv","--verbose=4",o])).stderr,s=/^TeamIdentifier=(\S+)$/m.exec(a)?.[1];if(s!==_)throw p(`the downloaded Desktop is signed by team ${s??"none"}, not Northlight's ${_}, so it was not installed`);await l("ditto",[o,h()])}finally{await l("hdiutil",["detach",e,"-quiet"]).catch(()=>{})}}n(H,"installFrom");async function z(){const t=d(m,"install.json");for(const e=Date.now()+A;Date.now()<e;await b(500))try{const o=JSON.parse(g(t,"utf8")).install_id;if(typeof o=="string"&&W.test(o))return o}catch{}throw p(`Desktop opened but did not write ${t} within ${A/1e3} seconds`,{next:"Open Northlight Desktop yourself, then run `crtr sys desktop install` again."})}n(z,"waitForInstallId");async function V(t,e){const o=k(),a=`${t.replace(/\/+$/,"")}/v1/developer/desktop-enrollment`;let s;try{s=await fetch(a,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${o.secret}`},body:JSON.stringify({install_id:e}),signal:AbortSignal.timeout(3e4)})}catch(c){throw w(c,a)}const i=await s.text();let r={};try{r=i===""?{}:JSON.parse(i)}catch{}if(s.status===401){const c=typeof r.error=="object"?r.error?.code:r.error;if(c==="credential_revoked"||c==="credential_expired"||c==="credential_unknown")throw y(`the sign-in on this computer is no longer valid (${String(c)})`)}if(!s.ok||typeof r.device_code!="string"||typeof r.expires_in!="number"){const c=typeof r.error=="object"&&typeof r.error?.message=="string"?r.error.message:`HTTP ${s.status}`;throw p(`Northlight refused to pair Desktop: ${c}`)}return{device_code:r.device_code,expires_in:r.expires_in}}n(V,"mintPairing");const u=n(()=>l("open",["-a",h()]),"openApp"),Q=U({name:"install",description:"download, install, open and pair Northlight Desktop to this account",whenToUse:"a `device` command answered that no Northlight app is paired to this account, and the person said yes to installing it",help:{name:"sys desktop install",summary:"install Northlight Desktop in Applications and pair it to the account crtr is signed in to, with no code and no Allow click",guide:"macOS only, and only on a computer signed in with `crtr sys signin` and moved to Northlight. Downloads Desktop from downloads.northlight.ai, checks it is signed by Northlight, installs it, opens it, pairs it, then waits up to two minutes until Desktop on this Mac has registered to the account. Safe to run again: a Desktop on this Mac that is already registered to the account is only opened. macOS permission prompts (Full Disk Access, Messages) still appear when a feature needs them.",params:[],output:[{name:"installed",type:"boolean",required:!0,constraint:"True when this run installed Desktop; false when it was already installed and paired."}],outputKind:"object",effects:["Downloads and mounts the Desktop image, then copies the app to `/Applications/Northlight Desktop.app` (a copy that is running is left as it is and only paired). Opens Desktop. Calls `POST /v1/developer/desktop-enrollment` with the stored sign-in and writes `~/Library/Application Support/Northlight Desktop/pairing.json` (mode 0600), which Desktop deletes when it reads it."]},run:n(async()=>{if(process.platform!=="darwin")throw C("Northlight Desktop is a Mac app: this command only runs on macOS");const t=k();if(t===void 0||R()===void 0)throw y("this computer is not signed in to Northlight");const{id:e}=await F(t.directoryUrl);if(f(h())&&v(e))return await u(),{installed:!1};if(!(f(h())&&await q())){const i=$(d(P(),"crtr-desktop-"));try{await H(await G(i),d(i,"mnt"))}finally{I(i,{recursive:!0,force:!0})}}await u();const a=await z(),s=await V(t.directoryUrl,a);E(d(m,"pairing.json"),{issuer:t.directoryUrl.replace(/\/+$/,""),device_code:s.device_code},{mode:384}),await u();for(const i=Date.now()+K;Date.now()<i;await b(3e3))if(v(e))return{installed:!0};throw new j("desktop_not_paired","Desktop opened but did not pair.",B.GENERAL,{next:"If Desktop is signed in to another account, sign out there and run `crtr sys desktop install` again."})},"run"),render:n(t=>`<desktop installed="${t.installed}"/>`,"render")}),lt=M({name:"desktop",description:"Northlight Desktop on this Mac",whenToUse:"Northlight Desktop must be installed and paired to this account",help:{name:"sys desktop",summary:"install and pair Northlight Desktop"},children:[Q]});export{S as DESKTOP_DMG_URL,_ as DESKTOP_TEAM_ID,lt as sysDesktopBranch,Q as sysDesktopInstallLeaf};
1
+ var I=Object.defineProperty;var i=(t,e)=>I(t,"name",{value:e,configurable:!0});import{execFile as x}from"node:child_process";import{existsSync as f,mkdtempSync as O,readFileSync as w,rmSync as $}from"node:fs";import{homedir as M,tmpdir as P}from"node:os";import{join as a}from"node:path";import{promisify as L}from"node:util";import{defineBranch as U,defineLeaf as C}from"../../core/command.js";import{CrtrError as g,general as h,usage as E}from"../../core/errors.js";import{atomicWriteJson as j}from"../../core/fs-utils.js";import{northlightUnreachable as y}from"../../core/directory/reach.js";import{readPersonCredential as k,personAccount as F}from"../../core/directory/person-credential.js";import{readMachineConfig as R}from"../../daemon/machine-link/config.js";import{ExitCode as B}from"../../types.js";const D=L(x),_="https://downloads.northlight.ai/desktop/mac/Northlight-Desktop.dmg",S="BRL7SHLNLU",N="Northlight Desktop.app",J="/Applications",m=a(M(),"Library","Application Support","Northlight Desktop"),A=3e4,q=12e4,K=/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,b=i(t=>new Promise(e=>setTimeout(e,t)),"sleep"),p=i(()=>a(J,N),"appPath"),l=i(async(t,e)=>(await D(t,e,{maxBuffer:16*1024*1024})).stdout,"sh");async function W(){try{return await l("pgrep",["-f",`${p()}/Contents/MacOS/`]),!0}catch{return!1}}i(W,"desktopRunning");function T(t){try{return JSON.parse(w(a(m,"machine.json"),"utf8"))?.owner===t}catch{return!1}}i(T,"desktopPaired");async function G(t){const e=a(t,"Northlight-Desktop.dmg");try{await l("curl",["-fsSL","--max-time","600","-o",e,_])}catch(o){const r=String(o.stderr??"");throw/Could not resolve|Failed to connect|timed out|SSL|Connection|resolve host/i.test(r)?y(o,_):h(`the Desktop download failed: ${r.trim()||o.message}`)}return e}i(G,"download");async function H(t,e){await l("hdiutil",["attach","-nobrowse","-readonly","-mountpoint",e,t]);try{const o=a(e,N);try{await l("codesign",["--verify","--deep","--strict",o])}catch(d){throw h(`the downloaded Desktop failed its signature check, so it was not installed: ${String(d.stderr??d.message).trim()}`)}const r=(await D("codesign",["-dv","--verbose=4",o])).stderr,s=/^TeamIdentifier=(\S+)$/m.exec(r)?.[1];if(s!==S)throw h(`the downloaded Desktop is signed by team ${s??"none"}, not Northlight's ${S}, so it was not installed`);await l("ditto",[o,p()])}finally{await l("hdiutil",["detach",e,"-quiet"]).catch(()=>{})}}i(H,"installFrom");async function z(){const t=a(m,"install.json");for(const e=Date.now()+A;Date.now()<e;await b(500))try{const o=JSON.parse(w(t,"utf8")).install_id;if(typeof o=="string"&&K.test(o))return o}catch{}throw h(`Desktop opened but did not write ${t} within ${A/1e3} seconds`,{next:"Open Northlight Desktop yourself, then run `crtr sys desktop install` again."})}i(z,"waitForInstallId");async function V(t,e){const o=k(),r=`${t.replace(/\/+$/,"")}/v1/developer/desktop-enrollment`;let s;try{s=await fetch(r,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${o.secret}`},body:JSON.stringify({install_id:e}),signal:AbortSignal.timeout(3e4)})}catch(c){throw y(c,r)}const d=await s.text();let n={};try{n=d===""?{}:JSON.parse(d)}catch{}if(s.status===401){const c=typeof n.error=="object"?n.error?.code:n.error;if(c==="credential_revoked"||c==="credential_expired"||c==="credential_unknown")return}if(!s.ok||typeof n.device_code!="string"||typeof n.expires_in!="number"){const c=typeof n.error=="object"&&typeof n.error?.message=="string"?n.error.message:`HTTP ${s.status}`;throw h(`Northlight refused to pair Desktop: ${c}`)}return{device_code:n.device_code,expires_in:n.expires_in}}i(V,"mintPairing");const u=i(()=>l("open",["-a",p()]),"openApp"),v='Northlight Desktop is open. Tell the person to sign in inside it with their Northlight account: its window shows a code and one Allow click, or use "Connect this Mac\u2026" in its menu-bar icon. Then retry the command that needed it.';async function Q(){const t=k();if(!(t===void 0||R()===void 0))try{return{owner:(await F(t.directoryUrl)).id,directoryUrl:t.directoryUrl}}catch(e){if(e instanceof g&&e.code==="not_signed_in")return;throw e}}i(Q,"pairingAccount");const X=C({name:"install",description:"download, install and open Northlight Desktop on this Mac",whenToUse:"a `device` command answered that no Northlight app is paired to this account, you are on the person's Mac, and the person said yes to installing it",help:{name:"sys desktop install",summary:"install Northlight Desktop in Applications and open it; the person then signs in inside Desktop",guide:'macOS only; needs no crtr sign-in. Downloads Desktop from downloads.northlight.ai, checks it is signed by Northlight, installs it and opens it. A Desktop already in Applications is opened, not replaced. When crtr on this Mac is also signed in and moved to Northlight, Desktop is paired to that account with no code and no Allow click, and the command waits up to two minutes for it to register. Otherwise `follow_up` says the person signs in inside Desktop with their Northlight account (its window, or "Connect this Mac\u2026" in the menu-bar icon). Safe to run again. macOS permission prompts (Full Disk Access, Messages) still appear when a feature needs them.',params:[],output:[{name:"installed",type:"boolean",required:!0,constraint:"True when this run installed Desktop; false when it was already installed."},{name:"paired",type:"boolean",required:!0,constraint:"True when Desktop on this Mac is registered to the account crtr is signed in to; false when the person still has to sign in inside Desktop."},{name:"follow_up",type:"string",required:!1,constraint:"Present when `paired` is false: what to tell the person."}],outputKind:"object",effects:["Downloads and mounts the Desktop image, then copies the app to `/Applications/Northlight Desktop.app` (a copy that is running is left as it is and only paired). Opens Desktop. Only when crtr on this Mac is signed in and moved: calls `POST /v1/developer/desktop-enrollment` with the stored sign-in and writes `~/Library/Application Support/Northlight Desktop/pairing.json` (mode 0600), which Desktop deletes when it reads it."]},run:i(async()=>{if(process.platform!=="darwin")throw E("Northlight Desktop is a Mac app: this command only runs on macOS");const t=await Q();if(t!==void 0&&f(p())&&T(t.owner))return await u(),{installed:!1,paired:!0};const e=t===void 0?!f(p()):!(f(p())&&await W());if(e){const n=O(a(P(),"crtr-desktop-"));try{await H(await G(n),a(n,"mnt"))}finally{$(n,{recursive:!0,force:!0})}}if(t===void 0)return await u(),{installed:e,paired:!1,follow_up:v};const{owner:o,directoryUrl:r}=t;await u();const s=await z(),d=await V(r,s);if(d===void 0)return{installed:e,paired:!1,follow_up:v};j(a(m,"pairing.json"),{issuer:r.replace(/\/+$/,""),device_code:d.device_code},{mode:384}),await u();for(const n=Date.now()+q;Date.now()<n;await b(3e3))if(T(o))return{installed:e,paired:!0};throw new g("desktop_not_paired","Desktop opened but did not pair.",B.GENERAL,{next:"If Desktop is signed in to another account, sign out there and run `crtr sys desktop install` again."})},"run"),render:i(t=>`<desktop installed="${t.installed}" paired="${t.paired}">${t.follow_up??""}</desktop>`,"render")}),pt=U({name:"desktop",description:"Northlight Desktop on this Mac",whenToUse:"Northlight Desktop must be installed and paired to this account",help:{name:"sys desktop",summary:"install and pair Northlight Desktop"},children:[X]});export{_ as DESKTOP_DMG_URL,S as DESKTOP_TEAM_ID,pt as sysDesktopBranch,X as sysDesktopInstallLeaf};
@@ -1,3 +1,3 @@
1
- var V=Object.defineProperty;var d=(i,a)=>V(i,"name",{value:a,configurable:!0});import{DatabaseSync as W}from"node:sqlite";import{existsSync as Y,mkdirSync as D,mkdtempSync as J,renameSync as X,rmSync as P}from"node:fs";import{tmpdir as Q}from"node:os";import{join as A}from"node:path";import j from"pg";import{migrate as z,MIGRATIONS as I}from"./migrations.js";import{canvasDbPath as Z}from"./paths.js";import{canvasSearchPathOptions as ee}from"./schema-version.js";import{EXPORT_MARK_KEY as F,IMPORT_MARK_KEY as te,OBJECT_COLUMNS as ne,pgColumns as se,quote as p,TABLE_ORDER as C}from"./import-sqlite.js";const oe=5e3,re=new Set(["environments","relations","schemas","charges","charge_totals","limits","viewer_attachments","reconciler_leases","environment_repos","conversation_entries","conversation_parts","charge_chunks","profiles","profile_projects","profile_defaults","scope_settings","scope_bookkeeping","scope_marketplaces","scope_plugins","person_settings","model_accounts","model_account_cooldowns","model_api_keys","model_settings","model_sign_in_flows","env_vars","import_runs","import_key_map","import_manifest","import_id_map","machine_links","machine_calls","machine_credentials","import_node_files","bodies","usage","grantee_usage","provider_receipts"]),ae={exposures:"node, object, channel",rules:"object, position",edges:"type, src, dst",unresolved_links:"src, name",revision_parents:"child, parent",canvas_meta:"key",runtime_state:"key",message_causes:"message_id",peer_ingested:"key",peer_ingested_destinations:"key",message_sequence:"id",messages:"seq",review_comment_events:"seq",audit_rows:"id",peer_outbox:"id",app_users:"uid"},ie={objects:"id",revisions:"rev_id",watches:"watch_id"},H=new Set(["daemon_owner","schema_version","schema_min_reader","ledger_migrated",te,F]);function ce(i){const a=J(A(Q(),"crtr-export-")),s=process.env.CRTR_HOME;process.env.CRTR_HOME=a,D(A(a,"runtime","nodes"),{recursive:!0});try{const o=new W(i);return o.exec("PRAGMA journal_mode = DELETE;"),o.exec("PRAGMA foreign_keys = ON;"),z(o),o.exec("DELETE FROM canvas_meta; DELETE FROM message_sequence;"),o}finally{s===void 0?delete process.env.CRTR_HOME:process.env.CRTR_HOME=s,P(a,{recursive:!0,force:!0})}}d(ce,"createEmptyDatabase");async function Re(i,a=()=>{}){const s=Z();if(Y(s))throw new Error(`${s} already exists; refusing to overwrite it`);D(A(s,".."),{recursive:!0,mode:448});const o=`${s}.exporting-${process.pid}`,u=Date.now(),t=new j.Client({connectionString:i,application_name:`crtr-export-${process.pid}`,options:ee(),types:{getTypeParser:d(((f,r)=>f===20?w=>Number(w):j.types.getTypeParser(f,r)),"getTypeParser")}});await t.connect();let _;try{if((await t.query("SELECT value FROM canvas_meta WHERE key = 'schema_version'").catch(()=>({rows:[]}))).rows.length===0)throw new Error("the Postgres database has no canvas schema; nothing to export");_=ce(o);const r=_;r.exec("PRAGMA foreign_keys = OFF;");const w=await se(t),L=[...w.keys()].filter(e=>!re.has(e)&&!C.includes(e));if(L.length>0)throw new Error(`export: Postgres tables with no export rule: ${L.join(", ")}`);await t.query("BEGIN READ ONLY ISOLATION LEVEL REPEATABLE READ");const E={};for(const[e,S]of Object.entries(ie)){const h=new Map;for(const l of(await t.query(`SELECT ${S} AS id, seq FROM ${e}`)).rows)h.set(l.id,Number(l.seq));E[e]=h}const B=E.objects;let M=0;r.exec("BEGIN");for(const e of C){const S=Date.now(),h=new Set(r.prepare(`PRAGMA table_info(${p(e)})`).all().map(n=>n.name)),l=w.get(e);if(l===void 0)throw new Error(`export: Postgres has no table ${e}`);const G=l.some(n=>n.name==="seq"),O=l.filter(n=>h.has(n.name)),K=new Set(ne[e]??[]),g=e==="objects"||e==="revisions"||e==="watches"||e==="watch_subscriptions",$=O.map(n=>n.type==="jsonb"?`${p(n.name)}::text AS ${p(n.name)}`:p(n.name));e==="watches"&&$.push("watch_id AS _watch_id"),g&&$.push("seq AS _seq");const R=O.map(n=>p(n.name)),U=r.prepare(`INSERT INTO ${p(e)} (${g?["rowid",...R]:R}) VALUES (${(g?["?",...R]:R).map(()=>"?").join(", ")})`),k=G?"seq":ae[e],b=`export_${e}`;await t.query(`DECLARE ${b} NO SCROLL CURSOR FOR SELECT ${$.join(", ")} FROM ${p(e)}${e==="canvas_meta"?` WHERE key NOT IN (${[...H].map(n=>`'${n}'`).join(", ")})`:""}${k===void 0?"":` ORDER BY ${k}`}`);let T=0;for(;;){const n=await t.query(`FETCH ${oe} FROM ${b}`);if(n.rows.length===0)break;for(const q of n.rows){const N=g?[Number(q._seq)]:[];for(const c of O){let m=q[c.name];e==="revision_parents"&&(c.name==="child"||c.name==="parent")?m=x(E.revisions,"revision",e,c.name,m):e==="watch_subscriptions"&&c.name==="watch"?m=x(E.watches,"watch",e,c.name,m):K.has(c.name)?m=x(B,"object",e,c.name,m):e==="jobs"&&(c.name==="job_dir"||c.name==="log_path")&&(m=m??""),N.push(m??null)}U.run(...N)}T+=n.rows.length}await t.query(`CLOSE ${b}`),M+=T,a(`export ${e}: ${T} rows in ${Date.now()-S} ms`)}r.exec("COMMIT"),await t.query("COMMIT");const y=r.prepare("PRAGMA foreign_key_check").all();if(y.length>0)throw new Error(`export: ${y.length} foreign key violation(s) in the exported canvas.db, first: ${JSON.stringify(y[0])}`);await me(t,r);const v=r.prepare("PRAGMA user_version").get().user_version;if(v!==I.length)throw new Error(`export: exported canvas.db is at user_version ${v}, expected ${I.length}`);return r.close(),_=void 0,await t.query("INSERT INTO canvas_meta (key, value) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value",[F,JSON.stringify({at:new Date().toISOString(),path:s})]),X(o,s),{path:s,rows:M,ms:Date.now()-u,userVersion:v}}catch(f){await t.query("ROLLBACK").catch(()=>{});try{_?.close()}catch{}for(const r of["","-wal","-shm","-journal"])P(`${o}${r}`,{force:!0});throw f}finally{await t.end().catch(()=>{})}}d(Re,"exportPostgresToSqlite");function x(i,a,s,o,u){if(u==null)return null;const t=i.get(u);if(t===void 0)throw new Error(`export: ${s}.${o} references ${a} ${String(u)}, which was not exported (`);return t}d(x,"mapId");async function me(i,a){const s=[];for(const o of C){const u=Number((await i.query(`SELECT count(*) AS n FROM ${p(o)}`)).rows[0].n);let t=Number(a.prepare(`SELECT count(*) AS n FROM ${p(o)}`).get().n),_=u;o==="canvas_meta"&&(_=Number((await i.query("SELECT count(*) AS n FROM canvas_meta WHERE key <> ALL($1)",[[...H]])).rows[0].n),t=Number(a.prepare("SELECT count(*) AS n FROM canvas_meta").get().n)),_!==t&&s.push(`${o}: Postgres ${_} rows, SQLite ${t}`)}if(s.length>0)throw new Error(`Postgres \u2192 SQLite export verification failed:
1
+ var V=Object.defineProperty;var d=(i,a)=>V(i,"name",{value:a,configurable:!0});import{DatabaseSync as W}from"node:sqlite";import{existsSync as Y,mkdirSync as D,mkdtempSync as J,renameSync as X,rmSync as P}from"node:fs";import{tmpdir as Q}from"node:os";import{join as A}from"node:path";import j from"pg";import{migrate as z,MIGRATIONS as I}from"./migrations.js";import{canvasDbPath as Z}from"./paths.js";import{canvasSearchPathOptions as ee}from"./schema-version.js";import{EXPORT_MARK_KEY as F,IMPORT_MARK_KEY as te,OBJECT_COLUMNS as ne,pgColumns as se,quote as m,TABLE_ORDER as C}from"./import-sqlite.js";const oe=5e3,re=new Set(["environments","relations","schemas","charges","charge_totals","limits","viewer_attachments","reconciler_leases","environment_repos","conversation_entries","conversation_parts","charge_chunks","profiles","profile_projects","profile_defaults","scope_settings","scope_bookkeeping","scope_marketplaces","scope_plugins","person_settings","model_accounts","model_account_cooldowns","model_api_keys","model_settings","model_sign_in_flows","env_vars","import_runs","import_key_map","import_manifest","import_id_map","machine_links","machine_calls","machine_credentials","import_node_files","bodies","package_publications","usage","grantee_usage","provider_receipts"]),ae={exposures:"node, object, channel",rules:"object, position",edges:"type, src, dst",unresolved_links:"src, name",revision_parents:"child, parent",canvas_meta:"key",runtime_state:"key",message_causes:"message_id",peer_ingested:"key",peer_ingested_destinations:"key",message_sequence:"id",messages:"seq",review_comment_events:"seq",audit_rows:"id",peer_outbox:"id",app_users:"uid"},ie={objects:"id",revisions:"rev_id",watches:"watch_id"},H=new Set(["daemon_owner","schema_version","schema_min_reader","ledger_migrated",te,F]);function ce(i){const a=J(A(Q(),"crtr-export-")),s=process.env.CRTR_HOME;process.env.CRTR_HOME=a,D(A(a,"runtime","nodes"),{recursive:!0});try{const o=new W(i);return o.exec("PRAGMA journal_mode = DELETE;"),o.exec("PRAGMA foreign_keys = ON;"),z(o),o.exec("DELETE FROM canvas_meta; DELETE FROM message_sequence;"),o}finally{s===void 0?delete process.env.CRTR_HOME:process.env.CRTR_HOME=s,P(a,{recursive:!0,force:!0})}}d(ce,"createEmptyDatabase");async function Re(i,a=()=>{}){const s=Z();if(Y(s))throw new Error(`${s} already exists; refusing to overwrite it`);D(A(s,".."),{recursive:!0,mode:448});const o=`${s}.exporting-${process.pid}`,u=Date.now(),t=new j.Client({connectionString:i,application_name:`crtr-export-${process.pid}`,options:ee(),types:{getTypeParser:d(((f,r)=>f===20?w=>Number(w):j.types.getTypeParser(f,r)),"getTypeParser")}});await t.connect();let _;try{if((await t.query("SELECT value FROM canvas_meta WHERE key = 'schema_version'").catch(()=>({rows:[]}))).rows.length===0)throw new Error("the Postgres database has no canvas schema; nothing to export");_=ce(o);const r=_;r.exec("PRAGMA foreign_keys = OFF;");const w=await se(t),L=[...w.keys()].filter(e=>!re.has(e)&&!C.includes(e));if(L.length>0)throw new Error(`export: Postgres tables with no export rule: ${L.join(", ")}`);await t.query("BEGIN READ ONLY ISOLATION LEVEL REPEATABLE READ");const E={};for(const[e,S]of Object.entries(ie)){const h=new Map;for(const l of(await t.query(`SELECT ${S} AS id, seq FROM ${e}`)).rows)h.set(l.id,Number(l.seq));E[e]=h}const B=E.objects;let M=0;r.exec("BEGIN");for(const e of C){const S=Date.now(),h=new Set(r.prepare(`PRAGMA table_info(${m(e)})`).all().map(n=>n.name)),l=w.get(e);if(l===void 0)throw new Error(`export: Postgres has no table ${e}`);const G=l.some(n=>n.name==="seq"),O=l.filter(n=>h.has(n.name)),K=new Set(ne[e]??[]),g=e==="objects"||e==="revisions"||e==="watches"||e==="watch_subscriptions",$=O.map(n=>n.type==="jsonb"?`${m(n.name)}::text AS ${m(n.name)}`:m(n.name));e==="watches"&&$.push("watch_id AS _watch_id"),g&&$.push("seq AS _seq");const R=O.map(n=>m(n.name)),U=r.prepare(`INSERT INTO ${m(e)} (${g?["rowid",...R]:R}) VALUES (${(g?["?",...R]:R).map(()=>"?").join(", ")})`),k=G?"seq":ae[e],b=`export_${e}`;await t.query(`DECLARE ${b} NO SCROLL CURSOR FOR SELECT ${$.join(", ")} FROM ${m(e)}${e==="canvas_meta"?` WHERE key NOT IN (${[...H].map(n=>`'${n}'`).join(", ")})`:""}${k===void 0?"":` ORDER BY ${k}`}`);let T=0;for(;;){const n=await t.query(`FETCH ${oe} FROM ${b}`);if(n.rows.length===0)break;for(const q of n.rows){const N=g?[Number(q._seq)]:[];for(const c of O){let p=q[c.name];e==="revision_parents"&&(c.name==="child"||c.name==="parent")?p=x(E.revisions,"revision",e,c.name,p):e==="watch_subscriptions"&&c.name==="watch"?p=x(E.watches,"watch",e,c.name,p):K.has(c.name)?p=x(B,"object",e,c.name,p):e==="jobs"&&(c.name==="job_dir"||c.name==="log_path")&&(p=p??""),N.push(p??null)}U.run(...N)}T+=n.rows.length}await t.query(`CLOSE ${b}`),M+=T,a(`export ${e}: ${T} rows in ${Date.now()-S} ms`)}r.exec("COMMIT"),await t.query("COMMIT");const y=r.prepare("PRAGMA foreign_key_check").all();if(y.length>0)throw new Error(`export: ${y.length} foreign key violation(s) in the exported canvas.db, first: ${JSON.stringify(y[0])}`);await pe(t,r);const v=r.prepare("PRAGMA user_version").get().user_version;if(v!==I.length)throw new Error(`export: exported canvas.db is at user_version ${v}, expected ${I.length}`);return r.close(),_=void 0,await t.query("INSERT INTO canvas_meta (key, value) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value",[F,JSON.stringify({at:new Date().toISOString(),path:s})]),X(o,s),{path:s,rows:M,ms:Date.now()-u,userVersion:v}}catch(f){await t.query("ROLLBACK").catch(()=>{});try{_?.close()}catch{}for(const r of["","-wal","-shm","-journal"])P(`${o}${r}`,{force:!0});throw f}finally{await t.end().catch(()=>{})}}d(Re,"exportPostgresToSqlite");function x(i,a,s,o,u){if(u==null)return null;const t=i.get(u);if(t===void 0)throw new Error(`export: ${s}.${o} references ${a} ${String(u)}, which was not exported (`);return t}d(x,"mapId");async function pe(i,a){const s=[];for(const o of C){const u=Number((await i.query(`SELECT count(*) AS n FROM ${m(o)}`)).rows[0].n);let t=Number(a.prepare(`SELECT count(*) AS n FROM ${m(o)}`).get().n),_=u;o==="canvas_meta"&&(_=Number((await i.query("SELECT count(*) AS n FROM canvas_meta WHERE key <> ALL($1)",[[...H]])).rows[0].n),t=Number(a.prepare("SELECT count(*) AS n FROM canvas_meta").get().n)),_!==t&&s.push(`${o}: Postgres ${_} rows, SQLite ${t}`)}if(s.length>0)throw new Error(`Postgres \u2192 SQLite export verification failed:
2
2
  ${s.join(`
3
- `)}`)}d(me,"verify");export{Re as exportPostgresToSqlite};
3
+ `)}`)}d(pe,"verify");export{Re as exportPostgresToSqlite};
@@ -0,0 +1,16 @@
1
+ -- 010-package-publications.sql — cycle 5 package publisher (design-package-publisher "Data model"). One transaction, additions only:
2
+ -- `schema_min_reader` is not touched and stays 2. The previous build (version 9) opens the result and never names the table.
3
+ --
4
+ -- One row per published app: the lock a publisher takes (`SELECT … FOR UPDATE`), the version a rolling deploy compares against,
5
+ -- and the digest that lets an unchanged start write nothing. `origin` already allows the three kinds of package the publisher
6
+ -- will carry; this release writes only `builtin` (app `crtr`). Timestamps are text, as in 002.
7
+ CREATE TABLE package_publications (
8
+ app text PRIMARY KEY REFERENCES objects(id) ON DELETE CASCADE,
9
+ origin text NOT NULL CHECK (origin IN ('builtin', 'provider', 'plugin')),
10
+ version text NOT NULL,
11
+ fetched_at text,
12
+ digest text NOT NULL,
13
+ published_at text NOT NULL,
14
+ published_by text NOT NULL,
15
+ withdrawn_at text
16
+ );
@@ -1,7 +1,7 @@
1
1
  /** The canvas schema version this build supports: the number of the highest delta in `schema-pg/` (the baseline
2
2
  * `schema-pg.sql` is version 1). `scripts/install-runtime.mjs` reads this module from a candidate generation to refuse
3
3
  * one below the live database's `schema_min_reader`. */
4
- export declare const CANVAS_SCHEMA_VERSION = 9;
4
+ export declare const CANVAS_SCHEMA_VERSION = 10;
5
5
  /** The Postgres schema holding the canvas tables: `CRTR_DATABASE_SCHEMA` (`crtr` in the cloud deploy). Unset or empty
6
6
  * means the role's own search_path (`public` on a Mac and on a VM). A name that is not a plain identifier is an error:
7
7
  * it is put in a `search_path` connection option. */
@@ -1 +1 @@
1
- var m=Object.defineProperty;var r=(n,e)=>m(n,"name",{value:e,configurable:!0});const A=9;function _(n=process.env){const e=n.CRTR_DATABASE_SCHEMA?.trim();if(e){if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(e))throw new Error(`CRTR_DATABASE_SCHEMA must be a plain identifier (letters, digits, underscore); got ${JSON.stringify(e)}`);return e}}r(_,"canvasDatabaseSchema");function h(n=process.env){return n.CRTR_SCHEMA_DATABASE_URL?.trim()||void 0}r(h,"canvasSchemaDatabaseUrl");function E(n=process.env){const e=_(n);return e===void 0?void 0:`-c search_path=${e}`}r(E,"canvasSearchPathOptions");async function v(n,e){const t=e===void 0?"canvas_meta":`"${e.replace(/"/g,'""')}".canvas_meta`;if((await n.query(`SELECT to_regclass('${t.replace(/'/g,"''")}') IS NOT NULL AS found`)).rows[0].found!==!0)return;const u=(await n.query(`SELECT key, value FROM ${t} WHERE key IN ('schema_version', 'schema_min_reader')`)).rows,o=r(c=>{const i=u.find(f=>f.key===c);if(i===void 0)return;const d=Number(i.value);if(!Number.isSafeInteger(d))throw new Error(`canvas_meta.${c} is not an integer: ${String(i.value)}`);return d},"value"),s=o("schema_version");return s===void 0?void 0:{version:s,minReader:o("schema_min_reader")??s}}r(v,"readStoredVersions");async function l(n,e,t){const a=await v(n,t);if(a!==void 0&&a.minReader>e)throw new Error(`refusing to select runtime with canvas schema ${e}: the live database requires readers at version ${a.minReader} or above (it is at ${a.version}); install a descendant build instead`)}r(l,"assertCandidateMayOpen");export{A as CANVAS_SCHEMA_VERSION,l as assertCandidateMayOpen,_ as canvasDatabaseSchema,h as canvasSchemaDatabaseUrl,E as canvasSearchPathOptions,v as readStoredVersions};
1
+ var m=Object.defineProperty;var r=(n,e)=>m(n,"name",{value:e,configurable:!0});const A=10;function _(n=process.env){const e=n.CRTR_DATABASE_SCHEMA?.trim();if(e){if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(e))throw new Error(`CRTR_DATABASE_SCHEMA must be a plain identifier (letters, digits, underscore); got ${JSON.stringify(e)}`);return e}}r(_,"canvasDatabaseSchema");function h(n=process.env){return n.CRTR_SCHEMA_DATABASE_URL?.trim()||void 0}r(h,"canvasSchemaDatabaseUrl");function E(n=process.env){const e=_(n);return e===void 0?void 0:`-c search_path=${e}`}r(E,"canvasSearchPathOptions");async function v(n,e){const t=e===void 0?"canvas_meta":`"${e.replace(/"/g,'""')}".canvas_meta`;if((await n.query(`SELECT to_regclass('${t.replace(/'/g,"''")}') IS NOT NULL AS found`)).rows[0].found!==!0)return;const u=(await n.query(`SELECT key, value FROM ${t} WHERE key IN ('schema_version', 'schema_min_reader')`)).rows,o=r(c=>{const i=u.find(f=>f.key===c);if(i===void 0)return;const d=Number(i.value);if(!Number.isSafeInteger(d))throw new Error(`canvas_meta.${c} is not an integer: ${String(i.value)}`);return d},"value"),s=o("schema_version");return s===void 0?void 0:{version:s,minReader:o("schema_min_reader")??s}}r(v,"readStoredVersions");async function l(n,e,t){const a=await v(n,t);if(a!==void 0&&a.minReader>e)throw new Error(`refusing to select runtime with canvas schema ${e}: the live database requires readers at version ${a.minReader} or above (it is at ${a.version}); install a descendant build instead`)}r(l,"assertCandidateMayOpen");export{A as CANVAS_SCHEMA_VERSION,l as assertCandidateMayOpen,_ as canvasDatabaseSchema,h as canvasSchemaDatabaseUrl,E as canvasSearchPathOptions,v as readStoredVersions};
@@ -1,4 +1,7 @@
1
1
  import { type QuarantinedPlugin } from '../plugin-quarantine.js';
2
+ import { type PackageFile } from './package-files.js';
3
+ import type { ReposFn } from './reader.js';
4
+ import type { ObjectRow } from './types.js';
2
5
  import type { CanvasDb } from '../canvas/db.js';
3
6
  export interface PackageSource {
4
7
  plugin: string;
@@ -28,3 +31,11 @@ export interface PackageReconcileReport {
28
31
  * document's single revision and publishes `edited`; a vanished file (or plugin) marks its
29
32
  * document deleted and publishes `deleted`. */
30
33
  export declare function reconcilePackageDocuments(db: CanvasDb, explicitSources?: readonly PackageSource[]): Promise<PackageReconcileReport>;
34
+ /** Record a package document's `links-to` edges the way a runtime document gets them, so delivery
35
+ * attaches the link previews. Each `[[name]]` resolves in its own app's view and links only when it
36
+ * names another shipped document (builtin or plugin): a package never links to a user's or a
37
+ * runtime document that happens to share a bare name. A name that resolves to nothing stays text.
38
+ * Edges are rewritten only when they differ, so an unchanged pass writes nothing. */
39
+ export declare function deriveLinks(db: CanvasDb, owner: ObjectRow, doc: ObjectRow, body: string): Promise<void>;
40
+ export declare function convergeFile(db: CanvasDb, owner: ObjectRow, file: PackageFile, report: PackageReconcileReport, reposFn: ReposFn): Promise<string>;
41
+ export declare function deleteMissing(db: CanvasDb, owner: ObjectRow, keep: ReadonlySet<string>, report: PackageReconcileReport, reposFn: ReposFn): Promise<void>;
@@ -1,6 +1,6 @@
1
- var S=Object.defineProperty;var u=(e,a)=>S(e,"name",{value:a,configurable:!0});import{randomUUID as _}from"node:crypto";import{CrtrError as g}from"../errors.js";import{emitEvent as O}from"../events/emit.js";import{quarantineBrokenPlugins as I}from"../plugin-quarantine.js";import{listInstalledPlugins as D}from"../resolver.js";import{builtinMemoryRoot as L,pluginMemoryDir as R}from"../scope.js";import{documentFields as T}from"../../migrations/004-canvas-documents/fields.js";import{put as A}from"./bodies.js";import{applyDocumentFields as k,deleteDocument as j,documentRecord as F,heads as x,importDocument as C,linksIn as U,moveDocument as H}from"./documents.js";import{deleteShippedReader as M,writeShippedReader as W}from"../permissions/facts.js";import{setLinks as J}from"./edges.js";import{publish as b}from"./events.js";import{resolveName as P}from"./names.js";import{packageFiles as q}from"./package-files.js";import{ensureOwner as B,getById as h,setObjectKind as K}from"./objects.js";import{checkoutsForCwd as $}from"./repo-sync/repos.js";import{rulesOf as z}from"./rules.js";function G(){return[{plugin:"crtr",dir:L()},...D("user").filter(e=>e.enabled).map(e=>({plugin:e.name,dir:R(e)}))]}u(G,"packageSources");async function Ee(e,a){const i={created:0,updated:0,deleted:0,conflicts:[],invalid:[],quarantined:[]};if(a===void 0){i.quarantined=I();for(const n of i.quarantined)O({level:"error",event:"daemon.plugins.quarantined",fields:{plugin:n.name,file:n.file,reason:n.reason,...n.movedTo!==void 0?{moved_to:n.movedTo}:{}}})}const l=a??G(),m=u(async n=>await $(e,n),"reposFn"),o=new Set,r=[],f=new Set;for(const n of l){let s;try{if(f.has(n.plugin))throw new g("conflict",`plugin "${n.plugin}" is already provided by another package`);f.add(n.plugin),s=await B(e,"app","app",`app:${n.plugin}`,n.plugin)}catch(p){if(!(p instanceof g)||p.code!=="conflict")throw p;i.conflicts.push({plugin:n.plugin,message:p.message});continue}o.add(s.id);const{files:c,invalid:w}=q(n.dir);i.invalid.push(...w);const E=new Set;for(const p of c){const d=await X(e,s,p,i,m);E.add(d),r.push({owner:s,id:d,body:p.body})}await v(e,s,E,i,m)}const t=await e.prepare(`SELECT DISTINCT o.owner AS id FROM objects o JOIN documents d ON d.object = o.id
2
- WHERE o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all();for(const{id:n}of t)o.has(n)||await v(e,await h(e,n),new Set,i,m);for(const{owner:n,id:s,body:c}of r)await Q(e,n,await h(e,s),c);return i}u(Ee,"reconcilePackageDocuments");async function Q(e,a,i,l){if(i.deleted_at!==null)return;const m={class:"app",app:a,scopes:[]},o=[];for(const t of U(l)){const n=await P(e,m,t);"object"in n&&n.object.id!==i.id&&await V(e,n.object.id)&&o.push({written_as:t,dst:n.object})}const r=u(t=>JSON.stringify(t.map(n=>[n.dst,n.written_as]).sort()),"key"),f=await e.prepare("SELECT dst, written_as FROM edges WHERE type = 'links-to' AND src = ?").all(i.id);r(f)!==r(o.map(t=>({dst:t.dst.id,written_as:t.written_as})))&&await J(e,i,o)}u(Q,"deriveLinks");async function V(e,a){return await e.prepare("SELECT 1 FROM documents WHERE object = ? AND source_path IS NOT NULL").get(a)!==void 0}u(V,"shippedDocument");async function X(e,a,i,l,m){const o=T(i.frontmatter,i.body,{missingPrivacy:"public"}),r={preview:o.preview.trim()===""?i.name:o.preview,summary:o.summary,whyItExists:o.whyItExists,unlisted:o.unlisted,rules:o.rules,lintIgnore:Array.isArray(o.lintIgnore)?o.lintIgnore.filter(d=>typeof d=="string"):null,extensions:o.extensions!==null&&typeof o.extensions=="object"&&!Array.isArray(o.extensions)?o.extensions:null},f=await A(e,i.body);let t=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
3
- WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path = ?`).get(a.id,i.path);if(t||(t=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
4
- WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND o.name = ? AND d.source_path IS NOT NULL`).get(a.id,i.name),t&&await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,t.id)),!t){const d=await C(e,{owner:a,name:i.name,kind:o.kind,privacy:"public",...r,revisions:[{body_hash:f,author:a,rationale:null,created_at:new Date().toISOString()}]});return await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(i.path,d.id),await W(e,d.id),l.created+=1,d.id}const n=t.name!==i.name||t.kind!==o.kind;t.name!==i.name&&(t=await H(e,null,t,{name:i.name})),t.kind!==o.kind&&(await K(e,t,o.kind),t=await h(e,t.id));const s=await F(e,t),[c]=await x(e,t),w=c?.body_hash!==f,E=t.preview!==r.preview||s.summary!==r.summary||s.why_it_exists!==r.whyItExists||s.unlisted!==r.unlisted||JSON.stringify(s.lint_ignore)!==JSON.stringify(r.lintIgnore)||JSON.stringify(s.extensions)!==JSON.stringify(r.extensions)||JSON.stringify((await z(e,t)).map(N))!==JSON.stringify(r.rules.map(N));if(!w&&!E&&!n)return t.id;const p=await k(e,t,r);if(w&&c){const d=_(),y=c.number+1;await e.prepare(`INSERT INTO revisions (rev_id, document, number, body_hash, author, rationale, verbatim, created_at)
5
- SELECT ?, document, ?, ?, author, rationale, verbatim, ? FROM revisions WHERE rev_id = ?`).run(d,y,f,new Date().toISOString(),c.rev_id),await e.prepare("INSERT INTO revision_parents (child, parent) SELECT ?, parent FROM revision_parents WHERE child = ?").run(d,c.rev_id),await e.prepare("DELETE FROM revisions WHERE rev_id = ?").run(c.rev_id),await e.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(d,y,p.id),await b(e,{event:"edited",object:p,actor:a,pointer:d,attrs:{revision:y,from_revision:c.number,rationale:"package file changed"}},m)}return l.updated+=1,p.id}u(X,"convergeFile");function N(e){return[e.on,e.deliver,e.match??null,e.matchFrontmatter??null,e.if??null]}u(N,"ruleKey");async function v(e,a,i,l,m){const o=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
6
- WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all(a.id);for(const r of o)i.has(r.id)||(await j(e,null,r,m),await M(e,r.id),l.deleted+=1)}u(v,"deleteMissing");export{G as packageSources,Ee as reconcilePackageDocuments};
1
+ var S=Object.defineProperty;var u=(e,a)=>S(e,"name",{value:a,configurable:!0});import{randomUUID as _}from"node:crypto";import{CrtrError as g}from"../errors.js";import{emitEvent as O}from"../events/emit.js";import{quarantineBrokenPlugins as I}from"../plugin-quarantine.js";import{listInstalledPlugins as D}from"../resolver.js";import{builtinMemoryRoot as T,pluginMemoryDir as R}from"../scope.js";import{documentFields as L}from"../../migrations/004-canvas-documents/fields.js";import{put as A}from"./bodies.js";import{applyDocumentFields as k,deleteDocument as j,documentRecord as F,heads as x,importDocument as C,linksIn as M,moveDocument as U}from"./documents.js";import{deleteShippedReader as H,writeShippedReader as W}from"../permissions/facts.js";import{setLinks as J}from"./edges.js";import{publish as b}from"./events.js";import{resolveName as P}from"./names.js";import{packageFiles as q}from"./package-files.js";import{ensureOwner as B,getById as y,setObjectKind as K}from"./objects.js";import{checkoutsForCwd as $}from"./repo-sync/repos.js";import{rulesOf as z}from"./rules.js";function G(){return[{plugin:"crtr",dir:T()},...D("user").filter(e=>e.enabled).map(e=>({plugin:e.name,dir:R(e)}))]}u(G,"packageSources");async function Ee(e,a){const n={created:0,updated:0,deleted:0,conflicts:[],invalid:[],quarantined:[]};if(a===void 0){n.quarantined=I();for(const t of n.quarantined)O({level:"error",event:"daemon.plugins.quarantined",fields:{plugin:t.name,file:t.file,reason:t.reason,...t.movedTo!==void 0?{moved_to:t.movedTo}:{}}})}const l=a??G(),m=u(async t=>await $(e,t),"reposFn"),o=new Set,r=[],f=new Set;for(const t of l){let s;try{if(f.has(t.plugin))throw new g("conflict",`plugin "${t.plugin}" is already provided by another package`);f.add(t.plugin),s=await B(e,"app","app",`app:${t.plugin}`,t.plugin)}catch(p){if(!(p instanceof g)||p.code!=="conflict")throw p;n.conflicts.push({plugin:t.plugin,message:p.message});continue}o.add(s.id);const{files:c,invalid:w}=q(t.dir);n.invalid.push(...w);const E=new Set;for(const p of c){const d=await X(e,s,p,n,m);E.add(d),r.push({owner:s,id:d,body:p.body})}await v(e,s,E,n,m)}const i=await e.prepare(`SELECT DISTINCT o.owner AS id FROM objects o JOIN documents d ON d.object = o.id
2
+ WHERE o.type = 'document' AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all();for(const{id:t}of i)o.has(t)||await v(e,await y(e,t),new Set,n,m);for(const{owner:t,id:s,body:c}of r)await Q(e,t,await y(e,s),c);return n}u(Ee,"reconcilePackageDocuments");async function Q(e,a,n,l){if(n.deleted_at!==null)return;const m={class:"app",app:a,scopes:[]},o=[];for(const i of M(l)){const t=await P(e,m,i);"object"in t&&t.object.id!==n.id&&await V(e,t.object.id)&&o.push({written_as:i,dst:t.object})}const r=u(i=>JSON.stringify(i.map(t=>[t.dst,t.written_as]).sort()),"key"),f=await e.prepare("SELECT dst, written_as FROM edges WHERE type = 'links-to' AND src = ?").all(n.id);r(f)!==r(o.map(i=>({dst:i.dst.id,written_as:i.written_as})))&&await J(e,n,o)}u(Q,"deriveLinks");async function V(e,a){return await e.prepare("SELECT 1 FROM documents WHERE object = ? AND source_path IS NOT NULL").get(a)!==void 0}u(V,"shippedDocument");async function X(e,a,n,l,m){const o=L(n.frontmatter,n.body,{missingPrivacy:"public"}),r={preview:o.preview.trim()===""?n.name:o.preview,summary:o.summary,whyItExists:o.whyItExists,unlisted:o.unlisted,rules:o.rules,lintIgnore:Array.isArray(o.lintIgnore)?o.lintIgnore.filter(d=>typeof d=="string"):null,extensions:o.extensions!==null&&typeof o.extensions=="object"&&!Array.isArray(o.extensions)?o.extensions:null},f=await A(e,n.body);let i=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
3
+ WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND o.name = ? AND d.source_path IS NOT NULL`).get(a.id,n.name);if(i?await e.prepare("UPDATE documents SET source_path = ? WHERE object = ? AND source_path IS DISTINCT FROM ?").run(n.path,i.id,n.path):i=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
4
+ WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path = ?`).get(a.id,n.path),!i){const d=await C(e,{owner:a,name:n.name,kind:o.kind,privacy:"public",...r,revisions:[{body_hash:f,author:a,rationale:null,created_at:new Date().toISOString()}]});return await e.prepare("UPDATE documents SET source_path = ? WHERE object = ?").run(n.path,d.id),await W(e,d.id),l.created+=1,d.id}const t=i.name!==n.name||i.kind!==o.kind;i.name!==n.name&&(i=await U(e,null,i,{name:n.name})),i.kind!==o.kind&&(await K(e,i,o.kind),i=await y(e,i.id));const s=await F(e,i),[c]=await x(e,i),w=c?.body_hash!==f,E=i.preview!==r.preview||s.summary!==r.summary||s.why_it_exists!==r.whyItExists||s.unlisted!==r.unlisted||JSON.stringify(s.lint_ignore)!==JSON.stringify(r.lintIgnore)||JSON.stringify(s.extensions)!==JSON.stringify(r.extensions)||JSON.stringify((await z(e,i)).map(N))!==JSON.stringify(r.rules.map(N));if(!w&&!E&&!t)return i.id;const p=await k(e,i,r);if(w&&c){const d=_(),h=c.number+1;await e.prepare(`INSERT INTO revisions (rev_id, document, number, body_hash, author, rationale, verbatim, created_at)
5
+ SELECT ?, document, ?, ?, author, rationale, verbatim, ? FROM revisions WHERE rev_id = ?`).run(d,h,f,new Date().toISOString(),c.rev_id),await e.prepare("INSERT INTO revision_parents (child, parent) SELECT ?, parent FROM revision_parents WHERE child = ?").run(d,c.rev_id),await e.prepare("DELETE FROM revisions WHERE rev_id = ?").run(c.rev_id),await e.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(d,h,p.id),await b(e,{event:"edited",object:p,actor:a,pointer:d,attrs:{revision:h,from_revision:c.number,rationale:"package file changed"}},m)}return l.updated+=1,p.id}u(X,"convergeFile");function N(e){return[e.on,e.deliver,e.match??null,e.matchFrontmatter??null,e.if??null]}u(N,"ruleKey");async function v(e,a,n,l,m){const o=await e.prepare(`SELECT o.* FROM objects o JOIN documents d ON d.object = o.id
6
+ WHERE o.type = 'document' AND o.owner = ? AND o.deleted_at IS NULL AND d.source_path IS NOT NULL`).all(a.id);for(const r of o)n.has(r.id)||(await j(e,null,r,m),await H(e,r.id),l.deleted+=1)}u(v,"deleteMissing");export{X as convergeFile,v as deleteMissing,Q as deriveLinks,G as packageSources,Ee as reconcilePackageDocuments};
@@ -0,0 +1,30 @@
1
+ import { type PackageReconcileReport } from './package-docs.js';
2
+ import { type PackageFile } from './package-files.js';
3
+ import type { CanvasDb } from '../canvas/db.js';
4
+ /** What one app's package folder holds right now, read before the publishing transaction opens. */
5
+ export interface PackageSnapshot {
6
+ app: string;
7
+ origin: 'builtin' | 'provider' | 'plugin';
8
+ /** crtr release (builtin), bundle generation (provider), manifest version (plugin) */
9
+ version: string;
10
+ /** when this process obtained the bytes; null for the builtin package */
11
+ fetchedAt: string | null;
12
+ digest: string;
13
+ /** files with `path` already package-relative, prefixed by the app: `<app>/<relative path>` */
14
+ files: PackageFile[];
15
+ invalid: Array<{
16
+ path: string;
17
+ error: string;
18
+ }>;
19
+ }
20
+ export interface PublishReport extends PackageReconcileReport {
21
+ /** the row's release is newer than this snapshot: nothing was written */
22
+ superseded: boolean;
23
+ }
24
+ /** The package folder `dir` as a snapshot of app `app`. Throws when the folder or any directory in it cannot be read. The digest covers the
25
+ * path and raw bytes of every markdown file, invalid ones included, so removing or changing an invalid file changes it. */
26
+ export declare function readPackageSnapshot(dir: string, meta: Pick<PackageSnapshot, 'app' | 'origin' | 'version' | 'fetchedAt'>): PackageSnapshot;
27
+ /** App `crtr` as this process's release ships it. */
28
+ export declare function readBuiltinSnapshot(): PackageSnapshot;
29
+ /** Publish one snapshot inside the caller's write transaction. Serialized per app on the publication row. Returns what changed. */
30
+ export declare function publish(db: CanvasDb, snapshot: PackageSnapshot, publishedBy: string): Promise<PublishReport>;
@@ -0,0 +1,3 @@
1
+ var g=Object.defineProperty;var p=(i,e)=>g(i,"name",{value:e,configurable:!0});import{createHash as w}from"node:crypto";import{readdirSync as v,readFileSync as y,statSync as h}from"node:fs";import{join as E,relative as u,sep as m}from"node:path";import{compareReleaseVersions as S}from"../../api/runtime-version.js";import{crtrVersion as _}from"../../shared/crtr-version.js";import{emitEvent as N}from"../events/emit.js";import{builtinMemoryRoot as k}from"../scope.js";import{deleteMissing as F,convergeFile as L,deriveLinks as O}from"./package-docs.js";import{packageFiles as T}from"./package-files.js";import{ensureOwner as D,getById as I}from"./objects.js";import{checkoutsForCwd as R}from"./repo-sync/repos.js";function A(i){if(!h(i,{throwIfNoEntry:!1})?.isDirectory())throw new Error(`package folder is missing or not a directory: ${i}`);const e=[],o=[i];for(;o.length>0;){const r=o.pop();for(const t of v(r,{withFileTypes:!0})){const n=E(r,t.name);t.isDirectory()?t.name.startsWith(".")||o.push(n):t.isFile()&&t.name.endsWith(".md")&&t.name!=="SKILL.md"&&e.push([u(i,n).split(m).join("/"),y(n)])}}return e.sort((r,t)=>r[0]<t[0]?-1:1)}p(A,"strictPackageBytes");function b(i,e){const o=A(i),{files:r,invalid:t}=T(i),n=r.map(c=>({...c,path:`${e.app}/${u(i,c.path).split(m).join("/")}`})),a=w("sha256");for(const[c,d]of o)a.update(c).update("\0").update(d).update("\0");return{...e,digest:a.digest("hex"),files:n,invalid:t}}p(b,"readPackageSnapshot");function G(){return b(k(),{app:"crtr",origin:"builtin",version:_(),fetchedAt:null})}p(G,"readBuiltinSnapshot");async function K(i,e,o){const r={created:0,updated:0,deleted:0,conflicts:[],invalid:[...e.invalid],quarantined:[],superseded:!1},t=await D(i,"app","app",`app:${e.app}`,e.app),n=new Date().toISOString();await i.prepare(`INSERT INTO package_publications (app, origin, version, digest, published_at, published_by)
2
+ VALUES (?, ?, '', '', ?, ?) ON CONFLICT (app) DO NOTHING`).run(t.id,e.origin,n,o);const a=await i.prepare("SELECT version, digest, withdrawn_at FROM package_publications WHERE app = ? FOR UPDATE").get(t.id);if(S(e.version,a.version)<0)return r.superseded=!0,N({level:"warn",event:"daemon.packages.superseded",fields:{app:e.app,snapshot_version:e.version,published_version:a.version}}),r;if(a.digest===e.digest&&a.withdrawn_at===null){if(a.version===e.version)return r}else{const d=p(async s=>await R(i,s),"reposFn"),l=new Set;for(const s of e.files)l.add(await L(i,t,s,r,d));if(e.invalid.length===0&&await F(i,t,l,r,d),r.created+r.updated+r.deleted>0)for(const s of e.files){const f=await i.prepare("SELECT o.id FROM objects o WHERE o.type = 'document' AND o.owner = ? AND o.name = ? AND o.deleted_at IS NULL").get(t.id,s.name);f!==void 0&&await O(i,t,await I(i,f.id),s.body)}}return await i.prepare(`UPDATE package_publications SET origin = ?, version = ?, fetched_at = ?, digest = ?, published_at = ?, published_by = ?, withdrawn_at = NULL
3
+ WHERE app = ?`).run(e.origin,e.version,e.fetchedAt,e.digest,n,o,t.id),r}p(K,"publish");export{K as publish,G as readBuiltinSnapshot,b as readPackageSnapshot};
@@ -1 +1 @@
1
- var p=Object.defineProperty;var a=(e,n)=>p(e,"name",{value:n,configurable:!0});import{directory as c,normalizeMachineId as m}from"../peer/directory.js";import{peerCall as f}from"../peer/client.js";import{ProviderError as s}from"./errors.js";const l={current:{list:a(()=>c.list(),"list"),lastList:a(()=>c.lastList(),"lastList"),call:a((e,n,i)=>f(e,n,i),"call")}},g="/v1/device/call",A=6e4,v=a(e=>({machine_id:e.machine_id,name:e.name,last_seen:e.last_seen}),"candidateView");function u(e,n){const i=e.map(r=>`${r.name} (${r.machine_id})`).join(", ");return new s("machine_required",n===void 0?`This account has ${e.length} devices: ${i}. Pick one with --machine <machine_id>.`:`${n} is not one of this account's devices. Devices: ${i}. Pick one with --machine <machine_id>.`,{param:"machine",details:{candidates:e.map(v)}})}a(u,"machineRequired");const h=a(e=>{const n=e.last_seen===null?NaN:Date.parse(e.last_seen);return Number.isFinite(n)?n:-1/0},"seenAt");async function x(e,n=l.current,i={}){let r;try{r=await n.list()}catch(t){const d=n.lastList();if(d===null)throw new s("provider_unavailable",`The directory could not be reached to find your devices: ${t instanceof Error?t.message:String(t)}`,{origin:"directory"});r=d}const o=r.filter(t=>t.kind==="device");if(o.length===0){const t="This needs the Northlight app on your Mac, and none is paired to this account. Run `crtr sys desktop install`: it downloads Northlight Desktop from Northlight, installs it in Applications, opens it and pairs it to this account.",d="Tell the person in one or two sentences what the Northlight app gives them for this task (their own signed-in browser, Messages, files and shell on this Mac) and ask once whether to install it; on yes run `crtr sys desktop install` yourself. If they already said no to it in this conversation, do not ask again: say it needs the app and carry on without it.";throw new s("provider_not_connected",t,{origin:"provider",details:{code:"provider_not_connected",message:t,next:d,status:403}})}if(e!==void 0){const t=m(e),d=o.find(_=>_.machine_id===t);if(!d)throw u(o,e);return d}if(o.length>1&&i.newest===!0)return o.reduce((t,d)=>h(d)>h(t)?d:t);if(o.length>1)throw u(o,void 0);return o[0]}a(x,"selectDevice");function w(e){return{tool:e.tool,arguments:e.arguments,version:e.version,request_id:e.requestId,...e.runId===null?{}:{run_id:e.runId},deadline_at:e.deadlineAt,...e.upload===null?{}:{upload:e.upload}}}a(w,"deviceBody");async function b(e,n,i,r,o=l.current){const t=Math.max(1,n.deadlineAt-Date.now());return o.call(e.machine_id,{method:"POST",path:g,body:w(n)},{envelope:i,device:!0,signal:AbortSignal.any([r,AbortSignal.timeout(t)])})}a(b,"sendDeviceCall");function E(e,n,i){switch(n){case"timeout":return new s("device_timeout",`${e.name} did not answer in time.`,{origin:"runtime",details:{machine_id:e.machine_id}});case"directory_unreachable":case"no_directory":return new s("provider_unavailable",i,{origin:"directory"});case"unknown_machine":return new s("provider_not_connected",`${e.name} is no longer on this account.`,{origin:"directory"});case"not_a_crtr_peer":return new s("server_error",i);default:return new s("device_offline",i.trim()===""?`${e.name} is offline.`:i,{origin:"runtime",details:{machine_id:e.machine_id}})}}a(E,"deviceUnreachable");function N(e,n,i,r,o){if(n===401)return new s("device_offline",`${e.name}: Desktop is signed out. Sign in to Northlight Desktop again.`,{origin:"runtime",details:{machine_id:e.machine_id}});if(n===426){const t=o?.error?.details?.supported;return new s("peer_version_too_old",r??`${e.name} does not serve this device major. Update Northlight Desktop.`,{origin:"runtime",details:{machine_id:e.machine_id,...Array.isArray(t)?{supported:t}:{}}})}return n===502&&i==="machine_disconnected"?new s("device_disconnected",`${e.name} disconnected during the call; the action may have run.`,{origin:"runtime",details:{machine_id:e.machine_id}}):i==="payload_too_large"||i==="response_too_large"?new s("provider_error",r??"The call or its answer was too large for the relay (16 MiB).",{origin:"runtime",status:413,details:{code:i,message:r??null,status:n}}):null}a(N,"deviceFailure");export{A as DEFAULT_DEVICE_REQUEST_MS,g as DEVICE_CALL_PATH,w as deviceBody,l as deviceDeps,N as deviceFailure,E as deviceUnreachable,x as selectDevice,b as sendDeviceCall};
1
+ var p=Object.defineProperty;var a=(e,n)=>p(e,"name",{value:n,configurable:!0});import{directory as c,normalizeMachineId as m}from"../peer/directory.js";import{peerCall as g}from"../peer/client.js";import{ProviderError as s}from"./errors.js";const l={current:{list:a(()=>c.list(),"list"),lastList:a(()=>c.lastList(),"lastList"),call:a((e,n,t)=>g(e,n,t),"call")}},f="/v1/device/call",A=6e4,w=a(e=>({machine_id:e.machine_id,name:e.name,last_seen:e.last_seen}),"candidateView");function h(e,n){const t=e.map(o=>`${o.name} (${o.machine_id})`).join(", ");return new s("machine_required",n===void 0?`This account has ${e.length} devices: ${t}. Pick one with --machine <machine_id>.`:`${n} is not one of this account's devices. Devices: ${t}. Pick one with --machine <machine_id>.`,{param:"machine",details:{candidates:e.map(w)}})}a(h,"machineRequired");const u=a(e=>{const n=e.last_seen===null?NaN:Date.parse(e.last_seen);return Number.isFinite(n)?n:-1/0},"seenAt");async function M(e,n=l.current,t={}){let o;try{o=await n.list()}catch(i){const d=n.lastList();if(d===null)throw new s("provider_unavailable",`The directory could not be reached to find your devices: ${i instanceof Error?i.message:String(i)}`,{origin:"directory"});o=d}const r=o.filter(i=>i.kind==="device");if(r.length===0){const i="This needs the Northlight app on your Mac, and none is signed in to this account. Install Northlight Desktop on the Mac (on that Mac, `crtr sys desktop install`; otherwise https://account.northlight.ai/desktop), then sign in inside Desktop with the same Northlight account: its window shows a code and one Allow click.",d="Tell the person in one or two sentences what the Northlight app gives them for this task (their own signed-in browser, Messages, files and shell on this Mac) and ask once whether to install it. On yes: if you are running on their Mac, run `crtr sys desktop install` yourself; otherwise give them https://account.northlight.ai/desktop (or the download steps you already have) to install Desktop on their Mac. Then they sign in inside Desktop with the same Northlight account; do not ask them to sign crtr in. If they already said no to it in this conversation, do not ask again: say it needs the app and carry on without it.";throw new s("provider_not_connected",i,{origin:"provider",details:{code:"provider_not_connected",message:i,next:d,status:403}})}if(e!==void 0){const i=m(e),d=r.find(_=>_.machine_id===i);if(!d)throw h(r,e);return d}if(r.length>1&&t.newest===!0)return r.reduce((i,d)=>u(d)>u(i)?d:i);if(r.length>1)throw h(r,void 0);return r[0]}a(M,"selectDevice");function v(e){return{tool:e.tool,arguments:e.arguments,version:e.version,request_id:e.requestId,...e.runId===null?{}:{run_id:e.runId},deadline_at:e.deadlineAt,...e.upload===null?{}:{upload:e.upload}}}a(v,"deviceBody");async function x(e,n,t,o,r=l.current){const i=Math.max(1,n.deadlineAt-Date.now());return r.call(e.machine_id,{method:"POST",path:f,body:v(n)},{envelope:t,device:!0,signal:AbortSignal.any([o,AbortSignal.timeout(i)])})}a(x,"sendDeviceCall");function N(e,n,t){switch(n){case"timeout":return new s("device_timeout",`${e.name} did not answer in time.`,{origin:"runtime",details:{machine_id:e.machine_id}});case"directory_unreachable":case"no_directory":return new s("provider_unavailable",t,{origin:"directory"});case"unknown_machine":return new s("provider_not_connected",`${e.name} is no longer on this account.`,{origin:"directory"});case"not_a_crtr_peer":return new s("server_error",t);default:return new s("device_offline",t.trim()===""?`${e.name} is offline.`:t,{origin:"runtime",details:{machine_id:e.machine_id}})}}a(N,"deviceUnreachable");function T(e,n,t,o,r){if(n===401)return new s("device_offline",`${e.name}: Desktop is signed out. Sign in to Northlight Desktop again.`,{origin:"runtime",details:{machine_id:e.machine_id}});if(n===426){const i=r?.error?.details?.supported;return new s("peer_version_too_old",o??`${e.name} does not serve this device major. Update Northlight Desktop.`,{origin:"runtime",details:{machine_id:e.machine_id,...Array.isArray(i)?{supported:i}:{}}})}return n===502&&t==="machine_disconnected"?new s("device_disconnected",`${e.name} disconnected during the call; the action may have run.`,{origin:"runtime",details:{machine_id:e.machine_id}}):t==="payload_too_large"||t==="response_too_large"?new s("provider_error",o??"The call or its answer was too large for the relay (16 MiB).",{origin:"runtime",status:413,details:{code:t,message:o??null,status:n}}):null}a(T,"deviceFailure");export{A as DEFAULT_DEVICE_REQUEST_MS,f as DEVICE_CALL_PATH,v as deviceBody,l as deviceDeps,T as deviceFailure,N as deviceUnreachable,M as selectDevice,x as sendDeviceCall};
@@ -1 +1 @@
1
- var o=Object.defineProperty;var t=(a,e)=>o(a,"name",{value:e,configurable:!0});import{withCanvasWrite as c}from"../../../core/canvas/db.js";import{reconcilePackages as r}from"../../../core/graph/package-reconcile.js";const m=[{method:"POST",pattern:"/v1/documents/reconcile-packages",handler:t(async()=>({status:200,body:await c(async a=>await r(a))}),"handler")}];export{m as packageDocRoutes};
1
+ var o=Object.defineProperty;var a=(e,t)=>o(e,"name",{value:t,configurable:!0});import{withCanvasWrite as d}from"../../../core/canvas/db.js";import{isCloudRole as i}from"../../../core/canvas/person-state.js";import{reconcilePackages as n}from"../../../core/graph/package-reconcile.js";const r=a(()=>({created:0,updated:0,deleted:0,conflicts:[],invalid:[],quarantined:[],kinds:{applied:[],refused:[],invalid:[]},tokens:{granted:[],revoked:[]}}),"EMPTY_REPORT"),m=[{method:"POST",pattern:"/v1/documents/reconcile-packages",handler:a(async()=>({status:200,body:i()?r():await d(async e=>await n(e))}),"handler")}];export{m as packageDocRoutes};
@@ -1,18 +1,19 @@
1
- var er=Object.defineProperty;var s=(e,l)=>er(e,"name",{value:l,configurable:!0});import{applyBootSettings as rr}from"./boot-settings.js";import{startPeerOutbox as tr}from"./peer/outbox.js";import{reportMachineStatus as nr}from"../core/peer/directory.js";import{createPeerRelay as or}from"./peer/relay-service.js";import{writeFileSync as ir,readFileSync as ar,rmSync as sr,mkdirSync as ye,existsSync as k,unlinkSync as _e,chmodSync as be}from"node:fs";import{createServer as J}from"node:http";import{dirname as cr}from"node:path";import{envCrtrdTcp as ke,envDocumentPort as lr}from"../shared/env.js";import{openCanvasConversationStore as dr,serveCanvasConversationStore as mr}from"../core/canvas/sessions.js";import{installConversationStore as pr}from"../core/conversation-store/index.js";import{pollRunObservers as fr}from"./runs/observer.js";import{randomBytes as Re}from"node:crypto";import{apiSocketPath as Pe,crtrHome as ur}from"../core/canvas/paths.js";import{activeAppUsers as wr}from"../core/spaces/users.js";import{ensureAppSpace as hr,ensureUserFiles as vr}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as x}from"../launcher/spec.js";import{launcherRequest as L}from"../launcher/client.js";import{installLauncherEventSink as gr}from"./launcher-events.js";import{initializeEmptyLayout as Sr,layoutNeedsMigration as yr,socketDir as Ee}from"../core/layout.js";import{migrateLooseKeys as _r}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as br}from"../core/profiles/relocate.js";import{getNode as kr,listNodesForSupervision as Ie,listParkedResidentsWithWakeMail as Rr,setFrozen as xe}from"../core/canvas/index.js";import{createApiServer as Pr,closeServer as Te}from"./api/server.js";import{Router as Er}from"./api/router.js";import{healthRoutes as Ir}from"./api/handlers/health.js";import{createDocumentListener as xr}from"./api/document-listener.js";import{documentSecret as Tr}from"./api/document-secret.js";import{createAppListener as Dr}from"./api/app-listener.js";import{registerMachineEnvironment as Cr}from"../core/environments.js";import{registerBlaxelRunnerFromEnv as Lr}from"../core/secrets/blaxel-register.js";import{markCloudRole as $r,resolvePerson as Ar,retireLocalPerson as Nr,setPersonId as Mr}from"../core/canvas/person.js";import{backfillNodeApps as Gr,runPermissionsBoot as Or}from"../core/permissions/boot.js";import{startRunNotices as Fr}from"./run-notices.js";import{startGrantEffects as Br}from"./reconcilers/grant-effects.js";import{createGrantSync as Hr,GRANT_SYNC_INTERVAL_S as jr}from"../core/grants/sync.js";import{sweepSandboxes as zr}from"../core/grants/on-sync.js";import{rebuildCronExecutions as Ur}from"./cron/run-socket.js";import{hasSyncedGrants as qr}from"../core/grants/mirror.js";import{readConfig as se}from"../core/config.js";import{reconcileUndeliveredTickets as Wr}from"./human/sweep.js";import{resetHandedOffDeliveries as Jr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as Kr}from"../core/canvas/daemon-actions.js";import{sweepReviews as Vr}from"./review/sweep.js";import{pidfilePath as ce}from"./pidfile.js";import{rendererWarning as Yr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as Qr}from"./supervise-cadence.js";import{createResumeDetector as Xr}from"./resume-detector.js";import{setFatalFaultAnnouncer as Zr}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as et}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as rt}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as tt}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as nt}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as ot}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as it}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as at}from"./reconcilers/action-lane.js";import{endLiveExecution as st,migrateLegacyExecutionIdentities as ct}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as lt}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as dt}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as mt}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as pt}from"./reconcilers/storage-due-work.js";import{PurgeDueWorkReconciler as ft}from"./reconcilers/purge-due-work.js";import{assertLedgerEnv as ut}from"../core/ledger/rates.js";import{LedgerSandboxReconciler as wt}from"./reconcilers/ledger-sandbox.js";import{LedgerStorageReconciler as ht}from"./reconcilers/ledger-storage.js";import{LedgerR2Reconciler as vt}from"./reconcilers/ledger-r2.js";import{ImportLaneReconciler as gt}from"./reconcilers/import-lane.js";import{BashDeadlineReconciler as St}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as yt}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as _t}from"./reconcilers/structured-request-lane.js";import{RecipeImageReconciler as bt,subscribeImageBuilt as kt}from"./reconcilers/recipe-image.js";import{reconcileStructuredRequestSidecars as Rt}from"./structured-requests/boot.js";import{captureLivenessSnapshot as De,capturePidCommand as Pt,captureTeardownSnapshot as Ce,isPidAlive as Et,isAnyPidAlive as Le,killProcessTreePids as $e}from"../core/canvas/pid.js";import{bindFleet as It,boundFleet as xt}from"../core/runtime/fleet.js";import{bindDaemonControl as Tt,clearDaemonControl as Dt}from"./control.js";import{execDaemon as Ct,spawnDaemon as Lt}from"./manage.js";import{reconcileAndMigrateBoot as $t}from"../core/canvas/boot.js";import{resolveInstallId as At}from"../core/canvas/install-id.js";import{setInstallId as Nt}from"../core/runtime/nodes.js";import{claimDaemonOwnership as Mt,daemonOwnershipApplies as Gt,daemonOwnershipClaimed as Ot,releaseDaemonOwnership as Ae}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as Ft,closeAllDbs as le,openDb as Ne,withCanvasWrite as Bt}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as Ht}from"../core/canvas/import-sqlite.js";import{ensurePostgres as jt,ensurePostgresHealthy as zt,stopPostgres as Ut,withPostgresStartLock as qt}from"./postgres/index.js";import{brokerSignalBus as F}from"./broker-signals/bus.js";import{brokerSignalStreams as Wt}from"./api/handlers/broker-signals.js";import{setRequestsStartRunObservers as Jt}from"./api/handlers/broker-ops.js";import{reconcilePackages as Kt}from"../core/graph/package-reconcile.js";import{startRepoSync as Vt}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as Yt,reapStaleSpares as Qt}from"../core/runtime/warm-pool.js";import{DaemonFleet as Xt}from"./fleet.js";import{copyNodeFilesToRows as Zt}from"../core/runtime/first-boot-rows.js";import{BOOT_MIGRATIONS as en}from"./boot-migrations.js";import{emitEvent as p}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as rn}from"../core/events/source.js";import{activateCanvasImport as tn,MigrationBlockedError as nn,runForcedCanvasImport as on}from"../migrations/activation.js";import{adoptInstallPerson as an,convertLegacyScopedStore as sn,personStoresCopied as cn}from"../core/scoped-state/migrate.js";import{readLegacyUserSnapshot as ln}from"../core/scoped-state/legacy-profiles.js";import{heldPersons as Me,installPreludeSnapshot as dn}from"../core/scoped-state/person-store.js";import{loadPersonSettings as Ge}from"../core/scoped-state/person-rows.js";import{userScopeRoot as Oe}from"../core/scope.js";import{brokerCommandDeclaresCanvasHome as de}from"../core/runtime/broker-ownership.js";import{brokerCommandDeclaresCanvasHome as Oi}from"../core/runtime/broker-ownership.js";function me(e){return/ --epoch (\S+) /.exec(e)?.[1]??null}s(me,"brokerCommandEpoch");const mn=5e3,Fe=150;async function pn(e){const l=De();if(l===null)throw p({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const u=ur(),f=[];for(const[n,d]of l.processes){const y=d.command;if(d.zombie||!y.includes("broker-cli.js")||!de(y,u)||me(y)===e)continue;const b=y.trim().split(/\s+/).at(-1)??"";b===""||b.endsWith("broker-cli.js")||f.push({pid:n,nodeId:b})}let v=0;const a=[];for(const n of f){const d=Pt(n.pid);if(d===null||!d.includes("broker-cli.js")||!de(d,u)||me(d)===e||d.trim().split(/\s+/).at(-1)!==n.nodeId)continue;const y=Ce(n.pid,null);if(!y.reused){try{process.platform==="linux"&&k(x)?await L({operation:"stop",pid:n.pid}):process.kill(n.pid,"SIGTERM")}catch{continue}v++,a.push({pid:n.pid,snap:y})}}const w=Date.now()+mn;for(;Date.now()<w&&a.some(n=>Le(n.snap.tree.length>0?n.snap.tree:[n.pid]));)await new Promise(n=>setTimeout(n,Fe));let g=0;for(const n of a){const d=n.snap.tree.length>0?n.snap.tree:[n.pid];Le(d)&&(process.platform==="linux"&&k(x)?await L({operation:"stop",pid:n.pid}):$e(d,"SIGKILL",n.snap.identities),g++)}g>0&&await new Promise(n=>setTimeout(n,Fe));const m=De();if(m===null)throw p({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const c=[];for(const[n,d]of m.processes){const y=d.command;d.zombie||!y.includes("broker-cli.js")||de(y,u)&&me(y)!==e&&c.push(n)}if(c.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${c.join(", ")}`);p({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:f.length,sigtermed:v,sigkilled:g}})}s(pn,"reapPriorEpochBrokers");const fn=5e3;async function un(e){const l=Array.from(e.entries());if(l.length===0)return;for(const[a]of l){const w=(await kr(a))?.status;(w==="active"||w==="idle")&&await xe(a,new Date().toISOString())}const u=l.map(([,a])=>({pid:a.pid,snap:Ce(a.pid,null),exited:a.handle.exited}));for(const a of u)if(!a.snap.reused)try{process.platform==="linux"&&k(x)?await L({operation:"stop",pid:a.pid}):process.kill(a.pid,"SIGTERM")}catch{}const f=new Promise(a=>{setTimeout(a,fn).unref()});await Promise.allSettled(u.map(a=>Promise.race([a.exited.then(()=>{}),f])));const v=[];for(const a of u)Et(a.pid)&&(process.platform==="linux"&&k(x)?await L({operation:"stop",pid:a.pid}):$e(a.snap.tree.length>0?a.snap.tree:[a.pid],"SIGKILL",a.snap.identities),v.push(a));await Promise.all(v.map(a=>a.exited));for(const[a]of l)await st(a,{reason:"daemon-teardown"});p({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:u.length,sigkilled:v.length}})}s(un,"teardownFleet");function Be(){ye(cr(ce()),{recursive:!0}),ir(ce(),String(process.pid),"utf8")}s(Be,"writePidfile");function He(e){return typeof e=="object"&&e!==null&&e.code==="ENOENT"}s(He,"isMissingPathError");function je(e){const l=ce();let u;try{u=ar(l,"utf8")}catch(f){if(He(f))return;throw f}if(u.trim()!==String(e))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${e})`);try{sr(l)}catch(f){if(He(f))return;throw f}}s(je,"removeOwnedPidfileMirror");const wn={acceptsDetachedWork:s(()=>!0,"acceptsDetachedWork"),registerDetached:s(e=>{e.catch(()=>{})},"registerDetached")},K=new et;Zr(e=>K.handleFatalFault(e,!1));const hn=new rt,vn=new tt,gn=new nt,Sn=new ot,yn=new it,_n=new at,bn=new dt,kn=new mt,Rn=new pt,Pn=new ft,En=new wt,In=new ht,xn=new vt,Tn=new gt,Dn=new St,Cn=new yt,Ln=new _t,$n=new bt;async function An(e=Date.now(),l=wn){const u=xt(),f=await K.runFleetPreamble(u);let v,a;try{const g=await Ie();v=g.filter(c=>c.status==="active"||c.status==="idle"),a=g.filter(c=>c.status==="done"||c.status==="canceled"||c.status==="dead");const m=new Set(a.map(c=>c.node_id));a.push(...(await Rr()).filter(c=>!m.has(c.node_id)))}catch(g){p({level:"error",event:"canvas.node_list.failed",error:g});return}await K.run(e,{rows:v,fleet:u,lifecycle:l}),await hn.run(e,{rows:v});const w=await vn.run(e,{rows:[...v,...a],fleet:u});await gn.run(e),await Ln.run(e),await $n.run(e,{lifecycle:l}),await Sn.run(e,{lifecycle:l}),await yn.run(e,{lifecycle:l}),await _n.run(e,{lifecycle:l}),await bn.run(e,{lifecycle:l}),await kn.run(e,{lifecycle:l,fleet:u}),Rn.run(e,{lifecycle:l}),Pn.run(e,{lifecycle:l}),En.run(e,{lifecycle:l}),In.run(e,{lifecycle:l}),xn.run(e,{lifecycle:l}),Tn.run(e,{lifecycle:l}),await fr(),Cn.run(e,{fleet:u,lifecycle:l}),await Dn.run(e,{rows:v}),K.warnIfSaturated({fleet:u,lifecycle:l,thresholds:f,frozen:w})}s(An,"superviseTick");function V(e){return e===void 0||e==="reconcile"}s(V,"roleRunsLanes");function $(e){return e===void 0||e==="api"}s($,"roleServesRequests");const Nn=66e4;function Mn(e){const l=new Er().registerAll(Ir(()=>null).filter(n=>n.pattern==="/healthz")),u=s((n,d)=>{const y=(n.url??"/").split("?")[0];if((n.method??"GET").toUpperCase()==="GET"&&y==="/healthz"){l.handle(n,d,null,{class:"person"});return}d.writeHead(404,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({error:{code:"not_found",message:"this process serves only /healthz",origin:"runtime"}}))},"handler"),f=Pe();k(f)&&_e(f);const v=J(u),a=new Promise((n,d)=>{v.once("error",d),v.listen(f,()=>{v.off("error",d),be(f,384),n()})});let w,g=null;const m=e.tcp??ke()??se("user").api.tcp;if(m!==void 0&&m!==""){const n=/^(.*):(\d+)$/.exec(m.trim());if(n===null||Number(n[2])>65535)throw new Error(`invalid reconcile probe TCP address: ${m}`);const d=J(u);d.on("error",y=>p({event:"api.server.failed",level:"error",error:y})),d.listen(Number(n[2]),n[1]===""?"0.0.0.0":n[1],()=>{g=`${n[1]===""?"0.0.0.0":n[1]}:${n[2]}`}),w=d}let c=!1;return{ready:a,tcpAddress:s(()=>g,"tcpAddress"),async close(n={}){if(!c){c=!0,await Promise.all([Te(v,new Set,n.graceMs),w===void 0?Promise.resolve():Te(w,new Set,n.graceMs)]);try{k(f)&&_e(f)}catch{}}}}}s(Mn,"createProbeServer");const Gn="daemon is draining for shutdown and admits no new work";async function On(e){ye(Ee(),{recursive:!0,mode:493}),process.platform==="linux"&&be(Ee(),493);const l=Pe();if(k(l))throw new Error(`layout refusal cannot bind occupied socket: ${l}`);const u=J((m,c)=>{if(m.method==="GET"&&m.url==="/healthz"){c.writeHead(200,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}c.writeHead(503,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((m,c)=>{u.once("error",c),u.listen(l,()=>{u.off("error",c),m()})});let f;const v=e.tcp??ke();if(v){const m=/^(.*):(\d+)$/.exec(v);if(!m||Number(m[2])>65535)throw new Error(`invalid refusal TCP address: ${v}`);const c=J(u.listeners("request")[0]);await new Promise((n,d)=>{c.once("error",d),c.listen(Number(m[2]),m[1],()=>{c.off("error",d),n()})}),f=c}Be();let a;const w=s(()=>(a??=(async()=>{u.closeAllConnections(),f?.closeAllConnections(),await Promise.all([u,f].filter(m=>m!==void 0).map(m=>new Promise((c,n)=>m.close(d=>d?n(d):c())))),je(process.pid),process.off("SIGINT",g),process.off("SIGTERM",g)})(),a),"cleanup"),g=s(()=>{w().then(()=>process.exit(0),m=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(m)}
2
- `),process.exit(1)})},"onSignal");return process.on("SIGINT",g),process.on("SIGTERM",g),w}s(On,"serveLayoutRefusal");async function Fn(e={}){if(e.role==="machine")return(await import("./machine-daemon.js")).runMachineDaemon();if(yr())return On(e);Sr();const l=s(r=>{process.stderr.write(`[crtrd] ${r}
3
- `)},"logPostgres"),u=!!process.env.CRTR_TEST_PG_URL,f=Gt(e.role);Jt(V(e.role));const v=s(async()=>f?await Mt(Re(16).toString("hex")):{claimed:!0,claim:{pid:process.pid,pid_identity:"",token:Re(16).toString("hex")}},"claimOwnership"),{database:a,ownership:w}=u?{database:{url:await Ft()},ownership:await v()}:await qt(async()=>({database:await jt({log:l,startLockHeld:!0}),ownership:await v()}));if(!w.claimed){if(w.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${w.incumbentPid??"?"})
4
- `);else{const r=[w.incumbentPid!==void 0?`pid ${w.incumbentPid}`:void 0,w.verdict!==void 0?`verdict ${w.verdict}`:void 0].filter(h=>h!==void 0),o=r.length>0?` (${r.join(", ")})`:"";process.stderr.write(`[crtrd] claim lost: ${w.reason}${o}
5
- `),w.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(w.reason==="own_identity_indeterminate"||w.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const g=w.claim;let m=null,c=null,n=null,d,y="";try{f&&await pn(g.token);for(const t of br())process.stderr.write(`[crtrd] ${t}
6
- `);for(const t of await _r())process.stderr.write(`[crtrd] ${t}
7
- `);await Ne(),await Ht(a.url,l);const r=e.role===void 0&&!await cn();let o=null;r&&(sn({kind:"user",root:Oe()}),o=ln(Oe())),e.role===void 0&&dn(o??{profiles:[],defaults:{},settings:{},state:{}}),await F.listen(a.url);const h=s(t=>{Ge(t).catch(O=>p({level:"error",event:"daemon.settings.reload_failed",error:O}))},"reloadPerson");d=await F.subscribe(t=>{Me().includes(t)&&h(t)},{kinds:["settings"],onListenStart:s(()=>{for(const t of Me())h(t)},"onListenStart")}),e.role!==void 0&&$r();const i=await Bt(async t=>(await Gr(t),await Kt(t)));for(const t of i.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${t.message}
8
- `);for(const t of i.invalid)process.stderr.write(`[crtrd] package file is not a document: ${t.path}: ${t.error}
9
- `);for(const t of i.quarantined)process.stderr.write(`[crtrd] plugin "${t.name}" has a damaged ${t.file} (${t.reason}); ${t.movedTo!==void 0?`moved to ${t.movedTo}`:"left in place and skipped"}
10
- `);for(const t of i.kinds.refused)process.stderr.write(`[crtrd] plugin "${t.plugin}" kind ${t.kind} not registered: ${t.message}
11
- `);for(const t of i.kinds.invalid)process.stderr.write(`[crtrd] plugin "${t.plugin}" declares invalid object kinds or scopes; no kinds registered and no app token: ${t.error}
12
- `);if(y=await At(),Nt(y),e.role===void 0&&(m=await Ar(),m!==null)){if(Mr(m.id),await an(m.id),process.platform==="darwin"){const{readConfig:t,updateConfig:O}=await import("../core/config.js");if(t("user").paths?.user_files===void 0){const{homedir:Ze}=await import("node:os");await O("user",Se=>{Se.paths??={},Se.paths.user_files??=Ze()})}}c=await Nr(),c.joinedFrom!==null&&await Ge(c.person),await rr(),n=await Or()}if(process.platform==="linux"&&k(x)&&(gr(),await L({operation:"users.sync",users:await wr()})),f){await lt(g.token);const t=await Zt();Object.values(t).some(O=>O>0)&&process.stderr.write(`[crtrd] node files copied into rows: ${JSON.stringify(t)}
13
- `)}}catch(r){await F.close();try{if(f){const{released:o}=await Ae(g);if(!o)throw new Error("daemon ownership claim was not released")}await le()}catch(o){throw new AggregateError([r,o],"daemon startup failed and cleanup also failed")}throw r}let b=!0,Y,B=Promise.resolve(),Q=!1,H,X,Z,T;const j=new Set;let ee=!1,pe=!1,P,D,re,z=!1,U,A,N,M,te,ne,oe;const ze=Xr();let E,S={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},C="initial startup activation",Ue=null;function qe(){return S.phase==="ready"?null:{phase:S.phase,message:S.message,blockerPaths:S.blockerPaths}}s(qe,"startupBlock");function R(r){j.add(r),r.finally(()=>{j.delete(r)}).catch(()=>{})}s(R,"registerDetached");const fe={acceptsDetachedWork:s(()=>b,"acceptsDetachedWork"),registerDetached:R};V(e.role)&&await Vt(fe,Ne);async function We(){try{b=!1,Wt.close(),X?.(),X=void 0,Z?.(),d?.(),Z=void 0,ne?.(),ne=void 0,await F.close(),H!==void 0&&(clearInterval(H),H=void 0),N!==void 0&&(clearInterval(N),N=void 0),oe?.(),oe=void 0;let r;do r=B,await r;while(r!==B);for(;j.size>0;)await Promise.allSettled([...j]);if(T!==void 0&&(f&&await un(T),await T.exitsSettled(),T.dispose()),re!==void 0&&await re.close(),D!==void 0&&!z&&(z=!0,await D.close()),te?.(),te=void 0,M!==void 0){const o=M;M=void 0,await o.stop()}if(A!==void 0){const o=A;A=void 0,await o.close()}if(U!==void 0){const o=U;U=void 0,await o.close()}if(ee||(je(process.pid),ee=!0),ee&&!pe){if(f){const{released:o}=await Ae(g);if(!o)throw new Error("daemon ownership claim was not released")}pe=!0,await le()}}finally{Dt(),process.off("SIGINT",he),process.off("SIGTERM",ve),process.off("exit",ge)}}s(We,"cleanupAttempt");const q=1e3;let ue=!1;function Je(){return ue?{graceMs:q}:(ue=!0,_.fresh(()=>{p({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:q}})}),setTimeout(()=>{Ke()},q),{graceMs:q})}s(Je,"requestHandover");async function Ke(){try{await I()}catch(r){const o=r instanceof Error?r.stack??r.message:String(r);process.stderr.write(`[crtrd] handover cleanup failed:
1
+ var rr=Object.defineProperty;var s=(e,l)=>rr(e,"name",{value:l,configurable:!0});import{applyBootSettings as tr}from"./boot-settings.js";import{startPeerOutbox as nr}from"./peer/outbox.js";import{reportMachineStatus as or}from"../core/peer/directory.js";import{createPeerRelay as ir}from"./peer/relay-service.js";import{writeFileSync as ar,readFileSync as sr,rmSync as cr,mkdirSync as ye,existsSync as k,unlinkSync as _e,chmodSync as be}from"node:fs";import{createServer as J}from"node:http";import{dirname as lr}from"node:path";import{envCrtrdTcp as ke,envDocumentPort as dr}from"../shared/env.js";import{openCanvasConversationStore as mr,serveCanvasConversationStore as pr}from"../core/canvas/sessions.js";import{installConversationStore as fr}from"../core/conversation-store/index.js";import{pollRunObservers as ur}from"./runs/observer.js";import{randomBytes as Re}from"node:crypto";import{apiSocketPath as Pe,crtrHome as hr}from"../core/canvas/paths.js";import{activeAppUsers as wr}from"../core/spaces/users.js";import{ensureAppSpace as vr,ensureUserFiles as gr}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as T}from"../launcher/spec.js";import{launcherRequest as $}from"../launcher/client.js";import{installLauncherEventSink as Sr}from"./launcher-events.js";import{initializeEmptyLayout as yr,layoutNeedsMigration as _r,socketDir as Ee}from"../core/layout.js";import{migrateLooseKeys as br}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as kr}from"../core/profiles/relocate.js";import{getNode as Rr,listNodesForSupervision as Ie,listParkedResidentsWithWakeMail as Pr,setFrozen as xe}from"../core/canvas/index.js";import{createApiServer as Er,closeServer as Te}from"./api/server.js";import{Router as Ir}from"./api/router.js";import{healthRoutes as xr}from"./api/handlers/health.js";import{createDocumentListener as Tr}from"./api/document-listener.js";import{documentSecret as Dr}from"./api/document-secret.js";import{createAppListener as Cr}from"./api/app-listener.js";import{registerMachineEnvironment as Lr}from"../core/environments.js";import{registerBlaxelRunnerFromEnv as $r}from"../core/secrets/blaxel-register.js";import{markCloudRole as Ar,resolvePerson as Nr,retireLocalPerson as Mr,setPersonId as Gr}from"../core/canvas/person.js";import{backfillNodeApps as De,runPermissionsBoot as Or}from"../core/permissions/boot.js";import{startRunNotices as Fr}from"./run-notices.js";import{startGrantEffects as Br}from"./reconcilers/grant-effects.js";import{createGrantSync as Hr,GRANT_SYNC_INTERVAL_S as jr}from"../core/grants/sync.js";import{sweepSandboxes as zr}from"../core/grants/on-sync.js";import{rebuildCronExecutions as Ur}from"./cron/run-socket.js";import{hasSyncedGrants as qr}from"../core/grants/mirror.js";import{readConfig as se}from"../core/config.js";import{reconcileUndeliveredTickets as Wr}from"./human/sweep.js";import{resetHandedOffDeliveries as Jr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as Kr}from"../core/canvas/daemon-actions.js";import{sweepReviews as Vr}from"./review/sweep.js";import{pidfilePath as ce}from"./pidfile.js";import{rendererWarning as Yr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as Qr}from"./supervise-cadence.js";import{createResumeDetector as Xr}from"./resume-detector.js";import{setFatalFaultAnnouncer as Zr}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as et}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as rt}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as tt}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as nt}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as ot}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as it}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as at}from"./reconcilers/action-lane.js";import{endLiveExecution as st,migrateLegacyExecutionIdentities as ct}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as lt}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as dt}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as mt}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as pt}from"./reconcilers/storage-due-work.js";import{PurgeDueWorkReconciler as ft}from"./reconcilers/purge-due-work.js";import{assertLedgerEnv as ut}from"../core/ledger/rates.js";import{LedgerSandboxReconciler as ht}from"./reconcilers/ledger-sandbox.js";import{LedgerStorageReconciler as wt}from"./reconcilers/ledger-storage.js";import{LedgerR2Reconciler as vt}from"./reconcilers/ledger-r2.js";import{ImportLaneReconciler as gt}from"./reconcilers/import-lane.js";import{BashDeadlineReconciler as St}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as yt}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as _t}from"./reconcilers/structured-request-lane.js";import{RecipeImageReconciler as bt,subscribeImageBuilt as kt}from"./reconcilers/recipe-image.js";import{reconcileStructuredRequestSidecars as Rt}from"./structured-requests/boot.js";import{captureLivenessSnapshot as Ce,capturePidCommand as Pt,captureTeardownSnapshot as Le,isPidAlive as Et,isAnyPidAlive as $e,killProcessTreePids as Ae}from"../core/canvas/pid.js";import{bindFleet as It,boundFleet as xt}from"../core/runtime/fleet.js";import{bindDaemonControl as Tt,clearDaemonControl as Dt}from"./control.js";import{execDaemon as Ct,spawnDaemon as Lt}from"./manage.js";import{reconcileAndMigrateBoot as $t}from"../core/canvas/boot.js";import{resolveInstallId as At}from"../core/canvas/install-id.js";import{setInstallId as Nt}from"../core/runtime/nodes.js";import{claimDaemonOwnership as Mt,daemonOwnershipApplies as Gt,daemonOwnershipClaimed as Ot,releaseDaemonOwnership as Ne}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as Ft,closeAllDbs as le,openDb as Me,withCanvasWrite as Ge}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as Bt}from"../core/canvas/import-sqlite.js";import{ensurePostgres as Ht,ensurePostgresHealthy as jt,stopPostgres as zt,withPostgresStartLock as Ut}from"./postgres/index.js";import{brokerSignalBus as F}from"./broker-signals/bus.js";import{brokerSignalStreams as qt}from"./api/handlers/broker-signals.js";import{setRequestsStartRunObservers as Wt}from"./api/handlers/broker-ops.js";import{reconcilePackages as Jt}from"../core/graph/package-reconcile.js";import{publish as Kt,readBuiltinSnapshot as Vt}from"../core/graph/package-publish.js";import{startRepoSync as Yt}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as Qt,reapStaleSpares as Xt}from"../core/runtime/warm-pool.js";import{DaemonFleet as Zt}from"./fleet.js";import{copyNodeFilesToRows as en}from"../core/runtime/first-boot-rows.js";import{BOOT_MIGRATIONS as rn}from"./boot-migrations.js";import{emitEvent as m}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as tn}from"../core/events/source.js";import{activateCanvasImport as nn,MigrationBlockedError as on,runForcedCanvasImport as an}from"../migrations/activation.js";import{adoptInstallPerson as sn,convertLegacyScopedStore as cn,personStoresCopied as ln}from"../core/scoped-state/migrate.js";import{readLegacyUserSnapshot as dn}from"../core/scoped-state/legacy-profiles.js";import{heldPersons as Oe,installPreludeSnapshot as mn}from"../core/scoped-state/person-store.js";import{loadPersonSettings as Fe}from"../core/scoped-state/person-rows.js";import{userScopeRoot as Be}from"../core/scope.js";import{brokerCommandDeclaresCanvasHome as de}from"../core/runtime/broker-ownership.js";import{brokerCommandDeclaresCanvasHome as Bi}from"../core/runtime/broker-ownership.js";function me(e){return/ --epoch (\S+) /.exec(e)?.[1]??null}s(me,"brokerCommandEpoch");const pn=5e3,He=150;async function fn(e){const l=Ce();if(l===null)throw m({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const u=hr(),f=[];for(const[n,d]of l.processes){const y=d.command;if(d.zombie||!y.includes("broker-cli.js")||!de(y,u)||me(y)===e)continue;const b=y.trim().split(/\s+/).at(-1)??"";b===""||b.endsWith("broker-cli.js")||f.push({pid:n,nodeId:b})}let v=0;const a=[];for(const n of f){const d=Pt(n.pid);if(d===null||!d.includes("broker-cli.js")||!de(d,u)||me(d)===e||d.trim().split(/\s+/).at(-1)!==n.nodeId)continue;const y=Le(n.pid,null);if(!y.reused){try{process.platform==="linux"&&k(T)?await $({operation:"stop",pid:n.pid}):process.kill(n.pid,"SIGTERM")}catch{continue}v++,a.push({pid:n.pid,snap:y})}}const h=Date.now()+pn;for(;Date.now()<h&&a.some(n=>$e(n.snap.tree.length>0?n.snap.tree:[n.pid]));)await new Promise(n=>setTimeout(n,He));let g=0;for(const n of a){const d=n.snap.tree.length>0?n.snap.tree:[n.pid];$e(d)&&(process.platform==="linux"&&k(T)?await $({operation:"stop",pid:n.pid}):Ae(d,"SIGKILL",n.snap.identities),g++)}g>0&&await new Promise(n=>setTimeout(n,He));const p=Ce();if(p===null)throw m({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const c=[];for(const[n,d]of p.processes){const y=d.command;d.zombie||!y.includes("broker-cli.js")||de(y,u)&&me(y)!==e&&c.push(n)}if(c.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${c.join(", ")}`);m({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:f.length,sigtermed:v,sigkilled:g}})}s(fn,"reapPriorEpochBrokers");const un=5e3;async function hn(e){const l=Array.from(e.entries());if(l.length===0)return;for(const[a]of l){const h=(await Rr(a))?.status;(h==="active"||h==="idle")&&await xe(a,new Date().toISOString())}const u=l.map(([,a])=>({pid:a.pid,snap:Le(a.pid,null),exited:a.handle.exited}));for(const a of u)if(!a.snap.reused)try{process.platform==="linux"&&k(T)?await $({operation:"stop",pid:a.pid}):process.kill(a.pid,"SIGTERM")}catch{}const f=new Promise(a=>{setTimeout(a,un).unref()});await Promise.allSettled(u.map(a=>Promise.race([a.exited.then(()=>{}),f])));const v=[];for(const a of u)Et(a.pid)&&(process.platform==="linux"&&k(T)?await $({operation:"stop",pid:a.pid}):Ae(a.snap.tree.length>0?a.snap.tree:[a.pid],"SIGKILL",a.snap.identities),v.push(a));await Promise.all(v.map(a=>a.exited));for(const[a]of l)await st(a,{reason:"daemon-teardown"});m({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:u.length,sigkilled:v.length}})}s(hn,"teardownFleet");function je(){ye(lr(ce()),{recursive:!0}),ar(ce(),String(process.pid),"utf8")}s(je,"writePidfile");function ze(e){return typeof e=="object"&&e!==null&&e.code==="ENOENT"}s(ze,"isMissingPathError");function Ue(e){const l=ce();let u;try{u=sr(l,"utf8")}catch(f){if(ze(f))return;throw f}if(u.trim()!==String(e))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${e})`);try{cr(l)}catch(f){if(ze(f))return;throw f}}s(Ue,"removeOwnedPidfileMirror");const wn={acceptsDetachedWork:s(()=>!0,"acceptsDetachedWork"),registerDetached:s(e=>{e.catch(()=>{})},"registerDetached")},K=new et;Zr(e=>K.handleFatalFault(e,!1));const vn=new rt,gn=new tt,Sn=new nt,yn=new ot,_n=new it,bn=new at,kn=new dt,Rn=new mt,Pn=new pt,En=new ft,In=new ht,xn=new wt,Tn=new vt,Dn=new gt,Cn=new St,Ln=new yt,$n=new _t,An=new bt;async function Nn(e=Date.now(),l=wn){const u=xt(),f=await K.runFleetPreamble(u);let v,a;try{const g=await Ie();v=g.filter(c=>c.status==="active"||c.status==="idle"),a=g.filter(c=>c.status==="done"||c.status==="canceled"||c.status==="dead");const p=new Set(a.map(c=>c.node_id));a.push(...(await Pr()).filter(c=>!p.has(c.node_id)))}catch(g){m({level:"error",event:"canvas.node_list.failed",error:g});return}await K.run(e,{rows:v,fleet:u,lifecycle:l}),await vn.run(e,{rows:v});const h=await gn.run(e,{rows:[...v,...a],fleet:u});await Sn.run(e),await $n.run(e),await An.run(e,{lifecycle:l}),await yn.run(e,{lifecycle:l}),await _n.run(e,{lifecycle:l}),await bn.run(e,{lifecycle:l}),await kn.run(e,{lifecycle:l}),await Rn.run(e,{lifecycle:l,fleet:u}),Pn.run(e,{lifecycle:l}),En.run(e,{lifecycle:l}),In.run(e,{lifecycle:l}),xn.run(e,{lifecycle:l}),Tn.run(e,{lifecycle:l}),Dn.run(e,{lifecycle:l}),await ur(),Ln.run(e,{fleet:u,lifecycle:l}),await Cn.run(e,{rows:v}),K.warnIfSaturated({fleet:u,lifecycle:l,thresholds:f,frozen:h})}s(Nn,"superviseTick");function V(e){return e===void 0||e==="reconcile"}s(V,"roleRunsLanes");function A(e){return e===void 0||e==="api"}s(A,"roleServesRequests");const Mn=66e4;function Gn(e){const l=new Ir().registerAll(xr(()=>null).filter(n=>n.pattern==="/healthz")),u=s((n,d)=>{const y=(n.url??"/").split("?")[0];if((n.method??"GET").toUpperCase()==="GET"&&y==="/healthz"){l.handle(n,d,null,{class:"person"});return}d.writeHead(404,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({error:{code:"not_found",message:"this process serves only /healthz",origin:"runtime"}}))},"handler"),f=Pe();k(f)&&_e(f);const v=J(u),a=new Promise((n,d)=>{v.once("error",d),v.listen(f,()=>{v.off("error",d),be(f,384),n()})});let h,g=null;const p=e.tcp??ke()??se("user").api.tcp;if(p!==void 0&&p!==""){const n=/^(.*):(\d+)$/.exec(p.trim());if(n===null||Number(n[2])>65535)throw new Error(`invalid reconcile probe TCP address: ${p}`);const d=J(u);d.on("error",y=>m({event:"api.server.failed",level:"error",error:y})),d.listen(Number(n[2]),n[1]===""?"0.0.0.0":n[1],()=>{g=`${n[1]===""?"0.0.0.0":n[1]}:${n[2]}`}),h=d}let c=!1;return{ready:a,tcpAddress:s(()=>g,"tcpAddress"),async close(n={}){if(!c){c=!0,await Promise.all([Te(v,new Set,n.graceMs),h===void 0?Promise.resolve():Te(h,new Set,n.graceMs)]);try{k(f)&&_e(f)}catch{}}}}}s(Gn,"createProbeServer");const On="daemon is draining for shutdown and admits no new work";async function Fn(e){ye(Ee(),{recursive:!0,mode:493}),process.platform==="linux"&&be(Ee(),493);const l=Pe();if(k(l))throw new Error(`layout refusal cannot bind occupied socket: ${l}`);const u=J((p,c)=>{if(p.method==="GET"&&p.url==="/healthz"){c.writeHead(200,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}c.writeHead(503,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((p,c)=>{u.once("error",c),u.listen(l,()=>{u.off("error",c),p()})});let f;const v=e.tcp??ke();if(v){const p=/^(.*):(\d+)$/.exec(v);if(!p||Number(p[2])>65535)throw new Error(`invalid refusal TCP address: ${v}`);const c=J(u.listeners("request")[0]);await new Promise((n,d)=>{c.once("error",d),c.listen(Number(p[2]),p[1],()=>{c.off("error",d),n()})}),f=c}je();let a;const h=s(()=>(a??=(async()=>{u.closeAllConnections(),f?.closeAllConnections(),await Promise.all([u,f].filter(p=>p!==void 0).map(p=>new Promise((c,n)=>p.close(d=>d?n(d):c())))),Ue(process.pid),process.off("SIGINT",g),process.off("SIGTERM",g)})(),a),"cleanup"),g=s(()=>{h().then(()=>process.exit(0),p=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(p)}
2
+ `),process.exit(1)})},"onSignal");return process.on("SIGINT",g),process.on("SIGTERM",g),h}s(Fn,"serveLayoutRefusal");async function Bn(e={}){if(e.role==="machine")return(await import("./machine-daemon.js")).runMachineDaemon();if(_r())return Fn(e);yr();const l=s(t=>{process.stderr.write(`[crtrd] ${t}
3
+ `)},"logPostgres"),u=!!process.env.CRTR_TEST_PG_URL,f=Gt(e.role);Wt(V(e.role));const v=s(async()=>f?await Mt(Re(16).toString("hex")):{claimed:!0,claim:{pid:process.pid,pid_identity:"",token:Re(16).toString("hex")}},"claimOwnership"),{database:a,ownership:h}=u?{database:{url:await Ft()},ownership:await v()}:await Ut(async()=>({database:await Ht({log:l,startLockHeld:!0}),ownership:await v()}));if(!h.claimed){if(h.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${h.incumbentPid??"?"})
4
+ `);else{const t=[h.incumbentPid!==void 0?`pid ${h.incumbentPid}`:void 0,h.verdict!==void 0?`verdict ${h.verdict}`:void 0].filter(w=>w!==void 0),o=t.length>0?` (${t.join(", ")})`:"";process.stderr.write(`[crtrd] claim lost: ${h.reason}${o}
5
+ `),h.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(h.reason==="own_identity_indeterminate"||h.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const g=h.claim;let p=null,c=null,n=null,d,y="";try{f&&await fn(g.token);for(const r of kr())process.stderr.write(`[crtrd] ${r}
6
+ `);for(const r of await br())process.stderr.write(`[crtrd] ${r}
7
+ `);await Me(),await Bt(a.url,l);const t=e.role===void 0&&!await ln();let o=null;t&&(cn({kind:"user",root:Be()}),o=dn(Be())),e.role===void 0&&mn(o??{profiles:[],defaults:{},settings:{},state:{}}),await F.listen(a.url);const w=s(r=>{Fe(r).catch(i=>m({level:"error",event:"daemon.settings.reload_failed",error:i}))},"reloadPerson");if(d=await F.subscribe(r=>{Oe().includes(r)&&w(r)},{kinds:["settings"],onListenStart:s(()=>{for(const r of Oe())w(r)},"onListenStart")}),e.role!==void 0&&Ar(),e.role!==void 0){let r;try{r=Vt()}catch(P){m({level:"error",event:"daemon.packages.snapshot_failed",error:P})}const i=await Ge(async P=>(await De(P),r===void 0?void 0:await Kt(P,r,"release")));for(const P of i?.invalid??[])process.stderr.write(`[crtrd] package file is not a document: ${P.path}: ${P.error}
8
+ `)}else{const r=await Ge(async i=>(await De(i),await Jt(i)));for(const i of r.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${i.message}
9
+ `);for(const i of r.invalid)process.stderr.write(`[crtrd] package file is not a document: ${i.path}: ${i.error}
10
+ `);for(const i of r.quarantined)process.stderr.write(`[crtrd] plugin "${i.name}" has a damaged ${i.file} (${i.reason}); ${i.movedTo!==void 0?`moved to ${i.movedTo}`:"left in place and skipped"}
11
+ `);for(const i of r.kinds.refused)process.stderr.write(`[crtrd] plugin "${i.plugin}" kind ${i.kind} not registered: ${i.message}
12
+ `);for(const i of r.kinds.invalid)process.stderr.write(`[crtrd] plugin "${i.plugin}" declares invalid object kinds or scopes; no kinds registered and no app token: ${i.error}
13
+ `)}if(y=await At(),Nt(y),e.role===void 0&&(p=await Nr(),p!==null)){if(Gr(p.id),await sn(p.id),process.platform==="darwin"){const{readConfig:r,updateConfig:i}=await import("../core/config.js");if(r("user").paths?.user_files===void 0){const{homedir:P}=await import("node:os");await i("user",Se=>{Se.paths??={},Se.paths.user_files??=P()})}}c=await Mr(),c.joinedFrom!==null&&await Fe(c.person),await tr(),n=await Or()}if(process.platform==="linux"&&k(T)&&(Sr(),await $({operation:"users.sync",users:await wr()})),f){await lt(g.token);const r=await en();Object.values(r).some(i=>i>0)&&process.stderr.write(`[crtrd] node files copied into rows: ${JSON.stringify(r)}
14
+ `)}}catch(t){await F.close();try{if(f){const{released:o}=await Ne(g);if(!o)throw new Error("daemon ownership claim was not released")}await le()}catch(o){throw new AggregateError([t,o],"daemon startup failed and cleanup also failed")}throw t}let b=!0,Y,B=Promise.resolve(),Q=!1,H,X,Z,D;const j=new Set;let ee=!1,pe=!1,E,C,re,z=!1,U,N,M,G,te,ne,oe;const qe=Xr();let I,S={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},L="initial startup activation",We=null;function Je(){return S.phase==="ready"?null:{phase:S.phase,message:S.message,blockerPaths:S.blockerPaths}}s(Je,"startupBlock");function R(t){j.add(t),t.finally(()=>{j.delete(t)}).catch(()=>{})}s(R,"registerDetached");const fe={acceptsDetachedWork:s(()=>b,"acceptsDetachedWork"),registerDetached:R};V(e.role)&&await Yt(fe,Me);async function Ke(){try{b=!1,qt.close(),X?.(),X=void 0,Z?.(),d?.(),Z=void 0,ne?.(),ne=void 0,await F.close(),H!==void 0&&(clearInterval(H),H=void 0),M!==void 0&&(clearInterval(M),M=void 0),oe?.(),oe=void 0;let t;do t=B,await t;while(t!==B);for(;j.size>0;)await Promise.allSettled([...j]);if(D!==void 0&&(f&&await hn(D),await D.exitsSettled(),D.dispose()),re!==void 0&&await re.close(),C!==void 0&&!z&&(z=!0,await C.close()),te?.(),te=void 0,G!==void 0){const o=G;G=void 0,await o.stop()}if(N!==void 0){const o=N;N=void 0,await o.close()}if(U!==void 0){const o=U;U=void 0,await o.close()}if(ee||(Ue(process.pid),ee=!0),ee&&!pe){if(f){const{released:o}=await Ne(g);if(!o)throw new Error("daemon ownership claim was not released")}pe=!0,await le()}}finally{Dt(),process.off("SIGINT",we),process.off("SIGTERM",ve),process.off("exit",ge)}}s(Ke,"cleanupAttempt");const q=1e3;let ue=!1;function Ve(){return ue?{graceMs:q}:(ue=!0,_.fresh(()=>{m({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:q}})}),setTimeout(()=>{Ye()},q),{graceMs:q})}s(Ve,"requestHandover");async function Ye(){try{await x()}catch(t){const o=t instanceof Error?t.stack??t.message:String(t);process.stderr.write(`[crtrd] handover cleanup failed:
14
15
  ${o}
15
- `),process.exit(1)}try{process.platform==="linux"&&k(x)&&(await L({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await Ct();const r=await Lt({waitForJoinLock:!0});_.fresh(()=>{p({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:r.pid??r.existing_pid??null,spawned:r.started}})})}catch(r){_.fresh(()=>{p({level:"error",event:"daemon.handover.successor_failed",error:r})});const o=r instanceof Error?r.message:String(r);process.stderr.write(`[crtrd] handover successor failed to start: ${o}
16
- `),process.exit(1)}process.exit(0)}s(Ke,"performHandover");function I(){if(P!==void 0)return P;const r=We();return P=r,r.then(()=>{P===r&&(P=void 0)},()=>{P===r&&(P=void 0)}),r}s(I,"requestCleanup");async function Ve(){S={phase:"initializing",message:Gn,blockerPaths:[]},D!==void 0&&!z&&(z=!0,await D.close({graceMs:Nn}))}s(Ve,"drainApi");async function we(r){try{r==="SIGTERM"&&e.role==="api"&&await Ve(),await I(),await Ut({claimed:s(async()=>{try{return f&&await Ot()}finally{await le()}},"claimed")}),process.exit(0)}catch(o){process.exitCode=1;const h=o instanceof Error?o.stack??`${o.name}: ${o.message}`:String(o);process.stderr.write(`[crtrd] ${r} cleanup failed:
17
- ${h}
18
- `),process.exit(1)}}s(we,"handleSignal");function he(){we("SIGINT")}s(he,"onSigint");function ve(){we("SIGTERM")}s(ve,"onSigterm");function ge(){I().catch(()=>{})}s(ge,"onExit");async function ie(r){b&&(B=B.then(r).then(()=>{},o=>{_.fresh(()=>{p({level:"error",event:"daemon.lane_job.failed",error:o})})}))}s(ie,"enqueue");async function W(){!b||Q||(ze.check()&&E&&(p({level:"info",event:"daemon.resume.detected"}),R(E().catch(()=>{}))),Q=!0,await ie(async()=>{if(Q=!1,!!b)try{u||await zt({log:l}),await An(Date.now(),fe)}catch(r){_.fresh(()=>{p({level:"error",event:"daemon.tick.failed",error:r})})}}))}s(W,"enqueueTick");let G=null;async function Ye(){if(G!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${G instanceof Error?G.message:String(G)}`};if(S.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(S.phase!=="prepared"&&S.phase!=="recovering")throw{status:409,code:"not_prepared",message:S.message};const r=S.phase==="prepared";try{return await ae(),S={phase:"ready",message:"",blockerPaths:[]},r?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(o){throw G=o,{status:500,code:"admission_failed",message:`daemon admission failed: ${o instanceof Error?o.message:String(o)}`}}}s(Ye,"admitStartup");async function Qe(r){if(C!==null)throw{status:409,code:"migration_in_progress",message:`the canvas import is already running (${C})`};C="repair";try{const o=await on({dryRun:r.dryRun});if(r.dryRun||o.pending||S.phase!=="blocked")return{result:o,resumed:!1};if(e.prepare)return S={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:o,resumed:!1};try{return await ae(),S={phase:"ready",message:"",blockerPaths:[]},{result:o,resumed:!0}}catch(h){const i=h instanceof Error?h.message:String(h);return setTimeout(()=>{I().catch(()=>{})},1e3),{result:o,resumed:!1,resumeError:i}}}finally{C=null}}s(Qe,"repairMigrations");async function Xe(){Y=e.intervalMs??Qr;const r=g.token;T=new Xt({epoch:r,enqueue:ie}),It(T),S={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{p({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:Y}})});const o=y;_.fresh(()=>{p({level:"info",event:"daemon.install_id.resolved",fields:{install_id:o}})});const h=await Cr(o);if(_.fresh(()=>{p({level:"info",event:"daemon.environment.registered",fields:{environment_id:h.environmentId,created:h.created}})}),_.fresh(()=>{let i;try{i=Yr()}catch{return}i!==null&&p({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:i.slice(0,2048)}})}),ut(),e.role!==void 0&&!process.env.CRTR_DIRECTORY_URL)throw new Error("CRTR_DIRECTORY_URL must be set on a cloud role (--role api|reconcile): it names the directory this deployment mints tokens against");if(e.role!==void 0&&Lr(),!V(e.role)){S={phase:"ready",message:"",blockerPaths:[]};return}if(await _.fresh(async()=>{await Vr(),await Wr(),await Jr(Date.now()),await Kr(Date.now())}),await _.fresh(async()=>{const{reconcile:i,migrated:t}=await $t(ct);i.changed&&p({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:i.cleared.length,same_boot_proven:i.sameBootProven}}),t.length>0&&p({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:t.length}})}),await _.fresh(async()=>{for(const i of await Ie())i.status!=="active"||i.pi_pid!=null||i.frozen_at!=null||i.respawn_not_before!=null||(await xe(i.node_id,new Date().toISOString()),p({level:"warn",event:"daemon.startup.launch_debt_recorded",node_id:i.node_id}))}),await _.fresh(async()=>{await Rt();for(const i of en){const t=await i.apply();t.changed&&p({level:"info",event:"daemon.boot_migration.applied",fields:{migration:i.id,...t.summary}})}}),R(Ur()),E){const i=s(()=>{R(E().catch(()=>{})),R(nr().then(t=>M?.observe(t)).then(()=>{},t=>{p({level:"warn",event:"daemon.peer.status_failed",error:t})}))},"refresh");R(zr().then(({completion:t})=>t).catch(t=>{p({level:"error",event:"daemon.grants.sweep_failed",error:t})})),i(),N=setInterval(i,jr*1e3),N.unref(),oe=tr({joined:!0,register:R})}await ie(async()=>{await Qt(),await Yt()}),H=setInterval(()=>{W().catch(i=>p({level:"error",event:"daemon.tick.failed",error:i}))},Y),X=await kt(()=>{W().catch(i=>p({level:"error",event:"daemon.tick.failed",error:i}))}),Z=await F.subscribe(()=>{W().catch(i=>p({level:"error",event:"daemon.tick.failed",error:i}))},{kinds:["spend_limits"]}),await W(),e.role===void 0&&import("../commands/sys/import/move.js").then(({settleMoveAtDaemonStart:i})=>i(()=>!b,t=>p({level:"warn",event:"daemon.move.settle_failed",error:t}))).catch(i=>p({level:"warn",event:"daemon.move.settle_failed",error:i}))}s(Xe,"completeStartup");function ae(){return Ue??=Xe().catch(r=>{throw S={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},r})}s(ae,"resumeStartup");try{process.on("SIGINT",he),process.on("SIGTERM",ve),process.on("exit",ge),rn(),m!==null&&p({level:"info",event:"daemon.person.resolved",fields:{person_id:m.id,source:m.source}}),n!==null&&p({level:"info",event:"daemon.permissions.boot",fields:{...n}}),c!==null&&p({level:"info",event:"daemon.person.retired",fields:{person_id:c.person,joined_from:c.joinedFrom,retired:c.retired,columns:c.columns}}),$(e.role)?U=await mr():pr(dr());const r=g.token;Tt({epoch:r,requestHandover:Je,registerDetached:R,startupBlock:qe,admitStartup:Ye,repairMigrations:Qe}),Be(),D=$(e.role)?Pr({tcp:e.tcp}):Mn(e),await D.ready;const o=lr()??se("user").document_port;if($(e.role)){const i=xr(o,Tr());try{await i.ready,re=i}catch(t){p({level:"warn",event:"daemon.document_listener.bind_failed",fields:{port:o,error:t instanceof Error?t.message:String(t)}})}}const h=se("user").runtime;if(h?.issuer&&(E=Hr(h.issuer,R),h.unsandboxed===!0&&(M=or({syncGrants:E}))),$(e.role)&&(te=await Fr()),ne=await Br({serves:$(e.role),runsLanes:V(e.role)}),h?.unsandboxed===!0&&h.listener!==void 0&&p({level:"warn",event:"daemon.app_listener.refused_unsandboxed",fields:{reason:"runtime.unsandboxed is set; runtime.listener is ignored"}}),$(e.role)&&h!==void 0&&h.listener!==void 0&&h.unsandboxed!==!0&&(A=await Dr(h,async()=>await qr(h.issuer.replace(/\/$/,"")),E),await A.ready),e.role===void 0)try{await tn()}catch(i){if(C=null,i instanceof nn)return S={phase:"blocked",message:i.message,blockerPaths:i.blockerPaths},I;throw i}process.platform==="linux"&&k(x)&&(vr(),await hr("app:terminal"));try{e.prepare?S={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await ae(),S={phase:"ready",message:"",blockerPaths:[]})}finally{C=null}return I}catch(r){try{await I()}catch(o){throw new AggregateError([r,o],"daemon startup failed and cleanup also failed")}throw r}}s(Fn,"runDaemon");var Ni=Fn;export{Nn as API_DRAIN_GRACE_MS,Oi as brokerCommandDeclaresCanvasHome,me as brokerCommandEpoch,Ni as default,V as roleRunsLanes,$ as roleServesRequests,Fn as runDaemon,An as superviseTick,un as teardownFleet};
16
+ `),process.exit(1)}try{process.platform==="linux"&&k(T)&&(await $({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await Ct();const t=await Lt({waitForJoinLock:!0});_.fresh(()=>{m({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:t.pid??t.existing_pid??null,spawned:t.started}})})}catch(t){_.fresh(()=>{m({level:"error",event:"daemon.handover.successor_failed",error:t})});const o=t instanceof Error?t.message:String(t);process.stderr.write(`[crtrd] handover successor failed to start: ${o}
17
+ `),process.exit(1)}process.exit(0)}s(Ye,"performHandover");function x(){if(E!==void 0)return E;const t=Ke();return E=t,t.then(()=>{E===t&&(E=void 0)},()=>{E===t&&(E=void 0)}),t}s(x,"requestCleanup");async function Qe(){S={phase:"initializing",message:On,blockerPaths:[]},C!==void 0&&!z&&(z=!0,await C.close({graceMs:Mn}))}s(Qe,"drainApi");async function he(t){try{t==="SIGTERM"&&e.role==="api"&&await Qe(),await x(),await zt({claimed:s(async()=>{try{return f&&await Ot()}finally{await le()}},"claimed")}),process.exit(0)}catch(o){process.exitCode=1;const w=o instanceof Error?o.stack??`${o.name}: ${o.message}`:String(o);process.stderr.write(`[crtrd] ${t} cleanup failed:
18
+ ${w}
19
+ `),process.exit(1)}}s(he,"handleSignal");function we(){he("SIGINT")}s(we,"onSigint");function ve(){he("SIGTERM")}s(ve,"onSigterm");function ge(){x().catch(()=>{})}s(ge,"onExit");async function ie(t){b&&(B=B.then(t).then(()=>{},o=>{_.fresh(()=>{m({level:"error",event:"daemon.lane_job.failed",error:o})})}))}s(ie,"enqueue");async function W(){!b||Q||(qe.check()&&I&&(m({level:"info",event:"daemon.resume.detected"}),R(I().catch(()=>{}))),Q=!0,await ie(async()=>{if(Q=!1,!!b)try{u||await jt({log:l}),await Nn(Date.now(),fe)}catch(t){_.fresh(()=>{m({level:"error",event:"daemon.tick.failed",error:t})})}}))}s(W,"enqueueTick");let O=null;async function Xe(){if(O!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${O instanceof Error?O.message:String(O)}`};if(S.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(S.phase!=="prepared"&&S.phase!=="recovering")throw{status:409,code:"not_prepared",message:S.message};const t=S.phase==="prepared";try{return await ae(),S={phase:"ready",message:"",blockerPaths:[]},t?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(o){throw O=o,{status:500,code:"admission_failed",message:`daemon admission failed: ${o instanceof Error?o.message:String(o)}`}}}s(Xe,"admitStartup");async function Ze(t){if(L!==null)throw{status:409,code:"migration_in_progress",message:`the canvas import is already running (${L})`};L="repair";try{const o=await an({dryRun:t.dryRun});if(t.dryRun||o.pending||S.phase!=="blocked")return{result:o,resumed:!1};if(e.prepare)return S={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:o,resumed:!1};try{return await ae(),S={phase:"ready",message:"",blockerPaths:[]},{result:o,resumed:!0}}catch(w){const r=w instanceof Error?w.message:String(w);return setTimeout(()=>{x().catch(()=>{})},1e3),{result:o,resumed:!1,resumeError:r}}}finally{L=null}}s(Ze,"repairMigrations");async function er(){Y=e.intervalMs??Qr;const t=g.token;D=new Zt({epoch:t,enqueue:ie}),It(D),S={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{m({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:Y}})});const o=y;_.fresh(()=>{m({level:"info",event:"daemon.install_id.resolved",fields:{install_id:o}})});const w=await Lr(o);if(_.fresh(()=>{m({level:"info",event:"daemon.environment.registered",fields:{environment_id:w.environmentId,created:w.created}})}),_.fresh(()=>{let r;try{r=Yr()}catch{return}r!==null&&m({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:r.slice(0,2048)}})}),ut(),e.role!==void 0&&!process.env.CRTR_DIRECTORY_URL)throw new Error("CRTR_DIRECTORY_URL must be set on a cloud role (--role api|reconcile): it names the directory this deployment mints tokens against");if(e.role!==void 0&&$r(),!V(e.role)){S={phase:"ready",message:"",blockerPaths:[]};return}if(await _.fresh(async()=>{await Vr(),await Wr(),await Jr(Date.now()),await Kr(Date.now())}),await _.fresh(async()=>{const{reconcile:r,migrated:i}=await $t(ct);r.changed&&m({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:r.cleared.length,same_boot_proven:r.sameBootProven}}),i.length>0&&m({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:i.length}})}),await _.fresh(async()=>{for(const r of await Ie())r.status!=="active"||r.pi_pid!=null||r.frozen_at!=null||r.respawn_not_before!=null||(await xe(r.node_id,new Date().toISOString()),m({level:"warn",event:"daemon.startup.launch_debt_recorded",node_id:r.node_id}))}),await _.fresh(async()=>{await Rt();for(const r of rn){const i=await r.apply();i.changed&&m({level:"info",event:"daemon.boot_migration.applied",fields:{migration:r.id,...i.summary}})}}),R(Ur()),I){const r=s(()=>{R(I().catch(()=>{})),R(or().then(i=>G?.observe(i)).then(()=>{},i=>{m({level:"warn",event:"daemon.peer.status_failed",error:i})}))},"refresh");R(zr().then(({completion:i})=>i).catch(i=>{m({level:"error",event:"daemon.grants.sweep_failed",error:i})})),r(),M=setInterval(r,jr*1e3),M.unref(),oe=nr({joined:!0,register:R})}await ie(async()=>{await Xt(),await Qt()}),H=setInterval(()=>{W().catch(r=>m({level:"error",event:"daemon.tick.failed",error:r}))},Y),X=await kt(()=>{W().catch(r=>m({level:"error",event:"daemon.tick.failed",error:r}))}),Z=await F.subscribe(()=>{W().catch(r=>m({level:"error",event:"daemon.tick.failed",error:r}))},{kinds:["spend_limits"]}),await W(),e.role===void 0&&import("../commands/sys/import/move.js").then(({settleMoveAtDaemonStart:r})=>r(()=>!b,i=>m({level:"warn",event:"daemon.move.settle_failed",error:i}))).catch(r=>m({level:"warn",event:"daemon.move.settle_failed",error:r}))}s(er,"completeStartup");function ae(){return We??=er().catch(t=>{throw S={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},t})}s(ae,"resumeStartup");try{process.on("SIGINT",we),process.on("SIGTERM",ve),process.on("exit",ge),tn(),p!==null&&m({level:"info",event:"daemon.person.resolved",fields:{person_id:p.id,source:p.source}}),n!==null&&m({level:"info",event:"daemon.permissions.boot",fields:{...n}}),c!==null&&m({level:"info",event:"daemon.person.retired",fields:{person_id:c.person,joined_from:c.joinedFrom,retired:c.retired,columns:c.columns}}),A(e.role)?U=await pr():fr(mr());const t=g.token;Tt({epoch:t,requestHandover:Ve,registerDetached:R,startupBlock:Je,admitStartup:Xe,repairMigrations:Ze}),je(),C=A(e.role)?Er({tcp:e.tcp}):Gn(e),await C.ready;const o=dr()??se("user").document_port;if(A(e.role)){const r=Tr(o,Dr());try{await r.ready,re=r}catch(i){m({level:"warn",event:"daemon.document_listener.bind_failed",fields:{port:o,error:i instanceof Error?i.message:String(i)}})}}const w=se("user").runtime;if(w?.issuer&&(I=Hr(w.issuer,R),w.unsandboxed===!0&&(G=ir({syncGrants:I}))),A(e.role)&&(te=await Fr()),ne=await Br({serves:A(e.role),runsLanes:V(e.role)}),w?.unsandboxed===!0&&w.listener!==void 0&&m({level:"warn",event:"daemon.app_listener.refused_unsandboxed",fields:{reason:"runtime.unsandboxed is set; runtime.listener is ignored"}}),A(e.role)&&w!==void 0&&w.listener!==void 0&&w.unsandboxed!==!0&&(N=await Cr(w,async()=>await qr(w.issuer.replace(/\/$/,"")),I),await N.ready),e.role===void 0)try{await nn()}catch(r){if(L=null,r instanceof on)return S={phase:"blocked",message:r.message,blockerPaths:r.blockerPaths},x;throw r}process.platform==="linux"&&k(T)&&(gr(),await vr("app:terminal"));try{e.prepare?S={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await ae(),S={phase:"ready",message:"",blockerPaths:[]})}finally{L=null}return x}catch(t){try{await x()}catch(o){throw new AggregateError([t,o],"daemon startup failed and cleanup also failed")}throw t}}s(Bn,"runDaemon");var Gi=Bn;export{Mn as API_DRAIN_GRACE_MS,Bi as brokerCommandDeclaresCanvasHome,me as brokerCommandEpoch,Gi as default,V as roleRunsLanes,A as roleServesRequests,Bn as runDaemon,Nn as superviseTick,hn as teardownFleet};
@@ -7,7 +7,7 @@ description: Behavior changes in @north-light/sdk that an app must act on, newes
7
7
 
8
8
  Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
9
9
 
10
- ## 0.3.584
10
+ ## 0.3.586
11
11
 
12
12
  `GET /v1/nodes/{id}/jobs/{jobId}/output` reads a background bash job's stored output, last 256 KiB by default (`max_bytes` up to 4 MiB, or `tail_lines`), from the blob store, so it works after the job's sandbox is deleted. It answers `job_not_found` for a job the node does not own and an empty `output` for a job that has written nothing. `CrtrClient.readBashJobOutput` calls it. Nothing existing changes.
13
13
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.585",
3
+ "version": "0.3.587",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -145,7 +145,7 @@
145
145
  },
146
146
  "license": "UNLICENSED",
147
147
  "dependencies": {
148
- "@north-light/identity": "0.3.585",
148
+ "@north-light/identity": "0.3.587",
149
149
  "@earendil-works/chord": "1.1.0",
150
150
  "@earendil-works/pi-agent-core": "1.1.0",
151
151
  "@earendil-works/pi-ai": "1.1.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/identity",
3
- "version": "0.3.585",
3
+ "version": "0.3.587",
4
4
  "description": "Northlight access-token verification and scope rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/runtime.lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.585",
3
+ "version": "0.3.587",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/cli",
9
- "version": "0.3.585",
9
+ "version": "0.3.587",
10
10
  "hasInstallScript": true,
11
11
  "license": "UNLICENSED",
12
12
  "workspaces": [
@@ -28,7 +28,7 @@
28
28
  "@milkdown/preset-gfm": "^7.22.3",
29
29
  "@milkdown/prose": "^7.22.3",
30
30
  "@milkdown/utils": "^7.22.3",
31
- "@north-light/identity": "0.3.585",
31
+ "@north-light/identity": "0.3.587",
32
32
  "cron-parser": "^5.6.0",
33
33
  "esbuild": "^0.27.7",
34
34
  "jose": "^6.2.12",
@@ -11956,17 +11956,17 @@
11956
11956
  },
11957
11957
  "packages/crouter-api": {
11958
11958
  "name": "@north-light/api",
11959
- "version": "0.3.585",
11959
+ "version": "0.3.587",
11960
11960
  "license": "UNLICENSED"
11961
11961
  },
11962
11962
  "packages/crouter-env-docker": {
11963
11963
  "name": "@north-light/env-docker",
11964
- "version": "0.3.585",
11964
+ "version": "0.3.587",
11965
11965
  "license": "UNLICENSED"
11966
11966
  },
11967
11967
  "packages/crouter-identity": {
11968
11968
  "name": "@north-light/identity",
11969
- "version": "0.3.585",
11969
+ "version": "0.3.587",
11970
11970
  "license": "UNLICENSED",
11971
11971
  "dependencies": {
11972
11972
  "jose": "^6.2.1"
@@ -11974,21 +11974,21 @@
11974
11974
  },
11975
11975
  "packages/crouter-plugin": {
11976
11976
  "name": "@north-light/plugin",
11977
- "version": "0.3.585",
11977
+ "version": "0.3.587",
11978
11978
  "license": "UNLICENSED",
11979
11979
  "dependencies": {
11980
- "@north-light/api": "0.3.585",
11981
- "@north-light/identity": "0.3.585"
11980
+ "@north-light/api": "0.3.587",
11981
+ "@north-light/identity": "0.3.587"
11982
11982
  }
11983
11983
  },
11984
11984
  "packages/crouter-sdk": {
11985
11985
  "name": "@north-light/sdk",
11986
- "version": "0.3.585",
11986
+ "version": "0.3.587",
11987
11987
  "license": "UNLICENSED",
11988
11988
  "dependencies": {
11989
- "@north-light/api": "0.3.585",
11990
- "@north-light/identity": "0.3.585",
11991
- "@north-light/plugin": "0.3.585",
11989
+ "@north-light/api": "0.3.587",
11990
+ "@north-light/identity": "0.3.587",
11991
+ "@north-light/plugin": "0.3.587",
11992
11992
  "jose": "^6.2.1"
11993
11993
  },
11994
11994
  "bin": {