@north-light/crouter 0.3.351 → 0.3.353
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/clients/attach/viewer.js +924 -908
- package/dist/core/canvas/migrations.js +42 -26
- package/dist/core/canvas/recovery.js +1 -1
- package/dist/core/grants/remove.js +1 -1
- package/dist/core/grants/sync.d.ts +21 -0
- package/dist/core/grants/sync.js +1 -1
- package/dist/core/human/page-schema.d.ts +1 -1
- package/dist/core/human/requests.d.ts +3 -0
- package/dist/core/human/requests.js +15 -14
- package/dist/core/human/scan.js +1 -1
- package/dist/core/runs/operations.d.ts +24 -3
- package/dist/core/runs/operations.js +5 -5
- package/dist/core/runs/questions.d.ts +54 -0
- package/dist/core/runs/questions.js +4 -0
- package/dist/core/storage/attachments.d.ts +33 -0
- package/dist/core/storage/attachments.js +1 -0
- package/dist/core/storage/client.d.ts +67 -0
- package/dist/core/storage/client.js +3 -0
- package/dist/core/storage/download.d.ts +23 -0
- package/dist/core/storage/download.js +1 -0
- package/dist/core/storage/shares.d.ts +32 -0
- package/dist/core/storage/shares.js +1 -0
- package/dist/core/storage/tables.d.ts +59 -0
- package/dist/core/storage/tables.js +3 -0
- package/dist/daemon/api/app-listener.js +12 -12
- package/dist/daemon/api/handlers/app-profiles.d.ts +17 -0
- package/dist/daemon/api/handlers/app-profiles.js +1 -0
- package/dist/daemon/api/handlers/modelauth.d.ts +3 -0
- package/dist/daemon/api/handlers/modelauth.js +1 -1
- package/dist/daemon/api/handlers/questions.d.ts +7 -0
- package/dist/daemon/api/handlers/questions.js +1 -0
- package/dist/daemon/api/handlers/runs.js +1 -1
- package/dist/daemon/api/handlers/transfer.d.ts +15 -0
- package/dist/daemon/api/handlers/transfer.js +1 -0
- package/dist/daemon/api/handlers/vendor-status.d.ts +8 -0
- package/dist/daemon/api/handlers/vendor-status.js +1 -0
- package/dist/daemon/api/operations.d.ts +2 -1
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/principal.d.ts +5 -0
- package/dist/daemon/api/principal.js +1 -1
- package/dist/daemon/cron-run.js +1 -1
- package/dist/daemon/crtrd.js +7 -7
- package/dist/daemon/human/finish.d.ts +5 -0
- package/dist/daemon/human/finish.js +3 -3
- package/dist/daemon/human/settle.js +1 -1
- package/dist/daemon/reconcilers/storage-due-work.d.ts +32 -0
- package/dist/daemon/reconcilers/storage-due-work.js +1 -0
- package/dist/daemon/runs/append-section.js +2 -2
- package/dist/daemon/runs/observer.js +2 -2
- package/dist/types.d.ts +6 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +11 -11
|
@@ -38,5 +38,10 @@ export declare class AppPrincipalResolver {
|
|
|
38
38
|
* present (runtime spec §7 "Vendor sign-in page", RT-LLM-52). The owner and
|
|
39
39
|
* single-use `jti` checks are the caller's. Every failure is 401. */
|
|
40
40
|
verifyHandoff(token: string | undefined): Promise<VendorHandoff>;
|
|
41
|
+
/** Verify a directory read token (`Authorization: Bearer <ES256 JWT>`): signature
|
|
42
|
+
* against the JWKS mirror, `iss`, `aud`, `exp`, and that `purpose` is exactly
|
|
43
|
+
* the one this route accepts. It names no owner and is not single-use, so
|
|
44
|
+
* there is no `sub` or `jti` requirement. Every failure is 401. */
|
|
45
|
+
verifyDirectoryRead(authorization: string | undefined, purpose: string): Promise<void>;
|
|
41
46
|
resolve(authorization: string | undefined, scope?: string): Promise<AppPrincipal>;
|
|
42
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var p=Object.defineProperty;var c=(h,t)=>p(h,"name",{value:t,configurable:!0});import{covers as g,JwksCache as m,verifyAccessToken as v,verifySyncNotice as _}from"@north-light/crouter-identity";import{createLocalJWKSet as u,decodeProtectedHeader as l,jwtVerify as f}from"jose";import{activeGrant as k,hasGrant as x}from"../../core/grants/mirror.js";import{RunError as s}from"../../core/runs/operations.js";class q{static{c(this,"AppPrincipalResolver")}issuer;audience;syncGrants;cache;ready=!1;timer;constructor(t,r,e,i){this.issuer=t,this.audience=r,this.syncGrants=e,this.cache=new m(t,fetch,i),this.load(),this.timer=setInterval(()=>{this.ready||this.load()},5e3),this.timer.unref()}async load(){try{await this.cache.load(),this.ready=!0}catch{}}isReady(){return this.ready}close(){this.timer&&clearInterval(this.timer)}notices=new Map;async verifySyncNotice(t){if(!this.ready)throw new s(503,"runtime_starting","runtime_starting");if(!t?.startsWith("Bearer ")||t.length<=7)throw new s(401,"unauthorized","a directory sync notice token is required");let r;try{r=await _({token:t.slice(7),jwks:this.cache,issuer:this.issuer,audience:this.audience})}catch{throw new s(401,"unauthorized","invalid directory sync notice token")}const e=Math.floor(Date.now()/1e3);for(const[i,n]of this.notices)n+30<e&&this.notices.delete(i);return this.notices.has(r.jti)?!1:(this.notices.set(r.jti,r.exp),!0)}async verifyHandoff(t){if(!this.ready)throw new s(503,"runtime_starting","runtime_starting");const r=c(()=>new s(401,"unauthorized","invalid vendor sign-in handoff"),"refuse");if(!t)throw r();let e;try{const d=l(t);if(d.alg!=="ES256")throw r();const y=await this.cache.forKid(d.kid);({payload:e}=await f(t,u(y),{issuer:this.issuer,audience:this.audience,algorithms:["ES256"],requiredClaims:["exp","jti","sub"]}))}catch{throw r()}const{sub:i,jti:n,exp:a,purpose:o,aud:w}=e;if(o!=="vendor_sign_in"||w!==this.audience||typeof i!="string"||typeof n!="string"||n===""||typeof a!="number")throw r();return{sub:i,jti:n,exp:a}}async verifyDirectoryRead(t,r){if(!this.ready)throw new s(503,"runtime_starting","runtime_starting");const e=c(()=>new s(401,"unauthorized","invalid directory token"),"refuse");if(!t?.startsWith("Bearer ")||t.length<=7)throw e();const i=t.slice(7);let n;try{const a=l(i);if(a.alg!=="ES256")throw e();const o=await this.cache.forKid(a.kid);({payload:n}=await f(i,u(o),{issuer:this.issuer,audience:this.audience,algorithms:["ES256"],requiredClaims:["exp"]}))}catch{throw e()}if(n.purpose!==r||n.aud!==this.audience)throw e()}async resolve(t,r){if(!this.ready)throw new s(503,"runtime_starting","runtime_starting");if(!t?.startsWith("Bearer ")||t.length<=7)throw new s(401,"unauthorized","a directory access token is required");let e;try{e=await v({token:t.slice(7),jwks:this.cache,issuer:this.issuer,audience:this.audience})}catch{throw new s(401,"unauthorized","invalid directory access token")}x(e.grant)||await this.syncGrants();const i=k(e.grant);if(!i||i.sub!==e.sub||i.grantee!==e.grantee||i.expires_at!==null&&!(Date.parse(i.expires_at)>Date.now()))throw new s(401,"grant_revoked","grant is not active");if(r&&!i.resolved.some(n=>g(n,r)))throw new s(403,"scope_missing",`missing scope ${r}`,void 0,void 0,r);return{sub:e.sub,grantee:e.grantee,client_id:e.client_id,grant:e.grant,rev:i.rev,resolved:i.resolved,resolved_scopes:i.resolved_scopes,spend_cap:i.spend_cap,exp:e.exp}}}export{q as AppPrincipalResolver};
|
package/dist/daemon/cron-run.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var A=Object.defineProperty;var f=(e,t)=>A(e,"name",{value:t,configurable:!0});import{randomUUID as R}from"node:crypto";import{CRON_OUTPUT_HEAD_BYTES as h,EMPTY_STDOUT_SHA256 as T,formatCronOutputHead as H}from"./cron/capture.js";import{clearLiveRun as _,cronDaemonInstanceId as E,registerLiveRun as g}from"./cron/live-runs.js";import{buildArmedRunContext as v}from"./cron/armed-context.js";import{decideHeldSettle as M,EXIT_HELD as k,HELD_DELIVERED as B}from"./cron/held.js";import{settleRun as D}from"./cron/settle.js";import{applyOverlapPolicies as L,expireDueCrons as U}from"./cron/passes.js";import{dueClockCrons as b,acquireCronRunLease as y,attachCronRunPid as P,advanceCron as $,setCronState as N}from"../core/canvas/crons.js";import{emitEvent as Y}from"../core/events/emit.js";import{runAppCommand as q}from"../core/runtime/run-command.js";import{nextSlotAfter as F}from"../core/wake.js";import{CRON_OUTPUT_HEAD_BYTES as ie}from"./cron/capture.js";import{cronDaemonInstanceId as ue,markSelfCanceledCronRun as ce}from"./cron/live-runs.js";import{recoverStaleCronLeases as me}from"./cron/lease-recovery.js";async function J(e,t){const{runId:s,outOfBand:r,startedAtIso:n,finishedAtMs:o,code:u,timedOut:i,processError:l}=t,p=!r&&!i&&l==null&&u===k&&!t.wasReplaced&&!t.wasSelfCanceled;let c,m;return t.wasReplaced?(c={kind:"stated",delivered:"replaced (overlap=replace)"},m="release"):p?(c={kind:"stated",delivered:B},m=M(e,n,o)??"release"):(c={kind:"disposed",ctx:{runId:s,startedAtIso:n,exitCode:i?-1:u,timedOut:i,runTimeoutS:e.run_timeout_s,processErrorMessage:l!=null?l.message:null,stdoutHead:t.stdoutHead,stdoutSha256:t.stdoutSha256,stderrHead:t.stderrHead},selfCanceled:t.wasSelfCanceled},m=r?"release":{kind:"clear"}),D(e,{outOfBand:r,outcome:c,record:{runId:s,startedAtIso:n,finishedAtMs:o,durationMs:o-t.startedAtMs,exitCode:i?-1:u,stdoutHead:t.stdoutHead===""?null:t.stdoutHead,stderrHead:l!=null?`run process error: ${l.message}`.slice(0,h):t.stderrHead===""?null:t.stderrHead},lease:m})}f(J,"settleClosedRun");function S(e,t,s,r){const n=Date.now(),o=`${s}: ${r.message}`.slice(0,h);return D(e,{outOfBand:t.outOfBand,outcome:{kind:"disposed",ctx:{runId:t.runId,startedAtIso:t.startedAtIso,exitCode:null,timedOut:!1,runTimeoutS:e.run_timeout_s,processErrorMessage:r.message,stdoutHead:"",stdoutSha256:T,stderrHead:o}},record:{runId:t.runId,startedAtIso:t.startedAtIso,finishedAtMs:n,durationMs:n-t.startedAtMs,exitCode:null,stdoutHead:null,stderrHead:o},lease:"release"})}f(S,"settleUnspawnedRun");function V(e,t){let s;try{s=F(e.recur,new Date(t))}catch(r){return Y({level:"error",event:"cron.recur.unparseable",error:r,fields:{cron_id:e.cron_id,name:e.name,recur:e.recur}}),N(e.cron_id,"paused"),new Error(`unparseable recurrence ${JSON.stringify(e.recur)}: ${r.message} \u2014 this cron cannot be rescheduled until its recurrence is fixed (\`crtr cron cancel\` and re-arm it), so it has been paused.`)}return $(e.cron_id,s),null}f(V,"advanceRecurrence");function X(e,t){const{outOfBand:s}=t;return new Promise((r,n)=>{const o=R(),u=Date.now(),i=new Date(u).toISOString(),l={runId:o,outOfBand:s,startedAtIso:i,startedAtMs:u};if(!y(e.cron_id,E,i,s)){r(null);return}let p;try{if({env:p}=v(e,o),!s&&e.recur!=null){const d=V(e,u);if(d!==null)throw d}}catch(d){S(e,l,"run could not be armed",d).then(r,n);return}let c=null,m=!1;const C={runId:o,replaced:!1,selfCanceled:!1,kill:f(()=>{c!==null?c():m=!0},"kill")};g(e.cron_id,C),q({command:e.command,cwd:e.cwd,env:p,app:e.grantee,...e.anchor_node===null?{}:{nodeId:e.anchor_node},timeoutMs:e.run_timeout_s*1e3,headBytes:h}).then(d=>{if(d.pid===null){_(e.cron_id,o),d.completion.then(a=>{S(e,l,"run failed to spawn",a.processError??new Error("bash did not provide a process id")).then(r,n)},n);return}let I=!1;const w=f(()=>{d.completion.then(a=>{const O=C.replaced,x=C.selfCanceled;if(_(e.cron_id,o),I){r(null);return}J(e,{runId:o,outOfBand:s,startedAtIso:i,startedAtMs:u,finishedAtMs:Date.now(),code:a.exitCode,timedOut:a.timedOut,processError:a.processError,stdoutHead:H(a.stdout,a.stdoutTruncated),stderrHead:H(a.stderr,a.stderrTruncated),stdoutSha256:a.stdoutSha256,wasReplaced:O,wasSelfCanceled:x}).then(r,n)},n)},"settleCommand");if(!P(e.cron_id,E,i,d.pid)&&!C.selfCanceled){I=!0,d.terminate(),w();return}c=d.terminate,m&&d.terminate(),w()},d=>{_(e.cron_id,o),S(e,l,"run failed to spawn",d).then(r,n)})})}f(X,"executeCron");function se(e,t={}){const s=new Date(e).toISOString();U(s),L(e,s);const r=[];for(const n of b(s)){const o=X(n,{outOfBand:!1}).then(()=>{});r.push(o),t.register?.(o)}for(const n of r)n.catch(()=>{});return r}f(se,"runDueCrons");export{ie as CRON_OUTPUT_HEAD_BYTES,ue as cronDaemonInstanceId,X as executeCron,ce as markSelfCanceledCronRun,me as recoverStaleCronLeases,se as runDueCrons};
|
package/dist/daemon/crtrd.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
var ve=Object.defineProperty;var a=(t,o)=>ve(t,"name",{value:o,configurable:!0});import{writeFileSync as ge,readFileSync as we,rmSync as ye,mkdirSync as V,existsSync as b,chmodSync as Se}from"node:fs";import{createServer as J}from"node:http";import{dirname as _e}from"node:path";import{envCrtrdTcp as be}from"../shared/env.js";import{serveCanvasConversationStore as ke}from"../core/canvas/sessions.js";import{pollRunObservers as Ee}from"./runs/observer.js";import{randomBytes as Pe}from"node:crypto";import{apiSocketPath as Ie,crtrHome as Te}from"../core/canvas/paths.js";import{activeAppUsers as Re}from"../core/spaces/users.js";import{ensureAppSpace as xe,ensureUserFiles as De}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as E}from"../launcher/spec.js";import{launcherRequest as T}from"../launcher/client.js";import{initializeEmptyLayout as Ce,layoutNeedsMigration as Me,socketDir as Q}from"../core/layout.js";import{listNodeMemoryTargets as Y,listNodesForSupervision as Ae}from"../core/canvas/index.js";import{createApiServer as Le}from"./api/server.js";import{createAppListener as Ne}from"./api/app-listener.js";import{installLocalTerminalGrant as Ge}from"../core/grants/local.js";import{createGrantSync as $e,GRANT_SYNC_INTERVAL_S as Oe}from"../core/grants/sync.js";import{runRemovalCascades as Fe}from"../core/grants/remove.js";import{sweepSandboxes as je}from"../core/grants/on-sync.js";import{hasSyncedGrants as He}from"../core/grants/mirror.js";import{readConfig as ze}from"../core/config.js";import{reconcileUndeliveredTickets as Be}from"./human/sweep.js";import{resetHandedOffDeliveries as Ue}from"../core/canvas/deliveries.js";import{sweepReviews as qe}from"./review/sweep.js";import{pidfilePath as $}from"./pidfile.js";import{rendererWarning as We}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as Ke}from"./supervise-cadence.js";import{BrokerSupervisionReconciler as Ve}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as Je}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as Qe}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as Ye}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as Xe}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as Ze}from"./reconcilers/delivery-lane.js";import{migrateLegacyExecutionIdentities as er}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as rr}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as tr}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as nr}from"./reconcilers/storage-maintenance.js";import{BashDeadlineReconciler as or}from"./reconcilers/bash-deadline.js";import{captureLivenessSnapshot as X,capturePidCommand as ir,captureTeardownSnapshot as Z,isPidAlive as ar,isAnyPidAlive as ee,killProcessTreePids as re}from"../core/canvas/pid.js";import{bindFleet as sr,boundFleet as cr}from"../core/runtime/fleet.js";import{bindDaemonControl as lr,clearDaemonControl as dr}from"./control.js";import{execDaemon as pr,spawnDaemon as mr}from"./manage.js";import{reconcileAndMigrateBoot as fr}from"../core/canvas/boot.js";import{resolveInstallId as ur}from"../core/canvas/install-id.js";import{setInstallId as hr}from"../core/runtime/nodes.js";import{claimDaemonOwnership as vr,releaseDaemonOwnership as te}from"../core/canvas/daemon-owner.js";import{migrate as gr,openDb as wr}from"../core/canvas/db.js";import{prewarmRecentRecipes as yr,reapStaleSpares as Sr}from"../core/runtime/warm-pool.js";import{DaemonFleet as _r}from"./fleet.js";import{BOOT_MIGRATIONS as br}from"./boot-migrations.js";import{applyJoinedOwner as kr}from"./joined-owner.js";import{emitEvent as h}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as Er}from"../core/events/source.js";import{activateOnDiskMigrations as Pr,MigrationBlockedError as Ir,runForcedOnDiskMigrations as Tr}from"../migrations/activation.js";import{convertLegacyProviderStore as Rr,convertLegacyScopedStore as xr}from"../core/scoped-state/migrate.js";import{userScopeRoot as Dr}from"../core/scope.js";import{secretsDir as Cr}from"../core/layout.js";function O(t,o){return t.includes(` --canvas-home ${o} `)}a(O,"brokerCommandDeclaresCanvasHome");function F(t){return/ --epoch (\S+) /.exec(t)?.[1]??null}a(F,"brokerCommandEpoch");const Mr=5e3,ne=150;async function Ar(t){const o=X();if(o===null)throw h({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const s=Te(),m=[];for(const[i,p]of o.processes){const g=p.command;if(p.zombie||!g.includes("broker-cli.js")||!O(g,s)||F(g)===t)continue;const y=g.trim().split(/\s+/).at(-1)??"";y===""||y.endsWith("broker-cli.js")||m.push({pid:i,nodeId:y})}let f=0;const n=[];for(const i of m){const p=ir(i.pid);if(p===null||!p.includes("broker-cli.js")||!O(p,s)||F(p)===t||p.trim().split(/\s+/).at(-1)!==i.nodeId)continue;const g=Z(i.pid,null);if(!g.reused){try{process.platform==="linux"&&b(E)?await T({operation:"stop",pid:i.pid}):process.kill(i.pid,"SIGTERM")}catch{continue}f++,n.push({pid:i.pid,snap:g})}}const w=Date.now()+Mr;for(;Date.now()<w&&n.some(i=>ee(i.snap.tree.length>0?i.snap.tree:[i.pid]));)await new Promise(i=>setTimeout(i,ne));let v=0;for(const i of n){const p=i.snap.tree.length>0?i.snap.tree:[i.pid];ee(p)&&(process.platform==="linux"&&b(E)?await T({operation:"stop",pid:i.pid}):re(p,"SIGKILL",i.snap.identities),v++)}v>0&&await new Promise(i=>setTimeout(i,ne));const c=X();if(c===null)throw h({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const d=[];for(const[i,p]of c.processes){const g=p.command;p.zombie||!g.includes("broker-cli.js")||O(g,s)&&F(g)!==t&&d.push(i)}if(d.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${d.join(", ")}`);h({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:m.length,sigtermed:f,sigkilled:v}})}a(Ar,"reapPriorEpochBrokers");const Lr=5e3;async function Nr(t){const o=Array.from(t.entries());if(o.length===0)return;const s=o.map(([,n])=>({pid:n.pid,snap:Z(n.pid,null),exited:n.handle.exited}));for(const n of s)if(!n.snap.reused)try{process.platform==="linux"&&b(E)?await T({operation:"stop",pid:n.pid}):process.kill(n.pid,"SIGTERM")}catch{}const m=new Promise(n=>{setTimeout(n,Lr).unref()});await Promise.allSettled(s.map(n=>Promise.race([n.exited.then(()=>{}),m])));const f=[];for(const n of s)ar(n.pid)&&(process.platform==="linux"&&b(E)?await T({operation:"stop",pid:n.pid}):re(n.snap.tree.length>0?n.snap.tree:[n.pid],"SIGKILL",n.snap.identities),f.push(n));await Promise.all(f.map(n=>n.exited)),h({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:s.length,sigkilled:f.length}})}a(Nr,"teardownFleet");function oe(){V(_e($()),{recursive:!0}),ge($(),String(process.pid),"utf8")}a(oe,"writePidfile");function ie(t){return typeof t=="object"&&t!==null&&t.code==="ENOENT"}a(ie,"isMissingPathError");function ae(t){const o=$();let s;try{s=we(o,"utf8")}catch(m){if(ie(m))return;throw m}if(s.trim()!==String(t))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${t})`);try{ye(o)}catch(m){if(ie(m))return;throw m}}a(ae,"removeOwnedPidfileMirror");const Gr={acceptsDetachedWork:a(()=>!0,"acceptsDetachedWork"),registerDetached:a(t=>{t.catch(()=>{})},"registerDetached")},j=new Ve,$r=new Je,Or=new Qe,Fr=new Ye,jr=new Xe,Hr=new Ze,zr=new tr,Br=new nr,Ur=new or;async function qr(t=Date.now(),o=Gr){const s=cr(),m=await j.runFleetPreamble(s);let f,n;try{const v=Ae();f=v.filter(c=>c.status==="active"||c.status==="idle"),n=v.filter(c=>c.status==="done"||c.status==="canceled"||c.status==="dead")}catch(v){h({level:"error",event:"canvas.node_list.failed",error:v});return}await j.run(t,{rows:f,fleet:s,lifecycle:o}),$r.run(t,{rows:f});const w=await Or.run(t,{rows:[...f,...n],fleet:s});Fr.run(t),jr.run(t,{lifecycle:o}),Hr.run(t,{lifecycle:o}),zr.run(t,{lifecycle:o}),Br.run(t,{lifecycle:o,fleet:s}),Ee(),await Ur.run(t,{rows:f}),j.warnIfSaturated({fleet:s,lifecycle:o,thresholds:m,frozen:w})}a(qr,"superviseTick");async function Wr(t){V(Q(),{recursive:!0,mode:493}),process.platform==="linux"&&Se(Q(),493);const o=Ie();if(b(o))throw new Error(`layout refusal cannot bind occupied socket: ${o}`);const s=J((c,d)=>{if(c.method==="GET"&&c.url==="/healthz"){d.writeHead(200,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}d.writeHead(503,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((c,d)=>{s.once("error",d),s.listen(o,()=>{s.off("error",d),c()})});let m;const f=t.tcp??be();if(f){const c=/^(.*):(\d+)$/.exec(f);if(!c||Number(c[2])>65535)throw new Error(`invalid refusal TCP address: ${f}`);const d=J(s.listeners("request")[0]);await new Promise((i,p)=>{d.once("error",p),d.listen(Number(c[2]),c[1],()=>{d.off("error",p),i()})}),m=d}oe();let n;const w=a(()=>(n??=(async()=>{s.closeAllConnections(),m?.closeAllConnections(),await Promise.all([s,m].filter(c=>c!==void 0).map(c=>new Promise((d,i)=>c.close(p=>p?i(p):d())))),ae(process.pid),process.off("SIGINT",v),process.off("SIGTERM",v)})(),n),"cleanup"),v=a(()=>{w().then(()=>process.exit(0),c=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(c)}
|
|
2
|
-
`),process.exit(1)})},"onSignal");return process.on("SIGINT",v),process.on("SIGTERM",v),w}a(
|
|
3
|
-
`);else{const e=[
|
|
4
|
-
`)}return async()=>{}}const s=
|
|
1
|
+
var ve=Object.defineProperty;var a=(t,n)=>ve(t,"name",{value:n,configurable:!0});import{writeFileSync as ge,readFileSync as we,rmSync as ye,mkdirSync as V,existsSync as k,chmodSync as Se}from"node:fs";import{createServer as J}from"node:http";import{dirname as _e}from"node:path";import{envCrtrdTcp as ke}from"../shared/env.js";import{serveCanvasConversationStore as be}from"../core/canvas/sessions.js";import{pollRunObservers as Ee}from"./runs/observer.js";import{randomBytes as Pe}from"node:crypto";import{apiSocketPath as Ie,crtrHome as Te}from"../core/canvas/paths.js";import{activeAppUsers as Re}from"../core/spaces/users.js";import{ensureAppSpace as xe,ensureUserFiles as De}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as E}from"../launcher/spec.js";import{launcherRequest as T}from"../launcher/client.js";import{initializeEmptyLayout as Ce,layoutNeedsMigration as Me,socketDir as Q}from"../core/layout.js";import{listNodeMemoryTargets as Y,listNodesForSupervision as Ae}from"../core/canvas/index.js";import{createApiServer as Le}from"./api/server.js";import{createAppListener as Ne}from"./api/app-listener.js";import{installLocalTerminalGrant as Ge}from"../core/grants/local.js";import{createGrantSync as $e,GRANT_SYNC_INTERVAL_S as Oe}from"../core/grants/sync.js";import{runRemovalCascades as Fe}from"../core/grants/remove.js";import{sweepSandboxes as je}from"../core/grants/on-sync.js";import{hasSyncedGrants as He}from"../core/grants/mirror.js";import{readConfig as ze}from"../core/config.js";import{reconcileUndeliveredTickets as Be}from"./human/sweep.js";import{resetHandedOffDeliveries as We}from"../core/canvas/deliveries.js";import{sweepReviews as Ue}from"./review/sweep.js";import{pidfilePath as $}from"./pidfile.js";import{rendererWarning as qe}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as Ke}from"./supervise-cadence.js";import{BrokerSupervisionReconciler as Ve}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as Je}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as Qe}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as Ye}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as Xe}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as Ze}from"./reconcilers/delivery-lane.js";import{migrateLegacyExecutionIdentities as er}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as rr}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as tr}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as nr}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as or}from"./reconcilers/storage-due-work.js";import{BashDeadlineReconciler as ir}from"./reconcilers/bash-deadline.js";import{captureLivenessSnapshot as X,capturePidCommand as ar,captureTeardownSnapshot as Z,isPidAlive as sr,isAnyPidAlive as ee,killProcessTreePids as re}from"../core/canvas/pid.js";import{bindFleet as cr,boundFleet as lr}from"../core/runtime/fleet.js";import{bindDaemonControl as dr,clearDaemonControl as pr}from"./control.js";import{execDaemon as mr,spawnDaemon as fr}from"./manage.js";import{reconcileAndMigrateBoot as ur}from"../core/canvas/boot.js";import{resolveInstallId as hr}from"../core/canvas/install-id.js";import{setInstallId as vr}from"../core/runtime/nodes.js";import{claimDaemonOwnership as gr,releaseDaemonOwnership as te}from"../core/canvas/daemon-owner.js";import{migrate as wr,openDb as yr}from"../core/canvas/db.js";import{prewarmRecentRecipes as Sr,reapStaleSpares as _r}from"../core/runtime/warm-pool.js";import{DaemonFleet as kr}from"./fleet.js";import{BOOT_MIGRATIONS as br}from"./boot-migrations.js";import{applyJoinedOwner as Er}from"./joined-owner.js";import{emitEvent as h}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as Pr}from"../core/events/source.js";import{activateOnDiskMigrations as Ir,MigrationBlockedError as Tr,runForcedOnDiskMigrations as Rr}from"../migrations/activation.js";import{convertLegacyProviderStore as xr,convertLegacyScopedStore as Dr}from"../core/scoped-state/migrate.js";import{userScopeRoot as Cr}from"../core/scope.js";import{secretsDir as Mr}from"../core/layout.js";function O(t,n){return t.includes(` --canvas-home ${n} `)}a(O,"brokerCommandDeclaresCanvasHome");function F(t){return/ --epoch (\S+) /.exec(t)?.[1]??null}a(F,"brokerCommandEpoch");const Ar=5e3,ne=150;async function Lr(t){const n=X();if(n===null)throw h({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const s=Te(),m=[];for(const[i,p]of n.processes){const g=p.command;if(p.zombie||!g.includes("broker-cli.js")||!O(g,s)||F(g)===t)continue;const y=g.trim().split(/\s+/).at(-1)??"";y===""||y.endsWith("broker-cli.js")||m.push({pid:i,nodeId:y})}let f=0;const o=[];for(const i of m){const p=ar(i.pid);if(p===null||!p.includes("broker-cli.js")||!O(p,s)||F(p)===t||p.trim().split(/\s+/).at(-1)!==i.nodeId)continue;const g=Z(i.pid,null);if(!g.reused){try{process.platform==="linux"&&k(E)?await T({operation:"stop",pid:i.pid}):process.kill(i.pid,"SIGTERM")}catch{continue}f++,o.push({pid:i.pid,snap:g})}}const w=Date.now()+Ar;for(;Date.now()<w&&o.some(i=>ee(i.snap.tree.length>0?i.snap.tree:[i.pid]));)await new Promise(i=>setTimeout(i,ne));let v=0;for(const i of o){const p=i.snap.tree.length>0?i.snap.tree:[i.pid];ee(p)&&(process.platform==="linux"&&k(E)?await T({operation:"stop",pid:i.pid}):re(p,"SIGKILL",i.snap.identities),v++)}v>0&&await new Promise(i=>setTimeout(i,ne));const c=X();if(c===null)throw h({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const d=[];for(const[i,p]of c.processes){const g=p.command;p.zombie||!g.includes("broker-cli.js")||O(g,s)&&F(g)!==t&&d.push(i)}if(d.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${d.join(", ")}`);h({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:m.length,sigtermed:f,sigkilled:v}})}a(Lr,"reapPriorEpochBrokers");const Nr=5e3;async function Gr(t){const n=Array.from(t.entries());if(n.length===0)return;const s=n.map(([,o])=>({pid:o.pid,snap:Z(o.pid,null),exited:o.handle.exited}));for(const o of s)if(!o.snap.reused)try{process.platform==="linux"&&k(E)?await T({operation:"stop",pid:o.pid}):process.kill(o.pid,"SIGTERM")}catch{}const m=new Promise(o=>{setTimeout(o,Nr).unref()});await Promise.allSettled(s.map(o=>Promise.race([o.exited.then(()=>{}),m])));const f=[];for(const o of s)sr(o.pid)&&(process.platform==="linux"&&k(E)?await T({operation:"stop",pid:o.pid}):re(o.snap.tree.length>0?o.snap.tree:[o.pid],"SIGKILL",o.snap.identities),f.push(o));await Promise.all(f.map(o=>o.exited)),h({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:s.length,sigkilled:f.length}})}a(Gr,"teardownFleet");function oe(){V(_e($()),{recursive:!0}),ge($(),String(process.pid),"utf8")}a(oe,"writePidfile");function ie(t){return typeof t=="object"&&t!==null&&t.code==="ENOENT"}a(ie,"isMissingPathError");function ae(t){const n=$();let s;try{s=we(n,"utf8")}catch(m){if(ie(m))return;throw m}if(s.trim()!==String(t))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${t})`);try{ye(n)}catch(m){if(ie(m))return;throw m}}a(ae,"removeOwnedPidfileMirror");const $r={acceptsDetachedWork:a(()=>!0,"acceptsDetachedWork"),registerDetached:a(t=>{t.catch(()=>{})},"registerDetached")},j=new Ve,Or=new Je,Fr=new Qe,jr=new Ye,Hr=new Xe,zr=new Ze,Br=new tr,Wr=new nr,Ur=new or,qr=new ir;async function Kr(t=Date.now(),n=$r){const s=lr(),m=await j.runFleetPreamble(s);let f,o;try{const v=Ae();f=v.filter(c=>c.status==="active"||c.status==="idle"),o=v.filter(c=>c.status==="done"||c.status==="canceled"||c.status==="dead")}catch(v){h({level:"error",event:"canvas.node_list.failed",error:v});return}await j.run(t,{rows:f,fleet:s,lifecycle:n}),Or.run(t,{rows:f});const w=await Fr.run(t,{rows:[...f,...o],fleet:s});jr.run(t),Hr.run(t,{lifecycle:n}),zr.run(t,{lifecycle:n}),Br.run(t,{lifecycle:n}),Wr.run(t,{lifecycle:n,fleet:s}),Ur.run(t,{lifecycle:n}),Ee(),await qr.run(t,{rows:f}),j.warnIfSaturated({fleet:s,lifecycle:n,thresholds:m,frozen:w})}a(Kr,"superviseTick");async function Vr(t){V(Q(),{recursive:!0,mode:493}),process.platform==="linux"&&Se(Q(),493);const n=Ie();if(k(n))throw new Error(`layout refusal cannot bind occupied socket: ${n}`);const s=J((c,d)=>{if(c.method==="GET"&&c.url==="/healthz"){d.writeHead(200,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}d.writeHead(503,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((c,d)=>{s.once("error",d),s.listen(n,()=>{s.off("error",d),c()})});let m;const f=t.tcp??ke();if(f){const c=/^(.*):(\d+)$/.exec(f);if(!c||Number(c[2])>65535)throw new Error(`invalid refusal TCP address: ${f}`);const d=J(s.listeners("request")[0]);await new Promise((i,p)=>{d.once("error",p),d.listen(Number(c[2]),c[1],()=>{d.off("error",p),i()})}),m=d}oe();let o;const w=a(()=>(o??=(async()=>{s.closeAllConnections(),m?.closeAllConnections(),await Promise.all([s,m].filter(c=>c!==void 0).map(c=>new Promise((d,i)=>c.close(p=>p?i(p):d())))),ae(process.pid),process.off("SIGINT",v),process.off("SIGTERM",v)})(),o),"cleanup"),v=a(()=>{w().then(()=>process.exit(0),c=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(c)}
|
|
2
|
+
`),process.exit(1)})},"onSignal");return process.on("SIGINT",v),process.on("SIGTERM",v),w}a(Vr,"serveLayoutRefusal");async function Jr(t={}){if(Me())return Vr(t);Ce();const n=gr(Pe(16).toString("hex"));if(!n.claimed){if(n.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${n.incumbentPid??"?"})
|
|
3
|
+
`);else{const e=[n.incumbentPid!==void 0?`pid ${n.incumbentPid}`:void 0,n.verdict!==void 0?`verdict ${n.verdict}`:void 0].filter(l=>l!==void 0),r=e.length>0?` (${e.join(", ")})`:"";process.stderr.write(`[crtrd] claim lost: ${n.reason}${r}
|
|
4
|
+
`)}return async()=>{}}const s=n.claim;let m=null;try{await Lr(s.token),Dr({kind:"user",root:Cr()}),await xr(Mr()),wr(yr()),m=Er(),process.platform==="linux"&&k(E)&&await T({operation:"users.sync",users:Re()}),rr(s.token)}catch(e){try{const{released:r}=te(s);if(!r)throw new Error("daemon ownership claim was not released")}catch(r){throw new AggregateError([e,r],"daemon startup failed and cleanup also failed")}throw e}let f=!0,o,w=Promise.resolve(),v=!1,c,d;const i=new Set;let p=!1,g=!1,y,C,H=!1,M,R,x,A,u={phase:"initializing",message:"daemon startup is initializing its on-disk memory migration and normal services are unavailable",blockerPaths:[]},P="initial startup activation",se=null;function ce(){return u.phase==="ready"?null:{phase:u.phase,message:u.message,blockerPaths:u.blockerPaths}}a(ce,"startupBlock");function I(e){i.add(e),e.finally(()=>{i.delete(e)}).catch(()=>{})}a(I,"registerDetached");const le={acceptsDetachedWork:a(()=>f,"acceptsDetachedWork"),registerDetached:I};async function de(){try{f=!1,c!==void 0&&(clearInterval(c),c=void 0),x!==void 0&&(clearInterval(x),x=void 0);let e;do e=w,await e;while(e!==w);for(;i.size>0;)await Promise.allSettled([...i]);if(d!==void 0&&(await Gr(d),d.dispose()),C!==void 0&&!H&&(H=!0,await C.close()),R!==void 0){const r=R;R=void 0,await r.close()}if(M!==void 0){const r=M;M=void 0,await r.close()}if(p||(ae(process.pid),p=!0),p&&!g){const{released:r}=te(s);if(!r)throw new Error("daemon ownership claim was not released");g=!0}}finally{pr(),process.off("SIGINT",W),process.off("SIGTERM",U),process.off("exit",q)}}a(de,"cleanupAttempt");const L=1e3;let z=!1;function pe(){return z?{graceMs:L}:(z=!0,_.fresh(()=>{h({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:L}})}),setTimeout(()=>{me()},L),{graceMs:L})}a(pe,"requestHandover");async function me(){try{await b()}catch(e){const r=e instanceof Error?e.stack??e.message:String(e);process.stderr.write(`[crtrd] handover cleanup failed:
|
|
5
5
|
${r}
|
|
6
|
-
`),process.exit(1)}try{process.platform==="linux"&&
|
|
7
|
-
`),process.exit(1)}process.exit(0)}a(me,"performHandover");function
|
|
6
|
+
`),process.exit(1)}try{process.platform==="linux"&&k(E)&&(await T({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await mr();const e=await fr();_.fresh(()=>{h({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:e.pid??e.existing_pid??null,spawned:e.started}})})}catch(e){_.fresh(()=>{h({level:"error",event:"daemon.handover.successor_failed",error:e})});const r=e instanceof Error?e.message:String(e);process.stderr.write(`[crtrd] handover successor failed to start: ${r}
|
|
7
|
+
`),process.exit(1)}process.exit(0)}a(me,"performHandover");function b(){if(y!==void 0)return y;const e=de();return y=e,e.then(()=>{y===e&&(y=void 0)},()=>{y===e&&(y=void 0)}),e}a(b,"requestCleanup");async function B(e){try{await b(),process.exit(0)}catch(r){process.exitCode=1;const l=r instanceof Error?r.stack??`${r.name}: ${r.message}`:String(r);process.stderr.write(`[crtrd] ${e} cleanup failed:
|
|
8
8
|
${l}
|
|
9
|
-
`),process.exit(1)}}a(B,"handleSignal");function
|
|
9
|
+
`),process.exit(1)}}a(B,"handleSignal");function W(){B("SIGINT")}a(W,"onSigint");function U(){B("SIGTERM")}a(U,"onSigterm");function q(){b().catch(()=>{})}a(q,"onExit");function N(e){f&&(w=w.then(e).then(()=>{},r=>{_.fresh(()=>{h({level:"error",event:"daemon.lane_job.failed",error:r})})}))}a(N,"enqueue");function K(){!f||v||(v=!0,N(async()=>{if(v=!1,!!f)try{await Kr(Date.now(),le)}catch(e){_.fresh(()=>{h({level:"error",event:"daemon.tick.failed",error:e})})}}))}a(K,"enqueueTick");let D=null;async function fe(){if(D!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${D instanceof Error?D.message:String(D)}`};if(u.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(u.phase!=="prepared"&&u.phase!=="recovering")throw{status:409,code:"not_prepared",message:u.message};const e=u.phase==="prepared";try{return await G(),u={phase:"ready",message:"",blockerPaths:[]},e?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(r){throw D=r,{status:500,code:"admission_failed",message:`daemon admission failed: ${r instanceof Error?r.message:String(r)}`}}}a(fe,"admitStartup");async function ue(e){if(P!==null)throw{status:409,code:"migration_in_progress",message:`on-disk migration is already running (${P})`};P="repair";try{const r=await Rr({dryRun:e.dryRun,explicitDirs:e.dirs,nodes:Y()});if(e.dryRun||!r.finalVerificationSucceeded||u.phase!=="blocked")return{result:r,resumed:!1};if(t.prepare)return u={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:r,resumed:!1};try{return await G(),u={phase:"ready",message:"",blockerPaths:[]},{result:r,resumed:!0}}catch(l){const S=l instanceof Error?l.message:String(l);return setTimeout(()=>{b().catch(()=>{})},1e3),{result:r,resumed:!1,resumeError:S}}}finally{P=null}}a(ue,"repairMigrations");async function he(){o=t.intervalMs??Ke;const e=s.token;d=new kr({epoch:e,enqueue:N}),cr(d),u={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{h({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:o}})});const r=hr();if(vr(r),_.fresh(()=>{h({level:"info",event:"daemon.install_id.resolved",fields:{install_id:r}})}),_.fresh(()=>{let l;try{l=qe()}catch{return}l!==null&&h({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:l.slice(0,2048)}})}),await _.fresh(async()=>{await Ue(),await Be(),We(Date.now())}),_.fresh(()=>{const{reconcile:l,migrated:S}=ur(er);l.changed&&h({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:l.cleared.length,same_boot_proven:l.sameBootProven}}),S.length>0&&h({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:S.length}})}),_.fresh(()=>{for(const l of br){const S=l.apply();S.changed&&h({level:"info",event:"daemon.boot_migration.applied",fields:{migration:l.id,...S.summary}})}}),A){const l=a(()=>{I(A().catch(()=>{}))},"refresh");I(Fe().catch(()=>{})),I(je().then(({completion:S})=>S).catch(S=>{h({level:"error",event:"daemon.grants.sweep_failed",error:S})})),l(),x=setInterval(l,Oe*1e3),x.unref()}N(()=>{_r(),Sr()}),c=setInterval(K,o),K()}a(he,"completeStartup");function G(){return se??=he().catch(e=>{throw u={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},e})}a(G,"resumeStartup");try{process.on("SIGINT",W),process.on("SIGTERM",U),process.on("exit",q),Pr(),m!==null&&h({level:"info",event:"daemon.joined_owner.applied",fields:{owner:m.owner,rows:m.rows}}),M=await be();const e=s.token;dr({epoch:e,requestHandover:pe,registerDetached:I,startupBlock:ce,admitStartup:fe,repairMigrations:ue}),oe(),C=Le({tcp:t.tcp}),await C.ready;const r=ze("user").runtime;r?.issuer?A=$e(r.issuer,I):Ge(),r!==void 0&&(R=Ne(r,()=>He(r.issuer.replace(/\/$/,"")),A),await R.ready);try{await Ir({nodes:Y()})}catch(l){if(P=null,l instanceof Tr)return u={phase:"blocked",message:l.message,blockerPaths:l.blockerPaths},b;throw l}process.platform==="linux"&&k(E)&&(De(),await xe("app:terminal"));try{t.prepare?u={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await G(),u={phase:"ready",message:"",blockerPaths:[]})}finally{P=null}return b}catch(e){try{await b()}catch(r){throw new AggregateError([e,r],"daemon startup failed and cleanup also failed")}throw e}}a(Jr,"runDaemon");var nn=Jr;export{O as brokerCommandDeclaresCanvasHome,F as brokerCommandEpoch,nn as default,Jr as runDaemon,Kr as superviseTick};
|
|
@@ -10,6 +10,11 @@ export interface CancelTicketOptions {
|
|
|
10
10
|
}
|
|
11
11
|
/** Settle a page request directly in human_requests; result files and bridge nodes are not part of this path. */
|
|
12
12
|
export declare function resolvePageTicket(ticketId: string, responses: unknown, opts?: CompletePageOptions): Promise<TicketResult>;
|
|
13
|
+
/** {@link resolvePageTicket}, also saying whether this answer won (`settled`) or the request was already settled. */
|
|
14
|
+
export declare function answerPageTicket(ticketId: string, responses: unknown, opts?: CompletePageOptions): Promise<{
|
|
15
|
+
outcome: 'settled' | 'already_settled';
|
|
16
|
+
result: TicketResult;
|
|
17
|
+
}>;
|
|
13
18
|
export declare function cancelHumanTicket(ticketId: string, opts: CancelTicketOptions): Promise<{
|
|
14
19
|
outcome: 'settled' | 'already_settled';
|
|
15
20
|
result: TicketResult;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var _=Object.defineProperty;var
|
|
2
|
-
`)[0]??"";return e.length>n?`${e.slice(0,n-1)}\u2026`:e}
|
|
1
|
+
var _=Object.defineProperty;var o=(t,n)=>_(t,"name",{value:n,configurable:!0});import{getRequest as y,getRequestByInboxTicket as b,presentationDir as q}from"../../core/human/requests.js";import{buildPageSummary as v}from"../../core/human/summary.js";import{validateFinalPageResponses as $}from"../../core/human/tickets.js";import{composeHumanCompletion as j}from"../../core/human/completion.js";import{describePageAnswer as k}from"../../core/human/answer.js";import{renderAnswerText as S}from"../../core/human/answer-text.js";import{readPageFeedback as T}from"../../core/human/feedback.js";import{pageDocumentSha256 as A}from"../../core/human/feedback-companion.js";import{pagePath as N}from"../../core/human/convention.js";import{retireCompanion as f}from"../companion-retire.js";import{settleHumanRequest as h}from"./settle.js";function c(t){const n=y(t)??b(t);if(n===null||n.kind!=="page")throw new Error(`unknown page request: ${t}`);return n}o(c,"requestForTicket");function d(t){try{const n=JSON.parse(t.specification_json);if(typeof n!="object"||n===null)throw new Error("not an object");return n}catch(n){throw new Error(`corrupt page specification: ${t.request_id}`,{cause:n})}}o(d,"manifest");function u(t,n){if(t.action_json!==null)try{const e=JSON.parse(t.action_json);if(e.schema!=="crtr.human-action-binding/v1"||!Array.isArray(e.argv)||typeof e.cwd!="string")throw new Error("invalid binding");return{destinationId:e.name,adapter:{adapter:"subprocess",argv:e.argv,cwd:e.cwd,node_id:t.requester_node_id,...e.app?{app:e.app}:{}},bodyJson:j({requestId:t.request_id,result:n,binding:e,source:d(t).source??{}})}}catch(e){throw new Error(`corrupt frozen completion action: ${t.request_id}`,{cause:e})}}o(u,"completionAction");function O(t,n=80){const e=t.split(`
|
|
2
|
+
`)[0]??"";return e.length>n?`${e.slice(0,n-1)}\u2026`:e}o(O,"firstLine");function x(t){try{return d(t).title}catch{return}}o(x,"pageTitle");function w(t){let n;try{const e=T(t.request_dir);if(e.companionNodeId===void 0)return"";n=[],e.firstCommentedSha256!==void 0&&A(t.request_dir)!==e.firstCommentedSha256&&n.push(`The page changed during feedback: the person commented on it and its companion conversation (node ${e.companionNodeId}) revised it in place. Reread the final page at ${N(q(t),d(t).dialect)} before interpreting this outcome.`);const r=e.comments.filter(i=>i.status==="open");if(r.length>0){const i=r.map(s=>[...s.quote.split(`
|
|
3
3
|
`).map(a=>`> ${a}`),s.note].join(`
|
|
4
4
|
`));n.push([`Feedback left unresolved at settlement \u2014 the companion conversation (node ${e.companionNodeId}) had not dealt with these items:`,...i].join(`
|
|
5
5
|
|
|
@@ -7,4 +7,4 @@ var _=Object.defineProperty;var r=(t,n)=>_(t,"name",{value:n,configurable:!0});i
|
|
|
7
7
|
|
|
8
8
|
${n.join(`
|
|
9
9
|
|
|
10
|
-
`)}`}
|
|
10
|
+
`)}`}o(w,"feedbackAddendum");function E(t,n){const e=k(d(t),n.responses,{completedAt:n.completedAt});return S(e)+w(t)}o(E,"answerBody");function I(t,n,e){const r=x(t),i=r===void 0?`the human interaction ${t.request_id}`:`\u201C${r}\u201D`,s=e!==void 0&&e!==""?e:"no reason was given";return(n?`The user dismissed ${i} without answering \u2014 a deliberate \u201Cno, I'm not answering this.\u201D Treat it as a rejection: no response will arrive, no partial answer exists, and the same ask must not be raised again.`:`${r===void 0?`The human interaction ${t.request_id}`:`The page \u201C${r}\u201D`} was canceled \u2014 ${s}. No response will arrive, and any partial answers the user had entered were discarded unsent.`)+w(t)}o(I,"cancelBody");function g(t,n,e,r){if(t.requester_node_id!==null)return{label:O(e),disposition:n,body_json:{body:e,result:r}}}o(g,"notification");function l(t){return t.companion_node_id===null?void 0:{retire:o(async(n,e)=>{await f(n,e)},"retire")}}o(l,"retire");async function Q(t,n,e={}){return(await J(t,n,e)).result}o(Q,"resolvePageTicket");async function J(t,n,e={}){const r=c(t),i=d(r),s=$(i,n),a={schema:"crtr.page-response/v1",kind:"page",responses:s,summary:v(i,s),completedAt:e.completedAt??new Date().toISOString(),...e.actor===void 0?{}:{actor:e.actor}},m=await h({request_id:r.request_id,outcome:"answered",result_json:JSON.stringify(a),notification:o(p=>g(p,"human-answer",E(p,a),a),"notification"),...u(r,a)===void 0?{}:{completion_action:u(r,a)},...l(r)===void 0?{}:{post_commit:l(r)}});if(m.outcome==="already_settled"){if(m.request.result_json===null)throw new Error(`settled page request lacks a result: ${r.request_id}`);return{outcome:"already_settled",result:JSON.parse(m.request.result_json)}}return{outcome:"settled",result:a}}o(J,"answerPageTicket");async function U(t,n){const e=c(t),i={schema:"humanloop.cancel/v1",kind:"canceled",canceledAt:new Date().toISOString(),...n.reason===void 0?{}:{reason:n.reason},...n.actor===void 0?{}:{actor:n.actor},...n.disposition===void 0?{}:{disposition:n.disposition}},s=await h({request_id:e.request_id,outcome:n.disposition==="dismissed"?"dismissed":"canceled",result_json:JSON.stringify(i),notification:o(a=>g(a,"human-canceled",I(a,n.disposition==="dismissed",n.reason),i),"notification"),...u(e,i)===void 0?{}:{completion_action:u(e,i)},...l(e)===void 0?{}:{post_commit:l(e)}});if(s.outcome==="already_settled"){if(s.request.result_json===null)throw new Error(`settled page request lacks a result: ${e.request_id}`);return{outcome:"already_settled",result:JSON.parse(s.request.result_json)}}return{outcome:"settled",result:i}}o(U,"cancelHumanTicket");async function V(t){return c(t).state==="settled"}o(V,"deliverTerminalResult");async function W(t,n){const e=c(t);e.companion_node_id!==null&&await f(e.companion_node_id,n)}o(W,"settleFeedbackCompanion");export{J as answerPageTicket,U as cancelHumanTicket,V as deliverTerminalResult,Q as resolvePageTicket,W as settleFeedbackCompanion};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var _=Object.defineProperty;var
|
|
1
|
+
var _=Object.defineProperty;var d=(e,n)=>_(e,"name",{value:n,configurable:!0});import{withCanvasWrite as m}from"../../core/canvas/db.js";import{cancelDelivery as f,enqueueExternalAction as w,enqueueNodeMail as p}from"../../core/feed/messages.js";import{getRequest as l,markCompanionRetired as y,markProjected as q,markProjectionError as h,setSettlementDeliveryMarksLocked as j,settleRequestLocked as v}from"../../core/human/requests.js";import{appendQuestionAnswered as k}from"../../core/runs/questions.js";async function I(e){const n=m(i=>{const u=e.settled_at??new Date().toISOString(),t=v(i,{request_id:e.request_id,outcome:e.outcome,result_json:e.result_json,settled_at:u});if(t===null){const c=l(e.request_id);if(c===null)throw new Error(`human request disappeared while settling: ${e.request_id}`);return{outcome:"already_settled",request:c}}const o=typeof e.notification=="function"?e.notification(t):e.notification;if(t.requester_node_id!==null&&o===void 0)throw new Error(`request ${t.request_id} needs a requester notification`);if(t.action_json!==null&&t.kind==="page"&&e.completion_action===void 0)throw new Error(`request ${t.request_id} needs its frozen completion action`);if(t.kind==="review"&&t.projected_at===null&&e.post_commit?.project===void 0)throw new Error(`review ${t.request_id} needs a projection effect`);if(t.companion_node_id!==null&&t.companion_retired_at===null&&e.post_commit?.retire===void 0)throw new Error(`request ${t.request_id} needs a companion retirement effect`);let a=null,s=null;if(t.requester_node_id!==null){if(o===void 0)throw new Error(`request ${t.request_id} needs a requester notification`);a=p(t.requester_node_id,{kind:"completed",label:o.label,senderName:"crtrd",...o.body_ref===void 0?{}:{bodyRef:o.body_ref},...o.body_json===void 0?{}:{bodyJson:o.body_json},disposition:o.disposition==="human-answer"?"human-answer":"human-canceled",urgency:"normal",wake:1}).deliveryId}t.action_json!==null&&e.completion_action!==void 0&&t.kind==="page"&&(s=w({...e.completion_action,idempotencyKey:`human-action:${t.inbox_ticket_id}`}).deliveryId),t.submit_note_delivery_id!==null&&f(t.submit_note_delivery_id),j(i,t.request_id,{settlement_delivery_id:a,action_delivery_id:s});const r=l(t.request_id);if(r===null)throw new Error(`settled human request disappeared: ${t.request_id}`);return k(i,r),{outcome:"settled",request:r}});return n.outcome==="settled"&&e.post_commit!==void 0&&await g(n.request,e.post_commit),n}d(I,"settleHumanRequest");async function g(e,n){if(e.kind==="review"&&e.projected_at===null&&n.project!==void 0)try{await n.project(e),q(e.request_id,new Date().toISOString())}catch(i){h(e.request_id,i instanceof Error?i.message:String(i))}if(e.companion_node_id!==null&&e.companion_retired_at===null&&n.retire!==void 0)try{await n.retire(e.companion_node_id,e.outcome==="canceled"||e.outcome==="dismissed"?"canceled":"approved"),y(e.request_id,new Date().toISOString())}catch{}}d(g,"projectAndRetire");export{g as projectAndRetire,I as settleHumanRequest};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type StorageClient, type StorageConfig } from '../../core/storage/client.js';
|
|
2
|
+
import type { DetachedWorkLifecycle } from './broker-supervision.js';
|
|
3
|
+
export declare const STORAGE_DUE_WORK_INTERVAL_MS = 60000;
|
|
4
|
+
/** An upload no run has used this long after creation is released. */
|
|
5
|
+
export declare const UNUSED_UPLOAD_TTL_MS: number;
|
|
6
|
+
/** Storage deletes attempted per kind per pass; the rest wait for the next pass. */
|
|
7
|
+
export declare const STORAGE_DUE_WORK_BUDGET = 200;
|
|
8
|
+
/** Consecutive `storage_unavailable` failures after which a pass stops early. */
|
|
9
|
+
export declare const STORAGE_DOWN_AFTER = 3;
|
|
10
|
+
export interface StorageDueWorkContext {
|
|
11
|
+
lifecycle: DetachedWorkLifecycle;
|
|
12
|
+
}
|
|
13
|
+
export interface StorageDueWorkDeps {
|
|
14
|
+
config: () => StorageConfig | null;
|
|
15
|
+
client: () => StorageClient;
|
|
16
|
+
}
|
|
17
|
+
export interface StorageDueWorkResult {
|
|
18
|
+
marked_uploads: number;
|
|
19
|
+
expired_shares: number;
|
|
20
|
+
deleted_uploads: number;
|
|
21
|
+
deleted_share_objects: number;
|
|
22
|
+
failed: number;
|
|
23
|
+
}
|
|
24
|
+
export declare class StorageDueWorkReconciler {
|
|
25
|
+
private readonly deps;
|
|
26
|
+
private lastPassAt;
|
|
27
|
+
private inFlight;
|
|
28
|
+
constructor(deps?: StorageDueWorkDeps);
|
|
29
|
+
run(now: number, ctx: StorageDueWorkContext): void;
|
|
30
|
+
/** One pass over the due-work rows. Exposed for tests. */
|
|
31
|
+
pass(now: number): Promise<StorageDueWorkResult>;
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var v=Object.defineProperty;var d=(l,t)=>v(l,"name",{value:t,configurable:!0});import{withCanvasWrite as E}from"../../core/canvas/db.js";import{emitEvent as c}from"../../core/events/emit.js";import{operationIdContext as p}from"../../core/events/operation-id.js";import{RunError as S}from"../../core/runs/operations.js";import{storageClient as k,storageConfig as O}from"../../core/storage/client.js";import{deleteUploadRow as U,expireShares as w,markShareObjectDeleted as R,markUploadsDeletePending as b,pendingUploadDeletes as A,sharesOwingObjectDelete as T,staleUnusedUploads as x}from"../../core/storage/tables.js";const I=6e4,y=1440*6e4,D=200,N=3,F=d(l=>l instanceof S&&l.code==="storage_unavailable","unavailable");class M{static{d(this,"StorageDueWorkReconciler")}deps;lastPassAt=Number.NEGATIVE_INFINITY;inFlight=!1;constructor(t={config:O,client:k}){this.deps=t}run(t,o){if(this.inFlight||t-this.lastPassAt<I||!o.lifecycle.acceptsDetachedWork())return;this.lastPassAt=t;let i;try{i=this.deps.config()}catch(e){p.fresh(()=>c({level:"error",event:"storage.due_work.failed",error:e}));return}if(!i)return;this.inFlight=!0;const f=p.fresh(()=>this.pass(t)).then(()=>{}).catch(e=>{p.fresh(()=>c({level:"error",event:"storage.due_work.failed",error:e}))}).finally(()=>{this.inFlight=!1});o.lifecycle.registerDetached(f)}async pass(t){const o=new Date(t).toISOString(),{marked:i,expired:f}=E(s=>{const a=x(new Date(t-y).toISOString(),s);return b(a,s,o),{marked:a.length,expired:w(o,s).length}}),e={marked_uploads:i,expired_shares:f,deleted_uploads:0,deleted_share_objects:0,failed:0},h=A().slice(0,D),_=T().slice(0,D);if(h.length||_.length){const s=this.deps.client();let a,n=0,u=!1;const g=d(async r=>{if(u)return!1;try{return await r(),n=0,!0}catch(m){return e.failed++,a??=m,n=F(m)?n+1:0,n>=N&&(u=!0),!1}},"attempt");for(const r of h)await g(()=>s.deleteUpload(r.upload_id))&&(U(r.upload_id),e.deleted_uploads++);for(const r of _)await g(()=>s.deleteShare(r.share_id))&&(R(r.share_id),e.deleted_share_objects++);a!==void 0&&c({level:"warn",event:"storage.due_work.deletes_failed",error:a,fields:{failed:e.failed,storage_unavailable:u}})}return(e.marked_uploads||e.expired_shares||e.deleted_uploads||e.deleted_share_objects)&&c({level:"info",event:"storage.due_work.settled",outcome:"succeeded",fields:{...e}}),e}}export{N as STORAGE_DOWN_AFTER,D as STORAGE_DUE_WORK_BUDGET,I as STORAGE_DUE_WORK_INTERVAL_MS,M as StorageDueWorkReconciler,y as UNUSED_UPLOAD_TTL_MS};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var y=Object.defineProperty;var p=(e,
|
|
2
|
-
`):""}p(
|
|
1
|
+
var y=Object.defineProperty;var p=(e,s)=>y(e,"name",{value:s,configurable:!0});import{appendRunEvent as g}from"../../core/runs/events.js";import{openQuestionIds as E}from"../../core/runs/questions.js";const T="crtr-turn-input",h="crtr-system-prompt",x="crtr-turn-end",l=p(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"zero");function R(e){return typeof e=="string"?e:Array.isArray(e)?e.filter(s=>typeof s?.text=="string").map(s=>s.text).join(`
|
|
2
|
+
`):""}p(R,"text");function k(e){return{input_tokens:e.input??0,output_tokens:e.output??0,cache_read_tokens:e.cacheRead??0,cache_write_tokens:e.cacheWrite??0,cost_usd:e.cost?.total??0}}p(k,"usageParts");function S(e,s,_,m,f){let u=f;for(const n of m){if(n.type==="custom"&&n.customType===T){const t=n.data?.message_ids;if(!Array.isArray(t)||!t.every(o=>typeof o=="string"))throw new Error("invalid crtr-turn-input message_ids");u=t}const r=p((t,o)=>g(e,s,_,t,{...o,entry_id:n.id}),"emit");if(n.type==="custom"&&n.customType===h){const t=n.data?.text;if(typeof t!="string")throw new Error("invalid crtr-system-prompt text");r("node.system_prompt.set",{text:t})}if(n.type==="custom"&&n.customType===x){const t=n.data?.stop_reason;if(typeof t!="string")throw new Error("invalid crtr-turn-end stop_reason");const o=e.prepare("SELECT body_json FROM run_events WHERE run_id = ? AND node_id = ? AND type = 'node.turn.started' ORDER BY sequence_number DESC LIMIT 1").get(s,_);if(!o)throw new Error("crtr-turn-end without node.turn.started");const{input:c}=JSON.parse(o.body_json),i=e.prepare("SELECT status, pi_pid FROM node_rows WHERE run_id = ? AND node_id != ?").all(s,_).some(a=>a.pi_pid!==null&&a.status==="active")?"running":E(s,e).length?"waiting_on_user":"idle";r("node.turn.completed",{message_ids:c.message_ids,run_status:i,stop_reason:t})}if(n.type==="message"){const t=n.message;if(t.role==="user"&&(r("node.turn.started",{input:{text:R(t.content),message_ids:u}}),u=[]),t.role==="assistant"){for(const o of t.content)o.type==="thinking"&&r("node.thinking.done",{text:o.thinking}),o.type==="text"&&r("node.output_text.done",{text:o.text,stop_reason:t.stopReason}),o.type==="toolCall"&&r("node.tool_call.started",{tool_call_id:o.id,name:o.name,arguments:o.arguments});if(t.usage){const o=k(t.usage),c=e.prepare("SELECT usage_json FROM nodes WHERE node_id = ?").get(s),d=c.usage_json?JSON.parse(c.usage_json):l(),i=l();for(const a of Object.keys(i))i[a]=d[a]+o[a];e.prepare("UPDATE nodes SET usage_json = ? WHERE node_id = ?").run(JSON.stringify(i),s),r("usage",{served_model:t.model,...o,usage:i})}}t.role==="toolResult"&&r("node.tool_call.completed",{tool_call_id:t.toolCallId,name:t.toolName,result:t.content,status:t.isError?"error":"ok"})}}return u}p(S,"appendConversationEvents");export{h as SYSTEM_PROMPT_TYPE,x as TURN_END_TYPE,T as TURN_INPUT_TYPE,S as appendConversationEvents};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var f=Object.defineProperty;var a=(
|
|
1
|
+
var f=Object.defineProperty;var a=(n,t)=>f(n,"name",{value:t,configurable:!0});import{randomUUID as g}from"node:crypto";import{openDb as d}from"../../core/canvas/db.js";import{getNode as l}from"../../core/canvas/canvas.js";import{BrokerClient as I,SocketTransport as S}from"../../core/broker-client/client.js";import{emitRunEvent as h,emitRunSettlement as m}from"../../core/runs/events.js";import{cancelOpenQuestions as D}from"../../core/runs/questions.js";import{emitEvent as R}from"../../core/events/emit.js";import{toRunOutcomeDTO as N}from"../api/map.js";const u=new Map;class v{static{a(this,"RunObserver")}nodeId;runId;client;timer;status;outcomeRevision;openAssistant=!1;assistantStartSequence=0;thinking="";output="";closed=!1;constructor(t,e){this.nodeId=t,this.runId=e;const i=d(),o=i.prepare("SELECT body_json FROM run_events WHERE node_id = ? AND type = ? ORDER BY sequence_number DESC LIMIT 1").get(t,t===e?"node.settled":"child.settled");o&&(this.outcomeRevision=JSON.parse(o.body_json).outcome.revision);const s=i.prepare("SELECT body_json FROM run_events WHERE node_id = ? AND type = 'node.status.changed' ORDER BY sequence_number DESC LIMIT 1").get(t);s&&(this.status=JSON.parse(s.body_json).status),this.timer=setInterval(()=>this.poll(),250),this.timer.unref(),u.set(t,this),this.poll()}poll(){const t=l(this.nodeId);if(!t||t.run_id!==this.runId){this.close();return}const e=t.pi_pid===null?"dormant":t.status;this.status!==e&&h(this.runId,this.nodeId,"node.status.changed",{status:e,reason:t.intent??null}),this.status=e;const i=N(t);if(i&&i.revision!==this.outcomeRevision&&Date.now()-Date.parse(i.settled_at)<720*60*6e4){if(this.nodeId===this.runId){const o=d().prepare("SELECT node_id FROM nodes WHERE run_id = ? AND node_id != ? ORDER BY created DESC").all(this.runId,this.runId);for(const s of o)E(s.node_id,this.runId),u.get(s.node_id)?.poll()}if(this.outcomeRevision=i.revision,this.nodeId===this.runId?m(this.runId,this.nodeId,"node.settled",{outcome:i,usage:JSON.parse(t.usage_json??"{}")}):m(this.runId,this.nodeId,"child.settled",{child_node_id:this.nodeId,outcome:i}),t.grantee&&t.grantee!=="app:terminal"){const o=this.nodeId===this.runId?{run_id:this.runId}:{node_id:this.nodeId};D(o,this.nodeId===this.runId?"the run settled":"the asking node was closed").catch(s=>R({level:"error",event:"daemon.runs.question_cancel_failed",node_id:this.nodeId,error:s}))}}t.pi_pid===null?this.close():this.connect()}get assistantMessageStartSequence(){return this.openAssistant&&this.client&&l(this.nodeId)?.pi_pid!=null?this.assistantStartSequence:null}connect(){if(this.client||this.closed)return;const t=new I(this.nodeId,{transport:new S(this.nodeId,{recordsViewerFaults:!1})});this.client=t,t.on("connect",()=>t.send({type:"hello",role:"observer",client_id:g(),attends:!1})),t.on("frame",e=>{if(e.type==="message_start"&&e.message.role==="assistant"&&(this.openAssistant=!0,this.thinking="",this.output="",this.assistantStartSequence=d().prepare("SELECT event_cursor FROM nodes WHERE node_id = ?").get(this.runId).event_cursor),e.type==="message_update"&&e.message.role==="assistant"&&this.openAssistant){if(d().prepare("SELECT 1 FROM run_events WHERE run_id = ? AND node_id = ? AND sequence_number >= ? AND type IN ('node.output_text.done', 'node.thinking.done', 'usage') LIMIT 1").get(this.runId,this.nodeId,this.assistantStartSequence)){this.openAssistant=!1;return}const o=e.message.content;if(!Array.isArray(o))return;const s=o.filter(r=>r?.type==="thinking").map(r=>r.thinking).join(""),c=o.filter(r=>r?.type==="text").map(r=>r.text).join(""),p=s.startsWith(this.thinking)?s.slice(this.thinking.length):s,_=c.startsWith(this.output)?c.slice(this.output.length):c;this.thinking=s,this.output=c,p&&h(this.runId,this.nodeId,"node.thinking.delta",{delta:p}),_&&h(this.runId,this.nodeId,"node.output_text.delta",{delta:_})}e.type==="message_end"&&e.message.role==="assistant"&&(this.openAssistant=!1)}),t.on("error",()=>{}),t.on("close",()=>{this.client===t&&(this.client=void 0,this.openAssistant=!1)}),t.connect()}close(){this.closed=!0,clearInterval(this.timer),u.delete(this.nodeId);const t=this.client;this.client=void 0,t?.close()}}function q(n){return u.get(n)?.assistantMessageStartSequence??null}a(q,"openAssistantMessageStartSequence");function E(n,t){if(!u.has(n)){if(n!==t){const e=l(n);!d().prepare("SELECT 1 FROM run_events WHERE run_id = ? AND node_id = ? AND type = 'child.started' LIMIT 1").get(t,n)&&e&&Date.now()-Date.parse(e.created)<720*60*6e4&&h(t,n,"child.started",{child_node_id:n,kind:e.kind})}new v(n,t)}}a(E,"ensureRunObserver");function w(){const n=new Date(Date.now()-2592e6).toISOString(),t=d().prepare(`SELECT n.node_id, n.run_id FROM node_rows n WHERE n.run_id IS NOT NULL AND (n.pi_pid IS NOT NULL OR (n.outcome_at IS NOT NULL AND n.outcome_at > ?
|
|
2
2
|
AND n.outcome_reason != 'parked' AND NOT EXISTS (
|
|
3
3
|
SELECT 1 FROM run_events e WHERE e.run_id = n.run_id AND e.node_id = n.node_id AND e.type = CASE WHEN n.node_id = n.run_id THEN 'node.settled' ELSE 'child.settled' END
|
|
4
|
-
AND json_extract(e.body_json, '$.outcome.revision') = COALESCE(n.outcome_revision, 1))))`).all(
|
|
4
|
+
AND json_extract(e.body_json, '$.outcome.revision') = COALESCE(n.outcome_revision, 1))))`).all(n);for(const e of t)E(e.node_id,e.run_id)}a(w,"pollRunObservers");export{E as ensureRunObserver,q as openAssistantMessageStartSequence,w as pollRunObservers};
|
package/dist/types.d.ts
CHANGED
|
@@ -381,6 +381,12 @@ export interface ScopeConfig {
|
|
|
381
381
|
};
|
|
382
382
|
jwksUrl?: string;
|
|
383
383
|
person_uids?: number[];
|
|
384
|
+
/** Hosting file storage: `service_url` is the storage token audience and API
|
|
385
|
+
* base; `base_url` prefixes storage URLs in message text. Absent: no file transfer. */
|
|
386
|
+
storage?: {
|
|
387
|
+
service_url: string;
|
|
388
|
+
base_url: string;
|
|
389
|
+
};
|
|
384
390
|
};
|
|
385
391
|
/** Extra env-var names/globs a scope explicitly admits across the broker
|
|
386
392
|
* spawn-env boundary (`buildBrokerEnv`, `core/runtime/spawn-env.ts`) — the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/crouter",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.353",
|
|
4
4
|
"description": "crtr — agent runtime with memory, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
"@earendil-works/pi-ai": "0.87.1",
|
|
132
132
|
"@earendil-works/pi-coding-agent": "0.87.1",
|
|
133
133
|
"@earendil-works/pi-tui": "0.87.1",
|
|
134
|
-
"@north-light/crouter-identity": "^0.3.
|
|
134
|
+
"@north-light/crouter-identity": "^0.3.353",
|
|
135
135
|
"cron-parser": "^5.6.0",
|
|
136
136
|
"esbuild": "^0.27.7",
|
|
137
137
|
"jose": "^6.2.12",
|
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/crouter",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.353",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/crouter",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.353",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "GPL-3.0-only",
|
|
12
12
|
"workspaces": [
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@earendil-works/pi-ai": "0.87.1",
|
|
19
19
|
"@earendil-works/pi-coding-agent": "0.87.1",
|
|
20
20
|
"@earendil-works/pi-tui": "0.87.1",
|
|
21
|
-
"@north-light/crouter-identity": "^0.3.
|
|
21
|
+
"@north-light/crouter-identity": "^0.3.353",
|
|
22
22
|
"cron-parser": "^5.6.0",
|
|
23
23
|
"esbuild": "^0.27.7",
|
|
24
24
|
"jose": "^6.2.12",
|
|
@@ -10885,17 +10885,17 @@
|
|
|
10885
10885
|
},
|
|
10886
10886
|
"packages/crouter-api": {
|
|
10887
10887
|
"name": "@north-light/crouter-api",
|
|
10888
|
-
"version": "0.3.
|
|
10888
|
+
"version": "0.3.353",
|
|
10889
10889
|
"license": "GPL-3.0-only"
|
|
10890
10890
|
},
|
|
10891
10891
|
"packages/crouter-env-docker": {
|
|
10892
10892
|
"name": "@north-light/crouter-env-docker",
|
|
10893
|
-
"version": "0.3.
|
|
10893
|
+
"version": "0.3.353",
|
|
10894
10894
|
"license": "GPL-3.0-only"
|
|
10895
10895
|
},
|
|
10896
10896
|
"packages/crouter-identity": {
|
|
10897
10897
|
"name": "@north-light/crouter-identity",
|
|
10898
|
-
"version": "0.3.
|
|
10898
|
+
"version": "0.3.353",
|
|
10899
10899
|
"license": "GPL-3.0-only",
|
|
10900
10900
|
"dependencies": {
|
|
10901
10901
|
"jose": "^6.2.1"
|
|
@@ -10903,19 +10903,19 @@
|
|
|
10903
10903
|
},
|
|
10904
10904
|
"packages/crouter-plugin": {
|
|
10905
10905
|
"name": "@north-light/crouter-plugin",
|
|
10906
|
-
"version": "0.3.
|
|
10906
|
+
"version": "0.3.353",
|
|
10907
10907
|
"license": "GPL-3.0-only",
|
|
10908
10908
|
"dependencies": {
|
|
10909
|
-
"@north-light/crouter-api": "^0.3.
|
|
10909
|
+
"@north-light/crouter-api": "^0.3.353"
|
|
10910
10910
|
}
|
|
10911
10911
|
},
|
|
10912
10912
|
"packages/crouter-sdk": {
|
|
10913
10913
|
"name": "@north-light/crouter-sdk",
|
|
10914
|
-
"version": "0.3.
|
|
10914
|
+
"version": "0.3.353",
|
|
10915
10915
|
"license": "GPL-3.0-only",
|
|
10916
10916
|
"dependencies": {
|
|
10917
|
-
"@north-light/crouter-api": "^0.3.
|
|
10918
|
-
"@north-light/crouter-identity": "^0.3.
|
|
10917
|
+
"@north-light/crouter-api": "^0.3.353",
|
|
10918
|
+
"@north-light/crouter-identity": "^0.3.353",
|
|
10919
10919
|
"jose": "^6.2.1"
|
|
10920
10920
|
}
|
|
10921
10921
|
}
|