@north-light/cli 0.3.562 → 0.3.564

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.
@@ -22,6 +22,7 @@ export declare function declaredExtensibleCoreBranches(): Promise<ReadonlySet<st
22
22
  export declare function personOnlyInvocation(argv: readonly string[]): string | null;
23
23
  /** Select a smaller canonical tree; `runCli` still parses, renders help, and reports errors. */
24
24
  export declare function isDaemonControlInvocation(argv: readonly string[]): boolean;
25
+ export declare function isJoinInvocation(argv: readonly string[]): boolean;
25
26
  export declare function isLayoutMigrationInvocation(argv: readonly string[]): boolean;
26
27
  /** Assemble the canonical daemon-control definitions without loading settings or plugin discovery. This path exists so `crtr sys daemon restart` can start the new generation that converts pre-SQL user state. */
27
28
  export declare function resolveDaemonControlRoot(): Promise<RootDef>;
@@ -1 +1 @@
1
- var I=Object.defineProperty;var o=(e,t)=>I(e,"name",{value:t,configurable:!0});import{defineBranch as D,defineRoot as y,GLOBAL_TOKENS as O}from"./core/command.js";import{createCoreHookCatalog as x}from"./core/command-hooks/catalog.js";import{composeCoreHooks as R}from"./core/command-hooks/compose.js";import{mark as p}from"./core/timing.js";import{envNodeId as G,envScopes as U,isSandboxedNode as E,scopeAllowed as q}from"./shared/env.js";const S="crtr: agentic runtime.",b=[{name:"--json",desc:"stdout as the leaf's declared outputs in raw JSON instead of prose \u2014 for scripts (a cron's bash, external tooling) that must branch on canvas state; prose stays the contract for what you read yourself."}],k=Object.assign(Object.create(null),{doc:o(async()=>(await import("./commands/doc.js")).registerDoc(),"doc"),"custom-object":o(async()=>(await import("./commands/custom-object.js")).registerCustomObject(),"custom-object"),profile:o(async()=>(await import("./commands/profile.js")).registerProfile(),"profile"),pkg:o(async()=>(await import("./commands/pkg.js")).registerPkg(),"pkg"),human:o(async()=>(await import("./commands/human.js")).registerHuman(),"human"),sys:o(async()=>(await import("./commands/sys.js")).registerSys(),"sys"),node:o(async()=>(await import("./commands/node.js")).registerNode(),"node"),push:o(async()=>(await import("./commands/push.js")).registerPush(),"push"),cron:o(async()=>(await import("./commands/cron.js")).registerCron(),"cron"),canvas:o(async()=>(await import("./commands/canvas.js")).registerCanvas(),"canvas"),surface:o(async()=>(await import("./commands/surface.js")).registerSurface(),"surface")}),m=Object.freeze(Object.keys(k)),z={human:["crtr:act"],cron:["crtr:schedule"],doc:["crtr:memory:read:user","crtr:memory:rw:user"]};function B(e){const t=U();return t===null?e:e.filter(r=>{const n=z[r.name];return n===void 0||n.some(s=>q(t,s))||r.name==="doc"&&process.env.CRTR_GRANTEE?.startsWith("app:")===!0})}o(B,"allowedSubtrees");const N=new Map;function L(e){let t=N.get(e);return t===void 0&&(t=k[e](),N.set(e,t)),t}o(L,"loadSubtree");function v(e){return Promise.all(m.filter(t=>e.has(t)).map(L))}o(v,"loadCoreSubtreesNamed");function C(){return v(new Set(m))}o(C,"loadCoreSubtrees");function P(e){const t=new Set;for(const r of e){const n=r.split(" ")[0];m.includes(n)&&t.add(n)}return t}o(P,"subtreesNamedBy");let j;function re(){return p("cli.hooks.catalog"),j??=C().then(x),j}o(re,"coreHookCatalog");function T(){let e;return()=>(e??=(async()=>{const{compileHookRegistry:t,declaredCommandHookTargets:r,effectiveHookPlugins:n}=await import("./core/command-hooks/discovery.js"),s=n(),a=E()&&G()!==void 0?await(await import("./core/command-hooks/transport/plan.js")).remoteHookPlan(process.cwd()):[];if(p("cli.hooks.effective_plugins",{count:s.length+a.length}),s.length===0&&a.length===0)return p("cli.hooks.empty_registry"),t(x([]),[]);const c=E()?s.filter(l=>l.scope==="project"):s,i=P([...r(c),...a.map(l=>l.target)]);return p("cli.hooks.catalog",{subtrees:[...i].join(",")}),t(x(await v(i)),c,a)})(),e)}o(T,"hookRegistryLoader");function g(e){const t=new Set,r=o((n,s)=>{if(!(n.kind==="branch"&&n.passthrough!==void 0)&&(t.add(s),n.kind==="branch"))for(const a of n.children)r(a,`${s} ${a.name}`)},"visit");for(const n of e)r(n,n.name);return t}o(g,"commandPathsFor");async function se(){return g(await C())}o(se,"coreCommandPaths");async function ae(e){return g(await v(P(e)))}o(ae,"coreCommandPathsCovering");function W(e){const t=[],r=o((n,s)=>{n.extensible===!0&&t.push(s);for(const a of n.children)a.kind==="branch"&&r(a,[...s,a.name])},"visit");for(const n of e)r(n,[n.name]);return t}o(W,"extensibleBranchPaths");const $=Object.freeze(["human","sys tutorial"]);async function _(){return new Set($)}o(_,"extensibleCoreBranches");async function ie(){return new Set(W(await C()).map(e=>e.join(" ")))}o(ie,"declaredExtensibleCoreBranches");function F(e,t){const{listing:r,...n}=e.help;return D({name:e.name,description:e.description,whenToUse:e.whenToUse,tier:e.tier,help:n,rootEntry:e.rootEntry,extensible:e.extensible,plugin:e.plugin,slash:e.slash,passthrough:e.passthrough,children:t})}o(F,"rebuildBranch");async function A(e,t,r){if(t.size===0)return e;const[{buildExternalCommandSnapshot:n},{composeCoreBranchMounts:s}]=await Promise.all([import("./core/command-plugins/discovery.js"),import("./core/command-plugins/compose.js")]),a=n(new Set(m),void 0,void 0,r,t),c=o((i,l)=>{let h=!1;const w=i.children.map(d=>{if(d.kind!=="branch")return d;const u=c(d,[...l,d.name]);return u!==d&&(h=!0),u}),f=i.extensible===!0?s(a.coreMounts,l):[];return h||f.length>0?F(i,[...w,...f]):i},"composeBranch");return e.map(i=>c(i,[i.name]))}o(A,"composeCorePluginMounts");async function J(e,t){const{validateEffectiveCommandPlugins:r}=await import("./core/command-plugins/discovery.js"),s=r(new Set(m),void 0,void 0,void 0,t).flatMap(c=>c.manifest===void 0?[]:[c.manifest]).filter(c=>c.coreMounts.some(i=>i.parent[0]===e));if(s.length===0)return null;const a=P(s.flatMap(c=>[...c.coreMounts.map(i=>i.parent.join(" ")),...Object.keys(c.helpAddenda??{})]));return a.add(e),g(await v(a))}o(J,"dispatchCommandPaths");function M(e,t){const r=e.slice(2).filter(n=>!O.has(n));return r[0]==="sys"&&r[1]===t}o(M,"isSysInvocation");const K=[["surface"],["sys"],["pkg"],["profile","default"],["canvas","chord"],["canvas","config"],["canvas","use"],["canvas","browse"],["canvas","issue"]],X=[["sys","feedback"]];function H(e,t){return t.every((r,n)=>e[n]===r)}o(H,"startsWithPath");function ce(e){const t=e.slice(2).filter(n=>!O.has(n));if(X.some(n=>H(t,n)))return null;const r=K.find(n=>H(t,n));return r===void 0?null:r.join(" ")}o(ce,"personOnlyInvocation");function ue(e){return M(e,"daemon")}o(ue,"isDaemonControlInvocation");function le(e){return M(e,"migrate")}o(le,"isLayoutMigrationInvocation");async function de(){const{registerDaemonControlSys:e}=await import("./commands/sys/daemon.js");return y({tagline:S,globals:b,subtrees:[e()]})}o(de,"resolveDaemonControlRoot");async function me(){const[{defineSysBranch:e},{sysMigrateLeaf:t}]=await Promise.all([import("./commands/sys/branch.js"),import("./commands/sys/migrate.js")]);return y({tagline:S,globals:b,subtrees:[e([t])]})}o(me,"resolveLayoutMigrationRoot");async function pe(e){return(e!==void 0?await Y(e):null)??Q()}o(pe,"resolveRoot");async function Y(e){if(k[e]===void 0)return null;const t=await L(e),r=await _(),n=[...r].some(a=>a.split(" ")[0]===e)?await J(e,r):null,s=B(n===null?[t]:await A([t],r,n));return s.length>0?y({tagline:S,globals:b,subtrees:R(s,T())}):null}o(Y,"resolveCoreRoot");async function Q(){const e=await C(),[{buildExternalCommandSnapshot:t},{composeExternalSubtrees:r,composeSandboxProviderSubtrees:n},{qualifiedTopLevelCommandName:s}]=await Promise.all([import("./core/command-plugins/discovery.js"),import("./core/command-plugins/compose.js"),import("./core/command-manifests/registry.js")]),a=await _(),c=await A(e,a,g(e)),i=t(new Set(m),void 0,void 0,g(e),a);let l=r(i);if(E()){const{cliClient:h}=await import("./commands/api-client.js");p("cli.providers.request");const{providers:w}=await h().request("GET","/v1/providers/tools");p("cli.providers.fetched",{providers:w.length});const f=n(w,new Set(m)),d=new Set(f.map(u=>u.name));l=[...f,...r({...i,contributions:i.contributions.map(u=>d.has(u.node.name)?{...u,node:{...u.node,name:s(u.contributor,u.node.name)}}:u)})]}return y({tagline:S,globals:b,subtrees:[...R(B(c),T()),...l]})}o(Q,"buildRoot");export{$ as EXTENSIBLE_CORE_BRANCHES,m as SUBTREE_NAMES,Q as buildRoot,se as coreCommandPaths,ae as coreCommandPathsCovering,re as coreHookCatalog,ie as declaredExtensibleCoreBranches,_ as extensibleCoreBranches,ue as isDaemonControlInvocation,le as isLayoutMigrationInvocation,ce as personOnlyInvocation,Y as resolveCoreRoot,de as resolveDaemonControlRoot,me as resolveLayoutMigrationRoot,pe as resolveRoot};
1
+ var I=Object.defineProperty;var n=(e,t)=>I(e,"name",{value:t,configurable:!0});import{defineBranch as D,defineRoot as y,GLOBAL_TOKENS as R}from"./core/command.js";import{createCoreHookCatalog as x}from"./core/command-hooks/catalog.js";import{composeCoreHooks as B}from"./core/command-hooks/compose.js";import{mark as p}from"./core/timing.js";import{envNodeId as G,envScopes as U,isSandboxedNode as E,scopeAllowed as q}from"./shared/env.js";const S="crtr: agentic runtime.",b=[{name:"--json",desc:"stdout as the leaf's declared outputs in raw JSON instead of prose \u2014 for scripts (a cron's bash, external tooling) that must branch on canvas state; prose stays the contract for what you read yourself."}],k=Object.assign(Object.create(null),{doc:n(async()=>(await import("./commands/doc.js")).registerDoc(),"doc"),"custom-object":n(async()=>(await import("./commands/custom-object.js")).registerCustomObject(),"custom-object"),profile:n(async()=>(await import("./commands/profile.js")).registerProfile(),"profile"),pkg:n(async()=>(await import("./commands/pkg.js")).registerPkg(),"pkg"),human:n(async()=>(await import("./commands/human.js")).registerHuman(),"human"),sys:n(async()=>(await import("./commands/sys.js")).registerSys(),"sys"),node:n(async()=>(await import("./commands/node.js")).registerNode(),"node"),push:n(async()=>(await import("./commands/push.js")).registerPush(),"push"),cron:n(async()=>(await import("./commands/cron.js")).registerCron(),"cron"),canvas:n(async()=>(await import("./commands/canvas.js")).registerCanvas(),"canvas"),surface:n(async()=>(await import("./commands/surface.js")).registerSurface(),"surface")}),m=Object.freeze(Object.keys(k)),z={human:["crtr:act"],cron:["crtr:schedule"],doc:["crtr:memory:read:user","crtr:memory:rw:user"]};function N(e){const t=U();return t===null?e:e.filter(r=>{const o=z[r.name];return o===void 0||o.some(s=>q(t,s))||r.name==="doc"&&process.env.CRTR_GRANTEE?.startsWith("app:")===!0})}n(N,"allowedSubtrees");const j=new Map;function L(e){let t=j.get(e);return t===void 0&&(t=k[e](),j.set(e,t)),t}n(L,"loadSubtree");function v(e){return Promise.all(m.filter(t=>e.has(t)).map(L))}n(v,"loadCoreSubtreesNamed");function C(){return v(new Set(m))}n(C,"loadCoreSubtrees");function P(e){const t=new Set;for(const r of e){const o=r.split(" ")[0];m.includes(o)&&t.add(o)}return t}n(P,"subtreesNamedBy");let T;function re(){return p("cli.hooks.catalog"),T??=C().then(x),T}n(re,"coreHookCatalog");function _(){let e;return()=>(e??=(async()=>{const{compileHookRegistry:t,declaredCommandHookTargets:r,effectiveHookPlugins:o}=await import("./core/command-hooks/discovery.js"),s=o(),a=E()&&G()!==void 0?await(await import("./core/command-hooks/transport/plan.js")).remoteHookPlan(process.cwd()):[];if(p("cli.hooks.effective_plugins",{count:s.length+a.length}),s.length===0&&a.length===0)return p("cli.hooks.empty_registry"),t(x([]),[]);const c=E()?s.filter(l=>l.scope==="project"):s,i=P([...r(c),...a.map(l=>l.target)]);return p("cli.hooks.catalog",{subtrees:[...i].join(",")}),t(x(await v(i)),c,a)})(),e)}n(_,"hookRegistryLoader");function g(e){const t=new Set,r=n((o,s)=>{if(!(o.kind==="branch"&&o.passthrough!==void 0)&&(t.add(s),o.kind==="branch"))for(const a of o.children)r(a,`${s} ${a.name}`)},"visit");for(const o of e)r(o,o.name);return t}n(g,"commandPathsFor");async function se(){return g(await C())}n(se,"coreCommandPaths");async function ae(e){return g(await v(P(e)))}n(ae,"coreCommandPathsCovering");function J(e){const t=[],r=n((o,s)=>{o.extensible===!0&&t.push(s);for(const a of o.children)a.kind==="branch"&&r(a,[...s,a.name])},"visit");for(const o of e)r(o,[o.name]);return t}n(J,"extensibleBranchPaths");const W=Object.freeze(["human","sys tutorial"]);async function A(){return new Set(W)}n(A,"extensibleCoreBranches");async function ie(){return new Set(J(await C()).map(e=>e.join(" ")))}n(ie,"declaredExtensibleCoreBranches");function $(e,t){const{listing:r,...o}=e.help;return D({name:e.name,description:e.description,whenToUse:e.whenToUse,tier:e.tier,help:o,rootEntry:e.rootEntry,extensible:e.extensible,plugin:e.plugin,slash:e.slash,passthrough:e.passthrough,children:t})}n($,"rebuildBranch");async function M(e,t,r){if(t.size===0)return e;const[{buildExternalCommandSnapshot:o},{composeCoreBranchMounts:s}]=await Promise.all([import("./core/command-plugins/discovery.js"),import("./core/command-plugins/compose.js")]),a=o(new Set(m),void 0,void 0,r,t),c=n((i,l)=>{let h=!1;const w=i.children.map(d=>{if(d.kind!=="branch")return d;const u=c(d,[...l,d.name]);return u!==d&&(h=!0),u}),f=i.extensible===!0?s(a.coreMounts,l):[];return h||f.length>0?$(i,[...w,...f]):i},"composeBranch");return e.map(i=>c(i,[i.name]))}n(M,"composeCorePluginMounts");async function F(e,t){const{validateEffectiveCommandPlugins:r}=await import("./core/command-plugins/discovery.js"),s=r(new Set(m),void 0,void 0,void 0,t).flatMap(c=>c.manifest===void 0?[]:[c.manifest]).filter(c=>c.coreMounts.some(i=>i.parent[0]===e));if(s.length===0)return null;const a=P(s.flatMap(c=>[...c.coreMounts.map(i=>i.parent.join(" ")),...Object.keys(c.helpAddenda??{})]));return a.add(e),g(await v(a))}n(F,"dispatchCommandPaths");function O(e,t){const r=e.slice(2).filter(o=>!R.has(o));return r[0]==="sys"&&r[1]===t}n(O,"isSysInvocation");const K=[["surface"],["sys"],["pkg"],["profile","default"],["canvas","chord"],["canvas","config"],["canvas","use"],["canvas","browse"],["canvas","issue"]],X=[["sys","feedback"]];function H(e,t){return t.every((r,o)=>e[o]===r)}n(H,"startsWithPath");function ce(e){const t=e.slice(2).filter(o=>!R.has(o));if(X.some(o=>H(t,o)))return null;const r=K.find(o=>H(t,o));return r===void 0?null:r.join(" ")}n(ce,"personOnlyInvocation");function ue(e){return O(e,"daemon")}n(ue,"isDaemonControlInvocation");function le(e){return O(e,"join")}n(le,"isJoinInvocation");function de(e){return O(e,"migrate")}n(de,"isLayoutMigrationInvocation");async function me(){const{registerDaemonControlSys:e}=await import("./commands/sys/daemon.js");return y({tagline:S,globals:b,subtrees:[e()]})}n(me,"resolveDaemonControlRoot");async function pe(){const[{defineSysBranch:e},{sysMigrateLeaf:t}]=await Promise.all([import("./commands/sys/branch.js"),import("./commands/sys/migrate.js")]);return y({tagline:S,globals:b,subtrees:[e([t])]})}n(pe,"resolveLayoutMigrationRoot");async function fe(e){return(e!==void 0?await Y(e):null)??Q()}n(fe,"resolveRoot");async function Y(e){if(k[e]===void 0)return null;const t=await L(e),r=await A(),o=[...r].some(a=>a.split(" ")[0]===e)?await F(e,r):null,s=N(o===null?[t]:await M([t],r,o));return s.length>0?y({tagline:S,globals:b,subtrees:B(s,_())}):null}n(Y,"resolveCoreRoot");async function Q(){const e=await C(),[{buildExternalCommandSnapshot:t},{composeExternalSubtrees:r,composeSandboxProviderSubtrees:o},{qualifiedTopLevelCommandName:s}]=await Promise.all([import("./core/command-plugins/discovery.js"),import("./core/command-plugins/compose.js"),import("./core/command-manifests/registry.js")]),a=await A(),c=await M(e,a,g(e)),i=t(new Set(m),void 0,void 0,g(e),a);let l=r(i);if(E()){const{cliClient:h}=await import("./commands/api-client.js");p("cli.providers.request");const{providers:w}=await h().request("GET","/v1/providers/tools");p("cli.providers.fetched",{providers:w.length});const f=o(w,new Set(m)),d=new Set(f.map(u=>u.name));l=[...f,...r({...i,contributions:i.contributions.map(u=>d.has(u.node.name)?{...u,node:{...u.node,name:s(u.contributor,u.node.name)}}:u)})]}return y({tagline:S,globals:b,subtrees:[...B(N(c),_()),...l]})}n(Q,"buildRoot");export{W as EXTENSIBLE_CORE_BRANCHES,m as SUBTREE_NAMES,Q as buildRoot,se as coreCommandPaths,ae as coreCommandPathsCovering,re as coreHookCatalog,ie as declaredExtensibleCoreBranches,A as extensibleCoreBranches,ue as isDaemonControlInvocation,le as isJoinInvocation,de as isLayoutMigrationInvocation,ce as personOnlyInvocation,Y as resolveCoreRoot,me as resolveDaemonControlRoot,pe as resolveLayoutMigrationRoot,fe as resolveRoot};
package/dist/cli.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- var R=Object.defineProperty;var s=(r,o)=>R(r,"name",{value:o,configurable:!0});import"./suppress-experimental-warnings.js";import{mkdirSync as S,openSync as _,readFileSync as E,renameSync as b,rmSync as C,statSync as u,writeFileSync as I}from"node:fs";import{dirname as g,join as a}from"node:path";import{enableCompileCache as A}from"node:module";import{fileURLToPath as h}from"node:url";import{isDaemonControlInvocation as m,isLayoutMigrationInvocation as N,personOnlyInvocation as O,resolveDaemonControlRoot as x,resolveLayoutMigrationRoot as $,resolveRoot as k}from"./build-root.js";import{maybeBootRoot as T}from"./core/runtime/front-door.js";import{maybeAutoUpdate as P}from"./core/auto-update.js";import{ensureProjectScope as F}from"./core/bootstrap.js";import{provisionExports as M,pruneLegacyExports as D}from"./core/host-exports/export.js";import{revalidateBundlePlugins as L}from"./core/command-plugins/revalidate.js";import{renderNode as j,runCli as y,walk as K}from"./core/command.js";import{dispatchWithStaleRecovery as U}from"./core/manifest-recovery.js";import{installStdoutErrorPolicy as B}from"./core/stdout-errors.js";import{handle as J}from"./core/io.js";import{CrtrError as w,ownerOnly as V}from"./core/errors.js";import{ExitCode as W}from"./types.js";import{layoutNeedsMigration as q,runtimeRoot as G}from"./core/layout.js";import{ApiError as v}from"./api/index.js";import{mark as i}from"./core/timing.js";import{launchSnapshotFile as z,sandboxCliNodeId as H}from"./shared/env.js";import{nodeRunDir as f}from"./core/canvas/paths.js";import{installBrokerConfigSnapshot as Q}from"./core/config.js";_(h(import.meta.url),"r"),B(),i("cli.entry",{cmd:process.argv[2]??"(bare)"});try{A()}catch{}i("cli.compile_cache");const X=a(g(h(import.meta.url)),".."),l=a(G(),"first-run.json");function c(r){return(r instanceof Error?r.message:String(r)).split(/\r?\n/,1)[0]??""}s(c,"errorMessage");function Y(r){const o=g(l);S(o,{recursive:!0});const t=`${l}.${process.pid}.${Date.now()}.tmp`;try{I(t,JSON.stringify({packageVersion:r,completedAt:new Date().toISOString()})+`
3
- `,"utf8"),b(t,l)}finally{C(t,{force:!0})}}s(Y,"writeFirstRunMarker");async function Z(){if(process.env.CRTR_NO_AUTO_INIT==="1")return;try{if(u(l,{throwIfNoEntry:!1})!==void 0)return}catch(o){process.stderr.write(`crtr: first-run reconcile marker check failed: ${c(o)}
4
- `);return}try{if(u(a(X,".git"),{throwIfNoEntry:!1})!==void 0)return}catch(o){process.stderr.write(`crtr: first-run reconcile source check failed: ${c(o)}
2
+ var R=Object.defineProperty;var s=(r,o)=>R(r,"name",{value:o,configurable:!0});import"./suppress-experimental-warnings.js";import{mkdirSync as S,openSync as _,readFileSync as E,renameSync as b,rmSync as C,statSync as u,writeFileSync as I}from"node:fs";import{dirname as g,join as a}from"node:path";import{enableCompileCache as A}from"node:module";import{fileURLToPath as h}from"node:url";import{isDaemonControlInvocation as m,isJoinInvocation as N,isLayoutMigrationInvocation as O,personOnlyInvocation as x,resolveDaemonControlRoot as $,resolveLayoutMigrationRoot as k,resolveRoot as T}from"./build-root.js";import{maybeBootRoot as P}from"./core/runtime/front-door.js";import{maybeAutoUpdate as F}from"./core/auto-update.js";import{ensureProjectScope as M}from"./core/bootstrap.js";import{provisionExports as D,pruneLegacyExports as L}from"./core/host-exports/export.js";import{revalidateBundlePlugins as j}from"./core/command-plugins/revalidate.js";import{renderNode as K,runCli as y,walk as U}from"./core/command.js";import{dispatchWithStaleRecovery as B}from"./core/manifest-recovery.js";import{installStdoutErrorPolicy as J}from"./core/stdout-errors.js";import{handle as V}from"./core/io.js";import{CrtrError as w,ownerOnly as W}from"./core/errors.js";import{ExitCode as q}from"./types.js";import{layoutNeedsMigration as G,runtimeRoot as z}from"./core/layout.js";import{ApiError as v}from"./api/index.js";import{mark as i}from"./core/timing.js";import{launchSnapshotFile as H,sandboxCliNodeId as Q}from"./shared/env.js";import{nodeRunDir as f}from"./core/canvas/paths.js";import{installBrokerConfigSnapshot as X}from"./core/config.js";_(h(import.meta.url),"r"),J(),i("cli.entry",{cmd:process.argv[2]??"(bare)"});try{A()}catch{}i("cli.compile_cache");const Y=a(g(h(import.meta.url)),".."),l=a(z(),"first-run.json");function c(r){return(r instanceof Error?r.message:String(r)).split(/\r?\n/,1)[0]??""}s(c,"errorMessage");function Z(r){const o=g(l);S(o,{recursive:!0});const t=`${l}.${process.pid}.${Date.now()}.tmp`;try{I(t,JSON.stringify({packageVersion:r,completedAt:new Date().toISOString()})+`
3
+ `,"utf8"),b(t,l)}finally{C(t,{force:!0})}}s(Z,"writeFirstRunMarker");async function rr(){if(process.env.CRTR_NO_AUTO_INIT==="1")return;try{if(u(l,{throwIfNoEntry:!1})!==void 0)return}catch(o){process.stderr.write(`crtr: first-run reconcile marker check failed: ${c(o)}
4
+ `);return}try{if(u(a(Y,".git"),{throwIfNoEntry:!1})!==void 0)return}catch(o){process.stderr.write(`crtr: first-run reconcile source check failed: ${c(o)}
5
5
  `);return}let r;try{const{reconcileFirstRun:o}=await import("./commands/sys/setup-core.js");r=await o((t,p)=>{process.stderr.write(`crtr: first-run reconcile ${t} failed: ${c(p)}
6
6
  `)})}catch(o){process.stderr.write(`crtr: first-run reconcile failed: ${c(o)}
7
- `)}finally{if(r!==void 0)try{Y(r)}catch(o){process.stderr.write(`crtr: first-run reconcile marker failed: ${c(o)}
8
- `)}}}s(Z,"maybeReconcileFirstRun");async function rr(){const r=N(process.argv),o=H(),t=o!==void 0;if(o!==void 0){process.env.CRTR_API_SOCKET=a(f(o),"cli.sock");const e=O(process.argv);if(e!==null)throw V(`crtr ${e} is not available to an app's node`,"Run this command from the person's terminal.");const n=JSON.parse(E(z(f(o))??a(f(o),"broker-launch.json"),"utf8"));if(!n.configSnapshot)throw new Error("node launch is missing its config snapshot");Q(n.configSnapshot)}if(!t&&q()&&!m(process.argv)&&!r)throw new w("layout_migration_required","run crtr sys migrate while the runtime is offline",W.NETWORK,{next:"Run `crtr sys migrate` while the runtime is offline, then restart it."});if(r){const e=await $();if(process.argv.slice(4).some(n=>n==="-h"||n==="--help")){process.stdout.write(j(K(e,["sys","migrate"]).node)+`
9
- `);return}await y(e,process.argv);return}if(!t&&!m(process.argv)){try{await(await import("./commands/person-settings.js")).loadPersonSettingsViaApi()}catch(e){if(!(process.argv.slice(2).some(d=>d==="-h"||d==="--help")&&e instanceof v&&e.code==="daemon_unavailable"))throw e;(await import("./core/scoped-state/person-store.js")).installPreludeSnapshot({profiles:[],defaults:{},settings:{},state:{}})}i("cli.settings_loaded")}if(t||(D(process.argv),await Z()),await T(process.argv),i("cli.front_door_passed"),m(process.argv)){await y(await x(),process.argv);return}t||await L(process.argv),i("cli.plugins_revalidated");const p=await k(process.argv[2]);i("cli.root_resolved"),t||(M(p,process.argv),await F(process.argv),await P(process.argv)),i("cli.dispatch_start"),await U(p,process.argv),i("cli.dispatch_done")}s(rr,"main"),rr().catch(r=>{(r instanceof Error&&r.name==="AbortError"||r instanceof Error&&/operation was aborted/i.test(r.message))&&(process.stderr.write(`crtr: Interrupted
10
- `),process.exit(130)),(r instanceof v||r instanceof w)&&(J(r),process.exit(typeof process.exitCode=="number"?process.exitCode:1));const t=r instanceof Error?r.message:String(r);process.stderr.write(`crtr: ${t}
7
+ `)}finally{if(r!==void 0)try{Z(r)}catch(o){process.stderr.write(`crtr: first-run reconcile marker failed: ${c(o)}
8
+ `)}}}s(rr,"maybeReconcileFirstRun");async function or(){const r=O(process.argv),o=Q(),t=o!==void 0;if(o!==void 0){process.env.CRTR_API_SOCKET=a(f(o),"cli.sock");const e=x(process.argv);if(e!==null)throw W(`crtr ${e} is not available to an app's node`,"Run this command from the person's terminal.");const n=JSON.parse(E(H(f(o))??a(f(o),"broker-launch.json"),"utf8"));if(!n.configSnapshot)throw new Error("node launch is missing its config snapshot");X(n.configSnapshot)}if(!t&&G()&&!m(process.argv)&&!r)throw new w("layout_migration_required","run crtr sys migrate while the runtime is offline",q.NETWORK,{next:"Run `crtr sys migrate` while the runtime is offline, then restart it."});if(r){const e=await k();if(process.argv.slice(4).some(n=>n==="-h"||n==="--help")){process.stdout.write(K(U(e,["sys","migrate"]).node)+`
9
+ `);return}await y(e,process.argv);return}if(!t&&!m(process.argv)&&!N(process.argv)){try{await(await import("./commands/person-settings.js")).loadPersonSettingsViaApi()}catch(e){if(!(process.argv.slice(2).some(d=>d==="-h"||d==="--help")&&e instanceof v&&e.code==="daemon_unavailable"))throw e;(await import("./core/scoped-state/person-store.js")).installPreludeSnapshot({profiles:[],defaults:{},settings:{},state:{}})}i("cli.settings_loaded")}if(t||(L(process.argv),await rr()),await P(process.argv),i("cli.front_door_passed"),m(process.argv)){await y(await $(),process.argv);return}t||await j(process.argv),i("cli.plugins_revalidated");const p=await T(process.argv[2]);i("cli.root_resolved"),t||(D(p,process.argv),await M(process.argv),await F(process.argv)),i("cli.dispatch_start"),await B(p,process.argv),i("cli.dispatch_done")}s(or,"main"),or().catch(r=>{(r instanceof Error&&r.name==="AbortError"||r instanceof Error&&/operation was aborted/i.test(r.message))&&(process.stderr.write(`crtr: Interrupted
10
+ `),process.exit(130)),(r instanceof v||r instanceof w)&&(V(r),process.exit(typeof process.exitCode=="number"?process.exitCode:1));const t=r instanceof Error?r.message:String(r);process.stderr.write(`crtr: ${t}
11
11
  `),process.exit(1)});
@@ -1 +1 @@
1
- var s=Object.defineProperty;var n=(e,r)=>s(e,"name",{value:r,configurable:!0});import{defineLeaf as o}from"../../core/command.js";import{existsSync as a}from"node:fs";import{join as d}from"node:path";import{pluginsDir as l}from"../../core/scope.js";import{joinDirectory as c}from"../../core/directory/join.js";import{apiSocketPath as u}from"../../core/canvas/paths.js";import{listProcessTable as h}from"../../core/canvas/pid.js";import{daemonAnswers as m}from"../../core/layout-migrate/index.js";import{envNodeId as p}from"../../shared/env.js";import{isDaemonRunning as f}from"../../daemon/pidfile.js";import{findDaemonPids as w,spawnDaemon as y,stopAllDaemons as g}from"../../daemon/manage.js";function k(){const e=h();if(e===null||(p()??"")!=="")return!0;const r=new Map(e.map(t=>[t.pid,t])),i=new Set;for(let t=process.ppid;t>1&&!i.has(t);t=r.get(t)?.ppid??0)if(i.add(t),r.get(t)?.command.includes("broker-cli"))return!0;return!1}n(k,"insideBroker");function b(){if(!(process.platform==="linux"&&a("/run/crtr/launcher.sock")))return{running:n(async()=>f()||w().length>0||await m(u()),"running"),stop:n(async()=>{await g()},"stop"),start:n(async()=>{await y()},"start"),firstSync:n(async()=>{const e=l("user"),r=Date.now()+2e4;for(;e&&Date.now()<r&&!a(d(e,"cloud","commands.json"));)await new Promise(i=>setTimeout(i,250))},"firstSync"),insideNode:k}}n(b,"daemonControl");const I=o({name:"join",description:"register this runtime with its directory using an enrollment token, offline",whenToUse:"on your own computer, with a token from `<issuer>/account/machines`: run it from a plain terminal, with any viewers open. It stops the daemon, joins, and starts it again. Also at a hosted runtime's first boot, before the daemon starts, with the enrollment token provisioning passed in.",help:{name:"sys join",summary:"exchange a directory enrollment token for a new runtime client key and record the returned identity",guide:"Generates a new ES256 client key, sends only its public key to `PUT <directory>/v1/runtimes/registration` with the token as bearer, and learns the runtime id, client id, issuer, owner and URL from the answer. Only after that answer does it write the key file, set the user settings `runtime.issuer` and `runtime.audience`, and rewrite the legacy local person's profile grantees to `app:terminal`. canvas.db belongs to the daemon: its next start replaces this install's person with the owner everywhere in canvas.db, before it serves anything. On your own computer a running daemon is no obstacle: join takes a `join.lock` in the runtime directory (so no open viewer's redial can autostart a daemon while it works), registers with the directory while the daemon still serves, stops the daemon, writes the identity, and starts the daemon again, whose start applies the canvas.db change. Run it from a plain terminal, not from inside a node: a node's broker dies with the daemon, so join refuses `join_inside_node` instead. Where the launcher owns the daemon (a hosted container) join keeps the offline contract and refuses `daemon_running`. Presenting the token of the stored successful registration again finishes those local writes without calling the directory. Any other token generates a new key and registers again.",params:[{kind:"flag",name:"directory",type:"string",required:!0,constraint:"Directory base URL (http or https) that minted the token."},{kind:"flag",name:"token",type:"string",required:!0,sensitive:!0,constraint:"Single-use enrollment token. Sensitive: never echoed in output or errors."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Label shown for this machine on the account page. Default: this computer's hostname."}],output:[{name:"runtime",type:"string",required:!0,constraint:"Runtime principal from the directory, `runtime:<id>`."},{name:"client_id",type:"string",required:!0,constraint:"OAuth client id registered for the new key."},{name:"issuer",type:"string",required:!0,constraint:"Canonical issuer, written to `runtime.issuer`."},{name:"owner",type:"string",required:!0,constraint:"Owner principal, `user:<id>`, that replaces this install's own person at the next daemon start."},{name:"canvas_rewrite",type:"string",required:!0,constraint:"Always `next daemon start`: when canvas.db identity fields are rewritten."},{name:"url",type:"string",required:!0,constraint:"Runtime URL, written to `runtime.audience`."},{name:"registered",type:"boolean",required:!0,constraint:"False when the token matched the stored registration and the directory was not called."},{name:"daemon",type:"string",required:!0,constraint:"`restarted` (a daemon was stopped and started on the joined settings), `started` (none was running; join started one and waited, up to 20s, for its first grant sync to install the account's provider plugins, so `crtr cloud \u2026` resolves), `not_running` (none was started: a hosted container's launcher owns its daemon), or `restart_failed` (joined, but the new daemon did not start: see `daemon_error`, then run `crtr sys daemon start`)."},{name:"daemon_error",type:"string",required:!1,constraint:"Why the new daemon did not start, with `daemon: restart_failed`."}],outputKind:"object",effects:["Holds `<runtime>/join.lock` (its pid) from the start until the daemon is back; `ensureDaemon` and `sys daemon start` (without `--force`) decline to start a daemon while a live process holds it (including a start already in flight when the join began). A lock whose holder died is reclaimed.","A running daemon is stopped (SIGTERM, then SIGKILL, plus strays) after the directory accepts the registration and before the local writes, then started again. Every broker dies with it and is revived by the new daemon. If the join fails after the stop, the daemon is started again anyway. With no daemon running join starts one anyway, because a bare `crtr cloud \u2026` is an unknown command (it never autostarts a daemon) until a daemon's grant sync has installed the provider plugins. Refuses `join_inside_node` when run from inside a node and a daemon is running; refuses `daemon_running` where a container launcher owns the daemon.","On a new token: calls the directory once; on success writes `<runtime>/secrets/runtime-client.json` (mode 0600) with the private key, registration and the token's SHA-256, replacing any earlier key file. A refused or failed call writes nothing.","Sends name, platform, crtr version, installed plugin ids and sandbox state with the public key. Before the request it writes `runtime-client.pending.json` (mode 0600); a rerun with the same token and directory reuses that key, so a lost reply is retried safely. The file is deleted on success or a refusal.","Sets `runtime.issuer` and `runtime.audience` in user settings. When the directory says this machine is not the account's default (`is_default: false`) it replaces the whole `runtime` setting with `{issuer, audience, unsandboxed: true}`: no app listener, and only `app:terminal` nodes run.","Rewrites the legacy local person's profile grantees to `app:terminal`.","At the next daemon start, before any listener, the person this install minted (or still the legacy local person) is replaced by the owner in every canvas.db column that holds a person: node, token, grant, cron, audit and usage subjects and grantees, the person's identity, and queued cron births. References to the install's local terminal grant are cleared and that local grant mirror is deleted so grant sync installs the directory's. Free text is unchanged. The start after that changes nothing."]},run:n(async e=>({...await c(e.directory,e.token,e.name||void 0,b()),canvas_rewrite:"next daemon start"}),"run")});export{I as sysJoinLeaf};
1
+ var a=Object.defineProperty;var n=(e,r)=>a(e,"name",{value:r,configurable:!0});import{defineLeaf as o}from"../../core/command.js";import{existsSync as s}from"node:fs";import{join as d}from"node:path";import{pluginsDir as l}from"../../core/scope.js";import{joinDirectory as c}from"../../core/directory/join.js";import{apiSocketPath as u}from"../../core/canvas/paths.js";import{listProcessTable as h}from"../../core/canvas/pid.js";import{daemonAnswers as m}from"../../core/layout-migrate/index.js";import{envNodeId as p}from"../../shared/env.js";import{isDaemonRunning as f}from"../../daemon/pidfile.js";import{findDaemonPids as w,spawnDaemon as y,stopAllDaemons as g}from"../../daemon/manage.js";function k(){const e=h();if(e===null||(p()??"")!=="")return!0;const r=new Map(e.map(t=>[t.pid,t])),i=new Set;for(let t=process.ppid;t>1&&!i.has(t);t=r.get(t)?.ppid??0)if(i.add(t),r.get(t)?.command.includes("broker-cli"))return!0;return!1}n(k,"insideBroker");function b(){if(!(process.platform==="linux"&&s("/run/crtr/launcher.sock")))return{running:n(async()=>f()||w().length>0||await m(u()),"running"),stop:n(async()=>{await g()},"stop"),start:n(async()=>{await y()},"start"),firstSync:n(async()=>{const e=l("user"),r=Date.now()+2e4;for(;e&&Date.now()<r&&!s(d(e,"cloud","commands.json"));)await new Promise(i=>setTimeout(i,250))},"firstSync"),insideNode:k}}n(b,"daemonControl");const T=o({name:"join",description:"register this runtime with its directory using an enrollment token, offline",whenToUse:"on your own computer, with a token from `<issuer>/account/machines`: run it from a plain terminal, with any viewers open. It stops the daemon, joins, and starts it again. Also at a hosted runtime's first boot, before the daemon starts, with the enrollment token provisioning passed in.",help:{name:"sys join",summary:"exchange a directory enrollment token for a new runtime client key and record the returned identity",guide:"Generates a new ES256 client key, sends only its public key to `PUT <directory>/v1/runtimes/registration` with the token as bearer, and learns the runtime id, client id, issuer, owner and URL from the answer. Only after that answer does it write the key file. It writes no settings and touches no canvas.db: the daemon's next start replaces this install's person with the owner everywhere in canvas.db, sets the user settings `runtime.issuer` and `runtime.audience` from the key file, and rewrites the legacy local person's profile grantees to `app:terminal`, all before it serves anything. On your own computer a running daemon is no obstacle: join takes a `join.lock` in the runtime directory (so no open viewer's redial can autostart a daemon while it works), registers with the directory while the daemon still serves, stops the daemon, writes the identity, and starts the daemon again, whose start applies the change. Run it from a plain terminal, not from inside a node: a node's broker dies with the daemon, so join refuses `join_inside_node` instead. Where the launcher owns the daemon (a hosted container) join keeps the offline contract and refuses `daemon_running`. Presenting the token of the stored successful registration again finishes those local writes without calling the directory. Any other token generates a new key and registers again.",params:[{kind:"flag",name:"directory",type:"string",required:!0,constraint:"Directory base URL (http or https) that minted the token."},{kind:"flag",name:"token",type:"string",required:!0,sensitive:!0,constraint:"Single-use enrollment token. Sensitive: never echoed in output or errors."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Label shown for this machine on the account page. Default: this computer's hostname."}],output:[{name:"runtime",type:"string",required:!0,constraint:"Runtime principal from the directory, `runtime:<id>`."},{name:"client_id",type:"string",required:!0,constraint:"OAuth client id registered for the new key."},{name:"issuer",type:"string",required:!0,constraint:"Canonical issuer, written to `runtime.issuer`."},{name:"owner",type:"string",required:!0,constraint:"Owner principal, `user:<id>`, that replaces this install's own person at the next daemon start."},{name:"canvas_rewrite",type:"string",required:!0,constraint:"Always `next daemon start`: when canvas.db identity fields are rewritten."},{name:"url",type:"string",required:!0,constraint:"Runtime URL, written to `runtime.audience`."},{name:"registered",type:"boolean",required:!0,constraint:"False when the token matched the stored registration and the directory was not called."},{name:"daemon",type:"string",required:!0,constraint:"`restarted` (a daemon was stopped and started on the joined settings), `started` (none was running; join started one and waited, up to 20s, for its first grant sync to install the account's provider plugins, so `crtr cloud \u2026` resolves), `not_running` (none was started: a hosted container's launcher owns its daemon), or `restart_failed` (joined, but the new daemon did not start: see `daemon_error`, then run `crtr sys daemon start`)."},{name:"daemon_error",type:"string",required:!1,constraint:"Why the new daemon did not start, with `daemon: restart_failed`."}],outputKind:"object",effects:["Holds `<runtime>/join.lock` (its pid) from the start until the daemon is back; `ensureDaemon` and `sys daemon start` (without `--force`) decline to start a daemon while a live process holds it (including a start already in flight when the join began). A lock whose holder died is reclaimed.","A running daemon is stopped (SIGTERM, then SIGKILL, plus strays) after the directory accepts the registration and before the local writes, then started again. Every broker dies with it and is revived by the new daemon. If the join fails after the stop, the daemon is started again anyway. With no daemon running join starts one anyway, because a bare `crtr cloud \u2026` is an unknown command (it never autostarts a daemon) until a daemon's grant sync has installed the provider plugins. Refuses `join_inside_node` when run from inside a node and a daemon is running; refuses `daemon_running` where a container launcher owns the daemon.","On a new token: calls the directory once; on success writes `<runtime>/secrets/runtime-client.json` (mode 0600) with the private key, registration and the token's SHA-256, replacing any earlier key file. A refused or failed call writes nothing.","Sends name, platform, crtr version, installed plugin ids and sandbox state with the public key. Before the request it writes `runtime-client.pending.json` (mode 0600); a rerun with the same token and directory reuses that key, so a lost reply is retried safely. The file is deleted on success or a refusal.","Sets `runtime.issuer` and `runtime.audience` in user settings. When the directory says this machine is not the account's default (`is_default: false`) it replaces the whole `runtime` setting with `{issuer, audience, unsandboxed: true}`: no app listener, and only `app:terminal` nodes run.","Rewrites the legacy local person's profile grantees to `app:terminal`.","At the next daemon start, before any listener, the person this install minted (or still the legacy local person) is replaced by the owner in every canvas.db column that holds a person: node, token, grant, cron, audit and usage subjects and grantees, the person's identity, and queued cron births. References to the install's local terminal grant are cleared and that local grant mirror is deleted so grant sync installs the directory's. Free text is unchanged. The start after that changes nothing."]},run:n(async e=>({...await c(e.directory,e.token,e.name||void 0,b()),canvas_rewrite:"next daemon start"}),"run")});export{T as sysJoinLeaf};
@@ -1,3 +1,11 @@
1
1
  import { type BlobStore } from './index.js';
2
+ /** Move the objects of the directory store's former place (`<layout root>/blobs`) to `root`, one object at a time. Safe when
3
+ * several processes run it at once and when an older build is still writing to `former`: an object is published at its new key
4
+ * with a hard link (which refuses to replace an existing object) and its old name is removed only after that, so an interrupted
5
+ * or raced move leaves every object readable in one place or the other. Entries it did not move (an object whose key already
6
+ * exists at `root`, or one written after the listing) stay in `former` and are picked up by the next call. A `former` that is
7
+ * a symbolic link is followed, so the objects behind it move too. When the objects are on another filesystem (`link` fails with
8
+ * EXDEV) each is copied to a temporary file, fsynced and linked in. Does nothing once `former` is gone. */
9
+ export declare function adoptBlobDirectory(former: string, root: string, link?: (from: string, to: string) => void): void;
2
10
  /** Objects as files under `root`: written to a temp file, fsynced, renamed into place, and the directory fsynced. */
3
11
  export declare function createDirectoryBlobStore(root: string): BlobStore;
@@ -1 +1 @@
1
- var S=Object.defineProperty;var i=(s,c)=>S(s,"name",{value:c,configurable:!0});import{randomUUID as w}from"node:crypto";import{closeSync as u,fsyncSync as a,mkdirSync as B,openSync as m,readFileSync as b,readSync as D,readdirSync as E,renameSync as N,rmSync as p,writeSync as $}from"node:fs";import{dirname as h,join as d}from"node:path";import{assertBlobKey as g}from"./index.js";import{blobFault as F}from"./fault.js";function O(s){const c=m(s,"r");try{a(c)}finally{u(c)}}i(O,"syncDir");function x(s){const c=i(e=>(g(e),d(s,...e.split("/"))),"pathOf");function l(e,n,r){let o;try{o=E(e,{withFileTypes:!0})}catch(t){if(t.code==="ENOENT")return;throw t}for(const t of o){if(t.name.endsWith(".tmp"))continue;const f=n===""?t.name:`${n}/${t.name}`;t.isDirectory()?l(d(e,t.name),f,r):r.push(f)}}return i(l,"walk"),{async put(e,n){F("put");const r=c(e);B(h(r),{recursive:!0,mode:448});const o=`${r}.${w()}.tmp`,t=m(o,"wx",384);try{const f=typeof n=="string"?Buffer.from(n,"utf8"):Buffer.from(n);let y=0;for(;y<f.length;)y+=$(t,f,y);a(t)}catch(f){throw u(t),p(o,{force:!0}),f}u(t),N(o,r),O(h(r))},async get(e,n){try{if(!n)return b(c(e));const r=m(c(e),"r");try{const o=Buffer.allocUnsafe(n.length),t=D(r,o,0,n.length,n.start);return o.subarray(0,t)}finally{u(r)}}catch(r){if(r.code==="ENOENT")return null;throw r}},async list(e){const n=[];return l(s,"",n),n.filter(r=>r.startsWith(e)).sort((r,o)=>Buffer.compare(Buffer.from(r),Buffer.from(o)))},async delete(e){p(c(e),{force:!0})}}}i(x,"createDirectoryBlobStore");export{x as createDirectoryBlobStore};
1
+ var g=Object.defineProperty;var y=(i,n)=>g(i,"name",{value:n,configurable:!0});import{randomUUID as O}from"node:crypto";import{closeSync as m,existsSync as k,fsyncSync as S,linkSync as b,lstatSync as U,mkdirSync as B,openSync as E,readFileSync as D,readSync as W,readdirSync as $,realpathSync as M,renameSync as P,rmSync as a,rmdirSync as v,statSync as x,writeSync as F}from"node:fs";import{dirname as w,join as d,resolve as X}from"node:path";import{assertBlobKey as Y}from"./index.js";import{blobFault as j}from"./fault.js";function I(i){const n=E(i,"r");try{S(n)}finally{m(n)}}y(I,"syncDir");function J(i,n,f=b){let t;try{t=M(i)}catch(r){if(r.code==="ENOENT")return;throw r}if(t===X(n))return;const e=new Set,o=y((r,c)=>{let u;try{u=$(r,{withFileTypes:!0})}catch(s){if(s.code==="ENOENT")return;throw s}for(const s of u){if(s.name.endsWith(".tmp"))continue;const l=d(r,s.name),N=d(c,s.name);if(s.isDirectory()){o(l,N);try{v(l)}catch(p){if(!["ENOTEMPTY","ENOENT"].includes(p.code??""))throw p}continue}const h=d(n,N);B(w(h),{recursive:!0,mode:448}),e.add(w(h));try{f(l,h)}catch(p){const T=p.code;if(T==="EEXIST"){K(l,h)&&a(l,{force:!0});continue}if(T==="ENOENT"||!L(l,h))continue}a(l,{force:!0})}},"adopt");o(t,"");for(const r of e)I(r);try{v(t)}catch(r){if(!["ENOTEMPTY","ENOENT"].includes(r.code??""))throw r}U(i,{throwIfNoEntry:!1})?.isSymbolicLink()&&!k(i)&&a(i,{force:!0})}y(J,"adoptBlobDirectory");function K(i,n){try{const f=x(i),t=x(n);return f.ino===t.ino&&f.dev===t.dev}catch{return!1}}y(K,"sameFile");function L(i,n){const f=`${n}.${O()}.tmp`;try{const t=E(f,"wx",384);try{let e;try{e=D(i)}catch(r){if(r.code==="ENOENT")return!1;throw r}let o=0;for(;o<e.length;)o+=F(t,e,o);S(t)}finally{m(t)}try{b(f,n)}catch(e){if(e.code==="EEXIST")return!1;throw e}return!0}finally{a(f,{force:!0})}}y(L,"copyInto");function Q(i){const n=y(t=>(Y(t),d(i,...t.split("/"))),"pathOf");function f(t,e,o){let r;try{r=$(t,{withFileTypes:!0})}catch(c){if(c.code==="ENOENT")return;throw c}for(const c of r){if(c.name.endsWith(".tmp"))continue;const u=e===""?c.name:`${e}/${c.name}`;c.isDirectory()?f(d(t,c.name),u,o):o.push(u)}}return y(f,"walk"),{async put(t,e){j("put");const o=n(t);B(w(o),{recursive:!0,mode:448});const r=`${o}.${O()}.tmp`,c=E(r,"wx",384);try{const u=typeof e=="string"?Buffer.from(e,"utf8"):Buffer.from(e);let s=0;for(;s<u.length;)s+=F(c,u,s);S(c)}catch(u){throw m(c),a(r,{force:!0}),u}m(c),P(r,o),I(w(o))},async get(t,e){try{if(!e)return D(n(t));const o=E(n(t),"r");try{const r=Buffer.allocUnsafe(e.length),c=W(o,r,0,e.length,e.start);return r.subarray(0,c)}finally{m(o)}}catch(o){if(o.code==="ENOENT")return null;throw o}},async list(t){const e=[];return f(i,"",e),e.filter(o=>o.startsWith(t)).sort((o,r)=>Buffer.compare(Buffer.from(o),Buffer.from(r)))},async delete(t){a(n(t),{force:!0})}}}y(Q,"createDirectoryBlobStore");export{J as adoptBlobDirectory,Q as createDirectoryBlobStore};
@@ -1,6 +1,6 @@
1
1
  /** The one blob-store interface: conversation objects, run-event chunks, job output, attachments and releases are
2
2
  * objects under slash-separated keys (`conversations/<node>/<session>/<seq>.jsonl`). Implementations: a directory
3
- * (a Mac, `~/.crouter/blobs/`) and R2 (the cloud). A key never starts with `/` and has no `.` or `..` segment. */
3
+ * (a Mac, `~/.crouter/runtime/blobs/`) and R2 (the cloud). A key never starts with `/` and has no `.` or `..` segment. */
4
4
  export interface BlobStore {
5
5
  /** Write `data` at `key`, replacing any object there. Resolves once the object is durable. */
6
6
  put(key: string, data: Buffer | Uint8Array | string): Promise<void>;
@@ -1 +1 @@
1
- var c=Object.defineProperty;var r=(e,t)=>c(e,"name",{value:t,configurable:!0});function l(e){if(e===""||e.startsWith("/")||e.endsWith("/")||e.split("/").some(t=>t===""||t==="."||t===".."))throw new Error(`invalid blob key: ${JSON.stringify(e)}`)}r(l,"assertBlobKey");let o;async function _(){if(o)return o;const e=process.env.CRTR_R2_BUCKET;if(e){const t=r(s=>{const n=process.env[s];if(!n)throw new Error(`${s} is required when CRTR_R2_BUCKET is set`);return n},"need"),{createR2BlobStore:i}=await import("./r2.js");o=i({bucket:e,endpoint:t("CRTR_R2_ENDPOINT"),accessKeyId:t("CRTR_R2_ACCESS_KEY_ID"),secretAccessKey:t("CRTR_R2_SECRET_ACCESS_KEY")})}else{const[{join:t},{layoutRoot:i},{createDirectoryBlobStore:s}]=await Promise.all([import("node:path"),import("../layout.js"),import("./directory.js")]);o=s(t(i(),"blobs"))}return o}r(_,"blobStore");function a(){o=void 0}r(a,"resetBlobStore");export{l as assertBlobKey,_ as blobStore,a as resetBlobStore};
1
+ var l=Object.defineProperty;var r=(t,o)=>l(t,"name",{value:o,configurable:!0});function _(t){if(t===""||t.startsWith("/")||t.endsWith("/")||t.split("/").some(o=>o===""||o==="."||o===".."))throw new Error(`invalid blob key: ${JSON.stringify(t)}`)}r(_,"assertBlobKey");let e;async function a(){if(e)return e;const t=process.env.CRTR_R2_BUCKET;if(t){const o=r(s=>{const i=process.env[s];if(!i)throw new Error(`${s} is required when CRTR_R2_BUCKET is set`);return i},"need"),{createR2BlobStore:n}=await import("./r2.js");e=n({bucket:t,endpoint:o("CRTR_R2_ENDPOINT"),accessKeyId:o("CRTR_R2_ACCESS_KEY_ID"),secretAccessKey:o("CRTR_R2_SECRET_ACCESS_KEY")})}else{const[{join:o},{layoutRoot:n,runtimeRoot:s},{adoptBlobDirectory:i,createDirectoryBlobStore:R}]=await Promise.all([import("node:path"),import("../layout.js"),import("./directory.js")]),c=o(s(),"blobs");i(o(n(),"blobs"),c),e=R(c)}return e}r(a,"blobStore");function p(){e=void 0}r(p,"resetBlobStore");export{_ as assertBlobKey,a as blobStore,p as resetBlobStore};
@@ -1,11 +1,11 @@
1
- var H=Object.defineProperty;var c=(n,e)=>H(n,"name",{value:e,configurable:!0});import{randomBytes as W}from"node:crypto";import{CrtrError as T}from"../errors.js";import{PersonUnresolvedError as g,PersonOutsideRequestError as A,setPersonId as y,markCloudRole as b,isCloudRole as D,runAsPerson as v,personId as $,bootPersonId as k,requestPersonId as C}from"./person-state.js";import{readConfig as S}from"../config.js";import{joinedOwner as j}from"../directory/join.js";import{installLocalTerminalGrant as M}from"../grants/local.js";import{dropLocalGrantMirror as F}from"../grants/mirror.js";import{ownerForGrantee as x,ensureOwner as G}from"../graph/objects.js";import{canvasDatabaseUrl as J,CanvasConstraintError as q,withCanvasWrite as h}from"./db.js";import{LEGACY_LOCAL_PERSON as E,LEGACY_LOCAL_TERMINAL_GRANT as B}from"./legacy-person.js";import{rebuildTotalsFor as Y}from"../ledger/totals.js";const l="person_id",O="local_person_retired",z=/^user:[A-Za-z0-9._-]+$/,I="abcdefghijklmnopqrstuvwxyz0123456789",L=new Map;async function me(){const n=C();if(n!==void 0)return n;if(D())throw new A;const e=k();if(e!==void 0)return e;const r=await J(),t=L.get(r);if(t!==void 0)return t;const a=await V();if(a===null)throw new g;return L.set(r,a),a}c(me,"personIdAsync");function K(){const n=W(12);let e="user:";for(let r=0;r<12;r++)e+=I[n[r]%I.length];return e}c(K,"mintPersonId");async function m(n,e){return(await n.prepare("SELECT value FROM canvas_meta WHERE key = ?").get(e))?.value??null}c(m,"readMeta");async function d(n,e,r){await n.prepare("INSERT INTO canvas_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(e,r)}c(d,"writeMeta");async function X(){return await h(async n=>{const e=await m(n,l);if(e!==null)return{id:e,source:"stored"};const r=j();if(r!==null)return await d(n,l,r),{id:r,source:"joined"};if(S("user").runtime?.issuer){const a=await n.prepare("SELECT DISTINCT sub FROM grants").all();return a.length!==1?null:(await d(n,l,a[0].sub),{id:a[0].sub,source:"grants"})}const t=K();return await d(n,l,t),{id:t,source:"minted"}})}c(X,"resolvePerson");async function V(){return(await X())?.id??null}c(V,"resolvePersonId");const o=c(n=>Number(n.changes),"changes"),Z=[["nodes","sub"],["node_tokens","sub"],["grants","sub"],["crons","sub"],["audit_rows","sub"],["provider_approvals","sub"],["audit_rows","grantee"],["run_events","grantee"],["node_tokens","grantee"],["uploads","grantee"],["provider_approvals","grantee"]],Q=["profiles","profile_defaults","scope_settings","scope_bookkeeping","scope_marketplaces","scope_plugins","person_settings","model_accounts","model_account_cooldowns","model_api_keys","model_settings","model_sign_in_flows","env_vars"],ee=[["charges","sub"],["charges","payer"],["charges","grantee"],["limits","payer"],["limits","sub"],["limits","grantee"]];function P(n,e){if(!z.test(e))throw new T("invalid_person",`${n} must be a user:<id> principal, got ${JSON.stringify(e)}`)}c(P,"assertPerson");async function ne(n,e){return await n.prepare("SELECT 1 AS found FROM charge_totals WHERE payer = ? OR sub = ? OR grantee = ? UNION ALL SELECT 1 FROM charge_chunks WHERE payer = ? LIMIT 1").get(e,e,e,e)!==void 0}c(ne,"namesPerson");async function U(n,e,r){if(P("from",e),P("to",r),e===r)throw new T("invalid_person","cannot substitute a person with itself");const t={};try{t.identities=o(await n.prepare("UPDATE identities SET scheme = 'user', external_id = ? WHERE scheme IN ('local-user', 'user') AND external_id = ?").run(r,e))}catch(s){throw s instanceof q?new T("person_conflict",`another person object already holds ${r}; merging two person objects is not supported (${s.message})`,void 0,{from:e,to:r}):s}t["daemon_actions.principal_sub"]=o(await n.prepare(`UPDATE daemon_actions SET adapter_json = jsonb_set(adapter_json::jsonb, '{principal,sub}', to_jsonb(?::text))::text
1
+ var H=Object.defineProperty;var c=(n,e)=>H(n,"name",{value:e,configurable:!0});import{randomBytes as W}from"node:crypto";import{CrtrError as T}from"../errors.js";import{PersonUnresolvedError as g,PersonOutsideRequestError as A,setPersonId as y,markCloudRole as b,isCloudRole as D,runAsPerson as v,personId as $,bootPersonId as k,requestPersonId as C}from"./person-state.js";import{readConfig as j}from"../config.js";import{joinedIssuer as M,joinedOwner as S}from"../directory/join.js";import{installLocalTerminalGrant as F}from"../grants/local.js";import{dropLocalGrantMirror as x}from"../grants/mirror.js";import{ownerForGrantee as G,ensureOwner as J}from"../graph/objects.js";import{canvasDatabaseUrl as q,CanvasConstraintError as B,withCanvasWrite as h}from"./db.js";import{LEGACY_LOCAL_PERSON as E,LEGACY_LOCAL_TERMINAL_GRANT as Y}from"./legacy-person.js";import{rebuildTotalsFor as z}from"../ledger/totals.js";const l="person_id",O="local_person_retired",K=/^user:[A-Za-z0-9._-]+$/,I="abcdefghijklmnopqrstuvwxyz0123456789",L=new Map;async function Te(){const n=C();if(n!==void 0)return n;if(D())throw new A;const e=k();if(e!==void 0)return e;const r=await q(),t=L.get(r);if(t!==void 0)return t;const a=await Z();if(a===null)throw new g;return L.set(r,a),a}c(Te,"personIdAsync");function X(){const n=W(12);let e="user:";for(let r=0;r<12;r++)e+=I[n[r]%I.length];return e}c(X,"mintPersonId");async function m(n,e){return(await n.prepare("SELECT value FROM canvas_meta WHERE key = ?").get(e))?.value??null}c(m,"readMeta");async function d(n,e,r){await n.prepare("INSERT INTO canvas_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(e,r)}c(d,"writeMeta");async function V(){return await h(async n=>{const e=await m(n,l);if(e!==null)return{id:e,source:"stored"};const r=S();if(r!==null)return await d(n,l,r),{id:r,source:"joined"};if(j("user").runtime?.issuer){const a=await n.prepare("SELECT DISTINCT sub FROM grants").all();return a.length!==1?null:(await d(n,l,a[0].sub),{id:a[0].sub,source:"grants"})}const t=X();return await d(n,l,t),{id:t,source:"minted"}})}c(V,"resolvePerson");async function Z(){return(await V())?.id??null}c(Z,"resolvePersonId");const o=c(n=>Number(n.changes),"changes"),Q=[["nodes","sub"],["node_tokens","sub"],["grants","sub"],["crons","sub"],["audit_rows","sub"],["provider_approvals","sub"],["audit_rows","grantee"],["run_events","grantee"],["node_tokens","grantee"],["uploads","grantee"],["provider_approvals","grantee"]],ee=["profiles","profile_defaults","scope_settings","scope_bookkeeping","scope_marketplaces","scope_plugins","person_settings","model_accounts","model_account_cooldowns","model_api_keys","model_settings","model_sign_in_flows","env_vars"],ne=[["charges","sub"],["charges","payer"],["charges","grantee"],["limits","payer"],["limits","sub"],["limits","grantee"]];function P(n,e){if(!K.test(e))throw new T("invalid_person",`${n} must be a user:<id> principal, got ${JSON.stringify(e)}`)}c(P,"assertPerson");async function re(n,e){return await n.prepare("SELECT 1 AS found FROM charge_totals WHERE payer = ? OR sub = ? OR grantee = ? UNION ALL SELECT 1 FROM charge_chunks WHERE payer = ? LIMIT 1").get(e,e,e,e)!==void 0}c(re,"namesPerson");async function U(n,e,r){if(P("from",e),P("to",r),e===r)throw new T("invalid_person","cannot substitute a person with itself");const t={};try{t.identities=o(await n.prepare("UPDATE identities SET scheme = 'user', external_id = ? WHERE scheme IN ('local-user', 'user') AND external_id = ?").run(r,e))}catch(s){throw s instanceof B?new T("person_conflict",`another person object already holds ${r}; merging two person objects is not supported (${s.message})`,void 0,{from:e,to:r}):s}t["daemon_actions.principal_sub"]=o(await n.prepare(`UPDATE daemon_actions SET adapter_json = jsonb_set(adapter_json::jsonb, '{principal,sub}', to_jsonb(?::text))::text
2
2
  WHERE state IN ('pending', 'handed_off') AND adapter_json::jsonb ->> 'adapter' = 'cron_spawn'
3
- AND adapter_json::jsonb #>> '{principal,sub}' = ?`).run(r,e));for(const[s,i]of Z)t[`${s}.${i}`]=o(await n.prepareUncached(`UPDATE ${s} SET ${i} = ? WHERE ${i} = ?`).run(r,e));t["idempotency_keys.grantee"]=o(await n.prepare("DELETE FROM idempotency_keys WHERE grantee = ? AND key IN (SELECT key FROM idempotency_keys WHERE grantee = ?)").run(e,r))+o(await n.prepare("UPDATE idempotency_keys SET grantee = ? WHERE grantee = ?").run(r,e));const a=e.replace(/^user:/,""),p=r.replace(/^user:/,"");t["relations.subject_id"]=o(await n.prepare(`DELETE FROM relations r WHERE r.subject_type = 'user' AND r.subject_id = ?
3
+ AND adapter_json::jsonb #>> '{principal,sub}' = ?`).run(r,e));for(const[s,i]of Q)t[`${s}.${i}`]=o(await n.prepareUncached(`UPDATE ${s} SET ${i} = ? WHERE ${i} = ?`).run(r,e));t["idempotency_keys.grantee"]=o(await n.prepare("DELETE FROM idempotency_keys WHERE grantee = ? AND key IN (SELECT key FROM idempotency_keys WHERE grantee = ?)").run(e,r))+o(await n.prepare("UPDATE idempotency_keys SET grantee = ? WHERE grantee = ?").run(r,e));const a=e.replace(/^user:/,""),p=r.replace(/^user:/,"");t["relations.subject_id"]=o(await n.prepare(`DELETE FROM relations r WHERE r.subject_type = 'user' AND r.subject_id = ?
4
4
  AND EXISTS (SELECT 1 FROM relations n WHERE n.subject_type = 'user' AND n.subject_id = ? AND n.resource_type = r.resource_type
5
- AND n.resource_id = r.resource_id AND n.relation = r.relation AND n.subject_relation = r.subject_relation)`).run(a,p))+o(await n.prepare("UPDATE relations SET subject_id = ? WHERE subject_type = 'user' AND subject_id = ?").run(p,a)),t["relations.written_by"]=o(await n.prepare("UPDATE relations SET written_by = ? WHERE written_by = ?").run(r,e)),t["revisions.acting_for"]=o(await n.prepare("UPDATE revisions SET acting_for = ? WHERE acting_for = ?").run(r,e));for(const[s,i]of ee)t[`${s}.${i}`]=o(await n.prepareUncached(`UPDATE ${s} SET ${i} = ? WHERE ${i} = ?`).run(r,e));for(const s of Q){const u=o(await n.prepareUncached(`DELETE FROM ${s} WHERE person = ? AND EXISTS (SELECT 1 FROM ${s} WHERE person = ?)`).run(e,r))+o(await n.prepareUncached(`UPDATE ${s} SET person = ? WHERE person = ?`).run(r,e));u!==0&&(t[`${s}.person`]=u)}return(Object.entries(t).some(([s,i])=>s.startsWith("charges.")&&i!==0)||await ne(n,e))&&(await Y(n,e,r),t["charge_totals.rebuilt"]=1),t}c(U,"substitutePerson");async function re(n){const e=`SELECT o.id FROM objects o JOIN identities i ON i.object = o.owner JOIN nodes n ON n.node_id = o.id
6
- WHERE o.type = 'node' AND i.scheme = 'local-user' AND i.external_id = '${E}'`;return(await n.prepareUncached(`${e} LIMIT 1`).get()!==void 0?o(await n.prepareUncached(`UPDATE objects SET owner = ? WHERE id IN (${e})`).run((await x(n,"app:terminal")).id)):0)+o(await n.prepare("UPDATE node_tokens SET grantee = 'app:terminal' WHERE grantee = ?").run(E))+o(await n.prepare("UPDATE run_events SET grantee = 'app:terminal' WHERE grantee = ?").run(E))+o(await n.prepare("DELETE FROM idempotency_keys WHERE grantee = ? AND key IN (SELECT key FROM idempotency_keys WHERE grantee = 'app:terminal')").run(E))+o(await n.prepare("UPDATE idempotency_keys SET grantee = 'app:terminal' WHERE grantee = ?").run(E))}c(re,"rewriteLegacyGrantees");async function te(n,e,r){const t=[B,`terminal:${e}`].filter(i=>i!==r),a=t.map(()=>"?").join(", "),p={};p["nodes.grant_id"]=o(await n.prepareUncached(`UPDATE nodes SET grant_id = ? WHERE grant_id IN (${a})`).run(r,...t)),p["node_tokens.grant"]=o(await n.prepareUncached(`UPDATE node_tokens SET "grant" = ? WHERE "grant" IN (${a})`).run(r,...t)),p["crons.grant_id"]=o(await n.prepareUncached(`UPDATE crons SET grant_id = ? WHERE grant_id IN (${a})`).run(r,...t));const s=r===null?"'null'::jsonb":"to_jsonb(?::text)";return p["daemon_actions.principal_grant_id"]=o(await n.prepareUncached(`UPDATE daemon_actions SET adapter_json = jsonb_set(adapter_json::jsonb, '{principal,grant_id}', ${s})::text
5
+ AND n.resource_id = r.resource_id AND n.relation = r.relation AND n.subject_relation = r.subject_relation)`).run(a,p))+o(await n.prepare("UPDATE relations SET subject_id = ? WHERE subject_type = 'user' AND subject_id = ?").run(p,a)),t["relations.written_by"]=o(await n.prepare("UPDATE relations SET written_by = ? WHERE written_by = ?").run(r,e)),t["revisions.acting_for"]=o(await n.prepare("UPDATE revisions SET acting_for = ? WHERE acting_for = ?").run(r,e));for(const[s,i]of ne)t[`${s}.${i}`]=o(await n.prepareUncached(`UPDATE ${s} SET ${i} = ? WHERE ${i} = ?`).run(r,e));for(const s of ee){const u=o(await n.prepareUncached(`DELETE FROM ${s} WHERE person = ? AND EXISTS (SELECT 1 FROM ${s} WHERE person = ?)`).run(e,r))+o(await n.prepareUncached(`UPDATE ${s} SET person = ? WHERE person = ?`).run(r,e));u!==0&&(t[`${s}.person`]=u)}return(Object.entries(t).some(([s,i])=>s.startsWith("charges.")&&i!==0)||await re(n,e))&&(await z(n,e,r),t["charge_totals.rebuilt"]=1),t}c(U,"substitutePerson");async function te(n){const e=`SELECT o.id FROM objects o JOIN identities i ON i.object = o.owner JOIN nodes n ON n.node_id = o.id
6
+ WHERE o.type = 'node' AND i.scheme = 'local-user' AND i.external_id = '${E}'`;return(await n.prepareUncached(`${e} LIMIT 1`).get()!==void 0?o(await n.prepareUncached(`UPDATE objects SET owner = ? WHERE id IN (${e})`).run((await G(n,"app:terminal")).id)):0)+o(await n.prepare("UPDATE node_tokens SET grantee = 'app:terminal' WHERE grantee = ?").run(E))+o(await n.prepare("UPDATE run_events SET grantee = 'app:terminal' WHERE grantee = ?").run(E))+o(await n.prepare("DELETE FROM idempotency_keys WHERE grantee = ? AND key IN (SELECT key FROM idempotency_keys WHERE grantee = 'app:terminal')").run(E))+o(await n.prepare("UPDATE idempotency_keys SET grantee = 'app:terminal' WHERE grantee = ?").run(E))}c(te,"rewriteLegacyGrantees");async function ae(n,e,r){const t=[Y,`terminal:${e}`].filter(i=>i!==r),a=t.map(()=>"?").join(", "),p={};p["nodes.grant_id"]=o(await n.prepareUncached(`UPDATE nodes SET grant_id = ? WHERE grant_id IN (${a})`).run(r,...t)),p["node_tokens.grant"]=o(await n.prepareUncached(`UPDATE node_tokens SET "grant" = ? WHERE "grant" IN (${a})`).run(r,...t)),p["crons.grant_id"]=o(await n.prepareUncached(`UPDATE crons SET grant_id = ? WHERE grant_id IN (${a})`).run(r,...t));const s=r===null?"'null'::jsonb":"to_jsonb(?::text)";return p["daemon_actions.principal_grant_id"]=o(await n.prepareUncached(`UPDATE daemon_actions SET adapter_json = jsonb_set(adapter_json::jsonb, '{principal,grant_id}', ${s})::text
7
7
  WHERE state IN ('pending', 'handed_off') AND adapter_json::jsonb ->> 'adapter' = 'cron_spawn'
8
- AND adapter_json::jsonb #>> '{principal,grant_id}' IN (${a})`).run(...r===null?[]:[r],...t)),p}c(te,"repointGrantReferences");async function Te(){const n=await h(async e=>{const r={},t=c((_,w="")=>{for(const[N,R]of Object.entries(_))R!==0&&(r[w+N]=(r[w+N]??0)+R)},"add");let a=await m(e,l);if(a===null)throw new g;let p=null;const s=j();s!==null&&s!==a&&(t(await U(e,a,s),"join."),await d(e,l,s),p=a,a=s);const i=S("user").runtime?.issuer,u=await m(e,"install_id");if(u===null)throw new Error("retireLocalPerson: the install id is not stored");const f=await m(e,O)!==a;if(f){t({legacy_grantees:await re(e)}),t(await U(e,E,a));const _=`SELECT o.id FROM objects o JOIN peer_stubs s ON s.object = o.id JOIN identities i ON i.object = o.owner
9
- WHERE o.type = 'node' AND o.reception = 2 AND s.envelope_json ->> 'app' IS NULL AND i.scheme = 'app' AND i.external_id = 'app:terminal'`;if(await e.prepareUncached(`${_} LIMIT 1`).get()!==void 0){const w=await G(e,"person","user",a,"user");t({repaired_stubs:o(await e.prepareUncached(`UPDATE objects SET owner = ? WHERE id IN (${_})`).run(w.id))})}t({"nodes.sub_null_terminal":o(await e.prepare(`UPDATE nodes SET sub = ? WHERE sub IS NULL AND node_id IN (
8
+ AND adapter_json::jsonb #>> '{principal,grant_id}' IN (${a})`).run(...r===null?[]:[r],...t)),p}c(ae,"repointGrantReferences");async function ge(){const n=await h(async e=>{const r={},t=c((_,w="")=>{for(const[N,R]of Object.entries(_))R!==0&&(r[w+N]=(r[w+N]??0)+R)},"add");let a=await m(e,l);if(a===null)throw new g;let p=null;const s=S();s!==null&&s!==a&&(t(await U(e,a,s),"join."),await d(e,l,s),p=a,a=s);const i=M()??j("user").runtime?.issuer,u=await m(e,"install_id");if(u===null)throw new Error("retireLocalPerson: the install id is not stored");const f=await m(e,O)!==a;if(f){t({legacy_grantees:await te(e)}),t(await U(e,E,a));const _=`SELECT o.id FROM objects o JOIN peer_stubs s ON s.object = o.id JOIN identities i ON i.object = o.owner
9
+ WHERE o.type = 'node' AND o.reception = 2 AND s.envelope_json ->> 'app' IS NULL AND i.scheme = 'app' AND i.external_id = 'app:terminal'`;if(await e.prepareUncached(`${_} LIMIT 1`).get()!==void 0){const w=await J(e,"person","user",a,"user");t({repaired_stubs:o(await e.prepareUncached(`UPDATE objects SET owner = ? WHERE id IN (${_})`).run(w.id))})}t({"nodes.sub_null_terminal":o(await e.prepare(`UPDATE nodes SET sub = ? WHERE sub IS NULL AND node_id IN (
10
10
  SELECT n.node_id FROM nodes n JOIN identities i ON i.object = n.app
11
- WHERE i.scheme = 'app' AND i.external_id = 'app:terminal')`).run(a))}),t(await te(e,u,i?null:`terminal:${u}`)),i&&t({local_grant_mirror:await F(e)}),await d(e,O,a)}return i||await M(u,a),{person:a,joinedFrom:p,retired:f,columns:r}});return y(n.person),n}c(Te,"retireLocalPerson");export{E as LEGACY_LOCAL_PERSON,A as PersonOutsideRequestError,g as PersonUnresolvedError,D as isCloudRole,b as markCloudRole,$ as personId,me as personIdAsync,X as resolvePerson,V as resolvePersonId,Te as retireLocalPerson,v as runAsPerson,y as setPersonId,U as substitutePerson};
11
+ WHERE i.scheme = 'app' AND i.external_id = 'app:terminal')`).run(a))}),t(await ae(e,u,i?null:`terminal:${u}`)),i&&t({local_grant_mirror:await x(e)}),await d(e,O,a)}return i||await F(u,a),{person:a,joinedFrom:p,retired:f,columns:r}});return y(n.person),n}c(ge,"retireLocalPerson");export{E as LEGACY_LOCAL_PERSON,A as PersonOutsideRequestError,g as PersonUnresolvedError,D as isCloudRole,b as markCloudRole,$ as personId,Te as personIdAsync,V as resolvePerson,Z as resolvePersonId,ge as retireLocalPerson,v as runAsPerson,y as setPersonId,U as substitutePerson};
@@ -40,10 +40,12 @@ export interface JoinDaemonControl {
40
40
  }
