@north-light/cli 0.3.573 → 0.3.575

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/clients/attach/viewer.js +1 -1
  2. package/dist/core/canvas/canvas.d.ts +2 -0
  3. package/dist/core/canvas/canvas.js +18 -18
  4. package/dist/core/canvas/crons.d.ts +6 -2
  5. package/dist/core/canvas/crons.js +11 -9
  6. package/dist/core/grants/mirror.d.ts +5 -0
  7. package/dist/core/grants/mirror.js +2 -2
  8. package/dist/core/provider-error-recognition.js +1 -1
  9. package/dist/core/providers/bundles.js +2 -2
  10. package/dist/core/providers/call.js +1 -1
  11. package/dist/core/providers/files.js +1 -1
  12. package/dist/core/runs/operations.js +1 -1
  13. package/dist/core/runtime/host.js +3 -3
  14. package/dist/core/runtime/revive.js +2 -2
  15. package/dist/core/runtime/spawn.js +2 -2
  16. package/dist/core/runtime/view-socket.d.ts +24 -3
  17. package/dist/core/runtime/view-socket.js +2 -2
  18. package/dist/core/storage/client.js +2 -2
  19. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  20. package/dist/daemon/api/handlers/crons.js +1 -1
  21. package/dist/daemon/api/handlers/docs.js +1 -1
  22. package/dist/daemon/api/handlers/objects.d.ts +7 -1
  23. package/dist/daemon/api/handlers/objects.js +1 -1
  24. package/dist/daemon/boot-migrations.d.ts +1 -1
  25. package/dist/daemon/boot-migrations.js +1 -1
  26. package/dist/daemon/cron/lease-recovery.js +1 -1
  27. package/dist/daemon/cron/narrowed.d.ts +2 -2
  28. package/dist/daemon/cron/narrowed.js +2 -2
  29. package/dist/daemon/cron/settle.js +1 -1
  30. package/dist/daemon/cron-run.js +2 -2
  31. package/dist/daemon/crtrd.js +1 -1
  32. package/dist/launcher/run.js +2 -2
  33. package/dist/launcher/spec.d.ts +3 -1
  34. package/dist/pi-extensions/canvas-inbox-watcher.js +1 -1
  35. package/package.json +2 -2
  36. package/packages/crouter-identity/package.json +1 -1
  37. package/runtime.lock.json +13 -13
