@north-light/cli 0.3.515 → 0.3.518

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/builtin-memory/crouter-plugin/bundles-and-memory.md +7 -1
  4. package/dist/builtin-memory/crouter-plugin/commands.md +18 -1
  5. package/dist/builtin-memory/crouter-sdk/release-notes.md +7 -0
  6. package/dist/clients/attach/render/doc-links.d.ts +5 -0
  7. package/dist/clients/attach/render/doc-links.js +1 -1
  8. package/dist/clients/attach/render/markdown-source.d.ts +5 -0
  9. package/dist/clients/attach/render/markdown-source.js +5 -5
  10. package/dist/clients/attach/session/document-links.js +1 -1
  11. package/dist/clients/attach/viewer.js +544 -535
  12. package/dist/clients/inbox/controller.js +1 -1
  13. package/dist/clients/inbox/tui/page-body.js +1 -1
  14. package/dist/clients/inbox/tui/render.js +2 -2
  15. package/dist/commands/sys/env.js +1 -1
  16. package/dist/core/command-plugins/bundle.js +1 -1
  17. package/dist/core/human/component-docs.js +5 -5
  18. package/dist/core/human/page-schema.d.ts +2 -0
  19. package/dist/core/human/page-schema.js +4 -1
  20. package/dist/core/human/page.js +6 -1
  21. package/dist/core/human/requests.d.ts +4 -1
  22. package/dist/core/human/requests.js +18 -18
  23. package/dist/core/profiles/app-env-store.js +1 -1
  24. package/dist/core/profiles/env-store.d.ts +3 -0
  25. package/dist/core/profiles/env-store.js +2 -2
  26. package/dist/core/runs/operations.js +4 -4
  27. package/dist/core/runs/questions.d.ts +6 -1
  28. package/dist/core/runs/questions.js +3 -3
  29. package/dist/core/termrender/version.d.ts +1 -1
  30. package/dist/core/termrender/version.js +1 -1
  31. package/dist/core/usage/ledger.js +4 -4
  32. package/dist/daemon/api/handlers/questions.d.ts +1 -1
  33. package/dist/daemon/api/handlers/questions.js +1 -1
  34. package/dist/daemon/api/operations.js +1 -1
  35. package/dist/daemon/crtrd.js +8 -8
  36. package/dist/launcher/main.d.ts +1 -1
  37. package/dist/launcher/main.js +1 -3
  38. package/dist/launcher/run.d.ts +5 -0
  39. package/dist/launcher/run.js +3 -0
  40. package/dist/shared/env.d.ts +5 -0
  41. package/dist/shared/env.js +2 -2
  42. package/docs/ci-runner.md +4 -4
  43. package/docs/plugin/bundles-and-memory.md +7 -1
  44. package/docs/plugin/commands.md +18 -1
  45. package/docs/sdk/release-notes.md +7 -0
  46. package/package.json +2 -2
  47. package/packages/crouter-identity/package.json +1 -1
  48. package/runtime.lock.json +12 -12