41
41
  /** Provider plugin ids installed on this machine (reported for display only). */
42
42
  export declare function installedPluginIds(): string[];
43
- /** The owner a successful join recorded, or null when `crtr sys join` never
44
- * wrote the client file (no file, or one provisioned without join, which has
45
- * no `token_sha256`). The daemon's `retireLocalPerson` makes it the install's person at the next start. */
43
+ /** The owner a successful join recorded, or null when `crtr sys join` never wrote the client file. */
46
44
  export declare function joinedOwner(): string | null;
45
+ /** The issuer a successful join recorded, or null. */
46
+ export declare function joinedIssuer(): string | null;
47
+ /** The whole registration a successful join recorded, or null; a damaged file throws. */
48
+ export declare function joinedClient(): Registration | null;
47
49
  /** Join this runtime to a directory.
48
50
  *
49
51
  * With `daemon` control, a running daemon is no obstacle: the join holds the
@@ -1 +1 @@
1
- var I=Object.defineProperty;var a=(t,e)=>I(t,"name",{value:e,configurable:!0});import{createHash as N,generateKeyPairSync as J,randomUUID as L}from"node:crypto";import{existsSync as R,mkdirSync as U,readdirSync as O,rmSync as y}from"node:fs";import{hostname as C}from"node:os";import{join as b}from"node:path";import{InputError as p}from"../io.js";import{network as k}from"../errors.js";import{secretsDir as _}from"../layout.js";import{apiSocketPath as D}from"../canvas/paths.js";import{daemonAnswers as A}from"../layout-migrate/index.js";import{atomicWriteJson as j,readJsonIfExists as x}from"../fs-utils.js";import{updateConfig as K}from"../config.js";import{launcherPresent as T}from"../grants/sandboxes.js";import{crtrVersion as W}from"../../shared/crtr-version.js";import{pluginsDir as q}from"../scope.js";import{writeSettings as z}from"../scoped-state/person-store.js";import{LEGACY_LOCAL_PERSON as B}from"../canvas/legacy-person.js";import{withJoinLock as F}from"./join-lock.js";function l(){return b(_(),"runtime-client.json")}a(l,"clientPath");function S(t){if(typeof t!="string")return!1;try{const{protocol:e}=new URL(t);return e==="http:"||e==="https:"}catch{return!1}}a(S,"isUrl");function $(t){const e=t??{},{runtime:o,client_id:r,issuer:u,owner:n,url:s}=e;if(typeof o!="string"||!/^runtime:.+/.test(o)||typeof r!="string"||r===""||!S(u)||typeof n!="string"||!/^user:.+/.test(n)||!S(s))throw k("directory registration answer is missing runtime, client_id, issuer, owner, or url");const{machine_id:i,is_default:c}=e;return{runtime:o,client_id:r,issuer:u,owner:n,url:s,...typeof i=="string"?{machine_id:i}:{},...typeof c=="boolean"?{is_default:c}:{}}}a($,"registration");function w(){return b(_(),"runtime-client.pending.json")}a(w,"pendingPath");function G(){try{const t=q("user");return!t||!R(t)?[]:O(t,{withFileTypes:!0}).filter(e=>e.isDirectory()&&!e.name.startsWith(".")).map(e=>e.name).sort()}catch{return[]}}a(G,"installedPluginIds");async function V(t,e,o,r){const u=`${t.replace(/\/$/,"")}/v1/runtimes/registration`;let n;try{n=await fetch(u,{method:"PUT",headers:{authorization:`Bearer ${e}`,"content-type":"application/json"},body:JSON.stringify({jwk:o,name:r,platform:`${process.platform}-${process.arch}`,crtr_version:W(),plugins:G(),sandboxed:T()}),signal:AbortSignal.timeout(3e4)})}catch(i){throw k(`directory registration request to ${u} failed: ${i instanceof Error?i.message:String(i)}`)}const s=await n.text();if(!n.ok)throw n.status<500&&y(w(),{force:!0}),new p({error:"join_refused",message:`directory refused the registration: ${n.status} ${s.slice(0,500)}`,next:`Create a new join token at ${t.replace(/\/$/,"")}/account/machines (tokens are single-use and expire after 10 minutes), and run \`crtr sys join\` again.`});try{return $(JSON.parse(s))}catch(i){throw i instanceof SyntaxError?k("directory registration answer is not JSON"):i}}a(V,"register");async function E(t){await K("user",e=>{e.runtime=t.is_default===!1?{issuer:t.issuer,audience:t.url,unsandboxed:!0}:{...e.runtime,issuer:t.issuer,audience:t.url}}),await z(e=>e.profiles.filter(o=>o.manifest.grantee===B).map(o=>({op:"profile.update",profile:{profileId:o.profileId,manifest:{...o.manifest,grantee:"app:terminal"}}})))}a(E,"commit");function mt(){const t=x(l());if(t?.token_sha256===void 0)return null;const{owner:e}=t;if(typeof e!="string"||!/^user:.+/.test(e))throw new Error(`${l()} was written by join but has a missing or invalid owner`);return e}a(mt,"joinedOwner");function P(t){return new p({error:"daemon_running",message:`cannot join while a daemon answers on ${t}`,next:"This daemon is managed outside crtr, so join cannot restart it: stop the daemon that owns this volume, then run `crtr sys join` again."})}a(P,"daemonRunningError");async function v(t,e,o,r){const u=x(l());if(u?.token_sha256===r)return{joined:$(u),registered:!1};U(_(),{recursive:!0,mode:448});let n=x(w());if(n?.token_sha256!==r||n.directory!==t||!n.jwk||!n.public_jwk){const{privateKey:g,publicKey:d}=J("ec",{namedCurve:"P-256"}),f=L();n={token_sha256:r,directory:t,jwk:{...g.export({format:"jwk"}),kid:f,alg:"ES256",use:"sig"},public_jwk:{...d.export({format:"jwk"}),kid:f,alg:"ES256",use:"sig"}},j(w(),n,{mode:384})}const{jwk:s,public_jwk:i}=n,c=await V(t,e,i,o);return{joined:c,registered:!0,client:{jwk:s,kid:s.kid,alg:"ES256",...c,token_sha256:r}}}a(v,"obtainRegistration");async function pt(t,e,o=C(),r){if(!S(t))throw new p({error:"invalid_directory",message:"directory must be an http or https URL",received:t,field:"directory",next:"Pass the directory base URL, for example https://directory.example."});if(e==="")throw new p({error:"invalid_token",message:"token is empty",field:"token",next:"Pass the enrollment token provisioning supplied."});const u=D(),n=N("sha256").update(e).digest("hex");if(r===void 0){if(await A(u))throw P(u);const{joined:s,registered:i,client:c}=await v(t,e,o,n);return c&&(j(l(),c,{mode:384}),y(w(),{force:!0})),await E(s),{...s,registered:i,daemon:"not_running"}}return F(async()=>{const s=a(()=>new p({error:"join_inside_node",message:"a daemon is running and this command is inside a node: stopping the daemon would kill this node's broker and this command with it",next:"Run `crtr sys join` from a plain terminal outside any crtr node. It stops the daemon, joins, and starts it again for you."}),"refuseInsideNode");if(await r.running()&&r.insideNode())throw s();const{joined:i,registered:c,client:g}=await v(t,e,o,n);let d=!1,f;try{if(await r.running()){if(r.insideNode())throw s();if(d=!0,await r.stop(),await r.running())throw P(u)}if(g&&(j(l(),g,{mode:384}),y(w(),{force:!0})),await E(i),await r.running()){if(r.insideNode())throw s();d=!0,await r.stop()}}catch(m){f=m}let h={daemon:"not_running"};if(f===void 0||d)try{await r.start(),h={daemon:d?"restarted":"started"}}catch(m){h={daemon:"restart_failed",daemon_error:m instanceof Error?m.message:String(m)}}if(f!==void 0)throw f;return h.daemon==="started"&&await r.firstSync?.(),{...i,registered:c,...h}})}a(pt,"joinDirectory");export{G as installedPluginIds,pt as joinDirectory,mt as joinedOwner};
1
+ var J=Object.defineProperty;var o=(t,r)=>J(t,"name",{value:r,configurable:!0});import{createHash as I,generateKeyPairSync as N,randomUUID as U}from"node:crypto";import{existsSync as R,mkdirSync as D,readdirSync as L,rmSync as y}from"node:fs";import{hostname as O}from"node:os";import{join as v}from"node:path";import{InputError as l}from"../io.js";import{network as j}from"../errors.js";import{secretsDir as k}from"../layout.js";import{apiSocketPath as C}from"../canvas/paths.js";import{daemonAnswers as K}from"../layout-migrate/index.js";import{atomicWriteJson as _,readJsonIfExists as x}from"../fs-utils.js";import{launcherPresent as T}from"../grants/sandboxes.js";import{crtrVersion as A}from"../../shared/crtr-version.js";import{pluginsDir as W}from"../scope.js";import{withJoinLock as q}from"./join-lock.js";function p(){return v(k(),"runtime-client.json")}o(p,"clientPath");function S(t){if(typeof t!="string")return!1;try{const{protocol:r}=new URL(t);return r==="http:"||r==="https:"}catch{return!1}}o(S,"isUrl");function b(t){const r=t??{},{runtime:c,client_id:e,issuer:a,owner:n,url:s}=r;if(typeof c!="string"||!/^runtime:.+/.test(c)||typeof e!="string"||e===""||!S(a)||typeof n!="string"||!/^user:.+/.test(n)||!S(s))throw j("directory registration answer is missing runtime, client_id, issuer, owner, or url");const{machine_id:i,is_default:u}=r;return{runtime:c,client_id:e,issuer:a,owner:n,url:s,...typeof i=="string"?{machine_id:i}:{},...typeof u=="boolean"?{is_default:u}:{}}}o(b,"registration");function w(){return v(k(),"runtime-client.pending.json")}o(w,"pendingPath");function z(){try{const t=W("user");return!t||!R(t)?[]:L(t,{withFileTypes:!0}).filter(r=>r.isDirectory()&&!r.name.startsWith(".")).map(r=>r.name).sort()}catch{return[]}}o(z,"installedPluginIds");async function B(t,r,c,e){const a=`${t.replace(/\/$/,"")}/v1/runtimes/registration`;let n;try{n=await fetch(a,{method:"PUT",headers:{authorization:`Bearer ${r}`,"content-type":"application/json"},body:JSON.stringify({jwk:c,name:e,platform:`${process.platform}-${process.arch}`,crtr_version:A(),plugins:z(),sandboxed:T()}),signal:AbortSignal.timeout(3e4)})}catch(i){throw j(`directory registration request to ${a} failed: ${i instanceof Error?i.message:String(i)}`)}const s=await n.text();if(!n.ok)throw n.status<500&&y(w(),{force:!0}),new l({error:"join_refused",message:`directory refused the registration: ${n.status} ${s.slice(0,500)}`,next:`Create a new join token at ${t.replace(/\/$/,"")}/account/machines (tokens are single-use and expire after 10 minutes), and run \`crtr sys join\` again.`});try{return b(JSON.parse(s))}catch(i){throw i instanceof SyntaxError?j("directory registration answer is not JSON"):i}}o(B,"register");function $(){const t=x(p());if(t?.token_sha256===void 0)return null;const{owner:r}=t;if(typeof r!="string"||!/^user:.+/.test(r))throw new Error(`${p()} was written by join but has a missing or invalid owner`);return t}o($,"readJoined");function st(){return $()?.owner??null}o(st,"joinedOwner");function at(){return $()?.issuer??null}o(at,"joinedIssuer");function ct(){const t=$();return t===null?null:b(t)}o(ct,"joinedClient");function E(t){return new l({error:"daemon_running",message:`cannot join while a daemon answers on ${t}`,next:"This daemon is managed outside crtr, so join cannot restart it: stop the daemon that owns this volume, then run `crtr sys join` again."})}o(E,"daemonRunningError");async function P(t,r,c,e){const a=x(p());if(a?.token_sha256===e)return{joined:b(a),registered:!1};D(k(),{recursive:!0,mode:448});let n=x(w());if(n?.token_sha256!==e||n.directory!==t||!n.jwk||!n.public_jwk){const{privateKey:h,publicKey:f}=N("ec",{namedCurve:"P-256"}),d=U();n={token_sha256:e,directory:t,jwk:{...h.export({format:"jwk"}),kid:d,alg:"ES256",use:"sig"},public_jwk:{...f.export({format:"jwk"}),kid:d,alg:"ES256",use:"sig"}},_(w(),n,{mode:384})}const{jwk:s,public_jwk:i}=n,u=await B(t,r,i,c);return{joined:u,registered:!0,client:{jwk:s,kid:s.kid,alg:"ES256",...u,token_sha256:e}}}o(P,"obtainRegistration");async function ut(t,r,c=O(),e){if(!S(t))throw new l({error:"invalid_directory",message:"directory must be an http or https URL",received:t,field:"directory",next:"Pass the directory base URL, for example https://directory.example."});if(r==="")throw new l({error:"invalid_token",message:"token is empty",field:"token",next:"Pass the enrollment token provisioning supplied."});const a=C(),n=I("sha256").update(r).digest("hex");if(e===void 0){if(await K(a))throw E(a);const{joined:s,registered:i,client:u}=await P(t,r,c,n);return u&&(_(p(),u,{mode:384}),y(w(),{force:!0})),{...s,registered:i,daemon:"not_running"}}return q(async()=>{const s=o(()=>new l({error:"join_inside_node",message:"a daemon is running and this command is inside a node: stopping the daemon would kill this node's broker and this command with it",next:"Run `crtr sys join` from a plain terminal outside any crtr node. It stops the daemon, joins, and starts it again for you."}),"refuseInsideNode");if(await e.running()&&e.insideNode())throw s();const{joined:i,registered:u,client:h}=await P(t,r,c,n);let f=!1,d;try{if(await e.running()){if(e.insideNode())throw s();if(f=!0,await e.stop(),await e.running())throw E(a)}if(h&&(_(p(),h,{mode:384}),y(w(),{force:!0})),await e.running()){if(e.insideNode())throw s();f=!0,await e.stop()}}catch(m){d=m}let g={daemon:"not_running"};if(d===void 0||f)try{await e.start(),g={daemon:f?"restarted":"started"}}catch(m){g={daemon:"restart_failed",daemon_error:m instanceof Error?m.message:String(m)}}if(d!==void 0)throw d;return g.daemon==="started"&&await e.firstSync?.(),{...i,registered:u,...g}})}o(ut,"joinDirectory");export{z as installedPluginIds,ut as joinDirectory,ct as joinedClient,at as joinedIssuer,st as joinedOwner};
@@ -0,0 +1,3 @@
1
+ /** Apply `CRTR_BOOT_USER_FILES`, `CRTR_BOOT_LISTENER_ADDRESS`/`_PORT`, `CRTR_BOOT_STORAGE_URL` and the join file's identity to the user
2
+ * settings, and move the legacy local person's profiles to `app:terminal` once a join exists. Writes nothing when the settings already hold them. */
3
+ export declare function applyBootSettings(): Promise<void>;
@@ -0,0 +1 @@
1
+ var R=Object.defineProperty;var u=(t,n)=>R(t,"name",{value:n,configurable:!0});import{isDeepStrictEqual as _}from"node:util";import{readConfig as p,updateConfig as T}from"../core/config.js";import{joinedClient as c}from"../core/directory/join.js";import{LEGACY_LOCAL_PERSON as m}from"../core/canvas/legacy-person.js";import{writeSettings as E}from"../core/scoped-state/person-store.js";function f(t){const n=process.env[t];return n===void 0||n===""?void 0:n}u(f,"bootEnv");function O(){const t=f("CRTR_BOOT_LISTENER_ADDRESS"),n=f("CRTR_BOOT_LISTENER_PORT"),e=f("CRTR_BOOT_STORAGE_URL");if(t===void 0&&n===void 0&&e===void 0)return null;const s={};if(t===void 0!=(n===void 0))throw new Error("CRTR_BOOT_LISTENER_ADDRESS and CRTR_BOOT_LISTENER_PORT must be set together");if(t!==void 0&&n!==void 0){const o=Number(n);if(!Number.isInteger(o)||o<1||o>65535)throw new Error(`CRTR_BOOT_LISTENER_PORT is not a port: ${n}`);s.listener={address:t,port:o}}if(e!==void 0){try{new URL(e)}catch{throw new Error(`CRTR_BOOT_STORAGE_URL is not a URL: ${e}`)}s.storage={service_url:e,base_url:e}}return s}u(O,"bootRuntimeSettings");async function B(){const t=f("CRTR_BOOT_USER_FILES"),n=O(),e=c(),s=p("user"),d=u(i=>{let r=i;const a=e!==null&&e.is_default===!1;if(e!==null){const l=i?.issuer===e.issuer&&i?.audience===e.url;r=a&&!l?{issuer:e.issuer,audience:e.url,unsandboxed:!0}:{...r,issuer:e.issuer,audience:e.url}}return n!==null&&!a&&(r={...r,...n}),r},"runtimeNext")(s.runtime);(t!==void 0&&s.paths?.user_files!==t||!_(d,s.runtime))&&await T("user",i=>{t!==void 0&&(i.paths={...i.paths,user_files:t}),d!==void 0&&(i.runtime=d)}),e!==null&&await E(i=>i.profiles.filter(r=>r.manifest.grantee===m).map(r=>({op:"profile.update",profile:{profileId:r.profileId,manifest:{...r.manifest,grantee:"app:terminal"}}})))}u(B,"applyBootSettings");export{B as applyBootSettings};
@@ -1,18 +1,18 @@
1
- var er=Object.defineProperty;var i=(e,l)=>er(e,"name",{value:l,configurable:!0});import{startPeerOutbox as rr}from"./peer/outbox.js";import{reportMachineStatus as tr}from"../core/peer/directory.js";import{createPeerRelay as nr}from"./peer/relay-service.js";import{writeFileSync as or,readFileSync as ir,rmSync as ar,mkdirSync as ye,existsSync as _,unlinkSync as be,chmodSync as _e}from"node:fs";import{createServer as J}from"node:http";import{dirname as sr}from"node:path";import{envCrtrdTcp as ke,envDocumentPort as cr}from"../shared/env.js";import{serveCanvasConversationStore as lr}from"../core/canvas/sessions.js";import{pollRunObservers as dr}from"./runs/observer.js";import{randomBytes as Re}from"node:crypto";import{apiSocketPath as Pe,crtrHome as pr}from"../core/canvas/paths.js";import{activeAppUsers as mr}from"../core/spaces/users.js";import{ensureAppSpace as fr,ensureUserFiles as ur}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 wr}from"./launcher-events.js";import{initializeEmptyLayout as hr,layoutNeedsMigration as vr,socketDir as Ee}from"../core/layout.js";import{migrateLooseKeys as gr}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as Sr}from"../core/profiles/relocate.js";import{getNode as yr,listNodesForSupervision as Ie,listParkedResidentsWithWakeMail as br,setFrozen as xe}from"../core/canvas/index.js";import{createApiServer as _r,closeServer as Te}from"./api/server.js";import{Router as kr}from"./api/router.js";import{healthRoutes as Rr}from"./api/handlers/health.js";import{createDocumentListener as Pr}from"./api/document-listener.js";import{documentSecret as Er}from"./api/document-secret.js";import{createAppListener as Ir}from"./api/app-listener.js";import{registerMachineEnvironment as xr}from"../core/environments.js";import{registerBlaxelRunnerFromEnv as Tr}from"../core/secrets/blaxel-register.js";import{markCloudRole as Dr,resolvePerson as Cr,retireLocalPerson as Lr,setPersonId as $r}from"../core/canvas/person.js";import{backfillNodeApps as Ar,runPermissionsBoot as Nr}from"../core/permissions/boot.js";import{startRunNotices as Mr}from"./run-notices.js";import{startGrantEffects as Gr}from"./reconcilers/grant-effects.js";import{createGrantSync as Or,GRANT_SYNC_INTERVAL_S as Fr}from"../core/grants/sync.js";import{sweepSandboxes as Br}from"../core/grants/on-sync.js";import{rebuildCronExecutions as jr}from"./cron/run-socket.js";import{hasSyncedGrants as zr}from"../core/grants/mirror.js";import{readConfig as se}from"../core/config.js";import{reconcileUndeliveredTickets as Hr}from"./human/sweep.js";import{resetHandedOffDeliveries as qr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as Ur}from"../core/canvas/daemon-actions.js";import{sweepReviews as Wr}from"./review/sweep.js";import{pidfilePath as ce}from"./pidfile.js";import{rendererWarning as Jr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as Kr}from"./supervise-cadence.js";import{createResumeDetector as Vr}from"./resume-detector.js";import{setFatalFaultAnnouncer as Qr}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as Yr}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as Xr}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as Zr}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as et}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as rt}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as tt}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as nt}from"./reconcilers/action-lane.js";import{endLiveExecution as ot,migrateLegacyExecutionIdentities as it}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as at}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as st}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as ct}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as lt}from"./reconcilers/storage-due-work.js";import{LedgerSandboxReconciler as dt}from"./reconcilers/ledger-sandbox.js";import{LedgerStorageReconciler as pt}from"./reconcilers/ledger-storage.js";import{LedgerR2Reconciler as mt}from"./reconcilers/ledger-r2.js";import{BashDeadlineReconciler as ft}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as ut}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as wt}from"./reconcilers/structured-request-lane.js";import{RecipeImageReconciler as ht,subscribeImageBuilt as vt}from"./reconcilers/recipe-image.js";import{reconcileStructuredRequestSidecars as gt}from"./structured-requests/boot.js";import{captureLivenessSnapshot as De,capturePidCommand as St,captureTeardownSnapshot as Ce,isPidAlive as yt,isAnyPidAlive as Le,killProcessTreePids as $e}from"../core/canvas/pid.js";import{bindFleet as bt,boundFleet as _t}from"../core/runtime/fleet.js";import{bindDaemonControl as kt,clearDaemonControl as Rt}from"./control.js";import{execDaemon as Pt,spawnDaemon as Et}from"./manage.js";import{reconcileAndMigrateBoot as It}from"../core/canvas/boot.js";import{resolveInstallId as xt}from"../core/canvas/install-id.js";import{setInstallId as Tt}from"../core/runtime/nodes.js";import{claimDaemonOwnership as Dt,daemonOwnershipApplies as Ct,daemonOwnershipClaimed as Lt,releaseDaemonOwnership as Ae}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as $t,closeAllDbs as le,openDb as Ne,withCanvasWrite as At}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as Nt}from"../core/canvas/import-sqlite.js";import{ensurePostgres as Mt,ensurePostgresHealthy as Gt,stopPostgres as Ot,withPostgresStartLock as Ft}from"./postgres/index.js";import{brokerSignalBus as F}from"./broker-signals/bus.js";import{brokerSignalStreams as Bt}from"./api/handlers/broker-signals.js";import{setRequestsStartRunObservers as jt}from"./api/handlers/broker-ops.js";import{reconcilePackages as zt}from"../core/graph/package-reconcile.js";import{startRepoSync as Ht}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as qt,reapStaleSpares as Ut}from"../core/runtime/warm-pool.js";import{DaemonFleet as Wt}from"./fleet.js";import{copyNodeFilesToRows as Jt}from"../core/runtime/first-boot-rows.js";import{BOOT_MIGRATIONS as Kt}from"./boot-migrations.js";import{emitEvent as u}from"../core/events/emit.js";import{operationIdContext as b}from"../core/events/operation-id.js";import{bindDaemonEventSource as Vt}from"../core/events/source.js";import{activateCanvasImport as Qt,MigrationBlockedError as Yt,runForcedCanvasImport as Xt}from"../migrations/activation.js";import{adoptInstallPerson as Zt,convertLegacyScopedStore as en,personStoresCopied as rn}from"../core/scoped-state/migrate.js";import{readLegacyUserSnapshot as tn}from"../core/scoped-state/legacy-profiles.js";import{heldPersons as Me,installPreludeSnapshot as nn}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 on=5e3,Fe=150;async function an(e){const l=De();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=pr(),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=St(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=Ce(n.pid,null);if(!y.reused){try{process.platform==="linux"&&_(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()+on;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"&&_(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=De();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(an,"reapPriorEpochBrokers");const sn=5e3;async function cn(e){const l=Array.from(e.entries());if(l.length===0)return;for(const[a]of l){const w=(await yr(a))?.status;(w==="active"||w==="idle")&&await xe(a,new Date().toISOString())}const f=l.map(([,a])=>({pid:a.pid,snap:Ce(a.pid,null),exited:a.handle.exited}));for(const a of f)if(!a.snap.reused)try{process.platform==="linux"&&_(x)?await L({operation:"stop",pid:a.pid}):process.kill(a.pid,"SIGTERM")}catch{}const m=new Promise(a=>{setTimeout(a,sn).unref()});await Promise.allSettled(f.map(a=>Promise.race([a.exited.then(()=>{}),m])));const v=[];for(const a of f)yt(a.pid)&&(process.platform==="linux"&&_(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 ot(a,{reason:"daemon-teardown"});u({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:f.length,sigkilled:v.length}})}i(cn,"teardownFleet");function Be(){ye(sr(ce()),{recursive:!0}),or(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=ir(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{ar(l)}catch(m){if(je(m))return;throw m}}i(ze,"removeOwnedPidfileMirror");const ln={acceptsDetachedWork:i(()=>!0,"acceptsDetachedWork"),registerDetached:i(e=>{e.catch(()=>{})},"registerDetached")},K=new Yr;Qr(e=>K.handleFatalFault(e,!1));const dn=new Xr,pn=new Zr,mn=new et,fn=new rt,un=new tt,wn=new nt,hn=new st,vn=new ct,gn=new lt,Sn=new dt,yn=new pt,bn=new mt,_n=new ft,kn=new ut,Rn=new wt,Pn=new ht;async function En(e=Date.now(),l=ln){const f=_t(),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 br()).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 dn.run(e,{rows:v});const w=await pn.run(e,{rows:[...v,...a],fleet:f});await mn.run(e),await Rn.run(e),await Pn.run(e,{lifecycle:l}),await fn.run(e,{lifecycle:l}),await un.run(e,{lifecycle:l}),await wn.run(e,{lifecycle:l}),await hn.run(e,{lifecycle:l}),await vn.run(e,{lifecycle:l,fleet:f}),gn.run(e,{lifecycle:l}),Sn.run(e,{lifecycle:l}),yn.run(e,{lifecycle:l}),bn.run(e,{lifecycle:l}),await dr(),kn.run(e,{fleet:f,lifecycle:l}),await _n.run(e,{rows:v}),K.warnIfSaturated({fleet:f,lifecycle:l,thresholds:m,frozen:w})}i(En,"superviseTick");function V(e){return e!=="api"}i(V,"roleRunsLanes");function $(e){return e!=="reconcile"}i($,"roleServesRequests");const In=66e4;function xn(e){const l=new kr().registerAll(Rr(()=>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();_(m)&&be(m);const v=J(f),a=new Promise((n,d)=>{v.once("error",d),v.listen(m,()=>{v.off("error",d),_e(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{_(m)&&be(m)}catch{}}}}}i(xn,"createProbeServer");const Tn="daemon is draining for shutdown and admits no new work";async function Dn(e){ye(Ee(),{recursive:!0,mode:493}),process.platform==="linux"&&_e(Ee(),493);const l=Pe();if(_(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}i(Dn,"serveLayoutRefusal");async function Cn(e={}){if(vr())return Dn(e);hr();const l=i(r=>{process.stderr.write(`[crtrd] ${r}
3
- `)},"logPostgres"),f=!!process.env.CRTR_TEST_PG_URL,m=Ct(e.role);jt(V(e.role));const v=i(async()=>m?await Dt(Re(16).toString("hex")):{claimed:!0,claim:{pid:process.pid,pid_identity:"",token:Re(16).toString("hex")}},"claimOwnership"),{database:a,ownership:w}=f?{database:{url:await $t()},ownership:await v()}:await Ft(async()=>({database:await Mt({log:l,startLockHeld:!0}),ownership:await v()}));if(!w.claimed){if(w.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${w.incumbentPid??"?"})
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 _,unlinkSync as be,chmodSync as _e}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{serveCanvasConversationStore as dr}from"../core/canvas/sessions.js";import{pollRunObservers as pr}from"./runs/observer.js";import{randomBytes as Re}from"node:crypto";import{apiSocketPath as Pe,crtrHome as mr}from"../core/canvas/paths.js";import{activeAppUsers as fr}from"../core/spaces/users.js";import{ensureAppSpace as ur,ensureUserFiles as wr}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 hr}from"./launcher-events.js";import{initializeEmptyLayout as vr,layoutNeedsMigration as gr,socketDir as Ee}from"../core/layout.js";import{migrateLooseKeys as Sr}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as yr}from"../core/profiles/relocate.js";import{getNode as br,listNodesForSupervision as Ie,listParkedResidentsWithWakeMail as _r,setFrozen as xe}from"../core/canvas/index.js";import{createApiServer as kr,closeServer as Te}from"./api/server.js";import{Router as Rr}from"./api/router.js";import{healthRoutes as Pr}from"./api/handlers/health.js";import{createDocumentListener as Er}from"./api/document-listener.js";import{documentSecret as Ir}from"./api/document-secret.js";import{createAppListener as xr}from"./api/app-listener.js";import{registerMachineEnvironment as Tr}from"../core/environments.js";import{registerBlaxelRunnerFromEnv as Dr}from"../core/secrets/blaxel-register.js";import{markCloudRole as Cr,resolvePerson as Lr,retireLocalPerson as $r,setPersonId as Ar}from"../core/canvas/person.js";import{backfillNodeApps as Nr,runPermissionsBoot as Mr}from"../core/permissions/boot.js";import{startRunNotices as Gr}from"./run-notices.js";import{startGrantEffects as Or}from"./reconcilers/grant-effects.js";import{createGrantSync as Fr,GRANT_SYNC_INTERVAL_S as Br}from"../core/grants/sync.js";import{sweepSandboxes as jr}from"../core/grants/on-sync.js";import{rebuildCronExecutions as zr}from"./cron/run-socket.js";import{hasSyncedGrants as Hr}from"../core/grants/mirror.js";import{readConfig as se}from"../core/config.js";import{reconcileUndeliveredTickets as qr}from"./human/sweep.js";import{resetHandedOffDeliveries as Ur}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as Wr}from"../core/canvas/daemon-actions.js";import{sweepReviews as Jr}from"./review/sweep.js";import{pidfilePath as ce}from"./pidfile.js";import{rendererWarning as Kr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as Vr}from"./supervise-cadence.js";import{createResumeDetector as Qr}from"./resume-detector.js";import{setFatalFaultAnnouncer as Yr}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as Xr}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as Zr}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as et}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as rt}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as tt}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as nt}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as ot}from"./reconcilers/action-lane.js";import{endLiveExecution as it,migrateLegacyExecutionIdentities as at}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as st}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as ct}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as lt}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as dt}from"./reconcilers/storage-due-work.js";import{LedgerSandboxReconciler as pt}from"./reconcilers/ledger-sandbox.js";import{LedgerStorageReconciler as mt}from"./reconcilers/ledger-storage.js";import{LedgerR2Reconciler as ft}from"./reconcilers/ledger-r2.js";import{BashDeadlineReconciler as ut}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as wt}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as ht}from"./reconcilers/structured-request-lane.js";import{RecipeImageReconciler as vt,subscribeImageBuilt as gt}from"./reconcilers/recipe-image.js";import{reconcileStructuredRequestSidecars as St}from"./structured-requests/boot.js";import{captureLivenessSnapshot as De,capturePidCommand as yt,captureTeardownSnapshot as Ce,isPidAlive as bt,isAnyPidAlive as Le,killProcessTreePids as $e}from"../core/canvas/pid.js";import{bindFleet as _t,boundFleet as kt}from"../core/runtime/fleet.js";import{bindDaemonControl as Rt,clearDaemonControl as Pt}from"./control.js";import{execDaemon as Et,spawnDaemon as It}from"./manage.js";import{reconcileAndMigrateBoot as xt}from"../core/canvas/boot.js";import{resolveInstallId as Tt}from"../core/canvas/install-id.js";import{setInstallId as Dt}from"../core/runtime/nodes.js";import{claimDaemonOwnership as Ct,daemonOwnershipApplies as Lt,daemonOwnershipClaimed as $t,releaseDaemonOwnership as Ae}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as At,closeAllDbs as le,openDb as Ne,withCanvasWrite as Nt}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as Mt}from"../core/canvas/import-sqlite.js";import{ensurePostgres as Gt,ensurePostgresHealthy as Ot,stopPostgres as Ft,withPostgresStartLock as Bt}from"./postgres/index.js";import{brokerSignalBus as F}from"./broker-signals/bus.js";import{brokerSignalStreams as jt}from"./api/handlers/broker-signals.js";import{setRequestsStartRunObservers as zt}from"./api/handlers/broker-ops.js";import{reconcilePackages as Ht}from"../core/graph/package-reconcile.js";import{startRepoSync as qt}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as Ut,reapStaleSpares as Wt}from"../core/runtime/warm-pool.js";import{DaemonFleet as Jt}from"./fleet.js";import{copyNodeFilesToRows as Kt}from"../core/runtime/first-boot-rows.js";import{BOOT_MIGRATIONS as Vt}from"./boot-migrations.js";import{emitEvent as u}from"../core/events/emit.js";import{operationIdContext as b}from"../core/events/operation-id.js";import{bindDaemonEventSource as Qt}from"../core/events/source.js";import{activateCanvasImport as Yt,MigrationBlockedError as Xt,runForcedCanvasImport as Zt}from"../migrations/activation.js";import{adoptInstallPerson as en,convertLegacyScopedStore as rn,personStoresCopied as tn}from"../core/scoped-state/migrate.js";import{readLegacyUserSnapshot as nn}from"../core/scoped-state/legacy-profiles.js";import{heldPersons as Me,installPreludeSnapshot as on}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 an=5e3,Fe=150;async function sn(e){const l=De();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=mr(),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=yt(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=Ce(n.pid,null);if(!y.reused){try{process.platform==="linux"&&_(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()+an;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"&&_(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=De();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(sn,"reapPriorEpochBrokers");const cn=5e3;async function ln(e){const l=Array.from(e.entries());if(l.length===0)return;for(const[a]of l){const w=(await br(a))?.status;(w==="active"||w==="idle")&&await xe(a,new Date().toISOString())}const f=l.map(([,a])=>({pid:a.pid,snap:Ce(a.pid,null),exited:a.handle.exited}));for(const a of f)if(!a.snap.reused)try{process.platform==="linux"&&_(x)?await L({operation:"stop",pid:a.pid}):process.kill(a.pid,"SIGTERM")}catch{}const m=new Promise(a=>{setTimeout(a,cn).unref()});await Promise.allSettled(f.map(a=>Promise.race([a.exited.then(()=>{}),m])));const v=[];for(const a of f)bt(a.pid)&&(process.platform==="linux"&&_(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 it(a,{reason:"daemon-teardown"});u({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:f.length,sigkilled:v.length}})}i(ln,"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 dn={acceptsDetachedWork:i(()=>!0,"acceptsDetachedWork"),registerDetached:i(e=>{e.catch(()=>{})},"registerDetached")},K=new Xr;Yr(e=>K.handleFatalFault(e,!1));const pn=new Zr,mn=new et,fn=new rt,un=new tt,wn=new nt,hn=new ot,vn=new ct,gn=new lt,Sn=new dt,yn=new pt,bn=new mt,_n=new ft,kn=new ut,Rn=new wt,Pn=new ht,En=new vt;async function In(e=Date.now(),l=dn){const f=kt(),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 _r()).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 pn.run(e,{rows:v});const w=await mn.run(e,{rows:[...v,...a],fleet:f});await fn.run(e),await Pn.run(e),await En.run(e,{lifecycle:l}),await un.run(e,{lifecycle:l}),await wn.run(e,{lifecycle:l}),await hn.run(e,{lifecycle:l}),await vn.run(e,{lifecycle:l}),await gn.run(e,{lifecycle:l,fleet:f}),Sn.run(e,{lifecycle:l}),yn.run(e,{lifecycle:l}),bn.run(e,{lifecycle:l}),_n.run(e,{lifecycle:l}),await pr(),Rn.run(e,{fleet:f,lifecycle:l}),await kn.run(e,{rows:v}),K.warnIfSaturated({fleet:f,lifecycle:l,thresholds:m,frozen:w})}i(In,"superviseTick");function V(e){return e!=="api"}i(V,"roleRunsLanes");function $(e){return e!=="reconcile"}i($,"roleServesRequests");const xn=66e4;function Tn(e){const l=new Rr().registerAll(Pr(()=>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();_(m)&&be(m);const v=J(f),a=new Promise((n,d)=>{v.once("error",d),v.listen(m,()=>{v.off("error",d),_e(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{_(m)&&be(m)}catch{}}}}}i(Tn,"createProbeServer");const Dn="daemon is draining for shutdown and admits no new work";async function Cn(e){ye(Ee(),{recursive:!0,mode:493}),process.platform==="linux"&&_e(Ee(),493);const l=Pe();if(_(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}i(Cn,"serveLayoutRefusal");async function Ln(e={}){if(gr())return Cn(e);vr();const l=i(r=>{process.stderr.write(`[crtrd] ${r}
3
+ `)},"logPostgres"),f=!!process.env.CRTR_TEST_PG_URL,m=Lt(e.role);zt(V(e.role));const v=i(async()=>m?await Ct(Re(16).toString("hex")):{claimed:!0,claim:{pid:process.pid,pid_identity:"",token:Re(16).toString("hex")}},"claimOwnership"),{database:a,ownership:w}=f?{database:{url:await At()},ownership:await v()}:await Bt(async()=>({database:await Gt({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 p=null,c=null,n=null,d,y="";try{m&&await an(g.token);for(const t of Sr())process.stderr.write(`[crtrd] ${t}
6
- `);for(const t of await gr())process.stderr.write(`[crtrd] ${t}
7
- `);await Ne(),await Nt(a.url,l);const r=e.role===void 0&&!await rn();let o=null;r&&(en({kind:"user",root:Oe()}),o=tn(Oe())),e.role===void 0&&nn(o??{profiles:[],defaults:{},settings:{},state:{}}),await F.listen(a.url);const h=i(t=>{Ge(t).catch(O=>u({level:"error",event:"daemon.settings.reload_failed",error:O}))},"reloadPerson");d=await F.subscribe(t=>{Me().includes(t)&&h(t)},{kinds:["settings"],onListenStart:i(()=>{for(const t of Me())h(t)},"onListenStart")}),e.role!==void 0&&Dr();const s=await At(async t=>(await Ar(t),await zt(t)));for(const t of s.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${t.message}
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 p=null,c=null,n=null,d,y="";try{m&&await sn(g.token);for(const t of yr())process.stderr.write(`[crtrd] ${t}
6
+ `);for(const t of await Sr())process.stderr.write(`[crtrd] ${t}
7
+ `);await Ne(),await Mt(a.url,l);const r=e.role===void 0&&!await tn();let o=null;r&&(rn({kind:"user",root:Oe()}),o=nn(Oe())),e.role===void 0&&on(o??{profiles:[],defaults:{},settings:{},state:{}}),await F.listen(a.url);const h=i(t=>{Ge(t).catch(O=>u({level:"error",event:"daemon.settings.reload_failed",error:O}))},"reloadPerson");d=await F.subscribe(t=>{Me().includes(t)&&h(t)},{kinds:["settings"],onListenStart:i(()=>{for(const t of Me())h(t)},"onListenStart")}),e.role!==void 0&&Cr();const s=await Nt(async t=>(await Nr(t),await Ht(t)));for(const t of s.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${t.message}
8
8
  `);for(const t of s.invalid)process.stderr.write(`[crtrd] package file is not a document: ${t.path}: ${t.error}
9
9
  `);for(const t of s.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
10
  `);for(const t of s.kinds.refused)process.stderr.write(`[crtrd] plugin "${t.plugin}" kind ${t.kind} not registered: ${t.message}
11
11
  `);for(const t of s.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 xt(),Tt(y),e.role===void 0&&(p=await Cr(),p!==null)){if($r(p.id),await Zt(p.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 Lr(),c.joinedFrom!==null&&await Ge(c.person),n=await Nr()}if(process.platform==="linux"&&_(x)&&(wr(),await L({operation:"users.sync",users:await mr()})),m){await at(g.token);const t=await Jt();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(m){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 k=!0,Q,B=Promise.resolve(),Y=!1,j,X,Z,T;const z=new Set;let ee=!1,me=!1,P,D,re,H=!1,q,A,N,M,te,ne,oe;const He=Vr();let E,S={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},C="initial startup activation",qe=null;function Ue(){return S.phase==="ready"?null:{phase:S.phase,message:S.message,blockerPaths:S.blockerPaths}}i(Ue,"startupBlock");function R(r){z.add(r),r.finally(()=>{z.delete(r)}).catch(()=>{})}i(R,"registerDetached");const fe={acceptsDetachedWork:i(()=>k,"acceptsDetachedWork"),registerDetached:R};V(e.role)&&await Ht(fe,Ne);async function We(){try{k=!1,Bt.close(),X?.(),X=void 0,Z?.(),d?.(),Z=void 0,ne?.(),ne=void 0,await F.close(),j!==void 0&&(clearInterval(j),j=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(;z.size>0;)await Promise.allSettled([...z]);if(T!==void 0&&(m&&await cn(T),await T.exitsSettled(),T.dispose()),re!==void 0&&await re.close(),D!==void 0&&!H&&(H=!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(q!==void 0){const o=q;q=void 0,await o.close()}if(ee||(ze(process.pid),ee=!0),ee&&!me){if(m){const{released:o}=await Ae(g);if(!o)throw new Error("daemon ownership claim was not released")}me=!0,await le()}}finally{Rt(),process.off("SIGINT",he),process.off("SIGTERM",ve),process.off("exit",ge)}}i(We,"cleanupAttempt");const U=1e3;let ue=!1;function Je(){return ue?{graceMs:U}:(ue=!0,b.fresh(()=>{u({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:U}})}),setTimeout(()=>{Ke()},U),{graceMs:U})}i(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:
12
+ `);if(y=await Tt(),Dt(y),e.role===void 0&&(p=await Lr(),p!==null)){if(Ar(p.id),await en(p.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 $r(),c.joinedFrom!==null&&await Ge(c.person),await rr(),n=await Mr()}if(process.platform==="linux"&&_(x)&&(hr(),await L({operation:"users.sync",users:await fr()})),m){await st(g.token);const t=await Kt();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(m){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 k=!0,Q,B=Promise.resolve(),Y=!1,j,X,Z,T;const z=new Set;let ee=!1,me=!1,P,D,re,H=!1,q,A,N,M,te,ne,oe;const He=Qr();let E,S={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},C="initial startup activation",qe=null;function Ue(){return S.phase==="ready"?null:{phase:S.phase,message:S.message,blockerPaths:S.blockerPaths}}i(Ue,"startupBlock");function R(r){z.add(r),r.finally(()=>{z.delete(r)}).catch(()=>{})}i(R,"registerDetached");const fe={acceptsDetachedWork:i(()=>k,"acceptsDetachedWork"),registerDetached:R};V(e.role)&&await qt(fe,Ne);async function We(){try{k=!1,jt.close(),X?.(),X=void 0,Z?.(),d?.(),Z=void 0,ne?.(),ne=void 0,await F.close(),j!==void 0&&(clearInterval(j),j=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(;z.size>0;)await Promise.allSettled([...z]);if(T!==void 0&&(m&&await ln(T),await T.exitsSettled(),T.dispose()),re!==void 0&&await re.close(),D!==void 0&&!H&&(H=!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(q!==void 0){const o=q;q=void 0,await o.close()}if(ee||(ze(process.pid),ee=!0),ee&&!me){if(m){const{released:o}=await Ae(g);if(!o)throw new Error("daemon ownership claim was not released")}me=!0,await le()}}finally{Pt(),process.off("SIGINT",he),process.off("SIGTERM",ve),process.off("exit",ge)}}i(We,"cleanupAttempt");const U=1e3;let ue=!1;function Je(){return ue?{graceMs:U}:(ue=!0,b.fresh(()=>{u({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:U}})}),setTimeout(()=>{Ke()},U),{graceMs:U})}i(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"&&_(x)&&(await L({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await Pt();const r=await Et({waitForJoinLock:!0});b.fresh(()=>{u({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:r.pid??r.existing_pid??null,spawned:r.started}})})}catch(r){b.fresh(()=>{u({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)}i(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}i(I,"requestCleanup");async function Ve(){S={phase:"initializing",message:Tn,blockerPaths:[]},D!==void 0&&!H&&(H=!0,await D.close({graceMs:In}))}i(Ve,"drainApi");async function we(r){try{r==="SIGTERM"&&e.role==="api"&&await Ve(),await I(),await Ot({claimed:i(async()=>{try{return m&&await Lt()}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:
15
+ `),process.exit(1)}try{process.platform==="linux"&&_(x)&&(await L({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await Et();const r=await It({waitForJoinLock:!0});b.fresh(()=>{u({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:r.pid??r.existing_pid??null,spawned:r.started}})})}catch(r){b.fresh(()=>{u({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)}i(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}i(I,"requestCleanup");async function Ve(){S={phase:"initializing",message:Dn,blockerPaths:[]},D!==void 0&&!H&&(H=!0,await D.close({graceMs:xn}))}i(Ve,"drainApi");async function we(r){try{r==="SIGTERM"&&e.role==="api"&&await Ve(),await I(),await Ft({claimed:i(async()=>{try{return m&&await $t()}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)}}i(we,"handleSignal");function he(){we("SIGINT")}i(he,"onSigint");function ve(){we("SIGTERM")}i(ve,"onSigterm");function ge(){I().catch(()=>{})}i(ge,"onExit");async function ie(r){k&&(B=B.then(r).then(()=>{},o=>{b.fresh(()=>{u({level:"error",event:"daemon.lane_job.failed",error:o})})}))}i(ie,"enqueue");async function W(){!k||Y||(He.check()&&E&&(u({level:"info",event:"daemon.resume.detected"}),R(E().catch(()=>{}))),Y=!0,await ie(async()=>{if(Y=!1,!!k)try{f||await Gt({log:l}),await En(Date.now(),fe)}catch(r){b.fresh(()=>{u({level:"error",event:"daemon.tick.failed",error:r})})}}))}i(W,"enqueueTick");let G=null;async function Qe(){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)}`}}}i(Qe,"admitStartup");async function Ye(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 Xt({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 s=h instanceof Error?h.message:String(h);return setTimeout(()=>{I().catch(()=>{})},1e3),{result:o,resumed:!1,resumeError:s}}}finally{C=null}}i(Ye,"repairMigrations");async function Xe(){Q=e.intervalMs??Kr;const r=g.token;T=new Wt({epoch:r,enqueue:ie}),bt(T),S={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},b.fresh(()=>{u({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:Q}})});const o=y;b.fresh(()=>{u({level:"info",event:"daemon.install_id.resolved",fields:{install_id:o}})});const h=await xr(o);if(b.fresh(()=>{u({level:"info",event:"daemon.environment.registered",fields:{environment_id:h.environmentId,created:h.created}})}),b.fresh(()=>{let s;try{s=Jr()}catch{return}s!==null&&u({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:s.slice(0,2048)}})}),e.role!==void 0&&Tr(),!V(e.role)){S={phase:"ready",message:"",blockerPaths:[]};return}if(await b.fresh(async()=>{await Wr(),await Hr(),await qr(Date.now()),await Ur(Date.now())}),await b.fresh(async()=>{const{reconcile:s,migrated:t}=await It(it);s.changed&&u({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:s.cleared.length,same_boot_proven:s.sameBootProven}}),t.length>0&&u({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:t.length}})}),await b.fresh(async()=>{for(const s of await Ie())s.status!=="active"||s.pi_pid!=null||s.frozen_at!=null||s.respawn_not_before!=null||(await xe(s.node_id,new Date().toISOString()),u({level:"warn",event:"daemon.startup.launch_debt_recorded",node_id:s.node_id}))}),await b.fresh(async()=>{await gt();for(const s of Kt){const t=s.apply();t.changed&&u({level:"info",event:"daemon.boot_migration.applied",fields:{migration:s.id,...t.summary}})}}),R(jr()),E){const s=i(()=>{R(E().catch(()=>{})),R(tr().then(t=>M?.observe(t)).then(()=>{},t=>{u({level:"warn",event:"daemon.peer.status_failed",error:t})}))},"refresh");R(Br().then(({completion:t})=>t).catch(t=>{u({level:"error",event:"daemon.grants.sweep_failed",error:t})})),s(),N=setInterval(s,Fr*1e3),N.unref(),oe=rr({joined:!0,register:R})}await ie(async()=>{await Ut(),await qt()}),j=setInterval(()=>{W().catch(s=>u({level:"error",event:"daemon.tick.failed",error:s}))},Q),X=await vt(()=>{W().catch(s=>u({level:"error",event:"daemon.tick.failed",error:s}))}),Z=await F.subscribe(()=>{W().catch(s=>u({level:"error",event:"daemon.tick.failed",error:s}))},{kinds:["spend_limits"]}),await W()}i(Xe,"completeStartup");function ae(){return qe??=Xe().catch(r=>{throw S={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},r})}i(ae,"resumeStartup");try{process.on("SIGINT",he),process.on("SIGTERM",ve),process.on("exit",ge),Vt(),p!==null&&u({level:"info",event:"daemon.person.resolved",fields:{person_id:p.id,source:p.source}}),n!==null&&u({level:"info",event:"daemon.permissions.boot",fields:{...n}}),c!==null&&u({level:"info",event:"daemon.person.retired",fields:{person_id:c.person,joined_from:c.joinedFrom,retired:c.retired,columns:c.columns}}),$(e.role)&&(q=await lr());const r=g.token;kt({epoch:r,requestHandover:Je,registerDetached:R,startupBlock:Ue,admitStartup:Qe,repairMigrations:Ye}),Be(),D=$(e.role)?_r({tcp:e.tcp}):xn(e),await D.ready;const o=cr()??se("user").document_port;if($(e.role)){const s=Pr(o,Er());try{await s.ready,re=s}catch(t){u({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=Or(h.issuer,R),h.unsandboxed===!0&&(M=nr({syncGrants:E}))),$(e.role)&&(te=await Mr()),ne=await Gr({serves:$(e.role),runsLanes:V(e.role)}),h?.unsandboxed===!0&&h.listener!==void 0&&u({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 Ir(h,async()=>await zr(h.issuer.replace(/\/$/,"")),E),await A.ready),e.role===void 0)try{await Qt()}catch(s){if(C=null,s instanceof Yt)return S={phase:"blocked",message:s.message,blockerPaths:s.blockerPaths},I;throw s}process.platform==="linux"&&_(x)&&(ur(),await fr("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}}i(Cn,"runDaemon");var bi=Cn;export{In as API_DRAIN_GRACE_MS,de as brokerCommandDeclaresCanvasHome,pe as brokerCommandEpoch,bi as default,V as roleRunsLanes,$ as roleServesRequests,Cn as runDaemon,En as superviseTick,cn as teardownFleet};
18
+ `),process.exit(1)}}i(we,"handleSignal");function he(){we("SIGINT")}i(he,"onSigint");function ve(){we("SIGTERM")}i(ve,"onSigterm");function ge(){I().catch(()=>{})}i(ge,"onExit");async function ie(r){k&&(B=B.then(r).then(()=>{},o=>{b.fresh(()=>{u({level:"error",event:"daemon.lane_job.failed",error:o})})}))}i(ie,"enqueue");async function W(){!k||Y||(He.check()&&E&&(u({level:"info",event:"daemon.resume.detected"}),R(E().catch(()=>{}))),Y=!0,await ie(async()=>{if(Y=!1,!!k)try{f||await Ot({log:l}),await In(Date.now(),fe)}catch(r){b.fresh(()=>{u({level:"error",event:"daemon.tick.failed",error:r})})}}))}i(W,"enqueueTick");let G=null;async function Qe(){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)}`}}}i(Qe,"admitStartup");async function Ye(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 Zt({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 s=h instanceof Error?h.message:String(h);return setTimeout(()=>{I().catch(()=>{})},1e3),{result:o,resumed:!1,resumeError:s}}}finally{C=null}}i(Ye,"repairMigrations");async function Xe(){Q=e.intervalMs??Vr;const r=g.token;T=new Jt({epoch:r,enqueue:ie}),_t(T),S={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},b.fresh(()=>{u({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:Q}})});const o=y;b.fresh(()=>{u({level:"info",event:"daemon.install_id.resolved",fields:{install_id:o}})});const h=await Tr(o);if(b.fresh(()=>{u({level:"info",event:"daemon.environment.registered",fields:{environment_id:h.environmentId,created:h.created}})}),b.fresh(()=>{let s;try{s=Kr()}catch{return}s!==null&&u({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:s.slice(0,2048)}})}),e.role!==void 0&&Dr(),!V(e.role)){S={phase:"ready",message:"",blockerPaths:[]};return}if(await b.fresh(async()=>{await Jr(),await qr(),await Ur(Date.now()),await Wr(Date.now())}),await b.fresh(async()=>{const{reconcile:s,migrated:t}=await xt(at);s.changed&&u({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:s.cleared.length,same_boot_proven:s.sameBootProven}}),t.length>0&&u({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:t.length}})}),await b.fresh(async()=>{for(const s of await Ie())s.status!=="active"||s.pi_pid!=null||s.frozen_at!=null||s.respawn_not_before!=null||(await xe(s.node_id,new Date().toISOString()),u({level:"warn",event:"daemon.startup.launch_debt_recorded",node_id:s.node_id}))}),await b.fresh(async()=>{await St();for(const s of Vt){const t=s.apply();t.changed&&u({level:"info",event:"daemon.boot_migration.applied",fields:{migration:s.id,...t.summary}})}}),R(zr()),E){const s=i(()=>{R(E().catch(()=>{})),R(nr().then(t=>M?.observe(t)).then(()=>{},t=>{u({level:"warn",event:"daemon.peer.status_failed",error:t})}))},"refresh");R(jr().then(({completion:t})=>t).catch(t=>{u({level:"error",event:"daemon.grants.sweep_failed",error:t})})),s(),N=setInterval(s,Br*1e3),N.unref(),oe=tr({joined:!0,register:R})}await ie(async()=>{await Wt(),await Ut()}),j=setInterval(()=>{W().catch(s=>u({level:"error",event:"daemon.tick.failed",error:s}))},Q),X=await gt(()=>{W().catch(s=>u({level:"error",event:"daemon.tick.failed",error:s}))}),Z=await F.subscribe(()=>{W().catch(s=>u({level:"error",event:"daemon.tick.failed",error:s}))},{kinds:["spend_limits"]}),await W()}i(Xe,"completeStartup");function ae(){return qe??=Xe().catch(r=>{throw S={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},r})}i(ae,"resumeStartup");try{process.on("SIGINT",he),process.on("SIGTERM",ve),process.on("exit",ge),Qt(),p!==null&&u({level:"info",event:"daemon.person.resolved",fields:{person_id:p.id,source:p.source}}),n!==null&&u({level:"info",event:"daemon.permissions.boot",fields:{...n}}),c!==null&&u({level:"info",event:"daemon.person.retired",fields:{person_id:c.person,joined_from:c.joinedFrom,retired:c.retired,columns:c.columns}}),$(e.role)&&(q=await dr());const r=g.token;Rt({epoch:r,requestHandover:Je,registerDetached:R,startupBlock:Ue,admitStartup:Qe,repairMigrations:Ye}),Be(),D=$(e.role)?kr({tcp:e.tcp}):Tn(e),await D.ready;const o=lr()??se("user").document_port;if($(e.role)){const s=Er(o,Ir());try{await s.ready,re=s}catch(t){u({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=Fr(h.issuer,R),h.unsandboxed===!0&&(M=or({syncGrants:E}))),$(e.role)&&(te=await Gr()),ne=await Or({serves:$(e.role),runsLanes:V(e.role)}),h?.unsandboxed===!0&&h.listener!==void 0&&u({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 xr(h,async()=>await Hr(h.issuer.replace(/\/$/,"")),E),await A.ready),e.role===void 0)try{await Yt()}catch(s){if(C=null,s instanceof Xt)return S={phase:"blocked",message:s.message,blockerPaths:s.blockerPaths},I;throw s}process.platform==="linux"&&_(x)&&(wr(),await ur("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}}i(Ln,"runDaemon");var ki=Ln;export{xn as API_DRAIN_GRACE_MS,de as brokerCommandDeclaresCanvasHome,pe as brokerCommandEpoch,ki as default,V as roleRunsLanes,$ as roleServesRequests,Ln as runDaemon,In as superviseTick,ln as teardownFleet};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.562",
3
+ "version": "0.3.564",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -144,7 +144,7 @@
144
144
  },
145
145
  "license": "UNLICENSED",
146
146
  "dependencies": {
147
- "@north-light/identity": "0.3.562",
147
+ "@north-light/identity": "0.3.564",
148
148
  "@earendil-works/pi-agent-core": "1.1.0",
149
149
  "@earendil-works/pi-ai": "1.1.0",
150
150
  "@earendil-works/pi-coding-agent": "1.1.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/identity",
3
- "version": "0.3.562",
3
+ "version": "0.3.564",
4
4
  "description": "Northlight access-token verification and scope rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/runtime.lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.562",
3
+ "version": "0.3.564",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/cli",
9
- "version": "0.3.562",
9
+ "version": "0.3.564",
10
10
  "hasInstallScript": true,
11
11
  "license": "UNLICENSED",
12
12
  "workspaces": [
@@ -24,7 +24,7 @@
24
24
  "@milkdown/preset-gfm": "^7.22.3",
25
25
  "@milkdown/prose": "^7.22.3",
26
26
  "@milkdown/utils": "^7.22.3",
27
- "@north-light/identity": "0.3.562",
27
+ "@north-light/identity": "0.3.564",
28
28
  "cron-parser": "^5.6.0",
29
29
  "esbuild": "^0.27.7",
30
30
  "jose": "^6.2.12",
@@ -12455,17 +12455,17 @@
12455
12455
  },
12456
12456
  "packages/crouter-api": {
12457
12457
  "name": "@north-light/api",
12458
- "version": "0.3.562",
12458
+ "version": "0.3.564",
12459
12459
  "license": "UNLICENSED"
12460
12460
  },
12461
12461
  "packages/crouter-env-docker": {
12462
12462
  "name": "@north-light/env-docker",
12463
- "version": "0.3.562",
12463
+ "version": "0.3.564",
12464
12464
  "license": "UNLICENSED"
12465
12465
  },
12466
12466
  "packages/crouter-identity": {
12467
12467
  "name": "@north-light/identity",
12468
- "version": "0.3.562",
12468
+ "version": "0.3.564",
12469
12469
  "license": "UNLICENSED",
12470
12470
  "dependencies": {
12471
12471
  "jose": "^6.2.1"
@@ -12473,21 +12473,21 @@
12473
12473
  },
12474
12474
  "packages/crouter-plugin": {
12475
12475
  "name": "@north-light/plugin",
12476
- "version": "0.3.562",
12476
+ "version": "0.3.564",
12477
12477
  "license": "UNLICENSED",
12478
12478
  "dependencies": {
12479
- "@north-light/api": "0.3.562",
12480
- "@north-light/identity": "0.3.562"
12479
+ "@north-light/api": "0.3.564",
12480
+ "@north-light/identity": "0.3.564"
12481
12481
  }
12482
12482
  },
12483
12483
  "packages/crouter-sdk": {
12484
12484
  "name": "@north-light/sdk",
12485
- "version": "0.3.562",
12485
+ "version": "0.3.564",
12486
12486
  "license": "UNLICENSED",
12487
12487
  "dependencies": {
12488
- "@north-light/api": "0.3.562",
12489
- "@north-light/identity": "0.3.562",
12490
- "@north-light/plugin": "0.3.562",
12488
+ "@north-light/api": "0.3.564",
12489
+ "@north-light/identity": "0.3.564",
12490
+ "@north-light/plugin": "0.3.564",
12491
12491
  "jose": "^6.2.1"
12492
12492
  },
12493
12493
  "bin": {