@north-light/cli 0.3.574 → 0.3.575
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/providers/bundles.js +2 -2
- package/dist/core/providers/files.js +1 -1
- package/dist/core/runs/operations.js +1 -1
- package/dist/core/runtime/host.js +3 -3
- package/dist/core/runtime/spawn.js +2 -2
- package/dist/core/runtime/view-socket.d.ts +24 -3
- package/dist/core/runtime/view-socket.js +2 -2
- package/dist/core/storage/client.js +2 -2
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/cron/narrowed.d.ts +2 -2
- package/dist/daemon/cron/narrowed.js +2 -2
- package/dist/daemon/cron/settle.js +1 -1
- package/dist/launcher/run.js +2 -2
- package/dist/launcher/spec.d.ts +3 -1
- package/dist/pi-extensions/canvas-inbox-watcher.js +1 -1
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var se=Object.defineProperty;var
|
|
2
|
-
`)
|
|
1
|
+
var se=Object.defineProperty;var a=(e,n)=>se(e,"name",{value:n,configurable:!0});import{existsSync as $,readdirSync as U,readFileSync as P,rmSync as y,statSync as T}from"node:fs";import{join as d}from"node:path";import{validateCommandManifest as ue}from"../../api/command-manifest/manifest.js";import{crtrHome as W}from"../canvas/paths.js";import{withCanvasWrite as z}from"../canvas/db.js";import{bundleVersion as de,isInstalledGeneration as ce,recordCheckedGeneration as le}from"../command-plugins/bundle.js";import{readState as fe,updateConfig as pe,updateState as me}from"../config.js";import{directoryIssuer as G}from"../directory/issuer.js";import{emitEvent as m}from"../events/emit.js";import{describeOutboundFailure as ve,outboundFetch as he,outboundRefusal as L,OutboundError as ge}from"../events/outbound.js";import{withExclusiveDirectoryLockAsync as q}from"../exclusive-lock.js";import{atomicWriteText as A,ensureDir as x,pathExists as we}from"../fs-utils.js";import{reconcilePackages as Z}from"../graph/package-reconcile.js";import{readPluginManifest as j}from"../manifest.js";import{bundleSwapLockPath as V}from"../plugin-swap-lock.js";import{pluginsDir as $e,userScopeRoot as J}from"../scope.js";import{listInstalledPlugins as ye}from"../resolver.js";import{localGrants as C,providerIdsFromGrants as K}from"../grants/mirror.js";import{ProviderError as c}from"./errors.js";const be="[A-Za-z0-9_-]{1,64}",Q=new RegExp(`^${be}$`),_e=3e4,X=3e4;function v(e){return d($e("user"),e)}a(v,"pluginRoot");function D(){return d(W(),"providers")}a(D,"providersRoot");function Y(e,n){return d(D(),e,`v${n}`)}a(Y,"majorDir");function B(e){if(!Q.test(e))throw new c("not_found",`"${e}" is not a provider id`,{param:"provider"})}a(B,"assertId");function M(){const e=G();if(!e)throw new c("not_found","provider bundles need a directory-connected runtime");return e}a(M,"directoryBase");async function ee(e,n){const r=`${M()}/providers/${encodeURIComponent(e)}/plugin${n.version!==void 0?`?version=${n.version}`:""}`;let t;try{t=await he("bundle_fetch",r,{headers:{Accept:"application/x-tar",...n.ifNoneMatch!==void 0?{"If-None-Match":n.ifNoneMatch}:{}},signal:AbortSignal.timeout(_e)})}catch(i){return{status:"failed",message:`could not reach ${r}: ${i instanceof Error?i.message:String(i)}`,cause:i}}if(t.status===304){if(n.ifNoneMatch===void 0){const i=`HTTP 304 from ${r} without a conditional request`;return{status:"failed",message:i,cause:L("bundle_fetch",r,i)}}return{status:304}}if(t.status===404||t.status===410)return await t.body?.cancel().catch(()=>{}),{status:t.status===404?404:410,message:`HTTP ${t.status} from ${r}`};if(t.status<200||t.status>=300){await t.body?.cancel().catch(()=>{});{const i=`HTTP ${t.status} from ${r}`;return{status:"failed",message:i,cause:L("bundle_fetch",r,i)}}}let o;try{o=new Uint8Array(await t.arrayBuffer())}catch(i){return{status:"failed",message:`reading ${r} failed: ${i instanceof Error?i.message:String(i)}`,cause:new ge("bundle_fetch",r,i)}}const s=t.headers.get("etag");return{status:200,raw:o,...s!==null?{etag:s}:{}}}a(ee,"fetchProviderBundle");const E=new WeakSet;class I extends Error{static{a(this,"BundleInvalid")}issues;constructor(n,r){super(n),this.issues=r}}function ne(e,n,r){if(r.provider===void 0)return"the archive carries no provider block";if(r.provider.id!==e)return`the archive is provider "${r.provider.id}", not "${e}"`;if(n!==void 0&&r.provider.major!==n)return`the archive is major ${r.provider.major}, not ${n}`;const t=["hooks","kinds","page_components","memory_extensions"].filter(o=>r[o]!==void 0);if(t.length>0)return`a provider bundle may not carry ${t.join(", ")}`}a(ne,"wrongBundle");function N(e,n,r){m({level:"error",event:"daemon.providers.bundle_invalid",fields:{provider:e,...n!==void 0?{major:n}:{},...r instanceof I?{issues:r.issues}:{}},error:r})}a(N,"logInvalid");function re(e,n,r){m({level:"warn",event:"daemon.providers.bundle_fetch_failed",fields:{provider:e,...n!==void 0?{major:n}:{},message:r instanceof Error?r.message:String(r),...ve(r instanceof Error?r.cause:void 0)}})}a(re,"logFailure");async function te(){return import("../../commands/pkg/plugin-manage.js")}a(te,"pluginManage");const b=new Map,_=a(e=>`${W()}\0${e}`,"installKey");function Se(e){const n=e,r=n?.code??n?.cause?.code;return typeof r=="string"&&/^[A-Z][A-Z0-9_]{1,30}$/.test(r)?`${r} on provider install directory`:e instanceof c&&e.message.includes("HTTP 404")?"directory returned HTTP 404":e instanceof c&&e.message.includes("HTTP 410")?"directory returned HTTP 410":"provider bundle could not be installed"}a(Se,"publicInstallCause");function Pe(e){if(h(e)===void 0)return b.get(_(e))??{status:"installing"}}a(Pe,"providerInstallState");function Me(e){return new c("provider_unavailable",`provider "${e}" unavailable: ${Pe(e)?.error??"bundle installation pending"}`)}a(Me,"unavailableProviderError");function h(e){const n=j(v(e));if(!(n?.provider===void 0||n.transport?.kind!=="http"&&n.transport?.kind!=="machine")&&we(d(v(e),"commands.json"))){try{if(T(d(v(e),"commands.json")).size===0)return}catch{return}return{provider:n.provider,transport:n.transport.kind==="machine"?{kind:"machine"}:{kind:"http",endpoint:n.transport.endpoint}}}}a(h,"installedManifest");async function R(e,n,r=!1){const t=J();h(e)===void 0&&b.set(_(e),{status:"installing"});try{return x(d(t,"tmp")),await q(V(t,e),async()=>{const o=v(e),s=j(o);if(s!==null&&s.provider===void 0)throw new c("provider_unavailable",`plugin "${e}" is installed and is not the directory's provider bundle`);const i=h(e)!==void 0;if(i&&r)return"unchanged";const l=i&&n?fe("user").plugins[e]?.etag:void 0,u=await ee(e,l!==void 0?{ifNoneMatch:l}:{});if(u.status===304)return"unchanged";if(u.status===404||u.status===410)throw new c("not_found",`the directory serves no provider "${e}" (${u.message})`,{param:"provider"});if(u.status==="failed")throw new c("provider_unavailable",`provider "${e}" bundle fetch failed: ${u.message}`,{cause:u.cause});const f=de(u.raw);if(i&&ce(e,"user",o,f))return await le("user",e,u.etag,{swapped:!1}),"unchanged";const{installPluginArchive:w}=await te();try{await w(e,u.raw,{scope:"user",scopeRootPath:t,root:o,version:f,etag:u.etag,enable:!i,bundle:{endpoint:`${M()}/providers/${encodeURIComponent(e)}/plugin`},transport:{kind:"http",endpoint:M()},accept:a(p=>ne(e,void 0,p),"accept")})}catch(p){N(e,void 0,p);const F=new c("not_found",`provider "${e}" served an invalid bundle: ${p instanceof Error?p.message:String(p)}`,{param:"provider"});throw E.add(F),F}try{await z(async p=>await Z(p))}catch(p){m({level:"error",event:"daemon.providers.docs_reconcile_failed",fields:{provider:e},error:p})}return m({level:"info",event:"daemon.providers.bundle_installed",fields:{provider:e,version:f,replaced:i}}),b.delete(_(e)),i?"updated":"installed"},{timeoutMs:X,timeoutError:a(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}catch(o){throw h(e)===void 0&&b.set(_(e),{status:"install_failed",error:Se(o)}),o instanceof c?o:(m({level:"error",event:"daemon.providers.bundle_install_failed",fields:{provider:e},error:o}),Me(e))}}a(R,"refreshInstalled");async function nn(e){B(e),h(e)===void 0&&await R(e,!1,!0)}a(nn,"ensureProviderBundle");async function rn(e,n){return B(e),n.version!==void 0&&h(e)?.provider.major!==n.version?H(e,n.version,n.conditional):R(e,n.conditional)}a(rn,"refreshProviderBundle");async function oe(e,n,r){const{validatePluginArchive:t}=await te(),o=await t(r);if(o.bundle===void 0)throw new I(`provider "${e}" major ${n} bundle is invalid: ${o.issues.map(i=>i.message).join("; ")}`,o.issues);const s=ne(e,n,o.bundle);if(s!==void 0)throw new I(`provider "${e}" major ${n} bundle is invalid: ${s}`,[]);if(o.bundle.transport===void 0)throw new I(`provider "${e}" major ${n} bundle declares no transport`,[]);return o.bundle}a(oe,"validateArchive");function Ee(e){x(e),A(d(e,"retired"),`${new Date().toISOString()}
|
|
2
|
+
`),y(d(e,"bundle.tar"),{force:!0}),y(d(e,"etag"),{force:!0})}a(Ee,"retire");function ae(e,n){return new c("provider_version_retired",`provider "${e}" major ${n} is retired`,{param:"version"})}a(ae,"retiredError");async function H(e,n,r){const t=Y(e,n),o=d(t,"bundle.tar"),s=$(o),i=s&&r&&$(d(t,"etag"))?P(d(t,"etag"),"utf8"):void 0,l=await ee(e,{version:n,...i!==void 0?{ifNoneMatch:i}:{}});if(l.status===304)return"unchanged";if(l.status===410)throw Ee(t),m({level:"info",event:"daemon.providers.major_retired",fields:{provider:e,major:n}}),ae(e,n);if(l.status===404)throw new c("not_found",`provider "${e}" never served major ${n}`,{param:"version"});if(l.status==="failed")throw new c("provider_unavailable",`provider "${e}" major ${n} bundle fetch failed: ${l.message}`,{cause:l.cause});try{await oe(e,n,l.raw)}catch(f){N(e,n,f);const w=new c(s?"provider_unavailable":"not_found",f instanceof Error?f.message:String(f),{param:"version"});throw E.add(w),w}x(t),y(d(t,"retired"),{force:!0});const u=s&&Buffer.from(l.raw).equals(P(o));return u||A(o,l.raw),l.etag!==void 0?A(d(t,"etag"),l.etag):y(d(t,"etag"),{force:!0}),u?"unchanged":s?"updated":"installed"}a(H,"refreshPinned");function Ie(){const e=[],n=D();if(!$(n))return e;for(const r of U(n)){if(!Q.test(r))continue;let t;try{t=U(d(n,r))}catch{continue}for(const o of t){const s=/^v([1-9][0-9]*)$/.exec(o);s&&$(d(n,r,o,"bundle.tar"))&&e.push([r,Number(s[1])])}}return e}a(Ie,"cachedMajors");function ke(e){const n=ue(e,{transport:"http",reservedCoreNames:new Set});if(n.manifest===void 0)throw new c("not_found","the provider bundle's commands.json no longer validates");return n.manifest}a(ke,"readCommands");const S=new Map,O=new Map;function Te(e,n){const r=T(n),t=`${r.ino}:${r.mtimeMs}:${r.size}`,o=O.get(e);if(o?.stamp===t)return o.commands;const s=ke(JSON.parse(P(n,"utf8")));return O.set(e,{stamp:t,commands:s}),s}a(Te,"installedCommands");async function tn(e,n){B(e);const r=h(e);if(n===void 0||r?.provider.major===n){if(r===void 0)throw new c("not_found",`provider "${e}" has no installed bundle`,{param:"provider"});const f=v(e),w=Te(e,d(f,"commands.json"));return{id:e,major:r.provider.major,provider:r.provider,commands:w,transport:r.transport,source:"installed",root:f}}if(!Number.isInteger(n)||n<1)throw new c("invalid_request","version must be an integer of at least 1",{param:"version"});const t=Y(e,n);if($(d(t,"retired")))throw ae(e,n);const o=d(t,"bundle.tar");$(o)||await H(e,n,!1);const s=`${e}/${n}`,i=T(o),l=`${i.mtimeMs}:${i.size}`;let u=S.get(s)?.stamp===l?S.get(s).bundle:void 0;if(u===void 0){try{u=await oe(e,n,new Uint8Array(P(o)))}catch(f){throw N(e,n,f),new c("not_found",`provider "${e}" has no valid bundle for major ${n}`,{param:"version"})}S.set(s,{stamp:l,bundle:u})}return{id:e,major:n,provider:u.provider,commands:u.commands,transport:u.transport.kind==="machine"?{kind:"machine"}:{kind:"http",endpoint:u.transport.endpoint},source:"pinned"}}a(tn,"resolveProviderBundle");let g=null;const Ae=4*6e4;function k(e,n){e.stage=n,e.stageAt=Date.now()}a(k,"enter");function xe(e,n){return e?.provider?.id===n&&e.bundle?.endpoint===`${M()}/providers/${encodeURIComponent(n)}/plugin`}a(xe,"isDirectoryBundle");async function ie(e){return K(await C()).includes(e)}a(ie,"grantNames");async function je(e){await q(V(J(),e),async()=>{if(!(await ie(e)||!xe(j(v(e)),e))){y(v(e),{recursive:!0,force:!0}),await pe("user",n=>{delete n.plugins[e]}),await me("user",n=>{delete n.plugins[e]}),y(d(D(),e),{recursive:!0,force:!0}),O.delete(e),b.delete(_(e));for(const n of[...S.keys()])n.startsWith(`${e}/`)&&S.delete(n);try{await z(async n=>await Z(n))}catch(n){m({level:"error",event:"daemon.providers.docs_reconcile_failed",fields:{provider:e},error:n})}m({level:"info",event:"daemon.providers.bundle_uninstalled",fields:{provider:e}})}},{timeoutMs:X,timeoutError:a(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}a(je,"uninstallProvider");async function Ce(){if(!(await C()).every(e=>e.status!=="active")){for(const e of ye("user"))if(!(e.manifest.provider===void 0||await ie(e.name)))try{await je(e.name)}catch(n){m({level:"error",event:"daemon.providers.bundle_uninstall_failed",fields:{provider:e.name},error:n})}}}a(Ce,"uninstallDisconnected");async function De(e){if(!G())return;k(e,"reading grants");const n=K(await C());for(const r of n){k(e,`refreshing installed bundle ${r}`);try{await R(r,!0)}catch(t){t instanceof Object&&E.has(t)||re(r,void 0,t)}}k(e,"uninstalling disconnected bundles"),await Ce();for(const[r,t]of Ie()){k(e,`refreshing pinned bundle ${r} v${t}`);try{await H(r,t,!0)}catch(o){if(o instanceof c&&o.code==="provider_version_retired"||o instanceof Object&&E.has(o))continue;re(r,t,o)}}}a(De,"syncOnce");function on(){if(g!==null){g.rerun=!0;const n=Date.now()-g.stageAt;return n>=Ae&&m({level:"error",event:"daemon.providers.sync_stalled",fields:{stage:g.stage,stalled_ms:n}}),g.promise}const e={promise:Promise.resolve(),stage:"starting",stageAt:Date.now(),rerun:!1};return g=e,e.promise=(async()=>{try{do e.rerun=!1,await De(e);while(e.rerun)}finally{g=null}})(),e.promise}a(on,"syncProviderBundles");export{nn as ensureProviderBundle,Pe as providerInstallState,rn as refreshProviderBundle,tn as resolveProviderBundle,on as syncProviderBundles,Me as unavailableProviderError};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var te=Object.defineProperty;var d=(e,t)=>te(e,"name",{value:t,configurable:!0});import{createHash as oe}from"node:crypto";import{closeSync as D,constants as ne,createWriteStream as ie}from"node:fs";import{basename as re,extname as ae,isAbsolute as se,join as C}from"node:path";import{Transform as de}from"node:stream";import{pipeline as ue}from"node:stream/promises";import{appSpace as I}from"../layout.js";import{RunError as F}from"../runs/operations.js";import{createFileBeneath as le,existsBeneath as M,openBeneath as ce,removeTreeBeneath as N,setModeBeneath as fe,unlinkBeneath as pe,withDirBeneath as he}from"../spaces/open-beneath.js";import{ensureAppSpace as we}from"../spaces/space.js";import{isCloudRole as _e}from"../canvas/person-state.js";import{invalidStorageName as H,storageClient as T,StorageRefused as U,UPLOAD_MAX_BYTES as B}from"../storage/client.js";import{contentTypeFor as me,openCallerFile as ge}from"../storage/download.js";import{withCanvasWrite as ye}from"../canvas/db.js";import{deleteUploadUse as be,getUpload as $e,insertUpload as ve,insertUploadUses as Se,setUploadSize as Ue,uploadUseAt as ze}from"../storage/tables.js";import{ProviderError as u}from"./errors.js";const Ee=11e4,Ae="app:terminal";function P(e){if(e.class==="person")return e.grantee??Ae;if(!e.grantee)throw new u("server_error","provider caller has no grantee");return e.grantee}d(P,"grantee");function Ce(e){return e.class==="person"?null:{app:P(e),resolved:e.resolved??[],launched:e.class==="broker"?e.nodeScopes:null}}d(Ce,"fileCaller");function y(e){return e instanceof u?e:e instanceof U?new u("storage_unavailable",`storage refused: ${e.message}`):e instanceof F&&e.code==="storage_unavailable"?new u("storage_unavailable",e.message):e}d(y,"storageFailure");const b=d(e=>new u("provider_error",e,{origin:"provider"}),"providerError");function Fe(e,t,n){if(typeof n!="string"||!n||!se(n)||n.includes("\0"))throw new u("invalid_request",`${t} must be an absolute runtime path`,{param:t});let r;try{r=ge(Ce(e),n)}catch(i){throw i instanceof F?i.code==="scope_missing"?new u("scope_missing",i.message,{param:t,...i.scope?{scope:i.scope}:{}}):i.code==="not_found"||i.code==="invalid_request"?new u("not_found",`no such file: ${n}`,{param:t}):i:i}try{if(r.size>B)throw new u("invalid_request",`${t} exceeds 100 MiB`,{param:t});const i=re(r.path),o=H(i);if(o)throw new u("invalid_request",`${t}: file name cannot be sent: ${o}`,{param:t});return{param:t,file:r,name:i,mediaType:me(r.path)}}catch(i){throw D(r.fd),i}}d(Fe,"checkFileArgument");async function Te(e,t){try{const n=await e.createOutbound({name:t.name,size:t.file.size,media_type:t.mediaType});await e.putFileBytes(n.upload_link,t.file.fd,t.file.size,t.mediaType);const r=await e.signOutbound(n.object_id);return{param:t.param,path:t.file.path,name:t.name,object_id:n.object_id,url:r.url}}catch(n){throw y(n)}}d(Te,"copyOutbound");async function k(e,t,n,r){const i=[];try{for(const a of t)n[a]===void 0||n[a]===null||i.push(Fe(e,a,n[a]));if(!i.length)return[];let o;try{o=r()}catch(a){throw y(a)}const c=[];for(const a of i)c.push(await Te(o,a));return c}finally{for(const o of i)D(o.file.fd)}}d(k,"copyArguments");function q(e){const t=H(e);if(t)throw new u("server_error",`file output ${e} cannot name an upload: ${t}`)}d(q,"checkOutputField");async function W(e,t,n){q(t);const r=P(e),i="application/octet-stream";let o;try{o=await n().createResultUpload({name:t,media_type:i,max_size:B})}catch(c){throw y(c)}return await ve({upload_id:o.upload_id,grantee:r,name:t,media_type:i,size:B,created_at:new Date().toISOString()}),{field:t,upload_id:o.upload_id,upload_link:o.upload_link,grantee:r}}d(W,"createResultUpload");function X(e){return e?{"Crtr-Upload-Id":e.upload_id,"Crtr-Upload-Link":e.upload_link}:{}}d(X,"uploadHeaders");function Y(e,t){const n={...e};for(const r of t)n[r.param]=r.url;return n}d(Y,"withUrls");async function Je(e,t,n,r={}){const i=r.storage??T;t.output&&q(t.output);const o=await k(e,t.params,n,i),c=t.output?await W(e,t.output,i):null;return{arguments:Y(n,o),copies:o,upload:c,headers:X(c)}}d(Je,"prepareFileCall");async function Ke(e,t,n,r,i={}){const o=i.storage??T,c=[];for(const s of r.copies)try{const p=await o().signOutbound(s.object_id);c.push({...s,url:p.url})}catch(p){if(!(p instanceof U))throw y(p);c.push(...await k(e,[s.param],n,o))}let a=r.upload;if(a)try{const s=await o().signUploadLink(a.upload_id);a={...a,upload_link:s.upload_link}}catch(s){if(!(s instanceof U))throw y(s);s.status===404&&(a=await W(e,t.output??a.field,o))}return{arguments:Y(n,c),copies:c,upload:a,headers:X(a)}}d(Ke,"resignForRetry");const Be=/^[0-9a-f]{64}$/i;function Re(e,t){if(t===1)return e;const n=ae(e);return n&&n!==e?`${e.slice(0,-n.length)}-${t}${n}`:`${e}-${t}`}d(Re,"candidate");async function Qe(e,t,n,r={}){const i={result:n,file:null,cleanup:d(async()=>{},"cleanup")},o=t.upload;if(!o||!n||typeof n!="object"||Array.isArray(n))return i;const c=n,a=c[o.field];if(a==null)return i;if(typeof a!="object"||Array.isArray(a))throw b(`${o.field} must be {upload_id, size, sha256}`);const s=a;if(typeof s.upload_id!="string"||typeof s.size!="number"||!Number.isSafeInteger(s.size)||s.size<0||typeof s.sha256!="string"||!Be.test(s.sha256))throw b(`${o.field} must be {upload_id, size, sha256}`);if(s.upload_id!==o.upload_id)throw b(`${o.field} names an upload other than Crtr-Upload-Id`);const p=s.size,G=s.sha256.toLowerCase(),z=AbortSignal.timeout(r.budgetMs??Ee),R=r.signal?AbortSignal.any([z,r.signal]):z;let h;try{h=await(r.storage??T)().fetchUpload(o.upload_id,R)}catch(_){throw _ instanceof U?b(`${o.field} names an upload with nothing stored`):y(_)}if(h.size===null)throw h.body.destroy(),new u("storage_unavailable",`storage did not say how many bytes ${o.field} holds`);if(h.size!==p)throw h.body.destroy(),b(`${o.field} claims ${p} bytes; storage holds ${h.size}`);if(_e())throw new u("storage_unavailable","a cloud role has no folder to land a file result in");const E=o.grantee,$=e.node_id!==null,J=await we(E),f=$?I(E).nodes:I(E).files,A=$?C(f,e.node_id):f,v=$?C(A,"attachments"):f,K={uid:process.getuid?.()??0,gid:J.gid},O=$&&!M(f,A),Q=$&&!O&&!M(f,v);let w=null,S=null,x=!1;const L=d(async()=>{if(!x){if(x=!0,S)try{await be(S)}catch{}if(w)try{pe(f,w)}catch{}try{O?N(f,A):Q&&N(f,v)}catch{}}},"cleanup");try{he(f,v,()=>{},!0);for(let l=1;;l++){const m=C(v,Re(o.field,l));if(!await ze(m))try{le(f,m,Buffer.alloc(0),432),w=m;break}catch(g){if(g.code!=="EEXIST")throw g}}let _=0;const j=oe("sha256"),V=new de({transform(l,m,g){_+=l.length,j.update(l),g(null,l)}}),Z=ce(f,w,ne.O_WRONLY);try{await ue(h.body,V,ie("",{fd:Z,autoClose:!0}),{signal:R})}catch(l){throw z.aborted?new u("storage_unavailable","the file result could not be fetched within 110 s"):l instanceof F?y(l):new u("storage_unavailable",`storage stopped sending ${o.field}: ${l instanceof Error?l.message:String(l)}`)}if(_!==p)throw new u("storage_unavailable",`storage sent ${_} of ${p} bytes of ${o.field}`);if(j.digest("hex")!==G)throw b(`${o.field}: the stored bytes do not match sha256`);fe(f,w,432,K);const ee=w;await ye(async l=>{const m=await $e(o.upload_id,l);if(!m||m.delete_pending_since)throw new u("not_found",`${o.field}: the upload was deleted while it was fetched`);if(await Ue(o.upload_id,p,l),!e.run_id)return;const g=await l.prepare("SELECT deleting_at FROM nodes WHERE node_id = ?").get(e.run_id);if(!g||g.deleting_at)throw new u("not_found",`run ${e.run_id} was deleted while its file result was fetched`);S={upload_id:o.upload_id,run_id:e.run_id,path:ee},await Se([S],l)})}catch(_){throw h.body.destroy(),await L(),_}return{result:{...c,[o.field]:w},file:{path:w,upload_id:o.upload_id},cleanup:L}}d(Qe,"resolveFileResult");export{Ee as FILE_RESULT_FETCH_BUDGET_MS,Je as prepareFileCall,Ke as resignForRetry,Qe as resolveFileResult};
|
|
1
|
+
var te=Object.defineProperty;var d=(e,t)=>te(e,"name",{value:t,configurable:!0});import{createHash as oe}from"node:crypto";import{closeSync as D,constants as ne,createWriteStream as ie}from"node:fs";import{basename as re,extname as ae,isAbsolute as se,join as C}from"node:path";import{Transform as de}from"node:stream";import{pipeline as ue}from"node:stream/promises";import{appSpace as I}from"../layout.js";import{RunError as F}from"../runs/operations.js";import{createFileBeneath as le,existsBeneath as M,openBeneath as ce,removeTreeBeneath as N,setModeBeneath as fe,unlinkBeneath as pe,withDirBeneath as he}from"../spaces/open-beneath.js";import{ensureAppSpace as we}from"../spaces/space.js";import{isCloudRole as _e}from"../canvas/person-state.js";import{invalidStorageName as H,storageClient as T,StorageRefused as U,UPLOAD_MAX_BYTES as B}from"../storage/client.js";import{contentTypeFor as me,openCallerFile as ge}from"../storage/download.js";import{withCanvasWrite as ye}from"../canvas/db.js";import{deleteUploadUse as be,getUpload as $e,insertUpload as ve,insertUploadUses as Se,setUploadSize as Ue,uploadUseAt as ze}from"../storage/tables.js";import{ProviderError as u}from"./errors.js";const Ee=11e4,Ae="app:terminal";function P(e){if(e.class==="person")return e.grantee??Ae;if(!e.grantee)throw new u("server_error","provider caller has no grantee");return e.grantee}d(P,"grantee");function Ce(e){return e.class==="person"?null:{app:P(e),resolved:e.resolved??[],launched:e.class==="broker"?e.nodeScopes:null}}d(Ce,"fileCaller");function y(e){return e instanceof u?e:e instanceof U?new u("storage_unavailable",`storage refused: ${e.message}`):e instanceof F&&e.code==="storage_unavailable"&&e.retryable?new u("storage_unavailable",e.message):e}d(y,"storageFailure");const b=d(e=>new u("provider_error",e,{origin:"provider"}),"providerError");function Fe(e,t,n){if(typeof n!="string"||!n||!se(n)||n.includes("\0"))throw new u("invalid_request",`${t} must be an absolute runtime path`,{param:t});let r;try{r=ge(Ce(e),n)}catch(i){throw i instanceof F?i.code==="scope_missing"?new u("scope_missing",i.message,{param:t,...i.scope?{scope:i.scope}:{}}):i.code==="not_found"||i.code==="invalid_request"?new u("not_found",`no such file: ${n}`,{param:t}):i:i}try{if(r.size>B)throw new u("invalid_request",`${t} exceeds 100 MiB`,{param:t});const i=re(r.path),o=H(i);if(o)throw new u("invalid_request",`${t}: file name cannot be sent: ${o}`,{param:t});return{param:t,file:r,name:i,mediaType:me(r.path)}}catch(i){throw D(r.fd),i}}d(Fe,"checkFileArgument");async function Te(e,t){try{const n=await e.createOutbound({name:t.name,size:t.file.size,media_type:t.mediaType});await e.putFileBytes(n.upload_link,t.file.fd,t.file.size,t.mediaType);const r=await e.signOutbound(n.object_id);return{param:t.param,path:t.file.path,name:t.name,object_id:n.object_id,url:r.url}}catch(n){throw y(n)}}d(Te,"copyOutbound");async function k(e,t,n,r){const i=[];try{for(const a of t)n[a]===void 0||n[a]===null||i.push(Fe(e,a,n[a]));if(!i.length)return[];let o;try{o=r()}catch(a){throw y(a)}const c=[];for(const a of i)c.push(await Te(o,a));return c}finally{for(const o of i)D(o.file.fd)}}d(k,"copyArguments");function q(e){const t=H(e);if(t)throw new u("server_error",`file output ${e} cannot name an upload: ${t}`)}d(q,"checkOutputField");async function W(e,t,n){q(t);const r=P(e),i="application/octet-stream";let o;try{o=await n().createResultUpload({name:t,media_type:i,max_size:B})}catch(c){throw y(c)}return await ve({upload_id:o.upload_id,grantee:r,name:t,media_type:i,size:B,created_at:new Date().toISOString()}),{field:t,upload_id:o.upload_id,upload_link:o.upload_link,grantee:r}}d(W,"createResultUpload");function X(e){return e?{"Crtr-Upload-Id":e.upload_id,"Crtr-Upload-Link":e.upload_link}:{}}d(X,"uploadHeaders");function Y(e,t){const n={...e};for(const r of t)n[r.param]=r.url;return n}d(Y,"withUrls");async function Je(e,t,n,r={}){const i=r.storage??T;t.output&&q(t.output);const o=await k(e,t.params,n,i),c=t.output?await W(e,t.output,i):null;return{arguments:Y(n,o),copies:o,upload:c,headers:X(c)}}d(Je,"prepareFileCall");async function Ke(e,t,n,r,i={}){const o=i.storage??T,c=[];for(const s of r.copies)try{const p=await o().signOutbound(s.object_id);c.push({...s,url:p.url})}catch(p){if(!(p instanceof U))throw y(p);c.push(...await k(e,[s.param],n,o))}let a=r.upload;if(a)try{const s=await o().signUploadLink(a.upload_id);a={...a,upload_link:s.upload_link}}catch(s){if(!(s instanceof U))throw y(s);s.status===404&&(a=await W(e,t.output??a.field,o))}return{arguments:Y(n,c),copies:c,upload:a,headers:X(a)}}d(Ke,"resignForRetry");const Be=/^[0-9a-f]{64}$/i;function Re(e,t){if(t===1)return e;const n=ae(e);return n&&n!==e?`${e.slice(0,-n.length)}-${t}${n}`:`${e}-${t}`}d(Re,"candidate");async function Qe(e,t,n,r={}){const i={result:n,file:null,cleanup:d(async()=>{},"cleanup")},o=t.upload;if(!o||!n||typeof n!="object"||Array.isArray(n))return i;const c=n,a=c[o.field];if(a==null)return i;if(typeof a!="object"||Array.isArray(a))throw b(`${o.field} must be {upload_id, size, sha256}`);const s=a;if(typeof s.upload_id!="string"||typeof s.size!="number"||!Number.isSafeInteger(s.size)||s.size<0||typeof s.sha256!="string"||!Be.test(s.sha256))throw b(`${o.field} must be {upload_id, size, sha256}`);if(s.upload_id!==o.upload_id)throw b(`${o.field} names an upload other than Crtr-Upload-Id`);const p=s.size,G=s.sha256.toLowerCase(),z=AbortSignal.timeout(r.budgetMs??Ee),R=r.signal?AbortSignal.any([z,r.signal]):z;let h;try{h=await(r.storage??T)().fetchUpload(o.upload_id,R)}catch(_){throw _ instanceof U?b(`${o.field} names an upload with nothing stored`):y(_)}if(h.size===null)throw h.body.destroy(),new u("storage_unavailable",`storage did not say how many bytes ${o.field} holds`);if(h.size!==p)throw h.body.destroy(),b(`${o.field} claims ${p} bytes; storage holds ${h.size}`);if(_e())throw new u("storage_unavailable","a cloud role has no folder to land a file result in");const E=o.grantee,$=e.node_id!==null,J=await we(E),f=$?I(E).nodes:I(E).files,A=$?C(f,e.node_id):f,v=$?C(A,"attachments"):f,K={uid:process.getuid?.()??0,gid:J.gid},O=$&&!M(f,A),Q=$&&!O&&!M(f,v);let w=null,S=null,x=!1;const L=d(async()=>{if(!x){if(x=!0,S)try{await be(S)}catch{}if(w)try{pe(f,w)}catch{}try{O?N(f,A):Q&&N(f,v)}catch{}}},"cleanup");try{he(f,v,()=>{},!0);for(let l=1;;l++){const m=C(v,Re(o.field,l));if(!await ze(m))try{le(f,m,Buffer.alloc(0),432),w=m;break}catch(g){if(g.code!=="EEXIST")throw g}}let _=0;const j=oe("sha256"),V=new de({transform(l,m,g){_+=l.length,j.update(l),g(null,l)}}),Z=ce(f,w,ne.O_WRONLY);try{await ue(h.body,V,ie("",{fd:Z,autoClose:!0}),{signal:R})}catch(l){throw z.aborted?new u("storage_unavailable","the file result could not be fetched within 110 s"):l instanceof F?y(l):new u("storage_unavailable",`storage stopped sending ${o.field}: ${l instanceof Error?l.message:String(l)}`)}if(_!==p)throw new u("storage_unavailable",`storage sent ${_} of ${p} bytes of ${o.field}`);if(j.digest("hex")!==G)throw b(`${o.field}: the stored bytes do not match sha256`);fe(f,w,432,K);const ee=w;await ye(async l=>{const m=await $e(o.upload_id,l);if(!m||m.delete_pending_since)throw new u("not_found",`${o.field}: the upload was deleted while it was fetched`);if(await Ue(o.upload_id,p,l),!e.run_id)return;const g=await l.prepare("SELECT deleting_at FROM nodes WHERE node_id = ?").get(e.run_id);if(!g||g.deleting_at)throw new u("not_found",`run ${e.run_id} was deleted while its file result was fetched`);S={upload_id:o.upload_id,run_id:e.run_id,path:ee},await Se([S],l)})}catch(_){throw h.body.destroy(),await L(),_}return{result:{...c,[o.field]:w},file:{path:w,upload_id:o.upload_id},cleanup:L}}d(Qe,"resolveFileResult");export{Ee as FILE_RESULT_FETCH_BUDGET_MS,Je as prepareFileCall,Ke as resignForRetry,Qe as resolveFileResult};
|
|
@@ -4,7 +4,7 @@ var Me=Object.defineProperty;var c=(e,t)=>Me(e,"name",{value:t,configurable:!0})
|
|
|
4
4
|
AND e.sequence_number > COALESCE((SELECT MAX(c.sequence_number) FROM run_events c
|
|
5
5
|
WHERE c.run_id = e.run_id AND c.node_id = e.node_id AND c.type = 'node.turn.completed'), 0)
|
|
6
6
|
)) LIMIT 1`).get(e.node_id,e.node_id),s=!e.last_settled_at&&e.status!=="done";return t||s||await mt(e)?"running":(await me(e.node_id)).length?"waiting_on_user":await(await A()).prepare(`SELECT 1 FROM deliveries d JOIN nodes n ON n.node_id = d.destination_id
|
|
7
|
-
WHERE n.run_id = ? AND ${Ge("d","'armed', 'pending'")}
|
|
7
|
+
WHERE n.run_id = ? AND ${Ge("d","'armed', 'pending', 'handed_off'")}
|
|
8
8
|
AND (n.status IN ('active', 'idle') OR ${Ye("n")}) LIMIT 1`).get(e.node_id)?"running":"idle"}c(C,"runStatus");async function Wn(e){const t=await O(e);return t!==null&&await C(t)==="running"}c(Wn,"runIsRunning");async function W(e){await Z(e.node_id),e=await O(e.node_id)??e;const t=await C(e),s=t==="settled"?[]:await me(e.node_id),n=await Qe(e.node_id)||void 0,o=t==="settled"?await lt(e):null,m=o?.kind==="result"?await re(e.node_id):void 0,i=Xe(e.node_id);return{run_id:e.node_id,name:e.name,status:t,...o?{outcome:o}:{},...m===void 0?{}:{result:m},...n!==void 0?{last_report:n}:{},...i?{recap:i}:{},pending_question_ids:s,open_notice_ids:t==="settled"?[]:await Et(e.node_id),...e.last_turn_error_json?{last_turn_error:JSON.parse(e.last_turn_error_json)}:{},usage:JSON.parse(e.usage_json??JSON.stringify(X())),payer:await J(e.grant_id),scopes:e.scopes,metadata:e.metadata_json?JSON.parse(e.metadata_json):{},created_at:e.created,...t==="settled"&&e.outcome_at?{settled_at:e.outcome_at}:{},latest_sequence:(e.event_cursor??1)-1,last_activity_at:e.last_activity_at,activity:await gt(e.node_id,t)}}c(W,"runObject");const z=864e5,Ee=255;function Fn(e){if(e!==void 0){if(typeof e!="string"||!e.length||e.length>Ee)throw N(`Idempotency-Key must be 1-${Ee} characters`);return e}}c(Fn,"idempotencyKeyOf");function Dt(e){if(Le()){if(!e)throw new R(403,"permission_error","a cloud role serves this call only for a token that names a person");return e}}c(Dt,"cloudPersonScope");function k(e,t){const s=Dt(e);return s===void 0?t:`${s}:${t}`}c(k,"personKey");async function H(e,t,s,n){n&&await n.prepare("SELECT pg_advisory_xact_lock(hashtext(?))").get(`idempotency:${e}:${t}`),n??=await B();const o=await n.prepare("SELECT body_hash, run_id, response_json, created_at FROM idempotency_keys WHERE grantee = ? AND key = ?").get(e,t);if(!(!o||Date.now()-Date.parse(o.created_at)>=z)){if(o.body_hash!==s)throw new R(409,"idempotency_conflict","key used with a different request");return{run_id:o.run_id,response:JSON.parse(o.response_json)}}}c(H,"findIdempotent");async function ye(e,t,s,n,o,m,i){await e.prepare("DELETE FROM idempotency_keys WHERE grantee = ? AND key = ? AND created_at <= ?").run(t,s,new Date(Date.now()-z).toISOString()),await e.prepare("INSERT INTO idempotency_keys (grantee, key, body_hash, run_id, response_json, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(t,s,n,o,JSON.stringify(m),i)}c(ye,"recordIdempotent");async function qn(e=Date.now()){return Number((await(await B()).prepare("DELETE FROM idempotency_keys WHERE created_at <= ?").run(new Date(e-z).toISOString())).changes)}c(qn,"pruneExpiredIdempotencyKeys");async function he(e,t,s,n,o){const m=n.text;let i=0;return{messageId:(await ze(t,{kind:"message",label:m.slice(0,120),senderNodeId:"human",senderName:s,bodyJson:o?{body:m}:{body:m,start_turn:!1},urgency:o?"normal":"deferred",wake:o?1:0,withinEnqueue:c(async(u,l)=>{n.uses.length&&(await le(s,n.uses,u),await pe(n.uses.map(({upload_id:g,path:E,blob_key:y})=>({upload_id:g,path:E,blob_key:y,run_id:t})),u)),i=(await ce(u,t,t,"node.message.accepted",{message_id:l,start_turn:o})).sequence_number},"withinEnqueue")})).messageId,sequence:i}}c(he,"acceptRunMessage");class Re extends Error{static{c(this,"IdempotentReplay")}winner;constructor(t){super("idempotent replay"),this.winner=t}}const Mt=1440*60*1e3;function Se(e){const t=typeof e=="string"?nt(e):null;if(t===null||!Number.isSafeInteger(t))throw N(`deadline '${String(e)}' is not a duration; use one or more <int>[smhd] parts such as 10m or 1h30m`,"deadline");if(t<=0)throw N("deadline must be greater than zero","deadline");if(t>Mt)throw N("deadline must be at most 24h","deadline");return t}c(Se,"parseRunDeadlineMs");async function Hn(e,t,s,n=we){if(t.isolated&&!it()&&await rt((t.profile===void 0?await te(e.grantee):ne(t.profile)).profileId)===null)throw new R(503,"isolated_unavailable","isolated runs require the Linux root launcher","isolated");t.deadline!==void 0&&Se(t.deadline);const{causedBy:o,...m}=t,i=Q("sha256").update(JSON.stringify(m)).digest("hex");if(s){const a=await H(e.grantee,k(e.sub,s),i);if(a){const p=await q(a.run_id,e.grantee);return!p.pi_pid&&await C(p)==="running"&&(t.output_schema!==void 0&&!p.outcome_at&&await ut(p.node_id)===null&&await re(p.node_id)===void 0&&await oe(p.node_id,"terminal",t.output_schema),await j(p.node_id,{resume:!0,capacity:"freeze"})),a.response}}await yt(e.grantee);const r=t.profile===void 0?await te(e.grantee):ne(t.profile),u=r.manifest.grantee??"app:terminal";if(u!==e.grantee){const a=`crtr:memory:rw:${u}:profile:${r.profileId}`;if(!e.resolved.some(p=>P(p,a))||t.scopes!==null&&!t.scopes.some(p=>P(p,a)))throw new R(403,"scope_missing",`missing scope ${a}`,"profile",void 0,a)}if(t.model!==void 0){const a=t.model;if(tt(a)){if(a.includes("/")&&await J(e.grant)!=="app"){const p=a.lastIndexOf("/"),D=a.slice(0,p),v=a.slice(p+1);if(["ultra","strong","medium","light"].includes(v)&&(await st({kind:"logical",family:D,strength:v,spec:a},Y(e.grantee).files,r.profileId)).every(S=>S.family!==D))throw N(`model '${a}' names a model family that is not configured in this daemon`,"model")}}else if(at(a)===void 0)throw N(`model '${a}' is not a strength (ultra, strong, medium, light), a family/strength, or a provider/model id`,"model")}const l=await Ke(),g=We(Y(e.grantee).nodes,l);if(t.isolated&&t.cwd!==void 0){const a=Ce(g,ke(t.cwd));if(!V(t.cwd)||a===".."||a.startsWith(`..${xe}`)||V(a))throw N("cwd must be inside the isolated root node folder","cwd")}const E=await n(l,e.grantee,t.prompt),y=E.text,d=t.message===void 0?void 0:await n(l,e.grantee,t.message,E.uses.map(a=>a.path)),_=await J(e.grant);let h;try{const a=dt(r,t.model);await et({kind:ot(r),mode:"base",root:!0,rootLifecycle:"resident",readiness:"launch",cwd:t.isolated?t.cwd??g:Y(e.grantee).files,profile:r.profileId,prompt:y,nodeId:l,isolated:t.isolated??!1,...t.causedBy===void 0?{}:{causedBy:t.causedBy},...a===void 0?{}:{model:a},...t.deadline===void 0?{}:{deadlineAt:new Date(Date.now()+Se(t.deadline)).toISOString()},...t.scopes===null?{}:{scopes:t.scopes},run:{grantee:e.grantee,sub:e.sub,grant_id:e.grant,client_id:e.client_id,metadata_json:JSON.stringify(t.metadata),run_id:l,event_cursor:1,usage_json:JSON.stringify(X())},withinCreate:c(async(p,D)=>{if(s){const S=await H(e.grantee,k(e.sub,s),i,p);if(S)throw new Re(S)}const v=E.uses;if(v.length&&(await le(e.grantee,v,p),await pe(v.map(({upload_id:S,path:I,blob_key:U})=>({upload_id:S,path:I,blob_key:U,run_id:l})),p)),h={run_id:l,node_id:l,grantee:e.grantee,grant:e.grant,rev:e.rev,scopes:t.scopes,payer:_,status:"running",created_at:D.created,metadata:t.metadata,sequence_number:0},await ce(p,l,l,"run.started",{scopes:t.scopes,payer:_,usage:X()}),d){const S=await he(p,l,e.grantee,d,!1);h.message={run_id:l,message_id:S.messageId,sequence_number:S.sequence}}s&&await ye(p,e.grantee,k(e.sub,s),i,l,h,D.created)},"withinCreate")},t.output_schema===void 0?void 0:async p=>{await oe(p,"terminal",t.output_schema)})}catch(a){if(a instanceof Re)return a.winner.response;if(t.model!==void 0&&a instanceof Te&&a.code==="usage")throw N(a.message,"model");if(a instanceof Ie&&(a.code==="isolated_run"||a.code==="invalid_request")){const p=a.details&&typeof a.details=="object"&&"param"in a.details&&typeof a.details.param=="string"?a.details.param:void 0;throw new R(a.status,a.code,a.message,p)}throw a}if(!h)throw new Error("run creation did not commit");return h}c(Hn,"startRun");async function Un(e,t){let s;if(t.after)try{const r=JSON.parse(Buffer.from(t.after,"base64url").toString());if(!r||typeof r!="object"||!("activity"in r)||typeof r.activity!="string"||!("id"in r)||typeof r.id!="string")throw new Error("invalid cursor");s={activity:r.activity,id:r.id}}catch{throw N("invalid list cursor","after")}const n=await(await A()).prepareUncached(`SELECT node_id, COALESCE(last_activity_at, created) AS activity FROM node_rows
|
|
9
9
|
WHERE run_id = node_id AND deleting_at IS NULL ${e===null?"":"AND grantee = ?"}${t.sub===void 0?"":" AND sub = ?"}
|
|
10
10
|
ORDER BY activity DESC, node_id DESC`).all(...e===null?[]:[e],...t.sub===void 0?[]:[t.sub]),o=await _e(await x((await _e(n,async({node_id:r})=>!t.visible||await t.visible(r))).filter(({activity:r,node_id:u})=>!s||r<s.activity||r===s.activity&&u<s.id),async r=>({root:await O(r.node_id),activity:r.activity})),async({root:r})=>!t.status||await C(r)===t.status),m=o.slice(0,t.limit),i=m.at(-1);return{runs:await x(m,async({root:r})=>await W(r)),next:o.length>t.limit&&i?Buffer.from(JSON.stringify({activity:i.activity,id:i.root.node_id})).toString("base64url"):null}}c(Un,"listRuns");async function $n(e,t,s){const n=await q(e,t),o=c(async()=>await W(await q(e,t)),"read");return!s||ge.includes(await C(n))?await o():new Promise((m,i)=>{let r=c(()=>{},"unsubscribe");const u=c(()=>{clearTimeout(d),clearInterval(y),r()},"settle"),l=c(async()=>{u();try{m(await o())}catch(_){i(_)}},"finish"),g=c(async()=>{ge.includes(String((await o()).status))&&await l()},"check"),E=c(()=>{g().catch(_=>{u(),i(_)})},"run"),y=setInterval(E,250),d=setTimeout(()=>{l()},s*1e3);wt(e,E).then(_=>{r=_},_=>{u(),i(_)})})}c($n,"waitRun");const At=5;async function Pn(e,t,s=we,n=!0,o){const m=e.status==="canceled"||e.outcome_reason==="closed",i=e.grantee??"app:terminal",r=Q("sha256").update(JSON.stringify(["run.message",e.node_id,t,n])).digest("hex"),u=c(async g=>{const E=await O(e.node_id),y=await(await A()).prepare("SELECT 1 FROM deliveries WHERE message_id = ? AND state = 'pending' AND canceled_at IS NULL").get(String(g.message_id));return E&&!m&&n&&!E.pi_pid&&y&&await j(e.node_id,{resume:!0,capacity:"freeze"}),g},"replay");if(o){const g=await H(i,k(e.sub,o),r);if(g)return u(g.response)}if(m)throw new R(409,"run_settled","canceled run cannot receive a message");const l=n&&e.final_report&&!e.pi_pid?(await import("../runtime/reopen.js")).commitReopenResident:void 0;for(let g=1;;g++){const E=await s(e.node_id,i,t),y=await F(async d=>{if(o){const a=await H(i,k(e.sub,o),r,d);if(a)return{replayed:a.response}}if(E.uses.length){await _t(d,e.node_id);for(const a of E.uses)if(await bt(e.node_id,a.path,d))return"path_taken"}await l?.(e.node_id);const _=await he(d,e.node_id,i,E,n),h={run_id:e.node_id,message_id:_.messageId,sequence_number:_.sequence};return o&&await ye(d,i,k(e.sub,o),r,e.node_id,h,new Date().toISOString()),{result:h}});if(y==="path_taken"){if(g>=At)throw new R(503,"service_unavailable","attachments of concurrent messages kept taking each other's file names; retry");continue}return"replayed"in y?u(y.replayed):(n&&!e.pi_pid&&await j(e.node_id,{resume:!0,capacity:"freeze"}),y.result)}}c(Pn,"messageRun");async function It(e){const t=c(()=>new R(503,"service_unavailable",`node ${e} broker was unavailable before the interrupt reached it`),"unavailable");for(let s=0;s<2;s++){const n=await O(e);if(!n)throw G();if(!ae(n)){if(n.streaming)throw t();return}const o=(await K(e))?.executionId;try{await Ze(e)==="turn"&&fe({nodes:[e]});return}catch(m){if(!(m instanceof Ve))throw m;const i=await O(e);if(!i)throw G();if(!i.streaming)return;if(s===0&&ae(i)&&(await K(e))?.executionId!==o)continue;throw t()}}throw t()}c(It,"interruptNode");async function Bn(e,t="stop"){const s=t==="stop"?(await(await A()).prepare("SELECT node_id FROM nodes WHERE run_id = ?").all(e.node_id)).map(({node_id:n})=>n):[e.node_id];return await Promise.all(s.map(n=>It(n))),await W(await O(e.node_id)??e)}c(Bn,"interruptRun");async function jn(e,t={}){const s=(await(await A()).prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(i=>i.node_id);fe({run:e.node_id});const n=[];if(await C(e)!=="settled")n.push(...(await de(e.node_id,{preserveEmpty:!0,stopReason:"run_canceled"})).closed);else for(const i of s){if(i===e.node_id)continue;const r=await O(i);r&&r.status!=="done"&&r.status!=="canceled"&&r.status!=="dead"&&n.push(...(await de(i,{preserveEmpty:!0,stopReason:"run_canceled"})).closed)}if(await ue({run_id:e.node_id},"the run was canceled"),(await Promise.all([...new Set([e.node_id,...n])].map(i=>se(i,"run_canceled")))).some(i=>!i.confirmed))throw new R(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const m=await O(e.node_id);return await W(m??e)}c(jn,"cancelRun");const Ne=200;async function Jn(e,t){if(typeof t!="string"||!t.trim())throw N("name is required","name");const s=t.trim();if(s.length>Ne)throw N(`name exceeds ${Ne} characters`,"name");const n=await Pe(e.node_id,{name:s});return n.window&&je(n.window,Be(n)),await W(n)}c(Jn,"renameRun");async function Yn(e,t={config:St,client:Rt}){await ue({run_id:e.node_id},"the run was deleted");const s=await F(async u=>(await u.prepare("UPDATE nodes SET deleting_at = COALESCE(deleting_at, ?) WHERE node_id = ?").run(new Date().toISOString(),e.node_id),(await u.prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(l=>l.node_id)));if((await Promise.all(s.map(u=>se(u,"run_deleted",{deleting:!0})))).some(u=>!u.confirmed))throw new R(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const o=await x(s,async u=>{const l=await Ue(u);if(l.app===null)throw new Error(`run node missing during deletion: ${u}`);return l}),m=await(await A()).prepare("SELECT DISTINCT session_id FROM sessions WHERE session_id IS NOT NULL AND node_id IN (SELECT node_id FROM nodes WHERE run_id = ?)").all(e.node_id);for(const{session_id:u}of m)try{await ee().delete(u)}catch(l){if(!Je(l))throw l}const i=await F(async u=>{for(const y of s){const d=await He(u,y);d!==null&&d.deleted_at===null&&await qe(u,d)}const l=await vt([e.node_id],u);await u.prepare("DELETE FROM run_events WHERE run_id = ?").run(e.node_id),await u.prepare("DELETE FROM idempotency_keys WHERE run_id = ?").run(e.node_id);const g=s.map(()=>"?").join(","),E=await u.prepareUncached(`SELECT message_id FROM messages WHERE sender_node_id IN (${g}) UNION SELECT message_id FROM deliveries WHERE destination_id IN (${g})`).all(...s,...s);await u.prepareUncached(`DELETE FROM deliveries WHERE destination_id IN (${g})`).run(...s);for(const{message_id:y}of E)await u.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)").run(y,y);return l});for(const u of o)await $e(u);const{notifyRunDeleted:r}=await import("./events.js");return r(e.node_id),{uploads_pending:await Tt(i,t)}}c(Yn,"deleteRun");async function Tt(e,t){if(!e.length||!t.config())return e.length;let s;try{s=t.client()}catch{return e.length}const n=new Set,o=e.map(i=>s.deleteUpload(i).then(async()=>{await Nt(i),n.add(i)},()=>{}));let m;return await Promise.race([Promise.all(o),new Promise(i=>{m=setTimeout(i,t.budgetMs??Ot),m.unref?.()})]),clearTimeout(m),e.length-n.size}c(Tt,"deleteReleasedUploads");async function Gn(e,t,s){await q(e,t),await Z(e);const n=await F(async d=>{const _=await d.prepare("SELECT node_id, parent FROM nodes WHERE run_id = ? ORDER BY CASE WHEN node_id = ? THEN 0 ELSE 1 END, created, node_id").all(e,e),h=new Map;for(const S of _)h.set(S.node_id,(await d.prepare("SELECT session_id FROM sessions WHERE node_id = ? GROUP BY session_id ORDER BY min(created_at)").all(S.node_id)).map(I=>I.session_id));const p=(await d.prepare("SELECT event_cursor FROM nodes WHERE node_id = ?").get(e)).event_cursor-1,D=await d.prepare(`SELECT node_id, sequence_number FROM run_events
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
`,credentialed:JSON.stringify(o)!==JSON.stringify(n(o))}}a(
|
|
3
|
-
`,{atomic:!0,mode:432}),b(S,ne,432);const oe=p(Y(e),`token-${u}`),
|
|
1
|
+
var xe=Object.defineProperty;var a=(e,t)=>xe(e,"name",{value:t,configurable:!0});import{assertGranteeRunsHere as Te}from"./unsandboxed.js";import{spawn as Oe}from"node:child_process";import{mkdirSync as ue,existsSync as j,unlinkSync as fe,openSync as G,closeSync as C,writeSync as Ne,chmodSync as De,lstatSync as Ce}from"node:fs";import{ensureAppSpace as Le}from"../spaces/space.js";import{sandboxCacheEnv as Ae}from"../spaces/cache-env.js";import{compileLaunch as Be,launchStillGranted as $e}from"../spaces/mounts.js";import{grantsGeneration as de}from"../grants/mirror.js";import{LAUNCHER_SOCKET as q}from"../../launcher/spec.js";import{launcherRequest as F}from"../../launcher/client.js";import{piAgentDir as me}from"./package-health.js";import{existsBeneath as z,readFileBeneath as je,readTextBeneathIfExists as Ge,setModeBeneath as b,withDirBeneath as V,writeFileBeneath as M}from"../spaces/open-beneath.js";import{dirname as X,join as p,resolve as Fe,isAbsolute as Me}from"node:path";import{fileURLToPath as pe}from"node:url";import{connectViewSocket as Ke}from"./view-socket.js";import{appSpace as K}from"../layout.js";import{readConfig as Je,readMergedLaunchConfig as He}from"../config.js";import{loadRotationConfig as Ue}from"../subscription-state.js";import{getNode as g}from"../canvas/index.js";import{withCanvasWrite as We}from"../canvas/db.js";import{requestExecutionStop as qe}from"../canvas/executions.js";import{scopesFilePath as Q,crtrHome as he,nodeRunDir as Y,nodeRunJobDir as ze,jobDir as Ve,viewSocketPath as Xe,nodeAgentDir as Qe,contextDir as Ye,nodeHome as Ze}from"../canvas/paths.js";import{encodeFrame as we}from"./broker-protocol.js";import{buildBrokerEnv as Ie}from"./spawn-env-rows.js";import{hostExecPathFresh as er}from"./branded-host.js";import{isPidAlive as Z,killProcessTreePids as ge,isAnyPidAlive as rr,captureTeardownSnapshot as tr}from"../canvas/pid.js";import{assertMandatoryCanvasExtensions as nr}from"./canvas-extensions.js";import{appMemoryFull as or,appMemoryTooSmall as ir,boundFleet as sr,brokerCapReached as ar,brokerThresholdsForDaemon as cr}from"./fleet.js";import{admitBrokerMemory as lr}from"./memory-admission.js";import{emitEvent as E}from"../events/emit.js";import{bindBrokerEventSource as ur,eventSource as fr}from"../events/source.js";import{envBrokerEngine as dr}from"../../shared/env.js";import{conversationIdFromPath as mr,conversationStore as pr,ConversationNotFoundError as Ee}from"../conversation-store/index.js";import{mintNodeToken as hr}from"../canvas/node-tokens.js";import{personId as wr}from"../canvas/person.js";function J(){const e=X(pe(import.meta.url));return p(e,"broker-cli.js")}a(J,"resolveBrokerEntry");const gr="@earendil-works/pi-coding-agent";function _e(){const e=p(me(),"models.json"),t=Ge(X(e),e);if(t===null)return null;const o=JSON.parse(t),n=a(r=>Array.isArray(r)?r.map(n):r===null||typeof r!="object"?r:Object.fromEntries(Object.entries(r).filter(([s])=>!/^(apiKey|authorization|headers|token|secret)$/i.test(s)).map(([s,c])=>[s,n(c)])),"scrub");return{text:t,clean:`${JSON.stringify(n(o),null,2)}
|
|
2
|
+
`,credentialed:JSON.stringify(o)!==JSON.stringify(n(o))}}a(_e,"scrubbedBrokerModels");function Er(e,t){const o=p(me(),"models.json"),n=p(t,"models.json"),r=_e();if(r!==null){if(o===n&&r.text!==r.clean){if(r.credentialed)throw new Error("terminal models.json contains credentials; move them into daemon secrets");b(e,n,432);return}M(e,n,r.clean,{atomic:!0,mode:432}),b(e,n,432)}}a(Er,"writeBrokerModels");function ke(e){return e.env.CRTR_BROKER_ENGINE??dr()}a(ke,"resolveEngineSpec");function ve(e){return e.includes("/")||e.endsWith(".jsonl")}a(ve,"isPlainLaunchSessionPath");function _r(e,t){return Me(e)?e:Fe(t,e)}a(_r,"resolvePlainLaunchPath");async function kr(e,t,o,n){if(o==="--session"&&!ve(n))throw new Error(`[broker] refusing to launch ${e}: launch recipe passes bare --session id '${n}' \u2014 the broker fatally rejects bare ids; pass the .jsonl path instead.`);if(!ve(n))return;const r=_r(n,t),s=mr(r);try{if(s===null)throw new Ee(r);await pr().header(s)}catch(c){if(!(c instanceof Ee))throw c;const l=o==="--session"?"session file":"fork source";throw new Error(`[broker] refusing to launch ${e}: launch recipe references missing ${l} '${n}' (${r})`)}}a(kr,"preflightLaunchSessionArg");async function vr(e,t,o){const n=_e();return JSON.stringify({...t,configSnapshot:{user:Je("user"),launch:He(o,t.env.CRTR_PROFILE_ID||null),rotation:await Ue()},scopes:(await g(e))?.scopes??null,models:n===null?null:JSON.parse(n.clean)})}a(vr,"brokerLaunchJson");async function yr(e,t,o,n){const r=Y(e);ue(r,{recursive:!0}),process.platform==="linux"&&j(q)&&Ce(r).uid===process.getuid()&&De(r,504);const s=p(r,"broker-launch.json");M(r,s,n??await vr(e,t,o),{atomic:!0}),t.env.CRTR_SCOPES_FILE!==void 0&&M(r,Q(e),JSON.stringify((await g(e))?.scopes??[]),{atomic:!0});const c=je(r,s).toString("utf8"),l=JSON.parse(c);if(!Array.isArray(l.argv))throw new Error(`[broker] refusing to launch ${e}: broker-launch.json is missing the argv array`);const k=ke(t);try{const u=import.meta.resolve(k);if(u.startsWith("file:")&&!j(pe(u)))throw new Error(`resolved to missing file ${u}`)}catch(u){throw new Error(`[broker] refusing to launch ${e}: cannot resolve broker engine '${k}' from ${J()} \u2014 ${u.message}`)}const f=l.argv,N=[];for(let u=0;u<f.length;u++){const P=f[u];if(P==="-e"){const L=f[++u];L!==void 0&&N.push(L);continue}if(P!=="--session"&&P!=="--fork")continue;const T=f[++u];T!==void 0&&await kr(e,o,P,T)}try{nr(N)}catch(u){throw new Error(`[broker] refusing to launch ${e}: ${u.message}`)}}a(yr,"preflightBrokerLaunch");const Sr=2e3,br=2e3,Rr=150;async function H(e,t){const o=Date.now()+t;for(;rr(e);){const n=o-Date.now();if(n<=0)return!1;await new Promise(r=>setTimeout(r,Math.min(Rr,n)))}return!0}a(H,"waitForTreeExit");async function Pr(e,t,o){return e.length===0?{confirmed:!0}:o&&await H(e,Sr)?{confirmed:!0}:(ge(e,"SIGTERM",t),await H(e,br)?{confirmed:!0}:(ge(e,"SIGKILL",t),await H(e,5e3)?{confirmed:!0}:{confirmed:!1,error:new Error(`broker process tree for ${e.join(", ")} remained alive after SIGKILL`)}))}a(Pr,"terminateBrokerTree");async function ye(e,t){const o=await g(e),n=o?.pi_pid,r=n!=null?tr(n,o?.pi_pid_identity??null,t.excludePid):null;return r?.reused===!0&&E({level:"warn",event:"broker.teardown.pid_reuse_refused",node_id:e,fields:{recorded_pid:n,expected_identity:o?.pi_pid_identity??null}}),{tree:r?.tree??[],identities:r?.identities??null,nodeId:e,launcherPid:j(q)&&r?.tree.includes(n)?n:null}}a(ye,"prepareBrokerTeardown");async function Se(e){if(e.launcherPid!==null){try{await F({operation:"stop",pid:e.launcherPid})}catch(t){return Z(e.launcherPid)?{confirmed:!1,error:t instanceof Error?t:new Error(String(t))}:{confirmed:!0}}return await H([e.launcherPid],5e3)?{confirmed:!0}:{confirmed:!1,error:new Error(`launcher pid ${e.launcherPid} remained alive`)}}return new Promise(t=>{let o=!1,n=!1;const r=a(k=>{n||(n=!0,t(k))},"settle"),s=a(()=>{try{fe(Xe(e.nodeId))}catch{}},"cleanupSocket"),c=a(k=>{Pr(e.tree,e.identities,k).then(r,f=>r({confirmed:!1,error:f instanceof Error?f:new Error(String(f))}))},"finish"),l=Ke(e.nodeId);l.once("error",()=>{o||(s(),c(!1))}),l.once("connect",()=>{o=!0;try{l.write(we({type:"hello",role:"controller",client_id:`daemon-shutdown-${process.pid}`})),l.write(we({type:"shutdown"}))}catch{}l.end(),s(),c(!0)})})}a(Se,"runPreparedBrokerTeardown");async function ct(e,t={},o){let n;try{await qe(e,{afterTurn:t.startAfter!==void 0,reason:t.reason??"teardown"}),n=await ye(e,t)}catch(r){const s={confirmed:!1,error:r instanceof Error?r:new Error(String(r))};return E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:s.error}),s}return await new Promise(r=>{const s=a(()=>{Se(n).then(async c=>{if(c.confirmed){if(o!==void 0)try{await o()}catch(l){E({level:"error",event:"broker.teardown.after_failed",node_id:e,error:l})}}else E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:c.error});r(c)},c=>{const l={confirmed:!1,error:c instanceof Error?c:new Error(String(c))};E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:l.error}),r(l)})},"run");t.startAfter===void 0?setImmediate(s):t.startAfter.then(s,s)})}a(ct,"requestBrokerTeardown");function be(){return process.platform==="linux"&&j(q)}a(be,"isolatedBrokerLauncherAvailable");const lt={async launch(e,t,o){if(Te((await g(e))?.grantee),(await g(e))?.isolated===!0&&!be())throw new Error("isolated broker requires the Linux root launcher");await yr(e,t,o.cwd,o.launchJson);const n=sr();if(!await n.reserveSlot(e))throw ar(cr().automaticReviveCap,await n.occupancy());let r=null,s,c=!1;try{const l=Ve(e);ue(l,{recursive:!0}),r=G(p(l,"broker.log"),"a");const k=ke(t),f={...await Ie(t),CRTR_BROKER_ENGINE:k};if(k!==gr){const i=t.env.NODE_OPTIONS??process.env.NODE_OPTIONS;i!==void 0&&(f.NODE_OPTIONS=i);const d=t.env.CRTR_TEST_PG_URL??process.env.CRTR_TEST_PG_URL;d!==void 0&&(f.CRTR_TEST_PG_URL=d)}fr()===void 0&&ur(e);const N=(await g(e))?.run_id;if(N){const i=await g(N);if(!i||i.deleting_at)throw new Error("run is deleting")}const u=t.env.CRTR_EXECUTION_ID;if(!u)throw new Error("broker launch has no execution id");const P=await We(i=>hr(i,u,e,new Date().toISOString())),T=be();if((await g(e))?.isolated===!0&&!T)throw new Error("isolated broker requires the Linux root launcher");if(process.platform==="linux"&&process.env.CRTR_LAUNCHER_SOCKET&&!T)throw new Error("root launcher socket is unavailable; refusing an unsandboxed broker");if(T){const i=await g(e);if(!i)throw new Error(`missing node ${e}`);const d=i.grantee??"app:terminal",x=await lr(e,{useMemo:!1});if(x.verdict==="denied")throw or(x);if(x.verdict==="too_small")throw ir(x);const v=await Le(d);Object.assign(f,Ae(d));const y={uid:process.getuid(),gid:v.gid},w=Y(e);for(const[m,_]of[[K(d).nodes,Qe(e,d)],[K(d).nodes,Ye(e,d)],[w,ze(e)]])V(m,_,()=>{},!0),b(m,_,1528,y);b(w,p(w,"broker-launch.json"),416,y),z(w,Q(e))&&b(w,Q(e),416,y);const re=p(w,"system-prompt.md");z(w,re)&&b(w,re,432,y);for(const m of["output-schema.json","situational-context.md"]){const _=p(w,m);z(w,_)&&b(w,_,432,y)}const te=i.isolated===!0,S=Ze(e,d,te);te&&(V(K(d).nodes,S,()=>{},!0),b(K(d).nodes,S,1528,y),f.HOME=S,f.PI_CODING_AGENT_DIR=p(S,".pi","agent"));const W=p(S,".pi","agent");V(S,W,()=>{},!0),Er(S,W);const ne=p(W,"settings.json"),Re=p(X(J()),"../../builtin-pi-packages/pi-crtr-extensions");M(S,ne,`${JSON.stringify({packages:[Re]})}
|
|
3
|
+
`,{atomic:!0,mode:432}),b(S,ne,432);const oe=p(Y(e),`token-${u}`),B=G(oe,"wx+",384);fe(oe),Ne(B,P);const ie=[G(`/proc/self/fd/${B}`,"r"),G(`/proc/self/fd/${B}`,"r")];C(B);const se={nodeId:e,app:d,sub:i.sub??wr(),grantId:i.grant_id??null,scopes:i.scopes??null,isolated:i.isolated===!0,...i.isolated===!0?{cwd:i.cwd}:{},command:[process.execPath,J(),"--canvas-home",he(),"--epoch",n.epoch(),e],env:Object.fromEntries(Object.entries({...f,...i.isolated===!0?{PWD:i.cwd}:{}}).filter(m=>m[1]!==void 0)),auditMissing:a(m=>E({level:"warn",event:"broker.mount.missing",node_id:e,fields:{source:m}}),"auditMissing")};try{let m;for(const D of ie){const $=await de(),h=await Be(se);h.broker=!0;const R=await F({operation:"launch",spec:h},[D]);if(!("pid"in R))throw new Error("launcher did not return a pid");if(await de()===$||await $e(se,h)){m=R.pid;break}E({level:"info",event:"broker.launch.regranted",node_id:e,fields:{pid:R.pid}}),await F({operation:"stop",pid:R.pid,signal:"SIGKILL"}).catch(()=>{})}if(m===void 0)throw new Error("grant changed during every launch attempt");const _=m,Pe=Date.now(),ae=new Promise(D=>{const $=setInterval(()=>{Z(_)||(clearInterval($),F({operation:"exit.status",pid:_}).then(h=>"oomKilled"in h?{oomKilled:h.oomKilled,code:h.code??null,signal:h.signal??null}:{oomKilled:!1,code:null,signal:null},()=>({oomKilled:!1,code:null,signal:null})).then(({oomKilled:h,code:R,signal:le})=>{E({level:h||R!==0?"warn":"info",event:"broker.exited",node_id:e,fields:{pid:_,code:R,signal:le,uptime_ms:Date.now()-Pe,oom_killed:h}}),D({code:R,signal:le,...h?{oomKilled:!0}:{}})}))},250);$.unref()});C(r),r=null;const ce={pid:_,exited:ae};return n.register(e,ce),c=!0,ae.then(async D=>await n.onChildExit(e,D,u)),ce}finally{for(const m of ie)C(m)}}const L=await er(),O=Oe(L,[J(),"--canvas-home",he(),"--epoch",n.epoch(),e],{cwd:o.cwd,detached:!0,stdio:["ignore",r,r,"pipe"],env:f});s=O;const A=O.stdio[3];if(!A||!("end"in A))throw new Error("broker token pipe did not open");A.on("error",i=>E({event:"broker.token_pipe.failed",level:"error",node_id:e,error:i})),A.end(P),C(r),r=null;const I=Date.now(),ee=new Promise(i=>{let d=!1;const x=a(v=>{d||(d=!0,i(v))},"finish");O.once("exit",(v,y)=>{E({level:v===0?"info":"warn",event:"broker.exited",node_id:e,fields:{code:v,signal:y,uptime_ms:Date.now()-I}}),x({code:v,signal:y})}),O.once("error",v=>{E({level:"error",event:"broker.launch.failed",node_id:e,error:v,fields:{uptime_ms:Date.now()-I}}),x({code:null,signal:null})})});O.unref();const U={pid:O.pid??null,exited:ee};return U.pid!==null&&(n.register(e,U),c=!0,ee.then(async i=>await n.onChildExit(e,i,u))),U}catch(l){if(!c&&s?.pid!=null)try{s.kill("SIGTERM")}catch{}throw l}finally{c||n.releaseReservation(e),r!==null&&C(r)}},async isAlive(e){return Z((typeof e=="string"?await g(e):e)?.pi_pid)},async teardown(e,t={}){try{return Se(await ye(e,t))}catch(o){return Promise.resolve({confirmed:!1,error:o instanceof Error?o:new Error(String(o))})}},async signal(e,t){const o=(await g(e))?.pi_pid;if(o!=null)try{process.kill(o,t)}catch{}}};export{vr as brokerLaunchJson,lt as headlessBrokerHost,be as isolatedBrokerLauncherAvailable,yr as preflightBrokerLaunch,ct as requestBrokerTeardown};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var we=Object.defineProperty;var d=(e,o)=>we(e,"name",{value:o,configurable:!0});import{accessSync as me,constants as he}from"node:fs";import{isAbsolute as pe,resolve as _e,join as ke}from"node:path";import{appSpace as ve}from"../layout.js";import{ApiError as ee}from"../../api/errors.js";import{spawnNode as ye,forkSourceNode as ge,currentNodeContext as be,rootOfSpine as xe,newNodeId as Ce,preflightNodeId as Ee}from"./nodes.js";import{resolveProfileOperand as Se}from"../profiles/manifest.js";import{selectProfileForCwd as Oe,selectProfileForCwdReadOnly as Re}from"../profiles/select.js";import{buildLaunchSpecAsync as ne,buildPiArgv as $e}from"./launch.js";import{runsOnBlaxel as Fe,sandboxWorktreeRequest as Ie}from"../../daemon/api/environment-of.js";import{createManagedWorktreeAsync as Be,rollbackManagedWorktreeAsync as Ne}from"../worktree-mutation-async.js";import{usage as u,brokerLaunchFailed as B}from"../errors.js";import{writeGoal as Pe}from"./kickoff.js";import{appendSituationalContext as De,formatSituationalProse as Te}from"./situational-context.js";import{ensureRoadmap as Le}from"./roadmap.js";import{buildWakeBearings as Ae}from"./bearings.js";import{encodeKickoffOrigin as Me,KICKOFF_ORIGIN_ENV as We}from"./stamp/protocol.js";import{ExecutionCapReached as je,beginExecution as ze,crtrHome as He,getNode as l,fullName as oe,jobDir as Ke,recordExecutionPid as Ge,abortExecution as M,setFrozen as W}from"../canvas/index.js";import{contextDir as j,nodeAgentDir as z}from"../canvas/node-agent-paths.js";import{copyTreeBeneath as Ue}from"../spaces/open-beneath.js";import{conversationIdFromPath as N,conversationStore as E}from"../conversation-store/index.js";import{appMemoryTooSmall as Ve,boundFleet as Xe,brokerThresholdsForDaemon as re}from"./fleet.js";import{admitBrokerMemory as qe}from"./memory-admission.js";import{emitEvent as H}from"../events/emit.js";import{blaxelEnvironmentOf as Ye}from"./blaxel-environment.js";import{openViewerWindow as Je,focusOf as Qe,windowOfPane as Ze}from"./placement.js";import{waitForBrokerViewSocket as en}from"./placement-tmux.js";import{transition as k}from"./lifecycle.js";import{beginBootFaultAttempt as nn}from"../canvas/recovery.js";import{headlessBrokerHost as K}from"./host.js";import{ensureDaemon as on}from"../../daemon/manage.js";import{readConfig as rn}from"../config.js";import{modelRequestFromConfig as tn,unregisteredConcreteModel as an}from"../model-routes.js";import{openModelRegistry as dn}from"./model-registry.js";import{MODEL_SPEC_NEXT as sn}from"./model-selection.js";import{forkNodeLedger as cn}from"./ledger.js";import{appRungForGrant as ln}from"../llm/app-rungs.js";import{APP_FAMILY as G}from"../model-routes.js";import{discoverLifecycleHookRegistry as fn}from"../command-hooks/discovery.js";import{invokeLifecycleHooks as un}from"../command-hooks/transport/exec-lifecycle.js";import{someAsync as wn}from"../async-array.js";import{checkStart as mn}from"./start-check.js";function v(e){return{detail:{schema:"crtr.node-outcome-detail/v1",message:e instanceof Error?e.message:String(e)}}}d(v,"launchFailureOutcome");async function hn(e,o,a){try{return await K.launch(e,o,a)}catch(i){try{await k(e,"crash",{reason:"launch_failed",outcome:v(i)})}catch{}const t=i.message;throw B(t.startsWith("[broker]")?t:`[broker] refusing to launch ${e}: ${t}`,"Inspect the cause above, fix the launch recipe or environment, then retry.")}}d(hn,"launchBrokerOrCrash");async function pn(e,o){try{await k(e,"crash",{reason:"launch_failed",outcome:v(o)})}catch{}try{K.teardown(e)}catch{}}d(pn,"crashAndTearDown");async function _n(e){const o=(await E().list()).map(({conversation_id:t,path:s})=>({id:t,path:s})),a=o.filter(t=>t.id===e),i=a.length>0?a:o.filter(t=>t.id.startsWith(e));if(i.length===0)throw u(`--fork-from '${e}': no pi session with that id found.`,{next:"Pass a live node id, a real session uuid/prefix, or an absolute .jsonl session path."});if(i.length>1)throw u(`--fork-from '${e}': ambiguous \u2014 ${i.length} sessions match that id prefix; pass the full id or the .jsonl path.`,{next:"Pass the full session uuid or an absolute .jsonl path to disambiguate."});return i[0].path}d(_n,"resolveSessionUuid");async function ie(e){const o=e.trim();if(o==="")throw u("--fork-from requires a node id, session file, or session uuid.",{next:"Pass a live node id, an absolute .jsonl session path, or a session uuid/prefix."});if(o.includes("/")||o.endsWith(".jsonl")){const i=N(pe(o)?o:_e(process.cwd(),o));if(i===null)throw u(`--fork-from '${o}': invalid conversation path.`,{next:"Pass a conversation file path or id."});return(await E().header(i)).path}const a=await l(o);if(a!==null){const i=a.pi_session_file;if(i==null||i==="")throw u(`node ${o} has no pi session yet \u2014 it has not captured a session FILE to fork from (the broker forks a .jsonl path, not a bare id).`,{next:"Pick a node that has captured a session, or drop --fork-from."});const t=N(i);if(t===null)throw u(`node ${o} has no valid conversation id.`,{next:"Pick a node with a captured conversation."});return(await E().header(t)).path}return await _n(o)}d(ie,"resolveForkSource");async function kn(e,o,a,i={}){return e===null?null:e!==void 0&&e!==""?Se(e).profileId:o!==null?(await l(o))?.profile_id??null:i.readOnlySelection===!0?Re(a):Oe(a)}d(kn,"resolveProfileId");function te(e){const o=e.modelIntent===void 0?void 0:`${e.modelIntent.family}/${e.modelIntent.strength}`;return tn(e.model,o,e.modelExact===!0)}d(te,"requestFromLaunch");async function ae(e,o,a){const i=a??(e.modelIntent?`${e.modelIntent.family}/${e.modelIntent.strength}`:e.model),t=await ln(o,i,e.modelExact===!0);if(t===null)return e;const{CRTR_MODEL_INTENT:s,...c}=e.env??{};return{...e,model:t.model,modelIntent:{family:G,strength:t.strength},modelExact:void 0,env:{...c,CRTR_MODEL_INTENT:`${G}/${t.strength}`}}}d(ae,"appPaidLaunch");async function de(e,o,a){if(e===void 0||e.kind==="logical"&&e.family!==G)return;const i=await an(e.kind==="logical"?{kind:"concrete",spec:e.spec}:e,a??await dn(),o);if(i!==void 0)throw u(`model '${i.request.spec}' is not registered in this daemon.`,{received:i.request.spec,next:sn})}d(de,"assertLaunchModelRegistered");function vn(e,o,a){const i=e??o??null;if(!a&&i===null)throw new Error("spawnChild requires a calling node (CRTR_NODE_ID) or an explicit parent");return i}d(vn,"resolveSpawner");async function se(e,o,a){try{on()}catch{}const i=be(),t=e.root===!0,s=vn(e.parent,i.nodeId??null,t),c=e.mode??"base",y=e.rootLifecycle??(t?"resident":"terminal"),ce=t&&y==="resident",O=s===null?null:await l(s),U=e.creator===void 0||e.creator===null?null:await l(e.creator);e.nodeId!==void 0&&await Ee(e.nodeId);const g=e.forkFrom!==void 0?await ie(e.forkFrom):void 0,P=g===void 0?null:await ge(g),D=await wn([O,U,P],async f=>f?.isolated===!0||f?.run_id!=null&&(await l(f.run_id))?.isolated===!0);if(D&&e.isolated===!1)throw new ee(403,"isolated_run","isolation cannot be turned off under an isolated run",{param:"isolated"});const R=D||e.isolated===!0;if(R&&e.worktree===!0)throw new ee(400,"invalid_request","isolated nodes cannot use a worktree",{param:"worktree"});const S=e.worktree===!0,w=R||S?e.nodeId??await Ce():e.nodeId,$=R?e.run!==void 0&&t&&!D?e.cwd:ke(ve(e.run?.grantee??O?.grantee??"app:terminal").nodes,w):e.cwd,m=await kn(e.profile,s,$),V=e.run?.grant_id??O?.grant_id??null,X=await ae((await ne(e.kind,c,{lifecycle:y,hasManager:!t,model:e.model,modelOverride:e.model!==void 0,cwd:$,profileId:m})).launch,V,e.model);await de(te(X),{cwd:$,profileId:m});const T=e.run??U??O??P;await mn({grantee:T?.grantee??"app:terminal",sub:T?.sub,grant_id:T?.grant_id});const q=P?.node_id??null;let b,F=$;const L=S&&await Fe(m)?await Ie(m,e.worktreeCwd??e.cwd,w,e.worktreeBase):void 0;try{S&&L===void 0&&(b=await Be(e.worktreeCwd??e.cwd,w,e.worktreeBase),F=b.path);const f=S?await ae((await ne(e.kind,c,{lifecycle:y,hasManager:!t,model:e.model,modelOverride:e.model!==void 0,cwd:F,profileId:m})).launch,V,e.model):X;L!==void 0&&(f.worktree=L),S&&await de(te(f),{cwd:F,profileId:m});const n=await ye({kind:e.kind,mode:c,lifecycle:y,cwd:F,name:e.name??e.kind,description:e.description,parent:t?null:s,creator:e.creator,...e.actor===void 0?{}:{actor:e.actor},forkSourceNode:q,spawnedBy:t?s??void 0:void 0,profile_id:m,...e.scopes===void 0?{}:{scopes:e.scopes},isolated:R,...e.run===void 0?{}:{run:e.run},...e.causedBy===void 0?{}:{causedBy:e.causedBy},...e.withinCreate===void 0?{}:{withinCreate:e.withinCreate},forkFrom:e.forkFrom,forkSourceFile:g,modelOverride:e.model===void 0?void 0:f.model,...b!==void 0?{managedWorktree:b}:{},...w!==void 0?{nodeId:w}:{},...e.reviewBinding===void 0?{}:{reviewBinding:e.reviewBinding},...e.deadlineAt===void 0?{}:{deadlineAt:e.deadlineAt},...e.outcomeDelivery===void 0?{}:{outcomeDelivery:e.outcomeDelivery},launch:f});if(a?.(n.node_id),e.forkFrom!==void 0)try{await cn(n.node_id,q??null)}catch(r){throw await k(n.node_id,"crash",{reason:"launch_failed",outcome:v(r)}),r}if(await Pe(n.node_id,e.prompt),e.situationalContext!==void 0&&await De(n.node_id,Te(e.situationalContext)),o!==void 0)try{o(n.node_id)}catch(r){throw await k(n.node_id,"crash",{reason:"launch_failed",outcome:v(r)}),r}const A=e.prompt!=="",le=`${A&&e.wakeOrigin!==void 0?`${Ae(e.wakeOrigin)}
|
|
1
|
+
var me=Object.defineProperty;var d=(e,o)=>me(e,"name",{value:o,configurable:!0});import{accessSync as he,constants as pe}from"node:fs";import{isAbsolute as _e,resolve as ke,join as ve}from"node:path";import{appSpace as ge}from"../layout.js";import{ApiError as ne}from"../../api/errors.js";import{spawnNode as ye,forkSourceNode as be,currentNodeContext as xe,rootOfSpine as $e,newNodeId as Se,preflightNodeId as Ce}from"./nodes.js";import{resolveProfileOperand as Ee}from"../profiles/manifest.js";import{selectProfileForCwd as Re,selectProfileForCwdReadOnly as Oe}from"../profiles/select.js";import{buildLaunchSpecAsync as oe,buildPiArgv as Fe}from"./launch.js";import{runsOnBlaxel as Ie,sandboxWorktreeRequest as Be}from"../../daemon/api/environment-of.js";import{createManagedWorktreeAsync as De,rollbackManagedWorktreeAsync as Ne}from"../worktree-mutation-async.js";import{usage as w,brokerLaunchFailed as D}from"../errors.js";import{writeGoal as Pe}from"./kickoff.js";import{appendSituationalContext as Le,formatSituationalProse as Me}from"./situational-context.js";import{ensureRoadmap as Te}from"./roadmap.js";import{buildWakeBearings as Ae}from"./bearings.js";import{encodeKickoffOrigin as We,KICKOFF_ORIGIN_ENV as je}from"./stamp/protocol.js";import{ExecutionCapReached as ze,beginExecution as He,crtrHome as Ke,getNode as u,fullName as re,jobDir as Ge,recordExecutionPid as Ue,abortExecution as j,setFrozen as z}from"../canvas/index.js";import{contextDir as H,nodeAgentDir as K}from"../canvas/node-agent-paths.js";import{copyTreeBeneath as Ve}from"../spaces/open-beneath.js";import{conversationIdFromPath as N,conversationStore as S}from"../conversation-store/index.js";import{appMemoryTooSmall as Xe,boundFleet as qe,brokerThresholdsForDaemon as te}from"./fleet.js";import{admitBrokerMemory as Ye}from"./memory-admission.js";import{emitEvent as P}from"../events/emit.js";import{blaxelEnvironmentOf as Je}from"./blaxel-environment.js";import{openViewerWindow as Qe,focusOf as Ze,windowOfPane as en}from"./placement.js";import{awaitBrokerViewSocket as nn}from"./view-socket.js";import{readLogSnapshot as on}from"../events/read.js";import{transition as k}from"./lifecycle.js";import{beginBootFaultAttempt as rn}from"../canvas/recovery.js";import{headlessBrokerHost as G}from"./host.js";import{ensureDaemon as tn}from"../../daemon/manage.js";import{readConfig as an}from"../config.js";import{modelRequestFromConfig as dn,unregisteredConcreteModel as sn}from"../model-routes.js";import{openModelRegistry as ln}from"./model-registry.js";import{MODEL_SPEC_NEXT as cn}from"./model-selection.js";import{forkNodeLedger as un}from"./ledger.js";import{appRungForGrant as fn}from"../llm/app-rungs.js";import{APP_FAMILY as U}from"../model-routes.js";import{discoverLifecycleHookRegistry as wn}from"../command-hooks/discovery.js";import{invokeLifecycleHooks as mn}from"../command-hooks/transport/exec-lifecycle.js";import{someAsync as hn}from"../async-array.js";import{checkStart as pn}from"./start-check.js";function v(e){return{detail:{schema:"crtr.node-outcome-detail/v1",message:e instanceof Error?e.message:String(e)}}}d(v,"launchFailureOutcome");async function _n(e,o,a){try{return await G.launch(e,o,a)}catch(r){try{await k(e,"crash",{reason:"launch_failed",outcome:v(r)})}catch{}const i=r.message;throw D(i.startsWith("[broker]")?i:`[broker] refusing to launch ${e}: ${i}`,"Inspect the cause above, fix the launch recipe or environment, then retry.")}}d(_n,"launchBrokerOrCrash");function kn(e){return on({nodeId:e,component:"broker",pattern:"broker.startup."},!1).items.flatMap(a=>a.kind==="event"&&a.envelope.event.startsWith("broker.startup.")&&a.envelope.event!=="broker.startup.warning"?[a.envelope.event.slice(15)]:[]).at(-1)??null}d(kn,"lastStartupStep");function vn(e,o){const a=kn(e),r=a===null?"it had logged no startup step":`the last startup step it logged was \`${a}\``;if(o.reason==="exited"){const{code:i,signal:s}=o.status;return{text:`exited (code ${i??"null"}, signal ${s??"null"}) before binding its view socket; ${r}`,fields:{reason:"exited",code:i,signal:s,last_startup_step:a}}}return{text:`was still starting after ${Math.round(o.waitedMs/1e3)} s without binding its view socket, so the launcher stopped it; ${r}`,fields:{reason:"timeout",waited_ms:o.waitedMs,last_startup_step:a}}}d(vn,"notReadyDescription");async function gn(e,o){try{await k(e,"crash",{reason:"launch_failed",outcome:v(o)})}catch{}try{G.teardown(e)}catch{}}d(gn,"crashAndTearDown");async function yn(e){const o=(await S().list()).map(({conversation_id:i,path:s})=>({id:i,path:s})),a=o.filter(i=>i.id===e),r=a.length>0?a:o.filter(i=>i.id.startsWith(e));if(r.length===0)throw w(`--fork-from '${e}': no pi session with that id found.`,{next:"Pass a live node id, a real session uuid/prefix, or an absolute .jsonl session path."});if(r.length>1)throw w(`--fork-from '${e}': ambiguous \u2014 ${r.length} sessions match that id prefix; pass the full id or the .jsonl path.`,{next:"Pass the full session uuid or an absolute .jsonl path to disambiguate."});return r[0].path}d(yn,"resolveSessionUuid");async function ie(e){const o=e.trim();if(o==="")throw w("--fork-from requires a node id, session file, or session uuid.",{next:"Pass a live node id, an absolute .jsonl session path, or a session uuid/prefix."});if(o.includes("/")||o.endsWith(".jsonl")){const r=N(_e(o)?o:ke(process.cwd(),o));if(r===null)throw w(`--fork-from '${o}': invalid conversation path.`,{next:"Pass a conversation file path or id."});return(await S().header(r)).path}const a=await u(o);if(a!==null){const r=a.pi_session_file;if(r==null||r==="")throw w(`node ${o} has no pi session yet \u2014 it has not captured a session FILE to fork from (the broker forks a .jsonl path, not a bare id).`,{next:"Pick a node that has captured a session, or drop --fork-from."});const i=N(r);if(i===null)throw w(`node ${o} has no valid conversation id.`,{next:"Pick a node with a captured conversation."});return(await S().header(i)).path}return await yn(o)}d(ie,"resolveForkSource");async function bn(e,o,a,r={}){return e===null?null:e!==void 0&&e!==""?Ee(e).profileId:o!==null?(await u(o))?.profile_id??null:r.readOnlySelection===!0?Oe(a):Re(a)}d(bn,"resolveProfileId");function ae(e){const o=e.modelIntent===void 0?void 0:`${e.modelIntent.family}/${e.modelIntent.strength}`;return dn(e.model,o,e.modelExact===!0)}d(ae,"requestFromLaunch");async function de(e,o,a){const r=a??(e.modelIntent?`${e.modelIntent.family}/${e.modelIntent.strength}`:e.model),i=await fn(o,r,e.modelExact===!0);if(i===null)return e;const{CRTR_MODEL_INTENT:s,...l}=e.env??{};return{...e,model:i.model,modelIntent:{family:U,strength:i.strength},modelExact:void 0,env:{...l,CRTR_MODEL_INTENT:`${U}/${i.strength}`}}}d(de,"appPaidLaunch");async function se(e,o,a){if(e===void 0||e.kind==="logical"&&e.family!==U)return;const r=await sn(e.kind==="logical"?{kind:"concrete",spec:e.spec}:e,a??await ln(),o);if(r!==void 0)throw w(`model '${r.request.spec}' is not registered in this daemon.`,{received:r.request.spec,next:cn})}d(se,"assertLaunchModelRegistered");function xn(e,o,a){const r=e??o??null;if(!a&&r===null)throw new Error("spawnChild requires a calling node (CRTR_NODE_ID) or an explicit parent");return r}d(xn,"resolveSpawner");async function le(e,o,a){try{tn()}catch{}const r=xe(),i=e.root===!0,s=xn(e.parent,r.nodeId??null,i),l=e.mode??"base",g=e.rootLifecycle??(i?"resident":"terminal"),ce=i&&g==="resident",E=s===null?null:await u(s),V=e.creator===void 0||e.creator===null?null:await u(e.creator);e.nodeId!==void 0&&await Ce(e.nodeId);const y=e.forkFrom!==void 0?await ie(e.forkFrom):void 0,L=y===void 0?null:await be(y),M=await hn([E,V,L],async f=>f?.isolated===!0||f?.run_id!=null&&(await u(f.run_id))?.isolated===!0);if(M&&e.isolated===!1)throw new ne(403,"isolated_run","isolation cannot be turned off under an isolated run",{param:"isolated"});const R=M||e.isolated===!0;if(R&&e.worktree===!0)throw new ne(400,"invalid_request","isolated nodes cannot use a worktree",{param:"worktree"});const C=e.worktree===!0,m=R||C?e.nodeId??await Se():e.nodeId,O=R?e.run!==void 0&&i&&!M?e.cwd:ve(ge(e.run?.grantee??E?.grantee??"app:terminal").nodes,m):e.cwd,h=await bn(e.profile,s,O),X=e.run?.grant_id??E?.grant_id??null,q=await de((await oe(e.kind,l,{lifecycle:g,hasManager:!i,model:e.model,modelOverride:e.model!==void 0,cwd:O,profileId:h})).launch,X,e.model);await se(ae(q),{cwd:O,profileId:h});const T=e.run??V??E??L;await pn({grantee:T?.grantee??"app:terminal",sub:T?.sub,grant_id:T?.grant_id});const Y=L?.node_id??null;let b,F=O;const A=C&&await Ie(h)?await Be(h,e.worktreeCwd??e.cwd,m,e.worktreeBase):void 0;try{C&&A===void 0&&(b=await De(e.worktreeCwd??e.cwd,m,e.worktreeBase),F=b.path);const f=C?await de((await oe(e.kind,l,{lifecycle:g,hasManager:!i,model:e.model,modelOverride:e.model!==void 0,cwd:F,profileId:h})).launch,X,e.model):q;A!==void 0&&(f.worktree=A),C&&await se(ae(f),{cwd:F,profileId:h});const n=await ye({kind:e.kind,mode:l,lifecycle:g,cwd:F,name:e.name??e.kind,description:e.description,parent:i?null:s,creator:e.creator,...e.actor===void 0?{}:{actor:e.actor},forkSourceNode:Y,spawnedBy:i?s??void 0:void 0,profile_id:h,...e.scopes===void 0?{}:{scopes:e.scopes},isolated:R,...e.run===void 0?{}:{run:e.run},...e.causedBy===void 0?{}:{causedBy:e.causedBy},...e.withinCreate===void 0?{}:{withinCreate:e.withinCreate},forkFrom:e.forkFrom,forkSourceFile:y,modelOverride:e.model===void 0?void 0:f.model,...b!==void 0?{managedWorktree:b}:{},...m!==void 0?{nodeId:m}:{},...e.reviewBinding===void 0?{}:{reviewBinding:e.reviewBinding},...e.deadlineAt===void 0?{}:{deadlineAt:e.deadlineAt},...e.outcomeDelivery===void 0?{}:{outcomeDelivery:e.outcomeDelivery},launch:f});if(a?.(n.node_id),e.forkFrom!==void 0)try{await un(n.node_id,Y??null)}catch(t){throw await k(n.node_id,"crash",{reason:"launch_failed",outcome:v(t)}),t}if(await Pe(n.node_id,e.prompt),e.situationalContext!==void 0&&await Le(n.node_id,Me(e.situationalContext)),o!==void 0)try{o(n.node_id)}catch(t){throw await k(n.node_id,"crash",{reason:"launch_failed",outcome:v(t)}),t}const W=e.prompt!=="",ue=`${W&&e.wakeOrigin!==void 0?`${Ae(e.wakeOrigin)}
|
|
2
2
|
|
|
3
|
-
`:""}${e.prompt}`;
|
|
3
|
+
`:""}${e.prompt}`;l==="orchestrator"&&await Te(n.node_id,{goal:e.prompt.trim()}),await $n(n);let J;if(y!==void 0)try{const t=N(y);if(t===null)throw new Error(`invalid fork source ${y}`);const c=await S().fork(t,n.node_id);J=(await S().header(c)).path}catch(t){throw await k(n.node_id,"crash",{reason:"launch_failed",outcome:v(t)}),t}const x=Fe(n,{prompt:ue,resumeSessionPath:J});if(x.env={...x.env,CRTR_SUBTREE:await $e(n.node_id)},W){const t=e.wakeOrigin!==void 0?{type:"runtime",kind:"wake",wake:e.wakeOrigin}:{type:"runtime",kind:"kickoff",...n.spawned_by!=null&&n.spawned_by!==""?{spawnedBy:n.spawned_by}:{}};x.env[je]=We(t)}const p=qe();if(await Je(n.profile_id??null)!==null)return await z(n.node_id,new Date().toISOString()),P({level:"info",event:"broker.frozen",node_id:n.node_id,fields:{reason:"remote_environment"}}),{node:await u(n.node_id),frozen:!0,window:null,session:null};const Q=await p.reserveSlot(n.node_id),_=Q?await Ye(n.node_id):null;if(_?.verdict==="too_small"){p.releaseReservation(n.node_id);const t=Xe(_);throw await k(n.node_id,"crash",{reason:"launch_failed",outcome:v(t)}),t}if(!Q||_?.verdict==="denied")return _?.verdict==="denied"&&p.releaseReservation(n.node_id),await z(n.node_id,new Date().toISOString()),P({level:"info",event:"broker.frozen",node_id:n.node_id,fields:_?.verdict==="denied"?{reason:"app_memory",free_bytes:_.free_bytes,needed_bytes:_.needed_bytes}:{reason:"broker_cap",live_count:await p.occupancy(),hard_cap:te().automaticReviveCap}}),{node:await u(n.node_id),frozen:!0,window:null,session:null};const fe=await rn(n.node_id),{automaticReviveCap:we}=te();let $;try{$=await He(n.node_id,{daemonEpoch:p.epoch(),cap:we})}catch(t){if(!(t instanceof ze))throw t;return p.releaseReservation(n.node_id),await z(n.node_id,new Date().toISOString()),P({level:"info",event:"broker.frozen",node_id:n.node_id,fields:{reason:"broker_cap",live_count:t.live,hard_cap:t.cap}}),{node:await u(n.node_id),frozen:!0,window:null,session:null}}x.env={...x.env,CRTR_EXECUTION_ID:$};let I;try{I=await _n(n.node_id,x,{cwd:n.cwd,name:re(n),resuming:!1})}catch(t){throw await j($,{reason:"launch_failed"}),t}finally{p.releaseReservation(n.node_id)}if(I.pid===null){const t=new Error(`broker host returned no pid for ${n.node_id}`);throw await j($,{reason:"launch_failed"}),await k(n.node_id,"crash",{reason:"launch_failed",outcome:v(t)}),D(`failed to launch the broker engine for ${n.node_id} (${n.name}) \u2014 the node was not started.`,`Inspect canonical event diagnostics with \`crtr sys logs --node ${n.node_id}\`, then verify the pi engine can start in this environment.`)}if(await Ue($,n.node_id,I.pid)==="shutdown")throw await j($,{reason:"superseded"}),G.teardown(n.node_id),D(`broker execution was superseded for ${n.node_id}`,"Retry spawning the node.");if(ce&&e.readiness==="await"){const t=await nn(n.node_id,I.exited);if(!t.ready){const c=vn(n.node_id,t);P({level:"error",event:"broker.launch.not_ready",node_id:n.node_id,fields:c.fields});const B=await fe.explanation();throw await gn(n.node_id,new Error(B??c.text)),D(B!==null?`the root broker ${n.node_id} could not start \u2014 ${B}`:`the root broker ${n.node_id} ${c.text} \u2014 it was not started.`,B!==null?"Resolve the cause named above, then retry.":`Inspect canonical event diagnostics with \`crtr sys logs --node ${n.node_id}\`, then retry.`)}}let Z=null,ee=null;if(!i&&s!==null&&an("user").auto_open_child_viewers)try{const t=await Ze(s);if(t!==null&&t.pane!==null&&t.pane!==""){const c=await Qe(n.node_id,t.session??"",{name:re(n),cwd:e.cwd,besidePane:t.pane,waitForBroker:!0,initialWhip:W});c!==null&&c.pane!==null&&c.pane!==""&&(ee=c.session,Z=en(c.pane))}}catch{}return{node:await u(n.node_id),frozen:!1,window:Z,session:ee}}catch(f){throw b!==void 0&&m!==void 0&&await u(m)===null&&await Ne(b),f}}d(le,"spawnChildPrepared");async function ko(e){return le(e)}d(ko,"spawnChild");async function $n(e){const o=wn(e.cwd,e.profile_id).plans.get("node:start")??[];await mn(o,{node:{id:e.node_id,name:e.name,kind:e.kind,mode:e.mode,lifecycle:e.lifecycle,cwd:e.cwd,nodeDir:await K(e.node_id),contextDir:await H(e.node_id),jobDir:Ge(e.node_id)},runtime:{isBirth:!0,canvasHome:Ke(),profile:e.profile_id??null}})}d($n,"invokeBirthLifecycleHooks");const Sn=new Set(["initial-prompt.md","yield-message.md","result.json","jobs"]);async function vo(e,o,a){const r=await u(e);if(r===null)throw w(`unknown node: ${e}`,{received:e,next:"List nodes and pass the id of a node with a captured pi session."});const i=await ie(e),s=await H(e);try{await S().header(N(i)),he(s,pe.R_OK)}catch(l){throw new Error(`cannot fork ${e}: its conversation or context is unreadable \u2014 ${l.message}`)}return le({kind:r.kind,mode:"base",cwd:r.cwd,prompt:"",root:!0,readiness:"await",forkFrom:e,...a===void 0?{}:{actor:a},profile:r.profile_id,model:r.model_override??void 0,...o===void 0?{}:{scopes:o}},async l=>{const g=await H(l);Ve(await K(e),s,await K(l),g,Sn)})}d(vo,"forkNode");export{de as appPaidLaunch,se as assertLaunchModelRegistered,vo as forkNode,$n as invokeBirthLifecycleHooks,ie as resolveForkSource,bn as resolveProfileId,xn as resolveSpawner,ko as spawnChild,le as spawnChildPrepared};
|
|
@@ -10,10 +10,31 @@ export declare function connectViewSocket(nodeId: string): Socket;
|
|
|
10
10
|
* `timeoutMs`. A view.sock that is a symlink rejects with the refusal, so no
|
|
11
11
|
* wait reads a planted link as a broker that is still starting. */
|
|
12
12
|
export declare function probeViewSocket(nodeId: string, timeoutMs: number): Promise<boolean>;
|
|
13
|
+
/** How a wait for a broker's view.sock ended: it accepted, the launch handle reported the broker's exit (with the
|
|
14
|
+
* status Node observed), or the deadline passed with the broker still running. */
|
|
15
|
+
export type BrokerViewWait = {
|
|
16
|
+
ready: true;
|
|
17
|
+
} | {
|
|
18
|
+
ready: false;
|
|
19
|
+
reason: 'exited';
|
|
20
|
+
status: {
|
|
21
|
+
code: number | null;
|
|
22
|
+
signal: NodeJS.Signals | null;
|
|
23
|
+
};
|
|
24
|
+
} | {
|
|
25
|
+
ready: false;
|
|
26
|
+
reason: 'timeout';
|
|
27
|
+
waitedMs: number;
|
|
28
|
+
};
|
|
13
29
|
/** Wait for a launched broker's view.sock, failing fast when its launch handle
|
|
14
|
-
* exits. `exited`
|
|
15
|
-
* bounded readiness deadline without a handle; `waitMs`
|
|
16
|
-
* for a caller holding an HTTP request open. */
|
|
30
|
+
* exits and saying which way the wait ended. `exited` is optional for callers
|
|
31
|
+
* that only need the bounded readiness deadline without a handle; `waitMs`
|
|
32
|
+
* shortens that deadline for a caller holding an HTTP request open. */
|
|
33
|
+
export declare function awaitBrokerViewSocket(nodeId: string, exited?: Promise<{
|
|
34
|
+
code: number | null;
|
|
35
|
+
signal: NodeJS.Signals | null;
|
|
36
|
+
}>, waitMs?: number): Promise<BrokerViewWait>;
|
|
37
|
+
/** {@link awaitBrokerViewSocket} for callers that only branch on readiness. */
|
|
17
38
|
export declare function waitForBrokerViewSocket(nodeId: string, exited?: Promise<{
|
|
18
39
|
code: number | null;
|
|
19
40
|
signal: NodeJS.Signals | null;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var m=Object.defineProperty;var
|
|
1
|
+
var m=Object.defineProperty;var c=(n,e)=>m(n,"name",{value:e,configurable:!0});import{EventEmitter as p}from"node:events";import{createConnection as y,Socket as E}from"node:net";import{WebSocket as u}from"ws";import{CLIENT_READ_CAPS as S}from"./broker-protocol.js";import{nodeRunDir as x,viewSocketPath as k}from"../canvas/paths.js";import{isBeneathRefusal as O,socketBeneath as _}from"../spaces/open-beneath.js";const w=3e4,d=100,K=w;function h(n){return new Promise(e=>{setTimeout(e,n)})}c(h,"sleep");function R(n){let e;try{e=_(x(n),k(n))}catch(t){const r=new E;return process.nextTick(()=>r.destroy(t)),r}const s=y(e.path);return s.once("connect",e.release),s.once("close",e.release),s}c(R,"connectViewSocket");function T(n,e){return new Promise((s,t)=>{let r=!1,i=null;const f=c(o=>{r||(r=!0,i!==null&&i.destroy(),o instanceof Error?t(o):s(o))},"finish");i=R(n),i.once("connect",()=>f(!0)),i.once("error",o=>f(O(o)?o:!1)),i.setTimeout(e,()=>f(!1))})}c(T,"probeViewSocket");async function B(n,e,s=w){const t=Date.now()+s,r=e?.then(i=>({exit:i}));for(;Date.now()<=t;){const i=Math.max(0,t-Date.now()),f=T(n,Math.min(d,i)).then(a=>a?"ready":"retry"),o=r===void 0?await f:await Promise.race([f,r]);if(o==="ready")return{ready:!0};if(typeof o=="object")return{ready:!1,reason:"exited",status:o.exit};if(r!==void 0){const a=await Promise.race([h(d).then(()=>"sleep"),r]);if(typeof a=="object")return{ready:!1,reason:"exited",status:a.exit}}else await h(d)}return{ready:!1,reason:"timeout",waitedMs:s}}c(B,"awaitBrokerViewSocket");async function I(n,e,s=w){return(await B(n,e,s)).ready}c(I,"waitForBrokerViewSocket");const M=4180;function C(n,e){return new g(n,e)}c(C,"connectWssView");class g extends p{static{c(this,"WssViewLink")}ws;failed=!1;ended=!1;terminated=!1;pending=[];constructor(e,s){super(),this.ws=new u(e,{headers:s,maxPayload:S.maxLineBytes}),this.ws.on("open",()=>{for(const t of this.pending.splice(0))this.ws.send(t);this.emit("connect")}),this.ws.on("message",t=>{const r=Array.isArray(t)?Buffer.concat(t):Buffer.isBuffer(t)?t:Buffer.from(new Uint8Array(t));this.emit("data",r.at(-1)===10?r:Buffer.concat([r,Buffer.from(`
|
|
2
2
|
`)]))}),this.ws.on("unexpected-response",(t,r)=>{this.fail(Object.assign(new Error(`broker view refused the upgrade (${r.statusCode??"no status"})`),{code:"ECONNREFUSED"})),this.ws.terminate()}),this.ws.on("error",t=>this.fail(t)),this.ws.on("close",t=>{this.ended||(t===1006&&!this.terminated&&this.fail(Object.assign(new Error("broker view connection dropped (1006)"),{code:"ECONNRESET"})),this.ended=!0,this.emit("close",this.failed))})}get destroyed(){return this.ended||this.ws.readyState===u.CLOSING||this.ws.readyState===u.CLOSED}write(e){const s=e.endsWith(`
|
|
3
|
-
`)?e.slice(0,-1):e;return this.ws.readyState===u.CONNECTING?(this.pending.push(s),!0):this.ws.readyState!==u.OPEN?!1:(this.ws.send(s),!0)}destroy(){return this.terminated=!0,this.ws.terminate(),this}fail(e){this.failed||(this.failed=!0,this.emit("error",e))}}async function
|
|
3
|
+
`)?e.slice(0,-1):e;return this.ws.readyState===u.CONNECTING?(this.pending.push(s),!0):this.ws.readyState!==u.OPEN?!1:(this.ws.send(s),!0)}destroy(){return this.terminated=!0,this.ws.terminate(),this}fail(e){this.failed||(this.failed=!0,this.emit("error",e))}}async function F(n,e,s,t=w){const r=Date.now()+t;let i=!1;for(s?.then(()=>{i=!0});Date.now()<=r&&!i;){if(await new Promise(o=>{const a=C(n,e),l=setTimeout(()=>{a.destroy(),o(!1)},Math.max(1,r-Date.now()));a.on("connect",()=>{clearTimeout(l),a.destroy(),o(!0)}),a.on("error",()=>{}),a.on("close",()=>{clearTimeout(l),o(!1)})}))return!0;if(Date.now()+d>r)break;await h(d)}return!1}c(F,"waitForWssView");export{M as BROKER_VIEW_PORT,K as BROKER_VIEW_SOCKET_WAIT_MS,B as awaitBrokerViewSocket,R as connectViewSocket,C as connectWssView,T as probeViewSocket,I as waitForBrokerViewSocket,F as waitForWssView};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var x=Object.defineProperty;var
|
|
1
|
+
var x=Object.defineProperty;var i=(r,d)=>x(r,"name",{value:d,configurable:!0});import{createReadStream as v,read as S,statSync as $}from"node:fs";import{Readable as _}from"node:stream";import{promisify as T}from"node:util";import{readConfig as E}from"../config.js";import{directoryIssuer as O}from"../directory/issuer.js";import{RunError as b}from"../runs/operations.js";import{mintDirectoryToken as C,runtimeId as R}from"../grants/sync.js";const G=104857600;class m extends Error{static{i(this,"StorageRefused")}status;code;constructor(d,u,l){super(l),this.status=d,this.code=u}}const j=T(S);async function*U(r,d){let u=0;for(;u<d;){const l=Buffer.allocUnsafe(Math.min(1048576,d-u)),{bytesRead:f}=await j(r,l,0,l.length,u);if(f===0)throw new Error("file shrank while it was being copied");u+=f,yield l.subarray(0,f)}}i(U,"fdChunks");const s=i(r=>new b(503,"storage_unavailable",r,void 0,void 0,void 0,!0),"unavailable");function P(){const r=E("user").runtime?.storage;return!r?.service_url||!r.base_url?null:{service_url:r.service_url.replace(/\/$/,""),base_url:r.base_url.replace(/\/$/,"")}}i(P,"storageConfig");let w;function F(){const r=P(),d=O();if(!r||!d)throw new b(503,"storage_unavailable","file storage is not configured on this runtime: it has no `runtime.storage` setting (service_url, base_url) or no joined directory. Only a hosted runtime has file storage (its provisioner sets CRTR_BOOT_STORAGE_URL), and the directory issues storage tokens only to a sandboxed runtime, so a computer joined with `crtr sys join` cannot send or receive files yet. Retrying does not help: run this command from your hosted runtime.",void 0,void 0,void 0,!1);const u=`${d}
|
|
2
2
|
${r.service_url}
|
|
3
|
-
${r.base_url}`;return
|
|
3
|
+
${r.base_url}`;return w?.key!==u&&(w={key:u,client:A(r,()=>C(d,r.service_url),R())}),w.client}i(F,"storageClient");function J(){w=void 0}i(J,"resetStorageClient");function A(r,d,u){let l,f;const k=i(async()=>{if(l&&l.expires_at-6e4>Date.now())return l.access_token;f??=d().finally(()=>{f=void 0});try{l=await f}catch(t){throw s(`storage token could not be minted: ${t instanceof Error?t.message:String(t)}`)}return l.access_token},"bearer"),c=i(async(t,e,n={})=>{const p=[AbortSignal.timeout(n.timeoutMs??1e4),...n.signal?[n.signal]:[]];let o;try{o=await fetch(`${r.service_url}${e}`,{method:t,signal:AbortSignal.any(p),headers:{Authorization:`Bearer ${await k()}`,...n.body===void 0?{}:{"Content-Type":"application/json"}},...n.body===void 0?{}:{body:JSON.stringify(n.body)}})}catch(a){throw a instanceof b?a:s(`storage could not be reached: ${a instanceof Error?a.message:String(a)}`)}if(o.status===401&&(l=void 0),o.status===404||o.status===409){const a=await o.json().catch(()=>({})),g=a.error?.code??a.code??(o.status===404?"not_found":"not_uploaded");throw new m(o.status,g,a.error?.message??a.message??`storage answered ${o.status}`)}if(!o.ok)throw await o.body?.cancel().catch(()=>{}),s(`storage answered ${o.status} to ${t} ${e.split("?")[0]}`);return o},"call"),y=i(async t=>{try{return await t.json()}catch{throw s("storage answered with an invalid body")}},"json"),h=i(t=>encodeURIComponent(t),"id");return{config:r,runtimeId:u,async createUpload(t){const e=await y(await c("POST","/v1/uploads",{body:t}));if(typeof e.upload_id!="string"||typeof e.url!="string"||typeof e.upload_link!="string"||typeof e.expires_at!="string")throw s("storage answered with an invalid upload");return{upload_id:e.upload_id,url:e.url,upload_link:e.upload_link,expires_at:e.expires_at}},async fetchUpload(t,e){const n=await c("GET",`/v1/uploads/${h(t)}/content`,{...e?{signal:e}:{},timeoutMs:11e4});if(!n.body)throw s("storage answered with no body");const p=n.headers.get("content-length");return{body:_.fromWeb(n.body),size:p===null?null:Number(p),media_type:n.headers.get("content-type")}},async signDownload(t){const e=await y(await c("POST",`/v1/uploads/${h(t)}/download-link`));if(typeof e.url!="string"||typeof e.expires_at!="string")throw s("storage answered with an invalid link");return{url:e.url,expires_at:e.expires_at}},async deleteUpload(t){try{await c("DELETE",`/v1/uploads/${h(t)}`)}catch(e){if(!(e instanceof m&&e.status===404))throw e}},async createShare(t){const e=await y(await c("POST","/v1/shares",{body:t}));if(typeof e.share_id!="string"||typeof e.url!="string"||typeof e.upload_link!="string")throw s("storage answered with an invalid share");return{share_id:e.share_id,url:e.url,upload_link:e.upload_link,expires_at:e.expires_at??null}},async putShareBytes(t,e,n,p){if($(e).size!==n)throw new Error("shared file changed size while it was being copied");let o;try{o=await fetch(t,{method:"PUT",signal:AbortSignal.timeout(11e4),headers:{"Content-Type":p,"Content-Length":String(n)},body:_.toWeb(v(e)),duplex:"half"})}catch(a){throw s(`storage could not be reached: ${a instanceof Error?a.message:String(a)}`)}if(await o.body?.cancel().catch(()=>{}),!o.ok)throw s(`storage answered ${o.status} to the share upload`)},async deleteShare(t){try{await c("DELETE",`/v1/shares/${h(t)}`)}catch(e){if(!(e instanceof m&&e.status===404))throw e}},async createResultUpload(t){const e=await y(await c("POST","/v1/uploads",{body:{name:t.name,media_type:t.media_type,max_size:t.max_size}}));if(typeof e.upload_id!="string"||typeof e.url!="string"||typeof e.upload_link!="string"||typeof e.expires_at!="string")throw s("storage answered with an invalid upload");return{upload_id:e.upload_id,url:e.url,upload_link:e.upload_link,expires_at:e.expires_at}},async signUploadLink(t){const e=await y(await c("POST",`/v1/uploads/${h(t)}/upload-link`));if(typeof e.upload_link!="string"||typeof e.expires_at!="string")throw s("storage answered with an invalid upload link");return{upload_link:e.upload_link,expires_at:e.expires_at}},async createOutbound(t){const e=await y(await c("POST","/v1/outbound",{body:t}));if(typeof e.object_id!="string"||typeof e.upload_link!="string"||typeof e.url!="string"||typeof e.expires_at!="string")throw s("storage answered with an invalid outbound object");return{object_id:e.object_id,upload_link:e.upload_link,url:e.url,link_expires_at:e.link_expires_at,expires_at:e.expires_at}},async signOutbound(t){const e=await y(await c("POST",`/v1/outbound/${h(t)}/link`));if(typeof e.url!="string"||typeof e.expires_at!="string")throw s("storage answered with an invalid link");return{url:e.url,link_expires_at:e.link_expires_at,expires_at:e.expires_at}},async putFileBytes(t,e,n,p){const o=_.from(U(e,n));let a;try{a=await fetch(t,{method:"PUT",signal:AbortSignal.timeout(11e4),headers:{"Content-Type":p,"Content-Length":String(n)},body:_.toWeb(o),duplex:"half"})}catch(g){throw s(`storage could not be reached: ${g instanceof Error?g.message:String(g)}`)}finally{o.destroy()}if(await a.body?.cancel().catch(()=>{}),!a.ok)throw s(`storage answered ${a.status} to the upload`)}}}i(A,"createStorageClient");function X(r){return!r||Buffer.byteLength(r,"utf8")>255?"name must be 1-255 bytes":r==="."||r===".."?"name must not be . or ..":/[/\\\u0000-\u001f\u007f]/.test(r)?"name must not contain /, \\, or control characters":null}i(X,"invalidStorageName");export{m as StorageRefused,G as UPLOAD_MAX_BYTES,A as createStorageClient,X as invalidStorageName,J as resetStorageClient,F as storageClient,P as storageConfig};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var X=Object.defineProperty;var t=(e,n)=>X(e,"name",{value:n,configurable:!0});import{randomUUID as nn}from"node:crypto";import{relative as en,resolve as G,sep as F}from"node:path";import{ApiError as s}from"../../../api/errors.js";import{armCron as rn,parseCronSink as on,getCron as v,listCrons as A,listCronRuns as L,pokeHeldCrons as tn,setCronState as Z}from"../../../core/canvas/crons.js";import{getNode as _}from"../../../core/canvas/canvas.js";import{appSpace as an,cronFolder as sn}from"../../../core/layout.js";import{appProcessesSandboxed as cn,cwdIsMounted as ln}from"../../../core/spaces/process.js";import{activeGrant as dn,activeGrantForApp as un,grantFor as pn}from"../../../core/grants/mirror.js";import{callerTerminalGrant as fn}from"../../../core/grants/terminal.js";import{assertScope as H}from"../../../core/scopes.js";import{InputError as c}from"../../../core/io.js";import{nextSlotAfter as wn}from"../../../core/wake.js";import{executeCron as _n,markSelfCanceledCronRun as hn}from"../../cron-run.js";import{toCronDTO as k,toCronRunDTO as M}from"../map.js";import{appOf as P,assertCeiling as mn,canReadNode as gn}from"../router.js";import{checkCronBirth as yn,checkCronFire as bn,cronRefusalError as vn}from"../../cron/authority.js";import{endCron as kn}from"../../cron/ended.js";import{requireUsableProfile as Sn}from"./profiles.js";import{filterAsync as $n,mapAsync as On}from"../../../core/async-array.js";import{personId as Cn}from"../../../core/canvas/person.js";function E(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new c({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}t(E,"requireString");function O(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new c({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(O,"requireEnum");async function In(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new c({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(await _(n)===null)throw new c({error:"sink_node_not_found",message:`sink node ${n} does not exist on the canvas`,field:"sink",next:"Name a live node id, or use spawn:<kind> for a delivery that outlives any one node."});return JSON.stringify({node:n})}if(e.startsWith("spawn:")){const n=e.slice(6).trim();if(n==="")throw new c({error:"bad_sink",message:"spawn sink requires a node kind",field:"sink",next:'Use sink "spawn:<kind>" (e.g. spawn:general).'});return JSON.stringify({spawn:n})}throw new c({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(In,"parseSinkSpec");const qn=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function Y(e,n){if(!qn.test(e)||Number.isNaN(Date.parse(e)))throw new c({error:`bad_${n}`,message:`"${e}" is not a UTC ISO instant`,field:n,next:'Send a resolved UTC instant like "2026-06-07T09:00:00.000Z" (the client resolves the time grammar).'});return e}t(Y,"requireUtcIso");function Nn(e){try{wn(e,new Date)}catch(n){throw new c({error:"bad_recur",message:`recur is not advanceable: ${n instanceof Error?n.message:String(n)}`,field:"recur",next:'Send recur JSON the daemon can advance: {"every":"6h"} or {"cron":"0 9 * * *","tz":"America/New_York"}.'})}return e}t(Nn,"requireRecur");function jn(e){let n;try{n=JSON.parse(e)}catch{throw new c({error:"bad_env_json",message:"env_json is not valid JSON",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'})}if(typeof n!="object"||n===null||Array.isArray(n))throw new c({error:"bad_env_json",message:"env_json must be a JSON object",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'});for(const[r,o]of Object.entries(n))if(typeof o!="string")throw new c({error:"bad_env_json",message:`env_json value for "${r}" is not a string`,field:"env_json",next:"Every env value must be a string."});return e}t(jn,"requireEnvJson");function U(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(U,"callerProfile");function K(e,n){return n===null||e.scope==="global"||e.profile===n}t(K,"inScope");function J(e){if(e.principal.class==="person"&&e.principal.sub!==void 0)throw new s(403,"permission_error","schedules are managed by the install's person, a node or an app");return e.principal.class==="person"?null:P(e.principal)}t(J,"callerApp");async function R(e,n){return await gn(e.principal,n.grantee,n.created_by??void 0)}t(R,"visibleTo");function Tn(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(Tn,"anchorNodeOf");async function An(e,n,r,o,i,d){const u=typeof e.cwd=="string"&&e.cwd.trim()!==""?G(e.cwd):null;if(!cn()){if(u===null)throw new c({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return u}const h=o!==null&&await _(o)!==null?o:void 0,l=t(async q=>await ln(n,q,h,i,void 0,d),"reaches");if(u!==null&&await l(u))return u;const a=an(n).files;if(u!==null&&e.cwd_is_default!==!0)throw new s(400,"invalid_request",`cwd ${u} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${a}`,{cwd:u,default:a},void 0,{param:"cwd"});const p=r===null?null:(await _(r))?.cwd??null;return p!==null&&await l(p)?p:a}t(An,"resolveArmedCwd");async function Pn(e,n,r){const o=e.principal;if(o.class==="person"){if(o.sub!==void 0)throw new s(403,"permission_error","schedules are managed by the install's person, a node or an app");const a=await fn();return{sub:a?.sub??Cn(),grant_id:a?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const a=await un(n);if(a===null)throw new s(401,"grant_revoked",`no active grant for ${n}`);const p=e.ceiling??o.scopes;return{sub:a.sub,grant_id:a.id,scopes:p===null?null:[...p],origin_run_id:null}}if(o.class==="cron"){const a=await bn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!a.allowed)throw new s(a.reason==="scope_missing"?403:401,a.reason==="scope_missing"?"scope_missing":"grant_revoked",a.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?null:[...o.scopes],origin_run_id:o.origin_run,armed_from_exec:o.from_exec}}if(o.class!=="broker")throw new s(401,"unauthorized","this caller cannot arm a cron");const i=r??await _(o.node),d=i?.grant_id??o.grant,u=i?.sub??o.sub,h=d!=null?await dn(d):await pn(n,u);if(h===null)throw new s(401,"grant_revoked","the node grant is not active");const l=i?.scopes??o.scopes;return{sub:h.sub,grant_id:h.id,scopes:l===null?null:[...l],origin_run_id:i?.run_id??o.run}}t(Pn,"armPrincipal");async function En(e){const n=e.body??{},r=E(n,"name"),o=E(n,"command"),i=Y(E(n,"fire_at"),"fire_at"),d=typeof n.recur=="string"&&n.recur!==""?Nn(n.recur):null,u=typeof n.expires_at=="string"&&n.expires_at!==""?Y(n.expires_at,"expires_at"):null,h=typeof n.env_json=="string"&&n.env_json!==""?jn(n.env_json):null,l=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,a=O(n.on_output,"on_output",["silent","on-failure","always","on-change"],l?.startsWith("node:")?"on-change":"on-failure"),p=O(n.overlap,"overlap",["skip","queue","replace"],"skip"),q=O(n.tier,"tier",["normal","urgent","critical"],"normal"),W=a==="always"||a==="on-change";if(W&&l===null)throw new c({error:"sink_required",message:`on_output "${a}" delivers stdout every qualifying run \u2014 it needs a sink`,field:"sink",next:"Pass sink node:<node-id> (a living node's inbox), spawn:<kind> (birth a fresh node per delivery), or human (the humanloop inbox)."});if(!W&&l!==null)throw new c({error:"sink_unused",message:`on_output "${a}" never delivers stdout \u2014 a sink would be dead config`,field:"sink",next:'Drop the sink, or use on_output "always"/"on-change" if delivery is the point.'});const Q=l!==null?await In(l):"{}";if(l?.startsWith("spawn:")&&(mn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const y=await yn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes,from_exec:e.principal.from_exec},{kind:l.slice(6)});if(!y.allowed)throw vn(y)}const g=P(e.principal)??"app:terminal",b=e.principal.class==="broker"?e.principal.node:e.principal.class==="cron"?null:typeof n.created_by=="string"&&n.created_by!==""?n.created_by:null;if(e.principal.class==="broker"&&n.created_by!=null&&n.created_by!==b)throw new s(403,"permission_error","a broker may only arm a cron as its own node");const m=b===null?null:await _(b);if(m!==null){if(m.grantee!==g)throw new c({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});H(m,"crtr:schedule"),l?.startsWith("spawn:")&&H(m,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new s(400,"invalid_request","isolated must be a boolean");if(m?.isolated&&n.isolated===!1)throw new s(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const x=m?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(x&&n.isolated===!1)throw new s(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const N=x||n.isolated===!0,f=await Pn(e,g,m),j=nn(),S=N?sn(g,j):null;if(S!==null&&n.cwd_is_default!==!0){const y=en(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?G(n.cwd):S);if(y===".."||y.startsWith(`..${F}`)||y.startsWith(F))throw new s(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const V=S??await An(n,g,b,Tn(n),N,{sub:f.sub,grantId:f.grant_id,scopes:f.scopes}),w=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(w!==null&&await _(w)!==null&&(await _(w)).grantee!==g)throw new c({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(w!==null&&await _(w)===null)throw new c({error:"anchor_not_found",message:`anchor node ${w} does not exist on the canvas`,field:"anchor_node",next:"Name a live node id, or drop the anchor \u2014 an unanchored cron outlives every node."});const B=n.cancel_on_wake===!0;if(B&&w===null)throw new c({error:"cancel_on_wake_needs_anchor",message:"cancel_on_wake couples the cron to a node's dormancy \u2014 it requires an anchor node",field:"anchor_node",next:"Pass anchor_node with the node whose wake should cancel this cron."});const $=n.run_timeout_s??300;if(typeof $!="number"||!Number.isInteger($)||$<=0)throw new c({error:"bad_run_timeout",message:`run_timeout_s must be a positive integer (got ${String(n.run_timeout_s)})`,field:"run_timeout_s",next:"Send the timeout in whole seconds, e.g. 300."});const T=typeof n.profile=="string"&&n.profile!==""?(await Sn(e,n.profile)).profileId:null,z=O(n.scope,"scope",["profile","global"],T!==null?"profile":"global");if(z==="profile"&&T===null)throw new c({error:"scope_needs_profile",message:'scope "profile" names the profile the cron lists and cancels within \u2014 it requires a profile',field:"profile",next:'Send a profile, or use scope "global" for a canvas-home-wide cron.'});return await rn({cron_id:j,name:r,created_by:b,grantee:g,sub:f.sub,grant_id:f.grant_id,scopes:f.scopes,origin_run_id:f.origin_run_id,armed_from_exec:f.armed_from_exec===!0,isolated:N,command:o,fire_at:i,recur:d,tz:typeof n.tz=="string"?n.tz:null,expires_at:u,anchor_node:w,cancel_on_wake:B,cwd:V,env_json:h,profile:T,scope:z,run_timeout_s:$,overlap:p,on_output:a,sink:Q,tier:q}),{status:201,body:k(await v(j),null)}}t(En,"handleArm");async function Un(e){J(e);const n=U(e),r=e.query.get("created_by");if(r==="")throw new s(400,"invalid_request","created_by must be nonempty when present");const o=await $n((n===null?await A({all:!0}):[...await A({profile:n}),...await A({global:!0})].sort((i,d)=>i.fire_at<d.fire_at?-1:i.fire_at>d.fire_at?1:0)).filter(i=>r===null||i.created_by===r),async i=>await R(e,i));return{status:200,body:await On(o,async i=>k(i,await D(i)))}}t(Un,"handleList");async function D(e){return(await L(e.cron_id))[0]??null}t(D,"lastRunOf");async function C(e){const n=e.params.cronId,r=await v(n);if(r===null||!await R(e,r))throw new s(404,"cron_not_found",`no cron ${n}`);if(!K(r,U(e)))throw new s(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(C,"requireCron");function I(e,n){const r=J(e);if(r!==null&&n.grantee!==r)throw new s(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(I,"requireOwnCron");async function Jn(e){const n=await C(e),r=await L(n.cron_id);return{status:200,body:{cron:k(n,r[0]??null),runs:r.map(M)}}}t(Jn,"handleShow");async function Rn(e){const n=I(e,await C(e));await Z(n.cron_id,"paused");const r=await v(n.cron_id);return{status:200,body:k(r,await D(r))}}t(Rn,"handlePause");async function Dn(e){const n=I(e,await C(e));await Z(n.cron_id,"active");const r=await v(n.cron_id);return{status:200,body:k(r,await D(r))}}t(Dn,"handleResume");async function Wn(e){const n=I(e,await C(e));if(n.run_state==="running")throw new s(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await _n(n,{outOfBand:!0});if(r===null)throw new s(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:M(r)}}t(Wn,"handleRun");async function xn(e){const n=new Date().toISOString();return{status:200,body:{unparked:await tn(n,J(e)),at:n}}}t(xn,"handlePoke");function Bn(e,n){const r=e.principal;if(r.class==="cron")return r.kind==="exec"?{kind:"canceled",by:r.app}:r.cron===n.cron_id?null:{kind:"canceled",by:`cron ${r.cron}`};if(r.class==="broker"){const i=on(n.sink);return i?.kind==="node"&&i.node===r.node?null:{kind:"canceled",by:`node ${r.node}`}}const o=P(r);return{kind:"canceled",by:o===null||o==="app:terminal"?"the person":o}}t(Bn,"cancelNotice");async function zn(e){const n=e.params.cronId,r=await v(n);if(r!==null&&!await R(e,r))throw new s(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!K(r,U(e)))throw new s(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);r!==null&&I(e,r);const o=e.query.get("run_id"),i=e.principal.class==="cron"?e.principal.kind==="cron"&&o===e.principal.execution&&n===e.principal.cron:!0,d=o!==null&&i&&hn(n,o);return r!==null&&await kn(r,d?null:Bn(e,r),{preserveRunningProcess:d}),{status:204}}t(zn,"handleCancel");const de=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:En},{method:"GET",pattern:"/v1/crons",handler:Un},{method:"GET",pattern:"/v1/crons/:cronId",handler:Jn},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:Rn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:Dn},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:xn},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Wn},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:zn}];export{de as cronRoutes};
|
|
1
|
+
var X=Object.defineProperty;var t=(e,n)=>X(e,"name",{value:n,configurable:!0});import{randomUUID as nn}from"node:crypto";import{relative as en,resolve as G,sep as F}from"node:path";import{ApiError as s}from"../../../api/errors.js";import{armCron as rn,parseCronSink as on,getCron as k,listCrons as A,listCronRuns as L,pokeHeldCrons as tn,setCronState as Z}from"../../../core/canvas/crons.js";import{getNode as p}from"../../../core/canvas/canvas.js";import{appSpace as an,cronFolder as sn}from"../../../core/layout.js";import{appProcessesSandboxed as cn,cwdIsMounted as ln}from"../../../core/spaces/process.js";import{activeGrant as dn,activeGrantForApp as un,grantFor as pn}from"../../../core/grants/mirror.js";import{callerTerminalGrant as fn}from"../../../core/grants/terminal.js";import{assertScope as H}from"../../../core/scopes.js";import{InputError as c}from"../../../core/io.js";import{nextSlotAfter as wn}from"../../../core/wake.js";import{executeCron as _n,markSelfCanceledCronRun as hn}from"../../cron-run.js";import{toCronDTO as v,toCronRunDTO as M}from"../map.js";import{appOf as P,assertCeiling as mn,canReadNode as gn}from"../router.js";import{checkCronBirth as yn,checkCronFire as bn,cronRefusalError as kn}from"../../cron/authority.js";import{endCron as vn}from"../../cron/ended.js";import{requireUsableProfile as Sn}from"./profiles.js";import{filterAsync as $n,mapAsync as On}from"../../../core/async-array.js";import{personId as Cn}from"../../../core/canvas/person.js";function E(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new c({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}t(E,"requireString");function O(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new c({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(O,"requireEnum");async function In(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new c({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(await p(n)===null)throw new c({error:"sink_node_not_found",message:`sink node ${n} does not exist on the canvas`,field:"sink",next:"Name a live node id, or use spawn:<kind> for a delivery that outlives any one node."});return JSON.stringify({node:n})}if(e.startsWith("spawn:")){const n=e.slice(6).trim();if(n==="")throw new c({error:"bad_sink",message:"spawn sink requires a node kind",field:"sink",next:'Use sink "spawn:<kind>" (e.g. spawn:general).'});return JSON.stringify({spawn:n})}throw new c({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(In,"parseSinkSpec");const qn=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function Y(e,n){if(!qn.test(e)||Number.isNaN(Date.parse(e)))throw new c({error:`bad_${n}`,message:`"${e}" is not a UTC ISO instant`,field:n,next:'Send a resolved UTC instant like "2026-06-07T09:00:00.000Z" (the client resolves the time grammar).'});return e}t(Y,"requireUtcIso");function Nn(e){try{wn(e,new Date)}catch(n){throw new c({error:"bad_recur",message:`recur is not advanceable: ${n instanceof Error?n.message:String(n)}`,field:"recur",next:'Send recur JSON the daemon can advance: {"every":"6h"} or {"cron":"0 9 * * *","tz":"America/New_York"}.'})}return e}t(Nn,"requireRecur");function jn(e){let n;try{n=JSON.parse(e)}catch{throw new c({error:"bad_env_json",message:"env_json is not valid JSON",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'})}if(typeof n!="object"||n===null||Array.isArray(n))throw new c({error:"bad_env_json",message:"env_json must be a JSON object",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'});for(const[r,o]of Object.entries(n))if(typeof o!="string")throw new c({error:"bad_env_json",message:`env_json value for "${r}" is not a string`,field:"env_json",next:"Every env value must be a string."});return e}t(jn,"requireEnvJson");function U(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(U,"callerProfile");function K(e,n){return n===null||e.scope==="global"||e.profile===n}t(K,"inScope");function J(e){if(e.principal.class==="person"&&e.principal.sub!==void 0)throw new s(403,"permission_error","schedules are managed by the install's person, a node or an app");return e.principal.class==="person"?null:P(e.principal)}t(J,"callerApp");async function R(e,n){if(!await gn(e.principal,n.grantee))return!1;const r=e.principal,o=r.class==="broker"?await p(r.node):r.class==="cron"?{run_id:r.origin_run,isolated:r.isolated}:r.class==="forwarded"&&r.actor.kind==="app"?{run_id:r.actor.place.run,isolated:r.actor.place.isolated}:null;return!(o!==null&&(o.isolated||n.isolated)&&o.run_id!==n.origin_run_id)}t(R,"visibleTo");function Tn(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(Tn,"anchorNodeOf");async function An(e,n,r,o,i,d){const u=typeof e.cwd=="string"&&e.cwd.trim()!==""?G(e.cwd):null;if(!cn()){if(u===null)throw new c({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return u}const h=o!==null&&await p(o)!==null?o:void 0,l=t(async q=>await ln(n,q,h,i,void 0,d),"reaches");if(u!==null&&await l(u))return u;const a=an(n).files;if(u!==null&&e.cwd_is_default!==!0)throw new s(400,"invalid_request",`cwd ${u} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${a}`,{cwd:u,default:a},void 0,{param:"cwd"});const f=r===null?null:(await p(r))?.cwd??null;return f!==null&&await l(f)?f:a}t(An,"resolveArmedCwd");async function Pn(e,n,r){const o=e.principal;if(o.class==="person"){if(o.sub!==void 0)throw new s(403,"permission_error","schedules are managed by the install's person, a node or an app");const a=await fn();return{sub:a?.sub??Cn(),grant_id:a?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const a=await un(n);if(a===null)throw new s(401,"grant_revoked",`no active grant for ${n}`);const f=e.ceiling??o.scopes;return{sub:a.sub,grant_id:a.id,scopes:f===null?null:[...f],origin_run_id:null}}if(o.class==="cron"){const a=await bn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!a.allowed)throw new s(a.reason==="scope_missing"?403:401,a.reason==="scope_missing"?"scope_missing":"grant_revoked",a.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?null:[...o.scopes],origin_run_id:o.origin_run,armed_from_exec:o.from_exec}}if(o.class!=="broker")throw new s(401,"unauthorized","this caller cannot arm a cron");const i=r??await p(o.node),d=i?.grant_id??o.grant,u=i?.sub??o.sub,h=d!=null?await dn(d):await pn(n,u);if(h===null)throw new s(401,"grant_revoked","the node grant is not active");const l=i?.scopes??o.scopes;return{sub:h.sub,grant_id:h.id,scopes:l===null?null:[...l],origin_run_id:i?.run_id??o.run}}t(Pn,"armPrincipal");async function En(e){const n=e.body??{},r=E(n,"name"),o=E(n,"command"),i=Y(E(n,"fire_at"),"fire_at"),d=typeof n.recur=="string"&&n.recur!==""?Nn(n.recur):null,u=typeof n.expires_at=="string"&&n.expires_at!==""?Y(n.expires_at,"expires_at"):null,h=typeof n.env_json=="string"&&n.env_json!==""?jn(n.env_json):null,l=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,a=O(n.on_output,"on_output",["silent","on-failure","always","on-change"],l?.startsWith("node:")?"on-change":"on-failure"),f=O(n.overlap,"overlap",["skip","queue","replace"],"skip"),q=O(n.tier,"tier",["normal","urgent","critical"],"normal"),W=a==="always"||a==="on-change";if(W&&l===null)throw new c({error:"sink_required",message:`on_output "${a}" delivers stdout every qualifying run \u2014 it needs a sink`,field:"sink",next:"Pass sink node:<node-id> (a living node's inbox), spawn:<kind> (birth a fresh node per delivery), or human (the humanloop inbox)."});if(!W&&l!==null)throw new c({error:"sink_unused",message:`on_output "${a}" never delivers stdout \u2014 a sink would be dead config`,field:"sink",next:'Drop the sink, or use on_output "always"/"on-change" if delivery is the point.'});const Q=l!==null?await In(l):"{}";if(l?.startsWith("spawn:")&&(mn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const y=await yn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes,from_exec:e.principal.from_exec},{kind:l.slice(6)});if(!y.allowed)throw kn(y)}const g=P(e.principal)??"app:terminal",b=e.principal.class==="broker"?e.principal.node:e.principal.class==="cron"?null:typeof n.created_by=="string"&&n.created_by!==""?n.created_by:null;if(e.principal.class==="broker"&&n.created_by!=null&&n.created_by!==b)throw new s(403,"permission_error","a broker may only arm a cron as its own node");const m=b===null?null:await p(b);if(m!==null){if(m.grantee!==g)throw new c({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});H(m,"crtr:schedule"),l?.startsWith("spawn:")&&H(m,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new s(400,"invalid_request","isolated must be a boolean");if(m?.isolated&&n.isolated===!1)throw new s(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const x=m?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(x&&n.isolated===!1)throw new s(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const N=x||n.isolated===!0,w=await Pn(e,g,m),j=nn(),S=N?sn(g,j):null;if(S!==null&&n.cwd_is_default!==!0){const y=en(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?G(n.cwd):S);if(y===".."||y.startsWith(`..${F}`)||y.startsWith(F))throw new s(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const V=S??await An(n,g,b,Tn(n),N,{sub:w.sub,grantId:w.grant_id,scopes:w.scopes}),_=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(_!==null&&await p(_)!==null&&(await p(_)).grantee!==g)throw new c({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(_!==null&&await p(_)===null)throw new c({error:"anchor_not_found",message:`anchor node ${_} does not exist on the canvas`,field:"anchor_node",next:"Name a live node id, or drop the anchor \u2014 an unanchored cron outlives every node."});const B=n.cancel_on_wake===!0;if(B&&_===null)throw new c({error:"cancel_on_wake_needs_anchor",message:"cancel_on_wake couples the cron to a node's dormancy \u2014 it requires an anchor node",field:"anchor_node",next:"Pass anchor_node with the node whose wake should cancel this cron."});const $=n.run_timeout_s??300;if(typeof $!="number"||!Number.isInteger($)||$<=0)throw new c({error:"bad_run_timeout",message:`run_timeout_s must be a positive integer (got ${String(n.run_timeout_s)})`,field:"run_timeout_s",next:"Send the timeout in whole seconds, e.g. 300."});const T=typeof n.profile=="string"&&n.profile!==""?(await Sn(e,n.profile)).profileId:null,z=O(n.scope,"scope",["profile","global"],T!==null?"profile":"global");if(z==="profile"&&T===null)throw new c({error:"scope_needs_profile",message:'scope "profile" names the profile the cron lists and cancels within \u2014 it requires a profile',field:"profile",next:'Send a profile, or use scope "global" for a canvas-home-wide cron.'});return await rn({cron_id:j,name:r,created_by:b,grantee:g,sub:w.sub,grant_id:w.grant_id,scopes:w.scopes,origin_run_id:w.origin_run_id,armed_from_exec:w.armed_from_exec===!0,isolated:N,command:o,fire_at:i,recur:d,tz:typeof n.tz=="string"?n.tz:null,expires_at:u,anchor_node:_,cancel_on_wake:B,cwd:V,env_json:h,profile:T,scope:z,run_timeout_s:$,overlap:f,on_output:a,sink:Q,tier:q}),{status:201,body:v(await k(j),null)}}t(En,"handleArm");async function Un(e){J(e);const n=U(e),r=e.query.get("created_by");if(r==="")throw new s(400,"invalid_request","created_by must be nonempty when present");const o=await $n((n===null?await A({all:!0}):[...await A({profile:n}),...await A({global:!0})].sort((i,d)=>i.fire_at<d.fire_at?-1:i.fire_at>d.fire_at?1:0)).filter(i=>r===null||i.created_by===r),async i=>await R(e,i));return{status:200,body:await On(o,async i=>v(i,await D(i)))}}t(Un,"handleList");async function D(e){return(await L(e.cron_id))[0]??null}t(D,"lastRunOf");async function C(e){const n=e.params.cronId,r=await k(n);if(r===null||!await R(e,r))throw new s(404,"cron_not_found",`no cron ${n}`);if(!K(r,U(e)))throw new s(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(C,"requireCron");function I(e,n){const r=J(e);if(r!==null&&n.grantee!==r)throw new s(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(I,"requireOwnCron");async function Jn(e){const n=await C(e),r=await L(n.cron_id);return{status:200,body:{cron:v(n,r[0]??null),runs:r.map(M)}}}t(Jn,"handleShow");async function Rn(e){const n=I(e,await C(e));await Z(n.cron_id,"paused");const r=await k(n.cron_id);return{status:200,body:v(r,await D(r))}}t(Rn,"handlePause");async function Dn(e){const n=I(e,await C(e));await Z(n.cron_id,"active");const r=await k(n.cron_id);return{status:200,body:v(r,await D(r))}}t(Dn,"handleResume");async function Wn(e){const n=I(e,await C(e));if(n.run_state==="running")throw new s(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await _n(n,{outOfBand:!0});if(r===null)throw new s(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:M(r)}}t(Wn,"handleRun");async function xn(e){const n=new Date().toISOString();return{status:200,body:{unparked:await tn(n,J(e)),at:n}}}t(xn,"handlePoke");function Bn(e,n){const r=e.principal;if(r.class==="cron")return r.kind==="exec"?{kind:"canceled",by:r.app}:r.cron===n.cron_id?null:{kind:"canceled",by:`cron ${r.cron}`};if(r.class==="broker"){const i=on(n.sink);return i?.kind==="node"&&i.node===r.node?null:{kind:"canceled",by:`node ${r.node}`}}const o=P(r);return{kind:"canceled",by:o===null||o==="app:terminal"?"the person":o}}t(Bn,"cancelNotice");async function zn(e){const n=e.params.cronId,r=await k(n);if(r!==null&&!await R(e,r))throw new s(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!K(r,U(e)))throw new s(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);r!==null&&I(e,r);const o=e.query.get("run_id"),i=e.principal.class==="cron"?e.principal.kind==="cron"&&o===e.principal.execution&&n===e.principal.cron:!0,d=o!==null&&i&&hn(n,o);return r!==null&&await vn(r,d?null:Bn(e,r),{preserveRunningProcess:d}),{status:204}}t(zn,"handleCancel");const de=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:En},{method:"GET",pattern:"/v1/crons",handler:Un},{method:"GET",pattern:"/v1/crons/:cronId",handler:Jn},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:Rn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:Dn},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:xn},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Wn},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:zn}];export{de as cronRoutes};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Cron } from '../../core/canvas/crons.js';
|
|
2
2
|
import type { CronSinkResolution } from './sinks.js';
|
|
3
|
-
/** The notice for a paused
|
|
4
|
-
* otherwise a human ticket. `message` is the refusal
|
|
3
|
+
/** The notice for a cron paused by a refused fire: mail to the node that armed it while that node can still take mail,
|
|
4
|
+
* otherwise a human ticket. `message` is the refusal. */
|
|
5
5
|
export declare function narrowedNotice(c: Cron, message: string): Promise<CronSinkResolution>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var i=Object.defineProperty;var a=(e,
|
|
2
|
-
`),t=`\u23F0 cron ${e.name} paused:
|
|
1
|
+
var i=Object.defineProperty;var a=(e,n)=>i(e,"name",{value:n,configurable:!0});import{getNode as s}from"../../core/canvas/canvas.js";import{newNodeId as u}from"../../core/runtime/nodes.js";import{resolveLiveProfileName as l}from"./armed-context.js";import{canTakeMail as c}from"./ended.js";async function _(e,n){const o=e.created_by===null?null:await s(e.created_by),r=[`Cron "${e.name}" (${e.cron_id}) was paused: ${n}.`,"","Its scheduled fire was refused by the authority check, so it did not run. The cron will not fire again until you act. Nothing was widened.","",`- To keep it: fix what the refusal names (for a lost scope, restore it on the grant), then \`crtr cron resume ${e.cron_id}\`.`,`- To drop it: \`crtr cron cancel ${e.cron_id}\`, and arm the cron again if the work still matters.`].join(`
|
|
2
|
+
`),t=`\u23F0 cron ${e.name} paused: fire refused`,d=e.cancel_on_wake&&e.anchor_node===o?.node_id;return o!==null&&!d&&await c(o)?{kind:"node",destinationNodeId:o.node_id,senderNodeId:null,label:t,stdout:r,urgency:"urgent"}:{kind:"action",adapter:{adapter:"cron_human",cronId:e.cron_id,requestId:await u(),title:t,summary:"A scheduled cron was paused because its fire was refused.",sessionName:`cron ${e.name} (${e.cron_id})`,profileName:l(e)??null},bodyJson:{stdout:r}}}a(_,"narrowedNotice");export{_ as narrowedNotice};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var p=Object.defineProperty;var c=(e,u)=>p(e,"name",{value:u,configurable:!0});import{consumeCron as w,recordCronRunLocked as _,releaseCronRunLease as y,setCronState as k}from"../../core/canvas/crons.js";import{withCanvasWrite as I}from"../../core/canvas/db.js";import{enqueueDaemonAction as b}from"../../core/canvas/daemon-actions.js";import{enqueueNodeMail as v}from"../../core/feed/messages.js";import{emitEvent as g}from"../../core/events/emit.js";import{narrowedNotice as C}from"./narrowed.js";import{disposeSettledRun as h}from"./disposition.js";function x(e){return typeof e=="object"&&(e.kind==="park"||e.kind==="redue")}c(x,"occurrenceStaysOwed");async function $(e,u){const{outOfBand:
|
|
1
|
+
var p=Object.defineProperty;var c=(e,u)=>p(e,"name",{value:u,configurable:!0});import{consumeCron as w,recordCronRunLocked as _,releaseCronRunLease as y,setCronState as k}from"../../core/canvas/crons.js";import{withCanvasWrite as I}from"../../core/canvas/db.js";import{enqueueDaemonAction as b}from"../../core/canvas/daemon-actions.js";import{enqueueNodeMail as v}from"../../core/feed/messages.js";import{emitEvent as g}from"../../core/events/emit.js";import{narrowedNotice as C}from"./narrowed.js";import{disposeSettledRun as h}from"./disposition.js";function x(e){return typeof e=="object"&&(e.kind==="park"||e.kind==="redue")}c(x,"occurrenceStaysOwed");async function $(e,u){const{outOfBand:a,outcome:d,record:r,lease:s}=u;let i,t,n;if(d.kind==="disposed"){const o=await h(e,d.ctx,a,d.selfCanceled??!1);i=o.delivered,t=o.paused,n=o.obligation}else if(d.kind==="refused"){i=`refused: ${d.reason}`;const o=!a&&d.reason==="scope_narrowed";t=!a&&(e.recur==null||o),t&&await k(e.cron_id,"paused"),t&&(n=await C(e,d.message)),g({level:"warn",event:"cron.fire.refused",fields:{cron_id:e.cron_id,execution_id:r.runId,grant_id:e.grant_id,reason:d.reason,out_of_band:a}})}else i=d.delivered,t=d.paused??!1;const m=await I(async o=>{if(n!==void 0){if(n.kind==="node"){const f=await v(n.destinationNodeId,{kind:"message",label:n.label,senderNodeId:n.senderNodeId,bodyJson:{body:n.stdout},urgency:n.urgency,wake:1,idempotencyKey:r.runId});d.kind!=="refused"&&(i=`delivered: ${f.deliveryId}`)}else if(n.kind==="action"){const f=await b({adapter:n.adapter,bodyJson:n.bodyJson,idempotencyKey:r.runId});d.kind!=="refused"&&(i=`delivered: ${f}`)}}const l={run_id:r.runId,cron_id:e.cron_id,started:r.startedAtIso,finished:new Date(r.finishedAtMs).toISOString(),duration_ms:r.durationMs,exit_code:r.exitCode,stdout_head:r.stdoutHead,stderr_head:r.stderrHead,delivered:i};return await _(o,l),l});return s!=="none"&&await y(e.cron_id,r.runId,s==="release"?void 0:s),!a&&e.recur==null&&!t&&!x(s)&&await w(e.cron_id),m}c($,"settleRun");export{$ as settleRun};
|
package/dist/launcher/run.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var W=Object.defineProperty;var h=(L,k)=>W(L,"name",{value:k,configurable:!0});import{spawn as X}from"node:child_process";import{createHash as Y}from"node:crypto";import{chownSync as P,chmodSync as $,closeSync as Z,existsSync as ee,mkdirSync as F,readFileSync as te,rmSync as re,statSync as ne}from"node:fs";import{createServer as oe}from"node:net";import{dirname as J,join as M}from"node:path";import{fileURLToPath as se}from"node:url";import{loadLinuxNative as ie}from"../native/linux.js";import{envHomeOverride as ae}from"../shared/env.js";import{launchSandbox as de}from"./bwrap.js";import{LaunchGate as ce}from"./launch-gate.js";import{serveAccepted as ue}from"./accepted-socket.js";import{installNetworkRule as pe}from"./netrule.js";import{LAUNCHER_SOCKET as S,LaunchRefused as u}from"./spec.js";import{systemIdentity as le,AppUsers as fe}from"./users.js";import{SourceValidator as me}from"./validate.js";import{prepareDaemonScopeDirectories as he}from"./scope-directories.js";import{LimitsManager as we}from"./limits/manager.js";import{CRTRD_OOM_SCORE_ADJ as ge}from"./limits/policy.js";import{writeLimitsLog as ye}from"./limits/log.js";const U=ie();function j(L){const k=L._handle?.fd;if(k===void 0||k<0)throw new Error("launcher socket fd unavailable");return k}h(j,"socketFd");async function Ae(L={}){const k=L.superviseDaemon??!0;if(process.platform!=="linux"||process.getuid?.()!==0||!U)throw new Error("launcher requires Linux root and its native addon");const{daemonUid:T,daemonGid:x,dataGid:G}=le(),E=J(S);if(F(E,{recursive:!0,mode:493}),(ne(E).mode&511)!==493)throw new Error(`${E} must be mode 0755`);P(E,T,x),process.umask(7);const O=M(E,"nodes");F(O,{recursive:!0,mode:504}),P(O,0,x),$(O,504);const v=new me(ae()||"/",x);he(M(v.root,"runtime"),T,x);const b=new fe(G);pe();const d=new we({runtimeRoot:v.runtime,appsRoot:M(v.root,"apps"),stateDir:M(v.runtime,"limits-state"),dataGid:G,users:b});d.init();const B=new Map,C=new Map,q=h(r=>{const p=d.takeEvents();for(const o of p)ye(o);return p.length===0?r:{...r,events:p}},"reply_"),D=new ce,I=new Map,R=new Map,V=h((r,p)=>{const o=p.capture,c=Y("sha256"),w=h((l,i)=>{let f=0,a=!1;const m=[];return{take(g){i?.update(g);const _=Math.max(0,Math.min(g.length,l-f));_&&(m.push(g.subarray(0,_)),f+=_),_!==g.length&&(a=!0)},result:h(()=>({text:Buffer.concat(m).toString("utf8"),truncated:a}),"result")}},"chunks"),e=w(o.stdoutBytes,c),s=w(o.stderrBytes);let t=Buffer.alloc(0),n=0;return r.stdout?.on("data",l=>e.take(l)),r.stderr?.on("data",l=>{const i=l;o.stderrTail?(n+=i.length,t=i.length>=o.stderrBytes?i.subarray(i.length-o.stderrBytes):Buffer.concat([t,i]).subarray(Math.max(0,t.length+i.length-o.stderrBytes))):s.take(i)}),new Promise(l=>r.once("close",(i,f)=>{const a=e.result(),m=o.stderrTail?{text:t.toString("utf8"),truncated:n>o.stderrBytes}:s.result();l({exitCode:i,signal:f,stdout:a.text,stderr:m.text,stdoutTruncated:a.truncated,stderrTruncated:m.truncated,stdoutSha256:c.digest("hex")})}))},"captureExit"),z=h(r=>{const p=new Set;for(;r>1&&!p.has(r);){const o=I.get(r);if(o)return o;p.add(r);try{const c=te(`/proc/${r}/status`,"utf8");r=Number(/^PPid:\s*(\d+)/m.exec(c)?.[1]??0)}catch{return null}}return null},"sandboxForPid");let y,N=!1;const Q=M(J(se(import.meta.url)),"../daemon/crtrd-cli.js"),A=h(()=>{N||(y=X("/bin/setpriv",["--reuid",String(T),"--regid",String(x),"--groups",String(G),"--",process.execPath,Q,...process.argv.slice(2)],{stdio:"inherit",env:{...process.env,CRTR_LAUNCHER_SOCKET:S,CRTR_HOME:v.root}}),y.pid&&d.protectDaemon(y.pid,ge),y.once("exit",()=>{y=void 0,N||A()}),y.once("error",r=>{console.error("crtrd spawn failed:",r),process.exitCode=1,N=!0}))},"startDaemon");ee(S)&&re(S);const K=oe({pauseOnConnect:!0},r=>ue(r,p=>{const o=[];return(async()=>{try{const c=U.peerCred(j(r)).uid;if(c!==T&&c!==0)throw new u("invalid_spec","launcher peer is not crtrd or root");let w;for(;;)try{w=U.recvFds(j(r));break}catch(t){if(t.code!=="EINTR")throw t}p(),o.push(...w.fds);const e=JSON.parse(w.data);let s;switch(e.operation){case"launch":{if(o.length>1)throw new u("invalid_uid","app uid is not registered");let t,n,l;try{n=await D.launch(async()=>{if(!b.has(e.spec.uid))throw new u("invalid_uid","app uid is not registered");const i=l=d.prepareRun(e.spec.uid,e.spec.nodeId,e.spec.broker===!0);return de(e.spec,o[0],v,G,f=>{if(!f.pid)throw new u("launch_failed","bwrap has no pid");t=f.pid;const a=f.pid;if(I.set(a,{child:f,uid:e.spec.uid,nodeId:e.spec.nodeId,mounts:e.spec.mounts,plan:i}),f.once("exit",(m,g)=>{I.delete(a),C.set(a,{code:m,signal:g});const _=d.finishRun(i);B.set(a,_.oomKilled),setTimeout(()=>{B.delete(a),C.delete(a)},5*6e4).unref()}),e.spec.capture){const m=V(f,e.spec).then(g=>({...g,oomKilled:B.get(a)===!0}));R.set(a,m),m.then(()=>{setTimeout(()=>{R.get(a)===m&&R.delete(a)},3e5).unref()})}},i)})}catch(i){throw t!==void 0?R.delete(t):l&&d.finishRun(l),i}if(!n.pid)throw new u("launch_failed","bwrap has no pid");d.confirmStarted(l,n.pid),s={pid:n.pid};break}case"wait":{const t=R.get(e.pid);if(!t)throw new u("invalid_spec","unknown captured sandbox pid");const n=await t;r.writable&&R.delete(e.pid),s={result:n};break}case"stop":{const t=Number.isInteger(e.pid)?z(e.pid):null;if(!t||e.nodeId!==void 0&&t.nodeId!==e.nodeId)throw new u("invalid_spec","unknown sandbox pid");if(e.signal!==void 0&&e.signal!=="SIGTERM"&&e.signal!=="SIGKILL")throw new u("invalid_spec","invalid stop signal");const n=e.signal??"SIGKILL";t.child.kill(n),n==="SIGKILL"&&d.killRun(t.plan),s={ok:!0};break}case"user.create":s={uid:await D.write(()=>b.create(e.app,e.uid))};break;case"user.delete":{await D.write(()=>{if([...I.values()].some(t=>t.uid===e.uid))throw new u("invalid_spec","stop this app\u2019s sandboxes before deleting its user");b.delete(e.uid)}),s={ok:!0};break}case"users.sync":await D.write(()=>b.sync(e.users)),s={ok:!0};break;case"list":s={sandboxes:[...I].map(([t,n])=>({pid:t,uid:n.uid,nodeId:n.nodeId,mounts:n.mounts}))};break;case"limits.apply":{const t=b.entries().find(n=>n.app===e.app.replace(/^app:/,""));if(!t)throw new u("invalid_uid","app is not registered");d.applyApp(t.app,t.uid,!0),s={ok:!0};break}case"memory.admit":{const t=b.entries().find(n=>n.app===e.app.replace(/^app:/,""));if(!t)throw new u("invalid_uid","app is not registered");s=d.admitBroker(t.app,e.nodeId);break}case"limits.show":s={limits:d.show()};break;case"disk.usage":s={disk:await d.diskUsageReport()};break;case"cache.purge":s={purged:await d.cachePass()};break;case"exit.status":s={oomKilled:B.get(e.pid)===!0,code:C.get(e.pid)?.code??null,signal:C.get(e.pid)?.signal??null};break;case"daemon.handover":y?.kill("SIGTERM"),s={ok:!0};break;default:throw new u("invalid_spec","unknown launcher operation")}return`${JSON.stringify(q(s))}
|
|
2
2
|
`}catch(c){const w=c instanceof u?c.refusal:"launch_failed";return console.error(`launcher refusal ${w}:`,c),`${JSON.stringify({refusal:w})}
|
|
3
|
-
`}finally{for(const c of o)
|
|
3
|
+
`}finally{for(const c of o)Z(c)}})()}));await new Promise((r,p)=>{K.once("error",p),K.listen(S,r)}),P(S,T,x),$(S,384);const H=h(()=>{N=!0,y?.kill("SIGTERM");for(const{child:r}of I.values())r.kill("SIGKILL");K.close(),v.close()},"shutdown");process.once("SIGTERM",H),process.once("SIGINT",H),k&&A()}h(Ae,"runLauncher");export{Ae as runLauncher};
|
package/dist/launcher/spec.d.ts
CHANGED
|
@@ -75,7 +75,7 @@ export type LauncherRequest = {
|
|
|
75
75
|
| {
|
|
76
76
|
operation: 'cache.purge';
|
|
77
77
|
}
|
|
78
|
-
/**
|
|
78
|
+
/** How the sandbox with this first-process pid ended: whether the kernel OOM-killed its run, and the code and signal its first process exited with. */
|
|
79
79
|
| {
|
|
80
80
|
operation: 'exit.status';
|
|
81
81
|
pid: number;
|
|
@@ -167,6 +167,8 @@ export type LauncherReply = ({
|
|
|
167
167
|
purged: CachePurged[];
|
|
168
168
|
} | {
|
|
169
169
|
oomKilled: boolean;
|
|
170
|
+
code: number | null;
|
|
171
|
+
signal: NodeJS.Signals | null;
|
|
170
172
|
}) & {
|
|
171
173
|
events?: LimitEvent[];
|
|
172
174
|
} | {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
var U=Object.defineProperty;var f=(s,t)=>U(s,"name",{value:t,configurable:!0});import{envInboxBetweenTurns as q,envNodeId as B,envProfileId as J,envWatcherDebounceMs as W,envWatcherTickMs as $,envWatcherBackstopMs as G}from"../shared/env.js";import{coalesceBrokerInbox as X,entryCards as j}from"../core/runtime/broker/inbox.js";import{acknowledgeBrokerMail as z,claimBrokerMail as L,brokerExtensionState as F}from"../core/runtime/broker/daemon-ops.js";import{materializeSpilledBody as V}from"../core/runtime/broker/spilled-body.js";import{brokerSignalClient as S}from"../core/runtime/broker/signal-stream.js";import{wasTurnRefused as Q}from"../core/runtime/broker/extension-abort.js";import{isDaemonTransportApiError as R,isTokenRevokedApiError as Y}from"../api/errors.js";import{announceMessageOrigin as K,messageSignature as _}from"../core/runtime/stamp/channel.js";import{captureGoalIfAbsent as Z,generateAndCommitInitialName as ee}from"./broker-local.js";import{emitEvent as O}from"../core/events/emit.js";import{errorClassFromError as te}from"../core/events/errors.js";import{operationIdContext as m}from"../core/events/operation-id.js";import{SITUATIONAL_CONTEXT_CUSTOM_TYPE as I}from"../shared/generated-context.js";import{isProfilePaused as ie}from"../core/profiles/manifest.js";import{brokerDisposeAndExit as N,brokerIdleTurnStart as ne,brokerRunTurnBusy as re,HELD_DEFERRED_INBOX_PROMPT_JOIN as se}from"../core/runtime/broker/held-deferred-inbox.js";const b=64,A=16;let p,ae=0;class D extends Error{static{f(this,"WatcherFatalError")}constructor(t,e){super(t,{cause:e}),this.name="WatcherFatalError"}}function de(){return{tickMs:$(),debounceMs:W()}}f(de,"resolveCadence");function E(){const s=B();return s===void 0||s.trim()===""?void 0:s}f(E,"currentNodeId");function P(){const s=J();return s!==void 0&&s!==""&&ie(s)}f(P,"profilePaused");function k(s){try{return te(s)}catch{return{class:"unknown"}}}f(k,"safeErrorClass");function v(s){try{O(s)}catch{}}f(v,"emitBestEffort");function w(s){try{m.fresh(t=>{v({...s,operation_id:t})})}catch{}}f(w,"emitFreshBestEffort");function C(s,t){try{m.run(s,()=>{v({...t,operation_id:s})})}catch{}}f(C,"emitStoredBestEffort");function oe(s){return s.kind==="human"?{type:"human"}:{type:"runtime",kind:"inbox",entries:s.entries.map(t=>({...t.from===null?{}:{from:t.from},...t.from_name===void 0?{}:{fromName:t.from_name},kind:t.kind,tier:t.tier,...t.ref===void 0?{}:{ref:t.ref}}))}}f(oe,"deliveryOrigin");function x(s,t){if(s.length===0)throw new Error(`cannot create an empty ${t} delivery`);const e=[...new Set(s.map(r=>r.operation_id))].sort(),i=e.length-A;return{channel:t,entryIds:s.map(r=>r.entry_id),throughEntryId:s.at(-1).entry_id,sourceOperationIds:e.slice(0,A),...i>0?{sourceOperationIdsOmitted:i}:{}}}f(x,"deliveryFacts");function y(s){return{channel:s.channel,entry_ids:s.entryIds,through_entry_id:s.throughEntryId,source_operation_ids:s.sourceOperationIds,...s.sourceOperationIdsOmitted===void 0?{}:{source_operation_ids_omitted:s.sourceOperationIdsOmitted}}}f(y,"deliveryFields");function le(s){C(s.operationId,{level:"info",event:"inbox.delivery.completed",outcome:"succeeded",fields:y(s)})}f(le,"emitDeliveryCompleted");function T(s,t,e,i){C(s,{level:"error",event:"inbox.delivery.failed",outcome:"failed",error:i,error_class:k(i),fields:{stage:e,...y(t)}})}f(T,"emitDeliveryFailure");function M(s,t){C(s.operationId,{level:"error",event:"inbox.delivery.failed",outcome:"failed",error_class:{class:"unknown"},fields:{stage:"abandoned",reason:t,...y(s)}})}f(M,"emitDeliveryAbandoned");function fe(s){return typeof s=="string"?s:Array.isArray(s)?s.filter(t=>typeof t=="object"&&t!==null&&typeof t.text=="string").map(t=>t.text).join(`
|
|
2
|
-
`):""}f(fe,"messageText");function H(s){return s==="quit"||s==="reload"||s==="new"||s==="resume"||s==="fork"}f(H,"isSessionShutdownReason");function he(s){switch(s){case"quit":return"shutdown";case"reload":case"new":case"resume":case"fork":return"reload";default:return s}}f(he,"disposalReasonForShutdown");function ue(s){return typeof s=="string"?s.slice(0,128):s===null?"null":s===void 0?"undefined":typeof s=="number"||typeof s=="boolean"||typeof s=="bigint"?String(s).slice(0,128):typeof s}f(ue,"boundedRuntimeReason");class ce{static{f(this,"CanvasInboxWatcher")}pi;cadence;generation;timer;backstop;debounceTimer;signalUnsubscribers=[];rerun=!1;retryNeeded=!1;polledOk=!1;armed=!1;lastCtx;streaming=!1;runNode=q();nodeId;claims=new Map;reportBodies=new Map;tickInFlight;buffer=[];lastArrival=0;pendingHandoffs=[];pendingCommit;promptJoinInFlight=!1;replayUnseenInherited=!1;seeded=!1;disposed=!1;disposalReason;fatalError;fatalEscaped=!1;constructor(t,e,i){this.pi=t,this.cadence=e,this.generation=i}start(){const t=f((e,i)=>{this.canMutate()&&this.captureCtx(e,i)},"capture");this.pi.on("session_start",(e,i)=>{this.canMutate()&&(this.captureCtx(e,i),this.arm())}),this.pi.on("turn_end",t),this.pi.on("agent_start",(e,i)=>{this.canMutate()&&(this.captureCtx(e,i),this.streaming=!0)}),this.pi.on("agent_end",(e,i)=>{this.canMutate()&&(this.captureCtx(e,i),this.streaming=!1)}),this.pi.on("message_end",e=>{if(!this.canMutate())return;const i=e?.message;if(i?.role!=="user"&&i?.role!=="custom"||!this.pendingHandoffs.some(d=>d.awaitingDequeue!==void 0))return;const r=_(fe(i.content)),n=this.pendingHandoffs.find(d=>d.awaitingDequeue===r);n!==void 0&&delete n.awaitingDequeue}),this.pi.on("agent_settled",async(e,i)=>{if(!this.canMutate())return;this.captureCtx(e,i);const r=E();if(r!==void 0)try{this.assertNodeOwnership(r,"settle"),await this.tickInFlight,this.seeded||await this.claimMail(r);const n=await this.settle(r,i,!1,!0);n==="refresh_held"&&this.dispose("refresh",!1),this.runNode&&n==="settled"&&this.canMutate()&&this.buffer.length>0&&await this.flushOrdinary(r),this.syncTimers()}catch(n){this.dispose("shutdown",!1,void 0,{error:n,stage:"settle"})}}),this.pi.on("session_shutdown",async e=>{if(!this.canMutate())return;let i;try{i=e?.reason}catch{i=void 0}const r=H(i)?he(i):"reload";H(i)||w({level:"warn",event:"inbox.lifecycle.unknown_shutdown_reason",outcome:"failed",fields:{reason:ue(i)}});try{await this.disposeAtSettlement(r,this.lastCtx)}catch(n){this.dispose(r,!1,void 0,{error:n,stage:"settle"})}})}arm(){if(this.armed)return;this.armed=!0;const t=S();this.signalUnsubscribers.push(t.subscribe("state",()=>this.syncTimers()),t.subscribe("mail",()=>this.runTick("schedule")),t.subscribe("resync",()=>this.runTick("schedule"))),this.syncTimers(),t.state!=="connected"&&this.runTick("schedule")}async disposeAtSettlement(t,e){const i=E();if(i===void 0){this.dispose(t,!1);return}try{this.assertNodeOwnership(i,"settle");const r=await this.settle(i,e,t!=="refresh");this.dispose(r==="refresh_held"?"refresh":t,!1)}catch(r){this.dispose(t,!1,void 0,{error:r,stage:"settle"})}}dispose(t,e,i,r){if(this.disposed)return;this.disposed=!0,this.disposalReason=t,this.clearTimer();for(const d of this.signalUnsubscribers.splice(0))d();const n=r;{const d=[...new Set([...this.pendingCommit?.handoffs??[],...this.pendingHandoffs.splice(0)])];this.buffer.length=0,this.claims.clear(),this.reportBodies.clear(),this.pendingCommit=void 0,this.promptJoinInFlight=!1,this.replayUnseenInherited=!1,this.nodeId=void 0,this.lastCtx=void 0,this.lastArrival=0,this.streaming=!1,this.seeded=!1,this.ownsCurrentGeneration()&&(p=void 0);for(const a of d)M(a,t)}if(n!==void 0){this.scheduleHandlerFatal(n.error,n.stage);return}}ownsCurrentGeneration(){return p===this&&p.generation===this.generation}canMutate(){return this.ownsCurrentGeneration()&&!this.disposed&&this.fatalError===void 0}captureCtx(t,e){e!==void 0&&(this.lastCtx=e)}clearTimer(){this.timer!==void 0&&clearInterval(this.timer),this.backstop!==void 0&&clearInterval(this.backstop),this.debounceTimer!==void 0&&clearTimeout(this.debounceTimer),this.timer=this.backstop=this.debounceTimer=void 0}syncTimers(){if(!this.armed||!this.canMutate())return;const t=S().state==="connected",e=!t||this.buffer.length>0||this.pendingCommit!==void 0||this.retryNeeded;e&&this.timer===void 0?(this.timer=setInterval(()=>this.runTick("schedule"),this.cadence.tickMs),this.timer.unref?.()):!e&&this.timer!==void 0&&(clearInterval(this.timer),this.timer=void 0),t&&this.backstop===void 0?(this.backstop=setInterval(()=>this.runTick("schedule"),G()),this.backstop.unref?.()):!t&&this.backstop!==void 0&&(clearInterval(this.backstop),this.backstop=void 0),this.buffer.length===0&&this.debounceTimer!==void 0&&(clearTimeout(this.debounceTimer),this.debounceTimer=void 0)}armDebounce(){this.debounceTimer!==void 0&&clearTimeout(this.debounceTimer);const t=Math.max(0,this.lastArrival+this.cadence.debounceMs-Date.now());this.debounceTimer=setTimeout(()=>{this.debounceTimer=void 0,this.runTick("schedule")},t),this.debounceTimer.unref?.()}isIdle(){if(this.runNode&&re())return!1;try{if(typeof this.lastCtx?.isIdle=="function")return this.lastCtx.isIdle()===!0}catch{}return!this.streaming}assertNodeOwnership(t,e){!this.seeded||this.nodeId===void 0||this.nodeId===t||this.raiseReadFatal(e,new Error(`watcher node changed from ${this.nodeId} to ${t}`))}raiseFatal(t,e,i,r){throw this.fatalError===void 0&&(this.fatalError=new D(e,i),this.clearTimer(),w({level:"error",event:t,outcome:"failed",error:i,error_class:k(i),fields:r})),this.fatalError}raiseReadFatal(t,e){return this.raiseFatal("inbox.read.failed",`inbox ${t} state is corrupt`,e,{stage:t})}raiseSituationalFatal(t,e){return this.raiseFatal("inbox.situational_context.missing","situational context marker has no readable sidecar",e,{entry_id:t})}escapeFatal(t,e){if(!this.fatalEscaped){if(this.fatalEscaped=!0,e==="throw")throw t;queueMicrotask(()=>{throw t})}}scheduleHandlerFatal(t,e){let i;if(t instanceof D)i=t;else try{this.raiseReadFatal(e,t)}catch(r){i=r}this.escapeFatal(i,"schedule")}runTick(t="throw"){if(!this.canMutate())return;if(this.tickInFlight!==void 0){this.rerun=!0;return}const e=this.tick().then(()=>{this.retryNeeded=!1,this.polledOk=!0}).catch(i=>{if(this.canMutate()){if(R(i)){this.retryNeeded=!0,w({level:"warn",event:"inbox.read.failed",outcome:"failed",error:i,error_class:k(i),fields:{stage:"poll"}});return}if(this.polledOk&&Y(i)){this.clearTimer(),w({level:"info",event:"inbox.poll.token_revoked",outcome:"succeeded",fields:{stage:"poll"}}),N()?.("token-revoked");return}this.scheduleHandlerFatal(i,"poll")}}).finally(()=>{this.tickInFlight===e&&(this.tickInFlight=void 0),this.syncTimers(),this.rerun&&this.canMutate()&&(this.rerun=!1,this.runTick("schedule"))});this.tickInFlight=e}async claimMail(t){this.assertNodeOwnership(t,"poll");let e;try{e=await L(t,"wake",this.reportBodies)}catch(a){throw R(a)&&(this.replayUnseenInherited=!0),a}if(!this.canMutate()||e.action==="hold")return!1;const i=this.replayUnseenInherited;this.replayUnseenInherited=!1,this.nodeId=t,this.seeded=!0;const r=new Set(e.deliveries.map(a=>a.entry.entry_id)),n=this.buffer.filter(a=>!r.has(a.entry_id));for(const a of n)this.claims.delete(a.entry_id);if(this.buffer.splice(0,this.buffer.length,...this.buffer.filter(a=>r.has(a.entry_id))),n.length>0){const a=globalThis[Symbol.for("@crouton-kit/crtr:watcher-cancel-probe")];typeof a=="function"&&a(n.map(l=>l.entry_id),process.hrtime.bigint())}let d=!1;for(const a of e.deliveries){const l=a.entry.entry_id;this.claims.has(l)||(a.entry=V(a.entry),this.claims.set(l,a),a.handoff==="inherited"&&!i?m.fresh(o=>{this.pendingHandoffs.push({...x([a.entry],"visible"),operationId:o,settleAtAgentSettled:!0})}):(this.buffer.push(a.entry),this.lastArrival=Date.now(),d=!0))}return d&&this.armDebounce(),this.syncTimers(),!0}async tick(){const t=E();if(t!==void 0){if(this.pendingCommit!==void 0){await this.retryPendingCommit(t)==="refresh_held"&&this.canMutate()&&this.dispose("refresh",!1);return}this.promptJoinInFlight||P()||this.pendingHandoffs.length>=b||await this.claimMail(t)&&this.buffer.length>0&&(!this.runNode||this.isIdle()||this.buffer.some(e=>e.tier==="critical"))&&Date.now()-this.lastArrival>=this.cadence.debounceMs&&await this.flushOrdinary(t)}}missingChannelEntries(t){const e=t.filter(n=>this.claims.has(n.entry_id)),i=this.coveredEntryIds("hidden"),r=this.coveredEntryIds("visible");return{hiddenEntries:e.filter(n=>(n.data?.situational===!0||n.data?.situationalOnly===!0)&&!i.has(n.entry_id)),visibleEntries:e.filter(n=>n.data?.situationalOnly!==!0&&!r.has(n.entry_id))}}visibleRoute(t){return t.length===0?"none":this.isIdle()?"idle":this.runNode?t.some(e=>e.tier==="critical")?"critical":"none":t.some(e=>e.tier==="critical")?"critical":t.some(e=>e.tier==="urgent"||e.kind==="final")?"steer":"followUp"}buildPlan(t){const{hiddenEntries:e,visibleEntries:i}=this.missingChannelEntries(t),r=new Set(e.map(u=>u.entry_id)),n=new Set(i.map(u=>u.entry_id)),d=t.filter(u=>r.has(u.entry_id)||n.has(u.entry_id)),a=this.isIdle()&&d.length>0&&d.every(u=>u.tier==="deferred"),l=this.visibleRoute(i)==="critical",o=!a&&!l&&e.length>0,c=!a&&!l&&i.length>0;return{hiddenEntries:e,visibleEntries:i,deferredHold:a,sendCount:Number(o)+Number(c)}}ordinaryPlan(){return this.buildPlan(this.buffer)}async contextCards(t,e){const i=[];if(e.some(r=>r.data?.situational===!0)){const r=e.at(-1).entry_id;let n;try{n=(await F(t)).situational_card}catch(d){this.raiseSituationalFatal(r,d)}n===null&&this.raiseSituationalFatal(r,new Error(`missing situational context for entry ${r}`)),i.push(n)}for(const r of e)r.data?.situationalOnly===!0&&i.push(...j(r));return i}digest(t,e){try{return X(t)}catch(i){this.raiseReadFatal(e,i)}}sendDelivery(t,e,i,r,n){this.pendingHandoffs.length>=b&&this.raiseReadFatal(r,new Error(`pending handoff capacity exceeded before ${t} send`));let d;try{d=x(e,t)}catch(o){this.raiseReadFatal(r,o)}let a;try{m.fresh(o=>{a=o,v({level:"info",event:"inbox.delivery.started",operation_id:o,fields:y(d)}),i()})}catch(o){return a===void 0&&this.raiseReadFatal(r,o),T(a,d,"send",o),"failed"}a===void 0&&this.raiseReadFatal(r,new Error(`${t} delivery returned without an operation id`));const l={operationId:a,...d,...n===void 0?{}:{awaitingDequeue:n}};return this.canMutate()?(this.pendingHandoffs.push(l),"handed-off"):(this.disposed&&this.disposalReason!==void 0&&M(l,this.disposalReason),"inactive")}sendContext(t,e,i,r,n,d){const a=r==="steer"||r==="followUp"?r:this.isIdle()?void 0:"followUp",l=a===void 0||i.length===0?void 0:_(i.at(-1));return this.sendDelivery("hidden",e,()=>{this.runNode&&n&&this.pi.appendEntry("crtr-turn-input",{message_ids:e.map(o=>o.entry_id)}),i.forEach((o,c)=>{const u=n&&c===i.length-1;this.pi.sendMessage({customType:I,content:o,display:!0,details:{nodeId:t}},a!==void 0?{deliverAs:a}:u?{deliverAs:"followUp",triggerTurn:!0}:{})})},d,l)}sendVisible(t,e,i,r,n,d){return this.sendDelivery("visible",t,()=>{if(n!=="idle"&&n!=="steer"&&n!=="followUp")throw new Error(`visible send requested for non-send route ${n}`);for(const a of e)this.pi.sendMessage({customType:I,content:a,display:!0},n==="idle"?{}:{deliverAs:n});if(K(i,r),n==="idle"){const a=ne();if(a===void 0)throw new Error("broker idle-turn admission is unavailable");a(i,this.runNode?t.map(l=>l.entry_id):[])}else this.pi.sendUserMessage(i,{deliverAs:n})},d,n==="steer"||n==="followUp"?_(i):void 0)}abortCritical(){try{this.lastCtx?.abort?.()}catch{}}requeueFromBatch(t,e){if(e.size===0)return;const i=t.filter(d=>e.has(d.entry_id)),r=new Set(i.map(d=>d.entry_id)),n=this.buffer.filter(d=>!r.has(d.entry_id));this.buffer.splice(0,this.buffer.length,...i,...n)}async executePlan(t,e,i){const r=new Set,n=this.visibleRoute(e.visibleEntries);if(n==="critical"){if(this.abortCritical(),!this.canMutate())return{inactive:!0,requeueEntryIds:r};for(const o of e.visibleEntries)r.add(o.entry_id);for(const o of e.hiddenEntries)r.add(o.entry_id);return{inactive:!1,requeueEntryIds:r}}if(this.runNode&&n==="none")return{inactive:!1,requeueEntryIds:new Set([...e.hiddenEntries,...e.visibleEntries].map(o=>o.entry_id))};const d=e.visibleEntries.length>0?this.digest(e.visibleEntries,i):[],[a,...l]=d;for(const o of l)for(const c of o.entries)r.add(c.entry_id);if(e.hiddenEntries.length>0){const o=await this.contextCards(t,e.hiddenEntries),c=this.sendContext(t,e.hiddenEntries,o,n,a===void 0,i);if(c==="inactive")return{inactive:!0,requeueEntryIds:r};if(c==="failed")for(const u of e.hiddenEntries)r.add(u.entry_id)}if(!this.canMutate())return{inactive:!0,requeueEntryIds:r};if(a!==void 0){const o=this.sendVisible(a.entries,a.cards,a.text,oe(a),n,i);if(o==="inactive")return{inactive:!0,requeueEntryIds:r};if(o==="failed")for(const c of a.entries)r.add(c.entry_id);else a.kind==="human"&&(await Z(t,a.text).catch(()=>{}),ee(t,a.text))}return{inactive:!1,requeueEntryIds:r}}async flushOrdinary(t){if(this.buffer.length===0)return"empty";const e=this.ordinaryPlan();if(e.deferredHold)return"held";if(this.pendingHandoffs.length+e.sendCount>b)return"capacity-blocked";const i=[...this.buffer],r=await this.executePlan(t,e,"poll");return r.inactive||(this.buffer.splice(0,i.length),this.requeueFromBatch(i,r.requeueEntryIds)),"attempted"}async prependHeldDeferredInbox(t){if(!this.canMutate()||this.nodeId===void 0||P())return;const e=this.ordinaryPlan();if(!e.deferredHold)return;const i=e.visibleEntries.length===0?[]:this.digest(e.visibleEntries,"poll"),r=+(e.hiddenEntries.length>0)+i.length;if(this.pendingHandoffs.length+r>b)return;const n=[...this.buffer];this.promptJoinInFlight=!0;try{this.runNode&&this.pi.appendEntry("crtr-turn-input",{message_ids:[...e.hiddenEntries,...e.visibleEntries].map(d=>d.entry_id)}),e.hiddenEntries.length>0&&await this.sendPromptJoinDelivery(t,"hidden",e.hiddenEntries,await this.contextCards(this.nodeId,e.hiddenEntries));for(const d of i)await this.sendPromptJoinDelivery(t,"visible",d.entries,[...d.cards,d.text]);if(!this.canMutate())return;this.buffer.splice(0,n.length)}finally{this.promptJoinInFlight=!1}}async sendPromptJoinDelivery(t,e,i,r){let n;try{n=x(i,e)}catch(l){this.raiseReadFatal("poll",l)}let d;try{await m.fresh(async l=>{d=l,v({level:"info",event:"inbox.delivery.started",operation_id:l,fields:y(n)});for(const o of r)await t.sendCustomMessage({customType:I,content:o,display:!0})})}catch(l){throw d===void 0&&this.raiseReadFatal("poll",l),T(d,n,"send",l),l}d===void 0&&this.raiseReadFatal("poll",new Error(`${e} prompt-join delivery returned without an operation id`));const a={operationId:d,...n,settleAtAgentSettled:!0};if(!this.canMutate()){this.disposed&&this.disposalReason!==void 0&&M(a,this.disposalReason);return}this.pendingHandoffs.push(a)}coveredEntryIds(t){const e=new Set;for(const i of this.pendingHandoffs)if(i.channel===t)for(const r of i.entryIds)e.add(r);return e}retryPendingCommit(t){const e=this.pendingCommit;if(e===void 0)return Promise.resolve("settled");if(e.attempt!==void 0)return e.attempt;const i=this.attemptPendingCommit(t,e).finally(()=>{e.attempt===i&&(e.attempt=void 0)});return e.attempt=i,i}async attemptPendingCommit(t,e){let i=!1;try{const n=await z(t,e.receipts);if(n.action==="hold")return"refresh_held";for(const d of e.receipts){const a=n.results.find(l=>l.delivery_id===d.delivery_id);if(a?.outcome!==d.disposition&&a?.outcome!=="stale")throw new Error("daemon returned no terminal mail settlement outcome")}i=n.results.every(d=>d.outcome==="acknowledged")}catch(n){if(this.pendingCommit===e)for(const d of e.handoffs)T(d.operationId,d,"receipt_commit",n);return String(n?.message).startsWith("conversation_write_failed")&&(O({level:"error",event:"broker.exited",node_id:t,error:n,fields:{fault:"conversation_write_failed"}}),N()?.("conversation_write_failed",1)),"held"}if(this.pendingCommit!==e)return"settled";for(const n of e.entryIds)this.claims.delete(n);if(e.receipts.some(n=>n.disposition==="released")){const n=new Set(e.entryIds);this.buffer.splice(0,this.buffer.length,...this.buffer.filter(d=>!n.has(d.entry_id)))}this.pendingCommit=void 0;const r=this.detachPending(n=>n.entryIds.every(d=>!this.claims.has(d)));for(const n of r)i&&le(n);return"settled"}async settle(t,e,i,r=!1){if(i)try{if(e?.hasPendingMessages?.()===!0)return"held"}catch{}if(this.pendingCommit!==void 0)return this.retryPendingCommit(t);const n=Q(),d=!n&&this.pendingHandoffs.some(h=>h.awaitingDequeue!==void 0)&&await this.nodeRefreshing(t);if(this.pendingCommit!==void 0)return this.retryPendingCommit(t);const a=new Set(this.buffer.map(h=>h.entry_id)),l=new Set(d?this.pendingHandoffs.filter(h=>h.awaitingDequeue!==void 0).flatMap(h=>[...h.entryIds]):[]),o=this.pendingHandoffs.filter(h=>(r||h.settleAtAgentSettled!==!0)&&h.entryIds.every(g=>!l.has(g))),c=new Set(o.flatMap(h=>[...h.entryIds])),u=[...this.claims.keys()].filter(h=>n||c.has(h)&&!a.has(h));return u.length===0?"settled":(this.pendingCommit={handoffs:o,entryIds:u,receipts:u.map(h=>{const g=this.claims.get(h);return{delivery_id:g.delivery_id,attempts:g.attempts,disposition:n?"released":"acknowledged"}})},this.retryPendingCommit(t))}async nodeRefreshing(t){try{return(await F(t)).node.intent==="refresh"}catch{return!0}}detachPending(t){const e=[];for(let i=this.pendingHandoffs.length-1;i>=0;i-=1){const r=this.pendingHandoffs[i];t(r)&&e.push(...this.pendingHandoffs.splice(i,1))}return e.reverse(),e}}function me(s,t){p?.dispose("reload",!1);const e=new ce(s,t,++ae);p=e;const i=f(r=>e.prependHeldDeferredInbox(r),"promptJoin");globalThis[se]=i;try{e.start()}catch(r){throw e.dispose("reload",!1),r}return e}f(me,"installWatcher");function pe(s){const t=me(s,de());return()=>t.dispose("shutdown",!1)}f(pe,"registerCanvasInboxWatcher");var Oe=pe;export{Oe as default,pe as registerCanvasInboxWatcher};
|
|
2
|
+
`):""}f(fe,"messageText");function H(s){return s==="quit"||s==="reload"||s==="new"||s==="resume"||s==="fork"}f(H,"isSessionShutdownReason");function he(s){switch(s){case"quit":return"shutdown";case"reload":case"new":case"resume":case"fork":return"reload";default:return s}}f(he,"disposalReasonForShutdown");function ue(s){return typeof s=="string"?s.slice(0,128):s===null?"null":s===void 0?"undefined":typeof s=="number"||typeof s=="boolean"||typeof s=="bigint"?String(s).slice(0,128):typeof s}f(ue,"boundedRuntimeReason");class ce{static{f(this,"CanvasInboxWatcher")}pi;cadence;generation;timer;backstop;debounceTimer;signalUnsubscribers=[];rerun=!1;retryNeeded=!1;polledOk=!1;armed=!1;lastCtx;streaming=!1;runNode=q();nodeId;claims=new Map;reportBodies=new Map;tickInFlight;buffer=[];lastArrival=0;pendingHandoffs=[];pendingCommit;promptJoinInFlight=!1;replayUnseenInherited=!1;seeded=!1;disposed=!1;disposalReason;fatalError;fatalEscaped=!1;constructor(t,e,i){this.pi=t,this.cadence=e,this.generation=i}start(){const t=f((e,i)=>{this.canMutate()&&this.captureCtx(e,i)},"capture");this.pi.on("session_start",(e,i)=>{this.canMutate()&&(this.captureCtx(e,i),this.arm())}),this.pi.on("turn_end",t),this.pi.on("agent_start",(e,i)=>{this.canMutate()&&(this.captureCtx(e,i),this.streaming=!0)}),this.pi.on("agent_end",(e,i)=>{this.canMutate()&&(this.captureCtx(e,i),this.streaming=!1)}),this.pi.on("message_end",e=>{if(!this.canMutate())return;const i=e?.message;if(i?.role!=="user"&&i?.role!=="custom"||!this.pendingHandoffs.some(d=>d.awaitingDequeue!==void 0))return;const r=_(fe(i.content)),n=this.pendingHandoffs.find(d=>d.awaitingDequeue===r);n!==void 0&&delete n.awaitingDequeue}),this.pi.on("agent_settled",async(e,i)=>{if(!this.canMutate())return;this.captureCtx(e,i);const r=E();if(r!==void 0)try{this.assertNodeOwnership(r,"settle"),await this.tickInFlight,this.seeded||await this.claimMail(r);const n=await this.settle(r,i,!1,!0);n==="refresh_held"&&this.dispose("refresh",!1),this.runNode&&n==="settled"&&this.canMutate()&&this.buffer.length>0&&await this.flushOrdinary(r),this.syncTimers()}catch(n){this.dispose("shutdown",!1,void 0,{error:n,stage:"settle"})}}),this.pi.on("session_shutdown",async e=>{if(!this.canMutate())return;let i;try{i=e?.reason}catch{i=void 0}const r=H(i)?he(i):"reload";H(i)||w({level:"warn",event:"inbox.lifecycle.unknown_shutdown_reason",outcome:"failed",fields:{reason:ue(i)}});try{await this.disposeAtSettlement(r,this.lastCtx)}catch(n){this.dispose(r,!1,void 0,{error:n,stage:"settle"})}})}arm(){if(this.armed)return;this.armed=!0;const t=S();this.signalUnsubscribers.push(t.subscribe("state",()=>this.syncTimers()),t.subscribe("mail",()=>this.runTick("schedule")),t.subscribe("resync",()=>this.runTick("schedule"))),this.syncTimers(),t.state!=="connected"&&this.runTick("schedule")}async disposeAtSettlement(t,e){const i=E();if(i===void 0){this.dispose(t,!1);return}try{this.assertNodeOwnership(i,"settle");const r=await this.settle(i,e,t!=="refresh");this.dispose(r==="refresh_held"?"refresh":t,!1)}catch(r){this.dispose(t,!1,void 0,{error:r,stage:"settle"})}}dispose(t,e,i,r){if(this.disposed)return;this.disposed=!0,this.disposalReason=t,this.clearTimer();for(const d of this.signalUnsubscribers.splice(0))d();const n=r;{const d=[...new Set([...this.pendingCommit?.handoffs??[],...this.pendingHandoffs.splice(0)])];this.buffer.length=0,this.claims.clear(),this.reportBodies.clear(),this.pendingCommit=void 0,this.promptJoinInFlight=!1,this.replayUnseenInherited=!1,this.nodeId=void 0,this.lastCtx=void 0,this.lastArrival=0,this.streaming=!1,this.seeded=!1,this.ownsCurrentGeneration()&&(p=void 0);for(const a of d)M(a,t)}if(n!==void 0){this.scheduleHandlerFatal(n.error,n.stage);return}}ownsCurrentGeneration(){return p===this&&p.generation===this.generation}canMutate(){return this.ownsCurrentGeneration()&&!this.disposed&&this.fatalError===void 0}captureCtx(t,e){e!==void 0&&(this.lastCtx=e)}clearTimer(){this.timer!==void 0&&clearInterval(this.timer),this.backstop!==void 0&&clearInterval(this.backstop),this.debounceTimer!==void 0&&clearTimeout(this.debounceTimer),this.timer=this.backstop=this.debounceTimer=void 0}syncTimers(){if(!this.armed||!this.canMutate())return;const t=S().state,e=t==="connected",i=t!=="stopped"&&(t!=="connected"||this.buffer.length>0||this.pendingCommit!==void 0||this.retryNeeded);i&&this.timer===void 0?(this.timer=setInterval(()=>this.runTick("schedule"),this.cadence.tickMs),this.timer.unref?.()):!i&&this.timer!==void 0&&(clearInterval(this.timer),this.timer=void 0),e&&this.backstop===void 0?(this.backstop=setInterval(()=>this.runTick("schedule"),G()),this.backstop.unref?.()):!e&&this.backstop!==void 0&&(clearInterval(this.backstop),this.backstop=void 0),this.buffer.length===0&&this.debounceTimer!==void 0&&(clearTimeout(this.debounceTimer),this.debounceTimer=void 0)}armDebounce(){this.debounceTimer!==void 0&&clearTimeout(this.debounceTimer);const t=Math.max(0,this.lastArrival+this.cadence.debounceMs-Date.now());this.debounceTimer=setTimeout(()=>{this.debounceTimer=void 0,this.runTick("schedule")},t),this.debounceTimer.unref?.()}isIdle(){if(this.runNode&&re())return!1;try{if(typeof this.lastCtx?.isIdle=="function")return this.lastCtx.isIdle()===!0}catch{}return!this.streaming}assertNodeOwnership(t,e){!this.seeded||this.nodeId===void 0||this.nodeId===t||this.raiseReadFatal(e,new Error(`watcher node changed from ${this.nodeId} to ${t}`))}raiseFatal(t,e,i,r){throw this.fatalError===void 0&&(this.fatalError=new D(e,i),this.clearTimer(),w({level:"error",event:t,outcome:"failed",error:i,error_class:k(i),fields:r})),this.fatalError}raiseReadFatal(t,e){return this.raiseFatal("inbox.read.failed",`inbox ${t} state is corrupt`,e,{stage:t})}raiseSituationalFatal(t,e){return this.raiseFatal("inbox.situational_context.missing","situational context marker has no readable sidecar",e,{entry_id:t})}escapeFatal(t,e){if(!this.fatalEscaped){if(this.fatalEscaped=!0,e==="throw")throw t;queueMicrotask(()=>{throw t})}}scheduleHandlerFatal(t,e){let i;if(t instanceof D)i=t;else try{this.raiseReadFatal(e,t)}catch(r){i=r}this.escapeFatal(i,"schedule")}runTick(t="throw"){if(!this.canMutate())return;if(this.tickInFlight!==void 0){this.rerun=!0;return}const e=this.tick().then(()=>{this.retryNeeded=!1,this.polledOk=!0}).catch(i=>{if(this.canMutate()){if(R(i)){this.retryNeeded=!0,w({level:"warn",event:"inbox.read.failed",outcome:"failed",error:i,error_class:k(i),fields:{stage:"poll"}});return}if(this.polledOk&&Y(i)){this.clearTimer(),w({level:"info",event:"inbox.poll.token_revoked",outcome:"succeeded",fields:{stage:"poll"}}),N()?.("token-revoked");return}this.scheduleHandlerFatal(i,"poll")}}).finally(()=>{this.tickInFlight===e&&(this.tickInFlight=void 0),this.syncTimers(),this.rerun&&this.canMutate()&&(this.rerun=!1,this.runTick("schedule"))});this.tickInFlight=e}async claimMail(t){this.assertNodeOwnership(t,"poll");let e;try{e=await L(t,"wake",this.reportBodies)}catch(a){throw R(a)&&(this.replayUnseenInherited=!0),a}if(!this.canMutate()||e.action==="hold")return!1;const i=this.replayUnseenInherited;this.replayUnseenInherited=!1,this.nodeId=t,this.seeded=!0;const r=new Set(e.deliveries.map(a=>a.entry.entry_id)),n=this.buffer.filter(a=>!r.has(a.entry_id));for(const a of n)this.claims.delete(a.entry_id);if(this.buffer.splice(0,this.buffer.length,...this.buffer.filter(a=>r.has(a.entry_id))),n.length>0){const a=globalThis[Symbol.for("@crouton-kit/crtr:watcher-cancel-probe")];typeof a=="function"&&a(n.map(l=>l.entry_id),process.hrtime.bigint())}let d=!1;for(const a of e.deliveries){const l=a.entry.entry_id;this.claims.has(l)||(a.entry=V(a.entry),this.claims.set(l,a),a.handoff==="inherited"&&!i?m.fresh(o=>{this.pendingHandoffs.push({...x([a.entry],"visible"),operationId:o,settleAtAgentSettled:!0})}):(this.buffer.push(a.entry),this.lastArrival=Date.now(),d=!0))}return d&&this.armDebounce(),this.syncTimers(),!0}async tick(){const t=E();if(t!==void 0){if(this.pendingCommit!==void 0){await this.retryPendingCommit(t)==="refresh_held"&&this.canMutate()&&this.dispose("refresh",!1);return}this.promptJoinInFlight||P()||this.pendingHandoffs.length>=b||await this.claimMail(t)&&this.buffer.length>0&&(!this.runNode||this.isIdle()||this.buffer.some(e=>e.tier==="critical"))&&Date.now()-this.lastArrival>=this.cadence.debounceMs&&await this.flushOrdinary(t)}}missingChannelEntries(t){const e=t.filter(n=>this.claims.has(n.entry_id)),i=this.coveredEntryIds("hidden"),r=this.coveredEntryIds("visible");return{hiddenEntries:e.filter(n=>(n.data?.situational===!0||n.data?.situationalOnly===!0)&&!i.has(n.entry_id)),visibleEntries:e.filter(n=>n.data?.situationalOnly!==!0&&!r.has(n.entry_id))}}visibleRoute(t){return t.length===0?"none":this.isIdle()?"idle":this.runNode?t.some(e=>e.tier==="critical")?"critical":"none":t.some(e=>e.tier==="critical")?"critical":t.some(e=>e.tier==="urgent"||e.kind==="final")?"steer":"followUp"}buildPlan(t){const{hiddenEntries:e,visibleEntries:i}=this.missingChannelEntries(t),r=new Set(e.map(u=>u.entry_id)),n=new Set(i.map(u=>u.entry_id)),d=t.filter(u=>r.has(u.entry_id)||n.has(u.entry_id)),a=this.isIdle()&&d.length>0&&d.every(u=>u.tier==="deferred"),l=this.visibleRoute(i)==="critical",o=!a&&!l&&e.length>0,c=!a&&!l&&i.length>0;return{hiddenEntries:e,visibleEntries:i,deferredHold:a,sendCount:Number(o)+Number(c)}}ordinaryPlan(){return this.buildPlan(this.buffer)}async contextCards(t,e){const i=[];if(e.some(r=>r.data?.situational===!0)){const r=e.at(-1).entry_id;let n;try{n=(await F(t)).situational_card}catch(d){this.raiseSituationalFatal(r,d)}n===null&&this.raiseSituationalFatal(r,new Error(`missing situational context for entry ${r}`)),i.push(n)}for(const r of e)r.data?.situationalOnly===!0&&i.push(...j(r));return i}digest(t,e){try{return X(t)}catch(i){this.raiseReadFatal(e,i)}}sendDelivery(t,e,i,r,n){this.pendingHandoffs.length>=b&&this.raiseReadFatal(r,new Error(`pending handoff capacity exceeded before ${t} send`));let d;try{d=x(e,t)}catch(o){this.raiseReadFatal(r,o)}let a;try{m.fresh(o=>{a=o,v({level:"info",event:"inbox.delivery.started",operation_id:o,fields:y(d)}),i()})}catch(o){return a===void 0&&this.raiseReadFatal(r,o),T(a,d,"send",o),"failed"}a===void 0&&this.raiseReadFatal(r,new Error(`${t} delivery returned without an operation id`));const l={operationId:a,...d,...n===void 0?{}:{awaitingDequeue:n}};return this.canMutate()?(this.pendingHandoffs.push(l),"handed-off"):(this.disposed&&this.disposalReason!==void 0&&M(l,this.disposalReason),"inactive")}sendContext(t,e,i,r,n,d){const a=r==="steer"||r==="followUp"?r:this.isIdle()?void 0:"followUp",l=a===void 0||i.length===0?void 0:_(i.at(-1));return this.sendDelivery("hidden",e,()=>{this.runNode&&n&&this.pi.appendEntry("crtr-turn-input",{message_ids:e.map(o=>o.entry_id)}),i.forEach((o,c)=>{const u=n&&c===i.length-1;this.pi.sendMessage({customType:I,content:o,display:!0,details:{nodeId:t}},a!==void 0?{deliverAs:a}:u?{deliverAs:"followUp",triggerTurn:!0}:{})})},d,l)}sendVisible(t,e,i,r,n,d){return this.sendDelivery("visible",t,()=>{if(n!=="idle"&&n!=="steer"&&n!=="followUp")throw new Error(`visible send requested for non-send route ${n}`);for(const a of e)this.pi.sendMessage({customType:I,content:a,display:!0},n==="idle"?{}:{deliverAs:n});if(K(i,r),n==="idle"){const a=ne();if(a===void 0)throw new Error("broker idle-turn admission is unavailable");a(i,this.runNode?t.map(l=>l.entry_id):[])}else this.pi.sendUserMessage(i,{deliverAs:n})},d,n==="steer"||n==="followUp"?_(i):void 0)}abortCritical(){try{this.lastCtx?.abort?.()}catch{}}requeueFromBatch(t,e){if(e.size===0)return;const i=t.filter(d=>e.has(d.entry_id)),r=new Set(i.map(d=>d.entry_id)),n=this.buffer.filter(d=>!r.has(d.entry_id));this.buffer.splice(0,this.buffer.length,...i,...n)}async executePlan(t,e,i){const r=new Set,n=this.visibleRoute(e.visibleEntries);if(n==="critical"){if(this.abortCritical(),!this.canMutate())return{inactive:!0,requeueEntryIds:r};for(const o of e.visibleEntries)r.add(o.entry_id);for(const o of e.hiddenEntries)r.add(o.entry_id);return{inactive:!1,requeueEntryIds:r}}if(this.runNode&&n==="none")return{inactive:!1,requeueEntryIds:new Set([...e.hiddenEntries,...e.visibleEntries].map(o=>o.entry_id))};const d=e.visibleEntries.length>0?this.digest(e.visibleEntries,i):[],[a,...l]=d;for(const o of l)for(const c of o.entries)r.add(c.entry_id);if(e.hiddenEntries.length>0){const o=await this.contextCards(t,e.hiddenEntries),c=this.sendContext(t,e.hiddenEntries,o,n,a===void 0,i);if(c==="inactive")return{inactive:!0,requeueEntryIds:r};if(c==="failed")for(const u of e.hiddenEntries)r.add(u.entry_id)}if(!this.canMutate())return{inactive:!0,requeueEntryIds:r};if(a!==void 0){const o=this.sendVisible(a.entries,a.cards,a.text,oe(a),n,i);if(o==="inactive")return{inactive:!0,requeueEntryIds:r};if(o==="failed")for(const c of a.entries)r.add(c.entry_id);else a.kind==="human"&&(await Z(t,a.text).catch(()=>{}),ee(t,a.text))}return{inactive:!1,requeueEntryIds:r}}async flushOrdinary(t){if(this.buffer.length===0)return"empty";const e=this.ordinaryPlan();if(e.deferredHold)return"held";if(this.pendingHandoffs.length+e.sendCount>b)return"capacity-blocked";const i=[...this.buffer],r=await this.executePlan(t,e,"poll");return r.inactive||(this.buffer.splice(0,i.length),this.requeueFromBatch(i,r.requeueEntryIds)),"attempted"}async prependHeldDeferredInbox(t){if(!this.canMutate()||this.nodeId===void 0||P())return;const e=this.ordinaryPlan();if(!e.deferredHold)return;const i=e.visibleEntries.length===0?[]:this.digest(e.visibleEntries,"poll"),r=+(e.hiddenEntries.length>0)+i.length;if(this.pendingHandoffs.length+r>b)return;const n=[...this.buffer];this.promptJoinInFlight=!0;try{this.runNode&&this.pi.appendEntry("crtr-turn-input",{message_ids:[...e.hiddenEntries,...e.visibleEntries].map(d=>d.entry_id)}),e.hiddenEntries.length>0&&await this.sendPromptJoinDelivery(t,"hidden",e.hiddenEntries,await this.contextCards(this.nodeId,e.hiddenEntries));for(const d of i)await this.sendPromptJoinDelivery(t,"visible",d.entries,[...d.cards,d.text]);if(!this.canMutate())return;this.buffer.splice(0,n.length)}finally{this.promptJoinInFlight=!1}}async sendPromptJoinDelivery(t,e,i,r){let n;try{n=x(i,e)}catch(l){this.raiseReadFatal("poll",l)}let d;try{await m.fresh(async l=>{d=l,v({level:"info",event:"inbox.delivery.started",operation_id:l,fields:y(n)});for(const o of r)await t.sendCustomMessage({customType:I,content:o,display:!0})})}catch(l){throw d===void 0&&this.raiseReadFatal("poll",l),T(d,n,"send",l),l}d===void 0&&this.raiseReadFatal("poll",new Error(`${e} prompt-join delivery returned without an operation id`));const a={operationId:d,...n,settleAtAgentSettled:!0};if(!this.canMutate()){this.disposed&&this.disposalReason!==void 0&&M(a,this.disposalReason);return}this.pendingHandoffs.push(a)}coveredEntryIds(t){const e=new Set;for(const i of this.pendingHandoffs)if(i.channel===t)for(const r of i.entryIds)e.add(r);return e}retryPendingCommit(t){const e=this.pendingCommit;if(e===void 0)return Promise.resolve("settled");if(e.attempt!==void 0)return e.attempt;const i=this.attemptPendingCommit(t,e).finally(()=>{e.attempt===i&&(e.attempt=void 0)});return e.attempt=i,i}async attemptPendingCommit(t,e){let i=!1;try{const n=await z(t,e.receipts);if(n.action==="hold")return"refresh_held";for(const d of e.receipts){const a=n.results.find(l=>l.delivery_id===d.delivery_id);if(a?.outcome!==d.disposition&&a?.outcome!=="stale")throw new Error("daemon returned no terminal mail settlement outcome")}i=n.results.every(d=>d.outcome==="acknowledged")}catch(n){if(this.pendingCommit===e)for(const d of e.handoffs)T(d.operationId,d,"receipt_commit",n);return String(n?.message).startsWith("conversation_write_failed")&&(O({level:"error",event:"broker.exited",node_id:t,error:n,fields:{fault:"conversation_write_failed"}}),N()?.("conversation_write_failed",1)),"held"}if(this.pendingCommit!==e)return"settled";for(const n of e.entryIds)this.claims.delete(n);if(e.receipts.some(n=>n.disposition==="released")){const n=new Set(e.entryIds);this.buffer.splice(0,this.buffer.length,...this.buffer.filter(d=>!n.has(d.entry_id)))}this.pendingCommit=void 0;const r=this.detachPending(n=>n.entryIds.every(d=>!this.claims.has(d)));for(const n of r)i&&le(n);return"settled"}async settle(t,e,i,r=!1){if(i)try{if(e?.hasPendingMessages?.()===!0)return"held"}catch{}if(this.pendingCommit!==void 0)return this.retryPendingCommit(t);const n=Q(),d=!n&&this.pendingHandoffs.some(h=>h.awaitingDequeue!==void 0)&&await this.nodeRefreshing(t);if(this.pendingCommit!==void 0)return this.retryPendingCommit(t);const a=new Set(this.buffer.map(h=>h.entry_id)),l=new Set(d?this.pendingHandoffs.filter(h=>h.awaitingDequeue!==void 0).flatMap(h=>[...h.entryIds]):[]),o=this.pendingHandoffs.filter(h=>(r||h.settleAtAgentSettled!==!0)&&h.entryIds.every(g=>!l.has(g))),c=new Set(o.flatMap(h=>[...h.entryIds])),u=[...this.claims.keys()].filter(h=>n||c.has(h)&&!a.has(h));return u.length===0?"settled":(this.pendingCommit={handoffs:o,entryIds:u,receipts:u.map(h=>{const g=this.claims.get(h);return{delivery_id:g.delivery_id,attempts:g.attempts,disposition:n?"released":"acknowledged"}})},this.retryPendingCommit(t))}async nodeRefreshing(t){try{return(await F(t)).node.intent==="refresh"}catch{return!0}}detachPending(t){const e=[];for(let i=this.pendingHandoffs.length-1;i>=0;i-=1){const r=this.pendingHandoffs[i];t(r)&&e.push(...this.pendingHandoffs.splice(i,1))}return e.reverse(),e}}function me(s,t){p?.dispose("reload",!1);const e=new ce(s,t,++ae);p=e;const i=f(r=>e.prependHeldDeferredInbox(r),"promptJoin");globalThis[se]=i;try{e.start()}catch(r){throw e.dispose("reload",!1),r}return e}f(me,"installWatcher");function pe(s){const t=me(s,de());return()=>t.dispose("shutdown",!1)}f(pe,"registerCanvasInboxWatcher");var Oe=pe;export{Oe as default,pe as registerCanvasInboxWatcher};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.575",
|
|
4
4
|
"description": "crtr — agent runtime with memory, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -145,7 +145,7 @@
|
|
|
145
145
|
},
|
|
146
146
|
"license": "UNLICENSED",
|
|
147
147
|
"dependencies": {
|
|
148
|
-
"@north-light/identity": "0.3.
|
|
148
|
+
"@north-light/identity": "0.3.575",
|
|
149
149
|
"@earendil-works/chord": "1.1.0",
|
|
150
150
|
"@earendil-works/pi-agent-core": "1.1.0",
|
|
151
151
|
"@earendil-works/pi-ai": "1.1.0",
|
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.575",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/cli",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.575",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "UNLICENSED",
|
|
12
12
|
"workspaces": [
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@milkdown/preset-gfm": "^7.22.3",
|
|
29
29
|
"@milkdown/prose": "^7.22.3",
|
|
30
30
|
"@milkdown/utils": "^7.22.3",
|
|
31
|
-
"@north-light/identity": "0.3.
|
|
31
|
+
"@north-light/identity": "0.3.575",
|
|
32
32
|
"cron-parser": "^5.6.0",
|
|
33
33
|
"esbuild": "^0.27.7",
|
|
34
34
|
"jose": "^6.2.12",
|
|
@@ -12459,17 +12459,17 @@
|
|
|
12459
12459
|
},
|
|
12460
12460
|
"packages/crouter-api": {
|
|
12461
12461
|
"name": "@north-light/api",
|
|
12462
|
-
"version": "0.3.
|
|
12462
|
+
"version": "0.3.575",
|
|
12463
12463
|
"license": "UNLICENSED"
|
|
12464
12464
|
},
|
|
12465
12465
|
"packages/crouter-env-docker": {
|
|
12466
12466
|
"name": "@north-light/env-docker",
|
|
12467
|
-
"version": "0.3.
|
|
12467
|
+
"version": "0.3.575",
|
|
12468
12468
|
"license": "UNLICENSED"
|
|
12469
12469
|
},
|
|
12470
12470
|
"packages/crouter-identity": {
|
|
12471
12471
|
"name": "@north-light/identity",
|
|
12472
|
-
"version": "0.3.
|
|
12472
|
+
"version": "0.3.575",
|
|
12473
12473
|
"license": "UNLICENSED",
|
|
12474
12474
|
"dependencies": {
|
|
12475
12475
|
"jose": "^6.2.1"
|
|
@@ -12477,21 +12477,21 @@
|
|
|
12477
12477
|
},
|
|
12478
12478
|
"packages/crouter-plugin": {
|
|
12479
12479
|
"name": "@north-light/plugin",
|
|
12480
|
-
"version": "0.3.
|
|
12480
|
+
"version": "0.3.575",
|
|
12481
12481
|
"license": "UNLICENSED",
|
|
12482
12482
|
"dependencies": {
|
|
12483
|
-
"@north-light/api": "0.3.
|
|
12484
|
-
"@north-light/identity": "0.3.
|
|
12483
|
+
"@north-light/api": "0.3.575",
|
|
12484
|
+
"@north-light/identity": "0.3.575"
|
|
12485
12485
|
}
|
|
12486
12486
|
},
|
|
12487
12487
|
"packages/crouter-sdk": {
|
|
12488
12488
|
"name": "@north-light/sdk",
|
|
12489
|
-
"version": "0.3.
|
|
12489
|
+
"version": "0.3.575",
|
|
12490
12490
|
"license": "UNLICENSED",
|
|
12491
12491
|
"dependencies": {
|
|
12492
|
-
"@north-light/api": "0.3.
|
|
12493
|
-
"@north-light/identity": "0.3.
|
|
12494
|
-
"@north-light/plugin": "0.3.
|
|
12492
|
+
"@north-light/api": "0.3.575",
|
|
12493
|
+
"@north-light/identity": "0.3.575",
|
|
12494
|
+
"@north-light/plugin": "0.3.575",
|
|
12495
12495
|
"jose": "^6.2.1"
|
|
12496
12496
|
},
|
|
12497
12497
|
"bin": {
|