@north-light/cli 0.3.531 → 0.3.533

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.
Files changed (198) hide show
  1. package/dist/api/client.d.ts +6 -3
  2. package/dist/api/client.js +1 -1
  3. package/dist/api/contract.d.ts +1 -1
  4. package/dist/api/contract.js +1 -1
  5. package/dist/api/dto/canvas.d.ts +2 -0
  6. package/dist/api/dto/crons.d.ts +2 -0
  7. package/dist/builtin-memory/crouter-sdk/release-notes.md +8 -0
  8. package/dist/builtin-memory/northlight-apps/providers/README.md +2 -0
  9. package/dist/builtin-memory/northlight-apps/providers/sms.md +2 -0
  10. package/dist/clients/attach/chrome/inbox-strip.d.ts +6 -1
  11. package/dist/clients/attach/chrome/inbox-strip.js +1 -1
  12. package/dist/clients/attach/chrome/roster.js +1 -1
  13. package/dist/clients/attach/session/chrome-refresh.d.ts +12 -3
  14. package/dist/clients/attach/session/chrome-refresh.js +1 -1
  15. package/dist/clients/attach/session/document-links.js +1 -1
  16. package/dist/clients/attach/viewer.js +586 -587
  17. package/dist/commands/api-client.js +3 -3
  18. package/dist/commands/node/inspect.js +6 -6
  19. package/dist/commands/sys/context/expose.js +2 -2
  20. package/dist/commands/sys/db.d.ts +1 -0
  21. package/dist/commands/sys/db.js +1 -0
  22. package/dist/commands/sys.js +1 -1
  23. package/dist/core/bash-guard.js +9 -3
  24. package/dist/core/canvas/canvas.d.ts +12 -2
  25. package/dist/core/canvas/canvas.js +23 -20
  26. package/dist/core/canvas/daemon-actions.d.ts +0 -3
  27. package/dist/core/canvas/daemon-actions.js +7 -24
  28. package/dist/core/canvas/daemon-owner.d.ts +3 -0
  29. package/dist/core/canvas/daemon-owner.js +1 -6
  30. package/dist/core/canvas/db.d.ts +28 -20
  31. package/dist/core/canvas/db.js +4 -3
  32. package/dist/core/canvas/deliveries.d.ts +5 -0
  33. package/dist/core/canvas/deliveries.js +25 -30
  34. package/dist/core/canvas/export-sqlite.d.ts +14 -0
  35. package/dist/core/canvas/export-sqlite.js +3 -0
  36. package/dist/core/canvas/import-sqlite.d.ts +44 -0
  37. package/dist/core/canvas/import-sqlite.js +7 -0
  38. package/dist/core/canvas/node-tokens.js +3 -3
  39. package/dist/core/canvas/outcome-actions.js +4 -4
  40. package/dist/core/canvas/recovery.js +1 -1
  41. package/dist/core/canvas/schema-pg.sql +1022 -0
  42. package/dist/core/canvas/schema-version.d.ts +4 -0
  43. package/dist/core/canvas/schema-version.js +1 -0
  44. package/dist/core/canvas/sessions.js +3 -3
  45. package/dist/core/canvas/structured-requests.js +2 -2
  46. package/dist/core/canvas/tree.js +6 -6
  47. package/dist/core/canvas/waits.js +1 -1
  48. package/dist/core/feed/inbox.js +1 -1
  49. package/dist/core/feed/messages.js +5 -5
  50. package/dist/core/feed/reports.js +2 -2
  51. package/dist/core/grants/mirror.js +2 -2
  52. package/dist/core/graph/access.js +7 -7
  53. package/dist/core/graph/custom.js +10 -10
  54. package/dist/core/graph/deletion.js +6 -6
  55. package/dist/core/graph/documents.d.ts +1 -1
  56. package/dist/core/graph/documents.js +9 -9
  57. package/dist/core/graph/edges.d.ts +3 -3
  58. package/dist/core/graph/edges.js +3 -1
  59. package/dist/core/graph/events.js +7 -7
  60. package/dist/core/graph/exposures.d.ts +8 -8
  61. package/dist/core/graph/jobs.d.ts +1 -1
  62. package/dist/core/graph/jobs.js +2 -2
  63. package/dist/core/graph/kinds/custom.js +1 -1
  64. package/dist/core/graph/kinds/document.js +4 -4
  65. package/dist/core/graph/kinds/dto.js +1 -1
  66. package/dist/core/graph/kinds/job.js +2 -2
  67. package/dist/core/graph/kinds/kind.d.ts +2 -2
  68. package/dist/core/graph/kinds/kind.js +1 -1
  69. package/dist/core/graph/kinds/live-read.d.ts +5 -0
  70. package/dist/core/graph/kinds/live-read.js +1 -0
  71. package/dist/core/graph/kinds/node.js +5 -5
  72. package/dist/core/graph/kinds/owner.js +2 -2
  73. package/dist/core/graph/names.js +3 -3
  74. package/dist/core/graph/objects.d.ts +5 -5
  75. package/dist/core/graph/objects.js +10 -10
  76. package/dist/core/graph/package-docs.js +6 -5
  77. package/dist/core/graph/reader.d.ts +1 -1
  78. package/dist/core/graph/reader.js +2 -2
  79. package/dist/core/graph/receivers.js +4 -5
  80. package/dist/core/graph/repo-sync/exchange.js +2 -2
  81. package/dist/core/graph/repo-sync/repos.js +3 -3
  82. package/dist/core/graph/repo-sync/sync.js +1 -1
  83. package/dist/core/graph/rules.d.ts +3 -4
  84. package/dist/core/graph/rules.js +3 -3
  85. package/dist/core/graph/spaces/app.js +1 -1
  86. package/dist/core/graph/spaces/index.d.ts +1 -1
  87. package/dist/core/graph/spaces/index.js +1 -1
  88. package/dist/core/graph/spaces/own-memory.js +1 -1
  89. package/dist/core/graph/spaces/person.js +3 -3
  90. package/dist/core/graph/spaces/repo.d.ts +1 -1
  91. package/dist/core/graph/spaces/repo.js +1 -1
  92. package/dist/core/graph/types.d.ts +4 -3
  93. package/dist/core/graph/watches.d.ts +2 -2
  94. package/dist/core/graph/watches.js +17 -17
  95. package/dist/core/human/page-schema.js +5 -4
  96. package/dist/core/human/requests.d.ts +1 -1
  97. package/dist/core/human/requests.js +20 -23
  98. package/dist/core/llm/run.js +2 -2
  99. package/dist/core/peer/home.d.ts +6 -0
  100. package/dist/core/peer/home.js +2 -2
  101. package/dist/core/peer/ingest.js +1 -1
  102. package/dist/core/peer/receiver.js +1 -1
  103. package/dist/core/peer/stubs.js +1 -1
  104. package/dist/core/providers/receipts.js +3 -2
  105. package/dist/core/review/comments.js +5 -4
  106. package/dist/core/runs/activity.js +4 -5
  107. package/dist/core/runs/confinement.d.ts +1 -1
  108. package/dist/core/runs/confinement.js +2 -2
  109. package/dist/core/runs/events.d.ts +1 -1
  110. package/dist/core/runs/events.js +5 -5
  111. package/dist/core/runs/operations.js +6 -4
  112. package/dist/core/runtime/close.d.ts +3 -0
  113. package/dist/core/runtime/close.js +1 -1
  114. package/dist/core/runtime/host.d.ts +5 -1
  115. package/dist/core/runtime/host.js +3 -3
  116. package/dist/core/runtime/launch.js +2 -2
  117. package/dist/core/runtime/ledger.js +3 -3
  118. package/dist/core/runtime/lifecycle.d.ts +9 -0
  119. package/dist/core/runtime/lifecycle.js +2 -2
  120. package/dist/core/runtime/nodes.d.ts +1 -1
  121. package/dist/core/runtime/nodes.js +1 -1
  122. package/dist/core/runtime/roadmap.js +1 -1
  123. package/dist/core/spaces/stop.d.ts +1 -0
  124. package/dist/core/spaces/stop.js +2 -2
  125. package/dist/core/statements.d.ts +17 -0
  126. package/dist/core/statements.js +2 -0
  127. package/dist/core/substrate/delivery/corpus.js +3 -3
  128. package/dist/core/substrate/delivery/deliver.js +5 -5
  129. package/dist/core/substrate/delivery/listings.js +1 -1
  130. package/dist/core/substrate/delivery/match.d.ts +2 -2
  131. package/dist/core/substrate/delivery/match.js +1 -1
  132. package/dist/core/substrate/delivery/plan.js +3 -3
  133. package/dist/core/substrate/delivery/render-boot.js +5 -5
  134. package/dist/core/substrate/delivery/render-event.js +6 -6
  135. package/dist/core/usage/ledger.js +8 -8
  136. package/dist/daemon/api/bridge.js +4 -4
  137. package/dist/daemon/api/document-listener.js +1 -1
  138. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  139. package/dist/daemon/api/handlers/canvas.js +1 -1
  140. package/dist/daemon/api/handlers/crons.js +1 -1
  141. package/dist/daemon/api/handlers/custom-objects.js +3 -3
  142. package/dist/daemon/api/handlers/docs.js +1 -1
  143. package/dist/daemon/api/handlers/inbox.js +1 -1
  144. package/dist/daemon/api/handlers/memory.js +7 -7
  145. package/dist/daemon/api/handlers/messages.js +1 -1
  146. package/dist/daemon/api/handlers/nodes.js +1 -1
  147. package/dist/daemon/api/handlers/objects.js +3 -3
  148. package/dist/daemon/api/handlers/reviews.js +1 -1
  149. package/dist/daemon/api/handlers/structured-requests.js +1 -1
  150. package/dist/daemon/api/reader.js +1 -1
  151. package/dist/daemon/broker-signals/bus.d.ts +14 -6
  152. package/dist/daemon/broker-signals/bus.js +1 -10
  153. package/dist/daemon/crtrd-cli.js +1 -1
  154. package/dist/daemon/crtrd.js +12 -11
  155. package/dist/daemon/due.js +1 -1
  156. package/dist/daemon/export-sqlite-run.d.ts +1 -0
  157. package/dist/daemon/export-sqlite-run.js +4 -0
  158. package/dist/daemon/fleet.d.ts +1 -0
  159. package/dist/daemon/fleet.js +3 -3
  160. package/dist/daemon/human/settle.js +1 -1
  161. package/dist/daemon/manage.d.ts +10 -0
  162. package/dist/daemon/manage.js +3 -2
  163. package/dist/daemon/peer/forward.d.ts +7 -0
  164. package/dist/daemon/peer/forward.js +1 -1
  165. package/dist/daemon/postgres/binary.d.ts +17 -0
  166. package/dist/daemon/postgres/binary.js +2 -0
  167. package/dist/daemon/postgres/cluster.d.ts +37 -0
  168. package/dist/daemon/postgres/cluster.js +6 -0
  169. package/dist/daemon/postgres/index.d.ts +2 -0
  170. package/dist/daemon/postgres/index.js +1 -0
  171. package/dist/daemon/postgres/pins.d.ts +7 -0
  172. package/dist/daemon/postgres/pins.js +1 -0
  173. package/dist/daemon/postgres/supervisor.d.ts +95 -0
  174. package/dist/daemon/postgres/supervisor.js +2 -0
  175. package/dist/daemon/reconcilers/bash-deadline.js +2 -2
  176. package/dist/daemon/reconcilers/delivery-lane.js +2 -2
  177. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +7 -3
  178. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
  179. package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
  180. package/dist/daemon/reconcilers/storage-maintenance.d.ts +0 -6
  181. package/dist/daemon/reconcilers/storage-maintenance.js +1 -8
  182. package/dist/daemon/runs/observer.js +5 -4
  183. package/dist/migrations/004-canvas-documents/index.js +1 -1
  184. package/dist/pi-extensions/canvas-doc-substrate.d.ts +2 -0
  185. package/dist/pi-extensions/canvas-doc-substrate.js +6 -6
  186. package/dist/pi-extensions/envelope-merge.d.ts +3 -2
  187. package/dist/pi-extensions/envelope-merge.js +9 -8
  188. package/dist/shared/env.d.ts +3 -0
  189. package/dist/shared/env.js +2 -2
  190. package/docs/apps/providers/README.md +2 -0
  191. package/docs/apps/providers/sms.md +2 -0
  192. package/docs/public-api.md +11 -2
  193. package/docs/sdk/release-notes.md +8 -0
  194. package/package.json +6 -6
  195. package/packages/crouter-identity/package.json +1 -1
  196. package/runtime.lock.json +13 -26
  197. package/scripts/install-runtime.mjs +53 -3
  198. package/scripts/lib/isolation-env.mjs +3 -1
