@north-light/cli 0.3.555 → 0.3.557
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 +172 -172
- package/dist/commands/node/bash.js +2 -2
- package/dist/core/bash-job-supervisor.d.ts +1 -1
- package/dist/core/bash-job-supervisor.js +3 -3
- package/dist/core/bash-jobs.d.ts +26 -0
- package/dist/core/bash-jobs.js +10 -8
- package/dist/core/canvas/export-sqlite.js +1 -1
- package/dist/core/canvas/import-sqlite.d.ts +1 -1
- package/dist/core/canvas/import-sqlite.js +16 -4
- package/dist/core/canvas/schema-pg/003-fleet-removals.sql +52 -0
- package/dist/core/canvas/schema-version.d.ts +1 -1
- package/dist/core/canvas/schema-version.js +1 -1
- package/dist/core/graph/kinds/job.js +2 -2
- package/dist/core/ledger/index.d.ts +1 -2
- package/dist/core/ledger/index.js +1 -1
- package/dist/core/ledger/totals.d.ts +3 -2
- package/dist/core/ledger/totals.js +4 -4
- package/dist/core/runtime/broker/bash-job-launch.js +1 -1
- package/dist/daemon/api/handlers/bash-jobs.js +1 -1
- package/dist/daemon/crtrd.js +10 -10
- package/dist/daemon/reconcilers/bash-deadline.d.ts +8 -0
- package/dist/daemon/reconcilers/bash-deadline.js +3 -3
- package/dist/shared/env.d.ts +4 -0
- package/dist/shared/env.js +1 -1
- package/dist/shared/inbox-entry-body.js +7 -7
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
- package/dist/core/ledger/backfill.d.ts +0 -11
- package/dist/core/ledger/backfill.js +0 -19
|
@@ -1 +1 @@
|
|
|
1
|
-
var B=Object.defineProperty;var l=(e,o)=>B(e,"name",{value:o,configurable:!0});import{killBashJobObject as E,recordBashJobObject as J}from"../../../core/spaces/stop.js";import{readerFor as P}from"../reader.js";import{join as
|
|
1
|
+
var B=Object.defineProperty;var l=(e,o)=>B(e,"name",{value:o,configurable:!0});import{killBashJobObject as E,recordBashJobObject as J}from"../../../core/spaces/stop.js";import{readerFor as P}from"../reader.js";import{join as A}from"node:path";import{createFileBeneath as g,existsBeneath as m,unlinkBeneath as $}from"../../../core/spaces/open-beneath.js";import{launchAppProcess as q,appProcessSandboxed as O}from"../../../core/spaces/process.js";import{BASH_JOB_SUPERVISOR_SCRIPT as N}from"../../../core/bash-job-supervisor.js";import{resolveAdmittedHostCommand as L}from"../../../core/runtime/spawn-env.js";import{requireCurrentExecution as _}from"./broker-recovery.js";import{isSafeNodeId as T}from"../../../core/canvas/paths.js";import{contextDir as v}from"../../../core/canvas/node-agent-paths.js";import{getNode as f}from"../../../core/canvas/canvas.js";import{isBrokerLive as D}from"../../../core/runtime/model-swap.js";import{assertNotFinalized as R}from"../../../core/runtime/reopen.js";import{reviveNode as C}from"../../../core/runtime/revive.js";import{checkStart as z}from"../../../core/runtime/start-check.js";import{activeBackgroundBashJobs as F,bashJobPaths as k,isBashJobProcessGroupAlive as M,readJobPgid as G,readJobPgidIdentity as H,isSafeBashJobId as I,readBashNiceness as Z,stopBackgroundBashJob as U}from"../../../core/bash-jobs.js";import{ApiError as i}from"../../../api/index.js";import{notFound as V}from"../../../core/errors.js";import{freshChildNodeExecPath as X}from"../../../core/runtime/branded-host.js";async function w(e){const o=e.params.id;if(o===void 0||!T(o))throw new i(400,"invalid_node_id",`invalid node id: ${JSON.stringify(o)}`);if(await f(o)===null)throw V(`unknown node: ${o}`,{received:o});const a=e.params.jobId;if(a!==void 0){if(!I(a))throw new i(400,"invalid_job_id",`invalid background job id: ${JSON.stringify(a)}`);return{nodeId:o,jobId:a,dir:await v(o)}}return{nodeId:o,dir:await v(o)}}l(w,"requireTarget");async function x(e){const{nodeId:o,dir:n}=await w(e);if(e.principal.class!=="broker"||e.principal.node!==o)throw new i(403,"permission_error","only this node\u2019s broker may start a job");const a=e.body;if(!a||typeof a!="object"||Array.isArray(a))throw new i(400,"invalid_request","job.start requires a JSON body");const d=a;await _(o,d.expected_execution_id);const r=d.job_id,s=d.cwd,p=d.env;if(typeof r!="string"||!I(r)||typeof s!="string"||!p||typeof p!="object"||Array.isArray(p)||Object.entries(p).some(([c,b])=>!/^[A-Za-z_][A-Za-z0-9_]*$/.test(c)||typeof b!="string"))throw new i(400,"invalid_request","invalid job id, cwd or environment");const h=await f(o);if(await z({grantee:h.grantee??"app:terminal",sub:h.sub,grant_id:h.grant_id}),!O())throw new i(400,"invalid_request","job.start is only used with the root launcher");const t=k(n,r);if(!(m(t.root,t.jobRun)||m(t.root,t.jobBg))||![t.cmdSh,t.jobLog].every(c=>m(t.root,c)))throw new i(404,"job_not_found","job was not prepared by this node");const j=A(t.dir,"job.starting");try{g(t.root,j,"")}catch(c){throw c.code==="EEXIST"?new i(409,"job_already_started","job was already started"):c}let u;try{const c=p,b=L("bash",c,s),y=await f(o),S=await X();return u=await q({nodeId:o,app:y.grantee??"app:terminal",isolated:y.isolated,cwd:s,env:c,capture:!1,command:[b,"-c",N,"--",t.cmdSh,t.jobLog,t.jobExit,t.jobRun,t.jobDone,b,String(Z(n)),S,t.jobActivity]}),g(t.root,t.jobPgid,String(u.pid),384),g(t.root,t.jobPgidIdentity,`launcher:${u.pid}`,384),{status:201,body:{pid:u.pid}}}catch(c){if(u!==void 0)try{await u.stop()}catch(b){throw new AggregateError([c,b],`job ${r} launched but could not be recorded or stopped`)}try{$(t.root,j)}catch(b){if(b.code!=="ENOENT")throw b}throw c}}l(x,"handleStart");async function K(e){const{nodeId:o,dir:n}=await w(e),a=Date.now();return{status:200,body:F(n).map(r=>{const s=G(n,r.jobId)??null;return{job_id:r.jobId,command:r.command,purpose:r.purpose,started_at:new Date(r.startedAtMs).toISOString(),elapsed_ms:Math.max(0,a-r.startedAtMs),pgid:s,pgid_alive:s!==null&&M(s,H(n,r.jobId))}})}}l(K,"handleList");async function Q(e){const{nodeId:o,jobId:n,dir:a}=await w(e);if(n===void 0)throw new i(400,"invalid_request","a job id is required");const d=e.query.get("foreground")==="1";if(d){if(e.principal.class!=="broker"||e.principal.node!==o)throw new i(403,"permission_error","only this node\u2019s broker may abort its foreground job");await _(o,e.body?.expected_execution_id)}const r=await U(a,n,d,o);if(r.kind==="missing")throw new i(404,"job_not_found",`no background job ${n} for ${o}`);if(r.kind==="retired")throw new i(404,"job_not_found",`background job ${n} for ${o} is no longer running`,{log_path:r.logPath});if(r.kind==="not-stoppable")throw new i(409,"job_not_stoppable",`job ${n} recorded no trusted process identity`,{log_path:r.logPath});if(d)return{status:200,body:{node_id:o,job_id:n,signaled:r.signaled}};await R(o),await E(n,async h=>{const t=await P(h,e);return t.class==="person"?t.person:t.class==="app"?t.app:t.node});const s=await f(o);if(s!==null&&!D(s))try{await C(o,{resume:!0,capacity:"freeze"})}catch{}return{status:200,body:{node_id:o,job_id:n,signaled:r.signaled}}}l(Q,"handleStop");async function W(e){const{nodeId:o,jobId:n,dir:a}=await w(e);if(n===void 0)throw new i(400,"invalid_request","a job id is required");if(e.principal.class!=="broker"||e.principal.node!==o)throw new i(403,"permission_error","only this node\u2019s broker may record its job");const d=e.body,r=k(a,n);if(typeof d?.command!="string"||d.job_dir!==r.dir||d.log_path!==r.jobLog)throw new i(400,"invalid_request",`job.background requires command, job_dir ${r.dir} and log_path ${r.jobLog}`);if(!m(r.root,r.jobBg))throw new i(404,"job_not_found",`job ${n} of ${o} is not backgrounded`);const s=d.command;return{status:201,body:{id:(await J(o,n,{command:s,jobDir:r.dir,logPath:r.jobLog})).id}}}l(W,"handleBackground");const _o=[{method:"POST",pattern:"/v1/nodes/:id/jobs/:jobId/background",handler:W},{method:"POST",pattern:"/v1/nodes/:id/jobs/start",handler:x},{method:"GET",pattern:"/v1/nodes/:id/jobs",handler:K},{method:"DELETE",pattern:"/v1/nodes/:id/jobs/:jobId",handler:Q}];export{_o as bashJobRoutes};
|
package/dist/daemon/crtrd.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
var Ae=Object.defineProperty;var i=(n,s)=>Ae(n,"name",{value:s,configurable:!0});import{backfillLedger as Me}from"../core/ledger/backfill.js";import{startPeerOutbox as Ne}from"./peer/outbox.js";import{reportMachineStatus as Fe}from"../core/peer/directory.js";import{createPeerRelay as Ge}from"./peer/relay-service.js";import{writeFileSync as Oe,readFileSync as Be,rmSync as He,mkdirSync as se,existsSync as k,chmodSync as je}from"node:fs";import{createServer as ce}from"node:http";import{dirname as qe}from"node:path";import{envCrtrdTcp as ze,envDocumentPort as We}from"../shared/env.js";import{serveCanvasConversationStore as Ue}from"../core/canvas/sessions.js";import{pollRunObservers as Ke}from"./runs/observer.js";import{randomBytes as de}from"node:crypto";import{apiSocketPath as Je,crtrHome as Ve}from"../core/canvas/paths.js";import{activeAppUsers as Qe}from"../core/spaces/users.js";import{ensureAppSpace as Ye,ensureUserFiles as Xe}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as x}from"../launcher/spec.js";import{launcherRequest as C}from"../launcher/client.js";import{installLauncherEventSink as Ze}from"./launcher-events.js";import{initializeEmptyLayout as er,layoutNeedsMigration as rr,socketDir as le}from"../core/layout.js";import{migrateLooseKeys as tr}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as nr}from"../core/profiles/relocate.js";import{getNode as or,listNodesForSupervision as pe,listParkedResidentsWithWakeMail as ir,setFrozen as me}from"../core/canvas/index.js";import{createApiServer as ar}from"./api/server.js";import{createDocumentListener as sr}from"./api/document-listener.js";import{documentSecret as cr}from"./api/document-secret.js";import{createAppListener as dr}from"./api/app-listener.js";import{registerMachineEnvironment as lr}from"../core/environments.js";import{resolvePerson as pr,retireLocalPerson as mr,setPersonId as fr}from"../core/canvas/person.js";import{backfillNodeApps as ur,runPermissionsBoot as wr}from"../core/permissions/boot.js";import{onRunEventAppended as hr}from"../core/runs/events.js";import{runNotices as vr}from"./run-notices.js";import{createGrantSync as gr,GRANT_SYNC_INTERVAL_S as yr}from"../core/grants/sync.js";import{runRemovalCascades as Sr}from"../core/grants/remove.js";import{sweepSandboxes as _r}from"../core/grants/on-sync.js";import{rebuildCronExecutions as br}from"./cron/run-socket.js";import{hasSyncedGrants as kr}from"../core/grants/mirror.js";import{readConfig as fe}from"../core/config.js";import{reconcileUndeliveredTickets as Pr}from"./human/sweep.js";import{resetHandedOffDeliveries as Er}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as Rr}from"../core/canvas/daemon-actions.js";import{sweepReviews as Ir}from"./review/sweep.js";import{pidfilePath as K}from"./pidfile.js";import{rendererWarning as xr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as Dr}from"./supervise-cadence.js";import{createResumeDetector as Tr}from"./resume-detector.js";import{setFatalFaultAnnouncer as Cr}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as Lr}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as $r}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as Ar}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as Mr}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as Nr}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as Fr}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as Gr}from"./reconcilers/action-lane.js";import{endLiveExecution as Or,migrateLegacyExecutionIdentities as Br}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as Hr}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as jr}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as qr}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as zr}from"./reconcilers/storage-due-work.js";import{BashDeadlineReconciler as Wr}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as Ur}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as Kr}from"./reconcilers/structured-request-lane.js";import{reconcileStructuredRequestSidecars as Jr}from"./structured-requests/boot.js";import{captureLivenessSnapshot as ue,capturePidCommand as Vr,captureTeardownSnapshot as we,isPidAlive as Qr,isAnyPidAlive as he,killProcessTreePids as ve}from"../core/canvas/pid.js";import{bindFleet as Yr,boundFleet as Xr}from"../core/runtime/fleet.js";import{bindDaemonControl as Zr,clearDaemonControl as et}from"./control.js";import{execDaemon as rt,spawnDaemon as tt}from"./manage.js";import{reconcileAndMigrateBoot as nt}from"../core/canvas/boot.js";import{resolveInstallId as ot}from"../core/canvas/install-id.js";import{setInstallId as it}from"../core/runtime/nodes.js";import{claimDaemonOwnership as ge,daemonOwnershipClaimed as at,releaseDaemonOwnership as ye}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as st,closeAllDbs as J,openDb as Se,withCanvasWrite as ct}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as dt}from"../core/canvas/import-sqlite.js";import{ensurePostgres as lt,ensurePostgresHealthy as pt,stopPostgres as mt,withPostgresStartLock as ft}from"./postgres/index.js";import{brokerSignalBus as V}from"./broker-signals/bus.js";import{brokerSignalStreams as ut}from"./api/handlers/broker-signals.js";import{reconcilePackages as wt}from"../core/graph/package-reconcile.js";import{startRepoSync as ht}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as vt,reapStaleSpares as gt}from"../core/runtime/warm-pool.js";import{DaemonFleet as yt}from"./fleet.js";import{BOOT_MIGRATIONS as St}from"./boot-migrations.js";import{emitEvent as m}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as _t}from"../core/events/source.js";import{activateCanvasImport as bt,MigrationBlockedError as kt,runForcedCanvasImport as Pt}from"../migrations/activation.js";import{convertLegacyProviderStore as Et,convertLegacyScopedStore as Rt}from"../core/scoped-state/migrate.js";import{userScopeRoot as It}from"../core/scope.js";import{secretsDir as xt}from"../core/layout.js";function Q(n,s){return n.includes(` --canvas-home ${s} `)}i(Q,"brokerCommandDeclaresCanvasHome");function Y(n){return/ --epoch (\S+) /.exec(n)?.[1]??null}i(Y,"brokerCommandEpoch");const Dt=5e3,_e=150;async function Tt(n){const s=ue();if(s===null)throw m({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 l=Ve(),w=[];for(const[a,u]of s.processes){const y=u.command;if(u.zombie||!y.includes("broker-cli.js")||!Q(y,l)||Y(y)===n)continue;const P=y.trim().split(/\s+/).at(-1)??"";P===""||P.endsWith("broker-cli.js")||w.push({pid:a,nodeId:P})}let d=0;const t=[];for(const a of w){const u=Vr(a.pid);if(u===null||!u.includes("broker-cli.js")||!Q(u,l)||Y(u)===n||u.trim().split(/\s+/).at(-1)!==a.nodeId)continue;const y=we(a.pid,null);if(!y.reused){try{process.platform==="linux"&&k(x)?await C({operation:"stop",pid:a.pid}):process.kill(a.pid,"SIGTERM")}catch{continue}d++,t.push({pid:a.pid,snap:y})}}const v=Date.now()+Dt;for(;Date.now()<v&&t.some(a=>he(a.snap.tree.length>0?a.snap.tree:[a.pid]));)await new Promise(a=>setTimeout(a,_e));let h=0;for(const a of t){const u=a.snap.tree.length>0?a.snap.tree:[a.pid];he(u)&&(process.platform==="linux"&&k(x)?await C({operation:"stop",pid:a.pid}):ve(u,"SIGKILL",a.snap.identities),h++)}h>0&&await new Promise(a=>setTimeout(a,_e));const f=ue();if(f===null)throw m({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 c=[];for(const[a,u]of f.processes){const y=u.command;u.zombie||!y.includes("broker-cli.js")||Q(y,l)&&Y(y)!==n&&c.push(a)}if(c.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${c.join(", ")}`);m({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:w.length,sigtermed:d,sigkilled:h}})}i(Tt,"reapPriorEpochBrokers");const Ct=5e3;async function Lt(n){const s=Array.from(n.entries());if(s.length===0)return;for(const[t]of s){const v=(await or(t))?.status;(v==="active"||v==="idle")&&await me(t,new Date().toISOString())}const l=s.map(([,t])=>({pid:t.pid,snap:we(t.pid,null),exited:t.handle.exited}));for(const t of l)if(!t.snap.reused)try{process.platform==="linux"&&k(x)?await C({operation:"stop",pid:t.pid}):process.kill(t.pid,"SIGTERM")}catch{}const w=new Promise(t=>{setTimeout(t,Ct).unref()});await Promise.allSettled(l.map(t=>Promise.race([t.exited.then(()=>{}),w])));const d=[];for(const t of l)Qr(t.pid)&&(process.platform==="linux"&&k(x)?await C({operation:"stop",pid:t.pid}):ve(t.snap.tree.length>0?t.snap.tree:[t.pid],"SIGKILL",t.snap.identities),d.push(t));await Promise.all(d.map(t=>t.exited));for(const[t]of s)await Or(t,{reason:"daemon-teardown"});m({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:l.length,sigkilled:d.length}})}i(Lt,"teardownFleet");function be(){se(qe(K()),{recursive:!0}),Oe(K(),String(process.pid),"utf8")}i(be,"writePidfile");function ke(n){return typeof n=="object"&&n!==null&&n.code==="ENOENT"}i(ke,"isMissingPathError");function Pe(n){const s=K();let l;try{l=Be(s,"utf8")}catch(w){if(ke(w))return;throw w}if(l.trim()!==String(n))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${n})`);try{He(s)}catch(w){if(ke(w))return;throw w}}i(Pe,"removeOwnedPidfileMirror");const $t={acceptsDetachedWork:i(()=>!0,"acceptsDetachedWork"),registerDetached:i(n=>{n.catch(()=>{})},"registerDetached")},H=new Lr;Cr(n=>H.handleFatalFault(n,!1));const At=new $r,Mt=new Ar,Nt=new Mr,Ft=new Nr,Gt=new Fr,Ot=new Gr,Bt=new jr,Ht=new qr,jt=new zr,qt=new Wr,zt=new Ur,Wt=new Kr;async function Ut(n=Date.now(),s=$t){const l=Xr(),w=await H.runFleetPreamble(l);let d,t;try{const h=await pe();d=h.filter(c=>c.status==="active"||c.status==="idle"),t=h.filter(c=>c.status==="done"||c.status==="canceled"||c.status==="dead");const f=new Set(t.map(c=>c.node_id));t.push(...(await ir()).filter(c=>!f.has(c.node_id)))}catch(h){m({level:"error",event:"canvas.node_list.failed",error:h});return}await H.run(n,{rows:d,fleet:l,lifecycle:s}),await At.run(n,{rows:d});const v=await Mt.run(n,{rows:[...d,...t],fleet:l});await Nt.run(n),await Wt.run(n),await Ft.run(n,{lifecycle:s}),await Gt.run(n,{lifecycle:s}),await Ot.run(n,{lifecycle:s}),await Bt.run(n,{lifecycle:s}),await Ht.run(n,{lifecycle:s,fleet:l}),jt.run(n,{lifecycle:s}),await Ke(),zt.run(n,{fleet:l,lifecycle:s}),await qt.run(n,{rows:d}),H.warnIfSaturated({fleet:l,lifecycle:s,thresholds:w,frozen:v})}i(Ut,"superviseTick");async function Kt(n){se(le(),{recursive:!0,mode:493}),process.platform==="linux"&&je(le(),493);const s=Je();if(k(s))throw new Error(`layout refusal cannot bind occupied socket: ${s}`);const l=ce((f,c)=>{if(f.method==="GET"&&f.url==="/healthz"){c.writeHead(200,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}c.writeHead(503,{"content-type":"application/json; charset=utf-8"}),c.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((f,c)=>{l.once("error",c),l.listen(s,()=>{l.off("error",c),f()})});let w;const d=n.tcp??ze();if(d){const f=/^(.*):(\d+)$/.exec(d);if(!f||Number(f[2])>65535)throw new Error(`invalid refusal TCP address: ${d}`);const c=ce(l.listeners("request")[0]);await new Promise((a,u)=>{c.once("error",u),c.listen(Number(f[2]),f[1],()=>{c.off("error",u),a()})}),w=c}be();let t;const v=i(()=>(t??=(async()=>{l.closeAllConnections(),w?.closeAllConnections(),await Promise.all([l,w].filter(f=>f!==void 0).map(f=>new Promise((c,a)=>f.close(u=>u?a(u):c())))),Pe(process.pid),process.off("SIGINT",h),process.off("SIGTERM",h)})(),t),"cleanup"),h=i(()=>{v().then(()=>process.exit(0),f=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(f)}
|
|
2
|
-
`),process.exit(1)})},"onSignal");return process.on("SIGINT",h),process.on("SIGTERM",h),
|
|
3
|
-
`)},"logPostgres"),l=!!process.env.CRTR_TEST_PG_URL,{database:w,ownership:d}=l?{database:{url:await
|
|
1
|
+
var Ae=Object.defineProperty;var i=(n,s)=>Ae(n,"name",{value:s,configurable:!0});import{startPeerOutbox as Me}from"./peer/outbox.js";import{reportMachineStatus as Ne}from"../core/peer/directory.js";import{createPeerRelay as Fe}from"./peer/relay-service.js";import{writeFileSync as Ge,readFileSync as Oe,rmSync as Be,mkdirSync as se,existsSync as k,chmodSync as He}from"node:fs";import{createServer as ce}from"node:http";import{dirname as je}from"node:path";import{envCrtrdTcp as qe,envDocumentPort as ze}from"../shared/env.js";import{serveCanvasConversationStore as We}from"../core/canvas/sessions.js";import{pollRunObservers as Ue}from"./runs/observer.js";import{randomBytes as de}from"node:crypto";import{apiSocketPath as Ke,crtrHome as Je}from"../core/canvas/paths.js";import{activeAppUsers as Ve}from"../core/spaces/users.js";import{ensureAppSpace as Qe,ensureUserFiles as Ye}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as x}from"../launcher/spec.js";import{launcherRequest as C}from"../launcher/client.js";import{installLauncherEventSink as Xe}from"./launcher-events.js";import{initializeEmptyLayout as Ze,layoutNeedsMigration as er,socketDir as le}from"../core/layout.js";import{migrateLooseKeys as rr}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as tr}from"../core/profiles/relocate.js";import{getNode as nr,listNodesForSupervision as pe,listParkedResidentsWithWakeMail as or,setFrozen as me}from"../core/canvas/index.js";import{createApiServer as ir}from"./api/server.js";import{createDocumentListener as ar}from"./api/document-listener.js";import{documentSecret as sr}from"./api/document-secret.js";import{createAppListener as cr}from"./api/app-listener.js";import{registerMachineEnvironment as dr}from"../core/environments.js";import{resolvePerson as lr,retireLocalPerson as pr,setPersonId as mr}from"../core/canvas/person.js";import{backfillNodeApps as fr,runPermissionsBoot as ur}from"../core/permissions/boot.js";import{onRunEventAppended as wr}from"../core/runs/events.js";import{runNotices as hr}from"./run-notices.js";import{createGrantSync as vr,GRANT_SYNC_INTERVAL_S as gr}from"../core/grants/sync.js";import{runRemovalCascades as yr}from"../core/grants/remove.js";import{sweepSandboxes as Sr}from"../core/grants/on-sync.js";import{rebuildCronExecutions as _r}from"./cron/run-socket.js";import{hasSyncedGrants as br}from"../core/grants/mirror.js";import{readConfig as fe}from"../core/config.js";import{reconcileUndeliveredTickets as kr}from"./human/sweep.js";import{resetHandedOffDeliveries as Pr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as Er}from"../core/canvas/daemon-actions.js";import{sweepReviews as Rr}from"./review/sweep.js";import{pidfilePath as K}from"./pidfile.js";import{rendererWarning as Ir}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as xr}from"./supervise-cadence.js";import{createResumeDetector as Dr}from"./resume-detector.js";import{setFatalFaultAnnouncer as Tr}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as Cr}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as Lr}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as $r}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as Ar}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as Mr}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as Nr}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as Fr}from"./reconcilers/action-lane.js";import{endLiveExecution as Gr,migrateLegacyExecutionIdentities as Or}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as Br}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as Hr}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as jr}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as qr}from"./reconcilers/storage-due-work.js";import{BashDeadlineReconciler as zr}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as Wr}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as Ur}from"./reconcilers/structured-request-lane.js";import{reconcileStructuredRequestSidecars as Kr}from"./structured-requests/boot.js";import{captureLivenessSnapshot as ue,capturePidCommand as Jr,captureTeardownSnapshot as we,isPidAlive as Vr,isAnyPidAlive as he,killProcessTreePids as ve}from"../core/canvas/pid.js";import{bindFleet as Qr,boundFleet as Yr}from"../core/runtime/fleet.js";import{bindDaemonControl as Xr,clearDaemonControl as Zr}from"./control.js";import{execDaemon as et,spawnDaemon as rt}from"./manage.js";import{reconcileAndMigrateBoot as tt}from"../core/canvas/boot.js";import{resolveInstallId as nt}from"../core/canvas/install-id.js";import{setInstallId as ot}from"../core/runtime/nodes.js";import{claimDaemonOwnership as ge,daemonOwnershipClaimed as it,releaseDaemonOwnership as ye}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as at,closeAllDbs as J,openDb as Se,withCanvasWrite as st}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as ct}from"../core/canvas/import-sqlite.js";import{ensurePostgres as dt,ensurePostgresHealthy as lt,stopPostgres as pt,withPostgresStartLock as mt}from"./postgres/index.js";import{brokerSignalBus as V}from"./broker-signals/bus.js";import{brokerSignalStreams as ft}from"./api/handlers/broker-signals.js";import{reconcilePackages as ut}from"../core/graph/package-reconcile.js";import{startRepoSync as wt}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as ht,reapStaleSpares as vt}from"../core/runtime/warm-pool.js";import{DaemonFleet as gt}from"./fleet.js";import{BOOT_MIGRATIONS as yt}from"./boot-migrations.js";import{emitEvent as m}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as St}from"../core/events/source.js";import{activateCanvasImport as _t,MigrationBlockedError as bt,runForcedCanvasImport as kt}from"../migrations/activation.js";import{convertLegacyProviderStore as Pt,convertLegacyScopedStore as Et}from"../core/scoped-state/migrate.js";import{userScopeRoot as Rt}from"../core/scope.js";import{secretsDir as It}from"../core/layout.js";function Q(n,s){return n.includes(` --canvas-home ${s} `)}i(Q,"brokerCommandDeclaresCanvasHome");function Y(n){return/ --epoch (\S+) /.exec(n)?.[1]??null}i(Y,"brokerCommandEpoch");const xt=5e3,_e=150;async function Dt(n){const s=ue();if(s===null)throw m({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 l=Je(),w=[];for(const[a,u]of s.processes){const y=u.command;if(u.zombie||!y.includes("broker-cli.js")||!Q(y,l)||Y(y)===n)continue;const P=y.trim().split(/\s+/).at(-1)??"";P===""||P.endsWith("broker-cli.js")||w.push({pid:a,nodeId:P})}let d=0;const t=[];for(const a of w){const u=Jr(a.pid);if(u===null||!u.includes("broker-cli.js")||!Q(u,l)||Y(u)===n||u.trim().split(/\s+/).at(-1)!==a.nodeId)continue;const y=we(a.pid,null);if(!y.reused){try{process.platform==="linux"&&k(x)?await C({operation:"stop",pid:a.pid}):process.kill(a.pid,"SIGTERM")}catch{continue}d++,t.push({pid:a.pid,snap:y})}}const g=Date.now()+xt;for(;Date.now()<g&&t.some(a=>he(a.snap.tree.length>0?a.snap.tree:[a.pid]));)await new Promise(a=>setTimeout(a,_e));let h=0;for(const a of t){const u=a.snap.tree.length>0?a.snap.tree:[a.pid];he(u)&&(process.platform==="linux"&&k(x)?await C({operation:"stop",pid:a.pid}):ve(u,"SIGKILL",a.snap.identities),h++)}h>0&&await new Promise(a=>setTimeout(a,_e));const f=ue();if(f===null)throw m({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 c=[];for(const[a,u]of f.processes){const y=u.command;u.zombie||!y.includes("broker-cli.js")||Q(y,l)&&Y(y)!==n&&c.push(a)}if(c.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${c.join(", ")}`);m({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:w.length,sigtermed:d,sigkilled:h}})}i(Dt,"reapPriorEpochBrokers");const Tt=5e3;async function Ct(n){const s=Array.from(n.entries());if(s.length===0)return;for(const[t]of s){const g=(await nr(t))?.status;(g==="active"||g==="idle")&&await me(t,new Date().toISOString())}const l=s.map(([,t])=>({pid:t.pid,snap:we(t.pid,null),exited:t.handle.exited}));for(const t of l)if(!t.snap.reused)try{process.platform==="linux"&&k(x)?await C({operation:"stop",pid:t.pid}):process.kill(t.pid,"SIGTERM")}catch{}const w=new Promise(t=>{setTimeout(t,Tt).unref()});await Promise.allSettled(l.map(t=>Promise.race([t.exited.then(()=>{}),w])));const d=[];for(const t of l)Vr(t.pid)&&(process.platform==="linux"&&k(x)?await C({operation:"stop",pid:t.pid}):ve(t.snap.tree.length>0?t.snap.tree:[t.pid],"SIGKILL",t.snap.identities),d.push(t));await Promise.all(d.map(t=>t.exited));for(const[t]of s)await Gr(t,{reason:"daemon-teardown"});m({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:l.length,sigkilled:d.length}})}i(Ct,"teardownFleet");function be(){se(je(K()),{recursive:!0}),Ge(K(),String(process.pid),"utf8")}i(be,"writePidfile");function ke(n){return typeof n=="object"&&n!==null&&n.code==="ENOENT"}i(ke,"isMissingPathError");function Pe(n){const s=K();let l;try{l=Oe(s,"utf8")}catch(w){if(ke(w))return;throw w}if(l.trim()!==String(n))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${n})`);try{Be(s)}catch(w){if(ke(w))return;throw w}}i(Pe,"removeOwnedPidfileMirror");const Lt={acceptsDetachedWork:i(()=>!0,"acceptsDetachedWork"),registerDetached:i(n=>{n.catch(()=>{})},"registerDetached")},H=new Cr;Tr(n=>H.handleFatalFault(n,!1));const $t=new Lr,At=new $r,Mt=new Ar,Nt=new Mr,Ft=new Nr,Gt=new Fr,Ot=new Hr,Bt=new jr,Ht=new qr,jt=new zr,qt=new Wr,zt=new Ur;async function Wt(n=Date.now(),s=Lt){const l=Yr(),w=await H.runFleetPreamble(l);let d,t;try{const h=await pe();d=h.filter(c=>c.status==="active"||c.status==="idle"),t=h.filter(c=>c.status==="done"||c.status==="canceled"||c.status==="dead");const f=new Set(t.map(c=>c.node_id));t.push(...(await or()).filter(c=>!f.has(c.node_id)))}catch(h){m({level:"error",event:"canvas.node_list.failed",error:h});return}await H.run(n,{rows:d,fleet:l,lifecycle:s}),await $t.run(n,{rows:d});const g=await At.run(n,{rows:[...d,...t],fleet:l});await Mt.run(n),await zt.run(n),await Nt.run(n,{lifecycle:s}),await Ft.run(n,{lifecycle:s}),await Gt.run(n,{lifecycle:s}),await Ot.run(n,{lifecycle:s}),await Bt.run(n,{lifecycle:s,fleet:l}),Ht.run(n,{lifecycle:s}),await Ue(),qt.run(n,{fleet:l,lifecycle:s}),await jt.run(n,{rows:d}),H.warnIfSaturated({fleet:l,lifecycle:s,thresholds:w,frozen:g})}i(Wt,"superviseTick");async function Ut(n){se(le(),{recursive:!0,mode:493}),process.platform==="linux"&&He(le(),493);const s=Ke();if(k(s))throw new Error(`layout refusal cannot bind occupied socket: ${s}`);const l=ce((f,c)=>{if(f.method==="GET"&&f.url==="/healthz"){c.writeHead(200,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}c.writeHead(503,{"content-type":"application/json; charset=utf-8"}),c.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((f,c)=>{l.once("error",c),l.listen(s,()=>{l.off("error",c),f()})});let w;const d=n.tcp??qe();if(d){const f=/^(.*):(\d+)$/.exec(d);if(!f||Number(f[2])>65535)throw new Error(`invalid refusal TCP address: ${d}`);const c=ce(l.listeners("request")[0]);await new Promise((a,u)=>{c.once("error",u),c.listen(Number(f[2]),f[1],()=>{c.off("error",u),a()})}),w=c}be();let t;const g=i(()=>(t??=(async()=>{l.closeAllConnections(),w?.closeAllConnections(),await Promise.all([l,w].filter(f=>f!==void 0).map(f=>new Promise((c,a)=>f.close(u=>u?a(u):c())))),Pe(process.pid),process.off("SIGINT",h),process.off("SIGTERM",h)})(),t),"cleanup"),h=i(()=>{g().then(()=>process.exit(0),f=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(f)}
|
|
2
|
+
`),process.exit(1)})},"onSignal");return process.on("SIGINT",h),process.on("SIGTERM",h),g}i(Ut,"serveLayoutRefusal");async function Kt(n={}){if(er())return Ut(n);Ze();const s=i(r=>{process.stderr.write(`[crtrd] ${r}
|
|
3
|
+
`)},"logPostgres"),l=!!process.env.CRTR_TEST_PG_URL,{database:w,ownership:d}=l?{database:{url:await at()},ownership:await ge(de(16).toString("hex"))}:await mt(async()=>({database:await dt({log:s,startLockHeld:!0}),ownership:await ge(de(16).toString("hex"))}));if(!d.claimed){if(d.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${d.incumbentPid??"?"})
|
|
4
4
|
`);else{const r=[d.incumbentPid!==void 0?`pid ${d.incumbentPid}`:void 0,d.verdict!==void 0?`verdict ${d.verdict}`:void 0].filter(S=>S!==void 0),e=r.length>0?` (${r.join(", ")})`:"";process.stderr.write(`[crtrd] claim lost: ${d.reason}${e}
|
|
5
|
-
`),d.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(d.reason==="own_identity_indeterminate"||d.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const t=d.claim;let
|
|
6
|
-
`);if(process.platform==="darwin"){const{readConfig:e,updateConfig:S}=await import("../core/config.js");if(e("user").paths?.user_files===void 0){const{homedir:o}=await import("node:os");S("user",p=>{p.paths??={},p.paths.user_files??=o()})}}await
|
|
7
|
-
`);await Se(),await
|
|
5
|
+
`),d.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(d.reason==="own_identity_indeterminate"||d.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const t=d.claim;let g=null,h=null,f=null,c="";try{await Dt(t.token),Et({kind:"user",root:Rt()});for(const e of tr())process.stderr.write(`[crtrd] ${e}
|
|
6
|
+
`);if(process.platform==="darwin"){const{readConfig:e,updateConfig:S}=await import("../core/config.js");if(e("user").paths?.user_files===void 0){const{homedir:o}=await import("node:os");S("user",p=>{p.paths??={},p.paths.user_files??=o()})}}await Pt(It());for(const e of rr())process.stderr.write(`[crtrd] ${e}
|
|
7
|
+
`);await Se(),await ct(w.url,s),await V.listen(w.url);const r=await st(async e=>(await fr(e),await ut(e)));for(const e of r.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${e.message}
|
|
8
8
|
`);for(const e of r.invalid)process.stderr.write(`[crtrd] package file is not a document: ${e.path}: ${e.error}
|
|
9
9
|
`);for(const e of r.quarantined)process.stderr.write(`[crtrd] plugin "${e.name}" has a damaged ${e.file} (${e.reason}); ${e.movedTo!==void 0?`moved to ${e.movedTo}`:"left in place and skipped"}
|
|
10
10
|
`);for(const e of r.kinds.refused)process.stderr.write(`[crtrd] plugin "${e.plugin}" kind ${e.kind} not registered: ${e.message}
|
|
11
11
|
`);for(const e of r.kinds.invalid)process.stderr.write(`[crtrd] plugin "${e.plugin}" declares invalid object kinds or scopes; no kinds registered and no app token: ${e.error}
|
|
12
|
-
`);c=await
|
|
12
|
+
`);c=await nt(),ot(c),g=await lr(),g!==null&&(mr(g.id),h=await pr(),f=await ur()),process.platform==="linux"&&k(x)&&(Xe(),await C({operation:"users.sync",users:await Ve()})),await Br(t.token)}catch(r){await V.close();try{const{released:e}=await ye(t);if(!e)throw new Error("daemon ownership claim was not released");await J()}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}let a=!0,u,y=Promise.resolve(),P=!1,N,D;const F=new Set;let j=!1,X=!1,E,G,q,Z=!1,O,L,$,A,z;const Ee=Dr();let R,v={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},T="initial startup activation",Re=null;function Ie(){return v.phase==="ready"?null:{phase:v.phase,message:v.message,blockerPaths:v.blockerPaths}}i(Ie,"startupBlock");function b(r){F.add(r),r.finally(()=>{F.delete(r)}).catch(()=>{})}i(b,"registerDetached");const ee={acceptsDetachedWork:i(()=>a,"acceptsDetachedWork"),registerDetached:b};await wt(ee,Se);async function xe(){try{a=!1,ft.close(),await V.close(),N!==void 0&&(clearInterval(N),N=void 0),$!==void 0&&(clearInterval($),$=void 0),z?.(),z=void 0;let r;do r=y,await r;while(r!==y);for(;F.size>0;)await Promise.allSettled([...F]);if(D!==void 0&&(await Ct(D),await D.exitsSettled(),D.dispose()),q!==void 0&&await q.close(),G!==void 0&&!Z&&(Z=!0,await G.close()),A!==void 0){const e=A;A=void 0,await e.stop()}if(L!==void 0){const e=L;L=void 0,await e.close()}if(O!==void 0){const e=O;O=void 0,await e.close()}if(j||(Pe(process.pid),j=!0),j&&!X){const{released:e}=await ye(t);if(!e)throw new Error("daemon ownership claim was not released");X=!0,await J()}}finally{Zr(),process.off("SIGINT",ne),process.off("SIGTERM",oe),process.off("exit",ie)}}i(xe,"cleanupAttempt");const B=1e3;let re=!1;function De(){return re?{graceMs:B}:(re=!0,_.fresh(()=>{m({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:B}})}),setTimeout(()=>{Te()},B),{graceMs:B})}i(De,"requestHandover");async function Te(){try{await I()}catch(r){const e=r instanceof Error?r.stack??r.message:String(r);process.stderr.write(`[crtrd] handover cleanup failed:
|
|
13
13
|
${e}
|
|
14
|
-
`),process.exit(1)}try{process.platform==="linux"&&k(x)&&(await C({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await
|
|
15
|
-
`),process.exit(1)}process.exit(0)}i(Te,"performHandover");function I(){if(E!==void 0)return E;const r=xe();return E=r,r.then(()=>{E===r&&(E=void 0)},()=>{E===r&&(E=void 0)}),r}i(I,"requestCleanup");async function te(r){try{await I(),await
|
|
14
|
+
`),process.exit(1)}try{process.platform==="linux"&&k(x)&&(await C({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await et();const r=await rt({waitForJoinLock:!0});_.fresh(()=>{m({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:r.pid??r.existing_pid??null,spawned:r.started}})})}catch(r){_.fresh(()=>{m({level:"error",event:"daemon.handover.successor_failed",error:r})});const e=r instanceof Error?r.message:String(r);process.stderr.write(`[crtrd] handover successor failed to start: ${e}
|
|
15
|
+
`),process.exit(1)}process.exit(0)}i(Te,"performHandover");function I(){if(E!==void 0)return E;const r=xe();return E=r,r.then(()=>{E===r&&(E=void 0)},()=>{E===r&&(E=void 0)}),r}i(I,"requestCleanup");async function te(r){try{await I(),await pt({claimed:i(async()=>{try{return await it()}finally{await J()}},"claimed")}),process.exit(0)}catch(e){process.exitCode=1;const S=e instanceof Error?e.stack??`${e.name}: ${e.message}`:String(e);process.stderr.write(`[crtrd] ${r} cleanup failed:
|
|
16
16
|
${S}
|
|
17
|
-
`),process.exit(1)}}i(te,"handleSignal");function ne(){te("SIGINT")}i(ne,"onSigint");function oe(){te("SIGTERM")}i(oe,"onSigterm");function ie(){I().catch(()=>{})}i(ie,"onExit");async function W(r){a&&(y=y.then(r).then(()=>{},e=>{_.fresh(()=>{m({level:"error",event:"daemon.lane_job.failed",error:e})})}))}i(W,"enqueue");async function ae(){!a||P||(Ee.check()&&R&&(m({level:"info",event:"daemon.resume.detected"}),b(R().catch(()=>{}))),P=!0,await W(async()=>{if(P=!1,!!a)try{l||await
|
|
17
|
+
`),process.exit(1)}}i(te,"handleSignal");function ne(){te("SIGINT")}i(ne,"onSigint");function oe(){te("SIGTERM")}i(oe,"onSigterm");function ie(){I().catch(()=>{})}i(ie,"onExit");async function W(r){a&&(y=y.then(r).then(()=>{},e=>{_.fresh(()=>{m({level:"error",event:"daemon.lane_job.failed",error:e})})}))}i(W,"enqueue");async function ae(){!a||P||(Ee.check()&&R&&(m({level:"info",event:"daemon.resume.detected"}),b(R().catch(()=>{}))),P=!0,await W(async()=>{if(P=!1,!!a)try{l||await lt({log:s}),await Wt(Date.now(),ee)}catch(r){_.fresh(()=>{m({level:"error",event:"daemon.tick.failed",error:r})})}}))}i(ae,"enqueueTick");let M=null;async function Ce(){if(M!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${M instanceof Error?M.message:String(M)}`};if(v.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(v.phase!=="prepared"&&v.phase!=="recovering")throw{status:409,code:"not_prepared",message:v.message};const r=v.phase==="prepared";try{return await U(),v={phase:"ready",message:"",blockerPaths:[]},r?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(e){throw M=e,{status:500,code:"admission_failed",message:`daemon admission failed: ${e instanceof Error?e.message:String(e)}`}}}i(Ce,"admitStartup");async function Le(r){if(T!==null)throw{status:409,code:"migration_in_progress",message:`the canvas import is already running (${T})`};T="repair";try{const e=await kt({dryRun:r.dryRun});if(r.dryRun||e.pending||v.phase!=="blocked")return{result:e,resumed:!1};if(n.prepare)return v={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:e,resumed:!1};try{return await U(),v={phase:"ready",message:"",blockerPaths:[]},{result:e,resumed:!0}}catch(S){const o=S instanceof Error?S.message:String(S);return setTimeout(()=>{I().catch(()=>{})},1e3),{result:e,resumed:!1,resumeError:o}}}finally{T=null}}i(Le,"repairMigrations");async function $e(){u=n.intervalMs??xr;const r=t.token;D=new gt({epoch:r,enqueue:W}),Qr(D),v={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{m({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:u}})});const e=c;_.fresh(()=>{m({level:"info",event:"daemon.install_id.resolved",fields:{install_id:e}})});const S=await dr(e);if(_.fresh(()=>{m({level:"info",event:"daemon.environment.registered",fields:{environment_id:S.environmentId,created:S.created}})}),_.fresh(()=>{let o;try{o=Ir()}catch{return}o!==null&&m({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:o.slice(0,2048)}})}),await _.fresh(async()=>{await Rr(),await kr(),await Pr(Date.now()),await Er(Date.now())}),await _.fresh(async()=>{const{reconcile:o,migrated:p}=await tt(Or);o.changed&&m({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:o.cleared.length,same_boot_proven:o.sameBootProven}}),p.length>0&&m({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:p.length}})}),await _.fresh(async()=>{for(const o of await pe())o.status!=="active"||o.pi_pid!=null||o.frozen_at!=null||o.respawn_not_before!=null||(await me(o.node_id,new Date().toISOString()),m({level:"warn",event:"daemon.startup.launch_debt_recorded",node_id:o.node_id}))}),await _.fresh(async()=>{await Kr();for(const o of yt){const p=o.apply();p.changed&&m({level:"info",event:"daemon.boot_migration.applied",fields:{migration:o.id,...p.summary}})}}),b(_r()),R){const o=i(()=>{b(R().catch(()=>{})),b(Ne().then(p=>A?.observe(p)).then(()=>{},p=>{m({level:"warn",event:"daemon.peer.status_failed",error:p})}))},"refresh");b(yr().catch(()=>{})),b(Sr().then(({completion:p})=>p).catch(p=>{m({level:"error",event:"daemon.grants.sweep_failed",error:p})})),o(),$=setInterval(o,gr*1e3),$.unref(),z=Me({joined:!0,register:b})}await W(async()=>{await vt(),await ht()}),N=setInterval(()=>{ae().catch(o=>m({level:"error",event:"daemon.tick.failed",error:o}))},u),await ae()}i($e,"completeStartup");function U(){return Re??=$e().catch(r=>{throw v={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},r})}i(U,"resumeStartup");try{process.on("SIGINT",ne),process.on("SIGTERM",oe),process.on("exit",ie),St(),g!==null&&m({level:"info",event:"daemon.person.resolved",fields:{person_id:g.id,source:g.source}}),f!==null&&m({level:"info",event:"daemon.permissions.boot",fields:{...f}}),h!==null&&m({level:"info",event:"daemon.person.retired",fields:{person_id:h.person,joined_from:h.joinedFrom,retired:h.retired,columns:h.columns}}),O=await We();const r=t.token;Xr({epoch:r,requestHandover:De,registerDetached:b,startupBlock:Ie,admitStartup:Ce,repairMigrations:Le}),be(),G=ir({tcp:n.tcp}),await G.ready;const e=ze()??fe("user").document_port,S=ar(e,sr());try{await S.ready,q=S}catch(p){m({level:"warn",event:"daemon.document_listener.bind_failed",fields:{port:e,error:p instanceof Error?p.message:String(p)}})}const o=fe("user").runtime;o?.issuer&&(R=vr(o.issuer,b),o.unsandboxed===!0&&(A=Fe({syncGrants:R})),wr(p=>hr().appended(p))),o?.unsandboxed===!0&&o.listener!==void 0&&m({level:"warn",event:"daemon.app_listener.refused_unsandboxed",fields:{reason:"runtime.unsandboxed is set; runtime.listener is ignored"}}),o!==void 0&&o.listener!==void 0&&o.unsandboxed!==!0&&(L=await cr(o,async()=>await br(o.issuer.replace(/\/$/,"")),R),await L.ready);try{await _t()}catch(p){if(T=null,p instanceof bt)return v={phase:"blocked",message:p.message,blockerPaths:p.blockerPaths},I;throw p}process.platform==="linux"&&k(x)&&(Ye(),await Qe("app:terminal"));try{n.prepare?v={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await U(),v={phase:"ready",message:"",blockerPaths:[]})}finally{T=null}return I}catch(r){try{await I()}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}}i(Kt,"runDaemon");var xo=Kt;export{Q as brokerCommandDeclaresCanvasHome,Y as brokerCommandEpoch,xo as default,Kt as runDaemon,Wt as superviseTick,Ct as teardownFleet};
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { NodeRow } from '../../core/canvas/index.js';
|
|
2
|
+
/** The silence message due for a job silent for `silentMs`, given the threshold last announced: the
|
|
3
|
+
* largest of `base`, 2×`base`, 4×`base`, then every 4×`base` (15, 30, 60 min, then hourly) that the
|
|
4
|
+
* silence has reached and that was not announced yet. Null when none is due. */
|
|
5
|
+
export declare function dueSilenceThresholdMs(silentMs: number, announcedMs: number, base: number): number | null;
|
|
2
6
|
export interface BashDeadlineContext {
|
|
3
7
|
rows: NodeRow[];
|
|
4
8
|
}
|
|
@@ -17,6 +21,10 @@ export declare class BashDeadlineReconciler {
|
|
|
17
21
|
/** An exit written before this daemon started is history, not a job to adopt. */
|
|
18
22
|
private readonly startedAt;
|
|
19
23
|
run(now: number, ctx: BashDeadlineContext): Promise<void>;
|
|
24
|
+
/** Tell the owner about a live background job that has shown no activity (see the supervisor's
|
|
25
|
+
* activity sampler) for 15 minutes, again at 30 and 60, then hourly. Any activity restarts the
|
|
26
|
+
* schedule; a job marked as an expected wait is skipped. Messages only: the job is never stopped. */
|
|
27
|
+
private warnSilent;
|
|
20
28
|
/** A running job whose supervisor wrote job.exit has exited: `finishJob` publishes `exited`
|
|
21
29
|
* once (the row leaves `running`). A job being stopped (job.stop) is ended by its killer. */
|
|
22
30
|
private publishExits;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
WHERE j.status = 'running' AND o.deleted_at IS NULL`).all();for(const{object:e,job_dir:
|
|
3
|
-
`)}catch(
|
|
1
|
+
var E=Object.defineProperty;var m=(s,t)=>E(s,"name",{value:t,configurable:!0});import{existsSync as a,readFileSync as S,readdirSync as x,statSync as B}from"node:fs";import{join as l}from"node:path";import{bashJobPaths as j,bashJobsDir as J,activeBackgroundBashJobs as v,expiredAutoBackgroundBashJobs as g,formatBashElapsed as M,isJobQuietOk as N,readJobActivity as I,readJobSilence as P,writeJobSilence as T,isBashJobProcessConfirmedGone as k,readJobPgid as C,readJobPgidIdentity as L,stopBackgroundBashJob as D}from"../../core/bash-jobs.js";import{killBashJobObject as O,recordBashJobObject as F}from"../../core/spaces/stop.js";import{createFileBeneath as R}from"../../core/spaces/open-beneath.js";import{withCanvasWrite as f}from"../../core/canvas/db.js";import{finishJob as A,jobRow as V}from"../../core/graph/jobs.js";import{publish as G}from"../../core/graph/events.js";import{envBashSilenceMs as U}from"../../shared/env.js";import{getById as h}from"../../core/graph/objects.js";import{isSafeNodeId as u}from"../../core/canvas/paths.js";import{contextDir as W}from"../../core/canvas/node-agent-paths.js";import{emitEvent as c}from"../../core/events/emit.js";import{operationIdContext as b}from"../../core/events/operation-id.js";import{isProfilePaused as X}from"../../core/profiles/manifest.js";const Y=300*1e3;function H(s,t,o){const e=s<o?0:s<2*o?o:s<4*o?2*o:4*o*Math.floor(s/(4*o));return e>t?e:null}m(H,"dueSilenceThresholdMs");const Q=5*1e3;class ft{static{m(this,"BashDeadlineReconciler")}lastScanAt=Number.NEGATIVE_INFINITY;startedAt=Date.now();async run(t,o){if(await this.publishExits(),!(t-this.lastScanAt<Q)){this.lastScanAt=t;for(const e of o.rows){if(e.status==="dead"||X(e.profile_id))continue;const r=await W(e.node_id);let i;try{await this.adoptUnrecorded(e.node_id,r),await this.settleVanished(r),i=g(r,t)}catch(n){b.fresh(()=>{c({level:"error",event:"bash_deadline.scan_failed",...u(e.node_id)?{node_id:e.node_id}:{},error:n})});continue}for(const n of i)await this.cancel(e.node_id,r,n.jobId);try{await this.warnSilent(r,t)}catch(n){b.fresh(()=>{c({level:"error",event:"bash_silence.scan_failed",...u(e.node_id)?{node_id:e.node_id}:{},error:n})})}}}}async warnSilent(t,o){const e=U();for(const{jobId:r}of v(t)){if(N(t,r))continue;const i=I(t,r);if(i===void 0||o-i.sampledAtMs>Y)continue;const n=o-i.lastActivityMs,y=P(t,r),_=H(n,y?.lastActivityMs===i.lastActivityMs?y.thresholdMs:0,e);if(_===null)continue;const w=j(t,r);await f(async p=>{const d=await h(p,r);d===null||d.type!=="job"||(await V(p,d))?.status!=="running"||await G(p,{event:"silent",object:d,actor:null,attrs:{"silent-for":M(n),files:i.files.join(", "),"log-path":w.jobLog}})}),T(w,i.lastActivityMs,_)}}async publishExits(){try{await f(async t=>{const o=await t.prepare(`SELECT j.object, j.job_dir FROM jobs j JOIN objects o ON o.id = j.object
|
|
2
|
+
WHERE j.status = 'running' AND o.deleted_at IS NULL`).all();for(const{object:e,job_dir:r}of o){const i=l(r,"job.exit");if(!a(i)||a(l(r,"job.stop")))continue;const n=Number(S(i,"utf8").trim());Number.isSafeInteger(n)&&await A(t,await h(t,e),{exitCode:n})}})}catch(t){b.fresh(()=>c({level:"error",event:"bash_completion.delivery_failed",error:t}))}}async adoptUnrecorded(t,o){let e;try{e=x(J(o))}catch{return}await f(async r=>{for(const i of e){if(await h(r,i)!==null)continue;const n=j(o,i);!a(n.jobBg)||!a(n.cmdSh)||a(n.jobExit)&&B(n.jobExit).mtimeMs<this.startedAt||await F(t,i,{command:S(n.cmdSh,"utf8"),jobDir:n.dir,logPath:n.jobLog})}})}async settleVanished(t){for(const{jobId:o}of v(t)){const e=j(t,o);a(e.jobExit)||a(l(e.dir,"job.stop"))||k(C(t,o),L(t,o))&&await f(async r=>{const i=await h(r,o);if(!(i===null||i.type!=="job")&&!(a(e.jobExit)||a(l(e.dir,"job.stop")))){await A(r,i,{killedBy:null,reason:"process disappeared without an exit record"});try{R(e.root,e.jobExit,`-1
|
|
3
|
+
`)}catch(n){if(n.code!=="EEXIST")throw n}}})}}async cancel(t,o,e){(await D(o,e,!1,t)).kind==="stopped"&&await b.fresh(async()=>{try{c({level:"info",event:"bash_deadline.canceled",...u(t)?{node_id:t}:{},fields:{job_id:e}}),await O(e,async()=>null,"deadline")}catch(i){c({level:"error",event:"bash_deadline.notice_failed",...u(t)?{node_id:t}:{},error:i,fields:{job_id:e}})}})}}export{ft as BashDeadlineReconciler,H as dueSilenceThresholdMs};
|
package/dist/shared/env.d.ts
CHANGED
|
@@ -124,6 +124,10 @@ export declare function envTestUnattendedParkMs(): number | undefined;
|
|
|
124
124
|
* above the configured knob a deployment sets to tune how long an eligible
|
|
125
125
|
* resident idles before its parking turn. */
|
|
126
126
|
export declare function envUnattendedParkMs(): number | undefined;
|
|
127
|
+
/** How long a background bash job must show no activity before its owner is first told
|
|
128
|
+
* (`CRTR_BASH_SILENCE_MS`, milliseconds; default 15 min). Later messages come at twice, four times,
|
|
129
|
+
* then every four times that. */
|
|
130
|
+
export declare function envBashSilenceMs(): number;
|
|
127
131
|
/** Test-only park-summary grace-period override
|
|
128
132
|
* (`CRTR_TEST_PARK_SUMMARY_GRACE_MS`); no production default. */
|
|
129
133
|
export declare function envTestParkSummaryGraceMs(): number | undefined;
|
package/dist/shared/env.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
var p=Object.defineProperty;var n=(e,r)=>p(e,"name",{value:r,configurable:!0});import{lstatSync as f,readFileSync as c,readdirSync as T}from"node:fs";import{join as i}from"node:path";import{covers as _}from"@north-light/identity";function W(){return process.env.CRTR_NODE_ID}n(W,"envNodeId");function d(e){for(const r of["broker-launch.json","cron-launch.json"]){const t=i(e,r);if(f(t,{throwIfNoEntry:!1})?.isFile())return t}}n(d,"launchSnapshotFile");function E(){if(process.platform==="linux")try{const e="/run/crtr/nodes",r=new Set(c("/proc/self/mountinfo","utf8").split(`
|
|
2
|
-
`).map(o=>o.split(" ")[4])),t=T(e,{withFileTypes:!0}).filter(o=>o.isDirectory()&&/^[a-zA-Z0-9][a-zA-Z0-9-]{0,127}$/.test(o.name)&&r.has(i(e,o.name))&&d(i(e,o.name))!==void 0);return t.length===1?t[0].name:void 0}catch{return}}n(E,"sandboxCliNodeId");function F(){return E()!==void 0}n(F,"isSandboxedNode");function h(){return process.env.CRTR_GRANTEE}n(h,"envGrantee");const v="CRTR_INBOX_BETWEEN_TURNS";function H(){return process.env[v]==="1"}n(H,"envInboxBetweenTurns");const C="CRTR_SCOPES_FILE";function K(){const e=process.env[C];if(e!==void 0&&e!==""){const t=JSON.parse(c(e,"utf8"));if(!Array.isArray(t)||t.some(o=>typeof o!="string"))throw new Error(`invalid scopes file ${e}`);return t}const r=process.env.CRTR_SCOPES;return r===void 0?null:r===""?[]:r.split(",")}n(K,"envScopes");function G(e,r){return e==null?!0:a(e,r)}n(G,"scopeAllowed");function a(e,r){return e.includes(r)?(_(r,r),!0):e.some(t=>_(t,r))}n(a,"coveredByAny");function b(){return process.env.CRTR_EXECUTION_ID}n(b,"envExecutionId");function k(){return process.env.CRTR_PROFILE_ID}n(k,"envProfileId");function y(){return process.env.CRTR_NODE_CWD}n(y,"envNodeCwd");function w(){return process.env.CRTR_MODEL_INTENT}n(w,"envModelIntent");function g(){return process.env.CRTR_MODEL_EXACT==="1"}n(g,"envModelExact");function V(){return process.env.CRTR_HOME}n(V,"envHomeOverride");function X(){return process.env.CRTR_RUNTIME_HOME}n(X,"envRuntimeHomeOverride");function s(e,r){if(e===void 0)return r;const t=Number(e);return Number.isFinite(t)&&t>0?t:r}n(s,"parsePositiveMs");const
|
|
2
|
+
`).map(o=>o.split(" ")[4])),t=T(e,{withFileTypes:!0}).filter(o=>o.isDirectory()&&/^[a-zA-Z0-9][a-zA-Z0-9-]{0,127}$/.test(o.name)&&r.has(i(e,o.name))&&d(i(e,o.name))!==void 0);return t.length===1?t[0].name:void 0}catch{return}}n(E,"sandboxCliNodeId");function F(){return E()!==void 0}n(F,"isSandboxedNode");function h(){return process.env.CRTR_GRANTEE}n(h,"envGrantee");const v="CRTR_INBOX_BETWEEN_TURNS";function H(){return process.env[v]==="1"}n(H,"envInboxBetweenTurns");const C="CRTR_SCOPES_FILE";function K(){const e=process.env[C];if(e!==void 0&&e!==""){const t=JSON.parse(c(e,"utf8"));if(!Array.isArray(t)||t.some(o=>typeof o!="string"))throw new Error(`invalid scopes file ${e}`);return t}const r=process.env.CRTR_SCOPES;return r===void 0?null:r===""?[]:r.split(",")}n(K,"envScopes");function G(e,r){return e==null?!0:a(e,r)}n(G,"scopeAllowed");function a(e,r){return e.includes(r)?(_(r,r),!0):e.some(t=>_(t,r))}n(a,"coveredByAny");function b(){return process.env.CRTR_EXECUTION_ID}n(b,"envExecutionId");function k(){return process.env.CRTR_PROFILE_ID}n(k,"envProfileId");function y(){return process.env.CRTR_NODE_CWD}n(y,"envNodeCwd");function w(){return process.env.CRTR_MODEL_INTENT}n(w,"envModelIntent");function g(){return process.env.CRTR_MODEL_EXACT==="1"}n(g,"envModelExact");function V(){return process.env.CRTR_HOME}n(V,"envHomeOverride");function X(){return process.env.CRTR_RUNTIME_HOME}n(X,"envRuntimeHomeOverride");function s(e,r){if(e===void 0)return r;const t=Number(e);return Number.isFinite(t)&&t>0?t:r}n(s,"parsePositiveMs");const S=5*6e4;function j(){return s(process.env.CRTR_STREAM_WATCHDOG_MS,S)}n(j,"envStreamWatchdogMs");const A=800;function Y(){return s(process.env.CRTR_WATCHER_TICK_MS,A)}n(Y,"envWatcherTickMs");const M=6e4;function $(){return s(process.env.CRTR_WATCHER_BACKSTOP_MS,M)}n($,"envWatcherBackstopMs");const N=1e3;function z(){return s(process.env.CRTR_WATCHER_DEBOUNCE_MS,N)}n(z,"envWatcherDebounceMs");const x=6e4;function Z(){return s(process.env.CRTR_RECAP_IDLE_MS,x)}n(Z,"envRecapIdleMs");function J(){const e=process.env.CRTR_RECAP_MODEL;return e!==void 0&&e.trim()!==""?e.trim():void 0}n(J,"envRecapModelOverride");const D=300*1e3;function q(){return s(process.env.CRTR_BASH_VALVE_MS,D)}n(q,"envBashValveMs");const O=60*1e3;function Q(){return s(process.env.CRTR_BASH_AUTO_BG_DEADLINE_MS,O)}n(Q,"envBashAutoBgDeadlineMs");function ee(){const e=process.env.CRTR_PIDFILE;return e!==void 0&&e!==""?e:void 0}n(ee,"envPidfileOverride");const I="@earendil-works/pi-coding-agent";function ne(){return process.env.CRTR_BROKER_ENGINE??I}n(ne,"envBrokerEngine");function re(){return process.env.CRTR_PI_BINARY}n(re,"envPiBinaryOverride");function te(){return process.env.CRTR_NO_DAEMON_AUTOSTART==="1"}n(te,"envNoDaemonAutostart");function oe(){const e=process.env.CRTR_DOCUMENT_PORT;if(e===void 0||!/^\d+$/.test(e))return;const r=Number(e);return r<=65535?r:void 0}n(oe,"envDocumentPort");function se(){return process.env.CRTRD_TCP}n(se,"envCrtrdTcp");function ue(){const e=process.env.CRTRD_TOKEN;return e!==void 0&&e!==""?e:void 0}n(ue,"envCrtrdToken");function ie(){return process.env.CRTR_NO_EXPORTS==="1"}n(ie,"envNoExports");function ce(){return process.env.CRTR_DEBUG==="1"}n(ce,"envDebug");function R(e){if(e===void 0||e==="")return;const r=Number(e);return Number.isSafeInteger(r)&&r>=1?r:void 0}n(R,"parsePositiveInteger");function _e(){return R(process.env.CRTR_MAX_LIVE_BROKERS)}n(_e,"envMaxLiveBrokers");function Re(){return R(process.env.CRTR_WARN_LIVE_BROKERS)}n(Re,"envWarnLiveBrokers");function u(e){if(e===void 0)return;const r=Number(e);return Number.isFinite(r)&&r>0?r:void 0}n(u,"parsePositiveMsNoDefault");function pe(){return u(process.env.CRTR_TEST_NODE_SOCKET_WINDOW_MS)}n(pe,"envTestNodeSocketWindowMs");function fe(){return u(process.env.CRTR_TEST_UNATTENDED_PARK_MS)}n(fe,"envTestUnattendedParkMs");function Te(){return u(process.env.CRTR_UNATTENDED_PARK_MS)}n(Te,"envUnattendedParkMs");function de(){return u(process.env.CRTR_BASH_SILENCE_MS)??15*6e4}n(de,"envBashSilenceMs");function l(){return u(process.env.CRTR_TEST_PARK_SUMMARY_GRACE_MS)}n(l,"envTestParkSummaryGraceMs");const m=5*6e4;function Ee(){return l()??m}n(Ee,"parkSummaryGraceMs");export{v as INBOX_BETWEEN_TURNS_ENV,C as SCOPES_FILE_ENV,a as coveredByAny,Q as envBashAutoBgDeadlineMs,de as envBashSilenceMs,q as envBashValveMs,ne as envBrokerEngine,se as envCrtrdTcp,ue as envCrtrdToken,ce as envDebug,oe as envDocumentPort,b as envExecutionId,h as envGrantee,V as envHomeOverride,H as envInboxBetweenTurns,_e as envMaxLiveBrokers,g as envModelExact,w as envModelIntent,te as envNoDaemonAutostart,ie as envNoExports,y as envNodeCwd,W as envNodeId,re as envPiBinaryOverride,ee as envPidfileOverride,k as envProfileId,Z as envRecapIdleMs,J as envRecapModelOverride,X as envRuntimeHomeOverride,K as envScopes,j as envStreamWatchdogMs,pe as envTestNodeSocketWindowMs,l as envTestParkSummaryGraceMs,fe as envTestUnattendedParkMs,Te as envUnattendedParkMs,Re as envWarnLiveBrokers,$ as envWatcherBackstopMs,z as envWatcherDebounceMs,Y as envWatcherTickMs,F as isSandboxedNode,d as launchSnapshotFile,Ee as parkSummaryGraceMs,E as sandboxCliNodeId,G as scopeAllowed};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
var w=Object.defineProperty;var i=(e,t)=>w(e,"name",{value:t,configurable:!0});import{formatInboxRefInstruction as
|
|
2
|
-
`);return o.length
|
|
3
|
-
`),r=!0),t.length>
|
|
1
|
+
var w=Object.defineProperty;var i=(e,t)=>w(e,"name",{value:t,configurable:!0});import{formatInboxRefInstruction as l}from"./generated-context.js";const $=12,u=1e3,y=1e3;function A(e){return(e===void 0?null:/^([a-z0-9-]+)\/reports\/[^/]+$/.exec(e))?.[1]}i(A,"reportRefNodeId");function j(e){let t=e,r=!1;const o=t.split(`
|
|
2
|
+
`);return o.length>$&&(t=o.slice(0,$).join(`
|
|
3
|
+
`),r=!0),t.length>u&&(t=t.slice(0,u),r=!0),{text:t.trimEnd(),clipped:r}}i(j,"clipBody");function x(e){const t=typeof e.data?.body=="string"?String(e.data.body).trim():"";return t===""||t===e.label?"":t}i(x,"inlineBody");function v(e){const t=e.data?.push;return typeof t=="object"&&t!==null&&typeof t.event=="string"?t:null}i(v,"pushOf");function f(e){const t=e.owner_handle??e.owner;return t==null?e.name:`${t}/${e.name}`}i(f,"objectRef");function _(e){return e.type==="document"?`[[${f(e)}]]`:e.type==="job"?`job ${e.id} (\`${e.name}\`)`:e.type==="node"?`node ${e.name} (${e.id})${e.machine?` on ${e.machine}`:""}`:`${e.type} ${e.name}`}i(_,"objectText");function I(e,t){return t.actor===null?"the runtime":e.from_name===void 0||e.from_name===t.actor?t.actor:`${e.from_name} (${t.actor})`}i(I,"actorText");function m(e){return Array.isArray(e)?e.filter(t=>typeof t=="string"):[]}i(m,"list");function R(e,t){const r=_(t.object),o=I(e,t),n=t.attrs,a=t.object.type==="document"?` Read it with \`crtr canvas read ${f(t.object)}\`.`:"";switch(t.event){case"created":return t.object.type==="node"?`${o} created ${r}${typeof n.kind=="string"?`, kind ${n.kind}`:""}.`:`${o} created ${r}.${a}`;case"read":return`${o} read ${r}.`;case"watched":return`${o} started watching ${r}.`;case"deleted":return`${o} deleted ${r}.`;case"edited":{const s=m(n.authors),c=m(n.rationales),d=typeof n.from_revision=="number"&&typeof n.revision=="number"&&n.revision-n.from_revision>1?`revisions ${n.from_revision+1}\u2013${n.revision}`:typeof n.revision=="number"?`revision ${n.revision}`:"a new revision",g=s.length===0?o:s.join(", "),h=c.length===0?"":` Rationale: ${c.join("; ")}.`,b=n.needs_merge===!0?" Two edits were made from the same revision; read it and edit it to merge them.":"",p=` You get this because you read or wrote it; if it no longer concerns your work, stop with \`crtr canvas unwatch ${f(t.object)}\`.`;return`${g} edited ${r} (${d}).${h}${b}${a}${p}`}case"messaged":return`${o} messaged ${r}.`;case"sent":return`${o} sent a message as ${r}.`;case"configured":return`${o} changed the settings of ${r}${typeof n.changed=="string"?`: ${n.changed}`:""}.`;case"exited":return`${r[0].toUpperCase()}${r.slice(1)} exited with code ${String(n["exit-code"])}. Log: ${String(n["log-path"])}`;case"silent":{const s=typeof n.files=="string"?n.files:"";return`${r[0].toUpperCase()}${r.slice(1)} has been silent for ${String(n["silent-for"])}: its processes used almost no CPU and none of the files they hold open for writing changed. It may be hung. Watched: ${s===""?"the job log only":s}. Inspect those files; stop it with \`crtr node bash kill ${t.object.id}\`; or, if it is meant to sit idle, stop these messages with \`crtr node bash quiet ${t.object.id}\`.`}case"killed":{const s=t.actor===null?"":` by ${o}`,c=typeof n.reason=="string"?` (${n.reason})`:"",d=n.reason==="deadline"?` It was canceled at its deadline; claim it with \`crtr node bash extend ${t.object.id} --for <duration>\` next time.`:"";return`${r[0].toUpperCase()}${r.slice(1)} was killed${s}${c}.${d} Log: ${String(n["log-path"])}`}default:return e.label}}i(R,"renderPush");function C(e,t){if(t!==null&&t.length<y&&e.ref!==void 0)return`${t.trimEnd()}
|
|
4
4
|
|
|
5
|
-
${
|
|
5
|
+
${l(e.ref)}`;const r=x(e);if(r===""){const a=v(e);return a!==null&&e.ref===void 0?R(e,a):e.ref===void 0?e.label:`${e.label}
|
|
6
6
|
|
|
7
|
-
${
|
|
7
|
+
${l(e.ref)}`}const{text:o,clipped:n}=j(r);return e.ref!==void 0?`${o}
|
|
8
8
|
|
|
9
|
-
${
|
|
10
|
-
\u2026 (body clipped)`:o}i(
|
|
9
|
+
${l(e.ref)}`:n?`${o}
|
|
10
|
+
\u2026 (body clipped)`:o}i(C,"renderEntryBody");export{y as INLINE_REPORT_MAX_CHARS,j as clipBody,x as inlineBody,C as renderEntryBody,A as reportRefNodeId};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.557",
|
|
4
4
|
"description": "crtr — agent runtime with memory, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
},
|
|
142
142
|
"license": "UNLICENSED",
|
|
143
143
|
"dependencies": {
|
|
144
|
-
"@north-light/identity": "0.3.
|
|
144
|
+
"@north-light/identity": "0.3.557",
|
|
145
145
|
"@earendil-works/pi-agent-core": "1.1.0",
|
|
146
146
|
"@earendil-works/pi-ai": "1.1.0",
|
|
147
147
|
"@earendil-works/pi-coding-agent": "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.557",
|
|
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.557",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "UNLICENSED",
|
|
12
12
|
"workspaces": [
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@milkdown/preset-gfm": "^7.22.3",
|
|
25
25
|
"@milkdown/prose": "^7.22.3",
|
|
26
26
|
"@milkdown/utils": "^7.22.3",
|
|
27
|
-
"@north-light/identity": "0.3.
|
|
27
|
+
"@north-light/identity": "0.3.557",
|
|
28
28
|
"cron-parser": "^5.6.0",
|
|
29
29
|
"esbuild": "^0.27.7",
|
|
30
30
|
"jose": "^6.2.12",
|
|
@@ -12455,17 +12455,17 @@
|
|
|
12455
12455
|
},
|
|
12456
12456
|
"packages/crouter-api": {
|
|
12457
12457
|
"name": "@north-light/api",
|
|
12458
|
-
"version": "0.3.
|
|
12458
|
+
"version": "0.3.557",
|
|
12459
12459
|
"license": "UNLICENSED"
|
|
12460
12460
|
},
|
|
12461
12461
|
"packages/crouter-env-docker": {
|
|
12462
12462
|
"name": "@north-light/env-docker",
|
|
12463
|
-
"version": "0.3.
|
|
12463
|
+
"version": "0.3.557",
|
|
12464
12464
|
"license": "UNLICENSED"
|
|
12465
12465
|
},
|
|
12466
12466
|
"packages/crouter-identity": {
|
|
12467
12467
|
"name": "@north-light/identity",
|
|
12468
|
-
"version": "0.3.
|
|
12468
|
+
"version": "0.3.557",
|
|
12469
12469
|
"license": "UNLICENSED",
|
|
12470
12470
|
"dependencies": {
|
|
12471
12471
|
"jose": "^6.2.1"
|
|
@@ -12473,21 +12473,21 @@
|
|
|
12473
12473
|
},
|
|
12474
12474
|
"packages/crouter-plugin": {
|
|
12475
12475
|
"name": "@north-light/plugin",
|
|
12476
|
-
"version": "0.3.
|
|
12476
|
+
"version": "0.3.557",
|
|
12477
12477
|
"license": "UNLICENSED",
|
|
12478
12478
|
"dependencies": {
|
|
12479
|
-
"@north-light/api": "0.3.
|
|
12480
|
-
"@north-light/identity": "0.3.
|
|
12479
|
+
"@north-light/api": "0.3.557",
|
|
12480
|
+
"@north-light/identity": "0.3.557"
|
|
12481
12481
|
}
|
|
12482
12482
|
},
|
|
12483
12483
|
"packages/crouter-sdk": {
|
|
12484
12484
|
"name": "@north-light/sdk",
|
|
12485
|
-
"version": "0.3.
|
|
12485
|
+
"version": "0.3.557",
|
|
12486
12486
|
"license": "UNLICENSED",
|
|
12487
12487
|
"dependencies": {
|
|
12488
|
-
"@north-light/api": "0.3.
|
|
12489
|
-
"@north-light/identity": "0.3.
|
|
12490
|
-
"@north-light/plugin": "0.3.
|
|
12488
|
+
"@north-light/api": "0.3.557",
|
|
12489
|
+
"@north-light/identity": "0.3.557",
|
|
12490
|
+
"@north-light/plugin": "0.3.557",
|
|
12491
12491
|
"jose": "^6.2.1"
|
|
12492
12492
|
},
|
|
12493
12493
|
"bin": {
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export declare const LEDGER_MIGRATED_KEY = "ledger_migrated";
|
|
2
|
-
/** The id of a migrated `usage` row `u`, as SQL: `legacy:<grantee>:<month>:<payer>:<source>:<served_model>`. */
|
|
3
|
-
export declare const LEGACY_ID_SQL = "'legacy:' || u.grantee || ':' || u.month || ':' || u.payer || ':' || u.source || ':' || u.served_model";
|
|
4
|
-
export interface BackfillResult {
|
|
5
|
-
migrated: boolean;
|
|
6
|
-
charges: number;
|
|
7
|
-
}
|
|
8
|
-
/** Copy `usage` into `charges` as legacy rows and rebuild `charge_totals` from them, then
|
|
9
|
-
* verify per `(grantee, month, payer, source, served_model)` that usd and token sums equal the `usage` row's, null kept. A
|
|
10
|
-
* second call sees the marker and does nothing. A failed verification rolls the whole thing back and throws. */
|
|
11
|
-
export declare function backfillLedger(person: string): Promise<BackfillResult>;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
var E=Object.defineProperty;var t=(u,e)=>E(u,"name",{value:e,configurable:!0});import{withCanvasWrite as i}from"../canvas/db.js";import{rebuildTotals as o}from"./totals.js";const r="ledger_migrated",a="'legacy:' || u.grantee || ':' || u.month || ':' || u.payer || ':' || u.source || ':' || u.served_model";async function p(u){return await i(async e=>{if(await e.prepare("SELECT value FROM canvas_meta WHERE key = ?").get(r)!==void 0)return{migrated:!1,charges:0};const n=await e.prepare(`INSERT INTO charges (id, request, at, month, kind, what, units, usd, payer, funding, sub, grantee, provider)
|
|
2
|
-
SELECT ${a},
|
|
3
|
-
NULL, u.month || '-01T00:00:00.000Z', u.month,
|
|
4
|
-
CASE WHEN u.source = 'model' THEN 'model' ELSE 'tool' END,
|
|
5
|
-
CASE WHEN u.source = 'model' THEN u.served_model ELSE substr(u.source, 10) END,
|
|
6
|
-
CASE WHEN u.source = 'model' THEN jsonb_build_object('input', u.input_tokens, 'output', u.output_tokens, 'cache_read', u.cached_tokens, 'cache_write', 0) END,
|
|
7
|
-
u.usd_equivalent,
|
|
8
|
-
CASE WHEN u.payer = 'app' THEN u.grantee ELSE ?::text END,
|
|
9
|
-
CASE WHEN u.payer = 'app' THEN 'endpoint' WHEN u.source = 'model' THEN 'legacy' ELSE 'bill' END,
|
|
10
|
-
?::text, u.grantee,
|
|
11
|
-
CASE WHEN u.source = 'model' THEN NULL ELSE 'app:' || substr(u.source, 10) END
|
|
12
|
-
FROM usage u ON CONFLICT (id) DO NOTHING`).run(u,u);return await o(e),await s(e),await e.prepare("INSERT INTO canvas_meta (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value = excluded.value").run(r,new Date().toISOString()),{migrated:!0,charges:n.changes}})}t(p,"backfillLedger");async function s(u){const e=await u.prepare(`SELECT u.grantee, u.month, u.payer, u.source, u.served_model FROM usage u
|
|
13
|
-
LEFT JOIN charges c ON c.id = ${a}
|
|
14
|
-
WHERE c.id IS NULL
|
|
15
|
-
OR c.usd IS DISTINCT FROM u.usd_equivalent::numeric(14,6)
|
|
16
|
-
OR (u.source = 'model' AND (COALESCE((c.units ->> 'input')::bigint, -1) <> u.input_tokens
|
|
17
|
-
OR COALESCE((c.units ->> 'output')::bigint, -1) <> u.output_tokens
|
|
18
|
-
OR COALESCE((c.units ->> 'cache_read')::bigint, -1) <> u.cached_tokens))
|
|
19
|
-
LIMIT 5`).all();if(e.length>0)throw new Error(`ledger backfill verification failed; first differing usage rows: ${JSON.stringify(e)}`)}t(s,"verify");export{r as LEDGER_MIGRATED_KEY,a as LEGACY_ID_SQL,p as backfillLedger};
|