@north-light/cli 0.3.454 → 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.
Files changed (48) hide show
  1. package/dist/api/contract.d.ts +1 -1
  2. package/dist/api/contract.js +1 -1
  3. package/dist/api/error-codes.d.ts +25 -0
  4. package/dist/api/error-codes.js +1 -1
  5. package/dist/builtin-memory/crouter-sdk/release-notes.md +6 -0
  6. package/dist/clients/attach/viewer.js +526 -526
  7. package/dist/clients/inbox/controller.js +1 -1
  8. package/dist/clients/inbox/tui/page-body.js +1 -1
  9. package/dist/clients/inbox/tui/render.js +2 -2
  10. package/dist/commands/pkg/browse/catalog.js +1 -1
  11. package/dist/commands/pkg/plugin-manage.js +1 -1
  12. package/dist/core/command-manifests/registry.d.ts +2 -1
  13. package/dist/core/command-plugins/bundle.d.ts +3 -10
  14. package/dist/core/command-plugins/bundle.js +1 -1
  15. package/dist/core/command-plugins/compose.js +1 -1
  16. package/dist/core/command-plugins/discovery.js +1 -1
  17. package/dist/core/command-plugins/endpoint.d.ts +12 -0
  18. package/dist/core/command-plugins/endpoint.js +1 -1
  19. package/dist/core/command.d.ts +13 -0
  20. package/dist/core/command.js +9 -9
  21. package/dist/core/peer/client.d.ts +4 -2
  22. package/dist/core/peer/client.js +1 -1
  23. package/dist/core/peer/directory.d.ts +7 -0
  24. package/dist/core/peer/directory.js +1 -1
  25. package/dist/core/peer/envelope.d.ts +4 -0
  26. package/dist/core/peer/envelope.js +1 -1
  27. package/dist/core/peer/home.js +1 -1
  28. package/dist/core/providers/bundles.d.ts +2 -0
  29. package/dist/core/providers/bundles.js +2 -2
  30. package/dist/core/providers/call.d.ts +2 -0
  31. package/dist/core/providers/call.js +1 -1
  32. package/dist/core/providers/device.d.ts +39 -0
  33. package/dist/core/providers/device.js +1 -0
  34. package/dist/core/providers/errors.js +1 -1
  35. package/dist/daemon/api/handlers/objects.js +2 -2
  36. package/dist/daemon/api/handlers/providers.d.ts +2 -0
  37. package/dist/daemon/api/handlers/providers.js +1 -1
  38. package/dist/daemon/peer/outbox.js +1 -1
  39. package/dist/daemon/peer/relay-client.js +1 -1
  40. package/dist/daemon/peer/relay-protocol.d.ts +1 -1
  41. package/dist/daemon/peer/relay-protocol.js +1 -1
  42. package/dist/types.d.ts +4 -0
  43. package/docs/ci-runner.md +65 -5
  44. package/docs/sdk/release-notes.md +6 -0
  45. package/package.json +3 -3
  46. package/packages/crouter-identity/package.json +1 -1
  47. package/runtime.lock.json +12 -12
  48. 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 n,sanitize as o,singleLine as d,truncate as u,wrap as k}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${n}`]:p(r);const i=r===""?[]:p(r);let a=-1;for(const t of s.slots){s.steps>1&&t.step!==a&&(i.length>0&&i.push(""),i.push(`${b}Step ${t.step+1}/${s.steps}${n}`),a=t.step),i.length>0&&i.push(""),i.push(`${b}${u(o(d(S(t))),f)}${n}`);const h=t.config.body;if(typeof h=="string"&&h!==""&&i.push(...p(h)),!x(t)){i.push(`${c}${o(t.kind)} \u2014 open in Northlight to answer this${n}`);continue}if(t.kind==="text")i.push(...p(t.config.initialText));else if(t.kind==="table")i.push(...p(w(t.config)));else if(t.kind==="cards"){for(const e of t.config.cards??[])if(i.push(` \u2022 ${u(o(d(e.subtitle===void 0?e.title:`${e.title} \u2014 ${e.subtitle}`)),Math.max(10,f-4))}`),e.body!==void 0)for(const l of k(o(e.body),Math.max(10,f-6)))i.push(` ${c}${l}${n}`)}else if(t.kind==="options"){for(const e of t.config.options)if(i.push(` \u2022 ${u(o(d(e.label)),Math.max(10,f-4))}`),e.description!==void 0)for(const l of k(o(e.description),Math.max(10,f-6)))i.push(` ${c}${l}${n}`)}else if(t.kind==="chart"){const e=t.config,l=e.title??`${e.x??""} vs ${(e.y??[]).map(g=>g.label??g.field).join(", ")}`;i.push(`${c}chart \u2014 ${o(e.kind??"chart")}: ${o(l)}${n}`)}}return i}$(D,"pageBodyLines");export{D as pageBodyLines};
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 J=Object.defineProperty;var h=(n,s)=>J(n,"name",{value:s,configurable:!0});import R from"string-width";import{isResponseBearingSlot as O}from"../../../core/human/page-schema.js";import{answerCommentAnchorLabel as K,describePageAnswer as Q}from"../../../core/human/answer.js";import{renderMarkdownBlockAwareLines as U}from"../../../core/termrender/termrender.js";import{commentAnchors as X,describeSlot as S,pageComplete as Z,slotActions as nn,slotAnswered as v,tableMarkdown as en,terminalAnswerable as A}from"./slots.js";import{RESET as o,BOLD as M,DIM as $,GREEN as E,YELLOW as w,CYAN as k,REVERSE as W,sanitize as a,singleLine as C,truncate as B,hline as g,wrap as b,centerHorizontal as N,clipLine as q,panLine as tn,visibleWidth as L}from"./ansi.js";const sn=8,y=h((n,s,e)=>U(n,s,e),"mdLines");function T(n){return Q(n.manifest,Object.fromEntries(n.responses),{completedAt:""})}h(T,"pageAnswer");function on(n,s){return s.id===void 0?void 0:n.slots.find(e=>e.id===s.id)}h(on,"answerSlot");function H(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(e=>e.picked.length===0?"none":e.picked.map(t=>t.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"}h(H,"answerSummary");function P(n,s){return K(n,s)}h(P,"commentLabel");function pn(n,s,e){const t=Math.min(Math.max(20,s-4),120),r=Math.max(20,s-4),i=["",` ${M}${k}${a(n.manifest.title)}${o}`];if(n.manifest.subtitle)for(const m of b(a(n.manifest.subtitle),t))i.push(` ${$}${m}${o}`);if(n.displayMarkdown!==""){i.push("");for(const m of y(n.displayMarkdown,t,r))i.push(` ${m}`)}else n.manifest.dialect==="html"&&i.push("",` ${$}HTML page \u2014 open in Northlight to read this${o}`);const c=n.slots.length>0;c&&i.push(` ${$}${g(t)}${o}`,"");const f=T(n);let u=-1,l;for(let m=0;m<n.slots.length;m++){const p=n.slots[m];n.manifest.steps>1&&p.step!==u&&(u!==-1&&i.push(""),i.push(` ${M}Step ${p.step+1}/${n.manifest.steps}${o}`),u=p.step);const I=p.id===void 0?void 0:n.responses.get(p.id),V=O(p)?v(n,p)?`${E}\u2713${o}`:`${$}\u25CB${o}`:`${$}\xB7${o}`,_=m===n.currentIndex?`${k}\u25B8${o} `:" ";m===n.currentIndex&&(l=i.length),i.push(` ${_}${V} ${B(C(S(p)),Math.max(10,s-16))}`),I!==void 0&&i.push(` ${$}${B(C(H(on(f,p))),Math.max(10,s-10))}${o}`)}if(n.manifest.slots.some(m=>O(m)&&!A(m))){i.push("");for(const m of b("This page must be answered in Northlight. Leaving it here keeps it in your inbox; x on the list cancels it.",t-2))i.push(` ${w}${m}${o}`)}c&&i.push(""),i.push(` ${$}${g(t)}${o}`);const x=!n.manifest.slots.some(O)?` ${$}u/d${o} scroll ${$}q${o} dismiss`:` ${$}enter${o} review ${$}\u2191\u2193/jk${o} navigate ${$}u/d${o} scroll ${$}q${o} close`;return G(i,x,n,s,e,t+2,n.followSelection?l:void 0)}h(pn,"renderOverview");function rn(n,s,e){const t=[...n],r=Math.max(0,Math.min(s,t.length)),i=z(n,e);let c=0;for(let l=0;l<i.length;l++)i[l].start<=r&&(c=l);const f=i.map((l,d)=>{if(d!==c)return t.slice(l.start,l.end).join("");const x=t.slice(l.start,r).join(""),m=r<l.end?t[r]:" ",p=r<l.end?t.slice(r+1,l.end).join(""):"";return`${x}${W}${m}${o}${p}`}),u=i[c];return r===u.end&&R(t.slice(u.start,u.end).join(""))>=e&&(f[c]=t.slice(u.start,u.end).join(""),f.splice(c+1,0,`${W} ${o}`)),f}h(rn,"renderInputBuffer");function xn(n,s){const e=[...n];let t=Math.max(0,Math.min(s,e.length));for(;t>0&&/\s/.test(e[t-1]);)t--;for(;t>0&&!/\s/.test(e[t-1]);)t--;return t}h(xn,"wordLeftIndex");function Mn(n,s){const e=[...n];let t=Math.max(0,Math.min(s,e.length));for(;t<e.length&&/\s/.test(e[t]);)t++;for(;t<e.length&&!/\s/.test(e[t]);)t++;return t}h(Mn,"wordRightIndex");function z(n,s){const e=[...n],t=Math.max(1,s),r=[];let i=0,c=0;for(let f=0;f<e.length;f++){const u=e[f];if(u===`
2
- `){r.push({start:i,end:f}),i=f+1,c=0;continue}const l=R(u);c+l>t?(r.push({start:i,end:f}),i=f,c=l):c+=l}return r.push({start:i,end:e.length}),r}h(z,"wrappedRowBounds");function gn(n,s,e,t){const r=z(n,t),i=Math.max(0,Math.min(s,[...n].length));let c=0;for(let u=0;u<r.length;u++)r[u].start<=i&&(c=u);const f=r[c+e];return f===void 0?i:Math.min(f.start+(i-r[c].start),f.end)}h(gn,"verticalCursor");function ln(n,s){return s.id===void 0?[]:n.responses.get(s.id)?.comments??[]}h(ln,"responseComments");function cn(n,s,e,t){if(s.id===void 0)return!1;const r=n.responses.get(s.id);return e==="option"?r?.selectedOptionIds?.includes(t)===!0:e==="card"?r?.selectedCardIds?.includes(t)===!0:e==="row"?r?.selectedRowIds?.includes(t)===!0:r?.selectedColumnIds?.includes(t)===!0}h(cn,"selected");function D(n,s){const e=n.slots[n.currentIndex],t=Math.min(Math.max(20,s-4),120),r=Math.max(20,s-4),i=["",` ${M}${k}[${n.currentIndex+1}/${n.slots.length}]${o} ${$}${a(n.manifest.title)}${o}`,` ${$}${g(t)}${o}`,"",` ${M}${a(S(e))}${o}`],c=e.config.body;if(typeof c=="string"&&c.length>0){i.push("");for(const l of y(c,t,r))i.push(` ${l}`)}if(A(e)){if(e.kind==="chart"){const l=e.config,d=l.title??`${l.x} vs ${(l.y??[]).map(x=>x.label??x.field).join(", ")}`;i.push("",` ${$}chart \u2014 ${a(l.kind)}: ${a(d)}${o}`)}else if(e.kind==="table"){i.push("");for(const d of y(en(e.config),t,r))i.push(` ${d}`);const l=e.config.label;l&&i.push(` ${$}${a(l)}${o}`)}else if(e.kind==="text"){const d=(e.id===void 0?void 0:n.responses.get(e.id))?.text??e.config.initialText;i.push("");for(const x of y(d,t,r))i.push(` ${x}`)}}else{i.push("");for(const l of b(`${a(e.kind)} \u2014 must be answered in Northlight; the terminal cannot submit this page`,Math.max(10,t-2)))i.push(` ${w}${l}${o}`);i.push(` ${$}q leaves it in your inbox; x on the list cancels it${o}`)}i.push("",` ${$}${g(t)}${o}`);let f;if(n.inputMode!==null){const l=n.inputMode.kind==="comment"?`Comment on ${P(e,n.inputMode.anchor)}`:n.inputMode.kind==="text"?"Edit text":"Other response";i.push(` ${M}${l}${o}`);for(const d of rn(n.inputMode.buffer,n.inputMode.cursor,Math.max(10,t-4)))i.push(` ${d}`);f=` ${$}enter save esc cancel${n.editorAvailable?" ^o editor":""}${o}`}else{nn(e).forEach((d,x)=>{const m=x===n.selectedAction?`${k}\u25B8${o}`:" ";if(d.kind==="select"){const p=cn(n,e,d.group,d.id)?`${E}\u2713${o}`:" ";if(i.push(` ${m} [${p}] ${d.shortcut?`[${d.shortcut}] `:""}${a(d.label)}`),d.description)for(const I of b(a(d.description),Math.max(10,t-6)))i.push(` ${$}${I}${o}`)}else{const p=d.kind==="freetext"?"r":d.kind==="edit"?"e":"a";i.push(` ${m} [${p}] ${a(d.label)}`)}});for(const d of ln(n,e))for(const x of b(`attached: ${P(e,d.anchor)} \u2014 ${a(d.text)}`,Math.max(10,t-2)))i.push(` ${$}${x}${o}`);n.hint&&i.push(` ${w}${a(n.hint)}${o}`),f=` ${$}n/p next/previous \u2191\u2193/jk actions${X(e).length>0?" c comment":""} u/d scroll \u2190\u2192/hl pan q close${o}`}const u=Math.max(0,...i.map(L));return{lines:i,footer:f,maxW:t,maxH:Math.max(0,u-Math.max(1,s))}}h(D,"itemLines");function wn(n,s,e){if(n.slots[n.currentIndex]===void 0){n.scrollOffset=0,n.hscrollOffset=0,n.hscrollMax=0;return}const t=D(n,s);n.hscrollMax=t.maxH,n.hscrollOffset=Math.max(0,Math.min(n.hscrollOffset,n.hscrollMax)),n.scrollOffset=Math.max(0,Math.min(n.scrollOffset,Math.max(0,t.lines.length-j(e))))}h(wn,"clampItemReviewScroll");function kn(n,s){n.hscrollOffset=Math.max(0,Math.min(n.hscrollMax,n.hscrollOffset+s*sn))}h(kn,"panItemReview");function bn(n,s,e){const t=D(n,s),r=j(e),i=n.scrollOffset,c=t.lines.slice(i,i+r);for(;c.length<r;)c.push("");const f=n.hscrollOffset>0?c.map(l=>tn(l,n.hscrollOffset,s)):c,u=Math.min(s,t.maxW+2);return r<e&&f.push(Y(t.footer,F(i,r,t.lines.length),t.maxW+2)),N(f.map(l=>q(l,s)),s,u)}h(bn,"renderItemReview");function On(n,s,e){const t=Math.min(Math.max(20,s-4),120),r=["",` ${M}${k}${a(n.manifest.title)} \u2014 Summary${o}`,` ${$}${g(t)}${o}`,""],i=T(n);for(const u of i.slots){const l=n.manifest.slots.find(m=>m.id===u.id),x=l!==void 0&&v(n,l)?`${E}\u2713${o}`:`${w}\u25CB${o}`;r.push(` ${x} ${M}${a(l===void 0?u.kind:S(l))}${o} ${$}${a(H(u))}${o}`)}const c=Z(n);if(!c){r.push("",` ${w}Cannot submit yet.${o}`);for(const u of n.slots.filter(O))v(n,u)||(r.push(` ${$}${u.id??u.kind} is unanswered${o}`),A(u)||r.push(` ${$}${u.id} must be answered in Northlight${o}`))}r.push("",` ${$}${g(t)}${o}`);const f=c?` ${$}enter${o} submit ${$}u/d${o} scroll ${$}q${o} back`:` ${$}u/d${o} scroll ${$}q${o} back`;return G(r,f,n,s,e,t+2)}h(On,"renderFinal");function j(n){return n>=2?n-1:n}h(j,"bodyRows");function F(n,s,e){if(s<=0||e<=s)return{marks:"",full:""};const t=Math.min(e,n+s),r=`${n>0?"\u25B4":" "}${t<e?"\u25BE":" "}`;return{marks:`${$}${r}${o}`,full:`${$}${r} ${n+1}\u2013${t}/${e}${o}`}}h(F,"scrollBadge");function Y(n,s,e){if(s.full==="")return n;const t=h(r=>e-L(n)-L(r)-2,"room");for(const r of[s.full,s.marks])if(t(r)>=2)return`${n}${" ".repeat(t(r))}${r}`;return` ${s.marks} ${n.trimStart()}`}h(Y,"pinnedFooter");function G(n,s,e,t,r,i,c){const f=j(r),u=Math.max(0,n.length-f);e.scrollOffset=Math.max(0,Math.min(e.scrollOffset,u)),c!==void 0&&f>0&&(c<e.scrollOffset?e.scrollOffset=c:c>=e.scrollOffset+f&&(e.scrollOffset=Math.min(u,c-f+1)));const l=n.slice(e.scrollOffset,e.scrollOffset+f);for(;l.length<f;)l.push("");return f<r&&l.push(Y(s,F(e.scrollOffset,f,n.length),i)),N(l.map(d=>q(d,t)),t,Math.min(t,i))}h(G,"centeredWindow");export{wn as clampItemReviewScroll,kn as panItemReview,On as renderFinal,rn as renderInputBuffer,bn as renderItemReview,pn as renderOverview,gn as verticalCursor,xn as wordLeftIndex,Mn as wordRightIndex,z as wrappedRowBounds};
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};
@@ -1 +1 @@
1
- var z=Object.defineProperty;var l=(e,n)=>z(e,"name",{value:n,configurable:!0});import{statSync as w}from"node:fs";import{isAbsolute as D,join as k}from"node:path";import{SUBTREE_NAMES as U,coreCommandPaths as B,extensibleCoreBranches as _}from"../../../build-root.js";import{discoverPluginCommandCandidates as F,validateEffectiveCommandPlugins as L,validatePluginCommands as q}from"../../../core/command-plugins/discovery.js";import{qualifiedTopLevelCommandName as C}from"../../../core/command-manifests/registry.js";import{listDirs as x,pathExists as A,walkFiles as Z}from"../../../core/fs-utils.js";import{currentSha as W,gitSync as G}from"../../../core/git.js";import{packageFiles as H}from"../../../core/graph/package-files.js";import{readMarketplaceManifest as K,readPluginManifest as E}from"../../../core/manifest.js";import{readConfig as O}from"../../../core/config.js";import{documentFields as J}from"../../../migrations/004-canvas-documents/fields.js";import{pluginMemoryDir as Q,projectScopeRoot as y}from"../../../core/scope.js";import{runtimeRoot as S}from"../../../core/layout.js";import{isGitUrl as V}from"../shared.js";import{INSTALLED_SOURCE_ID as $,marketplaceSourceId as M}from"./model.js";function I(e){const n=/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/.exec(e);return n===null?null:{core:[Number(n[1]),Number(n[2]),Number(n[3])],prerelease:n[4]?.split(".")??[]}}l(I,"parseSemver");function X(e,n){for(let r=0;r<e.core.length;r+=1){const o=e.core[r]-n.core[r];if(o!==0)return o}if(e.prerelease.length===0||n.prerelease.length===0)return e.prerelease.length===n.prerelease.length?0:e.prerelease.length===0?1:-1;for(let r=0;r<Math.max(e.prerelease.length,n.prerelease.length);r+=1){const o=e.prerelease[r],a=n.prerelease[r];if(o===void 0||a===void 0)return o===void 0?-1:1;const i=/^\d+$/.test(o),t=/^\d+$/.test(a);if(i&&t){const s=Number(o)-Number(a);if(s!==0)return s}else{if(i!==t)return i?-1:1;if(o!==a)return o<a?-1:1}}return 0}l(X,"compareSemver");function P(e,n){if(e===void 0||n===void 0)return!1;const r=I(e),o=I(n);return r!==null&&o!==null&&X(o,r)>0}l(P,"offeredVersionIsNewer");function Y(e){return e.startsWith("./")||e.startsWith("../")||!D(e)&&!V(e)}l(Y,"isMarketplaceRelativeSource");function j(e,n){return{kind:"installed",scope:e.scope,enabled:e.enabled,version:e.version,path:e.root,updateAvailable:P(e.version,n)}}l(j,"pluginInstallState");function ee(e,n){return{key:`${$}:${e.scope}:${e.name}`,name:e.name,ref:e.name,sourceId:$,...e.sourceMarketplace!==void 0?{marketplace:e.sourceMarketplace}:{},...n===void 0?{}:{version:n},description:e.manifest.description,keywords:[],owner:e.manifest.owner,source:e.manifest.source,install:j(e,n),root:e.root}}l(ee,"installedEntry");function v(e,n){return`${e}:${n}`}l(v,"marketplacePluginKey");function ne(e,n){const r=W(e.root)??void 0,o=G(["show","-s","--format=%cI","HEAD"],e.root),a=o.status===0&&o.stdout.trim()||void 0,i=e.manifest.plugins;return{name:e.name,scope:e.scope,url:e.url,ref:e.ref,path:e.root,owner:e.manifest.owner,version:e.manifest.version,pluginCount:i.length,installedCount:i.filter(t=>n.has(v(e.name,t.name))).length,updatableCount:i.filter(t=>{const s=n.get(v(e.name,t.name));return s!==void 0&&P(s.version,t.version)}).length,revision:r,revisionDate:a}}l(ne,"marketplaceDetail");function te(e,n,r){const o=r.get(v(e.name,n.name)),a=Y(n.source)?k(e.root,n.source):void 0;return{key:`${M(e.name,e.scope)}:${n.name}`,name:n.name,ref:`${e.name}/${n.name}`,sourceId:M(e.name,e.scope),marketplace:e.name,version:n.version,description:n.description,keywords:n.keywords??[],owner:e.manifest.owner,source:n.source,install:o===void 0?{kind:"absent"}:j(o,n.version),...o!==void 0?{root:o.root}:a!==void 0&&A(a)?{root:a}:{}}}l(te,"marketplaceEntry");function N(e,n){try{return O(e)}catch(r){return n.push(`Could not read ${e} config: ${b(r)}`),null}}l(N,"readableConfig");function re(e){const n=[...y()===null?[]:[{scope:"project",root:y()}],{scope:"user",root:S()}],r=[];for(const{scope:o,root:a}of n){const i=N(o,e);for(const t of x(k(a,"plugins"))){const s=k(a,"plugins",t);try{const c=E(s);if(c===null){e.push(`Plugin "${t}" has no readable manifest: ${s}`);continue}const u=i?.plugins[t];r.push({name:t,scope:o,root:s,manifest:c,enabled:typeof u?.enabled=="boolean"?u.enabled:!0,sourceMarketplace:typeof u?.source_marketplace=="string"?u.source_marketplace:void 0,version:typeof u?.version=="string"?u.version:c.version})}catch(c){e.push(`Could not read plugin "${t}": ${b(c)}`)}}}return r}l(re,"readInstalledPlugins");function oe(e,n,r){if(!Array.isArray(e))return r.push(`Marketplace "${n}" has an invalid plugins list.`),[];const o=[];for(const[a,i]of e.entries()){if(typeof i!="object"||i===null||Array.isArray(i)){r.push(`Marketplace "${n}" has an invalid plugin entry at index ${a}.`);continue}const t=i;if(typeof t.name!="string"||t.name===""||typeof t.source!="string"||t.source===""){r.push(`Marketplace "${n}" has a plugin entry without a name and source at index ${a}.`);continue}o.push({name:t.name,source:t.source,...typeof t.version=="string"?{version:t.version}:{},...typeof t.description=="string"?{description:t.description}:{},...Array.isArray(t.keywords)&&t.keywords.every(s=>typeof s=="string")?{keywords:t.keywords}:{}})}return o}l(oe,"marketplaceEntries");function se(e,n,r){if(typeof e!="object"||e===null||Array.isArray(e))return r.push(`Marketplace "${n}" has an invalid manifest.`),null;const o=e;if(typeof o.name!="string"||o.name==="")return r.push(`Marketplace "${n}" has a manifest without a name.`),null;const a=o.owner,i=typeof a=="object"&&a!==null&&!Array.isArray(a)?{...typeof a.name=="string"?{name:a.name}:{},...typeof a.email=="string"?{email:a.email}:{}}:void 0;return{name:o.name,plugins:oe(o.plugins,o.name,r),...typeof o.version=="string"?{version:o.version}:{},...i!==void 0?{owner:i}:{}}}l(se,"readableMarketplaceManifest");function ae(e){const n=[...y()===null?[]:[{scope:"project",root:y()}],{scope:"user",root:S()}],r=[];for(const{scope:o,root:a}of n){const i=N(o,e);for(const t of x(k(a,"marketplaces"))){const s=k(a,"marketplaces",t);try{const c=K(s);if(c===null){e.push(`Marketplace "${t}" has no readable manifest: ${s}`);continue}const u=se(c,t,e);if(u===null)continue;const d=i?.marketplaces[t];r.push({name:t,scope:o,root:s,manifest:u,url:d?.url??"",ref:d?.ref??"main"})}catch(c){e.push(`Could not read marketplace "${t}": ${b(c)}`)}}}return r}l(ae,"readMarketplaces");function xe(){const e=[],n=re(e),r=ae(e),o=new Map(n.filter(t=>t.sourceMarketplace!==void 0).map(t=>[v(t.sourceMarketplace,t.name),t])),a=new Map(r.flatMap(t=>t.manifest.plugins.map(s=>[v(t.name,s.name),s.version]))),i=[{id:$,kind:"installed",label:"Installed",entries:n.map(t=>ee(t,t.sourceMarketplace===void 0?void 0:a.get(v(t.sourceMarketplace,t.name)))).sort((t,s)=>t.name.localeCompare(s.name))}];for(const t of r.sort((s,c)=>s.name.localeCompare(c.name)))try{i.push({id:M(t.name,t.scope),kind:"marketplace",label:t.name,marketplace:ne(t,o),entries:t.manifest.plugins.map(s=>te(t,s,o)).sort((s,c)=>s.name.localeCompare(c.name))})}catch(s){e.push(`Could not load marketplace "${t.name}": ${b(s)}`)}return{sources:i,installScopes:["user",...y()===null?[]:["project"]],warnings:e}}l(xe,"loadCatalog");function R(e,n=[]){const r=[...n,e.name];return e.kind==="branch"?{path:r,kind:"branch",description:e.description,whenToUse:e.whenToUse,summary:e.summary,...e.tier!==void 0?{tier:e.tier}:{},...e.passthrough!==void 0?{passthrough:e.passthrough}:{},...e.rootEntry!==void 0?{rootEntry:e.rootEntry}:{},children:e.children.map(o=>R(o,r)),params:[],output:[],effects:[]}:{path:r,kind:"leaf",description:e.description,whenToUse:e.whenToUse,summary:e.summary,...e.tier!==void 0?{tier:e.tier}:{},children:[],params:e.params,output:e.output,effects:[...e.effects],..."rest"in e?{rest:{method:e.rest.method,path:e.rest.path,streaming:e.rest.streaming===!0}}:{}}}l(R,"commandNode");function ie(e){return H(Q(e)).files.map(n=>{const r=J(n.frontmatter,n.body,{missingPrivacy:"public"});let o;try{o=w(n.path).size}catch{}return{name:`${e.name}/${n.name}`,kind:r.kind==="preference"?"preference":"knowledge",...r.summary!==null?{shortForm:r.summary}:{},...r.preview!==""?{routingLine:r.preview}:{},path:n.path,...o!==void 0?{sizeBytes:o}:{},rules:r.rules,unlisted:r.unlisted,...r.whyItExists!==null?{rationale:r.whyItExists}:{},body:n.body}})}l(ie,"docDetails");function le(e){try{return Z(e).reduce((n,r)=>n+w(r).size,0)}catch{return}}l(le,"directorySize");async function Ae(e){if(e.root===void 0||!A(e.root))return null;try{const n=E(e.root);if(n===null)return null;const r=e.install.kind==="installed"?e.install.scope:"user",o={name:e.name,scope:r,root:e.root,manifest:n,enabled:e.install.kind!=="installed"||e.install.enabled},a=new Set(U),[i,t]=await Promise.all([B(),_()]);let s=q(o,a,i,t);const c=[];if(e.install.kind==="installed"&&e.install.enabled)try{s=L(a,void 0,void 0,i,t).find(p=>p.plugin.root===e.root)??s}catch(d){c.push({message:`Could not resolve the effective command registry: ${b(d)}`,next:"Repair the unreadable installed plugin manifest, then reopen the dossier."})}if(e.install.kind!=="installed"||!e.install.enabled){const d=new Map;for(const m of F())if(m.plugin.root!==e.root)for(const f of m.contributions){const h=d.get(f.node.name)??[];h.push(m.plugin),d.set(f.node.name,h)}const p=e.install.kind==="installed"?"enabling":"installing";s.contributions=s.contributions.map(m=>{const f=d.get(m.node.name);if(f===void 0||f.length===0)return m;const h=C({name:e.name,sourceMarketplace:e.marketplace},m.node.name),T=f.map(g=>`moves ${g.name}'s to "${C(g,m.node.name)}"`);return s.issues.push({code:"command_collision",plugin:o.name,path:m.node.name,message:`top-level command "${m.node.name}" is already claimed by ${f.map(g=>g.name).join(", ")} \u2014 ${p} mounts this one as "${h}" and ${T.join(" and ")}`,received:[...f.map(g=>g.name),o.name].join(", "),expected:"an origin-qualified top-level command for each claimant",next:`After ${p}, invoke "${h}".`}),{...m,node:{...m.node,name:h}}})}const u=n.transport;return{root:e.root,transport:u?.kind??null,...u?.kind==="exec"&&u.executable!==void 0?{transportTarget:u.executable}:{},...u?.kind==="http"?{transportTarget:u.endpoint}:{},commands:s.contributions.map(d=>R(d.node)),commandIssues:[...s.issues.map(({message:d,next:p})=>({message:d,...p!==void 0?{next:p}:{}})),...c],docs:ie(o),sizeBytes:le(e.root)}}catch{return null}}l(Ae,"loadCapability");function b(e){return e instanceof Error?e.message:String(e)}l(b,"errorMessage");export{Ae as loadCapability,xe as loadCatalog};
1
+ var z=Object.defineProperty;var l=(e,n)=>z(e,"name",{value:n,configurable:!0});import{statSync as w}from"node:fs";import{isAbsolute as D,join as k}from"node:path";import{SUBTREE_NAMES as U,coreCommandPaths as B,extensibleCoreBranches as _}from"../../../build-root.js";import{discoverPluginCommandCandidates as F,validateEffectiveCommandPlugins as L,validatePluginCommands as q}from"../../../core/command-plugins/discovery.js";import{qualifiedTopLevelCommandName as C}from"../../../core/command-manifests/registry.js";import{listDirs as x,pathExists as A,walkFiles as Z}from"../../../core/fs-utils.js";import{currentSha as W,gitSync as G}from"../../../core/git.js";import{packageFiles as H}from"../../../core/graph/package-files.js";import{readMarketplaceManifest as K,readPluginManifest as E}from"../../../core/manifest.js";import{readConfig as O}from"../../../core/config.js";import{documentFields as J}from"../../../migrations/004-canvas-documents/fields.js";import{pluginMemoryDir as Q,projectScopeRoot as y}from"../../../core/scope.js";import{runtimeRoot as S}from"../../../core/layout.js";import{isGitUrl as V}from"../shared.js";import{INSTALLED_SOURCE_ID as $,marketplaceSourceId as M}from"./model.js";function I(e){const n=/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/.exec(e);return n===null?null:{core:[Number(n[1]),Number(n[2]),Number(n[3])],prerelease:n[4]?.split(".")??[]}}l(I,"parseSemver");function X(e,n){for(let r=0;r<e.core.length;r+=1){const o=e.core[r]-n.core[r];if(o!==0)return o}if(e.prerelease.length===0||n.prerelease.length===0)return e.prerelease.length===n.prerelease.length?0:e.prerelease.length===0?1:-1;for(let r=0;r<Math.max(e.prerelease.length,n.prerelease.length);r+=1){const o=e.prerelease[r],a=n.prerelease[r];if(o===void 0||a===void 0)return o===void 0?-1:1;const i=/^\d+$/.test(o),t=/^\d+$/.test(a);if(i&&t){const s=Number(o)-Number(a);if(s!==0)return s}else{if(i!==t)return i?-1:1;if(o!==a)return o<a?-1:1}}return 0}l(X,"compareSemver");function P(e,n){if(e===void 0||n===void 0)return!1;const r=I(e),o=I(n);return r!==null&&o!==null&&X(o,r)>0}l(P,"offeredVersionIsNewer");function Y(e){return e.startsWith("./")||e.startsWith("../")||!D(e)&&!V(e)}l(Y,"isMarketplaceRelativeSource");function j(e,n){return{kind:"installed",scope:e.scope,enabled:e.enabled,version:e.version,path:e.root,updateAvailable:P(e.version,n)}}l(j,"pluginInstallState");function ee(e,n){return{key:`${$}:${e.scope}:${e.name}`,name:e.name,ref:e.name,sourceId:$,...e.sourceMarketplace!==void 0?{marketplace:e.sourceMarketplace}:{},...n===void 0?{}:{version:n},description:e.manifest.description,keywords:[],owner:e.manifest.owner,source:e.manifest.source,install:j(e,n),root:e.root}}l(ee,"installedEntry");function v(e,n){return`${e}:${n}`}l(v,"marketplacePluginKey");function ne(e,n){const r=W(e.root)??void 0,o=G(["show","-s","--format=%cI","HEAD"],e.root),a=o.status===0&&o.stdout.trim()||void 0,i=e.manifest.plugins;return{name:e.name,scope:e.scope,url:e.url,ref:e.ref,path:e.root,owner:e.manifest.owner,version:e.manifest.version,pluginCount:i.length,installedCount:i.filter(t=>n.has(v(e.name,t.name))).length,updatableCount:i.filter(t=>{const s=n.get(v(e.name,t.name));return s!==void 0&&P(s.version,t.version)}).length,revision:r,revisionDate:a}}l(ne,"marketplaceDetail");function te(e,n,r){const o=r.get(v(e.name,n.name)),a=Y(n.source)?k(e.root,n.source):void 0;return{key:`${M(e.name,e.scope)}:${n.name}`,name:n.name,ref:`${e.name}/${n.name}`,sourceId:M(e.name,e.scope),marketplace:e.name,version:n.version,description:n.description,keywords:n.keywords??[],owner:e.manifest.owner,source:n.source,install:o===void 0?{kind:"absent"}:j(o,n.version),...o!==void 0?{root:o.root}:a!==void 0&&A(a)?{root:a}:{}}}l(te,"marketplaceEntry");function N(e,n){try{return O(e)}catch(r){return n.push(`Could not read ${e} config: ${b(r)}`),null}}l(N,"readableConfig");function re(e){const n=[...y()===null?[]:[{scope:"project",root:y()}],{scope:"user",root:S()}],r=[];for(const{scope:o,root:a}of n){const i=N(o,e);for(const t of x(k(a,"plugins"))){const s=k(a,"plugins",t);try{const c=E(s);if(c===null){e.push(`Plugin "${t}" has no readable manifest: ${s}`);continue}const u=i?.plugins[t];r.push({name:t,scope:o,root:s,manifest:c,enabled:typeof u?.enabled=="boolean"?u.enabled:!0,sourceMarketplace:typeof u?.source_marketplace=="string"?u.source_marketplace:void 0,version:typeof u?.version=="string"?u.version:c.version})}catch(c){e.push(`Could not read plugin "${t}": ${b(c)}`)}}}return r}l(re,"readInstalledPlugins");function oe(e,n,r){if(!Array.isArray(e))return r.push(`Marketplace "${n}" has an invalid plugins list.`),[];const o=[];for(const[a,i]of e.entries()){if(typeof i!="object"||i===null||Array.isArray(i)){r.push(`Marketplace "${n}" has an invalid plugin entry at index ${a}.`);continue}const t=i;if(typeof t.name!="string"||t.name===""||typeof t.source!="string"||t.source===""){r.push(`Marketplace "${n}" has a plugin entry without a name and source at index ${a}.`);continue}o.push({name:t.name,source:t.source,...typeof t.version=="string"?{version:t.version}:{},...typeof t.description=="string"?{description:t.description}:{},...Array.isArray(t.keywords)&&t.keywords.every(s=>typeof s=="string")?{keywords:t.keywords}:{}})}return o}l(oe,"marketplaceEntries");function se(e,n,r){if(typeof e!="object"||e===null||Array.isArray(e))return r.push(`Marketplace "${n}" has an invalid manifest.`),null;const o=e;if(typeof o.name!="string"||o.name==="")return r.push(`Marketplace "${n}" has a manifest without a name.`),null;const a=o.owner,i=typeof a=="object"&&a!==null&&!Array.isArray(a)?{...typeof a.name=="string"?{name:a.name}:{},...typeof a.email=="string"?{email:a.email}:{}}:void 0;return{name:o.name,plugins:oe(o.plugins,o.name,r),...typeof o.version=="string"?{version:o.version}:{},...i!==void 0?{owner:i}:{}}}l(se,"readableMarketplaceManifest");function ae(e){const n=[...y()===null?[]:[{scope:"project",root:y()}],{scope:"user",root:S()}],r=[];for(const{scope:o,root:a}of n){const i=N(o,e);for(const t of x(k(a,"marketplaces"))){const s=k(a,"marketplaces",t);try{const c=K(s);if(c===null){e.push(`Marketplace "${t}" has no readable manifest: ${s}`);continue}const u=se(c,t,e);if(u===null)continue;const d=i?.marketplaces[t];r.push({name:t,scope:o,root:s,manifest:u,url:d?.url??"",ref:d?.ref??"main"})}catch(c){e.push(`Could not read marketplace "${t}": ${b(c)}`)}}}return r}l(ae,"readMarketplaces");function xe(){const e=[],n=re(e),r=ae(e),o=new Map(n.filter(t=>t.sourceMarketplace!==void 0).map(t=>[v(t.sourceMarketplace,t.name),t])),a=new Map(r.flatMap(t=>t.manifest.plugins.map(s=>[v(t.name,s.name),s.version]))),i=[{id:$,kind:"installed",label:"Installed",entries:n.map(t=>ee(t,t.sourceMarketplace===void 0?void 0:a.get(v(t.sourceMarketplace,t.name)))).sort((t,s)=>t.name.localeCompare(s.name))}];for(const t of r.sort((s,c)=>s.name.localeCompare(c.name)))try{i.push({id:M(t.name,t.scope),kind:"marketplace",label:t.name,marketplace:ne(t,o),entries:t.manifest.plugins.map(s=>te(t,s,o)).sort((s,c)=>s.name.localeCompare(c.name))})}catch(s){e.push(`Could not load marketplace "${t.name}": ${b(s)}`)}return{sources:i,installScopes:["user",...y()===null?[]:["project"]],warnings:e}}l(xe,"loadCatalog");function R(e,n=[]){const r=[...n,e.name];return e.kind==="branch"?{path:r,kind:"branch",description:e.description,whenToUse:e.whenToUse,summary:e.summary,...e.tier!==void 0?{tier:e.tier}:{},...e.passthrough!==void 0?{passthrough:e.passthrough}:{},...e.rootEntry!==void 0?{rootEntry:e.rootEntry}:{},children:e.children.map(o=>R(o,r)),params:[],output:[],effects:[]}:{path:r,kind:"leaf",description:e.description,whenToUse:e.whenToUse,summary:e.summary,...e.tier!==void 0?{tier:e.tier}:{},children:[],params:e.params,output:e.output,effects:[...e.effects],..."rest"in e?{rest:{method:e.rest.method,path:e.rest.path,streaming:e.rest.streaming===!0}}:{}}}l(R,"commandNode");function ie(e){return H(Q(e)).files.map(n=>{const r=J(n.frontmatter,n.body,{missingPrivacy:"public"});let o;try{o=w(n.path).size}catch{}return{name:`${e.name}/${n.name}`,kind:r.kind==="preference"?"preference":"knowledge",...r.summary!==null?{shortForm:r.summary}:{},...r.preview!==""?{routingLine:r.preview}:{},path:n.path,...o!==void 0?{sizeBytes:o}:{},rules:r.rules,unlisted:r.unlisted,...r.whyItExists!==null?{rationale:r.whyItExists}:{},body:n.body}})}l(ie,"docDetails");function le(e){try{return Z(e).reduce((n,r)=>n+w(r).size,0)}catch{return}}l(le,"directorySize");async function Ae(e){if(e.root===void 0||!A(e.root))return null;try{const n=E(e.root);if(n===null)return null;const r=e.install.kind==="installed"?e.install.scope:"user",o={name:e.name,scope:r,root:e.root,manifest:n,enabled:e.install.kind!=="installed"||e.install.enabled},a=new Set(U),[i,t]=await Promise.all([B(),_()]);let s=q(o,a,i,t);const c=[];if(e.install.kind==="installed"&&e.install.enabled)try{s=L(a,void 0,void 0,i,t).find(p=>p.plugin.root===e.root)??s}catch(d){c.push({message:`Could not resolve the effective command registry: ${b(d)}`,next:"Repair the unreadable installed plugin manifest, then reopen the dossier."})}if(e.install.kind!=="installed"||!e.install.enabled){const d=new Map;for(const m of F())if(m.plugin.root!==e.root)for(const f of m.contributions){const h=d.get(f.node.name)??[];h.push(m.plugin),d.set(f.node.name,h)}const p=e.install.kind==="installed"?"enabling":"installing";s.contributions=s.contributions.map(m=>{const f=d.get(m.node.name);if(f===void 0||f.length===0)return m;const h=C({name:e.name,sourceMarketplace:e.marketplace},m.node.name),T=f.map(g=>`moves ${g.name}'s to "${C(g,m.node.name)}"`);return s.issues.push({code:"command_collision",plugin:o.name,path:m.node.name,message:`top-level command "${m.node.name}" is already claimed by ${f.map(g=>g.name).join(", ")} \u2014 ${p} mounts this one as "${h}" and ${T.join(" and ")}`,received:[...f.map(g=>g.name),o.name].join(", "),expected:"an origin-qualified top-level command for each claimant",next:`After ${p}, invoke "${h}".`}),{...m,node:{...m.node,name:h}}})}const u=n.transport;return{root:e.root,transport:u?.kind==="machine"?"http":u?.kind??null,...u?.kind==="exec"&&u.executable!==void 0?{transportTarget:u.executable}:{},...u?.kind==="http"?{transportTarget:u.endpoint}:{},commands:s.contributions.map(d=>R(d.node)),commandIssues:[...s.issues.map(({message:d,next:p})=>({message:d,...p!==void 0?{next:p}:{}})),...c],docs:ie(o),sizeBytes:le(e.root)}}catch{return null}}l(Ae,"loadCapability");function b(e){return e instanceof Error?e.message:String(e)}l(b,"errorMessage");export{Ae as loadCapability,xe as loadCatalog};
@@ -14,4 +14,4 @@ ${c.join(`
14
14
  ${d.map(f=>`- ${f}`).join(`
15
15
  `)}`,{issues:d})}const[r,c]=await Promise.all([D(),I()]),l=J({name:e.name,scope:n,root:t,manifest:e,enabled:!0},new Set(_),r,c);if(l.issues.length>0)throw W(`plugin command behavior at ${o} is invalid`,l.issues);await Rn(e,t,n,i,o,s);const p=g(t,"memory"),v=Qe(n,i,{candidate:e});for(const d of Je(p,f=>f.endsWith(".md"),f=>f.startsWith("."))){let f;try{f=tn(Ge(d)).data}catch(k){throw m(`plugin memory document ${L(t,d)} has invalid YAML frontmatter: ${k instanceof Error?k.message.split(`
16
16
  `)[0]:String(k)}`)}const y=en(f);if(y!==null)throw m(`plugin memory document ${L(t,d)} is invalid: ${y}`);for(const k of Xe(f?.extensions,v))throw m(`plugin memory document ${L(t,d)} is invalid: ${k.path}: ${k.message}`)}return Sn(e,t,n,o,a)}u(j,"assertPluginCandidateValid");function Tn(e){return{kind:"http",endpoint:new URL(e.endpoint).origin,...e.authEnv!==void 0?{authEnv:e.authEnv}:{}}}u(Tn,"invocationTransport");async function qn(e,t,n,i,o){const s=await j(o,n,t,i,`${n}/.crouter-plugin/plugin.json`),a={name:e,scope:t,root:n,manifest:o,enabled:!0},[r,c]=await Promise.all([D(),I()]),l=J(a,new Set(_),r,c);if(l.issues.length>0)throw W(`bundle staged package for "${e}" is invalid`,l.issues);return s}u(qn,"validateStagedBundle");function ve(e){return e.sourceMarketplace!==void 0||K(e.root)||F(e.root)}u(ve,"isSourceInstalled");function ye(e){throw m(`HTTP plugin "${e}" uses the removed commands-only endpoint format`,{next:`Run \`crtr pkg plugin remove ${e}\`, then reinstall it with \`crtr pkg plugin install --name ${e} --endpoint <archive-url>\`.`})}u(ye,"rejectLegacyHttpPlugin");function An(e,t,n){if(!b(n))return;const i=q(e,t);if(i?.manifest.bundle!==void 0)return i;throw i?.manifest.transport?.kind==="http"&&i!==void 0&&!ve(i)&&ye(e),m(`plugin "${e}" already exists at ${n}; run \`crtr pkg plugin remove ${e}\` before installing the archive plugin.`)}u(An,"existingBundleRoot");function ke(e,t,n,i){const o=he(e,t.endpoint,t.authEnv),s={endpoint:o.endpoint,...o.authEnv!==void 0?{authEnv:o.authEnv}:{}},a=g(A(n),e);return An(e,n,a),{bundle:s,transport:Tn(s),scopeRootPath:i,root:a}}u(ke,"resolveBundleTarget");function we(e,t){return{name:e,endpoint:t.endpoint,...t.authEnv!==void 0?{authEnv:t.authEnv}:{}}}u(we,"fetchTarget");const Cn=6e4,Mn=1e4;async function be(e,t,n,i){const o=t==="project"?O():R();return M(g(o,"tmp")),We(oe(o,e),()=>i(o),{timeoutMs:n,timeoutError:u(()=>m(`timed out waiting for another crtr process to finish replacing plugin "${e}"`,{next:`Re-run once that process has finished, or check for a stalled crtr holding ${oe(o,e)}.`}),"timeoutError")})}u(be,"withBundleSwapLock");async function $e(e,t,n,i){return be(e,n,Cn,async o=>{const s=ke(e,t,n,o),a=await ue(we(e,s.bundle));if(a.status!==200){const r=`HTTP plugin bundle fetch failed for "${e}": ${a.message}`;throw a.status==="auth_env_missing"?w(r):a.status==="cli_unreachable"?Be(r):m(r)}return Pe(e,n,s,a.raw,a.etag,i)})}u($e,"replaceBundlePlugin");async function kt(e,t,n,i){return be(e,n,Mn,async o=>{try{const s=ke(e,t,n,o),a=i.conditional?Ie(n).plugins[e]?.etag:void 0,r={...a!==void 0?{ifNoneMatch:a}:{},...i.timeoutMs!==void 0?{timeoutMs:i.timeoutMs}:{}},c=await ue(we(e,s.bundle),r);if(c.status===304)return;if(c.status!==200)throw m(`HTTP plugin bundle revalidation failed for "${e}": ${c.message}`);const l=dn(c.raw);if(pn(e,n,s.root,l)){mn(n,e,c.etag,{swapped:!1});return}return await Pe(e,n,s,c.raw,c.etag,i,l)}finally{Ln(n,e)}})}u(kt,"revalidateBundlePlugin");function Ln(e,t){U(e,n=>{n.plugins[t]===void 0&&(n.plugins[t]={}),n.plugins[t].checked_at=G()})}u(Ln,"recordPluginCheck");async function Pe(e,t,n,i,o,s,a){const{bundle:r,transport:c,scopeRootPath:l,root:p}=n,v=await Hn(e,i,{scope:t,scopeRootPath:l,root:p,bundle:r,transport:c,etag:o,enable:s.enable,...a!==void 0?{version:a}:{}}),{validated:d,version:f,staged:y}=v,k=await Y(e,t);if(k===void 0)throw m(`bundle plugin "${e}" has no staged command report`);const $=await Q(e,t);return{name:e,scope:t,path:p,transport:"http",version:f,docs:d.memory.length,kinds:Object.keys(d.kinds??{}).length,pageComponents:(d.page_components??[]).length,commands:k,...$!==void 0?{hooks:$}:{},...y!==void 0?{memory:y}:{}}}u(Pe,"applyBundleArchive");async function wt(e){const[t,n]=await Promise.all([D(),I()]);return fn(e,{reservedCoreNames:new Set(_),coreCommandPaths:t,extensibleCoreBranches:n})}u(wt,"validatePluginArchive");async function Hn(e,t,n){const[i,o]=await Promise.all([D(),I()]);return un(e,t,{...n,validation:{reservedCoreNames:new Set(_),coreCommandPaths:i,extensibleCoreBranches:o},validateStaged:u((s,a)=>qn(e,n.scope,s,n.scopeRootPath,a),"validateStaged"),invalid:u((s,a)=>W(s,a),"invalid")})}u(Hn,"installPluginArchive");async function Bn(e,t,n,i){const o=he(e,t,n);return $e(e,{endpoint:o.endpoint,...o.authEnv!==void 0?{authEnv:o.authEnv}:{}},i,{enable:!0})}u(Bn,"installHttpPlugin");function _n(e){if(e.startsWith("./")||e.startsWith("../")||e.startsWith("/"))return{kind:"local",value:e};if(me(e))return{kind:"git",value:e};const t=e.split("/");if(t.length===2&&t[0]!==""&&t[1]!=="")return{kind:"marketplace",value:{marketplace:t[0],plugin:t[1]}};throw w(`invalid plugin install ref: ${e}
17
- Accepted forms: marketplace/plugin, git URL (https://, ssh://, git@, file://, or *.git), or local path starting ./, ../, or /. Bare plugin names are not install refs.`)}u(_n,"classifyInstallRef");function je(e){return e.startsWith("./")||e.startsWith("../")||!Me(e)&&!me(e)}u(je,"isMarketplaceRelativeSource");async function xe(e,t,n){const i=t.get(e);if(i!==void 0)return i;const o=De(e);if(o===null){if(n.strict)throw h(`marketplace not found: ${e}`);return null}const s=se(o.root);if(s===null)throw h(`marketplace manifest not found: ${o.root}/.crouter-marketplace/marketplace.json`);if(!K(o.root)){const l={marketplace:o,manifest:s,pull:{updated:!1,sha:S(o.root)??""}};return t.set(e,l),l}const a=S(o.root),r=g(ee(o.root),`.${e}.${process.pid}.${Date.now()}.candidate`),c=`${r}.previous`;try{V(o.url,r,{ref:o.ref,depth:1});const l=se(r);if(l===null)throw h(`marketplace manifest not found in candidate checkout: ${r}/.crouter-marketplace/marketplace.json`);const p=ne().filter(d=>d.sourceMarketplace===e&&F(d.root));for(const d of p){const f=l.plugins.find(Te=>Te.name===d.name);if(f===void 0||!je(f.source))throw m(`marketplace candidate removes relative-source plugin "${d.name}" while it is installed`);const y=ze(d.root);if(y===null)throw m(`installed marketplace plugin "${d.name}" has no readable symlink target`);const k=L(o.root,N(ee(d.root),y)),$=N(r,f.source);if($!==N(r,k))throw m(`marketplace candidate moves installed relative-source plugin "${d.name}" from ${k} to ${f.source}`);if(!b($))throw h(`marketplace candidate source path does not exist for plugin "${d.name}": ${$}`);const X=x($);if(X===null)throw h(`plugin manifest not found in marketplace candidate: ${$}/.crouter-plugin/plugin.json`);const Se=d.scope==="project"?C("project"):R();await j(X,$,d.scope,Se,`${$}/.crouter-plugin/plugin.json`)}P(o.root,c);try{P(r,o.root)}catch(d){throw P(c,o.root),d}E(c);for(const d of p){const f=l.plugins.find(y=>y.name===d.name);Re(d.scope,d.name,f.version??d.manifest.version)}U(o.scope,d=>{d.marketplaces[e]===void 0&&(d.marketplaces[e]={}),d.marketplaces[e].last_updated=G()});const v={marketplace:o,manifest:l,pull:{updated:a!==S(o.root),sha:S(o.root)??""}};return t.set(e,v),v}finally{E(r),b(c)&&!b(o.root)&&P(c,o.root)}}u(xe,"refreshMarketplace");function Re(e,t,n){B(e,i=>{const o=i.plugins[t];o!==void 0?n!==void 0&&(o.version=n):i.plugins[t]={enabled:!0,version:n}}),U(e,i=>{i.plugins[t]===void 0&&(i.plugins[t]={}),i.plugins[t].last_updated=G()})}u(Re,"updatePluginVersion");async function Dn(e,t,n){const i=n==="project"?O():R();z(n,i);const o=A(n),s=Ye(e),a=g(i,"tmp",`${s}.${process.pid}.${Date.now()}`);M(g(i,"tmp"));try{V(e,a,{ref:t,depth:1});const r=x(a);if(r===null)throw m(`cloned repo does not contain a valid .crouter-plugin/plugin.json: ${e}`);const c=await j(r,a,n,i,`${e}/.crouter-plugin/plugin.json`),l=g(o,r.name);if(b(l))throw m(`plugin "${r.name}" is already installed at ${l}`);return M(o),P(a,l),B(n,p=>{p.plugins[r.name]={enabled:!0,version:r.version}}),{name:r.name,scope:n,path:l,...c!==void 0?{memory:c}:{}}}finally{E(a)}}u(Dn,"installFromGit");async function In(e,t){const n=N(e),i=x(n);if(i===null)throw h(`plugin manifest not found at ${n}/.crouter-plugin/plugin.json`);const o=t==="project"?O():R(),s=await j(i,n,t,o,`${n}/.crouter-plugin/plugin.json`,void 0,"linked");z(t,o);const a=g(A(t),i.name);if(b(a))throw m(`plugin "${i.name}" is already installed at ${a}`);return ae(n,a),B(t,r=>{r.plugins[i.name]={enabled:!0,version:i.version}}),{name:i.name,scope:t,path:a,...s!==void 0?{memory:s}:{}}}u(In,"installFromLocal");async function Nn(e,t){const n=await xe(e.marketplace,new Map,{strict:!0});if(n===null)throw h(`marketplace not found: ${e.marketplace}`);const i=n.manifest.plugins.find(l=>l.name===e.plugin);if(i===void 0)throw h(`plugin "${e.plugin}" not found in marketplace "${e.marketplace}"`);const o=C(t);z(t,o);const s=g(A(t),e.plugin);if(b(s))throw m(`plugin "${e.plugin}" is already installed at ${s}`);let a,r;if(je(i.source)){const l=g(n.marketplace.root,i.source);if(!b(l))throw h(`plugin source path does not exist: ${l}`);const p=x(l);if(p===null)throw h(`plugin manifest not found at ${l}/.crouter-plugin/plugin.json`);r=await j(p,l,t,o,`${l}/.crouter-plugin/plugin.json`,void 0,"linked"),ae(l,s),a=p}else{const l=g(o,"tmp",`${e.plugin}.${process.pid}.${Date.now()}`);M(g(o,"tmp"));try{V(i.source,l,{depth:1});const p=x(l);if(p===null)throw h(`plugin manifest not found at ${l}/.crouter-plugin/plugin.json`);r=await j(p,l,t,o,`${l}/.crouter-plugin/plugin.json`),M(A(t)),P(l,s),a=p}finally{E(l)}}const c=i.version??a.version;return B(t,l=>{const p={enabled:!0,source_marketplace:e.marketplace};c!==void 0&&(p.version=c),l.plugins[e.plugin]=p}),{name:e.plugin,scope:t,path:s,...r!==void 0?{memory:r}:{}}}u(Nn,"installFromMarketplace");async function Un(e){if(F(e.root)){const r=x(e.root);if(r===null)throw h(`plugin manifest not found at ${e.root}/.crouter-plugin/plugin.json`);const c=e.scope==="project"?C("project"):R(),l=await j(r,e.root,e.scope,c,`${e.root}/.crouter-plugin/plugin.json`,void 0,"linked");return{updated:!1,sha:S(e.root)??"",manifest:r,...l!==void 0?{memory:l}:{}}}if(!K(e.root)){const r=x(e.root);if(r===null)throw h(`plugin manifest not found at ${e.root}/.crouter-plugin/plugin.json`);const c=e.scope==="project"?C("project"):R(),l=await j(r,e.root,e.scope,c,`${e.root}/.crouter-plugin/plugin.json`);return{updated:!1,sha:"",manifest:r,...l!==void 0?{memory:l}:{}}}const t=re(["config","--get","remote.origin.url"],e.root).stdout.trim();if(t==="")throw m(`plugin "${e.name}" has no origin remote to stage an update from`);const n=re(["symbolic-ref","--short","HEAD"],e.root).stdout.trim();if(n==="")throw m(`plugin "${e.name}" is detached at its installed revision and has no tracked branch to update`);const i=e.scope==="project"?C("project"):R(),o=g(i,"tmp",`${e.name}.${process.pid}.${Date.now()}.candidate`),s=`${o}.previous`,a=S(e.root);M(g(i,"tmp"));try{V(t,o,{ref:n,depth:1});const r=x(o);if(r===null)throw h(`plugin manifest not found in update candidate: ${o}/.crouter-plugin/plugin.json`);const c=await j(r,o,e.scope,i,`${o}/.crouter-plugin/plugin.json`);P(e.root,s);try{P(o,e.root)}catch(p){throw P(s,e.root),p}E(s);const l=S(e.root)??"";return{updated:a!==l,sha:l,manifest:r,...c!==void 0?{memory:c}:{}}}finally{E(o),b(s)&&!b(e.root)&&P(s,e.root)}}u(Un,"updateGitPluginCandidate");async function Ee(e,t,n){if(e.manifest.provider!==void 0)return{name:e.name,transport:"http",updated:!1,warnings:[`"${e.name}" is a capability provider bundle; the runtime refreshes it on grant sync`]};const i=ve(e);if(!i&&e.manifest.bundle!==void 0){const p=await $e(e.name,e.manifest.bundle,e.scope,{enable:e.enabled});return{name:e.name,transport:"http",updated:!0,version:p.version,docs:p.docs,kinds:p.kinds,pageComponents:p.pageComponents,commands:p.commands,...p.hooks!==void 0?{hooks:p.hooks}:{},...p.memory!==void 0?{memory:p.memory}:{}}}e.manifest.transport?.kind==="http"&&!i&&ye(e.name);let o,s=!1;if(e.sourceMarketplace!==void 0){const p=await xe(e.sourceMarketplace,t,n);p!==null&&(o=p.manifest.plugins.find(d=>d.name===e.name)?.version,s=p.pull.updated)}const a=await Un(e);Re(e.scope,e.name,o??a.manifest.version);const r=await Y(e.name,e.scope),c=await Q(e.name,e.scope),l=[...ge(a.manifest),...await Z(a.manifest)];return{name:e.name,...e.manifest.transport!==void 0?{transport:e.manifest.transport.kind}:{},updated:s||a.updated,sha:a.sha,...r!==void 0?{commands:r}:{},...c!==void 0?{hooks:c}:{},...a.memory!==void 0?{memory:a.memory}:{},...l.length>0?{warnings:l}:{}}}u(Ee,"updateOnePlugin");async function T(e){const n=(await Le().documents.reconcilePackages().catch(i=>He(i))).conflicts.find(i=>i.plugin===e);if(n)throw new _e("conflict",`plugin "${e}" is installed but its documents are not on the canvas: ${n.message}`,yn.GENERAL,{plugin:e,next:"Uninstall the other package providing that plugin name, then run `crtr pkg plugin update --name "+e+"`."})}u(T,"reconcilePluginDocuments");const bt=H({name:"install",description:"install from a ref or HTTP endpoint",whenToUse:"installing one plugin from a marketplace ref, git URL, local path, or an HTTP directory-archive endpoint",help:{name:"pkg plugin install",summary:"install a plugin from a ref or HTTP endpoint",params:[{kind:"positional",name:"install-ref",type:"string",required:!1,constraint:"Plugin install ref. Provide exactly one of INSTALL-REF or --endpoint. Accepted ref forms: marketplace/plugin; git URL starting https://, ssh://, git@, file://, or ending .git; local path starting ./, ../, or /."},{kind:"flag",name:"endpoint",type:"string",required:!1,constraint:"HTTP plugin directory-archive endpoint. Provide exactly one of --endpoint or INSTALL-REF. Must be an absolute https: URL; http: is permitted only for localhost, any *.localhost host, 127.0.0.1, [::1], ::1, or host.docker.internal. Credentials and fragments are rejected."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Required with --endpoint and rejected without it. Lowercase kebab-case plugin handle."},{kind:"flag",name:"auth-env",type:"string",required:!1,constraint:"Optional environment variable holding the HTTP bearer token. Accepted only with --endpoint; its value is never stored."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: project if available, else user."},{kind:"flag",name:"ref",type:"string",required:!1,constraint:"Git ref (branch/tag) for git URL installs. Rejected with --endpoint; ignored for marketplace and local path refs."}],output:[{name:"name",type:"string",required:!0,constraint:"Plugin name as installed."},{name:"scope",type:"string",required:!0,constraint:"Scope the plugin was installed into."},{name:"path",type:"string",required:!0,constraint:"Absolute path to the installed plugin directory."},{name:"transport",type:"string",required:!1,constraint:"exec or http when the plugin declares commands. Always http for an --endpoint install."},{name:"version",type:"string",required:!1,constraint:"First 12 hex characters of the fetched archive SHA-256 for an --endpoint install."},{name:"docs",type:"integer",required:!1,constraint:"Memory document count written from an --endpoint archive."},{name:"kinds",type:"integer",required:!1,constraint:"Kind-registry entry count declared by an --endpoint archive\u2019s bundle.json."},{name:"pageComponents",type:"integer",required:!1,constraint:"Page-component registration count declared by an --endpoint archive\u2019s bundle.json."},{name:"commands",type:"object",required:!1,constraint:"Present only when the plugin declares a command manifest. {mounts: string[] (accepted command paths now live for the next invocation), issues: object[] (typed validation issues that rejected a contribution \u2014 {code, path?, message, received, expected, next})}. Validated statically; commands are never executed."},{name:"hooks",type:"object",required:!1,constraint:"Present only when the plugin declares hooks. {manifestPath?, executablePath?, declarations: {target, phase, op, description, effects}[], lifecycle?: {event, phase, op, description, effects}[], issues: object[], trust: string}. Command targets, lifecycle events, and replacement collisions are validated statically; hook executables are never run."},{name:"warnings",type:"string[]",required:!1,constraint:"Advisory missing PATH executable requirements declared by the installed plugin. The install still succeeds and leaves the plugin enabled."},{name:"memory",type:"object",required:!1,constraint:"Present only when the plugin ships documents (its `memory/` folder). {docs: integer (documents checked), linkedSource?: string (the caller-owned directory, present only when the install keeps that directory as a live link)}."}],outputKind:"object",effects:["A ref install clones, links, or copies the plugin into the scope plugins directory and registers it with enabled=true. Marketplace installs refresh the source marketplace before resolving the plugin entry.","An --endpoint install fetches and validates one authenticated uncompressed tar archive before writing. It replaces the complete plugin directory with synthesized provenance and invocation metadata, commands.json, memory docs, and, when declared, hooks.json plus the hooks/ tree; it also copies any kind-registry entries the bundle.json declares (they join the launch registry below this scope\u2019s own config.json kinds), and any page components it declares (they join the active page-component catalog alongside the user config entries); a fetch or validation failure preserves the prior package.","If the plugin declares a command manifest, its commands go live on the next crtr invocation. Exec commands run trusted local code only when explicitly invoked; HTTP commands call their declared endpoint only when explicitly invoked.","Plugin command hooks go live on the next crtr invocation. They run implicitly with caller authority, may receive normalized inputs and after results, may block or replace declared targets, and may transmit received data. Lifecycle hooks run unprompted at every declared event before node launch and may transmit node identity and resolved paths. Hook executables are never run during install or update validation.","A plugin manifest `requires` declaration is checked against the PATH a node bash receives after install. Missing executables emit a warning with the declared install hint but never fail the install, disable the plugin, or remove its bare-binary contributions.","Before activation the candidate\u2019s `memory/` documents are checked for identity findings and refused if it cannot mount; a local path or marketplace-relative source stays a live link, named as `memory.linkedSource`. A refused package installs nothing."]},run:u(async e=>{const t=e.installRef,n=e.endpoint,i=e.name,o=e.authEnv,s=e.scope,a=e.ref,r=wn(s);if(t===void 0==(n===void 0))throw w("provide exactly one of INSTALL-REF or --endpoint");if(n!==void 0){if(i===void 0)throw w("--name is required with --endpoint");if(a!==void 0)throw w("--ref is only valid with a git INSTALL-REF, not --endpoint");const y=await Bn(i,n,o,r);return await T(i),y}if(i!==void 0)throw w("--name is only valid with --endpoint");if(o!==void 0)throw w("--auth-env is only valid with --endpoint");const c=_n(t),l=c.kind==="marketplace"?await Nn(c.value,r):c.kind==="local"?await In(c.value,r):await Dn(c.value,a,r),p=q(l.name,l.scope),v=await Y(l.name,l.scope),d=await Q(l.name,l.scope),f=p===null?[]:[...ge(p.manifest),...await Z(p.manifest)];return await T(l.name),{...l,...p?.manifest.transport!==void 0?{transport:p.manifest.transport.kind}:{},...v!==void 0?{commands:v}:{},...d!==void 0?{hooks:d}:{},...f.length>0?{warnings:f}:{}}},"run")}),$t=H({name:"remove",description:"remove plugin and directory",whenToUse:"uninstalling a plugin: deletes its directory and removes it from config",help:{name:"pkg plugin remove",summary:"remove a plugin and its directory from the given scope",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Plugin name to remove."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: searches every scope."}],output:[{name:"scopes",type:"string[]",required:!0,constraint:"Scopes the plugin was removed from."}],outputKind:"object",effects:["Deletes the plugin directory. Removes the plugin entry from config.json."]},run:u(async e=>{const t=e.name,n=e.scope;if(t==="crtr")throw w('cannot remove builtin plugin "crtr" \u2014 it ships with the binary');let i;if(n!==void 0){const s=te(n);if(s==="builtin")throw w("cannot remove plugins from builtin scope");i=s==="all"?["project","user"].filter(a=>a!=="project"||ie()!==null):[s]}else i=["project","user"].filter(s=>s!=="project"||ie()!==null);const o=[];for(const s of i){const a=A(s);if(a===null)continue;const r=g(a,t);b(r)&&(E(r),B(s,c=>{delete c.plugins[t]}),U(s,c=>{delete c.plugins[t]}),o.push(s))}if(o.length===0)throw h(`plugin not found: ${t}`);return await T(),{scopes:o}},"run")}),Pt=H({name:"enable",description:"enable a plugin",whenToUse:"reactivating a plugin that was previously disabled so its memory docs resolve again",help:{name:"pkg plugin enable",summary:"enable a plugin in the given scope",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Plugin name to enable."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: scope where the plugin is installed."}],output:[{name:"scope",type:"string",required:!0,constraint:"Scope the change was applied in."},{name:"memory",type:"object",required:!1,constraint:"Present only when the plugin ships documents (its `memory/` folder). {docs: integer (documents checked), linkedSource?: string (the caller-owned directory, present only for a symlinked install)}."}],outputKind:"object",effects:["Validates the installed package before any config change: its `memory/` documents are checked for identity findings and refused if it cannot mount. A refusal leaves the plugin disabled.","Sets plugin enabled=true in config.json."]},run:u(async e=>{const t=e.name,n=kn(t,e.scope);if(n===null)throw h(`plugin not found: ${t}`);const i=x(n.root);if(i===null)throw h(`plugin manifest not found at ${n.root}/.crouter-plugin/plugin.json`);const o=n.scope==="project"?C("project"):R(),s=await j(i,n.root,n.scope,o,`${n.root}/.crouter-plugin/plugin.json`,void 0,F(n.root)?"linked":"package"),a=await fe(e,!0);return await T(t),{scope:a.scope,...s!==void 0?{memory:s}:{}}},"run")}),jt=H({name:"disable",description:"disable without removing",whenToUse:"hiding a plugin from resolution without deleting it, so you can re-enable it later",help:{name:"pkg plugin disable",summary:"disable a plugin (keeps files, hides from resolution)",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Plugin name to disable."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: scope where the plugin is installed."}],output:[{name:"scope",type:"string",required:!0,constraint:"Scope the change was applied in."}],outputKind:"object",effects:["Sets plugin enabled=false in config.json."]},run:u(async e=>{const t=await fe(e,!1);return await T(),{scope:t.scope}},"run")}),xt=H({name:"update",description:"update installed plugin content",whenToUse:"refreshing one named plugin or all installed plugins: archive plugins replace their complete package, while source-installed plugins retain their source update behavior",help:{name:"pkg plugin update",summary:"refresh one or all installed plugins",params:[{kind:"flag",name:"name",type:"string",required:!1,constraint:"Plugin name to update. Omit to update all installed plugins."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Narrows resolution."}],output:[{name:"updated",type:"object[]",required:!0,constraint:"One entry per plugin processed: {name, transport?, updated, sha?, version?, docs?, kinds?, pageComponents?, commands?, hooks?, memory?, warnings?, error?}. memory is present for a plugin shipping documents and reports the same memory check install performs: {docs, linkedSource?}. Archive plugins report transport=http and replace their complete directory after an authenticated refetch; version is the archive content identifier, docs is the written memory-document count, kinds is the declared kind-registry entry count, and pageComponents is the declared page-component registration count. sha is present for git updates. A bulk update reports and skips a failed archive plugin. commands is present for validated command plugins: {mounts: string[] (accepted command paths), issues: object[]}. hooks is present for plugins declaring hooks: {manifestPath?, executablePath?, declarations: {target, phase, op, description, effects}[], lifecycle?: {event, phase, op, description, effects}[], issues: object[], trust: string}; it is static and never executes hooks. warnings lists advisory PATH requirements that remain absent after update."}],outputKind:"object",effects:["Archive plugins unconditionally re-fetch, validate, and replace their complete package; a named failure exits nonzero and preserves the prior package. Bulk update reports a failed archive plugin and continues. Source-installed plugins retain their source update, version, and last_updated behavior.","A capability provider bundle (installed by the runtime from the directory) is skipped with a warning: the runtime refreshes it on grant sync.","Each candidate passes the same pre-activation memory check install performs."]},run:u(async e=>{const t=e.name,n=e.scope,i=n===void 0?void 0:te(n),o=new Map;if(t!==void 0){const r=i===void 0||i==="all"||i==="builtin"?q(t):q(t,i);if(r===null)throw h(`plugin not found: ${t}${i===void 0||i==="all"||i==="builtin"?"":` (scope: ${i})`}`);const c=await Ee(r,o,{strict:!0});return await T(t),{updated:[c]}}const s=ne().filter(r=>r.scope!=="builtin"&&(i===void 0||i==="all"||r.scope===i)),a=[];for(const r of s)try{a.push(await Ee(r,o,{strict:!1}))}catch(c){a.push({name:r.name,...r.manifest.transport!==void 0?{transport:r.manifest.transport.kind}:{},updated:!1,error:c instanceof Error?c.message:String(c)})}return await T(),{updated:a}},"run")});export{j as assertPluginCandidateValid,Nn as installFromMarketplace,Hn as installPluginArchive,je as isMarketplaceRelativeSource,jt as pluginDisable,Pt as pluginEnable,bt as pluginInstall,$t as pluginRemove,xt as pluginUpdate,kt as revalidateBundlePlugin,wt as validatePluginArchive};
17
+ Accepted forms: marketplace/plugin, git URL (https://, ssh://, git@, file://, or *.git), or local path starting ./, ../, or /. Bare plugin names are not install refs.`)}u(_n,"classifyInstallRef");function je(e){return e.startsWith("./")||e.startsWith("../")||!Me(e)&&!me(e)}u(je,"isMarketplaceRelativeSource");async function xe(e,t,n){const i=t.get(e);if(i!==void 0)return i;const o=De(e);if(o===null){if(n.strict)throw h(`marketplace not found: ${e}`);return null}const s=se(o.root);if(s===null)throw h(`marketplace manifest not found: ${o.root}/.crouter-marketplace/marketplace.json`);if(!K(o.root)){const l={marketplace:o,manifest:s,pull:{updated:!1,sha:S(o.root)??""}};return t.set(e,l),l}const a=S(o.root),r=g(ee(o.root),`.${e}.${process.pid}.${Date.now()}.candidate`),c=`${r}.previous`;try{V(o.url,r,{ref:o.ref,depth:1});const l=se(r);if(l===null)throw h(`marketplace manifest not found in candidate checkout: ${r}/.crouter-marketplace/marketplace.json`);const p=ne().filter(d=>d.sourceMarketplace===e&&F(d.root));for(const d of p){const f=l.plugins.find(Te=>Te.name===d.name);if(f===void 0||!je(f.source))throw m(`marketplace candidate removes relative-source plugin "${d.name}" while it is installed`);const y=ze(d.root);if(y===null)throw m(`installed marketplace plugin "${d.name}" has no readable symlink target`);const k=L(o.root,N(ee(d.root),y)),$=N(r,f.source);if($!==N(r,k))throw m(`marketplace candidate moves installed relative-source plugin "${d.name}" from ${k} to ${f.source}`);if(!b($))throw h(`marketplace candidate source path does not exist for plugin "${d.name}": ${$}`);const X=x($);if(X===null)throw h(`plugin manifest not found in marketplace candidate: ${$}/.crouter-plugin/plugin.json`);const Se=d.scope==="project"?C("project"):R();await j(X,$,d.scope,Se,`${$}/.crouter-plugin/plugin.json`)}P(o.root,c);try{P(r,o.root)}catch(d){throw P(c,o.root),d}E(c);for(const d of p){const f=l.plugins.find(y=>y.name===d.name);Re(d.scope,d.name,f.version??d.manifest.version)}U(o.scope,d=>{d.marketplaces[e]===void 0&&(d.marketplaces[e]={}),d.marketplaces[e].last_updated=G()});const v={marketplace:o,manifest:l,pull:{updated:a!==S(o.root),sha:S(o.root)??""}};return t.set(e,v),v}finally{E(r),b(c)&&!b(o.root)&&P(c,o.root)}}u(xe,"refreshMarketplace");function Re(e,t,n){B(e,i=>{const o=i.plugins[t];o!==void 0?n!==void 0&&(o.version=n):i.plugins[t]={enabled:!0,version:n}}),U(e,i=>{i.plugins[t]===void 0&&(i.plugins[t]={}),i.plugins[t].last_updated=G()})}u(Re,"updatePluginVersion");async function Dn(e,t,n){const i=n==="project"?O():R();z(n,i);const o=A(n),s=Ye(e),a=g(i,"tmp",`${s}.${process.pid}.${Date.now()}`);M(g(i,"tmp"));try{V(e,a,{ref:t,depth:1});const r=x(a);if(r===null)throw m(`cloned repo does not contain a valid .crouter-plugin/plugin.json: ${e}`);const c=await j(r,a,n,i,`${e}/.crouter-plugin/plugin.json`),l=g(o,r.name);if(b(l))throw m(`plugin "${r.name}" is already installed at ${l}`);return M(o),P(a,l),B(n,p=>{p.plugins[r.name]={enabled:!0,version:r.version}}),{name:r.name,scope:n,path:l,...c!==void 0?{memory:c}:{}}}finally{E(a)}}u(Dn,"installFromGit");async function In(e,t){const n=N(e),i=x(n);if(i===null)throw h(`plugin manifest not found at ${n}/.crouter-plugin/plugin.json`);const o=t==="project"?O():R(),s=await j(i,n,t,o,`${n}/.crouter-plugin/plugin.json`,void 0,"linked");z(t,o);const a=g(A(t),i.name);if(b(a))throw m(`plugin "${i.name}" is already installed at ${a}`);return ae(n,a),B(t,r=>{r.plugins[i.name]={enabled:!0,version:i.version}}),{name:i.name,scope:t,path:a,...s!==void 0?{memory:s}:{}}}u(In,"installFromLocal");async function Nn(e,t){const n=await xe(e.marketplace,new Map,{strict:!0});if(n===null)throw h(`marketplace not found: ${e.marketplace}`);const i=n.manifest.plugins.find(l=>l.name===e.plugin);if(i===void 0)throw h(`plugin "${e.plugin}" not found in marketplace "${e.marketplace}"`);const o=C(t);z(t,o);const s=g(A(t),e.plugin);if(b(s))throw m(`plugin "${e.plugin}" is already installed at ${s}`);let a,r;if(je(i.source)){const l=g(n.marketplace.root,i.source);if(!b(l))throw h(`plugin source path does not exist: ${l}`);const p=x(l);if(p===null)throw h(`plugin manifest not found at ${l}/.crouter-plugin/plugin.json`);r=await j(p,l,t,o,`${l}/.crouter-plugin/plugin.json`,void 0,"linked"),ae(l,s),a=p}else{const l=g(o,"tmp",`${e.plugin}.${process.pid}.${Date.now()}`);M(g(o,"tmp"));try{V(i.source,l,{depth:1});const p=x(l);if(p===null)throw h(`plugin manifest not found at ${l}/.crouter-plugin/plugin.json`);r=await j(p,l,t,o,`${l}/.crouter-plugin/plugin.json`),M(A(t)),P(l,s),a=p}finally{E(l)}}const c=i.version??a.version;return B(t,l=>{const p={enabled:!0,source_marketplace:e.marketplace};c!==void 0&&(p.version=c),l.plugins[e.plugin]=p}),{name:e.plugin,scope:t,path:s,...r!==void 0?{memory:r}:{}}}u(Nn,"installFromMarketplace");async function Un(e){if(F(e.root)){const r=x(e.root);if(r===null)throw h(`plugin manifest not found at ${e.root}/.crouter-plugin/plugin.json`);const c=e.scope==="project"?C("project"):R(),l=await j(r,e.root,e.scope,c,`${e.root}/.crouter-plugin/plugin.json`,void 0,"linked");return{updated:!1,sha:S(e.root)??"",manifest:r,...l!==void 0?{memory:l}:{}}}if(!K(e.root)){const r=x(e.root);if(r===null)throw h(`plugin manifest not found at ${e.root}/.crouter-plugin/plugin.json`);const c=e.scope==="project"?C("project"):R(),l=await j(r,e.root,e.scope,c,`${e.root}/.crouter-plugin/plugin.json`);return{updated:!1,sha:"",manifest:r,...l!==void 0?{memory:l}:{}}}const t=re(["config","--get","remote.origin.url"],e.root).stdout.trim();if(t==="")throw m(`plugin "${e.name}" has no origin remote to stage an update from`);const n=re(["symbolic-ref","--short","HEAD"],e.root).stdout.trim();if(n==="")throw m(`plugin "${e.name}" is detached at its installed revision and has no tracked branch to update`);const i=e.scope==="project"?C("project"):R(),o=g(i,"tmp",`${e.name}.${process.pid}.${Date.now()}.candidate`),s=`${o}.previous`,a=S(e.root);M(g(i,"tmp"));try{V(t,o,{ref:n,depth:1});const r=x(o);if(r===null)throw h(`plugin manifest not found in update candidate: ${o}/.crouter-plugin/plugin.json`);const c=await j(r,o,e.scope,i,`${o}/.crouter-plugin/plugin.json`);P(e.root,s);try{P(o,e.root)}catch(p){throw P(s,e.root),p}E(s);const l=S(e.root)??"";return{updated:a!==l,sha:l,manifest:r,...c!==void 0?{memory:c}:{}}}finally{E(o),b(s)&&!b(e.root)&&P(s,e.root)}}u(Un,"updateGitPluginCandidate");async function Ee(e,t,n){if(e.manifest.provider!==void 0)return{name:e.name,transport:"http",updated:!1,warnings:[`"${e.name}" is a capability provider bundle; the runtime refreshes it on grant sync`]};const i=ve(e);if(!i&&e.manifest.bundle!==void 0){const p=await $e(e.name,e.manifest.bundle,e.scope,{enable:e.enabled});return{name:e.name,transport:"http",updated:!0,version:p.version,docs:p.docs,kinds:p.kinds,pageComponents:p.pageComponents,commands:p.commands,...p.hooks!==void 0?{hooks:p.hooks}:{},...p.memory!==void 0?{memory:p.memory}:{}}}e.manifest.transport?.kind==="http"&&!i&&ye(e.name);let o,s=!1;if(e.sourceMarketplace!==void 0){const p=await xe(e.sourceMarketplace,t,n);p!==null&&(o=p.manifest.plugins.find(d=>d.name===e.name)?.version,s=p.pull.updated)}const a=await Un(e);Re(e.scope,e.name,o??a.manifest.version);const r=await Y(e.name,e.scope),c=await Q(e.name,e.scope),l=[...ge(a.manifest),...await Z(a.manifest)];return{name:e.name,...e.manifest.transport!==void 0?{transport:e.manifest.transport.kind==="machine"?"http":e.manifest.transport.kind}:{},updated:s||a.updated,sha:a.sha,...r!==void 0?{commands:r}:{},...c!==void 0?{hooks:c}:{},...a.memory!==void 0?{memory:a.memory}:{},...l.length>0?{warnings:l}:{}}}u(Ee,"updateOnePlugin");async function T(e){const n=(await Le().documents.reconcilePackages().catch(i=>He(i))).conflicts.find(i=>i.plugin===e);if(n)throw new _e("conflict",`plugin "${e}" is installed but its documents are not on the canvas: ${n.message}`,yn.GENERAL,{plugin:e,next:"Uninstall the other package providing that plugin name, then run `crtr pkg plugin update --name "+e+"`."})}u(T,"reconcilePluginDocuments");const bt=H({name:"install",description:"install from a ref or HTTP endpoint",whenToUse:"installing one plugin from a marketplace ref, git URL, local path, or an HTTP directory-archive endpoint",help:{name:"pkg plugin install",summary:"install a plugin from a ref or HTTP endpoint",params:[{kind:"positional",name:"install-ref",type:"string",required:!1,constraint:"Plugin install ref. Provide exactly one of INSTALL-REF or --endpoint. Accepted ref forms: marketplace/plugin; git URL starting https://, ssh://, git@, file://, or ending .git; local path starting ./, ../, or /."},{kind:"flag",name:"endpoint",type:"string",required:!1,constraint:"HTTP plugin directory-archive endpoint. Provide exactly one of --endpoint or INSTALL-REF. Must be an absolute https: URL; http: is permitted only for localhost, any *.localhost host, 127.0.0.1, [::1], ::1, or host.docker.internal. Credentials and fragments are rejected."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Required with --endpoint and rejected without it. Lowercase kebab-case plugin handle."},{kind:"flag",name:"auth-env",type:"string",required:!1,constraint:"Optional environment variable holding the HTTP bearer token. Accepted only with --endpoint; its value is never stored."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: project if available, else user."},{kind:"flag",name:"ref",type:"string",required:!1,constraint:"Git ref (branch/tag) for git URL installs. Rejected with --endpoint; ignored for marketplace and local path refs."}],output:[{name:"name",type:"string",required:!0,constraint:"Plugin name as installed."},{name:"scope",type:"string",required:!0,constraint:"Scope the plugin was installed into."},{name:"path",type:"string",required:!0,constraint:"Absolute path to the installed plugin directory."},{name:"transport",type:"string",required:!1,constraint:"exec or http when the plugin declares commands. Always http for an --endpoint install."},{name:"version",type:"string",required:!1,constraint:"First 12 hex characters of the fetched archive SHA-256 for an --endpoint install."},{name:"docs",type:"integer",required:!1,constraint:"Memory document count written from an --endpoint archive."},{name:"kinds",type:"integer",required:!1,constraint:"Kind-registry entry count declared by an --endpoint archive\u2019s bundle.json."},{name:"pageComponents",type:"integer",required:!1,constraint:"Page-component registration count declared by an --endpoint archive\u2019s bundle.json."},{name:"commands",type:"object",required:!1,constraint:"Present only when the plugin declares a command manifest. {mounts: string[] (accepted command paths now live for the next invocation), issues: object[] (typed validation issues that rejected a contribution \u2014 {code, path?, message, received, expected, next})}. Validated statically; commands are never executed."},{name:"hooks",type:"object",required:!1,constraint:"Present only when the plugin declares hooks. {manifestPath?, executablePath?, declarations: {target, phase, op, description, effects}[], lifecycle?: {event, phase, op, description, effects}[], issues: object[], trust: string}. Command targets, lifecycle events, and replacement collisions are validated statically; hook executables are never run."},{name:"warnings",type:"string[]",required:!1,constraint:"Advisory missing PATH executable requirements declared by the installed plugin. The install still succeeds and leaves the plugin enabled."},{name:"memory",type:"object",required:!1,constraint:"Present only when the plugin ships documents (its `memory/` folder). {docs: integer (documents checked), linkedSource?: string (the caller-owned directory, present only when the install keeps that directory as a live link)}."}],outputKind:"object",effects:["A ref install clones, links, or copies the plugin into the scope plugins directory and registers it with enabled=true. Marketplace installs refresh the source marketplace before resolving the plugin entry.","An --endpoint install fetches and validates one authenticated uncompressed tar archive before writing. It replaces the complete plugin directory with synthesized provenance and invocation metadata, commands.json, memory docs, and, when declared, hooks.json plus the hooks/ tree; it also copies any kind-registry entries the bundle.json declares (they join the launch registry below this scope\u2019s own config.json kinds), and any page components it declares (they join the active page-component catalog alongside the user config entries); a fetch or validation failure preserves the prior package.","If the plugin declares a command manifest, its commands go live on the next crtr invocation. Exec commands run trusted local code only when explicitly invoked; HTTP commands call their declared endpoint only when explicitly invoked.","Plugin command hooks go live on the next crtr invocation. They run implicitly with caller authority, may receive normalized inputs and after results, may block or replace declared targets, and may transmit received data. Lifecycle hooks run unprompted at every declared event before node launch and may transmit node identity and resolved paths. Hook executables are never run during install or update validation.","A plugin manifest `requires` declaration is checked against the PATH a node bash receives after install. Missing executables emit a warning with the declared install hint but never fail the install, disable the plugin, or remove its bare-binary contributions.","Before activation the candidate\u2019s `memory/` documents are checked for identity findings and refused if it cannot mount; a local path or marketplace-relative source stays a live link, named as `memory.linkedSource`. A refused package installs nothing."]},run:u(async e=>{const t=e.installRef,n=e.endpoint,i=e.name,o=e.authEnv,s=e.scope,a=e.ref,r=wn(s);if(t===void 0==(n===void 0))throw w("provide exactly one of INSTALL-REF or --endpoint");if(n!==void 0){if(i===void 0)throw w("--name is required with --endpoint");if(a!==void 0)throw w("--ref is only valid with a git INSTALL-REF, not --endpoint");const y=await Bn(i,n,o,r);return await T(i),y}if(i!==void 0)throw w("--name is only valid with --endpoint");if(o!==void 0)throw w("--auth-env is only valid with --endpoint");const c=_n(t),l=c.kind==="marketplace"?await Nn(c.value,r):c.kind==="local"?await In(c.value,r):await Dn(c.value,a,r),p=q(l.name,l.scope),v=await Y(l.name,l.scope),d=await Q(l.name,l.scope),f=p===null?[]:[...ge(p.manifest),...await Z(p.manifest)];return await T(l.name),{...l,...p?.manifest.transport!==void 0?{transport:p.manifest.transport.kind}:{},...v!==void 0?{commands:v}:{},...d!==void 0?{hooks:d}:{},...f.length>0?{warnings:f}:{}}},"run")}),$t=H({name:"remove",description:"remove plugin and directory",whenToUse:"uninstalling a plugin: deletes its directory and removes it from config",help:{name:"pkg plugin remove",summary:"remove a plugin and its directory from the given scope",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Plugin name to remove."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: searches every scope."}],output:[{name:"scopes",type:"string[]",required:!0,constraint:"Scopes the plugin was removed from."}],outputKind:"object",effects:["Deletes the plugin directory. Removes the plugin entry from config.json."]},run:u(async e=>{const t=e.name,n=e.scope;if(t==="crtr")throw w('cannot remove builtin plugin "crtr" \u2014 it ships with the binary');let i;if(n!==void 0){const s=te(n);if(s==="builtin")throw w("cannot remove plugins from builtin scope");i=s==="all"?["project","user"].filter(a=>a!=="project"||ie()!==null):[s]}else i=["project","user"].filter(s=>s!=="project"||ie()!==null);const o=[];for(const s of i){const a=A(s);if(a===null)continue;const r=g(a,t);b(r)&&(E(r),B(s,c=>{delete c.plugins[t]}),U(s,c=>{delete c.plugins[t]}),o.push(s))}if(o.length===0)throw h(`plugin not found: ${t}`);return await T(),{scopes:o}},"run")}),Pt=H({name:"enable",description:"enable a plugin",whenToUse:"reactivating a plugin that was previously disabled so its memory docs resolve again",help:{name:"pkg plugin enable",summary:"enable a plugin in the given scope",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Plugin name to enable."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: scope where the plugin is installed."}],output:[{name:"scope",type:"string",required:!0,constraint:"Scope the change was applied in."},{name:"memory",type:"object",required:!1,constraint:"Present only when the plugin ships documents (its `memory/` folder). {docs: integer (documents checked), linkedSource?: string (the caller-owned directory, present only for a symlinked install)}."}],outputKind:"object",effects:["Validates the installed package before any config change: its `memory/` documents are checked for identity findings and refused if it cannot mount. A refusal leaves the plugin disabled.","Sets plugin enabled=true in config.json."]},run:u(async e=>{const t=e.name,n=kn(t,e.scope);if(n===null)throw h(`plugin not found: ${t}`);const i=x(n.root);if(i===null)throw h(`plugin manifest not found at ${n.root}/.crouter-plugin/plugin.json`);const o=n.scope==="project"?C("project"):R(),s=await j(i,n.root,n.scope,o,`${n.root}/.crouter-plugin/plugin.json`,void 0,F(n.root)?"linked":"package"),a=await fe(e,!0);return await T(t),{scope:a.scope,...s!==void 0?{memory:s}:{}}},"run")}),jt=H({name:"disable",description:"disable without removing",whenToUse:"hiding a plugin from resolution without deleting it, so you can re-enable it later",help:{name:"pkg plugin disable",summary:"disable a plugin (keeps files, hides from resolution)",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Plugin name to disable."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: scope where the plugin is installed."}],output:[{name:"scope",type:"string",required:!0,constraint:"Scope the change was applied in."}],outputKind:"object",effects:["Sets plugin enabled=false in config.json."]},run:u(async e=>{const t=await fe(e,!1);return await T(),{scope:t.scope}},"run")}),xt=H({name:"update",description:"update installed plugin content",whenToUse:"refreshing one named plugin or all installed plugins: archive plugins replace their complete package, while source-installed plugins retain their source update behavior",help:{name:"pkg plugin update",summary:"refresh one or all installed plugins",params:[{kind:"flag",name:"name",type:"string",required:!1,constraint:"Plugin name to update. Omit to update all installed plugins."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Narrows resolution."}],output:[{name:"updated",type:"object[]",required:!0,constraint:"One entry per plugin processed: {name, transport?, updated, sha?, version?, docs?, kinds?, pageComponents?, commands?, hooks?, memory?, warnings?, error?}. memory is present for a plugin shipping documents and reports the same memory check install performs: {docs, linkedSource?}. Archive plugins report transport=http and replace their complete directory after an authenticated refetch; version is the archive content identifier, docs is the written memory-document count, kinds is the declared kind-registry entry count, and pageComponents is the declared page-component registration count. sha is present for git updates. A bulk update reports and skips a failed archive plugin. commands is present for validated command plugins: {mounts: string[] (accepted command paths), issues: object[]}. hooks is present for plugins declaring hooks: {manifestPath?, executablePath?, declarations: {target, phase, op, description, effects}[], lifecycle?: {event, phase, op, description, effects}[], issues: object[], trust: string}; it is static and never executes hooks. warnings lists advisory PATH requirements that remain absent after update."}],outputKind:"object",effects:["Archive plugins unconditionally re-fetch, validate, and replace their complete package; a named failure exits nonzero and preserves the prior package. Bulk update reports a failed archive plugin and continues. Source-installed plugins retain their source update, version, and last_updated behavior.","A capability provider bundle (installed by the runtime from the directory) is skipped with a warning: the runtime refreshes it on grant sync.","Each candidate passes the same pre-activation memory check install performs."]},run:u(async e=>{const t=e.name,n=e.scope,i=n===void 0?void 0:te(n),o=new Map;if(t!==void 0){const r=i===void 0||i==="all"||i==="builtin"?q(t):q(t,i);if(r===null)throw h(`plugin not found: ${t}${i===void 0||i==="all"||i==="builtin"?"":` (scope: ${i})`}`);const c=await Ee(r,o,{strict:!0});return await T(t),{updated:[c]}}const s=ne().filter(r=>r.scope!=="builtin"&&(i===void 0||i==="all"||r.scope===i)),a=[];for(const r of s)try{a.push(await Ee(r,o,{strict:!1}))}catch(c){a.push({name:r.name,...r.manifest.transport!==void 0?{transport:r.manifest.transport.kind}:{},updated:!1,error:c instanceof Error?c.message:String(c)})}return await T(),{updated:a}},"run")});export{j as assertPluginCandidateValid,Nn as installFromMarketplace,Hn as installPluginArchive,je as isMarketplaceRelativeSource,jt as pluginDisable,Pt as pluginEnable,bt as pluginInstall,$t as pluginRemove,xt as pluginUpdate,kt as revalidateBundlePlugin,wt as validatePluginArchive};
@@ -17,7 +17,8 @@ export declare function qualifiedTopLevelCommandName(contributor: Pick<CommandCo
17
17
  * run function to be used by the tree composer. */
18
18
  export type LeafAdapter = (leaf: DeclLeafBase, commandPath: readonly string[]) => {
19
19
  outputKind: 'object' | 'jsonl';
20
- run: LeafDef['run'];
20
+ run: LeafDef['run']; /** The leaf calls a device machine: it takes `--machine <id>` (see `LeafDef.machineSelector`). */
21
+ machineSelector?: true;
21
22
  };
22
23
  /** One accepted external contribution after collision resolution: a validated
23
24
  * top-level branch node, contributor identity, and the adapter callback that
@@ -1,5 +1,6 @@
1
1
  import { type ValidatedCommandManifest } from '../../api/command-manifest/manifest.js';
2
2
  import type { KindConfig, MemoryExtensionDeclarations, PageComponentRegistration, PluginBundle, PluginManifest, Scope } from '../../types.js';
3
+ import { type BundleTransport } from './endpoint.js';
3
4
  /** Who may call a capability provider's tool group (contract 3). */
4
5
  export type ProviderGroupWho = 'own' | 'user' | 'allowlist' | 'any';
5
6
  /** One tool group a capability provider declares in `bundle.json`. */
@@ -34,11 +35,7 @@ export interface ValidatedBundle {
34
35
  version?: number;
35
36
  /** The transport `bundle.json` declares, when it declares one (a bundle the
36
37
  * directory emits for a capability provider always does). */
37
- transport?: {
38
- kind: 'http';
39
- endpoint: string;
40
- authEnv?: string;
41
- };
38
+ transport?: BundleTransport;
42
39
  /** The capability-provider block: present only on a provider bundle. */
43
40
  provider?: ProviderBlock;
44
41
  /** Kind-registry contributions declared in `bundle.json` (optional). The
@@ -82,11 +79,7 @@ export declare function validatePluginBundle(archive: Uint8Array, options: {
82
79
  extensibleCoreBranches?: ReadonlySet<string>;
83
80
  }): Promise<PluginBundleValidation>;
84
81
  /** An archive install's invocation transport. */
85
- export type BundleInvocationTransport = {
86
- kind: 'http';
87
- endpoint: string;
88
- authEnv?: string;
89
- };
82
+ export type BundleInvocationTransport = BundleTransport;
90
83
  /** The hash recorded as a bundle package's `version`: the exact archive bytes
91
84
  * unpacked at its root. Same bytes, same version — which is what lets a
92
85
  * re-sent archive be recognized as the generation already installed. */
@@ -1 +1 @@
1
- var A=Object.defineProperty;var p=(e,r)=>A(e,"name",{value:r,configurable:!0});import{Parser as J}from"tar";import{createHash as I}from"node:crypto";import{chmodSync as W,renameSync as k}from"node:fs";import{dirname as $,join as w,resolve as V,sep as z}from"node:path";import{validateCommandManifest as B}from"../../api/command-manifest/manifest.js";import{ensureScopeInitialized as F,invalidPluginKindsReasons as M,readConfig as R,updateConfig as D,updateState as C}from"../config.js";import{general as x}from"../errors.js";import{atomicWriteJson as T,atomicWriteText as q,ensureDir as j,nowIso as E,pathExists as _,removePath as S,syncDirectory as H,syncTree as U}from"../fs-utils.js";import{invalidPageComponentsReasons as K,normalizePageComponents as G}from"../human/page-catalog.js";import{validateMemoryExtensionDeclarations as X}from"../plugin-extensions.js";import{validateHttpPluginTransport as Z}from"./endpoint.js";const O=/^[A-Za-z0-9_-]{1,64}$/,L=new Set(["id","major","groups"]),Y=new Set(["group","consent_line","rate","who","allowlist"]),Q=new Set(["own","user","allowlist","any"]),ee=new Set(["bundleVersion","kinds","page_components","memory_extensions","version","transport","provider"]);function ne(e,r,n,o,s,a){return{code:e,message:r,received:n,expected:o,next:s,...a!==void 0?{path:a}:{}}}p(ne,"issue");function d(e,r,n,o,s){return ne("plugin_bundle_invalid",e,r,n,o,s)}p(d,"bundleInvalid");function re(e){if(e[0]===31&&e[1]===139)return d("bundle archive must be uncompressed tar data","gzip-compressed archive","an uncompressed tar archive","Serve the archive without compression.");if(e.length===0||e.length%512!==0)return d("bundle archive must be a non-empty whole number of 512-byte tar blocks",`${e.length} bytes`,"a non-empty tar stream with a 512-byte block length","Serve one complete uncompressed tar archive.");if(e.length<1024||!e.subarray(-1024).every(r=>r===0))return d("bundle archive is missing the required two zero-block terminator","archive tail is not 1024 zero bytes","a complete tar stream ending in two 512-byte zero blocks","Serve the complete uncompressed tar archive.")}p(re,"archiveEnvelopeIssue");function oe(e,r){if(e.length===0||e.includes("\0")||e.includes("\\")||e.startsWith("/")||/^[A-Za-z]:($|\/)/.test(e))return d("bundle member path must be a safe relative POSIX path",e,"a relative path without absolute, drive-letter, backslash, or NUL forms","Use a safe relative POSIX member path.",e);if(r==="file"&&e.endsWith("/"))return d("only directory members may have a trailing slash",e,"a regular-file path without a trailing slash","Emit this member as a regular file path.",e);if((r==="directory"&&e.endsWith("/")?e.slice(0,-1):e).split("/").some(o=>o===""||o==="."||o===".."))return d("bundle member path contains an unsafe segment",e,"non-empty segments other than . or ..","Use a safe relative POSIX member path.",e)}p(oe,"validateMemberName");function se(e,r){if(r==="file")return e==="bundle.json"||e==="commands.json"||e==="hooks.json"||e.startsWith("memory/")&&e.endsWith(".md")||e.startsWith("hooks/")?void 0:d("bundle contains an unsupported file member",e,"bundle.json, commands.json, hooks.json, memory/**/*.md, or hooks/**","Remove unsupported members from the archive.",e);if(!(e==="memory"||e.startsWith("memory/")||e==="hooks"||e.startsWith("hooks/")))return d("bundle contains an unsupported directory member",e,"memory or hooks, or a directory below either subtree","Remove unsupported directory members from the archive.",e)}p(se,"validateMemberShape");function te(e){return e.type==="directory"&&e.path.endsWith("/")?e.path.slice(0,-1):e.path}p(te,"materializedPath");function ie(e){const r=new Map;for(const n of e){const o=te(n),s=r.get(o);if(s!==void 0)return d(s===n.type?"bundle contains a duplicate member":"bundle claims one path as both a file and a directory",`${o} (${s} and ${n.type})`,"each materialized path exactly once, with one type","Emit each archive path once, as a single member type.",o);r.set(o,n.type)}for(const[n,o]of r)if(o==="file"){for(const s of r.keys())if(s.startsWith(`${n}/`))return d("bundle nests a member beneath a regular-file path",`${s} beneath file ${n}`,"members nested only beneath directory paths","Emit the parent path as a directory or remove the nested member.",s)}}p(ie,"validateMemberTree");async function ae(e){const r=[],n=new Set;let o;return await new Promise((s,a)=>{const l=new J({strict:!0,maxMetaEntrySize:Number.MAX_SAFE_INTEGER,gzip:!1,brotli:!1,zstd:!1,onReadEntry:p(t=>{const m=t.path,f=t.type==="File"||t.type==="OldFile"?"file":t.type==="Directory"?"directory":void 0;if(f===void 0||t.unsupported||t.ignore||t.linkpath!==void 0){o??=d("bundle member must be a regular file or directory without a link target",`${m} (${t.type})`,"a regular file or directory member without link metadata","Remove links and unsupported member types from the archive.",m),t.resume();return}o??=oe(m,f),o??=se(m,f),n.has(m)&&(o??=d("bundle contains a duplicate member",m,"each member path exactly once","Emit each archive member only once.",m)),n.add(m);const i=[];t.on("data",b=>i.push(b)),t.on("end",()=>{o===void 0&&r.push({path:m,type:f,bytes:new Uint8Array(Buffer.concat(i))})})},"onReadEntry")});l.on("ignoredEntry",t=>{const m=t.path;o??=d("bundle contains an ignored or unsupported archive member",`${m} (${t.type})`,"only supported regular file or directory members","Remove ignored or unsupported members from the archive.",m),t.resume()}),l.once("error",a),l.once("end",s);try{l.end(Buffer.from(e))}catch(t){a(t)}}).catch(s=>{o??=d("bundle archive could not be parsed",s instanceof Error?s.message:String(s),"a valid complete uncompressed tar archive","Fix the archive format and serve it again.")}),o===void 0?{members:r}:{issue:o}}p(ae,"parseArchive");function N(e){return typeof e=="number"&&Number.isInteger(e)&&e>=1}p(N,"isPositiveInt");function de(e){const r=p((t,m,f,i)=>({issue:d(t,m,f,"Fix the provider block in bundle.json.",`bundle.json.${i}`)}),"bad");if(typeof e!="object"||e===null||Array.isArray(e))return r("bundle.json provider must be an object",JSON.stringify(e),"{ id, major, groups }","provider");const n=e,o=Object.keys(n).filter(t=>!L.has(t));if(o.length>0)return r("bundle.json provider has unknown keys",o.join(", "),"only: id, major, groups","provider");if(typeof n.id!="string"||!O.test(n.id))return r("provider id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(n.id),'a provider id of 1-64 letters, digits, "_" or "-"',"provider.id");if(!N(n.major))return r("provider major must be an integer of at least 1",JSON.stringify(n.major),"the served major as an integer >= 1","provider.major");const s=n.groups;if(!Array.isArray(s)||s.length===0)return r("provider groups must be a non-empty array",JSON.stringify(s),"at least one { group, consent_line, rate?, who, allowlist }","provider.groups");const a=[],l=new Set;for(let t=0;t<s.length;t++){const m=`provider.groups[${t}]`,f=s[t];if(typeof f!="object"||f===null||Array.isArray(f))return r("provider group must be an object",JSON.stringify(f),"{ group, consent_line, rate?, who, allowlist }",m);const i=f,b=Object.keys(i).filter(v=>!Y.has(v));if(b.length>0)return r("provider group has unknown keys",b.join(", "),"only: group, consent_line, rate, who, allowlist",m);if(typeof i.group!="string"||!O.test(i.group))return r("provider group id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(i.group),'a group id of 1-64 letters, digits, "_" or "-"',`${m}.group`);if(l.has(i.group))return r("provider group ids must be unique",i.group,"each group declared once",`${m}.group`);if(l.add(i.group),typeof i.consent_line!="string"||i.consent_line.trim().length===0)return r("provider group consent_line must be a non-empty string",JSON.stringify(i.consent_line),"the consent line shown for this group",`${m}.consent_line`);const h=i.rate;if(h!=null&&(typeof h!="number"||!Number.isFinite(h)||h<0))return r("provider group rate must be a non-negative number or null",JSON.stringify(h),"a finite number >= 0, null, or omitted",`${m}.rate`);if(typeof i.who!="string"||!Q.has(i.who))return r("provider group who must be own|user|allowlist|any",JSON.stringify(i.who),"own | user | allowlist | any",`${m}.who`);const c=i.allowlist;if(!Array.isArray(c)||!c.every(v=>typeof v=="string"&&v.length>0))return r("provider group allowlist must be an array of non-empty strings",JSON.stringify(c),"an array of app principals ([] unless who is allowlist)",`${m}.allowlist`);if(c.length>0&&i.who!=="allowlist")return r("provider group allowlist must be empty unless who is allowlist",`who ${i.who}`,'who: "allowlist", or allowlist: []',`${m}.allowlist`);a.push({group:i.group,consent_line:i.consent_line,...h!==void 0?{rate:h}:{},who:i.who,allowlist:c})}return{provider:{id:n.id,major:n.major,groups:a}}}p(de,"parseProviderBlock");function ue(e){const r=[],n=p((o,s)=>{const a=[...s,o.name];if(o.kind==="leaf")r.push({path:a.join(" "),leaf:o});else for(const l of o.children)n(l,a)},"walk");for(const o of e.roots)n(o,[]);for(const o of e.coreMounts)n(o.node,o.parent);return r}p(ue,"collectLeaves");function le(e,r){const n=e.provider;if(n===void 0)return;if(e.transport===void 0)return d("a provider bundle must declare its transport","(missing)",'transport { kind: "http", endpoint } in bundle.json',"Add the provider endpoint as bundle.json transport.","bundle.json.transport");if(e.transport.authEnv!==void 0)return d("a provider bundle must not declare authEnv",e.transport.authEnv,"no authEnv: the runtime presents its own outbound token","Remove transport.authEnv from bundle.json.","bundle.json.transport.authEnv");const o=new Set(n.groups.map(s=>s.group));for(const{path:s,leaf:a}of ue(r)){const l="group"in a?a.group:void 0;if(l===void 0)return d(`provider leaf "${s}" has no group`,s,"a group on every leaf of a provider bundle",`Add a group to leaf "${s}".`,`commands.json:${s}.group`);if(!o.has(l))return d(`provider leaf "${s}" names undeclared group "${l}"`,l,`one of: ${[...o].join(", ")}`,`Declare group "${l}" in the provider block, or move leaf "${s}" to a declared group.`,`commands.json:${s}.group`);if("rest"in a&&a.rest.streaming===!0)return d(`provider leaf "${s}" streams`,"rest.streaming: true","no streaming leaves in a provider bundle",`Make leaf "${s}" return one response.`,`commands.json:${s}.rest.streaming`)}}p(le,"providerBundleIssue");function me(e){let r;try{r=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issue:d("bundle.json is not valid JSON","invalid JSON",'{"bundleVersion":1}',"Write valid bundle metadata.","bundle.json")}}if(typeof r!="object"||r===null||Array.isArray(r))return{issue:d("bundle.json must be a JSON object",JSON.stringify(r),"an object with bundleVersion set to 1 and optionally kinds",'Set bundle.json to {"bundleVersion":1}.',"bundle.json")};const n=r,o=Object.keys(n).filter(t=>!ee.has(t));if(n.bundleVersion!==1||o.length>0)return{issue:d("bundle.json must contain bundleVersion 1 and at most version, transport, provider, kinds, page_components, and memory_extensions",JSON.stringify(r),"an object with bundleVersion set to 1 and optional version, transport, provider, kinds, page_components, and memory_extensions",'Set bundle.json to {"bundleVersion":1} plus optional contribution blocks.',"bundle.json")};if(n.kinds!==void 0){const t=M(n.kinds);if(t.length>0)return{issue:d("bundle.json kinds block is invalid",t.join("; "),"each entry a full KindConfig (whenToUse required) or a sparse patch of launch fields","Fix the kinds entries in bundle.json.","bundle.json")}}if(n.page_components!==void 0){const t=K(n.page_components);if(t.length>0)return{issue:d("bundle.json page_components block is invalid",t.join("; "),"an array of page component registrations, each a kind string or {kind, description?, useWhen?, doc?, display?}","Fix the page_components entries in bundle.json.","bundle.json")}}let s;if(n.memory_extensions!==void 0)try{s=X(n.memory_extensions)}catch(t){return{issue:d("bundle.json memory_extensions block is invalid",t instanceof Error?t.message:String(t),"a valid memory_extensions declaration object","Fix the memory_extensions declarations in bundle.json.","bundle.json")}}if(n.version!==void 0&&!N(n.version))return{issue:d("bundle.json version must be an integer of at least 1",JSON.stringify(n.version),"the plugin major as an integer >= 1, or omitted","Fix version in bundle.json.","bundle.json.version")};let a;if(n.transport!==void 0){const t=Z(n.transport);if(t.transport===void 0)return{issue:d("bundle.json transport is invalid",t.errors.join("; "),'{ kind: "http", endpoint, authEnv? }',"Fix transport in bundle.json.","bundle.json.transport")};a=t.transport}let l;if(n.provider!==void 0){const t=de(n.provider);if(t.issue!==void 0)return{issue:t.issue};if(l=t.provider,n.version!==void 0&&n.version!==l.major)return{issue:d("bundle.json provider major must equal version",`version ${String(n.version)}, major ${l.major}`,"one major","Set provider.major and version to the same integer.","bundle.json.provider.major")}}return{...n.version!==void 0?{version:n.version}:{},...a!==void 0?{transport:a}:{},...l!==void 0?{provider:l}:{},...n.kinds!==void 0?{kinds:n.kinds}:{},...n.page_components!==void 0?{page_components:G(n.page_components)}:{},...s!==void 0?{memory_extensions:s}:{}}}p(me,"parseBundleMetadata");function pe(e,r,n,o=new Set){let s;try{s=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issues:[d("commands.json is not valid JSON","invalid JSON","a valid command-manifest JSON object","Regenerate commands.json.","commands.json")]}}const a=B(s,{transport:"http",reservedCoreNames:r,extensibleCoreBranches:o,...n!==void 0?{coreCommandPaths:n}:{}});return{...a.manifest!==void 0?{commands:a.manifest}:{},issues:a.issues}}p(pe,"parseCommands");async function ce(e,r){const n=re(e);if(n!==void 0)return{issues:[n]};const o=await ae(e);if(o.issue!==void 0||o.members===void 0)return{issues:[o.issue]};const s=ie(o.members);if(s!==void 0)return{issues:[s]};const a=o.members.find(u=>u.path==="bundle.json"&&u.type==="file"),l=o.members.find(u=>u.path==="commands.json"&&u.type==="file");if(a===void 0||l===void 0)return{issues:[d("bundle must contain exactly one bundle.json and one commands.json regular file",`bundle.json=${a!==void 0}, commands.json=${l!==void 0}`,"one regular bundle.json and one regular commands.json member","Add the required metadata members to the archive.")]};const t=o.members.find(u=>u.path==="hooks.json"&&u.type==="file"),m=o.members.filter(u=>u.type==="file"&&u.path.startsWith("hooks/"));if((t!==void 0||o.members.some(u=>u.path==="hooks"||u.path.startsWith("hooks/")))&&(t===void 0||!m.some(u=>u.path==="hooks/dispatch")))return{issues:[d("bundle hook payload must contain both hooks.json and hooks/dispatch regular files",`hooks.json=${t!==void 0}, hooks/dispatch=${m.some(u=>u.path==="hooks/dispatch")}`,"both hooks.json and hooks/dispatch regular files","Add both required hook members or remove the hook payload.")]};const i=me(a.bytes);if(i.issue!==void 0)return{issues:[i.issue]};const b=pe(l.bytes,r.reservedCoreNames,r.coreCommandPaths,r.extensibleCoreBranches);if(b.commands===void 0)return{issues:b.issues};const h=b.commands.version;if(i.version!==void 0&&h!==void 0&&i.version!==h)return{issues:[d("bundle.json version and commands.json version differ",`bundle.json ${i.version}, commands.json ${h}`,"one plugin major","Emit the same version in bundle.json and commands.json.","bundle.json.version")]};const c=i.version??h;if(i.provider!==void 0&&c===void 0)return{issues:[d("a provider bundle requires a plugin version","no version in bundle.json or commands.json",`version ${i.provider.major}, the provider major`,"Emit the provider major as the plugin version.","bundle.json.version")]};if(i.provider!==void 0&&c!==i.provider.major)return{issues:[d("bundle provider major must equal the plugin version",`version ${c}, major ${i.provider.major}`,"one major","Emit the provider major as the plugin version.","bundle.json.provider.major")]};const v=le(i,b.commands);return v!==void 0?{issues:[v]}:{bundle:{bundleVersion:1,...c!==void 0?{version:c}:{},...i.transport!==void 0?{transport:i.transport}:{},...i.provider!==void 0?{provider:i.provider}:{},...i.kinds!==void 0?{kinds:i.kinds}:{},...i.page_components!==void 0?{page_components:i.page_components}:{},...i.memory_extensions!==void 0?{memory_extensions:i.memory_extensions}:{},commands:b.commands,commandsBytes:l.bytes,directories:o.members.filter(u=>u.type==="directory").map(u=>({path:u.path})),memory:o.members.filter(u=>u.type==="file"&&u.path.startsWith("memory/")).map(u=>({path:u.path,bytes:u.bytes})),...t!==void 0?{hooks:{manifest:t.bytes,files:m.map(u=>({path:u.path,bytes:u.bytes}))}}:{}},issues:[]}}p(ce,"validatePluginBundle");function fe(e){return I("sha256").update(e).digest("hex").slice(0,12)}p(fe,"bundleVersion");function Ne(e,r,n,o){return R(r).plugins[e]?.version!==o?!1:_(w(n,"commands.json"))}p(Ne,"isInstalledGeneration");function he(e,r,n,o){C(e,s=>{s.plugins[r]===void 0&&(s.plugins[r]={}),o.swapped&&(s.plugins[r].last_updated=E()),s.plugins[r].checked_at=E(),n===void 0?delete s.plugins[r].etag:s.plugins[r].etag=n})}p(he,"recordCheckedGeneration");function g(e,r){const n=V(e,r);if(n===e||!n.startsWith(e+z))throw x(`bundle member path escapes the staging directory: ${r}`);return n}p(g,"stagedPath");function be(e,r,n){const o=p((s,a,l=420)=>q(s,a,{mode:l,durable:!0}),"write");T(g(e,".crouter-plugin/plugin.json"),r,{durable:!0});for(const s of n.directories)j(g(e,s.path));o(g(e,"commands.json"),n.commandsBytes);for(const s of n.memory)o(g(e,s.path),s.bytes);if(n.hooks!==void 0){o(g(e,"hooks.json"),n.hooks.manifest);for(const s of n.hooks.files)o(g(e,s.path),s.bytes);W(g(e,"hooks/dispatch"),493)}U(e)}p(be,"writeStagedBundle");async function Pe(e,r,n){const{scope:o,scopeRootPath:s,root:a}=n,l=await ce(r,n.validation);if(l.bundle===void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,l.issues);const t=l.bundle,m=n.accept?.(t);if(m!==void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,[d(m,e,"the bundle this install asked for","Serve the requested bundle.")]);const f=n.version??fe(r),i=t.provider!==void 0&&t.transport!==void 0?t.transport:n.transport,b={name:e,version:f,commands:"commands.json",transport:i,bundle:n.bundle,...t.kinds!==void 0?{kinds:t.kinds}:{},...t.page_components!==void 0?{page_components:t.page_components}:{},...t.memory_extensions!==void 0?{memory_extensions:t.memory_extensions}:{},...t.hooks!==void 0?{hooks:"hooks.json",hookExecutable:"hooks/dispatch"}:{},...t.provider!==void 0?{provider:t.provider}:{}},h=w(s,"tmp"),c=w(h,`${e}.${process.pid}`),v=`${c}.prev`;if(_(v))throw x(`cannot replace plugin "${e}": prior interrupted replacement remains at ${v}`);j(h),j($(a)),S(c);let u;try{j(c),be(c,b,t),u=await n.validateStaged(c,b);let y=!1;_(a)&&(k(a,v),y=!0);try{k(c,a)}catch(P){throw y&&k(v,a),P}H($(a)),y&&S(v)}finally{S(c)}return F(o,s),D(o,y=>{y.plugins[e]={enabled:n.enable?!0:y.plugins[e]?.enabled??!0,version:f}}),he(o,e,n.etag,{swapped:!0}),{manifest:b,validated:t,version:f,root:a,staged:u}}p(Pe,"installBundleArchive");export{fe as bundleVersion,Pe as installBundleArchive,Ne as isInstalledGeneration,he as recordCheckedGeneration,ce as validatePluginBundle,be as writeStagedBundle};
1
+ var P=Object.defineProperty;var p=(e,r)=>P(e,"name",{value:r,configurable:!0});import{Parser as J}from"tar";import{createHash as I}from"node:crypto";import{chmodSync as W,renameSync as k}from"node:fs";import{dirname as $,join as w,resolve as V,sep as z}from"node:path";import{validateCommandManifest as B}from"../../api/command-manifest/manifest.js";import{ensureScopeInitialized as F,invalidPluginKindsReasons as M,readConfig as R,updateConfig as D,updateState as C}from"../config.js";import{general as x}from"../errors.js";import{atomicWriteJson as T,atomicWriteText as q,ensureDir as j,nowIso as E,pathExists as _,removePath as S,syncDirectory as U,syncTree as H}from"../fs-utils.js";import{invalidPageComponentsReasons as K,normalizePageComponents as G}from"../human/page-catalog.js";import{validateMemoryExtensionDeclarations as X}from"../plugin-extensions.js";import{validateBundleTransport as Z}from"./endpoint.js";const O=/^[A-Za-z0-9_-]{1,64}$/,L=new Set(["id","major","groups"]),Y=new Set(["group","consent_line","rate","who","allowlist"]),Q=new Set(["own","user","allowlist","any"]),ee=new Set(["bundleVersion","kinds","page_components","memory_extensions","version","transport","provider"]);function ne(e,r,n,o,s,a){return{code:e,message:r,received:n,expected:o,next:s,...a!==void 0?{path:a}:{}}}p(ne,"issue");function d(e,r,n,o,s){return ne("plugin_bundle_invalid",e,r,n,o,s)}p(d,"bundleInvalid");function re(e){if(e[0]===31&&e[1]===139)return d("bundle archive must be uncompressed tar data","gzip-compressed archive","an uncompressed tar archive","Serve the archive without compression.");if(e.length===0||e.length%512!==0)return d("bundle archive must be a non-empty whole number of 512-byte tar blocks",`${e.length} bytes`,"a non-empty tar stream with a 512-byte block length","Serve one complete uncompressed tar archive.");if(e.length<1024||!e.subarray(-1024).every(r=>r===0))return d("bundle archive is missing the required two zero-block terminator","archive tail is not 1024 zero bytes","a complete tar stream ending in two 512-byte zero blocks","Serve the complete uncompressed tar archive.")}p(re,"archiveEnvelopeIssue");function oe(e,r){if(e.length===0||e.includes("\0")||e.includes("\\")||e.startsWith("/")||/^[A-Za-z]:($|\/)/.test(e))return d("bundle member path must be a safe relative POSIX path",e,"a relative path without absolute, drive-letter, backslash, or NUL forms","Use a safe relative POSIX member path.",e);if(r==="file"&&e.endsWith("/"))return d("only directory members may have a trailing slash",e,"a regular-file path without a trailing slash","Emit this member as a regular file path.",e);if((r==="directory"&&e.endsWith("/")?e.slice(0,-1):e).split("/").some(o=>o===""||o==="."||o===".."))return d("bundle member path contains an unsafe segment",e,"non-empty segments other than . or ..","Use a safe relative POSIX member path.",e)}p(oe,"validateMemberName");function se(e,r){if(r==="file")return e==="bundle.json"||e==="commands.json"||e==="hooks.json"||e.startsWith("memory/")&&e.endsWith(".md")||e.startsWith("hooks/")?void 0:d("bundle contains an unsupported file member",e,"bundle.json, commands.json, hooks.json, memory/**/*.md, or hooks/**","Remove unsupported members from the archive.",e);if(!(e==="memory"||e.startsWith("memory/")||e==="hooks"||e.startsWith("hooks/")))return d("bundle contains an unsupported directory member",e,"memory or hooks, or a directory below either subtree","Remove unsupported directory members from the archive.",e)}p(se,"validateMemberShape");function te(e){return e.type==="directory"&&e.path.endsWith("/")?e.path.slice(0,-1):e.path}p(te,"materializedPath");function ie(e){const r=new Map;for(const n of e){const o=te(n),s=r.get(o);if(s!==void 0)return d(s===n.type?"bundle contains a duplicate member":"bundle claims one path as both a file and a directory",`${o} (${s} and ${n.type})`,"each materialized path exactly once, with one type","Emit each archive path once, as a single member type.",o);r.set(o,n.type)}for(const[n,o]of r)if(o==="file"){for(const s of r.keys())if(s.startsWith(`${n}/`))return d("bundle nests a member beneath a regular-file path",`${s} beneath file ${n}`,"members nested only beneath directory paths","Emit the parent path as a directory or remove the nested member.",s)}}p(ie,"validateMemberTree");async function ae(e){const r=[],n=new Set;let o;return await new Promise((s,a)=>{const l=new J({strict:!0,maxMetaEntrySize:Number.MAX_SAFE_INTEGER,gzip:!1,brotli:!1,zstd:!1,onReadEntry:p(t=>{const m=t.path,f=t.type==="File"||t.type==="OldFile"?"file":t.type==="Directory"?"directory":void 0;if(f===void 0||t.unsupported||t.ignore||t.linkpath!==void 0){o??=d("bundle member must be a regular file or directory without a link target",`${m} (${t.type})`,"a regular file or directory member without link metadata","Remove links and unsupported member types from the archive.",m),t.resume();return}o??=oe(m,f),o??=se(m,f),n.has(m)&&(o??=d("bundle contains a duplicate member",m,"each member path exactly once","Emit each archive member only once.",m)),n.add(m);const i=[];t.on("data",b=>i.push(b)),t.on("end",()=>{o===void 0&&r.push({path:m,type:f,bytes:new Uint8Array(Buffer.concat(i))})})},"onReadEntry")});l.on("ignoredEntry",t=>{const m=t.path;o??=d("bundle contains an ignored or unsupported archive member",`${m} (${t.type})`,"only supported regular file or directory members","Remove ignored or unsupported members from the archive.",m),t.resume()}),l.once("error",a),l.once("end",s);try{l.end(Buffer.from(e))}catch(t){a(t)}}).catch(s=>{o??=d("bundle archive could not be parsed",s instanceof Error?s.message:String(s),"a valid complete uncompressed tar archive","Fix the archive format and serve it again.")}),o===void 0?{members:r}:{issue:o}}p(ae,"parseArchive");function N(e){return typeof e=="number"&&Number.isInteger(e)&&e>=1}p(N,"isPositiveInt");function de(e){const r=p((t,m,f,i)=>({issue:d(t,m,f,"Fix the provider block in bundle.json.",`bundle.json.${i}`)}),"bad");if(typeof e!="object"||e===null||Array.isArray(e))return r("bundle.json provider must be an object",JSON.stringify(e),"{ id, major, groups }","provider");const n=e,o=Object.keys(n).filter(t=>!L.has(t));if(o.length>0)return r("bundle.json provider has unknown keys",o.join(", "),"only: id, major, groups","provider");if(typeof n.id!="string"||!O.test(n.id))return r("provider id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(n.id),'a provider id of 1-64 letters, digits, "_" or "-"',"provider.id");if(!N(n.major))return r("provider major must be an integer of at least 1",JSON.stringify(n.major),"the served major as an integer >= 1","provider.major");const s=n.groups;if(!Array.isArray(s)||s.length===0)return r("provider groups must be a non-empty array",JSON.stringify(s),"at least one { group, consent_line, rate?, who, allowlist }","provider.groups");const a=[],l=new Set;for(let t=0;t<s.length;t++){const m=`provider.groups[${t}]`,f=s[t];if(typeof f!="object"||f===null||Array.isArray(f))return r("provider group must be an object",JSON.stringify(f),"{ group, consent_line, rate?, who, allowlist }",m);const i=f,b=Object.keys(i).filter(v=>!Y.has(v));if(b.length>0)return r("provider group has unknown keys",b.join(", "),"only: group, consent_line, rate, who, allowlist",m);if(typeof i.group!="string"||!O.test(i.group))return r("provider group id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(i.group),'a group id of 1-64 letters, digits, "_" or "-"',`${m}.group`);if(l.has(i.group))return r("provider group ids must be unique",i.group,"each group declared once",`${m}.group`);if(l.add(i.group),typeof i.consent_line!="string"||i.consent_line.trim().length===0)return r("provider group consent_line must be a non-empty string",JSON.stringify(i.consent_line),"the consent line shown for this group",`${m}.consent_line`);const h=i.rate;if(h!=null&&(typeof h!="number"||!Number.isFinite(h)||h<0))return r("provider group rate must be a non-negative number or null",JSON.stringify(h),"a finite number >= 0, null, or omitted",`${m}.rate`);if(typeof i.who!="string"||!Q.has(i.who))return r("provider group who must be own|user|allowlist|any",JSON.stringify(i.who),"own | user | allowlist | any",`${m}.who`);const c=i.allowlist;if(!Array.isArray(c)||!c.every(v=>typeof v=="string"&&v.length>0))return r("provider group allowlist must be an array of non-empty strings",JSON.stringify(c),"an array of app principals ([] unless who is allowlist)",`${m}.allowlist`);if(c.length>0&&i.who!=="allowlist")return r("provider group allowlist must be empty unless who is allowlist",`who ${i.who}`,'who: "allowlist", or allowlist: []',`${m}.allowlist`);a.push({group:i.group,consent_line:i.consent_line,...h!==void 0?{rate:h}:{},who:i.who,allowlist:c})}return{provider:{id:n.id,major:n.major,groups:a}}}p(de,"parseProviderBlock");function ue(e){const r=[],n=p((o,s)=>{const a=[...s,o.name];if(o.kind==="leaf")r.push({path:a.join(" "),leaf:o});else for(const l of o.children)n(l,a)},"walk");for(const o of e.roots)n(o,[]);for(const o of e.coreMounts)n(o.node,o.parent);return r}p(ue,"collectLeaves");function le(e,r){const n=e.provider;if(n===void 0)return;if(e.transport===void 0)return d("a provider bundle must declare its transport","(missing)",'transport { kind: "http", endpoint } in bundle.json',"Add the provider endpoint as bundle.json transport.","bundle.json.transport");if(e.transport.kind==="http"&&e.transport.authEnv!==void 0)return d("a provider bundle must not declare authEnv",e.transport.authEnv,"no authEnv: the runtime presents its own outbound token","Remove transport.authEnv from bundle.json.","bundle.json.transport.authEnv");const o=new Set(n.groups.map(s=>s.group));for(const{path:s,leaf:a}of ue(r)){const l="group"in a?a.group:void 0;if(l===void 0)return d(`provider leaf "${s}" has no group`,s,"a group on every leaf of a provider bundle",`Add a group to leaf "${s}".`,`commands.json:${s}.group`);if(!o.has(l))return d(`provider leaf "${s}" names undeclared group "${l}"`,l,`one of: ${[...o].join(", ")}`,`Declare group "${l}" in the provider block, or move leaf "${s}" to a declared group.`,`commands.json:${s}.group`);if("rest"in a&&a.rest.streaming===!0)return d(`provider leaf "${s}" streams`,"rest.streaming: true","no streaming leaves in a provider bundle",`Make leaf "${s}" return one response.`,`commands.json:${s}.rest.streaming`)}}p(le,"providerBundleIssue");function me(e){let r;try{r=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issue:d("bundle.json is not valid JSON","invalid JSON",'{"bundleVersion":1}',"Write valid bundle metadata.","bundle.json")}}if(typeof r!="object"||r===null||Array.isArray(r))return{issue:d("bundle.json must be a JSON object",JSON.stringify(r),"an object with bundleVersion set to 1 and optionally kinds",'Set bundle.json to {"bundleVersion":1}.',"bundle.json")};const n=r,o=Object.keys(n).filter(t=>!ee.has(t));if(n.bundleVersion!==1||o.length>0)return{issue:d("bundle.json must contain bundleVersion 1 and at most version, transport, provider, kinds, page_components, and memory_extensions",JSON.stringify(r),"an object with bundleVersion set to 1 and optional version, transport, provider, kinds, page_components, and memory_extensions",'Set bundle.json to {"bundleVersion":1} plus optional contribution blocks.',"bundle.json")};if(n.kinds!==void 0){const t=M(n.kinds);if(t.length>0)return{issue:d("bundle.json kinds block is invalid",t.join("; "),"each entry a full KindConfig (whenToUse required) or a sparse patch of launch fields","Fix the kinds entries in bundle.json.","bundle.json")}}if(n.page_components!==void 0){const t=K(n.page_components);if(t.length>0)return{issue:d("bundle.json page_components block is invalid",t.join("; "),"an array of page component registrations, each a kind string or {kind, description?, useWhen?, doc?, display?}","Fix the page_components entries in bundle.json.","bundle.json")}}let s;if(n.memory_extensions!==void 0)try{s=X(n.memory_extensions)}catch(t){return{issue:d("bundle.json memory_extensions block is invalid",t instanceof Error?t.message:String(t),"a valid memory_extensions declaration object","Fix the memory_extensions declarations in bundle.json.","bundle.json")}}if(n.version!==void 0&&!N(n.version))return{issue:d("bundle.json version must be an integer of at least 1",JSON.stringify(n.version),"the plugin major as an integer >= 1, or omitted","Fix version in bundle.json.","bundle.json.version")};let a;if(n.transport!==void 0){const t=Z(n.transport);if(t.transport===void 0)return{issue:d("bundle.json transport is invalid",t.errors.join("; "),'{ kind: "http", endpoint, authEnv? } or { kind: "machine" }',"Fix transport in bundle.json.","bundle.json.transport")};a=t.transport}let l;if(n.provider!==void 0){const t=de(n.provider);if(t.issue!==void 0)return{issue:t.issue};if(l=t.provider,n.version!==void 0&&n.version!==l.major)return{issue:d("bundle.json provider major must equal version",`version ${String(n.version)}, major ${l.major}`,"one major","Set provider.major and version to the same integer.","bundle.json.provider.major")}}return{...n.version!==void 0?{version:n.version}:{},...a!==void 0?{transport:a}:{},...l!==void 0?{provider:l}:{},...n.kinds!==void 0?{kinds:n.kinds}:{},...n.page_components!==void 0?{page_components:G(n.page_components)}:{},...s!==void 0?{memory_extensions:s}:{}}}p(me,"parseBundleMetadata");function pe(e,r,n,o=new Set){let s;try{s=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issues:[d("commands.json is not valid JSON","invalid JSON","a valid command-manifest JSON object","Regenerate commands.json.","commands.json")]}}const a=B(s,{transport:"http",reservedCoreNames:r,extensibleCoreBranches:o,...n!==void 0?{coreCommandPaths:n}:{}});return{...a.manifest!==void 0?{commands:a.manifest}:{},issues:a.issues}}p(pe,"parseCommands");async function ce(e,r){const n=re(e);if(n!==void 0)return{issues:[n]};const o=await ae(e);if(o.issue!==void 0||o.members===void 0)return{issues:[o.issue]};const s=ie(o.members);if(s!==void 0)return{issues:[s]};const a=o.members.find(u=>u.path==="bundle.json"&&u.type==="file"),l=o.members.find(u=>u.path==="commands.json"&&u.type==="file");if(a===void 0||l===void 0)return{issues:[d("bundle must contain exactly one bundle.json and one commands.json regular file",`bundle.json=${a!==void 0}, commands.json=${l!==void 0}`,"one regular bundle.json and one regular commands.json member","Add the required metadata members to the archive.")]};const t=o.members.find(u=>u.path==="hooks.json"&&u.type==="file"),m=o.members.filter(u=>u.type==="file"&&u.path.startsWith("hooks/"));if((t!==void 0||o.members.some(u=>u.path==="hooks"||u.path.startsWith("hooks/")))&&(t===void 0||!m.some(u=>u.path==="hooks/dispatch")))return{issues:[d("bundle hook payload must contain both hooks.json and hooks/dispatch regular files",`hooks.json=${t!==void 0}, hooks/dispatch=${m.some(u=>u.path==="hooks/dispatch")}`,"both hooks.json and hooks/dispatch regular files","Add both required hook members or remove the hook payload.")]};const i=me(a.bytes);if(i.issue!==void 0)return{issues:[i.issue]};const b=pe(l.bytes,r.reservedCoreNames,r.coreCommandPaths,r.extensibleCoreBranches);if(b.commands===void 0)return{issues:b.issues};const h=b.commands.version;if(i.version!==void 0&&h!==void 0&&i.version!==h)return{issues:[d("bundle.json version and commands.json version differ",`bundle.json ${i.version}, commands.json ${h}`,"one plugin major","Emit the same version in bundle.json and commands.json.","bundle.json.version")]};const c=i.version??h;if(i.provider!==void 0&&c===void 0)return{issues:[d("a provider bundle requires a plugin version","no version in bundle.json or commands.json",`version ${i.provider.major}, the provider major`,"Emit the provider major as the plugin version.","bundle.json.version")]};if(i.provider!==void 0&&c!==i.provider.major)return{issues:[d("bundle provider major must equal the plugin version",`version ${c}, major ${i.provider.major}`,"one major","Emit the provider major as the plugin version.","bundle.json.provider.major")]};const v=le(i,b.commands);return v!==void 0?{issues:[v]}:{bundle:{bundleVersion:1,...c!==void 0?{version:c}:{},...i.transport!==void 0?{transport:i.transport}:{},...i.provider!==void 0?{provider:i.provider}:{},...i.kinds!==void 0?{kinds:i.kinds}:{},...i.page_components!==void 0?{page_components:i.page_components}:{},...i.memory_extensions!==void 0?{memory_extensions:i.memory_extensions}:{},commands:b.commands,commandsBytes:l.bytes,directories:o.members.filter(u=>u.type==="directory").map(u=>({path:u.path})),memory:o.members.filter(u=>u.type==="file"&&u.path.startsWith("memory/")).map(u=>({path:u.path,bytes:u.bytes})),...t!==void 0?{hooks:{manifest:t.bytes,files:m.map(u=>({path:u.path,bytes:u.bytes}))}}:{}},issues:[]}}p(ce,"validatePluginBundle");function fe(e){return I("sha256").update(e).digest("hex").slice(0,12)}p(fe,"bundleVersion");function Ne(e,r,n,o){return R(r).plugins[e]?.version!==o?!1:_(w(n,"commands.json"))}p(Ne,"isInstalledGeneration");function he(e,r,n,o){C(e,s=>{s.plugins[r]===void 0&&(s.plugins[r]={}),o.swapped&&(s.plugins[r].last_updated=E()),s.plugins[r].checked_at=E(),n===void 0?delete s.plugins[r].etag:s.plugins[r].etag=n})}p(he,"recordCheckedGeneration");function g(e,r){const n=V(e,r);if(n===e||!n.startsWith(e+z))throw x(`bundle member path escapes the staging directory: ${r}`);return n}p(g,"stagedPath");function be(e,r,n){const o=p((s,a,l=420)=>q(s,a,{mode:l,durable:!0}),"write");T(g(e,".crouter-plugin/plugin.json"),r,{durable:!0});for(const s of n.directories)j(g(e,s.path));o(g(e,"commands.json"),n.commandsBytes);for(const s of n.memory)o(g(e,s.path),s.bytes);if(n.hooks!==void 0){o(g(e,"hooks.json"),n.hooks.manifest);for(const s of n.hooks.files)o(g(e,s.path),s.bytes);W(g(e,"hooks/dispatch"),493)}H(e)}p(be,"writeStagedBundle");async function Ae(e,r,n){const{scope:o,scopeRootPath:s,root:a}=n,l=await ce(r,n.validation);if(l.bundle===void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,l.issues);const t=l.bundle,m=n.accept?.(t);if(m!==void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,[d(m,e,"the bundle this install asked for","Serve the requested bundle.")]);const f=n.version??fe(r),i=t.provider!==void 0&&t.transport!==void 0?t.transport:n.transport,b={name:e,version:f,commands:"commands.json",transport:i,bundle:n.bundle,...t.kinds!==void 0?{kinds:t.kinds}:{},...t.page_components!==void 0?{page_components:t.page_components}:{},...t.memory_extensions!==void 0?{memory_extensions:t.memory_extensions}:{},...t.hooks!==void 0?{hooks:"hooks.json",hookExecutable:"hooks/dispatch"}:{},...t.provider!==void 0?{provider:t.provider}:{}},h=w(s,"tmp"),c=w(h,`${e}.${process.pid}`),v=`${c}.prev`;if(_(v))throw x(`cannot replace plugin "${e}": prior interrupted replacement remains at ${v}`);j(h),j($(a)),S(c);let u;try{j(c),be(c,b,t),u=await n.validateStaged(c,b);let y=!1;_(a)&&(k(a,v),y=!0);try{k(c,a)}catch(A){throw y&&k(v,a),A}U($(a)),y&&S(v)}finally{S(c)}return F(o,s),D(o,y=>{y.plugins[e]={enabled:n.enable?!0:y.plugins[e]?.enabled??!0,version:f}}),he(o,e,n.etag,{swapped:!0}),{manifest:b,validated:t,version:f,root:a,staged:u}}p(Ae,"installBundleArchive");export{fe as bundleVersion,Ae as installBundleArchive,Ne as isInstalledGeneration,he as recordCheckedGeneration,ce as validatePluginBundle,be as writeStagedBundle};
@@ -1 +1 @@
1
- var x=Object.defineProperty;var p=(n,e)=>x(n,"name",{value:e,configurable:!0});import{resolve as k}from"node:path";import{envNodeId as E}from"../../shared/env.js";import{cliClient as c}from"../../commands/api-client.js";import{defineBranch as h,defineLeaf as j}from"../command.js";import{resolveCommandRegistry as w}from"../command-manifests/registry.js";import{executeExternalLeaf as C}from"./transport/exec-invoke.js";import{invokeHttpLeaf as L}from"./transport/http-invoke.js";async function U(n){const e=await c().request("GET",`/v1/providers/${encodeURIComponent(n)}/tools`);return new Set(e.groups.flatMap(r=>r.leaves.map(a=>a.tool)))}p(U,"allowedProviderTools");function m(n,e,r){return n.children.some(a=>a.kind==="leaf"?r.has([...e,a.name].join(" ")):m(a,[...e,a.name],r))}p(m,"hasAllowedLeaf");function M(n,e,r){return n.children.filter(a=>a.tier!=="hidden"&&(a.kind==="leaf"?r.has([...e,a.name].join(" ")):m(a,[...e,a.name],r))).length}p(M,"visibleChildCount");function T(n,e,r){return async()=>{if(!E())return n.help;const a=await U(e.id);return{...n.help,listing:n.help.listing?.flatMap(s=>{const t=n.children.find(o=>o.name===s.name);if(t?.kind==="leaf")return a.has([...r,t.name].join(" "))?[s]:[];if(t?.kind!=="branch")return[];const i=[...r,t.name];if(!m(t,i,a))return[];const u=M(t,i,a);return[{...s,...s.subCount!==void 0?{subCount:u}:{}}]})}}}p(T,"providerHelp");function g(n,e,r){return{outputKind:"object",run:p(async a=>{const s={...a};for(const t of r.params){if(!("type"in t)||t.type!=="file")continue;const i=t.name.replace(/-([a-z])/g,(o,d)=>d.toUpperCase()),u=s[i];typeof u=="string"&&(s[i]=k(u))}return c({timeoutMs:0}).request("POST","/v1/provider/call",{provider:n,tool:e,arguments:s},{timeout:0})},"run")}}p(g,"providerLeafAdapter");function H(n,e){const r=[],a=[];for(const s of n){const{provider:t,commands:i}=s;if(!/^[A-Za-z0-9_-]{1,64}$/.test(t)||t==="crtr"||e.has(t)||!Array.isArray(i))continue;if(s.status!=="available"){const o=s.error??"bundle installation pending",d=`Unavailable (${s.status}): ${o}`,f=h({name:t,rootEntry:{concept:s.display_name,desc:d,useWhen:d},help:{name:t,summary:d},children:[]});f.unavailable=d,a.push(f);continue}const u=p(o=>o.kind==="branch"?{...o,children:o.children.map(u)}:{...o,rest:{method:"GET",path:"/",params:{}}},"helpNode");for(const o of i)o.name!==t||o.kind!=="branch"||!Array.isArray(o.children)||r.push({contributor:{name:t,scope:"user"},manifestName:o.name,node:u(o),adaptLeaf:p((d,f)=>g(t,f.slice(1).join(" "),d),"adaptLeaf")})}return[...A(w(r,e)),...a]}p(H,"composeSandboxProviderSubtrees");function v(n,e){return(r,a)=>{const s=n.transport,t=e(a);if(n.provider!==void 0)return g(n.provider.id,t.slice(1).join(" "),r);if(s.kind==="exec"){const u=s.executable;if(u===void 0)throw new Error(`exec plugin "${n.plugin.name}" has an executable leaf without transport.executable`);return{outputKind:"object",run:p(async o=>C({plugin:n.plugin,executable:u,commandPath:t,output:r.output},o),"run")}}const i=r;return{outputKind:i.rest.streaming===!0?"jsonl":"object",run:p((u,o)=>L({registration:{name:n.plugin.name,endpoint:s.endpoint,...s.authEnv!==void 0?{authEnv:s.authEnv}:{}},...n.manifest?.baseUrl!==void 0?{baseUrl:n.manifest.baseUrl}:{},...n.manifest?.timeouts!==void 0?{timeouts:n.manifest.timeouts}:{},params:i.params,rest:i.rest,output:i.output,commandPath:t},u,o),"run")}}}p(v,"adaptLeaf");function _(n){return{contributions:n.map(e=>({contributor:{name:e.plugin.name,scope:e.plugin.scope,sourceMarketplace:e.plugin.sourceMarketplace},node:e.node,manifestName:e.manifestName,adaptLeaf:v(e,r=>[e.manifestName,...r.slice(1)]),...e.provider!==void 0?{provider:e.provider}:{}})),coreMounts:[],issues:[]}}p(_,"adaptPluginContributions");function q(n){return n.map(e=>({parent:e.parent,plugin:e.plugin.name,contributor:{name:e.plugin.name,scope:e.plugin.scope,sourceMarketplace:e.plugin.sourceMarketplace},node:e.node,...e.provider!==void 0?{provider:e.provider}:{},adaptLeaf:v({...e,manifestName:e.node.name},r=>e.provider===void 0?[...r]:[e.provider.id,...r.slice(e.parent.length)])}))}p(q,"adaptPluginCoreMounts");function A(n){return n.contributions.map(e=>l(e,e.node,[e.node.name]))}p(A,"composeExternalSubtrees");function z(n,e){return n.filter(r=>r.parent.join("\0")===e.join("\0")).map(r=>r.node.kind==="branch"?l(r,r.node,[...e,r.node.name],r.provider===void 0?1:e.length):y(r,r.node,[...e,r.node.name]))}p(z,"composeCoreBranchMounts");function l(n,e,r,a=1){const s=e.rootEntry===void 0?void 0:{concept:e.rootEntry.concept,desc:e.rootEntry.description,useWhen:e.rootEntry.whenToUse},t=h({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...s!==void 0?{rootEntry:s}:{},help:{name:r.join(" "),summary:e.summary,...e.model!==void 0?{model:e.model}:{},...r.length===1&&n.extensionIssue!==void 0?{extensionIssue:n.extensionIssue}:{}},...e.passthrough!==void 0?{passthrough:e.passthrough}:{},children:e.children.map(i=>i.kind==="branch"?l(n,i,[...r,i.name],a):y(n,i,[...r,i.name]))});return n.provider!==void 0&&(t.effectiveHelp=T(t,n.provider,r.slice(a))),t}p(l,"buildBranch");function y(n,e,r){const a=n.adaptLeaf(e,r);return j({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},help:{name:r.join(" "),summary:e.summary,params:e.params,output:e.output,outputKind:a.outputKind,effects:[...e.effects]},run:a.run})}p(y,"buildLeaf");export{_ as adaptPluginContributions,q as adaptPluginCoreMounts,z as composeCoreBranchMounts,A as composeExternalSubtrees,H as composeSandboxProviderSubtrees};
1
+ var k=Object.defineProperty;var u=(n,e)=>k(n,"name",{value:e,configurable:!0});import{resolve as w}from"node:path";import{envNodeId as x}from"../../shared/env.js";import{cliClient as f}from"../../commands/api-client.js";import{defineBranch as h,defineLeaf as C}from"../command.js";import{resolveCommandRegistry as j}from"../command-manifests/registry.js";import{executeExternalLeaf as L}from"./transport/exec-invoke.js";import{invokeHttpLeaf as U}from"./transport/http-invoke.js";async function M(n){const e=await f().request("GET",`/v1/providers/${encodeURIComponent(n)}/tools`);return new Set(e.groups.flatMap(r=>r.leaves.map(a=>a.tool)))}u(M,"allowedProviderTools");function m(n,e,r){return n.children.some(a=>a.kind==="leaf"?r.has([...e,a.name].join(" ")):m(a,[...e,a.name],r))}u(m,"hasAllowedLeaf");function T(n,e,r){return n.children.filter(a=>a.tier!=="hidden"&&(a.kind==="leaf"?r.has([...e,a.name].join(" ")):m(a,[...e,a.name],r))).length}u(T,"visibleChildCount");function S(n,e,r){return async()=>{if(!x())return n.help;const a=await M(e.id);return{...n.help,listing:n.help.listing?.flatMap(i=>{const t=n.children.find(s=>s.name===i.name);if(t?.kind==="leaf")return a.has([...r,t.name].join(" "))?[i]:[];if(t?.kind!=="branch")return[];const o=[...r,t.name];if(!m(t,o,a))return[];const p=T(t,o,a);return[{...i,...i.subCount!==void 0?{subCount:p}:{}}]})}}}u(S,"providerHelp");const A="Runs on one of your devices. Add `--machine <machine_id>` to pick which; without it the call goes to your only device, and with several the answer is `machine_required` listing each one's machine_id. The `runtime:` prefix on the id is optional.";function g(n,e,r,a=!1){return{outputKind:"object",...a?{machineSelector:!0}:{},run:u(async(i,t)=>{const o={...i};for(const p of r.params){if(!("type"in p)||p.type!=="file")continue;const s=p.name.replace(/-([a-z])/g,(c,E)=>E.toUpperCase()),d=o[s];typeof d=="string"&&(o[s]=w(d))}return f({timeoutMs:0}).request("POST","/v1/provider/call",{provider:n,tool:e,arguments:o,...t?.machine===void 0?{}:{machine:t.machine}},{timeout:0})},"run")}}u(g,"providerLeafAdapter");function q(n,e){const r=[],a=[];for(const i of n){const{provider:t,commands:o}=i;if(!/^[A-Za-z0-9_-]{1,64}$/.test(t)||t==="crtr"||e.has(t)||!Array.isArray(o))continue;if(i.status!=="available"){const s=i.error??"bundle installation pending",d=`Unavailable (${i.status}): ${s}`,c=h({name:t,rootEntry:{concept:i.display_name,desc:d,useWhen:d},help:{name:t,summary:d},children:[]});c.unavailable=d,a.push(c);continue}const p=u(s=>s.kind==="branch"?{...s,children:s.children.map(p)}:{...s,rest:{method:"GET",path:"/",params:{}}},"helpNode");for(const s of o)s.name!==t||s.kind!=="branch"||!Array.isArray(s.children)||r.push({contributor:{name:t,scope:"user"},manifestName:s.name,node:p(s),adaptLeaf:u((d,c)=>g(t,c.slice(1).join(" "),d,i.transport==="machine"),"adaptLeaf")})}return[...N(j(r,e)),...a]}u(q,"composeSandboxProviderSubtrees");function v(n,e){return(r,a)=>{const i=n.transport,t=e(a);if(n.provider!==void 0)return g(n.provider.id,t.slice(1).join(" "),r,i.kind==="machine");if(i.kind==="exec"){const p=i.executable;if(p===void 0)throw new Error(`exec plugin "${n.plugin.name}" has an executable leaf without transport.executable`);return{outputKind:"object",run:u(async s=>L({plugin:n.plugin,executable:p,commandPath:t,output:r.output},s),"run")}}if(i.kind==="machine")throw new Error(`machine plugin "${n.plugin.name}" is not a capability provider`);const o=r;return{outputKind:o.rest.streaming===!0?"jsonl":"object",run:u((p,s)=>U({registration:{name:n.plugin.name,endpoint:i.endpoint,...i.authEnv!==void 0?{authEnv:i.authEnv}:{}},...n.manifest?.baseUrl!==void 0?{baseUrl:n.manifest.baseUrl}:{},...n.manifest?.timeouts!==void 0?{timeouts:n.manifest.timeouts}:{},params:o.params,rest:o.rest,output:o.output,commandPath:t},p,s),"run")}}}u(v,"adaptLeaf");function B(n){return{contributions:n.map(e=>({contributor:{name:e.plugin.name,scope:e.plugin.scope,sourceMarketplace:e.plugin.sourceMarketplace},node:e.node,manifestName:e.manifestName,adaptLeaf:v(e,r=>[e.manifestName,...r.slice(1)]),...e.provider!==void 0?{provider:e.provider}:{}})),coreMounts:[],issues:[]}}u(B,"adaptPluginContributions");function G(n){return n.map(e=>({parent:e.parent,plugin:e.plugin.name,contributor:{name:e.plugin.name,scope:e.plugin.scope,sourceMarketplace:e.plugin.sourceMarketplace},node:e.node,...e.provider!==void 0?{provider:e.provider}:{},adaptLeaf:v({...e,manifestName:e.node.name},r=>e.provider===void 0?[...r]:[e.provider.id,...r.slice(e.parent.length)])}))}u(G,"adaptPluginCoreMounts");function N(n){return n.contributions.map(e=>l(e,e.node,[e.node.name]))}u(N,"composeExternalSubtrees");function z(n,e){return n.filter(r=>r.parent.join("\0")===e.join("\0")).map(r=>r.node.kind==="branch"?l(r,r.node,[...e,r.node.name],r.provider===void 0?1:e.length):y(r,r.node,[...e,r.node.name]))}u(z,"composeCoreBranchMounts");function l(n,e,r,a=1){const i=e.rootEntry===void 0?void 0:{concept:e.rootEntry.concept,desc:e.rootEntry.description,useWhen:e.rootEntry.whenToUse},t=h({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...i!==void 0?{rootEntry:i}:{},help:{name:r.join(" "),summary:e.summary,...e.model!==void 0?{model:e.model}:{},...r.length===1&&n.extensionIssue!==void 0?{extensionIssue:n.extensionIssue}:{}},...e.passthrough!==void 0?{passthrough:e.passthrough}:{},children:e.children.map(o=>o.kind==="branch"?l(n,o,[...r,o.name],a):y(n,o,[...r,o.name]))});return n.provider!==void 0&&(t.effectiveHelp=S(t,n.provider,r.slice(a))),t}u(l,"buildBranch");function y(n,e,r){const a=n.adaptLeaf(e,r);return C({name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...n.plugin!==void 0?{plugin:n.plugin}:{},...a.machineSelector===!0?{machineSelector:!0}:{},help:{name:r.join(" "),summary:e.summary,...a.machineSelector===!0?{guide:A}:{},params:e.params,output:e.output,outputKind:a.outputKind,effects:[...e.effects]},run:a.run})}u(y,"buildLeaf");export{B as adaptPluginContributions,G as adaptPluginCoreMounts,z as composeCoreBranchMounts,N as composeExternalSubtrees,q as composeSandboxProviderSubtrees};