@north-light/cli 0.3.442 → 0.3.445
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +6 -0
- package/dist/clients/attach/viewer.js +570 -570
- package/dist/core/audit.d.ts +23 -0
- package/dist/core/audit.js +3 -0
- package/dist/core/canvas/migrations.js +12 -12
- package/dist/core/directory/join.d.ts +2 -0
- package/dist/core/directory/join.js +1 -1
- package/dist/core/grants/mirror.d.ts +11 -0
- package/dist/core/grants/mirror.js +2 -2
- package/dist/core/grants/on-sync.js +1 -1
- package/dist/core/grants/remove.js +1 -1
- package/dist/core/grants/sync.js +1 -1
- package/dist/core/graph/reader.js +3 -3
- package/dist/core/peer/client.d.ts +57 -0
- package/dist/core/peer/client.js +1 -0
- package/dist/core/peer/directory.d.ts +65 -0
- package/dist/core/peer/directory.js +1 -0
- package/dist/core/peer/envelope.d.ts +40 -0
- package/dist/core/peer/envelope.js +1 -0
- package/dist/core/peer/events.d.ts +9 -0
- package/dist/core/peer/events.js +1 -0
- package/dist/core/peer/token.d.ts +15 -0
- package/dist/core/peer/token.js +2 -0
- package/dist/core/peer/version.d.ts +16 -0
- package/dist/core/peer/version.js +1 -0
- package/dist/core/providers/bundles.js +1 -1
- package/dist/daemon/api/app-listener.js +12 -12
- package/dist/daemon/api/handlers/bash.d.ts +1 -1
- package/dist/daemon/api/handlers/bash.js +1 -1
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/files.js +1 -1
- package/dist/daemon/api/handlers/human-requests.d.ts +1 -1
- package/dist/daemon/api/handlers/human-requests.js +1 -1
- package/dist/daemon/api/handlers/messages.js +1 -1
- package/dist/daemon/api/handlers/profiles.js +1 -1
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/dist/daemon/api/operations.d.ts +1 -1
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/principal.d.ts +3 -1
- package/dist/daemon/api/principal.js +1 -1
- package/dist/daemon/api/router.d.ts +29 -1
- package/dist/daemon/api/router.js +1 -1
- package/dist/daemon/api/server.js +1 -1
- package/dist/daemon/crtrd.js +7 -7
- package/dist/daemon/peer/handlers.d.ts +2 -0
- package/dist/daemon/peer/handlers.js +1 -0
- package/dist/daemon/peer/ingress.d.ts +34 -0
- package/dist/daemon/peer/ingress.js +1 -0
- package/docs/sdk/release-notes.md +6 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
package/dist/daemon/crtrd.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
var Se=Object.defineProperty;var s=(t,n)=>Se(t,"name",{value:n,configurable:!0});import{writeFileSync as _e,readFileSync as be,rmSync as ke,mkdirSync as Y,existsSync as k,chmodSync as Ee}from"node:fs";import{createServer as X}from"node:http";import{dirname as Re}from"node:path";import{envCrtrdTcp as Ie}from"../shared/env.js";import{serveCanvasConversationStore as xe}from"../core/canvas/sessions.js";import{pollRunObservers as Pe}from"./runs/observer.js";import{randomBytes as Te}from"node:crypto";import{apiSocketPath as De,crtrHome as Ce}from"../core/canvas/paths.js";import{activeAppUsers as Le}from"../core/spaces/users.js";import{ensureAppSpace as $e,ensureUserFiles as Ae}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as R}from"../launcher/spec.js";import{launcherRequest as P}from"../launcher/client.js";import{installLauncherEventSink as Ne}from"./launcher-events.js";import{initializeEmptyLayout as Me,layoutNeedsMigration as Ge,socketDir as Z}from"../core/layout.js";import{migrateLooseKeys as Oe}from"../core/layout-migrate/loose-keys.js";import{getNode as Fe,listNodesForSupervision as He,setFrozen as Be}from"../core/canvas/index.js";import{createApiServer as qe}from"./api/server.js";import{createAppListener as je}from"./api/app-listener.js";import{installLocalTerminalGrant as ze}from"../core/grants/local.js";import{onRunEventAppended as We}from"../core/runs/events.js";import{runNotices as Ue}from"./run-notices.js";import{createGrantSync as Ke,GRANT_SYNC_INTERVAL_S as Je}from"../core/grants/sync.js";import{runRemovalCascades as Ve}from"../core/grants/remove.js";import{sweepSandboxes as Qe}from"../core/grants/on-sync.js";import{rebuildCronExecutions as Ye}from"./cron/run-socket.js";import{hasSyncedGrants as Xe}from"../core/grants/mirror.js";import{readConfig as Ze}from"../core/config.js";import{reconcileUndeliveredTickets as er}from"./human/sweep.js";import{resetHandedOffDeliveries as rr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as tr}from"../core/canvas/daemon-actions.js";import{sweepReviews as nr}from"./review/sweep.js";import{pidfilePath as G}from"./pidfile.js";import{rendererWarning as or}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as ir}from"./supervise-cadence.js";import{createResumeDetector as sr}from"./resume-detector.js";import{BrokerSupervisionReconciler as ar}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as cr}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as dr}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as lr}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as pr}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as mr}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as fr}from"./reconcilers/action-lane.js";import{endLiveExecution as ur,migrateLegacyExecutionIdentities as hr}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as vr}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as wr}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as gr}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as yr}from"./reconcilers/storage-due-work.js";import{BashDeadlineReconciler as Sr}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as _r}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as br}from"./reconcilers/structured-request-lane.js";import{reconcileStructuredRequestSidecars as kr}from"./structured-requests/boot.js";import{captureLivenessSnapshot as ee,capturePidCommand as Er,captureTeardownSnapshot as re,isPidAlive as Rr,isAnyPidAlive as te,killProcessTreePids as ne}from"../core/canvas/pid.js";import{bindFleet as Ir,boundFleet as xr}from"../core/runtime/fleet.js";import{bindDaemonControl as Pr,clearDaemonControl as Tr}from"./control.js";import{execDaemon as Dr,spawnDaemon as Cr}from"./manage.js";import{reconcileAndMigrateBoot as Lr}from"../core/canvas/boot.js";import{resolveInstallId as $r}from"../core/canvas/install-id.js";import{setInstallId as Ar}from"../core/runtime/nodes.js";import{claimDaemonOwnership as Nr,releaseDaemonOwnership as oe}from"../core/canvas/daemon-owner.js";import{migrate as Mr,openDb as ie,withCanvasWrite as Gr,bindDaemonCanvasOpenHook as Or,clearDaemonCanvasOpenHook as se}from"../core/canvas/db.js";import{brokerSignalBus as O}from"./broker-signals/bus.js";import{brokerSignalStreams as Fr}from"./api/handlers/broker-signals.js";import{reconcilePackageDocuments as Hr}from"../core/graph/package-docs.js";import{startRepoSync as Br}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as qr,reapStaleSpares as jr}from"../core/runtime/warm-pool.js";import{DaemonFleet as zr}from"./fleet.js";import{BOOT_MIGRATIONS as Wr}from"./boot-migrations.js";import{applyJoinedOwner as Ur}from"./joined-owner.js";import{emitEvent as u}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as Kr}from"../core/events/source.js";import{activateCanvasImport as Jr,MigrationBlockedError as Vr,runForcedCanvasImport as Qr}from"../migrations/activation.js";import{convertLegacyProviderStore as Yr,convertLegacyScopedStore as Xr}from"../core/scoped-state/migrate.js";import{userScopeRoot as Zr}from"../core/scope.js";import{secretsDir as et}from"../core/layout.js";function F(t,n){return t.includes(` --canvas-home ${n} `)}s(F,"brokerCommandDeclaresCanvasHome");function H(t){return/ --epoch (\S+) /.exec(t)?.[1]??null}s(H,"brokerCommandEpoch");const rt=5e3,ae=150;async function tt(t){const n=ee();if(n===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 a=Ce(),m=[];for(const[i,p]of n.processes){const g=p.command;if(p.zombie||!g.includes("broker-cli.js")||!F(g,a)||H(g)===t)continue;const S=g.trim().split(/\s+/).at(-1)??"";S===""||S.endsWith("broker-cli.js")||m.push({pid:i,nodeId:S})}let f=0;const o=[];for(const i of m){const p=Er(i.pid);if(p===null||!p.includes("broker-cli.js")||!F(p,a)||H(p)===t||p.trim().split(/\s+/).at(-1)!==i.nodeId)continue;const g=re(i.pid,null);if(!g.reused){try{process.platform==="linux"&&k(R)?await P({operation:"stop",pid:i.pid}):process.kill(i.pid,"SIGTERM")}catch{continue}f++,o.push({pid:i.pid,snap:g})}}const w=Date.now()+rt;for(;Date.now()<w&&o.some(i=>te(i.snap.tree.length>0?i.snap.tree:[i.pid]));)await new Promise(i=>setTimeout(i,ae));let v=0;for(const i of o){const p=i.snap.tree.length>0?i.snap.tree:[i.pid];te(p)&&(process.platform==="linux"&&k(R)?await P({operation:"stop",pid:i.pid}):ne(p,"SIGKILL",i.snap.identities),v++)}v>0&&await new Promise(i=>setTimeout(i,ae));const d=ee();if(d===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 l=[];for(const[i,p]of d.processes){const g=p.command;p.zombie||!g.includes("broker-cli.js")||F(g,a)&&H(g)!==t&&l.push(i)}if(l.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${l.join(", ")}`);u({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:m.length,sigtermed:f,sigkilled:v}})}s(tt,"reapPriorEpochBrokers");const nt=5e3;async function ot(t){const n=Array.from(t.entries());if(n.length===0)return;for(const[o]of n){const w=Fe(o)?.status;(w==="active"||w==="idle")&&Be(o,new Date().toISOString())}const a=n.map(([,o])=>({pid:o.pid,snap:re(o.pid,null),exited:o.handle.exited}));for(const o of a)if(!o.snap.reused)try{process.platform==="linux"&&k(R)?await P({operation:"stop",pid:o.pid}):process.kill(o.pid,"SIGTERM")}catch{}const m=new Promise(o=>{setTimeout(o,nt).unref()});await Promise.allSettled(a.map(o=>Promise.race([o.exited.then(()=>{}),m])));const f=[];for(const o of a)Rr(o.pid)&&(process.platform==="linux"&&k(R)?await P({operation:"stop",pid:o.pid}):ne(o.snap.tree.length>0?o.snap.tree:[o.pid],"SIGKILL",o.snap.identities),f.push(o));await Promise.all(f.map(o=>o.exited));for(const[o]of n)ur(o,{reason:"daemon-teardown"});u({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:a.length,sigkilled:f.length}})}s(ot,"teardownFleet");function ce(){Y(Re(G()),{recursive:!0}),_e(G(),String(process.pid),"utf8")}s(ce,"writePidfile");function de(t){return typeof t=="object"&&t!==null&&t.code==="ENOENT"}s(de,"isMissingPathError");function le(t){const n=G();let a;try{a=be(n,"utf8")}catch(m){if(de(m))return;throw m}if(a.trim()!==String(t))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${t})`);try{ke(n)}catch(m){if(de(m))return;throw m}}s(le,"removeOwnedPidfileMirror");const it={acceptsDetachedWork:s(()=>!0,"acceptsDetachedWork"),registerDetached:s(t=>{t.catch(()=>{})},"registerDetached")},B=new ar,st=new cr,at=new dr,ct=new lr,dt=new pr,lt=new mr,pt=new fr,mt=new wr,ft=new gr,ut=new yr,ht=new Sr,vt=new _r,wt=new br;async function gt(t=Date.now(),n=it){const a=xr(),m=await B.runFleetPreamble(a);let f,o;try{const v=He();f=v.filter(d=>d.status==="active"||d.status==="idle"),o=v.filter(d=>d.status==="done"||d.status==="canceled"||d.status==="dead")}catch(v){u({level:"error",event:"canvas.node_list.failed",error:v});return}await B.run(t,{rows:f,fleet:a,lifecycle:n}),st.run(t,{rows:f});const w=await at.run(t,{rows:[...f,...o],fleet:a});ct.run(t),wt.run(t),dt.run(t,{lifecycle:n}),lt.run(t,{lifecycle:n}),pt.run(t,{lifecycle:n}),mt.run(t,{lifecycle:n}),ft.run(t,{lifecycle:n,fleet:a}),ut.run(t,{lifecycle:n}),Pe(),vt.run(t,{fleet:a,lifecycle:n}),await ht.run(t,{rows:f}),B.warnIfSaturated({fleet:a,lifecycle:n,thresholds:m,frozen:w})}s(gt,"superviseTick");async function yt(t){Y(Z(),{recursive:!0,mode:493}),process.platform==="linux"&&Ee(Z(),493);const n=De();if(k(n))throw new Error(`layout refusal cannot bind occupied socket: ${n}`);const a=X((d,l)=>{if(d.method==="GET"&&d.url==="/healthz"){l.writeHead(200,{"content-type":"application/json; charset=utf-8"}),l.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}l.writeHead(503,{"content-type":"application/json; charset=utf-8"}),l.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((d,l)=>{a.once("error",l),a.listen(n,()=>{a.off("error",l),d()})});let m;const f=t.tcp??Ie();if(f){const d=/^(.*):(\d+)$/.exec(f);if(!d||Number(d[2])>65535)throw new Error(`invalid refusal TCP address: ${f}`);const l=X(a.listeners("request")[0]);await new Promise((i,p)=>{l.once("error",p),l.listen(Number(d[2]),d[1],()=>{l.off("error",p),i()})}),m=l}ce();let o;const w=s(()=>(o??=(async()=>{a.closeAllConnections(),m?.closeAllConnections(),await Promise.all([a,m].filter(d=>d!==void 0).map(d=>new Promise((l,i)=>d.close(p=>p?i(p):l())))),le(process.pid),process.off("SIGINT",v),process.off("SIGTERM",v)})(),o),"cleanup"),v=s(()=>{w().then(()=>process.exit(0),d=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(d)}
|
|
2
|
-
`),process.exit(1)})},"onSignal");return process.on("SIGINT",v),process.on("SIGTERM",v),w}s(
|
|
1
|
+
var Se=Object.defineProperty;var s=(t,n)=>Se(t,"name",{value:n,configurable:!0});import{reportMachineStatus as _e}from"../core/peer/directory.js";import{writeFileSync as be,readFileSync as ke,rmSync as Ee,mkdirSync as Y,existsSync as k,chmodSync as Re}from"node:fs";import{createServer as X}from"node:http";import{dirname as Ie}from"node:path";import{envCrtrdTcp as xe}from"../shared/env.js";import{serveCanvasConversationStore as Pe}from"../core/canvas/sessions.js";import{pollRunObservers as Te}from"./runs/observer.js";import{randomBytes as De}from"node:crypto";import{apiSocketPath as Ce,crtrHome as Le}from"../core/canvas/paths.js";import{activeAppUsers as $e}from"../core/spaces/users.js";import{ensureAppSpace as Ae,ensureUserFiles as Ne}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as R}from"../launcher/spec.js";import{launcherRequest as P}from"../launcher/client.js";import{installLauncherEventSink as Me}from"./launcher-events.js";import{initializeEmptyLayout as Ge,layoutNeedsMigration as Oe,socketDir as Z}from"../core/layout.js";import{migrateLooseKeys as Fe}from"../core/layout-migrate/loose-keys.js";import{getNode as He,listNodesForSupervision as Be,setFrozen as qe}from"../core/canvas/index.js";import{createApiServer as je}from"./api/server.js";import{createAppListener as ze}from"./api/app-listener.js";import{installLocalTerminalGrant as We}from"../core/grants/local.js";import{onRunEventAppended as Ue}from"../core/runs/events.js";import{runNotices as Ke}from"./run-notices.js";import{createGrantSync as Je,GRANT_SYNC_INTERVAL_S as Ve}from"../core/grants/sync.js";import{runRemovalCascades as Qe}from"../core/grants/remove.js";import{sweepSandboxes as Ye}from"../core/grants/on-sync.js";import{rebuildCronExecutions as Xe}from"./cron/run-socket.js";import{hasSyncedGrants as Ze}from"../core/grants/mirror.js";import{readConfig as er}from"../core/config.js";import{reconcileUndeliveredTickets as rr}from"./human/sweep.js";import{resetHandedOffDeliveries as tr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as nr}from"../core/canvas/daemon-actions.js";import{sweepReviews as or}from"./review/sweep.js";import{pidfilePath as G}from"./pidfile.js";import{rendererWarning as ir}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as sr}from"./supervise-cadence.js";import{createResumeDetector as ar}from"./resume-detector.js";import{BrokerSupervisionReconciler as cr}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as dr}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as lr}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as pr}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as mr}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as fr}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as ur}from"./reconcilers/action-lane.js";import{endLiveExecution as hr,migrateLegacyExecutionIdentities as vr}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as wr}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as gr}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as yr}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as Sr}from"./reconcilers/storage-due-work.js";import{BashDeadlineReconciler as _r}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as br}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as kr}from"./reconcilers/structured-request-lane.js";import{reconcileStructuredRequestSidecars as Er}from"./structured-requests/boot.js";import{captureLivenessSnapshot as ee,capturePidCommand as Rr,captureTeardownSnapshot as re,isPidAlive as Ir,isAnyPidAlive as te,killProcessTreePids as ne}from"../core/canvas/pid.js";import{bindFleet as xr,boundFleet as Pr}from"../core/runtime/fleet.js";import{bindDaemonControl as Tr,clearDaemonControl as Dr}from"./control.js";import{execDaemon as Cr,spawnDaemon as Lr}from"./manage.js";import{reconcileAndMigrateBoot as $r}from"../core/canvas/boot.js";import{resolveInstallId as Ar}from"../core/canvas/install-id.js";import{setInstallId as Nr}from"../core/runtime/nodes.js";import{claimDaemonOwnership as Mr,releaseDaemonOwnership as oe}from"../core/canvas/daemon-owner.js";import{migrate as Gr,openDb as ie,withCanvasWrite as Or,bindDaemonCanvasOpenHook as Fr,clearDaemonCanvasOpenHook as se}from"../core/canvas/db.js";import{brokerSignalBus as O}from"./broker-signals/bus.js";import{brokerSignalStreams as Hr}from"./api/handlers/broker-signals.js";import{reconcilePackageDocuments as Br}from"../core/graph/package-docs.js";import{startRepoSync as qr}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as jr,reapStaleSpares as zr}from"../core/runtime/warm-pool.js";import{DaemonFleet as Wr}from"./fleet.js";import{BOOT_MIGRATIONS as Ur}from"./boot-migrations.js";import{applyJoinedOwner as Kr}from"./joined-owner.js";import{emitEvent as u}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as Jr}from"../core/events/source.js";import{activateCanvasImport as Vr,MigrationBlockedError as Qr,runForcedCanvasImport as Yr}from"../migrations/activation.js";import{convertLegacyProviderStore as Xr,convertLegacyScopedStore as Zr}from"../core/scoped-state/migrate.js";import{userScopeRoot as et}from"../core/scope.js";import{secretsDir as rt}from"../core/layout.js";function F(t,n){return t.includes(` --canvas-home ${n} `)}s(F,"brokerCommandDeclaresCanvasHome");function H(t){return/ --epoch (\S+) /.exec(t)?.[1]??null}s(H,"brokerCommandEpoch");const tt=5e3,ae=150;async function nt(t){const n=ee();if(n===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 a=Le(),m=[];for(const[i,p]of n.processes){const y=p.command;if(p.zombie||!y.includes("broker-cli.js")||!F(y,a)||H(y)===t)continue;const S=y.trim().split(/\s+/).at(-1)??"";S===""||S.endsWith("broker-cli.js")||m.push({pid:i,nodeId:S})}let f=0;const o=[];for(const i of m){const p=Rr(i.pid);if(p===null||!p.includes("broker-cli.js")||!F(p,a)||H(p)===t||p.trim().split(/\s+/).at(-1)!==i.nodeId)continue;const y=re(i.pid,null);if(!y.reused){try{process.platform==="linux"&&k(R)?await P({operation:"stop",pid:i.pid}):process.kill(i.pid,"SIGTERM")}catch{continue}f++,o.push({pid:i.pid,snap:y})}}const w=Date.now()+tt;for(;Date.now()<w&&o.some(i=>te(i.snap.tree.length>0?i.snap.tree:[i.pid]));)await new Promise(i=>setTimeout(i,ae));let v=0;for(const i of o){const p=i.snap.tree.length>0?i.snap.tree:[i.pid];te(p)&&(process.platform==="linux"&&k(R)?await P({operation:"stop",pid:i.pid}):ne(p,"SIGKILL",i.snap.identities),v++)}v>0&&await new Promise(i=>setTimeout(i,ae));const d=ee();if(d===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 l=[];for(const[i,p]of d.processes){const y=p.command;p.zombie||!y.includes("broker-cli.js")||F(y,a)&&H(y)!==t&&l.push(i)}if(l.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${l.join(", ")}`);u({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:m.length,sigtermed:f,sigkilled:v}})}s(nt,"reapPriorEpochBrokers");const ot=5e3;async function it(t){const n=Array.from(t.entries());if(n.length===0)return;for(const[o]of n){const w=He(o)?.status;(w==="active"||w==="idle")&&qe(o,new Date().toISOString())}const a=n.map(([,o])=>({pid:o.pid,snap:re(o.pid,null),exited:o.handle.exited}));for(const o of a)if(!o.snap.reused)try{process.platform==="linux"&&k(R)?await P({operation:"stop",pid:o.pid}):process.kill(o.pid,"SIGTERM")}catch{}const m=new Promise(o=>{setTimeout(o,ot).unref()});await Promise.allSettled(a.map(o=>Promise.race([o.exited.then(()=>{}),m])));const f=[];for(const o of a)Ir(o.pid)&&(process.platform==="linux"&&k(R)?await P({operation:"stop",pid:o.pid}):ne(o.snap.tree.length>0?o.snap.tree:[o.pid],"SIGKILL",o.snap.identities),f.push(o));await Promise.all(f.map(o=>o.exited));for(const[o]of n)hr(o,{reason:"daemon-teardown"});u({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:a.length,sigkilled:f.length}})}s(it,"teardownFleet");function ce(){Y(Ie(G()),{recursive:!0}),be(G(),String(process.pid),"utf8")}s(ce,"writePidfile");function de(t){return typeof t=="object"&&t!==null&&t.code==="ENOENT"}s(de,"isMissingPathError");function le(t){const n=G();let a;try{a=ke(n,"utf8")}catch(m){if(de(m))return;throw m}if(a.trim()!==String(t))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${t})`);try{Ee(n)}catch(m){if(de(m))return;throw m}}s(le,"removeOwnedPidfileMirror");const st={acceptsDetachedWork:s(()=>!0,"acceptsDetachedWork"),registerDetached:s(t=>{t.catch(()=>{})},"registerDetached")},B=new cr,at=new dr,ct=new lr,dt=new pr,lt=new mr,pt=new fr,mt=new ur,ft=new gr,ut=new yr,ht=new Sr,vt=new _r,wt=new br,gt=new kr;async function yt(t=Date.now(),n=st){const a=Pr(),m=await B.runFleetPreamble(a);let f,o;try{const v=Be();f=v.filter(d=>d.status==="active"||d.status==="idle"),o=v.filter(d=>d.status==="done"||d.status==="canceled"||d.status==="dead")}catch(v){u({level:"error",event:"canvas.node_list.failed",error:v});return}await B.run(t,{rows:f,fleet:a,lifecycle:n}),at.run(t,{rows:f});const w=await ct.run(t,{rows:[...f,...o],fleet:a});dt.run(t),gt.run(t),lt.run(t,{lifecycle:n}),pt.run(t,{lifecycle:n}),mt.run(t,{lifecycle:n}),ft.run(t,{lifecycle:n}),ut.run(t,{lifecycle:n,fleet:a}),ht.run(t,{lifecycle:n}),Te(),wt.run(t,{fleet:a,lifecycle:n}),await vt.run(t,{rows:f}),B.warnIfSaturated({fleet:a,lifecycle:n,thresholds:m,frozen:w})}s(yt,"superviseTick");async function St(t){Y(Z(),{recursive:!0,mode:493}),process.platform==="linux"&&Re(Z(),493);const n=Ce();if(k(n))throw new Error(`layout refusal cannot bind occupied socket: ${n}`);const a=X((d,l)=>{if(d.method==="GET"&&d.url==="/healthz"){l.writeHead(200,{"content-type":"application/json; charset=utf-8"}),l.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}l.writeHead(503,{"content-type":"application/json; charset=utf-8"}),l.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((d,l)=>{a.once("error",l),a.listen(n,()=>{a.off("error",l),d()})});let m;const f=t.tcp??xe();if(f){const d=/^(.*):(\d+)$/.exec(f);if(!d||Number(d[2])>65535)throw new Error(`invalid refusal TCP address: ${f}`);const l=X(a.listeners("request")[0]);await new Promise((i,p)=>{l.once("error",p),l.listen(Number(d[2]),d[1],()=>{l.off("error",p),i()})}),m=l}ce();let o;const w=s(()=>(o??=(async()=>{a.closeAllConnections(),m?.closeAllConnections(),await Promise.all([a,m].filter(d=>d!==void 0).map(d=>new Promise((l,i)=>d.close(p=>p?i(p):l())))),le(process.pid),process.off("SIGINT",v),process.off("SIGTERM",v)})(),o),"cleanup"),v=s(()=>{w().then(()=>process.exit(0),d=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(d)}
|
|
2
|
+
`),process.exit(1)})},"onSignal");return process.on("SIGINT",v),process.on("SIGTERM",v),w}s(St,"serveLayoutRefusal");async function _t(t={}){if(Oe())return St(t);Ge();const n=Mr(De(16).toString("hex"));if(!n.claimed){if(n.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${n.incumbentPid??"?"})
|
|
3
3
|
`);else{const r=[n.incumbentPid!==void 0?`pid ${n.incumbentPid}`:void 0,n.verdict!==void 0?`verdict ${n.verdict}`:void 0].filter(c=>c!==void 0),e=r.length>0?` (${r.join(", ")})`:"";process.stderr.write(`[crtrd] claim lost: ${n.reason}${e}
|
|
4
|
-
`),n.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(n.reason==="own_identity_indeterminate"||n.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const a=n.claim;let m=null;try{if(await
|
|
5
|
-
`);
|
|
4
|
+
`),n.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(n.reason==="own_identity_indeterminate"||n.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const a=n.claim;let m=null;try{if(await nt(a.token),Zr({kind:"user",root:et()}),process.platform==="darwin"){const{readConfig:e,updateConfig:c}=await import("../core/config.js");if(e("user").paths?.user_files===void 0){const{homedir:g}=await import("node:os");c("user",Q=>{Q.paths??={},Q.paths.user_files??=g()})}}await Xr(rt());for(const e of Fe())process.stderr.write(`[crtrd] ${e}
|
|
5
|
+
`);Gr(ie()),Fr(e=>O.install(e));const r=Or(e=>Br(e));for(const e of r.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${e.message}
|
|
6
6
|
`);for(const e of r.invalid)process.stderr.write(`[crtrd] package file is not a document: ${e.path}: ${e.error}
|
|
7
7
|
`);for(const e of r.quarantined)process.stderr.write(`[crtrd] plugin "${e.name}" has a damaged ${e.file} (${e.reason}); ${e.movedTo!==void 0?`moved to ${e.movedTo}`:"left in place and skipped"}
|
|
8
|
-
`);m=
|
|
8
|
+
`);m=Kr(),process.platform==="linux"&&k(R)&&(Me(),await P({operation:"users.sync",users:$e()})),wr(a.token)}catch(r){se(),O.close();try{const{released:e}=oe(a);if(!e)throw new Error("daemon ownership claim was not released")}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}let f=!0,o,w=Promise.resolve(),v=!1,d,l;const i=new Set;let p=!1,y=!1,S,L,q=!1,$,T,D;const pe=ar();let I,h={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},x="initial startup activation",me=null;function fe(){return h.phase==="ready"?null:{phase:h.phase,message:h.message,blockerPaths:h.blockerPaths}}s(fe,"startupBlock");function b(r){i.add(r),r.finally(()=>{i.delete(r)}).catch(()=>{})}s(b,"registerDetached");const j={acceptsDetachedWork:s(()=>f,"acceptsDetachedWork"),registerDetached:b};qr(j,ie);async function ue(){try{f=!1,Hr.close(),se(),O.close(),d!==void 0&&(clearInterval(d),d=void 0),D!==void 0&&(clearInterval(D),D=void 0);let r;do r=w,await r;while(r!==w);for(;i.size>0;)await Promise.allSettled([...i]);if(l!==void 0&&(await it(l),l.dispose()),L!==void 0&&!q&&(q=!0,await L.close()),T!==void 0){const e=T;T=void 0,await e.close()}if($!==void 0){const e=$;$=void 0,await e.close()}if(p||(le(process.pid),p=!0),p&&!y){const{released:e}=oe(a);if(!e)throw new Error("daemon ownership claim was not released");y=!0}}finally{Dr(),process.off("SIGINT",U),process.off("SIGTERM",K),process.off("exit",J)}}s(ue,"cleanupAttempt");const A=1e3;let z=!1;function he(){return z?{graceMs:A}:(z=!0,_.fresh(()=>{u({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:A}})}),setTimeout(()=>{ve()},A),{graceMs:A})}s(he,"requestHandover");async function ve(){try{await E()}catch(r){const e=r instanceof Error?r.stack??r.message:String(r);process.stderr.write(`[crtrd] handover cleanup failed:
|
|
9
9
|
${e}
|
|
10
|
-
`),process.exit(1)}try{process.platform==="linux"&&k(R)&&(await P({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await
|
|
10
|
+
`),process.exit(1)}try{process.platform==="linux"&&k(R)&&(await P({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await Cr();const r=await Lr({waitForJoinLock:!0});_.fresh(()=>{u({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:r.pid??r.existing_pid??null,spawned:r.started}})})}catch(r){_.fresh(()=>{u({level:"error",event:"daemon.handover.successor_failed",error:r})});const e=r instanceof Error?r.message:String(r);process.stderr.write(`[crtrd] handover successor failed to start: ${e}
|
|
11
11
|
`),process.exit(1)}process.exit(0)}s(ve,"performHandover");function E(){if(S!==void 0)return S;const r=ue();return S=r,r.then(()=>{S===r&&(S=void 0)},()=>{S===r&&(S=void 0)}),r}s(E,"requestCleanup");async function W(r){try{await E(),process.exit(0)}catch(e){process.exitCode=1;const c=e instanceof Error?e.stack??`${e.name}: ${e.message}`:String(e);process.stderr.write(`[crtrd] ${r} cleanup failed:
|
|
12
12
|
${c}
|
|
13
|
-
`),process.exit(1)}}s(W,"handleSignal");function U(){W("SIGINT")}s(U,"onSigint");function K(){W("SIGTERM")}s(K,"onSigterm");function J(){E().catch(()=>{})}s(J,"onExit");function N(r){f&&(w=w.then(r).then(()=>{},e=>{_.fresh(()=>{u({level:"error",event:"daemon.lane_job.failed",error:e})})}))}s(N,"enqueue");function V(){!f||v||(pe.check()&&I&&(u({level:"info",event:"daemon.resume.detected"}),b(I().catch(()=>{}))),v=!0,N(async()=>{if(v=!1,!!f)try{await
|
|
13
|
+
`),process.exit(1)}}s(W,"handleSignal");function U(){W("SIGINT")}s(U,"onSigint");function K(){W("SIGTERM")}s(K,"onSigterm");function J(){E().catch(()=>{})}s(J,"onExit");function N(r){f&&(w=w.then(r).then(()=>{},e=>{_.fresh(()=>{u({level:"error",event:"daemon.lane_job.failed",error:e})})}))}s(N,"enqueue");function V(){!f||v||(pe.check()&&I&&(u({level:"info",event:"daemon.resume.detected"}),b(I().catch(()=>{}))),v=!0,N(async()=>{if(v=!1,!!f)try{await yt(Date.now(),j)}catch(r){_.fresh(()=>{u({level:"error",event:"daemon.tick.failed",error:r})})}}))}s(V,"enqueueTick");let C=null;async function we(){if(C!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${C instanceof Error?C.message:String(C)}`};if(h.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(h.phase!=="prepared"&&h.phase!=="recovering")throw{status:409,code:"not_prepared",message:h.message};const r=h.phase==="prepared";try{return await M(),h={phase:"ready",message:"",blockerPaths:[]},r?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(e){throw C=e,{status:500,code:"admission_failed",message:`daemon admission failed: ${e instanceof Error?e.message:String(e)}`}}}s(we,"admitStartup");async function ge(r){if(x!==null)throw{status:409,code:"migration_in_progress",message:`the canvas import is already running (${x})`};x="repair";try{const e=await Yr({dryRun:r.dryRun});if(r.dryRun||e.pending||h.phase!=="blocked")return{result:e,resumed:!1};if(t.prepare)return h={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:e,resumed:!1};try{return await M(),h={phase:"ready",message:"",blockerPaths:[]},{result:e,resumed:!0}}catch(c){const g=c instanceof Error?c.message:String(c);return setTimeout(()=>{E().catch(()=>{})},1e3),{result:e,resumed:!1,resumeError:g}}}finally{x=null}}s(ge,"repairMigrations");async function ye(){o=t.intervalMs??sr;const r=a.token;l=new Wr({epoch:r,enqueue:N}),xr(l),h={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{u({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:o}})});const e=Ar();if(Nr(e),_.fresh(()=>{u({level:"info",event:"daemon.install_id.resolved",fields:{install_id:e}})}),_.fresh(()=>{let c;try{c=ir()}catch{return}c!==null&&u({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:c.slice(0,2048)}})}),await _.fresh(async()=>{await or(),await rr(),tr(Date.now()),nr(Date.now())}),_.fresh(()=>{const{reconcile:c,migrated:g}=$r(vr);c.changed&&u({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:c.cleared.length,same_boot_proven:c.sameBootProven}}),g.length>0&&u({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:g.length}})}),_.fresh(()=>{Er();for(const c of Ur){const g=c.apply();g.changed&&u({level:"info",event:"daemon.boot_migration.applied",fields:{migration:c.id,...g.summary}})}}),b(Xe()),I){const c=s(()=>{b(I().catch(()=>{})),b(_e().then(()=>{},g=>{u({level:"warn",event:"daemon.peer.status_failed",error:g})}))},"refresh");b(Qe().catch(()=>{})),b(Ye().then(({completion:g})=>g).catch(g=>{u({level:"error",event:"daemon.grants.sweep_failed",error:g})})),c(),D=setInterval(c,Ve*1e3),D.unref()}N(()=>{zr(),jr()}),d=setInterval(V,o),V()}s(ye,"completeStartup");function M(){return me??=ye().catch(r=>{throw h={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},r})}s(M,"resumeStartup");try{process.on("SIGINT",U),process.on("SIGTERM",K),process.on("exit",J),Jr(),m!==null&&u({level:"info",event:"daemon.joined_owner.applied",fields:{owner:m.owner,rows:m.rows}}),$=await Pe();const r=a.token;Tr({epoch:r,requestHandover:he,registerDetached:b,startupBlock:fe,admitStartup:we,repairMigrations:ge}),ce(),L=je({tcp:t.tcp}),await L.ready;const e=er("user").runtime;e?.issuer?(I=Je(e.issuer,b),Ue(c=>Ke().appended(c))):We(),e?.unsandboxed===!0&&e.listener!==void 0&&u({level:"warn",event:"daemon.app_listener.refused_unsandboxed",fields:{reason:"runtime.unsandboxed is set; runtime.listener is ignored"}}),e!==void 0&&e.listener!==void 0&&e.unsandboxed!==!0&&(T=ze(e,()=>Ze(e.issuer.replace(/\/$/,"")),I),await T.ready);try{await Vr()}catch(c){if(x=null,c instanceof Qr)return h={phase:"blocked",message:c.message,blockerPaths:c.blockerPaths},E;throw c}process.platform==="linux"&&k(R)&&(Ne(),await Ae("app:terminal"));try{t.prepare?h={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await M(),h={phase:"ready",message:"",blockerPaths:[]})}finally{x=null}return E}catch(r){try{await E()}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}}s(_t,"runDaemon");var Un=_t;export{F as brokerCommandDeclaresCanvasHome,H as brokerCommandEpoch,Un as default,_t as runDaemon,yt as superviseTick,it as teardownFleet};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var n=Object.defineProperty;var o=(t,r)=>n(t,"name",{value:r,configurable:!0});import{ApiError as p}from"../../api/errors.js";const e=o(t=>()=>{throw new p(501,"not_implemented",`${t} is not implemented on this machine yet`)},"notImplemented"),d=[{method:"POST",pattern:"/v1/peer/events",handler:e("peer.events")},{method:"POST",pattern:"/v1/peer/outbox/claim",handler:e("peer.outbox.claim")},{method:"POST",pattern:"/v1/peer/outbox/ack",handler:e("peer.outbox.ack")},{method:"POST",pattern:"/v1/peer/locate",handler:e("peer.locate")}];export{d as peerRoutes};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { type JwksCache, type MachineTokenClaims } from '@north-light/identity';
|
|
3
|
+
import { Router, type RequestPrincipal } from '../api/router.js';
|
|
4
|
+
export interface PeerIngressContext {
|
|
5
|
+
/** The directory the machine tokens come from. */
|
|
6
|
+
issuer: string;
|
|
7
|
+
/** Its key set (the app listener's cache, or the laptop's own). */
|
|
8
|
+
jwks: JwksCache;
|
|
9
|
+
/** The audience machine tokens must carry: `machineUrl()` on the laptop, `runtime.audience` on the hosted runtime. Null before it is known: every request is refused 503. */
|
|
10
|
+
audience(): string | null;
|
|
11
|
+
/** The account owner (`user:<id>`) this runtime belongs to. Null when it has none: every request is refused 401. */
|
|
12
|
+
owner(): string | null;
|
|
13
|
+
/** This machine's id (`runtime:<id>`). */
|
|
14
|
+
self(): string;
|
|
15
|
+
/** The router that serves the request; defaults to the daemon's own (`buildRouter(() => null)`), built on first use. */
|
|
16
|
+
router?: () => Router | Promise<Router>;
|
|
17
|
+
/** False while the JWKS or grants are not loaded: every request is refused 503 (audited). Defaults to ready. */
|
|
18
|
+
ready?: () => boolean;
|
|
19
|
+
}
|
|
20
|
+
/** The principal a verified token plus envelope yields, or the refusal. The checks are the C2 row: the envelope's `act.sub` is the token's `machine_id`, its `sub` the token's `sub`. */
|
|
21
|
+
export declare function peerPrincipal(claims: Pick<MachineTokenClaims, 'sub' | 'machine_id'>, envelopeHeader: string | undefined): {
|
|
22
|
+
principal: RequestPrincipal & {
|
|
23
|
+
class: 'forwarded' | 'machine';
|
|
24
|
+
};
|
|
25
|
+
} | {
|
|
26
|
+
status: number;
|
|
27
|
+
code: string;
|
|
28
|
+
message: string;
|
|
29
|
+
};
|
|
30
|
+
/** The account owner this runtime belongs to: the owner a successful `crtr sys join` recorded (a laptop), else — on a hosted runtime, which is
|
|
31
|
+
* provisioned without join — the one `sub` its mirrored grants all carry. Null when neither names exactly one owner. Never throws. */
|
|
32
|
+
export declare function runtimeOwner(): string | null;
|
|
33
|
+
/** Serve one peer request. Never throws. Every answer writes one `audit_rows` row: a request whose token never verified is recorded with `via` = `unauthenticated` and no identity. */
|
|
34
|
+
export declare function servePeer(req: IncomingMessage, res: ServerResponse, ctx: PeerIngressContext): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var S=Object.defineProperty;var s=(e,n)=>S(e,"name",{value:n,configurable:!0});import{randomUUID as q}from"node:crypto";import{verifyMachineToken as j}from"@north-light/identity";import{decodeEnvelope as A,ENVELOPE_HEADER as C,EnvelopeError as H}from"../../core/peer/envelope.js";import{peerVersionRefusal as I,PEER_VERSION_HEADER as P}from"../../core/peer/version.js";import{auditOutcome as k,writePeerAudit as U}from"../../core/audit.js";import{emitEvent as R}from"../../core/events/emit.js";import{joinedOwner as D}from"../../core/directory/join.js";import{allGrants as L}from"../../core/grants/mirror.js";import{errorCodes as N}from"../../api/error-codes.js";let z;function T(){return z??=import("../api/server.js").then(e=>e.buildRouter(()=>null)),z}s(T,"defaultRouter");function V(e,n,r,t,u,c){if(e.headersSent){e.destroy();return}const p=N[r];e.writeHead(n,{"content-type":"application/json; charset=utf-8","Request-Id":u,"X-Content-Type-Options":"nosniff"}),e.end(JSON.stringify({error:{code:r,message:t,...c===void 0?{}:{details:c},...p?{type:p.type}:{},request_id:u}}))}s(V,"refuse");const _=s((e,n)=>{const r=e.headers[n.toLowerCase()];return Array.isArray(r)?r[0]:r},"header");function G(e,n){if(n===void 0)return{principal:{class:"machine",via:e.machine_id,sub:e.sub}};let r;try{r=A(n)}catch(u){return{status:400,code:"invalid_request",message:u instanceof H?u.message:"invalid envelope"}}if(r.act.sub!==e.machine_id||r.sub!==e.sub)return{status:401,code:"unauthorized",message:"the envelope does not match the machine token"};const t=r.act.act;return t===void 0?{principal:{class:"forwarded",via:e.machine_id,sub:e.sub,actor:{kind:"person"}}}:t.place===null?{status:400,code:"invalid_request",message:"the envelope names an app with no run place"}:{principal:{class:"forwarded",via:e.machine_id,sub:e.sub,actor:{kind:"app",app:t.sub,node:t.node,node_scopes:t.node_scopes,place:t.place}}}}s(G,"peerPrincipal");function Z(){try{const e=D();if(e!==null)return e;const n=new Set(L().map(r=>r.sub));return n.size===1?[...n][0]:null}catch{return null}}s(Z,"runtimeOwner");async function x(e,n,r){const t=n.getHeader("Request-Id")?.toString()??q();n.setHeader("Request-Id",t);const u=_(e,C),c=new URL(e.url??"/","http://peer.local"),p=(e.method??"GET").toUpperCase();let d={class:u===void 0?"machine":"forwarded",grantee:null,sub:null,via:"unauthenticated",operation:`${p} ${c.pathname}`,node_id:null},v=!1;const b=s((a,l={})=>{if(!v){v=!0;try{U({request_id:t,resource:c.pathname,...d,...l,outcome:a})}catch(o){R({level:"error",event:"daemon.peer.audit_failed",error:o instanceof Error?o:new Error(String(o)),fields:{request_id:t}})}}},"record"),i=s((a,l,o,m)=>{b(k(a)),V(n,a,l,o,t,m)},"deny");try{if(r.ready&&!r.ready()){i(503,"runtime_starting","runtime_starting");return}const a=_(e,"authorization"),l=r.audience();if(l===null){i(503,"runtime_starting","this machine does not yet know its own address");return}if(!a?.startsWith("Bearer ")||a.length<=7){i(401,"unauthorized","a machine token is required");return}let o;try{o=await j({token:a.slice(7),jwks:r.jwks,issuer:r.issuer,audience:l})}catch{i(401,"unauthorized","invalid machine token");return}d={...d,grantee:o.sub,sub:o.sub,via:o.machine_id};const m=r.owner();if(m===null||o.sub!==m){i(401,"unauthorized","the machine token is for another account");return}if(o.machine_id===r.self()){i(401,"unauthorized","a machine does not call itself");return}const g=await(r.router??T)(),y=g.match(p,c.pathname);d={...d,operation:y?.operation.name??d.operation,node_id:y?.params.id??null};const h=I(_(e,P));if(h!==null){i(426,h.code,h.message,h.details);return}const f=G(o,u);if("status"in f){i(f.status,f.code,f.message);return}const{principal:w}=f,E=w.class==="forwarded"&&w.actor.kind==="app"?w.actor:null,O={grantee:E?.app??o.sub,node_id:E?.node??d.node_id};n.once("close",()=>b(n.writableFinished?k(n.statusCode):"aborted",O)),await g.handle(e,n,null,w)}catch(a){R({level:"error",event:"daemon.peer.ingress_failed",error:a instanceof Error?a:new Error(String(a)),fields:{request_id:t}}),i(500,"server_failure","peer request failed")}}s(x,"servePeer");export{G as peerPrincipal,Z as runtimeOwner,x as servePeer};
|
|
@@ -7,6 +7,12 @@ description: Behavior changes in @north-light/sdk that an app must act on, newes
|
|
|
7
7
|
|
|
8
8
|
Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
|
|
9
9
|
|
|
10
|
+
## 0.3.445
|
|
11
|
+
|
|
12
|
+
### Peer requests between an account's machines (additive, no app change)
|
|
13
|
+
|
|
14
|
+
A runtime now serves requests from another of the account's machines (a machine token plus a `Crtr-Envelope`). Nothing an app sends or reads changes: an app grant token takes the same path as before. The router gains a `forwarded` caller class on the object, node-read, message and canvas read rows, four machine-only `peer.*` rows that answer `501 not_implemented` until they ship, and two new refusal codes on peer requests only (`app_not_granted`, `peer_version_too_old`).
|
|
15
|
+
|
|
10
16
|
## 0.3.423
|
|
11
17
|
|
|
12
18
|
### Compatibility is a contract level, not the package version (additive)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.445",
|
|
4
4
|
"description": "crtr — agent runtime with memory, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
},
|
|
138
138
|
"license": "UNLICENSED",
|
|
139
139
|
"dependencies": {
|
|
140
|
-
"@north-light/identity": "0.3.
|
|
140
|
+
"@north-light/identity": "0.3.445",
|
|
141
141
|
"@earendil-works/pi-agent-core": "1.1.0",
|
|
142
142
|
"@earendil-works/pi-ai": "1.1.0",
|
|
143
143
|
"@earendil-works/pi-coding-agent": "1.1.0",
|
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.445",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/cli",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.445",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "UNLICENSED",
|
|
12
12
|
"workspaces": [
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@earendil-works/pi-ai": "1.1.0",
|
|
19
19
|
"@earendil-works/pi-coding-agent": "1.1.0",
|
|
20
20
|
"@earendil-works/pi-tui": "1.1.0",
|
|
21
|
-
"@north-light/identity": "0.3.
|
|
21
|
+
"@north-light/identity": "0.3.445",
|
|
22
22
|
"cron-parser": "^5.6.0",
|
|
23
23
|
"esbuild": "^0.27.7",
|
|
24
24
|
"jose": "^6.2.12",
|
|
@@ -12095,17 +12095,17 @@
|
|
|
12095
12095
|
},
|
|
12096
12096
|
"packages/crouter-api": {
|
|
12097
12097
|
"name": "@north-light/api",
|
|
12098
|
-
"version": "0.3.
|
|
12098
|
+
"version": "0.3.445",
|
|
12099
12099
|
"license": "UNLICENSED"
|
|
12100
12100
|
},
|
|
12101
12101
|
"packages/crouter-env-docker": {
|
|
12102
12102
|
"name": "@north-light/env-docker",
|
|
12103
|
-
"version": "0.3.
|
|
12103
|
+
"version": "0.3.445",
|
|
12104
12104
|
"license": "UNLICENSED"
|
|
12105
12105
|
},
|
|
12106
12106
|
"packages/crouter-identity": {
|
|
12107
12107
|
"name": "@north-light/identity",
|
|
12108
|
-
"version": "0.3.
|
|
12108
|
+
"version": "0.3.445",
|
|
12109
12109
|
"license": "UNLICENSED",
|
|
12110
12110
|
"dependencies": {
|
|
12111
12111
|
"jose": "^6.2.1"
|
|
@@ -12113,20 +12113,20 @@
|
|
|
12113
12113
|
},
|
|
12114
12114
|
"packages/crouter-plugin": {
|
|
12115
12115
|
"name": "@north-light/plugin",
|
|
12116
|
-
"version": "0.3.
|
|
12116
|
+
"version": "0.3.445",
|
|
12117
12117
|
"license": "UNLICENSED",
|
|
12118
12118
|
"dependencies": {
|
|
12119
|
-
"@north-light/api": "0.3.
|
|
12120
|
-
"@north-light/identity": "0.3.
|
|
12119
|
+
"@north-light/api": "0.3.445",
|
|
12120
|
+
"@north-light/identity": "0.3.445"
|
|
12121
12121
|
}
|
|
12122
12122
|
},
|
|
12123
12123
|
"packages/crouter-sdk": {
|
|
12124
12124
|
"name": "@north-light/sdk",
|
|
12125
|
-
"version": "0.3.
|
|
12125
|
+
"version": "0.3.445",
|
|
12126
12126
|
"license": "UNLICENSED",
|
|
12127
12127
|
"dependencies": {
|
|
12128
|
-
"@north-light/api": "0.3.
|
|
12129
|
-
"@north-light/identity": "0.3.
|
|
12128
|
+
"@north-light/api": "0.3.445",
|
|
12129
|
+
"@north-light/identity": "0.3.445",
|
|
12130
12130
|
"jose": "^6.2.1"
|
|
12131
12131
|
},
|
|
12132
12132
|
"bin": {
|