@north-light/cli 0.3.441 → 0.3.444
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clients/attach/viewer.js +527 -527
- package/dist/core/directory/join.d.ts +2 -0
- package/dist/core/directory/join.js +1 -1
- package/dist/core/fs-utils.d.ts +7 -0
- package/dist/core/fs-utils.js +3 -3
- 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/layout-migrate/index.js +4 -4
- package/dist/core/layout.d.ts +8 -0
- package/dist/core/layout.js +2 -2
- package/dist/core/locked-file.js +1 -1
- 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/core/scoped-state/db.js +2 -2
- package/dist/core/secrets.d.ts +3 -4
- package/dist/core/secrets.js +1 -2
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/dist/daemon/crtrd.js +7 -7
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var i=Object.defineProperty;var l=(e,o)=>i(e,"name",{value:o,configurable:!0});import{joinedOwner as d}from"../directory/join.js";import{runPlace as h}from"../runs/confinement.js";import{ownMachineId as p}from"./directory.js";const g="Crtr-Envelope",b=16*1024;class n extends Error{static{l(this,"EnvelopeError")}constructor(o){super(o),this.name="EnvelopeError"}}function y(e,o={}){const s=o.via??p(),t=e.class==="person"?"":e.sub,r=o.owner??(t.startsWith("user:")?t:d());if(r==null)throw new n("this machine has no account owner to make a peer call for");if(e.class==="person")return{sub:r,act:{sub:s}};if(e.class==="broker"){const a=(o.placeOf??(u=>h(u)))(e.node);return{sub:r,act:{sub:s,act:{sub:e.app,node:e.node,node_scopes:e.scopes===null?null:[...e.scopes],place:a}}}}return{sub:r,act:{sub:s,act:{sub:e.app,node:e.anchor,node_scopes:e.scopes===null?null:[...e.scopes],place:{run:e.origin_run??"",isolated:e.isolated}}}}}l(y,"envelopeFor");function _(e){return Buffer.from(JSON.stringify(e),"utf8").toString("base64url")}l(_,"encodeEnvelope");const c=l(e=>typeof e=="string"&&e!=="","isString"),f=l(e=>typeof e=="object"&&e!==null&&!Array.isArray(e),"isRecord");function A(e){if(e.length>b)throw new n("the envelope is too large");if(!/^[A-Za-z0-9_-]+$/.test(e))throw new n("the envelope is not base64url");let o;try{o=JSON.parse(Buffer.from(e,"base64url").toString("utf8"))}catch{throw new n("the envelope is not JSON")}if(!f(o)||!c(o.sub)||!o.sub.startsWith("user:"))throw new n("the envelope names no user");const s=o.act;if(!f(s)||!c(s.sub)||!s.sub.startsWith("runtime:"))throw new n("the envelope names no calling machine");const t=s.act;if(t===void 0)return{sub:o.sub,act:{sub:s.sub}};if(!f(t)||!c(t.sub)||!t.sub.startsWith("app:"))throw new n("the envelope names no calling app");const{node:r,node_scopes:a,place:u}=t;if(r!==null&&!c(r))throw new n("the envelope node is not a node id");if(a!==null&&!(Array.isArray(a)&&a.every(c)))throw new n("the envelope node_scopes is not a list of scopes");if(u!==null&&!(f(u)&&typeof u.run=="string"&&typeof u.isolated=="boolean"))throw new n("the envelope place is malformed");return{sub:o.sub,act:{sub:s.sub,act:{sub:t.sub,node:r,node_scopes:a,place:u===null?null:{run:u.run,isolated:u.isolated}}}}}l(A,"decodeEnvelope");export{g as ENVELOPE_HEADER,n as EnvelopeError,b as MAX_ENVELOPE_BYTES,A as decodeEnvelope,_ as encodeEnvelope,y as envelopeFor};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface PeerEvent {
|
|
2
|
+
/** Subscribe; returns the function that unsubscribes. */
|
|
3
|
+
on(listener: () => void): () => void;
|
|
4
|
+
/** Call every listener once. A throwing listener never stops the others. */
|
|
5
|
+
emit(): void;
|
|
6
|
+
listenerCount(): number;
|
|
7
|
+
}
|
|
8
|
+
/** Emitted on every successful connect of this daemon's WebSocket to the account relay. */
|
|
9
|
+
export declare const relayConnected: PeerEvent;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var r=Object.defineProperty;var n=(e,t)=>r(e,"name",{value:t,configurable:!0});function o(){const e=new Set;return{on(t){return e.add(t),()=>{e.delete(t)}},emit(){for(const t of[...e])try{t()}catch{}},listenerCount:n(()=>e.size,"listenerCount")}}n(o,"createPeerEvent");const s=o();export{s as relayConnected};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type DirectoryToken } from '../grants/sync.js';
|
|
2
|
+
export declare const REFRESH_BEFORE_EXPIRY_MS = 30000;
|
|
3
|
+
export type Mint = (issuer: string, audience?: string) => Promise<DirectoryToken>;
|
|
4
|
+
export interface TokenCache {
|
|
5
|
+
/** A token for `audience` (a peer machine URL or the relay origin), or for the directory's own `/v1` when omitted. */
|
|
6
|
+
get(issuer: string, audience?: string): Promise<string>;
|
|
7
|
+
/** Forget a token the far side refused (revoked key, clock skew); the next `get` mints a new one. */
|
|
8
|
+
drop(issuer: string, audience?: string): void;
|
|
9
|
+
clear(): void;
|
|
10
|
+
}
|
|
11
|
+
export declare function createTokenCache(mint?: Mint, now?: () => number): TokenCache;
|
|
12
|
+
/** The daemon's process-wide cache. */
|
|
13
|
+
export declare const peerTokens: TokenCache;
|
|
14
|
+
/** A machine token whose `aud` is `audience` (a peer machine's URL, or `https://relay.northlight.ai`). */
|
|
15
|
+
export declare function machineToken(issuer: string, audience: string): Promise<string>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var k=Object.defineProperty;var i=(n,r)=>k(n,"name",{value:r,configurable:!0});import{mintDirectoryToken as _}from"../grants/sync.js";const g=3e4;function h(n=_,r=Date.now){const c=new Map,a=new Map,p=i((e,t)=>`${e.replace(/\/$/,"")}
|
|
2
|
+
${t??""}`,"keyOf");return{async get(e,t){const o=p(e,t),l=c.get(o);if(l&&l.expires_at-g>r())return l.access_token;let s=a.get(o);return s||(s=n(e,t).then(f=>(c.set(o,f),f)).finally(()=>{a.delete(o)}),a.set(o,s)),(await s).access_token},drop(e,t){c.delete(p(e,t))},clear(){c.clear(),a.clear()}}}i(h,"createTokenCache");const u=h();function E(n,r){return u.get(n,r)}i(E,"machineToken");export{g as REFRESH_BEFORE_EXPIRY_MS,h as createTokenCache,E as machineToken,u as peerTokens};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Request header naming the sending machine's crtr release. */
|
|
2
|
+
export declare const PEER_VERSION_HEADER = "Crtr-Version";
|
|
3
|
+
/** The oldest crtr release that speaks the peer protocol. L1 sets this to the release that ships C1–C6. */
|
|
4
|
+
export declare const PEER_VERSION_FLOOR = "0.3.442";
|
|
5
|
+
export interface PeerVersionRefusal {
|
|
6
|
+
status: 426;
|
|
7
|
+
code: 'peer_version_too_old';
|
|
8
|
+
message: string;
|
|
9
|
+
details: {
|
|
10
|
+
peer_version: string | null;
|
|
11
|
+
floor: string;
|
|
12
|
+
receiver_version: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** The refusal for a peer announcing `peerVersion` (the `Crtr-Version` header, or a reach answer's `crtr_version`), or null when it is at or above the floor. */
|
|
16
|
+
export declare function peerVersionRefusal(peerVersion: string | null | undefined, floor?: string, own?: string): PeerVersionRefusal | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var n=Object.defineProperty;var i=(r,e)=>n(r,"name",{value:e,configurable:!0});import{compareReleaseVersions as a}from"../../api/runtime-version.js";import{crtrVersion as o}from"../../shared/crtr-version.js";const l="Crtr-Version",c="0.3.442";function d(r,e=c,s=o()){const t=r?.trim()?r.trim():null;return t!==null&&a(t,e)>=0?null:{status:426,code:"peer_version_too_old",message:t===null?`The peer did not name its crtr version; machines below ${e} cannot take part (this machine is ${s}). Update it with \`crtr sys update\`.`:`The peer runs crtr ${t}; machines below ${e} cannot take part (this machine is ${s}). Update it with \`crtr sys update\`.`,details:{peer_version:t,floor:e,receiver_version:s}}}i(d,"peerVersionRefusal");export{c as PEER_VERSION_FLOOR,l as PEER_VERSION_HEADER,d as peerVersionRefusal};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var X=Object.defineProperty;var i=(e,n)=>X(e,"name",{value:n,configurable:!0});import{existsSync as h,readdirSync as H,readFileSync as y,rmSync as b,statSync as x}from"node:fs";import{join as u}from"node:path";import{validateCommandManifest as Y}from"../../api/command-manifest/manifest.js";import{crtrHome as O}from"../canvas/paths.js";import{withCanvasWrite as ee}from"../canvas/db.js";import{bundleVersion as ne,isInstalledGeneration as re,recordCheckedGeneration as te}from"../command-plugins/bundle.js";import{readConfig as U,readState as oe}from"../config.js";import{emitEvent as g}from"../events/emit.js";import{withExclusiveDirectoryLockAsync as ie}from"../exclusive-lock.js";import{atomicWriteText as M,ensureDir as I,pathExists as ae}from"../fs-utils.js";import{reconcilePackageDocuments as se}from"../graph/package-docs.js";import{readPluginManifest as W}from"../manifest.js";import{bundleSwapLockPath as de}from"../plugin-swap-lock.js";import{pluginsDir as ue,userScopeRoot as ce}from"../scope.js";import{
|
|
1
|
+
var X=Object.defineProperty;var i=(e,n)=>X(e,"name",{value:n,configurable:!0});import{existsSync as h,readdirSync as H,readFileSync as y,rmSync as b,statSync as x}from"node:fs";import{join as u}from"node:path";import{validateCommandManifest as Y}from"../../api/command-manifest/manifest.js";import{crtrHome as O}from"../canvas/paths.js";import{withCanvasWrite as ee}from"../canvas/db.js";import{bundleVersion as ne,isInstalledGeneration as re,recordCheckedGeneration as te}from"../command-plugins/bundle.js";import{readConfig as U,readState as oe}from"../config.js";import{emitEvent as g}from"../events/emit.js";import{withExclusiveDirectoryLockAsync as ie}from"../exclusive-lock.js";import{atomicWriteText as M,ensureDir as I,pathExists as ae}from"../fs-utils.js";import{reconcilePackageDocuments as se}from"../graph/package-docs.js";import{readPluginManifest as W}from"../manifest.js";import{bundleSwapLockPath as de}from"../plugin-swap-lock.js";import{pluginsDir as ue,userScopeRoot as ce}from"../scope.js";import{localGrants as fe}from"../grants/mirror.js";import{ProviderError as c}from"./errors.js";const T="[A-Za-z0-9_-]{1,64}",le=new RegExp(`^(${T}):(${T})$`),z=new RegExp(`^${T}$`),pe=3e4,me=3e4;function ve(e){const n=new Set;for(const r of e)if(r.status==="active")for(const{scope:t}of r.resolved_scopes){const o=le.exec(t);o&&o[1]!=="crtr"&&n.add(o[1])}return[...n].sort()}i(ve,"providerIdsFromGrants");function w(e){return u(ue("user"),e)}i(w,"pluginRoot");function F(){return u(O(),"providers")}i(F,"providersRoot");function G(e,n){return u(F(),e,`v${n}`)}i(G,"majorDir");function j(e){if(!z.test(e))throw new c("not_found",`"${e}" is not a provider id`,{param:"provider"})}i(j,"assertId");function A(){const e=U("user").runtime?.issuer;if(!e)throw new c("not_found","provider bundles need a directory-connected runtime");return e.replace(/\/+$/,"")}i(A,"directoryBase");async function q(e,n){const r=`${A()}/providers/${encodeURIComponent(e)}/plugin${n.version!==void 0?`?version=${n.version}`:""}`;let t;try{t=await fetch(r,{headers:{Accept:"application/x-tar",...n.ifNoneMatch!==void 0?{"If-None-Match":n.ifNoneMatch}:{}},signal:AbortSignal.timeout(pe)})}catch(s){return{status:"failed",message:`could not reach ${r}: ${s instanceof Error?s.message:String(s)}`}}if(t.status===304)return n.ifNoneMatch===void 0?{status:"failed",message:`HTTP 304 from ${r} without a conditional request`}:{status:304};if(t.status===404||t.status===410)return await t.body?.cancel().catch(()=>{}),{status:t.status===404?404:410,message:`HTTP ${t.status} from ${r}`};if(t.status<200||t.status>=300)return await t.body?.cancel().catch(()=>{}),{status:"failed",message:`HTTP ${t.status} from ${r}`};let o;try{o=new Uint8Array(await t.arrayBuffer())}catch(s){return{status:"failed",message:`reading ${r} failed: ${s instanceof Error?s.message:String(s)}`}}const a=t.headers.get("etag");return{status:200,raw:o,...a!==null?{etag:a}:{}}}i(q,"fetchProviderBundle");const _=new WeakSet;class S extends Error{static{i(this,"BundleInvalid")}issues;constructor(n,r){super(n),this.issues=r}}function V(e,n,r){if(r.provider===void 0)return"the archive carries no provider block";if(r.provider.id!==e)return`the archive is provider "${r.provider.id}", not "${e}"`;if(n!==void 0&&r.provider.major!==n)return`the archive is major ${r.provider.major}, not ${n}`;const t=["hooks","kinds","page_components","memory_extensions"].filter(o=>r[o]!==void 0);if(t.length>0)return`a provider bundle may not carry ${t.join(", ")}`}i(V,"wrongBundle");function C(e,n,r){g({level:"error",event:"daemon.providers.bundle_invalid",fields:{provider:e,...n!==void 0?{major:n}:{},...r instanceof S?{issues:r.issues}:{}},error:r})}i(C,"logInvalid");function Z(e,n,r){g({level:"warn",event:"daemon.providers.bundle_fetch_failed",fields:{provider:e,...n!==void 0?{major:n}:{},message:r}})}i(Z,"logFailure");async function L(){return import("../../commands/pkg/plugin-manage.js")}i(L,"pluginManage");const P=new Map,E=i(e=>`${O()}\0${e}`,"installKey");function he(e){const n=e,r=n?.code??n?.cause?.code;return typeof r=="string"&&/^[A-Z][A-Z0-9_]{1,30}$/.test(r)?`${r} on provider install directory`:e instanceof c&&e.message.includes("HTTP 404")?"directory returned HTTP 404":e instanceof c&&e.message.includes("HTTP 410")?"directory returned HTTP 410":"provider bundle could not be installed"}i(he,"publicInstallCause");function ge(e){if(m(e)===void 0)return P.get(E(e))??{status:"installing"}}i(ge,"providerInstallState");function we(e){return new c("provider_unavailable",`provider "${e}" unavailable: ${ge(e)?.error??"bundle installation pending"}`)}i(we,"unavailableProviderError");function m(e){const n=W(w(e));if(!(n?.provider===void 0||n.transport?.kind!=="http")&&ae(u(w(e),"commands.json"))){try{if(x(u(w(e),"commands.json")).size===0)return}catch{return}return{provider:n.provider,transport:{kind:"http",endpoint:n.transport.endpoint}}}}i(m,"installedManifest");async function k(e,n,r=!1){const t=ce();m(e)===void 0&&P.set(E(e),{status:"installing"});try{return I(u(t,"tmp")),await ie(de(t,e),async()=>{const o=w(e),a=W(o);if(a!==null&&a.provider===void 0)throw new c("provider_unavailable",`plugin "${e}" is installed and is not the directory's provider bundle`);const s=m(e)!==void 0;if(s&&r)return"unchanged";const f=s&&n?oe("user").plugins[e]?.etag:void 0,d=await q(e,f!==void 0?{ifNoneMatch:f}:{});if(d.status===304)return"unchanged";if(d.status===404||d.status===410)throw new c("not_found",`the directory serves no provider "${e}" (${d.message})`,{param:"provider"});if(d.status==="failed")throw new c("provider_unavailable",`provider "${e}" bundle fetch failed: ${d.message}`);const l=ne(d.raw);if(s&&re(e,"user",o,l))return te("user",e,d.etag,{swapped:!1}),"unchanged";const{installPluginArchive:v}=await L();try{await v(e,d.raw,{scope:"user",scopeRootPath:t,root:o,version:l,etag:d.etag,enable:!s,bundle:{endpoint:`${A()}/providers/${encodeURIComponent(e)}/plugin`},transport:{kind:"http",endpoint:A()},accept:i(p=>V(e,void 0,p),"accept")})}catch(p){C(e,void 0,p);const N=new c("not_found",`provider "${e}" served an invalid bundle: ${p instanceof Error?p.message:String(p)}`,{param:"provider"});throw _.add(N),N}try{ee(p=>se(p))}catch(p){g({level:"error",event:"daemon.providers.docs_reconcile_failed",fields:{provider:e},error:p})}return g({level:"info",event:"daemon.providers.bundle_installed",fields:{provider:e,version:l,replaced:s}}),P.delete(E(e)),s?"updated":"installed"},{timeoutMs:me,timeoutError:i(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}catch(o){throw m(e)===void 0&&P.set(E(e),{status:"install_failed",error:he(o)}),o instanceof c?o:(g({level:"error",event:"daemon.providers.bundle_install_failed",fields:{provider:e},error:o}),we(e))}}i(k,"refreshInstalled");async function We(e){j(e),m(e)===void 0&&await k(e,!1,!0)}i(We,"ensureProviderBundle");async function ze(e,n){return j(e),n.version!==void 0&&m(e)?.provider.major!==n.version?R(e,n.version,n.conditional):k(e,n.conditional)}i(ze,"refreshProviderBundle");async function J(e,n,r){const{validatePluginArchive:t}=await L(),o=await t(r);if(o.bundle===void 0)throw new S(`provider "${e}" major ${n} bundle is invalid: ${o.issues.map(s=>s.message).join("; ")}`,o.issues);const a=V(e,n,o.bundle);if(a!==void 0)throw new S(`provider "${e}" major ${n} bundle is invalid: ${a}`,[]);if(o.bundle.transport===void 0)throw new S(`provider "${e}" major ${n} bundle declares no transport`,[]);return o.bundle}i(J,"validateArchive");function $e(e){I(e),M(u(e,"retired"),`${new Date().toISOString()}
|
|
2
2
|
`),b(u(e,"bundle.tar"),{force:!0}),b(u(e,"etag"),{force:!0})}i($e,"retire");function K(e,n){return new c("provider_version_retired",`provider "${e}" major ${n} is retired`,{param:"version"})}i(K,"retiredError");async function R(e,n,r){const t=G(e,n),o=u(t,"bundle.tar"),a=h(o),s=a&&r&&h(u(t,"etag"))?y(u(t,"etag"),"utf8"):void 0,f=await q(e,{version:n,...s!==void 0?{ifNoneMatch:s}:{}});if(f.status===304)return"unchanged";if(f.status===410)throw $e(t),g({level:"info",event:"daemon.providers.major_retired",fields:{provider:e,major:n}}),K(e,n);if(f.status===404)throw new c("not_found",`provider "${e}" never served major ${n}`,{param:"version"});if(f.status==="failed")throw new c("provider_unavailable",`provider "${e}" major ${n} bundle fetch failed: ${f.message}`);try{await J(e,n,f.raw)}catch(l){C(e,n,l);const v=new c(a?"provider_unavailable":"not_found",l instanceof Error?l.message:String(l),{param:"version"});throw _.add(v),v}I(t),b(u(t,"retired"),{force:!0});const d=a&&Buffer.from(f.raw).equals(y(o));return d||M(o,f.raw),f.etag!==void 0?M(u(t,"etag"),f.etag):b(u(t,"etag"),{force:!0}),d?"unchanged":a?"updated":"installed"}i(R,"refreshPinned");function ye(){const e=[],n=F();if(!h(n))return e;for(const r of H(n)){if(!z.test(r))continue;let t;try{t=H(u(n,r))}catch{continue}for(const o of t){const a=/^v([1-9][0-9]*)$/.exec(o);a&&h(u(n,r,o,"bundle.tar"))&&e.push([r,Number(a[1])])}}return e}i(ye,"cachedMajors");function be(e){const n=Y(e,{transport:"http",reservedCoreNames:new Set});if(n.manifest===void 0)throw new c("not_found","the provider bundle's commands.json no longer validates");return n.manifest}i(be,"readCommands");const B=new Map,Q=new Map;function _e(e,n){const r=x(n),t=`${r.ino}:${r.mtimeMs}:${r.size}`,o=Q.get(e);if(o?.stamp===t)return o.commands;const a=be(JSON.parse(y(n,"utf8")));return Q.set(e,{stamp:t,commands:a}),a}i(_e,"installedCommands");async function Fe(e,n){j(e);const r=m(e);if(n===void 0||r?.provider.major===n){if(r===void 0)throw new c("not_found",`provider "${e}" has no installed bundle`,{param:"provider"});const l=w(e),v=_e(e,u(l,"commands.json"));return{id:e,major:r.provider.major,provider:r.provider,commands:v,transport:r.transport,source:"installed",root:l}}if(!Number.isInteger(n)||n<1)throw new c("invalid_request","version must be an integer of at least 1",{param:"version"});const t=G(e,n);if(h(u(t,"retired")))throw K(e,n);const o=u(t,"bundle.tar");h(o)||await R(e,n,!1);const a=`${e}/${n}`,s=x(o),f=`${s.mtimeMs}:${s.size}`;let d=B.get(a)?.stamp===f?B.get(a).bundle:void 0;if(d===void 0){try{d=await J(e,n,new Uint8Array(y(o)))}catch(l){throw C(e,n,l),new c("not_found",`provider "${e}" has no valid bundle for major ${n}`,{param:"version"})}B.set(a,{stamp:f,bundle:d})}return{id:e,major:n,provider:d.provider,commands:d.commands,transport:{kind:"http",endpoint:d.transport.endpoint},source:"pinned"}}i(Fe,"resolveProviderBundle");let $=null,D=!1;async function Se(){if(U("user").runtime?.issuer){for(const e of ve(fe()))try{await k(e,!0)}catch(n){n instanceof Object&&_.has(n)||Z(e,void 0,n instanceof Error?n.message:String(n))}for(const[e,n]of ye())try{await R(e,n,!0)}catch(r){if(r instanceof c&&r.code==="provider_version_retired"||r instanceof Object&&_.has(r))continue;Z(e,n,r instanceof Error?r.message:String(r))}}}i(Se,"syncOnce");function Ge(){return $!==null?(D=!0,$):($=(async()=>{try{do D=!1,await Se();while(D)}finally{$=null}})(),$)}i(Ge,"syncProviderBundles");export{We as ensureProviderBundle,ve as providerIdsFromGrants,ge as providerInstallState,ze as refreshProviderBundle,Fe as resolveProviderBundle,Ge as syncProviderBundles,we as unavailableProviderError};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var T=Object.defineProperty;var n=(e,o)=>T(e,"name",{value:o,configurable:!0});import{DatabaseSync as p}from"node:sqlite";import{forgetPrepared as R}from"../sqlite-statements.js";import{chmodSync as m,existsSync as s,mkdirSync as y,readdirSync as O,rmSync as I,statSync as P,writeFileSync as v}from"node:fs";import{join as i}from"node:path";import j from"proper-lockfile";import{CONFIG_FILE as S,PROFILE_DIR as N,STATE_FILE as f}from"../../types.js";import{withExclusiveDirectoryLock as _}from"../exclusive-lock.js";import{
|
|
2
|
-
ALTER TABLE profile_projects DROP COLUMN memory;`),e.exec(`PRAGMA user_version = ${d}`)),e.exec("COMMIT")}catch(r){throw e.exec("ROLLBACK"),r}}}n(x,"upgradeScopedStore");function
|
|
1
|
+
var T=Object.defineProperty;var n=(e,o)=>T(e,"name",{value:o,configurable:!0});import{DatabaseSync as p}from"node:sqlite";import{forgetPrepared as R}from"../sqlite-statements.js";import{chmodSync as m,existsSync as s,mkdirSync as y,readdirSync as O,rmSync as I,statSync as P,writeFileSync as v}from"node:fs";import{join as i}from"node:path";import j from"proper-lockfile";import{CONFIG_FILE as S,PROFILE_DIR as N,STATE_FILE as f}from"../../types.js";import{withExclusiveDirectoryLock as _}from"../exclusive-lock.js";import{syncDirectory as k,syncFile as D}from"../fs-utils.js";import{layoutNeedsMigration as C}from"../layout.js";import{providerStore as G,projectStore as g,userStore as $}from"./paths.js";import{createScopedSchema as B,ensureProviderModelCooldowns as F,PROVIDER_SCHEMA_VERSION as w,SCOPED_SCHEMA_VERSION as d}from"./schema.js";const l=new Map,U=/^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/;function H(e,o,r){e.prepare("UPDATE scope_owners SET root=? WHERE owner_key=?").run(r,o)}n(H,"writeOwnerRoot");function c(e){return Number(e.prepare("PRAGMA user_version").get().user_version)}n(c,"version");function L(e,o){const r=c(e);if(r===0)throw new Error(`scoped SQL store is uninitialized: ${o.path}; crtr was updated and \`crtr sys daemon restart\` converts local state`);const t=o.kind==="provider"?w:d;if(r>t)throw new Error(`scoped SQL store ${o.path} uses unsupported schema version ${r}`);if(r!==t)throw new Error(`scoped SQL store ${o.path} requires an explicit conversion`)}n(L,"assertVersion");function x(e,o){if(o.kind==="provider"){c(e)===w&&(e.exec("PRAGMA busy_timeout = 5000"),F(e));return}if(!(c(e)>=d)){e.exec("PRAGMA busy_timeout = 5000; BEGIN IMMEDIATE");try{c(e)===1&&(e.prepare("PRAGMA table_info(profiles)").all().some(({name:t})=>t==="grantee")||e.exec("ALTER TABLE profiles ADD COLUMN grantee TEXT"),e.exec("PRAGMA user_version = 2")),c(e)===2&&(e.prepare("PRAGMA table_info(profiles)").all().some(({name:t})=>t==="home")&&e.exec("ALTER TABLE profiles DROP COLUMN home"),e.exec("PRAGMA user_version = 3")),c(e)===3&&(e.exec("ALTER TABLE profiles ADD COLUMN delivery_limits_json TEXT NOT NULL DEFAULT '{}' CHECK(json_valid(delivery_limits_json))"),e.exec("PRAGMA user_version = 4")),c(e)===4&&(e.exec("CREATE TABLE IF NOT EXISTS legacy_project_caps (profile_id TEXT NOT NULL, path TEXT NOT NULL, memory TEXT NOT NULL CHECK(memory IN ('none','name','preview')), PRIMARY KEY(profile_id,path))"),e.prepare("PRAGMA table_info(profile_projects)").all().some(({name:t})=>t==="memory")&&e.exec(`INSERT INTO legacy_project_caps SELECT profile_id, path, memory FROM profile_projects WHERE memory != 'content';
|
|
2
|
+
ALTER TABLE profile_projects DROP COLUMN memory;`),e.exec(`PRAGMA user_version = ${d}`)),e.exec("COMMIT")}catch(r){throw e.exec("ROLLBACK"),r}}}n(x,"upgradeScopedStore");function ae(e){const o=new p(e.path);try{x(o,e),L(o,e)}finally{o.close()}}n(ae,"assertScopedStoreReady");function K(e){if(!M(e))return;const o=new p(e.path,{readOnly:!0});let r;try{r=c(o)}finally{o.close()}if(r===0)for(const t of[e.path,`${e.path}-wal`,`${e.path}-shm`])I(t,{force:!0})}n(K,"discardIncompleteScopedStore");function X(e,o){return _(i(e,".init.lock"),()=>{const r=i(e,f),t=Date.now()+5e3;let a=10;for(;;){let E;try{E=j.lockSync(r,{realpath:!1,retries:0,stale:3e4,update:1e4})}catch(u){if(u.code!=="ELOCKED")throw u;const A=t-Date.now();if(A<=0)throw new Error(`timed out waiting for project state conversion lock: ${r}`,{cause:u});Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,Math.min(a,A)),a=Math.min(a*2,250);continue}try{return o()}finally{E()}}})}n(X,"withProjectStateLock");function h(e){s(e)&&m(e,384);for(const o of["-wal","-shm"])s(`${e}${o}`)&&m(`${e}${o}`,384)}n(h,"secure");function M(e){return s(e.path)}n(M,"scopedStoreExists");function z(e){if(e.kind==="project")return[i(e.root,f)];if(e.kind==="provider")return[i(e.root,"anthropic-subscriptions.json"),i(e.root,"openai-codex-subscriptions.json"),i(e.root,"provider-rotation.json")];const o=[i(e.root,S),i(e.root,f),i(e.root,"profile-defaults.json")],r=i(e.root,N);if(s(r))for(const t of O(r,{withFileTypes:!0}))t.isDirectory()&&U.test(t.name)&&o.push(i(r,t.name,"profile.json"),i(r,t.name,S),i(r,t.name,f));return o}n(z,"legacyScopedStorePaths");function Q(e){const o=e.kind==="user"?$(e.root):e.kind==="project"?g(e.root):G(e.root);o.kind!=="project"&&y(o.root,{recursive:!0,mode:448});const r=n(()=>{if(o.kind==="project"&&K(o),M(o))return;const t=z(o).find(s);if(t!==void 0)throw new Error(`legacy scoped state exists at ${t}; crtr was updated and \`crtr sys daemon restart\` converts local state`);V(o,a=>{B(a,o.kind),o.kind!=="provider"&&H(a,o.kind,o.root)})},"initialize");o.kind==="project"?X(o.root,r):_(i(o.root,".init.lock"),r)}n(Q,"initializeFreshScopedStore");function W(e){if(C())throw new Error("layout_migration_required");const o=l.get(e.path);if(o!==void 0)return o;Q(e);const r=new p(e.path);try{return r.exec("PRAGMA busy_timeout = 5000; PRAGMA journal_mode = WAL; PRAGMA foreign_keys = ON;"),x(r,e),L(r,e),h(e.path),l.set(e.path,r),r}catch(t){throw r.close(),t}}n(W,"openScopedStore");function V(e,o){if(s(e.path))throw new Error(`scoped SQL store already exists: ${e.path}`);if(e.kind!=="project"&&y(e.root,{recursive:!0,mode:448}),!P(e.root).isDirectory())throw new Error(`scoped SQL root is not a directory: ${e.root}`);(e.kind!=="project"||process.platform!=="linux")&&m(e.root,448),v(e.path,"",{mode:384});const r=new p(e.path);try{r.exec("PRAGMA busy_timeout = 5000; PRAGMA journal_mode = WAL; PRAGMA foreign_keys = ON; BEGIN IMMEDIATE;"),o(r),r.exec("COMMIT")}catch(t){try{r.exec("ROLLBACK")}catch{}throw t}finally{h(e.path),r.close()}D(e.path),k(e.root)}n(V,"createScopedStore");function se(e,o){const r=W(e);if(r.isTransaction)return o(r);r.exec("BEGIN IMMEDIATE");try{const t=o(r);return r.exec("COMMIT"),h(e.path),t}catch(t){try{r.exec("ROLLBACK")}catch{}throw t}}n(se,"withScopedWrite");function pe(){for(const e of l.values())R(e),e.close();l.clear()}n(pe,"closeScopedStores");export{U as PROFILE_ID_SHAPE,ae as assertScopedStoreReady,pe as closeScopedStores,V as createScopedStore,K as discardIncompleteScopedStore,Q as initializeFreshScopedStore,z as legacyScopedStorePaths,W as openScopedStore,M as scopedStoreExists,X as withProjectStateLock,se as withScopedWrite,H as writeOwnerRoot};
|
package/dist/core/secrets.d.ts
CHANGED
|
@@ -21,10 +21,9 @@ export interface ScopedApiToken {
|
|
|
21
21
|
created: string;
|
|
22
22
|
}
|
|
23
23
|
export declare function readSecrets(scope: Scope): SecretsStore;
|
|
24
|
-
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* permissions (e.g. hand-copied from another machine). */
|
|
24
|
+
/** Replaces secrets.json whole (temp file, fsync, rename, directory fsync) at mode 0600 -- forced even
|
|
25
|
+
* over a pre-existing looser file, e.g. one hand-copied from another machine -- so a killed VM or a
|
|
26
|
+
* volume handoff never leaves the relay tokens empty or partial. */
|
|
28
27
|
export declare function writeSecrets(scope: Scope, store: SecretsStore): void;
|
|
29
28
|
export declare function getRelayToken(ref: string, scope: Scope): string | undefined;
|
|
30
29
|
export declare function setRelayToken(ref: string, token: string, scope: Scope): void;
|
package/dist/core/secrets.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
var
|
|
2
|
-
`,{mode:384}),A(r,384)}t(p,"writeSecrets");function J(e,o){return s(o).relayTokens[e]}t(J,"getRelayToken");function N(e,o,n){const r=s(n);r.relayTokens[e]=o,p(n,r)}t(N,"setRelayToken");function O(e,o){const n=s(o);delete n.relayTokens[e],p(o,n)}t(O,"deleteRelayToken");function Z(e){return s(e).apiToken}t(Z,"getApiToken");function _(e,o){const n=s(o);n.apiToken=e,p(o,n)}t(_,"setApiToken");function q(e){return s(e).scopedApiTokens??[]}t(q,"listScopedApiTokens");function C(e,o){const n=s(o);n.scopedApiTokens=[...n.scopedApiTokens??[],e],p(o,n)}t(C,"addScopedApiToken");function H(e){return u("sha256").update(e).digest("hex").slice(0,12)}t(H,"fingerprint");export{C as addScopedApiToken,O as deleteRelayToken,H as fingerprint,Z as getApiToken,J as getRelayToken,q as listScopedApiTokens,s as readSecrets,_ as setApiToken,N as setRelayToken,p as writeSecrets};
|
|
1
|
+
var u=Object.defineProperty;var t=(e,o)=>u(e,"name",{value:o,configurable:!0});import{createHash as f}from"node:crypto";import{existsSync as y,readFileSync as d}from"node:fs";import{join as l}from"node:path";import{scopeRoot as A,requireScopeRoot as m,userScopeRoot as a}from"./scope.js";import{secretsDir as k}from"./layout.js";import{atomicWriteJson as x,ensureDir as g}from"./fs-utils.js";const S="secrets.json";function T(e){return l(e===a()?k():e,S)}t(T,"secretsPathFor");function s(e){const o=A(e);if(!o)return{relayTokens:{}};const r=T(o);if(!y(r))return{relayTokens:{}};try{const n=JSON.parse(d(r,"utf8")),c=Array.isArray(n.scopedApiTokens)?n.scopedApiTokens.filter(i=>typeof i?.token=="string"&&i.token!==""&&typeof i.grantee=="string"&&/^app:[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(i.grantee)&&Array.isArray(i.scopes)&&typeof i.created=="string"):[];return{relayTokens:n.relayTokens??{},...typeof n.apiToken=="string"&&n.apiToken!==""?{apiToken:n.apiToken}:{},...c.length>0?{scopedApiTokens:c}:{}}}catch{return{relayTokens:{}}}}t(s,"readSecrets");function p(e,o){const r=m(e),n=T(r);g(r===a()?k():r),x(n,o,{mode:384,durable:!0})}t(p,"writeSecrets");function J(e,o){return s(o).relayTokens[e]}t(J,"getRelayToken");function Z(e,o,r){const n=s(r);n.relayTokens[e]=o,p(r,n)}t(Z,"setRelayToken");function _(e,o){const r=s(o);delete r.relayTokens[e],p(o,r)}t(_,"deleteRelayToken");function b(e){return s(e).apiToken}t(b,"getApiToken");function q(e,o){const r=s(o);r.apiToken=e,p(o,r)}t(q,"setApiToken");function w(e){return s(e).scopedApiTokens??[]}t(w,"listScopedApiTokens");function C(e,o){const r=s(o);r.scopedApiTokens=[...r.scopedApiTokens??[],e],p(o,r)}t(C,"addScopedApiToken");function H(e){return f("sha256").update(e).digest("hex").slice(0,12)}t(H,"fingerprint");export{C as addScopedApiToken,_ as deleteRelayToken,H as fingerprint,b as getApiToken,J as getRelayToken,w as listScopedApiTokens,s as readSecrets,q as setApiToken,Z as setRelayToken,p as writeSecrets};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var E=Object.defineProperty;var s=(e,r)=>E(e,"name",{value:r,configurable:!0});import{randomUUID as P}from"node:crypto";import{readConfig as I}from"../../../core/config.js";import{activeGrantForApp as R,
|
|
1
|
+
var E=Object.defineProperty;var s=(e,r)=>E(e,"name",{value:r,configurable:!0});import{randomUUID as P}from"node:crypto";import{readConfig as I}from"../../../core/config.js";import{activeGrantForApp as R,localGrants as _}from"../../../core/grants/mirror.js";import{getNode as O}from"../../../core/canvas/canvas.js";import{scopeAllowed as $}from"../../../core/scopes.js";import{ProviderError as u,providerErrorBody as q,scopeMissing as x}from"../../../core/providers/errors.js";import{callProvider as G,providerTools as N}from"../../../core/providers/call.js";import{providerIdsFromGrants as T,providerInstallState as F}from"../../../core/providers/bundles.js";import{listInstalledPlugins as j}from"../../../core/resolver.js";import{listAudit as C,writeProviderAudit as D}from"../../../core/providers/audit.js";import{emitEvent as g}from"../../../core/events/emit.js";import{resolvedScopes as h}from"../router.js";const fe=new Set(["provider.tools","provider.call","audit.list"]);function M(e){if(e.class==="person")return{class:"person",sub:null,grantee:null,client_id:null,resolved:null,nodeScopes:null,run_id:null,node_id:null};if(e.class==="app"){const r=R(e.app);if(!r)throw new u("grant_revoked",`app ${e.app} has no active grant`);return{class:"app",sub:r.sub,grantee:e.app,client_id:null,resolved:r.resolved,nodeScopes:e.scopes,run_id:null,node_id:null}}return e.class==="cron"?{class:"app",sub:e.sub,grantee:e.app,client_id:null,resolved:h(e),nodeScopes:e.scopes,run_id:e.origin_run,node_id:null}:{class:"broker",sub:e.sub,grantee:e.app,client_id:e.client_id,resolved:h(e),nodeScopes:e.scopes,run_id:e.run??O(e.node)?.run_id??null,node_id:e.node}}s(M,"callerFromRequest");function ce(e){return{class:"app",sub:e.sub,grantee:e.grantee,client_id:e.client_id,resolved:e.resolved,nodeScopes:null,run_id:null,node_id:null}}s(ce,"callerFromApp");const U=/^([A-Za-z0-9_-]{1,64}):([A-Za-z0-9_-]{1,64})$/;function k(e){const r=U.exec(e);return r!==null&&r[1]!=="crtr"}s(k,"isProviderScope");const w=new WeakMap;function z(e){let r=w.get(e);return r===void 0&&(r=new Set(e),w.set(e,r)),r}s(z,"heldSet");function y(e,r){return e===null?!0:k(r)?z(e).has(r):$(e,r)}s(y,"holds");function S(e,r){return e.class==="person"?!0:y(e.resolved??[],r)&&y(e.nodeScopes,r)}s(S,"callerAllows");function pe(e,r,t){const n=`${r}:${t}`;if(!S(e,n))throw x(n)}s(pe,"requireGroup");function L(){return!!I("user").runtime?.issuer}s(L,"providerOperationsEnabled");const V=/^[A-Za-z0-9_-]{1,64}$/;function p(e,r){return new u("invalid_request",e,{param:r})}s(p,"invalid");function A(e){if(typeof e!="string"||!V.test(e)||e==="crtr")throw p("provider must be a provider id","provider");return e}s(A,"parseProvider");function Z(e){if(!e||typeof e!="object"||Array.isArray(e))throw new u("invalid_request","a JSON object is required");const r=e,t=A(r.provider),n=r.tool;if(typeof n!="string"||!n.trim())throw p("tool must be a non-empty string","tool");const i=r.arguments??{};if(!i||typeof i!="object"||Array.isArray(i))throw p("arguments must be an object","arguments");const a=r.version;if(a!==void 0&&(typeof a!="number"||!Number.isInteger(a)||a<1))throw p("version must be an integer >= 1","version");const o=r.approval;if(o!==void 0&&(typeof o!="string"||!o))throw p("approval must be a question id","approval");return{provider:t,tool:n.trim(),arguments:i,...a===void 0?{}:{version:a},...o===void 0?{}:{approval:o}}}s(Z,"parseCallInput");function B(e,r=new URLSearchParams){const t=s((o,f,c)=>{const l=r.get(o);if(l===null)return f;if(!/^\d+$/.test(l)||Number(l)>c||Number(l)<1)throw p(`invalid ${o}`,o);return Number(l)},"numeric"),n=r.has("after")?t("after",0,Number.MAX_SAFE_INTEGER):void 0,i=t("limit",100,1e3),a=Object.fromEntries(["operation","outcome","run_id","since","until"].filter(o=>r.has(o)).map(o=>[o,r.get(o)]));return{status:200,body:C(e.class==="person"?null:e.grantee,{after:n,limit:i,...a})}}s(B,"auditList");async function H(e,r,t){const n=performance.now(),i=new Set(j("user").filter(d=>d.enabled&&d.manifest.provider?.id===d.name).map(d=>d.name)),a=T(_()),o=[...new Set([...i,...a])].sort();if(r!==void 0&&!o.includes(r))throw new u("not_found",`provider "${r}" is not available`);const f=r===void 0?o:[r],c=e.class==="person"?_().flatMap(d=>d.status==="active"?d.resolved_scopes.map(({scope:v})=>v):[]):e.resolved??[],l=await Promise.all(f.map(async d=>{if(!c.some(m=>m.startsWith(`${d}:`)&&S(e,m)))return null;if(i.has(d)){const m=await N(e,d);return m.groups.length>0?m:null}const b=F(d);return{provider:d,display_name:d,status:b.status,...b.error?{error:b.error}:{},groups:[],commands:[]}})).then(d=>d.filter(v=>v!==null));if(g({level:"debug",event:"api.provider.tools",duration_ms:performance.now()-n,fields:{caller:e.class,provider:r??null,providers:l.length}}),r!==void 0){if(l.length===0)throw new u("not_found",`provider "${r}" is not available`);return{status:200,body:l[0]}}return{status:200,body:{providers:l}}}s(H,"providerTools");async function W(e,r,t){return G(e,r,t)}s(W,"providerCall");async function J(e,r,t,n){if(!L())throw new u("not_found","provider operations need a directory-connected runtime");if(e==="audit.list")return B(r,t.query);if(e==="provider.tools")return H(r,t.params.provider===void 0?void 0:A(t.params.provider),n);if(e==="provider.call"){let i;try{i=Z(t.body)}catch(a){const o=t.body&&typeof t.body=="object"&&!Array.isArray(t.body)?t.body:{};throw D({request_id:n.requestId,caller:r,provider:typeof o.provider=="string"?o.provider:"",tool:typeof o.tool=="string"?o.tool:"",outcome:`refused:${a instanceof u?a.code:"invalid_request"}`}),a}return W(r,i,n)}throw new u("not_found","unknown operation")}s(J,"handleProviderOperation");function X(e){const r=new AbortController,t=s(()=>{e.writableFinished||r.abort()},"abort");return e.on("close",t),{signal:r.signal,release:s(()=>e.off("close",t),"release")}}s(X,"abortOnClose");function K(e,r,t,n){const i=q(e,r,t,n??void 0);return i.status>=500&&!(e instanceof u)&&g({event:"api.provider.failed",level:"error",error:e instanceof Error?e:new Error(String(e)),fields:{request_id:r}}),i}s(K,"providerFailure");const me=[{method:"GET",pattern:"/v1/audit",name:"audit.list"},{method:"GET",pattern:"/v1/providers/tools",name:"provider.tools"},{method:"GET",pattern:"/v1/providers/:provider/tools",name:"provider.tools"},{method:"POST",pattern:"/v1/provider/call",name:"provider.call"}].map(({method:e,pattern:r,name:t})=>({method:e,pattern:r,handler:s(async n=>{const i=P(),{signal:a,release:o}=X(n.res);let f=null;try{return f=M(n.principal),{...await J(t,f,{params:n.params,body:n.body,query:new URL(n.req.url??"/","http://runtime.local").searchParams},{requestId:i,signal:a}),headers:{"Request-Id":i}}}catch(c){return K(c,i,!1,f?.run_id)}finally{o()}},"handler")}));export{fe as PROVIDER_OPERATIONS,X as abortOnClose,S as callerAllows,ce as callerFromApp,M as callerFromRequest,J as handleProviderOperation,Z as parseCallInput,A as parseProvider,K as providerFailure,L as providerOperationsEnabled,me as providerRoutes,pe as requireGroup};
|
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};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.444",
|
|
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.444",
|
|
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.444",
|
|
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.444",
|
|
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.444",
|
|
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.444",
|
|
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.444",
|
|
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.444",
|
|
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.444",
|
|
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.444",
|
|
12120
|
+
"@north-light/identity": "0.3.444"
|
|
12121
12121
|
}
|
|
12122
12122
|
},
|
|
12123
12123
|
"packages/crouter-sdk": {
|
|
12124
12124
|
"name": "@north-light/sdk",
|
|
12125
|
-
"version": "0.3.
|
|
12125
|
+
"version": "0.3.444",
|
|
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.444",
|
|
12129
|
+
"@north-light/identity": "0.3.444",
|
|
12130
12130
|
"jose": "^6.2.1"
|
|
12131
12131
|
},
|
|
12132
12132
|
"bin": {
|