@north-light/cli 0.3.578 → 0.3.580

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 (74) hide show
  1. package/dist/build-root.js +1 -1
  2. package/dist/builtin-memory/crouter-sdk/release-notes.md +1 -1
  3. package/dist/builtin-memory/internal/storage-tiers.md +2 -2
  4. package/dist/clients/attach/viewer.js +338 -338
  5. package/dist/core/canvas/export-sqlite.js +1 -1
  6. package/dist/core/canvas/schema-pg/009-bodies.sql +10 -0
  7. package/dist/core/canvas/schema-version.d.ts +1 -1
  8. package/dist/core/canvas/schema-version.js +1 -1
  9. package/dist/core/command.js +5 -5
  10. package/dist/core/feed/feed.js +3 -3
  11. package/dist/core/feed/inbox.js +1 -1
  12. package/dist/core/feed/reports.js +2 -2
  13. package/dist/core/graph/access.d.ts +1 -1
  14. package/dist/core/graph/bodies-boot-copy.d.ts +14 -0
  15. package/dist/core/graph/bodies-boot-copy.js +1 -0
  16. package/dist/core/graph/bodies.d.ts +13 -6
  17. package/dist/core/graph/bodies.js +1 -1
  18. package/dist/core/graph/documents.js +8 -8
  19. package/dist/core/graph/objects.js +11 -11
  20. package/dist/core/graph/package-docs.js +5 -5
  21. package/dist/core/graph/repo-sync/exchange.js +2 -2
  22. package/dist/core/graph/repo-sync/ref-format.d.ts +0 -1
  23. package/dist/core/graph/repo-sync/ref-format.js +2 -2
  24. package/dist/core/human/feedback-companion.js +1 -1
  25. package/dist/core/human/page.d.ts +2 -4
  26. package/dist/core/human/page.js +6 -6
  27. package/dist/core/human/request-files.d.ts +14 -0
  28. package/dist/core/human/request-files.js +1 -0
  29. package/dist/core/human/requests.d.ts +3 -0
  30. package/dist/core/human/requests.js +18 -17
  31. package/dist/core/human/tickets.js +1 -1
  32. package/dist/core/human/visible-paths.d.ts +7 -0
  33. package/dist/core/human/visible-paths.js +1 -1
  34. package/dist/core/peer/receiver.js +1 -1
  35. package/dist/core/review/document.d.ts +4 -4
  36. package/dist/core/review/document.js +2 -2
  37. package/dist/core/review/project.d.ts +7 -5
  38. package/dist/core/review/project.js +2 -1
  39. package/dist/core/review/remap.js +1 -1
  40. package/dist/core/review/stage.d.ts +2 -0
  41. package/dist/core/review/stage.js +1 -1
  42. package/dist/core/review/store.js +1 -1
  43. package/dist/core/runtime/roadmap.js +3 -3
  44. package/dist/core/substrate/delivery/corpus.d.ts +2 -0
  45. package/dist/core/substrate/delivery/corpus.js +3 -3
  46. package/dist/core/substrate/delivery/deliver.js +3 -3
  47. package/dist/core/substrate/delivery/render-boot.js +25 -25
  48. package/dist/core/substrate/delivery/render-event.d.ts +1 -1
  49. package/dist/core/substrate/delivery/render-event.js +4 -4
  50. package/dist/core/substrate/delivery/types.d.ts +2 -2
  51. package/dist/daemon/api/bridge.d.ts +1 -1
  52. package/dist/daemon/api/bridge.js +5 -5
  53. package/dist/daemon/api/document-listener.js +4 -4
  54. package/dist/daemon/api/handlers/docs.js +1 -1
  55. package/dist/daemon/api/handlers/feedback-comments.js +2 -2
  56. package/dist/daemon/api/handlers/inbox-page.js +1 -1
  57. package/dist/daemon/api/handlers/inbox.js +1 -1
  58. package/dist/daemon/api/handlers/memory.js +4 -4
  59. package/dist/daemon/api/handlers/reports.js +4 -4
  60. package/dist/daemon/api/handlers/reviews.js +1 -1
  61. package/dist/daemon/boot-migrations.js +1 -1
  62. package/dist/daemon/profile-delete.js +1 -1
  63. package/dist/daemon/review/finish.js +1 -1
  64. package/dist/daemon/review/sweep.js +1 -1
  65. package/dist/daemon/runs/observer.js +3 -3
  66. package/dist/migrations/004-canvas-documents/apply.d.ts +2 -4
  67. package/dist/migrations/004-canvas-documents/apply.js +1 -1
  68. package/dist/migrations/004-canvas-documents/index.js +2 -2
  69. package/dist/shared/env.d.ts +5 -0
  70. package/dist/shared/env.js +2 -2
  71. package/docs/sdk/release-notes.md +1 -1
  72. package/package.json +2 -2
  73. package/packages/crouter-identity/package.json +1 -1
  74. package/runtime.lock.json +13 -13
