@north-light/cli 0.3.557 → 0.3.559
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/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/exec.d.ts +10 -0
- package/dist/api/dto/exec.js +0 -0
- package/dist/api/error-codes.d.ts +15 -0
- package/dist/api/error-codes.js +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -1
- package/dist/api/routes.d.ts +1 -0
- package/dist/api/routes.js +1 -1
- package/dist/build-root.js +1 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +8 -0
- package/dist/builtin-memory/crouter-sdk/resources.md +1 -1
- package/dist/clients/attach/viewer.js +130 -130
- package/dist/core/canvas/crons.d.ts +4 -1
- package/dist/core/canvas/crons.js +5 -5
- package/dist/core/canvas/schema-pg/004-cron-armed-from-exec.sql +6 -0
- package/dist/core/canvas/schema-version.d.ts +1 -1
- package/dist/core/canvas/schema-version.js +1 -1
- package/dist/core/io.js +5 -5
- package/dist/core/model-auth/readiness.d.ts +12 -0
- package/dist/core/model-auth/readiness.js +1 -0
- package/dist/core/runtime/broker/fault-retry.d.ts +10 -3
- package/dist/core/runtime/broker/fault-retry.js +1 -1
- package/dist/core/runtime/broker.js +1 -1
- package/dist/core/runtime/promote.d.ts +2 -2
- package/dist/core/runtime/promote.js +1 -1
- package/dist/daemon/api/app-listener.js +13 -13
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/exec.d.ts +6 -0
- package/dist/daemon/api/handlers/exec.js +1 -0
- package/dist/daemon/api/handlers/modelauth.js +1 -1
- package/dist/daemon/api/handlers/nodes.js +1 -1
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/router.d.ts +11 -4
- package/dist/daemon/cron/armed-context.d.ts +2 -1
- package/dist/daemon/cron/armed-context.js +1 -1
- package/dist/daemon/cron/authority.d.ts +16 -4
- package/dist/daemon/cron/authority.js +1 -1
- package/dist/daemon/cron/escalation.js +3 -3
- package/dist/daemon/cron/live-runs.d.ts +12 -3
- package/dist/daemon/cron/run-socket.js +1 -1
- package/dist/daemon/cron/sinks.js +2 -2
- package/dist/daemon/cron-run.js +2 -2
- package/dist/daemon/reconcilers/action-lane.js +1 -1
- package/docs/sdk/release-notes.md +8 -0
- package/docs/sdk/resources.md +1 -1
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
var
|
|
2
|
-
data: ${JSON.stringify(
|
|
1
|
+
var Q=Object.defineProperty;var s=(e,n)=>Q(e,"name",{value:n,configurable:!0});import{randomUUID as W}from"node:crypto";import{createServer as q}from"node:http";import{Router as V,causedByOf as K}from"./router.js";import{toErrorBody as Z}from"./map.js";import{objectRoutes as ee}from"./handlers/objects.js";import{customObjectRoutes as te}from"./handlers/custom-objects.js";import{checkRoutes as re}from"./handlers/check.js";import{chargeRoutes as ne}from"./handlers/charges.js";import{docRoutes as ae}from"./handlers/docs.js";import{relationRoutes as oe}from"./handlers/relations.js";import{schemaRoutes as ie}from"./handlers/schemas.js";import{OPERATIONS as se}from"./operations.js";import{runtimeReadiness as ue}from"../readiness.js";import{errorCodes as X}from"../../api/error-codes.js";import{MINIMUM_RUNTIME_VERSION_HEADER as ce,RUNTIME_VERSION_HEADER as de,minimumRuntimeRefusal as le}from"../../api/runtime-version.js";import{CONTRACT_HEADER as me,CONTRACT_LEVEL as $,REQUIRED_CONTRACT_HEADER as fe,contractRefusal as pe}from"../../api/contract.js";import{crtrVersion as J}from"../../shared/crtr-version.js";import{newTraceId as he,parseTraceparent as we}from"../../shared/traceparent.js";import{AppPrincipalResolver as ye}from"./principal.js";import{isDaemonRoute as _e,runtimeOwner as ve,servePeer as z}from"../peer/ingress.js";import{applySyncNotice as ge}from"./sync-notice.js";import{ownMachineId as Re}from"../../core/peer/directory.js";import{handleRunOperation as Oe,integer as Ee}from"./handlers/runs.js";import{llmCallerOf as be,parseLlmRequest as Ae,runLlm as Te}from"../../core/llm/run.js";import{parseUsageMonth as Se,usageFor as Ne}from"../../core/ledger/index.js";import{capFields as Ce}from"./handlers/llm.js";import{CONTROL_BODY_MAX_BYTES as Ie,MODEL_CONTEXT_BODY_MAX_BYTES as Pe}from"../../core/llm/body-limits.js";import{RunError as m,runIsRunning as ke}from"../../core/runs/operations.js";import{requireRun as L}from"./router.js";import{deferHead as Me}from"./deferred-head.js";import{readRunEvents as G,registerGrantStream as De,subscribeRunEvents as He}from"../../core/runs/events.js";import{activeGrant as Y,payerOfGrant as xe}from"../../core/grants/mirror.js";async function Be(e){if(await xe(e)==="app")throw new m(401,"grant_revoked","this app-paid grant is no longer active");return null}s(Be,"appPaidGone");import{releaseGrantWaiters as je}from"../../core/grants/watch.js";import{emitEvent as Je}from"../../core/events/emit.js";import{runNotices as Le}from"../run-notices.js";import{handleVendorSignIn as Ue,isVendorSignInPath as Ve}from"./vendor-signin.js";import{listAppProfiles as Xe}from"./handlers/app-profiles.js";import{handleTransferDownload as $e,handleTransferOperation as ze,TRANSFER_OPERATIONS as Ge}from"./handlers/transfer.js";import{handleQuestionOperation as Ye,QUESTION_OPERATIONS as Fe}from"./handlers/questions.js";import{removeVendorAccount as Qe,vendorStatus as We}from"./handlers/vendor-status.js";import{handleMemoryOperation as qe,MEMORY_OPERATIONS as Ke}from"./handlers/memory.js";import{EXEC_BODY_MAX_BYTES as Ze,handleExec as et}from"./handlers/exec.js";import{abortOnClose as F,callerFromApp as tt,handleProviderOperation as rt,PROVIDER_OPERATIONS as nt,providerFailure as at}from"./handlers/providers.js";function ot(e){return Object.prototype.hasOwnProperty.call(X,e)?X[e]:void 0}s(ot,"fixedRow");function it(e){const n=ot(e);return n?n.type:e==="stream_gap"||e==="provider_error"||e==="runtime_version_unsupported"?"invalid_request_error":"server_error"}s(it,"errorType");function st(e){if(!e||typeof e!="object"||Array.isArray(e)||!("rate_limit"in e))return e;const{rate_limit:n,...o}=e;return o}s(st,"appDetails");function j(e,n,o){const p=st(e.details);return{error:{code:e.code,message:e.message,...p===void 0?{}:{details:p},type:it(e.code),...e.param?{param:e.param}:{},origin:"runtime",retryable:e.retryable||e.code==="stream_dropped",request_id:n,...o?{run_id:o}:{},...e.scope?{scope:e.scope}:{},...e.code==="unauthorized"?{user_action:"reconnect"}:{},...Ce(e),...e.code==="all_routes_exhausted"?{retry_after_s:e.details.retry_after_s}:{}}}}s(j,"envelope");function ut(e,n,o,p,f){const d=n instanceof m?n:new m(500,"server_failure","runtime request failed");if(n instanceof m||console.error("[app-listener]",`trace_id=${p}`,n),e.headersSent&&!String(e.getHeader("Content-Type")??"").startsWith("text/event-stream")){e.destroy();return}if(e.headersSent){e.end(`event: error
|
|
2
|
+
data: ${JSON.stringify(j(d,o,f))}
|
|
3
3
|
|
|
4
|
-
`);return}e.writeHead(d.status,{"Content-Type":"application/json; charset=utf-8","Request-Id":o,"X-Content-Type-Options":"nosniff",...d.code==="all_routes_exhausted"?{"Retry-After":String(d.details.retry_after_s)}:{}}),e.end(JSON.stringify(
|
|
4
|
+
`);return}e.writeHead(d.status,{"Content-Type":"application/json; charset=utf-8","Request-Id":o,"X-Content-Type-Options":"nosniff",...d.code==="all_routes_exhausted"?{"Retry-After":String(d.details.retry_after_s)}:{}}),e.end(JSON.stringify(j(d,o,f)))}s(ut,"fail");function U(e){return Array.isArray(e)?e[0]:e}s(U,"headerValue");function g(e,n,o){e.writeHead(n,{"Content-Type":"application/json; charset=utf-8"}),e.end(JSON.stringify(o))}s(g,"json");async function C(e,n=Ie){const o=[];let p=0;for await(const f of e){if(p+=f.length,p>n)throw new m(400,"invalid_request","request body too large");o.push(f)}if(p)try{return JSON.parse(Buffer.concat(o).toString("utf8"))}catch{throw new m(400,"invalid_request","invalid JSON body")}}s(C,"bodyOf");function ct(e,n){const o=e.headers["last-event-id"]??n.get("after");if(Array.isArray(o))throw new m(400,"invalid_request","invalid cursor","after");return Ee(o??null,"after",0,Number.MAX_SAFE_INTEGER,0)}s(ct,"cursor");function dt(e){return`id: ${e.sequence_number}
|
|
5
5
|
event: ${e.type}
|
|
6
6
|
data: ${JSON.stringify(e)}
|
|
7
7
|
|
|
8
|
-
`}
|
|
8
|
+
`}s(dt,"frame");const lt=6e4;async function mt(e,n,o,p,f,d,b,I={},D){await L(p,o);let w=await G(o,f);if(f>w.latest_sequence)throw new m(400,"invalid_request","cursor is beyond the run","after");if(f<w.latest_sequence&&(w.earliest_sequence===null||f<w.earliest_sequence-1))throw new m(409,"stream_gap","events have expired",void 0,{earliest_sequence:w.earliest_sequence,latest_sequence:w.latest_sequence});if(n.destroyed||e.destroyed)return n.destroy(),Promise.resolve();n.writeHead(200,{"Content-Type":b==="sse"?"text/event-stream":"application/x-ndjson","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),b==="sse"?n.write(`: connected
|
|
9
9
|
|
|
10
|
-
`):n.flushHeaders();let T=f;const r=await
|
|
11
|
-
data: ${JSON.stringify(
|
|
10
|
+
`):n.flushHeaders();let T=f;const r=await Le().streamOpened(o);let t=!1,y=!1,E=!1,A;const P=new Promise(c=>{A=c}),v=s(()=>{t||(t=!0,r(T).catch(c=>console.error("[app-listener]",...D===void 0?[]:[`trace_id=${D}`],c)),clearInterval(N),clearInterval(O),M(),x(),n.off("close",v),n.off("drain",h),n.end(),A())},"end"),h=s(()=>{E=!1,a()},"onDrain"),R=s(c=>{t||n.write(b==="sse"?`event: error
|
|
11
|
+
data: ${JSON.stringify(j(c,d,o))}
|
|
12
12
|
|
|
13
|
-
`:`${JSON.stringify({type:"error",...
|
|
14
|
-
`),
|
|
15
|
-
`),T=
|
|
13
|
+
`:`${JSON.stringify({type:"error",...j(c,d,o)})}
|
|
14
|
+
`),v()},"error"),a=s(async()=>{if(!(t||y)){y=!0;try{if(!await S()){H();return}if(await L(p,o),w=await G(o,T),w.backlog_count>1e3||w.backlog_bytes>32*1024*1024){R(new m(503,"stream_dropped","stream reader fell behind",void 0,{last_sequence:T}));return}for(const c of w.events){if(E)break;E=!n.write(b==="sse"?dt(c):`${JSON.stringify(c)}
|
|
15
|
+
`),T=c.sequence_number}}catch(c){R(c instanceof m?c:new m(500,"server_failure","stream failed"))}finally{y=!1}}},"flush"),H=s(()=>R(new m(401,"grant_revoked","grant is not active")),"revoked"),S=s(async()=>"class"in p||await Y(p.grant)!==null,"grantActive"),M=await He(o,a),x="class"in p?()=>{}:De(p.grant,H),N=setInterval(()=>{!t&&!E&&b==="sse"&&n.write(`: keepalive
|
|
16
16
|
|
|
17
|
-
`)},15e3),
|
|
17
|
+
`)},15e3),u=I.idleMs??lt,B=I.isRunning??ke;let i=Date.now(),_=T;const l=s(async()=>{if(t||b!=="sse")return;const c=Date.now();let k=!0;try{k=await B(o)}catch{}if(!t){if(k||T!==_){i=c,_=T;return}c-i<u||E||(n.write(`event: idle
|
|
18
18
|
data: {}
|
|
19
19
|
|
|
20
|
-
`),
|
|
20
|
+
`),v())}},"checkIdle"),O=setInterval(()=>{l().catch(c=>Je({level:"error",event:"api.run_stream.idle_check_failed",error:c}))},I.idleCheckMs??1e3);return O.unref(),n.on("drain",h),n.on("close",v),await a(),P}s(mt,"streamRunEvents");async function nr(e,n,o,p={}){const f=e.listener;if(!e.issuer||!e.audience||!f?.address||!Number.isInteger(f.port)||f.port<1||f.port>65535)throw new Error("runtime issuer, audience and listener address/port are required");if(["0.0.0.0","::","[::]"].includes(f.address))throw new Error("runtime listener must bind a specific address");const d=new ye(e.issuer,e.audience,o,e.jwksUrl),b=new V,I=new Map;for(const r of se.filter(t=>t.stage!==void 0)){const t=s(()=>({status:500}),"handler");b.register({method:r.method,pattern:r.path,handler:t}),I.set(t,r)}const D=new V().registerAll(ee).registerAll(te).registerAll(ae).registerAll(ie).registerAll(oe).registerAll(re).registerAll(ne),w=q((r,t)=>{(async()=>{Me(r,t,p.deferHead);const y=W();t.setHeader("Request-Id",y);const E=we(U(r.headers.traceparent))??he();t.setHeader(de,J()),t.setHeader(me,$);let A;try{const P=U(r.headers[ce.toLowerCase()]),v=le(P,J())??pe(U(r.headers[fe.toLowerCase()]),$,{legacyMinimum:P,runtimeVersion:J()});if(v!==null)throw v.status===400?new m(400,v.code,v.message,v.param):new m(426,v.code,v.message,void 0,v.details);const h=new URL(r.url??"/","http://runtime.local");if(Ve(h.pathname)){await Ue(r,t,h,d);return}const R=b.match(r.method??"GET",h.pathname),a=R?I.get(R.handler):void 0,H=a?.name==="runtime.sync-notice"||a?.name==="runtime.vendor-status"||a?.name==="runtime.vendor-account-remove";let S,M;if(!H&&r.headers.authorization!==void 0)try{S=await d.verifyCall(r.headers.authorization)}catch(i){M=i}const x={issuer:e.issuer,jwks:d.jwks,audience:s(async()=>e.audience,"audience"),owner:ve,self:Re,syncGrants:o,ready:s(async()=>d.isReady()&&await n(),"ready")};if(S?.chain[0]?.sub.startsWith("runtime:")){await z(r,t,x,S);return}if(S===void 0&&a===void 0&&await _e(r.method??"GET",h.pathname)){await z(r,t,x,{status:401,code:"unauthorized",message:M===void 0?"a machine token is required":"invalid machine token"});return}if(a?.name==="ready"){const i=await n(),_=await ue({jwksReady:d.isReady(),grantsReady:s(()=>i,"grantsReady")});g(t,_.ready?200:503,_);return}if((r.method!=="GET"||h.pathname!=="/v1/health")&&(!d.isReady()||!await n()))throw new m(503,"runtime_starting","runtime_starting");if(!R||!a)throw new m(404,"not_found","unknown operation");A=R.params.run_id;const N=K(r.headers);if(a.name==="health"){g(t,200,{status:"ok",api_version:"2026-09-23"});return}if(a.name==="runtime.sync-notice"){await ge(await d.verifySyncNotice(r.headers.authorization),o,y),t.writeHead(204),t.end();return}if(a.name==="runtime.vendor-status"){await d.verifyDirectoryRead(r.headers.authorization,"vendor_status"),g(t,200,We());return}if(a.name==="runtime.vendor-account-remove"){await d.verifyDirectoryRead(r.headers.authorization,"vendor_account_remove"),g(t,200,await Qe(await C(r)));return}if(S===void 0)throw M??new m(401,"unauthorized","a directory access token is required");const u=await d.resolveVerified(S,a.scope);if(t.setHeader("Request-Id",y),A&&await L(u,A,["run.message","run.interrupt","run.cancel","run.rename","run.delete"].includes(a.name),a.name==="run.delete"),a.stage==="objects"){const i=D.match(a.method,h.pathname);if(!i)throw new m(404,"not_found","unknown operation");const _={...N===void 0?{}:{causedBy:N},method:a.method,path:h.pathname,params:i.params,query:h.searchParams,body:await C(r,i.maxBodyBytes),ceiling:u.resolved,principal:{class:"app",app:u.grantee,scopes:u.resolved,sub:u.sub},req:r,res:t};let l;try{l=await i.handler(_)}catch(O){const{status:c,body:k}=Z(O);throw c>=500&&console.error("[app-listener]",`trace_id=${E}`,O),new m(c,k.error.code,k.error.message,void 0,k.error.details)}if(t.headersSent)return;if(l.rawBody!==void 0||l.status===204||l.body===void 0){t.writeHead(l.status,l.headers??{}),t.end(l.rawBody);return}t.writeHead(l.status,{"Content-Type":"application/json; charset=utf-8",...l.headers??{}}),t.end(JSON.stringify(l.body));return}if(a.stream==="bytes"){await $e(r,t,u,h.searchParams);return}if(a.stream){await mt(r,t,A,u,ct(r,h.searchParams),y,"sse",p.stream,E);return}if(a.name==="llm"){const i=Ae(await C(r,Pe)),_=new AbortController,l=s(()=>_.abort(),"abort");t.on("close",l);try{const O=await Te(i,_.signal,i.stream?c=>{t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.write(`data: ${JSON.stringify(c)}
|
|
21
21
|
|
|
22
|
-
`)}:void 0,void 0,{grantee:
|
|
22
|
+
`)}:void 0,void 0,{grantee:u.grantee,sub:u.sub,spend_cap:u.spend_cap,request:y},be(await Y(u.grant)??await Be(u.grant)),N);i.stream?(t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.end(`data: [DONE]
|
|
23
23
|
|
|
24
|
-
`)):g(t,200,O.completion)}finally{t.off("close",l)}return}if(a.name==="usage.get"){g(t,200,await
|
|
24
|
+
`)):g(t,200,O.completion)}finally{t.off("close",l)}return}if(a.name==="exec"){const{signal:i,release:_}=F(t);try{g(t,200,await et(u,await C(r,Ze),i))}finally{_()}return}if(a.name==="usage.get"){g(t,200,await Ne(u.grantee,Se(h.searchParams.get("month"))));return}if(a.name==="profile.list"){g(t,200,Xe(u));return}if(Fe.has(a.name)){const i=await Ye(a.name,u,R.params.question_id,a.method==="POST"?await C(r):void 0);g(t,i.status,i.body);return}if(nt.has(a.name)){const{signal:i,release:_}=F(t);try{const l=await rt(a.name,tt(u),{params:R.params,body:a.method==="POST"?await C(r):void 0,query:h.searchParams},{requestId:y,traceId:E,signal:i,...N===void 0?{}:{causedBy:N}});g(t,l.status,l.body)}catch(l){const O=at(l,y,!0);t.writeHead(O.status,{"Content-Type":"application/json; charset=utf-8","X-Content-Type-Options":"nosniff",...O.headers}),t.end(JSON.stringify(O.body))}finally{_()}return}if(Ke.has(a.name)){const i=await qe(a.name,{class:"app",app:u.grantee,scopes:u.resolved},h.searchParams);g(t,i.status,i.body);return}if(Ge.has(a.name)){const i=await ze(a,u,R.params,h.searchParams,await C(r));g(t,i.status,i.body);return}const B=await Oe(a,u,A,h.searchParams,await C(r),r.headers["idempotency-key"],N);g(t,B.status,B.body)}catch(P){ut(t,P,y,E,A)}})()});return{ready:new Promise((r,t)=>{w.once("error",t),w.listen(f.port,f.address,()=>{w.off("error",t),r()})}),close:s(async()=>{d.close(),je(),w.closeAllConnections(),await new Promise((r,t)=>w.close(y=>y?t(y):r()))},"close")}}s(nr,"createAppListener");export{lt as RUN_STREAM_IDLE_MS,nr as createAppListener,ct as cursor,mt as streamRunEvents};
|
|
@@ -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{assertScope as Z}from"../../../core/scopes.js";import{InputError as a}from"../../../core/io.js";import{nextSlotAfter as wn}from"../../../core/wake.js";import{executeCron as _n,markSelfCanceledCronRun as hn}from"../../cron-run.js";import{toCronDTO as k,toCronRunDTO as H}from"../map.js";import{appOf as T,assertCeiling as mn,canReadNode as gn}from"../router.js";import{checkCronBirth as yn,checkCronFire as bn}from"../../cron/authority.js";import{endCron as vn}from"../../cron/ended.js";import{requireUsableProfile as kn}from"./profiles.js";import{filterAsync as Sn,mapAsync as $n}from"../../../core/async-array.js";import{personId as On}from"../../../core/canvas/person.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 O(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(O,"requireEnum");async function Cn(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(Cn,"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{wn(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 gn(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 u=typeof e.cwd=="string"&&e.cwd.trim()!==""?z(e.cwd):null;if(!cn()){if(u===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 u}const h=o!==null&&await _(o)!==null?o:void 0,d=t(async q=>await ln(n,q,h,i,void 0,l),"reaches");if(u!==null&&await d(u))return u;const s=sn(n).files;if(u!==null&&e.cwd_is_default!==!0)throw new c(400,"invalid_request",`cwd ${u} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${s}`,{cwd:u,default:s},void 0,{param:"cwd"});const p=r===null?null:(await _(r))?.cwd??null;return p!==null&&await d(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??On(),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 bn({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,u=i?.sub??o.sub,h=l!=null?await dn(l):await pn(n,u);if(h===null)throw new c(401,"grant_revoked","the node grant is not active");const d=i?.scopes??o.scopes;return{sub:h.sub,grant_id:h.id,scopes:d===null?null:[...d],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,u=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,d=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,s=O(n.on_output,"on_output",["silent","on-failure","always","on-change"],d?.startsWith("node:")?"on-change":"on-failure"),p=O(n.overlap,"overlap",["skip","queue","replace"],"skip"),q=O(n.tier,"tier",["normal","urgent","critical"],"normal"),D=s==="always"||s==="on-change";if(D&&d===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&&d!==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=d!==null?await Cn(d):"{}";if(d?.startsWith("spawn:")&&(mn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const w=await yn({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"),d?.startsWith("spawn:")&&Z(m,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new c(400,"invalid_request","isolated must be a boolean");if(m?.isolated&&n.isolated===!1)throw new c(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const W=m?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(W&&n.isolated===!1)throw new c(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const 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 kn(e,n.profile)).profileId:null,x=O(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:u,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 Sn((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 $n(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 C(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(C,"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 C(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 C(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 C(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 C(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 _n(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&&hn(n,o);return r!==null&&await vn(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 an,cronFolder as sn}from"../../../core/layout.js";import{appProcessesSandboxed as cn,cwdIsMounted as ln}from"../../../core/spaces/process.js";import{activeGrant as dn,activeGrantForApp as un,grantFor as pn,terminalGrant as fn}from"../../../core/grants/mirror.js";import{assertScope as Z}from"../../../core/scopes.js";import{InputError as s}from"../../../core/io.js";import{nextSlotAfter as wn}from"../../../core/wake.js";import{executeCron as _n,markSelfCanceledCronRun as hn}from"../../cron-run.js";import{toCronDTO as k,toCronRunDTO as H}from"../map.js";import{appOf as T,assertCeiling as mn,canReadNode as gn}from"../router.js";import{checkCronBirth as yn,checkCronFire as bn,cronRefusalError as vn}from"../../cron/authority.js";import{endCron as kn}from"../../cron/ended.js";import{requireUsableProfile as Sn}from"./profiles.js";import{filterAsync as $n,mapAsync as On}from"../../../core/async-array.js";import{personId as Cn}from"../../../core/canvas/person.js";function E(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new s({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}t(E,"requireString");function O(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new s({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(O,"requireEnum");async function In(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new s({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(await _(n)===null)throw new s({error:"sink_node_not_found",message:`sink node ${n} does not exist on the canvas`,field:"sink",next:"Name a live node id, or use spawn:<kind> for a delivery that outlives any one node."});return JSON.stringify({node:n})}if(e.startsWith("spawn:")){const n=e.slice(6).trim();if(n==="")throw new s({error:"bad_sink",message:"spawn sink requires a node kind",field:"sink",next:'Use sink "spawn:<kind>" (e.g. spawn:general).'});return JSON.stringify({spawn:n})}throw new s({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(In,"parseSinkSpec");const qn=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function M(e,n){if(!qn.test(e)||Number.isNaN(Date.parse(e)))throw new s({error:`bad_${n}`,message:`"${e}" is not a UTC ISO instant`,field:n,next:'Send a resolved UTC instant like "2026-06-07T09:00:00.000Z" (the client resolves the time grammar).'});return e}t(M,"requireUtcIso");function Nn(e){try{wn(e,new Date)}catch(n){throw new s({error:"bad_recur",message:`recur is not advanceable: ${n instanceof Error?n.message:String(n)}`,field:"recur",next:'Send recur JSON the daemon can advance: {"every":"6h"} or {"cron":"0 9 * * *","tz":"America/New_York"}.'})}return e}t(Nn,"requireRecur");function jn(e){let n;try{n=JSON.parse(e)}catch{throw new s({error:"bad_env_json",message:"env_json is not valid JSON",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'})}if(typeof n!="object"||n===null||Array.isArray(n))throw new s({error:"bad_env_json",message:"env_json must be a JSON object",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'});for(const[r,o]of Object.entries(n))if(typeof o!="string")throw new s({error:"bad_env_json",message:`env_json value for "${r}" is not a string`,field:"env_json",next:"Every env value must be a string."});return e}t(jn,"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 gn(e.principal,n.grantee,n.created_by??void 0)}t(J,"visibleTo");function An(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(An,"anchorNodeOf");async function Pn(e,n,r,o,i,d){const u=typeof e.cwd=="string"&&e.cwd.trim()!==""?z(e.cwd):null;if(!cn()){if(u===null)throw new s({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return u}const h=o!==null&&await _(o)!==null?o:void 0,l=t(async q=>await ln(n,q,h,i,void 0,d),"reaches");if(u!==null&&await l(u))return u;const a=an(n).files;if(u!==null&&e.cwd_is_default!==!0)throw new c(400,"invalid_request",`cwd ${u} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${a}`,{cwd:u,default:a},void 0,{param:"cwd"});const p=r===null?null:(await _(r))?.cwd??null;return p!==null&&await l(p)?p:a}t(Pn,"resolveArmedCwd");async function Tn(e,n,r){const o=e.principal;if(o.class==="person"){const a=await fn();return{sub:a?.sub??Cn(),grant_id:a?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const a=await un(n);if(a===null)throw new c(401,"grant_revoked",`no active grant for ${n}`);const p=e.ceiling??o.scopes;return{sub:a.sub,grant_id:a.id,scopes:p===null?null:[...p],origin_run_id:null}}if(o.class==="cron"){const a=await bn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!a.allowed)throw new c(a.reason==="scope_missing"?403:401,a.reason==="scope_missing"?"scope_missing":"grant_revoked",a.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?null:[...o.scopes],origin_run_id:o.origin_run,armed_from_exec:o.from_exec}}if(o.class!=="broker")throw new c(401,"unauthorized","this caller cannot arm a cron");const i=r??await _(o.node),d=i?.grant_id??o.grant,u=i?.sub??o.sub,h=d!=null?await dn(d):await pn(n,u);if(h===null)throw new c(401,"grant_revoked","the node grant is not active");const l=i?.scopes??o.scopes;return{sub:h.sub,grant_id:h.id,scopes:l===null?null:[...l],origin_run_id:i?.run_id??o.run}}t(Tn,"armPrincipal");async function En(e){const n=e.body??{},r=E(n,"name"),o=E(n,"command"),i=M(E(n,"fire_at"),"fire_at"),d=typeof n.recur=="string"&&n.recur!==""?Nn(n.recur):null,u=typeof n.expires_at=="string"&&n.expires_at!==""?M(n.expires_at,"expires_at"):null,h=typeof n.env_json=="string"&&n.env_json!==""?jn(n.env_json):null,l=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,a=O(n.on_output,"on_output",["silent","on-failure","always","on-change"],l?.startsWith("node:")?"on-change":"on-failure"),p=O(n.overlap,"overlap",["skip","queue","replace"],"skip"),q=O(n.tier,"tier",["normal","urgent","critical"],"normal"),x=a==="always"||a==="on-change";if(x&&l===null)throw new s({error:"sink_required",message:`on_output "${a}" delivers stdout every qualifying run \u2014 it needs a sink`,field:"sink",next:"Pass sink node:<node-id> (a living node's inbox), spawn:<kind> (birth a fresh node per delivery), or human (the humanloop inbox)."});if(!x&&l!==null)throw new s({error:"sink_unused",message:`on_output "${a}" never delivers stdout \u2014 a sink would be dead config`,field:"sink",next:'Drop the sink, or use on_output "always"/"on-change" if delivery is the point.'});const Q=l!==null?await In(l):"{}";if(l?.startsWith("spawn:")&&(mn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const y=await yn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes,from_exec:e.principal.from_exec},{kind:l.slice(6)});if(!y.allowed)throw vn(y)}const g=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!==g)throw new s({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});Z(m,"crtr:schedule"),l?.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 D=m?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(D&&n.isolated===!1)throw new c(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const N=D||n.isolated===!0,f=await Tn(e,g,m),j=nn(),S=N?sn(g,j):null;if(S!==null&&n.cwd_is_default!==!0){const y=en(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?z(n.cwd):S);if(y===".."||y.startsWith(`..${G}`)||y.startsWith(G))throw new c(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const V=S??await Pn(n,g,b,An(n),N,{sub:f.sub,grantId:f.grant_id,scopes:f.scopes}),w=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(w!==null&&await _(w)!==null&&(await _(w)).grantee!==g)throw new s({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(w!==null&&await _(w)===null)throw new s({error:"anchor_not_found",message:`anchor node ${w} does not exist on the canvas`,field:"anchor_node",next:"Name a live node id, or drop the anchor \u2014 an unanchored cron outlives every node."});const W=n.cancel_on_wake===!0;if(W&&w===null)throw new s({error:"cancel_on_wake_needs_anchor",message:"cancel_on_wake couples the cron to a node's dormancy \u2014 it requires an anchor node",field:"anchor_node",next:"Pass anchor_node with the node whose wake should cancel this cron."});const $=n.run_timeout_s??300;if(typeof $!="number"||!Number.isInteger($)||$<=0)throw new s({error:"bad_run_timeout",message:`run_timeout_s must be a positive integer (got ${String(n.run_timeout_s)})`,field:"run_timeout_s",next:"Send the timeout in whole seconds, e.g. 300."});const A=typeof n.profile=="string"&&n.profile!==""?(await Sn(e,n.profile)).profileId:null,B=O(n.scope,"scope",["profile","global"],A!==null?"profile":"global");if(B==="profile"&&A===null)throw new s({error:"scope_needs_profile",message:'scope "profile" names the profile the cron lists and cancels within \u2014 it requires a profile',field:"profile",next:'Send a profile, or use scope "global" for a canvas-home-wide cron.'});return await rn({cron_id:j,name:r,created_by:b,grantee:g,sub:f.sub,grant_id:f.grant_id,scopes:f.scopes,origin_run_id:f.origin_run_id,armed_from_exec:f.armed_from_exec===!0,isolated:N,command:o,fire_at:i,recur:d,tz:typeof n.tz=="string"?n.tz:null,expires_at:u,anchor_node:w,cancel_on_wake:W,cwd:V,env_json:h,profile:A,scope:B,run_timeout_s:$,overlap:p,on_output:a,sink:Q,tier:q}),{status:201,body:k(await v(j),null)}}t(En,"handleArm");async function Un(e){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,d)=>i.fire_at<d.fire_at?-1:i.fire_at>d.fire_at?1:0)).filter(i=>r===null||i.created_by===r),async i=>await J(e,i));return{status:200,body:await On(o,async i=>k(i,await R(i)))}}t(Un,"handleList");async function R(e){return(await F(e.cron_id))[0]??null}t(R,"lastRunOf");async function C(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(C,"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 Jn(e){const n=await C(e),r=await F(n.cron_id);return{status:200,body:{cron:k(n,r[0]??null),runs:r.map(H)}}}t(Jn,"handleShow");async function Rn(e){const n=I(e,await C(e));await L(n.cron_id,"paused");const r=await v(n.cron_id);return{status:200,body:k(r,await R(r))}}t(Rn,"handlePause");async function xn(e){const n=I(e,await C(e));await L(n.cron_id,"active");const r=await v(n.cron_id);return{status:200,body:k(r,await R(r))}}t(xn,"handleResume");async function Dn(e){const n=I(e,await C(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 _n(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.kind==="exec"?{kind:"canceled",by:r.app}:r.cron===n.cron_id?null:{kind:"canceled",by:`cron ${r.cron}`};if(r.class==="broker"){const i=on(n.sink);return i?.kind==="node"&&i.node===r.node?null:{kind:"canceled",by:`node ${r.node}`}}const o=T(r);return{kind:"canceled",by:o===null||o==="app:terminal"?"the person":o}}t(Bn,"cancelNotice");async function zn(e){const n=e.params.cronId,r=await v(n);if(r!==null&&!await 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"?e.principal.kind==="cron"&&o===e.principal.execution&&n===e.principal.cron:!0,d=o!==null&&i&&hn(n,o);return r!==null&&await kn(r,d?null:Bn(e,r),{preserveRunningProcess:d}),{status:204}}t(zn,"handleCancel");const le=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:En},{method:"GET",pattern:"/v1/crons",handler:Un},{method:"GET",pattern:"/v1/crons/:cronId",handler:Jn},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:Rn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:xn},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:Wn},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Dn},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:zn}];export{le as cronRoutes};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ExecRunDTO } from '../../../api/index.js';
|
|
2
|
+
import type { AppPrincipal } from '../principal.js';
|
|
3
|
+
/** Request body ceiling: the argv limit plus the other field. */
|
|
4
|
+
export declare const EXEC_BODY_MAX_BYTES: number;
|
|
5
|
+
/** Run one exec for `caller`; `signal` aborts when the HTTP request is dropped. */
|
|
6
|
+
export declare function handleExec(caller: AppPrincipal, body: unknown, signal: AbortSignal): Promise<ExecRunDTO>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var k=Object.defineProperty;var o=(t,u)=>k(t,"name",{value:u,configurable:!0});import{randomUUID as V}from"node:crypto";import{covers as Y}from"@north-light/identity";import{appSpace as j}from"../../../core/layout.js";import{emitEvent as M}from"../../../core/events/emit.js";import{RunError as i}from"../../../core/runs/operations.js";import{readAppEnvVars as F}from"../../../core/profiles/app-env-store.js";import{ADMITTED_HOST_PATH_ENV as R,buildOperationalEnvBase as $,resolveAdmittedHostCommand as J}from"../../../core/runtime/spawn-env.js";import{appProcessesSandboxed as G,launchAppProcess as K}from"../../../core/spaces/process.js";import{uidForApp as z}from"../../../core/spaces/users.js";import{envHomeOverride as Q}from"../../../shared/env.js";import{closeCronExecution as W,openCronExecution as Z}from"../../cron/run-socket.js";const ee=60,U=100,C=32*1024,pe=C+1024,q=256*1024,L=4,l=new Map;function te(t){if(typeof t!="object"||t===null||Array.isArray(t))throw new i(400,"invalid_request","exec requires an object body");const u=t,r=u.argv;if(!Array.isArray(r)||r.length===0||!r.every(e=>typeof e=="string"))throw new i(400,"invalid_request","argv must be a non-empty array of strings","argv");if(r.some(e=>e.includes("\0")))throw new i(400,"invalid_request","argv strings cannot contain NUL","argv");if(Buffer.byteLength(JSON.stringify(r))>=C)throw new i(400,"invalid_request","argv must be less than 32 KB as JSON","argv");const s=u.timeout_s??ee;if(typeof s!="number"||!Number.isFinite(s)||s<=0||s>U)throw new i(400,"invalid_request",`timeout_s must be greater than 0 and at most ${U}`,"timeout_s");return{argv:r,timeout_s:s}}o(te,"parse");async function _e(t,u,r){if(!t.resolved.some(n=>Y(n,"crtr:schedule")))throw new i(403,"scope_missing","missing scope crtr:schedule",void 0,void 0,"crtr:schedule");const s=te(u);if(!G())throw new i(503,"sandbox_unavailable","this runtime has no root launcher, so it cannot sandbox an app process",void 0,void 0,void 0,!1);const e=t.grantee,h=l.get(e)??0;if(h>=L)throw new i(429,"busy",`${e} already has ${L} exec processes running; retry when one finishes`,void 0,void 0,void 0,!0);l.set(e,h+1);const f=V(),p=Date.now();let a,b=!1,E=!1;const y=o(()=>{if(E)return;E=!0,b&&W(f);const n=(l.get(e)??1)-1;n>0?l.set(e,n):l.delete(e),M({level:"info",event:"exec.run",fields:{app:e,argv:s.argv.join(" ").slice(0,200),exit_code:a?.exit_code??null,duration_ms:Date.now()-p}})},"release");let c=null,x=o(()=>{},"wake");const A=new Promise(n=>{x=o(()=>n(null),"wake")}),_=o(n=>{c===null&&(c=n,x())},"cancel"),H=setTimeout(()=>_("timeout"),s.timeout_s*1e3),T=o(()=>_("abort"),"onAbort");r.addEventListener("abort",T,{once:!0}),r.aborted&&_("abort");const I=o(()=>({exit_code:null,signal:null,stdout:"",stdout_truncated:!1,stderr:"",stderr_truncated:!1,timed_out:c==="timeout",duration_ms:Date.now()-p}),"unstarted");let D=!1;try{const n=j(e).files,O=$({targetCwd:n,targetProfileId:null,skipSpawnAllow:!0}),g=Q(),S={...O,...F(e),CRTR_RUN_ID:f,...g===void 0||g===""?{}:{CRTR_HOME:g},[R]:O[R]},N=J("crtr",S,n),v={executionId:f,kind:"exec",cronId:null,fromExec:!0,grantee:e,sub:t.sub,grantId:t.grant,scopes:null,originRunId:null,isolated:!1,anchor:null,uid:await z(e),rebuilt:!1,refusal:null};if(await Z(v,n,null),b=!0,c!==null)return a=I(),a;const P=K({app:e,principal:{sub:t.sub,grantId:t.grant,scopes:null},cron:{executionId:f},command:[N,...s.argv],cwd:n,env:S,stdoutBytes:q,stderrBytes:q}),m=await Promise.race([P,A]);if(m===null)return D=!0,P.then(w=>w.stop()).catch(()=>{}).finally(y),a=I(),a;const X=o(()=>{m.stop().catch(w=>M({level:"error",event:"exec.stop_failed",error:w,fields:{app:e,pid:m.pid}}))},"stop");A.then(X);const d=await m.completion;if(v.refusal!==null)throw new i(401,"grant_revoked",v.refusal.message);const B=c==="timeout";return a={exit_code:B?null:d.exitCode,signal:d.signal,stdout:d.stdout,stdout_truncated:d.stdoutTruncated,stderr:d.stderr,stderr_truncated:d.stderrTruncated,timed_out:B,duration_ms:Date.now()-p},a}finally{clearTimeout(H),r.removeEventListener("abort",T),D||y()}}o(_e,"handleExec");export{pe as EXEC_BODY_MAX_BYTES,_e as handleExec};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var q=Object.defineProperty;var r=(n,e)=>q(n,"name",{value:e,configurable:!0});import{join as O}from"node:path";import{deleteAuthCredential as _,readAuthCredentials as S,setAuthCredential as y}from"../../../core/auth-file.js";import{secretsDir as D}from"../../../core/layout.js";import{ApiError as s}from"../../../api/index.js";import{RunError as b}from"../../../core/runs/operations.js";import{usage as u}from"../../../core/errors.js";import{modelAuthReadiness as E}from"../../../core/model-auth/readiness.js";import{MANAGED_PROVIDER_IDS as I,OPENAI_CHATGPT_PROVIDER_ID as j,commitManagedLogin as F,isManagedProvider as p,readSubscriptionPool as h,removeAllManagedAccounts as P}from"../../../core/subscription-state.js";import{cancelModelAuthFlow as M,completeModelAuthFlow as R,startModelAuthFlow as T,waitModelAuthFlow as $}from"../../../core/model-auth/flows.js";import{waitSeconds as C}from"../long-poll-wait.js";function l(){return O(D(),"auth.json")}r(l,"authPath");function g(n,e){if(typeof e!="string"||e==="")throw new s(400,"invalid_request",`oauth credential requires \`${n}\``);const t=Date.parse(e);if(Number.isNaN(t))throw new s(400,"invalid_request",`\`${n}\` is not a valid ISO timestamp`);return t}r(g,"parseExpiry");function c(n,e,t){if(typeof t!="string"||t==="")throw new s(400,"invalid_request",`${n} credential requires \`${e}\``);return t}r(c,"requireField");function L(n,e=Date.now()){const t=Object.entries(n.modelCooldowns??{}).filter(([,o])=>o>e).map(([o,a])=>({family:o,rate_limited_until:new Date(a).toISOString()}));return{label:n.label,expires_at:new Date(n.expires).toISOString(),rate_limited_until:new Date(n.rateLimitedUntil).toISOString(),...t.length?{model_cooldowns:t}:{},...n.authFailure?{auth_failure:n.authFailure}:{},...n.accountId?{account_id:n.accountId}:{}}}r(L,"toManagedAccountStatus");function G(n){const e=h(n).map(t=>L(t));return{provider:n,managed:!0,configured:e.length>0,accounts:e}}r(G,"managedStatus");function f(n,e){const t=n.query.getAll(e);if(t.length>1)throw u(`${e} must appear at most once`);return t[0]}r(f,"singleQueryValue");async function N(){const n=await S(l()),e=r(o=>o===j&&n[o]!==void 0&&h(o).length===0,"legacyApiKey"),t=I.filter(o=>!e(o)).map(G);for(const[o,a]of Object.entries(n))p(o)&&!e(o)||a.type!=="api_key"&&a.type!=="oauth"||t.push({provider:o,managed:!1,configured:!0,kind:a.type});return{status:200,body:{providers:t}}}r(N,"handleList");async function x(n){for(const[i]of n.query)if(i!=="profile"&&i!=="cwd"&&i!=="kind"&&i!=="model")throw u(`unknown model-auth readiness query parameter: ${i}`);const e=f(n,"profile"),t=f(n,"cwd"),o=f(n,"kind"),a=f(n,"model");if(o!==void 0&&o.trim()==="")throw u("kind must be a non-empty string when provided");if(a!==void 0&&a.trim()==="")throw u("model must be a non-empty string when provided");return{status:200,body:await E({...e!==void 0?{profile:e}:{},...t!==void 0?{cwd:t}:{},...o!==void 0?{kind:o}:{},...a!==void 0?{model:a}:{}})}}r(x,"handleReadiness");async function V(n){const e=n.params.provider,t=n.body;if(t===void 0||typeof t!="object"||typeof t.kind!="string")throw new s(400,"invalid_request","a credential `kind` is required");switch(t.kind){case"api_key":{const o=c("api_key","api_key",t.api_key);return await y(e,{type:"api_key",key:o},l()),{status:200,body:{provider:e,installed:!0,managed:!1}}}case"oauth":{if(p(e))throw new s(422,"unprocessable",`provider ${e} stores oauth credentials as managed accounts \u2014 use kind \`managed_account\``);const o=c("oauth","access_token",t.access_token),a=c("oauth","refresh_token",t.refresh_token),i=g("expires_at",t.expires_at),d=t.extra??{};await y(e,{type:"oauth",...d,access:o,refresh:a,expires:i},l());const m={provider:e,installed:!0,managed:!1};return typeof t.account_id=="string"&&t.account_id!==""&&(m.account_id=t.account_id),{status:200,body:m}}case"managed_account":{if(!p(e))throw new s(422,"unprocessable",`provider ${e} does not support managed accounts`);const o=typeof t.label=="string"&&t.label.trim()?t.label.trim():void 0;if(!o&&t.auto_label!==!0)throw new s(400,"invalid_request","managed_account credential requires `label` or `auto_label`");const a=typeof t.account_id=="string"&&t.account_id!==""?t.account_id:void 0,i=c("managed_account","access_token",t.access_token),d=c("managed_account","refresh_token",t.refresh_token),m=g("expires_at",t.expires_at),k=typeof t.last_rate_limited_at=="number"?t.last_rate_limited_at:Math.max(0,...h(e).map(A=>A.lastRateLimitedAt));await F(e,o,{refresh:d,access:i,expires:m,...a?{accountId:a}:{},lastRateLimitedAt:k,autoLabel:t.auto_label===!0});const w={provider:e,installed:!0,managed:!0};return a&&(w.account_id=a),{status:200,body:w}}default:throw new s(400,"invalid_request",`unknown credential kind: ${String(t.kind)}`)}}r(V,"handleInstall");async function K(n){const e=n.params.provider;if(p(e)){const o=await P(e),a=await _(e,l());return{status:200,body:{provider:e,removed_accounts:o+(a?1:0)}}}const t=await _(e,l())?1:0;return{status:200,body:{provider:e,removed_accounts:t}}}r(K,"handleDelete");function v(n){const e=n.body;if(e===void 0)return{};if(typeof e!="object"||e===null||Array.isArray(e))throw new s(400,"invalid_request","request body must be a JSON object");return e}r(v,"bodyObject");async function U(n){const e=v(n);return{status:200,body:await T({provider:e.provider,...e.redirect!==void 0?{redirect:e.redirect}:{},...e.label!==void 0?{label:e.label}:{}})}}r(U,"handleFlowStart");async function H(n){const e=v(n);return{status:200,body:await R(n.params.flow_id,e.input)}}r(H,"handleFlowComplete");async function J(n){const e=M(n.params.flow_id);if(!e)throw new b(400,"invalid_request","no sign-in has this flow_id","flow_id");const{jti:t,...o}=e;return{status:200,body:o}}r(J,"handleFlowCancel");async function Q(n){for(const[d]of n.query)if(d!=="wait")throw u(`unknown model-auth flow query parameter: ${d}`);f(n,"wait");const e=C("model_auth.get",n.query),t=new AbortController;n.res.once("close",()=>t.abort());const o=await $(n.params.flow_id,e*1e3,t.signal);if(!o)throw new b(404,"not_found","no sign-in has this flow_id","flow_id");const{jti:a,...i}=o;return{status:200,body:i}}r(Q,"handleFlowGet");const re=[{method:"POST",pattern:"/v1/model-auth/flows",ownerOnly:!0,handler:U},{method:"POST",pattern:"/v1/model-auth/flows/:flow_id/complete",ownerOnly:!0,handler:H},{method:"POST",pattern:"/v1/model-auth/flows/:flow_id/cancel",ownerOnly:!0,handler:J},{method:"GET",pattern:"/v1/model-auth/flows/:flow_id",ownerOnly:!0,handler:Q},{method:"GET",pattern:"/v1/model-auth",handler:N},{method:"GET",pattern:"/v1/model-auth/readiness",handler:x},{method:"PUT",pattern:"/v1/model-auth/:provider",ownerOnly:!0,handler:V},{method:"DELETE",pattern:"/v1/model-auth/:provider",ownerOnly:!0,handler:K}];export{G as managedStatus,re as modelAuthRoutes};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var Se=Object.defineProperty;var s=(e,n)=>Se(e,"name",{value:n,configurable:!0});import{envProfileId as Re}from"../../../shared/env.js";import{pausedCanvasProfiles as Ie}from"../../../core/profiles/manifest.js";import{existsSync as C,realpathSync as q,statSync as ee}from"node:fs";import{isAbsolute as B,resolve as T}from"node:path";import{appendSituationalContext as Oe,formatSituationalProse as Ae}from"../../../core/runtime/situational-context.js";import{closeNode as $e}from"../../../core/runtime/close.js";import{reviveNode as Ne}from"../../../core/runtime/revive.js";import{nowIso as K}from"../../../core/fs-utils.js";import{appPaidLaunch as Ce,assertLaunchModelRegistered as V,forkNode as Te,resolveForkSource as Pe,spawnChildPrepared as Ee}from"../../../core/runtime/spawn.js";import{forkSourceNode as De,newNodeId as ne}from"../../../core/runtime/nodes.js";import{abandonCreateRequest as te,childrenOf as Le,completeCreateRequest as ie,findCreateRequest as je,getNode as R,recordCreateRequestNode as Me,reserveCreateRequest as oe,reserveCreateRequestNode as Ge,listNodes as Fe,recordRespawnAttempt as Be,subscribersOf as Ke,subscriptionsOf as re,updateNode as N}from"../../../core/canvas/canvas.js";import{armControllerWait as Ve}from"../../../core/canvas/waits.js";import{withCanvasWrite as W}from"../../../core/canvas/db.js";import{markNodeRead as We}from"../../../core/canvas/recovery.js";import{nodeObject as xe}from"../../../core/graph/objects.js";import{recordActedOn as ze}from"../../../core/graph/edges.js";import{publish as Je}from"../../../core/graph/events.js";import{readerFor as Ue}from"../reader.js";import{relocateSessionCwd as Ye,unbindSession as He}from"../../../core/canvas/sessions.js";import{ticketCountsForNodes as Qe}from"../../../core/canvas/attention.js";import{isSafeNodeId as Xe}from"../../../core/canvas/paths.js";import{contextDir as ae,nodeAgentDir as x}from"../../../core/canvas/node-agent-paths.js";import{checkCronBirth as Ze}from"../../cron/authority.js";import{cronBirthPlacement as qe}from"../../cron/armed-context.js";import{existsBeneath as en,isAbsentOrRefused as nn,isBeneathRefusal as tn,readdirBeneath as on}from"../../../core/spaces/open-beneath.js";import{readNodeMessagesPage as rn,readNodeSession as an,readNodeSnapshot as dn,transcriptMarkdown as sn}from"../../../core/runtime/node-read.js";import{assembleNodeSubject as ln}from"../../../core/substrate/subject.js";import{reviveAll as cn}from"../../../core/runtime/revive-all.js";import{promoteAsync as un,requestYieldAsync as fn,reshapeNodeAsync as z}from"../../../core/runtime/promote.js";import{recycleNode as pn}from"../../../core/runtime/recycle.js";import{relaunchRoot as mn}from"../../../core/runtime/reset.js";import{assertReopenGate as wn}from"../../../core/runtime/reopen.js";import{writeYieldMessage as hn,readGoal as yn}from"../../../core/runtime/kickoff.js";import{ensureRoadmap as _n,hasRoadmap as gn}from"../../../core/runtime/roadmap.js";import{renameWindow as bn}from"../../../core/runtime/placement.js";import{buildLaunchSpecAsync as vn,explicitModelIntent as kn,normalizeModel as Sn,readMergedLaunchConfigAsync as de}from"../../../core/runtime/launch.js";import{modelRequestFromConfig as Rn}from"../../../core/model-routes.js";import{persistDormantModel as In,setModelLive as On}from"../../../core/runtime/model-swap.js";import{waitForBrokerViewSocket as se}from"../../../core/runtime/view-socket.js";import{BrokerUnreachableError as An}from"../../../core/runtime/broker-request.js";import{boundFleet as P}from"../../../core/runtime/fleet.js";import{admitLegacyStructuredOutput as $n}from"../../structured-requests/sidecar.js";import{assertOutputSchemaAccepted as Nn}from"../../../core/runtime/structured-output.js";import{OUTCOME_PAYLOAD_MAX_BYTES as le}from"../../../core/canvas/types.js";import{HUMAN_ACTION_NAME_PATTERN as Cn}from"../../../core/human/action-binding.js";import{resolveHumanAction as Tn}from"../../../core/human-actions.js";import{parseDurationMs as Pn}from"../../../core/wake.js";import{claimWarmNode as En,refillWarmPool as ce,resumeWarmClaim as Dn,warmPoolEnabled as Ln}from"../../../core/runtime/warm-pool.js";import{assertLaunchKind as J,isLaunchKind as jn,resolveLaunchTarget as Mn}from"../../../core/runtime/launch-target.js";import{fullName as Gn}from"../../../core/canvas/labels.js";import{reportsForNode as ue}from"./reports.js";import{requireUsableProfile as Fn}from"./profiles.js";import{assertProfileActive as Bn,loadExactProfileManifest as Kn}from"../../../core/profiles/manifest.js";import{getCron as fe}from"../../../core/canvas/crons.js";import{cronWakeOrigin as Vn}from"../../../core/runtime/bearings.js";import{boundDaemonControl as Wn}from"../../control.js";import{notFound as E,usage as m}from"../../../core/errors.js";import{assertScope as xn,validateScopes as pe}from"../../../core/scopes.js";import{scopeAllowed as O}from"../../../shared/env.js";import{toCloseResultDTO as zn,toNodeDetailDTO as U,toNodeSummaryDTO as Jn,toReviveResultDTO as Un}from"../map.js";import{ApiError as p}from"../../../api/index.js";import{InputError as y}from"../../../core/io.js";import{actsAsPerson as Yn,canReadNode as D,readableNodeIds as Hn,resolvedScopes as Qn}from"../router.js";import{forwardVerb as Xn,queryWithoutMachine as Zn,relayError as qn,unknownError as et,withMachine as nt}from"../../peer/forward.js";import{fileRoot as tt}from"../../../core/spaces/file-root.js";import{activeGrant as it,payerOfGrant as ot,terminalGrant as rt}from"../../../core/grants/mirror.js";import{appRungForGrant as at}from"../../../core/llm/app-rungs.js";import{APP_FAMILY as dt}from"../../../core/model-routes.js";import{hasConfiguredProviderCredential as st,NO_PROVIDER_CREDENTIAL_MESSAGE as me}from"../../../core/runtime/provider-credential-gate.js";import{filterAsync as lt,mapAsync as we}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}s(h,"requireMeta");async function Y(e){return{subscribesTo:await re(e),subscribers:await Ke(e),children:await Le(e)}}s(Y,"edgesFor");async function b(e){const n=await h(e);return await U(n,await Y(e))}s(b,"detailOf");async function ct(e){let n=(await ue(e)).length;try{n+=on(await x(e),await ae(e)).length}catch(t){if(!nn(t))throw t}return n}s(ct,"countFiles");function ut(e){if(typeof e!="object"||e===null)throw m("a node create requires a JSON body");const n=e;if(n.kind!==void 0&&(typeof n.kind!="string"||n.kind.trim()===""))throw m("kind must be a non-empty string when provided");if(n.description!==void 0&&(typeof n.description!="string"||n.description.trim()===""))throw m("description must be a non-empty string when provided");if(n.creator!==void 0&&(typeof n.creator!="string"||n.creator.trim()===""))throw m("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 p(400,"invalid_request","isolated must be a boolean",{param:"isolated"});const t=n.root_lifecycle;if(t!==void 0&&t!=="resident"&&t!=="terminal")throw m("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 m("request_key must be a non-empty string of at most 128 characters when provided");return n}s(ut,"parseCreateBody");function ft(e){if(e===void 0)return;const n=Pn(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 p(422,"invalid_deadline","`deadline` must be a positive representable duration");return t.toISOString()}s(ft,"parseDeadline");function pt(e){if(e!==void 0){if(typeof e!="object"||e===null||Array.isArray(e))throw new p(400,"invalid_request","`outcome_delivery` must be an object");if(typeof e.action!="string"||!Cn.test(e.action))throw new p(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 p(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable")}if(n===void 0)throw new p(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable");if(Buffer.byteLength(n,"utf8")>le)throw new p(413,"payload_too_large",`serialized payload exceeds ${le} bytes`)}return e}}s(pt,"parseOutcomeDelivery");function mt(e,n){const t=Tn(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 p(422,"unknown_action",`no action named ${e.action} is declared from ${n}`):new p(422,"invalid_action",`${e.action} is declared in ${t.configOrigin} but unusable: ${t.reason}`,{reason:t.reason,configOrigin:t.configOrigin})}s(mt,"resolveOutcomeDelivery");function wt(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}s(wt,"buildRecipe");function ht(e,n,t){return!Ln()||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}}s(ht,"warmRequestFor");import{resolveCreateKind as mo}from"../../../core/runtime/launch-target.js";function he(e){const n=e.modelIntent===void 0?void 0:`${e.modelIntent.family}/${e.modelIntent.strength}`;return Rn(e.model,n,e.modelExact===!0)}s(he,"requestFromLaunch");function yt(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 p(403,"scope_denied",`scopes exceed the bearer token's ceiling: ${t.join(", ")}`,{scopes:t,ceiling:n})}s(yt,"withinCeiling");async function ye(e,n,t){const o=(await vn(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 V(he(o),{cwd:e.cwd,profileId:e.profile_id??null},t)}s(ye,"assertNodeModelChangeRegistered");async function _e(e,n){const t=e.principal;if(t.class!=="broker"&&t.class!=="cron")return;const o=tt({app:t.app,resolved:await Qn(t),launched:t.scopes},n,"read");if("root"in o)return;const i="missing"in o?o.missing:null;throw new p(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)}s(_e,"assertCallerReaches");const H=new Map;async function _t(e,n={},t={}){const o=yt(ut(e.body),e.ceiling);if(e.principal.class==="broker"){const d=e.principal;if(o.prefer_warm===!0&&d.app!=="app:terminal"&&(o.prefer_warm=!1),o.parent!=null){const r=await h(o.parent);if(r.grantee!==d.app)throw new p(403,"other_app_run","cannot create a child under another app's node");if(!await D(d,r.grantee??"app:terminal",o.parent))throw E(`unknown node: ${o.parent}`,{received:o.parent})}if(o.creator!==void 0&&o.creator!==d.node)throw new p(403,"other_app_run","cannot create for another node");if(o.fork_from!==void 0){let r=null;try{r=await De(await Pe(o.fork_from))}catch{}if(r?.grantee!==d.app||!await D(d,r.grantee,r.node_id))throw new p(403,"other_app_run","cannot fork another app's conversation")}o.creator=d.node}if(e.principal.class==="cron"){const d=e.principal;for(const l of["parent","creator","fork_from","node_id"])if(o[l]!=null)throw new p(403,"permission_error",`a cron cannot set ${l} on a node it starts`);o.prefer_warm=!1;const r=await Ze({grantee:d.app,sub:d.sub,grant_id:d.grant,scopes:d.scopes});if(!r.allowed)throw new p(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(f=>!O(r.effectiveScopes,f)||!O(d.scopes,f));if(l!==void 0)throw new p(403,"scope_missing",`scope ${l} is not held by this cron`,void 0,void 0,{scope:l})}else d.scopes!==null&&(o.scopes=[...d.scopes]);o.node_id=await ne(),d.isolated&&(o.isolated=!0,o.cwd=await x(o.node_id,d.app))}o.creator!==void 0&&xn(await h(o.creator),"crtr:act");const i=o.request_key?.trim();if(i===void 0||o.node_id!==void 0&&Xe(o.node_id)&&await R(o.node_id)!==null&&(await je(i))?.nodeId!==o.node_id)return L(e,o,n,t);const a=H.get(i);if(a!==void 0)return a;const u=gt(e,o,i,n,t);H.set(i,u);try{return await u}finally{H.delete(i)}}s(_t,"handleCreate");async function gt(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 u=await R(a.nodeId);if(e.principal.class!=="person"&&!await D(e.principal,u.grantee??"app:terminal",a.nodeId))throw new p(409,"create_request_conflict","request_key belongs to another caller");return{status:201,body:await b(a.nodeId)}}return L(e,n,o,i,a)}try{return await L(e,n,o,i,a)}catch(u){const d=await oe(t);throw(d.nodeId===null||await R(d.nodeId)===null)&&await te(t,d.nodeId),u}}s(gt,"handleKeyedCreateRequest");async function L(e,n,t,o,i=void 0){const a=o.deadlineAt??ft(n.deadline),u=pt(n.outcome_delivery),d=e.principal.class==="cron"?await qe({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;d!==null&&(n.root=d.root===!0);const r=d?.parent??n.parent??void 0;if(r==null&&n.root!==!0)throw m("a node create requires a parent (or root: true for an independent root)");let l=null;if(n.output_schema!==void 0){try{l=JSON.parse(n.output_schema)}catch{throw m("output_schema must be a JSON schema string")}if(l===null||typeof l!="object"||Array.isArray(l))throw m("output_schema must be a JSON schema object");await Nn(l)}const{profileId:f,kind:w,cwd:c,launch:k}=await Mn(n,{validateKind:ke});await _e(e,c),n.worktree!==void 0&&await _e(e,T(n.pin_cwd??n.cwd??c));const _={...n,kind:w},g=u===void 0?void 0:mt(u,c),X=("grant"in e.principal?e.principal.grant:null)??(r==null?null:(await R(r))?.grant_id??null);if(await V(he(await Ce(k,X,n.model)),{cwd:c,profileId:f},t.modelRegistry),await ot(X)!=="app"&&!await st(f))throw new p(412,"no_provider_credential",me,{error:"no_provider_credential",message:me,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"?ht(_,c,f):null,G=l===null&&o.beforeBrokerLaunch===void 0?void 0:async S=>{l!==null&&await $n(S,"terminal",l),o.beforeBrokerLaunch?.(S)};if(i?.state==="warm_claimed"&&i.nodeId!==null){const S=A===null?null:await Dn(A,i.nodeId,G);if(S===null)throw await te(i.requestKey,i.nodeId),new p(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 En(A,{...a===void 0?{}:{deadlineAt:a},...g===void 0?{}:{outcomeDelivery:g},...i===void 0?{}:{requestKey:i.requestKey}},G);if(S!==null)return i!==void 0&&await ie(i.requestKey,S),ce(A),{status:201,body:await b(S)};M=A}const F=i?.nodeId??n.node_id??(i===void 0?void 0:await ne());i!==void 0&&i.nodeId===null&&await Ge(i.requestKey,F);const I=wt(F===void 0?_:{..._,node_id:F},r,c);d!==null&&(d.rootLifecycle!==void 0&&(I.rootLifecycle=d.rootLifecycle),d.scopes!==void 0&&_.scopes===void 0&&(I.scopes=d.scopes),d.run!==void 0&&(I.run=d.run),d.withinCreate!==void 0&&(I.withinCreate=d.withinCreate)),a!==void 0&&(I.deadlineAt=a),g!==void 0&&(I.outcomeDelivery=g),I.readiness=n.root===!0?"await":"launch";const Z=await Ee(I,G,()=>{M!==null&&ce(M)});return i!==void 0&&await Me(i.requestKey,Z.node.node_id),{status:201,body:await b(Z.node.node_id)}}}s(L,"handleCreateRequest");function v(e,n){const t=e.query.getAll(n);if(t.length>1)throw m(`${n} must appear at most once`);return t[0]}s(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 m(`${n} must be true or false`)}}s(Q,"optionalBoolean");function j(e,n,t){if(e!==void 0){if(t.includes(e))return e;throw m(`${n} must be one of: ${t.join(", ")}`)}}s(j,"oneOf");function bt(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 m(`unknown nodes query parameter: ${_}`);const t=j(v(e,"status"),"status",["active","idle","done","dead","canceled"]),o=j(v(e,"lifecycle"),"lifecycle",["terminal","resident"]),i=v(e,"kind"),a=j(v(e,"mode"),"mode",["base","orchestrator"]),u=v(e,"profile_id"),d=v(e,"profile_prefix"),r=v(e,"parent"),l=Q(v(e,"top_level"),"top_level"),f=v(e,"under"),w=Q(v(e,"under_inclusive"),"under_inclusive"),c=Q(v(e,"hanging"),"hanging"),k=j(v(e,"include"),"include",["activity"]);if(r!==void 0&&l===!0)throw m("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}:{},...u!==void 0?{profile_id:u}:{},...d!==void 0?{profile_prefix:d}:{},...r!==void 0?{parent:r}:{},...l!==void 0?{top_level:l}:{},...f!==void 0?{under:f}:{},...w!==void 0?{under_inclusive:w}:{},...c!==void 0?{hanging:c}:{},...k!==void 0?{include:k}:{}}}s(bt,"parseListQuery");async function vt(e){const n=bt(e);let t=await Fe({...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}:{}});const o=await Hn(e.principal);o!==null&&(t=t.filter(d=>o.has(d.node_id)));const i=n.include==="activity"?await Qe(t.map(d=>d.node_id)):null,a=t.length>0?Ie():new Set;return{status:200,body:await we(t,async d=>{const r=await Jn(d,d.profile_id!==null&&a.has(d.profile_id));if(i!==null){const l=await ue(d.node_id);r.activity={latest_report:l[0]??null,final_report:d.final_report===null?null:l.find(f=>f.name===d.final_report)??null,pending_human_count:i[d.node_id]??0}}return r})}}s(vt,"handleList");async function kt(e){const n=e.params.id,t=await Xn(e,n,{method:"GET",path:`/v1/nodes/${encodeURIComponent(n)}`,query:Zn(e)});if(t.kind==="unknown")throw et(n,t.unreachable);if(t.kind==="remote"){if(t.status>=400)throw qn(t.status,t.body);return{status:t.status,body:nt(t.body,t.machine)}}return await h(n),{status:200,body:await b(n)}}s(kt,"handleDetail");async function St(e){const n=e.params.id;await h(n);const t=await dn(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:K()}}}s(St,"handleSnapshot");async function Rt(e){const n=e.params.id,t=await ln(n);if(t===null)throw E(`unknown node: ${n}`,{received:n});return{status:200,body:t}}s(Rt,"handleSubject");function It(e){for(const[a]of e.query)if(a!=="cursor"&&a!=="limit")throw m(`unknown messages query parameter: ${a}`);const n=e.query.getAll("cursor"),t=e.query.getAll("limit");if(n.length>1)throw m("cursor must appear at most once");if(t.length>1)throw m("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 m("limit must be an integer from 1 through 500");return n.length===0?{limit:i}:{cursor:n[0],limit:i}}s(It,"parseMessagesQuery");async function Ot(e){const n=e.params.id,t=await rn(n,It(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:K()}}}s(Ot,"handleMessages");async function At(e){const n=e.params.id;await h(n);const t=await an(n);return{status:200,body:{node_id:n,session_file:t.sessionFile,session_jsonl:t.sessionJsonl,system_prompt:t.systemPrompt,captured_at:K()}}}s(At,"handleSession");async function $t(e){const n=e.params.id;return await h(n),{status:200,body:{node_id:n,markdown:await sn(n)}}}s($t,"handleTranscript");async function Nt(e){const n=e.params.id;await h(n);const t=s(async(i,a)=>({node_id:i,label:(await R(i))?.name??i,dir:await ae(i),self:a,files:await ct(i)}),"root"),o=await lt([await t(n,!0),...await we(await re(n),async i=>await t(i.node_id,!1))],async i=>await Ct(i.node_id,i.dir)||i.files>0);return{status:200,body:{node_id:n,roots:o}}}s(Nt,"handleContext");async function Ct(e,n){try{return en(await x(e),n)}catch(t){if(tn(t))return!1;throw t}}s(Ct,"hasDir");async function Tt(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 Te(n.node_id,o,e.principal.class==="broker"?e.principal.node:void 0);return{status:201,body:await b(i.node.node_id)}}s(Tt,"handleFork");const Pt=1e4;async function Et(e){const n=e.params.id;await h(n);const t=e.body,o=t?.resume??!0,i=t?.reopen??!1;await wn(n,i,e.principal.class==="broker"?e.principal.node:null);const a=!o&&t?.cron_id!==void 0?await fe(t.cron_id):null,u=await Ne(n,{resume:o,capacity:"refuse",...a!==null?{wakeReason:await Vn(a,"schedule_revive")}:{}});u.outcome!=="already-live"&&await Be(n,{failures:0,notBefore:null});const d=(await R(n))?.status??"active",r=(u.launch??P().get(n)?.handle)?.exited,l=u.outcome!=="frozen"&&await se(n,r,Pt);return{status:200,body:Un(n,u,d,l)}}s(Et,"handleRevive");async function Dt(e){const n=e.params.id;return{status:200,body:{newNodeId:(await mn(n))?.newNodeId??null}}}s(Dt,"handleRelaunchRoot");async function Lt(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 m("caller_pid must be a positive integer when provided");const o=Wn(),i=new Promise(u=>{e.res.once("close",()=>u())}),a=await $e(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:zn(a)}}s(Lt,"handleClose");async function jt(e){if(!Yn(e.principal))throw new p(403,"permission_error","only the person marks a conversation read");const n=e.params.id;return await h(n),await We(n),{status:204}}s(jt,"handleMarkRead");async function Mt(e){const n=e.params.id;await h(n);const t=await pn(n);return{status:200,body:{recycled:t.recycled,finalized:t.finalized,new_root:t.newRoot,delivered:t.delivered}}}s(Mt,"handleRecycle");async function Gt(e){const n=e.params.id;return await h(n),await z(n,{lifecycle:"terminal"}),await $(e,n,["lifecycle"]),{status:200,body:await b(n)}}s(Gt,"handleDemote");async function Ft(e,n={}){const t=e.params.id,o=await h(t),i=e.body??{};i.kind!==void 0&&await J(i.kind,o.cwd,o.profile_id??null),i.model!==void 0&&await ye(o,{kind:i.kind??o.kind,mode:"orchestrator",lifecycle:i.resident===!0?"resident":o.lifecycle,model:i.model},n.modelRegistry);const a=await un(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 u=await U(a.meta,await Y(t));return u.roadmap_written=a.roadmapWritten,u.roadmap_ref=a.roadmapRef,u.goal_path=a.goalPath,{status:200,body:u}}s(Ft,"handlePromote");async function Bt(e,n={}){const t=e.params.id,o=await h(t),i=e.body??{};i.kind!==void 0&&await J(i.kind,o.cwd,o.profile_id??null),i.model!==void 0&&await ye(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 hn(t,a);const u=await fn(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 U(u.meta,await Y(t))}}s(Bt,"handleYield");async function Kt(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 m("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 p(422,"unprocessable",`controller ${t.controller} is ${o.status}`);return await Ve(n,t.controller),{status:200,body:await b(n)}}s(Kt,"handleWait");async function Vt(e){const n=e.principal,t=await cn(n.class==="broker"?async i=>i.grantee===n.app&&await D(n,i.grantee,i.node_id):void 0);return{status:200,body:{revived:[...t.revived],failed:[...t.failed],frozen:[...t.frozen]}}}s(Vt,"handleReviveAll");const Wt=new Set(["interrupt","steer","after-turn","next-turn"]);function xt(e){if(e===null)return null;const n=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 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"||!Wt.has(i))&&n(`listen delivery must be interrupt|steer|after-turn|next-turn: ${String(i)}`);return JSON.stringify(Object.fromEntries(t))}s(xt,"listenJson");async function $(e,n,t,o){t.length===0&&o===void 0||await W(async i=>{if(o?.(i),t.length===0)return;const a=await xe(i,n),u=await Ue(i,e),d=u.class==="person"?u.person:u.class==="app"?u.app:u.node;await ze(i,d,a),await Je(i,{event:"configured",object:a,actor:d,attrs:{changed:t.join(",")}})})}s($,"recordConfigured");function ge(e){const n=P().get(e);if(n===void 0)return!1;try{return process.kill(n.pid,"SIGTERM"),!0}catch{return!1}}s(ge,"restartLiveBroker");function be(e,n){try{return q(e)===q(n)}catch{return!1}}s(be,"sameDirectory");async function zt(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(w=>w!=="profile"&&w!=="restart"&&i[w]!==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),l=await Fn(e,i.profile);Bn(l.profileId);const f=Kn(l.profileId).manifest.grantee??"app:terminal";if(f!==(r.grantee??"app:terminal"))throw new y({error:"profile_grantee_mismatch",message:`profile ${l.profileId} belongs to ${f}, 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)!==l.profileId&&(jn(r.kind)?(await J(r.kind,r.cwd,l.profileId),await z(t,{profileId:l.profileId})):await N(t,{profile_id:l.profileId}),await $(e,t,["profile"])),i.restart===!0&&ge(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(c=>c!=="cwd"&&c!=="keep_session"&&c!=="restart"&&i[c]!==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(t),f=C(l.cwd);if(i.keep_session===!0||f&&B(r)&&C(r)&&be(l.cwd,r)){if(!B(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 c=T(r);let k=!1;try{k=ee(c).isDirectory()}catch{}if(!k)throw new y({error:"cwd_missing",message:`cwd is not an existing directory: ${c}`,received:r,field:"cwd",next:"Send an existing absolute directory."});if(f&&!be(l.cwd,c))throw new y({error:"cwd_not_same_directory",message:`${c} 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:`${t} owns an open managed worktree and cannot relocate its cwd`,received:r,field:"cwd",next:"Reconcile the managed worktree first."});const _=l.cwd;return _!==c&&(await W(async g=>{await N(t,{cwd:c}),await g.prepare("UPDATE crons SET cwd = ? WHERE anchor_node = ? AND cwd = ?").run(c,t,_),await Ye(t,_,c)}),await $(e,t,["cwd"])),i.restart===!0&&ge(t),{status:200,body:await b(t)}}if(P().has(t)){const c=`${t} cannot repair its cwd while its broker is live`;throw new p(409,"cwd_node_live",c,{error:"cwd_node_live",message:c,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:`${t} 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:`${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(C(l.cwd))throw new y({error:"cwd_still_present",message:`${t} 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(!B(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 w=T(r);if(!C(w))throw new y({error:"cwd_missing",message:`cwd does not exist: ${w}`,received:r,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."});try{if(!ee(w).isDirectory())throw new y({error:"cwd_not_a_directory",message:`cwd is not a directory: ${w}`,received:r,field:"cwd",next:"Send an existing absolute directory."})}catch(c){throw c instanceof y?c:new y({error:"cwd_missing",message:`cwd does not exist: ${w}`,received:r,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."})}return await W(async()=>{await N(t,{cwd:w}),await He(t)}),await $(e,t,["cwd"]),{status:200,body:await b(t)}}let a=await h(t);const u=i.listen===void 0?void 0:xt(i.listen);if(i.scopes!==void 0){const r=pe(i.scopes),l=a.grantee==="app:terminal"?await rt():a.grant_id?await it(a.grant_id):null;if(!r.every(f=>O(a.scopes,f)&&l!==null&&O(l.resolved,f)))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 p(400,"invalid_scope","crtr:act requires crtr:llm");a=await N(t,{scopes:r})}if(i.situational_context!==void 0&&Oe(t,Ae(i.situational_context)),i.model!==void 0){const r=(await de(a.cwd,a.profile_id??null)).modelLadders,l=kn(i.model,r),f=await at(a.grant_id,i.model,l===void 0),w=f?{family:dt,strength:f.strength}:l,c=f?f.model:Sn(i.model,r);await V({kind:"concrete",spec:c},{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 On(t,c,w).then(()=>!0,g=>{if(g instanceof An)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 p(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(!c.includes("/"))throw m(`model must resolve to a provider/id spec: ${i.model}`);await In(t,c,w)}a=await h(t)}if(i.kind!==void 0||i.mode!==void 0||i.lifecycle!==void 0){i.kind!==void 0&&await ke(i.kind);const r=i.kind??a.kind,l=i.mode??a.mode,f=i.lifecycle??a.lifecycle;let w;l==="orchestrator"&&!await gn(t)&&(w=s(async()=>{const c=await yn(t);await _n(t,c!==null&&c.trim()!==""?{goal:c.trim()}:{})},"beforePersist")),a=await z(t,{...i.kind!==void 0?{kind:r}:{},...i.mode!==void 0?{mode:l}:{},...i.lifecycle!==void 0?{lifecycle:f}:{}},w)}i.name!==void 0&&(a=await N(t,{name:i.name}),a.window&&bn(a.window,Gn(a)));const d=["scopes","situational_context","model","kind","mode","lifecycle","name","listen"].filter(r=>i[r]!==void 0);return await $(e,t,d,u===void 0?void 0:async r=>{await r.prepare("UPDATE objects SET listen_json = ? WHERE id = ? AND type = 'node'").run(u,t)}),{status:200,body:await b(t)}}s(zt,"handleConfig");async function ve(){return(await de(process.cwd(),Re()||null)).kinds}s(ve,"daemonKinds");async function ke(e){const n=Object.keys(await ve());if(!n.includes(e))throw m(`unknown kind: ${e}`,{received:e,next:`Valid kinds: ${n.sort().join(", ")}.`})}s(ke,"assertKindServer");async function Jt(){const e=await ve();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}))}}}s(Jt,"handleListKinds");const uo=[{method:"POST",pattern:"/v1/nodes",scope:"crtr:act",handler:_t},{method:"GET",pattern:"/v1/nodes",handler:vt},{method:"GET",pattern:"/v1/kinds",handler:s(()=>Jt(),"handler")},{method:"POST",pattern:"/v1/nodes/revive-all",scope:"crtr:act",handler:Vt},{method:"GET",pattern:"/v1/nodes/:id",handler:kt},{method:"GET",pattern:"/v1/nodes/:id/snapshot",handler:St},{method:"GET",pattern:"/v1/nodes/:id/subject",handler:Rt},{method:"GET",pattern:"/v1/nodes/:id/messages",handler:Ot},{method:"GET",pattern:"/v1/nodes/:id/session",handler:At},{method:"GET",pattern:"/v1/nodes/:id/transcript",handler:$t},{method:"GET",pattern:"/v1/nodes/:id/context",handler:Nt},{method:"POST",pattern:"/v1/nodes/:id/fork",scope:"crtr:act",handler:Tt},{method:"POST",pattern:"/v1/nodes/:id/revive",scope:"crtr:act",handler:Et},{method:"POST",pattern:"/v1/nodes/:id/relaunch-root",scope:"crtr:act",handler:Dt},{method:"POST",pattern:"/v1/nodes/:id/close",handler:Lt},{method:"POST",pattern:"/v1/nodes/:id/read",handler:jt},{method:"POST",pattern:"/v1/nodes/:id/recycle",scope:"crtr:act",handler:Mt},{method:"POST",pattern:"/v1/nodes/:id/demote",scope:"crtr:act",handler:Gt},{method:"POST",pattern:"/v1/nodes/:id/promote",scope:"crtr:act",handler:Ft},{method:"POST",pattern:"/v1/nodes/:id/yield",scope:"crtr:act",handler:Bt},{method:"POST",pattern:"/v1/nodes/:id/wait",handler:Kt},{method:"PATCH",pattern:"/v1/nodes/:id/config",handler:zt}];export{zt as handleConfig,_t as handleCreate,Tt as handleFork,vt as handleList,Ft as handlePromote,Bt as handleYield,uo as nodeRoutes,mo as resolveCreateKind};
|
|
1
|
+
var Se=Object.defineProperty;var s=(e,n)=>Se(e,"name",{value:n,configurable:!0});import{envProfileId as Re}from"../../../shared/env.js";import{pausedCanvasProfiles as Ie}from"../../../core/profiles/manifest.js";import{existsSync as C,realpathSync as q,statSync as ee}from"node:fs";import{isAbsolute as B,resolve as T}from"node:path";import{appendSituationalContext as Oe,formatSituationalProse as Ae}from"../../../core/runtime/situational-context.js";import{closeNode as $e}from"../../../core/runtime/close.js";import{reviveNode as Ne}from"../../../core/runtime/revive.js";import{nowIso as K}from"../../../core/fs-utils.js";import{appPaidLaunch as Ce,assertLaunchModelRegistered as x,forkNode as Te,resolveForkSource as Pe,spawnChildPrepared as Ee}from"../../../core/runtime/spawn.js";import{forkSourceNode as De,newNodeId as ne}from"../../../core/runtime/nodes.js";import{abandonCreateRequest as te,childrenOf as Le,completeCreateRequest as ie,findCreateRequest as je,getNode as R,recordCreateRequestNode as Me,reserveCreateRequest as oe,reserveCreateRequestNode as Ge,listNodes as Fe,recordRespawnAttempt as Be,subscribersOf as Ke,subscriptionsOf as re,updateNode as N}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 Ve}from"../../../core/canvas/recovery.js";import{nodeObject as We}from"../../../core/graph/objects.js";import{recordActedOn as ze}from"../../../core/graph/edges.js";import{publish as Je}from"../../../core/graph/events.js";import{readerFor as Ue}from"../reader.js";import{relocateSessionCwd as Ye,unbindSession as He}from"../../../core/canvas/sessions.js";import{ticketCountsForNodes as Qe}from"../../../core/canvas/attention.js";import{isSafeNodeId as Xe}from"../../../core/canvas/paths.js";import{contextDir as ae,nodeAgentDir as W}from"../../../core/canvas/node-agent-paths.js";import{checkCronBirth as Ze,cronRefusalError as qe}from"../../cron/authority.js";import{cronBirthPlacement as en}from"../../cron/armed-context.js";import{existsBeneath as nn,isAbsentOrRefused as tn,isBeneathRefusal as on,readdirBeneath as rn}from"../../../core/spaces/open-beneath.js";import{readNodeMessagesPage as an,readNodeSession as dn,readNodeSnapshot as sn,transcriptMarkdown as ln}from"../../../core/runtime/node-read.js";import{assembleNodeSubject as cn}from"../../../core/substrate/subject.js";import{reviveAll as un}from"../../../core/runtime/revive-all.js";import{promoteAsync as fn,requestYieldAsync as pn,reshapeNodeAsync as z}from"../../../core/runtime/promote.js";import{recycleNode as mn}from"../../../core/runtime/recycle.js";import{relaunchRoot as wn}from"../../../core/runtime/reset.js";import{assertReopenGate as hn}from"../../../core/runtime/reopen.js";import{writeYieldMessage as yn,readGoal as _n}from"../../../core/runtime/kickoff.js";import{ensureRoadmap as gn,hasRoadmap as bn}from"../../../core/runtime/roadmap.js";import{renameWindow as vn}from"../../../core/runtime/placement.js";import{buildLaunchSpecAsync as kn,explicitModelIntent as Sn,normalizeModel as Rn,readMergedLaunchConfigAsync as de}from"../../../core/runtime/launch.js";import{modelRequestFromConfig as In}from"../../../core/model-routes.js";import{persistDormantModel as On,setModelLive as An}from"../../../core/runtime/model-swap.js";import{waitForBrokerViewSocket as se}from"../../../core/runtime/view-socket.js";import{BrokerUnreachableError as $n}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 Cn}from"../../../core/runtime/structured-output.js";import{OUTCOME_PAYLOAD_MAX_BYTES as le}from"../../../core/canvas/types.js";import{HUMAN_ACTION_NAME_PATTERN as Tn}from"../../../core/human/action-binding.js";import{resolveHumanAction as Pn}from"../../../core/human-actions.js";import{parseDurationMs as En}from"../../../core/wake.js";import{claimWarmNode as Dn,refillWarmPool as ce,resumeWarmClaim as Ln,warmPoolEnabled as jn}from"../../../core/runtime/warm-pool.js";import{assertLaunchKind as J,isLaunchKind as Mn,resolveLaunchTarget as Gn}from"../../../core/runtime/launch-target.js";import{fullName as Fn}from"../../../core/canvas/labels.js";import{reportsForNode as ue}from"./reports.js";import{requireUsableProfile as Bn}from"./profiles.js";import{assertProfileActive as Kn,loadExactProfileManifest as xn}from"../../../core/profiles/manifest.js";import{getCron as fe}from"../../../core/canvas/crons.js";import{cronWakeOrigin as Vn}from"../../../core/runtime/bearings.js";import{boundDaemonControl as Wn}from"../../control.js";import{notFound as E,usage as m}from"../../../core/errors.js";import{assertScope as zn,validateScopes as pe}from"../../../core/scopes.js";import{scopeAllowed as O}from"../../../shared/env.js";import{toCloseResultDTO as Jn,toNodeDetailDTO as U,toNodeSummaryDTO as Un,toReviveResultDTO as Yn}from"../map.js";import{ApiError as p}from"../../../api/index.js";import{InputError as y}from"../../../core/io.js";import{actsAsPerson as Hn,canReadNode as D,readableNodeIds as Qn,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{hasConfiguredProviderCredential as lt,NO_PROVIDER_CREDENTIAL_MESSAGE as me}from"../../../core/runtime/provider-credential-gate.js";import{filterAsync as ct,mapAsync as we}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}s(h,"requireMeta");async function Y(e){return{subscribesTo:await re(e),subscribers:await Ke(e),children:await Le(e)}}s(Y,"edgesFor");async function b(e){const n=await h(e);return await U(n,await Y(e))}s(b,"detailOf");async function ut(e){let n=(await ue(e)).length;try{n+=rn(await W(e),await ae(e)).length}catch(t){if(!tn(t))throw t}return n}s(ut,"countFiles");function ft(e){if(typeof e!="object"||e===null)throw m("a node create requires a JSON body");const n=e;if(n.kind!==void 0&&(typeof n.kind!="string"||n.kind.trim()===""))throw m("kind must be a non-empty string when provided");if(n.description!==void 0&&(typeof n.description!="string"||n.description.trim()===""))throw m("description must be a non-empty string when provided");if(n.creator!==void 0&&(typeof n.creator!="string"||n.creator.trim()===""))throw m("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 p(400,"invalid_request","isolated must be a boolean",{param:"isolated"});const t=n.root_lifecycle;if(t!==void 0&&t!=="resident"&&t!=="terminal")throw m("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 m("request_key must be a non-empty string of at most 128 characters when provided");return n}s(ft,"parseCreateBody");function pt(e){if(e===void 0)return;const n=En(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 p(422,"invalid_deadline","`deadline` must be a positive representable duration");return t.toISOString()}s(pt,"parseDeadline");function mt(e){if(e!==void 0){if(typeof e!="object"||e===null||Array.isArray(e))throw new p(400,"invalid_request","`outcome_delivery` must be an object");if(typeof e.action!="string"||!Tn.test(e.action))throw new p(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 p(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable")}if(n===void 0)throw new p(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable");if(Buffer.byteLength(n,"utf8")>le)throw new p(413,"payload_too_large",`serialized payload exceeds ${le} bytes`)}return e}}s(mt,"parseOutcomeDelivery");function wt(e,n){const t=Pn(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 p(422,"unknown_action",`no action named ${e.action} is declared from ${n}`):new p(422,"invalid_action",`${e.action} is declared in ${t.configOrigin} but unusable: ${t.reason}`,{reason:t.reason,configOrigin:t.configOrigin})}s(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}s(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}}s(yt,"warmRequestFor");import{resolveCreateKind as wo}from"../../../core/runtime/launch-target.js";function he(e){const n=e.modelIntent===void 0?void 0:`${e.modelIntent.family}/${e.modelIntent.strength}`;return In(e.model,n,e.modelExact===!0)}s(he,"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 p(403,"scope_denied",`scopes exceed the bearer token's ceiling: ${t.join(", ")}`,{scopes:t,ceiling:n})}s(_t,"withinCeiling");async function ye(e,n,t){const o=(await kn(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 x(he(o),{cwd:e.cwd,profileId:e.profile_id??null},t)}s(ye,"assertNodeModelChangeRegistered");async function _e(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 p(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)}s(_e,"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 d=e.principal;if(o.prefer_warm===!0&&d.app!=="app:terminal"&&(o.prefer_warm=!1),o.parent!=null){const r=await h(o.parent);if(r.grantee!==d.app)throw new p(403,"other_app_run","cannot create a child under another app's node");if(!await D(d,r.grantee??"app:terminal",o.parent))throw E(`unknown node: ${o.parent}`,{received:o.parent})}if(o.creator!==void 0&&o.creator!==d.node)throw new p(403,"other_app_run","cannot create for another node");if(o.fork_from!==void 0){let r=null;try{r=await De(await Pe(o.fork_from))}catch{}if(r?.grantee!==d.app||!await D(d,r.grantee,r.node_id))throw new p(403,"other_app_run","cannot fork another app's conversation")}o.creator=d.node}if(e.principal.class==="cron"){const d=e.principal;for(const l of["parent","creator","fork_from","node_id"])if(o[l]!=null)throw new p(403,"permission_error",`a cron cannot set ${l} on a node it starts`);o.prefer_warm=!1,o.node_id=await ne(),d.isolated&&(o.isolated=!0,o.cwd=await W(o.node_id,d.app));const r=await Ze({grantee:d.app,sub:d.sub,grant_id:d.grant,scopes:d.scopes,from_exec:d.from_exec},{profile:o.profile,cwd:o.cwd,pin_cwd:o.pin_cwd,kind:o.kind,model:o.model,mode:o.mode});if(!r.allowed)throw qe(r);if(o.scopes!==void 0){const l=o.scopes.find(f=>!O(r.effectiveScopes,f)||!O(d.scopes,f));if(l!==void 0)throw new p(403,"scope_missing",`scope ${l} is not held by this cron`,void 0,void 0,{scope:l})}else d.scopes!==null&&(o.scopes=[...d.scopes])}o.creator!==void 0&&zn(await h(o.creator),"crtr:act");const i=o.request_key?.trim();if(i===void 0||o.node_id!==void 0&&Xe(o.node_id)&&await R(o.node_id)!==null&&(await je(i))?.nodeId!==o.node_id)return L(e,o,n,t);const a=H.get(i);if(a!==void 0)return a;const u=bt(e,o,i,n,t);H.set(i,u);try{return await u}finally{H.delete(i)}}s(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 u=await R(a.nodeId);if(e.principal.class!=="person"&&!await D(e.principal,u.grantee??"app:terminal",a.nodeId))throw new p(409,"create_request_conflict","request_key belongs to another caller");return{status:201,body:await b(a.nodeId)}}return L(e,n,o,i,a)}try{return await L(e,n,o,i,a)}catch(u){const d=await oe(t);throw(d.nodeId===null||await R(d.nodeId)===null)&&await te(t,d.nodeId),u}}s(bt,"handleKeyedCreateRequest");async function L(e,n,t,o,i=void 0){const a=o.deadlineAt??pt(n.deadline),u=mt(n.outcome_delivery),d=e.principal.class==="cron"?await en({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:e.principal.kind==="exec"?null:(await fe(e.principal.cron))?.created_by??null},n.node_id):null;d!==null&&(n.root=d.root===!0);const r=d?.parent??n.parent??void 0;if(r==null&&n.root!==!0)throw m("a node create requires a parent (or root: true for an independent root)");let l=null;if(n.output_schema!==void 0){try{l=JSON.parse(n.output_schema)}catch{throw m("output_schema must be a JSON schema string")}if(l===null||typeof l!="object"||Array.isArray(l))throw m("output_schema must be a JSON schema object");await Cn(l)}const{profileId:f,kind:w,cwd:c,launch:k}=await Gn(n,{validateKind:ke});await _e(e,c),n.worktree!==void 0&&await _e(e,T(n.pin_cwd??n.cwd??c));const _={...n,kind:w},g=u===void 0?void 0:wt(u,c),X=("grant"in e.principal?e.principal.grant:null)??(r==null?null:(await R(r))?.grant_id??null);if(await x(he(await Ce(k,X,n.model)),{cwd:c,profileId:f},t.modelRegistry),await rt(X)!=="app"&&!await lt(f))throw new p(412,"no_provider_credential",me,{error:"no_provider_credential",message:me,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(_,c,f):null,G=l===null&&o.beforeBrokerLaunch===void 0?void 0:async S=>{l!==null&&await Nn(S,"terminal",l),o.beforeBrokerLaunch?.(S)};if(i?.state==="warm_claimed"&&i.nodeId!==null){const S=A===null?null:await Ln(A,i.nodeId,G);if(S===null)throw await te(i.requestKey,i.nodeId),new p(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 Dn(A,{...a===void 0?{}:{deadlineAt:a},...g===void 0?{}:{outcomeDelivery:g},...i===void 0?{}:{requestKey:i.requestKey}},G);if(S!==null)return i!==void 0&&await ie(i.requestKey,S),ce(A),{status:201,body:await b(S)};M=A}const F=i?.nodeId??n.node_id??(i===void 0?void 0:await ne());i!==void 0&&i.nodeId===null&&await Ge(i.requestKey,F);const I=ht(F===void 0?_:{..._,node_id:F},r,c);d!==null&&(d.rootLifecycle!==void 0&&(I.rootLifecycle=d.rootLifecycle),d.scopes!==void 0&&_.scopes===void 0&&(I.scopes=d.scopes),d.run!==void 0&&(I.run=d.run),d.withinCreate!==void 0&&(I.withinCreate=d.withinCreate)),a!==void 0&&(I.deadlineAt=a),g!==void 0&&(I.outcomeDelivery=g),I.readiness=n.root===!0?"await":"launch";const Z=await Ee(I,G,()=>{M!==null&&ce(M)});return i!==void 0&&await Me(i.requestKey,Z.node.node_id),{status:201,body:await b(Z.node.node_id)}}}s(L,"handleCreateRequest");function v(e,n){const t=e.query.getAll(n);if(t.length>1)throw m(`${n} must appear at most once`);return t[0]}s(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 m(`${n} must be true or false`)}}s(Q,"optionalBoolean");function j(e,n,t){if(e!==void 0){if(t.includes(e))return e;throw m(`${n} must be one of: ${t.join(", ")}`)}}s(j,"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 m(`unknown nodes query parameter: ${_}`);const t=j(v(e,"status"),"status",["active","idle","done","dead","canceled"]),o=j(v(e,"lifecycle"),"lifecycle",["terminal","resident"]),i=v(e,"kind"),a=j(v(e,"mode"),"mode",["base","orchestrator"]),u=v(e,"profile_id"),d=v(e,"profile_prefix"),r=v(e,"parent"),l=Q(v(e,"top_level"),"top_level"),f=v(e,"under"),w=Q(v(e,"under_inclusive"),"under_inclusive"),c=Q(v(e,"hanging"),"hanging"),k=j(v(e,"include"),"include",["activity"]);if(r!==void 0&&l===!0)throw m("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}:{},...u!==void 0?{profile_id:u}:{},...d!==void 0?{profile_prefix:d}:{},...r!==void 0?{parent:r}:{},...l!==void 0?{top_level:l}:{},...f!==void 0?{under:f}:{},...w!==void 0?{under_inclusive:w}:{},...c!==void 0?{hanging:c}:{},...k!==void 0?{include:k}:{}}}s(vt,"parseListQuery");async function kt(e){const n=vt(e);let t=await Fe({...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}:{}});const o=await Qn(e.principal);o!==null&&(t=t.filter(d=>o.has(d.node_id)));const i=n.include==="activity"?await Qe(t.map(d=>d.node_id)):null,a=t.length>0?Ie():new Set;return{status:200,body:await we(t,async d=>{const r=await Un(d,d.profile_id!==null&&a.has(d.profile_id));if(i!==null){const l=await ue(d.node_id);r.activity={latest_report:l[0]??null,final_report:d.final_report===null?null:l.find(f=>f.name===d.final_report)??null,pending_human_count:i[d.node_id]??0}}return r})}}s(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)}}s(St,"handleDetail");async function Rt(e){const n=e.params.id;await h(n);const t=await sn(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:K()}}}s(Rt,"handleSnapshot");async function It(e){const n=e.params.id,t=await cn(n);if(t===null)throw E(`unknown node: ${n}`,{received:n});return{status:200,body:t}}s(It,"handleSubject");function Ot(e){for(const[a]of e.query)if(a!=="cursor"&&a!=="limit")throw m(`unknown messages query parameter: ${a}`);const n=e.query.getAll("cursor"),t=e.query.getAll("limit");if(n.length>1)throw m("cursor must appear at most once");if(t.length>1)throw m("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 m("limit must be an integer from 1 through 500");return n.length===0?{limit:i}:{cursor:n[0],limit:i}}s(Ot,"parseMessagesQuery");async function At(e){const n=e.params.id,t=await an(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:K()}}}s(At,"handleMessages");async function $t(e){const n=e.params.id;await h(n);const t=await dn(n);return{status:200,body:{node_id:n,session_file:t.sessionFile,session_jsonl:t.sessionJsonl,system_prompt:t.systemPrompt,captured_at:K()}}}s($t,"handleSession");async function Nt(e){const n=e.params.id;return await h(n),{status:200,body:{node_id:n,markdown:await ln(n)}}}s(Nt,"handleTranscript");async function Ct(e){const n=e.params.id;await h(n);const t=s(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 ct([await t(n,!0),...await we(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}}}s(Ct,"handleContext");async function Tt(e,n){try{return nn(await W(e),n)}catch(t){if(on(t))return!1;throw t}}s(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 Te(n.node_id,o,e.principal.class==="broker"?e.principal.node:void 0);return{status:201,body:await b(i.node.node_id)}}s(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 hn(n,i,e.principal.class==="broker"?e.principal.node:null);const a=!o&&t?.cron_id!==void 0?await fe(t.cron_id):null,u=await Ne(n,{resume:o,capacity:"refuse",...a!==null?{wakeReason:await Vn(a,"schedule_revive")}:{}});u.outcome!=="already-live"&&await Be(n,{failures:0,notBefore:null});const d=(await R(n))?.status??"active",r=(u.launch??P().get(n)?.handle)?.exited,l=u.outcome!=="frozen"&&await se(n,r,Et);return{status:200,body:Yn(n,u,d,l)}}s(Dt,"handleRevive");async function Lt(e){const n=e.params.id;return{status:200,body:{newNodeId:(await wn(n))?.newNodeId??null}}}s(Lt,"handleRelaunchRoot");async function jt(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 m("caller_pid must be a positive integer when provided");const o=Wn(),i=new Promise(u=>{e.res.once("close",()=>u())}),a=await $e(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:Jn(a)}}s(jt,"handleClose");async function Mt(e){if(!Hn(e.principal))throw new p(403,"permission_error","only the person marks a conversation read");const n=e.params.id;return await h(n),await Ve(n),{status:204}}s(Mt,"handleMarkRead");async function Gt(e){const n=e.params.id;await h(n);const t=await mn(n);return{status:200,body:{recycled:t.recycled,finalized:t.finalized,new_root:t.newRoot,delivered:t.delivered}}}s(Gt,"handleRecycle");async function Ft(e){const n=e.params.id;return await h(n),await z(n,{lifecycle:"terminal"}),await $(e,n,["lifecycle"]),{status:200,body:await b(n)}}s(Ft,"handleDemote");async function Bt(e,n={}){const t=e.params.id,o=await h(t),i=e.body??{};i.kind!==void 0&&await J(i.kind,o.cwd,o.profile_id??null),i.model!==void 0&&await ye(o,{kind:i.kind??o.kind,mode:"orchestrator",lifecycle:i.resident===!0?"resident":o.lifecycle,model:i.model},n.modelRegistry);const a=await fn(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 u=await U(a.meta,await Y(t));return u.roadmap_written=a.roadmapWritten,u.roadmap_ref=a.roadmapRef,u.goal_path=a.goalPath,{status:200,body:u}}s(Bt,"handlePromote");async function Kt(e,n={}){const t=e.params.id,o=await h(t),i=e.body??{};i.kind!==void 0&&await J(i.kind,o.cwd,o.profile_id??null),i.model!==void 0&&await ye(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 yn(t,a);const u=await pn(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 U(u.meta,await Y(t))}}s(Kt,"handleYield");async function xt(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 m("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 p(422,"unprocessable",`controller ${t.controller} is ${o.status}`);return await xe(n,t.controller),{status:200,body:await b(n)}}s(xt,"handleWait");async function Vt(e){const n=e.principal,t=await un(n.class==="broker"?async i=>i.grantee===n.app&&await D(n,i.grantee,i.node_id):void 0);return{status:200,body:{revived:[...t.revived],failed:[...t.failed],frozen:[...t.frozen]}}}s(Vt,"handleReviveAll");const Wt=new Set(["interrupt","steer","after-turn","next-turn"]);function zt(e){if(e===null)return null;const n=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 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"||!Wt.has(i))&&n(`listen delivery must be interrupt|steer|after-turn|next-turn: ${String(i)}`);return JSON.stringify(Object.fromEntries(t))}s(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 We(i,n),u=await Ue(i,e),d=u.class==="person"?u.person:u.class==="app"?u.app:u.node;await ze(i,d,a),await Je(i,{event:"configured",object:a,actor:d,attrs:{changed:t.join(",")}})})}s($,"recordConfigured");function ge(e){const n=P().get(e);if(n===void 0)return!1;try{return process.kill(n.pid,"SIGTERM"),!0}catch{return!1}}s(ge,"restartLiveBroker");function be(e,n){try{return q(e)===q(n)}catch{return!1}}s(be,"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(w=>w!=="profile"&&w!=="restart"&&i[w]!==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),l=await Bn(e,i.profile);Kn(l.profileId);const f=xn(l.profileId).manifest.grantee??"app:terminal";if(f!==(r.grantee??"app:terminal"))throw new y({error:"profile_grantee_mismatch",message:`profile ${l.profileId} belongs to ${f}, 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)!==l.profileId&&(Mn(r.kind)?(await J(r.kind,r.cwd,l.profileId),await z(t,{profileId:l.profileId})):await N(t,{profile_id:l.profileId}),await $(e,t,["profile"])),i.restart===!0&&ge(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(c=>c!=="cwd"&&c!=="keep_session"&&c!=="restart"&&i[c]!==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(t),f=C(l.cwd);if(i.keep_session===!0||f&&B(r)&&C(r)&&be(l.cwd,r)){if(!B(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 c=T(r);let k=!1;try{k=ee(c).isDirectory()}catch{}if(!k)throw new y({error:"cwd_missing",message:`cwd is not an existing directory: ${c}`,received:r,field:"cwd",next:"Send an existing absolute directory."});if(f&&!be(l.cwd,c))throw new y({error:"cwd_not_same_directory",message:`${c} 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:`${t} owns an open managed worktree and cannot relocate its cwd`,received:r,field:"cwd",next:"Reconcile the managed worktree first."});const _=l.cwd;return _!==c&&(await V(async g=>{await N(t,{cwd:c}),await g.prepare("UPDATE crons SET cwd = ? WHERE anchor_node = ? AND cwd = ?").run(c,t,_),await Ye(t,_,c)}),await $(e,t,["cwd"])),i.restart===!0&&ge(t),{status:200,body:await b(t)}}if(P().has(t)){const c=`${t} cannot repair its cwd while its broker is live`;throw new p(409,"cwd_node_live",c,{error:"cwd_node_live",message:c,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:`${t} 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:`${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(C(l.cwd))throw new y({error:"cwd_still_present",message:`${t} 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(!B(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 w=T(r);if(!C(w))throw new y({error:"cwd_missing",message:`cwd does not exist: ${w}`,received:r,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."});try{if(!ee(w).isDirectory())throw new y({error:"cwd_not_a_directory",message:`cwd is not a directory: ${w}`,received:r,field:"cwd",next:"Send an existing absolute directory."})}catch(c){throw c instanceof y?c:new y({error:"cwd_missing",message:`cwd does not exist: ${w}`,received:r,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."})}return await V(async()=>{await N(t,{cwd:w}),await He(t)}),await $(e,t,["cwd"]),{status:200,body:await b(t)}}let a=await h(t);const u=i.listen===void 0?void 0:zt(i.listen);if(i.scopes!==void 0){const r=pe(i.scopes),l=a.grantee==="app:terminal"?await at():a.grant_id?await ot(a.grant_id):null;if(!r.every(f=>O(a.scopes,f)&&l!==null&&O(l.resolved,f)))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 p(400,"invalid_scope","crtr:act requires crtr:llm");a=await N(t,{scopes:r})}if(i.situational_context!==void 0&&Oe(t,Ae(i.situational_context)),i.model!==void 0){const r=(await de(a.cwd,a.profile_id??null)).modelLadders,l=Sn(i.model,r),f=await dt(a.grant_id,i.model,l===void 0),w=f?{family:st,strength:f.strength}:l,c=f?f.model:Rn(i.model,r);await x({kind:"concrete",spec:c},{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 An(t,c,w).then(()=>!0,g=>{if(g instanceof $n)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 p(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(!c.includes("/"))throw m(`model must resolve to a provider/id spec: ${i.model}`);await On(t,c,w)}a=await h(t)}if(i.kind!==void 0||i.mode!==void 0||i.lifecycle!==void 0){i.kind!==void 0&&await ke(i.kind);const r=i.kind??a.kind,l=i.mode??a.mode,f=i.lifecycle??a.lifecycle;let w;l==="orchestrator"&&!await bn(t)&&(w=s(async()=>{const c=await _n(t);await gn(t,c!==null&&c.trim()!==""?{goal:c.trim()}:{})},"beforePersist")),a=await z(t,{...i.kind!==void 0?{kind:r}:{},...i.mode!==void 0?{mode:l}:{},...i.lifecycle!==void 0?{lifecycle:f}:{}},w)}i.name!==void 0&&(a=await N(t,{name:i.name}),a.window&&vn(a.window,Fn(a)));const d=["scopes","situational_context","model","kind","mode","lifecycle","name","listen"].filter(r=>i[r]!==void 0);return await $(e,t,d,u===void 0?void 0:async r=>{await r.prepare("UPDATE objects SET listen_json = ? WHERE id = ? AND type = 'node'").run(u,t)}),{status:200,body:await b(t)}}s(Jt,"handleConfig");async function ve(){return(await de(process.cwd(),Re()||null)).kinds}s(ve,"daemonKinds");async function ke(e){const n=Object.keys(await ve());if(!n.includes(e))throw m(`unknown kind: ${e}`,{received:e,next:`Valid kinds: ${n.sort().join(", ")}.`})}s(ke,"assertKindServer");async function Ut(){const e=await ve();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}))}}}s(Ut,"handleListKinds");const fo=[{method:"POST",pattern:"/v1/nodes",scope:"crtr:act",handler:gt},{method:"GET",pattern:"/v1/nodes",handler:kt},{method:"GET",pattern:"/v1/kinds",handler:s(()=>Ut(),"handler")},{method:"POST",pattern:"/v1/nodes/revive-all",scope:"crtr:act",handler:Vt},{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:Nt},{method:"GET",pattern:"/v1/nodes/:id/context",handler:Ct},{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:jt},{method:"POST",pattern:"/v1/nodes/:id/read",handler:Mt},{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:xt},{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,fo as nodeRoutes,wo as resolveCreateKind};
|