@@ -1,16 +1,17 @@
1
- var Pe=Object.defineProperty;var s=(t,n)=>Pe(t,"name",{value:n,configurable:!0});import{startPeerOutbox as xe}from"./peer/outbox.js";import{reportMachineStatus as Ie}from"../core/peer/directory.js";import{createPeerRelay as De}from"./peer/relay-service.js";import{writeFileSync as Te,readFileSync as Ce,rmSync as $e,mkdirSync as ee,existsSync as E,chmodSync as Le}from"node:fs";import{createServer as re}from"node:http";import{dirname as Ae}from"node:path";import{envCrtrdTcp as Ne,envDocumentPort as Me}from"../shared/env.js";import{serveCanvasConversationStore as Oe}from"../core/canvas/sessions.js";import{pollRunObservers as Fe}from"./runs/observer.js";import{randomBytes as Ge}from"node:crypto";import{apiSocketPath as He,crtrHome as je}from"../core/canvas/paths.js";import{activeAppUsers as Be}from"../core/spaces/users.js";import{ensureAppSpace as qe,ensureUserFiles as ze}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as x}from"../launcher/spec.js";import{launcherRequest as D}from"../launcher/client.js";import{installLauncherEventSink as We}from"./launcher-events.js";import{initializeEmptyLayout as Ue,layoutNeedsMigration as Ke,socketDir as te}from"../core/layout.js";import{migrateLooseKeys as Je}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as Ve}from"../core/profiles/relocate.js";import{getNode as Qe,listNodesForSupervision as ne,setFrozen as oe}from"../core/canvas/index.js";import{createApiServer as Ye}from"./api/server.js";import{createDocumentListener as Xe}from"./api/document-listener.js";import{documentSecret as Ze}from"./api/document-secret.js";import{createAppListener as er}from"./api/app-listener.js";import{installLocalTerminalGrant as rr}from"../core/grants/local.js";import{onRunEventAppended as tr}from"../core/runs/events.js";import{runNotices as nr}from"./run-notices.js";import{createGrantSync as or,GRANT_SYNC_INTERVAL_S as ir}from"../core/grants/sync.js";import{runRemovalCascades as ar}from"../core/grants/remove.js";import{sweepSandboxes as sr}from"../core/grants/on-sync.js";import{rebuildCronExecutions as cr}from"./cron/run-socket.js";import{hasSyncedGrants as dr}from"../core/grants/mirror.js";import{readConfig as ie}from"../core/config.js";import{reconcileUndeliveredTickets as lr}from"./human/sweep.js";import{resetHandedOffDeliveries as pr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as mr}from"../core/canvas/daemon-actions.js";import{sweepReviews as fr}from"./review/sweep.js";import{pidfilePath as B}from"./pidfile.js";import{rendererWarning as ur}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as hr}from"./supervise-cadence.js";import{createResumeDetector as wr}from"./resume-detector.js";import{setFatalFaultAnnouncer as vr}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as gr}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as yr}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as Sr}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as _r}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as br}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as kr}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as Er}from"./reconcilers/action-lane.js";import{endLiveExecution as Rr,migrateLegacyExecutionIdentities as Pr}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as xr}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as Ir}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as Dr}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as Tr}from"./reconcilers/storage-due-work.js";import{BashDeadlineReconciler as Cr}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as $r}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as Lr}from"./reconcilers/structured-request-lane.js";import{reconcileStructuredRequestSidecars as Ar}from"./structured-requests/boot.js";import{captureLivenessSnapshot as ae,capturePidCommand as Nr,captureTeardownSnapshot as se,isPidAlive as Mr,isAnyPidAlive as ce,killProcessTreePids as de}from"../core/canvas/pid.js";import{bindFleet as Or,boundFleet as Fr}from"../core/runtime/fleet.js";import{bindDaemonControl as Gr,clearDaemonControl as Hr}from"./control.js";import{execDaemon as jr,spawnDaemon as Br}from"./manage.js";import{reconcileAndMigrateBoot as qr}from"../core/canvas/boot.js";import{resolveInstallId as zr}from"../core/canvas/install-id.js";import{setInstallId as Wr}from"../core/runtime/nodes.js";import{claimDaemonOwnership as Ur,releaseDaemonOwnership as le}from"../core/canvas/daemon-owner.js";import{migrate as Kr,bindDaemonCanvasOpenHook as Jr,clearDaemonCanvasOpenHook as pe,openDb as me,withCanvasWrite as Vr}from"../core/canvas/db.js";import{brokerSignalBus as q}from"./broker-signals/bus.js";import{brokerSignalStreams as Qr}from"./api/handlers/broker-signals.js";import{reconcilePackages as Yr}from"../core/graph/package-reconcile.js";import{startRepoSync as Xr}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as Zr,reapStaleSpares as et}from"../core/runtime/warm-pool.js";import{DaemonFleet as rt}from"./fleet.js";import{BOOT_MIGRATIONS as tt}from"./boot-migrations.js";import{applyJoinedOwner as nt}from"./joined-owner.js";import{emitEvent as f}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as ot}from"../core/events/source.js";import{activateCanvasImport as it,MigrationBlockedError as at,runForcedCanvasImport as st}from"../migrations/activation.js";import{convertLegacyProviderStore as ct,convertLegacyScopedStore as dt}from"../core/scoped-state/migrate.js";import{userScopeRoot as lt}from"../core/scope.js";import{secretsDir as pt}from"../core/layout.js";function z(t,n){return t.includes(` --canvas-home ${n} `)}s(z,"brokerCommandDeclaresCanvasHome");function W(t){return/ --epoch (\S+) /.exec(t)?.[1]??null}s(W,"brokerCommandEpoch");const mt=5e3,fe=150;async function ft(t){const n=ae();if(n===null)throw f({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 c=je(),u=[];for(const[a,m]of n.processes){const y=m.command;if(m.zombie||!y.includes("broker-cli.js")||!z(y,c)||W(y)===t)continue;const b=y.trim().split(/\s+/).at(-1)??"";b===""||b.endsWith("broker-cli.js")||u.push({pid:a,nodeId:b})}let h=0;const o=[];for(const a of u){const m=Nr(a.pid);if(m===null||!m.includes("broker-cli.js")||!z(m,c)||W(m)===t||m.trim().split(/\s+/).at(-1)!==a.nodeId)continue;const y=se(a.pid,null);if(!y.reused){try{process.platform==="linux"&&E(x)?await D({operation:"stop",pid:a.pid}):process.kill(a.pid,"SIGTERM")}catch{continue}h++,o.push({pid:a.pid,snap:y})}}const g=Date.now()+mt;for(;Date.now()<g&&o.some(a=>ce(a.snap.tree.length>0?a.snap.tree:[a.pid]));)await new Promise(a=>setTimeout(a,fe));let v=0;for(const a of o){const m=a.snap.tree.length>0?a.snap.tree:[a.pid];ce(m)&&(process.platform==="linux"&&E(x)?await D({operation:"stop",pid:a.pid}):de(m,"SIGKILL",a.snap.identities),v++)}v>0&&await new Promise(a=>setTimeout(a,fe));const d=ae();if(d===null)throw f({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 p=[];for(const[a,m]of d.processes){const y=m.command;m.zombie||!y.includes("broker-cli.js")||z(y,c)&&W(y)!==t&&p.push(a)}if(p.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${p.join(", ")}`);f({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:u.length,sigtermed:h,sigkilled:v}})}s(ft,"reapPriorEpochBrokers");const ut=5e3;async function ht(t){const n=Array.from(t.entries());if(n.length===0)return;for(const[o]of n){const g=(await Qe(o))?.status;(g==="active"||g==="idle")&&await oe(o,new Date().toISOString())}const c=n.map(([,o])=>({pid:o.pid,snap:se(o.pid,null),exited:o.handle.exited}));for(const o of c)if(!o.snap.reused)try{process.platform==="linux"&&E(x)?await D({operation:"stop",pid:o.pid}):process.kill(o.pid,"SIGTERM")}catch{}const u=new Promise(o=>{setTimeout(o,ut).unref()});await Promise.allSettled(c.map(o=>Promise.race([o.exited.then(()=>{}),u])));const h=[];for(const o of c)Mr(o.pid)&&(process.platform==="linux"&&E(x)?await D({operation:"stop",pid:o.pid}):de(o.snap.tree.length>0?o.snap.tree:[o.pid],"SIGKILL",o.snap.identities),h.push(o));await Promise.all(h.map(o=>o.exited));for(const[o]of n)await Rr(o,{reason:"daemon-teardown"});f({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:c.length,sigkilled:h.length}})}s(ht,"teardownFleet");function ue(){ee(Ae(B()),{recursive:!0}),Te(B(),String(process.pid),"utf8")}s(ue,"writePidfile");function he(t){return typeof t=="object"&&t!==null&&t.code==="ENOENT"}s(he,"isMissingPathError");function we(t){const n=B();let c;try{c=Ce(n,"utf8")}catch(u){if(he(u))return;throw u}if(c.trim()!==String(t))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${t})`);try{$e(n)}catch(u){if(he(u))return;throw u}}s(we,"removeOwnedPidfileMirror");const wt={acceptsDetachedWork:s(()=>!0,"acceptsDetachedWork"),registerDetached:s(t=>{t.catch(()=>{})},"registerDetached")},O=new gr;vr(t=>O.handleFatalFault(t,!1));const vt=new yr,gt=new Sr,yt=new _r,St=new br,_t=new kr,bt=new Er,kt=new Ir,Et=new Dr,Rt=new Tr,Pt=new Cr,xt=new $r,It=new Lr;async function Dt(t=Date.now(),n=wt){const c=Fr(),u=await O.runFleetPreamble(c);let h,o;try{const v=await ne();h=v.filter(d=>d.status==="active"||d.status==="idle"),o=v.filter(d=>d.status==="done"||d.status==="canceled"||d.status==="dead")}catch(v){f({level:"error",event:"canvas.node_list.failed",error:v});return}await O.run(t,{rows:h,fleet:c,lifecycle:n}),await vt.run(t,{rows:h});const g=await gt.run(t,{rows:[...h,...o],fleet:c});await yt.run(t),await It.run(t),await St.run(t,{lifecycle:n}),await _t.run(t,{lifecycle:n}),await bt.run(t,{lifecycle:n}),await kt.run(t,{lifecycle:n}),await Et.run(t,{lifecycle:n,fleet:c}),Rt.run(t,{lifecycle:n}),await Fe(),xt.run(t,{fleet:c,lifecycle:n}),await Pt.run(t,{rows:h}),O.warnIfSaturated({fleet:c,lifecycle:n,thresholds:u,frozen:g})}s(Dt,"superviseTick");async function Tt(t){ee(te(),{recursive:!0,mode:493}),process.platform==="linux"&&Le(te(),493);const n=He();if(E(n))throw new Error(`layout refusal cannot bind occupied socket: ${n}`);const c=re((d,p)=>{if(d.method==="GET"&&d.url==="/healthz"){p.writeHead(200,{"content-type":"application/json; charset=utf-8"}),p.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}p.writeHead(503,{"content-type":"application/json; charset=utf-8"}),p.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((d,p)=>{c.once("error",p),c.listen(n,()=>{c.off("error",p),d()})});let u;const h=t.tcp??Ne();if(h){const d=/^(.*):(\d+)$/.exec(h);if(!d||Number(d[2])>65535)throw new Error(`invalid refusal TCP address: ${h}`);const p=re(c.listeners("request")[0]);await new Promise((a,m)=>{p.once("error",m),p.listen(Number(d[2]),d[1],()=>{p.off("error",m),a()})}),u=p}ue();let o;const g=s(()=>(o??=(async()=>{c.closeAllConnections(),u?.closeAllConnections(),await Promise.all([c,u].filter(d=>d!==void 0).map(d=>new Promise((p,a)=>d.close(m=>m?a(m):p())))),we(process.pid),process.off("SIGINT",v),process.off("SIGTERM",v)})(),o),"cleanup"),v=s(()=>{g().then(()=>process.exit(0),d=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(d)}
2
- `),process.exit(1)})},"onSignal");return process.on("SIGINT",v),process.on("SIGTERM",v),g}s(Tt,"serveLayoutRefusal");async function Ct(t={}){if(Ke())return Tt(t);Ue();const n=await Ur(Ge(16).toString("hex"));if(!n.claimed){if(n.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${n.incumbentPid??"?"})
3
- `);else{const r=[n.incumbentPid!==void 0?`pid ${n.incumbentPid}`:void 0,n.verdict!==void 0?`verdict ${n.verdict}`:void 0].filter(i=>i!==void 0),e=r.length>0?` (${r.join(", ")})`:"";process.stderr.write(`[crtrd] claim lost: ${n.reason}${e}
4
- `),n.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"),(n.reason==="own_identity_indeterminate"||n.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const c=n.claim;let u=null;try{await ft(c.token),dt({kind:"user",root:lt()});for(const e of Ve())process.stderr.write(`[crtrd] ${e}
5
- `);if(process.platform==="darwin"){const{readConfig:e,updateConfig:i}=await import("../core/config.js");if(e("user").paths?.user_files===void 0){const{homedir:l}=await import("node:os");i("user",S=>{S.paths??={},S.paths.user_files??=l()})}}await ct(pt());for(const e of Je())process.stderr.write(`[crtrd] ${e}
6
- `);Kr((await me()).raw),Jr(e=>q.install(e));const r=await Vr(async e=>await Yr(e));for(const e of r.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${e.message}
1
+ var Ce=Object.defineProperty;var i=(n,s)=>Ce(n,"name",{value:s,configurable:!0});import{startPeerOutbox as Le}from"./peer/outbox.js";import{reportMachineStatus as $e}from"../core/peer/directory.js";import{createPeerRelay as Ae}from"./peer/relay-service.js";import{writeFileSync as Me,readFileSync as Ne,rmSync as Ge,mkdirSync as oe,existsSync as E,chmodSync as Oe}from"node:fs";import{createServer as ie}from"node:http";import{dirname as Fe}from"node:path";import{envCrtrdTcp as Be,envDocumentPort as He}from"../shared/env.js";import{serveCanvasConversationStore as je}from"../core/canvas/sessions.js";import{pollRunObservers as qe}from"./runs/observer.js";import{randomBytes as ae}from"node:crypto";import{apiSocketPath as ze,crtrHome as We}from"../core/canvas/paths.js";import{activeAppUsers as Ue}from"../core/spaces/users.js";import{ensureAppSpace as Ke,ensureUserFiles as Je}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as I}from"../launcher/spec.js";import{launcherRequest as D}from"../launcher/client.js";import{installLauncherEventSink as Ve}from"./launcher-events.js";import{initializeEmptyLayout as Qe,layoutNeedsMigration as Ye,socketDir as se}from"../core/layout.js";import{migrateLooseKeys as Xe}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as Ze}from"../core/profiles/relocate.js";import{getNode as er,listNodesForSupervision as ce,listParkedResidentsWithWakeMail as rr,setFrozen as de}from"../core/canvas/index.js";import{createApiServer as tr}from"./api/server.js";import{createDocumentListener as nr}from"./api/document-listener.js";import{documentSecret as or}from"./api/document-secret.js";import{createAppListener as ir}from"./api/app-listener.js";import{installLocalTerminalGrant as ar}from"../core/grants/local.js";import{onRunEventAppended as sr}from"../core/runs/events.js";import{runNotices as cr}from"./run-notices.js";import{createGrantSync as dr,GRANT_SYNC_INTERVAL_S as lr}from"../core/grants/sync.js";import{runRemovalCascades as pr}from"../core/grants/remove.js";import{sweepSandboxes as mr}from"../core/grants/on-sync.js";import{rebuildCronExecutions as fr}from"./cron/run-socket.js";import{hasSyncedGrants as ur}from"../core/grants/mirror.js";import{readConfig as le}from"../core/config.js";import{reconcileUndeliveredTickets as wr}from"./human/sweep.js";import{resetHandedOffDeliveries as hr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as vr}from"../core/canvas/daemon-actions.js";import{sweepReviews as gr}from"./review/sweep.js";import{pidfilePath as z}from"./pidfile.js";import{rendererWarning as yr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as Sr}from"./supervise-cadence.js";import{createResumeDetector as _r}from"./resume-detector.js";import{setFatalFaultAnnouncer as br}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as kr}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as Er}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as Pr}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as Rr}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as xr}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as Ir}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as Tr}from"./reconcilers/action-lane.js";import{endLiveExecution as Dr,migrateLegacyExecutionIdentities as Cr}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as Lr}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as $r}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as Ar}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as Mr}from"./reconcilers/storage-due-work.js";import{BashDeadlineReconciler as Nr}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as Gr}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as Or}from"./reconcilers/structured-request-lane.js";import{reconcileStructuredRequestSidecars as Fr}from"./structured-requests/boot.js";import{captureLivenessSnapshot as pe,capturePidCommand as Br,captureTeardownSnapshot as me,isPidAlive as Hr,isAnyPidAlive as fe,killProcessTreePids as ue}from"../core/canvas/pid.js";import{bindFleet as jr,boundFleet as qr}from"../core/runtime/fleet.js";import{bindDaemonControl as zr,clearDaemonControl as Wr}from"./control.js";import{execDaemon as Ur,spawnDaemon as Kr}from"./manage.js";import{reconcileAndMigrateBoot as Jr}from"../core/canvas/boot.js";import{resolveInstallId as Vr}from"../core/canvas/install-id.js";import{setInstallId as Qr}from"../core/runtime/nodes.js";import{claimDaemonOwnership as we,daemonOwnershipClaimed as Yr,releaseDaemonOwnership as he}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as Xr,closeAllDbs as W,openDb as ve,withCanvasWrite as Zr}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as et}from"../core/canvas/import-sqlite.js";import{ensurePostgres as rt,ensurePostgresHealthy as tt,stopPostgres as nt,withPostgresStartLock as ot}from"./postgres/index.js";import{brokerSignalBus as U}from"./broker-signals/bus.js";import{brokerSignalStreams as it}from"./api/handlers/broker-signals.js";import{reconcilePackages as at}from"../core/graph/package-reconcile.js";import{startRepoSync as st}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as ct,reapStaleSpares as dt}from"../core/runtime/warm-pool.js";import{DaemonFleet as lt}from"./fleet.js";import{BOOT_MIGRATIONS as pt}from"./boot-migrations.js";import{applyJoinedOwner as mt}from"./joined-owner.js";import{emitEvent as u}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as ft}from"../core/events/source.js";import{activateCanvasImport as ut,MigrationBlockedError as wt,runForcedCanvasImport as ht}from"../migrations/activation.js";import{convertLegacyProviderStore as vt,convertLegacyScopedStore as gt}from"../core/scoped-state/migrate.js";import{userScopeRoot as yt}from"../core/scope.js";import{secretsDir as St}from"../core/layout.js";function K(n,s){return n.includes(` --canvas-home ${s} `)}i(K,"brokerCommandDeclaresCanvasHome");function J(n){return/ --epoch (\S+) /.exec(n)?.[1]??null}i(J,"brokerCommandEpoch");const _t=5e3,ge=150;async function bt(n){const s=pe();if(s===null)throw u({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const l=We(),w=[];for(const[c,m]of s.processes){const v=m.command;if(m.zombie||!v.includes("broker-cli.js")||!K(v,l)||J(v)===n)continue;const k=v.trim().split(/\s+/).at(-1)??"";k===""||k.endsWith("broker-cli.js")||w.push({pid:c,nodeId:k})}let d=0;const t=[];for(const c of w){const m=Br(c.pid);if(m===null||!m.includes("broker-cli.js")||!K(m,l)||J(m)===n||m.trim().split(/\s+/).at(-1)!==c.nodeId)continue;const v=me(c.pid,null);if(!v.reused){try{process.platform==="linux"&&E(I)?await D({operation:"stop",pid:c.pid}):process.kill(c.pid,"SIGTERM")}catch{continue}d++,t.push({pid:c.pid,snap:v})}}const y=Date.now()+_t;for(;Date.now()<y&&t.some(c=>fe(c.snap.tree.length>0?c.snap.tree:[c.pid]));)await new Promise(c=>setTimeout(c,ge));let h=0;for(const c of t){const m=c.snap.tree.length>0?c.snap.tree:[c.pid];fe(m)&&(process.platform==="linux"&&E(I)?await D({operation:"stop",pid:c.pid}):ue(m,"SIGKILL",c.snap.identities),h++)}h>0&&await new Promise(c=>setTimeout(c,ge));const f=pe();if(f===null)throw u({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const a=[];for(const[c,m]of f.processes){const v=m.command;m.zombie||!v.includes("broker-cli.js")||K(v,l)&&J(v)!==n&&a.push(c)}if(a.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${a.join(", ")}`);u({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:w.length,sigtermed:d,sigkilled:h}})}i(bt,"reapPriorEpochBrokers");const kt=5e3;async function Et(n){const s=Array.from(n.entries());if(s.length===0)return;for(const[t]of s){const y=(await er(t))?.status;(y==="active"||y==="idle")&&await de(t,new Date().toISOString())}const l=s.map(([,t])=>({pid:t.pid,snap:me(t.pid,null),exited:t.handle.exited}));for(const t of l)if(!t.snap.reused)try{process.platform==="linux"&&E(I)?await D({operation:"stop",pid:t.pid}):process.kill(t.pid,"SIGTERM")}catch{}const w=new Promise(t=>{setTimeout(t,kt).unref()});await Promise.allSettled(l.map(t=>Promise.race([t.exited.then(()=>{}),w])));const d=[];for(const t of l)Hr(t.pid)&&(process.platform==="linux"&&E(I)?await D({operation:"stop",pid:t.pid}):ue(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 Dr(t,{reason:"daemon-teardown"});u({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:l.length,sigkilled:d.length}})}i(Et,"teardownFleet");function ye(){oe(Fe(z()),{recursive:!0}),Me(z(),String(process.pid),"utf8")}i(ye,"writePidfile");function Se(n){return typeof n=="object"&&n!==null&&n.code==="ENOENT"}i(Se,"isMissingPathError");function _e(n){const s=z();let l;try{l=Ne(s,"utf8")}catch(w){if(Se(w))return;throw w}if(l.trim()!==String(n))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${n})`);try{Ge(s)}catch(w){if(Se(w))return;throw w}}i(_e,"removeOwnedPidfileMirror");const Pt={acceptsDetachedWork:i(()=>!0,"acceptsDetachedWork"),registerDetached:i(n=>{n.catch(()=>{})},"registerDetached")},O=new kr;br(n=>O.handleFatalFault(n,!1));const Rt=new Er,xt=new Pr,It=new Rr,Tt=new xr,Dt=new Ir,Ct=new Tr,Lt=new $r,$t=new Ar,At=new Mr,Mt=new Nr,Nt=new Gr,Gt=new Or;async function Ot(n=Date.now(),s=Pt){const l=qr(),w=await O.runFleetPreamble(l);let d,t;try{const h=await ce();d=h.filter(a=>a.status==="active"||a.status==="idle"),t=h.filter(a=>a.status==="done"||a.status==="canceled"||a.status==="dead");const f=new Set(t.map(a=>a.node_id));t.push(...(await rr()).filter(a=>!f.has(a.node_id)))}catch(h){u({level:"error",event:"canvas.node_list.failed",error:h});return}await O.run(n,{rows:d,fleet:l,lifecycle:s}),await Rt.run(n,{rows:d});const y=await xt.run(n,{rows:[...d,...t],fleet:l});await It.run(n),await Gt.run(n),await Tt.run(n,{lifecycle:s}),await Dt.run(n,{lifecycle:s}),await Ct.run(n,{lifecycle:s}),await Lt.run(n,{lifecycle:s}),await $t.run(n,{lifecycle:s,fleet:l}),At.run(n,{lifecycle:s}),await qe(),Nt.run(n,{fleet:l,lifecycle:s}),await Mt.run(n,{rows:d}),O.warnIfSaturated({fleet:l,lifecycle:s,thresholds:w,frozen:y})}i(Ot,"superviseTick");async function Ft(n){oe(se(),{recursive:!0,mode:493}),process.platform==="linux"&&Oe(se(),493);const s=ze();if(E(s))throw new Error(`layout refusal cannot bind occupied socket: ${s}`);const l=ie((f,a)=>{if(f.method==="GET"&&f.url==="/healthz"){a.writeHead(200,{"content-type":"application/json; charset=utf-8"}),a.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}a.writeHead(503,{"content-type":"application/json; charset=utf-8"}),a.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,a)=>{l.once("error",a),l.listen(s,()=>{l.off("error",a),f()})});let w;const d=n.tcp??Be();if(d){const f=/^(.*):(\d+)$/.exec(d);if(!f||Number(f[2])>65535)throw new Error(`invalid refusal TCP address: ${d}`);const a=ie(l.listeners("request")[0]);await new Promise((c,m)=>{a.once("error",m),a.listen(Number(f[2]),f[1],()=>{a.off("error",m),c()})}),w=a}ye();let t;const y=i(()=>(t??=(async()=>{l.closeAllConnections(),w?.closeAllConnections(),await Promise.all([l,w].filter(f=>f!==void 0).map(f=>new Promise((a,c)=>f.close(m=>m?c(m):a())))),_e(process.pid),process.off("SIGINT",h),process.off("SIGTERM",h)})(),t),"cleanup"),h=i(()=>{y().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),y}i(Ft,"serveLayoutRefusal");async function Bt(n={}){if(Ye())return Ft(n);Qe();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 Xr()},ownership:await we(ae(16).toString("hex"))}:await ot(async()=>({database:await rt({log:s,startLockHeld:!0}),ownership:await we(ae(16).toString("hex"))}));if(!d.claimed){if(d.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${d.incumbentPid??"?"})
4
+ `);else{const r=[d.incumbentPid!==void 0?`pid ${d.incumbentPid}`:void 0,d.verdict!==void 0?`verdict ${d.verdict}`:void 0].filter(o=>o!==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 y=null;try{await bt(t.token),gt({kind:"user",root:yt()});for(const e of Ze())process.stderr.write(`[crtrd] ${e}
6
+ `);if(process.platform==="darwin"){const{readConfig:e,updateConfig:o}=await import("../core/config.js");if(e("user").paths?.user_files===void 0){const{homedir:p}=await import("node:os");o("user",S=>{S.paths??={},S.paths.user_files??=p()})}}await vt(St());for(const e of Xe())process.stderr.write(`[crtrd] ${e}
7
+ `);await ve(),await et(w.url,s),await U.listen(w.url);const r=await Zr(async e=>await at(e));for(const e of r.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${e.message}
7
8
  `);for(const e of r.invalid)process.stderr.write(`[crtrd] package file is not a document: ${e.path}: ${e.error}
8
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"}
9
10
  `);for(const e of r.kinds.refused)process.stderr.write(`[crtrd] plugin "${e.plugin}" kind ${e.kind} not registered: ${e.message}
10
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}
11
- `);u=await nt(),process.platform==="linux"&&E(x)&&(We(),await D({operation:"users.sync",users:await Be()})),await xr(c.token)}catch(r){pe(),q.close();try{const{released:e}=await le(c);if(!e)throw new Error("daemon ownership claim was not released")}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}let h=!0,o,g=Promise.resolve(),v=!1,d,p;const a=new Set;let m=!1,y=!1,b,A,F,U=!1,N,T,C,$,G;const ve=wr();let R,w={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},I="initial startup activation",ge=null;function ye(){return w.phase==="ready"?null:{phase:w.phase,message:w.message,blockerPaths:w.blockerPaths}}s(ye,"startupBlock");function k(r){a.add(r),r.finally(()=>{a.delete(r)}).catch(()=>{})}s(k,"registerDetached");const K={acceptsDetachedWork:s(()=>h,"acceptsDetachedWork"),registerDetached:k};await Xr(K,me);async function Se(){try{h=!1,Qr.close(),pe(),q.close(),d!==void 0&&(clearInterval(d),d=void 0),C!==void 0&&(clearInterval(C),C=void 0),G?.(),G=void 0;let r;do r=g,await r;while(r!==g);for(;a.size>0;)await Promise.allSettled([...a]);if(p!==void 0&&(await ht(p),p.dispose()),F!==void 0&&await F.close(),A!==void 0&&!U&&(U=!0,await A.close()),$!==void 0){const e=$;$=void 0,await e.stop()}if(T!==void 0){const e=T;T=void 0,await e.close()}if(N!==void 0){const e=N;N=void 0,await e.close()}if(m||(we(process.pid),m=!0),m&&!y){const{released:e}=await le(c);if(!e)throw new Error("daemon ownership claim was not released");y=!0}}finally{Hr(),process.off("SIGINT",Q),process.off("SIGTERM",Y),process.off("exit",X)}}s(Se,"cleanupAttempt");const M=1e3;let J=!1;function _e(){return J?{graceMs:M}:(J=!0,_.fresh(()=>{f({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:M}})}),setTimeout(()=>{be()},M),{graceMs:M})}s(_e,"requestHandover");async function be(){try{await P()}catch(r){const e=r instanceof Error?r.stack??r.message:String(r);process.stderr.write(`[crtrd] handover cleanup failed:
12
+ `);y=await mt(),process.platform==="linux"&&E(I)&&(Ve(),await D({operation:"users.sync",users:await Ue()})),await Lr(t.token)}catch(r){await U.close();try{const{released:e}=await he(t);if(!e)throw new Error("daemon ownership claim was not released");await W()}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}let h=!0,f,a=Promise.resolve(),c=!1,m,v;const k=new Set;let F=!1,V=!1,P,M,B,Q=!1,N,C,L,$,H;const be=_r();let R,g={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},T="initial startup activation",ke=null;function Ee(){return g.phase==="ready"?null:{phase:g.phase,message:g.message,blockerPaths:g.blockerPaths}}i(Ee,"startupBlock");function b(r){k.add(r),r.finally(()=>{k.delete(r)}).catch(()=>{})}i(b,"registerDetached");const Y={acceptsDetachedWork:i(()=>h,"acceptsDetachedWork"),registerDetached:b};await st(Y,ve);async function Pe(){try{h=!1,it.close(),await U.close(),m!==void 0&&(clearInterval(m),m=void 0),L!==void 0&&(clearInterval(L),L=void 0),H?.(),H=void 0;let r;do r=a,await r;while(r!==a);for(;k.size>0;)await Promise.allSettled([...k]);if(v!==void 0&&(await Et(v),await v.exitsSettled(),v.dispose()),B!==void 0&&await B.close(),M!==void 0&&!Q&&(Q=!0,await M.close()),$!==void 0){const e=$;$=void 0,await e.stop()}if(C!==void 0){const e=C;C=void 0,await e.close()}if(N!==void 0){const e=N;N=void 0,await e.close()}if(F||(_e(process.pid),F=!0),F&&!V){const{released:e}=await he(t);if(!e)throw new Error("daemon ownership claim was not released");V=!0,await W()}}finally{Wr(),process.off("SIGINT",ee),process.off("SIGTERM",re),process.off("exit",te)}}i(Pe,"cleanupAttempt");const G=1e3;let X=!1;function Re(){return X?{graceMs:G}:(X=!0,_.fresh(()=>{u({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:G}})}),setTimeout(()=>{xe()},G),{graceMs:G})}i(Re,"requestHandover");async function xe(){try{await x()}catch(r){const e=r instanceof Error?r.stack??r.message:String(r);process.stderr.write(`[crtrd] handover cleanup failed:
12
13
  ${e}
13
- `),process.exit(1)}try{process.platform==="linux"&&E(x)&&(await D({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await jr();const r=await Br({waitForJoinLock:!0});_.fresh(()=>{f({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(()=>{f({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}
14
- `),process.exit(1)}process.exit(0)}s(be,"performHandover");function P(){if(b!==void 0)return b;const r=Se();return b=r,r.then(()=>{b===r&&(b=void 0)},()=>{b===r&&(b=void 0)}),r}s(P,"requestCleanup");async function V(r){try{await P(),process.exit(0)}catch(e){process.exitCode=1;const i=e instanceof Error?e.stack??`${e.name}: ${e.message}`:String(e);process.stderr.write(`[crtrd] ${r} cleanup failed:
15
- ${i}
16
- `),process.exit(1)}}s(V,"handleSignal");function Q(){V("SIGINT")}s(Q,"onSigint");function Y(){V("SIGTERM")}s(Y,"onSigterm");function X(){P().catch(()=>{})}s(X,"onExit");async function H(r){h&&(g=g.then(r).then(()=>{},e=>{_.fresh(()=>{f({level:"error",event:"daemon.lane_job.failed",error:e})})}))}s(H,"enqueue");async function Z(){!h||v||(ve.check()&&R&&(f({level:"info",event:"daemon.resume.detected"}),k(R().catch(()=>{}))),v=!0,await H(async()=>{if(v=!1,!!h)try{await Dt(Date.now(),K)}catch(r){_.fresh(()=>{f({level:"error",event:"daemon.tick.failed",error:r})})}}))}s(Z,"enqueueTick");let L=null;async function ke(){if(L!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${L instanceof Error?L.message:String(L)}`};if(w.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(w.phase!=="prepared"&&w.phase!=="recovering")throw{status:409,code:"not_prepared",message:w.message};const r=w.phase==="prepared";try{return await j(),w={phase:"ready",message:"",blockerPaths:[]},r?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(e){throw L=e,{status:500,code:"admission_failed",message:`daemon admission failed: ${e instanceof Error?e.message:String(e)}`}}}s(ke,"admitStartup");async function Ee(r){if(I!==null)throw{status:409,code:"migration_in_progress",message:`the canvas import is already running (${I})`};I="repair";try{const e=await st({dryRun:r.dryRun});if(r.dryRun||e.pending||w.phase!=="blocked")return{result:e,resumed:!1};if(t.prepare)return w={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:e,resumed:!1};try{return await j(),w={phase:"ready",message:"",blockerPaths:[]},{result:e,resumed:!0}}catch(i){const l=i instanceof Error?i.message:String(i);return setTimeout(()=>{P().catch(()=>{})},1e3),{result:e,resumed:!1,resumeError:l}}}finally{I=null}}s(Ee,"repairMigrations");async function Re(){o=t.intervalMs??hr;const r=c.token;p=new rt({epoch:r,enqueue:H}),Or(p),w={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{f({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:o}})});const e=await zr();if(Wr(e),_.fresh(()=>{f({level:"info",event:"daemon.install_id.resolved",fields:{install_id:e}})}),_.fresh(()=>{let i;try{i=ur()}catch{return}i!==null&&f({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:i.slice(0,2048)}})}),await _.fresh(async()=>{await fr(),await lr(),await pr(Date.now()),await mr(Date.now())}),await _.fresh(async()=>{const{reconcile:i,migrated:l}=await qr(Pr);i.changed&&f({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:i.cleared.length,same_boot_proven:i.sameBootProven}}),l.length>0&&f({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:l.length}})}),await _.fresh(async()=>{for(const i of await ne())i.status!=="active"||i.pi_pid!=null||i.frozen_at!=null||i.respawn_not_before!=null||(await oe(i.node_id,new Date().toISOString()),f({level:"warn",event:"daemon.startup.launch_debt_recorded",node_id:i.node_id}))}),await _.fresh(async()=>{await Ar();for(const i of tt){const l=i.apply();l.changed&&f({level:"info",event:"daemon.boot_migration.applied",fields:{migration:i.id,...l.summary}})}}),k(cr()),R){const i=s(()=>{k(R().catch(()=>{})),k(Ie().then(l=>$?.observe(l)).then(()=>{},l=>{f({level:"warn",event:"daemon.peer.status_failed",error:l})}))},"refresh");k(ar().catch(()=>{})),k(sr().then(({completion:l})=>l).catch(l=>{f({level:"error",event:"daemon.grants.sweep_failed",error:l})})),i(),C=setInterval(i,ir*1e3),C.unref(),G=xe({joined:!0,register:k})}await H(async()=>{await et(),await Zr()}),d=setInterval(()=>{Z().catch(i=>f({level:"error",event:"daemon.tick.failed",error:i}))},o),await Z()}s(Re,"completeStartup");function j(){return ge??=Re().catch(r=>{throw w={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},r})}s(j,"resumeStartup");try{process.on("SIGINT",Q),process.on("SIGTERM",Y),process.on("exit",X),ot(),u!==null&&f({level:"info",event:"daemon.joined_owner.applied",fields:{owner:u.owner,rows:u.rows}}),N=await Oe();const r=c.token;Gr({epoch:r,requestHandover:_e,registerDetached:k,startupBlock:ye,admitStartup:ke,repairMigrations:Ee}),ue(),A=Ye({tcp:t.tcp}),await A.ready;const e=Me()??ie("user").document_port,i=Xe(e,Ze());try{await i.ready,F=i}catch(S){f({level:"warn",event:"daemon.document_listener.bind_failed",fields:{port:e,error:S instanceof Error?S.message:String(S)}})}const l=ie("user").runtime;l?.issuer?(R=or(l.issuer,k),l.unsandboxed===!0&&($=De({syncGrants:R})),tr(S=>nr().appended(S))):await rr(),l?.unsandboxed===!0&&l.listener!==void 0&&f({level:"warn",event:"daemon.app_listener.refused_unsandboxed",fields:{reason:"runtime.unsandboxed is set; runtime.listener is ignored"}}),l!==void 0&&l.listener!==void 0&&l.unsandboxed!==!0&&(T=await er(l,async()=>await dr(l.issuer.replace(/\/$/,"")),R),await T.ready);try{await it()}catch(S){if(I=null,S instanceof at)return w={phase:"blocked",message:S.message,blockerPaths:S.blockerPaths},P;throw S}process.platform==="linux"&&E(x)&&(ze(),await qe("app:terminal"));try{t.prepare?w={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await j(),w={phase:"ready",message:"",blockerPaths:[]})}finally{I=null}return P}catch(r){try{await P()}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}}s(Ct,"runDaemon");var co=Ct;export{z as brokerCommandDeclaresCanvasHome,W as brokerCommandEpoch,co as default,Ct as runDaemon,Dt as superviseTick,ht as teardownFleet};
14
+ `),process.exit(1)}try{process.platform==="linux"&&E(I)&&(await D({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await Ur();const r=await Kr({waitForJoinLock:!0});_.fresh(()=>{u({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:r.pid??r.existing_pid??null,spawned:r.started}})})}catch(r){_.fresh(()=>{u({level:"error",event:"daemon.handover.successor_failed",error:r})});const e=r instanceof Error?r.message:String(r);process.stderr.write(`[crtrd] handover successor failed to start: ${e}
15
+ `),process.exit(1)}process.exit(0)}i(xe,"performHandover");function x(){if(P!==void 0)return P;const r=Pe();return P=r,r.then(()=>{P===r&&(P=void 0)},()=>{P===r&&(P=void 0)}),r}i(x,"requestCleanup");async function Z(r){try{await x(),await nt({claimed:i(async()=>{try{return await Yr()}finally{await W()}},"claimed")}),process.exit(0)}catch(e){process.exitCode=1;const o=e instanceof Error?e.stack??`${e.name}: ${e.message}`:String(e);process.stderr.write(`[crtrd] ${r} cleanup failed:
16
+ ${o}
17
+ `),process.exit(1)}}i(Z,"handleSignal");function ee(){Z("SIGINT")}i(ee,"onSigint");function re(){Z("SIGTERM")}i(re,"onSigterm");function te(){x().catch(()=>{})}i(te,"onExit");async function j(r){h&&(a=a.then(r).then(()=>{},e=>{_.fresh(()=>{u({level:"error",event:"daemon.lane_job.failed",error:e})})}))}i(j,"enqueue");async function ne(){!h||c||(be.check()&&R&&(u({level:"info",event:"daemon.resume.detected"}),b(R().catch(()=>{}))),c=!0,await j(async()=>{if(c=!1,!!h)try{l||await tt({log:s}),await Ot(Date.now(),Y)}catch(r){_.fresh(()=>{u({level:"error",event:"daemon.tick.failed",error:r})})}}))}i(ne,"enqueueTick");let A=null;async function Ie(){if(A!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${A instanceof Error?A.message:String(A)}`};if(g.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(g.phase!=="prepared"&&g.phase!=="recovering")throw{status:409,code:"not_prepared",message:g.message};const r=g.phase==="prepared";try{return await q(),g={phase:"ready",message:"",blockerPaths:[]},r?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(e){throw A=e,{status:500,code:"admission_failed",message:`daemon admission failed: ${e instanceof Error?e.message:String(e)}`}}}i(Ie,"admitStartup");async function Te(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 ht({dryRun:r.dryRun});if(r.dryRun||e.pending||g.phase!=="blocked")return{result:e,resumed:!1};if(n.prepare)return g={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:e,resumed:!1};try{return await q(),g={phase:"ready",message:"",blockerPaths:[]},{result:e,resumed:!0}}catch(o){const p=o instanceof Error?o.message:String(o);return setTimeout(()=>{x().catch(()=>{})},1e3),{result:e,resumed:!1,resumeError:p}}}finally{T=null}}i(Te,"repairMigrations");async function De(){f=n.intervalMs??Sr;const r=t.token;v=new lt({epoch:r,enqueue:j}),jr(v),g={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{u({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:f}})});const e=await Vr();if(Qr(e),_.fresh(()=>{u({level:"info",event:"daemon.install_id.resolved",fields:{install_id:e}})}),_.fresh(()=>{let o;try{o=yr()}catch{return}o!==null&&u({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:o.slice(0,2048)}})}),await _.fresh(async()=>{await gr(),await wr(),await hr(Date.now()),await vr(Date.now())}),await _.fresh(async()=>{const{reconcile:o,migrated:p}=await Jr(Cr);o.changed&&u({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:o.cleared.length,same_boot_proven:o.sameBootProven}}),p.length>0&&u({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:p.length}})}),await _.fresh(async()=>{for(const o of await ce())o.status!=="active"||o.pi_pid!=null||o.frozen_at!=null||o.respawn_not_before!=null||(await de(o.node_id,new Date().toISOString()),u({level:"warn",event:"daemon.startup.launch_debt_recorded",node_id:o.node_id}))}),await _.fresh(async()=>{await Fr();for(const o of pt){const p=o.apply();p.changed&&u({level:"info",event:"daemon.boot_migration.applied",fields:{migration:o.id,...p.summary}})}}),b(fr()),R){const o=i(()=>{b(R().catch(()=>{})),b($e().then(p=>$?.observe(p)).then(()=>{},p=>{u({level:"warn",event:"daemon.peer.status_failed",error:p})}))},"refresh");b(pr().catch(()=>{})),b(mr().then(({completion:p})=>p).catch(p=>{u({level:"error",event:"daemon.grants.sweep_failed",error:p})})),o(),L=setInterval(o,lr*1e3),L.unref(),H=Le({joined:!0,register:b})}await j(async()=>{await dt(),await ct()}),m=setInterval(()=>{ne().catch(o=>u({level:"error",event:"daemon.tick.failed",error:o}))},f),await ne()}i(De,"completeStartup");function q(){return ke??=De().catch(r=>{throw g={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},r})}i(q,"resumeStartup");try{process.on("SIGINT",ee),process.on("SIGTERM",re),process.on("exit",te),ft(),y!==null&&u({level:"info",event:"daemon.joined_owner.applied",fields:{owner:y.owner,rows:y.rows}}),N=await je();const r=t.token;zr({epoch:r,requestHandover:Re,registerDetached:b,startupBlock:Ee,admitStartup:Ie,repairMigrations:Te}),ye(),M=tr({tcp:n.tcp}),await M.ready;const e=He()??le("user").document_port,o=nr(e,or());try{await o.ready,B=o}catch(S){u({level:"warn",event:"daemon.document_listener.bind_failed",fields:{port:e,error:S instanceof Error?S.message:String(S)}})}const p=le("user").runtime;p?.issuer?(R=dr(p.issuer,b),p.unsandboxed===!0&&($=Ae({syncGrants:R})),sr(S=>cr().appended(S))):await ar(),p?.unsandboxed===!0&&p.listener!==void 0&&u({level:"warn",event:"daemon.app_listener.refused_unsandboxed",fields:{reason:"runtime.unsandboxed is set; runtime.listener is ignored"}}),p!==void 0&&p.listener!==void 0&&p.unsandboxed!==!0&&(C=await ir(p,async()=>await ur(p.issuer.replace(/\/$/,"")),R),await C.ready);try{await ut()}catch(S){if(T=null,S instanceof wt)return g={phase:"blocked",message:S.message,blockerPaths:S.blockerPaths},x;throw S}process.platform==="linux"&&E(I)&&(Je(),await Ke("app:terminal"));try{n.prepare?g={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await q(),g={phase:"ready",message:"",blockerPaths:[]})}finally{T=null}return x}catch(r){try{await x()}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}}i(Bt,"runDaemon");var So=Bt;export{K as brokerCommandDeclaresCanvasHome,J as brokerCommandEpoch,So as default,Bt as runDaemon,Ot as superviseTick,Et as teardownFleet};
@@ -8,4 +8,4 @@ var D=Object.defineProperty;var a=(n,e)=>D(n,"name",{value:e,configurable:!0});i
8
8
  n.turn_open_at, n.turn_closed_at,
9
9
  (SELECT x.started_at FROM executions x WHERE x.node_id = n.node_id AND x.state != 'exited') AS exec_started_at
10
10
  FROM node_rows n
11
- WHERE n.status IN ('active', 'idle') AND n.kind != 'human' AND n.${N}`);for(const t of e){if(p(t.profile_id)||!await L({node_id:t.node_id,status:t.status,intent:t.intent}))continue;if(t.pi_pid!==null&&R(t.pi_pid)){if(t.turn_open_at!==null&&t.turn_closed_at===null)return!0;const d=o(t.exec_started_at);if(t.turn_open_at===null&&d!==null&&n-d<w||await E(t.node_id))return!0;continue}if(t.respawn_not_before!==null)continue;if(t.pi_pid!==null||t.frozen_at!==null||t.status==="active")return!0;const u=Math.max(...[t.created,t.launched_at,t.exec_started_at].map(d=>o(d)??0));if(u>0&&n-u<w||t.status==="idle"&&await E(t.node_id))return!0}return!1}a(U,"nodesOweWork");async function X(n=Date.now()){await S();const{dueNow:e,nextDueMs:t}=await b(n,(u,d)=>{A({level:"warn",event:"daemon.due.source_failed",error:d,fields:{lane:u}})});return{busy:e||await U(n)||await s("SELECT 1 FROM crons WHERE run_state = 'running' LIMIT 1")!==void 0||await s("SELECT 1 FROM jobs j JOIN objects o ON o.rowid = j.object WHERE j.status = 'running' AND o.deleted_at IS NULL LIMIT 1")!==void 0,next_due_at:t===null?null:new Date(t).toISOString()}}a(X,"dueReport");async function K(){return await S(),[...g(),...M()]}a(K,"coveredLanes");export{w as START_GRACE_MS,K as coveredLanes,X as dueReport,S as installDueSources};
11
+ WHERE n.status IN ('active', 'idle') AND n.kind != 'human' AND n.${N}`);for(const t of e){if(p(t.profile_id)||!await L({node_id:t.node_id,status:t.status,intent:t.intent}))continue;if(t.pi_pid!==null&&R(t.pi_pid)){if(t.turn_open_at!==null&&t.turn_closed_at===null)return!0;const d=o(t.exec_started_at);if(t.turn_open_at===null&&d!==null&&n-d<w||await E(t.node_id))return!0;continue}if(t.respawn_not_before!==null)continue;if(t.pi_pid!==null||t.frozen_at!==null||t.status==="active")return!0;const u=Math.max(...[t.created,t.launched_at,t.exec_started_at].map(d=>o(d)??0));if(u>0&&n-u<w||t.status==="idle"&&await E(t.node_id))return!0}return!1}a(U,"nodesOweWork");async function X(n=Date.now()){await S();const{dueNow:e,nextDueMs:t}=await b(n,(u,d)=>{A({level:"warn",event:"daemon.due.source_failed",error:d,fields:{lane:u}})});return{busy:e||await U(n)||await s("SELECT 1 FROM crons WHERE run_state = 'running' LIMIT 1")!==void 0||await s("SELECT 1 FROM jobs j JOIN objects o ON o.id = j.object WHERE j.status = 'running' AND o.deleted_at IS NULL LIMIT 1")!==void 0,next_due_at:t===null?null:new Date(t).toISOString()}}a(X,"dueReport");async function K(){return await S(),[...g(),...M()]}a(K,"coveredLanes");export{w as START_GRACE_MS,K as coveredLanes,X as dueReport,S as installDueSources};
@@ -0,0 +1 @@
1
+ export declare function runExportSqlite(): Promise<number>;
@@ -0,0 +1,4 @@
1
+ var a=Object.defineProperty;var s=(e,r)=>a(e,"name",{value:r,configurable:!0});import{daemonOwnershipClaimed as i}from"../core/canvas/daemon-owner.js";import{closeAllDbs as n}from"../core/canvas/db.js";import{exportPostgresToSqlite as c}from"../core/canvas/export-sqlite.js";import{ensurePostgres as l,stopPostgres as p}from"./postgres/index.js";async function g(){let e=!1,r=0;try{const t=await l();e=t.started;const o=await c(t.url);process.stdout.write(`${JSON.stringify(o)}
2
+ `)}catch(t){process.stderr.write(`${t.message}
3
+ `),r=1}if(e)try{await p({claimed:s(async()=>{try{return await i()}finally{await n()}},"claimed")})}catch(t){process.stderr.write(`the export started Postgres and could not stop it: ${t.message}
4
+ `),r=1}return r}s(g,"runExportSqlite");export{g as runExportSqlite};
@@ -66,6 +66,7 @@ export declare class DaemonFleet implements FleetRegistry {
66
66
  interruptForProfilePause(nodeId: string, pid?: number | undefined): void;
67
67
  hasProfilePauseInterruption(nodeId: string): boolean;
68
68
  forget(nodeId: string): void;
69
+ exitsSettled(): Promise<void>;
69
70
  onChildExit(nodeId: string, status: FleetExitStatus, executionId?: string): Promise<void>;
70
71
  deliverExit(nodeId: string, status: FleetExitStatus): Promise<void>;
71
72
  /** The node-lifecycle tick's enactment entry. */
@@ -1,4 +1,4 @@
1
- var g=Object.defineProperty;var l=(i,e)=>g(i,"name",{value:e,configurable:!0});import{getNode as w,getRow as k}from"../core/canvas/index.js";import{revokeExitedNodeToken as $}from"../core/canvas/node-tokens.js";import{isProfilePaused as T}from"../core/profiles/manifest.js";import{fullName as p}from"../core/canvas/labels.js";import{isSafeNodeId as o}from"../core/canvas/paths.js";import{hasIdleReleaseIntent as R,transition as v}from"../core/runtime/lifecycle.js";import{publishNotice as E}from"../core/runtime/close.js";import{readFault as P,readInterruptedTurn as N,readTransportPoisonVerdict as x}from"../core/canvas/recovery.js";import{hasPendingWake as C}from"./reconcilers/live-obligation.js";import{clearRespawnPolicy as h,deferRespawnWhileOffline as F,MAX_RESPAWN_ATTEMPTS as M,OFFLINE_RESPAWN_RETRY_MS as S,recordRespawnPolicy as O,respawnBackoffMs as z}from"./reconcilers/node-lifecycle/respawn-policy.js";import{probeOnline as A}from"../core/runtime/connectivity.js";import{reviveNode as B}from"../core/runtime/revive.js";import{pushUrgent as m}from"../core/feed/feed.js";import{emitEvent as c}from"../core/events/emit.js";import{brokerThresholdsForDaemon as D}from"../core/runtime/fleet.js";import{isRecoveryHeld as L}from"../core/runtime/recovery-hold.js";async function _(i,e){const t=await P(i.node_id),r=t===null?"":`The broker reported a ${t.kind} fault on ${t.link} (${t.op}) before it exited: ${t.message}
1
+ var g=Object.defineProperty;var l=(i,e)=>g(i,"name",{value:e,configurable:!0});import{getNode as w,getRow as k}from"../core/canvas/index.js";import{revokeExitedNodeToken as $}from"../core/canvas/node-tokens.js";import{isProfilePaused as T}from"../core/profiles/manifest.js";import{fullName as p}from"../core/canvas/labels.js";import{isSafeNodeId as o}from"../core/canvas/paths.js";import{hasIdleReleaseIntent as x,transition as v}from"../core/runtime/lifecycle.js";import{publishNotice as E}from"../core/runtime/close.js";import{readFault as P,readInterruptedTurn as R,readTransportPoisonVerdict as N}from"../core/canvas/recovery.js";import{hasPendingWake as C}from"./reconcilers/live-obligation.js";import{clearRespawnPolicy as h,deferRespawnWhileOffline as F,MAX_RESPAWN_ATTEMPTS as S,OFFLINE_RESPAWN_RETRY_MS as M,recordRespawnPolicy as O,respawnBackoffMs as z}from"./reconcilers/node-lifecycle/respawn-policy.js";import{probeOnline as A}from"../core/runtime/connectivity.js";import{reviveNode as B}from"../core/runtime/revive.js";import{pushUrgent as m}from"../core/feed/feed.js";import{emitEvent as c}from"../core/events/emit.js";import{brokerThresholdsForDaemon as D}from"../core/runtime/fleet.js";import{isRecoveryHeld as L}from"../core/runtime/recovery-hold.js";async function _(i,e){const t=await P(i.node_id),r=t===null?"":`The broker reported a ${t.kind} fault on ${t.link} (${t.op}) before it exited: ${t.message}
2
2
 
3
3
  `,s=e===void 0?"The daemon did not observe an exit status.":`The observed exit status was code ${e.code??"null"}, signal ${e.signal??"null"}.`;return`\u26A0 Spawn failed \u2014 \`${i.name}\` (${i.kind}) never started.
4
4
 
@@ -8,7 +8,7 @@ Its pi vehicle exited before the session came up (no pi_session_id was ever reco
8
8
 
9
9
  If the work still needs doing, re-spawn it.`}l(_,"bootFailureBody");async function y(i,e){await m(i.node_id,await _(i,e),{from:i.node_id})}l(y,"surfaceBootFailure");function b(i){return`\u26A0 Out of memory \u2014 \`${i.name}\` (${i.kind}) was killed because its app's processes together passed the app's memory limit.
10
10
 
11
- Every process of the run was stopped. The daemon does not restart it, because it would reach the same limit. Reduce what the run holds in memory, or ask the person to raise the app's limit (\`crtr sys limits set --app ${i.grantee??"app:terminal"} --memory <size>\`), then revive the node.`}l(b,"outOfMemoryBody");async function W(i){const e=await x(i.node_id),t=e===null?"":`
11
+ Every process of the run was stopped. The daemon does not restart it, because it would reach the same limit. Reduce what the run holds in memory, or ask the person to raise the app's limit (\`crtr sys limits set --app ${i.grantee??"app:terminal"} --memory <size>\`), then revive the node.`}l(b,"outOfMemoryBody");async function W(i){const e=await N(i.node_id),t=e===null?"":`
12
12
 
13
13
  Provider rotation observed ${e.consecutivePasses} consecutive route-queue passes with no HTTP response (tried ${e.routes.join(", ")}). Check network and provider transport before reviving.`,r=`\u26A0 Crash loop \u2014 \`${i.name}\` (${i.kind}) kept dying without settling a turn.
14
14
 
@@ -18,4 +18,4 @@ Fix the crash cause, then revive the node if the work still needs doing.`;await
18
18
 
19
19
  The daemon refused this recovery before starting a broker: ${t}
20
20
 
21
- The node is now dead. Fix the recorded launch configuration, then revive it if the work still needs doing.`;await m(i.node_id,r,{from:i.node_id})}l(q,"surfaceRecoveryFailure");function G(i){return i?.cycle_pending!==!0}l(G,"retryResumeMode");function H(i,e,t=!0){return i===null||i.status==="done"||i.status==="canceled"||i.status==="dead"?"forget":i.intent==="refresh"?"respawn-fresh":R(i)?"dormant":i.lifecycle==="resident"&&!e&&!t&&i.pi_session_id!=null?"release-idle":i.pi_session_id!=null?G(i)?"respawn-resume":"respawn-cycle":e||i.fork_from!=null?"respawn-fresh":"boot-failure"}l(H,"classifyDeadNode");const Y=3e4;class le{static{l(this,"DaemonFleet")}#n;#o;#i;#l;#e=new Map;#t=new Set;#a=new Set;#r=new Map;#c=Number.NEGATIVE_INFINITY;constructor(e){this.#n=e.epoch,this.#o=e.enqueue,this.#i=e.now??Date.now,this.#l=e.probeOnline??(()=>A())}epoch(){return this.#n}register(e,t){if(this.#e.has(e))throw new Error(`fleet: ${e} already has a live broker entry \u2014 refusing a second launch (programming error)`);if(t.pid===null)throw new Error(`fleet: refusing to register ${e} with no pid`);this.#t.delete(e),this.#r.delete(e),this.#e.set(e,{pid:t.pid,handle:t})}has(e){return this.#e.has(e)}isClaimed(e){return this.#e.has(e)||this.#t.has(e)}get(e){return this.#e.get(e)}size(){return this.#e.size}occupancy(){return this.#e.size+this.#t.size}reserveSlot(e){if(this.#t.has(e)||this.#e.has(e))return!0;const{automaticReviveCap:t}=D();if(this.occupancy()<t)return this.#t.add(e),!0;const r=this.#i();return r-this.#c>=Y&&(this.#c=r,c({level:"warn",event:"broker.capacity.revive_deferred",fields:{live_count:this.occupancy(),hard_cap:t}})),!1}releaseReservation(e){this.#t.delete(e)}entries(){return this.#e.entries()}interruptForProfilePause(e,t=this.#e.get(e)?.pid){if(t!==void 0){this.#a.add(e);try{process.kill(t,"SIGTERM")}catch{}}}hasProfilePauseInterruption(e){return this.#a.has(e)}forget(e){this.#e.delete(e),this.#t.delete(e),this.#r.delete(e)}async onChildExit(e,t,r){r&&await $(r),this.#e.delete(e),this.#o(async()=>{const s=await w(e),a=s?.launched_at==null?Number.NaN:Date.parse(s.launched_at),n=Number.isFinite(a)?Math.max(0,this.#i()-a):null,d=s?.respawn_failures??0;s?.status==="done"||s?.status==="canceled"||s?.status==="dead"?this.#r.delete(e):this.#r.set(e,t),this.#u(e,t,n,d)})}async deliverExit(e,t){await this.onChildExit(e,t)}async applyDeadRowPolicy(e){const t=this.#r.get(e);try{await this.#f(e,t)}finally{this.#r.get(e)===t&&this.#r.delete(e)}}async terminalizeIfStillNonTerminal(e,t,r,s,a){const n=await w(e);return n===null||n.status==="done"||n.status==="canceled"||n.status==="dead"?!1:(await this.#s(e,n,{event:t,reason:s,detail:a,wake:`Child crashed \u2014 ${p(n)} (${e}) could not be relaunched and is now dead. It stays dead until you revive it \u2014 \`crtr node lifecycle revive ${e}\`.`,surface:l(()=>q(n,r),"surface")}),!0)}dispose(){}#u(e,t,r,s){c({level:"info",event:"broker.exit.observed",...o(e)?{node_id:e}:{},fields:{...o(e)?{}:{affected_node_id:e},code:t.code,signal:t.signal,uptime_ms:r,consecutive_failures:s}})}async#f(e,t){return this.#h(e,t)}async#h(e,t){const r=await w(e);if(r!==null&&T(r.profile_id))return await h(e),"forget";if(this.#a.delete(e))return await h(e),"forget";if(L(e))return"dormant";const s=(await N(e))?.interrupted===!0;if(t?.oomKilled===!0&&r!==null&&r.status!=="done"&&r.status!=="canceled"&&r.status!=="dead")return await h(e),await this.#s(e,r,{event:"broker.out_of_memory",reason:"out_of_memory",detail:{schema:"crtr.node-outcome-detail/v1",message:b(r)},wake:`Child out of memory \u2014 ${p(r)} (${e}) used more memory than its app is allowed and is now dead. It is not restarted automatically. Revive it with \`crtr node lifecycle revive ${e}\` once the work needs less memory or the cap is raised (\`crtr sys limits set\`).`,surface:l(async()=>{await m(e,b(r),{from:e})},"surface")}),"forget";const a=H(r,s,await C(e));switch(a){case"forget":await h(e);break;case"dormant":await h(e);break;case"release-idle":await h(e),await v(e,"release"),c({level:"info",event:"broker.recovery.released",...o(e)?{node_id:e}:{},fields:{...o(e)?{}:{affected_node_id:e}}});break;case"boot-failure":await this.#s(e,r,{event:"broker.boot.failed",reason:"boot_failed",detail:{schema:"crtr.node-outcome-detail/v1",message:await _(r,t)},wake:`Child crashed \u2014 ${p(r)} (${e}) exited before its session ever came up and is now dead. It stays dead until you revive it \u2014 \`crtr node lifecycle revive ${e}\`.`,surface:l(()=>y(r,t),"surface")});break;case"respawn-fresh":case"respawn-cycle":case"respawn-resume":await this.#d(e,r,a,s);break}return a}async#d(e,t,r,s){const a=await k(e);if(a===null)return;const n=a.respawn_failures??0;if(!await this.#l()){await F(e,n,this.#i()),c({level:"info",event:"broker.respawn.offline_deferred",...o(e)?{node_id:e}:{},fields:{...o(e)?{}:{affected_node_id:e},action:r,retry_in_ms:S,consecutive_failures:n}});return}const d=a.respawn_not_before==null,f=d?await O(a,this.#i()):{failures:n,notBefore:a.respawn_not_before,exhausted:n>=M};if(f.exhausted){const u=`Child crashed \u2014 ${p(t)} (${e}) kept dying without settling a turn (${f.failures} consecutive launches) and is now dead. It stays dead until you revive it \u2014 \`crtr node lifecycle revive ${e}\`.`;await this.#s(e,t,{event:"broker.respawn.exhausted",reason:"crash_looped",detail:{schema:"crtr.node-outcome-detail/v1",message:u,respawn_failures:f.failures},wake:u,...t.pi_session_id!=null?{surface:l(()=>W(t),"surface")}:{}});return}if(d&&f.notBefore!==null){c({level:"info",event:"broker.respawn.deferred",...o(e)?{node_id:e}:{},fields:{...o(e)?{}:{affected_node_id:e},action:r,backoff_ms:z(f.failures),consecutive_failures:f.failures}});return}c({level:"info",event:"broker.respawn.started",...o(e)?{node_id:e}:{},fields:{...o(e)?{}:{affected_node_id:e},action:r,consecutive_failures:f.failures}});try{await B(e,{capacity:"freeze",resume:r==="respawn-resume",wakeReason:r==="respawn-resume"&&s?"runtime-restart-abort":void 0,recovery:!0})}catch(u){c({level:"error",event:"broker.respawn.failed",...o(e)?{node_id:e}:{},error:u,fields:{...o(e)?{}:{affected_node_id:e},action:r}}),await this.terminalizeIfStillNonTerminal(e,"broker.respawn.refused",u,"launch_failed",{schema:"crtr.node-outcome-detail/v1",message:u instanceof Error?u.message:String(u)})}}async#s(e,t,r){c({level:"error",event:r.event,...o(e)?{node_id:e}:{fields:{affected_node_id:e}}}),await v(e,"crash",{reason:r.reason,outcome:{detail:r.detail}});try{await(r.surface??(()=>y(t)))()}catch(s){c({level:"error",event:"broker.boot_failure_surface.failed",...o(e)?{node_id:e}:{fields:{affected_node_id:e}},error:s})}await E(e,"notice.crash",r.wake,{reason:"child-crashed",child:e})}}export{le as DaemonFleet,H as classifyDeadNode,G as retryResumeMode,y as surfaceBootFailure};
21
+ The node is now dead. Fix the recorded launch configuration, then revive it if the work still needs doing.`;await m(i.node_id,r,{from:i.node_id})}l(q,"surfaceRecoveryFailure");function G(i){return i?.cycle_pending!==!0}l(G,"retryResumeMode");function H(i,e,t=!0){return i===null||i.status==="done"||i.status==="canceled"||i.status==="dead"?"forget":i.intent==="refresh"?"respawn-fresh":x(i)?"dormant":i.lifecycle==="resident"&&!e&&!t&&i.pi_session_id!=null?"release-idle":i.pi_session_id!=null?G(i)?"respawn-resume":"respawn-cycle":e||i.fork_from!=null?"respawn-fresh":"boot-failure"}l(H,"classifyDeadNode");const Y=3e4;class le{static{l(this,"DaemonFleet")}#o;#l;#i;#c;#e=new Map;#t=new Set;#n=new Set;#r=new Map;#u=Number.NEGATIVE_INFINITY;constructor(e){this.#o=e.epoch,this.#l=e.enqueue,this.#i=e.now??Date.now,this.#c=e.probeOnline??(()=>A())}epoch(){return this.#o}register(e,t){if(this.#e.has(e))throw new Error(`fleet: ${e} already has a live broker entry \u2014 refusing a second launch (programming error)`);if(t.pid===null)throw new Error(`fleet: refusing to register ${e} with no pid`);this.#t.delete(e),this.#r.delete(e),this.#e.set(e,{pid:t.pid,handle:t})}has(e){return this.#e.has(e)}isClaimed(e){return this.#e.has(e)||this.#t.has(e)}get(e){return this.#e.get(e)}size(){return this.#e.size}occupancy(){return this.#e.size+this.#t.size}reserveSlot(e){if(this.#t.has(e)||this.#e.has(e))return!0;const{automaticReviveCap:t}=D();if(this.occupancy()<t)return this.#t.add(e),!0;const r=this.#i();return r-this.#u>=Y&&(this.#u=r,c({level:"warn",event:"broker.capacity.revive_deferred",fields:{live_count:this.occupancy(),hard_cap:t}})),!1}releaseReservation(e){this.#t.delete(e)}entries(){return this.#e.entries()}interruptForProfilePause(e,t=this.#e.get(e)?.pid){if(t!==void 0){this.#n.add(e);try{process.kill(t,"SIGTERM")}catch{}}}hasProfilePauseInterruption(e){return this.#n.has(e)}forget(e){this.#e.delete(e),this.#t.delete(e),this.#r.delete(e)}#s=new Set;async exitsSettled(){for(;this.#s.size>0;)await Promise.allSettled([...this.#s])}async onChildExit(e,t,r){const s=this.#f(e,t,r);this.#s.add(s);try{await s}finally{this.#s.delete(s)}}async#f(e,t,r){r&&await $(r),this.#e.delete(e),this.#l(async()=>{const s=await w(e),a=s?.launched_at==null?Number.NaN:Date.parse(s.launched_at),n=Number.isFinite(a)?Math.max(0,this.#i()-a):null,d=s?.respawn_failures??0;s?.status==="done"||s?.status==="canceled"||s?.status==="dead"?this.#r.delete(e):this.#r.set(e,t),this.#h(e,t,n,d)})}async deliverExit(e,t){await this.onChildExit(e,t)}async applyDeadRowPolicy(e){const t=this.#r.get(e);try{await this.#d(e,t)}finally{this.#r.get(e)===t&&this.#r.delete(e)}}async terminalizeIfStillNonTerminal(e,t,r,s,a){const n=await w(e);return n===null||n.status==="done"||n.status==="canceled"||n.status==="dead"?!1:(await this.#a(e,n,{event:t,reason:s,detail:a,wake:`Child crashed \u2014 ${p(n)} (${e}) could not be relaunched and is now dead. It stays dead until you revive it \u2014 \`crtr node lifecycle revive ${e}\`.`,surface:l(()=>q(n,r),"surface")}),!0)}dispose(){}#h(e,t,r,s){c({level:"info",event:"broker.exit.observed",...o(e)?{node_id:e}:{},fields:{...o(e)?{}:{affected_node_id:e},code:t.code,signal:t.signal,uptime_ms:r,consecutive_failures:s}})}async#d(e,t){return this.#p(e,t)}async#p(e,t){const r=await w(e);if(r!==null&&T(r.profile_id))return await h(e),"forget";if(this.#n.delete(e))return await h(e),"forget";if(L(e))return"dormant";const s=(await R(e))?.interrupted===!0;if(t?.oomKilled===!0&&r!==null&&r.status!=="done"&&r.status!=="canceled"&&r.status!=="dead")return await h(e),await this.#a(e,r,{event:"broker.out_of_memory",reason:"out_of_memory",detail:{schema:"crtr.node-outcome-detail/v1",message:b(r)},wake:`Child out of memory \u2014 ${p(r)} (${e}) used more memory than its app is allowed and is now dead. It is not restarted automatically. Revive it with \`crtr node lifecycle revive ${e}\` once the work needs less memory or the cap is raised (\`crtr sys limits set\`).`,surface:l(async()=>{await m(e,b(r),{from:e})},"surface")}),"forget";const a=H(r,s,await C(e));switch(a){case"forget":await h(e);break;case"dormant":await h(e);break;case"release-idle":await h(e),await v(e,"release"),c({level:"info",event:"broker.recovery.released",...o(e)?{node_id:e}:{},fields:{...o(e)?{}:{affected_node_id:e}}});break;case"boot-failure":await this.#a(e,r,{event:"broker.boot.failed",reason:"boot_failed",detail:{schema:"crtr.node-outcome-detail/v1",message:await _(r,t)},wake:`Child crashed \u2014 ${p(r)} (${e}) exited before its session ever came up and is now dead. It stays dead until you revive it \u2014 \`crtr node lifecycle revive ${e}\`.`,surface:l(()=>y(r,t),"surface")});break;case"respawn-fresh":case"respawn-cycle":case"respawn-resume":await this.#m(e,r,a,s);break}return a}async#m(e,t,r,s){const a=await k(e);if(a===null)return;const n=a.respawn_failures??0;if(!await this.#c()){await F(e,n,this.#i()),c({level:"info",event:"broker.respawn.offline_deferred",...o(e)?{node_id:e}:{},fields:{...o(e)?{}:{affected_node_id:e},action:r,retry_in_ms:M,consecutive_failures:n}});return}const d=a.respawn_not_before==null,f=d?await O(a,this.#i()):{failures:n,notBefore:a.respawn_not_before,exhausted:n>=S};if(f.exhausted){const u=`Child crashed \u2014 ${p(t)} (${e}) kept dying without settling a turn (${f.failures} consecutive launches) and is now dead. It stays dead until you revive it \u2014 \`crtr node lifecycle revive ${e}\`.`;await this.#a(e,t,{event:"broker.respawn.exhausted",reason:"crash_looped",detail:{schema:"crtr.node-outcome-detail/v1",message:u,respawn_failures:f.failures},wake:u,...t.pi_session_id!=null?{surface:l(()=>W(t),"surface")}:{}});return}if(d&&f.notBefore!==null){c({level:"info",event:"broker.respawn.deferred",...o(e)?{node_id:e}:{},fields:{...o(e)?{}:{affected_node_id:e},action:r,backoff_ms:z(f.failures),consecutive_failures:f.failures}});return}c({level:"info",event:"broker.respawn.started",...o(e)?{node_id:e}:{},fields:{...o(e)?{}:{affected_node_id:e},action:r,consecutive_failures:f.failures}});try{await B(e,{capacity:"freeze",resume:r==="respawn-resume",wakeReason:r==="respawn-resume"&&s?"runtime-restart-abort":void 0,recovery:!0})}catch(u){c({level:"error",event:"broker.respawn.failed",...o(e)?{node_id:e}:{},error:u,fields:{...o(e)?{}:{affected_node_id:e},action:r}}),await this.terminalizeIfStillNonTerminal(e,"broker.respawn.refused",u,"launch_failed",{schema:"crtr.node-outcome-detail/v1",message:u instanceof Error?u.message:String(u)})}}async#a(e,t,r){c({level:"error",event:r.event,...o(e)?{node_id:e}:{fields:{affected_node_id:e}}}),await v(e,"crash",{reason:r.reason,outcome:{detail:r.detail}});try{await(r.surface??(()=>y(t)))()}catch(s){c({level:"error",event:"broker.boot_failure_surface.failed",...o(e)?{node_id:e}:{fields:{affected_node_id:e}},error:s})}await E(e,"notice.crash",r.wake,{reason:"child-crashed",child:e})}}export{le as DaemonFleet,H as classifyDeadNode,G as retryResumeMode,y as surfaceBootFailure};
@@ -1 +1 @@
1
- var u=Object.defineProperty;var d=(e,o)=>u(e,"name",{value:o,configurable:!0});import{withCanvasWrite as m}from"../../core/canvas/db.js";import{enqueueDaemonAction as f}from"../../core/canvas/daemon-actions.js";import{cancelDelivery as w,enqueueNodeMail as p}from"../../core/feed/messages.js";import{getRequest as l,markCompanionRetired as y,markProjected as q,markProjectionError as v,setSettlementDeliveryMarksLocked as j,settleRequestLocked as h}from"../../core/human/requests.js";import{appendQuestionAnswered as k}from"../../core/runs/questions.js";import{notifyProviderApproval as b}from"../../core/providers/approval.js";async function x(e){const o=await m(async n=>{const _=e.settled_at??new Date().toISOString(),t=await h(n,{request_id:e.request_id,outcome:e.outcome,result_json:e.result_json,settled_at:_});if(t===null){const c=await 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 i=typeof e.notification=="function"?await e.notification(t):e.notification;if(t.requester_node_id!==null&&i===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(i===void 0)throw new Error(`request ${t.request_id} needs a requester notification`);a=(await p(t.requester_node_id,{kind:"completed",label:i.label,senderName:"crtrd",...i.body_ref===void 0?{}:{bodyRef:i.body_ref},...i.body_json===void 0?{}:{bodyJson:i.body_json},disposition:i.disposition,urgency:"normal",wake:1})).deliveryId}t.action_json!==null&&e.completion_action!==void 0&&t.kind==="page"&&(s=await f({adapter:e.completion_action.adapter,bodyJson:e.completion_action.bodyJson,idempotencyKey:`human-action:${t.inbox_ticket_id}`})),t.submit_note_delivery_id!==null&&await w(t.submit_note_delivery_id),await j(n,t.request_id,{settlement_delivery_id:a,action_delivery_id:s});const r=await l(t.request_id);if(r===null)throw new Error(`settled human request disappeared: ${t.request_id}`);return await k(n,r),{outcome:"settled",request:r}});return o.outcome==="settled"&&(b(o.request.request_id),e.post_commit!==void 0&&await g(o.request,e.post_commit)),o}d(x,"settleHumanRequest");async function g(e,o){if(e.kind==="review"&&e.projected_at===null&&o.project!==void 0)try{await o.project(e),await q(e.request_id,new Date().toISOString())}catch(n){await v(e.request_id,n instanceof Error?n.message:String(n))}if(e.companion_node_id!==null&&e.companion_retired_at===null&&o.retire!==void 0)try{await o.retire(e.companion_node_id,e.outcome==="canceled"||e.outcome==="dismissed"?"canceled":"approved"),await y(e.request_id,new Date().toISOString())}catch{}}d(g,"projectAndRetire");export{g as projectAndRetire,x as settleHumanRequest};
1
+ var m=Object.defineProperty;var d=(e,o)=>m(e,"name",{value:o,configurable:!0});import{withCanvasWrite as u}from"../../core/canvas/db.js";import{enqueueDaemonAction as f}from"../../core/canvas/daemon-actions.js";import{cancelDelivery as w,enqueueNodeMail as p}from"../../core/feed/messages.js";import{getRequest as l,markCompanionRetired as y,markProjected as q,markProjectionError as v,setSettlementDeliveryMarksLocked as j,settleRequestLocked as h}from"../../core/human/requests.js";import{appendQuestionAnswered as k}from"../../core/runs/questions.js";import{notifyProviderApproval as b}from"../../core/providers/approval.js";import{signalInboxActivity as g}from"../../core/human/root.js";async function O(e){const o=await u(async n=>{const _=e.settled_at??new Date().toISOString(),t=await h(n,{request_id:e.request_id,outcome:e.outcome,result_json:e.result_json,settled_at:_});if(t===null){const c=await 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 i=typeof e.notification=="function"?await e.notification(t):e.notification;if(t.requester_node_id!==null&&i===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(i===void 0)throw new Error(`request ${t.request_id} needs a requester notification`);a=(await p(t.requester_node_id,{kind:"completed",label:i.label,senderName:"crtrd",...i.body_ref===void 0?{}:{bodyRef:i.body_ref},...i.body_json===void 0?{}:{bodyJson:i.body_json},disposition:i.disposition,urgency:"normal",wake:1})).deliveryId}t.action_json!==null&&e.completion_action!==void 0&&t.kind==="page"&&(s=await f({adapter:e.completion_action.adapter,bodyJson:e.completion_action.bodyJson,idempotencyKey:`human-action:${t.inbox_ticket_id}`})),t.submit_note_delivery_id!==null&&await w(t.submit_note_delivery_id),await j(n,t.request_id,{settlement_delivery_id:a,action_delivery_id:s});const r=await l(t.request_id);if(r===null)throw new Error(`settled human request disappeared: ${t.request_id}`);return await k(n,r),{outcome:"settled",request:r}});return o.outcome==="settled"&&(b(o.request.request_id),g(),e.post_commit!==void 0&&await E(o.request,e.post_commit)),o}d(O,"settleHumanRequest");async function E(e,o){if(e.kind==="review"&&e.projected_at===null&&o.project!==void 0)try{await o.project(e),await q(e.request_id,new Date().toISOString())}catch(n){await v(e.request_id,n instanceof Error?n.message:String(n))}if(e.companion_node_id!==null&&e.companion_retired_at===null&&o.retire!==void 0)try{await o.retire(e.companion_node_id,e.outcome==="canceled"||e.outcome==="dismissed"?"canceled":"approved"),await y(e.request_id,new Date().toISOString())}catch{}}d(E,"projectAndRetire");export{E as projectAndRetire,O as settleHumanRequest};
@@ -40,6 +40,16 @@ export declare function sweepStrayDaemons(keepPid: number | null): number[];
40
40
  /** Replace PID 1 with the selected daemon generation. Docker keeps a container
41
41
  * alive only while PID 1 lives, so a handover there must preserve its pid. */
42
42
  export declare function execDaemon(): Promise<never>;
43
+ export interface DaemonExportSqliteResult {
44
+ path: string;
45
+ rows: number;
46
+ ms: number;
47
+ userVersion: number;
48
+ }
49
+ /** Run the selected generation's `crtrd-cli export-sqlite` and wait for it. The export loads canvas database code, which
50
+ * a CLI process may not, so it runs in the daemon entry the way the daemon itself is launched. Throws with the child's
51
+ * stderr when it fails. */
52
+ export declare function runDaemonExportSqlite(): Promise<DaemonExportSqliteResult>;
43
53
  export interface SpawnDaemonOptions {
44
54
  /** The phase that proves a daemon launched by this call is available. */
45
55
  targetPhase?: 'ready' | 'prepared';
@@ -1,2 +1,3 @@
1
- var Y=Object.defineProperty;var o=(e,t)=>Y(e,"name",{value:t,configurable:!0});import{spawn as J,execFileSync as X}from"node:child_process";import{createRequire as V}from"node:module";import{dirname as b,join as _}from"node:path";import{fileURLToPath as q,pathToFileURL as k}from"node:url";import{existsSync as $,lstatSync as Q,mkdirSync as Z,openSync as C,closeSync as F,fstatSync as W,readSync as ee}from"node:fs";import{crtrHome as x}from"../core/canvas/paths.js";import{listProcessTable as te}from"../core/canvas/pid.js";import{bindDaemonEventSource as re,eventSource as ne}from"../core/events/source.js";import{hostExecPath as B}from"../core/runtime/branded-host.js";import{isDaemonRunning as oe,readPidfile as E,isPidAlive as f}from"./pidfile.js";import{DAEMON_STARTUP_WINDOW_MS as H}from"./startup-policy.js";import{exclusiveLockOwnerPid as ae}from"../core/exclusive-lock.js";import{onDiskMigrationLockPath as ie}from"../core/canvas/paths.js";import{envNoDaemonAutostart as se}from"../shared/env.js";import{awaitJoinLockRelease as ce,joinInProgress as ue,joinInProgressError as le,joinLockHolder as de}from"../core/directory/join-lock.js";import{waitForDaemonAvailability as fe}from"../api/index.js";import{localClient as pe}from"../api/node-transport.js";const me=["CRTR_NODE_ID","CRTR_KIND","CRTR_MODE","CRTR_LIFECYCLE","CRTR_NODE_CWD","CRTR_CONTEXT_DIR","CRTR_CYCLES","CRTR_PROFILE_ID","CRTR_PARENT_NODE_ID","CRTR_FRONT_DOOR","TMUX","TMUX_PANE","CRTR_BROKER_ENGINE","CRTR_PI_BINARY","NODE_OPTIONS"];function U(){const e={...process.env};for(const t of me)delete e[t];return e}o(U,"sanitizedDaemonEnv");function he(){for(let e=b(q(import.meta.url));;e=b(e)){if($(_(e,"bin","runtime-generation-manifest.mjs")))return e;if(b(e)===e)throw new Error(`cannot locate the crouter generation root from ${import.meta.url}`)}}o(he,"generationRoot");async function j(){const e=he(),t=k(_(e,"bin","runtime-generation-manifest.mjs")).href,{runtimeHome:r}=await import(t);try{Q(_(r(),"selected"))}catch(s){if(s.code!=="ENOENT")throw s;return["--conditions","crtr-src","--import",k(V(import.meta.url).resolve("tsx/esm")).href,_(e,"src","daemon","crtrd-cli.ts")]}const n=k(_(e,"bin","runtime-selector.mjs")).href,{tryResolveRuntime:a}=await import(n),c=a("daemon");if(!c.ok)throw new Error(`cannot resolve the selected runtime generation's daemon entry: ${c.error}`);return[c.runtime.entryPath]}o(j,"resolveDaemonLaunch");function we(e,t){return e.includes(` --canvas-home ${t} `)||e.endsWith(` --canvas-home ${t}`)}o(we,"daemonCommandDeclaresCanvasHome");function _e(){const e=te();if(e===null)return[];const t=x(),r=[];for(const{pid:n,command:a}of e)a.includes("daemon/crtrd")&&we(a,t)&&n!==process.pid&&r.push(n);return r}o(_e,"findDaemonPids");function Ee(e){const t=_e().filter(n=>n!==e);for(const n of t)try{process.kill(n,"SIGTERM")}catch{}if(t.length===0)return t;const r=Date.now()+2e3;for(;Date.now()<r&&t.some(n=>f(n));)X("sleep",["0.1"]);for(const n of t)if(f(n))try{process.kill(n,"SIGKILL")}catch{}return t}o(Ee,"sweepStrayDaemons");async function Ve(){const e=process.execve;if(e===void 0)throw new Error(`this Node.js ${process.version} runtime cannot replace PID 1 with the successor daemon`);const t=B(),r=await j();return e(t,[t,...r,"--canvas-home",x()],U())}o(Ve,"execDaemon");const De=3*6e4,K=2e4,M=10,O=1e3;async function I(e=O){const t=Math.min(e,O);return pe({autostart:!1,timeoutMs:t}).probeHealthz(t)}o(I,"probeDaemonHealth");function ye(e){const t=e instanceof Error?e.message:String(e);if(typeof e!="object"||e===null)return t;const r=e.code,n=e.cause,a=typeof r=="string"?r:n?.code;return typeof a=="string"&&!t.includes(a)?`${t}: ${a}`:t}o(ye,"probeErrorMessage");async function G(e=O){const t=await I(e);if(t.startup_blocked!==void 0){const r=E()??process.pid;throw new S(r,t.startup_blocked)}if(!t.ok)throw new Error("crtrd health check reported not ready")}o(G,"probeDaemonServing");async function qe(){try{return await G(),!0}catch{return!1}}o(qe,"isDaemonServing");function A(e){return new Promise(t=>{setTimeout(t,e)})}o(A,"sleepMs");class N extends Error{static{o(this,"DaemonExitedBeforeReadyError")}stderrTail;constructor(t,r){super(r===""?t:`${t}
2
- ${r}`),this.name="DaemonExitedBeforeReadyError",this.stderrTail=r}}class S extends Error{static{o(this,"DaemonStartupBlockedError")}constructor(t,r){super(`daemon ${t} is running but normal startup is blocked: ${r}`),this.name="DaemonStartupBlockedError"}}class P extends Error{static{o(this,"DaemonNotPreparedError")}constructor(t,r){super(r===void 0?`daemon ${t} is serving but did not report the prepared startup phase`:`daemon ${t} reported startup phase ${r}, not prepared`),this.name="DaemonNotPreparedError"}}async function ge(e,t=H,r={},n="ready"){const a=r.readPidfile??E,c=r.isPidAlive??f,s=r.childExited,l=r.isDaemonServing,D=r.probeDaemonServing??(async h=>{if(l!==void 0){if(await l())return;throw new Error("crtrd health check reported not ready")}await G(h)}),p=r.probeDaemonHealth??I,y=r.sleepMs??A,g=r.now??Date.now;let i=null,u=null;return await fe({windowMs:t,pollIntervalMs:M,now:g,sleep:y,retry:o(h=>h!==u,"retry"),probe:o(async h=>{const m=a();if(m===null||!c(m)){const d=s?.();throw d!=null&&(d.signal!==null?u=new N(`daemon ${e} exited before becoming ready (by ${d.signal})`,r.childStderrTail?.()??""):d.code!==0&&(u=new N(`daemon ${e} exited before becoming ready (with exit code ${d.code??"?"})`,r.childStderrTail?.()??"")),u!==null)?u:new Error(`daemon ${e} has no live pidfile owner`)}if(n==="ready"){try{await D(h)}catch(d){throw d instanceof S&&(u=d),d}i=m;return}const w=await p(h);if(w.startup_phase==="prepared"){i=m;return}throw w.startup_phase==="blocked"?(u=new S(m,w.startup_blocked??"on-disk migration is blocked"),u):w.startup_phase==="ready"?(u=new P(m,w.startup_phase),u):new Error(`daemon ${m} is ${w.startup_phase??"not reporting a startup phase"}; waiting for prepared`)},"probe")}),i===e?null:i}o(ge,"verifyDaemonStartup");async function xe(e,t=K,r={}){const n=r.readPidfile??E,a=r.isPidAlive??f,c=r.sleepMs??A,s=r.now??Date.now,l=s()+t;for(;s()<=l;){if(!a(e)&&n()!==e)return;await c(M)}throw new Error(`daemon ${e} did not exit and release its pidfile within ${t}ms`)}o(xe,"waitForDaemonExit");const Re=2e3,Se=5e3;async function z(e,t=K,r={}){const n=r.isPidAlive??f,a=r.sleepMs??A,c=r.now??Date.now;try{process.kill(e,"SIGTERM")}catch{}try{return await xe(e,t,r),"terminated"}catch{}try{process.kill(e,"SIGKILL")}catch{}const s=c()+Re;for(;c()<=s;){if(!n(e))return"killed";await a(M)}throw new Error(`daemon ${e} survived both SIGTERM and SIGKILL`)}o(z,"stopDaemonProcess");async function Qe(){const e=E(),t=e!==null&&f(e),r=t&&await z(e)==="killed",n=Ee(null);return{stopped:t||n.length>0,...t?{pid:e}:{},strays_killed:n,force_killed:r}}o(Qe,"stopAllDaemons");function Te(e,t,r=2048){let n;try{n=C(e,"r");const{size:a}=W(n),c=Math.max(t,a-r),s=a-c;if(s<=0)return"";const l=Buffer.alloc(s);return ee(n,l,0,s,c),l.toString("utf8").trim()}catch{return""}finally{if(n!==void 0)try{F(n)}catch{}}}o(Te,"readLogTailFrom");async function ve(e={}){if(process.platform==="linux"&&$("/run/crtr/launcher.sock")&&!f(E()??-1))throw new Error("crtrd is managed by the root launcher; it cannot self-daemonize");const t=e.targetPhase??"ready",r=E();if(r!==null&&f(r))try{const i=await I();if(t==="prepared"){if(i.startup_phase==="prepared")return{started:!1,existing_pid:r,startup_phase:i.startup_phase};throw new P(r,i.startup_phase)}return i.ok?{started:!1,existing_pid:r,...i.startup_phase===void 0?{}:{startup_phase:i.startup_phase}}:{started:!1,existing_pid:r,running:!0,serving:!1,probe_error:i.startup_blocked??"crtrd health check reported not ready",...i.startup_phase===void 0?{}:{startup_phase:i.startup_phase}}}catch(i){if(i instanceof P)throw i;return{started:!1,existing_pid:r,running:!0,serving:!1,probe_error:ye(i)}}Z(x(),{recursive:!0});const n=await j();e.waitForJoinLock===!0&&await ce(De);const a=de();if(a!==null&&a!==process.pid&&e.ignoreJoinLock!==!0)throw le(a);const c=_(x(),"crtrd.err"),s=C(c,"a"),l=(()=>{try{return W(s).size}catch{return 0}})();ne()===void 0&&re();const D=J(B(),[...n,"--canvas-home",x(),...t==="prepared"?["--prepare"]:[]],{detached:!0,stdio:["ignore",s,s],env:U()});F(s);const p=D.pid;if(p===void 0)throw new Error("daemon spawn did not return a pid");let y=null;D.once("exit",(i,u)=>{y={code:i,signal:u}}),D.unref();let g;try{g=await ge(p,H,{childExited:o(()=>y,"childExited"),childStderrTail:o(()=>Te(c,l),"childStderrTail")},t)}catch(i){const u=ae(ie())===p;if(y===null&&!u&&!(i instanceof S))try{await z(p,Se)}catch{}throw i}return g!==null?{started:!1,existing_pid:g,startup_phase:t}:{started:!0,pid:p,startup_phase:t}}o(ve,"spawnDaemon");function be(){return se()||process.argv.includes("--no-autostart")}o(be,"autostartDisabled");const ke=5e3,Me=6e4;let L=!1,R,T=0,v=0;function Oe(e){return Math.min(ke*2**(e-1),Me)}o(Oe,"autostartBackoffMs");function Ie(){R=void 0,T=0,v=0}o(Ie,"resetDaemonAutostart");function Ze(){return R}o(Ze,"daemonAutostartStartupFailure");function et(e={}){const t=e.isDaemonRunning??oe,r=e.spawnDaemon??ve,n=e.now??Date.now;if(!be()&&!ue()&&!L&&!(n()<v)){if(t()){Ie();return}R=void 0,L=!0,r().then(()=>{R=void 0,T=0,v=0},a=>{a instanceof N&&(R=a.message),T+=1,v=n()+Oe(T)}).finally(()=>{L=!1})}}o(et,"ensureDaemon");export{me as DAEMON_ENV_STRIP_KEYS,N as DaemonExitedBeforeReadyError,P as DaemonNotPreparedError,S as DaemonStartupBlockedError,be as autostartDisabled,Ze as daemonAutostartStartupFailure,we as daemonCommandDeclaresCanvasHome,et as ensureDaemon,Ve as execDaemon,_e as findDaemonPids,qe as isDaemonServing,I as probeDaemonHealth,G as probeDaemonServing,ye as probeErrorMessage,Ie as resetDaemonAutostart,U as sanitizedDaemonEnv,ve as spawnDaemon,Qe as stopAllDaemons,z as stopDaemonProcess,Ee as sweepStrayDaemons,ge as verifyDaemonStartup,xe as waitForDaemonExit};
1
+ var Y=Object.defineProperty;var o=(e,t)=>Y(e,"name",{value:t,configurable:!0});import{spawn as W,execFileSync as X}from"node:child_process";import{createRequire as q}from"node:module";import{dirname as b,join as E}from"node:path";import{fileURLToPath as V,pathToFileURL as k}from"node:url";import{existsSync as B,lstatSync as Q,mkdirSync as Z,openSync as H,closeSync as U,fstatSync as j,readSync as ee}from"node:fs";import{crtrHome as g}from"../core/canvas/paths.js";import{listProcessTable as te}from"../core/canvas/pid.js";import{bindDaemonEventSource as re,eventSource as ne}from"../core/events/source.js";import{hostExecPath as O}from"../core/runtime/branded-host.js";import{isDaemonRunning as oe,readPidfile as D,isPidAlive as p}from"./pidfile.js";import{DAEMON_STARTUP_WINDOW_MS as K}from"./startup-policy.js";import{exclusiveLockOwnerPid as ae}from"../core/exclusive-lock.js";import{onDiskMigrationLockPath as ie}from"../core/canvas/paths.js";import{envNoDaemonAutostart as se}from"../shared/env.js";import{awaitJoinLockRelease as ce,joinInProgress as ue,joinInProgressError as le,joinLockHolder as de}from"../core/directory/join-lock.js";import{waitForDaemonAvailability as pe}from"../api/index.js";import{localClient as fe}from"../api/node-transport.js";const me=["CRTR_NODE_ID","CRTR_KIND","CRTR_MODE","CRTR_LIFECYCLE","CRTR_NODE_CWD","CRTR_CONTEXT_DIR","CRTR_CYCLES","CRTR_PROFILE_ID","CRTR_PARENT_NODE_ID","CRTR_FRONT_DOOR","TMUX","TMUX_PANE","CRTR_BROKER_ENGINE","CRTR_PI_BINARY","NODE_OPTIONS"];function M(){const e={...process.env};for(const t of me)delete e[t];return e}o(M,"sanitizedDaemonEnv");function he(){for(let e=b(V(import.meta.url));;e=b(e)){if(B(E(e,"bin","runtime-generation-manifest.mjs")))return e;if(b(e)===e)throw new Error(`cannot locate the crouter generation root from ${import.meta.url}`)}}o(he,"generationRoot");async function A(){const e=he(),t=k(E(e,"bin","runtime-generation-manifest.mjs")).href,{runtimeHome:r}=await import(t);try{Q(E(r(),"selected"))}catch(i){if(i.code!=="ENOENT")throw i;return["--conditions","crtr-src","--import",k(q(import.meta.url).resolve("tsx/esm")).href,E(e,"src","daemon","crtrd-cli.ts")]}const n=k(E(e,"bin","runtime-selector.mjs")).href,{tryResolveRuntime:a}=await import(n),c=a("daemon");if(!c.ok)throw new Error(`cannot resolve the selected runtime generation's daemon entry: ${c.error}`);return[c.runtime.entryPath]}o(A,"resolveDaemonLaunch");function we(e,t){return e.includes(` --canvas-home ${t} `)||e.endsWith(` --canvas-home ${t}`)}o(we,"daemonCommandDeclaresCanvasHome");function _e(){const e=te();if(e===null)return[];const t=g(),r=[];for(const{pid:n,command:a}of e)a.includes("daemon/crtrd")&&we(a,t)&&n!==process.pid&&r.push(n);return r}o(_e,"findDaemonPids");function Ee(e){const t=_e().filter(n=>n!==e);for(const n of t)try{process.kill(n,"SIGTERM")}catch{}if(t.length===0)return t;const r=Date.now()+2e3;for(;Date.now()<r&&t.some(n=>p(n));)X("sleep",["0.1"]);for(const n of t)if(p(n))try{process.kill(n,"SIGKILL")}catch{}return t}o(Ee,"sweepStrayDaemons");async function qe(){const e=process.execve;if(e===void 0)throw new Error(`this Node.js ${process.version} runtime cannot replace PID 1 with the successor daemon`);const t=O(),r=await A();return e(t,[t,...r,"--canvas-home",g()],M())}o(qe,"execDaemon");async function Ve(){const e=await A(),t=W(O(),[...e,"export-sqlite"],{stdio:["ignore","pipe","pipe"],env:M()});let r="",n="";t.stdout.setEncoding("utf8").on("data",i=>{r+=i}),t.stderr.setEncoding("utf8").on("data",i=>{n+=i});const a=await new Promise((i,l)=>{t.once("error",l),t.once("close",f=>i(f))});if(a!==0)throw new Error(n.trim()||`the export process exited with code ${a}`);const c=r.trim().split(`
2
+ `).at(-1)??"";return JSON.parse(c)}o(Ve,"runDaemonExportSqlite");const De=3*6e4,G=2e4,I=10,N=1e3;async function P(e=N){const t=Math.min(e,N);return fe({autostart:!1,timeoutMs:t}).probeHealthz(t)}o(P,"probeDaemonHealth");function ye(e){const t=e instanceof Error?e.message:String(e);if(typeof e!="object"||e===null)return t;const r=e.code,n=e.cause,a=typeof r=="string"?r:n?.code;return typeof a=="string"&&!t.includes(a)?`${t}: ${a}`:t}o(ye,"probeErrorMessage");async function z(e=N){const t=await P(e);if(t.startup_phase==="blocked"){const r=D()??process.pid;throw new R(r,t.startup_blocked??"daemon startup is blocked")}if(!t.ok)throw new Error(t.startup_blocked??"crtrd health check reported not ready")}o(z,"probeDaemonServing");async function Qe(){try{return await z(),!0}catch{return!1}}o(Qe,"isDaemonServing");function L(e){return new Promise(t=>{setTimeout(t,e)})}o(L,"sleepMs");class $ extends Error{static{o(this,"DaemonExitedBeforeReadyError")}stderrTail;constructor(t,r){super(r===""?t:`${t}
3
+ ${r}`),this.name="DaemonExitedBeforeReadyError",this.stderrTail=r}}class R extends Error{static{o(this,"DaemonStartupBlockedError")}constructor(t,r){super(`daemon ${t} is running but normal startup is blocked: ${r}`),this.name="DaemonStartupBlockedError"}}class C extends Error{static{o(this,"DaemonNotPreparedError")}constructor(t,r){super(r===void 0?`daemon ${t} is serving but did not report the prepared startup phase`:`daemon ${t} reported startup phase ${r}, not prepared`),this.name="DaemonNotPreparedError"}}async function xe(e,t=K,r={},n="ready"){const a=r.readPidfile??D,c=r.isPidAlive??p,i=r.childExited,l=r.isDaemonServing,f=r.probeDaemonServing??(async w=>{if(l!==void 0){if(await l())return;throw new Error("crtrd health check reported not ready")}await z(w)}),m=r.probeDaemonHealth??P,y=r.sleepMs??L,x=r.now??Date.now;let s=null,u=null;return await pe({windowMs:t,pollIntervalMs:I,now:x,sleep:y,retry:o(w=>w!==u,"retry"),probe:o(async w=>{const h=a();if(h===null||!c(h)){const d=i?.();throw d!=null&&(d.signal!==null?u=new $(`daemon ${e} exited before becoming ready (by ${d.signal})`,r.childStderrTail?.()??""):d.code!==0&&(u=new $(`daemon ${e} exited before becoming ready (with exit code ${d.code??"?"})`,r.childStderrTail?.()??"")),u!==null)?u:new Error(`daemon ${e} has no live pidfile owner`)}if(n==="ready"){try{await f(w)}catch(d){throw d instanceof R&&(u=d),d}s=h;return}const _=await m(w);if(_.startup_phase==="prepared"){s=h;return}throw _.startup_phase==="blocked"?(u=new R(h,_.startup_blocked??"on-disk migration is blocked"),u):_.startup_phase==="ready"?(u=new C(h,_.startup_phase),u):new Error(`daemon ${h} is ${_.startup_phase??"not reporting a startup phase"}; waiting for prepared`)},"probe")}),s===e?null:s}o(xe,"verifyDaemonStartup");async function ge(e,t=G,r={}){const n=r.readPidfile??D,a=r.isPidAlive??p,c=r.sleepMs??L,i=r.now??Date.now,l=i()+t;for(;i()<=l;){if(!a(e)&&n()!==e)return;await c(I)}throw new Error(`daemon ${e} did not exit and release its pidfile within ${t}ms`)}o(ge,"waitForDaemonExit");const Se=2e3,Re=5e3;async function J(e,t=G,r={}){const n=r.isPidAlive??p,a=r.sleepMs??L,c=r.now??Date.now;try{process.kill(e,"SIGTERM")}catch{}try{return await ge(e,t,r),"terminated"}catch{}try{process.kill(e,"SIGKILL")}catch{}const i=c()+Se;for(;c()<=i;){if(!n(e))return"killed";await a(I)}throw new Error(`daemon ${e} survived both SIGTERM and SIGKILL`)}o(J,"stopDaemonProcess");async function Ze(){const e=D(),t=e!==null&&p(e),r=t&&await J(e)==="killed",n=Ee(null);return{stopped:t||n.length>0,...t?{pid:e}:{},strays_killed:n,force_killed:r}}o(Ze,"stopAllDaemons");function ve(e,t,r=2048){let n;try{n=H(e,"r");const{size:a}=j(n),c=Math.max(t,a-r),i=a-c;if(i<=0)return"";const l=Buffer.alloc(i);return ee(n,l,0,i,c),l.toString("utf8").trim()}catch{return""}finally{if(n!==void 0)try{U(n)}catch{}}}o(ve,"readLogTailFrom");async function Te(e={}){if(process.platform==="linux"&&B("/run/crtr/launcher.sock")&&!p(D()??-1))throw new Error("crtrd is managed by the root launcher; it cannot self-daemonize");const t=e.targetPhase??"ready",r=D();if(r!==null&&p(r))try{const s=await P();if(t==="prepared"){if(s.startup_phase==="prepared")return{started:!1,existing_pid:r,startup_phase:s.startup_phase};throw new C(r,s.startup_phase)}return s.ok?{started:!1,existing_pid:r,...s.startup_phase===void 0?{}:{startup_phase:s.startup_phase}}:{started:!1,existing_pid:r,running:!0,serving:!1,probe_error:s.startup_blocked??"crtrd health check reported not ready",...s.startup_phase===void 0?{}:{startup_phase:s.startup_phase}}}catch(s){if(s instanceof C)throw s;return{started:!1,existing_pid:r,running:!0,serving:!1,probe_error:ye(s)}}Z(g(),{recursive:!0});const n=await A();e.waitForJoinLock===!0&&await ce(De);const a=de();if(a!==null&&a!==process.pid&&e.ignoreJoinLock!==!0)throw le(a);const c=E(g(),"crtrd.err"),i=H(c,"a"),l=(()=>{try{return j(i).size}catch{return 0}})();ne()===void 0&&re();const f=W(O(),[...n,"--canvas-home",g(),...t==="prepared"?["--prepare"]:[]],{detached:!0,stdio:["ignore",i,i],env:M()});U(i);const m=f.pid;if(m===void 0)throw new Error("daemon spawn did not return a pid");let y=null;f.once("exit",(s,u)=>{y={code:s,signal:u}}),f.unref();let x;try{x=await xe(m,K,{childExited:o(()=>y,"childExited"),childStderrTail:o(()=>ve(c,l),"childStderrTail")},t)}catch(s){const u=ae(ie())===m;if(y===null&&!u&&!(s instanceof R))try{await J(m,Re)}catch{}throw s}return x!==null?{started:!1,existing_pid:x,startup_phase:t}:{started:!0,pid:m,startup_phase:t}}o(Te,"spawnDaemon");function be(){return se()||process.argv.includes("--no-autostart")}o(be,"autostartDisabled");const ke=5e3,Oe=6e4;let F=!1,S,v=0,T=0;function Me(e){return Math.min(ke*2**(e-1),Oe)}o(Me,"autostartBackoffMs");function Ae(){S=void 0,v=0,T=0}o(Ae,"resetDaemonAutostart");function et(){return S}o(et,"daemonAutostartStartupFailure");function tt(e={}){const t=e.isDaemonRunning??oe,r=e.spawnDaemon??Te,n=e.now??Date.now;if(!be()&&!ue()&&!F&&!(n()<T)){if(t()){Ae();return}S=void 0,F=!0,r().then(()=>{S=void 0,v=0,T=0},a=>{a instanceof $&&(S=a.message),v+=1,T=n()+Me(v)}).finally(()=>{F=!1})}}o(tt,"ensureDaemon");export{me as DAEMON_ENV_STRIP_KEYS,$ as DaemonExitedBeforeReadyError,C as DaemonNotPreparedError,R as DaemonStartupBlockedError,be as autostartDisabled,et as daemonAutostartStartupFailure,we as daemonCommandDeclaresCanvasHome,tt as ensureDaemon,qe as execDaemon,_e as findDaemonPids,Qe as isDaemonServing,P as probeDaemonHealth,z as probeDaemonServing,ye as probeErrorMessage,Ae as resetDaemonAutostart,Ve as runDaemonExportSqlite,M as sanitizedDaemonEnv,Te as spawnDaemon,Ze as stopAllDaemons,J as stopDaemonProcess,Ee as sweepStrayDaemons,xe as verifyDaemonStartup,ge as waitForDaemonExit};
@@ -46,6 +46,13 @@ export declare const withMachine: (body: unknown, machine: MachineLabel) => unkn
46
46
  * machine's id only for logging; it is the same call whatever machine answers.
47
47
  */
48
48
  export declare function forwardVerb(ctx: RequestContext, id: string, request: PeerRequest): Promise<ForwardOutcome>;
49
+ /**
50
+ * Forward `request` for a ref that is a name rather than an id: no local object or stub holds it, and the id-keyed home resolver cannot
51
+ * place it. Every other machine of the account is probed with the read-only `probe` (a GET of the object); the machine that answers below
52
+ * 400 homes it, and `request` goes to that one machine (a GET `request` is its own probe). A `404` is a miss; any other refusal or error
53
+ * says nothing about whether the machine holds the name (as in `locate`), so with no hit it makes the answer `unknown`.
54
+ */
55
+ export declare function forwardNamed(ctx: RequestContext, request: PeerRequest, probe: PeerRequest): Promise<ForwardOutcome>;
49
56
  /** The envelope a forwarded call carried, rebuilt from its verified principal. */
50
57
  export declare function envelopeOfForwarded(principal: Extract<RequestPrincipal, {
51
58
  class: 'forwarded';
@@ -1 +1 @@
1
- var _=Object.defineProperty;var t=(e,n)=>_(e,"name",{value:n,configurable:!0});import{ApiError as p}from"../../api/errors.js";import{openDb as v}from"../../core/canvas/db.js";import{getById as x,nodeObject as $}from"../../core/graph/objects.js";import{envelopeFor as g}from"../../core/peer/envelope.js";import{isUnreachable as j}from"../../core/peer/client.js";import{machineReach as D}from"../../core/peer/directory.js";import{defaultHomeDeps as O,homeOf as S,relocate as q}from"../../core/peer/home.js";import{ensureStub as y,rehomeStub as A,stubOf as E}from"../../core/peer/stubs.js";import{peerCall as F}from"../../core/peer/client.js";const C={...O,call:t((e,n,o,a)=>F(e,n,{...o===void 0?{}:{envelope:o},...a===void 0?{}:{signal:a}}),"call"),envelope:t(async e=>await g(e),"envelope"),label:t(async e=>{try{const n=await D(e);return{id:e,name:n?.name??e}}catch{return{id:e,name:e}}},"label")},M={current:C},N=t(e=>e.class==="person"||e.class==="broker"||e.class==="cron","originates"),m=t(e=>{const n=e?.error;return typeof n=="object"&&n!==null&&typeof n.code=="string"?n.code:null},"errorCode");function L(e,n){const o=n?.error;return new p(e,o?.code??"error",o?.message??`the home machine answered ${e}`,o?.details)}t(L,"relayError");function P(e,n){return new p(503,"runtime_unreachable",`No machine of yours answers for ${e} (unreachable: ${n.join(", ")}).`,{id:e,unreachable:n})}t(P,"unknownError");const Q=t((e,n)=>e!==null&&typeof e=="object"&&!Array.isArray(e)?{...e,machine:n}:e,"withMachine");async function T(e,n,o){const a=M.current,r=await v(),u=await x(r,n),i=u===null?null:await E(r,u),w=e.principal;if(!N(w)){if(i!==null)throw new p(404,"not_here",`${n} is not homed on this machine (it is a stub of ${i.home_machine})`,{id:n,home_machine:i.home_machine});return{kind:"local"}}if(u!==null&&i===null)return{kind:"local"};const b={...a,call:t((s,d)=>a.call(s,d),"call")};let l=await S(r,n,{hint:e.query.get("machine"),deps:b});for(let s=0;s<2;s++){if(l.where==="nowhere"||l.where==="here"){if(i!==null)throw new p(404,"not_found",`${n} is no longer on any machine of yours`,{id:n});return{kind:"local"}}if(l.where==="unknown")return{kind:"unknown",unreachable:l.unreachable};const d=l.machine,c=await a.call(d,o,await a.envelope(w)),f=await a.label(d);if(j(c))return{kind:"unknown",unreachable:[`${f.name} (${c.unreachable.reason})`]};const k=l.source!=="locate";if(c.status===404&&s===0&&(m(c.body)==="not_here"||k&&m(c.body)==="not_found")){const h=await q(r,n,d,b);if(h.where==="machine"){l=h;continue}if(h.where==="unknown")return{kind:"unknown",unreachable:h.unreachable};if(m(c.body)==="not_found")return{kind:"remote",status:c.status,body:c.body,machine:f};if(i!==null)throw new p(404,"not_found",`${n} is no longer on any machine of yours`,{id:n});return{kind:"local"}}return{kind:"remote",status:c.status,body:c.body,machine:f}}return{kind:"unknown",unreachable:[]}}t(T,"forwardVerb");function W(e){if(e.actor.kind==="person")return{sub:e.sub,act:{sub:e.via}};const{app:n,node:o,node_scopes:a,place:r}=e.actor;return{sub:e.sub,act:{sub:e.via,act:{sub:n,node:o,node_scopes:a,place:r}}}}t(W,"envelopeOfForwarded");function X(e){const n={};for(const[o,a]of e.query)o!=="machine"&&(n[o]=a);return n}t(X,"queryWithoutMachine");async function Y(e,n){const o=n.principal;return o.class!=="forwarded"||o.actor.kind!=="app"||o.actor.node===null?null:await y(e,o.actor.node,"node",o.via,W(o))}t(Y,"callerStub");async function Z(e,n){return n.principal.class==="broker"?await $(e,n.principal.node):null}t(Z,"callerNode");async function ee(e,n,o,a,r){await A(e,n,a);const u=r?.owner,i=u&&u.type==="app"?{id:"",type:"app",scheme:"app",external_id:u.external_id??(u.handle.startsWith("app:")?u.handle:`app:${u.handle}`),handle:u.handle}:null;return await y(e,n,o,a,void 0,r===void 0?void 0:{id:n,type:o,kind:r.kind??null,name:r.name??n,...r.preview===void 0?{}:{preview:r.preview},owner:i,...r.kind_decl===void 0?{}:{kind_decl:r.kind_decl},...r.privacy===void 0?{}:{privacy:r.privacy}})}t(ee,"remoteStub");export{Z as callerNode,Y as callerStub,C as defaultForwardDeps,W as envelopeOfForwarded,T as forwardVerb,M as forwarding,N as originates,X as queryWithoutMachine,L as relayError,ee as remoteStub,P as unknownError,Q as withMachine};
1
+ var $=Object.defineProperty;var i=(n,e)=>$(n,"name",{value:e,configurable:!0});import{ApiError as m}from"../../api/errors.js";import{openDb as g}from"../../core/canvas/db.js";import{getById as j,nodeObject as A}from"../../core/graph/objects.js";import{envelopeFor as D}from"../../core/peer/envelope.js";import{isUnreachable as w}from"../../core/peer/client.js";import{machineReach as E}from"../../core/peer/directory.js";import{defaultHomeDeps as O,homeOf as S,peersToAsk as F,relocate as N}from"../../core/peer/home.js";import{ensureStub as y,rehomeStub as q,stubOf as C}from"../../core/peer/stubs.js";import{peerCall as M}from"../../core/peer/client.js";const T={...O,call:i((n,e,o,t)=>M(n,e,{...o===void 0?{}:{envelope:o},...t===void 0?{}:{signal:t}}),"call"),envelope:i(async n=>await D(n),"envelope"),label:i(async n=>{try{const e=await E(n);return{id:n,name:e?.name??n}}catch{return{id:n,name:n}}},"label")},_={current:T},v=i(n=>n.class==="person"||n.class==="broker"||n.class==="cron","originates"),k=i(n=>{const e=n?.error;return typeof e=="object"&&e!==null&&typeof e.code=="string"?e.code:null},"errorCode");function K(n,e){const o=e?.error;return new m(n,o?.code??"error",o?.message??`the home machine answered ${n}`,o?.details)}i(K,"relayError");function L(n,e){return new m(503,"runtime_unreachable",`No machine of yours answers for ${n} (unreachable: ${e.join(", ")}).`,{id:n,unreachable:e})}i(L,"unknownError");const Q=i((n,e)=>n!==null&&typeof n=="object"&&!Array.isArray(n)?{...n,machine:e}:n,"withMachine");async function X(n,e,o){const t=_.current,a=await g(),c=await j(a,e),u=c===null?null:await C(a,c),p=n.principal;if(!v(p)){if(u!==null)throw new m(404,"not_here",`${e} is not homed on this machine (it is a stub of ${u.home_machine})`,{id:e,home_machine:u.home_machine});return{kind:"local"}}if(c!==null&&u===null)return{kind:"local"};const h={...t,call:i((r,d)=>t.call(r,d),"call")};let l=await S(a,e,{hint:n.query.get("machine"),deps:h});for(let r=0;r<2;r++){if(l.where==="nowhere"||l.where==="here"){if(u!==null)throw new m(404,"not_found",`${e} is no longer on any machine of yours`,{id:e});return{kind:"local"}}if(l.where==="unknown")return{kind:"unknown",unreachable:l.unreachable};const d=l.machine,s=await t.call(d,o,await t.envelope(p)),b=await t.label(d);if(w(s))return{kind:"unknown",unreachable:[`${b.name} (${s.unreachable.reason})`]};const x=l.source!=="locate";if(s.status===404&&r===0&&(k(s.body)==="not_here"||x&&k(s.body)==="not_found")){const f=await N(a,e,d,h);if(f.where==="machine"){l=f;continue}if(f.where==="unknown")return{kind:"unknown",unreachable:f.unreachable};if(k(s.body)==="not_found")return{kind:"remote",status:s.status,body:s.body,machine:b};if(u!==null)throw new m(404,"not_found",`${e} is no longer on any machine of yours`,{id:e});return{kind:"local"}}return{kind:"remote",status:s.status,body:s.body,machine:b}}return{kind:"unknown",unreachable:[]}}i(X,"forwardVerb");async function Y(n,e,o){const t=_.current,a=n.principal;if(!v(a))return{kind:"local"};const c=await F(t);if("where"in c)return{kind:"unknown",unreachable:c.unreachable};if(c.length===0)return{kind:"local"};const u=await t.envelope(a),p=await Promise.all(c.map(async r=>({m:r,out:await t.call(r.machine_id,o,u)}))),h=p.find(r=>!w(r.out)&&r.out.status<400);if(h){const r=e.method==="GET"?h.out:await t.call(h.m.machine_id,e,u),d=await t.label(h.m.machine_id);return w(r)?{kind:"unknown",unreachable:[`${d.name} (${r.unreachable.reason})`]}:{kind:"remote",status:r.status,body:r.body,machine:d}}const l=p.filter(r=>w(r.out)||r.out.status!==404).map(r=>r.m.name||r.m.machine_id);return l.length>0?{kind:"unknown",unreachable:l}:{kind:"local"}}i(Y,"forwardNamed");function W(n){if(n.actor.kind==="person")return{sub:n.sub,act:{sub:n.via}};const{app:e,node:o,node_scopes:t,place:a}=n.actor;return{sub:n.sub,act:{sub:n.via,act:{sub:e,node:o,node_scopes:t,place:a}}}}i(W,"envelopeOfForwarded");function Z(n){const e={};for(const[o,t]of n.query)o!=="machine"&&(e[o]=t);return e}i(Z,"queryWithoutMachine");async function nn(n,e){const o=e.principal;return o.class!=="forwarded"||o.actor.kind!=="app"||o.actor.node===null?null:await y(n,o.actor.node,"node",o.via,W(o))}i(nn,"callerStub");async function en(n,e){return e.principal.class==="broker"?await A(n,e.principal.node):null}i(en,"callerNode");async function on(n,e,o,t,a){await q(n,e,t);const c=a?.owner,u=c&&c.type==="app"?{id:"",type:"app",scheme:"app",external_id:c.external_id??(c.handle.startsWith("app:")?c.handle:`app:${c.handle}`),handle:c.handle}:null;return await y(n,e,o,t,void 0,a===void 0?void 0:{id:e,type:o,kind:a.kind??null,name:a.name??e,...a.preview===void 0?{}:{preview:a.preview},owner:u,...a.kind_decl===void 0?{}:{kind_decl:a.kind_decl},...a.privacy===void 0?{}:{privacy:a.privacy}})}i(on,"remoteStub");export{en as callerNode,nn as callerStub,T as defaultForwardDeps,W as envelopeOfForwarded,Y as forwardNamed,X as forwardVerb,_ as forwarding,v as originates,Z as queryWithoutMachine,K as relayError,on as remoteStub,L as unknownError,Q as withMachine};
@@ -0,0 +1,17 @@
1
+ export interface PostgresBinaries {
2
+ /** Directory holding `bin/`, `lib/` and `share/`. */
3
+ root: string;
4
+ initdb: string;
5
+ postgres: string;
6
+ pgCtl: string;
7
+ }
8
+ /** The jar artifact suffix for this host. Linux picks the musl build on Alpine-style hosts. */
9
+ export declare function hostJarSuffix(): string;
10
+ /** `$XDG_CACHE_HOME/crouter/postgres/<version>/`, the same shared cache as the termrender pin. */
11
+ export declare function postgresCacheDir(version?: string): string;
12
+ /**
13
+ * Resolve the Postgres binaries: `CRTR_POSTGRES_HOME` names a pre-unpacked tree (the CI image bakes one); otherwise
14
+ * the pinned jar is downloaded from Maven Central, sha256-verified, unpacked and slimmed into the version-keyed shared
15
+ * cache, once, under a lock. A later call finds the ready stamp and does no work.
16
+ */
17
+ export declare function ensurePostgresBinaries(log?: (message: string) => void): Promise<PostgresBinaries>;
@@ -0,0 +1,2 @@
1
+ var M=Object.defineProperty;var f=(r,s)=>M(r,"name",{value:s,configurable:!0});import{spawnSync as g}from"node:child_process";import{createHash as _}from"node:crypto";import{createWriteStream as O,existsSync as b,mkdirSync as S,readFileSync as P,readdirSync as U,renameSync as k,rmSync as y,lstatSync as C,symlinkSync as D,unlinkSync as H,writeFileSync as I,openSync as A,readSync as j,closeSync as F}from"node:fs";import{homedir as G}from"node:os";import{basename as $,dirname as x,join as c,resolve as R}from"node:path";import{Readable as W}from"node:stream";import{pipeline as B}from"node:stream/promises";import{inflateRawSync as J}from"node:zlib";import{withExclusiveDirectoryLockAsync as N}from"../../core/exclusive-lock.js";import{POSTGRES_JAR_SHA256 as V,POSTGRES_VERSION as v,jarUrl as X}from"./pins.js";const T=".crtr-postgres-ready";function Y(){const r=process.arch==="arm64"?"arm64v8":process.arch==="x64"?"amd64":null;if(r===null)throw new Error(`no pinned Postgres build for CPU architecture ${process.arch}`);if(process.platform==="darwin")return`darwin-${r}`;if(process.platform==="linux"){const s=U("/lib").some(t=>t.startsWith("ld-musl-"));return`linux-${r}${s?"-alpine":""}`}throw new Error(`no pinned Postgres build for platform ${process.platform}`)}f(Y,"hostJarSuffix");function Z(r=v){return c(R(process.env.XDG_CACHE_HOME||c(G(),".cache")),"crouter","postgres",r)}f(Z,"postgresCacheDir");function L(r){return{root:r,initdb:c(r,"bin","initdb"),postgres:c(r,"bin","postgres"),pgCtl:c(r,"bin","pg_ctl")}}f(L,"describe");function q(r,s){let t=-1;for(let e=r.length-22;e>=Math.max(0,r.length-65557);e--)if(r.readUInt32LE(e)===101010256){t=e;break}if(t<0)throw new Error("Postgres jar is not a zip file");let n=r.readUInt32LE(t+16);const a=r.readUInt16LE(t+10);for(let e=0;e<a;e++){if(r.readUInt32LE(n)!==33639248)throw new Error("corrupt zip central directory");const i=r.readUInt16LE(n+10),o=r.readUInt32LE(n+20),l=r.readUInt16LE(n+28),m=r.readUInt16LE(n+30),E=r.readUInt16LE(n+32),d=r.readUInt32LE(n+42),u=r.toString("utf8",n+46,n+46+l);if(n+=46+l+m+E,!s(u))continue;const h=d+30+r.readUInt16LE(d+26)+r.readUInt16LE(d+28),w=r.subarray(h,h+o);if(i===0)return{name:u,data:w};if(i===8)return{name:u,data:J(w)};throw new Error(`unsupported zip compression method ${i}`)}throw new Error("Postgres jar holds no .txz archive")}f(q,"extractZipEntry");async function K(r,s,t){const n=await fetch(r,{signal:AbortSignal.timeout(3e5)});if(!n.ok||n.body===null)throw new Error(`downloading ${r} failed: HTTP ${n.status}`);const a=_("sha256"),e=W.fromWeb(n.body);e.on("data",o=>a.update(o)),await B(e,O(t));const i=a.digest("hex");if(i!==s)throw y(t,{force:!0}),new Error(`${r} has sha256 ${i}, expected the pinned ${s}; refusing to unpack it`)}f(K,"download");function Q(r){const s=A(r,"r");try{const t=Buffer.alloc(4);return j(s,t,0,4,0)===4&&t.readUInt32BE(0)===3405691582}finally{F(s)}}f(Q,"isFatMachO");function p(r,s=[]){for(const t of U(r,{withFileTypes:!0})){const n=c(r,t.name);t.isDirectory()?p(n,s):t.isFile()&&s.push(n)}return s}f(p,"walkFiles");function z(r){const s=process.arch==="arm64"?"arm64":"x86_64",t=[...p(c(r,"bin")),...p(c(r,"lib"))];for(const e of t){if(!Q(e))continue;const i=`${e}.thin`,o=g("lipo",["-thin",s,"-output",i,e],{encoding:"utf8"});if(o.status!==0)throw new Error(`lipo -thin ${s} ${e} failed: ${o.stderr}`);k(i,e)}const n=new Map;for(const e of p(c(r,"lib"))){const i=_("sha256").update(P(e)).digest("hex");n.set(i,[...n.get(i)??[],e])}for(const e of n.values()){if(e.length<2)continue;const i=e.reduce((o,l)=>$(l).length>$(o).length?l:o);for(const o of e)o===i||x(o)!==x(i)||(H(o),D($(i),o))}if(g("codesign",["--verify",c(r,"bin","postgres")],{encoding:"utf8"}).status!==0)for(const e of[...p(c(r,"bin")),...p(c(r,"lib")).filter(i=>!C(i).isSymbolicLink())])g("codesign",["--force","--sign","-",e],{encoding:"utf8"})}f(z,"slimDarwinTree");async function lr(r=()=>{}){const s=process.env.CRTR_POSTGRES_HOME;if(s){const o=L(R(s));if(!b(o.postgres))throw new Error(`CRTR_POSTGRES_HOME=${s} has no bin/postgres`);return o}const t=Z(),n=c(t,T);if(b(n))return L(t);const a=Y(),e=V[a];if(e===void 0)throw new Error(`no sha256 pinned for Postgres build ${a}`);S(x(t),{recursive:!0});const i=`${t}.lock`;return await N(i,async()=>{if(b(n))return;const o=`${t}.tmp-${process.pid}`;y(o,{recursive:!0,force:!0}),S(o,{recursive:!0});try{const l=Date.now(),m=c(o,"postgres.jar");await K(X(a),e,m),r(`postgres ${v} (${a}) downloaded and verified in ${Date.now()-l} ms`);const E=q(P(m),w=>w.endsWith(".txz")),d=c(o,"postgres.txz");I(d,E.data);const u=c(o,"tree");S(u);const h=g("tar",["-xJf",d,"-C",u],{encoding:"utf8"});if(h.status!==0)throw new Error(`unpacking the Postgres archive failed: ${h.stderr||h.error?.message}`);process.platform==="darwin"&&z(u),I(c(u,T),`${v} ${a}
2
+ `),y(t,{recursive:!0,force:!0}),k(u,t)}finally{y(o,{recursive:!0,force:!0})}},{timeoutMs:6e5,staleMs:6e5}),L(t)}f(lr,"ensurePostgresBinaries");export{lr as ensurePostgresBinaries,Y as hostJarSuffix,Z as postgresCacheDir};
@@ -0,0 +1,37 @@
1
+ import type { PostgresBinaries } from './binary.js';
2
+ /** A Unix socket path longer than this fails in `bind()` on macOS (`sun_path` is 104 bytes with the NUL). */
3
+ export declare const MAX_SOCKET_PATH_BYTES = 103;
4
+ export declare const POSTGRES_PORT = 5432;
5
+ export declare const DATABASE_NAME = "postgres";
6
+ /** The supervisor refuses to boot the daemon if Postgres does not answer within this long. */
7
+ export declare const READY_TIMEOUT_MS = 15000;
8
+ export interface ClusterSettings {
9
+ dataDir: string;
10
+ socketDir: string;
11
+ maxConnections: number;
12
+ /** Also listen on this TCP address (scratch clusters only; the local supervisor listens on the socket alone). */
13
+ tcp?: {
14
+ host: string;
15
+ port: number;
16
+ };
17
+ /** Run initdb and postgres as this OS user (a root-run test lane starts its cluster as a non-root user). */
18
+ asUser?: {
19
+ uid: number;
20
+ gid: number;
21
+ };
22
+ }
23
+ /** The socket file is `.s.PGSQL.<port>`; pass `port` when the postmaster also listens on a TCP port other than the default. */
24
+ export declare function socketUrl(user: string, socketDir: string, port?: number): string;
25
+ export declare function tcpUrl(user: string, host: string, port: number): string;
26
+ /** Postgres refuses to run as root; say so before it does, in terms of what to change. */
27
+ export declare function assertNotRoot(what: string): void;
28
+ export declare function clusterInitialized(dataDir: string): boolean;
29
+ export declare function postmasterPid(dataDir: string): number | null;
30
+ /** `initdb` a new cluster. `trust` is for throwaway clusters; the local cluster uses peer authentication on its socket. */
31
+ export declare function initCluster(bins: PostgresBinaries, settings: ClusterSettings, user: string, auth: 'peer' | 'trust'): void;
32
+ /** Open a connection, run `SELECT 1`, close. Resolves to the failure message when the server does not answer. */
33
+ export declare function probe(url: string, timeoutMs?: number): Promise<string | null>;
34
+ /** Start `postgres` detached in its own process group and wait until `url` answers `SELECT 1`. */
35
+ export declare function startPostmaster(bins: PostgresBinaries, settings: ClusterSettings, url: string, logFile: string, timeoutMs?: number): Promise<void>;
36
+ /** `pg_ctl stop -m fast`, waiting for the postmaster to exit. A cluster that is not running is a no-op. */
37
+ export declare function stopPostmaster(bins: PostgresBinaries, settings: Pick<ClusterSettings, 'dataDir' | 'asUser'>): void;
@@ -0,0 +1,6 @@
1
+ var S=Object.defineProperty;var o=(r,t)=>S(r,"name",{value:t,configurable:!0});import{spawn as _,spawnSync as f}from"node:child_process";import{closeSync as y,existsSync as C,openSync as T,readFileSync as m}from"node:fs";import{join as w}from"node:path";const I=103,b=5432,h="postgres",D=15e3;function v(r,t,e){return`postgresql://${encodeURIComponent(r)}@/${h}?host=${encodeURIComponent(t)}${e===void 0?"":`&port=${e}`}`}o(v,"socketUrl");function N(r,t,e){return`postgresql://${encodeURIComponent(r)}@${t}:${e}/${h}`}o(N,"tcpUrl");function O(r){if(typeof process.getuid=="function"&&process.getuid()===0)throw new Error(`${r}: Postgres refuses to run as root, and this process is root. Run crtrd as a non-root user (the hosted guest launches it as the crtrd user through setpriv), or set CRTR_DATABASE_URL to an existing Postgres.`)}o(O,"assertNotRoot");function M(r){return C(w(r,"PG_VERSION"))}o(M,"clusterInitialized");function $(r){try{const t=Number(m(w(r,"postmaster.pid"),"utf8").split(`
2
+ `,1)[0]);return Number.isSafeInteger(t)&&t>0?t:null}catch{return null}}o($,"postmasterPid");function d(r){return r.asUser?{uid:r.asUser.uid,gid:r.asUser.gid}:{}}o(d,"identity");function j(r,t,e,n){const s=["-D",t.dataDir,"-U",e,"--encoding=UTF8","--locale=C","--no-instructions",...n==="peer"?["--auth-local=peer","--auth-host=reject"]:["--auth=trust"]],i=f(r.initdb,s,{encoding:"utf8",...d(t),env:{...process.env,LC_ALL:"C"}});if(i.status!==0)throw new Error(`initdb failed (${i.status??i.error?.message}): ${(i.stderr||i.stdout).trim()}`)}o(j,"initCluster");function x(r){try{return m(r,"utf8").trim().split(`
3
+ `).slice(-12).join(`
4
+ `)}catch{return"(no log)"}}o(x,"logTail");async function A(r,t=1e3){const{default:e}=await import("pg"),n=new e.Client({connectionString:r,connectionTimeoutMillis:t});n.on("error",()=>{});try{return await n.connect(),await n.query("SELECT 1"),null}catch(s){return s instanceof Error?s.message:String(s)}finally{await n.end().catch(()=>{})}}o(A,"probe");async function k(r,t,e,n,s=D){const i=["-D",t.dataDir,"-c",`listen_addresses=${t.tcp?t.tcp.host:""}`,...t.tcp?["-c",`port=${t.tcp.port}`]:[],"-c",`unix_socket_directories=${t.socketDir}`,"-c",`max_connections=${t.maxConnections}`,"-c","fsync=on"],p=T(n,"a",384);let a=null;try{const u=_(r.postgres,i,{detached:!0,stdio:["ignore",p,p],env:{...process.env,LC_ALL:"C"},...d(t)});u.on("exit",(c,E)=>{a=`exited with ${c??E}`}),u.on("error",c=>{a=c.message}),u.unref();const g=Date.now()+s;let l="not tried";for(;Date.now()<g;){if(a!==null)throw new Error(`postgres ${a} before answering:
5
+ ${x(n)}`);if(l=await A(e,500),l===null){u.removeAllListeners();return}await new Promise(c=>setTimeout(c,50))}throw new Error(`postgres did not answer SELECT 1 within ${s/1e3} s (${l}):
6
+ ${x(n)}`)}finally{y(p)}}o(k,"startPostmaster");function q(r,t){if($(t.dataDir)===null)return;const e=f(r.pgCtl,["stop","-D",t.dataDir,"-m","fast","-w","-t","60"],{encoding:"utf8",...d(t)});if(e.status!==0&&$(t.dataDir)!==null)throw new Error(`pg_ctl stop failed (${e.status??e.error?.message}): ${(e.stderr||e.stdout).trim()}`)}o(q,"stopPostmaster");export{h as DATABASE_NAME,I as MAX_SOCKET_PATH_BYTES,b as POSTGRES_PORT,D as READY_TIMEOUT_MS,O as assertNotRoot,M as clusterInitialized,j as initCluster,$ as postmasterPid,A as probe,v as socketUrl,k as startPostmaster,q as stopPostmaster,N as tcpUrl};
@@ -0,0 +1,2 @@
1
+ export { ensurePostgresBinaries } from './binary.js';
2
+ export { ensurePostgres, ensurePostgresHealthy, startScratchCluster, stopPostgres, withPostgresStartLock } from './supervisor.js';
@@ -0,0 +1 @@
1
+ import{ensurePostgresBinaries as e}from"./binary.js";import{ensurePostgres as o,ensurePostgresHealthy as a,startScratchCluster as g,stopPostgres as P,withPostgresStartLock as n}from"./supervisor.js";export{o as ensurePostgres,e as ensurePostgresBinaries,a as ensurePostgresHealthy,g as startScratchCluster,P as stopPostgres,n as withPostgresStartLock};
@@ -0,0 +1,7 @@
1
+ /** The pinned zonky embedded-postgres-binaries release (Maven Central, io.zonky.test.postgres). A bump is a
2
+ * deliberate edit here plus `node scripts/postgres-pin.mjs` for the hashes; the cache path is version-keyed, so the
3
+ * old tree is orphaned and one fresh download happens. A new major version also needs a pg_upgrade step. */
4
+ export declare const POSTGRES_VERSION = "18.6.0";
5
+ /** Artifact id suffix of `embedded-postgres-binaries-<suffix>` → sha256 of its jar. */
6
+ export declare const POSTGRES_JAR_SHA256: Record<string, string>;
7
+ export declare function jarUrl(suffix: string, version?: string): string;
@@ -0,0 +1 @@
1
+ var d=Object.defineProperty;var c=(a,e)=>d(a,"name",{value:e,configurable:!0});const b="18.6.0",r={"darwin-arm64v8":"8f6ed6f38195b50a40712ada8b2a12060b6193c1a6977a6eb5cff053bb5a1b89","darwin-amd64":"690941008aac9ee360820fde9bb7870a4f3dcac5e9e2f9e45e42333a436ad0f9","linux-amd64":"008ee189eaa7ca2b58bd01dd950291e638b998392cf982297acd4779ec898ac1","linux-arm64v8":"5f54e016723afa6213925a8acb939bac52944b1d348f65c7a20f0cd2928c31be","linux-amd64-alpine":"1568b3805c3a3b99b5f4f0d7f6cf16e2a39075462ed47a614bfbabd92508350d","linux-arm64v8-alpine":"dccf3c9c8438973e6e8cd9cd511af4a2c6a88d1c90a4cc0465a8f8797808e717"};function n(a,e=b){return`https://repo1.maven.org/maven2/io/zonky/test/postgres/embedded-postgres-binaries-${a}/${e}/embedded-postgres-binaries-${a}-${e}.jar`}c(n,"jarUrl");export{r as POSTGRES_JAR_SHA256,b as POSTGRES_VERSION,n as jarUrl};