@north-light/cli 0.3.414 → 0.3.416
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +20 -0
- package/dist/api/client.js +3 -3
- package/dist/api/dto/common.d.ts +1 -1
- package/dist/api/dto/crons.d.ts +7 -1
- package/dist/api/dto/human-requests.d.ts +1 -1
- package/dist/api/dto/run-events.d.ts +1 -1
- package/dist/api/dto/usage.d.ts +27 -0
- package/dist/api/dto/usage.js +0 -0
- package/dist/api/errors.js +1 -1
- package/dist/api/index.d.ts +3 -2
- package/dist/api/index.js +1 -1
- package/dist/builtin-memory/crouter-sdk/README.md +22 -4
- package/dist/builtin-memory/crouter-sdk/client.md +32 -1
- package/dist/builtin-memory/crouter-sdk/errors.md +3 -0
- package/dist/builtin-memory/crouter-sdk/getting-started.md +26 -4
- package/dist/builtin-memory/crouter-sdk/release-notes.md +23 -0
- package/dist/builtin-memory/crouter-sdk/testing.md +9 -3
- package/dist/builtin-memory/internal/nodes-and-canvas.md +4 -0
- package/dist/cli.js +7 -7
- package/dist/clients/attach/viewer.js +606 -606
- package/dist/commands/cron.js +1 -1
- package/dist/commands/node/create.js +1 -1
- package/dist/core/canvas/crons.d.ts +15 -1
- package/dist/core/canvas/crons.js +13 -13
- package/dist/core/canvas/delivery-contract.d.ts +1 -1
- package/dist/core/canvas/migrations.js +73 -66
- package/dist/core/canvas/types.d.ts +1 -1
- package/dist/core/command.d.ts +7 -0
- package/dist/core/command.js +9 -9
- package/dist/core/grants/mirror.d.ts +4 -0
- package/dist/core/grants/mirror.js +2 -2
- package/dist/core/grants/on-sync.js +1 -1
- package/dist/core/graph/custom.js +5 -5
- package/dist/core/graph/reader.js +3 -3
- package/dist/core/graph/spaces/app.js +1 -1
- package/dist/core/graph/types.d.ts +5 -0
- package/dist/core/layout.d.ts +2 -0
- package/dist/core/layout.js +2 -2
- package/dist/core/llm/run.d.ts +5 -1
- package/dist/core/llm/run.js +3 -3
- package/dist/core/providers/call.d.ts +2 -0
- package/dist/core/providers/call.js +1 -1
- package/dist/core/providers/errors.js +1 -1
- package/dist/core/providers/receipts.d.ts +2 -0
- package/dist/core/providers/receipts.js +2 -3
- package/dist/core/runs/confinement.d.ts +2 -0
- package/dist/core/runs/confinement.js +2 -2
- package/dist/core/runs/operations.d.ts +2 -0
- package/dist/core/runs/operations.js +4 -4
- package/dist/core/runtime/broker/model-adapter.js +2 -2
- package/dist/core/runtime/run-command.d.ts +10 -0
- package/dist/core/runtime/run-command.js +1 -1
- package/dist/core/spaces/mounts.d.ts +7 -0
- package/dist/core/spaces/mounts.js +1 -1
- package/dist/core/spaces/process.d.ts +22 -2
- package/dist/core/spaces/process.js +1 -1
- package/dist/core/usage/ledger.d.ts +49 -0
- package/dist/core/usage/ledger.js +10 -0
- package/dist/daemon/api/app-listener.js +12 -12
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/files.js +1 -1
- package/dist/daemon/api/handlers/llm.d.ts +8 -0
- package/dist/daemon/api/handlers/llm.js +3 -3
- package/dist/daemon/api/handlers/nodes.js +1 -1
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/dist/daemon/api/handlers/runs.js +1 -1
- package/dist/daemon/api/map.d.ts +1 -1
- package/dist/daemon/api/map.js +1 -1
- package/dist/daemon/api/operations.d.ts +1 -1
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/router.d.ts +15 -0
- package/dist/daemon/api/router.js +1 -1
- package/dist/daemon/api/server.d.ts +5 -1
- package/dist/daemon/api/server.js +1 -1
- package/dist/daemon/cron/armed-context.d.ts +21 -4
- package/dist/daemon/cron/armed-context.js +1 -1
- package/dist/daemon/cron/authority.d.ts +27 -0
- package/dist/daemon/cron/authority.js +1 -0
- package/dist/daemon/cron/disposition.d.ts +2 -0
- package/dist/daemon/cron/disposition.js +1 -1
- package/dist/daemon/cron/escalation.js +3 -3
- package/dist/daemon/cron/folders.d.ts +5 -0
- package/dist/daemon/cron/folders.js +1 -0
- package/dist/daemon/cron/lease-recovery.js +1 -1
- package/dist/daemon/cron/live-runs.d.ts +28 -0
- package/dist/daemon/cron/live-runs.js +1 -1
- package/dist/daemon/cron/run-socket.d.ts +30 -0
- package/dist/daemon/cron/run-socket.js +1 -0
- package/dist/daemon/cron/settle.d.ts +10 -0
- package/dist/daemon/cron/settle.js +1 -1
- package/dist/daemon/cron/sinks.d.ts +2 -1
- package/dist/daemon/cron/sinks.js +2 -2
- package/dist/daemon/cron-run.js +2 -1
- package/dist/daemon/crtrd.js +7 -7
- package/dist/daemon/reconcilers/action-lane.js +1 -1
- package/dist/daemon/reconcilers/broker-supervision.d.ts +10 -0
- package/dist/daemon/reconcilers/broker-supervision.js +4 -4
- package/dist/daemon/reconcilers/cron-lane.d.ts +1 -0
- package/dist/daemon/reconcilers/cron-lane.js +1 -1
- package/dist/shared/env.d.ts +6 -5
- package/dist/shared/env.js +2 -2
- package/docs/sdk/README.md +22 -4
- package/docs/sdk/client.md +32 -1
- package/docs/sdk/errors.md +3 -0
- package/docs/sdk/getting-started.md +26 -4
- package/docs/sdk/release-notes.md +23 -0
- package/docs/sdk/testing.md +9 -3
- package/package.json +2 -2
- package/packages/crouter-identity/dist/index.d.ts +29 -2
- package/packages/crouter-identity/dist/index.js +81 -12
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
var B=Object.defineProperty;var s=(e,n)=>B(e,"name",{value:n,configurable:!0});import{randomUUID as L}from"node:crypto";import{createServer as $}from"node:http";import{Router as j}from"./router.js";import{OPERATIONS as V}from"./operations.js";import{errorCodes as D}from"../../api/error-codes.js";import{MINIMUM_RUNTIME_VERSION_HEADER as
|
|
1
|
+
var B=Object.defineProperty;var s=(e,n)=>B(e,"name",{value:n,configurable:!0});import{randomUUID as L}from"node:crypto";import{createServer as $}from"node:http";import{Router as j}from"./router.js";import{OPERATIONS as V}from"./operations.js";import{errorCodes as D}from"../../api/error-codes.js";import{MINIMUM_RUNTIME_VERSION_HEADER as F,RUNTIME_VERSION_HEADER as G,minimumRuntimeRefusal as Y}from"../../api/runtime-version.js";import{crtrVersion as x}from"../../shared/crtr-version.js";import{AppPrincipalResolver as z}from"./principal.js";import{handleRunOperation as Q,integer as W}from"./handlers/runs.js";import{parseLlmRequest as K,runLlm as Z}from"../../core/llm/run.js";import{parseUsageMonth as ee,usageFor as te}from"../../core/usage/ledger.js";import{capFields as ne}from"./handlers/llm.js";import{CONTROL_BODY_MAX_BYTES as re,MODEL_CONTEXT_BODY_MAX_BYTES as ae}from"../../core/llm/body-limits.js";import{RunError as d,runIsRunning as oe}from"../../core/runs/operations.js";import{requireRun as I}from"./router.js";import{deferHead as se}from"./deferred-head.js";import{readRunEvents as J,registerGrantStream as ie,subscribeRunEvents as ue}from"../../core/runs/events.js";import{activeGrant as le}from"../../core/grants/mirror.js";import{releaseGrantWaiters as de}from"../../core/grants/watch.js";import{emitEvent as ce}from"../../core/events/emit.js";import{runNotices as fe}from"../run-notices.js";import{handleVendorSignIn as me,isVendorSignInPath as pe}from"./vendor-signin.js";import{listAppProfiles as he}from"./handlers/app-profiles.js";import{handleTransferDownload as _e,handleTransferOperation as ye,TRANSFER_OPERATIONS as we}from"./handlers/transfer.js";import{handleQuestionOperation as ve,QUESTION_OPERATIONS as ge}from"./handlers/questions.js";import{vendorStatus as Oe}from"./handlers/vendor-status.js";import{handleMemoryOperation as Re,MEMORY_OPERATIONS as Se}from"./handlers/memory.js";import{abortOnClose as be,callerFromApp as Ee,handleProviderOperation as Ne,PROVIDER_OPERATIONS as Te,providerFailure as Ae}from"./handlers/providers.js";function Pe(e){return Object.prototype.hasOwnProperty.call(D,e)?D[e]:void 0}s(Pe,"fixedRow");function Ie(e){const n=Pe(e);return n?n.type:e==="stream_gap"||e==="provider_error"||e==="runtime_version_unsupported"?"invalid_request_error":"server_error"}s(Ie,"errorType");function Ce(e){if(!e||typeof e!="object"||Array.isArray(e)||!("rate_limit"in e))return e;const{rate_limit:n,...a}=e;return a}s(Ce,"appDetails");function A(e,n,a){const u=Ce(e.details);return{error:{code:e.code,message:e.message,...u===void 0?{}:{details:u},type:Ie(e.code),...e.param?{param:e.param}:{},origin:"runtime",retryable:e.retryable||e.code==="stream_dropped",request_id:n,...a?{run_id:a}:{},...e.scope?{scope:e.scope}:{},...e.code==="unauthorized"?{user_action:"reconnect"}:{},...ne(e),...e.code==="all_routes_exhausted"?{retry_after_s:e.details.retry_after_s}:{}}}}s(A,"envelope");function Me(e,n,a,u){const i=n instanceof d?n:new d(500,"server_failure","runtime request failed");if(n instanceof d||console.error("[app-listener]",n),e.headersSent&&!String(e.getHeader("Content-Type")??"").startsWith("text/event-stream")){e.destroy();return}if(e.headersSent){e.end(`event: error
|
|
2
2
|
data: ${JSON.stringify(A(i,a,u))}
|
|
3
3
|
|
|
4
|
-
`);return}e.writeHead(i.status,{"Content-Type":"application/json; charset=utf-8","Request-Id":a,"X-Content-Type-Options":"nosniff",...i.code==="all_routes_exhausted"?{"Retry-After":String(i.details.retry_after_s)}:{}}),e.end(JSON.stringify(A(i,a,u)))}s(
|
|
4
|
+
`);return}e.writeHead(i.status,{"Content-Type":"application/json; charset=utf-8","Request-Id":a,"X-Content-Type-Options":"nosniff",...i.code==="all_routes_exhausted"?{"Retry-After":String(i.details.retry_after_s)}:{}}),e.end(JSON.stringify(A(i,a,u)))}s(Me,"fail");function He(e){return Array.isArray(e)?e[0]:e}s(He,"headerValue");function w(e,n,a){e.writeHead(n,{"Content-Type":"application/json; charset=utf-8"}),e.end(JSON.stringify(a))}s(w,"json");async function T(e,n=re){const a=[];let u=0;for await(const i of e){if(u+=i.length,u>n)throw new d(400,"invalid_request","request body too large");a.push(i)}if(u)try{return JSON.parse(Buffer.concat(a).toString("utf8"))}catch{throw new d(400,"invalid_request","invalid JSON body")}}s(T,"bodyOf");function ke(e,n){const a=e.headers["last-event-id"]??n.get("after");if(Array.isArray(a))throw new d(400,"invalid_request","invalid cursor","after");return W(a??null,"after",0,Number.MAX_SAFE_INTEGER,0)}s(ke,"cursor");function De(e){return`id: ${e.sequence_number}
|
|
5
5
|
event: ${e.type}
|
|
6
6
|
data: ${JSON.stringify(e)}
|
|
7
7
|
|
|
8
|
-
`}s(
|
|
8
|
+
`}s(De,"frame");const xe=6e4;function Je(e,n,a,u,i,N,v,g={}){I(u,a);let _=J(a,i);if(i>_.latest_sequence)throw new d(400,"invalid_request","cursor is beyond the run","after");if(i<_.latest_sequence&&(_.earliest_sequence===null||i<_.earliest_sequence-1))throw new d(409,"stream_gap","events have expired",void 0,{earliest_sequence:_.earliest_sequence,latest_sequence:_.latest_sequence});if(n.destroyed||e.destroyed)return n.destroy(),Promise.resolve();n.writeHead(200,{"Content-Type":v==="sse"?"text/event-stream":"application/x-ndjson","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),v==="sse"?n.write(`: connected
|
|
9
9
|
|
|
10
|
-
`):n.flushHeaders();let r=i;const t=
|
|
11
|
-
data: ${JSON.stringify(A(
|
|
10
|
+
`):n.flushHeaders();let r=i;const t=fe().streamOpened(a);let c=!1,y=!1,m=!1,p;const R=new Promise(h=>{p=h}),o=s(()=>{c||(c=!0,t(r),clearInterval(U),clearInterval(H),b(),P(),n.off("close",o),n.off("drain",f),n.end(),p())},"end"),f=s(()=>{m=!1,l()},"onDrain"),E=s(h=>{c||n.write(v==="sse"?`event: error
|
|
11
|
+
data: ${JSON.stringify(A(h,N,a))}
|
|
12
12
|
|
|
13
|
-
`:`${JSON.stringify({type:"error",...A(
|
|
14
|
-
`),o()},"error"),l=s(()=>{if(!(c||
|
|
15
|
-
`),r=
|
|
13
|
+
`:`${JSON.stringify({type:"error",...A(h,N,a)})}
|
|
14
|
+
`),o()},"error"),l=s(()=>{if(!(c||y)){y=!0;try{if(!O()){S();return}if(I(u,a),_=J(a,r),_.backlog_count>1e3||_.backlog_bytes>32*1024*1024){E(new d(503,"stream_dropped","stream reader fell behind",void 0,{last_sequence:r}));return}for(const h of _.events){if(m)break;m=!n.write(v==="sse"?De(h):`${JSON.stringify(h)}
|
|
15
|
+
`),r=h.sequence_number}}catch(h){E(h instanceof d?h:new d(500,"server_failure","stream failed"))}finally{y=!1}}},"flush"),S=s(()=>E(new d(401,"grant_revoked","grant is not active")),"revoked"),O=s(()=>"class"in u||le(u.grant)!==null,"grantActive"),b=ue(a,l),P="class"in u?()=>{}:ie(u.grant,S),U=setInterval(()=>{!c&&!m&&v==="sse"&&n.write(`: keepalive
|
|
16
16
|
|
|
17
|
-
`)},15e3),
|
|
17
|
+
`)},15e3),q=g.idleMs??xe,X=g.isRunning??oe;let C=Date.now(),M=r;const H=setInterval(()=>{if(c||v!=="sse")return;const h=Date.now();let k=!0;try{k=X(a)}catch{}if(k||r!==M){C=h,M=r;return}h-C<q||m||(n.write(`event: idle
|
|
18
18
|
data: {}
|
|
19
19
|
|
|
20
|
-
`),o())},
|
|
20
|
+
`),o())},g.idleCheckMs??1e3);return H.unref(),n.on("drain",f),n.on("close",o),l(),R}s(Je,"streamRunEvents");function mt(e,n,a,u={}){if(!e.issuer||!e.audience||!e.listener?.address||!Number.isInteger(e.listener.port)||e.listener.port<1||e.listener.port>65535)throw new Error("runtime issuer, audience and listener address/port are required");if(["0.0.0.0","::","[::]"].includes(e.listener.address))throw new Error("runtime listener must bind a specific address");const i=new z(e.issuer,e.audience,a,e.jwksUrl),N=new j,v=new Map;for(const r of V.filter(t=>t.stage!==void 0)){const t=s(()=>({status:500}),"handler");N.register({method:r.method,pattern:r.path,handler:t}),v.set(t,r)}const g=$((r,t)=>{(async()=>{se(r,t,u.deferHead);const c=L();t.setHeader("Request-Id",c),t.setHeader(G,x());let y;try{const m=Y(He(r.headers[F.toLowerCase()]),x());if(m!==null)throw m.status===400?new d(400,m.code,m.message,m.param):new d(426,m.code,m.message,void 0,m.details);const p=new URL(r.url??"/","http://runtime.local");if(pe(p.pathname)){await me(r,t,p,i);return}if((r.method!=="GET"||p.pathname!=="/v1/health")&&(!i.isReady()||!n()))throw new d(503,"runtime_starting","runtime_starting");const R=N.match(r.method??"GET",p.pathname);if(!R)throw new d(404,"not_found","unknown operation");const o=v.get(R.handler);if(!o)throw new d(404,"not_found","unknown operation");if(y=R.params.run_id,o.name==="health"){w(t,200,{status:"ok",api_version:"2026-09-23"});return}if(o.name==="runtime.sync-notice"){if(!await i.verifySyncNotice(r.headers.authorization)){t.writeHead(204),t.end();return}ce({level:"info",event:"daemon.grants.sync_notice",fields:{request_id:c}});try{await a({fresh:!0})}catch{throw new d(503,"service_unavailable","grant sync failed")}t.writeHead(204),t.end();return}if(o.name==="runtime.vendor-status"){await i.verifyDirectoryRead(r.headers.authorization,"vendor_status"),w(t,200,Oe());return}const f=await i.resolve(r.headers.authorization,o.scope);if(t.setHeader("Request-Id",c),y&&I(f,y,["run.message","run.interrupt","run.cancel","run.rename","run.delete"].includes(o.name),o.name==="run.delete"),o.stream==="bytes"){await _e(r,t,f,p.searchParams);return}if(o.stream){Je(r,t,y,f,ke(r,p.searchParams),c,"sse",u.stream);return}if(o.name==="llm"){const l=K(await T(r,ae)),S=new AbortController,O=s(()=>S.abort(),"abort");t.on("close",O);try{const b=await Z(l,S.signal,l.stream?P=>{t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.write(`data: ${JSON.stringify(P)}
|
|
21
21
|
|
|
22
|
-
`)}:void 0);l.stream?(t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.end(`data: [DONE]
|
|
22
|
+
`)}:void 0,void 0,{grantee:f.grantee,spend_cap:f.spend_cap});l.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
|
-
`)):
|
|
24
|
+
`)):w(t,200,b.completion)}finally{t.off("close",O)}return}if(o.name==="usage.get"){w(t,200,te(f.grantee,f.spend_cap,ee(p.searchParams.get("month"))));return}if(o.name==="profile.list"){w(t,200,he(f));return}if(ge.has(o.name)){const l=await ve(o.name,f,R.params.question_id,o.method==="POST"?await T(r):void 0);w(t,l.status,l.body);return}if(Te.has(o.name)){const{signal:l,release:S}=be(t);try{const O=await Ne(o.name,Ee(f),{params:R.params,body:o.method==="POST"?await T(r):void 0,query:p.searchParams},{requestId:c,signal:l});w(t,O.status,O.body)}catch(O){const b=Ae(O,c,!0);t.writeHead(b.status,{"Content-Type":"application/json; charset=utf-8","X-Content-Type-Options":"nosniff",...b.headers}),t.end(JSON.stringify(b.body))}finally{S()}return}if(Se.has(o.name)){const l=Re(o.name,{class:"app",app:f.grantee,scopes:f.resolved},p.searchParams);w(t,l.status,l.body);return}if(we.has(o.name)){const l=await ye(o,f,R.params,p.searchParams,await T(r));w(t,l.status,l.body);return}const E=await Q(o,f,y,p.searchParams,await T(r),r.headers["idempotency-key"]);w(t,E.status,E.body)}catch(m){Me(t,m,c,y)}})()});return{ready:new Promise((r,t)=>{g.once("error",t),g.listen(e.listener.port,e.listener.address,()=>{g.off("error",t),r()})}),close:s(async()=>{i.close(),de(),g.closeAllConnections(),await new Promise((r,t)=>g.close(c=>c?t(c):r()))},"close")}}s(mt,"createAppListener");export{xe as RUN_STREAM_IDLE_MS,mt as createAppListener,ke as cursor,Je as streamRunEvents};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var K=Object.defineProperty;var o=(n,e)=>K(n,"name",{value:e,configurable:!0});import{randomUUID as Q}from"node:crypto";import{relative as V,resolve as R,sep as D}from"node:path";import{ApiError as i}from"../../../api/errors.js";import{armCron as X,cancelCron as ee,getCron as _,listCrons as N,listCronRuns as W,pokeHeldCrons as ne,setCronState as z}from"../../../core/canvas/crons.js";import{getNode as f}from"../../../core/canvas/canvas.js";import{nodeAgentDir as re}from"../../../core/canvas/node-agent-paths.js";import{appSpace as oe}from"../../../core/layout.js";import{appProcessesSandboxed as te,cwdIsMounted as se}from"../../../core/spaces/process.js";import{assertScope as B}from"../../../core/scopes.js";import{InputError as s}from"../../../core/io.js";import{nextSlotAfter as ie}from"../../../core/wake.js";import{executeCron as ae,markSelfCanceledCronRun as ce}from"../../cron-run.js";import{toCronDTO as w,toCronRunDTO as G}from"../map.js";import{assertCeiling as le,canReadNode as de}from"../router.js";import{requireUsableProfile as ue}from"./profiles.js";function j(n,e){const r=n[e];if(typeof r!="string"||r.trim()==="")throw new s({error:"missing_field",message:`cron arm request requires a non-empty "${e}"`,field:e,next:`Send a non-empty string "${e}".`});return r}o(j,"requireString");function k(n,e,r,t){if(n==null)return t;if(typeof n=="string"&&r.includes(n))return n;throw new s({error:`bad_${e}`,message:`"${String(n)}" is not a valid ${e}`,field:e,next:`Use one of: ${r.join(", ")}.`})}o(k,"requireEnum");function pe(n){if(n==="human")return JSON.stringify({human:!0});if(n.startsWith("node:")){const e=n.slice(5).trim();if(e==="")throw new s({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(f(e)===null)throw new s({error:"sink_node_not_found",message:`sink node ${e} 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:e})}if(n.startsWith("spawn:")){const e=n.slice(6).trim();if(e==="")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:e})}throw new s({error:"bad_sink",message:`"${n}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}o(pe,"parseSinkSpec");const fe=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function L(n,e){if(!fe.test(n)||Number.isNaN(Date.parse(n)))throw new s({error:`bad_${e}`,message:`"${n}" is not a UTC ISO instant`,field:e,next:'Send a resolved UTC instant like "2026-06-07T09:00:00.000Z" (the client resolves the time grammar).'});return n}o(L,"requireUtcIso");function he(n){try{ie(n,new Date)}catch(e){throw new s({error:"bad_recur",message:`recur is not advanceable: ${e instanceof Error?e.message:String(e)}`,field:"recur",next:'Send recur JSON the daemon can advance: {"every":"6h"} or {"cron":"0 9 * * *","tz":"America/New_York"}.'})}return n}o(he,"requireRecur");function me(n){let e;try{e=JSON.parse(n)}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 e!="object"||e===null||Array.isArray(e))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,t]of Object.entries(e))if(typeof t!="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 n}o(me,"requireEnvJson");function x(n){const e=n.query.get("profile");return e!==null&&e.trim()!==""?e.trim():null}o(x,"callerProfile");function Z(n,e){return e===null||n.scope==="global"||n.profile===e}o(Z,"inScope");function H(n){return n.principal.class==="person"?null:n.principal.app}o(H,"callerApp");function T(n,e){return de(n.principal,e.grantee,e.created_by??void 0)}o(T,"visibleTo");function _e(n){return typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null}o(_e,"anchorNodeOf");function we(n,e,r,t,d){const c=typeof n.cwd=="string"&&n.cwd.trim()!==""?R(n.cwd):null;if(!te()){if(c===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 c}const O=t!==null&&f(t)!==null?t:void 0,g=o(C=>se(e,C,O,d),"reaches");if(c!==null&&g(c))return c;const a=oe(e).files;if(c!==null&&n.cwd_is_default!==!0)throw new i(400,"invalid_request",`cwd ${c} 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:c,default:a},void 0,{param:"cwd"});const l=r===null?null:f(r)?.cwd??null;return l!==null&&g(l)?l:a}o(we,"resolveArmedCwd");function ge(n){const e=n.body??{},r=j(e,"name"),t=j(e,"command"),d=L(j(e,"fire_at"),"fire_at"),c=typeof e.recur=="string"&&e.recur!==""?he(e.recur):null,O=typeof e.expires_at=="string"&&e.expires_at!==""?L(e.expires_at,"expires_at"):null,g=typeof e.env_json=="string"&&e.env_json!==""?me(e.env_json):null,a=typeof e.sink=="string"&&e.sink.trim()!==""?e.sink.trim():null,l=k(e.on_output,"on_output",["silent","on-failure","always","on-change"],a?.startsWith("node:")?"on-change":"on-failure"),C=k(e.overlap,"overlap",["skip","queue","replace"],"skip"),M=k(e.tier,"tier",["normal","urgent","critical"],"normal"),P=l==="always"||l==="on-change";if(P&&a===null)throw new s({error:"sink_required",message:`on_output "${l}" 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(!P&&a!==null)throw new s({error:"sink_unused",message:`on_output "${l}" 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 Y=a!==null?pe(a):"{}";a?.startsWith("spawn:")&&le(n.ceiling,"crtr:act");const m=n.principal.class==="person"?"app:terminal":n.principal.app,h=n.principal.class==="broker"?n.principal.node:typeof e.created_by=="string"&&e.created_by!==""?e.created_by:null;if(n.principal.class==="broker"&&e.created_by!=null&&e.created_by!==h)throw new i(403,"permission_error","a broker may only arm a cron as its own node");const p=h===null?null:f(h);if(p!==null){if(p.grantee!==m)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."});B(p,"crtr:schedule"),a?.startsWith("spawn:")&&B(p,"crtr:act")}if(e.isolated!==void 0&&typeof e.isolated!="boolean")throw new i(400,"invalid_request","isolated must be a boolean");if(p?.isolated&&e.isolated===!1)throw new i(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const y=p?.isolated===!0||e.isolated===!0;if(y&&p===null)throw new i(400,"invalid_request","an isolated cron requires an existing creator node");const v=y?re(h,m):null;if(v!==null){const I=V(v,typeof e.cwd=="string"&&e.cwd.trim()!==""?R(e.cwd):v);if(I===".."||I.startsWith(`..${D}`)||I.startsWith(D))throw new i(400,"invalid_request","an isolated cron must run in its creating node folder",{param:"cwd"})}const F=v??we(e,m,h,_e(e),y),u=typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null;if(u!==null&&f(u)!==null&&f(u).grantee!==m)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(u!==null&&f(u)===null)throw new s({error:"anchor_not_found",message:`anchor node ${u} 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 E=e.cancel_on_wake===!0;if(E&&u===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 b=e.run_timeout_s??300;if(typeof b!="number"||!Number.isInteger(b)||b<=0)throw new s({error:"bad_run_timeout",message:`run_timeout_s must be a positive integer (got ${String(e.run_timeout_s)})`,field:"run_timeout_s",next:"Send the timeout in whole seconds, e.g. 300."});const q=typeof e.profile=="string"&&e.profile!==""?ue(n,e.profile).profileId:null,U=k(e.scope,"scope",["profile","global"],q!==null?"profile":"global");if(U==="profile"&&q===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.'});const J=Q();return X({cron_id:J,name:r,created_by:h,grantee:m,isolated:y,command:t,fire_at:d,recur:c,tz:typeof e.tz=="string"?e.tz:null,expires_at:O,anchor_node:u,cancel_on_wake:E,cwd:F,env_json:g,profile:q,scope:U,run_timeout_s:b,overlap:C,on_output:l,sink:Y,tier:M}),{status:201,body:w(_(J),null)}}o(ge,"handleArm");function ye(n){const e=x(n);return{status:200,body:(e===null?N({all:!0}):[...N({profile:e}),...N({global:!0})].sort((t,d)=>t.fire_at<d.fire_at?-1:t.fire_at>d.fire_at?1:0)).filter(t=>T(n,t)).map(t=>w(t,A(t)))}}o(ye,"handleList");function A(n){return W(n.cron_id)[0]??null}o(A,"lastRunOf");function S(n){const e=n.params.cronId,r=_(e);if(r===null||!T(n,r))throw new i(404,"cron_not_found",`no cron ${e}`);if(!Z(r,x(n)))throw new i(404,"cron_not_found",`no cron ${e} in this profile \u2014 it is scoped to another profile`);return r}o(S,"requireCron");function $(n,e){const r=H(n);if(r!==null&&e.grantee!==r)throw new i(403,"other_app_run",`cron ${e.cron_id} belongs to another app`);return e}o($,"requireOwnCron");function ve(n){const e=S(n),r=W(e.cron_id);return{status:200,body:{cron:w(e,r[0]??null),runs:r.map(G)}}}o(ve,"handleShow");function be(n){const e=$(n,S(n));z(e.cron_id,"paused");const r=_(e.cron_id);return{status:200,body:w(r,A(r))}}o(be,"handlePause");function ke(n){const e=$(n,S(n));z(e.cron_id,"active");const r=_(e.cron_id);return{status:200,body:w(r,A(r))}}o(ke,"handleResume");async function Se(n){const e=$(n,S(n));if(e.run_state==="running")throw new i(409,"cron_running",`cron ${e.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await ae(e,{outOfBand:!0});if(r===null)throw new i(409,"cron_running",`cron ${e.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:G(r)}}o(Se,"handleRun");function $e(n){const e=new Date().toISOString();return{status:200,body:{unparked:ne(e,H(n)),at:e}}}o($e,"handlePoke");function Oe(n){const e=n.params.cronId,r=_(e);if(r!==null&&!T(n,r))throw new i(404,"cron_not_found",`no cron ${e}`);if(r!==null&&!Z(r,x(n)))throw new i(404,"cron_not_found",`no cron ${e} in this profile \u2014 it is scoped to another profile`);r!==null&&$(n,r);const t=n.query.get("run_id"),d=t!==null&&ce(e,t);return ee(e,{preserveRunningProcess:d}),{status:204}}o(Oe,"handleCancel");const Be=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:ge},{method:"GET",pattern:"/v1/crons",handler:ye},{method:"GET",pattern:"/v1/crons/:cronId",handler:ve},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:be},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:ke},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:$e},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Se},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:Oe}];export{Be as cronRoutes};
|
|
1
|
+
var V=Object.defineProperty;var t=(e,n)=>V(e,"name",{value:n,configurable:!0});import{randomUUID as X}from"node:crypto";import{relative as nn,resolve as z,sep as B}from"node:path";import{ApiError as a}from"../../../api/errors.js";import{armCron as en,cancelCron as rn,getCron as y,listCrons as T,listCronRuns as G,pokeHeldCrons as on,setCronState as F}from"../../../core/canvas/crons.js";import{getNode as h}from"../../../core/canvas/canvas.js";import{appSpace as tn,cronFolder as sn}from"../../../core/layout.js";import{appProcessesSandboxed as an,cwdIsMounted as cn}from"../../../core/spaces/process.js";import{activeGrant as ln,activeGrantForApp as dn,grantFor as un,terminalGrant as pn}from"../../../core/grants/mirror.js";import{readConfig as fn}from"../../../core/config.js";import{assertScope as L}from"../../../core/scopes.js";import{InputError as i}from"../../../core/io.js";import{nextSlotAfter as _n}from"../../../core/wake.js";import{executeCron as hn,markSelfCanceledCronRun as mn}from"../../cron-run.js";import{toCronDTO as k,toCronRunDTO as Z}from"../map.js";import{assertCeiling as gn,canReadNode as wn}from"../router.js";import{checkCronBirth as bn,checkCronFire as vn}from"../../cron/authority.js";import{requireUsableProfile as yn}from"./profiles.js";function A(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new i({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}t(A,"requireString");function C(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new i({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(C,"requireEnum");function kn(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new i({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(h(n)===null)throw new i({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 i({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 i({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(kn,"parseSinkSpec");const Sn=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function H(e,n){if(!Sn.test(e)||Number.isNaN(Date.parse(e)))throw new i({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(H,"requireUtcIso");function $n(e){try{_n(e,new Date)}catch(n){throw new i({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($n,"requireRecur");function Cn(e){let n;try{n=JSON.parse(e)}catch{throw new i({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 i({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 i({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(Cn,"requireEnvJson");function E(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(E,"callerProfile");function M(e,n){return n===null||e.scope==="global"||e.profile===n}t(M,"inScope");function Y(e){return e.principal.class==="person"?null:e.principal.app}t(Y,"callerApp");function U(e,n){return wn(e.principal,n.grantee,n.created_by??void 0)}t(U,"visibleTo");function On(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(On,"anchorNodeOf");function In(e,n,r,o,c,u){const l=typeof e.cwd=="string"&&e.cwd.trim()!==""?z(e.cwd):null;if(!an()){if(l===null)throw new i({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return l}const p=o!==null&&h(o)!==null?o:void 0,d=t(N=>cn(n,N,p,c,void 0,u),"reaches");if(l!==null&&d(l))return l;const s=tn(n).files;if(l!==null&&e.cwd_is_default!==!0)throw new a(400,"invalid_request",`cwd ${l} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${s}`,{cwd:l,default:s},void 0,{param:"cwd"});const m=r===null?null:h(r)?.cwd??null;return m!==null&&d(m)?m:s}t(In,"resolveArmedCwd");function Nn(e,n,r){const o=e.principal;if(o.class==="person"){const s=pn();return{sub:s?.sub??"user:local",grant_id:s?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const s=dn(n);if(s===null)throw new a(401,"grant_revoked",`no active grant for ${n}`);const m=e.ceiling??o.scopes;return{sub:s.sub,grant_id:s.id,scopes:[...m??s.resolved],origin_run_id:null}}if(o.class==="cron"){const s=vn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!s.allowed)throw new a(s.reason==="scope_missing"?403:401,s.reason==="scope_missing"?"scope_missing":"grant_revoked",s.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?[...s.effectiveScopes]:[...o.scopes],origin_run_id:o.origin_run}}if(o.class!=="broker")throw new a(401,"unauthorized","this caller cannot arm a cron");const c=r??h(o.node),u=c?.grant_id??o.grant,l=c?.sub??o.sub,p=u!=null?ln(u):un(n,l);if(p===null){if(n==="app:terminal"&&!fn("user").runtime?.issuer)return{sub:l,grant_id:u??null,scopes:c?.scopes??o.scopes,origin_run_id:c?.run_id??o.run};throw new a(401,"grant_revoked","the node grant is not active")}const d=c?.scopes??o.scopes;return{sub:p.sub,grant_id:p.id,scopes:d===null?[...p.resolved]:[...d],origin_run_id:c?.run_id??o.run}}t(Nn,"armPrincipal");function qn(e){const n=e.body??{},r=A(n,"name"),o=A(n,"command"),c=H(A(n,"fire_at"),"fire_at"),u=typeof n.recur=="string"&&n.recur!==""?$n(n.recur):null,l=typeof n.expires_at=="string"&&n.expires_at!==""?H(n.expires_at,"expires_at"):null,p=typeof n.env_json=="string"&&n.env_json!==""?Cn(n.env_json):null,d=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,s=C(n.on_output,"on_output",["silent","on-failure","always","on-change"],d?.startsWith("node:")?"on-change":"on-failure"),m=C(n.overlap,"overlap",["skip","queue","replace"],"skip"),N=C(n.tier,"tier",["normal","urgent","critical"],"normal"),R=s==="always"||s==="on-change";if(R&&d===null)throw new i({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(!R&&d!==null)throw new i({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 K=d!==null?kn(d):"{}";if(d?.startsWith("spawn:")&&(gn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const _=bn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes});if(!_.allowed)throw new a(_.reason==="scope_missing"?403:401,_.reason==="scope_missing"?"scope_missing":"grant_revoked",_.message)}const b=e.principal.class==="person"?"app:terminal":e.principal.app,v=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!==v)throw new a(403,"permission_error","a broker may only arm a cron as its own node");const g=v===null?null:h(v);if(g!==null){if(g.grantee!==b)throw new i({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});L(g,"crtr:schedule"),d?.startsWith("spawn:")&&L(g,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new a(400,"invalid_request","isolated must be a boolean");if(g?.isolated&&n.isolated===!1)throw new a(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const D=g?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(D&&n.isolated===!1)throw new a(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const q=D||n.isolated===!0,w=Nn(e,b,g),j=X(),S=q?sn(b,j):null;if(S!==null&&n.cwd_is_default!==!0){const _=nn(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?z(n.cwd):S);if(_===".."||_.startsWith(`..${B}`)||_.startsWith(B))throw new a(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const Q=S??In(n,b,v,On(n),q,{sub:w.sub,grantId:w.grant_id,scopes:w.scopes}),f=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(f!==null&&h(f)!==null&&h(f).grantee!==b)throw new i({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&&h(f)===null)throw new i({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 W=n.cancel_on_wake===!0;if(W&&f===null)throw new i({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 i({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 P=typeof n.profile=="string"&&n.profile!==""?yn(e,n.profile).profileId:null,x=C(n.scope,"scope",["profile","global"],P!==null?"profile":"global");if(x==="profile"&&P===null)throw new i({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 en({cron_id:j,name:r,created_by:v,grantee:b,sub:w.sub,grant_id:w.grant_id,scopes:w.scopes,origin_run_id:w.origin_run_id,isolated:q,command:o,fire_at:c,recur:u,tz:typeof n.tz=="string"?n.tz:null,expires_at:l,anchor_node:f,cancel_on_wake:W,cwd:Q,env_json:p,profile:P,scope:x,run_timeout_s:$,overlap:m,on_output:s,sink:K,tier:N}),{status:201,body:k(y(j),null)}}t(qn,"handleArm");function jn(e){const n=E(e);return{status:200,body:(n===null?T({all:!0}):[...T({profile:n}),...T({global:!0})].sort((o,c)=>o.fire_at<c.fire_at?-1:o.fire_at>c.fire_at?1:0)).filter(o=>U(e,o)).map(o=>k(o,J(o)))}}t(jn,"handleList");function J(e){return G(e.cron_id)[0]??null}t(J,"lastRunOf");function O(e){const n=e.params.cronId,r=y(n);if(r===null||!U(e,r))throw new a(404,"cron_not_found",`no cron ${n}`);if(!M(r,E(e)))throw new a(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(O,"requireCron");function I(e,n){const r=Y(e);if(r!==null&&n.grantee!==r)throw new a(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(I,"requireOwnCron");function Pn(e){const n=O(e),r=G(n.cron_id);return{status:200,body:{cron:k(n,r[0]??null),runs:r.map(Z)}}}t(Pn,"handleShow");function Tn(e){const n=I(e,O(e));F(n.cron_id,"paused");const r=y(n.cron_id);return{status:200,body:k(r,J(r))}}t(Tn,"handlePause");function An(e){const n=I(e,O(e));F(n.cron_id,"active");const r=y(n.cron_id);return{status:200,body:k(r,J(r))}}t(An,"handleResume");async function En(e){const n=I(e,O(e));if(n.run_state==="running")throw new a(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await hn(n,{outOfBand:!0});if(r===null)throw new a(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:Z(r)}}t(En,"handleRun");function Un(e){const n=new Date().toISOString();return{status:200,body:{unparked:on(n,Y(e)),at:n}}}t(Un,"handlePoke");function Jn(e){const n=e.params.cronId,r=y(n);if(r!==null&&!U(e,r))throw new a(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!M(r,E(e)))throw new a(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"),c=e.principal.class==="cron"?o===e.principal.execution&&n===e.principal.cron:!0,u=o!==null&&c&&mn(n,o);return rn(n,{preserveRunningProcess:u}),{status:204}}t(Jn,"handleCancel");const ee=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:qn},{method:"GET",pattern:"/v1/crons",handler:jn},{method:"GET",pattern:"/v1/crons/:cronId",handler:Pn},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:Tn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:An},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:Un},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:En},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:Jn}];export{ee as cronRoutes};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var A=Object.defineProperty;var o=(e,t)=>A(e,"name",{value:t,configurable:!0});import{closeSync as _,constants as q,fstatSync as B,readSync as T}from"node:fs";import{isAbsolute as R,resolve as I}from"node:path";import{appSpace as L}from"../../../core/layout.js";import{ApiError as u}from"../../../api/index.js";import{notFound as b,usage as c}from"../../../core/errors.js";import{InputError as v}from"../../../core/io.js";import{RunError as p}from"../../../core/runs/operations.js";import{isBeneathRefusal as M,listBeneath as k,openBeneath as z,writeFileBeneath as F}from"../../../core/spaces/open-beneath.js";import{fileRoot as N}from"../../../core/spaces/permissions.js";import{nodeMayReach as P,runNodeIds as O,runPlace as D}from"../../../core/runs/confinement.js";import{resolvedScopes as w}from"../router.js";const m=512*1024,E=1024*1024,C=1e3;function h(e,t){if(e===null||e==="")throw c(`a file ${t} requires a \`path\` ${t==="write"?"body field":"query param"}`);if(!R(e))throw c(`file ${t} path must be absolute`,{received:e});return e}o(h,"requireAbsolutePath");function $(e){if(e==null)return"utf8";if(e==="utf8"||e==="base64")return e;throw c("encoding must be `utf8` or `base64`",{received:e})}o($,"parseEncoding");function d(e,t,r){if(e instanceof u||e instanceof v||e instanceof p)throw e;if(M(e))throw new p(400,"invalid_request",`${t} meets a symlink, leaves its permitted root, or is not a regular file`,"path");const n=e.code;throw n==="ENOENT"?b(`no such file: ${t}`,{received:t}):n==="EACCES"||n==="EPERM"?new u(403,"forbidden",`permission denied ${r} ${t}`):new u(500,"internal",`cannot ${r} ${t}: ${e instanceof Error?e.message:String(e)}`)}o(d,"mapFilesystemError");function S(e,t){const r=D(e);if(r?.isolated){const n=new Set(O(r.run));return{isolated:!0,reaches:o(i=>n.has(i),"reaches")}}return{isolated:!1,reaches:o(n=>P(e,t,n,t),"reaches")}}o(S,"runConfinement");function y(e,t,r){const n=e.principal,i=n.class==="person"?null:n.class==="app"?{app:n.app,resolved:n.scopes,launched:null}:n.class==="cron"?{app:n.app,resolved:w(n),launched:n.scopes}:{app:n.app,resolved:w(n),launched:n.scopes,run:S(n.node,n.app)},s=N(i,t,r);if("root"in s)return s.root;throw"hidden"in s?b(`no such file: ${t}`,{received:t}):new p(403,"scope_missing",s.missing?`missing scope ${s.missing}`:`no permission names ${t}`,void 0,void 0,s.missing??void 0)}o(y,"rootFor");function W(e,t,r){let n;try{n=z(e,t,q.O_RDONLY)}catch(i){return d(i,t,"reading")}try{const i=B(n);if(i.isDirectory())throw c(`file peek path is a directory: ${t}`,{received:t});const s=Buffer.allocUnsafe(Math.min(i.size,m)+1),l=T(n,s,0,s.length,0),g=l>m,a=s.subarray(0,Math.min(l,m)).toString(r);return{path:t,content:a,truncated:g}}catch(i){return d(i,t,"reading")}finally{_(n)}}o(W,"readHead");function Y(e){const t=h(e.query.get("path"),"peek");return{status:200,body:W(y(e,t,"read"),t,$(e.query.get("encoding")))}}o(Y,"handlePeek");function Z(e){return e.length%4===0&&/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(e)}o(Z,"isBase64");function j(e){if(typeof e!="object"||e===null||Array.isArray(e))throw c("file write requires an object body");const t=e,r=h(typeof t.path=="string"?t.path:null,"write");if(typeof t.content!="string")throw c("file write requires a string `content` body field");const n=$(t.encoding);if(n==="base64"&&!Z(t.content))throw c("base64 file content must be valid base64");return{path:r,content:t.content,encoding:n}}o(j,"parseWriteBody");function G(e,t,r){if(r.byteLength>E)throw new u(413,"file_too_large",`file content exceeds the ${E} byte limit`);try{return F(e,t,r,{mode:process.platform==="linux"?432:384,atomic:!0}),{path:t,bytes_written:r.byteLength}}catch(n){return d(n,t,"writing")}}o(G,"writeAtomically");function U(e){const{path:t,content:r,encoding:n}=j(e.body);return{status:200,body:G(y(e,t,"rw"),t,Buffer.from(r,n))}}o(U,"handleWrite");function X(e){if(e===null)return C;const t=Number(e);if(!Number.isInteger(t)||t<1)throw c("file list limit must be a positive integer",{received:e});return t}o(X,"parseLimit");function H(e){const t=h(e.query.get("path"),"list"),r=X(e.query.get("limit")),n=y(e,t,"read");let i;try{i=k(n,t,r+1)}catch(a){return d(a,t,"listing")}const s=e.principal;if(s.class==="broker"&&I(t)===L(s.app).nodes){const a=S(s.node,s.app);i=i.filter(({name:f})=>a.reaches(f))}const l=i.slice(0,r).map(({name:a,stat:f})=>({name:a,type:f.isFile()?"file":f.isDirectory()?"dir":"other",size:f.size,modified:f.mtime.toISOString()}));return{status:200,body:{path:t,entries:l,truncated:i.length>r}}}o(H,"handleList");const ce=[{method:"GET",pattern:"/v1/files/peek",scope:"crtr:act",handler:Y},{method:"POST",pattern:"/v1/files/write",scope:"crtr:act",handler:U},{method:"GET",pattern:"/v1/files/list",scope:"crtr:act",handler:H}];export{ce as fileRoutes};
|
|
@@ -1,2 +1,10 @@
|
|
|
1
1
|
import type { RouteTable } from '../router.js';
|
|
2
|
+
import { RunError } from '../../../core/runs/operations.js';
|
|
3
|
+
import { type SocketPrincipal } from '../server.js';
|
|
4
|
+
import { type SpendTarget } from '../../../core/usage/ledger.js';
|
|
5
|
+
/** Who a socket caller's model call bills: a broker's node bills its grant's app under
|
|
6
|
+
* that grant's cap; the person (and a local scoped token) bills the person's own agent, uncapped. */
|
|
7
|
+
export declare function spendTarget(principal: SocketPrincipal | undefined): SpendTarget;
|
|
8
|
+
/** `cap_exhausted` names when the count starts over and what the person can do (contract 2 error table). */
|
|
9
|
+
export declare function capFields(error: RunError): Record<string, unknown>;
|
|
2
10
|
export declare const llmRoutes: RouteTable;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)}:void 0,
|
|
3
|
-
`),{status:200}}if(
|
|
1
|
+
var w=Object.defineProperty;var s=(e,t)=>w(e,"name",{value:t,configurable:!0});import{randomUUID as _}from"node:crypto";import{MODEL_CONTEXT_BODY_MAX_BYTES as v}from"../../../core/llm/body-limits.js";import{parseLlmRequest as A,runLlm as x}from"../../../core/llm/run.js";import{RunError as c}from"../../../core/runs/operations.js";import{errorCodes as S}from"../../../api/error-codes.js";import{getNode as E}from"../../../core/canvas/canvas.js";import{socketPrincipal as u}from"../server.js";import{activeGrant as R,activeGrantForApp as T,grantFor as k,grantsSource as O}from"../../../core/grants/mirror.js";import{parseUsageMonth as q,UNCAPPED_GRANTEE as I,usageFor as N}from"../../../core/usage/ledger.js";function g(e,t){if(t==="broker"||t==="person"||t===void 0||!e||typeof e!="object"||Array.isArray(e)||!("rate_limit"in e))return e;const{rate_limit:n,...a}=e;return a}s(g,"operatorDetails");function y(e){if(e?.class==="broker"){const t=e.grant?R(e.grant):k(e.app,e.sub);return{grantee:e.app,spend_cap:t&&t.grantee===e.app?t.spend_cap:null}}if(e?.class==="app"){const t=O();if(t===null||t==="local")return{grantee:e.app,spend_cap:null};let n;try{n=T(e.app)}catch{throw new c(401,"grant_revoked",`${e.app} has several active grants; a local token cannot name which user's cap applies`)}if(!n)throw new c(401,"grant_revoked",`${e.app} has no active grant`);return{grantee:e.app,spend_cap:n.spend_cap}}return{grantee:I,spend_cap:null}}s(y,"spendTarget");function h(e){const t=S[e.code];return t?t.type:e.status===429?"rate_limit_error":e.status<500?"invalid_request_error":"server_error"}s(h,"errorType");function D(e){if(e.code!=="cap_exhausted")return{};const t=e.details?.reset_at;return{...typeof t=="string"?{reset_at:t}:{},user_action:"raise_cap"}}s(D,"capFields");const C=[{method:"POST",pattern:"/v1/llm",scope:"crtr:llm",maxBodyBytes:v,handler:s(async({body:e,res:t,req:n})=>{const a=_();t.setHeader("Request-Id",a);const m=new AbortController,f=s(()=>m.abort(),"abort");t.on("close",f);let l;try{const r=A(e),o=u(n),d=o?.class==="broker"?E(o.node):null;if(o?.class==="broker"&&!d)throw new c(401,"unauthorized","node not found");const i=y(o),b=await x(r,m.signal,r.stream?p=>{typeof p.served_model=="string"&&(l=p.served_model),t.headersSent||t.writeHead(200,{"content-type":"application/x-ndjson; charset=utf-8","cache-control":"no-cache"}),t.write(`${JSON.stringify(p)}
|
|
2
|
+
`)}:void 0,d?{cwd:d.cwd,profileId:d.profile_id??null}:void 0,i);return r.stream?(t.headersSent||t.writeHead(200,{"content-type":"application/x-ndjson; charset=utf-8","cache-control":"no-cache"}),t.end(),{status:200}):{status:200,body:b.completion}}catch(r){if(t.headersSent){const o=r instanceof c?r:new c(503,"upstream_model_unavailable","model stream failed"),d=o.details&&typeof o.details=="object"&&!Array.isArray(o.details)?o.details:{},i=g(l!==void 0&&typeof d.served_model!="string"?{...d,served_model:l}:o.details,u(n)?.class);return t.end(`${JSON.stringify({error:{code:o.code,message:o.message,...i===void 0?{}:{details:i},type:"server_error",origin:"runtime",retryable:o.retryable,request_id:a}})}
|
|
3
|
+
`),{status:200}}if(r instanceof c){const o=g(r.details,u(n)?.class);return r.code==="all_routes_exhausted"&&t.setHeader("Retry-After",String(r.details.retry_after_s)),{status:r.status,body:{error:{code:r.code,message:r.message,...o===void 0?{}:{details:o},type:h(r),...r.param?{param:r.param}:{},origin:"runtime",retryable:r.retryable,request_id:a,...r.code==="all_routes_exhausted"?{retry_after_s:r.details.retry_after_s}:{},...D(r)}}}}throw r}finally{t.off("close",f)}},"handler")},{method:"GET",pattern:"/v1/usage",handler:s(({req:e})=>{const t=_(),n={"Request-Id":t};try{const a=y(u(e));return{status:200,headers:n,body:N(a.grantee,a.spend_cap,q(new URL(e.url??"/","http://runtime.local").searchParams.get("month")))}}catch(a){if(!(a instanceof c))throw a;return{status:a.status,headers:n,body:{error:{code:a.code,message:a.message,type:h(a),...a.param?{param:a.param}:{},origin:"runtime",retryable:!1,request_id:t}}}}},"handler")}];export{D as capFields,C as llmRoutes,y as spendTarget};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var be=Object.defineProperty;var a=(e,t)=>be(e,"name",{value:t,configurable:!0});import{prepared as Y}from"../../../core/sqlite-statements.js";import{envProfileId as ve}from"../../../shared/env.js";import{pausedCanvasProfiles as ke}from"../../../core/profiles/manifest.js";import{existsSync as C,realpathSync as Q,statSync as X}from"node:fs";import{isAbsolute as L,resolve as T}from"node:path";import{appendSituationalContext as Se,formatSituationalProse as Oe}from"../../../core/runtime/situational-context.js";import{closeNode as Re}from"../../../core/runtime/close.js";import{reviveNode as Ie}from"../../../core/runtime/revive.js";import{nowIso as M}from"../../../core/fs-utils.js";import{assertLaunchModelRegistered as F,forkNode as $e,resolveForkSource as Ne,spawnChildPrepared as Ae}from"../../../core/runtime/spawn.js";import{forkSourceNode as Ce,newNodeId as Te}from"../../../core/runtime/nodes.js";import{abandonCreateRequest as Z,childrenOf as Ee,completeCreateRequest as q,findCreateRequest as Pe,getNode as k,recordCreateRequestNode as De,reserveCreateRequest as ee,reserveCreateRequestNode as je,listNodes as ne,recordRespawnAttempt as xe,subscribersOf as Le,subscriptionsOf as te,setMessageWait as Me,updateNode as N}from"../../../core/canvas/canvas.js";import{subtreeIds as Fe}from"../../../core/canvas/nav-model.js";import{withCanvasWrite as G}from"../../../core/canvas/db.js";import{nodeObject as Ge}from"../../../core/graph/objects.js";import{recordActedOn as Be}from"../../../core/graph/edges.js";import{publish as Ke}from"../../../core/graph/events.js";import{readerFor as We}from"../reader.js";import{relocateSessionCwd as Ve,unbindSession as ze}from"../../../core/canvas/sessions.js";import{ticketCountsForNodes as Je}from"../../../core/canvas/attention.js";import{isSafeNodeId as Ue}from"../../../core/canvas/paths.js";import{contextDir as oe,nodeAgentDir as re}from"../../../core/canvas/node-agent-paths.js";import{existsBeneath as He,isAbsentOrRefused as Ye,isBeneathRefusal as Qe,readdirBeneath as Xe}from"../../../core/spaces/open-beneath.js";import{readNodeMessagesPage as Ze,readNodeSession as qe,readNodeSnapshot as en,transcriptMarkdown as nn}from"../../../core/runtime/node-read.js";import{assembleNodeSubject as tn}from"../../../core/substrate/subject.js";import{reviveAll as on}from"../../../core/runtime/revive-all.js";import{promoteAsync as rn,requestYieldAsync as dn,reshapeNodeAsync as B}from"../../../core/runtime/promote.js";import{recycleNode as sn}from"../../../core/runtime/recycle.js";import{relaunchRoot as an}from"../../../core/runtime/reset.js";import{assertReopenGate as ln}from"../../../core/runtime/reopen.js";import{writeYieldMessage as cn,readGoal as fn}from"../../../core/runtime/kickoff.js";import{ensureRoadmap as un,hasRoadmap as pn}from"../../../core/runtime/roadmap.js";import{renameWindow as mn}from"../../../core/runtime/placement.js";import{buildLaunchSpecAsync as hn,explicitModelIntent as wn,normalizeModel as yn,readMergedLaunchConfigAsync as ie}from"../../../core/runtime/launch.js";import{modelRequestFromConfig as _n}from"../../../core/model-routes.js";import{persistDormantModel as gn,setModelLive as bn}from"../../../core/runtime/model-swap.js";import{waitForBrokerViewSocket as vn}from"../../../core/runtime/view-socket.js";import{BrokerUnreachableError as kn}from"../../../core/runtime/broker-request.js";import{boundFleet as K}from"../../../core/runtime/fleet.js";import{admitLegacyStructuredOutput as Sn}from"../../structured-requests/sidecar.js";import{OUTCOME_PAYLOAD_MAX_BYTES as de}from"../../../core/canvas/types.js";import{HUMAN_ACTION_NAME_PATTERN as On}from"../../../core/human/action-binding.js";import{resolveHumanAction as Rn}from"../../../core/human-actions.js";import{parseDurationMs as In}from"../../../core/wake.js";import{claimWarmNode as $n,refillWarmPool as se,resumeWarmClaim as Nn,warmPoolEnabled as An}from"../../../core/runtime/warm-pool.js";import{assertLaunchKind as W,isLaunchKind as Cn,resolveLaunchTarget as Tn}from"../../../core/runtime/launch-target.js";import{fullName as En}from"../../../core/canvas/labels.js";import{reportsForNode as ae}from"./reports.js";import{requireUsableProfile as Pn}from"./profiles.js";import{assertProfileActive as Dn,loadExactProfileManifest as jn}from"../../../core/profiles/manifest.js";import{getCron as xn}from"../../../core/canvas/crons.js";import{cronWakeOrigin as Ln}from"../../../core/runtime/bearings.js";import{boundDaemonControl as Mn}from"../../control.js";import{notFound as E,usage as p}from"../../../core/errors.js";import{assertScope as Fn,validateScopes as le}from"../../../core/scopes.js";import{scopeAllowed as R}from"../../../shared/env.js";import{toCloseResultDTO as Gn,toNodeDetailDTO as V,toNodeSummaryDTO as Bn,toReviveResultDTO as Kn}from"../map.js";import{ApiError as m}from"../../../api/index.js";import{InputError as h}from"../../../core/io.js";import{brokerMayReach as z,canReadNode as ce,resolvedScopes as Wn}from"../router.js";import{fileRoot as Vn}from"../../../core/spaces/file-root.js";import{activeGrant as zn,terminalGrant as Jn}from"../../../core/grants/mirror.js";import{readConfig as Un}from"../../../core/config.js";import{hasConfiguredProviderCredential as Hn,NO_PROVIDER_CREDENTIAL_MESSAGE as fe}from"../../../core/runtime/provider-credential-gate.js";function w(e){const t=k(e);if(t===null)throw E(`unknown node: ${e}`,{received:e});return t}a(w,"requireMeta");function J(e){return{subscribesTo:te(e),subscribers:Le(e),children:Ee(e)}}a(J,"edgesFor");function g(e){const t=w(e);return V(t,J(e))}a(g,"detailOf");function Yn(e){let t=ae(e).length;try{t+=Xe(re(e),oe(e)).length}catch(n){if(!Ye(n))throw n}return t}a(Yn,"countFiles");function Qn(e){if(typeof e!="object"||e===null)throw p("a node create requires a JSON body");const t=e;if(t.kind!==void 0&&(typeof t.kind!="string"||t.kind.trim()===""))throw p("kind must be a non-empty string when provided");if(t.description!==void 0&&(typeof t.description!="string"||t.description.trim()===""))throw p("description must be a non-empty string when provided");if(t.creator!==void 0&&(typeof t.creator!="string"||t.creator.trim()===""))throw p("creator must be a non-empty node id when provided");if(t.scopes!==void 0&&le(t.scopes),t.isolated!==void 0&&typeof t.isolated!="boolean")throw new m(400,"invalid_request","isolated must be a boolean",{param:"isolated"});const n=t.root_lifecycle;if(n!==void 0&&n!=="resident"&&n!=="terminal")throw p("root_lifecycle must be 'resident' or 'terminal'");if(t.request_key!==void 0&&(typeof t.request_key!="string"||t.request_key.trim()===""||t.request_key.length>128))throw p("request_key must be a non-empty string of at most 128 characters when provided");return t}a(Qn,"parseCreateBody");function Xn(e){if(e===void 0)return;const t=In(e),n=t===null?null:new Date(Date.now()+t);if(t===null||!Number.isFinite(t)||t<=0||n===null||!Number.isFinite(n.getTime()))throw new m(422,"invalid_deadline","`deadline` must be a positive representable duration");return n.toISOString()}a(Xn,"parseDeadline");function Zn(e){if(e!==void 0){if(typeof e!="object"||e===null||Array.isArray(e))throw new m(400,"invalid_request","`outcome_delivery` must be an object");if(typeof e.action!="string"||!On.test(e.action))throw new m(400,"invalid_request","`outcome_delivery.action` must be a declared action name");if(e.payload!==void 0){let t;try{t=JSON.stringify(e.payload)}catch{throw new m(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable")}if(t===void 0)throw new m(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable");if(Buffer.byteLength(t,"utf8")>de)throw new m(413,"payload_too_large",`serialized payload exceeds ${de} bytes`)}return e}}a(Zn,"parseOutcomeDelivery");function qn(e,t){const n=Rn(e.action,t);if(!("kind"in n))return{actionName:e.action,argv:n.argv,cwd:n.cwd,configOrigin:n.configOrigin,...e.payload===void 0?{}:{payload:e.payload}};throw n.kind==="unknown_action"?new m(422,"unknown_action",`no action named ${e.action} is declared from ${t}`):new m(422,"invalid_action",`${e.action} is declared in ${n.configOrigin} but unusable: ${n.reason}`,{reason:n.reason,configOrigin:n.configOrigin})}a(qn,"resolveOutcomeDelivery");function et(e,t,n){const r={kind:e.kind,cwd:n,prompt:e.prompt??""};return e.mode!==void 0&&(r.mode=e.mode),e.name!==void 0&&(r.name=e.name),e.description!==void 0&&(r.description=e.description),t!==void 0&&(r.parent=t),e.creator!==void 0&&(r.creator=e.creator),e.scopes!==void 0&&(r.scopes=e.scopes),e.isolated!==void 0&&(r.isolated=e.isolated),e.root===!0&&(r.root=!0),e.root_lifecycle!==void 0&&(r.rootLifecycle=e.root_lifecycle),(e.worktree===!0||typeof e.worktree=="string")&&(r.worktree=!0),typeof e.worktree=="string"&&(r.worktreeBase=e.worktree),e.worktree!==void 0&&(r.worktreeCwd=T(e.pin_cwd??e.cwd??n)),e.fork_from!==void 0&&(r.forkFrom=e.fork_from),e.model!==void 0&&(r.model=e.model),e.profile!==void 0&&(r.profile=e.profile),e.situational_context!==void 0&&(r.situationalContext=e.situational_context),e.node_id!==void 0&&(r.nodeId=e.node_id),r}a(et,"buildRecipe");function nt(e,t,n){return!An()||e.prefer_warm!==!0||e.root!==!0||e.parent!=null||e.root_lifecycle==="terminal"||(e.prompt??"")!==""||e.model!==void 0||e.fork_from!==void 0||e.worktree!==void 0||e.node_id!==void 0||e.scopes!==void 0||e.isolated!==void 0?null:{kind:e.kind,mode:e.mode??"base",cwd:t,profileId:n,model:e.model??null,situationalContext:e.situational_context??null,name:e.name??null,creator:e.creator??null}}a(nt,"warmRequestFor");import{resolveCreateKind as zo}from"../../../core/runtime/launch-target.js";function ue(e){const t=e.modelIntent===void 0?void 0:`${e.modelIntent.family}/${e.modelIntent.strength}`;return _n(e.model,t,e.modelExact===!0)}a(ue,"requestFromLaunch");function tt(e,t){if(t===null)return e;if(e.scopes===void 0)return{...e,scopes:[...t]};const n=e.scopes.filter(r=>!R(t,r));if(n.length===0)return e;throw new m(403,"scope_denied",`scopes exceed the bearer token's ceiling: ${n.join(", ")}`,{scopes:n,ceiling:t})}a(tt,"withinCeiling");async function pe(e,t,n){const r=(await hn(t.kind,t.mode,{lifecycle:t.lifecycle,hasManager:e.parent!==null,model:t.model,modelOverride:!0,cwd:e.cwd,profileId:e.profile_id??null})).launch;await F(ue(r),{cwd:e.cwd,profileId:e.profile_id??null},n)}a(pe,"assertNodeModelChangeRegistered");function me(e,t){const n=e.principal;if(n.class!=="broker")return;const r=Vn({app:n.app,resolved:Wn(n),launched:n.scopes},t,"read");if("root"in r)return;const o="missing"in r?r.missing:null;throw new m(403,"scope_missing",o?`the child's directory ${t} needs missing scope ${o}`:`no permission of this run names the child's directory ${t}`,void 0,void 0,o?{scope:o}:void 0)}a(me,"assertCallerReaches");const U=new Map;async function ot(e,t={},n={}){const r=tt(Qn(e.body),e.ceiling);if(e.principal.class==="broker"){const u=e.principal;if(r.prefer_warm===!0&&u.app!=="app:terminal"&&(r.prefer_warm=!1),r.parent!=null){const i=w(r.parent);if(i.grantee!==u.app)throw new m(403,"other_app_run","cannot create a child under another app's node");if(!z(u,r.parent,i.grantee??"app:terminal"))throw E(`unknown node: ${r.parent}`,{received:r.parent})}if(r.creator!==void 0&&r.creator!==u.node)throw new m(403,"other_app_run","cannot create for another node");if(r.fork_from!==void 0){let i=null;try{i=Ce(Ne(r.fork_from))}catch{}if(i?.grantee!==u.app||!z(u,i.node_id,i.grantee))throw new m(403,"other_app_run","cannot fork another app's conversation")}r.creator=u.node}r.creator!==void 0&&Fn(w(r.creator),"crtr:act");const o=r.request_key?.trim();if(o===void 0||r.node_id!==void 0&&Ue(r.node_id)&&k(r.node_id)!==null&&Pe(o)?.nodeId!==r.node_id)return P(e,r,t,n);const d=U.get(o);if(d!==void 0)return d;const s=rt(e,r,o,t,n);U.set(o,s);try{return await s}finally{U.delete(o)}}a(ot,"handleCreate");async function rt(e,t,n,r,o){const d=ee(n);if(d.nodeId!==null&&k(d.nodeId)!==null){if(d.state!=="warm_claimed"){const s=k(d.nodeId);if(e.principal.class!=="person"&&!ce(e.principal,s.grantee??"app:terminal",d.nodeId))throw new m(409,"create_request_conflict","request_key belongs to another caller");return{status:201,body:g(d.nodeId)}}return P(e,t,r,o,d)}try{return await P(e,t,r,o,d)}catch(s){const u=ee(n);throw(u.nodeId===null||k(u.nodeId)===null)&&Z(n,u.nodeId),s}}a(rt,"handleKeyedCreateRequest");async function P(e,t,n,r,o=void 0){const d=r.deadlineAt??Xn(t.deadline),s=Zn(t.outcome_delivery),u=t.parent??void 0;if(u==null&&t.root!==!0)throw p("a node create requires a parent (or root: true for an independent root)");let i=null;if(t.output_schema!==void 0)try{i=JSON.parse(t.output_schema)}catch{throw p("output_schema must be a JSON schema string")}const{profileId:l,kind:y,cwd:f,launch:c}=await Tn(t,{validateKind:ge});me(e,f),t.worktree!==void 0&&me(e,T(t.pin_cwd??t.cwd??f));const _={...t,kind:y},S=s===void 0?void 0:qn(s,f);if(await F(ue(c),{cwd:f,profileId:l},n.modelRegistry),!await Hn(l))throw new m(412,"no_provider_credential",fe,{error:"no_provider_credential",message:fe,next:"The person running this runtime logs a provider in with `crtr sys setup`, or stores a provider key on the profile."});let $=null;{const O=o===void 0||o.nodeId===null||o.state==="warm_claimed"?nt(_,f,l):null,j=i===null&&r.beforeBrokerLaunch===void 0?void 0:v=>{i!==null&&Sn(v,"terminal",i),r.beforeBrokerLaunch?.(v)};if(o?.state==="warm_claimed"&&o.nodeId!==null){const v=O===null?null:await Nn(O,o.nodeId,j);if(v===null)throw Z(o.requestKey,o.nodeId),new m(503,"create_resume_failed",`could not resume warm claim ${o.nodeId}; retry the create`);return q(o.requestKey,v),{status:201,body:g(v)}}if(O!==null){const v=await $n(O,{...d===void 0?{}:{deadlineAt:d},...S===void 0?{}:{outcomeDelivery:S},...o===void 0?{}:{requestKey:o.requestKey}},j);if(v!==null)return o!==void 0&&q(o.requestKey,v),se(O),{status:201,body:g(v)};$=O}const x=o?.nodeId??t.node_id??(o===void 0?void 0:Te());o!==void 0&&o.nodeId===null&&je(o.requestKey,x);const A=et(x===void 0?_:{..._,node_id:x},u,f);d!==void 0&&(A.deadlineAt=d),S!==void 0&&(A.outcomeDelivery=S),A.readiness=t.root===!0?"await":"launch";const H=await Ae(A,j,()=>{$!==null&&se($)});return o!==void 0&&De(o.requestKey,H.node.node_id),{status:201,body:g(H.node.node_id)}}}a(P,"handleCreateRequest");function b(e,t){const n=e.query.getAll(t);if(n.length>1)throw p(`${t} must appear at most once`);return n[0]}a(b,"singleQueryValue");function he(e,t){if(e!==void 0){if(e==="1"||e==="true")return!0;if(e==="0"||e==="false")return!1;throw p(`${t} must be true or false`)}}a(he,"optionalBoolean");function D(e,t,n){if(e!==void 0){if(n.includes(e))return e;throw p(`${t} must be one of: ${n.join(", ")}`)}}a(D,"oneOf");function it(e){const t=new Set(["status","lifecycle","kind","mode","profile_id","profile_prefix","parent","top_level","under","hanging","include"]);for(const[_]of e.query)if(!t.has(_))throw p(`unknown nodes query parameter: ${_}`);const n=D(b(e,"status"),"status",["active","idle","done","dead","canceled"]),r=D(b(e,"lifecycle"),"lifecycle",["terminal","resident"]),o=b(e,"kind"),d=D(b(e,"mode"),"mode",["base","orchestrator"]),s=b(e,"profile_id"),u=b(e,"profile_prefix"),i=b(e,"parent"),l=he(b(e,"top_level"),"top_level"),y=b(e,"under"),f=he(b(e,"hanging"),"hanging"),c=D(b(e,"include"),"include",["activity"]);if(i!==void 0&&l===!0)throw p("parent and top_level=true cannot be combined");return{...n!==void 0?{status:n}:{},...r!==void 0?{lifecycle:r}:{},...o!==void 0?{kind:o}:{},...d!==void 0?{mode:d}:{},...s!==void 0?{profile_id:s}:{},...u!==void 0?{profile_prefix:u}:{},...i!==void 0?{parent:i}:{},...l!==void 0?{top_level:l}:{},...y!==void 0?{under:y}:{},...f!==void 0?{hanging:f}:{},...c!==void 0?{include:c}:{}}}a(it,"parseListQuery");function dt(e){const t=it(e);let n=t.status!==void 0?ne({status:t.status}):ne();if(e.principal.class!=="person"&&(n=n.filter(s=>ce(e.principal,s.grantee??"app:terminal",s.node_id))),t.lifecycle!==void 0&&(n=n.filter(s=>s.lifecycle===t.lifecycle)),t.kind!==void 0&&(n=n.filter(s=>s.kind===t.kind)),t.mode!==void 0&&(n=n.filter(s=>s.mode===t.mode)),t.profile_id!==void 0&&(n=n.filter(s=>s.profile_id===t.profile_id)),t.profile_prefix!==void 0&&(n=n.filter(s=>s.profile_id?.startsWith(t.profile_prefix))),t.parent!==void 0&&(n=n.filter(s=>s.parent===t.parent)),t.top_level===!0&&(n=n.filter(s=>s.parent===null)),t.under!==void 0){const s=new Set(Fe(t.under));n=n.filter(u=>s.has(u.node_id))}t.hanging===!0&&(n=n.filter(s=>s.parent!==null&&k(s.parent)===null));const r=t.include==="activity"?Je(n.map(s=>s.node_id)):null,o=n.length>0?ke():new Set;return{status:200,body:n.map(s=>{const u=Bn(s,s.profile_id!==null&&o.has(s.profile_id));if(r!==null){const i=ae(s.node_id);u.activity={latest_report:i[0]??null,final_report:s.final_report===null?null:i.find(l=>l.name===s.final_report)??null,pending_human_count:r[s.node_id]??0}}return u})}}a(dt,"handleList");function st(e){const t=e.params.id;return w(t),{status:200,body:g(t)}}a(st,"handleDetail");async function at(e){const t=e.params.id;w(t);const n=await en(t);return{status:200,body:{node_id:t,snapshot:{messages:n.snapshot.messages,...n.snapshot.messageIds===void 0?{}:{messageIds:n.snapshot.messageIds},messageVisibility:n.snapshot.messageVisibility,turnVisibility:n.snapshot.turnVisibility,stats:n.snapshot.stats,state:n.snapshot.state,display:n.snapshot.display,...n.snapshot.queued===void 0?{}:{queued:n.snapshot.queued},...n.snapshot.toolGroupSummaries===void 0?{}:{toolGroupSummaries:n.snapshot.toolGroupSummaries},...n.snapshot.workingActivity===void 0?{}:{workingActivity:n.snapshot.workingActivity}},commands:n.commands.map(o=>({name:o.name,description:o.description,source:o.source})),captured_at:M()}}}a(at,"handleSnapshot");async function lt(e){const t=e.params.id,n=tn(t);if(n===null)throw E(`unknown node: ${t}`,{received:t});return{status:200,body:n}}a(lt,"handleSubject");function ct(e){for(const[d]of e.query)if(d!=="cursor"&&d!=="limit")throw p(`unknown messages query parameter: ${d}`);const t=e.query.getAll("cursor"),n=e.query.getAll("limit");if(t.length>1)throw p("cursor must appear at most once");if(n.length>1)throw p("limit must appear at most once");if(n.length===0)return t.length===0?{}:{cursor:t[0]};const r=n[0],o=Number(r);if(!Number.isSafeInteger(o)||r==="")throw p("limit must be an integer from 1 through 500");return t.length===0?{limit:o}:{cursor:t[0],limit:o}}a(ct,"parseMessagesQuery");async function ft(e){const t=e.params.id,n=await Ze(t,ct(e));return{status:200,body:{node_id:t,messages:n.messages,...n.messageIds===void 0?{}:{message_ids:n.messageIds},message_visibility:n.messageVisibility,next_cursor:n.nextCursor,captured_at:M()}}}a(ft,"handleMessages");function ut(e){const t=e.params.id;w(t);const n=qe(t);return{status:200,body:{node_id:t,session_file:n.sessionFile,session_jsonl:n.sessionJsonl,system_prompt:n.systemPrompt,captured_at:M()}}}a(ut,"handleSession");async function pt(e){const t=e.params.id;return w(t),{status:200,body:{node_id:t,markdown:await nn(t)}}}a(pt,"handleTranscript");function mt(e){const t=e.params.id;w(t);const n=a((o,d)=>({node_id:o,label:k(o)?.name??o,dir:oe(o),self:d,files:Yn(o)}),"root"),r=[n(t,!0),...te(t).map(o=>n(o.node_id,!1))].filter(o=>ht(o.node_id,o.dir)||o.files>0);return{status:200,body:{node_id:t,roots:r}}}a(mt,"handleContext");function ht(e,t){try{return He(re(e),t)}catch(n){if(Qe(n))return!1;throw n}}a(ht,"hasDir");async function wt(e){const t=w(e.params.id),n=e.principal.class==="broker"?e.principal.scopes:e.ceiling,r=n===null?void 0:t.scopes==null?[...n]:[...new Set([...t.scopes.filter(d=>R(n,d)),...n.filter(d=>R(t.scopes,d))])],o=await $e(t.node_id,r,e.principal.class==="broker"?e.principal.node:void 0);return{status:201,body:g(o.node.node_id)}}a(wt,"handleFork");async function yt(e){const t=e.params.id;w(t);const n=e.body,r=n?.resume??!0,o=n?.reopen??!1;ln(t,o);const d=!r&&n?.cron_id!==void 0?xn(n.cron_id):null,s=await Ie(t,{resume:r,capacity:"refuse",...d!==null?{wakeReason:Ln(d,"schedule_revive")}:{}});s.outcome!=="already-live"&&xe(t,{failures:0,notBefore:null});const u=k(t)?.status??"active";return{status:200,body:Kn(t,s,u)}}a(yt,"handleRevive");async function _t(e){const t=e.params.id;return{status:200,body:{newNodeId:(await an(t))?.newNodeId??null}}}a(_t,"handleRelaunchRoot");function gt(e){const t=e.params.id;w(t);const n=e.body;if(n?.caller_pid!==void 0&&(!Number.isInteger(n.caller_pid)||n.caller_pid<=0))throw p("caller_pid must be a positive integer when provided");const r=Mn(),o=Re(t,{rootEvent:n?.finish===!0?"finish":"cancel",callerPid:n?.caller_pid,...e.principal.class==="broker"?{closer:e.principal.node}:{},...r===null?{}:{registerDetached:r.registerDetached}});return{status:200,body:Gn(o)}}a(gt,"handleClose");async function bt(e){const t=e.params.id;w(t);const n=await sn(t);return{status:200,body:{recycled:n.recycled,finalized:n.finalized,new_root:n.newRoot,delivered:n.delivered}}}a(bt,"handleRecycle");async function vt(e){const t=e.params.id;return w(t),await B(t,{lifecycle:"terminal"}),I(e,t,["lifecycle"]),{status:200,body:g(t)}}a(vt,"handleDemote");async function kt(e,t={}){const n=e.params.id,r=w(n),o=e.body??{};o.kind!==void 0&&await W(o.kind,r.cwd,r.profile_id??null),o.model!==void 0&&await pe(r,{kind:o.kind??r.kind,mode:"orchestrator",lifecycle:o.resident===!0?"resident":r.lifecycle,model:o.model},t.modelRegistry);const d=await rn(n,{...o.kind!==void 0?{kind:o.kind}:{},...o.resident===!0?{resident:!0}:{},...o.model!==void 0?{model:o.model}:{}});I(e,n,["mode",...o.kind!==void 0?["kind"]:[],...o.resident===!0?["lifecycle"]:[],...o.model!==void 0?["model"]:[]]);const s=V(d.meta,J(n));return s.roadmap_written=d.roadmapWritten,s.roadmap_ref=d.roadmapRef,s.goal_path=d.goalPath,{status:200,body:s}}a(kt,"handlePromote");async function St(e,t={}){const n=e.params.id,r=w(n),o=e.body??{};o.kind!==void 0&&await W(o.kind,r.cwd,r.profile_id??null),o.model!==void 0&&await pe(r,{kind:o.kind??r.kind,mode:o.promote===!0?"orchestrator":r.mode,lifecycle:r.lifecycle,model:o.model},t.modelRegistry);const d=o.note??"Refresh: continue against your roadmap or goal.";cn(n,d);const s=await dn(n,{...o.promote===!0?{promote:!0}:{},...o.kind!==void 0?{kind:o.kind}:{},...o.model!==void 0?{model:o.model}:{}});return{status:200,body:V(s.meta,J(n))}}a(St,"handleYield");function Ot(e){const t=e.params.id;w(t);const n=e.body;if(n===void 0||typeof n.controller!="string"||n.controller.trim()==="")throw p("a wait requires a controller node id");const r=k(n.controller);if(r===null)throw E(`unknown controller: ${n.controller}`,{received:n.controller});if(r.status!=="active"&&r.status!=="idle")throw new m(422,"unprocessable",`controller ${n.controller} is ${r.status}`);return Me(t,n.controller),{status:200,body:g(t)}}a(Ot,"handleWait");async function Rt(e){const t=e.principal,n=await on(t.class==="broker"?o=>o.grantee===t.app&&z(t,o.node_id,o.grantee):void 0);return{status:200,body:{revived:[...n.revived],failed:[...n.failed],frozen:[...n.frozen]}}}a(Rt,"handleReviveAll");const It=new Set(["interrupt","steer","after-turn","next-turn"]);function $t(e){if(e===null)return null;const t=a(r=>{throw new h({error:"invalid_listen",message:r,received:e,field:"listen",next:'Send an object mapping levels "1".."10" to interrupt|steer|after-turn|next-turn (e.g. {"3":"next-turn"}), or null to reset to the global default.'})},"bad");if(typeof e!="object"||Array.isArray(e))return t("listen must be an object or null");const n=Object.entries(e);if(n.length===0)return t("listen must name at least one level");for(const[r,o]of n)/^(?:[1-9]|10)$/.test(r)||t(`listen level must be 1..10: ${r}`),(typeof o!="string"||!It.has(o))&&t(`listen delivery must be interrupt|steer|after-turn|next-turn: ${String(o)}`);return JSON.stringify(Object.fromEntries(n))}a($t,"listenJson");function I(e,t,n,r){n.length===0&&r===void 0||G(o=>{if(r?.(o),n.length===0)return;const d=Ge(o,t),s=We(o,e),u=s.class==="person"?s.person:s.class==="app"?s.app:s.node;Be(o,u,d),Ke(o,{event:"configured",object:d,actor:u,attrs:{changed:n.join(",")}})})}a(I,"recordConfigured");function we(e){const t=K().get(e);if(t===void 0)return!1;try{return process.kill(t.pid,"SIGTERM"),!0}catch{return!1}}a(we,"restartLiveBroker");function ye(e,t){try{return Q(e)===Q(t)}catch{return!1}}a(ye,"sameDirectory");async function Nt(e,t={}){const n=e.params.id,r=e.body;let o;if(r===void 0)o={};else if(typeof r=="object"&&r!==null&&!Array.isArray(r))o=r;else throw new h({error:"cwd_invalid",message:"cwd repair requires a non-null object with a non-empty cwd string",received:r,field:"cwd",next:"Send a config patch with a non-empty absolute cwd string."});if(o.profile!==void 0){if(typeof o.profile!="string"||o.profile==="")throw new h({error:"profile_invalid",message:"profile must be a non-empty profile id or name",received:o.profile,field:"profile",next:"Send a profile id or exact name."});if(Object.keys(o).some(f=>f!=="profile"&&f!=="restart"&&o[f]!==void 0))throw new h({error:"profile_not_exclusive",message:"profile can only be combined with restart",field:"profile",next:"Send profile (and optionally restart) alone."});const i=w(n),l=Pn(e,o.profile);Dn(l.profileId);const y=jn(l.profileId).manifest.grantee??"app:terminal";if(y!==(i.grantee??"app:terminal"))throw new h({error:"profile_grantee_mismatch",message:`profile ${l.profileId} belongs to ${y}, node ${n} to ${i.grantee}`,received:o.profile,field:"profile",next:"Pick a profile owned by the same app as the node."});return(i.profile_id??null)!==l.profileId&&(Cn(i.kind)?(await W(i.kind,i.cwd,l.profileId),await B(n,{profileId:l.profileId})):N(n,{profile_id:l.profileId}),I(e,n,["profile"])),o.restart===!0&&we(n),{status:200,body:g(n)}}if(Object.prototype.hasOwnProperty.call(o,"cwd")){const i=o.cwd;if(typeof i!="string"||i==="")throw new h({error:"cwd_invalid",message:"cwd must be a non-empty string",received:i,field:"cwd",next:"Send a non-empty absolute cwd string."});if(Object.keys(o).some(c=>c!=="cwd"&&c!=="keep_session"&&c!=="restart"&&o[c]!==void 0))throw new h({error:"cwd_not_exclusive",message:"cwd repair cannot be combined with another config field",received:o,field:"cwd",next:"Send cwd as the only config field."});const l=w(n),y=C(l.cwd);if(o.keep_session===!0||y&&L(i)&&C(i)&&ye(l.cwd,i)){if(!L(i))throw new h({error:"cwd_not_absolute",message:`cwd must be an absolute path: ${i}`,received:i,field:"cwd",next:"Send an absolute path."});const c=T(i);let _=!1;try{_=X(c).isDirectory()}catch{}if(!_)throw new h({error:"cwd_missing",message:`cwd is not an existing directory: ${c}`,received:i,field:"cwd",next:"Send an existing absolute directory."});if(y&&!ye(l.cwd,c))throw new h({error:"cwd_not_same_directory",message:`${c} is not the same directory as the recorded ${l.cwd}; --keep-session only relocates an alias`,received:i,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 h({error:"cwd_node_managed_worktree_open",message:`${n} owns an open managed worktree and cannot relocate its cwd`,received:i,field:"cwd",next:"Reconcile the managed worktree first."});const S=l.cwd;return S!==c&&(G($=>{N(n,{cwd:c}),Y($,"UPDATE crons SET cwd = ? WHERE anchor_node = ? AND cwd = ?").run(c,n,S),Ve(n,S,c)}),I(e,n,["cwd"])),o.restart===!0&&we(n),{status:200,body:g(n)}}if(K().has(n)){const c=`${n} cannot repair its cwd while its broker is live`;throw new m(409,"cwd_node_live",c,{error:"cwd_node_live",message:c,received:i,field:"cwd",next:"Let the node become dormant, then retry the cwd repair."})}if(l.fork_from!=null)throw new h({error:"cwd_node_forked",message:`${n} is a fork and cannot repair its cwd; recorded cwd: ${l.cwd}`,received:i,field:"cwd",next:`Restore ${l.cwd}, then revive the fork normally.`});if(l.managed_worktree?.state==="open")throw new h({error:"cwd_node_managed_worktree_open",message:`${n} owns an open managed worktree and cannot repair its cwd`,received:i,field:"cwd",next:"Restore or reconcile the recorded checkout, then use the managed-worktree close or abandon flow."});if(C(l.cwd))throw new h({error:"cwd_still_present",message:`${n} recorded cwd still exists: ${l.cwd}; cwd repair is not a relocation command`,received:i,field:"cwd",next:"This command only repairs a node whose recorded cwd is gone."});if(!L(i))throw new h({error:"cwd_not_absolute",message:`cwd must be an absolute path: ${i}`,received:i,field:"cwd",next:"Send an absolute path."});const f=T(i);if(!C(f))throw new h({error:"cwd_missing",message:`cwd does not exist: ${f}`,received:i,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."});try{if(!X(f).isDirectory())throw new h({error:"cwd_not_a_directory",message:`cwd is not a directory: ${f}`,received:i,field:"cwd",next:"Send an existing absolute directory."})}catch(c){throw c instanceof h?c:new h({error:"cwd_missing",message:`cwd does not exist: ${f}`,received:i,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."})}return G(()=>{N(n,{cwd:f}),ze(n)}),I(e,n,["cwd"]),{status:200,body:g(n)}}let d=w(n);const s=o.listen===void 0?void 0:$t(o.listen);if(o.scopes!==void 0){const i=le(o.scopes),l=d.grantee==="app:terminal"?Jn():d.grant_id?zn(d.grant_id):null,y=d.grantee==="app:terminal"&&!Un("user").runtime?.issuer;if(!i.every(f=>R(d.scopes,f)&&(l?R(l.resolved,f):y)))throw new h({error:"unknown_scope",message:"scopes may only narrow the node's current effective set",received:i,field:"scopes",next:"Send only scopes the node currently holds."});if(i.includes("crtr:act")&&!R(i,"crtr:llm"))throw new m(400,"invalid_scope","crtr:act requires crtr:llm");d=N(n,{scopes:i})}if(o.situational_context!==void 0&&Se(n,Oe(o.situational_context)),o.model!==void 0){const i=(await ie(d.cwd,d.profile_id??null)).modelLadders,l=wn(o.model,i),y=yn(o.model,i);await F({kind:"concrete",spec:y},{cwd:d.cwd,profileId:d.profile_id??null},t.modelRegistry);const f=K();let c=!1;if(f.isClaimed(n)&&await vn(n,f.get(n)?.handle.exited)&&(c=await bn(n,y,l).then(()=>!0,_=>{if(_ instanceof kn)return!1;throw _})),!c){if(f.isClaimed(n)){const _=`${n}'s broker did not accept the model switch; the model was not changed`;throw new m(409,"broker_not_ready",_,{error:"broker_not_ready",message:_,received:o.model,field:"model",next:"Check the node with `crtr node show`, then retry the model change."})}if(!y.includes("/"))throw p(`model must resolve to a provider/id spec: ${o.model}`);gn(n,y,l)}d=w(n)}if(o.kind!==void 0||o.mode!==void 0||o.lifecycle!==void 0){o.kind!==void 0&&await ge(o.kind);const i=o.kind??d.kind,l=o.mode??d.mode,y=o.lifecycle??d.lifecycle;let f;l==="orchestrator"&&!pn(n)&&(f=a(()=>{const c=fn(n);un(n,c!==null&&c.trim()!==""?{goal:c.trim()}:{})},"beforePersist")),d=await B(n,{...o.kind!==void 0?{kind:i}:{},...o.mode!==void 0?{mode:l}:{},...o.lifecycle!==void 0?{lifecycle:y}:{}},f)}o.name!==void 0&&(d=N(n,{name:o.name}),d.window&&mn(d.window,En(d)));const u=["scopes","situational_context","model","kind","mode","lifecycle","name","listen"].filter(i=>o[i]!==void 0);return I(e,n,u,s===void 0?void 0:i=>{Y(i,"UPDATE objects SET listen_json = ? WHERE id = ? AND type = 'node'").run(s,n)}),{status:200,body:g(n)}}a(Nt,"handleConfig");async function _e(){return(await ie(process.cwd(),ve()||null)).kinds}a(_e,"daemonKinds");async function ge(e){const t=Object.keys(await _e());if(!t.includes(e))throw p(`unknown kind: ${e}`,{received:e,next:`Valid kinds: ${t.sort().join(", ")}.`})}a(ge,"assertKindServer");async function At(){const e=await _e();return{status:200,body:{kinds:Object.keys(e).sort().map(n=>({kind:n,when_to_use:e[n]?.whenToUse??null,available_to:e[n]?.availableTo??null}))}}}a(At,"handleListKinds");const Ko=[{method:"POST",pattern:"/v1/nodes",scope:"crtr:act",handler:ot},{method:"GET",pattern:"/v1/nodes",handler:dt},{method:"GET",pattern:"/v1/kinds",handler:a(()=>At(),"handler")},{method:"POST",pattern:"/v1/nodes/revive-all",scope:"crtr:act",handler:Rt},{method:"GET",pattern:"/v1/nodes/:id",handler:st},{method:"GET",pattern:"/v1/nodes/:id/snapshot",handler:at},{method:"GET",pattern:"/v1/nodes/:id/subject",handler:lt},{method:"GET",pattern:"/v1/nodes/:id/messages",handler:ft},{method:"GET",pattern:"/v1/nodes/:id/session",handler:ut},{method:"GET",pattern:"/v1/nodes/:id/transcript",handler:pt},{method:"GET",pattern:"/v1/nodes/:id/context",handler:mt},{method:"POST",pattern:"/v1/nodes/:id/fork",scope:"crtr:act",handler:wt},{method:"POST",pattern:"/v1/nodes/:id/revive",scope:"crtr:act",handler:yt},{method:"POST",pattern:"/v1/nodes/:id/relaunch-root",scope:"crtr:act",handler:_t},{method:"POST",pattern:"/v1/nodes/:id/close",handler:gt},{method:"POST",pattern:"/v1/nodes/:id/recycle",scope:"crtr:act",handler:bt},{method:"POST",pattern:"/v1/nodes/:id/demote",scope:"crtr:act",handler:vt},{method:"POST",pattern:"/v1/nodes/:id/promote",scope:"crtr:act",handler:kt},{method:"POST",pattern:"/v1/nodes/:id/yield",scope:"crtr:act",handler:St},{method:"POST",pattern:"/v1/nodes/:id/wait",handler:Ot},{method:"PATCH",pattern:"/v1/nodes/:id/config",handler:Nt}];export{Nt as handleConfig,ot as handleCreate,wt as handleFork,dt as handleList,kt as handlePromote,St as handleYield,Ko as nodeRoutes,zo as resolveCreateKind};
|
|
1
|
+
var Se=Object.defineProperty;var a=(e,t)=>Se(e,"name",{value:t,configurable:!0});import{prepared as X}from"../../../core/sqlite-statements.js";import{envProfileId as Oe}from"../../../shared/env.js";import{pausedCanvasProfiles as Re}from"../../../core/profiles/manifest.js";import{existsSync as A,realpathSync as Z,statSync as q}from"node:fs";import{isAbsolute as F,resolve as T}from"node:path";import{appendSituationalContext as Ie,formatSituationalProse as $e}from"../../../core/runtime/situational-context.js";import{closeNode as Ce}from"../../../core/runtime/close.js";import{reviveNode as Ne}from"../../../core/runtime/revive.js";import{nowIso as G}from"../../../core/fs-utils.js";import{assertLaunchModelRegistered as B,forkNode as Ae,resolveForkSource as Te,spawnChildPrepared as Pe}from"../../../core/runtime/spawn.js";import{forkSourceNode as Ee,newNodeId as ee}from"../../../core/runtime/nodes.js";import{abandonCreateRequest as ne,childrenOf as De,completeCreateRequest as te,findCreateRequest as Le,getNode as k,recordCreateRequestNode as je,reserveCreateRequest as oe,reserveCreateRequestNode as Me,listNodes as re,recordRespawnAttempt as Fe,subscribersOf as Ge,subscriptionsOf as ie,setMessageWait as Be,updateNode as N}from"../../../core/canvas/canvas.js";import{subtreeIds as xe}from"../../../core/canvas/nav-model.js";import{withCanvasWrite as x}from"../../../core/canvas/db.js";import{nodeObject as Ke}from"../../../core/graph/objects.js";import{recordActedOn as We}from"../../../core/graph/edges.js";import{publish as Ve}from"../../../core/graph/events.js";import{readerFor as ze}from"../reader.js";import{relocateSessionCwd as Je,unbindSession as Ue}from"../../../core/canvas/sessions.js";import{ticketCountsForNodes as He}from"../../../core/canvas/attention.js";import{isSafeNodeId as Ye}from"../../../core/canvas/paths.js";import{contextDir as de,nodeAgentDir as K}from"../../../core/canvas/node-agent-paths.js";import{checkCronBirth as Qe}from"../../cron/authority.js";import{cronBirthPlacement as Xe}from"../../cron/armed-context.js";import{existsBeneath as Ze,isAbsentOrRefused as qe,isBeneathRefusal as en,readdirBeneath as nn}from"../../../core/spaces/open-beneath.js";import{readNodeMessagesPage as tn,readNodeSession as on,readNodeSnapshot as rn,transcriptMarkdown as dn}from"../../../core/runtime/node-read.js";import{assembleNodeSubject as sn}from"../../../core/substrate/subject.js";import{reviveAll as an}from"../../../core/runtime/revive-all.js";import{promoteAsync as ln,requestYieldAsync as cn,reshapeNodeAsync as W}from"../../../core/runtime/promote.js";import{recycleNode as fn}from"../../../core/runtime/recycle.js";import{relaunchRoot as un}from"../../../core/runtime/reset.js";import{assertReopenGate as pn}from"../../../core/runtime/reopen.js";import{writeYieldMessage as mn,readGoal as hn}from"../../../core/runtime/kickoff.js";import{ensureRoadmap as wn,hasRoadmap as yn}from"../../../core/runtime/roadmap.js";import{renameWindow as _n}from"../../../core/runtime/placement.js";import{buildLaunchSpecAsync as gn,explicitModelIntent as bn,normalizeModel as vn,readMergedLaunchConfigAsync as se}from"../../../core/runtime/launch.js";import{modelRequestFromConfig as kn}from"../../../core/model-routes.js";import{persistDormantModel as Sn,setModelLive as On}from"../../../core/runtime/model-swap.js";import{waitForBrokerViewSocket as Rn}from"../../../core/runtime/view-socket.js";import{BrokerUnreachableError as In}from"../../../core/runtime/broker-request.js";import{boundFleet as V}from"../../../core/runtime/fleet.js";import{admitLegacyStructuredOutput as $n}from"../../structured-requests/sidecar.js";import{OUTCOME_PAYLOAD_MAX_BYTES as ae}from"../../../core/canvas/types.js";import{HUMAN_ACTION_NAME_PATTERN as Cn}from"../../../core/human/action-binding.js";import{resolveHumanAction as Nn}from"../../../core/human-actions.js";import{parseDurationMs as An}from"../../../core/wake.js";import{claimWarmNode as Tn,refillWarmPool as le,resumeWarmClaim as Pn,warmPoolEnabled as En}from"../../../core/runtime/warm-pool.js";import{assertLaunchKind as z,isLaunchKind as Dn,resolveLaunchTarget as Ln}from"../../../core/runtime/launch-target.js";import{fullName as jn}from"../../../core/canvas/labels.js";import{reportsForNode as ce}from"./reports.js";import{requireUsableProfile as Mn}from"./profiles.js";import{assertProfileActive as Fn,loadExactProfileManifest as Gn}from"../../../core/profiles/manifest.js";import{getCron as fe}from"../../../core/canvas/crons.js";import{cronWakeOrigin as Bn}from"../../../core/runtime/bearings.js";import{boundDaemonControl as xn}from"../../control.js";import{notFound as P,usage as h}from"../../../core/errors.js";import{assertScope as Kn,validateScopes as ue}from"../../../core/scopes.js";import{scopeAllowed as R}from"../../../shared/env.js";import{toCloseResultDTO as Wn,toNodeDetailDTO as J,toNodeSummaryDTO as Vn,toReviveResultDTO as zn}from"../map.js";import{ApiError as m}from"../../../api/index.js";import{InputError as w}from"../../../core/io.js";import{brokerMayReach as U,canReadNode as pe,resolvedScopes as Jn}from"../router.js";import{fileRoot as Un}from"../../../core/spaces/file-root.js";import{activeGrant as Hn,terminalGrant as Yn}from"../../../core/grants/mirror.js";import{readConfig as Qn}from"../../../core/config.js";import{hasConfiguredProviderCredential as Xn,NO_PROVIDER_CREDENTIAL_MESSAGE as me}from"../../../core/runtime/provider-credential-gate.js";function y(e){const t=k(e);if(t===null)throw P(`unknown node: ${e}`,{received:e});return t}a(y,"requireMeta");function H(e){return{subscribesTo:ie(e),subscribers:Ge(e),children:De(e)}}a(H,"edgesFor");function g(e){const t=y(e);return J(t,H(e))}a(g,"detailOf");function Zn(e){let t=ce(e).length;try{t+=nn(K(e),de(e)).length}catch(n){if(!qe(n))throw n}return t}a(Zn,"countFiles");function qn(e){if(typeof e!="object"||e===null)throw h("a node create requires a JSON body");const t=e;if(t.kind!==void 0&&(typeof t.kind!="string"||t.kind.trim()===""))throw h("kind must be a non-empty string when provided");if(t.description!==void 0&&(typeof t.description!="string"||t.description.trim()===""))throw h("description must be a non-empty string when provided");if(t.creator!==void 0&&(typeof t.creator!="string"||t.creator.trim()===""))throw h("creator must be a non-empty node id when provided");if(t.scopes!==void 0&&ue(t.scopes),t.isolated!==void 0&&typeof t.isolated!="boolean")throw new m(400,"invalid_request","isolated must be a boolean",{param:"isolated"});const n=t.root_lifecycle;if(n!==void 0&&n!=="resident"&&n!=="terminal")throw h("root_lifecycle must be 'resident' or 'terminal'");if(t.request_key!==void 0&&(typeof t.request_key!="string"||t.request_key.trim()===""||t.request_key.length>128))throw h("request_key must be a non-empty string of at most 128 characters when provided");return t}a(qn,"parseCreateBody");function et(e){if(e===void 0)return;const t=An(e),n=t===null?null:new Date(Date.now()+t);if(t===null||!Number.isFinite(t)||t<=0||n===null||!Number.isFinite(n.getTime()))throw new m(422,"invalid_deadline","`deadline` must be a positive representable duration");return n.toISOString()}a(et,"parseDeadline");function nt(e){if(e!==void 0){if(typeof e!="object"||e===null||Array.isArray(e))throw new m(400,"invalid_request","`outcome_delivery` must be an object");if(typeof e.action!="string"||!Cn.test(e.action))throw new m(400,"invalid_request","`outcome_delivery.action` must be a declared action name");if(e.payload!==void 0){let t;try{t=JSON.stringify(e.payload)}catch{throw new m(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable")}if(t===void 0)throw new m(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable");if(Buffer.byteLength(t,"utf8")>ae)throw new m(413,"payload_too_large",`serialized payload exceeds ${ae} bytes`)}return e}}a(nt,"parseOutcomeDelivery");function tt(e,t){const n=Nn(e.action,t);if(!("kind"in n))return{actionName:e.action,argv:n.argv,cwd:n.cwd,configOrigin:n.configOrigin,...e.payload===void 0?{}:{payload:e.payload}};throw n.kind==="unknown_action"?new m(422,"unknown_action",`no action named ${e.action} is declared from ${t}`):new m(422,"invalid_action",`${e.action} is declared in ${n.configOrigin} but unusable: ${n.reason}`,{reason:n.reason,configOrigin:n.configOrigin})}a(tt,"resolveOutcomeDelivery");function ot(e,t,n){const r={kind:e.kind,cwd:n,prompt:e.prompt??""};return e.mode!==void 0&&(r.mode=e.mode),e.name!==void 0&&(r.name=e.name),e.description!==void 0&&(r.description=e.description),t!==void 0&&(r.parent=t),e.creator!==void 0&&(r.creator=e.creator),e.scopes!==void 0&&(r.scopes=e.scopes),e.isolated!==void 0&&(r.isolated=e.isolated),e.root===!0&&(r.root=!0),e.root_lifecycle!==void 0&&(r.rootLifecycle=e.root_lifecycle),(e.worktree===!0||typeof e.worktree=="string")&&(r.worktree=!0),typeof e.worktree=="string"&&(r.worktreeBase=e.worktree),e.worktree!==void 0&&(r.worktreeCwd=T(e.pin_cwd??e.cwd??n)),e.fork_from!==void 0&&(r.forkFrom=e.fork_from),e.model!==void 0&&(r.model=e.model),e.profile!==void 0&&(r.profile=e.profile),e.situational_context!==void 0&&(r.situationalContext=e.situational_context),e.node_id!==void 0&&(r.nodeId=e.node_id),r}a(ot,"buildRecipe");function rt(e,t,n){return!En()||e.prefer_warm!==!0||e.root!==!0||e.parent!=null||e.root_lifecycle==="terminal"||(e.prompt??"")!==""||e.model!==void 0||e.fork_from!==void 0||e.worktree!==void 0||e.node_id!==void 0||e.scopes!==void 0||e.isolated!==void 0?null:{kind:e.kind,mode:e.mode??"base",cwd:t,profileId:n,model:e.model??null,situationalContext:e.situational_context??null,name:e.name??null,creator:e.creator??null}}a(rt,"warmRequestFor");import{resolveCreateKind as Qo}from"../../../core/runtime/launch-target.js";function he(e){const t=e.modelIntent===void 0?void 0:`${e.modelIntent.family}/${e.modelIntent.strength}`;return kn(e.model,t,e.modelExact===!0)}a(he,"requestFromLaunch");function it(e,t){if(t===null)return e;if(e.scopes===void 0)return{...e,scopes:[...t]};const n=e.scopes.filter(r=>!R(t,r));if(n.length===0)return e;throw new m(403,"scope_denied",`scopes exceed the bearer token's ceiling: ${n.join(", ")}`,{scopes:n,ceiling:t})}a(it,"withinCeiling");async function we(e,t,n){const r=(await gn(t.kind,t.mode,{lifecycle:t.lifecycle,hasManager:e.parent!==null,model:t.model,modelOverride:!0,cwd:e.cwd,profileId:e.profile_id??null})).launch;await B(he(r),{cwd:e.cwd,profileId:e.profile_id??null},n)}a(we,"assertNodeModelChangeRegistered");function ye(e,t){const n=e.principal;if(n.class!=="broker"&&n.class!=="cron")return;const r=Un({app:n.app,resolved:Jn(n),launched:n.scopes},t,"read");if("root"in r)return;const o="missing"in r?r.missing:null;throw new m(403,"scope_missing",o?`the child's directory ${t} needs missing scope ${o}`:`no permission of this run names the child's directory ${t}`,void 0,void 0,o?{scope:o}:void 0)}a(ye,"assertCallerReaches");const Y=new Map;async function dt(e,t={},n={}){const r=it(qn(e.body),e.ceiling);if(e.principal.class==="broker"){const l=e.principal;if(r.prefer_warm===!0&&l.app!=="app:terminal"&&(r.prefer_warm=!1),r.parent!=null){const i=y(r.parent);if(i.grantee!==l.app)throw new m(403,"other_app_run","cannot create a child under another app's node");if(!U(l,r.parent,i.grantee??"app:terminal"))throw P(`unknown node: ${r.parent}`,{received:r.parent})}if(r.creator!==void 0&&r.creator!==l.node)throw new m(403,"other_app_run","cannot create for another node");if(r.fork_from!==void 0){let i=null;try{i=Ee(Te(r.fork_from))}catch{}if(i?.grantee!==l.app||!U(l,i.node_id,i.grantee))throw new m(403,"other_app_run","cannot fork another app's conversation")}r.creator=l.node}if(e.principal.class==="cron"){const l=e.principal;for(const c of["parent","creator","fork_from","node_id"])if(r[c]!=null)throw new m(403,"permission_error",`a cron cannot set ${c} on a node it starts`);r.prefer_warm=!1;const i=Qe({grantee:l.app,sub:l.sub,grant_id:l.grant,scopes:l.scopes});if(!i.allowed)throw new m(i.reason==="scope_missing"?403:401,i.reason==="scope_missing"?"scope_missing":"grant_revoked",i.message);if(r.scopes!==void 0){const c=r.scopes.find(p=>!R(i.effectiveScopes,p)||!R(l.scopes,p));if(c!==void 0)throw new m(403,"scope_missing",`scope ${c} is not held by this cron`,void 0,void 0,{scope:c})}else l.scopes!==null&&(r.scopes=[...l.scopes]);r.node_id=ee(),l.isolated&&(r.isolated=!0,r.cwd=K(r.node_id,l.app))}r.creator!==void 0&&Kn(y(r.creator),"crtr:act");const o=r.request_key?.trim();if(o===void 0||r.node_id!==void 0&&Ye(r.node_id)&&k(r.node_id)!==null&&Le(o)?.nodeId!==r.node_id)return E(e,r,t,n);const d=Y.get(o);if(d!==void 0)return d;const s=st(e,r,o,t,n);Y.set(o,s);try{return await s}finally{Y.delete(o)}}a(dt,"handleCreate");async function st(e,t,n,r,o){const d=oe(n);if(d.nodeId!==null&&k(d.nodeId)!==null){if(d.state!=="warm_claimed"){const s=k(d.nodeId);if(e.principal.class!=="person"&&!pe(e.principal,s.grantee??"app:terminal",d.nodeId))throw new m(409,"create_request_conflict","request_key belongs to another caller");return{status:201,body:g(d.nodeId)}}return E(e,t,r,o,d)}try{return await E(e,t,r,o,d)}catch(s){const l=oe(n);throw(l.nodeId===null||k(l.nodeId)===null)&&ne(n,l.nodeId),s}}a(st,"handleKeyedCreateRequest");async function E(e,t,n,r,o=void 0){const d=r.deadlineAt??et(t.deadline),s=nt(t.outcome_delivery),l=e.principal.class==="cron"?Xe({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:fe(e.principal.cron)?.created_by??null},t.node_id):null;l!==null&&(t.root=l.root===!0);const i=l?.parent??t.parent??void 0;if(i==null&&t.root!==!0)throw h("a node create requires a parent (or root: true for an independent root)");let c=null;if(t.output_schema!==void 0)try{c=JSON.parse(t.output_schema)}catch{throw h("output_schema must be a JSON schema string")}const{profileId:p,kind:u,cwd:f,launch:_}=await Ln(t,{validateKind:ke});ye(e,f),t.worktree!==void 0&&ye(e,T(t.pin_cwd??t.cwd??f));const S={...t,kind:u},I=s===void 0?void 0:tt(s,f);if(await B(he(_),{cwd:f,profileId:p},n.modelRegistry),!await Xn(p))throw new m(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 L=null;{const $=o===void 0||o.nodeId===null||o.state==="warm_claimed"?rt(S,f,p):null,j=c===null&&r.beforeBrokerLaunch===void 0?void 0:v=>{c!==null&&$n(v,"terminal",c),r.beforeBrokerLaunch?.(v)};if(o?.state==="warm_claimed"&&o.nodeId!==null){const v=$===null?null:await Pn($,o.nodeId,j);if(v===null)throw ne(o.requestKey,o.nodeId),new m(503,"create_resume_failed",`could not resume warm claim ${o.nodeId}; retry the create`);return te(o.requestKey,v),{status:201,body:g(v)}}if($!==null){const v=await Tn($,{...d===void 0?{}:{deadlineAt:d},...I===void 0?{}:{outcomeDelivery:I},...o===void 0?{}:{requestKey:o.requestKey}},j);if(v!==null)return o!==void 0&&te(o.requestKey,v),le($),{status:201,body:g(v)};L=$}const M=o?.nodeId??t.node_id??(o===void 0?void 0:ee());o!==void 0&&o.nodeId===null&&Me(o.requestKey,M);const O=ot(M===void 0?S:{...S,node_id:M},i,f);l!==null&&(l.rootLifecycle!==void 0&&(O.rootLifecycle=l.rootLifecycle),l.scopes!==void 0&&S.scopes===void 0&&(O.scopes=l.scopes),l.run!==void 0&&(O.run=l.run),l.withinCreate!==void 0&&(O.withinCreate=l.withinCreate)),d!==void 0&&(O.deadlineAt=d),I!==void 0&&(O.outcomeDelivery=I),O.readiness=t.root===!0?"await":"launch";const Q=await Pe(O,j,()=>{L!==null&&le(L)});return o!==void 0&&je(o.requestKey,Q.node.node_id),{status:201,body:g(Q.node.node_id)}}}a(E,"handleCreateRequest");function b(e,t){const n=e.query.getAll(t);if(n.length>1)throw h(`${t} must appear at most once`);return n[0]}a(b,"singleQueryValue");function _e(e,t){if(e!==void 0){if(e==="1"||e==="true")return!0;if(e==="0"||e==="false")return!1;throw h(`${t} must be true or false`)}}a(_e,"optionalBoolean");function D(e,t,n){if(e!==void 0){if(n.includes(e))return e;throw h(`${t} must be one of: ${n.join(", ")}`)}}a(D,"oneOf");function at(e){const t=new Set(["status","lifecycle","kind","mode","profile_id","profile_prefix","parent","top_level","under","hanging","include"]);for(const[_]of e.query)if(!t.has(_))throw h(`unknown nodes query parameter: ${_}`);const n=D(b(e,"status"),"status",["active","idle","done","dead","canceled"]),r=D(b(e,"lifecycle"),"lifecycle",["terminal","resident"]),o=b(e,"kind"),d=D(b(e,"mode"),"mode",["base","orchestrator"]),s=b(e,"profile_id"),l=b(e,"profile_prefix"),i=b(e,"parent"),c=_e(b(e,"top_level"),"top_level"),p=b(e,"under"),u=_e(b(e,"hanging"),"hanging"),f=D(b(e,"include"),"include",["activity"]);if(i!==void 0&&c===!0)throw h("parent and top_level=true cannot be combined");return{...n!==void 0?{status:n}:{},...r!==void 0?{lifecycle:r}:{},...o!==void 0?{kind:o}:{},...d!==void 0?{mode:d}:{},...s!==void 0?{profile_id:s}:{},...l!==void 0?{profile_prefix:l}:{},...i!==void 0?{parent:i}:{},...c!==void 0?{top_level:c}:{},...p!==void 0?{under:p}:{},...u!==void 0?{hanging:u}:{},...f!==void 0?{include:f}:{}}}a(at,"parseListQuery");function lt(e){const t=at(e);let n=t.status!==void 0?re({status:t.status}):re();if(e.principal.class!=="person"&&(n=n.filter(s=>pe(e.principal,s.grantee??"app:terminal",s.node_id))),t.lifecycle!==void 0&&(n=n.filter(s=>s.lifecycle===t.lifecycle)),t.kind!==void 0&&(n=n.filter(s=>s.kind===t.kind)),t.mode!==void 0&&(n=n.filter(s=>s.mode===t.mode)),t.profile_id!==void 0&&(n=n.filter(s=>s.profile_id===t.profile_id)),t.profile_prefix!==void 0&&(n=n.filter(s=>s.profile_id?.startsWith(t.profile_prefix))),t.parent!==void 0&&(n=n.filter(s=>s.parent===t.parent)),t.top_level===!0&&(n=n.filter(s=>s.parent===null)),t.under!==void 0){const s=new Set(xe(t.under));n=n.filter(l=>s.has(l.node_id))}t.hanging===!0&&(n=n.filter(s=>s.parent!==null&&k(s.parent)===null));const r=t.include==="activity"?He(n.map(s=>s.node_id)):null,o=n.length>0?Re():new Set;return{status:200,body:n.map(s=>{const l=Vn(s,s.profile_id!==null&&o.has(s.profile_id));if(r!==null){const i=ce(s.node_id);l.activity={latest_report:i[0]??null,final_report:s.final_report===null?null:i.find(c=>c.name===s.final_report)??null,pending_human_count:r[s.node_id]??0}}return l})}}a(lt,"handleList");function ct(e){const t=e.params.id;return y(t),{status:200,body:g(t)}}a(ct,"handleDetail");async function ft(e){const t=e.params.id;y(t);const n=await rn(t);return{status:200,body:{node_id:t,snapshot:{messages:n.snapshot.messages,...n.snapshot.messageIds===void 0?{}:{messageIds:n.snapshot.messageIds},messageVisibility:n.snapshot.messageVisibility,turnVisibility:n.snapshot.turnVisibility,stats:n.snapshot.stats,state:n.snapshot.state,display:n.snapshot.display,...n.snapshot.queued===void 0?{}:{queued:n.snapshot.queued},...n.snapshot.toolGroupSummaries===void 0?{}:{toolGroupSummaries:n.snapshot.toolGroupSummaries},...n.snapshot.workingActivity===void 0?{}:{workingActivity:n.snapshot.workingActivity}},commands:n.commands.map(o=>({name:o.name,description:o.description,source:o.source})),captured_at:G()}}}a(ft,"handleSnapshot");async function ut(e){const t=e.params.id,n=sn(t);if(n===null)throw P(`unknown node: ${t}`,{received:t});return{status:200,body:n}}a(ut,"handleSubject");function pt(e){for(const[d]of e.query)if(d!=="cursor"&&d!=="limit")throw h(`unknown messages query parameter: ${d}`);const t=e.query.getAll("cursor"),n=e.query.getAll("limit");if(t.length>1)throw h("cursor must appear at most once");if(n.length>1)throw h("limit must appear at most once");if(n.length===0)return t.length===0?{}:{cursor:t[0]};const r=n[0],o=Number(r);if(!Number.isSafeInteger(o)||r==="")throw h("limit must be an integer from 1 through 500");return t.length===0?{limit:o}:{cursor:t[0],limit:o}}a(pt,"parseMessagesQuery");async function mt(e){const t=e.params.id,n=await tn(t,pt(e));return{status:200,body:{node_id:t,messages:n.messages,...n.messageIds===void 0?{}:{message_ids:n.messageIds},message_visibility:n.messageVisibility,next_cursor:n.nextCursor,captured_at:G()}}}a(mt,"handleMessages");function ht(e){const t=e.params.id;y(t);const n=on(t);return{status:200,body:{node_id:t,session_file:n.sessionFile,session_jsonl:n.sessionJsonl,system_prompt:n.systemPrompt,captured_at:G()}}}a(ht,"handleSession");async function wt(e){const t=e.params.id;return y(t),{status:200,body:{node_id:t,markdown:await dn(t)}}}a(wt,"handleTranscript");function yt(e){const t=e.params.id;y(t);const n=a((o,d)=>({node_id:o,label:k(o)?.name??o,dir:de(o),self:d,files:Zn(o)}),"root"),r=[n(t,!0),...ie(t).map(o=>n(o.node_id,!1))].filter(o=>_t(o.node_id,o.dir)||o.files>0);return{status:200,body:{node_id:t,roots:r}}}a(yt,"handleContext");function _t(e,t){try{return Ze(K(e),t)}catch(n){if(en(n))return!1;throw n}}a(_t,"hasDir");async function gt(e){const t=y(e.params.id),n=e.principal.class==="broker"?e.principal.scopes:e.ceiling,r=n===null?void 0:t.scopes==null?[...n]:[...new Set([...t.scopes.filter(d=>R(n,d)),...n.filter(d=>R(t.scopes,d))])],o=await Ae(t.node_id,r,e.principal.class==="broker"?e.principal.node:void 0);return{status:201,body:g(o.node.node_id)}}a(gt,"handleFork");async function bt(e){const t=e.params.id;y(t);const n=e.body,r=n?.resume??!0,o=n?.reopen??!1;pn(t,o);const d=!r&&n?.cron_id!==void 0?fe(n.cron_id):null,s=await Ne(t,{resume:r,capacity:"refuse",...d!==null?{wakeReason:Bn(d,"schedule_revive")}:{}});s.outcome!=="already-live"&&Fe(t,{failures:0,notBefore:null});const l=k(t)?.status??"active";return{status:200,body:zn(t,s,l)}}a(bt,"handleRevive");async function vt(e){const t=e.params.id;return{status:200,body:{newNodeId:(await un(t))?.newNodeId??null}}}a(vt,"handleRelaunchRoot");function kt(e){const t=e.params.id;y(t);const n=e.body;if(n?.caller_pid!==void 0&&(!Number.isInteger(n.caller_pid)||n.caller_pid<=0))throw h("caller_pid must be a positive integer when provided");const r=xn(),o=Ce(t,{rootEvent:n?.finish===!0?"finish":"cancel",callerPid:n?.caller_pid,...e.principal.class==="broker"?{closer:e.principal.node}:{},...r===null?{}:{registerDetached:r.registerDetached}});return{status:200,body:Wn(o)}}a(kt,"handleClose");async function St(e){const t=e.params.id;y(t);const n=await fn(t);return{status:200,body:{recycled:n.recycled,finalized:n.finalized,new_root:n.newRoot,delivered:n.delivered}}}a(St,"handleRecycle");async function Ot(e){const t=e.params.id;return y(t),await W(t,{lifecycle:"terminal"}),C(e,t,["lifecycle"]),{status:200,body:g(t)}}a(Ot,"handleDemote");async function Rt(e,t={}){const n=e.params.id,r=y(n),o=e.body??{};o.kind!==void 0&&await z(o.kind,r.cwd,r.profile_id??null),o.model!==void 0&&await we(r,{kind:o.kind??r.kind,mode:"orchestrator",lifecycle:o.resident===!0?"resident":r.lifecycle,model:o.model},t.modelRegistry);const d=await ln(n,{...o.kind!==void 0?{kind:o.kind}:{},...o.resident===!0?{resident:!0}:{},...o.model!==void 0?{model:o.model}:{}});C(e,n,["mode",...o.kind!==void 0?["kind"]:[],...o.resident===!0?["lifecycle"]:[],...o.model!==void 0?["model"]:[]]);const s=J(d.meta,H(n));return s.roadmap_written=d.roadmapWritten,s.roadmap_ref=d.roadmapRef,s.goal_path=d.goalPath,{status:200,body:s}}a(Rt,"handlePromote");async function It(e,t={}){const n=e.params.id,r=y(n),o=e.body??{};o.kind!==void 0&&await z(o.kind,r.cwd,r.profile_id??null),o.model!==void 0&&await we(r,{kind:o.kind??r.kind,mode:o.promote===!0?"orchestrator":r.mode,lifecycle:r.lifecycle,model:o.model},t.modelRegistry);const d=o.note??"Refresh: continue against your roadmap or goal.";mn(n,d);const s=await cn(n,{...o.promote===!0?{promote:!0}:{},...o.kind!==void 0?{kind:o.kind}:{},...o.model!==void 0?{model:o.model}:{}});return{status:200,body:J(s.meta,H(n))}}a(It,"handleYield");function $t(e){const t=e.params.id;y(t);const n=e.body;if(n===void 0||typeof n.controller!="string"||n.controller.trim()==="")throw h("a wait requires a controller node id");const r=k(n.controller);if(r===null)throw P(`unknown controller: ${n.controller}`,{received:n.controller});if(r.status!=="active"&&r.status!=="idle")throw new m(422,"unprocessable",`controller ${n.controller} is ${r.status}`);return Be(t,n.controller),{status:200,body:g(t)}}a($t,"handleWait");async function Ct(e){const t=e.principal,n=await an(t.class==="broker"?o=>o.grantee===t.app&&U(t,o.node_id,o.grantee):void 0);return{status:200,body:{revived:[...n.revived],failed:[...n.failed],frozen:[...n.frozen]}}}a(Ct,"handleReviveAll");const Nt=new Set(["interrupt","steer","after-turn","next-turn"]);function At(e){if(e===null)return null;const t=a(r=>{throw new w({error:"invalid_listen",message:r,received:e,field:"listen",next:'Send an object mapping levels "1".."10" to interrupt|steer|after-turn|next-turn (e.g. {"3":"next-turn"}), or null to reset to the global default.'})},"bad");if(typeof e!="object"||Array.isArray(e))return t("listen must be an object or null");const n=Object.entries(e);if(n.length===0)return t("listen must name at least one level");for(const[r,o]of n)/^(?:[1-9]|10)$/.test(r)||t(`listen level must be 1..10: ${r}`),(typeof o!="string"||!Nt.has(o))&&t(`listen delivery must be interrupt|steer|after-turn|next-turn: ${String(o)}`);return JSON.stringify(Object.fromEntries(n))}a(At,"listenJson");function C(e,t,n,r){n.length===0&&r===void 0||x(o=>{if(r?.(o),n.length===0)return;const d=Ke(o,t),s=ze(o,e),l=s.class==="person"?s.person:s.class==="app"?s.app:s.node;We(o,l,d),Ve(o,{event:"configured",object:d,actor:l,attrs:{changed:n.join(",")}})})}a(C,"recordConfigured");function ge(e){const t=V().get(e);if(t===void 0)return!1;try{return process.kill(t.pid,"SIGTERM"),!0}catch{return!1}}a(ge,"restartLiveBroker");function be(e,t){try{return Z(e)===Z(t)}catch{return!1}}a(be,"sameDirectory");async function Tt(e,t={}){const n=e.params.id,r=e.body;let o;if(r===void 0)o={};else if(typeof r=="object"&&r!==null&&!Array.isArray(r))o=r;else throw new w({error:"cwd_invalid",message:"cwd repair requires a non-null object with a non-empty cwd string",received:r,field:"cwd",next:"Send a config patch with a non-empty absolute cwd string."});if(o.profile!==void 0){if(typeof o.profile!="string"||o.profile==="")throw new w({error:"profile_invalid",message:"profile must be a non-empty profile id or name",received:o.profile,field:"profile",next:"Send a profile id or exact name."});if(Object.keys(o).some(u=>u!=="profile"&&u!=="restart"&&o[u]!==void 0))throw new w({error:"profile_not_exclusive",message:"profile can only be combined with restart",field:"profile",next:"Send profile (and optionally restart) alone."});const i=y(n),c=Mn(e,o.profile);Fn(c.profileId);const p=Gn(c.profileId).manifest.grantee??"app:terminal";if(p!==(i.grantee??"app:terminal"))throw new w({error:"profile_grantee_mismatch",message:`profile ${c.profileId} belongs to ${p}, node ${n} to ${i.grantee}`,received:o.profile,field:"profile",next:"Pick a profile owned by the same app as the node."});return(i.profile_id??null)!==c.profileId&&(Dn(i.kind)?(await z(i.kind,i.cwd,c.profileId),await W(n,{profileId:c.profileId})):N(n,{profile_id:c.profileId}),C(e,n,["profile"])),o.restart===!0&&ge(n),{status:200,body:g(n)}}if(Object.prototype.hasOwnProperty.call(o,"cwd")){const i=o.cwd;if(typeof i!="string"||i==="")throw new w({error:"cwd_invalid",message:"cwd must be a non-empty string",received:i,field:"cwd",next:"Send a non-empty absolute cwd string."});if(Object.keys(o).some(f=>f!=="cwd"&&f!=="keep_session"&&f!=="restart"&&o[f]!==void 0))throw new w({error:"cwd_not_exclusive",message:"cwd repair cannot be combined with another config field",received:o,field:"cwd",next:"Send cwd as the only config field."});const c=y(n),p=A(c.cwd);if(o.keep_session===!0||p&&F(i)&&A(i)&&be(c.cwd,i)){if(!F(i))throw new w({error:"cwd_not_absolute",message:`cwd must be an absolute path: ${i}`,received:i,field:"cwd",next:"Send an absolute path."});const f=T(i);let _=!1;try{_=q(f).isDirectory()}catch{}if(!_)throw new w({error:"cwd_missing",message:`cwd is not an existing directory: ${f}`,received:i,field:"cwd",next:"Send an existing absolute directory."});if(p&&!be(c.cwd,f))throw new w({error:"cwd_not_same_directory",message:`${f} is not the same directory as the recorded ${c.cwd}; --keep-session only relocates an alias`,received:i,field:"cwd",next:"Drop --keep-session to repair a gone cwd, or pass the real path of the recorded directory."});if(c.managed_worktree?.state==="open")throw new w({error:"cwd_node_managed_worktree_open",message:`${n} owns an open managed worktree and cannot relocate its cwd`,received:i,field:"cwd",next:"Reconcile the managed worktree first."});const S=c.cwd;return S!==f&&(x(I=>{N(n,{cwd:f}),X(I,"UPDATE crons SET cwd = ? WHERE anchor_node = ? AND cwd = ?").run(f,n,S),Je(n,S,f)}),C(e,n,["cwd"])),o.restart===!0&&ge(n),{status:200,body:g(n)}}if(V().has(n)){const f=`${n} cannot repair its cwd while its broker is live`;throw new m(409,"cwd_node_live",f,{error:"cwd_node_live",message:f,received:i,field:"cwd",next:"Let the node become dormant, then retry the cwd repair."})}if(c.fork_from!=null)throw new w({error:"cwd_node_forked",message:`${n} is a fork and cannot repair its cwd; recorded cwd: ${c.cwd}`,received:i,field:"cwd",next:`Restore ${c.cwd}, then revive the fork normally.`});if(c.managed_worktree?.state==="open")throw new w({error:"cwd_node_managed_worktree_open",message:`${n} owns an open managed worktree and cannot repair its cwd`,received:i,field:"cwd",next:"Restore or reconcile the recorded checkout, then use the managed-worktree close or abandon flow."});if(A(c.cwd))throw new w({error:"cwd_still_present",message:`${n} recorded cwd still exists: ${c.cwd}; cwd repair is not a relocation command`,received:i,field:"cwd",next:"This command only repairs a node whose recorded cwd is gone."});if(!F(i))throw new w({error:"cwd_not_absolute",message:`cwd must be an absolute path: ${i}`,received:i,field:"cwd",next:"Send an absolute path."});const u=T(i);if(!A(u))throw new w({error:"cwd_missing",message:`cwd does not exist: ${u}`,received:i,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."});try{if(!q(u).isDirectory())throw new w({error:"cwd_not_a_directory",message:`cwd is not a directory: ${u}`,received:i,field:"cwd",next:"Send an existing absolute directory."})}catch(f){throw f instanceof w?f:new w({error:"cwd_missing",message:`cwd does not exist: ${u}`,received:i,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."})}return x(()=>{N(n,{cwd:u}),Ue(n)}),C(e,n,["cwd"]),{status:200,body:g(n)}}let d=y(n);const s=o.listen===void 0?void 0:At(o.listen);if(o.scopes!==void 0){const i=ue(o.scopes),c=d.grantee==="app:terminal"?Yn():d.grant_id?Hn(d.grant_id):null,p=d.grantee==="app:terminal"&&!Qn("user").runtime?.issuer;if(!i.every(u=>R(d.scopes,u)&&(c?R(c.resolved,u):p)))throw new w({error:"unknown_scope",message:"scopes may only narrow the node's current effective set",received:i,field:"scopes",next:"Send only scopes the node currently holds."});if(i.includes("crtr:act")&&!R(i,"crtr:llm"))throw new m(400,"invalid_scope","crtr:act requires crtr:llm");d=N(n,{scopes:i})}if(o.situational_context!==void 0&&Ie(n,$e(o.situational_context)),o.model!==void 0){const i=(await se(d.cwd,d.profile_id??null)).modelLadders,c=bn(o.model,i),p=vn(o.model,i);await B({kind:"concrete",spec:p},{cwd:d.cwd,profileId:d.profile_id??null},t.modelRegistry);const u=V();let f=!1;if(u.isClaimed(n)&&await Rn(n,u.get(n)?.handle.exited)&&(f=await On(n,p,c).then(()=>!0,_=>{if(_ instanceof In)return!1;throw _})),!f){if(u.isClaimed(n)){const _=`${n}'s broker did not accept the model switch; the model was not changed`;throw new m(409,"broker_not_ready",_,{error:"broker_not_ready",message:_,received:o.model,field:"model",next:"Check the node with `crtr node show`, then retry the model change."})}if(!p.includes("/"))throw h(`model must resolve to a provider/id spec: ${o.model}`);Sn(n,p,c)}d=y(n)}if(o.kind!==void 0||o.mode!==void 0||o.lifecycle!==void 0){o.kind!==void 0&&await ke(o.kind);const i=o.kind??d.kind,c=o.mode??d.mode,p=o.lifecycle??d.lifecycle;let u;c==="orchestrator"&&!yn(n)&&(u=a(()=>{const f=hn(n);wn(n,f!==null&&f.trim()!==""?{goal:f.trim()}:{})},"beforePersist")),d=await W(n,{...o.kind!==void 0?{kind:i}:{},...o.mode!==void 0?{mode:c}:{},...o.lifecycle!==void 0?{lifecycle:p}:{}},u)}o.name!==void 0&&(d=N(n,{name:o.name}),d.window&&_n(d.window,jn(d)));const l=["scopes","situational_context","model","kind","mode","lifecycle","name","listen"].filter(i=>o[i]!==void 0);return C(e,n,l,s===void 0?void 0:i=>{X(i,"UPDATE objects SET listen_json = ? WHERE id = ? AND type = 'node'").run(s,n)}),{status:200,body:g(n)}}a(Tt,"handleConfig");async function ve(){return(await se(process.cwd(),Oe()||null)).kinds}a(ve,"daemonKinds");async function ke(e){const t=Object.keys(await ve());if(!t.includes(e))throw h(`unknown kind: ${e}`,{received:e,next:`Valid kinds: ${t.sort().join(", ")}.`})}a(ke,"assertKindServer");async function Pt(){const e=await ve();return{status:200,body:{kinds:Object.keys(e).sort().map(n=>({kind:n,when_to_use:e[n]?.whenToUse??null,available_to:e[n]?.availableTo??null}))}}}a(Pt,"handleListKinds");const Uo=[{method:"POST",pattern:"/v1/nodes",scope:"crtr:act",handler:dt},{method:"GET",pattern:"/v1/nodes",handler:lt},{method:"GET",pattern:"/v1/kinds",handler:a(()=>Pt(),"handler")},{method:"POST",pattern:"/v1/nodes/revive-all",scope:"crtr:act",handler:Ct},{method:"GET",pattern:"/v1/nodes/:id",handler:ct},{method:"GET",pattern:"/v1/nodes/:id/snapshot",handler:ft},{method:"GET",pattern:"/v1/nodes/:id/subject",handler:ut},{method:"GET",pattern:"/v1/nodes/:id/messages",handler:mt},{method:"GET",pattern:"/v1/nodes/:id/session",handler:ht},{method:"GET",pattern:"/v1/nodes/:id/transcript",handler:wt},{method:"GET",pattern:"/v1/nodes/:id/context",handler:yt},{method:"POST",pattern:"/v1/nodes/:id/fork",scope:"crtr:act",handler:gt},{method:"POST",pattern:"/v1/nodes/:id/revive",scope:"crtr:act",handler:bt},{method:"POST",pattern:"/v1/nodes/:id/relaunch-root",scope:"crtr:act",handler:vt},{method:"POST",pattern:"/v1/nodes/:id/close",handler:kt},{method:"POST",pattern:"/v1/nodes/:id/recycle",scope:"crtr:act",handler:St},{method:"POST",pattern:"/v1/nodes/:id/demote",scope:"crtr:act",handler:Ot},{method:"POST",pattern:"/v1/nodes/:id/promote",scope:"crtr:act",handler:Rt},{method:"POST",pattern:"/v1/nodes/:id/yield",scope:"crtr:act",handler:It},{method:"POST",pattern:"/v1/nodes/:id/wait",handler:$t},{method:"PATCH",pattern:"/v1/nodes/:id/config",handler:Tt}];export{Tt as handleConfig,dt as handleCreate,gt as handleFork,lt as handleList,Rt as handlePromote,It as handleYield,Uo as nodeRoutes,Qo as resolveCreateKind};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var E=Object.defineProperty;var s=(e,r)=>E(e,"name",{value:r,configurable:!0});import{randomUUID as P}from"node:crypto";import{readConfig as I}from"../../../core/config.js";import{activeGrantForApp as R,allGrants as _}from"../../../core/grants/mirror.js";import{getNode as O}from"../../../core/canvas/canvas.js";import{scopeAllowed as $}from"../../../core/scopes.js";import{ProviderError as u,providerErrorBody as q,scopeMissing as x}from"../../../core/providers/errors.js";import{callProvider as G,providerTools as N}from"../../../core/providers/call.js";import{providerIdsFromGrants as T,providerInstallState as F}from"../../../core/providers/bundles.js";import{listInstalledPlugins as j}from"../../../core/resolver.js";import{listAudit as C,writeProviderAudit as D}from"../../../core/providers/audit.js";import{emitEvent as g}from"../../../core/events/emit.js";import{resolvedScopes as h}from"../router.js";const fe=new Set(["provider.tools","provider.call","audit.list"]);function M(e){if(e.class==="person")return{class:"person",sub:null,grantee:null,client_id:null,resolved:null,nodeScopes:null,run_id:null,node_id:null};if(e.class==="app"){const r=R(e.app);if(!r)throw new u("grant_revoked",`app ${e.app} has no active grant`);return{class:"app",sub:r.sub,grantee:e.app,client_id:null,resolved:r.resolved,nodeScopes:e.scopes,run_id:null,node_id:null}}return e.class==="cron"?{class:"app",sub:e.sub,grantee:e.app,client_id:null,resolved:h(e),nodeScopes:e.scopes,run_id:e.origin_run,node_id:null}:{class:"broker",sub:e.sub,grantee:e.app,client_id:e.client_id,resolved:h(e),nodeScopes:e.scopes,run_id:e.run??O(e.node)?.run_id??null,node_id:e.node}}s(M,"callerFromRequest");function ce(e){return{class:"app",sub:e.sub,grantee:e.grantee,client_id:e.client_id,resolved:e.resolved,nodeScopes:null,run_id:null,node_id:null}}s(ce,"callerFromApp");const U=/^([A-Za-z0-9_-]{1,64}):([A-Za-z0-9_-]{1,64})$/;function k(e){const r=U.exec(e);return r!==null&&r[1]!=="crtr"}s(k,"isProviderScope");const w=new WeakMap;function z(e){let r=w.get(e);return r===void 0&&(r=new Set(e),w.set(e,r)),r}s(z,"heldSet");function y(e,r){return e===null?!0:k(r)?z(e).has(r):$(e,r)}s(y,"holds");function S(e,r){return e.class==="person"?!0:y(e.resolved??[],r)&&y(e.nodeScopes,r)}s(S,"callerAllows");function pe(e,r,t){const n=`${r}:${t}`;if(!S(e,n))throw x(n)}s(pe,"requireGroup");function L(){return!!I("user").runtime?.issuer}s(L,"providerOperationsEnabled");const V=/^[A-Za-z0-9_-]{1,64}$/;function p(e,r){return new u("invalid_request",e,{param:r})}s(p,"invalid");function A(e){if(typeof e!="string"||!V.test(e)||e==="crtr")throw p("provider must be a provider id","provider");return e}s(A,"parseProvider");function Z(e){if(!e||typeof e!="object"||Array.isArray(e))throw new u("invalid_request","a JSON object is required");const r=e,t=A(r.provider),n=r.tool;if(typeof n!="string"||!n.trim())throw p("tool must be a non-empty string","tool");const i=r.arguments??{};if(!i||typeof i!="object"||Array.isArray(i))throw p("arguments must be an object","arguments");const a=r.version;if(a!==void 0&&(typeof a!="number"||!Number.isInteger(a)||a<1))throw p("version must be an integer >= 1","version");const o=r.approval;if(o!==void 0&&(typeof o!="string"||!o))throw p("approval must be a question id","approval");return{provider:t,tool:n.trim(),arguments:i,...a===void 0?{}:{version:a},...o===void 0?{}:{approval:o}}}s(Z,"parseCallInput");function B(e,r=new URLSearchParams){const t=s((o,f,c)=>{const l=r.get(o);if(l===null)return f;if(!/^\d+$/.test(l)||Number(l)>c||Number(l)<1)throw p(`invalid ${o}`,o);return Number(l)},"numeric"),n=r.has("after")?t("after",0,Number.MAX_SAFE_INTEGER):void 0,i=t("limit",100,1e3),a=Object.fromEntries(["operation","outcome","run_id","since","until"].filter(o=>r.has(o)).map(o=>[o,r.get(o)]));return{status:200,body:C(e.class==="person"?null:e.grantee,{after:n,limit:i,...a})}}s(B,"auditList");async function H(e,r,t){const n=performance.now(),i=new Set(j("user").filter(d=>d.enabled&&d.manifest.provider?.id===d.name).map(d=>d.name)),a=T(_()),o=[...new Set([...i,...a])].sort();if(r!==void 0&&!o.includes(r))throw new u("not_found",`provider "${r}" is not available`);const f=r===void 0?o:[r],c=e.class==="person"?_().flatMap(d=>d.status==="active"?d.resolved_scopes.map(({scope:v})=>v):[]):e.resolved??[],l=await Promise.all(f.map(async d=>{if(!c.some(m=>m.startsWith(`${d}:`)&&S(e,m)))return null;if(i.has(d)){const m=await N(e,d);return m.groups.length>0?m:null}const b=F(d);return{provider:d,display_name:d,status:b.status,...b.error?{error:b.error}:{},groups:[],commands:[]}})).then(d=>d.filter(v=>v!==null));if(g({level:"debug",event:"api.provider.tools",duration_ms:performance.now()-n,fields:{caller:e.class,provider:r??null,providers:l.length}}),r!==void 0){if(l.length===0)throw new u("not_found",`provider "${r}" is not available`);return{status:200,body:l[0]}}return{status:200,body:{providers:l}}}s(H,"providerTools");async function W(e,r,t){return G(e,r,t)}s(W,"providerCall");async function J(e,r,t,n){if(!L())throw new u("not_found","provider operations need a directory-connected runtime");if(e==="audit.list")return B(r,t.query);if(e==="provider.tools")return H(r,t.params.provider===void 0?void 0:A(t.params.provider),n);if(e==="provider.call"){let i;try{i=Z(t.body)}catch(a){const o=t.body&&typeof t.body=="object"&&!Array.isArray(t.body)?t.body:{};throw D({request_id:n.requestId,caller:r,provider:typeof o.provider=="string"?o.provider:"",tool:typeof o.tool=="string"?o.tool:"",outcome:`refused:${a instanceof u?a.code:"invalid_request"}`}),a}return W(r,i,n)}throw new u("not_found","unknown operation")}s(J,"handleProviderOperation");function X(e){const r=new AbortController,t=s(()=>{e.writableFinished||r.abort()},"abort");return e.on("close",t),{signal:r.signal,release:s(()=>e.off("close",t),"release")}}s(X,"abortOnClose");function K(e,r,t,n){const i=q(e,r,t,n??void 0);return i.status>=500&&!(e instanceof u)&&g({event:"api.provider.failed",level:"error",error:e instanceof Error?e:new Error(String(e)),fields:{request_id:r}}),i}s(K,"providerFailure");const me=[{method:"GET",pattern:"/v1/audit",name:"audit.list"},{method:"GET",pattern:"/v1/providers/tools",name:"provider.tools"},{method:"GET",pattern:"/v1/providers/:provider/tools",name:"provider.tools"},{method:"POST",pattern:"/v1/provider/call",name:"provider.call"}].map(({method:e,pattern:r,name:t})=>({method:e,pattern:r,handler:s(async n=>{const i=P(),{signal:a,release:o}=X(n.res);let f=null;try{return f=M(n.principal),{...await J(t,f,{params:n.params,body:n.body,query:new URL(n.req.url??"/","http://runtime.local").searchParams},{requestId:i,signal:a}),headers:{"Request-Id":i}}}catch(c){return K(c,i,!1,f?.run_id)}finally{o()}},"handler")}));export{fe as PROVIDER_OPERATIONS,X as abortOnClose,S as callerAllows,ce as callerFromApp,M as callerFromRequest,J as handleProviderOperation,Z as parseCallInput,A as parseProvider,K as providerFailure,L as providerOperationsEnabled,me as providerRoutes,pe as requireGroup};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var q=Object.defineProperty;var
|
|
1
|
+
var q=Object.defineProperty;var c=(a,t)=>q(a,"name",{value:t,configurable:!0});import{callerGrant as p}from"../principal.js";import{grantVersion as g}from"../../../core/grants/mirror.js";import{waitGrantChange as R}from"../../../core/grants/watch.js";import{RunError as n,idempotencyKeyOf as h,cancelRun as S,deleteRun as N,interruptRun as O,listRuns as $,messageRun as A,renameRun as j,requestedScopes as x,startRun as B,traceRun as J,waitRun as z}from"../../../core/runs/operations.js";import{canReadRun as b,requireRun as f,runCallerApp as C}from"../router.js";import{waitSeconds as v}from"../long-poll-wait.js";function l(a){if(!a||typeof a!="object"||Array.isArray(a))throw new n(400,"invalid_request","a JSON object is required");return a}c(l,"object");function y(a,t,i,u,d){if(a===null)return d;if(!/^(0|[1-9]\d*)$/.test(a)||!Number.isSafeInteger(Number(a))||Number(a)<i||Number(a)>u)throw new n(400,"invalid_request",`invalid ${t}`,t);return Number(a)}c(y,"integer");const E=256*1024;async function I(a,t,i,u,d,_){switch(a.name){case"grant.get":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=v("grant.get",u),s=u.get("since");if(s!==null&&!/^[A-Za-z0-9_-]{1,64}$/.test(s))throw new n(400,"invalid_request","invalid since","since");let r=p(t);return e&&s!==null&&g(r)===s&&(await R(t.grant,e),r=p(t)),{status:200,body:{sub:t.sub,grantee:t.grantee,client_id:t.client_id,grant:t.grant,rev:r.rev,resolved_scopes:r.resolved_scopes,spend_cap:r.spend_cap,exp:t.exp,payer:"user",dependencies:r.dependencies??{},version:g(r)}}}case"run.start":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=l(d),s=["mode","parent","fork_from","node_id","env","tools","system_prompt"];for(const o of s)if(e[o]!==void 0)throw new n(403,"owner_only",`${o} is owner-only`,o);const r=["payer","model","model_policy","deadline","kind"];for(const o of r)if(e[o]!==void 0)throw new n(400,"invalid_request",`${o} is not available`,o);for(const o of Object.keys(e))if(!["prompt","scopes","metadata","profile","isolated","cwd","worktree","output_schema","message"].includes(o))throw new n(400,"invalid_request",`unknown field ${o}`,o);if(e.profile!==void 0&&(typeof e.profile!="string"||!e.profile))throw new n(400,"invalid_request","profile must be a profile id","profile");if(e.isolated!==void 0&&typeof e.isolated!="boolean")throw new n(400,"invalid_request","isolated must be a boolean","isolated");if(e.cwd!==void 0&&(e.isolated!==!0||typeof e.cwd!="string"||!e.cwd))throw new n(400,"invalid_request","cwd is available only for an isolated run and must be an absolute path inside its root folder","cwd");if(e.worktree!==void 0&&e.isolated!==!0)throw new n(400,"invalid_request","worktree is not available","worktree");if(e.isolated===!0&&e.worktree!==void 0)throw new n(400,"invalid_request","worktree is unavailable for an isolated run","worktree");if(typeof e.prompt!="string"||!e.prompt.trim())throw new n(400,"invalid_request","prompt is required","prompt");if(e.message!==void 0&&(typeof e.message!="string"||!e.message.trim()))throw new n(400,"invalid_request","message must be a non-empty string","message");const w=e.metadata===void 0?{}:l(e.metadata);if(Buffer.byteLength(JSON.stringify(w))>4096)throw new n(400,"invalid_request","metadata exceeds 4 KB","metadata");let m;if(e.output_schema!==void 0){const o=e.output_schema;if(!o||typeof o!="object"||Array.isArray(o))throw new n(400,"invalid_request","output_schema must be a JSON Schema object","output_schema");if(Buffer.byteLength(JSON.stringify(o))>E)throw new n(400,"invalid_request","output_schema exceeds 256 KB","output_schema");m=o}const k=x(t.resolved,e.scopes,t.grantee);return{status:201,body:await B(t,{prompt:e.prompt,scopes:k,metadata:w,profile:e.profile,isolated:e.isolated,cwd:e.cwd,...m===void 0?{}:{output_schema:m},...e.message===void 0?{}:{message:e.message}},h(_))}}case"run.list":{const e=u.get("status")??void 0;if(e&&!["running","waiting_on_user","idle","settled"].includes(e))throw new n(400,"invalid_request","invalid status","status");const s=u.get("app");if(s!==null&&!/^(?:app:)?[a-z0-9][a-z0-9-]*$/.test(s))throw new n(400,"invalid_request","invalid app","app");const r=s===null?C(t):s.startsWith("app:")?s:`app:${s}`;if(r!==null&&!b(t,r))throw new n(404,"not_found","run not found");return{status:200,body:$(r,{status:e,after:u.get("after")??void 0,limit:y(u.get("limit"),"limit",1,100,50),..."class"in t&&(t.class==="broker"||t.class==="cron")&&r!==null?{visible:c(w=>b(t,r,w),"visible")}:{}})}}case"run.get":{const e=v("run.get",u),s=f(t,i),r=await z(i,s.grantee,e);return f(t,i),{status:200,body:r}}case"run.message":{const e=l(d);if(typeof e.message!="string"||!e.message.trim())throw new n(400,"invalid_request","message is required","message");if(e.start_turn!==void 0&&typeof e.start_turn!="boolean")throw new n(400,"invalid_request","start_turn must be a boolean","start_turn");return{status:202,body:await A(f(t,i,!0),e.message,void 0,e.start_turn,h(_))}}case"run.interrupt":{const e=l(d===void 0?{}:d);if(e.children!==void 0&&e.children!=="stop"&&e.children!=="continue")throw new n(400,"invalid_request","invalid children","children");return{status:200,body:await O(f(t,i,!0),e.children)}}case"run.cancel":{const e=l(d===void 0?{}:d);if(e.keep_schedules!==void 0&&typeof e.keep_schedules!="boolean")throw new n(400,"invalid_request","invalid keep_schedules","keep_schedules");return{status:200,body:await S(f(t,i,!0),{keepSchedules:e.keep_schedules})}}case"run.rename":{const e=l(d);for(const s of Object.keys(e))if(s!=="name")throw new n(400,"invalid_request",`unknown field ${s}`,s);return{status:200,body:j(f(t,i,!0),e.name)}}case"run.delete":{const{uploads_pending:e}=await N(f(t,i,!0,!0));return{status:200,body:{run_id:i,uploads_pending:e}}}case"trace.get":{const e=u.get("format")??void 0;if(e&&e!=="entries")throw new n(400,"invalid_request","unknown trace format","format");const s=f(t,i);return{status:200,body:J(i,s.grantee,e)}}default:throw new n(404,"not_found","unknown operation")}}c(I,"handleRunOperation");export{I as handleRunOperation,y as integer};
|
package/dist/daemon/api/map.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export interface OutcomeDeliveryDTOInput {
|
|
|
27
27
|
acceptedAt: number | null;
|
|
28
28
|
permanentFailedAt: number | null;
|
|
29
29
|
lastFailure: {
|
|
30
|
-
kind: 'exit' | 'signal' | 'timeout' | 'spawn_error';
|
|
30
|
+
kind: 'exit' | 'signal' | 'timeout' | 'spawn_error' | 'grant_refused';
|
|
31
31
|
exitCode?: number;
|
|
32
32
|
signal?: string;
|
|
33
33
|
message?: string;
|