@@ -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,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
+ var R=Object.defineProperty;var o=(d,e)=>R(d,"name",{value:e,configurable:!0});import{existsSync as P,watch as F}from"node:fs";import{visibleWidth as C}from"@earendil-works/pi-tui";import{renderMarkdown as M}from"../../core/termrender/termrender.js";import{BOLD as L,CYAN as W,DIM as h,GRAY as q,RESET as a,YELLOW as k,clipLine as p,singleLine as O,wrap as E}from"./tui/ansi.js";import{buildInboxLines as N,recommendedLabel as H}from"./tui.js";import{inboxLayout as g}from"./layout.js";import{resolvePageComponents as T}from"../../core/config.js";import{watchInboxActivity as B}from"../../core/human/root.js";import{claimTicket as x,heartbeatClaim as _,releaseClaim as u}from"../../core/human/claim.js";import{claimPath as z,clearProgress as V}from"../../core/human/convention.js";import{emitEvent as j}from"../../core/events/emit.js";import{cliClient as f}from"../../commands/api-client.js";import{projectPageDisplayMarkdown as K}from"../../core/human/page-markdown.js";import{projectHtmlDocumentMarkdown as Y}from"../../core/human/page-html-markdown.js";import{pageBodyLines as G}from"./tui/page-body.js";import{docLinksRevision as b}from"../attach/render/doc-links.js";import{byNewestFirst as X,inboxRowFromDTO as J,pageViewFromDTO as y,settledStatusOf as $}from"./dto-adapt.js";import{PageAdapter as Q}from"./page-adapter.js";import{ReviewAdapter as U}from"./review-adapter.js";import{createReviewClient as Z}from"./review/review-client.js";import{editBufferInEditor as ee}from"./review/roundtrip.js";import{daemonErrorMessage as m,dismissTicketViaDaemon as te,resolveFailureKind as I,resolveTicketPage as S}from"./resolve.js";class Se{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??ie,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&&V(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&&_(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[`${k}Resize terminal to at least 60\xD718 to use inbox.${a}`];const t=N(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-C(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(D(t)){this.status=w(t),await this.rescan();return}const i=x(e.dir);if(i===null){this.status="ticket is being edited by another inbox";return}const s=e.fastAction.kind==="acknowledge"?"acknowledged":`sent: ${H(e)??"recommended answer"}`;this.submittingDir=e.dir;try{await S(e.id,e.fastAction.responses),this.status=s}catch(n){this.status=I(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 Z().cancel(e.id,"Canceled from the inbox."):await te(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(D(t)){this.status=w(t),await this.rescan();return}const i=x(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=$(r),await this.rescan();return}s=y(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 Q({ticketId:e.inboxTicketId,page:s,productKinds:T(),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 U({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(y(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 S(t.id,e)}catch(s){this.submittingDir=void 0,this.status=m(s);const n=I(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);j({level:i?"warn":"info",event:"inbox.page.resolved",fields:{ticket_id:t.id,request_settled:D(await this.requestOrNull(t.id)),still_listed:i,claim_left_behind:P(z(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=ee(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&&i.links===b())return i.lines;const s=A(i.view,t);return this.preview={...i,width:t,links:b(),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=y(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,links:b(),lines:A(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=[...E(O(i.title),Math.max(10,e-2)).map(r=>` ${L}${W}${r}${a}`),""];for(const r of M(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]=`${k}${t}${a}`,i}markDirty(){this.options.onDirty()}watchRoots(){const e=B(this.options.onInboxActivity??(()=>{this.invalidate()}));this.watchers.push({close:e})}watchSelected(e){this.selectedWatcher?.close();try{this.selectedWatcher=F(e,()=>{this.invalidate()})}catch{this.selectedWatcher=void 0}}}async function ie(){const{tickets:d}=await f().listHumanInbox();return d.map(J).sort(X)}o(ie,"defaultScan");function A(d,e){const t=d.manifest.dialect==="html"?Y(d.document):K(d.document,d.manifest);return G(d.manifest,t,Math.min(Math.max(20,e-4),100),Math.max(20,e-4))}o(A,"previewLines");function D(d){return d!==void 0&&d.state!=="pending"}o(D,"isSettled");function w(d){return d?.page===void 0?"ticket was already resolved":$(d.page)}o(w,"settledStatus");export{Se 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 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
+ 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 w,tableMarkdown as S,terminalAnswerable as x}from"./slots.js";import{rewriteMarkdownDocLinks as y}from"../../attach/render/markdown-source.js";import{BOLD as b,DIM as l,RESET as o,sanitize as n,singleLine as d,truncate as k,wrap as u}from"./ansi.js";function N(s,r,f,m){const p=$(t=>M(y(t),f,m),"md");if(s.dialect==="html")return r===""?[`${l}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(w(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(`${l}${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(S(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 c of u(n(i.body),Math.max(10,f-6)))e.push(` ${l}${c}${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 c of u(n(i.description),Math.max(10,f-6)))e.push(` ${l}${c}${o}`)}else if(t.kind==="chart"){const i=t.config,c=i.title??`${i.x??""} vs ${(i.y??[]).map(g=>g.label??g.field).join(", ")}`;e.push(`${l}chart \u2014 ${n(i.kind??"chart")}: ${n(c)}${o}`)}}return e}$(N,"pageBodyLines");export{N as pageBodyLines};
@@ -1,2 +1,2 @@
1
- var K=Object.defineProperty;var a=(n,s)=>K(n,"name",{value:s,configurable:!0});import E from"string-width";import{isResponseBearingSlot as I}from"../../../core/human/page-schema.js";import{answerCommentAnchorLabel as Q,describePageAnswer as U}from"../../../core/human/answer.js";import{renderMarkdownBlockAwareLines as X}from"../../../core/termrender/termrender.js";import{commentAnchors as Z,describeSlot as L,pageComplete as nn,slotActions as en,slotAnswered as j,tableMarkdown as tn,terminalAnswerable as R}from"./slots.js";import{RESET as r,BOLD as M,DIM as u,GREEN as W,YELLOW as O,CYAN as y,REVERSE as N,sanitize as x,singleLine as g,truncate as q,hline as w,wrap as k,centerHorizontal as T,clipLine as H,panLine as sn,visibleWidth as C}from"./ansi.js";const on=8,S=a((n,s,t)=>X(n,s,t),"mdLines");function P(n){return U(n.manifest,Object.fromEntries(n.responses),{completedAt:""})}a(P,"pageAnswer");function rn(n,s){return s.id===void 0?void 0:n.slots.find(t=>t.id===s.id)}a(rn,"answerSlot");function z(n){if(n===void 0||n.state==="unanswered")return"unanswered";if(n.raw!==void 0)return"custom response";const s=n.text!==void 0?[n.text.edited?"edited":"accepted"]:n.groups.map(t=>t.picked.length===0?"none":t.picked.map(e=>e.label).join(", "));return n.freetext!==void 0&&s.push(n.freetext),n.comments.length>0&&s.push(`${n.comments.length} comment${n.comments.length===1?"":"s"}`),s.join("; ")||"answered"}a(z,"answerSummary");function D(n,s){return Q(n,s)}a(D,"commentLabel");function xn(n,s,t){const e=Math.min(Math.max(20,s-4),120),o=Math.max(20,s-4),l=["",...v(g(n.manifest.title),e,"",0,`${M}${y}`)];if(n.manifest.subtitle)for(const h of k(x(n.manifest.subtitle),e))l.push(` ${u}${h}${r}`);if(n.displayMarkdown!==""){l.push("");for(const h of S(n.displayMarkdown,e,o))l.push(` ${h}`)}else n.manifest.dialect==="html"&&l.push("",` ${u}HTML page \u2014 open in Northlight to read this${r}`);const i=n.slots.length>0;i&&l.push(` ${u}${w(e)}${r}`,"");const f=P(n);let c=-1,$;for(let h=0;h<n.slots.length;h++){const p=n.slots[h];n.manifest.steps>1&&p.step!==c&&(c!==-1&&l.push(""),l.push(` ${M}Step ${p.step+1}/${n.manifest.steps}${r}`),c=p.step);const b=p.id===void 0?void 0:n.responses.get(p.id),A=I(p)?j(n,p)?`${W}\u2713${r}`:`${u}\u25CB${r}`:`${u}\xB7${r}`,J=h===n.currentIndex?`${y}\u25B8${r} `:" ";h===n.currentIndex&&($=l.length),l.push(` ${J}${A} ${q(g(L(p)),Math.max(10,s-16))}`),b!==void 0&&l.push(` ${u}${q(g(z(rn(f,p))),Math.max(10,s-10))}${r}`)}if(n.manifest.slots.some(h=>I(h)&&!R(h))){l.push("");for(const h of k("This page must be answered in Northlight. Leaving it here keeps it in your inbox; x on the list cancels it.",e-2))l.push(` ${O}${h}${r}`)}i&&l.push(""),l.push(` ${u}${w(e)}${r}`);const d=!n.manifest.slots.some(I)?` ${u}u/d${r} scroll ${u}q${r} dismiss`:` ${u}enter${r} review ${u}\u2191\u2193/jk${r} navigate ${u}u/d${r} scroll ${u}q${r} close`;return _(l,d,n,s,t,e+2,n.followSelection?$:void 0)}a(xn,"renderOverview");function ln(n,s,t){const e=[...n],o=Math.max(0,Math.min(s,e.length)),l=F(n,t);let i=0;for(let $=0;$<l.length;$++)l[$].start<=o&&(i=$);const f=l.map(($,m)=>{if(m!==i)return e.slice($.start,$.end).join("");const d=e.slice($.start,o).join(""),h=o<$.end?e[o]:" ",p=o<$.end?e.slice(o+1,$.end).join(""):"";return`${d}${N}${h}${r}${p}`}),c=l[i];return o===c.end&&E(e.slice(c.start,c.end).join(""))>=t&&(f[i]=e.slice(c.start,c.end).join(""),f.splice(i+1,0,`${N} ${r}`)),f}a(ln,"renderInputBuffer");function Mn(n,s){const t=[...n];let e=Math.max(0,Math.min(s,t.length));for(;e>0&&/\s/.test(t[e-1]);)e--;for(;e>0&&!/\s/.test(t[e-1]);)e--;return e}a(Mn,"wordLeftIndex");function gn(n,s){const t=[...n];let e=Math.max(0,Math.min(s,t.length));for(;e<t.length&&/\s/.test(t[e]);)e++;for(;e<t.length&&!/\s/.test(t[e]);)e++;return e}a(gn,"wordRightIndex");function F(n,s){const t=[...n],e=Math.max(1,s),o=[];let l=0,i=0;for(let f=0;f<t.length;f++){const c=t[f];if(c===`
2
- `){o.push({start:l,end:f}),l=f+1,i=0;continue}const $=E(c);i+$>e?(o.push({start:l,end:f}),l=f,i=$):i+=$}return o.push({start:l,end:t.length}),o}a(F,"wrappedRowBounds");function wn(n,s,t,e){const o=F(n,e),l=Math.max(0,Math.min(s,[...n].length));let i=0;for(let c=0;c<o.length;c++)o[c].start<=l&&(i=c);const f=o[i+t];return f===void 0?l:Math.min(f.start+(l-o[i].start),f.end)}a(wn,"verticalCursor");function cn(n,s){return s.id===void 0?[]:n.responses.get(s.id)?.comments??[]}a(cn,"responseComments");function v(n,s,t,e,o){return k(n,Math.max(10,s-e)).map((l,i)=>` ${i===0?t:" ".repeat(e)}${o}${l}${r}`)}a(v,"hangingWrap");function fn(n,s,t,e){if(s.id===void 0)return!1;const o=n.responses.get(s.id);return t==="option"?o?.selectedOptionIds?.includes(e)===!0:t==="card"?o?.selectedCardIds?.includes(e)===!0:t==="row"?o?.selectedRowIds?.includes(e)===!0:o?.selectedColumnIds?.includes(e)===!0}a(fn,"selected");function Y(n,s){const t=n.slots[n.currentIndex],e=Math.min(Math.max(20,s-4),120),o=Math.max(20,s-4),l=`[${n.currentIndex+1}/${n.slots.length}]`,i=["",...v(g(n.manifest.title),e,`${M}${y}${l}${r} `,E(l)+2,u),` ${u}${w(e)}${r}`,"",...v(g(L(t)),e,"",0,M)],f=t.config.body;if(typeof f=="string"&&f.length>0){i.push("");for(const m of S(f,e,o))i.push(` ${m}`)}if(R(t)){if(t.kind==="chart"){const m=t.config,d=m.title??`${m.x} vs ${(m.y??[]).map(h=>h.label??h.field).join(", ")}`;i.push("",` ${u}chart \u2014 ${x(m.kind)}: ${x(d)}${r}`)}else if(t.kind==="table"){i.push("");for(const d of S(tn(t.config),e,o))i.push(` ${d}`);const m=t.config.label;m&&i.push(` ${u}${x(m)}${r}`)}else if(t.kind==="text"){const d=(t.id===void 0?void 0:n.responses.get(t.id))?.text??t.config.initialText;i.push("");for(const h of S(d,e,o))i.push(` ${h}`)}}else{i.push("");for(const m of k(`${x(t.kind)} \u2014 must be answered in Northlight; the terminal cannot submit this page`,Math.max(10,e-2)))i.push(` ${O}${m}${r}`);i.push(` ${u}q leaves it in your inbox; x on the list cancels it${r}`)}i.push("",` ${u}${w(e)}${r}`);let c;if(n.inputMode!==null){const m=n.inputMode.kind==="comment"?`Comment on ${D(t,n.inputMode.anchor)}`:n.inputMode.kind==="text"?"Edit text":"Other response";i.push(` ${M}${m}${r}`);for(const d of ln(n.inputMode.buffer,n.inputMode.cursor,Math.max(10,e-4)))i.push(` ${d}`);c=` ${u}enter save esc cancel${n.editorAvailable?" ^o editor":""}${r}`}else{en(t).forEach((d,h)=>{const p=h===n.selectedAction?`${y}\u25B8${r}`:" ";if(d.kind==="select"){const b=fn(n,t,d.group,d.id)?`${W}\u2713${r}`:" ";if(i.push(` ${p} [${b}] ${d.shortcut?`[${d.shortcut}] `:""}${x(d.label)}`),d.description)for(const A of k(x(d.description),Math.max(10,e-6)))i.push(` ${u}${A}${r}`)}else{const b=d.kind==="freetext"?"r":d.kind==="edit"?"e":"a";i.push(` ${p} [${b}] ${x(d.label)}`)}});for(const d of cn(n,t))for(const h of k(`attached: ${D(t,d.anchor)} \u2014 ${x(d.text)}`,Math.max(10,e-2)))i.push(` ${u}${h}${r}`);n.hint&&i.push(` ${O}${x(n.hint)}${r}`),c=` ${u}n/p next/previous \u2191\u2193/jk actions${Z(t).length>0?" c comment":""} u/d scroll \u2190\u2192/hl pan q close${r}`}const $=Math.max(0,...i.map(C));return{lines:i,footer:c,maxW:e,maxH:Math.max(0,$-Math.max(1,s))}}a(Y,"itemLines");function kn(n,s,t){if(n.slots[n.currentIndex]===void 0){n.scrollOffset=0,n.hscrollOffset=0,n.hscrollMax=0;return}const e=Y(n,s);n.hscrollMax=e.maxH,n.hscrollOffset=Math.max(0,Math.min(n.hscrollOffset,n.hscrollMax)),n.scrollOffset=Math.max(0,Math.min(n.scrollOffset,Math.max(0,e.lines.length-B(t))))}a(kn,"clampItemReviewScroll");function bn(n,s){n.hscrollOffset=Math.max(0,Math.min(n.hscrollMax,n.hscrollOffset+s*on))}a(bn,"panItemReview");function On(n,s,t){const e=Y(n,s),o=B(t),l=n.scrollOffset,i=e.lines.slice(l,l+o);for(;i.length<o;)i.push("");const f=n.hscrollOffset>0?i.map($=>sn($,n.hscrollOffset,s)):i,c=Math.min(s,e.maxW+2);return o<t&&f.push(V(e.footer,G(l,o,e.lines.length),e.maxW+2)),T(f.map($=>H($,s)),s,c)}a(On,"renderItemReview");function yn(n,s,t){const e=Math.min(Math.max(20,s-4),120),o=["",...v(`${g(n.manifest.title)} \u2014 Summary`,e,"",0,`${M}${y}`),` ${u}${w(e)}${r}`,""],l=P(n);for(const c of l.slots){const $=n.manifest.slots.find(h=>h.id===c.id),d=$!==void 0&&j(n,$)?`${W}\u2713${r}`:`${O}\u25CB${r}`;o.push(` ${d} ${M}${x($===void 0?c.kind:L($))}${r} ${u}${x(z(c))}${r}`)}const i=nn(n);if(!i){o.push("",` ${O}Cannot submit yet.${r}`);for(const c of n.slots.filter(I))j(n,c)||(o.push(` ${u}${c.id??c.kind} is unanswered${r}`),R(c)||o.push(` ${u}${c.id} must be answered in Northlight${r}`))}o.push("",` ${u}${w(e)}${r}`);const f=i?` ${u}enter${r} submit ${u}u/d${r} scroll ${u}q${r} back`:` ${u}u/d${r} scroll ${u}q${r} back`;return _(o,f,n,s,t,e+2)}a(yn,"renderFinal");function B(n){return n>=2?n-1:n}a(B,"bodyRows");function G(n,s,t){if(s<=0||t<=s)return{marks:"",full:""};const e=Math.min(t,n+s),o=`${n>0?"\u25B4":" "}${e<t?"\u25BE":" "}`;return{marks:`${u}${o}${r}`,full:`${u}${o} ${n+1}\u2013${e}/${t}${r}`}}a(G,"scrollBadge");function V(n,s,t){if(s.full==="")return n;const e=a(o=>t-C(n)-C(o)-2,"room");for(const o of[s.full,s.marks])if(e(o)>=2)return`${n}${" ".repeat(e(o))}${o}`;return` ${s.marks} ${n.trimStart()}`}a(V,"pinnedFooter");function _(n,s,t,e,o,l,i){const f=B(o),c=Math.max(0,n.length-f);t.scrollOffset=Math.max(0,Math.min(t.scrollOffset,c)),i!==void 0&&f>0&&(i<t.scrollOffset?t.scrollOffset=i:i>=t.scrollOffset+f&&(t.scrollOffset=Math.min(c,i-f+1)));const $=n.slice(t.scrollOffset,t.scrollOffset+f);for(;$.length<f;)$.push("");return f<o&&$.push(V(s,G(t.scrollOffset,f,n.length),l)),T($.map(m=>H(m,e)),e,Math.min(e,l))}a(_,"centeredWindow");export{kn as clampItemReviewScroll,bn as panItemReview,yn as renderFinal,ln as renderInputBuffer,On as renderItemReview,xn as renderOverview,wn as verticalCursor,Mn as wordLeftIndex,gn as wordRightIndex,F as wrappedRowBounds};
1
+ var K=Object.defineProperty;var a=(n,s)=>K(n,"name",{value:s,configurable:!0});import L 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{rewriteMarkdownDocLinks as Z}from"../../attach/render/markdown-source.js";import{commentAnchors as nn,describeSlot as E,pageComplete as en,slotActions as tn,slotAnswered as j,tableMarkdown as sn,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 on,visibleWidth as C}from"./ansi.js";const rn=8,S=a((n,s,t)=>X(Z(n),s,t),"mdLines");function D(n){return U(n.manifest,Object.fromEntries(n.responses),{completedAt:""})}a(D,"pageAnswer");function ln(n,s){return s.id===void 0?void 0:n.slots.find(t=>t.id===s.id)}a(ln,"answerSlot");function P(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(P,"answerSummary");function z(n,s){return Q(n,s)}a(z,"commentLabel");function gn(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=D(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(E(p)),Math.max(10,s-16))}`),b!==void 0&&l.push(` ${u}${q(g(P(ln(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(gn,"renderOverview");function cn(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&&L(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(cn,"renderInputBuffer");function wn(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(wn,"wordLeftIndex");function kn(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(kn,"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 $=L(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 bn(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(bn,"verticalCursor");function fn(n,s){return s.id===void 0?[]:n.responses.get(s.id)?.comments??[]}a(fn,"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 un(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(un,"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} `,L(l)+2,u),` ${u}${w(e)}${r}`,"",...v(g(E(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(sn(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 ${z(t,n.inputMode.anchor)}`:n.inputMode.kind==="text"?"Edit text":"Other response";i.push(` ${M}${m}${r}`);for(const d of cn(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{tn(t).forEach((d,h)=>{const p=h===n.selectedAction?`${y}\u25B8${r}`:" ";if(d.kind==="select"){const b=un(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 fn(n,t))for(const h of k(`attached: ${z(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${nn(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 On(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(On,"clampItemReviewScroll");function yn(n,s){n.hscrollOffset=Math.max(0,Math.min(n.hscrollMax,n.hscrollOffset+s*rn))}a(yn,"panItemReview");function In(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($=>on($,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(In,"renderItemReview");function Sn(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=D(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:E($))}${r} ${u}${x(P(c))}${r}`)}const i=en(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(Sn,"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{On as clampItemReviewScroll,yn as panItemReview,Sn as renderFinal,cn as renderInputBuffer,In as renderItemReview,gn as renderOverview,bn as verticalCursor,wn as wordLeftIndex,kn as wordRightIndex,F as wrappedRowBounds};
@@ -1 +1 @@
1
- var v=Object.defineProperty;var a=(e,n)=>v(e,"name",{value:n,configurable:!0});import{defineBranch as u,defineLeaf as r}from"../../core/command.js";import{InputError as i}from"../../core/io.js";import{validateEnvVarName as l}from"../../core/env-name.js";import{stripSingleTrailingLineEnding as p}from"../profile/env.js";import{cliClient as t}from"../api-client.js";function s(e){const n=e.name;if(!l(n).isValid)throw new i({error:"invalid_name",message:"invalid env var name",field:"name",next:"Use an env-var-shaped name."});return n}a(s,"name");const y=u({name:"env",description:"manage env vars for every profile of the calling app",whenToUse:"a plugin needs a credential available to every node in this app",help:{name:"sys env",summary:"write-only app env store; list returns names, never values",model:"A stored value reaches new launches immediately, and a running node on its next revive. Profile env overrides app env."},children:[r({name:"set",description:"store one app env value from stdin",whenToUse:"a plugin declares a required env name",help:{name:"sys env set",summary:"set one app-wide env variable without printing its value",params:[{kind:"flag",name:"name",type:"string",required:!0,constraint:"Env var name, not CRTR_* or HOME."},{kind:"stdin",name:"value",required:!0,constraint:"Pipe value on stdin, never argv. One trailing line ending is removed."}],output:[{name:"created",constraint:"Never includes a stored value.",type:"boolean",required:!0},{name:"follow_up",constraint:"Never includes a stored value.",type:"string",required:!0}],outputKind:"object",effects:["Writes the 0600 app env store; a running node picks up changes on next revive."]},run:a(async e=>{const n=p(e.value);if(!n)throw new i({error:"empty_value",message:"env var value is empty",field:"value",next:"Pipe a non-empty value on stdin."});const{changed:o}=await t().setAppEnv(s(e),n);return{created:o,follow_up:"A running node picks up the change on its next revive."}},"run")}),r({name:"list",description:"list app env names, not values",whenToUse:"checking which plugin credentials are set",help:{name:"sys env list",summary:"list stored app env names only",params:[],output:[{name:"names",constraint:"Never includes a stored value.",type:"string[]",required:!0},{name:"count",constraint:"Never includes a stored value.",type:"int",required:!0}],outputKind:"object",effects:["Read-only; never returns values."]},run:a(async()=>{const{names:e}=await t().listAppEnv();return{names:e,count:e.length}},"run")}),r({name:"remove",description:"remove one app env value",whenToUse:"the app no longer needs a plugin credential",help:{name:"sys env remove",summary:"delete a stored app env variable",params:[{kind:"flag",name:"name",type:"string",required:!0,constraint:"Env var name."}],output:[{name:"removed",constraint:"Never includes a stored value.",type:"boolean",required:!0}],outputKind:"object",effects:["A running node picks up the removal on its next revive."]},run:a(async e=>({removed:(await t().removeAppEnv(s(e))).changed}),"run")})]});export{y as sysEnvBranch};
1
+ var v=Object.defineProperty;var a=(e,n)=>v(e,"name",{value:n,configurable:!0});import{defineBranch as u,defineLeaf as r}from"../../core/command.js";import{InputError as i}from"../../core/io.js";import{validateEnvVarName as p}from"../../core/env-name.js";import{stripSingleTrailingLineEnding as l}from"../profile/env.js";import{cliClient as t}from"../api-client.js";function s(e){const n=e.name;if(!p(n).isValid)throw new i({error:"invalid_name",message:"invalid env var name",field:"name",next:"Use an env-var-shaped name."});return n}a(s,"name");const y=u({name:"env",description:"manage env vars for every profile of the calling app",whenToUse:"a plugin or the feedback pipeline needs a credential available to every node in this app",help:{name:"sys env",summary:"write-only app env store; list returns names, never values",model:"A stored value reaches new launches immediately, and a running node on its next revive. Profile env overrides app env."},children:[r({name:"set",description:"store one app env value from stdin",whenToUse:"a plugin declares a required env name, or the feedback endpoint and token are being configured",help:{name:"sys env set",summary:"set one app-wide env variable without printing its value",params:[{kind:"flag",name:"name",type:"string",required:!0,constraint:"Env var name. CRTR_* and HOME are reserved, except CRTR_FEEDBACK_ENDPOINT and CRTR_FEEDBACK_TOKEN."},{kind:"stdin",name:"value",required:!0,constraint:"Pipe value on stdin, never argv. One trailing line ending is removed."}],output:[{name:"created",constraint:"Never includes a stored value.",type:"boolean",required:!0},{name:"follow_up",constraint:"Never includes a stored value.",type:"string",required:!0}],outputKind:"object",effects:["Writes the 0600 app env store; a running node picks up changes on next revive."]},run:a(async e=>{const n=l(e.value);if(!n)throw new i({error:"empty_value",message:"env var value is empty",field:"value",next:"Pipe a non-empty value on stdin."});const{changed:o}=await t().setAppEnv(s(e),n);return{created:o,follow_up:"A running node picks up the change on its next revive."}},"run")}),r({name:"list",description:"list app env names, not values",whenToUse:"checking which plugin credentials are set",help:{name:"sys env list",summary:"list stored app env names only",params:[],output:[{name:"names",constraint:"Never includes a stored value.",type:"string[]",required:!0},{name:"count",constraint:"Never includes a stored value.",type:"int",required:!0}],outputKind:"object",effects:["Read-only; never returns values."]},run:a(async()=>{const{names:e}=await t().listAppEnv();return{names:e,count:e.length}},"run")}),r({name:"remove",description:"remove one app env value",whenToUse:"the app no longer needs a plugin credential",help:{name:"sys env remove",summary:"delete a stored app env variable",params:[{kind:"flag",name:"name",type:"string",required:!0,constraint:"Env var name."}],output:[{name:"removed",constraint:"Never includes a stored value.",type:"boolean",required:!0}],outputKind:"object",effects:["A running node picks up the removal on its next revive."]},run:a(async e=>({removed:(await t().removeAppEnv(s(e))).changed}),"run")})]});export{y as sysEnvBranch};
@@ -1 +1 @@
1
- var P=Object.defineProperty;var c=(e,o)=>P(e,"name",{value:o,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 B,sep as V}from"node:path";import{validateCommandManifest as z}from"../../api/command-manifest/manifest.js";import{ensureScopeInitialized as R,invalidPluginKindsReasons as F,readConfig as M,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{objectBlocksShapeReasons as Z}from"../plugin-objects-manifest.js";import{validateBundleTransport as L}from"./endpoint.js";const O=/^[A-Za-z0-9_-]{1,64}$/,Y=new Set(["id","major","groups"]),Q=new Set(["group","consent_line","rate","who","allowlist"]),ee=new Set(["own","user","allowlist","any"]),ne=new Set(["bundleVersion","kinds","page_components","memory_extensions","version","transport","provider","object_kinds","scopes"]);function oe(e,o,n,r,s,a){return{code:e,message:o,received:n,expected:r,next:s,...a!==void 0?{path:a}:{}}}c(oe,"issue");function u(e,o,n,r,s){return oe("plugin_bundle_invalid",e,o,n,r,s)}c(u,"bundleInvalid");function re(e){if(e[0]===31&&e[1]===139)return u("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 u("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(o=>o===0))return u("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.")}c(re,"archiveEnvelopeIssue");function se(e,o){if(e.length===0||e.includes("\0")||e.includes("\\")||e.startsWith("/")||/^[A-Za-z]:($|\/)/.test(e))return u("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(o==="file"&&e.endsWith("/"))return u("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((o==="directory"&&e.endsWith("/")?e.slice(0,-1):e).split("/").some(r=>r===""||r==="."||r===".."))return u("bundle member path contains an unsafe segment",e,"non-empty segments other than . or ..","Use a safe relative POSIX member path.",e)}c(se,"validateMemberName");function te(e,o){if(o==="file")return e==="bundle.json"||e==="commands.json"||e==="hooks.json"||e.startsWith("memory/")&&e.endsWith(".md")||e.startsWith("hooks/")?void 0:u("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 u("bundle contains an unsupported directory member",e,"memory or hooks, or a directory below either subtree","Remove unsupported directory members from the archive.",e)}c(te,"validateMemberShape");function ie(e){return e.type==="directory"&&e.path.endsWith("/")?e.path.slice(0,-1):e.path}c(ie,"materializedPath");function de(e){const o=new Map;for(const n of e){const r=ie(n),s=o.get(r);if(s!==void 0)return u(s===n.type?"bundle contains a duplicate member":"bundle claims one path as both a file and a directory",`${r} (${s} and ${n.type})`,"each materialized path exactly once, with one type","Emit each archive path once, as a single member type.",r);o.set(r,n.type)}for(const[n,r]of o)if(r==="file"){for(const s of o.keys())if(s.startsWith(`${n}/`))return u("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)}}c(de,"validateMemberTree");async function ae(e){const o=[],n=new Set;let r;return await new Promise((s,a)=>{const p=new J({strict:!0,maxMetaEntrySize:Number.MAX_SAFE_INTEGER,gzip:!1,brotli:!1,zstd:!1,onReadEntry:c(t=>{const d=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){r??=u("bundle member must be a regular file or directory without a link target",`${d} (${t.type})`,"a regular file or directory member without link metadata","Remove links and unsupported member types from the archive.",d),t.resume();return}r??=se(d,f),r??=te(d,f),n.has(d)&&(r??=u("bundle contains a duplicate member",d,"each member path exactly once","Emit each archive member only once.",d)),n.add(d);const i=[];t.on("data",b=>i.push(b)),t.on("end",()=>{r===void 0&&o.push({path:d,type:f,bytes:new Uint8Array(Buffer.concat(i))})})},"onReadEntry")});p.on("ignoredEntry",t=>{const d=t.path;r??=u("bundle contains an ignored or unsupported archive member",`${d} (${t.type})`,"only supported regular file or directory members","Remove ignored or unsupported members from the archive.",d),t.resume()}),p.once("error",a),p.once("end",s);try{p.end(Buffer.from(e))}catch(t){a(t)}}).catch(s=>{r??=u("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.")}),r===void 0?{members:o}:{issue:r}}c(ae,"parseArchive");function N(e){return typeof e=="number"&&Number.isInteger(e)&&e>=1}c(N,"isPositiveInt");function ue(e){const o=c((t,d,f,i)=>({issue:u(t,d,f,"Fix the provider block in bundle.json.",`bundle.json.${i}`)}),"bad");if(typeof e!="object"||e===null||Array.isArray(e))return o("bundle.json provider must be an object",JSON.stringify(e),"{ id, major, groups }","provider");const n=e,r=Object.keys(n).filter(t=>!Y.has(t));if(r.length>0)return o("bundle.json provider has unknown keys",r.join(", "),"only: id, major, groups","provider");if(typeof n.id!="string"||!O.test(n.id))return o("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 o("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 o("provider groups must be a non-empty array",JSON.stringify(s),"at least one { group, consent_line, rate?, who, allowlist }","provider.groups");const a=[],p=new Set;for(let t=0;t<s.length;t++){const d=`provider.groups[${t}]`,f=s[t];if(typeof f!="object"||f===null||Array.isArray(f))return o("provider group must be an object",JSON.stringify(f),"{ group, consent_line, rate?, who, allowlist }",d);const i=f,b=Object.keys(i).filter(v=>!Q.has(v));if(b.length>0)return o("provider group has unknown keys",b.join(", "),"only: group, consent_line, rate, who, allowlist",d);if(typeof i.group!="string"||!O.test(i.group))return o("provider group id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(i.group),'a group id of 1-64 letters, digits, "_" or "-"',`${d}.group`);if(p.has(i.group))return o("provider group ids must be unique",i.group,"each group declared once",`${d}.group`);if(p.add(i.group),typeof i.consent_line!="string"||i.consent_line.trim().length===0)return o("provider group consent_line must be a non-empty string",JSON.stringify(i.consent_line),"the consent line shown for this group",`${d}.consent_line`);const h=i.rate;if(h!=null&&(typeof h!="number"||!Number.isFinite(h)||h<0))return o("provider group rate must be a non-negative number or null",JSON.stringify(h),"a finite number >= 0, null, or omitted",`${d}.rate`);if(typeof i.who!="string"||!ee.has(i.who))return o("provider group who must be own|user|allowlist|any",JSON.stringify(i.who),"own | user | allowlist | any",`${d}.who`);const m=i.allowlist;if(!Array.isArray(m)||!m.every(v=>typeof v=="string"&&v.length>0))return o("provider group allowlist must be an array of non-empty strings",JSON.stringify(m),"an array of app principals ([] unless who is allowlist)",`${d}.allowlist`);if(m.length>0&&i.who!=="allowlist")return o("provider group allowlist must be empty unless who is allowlist",`who ${i.who}`,'who: "allowlist", or allowlist: []',`${d}.allowlist`);a.push({group:i.group,consent_line:i.consent_line,...h!==void 0?{rate:h}:{},who:i.who,allowlist:m})}return{provider:{id:n.id,major:n.major,groups:a}}}c(ue,"parseProviderBlock");function le(e){const o=[],n=c((r,s)=>{const a=[...s,r.name];if(r.kind==="leaf")o.push({path:a.join(" "),leaf:r});else for(const p of r.children)n(p,a)},"walk");for(const r of e.roots)n(r,[]);for(const r of e.coreMounts)n(r.node,r.parent);return o}c(le,"collectLeaves");function pe(e,o){const n=e.provider;if(n===void 0)return;if(e.transport===void 0)return u("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 u("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 r=new Set(n.groups.map(s=>s.group));for(const{path:s,leaf:a}of le(o)){const p="group"in a?a.group:void 0;if(p===void 0)return u(`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(!r.has(p))return u(`provider leaf "${s}" names undeclared group "${p}"`,p,`one of: ${[...r].join(", ")}`,`Declare group "${p}" 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 u(`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`)}}c(pe,"providerBundleIssue");function ce(e){let o;try{o=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issue:u("bundle.json is not valid JSON","invalid JSON",'{"bundleVersion":1}',"Write valid bundle metadata.","bundle.json")}}if(typeof o!="object"||o===null||Array.isArray(o))return{issue:u("bundle.json must be a JSON object",JSON.stringify(o),"an object with bundleVersion set to 1 and optionally kinds",'Set bundle.json to {"bundleVersion":1}.',"bundle.json")};const n=o,r=Object.keys(n).filter(d=>!ne.has(d));if(n.bundleVersion!==1||r.length>0)return{issue:u("bundle.json must contain bundleVersion 1 and at most version, transport, provider, kinds, page_components, memory_extensions, object_kinds, and scopes",JSON.stringify(o),"an object with bundleVersion set to 1 and optional version, transport, provider, kinds, page_components, memory_extensions, object_kinds, and scopes",'Set bundle.json to {"bundleVersion":1} plus optional contribution blocks.',"bundle.json")};if(n.kinds!==void 0){const d=F(n.kinds);if(d.length>0)return{issue:u("bundle.json kinds block is invalid",d.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 d=K(n.page_components);if(d.length>0)return{issue:u("bundle.json page_components block is invalid",d.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")}}const s=Z(n);if(n.provider!==void 0&&n.scopes!==void 0&&s.push("scopes is for local plugins; a provider gets its scopes from directory consent"),s.length>0)return{issue:u("bundle.json object_kinds or scopes block is invalid",s.join("; "),"object_kinds keyed by kind name with description, events and optional read.command; scopes an array of crtr:objects:<verb>:<target> strings (local plugins only)","Fix the object_kinds and scopes blocks in bundle.json.","bundle.json")};let a;if(n.memory_extensions!==void 0)try{a=X(n.memory_extensions)}catch(d){return{issue:u("bundle.json memory_extensions block is invalid",d instanceof Error?d.message:String(d),"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:u("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 p;if(n.transport!==void 0){const d=L(n.transport);if(d.transport===void 0)return{issue:u("bundle.json transport is invalid",d.errors.join("; "),'{ kind: "http", endpoint, authEnv? } or { kind: "machine" }',"Fix transport in bundle.json.","bundle.json.transport")};p=d.transport}let t;if(n.provider!==void 0){const d=ue(n.provider);if(d.issue!==void 0)return{issue:d.issue};if(t=d.provider,n.version!==void 0&&n.version!==t.major)return{issue:u("bundle.json provider major must equal version",`version ${String(n.version)}, major ${t.major}`,"one major","Set provider.major and version to the same integer.","bundle.json.provider.major")}}return{...n.version!==void 0?{version:n.version}:{},...p!==void 0?{transport:p}:{},...t!==void 0?{provider:t}:{},...n.kinds!==void 0?{kinds:n.kinds}:{},...n.page_components!==void 0?{page_components:G(n.page_components)}:{},...a!==void 0?{memory_extensions:a}:{},...n.object_kinds!==void 0?{object_kinds:n.object_kinds}:{},...n.scopes!==void 0?{scopes:n.scopes}:{}}}c(ce,"parseBundleMetadata");function me(e,o,n,r=new Set){let s;try{s=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issues:[u("commands.json is not valid JSON","invalid JSON","a valid command-manifest JSON object","Regenerate commands.json.","commands.json")]}}const a=z(s,{transport:"http",reservedCoreNames:o,extensibleCoreBranches:r,...n!==void 0?{coreCommandPaths:n}:{}});return{...a.manifest!==void 0?{commands:a.manifest}:{},issues:a.issues}}c(me,"parseCommands");async function fe(e,o){const n=re(e);if(n!==void 0)return{issues:[n]};const r=await ae(e);if(r.issue!==void 0||r.members===void 0)return{issues:[r.issue]};const s=de(r.members);if(s!==void 0)return{issues:[s]};const a=r.members.find(l=>l.path==="bundle.json"&&l.type==="file"),p=r.members.find(l=>l.path==="commands.json"&&l.type==="file");if(a===void 0||p===void 0)return{issues:[u("bundle must contain exactly one bundle.json and one commands.json regular file",`bundle.json=${a!==void 0}, commands.json=${p!==void 0}`,"one regular bundle.json and one regular commands.json member","Add the required metadata members to the archive.")]};const t=r.members.find(l=>l.path==="hooks.json"&&l.type==="file"),d=r.members.filter(l=>l.type==="file"&&l.path.startsWith("hooks/"));if((t!==void 0||r.members.some(l=>l.path==="hooks"||l.path.startsWith("hooks/")))&&(t===void 0||!d.some(l=>l.path==="hooks/dispatch")))return{issues:[u("bundle hook payload must contain both hooks.json and hooks/dispatch regular files",`hooks.json=${t!==void 0}, hooks/dispatch=${d.some(l=>l.path==="hooks/dispatch")}`,"both hooks.json and hooks/dispatch regular files","Add both required hook members or remove the hook payload.")]};const i=ce(a.bytes);if(i.issue!==void 0)return{issues:[i.issue]};const b=me(p.bytes,o.reservedCoreNames,o.coreCommandPaths,o.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:[u("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 m=i.version??h;if(i.provider!==void 0&&m===void 0)return{issues:[u("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&&m!==i.provider.major)return{issues:[u("bundle provider major must equal the plugin version",`version ${m}, major ${i.provider.major}`,"one major","Emit the provider major as the plugin version.","bundle.json.provider.major")]};const v=pe(i,b.commands);return v!==void 0?{issues:[v]}:{bundle:{bundleVersion:1,...m!==void 0?{version:m}:{},...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}:{},...i.object_kinds!==void 0?{object_kinds:i.object_kinds}:{},...i.scopes!==void 0?{scopes:i.scopes}:{},commands:b.commands,commandsBytes:p.bytes,directories:r.members.filter(l=>l.type==="directory").map(l=>({path:l.path})),memory:r.members.filter(l=>l.type==="file"&&l.path.startsWith("memory/")).map(l=>({path:l.path,bytes:l.bytes})),...t!==void 0?{hooks:{manifest:t.bytes,files:d.map(l=>({path:l.path,bytes:l.bytes}))}}:{}},issues:[]}}c(fe,"validatePluginBundle");function he(e){return I("sha256").update(e).digest("hex").slice(0,12)}c(he,"bundleVersion");function Pe(e,o,n,r){return M(o).plugins[e]?.version!==r?!1:_(w(n,"commands.json"))}c(Pe,"isInstalledGeneration");function be(e,o,n,r){C(e,s=>{s.plugins[o]===void 0&&(s.plugins[o]={}),r.swapped&&(s.plugins[o].last_updated=E()),s.plugins[o].checked_at=E(),n===void 0?delete s.plugins[o].etag:s.plugins[o].etag=n})}c(be,"recordCheckedGeneration");function g(e,o){const n=B(e,o);if(n===e||!n.startsWith(e+V))throw x(`bundle member path escapes the staging directory: ${o}`);return n}c(g,"stagedPath");function ve(e,o,n){const r=c((s,a,p=420)=>q(s,a,{mode:p,durable:!0}),"write");T(g(e,".crouter-plugin/plugin.json"),o,{durable:!0});for(const s of n.directories)j(g(e,s.path));r(g(e,"commands.json"),n.commandsBytes);for(const s of n.memory)r(g(e,s.path),s.bytes);if(n.hooks!==void 0){r(g(e,"hooks.json"),n.hooks.manifest);for(const s of n.hooks.files)r(g(e,s.path),s.bytes);W(g(e,"hooks/dispatch"),493)}H(e)}c(ve,"writeStagedBundle");async function Je(e,o,n){const{scope:r,scopeRootPath:s,root:a}=n,p=await fe(o,n.validation);if(p.bundle===void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,p.issues);const t=p.bundle,d=n.accept?.(t);if(d!==void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,[u(d,e,"the bundle this install asked for","Serve the requested bundle.")]);const f=n.version??he(o),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.object_kinds!==void 0?{object_kinds:t.object_kinds}:{},...t.scopes!==void 0?{scopes:t.scopes}:{},...t.hooks!==void 0?{hooks:"hooks.json",hookExecutable:"hooks/dispatch"}:{},...t.provider!==void 0?{provider:t.provider}:{}},h=w(s,"tmp"),m=w(h,`${e}.${process.pid}`),v=`${m}.prev`;if(_(v))throw x(`cannot replace plugin "${e}": prior interrupted replacement remains at ${v}`);j(h),j($(a)),S(m);let l;try{j(m),ve(m,b,t),l=await n.validateStaged(m,b);let y=!1;_(a)&&(k(a,v),y=!0);try{k(m,a)}catch(A){throw y&&k(v,a),A}U($(a)),y&&S(v)}finally{S(m)}return R(r,s),D(r,y=>{y.plugins[e]={enabled:n.enable?!0:y.plugins[e]?.enabled??!0,version:f}}),be(r,e,n.etag,{swapped:!0}),{manifest:b,validated:t,version:f,root:a,staged:l}}c(Je,"installBundleArchive");export{he as bundleVersion,Je as installBundleArchive,Pe as isInstalledGeneration,be as recordCheckedGeneration,fe as validatePluginBundle,ve as writeStagedBundle};
1
+ var P=Object.defineProperty;var c=(e,o)=>P(e,"name",{value:o,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 B,sep as V}from"node:path";import{validateCommandManifest as z}from"../../api/command-manifest/manifest.js";import{ensureScopeInitialized as R,invalidPluginKindsReasons as F,readConfig as M,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{objectBlocksShapeReasons as Z}from"../plugin-objects-manifest.js";import{validateBundleTransport as L}from"./endpoint.js";const O=/^[A-Za-z0-9_-]{1,64}$/,Y=new Set(["id","major","groups"]),Q=new Set(["group","consent_line","rate","who","allowlist"]),ee=new Set(["own","user","allowlist","any"]),ne=new Set(["bundleVersion","kinds","page_components","memory_extensions","version","transport","provider","object_kinds","scopes","groups"]);function oe(e,o,n,r,s,a){return{code:e,message:o,received:n,expected:r,next:s,...a!==void 0?{path:a}:{}}}c(oe,"issue");function u(e,o,n,r,s){return oe("plugin_bundle_invalid",e,o,n,r,s)}c(u,"bundleInvalid");function re(e){if(e[0]===31&&e[1]===139)return u("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 u("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(o=>o===0))return u("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.")}c(re,"archiveEnvelopeIssue");function se(e,o){if(e.length===0||e.includes("\0")||e.includes("\\")||e.startsWith("/")||/^[A-Za-z]:($|\/)/.test(e))return u("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(o==="file"&&e.endsWith("/"))return u("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((o==="directory"&&e.endsWith("/")?e.slice(0,-1):e).split("/").some(r=>r===""||r==="."||r===".."))return u("bundle member path contains an unsafe segment",e,"non-empty segments other than . or ..","Use a safe relative POSIX member path.",e)}c(se,"validateMemberName");function te(e,o){if(o==="file")return e==="bundle.json"||e==="commands.json"||e==="hooks.json"||e.startsWith("memory/")&&e.endsWith(".md")||e.startsWith("hooks/")?void 0:u("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 u("bundle contains an unsupported directory member",e,"memory or hooks, or a directory below either subtree","Remove unsupported directory members from the archive.",e)}c(te,"validateMemberShape");function ie(e){return e.type==="directory"&&e.path.endsWith("/")?e.path.slice(0,-1):e.path}c(ie,"materializedPath");function de(e){const o=new Map;for(const n of e){const r=ie(n),s=o.get(r);if(s!==void 0)return u(s===n.type?"bundle contains a duplicate member":"bundle claims one path as both a file and a directory",`${r} (${s} and ${n.type})`,"each materialized path exactly once, with one type","Emit each archive path once, as a single member type.",r);o.set(r,n.type)}for(const[n,r]of o)if(r==="file"){for(const s of o.keys())if(s.startsWith(`${n}/`))return u("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)}}c(de,"validateMemberTree");async function ae(e){const o=[],n=new Set;let r;return await new Promise((s,a)=>{const p=new J({strict:!0,maxMetaEntrySize:Number.MAX_SAFE_INTEGER,gzip:!1,brotli:!1,zstd:!1,onReadEntry:c(t=>{const d=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){r??=u("bundle member must be a regular file or directory without a link target",`${d} (${t.type})`,"a regular file or directory member without link metadata","Remove links and unsupported member types from the archive.",d),t.resume();return}r??=se(d,f),r??=te(d,f),n.has(d)&&(r??=u("bundle contains a duplicate member",d,"each member path exactly once","Emit each archive member only once.",d)),n.add(d);const i=[];t.on("data",b=>i.push(b)),t.on("end",()=>{r===void 0&&o.push({path:d,type:f,bytes:new Uint8Array(Buffer.concat(i))})})},"onReadEntry")});p.on("ignoredEntry",t=>{const d=t.path;r??=u("bundle contains an ignored or unsupported archive member",`${d} (${t.type})`,"only supported regular file or directory members","Remove ignored or unsupported members from the archive.",d),t.resume()}),p.once("error",a),p.once("end",s);try{p.end(Buffer.from(e))}catch(t){a(t)}}).catch(s=>{r??=u("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.")}),r===void 0?{members:o}:{issue:r}}c(ae,"parseArchive");function N(e){return typeof e=="number"&&Number.isInteger(e)&&e>=1}c(N,"isPositiveInt");function ue(e){const o=c((t,d,f,i)=>({issue:u(t,d,f,"Fix the provider block in bundle.json.",`bundle.json.${i}`)}),"bad");if(typeof e!="object"||e===null||Array.isArray(e))return o("bundle.json provider must be an object",JSON.stringify(e),"{ id, major, groups }","provider");const n=e,r=Object.keys(n).filter(t=>!Y.has(t));if(r.length>0)return o("bundle.json provider has unknown keys",r.join(", "),"only: id, major, groups","provider");if(typeof n.id!="string"||!O.test(n.id))return o("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 o("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 o("provider groups must be a non-empty array",JSON.stringify(s),"at least one { group, consent_line, rate?, who, allowlist }","provider.groups");const a=[],p=new Set;for(let t=0;t<s.length;t++){const d=`provider.groups[${t}]`,f=s[t];if(typeof f!="object"||f===null||Array.isArray(f))return o("provider group must be an object",JSON.stringify(f),"{ group, consent_line, rate?, who, allowlist }",d);const i=f,b=Object.keys(i).filter(v=>!Q.has(v));if(b.length>0)return o("provider group has unknown keys",b.join(", "),"only: group, consent_line, rate, who, allowlist",d);if(typeof i.group!="string"||!O.test(i.group))return o("provider group id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(i.group),'a group id of 1-64 letters, digits, "_" or "-"',`${d}.group`);if(p.has(i.group))return o("provider group ids must be unique",i.group,"each group declared once",`${d}.group`);if(p.add(i.group),typeof i.consent_line!="string"||i.consent_line.trim().length===0)return o("provider group consent_line must be a non-empty string",JSON.stringify(i.consent_line),"the consent line shown for this group",`${d}.consent_line`);const h=i.rate;if(h!=null&&(typeof h!="number"||!Number.isFinite(h)||h<0))return o("provider group rate must be a non-negative number or null",JSON.stringify(h),"a finite number >= 0, null, or omitted",`${d}.rate`);if(typeof i.who!="string"||!ee.has(i.who))return o("provider group who must be own|user|allowlist|any",JSON.stringify(i.who),"own | user | allowlist | any",`${d}.who`);const m=i.allowlist;if(!Array.isArray(m)||!m.every(v=>typeof v=="string"&&v.length>0))return o("provider group allowlist must be an array of non-empty strings",JSON.stringify(m),"an array of app principals ([] unless who is allowlist)",`${d}.allowlist`);if(m.length>0&&i.who!=="allowlist")return o("provider group allowlist must be empty unless who is allowlist",`who ${i.who}`,'who: "allowlist", or allowlist: []',`${d}.allowlist`);a.push({group:i.group,consent_line:i.consent_line,...h!==void 0?{rate:h}:{},who:i.who,allowlist:m})}return{provider:{id:n.id,major:n.major,groups:a}}}c(ue,"parseProviderBlock");function le(e){const o=[],n=c((r,s)=>{const a=[...s,r.name];if(r.kind==="leaf")o.push({path:a.join(" "),leaf:r});else for(const p of r.children)n(p,a)},"walk");for(const r of e.roots)n(r,[]);for(const r of e.coreMounts)n(r.node,r.parent);return o}c(le,"collectLeaves");function pe(e,o){const n=e.provider;if(n===void 0)return;if(e.transport===void 0)return u("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 u("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 r=new Set(n.groups.map(s=>s.group));for(const{path:s,leaf:a}of le(o)){const p="group"in a?a.group:void 0;if(p===void 0)return u(`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(!r.has(p))return u(`provider leaf "${s}" names undeclared group "${p}"`,p,`one of: ${[...r].join(", ")}`,`Declare group "${p}" 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 u(`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`)}}c(pe,"providerBundleIssue");function ce(e){let o;try{o=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issue:u("bundle.json is not valid JSON","invalid JSON",'{"bundleVersion":1}',"Write valid bundle metadata.","bundle.json")}}if(typeof o!="object"||o===null||Array.isArray(o))return{issue:u("bundle.json must be a JSON object",JSON.stringify(o),"an object with bundleVersion set to 1 and optionally kinds",'Set bundle.json to {"bundleVersion":1}.',"bundle.json")};const n=o,r=Object.keys(n).filter(d=>!ne.has(d));if(n.bundleVersion!==1||r.length>0)return{issue:u("bundle.json must contain bundleVersion 1 and at most version, transport, provider, kinds, page_components, memory_extensions, object_kinds, scopes, and groups",JSON.stringify(o),"an object with bundleVersion set to 1 and optional version, transport, provider, kinds, page_components, memory_extensions, object_kinds, scopes, and groups",'Set bundle.json to {"bundleVersion":1} plus optional contribution blocks.',"bundle.json")};if(n.kinds!==void 0){const d=F(n.kinds);if(d.length>0)return{issue:u("bundle.json kinds block is invalid",d.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 d=K(n.page_components);if(d.length>0)return{issue:u("bundle.json page_components block is invalid",d.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")}}const s=Z(n);if(n.provider!==void 0&&n.scopes!==void 0&&s.push("scopes is for local plugins; a provider gets its scopes from directory consent"),s.length>0)return{issue:u("bundle.json object_kinds or scopes block is invalid",s.join("; "),"object_kinds keyed by kind name with description, events and optional read.command; scopes an array of crtr:objects:<verb>:<target> strings (local plugins only)","Fix the object_kinds and scopes blocks in bundle.json.","bundle.json")};let a;if(n.memory_extensions!==void 0)try{a=X(n.memory_extensions)}catch(d){return{issue:u("bundle.json memory_extensions block is invalid",d instanceof Error?d.message:String(d),"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:u("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 p;if(n.transport!==void 0){const d=L(n.transport);if(d.transport===void 0)return{issue:u("bundle.json transport is invalid",d.errors.join("; "),'{ kind: "http", endpoint, authEnv? } or { kind: "machine" }',"Fix transport in bundle.json.","bundle.json.transport")};p=d.transport}let t;if(n.provider!==void 0){const d=ue(n.provider);if(d.issue!==void 0)return{issue:d.issue};if(t=d.provider,n.version!==void 0&&n.version!==t.major)return{issue:u("bundle.json provider major must equal version",`version ${String(n.version)}, major ${t.major}`,"one major","Set provider.major and version to the same integer.","bundle.json.provider.major")}}return{...n.version!==void 0?{version:n.version}:{},...p!==void 0?{transport:p}:{},...t!==void 0?{provider:t}:{},...n.kinds!==void 0?{kinds:n.kinds}:{},...n.page_components!==void 0?{page_components:G(n.page_components)}:{},...a!==void 0?{memory_extensions:a}:{},...n.object_kinds!==void 0?{object_kinds:n.object_kinds}:{},...n.scopes!==void 0?{scopes:n.scopes}:{}}}c(ce,"parseBundleMetadata");function me(e,o,n,r=new Set){let s;try{s=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issues:[u("commands.json is not valid JSON","invalid JSON","a valid command-manifest JSON object","Regenerate commands.json.","commands.json")]}}const a=z(s,{transport:"http",reservedCoreNames:o,extensibleCoreBranches:r,...n!==void 0?{coreCommandPaths:n}:{}});return{...a.manifest!==void 0?{commands:a.manifest}:{},issues:a.issues}}c(me,"parseCommands");async function fe(e,o){const n=re(e);if(n!==void 0)return{issues:[n]};const r=await ae(e);if(r.issue!==void 0||r.members===void 0)return{issues:[r.issue]};const s=de(r.members);if(s!==void 0)return{issues:[s]};const a=r.members.find(l=>l.path==="bundle.json"&&l.type==="file"),p=r.members.find(l=>l.path==="commands.json"&&l.type==="file");if(a===void 0||p===void 0)return{issues:[u("bundle must contain exactly one bundle.json and one commands.json regular file",`bundle.json=${a!==void 0}, commands.json=${p!==void 0}`,"one regular bundle.json and one regular commands.json member","Add the required metadata members to the archive.")]};const t=r.members.find(l=>l.path==="hooks.json"&&l.type==="file"),d=r.members.filter(l=>l.type==="file"&&l.path.startsWith("hooks/"));if((t!==void 0||r.members.some(l=>l.path==="hooks"||l.path.startsWith("hooks/")))&&(t===void 0||!d.some(l=>l.path==="hooks/dispatch")))return{issues:[u("bundle hook payload must contain both hooks.json and hooks/dispatch regular files",`hooks.json=${t!==void 0}, hooks/dispatch=${d.some(l=>l.path==="hooks/dispatch")}`,"both hooks.json and hooks/dispatch regular files","Add both required hook members or remove the hook payload.")]};const i=ce(a.bytes);if(i.issue!==void 0)return{issues:[i.issue]};const b=me(p.bytes,o.reservedCoreNames,o.coreCommandPaths,o.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:[u("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 m=i.version??h;if(i.provider!==void 0&&m===void 0)return{issues:[u("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&&m!==i.provider.major)return{issues:[u("bundle provider major must equal the plugin version",`version ${m}, major ${i.provider.major}`,"one major","Emit the provider major as the plugin version.","bundle.json.provider.major")]};const v=pe(i,b.commands);return v!==void 0?{issues:[v]}:{bundle:{bundleVersion:1,...m!==void 0?{version:m}:{},...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}:{},...i.object_kinds!==void 0?{object_kinds:i.object_kinds}:{},...i.scopes!==void 0?{scopes:i.scopes}:{},commands:b.commands,commandsBytes:p.bytes,directories:r.members.filter(l=>l.type==="directory").map(l=>({path:l.path})),memory:r.members.filter(l=>l.type==="file"&&l.path.startsWith("memory/")).map(l=>({path:l.path,bytes:l.bytes})),...t!==void 0?{hooks:{manifest:t.bytes,files:d.map(l=>({path:l.path,bytes:l.bytes}))}}:{}},issues:[]}}c(fe,"validatePluginBundle");function he(e){return I("sha256").update(e).digest("hex").slice(0,12)}c(he,"bundleVersion");function Pe(e,o,n,r){return M(o).plugins[e]?.version!==r?!1:_(w(n,"commands.json"))}c(Pe,"isInstalledGeneration");function be(e,o,n,r){C(e,s=>{s.plugins[o]===void 0&&(s.plugins[o]={}),r.swapped&&(s.plugins[o].last_updated=E()),s.plugins[o].checked_at=E(),n===void 0?delete s.plugins[o].etag:s.plugins[o].etag=n})}c(be,"recordCheckedGeneration");function g(e,o){const n=B(e,o);if(n===e||!n.startsWith(e+V))throw x(`bundle member path escapes the staging directory: ${o}`);return n}c(g,"stagedPath");function ve(e,o,n){const r=c((s,a,p=420)=>q(s,a,{mode:p,durable:!0}),"write");T(g(e,".crouter-plugin/plugin.json"),o,{durable:!0});for(const s of n.directories)j(g(e,s.path));r(g(e,"commands.json"),n.commandsBytes);for(const s of n.memory)r(g(e,s.path),s.bytes);if(n.hooks!==void 0){r(g(e,"hooks.json"),n.hooks.manifest);for(const s of n.hooks.files)r(g(e,s.path),s.bytes);W(g(e,"hooks/dispatch"),493)}H(e)}c(ve,"writeStagedBundle");async function Je(e,o,n){const{scope:r,scopeRootPath:s,root:a}=n,p=await fe(o,n.validation);if(p.bundle===void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,p.issues);const t=p.bundle,d=n.accept?.(t);if(d!==void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,[u(d,e,"the bundle this install asked for","Serve the requested bundle.")]);const f=n.version??he(o),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.object_kinds!==void 0?{object_kinds:t.object_kinds}:{},...t.scopes!==void 0?{scopes:t.scopes}:{},...t.hooks!==void 0?{hooks:"hooks.json",hookExecutable:"hooks/dispatch"}:{},...t.provider!==void 0?{provider:t.provider}:{}},h=w(s,"tmp"),m=w(h,`${e}.${process.pid}`),v=`${m}.prev`;if(_(v))throw x(`cannot replace plugin "${e}": prior interrupted replacement remains at ${v}`);j(h),j($(a)),S(m);let l;try{j(m),ve(m,b,t),l=await n.validateStaged(m,b);let y=!1;_(a)&&(k(a,v),y=!0);try{k(m,a)}catch(A){throw y&&k(v,a),A}U($(a)),y&&S(v)}finally{S(m)}return R(r,s),D(r,y=>{y.plugins[e]={enabled:n.enable?!0:y.plugins[e]?.enabled??!0,version:f}}),be(r,e,n.etag,{swapped:!0}),{manifest:b,validated:t,version:f,root:a,staged:l}}c(Je,"installBundleArchive");export{he as bundleVersion,Je as installBundleArchive,Pe as isInstalledGeneration,be as recordCheckedGeneration,fe as validatePluginBundle,ve as writeStagedBundle};
@@ -1,4 +1,4 @@
1
- var m=Object.defineProperty;var r=(t,n)=>m(t,"name",{value:n,configurable:!0});import{pageComponentCatalog as b}from"./page-catalog.js";function p(t){return new Map(b(t).filter(n=>!n.builtin).map(n=>[n.kind,n.tag]))}r(p,"productTags");const c='Component contract\n- Props are the component\'s config, spread flat as JSX attributes; the component registers itself with the host at mount and enforces its own response shape.\n- `id` is required, must match `^[A-Za-z0-9_-]{1,64}$`, and must be unique across the page.\n- When a page has two or more response-bearing components, every one needs a nonempty `label`.\n- Config objects and every documented nested object reject unknown fields.\n- A quoted JSX attribute does not interpret escape sequences, so `body="one.\\n\\ntwo."` reaches the reader as one run-on paragraph with a literal backslash-n in the middle of it. Write any markdown body that has a line or paragraph break as a braced string \u2014 `body={"one.\\n\\ntwo."}` \u2014 where the escape is real.\n- The page is rendered once at submit to build the ticket manifest, so every prop reaches it whatever its shape \u2014 mapped data, a helper component, a value from `useState`. Event handlers are dropped, because a manifest is JSON. The user\'s answer is stored in the request\'s terminal result under this `id`.',h='Comments are `{id:string (nonempty), text:string, anchor:Anchor}` and qualify the answer itself. `Anchor` is `{kind:"option",optionId:string}` or `{kind:"row",rowId:string}`; each component accepts only the anchors named next. The reader writes one against a specific option or row in whatever host they read in \u2014 in the terminal inbox, `c` on the selected one \u2014 so every option and row needs a label that reads on its own. Feedback on the page\'s own prose is a separate channel, offered only by hosts that render the page, and never part of a response.',s="Display-only: it is not recorded in the page manifest and contributes nothing to the response. Every display component also takes `className` for Tailwind utilities, and standard shadcn props otherwise.",i=[{names:["Page"],group:"structural",selectionLine:"`Page` \u2014 the page root. Every module default-exports a component whose root element is `<Page>`; it carries title and subtitle.",contract:`\`<Page>\`
1
+ var g=Object.defineProperty;var r=(t,n)=>g(t,"name",{value:n,configurable:!0});import{pageComponentCatalog as b}from"./page-catalog.js";function p(t){return new Map(b(t).filter(n=>!n.builtin).map(n=>[n.kind,n.tag]))}r(p,"productTags");const c='Component contract\n- Props are the component\'s config, spread flat as JSX attributes; the component registers itself with the host at mount and enforces its own response shape.\n- `id` is required, must match `^[A-Za-z0-9_-]{1,64}$`, and must be unique across the page.\n- When a page has two or more response-bearing components, every one needs a nonempty `label`.\n- Config objects and every documented nested object reject unknown fields.\n- A quoted JSX attribute does not interpret escape sequences, so `body="one.\\n\\ntwo."` reaches the reader as one run-on paragraph with a literal backslash-n in the middle of it. Write any markdown body that has a line or paragraph break as a braced string \u2014 `body={"one.\\n\\ntwo."}` \u2014 where the escape is real.\n- The page is rendered once at submit to build the ticket manifest, so every prop reaches it whatever its shape \u2014 mapped data, a helper component, a value from `useState`. Event handlers are dropped, because a manifest is JSON. The user\'s answer is stored in the request\'s terminal result under this `id`.',h='Comments are `{id:string (nonempty), text:string, anchor:Anchor}` and qualify the answer itself. `Anchor` is `{kind:"option",optionId:string}` or `{kind:"row",rowId:string}`; each component accepts only the anchors named next. The reader writes one against a specific option or row in whatever host they read in \u2014 in the terminal inbox, `c` on the selected one \u2014 so every option and row needs a label that reads on its own. Feedback on the page\'s own prose is a separate channel, offered only by hosts that render the page, and never part of a response.',s="Display-only: it is not recorded in the page manifest and contributes nothing to the response. Every display component also takes `className` for Tailwind utilities, and standard shadcn props otherwise.",i=[{names:["Page"],group:"structural",selectionLine:"`Page` \u2014 the page root. Every module default-exports a component whose root element is `<Page>`; it carries title and subtitle.",contract:`\`<Page>\`
2
2
 
3
3
  Structural root. The \`.tsx\` module's default export returns exactly one \`<Page>\`; it takes no \`id\` and contributes no response.
4
4
 
@@ -29,7 +29,7 @@ ${c}
29
29
  Props
30
30
  - \`id\` \u2014 required component id.
31
31
  - \`options\` \u2014 required nonempty array of \`{id:string (nonempty, unique), label:string (nonempty), description?:string, recommended?:boolean}\`.
32
- - \`recommended\` on one option marks it the suggested answer. At most one option per question may set it. The inbox shows it on the row, and when this question is the page's only response-bearing component the user can accept it from the list with one keystroke, without opening the page. Recommend when you have a view worth acting on; leave it off when the choice is genuinely theirs.
32
+ - \`recommended\` on one option marks it the suggested answer. A \`single\` question may recommend at most one option; a \`multi\` question may recommend several, and accepting from the list ticks them all. The inbox shows it on the row, and when this question is the page's only response-bearing component the user can accept it from the list with one keystroke, without opening the page. Recommend when you have a view worth acting on; leave it off when the choice is genuinely theirs.
33
33
  - \`mode\` \u2014 required: \`single\` or \`multi\`.
34
34
  - \`required?:boolean\` \u2014 whether an empty answer is refused. Defaults to the mode: a \`single\` question must be answered, a \`multi\` one need not be, because leaving every box unticked says "none of these apply". Set \`required\` when the mode's default is wrong \u2014 \`required={false}\` on a \`single\` question the user may skip, \`required\` on a \`multi\` one where at least one tick is the point.
35
35
  - \`label\` \u2014 required nonempty string: the question itself, drawn as the bold first line.
@@ -242,7 +242,7 @@ Example
242
242
  <AccordionContent>The audit job has not run since Tuesday.</AccordionContent>
243
243
  </AccordionItem>
244
244
  </Accordion>
245
- \`\`\``}],w=new Map(i.flatMap(t=>t.names.map(n=>[n,t]))),x=[...i.filter(t=>t.group!=="display").map(t=>t.selectionLine),"`usePageHost` \u2014 reserved host hook. Its product-supplied members are opaque to crouter; consult the owning product documentation."];function f(t){return w.get(t)}r(f,"componentDoc");function k(t){return[...i.flatMap(n=>[...n.names]),...p(t).values()]}r(k,"componentNames");const y={structural:"Structural \u2014 every page is built from these.",slot:"Response \u2014 each is recorded in the page manifest under its wire kind, and their answers are the page response.",display:"Display \u2014 shadcn components with their standard props, plus `className` for Tailwind. They contribute nothing to the response."};function v(t){const a=[`<components count="${i.reduce((e,o)=>e+o.names.length,0)+t.length}">`];for(const e of["structural","slot","display"])a.push(y[e],...i.filter(o=>o.group===e).map(o=>o.selectionLine),"");const l=p(t);t.length>0?a.push("Product-registered \u2014 registry components this product ships. Write each one under the tag shown; a `(kind \u2026)` suffix names its lowercase-hyphen wire kind."):a.pop();for(const e of t){const o=l.get(e.kind)??e.kind,u=o===e.kind?`\`${o}\``:`\`${o}\` (kind \`${e.kind}\`)`;if(e.description===void 0&&e.useWhen===void 0){a.push(`${u} \u2014 ${e.display===!0?"product-registered; display-only":"product-registered"}`);continue}const d=[e.description,e.useWhen===void 0?void 0:`${e.useWhen[0].toUpperCase()}${e.useWhen.slice(1)}`,...e.display===!0?["Display-only"]:[]].filter(g=>g!==void 0);a.push(`${u} \u2014 ${d.join(". ")}`)}return a.push("</components>"),a.join(`
246
- `)}r(v,"renderComponentCatalog");function A(t,n){const a=f(t);if(a!==void 0)return a.contract;const l=p(n),e=n.find(d=>d.kind===t||l.get(d.kind)===t);return e===void 0?void 0:`${`\`<${l.get(e.kind)??e.kind}>\` \u2014 product-registered, wire kind \`${e.kind}\``}
245
+ \`\`\``}],f=new Map(i.flatMap(t=>t.names.map(n=>[n,t]))),x=[...i.filter(t=>t.group!=="display").map(t=>t.selectionLine),"`usePageHost` \u2014 reserved host hook. Its product-supplied members are opaque to crouter; consult the owning product documentation."];function w(t){return f.get(t)}r(w,"componentDoc");function k(t){return[...i.flatMap(n=>[...n.names]),...p(t).values()]}r(k,"componentNames");const y={structural:"Structural \u2014 every page is built from these.",slot:"Response \u2014 each is recorded in the page manifest under its wire kind, and their answers are the page response.",display:"Display \u2014 shadcn components with their standard props, plus `className` for Tailwind. They contribute nothing to the response."};function v(t){const a=[`<components count="${i.reduce((e,o)=>e+o.names.length,0)+t.length}">`];for(const e of["structural","slot","display"])a.push(y[e],...i.filter(o=>o.group===e).map(o=>o.selectionLine),"");const l=p(t);t.length>0?a.push("Product-registered \u2014 registry components this product ships. Write each one under the tag shown; a `(kind \u2026)` suffix names its lowercase-hyphen wire kind."):a.pop();for(const e of t){const o=l.get(e.kind)??e.kind,u=o===e.kind?`\`${o}\``:`\`${o}\` (kind \`${e.kind}\`)`;if(e.description===void 0&&e.useWhen===void 0){a.push(`${u} \u2014 ${e.display===!0?"product-registered; display-only":"product-registered"}`);continue}const d=[e.description,e.useWhen===void 0?void 0:`${e.useWhen[0].toUpperCase()}${e.useWhen.slice(1)}`,...e.display===!0?["Display-only"]:[]].filter(m=>m!==void 0);a.push(`${u} \u2014 ${d.join(". ")}`)}return a.push("</components>"),a.join(`
246
+ `)}r(v,"renderComponentCatalog");function A(t,n){const a=w(t);if(a!==void 0)return a.contract;const l=p(n),e=n.find(d=>d.kind===t||l.get(d.kind)===t);return e===void 0?void 0:`${`\`<${l.get(e.kind)??e.kind}>\` \u2014 product-registered, wire kind \`${e.kind}\``}
247
247
 
248
- ${e.doc??`product-registered${e.display===!0?"; display-only":"; config is passed through unvalidated (unvalidated: true)"} \u2014 see the product's own docs`}`}r(A,"renderComponentContract");export{i as BUILTIN_COMPONENT_DOCS,x as CORE_COMPONENT_SELECTION_LINES,f as componentDoc,k as componentNames,v as renderComponentCatalog,A as renderComponentContract};
248
+ ${e.doc??`product-registered${e.display===!0?"; display-only":"; config is passed through unvalidated (unvalidated: true)"} \u2014 see the product's own docs`}`}r(A,"renderComponentContract");export{i as BUILTIN_COMPONENT_DOCS,x as CORE_COMPONENT_SELECTION_LINES,w as componentDoc,k as componentNames,v as renderComponentCatalog,A as renderComponentContract};
@@ -274,6 +274,8 @@ export declare function pageOverviewActions(manifest: PageManifest): PageOvervie
274
274
  export declare function pageAwaitsResponse(manifest: PageManifest): boolean;
275
275
  /** The canonical response-bearing predicate used by every page consumer. */
276
276
  export declare function isResponseBearingSlot(slot: PageSlot): boolean;
277
+ /** SQL form of `!pageAwaitsResponse(manifest)` over a `specification_json` column; keep it beside `isResponseBearingSlot`, which it mirrors. */
278
+ export declare const pageIsNoticeSql: (column: string) => string;
277
279
  /** Validate a persisted manifest. Publish-time validation made its slots authoritative, so reads do not resolve a component catalog. */
278
280
  export declare function validatePageManifest(parsed: unknown): PageManifest;
279
281
  /** Validate a manifest derived from an authored page, where the display flag comes from the registration rather than the source. */
@@ -1 +1,4 @@
1
- var q=Object.defineProperty;var c=(t,o)=>q(t,"name",{value:o,configurable:!0});import{z as e}from"zod";import{BUILTIN_PAGE_KINDS as P,productPageComponent as A}from"./page-catalog.js";const O=/^[A-Za-z0-9_-]{1,64}$/,_=e.string().regex(O,{error:"slot id must match /^[A-Za-z0-9_-]{1,64}$/"}),p=e.string().min(1),k=e.enum(["none","single","multi"]),w=e.record(e.string(),e.unknown());function m(t,o,s){const i=new Set;t.forEach((r,d)=>{i.has(r.id)&&o.addIssue({code:"custom",path:[d,"id"],message:`duplicate ${s} id "${r.id}"`}),i.add(r.id)})}c(m,"uniqueIds");const M=e.discriminatedUnion("kind",[e.object({kind:e.literal("option"),optionId:p}).strict(),e.object({kind:e.literal("row"),rowId:p}).strict()]),x=e.object({id:e.string().min(1),anchor:M,text:e.string()}).strict(),N=e.object({id:p,label:e.string().min(1),description:e.string().optional(),recommended:e.boolean().optional()}).strict(),T=e.object({label:e.string().optional(),body:e.string().optional(),options:e.array(N).min(1).superRefine((t,o)=>m(t,o,"option")),mode:e.enum(["single","multi"]),required:e.boolean().optional(),allowFreetext:e.boolean().optional(),freetextLabel:e.string().optional(),freetextPlaceholder:e.string().optional()}).strict().superRefine((t,o)=>{const s=t.options.filter(i=>i.recommended===!0);s.length>1&&o.addIssue({code:"custom",path:["options"],message:`at most one option may be recommended, but ${s.length} are`})}),I=e.object({selectedOptionIds:e.array(p),comments:e.array(x),freetext:e.string().optional()}).strict(),F=e.object({label:e.string().optional(),initialText:e.string(),placeholder:e.string().optional(),singleLine:e.boolean().optional(),required:e.boolean().optional()}).strict(),S=e.object({text:e.string(),edited:e.boolean()}).strict(),L=e.object({id:p,label:e.string().min(1),align:e.enum(["left","right"]).optional(),mono:e.boolean().optional()}).strict(),B=e.union([e.string(),e.number(),e.boolean(),e.null()]),G=e.object({id:p,cells:e.record(e.string(),B)}).strict(),U=e.array(G).superRefine((t,o)=>m(t,o,"row")),z={label:e.string().optional(),body:e.string().optional(),columns:e.array(L).min(1).superRefine((t,o)=>m(t,o,"column")),rows:U.optional(),rowSelect:k.default("none"),columnSelect:k.default("none"),required:e.boolean().optional()},D=e.object(z).strict(),y=e.object({selectedRowIds:e.array(p),selectedColumnIds:e.array(p),comments:e.array(x)}).strict(),H=e.object({id:p,title:e.string().min(1),subtitle:e.string().optional(),body:e.string().optional(),tag:e.string().optional()}).strict(),Z=e.array(H).superRefine((t,o)=>m(t,o,"card")),K={label:e.string().optional(),body:e.string().optional(),cards:Z.optional(),mode:e.enum(["single","multi"]),columns:e.union([e.literal(2),e.literal(3)]).optional(),required:e.boolean().optional()},J=e.object(K).strict(),E=e.object({selectedCardIds:e.array(p)}).strict(),Q=e.object({field:e.string().min(1),label:e.string().optional()}).strict(),V=e.array(e.record(e.string(),e.unknown())),W={kind:e.enum(["line","bar","area"]),x:e.string().min(1),y:e.array(Q).min(1),title:e.string().optional(),data:V.optional()},X=e.object(W).strict(),Y=e.object({sessionName:e.string().optional(),askedBy:e.string().optional(),emittedAt:e.string().datetime({offset:!0}).optional(),profileName:e.string().optional(),nodeId:e.string().optional()}).strict(),ee=e.object({placement:e.enum(["inline","panel"]),inbox:e.boolean(),reply:e.boolean()}).strict(),te=e.object({id:_.optional(),kind:e.string().min(1),step:e.number().int().nonnegative(),config:w,unvalidated:e.literal(!0).optional(),display:e.literal(!0).optional()}).strict(),ne=e.object({schema:e.literal("crtr.page/v2"),dialect:e.enum(["jsx","html"]),title:e.string().trim().min(1),subtitle:e.string().optional(),delivery:ee,document:e.enum(["page.tsx","page.html"]),steps:e.number().int().positive(),slots:e.array(te),source:Y.optional()}).strict().superRefine((t,o)=>{t.dialect==="jsx"!=(t.document==="page.tsx")&&o.addIssue({code:"custom",path:["document"],message:"document must agree with dialect"}),t.dialect==="html"&&(t.steps!==1||t.slots.length!==0)&&o.addIssue({code:"custom",path:["slots"],message:"HTML pages have exactly one step and no slots"})}),oe={options:T,text:F,table:D,cards:J,chart:X},le={options:I,text:S,table:y,cards:E};function f(t){return t.issues.map(o=>`${o.path.length===0?"value":o.path.join(".")}: ${o.message}`).join("; ")}c(f,"issueText");function pe(t){const o=[];for(const d of t.slots){if(d.kind!=="options"||d.id===void 0)continue;const n=d.config.options.find(a=>a.recommended===!0);n!==void 0&&o.push({slotId:d.id,optionId:n.id,label:n.label})}if(t.delivery.placement!=="panel")return{recommendedOptions:o};const s=t.slots.filter(g);if(s.length===0)return{recommendedOptions:o,fastAction:{kind:"acknowledge",responses:{}}};if(s.length>1)return{recommendedOptions:o};const i=s[0],r=o.find(d=>d.slotId===i.id);return i.kind!=="options"||r===void 0?{recommendedOptions:o}:{recommendedOptions:o,fastAction:{kind:"answer",responses:{[i.id]:{selectedOptionIds:[r.optionId],comments:[]}}}}}c(pe,"pageOverviewActions");function ue(t){return t.delivery.reply||t.slots.some(g)}c(ue,"pageAwaitsResponse");function g(t){if(t.display===!0)return!1;if(t.unvalidated===!0||t.kind==="options"||t.kind==="text"||t.kind==="cards")return!0;if(t.kind!=="table")return!1;const o=t.config;return(o.rowSelect??"none")!=="none"||(o.columnSelect??"none")!=="none"}c(g,"isResponseBearingSlot");function v(t,o,s){const i=ne.parse(t);if(!s&&i.dialect==="html")return i;const r=new Set,d=i.slots.map((n,a)=>{if(n.step>=i.steps)throw new Error(`slot ${n.id===void 0?`#${a}`:`"${n.id}"`} has step ${n.step}, outside page steps ${i.steps}`);let l;if(P.includes(n.kind)){if(n.unvalidated===!0)throw new Error(`built-in slot kind "${n.kind}" cannot be unvalidated`);if(n.display===!0)throw new Error(`built-in slot kind "${n.kind}" cannot set display: true`);const u=oe[n.kind].safeParse(n.config);if(!u.success)throw new Error(`invalid config for slot ${n.id===void 0?`#${a}`:`"${n.id}"`} (${n.kind}): ${f(u.error)}`);if(s&&n.kind==="options"){const b=u.data,$=n.id===void 0?`#${a}`:`"${n.id}"`;if((b.label??"").trim()==="")throw new Error(`slot ${$} (options): label is required \u2014 the question itself, drawn as the bold first line`);if((b.body??"").trim()==="")throw new Error(`slot ${$} (options): body is required \u2014 the context under the question a label cannot carry`)}l={...n.id===void 0?{}:{id:n.id},kind:n.kind,step:n.step,config:u.data}}else{const u=o===void 0?void 0:A(n.kind,o);if(s&&u===void 0)throw new Error(`unregistered page slot kind "${n.kind}" at slot #${a}`);if(n.unvalidated!==!0)throw new Error(`registered page slot kind "${n.kind}" must set unvalidated: true`);if(!s)l={...n.id===void 0?{}:{id:n.id},kind:n.kind,step:n.step,config:n.config,unvalidated:!0,...n.display===!0?{display:!0}:{}};else{if(n.display===!0&&u?.display!==!0)throw new Error(`non-display page slot kind "${n.kind}" cannot set display: true`);l={...n.id===void 0?{}:{id:n.id},kind:n.kind,step:n.step,config:n.config,unvalidated:!0,...u?.display===!0?{display:!0}:{}}}}if(g(l)&&l.id===void 0)throw new Error(l.unvalidated===!0?`product slot kind "${l.kind}" at slot #${a} requires an id prop`:`response-bearing slot kind "${l.kind}" at slot #${a} requires an id prop`);if(l.id!==void 0){if(r.has(l.id))throw new Error(`duplicate slot id "${l.id}"`);r.add(l.id)}return l});return{...i,slots:d}}c(v,"validateManifest");function j(t){return v(t,void 0,!1)}c(j,"validatePageManifest");function fe(t,o=[]){return v(t,o,!0)}c(fe,"validateAuthoredPageManifest");function ie(){return e.custom(t=>{try{return j(t),!0}catch{return!1}},{error:"invalid page manifest"}).transform(t=>j(t))}c(ie,"createPageManifestSchema");const me=ie();function h(t,o,s){if(new Set(t).size!==t.length)throw new Error(`slot "${o}" has duplicate id in ${s}`)}c(h,"assertUnique");function C(t,o){const s=t.config;for(const i of o){const r=i.anchor;if(t.kind==="options"&&r.kind==="option"){if(!s.options.some(d=>d.id===r.optionId))throw new Error(`slot "${t.id}" comment names unknown option id "${r.optionId}"`);continue}if(t.kind==="table"&&r.kind==="row"){if(!(s.rows??[]).some(d=>d.id===r.rowId))throw new Error(`slot "${t.id}" comment names unknown row id "${r.rowId}"`);continue}throw new Error(`slot "${t.id}" does not allow comment anchor kind "${r.kind}"`)}}c(C,"validateComments");function re(t){return t.required??t.mode==="single"}c(re,"optionsRequired");function se(t,o){if(t.unvalidated===!0){const s=w.safeParse(o);if(!s.success)throw new Error(`response for unvalidated slot "${t.id}" must be an object`);return s.data}if(t.kind==="options"){const s=I.safeParse(o);if(!s.success)throw new Error(`invalid response for slot "${t.id}": ${f(s.error)}`);const i=s.data,r=t.config;h(i.selectedOptionIds,t.id,"selectedOptionIds");for(const n of i.selectedOptionIds)if(!r.options.some(a=>a.id===n))throw new Error(`slot "${t.id}" selects unknown option id "${n}"`);if(i.freetext!==void 0&&r.allowFreetext!==!0)throw new Error(`slot "${t.id}" does not allow freetext`);if(r.mode==="single"&&i.selectedOptionIds.length>1)throw new Error(`slot "${t.id}" is single-select but received ${i.selectedOptionIds.length} option ids`);const d=r.allowFreetext===!0&&(i.freetext?.trim()??"")!=="";if(re(r)){if(r.mode==="single"&&i.selectedOptionIds.length!==1&&!d)throw new Error(`single-select slot "${t.id}" requires exactly one selection or nonempty allowed freetext`);if(r.mode==="multi"&&i.selectedOptionIds.length===0&&i.comments.length===0&&!d)throw new Error(`multi-select slot "${t.id}" requires a selection, comment, or nonempty allowed freetext`)}return C(t,i.comments),i}if(t.kind==="text"){const s=S.safeParse(o);if(!s.success)throw new Error(`invalid response for slot "${t.id}": ${f(s.error)}`);if(t.config.required===!0&&s.data.text.trim()==="")throw new Error(`slot "${t.id}" is required and was left empty`);return s.data}if(t.kind==="table"){const s=y.safeParse(o);if(!s.success)throw new Error(`invalid response for slot "${t.id}": ${f(s.error)}`);const i=s.data,r=t.config;h(i.selectedRowIds,t.id,"selectedRowIds"),h(i.selectedColumnIds,t.id,"selectedColumnIds");const d=new Set((r.rows??[]).map(a=>a.id)),n=new Set(r.columns.map(a=>a.id));for(const a of i.selectedRowIds)if(!d.has(a))throw new Error(`slot "${t.id}" selects unknown row id "${a}"`);for(const a of i.selectedColumnIds)if(!n.has(a))throw new Error(`slot "${t.id}" selects unknown column id "${a}"`);if(r.required===!0&&i.selectedRowIds.length===0&&i.selectedColumnIds.length===0)throw new Error(`slot "${t.id}" is required and nothing is selected`);if(r.rowSelect==="none"&&i.selectedRowIds.length>0||r.rowSelect==="single"&&i.selectedRowIds.length>1)throw new Error(`slot "${t.id}" response violates row select mode "${r.rowSelect}"`);if(r.columnSelect==="none"&&i.selectedColumnIds.length>0||r.columnSelect==="single"&&i.selectedColumnIds.length>1)throw new Error(`slot "${t.id}" response violates column select mode "${r.columnSelect}"`);return C(t,i.comments),i}if(t.kind==="cards"){const s=E.safeParse(o);if(!s.success)throw new Error(`invalid response for slot "${t.id}": ${f(s.error)}`);const i=s.data,r=t.config;h(i.selectedCardIds,t.id,"selectedCardIds");for(const d of i.selectedCardIds)if(!(r.cards??[]).some(n=>n.id===d))throw new Error(`slot "${t.id}" selects unknown card id "${d}"`);if(r.mode==="single"&&i.selectedCardIds.length>1)throw new Error(`slot "${t.id}" is single-select but received ${i.selectedCardIds.length} card ids`);if(r.required===!0&&i.selectedCardIds.length===0)throw new Error(`slot "${t.id}" is required and nothing is selected`);return i}throw new Error(`slot "${t.id}" kind "${t.kind}" does not accept a response`)}c(se,"parseResponse");function R(t,o,s){const i=w.safeParse(o);if(!i.success)throw new Error("page responses must be an object keyed by slot id");const r=new Map(t.slots.filter(g).map(n=>[n.id,n])),d={};for(const[n,a]of Object.entries(i.data)){const l=r.get(n);if(l===void 0)throw new Error(`response names unknown or non-response-bearing slot id "${n}"`);d[n]=se(l,a)}if(!s){for(const n of r.keys())if(!(n in d))throw new Error(`final page response is missing response-bearing slot id "${n}"`)}return d}c(R,"validateResponses");function ge(t,o){return R(t,o,!1)}c(ge,"validatePageResponses");function he(t,o){return R(t,o,!0)}c(he,"validatePartialPageResponses");export{oe as BUILTIN_PAGE_CONFIG_SCHEMAS,le as BUILTIN_PAGE_RESPONSE_SCHEMAS,O as PAGE_SLOT_ID_PATTERN,H as cardSchema,J as cardsConfigSchema,E as cardsResponseSchema,Z as cardsSchema,X as chartConfigSchema,V as chartDataRowsSchema,Q as chartSeriesSchema,x as commentSchema,ie as createPageManifestSchema,g as isResponseBearingSlot,f as issueText,N as optionSchema,T as optionsConfigSchema,re as optionsRequired,I as optionsResponseSchema,ue as pageAwaitsResponse,M as pageCommentAnchorSchema,ee as pageDeliverySchema,me as pageManifestSchema,pe as pageOverviewActions,B as tableCellSchema,L as tableColumnSchema,D as tableConfigSchema,y as tableResponseSchema,G as tableRowSchema,U as tableRowsSchema,F as textConfigSchema,S as textResponseSchema,fe as validateAuthoredPageManifest,j as validatePageManifest,ge as validatePageResponses,he as validatePartialPageResponses};
1
+ var O=Object.defineProperty;var l=(t,i)=>O(t,"name",{value:i,configurable:!0});import{z as e}from"zod";import{BUILTIN_PAGE_KINDS as A,productPageComponent as q}from"./page-catalog.js";const P=/^[A-Za-z0-9_-]{1,64}$/,_=e.string().regex(P,{error:"slot id must match /^[A-Za-z0-9_-]{1,64}$/"}),p=e.string().min(1),x=e.enum(["none","single","multi"]),w=e.record(e.string(),e.unknown());function f(t,i,r){const o=new Set;t.forEach((s,d)=>{o.has(s.id)&&i.addIssue({code:"custom",path:[d,"id"],message:`duplicate ${r} id "${s.id}"`}),o.add(s.id)})}l(f,"uniqueIds");const N=e.discriminatedUnion("kind",[e.object({kind:e.literal("option"),optionId:p}).strict(),e.object({kind:e.literal("row"),rowId:p}).strict()]),k=e.object({id:e.string().min(1),anchor:N,text:e.string()}).strict(),L=e.object({id:p,label:e.string().min(1),description:e.string().optional(),recommended:e.boolean().optional()}).strict(),M=e.object({label:e.string().optional(),body:e.string().optional(),options:e.array(L).min(1).superRefine((t,i)=>f(t,i,"option")),mode:e.enum(["single","multi"]),required:e.boolean().optional(),allowFreetext:e.boolean().optional(),freetextLabel:e.string().optional(),freetextPlaceholder:e.string().optional()}).strict().superRefine((t,i)=>{const r=t.options.filter(o=>o.recommended===!0);t.mode==="single"&&r.length>1&&i.addIssue({code:"custom",path:["options"],message:`a mode="single" question may recommend at most one option, but ${r.length} are marked recommended (${r.map(o=>o.id).join(", ")}); keep one, or use mode="multi" to recommend several`})}),S=e.object({selectedOptionIds:e.array(p),comments:e.array(k),freetext:e.string().optional()}).strict(),T=e.object({label:e.string().optional(),initialText:e.string(),placeholder:e.string().optional(),singleLine:e.boolean().optional(),required:e.boolean().optional()}).strict(),E=e.object({text:e.string(),edited:e.boolean()}).strict(),F=e.object({id:p,label:e.string().min(1),align:e.enum(["left","right"]).optional(),mono:e.boolean().optional()}).strict(),D=e.union([e.string(),e.number(),e.boolean(),e.null()]),B=e.object({id:p,cells:e.record(e.string(),D)}).strict(),G=e.array(B).superRefine((t,i)=>f(t,i,"row")),U={label:e.string().optional(),body:e.string().optional(),columns:e.array(F).min(1).superRefine((t,i)=>f(t,i,"column")),rows:G.optional(),rowSelect:x.default("none"),columnSelect:x.default("none"),required:e.boolean().optional()},z=e.object(U).strict(),I=e.object({selectedRowIds:e.array(p),selectedColumnIds:e.array(p),comments:e.array(k)}).strict(),H=e.object({id:p,title:e.string().min(1),subtitle:e.string().optional(),body:e.string().optional(),tag:e.string().optional()}).strict(),Z=e.array(H).superRefine((t,i)=>f(t,i,"card")),K={label:e.string().optional(),body:e.string().optional(),cards:Z.optional(),mode:e.enum(["single","multi"]),columns:e.union([e.literal(2),e.literal(3)]).optional(),required:e.boolean().optional()},W=e.object(K).strict(),y=e.object({selectedCardIds:e.array(p)}).strict(),X=e.object({field:e.string().min(1),label:e.string().optional()}).strict(),J=e.array(e.record(e.string(),e.unknown())),Q={kind:e.enum(["line","bar","area"]),x:e.string().min(1),y:e.array(X).min(1),title:e.string().optional(),data:J.optional()},V=e.object(Q).strict(),Y=e.object({sessionName:e.string().optional(),askedBy:e.string().optional(),emittedAt:e.string().datetime({offset:!0}).optional(),profileName:e.string().optional(),nodeId:e.string().optional()}).strict(),ee=e.object({placement:e.enum(["inline","panel"]),inbox:e.boolean(),reply:e.boolean()}).strict(),te=e.object({id:_.optional(),kind:e.string().min(1),step:e.number().int().nonnegative(),config:w,unvalidated:e.literal(!0).optional(),display:e.literal(!0).optional()}).strict(),ne=e.object({schema:e.literal("crtr.page/v2"),dialect:e.enum(["jsx","html"]),title:e.string().trim().min(1),subtitle:e.string().optional(),delivery:ee,document:e.enum(["page.tsx","page.html"]),steps:e.number().int().positive(),slots:e.array(te),source:Y.optional()}).strict().superRefine((t,i)=>{t.dialect==="jsx"!=(t.document==="page.tsx")&&i.addIssue({code:"custom",path:["document"],message:"document must agree with dialect"}),t.dialect==="html"&&(t.steps!==1||t.slots.length!==0)&&i.addIssue({code:"custom",path:["slots"],message:"HTML pages have exactly one step and no slots"})}),oe={options:M,text:T,table:z,cards:W,chart:V},le={options:S,text:E,table:I,cards:y};function m(t){return t.issues.map(i=>`${i.path.length===0?"value":i.path.join(".")}: ${i.message}`).join("; ")}l(m,"issueText");function pe(t){const i=[];for(const d of t.slots){if(d.kind!=="options"||d.id===void 0)continue;const n=d.config,a=n.options.filter(c=>c.recommended===!0);for(const c of n.mode==="multi"?a:a.slice(0,1))i.push({slotId:d.id,optionId:c.id,label:c.label})}if(t.delivery.placement!=="panel")return{recommendedOptions:i};const r=t.slots.filter(g);if(r.length===0)return{recommendedOptions:i,fastAction:{kind:"acknowledge",responses:{}}};if(r.length>1)return{recommendedOptions:i};const o=r[0],s=i.filter(d=>d.slotId===o.id);return o.kind!=="options"||s.length===0?{recommendedOptions:i}:{recommendedOptions:i,fastAction:{kind:"answer",responses:{[o.id]:{selectedOptionIds:s.map(d=>d.optionId),comments:[]}}}}}l(pe,"pageOverviewActions");function ue(t){return t.delivery.reply||t.slots.some(g)}l(ue,"pageAwaitsResponse");function g(t){if(t.display===!0)return!1;if(t.unvalidated===!0||t.kind==="options"||t.kind==="text"||t.kind==="cards")return!0;if(t.kind!=="table")return!1;const i=t.config;return(i.rowSelect??"none")!=="none"||(i.columnSelect??"none")!=="none"}l(g,"isResponseBearingSlot");const me=l(t=>`(json_valid(${t}) AND COALESCE(json_extract(${t}, '$.delivery.reply'), 0) = 0
2
+ AND NOT EXISTS (SELECT 1 FROM json_each(${t}, '$.slots') s WHERE COALESCE(json_extract(s.value, '$.display'), 0) <> 1
3
+ AND (COALESCE(json_extract(s.value, '$.unvalidated'), 0) = 1 OR json_extract(s.value, '$.kind') IN ('options', 'text', 'cards')
4
+ OR (json_extract(s.value, '$.kind') = 'table' AND (COALESCE(json_extract(s.value, '$.config.rowSelect'), 'none') <> 'none' OR COALESCE(json_extract(s.value, '$.config.columnSelect'), 'none') <> 'none')))))`,"pageIsNoticeSql");function v(t,i,r){const o=ne.parse(t);if(!r&&o.dialect==="html")return o;const s=new Set,d=o.slots.map((n,a)=>{if(n.step>=o.steps)throw new Error(`slot ${n.id===void 0?`#${a}`:`"${n.id}"`} has step ${n.step}, outside page steps ${o.steps}`);let c;if(A.includes(n.kind)){if(n.unvalidated===!0)throw new Error(`built-in slot kind "${n.kind}" cannot be unvalidated`);if(n.display===!0)throw new Error(`built-in slot kind "${n.kind}" cannot set display: true`);const u=oe[n.kind].safeParse(n.config);if(!u.success)throw new Error(`invalid config for slot ${n.id===void 0?`#${a}`:`"${n.id}"`} (${n.kind}): ${m(u.error)}`);if(r&&n.kind==="options"){const b=u.data,$=n.id===void 0?`#${a}`:`"${n.id}"`;if((b.label??"").trim()==="")throw new Error(`slot ${$} (options): label is required \u2014 the question itself, drawn as the bold first line`);if((b.body??"").trim()==="")throw new Error(`slot ${$} (options): body is required \u2014 the context under the question a label cannot carry`)}c={...n.id===void 0?{}:{id:n.id},kind:n.kind,step:n.step,config:u.data}}else{const u=i===void 0?void 0:q(n.kind,i);if(r&&u===void 0)throw new Error(`unregistered page slot kind "${n.kind}" at slot #${a}`);if(n.unvalidated!==!0)throw new Error(`registered page slot kind "${n.kind}" must set unvalidated: true`);if(!r)c={...n.id===void 0?{}:{id:n.id},kind:n.kind,step:n.step,config:n.config,unvalidated:!0,...n.display===!0?{display:!0}:{}};else{if(n.display===!0&&u?.display!==!0)throw new Error(`non-display page slot kind "${n.kind}" cannot set display: true`);c={...n.id===void 0?{}:{id:n.id},kind:n.kind,step:n.step,config:n.config,unvalidated:!0,...u?.display===!0?{display:!0}:{}}}}if(g(c)&&c.id===void 0)throw new Error(c.unvalidated===!0?`product slot kind "${c.kind}" at slot #${a} requires an id prop`:`response-bearing slot kind "${c.kind}" at slot #${a} requires an id prop`);if(c.id!==void 0){if(s.has(c.id))throw new Error(`duplicate slot id "${c.id}"`);s.add(c.id)}return c});return{...o,slots:d}}l(v,"validateManifest");function j(t){return v(t,void 0,!1)}l(j,"validatePageManifest");function fe(t,i=[]){return v(t,i,!0)}l(fe,"validateAuthoredPageManifest");function ie(){return e.custom(t=>{try{return j(t),!0}catch{return!1}},{error:"invalid page manifest"}).transform(t=>j(t))}l(ie,"createPageManifestSchema");const ge=ie();function h(t,i,r){if(new Set(t).size!==t.length)throw new Error(`slot "${i}" has duplicate id in ${r}`)}l(h,"assertUnique");function C(t,i){const r=t.config;for(const o of i){const s=o.anchor;if(t.kind==="options"&&s.kind==="option"){if(!r.options.some(d=>d.id===s.optionId))throw new Error(`slot "${t.id}" comment names unknown option id "${s.optionId}"`);continue}if(t.kind==="table"&&s.kind==="row"){if(!(r.rows??[]).some(d=>d.id===s.rowId))throw new Error(`slot "${t.id}" comment names unknown row id "${s.rowId}"`);continue}throw new Error(`slot "${t.id}" does not allow comment anchor kind "${s.kind}"`)}}l(C,"validateComments");function se(t){return t.required??t.mode==="single"}l(se,"optionsRequired");function re(t,i){if(t.unvalidated===!0){const r=w.safeParse(i);if(!r.success)throw new Error(`response for unvalidated slot "${t.id}" must be an object`);return r.data}if(t.kind==="options"){const r=S.safeParse(i);if(!r.success)throw new Error(`invalid response for slot "${t.id}": ${m(r.error)}`);const o=r.data,s=t.config;h(o.selectedOptionIds,t.id,"selectedOptionIds");for(const n of o.selectedOptionIds)if(!s.options.some(a=>a.id===n))throw new Error(`slot "${t.id}" selects unknown option id "${n}"`);if(o.freetext!==void 0&&s.allowFreetext!==!0)throw new Error(`slot "${t.id}" does not allow freetext`);if(s.mode==="single"&&o.selectedOptionIds.length>1)throw new Error(`slot "${t.id}" is single-select but received ${o.selectedOptionIds.length} option ids`);const d=s.allowFreetext===!0&&(o.freetext?.trim()??"")!=="";if(se(s)){if(s.mode==="single"&&o.selectedOptionIds.length!==1&&!d)throw new Error(`single-select slot "${t.id}" requires exactly one selection or nonempty allowed freetext`);if(s.mode==="multi"&&o.selectedOptionIds.length===0&&o.comments.length===0&&!d)throw new Error(`multi-select slot "${t.id}" requires a selection, comment, or nonempty allowed freetext`)}return C(t,o.comments),o}if(t.kind==="text"){const r=E.safeParse(i);if(!r.success)throw new Error(`invalid response for slot "${t.id}": ${m(r.error)}`);if(t.config.required===!0&&r.data.text.trim()==="")throw new Error(`slot "${t.id}" is required and was left empty`);return r.data}if(t.kind==="table"){const r=I.safeParse(i);if(!r.success)throw new Error(`invalid response for slot "${t.id}": ${m(r.error)}`);const o=r.data,s=t.config;h(o.selectedRowIds,t.id,"selectedRowIds"),h(o.selectedColumnIds,t.id,"selectedColumnIds");const d=new Set((s.rows??[]).map(a=>a.id)),n=new Set(s.columns.map(a=>a.id));for(const a of o.selectedRowIds)if(!d.has(a))throw new Error(`slot "${t.id}" selects unknown row id "${a}"`);for(const a of o.selectedColumnIds)if(!n.has(a))throw new Error(`slot "${t.id}" selects unknown column id "${a}"`);if(s.required===!0&&o.selectedRowIds.length===0&&o.selectedColumnIds.length===0)throw new Error(`slot "${t.id}" is required and nothing is selected`);if(s.rowSelect==="none"&&o.selectedRowIds.length>0||s.rowSelect==="single"&&o.selectedRowIds.length>1)throw new Error(`slot "${t.id}" response violates row select mode "${s.rowSelect}"`);if(s.columnSelect==="none"&&o.selectedColumnIds.length>0||s.columnSelect==="single"&&o.selectedColumnIds.length>1)throw new Error(`slot "${t.id}" response violates column select mode "${s.columnSelect}"`);return C(t,o.comments),o}if(t.kind==="cards"){const r=y.safeParse(i);if(!r.success)throw new Error(`invalid response for slot "${t.id}": ${m(r.error)}`);const o=r.data,s=t.config;h(o.selectedCardIds,t.id,"selectedCardIds");for(const d of o.selectedCardIds)if(!(s.cards??[]).some(n=>n.id===d))throw new Error(`slot "${t.id}" selects unknown card id "${d}"`);if(s.mode==="single"&&o.selectedCardIds.length>1)throw new Error(`slot "${t.id}" is single-select but received ${o.selectedCardIds.length} card ids`);if(s.required===!0&&o.selectedCardIds.length===0)throw new Error(`slot "${t.id}" is required and nothing is selected`);return o}throw new Error(`slot "${t.id}" kind "${t.kind}" does not accept a response`)}l(re,"parseResponse");function R(t,i,r){const o=w.safeParse(i);if(!o.success)throw new Error("page responses must be an object keyed by slot id");const s=new Map(t.slots.filter(g).map(n=>[n.id,n])),d={};for(const[n,a]of Object.entries(o.data)){const c=s.get(n);if(c===void 0)throw new Error(`response names unknown or non-response-bearing slot id "${n}"`);d[n]=re(c,a)}if(!r){for(const n of s.keys())if(!(n in d))throw new Error(`final page response is missing response-bearing slot id "${n}"`)}return d}l(R,"validateResponses");function he(t,i){return R(t,i,!1)}l(he,"validatePageResponses");function we(t,i){return R(t,i,!0)}l(we,"validatePartialPageResponses");export{oe as BUILTIN_PAGE_CONFIG_SCHEMAS,le as BUILTIN_PAGE_RESPONSE_SCHEMAS,P as PAGE_SLOT_ID_PATTERN,H as cardSchema,W as cardsConfigSchema,y as cardsResponseSchema,Z as cardsSchema,V as chartConfigSchema,J as chartDataRowsSchema,X as chartSeriesSchema,k as commentSchema,ie as createPageManifestSchema,g as isResponseBearingSlot,m as issueText,L as optionSchema,M as optionsConfigSchema,se as optionsRequired,S as optionsResponseSchema,ue as pageAwaitsResponse,N as pageCommentAnchorSchema,ee as pageDeliverySchema,me as pageIsNoticeSql,ge as pageManifestSchema,pe as pageOverviewActions,D as tableCellSchema,F as tableColumnSchema,z as tableConfigSchema,I as tableResponseSchema,B as tableRowSchema,G as tableRowsSchema,T as textConfigSchema,E as textResponseSchema,fe as validateAuthoredPageManifest,j as validatePageManifest,he as validatePageResponses,we as validatePartialPageResponses};
@@ -1 +1,6 @@
1
- var q=Object.defineProperty;var i=(t,e)=>q(t,"name",{value:e,configurable:!0});import{readFileSync as v}from"node:fs";import{createRequire as D}from"node:module";import{join as O}from"node:path";import{BUILTIN_KIND_BY_TAG as L,BUILTIN_PAGE_COMPONENT_TAGS as E,DISPLAY_PAGE_COMPONENT_TAGS as R,pageComponentTag as M}from"./page-catalog.js";import{isResponseBearingSlot as F,pageDeliverySchema as B,validateAuthoredPageManifest as G,validatePageManifest as J}from"./page-schema.js";import{evaluatePageTree as K,jsonProps as U}from"./page-eval.js";import{PageAuthoringError as s,PageCompilerError as $}from"./page-errors.js";const T=D(import.meta.url);let X,H;function k(){return X??=T("esbuild")}i(k,"esbuild");function A(){return H??=T("typescript")}i(A,"typescript");function Y(t){return t instanceof s?t:new s(t instanceof Error?t.message:String(t))}i(Y,"asPageAuthoringError");function j(t,e){try{const n=G(t,e),r=n.slots.filter(F);if(r.length>=2)for(const o of r){const a=o.config.label;if(typeof a!="string"||a==="")throw new s(`response-bearing slot "${o.id}" requires a nonempty config.label when the page has multiple response-bearing slots`)}return n}catch(n){throw Y(n)}}i(j,"validateAuthoredManifest");const z="a page is evaluated with React and every registry component already in scope, so it takes no imports \u2014 write `<Page>`, `<UserQuestion>`, `<Card>` and friends directly.";function Q(t){return k().transformSync(t,{loader:"tsx",format:"cjs",jsx:"transform",jsxFactory:"React.createElement",jsxFragment:"React.Fragment",target:"es2020",sourcefile:"page.tsx"}).code}i(Q,"transformPage");function V(t){let e;for(let n=0;n<2;n++)try{return Q(t)}catch(r){const o=r.errors?.[0];if(o!==void 0){const a=o.location==null?"":` (page.tsx:${o.location.line}:${o.location.column})`;throw new s(`page does not compile: ${o.text??"unknown error"}${a}`)}e=r,k().stop()}throw new $(`page compiler (esbuild) service failed and did not recover after a restart: ${e instanceof Error?e.message:String(e)}`)}i(V,"compilePage");function W(t){const e=A();return e.createSourceFile("page.tsx",t,e.ScriptTarget.ES2022,!0,e.ScriptKind.TSX)}i(W,"parseTsx");function I(t,e){e(t),t.forEachChild(n=>I(n,e))}i(I,"eachNode");function Z(t){const e=A();let n;if(I(t,r=>{n===void 0&&(e.isImportDeclaration(r)||e.isImportEqualsDeclaration(r)?n="import":e.isExportDeclaration(r)&&r.moduleSpecifier!==void 0?n="export \u2026 from":e.isCallExpression(r)&&(r.expression.kind===e.SyntaxKind.ImportKeyword?n="dynamic import()":e.isIdentifier(r.expression)&&r.expression.text==="require"&&(n="require()")))}),n!==void 0)throw new s(`page source uses ${n}: ${z}`)}i(Z,"rejectModulePlumbing");function N(t,e){const n=t[e];if(n!==void 0){if(typeof n!="string")throw new s(`<Page> prop ${e} must be a string; it is part of the page manifest`);return n}}i(N,"stringProp");function ee(t,e,n){Z(W(t));const r=[...E,...R,...n.map(l=>M(l.kind))],o=K(e,r),a=[],h=i(l=>{for(const c of l)c.kind!=="text"&&(c.tag==="Page"&&a.push(c),h(c.children))},"collectPages");if(h(o),a.length===0)throw new s('page default export must render exactly one <Page title="\u2026"> root; it rendered none');if(a.length>1)throw new s(`page default export renders ${a.length} <Page> elements; exactly one is allowed`);const f=a[0],w=(N(f.props,"title")??"").trim();if(w==="")throw new s("<Page> requires a nonempty title prop");const g=N(f.props,"subtitle")?.trim();if(f.props.placement!==void 0)throw new s("<Page> placement is not authored; choose --placement inline or --placement panel when sending the page.");const d=new Map;for(const l of n){const c=M(l.kind);if(E.has(c))throw new s(`product page component kind "${l.kind}" derives JSX tag "${c}", which collides with a built-in page component`);if(d.has(c))throw new s(`product page component kind "${l.kind}" derives JSX tag "${c}", which duplicates product page component kind "${d.get(c)}"`);d.set(c,l.kind)}const x=[];let y=0;const P=i((l,c)=>{for(const p of l){if(p.kind==="text")continue;const S=p.tag==="Step"?y++:c,m=L[p.tag],b=d.get(p.tag);if(m!==void 0||b!==void 0){const{id:u,...C}=U(p.props);if(u!==void 0&&typeof u!="string")throw new s(`<${p.tag}> id prop must be a string`);x.push({...u===void 0?{}:{id:u},kind:m??b,step:S,config:C,...m===void 0?{unvalidated:!0}:{}})}P(p.children,S)}},"collectSlots");return P(f.children,0),{title:w,...g===void 0||g===""?{}:{subtitle:g},steps:Math.max(y,1),slots:x}}i(ee,"scanPage");function te(t,e=[],n,r={placement:"inline",inbox:!1,reply:!1}){const o=ee(t,V(t),e);return j({schema:"crtr.page/v2",dialect:"jsx",title:o.title,...o.subtitle===void 0?{}:{subtitle:o.subtitle},delivery:r,document:"page.tsx",steps:o.steps,slots:o.slots,...n===void 0?{}:{source:n}},e)}i(te,"derivePageManifestFromDocument");function ne(t){const e=/<title\b[^>]*>([\s\S]*?)<\/title\s*>/i.exec(t)?.[1]?.replace(/<[^>]+>/g,"").trim();if(e===void 0||e==="")throw new s("HTML page requires a nonempty <title>.");const n=[...t.matchAll(/<meta\b[^>]*>/gi)].map(r=>r[0]).map(r=>{const o=new Map([...r.matchAll(/\b([^\s=/>]+)\s*=\s*(["'])([\s\S]*?)\2/g)].map(a=>[a[1].toLowerCase(),a[3]]));return o.get("name")?.toLowerCase()==="description"?o.get("content")?.trim():void 0}).find(r=>r!==void 0);return{title:e,...n===void 0||n===""?{}:{subtitle:n}}}i(ne,"htmlMetadata");function fe(t,e,n={placement:"inline",inbox:!1,reply:!1}){const r=ne(t);return j({schema:"crtr.page/v2",dialect:"html",...r,delivery:n,document:"page.html",steps:1,slots:[],...e===void 0?{}:{source:e}},[])}i(fe,"deriveHtmlPageManifest");function de(t,e=[],n,r){return te(v(t,"utf8"),e,n,r)}i(de,"derivePageManifest");function _(t){try{return JSON.parse(v(O(t,"page.json"),"utf8"))}catch{throw new s("page.json is not valid JSON")}}i(_,"readPageJson");function ue(t){return J(_(t))}i(ue,"parsePage");function ge(t){const e=_(t),n=B.safeParse(typeof e=="object"&&e!==null&&!Array.isArray(e)?e.delivery:void 0);if(!n.success)throw new s("page.json has an invalid delivery envelope");return n.data}i(ge,"parsePageDelivery");export{s as PageAuthoringError,$ as PageCompilerError,V as compilePage,fe as deriveHtmlPageManifest,de as derivePageManifest,te as derivePageManifestFromDocument,ue as parsePage,ge as parsePageDelivery};
1
+ var N=Object.defineProperty;var o=(n,t)=>N(n,"name",{value:t,configurable:!0});import{readFileSync as $}from"node:fs";import{createRequire as R}from"node:module";import{join as O}from"node:path";import{BUILTIN_KIND_BY_TAG as L,BUILTIN_PAGE_COMPONENT_TAGS as v,DISPLAY_PAGE_COMPONENT_TAGS as F,pageComponentTag as E}from"./page-catalog.js";import{isResponseBearingSlot as G,pageDeliverySchema as B,validateAuthoredPageManifest as J,validatePageManifest as X}from"./page-schema.js";import{evaluatePageTree as K,jsonProps as U}from"./page-eval.js";import{PageAuthoringError as s,PageCompilerError as M}from"./page-errors.js";const T=R(import.meta.url);let H,Y;function A(){return H??=T("esbuild")}o(A,"esbuild");function j(){return Y??=T("typescript")}o(j,"typescript");function z(n){return n instanceof s?n:new s(n instanceof Error?n.message:String(n))}o(z,"asPageAuthoringError");const Q=5,k=60;function V(n,t){const e=Math.max(0,t-k),r=Math.min(n.length,t+k),i=e>0?"\u2026":"";return` ${`${i}${n.slice(e,r)}${r<n.length?"\u2026":""}`}
2
+ ${" ".repeat(i.length+t-e)}^`}o(V,"excerpt");function W(n){const t=n.text?.trim()?n.text:"unknown error",e=n.location;if(e==null||e.line===void 0)return t;const r=`page.tsx:${e.line}:${e.column??0}`,i=e.lineText===void 0||e.column===void 0?"":`
3
+ ${V(e.lineText,e.column)}`;return`${t} (${r})${i}${Z(e.lineText,e.column)}`}o(W,"describeCompileDiagnostic");function Z(n,t){if(n===void 0||t===void 0||t<1)return"";const e=n[t-1];return e!=="'"&&e!=='"'?"":`
4
+ hint: the ${e} just before this column may be a stray quote inside a ${e}-quoted string, which ends the string early; escape it (\\${e}) or quote the string with the other quote character.`}o(Z,"quoteHint");function I(n,t){try{const e=J(n,t),r=e.slots.filter(G);if(r.length>=2)for(const i of r){const a=i.config.label;if(typeof a!="string"||a==="")throw new s(`response-bearing slot "${i.id}" requires a nonempty config.label when the page has multiple response-bearing slots`)}return e}catch(e){throw z(e)}}o(I,"validateAuthoredManifest");const ee="a page is evaluated with React and every registry component already in scope, so it takes no imports \u2014 write `<Page>`, `<UserQuestion>`, `<Card>` and friends directly.";function te(n){return A().transformSync(n,{loader:"tsx",format:"cjs",jsx:"transform",jsxFactory:"React.createElement",jsxFragment:"React.Fragment",target:"es2020",sourcefile:"page.tsx"}).code}o(te,"transformPage");function ne(n){let t;for(let e=0;e<2;e++)try{return te(n)}catch(r){const i=r.errors;if(i!==void 0&&i.length>0){const a=i.slice(0,Q).map(W),u=i.length>a.length?`
5
+ \u2026and ${i.length-a.length} more`:"";throw new s(`page does not compile: ${a.join(`
6
+ `)}${u}`)}t=r,A().stop()}throw new M(`page compiler (esbuild) service failed and did not recover after a restart: ${t instanceof Error?t.message:String(t)}`)}o(ne,"compilePage");function re(n){const t=j();return t.createSourceFile("page.tsx",n,t.ScriptTarget.ES2022,!0,t.ScriptKind.TSX)}o(re,"parseTsx");function q(n,t){t(n),n.forEachChild(e=>q(e,t))}o(q,"eachNode");function ie(n){const t=j();let e;if(q(n,r=>{e===void 0&&(t.isImportDeclaration(r)||t.isImportEqualsDeclaration(r)?e="import":t.isExportDeclaration(r)&&r.moduleSpecifier!==void 0?e="export \u2026 from":t.isCallExpression(r)&&(r.expression.kind===t.SyntaxKind.ImportKeyword?e="dynamic import()":t.isIdentifier(r.expression)&&r.expression.text==="require"&&(e="require()")))}),e!==void 0)throw new s(`page source uses ${e}: ${ee}`)}o(ie,"rejectModulePlumbing");function _(n,t){const e=n[t];if(e!==void 0){if(typeof e!="string")throw new s(`<Page> prop ${t} must be a string; it is part of the page manifest`);return e}}o(_,"stringProp");function oe(n,t,e){ie(re(n));const r=[...v,...F,...e.map(l=>E(l.kind))],i=K(t,r),a=[],u=o(l=>{for(const c of l)c.kind!=="text"&&(c.tag==="Page"&&a.push(c),u(c.children))},"collectPages");if(u(i),a.length===0)throw new s('page default export must render exactly one <Page title="\u2026"> root; it rendered none');if(a.length>1)throw new s(`page default export renders ${a.length} <Page> elements; exactly one is allowed`);const d=a[0],w=(_(d.props,"title")??"").trim();if(w==="")throw new s("<Page> requires a nonempty title prop");const m=_(d.props,"subtitle")?.trim();if(d.props.placement!==void 0)throw new s("<Page> placement is not authored; choose --placement inline or --placement panel when sending the page.");const f=new Map;for(const l of e){const c=E(l.kind);if(v.has(c))throw new s(`product page component kind "${l.kind}" derives JSX tag "${c}", which collides with a built-in page component`);if(f.has(c))throw new s(`product page component kind "${l.kind}" derives JSX tag "${c}", which duplicates product page component kind "${f.get(c)}"`);f.set(c,l.kind)}const x=[];let y=0;const P=o((l,c)=>{for(const p of l){if(p.kind==="text")continue;const S=p.tag==="Step"?y++:c,h=L[p.tag],b=f.get(p.tag);if(h!==void 0||b!==void 0){const{id:g,...D}=U(p.props);if(g!==void 0&&typeof g!="string")throw new s(`<${p.tag}> id prop must be a string`);x.push({...g===void 0?{}:{id:g},kind:h??b,step:S,config:D,...h===void 0?{unvalidated:!0}:{}})}P(p.children,S)}},"collectSlots");return P(d.children,0),{title:w,...m===void 0||m===""?{}:{subtitle:m},steps:Math.max(y,1),slots:x}}o(oe,"scanPage");function se(n,t=[],e,r={placement:"inline",inbox:!1,reply:!1}){const i=oe(n,ne(n),t);return I({schema:"crtr.page/v2",dialect:"jsx",title:i.title,...i.subtitle===void 0?{}:{subtitle:i.subtitle},delivery:r,document:"page.tsx",steps:i.steps,slots:i.slots,...e===void 0?{}:{source:e}},t)}o(se,"derivePageManifestFromDocument");function ae(n){const t=/<title\b[^>]*>([\s\S]*?)<\/title\s*>/i.exec(n)?.[1]?.replace(/<[^>]+>/g,"").trim();if(t===void 0||t==="")throw new s("HTML page requires a nonempty <title>.");const e=[...n.matchAll(/<meta\b[^>]*>/gi)].map(r=>r[0]).map(r=>{const i=new Map([...r.matchAll(/\b([^\s=/>]+)\s*=\s*(["'])([\s\S]*?)\2/g)].map(a=>[a[1].toLowerCase(),a[3]]));return i.get("name")?.toLowerCase()==="description"?i.get("content")?.trim():void 0}).find(r=>r!==void 0);return{title:t,...e===void 0||e===""?{}:{subtitle:e}}}o(ae,"htmlMetadata");function he(n,t,e={placement:"inline",inbox:!1,reply:!1}){const r=ae(n);return I({schema:"crtr.page/v2",dialect:"html",...r,delivery:e,document:"page.html",steps:1,slots:[],...t===void 0?{}:{source:t}},[])}o(he,"deriveHtmlPageManifest");function we(n,t=[],e,r){return se($(n,"utf8"),t,e,r)}o(we,"derivePageManifest");function C(n){try{return JSON.parse($(O(n,"page.json"),"utf8"))}catch{throw new s("page.json is not valid JSON")}}o(C,"readPageJson");function xe(n){return X(C(n))}o(xe,"parsePage");function ye(n){const t=C(n),e=B.safeParse(typeof t=="object"&&t!==null&&!Array.isArray(t)?t.delivery:void 0);if(!e.success)throw new s("page.json has an invalid delivery envelope");return e.data}o(ye,"parsePageDelivery");export{s as PageAuthoringError,M as PageCompilerError,ne as compilePage,he as deriveHtmlPageManifest,we as derivePageManifest,se as derivePageManifestFromDocument,xe as parsePage,ye as parsePageDelivery};
@@ -97,9 +97,12 @@ export declare function getRequestByInboxTicket(ticketId: string): HumanRequestR
97
97
  export declare function getRequestByDirectory(dir: string): HumanRequestRecord | null;
98
98
  export declare function getRequestByCompanion(nodeId: string): HumanRequestRecord | null;
99
99
  export declare function getRequestByIdempotencyKey(key: string): HumanRequestRecord | null;
100
+ /** SQL for the node a page request belongs to: its requester, else, for a notice only (a page that takes no response
101
+ * and so has no requester), the node that sent it. `alias` is the human_requests table or alias. */
102
+ export declare const askingNodeSql: (alias: string) => string;
100
103
  /** A page asked from a run of an app other than the terminal is that app's to show
101
104
  * (daemon-operations §question.*: the terminal shows `app:terminal` runs' questions). */
102
- export declare const NOT_OTHER_APP_QUESTION = "NOT (kind = 'page' AND EXISTS (SELECT 1 FROM node_rows n WHERE n.node_id = human_requests.requester_node_id\n AND n.run_id IS NOT NULL AND n.grantee IS NOT NULL AND n.grantee <> 'app:terminal'))";
105
+ export declare const NOT_OTHER_APP_QUESTION: string;
103
106
  export declare function listOpenInboxRequests(): HumanRequestRecord[];
104
107
  /** Every open request a person is being asked to act on: the inbox's set (`listOpenInboxRequests`), plus
105
108
  * answerable pages kept out of the inbox. This is the set the attach viewer's inbox strip shows. */