@north-light/cli 0.3.532 → 0.3.534
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +6 -3
- package/dist/api/client.js +1 -1
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/canvas.d.ts +2 -0
- package/dist/api/dto/crons.d.ts +3 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +13 -0
- package/dist/builtin-memory/northlight-apps/providers/README.md +2 -0
- package/dist/builtin-memory/northlight-apps/providers/sms.md +2 -0
- package/dist/clients/attach/chrome/inbox-strip.d.ts +6 -1
- package/dist/clients/attach/chrome/inbox-strip.js +1 -1
- package/dist/clients/attach/chrome/roster.js +1 -1
- package/dist/clients/attach/session/chrome-refresh.d.ts +12 -3
- package/dist/clients/attach/session/chrome-refresh.js +1 -1
- package/dist/clients/attach/session/document-links.js +1 -1
- package/dist/clients/attach/viewer.js +586 -587
- package/dist/commands/api-client.js +3 -3
- package/dist/commands/canvas.js +1 -1
- package/dist/commands/node/inspect.js +6 -6
- package/dist/commands/sys/context/expose.js +2 -2
- package/dist/commands/sys/db.d.ts +1 -0
- package/dist/commands/sys/db.js +1 -0
- package/dist/commands/sys.js +1 -1
- package/dist/core/bash-guard.js +9 -3
- package/dist/core/canvas/canvas.d.ts +12 -2
- package/dist/core/canvas/canvas.js +23 -20
- package/dist/core/canvas/daemon-actions.d.ts +0 -3
- package/dist/core/canvas/daemon-actions.js +7 -24
- package/dist/core/canvas/daemon-owner.d.ts +3 -0
- package/dist/core/canvas/daemon-owner.js +1 -6
- package/dist/core/canvas/db.d.ts +28 -20
- package/dist/core/canvas/db.js +4 -3
- package/dist/core/canvas/deliveries.d.ts +5 -0
- package/dist/core/canvas/deliveries.js +25 -30
- package/dist/core/canvas/export-sqlite.d.ts +14 -0
- package/dist/core/canvas/export-sqlite.js +3 -0
- package/dist/core/canvas/import-sqlite.d.ts +44 -0
- package/dist/core/canvas/import-sqlite.js +7 -0
- package/dist/core/canvas/node-tokens.js +3 -3
- package/dist/core/canvas/outcome-actions.js +4 -4
- package/dist/core/canvas/recovery.js +1 -1
- package/dist/core/canvas/schema-pg.sql +1022 -0
- package/dist/core/canvas/schema-version.d.ts +4 -0
- package/dist/core/canvas/schema-version.js +1 -0
- package/dist/core/canvas/sessions.js +3 -3
- package/dist/core/canvas/structured-requests.js +2 -2
- package/dist/core/canvas/tree.js +6 -6
- package/dist/core/canvas/waits.js +1 -1
- package/dist/core/feed/inbox.js +1 -1
- package/dist/core/feed/messages.js +5 -5
- package/dist/core/feed/reports.js +2 -2
- package/dist/core/grants/mirror.js +2 -2
- package/dist/core/graph/access.js +7 -7
- package/dist/core/graph/custom.js +10 -10
- package/dist/core/graph/deletion.js +6 -6
- package/dist/core/graph/documents.d.ts +1 -1
- package/dist/core/graph/documents.js +9 -9
- package/dist/core/graph/edges.d.ts +3 -3
- package/dist/core/graph/edges.js +3 -1
- package/dist/core/graph/events.js +7 -7
- package/dist/core/graph/exposures.d.ts +8 -8
- package/dist/core/graph/jobs.d.ts +1 -1
- package/dist/core/graph/jobs.js +2 -2
- package/dist/core/graph/kinds/custom.js +1 -1
- package/dist/core/graph/kinds/document.js +4 -4
- package/dist/core/graph/kinds/dto.js +1 -1
- package/dist/core/graph/kinds/job.js +2 -2
- package/dist/core/graph/kinds/kind.d.ts +2 -2
- package/dist/core/graph/kinds/kind.js +1 -1
- package/dist/core/graph/kinds/live-read.d.ts +5 -0
- package/dist/core/graph/kinds/live-read.js +1 -0
- package/dist/core/graph/kinds/node.js +5 -5
- package/dist/core/graph/kinds/owner.js +2 -2
- package/dist/core/graph/names.js +3 -3
- package/dist/core/graph/objects.d.ts +5 -5
- package/dist/core/graph/objects.js +10 -10
- package/dist/core/graph/package-docs.js +6 -5
- package/dist/core/graph/reader.d.ts +1 -1
- package/dist/core/graph/reader.js +2 -2
- package/dist/core/graph/receivers.js +4 -5
- package/dist/core/graph/repo-sync/exchange.js +2 -2
- package/dist/core/graph/repo-sync/repos.js +3 -3
- package/dist/core/graph/repo-sync/sync.js +1 -1
- package/dist/core/graph/rules.d.ts +3 -4
- package/dist/core/graph/rules.js +3 -3
- package/dist/core/graph/spaces/app.js +1 -1
- package/dist/core/graph/spaces/index.d.ts +1 -1
- package/dist/core/graph/spaces/index.js +1 -1
- package/dist/core/graph/spaces/own-memory.js +1 -1
- package/dist/core/graph/spaces/person.js +3 -3
- package/dist/core/graph/spaces/repo.d.ts +1 -1
- package/dist/core/graph/spaces/repo.js +1 -1
- package/dist/core/graph/types.d.ts +4 -3
- package/dist/core/graph/watches.d.ts +2 -2
- package/dist/core/graph/watches.js +17 -17
- package/dist/core/human/page-schema.js +5 -4
- package/dist/core/human/requests.d.ts +1 -1
- package/dist/core/human/requests.js +20 -23
- package/dist/core/peer/ingest.js +1 -1
- package/dist/core/peer/receiver.js +1 -1
- package/dist/core/peer/stubs.js +1 -1
- package/dist/core/providers/receipts.js +3 -2
- package/dist/core/review/comments.js +5 -4
- package/dist/core/runs/activity.js +4 -5
- package/dist/core/runs/confinement.d.ts +1 -1
- package/dist/core/runs/confinement.js +2 -2
- package/dist/core/runs/events.d.ts +1 -1
- package/dist/core/runs/events.js +5 -5
- package/dist/core/runs/operations.js +6 -4
- package/dist/core/runs/questions.d.ts +2 -0
- package/dist/core/runs/questions.js +1 -1
- package/dist/core/runtime/close.d.ts +3 -0
- package/dist/core/runtime/close.js +1 -1
- package/dist/core/runtime/host.d.ts +5 -1
- package/dist/core/runtime/host.js +3 -3
- package/dist/core/runtime/launch.js +2 -2
- package/dist/core/runtime/ledger.js +3 -3
- package/dist/core/runtime/lifecycle.d.ts +9 -0
- package/dist/core/runtime/lifecycle.js +2 -2
- package/dist/core/runtime/nodes.d.ts +1 -1
- package/dist/core/runtime/nodes.js +1 -1
- package/dist/core/runtime/roadmap.js +1 -1
- package/dist/core/spaces/stop.d.ts +1 -0
- package/dist/core/spaces/stop.js +2 -2
- package/dist/core/statements.d.ts +17 -0
- package/dist/core/statements.js +2 -0
- package/dist/core/substrate/delivery/corpus.js +3 -3
- package/dist/core/substrate/delivery/deliver.js +5 -5
- package/dist/core/substrate/delivery/listings.js +1 -1
- package/dist/core/substrate/delivery/match.d.ts +2 -2
- package/dist/core/substrate/delivery/match.js +1 -1
- package/dist/core/substrate/delivery/plan.js +3 -3
- package/dist/core/substrate/delivery/render-boot.js +5 -5
- package/dist/core/substrate/delivery/render-event.js +6 -6
- package/dist/core/usage/ledger.js +8 -8
- package/dist/core/worktree.js +8 -8
- package/dist/daemon/api/bridge.js +4 -4
- package/dist/daemon/api/document-listener.js +1 -1
- package/dist/daemon/api/handlers/broker-ops.js +1 -1
- package/dist/daemon/api/handlers/canvas.js +1 -1
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/custom-objects.js +3 -3
- package/dist/daemon/api/handlers/docs.js +1 -1
- package/dist/daemon/api/handlers/inbox.js +1 -1
- package/dist/daemon/api/handlers/memory.js +7 -7
- package/dist/daemon/api/handlers/messages.js +1 -1
- package/dist/daemon/api/handlers/nodes.js +1 -1
- package/dist/daemon/api/handlers/objects.js +3 -3
- package/dist/daemon/api/handlers/questions.js +1 -1
- package/dist/daemon/api/handlers/reviews.js +1 -1
- package/dist/daemon/api/handlers/structured-requests.js +1 -1
- package/dist/daemon/api/reader.js +1 -1
- package/dist/daemon/broker-signals/bus.d.ts +14 -6
- package/dist/daemon/broker-signals/bus.js +1 -10
- package/dist/daemon/cron/ended.d.ts +3 -0
- package/dist/daemon/cron/ended.js +2 -2
- package/dist/daemon/cron/narrowed.d.ts +5 -0
- package/dist/daemon/cron/narrowed.js +2 -0
- package/dist/daemon/cron/settle.js +1 -1
- package/dist/daemon/crtrd-cli.js +1 -1
- package/dist/daemon/crtrd.js +12 -11
- package/dist/daemon/due.js +1 -1
- package/dist/daemon/export-sqlite-run.d.ts +1 -0
- package/dist/daemon/export-sqlite-run.js +4 -0
- package/dist/daemon/fleet.d.ts +1 -0
- package/dist/daemon/fleet.js +3 -3
- package/dist/daemon/human/settle.js +1 -1
- package/dist/daemon/manage.d.ts +10 -0
- package/dist/daemon/manage.js +3 -2
- package/dist/daemon/postgres/binary.d.ts +17 -0
- package/dist/daemon/postgres/binary.js +2 -0
- package/dist/daemon/postgres/cluster.d.ts +37 -0
- package/dist/daemon/postgres/cluster.js +6 -0
- package/dist/daemon/postgres/index.d.ts +2 -0
- package/dist/daemon/postgres/index.js +1 -0
- package/dist/daemon/postgres/pins.d.ts +7 -0
- package/dist/daemon/postgres/pins.js +1 -0
- package/dist/daemon/postgres/supervisor.d.ts +95 -0
- package/dist/daemon/postgres/supervisor.js +2 -0
- package/dist/daemon/reconcilers/bash-deadline.js +2 -2
- package/dist/daemon/reconcilers/delivery-lane.js +2 -2
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +7 -3
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +0 -6
- package/dist/daemon/reconcilers/storage-maintenance.js +1 -8
- package/dist/daemon/runs/observer.js +5 -4
- package/dist/migrations/004-canvas-documents/index.js +1 -1
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +2 -0
- package/dist/pi-extensions/canvas-doc-substrate.js +6 -6
- package/dist/pi-extensions/envelope-merge.d.ts +3 -2
- package/dist/pi-extensions/envelope-merge.js +9 -8
- package/dist/shared/env.d.ts +3 -0
- package/dist/shared/env.js +2 -2
- package/docs/apps/providers/README.md +2 -0
- package/docs/apps/providers/sms.md +2 -0
- package/docs/public-api.md +11 -2
- package/docs/sdk/release-notes.md +13 -0
- package/package.json +6 -6
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -26
- package/scripts/install-runtime.mjs +53 -3
- package/scripts/lib/isolation-env.mjs +3 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var D=Object.defineProperty;var h=(n,a)=>D(n,"name",{value:a,configurable:!0});import{createHash as A,timingSafeEqual as U}from"node:crypto";import{createServer as W}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}from"./reader.js";import{mergeDocumentLines as q}from"./document-merge.js";import{crtrVersion as ee}from"../../shared/crtr-version.js";const te="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(ee()),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=>A("sha256").update(i).digest(),"digest");return U(t(n),t(a))}h(P,"sameSecret");function ne(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(ne,"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 L(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(L,"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 oe(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(oe,"requestBody");async function ae(n){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.
|
|
1
|
+
var D=Object.defineProperty;var h=(n,a)=>D(n,"name",{value:a,configurable:!0});import{createHash as A,timingSafeEqual as U}from"node:crypto";import{createServer as W}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}from"./reader.js";import{mergeDocumentLines as q}from"./document-merge.js";import{crtrVersion as ee}from"../../shared/crtr-version.js";const te="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(ee()),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=>A("sha256").update(i).digest(),"digest");return U(t(n),t(a))}h(P,"sameSecret");function ne(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(ne,"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 L(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(L,"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 oe(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(oe,"requestBody");async function ae(n){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?q(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(ae,"save");let v="the document listener has not started";function $e(){return v}h($e,"documentListenerUnavailable");function Te(n,a){const t=`crtr_doc_${n}`,i=W((o,e)=>{(async()=>{e.setHeader("Content-Security-Policy",te),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=ne(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 L(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 oe(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 ae(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
|
|
2
2
|
|
|
3
3
|
`);const s=h(async _=>{if(_==="deleted"){e.write(`event: deleted
|
|
4
4
|
data: {}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var F=Object.defineProperty;var r=(e,t)=>F(e,"name",{value:t,configurable:!0});import{canonicalSessionFile as O,findNodeBySessionFile as R,getNode as y,isWarmSpare as T,listNodeIdentities as I,recordRespawnAttempt as $,recordSettledTurn as j,setPresence as b,subscriptionsOf as C,updateNode as g,updateNodeTelemetry as q}from"../../../core/canvas/canvas.js";import{recordExecutionPid as E}from"../../../core/canvas/executions.js";import{bindSession as M,readLiveSession as L,transferSession as D}from"../../../core/canvas/sessions.js";import{isSafeNodeId as G}from"../../../core/canvas/paths.js";import{appForNode as J}from"../../../core/canvas/node-agent-paths.js";import{isPidAlive as W}from"../../../core/canvas/pid.js";import{editorLabel as P}from"../../../core/canvas/labels.js";import{assembleNodeSubject as U}from"../../../core/substrate/subject.js";import{withCanvasWrite as l}from"../../../core/canvas/db.js";import{notFound as v,usage as s}from"../../../core/errors.js";import{hasUnseenWakeMail as N}from"../../../core/canvas/deliveries.js";import{clearYieldMessage as V}from"../../../core/runtime/kickoff.js";import{readRoadmap as Y}from"../../../core/runtime/roadmap.js";import{forkSourceNode as z}from"../../../core/runtime/nodes.js";import{transition as h}from"../../../core/runtime/lifecycle.js";import{focusOf as _,tearDownNode as x}from"../../../core/runtime/placement.js";import{hasAttachedBrokerViewers as H}from"../viewer-presence.js";import{evaluateStop as K}from"../../../core/runtime/stop-guard.js";import{clearFault as w,recordFault as Q}from"../../../core/canvas/recovery.js";import{updateModelRecipe as X}from"../../../core/runtime/model-swap.js";import{completeCompanionRequestedSubmit as Z}from"../../review/finish.js";import{emitEvent as ee}from"../../../core/events/emit.js";import{consumePendingPark as te,recordPendingParkActivity as ne}from"../../park-pending.js";import{shouldParkAfterSummary as ie}from"../../park-activity.js";import{ownerOnly as oe}from"../router.js";import{requireCurrentExecution as m}from"./broker-recovery.js";import{ensureRunObserver as re}from"../../runs/observer.js";import{parseNodeTelemetryRecord as ae,writeNodeTelemetryRecord as se}from"./telemetry-record.js";import{mapAsync as A}from"../../../core/async-array.js";function p(e,t){if(typeof e!="object"||e===null||Array.isArray(e))throw s(`${t} requires a JSON body`);return e}r(p,"objectBody");function de(e){const t=p(e,"broker session-bound");if(typeof t.executionId!="string"||t.executionId==="")throw s("executionId must be a non-empty string");if(typeof t.piSessionId!="string"||t.piSessionId==="")throw s("piSessionId must be a non-empty string");if(t.sessionFile!==null&&typeof t.sessionFile!="string")throw s("sessionFile must be a string or null");if(!Number.isSafeInteger(t.pid)||t.pid<=0)throw s("pid must be a positive safe integer");if(t.reason!==null&&typeof t.reason!="string")throw s("reason must be a string or null");if(!Array.isArray(t.reviewBoundaryIds)||!t.reviewBoundaryIds.every(n=>typeof n=="string"))throw s("reviewBoundaryIds must be an array of strings");return t}r(de,"parseSessionBoundBody");function ce(e){const t=p(e,"broker settle");if(typeof t.stopReason!="string")throw s("stopReason must be a string");for(const n of["backgroundJobsRunning","pushedFinal"])if(typeof t[n]!="boolean")throw s(`${n} must be a boolean`);return t}r(ce,"parseSettleBody");function ue(e){const t=p(e,"broker park-complete");if(t.outcome!=="completed"&&t.outcome!=="failed")throw s("outcome must be completed or failed");return t}r(ue,"parseParkCompleteBody");function le(e){const t=p(e,"broker telemetry");if(typeof t.expected_execution_id!="string"||t.expected_execution_id==="")throw s("expected_execution_id must be a non-empty string");const n=ae(t);return{...n,expected_execution_id:t.expected_execution_id,updated_at:new Date(n.updated_at).toISOString()}}r(le,"parseTelemetryBody");function pe(e){const t=p(e,"broker model");if(typeof t.spec!="string"||t.spec.trim()==="")throw s("spec must be a non-empty string");const n=t.intent;if(n!==void 0){const o=n!==null&&typeof n=="object"?n:void 0;if(o===void 0||typeof o.family!="string"||o.family===""||!["ultra","strong","medium","light"].includes(o.strength))throw s("intent must be { family, strength } with strength ultra|strong|medium|light when present")}if(typeof t.userSelected!="boolean")throw s("userSelected must be a boolean");return t}r(pe,"parseModelCommitBody");async function f(e){const t=await y(e);if(t===null)throw v(`unknown node: ${e}`,{received:e});return t}r(f,"requireNode");async function k(e){return{node_id:e.node_id,grantee:await J(e.node_id),name:e.name,...e.description===void 0?{}:{description:e.description},...e.title===void 0?{}:{title:e.title},...e.icon===void 0?{}:{icon:e.icon},kind:e.kind,mode:e.mode,lifecycle:e.lifecycle,status:e.status,cwd:e.cwd,parent:e.parent??null,fork_from:e.fork_from??null,profile_id:e.profile_id??null,...e.managed_worktree===void 0?{}:{managed_worktree:e.managed_worktree===null?null:{state:e.managed_worktree.state,...e.managed_worktree.cleanup===void 0?{}:{cleanup:e.managed_worktree.cleanup},path:e.managed_worktree.path,branch:e.managed_worktree.branch,base_ref:e.managed_worktree.base_ref,base_sha:e.managed_worktree.base_sha}},...e.review_binding===void 0?{}:{review_binding:e.review_binding??null},intent:e.intent??null,...e.persona_ack===void 0?{}:{persona_ack:e.persona_ack},created:e.created}}r(k,"extensionNode");async function me(e){const t=await f(e),n=[],o=new Set([e]);let i=t.parent??null;for(;i!==null&&i!==""&&!o.has(i);){o.add(i);const u=await y(i);if(u===null)break;n.unshift(await k(u)),i=u.parent??null}const a=await A((await A(await C(e),async u=>await y(u.node_id))).filter(u=>u!==null&&u.kind!=="human").sort((u,S)=>u.status===S.status?0:u.status==="active"?-1:S.status==="active"?1:0),k),c=typeof t.fork_source_file=="string"?await z(t.fork_source_file):null,d=await U(e);if(d===null)throw v(`unknown node: ${e}`,{received:e});return{node:await k(t),warm_spare:await T(e),ancestors:n,children:a,fork_source:c===null?null:await k(c),subject:d,report_nodes:await I(),roadmap:await Y(e)}}r(me,"extensionState");function B(e,t){return e?.kind===t.kind&&e?.mode===t.mode&&e?.lifecycle===t.lifecycle}r(B,"samePersona");function fe(e){const t=p(e,"broker generated-name");if(t.kind!=="initial"&&t.kind!=="recap")throw s("kind must be initial or recap");if(typeof t.description!="string"||typeof t.title!="string"||typeof t.icon!="string")throw s("description, title, and icon must be strings");if(t.kind==="initial")return t;const n=t.expected;if(typeof n!="object"||n===null||Array.isArray(n)||["name","description","title","icon","kind"].some(o=>typeof n[o]!="string"))throw s("recap expected must contain name, description, title, icon, and kind strings");return t}r(fe,"parseGeneratedNameBody");function we(e){const t=p(e,"broker persona-ack"),n=r(o=>{const i=t[o];if(typeof i!="object"||i===null||Array.isArray(i)||typeof i.kind!="string"||i.mode!=="base"&&i.mode!=="orchestrator"||i.lifecycle!=="terminal"&&i.lifecycle!=="resident")throw s(`${o} must be a persona state`);return i},"parse");return{expected_execution_id:t.expected_execution_id,from:n("from"),to:n("to")}}r(we,"parsePersonaAckBody");async function ye(e){const t=e.params.id,n=de(e.body),o=n.sessionFile===null?null:O(n.sessionFile);return{status:200,body:await l(async()=>{const a=await f(t);if(await E(n.executionId,t,n.pid)==="shutdown")return{action:"shutdown"};const c=a.review_binding?.review_id;if(c!==void 0&&!n.reviewBoundaryIds.includes(c))return{action:"shutdown"};if(n.reason==="new"&&a.parent==null)return{action:"relaunch_root"};if(n.reason!=="new"&&a.fork_from!=null&&a.pi_session_id==null&&o===null)return{action:"shutdown"};const d=o===null?null:await R(o);if(d!==null&&d.node_id!==t){if(W(d.pi_pid))return await Q(t,{link:"daemon\u2192node",op:"session-file-ownership",kind:"protocol",retry:{disposition:"fatal"},message:`refusing to bind ${o}: it is owned by live node ${d.node_id}`}),{action:"shutdown"};const u=await L(d.node_id);if(u===null||await D({fromNodeId:d.node_id,fromBindingId:u.bindingId,toNodeId:t,executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:o})!=="transferred")return{action:"shutdown"}}else if(await M(t,{executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:o})==="shutdown")return{action:"shutdown"};return n.reason!=="new"&&(await V(t),a.intent==="refresh"&&await h(t,"revive")),{action:"none"}})}}r(ye,"handleSessionBound");async function he(e,t){const n=await y(e);if(n===null){if(t.pushedFinal)return{action:"shutdown"};throw v(`unknown node: ${e}`,{received:e})}if(await j(e,new Date().toISOString()),(t.stopReason==="stop"||t.stopReason==="length")&&await $(e,{failures:0,notBefore:null}),t.stopReason==="quit")return(n.status==="active"||n.status==="idle")&&n.intent===null&&(await h(e,"finish"),await w(e),await _(e)!==null&&await x(e),await b(e,{pane:null,tmux_session:null,window:null})),{action:"shutdown"};if(t.stopReason!=="stop"&&t.stopReason!=="length")return await w(e,{link:"daemon\u2192node"}),n.intent==="refresh"?{action:"shutdown"}:{action:"stay_dormant"};const o={pushedFinal:t.pushedFinal};if(o.pushedFinal||n.status==="done")return await w(e),await _(e)!==null&&await x(e),await b(e,{pane:null,tmux_session:null,window:null}),{action:"shutdown"};if(n.intent==="refresh")return await w(e),{action:"shutdown"};const i=await K(e,o,t.backgroundJobsRunning);return await w(e),i.action==="reprompt"?{action:"reprompt",prompt:i.message}:i.reason==="finished"?await N(e)?{action:"stay_dormant"}:{action:"shutdown"}:i.reason!=="awaiting"&&i.reason!=="scheduled"?{action:"stay_dormant"}:await _(e)!==null||H(e)||await N(e)?{action:"stay_dormant"}:(await h(e,"release"),{action:"shutdown"})}r(he,"settleBroker");async function _e(e){const t=e.params.id,n=ce(e.body);await m(t,n.expected_execution_id);const o=await l(async()=>await he(t,n));return o.action!=="reprompt"&&Z(t),{status:200,body:o}}r(_e,"handleSettle");async function ke(e){const t=e.params.id,n=ue(e.body);return await m(t,n.expected_execution_id),{status:200,body:await l(async()=>{const i=te(t);if(i===void 0)return{action:"stay_dormant"};const a=await f(t);return await ie(t,a,i)?(await w(t),await h(t,"park"),await _(t)!==null&&await x(t),await b(t,{pane:null,tmux_session:null,window:null}),ee({level:n.outcome==="completed"?"info":"warn",event:"broker.park.complete",...G(t)?{node_id:t}:{},outcome:n.outcome==="completed"?"succeeded":"failed"}),{action:"shutdown"}):{action:"stay_dormant"}})}}r(ke,"handleParkComplete");async function be(e){const t=e.params.id;return await m(t,p(e.body,"broker park-activity").expected_execution_id),{status:200,body:await l(async()=>({activity:ne(t)?"recorded":"none"}))}}r(be,"handleParkActivity");async function ge(e){const t=e.params.id,n=le(e.body);return await m(t,n.expected_execution_id),await l(async()=>{await q(t,{tokensIn:n.tokens_in,contextTokens:n.context_tokens,lastActivity:n.last_activity,updatedAt:n.updated_at}),se(t,n)}),{status:204}}r(ge,"handleTelemetry");async function ve(e){const t=e.params.id,n=pe(e.body);return await m(t,n.expected_execution_id),{status:200,body:{modelOverride:(await l(async()=>await X(await f(t),n.spec,n.intent,n.userSelected))).model_override}}}r(ve,"handleModelCommit");async function xe(e){const t=e.params.id,n=await f(t);return n.run_id&&await re(t,n.run_id),{status:200,body:await me(t)}}r(xe,"handleExtensionState");async function Se(e){const t=e.params.id,n=fe(e.body);return await m(t,n.expected_execution_id),{status:200,body:await l(async()=>{const i=await f(t);if(n.kind==="initial"){if((i.description??"").trim()!=="")return{applied:!1};const d=await g(t,{description:n.description,...n.title.trim()===""?{}:{title:n.title},...n.icon.trim()===""?{}:{icon:n.icon}});return{applied:!0,editorLabel:P(d),description:d.description??"",title:d.title??"",icon:d.icon??""}}const a=n.expected;if(i.name!==a.name||(i.description??"")!==a.description||(i.title??"")!==a.title||(i.icon??"")!==a.icon||i.kind!==a.kind)return{applied:!1};const c=await g(t,{description:n.description,title:n.title.trim()===""?void 0:n.title,icon:n.icon.trim()===""?void 0:n.icon});return{applied:!0,editorLabel:P(c),description:c.description??"",title:c.title??"",icon:c.icon??""}})}}r(Se,"handleGeneratedName");async function Pe(e){const t=e.params.id,n=we(e.body);return await m(t,n.expected_execution_id),{status:200,body:await l(async()=>{const i=await f(t),a={kind:i.kind,mode:i.mode,lifecycle:i.lifecycle},c=i.persona_ack,d=c===void 0?a:{kind:c.kind??i.kind,mode:c.mode,lifecycle:c.lifecycle};return!B(a,n.to)||!B(d,n.from)?{applied:!1}:(await g(t,{persona_ack:n.to}),{applied:!0})})}}r(Pe,"handlePersonaAck");const nt=oe([{method:"POST",pattern:"/v1/nodes/:id/broker/session-bound",handler:ye},{method:"POST",pattern:"/v1/nodes/:id/broker/settle",handler:_e},{method:"POST",pattern:"/v1/nodes/:id/broker/park-complete",handler:ke},{method:"POST",pattern:"/v1/nodes/:id/broker/park-activity",handler:be},{method:"POST",pattern:"/v1/nodes/:id/broker/telemetry",handler:ge},{method:"POST",pattern:"/v1/nodes/:id/broker/model",handler:ve},{method:"GET",pattern:"/v1/nodes/:id/broker/extension-state",handler:xe},{method:"POST",pattern:"/v1/nodes/:id/broker/generated-name",handler:Se},{method:"POST",pattern:"/v1/nodes/:id/broker/persona-ack",handler:Pe}]);export{nt as brokerOperationRoutes,k as extensionNode};
|
|
1
|
+
var O=Object.defineProperty;var r=(e,t)=>O(e,"name",{value:t,configurable:!0});import{canonicalSessionFile as R,findNodeBySessionFile as T,getNode as y,isWarmSpare as I,listNodeIdentities as $,recordRespawnAttempt as j,recordSettledTurn as C,setPresence as b,subscriptionsOf as q,updateNode as g,updateNodeTelemetry as E}from"../../../core/canvas/canvas.js";import{recordExecutionPid as M}from"../../../core/canvas/executions.js";import{bindSession as L,readLiveSession as D,transferSession as G}from"../../../core/canvas/sessions.js";import{isSafeNodeId as J}from"../../../core/canvas/paths.js";import{appForNode as W}from"../../../core/canvas/node-agent-paths.js";import{isPidAlive as U}from"../../../core/canvas/pid.js";import{editorLabel as P}from"../../../core/canvas/labels.js";import{assembleNodeSubject as V}from"../../../core/substrate/subject.js";import{withCanvasWrite as l}from"../../../core/canvas/db.js";import{notFound as v,usage as s}from"../../../core/errors.js";import{hasUnseenWakeMail as N}from"../../../core/canvas/deliveries.js";import{clearYieldMessage as Y}from"../../../core/runtime/kickoff.js";import{readRoadmap as z}from"../../../core/runtime/roadmap.js";import{forkSourceNode as H}from"../../../core/runtime/nodes.js";import{transition as h}from"../../../core/runtime/lifecycle.js";import{focusOf as _,tearDownNode as x}from"../../../core/runtime/placement.js";import{hasAttachedBrokerViewers as K}from"../viewer-presence.js";import{evaluateStop as Q}from"../../../core/runtime/stop-guard.js";import{clearFault as w,recordFault as X}from"../../../core/canvas/recovery.js";import{updateModelRecipe as Z}from"../../../core/runtime/model-swap.js";import{completeCompanionRequestedSubmit as ee}from"../../review/finish.js";import{emitEvent as A}from"../../../core/events/emit.js";import{consumePendingPark as te,recordPendingParkActivity as ne}from"../../park-pending.js";import{shouldParkAfterSummary as ie}from"../../park-activity.js";import{ownerOnly as oe}from"../router.js";import{requireCurrentExecution as m}from"./broker-recovery.js";import{ensureRunObserver as re}from"../../runs/observer.js";import{parseNodeTelemetryRecord as ae,writeNodeTelemetryRecord as se}from"./telemetry-record.js";import{mapAsync as B}from"../../../core/async-array.js";function p(e,t){if(typeof e!="object"||e===null||Array.isArray(e))throw s(`${t} requires a JSON body`);return e}r(p,"objectBody");function de(e){const t=p(e,"broker session-bound");if(typeof t.executionId!="string"||t.executionId==="")throw s("executionId must be a non-empty string");if(typeof t.piSessionId!="string"||t.piSessionId==="")throw s("piSessionId must be a non-empty string");if(t.sessionFile!==null&&typeof t.sessionFile!="string")throw s("sessionFile must be a string or null");if(!Number.isSafeInteger(t.pid)||t.pid<=0)throw s("pid must be a positive safe integer");if(t.reason!==null&&typeof t.reason!="string")throw s("reason must be a string or null");if(!Array.isArray(t.reviewBoundaryIds)||!t.reviewBoundaryIds.every(n=>typeof n=="string"))throw s("reviewBoundaryIds must be an array of strings");return t}r(de,"parseSessionBoundBody");function ce(e){const t=p(e,"broker settle");if(typeof t.stopReason!="string")throw s("stopReason must be a string");for(const n of["backgroundJobsRunning","pushedFinal"])if(typeof t[n]!="boolean")throw s(`${n} must be a boolean`);return t}r(ce,"parseSettleBody");function ue(e){const t=p(e,"broker park-complete");if(t.outcome!=="completed"&&t.outcome!=="failed")throw s("outcome must be completed or failed");return t}r(ue,"parseParkCompleteBody");function le(e){const t=p(e,"broker telemetry");if(typeof t.expected_execution_id!="string"||t.expected_execution_id==="")throw s("expected_execution_id must be a non-empty string");const n=ae(t);return{...n,expected_execution_id:t.expected_execution_id,updated_at:new Date(n.updated_at).toISOString()}}r(le,"parseTelemetryBody");function pe(e){const t=p(e,"broker model");if(typeof t.spec!="string"||t.spec.trim()==="")throw s("spec must be a non-empty string");const n=t.intent;if(n!==void 0){const o=n!==null&&typeof n=="object"?n:void 0;if(o===void 0||typeof o.family!="string"||o.family===""||!["ultra","strong","medium","light"].includes(o.strength))throw s("intent must be { family, strength } with strength ultra|strong|medium|light when present")}if(typeof t.userSelected!="boolean")throw s("userSelected must be a boolean");return t}r(pe,"parseModelCommitBody");async function f(e){const t=await y(e);if(t===null)throw v(`unknown node: ${e}`,{received:e});return t}r(f,"requireNode");async function k(e){return{node_id:e.node_id,grantee:await W(e.node_id),name:e.name,...e.description===void 0?{}:{description:e.description},...e.title===void 0?{}:{title:e.title},...e.icon===void 0?{}:{icon:e.icon},kind:e.kind,mode:e.mode,lifecycle:e.lifecycle,status:e.status,cwd:e.cwd,parent:e.parent??null,fork_from:e.fork_from??null,profile_id:e.profile_id??null,...e.managed_worktree===void 0?{}:{managed_worktree:e.managed_worktree===null?null:{state:e.managed_worktree.state,...e.managed_worktree.cleanup===void 0?{}:{cleanup:e.managed_worktree.cleanup},path:e.managed_worktree.path,branch:e.managed_worktree.branch,base_ref:e.managed_worktree.base_ref,base_sha:e.managed_worktree.base_sha}},...e.review_binding===void 0?{}:{review_binding:e.review_binding??null},intent:e.intent??null,...e.persona_ack===void 0?{}:{persona_ack:e.persona_ack},created:e.created}}r(k,"extensionNode");async function me(e){const t=await f(e),n=[],o=new Set([e]);let i=t.parent??null;for(;i!==null&&i!==""&&!o.has(i);){o.add(i);const u=await y(i);if(u===null)break;n.unshift(await k(u)),i=u.parent??null}const a=await B((await B(await q(e),async u=>await y(u.node_id))).filter(u=>u!==null&&u.kind!=="human").sort((u,S)=>u.status===S.status?0:u.status==="active"?-1:S.status==="active"?1:0),k),c=typeof t.fork_source_file=="string"?await H(t.fork_source_file):null,d=await V(e);if(d===null)throw v(`unknown node: ${e}`,{received:e});return{node:await k(t),warm_spare:await I(e),ancestors:n,children:a,fork_source:c===null?null:await k(c),subject:d,report_nodes:await $(),roadmap:await z(e)}}r(me,"extensionState");function F(e,t){return e?.kind===t.kind&&e?.mode===t.mode&&e?.lifecycle===t.lifecycle}r(F,"samePersona");function fe(e){const t=p(e,"broker generated-name");if(t.kind!=="initial"&&t.kind!=="recap")throw s("kind must be initial or recap");if(typeof t.description!="string"||typeof t.title!="string"||typeof t.icon!="string")throw s("description, title, and icon must be strings");if(t.kind==="initial")return t;const n=t.expected;if(typeof n!="object"||n===null||Array.isArray(n)||["name","description","title","icon","kind"].some(o=>typeof n[o]!="string"))throw s("recap expected must contain name, description, title, icon, and kind strings");return t}r(fe,"parseGeneratedNameBody");function we(e){const t=p(e,"broker persona-ack"),n=r(o=>{const i=t[o];if(typeof i!="object"||i===null||Array.isArray(i)||typeof i.kind!="string"||i.mode!=="base"&&i.mode!=="orchestrator"||i.lifecycle!=="terminal"&&i.lifecycle!=="resident")throw s(`${o} must be a persona state`);return i},"parse");return{expected_execution_id:t.expected_execution_id,from:n("from"),to:n("to")}}r(we,"parsePersonaAckBody");async function ye(e){const t=e.params.id,n=de(e.body),o=n.sessionFile===null?null:R(n.sessionFile);return{status:200,body:await l(async()=>{const a=await f(t);if(await M(n.executionId,t,n.pid)==="shutdown")return{action:"shutdown"};const c=a.review_binding?.review_id;if(c!==void 0&&!n.reviewBoundaryIds.includes(c))return{action:"shutdown"};if(n.reason==="new"&&a.parent==null)return{action:"relaunch_root"};if(n.reason!=="new"&&a.fork_from!=null&&a.pi_session_id==null&&o===null)return{action:"shutdown"};const d=o===null?null:await T(o);if(d!==null&&d.node_id!==t){if(U(d.pi_pid))return await X(t,{link:"daemon\u2192node",op:"session-file-ownership",kind:"protocol",retry:{disposition:"fatal"},message:`refusing to bind ${o}: it is owned by live node ${d.node_id}`}),{action:"shutdown"};const u=await D(d.node_id);if(u===null||await G({fromNodeId:d.node_id,fromBindingId:u.bindingId,toNodeId:t,executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:o})!=="transferred")return{action:"shutdown"}}else if(await L(t,{executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:o})==="shutdown")return{action:"shutdown"};return n.reason!=="new"&&(await Y(t),a.intent==="refresh"&&await h(t,"revive")),{action:"none"}})}}r(ye,"handleSessionBound");async function he(e,t){const n=await y(e);if(n===null){if(t.pushedFinal)return{action:"shutdown"};throw v(`unknown node: ${e}`,{received:e})}if(await C(e,new Date().toISOString()),(t.stopReason==="stop"||t.stopReason==="length")&&await j(e,{failures:0,notBefore:null}),t.stopReason==="quit")return(n.status==="active"||n.status==="idle")&&n.intent===null&&(await h(e,"finish"),await w(e),await _(e)!==null&&await x(e),await b(e,{pane:null,tmux_session:null,window:null})),{action:"shutdown"};if(t.stopReason!=="stop"&&t.stopReason!=="length")return await w(e,{link:"daemon\u2192node"}),n.intent==="refresh"?{action:"shutdown"}:{action:"stay_dormant"};const o={pushedFinal:t.pushedFinal};if(o.pushedFinal||n.status==="done")return await w(e),await _(e)!==null&&await x(e),await b(e,{pane:null,tmux_session:null,window:null}),{action:"shutdown"};if(n.intent==="refresh")return await w(e),{action:"shutdown"};const i=await Q(e,o,t.backgroundJobsRunning);return await w(e),i.action==="reprompt"?{action:"reprompt",prompt:i.message}:i.reason==="finished"?await N(e)?{action:"stay_dormant"}:{action:"shutdown"}:i.reason!=="awaiting"&&i.reason!=="scheduled"?{action:"stay_dormant"}:await _(e)!==null||K(e)||await N(e)?{action:"stay_dormant"}:(await h(e,"release"),{action:"shutdown"})}r(he,"settleBroker");async function _e(e){const t=e.params.id,n=ce(e.body);await m(t,n.expected_execution_id);const o=await l(async()=>await he(t,n));return o.action!=="reprompt"&&ee(t).catch(i=>A({level:"error",event:"api.broker_ops.companion_submit_failed",node_id:t,error:i})),{status:200,body:o}}r(_e,"handleSettle");async function ke(e){const t=e.params.id,n=ue(e.body);return await m(t,n.expected_execution_id),{status:200,body:await l(async()=>{const i=te(t);if(i===void 0)return{action:"stay_dormant"};const a=await f(t);return await ie(t,a,i)?(await w(t),await h(t,"park"),await _(t)!==null&&await x(t),await b(t,{pane:null,tmux_session:null,window:null}),A({level:n.outcome==="completed"?"info":"warn",event:"broker.park.complete",...J(t)?{node_id:t}:{},outcome:n.outcome==="completed"?"succeeded":"failed"}),{action:"shutdown"}):{action:"stay_dormant"}})}}r(ke,"handleParkComplete");async function be(e){const t=e.params.id;return await m(t,p(e.body,"broker park-activity").expected_execution_id),{status:200,body:await l(async()=>({activity:ne(t)?"recorded":"none"}))}}r(be,"handleParkActivity");async function ge(e){const t=e.params.id,n=le(e.body);return await m(t,n.expected_execution_id),await l(async()=>{await E(t,{tokensIn:n.tokens_in,contextTokens:n.context_tokens,lastActivity:n.last_activity,updatedAt:n.updated_at}),se(t,n)}),{status:204}}r(ge,"handleTelemetry");async function ve(e){const t=e.params.id,n=pe(e.body);return await m(t,n.expected_execution_id),{status:200,body:{modelOverride:(await l(async()=>await Z(await f(t),n.spec,n.intent,n.userSelected))).model_override}}}r(ve,"handleModelCommit");async function xe(e){const t=e.params.id,n=await f(t);return n.run_id&&await re(t,n.run_id),{status:200,body:await me(t)}}r(xe,"handleExtensionState");async function Se(e){const t=e.params.id,n=fe(e.body);return await m(t,n.expected_execution_id),{status:200,body:await l(async()=>{const i=await f(t);if(n.kind==="initial"){if((i.description??"").trim()!=="")return{applied:!1};const d=await g(t,{description:n.description,...n.title.trim()===""?{}:{title:n.title},...n.icon.trim()===""?{}:{icon:n.icon}});return{applied:!0,editorLabel:P(d),description:d.description??"",title:d.title??"",icon:d.icon??""}}const a=n.expected;if(i.name!==a.name||(i.description??"")!==a.description||(i.title??"")!==a.title||(i.icon??"")!==a.icon||i.kind!==a.kind)return{applied:!1};const c=await g(t,{description:n.description,title:n.title.trim()===""?void 0:n.title,icon:n.icon.trim()===""?void 0:n.icon});return{applied:!0,editorLabel:P(c),description:c.description??"",title:c.title??"",icon:c.icon??""}})}}r(Se,"handleGeneratedName");async function Pe(e){const t=e.params.id,n=we(e.body);return await m(t,n.expected_execution_id),{status:200,body:await l(async()=>{const i=await f(t),a={kind:i.kind,mode:i.mode,lifecycle:i.lifecycle},c=i.persona_ack,d=c===void 0?a:{kind:c.kind??i.kind,mode:c.mode,lifecycle:c.lifecycle};return!F(a,n.to)||!F(d,n.from)?{applied:!1}:(await g(t,{persona_ack:n.to}),{applied:!0})})}}r(Pe,"handlePersonaAck");const nt=oe([{method:"POST",pattern:"/v1/nodes/:id/broker/session-bound",handler:ye},{method:"POST",pattern:"/v1/nodes/:id/broker/settle",handler:_e},{method:"POST",pattern:"/v1/nodes/:id/broker/park-complete",handler:ke},{method:"POST",pattern:"/v1/nodes/:id/broker/park-activity",handler:be},{method:"POST",pattern:"/v1/nodes/:id/broker/telemetry",handler:ge},{method:"POST",pattern:"/v1/nodes/:id/broker/model",handler:ve},{method:"GET",pattern:"/v1/nodes/:id/broker/extension-state",handler:xe},{method:"POST",pattern:"/v1/nodes/:id/broker/generated-name",handler:Se},{method:"POST",pattern:"/v1/nodes/:id/broker/persona-ack",handler:Pe}]);export{nt as brokerOperationRoutes,k as extensionNode};
|
|
@@ -3,4 +3,4 @@ var X=Object.defineProperty;var l=(t,e)=>X(t,"name",{value:e,configurable:!0});i
|
|
|
3
3
|
`).forEach((u,g)=>{o.test(u)&&c.push({ref:a.ref,node:`${a.nodeName} (${a.nodeId})`,ts:a.ts,line:g+1,text:u.trim()})});c.sort((a,s)=>a.ref.localeCompare(s.ref)||a.line-s.line);const p=c.slice(i,i+r),f=i+r<c.length?J(i+r):null;return{status:200,body:{hits:p,next_cursor:f,total:c.length}}}l(Pe,"handleHistoryGrep");async function Fe(t){const e=Ae(t.body),r=(e.query??"").trim(),n=e.weigh_body===!0,i=e.full===!0,o=Math.max(1,e.snippet_lines??1),c=Math.min(100,Math.max(1,e.limit??20)),p=e.per_node===!0;if(p&&(e.nodes===void 0||e.nodes.length===0))throw new T(400,"invalid_request","`per_node` requires a non-empty `nodes` list",{param:"nodes"});const f=await B(e,t),y=e.cursor!==void 0?K(e.cursor):0,a=r.toLowerCase().split(/\s+/).filter(d=>d.length>0);let s=f,u;const g=e.sort??(a.length>0?"relevance":"recency");if(a.length>0&&g==="relevance"){u=new Map;const d=[];for(const m of f){const E=m.title.toLowerCase(),Y=m.nodeName.toLowerCase(),V=m.nodeDesc.toLowerCase(),P=n?m.loadBody().toLowerCase():null;let k=0;for(const j of a)E.includes(j)&&(k+=10),Y.includes(j)&&(k+=5),V.includes(j)&&(k+=3),P!==null&&P.includes(j)&&(k+=1);k>0&&(u.set(m.ref,k),d.push(m))}s=d}else a.length>0&&(s=f.filter(d=>{const m=`${d.title}
|
|
4
4
|
${d.nodeName}
|
|
5
5
|
${d.nodeDesc}${n?`
|
|
6
|
-
`+d.loadBody():""}`.toLowerCase();return a.some(E=>m.includes(E))}));let b=Ee(s,g,u);if(p){const d=new Set;b=b.filter(m=>!d.has(m.nodeId)&&d.add(m.nodeId))}const v=p?b:b.slice(y,y+c),h=!p&&y+c<b.length?J(y+c):null;return{status:200,body:{hits:v.map(d=>{const m={ref:d.ref,node:`${d.nodeName} (${d.nodeId})`,kind:d.nodeKind,source:d.source,ts:d.ts,...p?{node_id:d.nodeId}:{}};return g==="relevance"&&u!==void 0&&(m.score=u.get(d.ref)??0),i?m.body=d.loadBody():m.snippet=Oe(d,a,o),m}),next_cursor:h,total:b.length}}}l(Fe,"handleHistorySearch");async function Ge(t){const e=(t.query.get("ref")??"").trim();if(!e.includes(":"))throw _(`ref must be <node-id>:inbox/<n>, <node-id>:session, or <node-id>:session/<n>; received: ${e}`,{received:e,next:"Get a valid ref from `crtr canvas history search`. Reports and documents are `crtr canvas read <name>`."});const r=await me(e);if(r===null)throw be(`no inbox entry or message for ref: ${e}`,{ref:e,next:"Re-run `crtr canvas history search` for a current ref. Reports and documents are `crtr canvas read <name>`."});if((await z(t))?.has(r.nodeId)===!1)throw new T(404,"not_found","artifact not found");const n=r.source,o=t.query.get("format")==="raw"&&r.raw!==null?r.raw:r.body;return{status:200,body:{ref:e,node:`${r.nodeName} (${r.nodeId})`,source:n,ts:r.ts,content:o}}}l(Ge,"handleHistoryRead");function De(t){for(const[n]of t.query)if(n!=="live")throw _(`unknown snapshot query parameter: ${n}`);const e=t.query.getAll("live");if(e.length>1)throw _("live must appear at most once");const r=e[0];if(r===void 0||r==="false"||r==="0")return!1;if(r==="true"||r==="1")return!0;throw _("live must be true or false")}l(De,"snapshotLiveOnly");async function qe(t){const e=new Set((await H()).map(a=>a.node_id)),r=await N(t),n=(await le(D,e,De(t))).filter(a=>r===null||r.has(a.node_id)),i=await q(n.map(a=>a.node_id));await ce(D,n,i);const o=await x(n,async a=>{const s=a.srcRow,u=s?.host_kind??"tmux";return{node_id:a.node_id,name:a.name,title:a.handle??a.name,description:a.description??null,kind:a.kind,mode:a.mode,lifecycle:a.lifecycle??s?.lifecycle,status:a.status,cwd:a.cwd,profile_id:a.profile_id??null,parent:s?.parent??null,created:a.created,host_kind:u,enterable:u==="broker",attention_count:a.asks,...s?.cycles!=null?{cycles:s.cycles}:{},...a.mtimeMs!==void 0?{last_activity:new Date(a.mtimeMs).toISOString()}:{},ctx_tokens:a.ctx_tokens,streaming:a.streaming??!1,jobs_active:s!==void 0&&de(s.pi_pid)?ae(await ie(a.node_id)).length:0,waiting_for:(await re(a.node_id))?.controllerId??null,hanging:a.hanging??null,viewed:a.viewed??!1,final_report:s?.final_report??null,finalized_at:s?.finalized_at??null}}),c=new Map;for(const a of await S()){const s=c.get(a.from_id);s===void 0?c.set(a.from_id,[a.to_id]):s.push(a.to_id)}const p=new Set(n.map(a=>a.node_id)),f=Object.fromEntries(n.map(a=>[a.node_id,(c.get(a.node_id)??[]).filter(s=>p.has(s))]));return{status:200,body:{generated_at:I(),nodes:o,subscriptions:f}}}l(qe,"handleSnapshot");async function U(){const t=(await se()).map(n=>{const i=n.host_kind??"tmux";return{node_id:n.node_id,name:n.name,kind:n.kind,mode:n.mode,lifecycle:n.lifecycle,status:n.status,cwd:n.cwd,host_kind:i,enterable:i==="broker",parent:n.parent,created:n.created}}),e=(await S()).map(n=>({from_id:n.from_id,to_id:n.to_id,active:n.active,created:n.created})),r=(await oe()).map(n=>({node_id:n.nodeId,object_id:n.objectId,object_type:n.objectType,object_name:n.objectName,created:new Date(n.createdAt).toISOString()}));return{generated_at:I(),nodes:t,edges:e,object_waits:r}}l(U,"buildRoster");let R=null,M=null;function He(){return R===null?(R=Promise.resolve().then(async()=>await U()).finally(()=>{R=null}),R):(M===null&&(M=R.then(async()=>await U()).finally(()=>{M=null})),M)}l(He,"rosterCoalesced");async function xe(t){const e=await He(),r=await N(t);return r===null?{status:200,body:e}:{status:200,body:{...e,nodes:e.nodes.filter(n=>r.has(n.node_id)),edges:e.edges.filter(n=>r.has(n.from_id)&&r.has(n.to_id)),object_waits:(e.object_waits??[]).filter(n=>r.has(n.node_id))}}}l(xe,"handleRoster");function Q(t){if(t.name!==""&&t.name!==t.kind)return t.name;const e=(t.description??"").trim();return e!==""?e:t.name}l(Q,"liveRootName");const ze=1440*6e4;async function Je(){const t=await F(),e=new Map(t.map(s=>[s.node_id,s])),r=new Map((await(await ge()).prepare("SELECT node_id, reply_at, read_at, intent FROM nodes WHERE parent IS NULL").all()).map(s=>[s.node_id,s])),n=await Re(),i=new Map;for(const s of n)i.set(s.requester_node_id,(i.get(s.requester_node_id)??0)+1);const o=new Date(Date.now()-ze).toISOString(),c=l(s=>{const u=r.get(s.node_id);return u?.reply_at!=null&&(u.read_at==null||u.reply_at>u.read_at)||(i.get(s.node_id)??0)>0?!0:s.status!=="done"||u?.intent!=="parked"?!1:[s.created,s.telemetry_updated_at,u.reply_at].reduce((v,h)=>h!=null&&h>v?h:v,"")>=o},"keepFinished"),p=t.filter(s=>s.parent===null&&s.kind!=="human"&&(we(s.status)||c(s))),f=new Map;for(const s of await S())f.set(s.from_id,[...f.get(s.from_id)??[],s.to_id]);const y=l(s=>{const u=e.get(s)?.fault;return u&&u.link!=="broker\u2194crtrd"?Ne(u):null},"faultOf"),a=await x(p,async s=>{const u=[s.node_id,...await ve(s.node_id)].filter(d=>e.get(d)?.kind!=="human"&&e.has(d)),g=new Set(u),b=n.filter(d=>g.has(d.requester_node_id)),v=r.get(s.node_id),h=v?.reply_at??null,O=v?.read_at??null;let w=s.created;for(const d of u){const m=e.get(d).telemetry_updated_at;m!=null&&m>w&&(w=m)}return h!==null&&h>w&&(w=h),{node_id:s.node_id,name:Q(s),cwd:s.cwd,profile_id:s.profile_id,status:s.status,state_line:(()=>{const d=ke(s.node_id)?.state.trim();return d||null})(),goal:(await Se(s.node_id))?.trim()||null,asks:b.length,first_ask_ticket_id:b[0]?.inbox_ticket_id??null,fault:u.map(y).find(d=>d!==null)??null,running_count:u.filter(d=>e.get(d).streaming===!0).length,unread:h!==null&&(O===null||h>O),last_activity:w,children:(f.get(s.node_id)??[]).filter(d=>e.has(d)&&e.get(d).kind!=="human").map(d=>{const m=e.get(d);return{node_id:d,name:Q(m),status:m.status,streaming:m.streaming===!0,asks:i.get(d)??0,fault:y(d)}})}});return{generated_at:I(),rows:a}}l(Je,"buildLiveRoots");let C=null,L=null;function Ke(){const t=l(()=>Promise.resolve().then(Je),"run");return C===null?(C=t().finally(()=>{C=null}),C):(L===null&&(L=C.then(t).finally(()=>{L=null})),L)}l(Ke,"liveRootsCoalesced");async function We(t){if(!je(t.principal))throw new T(403,"permission_error","only the person reads the dashboard");const e=await Ke(),r=t.query.get("profile");return{status:200,body:r===null?e:{...e,rows:e.rows.filter(n=>n.profile_id===r)}}}l(We,"handleLiveRoots");async function Ue(t){const e=(await $(await F(),async o=>t.principal.class==="person"||await A(t.principal,o.grantee??"app:terminal",o.node_id))).map(o=>({node_id:o.node_id,name:o.name,description:o.description,kind:o.kind,mode:o.mode,lifecycle:o.lifecycle,status:o.status,cwd:o.cwd,profile_id:o.profile_id,grantee:o.grantee??"app:terminal",parent:o.parent,created:o.created,frozen_at:o.frozen_at??null,terminal_reason:o.terminal_reason??null,final_report:o.final_report??null,telemetry_tokens_in:o.telemetry_tokens_in??null,telemetry_updated_at:o.telemetry_updated_at??null,fault:o.fault&&o.fault.link!=="broker\u2194crtrd"?{...o.fault,link:o.fault.link}:null,streaming:o.streaming??!1})),r=new Set(e.map(o=>o.node_id)),n=(await S()).filter(o=>r.has(o.from_id)&&r.has(o.to_id));return{status:200,rawBody:Z(JSON.stringify({nodes:e,edges:n}),{level:1}),headers:{"content-type":"application/gzip"}}}l(Ue,"handleBrowse");async function Qe(t){const e=await N(t);return{status:200,body:{generated_at:I(),nodes:(await ee()).filter(n=>e===null||e.has(n.node_id)).map(Ce),edges:(await S()).filter(n=>e===null||e.has(n.from_id)&&e.has(n.to_id)).map(n=>({from_id:n.from_id,to_id:n.to_id,active:n.active,created:n.created})),focused_node_ids:(await H()).map(n=>n.node_id).filter(n=>e===null||e.has(n))}}}l(Qe,"handleGraph");function Ye(t){if(t==null)return{};if(typeof t!="object")throw _("prune body must be a JSON object");const e=t,r=l(a=>{const s=e[a];if(s!==void 0){if(typeof s!="number"||!Number.isFinite(s))throw _(`${a} must be a number`);return s}},"num"),n=l(a=>{const s=e[a];if(s!==void 0){if(typeof s!="boolean")throw _(`${a} must be a boolean`);return s}},"bool"),i={},o=r("limit");o!==void 0&&(i.limit=o);const c=r("ttl_days");c!==void 0&&(i.ttl_days=c);const p=n("include_stale");p!==void 0&&(i.include_stale=p);const f=n("empty");f!==void 0&&(i.empty=f);const y=n("dry_run");return y!==void 0&&(i.dry_run=y),i}l(Ye,"parsePruneBody");async function Ve(t){const e=Ye(t.body),r=e.ttl_days??14,n=e.dry_run??!1,i=await he(ye());let o;return e.limit!==void 0?o=(await ne({maxNodes:e.limit,held:i,dryRun:n})).pruned.map(f=>({node_id:f.node_id,status:f.status,created:f.created})):e.empty===!0?o=(await _e({dryRun:n})).map(f=>({node_id:f,status:"empty",created:""})):o=(await te({ttlDays:r,held:i,dryRun:n,includeStale:e.include_stale??!1})).pruned.map(f=>({node_id:f.node_id,status:f.status,created:f.created})),{status:200,body:{pruned:o,dry_run:n,ttl_days:r}}}l(Ve,"handlePrune");const Rt=[{method:"GET",pattern:"/v1/canvas/attention",handler:Me},{method:"POST",pattern:"/v1/canvas/attention/counts",handler:Le},{method:"POST",pattern:"/v1/canvas/history/search",handler:Fe},{method:"POST",pattern:"/v1/canvas/history/grep",handler:Pe},{method:"GET",pattern:"/v1/canvas/history/read",handler:Ge},{method:"POST",pattern:"/v1/canvas/history/stats",handler:Be},{method:"GET",pattern:"/v1/canvas/snapshot",handler:qe},{method:"GET",pattern:"/v1/canvas/roster",handler:xe},{method:"GET",pattern:"/v1/canvas/live-roots",handler:We},{method:"GET",pattern:"/v1/canvas/browse",handler:Ue},{method:"GET",pattern:"/v1/canvas/graph",handler:Qe},{method:"POST",pattern:"/v1/canvas/prune",ownerOnly:!0,handler:Ve}];export{Rt as canvasRoutes,Ue as handleBrowse};
|
|
6
|
+
`+d.loadBody():""}`.toLowerCase();return a.some(E=>m.includes(E))}));let b=Ee(s,g,u);if(p){const d=new Set;b=b.filter(m=>!d.has(m.nodeId)&&d.add(m.nodeId))}const v=p?b:b.slice(y,y+c),h=!p&&y+c<b.length?J(y+c):null;return{status:200,body:{hits:v.map(d=>{const m={ref:d.ref,node:`${d.nodeName} (${d.nodeId})`,kind:d.nodeKind,source:d.source,ts:d.ts,...p?{node_id:d.nodeId}:{}};return g==="relevance"&&u!==void 0&&(m.score=u.get(d.ref)??0),i?m.body=d.loadBody():m.snippet=Oe(d,a,o),m}),next_cursor:h,total:b.length}}}l(Fe,"handleHistorySearch");async function Ge(t){const e=(t.query.get("ref")??"").trim();if(!e.includes(":"))throw _(`ref must be <node-id>:inbox/<n>, <node-id>:session, or <node-id>:session/<n>; received: ${e}`,{received:e,next:"Get a valid ref from `crtr canvas history search`. Reports and documents are `crtr canvas read <name>`."});const r=await me(e);if(r===null)throw be(`no inbox entry or message for ref: ${e}`,{ref:e,next:"Re-run `crtr canvas history search` for a current ref. Reports and documents are `crtr canvas read <name>`."});if((await z(t))?.has(r.nodeId)===!1)throw new T(404,"not_found","artifact not found");const n=r.source,o=t.query.get("format")==="raw"&&r.raw!==null?r.raw:r.body;return{status:200,body:{ref:e,node:`${r.nodeName} (${r.nodeId})`,source:n,ts:r.ts,content:o}}}l(Ge,"handleHistoryRead");function De(t){for(const[n]of t.query)if(n!=="live")throw _(`unknown snapshot query parameter: ${n}`);const e=t.query.getAll("live");if(e.length>1)throw _("live must appear at most once");const r=e[0];if(r===void 0||r==="false"||r==="0")return!1;if(r==="true"||r==="1")return!0;throw _("live must be true or false")}l(De,"snapshotLiveOnly");async function qe(t){const e=new Set((await H()).map(a=>a.node_id)),r=await N(t),n=(await le(D,e,De(t))).filter(a=>r===null||r.has(a.node_id)),i=await q(n.map(a=>a.node_id));await ce(D,n,i);const o=await x(n,async a=>{const s=a.srcRow,u=s?.host_kind??"tmux";return{node_id:a.node_id,name:a.name,title:a.handle??a.name,description:a.description??null,kind:a.kind,mode:a.mode,lifecycle:a.lifecycle??s?.lifecycle,status:a.status,cwd:a.cwd,profile_id:a.profile_id??null,parent:s?.parent??null,created:a.created,host_kind:u,enterable:u==="broker",attention_count:a.asks,...s?.cycles!=null?{cycles:s.cycles}:{},...a.mtimeMs!==void 0?{last_activity:new Date(a.mtimeMs).toISOString()}:{},ctx_tokens:a.ctx_tokens,streaming:a.streaming??!1,jobs_active:s!==void 0&&de(s.pi_pid)?ae(await ie(a.node_id)).length:0,waiting_for:(await re(a.node_id))?.controllerId??null,hanging:a.hanging??null,viewed:a.viewed??!1,final_report:s?.final_report??null,finalized_at:s?.finalized_at??null}}),c=new Map;for(const a of await S()){const s=c.get(a.from_id);s===void 0?c.set(a.from_id,[a.to_id]):s.push(a.to_id)}const p=new Set(n.map(a=>a.node_id)),f=Object.fromEntries(n.map(a=>[a.node_id,(c.get(a.node_id)??[]).filter(s=>p.has(s))]));return{status:200,body:{generated_at:I(),nodes:o,subscriptions:f}}}l(qe,"handleSnapshot");async function U(){const t=(await se()).map(n=>{const i=n.host_kind??"tmux";return{node_id:n.node_id,name:n.name,kind:n.kind,mode:n.mode,lifecycle:n.lifecycle,status:n.status,cwd:n.cwd,host_kind:i,enterable:i==="broker",parent:n.parent,created:n.created,description:n.description}}),e=(await S()).map(n=>({from_id:n.from_id,to_id:n.to_id,active:n.active,created:n.created})),r=(await oe()).map(n=>({node_id:n.nodeId,object_id:n.objectId,object_type:n.objectType,object_name:n.objectName,created:new Date(n.createdAt).toISOString()}));return{generated_at:I(),nodes:t,edges:e,object_waits:r}}l(U,"buildRoster");let R=null,M=null;function He(){return R===null?(R=Promise.resolve().then(async()=>await U()).finally(()=>{R=null}),R):(M===null&&(M=R.then(async()=>await U()).finally(()=>{M=null})),M)}l(He,"rosterCoalesced");async function xe(t){const e=await He(),r=await N(t);return r===null?{status:200,body:e}:{status:200,body:{...e,nodes:e.nodes.filter(n=>r.has(n.node_id)),edges:e.edges.filter(n=>r.has(n.from_id)&&r.has(n.to_id)),object_waits:(e.object_waits??[]).filter(n=>r.has(n.node_id))}}}l(xe,"handleRoster");function Q(t){if(t.name!==""&&t.name!==t.kind)return t.name;const e=(t.description??"").trim();return e!==""?e:t.name}l(Q,"liveRootName");const ze=1440*6e4;async function Je(){const t=await F(),e=new Map(t.map(s=>[s.node_id,s])),r=new Map((await(await ge()).prepare("SELECT node_id, reply_at, read_at, intent FROM nodes WHERE parent IS NULL").all()).map(s=>[s.node_id,s])),n=await Re(),i=new Map;for(const s of n)i.set(s.requester_node_id,(i.get(s.requester_node_id)??0)+1);const o=new Date(Date.now()-ze).toISOString(),c=l(s=>{const u=r.get(s.node_id);return u?.reply_at!=null&&(u.read_at==null||u.reply_at>u.read_at)||(i.get(s.node_id)??0)>0?!0:s.status!=="done"||u?.intent!=="parked"?!1:[s.created,s.telemetry_updated_at,u.reply_at].reduce((v,h)=>h!=null&&h>v?h:v,"")>=o},"keepFinished"),p=t.filter(s=>s.parent===null&&s.kind!=="human"&&(we(s.status)||c(s))),f=new Map;for(const s of await S())f.set(s.from_id,[...f.get(s.from_id)??[],s.to_id]);const y=l(s=>{const u=e.get(s)?.fault;return u&&u.link!=="broker\u2194crtrd"?Ne(u):null},"faultOf"),a=await x(p,async s=>{const u=[s.node_id,...await ve(s.node_id)].filter(d=>e.get(d)?.kind!=="human"&&e.has(d)),g=new Set(u),b=n.filter(d=>g.has(d.requester_node_id)),v=r.get(s.node_id),h=v?.reply_at??null,O=v?.read_at??null;let w=s.created;for(const d of u){const m=e.get(d).telemetry_updated_at;m!=null&&m>w&&(w=m)}return h!==null&&h>w&&(w=h),{node_id:s.node_id,name:Q(s),cwd:s.cwd,profile_id:s.profile_id,status:s.status,state_line:(()=>{const d=ke(s.node_id)?.state.trim();return d||null})(),goal:(await Se(s.node_id))?.trim()||null,asks:b.length,first_ask_ticket_id:b[0]?.inbox_ticket_id??null,fault:u.map(y).find(d=>d!==null)??null,running_count:u.filter(d=>e.get(d).streaming===!0).length,unread:h!==null&&(O===null||h>O),last_activity:w,children:(f.get(s.node_id)??[]).filter(d=>e.has(d)&&e.get(d).kind!=="human").map(d=>{const m=e.get(d);return{node_id:d,name:Q(m),status:m.status,streaming:m.streaming===!0,asks:i.get(d)??0,fault:y(d)}})}});return{generated_at:I(),rows:a}}l(Je,"buildLiveRoots");let C=null,L=null;function Ke(){const t=l(()=>Promise.resolve().then(Je),"run");return C===null?(C=t().finally(()=>{C=null}),C):(L===null&&(L=C.then(t).finally(()=>{L=null})),L)}l(Ke,"liveRootsCoalesced");async function We(t){if(!je(t.principal))throw new T(403,"permission_error","only the person reads the dashboard");const e=await Ke(),r=t.query.get("profile");return{status:200,body:r===null?e:{...e,rows:e.rows.filter(n=>n.profile_id===r)}}}l(We,"handleLiveRoots");async function Ue(t){const e=(await $(await F(),async o=>t.principal.class==="person"||await A(t.principal,o.grantee??"app:terminal",o.node_id))).map(o=>({node_id:o.node_id,name:o.name,description:o.description,kind:o.kind,mode:o.mode,lifecycle:o.lifecycle,status:o.status,cwd:o.cwd,profile_id:o.profile_id,grantee:o.grantee??"app:terminal",parent:o.parent,created:o.created,frozen_at:o.frozen_at??null,terminal_reason:o.terminal_reason??null,final_report:o.final_report??null,telemetry_tokens_in:o.telemetry_tokens_in??null,telemetry_updated_at:o.telemetry_updated_at??null,fault:o.fault&&o.fault.link!=="broker\u2194crtrd"?{...o.fault,link:o.fault.link}:null,streaming:o.streaming??!1})),r=new Set(e.map(o=>o.node_id)),n=(await S()).filter(o=>r.has(o.from_id)&&r.has(o.to_id));return{status:200,rawBody:Z(JSON.stringify({nodes:e,edges:n}),{level:1}),headers:{"content-type":"application/gzip"}}}l(Ue,"handleBrowse");async function Qe(t){const e=await N(t);return{status:200,body:{generated_at:I(),nodes:(await ee()).filter(n=>e===null||e.has(n.node_id)).map(Ce),edges:(await S()).filter(n=>e===null||e.has(n.from_id)&&e.has(n.to_id)).map(n=>({from_id:n.from_id,to_id:n.to_id,active:n.active,created:n.created})),focused_node_ids:(await H()).map(n=>n.node_id).filter(n=>e===null||e.has(n))}}}l(Qe,"handleGraph");function Ye(t){if(t==null)return{};if(typeof t!="object")throw _("prune body must be a JSON object");const e=t,r=l(a=>{const s=e[a];if(s!==void 0){if(typeof s!="number"||!Number.isFinite(s))throw _(`${a} must be a number`);return s}},"num"),n=l(a=>{const s=e[a];if(s!==void 0){if(typeof s!="boolean")throw _(`${a} must be a boolean`);return s}},"bool"),i={},o=r("limit");o!==void 0&&(i.limit=o);const c=r("ttl_days");c!==void 0&&(i.ttl_days=c);const p=n("include_stale");p!==void 0&&(i.include_stale=p);const f=n("empty");f!==void 0&&(i.empty=f);const y=n("dry_run");return y!==void 0&&(i.dry_run=y),i}l(Ye,"parsePruneBody");async function Ve(t){const e=Ye(t.body),r=e.ttl_days??14,n=e.dry_run??!1,i=await he(ye());let o;return e.limit!==void 0?o=(await ne({maxNodes:e.limit,held:i,dryRun:n})).pruned.map(f=>({node_id:f.node_id,status:f.status,created:f.created})):e.empty===!0?o=(await _e({dryRun:n})).map(f=>({node_id:f,status:"empty",created:""})):o=(await te({ttlDays:r,held:i,dryRun:n,includeStale:e.include_stale??!1})).pruned.map(f=>({node_id:f.node_id,status:f.status,created:f.created})),{status:200,body:{pruned:o,dry_run:n,ttl_days:r}}}l(Ve,"handlePrune");const Rt=[{method:"GET",pattern:"/v1/canvas/attention",handler:Me},{method:"POST",pattern:"/v1/canvas/attention/counts",handler:Le},{method:"POST",pattern:"/v1/canvas/history/search",handler:Fe},{method:"POST",pattern:"/v1/canvas/history/grep",handler:Pe},{method:"GET",pattern:"/v1/canvas/history/read",handler:Ge},{method:"POST",pattern:"/v1/canvas/history/stats",handler:Be},{method:"GET",pattern:"/v1/canvas/snapshot",handler:qe},{method:"GET",pattern:"/v1/canvas/roster",handler:xe},{method:"GET",pattern:"/v1/canvas/live-roots",handler:We},{method:"GET",pattern:"/v1/canvas/browse",handler:Ue},{method:"GET",pattern:"/v1/canvas/graph",handler:Qe},{method:"POST",pattern:"/v1/canvas/prune",ownerOnly:!0,handler:Ve}];export{Rt as canvasRoutes,Ue as handleBrowse};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var X=Object.defineProperty;var t=(e,n)=>X(e,"name",{value:n,configurable:!0});import{randomUUID as nn}from"node:crypto";import{relative as en,resolve as B,sep as G}from"node:path";import{ApiError as c}from"../../../api/errors.js";import{armCron as rn,parseCronSink as on,getCron as v,listCrons as P,listCronRuns as F,pokeHeldCrons as tn,setCronState as L}from"../../../core/canvas/crons.js";import{getNode as _}from"../../../core/canvas/canvas.js";import{appSpace as sn,cronFolder as an}from"../../../core/layout.js";import{appProcessesSandboxed as cn,cwdIsMounted as ln}from"../../../core/spaces/process.js";import{activeGrant as dn,activeGrantForApp as un,grantFor as pn,terminalGrant as fn}from"../../../core/grants/mirror.js";import{readConfig as wn}from"../../../core/config.js";import{assertScope as Z}from"../../../core/scopes.js";import{InputError as s}from"../../../core/io.js";import{nextSlotAfter as _n}from"../../../core/wake.js";import{executeCron as hn,markSelfCanceledCronRun as mn}from"../../cron-run.js";import{toCronDTO as k,toCronRunDTO as H}from"../map.js";import{appOf as T,assertCeiling as gn,canReadNode as yn}from"../router.js";import{checkCronBirth as bn,checkCronFire as vn}from"../../cron/authority.js";import{endCron as kn}from"../../cron/ended.js";import{requireUsableProfile as Sn}from"./profiles.js";import{filterAsync as $n,mapAsync as Cn}from"../../../core/async-array.js";function E(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new s({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}t(E,"requireString");function C(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new s({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(C,"requireEnum");async function On(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new s({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(await _(n)===null)throw new s({error:"sink_node_not_found",message:`sink node ${n} does not exist on the canvas`,field:"sink",next:"Name a live node id, or use spawn:<kind> for a delivery that outlives any one node."});return JSON.stringify({node:n})}if(e.startsWith("spawn:")){const n=e.slice(6).trim();if(n==="")throw new s({error:"bad_sink",message:"spawn sink requires a node kind",field:"sink",next:'Use sink "spawn:<kind>" (e.g. spawn:general).'});return JSON.stringify({spawn:n})}throw new s({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(On,"parseSinkSpec");const In=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function M(e,n){if(!In.test(e)||Number.isNaN(Date.parse(e)))throw new s({error:`bad_${n}`,message:`"${e}" is not a UTC ISO instant`,field:n,next:'Send a resolved UTC instant like "2026-06-07T09:00:00.000Z" (the client resolves the time grammar).'});return e}t(M,"requireUtcIso");function Nn(e){try{_n(e,new Date)}catch(n){throw new s({error:"bad_recur",message:`recur is not advanceable: ${n instanceof Error?n.message:String(n)}`,field:"recur",next:'Send recur JSON the daemon can advance: {"every":"6h"} or {"cron":"0 9 * * *","tz":"America/New_York"}.'})}return e}t(Nn,"requireRecur");function qn(e){let n;try{n=JSON.parse(e)}catch{throw new s({error:"bad_env_json",message:"env_json is not valid JSON",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'})}if(typeof n!="object"||n===null||Array.isArray(n))throw new s({error:"bad_env_json",message:"env_json must be a JSON object",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'});for(const[r,o]of Object.entries(n))if(typeof o!="string")throw new s({error:"bad_env_json",message:`env_json value for "${r}" is not a string`,field:"env_json",next:"Every env value must be a string."});return e}t(qn,"requireEnvJson");function U(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(U,"callerProfile");function Y(e,n){return n===null||e.scope==="global"||e.profile===n}t(Y,"inScope");function K(e){return e.principal.class==="person"?null:T(e.principal)}t(K,"callerApp");async function J(e,n){return await yn(e.principal,n.grantee,n.created_by??void 0)}t(J,"visibleTo");function jn(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(jn,"anchorNodeOf");async function An(e,n,r,o,a,u){const l=typeof e.cwd=="string"&&e.cwd.trim()!==""?B(e.cwd):null;if(!cn()){if(l===null)throw new s({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return l}const p=o!==null&&await _(o)!==null?o:void 0,d=t(async N=>await ln(n,N,p,a,void 0,u),"reaches");if(l!==null&&await d(l))return l;const i=sn(n).files;if(l!==null&&e.cwd_is_default!==!0)throw new c(400,"invalid_request",`cwd ${l} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${i}`,{cwd:l,default:i},void 0,{param:"cwd"});const h=r===null?null:(await _(r))?.cwd??null;return h!==null&&await d(h)?h:i}t(An,"resolveArmedCwd");async function Pn(e,n,r){const o=e.principal;if(o.class==="person"){const i=await fn();return{sub:i?.sub??"user:local",grant_id:i?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const i=await un(n);if(i===null)throw new c(401,"grant_revoked",`no active grant for ${n}`);const h=e.ceiling??o.scopes;return{sub:i.sub,grant_id:i.id,scopes:[...h??i.resolved],origin_run_id:null}}if(o.class==="cron"){const i=await vn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!i.allowed)throw new c(i.reason==="scope_missing"?403:401,i.reason==="scope_missing"?"scope_missing":"grant_revoked",i.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?[...i.effectiveScopes]:[...o.scopes],origin_run_id:o.origin_run}}if(o.class!=="broker")throw new c(401,"unauthorized","this caller cannot arm a cron");const a=r??await _(o.node),u=a?.grant_id??o.grant,l=a?.sub??o.sub,p=u!=null?await dn(u):await pn(n,l);if(p===null){if(n==="app:terminal"&&!wn("user").runtime?.issuer)return{sub:l,grant_id:u??null,scopes:a?.scopes??o.scopes,origin_run_id:a?.run_id??o.run};throw new c(401,"grant_revoked","the node grant is not active")}const d=a?.scopes??o.scopes;return{sub:p.sub,grant_id:p.id,scopes:d===null?[...p.resolved]:[...d],origin_run_id:a?.run_id??o.run}}t(Pn,"armPrincipal");async function Tn(e){const n=e.body??{},r=E(n,"name"),o=E(n,"command"),a=M(E(n,"fire_at"),"fire_at"),u=typeof n.recur=="string"&&n.recur!==""?Nn(n.recur):null,l=typeof n.expires_at=="string"&&n.expires_at!==""?M(n.expires_at,"expires_at"):null,p=typeof n.env_json=="string"&&n.env_json!==""?qn(n.env_json):null,d=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,i=C(n.on_output,"on_output",["silent","on-failure","always","on-change"],d?.startsWith("node:")?"on-change":"on-failure"),h=C(n.overlap,"overlap",["skip","queue","replace"],"skip"),N=C(n.tier,"tier",["normal","urgent","critical"],"normal"),D=i==="always"||i==="on-change";if(D&&d===null)throw new s({error:"sink_required",message:`on_output "${i}" delivers stdout every qualifying run \u2014 it needs a sink`,field:"sink",next:"Pass sink node:<node-id> (a living node's inbox), spawn:<kind> (birth a fresh node per delivery), or human (the humanloop inbox)."});if(!D&&d!==null)throw new s({error:"sink_unused",message:`on_output "${i}" never delivers stdout \u2014 a sink would be dead config`,field:"sink",next:'Drop the sink, or use on_output "always"/"on-change" if delivery is the point.'});const Q=d!==null?await On(d):"{}";if(d?.startsWith("spawn:")&&(gn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const w=await bn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes});if(!w.allowed)throw new c(w.reason==="scope_missing"?403:401,w.reason==="scope_missing"?"scope_missing":"grant_revoked",w.message)}const y=T(e.principal)??"app:terminal",b=e.principal.class==="broker"?e.principal.node:e.principal.class==="cron"?null:typeof n.created_by=="string"&&n.created_by!==""?n.created_by:null;if(e.principal.class==="broker"&&n.created_by!=null&&n.created_by!==b)throw new c(403,"permission_error","a broker may only arm a cron as its own node");const m=b===null?null:await _(b);if(m!==null){if(m.grantee!==y)throw new s({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});Z(m,"crtr:schedule"),d?.startsWith("spawn:")&&Z(m,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new c(400,"invalid_request","isolated must be a boolean");if(m?.isolated&&n.isolated===!1)throw new c(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const W=m?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(W&&n.isolated===!1)throw new c(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const q=W||n.isolated===!0,g=await Pn(e,y,m),j=nn(),S=q?an(y,j):null;if(S!==null&&n.cwd_is_default!==!0){const w=en(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?B(n.cwd):S);if(w===".."||w.startsWith(`..${G}`)||w.startsWith(G))throw new c(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const V=S??await An(n,y,b,jn(n),q,{sub:g.sub,grantId:g.grant_id,scopes:g.scopes}),f=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(f!==null&&await _(f)!==null&&(await _(f)).grantee!==y)throw new s({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(f!==null&&await _(f)===null)throw new s({error:"anchor_not_found",message:`anchor node ${f} does not exist on the canvas`,field:"anchor_node",next:"Name a live node id, or drop the anchor \u2014 an unanchored cron outlives every node."});const x=n.cancel_on_wake===!0;if(x&&f===null)throw new s({error:"cancel_on_wake_needs_anchor",message:"cancel_on_wake couples the cron to a node's dormancy \u2014 it requires an anchor node",field:"anchor_node",next:"Pass anchor_node with the node whose wake should cancel this cron."});const $=n.run_timeout_s??300;if(typeof $!="number"||!Number.isInteger($)||$<=0)throw new s({error:"bad_run_timeout",message:`run_timeout_s must be a positive integer (got ${String(n.run_timeout_s)})`,field:"run_timeout_s",next:"Send the timeout in whole seconds, e.g. 300."});const A=typeof n.profile=="string"&&n.profile!==""?(await Sn(e,n.profile)).profileId:null,z=C(n.scope,"scope",["profile","global"],A!==null?"profile":"global");if(z==="profile"&&A===null)throw new s({error:"scope_needs_profile",message:'scope "profile" names the profile the cron lists and cancels within \u2014 it requires a profile',field:"profile",next:'Send a profile, or use scope "global" for a canvas-home-wide cron.'});return await rn({cron_id:j,name:r,created_by:b,grantee:y,sub:g.sub,grant_id:g.grant_id,scopes:g.scopes,origin_run_id:g.origin_run_id,isolated:q,command:o,fire_at:a,recur:u,tz:typeof n.tz=="string"?n.tz:null,expires_at:l,anchor_node:f,cancel_on_wake:x,cwd:V,env_json:p,profile:A,scope:z,run_timeout_s:$,overlap:h,on_output:i,sink:Q,tier:N}),{status:201,body:k(await v(j),null)}}t(Tn,"handleArm");async function En(e){const n=U(e),r=await $n(n===null?await P({all:!0}):[...await P({profile:n}),...await P({global:!0})].sort((o,a)=>o.fire_at<a.fire_at?-1:o.fire_at>a.fire_at?1:0),async o=>await J(e,o));return{status:200,body:await Cn(r,async o=>k(o,await R(o)))}}t(En,"handleList");async function R(e){return(await F(e.cron_id))[0]??null}t(R,"lastRunOf");async function O(e){const n=e.params.cronId,r=await v(n);if(r===null||!await J(e,r))throw new c(404,"cron_not_found",`no cron ${n}`);if(!Y(r,U(e)))throw new c(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(O,"requireCron");function I(e,n){const r=K(e);if(r!==null&&n.grantee!==r)throw new c(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(I,"requireOwnCron");async function Un(e){const n=await O(e),r=await F(n.cron_id);return{status:200,body:{cron:k(n,r[0]??null),runs:r.map(H)}}}t(Un,"handleShow");async function Jn(e){const n=I(e,await O(e));await L(n.cron_id,"paused");const r=await v(n.cron_id);return{status:200,body:k(r,await R(r))}}t(Jn,"handlePause");async function Rn(e){const n=I(e,await O(e));await L(n.cron_id,"active");const r=await v(n.cron_id);return{status:200,body:k(r,await R(r))}}t(Rn,"handleResume");async function Dn(e){const n=I(e,await O(e));if(n.run_state==="running")throw new c(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await hn(n,{outOfBand:!0});if(r===null)throw new c(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:H(r)}}t(Dn,"handleRun");async function Wn(e){const n=new Date().toISOString();return{status:200,body:{unparked:await tn(n,K(e)),at:n}}}t(Wn,"handlePoke");function xn(e,n){const r=e.principal;if(r.class==="cron")return r.cron===n.cron_id?null:{kind:"canceled",by:`cron ${r.cron}`};if(r.class==="broker"){const a=on(n.sink);return a?.kind==="node"&&a.node===r.node?null:{kind:"canceled",by:`node ${r.node}`}}const o=T(r);return{kind:"canceled",by:o===null||o==="app:terminal"?"the person":o}}t(xn,"cancelNotice");async function zn(e){const n=e.params.cronId,r=await v(n);if(r!==null&&!await J(e,r))throw new c(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!Y(r,U(e)))throw new c(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);r!==null&&I(e,r);const o=e.query.get("run_id"),a=e.principal.class==="cron"?o===e.principal.execution&&n===e.principal.cron:!0,u=o!==null&&a&&mn(n,o);return r!==null&&await kn(r,u?null:xn(e,r),{preserveRunningProcess:u}),{status:204}}t(zn,"handleCancel");const ce=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:Tn},{method:"GET",pattern:"/v1/crons",handler:En},{method:"GET",pattern:"/v1/crons/:cronId",handler:Un},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:Jn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:Rn},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:Wn},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Dn},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:zn}];export{ce as cronRoutes};
|
|
1
|
+
var X=Object.defineProperty;var t=(e,n)=>X(e,"name",{value:n,configurable:!0});import{randomUUID as nn}from"node:crypto";import{relative as en,resolve as z,sep as G}from"node:path";import{ApiError as c}from"../../../api/errors.js";import{armCron as rn,parseCronSink as on,getCron as v,listCrons as P,listCronRuns as F,pokeHeldCrons as tn,setCronState as L}from"../../../core/canvas/crons.js";import{getNode as _}from"../../../core/canvas/canvas.js";import{appSpace as sn,cronFolder as an}from"../../../core/layout.js";import{appProcessesSandboxed as cn,cwdIsMounted as ln}from"../../../core/spaces/process.js";import{activeGrant as dn,activeGrantForApp as un,grantFor as pn,terminalGrant as fn}from"../../../core/grants/mirror.js";import{readConfig as wn}from"../../../core/config.js";import{assertScope as Z}from"../../../core/scopes.js";import{InputError as a}from"../../../core/io.js";import{nextSlotAfter as _n}from"../../../core/wake.js";import{executeCron as hn,markSelfCanceledCronRun as mn}from"../../cron-run.js";import{toCronDTO as k,toCronRunDTO as H}from"../map.js";import{appOf as T,assertCeiling as gn,canReadNode as yn}from"../router.js";import{checkCronBirth as bn,checkCronFire as vn}from"../../cron/authority.js";import{endCron as kn}from"../../cron/ended.js";import{requireUsableProfile as Sn}from"./profiles.js";import{filterAsync as $n,mapAsync as Cn}from"../../../core/async-array.js";function E(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new a({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}t(E,"requireString");function C(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new a({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(C,"requireEnum");async function On(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new a({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(await _(n)===null)throw new a({error:"sink_node_not_found",message:`sink node ${n} does not exist on the canvas`,field:"sink",next:"Name a live node id, or use spawn:<kind> for a delivery that outlives any one node."});return JSON.stringify({node:n})}if(e.startsWith("spawn:")){const n=e.slice(6).trim();if(n==="")throw new a({error:"bad_sink",message:"spawn sink requires a node kind",field:"sink",next:'Use sink "spawn:<kind>" (e.g. spawn:general).'});return JSON.stringify({spawn:n})}throw new a({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(On,"parseSinkSpec");const In=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function M(e,n){if(!In.test(e)||Number.isNaN(Date.parse(e)))throw new a({error:`bad_${n}`,message:`"${e}" is not a UTC ISO instant`,field:n,next:'Send a resolved UTC instant like "2026-06-07T09:00:00.000Z" (the client resolves the time grammar).'});return e}t(M,"requireUtcIso");function qn(e){try{_n(e,new Date)}catch(n){throw new a({error:"bad_recur",message:`recur is not advanceable: ${n instanceof Error?n.message:String(n)}`,field:"recur",next:'Send recur JSON the daemon can advance: {"every":"6h"} or {"cron":"0 9 * * *","tz":"America/New_York"}.'})}return e}t(qn,"requireRecur");function Nn(e){let n;try{n=JSON.parse(e)}catch{throw new a({error:"bad_env_json",message:"env_json is not valid JSON",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'})}if(typeof n!="object"||n===null||Array.isArray(n))throw new a({error:"bad_env_json",message:"env_json must be a JSON object",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'});for(const[r,o]of Object.entries(n))if(typeof o!="string")throw new a({error:"bad_env_json",message:`env_json value for "${r}" is not a string`,field:"env_json",next:"Every env value must be a string."});return e}t(Nn,"requireEnvJson");function U(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(U,"callerProfile");function Y(e,n){return n===null||e.scope==="global"||e.profile===n}t(Y,"inScope");function K(e){return e.principal.class==="person"?null:T(e.principal)}t(K,"callerApp");async function J(e,n){return await yn(e.principal,n.grantee,n.created_by??void 0)}t(J,"visibleTo");function jn(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(jn,"anchorNodeOf");async function An(e,n,r,o,i,l){const d=typeof e.cwd=="string"&&e.cwd.trim()!==""?z(e.cwd):null;if(!cn()){if(d===null)throw new a({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return d}const h=o!==null&&await _(o)!==null?o:void 0,u=t(async q=>await ln(n,q,h,i,void 0,l),"reaches");if(d!==null&&await u(d))return d;const s=sn(n).files;if(d!==null&&e.cwd_is_default!==!0)throw new c(400,"invalid_request",`cwd ${d} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${s}`,{cwd:d,default:s},void 0,{param:"cwd"});const p=r===null?null:(await _(r))?.cwd??null;return p!==null&&await u(p)?p:s}t(An,"resolveArmedCwd");async function Pn(e,n,r){const o=e.principal;if(o.class==="person"){const s=await fn();return{sub:s?.sub??"user:local",grant_id:s?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const s=await un(n);if(s===null)throw new c(401,"grant_revoked",`no active grant for ${n}`);const p=e.ceiling??o.scopes;return{sub:s.sub,grant_id:s.id,scopes:p===null?null:[...p],origin_run_id:null}}if(o.class==="cron"){const s=await vn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!s.allowed)throw new c(s.reason==="scope_missing"?403:401,s.reason==="scope_missing"?"scope_missing":"grant_revoked",s.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?null:[...o.scopes],origin_run_id:o.origin_run}}if(o.class!=="broker")throw new c(401,"unauthorized","this caller cannot arm a cron");const i=r??await _(o.node),l=i?.grant_id??o.grant,d=i?.sub??o.sub,h=l!=null?await dn(l):await pn(n,d);if(h===null){if(n==="app:terminal"&&!wn("user").runtime?.issuer)return{sub:d,grant_id:l??null,scopes:i?.scopes??o.scopes,origin_run_id:i?.run_id??o.run};throw new c(401,"grant_revoked","the node grant is not active")}const u=i?.scopes??o.scopes;return{sub:h.sub,grant_id:h.id,scopes:u===null?null:[...u],origin_run_id:i?.run_id??o.run}}t(Pn,"armPrincipal");async function Tn(e){const n=e.body??{},r=E(n,"name"),o=E(n,"command"),i=M(E(n,"fire_at"),"fire_at"),l=typeof n.recur=="string"&&n.recur!==""?qn(n.recur):null,d=typeof n.expires_at=="string"&&n.expires_at!==""?M(n.expires_at,"expires_at"):null,h=typeof n.env_json=="string"&&n.env_json!==""?Nn(n.env_json):null,u=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,s=C(n.on_output,"on_output",["silent","on-failure","always","on-change"],u?.startsWith("node:")?"on-change":"on-failure"),p=C(n.overlap,"overlap",["skip","queue","replace"],"skip"),q=C(n.tier,"tier",["normal","urgent","critical"],"normal"),D=s==="always"||s==="on-change";if(D&&u===null)throw new a({error:"sink_required",message:`on_output "${s}" delivers stdout every qualifying run \u2014 it needs a sink`,field:"sink",next:"Pass sink node:<node-id> (a living node's inbox), spawn:<kind> (birth a fresh node per delivery), or human (the humanloop inbox)."});if(!D&&u!==null)throw new a({error:"sink_unused",message:`on_output "${s}" never delivers stdout \u2014 a sink would be dead config`,field:"sink",next:'Drop the sink, or use on_output "always"/"on-change" if delivery is the point.'});const Q=u!==null?await On(u):"{}";if(u?.startsWith("spawn:")&&(gn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const w=await bn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes});if(!w.allowed)throw new c(w.reason==="scope_missing"?403:401,w.reason==="scope_missing"?"scope_missing":"grant_revoked",w.message)}const y=T(e.principal)??"app:terminal",b=e.principal.class==="broker"?e.principal.node:e.principal.class==="cron"?null:typeof n.created_by=="string"&&n.created_by!==""?n.created_by:null;if(e.principal.class==="broker"&&n.created_by!=null&&n.created_by!==b)throw new c(403,"permission_error","a broker may only arm a cron as its own node");const m=b===null?null:await _(b);if(m!==null){if(m.grantee!==y)throw new a({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});Z(m,"crtr:schedule"),u?.startsWith("spawn:")&&Z(m,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new c(400,"invalid_request","isolated must be a boolean");if(m?.isolated&&n.isolated===!1)throw new c(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const W=m?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(W&&n.isolated===!1)throw new c(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const N=W||n.isolated===!0,g=await Pn(e,y,m),j=nn(),S=N?an(y,j):null;if(S!==null&&n.cwd_is_default!==!0){const w=en(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?z(n.cwd):S);if(w===".."||w.startsWith(`..${G}`)||w.startsWith(G))throw new c(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const V=S??await An(n,y,b,jn(n),N,{sub:g.sub,grantId:g.grant_id,scopes:g.scopes}),f=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(f!==null&&await _(f)!==null&&(await _(f)).grantee!==y)throw new a({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(f!==null&&await _(f)===null)throw new a({error:"anchor_not_found",message:`anchor node ${f} does not exist on the canvas`,field:"anchor_node",next:"Name a live node id, or drop the anchor \u2014 an unanchored cron outlives every node."});const B=n.cancel_on_wake===!0;if(B&&f===null)throw new a({error:"cancel_on_wake_needs_anchor",message:"cancel_on_wake couples the cron to a node's dormancy \u2014 it requires an anchor node",field:"anchor_node",next:"Pass anchor_node with the node whose wake should cancel this cron."});const $=n.run_timeout_s??300;if(typeof $!="number"||!Number.isInteger($)||$<=0)throw new a({error:"bad_run_timeout",message:`run_timeout_s must be a positive integer (got ${String(n.run_timeout_s)})`,field:"run_timeout_s",next:"Send the timeout in whole seconds, e.g. 300."});const A=typeof n.profile=="string"&&n.profile!==""?(await Sn(e,n.profile)).profileId:null,x=C(n.scope,"scope",["profile","global"],A!==null?"profile":"global");if(x==="profile"&&A===null)throw new a({error:"scope_needs_profile",message:'scope "profile" names the profile the cron lists and cancels within \u2014 it requires a profile',field:"profile",next:'Send a profile, or use scope "global" for a canvas-home-wide cron.'});return await rn({cron_id:j,name:r,created_by:b,grantee:y,sub:g.sub,grant_id:g.grant_id,scopes:g.scopes,origin_run_id:g.origin_run_id,isolated:N,command:o,fire_at:i,recur:l,tz:typeof n.tz=="string"?n.tz:null,expires_at:d,anchor_node:f,cancel_on_wake:B,cwd:V,env_json:h,profile:A,scope:x,run_timeout_s:$,overlap:p,on_output:s,sink:Q,tier:q}),{status:201,body:k(await v(j),null)}}t(Tn,"handleArm");async function En(e){const n=U(e),r=e.query.get("created_by");if(r==="")throw new c(400,"invalid_request","created_by must be nonempty when present");const o=await $n((n===null?await P({all:!0}):[...await P({profile:n}),...await P({global:!0})].sort((i,l)=>i.fire_at<l.fire_at?-1:i.fire_at>l.fire_at?1:0)).filter(i=>r===null||i.created_by===r),async i=>await J(e,i));return{status:200,body:await Cn(o,async i=>k(i,await R(i)))}}t(En,"handleList");async function R(e){return(await F(e.cron_id))[0]??null}t(R,"lastRunOf");async function O(e){const n=e.params.cronId,r=await v(n);if(r===null||!await J(e,r))throw new c(404,"cron_not_found",`no cron ${n}`);if(!Y(r,U(e)))throw new c(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(O,"requireCron");function I(e,n){const r=K(e);if(r!==null&&n.grantee!==r)throw new c(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(I,"requireOwnCron");async function Un(e){const n=await O(e),r=await F(n.cron_id);return{status:200,body:{cron:k(n,r[0]??null),runs:r.map(H)}}}t(Un,"handleShow");async function Jn(e){const n=I(e,await O(e));await L(n.cron_id,"paused");const r=await v(n.cron_id);return{status:200,body:k(r,await R(r))}}t(Jn,"handlePause");async function Rn(e){const n=I(e,await O(e));await L(n.cron_id,"active");const r=await v(n.cron_id);return{status:200,body:k(r,await R(r))}}t(Rn,"handleResume");async function Dn(e){const n=I(e,await O(e));if(n.run_state==="running")throw new c(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await hn(n,{outOfBand:!0});if(r===null)throw new c(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:H(r)}}t(Dn,"handleRun");async function Wn(e){const n=new Date().toISOString();return{status:200,body:{unparked:await tn(n,K(e)),at:n}}}t(Wn,"handlePoke");function Bn(e,n){const r=e.principal;if(r.class==="cron")return r.cron===n.cron_id?null:{kind:"canceled",by:`cron ${r.cron}`};if(r.class==="broker"){const i=on(n.sink);return i?.kind==="node"&&i.node===r.node?null:{kind:"canceled",by:`node ${r.node}`}}const o=T(r);return{kind:"canceled",by:o===null||o==="app:terminal"?"the person":o}}t(Bn,"cancelNotice");async function xn(e){const n=e.params.cronId,r=await v(n);if(r!==null&&!await J(e,r))throw new c(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!Y(r,U(e)))throw new c(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);r!==null&&I(e,r);const o=e.query.get("run_id"),i=e.principal.class==="cron"?o===e.principal.execution&&n===e.principal.cron:!0,l=o!==null&&i&&mn(n,o);return r!==null&&await kn(r,l?null:Bn(e,r),{preserveRunningProcess:l}),{status:204}}t(xn,"handleCancel");const ce=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:Tn},{method:"GET",pattern:"/v1/crons",handler:En},{method:"GET",pattern:"/v1/crons/:cronId",handler:Un},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:Jn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:Rn},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:Wn},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Dn},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:xn}];export{ce as cronRoutes};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
`),o=s.seq}},"flush"),
|
|
3
|
-
`)},"beat"),
|
|
1
|
+
var C=Object.defineProperty;var a=(r,e)=>C(r,"name",{value:e,configurable:!0});import{ApiError as c}from"../../../api/errors.js";import{emitEvent as A}from"../../../core/events/emit.js";import{withCanvasWrite as w,openDb as F}from"../../../core/canvas/db.js";import{registerCustomObject as $,publishCustom as L,unregisterCustomObject as P,pendingFor as H,acknowledge as I,verifyPublishToken as j,mayActAs as b,onQueueSignal as W,customReachable as k}from"../../../core/graph/custom.js";import{getById as l}from"../../../core/graph/objects.js";import{readerFor as M}from"../../../core/graph/reader.js";import{resolveName as N}from"../../../core/graph/names.js";import{stubOf as D}from"../../../core/peer/stubs.js";import{PRIVACY_LEVELS_DTO as z}from"../../../api/dto/objects.js";import{receiverForObject as Q}from"../../../core/graph/receivers.js";import{readerFor as m,resolveRef as V}from"../reader.js";import{isRecord as d}from"./validate.js";import{receiverEndpoint as B,resolveReceiverAddress as G}from"../../../core/graph/receiver-url.js";const p=a(r=>{throw new c(400,"invalid_request",`invalid ${r}`)},"bad"),f=a(()=>{throw new c(404,"not_found","custom object not found")},"missing");async function y(r,e){const n=await l(r,e)??await(async()=>{const i=await M(r,{class:"person"},async()=>[]),o=await N(r,i,e);return"object"in o?o.object:null})();return!n||!n.type.includes(".")||n.deleted_at!==null?f():n}a(y,"customObjectRef");async function J(r,e,t){if(await Q(r,t)==="argv"&&(e.publishToken||e.principal.class!=="person"&&e.principal.class!=="broker"))throw new c(403,"argv_forbidden","argv object requires a person or node principal")}a(J,"fenceArgv");async function v(r,e,t,n=!1){if(e.publishToken){if(await j(r,t,e.publishToken))return t;if(n)return f();throw new c(401,"invalid_publish_token","invalid publish token")}if(e.principal.class==="anonymous")return f();const i=await m(r,e),o=t.owner===null?null:await l(r,t.owner);return!o||!await b(r,i,o)||!await k(r,i,t)?f():i.class==="person"?i.person:i.class==="app"?i.app:i.class==="node"?i.node:i.object}a(v,"authorizeCustom");async function U(r){const e=r.body;if((!d(e)||typeof e.kind!="string"||typeof e.name!="string"||typeof e.description!="string"||e.events!==void 0&&!d(e.events)||e.owner!==void 0&&typeof e.owner!="string"||e.rotate_token!==void 0&&typeof e.rotate_token!="boolean"||e.privacy!==void 0&&!z.includes(e.privacy))&&p("register request"),d(e.receiver)&&"url"in e.receiver&&typeof e.receiver.url=="string")try{await G(B(e.receiver.url))}catch(t){throw new c(400,"invalid_receiver",`invalid receiver URL: ${String(t)}`)}return await w(async t=>{const n=await m(t,r),i=e.owner===void 0?n.class==="person"?n.person:n.app:await(async()=>{const u=await V(t,n,e.owner);return"deleted"in u||"remote"in u?f():u})();["person","app","repo","profile"].includes(i.type)||p("owner");const o=await $(t,n,{kind:e.kind,name:e.name,owner:i,description:e.description,...e.events===void 0?{}:{events:e.events},...e.receiver===void 0?{}:{receiver:e.receiver},rotateToken:e.rotate_token,...e.privacy===void 0?{}:{privacy:e.privacy}});return{id:o.object.id,name:o.object.name,...o.token?{token:o.token}:{}}})}a(U,"register");async function Y(r,e,t){const n=await r.prepare("SELECT owner, source, declared FROM object_kinds WHERE name = ?").get(t.type);if(!n||n.source==="register")return;if(n.source==="manifest"&&n.declared===0)throw new c(409,"kind_inert",`${t.type} is no longer declared by an installed plugin`);if(e.publishToken)return;const i=await l(r,n.owner);if(!i||!await b(r,await m(r,e),i))throw new c(403,"forbidden",`only ${i?.name??"the kind owner"} may publish ${t.type}`)}a(Y,"requirePublisher");async function K(r){const e=r.body;return(!d(e)||typeof e.event!="string"||typeof e.summary!="string"||e.cause!==void 0&&typeof e.cause!="string")&&p("publish request"),await w(async t=>{const n=await y(t,r.params.ref),i=await D(t,n);if(i!==null)throw new c(404,"not_here",`${n.id} is not homed on this machine (it is a stub of ${i.home_machine})`,{id:n.id,home_machine:i.home_machine});const o=await v(t,r,n);return await Y(t,r,n),await L(t,n,o,e)})}a(K,"publish");async function X(r){await w(async e=>{const t=await y(e,r.params.ref),n=await v(e,r,t);await J(e,r,t),await P(e,t,n)})}a(X,"unregister");async function Z(r){const e=r.body;return(!d(e)||!Array.isArray(e.delivery_ids)||e.delivery_ids.some(t=>typeof t!="string"))&&p("delivery_ids"),await w(async t=>{const n=await y(t,r.params.ref);return await v(t,r,n,!0),await E(t,n),await I(t,n,e.delivery_ids)})}a(Z,"ack");async function E(r,e){if((await r.prepare("SELECT receiver_json FROM custom_objects WHERE object = ?").get(e.id))?.receiver_json!=='"queue"')throw new c(409,"not_a_queue","object does not have a queue receiver")}a(E,"requireQueue");async function x(r){const e=r.query.get("after");e!==null&&(!/^\d+$/.test(e)||!Number.isSafeInteger(Number(e)))&&p("after");const t=await F(),n=await y(t,r.params.ref);await v(t,r,n,!0),await E(t,n);const i=r.res;i.writeHead(200,{"content-type":"application/x-ndjson; charset=utf-8","cache-control":"no-cache","x-accel-buffering":"no"});let o=-1;const u=a(async()=>{const s=await l(t,n.id);if(!s||s.deleted_at!==null)return!1;if(r.publishToken)return await j(t,s,r.publishToken)&&(await t.prepare("SELECT receiver_json FROM custom_objects WHERE object = ?").get(s.id))?.receiver_json==='"queue"';if(r.principal.class==="anonymous")return!1;const _=s.owner===null?null:await l(t,s.owner),g=await m(t,r);return _!==null&&await b(t,g,_)&&await k(t,g,s)&&(await t.prepare("SELECT receiver_json FROM custom_objects WHERE object = ?").get(s.id))?.receiver_json==='"queue"'},"authorized"),h=a(async()=>{if(!(i.destroyed||i.writableEnded)){if(!await u()){i.end();return}for(const s of await H(t,n,o))i.write(`${JSON.stringify(s)}
|
|
2
|
+
`),o=s.seq}},"flush"),R=await W(n.id,h),O=a(async()=>{await h(),!i.destroyed&&!i.writableEnded&&i.write(`
|
|
3
|
+
`)},"beat"),q=setInterval(()=>{O().catch(s=>A({level:"error",event:"api.custom_objects.heartbeat_failed",node_id:n.id,error:s}))},15e3),S=a(()=>{clearInterval(q),R()},"close");return i.once("close",S),await h(),{status:200}}a(x,"stream");const T=a(r=>async e=>({status:200,body:await r(e)}),"ok"),we=[{method:"POST",pattern:"/v1/custom-objects",handler:a(async r=>({status:200,body:await U(r)}),"handler")},{method:"POST",pattern:"/v1/custom-objects/:ref/events",handler:T(K)},{method:"DELETE",pattern:"/v1/custom-objects/:ref",handler:a(async r=>(await X(r),{status:204}),"handler")},{method:"GET",pattern:"/v1/custom-objects/:ref/deliveries",handler:x},{method:"POST",pattern:"/v1/custom-objects/:ref/deliveries/ack",handler:T(Z)}];export{v as authorizeCustom,y as customObjectRef,we as customObjectRoutes,J as fenceArgv};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var oe=Object.defineProperty;var u=(n,e)=>oe(n,"name",{value:e,configurable:!0});import{ApiError as _}from"../../../api/errors.js";import{DOC_KINDS as P,DOC_UNSET_FIELDS as q}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as H}from"../../../api/dto/objects.js";import{withCanvasWrite as k}from"../../../core/canvas/db.js";import{CrtrError as re}from"../../../core/errors.js";import{emitEvent as ie}from"../../../core/events/emit.js";import{canChange as U,spaceOf as ae}from"../../../core/graph/access.js";import{get as O}from"../../../core/graph/bodies.js";import{deleteDocument as se,documentRecord as I,editDocument as ce,heads as x,history as de,linksIn as V,moveDocument as ue,readBase as fe,setDocumentRecord as le,withCurrentNames as me,writeDocument as pe}from"../../../core/graph/documents.js";import{resolveName as K}from"../../../core/graph/names.js";import{getByRowid as L}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as we}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as he}from"../../../core/graph/repo-sync/repos.js";import{lintRules as ye,rulesOf as ve}from"../../../core/graph/rules.js";import{unifiedDiff as ge}from"../../../core/graph/diff.js";import{ownerFromFlag as M,readerFor as E,resolveRef as X}from"../reader.js";import{filterAsync as _e,flatMapAsync as be}from"../../../core/async-array.js";function v(n,e,t,o,i={}){return new _(n,e,t,{...i,error:e,message:t,next:o})}u(v,"refuse");const b=u((n,e)=>new _(400,"invalid_request",n,e),"invalid");function R(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(R,"bodyOf");const C=u(async n=>async e=>await he(n,e),"reposFnFor");function S(n,e){if(n instanceof re){const t=n.details;if(n.code==="read_first")throw v(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 _(409,"name_taken",n.message,t);if(n.code==="forbidden")throw new _(403,"forbidden",n.message,t);if(n.code==="scope_missing")throw v(403,"scope_missing",n.message,`Launch the run with \`${String(t?.scope)}\` in its scopes list.`,t??{});if(t?.rule==="repo-public-only")throw v(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 n}u(S,"mapGraphError");async function W(n,e,t){const o=await X(n,e,decodeURIComponent(t));if("remote"in o)throw v(404,"not_found",z(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new _(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(W,"refDocument");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 ke(n,e){const t=e.owner===null?null:await L(n,e.owner);return t?t.type==="node"?t.id:t.name:""}u(ke,"ownerHandle");async function j(n,e){const t=await ke(n,e);return t?`${t}/${e.name}`:e.name}u(j,"canonicalName");async function Ee(n,e){const t=e.owner===null?null:await L(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}}u(Ee,"objectDTO");async function $e(n,e){const t=e.owner===null?null:await L(n,e.owner);return t?.type==="repo"?t:null}u($e,"repoOwner");async function N(n,e,t,o){const i=[...new Map(await be(t,async f=>{const a=await $e(n,f);return a?[[a.id,a]]:[]})).values()],s=e.class==="node"?e.cwd:o,r=e.class==="node"?[...e.checkouts,...e.projectCheckouts].map(f=>f.root):[];return{repos:i,dirs:[...new Set([...s!==void 0?[s]:[],...r])]}}u(N,"repoChange");async function T(n){for(const e of n.repos)try{await we(e,n.dirs)}catch(t){ie({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(T,"syncChanged");const G=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function Ae(n,e){if(n===void 0)return;if(!Array.isArray(n)){e.push("`rules` must be a list");return}const t=n.map(i=>{if(typeof i!="object"||i===null||Array.isArray(i))return i;const{match_frontmatter:s,...r}=i;return"matchFrontmatter"in r?{...r,match_frontmatter:r.matchFrontmatter}:s===void 0?r:{...r,matchFrontmatter:s}}),o=ye(t);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return t}u(Ae,"rulesFromDTO");function Y(n,e){const t={},o=u(a=>{const c=n[a];if(c!==void 0){if(typeof c!="string"){e.push(`\`${a}\` must be a string`);return}return c}},"str"),i=o("preview");i!==void 0&&(i.trim()===""||/[\r\n]/.test(i)?e.push("`preview` must be one non-empty line"):t.preview=i);const s=o("summary");s!==void 0&&(t.summary=s);const r=o("why_it_exists");if(r!==void 0&&(t.whyItExists=r),n.privacy!==void 0&&(H.includes(n.privacy)?t.privacy=n.privacy:e.push(`\`privacy\` must be one of ${H.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(c=>typeof c!="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(c=>typeof c!="object"||c===null||Array.isArray(c))?e.push("`extensions` must map each plugin to an object of fields"):t.extensions=a}const f=Ae(n.rules,e);return f!==void 0&&(t.rules=f),t}u(Y,"fieldsFrom");function Z(n){if(n.length>0)throw b(n.join("; "),{errors:n})}u(Z,"failOn");async function J(n,e,t,o){const i=await _e(V(t),async s=>"remote"in await K(n,e,s));return{...o.length>0?{unresolved_links:o}:{},...i.length>0?{remote_links:i}:{}}}u(J,"linkReport");async function Oe(n){const e=R(n),t=[],o=e.name;(typeof o!="string"||!G.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 i=e.kind??"knowledge";P.includes(i)||t.push(i==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${P.join(", ")}`);const s=Y(e,t);e.cwd!==void 0&&typeof e.cwd!="string"&&t.push("`cwd` must be a string"),Z(t);const r=e,{dto:f,change:a}=await k(async c=>{const w=await E(c,n),d=await M(c,w,r.owner,r.cwd);if(!await U(c,w,d))throw new _(404,"not_found",`no owner ${d.name} you can write to`);try{const{object:p,unresolved:l}=await pe(c,w,{...s,owner:d,name:r.name,kind:i,preview:s.preview,privacy:s.privacy??"public"},r.body,await C(c));return{dto:{id:p.id,name:await j(c,p),...await J(c,w,r.body,l)},change:await N(c,w,[p],r.cwd)}}catch(p){S(p)}});return await T(a),f}u(Oe,"write");function je(n,e){let t=n;e.forEach(({old:o,new:i},s)=>{const r=o===""?-1:t.indexOf(o);if(r<0)throw v(422,"replace_no_match",`replace[${s}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:s});if(t.indexOf(o,r+1)>=0)throw v(422,"replace_ambiguous",`replace[${s}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:s});t=t.slice(0,r)+i+t.slice(r+o.length)})}u(je,"checkReplacements");async function De(n){const e=R(n),t=[],o=e.body!==void 0,i=e.replace!==void 0;o&&i&&t.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&t.push("`body` must be a string");const s=e.replace;i&&(!Array.isArray(s)||s.length===0||s.some(d=>typeof d!="object"||d===null||typeof d.old!="string"||typeof d.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&&!i&&t.push("`base` applies only to a `body` or `replace` edit");const r=Y(e,t),f=e.unset??[];if(!Array.isArray(f)||f.some(d=>!q.includes(d)))t.push(`\`unset\` takes only ${q.join(", ")}`);else for(const d of f)d==="summary"&&(r.summary=null),d==="why_it_exists"&&(r.whyItExists=null),d==="unlisted"&&(r.unlisted=!1),d==="lint_ignore"&&(r.lintIgnore=null),d==="extensions"&&(r.extensions=null),d==="rules"&&(r.rules=[]);t.length===0&&!o&&!i&&Object.keys(r).length===0&&t.push("nothing to change: give `body`, `replace`, or a field"),Z(t);const a=e,{dto:c,synced:w}=await k(async d=>{const p=await E(d,n),l=await W(d,p,n.params.ref),g=await j(d,l),A=await N(d,p,[l]);try{if(!o&&!i)return await le(d,p,l,r),{dto:{id:l.id,name:g,revision:(await I(d,l)).revision},synced:A};const y=a.base??await fe(d,p,l);if(i&&y!==null){const ne=await I(d,l),B=await x(d,l);ne.heads_key===y&&B.length===1&&je(await me(d,l,O(B[0].body_hash)),a.replace)}const D=a.verbatim===!0,m=o?{body:a.body,verbatim:D}:{replace:a.replace,verbatim:D},h=await ce(d,p,l,m,a.rationale,y,r,await C(d));if("stale"in h)throw v(409,"stale_revision",`${g} changed after your read; it is now at revision ${h.stale.revision}`,`Read it again with \`crtr canvas read ${g}\`, then redo your change on the current text.`,{stale:h.stale});const F=O((await x(d,l))[0].body_hash);return{dto:{id:l.id,name:g,revision:h.revision,...await J(d,p,F,h.unresolved)},synced:A}}catch(y){S(y,l)}});return await T(w),c}u(De,"edit");async function Re(n){const e=R(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"||!G.test(e.new_name)))throw b("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");const{dto:t,change:o}=await k(async i=>{const s=await E(i,n),r=await W(i,s,n.params.ref),f=e.owner===void 0?void 0:await M(i,s,e.owner,e.cwd);try{const a=await ue(i,s,r,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}},await C(i));return{dto:{id:a.id,name:await j(i,a)},change:await N(i,s,[r,a],e.cwd)}}catch(a){S(a,r)}});return await T(o),t}u(Re,"move");async function Ce(n){const{dto:e,change:t}=await k(async o=>{const i=await E(o,n),s=await W(o,i,n.params.ref),r=await j(o,s),f=await N(o,i,[s]);try{await se(o,i,s,await C(o))}catch(a){S(a,s)}return{dto:{id:s.id,name:r},change:f}});return await T(t),e}u(Ce,"remove");async function Se(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 k(async o=>{const i=await E(o,n),s=await X(o,i,decodeURIComponent(n.params.ref));if("remote"in s)throw v(404,"not_found",z(s.remote),"Check the name with `crtr canvas search`.");const r="deleted"in s?s.deleted:s;if(r.type!=="document")throw b(`${r.name} is a ${r.type}, not a document`);const f=await de(o,i,r),a={object:await Ee(o,r),revisions:f.map(c=>({rev_id:c.rev_id,number:c.number,author_id:c.author,rationale:c.rationale,verbatim:c.verbatim,created_at:c.created_at,parents:c.parents}))};if(t!==void 0){const c=f.find(d=>d.number===t);if(!c)throw new _(404,"not_found",`${r.name} has no revision ${t}`);const w=c.parents[0]===void 0?void 0:f.find(d=>d.rev_id===c.parents[0]);a.diff=ge(w?O(w.body_hash):"",O(c.body_hash))}return a})}u(Se,"revisionHistory");const Q=1e3,ee=1e3,te=3e3,Ne=/^When\b.+\bbecause\b.+/,Te=u(n=>{const e=n.trim();return e===""?0:e.split(/\s+/).length},"words");function Fe(n,e){if(n.some(o=>o.on==="boot"&&o.deliver==="content")&&e>Q)return`body is ${e} words, over the ${Q}-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>ee?`body is ${e} words, over the ${ee}-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>te?`body is ${e} words, over the ${te}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(Fe,"lengthFinding");async function Ie(n){const e=R(n);return await k(async t=>{const o=await E(t,n),i=e.owner===void 0?null:await M(t,o,e.owner,e.cwd),s=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(),r=[],f=new Map;for(const a of s){if(i&&a.owner!==i.rowid||!await U(t,o,a))continue;const c=await I(t,a),w=new Set(c.lint_ignore??[]),d=await j(t,a),p=u((m,h,F)=>{w.has(m)||r.push({name:d,severity:h,rule:m,message:F})},"add"),l=await ve(t,a),g=(await x(t,a))[0],A=g?O(g.body_hash):"",y=Fe(l,Te(A));y&&p("length","error",y),l.some(m=>m.deliver==="preview")&&!Ne.test(a.preview??"")&&p("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(a.rowid)).map(m=>m.written_as));for(const m of V(A)){if(D.has(m))continue;const h=await K(t,o,m);"remote"in h||"object"in h||"deleted"in h||p("links","warning",`[[${m}]] names no object in your view`)}if(l.some(m=>m.on==="workspace-open"&&m.deliver==="content")){const m=await ae(t,a);m.type==="repo"&&!w.has("workspace-open")&&f.set(m.name,[...f.get(m.name)??[],d])}}for(const[a,c]of f)if(!(c.length<2))for(const w of c)r.push({name:w,severity:"warning",rule:"workspace-open",message:`repo ${a} has ${c.length} documents delivered at content on workspace-open (${c.join(", ")}); keep one and link the rest from it`});return{findings:r}})}u(Ie,"lint");const $=u((n,e=200)=>async t=>({status:e,body:await n(t)}),"ok"),et=[{method:"POST",pattern:"/v1/docs",handler:$(Oe,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:$(De)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:$(Re)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:$(Ce)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:$(Se)},{method:"POST",pattern:"/v1/docs/lint",handler:$(Ie)}];export{j as canonicalName,et as docRoutes,N as repoChange,T as syncChanged};
|
|
1
|
+
var oe=Object.defineProperty;var u=(n,e)=>oe(n,"name",{value:e,configurable:!0});import{ApiError as _}from"../../../api/errors.js";import{DOC_KINDS as q,DOC_UNSET_FIELDS as H}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as U}from"../../../api/dto/objects.js";import{withCanvasWrite as k}from"../../../core/canvas/db.js";import{CrtrError as re}from"../../../core/errors.js";import{emitEvent as ie}from"../../../core/events/emit.js";import{canChange as V,spaceOf as ae}from"../../../core/graph/access.js";import{get as O}from"../../../core/graph/bodies.js";import{deleteDocument as se,documentRecord as F,editDocument as ce,heads as x,history as de,linksIn as K,moveDocument as ue,readBase as fe,setDocumentRecord as le,withCurrentNames as me,writeDocument as pe}from"../../../core/graph/documents.js";import{resolveName as X}from"../../../core/graph/names.js";import{getById as L}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as we}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as he}from"../../../core/graph/repo-sync/repos.js";import{lintRules as ye,rulesOf as ve}from"../../../core/graph/rules.js";import{unifiedDiff as ge}from"../../../core/graph/diff.js";import{ownerFromFlag as M,readerFor as E,resolveRef as z}from"../reader.js";import{filterAsync as _e,flatMapAsync as be}from"../../../core/async-array.js";function y(n,e,t,o,i={}){return new _(n,e,t,{...i,error:e,message:t,next:o})}u(y,"refuse");const b=u((n,e)=>new _(400,"invalid_request",n,e),"invalid");function R(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(R,"bodyOf");const C=u(async n=>async e=>await he(n,e),"reposFnFor");function S(n,e){if(n instanceof re){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 _(409,"name_taken",n.message,t);if(n.code==="forbidden")throw typeof t?.next=="string"?y(403,"forbidden",n.message,t.next,t):new _(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 n}u(S,"mapGraphError");async function W(n,e,t){const o=await z(n,e,decodeURIComponent(t));if("remote"in o)throw y(404,"not_found",G(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new _(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(W,"refDocument");function G(n){return`no object named ${n} on this runtime; it may be on another runtime, or the name may be misspelled`}u(G,"remoteMessage");async function ke(n,e){const t=e.owner===null?null:await L(n,e.owner);return t?t.type==="node"?t.id:t.name:""}u(ke,"ownerHandle");async function j(n,e){const t=await ke(n,e);return t?`${t}/${e.name}`:e.name}u(j,"canonicalName");async function Ee(n,e){const t=e.owner===null?null:await L(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}}u(Ee,"objectDTO");async function $e(n,e){const t=e.owner===null?null:await L(n,e.owner);return t?.type==="repo"?t:null}u($e,"repoOwner");async function N(n,e,t,o){const i=[...new Map(await be(t,async f=>{const a=await $e(n,f);return a?[[a.id,a]]:[]})).values()],s=e.class==="node"?e.cwd:o,r=e.class==="node"?[...e.checkouts,...e.projectCheckouts].map(f=>f.root):[];return{repos:i,dirs:[...new Set([...s!==void 0?[s]:[],...r])]}}u(N,"repoChange");async function T(n){for(const e of n.repos)try{await we(e,n.dirs)}catch(t){ie({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(T,"syncChanged");const Y=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function Ae(n,e){if(n===void 0)return;if(!Array.isArray(n)){e.push("`rules` must be a list");return}const t=n.map(i=>{if(typeof i!="object"||i===null||Array.isArray(i))return i;const{match_frontmatter:s,...r}=i;return"matchFrontmatter"in r?{...r,match_frontmatter:r.matchFrontmatter}:s===void 0?r:{...r,matchFrontmatter:s}}),o=ye(t);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return t}u(Ae,"rulesFromDTO");function Z(n,e){const t={},o=u(a=>{const c=n[a];if(c!==void 0){if(typeof c!="string"){e.push(`\`${a}\` must be a string`);return}return c}},"str"),i=o("preview");i!==void 0&&(i.trim()===""||/[\r\n]/.test(i)?e.push("`preview` must be one non-empty line"):t.preview=i);const s=o("summary");s!==void 0&&(t.summary=s);const r=o("why_it_exists");if(r!==void 0&&(t.whyItExists=r),n.privacy!==void 0&&(U.includes(n.privacy)?t.privacy=n.privacy:e.push(`\`privacy\` must be one of ${U.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(c=>typeof c!="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(c=>typeof c!="object"||c===null||Array.isArray(c))?e.push("`extensions` must map each plugin to an object of fields"):t.extensions=a}const f=Ae(n.rules,e);return f!==void 0&&(t.rules=f),t}u(Z,"fieldsFrom");function J(n){if(n.length>0)throw b(n.join("; "),{errors:n})}u(J,"failOn");async function Q(n,e,t,o){const i=await _e(K(t),async s=>"remote"in await X(n,e,s));return{...o.length>0?{unresolved_links:o}:{},...i.length>0?{remote_links:i}:{}}}u(Q,"linkReport");async function Oe(n){const e=R(n),t=[],o=e.name;(typeof o!="string"||!Y.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 i=e.kind??"knowledge";q.includes(i)||t.push(i==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${q.join(", ")}`);const s=Z(e,t);e.cwd!==void 0&&typeof e.cwd!="string"&&t.push("`cwd` must be a string"),J(t);const r=e,{dto:f,change:a}=await k(async c=>{const w=await E(c,n),d=await M(c,w,r.owner,r.cwd);if(!await V(c,w,d))throw new _(404,"not_found",`no owner ${d.name} you can write to`);try{const{object:p,unresolved:l}=await pe(c,w,{...s,owner:d,name:r.name,kind:i,preview:s.preview,privacy:s.privacy??"public"},r.body,await C(c));return{dto:{id:p.id,name:await j(c,p),...await Q(c,w,r.body,l)},change:await N(c,w,[p],r.cwd)}}catch(p){S(p)}});return await T(a),f}u(Oe,"write");function je(n,e){let t=n;e.forEach(({old:o,new:i},s)=>{const r=o===""?-1:t.indexOf(o);if(r<0)throw y(422,"replace_no_match",`replace[${s}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:s});if(t.indexOf(o,r+1)>=0)throw y(422,"replace_ambiguous",`replace[${s}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:s});t=t.slice(0,r)+i+t.slice(r+o.length)})}u(je,"checkReplacements");async function De(n){const e=R(n),t=[],o=e.body!==void 0,i=e.replace!==void 0;o&&i&&t.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&t.push("`body` must be a string");const s=e.replace;i&&(!Array.isArray(s)||s.length===0||s.some(d=>typeof d!="object"||d===null||typeof d.old!="string"||typeof d.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&&!i&&t.push("`base` applies only to a `body` or `replace` edit");const r=Z(e,t),f=e.unset??[];if(!Array.isArray(f)||f.some(d=>!H.includes(d)))t.push(`\`unset\` takes only ${H.join(", ")}`);else for(const d of f)d==="summary"&&(r.summary=null),d==="why_it_exists"&&(r.whyItExists=null),d==="unlisted"&&(r.unlisted=!1),d==="lint_ignore"&&(r.lintIgnore=null),d==="extensions"&&(r.extensions=null),d==="rules"&&(r.rules=[]);t.length===0&&!o&&!i&&Object.keys(r).length===0&&t.push("nothing to change: give `body`, `replace`, or a field"),J(t);const a=e,{dto:c,synced:w}=await k(async d=>{const p=await E(d,n),l=await W(d,p,n.params.ref),g=await j(d,l),A=await N(d,p,[l]);try{if(!o&&!i)return await le(d,p,l,r),{dto:{id:l.id,name:g,revision:(await F(d,l)).revision},synced:A};const v=a.base??await fe(d,p,l);if(i&&v!==null){const B=await F(d,l),P=await x(d,l);B.source_path===null&&B.heads_key===v&&P.length===1&&je(await me(d,l,O(P[0].body_hash)),a.replace)}const D=a.verbatim===!0,m=o?{body:a.body,verbatim:D}:{replace:a.replace,verbatim:D},h=await ce(d,p,l,m,a.rationale,v,r,await C(d));if("stale"in h)throw y(409,"stale_revision",`${g} changed after your read; it is now at revision ${h.stale.revision}`,`Read it again with \`crtr canvas read ${g}\`, then redo your change on the current text.`,{stale:h.stale});const I=O((await x(d,l))[0].body_hash);return{dto:{id:l.id,name:g,revision:h.revision,...await Q(d,p,I,h.unresolved)},synced:A}}catch(v){S(v,l)}});return await T(w),c}u(De,"edit");async function Re(n){const e=R(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"||!Y.test(e.new_name)))throw b("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");const{dto:t,change:o}=await k(async i=>{const s=await E(i,n),r=await W(i,s,n.params.ref),f=e.owner===void 0?void 0:await M(i,s,e.owner,e.cwd);try{const a=await ue(i,s,r,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}},await C(i));return{dto:{id:a.id,name:await j(i,a)},change:await N(i,s,[r,a],e.cwd)}}catch(a){S(a,r)}});return await T(o),t}u(Re,"move");async function Ce(n){const{dto:e,change:t}=await k(async o=>{const i=await E(o,n),s=await W(o,i,n.params.ref),r=await j(o,s),f=await N(o,i,[s]);try{await se(o,i,s,await C(o))}catch(a){S(a,s)}return{dto:{id:s.id,name:r},change:f}});return await T(t),e}u(Ce,"remove");async function Se(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 k(async o=>{const i=await E(o,n),s=await z(o,i,decodeURIComponent(n.params.ref));if("remote"in s)throw y(404,"not_found",G(s.remote),"Check the name with `crtr canvas search`.");const r="deleted"in s?s.deleted:s;if(r.type!=="document")throw b(`${r.name} is a ${r.type}, not a document`);const f=await de(o,i,r),a={object:await Ee(o,r),revisions:f.map(c=>({rev_id:c.rev_id,number:c.number,author_id:c.author,rationale:c.rationale,verbatim:c.verbatim,created_at:c.created_at,parents:c.parents}))};if(t!==void 0){const c=f.find(d=>d.number===t);if(!c)throw new _(404,"not_found",`${r.name} has no revision ${t}`);const w=c.parents[0]===void 0?void 0:f.find(d=>d.rev_id===c.parents[0]);a.diff=ge(w?O(w.body_hash):"",O(c.body_hash))}return a})}u(Se,"revisionHistory");const ee=1e3,te=1e3,ne=3e3,Ne=/^When\b.+\bbecause\b.+/,Te=u(n=>{const e=n.trim();return e===""?0:e.split(/\s+/).length},"words");function Ie(n,e){if(n.some(o=>o.on==="boot"&&o.deliver==="content")&&e>ee)return`body is ${e} words, over the ${ee}-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>te?`body is ${e} words, over the ${te}-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>ne?`body is ${e} words, over the ${ne}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(Ie,"lengthFinding");async function Fe(n){const e=R(n);return await k(async t=>{const o=await E(t,n),i=e.owner===void 0?null:await M(t,o,e.owner,e.cwd),s=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(),r=[],f=new Map;for(const a of s){if(i&&a.owner!==i.id||!await V(t,o,a))continue;const c=await F(t,a),w=new Set(c.lint_ignore??[]),d=await j(t,a),p=u((m,h,I)=>{w.has(m)||r.push({name:d,severity:h,rule:m,message:I})},"add"),l=await ve(t,a),g=(await x(t,a))[0],A=g?O(g.body_hash):"",v=Ie(l,Te(A));v&&p("length","error",v),l.some(m=>m.deliver==="preview")&&!Ne.test(a.preview??"")&&p("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(a.id)).map(m=>m.written_as));for(const m of K(A)){if(D.has(m))continue;const h=await X(t,o,m);"remote"in h||"object"in h||"deleted"in h||p("links","warning",`[[${m}]] names no object in your view`)}if(l.some(m=>m.on==="workspace-open"&&m.deliver==="content")){const m=await ae(t,a);m.type==="repo"&&!w.has("workspace-open")&&f.set(m.name,[...f.get(m.name)??[],d])}}for(const[a,c]of f)if(!(c.length<2))for(const w of c)r.push({name:w,severity:"warning",rule:"workspace-open",message:`repo ${a} has ${c.length} documents delivered at content on workspace-open (${c.join(", ")}); keep one and link the rest from it`});return{findings:r}})}u(Fe,"lint");const $=u((n,e=200)=>async t=>({status:e,body:await n(t)}),"ok"),et=[{method:"POST",pattern:"/v1/docs",handler:$(Oe,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:$(De)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:$(Re)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:$(Ce)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:$(Se)},{method:"POST",pattern:"/v1/docs/lint",handler:$(Fe)}];export{j as canonicalName,et as docRoutes,N as repoChange,T as syncChanged};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var q=Object.defineProperty;var o=(e,i)=>q(e,"name",{value:i,configurable:!0});import{ApiError as t}from"../../../api/index.js";import{inboxPageProjection as T}from"./inbox-page.js";import{getRequestByInboxTicket as p}from"../../../core/human/requests.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{canReadNode as g}from"../router.js";import{parsePage as A}from"../../../core/human/page.js";import{scanInbox as I,scanPageHistory as P}from"../../../core/human/scan.js";import{readPageTicket as h,readTicketResult as k,selectedPageDir as E,validatePageTicketResponses as x,writePageProgress as R}from"../../../core/human/tickets.js";import{describePageAnswer as S}from"../../../core/human/answer.js";import{cancelHumanTicket as O,resolvePageTicket as N}from"../../human/finish.js";import{filterAsync as y}from"../../../core/async-array.js";const C="Canceled from the inbox.",D=1e3,j=/^[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||!j.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 G(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(G,"validateRespondRequest");function L(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(L,"validateProgressRequest");function H(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>D)throw new t(400,"invalid_request","`reason` must be nonempty after trim and at most 1000 characters");return{reason:i}}o(H,"validateCancelRequest");const K=[/^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&&K.some(i=>i.test(e.message))}o(_,"isSlotValidationError");function v(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(v,"pageSummaryToDTO");async function $(e){return{status:200,body:{tickets:(await y(await I(),async n=>{if(e.principal.class==="person")return!0;const s=await p(n.inboxTicketId),r=s?.requester_node_id?await b(s.requester_node_id):null;return r!==null&&await g(e.principal,r.grantee??"app:terminal",r.node_id)})).map(n=>{if(n.kind==="page")return v(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($,"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 P(i??void 0)).filter(d=>n===null||d.state===n);return{status:200,body:{tickets:(await y(s,async d=>{if(e.principal.class==="person")return!0;const m=await p(d.inboxTicketId),w=m?.requester_node_id?await b(m.requester_node_id):null;return w!==null&&await g(e.principal,w.grantee??"app:terminal",w.node_id)})).map(v)}}}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 T(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=G(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 x(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 N(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 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 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 R(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(M,"handleProgress");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 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 O(s.ticketId,{reason:n.reason??C,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(U,"handleCancel");async function X(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=A(await E(n.dir));return{status:200,body:{...s,answer:S(r,s.responses,{completedAt:s.completedAt})}}}catch{throw new t(500,"internal","the ticket answer could not be read")}}o(X,"handleGetResponse");const ae=[{method:"GET",pattern:"/v1/human/inbox",handler:$},{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:X},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/respond",handler:F},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/progress",handler:M},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/cancel",handler:U}];export{ae as inboxRoutes,l as requireTicketId,c as resolveTicket};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var
|
|
2
|
-
AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.
|
|
3
|
-
JOIN identities i ON i.object = CASE WHEN a.type = 'node' THEN a.owner ELSE a.
|
|
4
|
-
WHERE a.
|
|
1
|
+
var x=Object.defineProperty;var r=(e,t)=>x(e,"name",{value:t,configurable:!0});import{createHash as M}from"node:crypto";import{withCanvasWrite as N}from"../../../core/canvas/db.js";import{canRead as _}from"../../../core/graph/access.js";import{bodyOf as C}from"../../../core/graph/bodies.js";import{ensureOwner as H}from"../../../core/graph/objects.js";import{readerFor as j}from"../../../core/graph/reader.js";import{checkoutsForCwd as B}from"../../../core/graph/repo-sync/repos.js";import{RunError as E}from"../../../core/runs/operations.js";import{mapAsync as F}from"../../../core/async-array.js";const it=new Set(["memory.list","memory.get"]),W=1024*1024,g=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/,O=["knowledge","preference"],s=r((e,t)=>new E(400,"invalid_request",e,t),"invalid"),R=r(()=>new E(404,"not_found","no memory document by that name"),"notFound");function v(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(v,"store");function Y(e){const t=e.get("kind");if(t===null)return null;if(!O.includes(t))throw s("kind must be knowledge or preference","kind");return t}r(Y,"kindOf");function J(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(J,"limitOf");function z(e,t){return M("sha256").update(JSON.stringify(["user",e,t])).digest("base64url").slice(0,16)}r(z,"filterHash");function U(e,t){return Buffer.from(JSON.stringify({n:e,f:t}),"utf8").toString("base64url")}r(U,"encodeCursor");function $(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"&&g.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($,"decodeCursor");async function S(e){return await H(e,"person","local-user","user:local","user")}r(S,"personObject");async function A(e,t){return await j(e,t,async n=>await B(e,n))}r(A,"graphReader");async function b(e,t){const n=await e.prepare(`SELECT r.body_hash, r.created_at FROM revisions r WHERE r.document = ?
|
|
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 R();return{head:n,first:o}}r(b,"revisions");async function I(e,t){const{head:n,first:o}=await b(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(I,"item");async function G(e,t){return t===null?null:(await e.prepare(`SELECT i.external_id FROM objects a
|
|
3
|
+
JOIN identities i ON i.object = CASE WHEN a.type = 'node' THEN a.owner 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");const L=200;async function P(e,t){v(t);const n=t.get("path");if(n!==null&&!g.test(n))throw s("path must be name segments of letters, digits, `_` or `-` joined by `/`","path");const o=Y(t),a=J(t),d=z(n,o);let i=$(t.get("cursor"),d);return await N(async u=>{const p=await A(u,e),c=await S(u),m=n===null?null:`${n}/`,h=o===null?O:[o],k=u.prepare(`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
|
-
AND o.kind IN (?, ?) AND (
|
|
7
|
-
ORDER BY o.name LIMIT ?`),l=[];for(;;){const f=await k.all(
|
|
8
|
-
WHERE o.owner = ? AND o.type = 'document' AND o.name = ? AND o.deleted_at IS NULL AND o.kind IN ('knowledge', 'preference')`).get(d.
|
|
6
|
+
AND o.kind IN (?, ?) AND (?::text IS NULL OR starts_with(o.name, ?::text)) AND (?::text IS NULL OR o.name > ?::text)
|
|
7
|
+
ORDER BY o.name LIMIT ?`),l=[];for(;;){const f=await k.all(c.id,h[0],h[h.length-1],m,m,i,i,L);for(const y of f)if(i=y.name,await _(u,p,y)&&l.push(y),l.length>a)break;if(l.length>a||f.length<L)break}const w=l.slice(0,a),T=l.length>a;return{items:await F(w,async f=>await I(u,f)),next_cursor:T?U(w[w.length-1].name,d):null}})}r(P,"memoryList");async function X(e,t){v(t);const n=t.get("name");if(n===null)throw s("name is required","name");if(!g.test(n))throw s("name must be segments of letters, digits, `_` or `-` joined by `/`","name");return await N(async o=>{const a=await A(o,e),d=await S(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
|
|
8
|
+
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 _(o,a,i))throw R();const{head:u,first:p}=await b(o,i),c=Buffer.from(C(u.body_hash,i.source_path),"utf8"),m=Z(c,W);return{...await I(o,i),body:m.toString("utf8"),size:c.length,truncated:m.length<c.length,grantee:await G(o,p.author)}})}r(X,"memoryGet");async function D(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 E(404,"not_found","unknown operation")}r(D,"handleMemoryOperation");const st=[{method:"GET",pattern:"/v1/memory",handler:r(async e=>await D("memory.list",e.principal,e.query),"handler")},{method:"GET",pattern:"/v1/memory/document",handler:r(async e=>await D("memory.get",e.principal,e.query),"handler")}];export{W as MEMORY_BODY_MAX_BYTES,it as MEMORY_OPERATIONS,D as handleMemoryOperation,X as memoryGet,P as memoryList,st as memoryRoutes,Z as utf8Prefix};
|