@north-light/cli 0.3.533 → 0.3.535
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/dto/crons.d.ts +1 -1
- package/dist/api/dto/nodes.d.ts +2 -0
- package/dist/builtin-memory/crouter-sdk/release-notes.md +6 -1
- package/dist/builtin-memory/northlight-apps/providers/README.md +1 -1
- package/dist/builtin-memory/northlight-apps/providers/sms.md +1 -1
- package/dist/clients/attach/viewer.js +2 -2
- package/dist/commands/api-client.d.ts +1 -0
- package/dist/commands/api-client.js +1 -1
- package/dist/commands/canvas.js +1 -1
- package/dist/commands/node/inspect.js +9 -9
- package/dist/core/bash-jobs.d.ts +4 -0
- package/dist/core/bash-jobs.js +8 -8
- package/dist/core/canvas/canvas.d.ts +16 -2
- package/dist/core/canvas/canvas.js +20 -20
- package/dist/core/canvas/recovery.js +1 -1
- package/dist/core/canvas/remote-canvas-source.d.ts +1 -0
- package/dist/core/canvas/remote-canvas-source.js +1 -1
- package/dist/core/canvas/source.d.ts +4 -0
- package/dist/core/canvas/source.js +1 -1
- package/dist/core/canvas/tree.d.ts +2 -0
- package/dist/core/canvas/tree.js +12 -5
- package/dist/core/graph/documents.js +7 -7
- package/dist/core/graph/watches.d.ts +0 -2
- package/dist/core/graph/watches.js +7 -12
- package/dist/core/runs/questions.d.ts +2 -0
- package/dist/core/runs/questions.js +1 -1
- package/dist/core/runtime/broker/fault-retry.d.ts +6 -0
- package/dist/core/runtime/broker/fault-retry.js +1 -1
- package/dist/core/runtime/launch.js +2 -2
- package/dist/core/worktree.js +8 -8
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/docs.js +1 -1
- package/dist/daemon/api/handlers/nodes.js +1 -1
- package/dist/daemon/api/handlers/objects.js +3 -3
- package/dist/daemon/api/handlers/questions.js +1 -1
- package/dist/daemon/cron/ended.d.ts +3 -0
- package/dist/daemon/cron/ended.js +2 -2
- package/dist/daemon/cron/narrowed.d.ts +5 -0
- package/dist/daemon/cron/narrowed.js +2 -0
- package/dist/daemon/cron/settle.js +1 -1
- package/dist/daemon/reconcilers/bash-deadline.d.ts +6 -0
- package/dist/daemon/reconcilers/bash-deadline.js +3 -2
- package/dist/daemon/runs/observer.d.ts +2 -0
- package/dist/daemon/runs/observer.js +2 -2
- package/docs/apps/providers/README.md +1 -1
- package/docs/apps/providers/sms.md +1 -1
- package/docs/sdk/release-notes.md +6 -1
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
- package/dist/core/canvas/nav-model.d.ts +0 -44
- package/dist/core/canvas/nav-model.js +0 -1
|
@@ -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 z,sep as G}from"node:path";import{ApiError as c}from"../../../api/errors.js";import{armCron as rn,parseCronSink as on,getCron as v,listCrons as P,listCronRuns as F,pokeHeldCrons as tn,setCronState as L}from"../../../core/canvas/crons.js";import{getNode as _}from"../../../core/canvas/canvas.js";import{appSpace as sn,cronFolder as an}from"../../../core/layout.js";import{appProcessesSandboxed as cn,cwdIsMounted as ln}from"../../../core/spaces/process.js";import{activeGrant as dn,activeGrantForApp as un,grantFor as pn,terminalGrant as fn}from"../../../core/grants/mirror.js";import{readConfig as wn}from"../../../core/config.js";import{assertScope as Z}from"../../../core/scopes.js";import{InputError as a}from"../../../core/io.js";import{nextSlotAfter as _n}from"../../../core/wake.js";import{executeCron as hn,markSelfCanceledCronRun as mn}from"../../cron-run.js";import{toCronDTO as k,toCronRunDTO as H}from"../map.js";import{appOf as T,assertCeiling as gn,canReadNode as yn}from"../router.js";import{checkCronBirth as bn,checkCronFire as vn}from"../../cron/authority.js";import{endCron as kn}from"../../cron/ended.js";import{requireUsableProfile as Sn}from"./profiles.js";import{filterAsync as $n,mapAsync as Cn}from"../../../core/async-array.js";function E(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new a({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}t(E,"requireString");function C(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new a({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(C,"requireEnum");async function On(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new a({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(await _(n)===null)throw new a({error:"sink_node_not_found",message:`sink node ${n} does not exist on the canvas`,field:"sink",next:"Name a live node id, or use spawn:<kind> for a delivery that outlives any one node."});return JSON.stringify({node:n})}if(e.startsWith("spawn:")){const n=e.slice(6).trim();if(n==="")throw new a({error:"bad_sink",message:"spawn sink requires a node kind",field:"sink",next:'Use sink "spawn:<kind>" (e.g. spawn:general).'});return JSON.stringify({spawn:n})}throw new a({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(On,"parseSinkSpec");const In=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function M(e,n){if(!In.test(e)||Number.isNaN(Date.parse(e)))throw new a({error:`bad_${n}`,message:`"${e}" is not a UTC ISO instant`,field:n,next:'Send a resolved UTC instant like "2026-06-07T09:00:00.000Z" (the client resolves the time grammar).'});return e}t(M,"requireUtcIso");function qn(e){try{_n(e,new Date)}catch(n){throw new a({error:"bad_recur",message:`recur is not advanceable: ${n instanceof Error?n.message:String(n)}`,field:"recur",next:'Send recur JSON the daemon can advance: {"every":"6h"} or {"cron":"0 9 * * *","tz":"America/New_York"}.'})}return e}t(qn,"requireRecur");function Nn(e){let n;try{n=JSON.parse(e)}catch{throw new a({error:"bad_env_json",message:"env_json is not valid JSON",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'})}if(typeof n!="object"||n===null||Array.isArray(n))throw new a({error:"bad_env_json",message:"env_json must be a JSON object",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'});for(const[r,o]of Object.entries(n))if(typeof o!="string")throw new a({error:"bad_env_json",message:`env_json value for "${r}" is not a string`,field:"env_json",next:"Every env value must be a string."});return e}t(Nn,"requireEnvJson");function U(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(U,"callerProfile");function Y(e,n){return n===null||e.scope==="global"||e.profile===n}t(Y,"inScope");function K(e){return e.principal.class==="person"?null:T(e.principal)}t(K,"callerApp");async function J(e,n){return await yn(e.principal,n.grantee,n.created_by??void 0)}t(J,"visibleTo");function jn(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(jn,"anchorNodeOf");async function An(e,n,r,o,i,l){const d=typeof e.cwd=="string"&&e.cwd.trim()!==""?z(e.cwd):null;if(!cn()){if(d===null)throw new a({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return d}const p=o!==null&&await _(o)!==null?o:void 0,u=t(async q=>await ln(n,q,p,i,void 0,l),"reaches");if(d!==null&&await u(d))return d;const s=sn(n).files;if(d!==null&&e.cwd_is_default!==!0)throw new c(400,"invalid_request",`cwd ${d} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${s}`,{cwd:d,default:s},void 0,{param:"cwd"});const h=r===null?null:(await _(r))?.cwd??null;return h!==null&&await u(h)?h:s}t(An,"resolveArmedCwd");async function Pn(e,n,r){const o=e.principal;if(o.class==="person"){const s=await fn();return{sub:s?.sub??"user:local",grant_id:s?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const s=await un(n);if(s===null)throw new c(401,"grant_revoked",`no active grant for ${n}`);const h=e.ceiling??o.scopes;return{sub:s.sub,grant_id:s.id,scopes:[...h??s.resolved],origin_run_id:null}}if(o.class==="cron"){const s=await vn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!s.allowed)throw new c(s.reason==="scope_missing"?403:401,s.reason==="scope_missing"?"scope_missing":"grant_revoked",s.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?[...s.effectiveScopes]:[...o.scopes],origin_run_id:o.origin_run}}if(o.class!=="broker")throw new c(401,"unauthorized","this caller cannot arm a cron");const i=r??await _(o.node),l=i?.grant_id??o.grant,d=i?.sub??o.sub,p=l!=null?await dn(l):await pn(n,d);if(p===null){if(n==="app:terminal"&&!wn("user").runtime?.issuer)return{sub:d,grant_id:l??null,scopes:i?.scopes??o.scopes,origin_run_id:i?.run_id??o.run};throw new c(401,"grant_revoked","the node grant is not active")}const u=i?.scopes??o.scopes;return{sub:p.sub,grant_id:p.id,scopes:u===null?[...p.resolved]:[...u],origin_run_id:i?.run_id??o.run}}t(Pn,"armPrincipal");async function Tn(e){const n=e.body??{},r=E(n,"name"),o=E(n,"command"),i=M(E(n,"fire_at"),"fire_at"),l=typeof n.recur=="string"&&n.recur!==""?qn(n.recur):null,d=typeof n.expires_at=="string"&&n.expires_at!==""?M(n.expires_at,"expires_at"):null,p=typeof n.env_json=="string"&&n.env_json!==""?Nn(n.env_json):null,u=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,s=C(n.on_output,"on_output",["silent","on-failure","always","on-change"],u?.startsWith("node:")?"on-change":"on-failure"),h=C(n.overlap,"overlap",["skip","queue","replace"],"skip"),q=C(n.tier,"tier",["normal","urgent","critical"],"normal"),D=s==="always"||s==="on-change";if(D&&u===null)throw new a({error:"sink_required",message:`on_output "${s}" delivers stdout every qualifying run \u2014 it needs a sink`,field:"sink",next:"Pass sink node:<node-id> (a living node's inbox), spawn:<kind> (birth a fresh node per delivery), or human (the humanloop inbox)."});if(!D&&u!==null)throw new a({error:"sink_unused",message:`on_output "${s}" never delivers stdout \u2014 a sink would be dead config`,field:"sink",next:'Drop the sink, or use on_output "always"/"on-change" if delivery is the point.'});const Q=u!==null?await On(u):"{}";if(u?.startsWith("spawn:")&&(gn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const w=await bn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes});if(!w.allowed)throw new c(w.reason==="scope_missing"?403:401,w.reason==="scope_missing"?"scope_missing":"grant_revoked",w.message)}const y=T(e.principal)??"app:terminal",b=e.principal.class==="broker"?e.principal.node:e.principal.class==="cron"?null:typeof n.created_by=="string"&&n.created_by!==""?n.created_by:null;if(e.principal.class==="broker"&&n.created_by!=null&&n.created_by!==b)throw new c(403,"permission_error","a broker may only arm a cron as its own node");const m=b===null?null:await _(b);if(m!==null){if(m.grantee!==y)throw new a({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});Z(m,"crtr:schedule"),u?.startsWith("spawn:")&&Z(m,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new c(400,"invalid_request","isolated must be a boolean");if(m?.isolated&&n.isolated===!1)throw new c(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const W=m?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(W&&n.isolated===!1)throw new c(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const N=W||n.isolated===!0,g=await Pn(e,y,m),j=nn(),S=N?an(y,j):null;if(S!==null&&n.cwd_is_default!==!0){const w=en(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?z(n.cwd):S);if(w===".."||w.startsWith(`..${G}`)||w.startsWith(G))throw new c(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const V=S??await An(n,y,b,jn(n),N,{sub:g.sub,grantId:g.grant_id,scopes:g.scopes}),f=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(f!==null&&await _(f)!==null&&(await _(f)).grantee!==y)throw new a({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(f!==null&&await _(f)===null)throw new a({error:"anchor_not_found",message:`anchor node ${f} does not exist on the canvas`,field:"anchor_node",next:"Name a live node id, or drop the anchor \u2014 an unanchored cron outlives every node."});const B=n.cancel_on_wake===!0;if(B&&f===null)throw new a({error:"cancel_on_wake_needs_anchor",message:"cancel_on_wake couples the cron to a node's dormancy \u2014 it requires an anchor node",field:"anchor_node",next:"Pass anchor_node with the node whose wake should cancel this cron."});const $=n.run_timeout_s??300;if(typeof $!="number"||!Number.isInteger($)||$<=0)throw new a({error:"bad_run_timeout",message:`run_timeout_s must be a positive integer (got ${String(n.run_timeout_s)})`,field:"run_timeout_s",next:"Send the timeout in whole seconds, e.g. 300."});const A=typeof n.profile=="string"&&n.profile!==""?(await Sn(e,n.profile)).profileId:null,x=C(n.scope,"scope",["profile","global"],A!==null?"profile":"global");if(x==="profile"&&A===null)throw new a({error:"scope_needs_profile",message:'scope "profile" names the profile the cron lists and cancels within \u2014 it requires a profile',field:"profile",next:'Send a profile, or use scope "global" for a canvas-home-wide cron.'});return await rn({cron_id:j,name:r,created_by:b,grantee:y,sub:g.sub,grant_id:g.grant_id,scopes:g.scopes,origin_run_id:g.origin_run_id,isolated:N,command:o,fire_at:i,recur:l,tz:typeof n.tz=="string"?n.tz:null,expires_at:d,anchor_node:f,cancel_on_wake:B,cwd:V,env_json:p,profile:A,scope:x,run_timeout_s:$,overlap:h,on_output:s,sink:Q,tier:q}),{status:201,body:k(await v(j),null)}}t(Tn,"handleArm");async function En(e){const n=U(e),r=e.query.get("created_by");if(r==="")throw new c(400,"invalid_request","created_by must be nonempty when present");const o=await $n((n===null?await P({all:!0}):[...await P({profile:n}),...await P({global:!0})].sort((i,l)=>i.fire_at<l.fire_at?-1:i.fire_at>l.fire_at?1:0)).filter(i=>r===null||i.created_by===r),async i=>await J(e,i));return{status:200,body:await Cn(o,async i=>k(i,await R(i)))}}t(En,"handleList");async function R(e){return(await F(e.cron_id))[0]??null}t(R,"lastRunOf");async function O(e){const n=e.params.cronId,r=await v(n);if(r===null||!await J(e,r))throw new c(404,"cron_not_found",`no cron ${n}`);if(!Y(r,U(e)))throw new c(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(O,"requireCron");function I(e,n){const r=K(e);if(r!==null&&n.grantee!==r)throw new c(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(I,"requireOwnCron");async function Un(e){const n=await O(e),r=await F(n.cron_id);return{status:200,body:{cron:k(n,r[0]??null),runs:r.map(H)}}}t(Un,"handleShow");async function Jn(e){const n=I(e,await O(e));await L(n.cron_id,"paused");const r=await v(n.cron_id);return{status:200,body:k(r,await R(r))}}t(Jn,"handlePause");async function Rn(e){const n=I(e,await O(e));await L(n.cron_id,"active");const r=await v(n.cron_id);return{status:200,body:k(r,await R(r))}}t(Rn,"handleResume");async function Dn(e){const n=I(e,await O(e));if(n.run_state==="running")throw new c(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await hn(n,{outOfBand:!0});if(r===null)throw new c(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:H(r)}}t(Dn,"handleRun");async function Wn(e){const n=new Date().toISOString();return{status:200,body:{unparked:await tn(n,K(e)),at:n}}}t(Wn,"handlePoke");function Bn(e,n){const r=e.principal;if(r.class==="cron")return r.cron===n.cron_id?null:{kind:"canceled",by:`cron ${r.cron}`};if(r.class==="broker"){const i=on(n.sink);return i?.kind==="node"&&i.node===r.node?null:{kind:"canceled",by:`node ${r.node}`}}const o=T(r);return{kind:"canceled",by:o===null||o==="app:terminal"?"the person":o}}t(Bn,"cancelNotice");async function xn(e){const n=e.params.cronId,r=await v(n);if(r!==null&&!await J(e,r))throw new c(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!Y(r,U(e)))throw new c(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);r!==null&&I(e,r);const o=e.query.get("run_id"),i=e.principal.class==="cron"?o===e.principal.execution&&n===e.principal.cron:!0,l=o!==null&&i&&mn(n,o);return r!==null&&await kn(r,l?null:Bn(e,r),{preserveRunningProcess:l}),{status:204}}t(xn,"handleCancel");const ce=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:Tn},{method:"GET",pattern:"/v1/crons",handler:En},{method:"GET",pattern:"/v1/crons/:cronId",handler:Un},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:Jn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:Rn},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:Wn},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Dn},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:xn}];export{ce as cronRoutes};
|
|
1
|
+
var X=Object.defineProperty;var t=(e,n)=>X(e,"name",{value:n,configurable:!0});import{randomUUID as nn}from"node:crypto";import{relative as en,resolve as z,sep as G}from"node:path";import{ApiError as c}from"../../../api/errors.js";import{armCron as rn,parseCronSink as on,getCron as v,listCrons as P,listCronRuns as F,pokeHeldCrons as tn,setCronState as L}from"../../../core/canvas/crons.js";import{getNode as _}from"../../../core/canvas/canvas.js";import{appSpace as sn,cronFolder as an}from"../../../core/layout.js";import{appProcessesSandboxed as cn,cwdIsMounted as ln}from"../../../core/spaces/process.js";import{activeGrant as dn,activeGrantForApp as un,grantFor as pn,terminalGrant as fn}from"../../../core/grants/mirror.js";import{readConfig as wn}from"../../../core/config.js";import{assertScope as Z}from"../../../core/scopes.js";import{InputError as a}from"../../../core/io.js";import{nextSlotAfter as _n}from"../../../core/wake.js";import{executeCron as hn,markSelfCanceledCronRun as mn}from"../../cron-run.js";import{toCronDTO as k,toCronRunDTO as H}from"../map.js";import{appOf as T,assertCeiling as gn,canReadNode as yn}from"../router.js";import{checkCronBirth as bn,checkCronFire as vn}from"../../cron/authority.js";import{endCron as kn}from"../../cron/ended.js";import{requireUsableProfile as Sn}from"./profiles.js";import{filterAsync as $n,mapAsync as Cn}from"../../../core/async-array.js";function E(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new a({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}t(E,"requireString");function C(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new a({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(C,"requireEnum");async function On(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new a({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(await _(n)===null)throw new a({error:"sink_node_not_found",message:`sink node ${n} does not exist on the canvas`,field:"sink",next:"Name a live node id, or use spawn:<kind> for a delivery that outlives any one node."});return JSON.stringify({node:n})}if(e.startsWith("spawn:")){const n=e.slice(6).trim();if(n==="")throw new a({error:"bad_sink",message:"spawn sink requires a node kind",field:"sink",next:'Use sink "spawn:<kind>" (e.g. spawn:general).'});return JSON.stringify({spawn:n})}throw new a({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(On,"parseSinkSpec");const In=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function M(e,n){if(!In.test(e)||Number.isNaN(Date.parse(e)))throw new a({error:`bad_${n}`,message:`"${e}" is not a UTC ISO instant`,field:n,next:'Send a resolved UTC instant like "2026-06-07T09:00:00.000Z" (the client resolves the time grammar).'});return e}t(M,"requireUtcIso");function qn(e){try{_n(e,new Date)}catch(n){throw new a({error:"bad_recur",message:`recur is not advanceable: ${n instanceof Error?n.message:String(n)}`,field:"recur",next:'Send recur JSON the daemon can advance: {"every":"6h"} or {"cron":"0 9 * * *","tz":"America/New_York"}.'})}return e}t(qn,"requireRecur");function Nn(e){let n;try{n=JSON.parse(e)}catch{throw new a({error:"bad_env_json",message:"env_json is not valid JSON",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'})}if(typeof n!="object"||n===null||Array.isArray(n))throw new a({error:"bad_env_json",message:"env_json must be a JSON object",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'});for(const[r,o]of Object.entries(n))if(typeof o!="string")throw new a({error:"bad_env_json",message:`env_json value for "${r}" is not a string`,field:"env_json",next:"Every env value must be a string."});return e}t(Nn,"requireEnvJson");function U(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(U,"callerProfile");function Y(e,n){return n===null||e.scope==="global"||e.profile===n}t(Y,"inScope");function K(e){return e.principal.class==="person"?null:T(e.principal)}t(K,"callerApp");async function J(e,n){return await yn(e.principal,n.grantee,n.created_by??void 0)}t(J,"visibleTo");function jn(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(jn,"anchorNodeOf");async function An(e,n,r,o,i,l){const d=typeof e.cwd=="string"&&e.cwd.trim()!==""?z(e.cwd):null;if(!cn()){if(d===null)throw new a({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return d}const h=o!==null&&await _(o)!==null?o:void 0,u=t(async q=>await ln(n,q,h,i,void 0,l),"reaches");if(d!==null&&await u(d))return d;const s=sn(n).files;if(d!==null&&e.cwd_is_default!==!0)throw new c(400,"invalid_request",`cwd ${d} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${s}`,{cwd:d,default:s},void 0,{param:"cwd"});const p=r===null?null:(await _(r))?.cwd??null;return p!==null&&await u(p)?p:s}t(An,"resolveArmedCwd");async function Pn(e,n,r){const o=e.principal;if(o.class==="person"){const s=await fn();return{sub:s?.sub??"user:local",grant_id:s?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const s=await un(n);if(s===null)throw new c(401,"grant_revoked",`no active grant for ${n}`);const p=e.ceiling??o.scopes;return{sub:s.sub,grant_id:s.id,scopes:p===null?null:[...p],origin_run_id:null}}if(o.class==="cron"){const s=await vn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!s.allowed)throw new c(s.reason==="scope_missing"?403:401,s.reason==="scope_missing"?"scope_missing":"grant_revoked",s.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?null:[...o.scopes],origin_run_id:o.origin_run}}if(o.class!=="broker")throw new c(401,"unauthorized","this caller cannot arm a cron");const i=r??await _(o.node),l=i?.grant_id??o.grant,d=i?.sub??o.sub,h=l!=null?await dn(l):await pn(n,d);if(h===null){if(n==="app:terminal"&&!wn("user").runtime?.issuer)return{sub:d,grant_id:l??null,scopes:i?.scopes??o.scopes,origin_run_id:i?.run_id??o.run};throw new c(401,"grant_revoked","the node grant is not active")}const u=i?.scopes??o.scopes;return{sub:h.sub,grant_id:h.id,scopes:u===null?null:[...u],origin_run_id:i?.run_id??o.run}}t(Pn,"armPrincipal");async function Tn(e){const n=e.body??{},r=E(n,"name"),o=E(n,"command"),i=M(E(n,"fire_at"),"fire_at"),l=typeof n.recur=="string"&&n.recur!==""?qn(n.recur):null,d=typeof n.expires_at=="string"&&n.expires_at!==""?M(n.expires_at,"expires_at"):null,h=typeof n.env_json=="string"&&n.env_json!==""?Nn(n.env_json):null,u=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,s=C(n.on_output,"on_output",["silent","on-failure","always","on-change"],u?.startsWith("node:")?"on-change":"on-failure"),p=C(n.overlap,"overlap",["skip","queue","replace"],"skip"),q=C(n.tier,"tier",["normal","urgent","critical"],"normal"),D=s==="always"||s==="on-change";if(D&&u===null)throw new a({error:"sink_required",message:`on_output "${s}" delivers stdout every qualifying run \u2014 it needs a sink`,field:"sink",next:"Pass sink node:<node-id> (a living node's inbox), spawn:<kind> (birth a fresh node per delivery), or human (the humanloop inbox)."});if(!D&&u!==null)throw new a({error:"sink_unused",message:`on_output "${s}" never delivers stdout \u2014 a sink would be dead config`,field:"sink",next:'Drop the sink, or use on_output "always"/"on-change" if delivery is the point.'});const Q=u!==null?await On(u):"{}";if(u?.startsWith("spawn:")&&(gn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const w=await bn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes});if(!w.allowed)throw new c(w.reason==="scope_missing"?403:401,w.reason==="scope_missing"?"scope_missing":"grant_revoked",w.message)}const y=T(e.principal)??"app:terminal",b=e.principal.class==="broker"?e.principal.node:e.principal.class==="cron"?null:typeof n.created_by=="string"&&n.created_by!==""?n.created_by:null;if(e.principal.class==="broker"&&n.created_by!=null&&n.created_by!==b)throw new c(403,"permission_error","a broker may only arm a cron as its own node");const m=b===null?null:await _(b);if(m!==null){if(m.grantee!==y)throw new a({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});Z(m,"crtr:schedule"),u?.startsWith("spawn:")&&Z(m,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new c(400,"invalid_request","isolated must be a boolean");if(m?.isolated&&n.isolated===!1)throw new c(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const W=m?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(W&&n.isolated===!1)throw new c(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const N=W||n.isolated===!0,g=await Pn(e,y,m),j=nn(),S=N?an(y,j):null;if(S!==null&&n.cwd_is_default!==!0){const w=en(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?z(n.cwd):S);if(w===".."||w.startsWith(`..${G}`)||w.startsWith(G))throw new c(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const V=S??await An(n,y,b,jn(n),N,{sub:g.sub,grantId:g.grant_id,scopes:g.scopes}),f=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(f!==null&&await _(f)!==null&&(await _(f)).grantee!==y)throw new a({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(f!==null&&await _(f)===null)throw new a({error:"anchor_not_found",message:`anchor node ${f} does not exist on the canvas`,field:"anchor_node",next:"Name a live node id, or drop the anchor \u2014 an unanchored cron outlives every node."});const B=n.cancel_on_wake===!0;if(B&&f===null)throw new a({error:"cancel_on_wake_needs_anchor",message:"cancel_on_wake couples the cron to a node's dormancy \u2014 it requires an anchor node",field:"anchor_node",next:"Pass anchor_node with the node whose wake should cancel this cron."});const $=n.run_timeout_s??300;if(typeof $!="number"||!Number.isInteger($)||$<=0)throw new a({error:"bad_run_timeout",message:`run_timeout_s must be a positive integer (got ${String(n.run_timeout_s)})`,field:"run_timeout_s",next:"Send the timeout in whole seconds, e.g. 300."});const A=typeof n.profile=="string"&&n.profile!==""?(await Sn(e,n.profile)).profileId:null,x=C(n.scope,"scope",["profile","global"],A!==null?"profile":"global");if(x==="profile"&&A===null)throw new a({error:"scope_needs_profile",message:'scope "profile" names the profile the cron lists and cancels within \u2014 it requires a profile',field:"profile",next:'Send a profile, or use scope "global" for a canvas-home-wide cron.'});return await rn({cron_id:j,name:r,created_by:b,grantee:y,sub:g.sub,grant_id:g.grant_id,scopes:g.scopes,origin_run_id:g.origin_run_id,isolated:N,command:o,fire_at:i,recur:l,tz:typeof n.tz=="string"?n.tz:null,expires_at:d,anchor_node:f,cancel_on_wake:B,cwd:V,env_json:h,profile:A,scope:x,run_timeout_s:$,overlap:p,on_output:s,sink:Q,tier:q}),{status:201,body:k(await v(j),null)}}t(Tn,"handleArm");async function En(e){const n=U(e),r=e.query.get("created_by");if(r==="")throw new c(400,"invalid_request","created_by must be nonempty when present");const o=await $n((n===null?await P({all:!0}):[...await P({profile:n}),...await P({global:!0})].sort((i,l)=>i.fire_at<l.fire_at?-1:i.fire_at>l.fire_at?1:0)).filter(i=>r===null||i.created_by===r),async i=>await J(e,i));return{status:200,body:await Cn(o,async i=>k(i,await R(i)))}}t(En,"handleList");async function R(e){return(await F(e.cron_id))[0]??null}t(R,"lastRunOf");async function O(e){const n=e.params.cronId,r=await v(n);if(r===null||!await J(e,r))throw new c(404,"cron_not_found",`no cron ${n}`);if(!Y(r,U(e)))throw new c(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(O,"requireCron");function I(e,n){const r=K(e);if(r!==null&&n.grantee!==r)throw new c(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(I,"requireOwnCron");async function Un(e){const n=await O(e),r=await F(n.cron_id);return{status:200,body:{cron:k(n,r[0]??null),runs:r.map(H)}}}t(Un,"handleShow");async function Jn(e){const n=I(e,await O(e));await L(n.cron_id,"paused");const r=await v(n.cron_id);return{status:200,body:k(r,await R(r))}}t(Jn,"handlePause");async function Rn(e){const n=I(e,await O(e));await L(n.cron_id,"active");const r=await v(n.cron_id);return{status:200,body:k(r,await R(r))}}t(Rn,"handleResume");async function Dn(e){const n=I(e,await O(e));if(n.run_state==="running")throw new c(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await hn(n,{outOfBand:!0});if(r===null)throw new c(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:H(r)}}t(Dn,"handleRun");async function Wn(e){const n=new Date().toISOString();return{status:200,body:{unparked:await tn(n,K(e)),at:n}}}t(Wn,"handlePoke");function Bn(e,n){const r=e.principal;if(r.class==="cron")return r.cron===n.cron_id?null:{kind:"canceled",by:`cron ${r.cron}`};if(r.class==="broker"){const i=on(n.sink);return i?.kind==="node"&&i.node===r.node?null:{kind:"canceled",by:`node ${r.node}`}}const o=T(r);return{kind:"canceled",by:o===null||o==="app:terminal"?"the person":o}}t(Bn,"cancelNotice");async function xn(e){const n=e.params.cronId,r=await v(n);if(r!==null&&!await J(e,r))throw new c(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!Y(r,U(e)))throw new c(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);r!==null&&I(e,r);const o=e.query.get("run_id"),i=e.principal.class==="cron"?o===e.principal.execution&&n===e.principal.cron:!0,l=o!==null&&i&&mn(n,o);return r!==null&&await kn(r,l?null:Bn(e,r),{preserveRunningProcess:l}),{status:204}}t(xn,"handleCancel");const ce=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:Tn},{method:"GET",pattern:"/v1/crons",handler:En},{method:"GET",pattern:"/v1/crons/:cronId",handler:Un},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:Jn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:Rn},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:Wn},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Dn},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:xn}];export{ce as cronRoutes};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var oe=Object.defineProperty;var u=(n,e)=>oe(n,"name",{value:e,configurable:!0});import{ApiError as _}from"../../../api/errors.js";import{DOC_KINDS as P,DOC_UNSET_FIELDS as q}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as H}from"../../../api/dto/objects.js";import{withCanvasWrite as k}from"../../../core/canvas/db.js";import{CrtrError as re}from"../../../core/errors.js";import{emitEvent as ie}from"../../../core/events/emit.js";import{canChange as U,spaceOf as ae}from"../../../core/graph/access.js";import{get as O}from"../../../core/graph/bodies.js";import{deleteDocument as se,documentRecord as F,editDocument as ce,heads as x,history as de,linksIn as V,moveDocument as ue,readBase as fe,setDocumentRecord as le,withCurrentNames as me,writeDocument as pe}from"../../../core/graph/documents.js";import{resolveName as K}from"../../../core/graph/names.js";import{getById as L}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as we}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as he}from"../../../core/graph/repo-sync/repos.js";import{lintRules as ye,rulesOf as ve}from"../../../core/graph/rules.js";import{unifiedDiff as ge}from"../../../core/graph/diff.js";import{ownerFromFlag as M,readerFor as E,resolveRef as X}from"../reader.js";import{filterAsync as _e,flatMapAsync as be}from"../../../core/async-array.js";function v(n,e,t,o,i={}){return new _(n,e,t,{...i,error:e,message:t,next:o})}u(v,"refuse");const b=u((n,e)=>new _(400,"invalid_request",n,e),"invalid");function R(n){const e=n.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw b("the request body must be a JSON object");return e}u(R,"bodyOf");const C=u(async n=>async e=>await he(n,e),"reposFnFor");function S(n,e){if(n instanceof re){const t=n.details;if(n.code==="read_first")throw v(409,"read_first",n.message,`Read it first with \`crtr canvas read ${e?.id??"<ref>"}\`, then rerun the edit.`,t??{});if(n.code==="conflict")throw new _(409,"name_taken",n.message,t);if(n.code==="forbidden")throw new _(403,"forbidden",n.message,t);if(n.code==="scope_missing")throw v(403,"scope_missing",n.message,`Launch the run with \`${String(t?.scope)}\` in its scopes list.`,t??{});if(t?.rule==="repo-public-only")throw v(422,"repo_documents_are_public",n.message,'Own it as `user` and add a rule with `if: {"repo": "<handle>"}` so it loads only in that repo.')}throw n}u(S,"mapGraphError");async function W(n,e,t){const o=await X(n,e,decodeURIComponent(t));if("remote"in o)throw v(404,"not_found",z(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new _(404,"not_found",`${o.deleted.name} is deleted`,{id:o.deleted.id,deleted:!0});if(o.type!=="document")throw b(`${t} is a ${o.type}, not a document`);return o}u(W,"refDocument");function z(n){return`no object named ${n} on this runtime; it may be on another runtime, or the name may be misspelled`}u(z,"remoteMessage");async function ke(n,e){const t=e.owner===null?null:await L(n,e.owner);return t?t.type==="node"?t.id:t.name:""}u(ke,"ownerHandle");async function j(n,e){const t=await ke(n,e);return t?`${t}/${e.name}`:e.name}u(j,"canonicalName");async function Ee(n,e){const t=e.owner===null?null:await L(n,e.owner),o=t?{id:t.id,type:t.type,handle:t.type==="node"?t.id:t.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:o,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at}}u(Ee,"objectDTO");async function $e(n,e){const t=e.owner===null?null:await L(n,e.owner);return t?.type==="repo"?t:null}u($e,"repoOwner");async function N(n,e,t,o){const i=[...new Map(await be(t,async f=>{const a=await $e(n,f);return a?[[a.id,a]]:[]})).values()],s=e.class==="node"?e.cwd:o,r=e.class==="node"?[...e.checkouts,...e.projectCheckouts].map(f=>f.root):[];return{repos:i,dirs:[...new Set([...s!==void 0?[s]:[],...r])]}}u(N,"repoChange");async function T(n){for(const e of n.repos)try{await we(e,n.dirs)}catch(t){ie({level:"warn",event:"repo_sync.failed",outcome:"failed",error:t,fields:{repo:e.name,step:"export",reason:t instanceof Error?t.message:String(t)}})}}u(T,"syncChanged");const G=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function Ae(n,e){if(n===void 0)return;if(!Array.isArray(n)){e.push("`rules` must be a list");return}const t=n.map(i=>{if(typeof i!="object"||i===null||Array.isArray(i))return i;const{match_frontmatter:s,...r}=i;return"matchFrontmatter"in r?{...r,match_frontmatter:r.matchFrontmatter}:s===void 0?r:{...r,matchFrontmatter:s}}),o=ye(t);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return t}u(Ae,"rulesFromDTO");function Y(n,e){const t={},o=u(a=>{const c=n[a];if(c!==void 0){if(typeof c!="string"){e.push(`\`${a}\` must be a string`);return}return c}},"str"),i=o("preview");i!==void 0&&(i.trim()===""||/[\r\n]/.test(i)?e.push("`preview` must be one non-empty line"):t.preview=i);const s=o("summary");s!==void 0&&(t.summary=s);const r=o("why_it_exists");if(r!==void 0&&(t.whyItExists=r),n.privacy!==void 0&&(H.includes(n.privacy)?t.privacy=n.privacy:e.push(`\`privacy\` must be one of ${H.join(", ")}`)),n.unlisted!==void 0&&(typeof n.unlisted!="boolean"?e.push("`unlisted` must be a boolean"):t.unlisted=n.unlisted),n.lint_ignore!==void 0){const a=n.lint_ignore;!Array.isArray(a)||a.some(c=>typeof c!="string")?e.push("`lint_ignore` must be a list of strings"):t.lintIgnore=a}if(n.extensions!==void 0){const a=n.extensions;typeof a!="object"||a===null||Array.isArray(a)||Object.values(a).some(c=>typeof c!="object"||c===null||Array.isArray(c))?e.push("`extensions` must map each plugin to an object of fields"):t.extensions=a}const f=Ae(n.rules,e);return f!==void 0&&(t.rules=f),t}u(Y,"fieldsFrom");function Z(n){if(n.length>0)throw b(n.join("; "),{errors:n})}u(Z,"failOn");async function J(n,e,t,o){const i=await _e(V(t),async s=>"remote"in await K(n,e,s));return{...o.length>0?{unresolved_links:o}:{},...i.length>0?{remote_links:i}:{}}}u(J,"linkReport");async function Oe(n){const e=R(n),t=[],o=e.name;(typeof o!="string"||!G.test(o))&&t.push("`name` must be segments of letters, digits, `_` or `-` joined by `/`"),typeof e.body!="string"&&t.push("`body` must be a string"),typeof e.preview!="string"&&t.push("`preview` is required");const i=e.kind??"knowledge";P.includes(i)||t.push(i==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${P.join(", ")}`);const s=Y(e,t);e.cwd!==void 0&&typeof e.cwd!="string"&&t.push("`cwd` must be a string"),Z(t);const r=e,{dto:f,change:a}=await k(async c=>{const w=await E(c,n),d=await M(c,w,r.owner,r.cwd);if(!await U(c,w,d))throw new _(404,"not_found",`no owner ${d.name} you can write to`);try{const{object:p,unresolved:l}=await pe(c,w,{...s,owner:d,name:r.name,kind:i,preview:s.preview,privacy:s.privacy??"public"},r.body,await C(c));return{dto:{id:p.id,name:await j(c,p),...await J(c,w,r.body,l)},change:await N(c,w,[p],r.cwd)}}catch(p){S(p)}});return await T(a),f}u(Oe,"write");function je(n,e){let t=n;e.forEach(({old:o,new:i},s)=>{const r=o===""?-1:t.indexOf(o);if(r<0)throw v(422,"replace_no_match",`replace[${s}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:s});if(t.indexOf(o,r+1)>=0)throw v(422,"replace_ambiguous",`replace[${s}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:s});t=t.slice(0,r)+i+t.slice(r+o.length)})}u(je,"checkReplacements");async function De(n){const e=R(n),t=[],o=e.body!==void 0,i=e.replace!==void 0;o&&i&&t.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&t.push("`body` must be a string");const s=e.replace;i&&(!Array.isArray(s)||s.length===0||s.some(d=>typeof d!="object"||d===null||typeof d.old!="string"||typeof d.new!="string"))&&t.push("`replace` must be a non-empty list of {old, new} strings"),(typeof e.rationale!="string"||e.rationale.trim()==="")&&t.push("`rationale` is required"),e.verbatim!==void 0&&typeof e.verbatim!="boolean"&&t.push("`verbatim` must be a boolean"),e.base!==void 0&&(typeof e.base!="string"||e.base==="")&&t.push("`base` must be a non-empty heads key"),e.base!==void 0&&!o&&!i&&t.push("`base` applies only to a `body` or `replace` edit");const r=Y(e,t),f=e.unset??[];if(!Array.isArray(f)||f.some(d=>!q.includes(d)))t.push(`\`unset\` takes only ${q.join(", ")}`);else for(const d of f)d==="summary"&&(r.summary=null),d==="why_it_exists"&&(r.whyItExists=null),d==="unlisted"&&(r.unlisted=!1),d==="lint_ignore"&&(r.lintIgnore=null),d==="extensions"&&(r.extensions=null),d==="rules"&&(r.rules=[]);t.length===0&&!o&&!i&&Object.keys(r).length===0&&t.push("nothing to change: give `body`, `replace`, or a field"),Z(t);const a=e,{dto:c,synced:w}=await k(async d=>{const p=await E(d,n),l=await W(d,p,n.params.ref),g=await j(d,l),A=await N(d,p,[l]);try{if(!o&&!i)return await le(d,p,l,r),{dto:{id:l.id,name:g,revision:(await F(d,l)).revision},synced:A};const y=a.base??await fe(d,p,l);if(i&&y!==null){const ne=await F(d,l),B=await x(d,l);ne.heads_key===y&&B.length===1&&je(await me(d,l,O(B[0].body_hash)),a.replace)}const D=a.verbatim===!0,m=o?{body:a.body,verbatim:D}:{replace:a.replace,verbatim:D},h=await ce(d,p,l,m,a.rationale,y,r,await C(d));if("stale"in h)throw v(409,"stale_revision",`${g} changed after your read; it is now at revision ${h.stale.revision}`,`Read it again with \`crtr canvas read ${g}\`, then redo your change on the current text.`,{stale:h.stale});const I=O((await x(d,l))[0].body_hash);return{dto:{id:l.id,name:g,revision:h.revision,...await J(d,p,I,h.unresolved)},synced:A}}catch(y){S(y,l)}});return await T(w),c}u(De,"edit");async function Re(n){const e=R(n);if(e.new_name===void 0&&e.owner===void 0)throw b("give a new name, an owner, or both");if(e.new_name!==void 0&&(typeof e.new_name!="string"||!G.test(e.new_name)))throw b("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");const{dto:t,change:o}=await k(async i=>{const s=await E(i,n),r=await W(i,s,n.params.ref),f=e.owner===void 0?void 0:await M(i,s,e.owner,e.cwd);try{const a=await ue(i,s,r,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}},await C(i));return{dto:{id:a.id,name:await j(i,a)},change:await N(i,s,[r,a],e.cwd)}}catch(a){S(a,r)}});return await T(o),t}u(Re,"move");async function Ce(n){const{dto:e,change:t}=await k(async o=>{const i=await E(o,n),s=await W(o,i,n.params.ref),r=await j(o,s),f=await N(o,i,[s]);try{await se(o,i,s,await C(o))}catch(a){S(a,s)}return{dto:{id:s.id,name:r},change:f}});return await T(t),e}u(Ce,"remove");async function Se(n){const e=n.query.get("diff"),t=e===null?void 0:Number(e);if(t!==void 0&&!(Number.isInteger(t)&&t>0))throw b("`diff` must be a revision number");return await k(async o=>{const i=await E(o,n),s=await X(o,i,decodeURIComponent(n.params.ref));if("remote"in s)throw v(404,"not_found",z(s.remote),"Check the name with `crtr canvas search`.");const r="deleted"in s?s.deleted:s;if(r.type!=="document")throw b(`${r.name} is a ${r.type}, not a document`);const f=await de(o,i,r),a={object:await Ee(o,r),revisions:f.map(c=>({rev_id:c.rev_id,number:c.number,author_id:c.author,rationale:c.rationale,verbatim:c.verbatim,created_at:c.created_at,parents:c.parents}))};if(t!==void 0){const c=f.find(d=>d.number===t);if(!c)throw new _(404,"not_found",`${r.name} has no revision ${t}`);const w=c.parents[0]===void 0?void 0:f.find(d=>d.rev_id===c.parents[0]);a.diff=ge(w?O(w.body_hash):"",O(c.body_hash))}return a})}u(Se,"revisionHistory");const Q=1e3,ee=1e3,te=3e3,Ne=/^When\b.+\bbecause\b.+/,Te=u(n=>{const e=n.trim();return e===""?0:e.split(/\s+/).length},"words");function Ie(n,e){if(n.some(o=>o.on==="boot"&&o.deliver==="content")&&e>Q)return`body is ${e} words, over the ${Q}-word cap for a boot rule at content (the whole body enters every node's context). Keep the core here and move separate subjects into [[linked]] documents.`;const t=[...new Set(n.filter(o=>o.on!=="boot"&&o.deliver==="content").map(o=>o.on))];return t.length>0&&e>ee?`body is ${e} words, over the ${ee}-word cap for a content rule on ${t.join("/")}. Keep the core here and move separate subjects into [[linked]] documents.`:n.some(o=>o.on==="boot"&&o.deliver==="preview")&&e>te?`body is ${e} words, over the ${te}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(Ie,"lengthFinding");async function Fe(n){const e=R(n);return await k(async t=>{const o=await E(t,n),i=e.owner===void 0?null:await M(t,o,e.owner,e.cwd),s=await t.prepare("SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND home_machine IS NULL AND kind != 'report' ORDER BY name").all(),r=[],f=new Map;for(const a of s){if(i&&a.owner!==i.id||!await U(t,o,a))continue;const c=await F(t,a),w=new Set(c.lint_ignore??[]),d=await j(t,a),p=u((m,h,I)=>{w.has(m)||r.push({name:d,severity:h,rule:m,message:I})},"add"),l=await ve(t,a),g=(await x(t,a))[0],A=g?O(g.body_hash):"",y=Ie(l,Te(A));y&&p("length","error",y),l.some(m=>m.deliver==="preview")&&!Ne.test(a.preview??"")&&p("preview-shape","error",`a rule delivers this document at preview, so the preview must read "When <situation in the reader's work>, read this because <what the reader gains>"`);const D=new Set((await t.prepare("SELECT written_as FROM edges WHERE type = 'links-to' AND src = ?").all(a.id)).map(m=>m.written_as));for(const m of V(A)){if(D.has(m))continue;const h=await K(t,o,m);"remote"in h||"object"in h||"deleted"in h||p("links","warning",`[[${m}]] names no object in your view`)}if(l.some(m=>m.on==="workspace-open"&&m.deliver==="content")){const m=await ae(t,a);m.type==="repo"&&!w.has("workspace-open")&&f.set(m.name,[...f.get(m.name)??[],d])}}for(const[a,c]of f)if(!(c.length<2))for(const w of c)r.push({name:w,severity:"warning",rule:"workspace-open",message:`repo ${a} has ${c.length} documents delivered at content on workspace-open (${c.join(", ")}); keep one and link the rest from it`});return{findings:r}})}u(Fe,"lint");const $=u((n,e=200)=>async t=>({status:e,body:await n(t)}),"ok"),et=[{method:"POST",pattern:"/v1/docs",handler:$(Oe,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:$(De)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:$(Re)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:$(Ce)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:$(Se)},{method:"POST",pattern:"/v1/docs/lint",handler:$(Fe)}];export{j as canonicalName,et as docRoutes,N as repoChange,T as syncChanged};
|
|
1
|
+
var oe=Object.defineProperty;var u=(n,e)=>oe(n,"name",{value:e,configurable:!0});import{ApiError as _}from"../../../api/errors.js";import{DOC_KINDS as q,DOC_UNSET_FIELDS as H}from"../../../api/dto/docs.js";import{PRIVACY_LEVELS_DTO as U}from"../../../api/dto/objects.js";import{withCanvasWrite as k}from"../../../core/canvas/db.js";import{CrtrError as re}from"../../../core/errors.js";import{emitEvent as ie}from"../../../core/events/emit.js";import{canChange as V,spaceOf as ae}from"../../../core/graph/access.js";import{get as O}from"../../../core/graph/bodies.js";import{deleteDocument as se,documentRecord as F,editDocument as ce,heads as x,history as de,linksIn as K,moveDocument as ue,readBase as fe,setDocumentRecord as le,withCurrentNames as me,writeDocument as pe}from"../../../core/graph/documents.js";import{resolveName as X}from"../../../core/graph/names.js";import{getById as L}from"../../../core/graph/objects.js";import{noteRepoDocumentsChanged as we}from"../../../core/graph/repo-sync/index.js";import{checkoutsForCwd as he}from"../../../core/graph/repo-sync/repos.js";import{lintRules as ye,rulesOf as ve}from"../../../core/graph/rules.js";import{unifiedDiff as ge}from"../../../core/graph/diff.js";import{ownerFromFlag as M,readerFor as E,resolveRef as z}from"../reader.js";import{filterAsync as _e,flatMapAsync as be}from"../../../core/async-array.js";function y(n,e,t,o,i={}){return new _(n,e,t,{...i,error:e,message:t,next:o})}u(y,"refuse");const b=u((n,e)=>new _(400,"invalid_request",n,e),"invalid");function R(n){const e=n.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw b("the request body must be a JSON object");return e}u(R,"bodyOf");const C=u(async n=>async e=>await he(n,e),"reposFnFor");function S(n,e){if(n instanceof re){const t=n.details;if(n.code==="read_first")throw y(409,"read_first",n.message,`Read it first with \`crtr canvas read ${e?.id??"<ref>"}\`, then rerun the edit.`,t??{});if(n.code==="conflict")throw new _(409,"name_taken",n.message,t);if(n.code==="forbidden")throw typeof t?.next=="string"?y(403,"forbidden",n.message,t.next,t):new _(403,"forbidden",n.message,t);if(n.code==="scope_missing")throw y(403,"scope_missing",n.message,`Launch the run with \`${String(t?.scope)}\` in its scopes list.`,t??{});if(t?.rule==="repo-public-only")throw y(422,"repo_documents_are_public",n.message,'Own it as `user` and add a rule with `if: {"repo": "<handle>"}` so it loads only in that repo.')}throw n}u(S,"mapGraphError");async function W(n,e,t){const o=await z(n,e,decodeURIComponent(t));if("remote"in o)throw y(404,"not_found",G(o.remote),"Check the name with `crtr canvas search`.");if("deleted"in o)throw new _(404,"not_found",`${o.deleted.name} is deleted`,{id:o.deleted.id,deleted:!0});if(o.type!=="document")throw b(`${t} is a ${o.type}, not a document`);return o}u(W,"refDocument");function G(n){return`no object named ${n} on this runtime; it may be on another runtime, or the name may be misspelled`}u(G,"remoteMessage");async function ke(n,e){const t=e.owner===null?null:await L(n,e.owner);return t?t.type==="node"?t.id:t.name:""}u(ke,"ownerHandle");async function j(n,e){const t=await ke(n,e);return t?`${t}/${e.name}`:e.name}u(j,"canonicalName");async function Ee(n,e){const t=e.owner===null?null:await L(n,e.owner),o=t?{id:t.id,type:t.type,handle:t.type==="node"?t.id:t.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:o,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at}}u(Ee,"objectDTO");async function $e(n,e){const t=e.owner===null?null:await L(n,e.owner);return t?.type==="repo"?t:null}u($e,"repoOwner");async function N(n,e,t,o){const i=[...new Map(await be(t,async f=>{const a=await $e(n,f);return a?[[a.id,a]]:[]})).values()],s=e.class==="node"?e.cwd:o,r=e.class==="node"?[...e.checkouts,...e.projectCheckouts].map(f=>f.root):[];return{repos:i,dirs:[...new Set([...s!==void 0?[s]:[],...r])]}}u(N,"repoChange");async function T(n){for(const e of n.repos)try{await we(e,n.dirs)}catch(t){ie({level:"warn",event:"repo_sync.failed",outcome:"failed",error:t,fields:{repo:e.name,step:"export",reason:t instanceof Error?t.message:String(t)}})}}u(T,"syncChanged");const Y=/^[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)*$/;function Ae(n,e){if(n===void 0)return;if(!Array.isArray(n)){e.push("`rules` must be a list");return}const t=n.map(i=>{if(typeof i!="object"||i===null||Array.isArray(i))return i;const{match_frontmatter:s,...r}=i;return"matchFrontmatter"in r?{...r,match_frontmatter:r.matchFrontmatter}:s===void 0?r:{...r,matchFrontmatter:s}}),o=ye(t);if(o){e.push(o.replaceAll("matchFrontmatter","match_frontmatter"));return}return t}u(Ae,"rulesFromDTO");function Z(n,e){const t={},o=u(a=>{const c=n[a];if(c!==void 0){if(typeof c!="string"){e.push(`\`${a}\` must be a string`);return}return c}},"str"),i=o("preview");i!==void 0&&(i.trim()===""||/[\r\n]/.test(i)?e.push("`preview` must be one non-empty line"):t.preview=i);const s=o("summary");s!==void 0&&(t.summary=s);const r=o("why_it_exists");if(r!==void 0&&(t.whyItExists=r),n.privacy!==void 0&&(U.includes(n.privacy)?t.privacy=n.privacy:e.push(`\`privacy\` must be one of ${U.join(", ")}`)),n.unlisted!==void 0&&(typeof n.unlisted!="boolean"?e.push("`unlisted` must be a boolean"):t.unlisted=n.unlisted),n.lint_ignore!==void 0){const a=n.lint_ignore;!Array.isArray(a)||a.some(c=>typeof c!="string")?e.push("`lint_ignore` must be a list of strings"):t.lintIgnore=a}if(n.extensions!==void 0){const a=n.extensions;typeof a!="object"||a===null||Array.isArray(a)||Object.values(a).some(c=>typeof c!="object"||c===null||Array.isArray(c))?e.push("`extensions` must map each plugin to an object of fields"):t.extensions=a}const f=Ae(n.rules,e);return f!==void 0&&(t.rules=f),t}u(Z,"fieldsFrom");function J(n){if(n.length>0)throw b(n.join("; "),{errors:n})}u(J,"failOn");async function Q(n,e,t,o){const i=await _e(K(t),async s=>"remote"in await X(n,e,s));return{...o.length>0?{unresolved_links:o}:{},...i.length>0?{remote_links:i}:{}}}u(Q,"linkReport");async function Oe(n){const e=R(n),t=[],o=e.name;(typeof o!="string"||!Y.test(o))&&t.push("`name` must be segments of letters, digits, `_` or `-` joined by `/`"),typeof e.body!="string"&&t.push("`body` must be a string"),typeof e.preview!="string"&&t.push("`preview` is required");const i=e.kind??"knowledge";q.includes(i)||t.push(i==="report"?"reports are written only by `crtr push`":`\`kind\` must be one of ${q.join(", ")}`);const s=Z(e,t);e.cwd!==void 0&&typeof e.cwd!="string"&&t.push("`cwd` must be a string"),J(t);const r=e,{dto:f,change:a}=await k(async c=>{const w=await E(c,n),d=await M(c,w,r.owner,r.cwd);if(!await V(c,w,d))throw new _(404,"not_found",`no owner ${d.name} you can write to`);try{const{object:p,unresolved:l}=await pe(c,w,{...s,owner:d,name:r.name,kind:i,preview:s.preview,privacy:s.privacy??"public"},r.body,await C(c));return{dto:{id:p.id,name:await j(c,p),...await Q(c,w,r.body,l)},change:await N(c,w,[p],r.cwd)}}catch(p){S(p)}});return await T(a),f}u(Oe,"write");function je(n,e){let t=n;e.forEach(({old:o,new:i},s)=>{const r=o===""?-1:t.indexOf(o);if(r<0)throw y(422,"replace_no_match",`replace[${s}]: old text not found in the revision you read`,"Copy the old text exactly from your last read and rerun.",{index:s});if(t.indexOf(o,r+1)>=0)throw y(422,"replace_ambiguous",`replace[${s}]: old text occurs more than once`,"Include more surrounding text so the old text matches once, and rerun.",{index:s});t=t.slice(0,r)+i+t.slice(r+o.length)})}u(je,"checkReplacements");async function De(n){const e=R(n),t=[],o=e.body!==void 0,i=e.replace!==void 0;o&&i&&t.push("give `body` or `replace`, not both"),o&&typeof e.body!="string"&&t.push("`body` must be a string");const s=e.replace;i&&(!Array.isArray(s)||s.length===0||s.some(d=>typeof d!="object"||d===null||typeof d.old!="string"||typeof d.new!="string"))&&t.push("`replace` must be a non-empty list of {old, new} strings"),(typeof e.rationale!="string"||e.rationale.trim()==="")&&t.push("`rationale` is required"),e.verbatim!==void 0&&typeof e.verbatim!="boolean"&&t.push("`verbatim` must be a boolean"),e.base!==void 0&&(typeof e.base!="string"||e.base==="")&&t.push("`base` must be a non-empty heads key"),e.base!==void 0&&!o&&!i&&t.push("`base` applies only to a `body` or `replace` edit");const r=Z(e,t),f=e.unset??[];if(!Array.isArray(f)||f.some(d=>!H.includes(d)))t.push(`\`unset\` takes only ${H.join(", ")}`);else for(const d of f)d==="summary"&&(r.summary=null),d==="why_it_exists"&&(r.whyItExists=null),d==="unlisted"&&(r.unlisted=!1),d==="lint_ignore"&&(r.lintIgnore=null),d==="extensions"&&(r.extensions=null),d==="rules"&&(r.rules=[]);t.length===0&&!o&&!i&&Object.keys(r).length===0&&t.push("nothing to change: give `body`, `replace`, or a field"),J(t);const a=e,{dto:c,synced:w}=await k(async d=>{const p=await E(d,n),l=await W(d,p,n.params.ref),g=await j(d,l),A=await N(d,p,[l]);try{if(!o&&!i)return await le(d,p,l,r),{dto:{id:l.id,name:g,revision:(await F(d,l)).revision},synced:A};const v=a.base??await fe(d,p,l);if(i&&v!==null){const B=await F(d,l),P=await x(d,l);B.source_path===null&&B.heads_key===v&&P.length===1&&je(await me(d,l,O(P[0].body_hash)),a.replace)}const D=a.verbatim===!0,m=o?{body:a.body,verbatim:D}:{replace:a.replace,verbatim:D},h=await ce(d,p,l,m,a.rationale,v,r,await C(d));if("stale"in h)throw y(409,"stale_revision",`${g} changed after your read; it is now at revision ${h.stale.revision}`,`Read it again with \`crtr canvas read ${g}\`, then redo your change on the current text.`,{stale:h.stale});const I=O((await x(d,l))[0].body_hash);return{dto:{id:l.id,name:g,revision:h.revision,...await Q(d,p,I,h.unresolved)},synced:A}}catch(v){S(v,l)}});return await T(w),c}u(De,"edit");async function Re(n){const e=R(n);if(e.new_name===void 0&&e.owner===void 0)throw b("give a new name, an owner, or both");if(e.new_name!==void 0&&(typeof e.new_name!="string"||!Y.test(e.new_name)))throw b("`new_name` must be segments of letters, digits, `_` or `-` joined by `/`");const{dto:t,change:o}=await k(async i=>{const s=await E(i,n),r=await W(i,s,n.params.ref),f=e.owner===void 0?void 0:await M(i,s,e.owner,e.cwd);try{const a=await ue(i,s,r,{...e.new_name!==void 0?{name:e.new_name}:{},...f?{owner:f}:{}},await C(i));return{dto:{id:a.id,name:await j(i,a)},change:await N(i,s,[r,a],e.cwd)}}catch(a){S(a,r)}});return await T(o),t}u(Re,"move");async function Ce(n){const{dto:e,change:t}=await k(async o=>{const i=await E(o,n),s=await W(o,i,n.params.ref),r=await j(o,s),f=await N(o,i,[s]);try{await se(o,i,s,await C(o))}catch(a){S(a,s)}return{dto:{id:s.id,name:r},change:f}});return await T(t),e}u(Ce,"remove");async function Se(n){const e=n.query.get("diff"),t=e===null?void 0:Number(e);if(t!==void 0&&!(Number.isInteger(t)&&t>0))throw b("`diff` must be a revision number");return await k(async o=>{const i=await E(o,n),s=await z(o,i,decodeURIComponent(n.params.ref));if("remote"in s)throw y(404,"not_found",G(s.remote),"Check the name with `crtr canvas search`.");const r="deleted"in s?s.deleted:s;if(r.type!=="document")throw b(`${r.name} is a ${r.type}, not a document`);const f=await de(o,i,r),a={object:await Ee(o,r),revisions:f.map(c=>({rev_id:c.rev_id,number:c.number,author_id:c.author,rationale:c.rationale,verbatim:c.verbatim,created_at:c.created_at,parents:c.parents}))};if(t!==void 0){const c=f.find(d=>d.number===t);if(!c)throw new _(404,"not_found",`${r.name} has no revision ${t}`);const w=c.parents[0]===void 0?void 0:f.find(d=>d.rev_id===c.parents[0]);a.diff=ge(w?O(w.body_hash):"",O(c.body_hash))}return a})}u(Se,"revisionHistory");const ee=1e3,te=1e3,ne=3e3,Ne=/^When\b.+\bbecause\b.+/,Te=u(n=>{const e=n.trim();return e===""?0:e.split(/\s+/).length},"words");function Ie(n,e){if(n.some(o=>o.on==="boot"&&o.deliver==="content")&&e>ee)return`body is ${e} words, over the ${ee}-word cap for a boot rule at content (the whole body enters every node's context). Keep the core here and move separate subjects into [[linked]] documents.`;const t=[...new Set(n.filter(o=>o.on!=="boot"&&o.deliver==="content").map(o=>o.on))];return t.length>0&&e>te?`body is ${e} words, over the ${te}-word cap for a content rule on ${t.join("/")}. Keep the core here and move separate subjects into [[linked]] documents.`:n.some(o=>o.on==="boot"&&o.deliver==="preview")&&e>ne?`body is ${e} words, over the ${ne}-word cap for a boot rule at preview. Keep the core here and move separate subjects into [[linked]] documents.`:null}u(Ie,"lengthFinding");async function Fe(n){const e=R(n);return await k(async t=>{const o=await E(t,n),i=e.owner===void 0?null:await M(t,o,e.owner,e.cwd),s=await t.prepare("SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND home_machine IS NULL AND kind != 'report' ORDER BY name").all(),r=[],f=new Map;for(const a of s){if(i&&a.owner!==i.id||!await V(t,o,a))continue;const c=await F(t,a),w=new Set(c.lint_ignore??[]),d=await j(t,a),p=u((m,h,I)=>{w.has(m)||r.push({name:d,severity:h,rule:m,message:I})},"add"),l=await ve(t,a),g=(await x(t,a))[0],A=g?O(g.body_hash):"",v=Ie(l,Te(A));v&&p("length","error",v),l.some(m=>m.deliver==="preview")&&!Ne.test(a.preview??"")&&p("preview-shape","error",`a rule delivers this document at preview, so the preview must read "When <situation in the reader's work>, read this because <what the reader gains>"`);const D=new Set((await t.prepare("SELECT written_as FROM edges WHERE type = 'links-to' AND src = ?").all(a.id)).map(m=>m.written_as));for(const m of K(A)){if(D.has(m))continue;const h=await X(t,o,m);"remote"in h||"object"in h||"deleted"in h||p("links","warning",`[[${m}]] names no object in your view`)}if(l.some(m=>m.on==="workspace-open"&&m.deliver==="content")){const m=await ae(t,a);m.type==="repo"&&!w.has("workspace-open")&&f.set(m.name,[...f.get(m.name)??[],d])}}for(const[a,c]of f)if(!(c.length<2))for(const w of c)r.push({name:w,severity:"warning",rule:"workspace-open",message:`repo ${a} has ${c.length} documents delivered at content on workspace-open (${c.join(", ")}); keep one and link the rest from it`});return{findings:r}})}u(Fe,"lint");const $=u((n,e=200)=>async t=>({status:e,body:await n(t)}),"ok"),et=[{method:"POST",pattern:"/v1/docs",handler:$(Oe,201)},{method:"PATCH",pattern:"/v1/docs/:ref",handler:$(De)},{method:"POST",pattern:"/v1/docs/:ref/move",handler:$(Re)},{method:"DELETE",pattern:"/v1/docs/:ref",handler:$(Ce)},{method:"GET",pattern:"/v1/docs/:ref/history",handler:$(Se)},{method:"POST",pattern:"/v1/docs/lint",handler:$(Fe)}];export{j as canonicalName,et as docRoutes,N as repoChange,T as syncChanged};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var Ie=Object.defineProperty;var s=(e,t)=>Ie(e,"name",{value:t,configurable:!0});import{envProfileId as Ae}from"../../../shared/env.js";import{pausedCanvasProfiles as $e}from"../../../core/profiles/manifest.js";import{existsSync as N,realpathSync as q,statSync as ee}from"node:fs";import{isAbsolute as F,resolve as T}from"node:path";import{appendSituationalContext as Ce,formatSituationalProse as Ne}from"../../../core/runtime/situational-context.js";import{closeNode as Te}from"../../../core/runtime/close.js";import{reviveNode as Pe}from"../../../core/runtime/revive.js";import{nowIso as B}from"../../../core/fs-utils.js";import{appPaidLaunch as Ee,assertLaunchModelRegistered as K,forkNode as De,resolveForkSource as Le,spawnChildPrepared as Me}from"../../../core/runtime/spawn.js";import{forkSourceNode as je,newNodeId as ne}from"../../../core/runtime/nodes.js";import{abandonCreateRequest as te,childrenOf as Ge,completeCreateRequest as ie,findCreateRequest as Fe,getNode as R,recordCreateRequestNode as Be,reserveCreateRequest as oe,reserveCreateRequestNode as Ke,listNodes as re,recordRespawnAttempt as We,subscribersOf as Ve,subscriptionsOf as ae,updateNode as C}from"../../../core/canvas/canvas.js";import{armControllerWait as xe}from"../../../core/canvas/waits.js";import{subtreeIds as ze}from"../../../core/canvas/nav-model.js";import{withCanvasWrite as W}from"../../../core/canvas/db.js";import{markNodeRead as Je}from"../../../core/canvas/recovery.js";import{nodeObject as Ue}from"../../../core/graph/objects.js";import{recordActedOn as Ye}from"../../../core/graph/edges.js";import{publish as He}from"../../../core/graph/events.js";import{readerFor as Qe}from"../reader.js";import{relocateSessionCwd as Xe,unbindSession as Ze}from"../../../core/canvas/sessions.js";import{ticketCountsForNodes as qe}from"../../../core/canvas/attention.js";import{isSafeNodeId as en}from"../../../core/canvas/paths.js";import{contextDir as de,nodeAgentDir as V}from"../../../core/canvas/node-agent-paths.js";import{checkCronBirth as nn}from"../../cron/authority.js";import{cronBirthPlacement as tn}from"../../cron/armed-context.js";import{existsBeneath as on,isAbsentOrRefused as rn,isBeneathRefusal as an,readdirBeneath as dn}from"../../../core/spaces/open-beneath.js";import{readNodeMessagesPage as sn,readNodeSession as ln,readNodeSnapshot as cn,transcriptMarkdown as fn}from"../../../core/runtime/node-read.js";import{assembleNodeSubject as un}from"../../../core/substrate/subject.js";import{reviveAll as pn}from"../../../core/runtime/revive-all.js";import{promoteAsync as mn,requestYieldAsync as wn,reshapeNodeAsync as x}from"../../../core/runtime/promote.js";import{recycleNode as hn}from"../../../core/runtime/recycle.js";import{relaunchRoot as yn}from"../../../core/runtime/reset.js";import{assertReopenGate as _n}from"../../../core/runtime/reopen.js";import{writeYieldMessage as gn,readGoal as bn}from"../../../core/runtime/kickoff.js";import{ensureRoadmap as vn,hasRoadmap as kn}from"../../../core/runtime/roadmap.js";import{renameWindow as Sn}from"../../../core/runtime/placement.js";import{buildLaunchSpecAsync as Rn,explicitModelIntent as On,normalizeModel as In,readMergedLaunchConfigAsync as se}from"../../../core/runtime/launch.js";import{modelRequestFromConfig as An}from"../../../core/model-routes.js";import{persistDormantModel as $n,setModelLive as Cn}from"../../../core/runtime/model-swap.js";import{waitForBrokerViewSocket as le}from"../../../core/runtime/view-socket.js";import{BrokerUnreachableError as Nn}from"../../../core/runtime/broker-request.js";import{boundFleet as P}from"../../../core/runtime/fleet.js";import{admitLegacyStructuredOutput as Tn}from"../../structured-requests/sidecar.js";import{assertOutputSchemaAccepted as Pn}from"../../../core/runtime/structured-output.js";import{OUTCOME_PAYLOAD_MAX_BYTES as ce}from"../../../core/canvas/types.js";import{HUMAN_ACTION_NAME_PATTERN as En}from"../../../core/human/action-binding.js";import{resolveHumanAction as Dn}from"../../../core/human-actions.js";import{parseDurationMs as Ln}from"../../../core/wake.js";import{claimWarmNode as Mn,refillWarmPool as fe,resumeWarmClaim as jn,warmPoolEnabled as Gn}from"../../../core/runtime/warm-pool.js";import{assertLaunchKind as z,isLaunchKind as Fn,resolveLaunchTarget as Bn}from"../../../core/runtime/launch-target.js";import{fullName as Kn}from"../../../core/canvas/labels.js";import{reportsForNode as ue}from"./reports.js";import{requireUsableProfile as Wn}from"./profiles.js";import{assertProfileActive as Vn,loadExactProfileManifest as xn}from"../../../core/profiles/manifest.js";import{getCron as pe}from"../../../core/canvas/crons.js";import{cronWakeOrigin as zn}from"../../../core/runtime/bearings.js";import{boundDaemonControl as Jn}from"../../control.js";import{notFound as E,usage as w}from"../../../core/errors.js";import{assertScope as Un,validateScopes as me}from"../../../core/scopes.js";import{scopeAllowed as I}from"../../../shared/env.js";import{toCloseResultDTO as Yn,toNodeDetailDTO as J,toNodeSummaryDTO as Hn,toReviveResultDTO as Qn}from"../map.js";import{ApiError as u}from"../../../api/index.js";import{InputError as y}from"../../../core/io.js";import{actsAsPerson as Xn,brokerMayReach as U,canReadNode as we,resolvedScopes as Zn}from"../router.js";import{forwardVerb as qn,queryWithoutMachine as et,relayError as nt,unknownError as tt,withMachine as it}from"../../peer/forward.js";import{fileRoot as ot}from"../../../core/spaces/file-root.js";import{activeGrant as rt,payerOfGrant as at,terminalGrant as dt}from"../../../core/grants/mirror.js";import{appRungForGrant as st}from"../../../core/llm/app-rungs.js";import{APP_FAMILY as lt}from"../../../core/model-routes.js";import{readConfig as ct}from"../../../core/config.js";import{hasConfiguredProviderCredential as ft,NO_PROVIDER_CREDENTIAL_MESSAGE as he}from"../../../core/runtime/provider-credential-gate.js";import{filterAsync as Y,mapAsync as ye}from"../../../core/async-array.js";async function h(e){const t=await R(e);if(t===null)throw E(`unknown node: ${e}`,{received:e});return t}s(h,"requireMeta");async function H(e){return{subscribesTo:await ae(e),subscribers:await Ve(e),children:await Ge(e)}}s(H,"edgesFor");async function g(e){const t=await h(e);return await J(t,await H(e))}s(g,"detailOf");async function ut(e){let t=(await ue(e)).length;try{t+=dn(await V(e),await de(e)).length}catch(n){if(!rn(n))throw n}return t}s(ut,"countFiles");function pt(e){if(typeof e!="object"||e===null)throw w("a node create requires a JSON body");const t=e;if(t.kind!==void 0&&(typeof t.kind!="string"||t.kind.trim()===""))throw w("kind must be a non-empty string when provided");if(t.description!==void 0&&(typeof t.description!="string"||t.description.trim()===""))throw w("description must be a non-empty string when provided");if(t.creator!==void 0&&(typeof t.creator!="string"||t.creator.trim()===""))throw w("creator must be a non-empty node id when provided");if(t.scopes!==void 0&&me(t.scopes),t.isolated!==void 0&&typeof t.isolated!="boolean")throw new u(400,"invalid_request","isolated must be a boolean",{param:"isolated"});const n=t.root_lifecycle;if(n!==void 0&&n!=="resident"&&n!=="terminal")throw w("root_lifecycle must be 'resident' or 'terminal'");if(t.request_key!==void 0&&(typeof t.request_key!="string"||t.request_key.trim()===""||t.request_key.length>128))throw w("request_key must be a non-empty string of at most 128 characters when provided");return t}s(pt,"parseCreateBody");function mt(e){if(e===void 0)return;const t=Ln(e),n=t===null?null:new Date(Date.now()+t);if(t===null||!Number.isFinite(t)||t<=0||n===null||!Number.isFinite(n.getTime()))throw new u(422,"invalid_deadline","`deadline` must be a positive representable duration");return n.toISOString()}s(mt,"parseDeadline");function wt(e){if(e!==void 0){if(typeof e!="object"||e===null||Array.isArray(e))throw new u(400,"invalid_request","`outcome_delivery` must be an object");if(typeof e.action!="string"||!En.test(e.action))throw new u(400,"invalid_request","`outcome_delivery.action` must be a declared action name");if(e.payload!==void 0){let t;try{t=JSON.stringify(e.payload)}catch{throw new u(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable")}if(t===void 0)throw new u(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable");if(Buffer.byteLength(t,"utf8")>ce)throw new u(413,"payload_too_large",`serialized payload exceeds ${ce} bytes`)}return e}}s(wt,"parseOutcomeDelivery");function ht(e,t){const n=Dn(e.action,t);if(!("kind"in n))return{actionName:e.action,argv:n.argv,cwd:n.cwd,configOrigin:n.configOrigin,...e.payload===void 0?{}:{payload:e.payload}};throw n.kind==="unknown_action"?new u(422,"unknown_action",`no action named ${e.action} is declared from ${t}`):new u(422,"invalid_action",`${e.action} is declared in ${n.configOrigin} but unusable: ${n.reason}`,{reason:n.reason,configOrigin:n.configOrigin})}s(ht,"resolveOutcomeDelivery");function yt(e,t,n){const o={kind:e.kind,cwd:n,prompt:e.prompt??""};return e.mode!==void 0&&(o.mode=e.mode),e.name!==void 0&&(o.name=e.name),e.description!==void 0&&(o.description=e.description),t!==void 0&&(o.parent=t),e.creator!==void 0&&(o.creator=e.creator),e.scopes!==void 0&&(o.scopes=e.scopes),e.isolated!==void 0&&(o.isolated=e.isolated),e.root===!0&&(o.root=!0),e.root_lifecycle!==void 0&&(o.rootLifecycle=e.root_lifecycle),(e.worktree===!0||typeof e.worktree=="string")&&(o.worktree=!0),typeof e.worktree=="string"&&(o.worktreeBase=e.worktree),e.worktree!==void 0&&(o.worktreeCwd=T(e.pin_cwd??e.cwd??n)),e.fork_from!==void 0&&(o.forkFrom=e.fork_from),e.model!==void 0&&(o.model=e.model),e.profile!==void 0&&(o.profile=e.profile),e.situational_context!==void 0&&(o.situationalContext=e.situational_context),e.node_id!==void 0&&(o.nodeId=e.node_id),o}s(yt,"buildRecipe");function _t(e,t,n){return!Gn()||e.prefer_warm!==!0||e.root!==!0||e.parent!=null||e.root_lifecycle==="terminal"||(e.prompt??"")!==""||e.model!==void 0||e.fork_from!==void 0||e.worktree!==void 0||e.node_id!==void 0||e.scopes!==void 0||e.isolated!==void 0?null:{kind:e.kind,mode:e.mode??"base",cwd:t,profileId:n,model:e.model??null,situationalContext:e.situational_context??null,name:e.name??null,creator:e.creator??null}}s(_t,"warmRequestFor");import{resolveCreateKind as _o}from"../../../core/runtime/launch-target.js";function _e(e){const t=e.modelIntent===void 0?void 0:`${e.modelIntent.family}/${e.modelIntent.strength}`;return An(e.model,t,e.modelExact===!0)}s(_e,"requestFromLaunch");function gt(e,t){if(t===null)return e;if(e.scopes===void 0)return{...e,scopes:[...t]};const n=e.scopes.filter(o=>!I(t,o));if(n.length===0)return e;throw new u(403,"scope_denied",`scopes exceed the bearer token's ceiling: ${n.join(", ")}`,{scopes:n,ceiling:t})}s(gt,"withinCeiling");async function ge(e,t,n){const o=(await Rn(t.kind,t.mode,{lifecycle:t.lifecycle,hasManager:e.parent!==null,model:t.model,modelOverride:!0,cwd:e.cwd,profileId:e.profile_id??null})).launch;await K(_e(o),{cwd:e.cwd,profileId:e.profile_id??null},n)}s(ge,"assertNodeModelChangeRegistered");async function be(e,t){const n=e.principal;if(n.class!=="broker"&&n.class!=="cron")return;const o=ot({app:n.app,resolved:await Zn(n),launched:n.scopes},t,"read");if("root"in o)return;const i="missing"in o?o.missing:null;throw new u(403,"scope_missing",i?`the child's directory ${t} needs missing scope ${i}`:`no permission of this run names the child's directory ${t}`,void 0,void 0,i?{scope:i}:void 0)}s(be,"assertCallerReaches");const Q=new Map;async function bt(e,t={},n={}){const o=gt(pt(e.body),e.ceiling);if(e.principal.class==="broker"){const c=e.principal;if(o.prefer_warm===!0&&c.app!=="app:terminal"&&(o.prefer_warm=!1),o.parent!=null){const r=await h(o.parent);if(r.grantee!==c.app)throw new u(403,"other_app_run","cannot create a child under another app's node");if(!await U(c,o.parent,r.grantee??"app:terminal"))throw E(`unknown node: ${o.parent}`,{received:o.parent})}if(o.creator!==void 0&&o.creator!==c.node)throw new u(403,"other_app_run","cannot create for another node");if(o.fork_from!==void 0){let r=null;try{r=await je(await Le(o.fork_from))}catch{}if(r?.grantee!==c.app||!await U(c,r.node_id,r.grantee))throw new u(403,"other_app_run","cannot fork another app's conversation")}o.creator=c.node}if(e.principal.class==="cron"){const c=e.principal;for(const l of["parent","creator","fork_from","node_id"])if(o[l]!=null)throw new u(403,"permission_error",`a cron cannot set ${l} on a node it starts`);o.prefer_warm=!1;const r=await nn({grantee:c.app,sub:c.sub,grant_id:c.grant,scopes:c.scopes});if(!r.allowed)throw new u(r.reason==="scope_missing"?403:401,r.reason==="scope_missing"?"scope_missing":"grant_revoked",r.message);if(o.scopes!==void 0){const l=o.scopes.find(p=>!I(r.effectiveScopes,p)||!I(c.scopes,p));if(l!==void 0)throw new u(403,"scope_missing",`scope ${l} is not held by this cron`,void 0,void 0,{scope:l})}else c.scopes!==null&&(o.scopes=[...c.scopes]);o.node_id=await ne(),c.isolated&&(o.isolated=!0,o.cwd=await V(o.node_id,c.app))}o.creator!==void 0&&Un(await h(o.creator),"crtr:act");const i=o.request_key?.trim();if(i===void 0||o.node_id!==void 0&&en(o.node_id)&&await R(o.node_id)!==null&&(await Fe(i))?.nodeId!==o.node_id)return D(e,o,t,n);const d=Q.get(i);if(d!==void 0)return d;const a=vt(e,o,i,t,n);Q.set(i,a);try{return await a}finally{Q.delete(i)}}s(bt,"handleCreate");async function vt(e,t,n,o,i){const d=await oe(n);if(d.nodeId!==null&&await R(d.nodeId)!==null){if(d.state!=="warm_claimed"){const a=await R(d.nodeId);if(e.principal.class!=="person"&&!await we(e.principal,a.grantee??"app:terminal",d.nodeId))throw new u(409,"create_request_conflict","request_key belongs to another caller");return{status:201,body:await g(d.nodeId)}}return D(e,t,o,i,d)}try{return await D(e,t,o,i,d)}catch(a){const c=await oe(n);throw(c.nodeId===null||await R(c.nodeId)===null)&&await te(n,c.nodeId),a}}s(vt,"handleKeyedCreateRequest");async function D(e,t,n,o,i=void 0){const d=o.deadlineAt??mt(t.deadline),a=wt(t.outcome_delivery),c=e.principal.class==="cron"?await tn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes,cron_id:e.principal.cron,origin_run_id:e.principal.origin_run,isolated:e.principal.isolated,creator:(await pe(e.principal.cron))?.created_by??null},t.node_id):null;c!==null&&(t.root=c.root===!0);const r=c?.parent??t.parent??void 0;if(r==null&&t.root!==!0)throw w("a node create requires a parent (or root: true for an independent root)");let l=null;if(t.output_schema!==void 0){try{l=JSON.parse(t.output_schema)}catch{throw w("output_schema must be a JSON schema string")}if(l===null||typeof l!="object"||Array.isArray(l))throw w("output_schema must be a JSON schema object");await Pn(l)}const{profileId:p,kind:m,cwd:f,launch:b}=await Bn(t,{validateKind:Oe});await be(e,f),t.worktree!==void 0&&await be(e,T(t.pin_cwd??t.cwd??f));const v={...t,kind:m},_=a===void 0?void 0:ht(a,f),X=("grant"in e.principal?e.principal.grant:null)??(r==null?null:(await R(r))?.grant_id??null);if(await K(_e(await Ee(b,X,t.model)),{cwd:f,profileId:p},n.modelRegistry),await at(X)!=="app"&&!await ft(p))throw new u(412,"no_provider_credential",he,{error:"no_provider_credential",message:he,next:"The person running this runtime logs a provider in with `crtr sys setup`, or stores a provider key on the profile."});let M=null;{const A=i===void 0||i.nodeId===null||i.state==="warm_claimed"?_t(v,f,p):null,j=l===null&&o.beforeBrokerLaunch===void 0?void 0:async S=>{l!==null&&await Tn(S,"terminal",l),o.beforeBrokerLaunch?.(S)};if(i?.state==="warm_claimed"&&i.nodeId!==null){const S=A===null?null:await jn(A,i.nodeId,j);if(S===null)throw await te(i.requestKey,i.nodeId),new u(503,"create_resume_failed",`could not resume warm claim ${i.nodeId}; retry the create`);return await ie(i.requestKey,S),{status:201,body:await g(S)}}if(A!==null){const S=await Mn(A,{...d===void 0?{}:{deadlineAt:d},..._===void 0?{}:{outcomeDelivery:_},...i===void 0?{}:{requestKey:i.requestKey}},j);if(S!==null)return i!==void 0&&await ie(i.requestKey,S),fe(A),{status:201,body:await g(S)};M=A}const G=i?.nodeId??t.node_id??(i===void 0?void 0:await ne());i!==void 0&&i.nodeId===null&&await Ke(i.requestKey,G);const O=yt(G===void 0?v:{...v,node_id:G},r,f);c!==null&&(c.rootLifecycle!==void 0&&(O.rootLifecycle=c.rootLifecycle),c.scopes!==void 0&&v.scopes===void 0&&(O.scopes=c.scopes),c.run!==void 0&&(O.run=c.run),c.withinCreate!==void 0&&(O.withinCreate=c.withinCreate)),d!==void 0&&(O.deadlineAt=d),_!==void 0&&(O.outcomeDelivery=_),O.readiness=t.root===!0?"await":"launch";const Z=await Me(O,j,()=>{M!==null&&fe(M)});return i!==void 0&&await Be(i.requestKey,Z.node.node_id),{status:201,body:await g(Z.node.node_id)}}}s(D,"handleCreateRequest");function k(e,t){const n=e.query.getAll(t);if(n.length>1)throw w(`${t} must appear at most once`);return n[0]}s(k,"singleQueryValue");function ve(e,t){if(e!==void 0){if(e==="1"||e==="true")return!0;if(e==="0"||e==="false")return!1;throw w(`${t} must be true or false`)}}s(ve,"optionalBoolean");function L(e,t,n){if(e!==void 0){if(n.includes(e))return e;throw w(`${t} must be one of: ${n.join(", ")}`)}}s(L,"oneOf");function kt(e){const t=new Set(["status","lifecycle","kind","mode","profile_id","profile_prefix","parent","top_level","under","hanging","include"]);for(const[b]of e.query)if(!t.has(b))throw w(`unknown nodes query parameter: ${b}`);const n=L(k(e,"status"),"status",["active","idle","done","dead","canceled"]),o=L(k(e,"lifecycle"),"lifecycle",["terminal","resident"]),i=k(e,"kind"),d=L(k(e,"mode"),"mode",["base","orchestrator"]),a=k(e,"profile_id"),c=k(e,"profile_prefix"),r=k(e,"parent"),l=ve(k(e,"top_level"),"top_level"),p=k(e,"under"),m=ve(k(e,"hanging"),"hanging"),f=L(k(e,"include"),"include",["activity"]);if(r!==void 0&&l===!0)throw w("parent and top_level=true cannot be combined");return{...n!==void 0?{status:n}:{},...o!==void 0?{lifecycle:o}:{},...i!==void 0?{kind:i}:{},...d!==void 0?{mode:d}:{},...a!==void 0?{profile_id:a}:{},...c!==void 0?{profile_prefix:c}:{},...r!==void 0?{parent:r}:{},...l!==void 0?{top_level:l}:{},...p!==void 0?{under:p}:{},...m!==void 0?{hanging:m}:{},...f!==void 0?{include:f}:{}}}s(kt,"parseListQuery");async function St(e){const t=kt(e);let n=t.status!==void 0?await re({status:t.status}):await re();if(e.principal.class!=="person"&&(n=await Y(n,async a=>await we(e.principal,a.grantee??"app:terminal",a.node_id))),t.lifecycle!==void 0&&(n=n.filter(a=>a.lifecycle===t.lifecycle)),t.kind!==void 0&&(n=n.filter(a=>a.kind===t.kind)),t.mode!==void 0&&(n=n.filter(a=>a.mode===t.mode)),t.profile_id!==void 0&&(n=n.filter(a=>a.profile_id===t.profile_id)),t.profile_prefix!==void 0&&(n=n.filter(a=>a.profile_id?.startsWith(t.profile_prefix))),t.parent!==void 0&&(n=n.filter(a=>a.parent===t.parent)),t.top_level===!0&&(n=n.filter(a=>a.parent===null)),t.under!==void 0){const a=new Set(await ze(t.under));n=n.filter(c=>a.has(c.node_id))}t.hanging===!0&&(n=await Y(n,async a=>a.parent!==null&&await R(a.parent)===null));const o=t.include==="activity"?await qe(n.map(a=>a.node_id)):null,i=n.length>0?$e():new Set;return{status:200,body:await ye(n,async a=>{const c=await Hn(a,a.profile_id!==null&&i.has(a.profile_id));if(o!==null){const r=await ue(a.node_id);c.activity={latest_report:r[0]??null,final_report:a.final_report===null?null:r.find(l=>l.name===a.final_report)??null,pending_human_count:o[a.node_id]??0}}return c})}}s(St,"handleList");async function Rt(e){const t=e.params.id,n=await qn(e,t,{method:"GET",path:`/v1/nodes/${encodeURIComponent(t)}`,query:et(e)});if(n.kind==="unknown")throw tt(t,n.unreachable);if(n.kind==="remote"){if(n.status>=400)throw nt(n.status,n.body);return{status:n.status,body:it(n.body,n.machine)}}return await h(t),{status:200,body:await g(t)}}s(Rt,"handleDetail");async function Ot(e){const t=e.params.id;await h(t);const n=await cn(t);return{status:200,body:{node_id:t,snapshot:{messages:n.snapshot.messages,...n.snapshot.messageIds===void 0?{}:{messageIds:n.snapshot.messageIds},messageVisibility:n.snapshot.messageVisibility,turnVisibility:n.snapshot.turnVisibility,stats:n.snapshot.stats,state:n.snapshot.state,display:n.snapshot.display,...n.snapshot.queued===void 0?{}:{queued:n.snapshot.queued},...n.snapshot.toolGroupSummaries===void 0?{}:{toolGroupSummaries:n.snapshot.toolGroupSummaries},...n.snapshot.turnReplies===void 0?{}:{turnReplies:n.snapshot.turnReplies},...n.snapshot.workingActivity===void 0?{}:{workingActivity:n.snapshot.workingActivity}},commands:n.commands.map(i=>({name:i.name,description:i.description,source:i.source})),captured_at:B()}}}s(Ot,"handleSnapshot");async function It(e){const t=e.params.id,n=await un(t);if(n===null)throw E(`unknown node: ${t}`,{received:t});return{status:200,body:n}}s(It,"handleSubject");function At(e){for(const[d]of e.query)if(d!=="cursor"&&d!=="limit")throw w(`unknown messages query parameter: ${d}`);const t=e.query.getAll("cursor"),n=e.query.getAll("limit");if(t.length>1)throw w("cursor must appear at most once");if(n.length>1)throw w("limit must appear at most once");if(n.length===0)return t.length===0?{}:{cursor:t[0]};const o=n[0],i=Number(o);if(!Number.isSafeInteger(i)||o==="")throw w("limit must be an integer from 1 through 500");return t.length===0?{limit:i}:{cursor:t[0],limit:i}}s(At,"parseMessagesQuery");async function $t(e){const t=e.params.id,n=await sn(t,At(e));return{status:200,body:{node_id:t,messages:n.messages,...n.messageIds===void 0?{}:{message_ids:n.messageIds},message_visibility:n.messageVisibility,next_cursor:n.nextCursor,captured_at:B()}}}s($t,"handleMessages");async function Ct(e){const t=e.params.id;await h(t);const n=await ln(t);return{status:200,body:{node_id:t,session_file:n.sessionFile,session_jsonl:n.sessionJsonl,system_prompt:n.systemPrompt,captured_at:B()}}}s(Ct,"handleSession");async function Nt(e){const t=e.params.id;return await h(t),{status:200,body:{node_id:t,markdown:await fn(t)}}}s(Nt,"handleTranscript");async function Tt(e){const t=e.params.id;await h(t);const n=s(async(i,d)=>({node_id:i,label:(await R(i))?.name??i,dir:await de(i),self:d,files:await ut(i)}),"root"),o=await Y([await n(t,!0),...await ye(await ae(t),async i=>await n(i.node_id,!1))],async i=>await Pt(i.node_id,i.dir)||i.files>0);return{status:200,body:{node_id:t,roots:o}}}s(Tt,"handleContext");async function Pt(e,t){try{return on(await V(e),t)}catch(n){if(an(n))return!1;throw n}}s(Pt,"hasDir");async function Et(e){const t=await h(e.params.id),n=e.principal.class==="broker"?e.principal.scopes:e.ceiling,o=n===null?void 0:t.scopes==null?[...n]:[...new Set([...t.scopes.filter(d=>I(n,d)),...n.filter(d=>I(t.scopes,d))])],i=await De(t.node_id,o,e.principal.class==="broker"?e.principal.node:void 0);return{status:201,body:await g(i.node.node_id)}}s(Et,"handleFork");const Dt=1e4;async function Lt(e){const t=e.params.id;await h(t);const n=e.body,o=n?.resume??!0,i=n?.reopen??!1;await _n(t,i,e.principal.class==="broker"?e.principal.node:null);const d=!o&&n?.cron_id!==void 0?await pe(n.cron_id):null,a=await Pe(t,{resume:o,capacity:"refuse",...d!==null?{wakeReason:await zn(d,"schedule_revive")}:{}});a.outcome!=="already-live"&&await We(t,{failures:0,notBefore:null});const c=(await R(t))?.status??"active",r=(a.launch??P().get(t)?.handle)?.exited,l=a.outcome!=="frozen"&&await le(t,r,Dt);return{status:200,body:Qn(t,a,c,l)}}s(Lt,"handleRevive");async function Mt(e){const t=e.params.id;return{status:200,body:{newNodeId:(await yn(t))?.newNodeId??null}}}s(Mt,"handleRelaunchRoot");async function jt(e){const t=e.params.id;await h(t);const n=e.body;if(n?.caller_pid!==void 0&&(!Number.isInteger(n.caller_pid)||n.caller_pid<=0))throw w("caller_pid must be a positive integer when provided");const o=Jn(),i=new Promise(a=>{e.res.once("close",()=>a())}),d=await Te(t,{teardownAfter:i,rootEvent:n?.finish===!0?"finish":"cancel",callerPid:n?.caller_pid,...e.principal.class==="broker"?{closer:e.principal.node}:{},...o===null?{}:{registerDetached:o.registerDetached}});return{status:200,body:Yn(d)}}s(jt,"handleClose");async function Gt(e){if(!Xn(e.principal))throw new u(403,"permission_error","only the person marks a conversation read");const t=e.params.id;return await h(t),await Je(t),{status:204}}s(Gt,"handleMarkRead");async function Ft(e){const t=e.params.id;await h(t);const n=await hn(t);return{status:200,body:{recycled:n.recycled,finalized:n.finalized,new_root:n.newRoot,delivered:n.delivered}}}s(Ft,"handleRecycle");async function Bt(e){const t=e.params.id;return await h(t),await x(t,{lifecycle:"terminal"}),await $(e,t,["lifecycle"]),{status:200,body:await g(t)}}s(Bt,"handleDemote");async function Kt(e,t={}){const n=e.params.id,o=await h(n),i=e.body??{};i.kind!==void 0&&await z(i.kind,o.cwd,o.profile_id??null),i.model!==void 0&&await ge(o,{kind:i.kind??o.kind,mode:"orchestrator",lifecycle:i.resident===!0?"resident":o.lifecycle,model:i.model},t.modelRegistry);const d=await mn(n,{...i.kind!==void 0?{kind:i.kind}:{},...i.resident===!0?{resident:!0}:{},...i.model!==void 0?{model:i.model}:{}});await $(e,n,["mode",...i.kind!==void 0?["kind"]:[],...i.resident===!0?["lifecycle"]:[],...i.model!==void 0?["model"]:[]]);const a=await J(d.meta,await H(n));return a.roadmap_written=d.roadmapWritten,a.roadmap_ref=d.roadmapRef,a.goal_path=d.goalPath,{status:200,body:a}}s(Kt,"handlePromote");async function Wt(e,t={}){const n=e.params.id,o=await h(n),i=e.body??{};i.kind!==void 0&&await z(i.kind,o.cwd,o.profile_id??null),i.model!==void 0&&await ge(o,{kind:i.kind??o.kind,mode:i.promote===!0?"orchestrator":o.mode,lifecycle:o.lifecycle,model:i.model},t.modelRegistry);const d=i.note??"Refresh: continue against your roadmap or goal.";await gn(n,d);const a=await wn(n,{...i.promote===!0?{promote:!0}:{},...i.kind!==void 0?{kind:i.kind}:{},...i.model!==void 0?{model:i.model}:{}});return{status:200,body:await J(a.meta,await H(n))}}s(Wt,"handleYield");async function Vt(e){const t=e.params.id;await h(t);const n=e.body;if(n===void 0||typeof n.controller!="string"||n.controller.trim()==="")throw w("a wait requires a controller node id");const o=await R(n.controller);if(o===null)throw E(`unknown controller: ${n.controller}`,{received:n.controller});if(o.status!=="active"&&o.status!=="idle")throw new u(422,"unprocessable",`controller ${n.controller} is ${o.status}`);return await xe(t,n.controller),{status:200,body:await g(t)}}s(Vt,"handleWait");async function xt(e){const t=e.principal,n=await pn(t.class==="broker"?async i=>i.grantee===t.app&&await U(t,i.node_id,i.grantee):void 0);return{status:200,body:{revived:[...n.revived],failed:[...n.failed],frozen:[...n.frozen]}}}s(xt,"handleReviveAll");const zt=new Set(["interrupt","steer","after-turn","next-turn"]);function Jt(e){if(e===null)return null;const t=s(o=>{throw new y({error:"invalid_listen",message:o,received:e,field:"listen",next:'Send an object mapping levels "1".."10" to interrupt|steer|after-turn|next-turn (e.g. {"3":"next-turn"}), or null to reset to the global default.'})},"bad");if(typeof e!="object"||Array.isArray(e))return t("listen must be an object or null");const n=Object.entries(e);if(n.length===0)return t("listen must name at least one level");for(const[o,i]of n)/^(?:[1-9]|10)$/.test(o)||t(`listen level must be 1..10: ${o}`),(typeof i!="string"||!zt.has(i))&&t(`listen delivery must be interrupt|steer|after-turn|next-turn: ${String(i)}`);return JSON.stringify(Object.fromEntries(n))}s(Jt,"listenJson");async function $(e,t,n,o){n.length===0&&o===void 0||await W(async i=>{if(o?.(i),n.length===0)return;const d=await Ue(i,t),a=await Qe(i,e),c=a.class==="person"?a.person:a.class==="app"?a.app:a.node;await Ye(i,c,d),await He(i,{event:"configured",object:d,actor:c,attrs:{changed:n.join(",")}})})}s($,"recordConfigured");function ke(e){const t=P().get(e);if(t===void 0)return!1;try{return process.kill(t.pid,"SIGTERM"),!0}catch{return!1}}s(ke,"restartLiveBroker");function Se(e,t){try{return q(e)===q(t)}catch{return!1}}s(Se,"sameDirectory");async function Ut(e,t={}){const n=e.params.id,o=e.body;let i;if(o===void 0)i={};else if(typeof o=="object"&&o!==null&&!Array.isArray(o))i=o;else throw new y({error:"cwd_invalid",message:"cwd repair requires a non-null object with a non-empty cwd string",received:o,field:"cwd",next:"Send a config patch with a non-empty absolute cwd string."});if(i.profile!==void 0){if(typeof i.profile!="string"||i.profile==="")throw new y({error:"profile_invalid",message:"profile must be a non-empty profile id or name",received:i.profile,field:"profile",next:"Send a profile id or exact name."});if(Object.keys(i).some(m=>m!=="profile"&&m!=="restart"&&i[m]!==void 0))throw new y({error:"profile_not_exclusive",message:"profile can only be combined with restart",field:"profile",next:"Send profile (and optionally restart) alone."});const r=await h(n),l=await Wn(e,i.profile);Vn(l.profileId);const p=xn(l.profileId).manifest.grantee??"app:terminal";if(p!==(r.grantee??"app:terminal"))throw new y({error:"profile_grantee_mismatch",message:`profile ${l.profileId} belongs to ${p}, node ${n} to ${r.grantee}`,received:i.profile,field:"profile",next:"Pick a profile owned by the same app as the node."});return(r.profile_id??null)!==l.profileId&&(Fn(r.kind)?(await z(r.kind,r.cwd,l.profileId),await x(n,{profileId:l.profileId})):await C(n,{profile_id:l.profileId}),await $(e,n,["profile"])),i.restart===!0&&ke(n),{status:200,body:await g(n)}}if(Object.prototype.hasOwnProperty.call(i,"cwd")){const r=i.cwd;if(typeof r!="string"||r==="")throw new y({error:"cwd_invalid",message:"cwd must be a non-empty string",received:r,field:"cwd",next:"Send a non-empty absolute cwd string."});if(Object.keys(i).some(f=>f!=="cwd"&&f!=="keep_session"&&f!=="restart"&&i[f]!==void 0))throw new y({error:"cwd_not_exclusive",message:"cwd repair cannot be combined with another config field",received:i,field:"cwd",next:"Send cwd as the only config field."});const l=await h(n),p=N(l.cwd);if(i.keep_session===!0||p&&F(r)&&N(r)&&Se(l.cwd,r)){if(!F(r))throw new y({error:"cwd_not_absolute",message:`cwd must be an absolute path: ${r}`,received:r,field:"cwd",next:"Send an absolute path."});const f=T(r);let b=!1;try{b=ee(f).isDirectory()}catch{}if(!b)throw new y({error:"cwd_missing",message:`cwd is not an existing directory: ${f}`,received:r,field:"cwd",next:"Send an existing absolute directory."});if(p&&!Se(l.cwd,f))throw new y({error:"cwd_not_same_directory",message:`${f} is not the same directory as the recorded ${l.cwd}; --keep-session only relocates an alias`,received:r,field:"cwd",next:"Drop --keep-session to repair a gone cwd, or pass the real path of the recorded directory."});if(l.managed_worktree?.state==="open")throw new y({error:"cwd_node_managed_worktree_open",message:`${n} owns an open managed worktree and cannot relocate its cwd`,received:r,field:"cwd",next:"Reconcile the managed worktree first."});const v=l.cwd;return v!==f&&(await W(async _=>{await C(n,{cwd:f}),await _.prepare("UPDATE crons SET cwd = ? WHERE anchor_node = ? AND cwd = ?").run(f,n,v),await Xe(n,v,f)}),await $(e,n,["cwd"])),i.restart===!0&&ke(n),{status:200,body:await g(n)}}if(P().has(n)){const f=`${n} cannot repair its cwd while its broker is live`;throw new u(409,"cwd_node_live",f,{error:"cwd_node_live",message:f,received:r,field:"cwd",next:"Let the node become dormant, then retry the cwd repair."})}if(l.fork_from!=null)throw new y({error:"cwd_node_forked",message:`${n} is a fork and cannot repair its cwd; recorded cwd: ${l.cwd}`,received:r,field:"cwd",next:`Restore ${l.cwd}, then revive the fork normally.`});if(l.managed_worktree?.state==="open")throw new y({error:"cwd_node_managed_worktree_open",message:`${n} owns an open managed worktree and cannot repair its cwd`,received:r,field:"cwd",next:"Restore or reconcile the recorded checkout, then use the managed-worktree close or abandon flow."});if(N(l.cwd))throw new y({error:"cwd_still_present",message:`${n} recorded cwd still exists: ${l.cwd}; cwd repair is not a relocation command`,received:r,field:"cwd",next:"This command only repairs a node whose recorded cwd is gone."});if(!F(r))throw new y({error:"cwd_not_absolute",message:`cwd must be an absolute path: ${r}`,received:r,field:"cwd",next:"Send an absolute path."});const m=T(r);if(!N(m))throw new y({error:"cwd_missing",message:`cwd does not exist: ${m}`,received:r,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."});try{if(!ee(m).isDirectory())throw new y({error:"cwd_not_a_directory",message:`cwd is not a directory: ${m}`,received:r,field:"cwd",next:"Send an existing absolute directory."})}catch(f){throw f instanceof y?f:new y({error:"cwd_missing",message:`cwd does not exist: ${m}`,received:r,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."})}return await W(async()=>{await C(n,{cwd:m}),await Ze(n)}),await $(e,n,["cwd"]),{status:200,body:await g(n)}}let d=await h(n);const a=i.listen===void 0?void 0:Jt(i.listen);if(i.scopes!==void 0){const r=me(i.scopes),l=d.grantee==="app:terminal"?await dt():d.grant_id?await rt(d.grant_id):null,p=d.grantee==="app:terminal"&&!ct("user").runtime?.issuer;if(!r.every(m=>I(d.scopes,m)&&(l?I(l.resolved,m):p)))throw new y({error:"unknown_scope",message:"scopes may only narrow the node's current effective set",received:r,field:"scopes",next:"Send only scopes the node currently holds."});if(r.includes("crtr:act")&&!I(r,"crtr:llm"))throw new u(400,"invalid_scope","crtr:act requires crtr:llm");d=await C(n,{scopes:r})}if(i.situational_context!==void 0&&Ce(n,Ne(i.situational_context)),i.model!==void 0){const r=(await se(d.cwd,d.profile_id??null)).modelLadders,l=On(i.model,r),p=await st(d.grant_id,i.model,l===void 0),m=p?{family:lt,strength:p.strength}:l,f=p?p.model:In(i.model,r);await K({kind:"concrete",spec:f},{cwd:d.cwd,profileId:d.profile_id??null},t.modelRegistry);const b=P();let v=!1;if(b.isClaimed(n)&&await le(n,b.get(n)?.handle.exited)&&(v=await Cn(n,f,m).then(()=>!0,_=>{if(_ instanceof Nn)return!1;throw _})),!v){if(b.isClaimed(n)){const _=`${n}'s broker did not accept the model switch; the model was not changed`;throw new u(409,"broker_not_ready",_,{error:"broker_not_ready",message:_,received:i.model,field:"model",next:"Check the node with `crtr node show`, then retry the model change."})}if(!f.includes("/"))throw w(`model must resolve to a provider/id spec: ${i.model}`);await $n(n,f,m)}d=await h(n)}if(i.kind!==void 0||i.mode!==void 0||i.lifecycle!==void 0){i.kind!==void 0&&await Oe(i.kind);const r=i.kind??d.kind,l=i.mode??d.mode,p=i.lifecycle??d.lifecycle;let m;l==="orchestrator"&&!await kn(n)&&(m=s(async()=>{const f=await bn(n);await vn(n,f!==null&&f.trim()!==""?{goal:f.trim()}:{})},"beforePersist")),d=await x(n,{...i.kind!==void 0?{kind:r}:{},...i.mode!==void 0?{mode:l}:{},...i.lifecycle!==void 0?{lifecycle:p}:{}},m)}i.name!==void 0&&(d=await C(n,{name:i.name}),d.window&&Sn(d.window,Kn(d)));const c=["scopes","situational_context","model","kind","mode","lifecycle","name","listen"].filter(r=>i[r]!==void 0);return await $(e,n,c,a===void 0?void 0:async r=>{await r.prepare("UPDATE objects SET listen_json = ? WHERE id = ? AND type = 'node'").run(a,n)}),{status:200,body:await g(n)}}s(Ut,"handleConfig");async function Re(){return(await se(process.cwd(),Ae()||null)).kinds}s(Re,"daemonKinds");async function Oe(e){const t=Object.keys(await Re());if(!t.includes(e))throw w(`unknown kind: ${e}`,{received:e,next:`Valid kinds: ${t.sort().join(", ")}.`})}s(Oe,"assertKindServer");async function Yt(){const e=await Re();return{status:200,body:{kinds:Object.keys(e).sort().map(n=>({kind:n,when_to_use:e[n]?.whenToUse??null,available_to:e[n]?.availableTo??null}))}}}s(Yt,"handleListKinds");const wo=[{method:"POST",pattern:"/v1/nodes",scope:"crtr:act",handler:bt},{method:"GET",pattern:"/v1/nodes",handler:St},{method:"GET",pattern:"/v1/kinds",handler:s(()=>Yt(),"handler")},{method:"POST",pattern:"/v1/nodes/revive-all",scope:"crtr:act",handler:xt},{method:"GET",pattern:"/v1/nodes/:id",handler:Rt},{method:"GET",pattern:"/v1/nodes/:id/snapshot",handler:Ot},{method:"GET",pattern:"/v1/nodes/:id/subject",handler:It},{method:"GET",pattern:"/v1/nodes/:id/messages",handler:$t},{method:"GET",pattern:"/v1/nodes/:id/session",handler:Ct},{method:"GET",pattern:"/v1/nodes/:id/transcript",handler:Nt},{method:"GET",pattern:"/v1/nodes/:id/context",handler:Tt},{method:"POST",pattern:"/v1/nodes/:id/fork",scope:"crtr:act",handler:Et},{method:"POST",pattern:"/v1/nodes/:id/revive",scope:"crtr:act",handler:Lt},{method:"POST",pattern:"/v1/nodes/:id/relaunch-root",scope:"crtr:act",handler:Mt},{method:"POST",pattern:"/v1/nodes/:id/close",handler:jt},{method:"POST",pattern:"/v1/nodes/:id/read",handler:Gt},{method:"POST",pattern:"/v1/nodes/:id/recycle",scope:"crtr:act",handler:Ft},{method:"POST",pattern:"/v1/nodes/:id/demote",scope:"crtr:act",handler:Bt},{method:"POST",pattern:"/v1/nodes/:id/promote",scope:"crtr:act",handler:Kt},{method:"POST",pattern:"/v1/nodes/:id/yield",scope:"crtr:act",handler:Wt},{method:"POST",pattern:"/v1/nodes/:id/wait",handler:Vt},{method:"PATCH",pattern:"/v1/nodes/:id/config",handler:Ut}];export{Ut as handleConfig,bt as handleCreate,Et as handleFork,St as handleList,Kt as handlePromote,Wt as handleYield,wo as nodeRoutes,_o as resolveCreateKind};
|
|
1
|
+
var Ie=Object.defineProperty;var d=(e,n)=>Ie(e,"name",{value:n,configurable:!0});import{envProfileId as Oe}from"../../../shared/env.js";import{pausedCanvasProfiles as Ae}from"../../../core/profiles/manifest.js";import{existsSync as N,realpathSync as q,statSync as ee}from"node:fs";import{isAbsolute as F,resolve as T}from"node:path";import{appendSituationalContext as $e,formatSituationalProse as Ce}from"../../../core/runtime/situational-context.js";import{closeNode as Ne}from"../../../core/runtime/close.js";import{reviveNode as Te}from"../../../core/runtime/revive.js";import{nowIso as B}from"../../../core/fs-utils.js";import{appPaidLaunch as Pe,assertLaunchModelRegistered as K,forkNode as Ee,resolveForkSource as De,spawnChildPrepared as Le}from"../../../core/runtime/spawn.js";import{forkSourceNode as Me,newNodeId as ne}from"../../../core/runtime/nodes.js";import{abandonCreateRequest as te,childrenOf as je,completeCreateRequest as ie,findCreateRequest as Ge,getNode as R,recordCreateRequestNode as Fe,reserveCreateRequest as oe,reserveCreateRequestNode as Be,listNodes as Ke,recordRespawnAttempt as Ve,subscribersOf as We,subscriptionsOf as re,updateNode as C}from"../../../core/canvas/canvas.js";import{armControllerWait as xe}from"../../../core/canvas/waits.js";import{withCanvasWrite as V}from"../../../core/canvas/db.js";import{markNodeRead as ze}from"../../../core/canvas/recovery.js";import{nodeObject as Je}from"../../../core/graph/objects.js";import{recordActedOn as Ue}from"../../../core/graph/edges.js";import{publish as Ye}from"../../../core/graph/events.js";import{readerFor as He}from"../reader.js";import{relocateSessionCwd as Qe,unbindSession as Xe}from"../../../core/canvas/sessions.js";import{ticketCountsForNodes as Ze}from"../../../core/canvas/attention.js";import{isSafeNodeId as qe}from"../../../core/canvas/paths.js";import{contextDir as ae,nodeAgentDir as W}from"../../../core/canvas/node-agent-paths.js";import{checkCronBirth as en}from"../../cron/authority.js";import{cronBirthPlacement as nn}from"../../cron/armed-context.js";import{existsBeneath as tn,isAbsentOrRefused as on,isBeneathRefusal as rn,readdirBeneath as an}from"../../../core/spaces/open-beneath.js";import{readNodeMessagesPage as dn,readNodeSession as sn,readNodeSnapshot as ln,transcriptMarkdown as cn}from"../../../core/runtime/node-read.js";import{assembleNodeSubject as un}from"../../../core/substrate/subject.js";import{reviveAll as fn}from"../../../core/runtime/revive-all.js";import{promoteAsync as pn,requestYieldAsync as mn,reshapeNodeAsync as x}from"../../../core/runtime/promote.js";import{recycleNode as wn}from"../../../core/runtime/recycle.js";import{relaunchRoot as hn}from"../../../core/runtime/reset.js";import{assertReopenGate as yn}from"../../../core/runtime/reopen.js";import{writeYieldMessage as _n,readGoal as gn}from"../../../core/runtime/kickoff.js";import{ensureRoadmap as bn,hasRoadmap as vn}from"../../../core/runtime/roadmap.js";import{renameWindow as kn}from"../../../core/runtime/placement.js";import{buildLaunchSpecAsync as Sn,explicitModelIntent as Rn,normalizeModel as In,readMergedLaunchConfigAsync as de}from"../../../core/runtime/launch.js";import{modelRequestFromConfig as On}from"../../../core/model-routes.js";import{persistDormantModel as An,setModelLive as $n}from"../../../core/runtime/model-swap.js";import{waitForBrokerViewSocket as se}from"../../../core/runtime/view-socket.js";import{BrokerUnreachableError as Cn}from"../../../core/runtime/broker-request.js";import{boundFleet as P}from"../../../core/runtime/fleet.js";import{admitLegacyStructuredOutput as Nn}from"../../structured-requests/sidecar.js";import{assertOutputSchemaAccepted as Tn}from"../../../core/runtime/structured-output.js";import{OUTCOME_PAYLOAD_MAX_BYTES as le}from"../../../core/canvas/types.js";import{HUMAN_ACTION_NAME_PATTERN as Pn}from"../../../core/human/action-binding.js";import{resolveHumanAction as En}from"../../../core/human-actions.js";import{parseDurationMs as Dn}from"../../../core/wake.js";import{claimWarmNode as Ln,refillWarmPool as ce,resumeWarmClaim as Mn,warmPoolEnabled as jn}from"../../../core/runtime/warm-pool.js";import{assertLaunchKind as z,isLaunchKind as Gn,resolveLaunchTarget as Fn}from"../../../core/runtime/launch-target.js";import{fullName as Bn}from"../../../core/canvas/labels.js";import{reportsForNode as ue}from"./reports.js";import{requireUsableProfile as Kn}from"./profiles.js";import{assertProfileActive as Vn,loadExactProfileManifest as Wn}from"../../../core/profiles/manifest.js";import{getCron as fe}from"../../../core/canvas/crons.js";import{cronWakeOrigin as xn}from"../../../core/runtime/bearings.js";import{boundDaemonControl as zn}from"../../control.js";import{notFound as E,usage as w}from"../../../core/errors.js";import{assertScope as Jn,validateScopes as pe}from"../../../core/scopes.js";import{scopeAllowed as O}from"../../../shared/env.js";import{toCloseResultDTO as Un,toNodeDetailDTO as J,toNodeSummaryDTO as Yn,toReviveResultDTO as Hn}from"../map.js";import{ApiError as f}from"../../../api/index.js";import{InputError as y}from"../../../core/io.js";import{actsAsPerson as Qn,brokerMayReach as U,canReadNode as me,resolvedScopes as Xn}from"../router.js";import{forwardVerb as Zn,queryWithoutMachine as qn,relayError as et,unknownError as nt,withMachine as tt}from"../../peer/forward.js";import{fileRoot as it}from"../../../core/spaces/file-root.js";import{activeGrant as ot,payerOfGrant as rt,terminalGrant as at}from"../../../core/grants/mirror.js";import{appRungForGrant as dt}from"../../../core/llm/app-rungs.js";import{APP_FAMILY as st}from"../../../core/model-routes.js";import{readConfig as lt}from"../../../core/config.js";import{hasConfiguredProviderCredential as ct,NO_PROVIDER_CREDENTIAL_MESSAGE as we}from"../../../core/runtime/provider-credential-gate.js";import{filterAsync as he,mapAsync as ye}from"../../../core/async-array.js";async function h(e){const n=await R(e);if(n===null)throw E(`unknown node: ${e}`,{received:e});return n}d(h,"requireMeta");async function Y(e){return{subscribesTo:await re(e),subscribers:await We(e),children:await je(e)}}d(Y,"edgesFor");async function b(e){const n=await h(e);return await J(n,await Y(e))}d(b,"detailOf");async function ut(e){let n=(await ue(e)).length;try{n+=an(await W(e),await ae(e)).length}catch(t){if(!on(t))throw t}return n}d(ut,"countFiles");function ft(e){if(typeof e!="object"||e===null)throw w("a node create requires a JSON body");const n=e;if(n.kind!==void 0&&(typeof n.kind!="string"||n.kind.trim()===""))throw w("kind must be a non-empty string when provided");if(n.description!==void 0&&(typeof n.description!="string"||n.description.trim()===""))throw w("description must be a non-empty string when provided");if(n.creator!==void 0&&(typeof n.creator!="string"||n.creator.trim()===""))throw w("creator must be a non-empty node id when provided");if(n.scopes!==void 0&&pe(n.scopes),n.isolated!==void 0&&typeof n.isolated!="boolean")throw new f(400,"invalid_request","isolated must be a boolean",{param:"isolated"});const t=n.root_lifecycle;if(t!==void 0&&t!=="resident"&&t!=="terminal")throw w("root_lifecycle must be 'resident' or 'terminal'");if(n.request_key!==void 0&&(typeof n.request_key!="string"||n.request_key.trim()===""||n.request_key.length>128))throw w("request_key must be a non-empty string of at most 128 characters when provided");return n}d(ft,"parseCreateBody");function pt(e){if(e===void 0)return;const n=Dn(e),t=n===null?null:new Date(Date.now()+n);if(n===null||!Number.isFinite(n)||n<=0||t===null||!Number.isFinite(t.getTime()))throw new f(422,"invalid_deadline","`deadline` must be a positive representable duration");return t.toISOString()}d(pt,"parseDeadline");function mt(e){if(e!==void 0){if(typeof e!="object"||e===null||Array.isArray(e))throw new f(400,"invalid_request","`outcome_delivery` must be an object");if(typeof e.action!="string"||!Pn.test(e.action))throw new f(400,"invalid_request","`outcome_delivery.action` must be a declared action name");if(e.payload!==void 0){let n;try{n=JSON.stringify(e.payload)}catch{throw new f(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable")}if(n===void 0)throw new f(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable");if(Buffer.byteLength(n,"utf8")>le)throw new f(413,"payload_too_large",`serialized payload exceeds ${le} bytes`)}return e}}d(mt,"parseOutcomeDelivery");function wt(e,n){const t=En(e.action,n);if(!("kind"in t))return{actionName:e.action,argv:t.argv,cwd:t.cwd,configOrigin:t.configOrigin,...e.payload===void 0?{}:{payload:e.payload}};throw t.kind==="unknown_action"?new f(422,"unknown_action",`no action named ${e.action} is declared from ${n}`):new f(422,"invalid_action",`${e.action} is declared in ${t.configOrigin} but unusable: ${t.reason}`,{reason:t.reason,configOrigin:t.configOrigin})}d(wt,"resolveOutcomeDelivery");function ht(e,n,t){const o={kind:e.kind,cwd:t,prompt:e.prompt??""};return e.mode!==void 0&&(o.mode=e.mode),e.name!==void 0&&(o.name=e.name),e.description!==void 0&&(o.description=e.description),n!==void 0&&(o.parent=n),e.creator!==void 0&&(o.creator=e.creator),e.scopes!==void 0&&(o.scopes=e.scopes),e.isolated!==void 0&&(o.isolated=e.isolated),e.root===!0&&(o.root=!0),e.root_lifecycle!==void 0&&(o.rootLifecycle=e.root_lifecycle),(e.worktree===!0||typeof e.worktree=="string")&&(o.worktree=!0),typeof e.worktree=="string"&&(o.worktreeBase=e.worktree),e.worktree!==void 0&&(o.worktreeCwd=T(e.pin_cwd??e.cwd??t)),e.fork_from!==void 0&&(o.forkFrom=e.fork_from),e.model!==void 0&&(o.model=e.model),e.profile!==void 0&&(o.profile=e.profile),e.situational_context!==void 0&&(o.situationalContext=e.situational_context),e.node_id!==void 0&&(o.nodeId=e.node_id),o}d(ht,"buildRecipe");function yt(e,n,t){return!jn()||e.prefer_warm!==!0||e.root!==!0||e.parent!=null||e.root_lifecycle==="terminal"||(e.prompt??"")!==""||e.model!==void 0||e.fork_from!==void 0||e.worktree!==void 0||e.node_id!==void 0||e.scopes!==void 0||e.isolated!==void 0?null:{kind:e.kind,mode:e.mode??"base",cwd:n,profileId:t,model:e.model??null,situationalContext:e.situational_context??null,name:e.name??null,creator:e.creator??null}}d(yt,"warmRequestFor");import{resolveCreateKind as ho}from"../../../core/runtime/launch-target.js";function _e(e){const n=e.modelIntent===void 0?void 0:`${e.modelIntent.family}/${e.modelIntent.strength}`;return On(e.model,n,e.modelExact===!0)}d(_e,"requestFromLaunch");function _t(e,n){if(n===null)return e;if(e.scopes===void 0)return{...e,scopes:[...n]};const t=e.scopes.filter(o=>!O(n,o));if(t.length===0)return e;throw new f(403,"scope_denied",`scopes exceed the bearer token's ceiling: ${t.join(", ")}`,{scopes:t,ceiling:n})}d(_t,"withinCeiling");async function ge(e,n,t){const o=(await Sn(n.kind,n.mode,{lifecycle:n.lifecycle,hasManager:e.parent!==null,model:n.model,modelOverride:!0,cwd:e.cwd,profileId:e.profile_id??null})).launch;await K(_e(o),{cwd:e.cwd,profileId:e.profile_id??null},t)}d(ge,"assertNodeModelChangeRegistered");async function be(e,n){const t=e.principal;if(t.class!=="broker"&&t.class!=="cron")return;const o=it({app:t.app,resolved:await Xn(t),launched:t.scopes},n,"read");if("root"in o)return;const i="missing"in o?o.missing:null;throw new f(403,"scope_missing",i?`the child's directory ${n} needs missing scope ${i}`:`no permission of this run names the child's directory ${n}`,void 0,void 0,i?{scope:i}:void 0)}d(be,"assertCallerReaches");const H=new Map;async function gt(e,n={},t={}){const o=_t(ft(e.body),e.ceiling);if(e.principal.class==="broker"){const c=e.principal;if(o.prefer_warm===!0&&c.app!=="app:terminal"&&(o.prefer_warm=!1),o.parent!=null){const r=await h(o.parent);if(r.grantee!==c.app)throw new f(403,"other_app_run","cannot create a child under another app's node");if(!await U(c,o.parent,r.grantee??"app:terminal"))throw E(`unknown node: ${o.parent}`,{received:o.parent})}if(o.creator!==void 0&&o.creator!==c.node)throw new f(403,"other_app_run","cannot create for another node");if(o.fork_from!==void 0){let r=null;try{r=await Me(await De(o.fork_from))}catch{}if(r?.grantee!==c.app||!await U(c,r.node_id,r.grantee))throw new f(403,"other_app_run","cannot fork another app's conversation")}o.creator=c.node}if(e.principal.class==="cron"){const c=e.principal;for(const s of["parent","creator","fork_from","node_id"])if(o[s]!=null)throw new f(403,"permission_error",`a cron cannot set ${s} on a node it starts`);o.prefer_warm=!1;const r=await en({grantee:c.app,sub:c.sub,grant_id:c.grant,scopes:c.scopes});if(!r.allowed)throw new f(r.reason==="scope_missing"?403:401,r.reason==="scope_missing"?"scope_missing":"grant_revoked",r.message);if(o.scopes!==void 0){const s=o.scopes.find(p=>!O(r.effectiveScopes,p)||!O(c.scopes,p));if(s!==void 0)throw new f(403,"scope_missing",`scope ${s} is not held by this cron`,void 0,void 0,{scope:s})}else c.scopes!==null&&(o.scopes=[...c.scopes]);o.node_id=await ne(),c.isolated&&(o.isolated=!0,o.cwd=await W(o.node_id,c.app))}o.creator!==void 0&&Jn(await h(o.creator),"crtr:act");const i=o.request_key?.trim();if(i===void 0||o.node_id!==void 0&&qe(o.node_id)&&await R(o.node_id)!==null&&(await Ge(i))?.nodeId!==o.node_id)return D(e,o,n,t);const a=H.get(i);if(a!==void 0)return a;const l=bt(e,o,i,n,t);H.set(i,l);try{return await l}finally{H.delete(i)}}d(gt,"handleCreate");async function bt(e,n,t,o,i){const a=await oe(t);if(a.nodeId!==null&&await R(a.nodeId)!==null){if(a.state!=="warm_claimed"){const l=await R(a.nodeId);if(e.principal.class!=="person"&&!await me(e.principal,l.grantee??"app:terminal",a.nodeId))throw new f(409,"create_request_conflict","request_key belongs to another caller");return{status:201,body:await b(a.nodeId)}}return D(e,n,o,i,a)}try{return await D(e,n,o,i,a)}catch(l){const c=await oe(t);throw(c.nodeId===null||await R(c.nodeId)===null)&&await te(t,c.nodeId),l}}d(bt,"handleKeyedCreateRequest");async function D(e,n,t,o,i=void 0){const a=o.deadlineAt??pt(n.deadline),l=mt(n.outcome_delivery),c=e.principal.class==="cron"?await nn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes,cron_id:e.principal.cron,origin_run_id:e.principal.origin_run,isolated:e.principal.isolated,creator:(await fe(e.principal.cron))?.created_by??null},n.node_id):null;c!==null&&(n.root=c.root===!0);const r=c?.parent??n.parent??void 0;if(r==null&&n.root!==!0)throw w("a node create requires a parent (or root: true for an independent root)");let s=null;if(n.output_schema!==void 0){try{s=JSON.parse(n.output_schema)}catch{throw w("output_schema must be a JSON schema string")}if(s===null||typeof s!="object"||Array.isArray(s))throw w("output_schema must be a JSON schema object");await Tn(s)}const{profileId:p,kind:m,cwd:u,launch:k}=await Fn(n,{validateKind:Re});await be(e,u),n.worktree!==void 0&&await be(e,T(n.pin_cwd??n.cwd??u));const _={...n,kind:m},g=l===void 0?void 0:wt(l,u),X=("grant"in e.principal?e.principal.grant:null)??(r==null?null:(await R(r))?.grant_id??null);if(await K(_e(await Pe(k,X,n.model)),{cwd:u,profileId:p},t.modelRegistry),await rt(X)!=="app"&&!await ct(p))throw new f(412,"no_provider_credential",we,{error:"no_provider_credential",message:we,next:"The person running this runtime logs a provider in with `crtr sys setup`, or stores a provider key on the profile."});let M=null;{const A=i===void 0||i.nodeId===null||i.state==="warm_claimed"?yt(_,u,p):null,j=s===null&&o.beforeBrokerLaunch===void 0?void 0:async S=>{s!==null&&await Nn(S,"terminal",s),o.beforeBrokerLaunch?.(S)};if(i?.state==="warm_claimed"&&i.nodeId!==null){const S=A===null?null:await Mn(A,i.nodeId,j);if(S===null)throw await te(i.requestKey,i.nodeId),new f(503,"create_resume_failed",`could not resume warm claim ${i.nodeId}; retry the create`);return await ie(i.requestKey,S),{status:201,body:await b(S)}}if(A!==null){const S=await Ln(A,{...a===void 0?{}:{deadlineAt:a},...g===void 0?{}:{outcomeDelivery:g},...i===void 0?{}:{requestKey:i.requestKey}},j);if(S!==null)return i!==void 0&&await ie(i.requestKey,S),ce(A),{status:201,body:await b(S)};M=A}const G=i?.nodeId??n.node_id??(i===void 0?void 0:await ne());i!==void 0&&i.nodeId===null&&await Be(i.requestKey,G);const I=ht(G===void 0?_:{..._,node_id:G},r,u);c!==null&&(c.rootLifecycle!==void 0&&(I.rootLifecycle=c.rootLifecycle),c.scopes!==void 0&&_.scopes===void 0&&(I.scopes=c.scopes),c.run!==void 0&&(I.run=c.run),c.withinCreate!==void 0&&(I.withinCreate=c.withinCreate)),a!==void 0&&(I.deadlineAt=a),g!==void 0&&(I.outcomeDelivery=g),I.readiness=n.root===!0?"await":"launch";const Z=await Le(I,j,()=>{M!==null&&ce(M)});return i!==void 0&&await Fe(i.requestKey,Z.node.node_id),{status:201,body:await b(Z.node.node_id)}}}d(D,"handleCreateRequest");function v(e,n){const t=e.query.getAll(n);if(t.length>1)throw w(`${n} must appear at most once`);return t[0]}d(v,"singleQueryValue");function Q(e,n){if(e!==void 0){if(e==="1"||e==="true")return!0;if(e==="0"||e==="false")return!1;throw w(`${n} must be true or false`)}}d(Q,"optionalBoolean");function L(e,n,t){if(e!==void 0){if(t.includes(e))return e;throw w(`${n} must be one of: ${t.join(", ")}`)}}d(L,"oneOf");function vt(e){const n=new Set(["status","lifecycle","kind","mode","profile_id","profile_prefix","parent","top_level","under","under_inclusive","hanging","include"]);for(const[_]of e.query)if(!n.has(_))throw w(`unknown nodes query parameter: ${_}`);const t=L(v(e,"status"),"status",["active","idle","done","dead","canceled"]),o=L(v(e,"lifecycle"),"lifecycle",["terminal","resident"]),i=v(e,"kind"),a=L(v(e,"mode"),"mode",["base","orchestrator"]),l=v(e,"profile_id"),c=v(e,"profile_prefix"),r=v(e,"parent"),s=Q(v(e,"top_level"),"top_level"),p=v(e,"under"),m=Q(v(e,"under_inclusive"),"under_inclusive"),u=Q(v(e,"hanging"),"hanging"),k=L(v(e,"include"),"include",["activity"]);if(r!==void 0&&s===!0)throw w("parent and top_level=true cannot be combined");return{...t!==void 0?{status:t}:{},...o!==void 0?{lifecycle:o}:{},...i!==void 0?{kind:i}:{},...a!==void 0?{mode:a}:{},...l!==void 0?{profile_id:l}:{},...c!==void 0?{profile_prefix:c}:{},...r!==void 0?{parent:r}:{},...s!==void 0?{top_level:s}:{},...p!==void 0?{under:p}:{},...m!==void 0?{under_inclusive:m}:{},...u!==void 0?{hanging:u}:{},...k!==void 0?{include:k}:{}}}d(vt,"parseListQuery");async function kt(e){const n=vt(e);let t=await Ke({...n.status!==void 0?{status:n.status}:{},...n.lifecycle!==void 0?{lifecycle:n.lifecycle}:{},...n.kind!==void 0?{kind:n.kind}:{},...n.mode!==void 0?{mode:n.mode}:{},...n.profile_id!==void 0?{profileId:n.profile_id}:{},...n.profile_prefix!==void 0?{profilePrefix:n.profile_prefix}:{},...n.parent!==void 0?{parent:n.parent}:{},...n.top_level===!0?{topLevel:!0}:{},...n.under!==void 0?{under:n.under}:{},...n.under_inclusive===!0?{underInclusive:!0}:{},...n.hanging===!0?{hanging:!0}:{}});e.principal.class!=="person"&&(t=await he(t,async l=>await me(e.principal,l.grantee??"app:terminal",l.node_id)));const o=n.include==="activity"?await Ze(t.map(l=>l.node_id)):null,i=t.length>0?Ae():new Set;return{status:200,body:await ye(t,async l=>{const c=await Yn(l,l.profile_id!==null&&i.has(l.profile_id));if(o!==null){const r=await ue(l.node_id);c.activity={latest_report:r[0]??null,final_report:l.final_report===null?null:r.find(s=>s.name===l.final_report)??null,pending_human_count:o[l.node_id]??0}}return c})}}d(kt,"handleList");async function St(e){const n=e.params.id,t=await Zn(e,n,{method:"GET",path:`/v1/nodes/${encodeURIComponent(n)}`,query:qn(e)});if(t.kind==="unknown")throw nt(n,t.unreachable);if(t.kind==="remote"){if(t.status>=400)throw et(t.status,t.body);return{status:t.status,body:tt(t.body,t.machine)}}return await h(n),{status:200,body:await b(n)}}d(St,"handleDetail");async function Rt(e){const n=e.params.id;await h(n);const t=await ln(n);return{status:200,body:{node_id:n,snapshot:{messages:t.snapshot.messages,...t.snapshot.messageIds===void 0?{}:{messageIds:t.snapshot.messageIds},messageVisibility:t.snapshot.messageVisibility,turnVisibility:t.snapshot.turnVisibility,stats:t.snapshot.stats,state:t.snapshot.state,display:t.snapshot.display,...t.snapshot.queued===void 0?{}:{queued:t.snapshot.queued},...t.snapshot.toolGroupSummaries===void 0?{}:{toolGroupSummaries:t.snapshot.toolGroupSummaries},...t.snapshot.turnReplies===void 0?{}:{turnReplies:t.snapshot.turnReplies},...t.snapshot.workingActivity===void 0?{}:{workingActivity:t.snapshot.workingActivity}},commands:t.commands.map(i=>({name:i.name,description:i.description,source:i.source})),captured_at:B()}}}d(Rt,"handleSnapshot");async function It(e){const n=e.params.id,t=await un(n);if(t===null)throw E(`unknown node: ${n}`,{received:n});return{status:200,body:t}}d(It,"handleSubject");function Ot(e){for(const[a]of e.query)if(a!=="cursor"&&a!=="limit")throw w(`unknown messages query parameter: ${a}`);const n=e.query.getAll("cursor"),t=e.query.getAll("limit");if(n.length>1)throw w("cursor must appear at most once");if(t.length>1)throw w("limit must appear at most once");if(t.length===0)return n.length===0?{}:{cursor:n[0]};const o=t[0],i=Number(o);if(!Number.isSafeInteger(i)||o==="")throw w("limit must be an integer from 1 through 500");return n.length===0?{limit:i}:{cursor:n[0],limit:i}}d(Ot,"parseMessagesQuery");async function At(e){const n=e.params.id,t=await dn(n,Ot(e));return{status:200,body:{node_id:n,messages:t.messages,...t.messageIds===void 0?{}:{message_ids:t.messageIds},message_visibility:t.messageVisibility,next_cursor:t.nextCursor,captured_at:B()}}}d(At,"handleMessages");async function $t(e){const n=e.params.id;await h(n);const t=await sn(n);return{status:200,body:{node_id:n,session_file:t.sessionFile,session_jsonl:t.sessionJsonl,system_prompt:t.systemPrompt,captured_at:B()}}}d($t,"handleSession");async function Ct(e){const n=e.params.id;return await h(n),{status:200,body:{node_id:n,markdown:await cn(n)}}}d(Ct,"handleTranscript");async function Nt(e){const n=e.params.id;await h(n);const t=d(async(i,a)=>({node_id:i,label:(await R(i))?.name??i,dir:await ae(i),self:a,files:await ut(i)}),"root"),o=await he([await t(n,!0),...await ye(await re(n),async i=>await t(i.node_id,!1))],async i=>await Tt(i.node_id,i.dir)||i.files>0);return{status:200,body:{node_id:n,roots:o}}}d(Nt,"handleContext");async function Tt(e,n){try{return tn(await W(e),n)}catch(t){if(rn(t))return!1;throw t}}d(Tt,"hasDir");async function Pt(e){const n=await h(e.params.id),t=e.principal.class==="broker"?e.principal.scopes:e.ceiling,o=t===null?void 0:n.scopes==null?[...t]:[...new Set([...n.scopes.filter(a=>O(t,a)),...t.filter(a=>O(n.scopes,a))])],i=await Ee(n.node_id,o,e.principal.class==="broker"?e.principal.node:void 0);return{status:201,body:await b(i.node.node_id)}}d(Pt,"handleFork");const Et=1e4;async function Dt(e){const n=e.params.id;await h(n);const t=e.body,o=t?.resume??!0,i=t?.reopen??!1;await yn(n,i,e.principal.class==="broker"?e.principal.node:null);const a=!o&&t?.cron_id!==void 0?await fe(t.cron_id):null,l=await Te(n,{resume:o,capacity:"refuse",...a!==null?{wakeReason:await xn(a,"schedule_revive")}:{}});l.outcome!=="already-live"&&await Ve(n,{failures:0,notBefore:null});const c=(await R(n))?.status??"active",r=(l.launch??P().get(n)?.handle)?.exited,s=l.outcome!=="frozen"&&await se(n,r,Et);return{status:200,body:Hn(n,l,c,s)}}d(Dt,"handleRevive");async function Lt(e){const n=e.params.id;return{status:200,body:{newNodeId:(await hn(n))?.newNodeId??null}}}d(Lt,"handleRelaunchRoot");async function Mt(e){const n=e.params.id;await h(n);const t=e.body;if(t?.caller_pid!==void 0&&(!Number.isInteger(t.caller_pid)||t.caller_pid<=0))throw w("caller_pid must be a positive integer when provided");const o=zn(),i=new Promise(l=>{e.res.once("close",()=>l())}),a=await Ne(n,{teardownAfter:i,rootEvent:t?.finish===!0?"finish":"cancel",callerPid:t?.caller_pid,...e.principal.class==="broker"?{closer:e.principal.node}:{},...o===null?{}:{registerDetached:o.registerDetached}});return{status:200,body:Un(a)}}d(Mt,"handleClose");async function jt(e){if(!Qn(e.principal))throw new f(403,"permission_error","only the person marks a conversation read");const n=e.params.id;return await h(n),await ze(n),{status:204}}d(jt,"handleMarkRead");async function Gt(e){const n=e.params.id;await h(n);const t=await wn(n);return{status:200,body:{recycled:t.recycled,finalized:t.finalized,new_root:t.newRoot,delivered:t.delivered}}}d(Gt,"handleRecycle");async function Ft(e){const n=e.params.id;return await h(n),await x(n,{lifecycle:"terminal"}),await $(e,n,["lifecycle"]),{status:200,body:await b(n)}}d(Ft,"handleDemote");async function Bt(e,n={}){const t=e.params.id,o=await h(t),i=e.body??{};i.kind!==void 0&&await z(i.kind,o.cwd,o.profile_id??null),i.model!==void 0&&await ge(o,{kind:i.kind??o.kind,mode:"orchestrator",lifecycle:i.resident===!0?"resident":o.lifecycle,model:i.model},n.modelRegistry);const a=await pn(t,{...i.kind!==void 0?{kind:i.kind}:{},...i.resident===!0?{resident:!0}:{},...i.model!==void 0?{model:i.model}:{}});await $(e,t,["mode",...i.kind!==void 0?["kind"]:[],...i.resident===!0?["lifecycle"]:[],...i.model!==void 0?["model"]:[]]);const l=await J(a.meta,await Y(t));return l.roadmap_written=a.roadmapWritten,l.roadmap_ref=a.roadmapRef,l.goal_path=a.goalPath,{status:200,body:l}}d(Bt,"handlePromote");async function Kt(e,n={}){const t=e.params.id,o=await h(t),i=e.body??{};i.kind!==void 0&&await z(i.kind,o.cwd,o.profile_id??null),i.model!==void 0&&await ge(o,{kind:i.kind??o.kind,mode:i.promote===!0?"orchestrator":o.mode,lifecycle:o.lifecycle,model:i.model},n.modelRegistry);const a=i.note??"Refresh: continue against your roadmap or goal.";await _n(t,a);const l=await mn(t,{...i.promote===!0?{promote:!0}:{},...i.kind!==void 0?{kind:i.kind}:{},...i.model!==void 0?{model:i.model}:{}});return{status:200,body:await J(l.meta,await Y(t))}}d(Kt,"handleYield");async function Vt(e){const n=e.params.id;await h(n);const t=e.body;if(t===void 0||typeof t.controller!="string"||t.controller.trim()==="")throw w("a wait requires a controller node id");const o=await R(t.controller);if(o===null)throw E(`unknown controller: ${t.controller}`,{received:t.controller});if(o.status!=="active"&&o.status!=="idle")throw new f(422,"unprocessable",`controller ${t.controller} is ${o.status}`);return await xe(n,t.controller),{status:200,body:await b(n)}}d(Vt,"handleWait");async function Wt(e){const n=e.principal,t=await fn(n.class==="broker"?async i=>i.grantee===n.app&&await U(n,i.node_id,i.grantee):void 0);return{status:200,body:{revived:[...t.revived],failed:[...t.failed],frozen:[...t.frozen]}}}d(Wt,"handleReviveAll");const xt=new Set(["interrupt","steer","after-turn","next-turn"]);function zt(e){if(e===null)return null;const n=d(o=>{throw new y({error:"invalid_listen",message:o,received:e,field:"listen",next:'Send an object mapping levels "1".."10" to interrupt|steer|after-turn|next-turn (e.g. {"3":"next-turn"}), or null to reset to the global default.'})},"bad");if(typeof e!="object"||Array.isArray(e))return n("listen must be an object or null");const t=Object.entries(e);if(t.length===0)return n("listen must name at least one level");for(const[o,i]of t)/^(?:[1-9]|10)$/.test(o)||n(`listen level must be 1..10: ${o}`),(typeof i!="string"||!xt.has(i))&&n(`listen delivery must be interrupt|steer|after-turn|next-turn: ${String(i)}`);return JSON.stringify(Object.fromEntries(t))}d(zt,"listenJson");async function $(e,n,t,o){t.length===0&&o===void 0||await V(async i=>{if(o?.(i),t.length===0)return;const a=await Je(i,n),l=await He(i,e),c=l.class==="person"?l.person:l.class==="app"?l.app:l.node;await Ue(i,c,a),await Ye(i,{event:"configured",object:a,actor:c,attrs:{changed:t.join(",")}})})}d($,"recordConfigured");function ve(e){const n=P().get(e);if(n===void 0)return!1;try{return process.kill(n.pid,"SIGTERM"),!0}catch{return!1}}d(ve,"restartLiveBroker");function ke(e,n){try{return q(e)===q(n)}catch{return!1}}d(ke,"sameDirectory");async function Jt(e,n={}){const t=e.params.id,o=e.body;let i;if(o===void 0)i={};else if(typeof o=="object"&&o!==null&&!Array.isArray(o))i=o;else throw new y({error:"cwd_invalid",message:"cwd repair requires a non-null object with a non-empty cwd string",received:o,field:"cwd",next:"Send a config patch with a non-empty absolute cwd string."});if(i.profile!==void 0){if(typeof i.profile!="string"||i.profile==="")throw new y({error:"profile_invalid",message:"profile must be a non-empty profile id or name",received:i.profile,field:"profile",next:"Send a profile id or exact name."});if(Object.keys(i).some(m=>m!=="profile"&&m!=="restart"&&i[m]!==void 0))throw new y({error:"profile_not_exclusive",message:"profile can only be combined with restart",field:"profile",next:"Send profile (and optionally restart) alone."});const r=await h(t),s=await Kn(e,i.profile);Vn(s.profileId);const p=Wn(s.profileId).manifest.grantee??"app:terminal";if(p!==(r.grantee??"app:terminal"))throw new y({error:"profile_grantee_mismatch",message:`profile ${s.profileId} belongs to ${p}, node ${t} to ${r.grantee}`,received:i.profile,field:"profile",next:"Pick a profile owned by the same app as the node."});return(r.profile_id??null)!==s.profileId&&(Gn(r.kind)?(await z(r.kind,r.cwd,s.profileId),await x(t,{profileId:s.profileId})):await C(t,{profile_id:s.profileId}),await $(e,t,["profile"])),i.restart===!0&&ve(t),{status:200,body:await b(t)}}if(Object.prototype.hasOwnProperty.call(i,"cwd")){const r=i.cwd;if(typeof r!="string"||r==="")throw new y({error:"cwd_invalid",message:"cwd must be a non-empty string",received:r,field:"cwd",next:"Send a non-empty absolute cwd string."});if(Object.keys(i).some(u=>u!=="cwd"&&u!=="keep_session"&&u!=="restart"&&i[u]!==void 0))throw new y({error:"cwd_not_exclusive",message:"cwd repair cannot be combined with another config field",received:i,field:"cwd",next:"Send cwd as the only config field."});const s=await h(t),p=N(s.cwd);if(i.keep_session===!0||p&&F(r)&&N(r)&&ke(s.cwd,r)){if(!F(r))throw new y({error:"cwd_not_absolute",message:`cwd must be an absolute path: ${r}`,received:r,field:"cwd",next:"Send an absolute path."});const u=T(r);let k=!1;try{k=ee(u).isDirectory()}catch{}if(!k)throw new y({error:"cwd_missing",message:`cwd is not an existing directory: ${u}`,received:r,field:"cwd",next:"Send an existing absolute directory."});if(p&&!ke(s.cwd,u))throw new y({error:"cwd_not_same_directory",message:`${u} is not the same directory as the recorded ${s.cwd}; --keep-session only relocates an alias`,received:r,field:"cwd",next:"Drop --keep-session to repair a gone cwd, or pass the real path of the recorded directory."});if(s.managed_worktree?.state==="open")throw new y({error:"cwd_node_managed_worktree_open",message:`${t} owns an open managed worktree and cannot relocate its cwd`,received:r,field:"cwd",next:"Reconcile the managed worktree first."});const _=s.cwd;return _!==u&&(await V(async g=>{await C(t,{cwd:u}),await g.prepare("UPDATE crons SET cwd = ? WHERE anchor_node = ? AND cwd = ?").run(u,t,_),await Qe(t,_,u)}),await $(e,t,["cwd"])),i.restart===!0&&ve(t),{status:200,body:await b(t)}}if(P().has(t)){const u=`${t} cannot repair its cwd while its broker is live`;throw new f(409,"cwd_node_live",u,{error:"cwd_node_live",message:u,received:r,field:"cwd",next:"Let the node become dormant, then retry the cwd repair."})}if(s.fork_from!=null)throw new y({error:"cwd_node_forked",message:`${t} is a fork and cannot repair its cwd; recorded cwd: ${s.cwd}`,received:r,field:"cwd",next:`Restore ${s.cwd}, then revive the fork normally.`});if(s.managed_worktree?.state==="open")throw new y({error:"cwd_node_managed_worktree_open",message:`${t} owns an open managed worktree and cannot repair its cwd`,received:r,field:"cwd",next:"Restore or reconcile the recorded checkout, then use the managed-worktree close or abandon flow."});if(N(s.cwd))throw new y({error:"cwd_still_present",message:`${t} recorded cwd still exists: ${s.cwd}; cwd repair is not a relocation command`,received:r,field:"cwd",next:"This command only repairs a node whose recorded cwd is gone."});if(!F(r))throw new y({error:"cwd_not_absolute",message:`cwd must be an absolute path: ${r}`,received:r,field:"cwd",next:"Send an absolute path."});const m=T(r);if(!N(m))throw new y({error:"cwd_missing",message:`cwd does not exist: ${m}`,received:r,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."});try{if(!ee(m).isDirectory())throw new y({error:"cwd_not_a_directory",message:`cwd is not a directory: ${m}`,received:r,field:"cwd",next:"Send an existing absolute directory."})}catch(u){throw u instanceof y?u:new y({error:"cwd_missing",message:`cwd does not exist: ${m}`,received:r,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."})}return await V(async()=>{await C(t,{cwd:m}),await Xe(t)}),await $(e,t,["cwd"]),{status:200,body:await b(t)}}let a=await h(t);const l=i.listen===void 0?void 0:zt(i.listen);if(i.scopes!==void 0){const r=pe(i.scopes),s=a.grantee==="app:terminal"?await at():a.grant_id?await ot(a.grant_id):null,p=a.grantee==="app:terminal"&&!lt("user").runtime?.issuer;if(!r.every(m=>O(a.scopes,m)&&(s?O(s.resolved,m):p)))throw new y({error:"unknown_scope",message:"scopes may only narrow the node's current effective set",received:r,field:"scopes",next:"Send only scopes the node currently holds."});if(r.includes("crtr:act")&&!O(r,"crtr:llm"))throw new f(400,"invalid_scope","crtr:act requires crtr:llm");a=await C(t,{scopes:r})}if(i.situational_context!==void 0&&$e(t,Ce(i.situational_context)),i.model!==void 0){const r=(await de(a.cwd,a.profile_id??null)).modelLadders,s=Rn(i.model,r),p=await dt(a.grant_id,i.model,s===void 0),m=p?{family:st,strength:p.strength}:s,u=p?p.model:In(i.model,r);await K({kind:"concrete",spec:u},{cwd:a.cwd,profileId:a.profile_id??null},n.modelRegistry);const k=P();let _=!1;if(k.isClaimed(t)&&await se(t,k.get(t)?.handle.exited)&&(_=await $n(t,u,m).then(()=>!0,g=>{if(g instanceof Cn)return!1;throw g})),!_){if(k.isClaimed(t)){const g=`${t}'s broker did not accept the model switch; the model was not changed`;throw new f(409,"broker_not_ready",g,{error:"broker_not_ready",message:g,received:i.model,field:"model",next:"Check the node with `crtr node show`, then retry the model change."})}if(!u.includes("/"))throw w(`model must resolve to a provider/id spec: ${i.model}`);await An(t,u,m)}a=await h(t)}if(i.kind!==void 0||i.mode!==void 0||i.lifecycle!==void 0){i.kind!==void 0&&await Re(i.kind);const r=i.kind??a.kind,s=i.mode??a.mode,p=i.lifecycle??a.lifecycle;let m;s==="orchestrator"&&!await vn(t)&&(m=d(async()=>{const u=await gn(t);await bn(t,u!==null&&u.trim()!==""?{goal:u.trim()}:{})},"beforePersist")),a=await x(t,{...i.kind!==void 0?{kind:r}:{},...i.mode!==void 0?{mode:s}:{},...i.lifecycle!==void 0?{lifecycle:p}:{}},m)}i.name!==void 0&&(a=await C(t,{name:i.name}),a.window&&kn(a.window,Bn(a)));const c=["scopes","situational_context","model","kind","mode","lifecycle","name","listen"].filter(r=>i[r]!==void 0);return await $(e,t,c,l===void 0?void 0:async r=>{await r.prepare("UPDATE objects SET listen_json = ? WHERE id = ? AND type = 'node'").run(l,t)}),{status:200,body:await b(t)}}d(Jt,"handleConfig");async function Se(){return(await de(process.cwd(),Oe()||null)).kinds}d(Se,"daemonKinds");async function Re(e){const n=Object.keys(await Se());if(!n.includes(e))throw w(`unknown kind: ${e}`,{received:e,next:`Valid kinds: ${n.sort().join(", ")}.`})}d(Re,"assertKindServer");async function Ut(){const e=await Se();return{status:200,body:{kinds:Object.keys(e).sort().map(t=>({kind:t,when_to_use:e[t]?.whenToUse??null,available_to:e[t]?.availableTo??null}))}}}d(Ut,"handleListKinds");const po=[{method:"POST",pattern:"/v1/nodes",scope:"crtr:act",handler:gt},{method:"GET",pattern:"/v1/nodes",handler:kt},{method:"GET",pattern:"/v1/kinds",handler:d(()=>Ut(),"handler")},{method:"POST",pattern:"/v1/nodes/revive-all",scope:"crtr:act",handler:Wt},{method:"GET",pattern:"/v1/nodes/:id",handler:St},{method:"GET",pattern:"/v1/nodes/:id/snapshot",handler:Rt},{method:"GET",pattern:"/v1/nodes/:id/subject",handler:It},{method:"GET",pattern:"/v1/nodes/:id/messages",handler:At},{method:"GET",pattern:"/v1/nodes/:id/session",handler:$t},{method:"GET",pattern:"/v1/nodes/:id/transcript",handler:Ct},{method:"GET",pattern:"/v1/nodes/:id/context",handler:Nt},{method:"POST",pattern:"/v1/nodes/:id/fork",scope:"crtr:act",handler:Pt},{method:"POST",pattern:"/v1/nodes/:id/revive",scope:"crtr:act",handler:Dt},{method:"POST",pattern:"/v1/nodes/:id/relaunch-root",scope:"crtr:act",handler:Lt},{method:"POST",pattern:"/v1/nodes/:id/close",handler:Mt},{method:"POST",pattern:"/v1/nodes/:id/read",handler:jt},{method:"POST",pattern:"/v1/nodes/:id/recycle",scope:"crtr:act",handler:Gt},{method:"POST",pattern:"/v1/nodes/:id/demote",scope:"crtr:act",handler:Ft},{method:"POST",pattern:"/v1/nodes/:id/promote",scope:"crtr:act",handler:Bt},{method:"POST",pattern:"/v1/nodes/:id/yield",scope:"crtr:act",handler:Kt},{method:"POST",pattern:"/v1/nodes/:id/wait",handler:Vt},{method:"PATCH",pattern:"/v1/nodes/:id/config",handler:Jt}];export{Jt as handleConfig,gt as handleCreate,Pt as handleFork,kt as handleList,Bt as handlePromote,Kt as handleYield,po as nodeRoutes,ho as resolveCreateKind};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var Ne=Object.defineProperty;var l=(e,t)=>Ne(e,"name",{value:t,configurable:!0});import{ApiError as
|
|
2
|
-
content unavailable: ${r.unavailable}`,data:{...f,content:"markdown"in r?"live":"unavailable"}}})}l(et,"read");function Y(e,t,a,n){if(e!=null){if(!a.includes(e)){n.push({field:t,problem:`must be one of ${a.join(", ")}`,received:e});return}return e}}l(Y,"oneOf");async function ge(e,t,a){if(t!=null){if(typeof t!="string"||!await Ce(e,t)){a.push({field:"type",problem:`must be a kind: one of ${(await De(e)).join(", ")}`,received:t});return}return t}}l(ge,"kindName");function je(e,t,a,n){if(e==null)return n;const i=Number(e);return!Number.isInteger(i)||i<1||i>100?(a.push({field:t,problem:"must be an integer from 1 to 100",received:e}),n):i}l(je,"limitOf");async function _e(e){const t=e.query,a=[],n=await ge(await I(),t.get("type"),a),i=Y(t.get("privacy"),"privacy",$e,a),o=je(t.get("limit"),"limit",a,50),r=t.get("cursor");r!==null&&!/^\d+$/.test(r)&&a.push({field:"cursor",problem:"is not a cursor from a previous page",received:r});const s=t.get("deleted");return s!==null&&!["0","1","true","false"].includes(s)&&a.push({field:"deleted",problem:"must be true or false",received:s}),a.length>0&&C("object list request",a),await _(async c=>{const u=await v(c,e),f={...n?{type:n}:{},...i?{privacy:i}:{},...t.get("kind")!==null?{kind:t.get("kind")}:{},...s==="1"||s==="true"?{deleted:!0}:{}},h=t.get("owner");if(h!==null){const w=await he(c,u,h);if(w===null)return{items:[],next_cursor:null};f.owner=w}const b=t.get("name"),j=[];let m=r===null?0:Number(r),p=!0;for(;j.length<o&&p;){const w=await Me(c,u,f,{limit:100,after_seq:m});p=w.length===100;for(const q of w)if(m=q.seq,(b===null||q.name.startsWith(b))&&j.push(q),j.length===o)break}const d=j.at(-1);return{items:await we(j,async w=>await me(c,w)),next_cursor:j.length===o&&d?String(d.seq):null}})}l(_e,"listLocal");const tt={ms:3e4},nt="self",X=l((e,t)=>e.map(a=>({...a,machine:t})),"labelItems");async function ke(e,t,a,n){const i=W.current;try{const o=await i.call(t.id,{method:"GET",path:"/v1/objects",query:a},await i.envelope(e.principal),n);if(Ye(o))return{state:"unreachable",items:[],next:null};if(o.status===426)return{state:"too_old",items:[],next:null};const r=o.body;return o.status!==200||!Array.isArray(r?.items)?{state:"unreachable",items:[],next:null}:{state:"ok",items:X(r.items,t),next:r.next_cursor??null}}catch{return{state:"unreachable",items:[],next:null}}}l(ke,"remotePage");async function at(e){const t=new Promise(a=>e.addEventListener("abort",()=>a(null),{once:!0}));return Promise.race([W.current.machines().catch(()=>null),t])}l(at,"machinesWithin");async function it(e){const t=e.query.get("machine");if(!fe(e.principal)||!W.current.joined()||t===nt)return await _e(ve(e));const n=W.current.self(),i=t!==null&&A(t)!==n,o=i?null:await _e(ve(e)),r=new AbortController,s=setTimeout(()=>r.abort(),tt.ms);s.unref?.();try{const c=await at(r.signal);if(c===null){if(i)throw new y(503,"runtime_unreachable",`The directory did not answer, so ${A(t)} cannot be listed.`,{machine:A(t)});return o}const u=l(d=>({id:d,name:c.find(w=>w.machine_id===d)?.name??d}),"labelOf"),f=u(n),h=c.filter(d=>d.machine_id!==n&&d.kind!=="device"),b=D(e);if(t!==null){if(!i)return{items:X(o.items,f),next_cursor:o.next_cursor,machines:[{machine_id:n,name:f.name,state:"ok",more:o.next_cursor!==null}]};const d=A(t);if(!h.some(q=>q.machine_id===d))throw new y(404,"not_found",`no machine ${d} on your account`,{machine:d});const w=await ke(e,u(d),b,r.signal);return{items:w.items,next_cursor:w.next,machines:[{machine_id:d,name:u(d).name,state:w.state,more:w.next!==null}]}}const{cursor:j,...m}=b,p=await Promise.all(h.map(d=>ke(e,u(d.machine_id),m,r.signal)));return{items:[...X(o.items,f),...p.flatMap(d=>d.items)],next_cursor:o.next_cursor,machines:[{machine_id:n,name:f.name,state:"ok",more:o.next_cursor!==null},...h.map((d,w)=>({machine_id:d.machine_id,name:d.name,state:p[w].state,more:p[w].next!==null}))]}}finally{clearTimeout(s)}}l(it,"list");function ve(e){const t=new URLSearchParams(e.query);return t.delete("machine"),{...e,query:t}}l(ve,"withoutMachine");async function rt(e){const t=[],a=N(e.body)?e.body:{};N(e.body)||t.push({field:"body",problem:"must be an object",received:e.body});const n=a.text;(typeof n!="string"||n.trim()==="")&&t.push({field:"text",problem:"is required",received:n});const i=a.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(s){t.push({field:"grep",problem:`is not a valid regular expression: ${s.message}`,received:i})}a.body!==void 0&&typeof a.body!="boolean"&&t.push({field:"body",problem:"must be a boolean",received:a.body});const o=await ge(await I(),a.type,t);a.kind!==void 0&&typeof a.kind!="string"&&t.push({field:"kind",problem:"must be a string",received:a.kind}),a.owner!==void 0&&typeof a.owner!="string"&&t.push({field:"owner",problem:"must be a string",received:a.owner});const r=je(a.limit,"limit",t,50);return t.length>0&&C("object search request",t),await _(async s=>{const c=await v(s,e),u=typeof a.owner=="string"?await he(s,c,a.owner):null;if(typeof a.owner=="string"&&u===null)return{items:[]};const f=await se(s,c,{text:n,body:a.body===!0||typeof i=="string"}),h=new Set(f.map(m=>m.object.id)),j=(typeof i=="string"?(await se(s,c,{text:i,grep:!0})).filter(m=>h.has(m.object.id)):f).filter(m=>(!o||m.object.type===o)&&(a.kind===void 0||m.object.kind===a.kind)&&(!u||m.object.owner===u.id));return{items:await we(j.slice(0,r),async m=>({object:await me(s,m.object),...m.line!==void 0?{matches:[{line:m.line,text:m.text??""}]}:{}}))}})}l(rt,"search");const Oe=["interrupt","steer","after-turn","next-turn"],ot=/^(\*|>|[A-Za-z0-9_-]+)(\.(\*|>|[A-Za-z0-9_-]+))*$/;function st(e,t){if(e!==void 0){if(!Array.isArray(e)||e.length===0||e.some(a=>typeof a!="string"||!ot.test(a)||a.includes(">")&&!a.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(st,"patternsOf");function ut(e,t){if(e===void 0)return;if(!N(e)||Object.keys(e).length===0){t.push({field:"config",problem:"must map levels 1-10 to a delivery",received:e});return}const a=[];for(const[n,i]of Object.entries(e)){const o=Number(n);!Number.isInteger(o)||o<1||o>10?t.push({field:`config.${n}`,problem:"level must be an integer from 1 to 10"}):Oe.includes(i)?a.push({threshold:o,delivery:i}):t.push({field:`config.${n}`,problem:`delivery must be one of ${Oe.join(", ")}`,received:i})}return a.sort((n,i)=>i.threshold-n.threshold)}l(ut,"configOf");async function qe(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(qe,"refuseStubWatch");async function x(e,t){const a=await E(e,t)??await(async()=>{const n=await Fe(e,await Ae(e,{class:"person"},async()=>[]),t);return"object"in n?n.object:null})();return a===null||a.deleted_at!==null||!(a.type.includes(".")||a.type==="node")?null:a}l(x,"findWatcher");const O=l(()=>{throw new y(404,"not_found","watcher not found")},"watcherNotFound");async function Ee(e,t,a){if(typeof t!="string"||a===void 0||a===null)return null;if(typeof a!="string")throw new y(400,"invalid_request","invalid watcher_machine");if(await x(e,t)!==null)return null;const n=W.current;let i;try{i=await n.machines()}catch{throw new y(503,"runtime_unreachable","The directory did not answer, so the watcher machine cannot be checked.")}const o=A(a);return(o===n.self()||!i.some(r=>A(r.machine_id)===o))&&O(),o}l(Ee,"remoteWatcherMachine");async function Te(e,t,a,n){if(t.publishToken||t.principal.class==="anonymous")return O();const i=await v(e,t);if(i.class==="person")return O();let o;try{o=await T(e,i,n)}catch{return O()}const r=o.owner===null?null:await E(e,o.owner);return r===null||!await ie(e,i,r)||!await Le(e,i,"link",a)?O():(await z(e,t,a),{actor:i.app,object:o})}l(Te,"linkWatcher");async function Re(e,t,a){if(t.publishToken)return!0;if(t.principal.class==="anonymous"||!a.type.includes("."))return!1;const n=await v(e,t),i=a.owner===null?null:await E(e,a.owner);return i!==null&&await ie(e,n,i)&&await We(e,n,a)}l(Re,"ownsWatcher");async function ct(e){const t=k(e),a=N(e.body)?e.body:{};if(typeof a.watcher=="string"&&await qe(t),typeof a.watcher!="string"){const i=await Q(e,t,{method:"POST",path:`${G(t)}/watch`,body:e.body});if(i.kind==="unknown")throw de(t,i.unreachable);if(i.kind==="remote"){if(i.status>=400)throw M(i.status,i.body);const o=i.body.patterns;return await be(e,i.body,i.machine.id,"ask",o),Z(i.body,i.machine)}}const n=await Ee(await I(),a.watcher,a.watcher_machine);return await lt(e,n)}l(ct,"watch");async function lt(e,t=null){const a=[],n=e.body===void 0?{}:N(e.body)?e.body:(a.push({field:"body",problem:"must be an object"}),{}),i=st(n.patterns,a),o=ut(n.config,a);return n.filter!==void 0&&!N(n.filter)&&a.push({field:"filter",problem:"must be an object",received:n.filter}),n.watcher!==void 0&&typeof n.watcher!="string"&&a.push({field:"watcher",problem:"must be a custom-object ref",received:n.watcher}),n.watcher_machine!==void 0&&typeof n.watcher_machine!="string"&&a.push({field:"watcher_machine",problem:"must be a machine id",received:n.watcher_machine}),a.length>0&&C("watch request",a),await _(async r=>{const s=typeof n.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,...o?{config:o}:{},...n.filter!==void 0?{filter:n.filter}:{}};if(s){const j=n.watcher,m=t===null?null:await ft(r,e,j,t),p=m??(e.publishToken?await re(r,j):await x(r,j));if(p===null)return O();if(m===null&&await Re(r,e,p)){const q=await oe(r,e,p);await z(r,e,p);const ee=await U(r,p),R=await T(r,ee,k(e));if(!await ae(r,ee,R))throw new y(404,"not_found","object not found");return await L(r,p,R,"act",u),await P(r,p,R),await H(r,{event:"watched",object:R,actor:q,attrs:{}},await F(r)),{object:await $(r,R,await g(r,R)),patterns:c}}const{actor:d,object:w}=await Te(r,e,p,k(e));return await L(r,p,w,"act",{...u,linkApp:d}),await P(r,p,w),await H(r,{event:"watched",object:w,actor:d,attrs:{}},await F(r)),{object:await $(r,w,await g(r,w)),patterns:c}}const f=await v(r,e),h=await K(r,e)??te(f),b=await T(r,f,k(e));return await L(r,h,b,"ask",u),await P(r,h,b),await H(r,{event:"watched",object:b,actor:h,attrs:{}},await F(r)),{object:await $(r,b,await g(r,b)),patterns:c}})}l(lt,"watchLocal");async function ft(e,t,a,n){return t.publishToken||t.principal.class==="anonymous"?O():await Je(e,a,n)}l(ft,"ensureNodeStubFor");async function Ie(e,t,a,n){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,a.id)).n,"count"),o=await i();await Ue(e,t,a,n??void 0);const r=await i();return r===0&&await Ve(e,t,a),{object:await
|
|
1
|
+
var Ne=Object.defineProperty;var l=(e,t)=>Ne(e,"name",{value:t,configurable:!0});import{ApiError as p}from"../../../api/errors.js";import{PRIVACY_LEVELS_DTO as $e}from"../../../api/dto/objects.js";import{openDb as E,withCanvasWrite as j}from"../../../core/canvas/db.js";import{actorOf as te}from"../../../core/graph/documents.js";import{readerForObject as U,readerFor as Ae}from"../../../core/graph/reader.js";import{resolveName as Fe,stubsNamed as Se,unwrapRef as ne}from"../../../core/graph/names.js";import{canRead as ae,customReachable as We,holdsObjectsScope as Le,mayActAs as ie}from"../../../core/graph/access.js";import{customObjectRef as re,authorizeCustom as oe,fenceArgv as z}from"./custom-objects.js";import{edgesOf as Pe}from"../../../core/graph/edges.js";import{publish as H}from"../../../core/graph/events.js";import{objectDTO as b}from"../../../core/graph/kinds/dto.js";import{isKind as Ce,kindNames as De,kindOf as V}from"../../../core/graph/kinds/registry.js";import{listObjects as Me,searchObjects as se}from"../../../core/graph/objects.js";import{checkoutsForCwd as Ge}from"../../../core/graph/repo-sync/repos.js";import{recordWatch as L,unwatch as Ue,watchersOf as ze,watchesOf as He}from"../../../core/graph/watches.js";import{armObjectWaitForWatch as P,settleObjectWaitForUnwatch as Ve}from"../../../core/graph/object-waits.js";import{readerFor as v,resolveRef as B}from"../reader.js";import{readDelegationFor as ue,runReadLeaf as Be}from"../../plugin-objects/read-leaf.js";import{invalidRequest as C,isRecord as N}from"./validate.js";import{callerNode as ce,callerStub as K,forwardNamed as Ke,forwardVerb as le,originates as fe,queryWithoutMachine as D,relayError as M,remoteStub as Ze,unknownError as de,forwarding as W,withMachine as Z}from"../../peer/forward.js";import{getById as T}from"../../../core/graph/objects.js";import{ensureNodeStub as Je,isStub as J,stubOf as Qe,withKindDecl as $}from"../../../core/peer/stubs.js";import{isUnreachable as Ye}from"../../../core/peer/client.js";import{normalizeMachineId as A}from"../../../core/peer/directory.js";import{mapAsync as we}from"../../../core/async-array.js";const F=l(async e=>async t=>await Ge(e,t),"reposFnFor");async function me(e,t){return{...await b(e,t),...await(await V(e,t.type)).listItem?.(e,t)}}l(me,"listItem");function Xe(e){return`no object named ${e} on this runtime; it may be on another runtime, or the name may be misspelled`}l(Xe,"remoteMessage");async function he(e,t,a){try{return await R(e,t,a)}catch(n){if(n instanceof p&&n.code==="not_found"&&!n.details?.deleted)return null;throw n}}l(he,"ownerFilter");async function R(e,t,a){const n=await B(e,t,a);if("remote"in n)throw new p(404,"not_found",Xe(n.remote),{next:"Check the name with `crtr canvas search`."});if("deleted"in n)throw new p(404,"not_found",`${n.deleted.name} is deleted`,{id:n.deleted.id,deleted:!0});return n}l(R,"liveRef");const _=l(e=>decodeURIComponent(e.params.ref),"refParam"),pe=l(e=>{const t=ne(e),a=t.indexOf("/");return a>0?t.slice(0,a):t},"idOf");async function ye(e){const t=await T(await E(),pe(e));return t!==null&&await J(await E(),t)?t.id:null}l(ye,"stubIdOf");async function xe(e,t){const a=await ye(e);return a!==null?{stub:a,hit:!0}:await j(async n=>{try{const i=await v(n,t),o=l(async()=>{const s=await Se(n,i,e);if(s.length>1)throw new p(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 r;try{r=await B(n,i,e)}catch(s){if(s instanceof p&&s.code==="not_found")return await o();throw s}if("remote"in r)return await o();if("deleted"in r){const s=await o();return s.stub===null?{stub:null,hit:!0}:s}return{stub:await J(n,r)?r.id:null,hit:!0}}catch(i){if(i instanceof p&&i.code==="ambiguous_name")throw i;return{stub:null,hit:!1}}})}l(xe,"resolvedLocally");async function Q(e,t,a){const n=await xe(t,e);if(n.stub!==null)return le(e,n.stub,a);if(n.hit||!fe(e.principal))return{kind:"local"};const i=await le(e,pe(t),a);return i.kind==="local"&&ne(t).includes("/")?Ke(e,a,{method:"GET",path:G(t),query:D(e)}):i}l(Q,"forwardRef");const G=l(e=>`/v1/objects/${encodeURIComponent(e)}`,"objectPath");async function be(e,t,a,n,i){const o=t?.object;!o?.id||!o.type||await j(async r=>{const s=await ce(r,e);if(s===null)return;const c=await Ze(r,o.id,o.type,a,{kind:o.kind??null,name:o.name??o.id,...o.preview===void 0?{}:{preview:o.preview},owner:o.owner??null,...o.kind_decl===void 0?{}:{kind_decl:o.kind_decl},...o.privacy===void 0?{}:{privacy:o.privacy}});await L(r,s,c,n,i===void 0?{}:{patterns:i}),n==="ask"&&await P(r,s,c)})}l(be,"watchRemote");async function et(e){const t=_(e),a=["1","true"].includes(e.query.get("fields")??""),n=await Q(e,t,{method:"GET",path:G(t),query:D(e)});if(n.kind==="unknown")return{state:"unknown",ref:t,unreachable:n.unreachable};if(n.kind==="remote"){if(n.status>=400)throw M(n.status,n.body);return await be(e,n.body,n.machine.id,"read"),Z(n.body,n.machine)}const i=l(async s=>{const c=await K(s,e);return c===null?await v(s,e):await U(s,c,await F(s))},"readerOf"),o=await j(async s=>{const c=await i(s),u=await B(s,c,t);if("remote"in u)return{state:"remote",remote:u.remote};if("deleted"in u)return{state:"deleted",deleted:await b(s,u.deleted)};const w=await ue(s,u);if(w!==null)return{delegate:w,hitId:u.id};const{data:h,text:y}=await(await V(s,u.type)).read(s,c,u,{fields:a,reposFn:await F(s)});return{state:"object",type:u.type,object:await $(s,u,await b(s,u)),text:y,data:h}});if(!("delegate"in o))return o;const r=await Be(o.delegate,e.principal);return await j(async s=>{const c=await i(s),u=await T(s,o.hitId);if(u===null)throw new p(404,"not_found","object not found");if(u.deleted_at!==null)return{state:"deleted",deleted:await b(s,u)};if(!await ae(s,c,u))throw new p(404,"not_found","object not found");const{data:w,text:h}=await(await V(s,u.type)).read(s,c,u,{fields:a,reposFn:await F(s)});return await ue(s,u)===null?{state:"object",type:u.type,object:await $(s,u,await b(s,u)),text:h,data:w}:{state:"object",type:u.type,object:await $(s,u,await b(s,u)),text:"markdown"in r?r.markdown:`${h}
|
|
2
|
+
content unavailable: ${r.unavailable}`,data:{...w,content:"markdown"in r?"live":"unavailable"}}})}l(et,"read");function Y(e,t,a,n){if(e!=null){if(!a.includes(e)){n.push({field:t,problem:`must be one of ${a.join(", ")}`,received:e});return}return e}}l(Y,"oneOf");async function ge(e,t,a){if(t!=null){if(typeof t!="string"||!await Ce(e,t)){a.push({field:"type",problem:`must be a kind: one of ${(await De(e)).join(", ")}`,received:t});return}return t}}l(ge,"kindName");function je(e,t,a,n){if(e==null)return n;const i=Number(e);return!Number.isInteger(i)||i<1||i>100?(a.push({field:t,problem:"must be an integer from 1 to 100",received:e}),n):i}l(je,"limitOf");async function _e(e){const t=e.query,a=[],n=await ge(await E(),t.get("type"),a),i=Y(t.get("privacy"),"privacy",$e,a),o=je(t.get("limit"),"limit",a,50),r=t.get("cursor");r!==null&&!/^\d+$/.test(r)&&a.push({field:"cursor",problem:"is not a cursor from a previous page",received:r});const s=t.get("deleted");return s!==null&&!["0","1","true","false"].includes(s)&&a.push({field:"deleted",problem:"must be true or false",received:s}),a.length>0&&C("object list request",a),await j(async c=>{const u=await v(c,e),w={...n?{type:n}:{},...i?{privacy:i}:{},...t.get("kind")!==null?{kind:t.get("kind")}:{},...s==="1"||s==="true"?{deleted:!0}:{}},h=t.get("owner");if(h!==null){const m=await he(c,u,h);if(m===null)return{items:[],next_cursor:null};w.owner=m}const y=t.get("name"),g=[];let k=r===null?0:Number(r),f=!0;for(;g.length<o&&f;){const m=await Me(c,u,w,{limit:100,after_seq:k});f=m.length===100;for(const q of m)if(k=q.seq,(y===null||q.name.startsWith(y))&&g.push(q),g.length===o)break}const d=g.at(-1);return{items:await we(g,async m=>await me(c,m)),next_cursor:g.length===o&&d?String(d.seq):null}})}l(_e,"listLocal");const tt={ms:3e4},nt="self",X=l((e,t)=>e.map(a=>({...a,machine:t})),"labelItems");async function ke(e,t,a,n){const i=W.current;try{const o=await i.call(t.id,{method:"GET",path:"/v1/objects",query:a},await i.envelope(e.principal),n);if(Ye(o))return{state:"unreachable",items:[],next:null};if(o.status===426)return{state:"too_old",items:[],next:null};const r=o.body;return o.status!==200||!Array.isArray(r?.items)?{state:"unreachable",items:[],next:null}:{state:"ok",items:X(r.items,t),next:r.next_cursor??null}}catch{return{state:"unreachable",items:[],next:null}}}l(ke,"remotePage");async function at(e){const t=new Promise(a=>e.addEventListener("abort",()=>a(null),{once:!0}));return Promise.race([W.current.machines().catch(()=>null),t])}l(at,"machinesWithin");async function it(e){const t=e.query.get("machine");if(!fe(e.principal)||!W.current.joined()||t===nt)return await _e(ve(e));const n=W.current.self(),i=t!==null&&A(t)!==n,o=i?null:await _e(ve(e)),r=new AbortController,s=setTimeout(()=>r.abort(),tt.ms);s.unref?.();try{const c=await at(r.signal);if(c===null){if(i)throw new p(503,"runtime_unreachable",`The directory did not answer, so ${A(t)} cannot be listed.`,{machine:A(t)});return o}const u=l(d=>({id:d,name:c.find(m=>m.machine_id===d)?.name??d}),"labelOf"),w=u(n),h=c.filter(d=>d.machine_id!==n&&d.kind!=="device"),y=D(e);if(t!==null){if(!i)return{items:X(o.items,w),next_cursor:o.next_cursor,machines:[{machine_id:n,name:w.name,state:"ok",more:o.next_cursor!==null}]};const d=A(t);if(!h.some(q=>q.machine_id===d))throw new p(404,"not_found",`no machine ${d} on your account`,{machine:d});const m=await ke(e,u(d),y,r.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:g,...k}=y,f=await Promise.all(h.map(d=>ke(e,u(d.machine_id),k,r.signal)));return{items:[...X(o.items,w),...f.flatMap(d=>d.items)],next_cursor:o.next_cursor,machines:[{machine_id:n,name:w.name,state:"ok",more:o.next_cursor!==null},...h.map((d,m)=>({machine_id:d.machine_id,name:d.name,state:f[m].state,more:f[m].next!==null}))]}}finally{clearTimeout(s)}}l(it,"list");function ve(e){const t=new URLSearchParams(e.query);return t.delete("machine"),{...e,query:t}}l(ve,"withoutMachine");async function rt(e){const t=[],a=N(e.body)?e.body:{};N(e.body)||t.push({field:"body",problem:"must be an object",received:e.body});const n=a.text;(typeof n!="string"||n.trim()==="")&&t.push({field:"text",problem:"is required",received:n});const i=a.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(f){t.push({field:"grep",problem:`is not a valid regular expression: ${f.message}`,received:i})}a.body!==void 0&&typeof a.body!="boolean"&&t.push({field:"body",problem:"must be a boolean",received:a.body});const o=await ge(await E(),a.type,t);a.kind!==void 0&&typeof a.kind!="string"&&t.push({field:"kind",problem:"must be a string",received:a.kind}),a.owner!==void 0&&typeof a.owner!="string"&&t.push({field:"owner",problem:"must be a string",received:a.owner});const r=je(a.limit,"limit",t,50);t.length>0&&C("object search request",t);const s=await j(async f=>{const d=await v(f,e),m=typeof a.owner=="string"?await he(f,d,a.owner):null;return{reader:d,owner:m}}),{reader:c,owner:u}=s;if(typeof a.owner=="string"&&u===null)return{items:[]};const w=await E(),h=await se(w,c,{text:n,body:a.body===!0||typeof i=="string"}),y=new Set(h.map(f=>f.object.id)),k=(typeof i=="string"?(await se(w,c,{text:i,grep:!0})).filter(f=>y.has(f.object.id)):h).filter(f=>(!o||f.object.type===o)&&(a.kind===void 0||f.object.kind===a.kind)&&(!u||f.object.owner===u.id));return{items:await we(k.slice(0,r),async f=>({object:await me(w,f.object),...f.line!==void 0?{matches:[{line:f.line,text:f.text??""}]}:{}}))}}l(rt,"search");const Oe=["interrupt","steer","after-turn","next-turn"],ot=/^(\*|>|[A-Za-z0-9_-]+)(\.(\*|>|[A-Za-z0-9_-]+))*$/;function st(e,t){if(e!==void 0){if(!Array.isArray(e)||e.length===0||e.some(a=>typeof a!="string"||!ot.test(a)||a.includes(">")&&!a.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(st,"patternsOf");function ut(e,t){if(e===void 0)return;if(!N(e)||Object.keys(e).length===0){t.push({field:"config",problem:"must map levels 1-10 to a delivery",received:e});return}const a=[];for(const[n,i]of Object.entries(e)){const o=Number(n);!Number.isInteger(o)||o<1||o>10?t.push({field:`config.${n}`,problem:"level must be an integer from 1 to 10"}):Oe.includes(i)?a.push({threshold:o,delivery:i}):t.push({field:`config.${n}`,problem:`delivery must be one of ${Oe.join(", ")}`,received:i})}return a.sort((n,i)=>i.threshold-n.threshold)}l(ut,"configOf");async function qe(e){const t=await ye(e);if(t!==null)throw new p(400,"invalid_request",`${t} lives on another machine; an explicit watcher cannot watch it from here`,{id:t})}l(qe,"refuseStubWatch");async function x(e,t){const a=await T(e,t)??await(async()=>{const n=await Fe(e,await Ae(e,{class:"person"},async()=>[]),t);return"object"in n?n.object:null})();return a===null||a.deleted_at!==null||!(a.type.includes(".")||a.type==="node")?null:a}l(x,"findWatcher");const O=l(()=>{throw new p(404,"not_found","watcher not found")},"watcherNotFound");async function Ee(e,t,a){if(typeof t!="string"||a===void 0||a===null)return null;if(typeof a!="string")throw new p(400,"invalid_request","invalid watcher_machine");if(await x(e,t)!==null)return null;const n=W.current;let i;try{i=await n.machines()}catch{throw new p(503,"runtime_unreachable","The directory did not answer, so the watcher machine cannot be checked.")}const o=A(a);return(o===n.self()||!i.some(r=>A(r.machine_id)===o))&&O(),o}l(Ee,"remoteWatcherMachine");async function Te(e,t,a,n){if(t.publishToken||t.principal.class==="anonymous")return O();const i=await v(e,t);if(i.class==="person")return O();let o;try{o=await R(e,i,n)}catch{return O()}const r=o.owner===null?null:await T(e,o.owner);return r===null||!await ie(e,i,r)||!await Le(e,i,"link",a)?O():(await z(e,t,a),{actor:i.app,object:o})}l(Te,"linkWatcher");async function Re(e,t,a){if(t.publishToken)return!0;if(t.principal.class==="anonymous"||!a.type.includes("."))return!1;const n=await v(e,t),i=a.owner===null?null:await T(e,a.owner);return i!==null&&await ie(e,n,i)&&await We(e,n,a)}l(Re,"ownsWatcher");async function ct(e){const t=_(e),a=N(e.body)?e.body:{};if(typeof a.watcher=="string"&&await qe(t),typeof a.watcher!="string"){const i=await Q(e,t,{method:"POST",path:`${G(t)}/watch`,body:e.body});if(i.kind==="unknown")throw de(t,i.unreachable);if(i.kind==="remote"){if(i.status>=400)throw M(i.status,i.body);const o=i.body.patterns;return await be(e,i.body,i.machine.id,"ask",o),Z(i.body,i.machine)}}const n=await Ee(await E(),a.watcher,a.watcher_machine);return await lt(e,n)}l(ct,"watch");async function lt(e,t=null){const a=[],n=e.body===void 0?{}:N(e.body)?e.body:(a.push({field:"body",problem:"must be an object"}),{}),i=st(n.patterns,a),o=ut(n.config,a);return n.filter!==void 0&&!N(n.filter)&&a.push({field:"filter",problem:"must be an object",received:n.filter}),n.watcher!==void 0&&typeof n.watcher!="string"&&a.push({field:"watcher",problem:"must be a custom-object ref",received:n.watcher}),n.watcher_machine!==void 0&&typeof n.watcher_machine!="string"&&a.push({field:"watcher_machine",problem:"must be a machine id",received:n.watcher_machine}),a.length>0&&C("watch request",a),await j(async r=>{const s=typeof n.watcher=="string";if((e.publishToken||e.principal.class==="anonymous")&&!s)throw new p(404,"not_found","watcher not found");const c=i??[">"],u={patterns:c,...o?{config:o}:{},...n.filter!==void 0?{filter:n.filter}:{}};if(s){const g=n.watcher,k=t===null?null:await ft(r,e,g,t),f=k??(e.publishToken?await re(r,g):await x(r,g));if(f===null)return O();if(k===null&&await Re(r,e,f)){const q=await oe(r,e,f);await z(r,e,f);const ee=await U(r,f),I=await R(r,ee,_(e));if(!await ae(r,ee,I))throw new p(404,"not_found","object not found");return await L(r,f,I,"act",u),await P(r,f,I),await H(r,{event:"watched",object:I,actor:q,attrs:{}},await F(r)),{object:await $(r,I,await b(r,I)),patterns:c}}const{actor:d,object:m}=await Te(r,e,f,_(e));return await L(r,f,m,"act",{...u,linkApp:d}),await P(r,f,m),await H(r,{event:"watched",object:m,actor:d,attrs:{}},await F(r)),{object:await $(r,m,await b(r,m)),patterns:c}}const w=await v(r,e),h=await K(r,e)??te(w),y=await R(r,w,_(e));return await L(r,h,y,"ask",u),await P(r,h,y),await H(r,{event:"watched",object:y,actor:h,attrs:{}},await F(r)),{object:await $(r,y,await b(r,y)),patterns:c}})}l(lt,"watchLocal");async function ft(e,t,a,n){return t.publishToken||t.principal.class==="anonymous"?O():await Je(e,a,n)}l(ft,"ensureNodeStubFor");async function Ie(e,t,a,n){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,a.id)).n,"count"),o=await i();await Ue(e,t,a,n??void 0);const r=await i();return r===0&&await Ve(e,t,a),{object:await b(e,a),removed:o-r}}l(Ie,"dropWatch");async function dt(e){const t=_(e);if(e.query.get("watcher")&&await qe(t),!e.query.get("watcher")){const n=await Q(e,t,{method:"DELETE",path:`${G(t)}/watch`,query:D(e)});if(n.kind==="unknown")throw de(t,n.unreachable);if(n.kind==="remote"){const i=n.status===404?n.body.error?.details:void 0,o=i?.deleted===!0?i.id:void 0;if(n.status>=400&&o===void 0)throw M(n.status,n.body);const r=o??n.body.object?.id,s=await j(async c=>{const u=await ce(c,e),w=r===void 0?null:await T(c,r);return u===null||w===null||!await J(c,w)?null:await Ie(c,u,w,e.query.get("pattern"))});if(o!==void 0){if(s===null)throw M(n.status,n.body);return{...s,machine:n.machine}}return Z(n.body,n.machine)}}const a=await Ee(await E(),e.query.get("watcher")??void 0,e.query.get("watcher_machine")??void 0);return await wt(e,a)}l(dt,"unwatchRoute");async function wt(e,t=null){const a=e.query.get("pattern"),n=e.query.get("watcher");return await j(async i=>{if((e.publishToken||e.principal.class==="anonymous")&&!n)throw new p(404,"not_found","watcher not found");const o=l(async(c,u)=>await Ie(i,c,u,a),"drop");if(n){const c=t===null?null:await(async()=>{const w=await T(i,n);return w!==null&&await Qe(i,w)!==null?w:null})();if(t!==null&&c===null)return O();const u=c??(e.publishToken?await re(i,n):await x(i,n));return u===null?O():c===null&&await Re(i,e,u)?(await oe(i,e,u),await z(i,e,u),await o(u,await R(i,await U(i,u),_(e)))):await o(u,(await Te(i,e,u,_(e))).object)}const r=await v(i,e),s=await K(i,e)??te(r);return await o(s,await R(i,r,_(e)))})}l(wt,"unwatchLocal");async function mt(e){const t=[],a=Y(e.query.get("type"),"type",["acted-on","links-to","watches"],t),n=Y(e.query.get("direction"),"direction",["out","in"],t);return t.length>0&&C("edges request",t),await j(async i=>{const o=await v(i,e),r=await R(i,o,_(e)),s=[];if(a!=="watches")for(const c of await Pe(i,o,r,{...a?{type:a}:{},direction:n??"both"}))s.push({type:c.type,src:await b(i,c.src),dst:await b(i,c.dst),...c.written_as?{written_as:c.written_as}:{}});if(a===void 0||a==="watches"){const c=[...n!=="in"?await He(i,o,r):[],...n!=="out"?await ze(i,o,r):[]];for(const u of c)s.push({type:"watches",src:await b(i,u.watcher),dst:await b(i,u.object)})}return{items:s}})}l(mt,"edges");const S=l(e=>async t=>({status:200,body:await e(t)}),"ok"),Gt=[{method:"POST",pattern:"/v1/objects/search",handler:S(rt)},{method:"GET",pattern:"/v1/objects/:ref/edges",handler:S(mt)},{method:"POST",pattern:"/v1/objects/:ref/watch",handler:S(ct)},{method:"DELETE",pattern:"/v1/objects/:ref/watch",handler:S(dt)},{method:"GET",pattern:"/v1/objects/:ref",handler:S(et)},{method:"GET",pattern:"/v1/objects",handler:S(it)}];export{tt as listBudget,Gt as objectRoutes};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var b=Object.defineProperty;var o=(e,t)=>b(e,"name",{value:t,configurable:!0});import{
|
|
1
|
+
var b=Object.defineProperty;var o=(e,t)=>b(e,"name",{value:t,configurable:!0});import{validateFinalPageResponses as v}from"../../../core/human/tickets.js";import{getRequest as p}from"../../../core/human/requests.js";import{RunError as u}from"../../../core/runs/operations.js";import{getQuestion as O,manifestOf as R,questionObject as l,questionResult as A,questionStatus as m}from"../../../core/runs/questions.js";import{answerPageTicket as S,cancelHumanTicket as j}from"../../human/finish.js";import{requireRun as E}from"../router.js";const H=new Set(["question.get","question.answer","question.cancel"]),f=o(()=>new u(404,"not_found","question not found"),"missing");async function q(e,t,n){const s=await O(t);if(s===null)throw f();if(s.run===null){if(s.approval?.grantee!==e.grantee||s.approval.sub!==e.sub)throw f()}else try{await E(e,s.run.run_id,n)}catch(r){throw r instanceof u&&r.code==="not_found"?f():r}return s}o(q,"reachable");async function w(e){const t=await p(e);return new u(409,"run_settled",`question ${e} is already ${m(t)}`,void 0,{state:m(t),result:A(t)})}o(w,"settled");function P(e){return/slot(?: id)? "([^"]+)"/.exec(e)?.[1]??"responses"}o(P,"slotParam");async function U(e,t,n,s){if(e==="question.get"){const{request:a,run:c,approval:_}=await q(t,n,!1);return{status:200,body:await l(a,c,_)}}const{request:r,run:y,approval:g}=await q(t,n,!0);if(r.state==="settled")throw await w(n);if(e==="question.cancel"){if((await j(r.request_id,{reason:"dismissed from the app",actor:t.grantee,disposition:"dismissed"})).outcome==="already_settled")throw await w(n);return{status:200,body:await l(await p(n),y,g)}}const i=s&&typeof s=="object"&&!Array.isArray(s)?s.responses:void 0;if(!i||typeof i!="object"||Array.isArray(i))throw new u(400,"invalid_request","responses must be an object keyed by slot id","responses");try{v(R(r),i)}catch(a){const c=a instanceof Error?a.message:String(a);throw new u(400,"invalid_request",c,P(c))}const d=await S(r.request_id,i,{actor:t.grantee});if(d.outcome==="already_settled")throw await w(n);const h=d.result;return{status:200,body:{question_id:r.request_id,status:"answered",answer:h.responses}}}o(U,"handleQuestionOperation");export{H as QUESTION_OPERATIONS,U as handleQuestionOperation};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { NodeMeta } from '../../core/canvas/types.js';
|
|
1
2
|
import { type Cron } from '../../core/canvas/crons.js';
|
|
2
3
|
export type CronEnd = {
|
|
3
4
|
kind: 'expired';
|
|
@@ -5,6 +6,8 @@ export type CronEnd = {
|
|
|
5
6
|
kind: 'canceled';
|
|
6
7
|
by: string;
|
|
7
8
|
};
|
|
9
|
+
/** Can this node still take waking mail: not being deleted (nor its run root), and either has a cycle ahead or is parked. */
|
|
10
|
+
export declare function canTakeMail(node: NodeMeta): Promise<boolean>;
|
|
8
11
|
/** Delete `c` and, in the same write, mail its node sink the reason. Returns
|
|
9
12
|
* whether this call deleted the row. `end: null` deletes silently (the sink
|
|
10
13
|
* node canceled it itself). The notice is skipped when the sink is not a node
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)}}r(N,"endedMessage");async function C(e){return e.deleting_at!=null||e.run_id!=null&&(await c(e.run_id))?.deleting_at!=null}r(C,"runIsDeleting");async function K(e,n,
|
|
1
|
+
var f=Object.defineProperty;var r=(e,n)=>f(e,"name",{value:n,configurable:!0});import{getNode as c}from"../../core/canvas/canvas.js";import{withCanvasWrite as h}from"../../core/canvas/db.js";import{CRON_RUN_RING as p,cancelCron as y,listCronRuns as m,parseCronSink as g}from"../../core/canvas/crons.js";import{hasNoNaturalCycle as w}from"../../core/runtime/revive-all.js";import{isParked as $}from"../../core/runtime/lifecycle.js";import{enqueueNodeMail as _}from"../../core/feed/messages.js";import{cadenceDisplay as x}from"../../core/wake.js";const v=/^delivered: /,k=r(e=>e.exit_code===0&&(e.stdout_head??"")==="","EMPTY_OK");function b(e){return e.filter(n=>n.exit_code!==null||!(n.delivered??"").startsWith("skipped"))}r(b,"realRuns");function N(e,n,d){const t=b(d),o=d.length>=p,a=t.find(u=>v.test(u.delivered??"")),l=n.kind==="expired"?`expired at ${e.expires_at} and was deleted${e.held?" while held by exit 75; the pending occurrence never fired":""}`:`was canceled by ${n.by} and deleted`,s=[`Cron "${e.name}" (${e.cron_id}, ${e.recur!=null?x(e.recur):"one-shot"}) ${l}. It was delivering to you, so nothing will wake you from it any more.`,"",`- runs made: ${o?`at least ${t.length} (the run log keeps the last ${p} entries)`:t.length}`,a!==void 0?`- last delivery to you: ${a.finished}`:`- last delivery to you: ${o?`none in its last ${t.length} runs`:"never delivered"}`],i=t[0];if(i!==void 0){const u=i.exit_code===null?"no exit observed":i.exit_code===-1?"timed out":`exit ${i.exit_code}`;s.push(`- last run: ${i.finished} \u2014 ${u} \u2014 ${i.delivered??""}`)}return t.length>0&&t.every(k)&&s.push("","Every recorded run exited 0 with empty stdout, so the check may have been failing without saying so. A pipeline exits with its last stage, so `probe 2>&1 | grep \u2026 | jq \u2026` hides a failed probe unless the command starts with `set -o pipefail`."),{label:`\u23F0 cron ${e.name} ${n.kind==="expired"?"expired":"canceled"}`,body:s.join(`
|
|
2
|
+
`)}}r(N,"endedMessage");async function C(e){return e.deleting_at!=null||e.run_id!=null&&(await c(e.run_id))?.deleting_at!=null}r(C,"runIsDeleting");async function R(e){return!await C(e)&&(!w(e)||$(e))}r(R,"canTakeMail");async function K(e,n,d){const t=g(e.sink),o=t?.kind==="node"?await c(t.node):null,a=n!==null&&o!==null&&await R(o);return await h(async()=>{const l=a?await m(e.cron_id):[];if(!await y(e.cron_id,d))return!1;if(a){const{label:s,body:i}=N(e,n,l);await _(o.node_id,{kind:"message",label:s,senderNodeId:null,bodyJson:{body:i},urgency:"normal",wake:1,idempotencyKey:`cron-ended:${e.cron_id}`})}return!0})}r(K,"endCron");export{R as canTakeMail,K as endCron};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Cron } from '../../core/canvas/crons.js';
|
|
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. */
|
|
5
|
+
export declare function narrowedNotice(c: Cron, message: string): Promise<CronSinkResolution>;
|
|
@@ -0,0 +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 +1 @@
|
|
|
1
|
-
var p=Object.defineProperty;var c=(e,u)=>p(e,"name",{value:u,configurable:!0});import{consumeCron as
|
|
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};
|