@north-light/cli 0.3.455 → 0.3.456
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clients/attach/viewer.js +526 -526
- package/dist/clients/inbox/controller.js +1 -1
- package/dist/clients/inbox/tui/page-body.js +1 -1
- package/dist/clients/inbox/tui/render.js +2 -2
- package/docs/ci-runner.md +25 -11
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
- package/scripts/lib/isolation-env.mjs +15 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
var A=Object.defineProperty;var o=(d,e)=>A(d,"name",{value:e,configurable:!0});import{existsSync as P,watch as R}from"node:fs";import{visibleWidth as F}from"@earendil-works/pi-tui";import{renderMarkdown as C}from"../../core/termrender/termrender.js";import{BOLD as M,CYAN as W,DIM as h,GRAY as q,RESET as a,YELLOW as D,clipLine as p}from"./tui/ansi.js";import{buildInboxLines as O,recommendedLabel as L}from"./tui.js";import{inboxLayout as g}from"./layout.js";import{resolvePageComponents as E}from"../../core/config.js";import{watchInboxActivity as N}from"../../core/human/root.js";import{claimTicket as k,heartbeatClaim as H,releaseClaim as u}from"../../core/human/claim.js";import{claimPath as T,clearProgress as B}from"../../core/human/convention.js";import{emitEvent as _}from"../../core/events/emit.js";import{cliClient as f}from"../../commands/api-client.js";import{projectPageDisplayMarkdown as z}from"../../core/human/page-markdown.js";import{projectHtmlDocumentMarkdown as V}from"../../core/human/page-html-markdown.js";import{pageBodyLines as j}from"./tui/page-body.js";import{byNewestFirst as K,inboxRowFromDTO as Y,pageViewFromDTO as b,settledStatusOf as x}from"./dto-adapt.js";import{PageAdapter as G}from"./page-adapter.js";import{ReviewAdapter as X}from"./review-adapter.js";import{createReviewClient as J}from"./review/review-client.js";import{editBufferInEditor as Q}from"./review/roundtrip.js";import{daemonErrorMessage as m,dismissTicketViaDaemon as U,resolveFailureKind as $,resolveTicketPage as I}from"./resolve.js";class ke{static{o(this,"InboxController")}options;host;scan;items=[];visible=[];profile;filter;pendingSeat;selectedDir;selectedIndex=0;screen="list";adapter;reviewAdapter;focusingSource=!1;claim;submittingDir;cancelConfirmation;cols;rows;status;refreshError;watchers=[];selectedWatcher;heartbeat;started=!1;closed=!1;preview;previewFailed;previewFetching;previewStale=!1;openRevision;constructor(e){this.options=e,this.host=e.host,this.scan=e.scan??Z,this.cols=e.cols??80,this.rows=e.rows??24,this.profile=e.profile,this.filter=e.profile===void 0?"all":"profile",this.selectedDir=e.initialSelection,this.pendingSeat=e.initialSelection,this.rescan()}snapshot(){return{items:[...this.items],selectedDir:this.selectedDir,screen:this.screen,inputBuffer:this.adapter?.inputBuffer()}}async rescan(){const e=this.selectedDir,t=this.claim;try{this.items=await this.scan()}catch(s){if(this.closed)return;this.status=m(s),this.markDirty();return}if(this.closed)return;if(this.recomputeVisible(),this.pendingSeat!==void 0){const s=this.pendingSeat;this.pendingSeat=void 0,this.seatOn(s)}if(!(e!==void 0&&this.items.some(s=>s.dir===e))&&this.adapter!==void 0&&e!==void 0&&t!==void 0){const s=(await this.requestOrNull(t.id))?.state==="canceled";if(this.closed)return;s&&B(e),e!==this.submittingDir&&(this.status=s?"canceled by requester":"ticket resolved elsewhere"),this.leaveDetail(e!==this.submittingDir)}this.reseatSelection()}recomputeVisible(){if(this.filter==="all"||this.profile===void 0){this.visible=this.items;return}const e=this.profile.members();this.visible=this.items.filter(t=>{const i=t.source.nodeId;return i!==void 0&&e.has(i)})}reseatSelection(){const e=this.selectedDir===void 0?-1:this.visible.findIndex(t=>t.dir===this.selectedDir);if(e>=0){this.selectedIndex=e;return}if(!(this.adapter!==void 0&&this.selectedDir!==void 0&&this.items.some(t=>t.dir===this.selectedDir))){if(this.visible.length===0){this.selectedIndex=0,this.selectedDir=void 0;return}this.selectedIndex=Math.min(Math.max(0,this.selectedIndex),this.visible.length-1),this.selectedDir=this.visible[this.selectedIndex].dir}}highlightIndex(){return this.selectedDir===void 0?-1:this.visible.findIndex(e=>e.dir===this.selectedDir)}currentRow(){const e=this.highlightIndex();return e>=0?this.visible[e]:void 0}scopeHeading(){return this.profile===void 0?{label:"all profiles"}:this.filter==="profile"?{label:this.profile.label,hint:"P all profiles"}:{label:"all profiles",hint:`P ${this.profile.label} only`}}refreshOutcome(e){if(this.refreshError=e,e===void 0){this.invalidate();return}this.markDirty()}start(){this.started||this.closed||(this.started=!0,this.watchRoots(),this.heartbeat=setInterval(()=>{this.claim!==void 0&&H(this.claim.dir,this.claim.token)},1e4))}async invalidate(){if(this.previewStale=!0,await this.rescan(),!this.closed){if(this.adapter!==void 0&&this.claim!==void 0){await this.reloadSelectedPage();return}this.markDirty()}}resize(e,t){this.cols=e,this.rows=t,this.adapter?.resize(this.detailSize().cols,this.detailSize().rows),this.markDirty()}render(){const e=g(this.cols,this.rows,this.screen);if(e.mode==="minimum")return[`${D}Resize terminal to at least 60\xD718 to use inbox.${a}`];const t=O(this.visible,e.listWidth,this.highlightIndex(),this.scopeHeading(),e.height),i=this.detailLines(e.detailWidth,e.height);if(e.mode==="list")return this.withStatus(t);if(e.mode==="detail")return this.withStatus(i);const s=[],n=`${q}\u2502${a}`;for(let r=0;r<e.height;r++){const l=p(t[r]??"",e.listWidth),c=p(i[r]??"",e.detailWidth),v=" ".repeat(Math.max(0,e.listWidth-F(l)));s.push(`${l}${v}${n}${c}`)}return this.withStatus(s)}handleKey(e,t){if(t.ctrl&&e==="c"){this.close();return}if(this.screen==="detail"&&this.adapter!==void 0){if((e==="g"||e==="G")&&this.adapter.canAcceptHostKeys()){this.focusSelectedSource();return}this.adapter.handleKey(e,t),this.markDirty();return}if(this.cancelConfirmation!==void 0){if(e==="y"||e==="Y"){const i=this.cancelConfirmation.item;this.cancelConfirmation=void 0,this.status="canceling\u2026",this.cancelFromInbox(i)}else(e==="n"||e==="N"||t.escape)&&(this.cancelConfirmation=void 0,this.status=void 0);this.markDirty();return}if(e==="P"&&this.profile!==void 0){this.toggleProfileFilter(),this.markDirty();return}if(e==="x"||e==="X"){const i=this.currentRow();i!==void 0&&(this.cancelConfirmation={item:i},this.status=void 0),this.markDirty();return}if(e==="s"||e==="S"){this.sendFastAction(),this.markDirty();return}if(t.escape||e==="q"){this.close();return}e==="j"||t.downArrow?this.select(this.selectedIndex+1):e==="k"||t.upArrow?this.select(this.selectedIndex-1):e==="g"||e==="G"?this.focusSelectedSource():(t.return||e==="a")&&this.activate(),this.markDirty()}toggleProfileFilter(){this.filter=this.filter==="profile"?"all":"profile",this.recomputeVisible(),this.reseatSelection()}async sendFastAction(){const e=this.currentRow();if(e===void 0)return;if(e.kind!=="page"||e.fastAction===void 0){this.status="no recommended answer on this ticket \u2014 Enter opens it";return}const t=await this.requestOrNull(e.id);if(y(t)){this.status=w(t),await this.rescan();return}const i=k(e.dir);if(i===null){this.status="ticket is being edited by another inbox";return}const s=e.fastAction.kind==="acknowledge"?"acknowledged":`sent: ${L(e)??"recommended answer"}`;this.submittingDir=e.dir;try{await I(e.id,e.fastAction.responses),this.status=s}catch(n){this.status=$(n)==="already_settled"?w(await this.requestOrNull(e.id)):m(n)}finally{u(e.dir,i.token),this.submittingDir=void 0,await this.rescan(),this.markDirty()}}async cancelFromInbox(e){this.submittingDir=e.dir;try{e.kind==="review"?await J().cancel(e.id,"Canceled from the inbox."):await U(e.id,{reason:"Canceled from the inbox.",actor:"human"}),this.status="canceled"}catch(t){this.status=m(t)}finally{this.submittingDir=void 0,await this.rescan(),this.markDirty()}}async activate(){const e=this.currentRow();if(e===void 0)return;if(e.kind==="review"){await this.activateReview(e);return}const t=await this.requestOrNull(e.id);if(this.closed)return;if(y(t)){this.status=w(t),await this.rescan();return}const i=k(e.dir);if(i===null){this.status="ticket is being edited by another inbox";return}let s,n;try{const r=await f().getHumanInboxPage(e.inboxTicketId);if(r.state==="resolved"||r.state==="canceled"){u(e.dir,i.token),this.status=x(r),await this.rescan();return}s=b(r),n=r.revision}catch{u(e.dir,i.token),this.status="page could not be read",await this.invalidate();return}if(this.closed||this.claim!==void 0||this.adapter!==void 0){u(e.dir,i.token);return}this.claim={dir:e.dir,id:e.id,ticketId:e.inboxTicketId,token:i.token},this.screen="detail",this.adapter=new G({ticketId:e.inboxTicketId,page:s,productKinds:E(),cols:this.detailSize().cols,rows:this.detailSize().rows,onDirty:o(()=>this.markDirty(),"onDirty"),onEditorRequest:o(()=>this.editActivePageInput(),"onEditorRequest"),onBack:o(()=>{this.leaveDetail(),this.markDirty()},"onBack"),onComplete:o(r=>{this.complete(r)},"onComplete")}),this.openRevision=n,this.watchSelected(e.dir),this.markDirty()}async activateReview(e){this.reviewAdapter=new X({reviewId:e.id,host:this.host});let t=!1;try{t=await this.reviewAdapter.start()}catch(i){this.status=m(i)}finally{this.reviewAdapter=void 0}if(t){this.close();return}await this.rescan(),this.markDirty()}selectDir(e){this.seatOn(e)&&this.markDirty()}seatOn(e){let t=this.visible.findIndex(i=>i.dir===e);return t<0&&this.items.some(i=>i.dir===e)&&(this.filter="all",this.recomputeVisible(),t=this.visible.findIndex(i=>i.dir===e)),t<0?!1:(this.select(t),!0)}async reloadSelectedPage(){const e=this.claim;if(this.adapter===void 0||e===void 0){this.markDirty();return}try{const t=await f().getHumanInboxPage(e.ticketId);if(this.closed||this.claim!==e||this.adapter===void 0)return;t.revision!==this.openRevision&&(this.openRevision=t.revision,this.adapter.reload(b(t)))}catch{this.status="page could not be read"}this.markDirty()}close(){if(!this.closed){this.closed=!0,this.started=!1,this.heartbeat!==void 0&&clearInterval(this.heartbeat),this.heartbeat=void 0,this.reviewAdapter?.stop(),this.leaveDetail(this.claim?.dir!==this.submittingDir);for(const e of this.watchers)e.close();this.watchers=[],this.selectedWatcher?.close(),this.selectedWatcher=void 0,this.options.onClosed()}}async complete(e){const t=this.claim;if(t===void 0)return;this.submittingDir=t.dir;try{await I(t.id,e)}catch(s){this.submittingDir=void 0,this.status=m(s);const n=$(s);if(n==="unreachable"||n==="other"){this.markDirty();return}n==="already_settled"&&(this.status=w(await this.requestOrNull(t.id))),this.leaveDetail(!1),await this.rescan(),this.markDirty();return}this.submittingDir=void 0,this.leaveDetail(!1),await this.rescan();const i=this.items.some(s=>s.dir===t.dir);_({level:i?"warn":"info",event:"inbox.page.resolved",fields:{ticket_id:t.id,request_settled:y(await this.requestOrNull(t.id)),still_listed:i,claim_left_behind:P(T(t.dir))}}),this.markDirty()}editActivePageInput(){const e=this.adapter?.inputBuffer();if(e===void 0)return;const t=this.host.suspendForChild();let i={text:e};try{i=Q(e)}finally{t(),this.adapter?.setInputBuffer(i.text),i.error!==void 0&&(this.status=i.error),this.markDirty()}}leaveDetail(e=!0){this.adapter?.close(),this.adapter=void 0,this.selectedWatcher?.close(),this.selectedWatcher=void 0,e&&this.claim!==void 0&&u(this.claim.dir,this.claim.token),this.claim=void 0,this.openRevision=void 0,this.screen="list",this.reseatSelection()}select(e){this.visible.length!==0&&(this.selectedIndex=Math.max(0,Math.min(e,this.visible.length-1)),this.selectedDir=this.visible[this.selectedIndex].dir)}detailSize(){const e=g(this.cols,this.rows,this.screen);return{cols:Math.max(1,e.detailWidth-2),rows:e.height}}detailLines(e,t){if(this.adapter!==void 0)return this.adapter.render();const i=this.currentRow();if(i===void 0)return this.passiveDetailLines(e,t);const s=this.focusAvailable(i.dir)?` ${h}g${a} chat`:"",n=i.kind==="page"&&i.fastAction!==void 0?` ${h}s${a} ${i.fastAction.kind==="acknowledge"?"acknowledge":"send recommended"}`:"",r=i.kind==="page"?[` ${h}Enter${a} opens the full page ${h}\u2191\u2193/jk${a} select${n} ${h}x${a} cancel`,` ${h}Page:${a} c comment u/d scroll${s} ${h}q${a} close`]:[` ${h}Enter${a} opens the full review ${h}\u2191\u2193/jk${a} select ${h}x${a} cancel`,` ${s} ${h}q${a} close`],l=Math.max(0,t-r.length),c=this.passiveDetailLines(e,l);for(;c.length<l;)c.push("");return[...c,...r.map(v=>p(v,e))]}focusAvailable(e){return this.host.capabilities.canFocusNode}async focusSelectedSource(){const e=this.screen==="detail"?this.claim?.dir:this.currentRow()?.dir,t=e===void 0?void 0:this.items.find(s=>s.dir===e);if(t===void 0||this.focusingSource)return;const i=t.source.nodeId;if(i===void 0){this.status="This request was not raised by a canvas node",this.markDirty();return}this.focusingSource=!0;try{await this.host.focusNode(i)?this.close():(this.status="Could not open that node\u2019s viewer",this.markDirty())}finally{this.focusingSource=!1}}pagePreviewBody(e,t){const i=this.preview;if(i!==void 0&&i.dir===e.dir){if(this.previewStale&&this.schedulePreview(e),i.width===t)return i.lines;const s=S(i.view,t);return this.preview={...i,width:t,lines:s},s}return this.schedulePreview(e),this.previewFailed===e.dir?[`${h}page could not be read${a}`]:[]}schedulePreview(e){this.previewFetching!==e.dir&&(this.previewFetching=e.dir,this.previewStale=!1,f().getHumanInboxPage(e.inboxTicketId).then(t=>{if(this.closed||this.previewFetching!==e.dir||(this.previewFetching=void 0,this.previewFailed=void 0,this.preview?.dir===e.dir&&this.preview.revision===t.revision))return;const i=b(t),s=this.preview?.width??g(this.cols,this.rows,this.screen).detailWidth;this.preview={dir:e.dir,revision:t.revision,view:i,width:s,lines:S(i,s)},this.markDirty()},()=>{this.closed||this.previewFetching!==e.dir||(this.previewFetching=void 0,this.previewFailed=e.dir,this.preview?.dir===e.dir&&(this.preview=void 0),this.markDirty())}))}async requestOrNull(e){try{return await f().getHumanRequest(e)}catch{return}}passiveDetailLines(e,t){const i=this.currentRow();if(i===void 0)return[` ${h}Select a pending interaction.${a}`];const s=[` ${M}${W}${i.title}${a}`,""];for(const r of C(i.subtitle,Math.max(1,e-2)))s.push(` ${r}`);const n=i.kind==="page"?this.pagePreviewBody(i,e):[`${h}1 file to review${a}`];if(n.length>0){s.push("");const r=Math.max(0,t-s.length),l=n.length<=r?n:n.slice(0,Math.max(0,r-1));for(const c of l)s.push(` ${c}`);l.length<n.length&&s.push(` ${h}\u2026 +${n.length-l.length} more lines${a}`)}return s.map(r=>p(r,e)).slice(0,Math.max(1,t))}withStatus(e){const t=this.cancelConfirmation===void 0?this.refreshError??this.status:`cancel \u201C${this.cancelConfirmation.item.title}\u201D? y yes \xB7 n/Esc no`;if(t===void 0||this.rows<1)return e;const i=[...e];for(;i.length<this.rows;)i.push("");return i[this.rows-1]=`${D}${t}${a}`,i}markDirty(){this.options.onDirty()}watchRoots(){const e=N(this.options.onInboxActivity??(()=>{this.invalidate()}));this.watchers.push({close:e})}watchSelected(e){this.selectedWatcher?.close();try{this.selectedWatcher=R(e,()=>{this.invalidate()})}catch{this.selectedWatcher=void 0}}}async function Z(){const{tickets:d}=await f().listHumanInbox();return d.map(Y).sort(K)}o(Z,"defaultScan");function S(d,e){const t=d.manifest.dialect==="html"?V(d.document):z(d.document,d.manifest);return j(d.manifest,t,Math.min(Math.max(20,e-4),100),Math.max(20,e-4))}o(S,"previewLines");function y(d){return d!==void 0&&d.state!=="pending"}o(y,"isSettled");function w(d){return d?.page===void 0?"ticket was already resolved":x(d.page)}o(w,"settledStatus");export{ke as InboxController};
|
|
1
|
+
var A=Object.defineProperty;var o=(d,e)=>A(d,"name",{value:e,configurable:!0});import{existsSync as P,watch as R}from"node:fs";import{visibleWidth as F}from"@earendil-works/pi-tui";import{renderMarkdown as C}from"../../core/termrender/termrender.js";import{BOLD as M,CYAN as W,DIM as h,GRAY as q,RESET as a,YELLOW as D,clipLine as p,singleLine as L,wrap as O}from"./tui/ansi.js";import{buildInboxLines as E,recommendedLabel as N}from"./tui.js";import{inboxLayout as g}from"./layout.js";import{resolvePageComponents as H}from"../../core/config.js";import{watchInboxActivity as T}from"../../core/human/root.js";import{claimTicket as k,heartbeatClaim as B,releaseClaim as u}from"../../core/human/claim.js";import{claimPath as _,clearProgress as z}from"../../core/human/convention.js";import{emitEvent as V}from"../../core/events/emit.js";import{cliClient as f}from"../../commands/api-client.js";import{projectPageDisplayMarkdown as j}from"../../core/human/page-markdown.js";import{projectHtmlDocumentMarkdown as K}from"../../core/human/page-html-markdown.js";import{pageBodyLines as Y}from"./tui/page-body.js";import{byNewestFirst as G,inboxRowFromDTO as X,pageViewFromDTO as b,settledStatusOf as x}from"./dto-adapt.js";import{PageAdapter as J}from"./page-adapter.js";import{ReviewAdapter as Q}from"./review-adapter.js";import{createReviewClient as U}from"./review/review-client.js";import{editBufferInEditor as Z}from"./review/roundtrip.js";import{daemonErrorMessage as m,dismissTicketViaDaemon as ee,resolveFailureKind as $,resolveTicketPage as I}from"./resolve.js";class $e{static{o(this,"InboxController")}options;host;scan;items=[];visible=[];profile;filter;pendingSeat;selectedDir;selectedIndex=0;screen="list";adapter;reviewAdapter;focusingSource=!1;claim;submittingDir;cancelConfirmation;cols;rows;status;refreshError;watchers=[];selectedWatcher;heartbeat;started=!1;closed=!1;preview;previewFailed;previewFetching;previewStale=!1;openRevision;constructor(e){this.options=e,this.host=e.host,this.scan=e.scan??te,this.cols=e.cols??80,this.rows=e.rows??24,this.profile=e.profile,this.filter=e.profile===void 0?"all":"profile",this.selectedDir=e.initialSelection,this.pendingSeat=e.initialSelection,this.rescan()}snapshot(){return{items:[...this.items],selectedDir:this.selectedDir,screen:this.screen,inputBuffer:this.adapter?.inputBuffer()}}async rescan(){const e=this.selectedDir,t=this.claim;try{this.items=await this.scan()}catch(s){if(this.closed)return;this.status=m(s),this.markDirty();return}if(this.closed)return;if(this.recomputeVisible(),this.pendingSeat!==void 0){const s=this.pendingSeat;this.pendingSeat=void 0,this.seatOn(s)}if(!(e!==void 0&&this.items.some(s=>s.dir===e))&&this.adapter!==void 0&&e!==void 0&&t!==void 0){const s=(await this.requestOrNull(t.id))?.state==="canceled";if(this.closed)return;s&&z(e),e!==this.submittingDir&&(this.status=s?"canceled by requester":"ticket resolved elsewhere"),this.leaveDetail(e!==this.submittingDir)}this.reseatSelection()}recomputeVisible(){if(this.filter==="all"||this.profile===void 0){this.visible=this.items;return}const e=this.profile.members();this.visible=this.items.filter(t=>{const i=t.source.nodeId;return i!==void 0&&e.has(i)})}reseatSelection(){const e=this.selectedDir===void 0?-1:this.visible.findIndex(t=>t.dir===this.selectedDir);if(e>=0){this.selectedIndex=e;return}if(!(this.adapter!==void 0&&this.selectedDir!==void 0&&this.items.some(t=>t.dir===this.selectedDir))){if(this.visible.length===0){this.selectedIndex=0,this.selectedDir=void 0;return}this.selectedIndex=Math.min(Math.max(0,this.selectedIndex),this.visible.length-1),this.selectedDir=this.visible[this.selectedIndex].dir}}highlightIndex(){return this.selectedDir===void 0?-1:this.visible.findIndex(e=>e.dir===this.selectedDir)}currentRow(){const e=this.highlightIndex();return e>=0?this.visible[e]:void 0}scopeHeading(){return this.profile===void 0?{label:"all profiles"}:this.filter==="profile"?{label:this.profile.label,hint:"P all profiles"}:{label:"all profiles",hint:`P ${this.profile.label} only`}}refreshOutcome(e){if(this.refreshError=e,e===void 0){this.invalidate();return}this.markDirty()}start(){this.started||this.closed||(this.started=!0,this.watchRoots(),this.heartbeat=setInterval(()=>{this.claim!==void 0&&B(this.claim.dir,this.claim.token)},1e4))}async invalidate(){if(this.previewStale=!0,await this.rescan(),!this.closed){if(this.adapter!==void 0&&this.claim!==void 0){await this.reloadSelectedPage();return}this.markDirty()}}resize(e,t){this.cols=e,this.rows=t,this.adapter?.resize(this.detailSize().cols,this.detailSize().rows),this.markDirty()}render(){const e=g(this.cols,this.rows,this.screen);if(e.mode==="minimum")return[`${D}Resize terminal to at least 60\xD718 to use inbox.${a}`];const t=E(this.visible,e.listWidth,this.highlightIndex(),this.scopeHeading(),e.height),i=this.detailLines(e.detailWidth,e.height);if(e.mode==="list")return this.withStatus(t);if(e.mode==="detail")return this.withStatus(i);const s=[],n=`${q}\u2502${a}`;for(let r=0;r<e.height;r++){const l=p(t[r]??"",e.listWidth),c=p(i[r]??"",e.detailWidth),v=" ".repeat(Math.max(0,e.listWidth-F(l)));s.push(`${l}${v}${n}${c}`)}return this.withStatus(s)}handleKey(e,t){if(t.ctrl&&e==="c"){this.close();return}if(this.screen==="detail"&&this.adapter!==void 0){if((e==="g"||e==="G")&&this.adapter.canAcceptHostKeys()){this.focusSelectedSource();return}this.adapter.handleKey(e,t),this.markDirty();return}if(this.cancelConfirmation!==void 0){if(e==="y"||e==="Y"){const i=this.cancelConfirmation.item;this.cancelConfirmation=void 0,this.status="canceling\u2026",this.cancelFromInbox(i)}else(e==="n"||e==="N"||t.escape)&&(this.cancelConfirmation=void 0,this.status=void 0);this.markDirty();return}if(e==="P"&&this.profile!==void 0){this.toggleProfileFilter(),this.markDirty();return}if(e==="x"||e==="X"){const i=this.currentRow();i!==void 0&&(this.cancelConfirmation={item:i},this.status=void 0),this.markDirty();return}if(e==="s"||e==="S"){this.sendFastAction(),this.markDirty();return}if(t.escape||e==="q"){this.close();return}e==="j"||t.downArrow?this.select(this.selectedIndex+1):e==="k"||t.upArrow?this.select(this.selectedIndex-1):e==="g"||e==="G"?this.focusSelectedSource():(t.return||e==="a")&&this.activate(),this.markDirty()}toggleProfileFilter(){this.filter=this.filter==="profile"?"all":"profile",this.recomputeVisible(),this.reseatSelection()}async sendFastAction(){const e=this.currentRow();if(e===void 0)return;if(e.kind!=="page"||e.fastAction===void 0){this.status="no recommended answer on this ticket \u2014 Enter opens it";return}const t=await this.requestOrNull(e.id);if(y(t)){this.status=w(t),await this.rescan();return}const i=k(e.dir);if(i===null){this.status="ticket is being edited by another inbox";return}const s=e.fastAction.kind==="acknowledge"?"acknowledged":`sent: ${N(e)??"recommended answer"}`;this.submittingDir=e.dir;try{await I(e.id,e.fastAction.responses),this.status=s}catch(n){this.status=$(n)==="already_settled"?w(await this.requestOrNull(e.id)):m(n)}finally{u(e.dir,i.token),this.submittingDir=void 0,await this.rescan(),this.markDirty()}}async cancelFromInbox(e){this.submittingDir=e.dir;try{e.kind==="review"?await U().cancel(e.id,"Canceled from the inbox."):await ee(e.id,{reason:"Canceled from the inbox.",actor:"human"}),this.status="canceled"}catch(t){this.status=m(t)}finally{this.submittingDir=void 0,await this.rescan(),this.markDirty()}}async activate(){const e=this.currentRow();if(e===void 0)return;if(e.kind==="review"){await this.activateReview(e);return}const t=await this.requestOrNull(e.id);if(this.closed)return;if(y(t)){this.status=w(t),await this.rescan();return}const i=k(e.dir);if(i===null){this.status="ticket is being edited by another inbox";return}let s,n;try{const r=await f().getHumanInboxPage(e.inboxTicketId);if(r.state==="resolved"||r.state==="canceled"){u(e.dir,i.token),this.status=x(r),await this.rescan();return}s=b(r),n=r.revision}catch{u(e.dir,i.token),this.status="page could not be read",await this.invalidate();return}if(this.closed||this.claim!==void 0||this.adapter!==void 0){u(e.dir,i.token);return}this.claim={dir:e.dir,id:e.id,ticketId:e.inboxTicketId,token:i.token},this.screen="detail",this.adapter=new J({ticketId:e.inboxTicketId,page:s,productKinds:H(),cols:this.detailSize().cols,rows:this.detailSize().rows,onDirty:o(()=>this.markDirty(),"onDirty"),onEditorRequest:o(()=>this.editActivePageInput(),"onEditorRequest"),onBack:o(()=>{this.leaveDetail(),this.markDirty()},"onBack"),onComplete:o(r=>{this.complete(r)},"onComplete")}),this.openRevision=n,this.watchSelected(e.dir),this.markDirty()}async activateReview(e){this.reviewAdapter=new Q({reviewId:e.id,host:this.host});let t=!1;try{t=await this.reviewAdapter.start()}catch(i){this.status=m(i)}finally{this.reviewAdapter=void 0}if(t){this.close();return}await this.rescan(),this.markDirty()}selectDir(e){this.seatOn(e)&&this.markDirty()}seatOn(e){let t=this.visible.findIndex(i=>i.dir===e);return t<0&&this.items.some(i=>i.dir===e)&&(this.filter="all",this.recomputeVisible(),t=this.visible.findIndex(i=>i.dir===e)),t<0?!1:(this.select(t),!0)}async reloadSelectedPage(){const e=this.claim;if(this.adapter===void 0||e===void 0){this.markDirty();return}try{const t=await f().getHumanInboxPage(e.ticketId);if(this.closed||this.claim!==e||this.adapter===void 0)return;t.revision!==this.openRevision&&(this.openRevision=t.revision,this.adapter.reload(b(t)))}catch{this.status="page could not be read"}this.markDirty()}close(){if(!this.closed){this.closed=!0,this.started=!1,this.heartbeat!==void 0&&clearInterval(this.heartbeat),this.heartbeat=void 0,this.reviewAdapter?.stop(),this.leaveDetail(this.claim?.dir!==this.submittingDir);for(const e of this.watchers)e.close();this.watchers=[],this.selectedWatcher?.close(),this.selectedWatcher=void 0,this.options.onClosed()}}async complete(e){const t=this.claim;if(t===void 0)return;this.submittingDir=t.dir;try{await I(t.id,e)}catch(s){this.submittingDir=void 0,this.status=m(s);const n=$(s);if(n==="unreachable"||n==="other"){this.markDirty();return}n==="already_settled"&&(this.status=w(await this.requestOrNull(t.id))),this.leaveDetail(!1),await this.rescan(),this.markDirty();return}this.submittingDir=void 0,this.leaveDetail(!1),await this.rescan();const i=this.items.some(s=>s.dir===t.dir);V({level:i?"warn":"info",event:"inbox.page.resolved",fields:{ticket_id:t.id,request_settled:y(await this.requestOrNull(t.id)),still_listed:i,claim_left_behind:P(_(t.dir))}}),this.markDirty()}editActivePageInput(){const e=this.adapter?.inputBuffer();if(e===void 0)return;const t=this.host.suspendForChild();let i={text:e};try{i=Z(e)}finally{t(),this.adapter?.setInputBuffer(i.text),i.error!==void 0&&(this.status=i.error),this.markDirty()}}leaveDetail(e=!0){this.adapter?.close(),this.adapter=void 0,this.selectedWatcher?.close(),this.selectedWatcher=void 0,e&&this.claim!==void 0&&u(this.claim.dir,this.claim.token),this.claim=void 0,this.openRevision=void 0,this.screen="list",this.reseatSelection()}select(e){this.visible.length!==0&&(this.selectedIndex=Math.max(0,Math.min(e,this.visible.length-1)),this.selectedDir=this.visible[this.selectedIndex].dir)}detailSize(){const e=g(this.cols,this.rows,this.screen);return{cols:Math.max(1,e.detailWidth-2),rows:e.height}}detailLines(e,t){if(this.adapter!==void 0)return this.adapter.render();const i=this.currentRow();if(i===void 0)return this.passiveDetailLines(e,t);const s=this.focusAvailable(i.dir)?` ${h}g${a} chat`:"",n=i.kind==="page"&&i.fastAction!==void 0?` ${h}s${a} ${i.fastAction.kind==="acknowledge"?"acknowledge":"send recommended"}`:"",r=i.kind==="page"?[` ${h}Enter${a} opens the full page ${h}\u2191\u2193/jk${a} select${n} ${h}x${a} cancel`,` ${h}Page:${a} c comment u/d scroll${s} ${h}q${a} close`]:[` ${h}Enter${a} opens the full review ${h}\u2191\u2193/jk${a} select ${h}x${a} cancel`,` ${s} ${h}q${a} close`],l=Math.max(0,t-r.length),c=this.passiveDetailLines(e,l);for(;c.length<l;)c.push("");return[...c,...r.map(v=>p(v,e))]}focusAvailable(e){return this.host.capabilities.canFocusNode}async focusSelectedSource(){const e=this.screen==="detail"?this.claim?.dir:this.currentRow()?.dir,t=e===void 0?void 0:this.items.find(s=>s.dir===e);if(t===void 0||this.focusingSource)return;const i=t.source.nodeId;if(i===void 0){this.status="This request was not raised by a canvas node",this.markDirty();return}this.focusingSource=!0;try{await this.host.focusNode(i)?this.close():(this.status="Could not open that node\u2019s viewer",this.markDirty())}finally{this.focusingSource=!1}}pagePreviewBody(e,t){const i=this.preview;if(i!==void 0&&i.dir===e.dir){if(this.previewStale&&this.schedulePreview(e),i.width===t)return i.lines;const s=S(i.view,t);return this.preview={...i,width:t,lines:s},s}return this.schedulePreview(e),this.previewFailed===e.dir?[`${h}page could not be read${a}`]:[]}schedulePreview(e){this.previewFetching!==e.dir&&(this.previewFetching=e.dir,this.previewStale=!1,f().getHumanInboxPage(e.inboxTicketId).then(t=>{if(this.closed||this.previewFetching!==e.dir||(this.previewFetching=void 0,this.previewFailed=void 0,this.preview?.dir===e.dir&&this.preview.revision===t.revision))return;const i=b(t),s=this.preview?.width??g(this.cols,this.rows,this.screen).detailWidth;this.preview={dir:e.dir,revision:t.revision,view:i,width:s,lines:S(i,s)},this.markDirty()},()=>{this.closed||this.previewFetching!==e.dir||(this.previewFetching=void 0,this.previewFailed=e.dir,this.preview?.dir===e.dir&&(this.preview=void 0),this.markDirty())}))}async requestOrNull(e){try{return await f().getHumanRequest(e)}catch{return}}passiveDetailLines(e,t){const i=this.currentRow();if(i===void 0)return[` ${h}Select a pending interaction.${a}`];const s=[...O(L(i.title),Math.max(10,e-2)).map(r=>` ${M}${W}${r}${a}`),""];for(const r of C(i.subtitle,Math.max(1,e-2)))s.push(` ${r}`);const n=i.kind==="page"?this.pagePreviewBody(i,e):[`${h}1 file to review${a}`];if(n.length>0){s.push("");const r=Math.max(0,t-s.length),l=n.length<=r?n:n.slice(0,Math.max(0,r-1));for(const c of l)s.push(` ${c}`);l.length<n.length&&s.push(` ${h}\u2026 +${n.length-l.length} more lines${a}`)}return s.map(r=>p(r,e)).slice(0,Math.max(1,t))}withStatus(e){const t=this.cancelConfirmation===void 0?this.refreshError??this.status:`cancel \u201C${this.cancelConfirmation.item.title}\u201D? y yes \xB7 n/Esc no`;if(t===void 0||this.rows<1)return e;const i=[...e];for(;i.length<this.rows;)i.push("");return i[this.rows-1]=`${D}${t}${a}`,i}markDirty(){this.options.onDirty()}watchRoots(){const e=T(this.options.onInboxActivity??(()=>{this.invalidate()}));this.watchers.push({close:e})}watchSelected(e){this.selectedWatcher?.close();try{this.selectedWatcher=R(e,()=>{this.invalidate()})}catch{this.selectedWatcher=void 0}}}async function te(){const{tickets:d}=await f().listHumanInbox();return d.map(X).sort(G)}o(te,"defaultScan");function S(d,e){const t=d.manifest.dialect==="html"?K(d.document):j(d.document,d.manifest);return Y(d.manifest,t,Math.min(Math.max(20,e-4),100),Math.max(20,e-4))}o(S,"previewLines");function y(d){return d!==void 0&&d.state!=="pending"}o(y,"isSettled");function w(d){return d?.page===void 0?"ticket was already resolved":x(d.page)}o(w,"settledStatus");export{$e as InboxController};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var L=Object.defineProperty;var $=(s,r)=>L(s,"name",{value:r,configurable:!0});import{renderMarkdownBlockAwareLines as M}from"../../../core/termrender/termrender.js";import{describeSlot as S,tableMarkdown as w,terminalAnswerable as x}from"./slots.js";import{BOLD as b,DIM as c,RESET as
|
|
1
|
+
var L=Object.defineProperty;var $=(s,r)=>L(s,"name",{value:r,configurable:!0});import{renderMarkdownBlockAwareLines as M}from"../../../core/termrender/termrender.js";import{describeSlot as S,tableMarkdown as w,terminalAnswerable as x}from"./slots.js";import{BOLD as b,DIM as c,RESET as o,sanitize as n,singleLine as d,truncate as k,wrap as u}from"./ansi.js";function D(s,r,f,m){const p=$(t=>M(t,f,m),"md");if(s.dialect==="html")return r===""?[`${c}HTML page \u2014 open in Northlight to read this${o}`]:p(r);const e=r===""?[]:p(r);let a=-1;for(const t of s.slots){s.steps>1&&t.step!==a&&(e.length>0&&e.push(""),e.push(`${b}Step ${t.step+1}/${s.steps}${o}`),a=t.step),e.length>0&&e.push("");for(const i of u(d(S(t)),f))e.push(`${b}${i}${o}`);const h=t.config.body;if(typeof h=="string"&&h!==""&&e.push(...p(h)),!x(t)){e.push(`${c}${n(t.kind)} \u2014 open in Northlight to answer this${o}`);continue}if(t.kind==="text")e.push(...p(t.config.initialText));else if(t.kind==="table")e.push(...p(w(t.config)));else if(t.kind==="cards"){for(const i of t.config.cards??[])if(e.push(` \u2022 ${k(n(d(i.subtitle===void 0?i.title:`${i.title} \u2014 ${i.subtitle}`)),Math.max(10,f-4))}`),i.body!==void 0)for(const l of u(n(i.body),Math.max(10,f-6)))e.push(` ${c}${l}${o}`)}else if(t.kind==="options"){for(const i of t.config.options)if(e.push(` \u2022 ${k(n(d(i.label)),Math.max(10,f-4))}`),i.description!==void 0)for(const l of u(n(i.description),Math.max(10,f-6)))e.push(` ${c}${l}${o}`)}else if(t.kind==="chart"){const i=t.config,l=i.title??`${i.x??""} vs ${(i.y??[]).map(g=>g.label??g.field).join(", ")}`;e.push(`${c}chart \u2014 ${n(i.kind??"chart")}: ${n(l)}${o}`)}}return e}$(D,"pageBodyLines");export{D as pageBodyLines};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
`){
|
|
1
|
+
var K=Object.defineProperty;var a=(n,s)=>K(n,"name",{value:s,configurable:!0});import E from"string-width";import{isResponseBearingSlot as I}from"../../../core/human/page-schema.js";import{answerCommentAnchorLabel as Q,describePageAnswer as U}from"../../../core/human/answer.js";import{renderMarkdownBlockAwareLines as X}from"../../../core/termrender/termrender.js";import{commentAnchors as Z,describeSlot as L,pageComplete as nn,slotActions as en,slotAnswered as j,tableMarkdown as tn,terminalAnswerable as R}from"./slots.js";import{RESET as r,BOLD as M,DIM as u,GREEN as W,YELLOW as O,CYAN as y,REVERSE as N,sanitize as x,singleLine as g,truncate as q,hline as w,wrap as k,centerHorizontal as T,clipLine as H,panLine as sn,visibleWidth as C}from"./ansi.js";const on=8,S=a((n,s,t)=>X(n,s,t),"mdLines");function P(n){return U(n.manifest,Object.fromEntries(n.responses),{completedAt:""})}a(P,"pageAnswer");function rn(n,s){return s.id===void 0?void 0:n.slots.find(t=>t.id===s.id)}a(rn,"answerSlot");function z(n){if(n===void 0||n.state==="unanswered")return"unanswered";if(n.raw!==void 0)return"custom response";const s=n.text!==void 0?[n.text.edited?"edited":"accepted"]:n.groups.map(t=>t.picked.length===0?"none":t.picked.map(e=>e.label).join(", "));return n.freetext!==void 0&&s.push(n.freetext),n.comments.length>0&&s.push(`${n.comments.length} comment${n.comments.length===1?"":"s"}`),s.join("; ")||"answered"}a(z,"answerSummary");function D(n,s){return Q(n,s)}a(D,"commentLabel");function xn(n,s,t){const e=Math.min(Math.max(20,s-4),120),o=Math.max(20,s-4),l=["",...v(g(n.manifest.title),e,"",0,`${M}${y}`)];if(n.manifest.subtitle)for(const h of k(x(n.manifest.subtitle),e))l.push(` ${u}${h}${r}`);if(n.displayMarkdown!==""){l.push("");for(const h of S(n.displayMarkdown,e,o))l.push(` ${h}`)}else n.manifest.dialect==="html"&&l.push("",` ${u}HTML page \u2014 open in Northlight to read this${r}`);const i=n.slots.length>0;i&&l.push(` ${u}${w(e)}${r}`,"");const f=P(n);let c=-1,$;for(let h=0;h<n.slots.length;h++){const p=n.slots[h];n.manifest.steps>1&&p.step!==c&&(c!==-1&&l.push(""),l.push(` ${M}Step ${p.step+1}/${n.manifest.steps}${r}`),c=p.step);const b=p.id===void 0?void 0:n.responses.get(p.id),A=I(p)?j(n,p)?`${W}\u2713${r}`:`${u}\u25CB${r}`:`${u}\xB7${r}`,J=h===n.currentIndex?`${y}\u25B8${r} `:" ";h===n.currentIndex&&($=l.length),l.push(` ${J}${A} ${q(g(L(p)),Math.max(10,s-16))}`),b!==void 0&&l.push(` ${u}${q(g(z(rn(f,p))),Math.max(10,s-10))}${r}`)}if(n.manifest.slots.some(h=>I(h)&&!R(h))){l.push("");for(const h of k("This page must be answered in Northlight. Leaving it here keeps it in your inbox; x on the list cancels it.",e-2))l.push(` ${O}${h}${r}`)}i&&l.push(""),l.push(` ${u}${w(e)}${r}`);const d=!n.manifest.slots.some(I)?` ${u}u/d${r} scroll ${u}q${r} dismiss`:` ${u}enter${r} review ${u}\u2191\u2193/jk${r} navigate ${u}u/d${r} scroll ${u}q${r} close`;return _(l,d,n,s,t,e+2,n.followSelection?$:void 0)}a(xn,"renderOverview");function ln(n,s,t){const e=[...n],o=Math.max(0,Math.min(s,e.length)),l=F(n,t);let i=0;for(let $=0;$<l.length;$++)l[$].start<=o&&(i=$);const f=l.map(($,m)=>{if(m!==i)return e.slice($.start,$.end).join("");const d=e.slice($.start,o).join(""),h=o<$.end?e[o]:" ",p=o<$.end?e.slice(o+1,$.end).join(""):"";return`${d}${N}${h}${r}${p}`}),c=l[i];return o===c.end&&E(e.slice(c.start,c.end).join(""))>=t&&(f[i]=e.slice(c.start,c.end).join(""),f.splice(i+1,0,`${N} ${r}`)),f}a(ln,"renderInputBuffer");function Mn(n,s){const t=[...n];let e=Math.max(0,Math.min(s,t.length));for(;e>0&&/\s/.test(t[e-1]);)e--;for(;e>0&&!/\s/.test(t[e-1]);)e--;return e}a(Mn,"wordLeftIndex");function gn(n,s){const t=[...n];let e=Math.max(0,Math.min(s,t.length));for(;e<t.length&&/\s/.test(t[e]);)e++;for(;e<t.length&&!/\s/.test(t[e]);)e++;return e}a(gn,"wordRightIndex");function F(n,s){const t=[...n],e=Math.max(1,s),o=[];let l=0,i=0;for(let f=0;f<t.length;f++){const c=t[f];if(c===`
|
|
2
|
+
`){o.push({start:l,end:f}),l=f+1,i=0;continue}const $=E(c);i+$>e?(o.push({start:l,end:f}),l=f,i=$):i+=$}return o.push({start:l,end:t.length}),o}a(F,"wrappedRowBounds");function wn(n,s,t,e){const o=F(n,e),l=Math.max(0,Math.min(s,[...n].length));let i=0;for(let c=0;c<o.length;c++)o[c].start<=l&&(i=c);const f=o[i+t];return f===void 0?l:Math.min(f.start+(l-o[i].start),f.end)}a(wn,"verticalCursor");function cn(n,s){return s.id===void 0?[]:n.responses.get(s.id)?.comments??[]}a(cn,"responseComments");function v(n,s,t,e,o){return k(n,Math.max(10,s-e)).map((l,i)=>` ${i===0?t:" ".repeat(e)}${o}${l}${r}`)}a(v,"hangingWrap");function fn(n,s,t,e){if(s.id===void 0)return!1;const o=n.responses.get(s.id);return t==="option"?o?.selectedOptionIds?.includes(e)===!0:t==="card"?o?.selectedCardIds?.includes(e)===!0:t==="row"?o?.selectedRowIds?.includes(e)===!0:o?.selectedColumnIds?.includes(e)===!0}a(fn,"selected");function Y(n,s){const t=n.slots[n.currentIndex],e=Math.min(Math.max(20,s-4),120),o=Math.max(20,s-4),l=`[${n.currentIndex+1}/${n.slots.length}]`,i=["",...v(g(n.manifest.title),e,`${M}${y}${l}${r} `,E(l)+2,u),` ${u}${w(e)}${r}`,"",...v(g(L(t)),e,"",0,M)],f=t.config.body;if(typeof f=="string"&&f.length>0){i.push("");for(const m of S(f,e,o))i.push(` ${m}`)}if(R(t)){if(t.kind==="chart"){const m=t.config,d=m.title??`${m.x} vs ${(m.y??[]).map(h=>h.label??h.field).join(", ")}`;i.push("",` ${u}chart \u2014 ${x(m.kind)}: ${x(d)}${r}`)}else if(t.kind==="table"){i.push("");for(const d of S(tn(t.config),e,o))i.push(` ${d}`);const m=t.config.label;m&&i.push(` ${u}${x(m)}${r}`)}else if(t.kind==="text"){const d=(t.id===void 0?void 0:n.responses.get(t.id))?.text??t.config.initialText;i.push("");for(const h of S(d,e,o))i.push(` ${h}`)}}else{i.push("");for(const m of k(`${x(t.kind)} \u2014 must be answered in Northlight; the terminal cannot submit this page`,Math.max(10,e-2)))i.push(` ${O}${m}${r}`);i.push(` ${u}q leaves it in your inbox; x on the list cancels it${r}`)}i.push("",` ${u}${w(e)}${r}`);let c;if(n.inputMode!==null){const m=n.inputMode.kind==="comment"?`Comment on ${D(t,n.inputMode.anchor)}`:n.inputMode.kind==="text"?"Edit text":"Other response";i.push(` ${M}${m}${r}`);for(const d of ln(n.inputMode.buffer,n.inputMode.cursor,Math.max(10,e-4)))i.push(` ${d}`);c=` ${u}enter save esc cancel${n.editorAvailable?" ^o editor":""}${r}`}else{en(t).forEach((d,h)=>{const p=h===n.selectedAction?`${y}\u25B8${r}`:" ";if(d.kind==="select"){const b=fn(n,t,d.group,d.id)?`${W}\u2713${r}`:" ";if(i.push(` ${p} [${b}] ${d.shortcut?`[${d.shortcut}] `:""}${x(d.label)}`),d.description)for(const A of k(x(d.description),Math.max(10,e-6)))i.push(` ${u}${A}${r}`)}else{const b=d.kind==="freetext"?"r":d.kind==="edit"?"e":"a";i.push(` ${p} [${b}] ${x(d.label)}`)}});for(const d of cn(n,t))for(const h of k(`attached: ${D(t,d.anchor)} \u2014 ${x(d.text)}`,Math.max(10,e-2)))i.push(` ${u}${h}${r}`);n.hint&&i.push(` ${O}${x(n.hint)}${r}`),c=` ${u}n/p next/previous \u2191\u2193/jk actions${Z(t).length>0?" c comment":""} u/d scroll \u2190\u2192/hl pan q close${r}`}const $=Math.max(0,...i.map(C));return{lines:i,footer:c,maxW:e,maxH:Math.max(0,$-Math.max(1,s))}}a(Y,"itemLines");function kn(n,s,t){if(n.slots[n.currentIndex]===void 0){n.scrollOffset=0,n.hscrollOffset=0,n.hscrollMax=0;return}const e=Y(n,s);n.hscrollMax=e.maxH,n.hscrollOffset=Math.max(0,Math.min(n.hscrollOffset,n.hscrollMax)),n.scrollOffset=Math.max(0,Math.min(n.scrollOffset,Math.max(0,e.lines.length-B(t))))}a(kn,"clampItemReviewScroll");function bn(n,s){n.hscrollOffset=Math.max(0,Math.min(n.hscrollMax,n.hscrollOffset+s*on))}a(bn,"panItemReview");function On(n,s,t){const e=Y(n,s),o=B(t),l=n.scrollOffset,i=e.lines.slice(l,l+o);for(;i.length<o;)i.push("");const f=n.hscrollOffset>0?i.map($=>sn($,n.hscrollOffset,s)):i,c=Math.min(s,e.maxW+2);return o<t&&f.push(V(e.footer,G(l,o,e.lines.length),e.maxW+2)),T(f.map($=>H($,s)),s,c)}a(On,"renderItemReview");function yn(n,s,t){const e=Math.min(Math.max(20,s-4),120),o=["",...v(`${g(n.manifest.title)} \u2014 Summary`,e,"",0,`${M}${y}`),` ${u}${w(e)}${r}`,""],l=P(n);for(const c of l.slots){const $=n.manifest.slots.find(h=>h.id===c.id),d=$!==void 0&&j(n,$)?`${W}\u2713${r}`:`${O}\u25CB${r}`;o.push(` ${d} ${M}${x($===void 0?c.kind:L($))}${r} ${u}${x(z(c))}${r}`)}const i=nn(n);if(!i){o.push("",` ${O}Cannot submit yet.${r}`);for(const c of n.slots.filter(I))j(n,c)||(o.push(` ${u}${c.id??c.kind} is unanswered${r}`),R(c)||o.push(` ${u}${c.id} must be answered in Northlight${r}`))}o.push("",` ${u}${w(e)}${r}`);const f=i?` ${u}enter${r} submit ${u}u/d${r} scroll ${u}q${r} back`:` ${u}u/d${r} scroll ${u}q${r} back`;return _(o,f,n,s,t,e+2)}a(yn,"renderFinal");function B(n){return n>=2?n-1:n}a(B,"bodyRows");function G(n,s,t){if(s<=0||t<=s)return{marks:"",full:""};const e=Math.min(t,n+s),o=`${n>0?"\u25B4":" "}${e<t?"\u25BE":" "}`;return{marks:`${u}${o}${r}`,full:`${u}${o} ${n+1}\u2013${e}/${t}${r}`}}a(G,"scrollBadge");function V(n,s,t){if(s.full==="")return n;const e=a(o=>t-C(n)-C(o)-2,"room");for(const o of[s.full,s.marks])if(e(o)>=2)return`${n}${" ".repeat(e(o))}${o}`;return` ${s.marks} ${n.trimStart()}`}a(V,"pinnedFooter");function _(n,s,t,e,o,l,i){const f=B(o),c=Math.max(0,n.length-f);t.scrollOffset=Math.max(0,Math.min(t.scrollOffset,c)),i!==void 0&&f>0&&(i<t.scrollOffset?t.scrollOffset=i:i>=t.scrollOffset+f&&(t.scrollOffset=Math.min(c,i-f+1)));const $=n.slice(t.scrollOffset,t.scrollOffset+f);for(;$.length<f;)$.push("");return f<o&&$.push(V(s,G(t.scrollOffset,f,n.length),l)),T($.map(m=>H(m,e)),e,Math.min(e,l))}a(_,"centeredWindow");export{kn as clampItemReviewScroll,bn as panItemReview,yn as renderFinal,ln as renderInputBuffer,On as renderItemReview,xn as renderOverview,wn as verticalCursor,Mn as wordLeftIndex,gn as wordRightIndex,F as wrappedRowBounds};
|
package/docs/ci-runner.md
CHANGED
|
@@ -30,14 +30,26 @@ Repository variable `CRTR_CI_RUNNER` selects `runs-on` for the `macos` jobs of `
|
|
|
30
30
|
| Value | Runs on |
|
|
31
31
|
| --- | --- |
|
|
32
32
|
| unset, or anything other than `self-hosted` | GitHub-hosted `macos-latest` (billed) |
|
|
33
|
-
| `self-hosted` | runners labelled `[self-hosted, macOS, crouter]` (the Mac mini) |
|
|
33
|
+
| `self-hosted` | runners labelled `[self-hosted, macOS, crouter]` (the Mac mini) when one is free, otherwise `macos-latest` (see Routing below) |
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
36
|
gh variable set CRTR_CI_RUNNER --body self-hosted -R Northlight-Systems/crouter # use the mini
|
|
37
37
|
gh variable delete CRTR_CI_RUNNER -R Northlight-Systems/crouter # fall back to macos-latest
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
It takes effect on the next run; no commit is needed. **
|
|
40
|
+
It takes effect on the next run; no commit is needed. **Without the `CI_RUNNER_READ_TOKEN` secret (below), routing cannot see the minis: if they are offline, jobs queue until they return — delete the variable.** Cancel and re-run anything already queued. There is one `macos` job per lane that has a selected laptop-only file (`macos unit`, `macos integration`, `macos seam`).
|
|
41
|
+
|
|
42
|
+
### Routing: minis first, hosted when none is free
|
|
43
|
+
|
|
44
|
+
With `CRTR_CI_RUNNER=self-hosted` the `select` job's route step (`scripts/ci-route.mjs`) lists the repository's runners and sends the `macos` jobs to the minis only when at least one runner labelled `crouter` is online and idle; if every mini is busy or offline they go to `macos-latest` (billed), so a saturated mini no longer queues a pull request for half an hour. The step summary of the `select` job says which it chose and why. Any other value of the variable always means `macos-latest`. Publish's call of `test.yml` passes no secrets, so on a push to main routing follows the variable alone.
|
|
45
|
+
|
|
46
|
+
Listing runners needs more than `GITHUB_TOKEN` can do, so it reads the secret `CI_RUNNER_READ_TOKEN`. One-time setup:
|
|
47
|
+
|
|
48
|
+
1. github.com → Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token, owned by the person who administers the repository.
|
|
49
|
+
2. Resource owner `Northlight-Systems`; repository access: only `Northlight-Systems/crouter`; repository permission **Administration: Read-only** (nothing else). Choose an expiry and note the renewal date.
|
|
50
|
+
3. Paste it as a repository Actions secret: `gh secret set CI_RUNNER_READ_TOKEN -R Northlight-Systems/crouter` (or Settings → Secrets and variables → Actions → New repository secret). Do not make it an environment or organization secret.
|
|
51
|
+
|
|
52
|
+
Without the secret, or when the API call fails (expired token, rate limit), routing follows the variable (minis when it says `self-hosted`) and the summary says `routing fell back to the variable`.
|
|
41
53
|
|
|
42
54
|
A pull request from a fork always runs on `macos-latest`, whatever the variable says: a self-hosted runner must never execute fork code. The repository is private and forking is disabled today, so this only guards a later change.
|
|
43
55
|
|
|
@@ -80,16 +92,18 @@ The OIDC constraint at the top of `publish.yml` is unchanged: every publishing j
|
|
|
80
92
|
|
|
81
93
|
## What runs when (hosted-runner spend)
|
|
82
94
|
|
|
83
|
-
| Event | ubuntu `checks`
|
|
95
|
+
| Event | ubuntu `checks` + `select` | ubuntu `linux` (container) | `macos` (laptop-only tests) |
|
|
84
96
|
| --- | --- | --- | --- |
|
|
85
|
-
| pull request push
|
|
86
|
-
| pull request with the `full-ci` label
|
|
87
|
-
| merge queue run (`merge_group`; inert until a queue ruleset exists) | yes |
|
|
88
|
-
| `workflow_dispatch` (`gh workflow run test.yml --ref <branch>`) | yes |
|
|
89
|
-
| push to `main` (Publish's `tests` job) | yes |
|
|
90
|
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
97
|
+
| pull request push | yes | the selected files, sharded to ~4 min; none for a docs-only change | the selected files that carry `laptopOnly` tags, if any |
|
|
98
|
+
| pull request with the `full-ci` label, or a change whose path forces it | yes | all of it | all of it |
|
|
99
|
+
| merge queue run (`merge_group`; inert until a queue ruleset exists) | yes | all of it | all of it |
|
|
100
|
+
| `workflow_dispatch` (`gh workflow run test.yml --ref <branch>`) | yes | all of it | all of it |
|
|
101
|
+
| push to `main` (Publish's `tests` job) | yes | all of it, unless `gate` found `tested-tree-<tree>` | same |
|
|
102
|
+
|
|
103
|
+
- Selected tests run on every push with no label. Add `full-ci` to force the whole suite (`gh pr edit <n> --add-label full-ci`). Labels other than `full-ci` start nothing and cancel nothing.
|
|
104
|
+
- On a pull request each matrix (`linux`, `macos`) is `fail-fast`: the first failing job cancels that matrix's other jobs, the matrix result is `failure`, and `full suite` still runs and is red. On main, merge queue and dispatch runs every job finishes. A Linux failure does not cancel the macOS matrix.
|
|
105
|
+
- `record tested tree` runs only in mode `full`, so a partial run never lets Publish skip the suite.
|
|
106
|
+
- The `full suite` job is the single result to require. It fails if `checks` or `select` did not succeed or any job failed or was cancelled; in mode `full` it also fails unless every `linux` and `macos` job succeeded (skipped is not enough), so Publish (which calls this file) can only release a tree whose full suite passed. In mode `none` it passes and says no tests are affected.
|
|
93
107
|
- `checks` builds once on ubuntu and uploads the JS output; the `linux` jobs unpack it and compile the Linux native addon in the container (the build is glibc, the image musl), and the `macos` jobs unpack it as is.
|
|
94
108
|
|
|
95
109
|
## The selector: `scripts/ci-select.mjs`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.456",
|
|
4
4
|
"description": "crtr — agent runtime with memory, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
},
|
|
138
138
|
"license": "UNLICENSED",
|
|
139
139
|
"dependencies": {
|
|
140
|
-
"@north-light/identity": "0.3.
|
|
140
|
+
"@north-light/identity": "0.3.456",
|
|
141
141
|
"@earendil-works/pi-agent-core": "1.1.0",
|
|
142
142
|
"@earendil-works/pi-ai": "1.1.0",
|
|
143
143
|
"@earendil-works/pi-coding-agent": "1.1.0",
|
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.456",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/cli",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.456",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "UNLICENSED",
|
|
12
12
|
"workspaces": [
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@earendil-works/pi-ai": "1.1.0",
|
|
19
19
|
"@earendil-works/pi-coding-agent": "1.1.0",
|
|
20
20
|
"@earendil-works/pi-tui": "1.1.0",
|
|
21
|
-
"@north-light/identity": "0.3.
|
|
21
|
+
"@north-light/identity": "0.3.456",
|
|
22
22
|
"cron-parser": "^5.6.0",
|
|
23
23
|
"esbuild": "^0.27.7",
|
|
24
24
|
"jose": "^6.2.12",
|
|
@@ -12095,17 +12095,17 @@
|
|
|
12095
12095
|
},
|
|
12096
12096
|
"packages/crouter-api": {
|
|
12097
12097
|
"name": "@north-light/api",
|
|
12098
|
-
"version": "0.3.
|
|
12098
|
+
"version": "0.3.456",
|
|
12099
12099
|
"license": "UNLICENSED"
|
|
12100
12100
|
},
|
|
12101
12101
|
"packages/crouter-env-docker": {
|
|
12102
12102
|
"name": "@north-light/env-docker",
|
|
12103
|
-
"version": "0.3.
|
|
12103
|
+
"version": "0.3.456",
|
|
12104
12104
|
"license": "UNLICENSED"
|
|
12105
12105
|
},
|
|
12106
12106
|
"packages/crouter-identity": {
|
|
12107
12107
|
"name": "@north-light/identity",
|
|
12108
|
-
"version": "0.3.
|
|
12108
|
+
"version": "0.3.456",
|
|
12109
12109
|
"license": "UNLICENSED",
|
|
12110
12110
|
"dependencies": {
|
|
12111
12111
|
"jose": "^6.2.1"
|
|
@@ -12113,20 +12113,20 @@
|
|
|
12113
12113
|
},
|
|
12114
12114
|
"packages/crouter-plugin": {
|
|
12115
12115
|
"name": "@north-light/plugin",
|
|
12116
|
-
"version": "0.3.
|
|
12116
|
+
"version": "0.3.456",
|
|
12117
12117
|
"license": "UNLICENSED",
|
|
12118
12118
|
"dependencies": {
|
|
12119
|
-
"@north-light/api": "0.3.
|
|
12120
|
-
"@north-light/identity": "0.3.
|
|
12119
|
+
"@north-light/api": "0.3.456",
|
|
12120
|
+
"@north-light/identity": "0.3.456"
|
|
12121
12121
|
}
|
|
12122
12122
|
},
|
|
12123
12123
|
"packages/crouter-sdk": {
|
|
12124
12124
|
"name": "@north-light/sdk",
|
|
12125
|
-
"version": "0.3.
|
|
12125
|
+
"version": "0.3.456",
|
|
12126
12126
|
"license": "UNLICENSED",
|
|
12127
12127
|
"dependencies": {
|
|
12128
|
-
"@north-light/api": "0.3.
|
|
12129
|
-
"@north-light/identity": "0.3.
|
|
12128
|
+
"@north-light/api": "0.3.456",
|
|
12129
|
+
"@north-light/identity": "0.3.456",
|
|
12130
12130
|
"jose": "^6.2.1"
|
|
12131
12131
|
},
|
|
12132
12132
|
"bin": {
|
|
@@ -13,6 +13,21 @@ export function scrubInheritedBrokerEnv(env = process.env) {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
// A test run must never reach a network git remote. The repo-document sync engine
|
|
17
|
+
// (`refs/crtr/docs`) pushes whatever `origin` a checkout names, and a sandbox is a copy
|
|
18
|
+
// of a real checkout whose `origin` is the production remote, with the account's SSH
|
|
19
|
+
// agent and git credentials inherited. `GIT_ALLOW_PROTOCOL=file` makes git refuse every
|
|
20
|
+
// non-local transport before it authenticates (`fatal: transport 'ssh' not allowed`);
|
|
21
|
+
// tests that need a remote use a local bare repo. Applied to the runner's env, to the
|
|
22
|
+
// in-process lane env, and to every fixture subprocess env, so it reaches the deepest git.
|
|
23
|
+
export const TEST_GIT_ALLOW_PROTOCOL = 'file';
|
|
24
|
+
export function confineGitToLocalTransport(env = process.env) {
|
|
25
|
+
env.GIT_ALLOW_PROTOCOL = TEST_GIT_ALLOW_PROTOCOL;
|
|
26
|
+
// Defense in depth: with no agent a stray ssh transport has nothing to authenticate with.
|
|
27
|
+
delete env.SSH_AUTH_SOCK;
|
|
28
|
+
return env;
|
|
29
|
+
}
|
|
30
|
+
|
|
16
31
|
// A normal host install uses the account HOME. When HOME is overridden, no
|
|
17
32
|
// working directory or configured write root may still point into that account.
|
|
18
33
|
// A node's shell runs with HOME at its app-space home (`~/.crouter/apps/<app>/home`);
|