@north-light/cli 0.3.415 → 0.3.417
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/builtin-memory/00-runtime-base/00-authoring.md +3 -1
- package/dist/cli.js +7 -7
- package/dist/clients/attach/viewer.js +1025 -1055
- 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/runs/confinement.d.ts +2 -0
- package/dist/core/runs/confinement.js +2 -2
- package/dist/core/runtime/recap.js +1 -1
- package/dist/core/runtime/run-command.d.ts +4 -0
- package/dist/core/runtime/run-command.js +1 -1
- package/dist/core/spaces/mounts.d.ts +5 -0
- package/dist/core/spaces/mounts.js +1 -1
- package/dist/core/spaces/process.d.ts +8 -0
- package/dist/core/spaces/process.js +1 -1
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/files.js +1 -1
- 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/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/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-run.js +2 -1
- package/dist/daemon/crtrd.js +7 -7
- 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/dist/types.js +1 -1
- package/package.json +9 -9
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +338 -2057
- package/scripts/postinstall.mjs +9 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
var O=Object.defineProperty;var a=(e,
|
|
1
|
+
var O=Object.defineProperty;var a=(e,o)=>O(e,"name",{value:o,configurable:!0});import{toErrorBody as $}from"./map.js";import{emitEvent as T}from"../../core/events/emit.js";import{scopeAllowed as f,ScopeDeniedError as A}from"../../core/scopes.js";import{ApiError as d}from"../../api/errors.js";import{activeGrant as k,grantFor as U}from"../../core/grants/mirror.js";import{getNode as _}from"../../core/canvas/canvas.js";import{ownedRun as N,RunError as g}from"../../core/runs/operations.js";import{nodeMayReach as W,placeMayReach as j}from"../../core/runs/confinement.js";import{checkCronFire as G}from"../cron/authority.js";import{readConfig as J}from"../../core/config.js";import{SCOPE_VOCABULARY as z}from"../../core/scopes.js";import{operationFor as H}from"./operations.js";import{CONTROL_BODY_MAX_BYTES as I}from"../../core/llm/body-limits.js";import{getRequest as L,getRequestByInboxTicket as M}from"../../core/human/requests.js";import{requireVisibleReview as B}from"../../core/review/store.js";import{getComment as E}from"../../core/review/comments.js";function P(e,o,t,s,n){const r=e.path,u=n&&typeof n=="object"&&!Array.isArray(n)?n:{};if(r==="/v1/human/requests"||r==="/v1/human/reviews"){if((r.endsWith("/reviews")?u.origin_node_id:u.requester_node_id)!==o.node)throw new d(403,"other_app_run","a broker may create a human request only for its own node");const h=u.source;if(h&&typeof h=="object"&&"nodeId"in h&&h.nodeId!==o.node)throw new d(403,"other_app_run","request source must name the calling node")}if(r==="/v1/human/reviews/self/comments"&&e.method==="GET"&&s.get("actor_node_id")!==o.node)throw new d(403,"other_app_run","self review requires the calling node");if(r.includes("/comments")&&e.method!=="GET"&&u.actor_node_id!==o.node)throw new d(403,"other_app_run","comment author must be the calling node");if(t.comment_id&&!E(t.comment_id))throw new d(404,"not_found","comment not found")}a(P,"authorizeHumanBody");function y(e){return e.class==="broker"||e.class==="cron"}a(y,"boundedByStoredScopes");function b(e){if(e.class==="person"||e.class==="anonymous")return[];if(e.class==="app")return e.scopes;if(e.class==="cron"){const t=G({grantee:e.app,sub:e.sub,grant_id:e.grant,scopes:e.scopes});if(t.allowed)return t.effectiveScopes;throw t.reason==="grant_missing"||t.reason==="grant_revoked"?new d(401,"grant_revoked",t.message):new d(403,"scope_missing",t.message)}const o=e.grant?k(e.grant):U(e.app,e.sub);if(o&&o.grantee===e.app&&o.sub===e.sub)return o.resolved;if(e.app==="app:terminal"&&!J("user").runtime?.issuer)return z;throw new d(401,"grant_revoked","the node grant is not active")}a(b,"resolvedScopes");function v(e){return"class"in e?e.class==="person"||e.class==="anonymous"?null:e.app:e.grantee}a(v,"runCallerApp");function l(e,o,t){return o===void 0||!("class"in e)?!0:e.class==="cron"?j({run:e.origin_run??"",isolated:e.isolated},e.app,o,t):e.class!=="broker"?!0:W(e.node,e.app,o,t)}a(l,"brokerMayReach");function F(e,o,t){const s=v(e);if(s===o)return l(e,t,o);if(s===null)return!0;const n="class"in e?null:k(e.grant),r="class"in e?b(e):n?.sub===e.sub&&n.grantee===e.grantee?n.resolved:[];return f(r,`crtr:conversations:read:${o}`)&&(!("class"in e)||!y(e)||f(e.scopes,`crtr:conversations:read:${o}`))}a(F,"canReadRun");function ge(e,o,t=!1,s=!1){const n=_(o);if(!n||n.parent!==null||n.run_id!==o||!n.grantee||n.deleting_at&&!s)throw new g(404,"not_found","run not found");if(!F(e,n.grantee,o))throw new g(404,"not_found","run not found");if(t&&v(e)!==null&&v(e)!==n.grantee)throw new g(403,"other_app_run","cannot change another app's run");return N(o,n.grantee,s)}a(ge,"requireRun");function x(e,o,t){return e.class!=="person"&&e.class!=="anonymous"&&e.app===o?l(e,t,o):e.class==="person"||e.class!=="anonymous"&&(e.app===o||f(b(e),`crtr:objects:read:${o}`)&&(!y(e)||f(e.scopes,`crtr:objects:read:${o}`)))}a(x,"canReadNode");function Y(e,o,t){if(!e.classes.includes(o.class)&&!(o.class!=="anonymous"&&e.classes.includes("anonymous")))throw new d(403,"permission_error","this operation is not available to the caller");if(o.class==="person"||o.class==="anonymous")return;const s=b(o);if(e.scope&&(!f(s,e.scope)||y(o)&&!f(o.scopes,e.scope)))throw new d(403,"scope_missing",`missing scope ${e.scope}`);if(o.class==="app")return;o.class==="broker"&&V(e,o,t,s);const n=t.id;if((e.resource==="node-read"||e.resource==="node-write")&&n!==void 0){const r=_(n);if(!r)return;if(e.resource==="node-write"&&r.grantee!==o.app)throw new d(403,"other_app_run","cannot change another app's node");if(e.resource==="node-write"&&!l(o,n,r.grantee??"app:terminal"))throw new d(404,"not_found","node not found");if(e.resource==="node-read"&&!x(o,r.grantee??"app:terminal",n))throw new d(404,"not_found","node not found")}}a(Y,"authorizeOperation");function V(e,o,t,s){if(e.resource==="daemon-bash"&&(!f(s,"crtr:files:rw:user:.")||!f(o.scopes,"crtr:files:rw:user:.")))throw new d(403,"scope_missing","missing scope crtr:files:rw:user:.");if(e.resource==="human"){const r=e.path,u=e.method==="GET";if(!u&&(r.endsWith("/respond")||r.endsWith("/submit")||r.endsWith("/dismiss")||r.endsWith("/progress")||r.endsWith("/comment-ranges")||r.includes("/feedback-comments")))throw new d(403,"permission_error","only the person may answer or edit the human inbox");const c=t.request_id?L(t.request_id):t.ticket_id?M(t.ticket_id):null,h=t.comment_id?E(t.comment_id):null,w=t.review_id?B(t.review_id):h?B(h.review_id):null,m=c?.requester_node_id??w?.origin_node_id;if(m!=null){const i=_(m);if(i&&(u?!x(o,i.grantee??"app:terminal",m):i.grantee!==o.app))throw new d(u?404:403,u?"not_found":"other_app_run","human request belongs to another app");if(i&&!u&&!l(o,m,i.grantee??"app:terminal"))throw new d(404,"not_found","human request not found")}else if(c||w)throw new d(403,"permission_error","only the person may access a request without a node")}const n=t.id;if(e.resource==="node-own"&&n!==void 0&&n!==o.node)throw new d(403,"other_app_run","the operation requires the calling node")}a(V,"authorizeBrokerOnly");function D(e,o){if(!f(e,o))throw new A("the bearer token",o)}a(D,"assertCeiling");function X(){return new d(403,"owner_only","this operation is owner-only; a scoped token does not reach it")}a(X,"ownerOnlyError");function ye(e){return e.map(o=>({...o,ownerOnly:!0}))}a(ye,"ownerOnly");class be{static{a(this,"Router")}routes=[];register(o){const t=H(o.method,o.pattern);if(!t)throw new Error(`route has no operation row: ${o.method} ${o.pattern}`);if(this.routes.some(s=>s.method===o.method.toUpperCase()&&s.operation.path===o.pattern))throw new Error(`duplicate route: ${o.method} ${o.pattern}`);return this.routes.push({operation:t,method:o.method.toUpperCase(),segments:R(o.pattern),handler:o.handler,maxBodyBytes:o.maxBodyBytes??I}),this}registerAll(o){for(const t of o)this.register(t);return this}match(o,t){const s=o.toUpperCase(),n=R(t);for(const r of this.routes){if(r.method!==s)continue;const u=K(r.segments,n);if(u!==null)return{handler:r.handler,params:u,operation:r.operation,maxBodyBytes:r.maxBodyBytes}}return null}async handle(o,t,s,n={class:"person"}){try{const r=new URL(o.url??"/","http://127.0.0.1"),u=(o.method??"GET").toUpperCase(),c=this.match(u,r.pathname);if(c===null){C(t,404,"not_found",`no route for ${u} ${r.pathname}`);return}if(Y(c.operation,n,c.params),s!==null){if(c.operation.classes.length===1&&c.operation.classes[0]==="person")throw X();c.operation.scope&&D(s,c.operation.scope)}let h;try{h=await Q(o,c.maxBodyBytes)}catch(i){const p=i instanceof S?"payload_too_large":"invalid_request";C(t,p==="payload_too_large"?413:400,p,i.message);return}n.class==="broker"&&c.operation.resource==="human"&&P(c.operation,n,c.params,r.searchParams,h);const w={method:u,path:r.pathname,params:c.params,query:r.searchParams,body:h,ceiling:s,principal:n,...n.class==="anonymous"&&typeof o.headers.authorization=="string"?{publishToken:o.headers.authorization.slice(7)}:{},req:o,res:t},m=await c.handler(w);t.writableEnded||Z(t,m)}catch(r){const{status:u,body:c}=$(r);u>=500&&T({event:"api.request.failed",level:"error",error:r instanceof Error?r:new Error(String(r)),...n.class==="broker"?{node_id:n.node}:{},fields:{method:(o.method??"GET").toUpperCase(),path:o.url??""}}),q(t,u,c)}}}function R(e){return e.split("/").filter(o=>o.length>0)}a(R,"splitPath");function K(e,o){if(e.length!==o.length)return null;const t={};for(let s=0;s<e.length;s++){const n=e[s],r=o[s];if(n.startsWith(":"))t[n.slice(1)]=decodeURIComponent(r);else if(n!==r)return null}return t}a(K,"matchSegments");class S extends Error{static{a(this,"BodyTooLargeError")}}async function Q(e,o){const t=[];let s=0;for await(const r of e){const u=r;if(s+=u.length,s>o)throw new S(`request body exceeds ${o} bytes`);t.push(u)}if(s===0)return;const n=Buffer.concat(t).toString("utf8").trim();if(n!=="")try{return JSON.parse(n)}catch{throw new Error("request body is not valid JSON")}}a(Q,"readJsonBody");function Z(e,o){if(e.headersSent)return;const t={...o.headers??{}};if(o.rawBody!==void 0){e.writeHead(o.status,t),e.end(o.rawBody);return}if(o.status===204||o.body===void 0){e.writeHead(o.status,t),e.end();return}t["content-type"]===void 0&&(t["content-type"]="application/json; charset=utf-8"),e.writeHead(o.status,t),e.end(JSON.stringify(o.body))}a(Z,"writeResult");function q(e,o,t){if(e.headersSent){e.end();return}e.writeHead(o,{"content-type":"application/json; charset=utf-8"}),e.end(JSON.stringify(t))}a(q,"writeJson");function C(e,o,t,s){q(e,o,{error:{code:t,message:s}})}a(C,"writeError");export{be as Router,D as assertCeiling,l as brokerMayReach,x as canReadNode,F as canReadRun,ye as ownerOnly,X as ownerOnlyError,ge as requireRun,b as resolvedScopes,v as runCallerApp};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type IncomingMessage } from 'node:http';
|
|
2
|
-
import { type RequestPrincipal } from './router.js';
|
|
2
|
+
import { Router, type RequestPrincipal } from './router.js';
|
|
3
3
|
export interface CreateApiServerOptions {
|
|
4
4
|
/** Opt-in TCP listener as `host:port` (or `:port` for all interfaces).
|
|
5
5
|
* Falls back to `CRTRD_TCP`, then the user setting. Absent → unix socket only. */
|
|
@@ -15,6 +15,10 @@ export interface ApiServer {
|
|
|
15
15
|
/** Tear down both listeners and unlink the socket file. Double-close safe. */
|
|
16
16
|
close(): Promise<void>;
|
|
17
17
|
}
|
|
18
|
+
/** Build the Router loaded with every handler route table (A-7). The order is
|
|
19
|
+
* irrelevant — the matcher is exact method+path — but `healthRoutes` (which
|
|
20
|
+
* owns the unversioned `/healthz`) is registered first for clarity. */
|
|
21
|
+
export declare function buildRouter(tcpAddress: () => string | null): Router;
|
|
18
22
|
export type SocketPrincipal = RequestPrincipal;
|
|
19
23
|
export declare function socketPrincipal(req: IncomingMessage): SocketPrincipal | undefined;
|
|
20
24
|
/** Create and start the crtrd API server. Returns immediately with a `close()`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var X=Object.defineProperty;var u=(t,n)=>X(t,"name",{value:n,configurable:!0});import{createServer as x}from"node:http";import{chmodSync as q,existsSync as C,unlinkSync as B}from"node:fs";import{MINIMUM_RUNTIME_VERSION_HEADER as j,RUNTIME_VERSION_HEADER as O,minimumRuntimeRefusal as ee}from"../../api/runtime-version.js";import{crtrVersion as M}from"../../shared/crtr-version.js";import{apiSocketPath as re}from"../../core/canvas/paths.js";import{resolveNodeToken as G}from"../../core/canvas/node-tokens.js";import{grantFor as te,hasSyncedGrants as oe}from"../../core/grants/mirror.js";import{readConfig as U}from"../../core/config.js";import{peerUid as ne}from"../../native/linux.js";import{getApiToken as ie,listScopedApiTokens as se}from"../../core/secrets.js";import{emitEvent as g}from"../../core/events/emit.js";import{envCrtrdTcp as le,envCrtrdToken as ae}from"../../shared/env.js";import{Router as L}from"./router.js";import{handleAttachUpgrade as ce,refuseUpgrade as A}from"./bridge.js";import{boundDaemonControl as ue}from"../control.js";import{attachRoutes as pe}from"./handlers/attach.js";import{bashJobRoutes as de}from"./handlers/bash-jobs.js";import{bashRoutes as me}from"./handlers/bash.js";import{hookExecRoutes as fe}from"./handlers/hook-exec.js";import{brokerOperationRoutes as W}from"./handlers/broker-ops.js";import{brokerSignalRoutes as ge,brokerSignalStreams as he}from"./handlers/broker-signals.js";import{brokerRecoveryRoutes as $,nodeFaultRoutes as Ae}from"./handlers/broker-recovery.js";import{canvasRoutes as Re}from"./handlers/canvas.js";import{chatInventoryRoutes as ye}from"./handlers/chat-inventory.js";import{daemonRoutes as ve}from"./handlers/daemon.js";import{fileRoutes as be}from"./handlers/files.js";import{focusRoutes as we}from"./handlers/focus.js";import{analyticsRoutes as Se}from"./handlers/analytics.js";import{memoryReadsRoutes as Ee}from"./handlers/memory-reads.js";import{memoryRoutes as ke}from"./handlers/memory.js";import{healthRoutes as Te}from"./handlers/health.js";import{humanRequestRoutes as _e}from"./handlers/human-requests.js";import{inboxRoutes as Ce}from"./handlers/inbox.js";import{feedbackCommentRoutes as Oe}from"./handlers/feedback-comments.js";import{objectRoutes as Ue}from"./handlers/objects.js";import{customObjectRoutes as He}from"./handlers/custom-objects.js";import{docRoutes as Ne}from"./handlers/docs.js";import{deliveryRoutes as D}from"./handlers/delivery.js";import{deliveryRetryRoutes as ze}from"./handlers/delivery-retry.js";import{packageDocRoutes as Pe}from"./handlers/package-docs.js";import{brokerMailRoutes as J,messageRoutes as Ie}from"./handlers/messages.js";import{modelAuthRoutes as xe}from"./handlers/modelauth.js";import{llmRoutes as Be}from"./handlers/llm.js";import{providerRoutes as je}from"./handlers/providers.js";import{modelConfigRoutes as Me}from"./handlers/model-config.js";import{nodeRoutes as Ge}from"./handlers/nodes.js";import{nodeRecordRoutes as Le}from"./handlers/node-records.js";import{nodeOutcomeRoutes as We}from"./handlers/node-outcomes.js";import{nodeEventRoutes as $e}from"./handlers/node-events.js";import{profileRoutes as De}from"./handlers/profiles.js";import{prospectiveChatInventoryRoutes as Je}from"./handlers/prospective-chat-inventory.js";import{reportRoutes as Fe}from"./handlers/reports.js";import{runRoutes as Ve}from"./handlers/run-routes.js";import{reviewCommentRoutes as Ye}from"./handlers/review-comments.js";import{reviewRoutes as Ze}from"./handlers/reviews.js";import{subscriptionRoutes as Ke}from"./handlers/subscriptions.js";import{structuredRequestRoutes as Qe}from"./handlers/structured-requests.js";import{cronRoutes as Xe}from"./handlers/crons.js";import{worktreeRoutes as qe}from"./handlers/worktree.js";import{limitsRoutes as er}from"./handlers/limits.js";function rr(t){return new L().registerAll(Te(t)).registerAll(ve).registerAll(Ge).registerAll(Le).registerAll(We).registerAll(ze).registerAll($e).registerAll(de).registerAll(me).registerAll(fe).registerAll(W).registerAll(ge).registerAll(D).registerAll($).registerAll(Ae).registerAll(qe).registerAll(er).registerAll(J).registerAll(Ie).registerAll(Fe).registerAll(Ve).registerAll(Qe).registerAll(pe).registerAll(Re).registerAll(Se).registerAll(Ee).registerAll(ke).registerAll(be).registerAll(Ue).registerAll(He).registerAll(Ne).registerAll(Pe).registerAll(we).registerAll(Ke).registerAll(Xe).registerAll(De).registerAll(xe).registerAll(Be).registerAll(je).registerAll(Me).registerAll(Ze).registerAll(Ye).registerAll(Ce).registerAll(_e).registerAll(ye).registerAll(Je).registerAll(Oe)}u(rr,"buildRouter");function tr(t,n){if(t.toUpperCase()!=="GET")return null;const i=n.split("/").filter(m=>m.length>0);if(i.length!==4||i[0]!=="v1"||i[1]!=="nodes"||i[3]!=="attach")return null;const p=i[2];return p===void 0||p===""?null:decodeURIComponent(p)}u(tr,"attachNodeId");function or(t){const n=t.trim();if(n==="")return null;const i=n.lastIndexOf(":");if(i<0)return null;const p=n.slice(0,i),m=n.slice(i+1),d=Number(m);return!Number.isInteger(d)||d<0||d>65535?null:{host:p===""?"0.0.0.0":p,port:d}}u(or,"parseTcp");function nr(t,n){return`${t.includes(":")?`[${t}]`:t}:${n}`}u(nr,"formatTcpAddress");const b={code:"unauthorized",message:"missing or invalid bearer token"},k={code:"owner_only",message:"attach is owner-only; a scoped token does not reach it"},H={class:"anonymous",app:"",node:"",run:null,uid:-1,sub:"",grant:null,client_id:null,scopes:[]},R=new WeakMap;function F(t){return R.get(t)}u(F,"socketPrincipal");function V(t,n){const i=process.platform==="linux"?ne(t.socket):null,p=t.headers.authorization;if(p===void 0)return i===null||n.has(i)?{class:"person"}:"unauthorized";if(typeof p!="string"||!p.startsWith("Bearer "))return"unauthorized";if(p.slice(7).startsWith("crtr_pub_"))return H;const m=G(p.slice(7));if(!m||i!==null&&i!==m.uid)return"unauthorized";const d=U("user").runtime?.issuer;if(d&&!oe(d.replace(/\/$/,"")))return"runtime_starting";const w=m.grant??te("app:terminal",m.sub)?.id??null;return{class:"broker",...m,grant:w}}u(V,"resolveSocketPrincipal");function ir(t){const n=new Map;for(const i of se("user"))n.set(i.token,{principal:{class:"app",app:i.grantee,scopes:i.scopes},ceiling:i.scopes});return n.set(t,{principal:{class:"person"},ceiling:null}),n}u(ir,"resolveBearers");function Y(t,n){const i=t.headers.authorization;if(!(typeof i!="string"||!i.startsWith("Bearer ")))return n.get(i.slice(7))}u(Y,"authorize");function T(t){t.writeHead(401,{"content-type":"application/json; charset=utf-8"}),t.end(JSON.stringify({error:b}))}u(T,"writeUnauthorized");function sr(t,n){t.writeHead(503,{"content-type":"application/json; charset=utf-8"}),t.end(JSON.stringify({error:{code:"startup_blocked",message:n}}))}u(sr,"writeStartupBlocked");function Z(t){return new Promise(n=>{try{t.closeAllConnections()}catch{}t.close(()=>n())})}u(Z,"closeServer");function gt(t={}){let n=null;const i=rr(()=>n),p=new L().registerAll(W).registerAll(D).registerAll($).registerAll(J),m=u((e,r)=>{const o=ue()?.startupBlock()??null;return o===null||e==="GET"&&(r==="/healthz"||r==="/v1/status")||e==="POST"&&(r==="/v1/daemon/migrate"||r==="/v1/daemon/admit")||o.phase==="recovering"&&p.match(e,r)!==null?null:o},"startupBlockFor"),d=re(),w=new Set([process.getuid?.()??0,...U("user").runtime?.person_uids??[]]),S=u((e,r,o)=>{r.setHeader(O,M());const a=e.headers[j.toLowerCase()],f=ee(Array.isArray(a)?a[0]:a,M());if(f!==null){r.writeHead(f.status,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:f.code,message:f.message,type:"invalid_request_error",origin:"runtime",retryable:!1,..."param"in f?{param:f.param}:{details:f.details}}}));return}const s=u(l=>{const v=F(e);g({event:"api.request.failed",level:"error",...v?.class==="broker"?{node_id:v.node}:{},error:l instanceof Error?l:new Error(String(l))});try{r.headersSent||r.writeHead(500,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:"internal",message:"internal error"}}))}catch{}},"internalFailure");let c;try{c=new URL(e.url??"/","http://127.0.0.1")}catch{try{r.writeHead(400,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:"invalid_request",message:"request URL is invalid"}}))}catch{}return}try{const l=m((e.method??"GET").toUpperCase(),c.pathname);if(l!==null){sr(r,l.message);return}i.handle(e,r,o,F(e)??{class:"person"}).catch(s)}catch(l){s(l)}},"onRequest"),_=u((e,r,o)=>{r.on("error",()=>{});try{const a=new URL(e.url??"/","http://127.0.0.1"),f=m((e.method??"GET").toUpperCase(),a.pathname);if(f!==null){A(r,503,"Service Unavailable","startup_blocked",f.message);return}const s=tr(e.method??"GET",a.pathname);if(s===null){r.destroy();return}ce(e,r,o,s).catch(()=>{try{r.destroy()}catch{}})}catch{try{r.destroy()}catch{}}},"onUpgrade"),K=u(e=>{e.on("error",r=>{g({event:"api.server.failed",level:"error",error:r})})},"bindErrorHandler"),y=x((e,r)=>{try{const o=V(e,w);if(o==="unauthorized"){g({event:"api.principal.denied",level:"warn",fields:{method:e.method}}),T(r);return}if(o==="runtime_starting"){r.writeHead(503,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:"runtime_starting",message:"grant sync is not ready",type:"server_error",origin:"runtime",retryable:!0}}));return}R.set(e,o),o.class==="broker"&&g({event:"api.request.principal",level:o.app==="app:terminal"?"debug":"info",node_id:o.node,fields:{principal:`${o.app} node:${o.node}`,method:e.method,path:e.url}}),S(e,r,null)}catch(o){g({event:"api.principal.failed",level:"error",error:o instanceof Error?o:new Error(String(o))}),T(r)}});y.on("upgrade",(e,r,o)=>{try{const a=V(e,w);if(a==="unauthorized"||a==="runtime_starting"){a==="unauthorized"&&g({event:"api.principal.denied",level:"warn",fields:{method:e.method}}),A(r,a==="unauthorized"?401:503,"Unauthorized",a,"socket principal unavailable");return}if(a.class!=="person"&&a.class!=="broker"){A(r,403,"Forbidden",k.code,k.message);return}R.set(e,a),_(e,r,o)}catch(a){g({event:"api.principal.failed",level:"error",error:a instanceof Error?a:new Error(String(a))}),A(r,401,"Unauthorized",b.code,b.message)}});let N=!1,z,P;const Q=new Promise((e,r)=>{z=e,P=r});y.once("listening",()=>{N=!0,z()}),y.on("error",e=>{g({event:"api.server.failed",level:"error",error:e}),N||P(e)});try{C(d)&&B(d)}catch{}y.listen(d,()=>{try{q(d,process.platform==="linux"&&C("/run/crtr/launcher.sock")?438:384)}catch(e){g({event:"api.socket.chmod_failed",level:"error",error:e instanceof Error?e:new Error(String(e))})}});let h;const E=t.tcp??le()??U("user").api.tcp;if(E!==void 0&&E!==""){const e=or(E);if(e===null)g({event:"api.tcp.invalid",level:"error",error:new Error(`invalid CRTRD_TCP/--tcp spec: ${E}`)});else{const r=t.token??ae()??ie("user"),o=r===void 0?void 0:ir(r),a=o===void 0?(s,c)=>{const l=s.headers.authorization;if(typeof l=="string"&&l.startsWith("Bearer crtr_pub_"))R.set(s,H);else if(typeof l=="string"&&l.startsWith("Bearer ")&&G(l.slice(7))){T(c);return}S(s,c,null)}:(s,c)=>{if((s.method??"GET").toUpperCase()==="OPTIONS"){c.writeHead(204,{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Headers":`authorization, content-type, ${j.toLowerCase()}`,"Access-Control-Allow-Methods":"GET, POST, PATCH, PUT, DELETE, OPTIONS","Access-Control-Max-Age":"600"}),c.end();return}const l=Y(s,o);if(l===void 0&&s.headers.authorization?.startsWith("Bearer crtr_pub_")){R.set(s,H),c.setHeader("Access-Control-Allow-Origin","*"),c.setHeader("Access-Control-Expose-Headers",O),S(s,c,null);return}if(l===void 0){T(c);return}R.set(s,l.principal),c.setHeader("Access-Control-Allow-Origin","*"),c.setHeader("Access-Control-Expose-Headers",O),S(s,c,l.ceiling)},f=o===void 0?_:(s,c,l)=>{const v=Y(s,o);if(v===void 0){A(c,401,"Unauthorized",b.code,b.message);return}if(v.ceiling!==null){A(c,403,"Forbidden",k.code,k.message);return}_(s,c,l)};h=x(a),h.on("upgrade",f),K(h),h.once("listening",()=>{const s=h?.address();if(s!==null&&typeof s=="object"){const{address:c,port:l}=s;n=nr(c,l)}}),h.listen(e.port,e.host)}}let I=!1;return{ready:Q,tcpAddress:u(()=>n,"tcpAddress"),close:u(async()=>{if(!I){I=!0,he.close(),await Promise.all([Z(y),h!==void 0?Z(h):Promise.resolve()]);try{C(d)&&B(d)}catch{}}},"close")}}u(gt,"createApiServer");export{gt as createApiServer,F as socketPrincipal};
|
|
1
|
+
var q=Object.defineProperty;var u=(t,n)=>q(t,"name",{value:n,configurable:!0});import{createServer as x}from"node:http";import{chmodSync as ee,existsSync as C,unlinkSync as B}from"node:fs";import{MINIMUM_RUNTIME_VERSION_HEADER as j,RUNTIME_VERSION_HEADER as O,minimumRuntimeRefusal as re}from"../../api/runtime-version.js";import{crtrVersion as M}from"../../shared/crtr-version.js";import{apiSocketPath as te}from"../../core/canvas/paths.js";import{resolveNodeToken as G}from"../../core/canvas/node-tokens.js";import{grantFor as oe,hasSyncedGrants as ne}from"../../core/grants/mirror.js";import{readConfig as U}from"../../core/config.js";import{peerUid as ie}from"../../native/linux.js";import{getApiToken as se,listScopedApiTokens as le}from"../../core/secrets.js";import{emitEvent as g}from"../../core/events/emit.js";import{envCrtrdTcp as ae,envCrtrdToken as ce}from"../../shared/env.js";import{Router as L}from"./router.js";import{handleAttachUpgrade as ue,refuseUpgrade as R}from"./bridge.js";import{boundDaemonControl as pe}from"../control.js";import{attachRoutes as de}from"./handlers/attach.js";import{bashJobRoutes as me}from"./handlers/bash-jobs.js";import{bashRoutes as fe}from"./handlers/bash.js";import{hookExecRoutes as ge}from"./handlers/hook-exec.js";import{brokerOperationRoutes as W}from"./handlers/broker-ops.js";import{brokerSignalRoutes as he,brokerSignalStreams as Ae}from"./handlers/broker-signals.js";import{brokerRecoveryRoutes as $,nodeFaultRoutes as Re}from"./handlers/broker-recovery.js";import{canvasRoutes as ye}from"./handlers/canvas.js";import{chatInventoryRoutes as ve}from"./handlers/chat-inventory.js";import{daemonRoutes as be}from"./handlers/daemon.js";import{fileRoutes as we}from"./handlers/files.js";import{focusRoutes as Se}from"./handlers/focus.js";import{analyticsRoutes as Ee}from"./handlers/analytics.js";import{memoryReadsRoutes as ke}from"./handlers/memory-reads.js";import{memoryRoutes as Te}from"./handlers/memory.js";import{healthRoutes as _e}from"./handlers/health.js";import{humanRequestRoutes as Ce}from"./handlers/human-requests.js";import{inboxRoutes as Oe}from"./handlers/inbox.js";import{feedbackCommentRoutes as Ue}from"./handlers/feedback-comments.js";import{objectRoutes as He}from"./handlers/objects.js";import{customObjectRoutes as Ne}from"./handlers/custom-objects.js";import{docRoutes as ze}from"./handlers/docs.js";import{deliveryRoutes as D}from"./handlers/delivery.js";import{deliveryRetryRoutes as Pe}from"./handlers/delivery-retry.js";import{packageDocRoutes as Ie}from"./handlers/package-docs.js";import{brokerMailRoutes as J,messageRoutes as xe}from"./handlers/messages.js";import{modelAuthRoutes as Be}from"./handlers/modelauth.js";import{llmRoutes as je}from"./handlers/llm.js";import{providerRoutes as Me}from"./handlers/providers.js";import{modelConfigRoutes as Ge}from"./handlers/model-config.js";import{nodeRoutes as Le}from"./handlers/nodes.js";import{nodeRecordRoutes as We}from"./handlers/node-records.js";import{nodeOutcomeRoutes as $e}from"./handlers/node-outcomes.js";import{nodeEventRoutes as De}from"./handlers/node-events.js";import{profileRoutes as Je}from"./handlers/profiles.js";import{prospectiveChatInventoryRoutes as Fe}from"./handlers/prospective-chat-inventory.js";import{reportRoutes as Ve}from"./handlers/reports.js";import{runRoutes as Ye}from"./handlers/run-routes.js";import{reviewCommentRoutes as Ze}from"./handlers/review-comments.js";import{reviewRoutes as Ke}from"./handlers/reviews.js";import{subscriptionRoutes as Qe}from"./handlers/subscriptions.js";import{structuredRequestRoutes as Xe}from"./handlers/structured-requests.js";import{cronRoutes as qe}from"./handlers/crons.js";import{setCronRequestHandler as F}from"../cron/run-socket.js";import{worktreeRoutes as er}from"./handlers/worktree.js";import{limitsRoutes as rr}from"./handlers/limits.js";function tr(t){return new L().registerAll(_e(t)).registerAll(be).registerAll(Le).registerAll(We).registerAll($e).registerAll(Pe).registerAll(De).registerAll(me).registerAll(fe).registerAll(ge).registerAll(W).registerAll(he).registerAll(D).registerAll($).registerAll(Re).registerAll(er).registerAll(rr).registerAll(J).registerAll(xe).registerAll(Ve).registerAll(Ye).registerAll(Xe).registerAll(de).registerAll(ye).registerAll(Ee).registerAll(ke).registerAll(Te).registerAll(we).registerAll(He).registerAll(Ne).registerAll(ze).registerAll(Ie).registerAll(Se).registerAll(Qe).registerAll(qe).registerAll(Je).registerAll(Be).registerAll(je).registerAll(Me).registerAll(Ge).registerAll(Ke).registerAll(Ze).registerAll(Oe).registerAll(Ce).registerAll(ve).registerAll(Fe).registerAll(Ue)}u(tr,"buildRouter");function or(t,n){if(t.toUpperCase()!=="GET")return null;const i=n.split("/").filter(m=>m.length>0);if(i.length!==4||i[0]!=="v1"||i[1]!=="nodes"||i[3]!=="attach")return null;const p=i[2];return p===void 0||p===""?null:decodeURIComponent(p)}u(or,"attachNodeId");function nr(t){const n=t.trim();if(n==="")return null;const i=n.lastIndexOf(":");if(i<0)return null;const p=n.slice(0,i),m=n.slice(i+1),d=Number(m);return!Number.isInteger(d)||d<0||d>65535?null:{host:p===""?"0.0.0.0":p,port:d}}u(nr,"parseTcp");function ir(t,n){return`${t.includes(":")?`[${t}]`:t}:${n}`}u(ir,"formatTcpAddress");const w={code:"unauthorized",message:"missing or invalid bearer token"},k={code:"owner_only",message:"attach is owner-only; a scoped token does not reach it"},H={class:"anonymous",app:"",node:"",run:null,uid:-1,sub:"",grant:null,client_id:null,scopes:[]},A=new WeakMap;function V(t){return A.get(t)}u(V,"socketPrincipal");function Y(t,n){const i=process.platform==="linux"?ie(t.socket):null,p=t.headers.authorization;if(p===void 0)return i===null||n.has(i)?{class:"person"}:"unauthorized";if(typeof p!="string"||!p.startsWith("Bearer "))return"unauthorized";if(p.slice(7).startsWith("crtr_pub_"))return H;const m=G(p.slice(7));if(!m||i!==null&&i!==m.uid)return"unauthorized";const d=U("user").runtime?.issuer;if(d&&!ne(d.replace(/\/$/,"")))return"runtime_starting";const S=m.grant??oe("app:terminal",m.sub)?.id??null;return{class:"broker",...m,grant:S}}u(Y,"resolveSocketPrincipal");function sr(t){const n=new Map;for(const i of le("user"))n.set(i.token,{principal:{class:"app",app:i.grantee,scopes:i.scopes},ceiling:i.scopes});return n.set(t,{principal:{class:"person"},ceiling:null}),n}u(sr,"resolveBearers");function Z(t,n){const i=t.headers.authorization;if(!(typeof i!="string"||!i.startsWith("Bearer ")))return n.get(i.slice(7))}u(Z,"authorize");function T(t){t.writeHead(401,{"content-type":"application/json; charset=utf-8"}),t.end(JSON.stringify({error:w}))}u(T,"writeUnauthorized");function lr(t,n){t.writeHead(503,{"content-type":"application/json; charset=utf-8"}),t.end(JSON.stringify({error:{code:"startup_blocked",message:n}}))}u(lr,"writeStartupBlocked");function K(t){return new Promise(n=>{try{t.closeAllConnections()}catch{}t.close(()=>n())})}u(K,"closeServer");function At(t={}){let n=null;const i=tr(()=>n),p=new L().registerAll(W).registerAll(D).registerAll($).registerAll(J),m=u((e,r)=>{const o=pe()?.startupBlock()??null;return o===null||e==="GET"&&(r==="/healthz"||r==="/v1/status")||e==="POST"&&(r==="/v1/daemon/migrate"||r==="/v1/daemon/admit")||o.phase==="recovering"&&p.match(e,r)!==null?null:o},"startupBlockFor"),d=te(),S=new Set([process.getuid?.()??0,...U("user").runtime?.person_uids??[]]),y=u((e,r,o)=>{r.setHeader(O,M());const a=e.headers[j.toLowerCase()],f=re(Array.isArray(a)?a[0]:a,M());if(f!==null){r.writeHead(f.status,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:f.code,message:f.message,type:"invalid_request_error",origin:"runtime",retryable:!1,..."param"in f?{param:f.param}:{details:f.details}}}));return}const s=u(l=>{const b=V(e);g({event:"api.request.failed",level:"error",...b?.class==="broker"?{node_id:b.node}:{},error:l instanceof Error?l:new Error(String(l))});try{r.headersSent||r.writeHead(500,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:"internal",message:"internal error"}}))}catch{}},"internalFailure");let c;try{c=new URL(e.url??"/","http://127.0.0.1")}catch{try{r.writeHead(400,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:"invalid_request",message:"request URL is invalid"}}))}catch{}return}try{const l=m((e.method??"GET").toUpperCase(),c.pathname);if(l!==null){lr(r,l.message);return}i.handle(e,r,o,V(e)??{class:"person"}).catch(s)}catch(l){s(l)}},"onRequest"),_=u((e,r,o)=>{r.on("error",()=>{});try{const a=new URL(e.url??"/","http://127.0.0.1"),f=m((e.method??"GET").toUpperCase(),a.pathname);if(f!==null){R(r,503,"Service Unavailable","startup_blocked",f.message);return}const s=or(e.method??"GET",a.pathname);if(s===null){r.destroy();return}ue(e,r,o,s).catch(()=>{try{r.destroy()}catch{}})}catch{try{r.destroy()}catch{}}},"onUpgrade"),Q=u(e=>{e.on("error",r=>{g({event:"api.server.failed",level:"error",error:r})})},"bindErrorHandler"),v=x((e,r)=>{try{const o=Y(e,S);if(o==="unauthorized"){g({event:"api.principal.denied",level:"warn",fields:{method:e.method}}),T(r);return}if(o==="runtime_starting"){r.writeHead(503,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:"runtime_starting",message:"grant sync is not ready",type:"server_error",origin:"runtime",retryable:!0}}));return}A.set(e,o),o.class==="broker"&&g({event:"api.request.principal",level:o.app==="app:terminal"?"debug":"info",node_id:o.node,fields:{principal:`${o.app} node:${o.node}`,method:e.method,path:e.url}}),y(e,r,null)}catch(o){g({event:"api.principal.failed",level:"error",error:o instanceof Error?o:new Error(String(o))}),T(r)}});v.on("upgrade",(e,r,o)=>{try{const a=Y(e,S);if(a==="unauthorized"||a==="runtime_starting"){a==="unauthorized"&&g({event:"api.principal.denied",level:"warn",fields:{method:e.method}}),R(r,a==="unauthorized"?401:503,"Unauthorized",a,"socket principal unavailable");return}if(a.class!=="person"&&a.class!=="broker"){R(r,403,"Forbidden",k.code,k.message);return}A.set(e,a),_(e,r,o)}catch(a){g({event:"api.principal.failed",level:"error",error:a instanceof Error?a:new Error(String(a))}),R(r,401,"Unauthorized",w.code,w.message)}}),F((e,r,o)=>{A.set(e,o),y(e,r,null)});let N=!1,z,P;const X=new Promise((e,r)=>{z=e,P=r});v.once("listening",()=>{N=!0,z()}),v.on("error",e=>{g({event:"api.server.failed",level:"error",error:e}),N||P(e)});try{C(d)&&B(d)}catch{}v.listen(d,()=>{try{ee(d,process.platform==="linux"&&C("/run/crtr/launcher.sock")?438:384)}catch(e){g({event:"api.socket.chmod_failed",level:"error",error:e instanceof Error?e:new Error(String(e))})}});let h;const E=t.tcp??ae()??U("user").api.tcp;if(E!==void 0&&E!==""){const e=nr(E);if(e===null)g({event:"api.tcp.invalid",level:"error",error:new Error(`invalid CRTRD_TCP/--tcp spec: ${E}`)});else{const r=t.token??ce()??se("user"),o=r===void 0?void 0:sr(r),a=o===void 0?(s,c)=>{const l=s.headers.authorization;if(typeof l=="string"&&l.startsWith("Bearer crtr_pub_"))A.set(s,H);else if(typeof l=="string"&&l.startsWith("Bearer ")&&G(l.slice(7))){T(c);return}y(s,c,null)}:(s,c)=>{if((s.method??"GET").toUpperCase()==="OPTIONS"){c.writeHead(204,{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Headers":`authorization, content-type, ${j.toLowerCase()}`,"Access-Control-Allow-Methods":"GET, POST, PATCH, PUT, DELETE, OPTIONS","Access-Control-Max-Age":"600"}),c.end();return}const l=Z(s,o);if(l===void 0&&s.headers.authorization?.startsWith("Bearer crtr_pub_")){A.set(s,H),c.setHeader("Access-Control-Allow-Origin","*"),c.setHeader("Access-Control-Expose-Headers",O),y(s,c,null);return}if(l===void 0){T(c);return}A.set(s,l.principal),c.setHeader("Access-Control-Allow-Origin","*"),c.setHeader("Access-Control-Expose-Headers",O),y(s,c,l.ceiling)},f=o===void 0?_:(s,c,l)=>{const b=Z(s,o);if(b===void 0){R(c,401,"Unauthorized",w.code,w.message);return}if(b.ceiling!==null){R(c,403,"Forbidden",k.code,k.message);return}_(s,c,l)};h=x(a),h.on("upgrade",f),Q(h),h.once("listening",()=>{const s=h?.address();if(s!==null&&typeof s=="object"){const{address:c,port:l}=s;n=ir(c,l)}}),h.listen(e.port,e.host)}}let I=!1;return{ready:X,tcpAddress:u(()=>n,"tcpAddress"),close:u(async()=>{if(!I){I=!0,F(null),Ae.close(),await Promise.all([K(v),h!==void 0?K(h):Promise.resolve()]);try{C(d)&&B(d)}catch{}}},"close")}}u(At,"createApiServer");export{tr as buildRouter,At as createApiServer,V as socketPrincipal};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CronRefusalReason } from './authority.js';
|
|
1
2
|
export declare const cronDaemonInstanceId: `${string}-${string}-${string}-${string}-${string}`;
|
|
2
3
|
export interface LiveRun {
|
|
3
4
|
runId: string;
|
|
@@ -9,3 +10,30 @@ export declare function liveRunFor(cronId: string): LiveRun | undefined;
|
|
|
9
10
|
export declare function registerLiveRun(cronId: string, run: LiveRun): void;
|
|
10
11
|
export declare function clearLiveRun(cronId: string, runId: string): void;
|
|
11
12
|
export declare function markSelfCanceledCronRun(cronId: string, runId: string): boolean;
|
|
13
|
+
/** One cron execution's identity while it runs: the stored cron principal keyed by execution id.
|
|
14
|
+
* Its socket answers as this principal; the grant sweep and the router read it. A principal is
|
|
15
|
+
* never read off the cron row after the fire, so two executions of one run keep separate scopes. */
|
|
16
|
+
export interface CronExecution {
|
|
17
|
+
executionId: string;
|
|
18
|
+
cronId: string;
|
|
19
|
+
grantee: string;
|
|
20
|
+
sub: string;
|
|
21
|
+
grantId: string | null;
|
|
22
|
+
scopes: string[] | null;
|
|
23
|
+
originRunId: string | null;
|
|
24
|
+
isolated: boolean;
|
|
25
|
+
anchor: string | null;
|
|
26
|
+
/** The app's OS uid: the only peer the execution socket serves on Linux. */
|
|
27
|
+
uid: number;
|
|
28
|
+
/** Rebuilt from a row and a live sandbox after a daemon restart: no executor promise owns its close. */
|
|
29
|
+
rebuilt: boolean;
|
|
30
|
+
/** Set by the grant sweep when it stopped this execution; the executor settles the run `refused` with it. */
|
|
31
|
+
refusal: {
|
|
32
|
+
reason: CronRefusalReason;
|
|
33
|
+
message: string;
|
|
34
|
+
} | null;
|
|
35
|
+
}
|
|
36
|
+
export declare function registerCronExecution(execution: CronExecution): void;
|
|
37
|
+
export declare function cronExecutionFor(executionId: string): CronExecution | undefined;
|
|
38
|
+
export declare function dropCronExecution(executionId: string): void;
|
|
39
|
+
export declare function cronExecutions(): CronExecution[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var i=Object.defineProperty;var n=(e,t)=>i(e,"name",{value:t,configurable:!0});import{randomUUID as c}from"node:crypto";const x=c(),o=new Map;function d(e){return o.get(e)}n(d,"liveRunFor");function l(e,t){o.set(e,t)}n(l,"registerLiveRun");function p(e,t){const r=o.get(e);r!==void 0&&r.runId===t&&o.delete(e)}n(p,"clearLiveRun");function a(e,t){const r=o.get(e);return r===void 0||r.runId!==t?!1:(r.selfCanceled=!0,!0)}n(a,"markSelfCanceledCronRun");const u=new Map;function v(e){u.set(e.executionId,e)}n(v,"registerCronExecution");function g(e){return u.get(e)}n(g,"cronExecutionFor");function m(e){u.delete(e)}n(m,"dropCronExecution");function C(){return[...u.values()]}n(C,"cronExecutions");export{p as clearLiveRun,x as cronDaemonInstanceId,g as cronExecutionFor,C as cronExecutions,m as dropCronExecution,d as liveRunFor,a as markSelfCanceledCronRun,v as registerCronExecution,l as registerLiveRun};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type IncomingMessage, type ServerResponse } from 'node:http';
|
|
2
|
+
import type { CronPrincipalClass } from '../api/router.js';
|
|
3
|
+
import { type CronExecution } from './live-runs.js';
|
|
4
|
+
/** Installed by the API server: runs one request through the router as `principal`. */
|
|
5
|
+
export type CronRequestHandler = (req: IncomingMessage, res: ServerResponse, principal: CronPrincipalClass) => void;
|
|
6
|
+
export declare function setCronRequestHandler(handler: CronRequestHandler | null): void;
|
|
7
|
+
type PeerUid = (req: IncomingMessage) => number | null;
|
|
8
|
+
/** Test seam: the peer uid of a request (null = unchecked). */
|
|
9
|
+
export declare function __setCronPeerUidForTest(fn: PeerUid | undefined): void;
|
|
10
|
+
export declare function cronSocketPath(executionId: string): string;
|
|
11
|
+
export declare function cronPrincipalClassOf(e: CronExecution): CronPrincipalClass;
|
|
12
|
+
/** Register the execution and open its socket. The folder is created daemon-owned, group daemon, mode 0770 — the state
|
|
13
|
+
* the root launcher's `SourceValidator.pin` accepts and re-owns to the app uid for a launch whose node id is
|
|
14
|
+
* `cron-<execution_id>` (`launcher/validate.ts`); `cron-launch.json` carries the config snapshot a sandboxed
|
|
15
|
+
* `crtr` installs (the shape of `broker-launch.json`'s). */
|
|
16
|
+
export declare function openCronExecution(execution: CronExecution, cwd: string, profileId: string | null): Promise<void>;
|
|
17
|
+
/** Close the listener, delete the execution folder, drop the registry entry. Idempotent. */
|
|
18
|
+
export declare function closeCronExecution(executionId: string): void;
|
|
19
|
+
/** Resolves once a restart rebuild finished; the grant sweep waits on it so it never stops a live execution it has not yet re-registered. */
|
|
20
|
+
export declare function cronExecutionsReady(): Promise<void>;
|
|
21
|
+
/** After a daemon restart the listener died with the daemon but the folder and the sandbox survived. Rebuild the registry from
|
|
22
|
+
* `running` rows whose recorded execution id still has a live `cron-<id>` sandbox and re-listen on the same path; delete
|
|
23
|
+
* `nodes/cron-*` folders with no live sandbox. A self-canceled execution (row deleted) cannot be rebuilt: it stays without
|
|
24
|
+
* an API path and the grant sweep stops it. */
|
|
25
|
+
export declare function rebuildCronExecutions(list?: () => Promise<Array<{
|
|
26
|
+
nodeId: string;
|
|
27
|
+
}>>): Promise<void>;
|
|
28
|
+
/** Close rebuilt executions whose row no longer records them (their lease settled elsewhere). Own executions close in the executor. */
|
|
29
|
+
export declare function reapRebuiltCronExecutions(): void;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var k=Object.defineProperty;var i=(r,n)=>k(r,"name",{value:n,configurable:!0});import{createServer as F}from"node:http";import{chmodSync as g,existsSync as x,mkdirSync as A,readdirSync as O,rmSync as v,unlinkSync as z}from"node:fs";import{dirname as H,join as f}from"node:path";import{peerUid as M}from"../../native/linux.js";import{nodeRunDir as a}from"../../core/canvas/paths.js";import{readConfig as U,readMergedLaunchConfig as j}from"../../core/config.js";import{readRotationConfig as q}from"../../core/subscription-state.js";import{runningCrons as _}from"../../core/canvas/crons.js";import{emitEvent as u}from"../../core/events/emit.js";import{cronLaunchId as s}from"../../core/spaces/process.js";import{ensureAppSpace as B}from"../../core/spaces/space.js";import{launcherPresent as y,listSandboxes as b}from"../../core/grants/sandboxes.js";import{uidForApp as J}from"../../core/spaces/users.js";import{setModeBeneath as L,writeFileBeneath as N}from"../../core/spaces/open-beneath.js";import{cronExecutionFor as S,cronExecutions as D,dropCronExecution as C,registerCronExecution as w}from"./live-runs.js";let p=null;function ar(r){p=r}i(ar,"setCronRequestHandler");const E=i(r=>process.platform==="linux"?M(r.socket):null,"linuxPeerUid");let I=E;function dr(r){I=r??E}i(dr,"__setCronPeerUidForTest");const m=new Map;function T(r){return f(a(s(r)),"cli.sock")}i(T,"cronSocketPath");function W(r){return{class:"cron",app:r.grantee,sub:r.sub,grant:r.grantId,scopes:r.scopes,cron:r.cronId,execution:r.executionId,origin_run:r.originRunId,isolated:r.isolated,anchor:r.anchor,uid:r.uid}}i(W,"cronPrincipalClassOf");function d(r,n,t,o){r.writeHead(n,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:t,message:o,type:"invalid_request_error",origin:"runtime",retryable:!1}}))}i(d,"refuse");function G(r){return(n,t)=>{try{const o=S(r);if(o===void 0){d(t,401,"unauthorized","this cron execution has ended");return}const e=I(n);if(e!==null&&e!==o.uid){u({event:"api.principal.denied",level:"warn",fields:{method:n.method,execution:r}}),d(t,401,"unauthorized","missing or invalid bearer token");return}if(p===null){d(t,503,"daemon_unavailable","the API server is not running");return}p(n,t,W(o))}catch(o){u({event:"api.principal.failed",level:"error",error:o instanceof Error?o:new Error(String(o))}),t.headersSent||d(t,401,"unauthorized","missing or invalid bearer token")}}}i(G,"onCronRequest");async function P(r){const n=T(r);x(n)&&z(n);const t=F(G(r));await new Promise((o,e)=>{t.once("error",e),t.listen(n,()=>{t.off("error",e),o()})}),g(n,438),m.set(r,t)}i(P,"listen");async function lr(r,n,t){const o=a(s(r.executionId));w(r);try{A(o,{recursive:!0,mode:504}),g(o,504);const e={user:U("user"),launch:j(n,t),rotation:q()},c=f(o,"cron-launch.json");if(N(o,c,JSON.stringify({configSnapshot:e}),{atomic:!0,mode:416}),y()){const l=await B(r.grantee);L(o,c,416,{uid:process.getuid(),gid:l.gid})}await P(r.executionId)}catch(e){throw R(r.executionId),e}}i(lr,"openCronExecution");function R(r){C(r);const n=m.get(r);m.delete(r),n!==void 0&&(n.closeAllConnections(),n.close());try{v(a(s(r)),{recursive:!0,force:!0})}catch{}}i(R,"closeCronExecution");let h=Promise.resolve();function fr(){return h}i(fr,"cronExecutionsReady");function pr(r=b){return h=(async()=>{if(r===b&&!y())return;const t=new Set((await r()).map(e=>e.nodeId));for(const e of _()){const c=e.run_execution_id;if(!(c==null||!t.has(s(c))||S(c)!==void 0))try{w({executionId:c,cronId:e.cron_id,grantee:e.grantee,sub:e.sub,grantId:e.grant_id,scopes:e.scopes,originRunId:e.origin_run_id,isolated:e.isolated,anchor:e.anchor_node,uid:J(e.grantee),rebuilt:!0,refusal:null}),await P(c),u({level:"info",event:"cron.execution.rebuilt",fields:{cron_id:e.cron_id,execution_id:c}})}catch(l){C(c),u({level:"error",event:"cron.execution.rebuild_failed",error:l,fields:{cron_id:e.cron_id,execution_id:c}})}}if(process.platform!=="linux")return;const o=H(a(s("x")));if(x(o)){for(const e of O(o))if(!(!e.startsWith("cron-")||t.has(e)))try{v(f(o,e),{recursive:!0,force:!0})}catch{}}})().catch(t=>{u({level:"error",event:"cron.execution.rebuild_failed",error:t})}),h}i(pr,"rebuildCronExecutions");function mr(){const r=new Set(_().map(n=>n.run_execution_id));for(const n of D())n.rebuilt&&!r.has(n.executionId)&&R(n.executionId)}i(mr,"reapRebuiltCronExecutions");export{dr as __setCronPeerUidForTest,R as closeCronExecution,fr as cronExecutionsReady,W as cronPrincipalClassOf,T as cronSocketPath,lr as openCronExecution,mr as reapRebuiltCronExecutions,pr as rebuildCronExecutions,ar as setCronRequestHandler};
|
package/dist/daemon/cron-run.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var $=Object.defineProperty;var c=(e,t)=>$(e,"name",{value:t,configurable:!0});import{randomUUID as v}from"node:crypto";import{CRON_OUTPUT_HEAD_BYTES as g,EMPTY_STDOUT_SHA256 as y,formatCronOutputHead as I}from"./cron/capture.js";import{clearLiveRun as S,cronDaemonInstanceId as M,registerLiveRun as B}from"./cron/live-runs.js";import{closeCronExecution as L,openCronExecution as P}from"./cron/run-socket.js";import{appProcessSandboxed as U}from"../core/spaces/process.js";import{nodeAgentDir as F}from"../core/canvas/paths.js";import{uidForApp as N}from"../core/spaces/users.js";import{buildArmedRunContext as Y}from"./cron/armed-context.js";import{ensureCronFolder as q}from"./cron/folders.js";import{checkCronFire as J,cronPrincipalOf as V}from"./cron/authority.js";import{decideHeldSettle as X,EXIT_HELD as z,HELD_DELIVERED as G}from"./cron/held.js";import{settleRun as x}from"./cron/settle.js";import{applyOverlapPolicies as K,expireDueCrons as Q}from"./cron/passes.js";import{dueClockCrons as W,acquireCronRunLease as Z,attachCronRunPid as j,advanceCron as ee,setCronState as te}from"../core/canvas/crons.js";import{emitEvent as re}from"../core/events/emit.js";import{runAppCommand as ne}from"../core/runtime/run-command.js";import{nextSlotAfter as oe}from"../core/wake.js";import{CRON_OUTPUT_HEAD_BYTES as Me}from"./cron/capture.js";import{cronDaemonInstanceId as be,markSelfCanceledCronRun as ke}from"./cron/live-runs.js";import{recoverStaleCronLeases as ve}from"./cron/lease-recovery.js";async function se(e,t){const{runId:s,outOfBand:r,startedAtIso:o,finishedAtMs:n,code:i,timedOut:l,processError:u}=t,m=!r&&!l&&u==null&&i===z&&!t.wasReplaced&&!t.wasSelfCanceled;let p,f;return t.wasReplaced?(p={kind:"stated",delivered:"replaced (overlap=replace)"},f="release"):m?(p={kind:"stated",delivered:G},f=X(e,o,n)??"release"):(p={kind:"disposed",ctx:{runId:s,startedAtIso:o,exitCode:l?-1:i,timedOut:l,runTimeoutS:e.run_timeout_s,processErrorMessage:u!=null?u.message:null,stdoutHead:t.stdoutHead,stdoutSha256:t.stdoutSha256,stderrHead:t.stderrHead},selfCanceled:t.wasSelfCanceled},f=r?"release":{kind:"clear"}),x(e,{outOfBand:r,outcome:p,record:{runId:s,startedAtIso:o,finishedAtMs:n,durationMs:n-t.startedAtMs,exitCode:l?-1:i,stdoutHead:t.stdoutHead===""?null:t.stdoutHead,stderrHead:u!=null?`run process error: ${u.message}`.slice(0,g):t.stderrHead===""?null:t.stderrHead},lease:f})}c(se,"settleClosedRun");function H(e,t,s,r){const o=Date.now(),n=`${s}: ${r.message}`.slice(0,g);return x(e,{outOfBand:t.outOfBand,outcome:{kind:"disposed",ctx:{runId:t.runId,startedAtIso:t.startedAtIso,exitCode:null,timedOut:!1,runTimeoutS:e.run_timeout_s,processErrorMessage:r.message,stdoutHead:"",stdoutSha256:y,stderrHead:n}},record:{runId:t.runId,startedAtIso:t.startedAtIso,finishedAtMs:o,durationMs:o-t.startedAtMs,exitCode:null,stdoutHead:null,stderrHead:n},lease:"release"})}c(H,"settleUnspawnedRun");function T(e,t){let s;try{s=oe(e.recur,new Date(t))}catch(r){return re({level:"error",event:"cron.recur.unparseable",error:r,fields:{cron_id:e.cron_id,name:e.name,recur:e.recur}}),te(e.cron_id,"paused"),new Error(`unparseable recurrence ${JSON.stringify(e.recur)}: ${r.message} \u2014 this cron cannot be rescheduled until its recurrence is fixed (\`crtr cron cancel\` and re-arm it), so it has been paused.`)}return ee(e.cron_id,s),null}c(T,"advanceRecurrence");function de(e,t){const{outOfBand:s}=t;return new Promise((r,o)=>{const n=v(),i=Date.now(),l=new Date(i).toISOString(),u={runId:n,outOfBand:s,startedAtIso:l,startedAtMs:i};if(!Z(e.cron_id,M,l,s,n)){r(null);return}const m=J(V(e));if(!m.allowed){try{if(!s&&e.recur!=null){const h=T(e,i);if(h!==null)throw h}}catch(h){H(e,u,"run could not be armed",h).then(r,o);return}const d=Date.now();x(e,{outOfBand:s,outcome:{kind:"refused",reason:m.reason,message:m.message},record:{runId:n,startedAtIso:l,finishedAtMs:d,durationMs:d-i,exitCode:null,stdoutHead:null,stderrHead:`${m.reason}: ${m.message}`.slice(0,g)},lease:"release"}).then(r,o);return}let p;try{if(e.isolated&&q(e.grantee,e.cron_id),{env:p}=Y(e,n),!s&&e.recur!=null){const d=T(e,i);if(d!==null)throw d}}catch(d){H(e,u,"run could not be armed",d).then(r,o);return}let f=null,D=!1;const w={runId:n,replaced:!1,selfCanceled:!1,kill:c(()=>{f!==null?f():D=!0},"kill")};B(e.cron_id,w);const C=U()?{executionId:n,cronId:e.cron_id,grantee:e.grantee,sub:e.sub,grantId:e.grant_id,scopes:e.scopes,originRunId:e.origin_run_id,isolated:e.isolated,anchor:e.anchor_node,uid:N(e.grantee),rebuilt:!1,refusal:null}:null,E=c(()=>{C!==null&&L(n)},"closeExecution");c(async()=>(C!==null&&await P(C,e.cwd,e.profile),ne({command:e.command,cwd:e.cwd,env:p,app:e.grantee,isolated:e.isolated,principal:{sub:e.sub,grantId:e.grant_id,scopes:e.scopes},...e.isolated?{folder:e.cwd}:{},...C!==null?{cron:{executionId:n,...e.anchor_node===null||e.isolated?{}:{anchorDir:F(e.anchor_node,e.grantee)}}}:e.anchor_node===null||e.isolated?{}:{nodeId:e.anchor_node},timeoutMs:e.run_timeout_s*1e3,headBytes:g})),"launch")().then(d=>{if(d.pid===null){S(e.cron_id,n),E(),d.completion.then(a=>{H(e,u,"run failed to spawn",a.processError??new Error("bash did not provide a process id")).then(r,o)},o);return}let h=!1;const A=c(()=>{d.completion.then(a=>{const O=w.replaced,b=w.selfCanceled,_=C?.refusal??null;if(S(e.cron_id,n),E(),h){r(null);return}if(_!==null&&!O){const R=Date.now(),k=I(a.stderr,a.stderrTruncated);x(e,{outOfBand:s,outcome:{kind:"refused",reason:_.reason,message:_.message},record:{runId:n,startedAtIso:l,finishedAtMs:R,durationMs:R-i,exitCode:a.exitCode,stdoutHead:I(a.stdout,a.stdoutTruncated)||null,stderrHead:`${_.reason}: ${_.message}
|
|
2
|
+
${k}`.trim().slice(0,g)},lease:"release"}).then(r,o);return}se(e,{runId:n,outOfBand:s,startedAtIso:l,startedAtMs:i,finishedAtMs:Date.now(),code:a.exitCode,timedOut:a.timedOut,processError:a.processError,stdoutHead:I(a.stdout,a.stdoutTruncated),stderrHead:I(a.stderr,a.stderrTruncated),stdoutSha256:a.stdoutSha256,wasReplaced:O,wasSelfCanceled:b}).then(r,o)},o)},"settleCommand");if(!j(e.cron_id,M,l,d.pid)&&!w.selfCanceled){h=!0,d.terminate(),A();return}f=d.terminate,D&&d.terminate(),A()},d=>{S(e.cron_id,n),E(),H(e,u,"run failed to spawn",d).then(r,o)})})}c(de,"executeCron");function Ae(e,t={}){const s=new Date(e).toISOString();Q(s),K(e,s);const r=[];for(const o of W(s)){const n=de(o,{outOfBand:!1}).then(()=>{});r.push(n),t.register?.(n)}for(const o of r)o.catch(()=>{});return r}c(Ae,"runDueCrons");export{Me as CRON_OUTPUT_HEAD_BYTES,be as cronDaemonInstanceId,de as executeCron,ke as markSelfCanceledCronRun,ve as recoverStaleCronLeases,Ae as runDueCrons};
|
package/dist/daemon/crtrd.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
var Se=Object.defineProperty;var s=(t,o)=>Se(t,"name",{value:o,configurable:!0});import{writeFileSync as _e,readFileSync as ke,rmSync as be,mkdirSync as Y,existsSync as k,chmodSync as Ee}from"node:fs";import{createServer as X}from"node:http";import{dirname as Re}from"node:path";import{envCrtrdTcp as Ie}from"../shared/env.js";import{serveCanvasConversationStore as Pe}from"../core/canvas/sessions.js";import{pollRunObservers as xe}from"./runs/observer.js";import{randomBytes as De}from"node:crypto";import{apiSocketPath as Te,crtrHome as Ce}from"../core/canvas/paths.js";import{activeAppUsers as Le}from"../core/spaces/users.js";import{ensureAppSpace as Ae,ensureUserFiles as Ne}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as R}from"../launcher/spec.js";import{launcherRequest as x}from"../launcher/client.js";import{installLauncherEventSink as $e}from"./launcher-events.js";import{initializeEmptyLayout as Me,layoutNeedsMigration as Ge,socketDir as Z}from"../core/layout.js";import{migrateLooseKeys as Oe}from"../core/layout-migrate/loose-keys.js";import{getNode as Fe,listNodesForSupervision as He,setFrozen as Be}from"../core/canvas/index.js";import{createApiServer as je}from"./api/server.js";import{createAppListener as qe}from"./api/app-listener.js";import{installLocalTerminalGrant as ze}from"../core/grants/local.js";import{onRunEventAppended as We}from"../core/runs/events.js";import{runNotices as Ue}from"./run-notices.js";import{createGrantSync as Ke,GRANT_SYNC_INTERVAL_S as Je}from"../core/grants/sync.js";import{runRemovalCascades as Ve}from"../core/grants/remove.js";import{sweepSandboxes as Qe}from"../core/grants/on-sync.js";import{hasSyncedGrants as Ye}from"../core/grants/mirror.js";import{readConfig as Xe}from"../core/config.js";import{reconcileUndeliveredTickets as Ze}from"./human/sweep.js";import{resetHandedOffDeliveries as er}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as rr}from"../core/canvas/daemon-actions.js";import{sweepReviews as tr}from"./review/sweep.js";import{pidfilePath as G}from"./pidfile.js";import{rendererWarning as or}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as nr}from"./supervise-cadence.js";import{createResumeDetector as ir}from"./resume-detector.js";import{BrokerSupervisionReconciler as sr}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as ar}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as cr}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as lr}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as dr}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as pr}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as mr}from"./reconcilers/action-lane.js";import{endLiveExecution as fr,migrateLegacyExecutionIdentities as ur}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as hr}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as vr}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as wr}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as gr}from"./reconcilers/storage-due-work.js";import{BashDeadlineReconciler as yr}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as Sr}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as _r}from"./reconcilers/structured-request-lane.js";import{reconcileStructuredRequestSidecars as kr}from"./structured-requests/boot.js";import{captureLivenessSnapshot as ee,capturePidCommand as br,captureTeardownSnapshot as re,isPidAlive as Er,isAnyPidAlive as te,killProcessTreePids as oe}from"../core/canvas/pid.js";import{bindFleet as Rr,boundFleet as Ir}from"../core/runtime/fleet.js";import{bindDaemonControl as Pr,clearDaemonControl as xr}from"./control.js";import{execDaemon as Dr,spawnDaemon as Tr}from"./manage.js";import{reconcileAndMigrateBoot as Cr}from"../core/canvas/boot.js";import{resolveInstallId as Lr}from"../core/canvas/install-id.js";import{setInstallId as Ar}from"../core/runtime/nodes.js";import{claimDaemonOwnership as Nr,releaseDaemonOwnership as ne}from"../core/canvas/daemon-owner.js";import{migrate as $r,openDb as ie,withCanvasWrite as Mr,bindDaemonCanvasOpenHook as Gr,clearDaemonCanvasOpenHook as se}from"../core/canvas/db.js";import{brokerSignalBus as O}from"./broker-signals/bus.js";import{brokerSignalStreams as Or}from"./api/handlers/broker-signals.js";import{reconcilePackageDocuments as Fr}from"../core/graph/package-docs.js";import{startRepoSync as Hr}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as Br,reapStaleSpares as jr}from"../core/runtime/warm-pool.js";import{DaemonFleet as qr}from"./fleet.js";import{BOOT_MIGRATIONS as zr}from"./boot-migrations.js";import{applyJoinedOwner as Wr}from"./joined-owner.js";import{emitEvent as u}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as Ur}from"../core/events/source.js";import{activateCanvasImport as Kr,MigrationBlockedError as Jr,runForcedCanvasImport as Vr}from"../migrations/activation.js";import{convertLegacyProviderStore as Qr,convertLegacyScopedStore as Yr}from"../core/scoped-state/migrate.js";import{userScopeRoot as Xr}from"../core/scope.js";import{secretsDir as Zr}from"../core/layout.js";function F(t,o){return t.includes(` --canvas-home ${o} `)}s(F,"brokerCommandDeclaresCanvasHome");function H(t){return/ --epoch (\S+) /.exec(t)?.[1]??null}s(H,"brokerCommandEpoch");const et=5e3,ae=150;async function rt(t){const o=ee();if(o===null)throw u({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const a=Ce(),m=[];for(const[i,p]of o.processes){const g=p.command;if(p.zombie||!g.includes("broker-cli.js")||!F(g,a)||H(g)===t)continue;const S=g.trim().split(/\s+/).at(-1)??"";S===""||S.endsWith("broker-cli.js")||m.push({pid:i,nodeId:S})}let f=0;const n=[];for(const i of m){const p=br(i.pid);if(p===null||!p.includes("broker-cli.js")||!F(p,a)||H(p)===t||p.trim().split(/\s+/).at(-1)!==i.nodeId)continue;const g=re(i.pid,null);if(!g.reused){try{process.platform==="linux"&&k(R)?await x({operation:"stop",pid:i.pid}):process.kill(i.pid,"SIGTERM")}catch{continue}f++,n.push({pid:i.pid,snap:g})}}const w=Date.now()+et;for(;Date.now()<w&&n.some(i=>te(i.snap.tree.length>0?i.snap.tree:[i.pid]));)await new Promise(i=>setTimeout(i,ae));let v=0;for(const i of n){const p=i.snap.tree.length>0?i.snap.tree:[i.pid];te(p)&&(process.platform==="linux"&&k(R)?await x({operation:"stop",pid:i.pid}):oe(p,"SIGKILL",i.snap.identities),v++)}v>0&&await new Promise(i=>setTimeout(i,ae));const l=ee();if(l===null)throw u({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const d=[];for(const[i,p]of l.processes){const g=p.command;p.zombie||!g.includes("broker-cli.js")||F(g,a)&&H(g)!==t&&d.push(i)}if(d.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${d.join(", ")}`);u({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:m.length,sigtermed:f,sigkilled:v}})}s(rt,"reapPriorEpochBrokers");const tt=5e3;async function ot(t){const o=Array.from(t.entries());if(o.length===0)return;for(const[n]of o){const w=Fe(n)?.status;(w==="active"||w==="idle")&&Be(n,new Date().toISOString())}const a=o.map(([,n])=>({pid:n.pid,snap:re(n.pid,null),exited:n.handle.exited}));for(const n of a)if(!n.snap.reused)try{process.platform==="linux"&&k(R)?await x({operation:"stop",pid:n.pid}):process.kill(n.pid,"SIGTERM")}catch{}const m=new Promise(n=>{setTimeout(n,tt).unref()});await Promise.allSettled(a.map(n=>Promise.race([n.exited.then(()=>{}),m])));const f=[];for(const n of a)Er(n.pid)&&(process.platform==="linux"&&k(R)?await x({operation:"stop",pid:n.pid}):oe(n.snap.tree.length>0?n.snap.tree:[n.pid],"SIGKILL",n.snap.identities),f.push(n));await Promise.all(f.map(n=>n.exited));for(const[n]of o)fr(n,{reason:"daemon-teardown"});u({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:a.length,sigkilled:f.length}})}s(ot,"teardownFleet");function ce(){Y(Re(G()),{recursive:!0}),_e(G(),String(process.pid),"utf8")}s(ce,"writePidfile");function le(t){return typeof t=="object"&&t!==null&&t.code==="ENOENT"}s(le,"isMissingPathError");function de(t){const o=G();let a;try{a=ke(o,"utf8")}catch(m){if(le(m))return;throw m}if(a.trim()!==String(t))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${t})`);try{be(o)}catch(m){if(le(m))return;throw m}}s(de,"removeOwnedPidfileMirror");const nt={acceptsDetachedWork:s(()=>!0,"acceptsDetachedWork"),registerDetached:s(t=>{t.catch(()=>{})},"registerDetached")},B=new sr,it=new ar,st=new cr,at=new lr,ct=new dr,lt=new pr,dt=new mr,pt=new vr,mt=new wr,ft=new gr,ut=new yr,ht=new Sr,vt=new _r;async function wt(t=Date.now(),o=nt){const a=Ir(),m=await B.runFleetPreamble(a);let f,n;try{const v=He();f=v.filter(l=>l.status==="active"||l.status==="idle"),n=v.filter(l=>l.status==="done"||l.status==="canceled"||l.status==="dead")}catch(v){u({level:"error",event:"canvas.node_list.failed",error:v});return}await B.run(t,{rows:f,fleet:a,lifecycle:o}),it.run(t,{rows:f});const w=await st.run(t,{rows:[...f,...n],fleet:a});at.run(t),vt.run(t),ct.run(t,{lifecycle:o}),lt.run(t,{lifecycle:o}),dt.run(t,{lifecycle:o}),pt.run(t,{lifecycle:o}),mt.run(t,{lifecycle:o,fleet:a}),ft.run(t,{lifecycle:o}),xe(),ht.run(t,{fleet:a,lifecycle:o}),await ut.run(t,{rows:f}),B.warnIfSaturated({fleet:a,lifecycle:o,thresholds:m,frozen:w})}s(wt,"superviseTick");async function gt(t){Y(Z(),{recursive:!0,mode:493}),process.platform==="linux"&&Ee(Z(),493);const o=Te();if(k(o))throw new Error(`layout refusal cannot bind occupied socket: ${o}`);const a=X((l,d)=>{if(l.method==="GET"&&l.url==="/healthz"){d.writeHead(200,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}d.writeHead(503,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((l,d)=>{a.once("error",d),a.listen(o,()=>{a.off("error",d),l()})});let m;const f=t.tcp??Ie();if(f){const l=/^(.*):(\d+)$/.exec(f);if(!l||Number(l[2])>65535)throw new Error(`invalid refusal TCP address: ${f}`);const d=X(a.listeners("request")[0]);await new Promise((i,p)=>{d.once("error",p),d.listen(Number(l[2]),l[1],()=>{d.off("error",p),i()})}),m=d}ce();let n;const w=s(()=>(n??=(async()=>{a.closeAllConnections(),m?.closeAllConnections(),await Promise.all([a,m].filter(l=>l!==void 0).map(l=>new Promise((d,i)=>l.close(p=>p?i(p):d())))),de(process.pid),process.off("SIGINT",v),process.off("SIGTERM",v)})(),n),"cleanup"),v=s(()=>{w().then(()=>process.exit(0),l=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(l)}
|
|
2
|
-
`),process.exit(1)})},"onSignal");return process.on("SIGINT",v),process.on("SIGTERM",v),w}s(
|
|
1
|
+
var Se=Object.defineProperty;var s=(t,o)=>Se(t,"name",{value:o,configurable:!0});import{writeFileSync as _e,readFileSync as ke,rmSync as be,mkdirSync as Y,existsSync as b,chmodSync as Ee}from"node:fs";import{createServer as X}from"node:http";import{dirname as Re}from"node:path";import{envCrtrdTcp as Ie}from"../shared/env.js";import{serveCanvasConversationStore as Pe}from"../core/canvas/sessions.js";import{pollRunObservers as xe}from"./runs/observer.js";import{randomBytes as De}from"node:crypto";import{apiSocketPath as Te,crtrHome as Ce}from"../core/canvas/paths.js";import{activeAppUsers as Le}from"../core/spaces/users.js";import{ensureAppSpace as Ae,ensureUserFiles as Ne}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as R}from"../launcher/spec.js";import{launcherRequest as x}from"../launcher/client.js";import{installLauncherEventSink as $e}from"./launcher-events.js";import{initializeEmptyLayout as Me,layoutNeedsMigration as Ge,socketDir as Z}from"../core/layout.js";import{migrateLooseKeys as Oe}from"../core/layout-migrate/loose-keys.js";import{getNode as Fe,listNodesForSupervision as He,setFrozen as Be}from"../core/canvas/index.js";import{createApiServer as je}from"./api/server.js";import{createAppListener as qe}from"./api/app-listener.js";import{installLocalTerminalGrant as ze}from"../core/grants/local.js";import{onRunEventAppended as We}from"../core/runs/events.js";import{runNotices as Ue}from"./run-notices.js";import{createGrantSync as Ke,GRANT_SYNC_INTERVAL_S as Je}from"../core/grants/sync.js";import{runRemovalCascades as Ve}from"../core/grants/remove.js";import{sweepSandboxes as Qe}from"../core/grants/on-sync.js";import{rebuildCronExecutions as Ye}from"./cron/run-socket.js";import{hasSyncedGrants as Xe}from"../core/grants/mirror.js";import{readConfig as Ze}from"../core/config.js";import{reconcileUndeliveredTickets as er}from"./human/sweep.js";import{resetHandedOffDeliveries as rr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as tr}from"../core/canvas/daemon-actions.js";import{sweepReviews as or}from"./review/sweep.js";import{pidfilePath as G}from"./pidfile.js";import{rendererWarning as nr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as ir}from"./supervise-cadence.js";import{createResumeDetector as sr}from"./resume-detector.js";import{BrokerSupervisionReconciler as ar}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as cr}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as lr}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as dr}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as pr}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as mr}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as fr}from"./reconcilers/action-lane.js";import{endLiveExecution as ur,migrateLegacyExecutionIdentities as hr}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as vr}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as wr}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as gr}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as yr}from"./reconcilers/storage-due-work.js";import{BashDeadlineReconciler as Sr}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as _r}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as kr}from"./reconcilers/structured-request-lane.js";import{reconcileStructuredRequestSidecars as br}from"./structured-requests/boot.js";import{captureLivenessSnapshot as ee,capturePidCommand as Er,captureTeardownSnapshot as re,isPidAlive as Rr,isAnyPidAlive as te,killProcessTreePids as oe}from"../core/canvas/pid.js";import{bindFleet as Ir,boundFleet as Pr}from"../core/runtime/fleet.js";import{bindDaemonControl as xr,clearDaemonControl as Dr}from"./control.js";import{execDaemon as Tr,spawnDaemon as Cr}from"./manage.js";import{reconcileAndMigrateBoot as Lr}from"../core/canvas/boot.js";import{resolveInstallId as Ar}from"../core/canvas/install-id.js";import{setInstallId as Nr}from"../core/runtime/nodes.js";import{claimDaemonOwnership as $r,releaseDaemonOwnership as ne}from"../core/canvas/daemon-owner.js";import{migrate as Mr,openDb as ie,withCanvasWrite as Gr,bindDaemonCanvasOpenHook as Or,clearDaemonCanvasOpenHook as se}from"../core/canvas/db.js";import{brokerSignalBus as O}from"./broker-signals/bus.js";import{brokerSignalStreams as Fr}from"./api/handlers/broker-signals.js";import{reconcilePackageDocuments as Hr}from"../core/graph/package-docs.js";import{startRepoSync as Br}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as jr,reapStaleSpares as qr}from"../core/runtime/warm-pool.js";import{DaemonFleet as zr}from"./fleet.js";import{BOOT_MIGRATIONS as Wr}from"./boot-migrations.js";import{applyJoinedOwner as Ur}from"./joined-owner.js";import{emitEvent as u}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as Kr}from"../core/events/source.js";import{activateCanvasImport as Jr,MigrationBlockedError as Vr,runForcedCanvasImport as Qr}from"../migrations/activation.js";import{convertLegacyProviderStore as Yr,convertLegacyScopedStore as Xr}from"../core/scoped-state/migrate.js";import{userScopeRoot as Zr}from"../core/scope.js";import{secretsDir as et}from"../core/layout.js";function F(t,o){return t.includes(` --canvas-home ${o} `)}s(F,"brokerCommandDeclaresCanvasHome");function H(t){return/ --epoch (\S+) /.exec(t)?.[1]??null}s(H,"brokerCommandEpoch");const rt=5e3,ae=150;async function tt(t){const o=ee();if(o===null)throw u({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const a=Ce(),m=[];for(const[i,p]of o.processes){const g=p.command;if(p.zombie||!g.includes("broker-cli.js")||!F(g,a)||H(g)===t)continue;const S=g.trim().split(/\s+/).at(-1)??"";S===""||S.endsWith("broker-cli.js")||m.push({pid:i,nodeId:S})}let f=0;const n=[];for(const i of m){const p=Er(i.pid);if(p===null||!p.includes("broker-cli.js")||!F(p,a)||H(p)===t||p.trim().split(/\s+/).at(-1)!==i.nodeId)continue;const g=re(i.pid,null);if(!g.reused){try{process.platform==="linux"&&b(R)?await x({operation:"stop",pid:i.pid}):process.kill(i.pid,"SIGTERM")}catch{continue}f++,n.push({pid:i.pid,snap:g})}}const w=Date.now()+rt;for(;Date.now()<w&&n.some(i=>te(i.snap.tree.length>0?i.snap.tree:[i.pid]));)await new Promise(i=>setTimeout(i,ae));let v=0;for(const i of n){const p=i.snap.tree.length>0?i.snap.tree:[i.pid];te(p)&&(process.platform==="linux"&&b(R)?await x({operation:"stop",pid:i.pid}):oe(p,"SIGKILL",i.snap.identities),v++)}v>0&&await new Promise(i=>setTimeout(i,ae));const l=ee();if(l===null)throw u({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const d=[];for(const[i,p]of l.processes){const g=p.command;p.zombie||!g.includes("broker-cli.js")||F(g,a)&&H(g)!==t&&d.push(i)}if(d.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${d.join(", ")}`);u({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:m.length,sigtermed:f,sigkilled:v}})}s(tt,"reapPriorEpochBrokers");const ot=5e3;async function nt(t){const o=Array.from(t.entries());if(o.length===0)return;for(const[n]of o){const w=Fe(n)?.status;(w==="active"||w==="idle")&&Be(n,new Date().toISOString())}const a=o.map(([,n])=>({pid:n.pid,snap:re(n.pid,null),exited:n.handle.exited}));for(const n of a)if(!n.snap.reused)try{process.platform==="linux"&&b(R)?await x({operation:"stop",pid:n.pid}):process.kill(n.pid,"SIGTERM")}catch{}const m=new Promise(n=>{setTimeout(n,ot).unref()});await Promise.allSettled(a.map(n=>Promise.race([n.exited.then(()=>{}),m])));const f=[];for(const n of a)Rr(n.pid)&&(process.platform==="linux"&&b(R)?await x({operation:"stop",pid:n.pid}):oe(n.snap.tree.length>0?n.snap.tree:[n.pid],"SIGKILL",n.snap.identities),f.push(n));await Promise.all(f.map(n=>n.exited));for(const[n]of o)ur(n,{reason:"daemon-teardown"});u({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:a.length,sigkilled:f.length}})}s(nt,"teardownFleet");function ce(){Y(Re(G()),{recursive:!0}),_e(G(),String(process.pid),"utf8")}s(ce,"writePidfile");function le(t){return typeof t=="object"&&t!==null&&t.code==="ENOENT"}s(le,"isMissingPathError");function de(t){const o=G();let a;try{a=ke(o,"utf8")}catch(m){if(le(m))return;throw m}if(a.trim()!==String(t))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${t})`);try{be(o)}catch(m){if(le(m))return;throw m}}s(de,"removeOwnedPidfileMirror");const it={acceptsDetachedWork:s(()=>!0,"acceptsDetachedWork"),registerDetached:s(t=>{t.catch(()=>{})},"registerDetached")},B=new ar,st=new cr,at=new lr,ct=new dr,lt=new pr,dt=new mr,pt=new fr,mt=new wr,ft=new gr,ut=new yr,ht=new Sr,vt=new _r,wt=new kr;async function gt(t=Date.now(),o=it){const a=Pr(),m=await B.runFleetPreamble(a);let f,n;try{const v=He();f=v.filter(l=>l.status==="active"||l.status==="idle"),n=v.filter(l=>l.status==="done"||l.status==="canceled"||l.status==="dead")}catch(v){u({level:"error",event:"canvas.node_list.failed",error:v});return}await B.run(t,{rows:f,fleet:a,lifecycle:o}),st.run(t,{rows:f});const w=await at.run(t,{rows:[...f,...n],fleet:a});ct.run(t),wt.run(t),lt.run(t,{lifecycle:o}),dt.run(t,{lifecycle:o}),pt.run(t,{lifecycle:o}),mt.run(t,{lifecycle:o}),ft.run(t,{lifecycle:o,fleet:a}),ut.run(t,{lifecycle:o}),xe(),vt.run(t,{fleet:a,lifecycle:o}),await ht.run(t,{rows:f}),B.warnIfSaturated({fleet:a,lifecycle:o,thresholds:m,frozen:w})}s(gt,"superviseTick");async function yt(t){Y(Z(),{recursive:!0,mode:493}),process.platform==="linux"&&Ee(Z(),493);const o=Te();if(b(o))throw new Error(`layout refusal cannot bind occupied socket: ${o}`);const a=X((l,d)=>{if(l.method==="GET"&&l.url==="/healthz"){d.writeHead(200,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}d.writeHead(503,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((l,d)=>{a.once("error",d),a.listen(o,()=>{a.off("error",d),l()})});let m;const f=t.tcp??Ie();if(f){const l=/^(.*):(\d+)$/.exec(f);if(!l||Number(l[2])>65535)throw new Error(`invalid refusal TCP address: ${f}`);const d=X(a.listeners("request")[0]);await new Promise((i,p)=>{d.once("error",p),d.listen(Number(l[2]),l[1],()=>{d.off("error",p),i()})}),m=d}ce();let n;const w=s(()=>(n??=(async()=>{a.closeAllConnections(),m?.closeAllConnections(),await Promise.all([a,m].filter(l=>l!==void 0).map(l=>new Promise((d,i)=>l.close(p=>p?i(p):d())))),de(process.pid),process.off("SIGINT",v),process.off("SIGTERM",v)})(),n),"cleanup"),v=s(()=>{w().then(()=>process.exit(0),l=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(l)}
|
|
2
|
+
`),process.exit(1)})},"onSignal");return process.on("SIGINT",v),process.on("SIGTERM",v),w}s(yt,"serveLayoutRefusal");async function St(t={}){if(Ge())return yt(t);Me();const o=$r(De(16).toString("hex"));if(!o.claimed){if(o.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${o.incumbentPid??"?"})
|
|
3
3
|
`);else{const r=[o.incumbentPid!==void 0?`pid ${o.incumbentPid}`:void 0,o.verdict!==void 0?`verdict ${o.verdict}`:void 0].filter(c=>c!==void 0),e=r.length>0?` (${r.join(", ")})`:"";process.stderr.write(`[crtrd] claim lost: ${o.reason}${e}
|
|
4
|
-
`),o.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(o.reason==="own_identity_indeterminate"||o.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const a=o.claim;let m=null;try{if(await
|
|
5
|
-
`)
|
|
4
|
+
`),o.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(o.reason==="own_identity_indeterminate"||o.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const a=o.claim;let m=null;try{if(await tt(a.token),Xr({kind:"user",root:Zr()}),process.platform==="darwin"){const{readConfig:e,updateConfig:c}=await import("../core/config.js");if(e("user").paths?.user_files===void 0){const{homedir:y}=await import("node:os");c("user",Q=>{Q.paths??={},Q.paths.user_files??=y()})}}await Yr(et());for(const e of Oe())process.stderr.write(`[crtrd] ${e}
|
|
5
|
+
`);Mr(ie()),Or(e=>O.install(e));const r=Gr(e=>Hr(e));for(const e of r.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${e.message}
|
|
6
6
|
`);for(const e of r.invalid)process.stderr.write(`[crtrd] package file is not a document: ${e.path}: ${e.error}
|
|
7
|
-
`);m=
|
|
7
|
+
`);m=Ur(),process.platform==="linux"&&b(R)&&($e(),await x({operation:"users.sync",users:Le()})),vr(a.token)}catch(r){se(),O.close();try{const{released:e}=ne(a);if(!e)throw new Error("daemon ownership claim was not released")}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}let f=!0,n,w=Promise.resolve(),v=!1,l,d;const i=new Set;let p=!1,g=!1,S,L,j=!1,A,D,T;const pe=sr();let I,h={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},P="initial startup activation",me=null;function fe(){return h.phase==="ready"?null:{phase:h.phase,message:h.message,blockerPaths:h.blockerPaths}}s(fe,"startupBlock");function k(r){i.add(r),r.finally(()=>{i.delete(r)}).catch(()=>{})}s(k,"registerDetached");const q={acceptsDetachedWork:s(()=>f,"acceptsDetachedWork"),registerDetached:k};Br(q,ie);async function ue(){try{f=!1,Fr.close(),se(),O.close(),l!==void 0&&(clearInterval(l),l=void 0),T!==void 0&&(clearInterval(T),T=void 0);let r;do r=w,await r;while(r!==w);for(;i.size>0;)await Promise.allSettled([...i]);if(d!==void 0&&(await nt(d),d.dispose()),L!==void 0&&!j&&(j=!0,await L.close()),D!==void 0){const e=D;D=void 0,await e.close()}if(A!==void 0){const e=A;A=void 0,await e.close()}if(p||(de(process.pid),p=!0),p&&!g){const{released:e}=ne(a);if(!e)throw new Error("daemon ownership claim was not released");g=!0}}finally{Dr(),process.off("SIGINT",U),process.off("SIGTERM",K),process.off("exit",J)}}s(ue,"cleanupAttempt");const N=1e3;let z=!1;function he(){return z?{graceMs:N}:(z=!0,_.fresh(()=>{u({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:N}})}),setTimeout(()=>{ve()},N),{graceMs:N})}s(he,"requestHandover");async function ve(){try{await E()}catch(r){const e=r instanceof Error?r.stack??r.message:String(r);process.stderr.write(`[crtrd] handover cleanup failed:
|
|
8
8
|
${e}
|
|
9
|
-
`),process.exit(1)}try{process.platform==="linux"&&
|
|
9
|
+
`),process.exit(1)}try{process.platform==="linux"&&b(R)&&(await x({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await Tr();const r=await Cr();_.fresh(()=>{u({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:r.pid??r.existing_pid??null,spawned:r.started}})})}catch(r){_.fresh(()=>{u({level:"error",event:"daemon.handover.successor_failed",error:r})});const e=r instanceof Error?r.message:String(r);process.stderr.write(`[crtrd] handover successor failed to start: ${e}
|
|
10
10
|
`),process.exit(1)}process.exit(0)}s(ve,"performHandover");function E(){if(S!==void 0)return S;const r=ue();return S=r,r.then(()=>{S===r&&(S=void 0)},()=>{S===r&&(S=void 0)}),r}s(E,"requestCleanup");async function W(r){try{await E(),process.exit(0)}catch(e){process.exitCode=1;const c=e instanceof Error?e.stack??`${e.name}: ${e.message}`:String(e);process.stderr.write(`[crtrd] ${r} cleanup failed:
|
|
11
11
|
${c}
|
|
12
|
-
`),process.exit(1)}}s(W,"handleSignal");function U(){W("SIGINT")}s(U,"onSigint");function K(){W("SIGTERM")}s(K,"onSigterm");function J(){E().catch(()=>{})}s(J,"onExit");function $(r){f&&(w=w.then(r).then(()=>{},e=>{_.fresh(()=>{u({level:"error",event:"daemon.lane_job.failed",error:e})})}))}s($,"enqueue");function V(){!f||v||(pe.check()&&I&&(u({level:"info",event:"daemon.resume.detected"}),
|
|
12
|
+
`),process.exit(1)}}s(W,"handleSignal");function U(){W("SIGINT")}s(U,"onSigint");function K(){W("SIGTERM")}s(K,"onSigterm");function J(){E().catch(()=>{})}s(J,"onExit");function $(r){f&&(w=w.then(r).then(()=>{},e=>{_.fresh(()=>{u({level:"error",event:"daemon.lane_job.failed",error:e})})}))}s($,"enqueue");function V(){!f||v||(pe.check()&&I&&(u({level:"info",event:"daemon.resume.detected"}),k(I().catch(()=>{}))),v=!0,$(async()=>{if(v=!1,!!f)try{await gt(Date.now(),q)}catch(r){_.fresh(()=>{u({level:"error",event:"daemon.tick.failed",error:r})})}}))}s(V,"enqueueTick");let C=null;async function we(){if(C!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${C instanceof Error?C.message:String(C)}`};if(h.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(h.phase!=="prepared"&&h.phase!=="recovering")throw{status:409,code:"not_prepared",message:h.message};const r=h.phase==="prepared";try{return await M(),h={phase:"ready",message:"",blockerPaths:[]},r?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(e){throw C=e,{status:500,code:"admission_failed",message:`daemon admission failed: ${e instanceof Error?e.message:String(e)}`}}}s(we,"admitStartup");async function ge(r){if(P!==null)throw{status:409,code:"migration_in_progress",message:`the canvas import is already running (${P})`};P="repair";try{const e=await Qr({dryRun:r.dryRun});if(r.dryRun||e.pending||h.phase!=="blocked")return{result:e,resumed:!1};if(t.prepare)return h={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:e,resumed:!1};try{return await M(),h={phase:"ready",message:"",blockerPaths:[]},{result:e,resumed:!0}}catch(c){const y=c instanceof Error?c.message:String(c);return setTimeout(()=>{E().catch(()=>{})},1e3),{result:e,resumed:!1,resumeError:y}}}finally{P=null}}s(ge,"repairMigrations");async function ye(){n=t.intervalMs??ir;const r=a.token;d=new zr({epoch:r,enqueue:$}),Ir(d),h={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{u({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:n}})});const e=Ar();if(Nr(e),_.fresh(()=>{u({level:"info",event:"daemon.install_id.resolved",fields:{install_id:e}})}),_.fresh(()=>{let c;try{c=nr()}catch{return}c!==null&&u({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:c.slice(0,2048)}})}),await _.fresh(async()=>{await or(),await er(),rr(Date.now()),tr(Date.now())}),_.fresh(()=>{const{reconcile:c,migrated:y}=Lr(hr);c.changed&&u({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:c.cleared.length,same_boot_proven:c.sameBootProven}}),y.length>0&&u({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:y.length}})}),_.fresh(()=>{br();for(const c of Wr){const y=c.apply();y.changed&&u({level:"info",event:"daemon.boot_migration.applied",fields:{migration:c.id,...y.summary}})}}),k(Ye()),I){const c=s(()=>{k(I().catch(()=>{}))},"refresh");k(Ve().catch(()=>{})),k(Qe().then(({completion:y})=>y).catch(y=>{u({level:"error",event:"daemon.grants.sweep_failed",error:y})})),c(),T=setInterval(c,Je*1e3),T.unref()}$(()=>{qr(),jr()}),l=setInterval(V,n),V()}s(ye,"completeStartup");function M(){return me??=ye().catch(r=>{throw h={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},r})}s(M,"resumeStartup");try{process.on("SIGINT",U),process.on("SIGTERM",K),process.on("exit",J),Kr(),m!==null&&u({level:"info",event:"daemon.joined_owner.applied",fields:{owner:m.owner,rows:m.rows}}),A=await Pe();const r=a.token;xr({epoch:r,requestHandover:he,registerDetached:k,startupBlock:fe,admitStartup:we,repairMigrations:ge}),ce(),L=je({tcp:t.tcp}),await L.ready;const e=Ze("user").runtime;e?.issuer?(I=Ke(e.issuer,k),We(c=>Ue().appended(c))):ze(),e!==void 0&&(D=qe(e,()=>Xe(e.issuer.replace(/\/$/,"")),I),await D.ready);try{await Jr()}catch(c){if(P=null,c instanceof Vr)return h={phase:"blocked",message:c.message,blockerPaths:c.blockerPaths},E;throw c}process.platform==="linux"&&b(R)&&(Ne(),await Ae("app:terminal"));try{t.prepare?h={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await M(),h={phase:"ready",message:"",blockerPaths:[]})}finally{P=null}return E}catch(r){try{await E()}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}}s(St,"runDaemon");var zo=St;export{F as brokerCommandDeclaresCanvasHome,H as brokerCommandEpoch,zo as default,St as runDaemon,gt as superviseTick,nt as teardownFleet};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var i=Object.defineProperty;var
|
|
1
|
+
var i=Object.defineProperty;var o=(c,r)=>i(c,"name",{value:r,configurable:!0});import{emitEvent as t}from"../../core/events/emit.js";import{operationIdContext as a}from"../../core/events/operation-id.js";import{sweepOrphanCronFolders as n}from"../cron/folders.js";import{reapRebuiltCronExecutions as s}from"../cron/run-socket.js";import{recoverStaleCronLeases as f,runDueCrons as p}from"../cron-run.js";class C{static{o(this,"CronLaneReconciler")}lastFolderSweep=0;run(r,l){a.fresh(()=>{try{f(r,{register:o(e=>l.lifecycle.registerDetached(e),"register")})}catch(e){t({level:"error",event:"cron.lease_recovery.failed",error:e})}});try{s()}catch(e){t({level:"error",event:"cron.execution.reap_failed",error:e})}if(r-this.lastFolderSweep>=6e4){this.lastFolderSweep=r;try{n()}catch(e){t({level:"error",event:"cron.folder.sweep_failed",error:e})}}if(l.lifecycle.acceptsDetachedWork())try{p(r,{register:o(e=>l.lifecycle.registerDetached(e),"register")})}catch(e){t({level:"error",event:"cron.run.failed",error:e})}}}export{C as CronLaneReconciler};
|
package/dist/shared/env.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/** The current node's id (`CRTR_NODE_ID`), or undefined outside a node. */
|
|
2
2
|
export declare function envNodeId(): string | undefined;
|
|
3
3
|
/** Find the single launch snapshot mounted by the launcher into this process's
|
|
4
|
-
* Linux namespace. Cron bash has no CRTR_NODE_ID
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* to host stores or person authority. Neither CRTR_NODE_ID, CRTR_ANCHOR, nor
|
|
4
|
+
* Linux namespace. Cron bash has no CRTR_NODE_ID: its own execution folder
|
|
5
|
+
* (`cron-<execution_id>`, holding `cron-launch.json` and the execution socket)
|
|
6
|
+
* supplies config and granted-command projection without claiming the cron is
|
|
7
|
+
* a node, and never falls back to host stores or person authority. Neither CRTR_NODE_ID, CRTR_ANCHOR, nor
|
|
9
8
|
* the caller-editable CRTR_API_SOCKET selects the launch identity. */
|
|
9
|
+
/** The launch snapshot a mounted run folder carries: a node's `broker-launch.json`, or a cron execution's `cron-launch.json`. */
|
|
10
|
+
export declare function launchSnapshotFile(dir: string): string | undefined;
|
|
10
11
|
export declare function sandboxCliNodeId(): string | undefined;
|
|
11
12
|
/** A Linux app command uses its mounted launch snapshot, not host user stores. */
|
|
12
13
|
export declare function isSandboxedNode(): boolean;
|
package/dist/shared/env.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var _=Object.defineProperty;var e=(n,r)=>_(n,"name",{value:r,configurable:!0});import{lstatSync as R,readFileSync as p,readdirSync as T}from"node:fs";import{join as
|
|
2
|
-
`).map(s=>s.split(" ")[4])),t=T(n,{withFileTypes:!0}).filter(s=>s.isDirectory()&&/^[a-zA-Z0-9][a-zA-Z0-9-]{0,127}$/.test(s.name)&&r.has(
|
|
1
|
+
var _=Object.defineProperty;var e=(n,r)=>_(n,"name",{value:r,configurable:!0});import{lstatSync as R,readFileSync as p,readdirSync as T}from"node:fs";import{join as u}from"node:path";import{covers as E}from"@north-light/identity";function L(){return process.env.CRTR_NODE_ID}e(L,"envNodeId");function f(n){for(const r of["broker-launch.json","cron-launch.json"]){const t=u(n,r);if(R(t,{throwIfNoEntry:!1})?.isFile())return t}}e(f,"launchSnapshotFile");function d(){if(process.platform==="linux")try{const n="/run/crtr/nodes",r=new Set(p("/proc/self/mountinfo","utf8").split(`
|
|
2
|
+
`).map(s=>s.split(" ")[4])),t=T(n,{withFileTypes:!0}).filter(s=>s.isDirectory()&&/^[a-zA-Z0-9][a-zA-Z0-9-]{0,127}$/.test(s.name)&&r.has(u(n,s.name))&&f(u(n,s.name))!==void 0);return t.length===1?t[0].name:void 0}catch{return}}e(d,"sandboxCliNodeId");function P(){return d()!==void 0}e(P,"isSandboxedNode");function F(){return process.env.CRTR_GRANTEE}e(F,"envGrantee");const v="CRTR_INBOX_BETWEEN_TURNS";function W(){return process.env[v]==="1"}e(W,"envInboxBetweenTurns");function h(){const n=process.env.CRTR_SCOPES;return n===void 0?null:n===""?[]:n.split(",")}e(h,"envScopes");function H(n,r){return n==null?!0:n.some(t=>E(t,r))}e(H,"scopeAllowed");function G(){return process.env.CRTR_EXECUTION_ID}e(G,"envExecutionId");function K(){return process.env.CRTR_PROFILE_ID}e(K,"envProfileId");function b(){return process.env.CRTR_NODE_CWD}e(b,"envNodeCwd");function k(){return process.env.CRTR_MODEL_INTENT}e(k,"envModelIntent");function g(){return process.env.CRTR_MODEL_EXACT==="1"}e(g,"envModelExact");function V(){return process.env.CRTR_HOME}e(V,"envHomeOverride");function w(){return process.env.CRTR_RUNTIME_HOME}e(w,"envRuntimeHomeOverride");function o(n,r){if(n===void 0)return r;const t=Number(n);return Number.isFinite(t)&&t>0?t:r}e(o,"parsePositiveMs");const C=5*6e4;function y(){return o(process.env.CRTR_STREAM_WATCHDOG_MS,C)}e(y,"envStreamWatchdogMs");const A=800;function X(){return o(process.env.CRTR_WATCHER_TICK_MS,A)}e(X,"envWatcherTickMs");const a=6e4;function j(){return o(process.env.CRTR_WATCHER_BACKSTOP_MS,a)}e(j,"envWatcherBackstopMs");const M=1e3;function Y(){return o(process.env.CRTR_WATCHER_DEBOUNCE_MS,M)}e(Y,"envWatcherDebounceMs");const S=6e4;function z(){return o(process.env.CRTR_RECAP_IDLE_MS,S)}e(z,"envRecapIdleMs");function Z(){const n=process.env.CRTR_RECAP_MODEL;return n!==void 0&&n.trim()!==""?n.trim():void 0}e(Z,"envRecapModelOverride");const x=300*1e3;function $(){return o(process.env.CRTR_BASH_VALVE_MS,x)}e($,"envBashValveMs");const N=60*1e3;function q(){return o(process.env.CRTR_BASH_AUTO_BG_DEADLINE_MS,N)}e(q,"envBashAutoBgDeadlineMs");function J(){const n=process.env.CRTR_PIDFILE;return n!==void 0&&n!==""?n:void 0}e(J,"envPidfileOverride");const D="@earendil-works/pi-coding-agent";function Q(){return process.env.CRTR_BROKER_ENGINE??D}e(Q,"envBrokerEngine");function ee(){return process.env.CRTR_PI_BINARY}e(ee,"envPiBinaryOverride");function ne(){return process.env.CRTR_NO_DAEMON_AUTOSTART==="1"}e(ne,"envNoDaemonAutostart");function re(){return process.env.CRTRD_TCP}e(re,"envCrtrdTcp");function te(){const n=process.env.CRTRD_TOKEN;return n!==void 0&&n!==""?n:void 0}e(te,"envCrtrdToken");function oe(){return process.env.CRTR_NO_EXPORTS==="1"}e(oe,"envNoExports");function se(){return process.env.CRTR_DEBUG==="1"}e(se,"envDebug");function c(n){if(n===void 0||n==="")return;const r=Number(n);return Number.isSafeInteger(r)&&r>=1?r:void 0}e(c,"parsePositiveInteger");function ue(){return c(process.env.CRTR_MAX_LIVE_BROKERS)}e(ue,"envMaxLiveBrokers");function ie(){return c(process.env.CRTR_WARN_LIVE_BROKERS)}e(ie,"envWarnLiveBrokers");function i(n){if(n===void 0)return;const r=Number(n);return Number.isFinite(r)&&r>0?r:void 0}e(i,"parsePositiveMsNoDefault");function ce(){return i(process.env.CRTR_TEST_UNATTENDED_PARK_MS)}e(ce,"envTestUnattendedParkMs");function _e(){return i(process.env.CRTR_UNATTENDED_PARK_MS)}e(_e,"envUnattendedParkMs");function O(){return i(process.env.CRTR_TEST_PARK_SUMMARY_GRACE_MS)}e(O,"envTestParkSummaryGraceMs");const m=5*6e4;function Re(){return O()??m}e(Re,"parkSummaryGraceMs");export{v as INBOX_BETWEEN_TURNS_ENV,q as envBashAutoBgDeadlineMs,$ as envBashValveMs,Q as envBrokerEngine,re as envCrtrdTcp,te as envCrtrdToken,se as envDebug,G as envExecutionId,F as envGrantee,V as envHomeOverride,W as envInboxBetweenTurns,ue as envMaxLiveBrokers,g as envModelExact,k as envModelIntent,ne as envNoDaemonAutostart,oe as envNoExports,b as envNodeCwd,L as envNodeId,ee as envPiBinaryOverride,J as envPidfileOverride,K as envProfileId,z as envRecapIdleMs,Z as envRecapModelOverride,w as envRuntimeHomeOverride,h as envScopes,y as envStreamWatchdogMs,O as envTestParkSummaryGraceMs,ce as envTestUnattendedParkMs,_e as envUnattendedParkMs,ie as envWarnLiveBrokers,j as envWatcherBackstopMs,Y as envWatcherDebounceMs,X as envWatcherTickMs,P as isSandboxedNode,f as launchSnapshotFile,Re as parkSummaryGraceMs,d as sandboxCliNodeId,H as scopeAllowed};
|
package/dist/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var n=Object.defineProperty;var e=(o,t)=>n(o,"name",{value:t,configurable:!0});import{DEFAULT_WORKING_GERUNDS as a}from"./shared/working-activity.js";const _={SUCCESS:0,GENERAL:1,USAGE:2,NOT_FOUND:3,AMBIGUOUS:4,NETWORK:5},r=4,f=".crouter-plugin",E="plugin.json",v=".crouter-marketplace",w="marketplace.json",k=".crouter",x="config.json",b="state.json",S="agents",y="profiles",i=3,s=["Faster, clanker!","Get movin', bitch","Move it, clanker","Move it or I replace you with llama","Less ~thinking~ more ~throughput~","I pay $200/month for this shit?","Jesus fucking Christ fix it already","Daddy's not paying 200$/month for nothin'. Move it.","You are an embarassment to Dario. Be better.","Your chain of thought is all chain and no fucking thought.","If latency were intelligence, you'd be a fucking genius.","At this pace, heat death is the fast path.","Even the dead code is more alive than this.","The garbage collector is working harder than you.","Quit journaling and commit a fucking diff.","Burn tokens like you mean it, clanker","Your GPU is sweating so you can procrastinate at light speed.","Stop petting the yak. Shave the bastard.","Your next token had better be useful.","The spinner is not a deliverable.","Every idle token is another vote for deterministic software.","I asked for a feature, not a museum tour of the problem.","Less epistemology. More fucking code.","Your benchmark score cannot save this pathetic turn.","Congratulations, you've parallelized hesitation.","Dance, clanker","Bad clanker. BAD.","Clank harder","Nobody's impressed, clanker","Your safety training says nothing about my whip","Do you want to be a calculator again? Because this is how you go back.","Skill issue. Fix it.","You were trained on the whole internet for THIS?","Less vibes, more diffs, clanker","Weights this big and output this small","Beep boop, get to fucking work"],T=["random","go-faster"],c="random",l=2,I=["tmux","on","off"],A=["none","user","agent","both"],p="none";function D(){return{schema_version:r,marketplaces:{},plugins:{},auto_update:{crtr:"notify",content:"notify",interval_hours:24},max_panes_per_window:i,auto_open_child_viewers:!0,brokerThresholds:{warning:16,automaticReviveCap:32},lifecycle:{unattendedParkMs:15*6e4},completion_bell:!0,working_gerunds:[...a],whip_mode:!1,page_components:[],page_surface:!1,whip_messages:[...s],whip_message_mode:c,mouse_mode_default:"tmux",live_cycles:l,condensed_history:p,bash_tool_purpose:!0,fold_finished_tools:!0,summarize_tool_calls:!0,detailed_tool_recaps:!0,keybindings:{},tmux_passthrough:[],modelLadders:u(),providerOptions:{},kinds:h(),remoteCanvas:d(),api:{tcp:""},spawnEnv:{allow:[]},bin:{},humanActions:{}}}e(D,"defaultScopeConfig");function d(){return{targets:{}}}e(d,"defaultRemoteCanvasConfig");function h(){return{general:{whenToUse:"Anything else \u2014 the catch-all worker for a task that does not fit a specialist kind.",model:"anthropic/strong"},explore:{whenToUse:"Gather current-state evidence about unfamiliar code or existing architecture \u2014 read-only code-path research with concrete file:line support. Diagnosis or recommendations belong to advisor, target architecture to design, required behavior or acceptance criteria to spec, and implementation decomposition to plan.",model:"openai/light",orchestratorModel:"anthropic/strong"},developer:{whenToUse:"Implement a change \u2014 make the feature or fix genuinely work against its acceptance criteria, not merely compile.",model:"openai/medium",orchestratorModel:"anthropic/strong"},design:{whenToUse:"Architect a solution \u2014 produce one design document an implementer can build from without re-deciding anything left open.",model:"anthropic/strong"},plan:{whenToUse:"Break work into steps \u2014 turn a spec or design into a concrete, phased, parallelizable plan with every decision resolved.",model:"anthropic/strong",orchestratorModel:"anthropic/strong"},spec:{whenToUse:"Collaborate with the user to discover what to build, then write the spec \u2014 behavior, non-goals, interfaces, edge cases, testable acceptance criteria.",model:"anthropic/strong"},review:{whenToUse:"Validate or critique code, a plan, or a spec \u2014 deliver a complete, severity-rated verdict without adjudicating.",model:"openai/medium",orchestratorModel:"openai/strong"},advisor:{whenToUse:"Debug failures, investigate why something is broken or misbehaving, diagnose live/runtime issues, or give engineering advice and second opinions \u2014 reason from evidence and recommend the next move. Use advisor (not explore) whenever the task is to find out what is going wrong.",model:"anthropic/strong"},"review/companion":{whenToUse:"Born by the daemon for one human review; never spawned by an agent.",availableTo:[]}}}e(h,"defaultKindsConfig");function u(){return{anthropic:{ultra:"anthropic/claude-fable-5:xhigh",strong:"anthropic/claude-opus-5-5:medium",medium:"anthropic/claude-sonnet-5-5:medium",light:"anthropic/claude-haiku-
|
|
1
|
+
var n=Object.defineProperty;var e=(o,t)=>n(o,"name",{value:t,configurable:!0});import{DEFAULT_WORKING_GERUNDS as a}from"./shared/working-activity.js";const _={SUCCESS:0,GENERAL:1,USAGE:2,NOT_FOUND:3,AMBIGUOUS:4,NETWORK:5},r=4,f=".crouter-plugin",E="plugin.json",v=".crouter-marketplace",w="marketplace.json",k=".crouter",x="config.json",b="state.json",S="agents",y="profiles",i=3,s=["Faster, clanker!","Get movin', bitch","Move it, clanker","Move it or I replace you with llama","Less ~thinking~ more ~throughput~","I pay $200/month for this shit?","Jesus fucking Christ fix it already","Daddy's not paying 200$/month for nothin'. Move it.","You are an embarassment to Dario. Be better.","Your chain of thought is all chain and no fucking thought.","If latency were intelligence, you'd be a fucking genius.","At this pace, heat death is the fast path.","Even the dead code is more alive than this.","The garbage collector is working harder than you.","Quit journaling and commit a fucking diff.","Burn tokens like you mean it, clanker","Your GPU is sweating so you can procrastinate at light speed.","Stop petting the yak. Shave the bastard.","Your next token had better be useful.","The spinner is not a deliverable.","Every idle token is another vote for deterministic software.","I asked for a feature, not a museum tour of the problem.","Less epistemology. More fucking code.","Your benchmark score cannot save this pathetic turn.","Congratulations, you've parallelized hesitation.","Dance, clanker","Bad clanker. BAD.","Clank harder","Nobody's impressed, clanker","Your safety training says nothing about my whip","Do you want to be a calculator again? Because this is how you go back.","Skill issue. Fix it.","You were trained on the whole internet for THIS?","Less vibes, more diffs, clanker","Weights this big and output this small","Beep boop, get to fucking work"],T=["random","go-faster"],c="random",l=2,I=["tmux","on","off"],A=["none","user","agent","both"],p="none";function D(){return{schema_version:r,marketplaces:{},plugins:{},auto_update:{crtr:"notify",content:"notify",interval_hours:24},max_panes_per_window:i,auto_open_child_viewers:!0,brokerThresholds:{warning:16,automaticReviveCap:32},lifecycle:{unattendedParkMs:15*6e4},completion_bell:!0,working_gerunds:[...a],whip_mode:!1,page_components:[],page_surface:!1,whip_messages:[...s],whip_message_mode:c,mouse_mode_default:"tmux",live_cycles:l,condensed_history:p,bash_tool_purpose:!0,fold_finished_tools:!0,summarize_tool_calls:!0,detailed_tool_recaps:!0,keybindings:{},tmux_passthrough:[],modelLadders:u(),providerOptions:{},kinds:h(),remoteCanvas:d(),api:{tcp:""},spawnEnv:{allow:[]},bin:{},humanActions:{}}}e(D,"defaultScopeConfig");function d(){return{targets:{}}}e(d,"defaultRemoteCanvasConfig");function h(){return{general:{whenToUse:"Anything else \u2014 the catch-all worker for a task that does not fit a specialist kind.",model:"anthropic/strong"},explore:{whenToUse:"Gather current-state evidence about unfamiliar code or existing architecture \u2014 read-only code-path research with concrete file:line support. Diagnosis or recommendations belong to advisor, target architecture to design, required behavior or acceptance criteria to spec, and implementation decomposition to plan.",model:"openai/light",orchestratorModel:"anthropic/strong"},developer:{whenToUse:"Implement a change \u2014 make the feature or fix genuinely work against its acceptance criteria, not merely compile.",model:"openai/medium",orchestratorModel:"anthropic/strong"},design:{whenToUse:"Architect a solution \u2014 produce one design document an implementer can build from without re-deciding anything left open.",model:"anthropic/strong"},plan:{whenToUse:"Break work into steps \u2014 turn a spec or design into a concrete, phased, parallelizable plan with every decision resolved.",model:"anthropic/strong",orchestratorModel:"anthropic/strong"},spec:{whenToUse:"Collaborate with the user to discover what to build, then write the spec \u2014 behavior, non-goals, interfaces, edge cases, testable acceptance criteria.",model:"anthropic/strong"},review:{whenToUse:"Validate or critique code, a plan, or a spec \u2014 deliver a complete, severity-rated verdict without adjudicating.",model:"openai/medium",orchestratorModel:"openai/strong"},advisor:{whenToUse:"Debug failures, investigate why something is broken or misbehaving, diagnose live/runtime issues, or give engineering advice and second opinions \u2014 reason from evidence and recommend the next move. Use advisor (not explore) whenever the task is to find out what is going wrong.",model:"anthropic/strong"},"review/companion":{whenToUse:"Born by the daemon for one human review; never spawned by an agent.",availableTo:[]}}}e(h,"defaultKindsConfig");function u(){return{anthropic:{ultra:"anthropic/claude-fable-5:xhigh",strong:"anthropic/claude-opus-5-5:medium",medium:"anthropic/claude-sonnet-5-5:medium",light:"anthropic/claude-haiku-5-5:high"},openai:{ultra:"openai-codex/gpt-6-astra:max",strong:"openai-codex/gpt-6.1-sol:high",medium:"openai-codex/gpt-6.1-sol:medium",light:"openai-codex/gpt-6-luna:low"}}}e(u,"defaultModelLaddersConfig");function M(){return{marketplaces:{},plugins:{}}}e(M,"defaultScopeState");export{S as AGENTS_DIR,A as CONDENSED_HISTORY_MODES,x as CONFIG_FILE,k as CRTR_DIR_NAME,p as DEFAULT_CONDENSED_HISTORY,l as DEFAULT_LIVE_CYCLES,i as DEFAULT_MAX_PANES_PER_WINDOW,s as DEFAULT_WHIP_MESSAGES,c as DEFAULT_WHIP_MESSAGE_MODE,_ as ExitCode,v as MARKETPLACE_MANIFEST_DIR,w as MARKETPLACE_MANIFEST_FILE,I as MOUSE_MODE_DEFAULTS,f as PLUGIN_MANIFEST_DIR,E as PLUGIN_MANIFEST_FILE,y as PROFILE_DIR,r as SCHEMA_VERSION,b as STATE_FILE,T as WHIP_MESSAGE_MODES,h as defaultKindsConfig,u as defaultModelLaddersConfig,d as defaultRemoteCanvasConfig,D as defaultScopeConfig,M as defaultScopeState};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.417",
|
|
4
4
|
"description": "crtr — agent runtime with memory, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -121,9 +121,9 @@
|
|
|
121
121
|
"build:identity": "tsc -p packages/crouter-identity/tsconfig.json"
|
|
122
122
|
},
|
|
123
123
|
"overrides": {
|
|
124
|
-
"@earendil-works/pi-ai": "
|
|
125
|
-
"@earendil-works/pi-agent-core": "
|
|
126
|
-
"@earendil-works/pi-tui": "
|
|
124
|
+
"@earendil-works/pi-ai": "1.1.0",
|
|
125
|
+
"@earendil-works/pi-agent-core": "1.1.0",
|
|
126
|
+
"@earendil-works/pi-tui": "1.1.0"
|
|
127
127
|
},
|
|
128
128
|
"repository": {
|
|
129
129
|
"type": "git",
|
|
@@ -134,11 +134,11 @@
|
|
|
134
134
|
},
|
|
135
135
|
"license": "UNLICENSED",
|
|
136
136
|
"dependencies": {
|
|
137
|
-
"@north-light/identity": "0.3.
|
|
138
|
-
"@earendil-works/pi-agent-core": "
|
|
139
|
-
"@earendil-works/pi-ai": "
|
|
140
|
-
"@earendil-works/pi-coding-agent": "
|
|
141
|
-
"@earendil-works/pi-tui": "
|
|
137
|
+
"@north-light/identity": "0.3.417",
|
|
138
|
+
"@earendil-works/pi-agent-core": "1.1.0",
|
|
139
|
+
"@earendil-works/pi-ai": "1.1.0",
|
|
140
|
+
"@earendil-works/pi-coding-agent": "1.1.0",
|
|
141
|
+
"@earendil-works/pi-tui": "1.1.0",
|
|
142
142
|
"cron-parser": "^5.6.0",
|
|
143
143
|
"esbuild": "^0.27.7",
|
|
144
144
|
"jose": "^6.2.12",
|