@@ -1,3 +1,3 @@
1
- var x=Object.defineProperty;var s=(r,l)=>x(r,"name",{value:l,configurable:!0});import{createReadStream as S,read as v,statSync as $}from"node:fs";import{Readable as w}from"node:stream";import{promisify as T}from"node:util";import{readConfig as E}from"../config.js";import{directoryIssuer as C}from"../directory/issuer.js";import{RunError as m}from"../runs/operations.js";import{mintDirectoryToken as O,runtimeId as P}from"../grants/sync.js";const F=104857600;class b extends Error{static{s(this,"StorageRefused")}status;code;constructor(l,u,d){super(d),this.status=l,this.code=u}}const U=T(v);async function*j(r,l){let u=0;for(;u<l;){const d=Buffer.allocUnsafe(Math.min(1048576,l-u)),{bytesRead:f}=await U(r,d,0,d.length,u);if(f===0)throw new Error("file shrank while it was being copied");u+=f,yield d.subarray(0,f)}}s(j,"fdChunks");const i=s(r=>new m(503,"storage_unavailable",r,void 0,void 0,void 0,!0),"unavailable");function A(){const r=E("user").runtime?.storage;return!r?.service_url||!r.base_url?null:{service_url:r.service_url.replace(/\/$/,""),base_url:r.base_url.replace(/\/$/,"")}}s(A,"storageConfig");let _;function G(){const r=A(),l=C();if(!r||!l)throw i("file storage is not configured on this runtime");const u=`${l}
1
+ var x=Object.defineProperty;var i=(r,d)=>x(r,"name",{value:d,configurable:!0});import{createReadStream as v,read as S,statSync as $}from"node:fs";import{Readable as _}from"node:stream";import{promisify as T}from"node:util";import{readConfig as E}from"../config.js";import{directoryIssuer as O}from"../directory/issuer.js";import{RunError as b}from"../runs/operations.js";import{mintDirectoryToken as C,runtimeId as R}from"../grants/sync.js";const G=104857600;class m extends Error{static{i(this,"StorageRefused")}status;code;constructor(d,u,l){super(l),this.status=d,this.code=u}}const j=T(S);async function*U(r,d){let u=0;for(;u<d;){const l=Buffer.allocUnsafe(Math.min(1048576,d-u)),{bytesRead:f}=await j(r,l,0,l.length,u);if(f===0)throw new Error("file shrank while it was being copied");u+=f,yield l.subarray(0,f)}}i(U,"fdChunks");const s=i(r=>new b(503,"storage_unavailable",r,void 0,void 0,void 0,!0),"unavailable");function P(){const r=E("user").runtime?.storage;return!r?.service_url||!r.base_url?null:{service_url:r.service_url.replace(/\/$/,""),base_url:r.base_url.replace(/\/$/,"")}}i(P,"storageConfig");let w;function F(){const r=P(),d=O();if(!r||!d)throw new b(503,"storage_unavailable","file storage is not configured on this runtime: it has no `runtime.storage` setting (service_url, base_url) or no joined directory. Only a hosted runtime has file storage (its provisioner sets CRTR_BOOT_STORAGE_URL), and the directory issues storage tokens only to a sandboxed runtime, so a computer joined with `crtr sys join` cannot send or receive files yet. Retrying does not help: run this command from your hosted runtime.",void 0,void 0,void 0,!1);const u=`${d}
2
2
  ${r.service_url}
3
- ${r.base_url}`;return _?.key!==u&&(_={key:u,client:L(r,()=>O(l,r.service_url),P())}),_.client}s(G,"storageClient");function J(){_=void 0}s(J,"resetStorageClient");function L(r,l,u){let d,f;const k=s(async()=>{if(d&&d.expires_at-6e4>Date.now())return d.access_token;f??=l().finally(()=>{f=void 0});try{d=await f}catch(t){throw i(`storage token could not be minted: ${t instanceof Error?t.message:String(t)}`)}return d.access_token},"bearer"),c=s(async(t,e,n={})=>{const p=[AbortSignal.timeout(n.timeoutMs??1e4),...n.signal?[n.signal]:[]];let a;try{a=await fetch(`${r.service_url}${e}`,{method:t,signal:AbortSignal.any(p),headers:{Authorization:`Bearer ${await k()}`,...n.body===void 0?{}:{"Content-Type":"application/json"}},...n.body===void 0?{}:{body:JSON.stringify(n.body)}})}catch(o){throw o instanceof m?o:i(`storage could not be reached: ${o instanceof Error?o.message:String(o)}`)}if(a.status===401&&(d=void 0),a.status===404||a.status===409){const o=await a.json().catch(()=>({})),g=o.error?.code??o.code??(a.status===404?"not_found":"not_uploaded");throw new b(a.status,g,o.error?.message??o.message??`storage answered ${a.status}`)}if(!a.ok)throw await a.body?.cancel().catch(()=>{}),i(`storage answered ${a.status} to ${t} ${e.split("?")[0]}`);return a},"call"),y=s(async t=>{try{return await t.json()}catch{throw i("storage answered with an invalid body")}},"json"),h=s(t=>encodeURIComponent(t),"id");return{config:r,runtimeId:u,async createUpload(t){const e=await y(await c("POST","/v1/uploads",{body:t}));if(typeof e.upload_id!="string"||typeof e.url!="string"||typeof e.upload_link!="string"||typeof e.expires_at!="string")throw i("storage answered with an invalid upload");return{upload_id:e.upload_id,url:e.url,upload_link:e.upload_link,expires_at:e.expires_at}},async fetchUpload(t,e){const n=await c("GET",`/v1/uploads/${h(t)}/content`,{...e?{signal:e}:{},timeoutMs:11e4});if(!n.body)throw i("storage answered with no body");const p=n.headers.get("content-length");return{body:w.fromWeb(n.body),size:p===null?null:Number(p),media_type:n.headers.get("content-type")}},async signDownload(t){const e=await y(await c("POST",`/v1/uploads/${h(t)}/download-link`));if(typeof e.url!="string"||typeof e.expires_at!="string")throw i("storage answered with an invalid link");return{url:e.url,expires_at:e.expires_at}},async deleteUpload(t){try{await c("DELETE",`/v1/uploads/${h(t)}`)}catch(e){if(!(e instanceof b&&e.status===404))throw e}},async createShare(t){const e=await y(await c("POST","/v1/shares",{body:t}));if(typeof e.share_id!="string"||typeof e.url!="string"||typeof e.upload_link!="string")throw i("storage answered with an invalid share");return{share_id:e.share_id,url:e.url,upload_link:e.upload_link,expires_at:e.expires_at??null}},async putShareBytes(t,e,n,p){if($(e).size!==n)throw new Error("shared file changed size while it was being copied");let a;try{a=await fetch(t,{method:"PUT",signal:AbortSignal.timeout(11e4),headers:{"Content-Type":p,"Content-Length":String(n)},body:w.toWeb(S(e)),duplex:"half"})}catch(o){throw i(`storage could not be reached: ${o instanceof Error?o.message:String(o)}`)}if(await a.body?.cancel().catch(()=>{}),!a.ok)throw i(`storage answered ${a.status} to the share upload`)},async deleteShare(t){try{await c("DELETE",`/v1/shares/${h(t)}`)}catch(e){if(!(e instanceof b&&e.status===404))throw e}},async createResultUpload(t){const e=await y(await c("POST","/v1/uploads",{body:{name:t.name,media_type:t.media_type,max_size:t.max_size}}));if(typeof e.upload_id!="string"||typeof e.url!="string"||typeof e.upload_link!="string"||typeof e.expires_at!="string")throw i("storage answered with an invalid upload");return{upload_id:e.upload_id,url:e.url,upload_link:e.upload_link,expires_at:e.expires_at}},async signUploadLink(t){const e=await y(await c("POST",`/v1/uploads/${h(t)}/upload-link`));if(typeof e.upload_link!="string"||typeof e.expires_at!="string")throw i("storage answered with an invalid upload link");return{upload_link:e.upload_link,expires_at:e.expires_at}},async createOutbound(t){const e=await y(await c("POST","/v1/outbound",{body:t}));if(typeof e.object_id!="string"||typeof e.upload_link!="string"||typeof e.url!="string"||typeof e.expires_at!="string")throw i("storage answered with an invalid outbound object");return{object_id:e.object_id,upload_link:e.upload_link,url:e.url,link_expires_at:e.link_expires_at,expires_at:e.expires_at}},async signOutbound(t){const e=await y(await c("POST",`/v1/outbound/${h(t)}/link`));if(typeof e.url!="string"||typeof e.expires_at!="string")throw i("storage answered with an invalid link");return{url:e.url,link_expires_at:e.link_expires_at,expires_at:e.expires_at}},async putFileBytes(t,e,n,p){const a=w.from(j(e,n));let o;try{o=await fetch(t,{method:"PUT",signal:AbortSignal.timeout(11e4),headers:{"Content-Type":p,"Content-Length":String(n)},body:w.toWeb(a),duplex:"half"})}catch(g){throw i(`storage could not be reached: ${g instanceof Error?g.message:String(g)}`)}finally{a.destroy()}if(await o.body?.cancel().catch(()=>{}),!o.ok)throw i(`storage answered ${o.status} to the upload`)}}}s(L,"createStorageClient");function X(r){return!r||Buffer.byteLength(r,"utf8")>255?"name must be 1-255 bytes":r==="."||r===".."?"name must not be . or ..":/[/\\\u0000-\u001f\u007f]/.test(r)?"name must not contain /, \\, or control characters":null}s(X,"invalidStorageName");export{b as StorageRefused,F as UPLOAD_MAX_BYTES,L as createStorageClient,X as invalidStorageName,J as resetStorageClient,G as storageClient,A as storageConfig};
3
+ ${r.base_url}`;return w?.key!==u&&(w={key:u,client:A(r,()=>C(d,r.service_url),R())}),w.client}i(F,"storageClient");function J(){w=void 0}i(J,"resetStorageClient");function A(r,d,u){let l,f;const k=i(async()=>{if(l&&l.expires_at-6e4>Date.now())return l.access_token;f??=d().finally(()=>{f=void 0});try{l=await f}catch(t){throw s(`storage token could not be minted: ${t instanceof Error?t.message:String(t)}`)}return l.access_token},"bearer"),c=i(async(t,e,n={})=>{const p=[AbortSignal.timeout(n.timeoutMs??1e4),...n.signal?[n.signal]:[]];let o;try{o=await fetch(`${r.service_url}${e}`,{method:t,signal:AbortSignal.any(p),headers:{Authorization:`Bearer ${await k()}`,...n.body===void 0?{}:{"Content-Type":"application/json"}},...n.body===void 0?{}:{body:JSON.stringify(n.body)}})}catch(a){throw a instanceof b?a:s(`storage could not be reached: ${a instanceof Error?a.message:String(a)}`)}if(o.status===401&&(l=void 0),o.status===404||o.status===409){const a=await o.json().catch(()=>({})),g=a.error?.code??a.code??(o.status===404?"not_found":"not_uploaded");throw new m(o.status,g,a.error?.message??a.message??`storage answered ${o.status}`)}if(!o.ok)throw await o.body?.cancel().catch(()=>{}),s(`storage answered ${o.status} to ${t} ${e.split("?")[0]}`);return o},"call"),y=i(async t=>{try{return await t.json()}catch{throw s("storage answered with an invalid body")}},"json"),h=i(t=>encodeURIComponent(t),"id");return{config:r,runtimeId:u,async createUpload(t){const e=await y(await c("POST","/v1/uploads",{body:t}));if(typeof e.upload_id!="string"||typeof e.url!="string"||typeof e.upload_link!="string"||typeof e.expires_at!="string")throw s("storage answered with an invalid upload");return{upload_id:e.upload_id,url:e.url,upload_link:e.upload_link,expires_at:e.expires_at}},async fetchUpload(t,e){const n=await c("GET",`/v1/uploads/${h(t)}/content`,{...e?{signal:e}:{},timeoutMs:11e4});if(!n.body)throw s("storage answered with no body");const p=n.headers.get("content-length");return{body:_.fromWeb(n.body),size:p===null?null:Number(p),media_type:n.headers.get("content-type")}},async signDownload(t){const e=await y(await c("POST",`/v1/uploads/${h(t)}/download-link`));if(typeof e.url!="string"||typeof e.expires_at!="string")throw s("storage answered with an invalid link");return{url:e.url,expires_at:e.expires_at}},async deleteUpload(t){try{await c("DELETE",`/v1/uploads/${h(t)}`)}catch(e){if(!(e instanceof m&&e.status===404))throw e}},async createShare(t){const e=await y(await c("POST","/v1/shares",{body:t}));if(typeof e.share_id!="string"||typeof e.url!="string"||typeof e.upload_link!="string")throw s("storage answered with an invalid share");return{share_id:e.share_id,url:e.url,upload_link:e.upload_link,expires_at:e.expires_at??null}},async putShareBytes(t,e,n,p){if($(e).size!==n)throw new Error("shared file changed size while it was being copied");let o;try{o=await fetch(t,{method:"PUT",signal:AbortSignal.timeout(11e4),headers:{"Content-Type":p,"Content-Length":String(n)},body:_.toWeb(v(e)),duplex:"half"})}catch(a){throw s(`storage could not be reached: ${a instanceof Error?a.message:String(a)}`)}if(await o.body?.cancel().catch(()=>{}),!o.ok)throw s(`storage answered ${o.status} to the share upload`)},async deleteShare(t){try{await c("DELETE",`/v1/shares/${h(t)}`)}catch(e){if(!(e instanceof m&&e.status===404))throw e}},async createResultUpload(t){const e=await y(await c("POST","/v1/uploads",{body:{name:t.name,media_type:t.media_type,max_size:t.max_size}}));if(typeof e.upload_id!="string"||typeof e.url!="string"||typeof e.upload_link!="string"||typeof e.expires_at!="string")throw s("storage answered with an invalid upload");return{upload_id:e.upload_id,url:e.url,upload_link:e.upload_link,expires_at:e.expires_at}},async signUploadLink(t){const e=await y(await c("POST",`/v1/uploads/${h(t)}/upload-link`));if(typeof e.upload_link!="string"||typeof e.expires_at!="string")throw s("storage answered with an invalid upload link");return{upload_link:e.upload_link,expires_at:e.expires_at}},async createOutbound(t){const e=await y(await c("POST","/v1/outbound",{body:t}));if(typeof e.object_id!="string"||typeof e.upload_link!="string"||typeof e.url!="string"||typeof e.expires_at!="string")throw s("storage answered with an invalid outbound object");return{object_id:e.object_id,upload_link:e.upload_link,url:e.url,link_expires_at:e.link_expires_at,expires_at:e.expires_at}},async signOutbound(t){const e=await y(await c("POST",`/v1/outbound/${h(t)}/link`));if(typeof e.url!="string"||typeof e.expires_at!="string")throw s("storage answered with an invalid link");return{url:e.url,link_expires_at:e.link_expires_at,expires_at:e.expires_at}},async putFileBytes(t,e,n,p){const o=_.from(U(e,n));let a;try{a=await fetch(t,{method:"PUT",signal:AbortSignal.timeout(11e4),headers:{"Content-Type":p,"Content-Length":String(n)},body:_.toWeb(o),duplex:"half"})}catch(g){throw s(`storage could not be reached: ${g instanceof Error?g.message:String(g)}`)}finally{o.destroy()}if(await a.body?.cancel().catch(()=>{}),!a.ok)throw s(`storage answered ${a.status} to the upload`)}}}i(A,"createStorageClient");function X(r){return!r||Buffer.byteLength(r,"utf8")>255?"name must be 1-255 bytes":r==="."||r===".."?"name must not be . or ..":/[/\\\u0000-\u001f\u007f]/.test(r)?"name must not contain /, \\, or control characters":null}i(X,"invalidStorageName");export{m as StorageRefused,G as UPLOAD_MAX_BYTES,A as createStorageClient,X as invalidStorageName,J as resetStorageClient,F as storageClient,P as storageConfig};
@@ -1 +1 @@
1
- var I=Object.defineProperty;var o=(e,t)=>I(e,"name",{value:t,configurable:!0});import{canonicalSessionFile as q,findNodeBySessionFile as M,getNode as h,isWarmSpare as $,recordRespawnAttempt as j,recordSettledTurn as D,setPresence as k,subscriptionsOf as L,updateNode as v,updateNodeTelemetry as U}from"../../../core/canvas/canvas.js";import{liveExecutionForNode as x,recordExecutionIdle as W,recordExecutionPid as H}from"../../../core/canvas/executions.js";import{bindSession as G,readLiveSession as V,transferSession as J}from"../../../core/canvas/sessions.js";import{isSafeNodeId as Y}from"../../../core/canvas/paths.js";import{appForNode as z}from"../../../core/canvas/node-agent-paths.js";import{isPidAlive as K}from"../../../core/canvas/pid.js";import{editorLabel as S}from"../../../core/canvas/labels.js";import{assembleNodeSubject as Q}from"../../../core/substrate/subject.js";import{openDb as X,withCanvasWrite as p}from"../../../core/canvas/db.js";import{runningJobs as Z}from"../../../core/graph/jobs.js";import{notFound as g,usage as u}from"../../../core/errors.js";import{hasUnseenWakeMail as P}from"../../../core/canvas/deliveries.js";import{clearYieldMessage as ee}from"../../../core/runtime/kickoff.js";import{readRoadmap as te}from"../../../core/runtime/roadmap.js";import{forkSourceNode as ne}from"../../../core/runtime/nodes.js";import{transition as _}from"../../../core/runtime/lifecycle.js";import{confirmViewer as R,probeViewer as O,tearDownNode as F}from"../../../core/runtime/placement.js";import{hasAttachedBrokerViewers as ie}from"../viewer-presence.js";import{evaluateStop as re}from"../../../core/runtime/stop-guard.js";import{readOutputRequest as oe}from"../../../core/runtime/output-result.js";import{clearFault as w,readFault as ae,recordFault as se}from"../../../core/canvas/recovery.js";import{updateModelRecipe as de}from"../../../core/runtime/model-swap.js";import{completeCompanionRequestedSubmit as ce}from"../../review/finish.js";import{emitEvent as A}from"../../../core/events/emit.js";import{consumePendingPark as ue,recordPendingParkActivity as le}from"../../park-pending.js";import{shouldParkAfterSummary as pe}from"../../park-activity.js";import{ownerOnly as fe}from"../router.js";import{situationalContextEnvelope as me}from"../../../core/runtime/situational-context.js";import{requireCurrentExecution as f}from"./broker-recovery.js";import{ensureRunObserver as we}from"../../runs/observer.js";import{parseNodeTelemetryRecord as ye,writeNodeTelemetryRecord as he}from"./telemetry-record.js";import{mapAsync as E}from"../../../core/async-array.js";function l(e,t){if(typeof e!="object"||e===null||Array.isArray(e))throw u(`${t} requires a JSON body`);return e}o(l,"objectBody");function _e(e){const t=l(e,"broker session-bound");if(typeof t.executionId!="string"||t.executionId==="")throw u("executionId must be a non-empty string");if(typeof t.piSessionId!="string"||t.piSessionId==="")throw u("piSessionId must be a non-empty string");if(t.sessionFile!==null&&typeof t.sessionFile!="string")throw u("sessionFile must be a string or null");if(!Number.isSafeInteger(t.pid)||t.pid<=0)throw u("pid must be a positive safe integer");if(t.reason!==null&&typeof t.reason!="string")throw u("reason must be a string or null");if(!Array.isArray(t.reviewBoundaryIds)||!t.reviewBoundaryIds.every(n=>typeof n=="string"))throw u("reviewBoundaryIds must be an array of strings");return t}o(_e,"parseSessionBoundBody");function be(e){const t=l(e,"broker settle");if(typeof t.stopReason!="string")throw u("stopReason must be a string");for(const n of["pushedFinal"])if(typeof t[n]!="boolean")throw u(`${n} must be a boolean`);return t}o(be,"parseSettleBody");function ke(e){const t=l(e,"broker park-complete");if(t.outcome!=="completed"&&t.outcome!=="failed")throw u("outcome must be completed or failed");return t}o(ke,"parseParkCompleteBody");function ve(e){const t=l(e,"broker telemetry");if(typeof t.expected_execution_id!="string"||t.expected_execution_id==="")throw u("expected_execution_id must be a non-empty string");const n=ye(t);return{...n,expected_execution_id:t.expected_execution_id,updated_at:new Date(n.updated_at).toISOString()}}o(ve,"parseTelemetryBody");function ge(e){const t=l(e,"broker model");if(typeof t.spec!="string"||t.spec.trim()==="")throw u("spec must be a non-empty string");const n=t.intent;if(n!==void 0){const i=n!==null&&typeof n=="object"?n:void 0;if(i===void 0||typeof i.family!="string"||i.family===""||!["ultra","strong","medium","light"].includes(i.strength))throw u("intent must be { family, strength } with strength ultra|strong|medium|light when present")}if(typeof t.userSelected!="boolean")throw u("userSelected must be a boolean");return t}o(ge,"parseModelCommitBody");async function m(e){const t=await h(e);if(t===null)throw g(`unknown node: ${e}`,{received:e});return t}o(m,"requireNode");async function b(e){return{node_id:e.node_id,grantee:await z(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}}o(b,"extensionNode");async function xe(e){const t=await m(e),n=[],i=new Set([e]);let r=t.parent??null;for(;r!==null&&r!==""&&!i.has(r);){i.add(r);const s=await h(r);if(s===null)break;n.unshift(await b(s)),r=s.parent??null}const a=await E((await E(await L(e),async s=>await h(s.node_id))).filter(s=>s!==null&&s.kind!=="human").sort((s,y)=>s.status===y.status?0:s.status==="active"?-1:y.status==="active"?1:0),b),d=typeof t.fork_source_file=="string"?await ne(t.fork_source_file):null,c=await Q(e);if(c===null)throw g(`unknown node: ${e}`,{received:e});return{node:await b(t),warm_spare:await $(e),situational_card:await me(e),ancestors:n,children:a,fork_source:d===null?null:await b(d),subject:c,roadmap:await te(e)}}o(xe,"extensionState");function T(e,t){return e?.kind===t.kind&&e?.mode===t.mode&&e?.lifecycle===t.lifecycle}o(T,"samePersona");function Se(e){const t=l(e,"broker generated-name");if(t.kind!=="initial"&&t.kind!=="recap")throw u("kind must be initial or recap");if(typeof t.description!="string"||typeof t.title!="string"||typeof t.icon!="string")throw u("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(i=>typeof n[i]!="string"))throw u("recap expected must contain name, description, title, icon, and kind strings");return t}o(Se,"parseGeneratedNameBody");function Pe(e){const t=l(e,"broker persona-ack"),n=o(i=>{const r=t[i];if(typeof r!="object"||r===null||Array.isArray(r)||typeof r.kind!="string"||r.mode!=="base"&&r.mode!=="orchestrator"||r.lifecycle!=="terminal"&&r.lifecycle!=="resident")throw u(`${i} must be a persona state`);return r},"parse");return{expected_execution_id:t.expected_execution_id,from:n("from"),to:n("to")}}o(Pe,"parsePersonaAckBody");async function Re(e){const t=e.params.id,n=_e(e.body),i=n.sessionFile===null?null:q(n.sessionFile);return{status:200,body:await p(async a=>{await a.prepareUncached("SELECT 1 FROM nodes WHERE node_id = ? FOR UPDATE").get(t);const d=await m(t);if(await H(n.executionId,t,n.pid)==="shutdown")return{action:"shutdown"};const c=d.review_binding?.review_id;if(c!==void 0&&!n.reviewBoundaryIds.includes(c))return{action:"shutdown"};if(n.reason==="new"&&d.parent==null)return{action:"relaunch_root"};if(n.reason!=="new"&&d.fork_from!=null&&d.pi_session_id==null&&i===null)return{action:"shutdown"};const s=i===null?null:await M(i);if(s!==null&&s.node_id!==t){if(K(s.pi_pid))return await se(t,{link:"daemon\u2192node",op:"session-file-ownership",kind:"protocol",retry:{disposition:"fatal"},message:`refusing to bind ${i}: it is owned by live node ${s.node_id}`}),{action:"shutdown"};const y=await V(s.node_id);if(y===null||await J({fromNodeId:s.node_id,fromBindingId:y.bindingId,toNodeId:t,executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:i})!=="transferred")return{action:"shutdown"}}else if(await G(t,{executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:i})==="shutdown")return{action:"shutdown"};return n.reason!=="new"&&(await ee(t),d.intent==="refresh"&&await _(t,"revive")),{action:"none"}})}}o(Re,"handleSessionBound");async function B(e,t){return t.live!==null||await ie(e)||await P(e)}o(B,"isHeldAwake");async function N(e,t){return(await x(e))?.runner==="blaxel"&&!await B(e,t)&&(await Z(await X(),e)).length===0}o(N,"residentBlaxelCanIdle");async function Oe(e,t,n){const i=await h(e);if(i===null){if(t.pushedFinal)return{directive:{action:"shutdown"},closePane:!1};throw g(`unknown node: ${e}`,{received:e})}if(await D(e,new Date().toISOString()),(t.stopReason==="stop"||t.stopReason==="length")&&await j(e,{failures:0,notBefore:null}),t.stopReason==="quit"){let c=!1;return(i.status==="active"||i.status==="idle")&&i.intent===null&&(await _(e,"finish"),await w(e),await k(e,{pane:null,tmux_session:null,window:null}),c=!0),{directive:{action:"shutdown"},closePane:c}}if(t.stopReason!=="stop"&&t.stopReason!=="length")return await w(e,{link:"daemon\u2192node"}),i.intent==="refresh"?{directive:{action:"shutdown"},closePane:!1}:t.stopReason==="error"&&t.faultSettled===!0&&(await ae(e))?.retry.disposition==="fatal"&&await oe(e)===null&&await N(e,n)?{directive:{action:"stay_resident"},closePane:!1}:{directive:{action:"stay_dormant"},closePane:!1};const r={pushedFinal:t.pushedFinal};if(r.pushedFinal||i.status==="done")return await w(e),await k(e,{pane:null,tmux_session:null,window:null}),{directive:{action:"shutdown"},closePane:!0};if(i.intent==="refresh")return await w(e),{directive:{action:"shutdown"},closePane:!1};const a=await re(e,r);if(await w(e),a.action==="reprompt")return{directive:{action:"reprompt",prompt:a.message},closePane:!1};if(a.reason==="finished")return{directive:await P(e)?{action:"stay_dormant"}:{action:"shutdown"},closePane:!1};const d=await B(e,n);return(await x(e))?.runner==="blaxel"?{directive:await N(e,n)?{action:"stay_resident"}:{action:"stay_dormant"},closePane:!1}:a.reason!=="awaiting"&&a.reason!=="scheduled"?{directive:{action:"stay_dormant"},closePane:!1}:d?{directive:{action:"stay_dormant"},closePane:!1}:(await _(e,"release"),{directive:{action:"shutdown"},closePane:!1})}o(Oe,"settleBroker");async function Fe(e){const t=e.params.id,n=l(e.body,"broker idle");return await f(t,n.expected_execution_id),await W(n.expected_execution_id),{status:204}}o(Fe,"handleIdle");async function Ae(e){const t=e.params.id,n=be(e.body);await f(t,n.expected_execution_id);const i=await O(t),{directive:r,closePane:a}=await p(async()=>await Oe(t,n,await R(i)));return a&&await F(t),r.action!=="reprompt"&&ce(t).catch(d=>A({level:"error",event:"api.broker_ops.companion_submit_failed",node_id:t,error:d})),{status:200,body:r}}o(Ae,"handleSettle");async function Ee(e){const t=e.params.id,n=ke(e.body);await f(t,n.expected_execution_id);const i=await O(t),r=await p(async()=>{const a=await ue(t);if(a===void 0)return{action:"stay_dormant"};const d=await m(t);return await pe(t,d,a,await R(i))?(await w(t),await _(t,"park"),await k(t,{pane:null,tmux_session:null,window:null}),A({level:n.outcome==="completed"?"info":"warn",event:"broker.park.complete",...Y(t)?{node_id:t}:{},outcome:n.outcome==="completed"?"succeeded":"failed"}),{action:"shutdown"}):{action:"stay_dormant"}});return r.action==="shutdown"&&await F(t),{status:200,body:r}}o(Ee,"handleParkComplete");async function Te(e){const t=e.params.id;return await f(t,l(e.body,"broker park-activity").expected_execution_id),{status:200,body:await p(async()=>({activity:await le(t)?"recorded":"none"}))}}o(Te,"handleParkActivity");async function Be(e){const t=e.params.id,n=ve(e.body);return await f(t,n.expected_execution_id),await p(async()=>{await U(t,{tokensIn:n.tokens_in,contextTokens:n.context_tokens,lastActivity:n.last_activity,updatedAt:n.updated_at}),he(t,n)}),{status:204}}o(Be,"handleTelemetry");async function Ne(e){const t=e.params.id,n=ge(e.body);return await f(t,n.expected_execution_id),{status:200,body:{modelOverride:(await p(async()=>await de(await m(t),n.spec,n.intent,n.userSelected))).model_override}}}o(Ne,"handleModelCommit");let C=!0;function yt(e){C=e}o(yt,"setRequestsStartRunObservers");async function Ce(e){const t=e.params.id,n=await m(t);return n.run_id&&C&&await we(t,n.run_id),{status:200,body:await xe(t)}}o(Ce,"handleExtensionState");async function Ie(e){const t=e.params.id,n=Se(e.body);return await f(t,n.expected_execution_id),{status:200,body:await p(async r=>{await r.prepareUncached("SELECT 1 FROM nodes WHERE node_id = ? FOR UPDATE").get(t);const a=await m(t);if(n.kind==="initial"){if((a.description??"").trim()!=="")return{applied:!1};const s=await v(t,{description:n.description,...n.title.trim()===""?{}:{title:n.title},...n.icon.trim()===""?{}:{icon:n.icon}});return{applied:!0,editorLabel:S(s),description:s.description??"",title:s.title??"",icon:s.icon??""}}const d=n.expected;if(a.name!==d.name||(a.description??"")!==d.description||(a.title??"")!==d.title||(a.icon??"")!==d.icon||a.kind!==d.kind)return{applied:!1};const c=await v(t,{description:n.description,title:n.title.trim()===""?void 0:n.title,icon:n.icon.trim()===""?void 0:n.icon});return{applied:!0,editorLabel:S(c),description:c.description??"",title:c.title??"",icon:c.icon??""}})}}o(Ie,"handleGeneratedName");async function qe(e){const t=e.params.id,n=Pe(e.body);return await f(t,n.expected_execution_id),{status:200,body:await p(async r=>{await r.prepareUncached("SELECT 1 FROM nodes WHERE node_id = ? FOR UPDATE").get(t);const a=await m(t),d={kind:a.kind,mode:a.mode,lifecycle:a.lifecycle},c=a.persona_ack,s=c===void 0?d:{kind:c.kind??a.kind,mode:c.mode,lifecycle:c.lifecycle};return!T(d,n.to)||!T(s,n.from)?{applied:!1}:(await v(t,{persona_ack:n.to}),{applied:!0})})}}o(qe,"handlePersonaAck");const ht=fe([{method:"POST",pattern:"/v1/nodes/:id/broker/session-bound",handler:Re},{method:"POST",pattern:"/v1/nodes/:id/broker/settle",handler:Ae},{method:"POST",pattern:"/v1/nodes/:id/broker/idle",handler:Fe},{method:"POST",pattern:"/v1/nodes/:id/broker/park-complete",handler:Ee},{method:"POST",pattern:"/v1/nodes/:id/broker/park-activity",handler:Te},{method:"POST",pattern:"/v1/nodes/:id/broker/telemetry",handler:Be},{method:"POST",pattern:"/v1/nodes/:id/broker/model",handler:Ne},{method:"GET",pattern:"/v1/nodes/:id/broker/extension-state",handler:Ce},{method:"POST",pattern:"/v1/nodes/:id/broker/generated-name",handler:Ie},{method:"POST",pattern:"/v1/nodes/:id/broker/persona-ack",handler:qe}]);export{ht as brokerOperationRoutes,b as extensionNode,yt as setRequestsStartRunObservers};
1
+ var C=Object.defineProperty;var o=(e,t)=>C(e,"name",{value:t,configurable:!0});import{canonicalSessionFile as I,findNodeBySessionFile as q,getNode as b,getNodes as M,isWarmSpare as $,recordRespawnAttempt as j,recordSettledTurn as D,setPresence as k,subscriptionsOf as L,updateNode as g,updateNodeTelemetry as U}from"../../../core/canvas/canvas.js";import{liveExecutionForNode as x,recordExecutionIdle as W,recordExecutionPid as H}from"../../../core/canvas/executions.js";import{bindSession as G,readLiveSession as V,transferSession as J}from"../../../core/canvas/sessions.js";import{isSafeNodeId as Y}from"../../../core/canvas/paths.js";import{isPidAlive as z}from"../../../core/canvas/pid.js";import{editorLabel as S}from"../../../core/canvas/labels.js";import{assembleNodeSubject as K}from"../../../core/substrate/subject.js";import{openDb as Q,withCanvasWrite as p}from"../../../core/canvas/db.js";import{runningJobs as X}from"../../../core/graph/jobs.js";import{notFound as v,usage as u}from"../../../core/errors.js";import{hasUnseenWakeMail as P}from"../../../core/canvas/deliveries.js";import{clearYieldMessage as Z}from"../../../core/runtime/kickoff.js";import{readRoadmap as ee}from"../../../core/runtime/roadmap.js";import{forkSourceNode as te}from"../../../core/runtime/nodes.js";import{transition as h}from"../../../core/runtime/lifecycle.js";import{confirmViewer as R,probeViewer as O,tearDownNode as F}from"../../../core/runtime/placement.js";import{hasAttachedBrokerViewers as ne}from"../viewer-presence.js";import{evaluateStop as ie}from"../../../core/runtime/stop-guard.js";import{readOutputRequest as re}from"../../../core/runtime/output-result.js";import{clearFault as w,readFault as oe,recordFault as ae}from"../../../core/canvas/recovery.js";import{updateModelRecipe as se}from"../../../core/runtime/model-swap.js";import{completeCompanionRequestedSubmit as de}from"../../review/finish.js";import{emitEvent as A}from"../../../core/events/emit.js";import{consumePendingPark as ce,recordPendingParkActivity as ue}from"../../park-pending.js";import{shouldParkAfterSummary as le}from"../../park-activity.js";import{ownerOnly as pe}from"../router.js";import{situationalContextEnvelope as fe}from"../../../core/runtime/situational-context.js";import{requireCurrentExecution as f}from"./broker-recovery.js";import{ensureRunObserver as me}from"../../runs/observer.js";import{parseNodeTelemetryRecord as we,writeNodeTelemetryRecord as ye}from"./telemetry-record.js";import{mapAsync as he}from"../../../core/async-array.js";function l(e,t){if(typeof e!="object"||e===null||Array.isArray(e))throw u(`${t} requires a JSON body`);return e}o(l,"objectBody");function _e(e){const t=l(e,"broker session-bound");if(typeof t.executionId!="string"||t.executionId==="")throw u("executionId must be a non-empty string");if(typeof t.piSessionId!="string"||t.piSessionId==="")throw u("piSessionId must be a non-empty string");if(t.sessionFile!==null&&typeof t.sessionFile!="string")throw u("sessionFile must be a string or null");if(!Number.isSafeInteger(t.pid)||t.pid<=0)throw u("pid must be a positive safe integer");if(t.reason!==null&&typeof t.reason!="string")throw u("reason must be a string or null");if(!Array.isArray(t.reviewBoundaryIds)||!t.reviewBoundaryIds.every(n=>typeof n=="string"))throw u("reviewBoundaryIds must be an array of strings");return t}o(_e,"parseSessionBoundBody");function be(e){const t=l(e,"broker settle");if(typeof t.stopReason!="string")throw u("stopReason must be a string");for(const n of["pushedFinal"])if(typeof t[n]!="boolean")throw u(`${n} must be a boolean`);return t}o(be,"parseSettleBody");function ke(e){const t=l(e,"broker park-complete");if(t.outcome!=="completed"&&t.outcome!=="failed")throw u("outcome must be completed or failed");return t}o(ke,"parseParkCompleteBody");function ge(e){const t=l(e,"broker telemetry");if(typeof t.expected_execution_id!="string"||t.expected_execution_id==="")throw u("expected_execution_id must be a non-empty string");const n=we(t);return{...n,expected_execution_id:t.expected_execution_id,updated_at:new Date(n.updated_at).toISOString()}}o(ge,"parseTelemetryBody");function ve(e){const t=l(e,"broker model");if(typeof t.spec!="string"||t.spec.trim()==="")throw u("spec must be a non-empty string");const n=t.intent;if(n!==void 0){const i=n!==null&&typeof n=="object"?n:void 0;if(i===void 0||typeof i.family!="string"||i.family===""||!["ultra","strong","medium","light"].includes(i.strength))throw u("intent must be { family, strength } with strength ultra|strong|medium|light when present")}if(typeof t.userSelected!="boolean")throw u("userSelected must be a boolean");return t}o(ve,"parseModelCommitBody");async function m(e){const t=await b(e);if(t===null)throw v(`unknown node: ${e}`,{received:e});return t}o(m,"requireNode");async function _(e){return{node_id:e.node_id,grantee:e.grantee,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}}o(_,"extensionNode");async function xe(e){const t=await m(e),n=[],i=new Set([e]);let r=t.parent??null;for(;r!==null&&r!==""&&!i.has(r);){i.add(r);const s=await b(r);if(s===null)break;n.unshift(await _(s)),r=s.parent??null}const a=await he((await M((await L(e)).map(s=>s.node_id))).filter(s=>s.kind!=="human").sort((s,y)=>s.status===y.status?0:s.status==="active"?-1:y.status==="active"?1:0),_),d=typeof t.fork_source_file=="string"?await te(t.fork_source_file):null,c=await K(e);if(c===null)throw v(`unknown node: ${e}`,{received:e});return{node:await _(t),warm_spare:await $(e),situational_card:await fe(e),ancestors:n,children:a,fork_source:d===null?null:await _(d),subject:c,roadmap:await ee(e)}}o(xe,"extensionState");function E(e,t){return e?.kind===t.kind&&e?.mode===t.mode&&e?.lifecycle===t.lifecycle}o(E,"samePersona");function Se(e){const t=l(e,"broker generated-name");if(t.kind!=="initial"&&t.kind!=="recap")throw u("kind must be initial or recap");if(typeof t.description!="string"||typeof t.title!="string"||typeof t.icon!="string")throw u("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(i=>typeof n[i]!="string"))throw u("recap expected must contain name, description, title, icon, and kind strings");return t}o(Se,"parseGeneratedNameBody");function Pe(e){const t=l(e,"broker persona-ack"),n=o(i=>{const r=t[i];if(typeof r!="object"||r===null||Array.isArray(r)||typeof r.kind!="string"||r.mode!=="base"&&r.mode!=="orchestrator"||r.lifecycle!=="terminal"&&r.lifecycle!=="resident")throw u(`${i} must be a persona state`);return r},"parse");return{expected_execution_id:t.expected_execution_id,from:n("from"),to:n("to")}}o(Pe,"parsePersonaAckBody");async function Re(e){const t=e.params.id,n=_e(e.body),i=n.sessionFile===null?null:I(n.sessionFile);return{status:200,body:await p(async a=>{await a.prepareUncached("SELECT 1 FROM nodes WHERE node_id = ? FOR UPDATE").get(t);const d=await m(t);if(await H(n.executionId,t,n.pid)==="shutdown")return{action:"shutdown"};const c=d.review_binding?.review_id;if(c!==void 0&&!n.reviewBoundaryIds.includes(c))return{action:"shutdown"};if(n.reason==="new"&&d.parent==null)return{action:"relaunch_root"};if(n.reason!=="new"&&d.fork_from!=null&&d.pi_session_id==null&&i===null)return{action:"shutdown"};const s=i===null?null:await q(i);if(s!==null&&s.node_id!==t){if(z(s.pi_pid))return await ae(t,{link:"daemon\u2192node",op:"session-file-ownership",kind:"protocol",retry:{disposition:"fatal"},message:`refusing to bind ${i}: it is owned by live node ${s.node_id}`}),{action:"shutdown"};const y=await V(s.node_id);if(y===null||await J({fromNodeId:s.node_id,fromBindingId:y.bindingId,toNodeId:t,executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:i})!=="transferred")return{action:"shutdown"}}else if(await G(t,{executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:i})==="shutdown")return{action:"shutdown"};return n.reason!=="new"&&(await Z(t),d.intent==="refresh"&&await h(t,"revive")),{action:"none"}})}}o(Re,"handleSessionBound");async function T(e,t){return t.live!==null||await ne(e)||await P(e)}o(T,"isHeldAwake");async function B(e,t){return(await x(e))?.runner==="blaxel"&&!await T(e,t)&&(await X(await Q(),e)).length===0}o(B,"residentBlaxelCanIdle");async function Oe(e,t,n){const i=await b(e);if(i===null){if(t.pushedFinal)return{directive:{action:"shutdown"},closePane:!1};throw v(`unknown node: ${e}`,{received:e})}if(await D(e,new Date().toISOString()),(t.stopReason==="stop"||t.stopReason==="length")&&await j(e,{failures:0,notBefore:null}),t.stopReason==="quit"){let c=!1;return(i.status==="active"||i.status==="idle")&&i.intent===null&&(await h(e,"finish"),await w(e),await k(e,{pane:null,tmux_session:null,window:null}),c=!0),{directive:{action:"shutdown"},closePane:c}}if(t.stopReason!=="stop"&&t.stopReason!=="length")return await w(e,{link:"daemon\u2192node"}),i.intent==="refresh"?{directive:{action:"shutdown"},closePane:!1}:t.stopReason==="error"&&t.faultSettled===!0&&(await oe(e))?.retry.disposition==="fatal"&&await re(e)===null&&await B(e,n)?{directive:{action:"stay_resident"},closePane:!1}:{directive:{action:"stay_dormant"},closePane:!1};const r={pushedFinal:t.pushedFinal};if(r.pushedFinal||i.status==="done")return await w(e),await k(e,{pane:null,tmux_session:null,window:null}),{directive:{action:"shutdown"},closePane:!0};if(i.intent==="refresh")return await w(e),{directive:{action:"shutdown"},closePane:!1};const a=await ie(e,r);if(await w(e),a.action==="reprompt")return{directive:{action:"reprompt",prompt:a.message},closePane:!1};if(a.reason==="finished")return{directive:await P(e)?{action:"stay_dormant"}:{action:"shutdown"},closePane:!1};const d=await T(e,n);return(await x(e))?.runner==="blaxel"?{directive:await B(e,n)?{action:"stay_resident"}:{action:"stay_dormant"},closePane:!1}:a.reason!=="awaiting"&&a.reason!=="scheduled"?{directive:{action:"stay_dormant"},closePane:!1}:d?{directive:{action:"stay_dormant"},closePane:!1}:(await h(e,"release"),{directive:{action:"shutdown"},closePane:!1})}o(Oe,"settleBroker");async function Fe(e){const t=e.params.id,n=l(e.body,"broker idle");return await f(t,n.expected_execution_id),await W(n.expected_execution_id),{status:204}}o(Fe,"handleIdle");async function Ae(e){const t=e.params.id,n=be(e.body);await f(t,n.expected_execution_id);const i=await O(t),{directive:r,closePane:a}=await p(async()=>await Oe(t,n,await R(i)));return a&&await F(t),r.action!=="reprompt"&&de(t).catch(d=>A({level:"error",event:"api.broker_ops.companion_submit_failed",node_id:t,error:d})),{status:200,body:r}}o(Ae,"handleSettle");async function Ee(e){const t=e.params.id,n=ke(e.body);await f(t,n.expected_execution_id);const i=await O(t),r=await p(async()=>{const a=await ce(t);if(a===void 0)return{action:"stay_dormant"};const d=await m(t);return await le(t,d,a,await R(i))?(await w(t),await h(t,"park"),await k(t,{pane:null,tmux_session:null,window:null}),A({level:n.outcome==="completed"?"info":"warn",event:"broker.park.complete",...Y(t)?{node_id:t}:{},outcome:n.outcome==="completed"?"succeeded":"failed"}),{action:"shutdown"}):{action:"stay_dormant"}});return r.action==="shutdown"&&await F(t),{status:200,body:r}}o(Ee,"handleParkComplete");async function Te(e){const t=e.params.id;return await f(t,l(e.body,"broker park-activity").expected_execution_id),{status:200,body:await p(async()=>({activity:await ue(t)?"recorded":"none"}))}}o(Te,"handleParkActivity");async function Be(e){const t=e.params.id,n=ge(e.body);return await f(t,n.expected_execution_id),await p(async()=>{await U(t,{tokensIn:n.tokens_in,contextTokens:n.context_tokens,lastActivity:n.last_activity,updatedAt:n.updated_at}),ye(t,n)}),{status:204}}o(Be,"handleTelemetry");async function Ne(e){const t=e.params.id,n=ve(e.body);return await f(t,n.expected_execution_id),{status:200,body:{modelOverride:(await p(async()=>await se(await m(t),n.spec,n.intent,n.userSelected))).model_override}}}o(Ne,"handleModelCommit");let N=!0;function wt(e){N=e}o(wt,"setRequestsStartRunObservers");async function Ce(e){const t=e.params.id,n=await m(t);return n.run_id&&N&&await me(t,n.run_id),{status:200,body:await xe(t)}}o(Ce,"handleExtensionState");async function Ie(e){const t=e.params.id,n=Se(e.body);return await f(t,n.expected_execution_id),{status:200,body:await p(async r=>{await r.prepareUncached("SELECT 1 FROM nodes WHERE node_id = ? FOR UPDATE").get(t);const a=await m(t);if(n.kind==="initial"){if((a.description??"").trim()!=="")return{applied:!1};const s=await g(t,{description:n.description,...n.title.trim()===""?{}:{title:n.title},...n.icon.trim()===""?{}:{icon:n.icon}});return{applied:!0,editorLabel:S(s),description:s.description??"",title:s.title??"",icon:s.icon??""}}const d=n.expected;if(a.name!==d.name||(a.description??"")!==d.description||(a.title??"")!==d.title||(a.icon??"")!==d.icon||a.kind!==d.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:S(c),description:c.description??"",title:c.title??"",icon:c.icon??""}})}}o(Ie,"handleGeneratedName");async function qe(e){const t=e.params.id,n=Pe(e.body);return await f(t,n.expected_execution_id),{status:200,body:await p(async r=>{await r.prepareUncached("SELECT 1 FROM nodes WHERE node_id = ? FOR UPDATE").get(t);const a=await m(t),d={kind:a.kind,mode:a.mode,lifecycle:a.lifecycle},c=a.persona_ack,s=c===void 0?d:{kind:c.kind??a.kind,mode:c.mode,lifecycle:c.lifecycle};return!E(d,n.to)||!E(s,n.from)?{applied:!1}:(await g(t,{persona_ack:n.to}),{applied:!0})})}}o(qe,"handlePersonaAck");const yt=pe([{method:"POST",pattern:"/v1/nodes/:id/broker/session-bound",handler:Re},{method:"POST",pattern:"/v1/nodes/:id/broker/settle",handler:Ae},{method:"POST",pattern:"/v1/nodes/:id/broker/idle",handler:Fe},{method:"POST",pattern:"/v1/nodes/:id/broker/park-complete",handler:Ee},{method:"POST",pattern:"/v1/nodes/:id/broker/park-activity",handler:Te},{method:"POST",pattern:"/v1/nodes/:id/broker/telemetry",handler:Be},{method:"POST",pattern:"/v1/nodes/:id/broker/model",handler:Ne},{method:"GET",pattern:"/v1/nodes/:id/broker/extension-state",handler:Ce},{method:"POST",pattern:"/v1/nodes/:id/broker/generated-name",handler:Ie},{method:"POST",pattern:"/v1/nodes/:id/broker/persona-ack",handler:qe}]);export{yt as brokerOperationRoutes,_ as extensionNode,wt as setRequestsStartRunObservers};
@@ -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 G,sep as F}from"node:path";import{ApiError as s}from"../../../api/errors.js";import{armCron as rn,parseCronSink as on,getCron as v,listCrons as A,listCronRuns as L,pokeHeldCrons as tn,setCronState as Z}from"../../../core/canvas/crons.js";import{getNode as _}from"../../../core/canvas/canvas.js";import{appSpace as an,cronFolder as sn}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}from"../../../core/grants/mirror.js";import{callerTerminalGrant as fn}from"../../../core/grants/terminal.js";import{assertScope as H}from"../../../core/scopes.js";import{InputError as c}from"../../../core/io.js";import{nextSlotAfter as wn}from"../../../core/wake.js";import{executeCron as _n,markSelfCanceledCronRun as hn}from"../../cron-run.js";import{toCronDTO as k,toCronRunDTO as M}from"../map.js";import{appOf as P,assertCeiling as mn,canReadNode as gn}from"../router.js";import{checkCronBirth as yn,checkCronFire as bn,cronRefusalError 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 On}from"../../../core/async-array.js";import{personId as Cn}from"../../../core/canvas/person.js";function E(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new c({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 O(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new c({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(O,"requireEnum");async function In(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new c({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(await _(n)===null)throw new c({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 c({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 c({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(In,"parseSinkSpec");const qn=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function Y(e,n){if(!qn.test(e)||Number.isNaN(Date.parse(e)))throw new c({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(Y,"requireUtcIso");function Nn(e){try{wn(e,new Date)}catch(n){throw new c({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 jn(e){let n;try{n=JSON.parse(e)}catch{throw new c({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 c({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 c({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(jn,"requireEnvJson");function U(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(U,"callerProfile");function K(e,n){return n===null||e.scope==="global"||e.profile===n}t(K,"inScope");function J(e){if(e.principal.class==="person"&&e.principal.sub!==void 0)throw new s(403,"permission_error","schedules are managed by the install's person, a node or an app");return e.principal.class==="person"?null:P(e.principal)}t(J,"callerApp");async function R(e,n){return await gn(e.principal,n.grantee,n.created_by??void 0)}t(R,"visibleTo");function Tn(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(Tn,"anchorNodeOf");async function An(e,n,r,o,i,d){const u=typeof e.cwd=="string"&&e.cwd.trim()!==""?G(e.cwd):null;if(!cn()){if(u===null)throw new c({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return u}const h=o!==null&&await _(o)!==null?o:void 0,l=t(async q=>await ln(n,q,h,i,void 0,d),"reaches");if(u!==null&&await l(u))return u;const a=an(n).files;if(u!==null&&e.cwd_is_default!==!0)throw new s(400,"invalid_request",`cwd ${u} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${a}`,{cwd:u,default:a},void 0,{param:"cwd"});const p=r===null?null:(await _(r))?.cwd??null;return p!==null&&await l(p)?p:a}t(An,"resolveArmedCwd");async function Pn(e,n,r){const o=e.principal;if(o.class==="person"){if(o.sub!==void 0)throw new s(403,"permission_error","schedules are managed by the install's person, a node or an app");const a=await fn();return{sub:a?.sub??Cn(),grant_id:a?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const a=await un(n);if(a===null)throw new s(401,"grant_revoked",`no active grant for ${n}`);const p=e.ceiling??o.scopes;return{sub:a.sub,grant_id:a.id,scopes:p===null?null:[...p],origin_run_id:null}}if(o.class==="cron"){const a=await bn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!a.allowed)throw new s(a.reason==="scope_missing"?403:401,a.reason==="scope_missing"?"scope_missing":"grant_revoked",a.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?null:[...o.scopes],origin_run_id:o.origin_run,armed_from_exec:o.from_exec}}if(o.class!=="broker")throw new s(401,"unauthorized","this caller cannot arm a cron");const i=r??await _(o.node),d=i?.grant_id??o.grant,u=i?.sub??o.sub,h=d!=null?await dn(d):await pn(n,u);if(h===null)throw new s(401,"grant_revoked","the node grant is not active");const l=i?.scopes??o.scopes;return{sub:h.sub,grant_id:h.id,scopes:l===null?null:[...l],origin_run_id:i?.run_id??o.run}}t(Pn,"armPrincipal");async function En(e){const n=e.body??{},r=E(n,"name"),o=E(n,"command"),i=Y(E(n,"fire_at"),"fire_at"),d=typeof n.recur=="string"&&n.recur!==""?Nn(n.recur):null,u=typeof n.expires_at=="string"&&n.expires_at!==""?Y(n.expires_at,"expires_at"):null,h=typeof n.env_json=="string"&&n.env_json!==""?jn(n.env_json):null,l=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,a=O(n.on_output,"on_output",["silent","on-failure","always","on-change"],l?.startsWith("node:")?"on-change":"on-failure"),p=O(n.overlap,"overlap",["skip","queue","replace"],"skip"),q=O(n.tier,"tier",["normal","urgent","critical"],"normal"),W=a==="always"||a==="on-change";if(W&&l===null)throw new c({error:"sink_required",message:`on_output "${a}" 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(!W&&l!==null)throw new c({error:"sink_unused",message:`on_output "${a}" 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=l!==null?await In(l):"{}";if(l?.startsWith("spawn:")&&(mn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const y=await yn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes,from_exec:e.principal.from_exec},{kind:l.slice(6)});if(!y.allowed)throw vn(y)}const g=P(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 s(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!==g)throw new c({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});H(m,"crtr:schedule"),l?.startsWith("spawn:")&&H(m,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new s(400,"invalid_request","isolated must be a boolean");if(m?.isolated&&n.isolated===!1)throw new s(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const x=m?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(x&&n.isolated===!1)throw new s(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const N=x||n.isolated===!0,f=await Pn(e,g,m),j=nn(),S=N?sn(g,j):null;if(S!==null&&n.cwd_is_default!==!0){const y=en(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?G(n.cwd):S);if(y===".."||y.startsWith(`..${F}`)||y.startsWith(F))throw new s(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const V=S??await An(n,g,b,Tn(n),N,{sub:f.sub,grantId:f.grant_id,scopes:f.scopes}),w=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(w!==null&&await _(w)!==null&&(await _(w)).grantee!==g)throw new c({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(w!==null&&await _(w)===null)throw new c({error:"anchor_not_found",message:`anchor node ${w} 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&&w===null)throw new c({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 c({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 T=typeof n.profile=="string"&&n.profile!==""?(await Sn(e,n.profile)).profileId:null,z=O(n.scope,"scope",["profile","global"],T!==null?"profile":"global");if(z==="profile"&&T===null)throw new c({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:g,sub:f.sub,grant_id:f.grant_id,scopes:f.scopes,origin_run_id:f.origin_run_id,armed_from_exec:f.armed_from_exec===!0,isolated:N,command:o,fire_at:i,recur:d,tz:typeof n.tz=="string"?n.tz:null,expires_at:u,anchor_node:w,cancel_on_wake:B,cwd:V,env_json:h,profile:T,scope:z,run_timeout_s:$,overlap:p,on_output:a,sink:Q,tier:q}),{status:201,body:k(await v(j),null)}}t(En,"handleArm");async function Un(e){J(e);const n=U(e),r=e.query.get("created_by");if(r==="")throw new s(400,"invalid_request","created_by must be nonempty when present");const o=await $n((n===null?await A({all:!0}):[...await A({profile:n}),...await A({global:!0})].sort((i,d)=>i.fire_at<d.fire_at?-1:i.fire_at>d.fire_at?1:0)).filter(i=>r===null||i.created_by===r),async i=>await R(e,i));return{status:200,body:await On(o,async i=>k(i,await D(i)))}}t(Un,"handleList");async function D(e){return(await L(e.cron_id))[0]??null}t(D,"lastRunOf");async function C(e){const n=e.params.cronId,r=await v(n);if(r===null||!await R(e,r))throw new s(404,"cron_not_found",`no cron ${n}`);if(!K(r,U(e)))throw new s(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(C,"requireCron");function I(e,n){const r=J(e);if(r!==null&&n.grantee!==r)throw new s(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(I,"requireOwnCron");async function Jn(e){const n=await C(e),r=await L(n.cron_id);return{status:200,body:{cron:k(n,r[0]??null),runs:r.map(M)}}}t(Jn,"handleShow");async function Rn(e){const n=I(e,await C(e));await Z(n.cron_id,"paused");const r=await v(n.cron_id);return{status:200,body:k(r,await D(r))}}t(Rn,"handlePause");async function Dn(e){const n=I(e,await C(e));await Z(n.cron_id,"active");const r=await v(n.cron_id);return{status:200,body:k(r,await D(r))}}t(Dn,"handleResume");async function Wn(e){const n=I(e,await C(e));if(n.run_state==="running")throw new s(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await _n(n,{outOfBand:!0});if(r===null)throw new s(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:M(r)}}t(Wn,"handleRun");async function xn(e){const n=new Date().toISOString();return{status:200,body:{unparked:await tn(n,J(e)),at:n}}}t(xn,"handlePoke");function Bn(e,n){const r=e.principal;if(r.class==="cron")return r.kind==="exec"?{kind:"canceled",by:r.app}: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=P(r);return{kind:"canceled",by:o===null||o==="app:terminal"?"the person":o}}t(Bn,"cancelNotice");async function zn(e){const n=e.params.cronId,r=await v(n);if(r!==null&&!await R(e,r))throw new s(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!K(r,U(e)))throw new s(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"?e.principal.kind==="cron"&&o===e.principal.execution&&n===e.principal.cron:!0,d=o!==null&&i&&hn(n,o);return r!==null&&await kn(r,d?null:Bn(e,r),{preserveRunningProcess:d}),{status:204}}t(zn,"handleCancel");const de=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:En},{method:"GET",pattern:"/v1/crons",handler:Un},{method:"GET",pattern:"/v1/crons/:cronId",handler:Jn},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:Rn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:Dn},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:xn},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Wn},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:zn}];export{de 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 G,sep as F}from"node:path";import{ApiError as s}from"../../../api/errors.js";import{armCron as rn,parseCronSink as on,getCron as k,listCrons as A,listCronRuns as L,pokeHeldCrons as tn,setCronState as Z}from"../../../core/canvas/crons.js";import{getNode as p}from"../../../core/canvas/canvas.js";import{appSpace as an,cronFolder as sn}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}from"../../../core/grants/mirror.js";import{callerTerminalGrant as fn}from"../../../core/grants/terminal.js";import{assertScope as H}from"../../../core/scopes.js";import{InputError as c}from"../../../core/io.js";import{nextSlotAfter as wn}from"../../../core/wake.js";import{executeCron as _n,markSelfCanceledCronRun as hn}from"../../cron-run.js";import{toCronDTO as v,toCronRunDTO as M}from"../map.js";import{appOf as P,assertCeiling as mn,canReadNode as gn}from"../router.js";import{checkCronBirth as yn,checkCronFire as bn,cronRefusalError as kn}from"../../cron/authority.js";import{endCron as vn}from"../../cron/ended.js";import{requireUsableProfile as Sn}from"./profiles.js";import{filterAsync as $n,mapAsync as On}from"../../../core/async-array.js";import{personId as Cn}from"../../../core/canvas/person.js";function E(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new c({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 O(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new c({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(O,"requireEnum");async function In(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new c({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(await p(n)===null)throw new c({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 c({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 c({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(In,"parseSinkSpec");const qn=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function Y(e,n){if(!qn.test(e)||Number.isNaN(Date.parse(e)))throw new c({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(Y,"requireUtcIso");function Nn(e){try{wn(e,new Date)}catch(n){throw new c({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 jn(e){let n;try{n=JSON.parse(e)}catch{throw new c({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 c({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 c({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(jn,"requireEnvJson");function U(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(U,"callerProfile");function K(e,n){return n===null||e.scope==="global"||e.profile===n}t(K,"inScope");function J(e){if(e.principal.class==="person"&&e.principal.sub!==void 0)throw new s(403,"permission_error","schedules are managed by the install's person, a node or an app");return e.principal.class==="person"?null:P(e.principal)}t(J,"callerApp");async function R(e,n){if(!await gn(e.principal,n.grantee))return!1;const r=e.principal,o=r.class==="broker"?await p(r.node):r.class==="cron"?{run_id:r.origin_run,isolated:r.isolated}:r.class==="forwarded"&&r.actor.kind==="app"?{run_id:r.actor.place.run,isolated:r.actor.place.isolated}:null;return!(o!==null&&(o.isolated||n.isolated)&&o.run_id!==n.origin_run_id)}t(R,"visibleTo");function Tn(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(Tn,"anchorNodeOf");async function An(e,n,r,o,i,d){const u=typeof e.cwd=="string"&&e.cwd.trim()!==""?G(e.cwd):null;if(!cn()){if(u===null)throw new c({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return u}const h=o!==null&&await p(o)!==null?o:void 0,l=t(async q=>await ln(n,q,h,i,void 0,d),"reaches");if(u!==null&&await l(u))return u;const a=an(n).files;if(u!==null&&e.cwd_is_default!==!0)throw new s(400,"invalid_request",`cwd ${u} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${a}`,{cwd:u,default:a},void 0,{param:"cwd"});const f=r===null?null:(await p(r))?.cwd??null;return f!==null&&await l(f)?f:a}t(An,"resolveArmedCwd");async function Pn(e,n,r){const o=e.principal;if(o.class==="person"){if(o.sub!==void 0)throw new s(403,"permission_error","schedules are managed by the install's person, a node or an app");const a=await fn();return{sub:a?.sub??Cn(),grant_id:a?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const a=await un(n);if(a===null)throw new s(401,"grant_revoked",`no active grant for ${n}`);const f=e.ceiling??o.scopes;return{sub:a.sub,grant_id:a.id,scopes:f===null?null:[...f],origin_run_id:null}}if(o.class==="cron"){const a=await bn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!a.allowed)throw new s(a.reason==="scope_missing"?403:401,a.reason==="scope_missing"?"scope_missing":"grant_revoked",a.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?null:[...o.scopes],origin_run_id:o.origin_run,armed_from_exec:o.from_exec}}if(o.class!=="broker")throw new s(401,"unauthorized","this caller cannot arm a cron");const i=r??await p(o.node),d=i?.grant_id??o.grant,u=i?.sub??o.sub,h=d!=null?await dn(d):await pn(n,u);if(h===null)throw new s(401,"grant_revoked","the node grant is not active");const l=i?.scopes??o.scopes;return{sub:h.sub,grant_id:h.id,scopes:l===null?null:[...l],origin_run_id:i?.run_id??o.run}}t(Pn,"armPrincipal");async function En(e){const n=e.body??{},r=E(n,"name"),o=E(n,"command"),i=Y(E(n,"fire_at"),"fire_at"),d=typeof n.recur=="string"&&n.recur!==""?Nn(n.recur):null,u=typeof n.expires_at=="string"&&n.expires_at!==""?Y(n.expires_at,"expires_at"):null,h=typeof n.env_json=="string"&&n.env_json!==""?jn(n.env_json):null,l=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,a=O(n.on_output,"on_output",["silent","on-failure","always","on-change"],l?.startsWith("node:")?"on-change":"on-failure"),f=O(n.overlap,"overlap",["skip","queue","replace"],"skip"),q=O(n.tier,"tier",["normal","urgent","critical"],"normal"),W=a==="always"||a==="on-change";if(W&&l===null)throw new c({error:"sink_required",message:`on_output "${a}" 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(!W&&l!==null)throw new c({error:"sink_unused",message:`on_output "${a}" 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=l!==null?await In(l):"{}";if(l?.startsWith("spawn:")&&(mn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const y=await yn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes,from_exec:e.principal.from_exec},{kind:l.slice(6)});if(!y.allowed)throw kn(y)}const g=P(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 s(403,"permission_error","a broker may only arm a cron as its own node");const m=b===null?null:await p(b);if(m!==null){if(m.grantee!==g)throw new c({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});H(m,"crtr:schedule"),l?.startsWith("spawn:")&&H(m,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new s(400,"invalid_request","isolated must be a boolean");if(m?.isolated&&n.isolated===!1)throw new s(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const x=m?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(x&&n.isolated===!1)throw new s(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const N=x||n.isolated===!0,w=await Pn(e,g,m),j=nn(),S=N?sn(g,j):null;if(S!==null&&n.cwd_is_default!==!0){const y=en(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?G(n.cwd):S);if(y===".."||y.startsWith(`..${F}`)||y.startsWith(F))throw new s(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const V=S??await An(n,g,b,Tn(n),N,{sub:w.sub,grantId:w.grant_id,scopes:w.scopes}),_=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(_!==null&&await p(_)!==null&&(await p(_)).grantee!==g)throw new c({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(_!==null&&await p(_)===null)throw new c({error:"anchor_not_found",message:`anchor node ${_} 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&&_===null)throw new c({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 c({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 T=typeof n.profile=="string"&&n.profile!==""?(await Sn(e,n.profile)).profileId:null,z=O(n.scope,"scope",["profile","global"],T!==null?"profile":"global");if(z==="profile"&&T===null)throw new c({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:g,sub:w.sub,grant_id:w.grant_id,scopes:w.scopes,origin_run_id:w.origin_run_id,armed_from_exec:w.armed_from_exec===!0,isolated:N,command:o,fire_at:i,recur:d,tz:typeof n.tz=="string"?n.tz:null,expires_at:u,anchor_node:_,cancel_on_wake:B,cwd:V,env_json:h,profile:T,scope:z,run_timeout_s:$,overlap:f,on_output:a,sink:Q,tier:q}),{status:201,body:v(await k(j),null)}}t(En,"handleArm");async function Un(e){J(e);const n=U(e),r=e.query.get("created_by");if(r==="")throw new s(400,"invalid_request","created_by must be nonempty when present");const o=await $n((n===null?await A({all:!0}):[...await A({profile:n}),...await A({global:!0})].sort((i,d)=>i.fire_at<d.fire_at?-1:i.fire_at>d.fire_at?1:0)).filter(i=>r===null||i.created_by===r),async i=>await R(e,i));return{status:200,body:await On(o,async i=>v(i,await D(i)))}}t(Un,"handleList");async function D(e){return(await L(e.cron_id))[0]??null}t(D,"lastRunOf");async function C(e){const n=e.params.cronId,r=await k(n);if(r===null||!await R(e,r))throw new s(404,"cron_not_found",`no cron ${n}`);if(!K(r,U(e)))throw new s(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(C,"requireCron");function I(e,n){const r=J(e);if(r!==null&&n.grantee!==r)throw new s(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(I,"requireOwnCron");async function Jn(e){const n=await C(e),r=await L(n.cron_id);return{status:200,body:{cron:v(n,r[0]??null),runs:r.map(M)}}}t(Jn,"handleShow");async function Rn(e){const n=I(e,await C(e));await Z(n.cron_id,"paused");const r=await k(n.cron_id);return{status:200,body:v(r,await D(r))}}t(Rn,"handlePause");async function Dn(e){const n=I(e,await C(e));await Z(n.cron_id,"active");const r=await k(n.cron_id);return{status:200,body:v(r,await D(r))}}t(Dn,"handleResume");async function Wn(e){const n=I(e,await C(e));if(n.run_state==="running")throw new s(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await _n(n,{outOfBand:!0});if(r===null)throw new s(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:M(r)}}t(Wn,"handleRun");async function xn(e){const n=new Date().toISOString();return{status:200,body:{unparked:await tn(n,J(e)),at:n}}}t(xn,"handlePoke");function Bn(e,n){const r=e.principal;if(r.class==="cron")return r.kind==="exec"?{kind:"canceled",by:r.app}: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=P(r);return{kind:"canceled",by:o===null||o==="app:terminal"?"the person":o}}t(Bn,"cancelNotice");async function zn(e){const n=e.params.cronId,r=await k(n);if(r!==null&&!await R(e,r))throw new s(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!K(r,U(e)))throw new s(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"?e.principal.kind==="cron"&&o===e.principal.execution&&n===e.principal.cron:!0,d=o!==null&&i&&hn(n,o);return r!==null&&await vn(r,d?null:Bn(e,r),{preserveRunningProcess:d}),{status:204}}t(zn,"handleCancel");const de=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:En},{method:"GET",pattern:"/v1/crons",handler:Un},{method:"GET",pattern:"/v1/crons/:cronId",handler:Jn},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:Rn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:Dn},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:xn},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Wn},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:zn}];export{de as cronRoutes};
@@ -1 +1 @@
1
- var ie=Object.defineProperty;var u=(n,e)=>ie(n,"name",{value:e,configurable:!0});import{ApiError as k}from"../../../api/errors.js";import{DOC_KINDS as x,DOC_UNSET_FIELDS as U}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as V}from"../../../api/dto/objects.js";import{withCanvasWrite as A}from"../../../core/canvas/db.js";import{CrtrError as re}from"../../../core/errors.js";import{emitEvent as ae}from"../../../core/events/emit.js";import{canChange as se,spaceOf as ce}from"../../../core/graph/access.js";import{get as j}from"../../../core/graph/bodies.js";import{deleteDocument as de,documentRecord as L,editDocument as ue,heads as W,history as fe,linksIn as K,moveDocument as pe,readBase as le,setDocumentRecord as me,withCurrentNames as we,writeDocument as he}from"../../../core/graph/documents.js";import{resolveName as G}from"../../../core/graph/names.js";import{getById as M}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as ye}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as ge}from"../../../core/graph/repo-sync/repos.js";import{lintRules as ve,rulesOf as _e}from"../../../core/graph/rules.js";import{unifiedDiff as be}from"../../../core/graph/diff.js";import{ownerForGrantee as ke}from"../../../core/graph/objects.js";import{ownerFromFlag as q,readerFor as E,resolveRef as X,warmReaderRepos as O}from"../reader.js";import{appCaller as z,asApiError as Ae,requireOwnNamespace as Ee}from"./schemas.js";import{filterAsync as Oe,flatMapAsync as $e}from"../../../core/async-array.js";function g(n,e,t,o,a={}){return new k(n,e,t,{...a,error:e,message:t,next:o})}u(g,"refuse");const b=u((n,e)=>new k(400,"invalid_request",n,e),"invalid");function C(n){const e=n.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw b("the request body must be a JSON object");return e}u(C,"bodyOf");const N=u(async n=>async e=>await ge(n,e),"reposFnFor");function S(n,e){if(n instanceof re){const t=n.details;if(n.code==="read_first")throw g(409,"read_first",n.message,`Read it first with \`crtr canvas read ${e?.id??"<ref>"}\`, then rerun the edit.`,t??{});if(n.code==="conflict")throw new k(409,"name_taken",n.message,t);if(n.code==="forbidden")throw typeof t?.next=="string"?g(403,"forbidden",n.message,t.next,t):new k(403,"forbidden",n.message,t);if(n.code==="scope_missing")throw g(403,"scope_missing",n.message,`Launch the run with \`${String(t?.scope)}\` in its scopes list.`,t??{});if(t?.rule==="repo-public-only")throw g(422,"repo_documents_are_public",n.message,'Own it as `user` and add a rule with `if: {"repo": "<handle>"}` so it loads only in that repo.')}throw Ae(n)}u(S,"mapGraphError");async function B(n,e,t){const o=await X(n,e,decodeURIComponent(t));if("remote"in o)throw g(404,"not_found",Y(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new k(404,"not_found",`${o.deleted.name} is deleted`,{id:o.deleted.id,deleted:!0});if(o.type!=="document")throw b(`${t} is a ${o.type}, not a document`);return o}u(B,"refDocument");function Y(n){return`no object named ${n} on this runtime; it may be on another runtime, or the name may be misspelled`}u(Y,"remoteMessage");async function je(n,e){const t=e.owner===null?null:await M(n,e.owner);return t?t.type==="node"?t.id:t.name:""}u(je,"ownerHandle");async function R(n,e){const t=await je(n,e);return t?`${t}/${e.name}`:e.name}u(R,"canonicalName");async function Re(n,e){const t=e.owner===null?null:await M(n,e.owner),o=t?{id:t.id,type:t.type,handle:t.type==="node"?t.id:t.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:o,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at,...e.type==="document"?{schema_type:e.schema_type??null}:{}}}u(Re,"objectDTO");async function De(n,e){const t=e.owner===null?null:await M(n,e.owner);return t?.type==="repo"?t:null}u(De,"repoOwner");async function T(n,e,t,o){const a=[...new Map(await $e(t,async f=>{const i=await De(n,f);return i?[[i.id,i]]:[]})).values()],c=e.class==="node"?e.cwd:o,s=e.class==="node"?[...e.checkouts,...e.projectCheckouts].map(f=>f.root):[];return{repos:a,dirs:[...new Set([...c!==void 0?[c]:[],...s])]}}u(T,"repoChange");async function F(n){for(const e of n.repos)try{await ye(e,n.dirs)}catch(t){ae({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(F,"syncChanged");const Z=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function Ce(n,e){if(n===void 0)return;if(!Array.isArray(n)){e.push("`rules` must be a list");return}const t=n.map(a=>{if(typeof a!="object"||a===null||Array.isArray(a))return a;const{match_frontmatter:c,...s}=a;return"matchFrontmatter"in s?{...s,match_frontmatter:s.matchFrontmatter}:c===void 0?s:{...s,matchFrontmatter:c}}),o=ve(t);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return t}u(Ce,"rulesFromDTO");function J(n,e){const t={},o=u(i=>{const d=n[i];if(d!==void 0){if(typeof d!="string"){e.push(`\`${i}\` must be a string`);return}return d}},"str"),a=o("preview");a!==void 0&&(a.trim()===""||/[\r\n]/.test(a)?e.push("`preview` must be one non-empty line"):t.preview=a);const c=o("summary");c!==void 0&&(t.summary=c);const s=o("why_it_exists");if(s!==void 0&&(t.whyItExists=s),n.privacy!==void 0&&(V.includes(n.privacy)?t.privacy=n.privacy:e.push(`\`privacy\` must be one of ${V.join(", ")}`)),n.unlisted!==void 0&&(typeof n.unlisted!="boolean"?e.push("`unlisted` must be a boolean"):t.unlisted=n.unlisted),n.lint_ignore!==void 0){const i=n.lint_ignore;!Array.isArray(i)||i.some(d=>typeof d!="string")?e.push("`lint_ignore` must be a list of strings"):t.lintIgnore=i}if(n.extensions!==void 0){const i=n.extensions;typeof i!="object"||i===null||Array.isArray(i)||Object.values(i).some(d=>typeof d!="object"||d===null||Array.isArray(d))?e.push("`extensions` must map each plugin to an object of fields"):t.extensions=i}const f=Ce(n.rules,e);return f!==void 0&&(t.rules=f),t}u(J,"fieldsFrom");function Q(n){if(n.length>0)throw b(n.join("; "),{errors:n})}u(Q,"failOn");async function ee(n,e,t,o){const a=await Oe(K(t),async c=>"remote"in await G(n,e,c));return{...o.length>0?{unresolved_links:o}:{},...a.length>0?{remote_links:a}:{}}}u(ee,"linkReport");async function Ne(n){const e=C(n),t=[],o=e.name;(typeof o!="string"||!Z.test(o))&&t.push("`name` must be segments of letters, digits, `_` or `-` joined by `/`"),typeof e.body!="string"&&t.push("`body` must be a string"),typeof e.preview!="string"&&t.push("`preview` is required");const a=e.kind??"knowledge";x.includes(a)||t.push(a==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${x.join(", ")}`);const c=J(e,t);e.cwd!==void 0&&typeof e.cwd!="string"&&t.push("`cwd` must be a string");const s=e.schema_type;s!==void 0&&typeof s!="string"&&t.push("`schema_type` must be a string");const f=e.relations;f!==void 0&&(!Array.isArray(f)||f.some(r=>typeof r!="object"||r===null||typeof r.relation!="string"||typeof r.subject!="string"))&&t.push("`relations` must be a list of {relation, subject} strings"),Q(t);const i=e;await O(n.principal,i.cwd);const{dto:d,change:h}=await A(async r=>{const p=await E(r,n);i.schema_type!==void 0?await Ee(r,z(n).app,i.schema_type):i.relations!==void 0&&z(n);const l=i.owner==="user"&&p.class==="app"&&p.sub!==void 0?await ke(r,p.sub.startsWith("user:")?p.sub:`user:${p.sub}`):await q(r,p,i.owner,i.cwd);try{const{object:w,unresolved:_}=await he(r,p,{...c,owner:l,name:i.name,kind:a,preview:c.preview,privacy:c.privacy??"public",...i.schema_type!==void 0?{schemaType:i.schema_type}:{},...i.relations!==void 0?{relations:i.relations}:{}},i.body,await N(r));return{dto:{id:w.id,name:await R(r,w),...await ee(r,p,i.body,_)},change:await T(r,p,[w],i.cwd)}}catch(w){S(w)}});return await F(h),d}u(Ne,"write");function Se(n,e){let t=n;e.forEach(({old:o,new:a},c)=>{const s=o===""?-1:t.indexOf(o);if(s<0)throw g(422,"replace_no_match",`replace[${c}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:c});if(t.indexOf(o,s+1)>=0)throw g(422,"replace_ambiguous",`replace[${c}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:c});t=t.slice(0,s)+a+t.slice(s+o.length)})}u(Se,"checkReplacements");async function Te(n){const e=C(n),t=[],o=e.body!==void 0,a=e.replace!==void 0;o&&a&&t.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&t.push("`body` must be a string");const c=e.replace;a&&(!Array.isArray(c)||c.length===0||c.some(r=>typeof r!="object"||r===null||typeof r.old!="string"||typeof r.new!="string"))&&t.push("`replace` must be a non-empty list of {old, new} strings"),(typeof e.rationale!="string"||e.rationale.trim()==="")&&t.push("`rationale` is required"),e.verbatim!==void 0&&typeof e.verbatim!="boolean"&&t.push("`verbatim` must be a boolean"),e.base!==void 0&&(typeof e.base!="string"||e.base==="")&&t.push("`base` must be a non-empty heads key"),e.base!==void 0&&!o&&!a&&t.push("`base` applies only to a `body` or `replace` edit");const s=J(e,t),f=e.unset??[];if(!Array.isArray(f)||f.some(r=>!U.includes(r)))t.push(`\`unset\` takes only ${U.join(", ")}`);else for(const r of f)r==="summary"&&(s.summary=null),r==="why_it_exists"&&(s.whyItExists=null),r==="unlisted"&&(s.unlisted=!1),r==="lint_ignore"&&(s.lintIgnore=null),r==="extensions"&&(s.extensions=null),r==="rules"&&(s.rules=[]);t.length===0&&!o&&!a&&Object.keys(s).length===0&&t.push("nothing to change: give `body`, `replace`, or a field"),Q(t);const i=e;await O(n.principal);const{dto:d,synced:h}=await A(async r=>{const p=await E(r,n),l=await B(r,p,n.params.ref),w=await R(r,l),_=await T(r,p,[l]);try{if(!o&&!a)return await me(r,p,l,s,i.rationale),{dto:{id:l.id,name:w,revision:(await L(r,l)).revision},synced:_};const v=i.base??await le(r,p,l);if(a&&v!==null){const P=await L(r,l),H=await W(r,l);P.source_path===null&&P.heads_key===v&&H.length===1&&Se(await we(r,l,j(H[0].body_hash)),i.replace)}const D=i.verbatim===!0,m=o?{body:i.body,verbatim:D}:{replace:i.replace,verbatim:D},y=await ue(r,p,l,m,i.rationale,v,s,await N(r));if("stale"in y)throw g(409,"stale_revision",`${w} changed after your read; it is now at revision ${y.stale.revision}`,`Read it again with \`crtr canvas read ${w}\`, then redo your change on the current text.`,{stale:y.stale});const I=j((await W(r,l))[0].body_hash);return{dto:{id:l.id,name:w,revision:y.revision,...await ee(r,p,I,y.unresolved)},synced:_}}catch(v){S(v,l)}});return await F(h),d}u(Te,"edit");async function Fe(n){const e=C(n);if(e.new_name===void 0&&e.owner===void 0)throw b("give a new name, an owner, or both");if(e.new_name!==void 0&&(typeof e.new_name!="string"||!Z.test(e.new_name)))throw b("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");await O(n.principal,e.cwd);const{dto:t,change:o}=await A(async a=>{const c=await E(a,n),s=await B(a,c,n.params.ref),f=e.owner===void 0?void 0:await q(a,c,e.owner,e.cwd);try{const i=await pe(a,c,s,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}},await N(a));return{dto:{id:i.id,name:await R(a,i)},change:await T(a,c,[s,i],e.cwd)}}catch(i){S(i,s)}});return await F(o),t}u(Fe,"move");async function Ie(n){await O(n.principal);const{dto:e,change:t}=await A(async o=>{const a=await E(o,n),c=await B(o,a,n.params.ref),s=await R(o,c),f=await T(o,a,[c]);try{await de(o,a,c,await N(o))}catch(i){S(i,c)}return{dto:{id:c.id,name:s},change:f}});return await F(t),e}u(Ie,"remove");async function Le(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 O(n.principal),await A(async o=>{const a=await E(o,n),c=await X(o,a,decodeURIComponent(n.params.ref));if("remote"in c)throw g(404,"not_found",Y(c.remote),"Check the name with `crtr canvas search`.");const s="deleted"in c?c.deleted:c;if(s.type!=="document")throw b(`${s.name} is a ${s.type}, not a document`);const f=await fe(o,a,s),i={object:await Re(o,s),revisions:f.map(d=>({rev_id:d.rev_id,number:d.number,author_id:d.author,rationale:d.rationale,verbatim:d.verbatim,created_at:d.created_at,parents:d.parents,acting_app:d.acting_app,acting_for:d.acting_for}))};if(t!==void 0){const d=f.find(r=>r.number===t);if(!d)throw new k(404,"not_found",`${s.name} has no revision ${t}`);const h=d.parents[0]===void 0?void 0:f.find(r=>r.rev_id===d.parents[0]);i.diff=be(h?j(h.body_hash):"",j(d.body_hash))}return i})}u(Le,"revisionHistory");const te=1e3,ne=1e3,oe=3e3,We=/^When\b.+\bbecause\b.+/,Me=u(n=>{const e=n.trim();return e===""?0:e.split(/\s+/).length},"words");function qe(n,e){if(n.some(o=>o.on==="boot"&&o.deliver==="content")&&e>te)return`body is ${e} words, over the ${te}-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>ne?`body is ${e} words, over the ${ne}-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>oe?`body is ${e} words, over the ${oe}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(qe,"lengthFinding");async function Be(n){const e=C(n);return await O(n.principal,e.cwd),await A(async t=>{const o=await E(t,n),a=e.owner===void 0?null:await q(t,o,e.owner,e.cwd),c=await t.prepare("SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND home_machine IS NULL AND kind != 'report' ORDER BY name").all(),s=[],f=new Map;for(const i of c){if(a&&i.owner!==a.id||!await se(t,o,i))continue;const d=await L(t,i),h=new Set(d.lint_ignore??[]),r=await R(t,i),p=u((m,y,I)=>{h.has(m)||s.push({name:r,severity:y,rule:m,message:I})},"add"),l=await _e(t,i),w=(await W(t,i))[0],_=w?j(w.body_hash):"",v=qe(l,Me(_));v&&p("length","error",v),l.some(m=>m.deliver==="preview")&&!We.test(i.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(i.id)).map(m=>m.written_as));for(const m of K(_)){if(D.has(m))continue;const y=await G(t,o,m);"remote"in y||"object"in y||"deleted"in y||p("links","warning",`[[${m}]] names no object in your view`)}if(l.some(m=>m.on==="workspace-open"&&m.deliver==="content")){const m=await ce(t,i);m.type==="repo"&&!h.has("workspace-open")&&f.set(m.name,[...f.get(m.name)??[],r])}}for(const[i,d]of f)if(!(d.length<2))for(const h of d)s.push({name:h,severity:"warning",rule:"workspace-open",message:`repo ${i} has ${d.length} documents delivered at content on workspace-open (${d.join(", ")}); keep one and link the rest from it`});return{findings:s}})}u(Be,"lint");const $=u((n,e=200)=>async t=>({status:e,body:await n(t)}),"ok"),st=[{method:"POST",pattern:"/v1/docs",handler:$(Ne,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:$(Te)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:$(Fe)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:$(Ie)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:$(Le)},{method:"POST",pattern:"/v1/docs/lint",handler:$(Be)}];export{R as canonicalName,st as docRoutes,T as repoChange,F as syncChanged};
1
+ var re=Object.defineProperty;var u=(n,e)=>re(n,"name",{value:e,configurable:!0});import{ApiError as k}from"../../../api/errors.js";import{DOC_KINDS as U,DOC_UNSET_FIELDS as V}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as K}from"../../../api/dto/objects.js";import{openDb as ae,withCanvasWrite as A}from"../../../core/canvas/db.js";import{CrtrError as se}from"../../../core/errors.js";import{emitEvent as ce}from"../../../core/events/emit.js";import{canChange as de,spaceOf as ue}from"../../../core/graph/access.js";import{get as j}from"../../../core/graph/bodies.js";import{deleteDocument as fe,documentRecord as L,editDocument as me,heads as W,history as pe,linksIn as G,moveDocument as le,readBase as we,setDocumentRecord as he,withCurrentNames as ye,writeDocument as ge}from"../../../core/graph/documents.js";import{resolveName as X}from"../../../core/graph/names.js";import{getById as M}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as ve}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as _e}from"../../../core/graph/repo-sync/repos.js";import{lintRules as be,rulesOf as ke}from"../../../core/graph/rules.js";import{unifiedDiff as Ae}from"../../../core/graph/diff.js";import{ownerForGrantee as Ee}from"../../../core/graph/objects.js";import{stubOf as $e}from"../../../core/peer/stubs.js";import{forwarding as Oe}from"../../peer/forward.js";import{resolvedLocally as je}from"./objects.js";import{ownerFromFlag as q,readerFor as E,resolveRef as x,warmReaderRepos as $}from"../reader.js";import{appCaller as z,asApiError as Re,requireOwnNamespace as De}from"./schemas.js";import{filterAsync as Ce,flatMapAsync as Ne}from"../../../core/async-array.js";function g(n,e,t,o,a={}){return new k(n,e,t,{...a,error:e,message:t,next:o})}u(g,"refuse");const b=u((n,e)=>new k(400,"invalid_request",n,e),"invalid");function C(n){const e=n.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw b("the request body must be a JSON object");return e}u(C,"bodyOf");const N=u(async n=>async e=>await _e(n,e),"reposFnFor");function S(n,e){if(n instanceof se){const t=n.details;if(n.code==="read_first")throw g(409,"read_first",n.message,`Read it first with \`crtr canvas read ${e?.id??"<ref>"}\`, then rerun the edit.`,t??{});if(n.code==="conflict")throw new k(409,"name_taken",n.message,t);if(n.code==="forbidden")throw typeof t?.next=="string"?g(403,"forbidden",n.message,t.next,t):new k(403,"forbidden",n.message,t);if(n.code==="scope_missing")throw g(403,"scope_missing",n.message,`Launch the run with \`${String(t?.scope)}\` in its scopes list.`,t??{});if(t?.rule==="repo-public-only")throw g(422,"repo_documents_are_public",n.message,'Own it as `user` and add a rule with `if: {"repo": "<handle>"}` so it loads only in that repo.')}throw Re(n)}u(S,"mapGraphError");async function B(n,e,t){const o=await x(n,e,decodeURIComponent(t));if("remote"in o)throw g(404,"not_found",Z(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new k(404,"not_found",`${o.deleted.name} is deleted`,{id:o.deleted.id,deleted:!0});if(o.type!=="document")throw b(`${t} is a ${o.type}, not a document`);return o}u(B,"refDocument");async function Y(n){const{stub:e}=await je(decodeURIComponent(n.params.ref),n);if(e===null)return;const t=await $e(await ae(),e);if(t.object.type!=="document")return;const o=await Oe.current.label(t.home_machine);throw g(404,"not_here",`this document lives on runtime ${o.name}; edit it (or read its history) from a node there`,`Run the command from a node on ${o.name}.`,{id:t.object.id,home_machine:o.id})}u(Y,"refuseRemoteDocument");function Z(n){return`no object named ${n} on this runtime; it may be on another runtime, or the name may be misspelled`}u(Z,"remoteMessage");async function Se(n,e){const t=e.owner===null?null:await M(n,e.owner);return t?t.type==="node"?t.id:t.name:""}u(Se,"ownerHandle");async function R(n,e){const t=await Se(n,e);return t?`${t}/${e.name}`:e.name}u(R,"canonicalName");async function Te(n,e){const t=e.owner===null?null:await M(n,e.owner),o=t?{id:t.id,type:t.type,handle:t.type==="node"?t.id:t.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:o,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at,...e.type==="document"?{schema_type:e.schema_type??null}:{}}}u(Te,"objectDTO");async function Ie(n,e){const t=e.owner===null?null:await M(n,e.owner);return t?.type==="repo"?t:null}u(Ie,"repoOwner");async function T(n,e,t,o){const a=[...new Map(await Ne(t,async f=>{const i=await Ie(n,f);return i?[[i.id,i]]:[]})).values()],c=e.class==="node"?e.cwd:o,s=e.class==="node"?[...e.checkouts,...e.projectCheckouts].map(f=>f.root):[];return{repos:a,dirs:[...new Set([...c!==void 0?[c]:[],...s])]}}u(T,"repoChange");async function I(n){for(const e of n.repos)try{await ve(e,n.dirs)}catch(t){ce({level:"warn",event:"repo_sync.failed",outcome:"failed",error:t,fields:{repo:e.name,step:"export",reason:t instanceof Error?t.message:String(t)}})}}u(I,"syncChanged");const J=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function Fe(n,e){if(n===void 0)return;if(!Array.isArray(n)){e.push("`rules` must be a list");return}const t=n.map(a=>{if(typeof a!="object"||a===null||Array.isArray(a))return a;const{match_frontmatter:c,...s}=a;return"matchFrontmatter"in s?{...s,match_frontmatter:s.matchFrontmatter}:c===void 0?s:{...s,matchFrontmatter:c}}),o=be(t);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return t}u(Fe,"rulesFromDTO");function Q(n,e){const t={},o=u(i=>{const d=n[i];if(d!==void 0){if(typeof d!="string"){e.push(`\`${i}\` must be a string`);return}return d}},"str"),a=o("preview");a!==void 0&&(a.trim()===""||/[\r\n]/.test(a)?e.push("`preview` must be one non-empty line"):t.preview=a);const c=o("summary");c!==void 0&&(t.summary=c);const s=o("why_it_exists");if(s!==void 0&&(t.whyItExists=s),n.privacy!==void 0&&(K.includes(n.privacy)?t.privacy=n.privacy:e.push(`\`privacy\` must be one of ${K.join(", ")}`)),n.unlisted!==void 0&&(typeof n.unlisted!="boolean"?e.push("`unlisted` must be a boolean"):t.unlisted=n.unlisted),n.lint_ignore!==void 0){const i=n.lint_ignore;!Array.isArray(i)||i.some(d=>typeof d!="string")?e.push("`lint_ignore` must be a list of strings"):t.lintIgnore=i}if(n.extensions!==void 0){const i=n.extensions;typeof i!="object"||i===null||Array.isArray(i)||Object.values(i).some(d=>typeof d!="object"||d===null||Array.isArray(d))?e.push("`extensions` must map each plugin to an object of fields"):t.extensions=i}const f=Fe(n.rules,e);return f!==void 0&&(t.rules=f),t}u(Q,"fieldsFrom");function ee(n){if(n.length>0)throw b(n.join("; "),{errors:n})}u(ee,"failOn");async function te(n,e,t,o){const a=await Ce(G(t),async c=>"remote"in await X(n,e,c));return{...o.length>0?{unresolved_links:o}:{},...a.length>0?{remote_links:a}:{}}}u(te,"linkReport");async function Le(n){const e=C(n),t=[],o=e.name;(typeof o!="string"||!J.test(o))&&t.push("`name` must be segments of letters, digits, `_` or `-` joined by `/`"),typeof e.body!="string"&&t.push("`body` must be a string"),typeof e.preview!="string"&&t.push("`preview` is required");const a=e.kind??"knowledge";U.includes(a)||t.push(a==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${U.join(", ")}`);const c=Q(e,t);e.cwd!==void 0&&typeof e.cwd!="string"&&t.push("`cwd` must be a string");const s=e.schema_type;s!==void 0&&typeof s!="string"&&t.push("`schema_type` must be a string");const f=e.relations;f!==void 0&&(!Array.isArray(f)||f.some(r=>typeof r!="object"||r===null||typeof r.relation!="string"||typeof r.subject!="string"))&&t.push("`relations` must be a list of {relation, subject} strings"),ee(t);const i=e;await $(n.principal,i.cwd);const{dto:d,change:h}=await A(async r=>{const m=await E(r,n);i.schema_type!==void 0?await De(r,z(n).app,i.schema_type):i.relations!==void 0&&z(n);const p=i.owner==="user"&&m.class==="app"&&m.sub!==void 0?await Ee(r,m.sub.startsWith("user:")?m.sub:`user:${m.sub}`):await q(r,m,i.owner,i.cwd);try{const{object:w,unresolved:_}=await ge(r,m,{...c,owner:p,name:i.name,kind:a,preview:c.preview,privacy:c.privacy??"public",...i.schema_type!==void 0?{schemaType:i.schema_type}:{},...i.relations!==void 0?{relations:i.relations}:{}},i.body,await N(r));return{dto:{id:w.id,name:await R(r,w),...await te(r,m,i.body,_)},change:await T(r,m,[w],i.cwd)}}catch(w){S(w)}});return await I(h),d}u(Le,"write");function We(n,e){let t=n;e.forEach(({old:o,new:a},c)=>{const s=o===""?-1:t.indexOf(o);if(s<0)throw g(422,"replace_no_match",`replace[${c}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:c});if(t.indexOf(o,s+1)>=0)throw g(422,"replace_ambiguous",`replace[${c}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:c});t=t.slice(0,s)+a+t.slice(s+o.length)})}u(We,"checkReplacements");async function Me(n){const e=C(n),t=[],o=e.body!==void 0,a=e.replace!==void 0;o&&a&&t.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&t.push("`body` must be a string");const c=e.replace;a&&(!Array.isArray(c)||c.length===0||c.some(r=>typeof r!="object"||r===null||typeof r.old!="string"||typeof r.new!="string"))&&t.push("`replace` must be a non-empty list of {old, new} strings"),(typeof e.rationale!="string"||e.rationale.trim()==="")&&t.push("`rationale` is required"),e.verbatim!==void 0&&typeof e.verbatim!="boolean"&&t.push("`verbatim` must be a boolean"),e.base!==void 0&&(typeof e.base!="string"||e.base==="")&&t.push("`base` must be a non-empty heads key"),e.base!==void 0&&!o&&!a&&t.push("`base` applies only to a `body` or `replace` edit");const s=Q(e,t),f=e.unset??[];if(!Array.isArray(f)||f.some(r=>!V.includes(r)))t.push(`\`unset\` takes only ${V.join(", ")}`);else for(const r of f)r==="summary"&&(s.summary=null),r==="why_it_exists"&&(s.whyItExists=null),r==="unlisted"&&(s.unlisted=!1),r==="lint_ignore"&&(s.lintIgnore=null),r==="extensions"&&(s.extensions=null),r==="rules"&&(s.rules=[]);t.length===0&&!o&&!a&&Object.keys(s).length===0&&t.push("nothing to change: give `body`, `replace`, or a field"),ee(t);const i=e;await Y(n),await $(n.principal);const{dto:d,synced:h}=await A(async r=>{const m=await E(r,n),p=await B(r,m,n.params.ref),w=await R(r,p),_=await T(r,m,[p]);try{if(!o&&!a)return await he(r,m,p,s,i.rationale),{dto:{id:p.id,name:w,revision:(await L(r,p)).revision},synced:_};const v=i.base??await we(r,m,p);if(a&&v!==null){const P=await L(r,p),H=await W(r,p);P.source_path===null&&P.heads_key===v&&H.length===1&&We(await ye(r,p,j(H[0].body_hash)),i.replace)}const D=i.verbatim===!0,l=o?{body:i.body,verbatim:D}:{replace:i.replace,verbatim:D},y=await me(r,m,p,l,i.rationale,v,s,await N(r));if("stale"in y)throw g(409,"stale_revision",`${w} changed after your read; it is now at revision ${y.stale.revision}`,`Read it again with \`crtr canvas read ${w}\`, then redo your change on the current text.`,{stale:y.stale});const F=j((await W(r,p))[0].body_hash);return{dto:{id:p.id,name:w,revision:y.revision,...await te(r,m,F,y.unresolved)},synced:_}}catch(v){S(v,p)}});return await I(h),d}u(Me,"edit");async function qe(n){const e=C(n);if(e.new_name===void 0&&e.owner===void 0)throw b("give a new name, an owner, or both");if(e.new_name!==void 0&&(typeof e.new_name!="string"||!J.test(e.new_name)))throw b("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");await $(n.principal,e.cwd);const{dto:t,change:o}=await A(async a=>{const c=await E(a,n),s=await B(a,c,n.params.ref),f=e.owner===void 0?void 0:await q(a,c,e.owner,e.cwd);try{const i=await le(a,c,s,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}},await N(a));return{dto:{id:i.id,name:await R(a,i)},change:await T(a,c,[s,i],e.cwd)}}catch(i){S(i,s)}});return await I(o),t}u(qe,"move");async function Be(n){await $(n.principal);const{dto:e,change:t}=await A(async o=>{const a=await E(o,n),c=await B(o,a,n.params.ref),s=await R(o,c),f=await T(o,a,[c]);try{await fe(o,a,c,await N(o))}catch(i){S(i,c)}return{dto:{id:c.id,name:s},change:f}});return await I(t),e}u(Be,"remove");async function Pe(n){const e=n.query.get("diff"),t=e===null?void 0:Number(e);if(t!==void 0&&!(Number.isInteger(t)&&t>0))throw b("`diff` must be a revision number");return await Y(n),await $(n.principal),await A(async o=>{const a=await E(o,n),c=await x(o,a,decodeURIComponent(n.params.ref));if("remote"in c)throw g(404,"not_found",Z(c.remote),"Check the name with `crtr canvas search`.");const s="deleted"in c?c.deleted:c;if(s.type!=="document")throw b(`${s.name} is a ${s.type}, not a document`);const f=await pe(o,a,s),i={object:await Te(o,s),revisions:f.map(d=>({rev_id:d.rev_id,number:d.number,author_id:d.author,rationale:d.rationale,verbatim:d.verbatim,created_at:d.created_at,parents:d.parents,acting_app:d.acting_app,acting_for:d.acting_for}))};if(t!==void 0){const d=f.find(r=>r.number===t);if(!d)throw new k(404,"not_found",`${s.name} has no revision ${t}`);const h=d.parents[0]===void 0?void 0:f.find(r=>r.rev_id===d.parents[0]);i.diff=Ae(h?j(h.body_hash):"",j(d.body_hash))}return i})}u(Pe,"revisionHistory");const ne=1e3,oe=1e3,ie=3e3,He=/^When\b.+\bbecause\b.+/,Ue=u(n=>{const e=n.trim();return e===""?0:e.split(/\s+/).length},"words");function Ve(n,e){if(n.some(o=>o.on==="boot"&&o.deliver==="content")&&e>ne)return`body is ${e} words, over the ${ne}-word cap for a boot rule at content (the whole body enters every node's context). Keep the core here and move separate subjects into [[linked]] documents.`;const t=[...new Set(n.filter(o=>o.on!=="boot"&&o.deliver==="content").map(o=>o.on))];return t.length>0&&e>oe?`body is ${e} words, over the ${oe}-word cap for a content rule on ${t.join("/")}. Keep the core here and move separate subjects into [[linked]] documents.`:n.some(o=>o.on==="boot"&&o.deliver==="preview")&&e>ie?`body is ${e} words, over the ${ie}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(Ve,"lengthFinding");async function Ke(n){const e=C(n);return await $(n.principal,e.cwd),await A(async t=>{const o=await E(t,n),a=e.owner===void 0?null:await q(t,o,e.owner,e.cwd),c=await t.prepare("SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND home_machine IS NULL AND kind != 'report' ORDER BY name").all(),s=[],f=new Map;for(const i of c){if(a&&i.owner!==a.id||!await de(t,o,i))continue;const d=await L(t,i),h=new Set(d.lint_ignore??[]),r=await R(t,i),m=u((l,y,F)=>{h.has(l)||s.push({name:r,severity:y,rule:l,message:F})},"add"),p=await ke(t,i),w=(await W(t,i))[0],_=w?j(w.body_hash):"",v=Ve(p,Ue(_));v&&m("length","error",v),p.some(l=>l.deliver==="preview")&&!He.test(i.preview??"")&&m("preview-shape","error",`a rule delivers this document at preview, so the preview must read "When <situation in the reader's work>, read this because <what the reader gains>"`);const D=new Set((await t.prepare("SELECT written_as FROM edges WHERE type = 'links-to' AND src = ?").all(i.id)).map(l=>l.written_as));for(const l of G(_)){if(D.has(l))continue;const y=await X(t,o,l);"remote"in y||"object"in y||"deleted"in y||m("links","warning",`[[${l}]] names no object in your view`)}if(p.some(l=>l.on==="workspace-open"&&l.deliver==="content")){const l=await ue(t,i);l.type==="repo"&&!h.has("workspace-open")&&f.set(l.name,[...f.get(l.name)??[],r])}}for(const[i,d]of f)if(!(d.length<2))for(const h of d)s.push({name:h,severity:"warning",rule:"workspace-open",message:`repo ${i} has ${d.length} documents delivered at content on workspace-open (${d.join(", ")}); keep one and link the rest from it`});return{findings:s}})}u(Ke,"lint");const O=u((n,e=200)=>async t=>({status:e,body:await n(t)}),"ok"),wt=[{method:"POST",pattern:"/v1/docs",handler:O(Le,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:O(Me)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:O(qe)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:O(Be)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:O(Pe)},{method:"POST",pattern:"/v1/docs/lint",handler:O(Ke)}];export{R as canonicalName,wt as docRoutes,T as repoChange,I as syncChanged};
@@ -1,4 +1,10 @@
1
- import type { RouteTable } from '../router.js';
1
+ import type { RequestContext, RouteTable } from '../router.js';
2
+ /** What a local resolution of `raw` finds: the id of a stub of another machine's object (a stub is never served as the object, however
3
+ * it is named), and whether a live or deleted object is here (an exact answer, with no peer call). */
4
+ export declare function resolvedLocally(raw: string, ctx: RequestContext): Promise<{
5
+ stub: string | null;
6
+ hit: boolean;
7
+ }>;
2
8
  /** How long a merged list waits for any one other machine before it counts as unreachable. */
3
9
  export declare const listBudget: {
4
10
  ms: number;
@@ -1,3 +1,3 @@
1
1
  var Fe=Object.defineProperty;var l=(e,t)=>Fe(e,"name",{value:t,configurable:!0});import{ApiError as y}from"../../../api/errors.js";import{PRIVACY_LEVELS_DTO as Ae}from"../../../api/dto/objects.js";import{openDb as N,withCanvasWrite as v}from"../../../core/canvas/db.js";import{actorOf as ae}from"../../../core/graph/documents.js";import{readerForObject as C,readerFor as We}from"../../../core/graph/reader.js";import{resolveName as Le,stubsNamed as Pe,unwrapRef as ie}from"../../../core/graph/names.js";import{canRead as D,holdsObjectsScope as Ce,spaceOf as De}from"../../../core/graph/access.js";import{customReachable as Me,mayActAs as re}from"../../../core/graph/custom.js";import{customObjectRef as oe,authorizeCustom as se,fenceArgv as J}from"./custom-objects.js";import{edgesOf as Ge}from"../../../core/graph/edges.js";import{publish as B}from"../../../core/graph/events.js";import{objectDTO as Ue}from"../../../core/graph/kinds/dto.js";import{isKind as He,kindNames as ze,kindOf as K}from"../../../core/graph/kinds/registry.js";import{listObjects as Ve,searchObjects as ue}from"../../../core/graph/objects.js";import{checkoutsForCwd as Je}from"../../../core/graph/repo-sync/repos.js";import{recordWatch as M,unwatch as Be,watchersOf as Ke,watchesOf as Ze}from"../../../core/graph/watches.js";import{armObjectWaitForWatch as G,settleObjectWaitForUnwatch as Qe}from"../../../core/graph/object-waits.js";import{readerFor as T,resolveRef as Z,warmReaderRepos as R}from"../reader.js";import{readDelegationFor as ce,runReadLeaf as Ye}from"../../plugin-objects/read-leaf.js";import{invalidRequest as U,isRecord as q}from"./validate.js";import{callerNode as le,callerStub as Q,forwardNamed as Xe,forwardVerb as fe,originates as de,queryWithoutMachine as H,relayError as z,remoteStub as xe,unknownError as we,forwarding as P,withMachine as Y}from"../../peer/forward.js";import{getById as I}from"../../../core/graph/objects.js";import{ensureNodeStub as et,isStub as X,stubOf as tt,withKindDecl as $}from"../../../core/peer/stubs.js";import{isUnreachable as nt}from"../../../core/peer/client.js";import{normalizeMachineId as F}from"../../../core/peer/directory.js";import{mapAsync as at}from"../../../core/async-array.js";const A=l(async e=>async t=>await Je(e,t),"reposFnFor");async function g(e,t){return{...await Ue(e,t),...t.type==="document"?{schema_type:t.schema_type??null}:{}}}l(g,"objectDTO");async function it(e,t){const n=t.filter(r=>r.type==="document").map(r=>r.id),a=new Map;if(n.length===0)return a;const i=await e.prepareUncached(`SELECT object, extensions_json FROM documents WHERE object IN (${n.map(()=>"?").join(", ")})`).all(...n);for(const r of i)r.extensions_json!==null&&a.set(r.object,JSON.parse(r.extensions_json));return a}l(it,"extensionsOf");async function me(e,t){const n=await it(e,t);return await at(t,async a=>({...await g(e,a),...await(await K(e,a.type)).listItem?.(e,a),...a.type==="document"?{extensions:n.get(a.id)??{}}:{}}))}l(me,"listItems");function rt(e){return`no object named ${e} on this runtime; it may be on another runtime, or the name may be misspelled`}l(rt,"remoteMessage");async function pe(e,t,n){try{return await S(e,t,n)}catch(a){if(a instanceof y&&a.code==="not_found"&&!a.details?.deleted)return null;throw a}}l(pe,"ownerFilter");async function S(e,t,n){const a=await Z(e,t,n);if("remote"in a)throw new y(404,"not_found",rt(a.remote),{next:"Check the name with `crtr canvas search`."});if("deleted"in a)throw new y(404,"not_found",`${a.deleted.name} is deleted`,{id:a.deleted.id,deleted:!0});return a}l(S,"liveRef");const O=l(e=>decodeURIComponent(e.params.ref),"refParam"),he=l(e=>{const t=ie(e),n=t.indexOf("/");return n>0?t.slice(0,n):t},"idOf");async function ye(e){const t=await I(await N(),he(e));return t!==null&&await X(await N(),t)?t.id:null}l(ye,"stubIdOf");async function ot(e,t){const n=await ye(e);return n!==null?{stub:n,hit:!0}:(await R(t.principal),await v(async a=>{try{const i=await T(a,t),r=l(async()=>{const s=await Pe(a,i,e);if(s.length>1)throw new y(409,"ambiguous_name",`'${e}' names more than one remote object`,{candidates:s.map(c=>c.id),next:"Read one by its object id."});return{stub:s[0]?.id??null,hit:!1}},"named");let o;try{o=await Z(a,i,e)}catch(s){if(s instanceof y&&s.code==="not_found")return await r();throw s}if("remote"in o)return await r();if("deleted"in o){const s=await r();return s.stub===null?{stub:null,hit:!0}:s}return{stub:await X(a,o)?o.id:null,hit:!0}}catch(i){if(i instanceof y&&i.code==="ambiguous_name")throw i;return{stub:null,hit:!1}}}))}l(ot,"resolvedLocally");async function x(e,t,n){const a=await ot(t,e);if(a.stub!==null)return fe(e,a.stub,n);if(a.hit||!de(e.principal))return{kind:"local"};const i=await fe(e,he(t),n);return i.kind==="local"&&ie(t).includes("/")?Xe(e,n,{method:"GET",path:V(t),query:H(e)}):i}l(x,"forwardRef");const V=l(e=>`/v1/objects/${encodeURIComponent(e)}`,"objectPath");async function be(e,t,n,a,i){const r=t?.object;!r?.id||!r.type||await v(async o=>{const s=await le(o,e);if(s===null)return;const c=await xe(o,r.id,r.type,n,{kind:r.kind??null,name:r.name??r.id,...r.preview===void 0?{}:{preview:r.preview},owner:r.owner??null,...r.kind_decl===void 0?{}:{kind_decl:r.kind_decl},...r.privacy===void 0?{}:{privacy:r.privacy}});await M(o,s,c,a,i===void 0?{}:{patterns:i}),a==="ask"&&await G(o,s,c)})}l(be,"watchRemote");async function st(e){const t=O(e),n=["1","true"].includes(e.query.get("fields")??""),a=await x(e,t,{method:"GET",path:V(t),query:H(e)});if(a.kind==="unknown")return{state:"unknown",ref:t,unreachable:a.unreachable};if(a.kind==="remote"){if(a.status>=400)throw z(a.status,a.body);return await be(e,a.body,a.machine.id,"read"),Y(a.body,a.machine)}const i=l(async s=>{const c=await Q(s,e);return c===null?await T(s,e):await C(s,c,await A(s))},"readerOf");await R(e.principal);const r=await v(async s=>{const c=await i(s),u=await Z(s,c,t);if("remote"in u)return{state:"remote",remote:u.remote};if("deleted"in u)return{state:"deleted",deleted:await g(s,u.deleted)};const f=await ce(s,u);if(f!==null)return{delegate:f,hitId:u.id};const{data:w,text:b}=await(await K(s,u.type)).read(s,c,u,{fields:n,reposFn:await A(s)});return{state:"object",type:u.type,object:await $(s,u,await g(s,u)),text:b,data:w}});if(!("delegate"in r))return r;const o=await Ye(r.delegate,e.principal);return await R(e.principal),await v(async s=>{const c=await i(s),u=await I(s,r.hitId);if(u===null)throw new y(404,"not_found","object not found");if(u.deleted_at!==null)return{state:"deleted",deleted:await g(s,u)};if(!await D(s,c,u))throw new y(404,"not_found","object not found");const{data:f,text:w}=await(await K(s,u.type)).read(s,c,u,{fields:n,reposFn:await A(s)});return await ce(s,u)===null?{state:"object",type:u.type,object:await $(s,u,await g(s,u)),text:w,data:f}:{state:"object",type:u.type,object:await $(s,u,await g(s,u)),text:"markdown"in o?o.markdown:`${w}
2
2
  content unavailable: ${o.unavailable}`,data:{...f,content:"markdown"in o?"live":"unavailable"}}})}l(st,"read");function ee(e,t,n,a){if(e!=null){if(!n.includes(e)){a.push({field:t,problem:`must be one of ${n.join(", ")}`,received:e});return}return e}}l(ee,"oneOf");async function ge(e,t,n){if(t!=null){if(typeof t!="string"||!await He(e,t)){n.push({field:"type",problem:`must be a kind: one of ${(await ze(e)).join(", ")}`,received:t});return}return t}}l(ge,"kindName");function je(e,t,n,a){if(e==null)return a;const i=Number(e);return!Number.isInteger(i)||i<1||i>100?(n.push({field:t,problem:"must be an integer from 1 to 100",received:e}),a):i}l(je,"limitOf");function _e(e,t){if(e==null)return;let n=e;if(typeof e=="string")try{n=JSON.parse(e)}catch{n=null}if(!q(n)||Object.values(n).some(a=>!q(a))){t.push({field:"extensions",problem:'must be an object of objects, e.g. {"firstlight":{"state":"active"}}',received:e});return}return n}l(_e,"extensionsParam");function ke(e,t){if(e!=null){if(typeof e!="string"||e===""){t.push({field:"schema_type",problem:"must be a non-empty string",received:e});return}return e}}l(ke,"schemaTypeParam");async function ve(e){const t=e.query,n=[],a=await ge(await N(),t.get("type"),n),i=ee(t.get("privacy"),"privacy",Ae,n),r=je(t.get("limit"),"limit",n,50),o=t.get("cursor");o!==null&&!/^\d+$/.test(o)&&n.push({field:"cursor",problem:"is not a cursor from a previous page",received:o});const s=t.get("deleted");s!==null&&!["0","1","true","false"].includes(s)&&n.push({field:"deleted",problem:"must be true or false",received:s});const c=t.get("shared");c!==null&&!["0","1","true","false"].includes(c)&&n.push({field:"shared",problem:"must be true or false",received:c});const u=_e(t.get("extensions"),n),f=ke(t.get("schema_type"),n);return n.length>0&&U("object list request",n),await R(e.principal),await v(async w=>{const b=await T(w,e),j={...a?{type:a}:{},...i?{privacy:i}:{},...t.get("kind")!==null?{kind:t.get("kind")}:{},...s==="1"||s==="true"?{deleted:!0}:{},...c==="1"||c==="true"?{shared:!0}:{},...u?{extensions:u}:{},...f?{schemaType:f}:{},...t.get("name")!==null?{namePrefix:t.get("name")}:{}},k=t.get("owner");if(k!==null){const m=await pe(w,b,k);if(m===null)return{items:[],next_cursor:null};j.owner=m}const p=await Ve(w,b,j,{limit:r,after_seq:o===null?0:Number(o)}),d=p.at(-1);return{items:await me(w,p),next_cursor:p.length===r&&d?String(d.seq):null}})}l(ve,"listLocal");const ut={ms:3e4},ct="self",te=l((e,t)=>e.map(n=>({...n,machine:t})),"labelItems");async function Oe(e,t,n,a){const i=P.current;try{const r=await i.call(t.id,{method:"GET",path:"/v1/objects",query:n},await i.envelope(e.principal),a);if(nt(r))return{state:"unreachable",items:[],next:null};if(r.status===426)return{state:"too_old",items:[],next:null};const o=r.body;return r.status!==200||!Array.isArray(o?.items)?{state:"unreachable",items:[],next:null}:{state:"ok",items:te(o.items,t),next:o.next_cursor??null}}catch{return{state:"unreachable",items:[],next:null}}}l(Oe,"remotePage");async function lt(e){const t=new Promise(n=>e.addEventListener("abort",()=>n(null),{once:!0}));return Promise.race([P.current.machines().catch(()=>null),t])}l(lt,"machinesWithin");async function ft(e){const t=e.query.get("machine");if(!de(e.principal)||!P.current.joined()||t===ct)return await ve(Ee(e));const a=P.current.self(),i=t!==null&&F(t)!==a,r=i?null:await ve(Ee(e)),o=new AbortController,s=setTimeout(()=>o.abort(),ut.ms);s.unref?.();try{const c=await lt(o.signal);if(c===null){if(i)throw new y(503,"runtime_unreachable",`The directory did not answer, so ${F(t)} cannot be listed.`,{machine:F(t)});return r}const u=l(d=>({id:d,name:c.find(m=>m.machine_id===d)?.name??d}),"labelOf"),f=u(a),w=c.filter(d=>d.machine_id!==a&&d.kind!=="device"),b=H(e);if(t!==null){if(!i)return{items:te(r.items,f),next_cursor:r.next_cursor,machines:[{machine_id:a,name:f.name,state:"ok",more:r.next_cursor!==null}]};const d=F(t);if(!w.some(L=>L.machine_id===d))throw new y(404,"not_found",`no machine ${d} on your account`,{machine:d});const m=await Oe(e,u(d),b,o.signal);return{items:m.items,next_cursor:m.next,machines:[{machine_id:d,name:u(d).name,state:m.state,more:m.next!==null}]}}const{cursor:j,...k}=b,p=await Promise.all(w.map(d=>Oe(e,u(d.machine_id),k,o.signal)));return{items:[...te(r.items,f),...p.flatMap(d=>d.items)],next_cursor:r.next_cursor,machines:[{machine_id:a,name:f.name,state:"ok",more:r.next_cursor!==null},...w.map((d,m)=>({machine_id:d.machine_id,name:d.name,state:p[m].state,more:p[m].next!==null}))]}}finally{clearTimeout(s)}}l(ft,"list");function Ee(e){const t=new URLSearchParams(e.query);return t.delete("machine"),{...e,query:t}}l(Ee,"withoutMachine");async function dt(e){const t=[],n=q(e.body)?e.body:{};q(e.body)||t.push({field:"body",problem:"must be an object",received:e.body});const a=n.text;(typeof a!="string"||a.trim()==="")&&t.push({field:"text",problem:"is required",received:a});const i=n.grep;if(i!==void 0)if(typeof i!="string")t.push({field:"grep",problem:"must be a regular expression string",received:i});else try{new RegExp(i)}catch(h){t.push({field:"grep",problem:`is not a valid regular expression: ${h.message}`,received:i})}n.body!==void 0&&typeof n.body!="boolean"&&t.push({field:"body",problem:"must be a boolean",received:n.body});const r=await ge(await N(),n.type,t);n.kind!==void 0&&typeof n.kind!="string"&&t.push({field:"kind",problem:"must be a string",received:n.kind}),n.owner!==void 0&&typeof n.owner!="string"&&t.push({field:"owner",problem:"must be a string",received:n.owner});const o=je(n.limit,"limit",t,50),s=_e(n.extensions,t),c=ke(n.schema_type,t);t.length>0&&U("object search request",t),await R(e.principal);const u=await v(async h=>{const _=await T(h,e),$e=typeof n.owner=="string"?await pe(h,_,n.owner):null;return{reader:_,owner:$e}}),{reader:f,owner:w}=u;if(typeof n.owner=="string"&&w===null)return{items:[]};const b={...s?{extensions:s}:{},...c?{schemaType:c}:{},...r?{type:r}:{},...typeof n.kind=="string"?{kind:n.kind}:{},...w?{owner:w}:{}},j=await N(),k=await ue(j,f,{text:a,body:n.body===!0||typeof i=="string",...b}),p=new Set(k.map(h=>h.object.id)),m=(typeof i=="string"?(await ue(j,f,{text:i,grep:!0,...b})).filter(h=>p.has(h.object.id)):k).slice(0,o),L=await me(j,m.map(h=>h.object));return{items:m.map((h,_)=>({object:L[_],...h.line!==void 0?{matches:[{line:h.line,text:h.text??""}]}:{}}))}}l(dt,"search");const Te=["interrupt","steer","after-turn","next-turn"],wt=/^(\*|>|[A-Za-z0-9_-]+)(\.(\*|>|[A-Za-z0-9_-]+))*$/;function mt(e,t){if(e!==void 0){if(!Array.isArray(e)||e.length===0||e.some(n=>typeof n!="string"||!wt.test(n)||n.includes(">")&&!n.endsWith(">"))){t.push({field:"patterns",problem:"must be a non-empty list of event patterns (dot-separated words, `*` for one word, a final `>` for the rest)",received:e});return}return e}}l(mt,"patternsOf");function pt(e,t){if(e===void 0)return;if(!q(e)||Object.keys(e).length===0){t.push({field:"config",problem:"must map levels 1-10 to a delivery",received:e});return}const n=[];for(const[a,i]of Object.entries(e)){const r=Number(a);!Number.isInteger(r)||r<1||r>10?t.push({field:`config.${a}`,problem:"level must be an integer from 1 to 10"}):Te.includes(i)?n.push({threshold:r,delivery:i}):t.push({field:`config.${a}`,problem:`delivery must be one of ${Te.join(", ")}`,received:i})}return n.sort((a,i)=>i.threshold-a.threshold)}l(pt,"configOf");async function Re(e){const t=await ye(e);if(t!==null)throw new y(400,"invalid_request",`${t} lives on another machine; an explicit watcher cannot watch it from here`,{id:t})}l(Re,"refuseStubWatch");async function ne(e,t){const n=await I(e,t)??await(async()=>{const a=await Le(e,await We(e,{class:"person"},async()=>[]),t);return"object"in a?a.object:null})();return n===null||n.deleted_at!==null||!(n.type.includes(".")||n.type==="node")?null:n}l(ne,"findWatcher");const E=l(()=>{throw new y(404,"not_found","watcher not found")},"watcherNotFound");async function qe(e,t,n){if(typeof t!="string"||n===void 0||n===null)return null;if(typeof n!="string")throw new y(400,"invalid_request","invalid watcher_machine");if(await ne(e,t)!==null)return null;const a=P.current;let i;try{i=await a.machines()}catch{throw new y(503,"runtime_unreachable","The directory did not answer, so the watcher machine cannot be checked.")}const r=F(n);return(r===a.self()||!i.some(o=>F(o.machine_id)===r))&&E(),r}l(qe,"remoteWatcherMachine");async function Ne(e,t,n,a){if(t.publishToken||t.principal.class==="anonymous")return E();const i=await T(e,t);if(i.class==="person")return E();let r;try{r=await S(e,i,a)}catch{return E()}const o=r.owner===null?null:await I(e,r.owner);return o===null||!await re(e,i,o)||!await Ce(e,i,"link",n)||(await De(e,n)).id===i.app.id&&!(await D(e,i,n)&&await D(e,await C(e,n),r))?E():(await J(e,t,n),{actor:i.app,object:r})}l(Ne,"linkWatcher");async function Ie(e,t,n){if(t.publishToken)return!0;if(t.principal.class==="anonymous"||!n.type.includes("."))return!1;const a=await T(e,t),i=n.owner===null?null:await I(e,n.owner);return i!==null&&await re(e,a,i)&&await Me(e,a,n)}l(Ie,"ownsWatcher");async function ht(e){const t=O(e),n=q(e.body)?e.body:{};if(typeof n.watcher=="string"&&await Re(t),typeof n.watcher!="string"){const i=await x(e,t,{method:"POST",path:`${V(t)}/watch`,body:e.body});if(i.kind==="unknown")throw we(t,i.unreachable);if(i.kind==="remote"){if(i.status>=400)throw z(i.status,i.body);const r=i.body.patterns;return await be(e,i.body,i.machine.id,"ask",r),Y(i.body,i.machine)}}const a=await qe(await N(),n.watcher,n.watcher_machine);return await yt(e,a)}l(ht,"watch");async function yt(e,t=null){const n=[],a=e.body===void 0?{}:q(e.body)?e.body:(n.push({field:"body",problem:"must be an object"}),{}),i=mt(a.patterns,n),r=pt(a.config,n);return a.filter!==void 0&&!q(a.filter)&&n.push({field:"filter",problem:"must be an object",received:a.filter}),a.watcher!==void 0&&typeof a.watcher!="string"&&n.push({field:"watcher",problem:"must be a custom-object ref",received:a.watcher}),a.watcher_machine!==void 0&&typeof a.watcher_machine!="string"&&n.push({field:"watcher_machine",problem:"must be a machine id",received:a.watcher_machine}),n.length>0&&U("watch request",n),await R(e.principal),await v(async o=>{const s=typeof a.watcher=="string";if((e.publishToken||e.principal.class==="anonymous")&&!s)throw new y(404,"not_found","watcher not found");const c=i??[">"],u={patterns:c,...r?{config:r}:{},...a.filter!==void 0?{filter:a.filter}:{}};if(s){const j=a.watcher,k=t===null?null:await bt(o,e,j,t),p=k??(e.publishToken?await oe(o,j):await ne(o,j));if(p===null)return E();if(k===null&&await Ie(o,e,p)){const L=await se(o,e,p);await J(o,e,p);const h=await C(o,p),_=await S(o,h,O(e));if(!await D(o,h,_))throw new y(404,"not_found","object not found");return await M(o,p,_,"act",u),await G(o,p,_),await B(o,{event:"watched",object:_,actor:L,attrs:{}},await A(o)),{object:await $(o,_,await g(o,_)),patterns:c}}const{actor:d,object:m}=await Ne(o,e,p,O(e));return await M(o,p,m,"act",{...u,linkApp:d}),await G(o,p,m),await B(o,{event:"watched",object:m,actor:d,attrs:{}},await A(o)),{object:await $(o,m,await g(o,m)),patterns:c}}const f=await T(o,e),w=await Q(o,e)??ae(f),b=await S(o,f,O(e));return await M(o,w,b,"ask",u),await G(o,w,b),await B(o,{event:"watched",object:b,actor:w,attrs:{}},await A(o)),{object:await $(o,b,await g(o,b)),patterns:c}})}l(yt,"watchLocal");async function bt(e,t,n,a){return t.publishToken||t.principal.class==="anonymous"?E():await et(e,n,a)}l(bt,"ensureNodeStubFor");async function Se(e,t,n,a){const i=l(async()=>(await e.prepare(`SELECT count(*) AS n FROM watch_subscriptions s JOIN watches w ON w.watch_id = s.watch
3
- WHERE w.watcher = ? AND w.object = ?`).get(t.id,n.id)).n,"count"),r=await i();await Be(e,t,n,a??void 0);const o=await i();return o===0&&await Qe(e,t,n),{object:await g(e,n),removed:r-o}}l(Se,"dropWatch");async function gt(e){const t=O(e);if(e.query.get("watcher")&&await Re(t),!e.query.get("watcher")){const a=await x(e,t,{method:"DELETE",path:`${V(t)}/watch`,query:H(e)});if(a.kind==="unknown")throw we(t,a.unreachable);if(a.kind==="remote"){const i=a.status===404?a.body.error?.details:void 0,r=i?.deleted===!0?i.id:void 0;if(a.status>=400&&r===void 0)throw z(a.status,a.body);const o=r??a.body.object?.id,s=await v(async c=>{const u=await le(c,e),f=o===void 0?null:await I(c,o);return u===null||f===null||!await X(c,f)?null:await Se(c,u,f,e.query.get("pattern"))});if(r!==void 0){if(s===null)throw z(a.status,a.body);return{...s,machine:a.machine}}return Y(a.body,a.machine)}}const n=await qe(await N(),e.query.get("watcher")??void 0,e.query.get("watcher_machine")??void 0);return await jt(e,n)}l(gt,"unwatchRoute");async function jt(e,t=null){const n=e.query.get("pattern"),a=e.query.get("watcher");return await R(e.principal),await v(async i=>{if((e.publishToken||e.principal.class==="anonymous")&&!a)throw new y(404,"not_found","watcher not found");const r=l(async(c,u)=>await Se(i,c,u,n),"drop");if(a){const c=t===null?null:await(async()=>{const f=await I(i,a);return f!==null&&await tt(i,f)!==null?f:null})();if(t!==null&&c===null)return E();const u=c??(e.publishToken?await oe(i,a):await ne(i,a));return u===null?E():c===null&&await Ie(i,e,u)?(await se(i,e,u),await J(i,e,u),await r(u,await S(i,await C(i,u),O(e)))):await r(u,(await Ne(i,e,u,O(e))).object)}const o=await T(i,e),s=await Q(i,e)??ae(o);return await r(s,await S(i,o,O(e)))})}l(jt,"unwatchLocal");async function _t(e){const t=[],n=ee(e.query.get("type"),"type",["acted-on","links-to","watches"],t),a=ee(e.query.get("direction"),"direction",["out","in"],t);return t.length>0&&U("edges request",t),await R(e.principal),await v(async i=>{const r=await T(i,e),o=await S(i,r,O(e)),s=[];if(n!=="watches")for(const c of await Ge(i,r,o,{...n?{type:n}:{},direction:a??"both"}))s.push({type:c.type,src:await g(i,c.src),dst:await g(i,c.dst),...c.written_as?{written_as:c.written_as}:{}});if(n===void 0||n==="watches"){const c=[...a!=="in"?await Ze(i,r,o):[],...a!=="out"?await Ke(i,r,o):[]];for(const u of c)s.push({type:"watches",src:await g(i,u.watcher),dst:await g(i,u.object)})}return{items:s}})}l(_t,"edges");const W=l(e=>async t=>({status:200,body:await e(t)}),"ok"),Zt=[{method:"POST",pattern:"/v1/objects/search",handler:W(dt)},{method:"GET",pattern:"/v1/objects/:ref/edges",handler:W(_t)},{method:"POST",pattern:"/v1/objects/:ref/watch",handler:W(ht)},{method:"DELETE",pattern:"/v1/objects/:ref/watch",handler:W(gt)},{method:"GET",pattern:"/v1/objects/:ref",handler:W(st)},{method:"GET",pattern:"/v1/objects",handler:W(ft)}];export{ut as listBudget,Zt as objectRoutes};
3
+ WHERE w.watcher = ? AND w.object = ?`).get(t.id,n.id)).n,"count"),r=await i();await Be(e,t,n,a??void 0);const o=await i();return o===0&&await Qe(e,t,n),{object:await g(e,n),removed:r-o}}l(Se,"dropWatch");async function gt(e){const t=O(e);if(e.query.get("watcher")&&await Re(t),!e.query.get("watcher")){const a=await x(e,t,{method:"DELETE",path:`${V(t)}/watch`,query:H(e)});if(a.kind==="unknown")throw we(t,a.unreachable);if(a.kind==="remote"){const i=a.status===404?a.body.error?.details:void 0,r=i?.deleted===!0?i.id:void 0;if(a.status>=400&&r===void 0)throw z(a.status,a.body);const o=r??a.body.object?.id,s=await v(async c=>{const u=await le(c,e),f=o===void 0?null:await I(c,o);return u===null||f===null||!await X(c,f)?null:await Se(c,u,f,e.query.get("pattern"))});if(r!==void 0){if(s===null)throw z(a.status,a.body);return{...s,machine:a.machine}}return Y(a.body,a.machine)}}const n=await qe(await N(),e.query.get("watcher")??void 0,e.query.get("watcher_machine")??void 0);return await jt(e,n)}l(gt,"unwatchRoute");async function jt(e,t=null){const n=e.query.get("pattern"),a=e.query.get("watcher");return await R(e.principal),await v(async i=>{if((e.publishToken||e.principal.class==="anonymous")&&!a)throw new y(404,"not_found","watcher not found");const r=l(async(c,u)=>await Se(i,c,u,n),"drop");if(a){const c=t===null?null:await(async()=>{const f=await I(i,a);return f!==null&&await tt(i,f)!==null?f:null})();if(t!==null&&c===null)return E();const u=c??(e.publishToken?await oe(i,a):await ne(i,a));return u===null?E():c===null&&await Ie(i,e,u)?(await se(i,e,u),await J(i,e,u),await r(u,await S(i,await C(i,u),O(e)))):await r(u,(await Ne(i,e,u,O(e))).object)}const o=await T(i,e),s=await Q(i,e)??ae(o);return await r(s,await S(i,o,O(e)))})}l(jt,"unwatchLocal");async function _t(e){const t=[],n=ee(e.query.get("type"),"type",["acted-on","links-to","watches"],t),a=ee(e.query.get("direction"),"direction",["out","in"],t);return t.length>0&&U("edges request",t),await R(e.principal),await v(async i=>{const r=await T(i,e),o=await S(i,r,O(e)),s=[];if(n!=="watches")for(const c of await Ge(i,r,o,{...n?{type:n}:{},direction:a??"both"}))s.push({type:c.type,src:await g(i,c.src),dst:await g(i,c.dst),...c.written_as?{written_as:c.written_as}:{}});if(n===void 0||n==="watches"){const c=[...a!=="in"?await Ze(i,r,o):[],...a!=="out"?await Ke(i,r,o):[]];for(const u of c)s.push({type:"watches",src:await g(i,u.watcher),dst:await g(i,u.object)})}return{items:s}})}l(_t,"edges");const W=l(e=>async t=>({status:200,body:await e(t)}),"ok"),Zt=[{method:"POST",pattern:"/v1/objects/search",handler:W(dt)},{method:"GET",pattern:"/v1/objects/:ref/edges",handler:W(_t)},{method:"POST",pattern:"/v1/objects/:ref/watch",handler:W(ht)},{method:"DELETE",pattern:"/v1/objects/:ref/watch",handler:W(gt)},{method:"GET",pattern:"/v1/objects/:ref",handler:W(st)},{method:"GET",pattern:"/v1/objects",handler:W(ft)}];export{ut as listBudget,Zt as objectRoutes,ot as resolvedLocally};
@@ -9,6 +9,6 @@ export interface BootMigration {
9
9
  * time (idempotent via its own convergence, not a version gate) and must
10
10
  * isolate its own per-item failures — a thrown error here is NOT caught by
11
11
  * the boot sequence and would abort daemon startup. */
12
- apply(): BootMigrationResult;
12
+ apply(): BootMigrationResult | Promise<BootMigrationResult>;
13
13
  }
14
14
  export declare const BOOT_MIGRATIONS: readonly BootMigration[];
@@ -1 +1 @@
1
- var n=Object.defineProperty;var i=(e,r)=>n(e,"name",{value:r,configurable:!0});import{migrateLegacyInboxIdentities as t}from"../core/feed/inbox-migrate.js";const a=[{id:"legacy-inbox-identities",description:"normalize pre-cutover inbox entry/operation ids (UUID-spelled or missing) and cursors (ISO-timestamp boundaries) into canonical 32-lowercase-hex identities the strict inbox reader requires.",apply:i(()=>{const e=t();return{changed:e.nodes>0||e.failures>0,summary:{...e}}},"apply")}];export{a as BOOT_MIGRATIONS};
1
+ var o=Object.defineProperty;var r=(e,t)=>o(e,"name",{value:t,configurable:!0});import{migrateLegacyInboxIdentities as i}from"../core/feed/inbox-migrate.js";import{backfillComputedRev as a}from"../core/grants/mirror.js";import{isCloudRole as s}from"../core/canvas/person-state.js";import{emitEvent as n}from"../core/events/emit.js";const u=[{id:"legacy-inbox-identities",description:"normalize pre-cutover inbox entry/operation ids (UUID-spelled or missing) and cursors (ISO-timestamp boundaries) into canonical 32-lowercase-hex identities the strict inbox reader requires.",apply:r(()=>{const e=i();return{changed:e.nodes>0||e.failures>0,summary:{...e}}},"apply")},{id:"grant-computed-rev",description:"mark grant rows a pre-C-D2 build wrote (scopes computed, computed_rev NULL) as computed for their revision, so the terminal grant reads as active before the first directory sync after an upgrade or rollback (NL-550). A cloud role skips it: the directory owns its shared rows.",apply:r(async()=>{if(s())return{changed:!1,summary:{}};try{const e=await a();return{changed:e>0,summary:{rows:e}}}catch(e){return n({level:"warn",event:"daemon.boot_migration.failed",error:e,fields:{migration:"grant-computed-rev"}}),{changed:!1,summary:{}}}},"apply")}];export{u as BOOT_MIGRATIONS};
@@ -1 +1 @@
1
- var v=Object.defineProperty;var d=(e,r)=>v(e,"name",{value:r,configurable:!0});import{randomUUID as m}from"node:crypto";import{setTimeout as I}from"node:timers/promises";import{CRON_DEATH_POLL_MS as S,CRON_KILL_GRACE_MS as A,EMPTY_STDOUT_SHA256 as f}from"./capture.js";import{cronDaemonInstanceId as C,cronOwnerAlive as L,ensureCronOwnerLease as O}from"./live-runs.js";import{crashedOneShotSummary as y,escalateFailure as H}from"./escalation.js";import{settleRun as h}from"./settle.js";import{runningCrons as M,stopLeasedCronRun as w,setCronState as g,verifyLeasedCronRun as l}from"../../core/canvas/crons.js";import{emitEvent as c}from"../../core/events/emit.js";const i=new Set;async function B(e,r={}){await O();for(const t of await M()){if(t.run_lease_owner===C||i.has(t.cron_id)||t.run_lease_owner!==null&&await L(t.run_lease_owner))continue;if(t.run_pid===null||!l(t)){i.add(t.cron_id),D(t,e,r);continue}const a=t.run_started_at!=null?new Date(t.run_started_at).getTime():e;if(e-a<t.run_timeout_s*1e3)continue;const o={runId:t.run_execution_id??m(),startedAtIso:t.run_started_at??new Date(e).toISOString(),exitCode:-1,timedOut:!0,runTimeoutS:t.run_timeout_s,processErrorMessage:null,stdoutHead:"",stdoutSha256:f,stderrHead:`run timed out after ${t.run_timeout_s}s (recovered stale lease from a prior daemon instance)`};i.add(t.cron_id);const n=(async()=>{await w(t),await x(t),await h(t,{outOfBand:!1,outcome:{kind:"disposed",ctx:o},record:{runId:o.runId,startedAtIso:o.startedAtIso,finishedAtMs:e,durationMs:e-a,exitCode:-1,stdoutHead:null,stderrHead:o.stderrHead},lease:{kind:"clear"}})})().finally(()=>i.delete(t.cron_id));r.register?.(n),n.catch(s=>{c({level:"error",event:"cron.stale_lease.settle_failed",error:s,fields:{cron_id:t.cron_id}})})}}d(B,"recoverStaleCronLeases");async function x(e){if(!await p(e)&&(await w(e,"SIGKILL"),!await p(e)))throw new Error(`cron ${e.cron_id} run ${e.run_pid} survived SIGKILL`)}d(x,"awaitLeasedRunDeath");async function p(e){const r=Date.now()+A;for(;Date.now()<r;){if(!l(e))return!0;await I(S)}return!l(e)}d(p,"pollLeasedRunGone");function D(e,r,t){const a=e.recur==null,o=e.run_started_at!=null?new Date(e.run_started_at).getTime():r,n=e.run_pid===null?"the daemon life that claimed this run ended before its launch pid was recorded \u2014 the command may have started and its outcome is unknown":"the daemon life that owned this run ended before it settled \u2014 the process is gone and its outcome was never observed",s={runId:e.run_execution_id??m(),startedAtIso:e.run_started_at??new Date(r).toISOString(),exitCode:null,timedOut:!1,runTimeoutS:e.run_timeout_s,processErrorMessage:n,stdoutHead:"",stdoutSha256:f,stderrHead:`run outcome unobserved: ${n}`},_=(async()=>{let u=`crashed: ${n}${a?"":" \u2014 the next occurrence is unaffected"}`;a&&(await g(e.cron_id,"paused"),u=e.on_output==="silent"?`crashed: ${n} \u2014 one-shot paused, not re-fired (silent: not escalated)`:`crashed one-shot: ${await H(e,s,y(n))}`),await h(e,{outOfBand:!1,outcome:{kind:"stated",delivered:u,paused:a},record:{runId:s.runId,startedAtIso:s.startedAtIso,finishedAtMs:r,durationMs:Math.max(0,r-o),exitCode:null,stdoutHead:null,stderrHead:s.stderrHead},lease:"release"}),c({level:"warn",event:"cron.run.unobserved",fields:{cron_id:e.cron_id,name:e.name,one_shot:a}})})().finally(()=>i.delete(e.cron_id));t.register?.(_),_.catch(u=>{c({level:"error",event:"cron.crashed_run.settle_failed",error:u,fields:{cron_id:e.cron_id}})})}d(D,"settleCrashedRun");export{B as recoverStaleCronLeases};
1
+ var S=Object.defineProperty;var i=(e,t)=>S(e,"name",{value:t,configurable:!0});import{randomUUID as w}from"node:crypto";import{setTimeout as L}from"node:timers/promises";import{CRON_DEATH_POLL_MS as R,CRON_KILL_GRACE_MS as C,EMPTY_STDOUT_SHA256 as h}from"./capture.js";import{cronDaemonInstanceId as A,cronOwnerAlive as v,ensureCronOwnerLease as O}from"./live-runs.js";import{crashedOneShotSummary as x,escalateFailure as E}from"./escalation.js";import{settleRun as p}from"./settle.js";import{adoptCronRunId as g,getCron as M,runningCrons as D,stopLeasedCronRun as I,setCronState as H,verifyLeasedCronRun as l}from"../../core/canvas/crons.js";import{claimReconcilerLease as T,releaseReconcilerLease as $,renewReconcilerLease as b}from"../../core/canvas/reconciler-leases.js";import{emitEvent as c}from"../../core/events/emit.js";const _="cron-recovery",f=3e4;async function k(e){const t=w();if(await T(_,e,t,f)===void 0)return;let r=!1;const a=i(async()=>{if(r)return!1;try{r=!await b(_,e,t,f)}catch{r=!0}return!r},"holds"),n=setInterval(()=>{a()},f/3);return n.unref(),{holds:a,release:i(async()=>{clearInterval(n),await $(_,e,t)},"release")}}i(k,"claimRecovery");async function G(e){const t=await M(e.cron_id);if(t===null||t.run_state!=="running"||t.run_execution_id!==e.run_execution_id||t.run_started_at!==e.run_started_at||t.run_lease_owner!==e.run_lease_owner||t.run_lease_owner!==null&&await v(t.run_lease_owner))return null;if(t.run_execution_id!==null)return t;const r=w();return await g(t,r)?{...t,run_execution_id:r}:null}i(G,"reverifyAbandoned");async function W(e,t={}){await O();for(const r of await D()){if(r.run_lease_owner===A||r.run_lease_owner!==null&&await v(r.run_lease_owner))continue;const a=r.run_pid===null||!l(r);if(!a&&e-(r.run_started_at!=null?new Date(r.run_started_at).getTime():e)<r.run_timeout_s*1e3)continue;const n=await k(r.cron_id);if(n===void 0)continue;let o;try{o=await G(r)}catch(s){throw await n.release(),s}if(o===null){await n.release();continue}if(a){U(o,e,t,n);continue}K(o,e,t,n)}}i(W,"recoverStaleCronLeases");function K(e,t,r,a){const n=e.run_started_at!=null?new Date(e.run_started_at).getTime():t,o={runId:e.run_execution_id,startedAtIso:e.run_started_at??new Date(t).toISOString(),exitCode:-1,timedOut:!0,runTimeoutS:e.run_timeout_s,processErrorMessage:null,stdoutHead:"",stdoutSha256:h,stderrHead:`run timed out after ${e.run_timeout_s}s (recovered stale lease from a prior daemon instance)`},s=(async()=>{await a.holds()&&(await I(e),await N(e),await a.holds()&&await p(e,{outOfBand:!1,outcome:{kind:"disposed",ctx:o},record:{runId:o.runId,startedAtIso:o.startedAtIso,finishedAtMs:t,durationMs:t-n,exitCode:-1,stdoutHead:null,stderrHead:o.stderrHead},lease:{kind:"clear"}}))})().finally(()=>a.release());r.register?.(s),s.catch(u=>{c({level:"error",event:"cron.stale_lease.settle_failed",error:u,fields:{cron_id:e.cron_id}})})}i(K,"recoverTimedOutRun");async function N(e){if(!await y(e)&&(await I(e,"SIGKILL"),!await y(e)))throw new Error(`cron ${e.cron_id} run ${e.run_pid} survived SIGKILL`)}i(N,"awaitLeasedRunDeath");async function y(e){const t=Date.now()+C;for(;Date.now()<t;){if(!l(e))return!0;await L(R)}return!l(e)}i(y,"pollLeasedRunGone");function U(e,t,r,a){const n=e.recur==null,o=e.run_started_at!=null?new Date(e.run_started_at).getTime():t,s=e.run_pid===null?"the daemon life that claimed this run ended before its launch pid was recorded \u2014 the command may have started and its outcome is unknown":"the daemon life that owned this run ended before it settled \u2014 the process is gone and its outcome was never observed",u={runId:e.run_execution_id,startedAtIso:e.run_started_at??new Date(t).toISOString(),exitCode:null,timedOut:!1,runTimeoutS:e.run_timeout_s,processErrorMessage:s,stdoutHead:"",stdoutSha256:h,stderrHead:`run outcome unobserved: ${s}`},m=(async()=>{let d=`crashed: ${s}${n?"":" \u2014 the next occurrence is unaffected"}`;if(n){if(!await a.holds())return;if(await H(e.cron_id,"paused"),e.on_output==="silent")d=`crashed: ${s} \u2014 one-shot paused, not re-fired (silent: not escalated)`;else{if(!await a.holds())return;d=`crashed one-shot: ${await E(e,u,x(s))}`}}await a.holds()&&(await p(e,{outOfBand:!1,outcome:{kind:"stated",delivered:d,paused:n},record:{runId:u.runId,startedAtIso:u.startedAtIso,finishedAtMs:t,durationMs:Math.max(0,t-o),exitCode:null,stdoutHead:null,stderrHead:u.stderrHead},lease:"release"}),c({level:"warn",event:"cron.run.unobserved",fields:{cron_id:e.cron_id,name:e.name,one_shot:n}}))})().finally(()=>a.release());r.register?.(m),m.catch(d=>{c({level:"error",event:"cron.crashed_run.settle_failed",error:d,fields:{cron_id:e.cron_id}})})}i(U,"settleCrashedRun");export{W as recoverStaleCronLeases};
@@ -1,5 +1,5 @@
1
1
  import type { Cron } from '../../core/canvas/crons.js';
2
2
  import type { CronSinkResolution } from './sinks.js';
3
- /** The notice for a paused cron: mail to the node that armed it while that node can still take mail,
4
- * otherwise a human ticket. `message` is the refusal, which names the scope the grant lost. */
3
+ /** The notice for a cron paused by a refused fire: mail to the node that armed it while that node can still take mail,
4
+ * otherwise a human ticket. `message` is the refusal. */
5
5
  export declare function narrowedNotice(c: Cron, message: string): Promise<CronSinkResolution>;
@@ -1,2 +1,2 @@
1
- var i=Object.defineProperty;var a=(e,o)=>i(e,"name",{value:o,configurable:!0});import{getNode as s}from"../../core/canvas/canvas.js";import{newNodeId as c}from"../../core/runtime/nodes.js";import{resolveLiveProfileName as l}from"./armed-context.js";import{canTakeMail as u}from"./ended.js";async function g(e,o){const n=e.created_by===null?null:await s(e.created_by),r=[`Cron "${e.name}" (${e.cron_id}) was paused: ${o}.`,"","Its scheduled fire was refused because the cron was armed with that scope and the grant it runs under no longer offers it. The cron will not fire again until you act. Nothing was widened.","",`- To keep the scope: restore it on the grant, then \`crtr cron resume ${e.cron_id}\`.`,`- To run without it: \`crtr cron cancel ${e.cron_id}\` and arm the cron again. A cron armed by a node with no scope limit now follows the grant's current scopes.`].join(`
2
- `),t=`\u23F0 cron ${e.name} paused: scope no longer granted`,d=e.cancel_on_wake&&e.anchor_node===n?.node_id;return n!==null&&!d&&await u(n)?{kind:"node",destinationNodeId:n.node_id,senderNodeId:null,label:t,stdout:r,urgency:"urgent"}:{kind:"action",adapter:{adapter:"cron_human",cronId:e.cron_id,requestId:await c(),title:t,summary:"A scheduled cron was paused because its grant lost a scope.",sessionName:`cron ${e.name} (${e.cron_id})`,profileName:l(e)??null},bodyJson:{stdout:r}}}a(g,"narrowedNotice");export{g as narrowedNotice};
1
+ var i=Object.defineProperty;var a=(e,n)=>i(e,"name",{value:n,configurable:!0});import{getNode as s}from"../../core/canvas/canvas.js";import{newNodeId as u}from"../../core/runtime/nodes.js";import{resolveLiveProfileName as l}from"./armed-context.js";import{canTakeMail as c}from"./ended.js";async function _(e,n){const o=e.created_by===null?null:await s(e.created_by),r=[`Cron "${e.name}" (${e.cron_id}) was paused: ${n}.`,"","Its scheduled fire was refused by the authority check, so it did not run. The cron will not fire again until you act. Nothing was widened.","",`- To keep it: fix what the refusal names (for a lost scope, restore it on the grant), then \`crtr cron resume ${e.cron_id}\`.`,`- To drop it: \`crtr cron cancel ${e.cron_id}\`, and arm the cron again if the work still matters.`].join(`
2
+ `),t=`\u23F0 cron ${e.name} paused: fire refused`,d=e.cancel_on_wake&&e.anchor_node===o?.node_id;return o!==null&&!d&&await c(o)?{kind:"node",destinationNodeId:o.node_id,senderNodeId:null,label:t,stdout:r,urgency:"urgent"}:{kind:"action",adapter:{adapter:"cron_human",cronId:e.cron_id,requestId:await u(),title:t,summary:"A scheduled cron was paused because its fire was refused.",sessionName:`cron ${e.name} (${e.cron_id})`,profileName:l(e)??null},bodyJson:{stdout:r}}}a(_,"narrowedNotice");export{_ as narrowedNotice};
@@ -1 +1 @@
1
- var p=Object.defineProperty;var c=(e,u)=>p(e,"name",{value:u,configurable:!0});import{consumeCron as w,recordCronRunLocked as _,releaseCronRunLease as y,setCronState as k}from"../../core/canvas/crons.js";import{withCanvasWrite as I}from"../../core/canvas/db.js";import{enqueueDaemonAction as b}from"../../core/canvas/daemon-actions.js";import{enqueueNodeMail as v}from"../../core/feed/messages.js";import{emitEvent as g}from"../../core/events/emit.js";import{narrowedNotice as C}from"./narrowed.js";import{disposeSettledRun as h}from"./disposition.js";function x(e){return typeof e=="object"&&(e.kind==="park"||e.kind==="redue")}c(x,"occurrenceStaysOwed");async function $(e,u){const{outOfBand:t,outcome:d,record:o,lease:s}=u;let i,a,n;if(d.kind==="disposed"){const r=await h(e,d.ctx,t,d.selfCanceled??!1);i=r.delivered,a=r.paused,n=r.obligation}else if(d.kind==="refused"){i=`refused: ${d.reason}`;const r=!t&&d.reason==="scope_narrowed";a=!t&&(e.recur==null||r),a&&await k(e.cron_id,"paused"),r&&(n=await C(e,d.message)),g({level:"warn",event:"cron.fire.refused",fields:{cron_id:e.cron_id,execution_id:o.runId,grant_id:e.grant_id,reason:d.reason,out_of_band:t}})}else i=d.delivered,a=d.paused??!1;const m=await I(async r=>{if(n!==void 0){if(n.kind==="node"){const f=await v(n.destinationNodeId,{kind:"message",label:n.label,senderNodeId:n.senderNodeId,bodyJson:{body:n.stdout},urgency:n.urgency,wake:1,idempotencyKey:o.runId});d.kind!=="refused"&&(i=`delivered: ${f.deliveryId}`)}else if(n.kind==="action"){const f=await b({adapter:n.adapter,bodyJson:n.bodyJson,idempotencyKey:o.runId});d.kind!=="refused"&&(i=`delivered: ${f}`)}}const l={run_id:o.runId,cron_id:e.cron_id,started:o.startedAtIso,finished:new Date(o.finishedAtMs).toISOString(),duration_ms:o.durationMs,exit_code:o.exitCode,stdout_head:o.stdoutHead,stderr_head:o.stderrHead,delivered:i};return await _(r,l),l});return s!=="none"&&await y(e.cron_id,s==="release"?void 0:s),!t&&e.recur==null&&!a&&!x(s)&&await w(e.cron_id),m}c($,"settleRun");export{$ as settleRun};
1
+ var p=Object.defineProperty;var c=(e,u)=>p(e,"name",{value:u,configurable:!0});import{consumeCron as w,recordCronRunLocked as _,releaseCronRunLease as y,setCronState as k}from"../../core/canvas/crons.js";import{withCanvasWrite as I}from"../../core/canvas/db.js";import{enqueueDaemonAction as b}from"../../core/canvas/daemon-actions.js";import{enqueueNodeMail as v}from"../../core/feed/messages.js";import{emitEvent as g}from"../../core/events/emit.js";import{narrowedNotice as C}from"./narrowed.js";import{disposeSettledRun as h}from"./disposition.js";function x(e){return typeof e=="object"&&(e.kind==="park"||e.kind==="redue")}c(x,"occurrenceStaysOwed");async function $(e,u){const{outOfBand:a,outcome:d,record:r,lease:s}=u;let i,t,n;if(d.kind==="disposed"){const o=await h(e,d.ctx,a,d.selfCanceled??!1);i=o.delivered,t=o.paused,n=o.obligation}else if(d.kind==="refused"){i=`refused: ${d.reason}`;const o=!a&&d.reason==="scope_narrowed";t=!a&&(e.recur==null||o),t&&await k(e.cron_id,"paused"),t&&(n=await C(e,d.message)),g({level:"warn",event:"cron.fire.refused",fields:{cron_id:e.cron_id,execution_id:r.runId,grant_id:e.grant_id,reason:d.reason,out_of_band:a}})}else i=d.delivered,t=d.paused??!1;const m=await I(async o=>{if(n!==void 0){if(n.kind==="node"){const f=await v(n.destinationNodeId,{kind:"message",label:n.label,senderNodeId:n.senderNodeId,bodyJson:{body:n.stdout},urgency:n.urgency,wake:1,idempotencyKey:r.runId});d.kind!=="refused"&&(i=`delivered: ${f.deliveryId}`)}else if(n.kind==="action"){const f=await b({adapter:n.adapter,bodyJson:n.bodyJson,idempotencyKey:r.runId});d.kind!=="refused"&&(i=`delivered: ${f}`)}}const l={run_id:r.runId,cron_id:e.cron_id,started:r.startedAtIso,finished:new Date(r.finishedAtMs).toISOString(),duration_ms:r.durationMs,exit_code:r.exitCode,stdout_head:r.stdoutHead,stderr_head:r.stderrHead,delivered:i};return await _(o,l),l});return s!=="none"&&await y(e.cron_id,r.runId,s==="release"?void 0:s),!a&&e.recur==null&&!t&&!x(s)&&await w(e.cron_id),m}c($,"settleRun");export{$ as settleRun};
@@ -1,2 +1,2 @@
1
- var N=Object.defineProperty;var i=(e,t)=>N(e,"name",{value:t,configurable:!0});import{randomUUID as Y}from"node:crypto";import{CRON_OUTPUT_HEAD_BYTES as E,EMPTY_STDOUT_SHA256 as q,formatCronOutputHead as A}from"./cron/capture.js";import{clearLiveRun as g,cronDaemonInstanceId as L,ensureCronOwnerLease as J,registerLiveRun as V}from"./cron/live-runs.js";import{closeCronExecution as X,openCronExecution as z}from"./cron/run-socket.js";import{appProcessSandboxed as G}from"../core/spaces/process.js";import{nodeAgentDir as K}from"../core/canvas/paths.js";import{uidForApp as Q}from"../core/spaces/users.js";import{buildArmedRunContext as W}from"./cron/armed-context.js";import{ensureCronFolder as Z}from"./cron/folders.js";import{checkCronFire as j,cronPrincipalOf as ee}from"./cron/authority.js";import{refusalError as te}from"../core/ledger/index.js";import{startRefusal as re}from"../core/runtime/start-check.js";import{decideHeldSettle as ne,EXIT_HELD as oe,HELD_DELIVERED as ae}from"./cron/held.js";import{settleRun as O}from"./cron/settle.js";import{applyOverlapPolicies as se,expireDueCrons as de}from"./cron/passes.js";import{dueClockCrons as ie,acquireCronRunLease as le,releaseCronRunLease as ue,attachCronRunPid as ce,advanceCron as fe,setCronState as me}from"../core/canvas/crons.js";import{emitEvent as $}from"../core/events/emit.js";import{runAppCommand as pe}from"../core/runtime/run-command.js";import{nextSlotAfter as he}from"../core/wake.js";import{CRON_OUTPUT_HEAD_BYTES as Ye}from"./cron/capture.js";import{cronDaemonInstanceId as Je,markSelfCanceledCronRun as Ve}from"./cron/live-runs.js";import{recoverStaleCronLeases as ze}from"./cron/lease-recovery.js";async function we(e,t){const{runId:a,outOfBand:s,startedAtIso:u,finishedAtMs:c,code:d,timedOut:n,processError:f}=t,_=!s&&!n&&f==null&&d===oe&&!t.wasReplaced&&!t.wasSelfCanceled;let p,r;return t.wasReplaced?(p={kind:"stated",delivered:"replaced (overlap=replace)"},r="release"):_?(p={kind:"stated",delivered:ae},r=await ne(e,u,c)??"release"):(p={kind:"disposed",ctx:{runId:a,startedAtIso:u,exitCode:n?-1:d,timedOut:n,runTimeoutS:e.run_timeout_s,processErrorMessage:f!=null?f.message:null,stdoutHead:t.stdoutHead,stdoutSha256:t.stdoutSha256,stderrHead:t.stderrHead},selfCanceled:t.wasSelfCanceled},r=s?"release":{kind:"clear"}),O(e,{outOfBand:s,outcome:p,record:{runId:a,startedAtIso:u,finishedAtMs:c,durationMs:c-t.startedAtMs,exitCode:n?-1:d,stdoutHead:t.stdoutHead===""?null:t.stdoutHead,stderrHead:f!=null?`run process error: ${f.message}`.slice(0,E):t.stderrHead===""?null:t.stderrHead},lease:r})}i(we,"settleClosedRun");function R(e,t,a,s){const u=Date.now(),c=`${a}: ${s.message}`.slice(0,E);return O(e,{outOfBand:t.outOfBand,outcome:{kind:"disposed",ctx:{runId:t.runId,startedAtIso:t.startedAtIso,exitCode:null,timedOut:!1,runTimeoutS:e.run_timeout_s,processErrorMessage:s.message,stdoutHead:"",stdoutSha256:q,stderrHead:c}},record:{runId:t.runId,startedAtIso:t.startedAtIso,finishedAtMs:u,durationMs:u-t.startedAtMs,exitCode:null,stdoutHead:null,stderrHead:c},lease:"release"})}i(R,"settleUnspawnedRun");async function P(e,t){let a;try{a=he(e.recur,new Date(t))}catch(s){return $({level:"error",event:"cron.recur.unparseable",error:s,fields:{cron_id:e.cron_id,name:e.name,recur:e.recur}}),await me(e.cron_id,"paused"),new Error(`unparseable recurrence ${JSON.stringify(e.recur)}: ${s.message} \u2014 this cron cannot be rescheduled until its recurrence is fixed (\`crtr cron cancel\` and re-arm it), so it has been paused.`)}return await fe(e.cron_id,a),null}i(P,"advanceRecurrence");function Be(e,t){return B(e,t).settled}i(Be,"executeCron");function B(e,t){const{outOfBand:a}=t;let s=i(()=>{},"admit");const u=new Promise(d=>{s=d}),c=new Promise((d,n)=>{let f=!1,_=i(()=>{},"undo");const p=i(r=>{if(_(),!f){n(r);return}f=!1,ue(e.cron_id).then(()=>n(r),m=>{$({level:"error",event:"cron.lease.release_failed",error:m,fields:{cron_id:e.cron_id}}),n(r)})},"fail");(async()=>{const r=Y(),m=Date.now(),C=new Date(m).toISOString(),S={runId:r,outOfBand:a,startedAtIso:C,startedAtMs:m};if(await J(),!await le(e.cron_id,L,C,a,r)){d(null);return}f=!0;let h=await j(ee(e));if(h.allowed){const o=await re({grantee:e.grantee,sub:e.sub,grant_id:e.grant_id});o!==null&&(h={allowed:!1,reason:"limit_reached",message:te(o).message})}if(!h.allowed){try{if(!a&&e.recur!=null){const w=await P(e,m);if(w!==null)throw w}}catch(w){R(e,S,"run could not be armed",w).then(d,n);return}const o=Date.now();O(e,{outOfBand:a,outcome:{kind:"refused",reason:h.reason,message:h.message},record:{runId:r,startedAtIso:C,finishedAtMs:o,durationMs:o-m,exitCode:null,stdoutHead:null,stderrHead:`${h.reason}: ${h.message}`.slice(0,E)},lease:"release"}).then(d,n);return}let M;try{if(e.isolated&&Z(e.grantee,e.cron_id),{env:M}=await W(e,r),!a&&e.recur!=null){const o=await P(e,m);if(o!==null)throw o}}catch(o){R(e,S,"run could not be armed",o).then(d,n);return}let b=null,T=!1;const D={runId:r,replaced:!1,selfCanceled:!1,kill:i(()=>{b!==null?b():T=!0},"kill")};V(e.cron_id,D),_=i(()=>g(e.cron_id,r),"undo"),s();const x=G()?{executionId:r,kind:"cron",cronId:e.cron_id,fromExec:e.armed_from_exec,grantee:e.grantee,sub:e.sub,grantId:e.grant_id,scopes:e.scopes,originRunId:e.origin_run_id,isolated:e.isolated,anchor:e.anchor_node,uid:await Q(e.grantee),rebuilt:!1,refusal:null}:null,I=i(()=>{x!==null&&X(r)},"closeExecution");_=i(()=>{g(e.cron_id,r),I()},"undo"),i(async()=>(x!==null&&await z(x,e.cwd,e.profile),pe({command:e.command,cwd:e.cwd,env:M,app:e.grantee,isolated:e.isolated,principal:{sub:e.sub,grantId:e.grant_id,scopes:e.scopes},...e.isolated?{folder:e.cwd}:{},...x!==null?{cron:{executionId:r,...e.anchor_node===null||e.isolated?{}:{anchorDir:K(e.anchor_node,e.grantee)}}}:e.anchor_node===null||e.isolated?{}:{nodeId:e.anchor_node},timeoutMs:e.run_timeout_s*1e3,headBytes:E})),"launch")().then(async o=>{if(o.pid===null){g(e.cron_id,r),I(),o.completion.then(l=>{R(e,S,"run failed to spawn",l.processError??new Error("bash did not provide a process id")).then(d,n)},n);return}_=i(()=>{g(e.cron_id,r),I(),o.terminate()},"undo");let w=!1;const v=i(()=>{o.completion.then(l=>{const k=D.replaced,U=D.selfCanceled,H=x?.refusal??null;if(g(e.cron_id,r),I(),w){d(null);return}if(H!==null&&!k){const y=Date.now(),F=A(l.stderr,l.stderrTruncated);O(e,{outOfBand:a,outcome:{kind:"refused",reason:H.reason,message:H.message},record:{runId:r,startedAtIso:C,finishedAtMs:y,durationMs:y-m,exitCode:l.exitCode,stdoutHead:A(l.stdout,l.stdoutTruncated)||null,stderrHead:`${H.reason}: ${H.message}
2
- ${F}`.trim().slice(0,E)},lease:"release"}).then(d,n);return}we(e,{runId:r,outOfBand:a,startedAtIso:C,startedAtMs:m,finishedAtMs:Date.now(),code:l.exitCode,timedOut:l.timedOut,processError:l.processError,stdoutHead:A(l.stdout,l.stdoutTruncated),stderrHead:A(l.stderr,l.stderrTruncated),stdoutSha256:l.stdoutSha256,wasReplaced:k,wasSelfCanceled:U}).then(d,n)},n)},"settleCommand");if(!await ce(e.cron_id,L,C,o.pid)&&!D.selfCanceled){w=!0,o.terminate(),v();return}b=o.terminate,T&&o.terminate(),v()},o=>{g(e.cron_id,r),I(),R(e,S,"run failed to spawn",o).then(d,n)}).catch(p)})().catch(p)});return c.then(s,s),{admitted:u,settled:c}}i(B,"startCron");async function Ue(e,t={}){const a=new Date(e).toISOString();await de(a),await se(e,a);const s=[];for(const u of await ie(a)){const{admitted:c,settled:d}=B(u,{outOfBand:!1}),n=d.then(()=>{});n.catch(()=>{}),await c,s.push(n),t.register?.(n)}for(const u of s)u.catch(()=>{});return s}i(Ue,"runDueCrons");export{Ye as CRON_OUTPUT_HEAD_BYTES,Je as cronDaemonInstanceId,Be as executeCron,Ve as markSelfCanceledCronRun,ze as recoverStaleCronLeases,Ue as runDueCrons,B as startCron};
1
+ var N=Object.defineProperty;var i=(e,t)=>N(e,"name",{value:t,configurable:!0});import{randomUUID as Y}from"node:crypto";import{CRON_OUTPUT_HEAD_BYTES as E,EMPTY_STDOUT_SHA256 as q,formatCronOutputHead as A}from"./cron/capture.js";import{clearLiveRun as g,cronDaemonInstanceId as L,ensureCronOwnerLease as J,registerLiveRun as V}from"./cron/live-runs.js";import{closeCronExecution as X,openCronExecution as z}from"./cron/run-socket.js";import{appProcessSandboxed as G}from"../core/spaces/process.js";import{nodeAgentDir as K}from"../core/canvas/paths.js";import{uidForApp as Q}from"../core/spaces/users.js";import{buildArmedRunContext as W}from"./cron/armed-context.js";import{ensureCronFolder as Z}from"./cron/folders.js";import{checkCronFire as j,cronPrincipalOf as ee}from"./cron/authority.js";import{refusalError as te}from"../core/ledger/index.js";import{startRefusal as re}from"../core/runtime/start-check.js";import{decideHeldSettle as ne,EXIT_HELD as oe,HELD_DELIVERED as ae}from"./cron/held.js";import{settleRun as O}from"./cron/settle.js";import{applyOverlapPolicies as se,expireDueCrons as de}from"./cron/passes.js";import{dueClockCrons as ie,acquireCronRunLease as le,releaseCronRunLease as ue,attachCronRunPid as ce,advanceCron as fe,setCronState as me}from"../core/canvas/crons.js";import{emitEvent as $}from"../core/events/emit.js";import{runAppCommand as pe}from"../core/runtime/run-command.js";import{nextSlotAfter as he}from"../core/wake.js";import{CRON_OUTPUT_HEAD_BYTES as Ye}from"./cron/capture.js";import{cronDaemonInstanceId as Je,markSelfCanceledCronRun as Ve}from"./cron/live-runs.js";import{recoverStaleCronLeases as ze}from"./cron/lease-recovery.js";async function we(e,t){const{runId:o,outOfBand:s,startedAtIso:u,finishedAtMs:c,code:d,timedOut:r,processError:m}=t,a=!s&&!r&&m==null&&d===oe&&!t.wasReplaced&&!t.wasSelfCanceled;let p,w;return t.wasReplaced?(p={kind:"stated",delivered:"replaced (overlap=replace)"},w="release"):a?(p={kind:"stated",delivered:ae},w=await ne(e,u,c)??"release"):(p={kind:"disposed",ctx:{runId:o,startedAtIso:u,exitCode:r?-1:d,timedOut:r,runTimeoutS:e.run_timeout_s,processErrorMessage:m!=null?m.message:null,stdoutHead:t.stdoutHead,stdoutSha256:t.stdoutSha256,stderrHead:t.stderrHead},selfCanceled:t.wasSelfCanceled},w=s?"release":{kind:"clear"}),O(e,{outOfBand:s,outcome:p,record:{runId:o,startedAtIso:u,finishedAtMs:c,durationMs:c-t.startedAtMs,exitCode:r?-1:d,stdoutHead:t.stdoutHead===""?null:t.stdoutHead,stderrHead:m!=null?`run process error: ${m.message}`.slice(0,E):t.stderrHead===""?null:t.stderrHead},lease:w})}i(we,"settleClosedRun");function R(e,t,o,s){const u=Date.now(),c=`${o}: ${s.message}`.slice(0,E);return O(e,{outOfBand:t.outOfBand,outcome:{kind:"disposed",ctx:{runId:t.runId,startedAtIso:t.startedAtIso,exitCode:null,timedOut:!1,runTimeoutS:e.run_timeout_s,processErrorMessage:s.message,stdoutHead:"",stdoutSha256:q,stderrHead:c}},record:{runId:t.runId,startedAtIso:t.startedAtIso,finishedAtMs:u,durationMs:u-t.startedAtMs,exitCode:null,stdoutHead:null,stderrHead:c},lease:"release"})}i(R,"settleUnspawnedRun");async function P(e,t){let o;try{o=he(e.recur,new Date(t))}catch(s){return $({level:"error",event:"cron.recur.unparseable",error:s,fields:{cron_id:e.cron_id,name:e.name,recur:e.recur}}),await me(e.cron_id,"paused"),new Error(`unparseable recurrence ${JSON.stringify(e.recur)}: ${s.message} \u2014 this cron cannot be rescheduled until its recurrence is fixed (\`crtr cron cancel\` and re-arm it), so it has been paused.`)}return await fe(e.cron_id,o),null}i(P,"advanceRecurrence");function Be(e,t){return B(e,t).settled}i(Be,"executeCron");function B(e,t){const{outOfBand:o}=t;let s=i(()=>{},"admit");const u=new Promise(d=>{s=d}),c=new Promise((d,r)=>{let m=!1;const a=Y();let p=i(()=>{},"undo");const w=i(f=>{if(p(),!m){r(f);return}m=!1,ue(e.cron_id,a).then(()=>r(f),h=>{$({level:"error",event:"cron.lease.release_failed",error:h,fields:{cron_id:e.cron_id}}),r(f)})},"fail");(async()=>{const f=Date.now(),h=new Date(f).toISOString(),S={runId:a,outOfBand:o,startedAtIso:h,startedAtMs:f};if(await J(),!await le(e.cron_id,L,h,o,a)){d(null);return}m=!0;let _=await j(ee(e));if(_.allowed){const n=await re({grantee:e.grantee,sub:e.sub,grant_id:e.grant_id});n!==null&&(_={allowed:!1,reason:"limit_reached",message:te(n).message})}if(!_.allowed){try{if(!o&&e.recur!=null){const C=await P(e,f);if(C!==null)throw C}}catch(C){R(e,S,"run could not be armed",C).then(d,r);return}const n=Date.now();O(e,{outOfBand:o,outcome:{kind:"refused",reason:_.reason,message:_.message},record:{runId:a,startedAtIso:h,finishedAtMs:n,durationMs:n-f,exitCode:null,stdoutHead:null,stderrHead:`${_.reason}: ${_.message}`.slice(0,E)},lease:"release"}).then(d,r);return}let M;try{if(e.isolated&&Z(e.grantee,e.cron_id),{env:M}=await W(e,a),!o&&e.recur!=null){const n=await P(e,f);if(n!==null)throw n}}catch(n){R(e,S,"run could not be armed",n).then(d,r);return}let b=null,T=!1;const D={runId:a,replaced:!1,selfCanceled:!1,kill:i(()=>{b!==null?b():T=!0},"kill")};V(e.cron_id,D),p=i(()=>g(e.cron_id,a),"undo"),s();const x=G()?{executionId:a,kind:"cron",cronId:e.cron_id,fromExec:e.armed_from_exec,grantee:e.grantee,sub:e.sub,grantId:e.grant_id,scopes:e.scopes,originRunId:e.origin_run_id,isolated:e.isolated,anchor:e.anchor_node,uid:await Q(e.grantee),rebuilt:!1,refusal:null}:null,I=i(()=>{x!==null&&X(a)},"closeExecution");p=i(()=>{g(e.cron_id,a),I()},"undo"),i(async()=>(x!==null&&await z(x,e.cwd,e.profile),pe({command:e.command,cwd:e.cwd,env:M,app:e.grantee,isolated:e.isolated,principal:{sub:e.sub,grantId:e.grant_id,scopes:e.scopes},...e.isolated?{folder:e.cwd}:{},...x!==null?{cron:{executionId:a,...e.anchor_node===null||e.isolated?{}:{anchorDir:K(e.anchor_node,e.grantee)}}}:e.anchor_node===null||e.isolated?{}:{nodeId:e.anchor_node},timeoutMs:e.run_timeout_s*1e3,headBytes:E})),"launch")().then(async n=>{if(n.pid===null){g(e.cron_id,a),I(),n.completion.then(l=>{R(e,S,"run failed to spawn",l.processError??new Error("bash did not provide a process id")).then(d,r)},r);return}p=i(()=>{g(e.cron_id,a),I(),n.terminate()},"undo");let C=!1;const v=i(()=>{n.completion.then(l=>{const k=D.replaced,U=D.selfCanceled,H=x?.refusal??null;if(g(e.cron_id,a),I(),C){d(null);return}if(H!==null&&!k){const y=Date.now(),F=A(l.stderr,l.stderrTruncated);O(e,{outOfBand:o,outcome:{kind:"refused",reason:H.reason,message:H.message},record:{runId:a,startedAtIso:h,finishedAtMs:y,durationMs:y-f,exitCode:l.exitCode,stdoutHead:A(l.stdout,l.stdoutTruncated)||null,stderrHead:`${H.reason}: ${H.message}
2
+ ${F}`.trim().slice(0,E)},lease:"release"}).then(d,r);return}we(e,{runId:a,outOfBand:o,startedAtIso:h,startedAtMs:f,finishedAtMs:Date.now(),code:l.exitCode,timedOut:l.timedOut,processError:l.processError,stdoutHead:A(l.stdout,l.stdoutTruncated),stderrHead:A(l.stderr,l.stderrTruncated),stdoutSha256:l.stdoutSha256,wasReplaced:k,wasSelfCanceled:U}).then(d,r)},r)},"settleCommand");if(!await ce(e.cron_id,L,h,n.pid)&&!D.selfCanceled){C=!0,n.terminate(),v();return}b=n.terminate,T&&n.terminate(),v()},n=>{g(e.cron_id,a),I(),R(e,S,"run failed to spawn",n).then(d,r)}).catch(w)})().catch(w)});return c.then(s,s),{admitted:u,settled:c}}i(B,"startCron");async function Ue(e,t={}){const o=new Date(e).toISOString();await de(o),await se(e,o);const s=[];for(const u of await ie(o)){const{admitted:c,settled:d}=B(u,{outOfBand:!1}),r=d.then(()=>{});r.catch(()=>{}),await c,s.push(r),t.register?.(r)}for(const u of s)u.catch(()=>{});return s}i(Ue,"runDueCrons");export{Ye as CRON_OUTPUT_HEAD_BYTES,Je as cronDaemonInstanceId,Be as executeCron,Ve as markSelfCanceledCronRun,ze as recoverStaleCronLeases,Ue as runDueCrons,B as startCron};