@north-light/cli 0.3.580 → 0.3.582
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +6 -1
- package/dist/api/client.js +3 -3
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/execution-start.d.ts +14 -0
- package/dist/api/dto/health.d.ts +22 -0
- package/dist/api/dto/imports.d.ts +142 -0
- package/dist/api/dto/imports.js +0 -0
- package/dist/api/error-codes.d.ts +5 -0
- package/dist/api/error-codes.js +1 -1
- package/dist/api/errors.js +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -1
- package/dist/api/routes.d.ts +2 -0
- package/dist/api/routes.js +1 -1
- package/dist/build-root.d.ts +11 -4
- package/dist/build-root.js +1 -1
- package/dist/cli.js +7 -7
- package/dist/clients/attach/session/connection.d.ts +3 -1
- package/dist/clients/attach/session/connection.js +1 -1
- package/dist/clients/attach/viewer.js +529 -529
- package/dist/commands/api-client.d.ts +12 -4
- package/dist/commands/api-client.js +3 -3
- package/dist/commands/sys/daemon.js +1 -1
- package/dist/commands/sys/desktop.d.ts +5 -0
- package/dist/commands/sys/desktop.js +1 -0
- package/dist/commands/sys/import/move.d.ts +55 -0
- package/dist/commands/sys/import/move.js +8 -0
- package/dist/commands/sys/import/resume.d.ts +1 -0
- package/dist/commands/sys/import/resume.js +1 -0
- package/dist/commands/sys/import/start.d.ts +4 -0
- package/dist/commands/sys/import/start.js +1 -0
- package/dist/commands/sys/join.d.ts +2 -0
- package/dist/commands/sys/join.js +1 -1
- package/dist/commands/sys/machine/rotate.d.ts +1 -0
- package/dist/commands/sys/machine/rotate.js +1 -0
- package/dist/commands/sys/signin.d.ts +7 -0
- package/dist/commands/sys/signin.js +2 -0
- package/dist/commands/sys.js +1 -1
- package/dist/core/blobs/r2.d.ts +4 -0
- package/dist/core/blobs/r2.js +7 -4
- package/dist/core/canvas/daemon-owner.d.ts +1 -1
- package/dist/core/canvas/executions.d.ts +1 -1
- package/dist/core/canvas/node-tokens.d.ts +2 -0
- package/dist/core/canvas/node-tokens.js +3 -3
- package/dist/core/canvas/pid.d.ts +6 -0
- package/dist/core/canvas/pid.js +5 -5
- package/dist/core/command-plugins/compose.js +1 -1
- package/dist/core/command.d.ts +17 -8
- package/dist/core/command.js +9 -9
- package/dist/core/directory/issuer.d.ts +3 -0
- package/dist/core/directory/issuer.js +1 -1
- package/dist/core/directory/person-credential.d.ts +61 -0
- package/dist/core/directory/person-credential.js +1 -0
- package/dist/core/directory/reach.d.ts +3 -0
- package/dist/core/directory/reach.js +1 -0
- package/dist/core/grants/mirror.d.ts +3 -0
- package/dist/core/grants/mirror.js +2 -2
- package/dist/core/graph/bodies-boot-copy.d.ts +16 -2
- package/dist/core/graph/bodies-boot-copy.js +1 -1
- package/dist/core/graph/bodies.d.ts +13 -4
- package/dist/core/graph/bodies.js +1 -1
- package/dist/core/graph/deletion.d.ts +4 -1
- package/dist/core/graph/deletion.js +3 -3
- package/dist/core/graph/documents.d.ts +4 -0
- package/dist/core/graph/documents.js +9 -9
- package/dist/core/graph/reader.js +2 -2
- package/dist/core/graph/repo-sync/exchange.js +2 -2
- package/dist/core/graph/repo-sync/ref-format.d.ts +7 -2
- package/dist/core/graph/repo-sync/ref-format.js +2 -2
- package/dist/core/import/bundle/blob-keys.d.ts +10 -0
- package/dist/core/import/bundle/blob-keys.js +1 -0
- package/dist/core/import/bundle/layout.d.ts +90 -0
- package/dist/core/import/bundle/layout.js +5 -0
- package/dist/core/import/export/archive.d.ts +11 -0
- package/dist/core/import/export/archive.js +1 -0
- package/dist/core/import/export/cloud.d.ts +23 -0
- package/dist/core/import/export/cloud.js +1 -0
- package/dist/core/import/export/dump.d.ts +34 -0
- package/dist/core/import/export/dump.js +3 -0
- package/dist/core/import/export/files.d.ts +22 -0
- package/dist/core/import/export/files.js +1 -0
- package/dist/core/import/export/inline.d.ts +3 -0
- package/dist/core/import/export/inline.js +1 -0
- package/dist/core/import/export/source.d.ts +2 -0
- package/dist/core/import/export/source.js +1 -0
- package/dist/core/import/export/stores.d.ts +9 -0
- package/dist/core/import/export/stores.js +1 -0
- package/dist/core/import/load/bind.d.ts +19 -0
- package/dist/core/import/load/bind.js +15 -0
- package/dist/core/import/load/blobs.d.ts +10 -0
- package/dist/core/import/load/blobs.js +1 -0
- package/dist/core/import/load/catalog.d.ts +32 -0
- package/dist/core/import/load/catalog.js +9 -0
- package/dist/core/import/load/copy.d.ts +26 -0
- package/dist/core/import/load/copy.js +6 -0
- package/dist/core/import/load/path-map.d.ts +7 -0
- package/dist/core/import/load/path-map.js +1 -0
- package/dist/core/import/load/prepare.d.ts +37 -0
- package/dist/core/import/load/prepare.js +10 -0
- package/dist/core/import/load/rewrite.d.ts +39 -0
- package/dist/core/import/load/rewrite.js +13 -0
- package/dist/core/import/load/rollback.d.ts +3 -0
- package/dist/core/import/load/rollback.js +6 -0
- package/dist/core/import/load/run.d.ts +22 -0
- package/dist/core/import/load/run.js +3 -0
- package/dist/core/import/load/stage.d.ts +15 -0
- package/dist/core/import/load/stage.js +1 -0
- package/dist/core/import/load/stores.d.ts +24 -0
- package/dist/core/import/load/stores.js +2 -0
- package/dist/core/import/load/verify.d.ts +4 -0
- package/dist/core/import/load/verify.js +12 -0
- package/dist/core/io.js +4 -4
- package/dist/core/northlight/previews.d.ts +22 -0
- package/dist/core/northlight/previews.js +1 -0
- package/dist/core/providers/call.js +1 -1
- package/dist/core/providers/device.js +1 -1
- package/dist/core/providers/files.d.ts +2 -1
- package/dist/core/providers/files.js +1 -1
- package/dist/core/runtime/broker/cli-relay.js +1 -1
- package/dist/core/runtime/broker/model-adapter.js +3 -3
- package/dist/core/runtime/broker-ownership.d.ts +1 -0
- package/dist/core/runtime/broker-ownership.js +1 -0
- package/dist/core/runtime/broker.js +1 -1
- package/dist/core/runtime/first-run-offer.d.ts +7 -2
- package/dist/core/runtime/first-run-offer.js +10 -2
- package/dist/core/runtime/front-door.js +4 -4
- package/dist/core/runtime/revive.d.ts +5 -2
- package/dist/core/runtime/revive.js +2 -2
- package/dist/core/runtime/runner/blaxel/runner.js +3 -3
- package/dist/core/runtime/runner/index.d.ts +1 -0
- package/dist/core/runtime/runner/index.js +1 -1
- package/dist/core/runtime/runner/machine/call.d.ts +8 -0
- package/dist/core/runtime/runner/machine/call.js +3 -0
- package/dist/core/runtime/runner/machine/environment.d.ts +1 -0
- package/dist/core/runtime/runner/machine/environment.js +1 -0
- package/dist/core/runtime/runner/machine/executor.d.ts +29 -0
- package/dist/core/runtime/runner/machine/executor.js +1 -0
- package/dist/core/runtime/runner/machine/machine-executor.test.d.ts +1 -0
- package/dist/core/runtime/runner/machine/machine-executor.test.js +4 -0
- package/dist/core/runtime/runner/machine/ops.d.ts +127 -0
- package/dist/core/runtime/runner/machine/ops.js +1 -0
- package/dist/core/runtime/runner/machine/runner.d.ts +12 -0
- package/dist/core/runtime/runner/machine/runner.js +1 -0
- package/dist/core/runtime/runner/types.d.ts +7 -1
- package/dist/core/runtime/warm-pool.d.ts +2 -0
- package/dist/core/runtime/warm-pool.js +1 -1
- package/dist/core/secrets/env-rows.d.ts +1 -0
- package/dist/core/secrets/env-rows.js +1 -1
- package/dist/core/storage/purge.d.ts +6 -0
- package/dist/core/storage/purge.js +4 -0
- package/dist/core/subscription-state.d.ts +1 -0
- package/dist/core/subscription-state.js +1 -1
- package/dist/daemon/api/bridge.js +3 -3
- package/dist/daemon/api/handlers/attach.js +1 -1
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/daemon.js +1 -1
- package/dist/daemon/api/handlers/execution-start.js +2 -2
- package/dist/daemon/api/handlers/files.js +1 -1
- package/dist/daemon/api/handlers/imports.d.ts +2 -0
- package/dist/daemon/api/handlers/imports.js +1 -0
- package/dist/daemon/api/handlers/machine-credential.d.ts +2 -0
- package/dist/daemon/api/handlers/machine-credential.js +1 -0
- package/dist/daemon/api/handlers/profiles.js +1 -1
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/dist/daemon/api/handlers/runs.js +1 -1
- package/dist/daemon/api/import-auth.d.ts +6 -0
- package/dist/daemon/api/import-auth.js +1 -0
- package/dist/daemon/api/operations.d.ts +2 -2
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/router.d.ts +7 -1
- package/dist/daemon/api/router.js +1 -1
- package/dist/daemon/api/server.js +1 -1
- package/dist/daemon/boot-migrations.js +1 -1
- package/dist/daemon/broker-signals/bus.d.ts +3 -2
- package/dist/daemon/broker-signals/bus.js +1 -1
- package/dist/daemon/cron/armed-context.d.ts +1 -0
- package/dist/daemon/cron/armed-context.js +1 -1
- package/dist/daemon/cron/closed.d.ts +31 -0
- package/dist/daemon/cron/closed.js +1 -0
- package/dist/daemon/cron/ended.js +2 -2
- package/dist/daemon/cron/lease-recovery.js +1 -1
- package/dist/daemon/cron/machine-exec.d.ts +11 -0
- package/dist/daemon/cron/machine-exec.js +1 -0
- package/dist/daemon/cron/machine-fire.d.ts +26 -0
- package/dist/daemon/cron/machine-fire.js +1 -0
- package/dist/daemon/cron/recurrence.d.ts +14 -0
- package/dist/daemon/cron/recurrence.js +1 -0
- package/dist/daemon/cron/run-token.d.ts +7 -0
- package/dist/daemon/cron/run-token.js +1 -0
- package/dist/daemon/cron-run.js +2 -2
- package/dist/daemon/crtrd-cli.js +1 -1
- package/dist/daemon/crtrd.d.ts +3 -3
- package/dist/daemon/crtrd.js +14 -14
- package/dist/daemon/drain.d.ts +25 -0
- package/dist/daemon/drain.js +9 -0
- package/dist/daemon/due-sources.d.ts +1 -1
- package/dist/daemon/due-sources.js +1 -1
- package/dist/daemon/due.js +8 -8
- package/dist/daemon/fleet.js +5 -5
- package/dist/daemon/import-dump-run.d.ts +1 -0
- package/dist/daemon/import-dump-run.js +5 -0
- package/dist/daemon/machine-daemon.d.ts +10 -0
- package/dist/daemon/machine-daemon.js +1 -0
- package/dist/daemon/machine-link/client.d.ts +29 -0
- package/dist/daemon/machine-link/client.js +1 -0
- package/dist/daemon/machine-link/config.d.ts +45 -0
- package/dist/daemon/machine-link/config.js +3 -0
- package/dist/daemon/machine-link/credential.d.ts +7 -0
- package/dist/daemon/machine-link/credential.js +2 -0
- package/dist/daemon/machine-link/forward.d.ts +16 -0
- package/dist/daemon/machine-link/forward.js +1 -0
- package/dist/daemon/machine-link/frames.d.ts +49 -0
- package/dist/daemon/machine-link/frames.js +1 -0
- package/dist/daemon/machine-link/holder.d.ts +73 -0
- package/dist/daemon/machine-link/holder.js +9 -0
- package/dist/daemon/machine-link/move-notify.d.ts +4 -0
- package/dist/daemon/machine-link/move-notify.js +1 -0
- package/dist/daemon/manage.d.ts +19 -3
- package/dist/daemon/manage.js +3 -3
- package/dist/daemon/reconcilers/cron-lane.js +1 -1
- package/dist/daemon/reconcilers/delivery-lane.js +2 -2
- package/dist/daemon/reconcilers/import-lane.d.ts +9 -0
- package/dist/daemon/reconcilers/import-lane.js +1 -0
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/remote-wake.d.ts +3 -0
- package/dist/daemon/reconcilers/node-lifecycle/remote-wake.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
- package/dist/daemon/reconcilers/purge-due-work.d.ts +13 -0
- package/dist/daemon/reconcilers/purge-due-work.js +1 -0
- package/dist/daemon/reconcilers/row-claim.d.ts +1 -0
- package/dist/daemon/reconcilers/row-claim.js +2 -2
- package/dist/daemon/runs/observer.js +3 -3
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
- package/scripts/sandbox-start.mjs +185 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
var p=Object.defineProperty;var o=(e,r)=>p(e,"name",{value:r,configurable:!0});import{envHomeOverride as m}from"../../shared/env.js";import{loadProfileManifest as h,lookupProfile as v}from"../../core/profiles/manifest.js";import{localEnvironmentSecrets as g}from"../../core/secrets/environment-owner.js";import{ADMITTED_HOST_PATH_ENV as f,buildOperationalEnvBase as w}from"../../core/runtime/spawn-env.js";import{getNode as c}from"../../core/canvas/canvas.js";import{contextDir as R}from"../../core/canvas/paths.js";import{appendRunEvent as y}from"../../core/runs/events.js";import{payerOfGrant as O}from"../../core/grants/mirror.js";import{scopeAllowed as b}from"../../core/scopes.js";import{cronPrincipalOf as
|
|
1
|
+
var p=Object.defineProperty;var o=(e,r)=>p(e,"name",{value:r,configurable:!0});import{envHomeOverride as m}from"../../shared/env.js";import{loadProfileManifest as h,lookupProfile as v}from"../../core/profiles/manifest.js";import{localEnvironmentSecrets as g}from"../../core/secrets/environment-owner.js";import{ADMITTED_HOST_PATH_ENV as f,buildOperationalEnvBase as w}from"../../core/runtime/spawn-env.js";import{getNode as c}from"../../core/canvas/canvas.js";import{contextDir as R}from"../../core/canvas/paths.js";import{appendRunEvent as y}from"../../core/runs/events.js";import{payerOfGrant as O}from"../../core/grants/mirror.js";import{scopeAllowed as b}from"../../core/scopes.js";import{cronPrincipalOf as x}from"./authority.js";import{personId as C}from"../../core/canvas/person.js";function _(e){if(e.profile==null)return null;let r;try{r=v(e.profile)}catch{return null}if(r.kind==="resolved")return r.entry.profileId;if(r.kind==="absent")return null;throw new l(`the armed profile "${e.profile}" is still on this host but its manifest could not be read (${r.manifestPath}) \u2014 running as root would silently give this cron a different identity, env store, and purview. Repair the manifest, or re-arm the cron under a profile that resolves.`)}o(_,"resolveLiveProfile");function M(e){return{...x(e),cron_id:e.cron_id,origin_run_id:e.origin_run_id,isolated:e.isolated,creator:e.created_by}}o(M,"cronBirthPrincipalOf");async function $(e,r){const n=e.creator===null?null:await c(e.creator);return n!==null&&e.origin_run_id!==null&&n.run_id===e.origin_run_id&&(await c(e.origin_run_id))?.deleting_at==null&&n.grantee===e.grantee&&(n.sub??C())===e.sub&&(n.grant_id??null)===e.grant_id&&(e.scopes===null?n.scopes==null:e.scopes.every(t=>b(n.scopes,t)))?{parent:n.node_id,...e.scopes===null?{}:{scopes:[...e.scopes]}}:{root:!0,rootLifecycle:"terminal",...e.scopes===null?{}:{scopes:[...e.scopes]},run:{grantee:e.grantee,sub:e.sub,grant_id:e.grant_id,client_id:null,metadata_json:JSON.stringify(e.cron_id===null?{exec:!0}:{cron_id:e.cron_id}),run_id:r,event_cursor:1,usage_json:JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0})},withinCreate:o(async(t,a)=>{await y(t,r,r,"run.started",{scopes:a.scopes??null,payer:await O(e.grant_id),usage:JSON.parse(a.usage_json)})},"withinCreate")}}o($,"cronBirthPlacement");function B(e){try{return _(e)}catch{return null}}o(B,"resolveLiveProfileOrRoot");function F(e){if(e.profile!=null)try{return h(e.profile).manifest.name}catch{return}}o(F,"resolveLiveProfileName");class l extends Error{static{o(this,"ArmedContextError")}constructor(r){super(r),this.name="ArmedContextError"}}function E(e){if(e==null)return{};let r;try{r=JSON.parse(e)}catch(i){throw new l(`the armed env snapshot is not valid JSON (${i.message}) \u2014 this cron's stored env_json is corrupt, so the run would silently drop every armed variable. Re-arm the cron with a valid --env.`)}if(typeof r!="object"||r===null||Array.isArray(r))throw new l("the armed env snapshot is not a JSON object \u2014 this cron's stored env_json is corrupt. Re-arm the cron with a valid --env.");const n={};for(const[i,t]of Object.entries(r)){if(typeof t!="string")throw new l(`the armed env snapshot's value for "${i}" is not a string \u2014 this cron's stored env_json is corrupt, so the run would silently drop that variable. Re-arm the cron with a valid --env.`);n[i]=t}return n}o(E,"parseArmedEnv");async function G(e,r){const n=E(e.env_json),i=e.grantee==="app:terminal",t=i?_(e):null,a=w({targetCwd:e.cwd,targetProfileId:t,skipSpawnAllow:!i}),s={...a,...await g(t,e.grantee),...n,...t!==null?{CRTR_PROFILE_ID:t}:{},CRTR_CRON_ID:e.cron_id,CRTR_CRON_NAME:e.name,CRTR_RUN_ID:r},u=m();if(u!==void 0&&u!==""&&(s.CRTR_HOME=u),e.anchor_node!=null){s.CRTR_ANCHOR=e.anchor_node;const d=await c(e.anchor_node);d!==null&&(s.CRTR_CONTEXT_DIR=R(d.node_id,d.grantee))}return s[f]=a[f],{profileId:t,env:s}}o(G,"buildArmedRunContext");export{l as ArmedContextError,G as buildArmedRunContext,$ as cronBirthPlacement,M as cronBirthPrincipalOf,E as parseArmedEnv,_ as resolveLiveProfile,F as resolveLiveProfileName,B as resolveLiveProfileOrRoot};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Cron, CronRunRecord } from '../../core/canvas/crons.js';
|
|
2
|
+
/** Everything a CLOSED child process leaves behind — the flat record the
|
|
3
|
+
* settle decision is made over, instead of five levels of live closure. */
|
|
4
|
+
export interface ClosedRun {
|
|
5
|
+
runId: string;
|
|
6
|
+
outOfBand: boolean;
|
|
7
|
+
startedAtIso: string;
|
|
8
|
+
startedAtMs: number;
|
|
9
|
+
finishedAtMs: number;
|
|
10
|
+
/** The raw close code; `timedOut` overrides it to -1 in the record. */
|
|
11
|
+
code: number | null;
|
|
12
|
+
timedOut: boolean;
|
|
13
|
+
processError: Error | null;
|
|
14
|
+
stdoutHead: string;
|
|
15
|
+
stderrHead: string;
|
|
16
|
+
stdoutSha256: string;
|
|
17
|
+
/** overlap=replace killed this run so the next fire could start fresh. */
|
|
18
|
+
wasReplaced: boolean;
|
|
19
|
+
/** The run canceled its own row — the documented self-cancel pattern. */
|
|
20
|
+
wasSelfCanceled: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** Settle one closed child: choose the outcome (replaced short-circuit, owed
|
|
23
|
+
* gate, or the full disposition), then hand it to the one settle tail. */
|
|
24
|
+
export declare function settleClosedRun(c: Cron, run: ClosedRun): Promise<CronRunRecord>;
|
|
25
|
+
/** Settle a claimed run that failed before its launched child could be observed. */
|
|
26
|
+
export declare function settleUnspawnedRun(c: Cron, run: {
|
|
27
|
+
runId: string;
|
|
28
|
+
outOfBand: boolean;
|
|
29
|
+
startedAtIso: string;
|
|
30
|
+
startedAtMs: number;
|
|
31
|
+
}, lead: string, err: Error): Promise<CronRunRecord>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var p=Object.defineProperty;var c=(t,e)=>p(t,"name",{value:e,configurable:!0});import{CRON_OUTPUT_HEAD_BYTES as m,EMPTY_STDOUT_SHA256 as E}from"./capture.js";import{decideHeldSettle as I,EXIT_HELD as A,HELD_DELIVERED as S}from"./held.js";import{settleRun as H}from"./settle.js";async function x(t,e){const{runId:r,outOfBand:s,startedAtIso:d,finishedAtMs:o,code:u,timedOut:l,processError:a}=e,f=!s&&!l&&a==null&&u===A&&!e.wasReplaced&&!e.wasSelfCanceled;let i,n;return e.wasReplaced?(i={kind:"stated",delivered:"replaced (overlap=replace)"},n="release"):f?(i={kind:"stated",delivered:S},n=await I(t,d,o)??"release"):(i={kind:"disposed",ctx:{runId:r,startedAtIso:d,exitCode:l?-1:u,timedOut:l,runTimeoutS:t.run_timeout_s,processErrorMessage:a!=null?a.message:null,stdoutHead:e.stdoutHead,stdoutSha256:e.stdoutSha256,stderrHead:e.stderrHead},selfCanceled:e.wasSelfCanceled},n=s?"release":{kind:"clear"}),H(t,{outOfBand:s,outcome:i,record:{runId:r,startedAtIso:d,finishedAtMs:o,durationMs:o-e.startedAtMs,exitCode:l?-1:u,stdoutHead:e.stdoutHead===""?null:e.stdoutHead,stderrHead:a!=null?`run process error: ${a.message}`.slice(0,m):e.stderrHead===""?null:e.stderrHead},lease:n})}c(x,"settleClosedRun");function D(t,e,r,s){const d=Date.now(),o=`${r}: ${s.message}`.slice(0,m);return H(t,{outOfBand:e.outOfBand,outcome:{kind:"disposed",ctx:{runId:e.runId,startedAtIso:e.startedAtIso,exitCode:null,timedOut:!1,runTimeoutS:t.run_timeout_s,processErrorMessage:s.message,stdoutHead:"",stdoutSha256:E,stderrHead:o}},record:{runId:e.runId,startedAtIso:e.startedAtIso,finishedAtMs:d,durationMs:d-e.startedAtMs,exitCode:null,stdoutHead:null,stderrHead:o},lease:"release"})}c(D,"settleUnspawnedRun");export{x as settleClosedRun,D as settleUnspawnedRun};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)}}r(
|
|
1
|
+
var h=Object.defineProperty;var r=(e,n)=>h(e,"name",{value:n,configurable:!0});import{getNode as c}from"../../core/canvas/canvas.js";import{withCanvasWrite as m}from"../../core/canvas/db.js";import{CRON_RUN_RING as f,cancelCron as p,listCronRuns as y,parseCronSink as _}from"../../core/canvas/crons.js";import{hasNoNaturalCycle as g}from"../../core/runtime/revive-all.js";import{isParked as w}from"../../core/runtime/lifecycle.js";import{enqueueNodeMail as x}from"../../core/feed/messages.js";import{cadenceDisplay as $}from"../../core/wake.js";import{machineEnvironmentOfCron as v,queueCronCancel as k}from"./machine-fire.js";const b=/^delivered: /,C=r(e=>e.exit_code===0&&(e.stdout_head??"")==="","EMPTY_OK");function N(e){return e.filter(n=>n.exit_code!==null||!(n.delivered??"").startsWith("skipped"))}r(N,"realRuns");function R(e,n,s){const t=N(s),o=s.length>=f,l=t.find(u=>b.test(u.delivered??"")),a=n.kind==="expired"?`expired at ${e.expires_at} and was deleted${e.held?" while held by exit 75; the pending occurrence never fired":""}`:`was canceled by ${n.by} and deleted`,d=[`Cron "${e.name}" (${e.cron_id}, ${e.recur!=null?$(e.recur):"one-shot"}) ${a}. It was delivering to you, so nothing will wake you from it any more.`,"",`- runs made: ${o?`at least ${t.length} (the run log keeps the last ${f} entries)`:t.length}`,l!==void 0?`- last delivery to you: ${l.finished}`:`- last delivery to you: ${o?`none in its last ${t.length} runs`:"never delivered"}`],i=t[0];if(i!==void 0){const u=i.exit_code===null?"no exit observed":i.exit_code===-1?"timed out":`exit ${i.exit_code}`;d.push(`- last run: ${i.finished} \u2014 ${u} \u2014 ${i.delivered??""}`)}return t.length>0&&t.every(C)&&d.push("","Every recorded run exited 0 with empty stdout, so the check may have been failing without saying so. A pipeline exits with its last stage, so `probe 2>&1 | grep \u2026 | jq \u2026` hides a failed probe unless the command starts with `set -o pipefail`."),{label:`\u23F0 cron ${e.name} ${n.kind==="expired"?"expired":"canceled"}`,body:d.join(`
|
|
2
|
+
`)}}r(R,"endedMessage");async function E(e){return e.deleting_at!=null||e.run_id!=null&&(await c(e.run_id))?.deleting_at!=null}r(E,"runIsDeleting");async function I(e){return!await E(e)&&(!g(e)||w(e))}r(I,"canTakeMail");async function W(e,n,s){const t=_(e.sink),o=t?.kind==="node"?await c(t.node):null,l=n!==null&&o!==null&&await I(o);if(e.run_state==="running"&&e.run_execution_id!==null&&s?.preserveRunningProcess!==!0){const a=await v(e);a!==null&&await k(a,e.run_execution_id)}return await m(async()=>{const a=l?await y(e.cron_id):[];if(!await p(e.cron_id,s))return!1;if(l){const{label:d,body:i}=R(e,n,a);await x(o.node_id,{kind:"message",label:d,senderNodeId:null,bodyJson:{body:i},urgency:"normal",wake:1,idempotencyKey:`cron-ended:${e.cron_id}`})}return!0})}r(W,"endCron");export{I as canTakeMail,W as endCron};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var R=Object.defineProperty;var s=(e,t)=>R(e,"name",{value:t,configurable:!0});import{randomUUID as w}from"node:crypto";import{setTimeout as A}from"node:timers/promises";import{CRON_DEATH_POLL_MS as O,CRON_KILL_GRACE_MS as C,EMPTY_STDOUT_SHA256 as h}from"./capture.js";import{cronDaemonInstanceId as v,cronOwnerAlive as p,ensureCronOwnerLease as L}from"./live-runs.js";import{crashedOneShotSummary as x,escalateFailure as D}from"./escalation.js";import{settleRun as I}from"./settle.js";import{adoptCronRunId as E,getCron as g,runningCrons as M,stopLeasedCronRun as S,setCronState as T,verifyLeasedCronRun as _}from"../../core/canvas/crons.js";import{claimReconcilerLease as H,releaseReconcilerLease as b,renewReconcilerLease as N}from"../../core/canvas/reconciler-leases.js";import{emitEvent as l}from"../../core/events/emit.js";import{openDb as $}from"../../core/canvas/db.js";import{machineEnvironmentOfCron as k,resumeMachineFire as G}from"./machine-fire.js";const c="cron-recovery",f=3e4;async function U(e){const t=w();if(await H(c,e,t,f)===void 0)return;let n=!1;const r=s(async()=>{if(n)return!1;try{n=!await N(c,e,t,f)}catch{n=!0}return!n},"holds"),a=setInterval(()=>{r()},f/3);return a.unref(),{holds:r,release:s(async()=>{clearInterval(a),await b(c,e,t)},"release")}}s(U,"claimRecovery");async function F(e){const t=await g(e.cron_id);if(t===null||t.run_state!=="running"||t.run_execution_id!==e.run_execution_id||t.run_started_at!==e.run_started_at||t.run_lease_owner!==e.run_lease_owner||t.run_lease_owner!==null&&await p(t.run_lease_owner))return null;if(t.run_execution_id!==null)return t;const n=w();return await E(t,n)?{...t,run_execution_id:n}:null}s(F,"reverifyAbandoned");async function ae(e,t={}){await L();for(const n of await M()){if(n.run_lease_owner===v||n.run_lease_owner!==null&&await p(n.run_lease_owner))continue;const r=n.run_pid===null&&n.run_execution_id!==null?await k(n):null,a=r===null&&(n.run_pid===null||!_(n));if(r===null&&!a&&e-(n.run_started_at!=null?new Date(n.run_started_at).getTime():e)<n.run_timeout_s*1e3)continue;const o=await U(n.cron_id);if(o===void 0)continue;let i;try{i=await F(n)}catch(u){throw await o.release(),u}if(i===null){await o.release();continue}if(r!==null){P(i,r,t,o);continue}if(a){V(i,e,t,o);continue}K(i,e,t,o)}}s(ae,"recoverStaleCronLeases");function K(e,t,n,r){const a=e.run_started_at!=null?new Date(e.run_started_at).getTime():t,o={runId:e.run_execution_id,startedAtIso:e.run_started_at??new Date(t).toISOString(),exitCode:-1,timedOut:!0,runTimeoutS:e.run_timeout_s,processErrorMessage:null,stdoutHead:"",stdoutSha256:h,stderrHead:`run timed out after ${e.run_timeout_s}s (recovered stale lease from a prior daemon instance)`},i=(async()=>{await r.holds()&&(await S(e),await B(e),await r.holds()&&await I(e,{outOfBand:!1,outcome:{kind:"disposed",ctx:o},record:{runId:o.runId,startedAtIso:o.startedAtIso,finishedAtMs:t,durationMs:t-a,exitCode:-1,stdoutHead:null,stderrHead:o.stderrHead},lease:{kind:"clear"}}))})().finally(()=>r.release());n.register?.(i),i.catch(u=>{l({level:"error",event:"cron.stale_lease.settle_failed",error:u,fields:{cron_id:e.cron_id}})})}s(K,"recoverTimedOutRun");function P(e,t,n,r){const a=(async()=>{await r.holds()&&await Y(e)&&await G(e,t)})().finally(()=>r.release());n.register?.(a),a.catch(o=>{l({level:"error",event:"cron.machine_resume.failed",error:o,fields:{cron_id:e.cron_id,run_id:e.run_execution_id}})})}s(P,"resumeMachineRun");async function Y(e){const t=await(await $()).prepare("UPDATE crons SET run_lease_owner = ?, updated = ? WHERE cron_id = ? AND run_state = 'running' AND run_execution_id = ? AND run_lease_owner IS NOT DISTINCT FROM ?").run(v,new Date().toISOString(),e.cron_id,e.run_execution_id,e.run_lease_owner);return Number(t.changes)>0}s(Y,"takeOverLease");async function B(e){if(!await y(e)&&(await S(e,"SIGKILL"),!await y(e)))throw new Error(`cron ${e.cron_id} run ${e.run_pid} survived SIGKILL`)}s(B,"awaitLeasedRunDeath");async function y(e){const t=Date.now()+C;for(;Date.now()<t;){if(!_(e))return!0;await A(O)}return!_(e)}s(y,"pollLeasedRunGone");function V(e,t,n,r){const a=e.recur==null,o=e.run_started_at!=null?new Date(e.run_started_at).getTime():t,i=e.run_pid===null?"the daemon life that claimed this run ended before its launch pid was recorded \u2014 the command may have started and its outcome is unknown":"the daemon life that owned this run ended before it settled \u2014 the process is gone and its outcome was never observed",u={runId:e.run_execution_id,startedAtIso:e.run_started_at??new Date(t).toISOString(),exitCode:null,timedOut:!1,runTimeoutS:e.run_timeout_s,processErrorMessage:i,stdoutHead:"",stdoutSha256:h,stderrHead:`run outcome unobserved: ${i}`},m=(async()=>{let d=`crashed: ${i}${a?"":" \u2014 the next occurrence is unaffected"}`;if(a){if(!await r.holds())return;if(await T(e.cron_id,"paused"),e.on_output==="silent")d=`crashed: ${i} \u2014 one-shot paused, not re-fired (silent: not escalated)`;else{if(!await r.holds())return;d=`crashed one-shot: ${await D(e,u,x(i))}`}}await r.holds()&&(await I(e,{outOfBand:!1,outcome:{kind:"stated",delivered:d,paused:a},record:{runId:u.runId,startedAtIso:u.startedAtIso,finishedAtMs:t,durationMs:Math.max(0,t-o),exitCode:null,stdoutHead:null,stderrHead:u.stderrHead},lease:"release"}),l({level:"warn",event:"cron.run.unobserved",fields:{cron_id:e.cron_id,name:e.name,one_shot:a}}))})().finally(()=>r.release());n.register?.(m),m.catch(d=>{l({level:"error",event:"cron.crashed_run.settle_failed",error:d,fields:{cron_id:e.cron_id}})})}s(V,"settleCrashedRun");export{ae as recoverStaleCronLeases};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MachineCronCancelArgs, MachineCronCancelResult, MachineCronRunArgs, MachineCronRunResult } from '../../core/runtime/runner/machine/ops.js';
|
|
2
|
+
export declare const machineCronMarkerDir: () => string;
|
|
3
|
+
export declare function runMachineCron(args: MachineCronRunArgs): Promise<MachineCronRunResult>;
|
|
4
|
+
/** Stop a run. A run that has not started is canceled for good: a marker holding a canceled result is left, so a `cron.run` that arrives later returns it without spawning. */
|
|
5
|
+
export declare function cancelMachineCron(args: MachineCronCancelArgs): Promise<MachineCronCancelResult>;
|
|
6
|
+
/** Stop every process group a machine cron started on this home and left running: SIGTERM to the group, then SIGKILL after 5 s. A command can leave members
|
|
7
|
+
* behind after its leader (`bash -c` exits, a backgrounded `sleep 600 &` keeps running), with or without a recorded result, so every marker with a
|
|
8
|
+
* group is checked. The group is signalled only when `groupIsRuns` proves it is the run's; unknown ownership rejects sign-out.
|
|
9
|
+
* A group that survives SIGKILL, or a process table that cannot be read, rejects: the caller must not report stopped. A run with no recorded result then
|
|
10
|
+
* holds `exitCode: null`, "stopped: signed out of Northlight", which the control plane reads on the run's next `cron.run`. */
|
|
11
|
+
export declare function stopOrphanedMachineCrons(): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var j=Object.defineProperty;var s=(e,t)=>j(e,"name",{value:t,configurable:!0});import{closeSync as S,fsyncSync as E,mkdirSync as k,openSync as b,readFileSync as J,readdirSync as M,renameSync as G,rmSync as F,statSync as K,writeSync as O}from"node:fs";import{join as T}from"node:path";import{contextDir as L,crtrHome as V}from"../../core/canvas/paths.js";import{capturePidIdentities as v,identitiesMatch as R,isPidAlive as B,killProcessGroup as N,pidsInProcessGroup as X}from"../../core/canvas/pid.js";import{runCommandOnce as Y}from"../../core/runtime/run-command.js";import{ADMITTED_HOST_PATH_ENV as C,buildOperationalEnvBase as W}from"../../core/runtime/spawn-env.js";import{readMachineConfig as q}from"../machine-link/config.js";import{CRON_OUTPUT_HEAD_BYTES as $,EMPTY_STDOUT_SHA256 as g,formatCronOutputHead as P}from"./capture.js";const z=168*36e5,Q=1e3,y=new Map,f=s(()=>T(V(),"runtime","machine-cron"),"machineCronMarkerDir"),m=s(e=>T(f(),`${encodeURIComponent(e)}.json`),"markerPath");function D(e){const t=b(e,"r");try{E(t)}finally{S(t)}}s(D,"fsyncDirectory");function x(e,t){const n=b(e,"wx");try{O(n,t),E(n)}finally{S(n)}D(f())}s(x,"createMarkerFile");function I(e,t){const n=`${e}.${process.pid}.tmp`,r=b(n,"w");try{O(r,t),E(r)}finally{S(r)}G(n,e),D(f())}s(I,"replaceMarkerFile");function w(e){let t;try{t=J(m(e),"utf8")}catch(n){return n.code==="ENOENT"?{kind:"none"}:{kind:"unreadable"}}try{const n=JSON.parse(t);return n===null||typeof n!="object"||n.runId!==e?{kind:"unreadable"}:{kind:"marker",marker:n}}catch{return{kind:"unreadable"}}}s(w,"readMarker");function Z(e){try{for(const t of M(f())){const n=T(f(),t);e-K(n).mtimeMs>z&&F(n,{force:!0})}}catch{}}s(Z,"sweepMarkers");const l=s((e,t)=>({exitCode:null,stdoutHead:null,stderrHead:e.slice(0,$),durationMs:Date.now()-t,stdoutSha256:g}),"failed");function A(e,t,n){return e.kind==="unreadable"?l(`run ${t} has a marker on this machine that cannot be read, so whether it ran is unknown; it is not run again`,n):e.marker.result??l(`run ${t} was started on this machine and its outcome was not recorded (the daemon that ran it ended); it is not run again`,n)}s(A,"answerFromMarker");function ee(e){const t=e.grantee==="app:terminal",n=W({targetCwd:e.cwd,targetProfileId:t?e.profileId:null,skipSpawnAllow:!t}),r={...n,...e.env,CRTR_RUN_ID:e.runId,CRTR_CRON_ID:e.cronId},d=q();return d!==void 0&&(r.CRTR_API_URL=d.apiUrl,r.CRTR_ENVIRONMENT_ID=d.environmentId),e.contextAnchor!==null&&(r.CRTR_CONTEXT_DIR=L(e.contextAnchor.node,e.contextAnchor.grantee)),r[C]=n[C],r}s(ee,"machineRunEnv");function fe(e){const t=y.get(e.runId);if(t!==void 0)return t.promise;const n=Date.now();k(f(),{recursive:!0});const r=w(e.runId);if(r.kind!=="none")return Promise.resolve(A(r,e.runId,n));if(Date.parse(e.leaseExpiresAt)<=n)return Promise.resolve({...l(`lease expired at ${e.leaseExpiresAt} before the run started; nothing ran`,n),notStarted:!0});Z(n);let d;try{d=ee(e)}catch(c){return Promise.resolve({...l(`run could not be armed on this machine: ${c.message}`,n),notStarted:!0})}const o={runId:e.runId,startedAt:new Date(n).toISOString(),result:null};try{x(m(e.runId),JSON.stringify(o))}catch(c){if(c.code==="EEXIST"){const a=w(e.runId);if(a.kind!=="none")return Promise.resolve(A(a,e.runId,n))}return Promise.resolve({...l(`run ${e.runId} could not be recorded before it started: ${c.message}; nothing ran`,n),notStarted:!0})}const i={promise:Promise.resolve(l("unreachable",n)),command:null};return i.promise=(async()=>{let c,a;try{i.command=Y({command:e.command,cwd:e.cwd,env:d,timeoutMs:e.timeoutS*1e3,headBytes:$});const p=i.command.pid;if(p!==null){o.pgid=p,I(m(e.runId),JSON.stringify(o));const _=v([p])?.get(p);if(_===void 0)throw new Error(`run ${e.runId}: the spawned command's identity could not be captured`);o.leaderIdentity=_,I(m(e.runId),JSON.stringify(o))}o.pgid!==void 0&&(a=setInterval(()=>{try{U(o,e.runId)}catch{}},Q)),a?.unref();const u=await i.command.completion;clearInterval(a),o.pgid!==void 0&&U(o,e.runId),c=u.processError!==null?l(u.processError.message,n):{exitCode:u.timedOut?-1:u.exitCode,stdoutHead:u.stdout===""?null:P(u.stdout,u.stdoutTruncated),stderrHead:u.stderr===""?null:P(u.stderr,u.stderrTruncated),durationMs:u.durationMs,stdoutSha256:u.stdoutSha256}}catch(p){o.pgid!==void 0&&o.leaderIdentity===void 0&&(i.command?.terminate(),await H(o.pgid)),c=l(`run failed: ${p.message}`,n)}return clearInterval(a),I(m(e.runId),JSON.stringify({...o,result:c})),c})().finally(()=>{y.delete(e.runId)}),y.set(e.runId,i),i.promise}s(fe,"runMachineCron");async function me(e){const t=y.get(e.runId);if(t!==void 0)return t.command===null?{canceled:!1}:(t.command.terminate(),{canceled:!0});if(k(f(),{recursive:!0}),w(e.runId).kind!=="none")return{canceled:!1};const n={runId:e.runId,startedAt:new Date().toISOString(),result:{exitCode:null,stdoutHead:null,stderrHead:"canceled before it started; nothing ran",durationMs:0,stdoutSha256:g}};try{x(m(e.runId),JSON.stringify(n))}catch(r){if(r.code==="EEXIST")return{canceled:!1};throw r}return{canceled:!0}}s(me,"cancelMachineCron");function h(e){const t=X(e);if(t===null)throw new Error("the process table could not be read");return t}s(h,"pidsInGroup");async function H(e){N(e,"SIGTERM");for(let t=0;h(e).length>0&&t<5e3;t+=100)await new Promise(n=>setTimeout(n,100));if(h(e).length>0){N(e,"SIGKILL");for(let t=0;h(e).length>0&&t<2e3;t+=100)await new Promise(n=>setTimeout(n,100));if(h(e).length>0)throw new Error(`process group ${e} survived SIGKILL`)}}s(H,"stopProcessGroup");function U(e,t){const n=e.pgid;if(B(n))return;const r=h(n).filter(i=>i!==n);if(r.length===0)return;const d=v(r);if(d===null||r.some(i=>!d.has(i)))throw new Error(`run ${t}: identities of process group ${n}'s surviving members could not be captured`);const o=r.map(i=>({pid:i,identity:d.get(i)}));e.survivors=o,I(m(t),JSON.stringify(e))}s(U,"recordSurvivors");function ne(e,t,n){const r=v(n);if(r===null)throw new Error(`run ${e.runId}: the process table could not be read, so its process group ${t} was not checked`);const d=r.get(t);if(d!==void 0){if(e.leaderIdentity===void 0)throw new Error(`run ${e.runId}: process group ${t} has no recorded leader identity; cannot safely stop it`);return R(d,e.leaderIdentity)}if(e.survivors?.some(o=>{const i=r.get(o.pid);return i!==void 0&&R(i,o.identity)}))return!0;throw new Error(`run ${e.runId}: process group ${t} still has members but no recorded member matches; cannot safely stop it`)}s(ne,"groupIsRuns");async function pe(){let e;try{e=M(f()).filter(r=>r.endsWith(".json"))}catch(r){if(r.code==="ENOENT")return;throw r}const t=e.map(async r=>{const d=w(decodeURIComponent(r.slice(0,-5)));if(d.kind==="unreadable")throw new Error(`machine cron marker ${r} cannot be read; cannot safely stop its process group`);if(d.kind==="none"||d.marker.pgid===void 0)return;const{marker:o}=d,i=o.pgid,c=h(i);if(c.length===0)return;if(o.leaderIdentity===void 0)throw new Error(`run ${o.runId}: process group ${i} has no recorded leader identity; cannot safely stop it`);if(!ne(o,i,c))return;const a=Date.parse(o.startedAt);await H(i),o.result===null&&I(m(o.runId),JSON.stringify({...o,result:l("stopped: signed out of Northlight",Number.isNaN(a)?Date.now():a)}))}),n=(await Promise.allSettled(t)).filter(r=>r.status==="rejected");if(n.length>0)throw new Error(n.map(r=>r.reason.message).join("; "))}s(pe,"stopOrphanedMachineCrons");export{me as cancelMachineCron,f as machineCronMarkerDir,fe as runMachineCron,pe as stopOrphanedMachineCrons};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type MachineCronRunArgs, type MachineCronRunResult } from '../../core/runtime/runner/machine/ops.js';
|
|
2
|
+
import { type Cron, type CronRunRecord } from '../../core/canvas/crons.js';
|
|
3
|
+
/** The machine environment a cron runs on, or null when it runs here (a Mac's own daemon, a cron with no profile, a Blaxel profile). */
|
|
4
|
+
export declare function machineEnvironmentOfCron(c: Cron): Promise<string | null>;
|
|
5
|
+
export declare function machineLinkLive(environmentId: string): Promise<boolean>;
|
|
6
|
+
/** What a machine cron's call carries beyond the command: the stored variables and armed env, and who the Mac rebuilds its run context for. */
|
|
7
|
+
type PreparedRun = Pick<MachineCronRunArgs, 'env' | 'grantee' | 'profileId' | 'contextAnchor'>;
|
|
8
|
+
/** One send of the fire: a leased `cron.run` call. Throws MachineOfflineError, MachineLostError or MachineCallFailedError. */
|
|
9
|
+
export declare function sendCronRun(c: Cron, environmentId: string, runId: string, prepared: PreparedRun): Promise<MachineCronRunResult>;
|
|
10
|
+
/** Owe the machine a cancel of this run. The call is queued without a link check: an offline machine takes it when its link returns
|
|
11
|
+
* (the machine's own record of the run id decides whether that stops a running command or keeps a late `cron.run` from starting). */
|
|
12
|
+
export declare function queueCronCancel(environmentId: string, runId: string): Promise<void>;
|
|
13
|
+
export interface MachineFire {
|
|
14
|
+
environmentId: string;
|
|
15
|
+
runId: string;
|
|
16
|
+
outOfBand: boolean;
|
|
17
|
+
startedAtIso: string;
|
|
18
|
+
startedAtMs: number;
|
|
19
|
+
}
|
|
20
|
+
/** Fire `c` on its machine and settle the run. Resolves with the settled record, or null when the cron ended while the run was owed.
|
|
21
|
+
* The caller holds the cron's run lease and has already passed the authority and start checks; `admit` runs once the run is registered. */
|
|
22
|
+
export declare function fireOnMachine(c: Cron, fire: MachineFire, admit: () => void): Promise<CronRunRecord | null>;
|
|
23
|
+
/** A control plane that took a cron's run lease over from one that ended mid-fire: the same run id goes to the machine again, so the machine
|
|
24
|
+
* returns the stored result or joins the live run. A fresh id is never minted for this occurrence. Resolves with the settled record. */
|
|
25
|
+
export declare function resumeMachineFire(c: Cron, environmentId: string): Promise<CronRunRecord | null>;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var D=Object.defineProperty;var d=(e,t)=>D(e,"name",{value:t,configurable:!0});import{randomUUID as N}from"node:crypto";import{setTimeout as x}from"node:timers/promises";import{openDb as R}from"../../core/canvas/db.js";import{isCloudRole as L}from"../../core/canvas/person.js";import{withCanvasWrite as H}from"../../core/canvas/db.js";import{getNode as k}from"../../core/canvas/canvas.js";import{callMachine as F}from"../../core/runtime/runner/machine/call.js";import{machineEnvironmentOfProfile as U}from"../../core/runtime/runner/machine/environment.js";import{MachineCallFailedError as b,MachineLostError as P,MachineOfflineError as C}from"../../core/runtime/runner/machine/ops.js";import{environmentSecrets as B}from"../../core/secrets/environment-secrets.js";import{profileInApp as W}from"../../core/secrets/environment-owner.js";import{getCron as $}from"../../core/canvas/crons.js";import{emitEvent as _}from"../../core/events/emit.js";import{runTokenFor as j}from"./run-token.js";import{parseArmedEnv as K}from"./armed-context.js";import{CRON_OUTPUT_HEAD_BYTES as V}from"./capture.js";import{settleClosedRun as Y,settleUnspawnedRun as w}from"./closed.js";import{clearLiveRun as q,registerLiveRun as J}from"./live-runs.js";import{advanceRecurrence as v}from"./recurrence.js";const z=15,G=2e3;async function Re(e){return!L()||e.profile==null?null:await U(e.profile)}d(Re,"machineEnvironmentOfCron");async function Ce(e){return await(await R()).prepare("SELECT 1 AS live FROM machine_links WHERE environment = ? AND lease_expires_at > ?").get(e,new Date().toISOString())!==void 0}d(Ce,"machineLinkLive");async function S(e,t){const n=e.profile,l=await(await R()).prepare("SELECT person FROM profiles WHERE id = ?").get(n);if(l===void 0)throw new Error(`profile ${n} of cron ${e.cron_id} is not in the control plane`);const o=await B({person:l.person,app:e.grantee,ownerProfile:n,ownerProfileInApp:await W(n,e.grantee),includeDeployment:!1}),r=e.grantee==="app:terminal",a=e.anchor_node!=null?await k(e.anchor_node):null;return{env:{...o,...K(e.env_json),...r?{CRTR_PROFILE_ID:n}:{},CRTR_CRON_ID:e.cron_id,CRTR_CRON_NAME:e.name,CRTR_RUN_ID:t,CRTR_API_TOKEN:j(t),...e.anchor_node!=null?{CRTR_ANCHOR:e.anchor_node}:{}},grantee:e.grantee,profileId:n,contextAnchor:a===null?null:{node:a.node_id,grantee:a.grantee??e.grantee}}}d(S,"prepareRun");async function Q(e,t,n,l){const o=(e.run_timeout_s+z)*1e3;return await F(t,"cron.run",{runId:n,cronId:e.cron_id,command:e.command,cwd:e.cwd,...l,timeoutS:e.run_timeout_s,leaseExpiresAt:new Date(Date.now()+o).toISOString()},{timeoutMs:o})}d(Q,"sendCronRun");async function X(e,t){await H(async n=>{await n.prepare("INSERT INTO machine_calls (call_id, environment, op, args_json, created_at) VALUES (?, ?, ?, ?, ?)").run(N(),e,"cron.cancel",JSON.stringify({runId:t}),new Date().toISOString()),await n.prepare("SELECT pg_notify(?, ?)").get("crtr_signal",`machine:${e}`)})}d(X,"queueCronCancel");async function ve(e,t,n){const{runId:l,outOfBand:o,startedAtIso:r,startedAtMs:a}=t;let c;try{if(e.isolated)throw new Error("an isolated cron needs a root launcher, which a machine does not have");if(c=await S(e,l),!o&&e.recur!=null){const i=await v(e,a);if(i!==null)throw i}}catch(i){return await w(e,{runId:l,outOfBand:o,startedAtIso:r,startedAtMs:a},"run could not be armed",i)}return await g(e,t,c,n)}d(ve,"fireOnMachine");async function Se(e,t){const n=e.run_execution_id,l=e.run_started_at,o=Date.parse(l),r=e.run_out_of_band,a={environmentId:t,runId:n,outOfBand:r,startedAtIso:l,startedAtMs:o};let c;try{if(c=await S(e,n),!r&&e.recur!=null&&Date.parse(e.fire_at)<=o){const i=await v(e,o);if(i!==null)throw i}}catch(i){return await w(e,a,"run could not be armed",i)}return await g(e,a,c,()=>{})}d(Se,"resumeMachineFire");async function g(e,t,n,l){const{environmentId:o,runId:r,outOfBand:a,startedAtIso:c,startedAtMs:i}=t,m={runId:r,replaced:!1,selfCanceled:!1,kill:d(()=>{X(o,r).catch(u=>{_({level:"error",event:"cron.machine.cancel_failed",error:u,fields:{cron_id:e.cron_id,run_id:r}})})},"kill")};J(e.cron_id,m),l();try{let u=null,f=null;for(;u===null&&f===null;){try{const s=await Q(e,o,r,n);if(s.notStarted!==!0){u=s;break}_({level:"warn",event:"cron.machine.resend",fields:{cron_id:e.cron_id,run_id:r,why:s.stderrHead}})}catch(s){if(s instanceof b){f=s;break}if(!(s instanceof C)&&!(s instanceof P))throw s;if(a&&s instanceof C){f=s;break}_({level:"warn",event:"cron.machine.resend",fields:{cron_id:e.cron_id,run_id:r,why:s.message}})}if(!m.selfCanceled&&await $(e.cron_id)===null)return null;await x(G)}if(f!==null)return await w(e,{runId:r,outOfBand:a,startedAtIso:c,startedAtMs:i},"run failed on the machine",f);const{exitCode:p,stdoutHead:O,stderrHead:h,durationMs:A,stdoutSha256:y}=u,E=p===-1,I=p===null?new Error((h??"the machine reported no exit").slice(0,V)):null,T=m.replaced,M=m.selfCanceled;return await Y(e,{runId:r,outOfBand:a,startedAtIso:c,startedAtMs:i,finishedAtMs:i+A,code:E?null:p,timedOut:E,processError:I,stdoutHead:O??"",stderrHead:h??"",stdoutSha256:y,wasReplaced:T,wasSelfCanceled:M})}finally{q(e.cron_id,r)}}d(g,"driveFire");export{ve as fireOnMachine,Re as machineEnvironmentOfCron,Ce as machineLinkLive,X as queueCronCancel,Se as resumeMachineFire,Q as sendCronRun};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Cron } from '../../core/canvas/crons.js';
|
|
2
|
+
/** Settle the schedule BEFORE the run: a recurrence advances to its next slot
|
|
3
|
+
* now, so a crash mid-run never re-fires this slot (the lease guards the
|
|
4
|
+
* intra-life overlap; the advanced fire_at guards the crash window).
|
|
5
|
+
*
|
|
6
|
+
* Returns the process error to hand `close` when the recurrence cannot be
|
|
7
|
+
* parsed, else null. That row can never be scheduled again as written — but
|
|
8
|
+
* DELETING it would destroy the user's config AND its whole run history with
|
|
9
|
+
* no record and no escalation, the one outcome the spec has no vocabulary for.
|
|
10
|
+
* It takes the failure path instead, so the row PAUSES and an escalation node
|
|
11
|
+
* arrives holding the parse error. `fire_at` is deliberately left past-due: a
|
|
12
|
+
* paused row is not due, and once someone fixes the recurrence a `cron resume`
|
|
13
|
+
* fires immediately. */
|
|
14
|
+
export declare function advanceRecurrence(c: Cron, startedAtMs: number): Promise<Error | null>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var c=Object.defineProperty;var a=(r,e)=>c(r,"name",{value:e,configurable:!0});import{advanceCron as o,setCronState as i}from"../../core/canvas/crons.js";import{emitEvent as u}from"../../core/events/emit.js";import{nextSlotAfter as s}from"../../core/wake.js";async function p(r,e){let n;try{n=s(r.recur,new Date(e))}catch(t){return u({level:"error",event:"cron.recur.unparseable",error:t,fields:{cron_id:r.cron_id,name:r.name,recur:r.recur}}),await i(r.cron_id,"paused"),new Error(`unparseable recurrence ${JSON.stringify(r.recur)}: ${t.message} \u2014 this cron cannot be rescheduled until its recurrence is fixed (\`crtr cron cancel\` and re-arm it), so it has been paused.`)}return await o(r.cron_id,n),null}a(p,"advanceRecurrence");export{p as advanceRecurrence};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CronPrincipalClass } from '../api/router.js';
|
|
2
|
+
export declare const RUN_TOKEN_PREFIX = "crtr_run_";
|
|
3
|
+
export declare const runTokenFor: (runId: string) => string;
|
|
4
|
+
/** The run id a well-formed token with a valid MAC names, else null. */
|
|
5
|
+
export declare function runIdOfToken(token: string): string | null;
|
|
6
|
+
/** The cron principal of the run `token` names, or null when the token is invalid or the run is no longer the cron's running one. */
|
|
7
|
+
export declare function cronPrincipalForRunToken(token: string): Promise<CronPrincipalClass | null>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var a=Object.defineProperty;var t=(r,e)=>a(r,"name",{value:e,configurable:!0});import{createHmac as l,timingSafeEqual as d}from"node:crypto";import{openDb as f}from"../../core/canvas/db.js";import{getCron as _}from"../../core/canvas/crons.js";import{loadSecretsKey as p}from"../../core/secrets/key.js";const c="crtr_run_",s=t(r=>l("sha256",p().key).update(`cron-run:${r}`).digest("hex"),"mac"),T=t(r=>`${c}${r}.${s(r)}`,"runTokenFor");function m(r){if(!r.startsWith(c))return null;const e=r.slice(c.length),o=e.lastIndexOf(".");if(o<=0)return null;const n=e.slice(0,o),i=Buffer.from(e.slice(o+1)),u=Buffer.from(s(n));return i.length===u.length&&d(i,u)?n:null}t(m,"runIdOfToken");async function b(r){const e=m(r);if(e===null)return null;const o=await(await f()).prepare("SELECT cron_id FROM crons WHERE run_execution_id = ? AND run_state = 'running'").get(e),n=o===void 0?null:await _(o.cron_id);return n===null||n.run_execution_id!==e||n.run_state!=="running"?null:{class:"cron",kind:"cron",cron:n.cron_id,app:n.grantee,sub:n.sub,grant:n.grant_id,scopes:n.scopes,execution:e,from_exec:n.armed_from_exec,origin_run:n.origin_run_id,isolated:n.isolated,anchor:n.anchor_node,uid:-1}}t(b,"cronPrincipalForRunToken");export{c as RUN_TOKEN_PREFIX,b as cronPrincipalForRunToken,m as runIdOfToken,T as runTokenFor};
|
package/dist/daemon/cron-run.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
${F}`.trim().slice(0,
|
|
1
|
+
var q=Object.defineProperty;var i=(e,f)=>q(e,"name",{value:f,configurable:!0});import{randomUUID as N}from"node:crypto";import{CRON_OUTPUT_HEAD_BYTES as H,formatCronOutputHead as O}from"./cron/capture.js";import{clearLiveRun as p,cronDaemonInstanceId as M,ensureCronOwnerLease as Y,registerLiveRun as z}from"./cron/live-runs.js";import{closeCronExecution as G,openCronExecution as J}from"./cron/run-socket.js";import{appProcessSandboxed as K}from"../core/spaces/process.js";import{nodeAgentDir as Q}from"../core/canvas/paths.js";import{uidForApp as V}from"../core/spaces/users.js";import{buildArmedRunContext as W}from"./cron/armed-context.js";import{ensureCronFolder as X}from"./cron/folders.js";import{fireOnMachine as Z,machineEnvironmentOfCron as j,machineLinkLive as ee}from"./cron/machine-fire.js";import{advanceRecurrence as P}from"./cron/recurrence.js";import{checkCronFire as ne,cronPrincipalOf as te}from"./cron/authority.js";import{refusalError as re}from"../core/ledger/index.js";import{startRefusal as oe}from"../core/runtime/start-check.js";import{settleClosedRun as ae,settleUnspawnedRun as D}from"./cron/closed.js";import{settleRun as U}from"./cron/settle.js";import{applyOverlapPolicies as ie,expireDueCrons as se}from"./cron/passes.js";import{dueClockCrons as de,acquireCronRunLease as le,releaseCronRunLease as ue,attachCronRunPid as fe}from"../core/canvas/crons.js";import{emitEvent as me}from"../core/events/emit.js";import{runAppCommand as ce}from"../core/runtime/run-command.js";import{CRON_OUTPUT_HEAD_BYTES as Fe}from"./cron/capture.js";import{cronDaemonInstanceId as Ne,markSelfCanceledCronRun as Ye}from"./cron/live-runs.js";import{recoverStaleCronLeases as Ge}from"./cron/lease-recovery.js";function Pe(e,f){return B(e,f).settled}i(Pe,"executeCron");function B(e,f){const{outOfBand:s}=f;let l=i(()=>{},"admit");const h=new Promise(o=>{l=o}),g=new Promise((o,r)=>{let b=!1;const t=N();let x=i(()=>{},"undo");const S=i(d=>{if(x(),!b){r(d);return}b=!1,ue(e.cron_id,t).then(()=>r(d),u=>{me({level:"error",event:"cron.lease.release_failed",error:u,fields:{cron_id:e.cron_id}}),r(d)})},"fail");(async()=>{const d=Date.now(),u=new Date(d).toISOString(),E={runId:t,outOfBand:s,startedAtIso:u,startedAtMs:d},I=await j(e);if(I!==null&&!s&&!await ee(I)){o(null);return}if(await Y(),!await le(e.cron_id,M,u,s,t)){o(null);return}b=!0;let m=await ne(te(e));if(m.allowed){const n=await oe({grantee:e.grantee,sub:e.sub,grant_id:e.grant_id});n!==null&&(m={allowed:!1,reason:"limit_reached",message:re(n).message})}if(!m.allowed){try{if(!s&&e.recur!=null){const c=await P(e,d);if(c!==null)throw c}}catch(c){D(e,E,"run could not be armed",c).then(o,r);return}const n=Date.now();U(e,{outOfBand:s,outcome:{kind:"refused",reason:m.reason,message:m.message},record:{runId:t,startedAtIso:u,finishedAtMs:n,durationMs:n-d,exitCode:null,stdoutHead:null,stderrHead:`${m.reason}: ${m.message}`.slice(0,H)},lease:"release"}).then(o,r);return}if(I!==null){Z(e,{environmentId:I,runId:t,outOfBand:s,startedAtIso:u,startedAtMs:d},l).then(o,S);return}let v;try{if(e.isolated&&X(e.grantee,e.cron_id),{env:v}=await W(e,t),!s&&e.recur!=null){const n=await P(e,d);if(n!==null)throw n}}catch(n){D(e,E,"run could not be armed",n).then(o,r);return}let A=null,T=!1;const R={runId:t,replaced:!1,selfCanceled:!1,kill:i(()=>{A!==null?A():T=!0},"kill")};z(e.cron_id,R),x=i(()=>p(e.cron_id,t),"undo"),l();const w=K()?{executionId:t,kind:"cron",cronId:e.cron_id,fromExec:e.armed_from_exec,grantee:e.grantee,sub:e.sub,grantId:e.grant_id,scopes:e.scopes,originRunId:e.origin_run_id,isolated:e.isolated,anchor:e.anchor_node,uid:await V(e.grantee),rebuilt:!1,refusal:null}:null,_=i(()=>{w!==null&&G(t)},"closeExecution");x=i(()=>{p(e.cron_id,t),_()},"undo"),i(async()=>(w!==null&&await J(w,e.cwd,e.profile),ce({command:e.command,cwd:e.cwd,env:v,app:e.grantee,isolated:e.isolated,principal:{sub:e.sub,grantId:e.grant_id,scopes:e.scopes},...e.isolated?{folder:e.cwd}:{},...w!==null?{cron:{executionId:t,...e.anchor_node===null||e.isolated?{}:{anchorDir:Q(e.anchor_node,e.grantee)}}}:e.anchor_node===null||e.isolated?{}:{nodeId:e.anchor_node},timeoutMs:e.run_timeout_s*1e3,headBytes:H})),"launch")().then(async n=>{if(n.pid===null){p(e.cron_id,t),_(),n.completion.then(a=>{D(e,E,"run failed to spawn",a.processError??new Error("bash did not provide a process id")).then(o,r)},r);return}x=i(()=>{p(e.cron_id,t),_(),n.terminate()},"undo");let c=!1;const k=i(()=>{n.completion.then(a=>{const y=R.replaced,$=R.selfCanceled,C=w?.refusal??null;if(p(e.cron_id,t),_(),c){o(null);return}if(C!==null&&!y){const L=Date.now(),F=O(a.stderr,a.stderrTruncated);U(e,{outOfBand:s,outcome:{kind:"refused",reason:C.reason,message:C.message},record:{runId:t,startedAtIso:u,finishedAtMs:L,durationMs:L-d,exitCode:a.exitCode,stdoutHead:O(a.stdout,a.stdoutTruncated)||null,stderrHead:`${C.reason}: ${C.message}
|
|
2
|
+
${F}`.trim().slice(0,H)},lease:"release"}).then(o,r);return}ae(e,{runId:t,outOfBand:s,startedAtIso:u,startedAtMs:d,finishedAtMs:Date.now(),code:a.exitCode,timedOut:a.timedOut,processError:a.processError,stdoutHead:O(a.stdout,a.stdoutTruncated),stderrHead:O(a.stderr,a.stderrTruncated),stdoutSha256:a.stdoutSha256,wasReplaced:y,wasSelfCanceled:$}).then(o,r)},r)},"settleCommand");if(!await fe(e.cron_id,M,u,n.pid)&&!R.selfCanceled){c=!0,n.terminate(),k();return}A=n.terminate,T&&n.terminate(),k()},n=>{p(e.cron_id,t),_(),D(e,E,"run failed to spawn",n).then(o,r)}).catch(S)})().catch(S)});return g.then(l,l),{admitted:h,settled:g}}i(B,"startCron");async function Ue(e,f={}){const s=new Date(e).toISOString();await se(s),await ie(e,s);const l=[];for(const h of await de(s)){const{admitted:g,settled:o}=B(h,{outOfBand:!1}),r=o.then(()=>{});r.catch(()=>{}),await g,l.push(r),f.register?.(r)}for(const h of l)h.catch(()=>{});return l}i(Ue,"runDueCrons");export{Fe as CRON_OUTPUT_HEAD_BYTES,Ne as cronDaemonInstanceId,Pe as executeCron,Ye as markSelfCanceledCronRun,Ge as recoverStaleCronLeases,Ue as runDueCrons,B as startCron};
|
package/dist/daemon/crtrd-cli.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var c=Object.defineProperty;var
|
|
1
|
+
var c=Object.defineProperty;var o=(e,r)=>c(e,"name",{value:r,configurable:!0});import"../suppress-experimental-warnings.js";import{runDaemon as p}from"./crtrd.js";import{configureDaemonHttpDispatcher as a}from"./http-dispatcher.js";import{readMachineConfig as s}from"./machine-link/config.js";function u(e){for(let r=0;r<e.length;r++){const t=e[r];if(t==="--tcp")return e[r+1];if(t!==void 0&&t.startsWith("--tcp="))return t.slice(6)}}o(u,"parseTcpArg");function l(e){for(let r=0;r<e.length;r++){const t=e[r],n=t==="--role"?e[r+1]:t?.startsWith("--role=")?t.slice(7):void 0;if(n!==void 0){if(n!=="api"&&n!=="reconcile"&&n!=="machine")throw new Error(`--role must be api, reconcile or machine, got ${n}`);return n}}return s()===void 0?void 0:"machine"}o(l,"parseRoleArg");function f(e){return e.includes("--prepare")}o(f,"parsePrepareArg");const i=process.argv.slice(2);if(i[0]==="export-sqlite"){const{runExportSqlite:e}=await import("./export-sqlite-run.js");process.exitCode=await e()}else if(i[0]==="import-dump"){const{runImportDump:e}=await import("./import-dump-run.js");process.exitCode=await e(i[1],i[2])}else a(),await p({tcp:u(i),prepare:f(i),role:l(i)});
|
package/dist/daemon/crtrd.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type DetachedWorkLifecycle } from './reconcilers/broker-supervision.js';
|
|
2
2
|
import { DaemonFleet } from './fleet.js';
|
|
3
|
-
export
|
|
3
|
+
export { brokerCommandDeclaresCanvasHome } from '../core/runtime/broker-ownership.js';
|
|
4
4
|
/** The `--epoch <id>` tag host.ts stamps into every broker's argv, or null
|
|
5
5
|
* for a legacy/untagged broker (which the census treats as prior-epoch). */
|
|
6
6
|
export declare function brokerCommandEpoch(command: string): string | null;
|
|
@@ -18,10 +18,10 @@ export interface DaemonOpts {
|
|
|
18
18
|
/** Hold after storage preparation until `POST /v1/daemon/admit`. */
|
|
19
19
|
prepare?: boolean;
|
|
20
20
|
/** `api`: serves requests, runs no reconciler lane. `reconcile`: runs the lanes and serves only the probe API.
|
|
21
|
-
* Absent (a Mac): one process doing both, holding the `daemon_owner` claim. */
|
|
21
|
+
* `machine`: a Mac after import; holds the machine link and executes runner calls, no database. Absent (a Mac before import): one process doing both, holding the `daemon_owner` claim. */
|
|
22
22
|
role?: DaemonRole;
|
|
23
23
|
}
|
|
24
|
-
export type DaemonRole = 'api' | 'reconcile';
|
|
24
|
+
export type DaemonRole = 'api' | 'reconcile' | 'machine';
|
|
25
25
|
/** Whether a process of this role runs the reconciler lanes and their boot sweeps (an api process does not). */
|
|
26
26
|
export declare function roleRunsLanes(role: DaemonRole | undefined): boolean;
|
|
27
27
|
/** Whether a process of this role serves conversations, documents and the app listener (a reconcile process does not). */
|
package/dist/daemon/crtrd.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
var er=Object.defineProperty;var i=(e,l)=>er(e,"name",{value:l,configurable:!0});import{applyBootSettings as rr}from"./boot-settings.js";import{startPeerOutbox as tr}from"./peer/outbox.js";import{reportMachineStatus as nr}from"../core/peer/directory.js";import{createPeerRelay as or}from"./peer/relay-service.js";import{writeFileSync as ir,readFileSync as ar,rmSync as sr,mkdirSync as ye,existsSync as b,unlinkSync as _e,chmodSync as be}from"node:fs";import{createServer as J}from"node:http";import{dirname as cr}from"node:path";import{envCrtrdTcp as ke,envDocumentPort as lr}from"../shared/env.js";import{openCanvasConversationStore as dr,serveCanvasConversationStore as pr}from"../core/canvas/sessions.js";import{installConversationStore as mr}from"../core/conversation-store/index.js";import{pollRunObservers as fr}from"./runs/observer.js";import{randomBytes as Re}from"node:crypto";import{apiSocketPath as Pe,crtrHome as ur}from"../core/canvas/paths.js";import{activeAppUsers as wr}from"../core/spaces/users.js";import{ensureAppSpace as hr,ensureUserFiles as vr}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as x}from"../launcher/spec.js";import{launcherRequest as L}from"../launcher/client.js";import{installLauncherEventSink as gr}from"./launcher-events.js";import{initializeEmptyLayout as Sr,layoutNeedsMigration as yr,socketDir as Ee}from"../core/layout.js";import{migrateLooseKeys as _r}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as br}from"../core/profiles/relocate.js";import{getNode as kr,listNodesForSupervision as Ie,listParkedResidentsWithWakeMail as Rr,setFrozen as xe}from"../core/canvas/index.js";import{createApiServer as Pr,closeServer as Te}from"./api/server.js";import{Router as Er}from"./api/router.js";import{healthRoutes as Ir}from"./api/handlers/health.js";import{createDocumentListener as xr}from"./api/document-listener.js";import{documentSecret as Tr}from"./api/document-secret.js";import{createAppListener as Cr}from"./api/app-listener.js";import{registerMachineEnvironment as Dr}from"../core/environments.js";import{registerBlaxelRunnerFromEnv as Lr}from"../core/secrets/blaxel-register.js";import{markCloudRole as $r,resolvePerson as Ar,retireLocalPerson as Nr,setPersonId as Mr}from"../core/canvas/person.js";import{backfillNodeApps as Gr,runPermissionsBoot as Or}from"../core/permissions/boot.js";import{startRunNotices as Fr}from"./run-notices.js";import{startGrantEffects as Br}from"./reconcilers/grant-effects.js";import{createGrantSync as jr,GRANT_SYNC_INTERVAL_S as zr}from"../core/grants/sync.js";import{sweepSandboxes as Hr}from"../core/grants/on-sync.js";import{rebuildCronExecutions as Ur}from"./cron/run-socket.js";import{hasSyncedGrants as qr}from"../core/grants/mirror.js";import{readConfig as se}from"../core/config.js";import{reconcileUndeliveredTickets as Wr}from"./human/sweep.js";import{resetHandedOffDeliveries as Jr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as Kr}from"../core/canvas/daemon-actions.js";import{sweepReviews as Vr}from"./review/sweep.js";import{pidfilePath as ce}from"./pidfile.js";import{rendererWarning as Yr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as Qr}from"./supervise-cadence.js";import{createResumeDetector as Xr}from"./resume-detector.js";import{setFatalFaultAnnouncer as Zr}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as et}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as rt}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as tt}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as nt}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as ot}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as it}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as at}from"./reconcilers/action-lane.js";import{endLiveExecution as st,migrateLegacyExecutionIdentities as ct}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as lt}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as dt}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as pt}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as mt}from"./reconcilers/storage-due-work.js";import{assertLedgerEnv as ft}from"../core/ledger/rates.js";import{LedgerSandboxReconciler as ut}from"./reconcilers/ledger-sandbox.js";import{LedgerStorageReconciler as wt}from"./reconcilers/ledger-storage.js";import{LedgerR2Reconciler as ht}from"./reconcilers/ledger-r2.js";import{BashDeadlineReconciler as vt}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as gt}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as St}from"./reconcilers/structured-request-lane.js";import{RecipeImageReconciler as yt,subscribeImageBuilt as _t}from"./reconcilers/recipe-image.js";import{reconcileStructuredRequestSidecars as bt}from"./structured-requests/boot.js";import{captureLivenessSnapshot as Ce,capturePidCommand as kt,captureTeardownSnapshot as De,isPidAlive as Rt,isAnyPidAlive as Le,killProcessTreePids as $e}from"../core/canvas/pid.js";import{bindFleet as Pt,boundFleet as Et}from"../core/runtime/fleet.js";import{bindDaemonControl as It,clearDaemonControl as xt}from"./control.js";import{execDaemon as Tt,spawnDaemon as Ct}from"./manage.js";import{reconcileAndMigrateBoot as Dt}from"../core/canvas/boot.js";import{resolveInstallId as Lt}from"../core/canvas/install-id.js";import{setInstallId as $t}from"../core/runtime/nodes.js";import{claimDaemonOwnership as At,daemonOwnershipApplies as Nt,daemonOwnershipClaimed as Mt,releaseDaemonOwnership as Ae}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as Gt,closeAllDbs as le,openDb as Ne,withCanvasWrite as Ot}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as Ft}from"../core/canvas/import-sqlite.js";import{ensurePostgres as Bt,ensurePostgresHealthy as jt,stopPostgres as zt,withPostgresStartLock as Ht}from"./postgres/index.js";import{brokerSignalBus as F}from"./broker-signals/bus.js";import{brokerSignalStreams as Ut}from"./api/handlers/broker-signals.js";import{setRequestsStartRunObservers as qt}from"./api/handlers/broker-ops.js";import{reconcilePackages as Wt}from"../core/graph/package-reconcile.js";import{startRepoSync as Jt}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as Kt,reapStaleSpares as Vt}from"../core/runtime/warm-pool.js";import{DaemonFleet as Yt}from"./fleet.js";import{copyNodeFilesToRows as Qt}from"../core/runtime/first-boot-rows.js";import{BOOT_MIGRATIONS as Xt}from"./boot-migrations.js";import{emitEvent as u}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as Zt}from"../core/events/source.js";import{activateCanvasImport as en,MigrationBlockedError as rn,runForcedCanvasImport as tn}from"../migrations/activation.js";import{adoptInstallPerson as nn,convertLegacyScopedStore as on,personStoresCopied as an}from"../core/scoped-state/migrate.js";import{readLegacyUserSnapshot as sn}from"../core/scoped-state/legacy-profiles.js";import{heldPersons as Me,installPreludeSnapshot as cn}from"../core/scoped-state/person-store.js";import{loadPersonSettings as Ge}from"../core/scoped-state/person-rows.js";import{userScopeRoot as Oe}from"../core/scope.js";function de(e,l){return e.includes(` --canvas-home ${l} `)}i(de,"brokerCommandDeclaresCanvasHome");function pe(e){return/ --epoch (\S+) /.exec(e)?.[1]??null}i(pe,"brokerCommandEpoch");const ln=5e3,Fe=150;async function dn(e){const l=Ce();if(l===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 f=ur(),m=[];for(const[n,d]of l.processes){const y=d.command;if(d.zombie||!y.includes("broker-cli.js")||!de(y,f)||pe(y)===e)continue;const k=y.trim().split(/\s+/).at(-1)??"";k===""||k.endsWith("broker-cli.js")||m.push({pid:n,nodeId:k})}let v=0;const a=[];for(const n of m){const d=kt(n.pid);if(d===null||!d.includes("broker-cli.js")||!de(d,f)||pe(d)===e||d.trim().split(/\s+/).at(-1)!==n.nodeId)continue;const y=De(n.pid,null);if(!y.reused){try{process.platform==="linux"&&b(x)?await L({operation:"stop",pid:n.pid}):process.kill(n.pid,"SIGTERM")}catch{continue}v++,a.push({pid:n.pid,snap:y})}}const w=Date.now()+ln;for(;Date.now()<w&&a.some(n=>Le(n.snap.tree.length>0?n.snap.tree:[n.pid]));)await new Promise(n=>setTimeout(n,Fe));let g=0;for(const n of a){const d=n.snap.tree.length>0?n.snap.tree:[n.pid];Le(d)&&(process.platform==="linux"&&b(x)?await L({operation:"stop",pid:n.pid}):$e(d,"SIGKILL",n.snap.identities),g++)}g>0&&await new Promise(n=>setTimeout(n,Fe));const p=Ce();if(p===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 c=[];for(const[n,d]of p.processes){const y=d.command;d.zombie||!y.includes("broker-cli.js")||de(y,f)&&pe(y)!==e&&c.push(n)}if(c.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${c.join(", ")}`);u({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:m.length,sigtermed:v,sigkilled:g}})}i(dn,"reapPriorEpochBrokers");const pn=5e3;async function mn(e){const l=Array.from(e.entries());if(l.length===0)return;for(const[a]of l){const w=(await kr(a))?.status;(w==="active"||w==="idle")&&await xe(a,new Date().toISOString())}const f=l.map(([,a])=>({pid:a.pid,snap:De(a.pid,null),exited:a.handle.exited}));for(const a of f)if(!a.snap.reused)try{process.platform==="linux"&&b(x)?await L({operation:"stop",pid:a.pid}):process.kill(a.pid,"SIGTERM")}catch{}const m=new Promise(a=>{setTimeout(a,pn).unref()});await Promise.allSettled(f.map(a=>Promise.race([a.exited.then(()=>{}),m])));const v=[];for(const a of f)Rt(a.pid)&&(process.platform==="linux"&&b(x)?await L({operation:"stop",pid:a.pid}):$e(a.snap.tree.length>0?a.snap.tree:[a.pid],"SIGKILL",a.snap.identities),v.push(a));await Promise.all(v.map(a=>a.exited));for(const[a]of l)await st(a,{reason:"daemon-teardown"});u({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:f.length,sigkilled:v.length}})}i(mn,"teardownFleet");function Be(){ye(cr(ce()),{recursive:!0}),ir(ce(),String(process.pid),"utf8")}i(Be,"writePidfile");function je(e){return typeof e=="object"&&e!==null&&e.code==="ENOENT"}i(je,"isMissingPathError");function ze(e){const l=ce();let f;try{f=ar(l,"utf8")}catch(m){if(je(m))return;throw m}if(f.trim()!==String(e))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${e})`);try{sr(l)}catch(m){if(je(m))return;throw m}}i(ze,"removeOwnedPidfileMirror");const fn={acceptsDetachedWork:i(()=>!0,"acceptsDetachedWork"),registerDetached:i(e=>{e.catch(()=>{})},"registerDetached")},K=new et;Zr(e=>K.handleFatalFault(e,!1));const un=new rt,wn=new tt,hn=new nt,vn=new ot,gn=new it,Sn=new at,yn=new dt,_n=new pt,bn=new mt,kn=new ut,Rn=new wt,Pn=new ht,En=new vt,In=new gt,xn=new St,Tn=new yt;async function Cn(e=Date.now(),l=fn){const f=Et(),m=await K.runFleetPreamble(f);let v,a;try{const g=await Ie();v=g.filter(c=>c.status==="active"||c.status==="idle"),a=g.filter(c=>c.status==="done"||c.status==="canceled"||c.status==="dead");const p=new Set(a.map(c=>c.node_id));a.push(...(await Rr()).filter(c=>!p.has(c.node_id)))}catch(g){u({level:"error",event:"canvas.node_list.failed",error:g});return}await K.run(e,{rows:v,fleet:f,lifecycle:l}),await un.run(e,{rows:v});const w=await wn.run(e,{rows:[...v,...a],fleet:f});await hn.run(e),await xn.run(e),await Tn.run(e,{lifecycle:l}),await vn.run(e,{lifecycle:l}),await gn.run(e,{lifecycle:l}),await Sn.run(e,{lifecycle:l}),await yn.run(e,{lifecycle:l}),await _n.run(e,{lifecycle:l,fleet:f}),bn.run(e,{lifecycle:l}),kn.run(e,{lifecycle:l}),Rn.run(e,{lifecycle:l}),Pn.run(e,{lifecycle:l}),await fr(),In.run(e,{fleet:f,lifecycle:l}),await En.run(e,{rows:v}),K.warnIfSaturated({fleet:f,lifecycle:l,thresholds:m,frozen:w})}i(Cn,"superviseTick");function V(e){return e!=="api"}i(V,"roleRunsLanes");function $(e){return e!=="reconcile"}i($,"roleServesRequests");const Dn=66e4;function Ln(e){const l=new Er().registerAll(Ir(()=>null).filter(n=>n.pattern==="/healthz")),f=i((n,d)=>{const y=(n.url??"/").split("?")[0];if((n.method??"GET").toUpperCase()==="GET"&&y==="/healthz"){l.handle(n,d,null,{class:"person"});return}d.writeHead(404,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({error:{code:"not_found",message:"this process serves only /healthz",origin:"runtime"}}))},"handler"),m=Pe();b(m)&&_e(m);const v=J(f),a=new Promise((n,d)=>{v.once("error",d),v.listen(m,()=>{v.off("error",d),be(m,384),n()})});let w,g=null;const p=e.tcp??ke()??se("user").api.tcp;if(p!==void 0&&p!==""){const n=/^(.*):(\d+)$/.exec(p.trim());if(n===null||Number(n[2])>65535)throw new Error(`invalid reconcile probe TCP address: ${p}`);const d=J(f);d.on("error",y=>u({event:"api.server.failed",level:"error",error:y})),d.listen(Number(n[2]),n[1]===""?"0.0.0.0":n[1],()=>{g=`${n[1]===""?"0.0.0.0":n[1]}:${n[2]}`}),w=d}let c=!1;return{ready:a,tcpAddress:i(()=>g,"tcpAddress"),async close(n={}){if(!c){c=!0,await Promise.all([Te(v,new Set,n.graceMs),w===void 0?Promise.resolve():Te(w,new Set,n.graceMs)]);try{b(m)&&_e(m)}catch{}}}}}i(Ln,"createProbeServer");const $n="daemon is draining for shutdown and admits no new work";async function An(e){ye(Ee(),{recursive:!0,mode:493}),process.platform==="linux"&&be(Ee(),493);const l=Pe();if(b(l))throw new Error(`layout refusal cannot bind occupied socket: ${l}`);const f=J((p,c)=>{if(p.method==="GET"&&p.url==="/healthz"){c.writeHead(200,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}c.writeHead(503,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((p,c)=>{f.once("error",c),f.listen(l,()=>{f.off("error",c),p()})});let m;const v=e.tcp??ke();if(v){const p=/^(.*):(\d+)$/.exec(v);if(!p||Number(p[2])>65535)throw new Error(`invalid refusal TCP address: ${v}`);const c=J(f.listeners("request")[0]);await new Promise((n,d)=>{c.once("error",d),c.listen(Number(p[2]),p[1],()=>{c.off("error",d),n()})}),m=c}Be();let a;const w=i(()=>(a??=(async()=>{f.closeAllConnections(),m?.closeAllConnections(),await Promise.all([f,m].filter(p=>p!==void 0).map(p=>new Promise((c,n)=>p.close(d=>d?n(d):c())))),ze(process.pid),process.off("SIGINT",g),process.off("SIGTERM",g)})(),a),"cleanup"),g=i(()=>{w().then(()=>process.exit(0),p=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(p)}
|
|
2
|
-
`),process.exit(1)})},"onSignal");return process.on("SIGINT",g),process.on("SIGTERM",g),w}
|
|
3
|
-
`)},"logPostgres"),
|
|
1
|
+
var er=Object.defineProperty;var s=(e,l)=>er(e,"name",{value:l,configurable:!0});import{applyBootSettings as rr}from"./boot-settings.js";import{startPeerOutbox as tr}from"./peer/outbox.js";import{reportMachineStatus as nr}from"../core/peer/directory.js";import{createPeerRelay as or}from"./peer/relay-service.js";import{writeFileSync as ir,readFileSync as ar,rmSync as sr,mkdirSync as ye,existsSync as k,unlinkSync as _e,chmodSync as be}from"node:fs";import{createServer as J}from"node:http";import{dirname as cr}from"node:path";import{envCrtrdTcp as ke,envDocumentPort as lr}from"../shared/env.js";import{openCanvasConversationStore as dr,serveCanvasConversationStore as mr}from"../core/canvas/sessions.js";import{installConversationStore as pr}from"../core/conversation-store/index.js";import{pollRunObservers as fr}from"./runs/observer.js";import{randomBytes as Re}from"node:crypto";import{apiSocketPath as Pe,crtrHome as ur}from"../core/canvas/paths.js";import{activeAppUsers as wr}from"../core/spaces/users.js";import{ensureAppSpace as hr,ensureUserFiles as vr}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as x}from"../launcher/spec.js";import{launcherRequest as L}from"../launcher/client.js";import{installLauncherEventSink as gr}from"./launcher-events.js";import{initializeEmptyLayout as Sr,layoutNeedsMigration as yr,socketDir as Ee}from"../core/layout.js";import{migrateLooseKeys as _r}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as br}from"../core/profiles/relocate.js";import{getNode as kr,listNodesForSupervision as Ie,listParkedResidentsWithWakeMail as Rr,setFrozen as xe}from"../core/canvas/index.js";import{createApiServer as Pr,closeServer as Te}from"./api/server.js";import{Router as Er}from"./api/router.js";import{healthRoutes as Ir}from"./api/handlers/health.js";import{createDocumentListener as xr}from"./api/document-listener.js";import{documentSecret as Tr}from"./api/document-secret.js";import{createAppListener as Dr}from"./api/app-listener.js";import{registerMachineEnvironment as Cr}from"../core/environments.js";import{registerBlaxelRunnerFromEnv as Lr}from"../core/secrets/blaxel-register.js";import{markCloudRole as $r,resolvePerson as Ar,retireLocalPerson as Nr,setPersonId as Mr}from"../core/canvas/person.js";import{backfillNodeApps as Gr,runPermissionsBoot as Or}from"../core/permissions/boot.js";import{startRunNotices as Fr}from"./run-notices.js";import{startGrantEffects as Br}from"./reconcilers/grant-effects.js";import{createGrantSync as Hr,GRANT_SYNC_INTERVAL_S as jr}from"../core/grants/sync.js";import{sweepSandboxes as zr}from"../core/grants/on-sync.js";import{rebuildCronExecutions as Ur}from"./cron/run-socket.js";import{hasSyncedGrants as qr}from"../core/grants/mirror.js";import{readConfig as se}from"../core/config.js";import{reconcileUndeliveredTickets as Wr}from"./human/sweep.js";import{resetHandedOffDeliveries as Jr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as Kr}from"../core/canvas/daemon-actions.js";import{sweepReviews as Vr}from"./review/sweep.js";import{pidfilePath as ce}from"./pidfile.js";import{rendererWarning as Yr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as Qr}from"./supervise-cadence.js";import{createResumeDetector as Xr}from"./resume-detector.js";import{setFatalFaultAnnouncer as Zr}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as et}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as rt}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as tt}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as nt}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as ot}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as it}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as at}from"./reconcilers/action-lane.js";import{endLiveExecution as st,migrateLegacyExecutionIdentities as ct}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as lt}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as dt}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as mt}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as pt}from"./reconcilers/storage-due-work.js";import{PurgeDueWorkReconciler as ft}from"./reconcilers/purge-due-work.js";import{assertLedgerEnv as ut}from"../core/ledger/rates.js";import{LedgerSandboxReconciler as wt}from"./reconcilers/ledger-sandbox.js";import{LedgerStorageReconciler as ht}from"./reconcilers/ledger-storage.js";import{LedgerR2Reconciler as vt}from"./reconcilers/ledger-r2.js";import{ImportLaneReconciler as gt}from"./reconcilers/import-lane.js";import{BashDeadlineReconciler as St}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as yt}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as _t}from"./reconcilers/structured-request-lane.js";import{RecipeImageReconciler as bt,subscribeImageBuilt as kt}from"./reconcilers/recipe-image.js";import{reconcileStructuredRequestSidecars as Rt}from"./structured-requests/boot.js";import{captureLivenessSnapshot as De,capturePidCommand as Pt,captureTeardownSnapshot as Ce,isPidAlive as Et,isAnyPidAlive as Le,killProcessTreePids as $e}from"../core/canvas/pid.js";import{bindFleet as It,boundFleet as xt}from"../core/runtime/fleet.js";import{bindDaemonControl as Tt,clearDaemonControl as Dt}from"./control.js";import{execDaemon as Ct,spawnDaemon as Lt}from"./manage.js";import{reconcileAndMigrateBoot as $t}from"../core/canvas/boot.js";import{resolveInstallId as At}from"../core/canvas/install-id.js";import{setInstallId as Nt}from"../core/runtime/nodes.js";import{claimDaemonOwnership as Mt,daemonOwnershipApplies as Gt,daemonOwnershipClaimed as Ot,releaseDaemonOwnership as Ae}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as Ft,closeAllDbs as le,openDb as Ne,withCanvasWrite as Bt}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as Ht}from"../core/canvas/import-sqlite.js";import{ensurePostgres as jt,ensurePostgresHealthy as zt,stopPostgres as Ut,withPostgresStartLock as qt}from"./postgres/index.js";import{brokerSignalBus as F}from"./broker-signals/bus.js";import{brokerSignalStreams as Wt}from"./api/handlers/broker-signals.js";import{setRequestsStartRunObservers as Jt}from"./api/handlers/broker-ops.js";import{reconcilePackages as Kt}from"../core/graph/package-reconcile.js";import{startRepoSync as Vt}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as Yt,reapStaleSpares as Qt}from"../core/runtime/warm-pool.js";import{DaemonFleet as Xt}from"./fleet.js";import{copyNodeFilesToRows as Zt}from"../core/runtime/first-boot-rows.js";import{BOOT_MIGRATIONS as en}from"./boot-migrations.js";import{emitEvent as p}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as rn}from"../core/events/source.js";import{activateCanvasImport as tn,MigrationBlockedError as nn,runForcedCanvasImport as on}from"../migrations/activation.js";import{adoptInstallPerson as an,convertLegacyScopedStore as sn,personStoresCopied as cn}from"../core/scoped-state/migrate.js";import{readLegacyUserSnapshot as ln}from"../core/scoped-state/legacy-profiles.js";import{heldPersons as Me,installPreludeSnapshot as dn}from"../core/scoped-state/person-store.js";import{loadPersonSettings as Ge}from"../core/scoped-state/person-rows.js";import{userScopeRoot as Oe}from"../core/scope.js";import{brokerCommandDeclaresCanvasHome as de}from"../core/runtime/broker-ownership.js";import{brokerCommandDeclaresCanvasHome as Oi}from"../core/runtime/broker-ownership.js";function me(e){return/ --epoch (\S+) /.exec(e)?.[1]??null}s(me,"brokerCommandEpoch");const mn=5e3,Fe=150;async function pn(e){const l=De();if(l===null)throw p({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 u=ur(),f=[];for(const[n,d]of l.processes){const y=d.command;if(d.zombie||!y.includes("broker-cli.js")||!de(y,u)||me(y)===e)continue;const b=y.trim().split(/\s+/).at(-1)??"";b===""||b.endsWith("broker-cli.js")||f.push({pid:n,nodeId:b})}let v=0;const a=[];for(const n of f){const d=Pt(n.pid);if(d===null||!d.includes("broker-cli.js")||!de(d,u)||me(d)===e||d.trim().split(/\s+/).at(-1)!==n.nodeId)continue;const y=Ce(n.pid,null);if(!y.reused){try{process.platform==="linux"&&k(x)?await L({operation:"stop",pid:n.pid}):process.kill(n.pid,"SIGTERM")}catch{continue}v++,a.push({pid:n.pid,snap:y})}}const w=Date.now()+mn;for(;Date.now()<w&&a.some(n=>Le(n.snap.tree.length>0?n.snap.tree:[n.pid]));)await new Promise(n=>setTimeout(n,Fe));let g=0;for(const n of a){const d=n.snap.tree.length>0?n.snap.tree:[n.pid];Le(d)&&(process.platform==="linux"&&k(x)?await L({operation:"stop",pid:n.pid}):$e(d,"SIGKILL",n.snap.identities),g++)}g>0&&await new Promise(n=>setTimeout(n,Fe));const m=De();if(m===null)throw p({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const c=[];for(const[n,d]of m.processes){const y=d.command;d.zombie||!y.includes("broker-cli.js")||de(y,u)&&me(y)!==e&&c.push(n)}if(c.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${c.join(", ")}`);p({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:f.length,sigtermed:v,sigkilled:g}})}s(pn,"reapPriorEpochBrokers");const fn=5e3;async function un(e){const l=Array.from(e.entries());if(l.length===0)return;for(const[a]of l){const w=(await kr(a))?.status;(w==="active"||w==="idle")&&await xe(a,new Date().toISOString())}const u=l.map(([,a])=>({pid:a.pid,snap:Ce(a.pid,null),exited:a.handle.exited}));for(const a of u)if(!a.snap.reused)try{process.platform==="linux"&&k(x)?await L({operation:"stop",pid:a.pid}):process.kill(a.pid,"SIGTERM")}catch{}const f=new Promise(a=>{setTimeout(a,fn).unref()});await Promise.allSettled(u.map(a=>Promise.race([a.exited.then(()=>{}),f])));const v=[];for(const a of u)Et(a.pid)&&(process.platform==="linux"&&k(x)?await L({operation:"stop",pid:a.pid}):$e(a.snap.tree.length>0?a.snap.tree:[a.pid],"SIGKILL",a.snap.identities),v.push(a));await Promise.all(v.map(a=>a.exited));for(const[a]of l)await st(a,{reason:"daemon-teardown"});p({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:u.length,sigkilled:v.length}})}s(un,"teardownFleet");function Be(){ye(cr(ce()),{recursive:!0}),ir(ce(),String(process.pid),"utf8")}s(Be,"writePidfile");function He(e){return typeof e=="object"&&e!==null&&e.code==="ENOENT"}s(He,"isMissingPathError");function je(e){const l=ce();let u;try{u=ar(l,"utf8")}catch(f){if(He(f))return;throw f}if(u.trim()!==String(e))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${e})`);try{sr(l)}catch(f){if(He(f))return;throw f}}s(je,"removeOwnedPidfileMirror");const wn={acceptsDetachedWork:s(()=>!0,"acceptsDetachedWork"),registerDetached:s(e=>{e.catch(()=>{})},"registerDetached")},K=new et;Zr(e=>K.handleFatalFault(e,!1));const hn=new rt,vn=new tt,gn=new nt,Sn=new ot,yn=new it,_n=new at,bn=new dt,kn=new mt,Rn=new pt,Pn=new ft,En=new wt,In=new ht,xn=new vt,Tn=new gt,Dn=new St,Cn=new yt,Ln=new _t,$n=new bt;async function An(e=Date.now(),l=wn){const u=xt(),f=await K.runFleetPreamble(u);let v,a;try{const g=await Ie();v=g.filter(c=>c.status==="active"||c.status==="idle"),a=g.filter(c=>c.status==="done"||c.status==="canceled"||c.status==="dead");const m=new Set(a.map(c=>c.node_id));a.push(...(await Rr()).filter(c=>!m.has(c.node_id)))}catch(g){p({level:"error",event:"canvas.node_list.failed",error:g});return}await K.run(e,{rows:v,fleet:u,lifecycle:l}),await hn.run(e,{rows:v});const w=await vn.run(e,{rows:[...v,...a],fleet:u});await gn.run(e),await Ln.run(e),await $n.run(e,{lifecycle:l}),await Sn.run(e,{lifecycle:l}),await yn.run(e,{lifecycle:l}),await _n.run(e,{lifecycle:l}),await bn.run(e,{lifecycle:l}),await kn.run(e,{lifecycle:l,fleet:u}),Rn.run(e,{lifecycle:l}),Pn.run(e,{lifecycle:l}),En.run(e,{lifecycle:l}),In.run(e,{lifecycle:l}),xn.run(e,{lifecycle:l}),Tn.run(e,{lifecycle:l}),await fr(),Cn.run(e,{fleet:u,lifecycle:l}),await Dn.run(e,{rows:v}),K.warnIfSaturated({fleet:u,lifecycle:l,thresholds:f,frozen:w})}s(An,"superviseTick");function V(e){return e===void 0||e==="reconcile"}s(V,"roleRunsLanes");function $(e){return e===void 0||e==="api"}s($,"roleServesRequests");const Nn=66e4;function Mn(e){const l=new Er().registerAll(Ir(()=>null).filter(n=>n.pattern==="/healthz")),u=s((n,d)=>{const y=(n.url??"/").split("?")[0];if((n.method??"GET").toUpperCase()==="GET"&&y==="/healthz"){l.handle(n,d,null,{class:"person"});return}d.writeHead(404,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({error:{code:"not_found",message:"this process serves only /healthz",origin:"runtime"}}))},"handler"),f=Pe();k(f)&&_e(f);const v=J(u),a=new Promise((n,d)=>{v.once("error",d),v.listen(f,()=>{v.off("error",d),be(f,384),n()})});let w,g=null;const m=e.tcp??ke()??se("user").api.tcp;if(m!==void 0&&m!==""){const n=/^(.*):(\d+)$/.exec(m.trim());if(n===null||Number(n[2])>65535)throw new Error(`invalid reconcile probe TCP address: ${m}`);const d=J(u);d.on("error",y=>p({event:"api.server.failed",level:"error",error:y})),d.listen(Number(n[2]),n[1]===""?"0.0.0.0":n[1],()=>{g=`${n[1]===""?"0.0.0.0":n[1]}:${n[2]}`}),w=d}let c=!1;return{ready:a,tcpAddress:s(()=>g,"tcpAddress"),async close(n={}){if(!c){c=!0,await Promise.all([Te(v,new Set,n.graceMs),w===void 0?Promise.resolve():Te(w,new Set,n.graceMs)]);try{k(f)&&_e(f)}catch{}}}}}s(Mn,"createProbeServer");const Gn="daemon is draining for shutdown and admits no new work";async function On(e){ye(Ee(),{recursive:!0,mode:493}),process.platform==="linux"&&be(Ee(),493);const l=Pe();if(k(l))throw new Error(`layout refusal cannot bind occupied socket: ${l}`);const u=J((m,c)=>{if(m.method==="GET"&&m.url==="/healthz"){c.writeHead(200,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}c.writeHead(503,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((m,c)=>{u.once("error",c),u.listen(l,()=>{u.off("error",c),m()})});let f;const v=e.tcp??ke();if(v){const m=/^(.*):(\d+)$/.exec(v);if(!m||Number(m[2])>65535)throw new Error(`invalid refusal TCP address: ${v}`);const c=J(u.listeners("request")[0]);await new Promise((n,d)=>{c.once("error",d),c.listen(Number(m[2]),m[1],()=>{c.off("error",d),n()})}),f=c}Be();let a;const w=s(()=>(a??=(async()=>{u.closeAllConnections(),f?.closeAllConnections(),await Promise.all([u,f].filter(m=>m!==void 0).map(m=>new Promise((c,n)=>m.close(d=>d?n(d):c())))),je(process.pid),process.off("SIGINT",g),process.off("SIGTERM",g)})(),a),"cleanup"),g=s(()=>{w().then(()=>process.exit(0),m=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(m)}
|
|
2
|
+
`),process.exit(1)})},"onSignal");return process.on("SIGINT",g),process.on("SIGTERM",g),w}s(On,"serveLayoutRefusal");async function Fn(e={}){if(e.role==="machine")return(await import("./machine-daemon.js")).runMachineDaemon();if(yr())return On(e);Sr();const l=s(r=>{process.stderr.write(`[crtrd] ${r}
|
|
3
|
+
`)},"logPostgres"),u=!!process.env.CRTR_TEST_PG_URL,f=Gt(e.role);Jt(V(e.role));const v=s(async()=>f?await Mt(Re(16).toString("hex")):{claimed:!0,claim:{pid:process.pid,pid_identity:"",token:Re(16).toString("hex")}},"claimOwnership"),{database:a,ownership:w}=u?{database:{url:await Ft()},ownership:await v()}:await qt(async()=>({database:await jt({log:l,startLockHeld:!0}),ownership:await v()}));if(!w.claimed){if(w.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${w.incumbentPid??"?"})
|
|
4
4
|
`);else{const r=[w.incumbentPid!==void 0?`pid ${w.incumbentPid}`:void 0,w.verdict!==void 0?`verdict ${w.verdict}`:void 0].filter(h=>h!==void 0),o=r.length>0?` (${r.join(", ")})`:"";process.stderr.write(`[crtrd] claim lost: ${w.reason}${o}
|
|
5
|
-
`),w.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"),(w.reason==="own_identity_indeterminate"||w.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const g=w.claim;let
|
|
5
|
+
`),w.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"),(w.reason==="own_identity_indeterminate"||w.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const g=w.claim;let m=null,c=null,n=null,d,y="";try{f&&await pn(g.token);for(const t of br())process.stderr.write(`[crtrd] ${t}
|
|
6
6
|
`);for(const t of await _r())process.stderr.write(`[crtrd] ${t}
|
|
7
|
-
`);await Ne(),await
|
|
8
|
-
`);for(const t of
|
|
9
|
-
`);for(const t of
|
|
10
|
-
`);for(const t of
|
|
11
|
-
`);for(const t of
|
|
12
|
-
`);if(y=await
|
|
13
|
-
`)}}catch(r){await F.close();try{if(
|
|
7
|
+
`);await Ne(),await Ht(a.url,l);const r=e.role===void 0&&!await cn();let o=null;r&&(sn({kind:"user",root:Oe()}),o=ln(Oe())),e.role===void 0&&dn(o??{profiles:[],defaults:{},settings:{},state:{}}),await F.listen(a.url);const h=s(t=>{Ge(t).catch(O=>p({level:"error",event:"daemon.settings.reload_failed",error:O}))},"reloadPerson");d=await F.subscribe(t=>{Me().includes(t)&&h(t)},{kinds:["settings"],onListenStart:s(()=>{for(const t of Me())h(t)},"onListenStart")}),e.role!==void 0&&$r();const i=await Bt(async t=>(await Gr(t),await Kt(t)));for(const t of i.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${t.message}
|
|
8
|
+
`);for(const t of i.invalid)process.stderr.write(`[crtrd] package file is not a document: ${t.path}: ${t.error}
|
|
9
|
+
`);for(const t of i.quarantined)process.stderr.write(`[crtrd] plugin "${t.name}" has a damaged ${t.file} (${t.reason}); ${t.movedTo!==void 0?`moved to ${t.movedTo}`:"left in place and skipped"}
|
|
10
|
+
`);for(const t of i.kinds.refused)process.stderr.write(`[crtrd] plugin "${t.plugin}" kind ${t.kind} not registered: ${t.message}
|
|
11
|
+
`);for(const t of i.kinds.invalid)process.stderr.write(`[crtrd] plugin "${t.plugin}" declares invalid object kinds or scopes; no kinds registered and no app token: ${t.error}
|
|
12
|
+
`);if(y=await At(),Nt(y),e.role===void 0&&(m=await Ar(),m!==null)){if(Mr(m.id),await an(m.id),process.platform==="darwin"){const{readConfig:t,updateConfig:O}=await import("../core/config.js");if(t("user").paths?.user_files===void 0){const{homedir:Ze}=await import("node:os");await O("user",Se=>{Se.paths??={},Se.paths.user_files??=Ze()})}}c=await Nr(),c.joinedFrom!==null&&await Ge(c.person),await rr(),n=await Or()}if(process.platform==="linux"&&k(x)&&(gr(),await L({operation:"users.sync",users:await wr()})),f){await lt(g.token);const t=await Zt();Object.values(t).some(O=>O>0)&&process.stderr.write(`[crtrd] node files copied into rows: ${JSON.stringify(t)}
|
|
13
|
+
`)}}catch(r){await F.close();try{if(f){const{released:o}=await Ae(g);if(!o)throw new Error("daemon ownership claim was not released")}await le()}catch(o){throw new AggregateError([r,o],"daemon startup failed and cleanup also failed")}throw r}let b=!0,Y,B=Promise.resolve(),Q=!1,H,X,Z,T;const j=new Set;let ee=!1,pe=!1,P,D,re,z=!1,U,A,N,M,te,ne,oe;const ze=Xr();let E,S={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},C="initial startup activation",Ue=null;function qe(){return S.phase==="ready"?null:{phase:S.phase,message:S.message,blockerPaths:S.blockerPaths}}s(qe,"startupBlock");function R(r){j.add(r),r.finally(()=>{j.delete(r)}).catch(()=>{})}s(R,"registerDetached");const fe={acceptsDetachedWork:s(()=>b,"acceptsDetachedWork"),registerDetached:R};V(e.role)&&await Vt(fe,Ne);async function We(){try{b=!1,Wt.close(),X?.(),X=void 0,Z?.(),d?.(),Z=void 0,ne?.(),ne=void 0,await F.close(),H!==void 0&&(clearInterval(H),H=void 0),N!==void 0&&(clearInterval(N),N=void 0),oe?.(),oe=void 0;let r;do r=B,await r;while(r!==B);for(;j.size>0;)await Promise.allSettled([...j]);if(T!==void 0&&(f&&await un(T),await T.exitsSettled(),T.dispose()),re!==void 0&&await re.close(),D!==void 0&&!z&&(z=!0,await D.close()),te?.(),te=void 0,M!==void 0){const o=M;M=void 0,await o.stop()}if(A!==void 0){const o=A;A=void 0,await o.close()}if(U!==void 0){const o=U;U=void 0,await o.close()}if(ee||(je(process.pid),ee=!0),ee&&!pe){if(f){const{released:o}=await Ae(g);if(!o)throw new Error("daemon ownership claim was not released")}pe=!0,await le()}}finally{Dt(),process.off("SIGINT",he),process.off("SIGTERM",ve),process.off("exit",ge)}}s(We,"cleanupAttempt");const q=1e3;let ue=!1;function Je(){return ue?{graceMs:q}:(ue=!0,_.fresh(()=>{p({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:q}})}),setTimeout(()=>{Ke()},q),{graceMs:q})}s(Je,"requestHandover");async function Ke(){try{await I()}catch(r){const o=r instanceof Error?r.stack??r.message:String(r);process.stderr.write(`[crtrd] handover cleanup failed:
|
|
14
14
|
${o}
|
|
15
|
-
`),process.exit(1)}try{process.platform==="linux"&&
|
|
16
|
-
`),process.exit(1)}process.exit(0)}
|
|
15
|
+
`),process.exit(1)}try{process.platform==="linux"&&k(x)&&(await L({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await Ct();const r=await Lt({waitForJoinLock:!0});_.fresh(()=>{p({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(()=>{p({level:"error",event:"daemon.handover.successor_failed",error:r})});const o=r instanceof Error?r.message:String(r);process.stderr.write(`[crtrd] handover successor failed to start: ${o}
|
|
16
|
+
`),process.exit(1)}process.exit(0)}s(Ke,"performHandover");function I(){if(P!==void 0)return P;const r=We();return P=r,r.then(()=>{P===r&&(P=void 0)},()=>{P===r&&(P=void 0)}),r}s(I,"requestCleanup");async function Ve(){S={phase:"initializing",message:Gn,blockerPaths:[]},D!==void 0&&!z&&(z=!0,await D.close({graceMs:Nn}))}s(Ve,"drainApi");async function we(r){try{r==="SIGTERM"&&e.role==="api"&&await Ve(),await I(),await Ut({claimed:s(async()=>{try{return f&&await Ot()}finally{await le()}},"claimed")}),process.exit(0)}catch(o){process.exitCode=1;const h=o instanceof Error?o.stack??`${o.name}: ${o.message}`:String(o);process.stderr.write(`[crtrd] ${r} cleanup failed:
|
|
17
17
|
${h}
|
|
18
|
-
`),process.exit(1)}}
|
|
18
|
+
`),process.exit(1)}}s(we,"handleSignal");function he(){we("SIGINT")}s(he,"onSigint");function ve(){we("SIGTERM")}s(ve,"onSigterm");function ge(){I().catch(()=>{})}s(ge,"onExit");async function ie(r){b&&(B=B.then(r).then(()=>{},o=>{_.fresh(()=>{p({level:"error",event:"daemon.lane_job.failed",error:o})})}))}s(ie,"enqueue");async function W(){!b||Q||(ze.check()&&E&&(p({level:"info",event:"daemon.resume.detected"}),R(E().catch(()=>{}))),Q=!0,await ie(async()=>{if(Q=!1,!!b)try{u||await zt({log:l}),await An(Date.now(),fe)}catch(r){_.fresh(()=>{p({level:"error",event:"daemon.tick.failed",error:r})})}}))}s(W,"enqueueTick");let G=null;async function Ye(){if(G!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${G instanceof Error?G.message:String(G)}`};if(S.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(S.phase!=="prepared"&&S.phase!=="recovering")throw{status:409,code:"not_prepared",message:S.message};const r=S.phase==="prepared";try{return await ae(),S={phase:"ready",message:"",blockerPaths:[]},r?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(o){throw G=o,{status:500,code:"admission_failed",message:`daemon admission failed: ${o instanceof Error?o.message:String(o)}`}}}s(Ye,"admitStartup");async function Qe(r){if(C!==null)throw{status:409,code:"migration_in_progress",message:`the canvas import is already running (${C})`};C="repair";try{const o=await on({dryRun:r.dryRun});if(r.dryRun||o.pending||S.phase!=="blocked")return{result:o,resumed:!1};if(e.prepare)return S={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:o,resumed:!1};try{return await ae(),S={phase:"ready",message:"",blockerPaths:[]},{result:o,resumed:!0}}catch(h){const i=h instanceof Error?h.message:String(h);return setTimeout(()=>{I().catch(()=>{})},1e3),{result:o,resumed:!1,resumeError:i}}}finally{C=null}}s(Qe,"repairMigrations");async function Xe(){Y=e.intervalMs??Qr;const r=g.token;T=new Xt({epoch:r,enqueue:ie}),It(T),S={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{p({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:Y}})});const o=y;_.fresh(()=>{p({level:"info",event:"daemon.install_id.resolved",fields:{install_id:o}})});const h=await Cr(o);if(_.fresh(()=>{p({level:"info",event:"daemon.environment.registered",fields:{environment_id:h.environmentId,created:h.created}})}),_.fresh(()=>{let i;try{i=Yr()}catch{return}i!==null&&p({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:i.slice(0,2048)}})}),ut(),e.role!==void 0&&!process.env.CRTR_DIRECTORY_URL)throw new Error("CRTR_DIRECTORY_URL must be set on a cloud role (--role api|reconcile): it names the directory this deployment mints tokens against");if(e.role!==void 0&&Lr(),!V(e.role)){S={phase:"ready",message:"",blockerPaths:[]};return}if(await _.fresh(async()=>{await Vr(),await Wr(),await Jr(Date.now()),await Kr(Date.now())}),await _.fresh(async()=>{const{reconcile:i,migrated:t}=await $t(ct);i.changed&&p({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:i.cleared.length,same_boot_proven:i.sameBootProven}}),t.length>0&&p({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:t.length}})}),await _.fresh(async()=>{for(const i of await Ie())i.status!=="active"||i.pi_pid!=null||i.frozen_at!=null||i.respawn_not_before!=null||(await xe(i.node_id,new Date().toISOString()),p({level:"warn",event:"daemon.startup.launch_debt_recorded",node_id:i.node_id}))}),await _.fresh(async()=>{await Rt();for(const i of en){const t=await i.apply();t.changed&&p({level:"info",event:"daemon.boot_migration.applied",fields:{migration:i.id,...t.summary}})}}),R(Ur()),E){const i=s(()=>{R(E().catch(()=>{})),R(nr().then(t=>M?.observe(t)).then(()=>{},t=>{p({level:"warn",event:"daemon.peer.status_failed",error:t})}))},"refresh");R(zr().then(({completion:t})=>t).catch(t=>{p({level:"error",event:"daemon.grants.sweep_failed",error:t})})),i(),N=setInterval(i,jr*1e3),N.unref(),oe=tr({joined:!0,register:R})}await ie(async()=>{await Qt(),await Yt()}),H=setInterval(()=>{W().catch(i=>p({level:"error",event:"daemon.tick.failed",error:i}))},Y),X=await kt(()=>{W().catch(i=>p({level:"error",event:"daemon.tick.failed",error:i}))}),Z=await F.subscribe(()=>{W().catch(i=>p({level:"error",event:"daemon.tick.failed",error:i}))},{kinds:["spend_limits"]}),await W(),e.role===void 0&&import("../commands/sys/import/move.js").then(({settleMoveAtDaemonStart:i})=>i(()=>!b,t=>p({level:"warn",event:"daemon.move.settle_failed",error:t}))).catch(i=>p({level:"warn",event:"daemon.move.settle_failed",error:i}))}s(Xe,"completeStartup");function ae(){return Ue??=Xe().catch(r=>{throw S={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},r})}s(ae,"resumeStartup");try{process.on("SIGINT",he),process.on("SIGTERM",ve),process.on("exit",ge),rn(),m!==null&&p({level:"info",event:"daemon.person.resolved",fields:{person_id:m.id,source:m.source}}),n!==null&&p({level:"info",event:"daemon.permissions.boot",fields:{...n}}),c!==null&&p({level:"info",event:"daemon.person.retired",fields:{person_id:c.person,joined_from:c.joinedFrom,retired:c.retired,columns:c.columns}}),$(e.role)?U=await mr():pr(dr());const r=g.token;Tt({epoch:r,requestHandover:Je,registerDetached:R,startupBlock:qe,admitStartup:Ye,repairMigrations:Qe}),Be(),D=$(e.role)?Pr({tcp:e.tcp}):Mn(e),await D.ready;const o=lr()??se("user").document_port;if($(e.role)){const i=xr(o,Tr());try{await i.ready,re=i}catch(t){p({level:"warn",event:"daemon.document_listener.bind_failed",fields:{port:o,error:t instanceof Error?t.message:String(t)}})}}const h=se("user").runtime;if(h?.issuer&&(E=Hr(h.issuer,R),h.unsandboxed===!0&&(M=or({syncGrants:E}))),$(e.role)&&(te=await Fr()),ne=await Br({serves:$(e.role),runsLanes:V(e.role)}),h?.unsandboxed===!0&&h.listener!==void 0&&p({level:"warn",event:"daemon.app_listener.refused_unsandboxed",fields:{reason:"runtime.unsandboxed is set; runtime.listener is ignored"}}),$(e.role)&&h!==void 0&&h.listener!==void 0&&h.unsandboxed!==!0&&(A=await Dr(h,async()=>await qr(h.issuer.replace(/\/$/,"")),E),await A.ready),e.role===void 0)try{await tn()}catch(i){if(C=null,i instanceof nn)return S={phase:"blocked",message:i.message,blockerPaths:i.blockerPaths},I;throw i}process.platform==="linux"&&k(x)&&(vr(),await hr("app:terminal"));try{e.prepare?S={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await ae(),S={phase:"ready",message:"",blockerPaths:[]})}finally{C=null}return I}catch(r){try{await I()}catch(o){throw new AggregateError([r,o],"daemon startup failed and cleanup also failed")}throw r}}s(Fn,"runDaemon");var Ni=Fn;export{Nn as API_DRAIN_GRACE_MS,Oi as brokerCommandDeclaresCanvasHome,me as brokerCommandEpoch,Ni as default,V as roleRunsLanes,$ as roleServesRequests,Fn as runDaemon,An as superviseTick,un as teardownFleet};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** How long turns in flight get to settle before their broker is stopped. */
|
|
2
|
+
export declare const DRAIN_SETTLE_LIMIT_MS: number;
|
|
3
|
+
export interface DrainStateDTO {
|
|
4
|
+
drained: boolean;
|
|
5
|
+
/** When the drain began (ISO), null when not drained. */
|
|
6
|
+
since: string | null;
|
|
7
|
+
/** Nodes with a turn in flight and a live broker right now. */
|
|
8
|
+
busy_nodes: string[];
|
|
9
|
+
/** Brokers stopped as a dormant exit because their turn outlived the limit (set by the call that waited). */
|
|
10
|
+
stopped_nodes: string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function drainedSince(): Promise<string | null>;
|
|
13
|
+
export declare function isDrained(): Promise<boolean>;
|
|
14
|
+
/** Nodes mid-turn with a live broker: an open turn and a recorded pid that answers (the roster's own definition of busy). */
|
|
15
|
+
export declare function busyNodes(): Promise<string[]>;
|
|
16
|
+
/** Begin the drain. Idempotent: a second call keeps the first call's start time. */
|
|
17
|
+
export declare function startDrain(now?: Date): Promise<DrainStateDTO>;
|
|
18
|
+
export declare function endDrain(): Promise<DrainStateDTO>;
|
|
19
|
+
export declare function drainState(stopped?: string[]): Promise<DrainStateDTO>;
|
|
20
|
+
/** Wait for turns in flight to settle; past `limitMs`, stop each remaining broker as a dormant exit (the node is released, so
|
|
21
|
+
* the lifecycle lane does not respawn it as a crash, and its mail stays pending). */
|
|
22
|
+
export declare function settleDrain(opts?: {
|
|
23
|
+
limitMs?: number;
|
|
24
|
+
pollMs?: number;
|
|
25
|
+
}): Promise<DrainStateDTO>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
var m=Object.defineProperty;var n=(t,e)=>m(t,"name",{value:e,configurable:!0});import{openDb as o}from"../core/canvas/db.js";import{isPidPresent as _}from"../core/canvas/pid.js";import{emptyWarmPool as f}from"../core/runtime/warm-pool.js";import{requestBrokerTeardown as E}from"../core/runtime/host.js";import{transition as N}from"../core/runtime/lifecycle.js";import{emitEvent as i}from"../core/events/emit.js";const s="drain",D=10*6e4,L=1e3;async function l(){return(await(await o()).prepare("SELECT value FROM runtime_state WHERE key = ?").get(s))?.value??null}n(l,"drainedSince");async function T(){return await l()!==null}n(T,"isDrained");async function d(){return(await(await o()).prepare(`
|
|
2
|
+
SELECT node_id, pi_pid
|
|
3
|
+
FROM node_rows
|
|
4
|
+
WHERE node_id NOT IN (SELECT node_id FROM warm_pool)
|
|
5
|
+
AND kind != 'human'
|
|
6
|
+
AND pi_pid IS NOT NULL
|
|
7
|
+
AND turn_open_at IS NOT NULL
|
|
8
|
+
AND turn_closed_at IS NULL
|
|
9
|
+
`).all()).filter(e=>_(e.pi_pid)).map(e=>e.node_id)}n(d,"busyNodes");async function k(t=new Date){return await(await o()).prepare("INSERT INTO runtime_state (key, value) VALUES (?, ?) ON CONFLICT(key) DO NOTHING").run(s,t.toISOString()),await f(),i({level:"info",event:"drain.started"}),await p()}n(k,"startDrain");async function R(){return await(await o()).prepare("DELETE FROM runtime_state WHERE key = ?").run(s),i({level:"info",event:"drain.ended"}),await p()}n(R,"endDrain");async function p(t=[]){const e=await l();return{drained:e!==null,since:e,busy_nodes:await d(),stopped_nodes:t}}n(p,"drainState");async function x(t={}){const e=t.limitMs??D,u=t.pollMs??L;if(!await T())throw new Error("settleDrain: the daemon is not drained");const c=Date.now()+e;let a=await d();for(;a.length>0&&Date.now()<c;)await new Promise(r=>setTimeout(r,u)),a=await d();for(const r of a){await N(r,"release");const w=await E(r,{reason:"import-drain"});if(!w.confirmed)throw new Error(`drain: the broker of ${r} could not be confirmed stopped: ${w.error?.message??"unknown"}`);i({level:"warn",event:"drain.broker_stopped",node_id:r})}return await p(a)}n(x,"settleDrain");export{D as DRAIN_SETTLE_LIMIT_MS,d as busyNodes,p as drainState,l as drainedSince,R as endDrain,T as isDrained,x as settleDrain,k as startDrain};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Every lane `superviseTick` runs, named as in crtrd.ts. Keep in tick order. */
|
|
2
|
-
export declare const TICK_LANES: readonly ["brokerSupervision", "controllerDeath", "nodeLifecycle", "nodeDeadline", "structuredRequests", "recipeImage", "cronLane", "deliveryLane", "actionLane", "pendingReviewSubmit", "storageMaintenance", "storageDueWork", "pollRunObservers", "faultRetryBackstop", "bashDeadline", "ledgerSandbox", "ledgerStorage", "ledgerR2"];
|
|
2
|
+
export declare const TICK_LANES: readonly ["brokerSupervision", "controllerDeath", "nodeLifecycle", "nodeDeadline", "structuredRequests", "recipeImage", "cronLane", "deliveryLane", "actionLane", "pendingReviewSubmit", "storageMaintenance", "storageDueWork", "purgeDueWork", "pollRunObservers", "faultRetryBackstop", "bashDeadline", "ledgerSandbox", "ledgerStorage", "ledgerR2", "importLane"];
|
|
3
3
|
export interface DueSource {
|
|
4
4
|
/** Epoch ms of the earliest timed work this lane owes; `now` when work is due already; null when none. */
|
|
5
5
|
nextDue(now: number): Promise<number | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var d=Object.defineProperty;var r=(e,n)=>d(e,"name",{value:n,configurable:!0});const
|
|
1
|
+
var d=Object.defineProperty;var r=(e,n)=>d(e,"name",{value:n,configurable:!0});const f=["brokerSupervision","controllerDeath","nodeLifecycle","nodeDeadline","structuredRequests","recipeImage","cronLane","deliveryLane","actionLane","pendingReviewSubmit","storageMaintenance","storageDueWork","purgeDueWork","pollRunObservers","faultRetryBackstop","bashDeadline","ledgerSandbox","ledgerStorage","ledgerR2","importLane"],u=new Map,a=new Map;function y(e,n){u.set(e,n)}r(y,"registerDueSource");function g(e,n){a.set(e,n)}r(g,"declareBusyOnly");function x(){return[...u.keys()]}r(x,"dueSourceLanes");function D(){return[...a.keys()]}r(D,"busyOnlyLanes");async function b(e,n=()=>{}){let o=null,s=!1;for(const[c,l]of u){let t;try{t=await l.nextDue(e)}catch(i){n(c,i),t=e}t===null||!Number.isFinite(t)||(t<=e&&(s=!0),(o===null||t<o)&&(o=t))}return{dueNow:s,nextDueMs:o}}r(b,"collectDue");export{f as TICK_LANES,D as busyOnlyLanes,b as collectDue,g as declareBusyOnly,x as dueSourceLanes,y as registerDueSource};
|