@@ -1,11 +1,11 @@
1
- var L=Object.defineProperty;var h=(n,a)=>L(n,"name",{value:a,configurable:!0});import{createHash as W,timingSafeEqual as A}from"node:crypto";import{createServer as U}from"node:http";import{readFileSync as z}from"node:fs";import{join as E,extname as B}from"node:path";import{fileURLToPath as G}from"node:url";import{withCanvasWrite as b}from"../../core/canvas/db.js";import{ApiError as I}from"../../api/errors.js";import{bodyOf as C}from"../../core/graph/bodies.js";import{canChange as H,isShipped as F}from"../../core/graph/access.js";import{documentRecord as x,editDocument as J,heads as O,withCurrentNames as k}from"../../core/graph/documents.js";import{getById as N}from"../../core/graph/objects.js";import{subscribeDocumentEvents as M}from"../../core/graph/events.js";import{emitEvent as V}from"../../core/events/emit.js";import{checkoutsForCwd as X}from"../../core/graph/repo-sync/repos.js";import{canonicalName as Z,repoChange as K,syncChanged as Q}from"./handlers/docs.js";import{escapeHtml as S}from"./vendor-signin.js";import{readerFor as $,resolveRef as Y,warmDocumentWatchers as q}from"./reader.js";import{mergeDocumentLines as ee}from"./document-merge.js";import{crtrVersion as te}from"../../shared/crtr-version.js";const ne="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'",T={class:"person"},y=encodeURIComponent(te()),j=G(new URL("../../../dist/",import.meta.url));function m(n,a,t,i){n.writeHead(a,{"Content-Type":"application/json; charset=utf-8"}),n.end(JSON.stringify({error:{code:t,message:i}}))}h(m,"error");function P(n,a){const t=h(i=>W("sha256").update(i).digest(),"digest");return A(t(n),t(a))}h(P,"sameSecret");function oe(n,a){for(const t of(n??"").split(";")){const i=t.indexOf("=");if(i!==-1&&t.slice(0,i).trim()===a)return t.slice(i+1).trim()}}h(oe,"cookieValue");function R(n,a,t){n.writeHead(a,{"Content-Type":"application/json; charset=utf-8"}),n.end(JSON.stringify(t))}h(R,"json");async function D(n){return await b(async a=>{const t=await N(a,n.id);if(!t||t.deleted_at||t.type!=="document")return null;const i=await $(a,{principal:T}),l=await x(a,t),o=await O(a,t),e=await Z(a,t),u=o.length===1&&await H(a,i,t);return{id:t.id,name:e,revision:l.revision,heads_key:l.heads_key,body:o.length===1?await k(a,t,C(o[0].body_hash,l.source_path)):"",editable:u,...u?{}:{readonly_reason:o.length!==1?`has ${o.length} heads and needs a merge`:await F(a,t)?"shipped by a plugin":"you cannot change this document"}}})}h(D,"snapshot");async function p(n){return await b(async a=>{const t=await $(a,{principal:T});try{const i=await Y(a,t,n);return"deleted"in i||"remote"in i||i.type!=="document"?null:i}catch(i){if(i instanceof I&&i.code==="not_found")return null;throw i}})}h(p,"resolveDocument");async function ae(n){const a=[];let t=0;for await(const i of n){if(t+=i.length,t>2e6)throw new Error("request body exceeds 2 MB");a.push(i)}return JSON.parse(Buffer.concat(a).toString("utf8"))}h(ae,"requestBody");async function re(n){await q(n.id);const a=await b(async t=>{const i=await $(t,{principal:T}),l=await N(t,n.id);if(!l||l.type!=="document"||l.deleted_at)return{status:404,value:{error:{code:"not_found",message:"document not found"}}};const o=await x(t,l),e=await O(t,l);if(e.length!==1||!await H(t,i,l))return{status:403,value:{error:{code:"forbidden",message:"document is read-only"}}};const u=await k(t,l,C(e[0].body_hash,o.source_path)),c=await t.prepareUncached("SELECT body_hash FROM revisions WHERE document = ? AND rev_id = ?").get(l.id,n.base_heads_key);if(!c||await k(t,l,C(c.body_hash,o.source_path))!==n.base_body)return{status:409,value:{saved:!1,conflict:{revision:o.revision,heads_key:o.heads_key,current_body:u,your_body:n.body}}};const r=n.base_heads_key!==o.heads_key,s=r?ee(n.base_body,n.body,u):n.body;if(s===null)return{status:409,value:{saved:!1,conflict:{revision:o.revision,heads_key:o.heads_key,current_body:u,your_body:n.body}}};const d=await J(t,i,l,{body:s},n.rationale?.trim()||"edited by the person in the browser",o.heads_key,{},async w=>await X(t,w));return"stale"in d?{status:409,value:{saved:!1,conflict:{revision:d.stale.revision,heads_key:d.stale.heads_key,current_body:u,your_body:n.body}}}:{status:200,value:{saved:!0,revision:d.revision,heads_key:d.heads_key,body:s,merged:r},change:await K(t,i,[l])}});return a.change&&await Q(a.change),a}h(re,"save");let v="the document listener has not started";function Te(){return v}h(Te,"documentListenerUnavailable");function Ee(n,a){const t=`crtr_doc_${n}`,i=U((o,e)=>{(async()=>{e.setHeader("Content-Security-Policy",ne),e.setHeader("X-Content-Type-Options","nosniff");const u=[`127.0.0.1:${n}`,`localhost:${n}`];if(typeof o.headers.host!="string"||!u.includes(o.headers.host))return m(e,403,"forbidden","invalid Host");if(o.method==="POST"){if(!u.some(r=>o.headers.origin===`http://${r}`))return m(e,403,"forbidden","invalid Origin");if(o.headers["content-type"]!=="application/json")return m(e,415,"unsupported_media_type","Content-Type must be application/json")}const c=new URL(o.url??"/",`http://127.0.0.1:${n}`);if(!c.pathname.startsWith("/assets/")){const r=o.method==="GET"&&c.pathname.startsWith("/r/")?c.searchParams.get("t"):null;if(r!==null&&P(r,a))return c.searchParams.delete("t"),e.writeHead(303,{Location:`${c.pathname}${c.search}`,"Set-Cookie":`${t}=${a}; HttpOnly; SameSite=Strict; Path=/`,"Cache-Control":"no-store"}),e.end();const s=oe(o.headers.cookie,t);if(r!==null||s===void 0||!P(s,a))return c.pathname.startsWith("/r/")?(e.writeHead(401,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}),e.end('<!doctype html><html><head><meta charset="utf-8"><title>Not authorized</title><link rel="icon" href="data:,"></head><body style="font-family:system-ui,sans-serif;max-width:40rem;margin:4rem auto;padding:0 1rem"><h1>Not authorized</h1><p>Open this document from its link in the attach viewer.</p></body></html>')):m(e,401,"unauthorized","open the document link from the attach viewer")}if(o.method==="GET"&&c.pathname.startsWith("/r/")){let r;try{r=decodeURIComponent(c.pathname.slice(3))}catch{return m(e,400,"invalid_request","ref is not valid percent-encoding")}const s=await p(r);return s?(e.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}),e.end(`<!doctype html><html><head><meta charset="utf-8"><title>${S(s.name)}</title><link rel="icon" href="data:,"><link rel="stylesheet" href="/assets/${y}/editor.css"></head><body><div id="app" data-ref="${S(r)}"></div><script type="module" src="/assets/${y}/editor.js"><\/script></body></html>`)):(e.writeHead(404,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}),e.end(`<!doctype html><html><head><meta charset="utf-8"><title>Document not found</title><link rel="icon" href="data:,"></head><body style="font-family:system-ui,sans-serif;max-width:40rem;margin:4rem auto;padding:0 1rem"><h1>Document not found</h1><p>No document you can read is named <code>${S(r)}</code>.</p></body></html>`))}if(o.method==="GET"&&c.pathname==="/api/doc"){const r=c.searchParams.get("ref"),s=r?await p(r):null,d=s&&await D(s);return d?R(e,200,{...d,ref:r}):m(e,404,"not_found","document not found")}if(o.method==="POST"&&c.pathname==="/api/save"){const r=await ae(o);if(!r||typeof r!="object"||Array.isArray(r))return m(e,400,"invalid_request","invalid save body");const s=r;if(typeof s.id!="string"||typeof s.base_heads_key!="string"||typeof s.base_body!="string"||typeof s.body!="string"||s.rationale!==void 0&&typeof s.rationale!="string")return m(e,400,"invalid_request","invalid save body");const d=await re(s);return R(e,d.status,d.value)}if(o.method==="GET"&&c.pathname==="/api/events"){const r=c.searchParams.get("id");if(!r||!await p(r))return m(e,404,"not_found","document not found");e.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-store",Connection:"keep-alive"}),e.write(`: connected
1
+ var L=Object.defineProperty;var h=(n,a)=>L(n,"name",{value:a,configurable:!0});import{createHash as W,timingSafeEqual as A}from"node:crypto";import{createServer as U}from"node:http";import{readFileSync as B}from"node:fs";import{join as E,extname as z}from"node:path";import{fileURLToPath as G}from"node:url";import{withCanvasWrite as b}from"../../core/canvas/db.js";import{ApiError as I}from"../../api/errors.js";import{get as C}from"../../core/graph/bodies.js";import{canChange as H,isShipped as F}from"../../core/graph/access.js";import{documentRecord as x,editDocument as J,heads as N,withCurrentNames as k}from"../../core/graph/documents.js";import{getById as O}from"../../core/graph/objects.js";import{subscribeDocumentEvents as M}from"../../core/graph/events.js";import{emitEvent as V}from"../../core/events/emit.js";import{checkoutsForCwd as X}from"../../core/graph/repo-sync/repos.js";import{canonicalName as Z,repoChange as K,syncChanged as Q}from"./handlers/docs.js";import{escapeHtml as S}from"./vendor-signin.js";import{readerFor as $,resolveRef as Y,warmDocumentWatchers as q}from"./reader.js";import{mergeDocumentLines as ee}from"./document-merge.js";import{crtrVersion as te}from"../../shared/crtr-version.js";const ne="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'",T={class:"person"},y=encodeURIComponent(te()),j=G(new URL("../../../dist/",import.meta.url));function m(n,a,t,r){n.writeHead(a,{"Content-Type":"application/json; charset=utf-8"}),n.end(JSON.stringify({error:{code:t,message:r}}))}h(m,"error");function P(n,a){const t=h(r=>W("sha256").update(r).digest(),"digest");return A(t(n),t(a))}h(P,"sameSecret");function ae(n,a){for(const t of(n??"").split(";")){const r=t.indexOf("=");if(r!==-1&&t.slice(0,r).trim()===a)return t.slice(r+1).trim()}}h(ae,"cookieValue");function R(n,a,t){n.writeHead(a,{"Content-Type":"application/json; charset=utf-8"}),n.end(JSON.stringify(t))}h(R,"json");async function D(n){return await b(async a=>{const t=await O(a,n.id);if(!t||t.deleted_at||t.type!=="document")return null;const r=await $(a,{principal:T}),l=await x(a,t),i=await N(a,t),e=await Z(a,t),u=i.length===1&&await H(a,r,t);return{id:t.id,name:e,revision:l.revision,heads_key:l.heads_key,body:i.length===1?await k(a,t,await C(a,i[0].body_hash)):"",editable:u,...u?{}:{readonly_reason:i.length!==1?`has ${i.length} heads and needs a merge`:await F(a,t)?"shipped by a plugin":"you cannot change this document"}}})}h(D,"snapshot");async function p(n){return await b(async a=>{const t=await $(a,{principal:T});try{const r=await Y(a,t,n);return"deleted"in r||"remote"in r||r.type!=="document"?null:r}catch(r){if(r instanceof I&&r.code==="not_found")return null;throw r}})}h(p,"resolveDocument");async function oe(n){const a=[];let t=0;for await(const r of n){if(t+=r.length,t>2e6)throw new Error("request body exceeds 2 MB");a.push(r)}return JSON.parse(Buffer.concat(a).toString("utf8"))}h(oe,"requestBody");async function re(n){await q(n.id);const a=await b(async t=>{const r=await $(t,{principal:T}),l=await O(t,n.id);if(!l||l.type!=="document"||l.deleted_at)return{status:404,value:{error:{code:"not_found",message:"document not found"}}};const i=await x(t,l),e=await N(t,l);if(e.length!==1||!await H(t,r,l))return{status:403,value:{error:{code:"forbidden",message:"document is read-only"}}};const u=await k(t,l,await C(t,e[0].body_hash)),c=await t.prepareUncached("SELECT body_hash FROM revisions WHERE document = ? AND rev_id = ?").get(l.id,n.base_heads_key);if(!c||await k(t,l,await C(t,c.body_hash))!==n.base_body)return{status:409,value:{saved:!1,conflict:{revision:i.revision,heads_key:i.heads_key,current_body:u,your_body:n.body}}};const o=n.base_heads_key!==i.heads_key,s=o?ee(n.base_body,n.body,u):n.body;if(s===null)return{status:409,value:{saved:!1,conflict:{revision:i.revision,heads_key:i.heads_key,current_body:u,your_body:n.body}}};const d=await J(t,r,l,{body:s},n.rationale?.trim()||"edited by the person in the browser",i.heads_key,{},async w=>await X(t,w));return"stale"in d?{status:409,value:{saved:!1,conflict:{revision:d.stale.revision,heads_key:d.stale.heads_key,current_body:u,your_body:n.body}}}:{status:200,value:{saved:!0,revision:d.revision,heads_key:d.heads_key,body:s,merged:o},change:await K(t,r,[l])}});return a.change&&await Q(a.change),a}h(re,"save");let v="the document listener has not started";function Te(){return v}h(Te,"documentListenerUnavailable");function Ee(n,a){const t=`crtr_doc_${n}`,r=U((i,e)=>{(async()=>{e.setHeader("Content-Security-Policy",ne),e.setHeader("X-Content-Type-Options","nosniff");const u=[`127.0.0.1:${n}`,`localhost:${n}`];if(typeof i.headers.host!="string"||!u.includes(i.headers.host))return m(e,403,"forbidden","invalid Host");if(i.method==="POST"){if(!u.some(o=>i.headers.origin===`http://${o}`))return m(e,403,"forbidden","invalid Origin");if(i.headers["content-type"]!=="application/json")return m(e,415,"unsupported_media_type","Content-Type must be application/json")}const c=new URL(i.url??"/",`http://127.0.0.1:${n}`);if(!c.pathname.startsWith("/assets/")){const o=i.method==="GET"&&c.pathname.startsWith("/r/")?c.searchParams.get("t"):null;if(o!==null&&P(o,a))return c.searchParams.delete("t"),e.writeHead(303,{Location:`${c.pathname}${c.search}`,"Set-Cookie":`${t}=${a}; HttpOnly; SameSite=Strict; Path=/`,"Cache-Control":"no-store"}),e.end();const s=ae(i.headers.cookie,t);if(o!==null||s===void 0||!P(s,a))return c.pathname.startsWith("/r/")?(e.writeHead(401,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}),e.end('<!doctype html><html><head><meta charset="utf-8"><title>Not authorized</title><link rel="icon" href="data:,"></head><body style="font-family:system-ui,sans-serif;max-width:40rem;margin:4rem auto;padding:0 1rem"><h1>Not authorized</h1><p>Open this document from its link in the attach viewer.</p></body></html>')):m(e,401,"unauthorized","open the document link from the attach viewer")}if(i.method==="GET"&&c.pathname.startsWith("/r/")){let o;try{o=decodeURIComponent(c.pathname.slice(3))}catch{return m(e,400,"invalid_request","ref is not valid percent-encoding")}const s=await p(o);return s?(e.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}),e.end(`<!doctype html><html><head><meta charset="utf-8"><title>${S(s.name)}</title><link rel="icon" href="data:,"><link rel="stylesheet" href="/assets/${y}/editor.css"></head><body><div id="app" data-ref="${S(o)}"></div><script type="module" src="/assets/${y}/editor.js"><\/script></body></html>`)):(e.writeHead(404,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}),e.end(`<!doctype html><html><head><meta charset="utf-8"><title>Document not found</title><link rel="icon" href="data:,"></head><body style="font-family:system-ui,sans-serif;max-width:40rem;margin:4rem auto;padding:0 1rem"><h1>Document not found</h1><p>No document you can read is named <code>${S(o)}</code>.</p></body></html>`))}if(i.method==="GET"&&c.pathname==="/api/doc"){const o=c.searchParams.get("ref"),s=o?await p(o):null,d=s&&await D(s);return d?R(e,200,{...d,ref:o}):m(e,404,"not_found","document not found")}if(i.method==="POST"&&c.pathname==="/api/save"){const o=await oe(i);if(!o||typeof o!="object"||Array.isArray(o))return m(e,400,"invalid_request","invalid save body");const s=o;if(typeof s.id!="string"||typeof s.base_heads_key!="string"||typeof s.base_body!="string"||typeof s.body!="string"||s.rationale!==void 0&&typeof s.rationale!="string")return m(e,400,"invalid_request","invalid save body");const d=await re(s);return R(e,d.status,d.value)}if(i.method==="GET"&&c.pathname==="/api/events"){const o=c.searchParams.get("id");if(!o||!await p(o))return m(e,404,"not_found","document not found");e.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-store",Connection:"keep-alive"}),e.write(`: connected
2
2
 
3
3
  `);const s=h(async _=>{if(_==="deleted"){e.write(`event: deleted
4
4
  data: {}
5
5
 
6
- `),e.end();return}const f=await p(r),g=f&&await D(f);g&&e.write(`event: edited
6
+ `),e.end();return}const f=await p(o),g=f&&await D(f);g&&e.write(`event: edited
7
7
  data: ${JSON.stringify({revision:g.revision,heads_key:g.heads_key})}
8
8
 
9
- `)},"notify"),d=await M(r,_=>{s(_).catch(f=>{V({level:"error",event:"daemon.document_events.notify_failed",error:f}),e.end()})}),w=setInterval(()=>e.write(`: keepalive
9
+ `)},"notify"),d=await M(o,_=>{s(_).catch(f=>{V({level:"error",event:"daemon.document_events.notify_failed",error:f}),e.end()})}),w=setInterval(()=>e.write(`: keepalive
10
10
 
11
- `),3e4);e.on("close",()=>{clearInterval(w),d()});return}if(o.method==="GET"&&c.pathname.startsWith(`/assets/${y}/`)){const r=c.pathname.slice(`/assets/${y}/`.length);if(!/^[a-zA-Z0-9_.-]+$/.test(r))return m(e,404,"not_found","asset not found");const s=r==="mermaid.js"?E(j,"clients/attach/assets/mermaid.min.js"):E(j,"doc-editor",r);try{const d=z(s);return e.writeHead(200,{"Content-Type":B(r)===".css"?"text/css; charset=utf-8":"text/javascript; charset=utf-8","Cache-Control":"public, max-age=31536000, immutable"}),e.end(d)}catch(d){if(d.code==="ENOENT")return m(e,404,"not_found","asset not found");throw d}}return m(e,404,"not_found","route not found")})().catch(u=>{e.headersSent?e.destroy():m(e,500,"server_failure",u instanceof Error?u.message:String(u))})});return{ready:new Promise((o,e)=>{i.once("error",u=>{v=`the document listener could not bind 127.0.0.1:${n}: ${u.message}`,e(u)}),i.listen(n,"127.0.0.1",()=>{i.removeAllListeners("error"),v=void 0,o()})}),close:h(()=>new Promise(o=>{v="the document listener is closed",i.closeAllConnections(),i.close(()=>o())}),"close")}}h(Ee,"createDocumentListener");export{Ee as createDocumentListener,Te as documentListenerUnavailable};
11
+ `),3e4);e.on("close",()=>{clearInterval(w),d()});return}if(i.method==="GET"&&c.pathname.startsWith(`/assets/${y}/`)){const o=c.pathname.slice(`/assets/${y}/`.length);if(!/^[a-zA-Z0-9_.-]+$/.test(o))return m(e,404,"not_found","asset not found");const s=o==="mermaid.js"?E(j,"clients/attach/assets/mermaid.min.js"):E(j,"doc-editor",o);try{const d=B(s);return e.writeHead(200,{"Content-Type":z(o)===".css"?"text/css; charset=utf-8":"text/javascript; charset=utf-8","Cache-Control":"public, max-age=31536000, immutable"}),e.end(d)}catch(d){if(d.code==="ENOENT")return m(e,404,"not_found","asset not found");throw d}}return m(e,404,"not_found","route not found")})().catch(u=>{e.headersSent?e.destroy():m(e,500,"server_failure",u instanceof Error?u.message:String(u))})});return{ready:new Promise((i,e)=>{r.once("error",u=>{v=`the document listener could not bind 127.0.0.1:${n}: ${u.message}`,e(u)}),r.listen(n,"127.0.0.1",()=>{r.removeAllListeners("error"),v=void 0,i()})}),close:h(()=>new Promise(i=>{v="the document listener is closed",r.closeAllConnections(),r.close(()=>i())}),"close")}}h(Ee,"createDocumentListener");export{Ee as createDocumentListener,Te as documentListenerUnavailable};
@@ -1 +1 @@
1
- var re=Object.defineProperty;var u=(n,e)=>re(n,"name",{value:e,configurable:!0});import{ApiError as k}from"../../../api/errors.js";import{DOC_KINDS as U,DOC_UNSET_FIELDS as V}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as K}from"../../../api/dto/objects.js";import{openDb as ae,withCanvasWrite as A}from"../../../core/canvas/db.js";import{CrtrError as se}from"../../../core/errors.js";import{emitEvent as ce}from"../../../core/events/emit.js";import{canChange as de,spaceOf as ue}from"../../../core/graph/access.js";import{get as j}from"../../../core/graph/bodies.js";import{deleteDocument as fe,documentRecord as L,editDocument as me,heads as W,history as pe,linksIn as G,moveDocument as le,readBase as we,setDocumentRecord as he,withCurrentNames as ye,writeDocument as ge}from"../../../core/graph/documents.js";import{resolveName as X}from"../../../core/graph/names.js";import{getById as M}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as ve}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as _e}from"../../../core/graph/repo-sync/repos.js";import{lintRules as be,rulesOf as ke}from"../../../core/graph/rules.js";import{unifiedDiff as Ae}from"../../../core/graph/diff.js";import{ownerForGrantee as Ee}from"../../../core/graph/objects.js";import{stubOf as $e}from"../../../core/peer/stubs.js";import{forwarding as Oe}from"../../peer/forward.js";import{resolvedLocally as je}from"./objects.js";import{ownerFromFlag as q,readerFor as E,resolveRef as x,warmReaderRepos as $}from"../reader.js";import{appCaller as z,asApiError as Re,requireOwnNamespace as De}from"./schemas.js";import{filterAsync as Ce,flatMapAsync as Ne}from"../../../core/async-array.js";function g(n,e,t,o,a={}){return new k(n,e,t,{...a,error:e,message:t,next:o})}u(g,"refuse");const b=u((n,e)=>new k(400,"invalid_request",n,e),"invalid");function C(n){const e=n.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw b("the request body must be a JSON object");return e}u(C,"bodyOf");const N=u(async n=>async e=>await _e(n,e),"reposFnFor");function S(n,e){if(n instanceof se){const t=n.details;if(n.code==="read_first")throw g(409,"read_first",n.message,`Read it first with \`crtr canvas read ${e?.id??"<ref>"}\`, then rerun the edit.`,t??{});if(n.code==="conflict")throw new k(409,"name_taken",n.message,t);if(n.code==="forbidden")throw typeof t?.next=="string"?g(403,"forbidden",n.message,t.next,t):new k(403,"forbidden",n.message,t);if(n.code==="scope_missing")throw g(403,"scope_missing",n.message,`Launch the run with \`${String(t?.scope)}\` in its scopes list.`,t??{});if(t?.rule==="repo-public-only")throw g(422,"repo_documents_are_public",n.message,'Own it as `user` and add a rule with `if: {"repo": "<handle>"}` so it loads only in that repo.')}throw Re(n)}u(S,"mapGraphError");async function B(n,e,t){const o=await x(n,e,decodeURIComponent(t));if("remote"in o)throw g(404,"not_found",Z(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new k(404,"not_found",`${o.deleted.name} is deleted`,{id:o.deleted.id,deleted:!0});if(o.type!=="document")throw b(`${t} is a ${o.type}, not a document`);return o}u(B,"refDocument");async function Y(n){const{stub:e}=await je(decodeURIComponent(n.params.ref),n);if(e===null)return;const t=await $e(await ae(),e);if(t.object.type!=="document")return;const o=await Oe.current.label(t.home_machine);throw g(404,"not_here",`this document lives on runtime ${o.name}; edit it (or read its history) from a node there`,`Run the command from a node on ${o.name}.`,{id:t.object.id,home_machine:o.id})}u(Y,"refuseRemoteDocument");function Z(n){return`no object named ${n} on this runtime; it may be on another runtime, or the name may be misspelled`}u(Z,"remoteMessage");async function Se(n,e){const t=e.owner===null?null:await M(n,e.owner);return t?t.type==="node"?t.id:t.name:""}u(Se,"ownerHandle");async function R(n,e){const t=await Se(n,e);return t?`${t}/${e.name}`:e.name}u(R,"canonicalName");async function Te(n,e){const t=e.owner===null?null:await M(n,e.owner),o=t?{id:t.id,type:t.type,handle:t.type==="node"?t.id:t.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:o,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at,...e.type==="document"?{schema_type:e.schema_type??null}:{}}}u(Te,"objectDTO");async function Ie(n,e){const t=e.owner===null?null:await M(n,e.owner);return t?.type==="repo"?t:null}u(Ie,"repoOwner");async function T(n,e,t,o){const a=[...new Map(await Ne(t,async f=>{const i=await Ie(n,f);return i?[[i.id,i]]:[]})).values()],c=e.class==="node"?e.cwd:o,s=e.class==="node"?[...e.checkouts,...e.projectCheckouts].map(f=>f.root):[];return{repos:a,dirs:[...new Set([...c!==void 0?[c]:[],...s])]}}u(T,"repoChange");async function I(n){for(const e of n.repos)try{await ve(e,n.dirs)}catch(t){ce({level:"warn",event:"repo_sync.failed",outcome:"failed",error:t,fields:{repo:e.name,step:"export",reason:t instanceof Error?t.message:String(t)}})}}u(I,"syncChanged");const J=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function Fe(n,e){if(n===void 0)return;if(!Array.isArray(n)){e.push("`rules` must be a list");return}const t=n.map(a=>{if(typeof a!="object"||a===null||Array.isArray(a))return a;const{match_frontmatter:c,...s}=a;return"matchFrontmatter"in s?{...s,match_frontmatter:s.matchFrontmatter}:c===void 0?s:{...s,matchFrontmatter:c}}),o=be(t);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return t}u(Fe,"rulesFromDTO");function Q(n,e){const t={},o=u(i=>{const d=n[i];if(d!==void 0){if(typeof d!="string"){e.push(`\`${i}\` must be a string`);return}return d}},"str"),a=o("preview");a!==void 0&&(a.trim()===""||/[\r\n]/.test(a)?e.push("`preview` must be one non-empty line"):t.preview=a);const c=o("summary");c!==void 0&&(t.summary=c);const s=o("why_it_exists");if(s!==void 0&&(t.whyItExists=s),n.privacy!==void 0&&(K.includes(n.privacy)?t.privacy=n.privacy:e.push(`\`privacy\` must be one of ${K.join(", ")}`)),n.unlisted!==void 0&&(typeof n.unlisted!="boolean"?e.push("`unlisted` must be a boolean"):t.unlisted=n.unlisted),n.lint_ignore!==void 0){const i=n.lint_ignore;!Array.isArray(i)||i.some(d=>typeof d!="string")?e.push("`lint_ignore` must be a list of strings"):t.lintIgnore=i}if(n.extensions!==void 0){const i=n.extensions;typeof i!="object"||i===null||Array.isArray(i)||Object.values(i).some(d=>typeof d!="object"||d===null||Array.isArray(d))?e.push("`extensions` must map each plugin to an object of fields"):t.extensions=i}const f=Fe(n.rules,e);return f!==void 0&&(t.rules=f),t}u(Q,"fieldsFrom");function ee(n){if(n.length>0)throw b(n.join("; "),{errors:n})}u(ee,"failOn");async function te(n,e,t,o){const a=await Ce(G(t),async c=>"remote"in await X(n,e,c));return{...o.length>0?{unresolved_links:o}:{},...a.length>0?{remote_links:a}:{}}}u(te,"linkReport");async function Le(n){const e=C(n),t=[],o=e.name;(typeof o!="string"||!J.test(o))&&t.push("`name` must be segments of letters, digits, `_` or `-` joined by `/`"),typeof e.body!="string"&&t.push("`body` must be a string"),typeof e.preview!="string"&&t.push("`preview` is required");const a=e.kind??"knowledge";U.includes(a)||t.push(a==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${U.join(", ")}`);const c=Q(e,t);e.cwd!==void 0&&typeof e.cwd!="string"&&t.push("`cwd` must be a string");const s=e.schema_type;s!==void 0&&typeof s!="string"&&t.push("`schema_type` must be a string");const f=e.relations;f!==void 0&&(!Array.isArray(f)||f.some(r=>typeof r!="object"||r===null||typeof r.relation!="string"||typeof r.subject!="string"))&&t.push("`relations` must be a list of {relation, subject} strings"),ee(t);const i=e;await $(n.principal,i.cwd);const{dto:d,change:h}=await A(async r=>{const m=await E(r,n);i.schema_type!==void 0?await De(r,z(n).app,i.schema_type):i.relations!==void 0&&z(n);const p=i.owner==="user"&&m.class==="app"&&m.sub!==void 0?await Ee(r,m.sub.startsWith("user:")?m.sub:`user:${m.sub}`):await q(r,m,i.owner,i.cwd);try{const{object:w,unresolved:_}=await ge(r,m,{...c,owner:p,name:i.name,kind:a,preview:c.preview,privacy:c.privacy??"public",...i.schema_type!==void 0?{schemaType:i.schema_type}:{},...i.relations!==void 0?{relations:i.relations}:{}},i.body,await N(r));return{dto:{id:w.id,name:await R(r,w),...await te(r,m,i.body,_)},change:await T(r,m,[w],i.cwd)}}catch(w){S(w)}});return await I(h),d}u(Le,"write");function We(n,e){let t=n;e.forEach(({old:o,new:a},c)=>{const s=o===""?-1:t.indexOf(o);if(s<0)throw g(422,"replace_no_match",`replace[${c}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:c});if(t.indexOf(o,s+1)>=0)throw g(422,"replace_ambiguous",`replace[${c}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:c});t=t.slice(0,s)+a+t.slice(s+o.length)})}u(We,"checkReplacements");async function Me(n){const e=C(n),t=[],o=e.body!==void 0,a=e.replace!==void 0;o&&a&&t.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&t.push("`body` must be a string");const c=e.replace;a&&(!Array.isArray(c)||c.length===0||c.some(r=>typeof r!="object"||r===null||typeof r.old!="string"||typeof r.new!="string"))&&t.push("`replace` must be a non-empty list of {old, new} strings"),(typeof e.rationale!="string"||e.rationale.trim()==="")&&t.push("`rationale` is required"),e.verbatim!==void 0&&typeof e.verbatim!="boolean"&&t.push("`verbatim` must be a boolean"),e.base!==void 0&&(typeof e.base!="string"||e.base==="")&&t.push("`base` must be a non-empty heads key"),e.base!==void 0&&!o&&!a&&t.push("`base` applies only to a `body` or `replace` edit");const s=Q(e,t),f=e.unset??[];if(!Array.isArray(f)||f.some(r=>!V.includes(r)))t.push(`\`unset\` takes only ${V.join(", ")}`);else for(const r of f)r==="summary"&&(s.summary=null),r==="why_it_exists"&&(s.whyItExists=null),r==="unlisted"&&(s.unlisted=!1),r==="lint_ignore"&&(s.lintIgnore=null),r==="extensions"&&(s.extensions=null),r==="rules"&&(s.rules=[]);t.length===0&&!o&&!a&&Object.keys(s).length===0&&t.push("nothing to change: give `body`, `replace`, or a field"),ee(t);const i=e;await Y(n),await $(n.principal);const{dto:d,synced:h}=await A(async r=>{const m=await E(r,n),p=await B(r,m,n.params.ref),w=await R(r,p),_=await T(r,m,[p]);try{if(!o&&!a)return await he(r,m,p,s,i.rationale),{dto:{id:p.id,name:w,revision:(await L(r,p)).revision},synced:_};const v=i.base??await we(r,m,p);if(a&&v!==null){const P=await L(r,p),H=await W(r,p);P.source_path===null&&P.heads_key===v&&H.length===1&&We(await ye(r,p,j(H[0].body_hash)),i.replace)}const D=i.verbatim===!0,l=o?{body:i.body,verbatim:D}:{replace:i.replace,verbatim:D},y=await me(r,m,p,l,i.rationale,v,s,await N(r));if("stale"in y)throw g(409,"stale_revision",`${w} changed after your read; it is now at revision ${y.stale.revision}`,`Read it again with \`crtr canvas read ${w}\`, then redo your change on the current text.`,{stale:y.stale});const F=j((await W(r,p))[0].body_hash);return{dto:{id:p.id,name:w,revision:y.revision,...await te(r,m,F,y.unresolved)},synced:_}}catch(v){S(v,p)}});return await I(h),d}u(Me,"edit");async function qe(n){const e=C(n);if(e.new_name===void 0&&e.owner===void 0)throw b("give a new name, an owner, or both");if(e.new_name!==void 0&&(typeof e.new_name!="string"||!J.test(e.new_name)))throw b("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");await $(n.principal,e.cwd);const{dto:t,change:o}=await A(async a=>{const c=await E(a,n),s=await B(a,c,n.params.ref),f=e.owner===void 0?void 0:await q(a,c,e.owner,e.cwd);try{const i=await le(a,c,s,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}},await N(a));return{dto:{id:i.id,name:await R(a,i)},change:await T(a,c,[s,i],e.cwd)}}catch(i){S(i,s)}});return await I(o),t}u(qe,"move");async function Be(n){await $(n.principal);const{dto:e,change:t}=await A(async o=>{const a=await E(o,n),c=await B(o,a,n.params.ref),s=await R(o,c),f=await T(o,a,[c]);try{await fe(o,a,c,await N(o))}catch(i){S(i,c)}return{dto:{id:c.id,name:s},change:f}});return await I(t),e}u(Be,"remove");async function Pe(n){const e=n.query.get("diff"),t=e===null?void 0:Number(e);if(t!==void 0&&!(Number.isInteger(t)&&t>0))throw b("`diff` must be a revision number");return await Y(n),await $(n.principal),await A(async o=>{const a=await E(o,n),c=await x(o,a,decodeURIComponent(n.params.ref));if("remote"in c)throw g(404,"not_found",Z(c.remote),"Check the name with `crtr canvas search`.");const s="deleted"in c?c.deleted:c;if(s.type!=="document")throw b(`${s.name} is a ${s.type}, not a document`);const f=await pe(o,a,s),i={object:await Te(o,s),revisions:f.map(d=>({rev_id:d.rev_id,number:d.number,author_id:d.author,rationale:d.rationale,verbatim:d.verbatim,created_at:d.created_at,parents:d.parents,acting_app:d.acting_app,acting_for:d.acting_for}))};if(t!==void 0){const d=f.find(r=>r.number===t);if(!d)throw new k(404,"not_found",`${s.name} has no revision ${t}`);const h=d.parents[0]===void 0?void 0:f.find(r=>r.rev_id===d.parents[0]);i.diff=Ae(h?j(h.body_hash):"",j(d.body_hash))}return i})}u(Pe,"revisionHistory");const ne=1e3,oe=1e3,ie=3e3,He=/^When\b.+\bbecause\b.+/,Ue=u(n=>{const e=n.trim();return e===""?0:e.split(/\s+/).length},"words");function Ve(n,e){if(n.some(o=>o.on==="boot"&&o.deliver==="content")&&e>ne)return`body is ${e} words, over the ${ne}-word cap for a boot rule at content (the whole body enters every node's context). Keep the core here and move separate subjects into [[linked]] documents.`;const t=[...new Set(n.filter(o=>o.on!=="boot"&&o.deliver==="content").map(o=>o.on))];return t.length>0&&e>oe?`body is ${e} words, over the ${oe}-word cap for a content rule on ${t.join("/")}. Keep the core here and move separate subjects into [[linked]] documents.`:n.some(o=>o.on==="boot"&&o.deliver==="preview")&&e>ie?`body is ${e} words, over the ${ie}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(Ve,"lengthFinding");async function Ke(n){const e=C(n);return await $(n.principal,e.cwd),await A(async t=>{const o=await E(t,n),a=e.owner===void 0?null:await q(t,o,e.owner,e.cwd),c=await t.prepare("SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND home_machine IS NULL AND kind != 'report' ORDER BY name").all(),s=[],f=new Map;for(const i of c){if(a&&i.owner!==a.id||!await de(t,o,i))continue;const d=await L(t,i),h=new Set(d.lint_ignore??[]),r=await R(t,i),m=u((l,y,F)=>{h.has(l)||s.push({name:r,severity:y,rule:l,message:F})},"add"),p=await ke(t,i),w=(await W(t,i))[0],_=w?j(w.body_hash):"",v=Ve(p,Ue(_));v&&m("length","error",v),p.some(l=>l.deliver==="preview")&&!He.test(i.preview??"")&&m("preview-shape","error",`a rule delivers this document at preview, so the preview must read "When <situation in the reader's work>, read this because <what the reader gains>"`);const D=new Set((await t.prepare("SELECT written_as FROM edges WHERE type = 'links-to' AND src = ?").all(i.id)).map(l=>l.written_as));for(const l of G(_)){if(D.has(l))continue;const y=await X(t,o,l);"remote"in y||"object"in y||"deleted"in y||m("links","warning",`[[${l}]] names no object in your view`)}if(p.some(l=>l.on==="workspace-open"&&l.deliver==="content")){const l=await ue(t,i);l.type==="repo"&&!h.has("workspace-open")&&f.set(l.name,[...f.get(l.name)??[],r])}}for(const[i,d]of f)if(!(d.length<2))for(const h of d)s.push({name:h,severity:"warning",rule:"workspace-open",message:`repo ${i} has ${d.length} documents delivered at content on workspace-open (${d.join(", ")}); keep one and link the rest from it`});return{findings:s}})}u(Ke,"lint");const O=u((n,e=200)=>async t=>({status:e,body:await n(t)}),"ok"),wt=[{method:"POST",pattern:"/v1/docs",handler:O(Le,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:O(Me)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:O(qe)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:O(Be)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:O(Pe)},{method:"POST",pattern:"/v1/docs/lint",handler:O(Ke)}];export{R as canonicalName,wt as docRoutes,T as repoChange,I as syncChanged};
1
+ var re=Object.defineProperty;var u=(n,e)=>re(n,"name",{value:e,configurable:!0});import{ApiError as A}from"../../../api/errors.js";import{DOC_KINDS as H,DOC_UNSET_FIELDS as U}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as V}from"../../../api/dto/objects.js";import{openDb as se,withCanvasWrite as E}from"../../../core/canvas/db.js";import{CrtrError as ce}from"../../../core/errors.js";import{emitEvent as de}from"../../../core/events/emit.js";import{canChange as ue,spaceOf as fe}from"../../../core/graph/access.js";import{get as x,getMany as K}from"../../../core/graph/bodies.js";import{deleteDocument as me,documentRecord as M,editDocument as pe,heads as W,history as le,linksIn as G,moveDocument as he,readBase as we,setDocumentRecord as ye,withCurrentNames as ge,writeDocument as ve}from"../../../core/graph/documents.js";import{resolveName as X}from"../../../core/graph/names.js";import{getById as B}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as _e}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as be}from"../../../core/graph/repo-sync/repos.js";import{lintRules as ke,rulesOf as Ae}from"../../../core/graph/rules.js";import{unifiedDiff as Ee}from"../../../core/graph/diff.js";import{ownerForGrantee as $e}from"../../../core/graph/objects.js";import{stubOf as Oe}from"../../../core/peer/stubs.js";import{forwarding as je}from"../../peer/forward.js";import{resolvedLocally as Re}from"./objects.js";import{ownerFromFlag as q,readerFor as $,resolveRef as z,warmReaderRepos as O}from"../reader.js";import{appCaller as Y,asApiError as De,requireOwnNamespace as Ce}from"./schemas.js";import{filterAsync as Ne,flatMapAsync as Se}from"../../../core/async-array.js";function y(n,e,t,o,r={}){return new A(n,e,t,{...r,error:e,message:t,next:o})}u(y,"refuse");const k=u((n,e)=>new A(400,"invalid_request",n,e),"invalid");function S(n){const e=n.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw k("the request body must be a JSON object");return e}u(S,"bodyOf");const T=u(async n=>async e=>await be(n,e),"reposFnFor");function I(n,e){if(n instanceof ce){const t=n.details;if(n.code==="read_first")throw y(409,"read_first",n.message,`Read it first with \`crtr canvas read ${e?.id??"<ref>"}\`, then rerun the edit.`,t??{});if(n.code==="conflict")throw new A(409,"name_taken",n.message,t);if(n.code==="forbidden")throw typeof t?.next=="string"?y(403,"forbidden",n.message,t.next,t):new A(403,"forbidden",n.message,t);if(n.code==="scope_missing")throw y(403,"scope_missing",n.message,`Launch the run with \`${String(t?.scope)}\` in its scopes list.`,t??{});if(t?.rule==="repo-public-only")throw y(422,"repo_documents_are_public",n.message,'Own it as `user` and add a rule with `if: {"repo": "<handle>"}` so it loads only in that repo.')}throw De(n)}u(I,"mapGraphError");async function P(n,e,t){const o=await z(n,e,decodeURIComponent(t));if("remote"in o)throw y(404,"not_found",J(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new A(404,"not_found",`${o.deleted.name} is deleted`,{id:o.deleted.id,deleted:!0});if(o.type!=="document")throw k(`${t} is a ${o.type}, not a document`);return o}u(P,"refDocument");async function Z(n){const{stub:e}=await Re(decodeURIComponent(n.params.ref),n);if(e===null)return;const t=await Oe(await se(),e);if(t.object.type!=="document")return;const o=await je.current.label(t.home_machine);throw y(404,"not_here",`this document lives on runtime ${o.name}; edit it (or read its history) from a node there`,`Run the command from a node on ${o.name}.`,{id:t.object.id,home_machine:o.id})}u(Z,"refuseRemoteDocument");function J(n){return`no object named ${n} on this runtime; it may be on another runtime, or the name may be misspelled`}u(J,"remoteMessage");async function Te(n,e){const t=e.owner===null?null:await B(n,e.owner);return t?t.type==="node"?t.id:t.name:""}u(Te,"ownerHandle");async function D(n,e){const t=await Te(n,e);return t?`${t}/${e.name}`:e.name}u(D,"canonicalName");async function Ie(n,e){const t=e.owner===null?null:await B(n,e.owner),o=t?{id:t.id,type:t.type,handle:t.type==="node"?t.id:t.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:o,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at,...e.type==="document"?{schema_type:e.schema_type??null}:{}}}u(Ie,"objectDTO");async function Fe(n,e){const t=e.owner===null?null:await B(n,e.owner);return t?.type==="repo"?t:null}u(Fe,"repoOwner");async function F(n,e,t,o){const r=[...new Map(await Se(t,async f=>{const a=await Fe(n,f);return a?[[a.id,a]]:[]})).values()],c=e.class==="node"?e.cwd:o,s=e.class==="node"?[...e.checkouts,...e.projectCheckouts].map(f=>f.root):[];return{repos:r,dirs:[...new Set([...c!==void 0?[c]:[],...s])]}}u(F,"repoChange");async function L(n){for(const e of n.repos)try{await _e(e,n.dirs)}catch(t){de({level:"warn",event:"repo_sync.failed",outcome:"failed",error:t,fields:{repo:e.name,step:"export",reason:t instanceof Error?t.message:String(t)}})}}u(L,"syncChanged");const Q=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function Le(n,e){if(n===void 0)return;if(!Array.isArray(n)){e.push("`rules` must be a list");return}const t=n.map(r=>{if(typeof r!="object"||r===null||Array.isArray(r))return r;const{match_frontmatter:c,...s}=r;return"matchFrontmatter"in s?{...s,match_frontmatter:s.matchFrontmatter}:c===void 0?s:{...s,matchFrontmatter:c}}),o=ke(t);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return t}u(Le,"rulesFromDTO");function ee(n,e){const t={},o=u(a=>{const d=n[a];if(d!==void 0){if(typeof d!="string"){e.push(`\`${a}\` must be a string`);return}return d}},"str"),r=o("preview");r!==void 0&&(r.trim()===""||/[\r\n]/.test(r)?e.push("`preview` must be one non-empty line"):t.preview=r);const c=o("summary");c!==void 0&&(t.summary=c);const s=o("why_it_exists");if(s!==void 0&&(t.whyItExists=s),n.privacy!==void 0&&(V.includes(n.privacy)?t.privacy=n.privacy:e.push(`\`privacy\` must be one of ${V.join(", ")}`)),n.unlisted!==void 0&&(typeof n.unlisted!="boolean"?e.push("`unlisted` must be a boolean"):t.unlisted=n.unlisted),n.lint_ignore!==void 0){const a=n.lint_ignore;!Array.isArray(a)||a.some(d=>typeof d!="string")?e.push("`lint_ignore` must be a list of strings"):t.lintIgnore=a}if(n.extensions!==void 0){const a=n.extensions;typeof a!="object"||a===null||Array.isArray(a)||Object.values(a).some(d=>typeof d!="object"||d===null||Array.isArray(d))?e.push("`extensions` must map each plugin to an object of fields"):t.extensions=a}const f=Le(n.rules,e);return f!==void 0&&(t.rules=f),t}u(ee,"fieldsFrom");function te(n){if(n.length>0)throw k(n.join("; "),{errors:n})}u(te,"failOn");async function ne(n,e,t,o){const r=await Ne(G(t),async c=>"remote"in await X(n,e,c));return{...o.length>0?{unresolved_links:o}:{},...r.length>0?{remote_links:r}:{}}}u(ne,"linkReport");async function Me(n){const e=S(n),t=[],o=e.name;(typeof o!="string"||!Q.test(o))&&t.push("`name` must be segments of letters, digits, `_` or `-` joined by `/`"),typeof e.body!="string"&&t.push("`body` must be a string"),typeof e.preview!="string"&&t.push("`preview` is required");const r=e.kind??"knowledge";H.includes(r)||t.push(r==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${H.join(", ")}`);const c=ee(e,t);e.cwd!==void 0&&typeof e.cwd!="string"&&t.push("`cwd` must be a string");const s=e.schema_type;s!==void 0&&typeof s!="string"&&t.push("`schema_type` must be a string");const f=e.relations;f!==void 0&&(!Array.isArray(f)||f.some(i=>typeof i!="object"||i===null||typeof i.relation!="string"||typeof i.subject!="string"))&&t.push("`relations` must be a list of {relation, subject} strings"),te(t);const a=e;await O(n.principal,a.cwd);const{dto:d,change:m}=await E(async i=>{const p=await $(i,n);a.schema_type!==void 0?await Ce(i,Y(n).app,a.schema_type):a.relations!==void 0&&Y(n);const h=a.owner==="user"&&p.class==="app"&&p.sub!==void 0?await $e(i,p.sub.startsWith("user:")?p.sub:`user:${p.sub}`):await q(i,p,a.owner,a.cwd);try{const{object:w,unresolved:v}=await ve(i,p,{...c,owner:h,name:a.name,kind:r,preview:c.preview,privacy:c.privacy??"public",...a.schema_type!==void 0?{schemaType:a.schema_type}:{},...a.relations!==void 0?{relations:a.relations}:{}},a.body,await T(i));return{dto:{id:w.id,name:await D(i,w),...await ne(i,p,a.body,v)},change:await F(i,p,[w],a.cwd)}}catch(w){I(w)}});return await L(m),d}u(Me,"write");function We(n,e){let t=n;e.forEach(({old:o,new:r},c)=>{const s=o===""?-1:t.indexOf(o);if(s<0)throw y(422,"replace_no_match",`replace[${c}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:c});if(t.indexOf(o,s+1)>=0)throw y(422,"replace_ambiguous",`replace[${c}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:c});t=t.slice(0,s)+r+t.slice(s+o.length)})}u(We,"checkReplacements");async function Be(n){const e=S(n),t=[],o=e.body!==void 0,r=e.replace!==void 0;o&&r&&t.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&t.push("`body` must be a string");const c=e.replace;r&&(!Array.isArray(c)||c.length===0||c.some(i=>typeof i!="object"||i===null||typeof i.old!="string"||typeof i.new!="string"))&&t.push("`replace` must be a non-empty list of {old, new} strings"),(typeof e.rationale!="string"||e.rationale.trim()==="")&&t.push("`rationale` is required"),e.verbatim!==void 0&&typeof e.verbatim!="boolean"&&t.push("`verbatim` must be a boolean"),e.base!==void 0&&(typeof e.base!="string"||e.base==="")&&t.push("`base` must be a non-empty heads key"),e.base!==void 0&&!o&&!r&&t.push("`base` applies only to a `body` or `replace` edit");const s=ee(e,t),f=e.unset??[];if(!Array.isArray(f)||f.some(i=>!U.includes(i)))t.push(`\`unset\` takes only ${U.join(", ")}`);else for(const i of f)i==="summary"&&(s.summary=null),i==="why_it_exists"&&(s.whyItExists=null),i==="unlisted"&&(s.unlisted=!1),i==="lint_ignore"&&(s.lintIgnore=null),i==="extensions"&&(s.extensions=null),i==="rules"&&(s.rules=[]);t.length===0&&!o&&!r&&Object.keys(s).length===0&&t.push("nothing to change: give `body`, `replace`, or a field"),te(t);const a=e;await Z(n),await O(n.principal);const{dto:d,synced:m}=await E(async i=>{const p=await $(i,n),h=await P(i,p,n.params.ref),w=await D(i,h),v=await F(i,p,[h]);try{if(!o&&!r)return await ye(i,p,h,s,a.rationale),{dto:{id:h.id,name:w,revision:(await M(i,h)).revision},synced:v};const g=a.base??await we(i,p,h);if(r&&g!==null){const _=await M(i,h),N=await W(i,h);_.source_path===null&&_.heads_key===g&&N.length===1&&We(await ge(i,h,await x(i,N[0].body_hash)),a.replace)}const R=a.verbatim===!0,C=o?{body:a.body,verbatim:R}:{replace:a.replace,verbatim:R},b=await pe(i,p,h,C,a.rationale,g,s,await T(i));if("stale"in b)throw y(409,"stale_revision",`${w} changed after your read; it is now at revision ${b.stale.revision}`,`Read it again with \`crtr canvas read ${w}\`, then redo your change on the current text.`,{stale:b.stale});const l=await x(i,(await W(i,h))[0].body_hash);return{dto:{id:h.id,name:w,revision:b.revision,...await ne(i,p,l,b.unresolved)},synced:v}}catch(g){I(g,h)}});return await L(m),d}u(Be,"edit");async function qe(n){const e=S(n);if(e.new_name===void 0&&e.owner===void 0)throw k("give a new name, an owner, or both");if(e.new_name!==void 0&&(typeof e.new_name!="string"||!Q.test(e.new_name)))throw k("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");await O(n.principal,e.cwd);const{dto:t,change:o}=await E(async r=>{const c=await $(r,n),s=await P(r,c,n.params.ref),f=e.owner===void 0?void 0:await q(r,c,e.owner,e.cwd);try{const a=await he(r,c,s,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}},await T(r));return{dto:{id:a.id,name:await D(r,a)},change:await F(r,c,[s,a],e.cwd)}}catch(a){I(a,s)}});return await L(o),t}u(qe,"move");async function Pe(n){await O(n.principal);const{dto:e,change:t}=await E(async o=>{const r=await $(o,n),c=await P(o,r,n.params.ref),s=await D(o,c),f=await F(o,r,[c]);try{await me(o,r,c,await T(o))}catch(a){I(a,c)}return{dto:{id:c.id,name:s},change:f}});return await L(t),e}u(Pe,"remove");async function He(n){const e=n.query.get("diff"),t=e===null?void 0:Number(e);if(t!==void 0&&!(Number.isInteger(t)&&t>0))throw k("`diff` must be a revision number");return await Z(n),await O(n.principal),await E(async o=>{const r=await $(o,n),c=await z(o,r,decodeURIComponent(n.params.ref));if("remote"in c)throw y(404,"not_found",J(c.remote),"Check the name with `crtr canvas search`.");const s="deleted"in c?c.deleted:c;if(s.type!=="document")throw k(`${s.name} is a ${s.type}, not a document`);const f=await le(o,r,s),a={object:await Ie(o,s),revisions:f.map(d=>({rev_id:d.rev_id,number:d.number,author_id:d.author,rationale:d.rationale,verbatim:d.verbatim,created_at:d.created_at,parents:d.parents,acting_app:d.acting_app,acting_for:d.acting_for}))};if(t!==void 0){const d=f.find(p=>p.number===t);if(!d)throw new A(404,"not_found",`${s.name} has no revision ${t}`);const m=d.parents[0]===void 0?void 0:f.find(p=>p.rev_id===d.parents[0]),i=await K(o,m?[m.body_hash,d.body_hash]:[d.body_hash]);a.diff=Ee(m?i.get(m.body_hash):"",i.get(d.body_hash))}return a})}u(He,"revisionHistory");const oe=1e3,ie=1e3,ae=3e3,Ue=/^When\b.+\bbecause\b.+/,Ve=u(n=>{const e=n.trim();return e===""?0:e.split(/\s+/).length},"words");function xe(n,e){if(n.some(o=>o.on==="boot"&&o.deliver==="content")&&e>oe)return`body is ${e} words, over the ${oe}-word cap for a boot rule at content (the whole body enters every node's context). Keep the core here and move separate subjects into [[linked]] documents.`;const t=[...new Set(n.filter(o=>o.on!=="boot"&&o.deliver==="content").map(o=>o.on))];return t.length>0&&e>ie?`body is ${e} words, over the ${ie}-word cap for a content rule on ${t.join("/")}. Keep the core here and move separate subjects into [[linked]] documents.`:n.some(o=>o.on==="boot"&&o.deliver==="preview")&&e>ae?`body is ${e} words, over the ${ae}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(xe,"lengthFinding");async function Ke(n){const e=S(n);return await O(n.principal,e.cwd),await E(async t=>{const o=await $(t,n),r=e.owner===void 0?null:await q(t,o,e.owner,e.cwd),c=await t.prepare("SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND home_machine IS NULL AND kind != 'report' ORDER BY name").all(),s=[],f=new Map,a=[];for(const m of c)r&&m.owner!==r.id||await ue(t,o,m)&&a.push({doc:m,head:(await W(t,m))[0]});const d=await K(t,a.flatMap(m=>m.head?[m.head.body_hash]:[]));for(const{doc:m,head:i}of a){const p=await M(t,m),h=new Set(p.lint_ignore??[]),w=await D(t,m),v=u((l,_,N)=>{h.has(l)||s.push({name:w,severity:_,rule:l,message:N})},"add"),g=await Ae(t,m),R=i?d.get(i.body_hash):"",C=xe(g,Ve(R));C&&v("length","error",C),g.some(l=>l.deliver==="preview")&&!Ue.test(m.preview??"")&&v("preview-shape","error",`a rule delivers this document at preview, so the preview must read "When <situation in the reader's work>, read this because <what the reader gains>"`);const b=new Set((await t.prepare("SELECT written_as FROM edges WHERE type = 'links-to' AND src = ?").all(m.id)).map(l=>l.written_as));for(const l of G(R)){if(b.has(l))continue;const _=await X(t,o,l);"remote"in _||"object"in _||"deleted"in _||v("links","warning",`[[${l}]] names no object in your view`)}if(g.some(l=>l.on==="workspace-open"&&l.deliver==="content")){const l=await fe(t,m);l.type==="repo"&&!h.has("workspace-open")&&f.set(l.name,[...f.get(l.name)??[],w])}}for(const[m,i]of f)if(!(i.length<2))for(const p of i)s.push({name:p,severity:"warning",rule:"workspace-open",message:`repo ${m} has ${i.length} documents delivered at content on workspace-open (${i.join(", ")}); keep one and link the rest from it`});return{findings:s}})}u(Ke,"lint");const j=u((n,e=200)=>async t=>({status:e,body:await n(t)}),"ok"),wt=[{method:"POST",pattern:"/v1/docs",handler:j(Me,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:j(Be)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:j(qe)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:j(Pe)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:j(He)},{method:"POST",pattern:"/v1/docs/lint",handler:j(Ke)}];export{D as canonicalName,wt as docRoutes,F as repoChange,L as syncChanged};
@@ -1,3 +1,3 @@
1
- var g=Object.defineProperty;var c=(n,t)=>g(n,"name",{value:t,configurable:!0});import{basename as y}from"node:path";import{ApiError as a}from"../../../api/index.js";import{createPageFeedbackComment as I,readPageFeedback as u,resolvePageFeedbackComment as q,PageFeedbackCommentAlreadyResolvedError as C,PageTicketTerminalError as k,UnknownPageFeedbackCommentError as T}from"../../../core/human/feedback.js";import{discardFeedbackCompanion as w,pageDocumentSha256 as P,realizeFeedbackCompanion as N}from"../../../core/human/feedback-companion.js";import{parsePage as S}from"../../../core/human/page.js";import{pageTicketState as F,selectedPageDir as E}from"../../../core/human/tickets.js";import{deliverNodeMessage as x}from"../../messaging/node-message.js";import{requireTicketId as D,resolveTicket as O}from"./inbox.js";import{invalidRequest as b,objectBody as p,optionalString as R,requiredString as l}from"./validate.js";import{toPageFeedbackDTO as h,toPageFeedbackCommentDTO as v}from"../map.js";const M=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;async function _(n){const t=D(n.params),e=await O(t);if(e===null)throw new a(404,"ticket_not_found","no matching ticket");if(e.kind==="review")throw new a(409,"ticket_kind_unsupported","review tickets have no feedback comments");const o=await F(e.dir);if(o==="resolved"||o==="canceled")throw new a(409,"ticket_terminal","ticket already has a terminal outcome");return e}c(_,"requireMutablePageTicket");function j(n){const t=n.comment_id;if(t===void 0||!M.test(t))throw new a(400,"invalid_request","invalid feedback comment id");return t}c(j,"requireCommentId");function $(n){return[...n.quote.split(`
1
+ var g=Object.defineProperty;var c=(n,t)=>g(n,"name",{value:t,configurable:!0});import{basename as y}from"node:path";import{ApiError as a}from"../../../api/index.js";import{createPageFeedbackComment as I,readPageFeedback as u,resolvePageFeedbackComment as q,PageFeedbackCommentAlreadyResolvedError as C,PageTicketTerminalError as k,UnknownPageFeedbackCommentError as T}from"../../../core/human/feedback.js";import{discardFeedbackCompanion as w,pageDocumentSha256 as P,realizeFeedbackCompanion as N}from"../../../core/human/feedback-companion.js";import{readPageManifest as S}from"../../../core/human/page.js";import{pageTicketState as F,selectedPageDir as E}from"../../../core/human/tickets.js";import{deliverNodeMessage as x}from"../../messaging/node-message.js";import{requireTicketId as D,resolveTicket as M}from"./inbox.js";import{invalidRequest as b,objectBody as p,optionalString as O,requiredString as l}from"./validate.js";import{toPageFeedbackDTO as h,toPageFeedbackCommentDTO as v}from"../map.js";const R=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;async function _(n){const t=D(n.params),e=await M(t);if(e===null)throw new a(404,"ticket_not_found","no matching ticket");if(e.kind==="review")throw new a(409,"ticket_kind_unsupported","review tickets have no feedback comments");const o=await F(e.dir);if(o==="resolved"||o==="canceled")throw new a(409,"ticket_terminal","ticket already has a terminal outcome");return e}c(_,"requireMutablePageTicket");function j(n){const t=n.comment_id;if(t===void 0||!R.test(t))throw new a(400,"invalid_request","invalid feedback comment id");return t}c(j,"requireCommentId");function $(n){return[...n.quote.split(`
2
2
  `).map(e=>`> ${e}`),"",n.note].join(`
3
- `)}c($,"renderCommentTurn");async function z(n){const t=n.mutation.feedback.companionNodeId;if(t===void 0)throw new a(500,"internal","feedback has comments but no companion conversation");try{const e=await x({node_id:t,body:$(n.mutation.comment),label:n.title,from:"human",mode:"interactive",data:{page_id:y(n.dir),comment_id:n.mutation.comment.id,verb:"create"}});return{target_node_id:t,status:"delivered",via:e.via,woke:e.revived,...e.reason===void 0?{}:{reason:e.reason}}}catch(e){return{target_node_id:t,status:"failed",woke:!1,error:e instanceof Error?e.message:String(e)}}}c(z,"deliverCommentTurn");function A(n){const t=[],e=p(n.body,["quote","context","note"],t),o=l(e,"quote",t),i=R(e,"context",t),d=l(e,"note",t);return(t.length>0||o===void 0||d===void 0)&&b("page feedback comment request",t),{quote:o,...i===void 0?{}:{context:i},note:d}}c(A,"parseCreate");async function B(n){const t=A(n),e=await _(n),o=S(await E(e.dir));if(o.dialect!=="jsx")throw new a(409,"page_dialect_unsupported","feedback comments exist only on TSX pages");let i=(await u(e.dir)).companionNodeId,d,s=!1;if(i===void 0){const m=o.source?.nodeId;if(m===void 0)throw new a(409,"ticket_has_no_sender","this page carries no sending node to deliver feedback to");d=await P(e.dir);try{i=await N({ticketId:e.ticketId,dir:e.dir,senderNodeId:m,title:o.title})}catch(f){throw new a(409,"fork_failed",`the sending node could not be forked: ${f instanceof Error?f.message:String(f)}`)}s=!0}let r;try{r=await I(e.dir,{...t,companionNodeId:i,firstCommentedSha256:d})}catch(m){throw s&&await w(i),m instanceof k?new a(409,"ticket_terminal","ticket already has a terminal outcome"):m}return s&&r.feedback.companionNodeId!==i&&await w(i),{status:200,body:{comment:v(r.comment),feedback:h(r.feedback),delivery:await z({dir:e.dir,title:o.title,mutation:r})}}}c(B,"handleCreate");function U(n){const t=[],e=p(n.body,["node_id"],t),o=l(e,"node_id",t);return(t.length>0||o===void 0)&&b("feedback resolve request",t),o}c(U,"parseResolve");async function X(n){const t=await _(n),e=j(n.params),o=U(n),i=(await u(t.dir)).companionNodeId;if(i===void 0||o!==i)throw new a(403,"not_companion","only the ticket's bound feedback companion resolves its comments");let d;try{d=await q(t.dir,e)}catch(r){throw r instanceof T?new a(404,"unknown_comment",r.message):r instanceof C?new a(409,"comment_already_resolved",r.message):r instanceof k?new a(409,"ticket_terminal","ticket already has a terminal outcome"):r}return{status:200,body:{comment:v(d.comment),feedback:h(d.feedback)}}}c(X,"handleResolve");const ne=[{method:"POST",pattern:"/v1/human/inbox/:ticket_id/feedback-comments",handler:B},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/feedback-comments/:comment_id/resolve",handler:X}];export{ne as feedbackCommentRoutes};
3
+ `)}c($,"renderCommentTurn");async function z(n){const t=n.mutation.feedback.companionNodeId;if(t===void 0)throw new a(500,"internal","feedback has comments but no companion conversation");try{const e=await x({node_id:t,body:$(n.mutation.comment),label:n.title,from:"human",mode:"interactive",data:{page_id:y(n.dir),comment_id:n.mutation.comment.id,verb:"create"}});return{target_node_id:t,status:"delivered",via:e.via,woke:e.revived,...e.reason===void 0?{}:{reason:e.reason}}}catch(e){return{target_node_id:t,status:"failed",woke:!1,error:e instanceof Error?e.message:String(e)}}}c(z,"deliverCommentTurn");function A(n){const t=[],e=p(n.body,["quote","context","note"],t),o=l(e,"quote",t),i=O(e,"context",t),d=l(e,"note",t);return(t.length>0||o===void 0||d===void 0)&&b("page feedback comment request",t),{quote:o,...i===void 0?{}:{context:i},note:d}}c(A,"parseCreate");async function B(n){const t=A(n),e=await _(n),o=await S(await E(e.dir));if(o.dialect!=="jsx")throw new a(409,"page_dialect_unsupported","feedback comments exist only on TSX pages");let i=(await u(e.dir)).companionNodeId,d,s=!1;if(i===void 0){const m=o.source?.nodeId;if(m===void 0)throw new a(409,"ticket_has_no_sender","this page carries no sending node to deliver feedback to");d=await P(e.dir);try{i=await N({ticketId:e.ticketId,dir:e.dir,senderNodeId:m,title:o.title})}catch(f){throw new a(409,"fork_failed",`the sending node could not be forked: ${f instanceof Error?f.message:String(f)}`)}s=!0}let r;try{r=await I(e.dir,{...t,companionNodeId:i,firstCommentedSha256:d})}catch(m){throw s&&await w(i),m instanceof k?new a(409,"ticket_terminal","ticket already has a terminal outcome"):m}return s&&r.feedback.companionNodeId!==i&&await w(i),{status:200,body:{comment:v(r.comment),feedback:h(r.feedback),delivery:await z({dir:e.dir,title:o.title,mutation:r})}}}c(B,"handleCreate");function U(n){const t=[],e=p(n.body,["node_id"],t),o=l(e,"node_id",t);return(t.length>0||o===void 0)&&b("feedback resolve request",t),o}c(U,"parseResolve");async function X(n){const t=await _(n),e=j(n.params),o=U(n),i=(await u(t.dir)).companionNodeId;if(i===void 0||o!==i)throw new a(403,"not_companion","only the ticket's bound feedback companion resolves its comments");let d;try{d=await q(t.dir,e)}catch(r){throw r instanceof T?new a(404,"unknown_comment",r.message):r instanceof C?new a(409,"comment_already_resolved",r.message):r instanceof k?new a(409,"ticket_terminal","ticket already has a terminal outcome"):r}return{status:200,body:{comment:v(d.comment),feedback:h(d.feedback)}}}c(X,"handleResolve");const ne=[{method:"POST",pattern:"/v1/human/inbox/:ticket_id/feedback-comments",handler:B},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/feedback-comments/:comment_id/resolve",handler:X}];export{ne as feedbackCommentRoutes};
@@ -1 +1 @@
1
- var s=Object.defineProperty;var r=(e,t)=>s(e,"name",{value:t,configurable:!0});import{readFileSync as n}from"node:fs";import{readPageFeedback as u}from"../../../core/human/feedback.js";import{pageModulePath as a,pagePath as l}from"../../../core/human/convention.js";import{parsePage as p}from"../../../core/human/page.js";import{presentationDir as f}from"../../../core/human/requests.js";import{readPageProgress as g,readTicketResult as m}from"../../../core/human/tickets.js";import{toPageFeedbackDTO as k}from"../map.js";function y(e){const t={kind:e.kind,step:e.step,config:e.config};return e.id!==void 0&&(t.id=e.id),e.unvalidated===!0&&(t.unvalidated=!0),e.display===!0&&(t.display=!0),t}r(y,"pageSlotToDTO");function b(e){const t={schema:"crtr.page/v2",dialect:e.dialect,title:e.title,delivery:e.delivery,document:e.document,steps:e.steps,slots:e.slots.map(y)};if(e.subtitle!==void 0&&(t.subtitle=e.subtitle),e.source!==void 0){const i={};e.source.sessionName!==void 0&&(i.sessionName=e.source.sessionName),e.source.askedBy!==void 0&&(i.askedBy=e.source.askedBy),e.source.emittedAt!==void 0&&(i.emittedAt=e.source.emittedAt),e.source.profileName!==void 0&&(i.profileName=e.source.profileName),e.source.nodeId!==void 0&&(i.nodeId=e.source.nodeId),Object.keys(i).length>0&&(t.source=i)}return t}r(b,"pageManifestToDTO");function _(e){return e.settleable?e.state!=="settled"?"pending":e.outcome==="canceled"||e.outcome==="dismissed"?"canceled":"resolved":"passive"}r(_,"state");async function v(e){const t=await m(e.request_dir);if(t===null)return null;if(t.kind==="page"||t.kind==="canceled")return t;throw new Error(`page request has an invalid terminal result: ${e.request_id}`)}r(v,"result");async function O(e){if(e.kind!=="page")throw new Error(`request is not a page: ${e.request_id}`);const t=f(e),i=p(t),o=n(l(t,i.dialect),"utf8"),c=i.dialect==="jsx"?n(a(t),"utf8"):o,d=e.settleable?await g(e.request_dir):null;return{ticket_id:e.inbox_ticket_id,kind:"page",state:_(e),revision:t,page:b(i),document:c,source_document:o,document_media_type:i.dialect==="jsx"?"text/jsx":"text/html",progress:d===null?null:{responses:d},result:await v(e),feedback:k(await u(e.request_dir))}}r(O,"inboxPageProjection");export{O as inboxPageProjection};
1
+ var c=Object.defineProperty;var r=(e,t)=>c(e,"name",{value:t,configurable:!0});import{readPageFeedback as s}from"../../../core/human/feedback.js";import{pageModulePath as u,pagePath as l}from"../../../core/human/convention.js";import{readPageManifest as p}from"../../../core/human/page.js";import{readRequestText as n}from"../../../core/human/request-files.js";import{presentationDir as f}from"../../../core/human/requests.js";import{readPageProgress as g,readTicketResult as m}from"../../../core/human/tickets.js";import{toPageFeedbackDTO as k}from"../map.js";function b(e){const t={kind:e.kind,step:e.step,config:e.config};return e.id!==void 0&&(t.id=e.id),e.unvalidated===!0&&(t.unvalidated=!0),e.display===!0&&(t.display=!0),t}r(b,"pageSlotToDTO");function w(e){const t={schema:"crtr.page/v2",dialect:e.dialect,title:e.title,delivery:e.delivery,document:e.document,steps:e.steps,slots:e.slots.map(b)};if(e.subtitle!==void 0&&(t.subtitle=e.subtitle),e.source!==void 0){const i={};e.source.sessionName!==void 0&&(i.sessionName=e.source.sessionName),e.source.askedBy!==void 0&&(i.askedBy=e.source.askedBy),e.source.emittedAt!==void 0&&(i.emittedAt=e.source.emittedAt),e.source.profileName!==void 0&&(i.profileName=e.source.profileName),e.source.nodeId!==void 0&&(i.nodeId=e.source.nodeId),Object.keys(i).length>0&&(t.source=i)}return t}r(w,"pageManifestToDTO");function y(e){return e.settleable?e.state!=="settled"?"pending":e.outcome==="canceled"||e.outcome==="dismissed"?"canceled":"resolved":"passive"}r(y,"state");async function _(e){const t=await m(e.request_dir);if(t===null)return null;if(t.kind==="page"||t.kind==="canceled")return t;throw new Error(`page request has an invalid terminal result: ${e.request_id}`)}r(_,"result");async function O(e){if(e.kind!=="page")throw new Error(`request is not a page: ${e.request_id}`);const t=f(e),i=await p(t),o=await n(l(t,i.dialect)),a=i.dialect==="jsx"?await n(u(t)):o,d=e.settleable?await g(e.request_dir):null;return{ticket_id:e.inbox_ticket_id,kind:"page",state:y(e),revision:t,page:w(i),document:a,source_document:o,document_media_type:i.dialect==="jsx"?"text/jsx":"text/html",progress:d===null?null:{responses:d},result:await _(e),feedback:k(await s(e.request_dir))}}r(O,"inboxPageProjection");export{O as inboxPageProjection};
@@ -1 +1 @@
1
- var T=Object.defineProperty;var o=(e,i)=>T(e,"name",{value:i,configurable:!0});import{ApiError as t}from"../../../api/index.js";import{inboxPageProjection as A}from"./inbox-page.js";import{getRequestByInboxTicket as p}from"../../../core/human/requests.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{actsAsPerson as g,canReadNode as y}from"../router.js";import{parsePage as I}from"../../../core/human/page.js";import{scanInbox as P,scanPageHistory as E}from"../../../core/human/scan.js";import{readPageTicket as h,readTicketResult as k,selectedPageDir as x,validatePageTicketResponses as R,writePageProgress as S}from"../../../core/human/tickets.js";import{describePageAnswer as O}from"../../../core/human/answer.js";import{cancelHumanTicket as N,resolvePageTicket as C}from"../../human/finish.js";import{filterAsync as v}from"../../../core/async-array.js";const D="Canceled from the inbox.",j=1e3,G=/^[a-f0-9]{64}$/;async function c(e){const i=await p(e);return i===null?null:{ticketId:i.request_id,dir:i.request_dir,kind:i.kind}}o(c,"resolveTicket");function l(e){const i=e.ticket_id;if(i===void 0||!G.test(i))throw new t(400,"invalid_request","invalid ticket id");return i}o(l,"requireTicketId");function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(u,"isPlainObject");function f(e,i){for(const n of Object.keys(e))if(!i.includes(n))throw new t(400,"invalid_request",`unexpected field: ${n}`)}o(f,"requireNoExtraKeys");function L(e){if(!u(e))throw new t(400,"invalid_request","respond body is required");if(f(e,["responses"]),!u(e.responses))throw new t(400,"invalid_request","`responses` must be an object");return{responses:e.responses}}o(L,"validateRespondRequest");function H(e){if(!u(e))throw new t(400,"invalid_request","progress body is required");if(f(e,["responses"]),!u(e.responses))throw new t(400,"invalid_request","`responses` must be an object");return{responses:e.responses}}o(H,"validateProgressRequest");function K(e){if(e===void 0)return{};if(!u(e))throw new t(400,"invalid_request","cancel body must be an object");if(f(e,["reason"]),e.reason===void 0)return{};const i=e.reason;if(typeof i!="string")throw new t(400,"invalid_request","`reason` must be a string");if(i.trim()===""||i.length>j)throw new t(400,"invalid_request","`reason` must be nonempty after trim and at most 1000 characters");return{reason:i}}o(K,"validateCancelRequest");const $=[/^slot ".*?" /,/^response names unknown or non-response-bearing slot id/,/^final page response is missing response-bearing slot id/];function _(e){return e instanceof Error&&$.some(i=>i.test(e.message))}o(_,"isSlotValidationError");function q(e){const i={request_id:e.id,request_dir:e.dir,ticket_id:e.inboxTicketId,kind:"page",title:e.title,placement:e.placement,dialect:e.dialect,steps:e.steps,emitted_at:e.emittedAt,source:e.source,slot_kinds:e.slotKinds,inbox:e.inbox,awaits_response:e.awaitsResponse,state:e.state};return e.subtitle!==""&&(i.subtitle=e.subtitle),e.answerDigest!==void 0&&(i.answer_digest=e.answerDigest),e.recommendedOptions.length>0&&(i.recommended_options=e.recommendedOptions.map(n=>({slot_id:n.slotId,option_id:n.optionId,label:n.label}))),e.fastAction!==void 0&&(i.fast_action={kind:e.fastAction.kind,responses:e.fastAction.responses}),i}o(q,"pageSummaryToDTO");async function V(e){return{status:200,body:{tickets:(await v(await P(),async n=>{if(g(e.principal))return!0;const s=await p(n.inboxTicketId),r=s?.requester_node_id?await b(s.requester_node_id):null;return r!==null&&await y(e.principal,r.grantee??"app:terminal",r.node_id)})).map(n=>{if(n.kind==="page")return q(n);if(n.kind==="review")return{request_id:n.id,request_dir:n.dir,ticket_id:n.inboxTicketId,kind:"review",title:n.title,subtitle:n.subtitle,blocked_since:n.blockedSince,source:n.source};throw new Error(`unknown ticket kind: ${n.kind}`)}).sort((n,s)=>{const r=o(d=>d.kind==="page"?d.emitted_at:d.blocked_since,"pageTime"),a=Date.parse(r(s))-Date.parse(r(n));return a!==0?a:n.ticket_id.localeCompare(s.ticket_id)})}}}o(V,"handleList");const z=new Set(["pending","resolved","canceled","passive"]);async function B(e){const i=e.query.get("node_id");if(i==="")throw new t(400,"invalid_request","node_id must be nonempty when present");const n=e.query.get("state");if(n!==null&&!z.has(n))throw new t(400,"invalid_request","state must be one of pending, resolved, canceled, passive");const s=(await E(i??void 0)).filter(d=>n===null||d.state===n);return{status:200,body:{tickets:(await v(s,async d=>{if(g(e.principal))return!0;const m=await p(d.inboxTicketId),w=m?.requester_node_id?await b(m.requester_node_id):null;return w!==null&&await y(e.principal,w.grantee??"app:terminal",w.node_id)})).map(q)}}}o(B,"handleHistory");async function F(e){const i=l(e.params),n=await c(i);if(n===null)throw new t(404,"ticket_not_found","no matching ticket");if(n.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets have no page read operation");const s=await p(i);if(s===null||s.kind!=="page")throw new t(404,"ticket_not_found","no matching ticket");try{return{status:200,body:await A(s)}}catch{throw new t(500,"internal","the ticket could not be read")}}o(F,"handleGetPage");async function M(e){const i=l(e.params),n=L(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets submit through their own path");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");const r=await k(s.dir);if(r!==null&&r.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");if(r?.kind==="canceled")throw new t(409,"ticket_canceled","canceled tickets cannot be answered");try{await R(s.dir,n.responses)}catch(a){throw _(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","validation failed")}try{const a=await C(s.ticketId,n.responses);if(a.kind!=="page")throw new t(409,"ticket_kind_mismatch","unexpected result kind");return{status:200,body:a}}catch(a){throw a instanceof t?a:_(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","the ticket could not be finalized")}}o(M,"handleRespond");async function U(e){const i=l(e.params),n=H(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets do not support progress autosave");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");const r=await k(s.dir);if(r!==null&&r.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");try{return await S(s.dir,n.responses),{status:200,body:{}}}catch(a){throw _(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","progress could not be saved")}}o(U,"handleProgress");async function X(e){const i=l(e.params),n=K(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets have their own cancel path");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");try{const r=await N(s.ticketId,{reason:n.reason??D,actor:"human",disposition:"dismissed"});if(r.outcome==="already_settled"||r.result.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");return{status:200,body:r.result}}catch(r){throw r instanceof t?r:new t(500,"internal","the ticket could not be canceled")}}o(X,"handleCancel");async function J(e){const i=l(e.params),n=await c(i);if(n===null)throw new t(404,"ticket_not_found","no matching ticket");if(n.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets fetch responses through their own path");const s=await k(n.dir);if(s===null)throw new t(404,"ticket_pending","ticket has no response yet");if(s.kind==="canceled")throw new t(404,"ticket_canceled","canceled tickets have no response");if(s.kind!=="page")throw new t(409,"ticket_kind_mismatch","unexpected result kind");try{const r=I(await x(n.dir));return{status:200,body:{...s,answer:O(r,s.responses,{completedAt:s.completedAt})}}}catch{throw new t(500,"internal","the ticket answer could not be read")}}o(J,"handleGetResponse");const de=[{method:"GET",pattern:"/v1/human/inbox",handler:V},{method:"GET",pattern:"/v1/human/inbox/history",handler:B},{method:"GET",pattern:"/v1/human/inbox/:ticket_id",handler:F},{method:"GET",pattern:"/v1/human/inbox/:ticket_id/response",handler:J},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/respond",handler:M},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/progress",handler:U},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/cancel",handler:X}];export{de as inboxRoutes,l as requireTicketId,c as resolveTicket};
1
+ var T=Object.defineProperty;var o=(e,i)=>T(e,"name",{value:i,configurable:!0});import{ApiError as t}from"../../../api/index.js";import{inboxPageProjection as A}from"./inbox-page.js";import{getRequestByInboxTicket as p}from"../../../core/human/requests.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{actsAsPerson as g,canReadNode as y}from"../router.js";import{readPageManifest as I}from"../../../core/human/page.js";import{scanInbox as P,scanPageHistory as E}from"../../../core/human/scan.js";import{readPageTicket as h,readTicketResult as k,selectedPageDir as x,validatePageTicketResponses as R,writePageProgress as S}from"../../../core/human/tickets.js";import{describePageAnswer as O}from"../../../core/human/answer.js";import{cancelHumanTicket as N,resolvePageTicket as C}from"../../human/finish.js";import{filterAsync as v}from"../../../core/async-array.js";const D="Canceled from the inbox.",j=1e3,G=/^[a-f0-9]{64}$/;async function c(e){const i=await p(e);return i===null?null:{ticketId:i.request_id,dir:i.request_dir,kind:i.kind}}o(c,"resolveTicket");function l(e){const i=e.ticket_id;if(i===void 0||!G.test(i))throw new t(400,"invalid_request","invalid ticket id");return i}o(l,"requireTicketId");function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(u,"isPlainObject");function f(e,i){for(const n of Object.keys(e))if(!i.includes(n))throw new t(400,"invalid_request",`unexpected field: ${n}`)}o(f,"requireNoExtraKeys");function L(e){if(!u(e))throw new t(400,"invalid_request","respond body is required");if(f(e,["responses"]),!u(e.responses))throw new t(400,"invalid_request","`responses` must be an object");return{responses:e.responses}}o(L,"validateRespondRequest");function H(e){if(!u(e))throw new t(400,"invalid_request","progress body is required");if(f(e,["responses"]),!u(e.responses))throw new t(400,"invalid_request","`responses` must be an object");return{responses:e.responses}}o(H,"validateProgressRequest");function K(e){if(e===void 0)return{};if(!u(e))throw new t(400,"invalid_request","cancel body must be an object");if(f(e,["reason"]),e.reason===void 0)return{};const i=e.reason;if(typeof i!="string")throw new t(400,"invalid_request","`reason` must be a string");if(i.trim()===""||i.length>j)throw new t(400,"invalid_request","`reason` must be nonempty after trim and at most 1000 characters");return{reason:i}}o(K,"validateCancelRequest");const $=[/^slot ".*?" /,/^response names unknown or non-response-bearing slot id/,/^final page response is missing response-bearing slot id/];function _(e){return e instanceof Error&&$.some(i=>i.test(e.message))}o(_,"isSlotValidationError");function q(e){const i={request_id:e.id,request_dir:e.dir,ticket_id:e.inboxTicketId,kind:"page",title:e.title,placement:e.placement,dialect:e.dialect,steps:e.steps,emitted_at:e.emittedAt,source:e.source,slot_kinds:e.slotKinds,inbox:e.inbox,awaits_response:e.awaitsResponse,state:e.state};return e.subtitle!==""&&(i.subtitle=e.subtitle),e.answerDigest!==void 0&&(i.answer_digest=e.answerDigest),e.recommendedOptions.length>0&&(i.recommended_options=e.recommendedOptions.map(n=>({slot_id:n.slotId,option_id:n.optionId,label:n.label}))),e.fastAction!==void 0&&(i.fast_action={kind:e.fastAction.kind,responses:e.fastAction.responses}),i}o(q,"pageSummaryToDTO");async function M(e){return{status:200,body:{tickets:(await v(await P(),async n=>{if(g(e.principal))return!0;const s=await p(n.inboxTicketId),r=s?.requester_node_id?await b(s.requester_node_id):null;return r!==null&&await y(e.principal,r.grantee??"app:terminal",r.node_id)})).map(n=>{if(n.kind==="page")return q(n);if(n.kind==="review")return{request_id:n.id,request_dir:n.dir,ticket_id:n.inboxTicketId,kind:"review",title:n.title,subtitle:n.subtitle,blocked_since:n.blockedSince,source:n.source};throw new Error(`unknown ticket kind: ${n.kind}`)}).sort((n,s)=>{const r=o(d=>d.kind==="page"?d.emitted_at:d.blocked_since,"pageTime"),a=Date.parse(r(s))-Date.parse(r(n));return a!==0?a:n.ticket_id.localeCompare(s.ticket_id)})}}}o(M,"handleList");const V=new Set(["pending","resolved","canceled","passive"]);async function z(e){const i=e.query.get("node_id");if(i==="")throw new t(400,"invalid_request","node_id must be nonempty when present");const n=e.query.get("state");if(n!==null&&!V.has(n))throw new t(400,"invalid_request","state must be one of pending, resolved, canceled, passive");const s=(await E(i??void 0)).filter(d=>n===null||d.state===n);return{status:200,body:{tickets:(await v(s,async d=>{if(g(e.principal))return!0;const m=await p(d.inboxTicketId),w=m?.requester_node_id?await b(m.requester_node_id):null;return w!==null&&await y(e.principal,w.grantee??"app:terminal",w.node_id)})).map(q)}}}o(z,"handleHistory");async function B(e){const i=l(e.params),n=await c(i);if(n===null)throw new t(404,"ticket_not_found","no matching ticket");if(n.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets have no page read operation");const s=await p(i);if(s===null||s.kind!=="page")throw new t(404,"ticket_not_found","no matching ticket");try{return{status:200,body:await A(s)}}catch{throw new t(500,"internal","the ticket could not be read")}}o(B,"handleGetPage");async function F(e){const i=l(e.params),n=L(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets submit through their own path");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");const r=await k(s.dir);if(r!==null&&r.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");if(r?.kind==="canceled")throw new t(409,"ticket_canceled","canceled tickets cannot be answered");try{await R(s.dir,n.responses)}catch(a){throw _(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","validation failed")}try{const a=await C(s.ticketId,n.responses);if(a.kind!=="page")throw new t(409,"ticket_kind_mismatch","unexpected result kind");return{status:200,body:a}}catch(a){throw a instanceof t?a:_(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","the ticket could not be finalized")}}o(F,"handleRespond");async function U(e){const i=l(e.params),n=H(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets do not support progress autosave");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");const r=await k(s.dir);if(r!==null&&r.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");try{return await S(s.dir,n.responses),{status:200,body:{}}}catch(a){throw _(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","progress could not be saved")}}o(U,"handleProgress");async function X(e){const i=l(e.params),n=K(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets have their own cancel path");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");try{const r=await N(s.ticketId,{reason:n.reason??D,actor:"human",disposition:"dismissed"});if(r.outcome==="already_settled"||r.result.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");return{status:200,body:r.result}}catch(r){throw r instanceof t?r:new t(500,"internal","the ticket could not be canceled")}}o(X,"handleCancel");async function J(e){const i=l(e.params),n=await c(i);if(n===null)throw new t(404,"ticket_not_found","no matching ticket");if(n.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets fetch responses through their own path");const s=await k(n.dir);if(s===null)throw new t(404,"ticket_pending","ticket has no response yet");if(s.kind==="canceled")throw new t(404,"ticket_canceled","canceled tickets have no response");if(s.kind!=="page")throw new t(409,"ticket_kind_mismatch","unexpected result kind");try{const r=await I(await x(n.dir));return{status:200,body:{...s,answer:O(r,s.responses,{completedAt:s.completedAt})}}}catch{throw new t(500,"internal","the ticket answer could not be read")}}o(J,"handleGetResponse");const de=[{method:"GET",pattern:"/v1/human/inbox",handler:M},{method:"GET",pattern:"/v1/human/inbox/history",handler:z},{method:"GET",pattern:"/v1/human/inbox/:ticket_id",handler:B},{method:"GET",pattern:"/v1/human/inbox/:ticket_id/response",handler:J},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/respond",handler:F},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/progress",handler:U},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/cancel",handler:X}];export{de as inboxRoutes,l as requireTicketId,c as resolveTicket};
@@ -1,9 +1,9 @@
1
- var b=Object.defineProperty;var r=(e,t)=>b(e,"name",{value:t,configurable:!0});import{createHash as x}from"node:crypto";import{withCanvasWrite as g}from"../../../core/canvas/db.js";import{canRead as T}from"../../../core/graph/access.js";import{permits as k}from"../../../core/permissions/evaluator.js";import{bodyOf as M}from"../../../core/graph/bodies.js";import{ownerForGrantee as C}from"../../../core/graph/objects.js";import{readerFor as F}from"../../../core/graph/reader.js";import{checkoutsForCwd as H}from"../../../core/graph/repo-sync/repos.js";import{RunError as h}from"../../../core/runs/operations.js";import{mapAsync as j}from"../../../core/async-array.js";import{personId as B}from"../../../core/canvas/person.js";const ut=new Set(["memory.list","memory.get"]),W=1024*1024,w=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/,N=["knowledge","preference"],s=r((e,t)=>new h(400,"invalid_request",e,t),"invalid"),O=r(()=>new h(404,"not_found","no memory document by that name"),"notFound");function _(e){const t=e.get("store");if(t===null)throw s("store is required","store");if(t!=="user")throw s('store must be "user"',"store")}r(_,"store");function J(e){const t=e.get("kind");if(t===null)return null;if(!N.includes(t))throw s("kind must be knowledge or preference","kind");return t}r(J,"kindOf");function Y(e){const t=e.get("limit");if(t===null)return 50;if(!/^(0|[1-9]\d*)$/.test(t)||Number(t)<1||Number(t)>100)throw s("limit must be an integer from 1 to 100","limit");return Number(t)}r(Y,"limitOf");function U(e,t){return x("sha256").update(JSON.stringify(["user",e,t])).digest("base64url").slice(0,16)}r(U,"filterHash");function $(e,t){return Buffer.from(JSON.stringify({n:e,f:t}),"utf8").toString("base64url")}r($,"encodeCursor");function z(e,t){if(e===null)return null;try{if(!/^[A-Za-z0-9_-]+$/.test(e))throw new Error("not base64url");const n=JSON.parse(Buffer.from(e,"base64url").toString("utf8"));if(typeof n.n=="string"&&w.test(n.n)&&n.f===t)return n.n}catch{}throw s("cursor is not the next_cursor of a previous page with the same store, path and kind","cursor")}r(z,"decodeCursor");async function R(e){return await C(e,B())}r(R,"personObject");async function v(e,t){return await F(e,t,async n=>await H(e,n))}r(v,"graphReader");async function S(e,t){const n=await e.prepare(`SELECT r.body_hash, r.created_at FROM revisions r WHERE r.document = ?
1
+ var x=Object.defineProperty;var r=(e,t)=>x(e,"name",{value:t,configurable:!0});import{createHash as T}from"node:crypto";import{withCanvasWrite as g}from"../../../core/canvas/db.js";import{canRead as k}from"../../../core/graph/access.js";import{permits as M}from"../../../core/permissions/evaluator.js";import{get as b}from"../../../core/graph/bodies.js";import{ownerForGrantee as C}from"../../../core/graph/objects.js";import{readerFor as F}from"../../../core/graph/reader.js";import{checkoutsForCwd as H}from"../../../core/graph/repo-sync/repos.js";import{RunError as h}from"../../../core/runs/operations.js";import{mapAsync as j}from"../../../core/async-array.js";import{personId as B}from"../../../core/canvas/person.js";const ut=new Set(["memory.list","memory.get"]),W=1024*1024,w=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/,N=["knowledge","preference"],i=r((e,t)=>new h(400,"invalid_request",e,t),"invalid"),O=r(()=>new h(404,"not_found","no memory document by that name"),"notFound");function _(e){const t=e.get("store");if(t===null)throw i("store is required","store");if(t!=="user")throw i('store must be "user"',"store")}r(_,"store");function J(e){const t=e.get("kind");if(t===null)return null;if(!N.includes(t))throw i("kind must be knowledge or preference","kind");return t}r(J,"kindOf");function Y(e){const t=e.get("limit");if(t===null)return 50;if(!/^(0|[1-9]\d*)$/.test(t)||Number(t)<1||Number(t)>100)throw i("limit must be an integer from 1 to 100","limit");return Number(t)}r(Y,"limitOf");function U(e,t){return T("sha256").update(JSON.stringify(["user",e,t])).digest("base64url").slice(0,16)}r(U,"filterHash");function $(e,t){return Buffer.from(JSON.stringify({n:e,f:t}),"utf8").toString("base64url")}r($,"encodeCursor");function z(e,t){if(e===null)return null;try{if(!/^[A-Za-z0-9_-]+$/.test(e))throw new Error("not base64url");const n=JSON.parse(Buffer.from(e,"base64url").toString("utf8"));if(typeof n.n=="string"&&w.test(n.n)&&n.f===t)return n.n}catch{}throw i("cursor is not the next_cursor of a previous page with the same store, path and kind","cursor")}r(z,"decodeCursor");async function R(e){return await C(e,B())}r(R,"personObject");async function v(e,t){return await F(e,t,async n=>await H(e,n))}r(v,"graphReader");async function S(e,t){const n=await e.prepare(`SELECT r.body_hash, r.created_at FROM revisions r WHERE r.document = ?
2
2
  AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rev_id) ORDER BY r.number DESC, r.rev_id LIMIT 1`).get(t.id),o=await e.prepare("SELECT created_at, author FROM revisions WHERE document = ? ORDER BY number, created_at LIMIT 1").get(t.id);if(!n||!o)throw O();return{head:n,first:o}}r(S,"revisions");async function A(e,t){const{head:n,first:o}=await S(e,t),a=t.name.lastIndexOf("/");return{name:t.name,folder:a<0?"":t.name.slice(0,a),kind:t.kind,preview:t.preview,summary:t.summary,privacy:t.privacy,revision:t.revision,created_at:o.created_at,updated_at:n.created_at}}r(A,"item");async function G(e,t){return t===null?null:(await e.prepare(`SELECT i.external_id FROM objects a LEFT JOIN nodes n ON n.node_id = a.id
3
3
  JOIN identities i ON i.object = CASE WHEN a.type = 'node' THEN n.app ELSE a.id END AND i.scheme = 'app'
4
- WHERE a.id = ?`).get(t))?.external_id??null}r(G,"granteeOf");function Z(e,t){if(e.length<=t)return e;let n=t;for(;n>0&&(e[n]&192)===128;)n--;return e.subarray(0,n)}r(Z,"utf8Prefix");async function P(e,t){_(t);const n=t.get("path");if(n!==null&&!w.test(n))throw s("path must be name segments of letters, digits, `_` or `-` joined by `/`","path");const o=J(t),a=Y(t),d=U(n,o),i=z(t.get("cursor"),d);return await g(async u=>{const l=await v(u,e),c=await R(u),m=n===null?null:`${n}/`,f=o===null?N:[o],y=await k(u,l,"read"),E=await u.prepareUncached(`SELECT o.*, d.summary, d.revision, d.source_path FROM objects o JOIN documents d ON d.object = o.id
4
+ WHERE a.id = ?`).get(t))?.external_id??null}r(G,"granteeOf");function Z(e,t){if(e.length<=t)return e;let n=t;for(;n>0&&(e[n]&192)===128;)n--;return e.subarray(0,n)}r(Z,"utf8Prefix");async function P(e,t){_(t);const n=t.get("path");if(n!==null&&!w.test(n))throw i("path must be name segments of letters, digits, `_` or `-` joined by `/`","path");const o=J(t),a=Y(t),d=U(n,o),s=z(t.get("cursor"),d);return await g(async u=>{const l=await v(u,e),c=await R(u),m=n===null?null:`${n}/`,f=o===null?N:[o],y=await M(u,l,"read"),E=await u.prepareUncached(`SELECT o.*, d.summary, d.revision, d.source_path FROM objects o JOIN documents d ON d.object = o.id
5
5
  WHERE o.owner = ? AND o.type = 'document' AND o.deleted_at IS NULL AND d.unlisted = 0
6
6
  AND o.kind IN (?, ?) AND (?::text IS NULL OR starts_with(o.name, ?::text)) AND (?::text IS NULL OR o.name > ?::text)
7
7
  AND ${y.sql}
8
- ORDER BY o.name LIMIT ?`).all(c.id,f[0],f[f.length-1],m,m,i,i,...y.params,a+1),p=E.slice(0,a),D=E.length>a;return{items:await j(p,async L=>await A(u,L)),next_cursor:D?$(p[p.length-1].name,d):null}})}r(P,"memoryList");async function X(e,t){_(t);const n=t.get("name");if(n===null)throw s("name is required","name");if(!w.test(n))throw s("name must be segments of letters, digits, `_` or `-` joined by `/`","name");return await g(async o=>{const a=await v(o,e),d=await R(o),i=await o.prepare(`SELECT o.*, d.summary, d.revision, d.source_path FROM objects o JOIN documents d ON d.object = o.id
9
- WHERE o.owner = ? AND o.type = 'document' AND o.name = ? AND o.deleted_at IS NULL AND o.kind IN ('knowledge', 'preference')`).get(d.id,n);if(!i||!await T(o,a,i))throw O();const{head:u,first:l}=await S(o,i),c=Buffer.from(M(u.body_hash,i.source_path),"utf8"),m=Z(c,W);return{...await A(o,i),body:m.toString("utf8"),size:c.length,truncated:m.length<c.length,grantee:await G(o,l.author)}})}r(X,"memoryGet");async function I(e,t,n){if(e==="memory.list")return{status:200,body:await P(t,n)};if(e==="memory.get")return{status:200,body:await X(t,n)};throw new h(404,"not_found","unknown operation")}r(I,"handleMemoryOperation");const ct=[{method:"GET",pattern:"/v1/memory",handler:r(async e=>await I("memory.list",e.principal,e.query),"handler")},{method:"GET",pattern:"/v1/memory/document",handler:r(async e=>await I("memory.get",e.principal,e.query),"handler")}];export{W as MEMORY_BODY_MAX_BYTES,ut as MEMORY_OPERATIONS,I as handleMemoryOperation,X as memoryGet,P as memoryList,ct as memoryRoutes,Z as utf8Prefix};
8
+ ORDER BY o.name LIMIT ?`).all(c.id,f[0],f[f.length-1],m,m,s,s,...y.params,a+1),p=E.slice(0,a),D=E.length>a;return{items:await j(p,async L=>await A(u,L)),next_cursor:D?$(p[p.length-1].name,d):null}})}r(P,"memoryList");async function X(e,t){_(t);const n=t.get("name");if(n===null)throw i("name is required","name");if(!w.test(n))throw i("name must be segments of letters, digits, `_` or `-` joined by `/`","name");return await g(async o=>{const a=await v(o,e),d=await R(o),s=await o.prepare(`SELECT o.*, d.summary, d.revision, d.source_path FROM objects o JOIN documents d ON d.object = o.id
9
+ WHERE o.owner = ? AND o.type = 'document' AND o.name = ? AND o.deleted_at IS NULL AND o.kind IN ('knowledge', 'preference')`).get(d.id,n);if(!s||!await k(o,a,s))throw O();const{head:u,first:l}=await S(o,s),c=Buffer.from(await b(o,u.body_hash),"utf8"),m=Z(c,W);return{...await A(o,s),body:m.toString("utf8"),size:c.length,truncated:m.length<c.length,grantee:await G(o,l.author)}})}r(X,"memoryGet");async function I(e,t,n){if(e==="memory.list")return{status:200,body:await P(t,n)};if(e==="memory.get")return{status:200,body:await X(t,n)};throw new h(404,"not_found","unknown operation")}r(I,"handleMemoryOperation");const ct=[{method:"GET",pattern:"/v1/memory",handler:r(async e=>await I("memory.list",e.principal,e.query),"handler")},{method:"GET",pattern:"/v1/memory/document",handler:r(async e=>await I("memory.get",e.principal,e.query),"handler")}];export{W as MEMORY_BODY_MAX_BYTES,ut as MEMORY_OPERATIONS,I as handleMemoryOperation,X as memoryGet,P as memoryList,ct as memoryRoutes,Z as utf8Prefix};
@@ -1,14 +1,14 @@
1
- var P=Object.defineProperty;var a=(t,e)=>P(t,"name",{value:e,configurable:!0});import{existsSync as D}from"node:fs";import{getNode as d}from"../../../core/canvas/canvas.js";import{liveExecutionForNode as B}from"../../../core/canvas/executions.js";import{markStopSignal as M}from"../../../core/runtime/stop-signals.js";import{emitEvent as j}from"../../../core/events/emit.js";import{push as J,FinalizationError as k,StructuredRequestFinalizationError as z}from"../../../core/feed/feed.js";import{branchIsPushedAtCurrentTip as L,hasOpenManagedWorktree as W,missingWorktreeRecoveryHint as H}from"../../../core/worktree.js";import{autoDropCleanManagedWorktreeAsync as X}from"../../../core/worktree-mutation-async.js";import{withCanvasWrite as G,openDb as K}from"../../../core/canvas/db.js";import{get as U}from"../../../core/graph/bodies.js";import{heads as V}from"../../../core/graph/documents.js";import{listObjects as Y,nodeObject as Q}from"../../../core/graph/objects.js";import{readerFor as Z}from"../../../core/graph/reader.js";import{notFound as ee,usage as l}from"../../../core/errors.js";import{InputError as u}from"../../../core/io.js";import{toReportResultDTO as te}from"../map.js";import{validateStructuredResult as re}from"../../../core/runtime/structured-output.js";import{clearSchemaReprompts as ne}from"../../../core/runtime/output-result.js";import{getPendingStructuredRequestForNode as _,getStructuredRequest as S,settleStructuredRequestIn as ie}from"../../../core/canvas/structured-requests.js";import{ApiError as h}from"../../../api/errors.js";import{applySettlementEffects as R}from"../../structured-requests/effects.js";import{nodeEventStreams as x}from"./node-events.js";import{emitRunEvent as E}from"../../../core/runs/events.js";import{mapAsync as oe}from"../../../core/async-array.js";async function g(t){const e=await d(t);if(e===null)throw ee(`unknown node: ${t}`,{received:t});return e}a(g,"requireNode");function ae(t){if(typeof t!="object"||t===null)throw l("a push report requires a JSON body { tier, body }");const e=t.tier,i=t.delivery_tier,r=t.body;if(e!=="update"&&e!=="urgent"&&e!=="final")throw l(`invalid report tier: ${String(e)} (expected update|urgent|final)`);if(i!==void 0&&i!=="deferred"&&i!=="normal"&&i!=="urgent")throw l(`invalid delivery_tier: ${String(i)} (expected deferred|normal|urgent)`);if(e!=="update"&&i!==void 0)throw l("delivery_tier is only valid for update reports");if(typeof r!="string"||r.trim()==="")throw l("a push report requires a non-empty body");return{tier:e,...i===void 0?{}:{delivery_tier:i},body:r}}a(ae,"parsePushBody");async function se(t){const e=t.params.id;await g(e);const i=ae(t.body);if(i.tier==="final"&&await _(e)!==null)throw new u({error:"structured_result_pending",message:"This node owes a structured result \u2014 a plain `push final` would finish it without recording one.",next:'Submit the result with the `submit_result` tool instead (or `crtr push result`); it validates against the pending schema, records it, and pushes the final report for you. If the schema cannot be honestly satisfied, call `submit_result` with `decline` (or `crtr push result --decline "<reason>" --code <token>`) to record the decline and finish the node.'});const r=i.tier==="final"?(await d(e))?.status:void 0,n=i.tier==="final"?await O(e):void 0,o=await b(e,i);i.tier==="final"&&await A(e);const s=i.tier==="final"&&r!==void 0?{from:r,to:(await d(e))?.status??"done"}:void 0,f=te(o,s,n),m=(await q(e)).find(p=>p.id===o.reportId);if(m!==void 0){x.report(e,m);const p=(await d(e))?.run_id;p&&await E(p,e,"node.report.pushed",{body:m.body,kind:m.tier==="final"?"final":"update"})}return{status:200,body:f}}a(se,"handlePush");async function O(t){if(!W(await d(t)))return;const e=await X(t);if(e!==null)return e.worktree_path;if(!await L((await d(t)).managed_worktree))throw new u({error:"open_managed_worktree",message:"This node still owns an open managed worktree that is neither clean and locally landed nor clean with its exact tip on origin \u2014 `push final` cannot finish.",next:await(async()=>{const i=(await d(t))?.managed_worktree;return i!=null&&i.state==="open"&&!D(i.path)?H(i):"Make the recorded worktree clean, including untracked files: commit work to retain and remove or ignore only disposable generated files. With its recorded branch checked out and no rebase in progress, push its current tip to a branch on origin, then rerun `crtr push final`; or run `crtr node worktree close` to land the clean worktree into its recorded local base."})()})}a(O,"guardFinalWorktree");const $=2e3;function de(t){const e=JSON.stringify(t,null,2);return`Structured output submitted.
1
+ var M=Object.defineProperty;var s=(t,e)=>M(t,"name",{value:e,configurable:!0});import{existsSync as P}from"node:fs";import{getNode as d}from"../../../core/canvas/canvas.js";import{liveExecutionForNode as D}from"../../../core/canvas/executions.js";import{markStopSignal as B}from"../../../core/runtime/stop-signals.js";import{emitEvent as j}from"../../../core/events/emit.js";import{push as J,FinalizationError as k,StructuredRequestFinalizationError as z}from"../../../core/feed/feed.js";import{branchIsPushedAtCurrentTip as L,hasOpenManagedWorktree as W,missingWorktreeRecoveryHint as H}from"../../../core/worktree.js";import{autoDropCleanManagedWorktreeAsync as X}from"../../../core/worktree-mutation-async.js";import{withCanvasWrite as G,openDb as K}from"../../../core/canvas/db.js";import{getMany as U}from"../../../core/graph/bodies.js";import{heads as V}from"../../../core/graph/documents.js";import{listObjects as Y,nodeObject as Q}from"../../../core/graph/objects.js";import{readerFor as Z}from"../../../core/graph/reader.js";import{notFound as ee,usage as f}from"../../../core/errors.js";import{InputError as u}from"../../../core/io.js";import{toReportResultDTO as te}from"../map.js";import{validateStructuredResult as re}from"../../../core/runtime/structured-output.js";import{clearSchemaReprompts as ne}from"../../../core/runtime/output-result.js";import{getPendingStructuredRequestForNode as _,getStructuredRequest as S,settleStructuredRequestIn as ie}from"../../../core/canvas/structured-requests.js";import{ApiError as h}from"../../../api/errors.js";import{applySettlementEffects as R}from"../../structured-requests/effects.js";import{nodeEventStreams as x}from"./node-events.js";import{emitRunEvent as E}from"../../../core/runs/events.js";import{mapAsync as oe}from"../../../core/async-array.js";async function g(t){const e=await d(t);if(e===null)throw ee(`unknown node: ${t}`,{received:t});return e}s(g,"requireNode");function ae(t){if(typeof t!="object"||t===null)throw f("a push report requires a JSON body { tier, body }");const e=t.tier,r=t.delivery_tier,i=t.body;if(e!=="update"&&e!=="urgent"&&e!=="final")throw f(`invalid report tier: ${String(e)} (expected update|urgent|final)`);if(r!==void 0&&r!=="deferred"&&r!=="normal"&&r!=="urgent")throw f(`invalid delivery_tier: ${String(r)} (expected deferred|normal|urgent)`);if(e!=="update"&&r!==void 0)throw f("delivery_tier is only valid for update reports");if(typeof i!="string"||i.trim()==="")throw f("a push report requires a non-empty body");return{tier:e,...r===void 0?{}:{delivery_tier:r},body:i}}s(ae,"parsePushBody");async function se(t){const e=t.params.id;await g(e);const r=ae(t.body);if(r.tier==="final"&&await _(e)!==null)throw new u({error:"structured_result_pending",message:"This node owes a structured result \u2014 a plain `push final` would finish it without recording one.",next:'Submit the result with the `submit_result` tool instead (or `crtr push result`); it validates against the pending schema, records it, and pushes the final report for you. If the schema cannot be honestly satisfied, call `submit_result` with `decline` (or `crtr push result --decline "<reason>" --code <token>`) to record the decline and finish the node.'});const i=r.tier==="final"?(await d(e))?.status:void 0,n=r.tier==="final"?await O(e):void 0,o=await b(e,r);r.tier==="final"&&await A(e);const a=r.tier==="final"&&i!==void 0?{from:i,to:(await d(e))?.status??"done"}:void 0,c=te(o,a,n),m=(await q(e)).find(p=>p.id===o.reportId);if(m!==void 0){x.report(e,m);const p=(await d(e))?.run_id;p&&await E(p,e,"node.report.pushed",{body:m.body,kind:m.tier==="final"?"final":"update"})}return{status:200,body:c}}s(se,"handlePush");async function O(t){if(!W(await d(t)))return;const e=await X(t);if(e!==null)return e.worktree_path;if(!await L((await d(t)).managed_worktree))throw new u({error:"open_managed_worktree",message:"This node still owns an open managed worktree that is neither clean and locally landed nor clean with its exact tip on origin \u2014 `push final` cannot finish.",next:await(async()=>{const r=(await d(t))?.managed_worktree;return r!=null&&r.state==="open"&&!P(r.path)?H(r):"Make the recorded worktree clean, including untracked files: commit work to retain and remove or ignore only disposable generated files. With its recorded branch checked out and no rebase in progress, push its current tip to a branch on origin, then rerun `crtr push final`; or run `crtr node worktree close` to land the clean worktree into its recorded local base."})()})}s(O,"guardFinalWorktree");const $=2e3;function de(t){const e=JSON.stringify(t,null,2);return`Structured output submitted.
2
2
 
3
3
  \`\`\`json
4
4
  ${e.length>$?`${e.slice(0,$)}
5
5
  \u2026 (truncated \u2014 the full result is on the structured request)`:e}
6
6
  \`\`\`
7
- `}a(de,"submitReportBody");function ue(t){return`Structured result declined.
7
+ `}s(de,"submitReportBody");function ue(t){return`Structured result declined.
8
8
 
9
9
  Reason: ${t.reason}
10
10
  Code: ${t.code}
11
11
  Retryable: ${t.retryable?"yes":"no"}
12
- `}a(ue,"declineReportBody");const I=1024,N=64;async function ce(t,e){if(typeof t!="object"||t===null)throw l("a result submission requires a JSON body { value } or { decline, code, retryable }");const i=t.request_id;if(typeof i!="string"||i.trim()===""){const o=await _(e);throw new u({error:"request_id_required",message:`a structured result requires request_id${o===null?"":` (pending request: ${o.request_id})`}`,next:"Pass `--request <request-id>` exactly as named in the structured-output instruction."})}const r=Object.hasOwn(t,"value"),n=Object.hasOwn(t,"decline");if(r&&n)throw l("a result submission carries either { value } or { decline, code, retryable }, not both");if(n){const{decline:o,code:s,retryable:f}=t;if(typeof o!="string"||o.trim()==="")throw new u({error:"missing_decline_reason",message:"A decline needs a reason.",next:'Rerun with `--decline "<one sentence on why the schema cannot be honestly satisfied>" --code <token>`.'});if(o.length>I)throw new u({error:"decline_reason_too_long",message:`A decline reason is at most ${I} characters (got ${o.length}).`,next:"Keep the reason to one plain sentence; put any detail in an artifact and name its path."});if(typeof s!="string"||s.trim()===""||/\s/.test(s.trim())||s.trim().length>N)throw new u({error:"invalid_decline_code",message:`A decline needs a code: one whitespace-free token of at most ${N} characters naming the decline class.`,next:"Rerun with `--code <token>` \u2014 the token your requester documented for this case."});if(typeof f!="boolean")throw l("a decline carries `retryable` as a boolean");return{requestId:i.trim(),declined:{reason:o.trim(),code:s.trim(),retryable:f}}}if(!r)throw l("a result submission requires a JSON body { value } or { decline, code, retryable }");return{requestId:i.trim(),value:t.value}}a(ce,"parseSubmitBody");const le=1024*1024;async function fe(t){const e=t.params.id,i=await g(e);await _(e)!==null&&await ne(e);const r=await ce(t.body,e),n=await S(r.requestId);if(n===null||n.node_id!==e)throw new h(404,"structured_request_not_found","the named structured request does not belong to this node");if(n.state!=="pending")throw new h(409,n.state==="expired"?"structured_request_expired":"structured_request_settled",`structured request ${n.request_id} is already ${n.state}`);let o;if("value"in r){if(Buffer.byteLength(JSON.stringify(r.value)??"")>le)throw new u({error:"result_too_large",message:"the submitted result is over 1 MiB serialized; nothing was recorded.",next:"Submit a smaller result \u2014 write bulky content to a file and put its path in the result."});try{o=await re(n.schema,r.value)}catch(c){throw new u({error:"result_schema_mismatch",message:`the submitted result does not match the requested schema: ${c.message}`,next:'Fix the listed fields and submit again; if the schema cannot be honestly satisfied, decline it (`submit_result` with `decline`, or `crtr push result --decline "<reason>" --code <token>`).'})}}const s="declined"in r?ue(r.declined):de(o);if(n.submission==="oneoff"){const c=await G(async C=>await ie(C,n.request_id,"declined"in r?{intended:"declined",declined:r.declined}:{intended:"completed",result:o}));if(!c.changed||c.row===null||c.row.state!=="completed"&&c.row.state!=="declined")throw new h(409,c.row?.state==="expired"?"structured_request_expired":"structured_request_settled",`structured request ${n.request_id} is already ${c.row?.state??"settled"}`);await R(c.row);const y=await b(e,{tier:"update",body:s},void 0,{requestMessageId:n.request_message_id});return await T(e,y.reportId),{status:200,body:{mode:n.submission,report:y.report,notified:[...y.deliveredTo],..."declined"in r?{declined:r.declined}:{}}}}const f=(await d(e))?.status,m=await O(e),p="declined"in r?{kind:"failure",reason:"declined",detailJson:JSON.stringify({schema:"crtr.node-outcome-detail/v1",message:r.declined.reason,declined:r.declined}),structured:{requestId:n.request_id,declined:r.declined,skipEffects:!0}}:{structured:{requestId:n.request_id,value:o,skipEffects:!0}},w=await b(e,{tier:"final",body:s},p);await A(e);const F=await S(n.request_id);await R(F),await T(e,w.reportId);const v=f===void 0?void 0:{from:f,to:(await d(e))?.status??"done"};return{status:200,body:{mode:n.submission,report:w.report,notified:[...w.deliveredTo],..."declined"in r?{declined:r.declined}:{},...v===void 0?{}:{transitioned:v},...m===void 0?{}:{worktree_auto_dropped:!0,worktree_auto_dropped_path:m}}}}a(fe,"handleSubmitResult");async function T(t,e){const i=(await q(t)).find(n=>n.id===e);if(i===void 0)return;x.report(t,i);const r=(await d(t))?.run_id;r&&await E(r,t,"node.report.pushed",{body:i.body,kind:i.tier==="final"?"final":"update"})}a(T,"emitReportEvents");async function A(t){try{const e=await B(t);e!==null&&M(t,"pushedFinal",e.executionId)}catch(e){try{j({event:"daemon.node.final-marker.failed",level:"error",node_id:t,error:e})||process.stderr.write(`final marker failed for ${t}: ${String(e)}
12
+ `}s(ue,"declineReportBody");const I=1024,N=64;async function ce(t,e){if(typeof t!="object"||t===null)throw f("a result submission requires a JSON body { value } or { decline, code, retryable }");const r=t.request_id;if(typeof r!="string"||r.trim()===""){const o=await _(e);throw new u({error:"request_id_required",message:`a structured result requires request_id${o===null?"":` (pending request: ${o.request_id})`}`,next:"Pass `--request <request-id>` exactly as named in the structured-output instruction."})}const i=Object.hasOwn(t,"value"),n=Object.hasOwn(t,"decline");if(i&&n)throw f("a result submission carries either { value } or { decline, code, retryable }, not both");if(n){const{decline:o,code:a,retryable:c}=t;if(typeof o!="string"||o.trim()==="")throw new u({error:"missing_decline_reason",message:"A decline needs a reason.",next:'Rerun with `--decline "<one sentence on why the schema cannot be honestly satisfied>" --code <token>`.'});if(o.length>I)throw new u({error:"decline_reason_too_long",message:`A decline reason is at most ${I} characters (got ${o.length}).`,next:"Keep the reason to one plain sentence; put any detail in an artifact and name its path."});if(typeof a!="string"||a.trim()===""||/\s/.test(a.trim())||a.trim().length>N)throw new u({error:"invalid_decline_code",message:`A decline needs a code: one whitespace-free token of at most ${N} characters naming the decline class.`,next:"Rerun with `--code <token>` \u2014 the token your requester documented for this case."});if(typeof c!="boolean")throw f("a decline carries `retryable` as a boolean");return{requestId:r.trim(),declined:{reason:o.trim(),code:a.trim(),retryable:c}}}if(!i)throw f("a result submission requires a JSON body { value } or { decline, code, retryable }");return{requestId:r.trim(),value:t.value}}s(ce,"parseSubmitBody");const le=1024*1024;async function fe(t){const e=t.params.id,r=await g(e);await _(e)!==null&&await ne(e);const i=await ce(t.body,e),n=await S(i.requestId);if(n===null||n.node_id!==e)throw new h(404,"structured_request_not_found","the named structured request does not belong to this node");if(n.state!=="pending")throw new h(409,n.state==="expired"?"structured_request_expired":"structured_request_settled",`structured request ${n.request_id} is already ${n.state}`);let o;if("value"in i){if(Buffer.byteLength(JSON.stringify(i.value)??"")>le)throw new u({error:"result_too_large",message:"the submitted result is over 1 MiB serialized; nothing was recorded.",next:"Submit a smaller result \u2014 write bulky content to a file and put its path in the result."});try{o=await re(n.schema,i.value)}catch(l){throw new u({error:"result_schema_mismatch",message:`the submitted result does not match the requested schema: ${l.message}`,next:'Fix the listed fields and submit again; if the schema cannot be honestly satisfied, decline it (`submit_result` with `decline`, or `crtr push result --decline "<reason>" --code <token>`).'})}}const a="declined"in i?ue(i.declined):de(o);if(n.submission==="oneoff"){const l=await G(async C=>await ie(C,n.request_id,"declined"in i?{intended:"declined",declined:i.declined}:{intended:"completed",result:o}));if(!l.changed||l.row===null||l.row.state!=="completed"&&l.row.state!=="declined")throw new h(409,l.row?.state==="expired"?"structured_request_expired":"structured_request_settled",`structured request ${n.request_id} is already ${l.row?.state??"settled"}`);await R(l.row);const y=await b(e,{tier:"update",body:a},void 0,{requestMessageId:n.request_message_id});return await T(e,y.reportId),{status:200,body:{mode:n.submission,report:y.report,notified:[...y.deliveredTo],..."declined"in i?{declined:i.declined}:{}}}}const c=(await d(e))?.status,m=await O(e),p="declined"in i?{kind:"failure",reason:"declined",detailJson:JSON.stringify({schema:"crtr.node-outcome-detail/v1",message:i.declined.reason,declined:i.declined}),structured:{requestId:n.request_id,declined:i.declined,skipEffects:!0}}:{structured:{requestId:n.request_id,value:o,skipEffects:!0}},w=await b(e,{tier:"final",body:a},p);await A(e);const F=await S(n.request_id);await R(F),await T(e,w.reportId);const v=c===void 0?void 0:{from:c,to:(await d(e))?.status??"done"};return{status:200,body:{mode:n.submission,report:w.report,notified:[...w.deliveredTo],..."declined"in i?{declined:i.declined}:{},...v===void 0?{}:{transitioned:v},...m===void 0?{}:{worktree_auto_dropped:!0,worktree_auto_dropped_path:m}}}}s(fe,"handleSubmitResult");async function T(t,e){const r=(await q(t)).find(n=>n.id===e);if(r===void 0)return;x.report(t,r);const i=(await d(t))?.run_id;i&&await E(i,t,"node.report.pushed",{body:r.body,kind:r.tier==="final"?"final":"update"})}s(T,"emitReportEvents");async function A(t){try{const e=await D(t);e!==null&&B(t,"pushedFinal",e.executionId)}catch(e){try{j({event:"daemon.node.final-marker.failed",level:"error",node_id:t,error:e})||process.stderr.write(`final marker failed for ${t}: ${String(e)}
13
13
  `)}catch{process.stderr.write(`final marker failed for ${t}: ${String(e)}
14
- `)}}}a(A,"markCommittedFinal");async function b(t,e,i,r){try{return await J(t,{kind:e.tier,body:e.body,...e.delivery_tier===void 0?{}:{deliveryTier:e.delivery_tier},...i===void 0?{}:{outcome:i},...r===void 0?{}:{result:r}})}catch(n){throw n instanceof z?new h(409,n.message,n.message==="structured_request_expired"?"the named structured request expired before this submission committed":"the named structured request is already settled"):n instanceof k&&n.code==="already_finalized"?new u({error:"already_finalized",message:"This node already submitted its canonical final result.",next:"Push an update with the new information, then stop."}):n instanceof k&&n.code==="finalization_forbidden"?new u({error:"finalization_forbidden",message:"A canceled or dead node cannot submit a canonical final result.",next:"Do not push final from this node."}):n}}a(b,"pushOrRethrow");async function q(t){const e=await K(),i=await Y(e,await Z(e,{class:"person"},async()=>[]),{type:"document",kind:"report",owner:await Q(e,t)});return(await oe(i,async r=>{const n=r.name.match(/-(update|urgent|final)$/)?.[1]??"update",o=(await V(e,r))[0];return{name:r.name,id:r.id,tier:n,body:o===void 0?"":U(o.body_hash).trim(),created:r.created_at}})).sort((r,n)=>r.created<n.created?1:r.created>n.created?-1:n.name.localeCompare(r.name))}a(q,"reportsForNode");async function me(t){const e=t.params.id;await g(e);const i=t.query.get("tier"),r=t.query.get("limit"),n=r!==null?Number(r):void 0;let o=await q(e);return i!==null&&(o=o.filter(s=>s.tier===i)),n!==void 0&&Number.isFinite(n)&&n>=0&&(o=o.slice(0,n)),{status:200,body:o}}a(me,"handleList");const Me=[{method:"POST",pattern:"/v1/nodes/:id/reports",ownerOnly:!0,handler:se},{method:"GET",pattern:"/v1/nodes/:id/reports",handler:me},{method:"POST",pattern:"/v1/nodes/:id/result",handler:fe}];export{Me as reportRoutes,q as reportsForNode};
14
+ `)}}}s(A,"markCommittedFinal");async function b(t,e,r,i){try{return await J(t,{kind:e.tier,body:e.body,...e.delivery_tier===void 0?{}:{deliveryTier:e.delivery_tier},...r===void 0?{}:{outcome:r},...i===void 0?{}:{result:i}})}catch(n){throw n instanceof z?new h(409,n.message,n.message==="structured_request_expired"?"the named structured request expired before this submission committed":"the named structured request is already settled"):n instanceof k&&n.code==="already_finalized"?new u({error:"already_finalized",message:"This node already submitted its canonical final result.",next:"Push an update with the new information, then stop."}):n instanceof k&&n.code==="finalization_forbidden"?new u({error:"finalization_forbidden",message:"A canceled or dead node cannot submit a canonical final result.",next:"Do not push final from this node."}):n}}s(b,"pushOrRethrow");async function q(t){const e=await K(),r=await Y(e,await Z(e,{class:"person"},async()=>[]),{type:"document",kind:"report",owner:await Q(e,t)}),i=await oe(r,async o=>({doc:o,head:(await V(e,o))[0]})),n=await U(e,i.flatMap(o=>o.head===void 0?[]:[o.head.body_hash]));return i.map(({doc:o,head:a})=>{const c=o.name.match(/-(update|urgent|final)$/)?.[1]??"update";return{name:o.name,id:o.id,tier:c,body:a===void 0?"":n.get(a.body_hash).trim(),created:o.created_at}}).sort((o,a)=>o.created<a.created?1:o.created>a.created?-1:a.name.localeCompare(o.name))}s(q,"reportsForNode");async function me(t){const e=t.params.id;await g(e);const r=t.query.get("tier"),i=t.query.get("limit"),n=i!==null?Number(i):void 0;let o=await q(e);return r!==null&&(o=o.filter(a=>a.tier===r)),n!==void 0&&Number.isFinite(n)&&n>=0&&(o=o.slice(0,n)),{status:200,body:o}}s(me,"handleList");const Be=[{method:"POST",pattern:"/v1/nodes/:id/reports",ownerOnly:!0,handler:se},{method:"GET",pattern:"/v1/nodes/:id/reports",handler:me},{method:"POST",pattern:"/v1/nodes/:id/result",handler:fe}];export{Be as reportRoutes,q as reportsForNode};
@@ -1 +1 @@
1
- var R=Object.defineProperty;var s=(t,e)=>R(t,"name",{value:e,configurable:!0});import{ApiError as m}from"../../../api/index.js";import{withCanvasWrite as k}from"../../../core/canvas/db.js";import{isSafeNodeId as p}from"../../../core/canvas/paths.js";import{readDocument as q}from"../../../core/graph/documents.js";import{getById as E}from"../../../core/graph/objects.js";import{checkoutsForCwd as S}from"../../../core/graph/repo-sync/repos.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{assertScope as T}from"../../../core/scopes.js";import{createReview as $}from"../../../core/review/birth.js";import{readAnchorBase as C,listReviews as I,requireVisibleReview as y}from"../../../core/review/store.js";import{cancelReview as A,submitReviewApproval as D}from"../../review/finish.js";import{toFeedbackResultDTO as O,toReviewDTO as F}from"../map.js";import{readerFor as L,resolveRef as P,warmReaderRepos as B}from"../reader.js";import{actsAsPerson as N,canReadNode as j}from"../router.js";import{invalidRequest as G,objectBody as v,optionalString as c,requireParam as V,requiredString as g}from"./validate.js";import{filterAsync as W}from"../../../core/async-array.js";const K=new Set(["open","approved","canceled"]),M=["origin_kind","origin_node_id","document","file","idempotency_key","subtitle","output_path"],z=["reason","actor","disposition"];function l(t){G("review request",t)}s(l,"invalidRequest");function w(t,e){return V(t,"review_id","must be a safe review id",p,e)}s(w,"requireReviewId");function f(t){return{...F(t,t.state==="approved"?{result_path:`${t.review_dir}/result.json`}:{}),review_dir:t.review_dir}}s(f,"toReviewResponseDTO");function H(t){const e=[],n=v(t.body,M,e),o=n.origin_kind,i=o==="ticket"||o==="inline"?o:void 0;i===void 0&&e.push({field:"origin_kind",problem:"must be ticket or inline",...o===void 0?{}:{received:o}});const r=g(n,"origin_node_id",e);r!==void 0&&!p(r)&&e.push({field:"origin_node_id",problem:"must be a safe node id",received:r});const d=c(n,"document",e),u=c(n,"file",e);i==="ticket"&&(d===void 0||u!==void 0)&&e.push({field:"document",problem:"is required for ticket reviews, and file is not allowed"}),i==="inline"&&u===void 0==(d===void 0)&&e.push({field:"file",problem:"inline reviews take exactly one of file or document"});const a=g(n,"idempotency_key",e);a!==void 0&&a.length>128&&e.push({field:"idempotency_key",problem:"must be at most 128 characters",received:a});const h=c(n,"subtitle",e),_=c(n,"output_path",e);return i==="ticket"&&h===void 0&&n.subtitle===void 0&&e.push({field:"subtitle",problem:"is required for ticket reviews"}),i==="inline"&&n.subtitle!==void 0&&e.push({field:"subtitle",problem:"is only allowed for ticket reviews",received:n.subtitle}),i==="inline"&&n.output_path!==void 0&&e.push({field:"output_path",problem:"is only allowed for ticket reviews",received:n.output_path}),(e.length>0||i===void 0||r===void 0||!p(r)||a===void 0||a.length>128)&&l(e),{origin_kind:i,origin_node_id:r,...d===void 0?{}:{document:d},...u===void 0?{}:{file:u},idempotency_key:a,...h===void 0?{}:{subtitle:h},..._===void 0?{}:{output_path:_}}}s(H,"parseCreate");function J(t){const e=[],n={},o=new Set(["state","origin_node_id","file"]);for(const[i,r]of t.query.entries())o.has(i)||e.push({field:i,problem:"is not allowed",received:r});for(const i of o){const r=t.query.getAll(i);if(r.length>1){e.push({field:i,problem:"must appear at most once",received:r});continue}const d=r[0];d!==void 0&&(i==="state"?K.has(d)?n.state=d:e.push({field:i,problem:"must be open, approved, or canceled",received:d}):i==="origin_node_id"?p(d)?n.origin_node_id=d:e.push({field:i,problem:"must be a safe node id",received:d}):d===""?e.push({field:i,problem:"must be a non-empty string",received:d}):n.file=d)}return e.length>0&&l(e),n}s(J,"parseList");function Q(t,e){const n=t.body===void 0?{}:v(t.body,z,e),o=c(n,"reason",e),i=c(n,"actor",e),r=c(n,"disposition",e);return r!==void 0&&r!=="canceled"&&r!=="dismissed"&&e.push({field:"disposition",problem:'must be "canceled" or "dismissed"',received:r}),{...o===void 0?{}:{reason:o},...i===void 0?{}:{actor:i},...r==="canceled"||r==="dismissed"?{disposition:r}:{}}}s(Q,"parseCancel");function U(t,e){v(t.body,[],e)}s(U,"requireEmptyBody");async function X(t){const e=H(t),n=await b(e.origin_node_id);n!==null&&T(n,"crtr:act");const{document:o,file:i,...r}=e,d=await $({...r,...o===void 0?{file:i??""}:{document:await Y(t,o)}});return{status:200,body:f(d)}}s(X,"handleCreate");async function Y(t,e){return await B(t.principal),await k(async n=>{const o=await L(n,t),i=await P(n,o,e);if("remote"in i)throw new m(404,"not_found",`no document named ${i.remote} on this runtime`,{next:"Check the name with `crtr canvas search`."});if("deleted"in i)throw new m(404,"not_found",`${i.deleted.name} is deleted`,{id:i.deleted.id,deleted:!0});if(i.type!=="document")throw new m(400,"invalid_request",`${e} is a ${i.type}, not a document`);const r=await q(n,o,i,async a=>await S(n,a));if(r.needsMerge)throw new m(409,"needs_merge",`${i.name} has ${r.heads.length} heads; merge them before a review`,{id:i.id,next:`Read it with \`crtr canvas read ${i.id}\` and write the merged body with \`crtr doc edit\`.`});const d=i.owner===null?null:await E(n,i.owner),u=d===null?"":d.type==="node"?d.id:d.name;return{id:i.id,name:u===""?i.name:`${u}/${i.name}`,base:r.record.heads_key,body:r.heads[0].body}})}s(Y,"reviewedDocument");async function Z(t){const e=J(t);return{status:200,body:{reviews:(await W(await I(e),async o=>{if(N(t.principal))return!0;const i=await b(o.origin_node_id);return i!==null&&await j(t.principal,i.grantee??"app:terminal",i.node_id)})).map(f)}}}s(Z,"handleList");async function x(t){const e=[],n=w(t,e);return(e.length>0||n===void 0)&&l(e),{status:200,body:f(await y(n))}}s(x,"handleRead");async function ee(t){const e=[],n=w(t,e);U(t,e),(e.length>0||n===void 0)&&l(e);const o=await D(n);return{status:200,body:o.outcome==="awaiting_companion"?{review:f(o.record),outcome:o.outcome}:{review:f(o.record),result:O(o.result),outcome:o.outcome}}}s(ee,"handleSubmit");async function ie(t){const e=[],n=w(t,e),o=Q(t,e);(e.length>0||n===void 0)&&l(e);const i=await A(n,o);return{status:200,body:{review:f(i.record),outcome:i.outcome}}}s(ie,"handleCancel");async function ne(t){const e=[],n=w(t,e);(e.length>0||n===void 0)&&l(e);const o=await y(n),i=await C(o.review_id);if(i===null)throw new Error(`review anchor base is missing: ${o.review_id}`);return{status:200,body:{review_id:o.review_id,anchor_base_sha256:i.sha256,anchor_base_text:new TextDecoder("utf-8",{fatal:!0}).decode(i.bytes),file:o.file}}}s(ne,"handleDocument");const ye=[{method:"POST",pattern:"/v1/human/reviews",scope:"crtr:act",handler:X},{method:"GET",pattern:"/v1/human/reviews",handler:Z},{method:"GET",pattern:"/v1/human/reviews/:review_id",handler:x},{method:"POST",pattern:"/v1/human/reviews/:review_id/submit",handler:ee},{method:"POST",pattern:"/v1/human/reviews/:review_id/cancel",handler:ie},{method:"GET",pattern:"/v1/human/reviews/:review_id/document",handler:ne}];export{H as parseCreate,ye as reviewRoutes};
1
+ var R=Object.defineProperty;var s=(n,e)=>R(n,"name",{value:e,configurable:!0});import{ApiError as p}from"../../../api/index.js";import{isCloudRole as k}from"../../../core/canvas/person.js";import{withCanvasWrite as q}from"../../../core/canvas/db.js";import{isSafeNodeId as w}from"../../../core/canvas/paths.js";import{readDocument as E}from"../../../core/graph/documents.js";import{getById as S}from"../../../core/graph/objects.js";import{checkoutsForCwd as T}from"../../../core/graph/repo-sync/repos.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{assertScope as C}from"../../../core/scopes.js";import{createReview as $}from"../../../core/review/birth.js";import{readAnchorBase as I,listReviews as A,requireVisibleReview as y}from"../../../core/review/store.js";import{cancelReview as D,submitReviewApproval as O}from"../../review/finish.js";import{toFeedbackResultDTO as F,toReviewDTO as L}from"../map.js";import{readerFor as P,resolveRef as j,warmReaderRepos as B}from"../reader.js";import{actsAsPerson as N,canReadNode as G}from"../router.js";import{invalidRequest as V,objectBody as _,optionalString as c,requireParam as W,requiredString as g}from"./validate.js";import{filterAsync as K}from"../../../core/async-array.js";const M=new Set(["open","approved","canceled"]),z=["origin_kind","origin_node_id","document","file","idempotency_key","subtitle","output_path"],H=["reason","actor","disposition"];function l(n){V("review request",n)}s(l,"invalidRequest");function h(n,e){return W(n,"review_id","must be a safe review id",w,e)}s(h,"requireReviewId");function f(n){return{...L(n,n.state==="approved"?{result_path:`${n.review_dir}/result.json`}:{}),review_dir:n.review_dir}}s(f,"toReviewResponseDTO");function J(n){const e=[],t=_(n.body,z,e),o=t.origin_kind,i=o==="ticket"||o==="inline"?o:void 0;i===void 0&&e.push({field:"origin_kind",problem:"must be ticket or inline",...o===void 0?{}:{received:o}});const r=g(t,"origin_node_id",e);r!==void 0&&!w(r)&&e.push({field:"origin_node_id",problem:"must be a safe node id",received:r});const d=c(t,"document",e),u=c(t,"file",e);i==="ticket"&&(d===void 0||u!==void 0)&&e.push({field:"document",problem:"is required for ticket reviews, and file is not allowed"}),i==="inline"&&u===void 0==(d===void 0)&&e.push({field:"file",problem:"inline reviews take exactly one of file or document"});const a=g(t,"idempotency_key",e);a!==void 0&&a.length>128&&e.push({field:"idempotency_key",problem:"must be at most 128 characters",received:a});const v=c(t,"subtitle",e),m=c(t,"output_path",e);return i==="ticket"&&v===void 0&&t.subtitle===void 0&&e.push({field:"subtitle",problem:"is required for ticket reviews"}),i==="inline"&&t.subtitle!==void 0&&e.push({field:"subtitle",problem:"is only allowed for ticket reviews",received:t.subtitle}),i==="inline"&&t.output_path!==void 0&&e.push({field:"output_path",problem:"is only allowed for ticket reviews",received:t.output_path}),m!==void 0&&k()&&e.push({field:"output_path",problem:"is not supported on the cloud: a review result is projected only to its own request files",received:m}),(e.length>0||i===void 0||r===void 0||!w(r)||a===void 0||a.length>128)&&l(e),{origin_kind:i,origin_node_id:r,...d===void 0?{}:{document:d},...u===void 0?{}:{file:u},idempotency_key:a,...v===void 0?{}:{subtitle:v},...m===void 0?{}:{output_path:m}}}s(J,"parseCreate");function Q(n){const e=[],t={},o=new Set(["state","origin_node_id","file"]);for(const[i,r]of n.query.entries())o.has(i)||e.push({field:i,problem:"is not allowed",received:r});for(const i of o){const r=n.query.getAll(i);if(r.length>1){e.push({field:i,problem:"must appear at most once",received:r});continue}const d=r[0];d!==void 0&&(i==="state"?M.has(d)?t.state=d:e.push({field:i,problem:"must be open, approved, or canceled",received:d}):i==="origin_node_id"?w(d)?t.origin_node_id=d:e.push({field:i,problem:"must be a safe node id",received:d}):d===""?e.push({field:i,problem:"must be a non-empty string",received:d}):t.file=d)}return e.length>0&&l(e),t}s(Q,"parseList");function U(n,e){const t=n.body===void 0?{}:_(n.body,H,e),o=c(t,"reason",e),i=c(t,"actor",e),r=c(t,"disposition",e);return r!==void 0&&r!=="canceled"&&r!=="dismissed"&&e.push({field:"disposition",problem:'must be "canceled" or "dismissed"',received:r}),{...o===void 0?{}:{reason:o},...i===void 0?{}:{actor:i},...r==="canceled"||r==="dismissed"?{disposition:r}:{}}}s(U,"parseCancel");function X(n,e){_(n.body,[],e)}s(X,"requireEmptyBody");async function Y(n){const e=J(n),t=await b(e.origin_node_id);t!==null&&C(t,"crtr:act");const{document:o,file:i,...r}=e,d=await $({...r,...o===void 0?{file:i??""}:{document:await Z(n,o)}});return{status:200,body:f(d)}}s(Y,"handleCreate");async function Z(n,e){return await B(n.principal),await q(async t=>{const o=await P(t,n),i=await j(t,o,e);if("remote"in i)throw new p(404,"not_found",`no document named ${i.remote} on this runtime`,{next:"Check the name with `crtr canvas search`."});if("deleted"in i)throw new p(404,"not_found",`${i.deleted.name} is deleted`,{id:i.deleted.id,deleted:!0});if(i.type!=="document")throw new p(400,"invalid_request",`${e} is a ${i.type}, not a document`);const r=await E(t,o,i,async a=>await T(t,a));if(r.needsMerge)throw new p(409,"needs_merge",`${i.name} has ${r.heads.length} heads; merge them before a review`,{id:i.id,next:`Read it with \`crtr canvas read ${i.id}\` and write the merged body with \`crtr doc edit\`.`});const d=i.owner===null?null:await S(t,i.owner),u=d===null?"":d.type==="node"?d.id:d.name;return{id:i.id,name:u===""?i.name:`${u}/${i.name}`,base:r.record.heads_key,body:r.heads[0].body}})}s(Z,"reviewedDocument");async function x(n){const e=Q(n);return{status:200,body:{reviews:(await K(await A(e),async o=>{if(N(n.principal))return!0;const i=await b(o.origin_node_id);return i!==null&&await G(n.principal,i.grantee??"app:terminal",i.node_id)})).map(f)}}}s(x,"handleList");async function ee(n){const e=[],t=h(n,e);return(e.length>0||t===void 0)&&l(e),{status:200,body:f(await y(t))}}s(ee,"handleRead");async function ie(n){const e=[],t=h(n,e);X(n,e),(e.length>0||t===void 0)&&l(e);const o=await O(t);return{status:200,body:o.outcome==="awaiting_companion"?{review:f(o.record),outcome:o.outcome}:{review:f(o.record),result:F(o.result),outcome:o.outcome}}}s(ie,"handleSubmit");async function te(n){const e=[],t=h(n,e),o=U(n,e);(e.length>0||t===void 0)&&l(e);const i=await D(t,o);return{status:200,body:{review:f(i.record),outcome:i.outcome}}}s(te,"handleCancel");async function ne(n){const e=[],t=h(n,e);(e.length>0||t===void 0)&&l(e);const o=await y(t),i=await I(o.review_id);if(i===null)throw new Error(`review anchor base is missing: ${o.review_id}`);return{status:200,body:{review_id:o.review_id,anchor_base_sha256:i.sha256,anchor_base_text:new TextDecoder("utf-8",{fatal:!0}).decode(i.bytes),file:o.file}}}s(ne,"handleDocument");const Re=[{method:"POST",pattern:"/v1/human/reviews",scope:"crtr:act",handler:Y},{method:"GET",pattern:"/v1/human/reviews",handler:x},{method:"GET",pattern:"/v1/human/reviews/:review_id",handler:ee},{method:"POST",pattern:"/v1/human/reviews/:review_id/submit",handler:ie},{method:"POST",pattern:"/v1/human/reviews/:review_id/cancel",handler:te},{method:"GET",pattern:"/v1/human/reviews/:review_id/document",handler:ne}];export{J as parseCreate,Re as reviewRoutes};
@@ -1 +1 @@
1
- var o=Object.defineProperty;var r=(e,t)=>o(e,"name",{value:t,configurable:!0});import{migrateLegacyInboxIdentities as i}from"../core/feed/inbox-migrate.js";import{backfillComputedRev as a}from"../core/grants/mirror.js";import{isCloudRole as s}from"../core/canvas/person-state.js";import{emitEvent as n}from"../core/events/emit.js";const u=[{id:"legacy-inbox-identities",description:"normalize pre-cutover inbox entry/operation ids (UUID-spelled or missing) and cursors (ISO-timestamp boundaries) into canonical 32-lowercase-hex identities the strict inbox reader requires.",apply:r(()=>{const e=i();return{changed:e.nodes>0||e.failures>0,summary:{...e}}},"apply")},{id:"grant-computed-rev",description:"mark grant rows a pre-C-D2 build wrote (scopes computed, computed_rev NULL) as computed for their revision, so the terminal grant reads as active before the first directory sync after an upgrade or rollback (NL-550). A cloud role skips it: the directory owns its shared rows.",apply:r(async()=>{if(s())return{changed:!1,summary:{}};try{const e=await a();return{changed:e>0,summary:{rows:e}}}catch(e){return n({level:"warn",event:"daemon.boot_migration.failed",error:e,fields:{migration:"grant-computed-rev"}}),{changed:!1,summary:{}}}},"apply")}];export{u as BOOT_MIGRATIONS};
1
+ var i=Object.defineProperty;var r=(e,t)=>i(e,"name",{value:t,configurable:!0});import{migrateLegacyInboxIdentities as a}from"../core/feed/inbox-migrate.js";import{backfillComputedRev as s}from"../core/grants/mirror.js";import{copyBodyFilesToTable as n}from"../core/graph/bodies-boot-copy.js";import{isCloudRole as o}from"../core/canvas/person-state.js";import{emitEvent as c}from"../core/events/emit.js";const h=[{id:"legacy-inbox-identities",description:"normalize pre-cutover inbox entry/operation ids (UUID-spelled or missing) and cursors (ISO-timestamp boundaries) into canonical 32-lowercase-hex identities the strict inbox reader requires.",apply:r(()=>{const e=a();return{changed:e.nodes>0||e.failures>0,summary:{...e}}},"apply")},{id:"grant-computed-rev",description:"mark grant rows a pre-C-D2 build wrote (scopes computed, computed_rev NULL) as computed for their revision, so the terminal grant reads as active before the first directory sync after an upgrade or rollback (NL-550). A cloud role skips it: the directory owns its shared rows.",apply:r(async()=>{if(o())return{changed:!1,summary:{}};try{const e=await s();return{changed:e>0,summary:{rows:e}}}catch(e){return c({level:"warn",event:"daemon.boot_migration.failed",error:e,fields:{migration:"grant-computed-rev"}}),{changed:!1,summary:{}}}},"apply")},{id:"bodies-to-table",description:"copy document text from the old <home>/bodies files into the bodies table (D106). Verifies each file by sha256, skips and reports mismatches, leaves the files for rollback. A cloud role copies nothing.",apply:r(async()=>{if(o())return{changed:!1,summary:{}};const e=await n();return{changed:e.copied>0||e.mismatched.length>0,summary:{...e}}},"apply")}];export{h as BOOT_MIGRATIONS};
@@ -1 +1 @@
1
- var q=Object.defineProperty;var d=(e,t)=>q(e,"name",{value:t,configurable:!0});import{existsSync as w,readFileSync as E,readdirSync as g,rmSync as p}from"node:fs";import{join as u}from"node:path";import{cronIdsForProfileDeletion as R,cronIdsForProfileDetach as C,deleteCronsForProfile as T,deleteNodes as O,getNode as m,getRow as W,globalizeCronsForProfile as A,listNodesByProfile as M,nodesRoot as B,reviewDir as $}from"../core/canvas/index.js";import{withCanvasWrite as v}from"../core/canvas/db.js";import{deleteProfileWithOwned as _}from"../core/graph/deletion.js";import{usage as f}from"../core/errors.js";import{emitEvent as z}from"../core/events/emit.js";import{clearDefaultProfileId as k,defaultProfileDirs as L}from"../core/profiles/default-binding.js";import{reserveProfileDeletion as J}from"../core/profiles/deletion-reservation.js";import{deleteProfile as D,loadProfileManifest as G}from"../core/profiles/manifest.js";import{deleteReviewsByOriginNodes as H,reviewIdsByOriginNodes as K}from"../core/review/store.js";import{boundFleet as U}from"../core/runtime/fleet.js";import{headlessBrokerHost as b}from"../core/runtime/host.js";import{transition as Q}from"../core/runtime/lifecycle.js";import{tearDownNode as V}from"../core/runtime/placement.js";import{reshapeNode as X}from"../core/runtime/promote.js";import{reviveNode as Y}from"../core/runtime/revive.js";import{filterAsync as Z,flatMapAsync as ee}from"../core/async-array.js";const P=1e3,N=new Set(["page.json","page.tsx","page.html","page.js","review.json","response.json","progress.json","claim.json","reply-route.json","feedback-comments.json","feedback.json",".ticket-lock"]),h=new Map;function te(e){if(typeof e!="object"||e===null||Array.isArray(e))throw f("profile deletion requires a JSON request body",{error:"force_required",message:"profile deletion requires --force",next:"Re-run `crtr profile delete <profile> --force` after reviewing its effects."});return e}d(te,"requestObject");function re(e){const t=te(e);if(t.force!==!0)throw f("profile deletion requires --force",{error:"force_required",message:"profile deletion requires --force",next:"Re-run `crtr profile delete <profile> --force` after reviewing its effects."});if(t.detach!==void 0&&typeof t.detach!="boolean")throw f("detach must be a boolean",{error:"invalid_detach",message:"detach must be a boolean",next:"Use --detach as a presence-only flag, or omit it."});const r=t.caller_node_id;if(r!=null&&(typeof r!="string"||r===""))throw f("caller_node_id must be a nonempty string or null");return{force:!0,...t.detach===!0?{detach:!0}:{},...r===void 0?{}:{caller_node_id:r}}}d(re,"validateRequest");function j(e){if(!w(e))return null;try{const t=JSON.parse(E(e,"utf8"));return typeof t.source?.nodeId=="string"&&t.source.nodeId!==""?t.source.nodeId:null}catch{return null}}d(j,"manifestSourceNodeId");async function oe(e){const t=new Set(e),r=B();if(t.size===0||!w(r))return[];const o=[];for(const n of g(r,{withFileTypes:!0})){if(!n.isDirectory())continue;const s=u(r,n.name);[j(u(s,"page.json")),j(u(s,"review.json"))].some(c=>c!==null&&t.has(c))&&o.push({dir:s,wholeDirectory:t.has(n.name)||await W(n.name)===null})}return o.sort((n,s)=>n.dir.localeCompare(s.dir))}d(oe,"ticketCleanupForNodes");function ne(e){if(N.has(e))return!0;for(const t of N)if(e.startsWith(`${t}.`)&&(e.endsWith(".tmp")||e.endsWith(".lock")))return!0;return!1}d(ne,"isTicketOwnedResidue");function ie(e){let t=0;for(const r of e)if(w(r.dir)){if(r.wholeDirectory){p(r.dir,{recursive:!0,force:!0}),t+=1;continue}for(const o of g(r.dir,{withFileTypes:!0}))ne(o.name)&&p(u(r.dir,o.name),{recursive:!0,force:!0});t+=1}return t}d(ie,"cleanTickets");function S(e){const t=e.filter(o=>o.managed_worktree?.state==="open").map(o=>({node_id:o.node_id,path:o.managed_worktree.path}));if(t.length===0)return null;const r=`profile deletion is blocked by ${t.length} open managed worktree${t.length===1?"":"s"}`;return f(r,{error:"open_managed_worktree",message:r,nodes:t,next:`Close ${t.map(({node_id:o})=>o).join(", ")} with \`crtr node worktree close <node>\`, then retry.`})}d(S,"openWorktreeError");async function se(e,t){const r=await M(e.profileId),o=S(r);if(o!==null)throw o;const n=r.map(({node_id:a})=>a),s=t.detach===!0?"detached":"deleted";return{entry:e,mode:s,nodes:r,nodeIds:n,tickets:s==="deleted"?await oe(n):[],cronIds:s==="deleted"?await R(e.profileId,n):await C(e.profileId),reviewIds:s==="deleted"?await K(n):[],defaultDirs:L(e.profileId)}}d(se,"prepareDeletion");async function x(e){const t=await ee(e.nodeIds,async o=>{const n=await m(o);return n===null?[]:[n]}),r=S(t);if(r!==null)throw r}d(x,"assertDeletionPlanPreconditions");function y(e){return{profile_id:e.entry.profileId,name:e.entry.manifest.name,mode:e.mode,nodes:e.nodeIds.length,crons:e.cronIds.length,tickets:e.tickets.length,reviews:e.reviewIds.length,default_pins:e.defaultDirs.length}}d(y,"resultForPlan");async function de(e){await x(e);const t=await Z(e.nodeIds,async i=>await m(i)!==null),r=U(),o=t.flatMap(i=>{const l=r.get(i);return l===void 0?[]:[{nodeId:i,exited:l.handle.exited}]}),n=[];for(const{nodeId:i}of o){const l=await m(i);(l?.status==="active"||l?.status==="idle")&&(await Q(i,"release"),n.push(i)),b.teardown(i)}await Promise.all(o.map(({exited:i})=>i));const s=[];for(const i of t)await m(i)!==null&&(await X(i,{profileId:null}),s.push(i));const a=await A(e.entry.profileId),c=await k(e.entry.profileId);await v(async i=>await _(i,e.entry.profileId)),await D(e.entry.profileId);const I=new Set(s);for(const i of n)I.has(i)&&await Y(i,{resume:!0,recovery:!0,capacity:"freeze"});return{...y(e),nodes:s.length,crons:a.length,default_pins:c.length}}d(de,"detachProfile");async function ae(e){await x(e);const t=await T(e.entry.profileId,e.nodeIds);for(const a of e.nodeIds)b.teardown(a),await V(a);const r=await H(e.nodeIds);for(const a of r)p($(a),{recursive:!0,force:!0});const o=ie(e.tickets),n=await O(e.nodeIds),s=await k(e.entry.profileId);return await v(async a=>await _(a,e.entry.profileId)),await D(e.entry.profileId),{...y(e),nodes:n.length,crons:t.length,tickets:o,reviews:r.length,default_pins:s.length}}d(ae,"hardDeleteProfile");async function F(e){return e.mode==="detached"?de(e):await ae(e)}d(F,"executePlan");function ce(e,t){const r={...y(e),accepted:!0,grace_ms:P};return h.set(e.entry.profileId,{result:r}),setTimeout(()=>{(async()=>{try{await F(e)}catch(o){z({level:"error",event:"profile.delete.deferred_failed",error:o instanceof Error?o:new Error(String(o)),fields:{profile_id:e.entry.profileId,mode:e.mode}})}finally{h.delete(e.entry.profileId),t()}})()},P),r}d(ce,"scheduleSelfDeletion");async function xe(e,t){const r=re(t),o=G(e),n=h.get(o.profileId);if(n!==void 0)return{status:202,body:n.result};const s=J(o.profileId);let a=!1;try{const c=await se(o,r);if(r.caller_node_id!=null&&c.nodeIds.includes(r.caller_node_id)){const i=ce(c,s);return a=!0,{status:202,body:i}}return{status:200,body:await F(c)}}finally{a||s()}}d(xe,"requestProfileDeletion");export{xe as requestProfileDeletion};
1
+ var q=Object.defineProperty;var d=(e,t)=>q(e,"name",{value:t,configurable:!0});import{existsSync as w,readFileSync as E,readdirSync as I,rmSync as g}from"node:fs";import{join as u}from"node:path";import{cronIdsForProfileDeletion as R,cronIdsForProfileDetach as C,deleteCronsForProfile as T,deleteNodes as O,getNode as m,getRow as W,globalizeCronsForProfile as A,listNodesByProfile as M,nodesRoot as B}from"../core/canvas/index.js";import{deleteReviewFiles as $}from"../core/review/stage.js";import{withCanvasWrite as v}from"../core/canvas/db.js";import{deleteProfileWithOwned as _}from"../core/graph/deletion.js";import{usage as f}from"../core/errors.js";import{emitEvent as z}from"../core/events/emit.js";import{clearDefaultProfileId as k,defaultProfileDirs as L}from"../core/profiles/default-binding.js";import{reserveProfileDeletion as J}from"../core/profiles/deletion-reservation.js";import{deleteProfile as D,loadProfileManifest as G}from"../core/profiles/manifest.js";import{deleteReviewsByOriginNodes as H,reviewIdsByOriginNodes as K}from"../core/review/store.js";import{boundFleet as U}from"../core/runtime/fleet.js";import{headlessBrokerHost as b}from"../core/runtime/host.js";import{transition as Q}from"../core/runtime/lifecycle.js";import{tearDownNode as V}from"../core/runtime/placement.js";import{reshapeNode as X}from"../core/runtime/promote.js";import{reviveNode as Y}from"../core/runtime/revive.js";import{filterAsync as Z,flatMapAsync as ee}from"../core/async-array.js";const P=1e3,N=new Set(["page.json","page.tsx","page.html","page.js","review.json","response.json","progress.json","claim.json","reply-route.json","feedback-comments.json","feedback.json",".ticket-lock"]),p=new Map;function te(e){if(typeof e!="object"||e===null||Array.isArray(e))throw f("profile deletion requires a JSON request body",{error:"force_required",message:"profile deletion requires --force",next:"Re-run `crtr profile delete <profile> --force` after reviewing its effects."});return e}d(te,"requestObject");function re(e){const t=te(e);if(t.force!==!0)throw f("profile deletion requires --force",{error:"force_required",message:"profile deletion requires --force",next:"Re-run `crtr profile delete <profile> --force` after reviewing its effects."});if(t.detach!==void 0&&typeof t.detach!="boolean")throw f("detach must be a boolean",{error:"invalid_detach",message:"detach must be a boolean",next:"Use --detach as a presence-only flag, or omit it."});const r=t.caller_node_id;if(r!=null&&(typeof r!="string"||r===""))throw f("caller_node_id must be a nonempty string or null");return{force:!0,...t.detach===!0?{detach:!0}:{},...r===void 0?{}:{caller_node_id:r}}}d(re,"validateRequest");function j(e){if(!w(e))return null;try{const t=JSON.parse(E(e,"utf8"));return typeof t.source?.nodeId=="string"&&t.source.nodeId!==""?t.source.nodeId:null}catch{return null}}d(j,"manifestSourceNodeId");async function oe(e){const t=new Set(e),r=B();if(t.size===0||!w(r))return[];const o=[];for(const n of I(r,{withFileTypes:!0})){if(!n.isDirectory())continue;const s=u(r,n.name);[j(u(s,"page.json")),j(u(s,"review.json"))].some(c=>c!==null&&t.has(c))&&o.push({dir:s,wholeDirectory:t.has(n.name)||await W(n.name)===null})}return o.sort((n,s)=>n.dir.localeCompare(s.dir))}d(oe,"ticketCleanupForNodes");function ne(e){if(N.has(e))return!0;for(const t of N)if(e.startsWith(`${t}.`)&&(e.endsWith(".tmp")||e.endsWith(".lock")))return!0;return!1}d(ne,"isTicketOwnedResidue");function ie(e){let t=0;for(const r of e)if(w(r.dir)){if(r.wholeDirectory){g(r.dir,{recursive:!0,force:!0}),t+=1;continue}for(const o of I(r.dir,{withFileTypes:!0}))ne(o.name)&&g(u(r.dir,o.name),{recursive:!0,force:!0});t+=1}return t}d(ie,"cleanTickets");function S(e){const t=e.filter(o=>o.managed_worktree?.state==="open").map(o=>({node_id:o.node_id,path:o.managed_worktree.path}));if(t.length===0)return null;const r=`profile deletion is blocked by ${t.length} open managed worktree${t.length===1?"":"s"}`;return f(r,{error:"open_managed_worktree",message:r,nodes:t,next:`Close ${t.map(({node_id:o})=>o).join(", ")} with \`crtr node worktree close <node>\`, then retry.`})}d(S,"openWorktreeError");async function se(e,t){const r=await M(e.profileId),o=S(r);if(o!==null)throw o;const n=r.map(({node_id:a})=>a),s=t.detach===!0?"detached":"deleted";return{entry:e,mode:s,nodes:r,nodeIds:n,tickets:s==="deleted"?await oe(n):[],cronIds:s==="deleted"?await R(e.profileId,n):await C(e.profileId),reviewIds:s==="deleted"?await K(n):[],defaultDirs:L(e.profileId)}}d(se,"prepareDeletion");async function F(e){const t=await ee(e.nodeIds,async o=>{const n=await m(o);return n===null?[]:[n]}),r=S(t);if(r!==null)throw r}d(F,"assertDeletionPlanPreconditions");function h(e){return{profile_id:e.entry.profileId,name:e.entry.manifest.name,mode:e.mode,nodes:e.nodeIds.length,crons:e.cronIds.length,tickets:e.tickets.length,reviews:e.reviewIds.length,default_pins:e.defaultDirs.length}}d(h,"resultForPlan");async function de(e){await F(e);const t=await Z(e.nodeIds,async i=>await m(i)!==null),r=U(),o=t.flatMap(i=>{const l=r.get(i);return l===void 0?[]:[{nodeId:i,exited:l.handle.exited}]}),n=[];for(const{nodeId:i}of o){const l=await m(i);(l?.status==="active"||l?.status==="idle")&&(await Q(i,"release"),n.push(i)),b.teardown(i)}await Promise.all(o.map(({exited:i})=>i));const s=[];for(const i of t)await m(i)!==null&&(await X(i,{profileId:null}),s.push(i));const a=await A(e.entry.profileId),c=await k(e.entry.profileId);await v(async i=>await _(i,e.entry.profileId)),await D(e.entry.profileId);const y=new Set(s);for(const i of n)y.has(i)&&await Y(i,{resume:!0,recovery:!0,capacity:"freeze"});return{...h(e),nodes:s.length,crons:a.length,default_pins:c.length}}d(de,"detachProfile");async function ae(e){await F(e);const t=await T(e.entry.profileId,e.nodeIds);for(const a of e.nodeIds)b.teardown(a),await V(a);const r=await H(e.nodeIds);for(const a of r)await $(a);const o=ie(e.tickets),n=await O(e.nodeIds),s=await k(e.entry.profileId);return await v(async a=>await _(a,e.entry.profileId)),await D(e.entry.profileId),{...h(e),nodes:n.length,crons:t.length,tickets:o,reviews:r.length,default_pins:s.length}}d(ae,"hardDeleteProfile");async function x(e){return e.mode==="detached"?de(e):await ae(e)}d(x,"executePlan");function ce(e,t){const r={...h(e),accepted:!0,grace_ms:P};return p.set(e.entry.profileId,{result:r}),setTimeout(()=>{(async()=>{try{await x(e)}catch(o){z({level:"error",event:"profile.delete.deferred_failed",error:o instanceof Error?o:new Error(String(o)),fields:{profile_id:e.entry.profileId,mode:e.mode}})}finally{p.delete(e.entry.profileId),t()}})()},P),r}d(ce,"scheduleSelfDeletion");async function xe(e,t){const r=re(t),o=G(e),n=p.get(o.profileId);if(n!==void 0)return{status:202,body:n.result};const s=J(o.profileId);let a=!1;try{const c=await se(o,r);if(r.caller_node_id!=null&&c.nodeIds.includes(r.caller_node_id)){const i=ce(c,s);return a=!0,{status:202,body:i}}return{status:200,body:await x(c)}}finally{a||s()}}d(xe,"requestProfileDeletion");export{xe as requestProfileDeletion};
@@ -1 +1 @@
1
- var g=Object.defineProperty;var n=(e,t)=>g(e,"name",{value:t,configurable:!0});import{withCanvasWrite as j}from"../../core/canvas/db.js";import{get as S}from"../../core/graph/bodies.js";import{documentById as q,editDocument as C,heads as $}from"../../core/graph/documents.js";import{readerFor as k}from"../../core/graph/reader.js";import{warmDocumentWatchers as J}from"../api/reader.js";import{checkoutsForCwd as x}from"../../core/graph/repo-sync/repos.js";import{snapshotCommentsLocked as N}from"../../core/review/comments.js";import{readReviewSource as O}from"../../core/review/document.js";import{projectReviewCancellation as T,projectReviewResult as D,buildReviewResult as A}from"../../core/review/project.js";import{signalReviewActivity as p}from"../../core/review/signal.js";import{clearReviewSubmit as E,getReview as W,getReviewByCompanion as B,requestReviewSubmit as F,requireVisibleReview as u}from"../../core/review/store.js";import{ReviewOperationError as w}from"../../core/review/types.js";import{settleHumanRequest as v}from"../human/settle.js";import{retireCompanion as h}from"../companion-retire.js";import{companionHasQueuedWork as H,companionMidTurn as M,companionStillWorking as L}from"./companion.js";import{renderApprovalMessage as P}from"./deliver.js";function f(){return new Date().toISOString()}n(f,"now");function l(e,t){throw new w("review_terminal","review already has a terminal outcome",{state:e.state,requested:t,review:e})}n(l,"terminalConflict");function _(e){if(e.result_json===null)throw new Error(`approved review lacks its immutable result: ${e.review_id}`);return JSON.parse(e.result_json)}n(_,"approvedResult");function y(e){return`${e.review_dir}/result.json`}n(y,"resultPath");function Q(e){return{project:n(async t=>{D(await u(t.request_id),e)},"project"),retire:n(async(t,o)=>{await h(t,o)},"retire")}}n(Q,"effects");function V(e){return{label:"review approved by the user",disposition:null,body_json:{review_id:e.review_id,result_path:y(e),body:P(e,y(e))}}}n(V,"approvalNotification");function z(e,t,o){const a=o==="dismissed";return{label:a?"review dismissed":"review canceled",disposition:"human-canceled",body_json:{review_id:e.review_id,disposition:o,body:a?`The review of \`${e.document_name??e.file}\` was dismissed without approval.`:`The review of \`${e.document_name??e.file}\` was canceled${t===""?"":` \u2014 ${t}`}.`}}}n(z,"cancellationNotification");async function G(e,t,o){if(t.document_id===null||t.document_base===null)return;const a=n(async d=>await x(e,d),"reposFn"),i=await q(e,t.document_id),s=await k(e,{class:"person"},a),c=await C(e,s,i,{body:o},`edited in review ${t.review_id}`,t.document_base,{},a);if(!("stale"in c))return;const r=await $(e,i);if(!(r.length===1&&S(r[0].body_hash)===o))throw new w("stale_revision",`${t.document_name??i.name} changed since the review began; the reviewed text was not saved`,{review_id:t.review_id,document:t.document_name??i.name,base:t.document_base,heads_key:c.stale.heads_key,revision:c.stale.revision,next:`Read the current revision with \`crtr canvas read ${i.id}\`, then start a new review of it.`})}n(G,"saveReviewedDocument");async function b(e){if(e.state==="canceled")return l(e,"submit");if(e.state==="approved")return{outcome:"already_settled",record:e,result:_(e)};const t=O(e.file,"submit");e.document_id!==null&&await J(e.document_id);const{result:o,resultJson:a}=await j(async c=>{const r=await u(e.review_id);if(r.state==="canceled")return l(r,"submit");if(r.state==="approved")return{result:_(r),resultJson:r.result_json};const d=await N(c,r.review_id),m=A(r,d,t.sha256,f());return m.changed&&await G(c,r,new TextDecoder("utf-8").decode(t.bytes)),{result:m,resultJson:JSON.stringify(m)}}),i=await v({request_id:e.review_id,outcome:"approved",result_json:a,notification:V({...e,changed:o.changed}),post_commit:Q(o)});i.outcome==="settled"&&p();const s=await u(e.review_id);return i.outcome==="already_settled"&&s.state!=="approved"?l(s,"submit"):{outcome:i.outcome,record:s,result:i.outcome==="settled"?o:_(s)}}n(b,"approveReview");async function le(e){const t=await u(e);return t.state==="canceled"?l(t,"submit"):t.state==="open"&&await L(t.companion_node_id)?(await F(e,f())&&p(),{outcome:"awaiting_companion",record:await u(e)}):b(t)}n(le,"submitReviewApproval");async function K(e,t){const o=await W(e);if(o===null||o.state!=="open"||o.submit_requested_at===null||await H(o.companion_node_id)||!t.atCompanionTurnEnd&&await M(o.companion_node_id))return null;try{return await b(o)}catch(a){throw a instanceof w&&a.code==="stale_revision"&&(await E(e),p()),a}}n(K,"completeRequestedSubmit");async function pe(e){const t=await B(e);return t===null?null:K(t.review_id,{atCompanionTurnEnd:!0})}n(pe,"completeCompanionRequestedSubmit");async function we(e,t={}){const o=await u(e);if(o.state==="approved")return l(o,"cancel");const a=f(),i=t.reason??"",s=t.disposition??"canceled",c=JSON.stringify({schema:"humanloop.cancel/v1",kind:"canceled",canceledAt:a,...i===""?{}:{reason:i},...t.actor===void 0?{}:{actor:t.actor},...s==="dismissed"?{disposition:s}:{}}),r=await v({request_id:e,outcome:s==="dismissed"?"dismissed":"canceled",result_json:c,notification:z(o,i,s),post_commit:{project:n(async m=>{T(await u(m.request_id),JSON.parse(c))},"project"),retire:n(async(m,R)=>{await h(m,R)},"retire")}});r.outcome==="settled"&&p();const d=await u(e);return r.outcome==="already_settled"&&d.state==="approved"?l(d,"cancel"):{outcome:r.outcome,record:d}}n(we,"cancelReview");export{we as cancelReview,pe as completeCompanionRequestedSubmit,K as completeRequestedSubmit,le as submitReviewApproval};
1
+ var g=Object.defineProperty;var n=(e,t)=>g(e,"name",{value:t,configurable:!0});import{withCanvasWrite as j}from"../../core/canvas/db.js";import{get as S}from"../../core/graph/bodies.js";import{documentById as q,editDocument as C,heads as $}from"../../core/graph/documents.js";import{readerFor as k}from"../../core/graph/reader.js";import{warmDocumentWatchers as J}from"../api/reader.js";import{checkoutsForCwd as x}from"../../core/graph/repo-sync/repos.js";import{snapshotCommentsLocked as N}from"../../core/review/comments.js";import{readReviewSource as O}from"../../core/review/document.js";import{projectReviewCancellation as T,projectReviewResult as D,buildReviewResult as A}from"../../core/review/project.js";import{signalReviewActivity as p}from"../../core/review/signal.js";import{clearReviewSubmit as E,getReview as W,getReviewByCompanion as B,requestReviewSubmit as F,requireVisibleReview as u}from"../../core/review/store.js";import{ReviewOperationError as w}from"../../core/review/types.js";import{settleHumanRequest as v}from"../human/settle.js";import{retireCompanion as h}from"../companion-retire.js";import{companionHasQueuedWork as H,companionMidTurn as M,companionStillWorking as L}from"./companion.js";import{renderApprovalMessage as P}from"./deliver.js";function f(){return new Date().toISOString()}n(f,"now");function l(e,t){throw new w("review_terminal","review already has a terminal outcome",{state:e.state,requested:t,review:e})}n(l,"terminalConflict");function _(e){if(e.result_json===null)throw new Error(`approved review lacks its immutable result: ${e.review_id}`);return JSON.parse(e.result_json)}n(_,"approvedResult");function y(e){return`${e.review_dir}/result.json`}n(y,"resultPath");function Q(e){return{project:n(async t=>{await D(await u(t.request_id),e)},"project"),retire:n(async(t,o)=>{await h(t,o)},"retire")}}n(Q,"effects");function V(e){return{label:"review approved by the user",disposition:null,body_json:{review_id:e.review_id,result_path:y(e),body:P(e,y(e))}}}n(V,"approvalNotification");function z(e,t,o){const i=o==="dismissed";return{label:i?"review dismissed":"review canceled",disposition:"human-canceled",body_json:{review_id:e.review_id,disposition:o,body:i?`The review of \`${e.document_name??e.file}\` was dismissed without approval.`:`The review of \`${e.document_name??e.file}\` was canceled${t===""?"":` \u2014 ${t}`}.`}}}n(z,"cancellationNotification");async function G(e,t,o){if(t.document_id===null||t.document_base===null)return;const i=n(async d=>await x(e,d),"reposFn"),a=await q(e,t.document_id),s=await k(e,{class:"person"},i),c=await C(e,s,a,{body:o},`edited in review ${t.review_id}`,t.document_base,{},i);if(!("stale"in c))return;const r=await $(e,a);if(!(r.length===1&&await S(e,r[0].body_hash)===o))throw new w("stale_revision",`${t.document_name??a.name} changed since the review began; the reviewed text was not saved`,{review_id:t.review_id,document:t.document_name??a.name,base:t.document_base,heads_key:c.stale.heads_key,revision:c.stale.revision,next:`Read the current revision with \`crtr canvas read ${a.id}\`, then start a new review of it.`})}n(G,"saveReviewedDocument");async function b(e){if(e.state==="canceled")return l(e,"submit");if(e.state==="approved")return{outcome:"already_settled",record:e,result:_(e)};const t=await O(e.file,"submit");e.document_id!==null&&await J(e.document_id);const{result:o,resultJson:i}=await j(async c=>{const r=await u(e.review_id);if(r.state==="canceled")return l(r,"submit");if(r.state==="approved")return{result:_(r),resultJson:r.result_json};const d=await N(c,r.review_id),m=A(r,d,t.sha256,f());return m.changed&&await G(c,r,new TextDecoder("utf-8").decode(t.bytes)),{result:m,resultJson:JSON.stringify(m)}}),a=await v({request_id:e.review_id,outcome:"approved",result_json:i,notification:V({...e,changed:o.changed}),post_commit:Q(o)});a.outcome==="settled"&&p();const s=await u(e.review_id);return a.outcome==="already_settled"&&s.state!=="approved"?l(s,"submit"):{outcome:a.outcome,record:s,result:a.outcome==="settled"?o:_(s)}}n(b,"approveReview");async function le(e){const t=await u(e);return t.state==="canceled"?l(t,"submit"):t.state==="open"&&await L(t.companion_node_id)?(await F(e,f())&&p(),{outcome:"awaiting_companion",record:await u(e)}):b(t)}n(le,"submitReviewApproval");async function K(e,t){const o=await W(e);if(o===null||o.state!=="open"||o.submit_requested_at===null||await H(o.companion_node_id)||!t.atCompanionTurnEnd&&await M(o.companion_node_id))return null;try{return await b(o)}catch(i){throw i instanceof w&&i.code==="stale_revision"&&(await E(e),p()),i}}n(K,"completeRequestedSubmit");async function pe(e){const t=await B(e);return t===null?null:K(t.review_id,{atCompanionTurnEnd:!0})}n(pe,"completeCompanionRequestedSubmit");async function we(e,t={}){const o=await u(e);if(o.state==="approved")return l(o,"cancel");const i=f(),a=t.reason??"",s=t.disposition??"canceled",c=JSON.stringify({schema:"humanloop.cancel/v1",kind:"canceled",canceledAt:i,...a===""?{}:{reason:a},...t.actor===void 0?{}:{actor:t.actor},...s==="dismissed"?{disposition:s}:{}}),r=await v({request_id:e,outcome:s==="dismissed"?"dismissed":"canceled",result_json:c,notification:z(o,a,s),post_commit:{project:n(async m=>{await T(await u(m.request_id),JSON.parse(c))},"project"),retire:n(async(m,R)=>{await h(m,R)},"retire")}});r.outcome==="settled"&&p();const d=await u(e);return r.outcome==="already_settled"&&d.state==="approved"?l(d,"cancel"):{outcome:r.outcome,record:d}}n(we,"cancelReview");export{we as cancelReview,pe as completeCompanionRequestedSubmit,K as completeRequestedSubmit,le as submitReviewApproval};
@@ -1 +1 @@
1
- var o=Object.defineProperty;var i=(r,e)=>o(r,"name",{value:e,configurable:!0});import{requireVisibleReview as t}from"../../core/review/store.js";import{projectReviewResult as s}from"../../core/review/project.js";import{realizeReview as n}from"../../core/review/realize.js";import{retireCompanion as a}from"../companion-retire.js";import{sweepHumanRequests as m}from"../human/sweep.js";async function f(){return m({realizeReview:n,projectReview:i(async r=>{const e=await t(r.request_id);if(e.result_json===null)throw new Error(`approved review lacks its immutable result: ${e.review_id}`);s(e,JSON.parse(e.result_json))},"projectReview"),retireCompanion:i(async(r,e)=>{await a(r,e)},"retireCompanion")})}i(f,"sweepReviews");export{f as sweepReviews};
1
+ var t=Object.defineProperty;var i=(r,e)=>t(r,"name",{value:e,configurable:!0});import{requireVisibleReview as o}from"../../core/review/store.js";import{projectReviewResult as s}from"../../core/review/project.js";import{realizeReview as a}from"../../core/review/realize.js";import{retireCompanion as n}from"../companion-retire.js";import{sweepHumanRequests as m}from"../human/sweep.js";async function f(){return m({realizeReview:a,projectReview:i(async r=>{const e=await o(r.request_id);if(e.result_json===null)throw new Error(`approved review lacks its immutable result: ${e.review_id}`);await s(e,JSON.parse(e.result_json))},"projectReview"),retireCompanion:i(async(r,e)=>{await n(r,e)},"retireCompanion")})}i(f,"sweepReviews");export{f as sweepReviews};