@north-light/cli 0.3.592 → 0.3.594
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/api/dto/imports.d.ts +6 -0
- package/dist/cli.js +7 -7
- package/dist/commands/pkg/market-inspect.js +1 -1
- package/dist/commands/pkg/market-manage.js +1 -1
- package/dist/commands/pkg/plugin-inspect.js +1 -1
- package/dist/commands/pkg/plugin-manage.js +6 -6
- package/dist/core/canvas/canvas.js +1 -1
- package/dist/core/grants/remove.d.ts +4 -0
- package/dist/core/grants/remove.js +1 -1
- package/dist/core/import/load/prepare.js +5 -5
- package/dist/core/import/load/publish-plugins.d.ts +10 -0
- package/dist/core/import/load/publish-plugins.js +1 -0
- package/dist/core/import/load/run.js +2 -2
- package/dist/core/import/load/stores.d.ts +8 -1
- package/dist/core/import/load/stores.js +2 -2
- package/dist/core/packages/directory-sync.d.ts +2 -0
- package/dist/core/packages/directory-sync.js +3 -3
- package/dist/core/peer/home.d.ts +1 -1
- package/dist/core/peer/home.js +2 -2
- package/dist/core/runtime/runner/blaxel/runner.js +3 -3
- package/dist/daemon/api/handlers/imports.js +1 -1
- package/dist/daemon/crtrd.js +12 -12
- package/dist/daemon/reconcilers/grant-effects.js +1 -1
- package/dist/shared/env.d.ts +4 -0
- package/dist/shared/env.js +2 -2
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
- package/scripts/sandbox-bootstrap.sh +10 -4
- package/scripts/sandbox-start.mjs +54 -17
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var G=Object.defineProperty;var c=(e,i)=>G(e,"name",{value:i,configurable:!0});import{zstdCompressSync as z}from"node:zlib";import{blobStore as Q}from"../../blobs/index.js";import{openDb as E,withCanvasWrite as V}from"../../canvas/db.js";import{insertImportedRevisions as Y}from"../../graph/documents.js";import{getById as X}from"../../graph/objects.js";import{bundleKeys as S,decodeRows as Z,rowKeys as x,SEQ_TABLES as ee}from"../bundle/layout.js";import{readCatalog as te}from"./catalog.js";import{copyRows as ae,decodeKeys as N,encodeKeys as C}from"./copy.js";import{pruneStaged as ne,precheck as ie,renameClashingProfiles as se,rewritePaths as re,rewriteRequestPaths as oe,takeRepoSyncedDocuments as de}from"./prepare.js";import{boundSub as pe,checkSourcePerson as ue,checkStagedBlobKeys as ce,checkStagedPersons as fe,checkStagedReferences as le,checkStagedRequestKeys as me}from"./bind.js";import{deleteRunBlobs as _e,moveRunBlobs as we}from"./blobs.js";import{dropStubRows as ge,findFleetGrant as be,grantMissing as he,rewriteIds as ye,rewritePerson as Ee}from"./rewrite.js";import{dropStaging as W,ImportLaneError as w,stage as Se,stagingSchema as ve,targetSchema as Re}from"./stage.js";import{verifyRun as Oe}from"./verify.js";import{sandboxPathMap as Te,writeNodeFiles as ke}from"./path-map.js";import{allStoreKeys as Pe,readPluginsStore as je,writeStores as De}from"./stores.js";import{publishImportedPlugins as Ie}from"./publish-plugins.js";import{COPIED_TABLES as Ae}from"../bundle/layout.js";import{ClaimLostError as Me}from"../../../daemon/reconcilers/row-claim.js";const B=10*6e4,v=c(()=>new Date().toISOString(),"now"),R=c(async(e,i)=>{await(await E()).prepare("UPDATE import_runs SET step = ? WHERE run_id = ?").run(i,e)},"setStep");async function Ne(e,i){const n=await e.get(S.source(i));if(n===null)throw new w("stage","bundle_invalid","source.json is missing");return JSON.parse(n.toString("utf8"))}c(Ne,"readSource");async function Ce(e,i,n,r,s,d){const t=await e.get(S.rows(n.run_id,"grants")),f=t===null?[]:Z(t),l=[];for(const a of f){const p=pe(a.sub,r,n.person,"grants.sub");await be(i,s,d,p,a.grantee)===void 0&&l.push(a.grantee)}if(l.length===0)return!0;if(n.loaded_at===void 0||n.loaded_at===null)throw new w("stage","import_failed","a loading run has no loaded_at");if(Date.now()-Date.parse(n.loaded_at)>=B)throw he(l);return!1}c(Ce,"grantsReady");async function rt(e,i,n={}){const r=n.blobs??await Q(),s=await E(),d=ve(e.run_id),t=c(async a=>{await i.assertOwned(),await R(e.run_id,a),await n.atStep?.(a)},"at");let f=e.state==="committed";const l=[];try{const a=await Ne(r,e.run_id);if(e.source_kind==="machine"&&a.installId!==e.source_install_id)throw new w("stage","bundle_invalid",`source.json names install ${a.installId}, the run is for ${e.source_install_id}`);a.installId=e.source_install_id;const p=await Re(s),b=[...Ae],g=await te(s,p,[...b,"grants"]);if(!f){const m={},k=[],P=[],j=n.fleetRuntime??process.env.CRTR_RUNTIME??null;if(await ue(s,p,a.person,e.person),!await Ce(r,s,e,a.person,p,j)){await i.assertOwned(),await R(e.run_id,"waiting_for_grants");return}await t("publish-plugins");const y=await Ie(await je(r,e.run_id));if(y.unavailable.length>0){if(e.loaded_at===void 0||e.loaded_at===null)throw new w("publish-plugins","import_failed","a loading run has no loaded_at");if(Date.now()-Date.parse(e.loaded_at)>=B)throw new w("publish-plugins","directory_unavailable",`the directory did not answer for: ${y.unavailable.slice(0,10).join(", ")}; rerun the import once it is reachable`,{plugins:y.unavailable});await i.assertOwned(),await R(e.run_id,"waiting_for_directory");return}P.push(...y.refused),await t("stage");const H=await Se(s,e.run_id,a.counts,o=>x(e.run_id,o,a),o=>r.get(o));for(const[o,u]of Object.entries(H))m[o]={staged:u,inserted:0,mapped:0,dropped:0};const _={db:s,schema:d,target:p,catalog:g,sourcePerson:a.person,targetPerson:e.person,fleetRuntime:j,shippedBodies:a.shippedBodies??{}};await t("bind-staged");for(const[o,u]of Object.entries(await fe(_)))O(m,o,u);await ce(_),await t("rewrite-person"),await Ee(_),await t("prune");const K=await ne(s,d,g,a),D=a.kind==="hosted"?Te(a.pathPrefixes??[]):[];await t("rewrite-paths"),await re(s,d,D),await oe(s,d),await me(_),await se(s,d,p,a.installId,e.person),await t("rewrite-ids");const I=await ye(_);await t("drop");const U=await de(s,d,p),F=await ge(_);for(const[o,u]of Object.entries({...K}))O(m,o,u);for(const[o,u]of Object.entries(F))O(m,o,u);await le(_),await t("pre-check");const J=new Map([["messages",["seq"]],...ee.map(o=>[o,["seq"]])]);await ie(s,d,p,g,b,new Set,J),await t("blobs"),await we(s,d,r,e.run_id,l),await t("copy"),await V(async o=>{await i.assertOwned();const u=await ae({db:o,runId:e.run_id,schema:d,target:p,catalog:g,source:a,person:e.person,idMap:I,pathMap:D}),A=await Le(o,e.run_id,U);A>0&&(u.inserted.revisions=(u.inserted.revisions??0)+A),await ke(o,d,e.run_id,a);const M=await De({db:o,blobs:r,runId:e.run_id,person:e.person,objectMap:I.object});for(const[q,$]of Object.entries(M.inserted))u.inserted[q]=$.length;return k.push(...M.unresolvedPlugins),await o.prepare("UPDATE import_runs SET counts_json = ?, loaded_at = COALESCE(loaded_at, ?) WHERE run_id = ?").run(JSON.stringify({...Be(m,u),unresolved_plugins:k,refused_plugins:P}),v(),e.run_id),u}),f=!0,await n.atStep?.("committed")}await t("verify");const h=await Oe(s,e.run_id,d,p,g,a.installId);if(await s.prepare("UPDATE import_runs SET verify_json = ? WHERE run_id = ?").run(JSON.stringify(h),e.run_id),!h.ok)throw new w("verify","verify_failed","the committed rows differ from the staged rows or an invariant failed",{differences:h.differences,invariants:h.invariants});await t("manifest");const T=S.manifest(e.run_id);await r.put(T,await He(e.run_id)),await W(s,e.run_id);for(const m of Pe(e.run_id))await r.delete(m);await _e(r,e.run_id),await s.prepare("UPDATE import_runs SET state = 'done', step = 'done', manifest_key = ?, finished_at = ? WHERE run_id = ?").run(T,v(),e.run_id)}catch(a){if(a instanceof Me)throw a;const p=We(a);if(await W(s,e.run_id).catch(()=>{}),!f)for(const b of l)await r.delete(b).catch(()=>{});await s.prepare("UPDATE import_runs SET state = 'failed', failure_json = ?, finished_at = ? WHERE run_id = ?").run(JSON.stringify(p),v(),e.run_id)}}c(rt,"processRun");function We(e){if(e instanceof w)return{step:e.step,code:e.code,message:e.message,...e.detail===void 0?{}:{detail:e.detail}};const i=e.code;return{step:"unknown",code:typeof i=="string"?i:"import_failed",message:e instanceof Error?e.message:String(e)}}c(We,"failureOf");function O(e,i,n){(e[i]??={staged:0,inserted:0,mapped:0,dropped:0}).dropped+=n}c(O,"addDropped");function Be(e,i){for(const[n,r]of Object.entries(i.inserted))(e[n]??={staged:0,inserted:0,mapped:0,dropped:0}).inserted=r;for(const[n,r]of Object.entries(i.mapped))(e[n]??={staged:0,inserted:0,mapped:0,dropped:0}).mapped=r;return e}c(Be,"withInserted");async function Le(e,i,n){const r=new Set((await e.prepareUncached("SELECT rev_id FROM revisions WHERE rev_id IN (SELECT jsonb_array_elements_text(?::jsonb))").all(JSON.stringify(n.map(t=>t.rev_id)))).map(t=>t.rev_id)),s=n.filter(t=>!r.has(t.rev_id)),d=new Map;for(const t of n)d.set(t.document,[...d.get(t.document)??[],t]);for(const[t,f]of d){const l=await X(e,t);l!==null&&await Y(e,l,f.map(a=>({rev_id:a.rev_id,parents:a.parents,body_hash:a.body_hash,rationale:a.rationale,created_at:a.created_at})))}return await L(e,i,"revisions",s.map(t=>[t.rev_id])),await L(e,i,"revision_parents",s.flatMap(t=>t.parents.map(f=>[t.rev_id,f]))),s.length}c(Le,"arrive");async function L(e,i,n,r){if(r.length===0)return;const s=await e.prepare("SELECT inserted_keys FROM import_manifest WHERE run_id = ? AND table_name = ?").get(i,n);s===void 0?await e.prepare("INSERT INTO import_manifest (run_id, table_name, inserted_keys) VALUES (?, ?, ?)").run(i,n,C(r)):await e.prepare("UPDATE import_manifest SET inserted_keys = ? WHERE run_id = ? AND table_name = ?").run(C([...N(s.inserted_keys),...r]),i,n)}c(L,"appendManifest");async function He(e){const n=await(await E()).prepare("SELECT table_name, inserted_keys FROM import_manifest WHERE run_id = ? ORDER BY table_name").all(e);return z(Buffer.from(n.map(r=>JSON.stringify({table:r.table_name,keys:N(r.inserted_keys)})).join(`
|
|
2
2
|
`)+`
|
|
3
|
-
`))}
|
|
3
|
+
`))}c(He,"manifestFile");export{B as GRANT_WAIT_MS,rt as processRun};
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import type { BlobStore } from '../../blobs/index.js';
|
|
2
2
|
import type { CanvasDb } from '../../canvas/db.js';
|
|
3
|
-
import type { ImportStoreNameDTO } from '../../../api/dto/imports.js';
|
|
3
|
+
import type { ImportPluginsStoreDTO, ImportStoreNameDTO } from '../../../api/dto/imports.js';
|
|
4
4
|
export declare const STORE_NAMES: readonly ImportStoreNameDTO[];
|
|
5
5
|
export declare const storeKey: (run: string, name: ImportStoreNameDTO) => string;
|
|
6
6
|
/** Seal one uploaded body under the target key and store it. The plaintext lives only in this call. */
|
|
7
7
|
export declare function putStore(blobs: BlobStore, run: string, name: ImportStoreNameDTO, body: unknown): Promise<void>;
|
|
8
8
|
export declare class StoreBodyError extends Error {
|
|
9
9
|
}
|
|
10
|
+
/** The uploaded plugins store, or null when the run has none. */
|
|
11
|
+
export declare const readPluginsStore: (blobs: BlobStore, run: string) => Promise<ImportPluginsStoreDTO | null>;
|
|
12
|
+
/** Whether the fleet can reinstall a plugin row: it names a marketplace. A row with a path only, or with neither (a Mac git-URL or HTTP-endpoint install, a provider
|
|
13
|
+
* bundle row), has no source the fleet can fetch; it is reported, not installed. Provider bundles reach the fleet through grants. */
|
|
14
|
+
export declare const hasFleetSource: (p: {
|
|
15
|
+
source_marketplace?: string | null;
|
|
16
|
+
}) => boolean;
|
|
10
17
|
/** Every store's blob key under a run, for deletion once the run is done. */
|
|
11
18
|
export declare const allStoreKeys: (run: string) => string[];
|
|
12
19
|
export interface StoreInput {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var U=Object.defineProperty;var
|
|
2
|
-
`),a]))}
|
|
1
|
+
var U=Object.defineProperty;var l=(o,r)=>U(o,"name",{value:r,configurable:!0});import{loadSecretsKey as y,open as C,seal as k}from"../../secrets/index.js";import{insertAccount as j,upsertModelCooldown as D}from"../../subscription-state.js";import{validateEnvVarName as F,isReservedEnvName as V}from"../../env-name.js";import{encodeKeys as x}from"./copy.js";import{compareInstallOwners as M}from"../../scoped-state/migrate-app-installs.js";const W=["auth","credentials","env","settings","plugins"],h=l((o,r)=>`imports/${o}/stores/${r}.sealed`,"storeKey"),$=l((o,r)=>`import_store|${o}|${r}`,"aadOf");async function z(o,r,d,i){if(i===null||typeof i!="object"||Array.isArray(i))throw new K(`the ${d} store must be a JSON object`);const{sealed:a,keyId:m}=k(JSON.stringify(i),y(),$(r,d));await o.put(h(r,d),Buffer.concat([Buffer.from(`${m}
|
|
2
|
+
`),a]))}l(z,"putStore");class K extends Error{static{l(this,"StoreBodyError")}}async function S(o,r,d){const i=await o.get(h(r,d));if(i===null)return null;const a=i.indexOf(10);return JSON.parse(C(i.subarray(a+1),i.subarray(0,a).toString("utf8"),y(),$(r,d)))}l(S,"readStore");const Q=l((o,r)=>S(o,r,"plugins"),"readPluginsStore"),P=l(o=>o.source_marketplace!=null,"hasFleetSource"),Y=l(o=>W.map(r=>h(o,r)),"allStoreKeys");async function Z(o){const{db:r,blobs:d,runId:i,person:a}=o,m=l(e=>o.objectMap.get(e)??e,"mapId"),w=l(e=>e.startsWith("profile:")?`profile:${m(e.slice(8))}`:e,"mapOwner"),E={},T=[],N=l((e,n)=>{(E[e]??=[]).push(n)},"record"),f=l(async(e,n,s,p)=>{await r.prepareUncached(`${n} ON CONFLICT DO NOTHING RETURNING 1 AS ok`).get(...s)!==void 0&&N(e,p)},"insert"),b=y(),A=new Date().toISOString(),u=await S(d,i,"credentials");if(u!==null){for(const e of u.accounts)if(await r.prepare("SELECT 1 FROM model_accounts WHERE person = ? AND provider_id = ? AND (id = ? OR label_key = ?)").get(a,e.provider,e.id,e.labelKey)===void 0){await j(r,a,e.provider,{providerId:e.provider,id:e.id,label:e.label,labelKey:e.labelKey,position:e.position,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}},{refresh:e.refresh,access:e.access,expires:e.expires,...e.accountId?{accountId:e.accountId}:{},...e.clientId?{clientId:e.clientId}:{}}),N("model_accounts",[a,e.provider,e.id]);for(const s of e.cooldowns)await D(r,a,e.provider,e.id,s.family,s.until,s.at),N("model_account_cooldowns",[a,e.provider,e.id,s.family])}u.rotation!==void 0&&await f("model_settings","INSERT INTO model_settings (person, default_fallback_strength, revert_when_available) VALUES (?, ?, ?)",[a,u.rotation.defaultFallbackStrength,u.rotation.revertWhenAvailable===null?null:u.rotation.revertWhenAvailable?1:0],[a]),u.openaiDeviceId!==void 0&&/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(u.openaiDeviceId)&&await r.prepare("INSERT INTO model_settings (person, openai_device_id) VALUES (?, ?) ON CONFLICT (person) DO UPDATE SET openai_device_id = COALESCE(model_settings.openai_device_id, EXCLUDED.openai_device_id)").run(a,u.openaiDeviceId.toLowerCase())}const L=await S(d,i,"auth");if(L!==null)for(const[e,n]of Object.entries(L.apiKeys)){const{sealed:s,keyId:p}=k(JSON.stringify(n),b,`model_api_keys|${a}|${e}`);await f("model_api_keys","INSERT INTO model_api_keys (person, provider_id, credential_sealed, key_id, updated_at) VALUES (?, ?, ?, ?, ?)",[a,e,s,p,A],[a,e])}const R=await S(d,i,"env");if(R!==null)for(const[e,n]of Object.entries(R.profiles)){const s=m(e);for(const[p,t]of Object.entries(n)){if(!F(p).isValid||V(p)||typeof t!="string"||t.includes("\0"))continue;const{sealed:c,keyId:_}=k(t,b,`env_vars|${a}|profile|${s}|${p}`);await f("env_vars","INSERT INTO env_vars (person, owner_kind, owner_id, name, value_sealed, key_id, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)",[a,"profile",s,p,c,_,A],[a,"profile",s,p])}}const O=await S(d,i,"settings");if(O!==null){for(const[e,n]of Object.entries(O.settings))await f("scope_settings","INSERT INTO scope_settings (person, owner_key, raw_json) VALUES (?, ?, ?)",[a,w(e),JSON.stringify(n)],[a,w(e)]);for(const[e,n]of Object.entries(O.state))await f("scope_bookkeeping","INSERT INTO scope_bookkeeping (person, owner_key, last_self_check, active_canvas) VALUES (?, ?, ?, ?)",[a,w(e),n.last_self_check??null,n.activeCanvas??null],[a,w(e)]);for(const[e,n]of Object.entries(O.profileProjects)){const s=m(e);if(await r.prepare("SELECT 1 FROM profile_projects WHERE profile_id = ?").get(s)!==void 0)continue;let p=0;for(const t of n)await f("profile_projects","INSERT INTO profile_projects (profile_id, path, position) VALUES (?, ?, ?)",[s,t,p++],[s,t])}for(const[e,n]of Object.entries(O.defaults))await f("profile_defaults","INSERT INTO profile_defaults (person, directory, profile_id) VALUES (?, ?, ?)",[a,e,m(n)],[a,e])}const v=await S(d,i,"plugins");if(v!==null){const e=l(async t=>{if(t.startsWith("app:"))return t;if(t==="user")return"app:terminal";if(!t.startsWith("profile:"))throw new Error(`unsupported install owner in the bundle: ${t}`);return(await r.prepare("SELECT grantee FROM profiles WHERE id = ?").get(m(t.slice(8))))?.grantee??"app:terminal"},"appOf"),n=new Map;for(const t of new Set([...v.marketplaces,...v.plugins].map(c=>c.owner)))t.startsWith("profile:")&&n.set(t,(await r.prepare("SELECT last_used_at FROM profiles WHERE id = ?").get(m(t.slice(8))))?.last_used_at??"");const s=l(t=>[...t].sort((c,_)=>M(w(c.owner),n.get(c.owner)??"",w(_.owner),n.get(_.owner)??"")),"byProfileFirst"),p=new Map;for(const t of s(v.marketplaces)){const c=await e(t.owner),_=new Map((await r.prepare("SELECT name, url FROM scope_marketplaces WHERE person = ? AND owner_key = ?").all(a,c)).map(I=>[I.name,I.url]));let g=t.name;for(let I=2;_.has(g)&&_.get(g)!==(t.url??null);I+=1)g=`${t.name}-${I}`;p.set(`${t.owner}\0${t.name}`,g),await f("scope_marketplaces","INSERT INTO scope_marketplaces (person, owner_key, name, last_updated, url, ref, installed_at) VALUES (?, ?, ?, ?, ?, ?, ?)",[a,c,g,t.last_updated??null,t.url??null,t.ref??null,t.installed_at??null],[a,c,g])}for(const t of s(v.plugins)){if(!P(t)){T.push(`${t.owner}:${t.name}`);continue}const c=await e(t.owner),_=t.source_marketplace==null?null:p.get(`${t.owner}\0${t.source_marketplace}`)??t.source_marketplace;await f("scope_plugins","INSERT INTO scope_plugins (person, owner_key, name, last_updated, etag, checked_at, enabled, source_marketplace, version, source_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",[a,c,t.name,t.last_updated??null,t.etag??null,t.checked_at??null,t.enabled?1:0,_,t.version??null,t.source_path??null],[a,c,t.name])}(E.scope_plugins??[]).length+(E.scope_marketplaces??[]).length>0&&await f("person_settings","INSERT INTO person_settings (person, plugins_revision) VALUES (?, 1)",[a],[a])}for(const[e,n]of Object.entries(E))await r.prepare("INSERT INTO import_manifest (run_id, table_name, inserted_keys) VALUES (?, ?, ?) ON CONFLICT (run_id, table_name) DO UPDATE SET inserted_keys = EXCLUDED.inserted_keys").run(i,e,x(n));return Object.keys(E).length>0&&await r.prepare("SELECT pg_notify('crtr_signal', ?)").get(`settings:${a}`),{inserted:E,unresolvedPlugins:T}}l(Z,"writeStores");export{W as STORE_NAMES,K as StoreBodyError,Y as allStoreKeys,P as hasFleetSource,z as putStore,Q as readPluginsStore,h as storeKey,Z as writeStores};
|
|
@@ -37,5 +37,7 @@ export declare function syncInstall(name: string, url: string): Promise<{
|
|
|
37
37
|
out: FetchOutcome;
|
|
38
38
|
published?: PublishReport;
|
|
39
39
|
}>;
|
|
40
|
+
/** Callers with no one to answer (the timer, the import lane): files the publisher could not read and kinds it refused are events. */
|
|
41
|
+
export declare function reportPublished(name: string, r: PublishReport): void;
|
|
40
42
|
/** One package-sync pass. Called after each grant sync on the api role. */
|
|
41
43
|
export declare function syncPackages(): Promise<void>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var S=Object.defineProperty;var i=(e,n)=>S(e,"name",{value:n,configurable:!0});import{mkdtempSync as A,mkdirSync as I,rmSync as N,writeFileSync as $}from"node:fs";import{tmpdir as
|
|
1
|
+
var S=Object.defineProperty;var i=(e,n)=>S(e,"name",{value:n,configurable:!0});import{mkdtempSync as A,mkdirSync as I,rmSync as N,writeFileSync as $}from"node:fs";import{tmpdir as x}from"node:os";import{dirname as O,join as m}from"node:path";import{withCanvasWrite as w,openDb as T}from"../canvas/db.js";import{emitEvent as p}from"../events/emit.js";import{outboundFetch as P}from"../events/outbound.js";import{directoryIssuer as g}from"../directory/issuer.js";import{allGrants as j,providerIdsFromGrants as D}from"../grants/mirror.js";import{mintDirectoryToken as M}from"../grants/sync.js";import{publish as R,readPackageSnapshot as F,withdraw as W}from"../graph/package-publish.js";import{normalizeSourceUrl as k}from"./source-url.js";const L=25e3,C=15e3;async function h(e,n){const o=g();if(o===null)return{ok:!1,reason:"unavailable"};try{const t=await M(o),r=await P("package_sync",`${o}${e}`,{method:n.method,headers:{Authorization:`Bearer ${t.access_token}`,...n.body===void 0?{}:{"content-type":"application/json"},...n.etag===void 0?{}:{"If-None-Match":`"${n.etag}"`}},...n.body===void 0?{}:{body:JSON.stringify(n.body)},signal:AbortSignal.timeout(n.wait)});if(r.status===304)return{ok:!0,notModified:!0};if(r.status===404)return{ok:!1,reason:"not_found"};if(r.status===409){const l=await r.json().catch(()=>({}));return(l.error?.code??l.code)==="name_taken"?{ok:!1,reason:"name_taken",holder:l.error?.details?.holder_source??l.error?.holder_source??l.holder_source}:{ok:!1,reason:"not_public"}}return r.ok?{ok:!0,pkg:await r.json()}:{ok:!1,reason:"unavailable"}}catch(t){return p({level:"warn",event:"daemon.packages.fetch_failed",fields:{path:e},error:t}),{ok:!1,reason:"unavailable"}}}i(h,"call");const y=i((e,n)=>h(`/v1/packages/${encodeURIComponent(e)}`,{method:"GET",etag:n,wait:C}),"getPackage"),v=i((e,n)=>h("/v1/packages/resolve",{method:"POST",body:{source_url:e,name:n},wait:L}),"resolvePackage");async function _(e){const n=A(m(x(),"crtr-pkg-"));try{for(const t of e.files){const r=m(n,t.path);if(!r.startsWith(`${n}/`))throw new Error(`package file escapes its folder: ${t.path}`);I(O(r),{recursive:!0}),$(r,t.text)}const o={...F(n,{app:e.name,origin:e.origin,version:e.version,revision:e.revision,source:e.source===null?null:k(e.source),launchKinds:e.kinds,objectKinds:e.object_kinds}),digest:e.digest};return await w(async t=>await R(t,o,"package-sync"))}finally{N(n,{recursive:!0,force:!0})}}i(_,"publishDirectoryPackage");async function G(e){return await e.prepare(`SELECT p.name, m.url FROM scope_plugins p LEFT JOIN scope_marketplaces m
|
|
2
2
|
ON m.person = p.person AND m.owner_key = p.owner_key AND m.name = p.source_marketplace
|
|
3
3
|
WHERE p.owner_key LIKE 'app:%' AND p.enabled = 1`).all()}i(G,"wantedPlugins");async function J(e){return(await e.prepare(`SELECT i.external_id, p.origin, p.source, p.digest, p.withdrawn_at FROM package_publications p
|
|
4
|
-
JOIN identities i ON i.object = p.app AND i.scheme = 'app' WHERE p.origin <> 'builtin'`).all()).map(o=>({name:o.external_id.replace(/^app:/,""),origin:o.origin,source:o.source,digest:o.digest,withdrawn:o.withdrawn_at!==null}))}i(J,"publications");async function
|
|
4
|
+
JOIN identities i ON i.object = p.app AND i.scheme = 'app' WHERE p.origin <> 'builtin'`).all()).map(o=>({name:o.external_id.replace(/^app:/,""),origin:o.origin,source:o.source,digest:o.digest,withdrawn:o.withdrawn_at!==null}))}i(J,"publications");async function oe(e,n){const o=await v(n,e);return"pkg"in o?{out:o,published:await _(o.pkg)}:{out:o}}i(oe,"syncInstall");function K(e,n){for(const o of n.invalid)p({level:"error",event:"daemon.packages.invalid_file",fields:{app:e,path:o.path,error:o.error}});for(const o of n.kinds.refused)p({level:"warn",event:"daemon.packages.kind_refused",fields:{app:e,kind:o.kind,message:o.message}})}i(K,"reportPublished");async function d(e){K(e.name,await _(e))}i(d,"publishAndReport");const b=new Set;function E(e,n,o){if(o.ok)return;const t=`${e}
|
|
5
5
|
${n}
|
|
6
|
-
${o.reason}`;b.has(t)||(b.add(t),o.reason!=="unavailable"&&
|
|
6
|
+
${o.reason}`;b.has(t)||(b.add(t),o.reason!=="unavailable"&&p({level:"warn",event:"daemon.packages.resolve_refused",fields:{name:e,source:n??"",reason:o.reason}}))}i(E,"refused");async function ae(){if(g()===null)return;const e=await T(),n=await j(),o=D(n),t=await G(e),r=new Map((await J(e)).map(a=>[a.name,a])),l=new Set([...o,...t.map(a=>a.name)]);for(const a of t){const s=r.get(a.name),c=a.url===null?null:k(a.url);if(c!==null&&(s===void 0||s.withdrawn||s.source!==c)){const u=await v(c,a.name);"pkg"in u?await d(u.pkg):E(a.name,c,u)}}for(const a of l){const s=r.get(a);if(s===void 0||s.withdrawn){if(o.includes(a)){const u=await y(a);"pkg"in u?await d(u.pkg):E(a,null,u)}continue}const c=await y(a,s.digest);"pkg"in c&&await d(c.pkg)}const f=n.some(a=>a.status==="active");for(const[a,s]of r)s.withdrawn||l.has(a)||s.origin==="provider"&&!f||await w(async c=>{await W(c,a)})}i(ae,"syncPackages");export{y as getPackage,_ as publishDirectoryPackage,K as reportPublished,v as resolvePackage,oe as syncInstall,ae as syncPackages};
|
package/dist/core/peer/home.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export interface HomeDeps {
|
|
|
22
22
|
machines(): Promise<Machine[]>;
|
|
23
23
|
self(): string;
|
|
24
24
|
now(): number;
|
|
25
|
-
/** Whether this daemon
|
|
25
|
+
/** Whether this daemon has peer machines to ask; one without them has none, so nothing is ever "elsewhere". */
|
|
26
26
|
joined(): boolean;
|
|
27
27
|
}
|
|
28
28
|
export declare const defaultHomeDeps: HomeDeps;
|
package/dist/core/peer/home.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var d=Object.defineProperty;var
|
|
2
|
-
ON CONFLICT(id) DO UPDATE SET machine_id = excluded.machine_id, learned_at = excluded.learned_at`).run(n,r,i)}
|
|
1
|
+
var d=Object.defineProperty;var o=(e,n)=>d(e,"name",{value:n,configurable:!0});import{getById as p}from"../graph/objects.js";import{isUnreachable as _,peerCall as y}from"./client.js";import{listMachines as E,normalizeMachineId as w,ownMachineId as x}from"./directory.js";import{directoryIssuer as b}from"../directory/issuer.js";import{isCloudRole as O}from"../canvas/person-state.js";import{stubOf as T}from"./stubs.js";const l={call:o((e,n)=>y(e,n),"call"),machines:E,self:x,now:Date.now,joined:o(()=>!O()&&b()!==null,"joined")};async function I(e,n){return(await e.prepare("SELECT machine_id FROM peer_locations WHERE id = ?").get(n))?.machine_id??null}o(I,"cachedLocation");async function L(e,n,r,i=Date.now()){await e.prepare(`INSERT INTO peer_locations (id, machine_id, learned_at) VALUES (?, ?, ?)
|
|
2
|
+
ON CONFLICT(id) DO UPDATE SET machine_id = excluded.machine_id, learned_at = excluded.learned_at`).run(n,r,i)}o(L,"rememberLocation");async function g(e,n){await e.prepare("DELETE FROM peer_locations WHERE id = ?").run(n)}o(g,"dropLocation");async function D(e,n){if(!e.joined())return[];let r;try{r=await e.machines()}catch(a){return{where:"unknown",unreachable:[`directory (${a instanceof Error?a.message:String(a)})`]}}const i=e.self();return r.filter(a=>a.machine_id!==i&&a.machine_id!==n&&a.kind!=="device")}o(D,"peersToAsk");async function f(e,n,r=l,i){const a=await D(r,i);if("where"in a)return a;const c=await Promise.all(a.map(async t=>{const h=await r.call(t.machine_id,{method:"POST",path:"/v1/peer/locate",body:{id:n}});if(_(h))return{m:t,state:"unreachable"};const s=h.body;return h.status===200&&s?.answer==="here"?{m:t,state:"here",machine:t.machine_id}:h.status===200&&s?.answer==="moved"&&typeof s.machine=="string"?{m:t,state:"here",machine:w(s.machine)}:h.status===200&&s?.answer==="no"?{m:t,state:"no"}:{m:t,state:"unreachable"}})),u=c.find(t=>t.state==="here");if(u&&"machine"in u)return await L(e,n,u.machine,r.now()),{where:"machine",machine:u.machine,source:"locate"};const m=c.filter(t=>t.state==="unreachable").map(t=>t.m.name||t.m.machine_id);return m.length>0?{where:"unknown",unreachable:m}:{where:"nowhere"}}o(f,"locate");async function v(e,n,r={}){const i=await p(e,n);if(i!==null){const c=await T(e,i);return c===null?{where:"here"}:{where:"machine",machine:c.home_machine,source:"stub"}}if(!(r.deps??l).joined())return{where:"nowhere"};if(r.hint)return{where:"machine",machine:w(r.hint),source:"hint"};const a=await I(e,n);return a!==null?{where:"machine",machine:a,source:"cache"}:f(e,n,r.deps)}o(v,"homeOf");async function A(e,n,r,i=l){return await g(e,n),f(e,n,i,r)}o(A,"relocate");export{I as cachedLocation,l as defaultHomeDeps,g as dropLocation,v as homeOf,f as locate,D as peersToAsk,A as relocate,L as rememberLocation};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
${
|
|
3
|
-
${
|
|
1
|
+
var $=Object.defineProperty;var i=(r,a)=>$(r,"name",{value:a,configurable:!0});import{openDb as k}from"../../../canvas/db.js";import{mintExecutionToken as I}from"../../../canvas/node-tokens.js";import{BlaxelApiError as T,isWorkloadUnavailable as f}from"./client.js";const x=4180,u="view",S="node /opt/crtr/current/scripts/sandbox-start.mjs --wake",C=3,y=/^crtr-sandbox-start: failed (\S+)/m,m=i(r=>`broker-${r}`,"brokerProcessName"),p=i(r=>r.sandboxName??`${r.nodeId}-${r.attempt}`,"sandboxNameFor");async function N(r){return(await(await k()).prepare("SELECT DISTINCT sandbox_name FROM executions WHERE node_id = ? AND sandbox_name IS NOT NULL").all(r)).map(l=>l.sandbox_name)}i(N,"defaultSandboxNames");function h(r){const a=`${r.logs}
|
|
2
|
+
${r.stderr}
|
|
3
|
+
${r.stdout}`;return{state:"failed",reason:y.exec(a)?.[1]??"start_failed",code:r.exitCode}}i(h,"failureOf");function v(r){const{client:a}=r,l=(r.stopGraceS??10)*1e3,E=new Map;async function d(t){return await a.getSandbox(p(t))}i(d,"sandboxOf");async function _(t,n){if(!/^[A-Za-z0-9_-]+$/.test(n))throw new Error(`execution id ${JSON.stringify(n)} is not safe to name in a command`);const e=Math.max(1,Math.ceil(l/1e3)),o=`p='[b]roker-cli\\.js .*--epoch ${n}( |$)'; pkill -TERM -f "$p"; i=0; while pgrep -f "$p" >/dev/null && [ $i -lt ${e*5} ]; do sleep 0.2; i=$((i+1)); done; pkill -KILL -f "$p"; sleep 0.3; ! pgrep -f "$p"`;return(await a.exec(t,`stop-${n}-${Date.now()}`,o,e+20)).exitCode===0}return i(_,"brokerGone"),{kind:"blaxel",async start(t){const n=await r.launchContext(t),e=p(t),o=await a.createSandbox({name:e,image:n.image,memoryMb:n.memoryMb,ports:[{name:u,target:x}]}),s=m(t.executionId);if((await a.getProcess(o,s))?.status==="running")return{pid:null,exited:null};await a.ensurePreview(e,u,x);const w=await(r.mintToken??(b=>I(b.executionId,b.nodeId)))(t),R={...n.secrets,CRTR_API_URL:n.apiUrl,CRTR_EXECUTION_ID:t.executionId,CRTR_NODE_TOKEN:w,CRTR_RELEASE:t.release,CRTR_RELEASE_URL:n.releaseUrl,CRTR_RELEASE_SHA256:n.releaseSha256};return await a.startProcess(o,{name:s,command:r.bootstrap(),env:R,waitForCompletion:!1}),{pid:null,exited:null}},async wake(t){const n=await d(t);if(n===null)return"no_broker";try{const e=await a.exec(n,`wake-${t.executionId}-${Date.now()}`,`${S} ${t.executionId}`,30);if(e.exitCode===0)return"signalled";if(e.exitCode===C)return"no_broker";throw new Error(`wake of ${t.executionId} failed (exit ${e.exitCode}): ${(e.stderr||e.logs).slice(0,300)}`)}catch(e){if(f(e))return"no_broker";throw e}},async stop(t,n){try{const e=await d(t);if(e===null)return{confirmed:!0};const o=m(t.executionId),s=i(async()=>(await a.getProcess(e,o))?.status==="running","running");if(await s()){await a.stopProcess(e,o);const c=Date.now()+l;for(;Date.now()<c&&await s();)await new Promise(w=>setTimeout(w,250));if(await s()&&await a.killProcess(e,o),await s())return{confirmed:!1}}return{confirmed:await _(e,t.executionId)}}catch(e){return f(e)?{confirmed:!0}:{confirmed:!1,error:e instanceof Error?e:new Error(String(e))}}},async status(t,n){try{const e=await d(t);if(e===null)return{state:"gone"};if(n?.idle===!0)return{state:"running",pid:null};const o=await a.getProcess(e,m(t.executionId));return o===null?{state:"gone"}:o.status==="running"?{state:"running",pid:null}:o.status==="failed"||o.status==="completed"&&o.exitCode!==0?h(o):{state:"gone"}}catch(e){return f(e)?{state:"gone"}:{state:"unknown",error:e instanceof Error?e.message:String(e)}}},async delete(t){for(const n of await(r.sandboxNamesForNode??N)(t))await a.deleteSandbox(n)},async viewerAddress(t){const n=p(t),e=E.get(n);if(e&&e.expiresAt-Date.now()>5*6e4)return g(e.url,e.token);const o=await a.ensurePreview(n,u,x),s=new Date(Date.now()+60*6e4),{token:c}=await a.createPreviewToken(n,u,s);return E.set(n,{url:o,token:c,expiresAt:s.getTime()}),g(o,c)},async sandboxState(t){return(await d(t))?.state??null}}}i(v,"createBlaxelRunner");function g(r,a){return{kind:"wss",url:`${r.replace(/^https:/,"wss:")}/view`,headers:{"x-blaxel-preview-token":a}}}i(g,"viewer");export{x as BROKER_VIEW_PORT,T as BlaxelApiError,C as EXIT_NO_BROKER,y as FAILURE_LINE,S as WAKE_COMMAND,m as brokerProcessName,v as createBlaxelRunner,p as sandboxNameFor};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var k=Object.defineProperty;var a=(t,n)=>k(t,"name",{value:n,configurable:!0});import{randomBytes as E}from"node:crypto";import{ApiError as o}from"../../../api/errors.js";import{blobStore as
|
|
1
|
+
var k=Object.defineProperty;var a=(t,n)=>k(t,"name",{value:n,configurable:!0});import{randomBytes as E}from"node:crypto";import{ApiError as o}from"../../../api/errors.js";import{blobStore as f}from"../../../core/blobs/index.js";import{presignPutUrl as $,r2ConfigFromEnv as v}from"../../../core/blobs/r2.js";import{openDb as c}from"../../../core/canvas/db.js";import{bundleKeys as m}from"../../../core/import/bundle/layout.js";import{putStore as O,StoreBodyError as T,STORE_NAMES as R}from"../../../core/import/load/stores.js";import{rollbackRun as D}from"../../../core/import/load/rollback.js";import{crtrVersion as w}from"../../../shared/crtr-version.js";import{importTokenFor as N}from"../import-auth.js";const h=3600;function y(t){if(t.principal.class!=="person"||t.principal.sub===void 0)throw new o(403,"person_only","an import is made by a signed-in person");return t.principal.sub}a(y,"personOf");async function _(t,n=!1){const e=t.params.run_id??"",r=await(await c()).prepare("SELECT * FROM import_runs WHERE run_id = ?").get(e),s=t.principal;if(s.class==="import"){if(s.run===null&&!n)throw new o(403,"permission_error","the service credential starts and reads runs; the import token uploads and loads");if(r===void 0||(s.run===null?r.source_kind!=="hosted":s.run!==r.run_id))throw new o(404,"import_not_found",`no import run ${e}`);if(s.run!==null&&!n&&r.state!=="uploading"&&r.state!=="loading")throw new o(403,"import_token_expired",`import run ${r.run_id} is ${r.state}: its import token no longer works`);return r}if(r===void 0||r.person!==y(t))throw new o(404,"import_not_found",`no import run ${e}`);return r}a(_,"ownRun");function A(t,n,e){const r=t.principal;if(r.class!=="import")return y(t);if(r.run!==null)throw new o(403,"permission_error","an import token reaches only its own run");const s=n.person;if(typeof s!="string"||!/^user:.+/.test(s))throw new o(400,"invalid_body","person (user:<id>) is required");if(e!=="hosted")throw new o(400,"invalid_body","the service credential starts hosted runs only: source_kind must be hosted");return s}a(A,"startPerson");const g=a(t=>{if(t===null||typeof t!="object"||Array.isArray(t))throw new o(400,"invalid_body","the body must be a JSON object");return t},"objectBody");async function j(t){const n=g(t.body),e=A(t,n,n.source_kind),r=n.source_install_id,s=n.source_kind,u=n.release;if(typeof r!="string"||r===""||s!=="machine"&&s!=="hosted"||typeof u!="string")throw new o(400,"invalid_body","source_install_id, source_kind (machine | hosted) and release are required");if(u!==w())throw new o(409,"release_mismatch",`the source runs ${u}, the control plane accepts ${w()}`);const p=await c();let i=await p.prepare("SELECT run_id, state FROM import_runs WHERE source_install_id = ? AND state IN ('uploading', 'loading', 'committed', 'done')").get(r);if(i?.state==="uploading"){const S={step:"uploading",code:"superseded",message:`a new import run was started for install ${r} before this one asked for the load`};await p.prepare("UPDATE import_runs SET state = 'failed', failure_json = ?, finished_at = ? WHERE run_id = ? AND state = 'uploading'").run(JSON.stringify(S),new Date().toISOString(),i.run_id),i=await p.prepare("SELECT run_id, state FROM import_runs WHERE source_install_id = ? AND state IN ('uploading', 'loading', 'committed', 'done')").get(r)}if(i?.state==="done")throw new o(409,"already_imported",`install ${r} was imported by run ${i.run_id}`);if(i!==void 0)throw new o(409,"import_running",`install ${r} has an open run ${i.run_id} (${i.state})`);const d=`imp_${E(8).toString("hex")}`;await p.prepare("INSERT INTO import_runs (run_id, source_install_id, source_kind, person, release, state, created_at) VALUES (?, ?, ?, ?, ?, 'uploading', ?)").run(d,r,s,e,u,new Date().toISOString());const l=t.principal.class==="import"?N(d):null;return{status:201,body:{run_id:d,upload:{rows_prefix:m.prefix(d),url_template:`POST /v1/imports/${d}/upload-urls {"keys":[...]}`},...l===null?{}:{import_token:l}}}}a(j,"handleStart");async function P(t){const n=await _(t);if(n.state!=="uploading")throw new o(409,"import_not_uploading",`import run ${n.run_id} is ${n.state}`);const e=g(t.body).keys;if(!Array.isArray(e)||e.some(i=>typeof i!="string"))throw new o(400,"invalid_body","keys must be a list of blob keys");const r=m.prefix(n.run_id);for(const i of e)if(!(i.startsWith(r)&&!i.startsWith(`${r}stores/`)&&i!==m.manifest(n.run_id))||i.split("/").some(l=>l===""||l==="."||l===".."))throw new o(400,"invalid_key",`${i} is not a key this run may upload`);const s=v();if(s===null)throw new o(501,"upload_not_supported","this control plane keeps blobs in a directory: there is no URL to upload to");const u={};for(const i of e)u[i]=$(s,i,h);return{status:200,body:{urls:u,expires_at:new Date(Date.now()+h*1e3).toISOString()}}}a(P,"handleUploadUrls");async function I(t){const n=await _(t);if(n.state!=="uploading")throw new o(409,"import_not_uploading",`import run ${n.run_id} is ${n.state}`);const e=t.params.name;if(!R.includes(e))throw new o(404,"unknown_store",`no file store ${e}`);try{await O(await f(),n.run_id,e,t.body)}catch(r){throw r instanceof T?new o(400,"invalid_body",r.message):r}return{status:204}}a(I,"handleStore");async function U(t){const n=await _(t),e=await c();if(n.state==="uploading"){if(await(await f()).get(m.source(n.run_id))===null)throw new o(409,"bundle_invalid","source.json is not uploaded");await e.prepare("UPDATE import_runs SET state = 'loading', step = 'queued', loaded_at = ? WHERE run_id = ? AND state = 'uploading'").run(new Date().toISOString(),n.run_id)}else if(n.state!=="loading"&&n.state!=="committed"&&n.state!=="done")throw new o(409,"import_not_uploading",`import run ${n.run_id} is ${n.state}`);return{status:202,body:await b(n.run_id)}}a(U,"handleLoad");async function b(t){const n=await(await c()).prepare("SELECT * FROM import_runs WHERE run_id = ?").get(t),e=n.counts_json===null?{}:JSON.parse(n.counts_json),{unresolved_plugins:r,refused_plugins:s,...u}=e;return{run_id:n.run_id,source_install_id:n.source_install_id,source_kind:n.source_kind,person:n.person,release:n.release,state:n.state,step:n.step,counts:u,verify:n.verify_json===null?null:JSON.parse(n.verify_json),failure:n.failure_json===null?null:JSON.parse(n.failure_json),unresolved_plugins:r??[],refused_plugins:s??[],created:n.created_at,loaded:n.loaded_at,finished:n.finished_at}}a(b,"runDto");async function L(t){const n=await _(t,!0);return{status:200,body:await b(n.run_id)}}a(L,"handleGet");async function W(t){const n=await _(t),e=await D(n.run_id,await f());if(!e.rolled_back)throw new o(409,"rollback_has_dependents",`later rows depend on import run ${n.run_id}`,e.dependents);return{status:200,body:e}}a(W,"handleRollback");const X=[{method:"POST",pattern:"/v1/imports",handler:j},{method:"POST",pattern:"/v1/imports/:run_id/upload-urls",handler:P},{method:"PUT",pattern:"/v1/imports/:run_id/stores/:name",handler:I},{method:"POST",pattern:"/v1/imports/:run_id/load",handler:U},{method:"GET",pattern:"/v1/imports/:run_id",handler:L},{method:"POST",pattern:"/v1/imports/:run_id/rollback",handler:W}];export{X as importRoutes};
|
package/dist/daemon/crtrd.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
var nr=Object.defineProperty;var a=(e,l)=>nr(e,"name",{value:l,configurable:!0});import{applyBootSettings as or}from"./boot-settings.js";import{startPeerOutbox as ir}from"./peer/outbox.js";import{reportMachineStatus as ar}from"../core/peer/directory.js";import{createPeerRelay as sr}from"./peer/relay-service.js";import{writeFileSync as cr,readFileSync as lr,rmSync as dr,mkdirSync as be,existsSync as P,unlinkSync as ke,chmodSync as Re}from"node:fs";import{createServer as V}from"node:http";import{dirname as mr}from"node:path";import{envCrtrdTcp as Pe,envDocumentPort as pr}from"../shared/env.js";import{openCanvasConversationStore as fr,serveCanvasConversationStore as ur}from"../core/canvas/sessions.js";import{installConversationStore as wr}from"../core/conversation-store/index.js";import{pollRunObservers as hr}from"./runs/observer.js";import{randomBytes as Ee}from"node:crypto";import{apiSocketPath as Ie,crtrHome as vr}from"../core/canvas/paths.js";import{activeAppUsers as gr}from"../core/spaces/users.js";import{ensureAppSpace as Sr,ensureUserFiles as yr}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as T}from"../launcher/spec.js";import{launcherRequest as A}from"../launcher/client.js";import{installLauncherEventSink as _r}from"./launcher-events.js";import{initializeEmptyLayout as br,layoutNeedsMigration as kr,socketDir as xe}from"../core/layout.js";import{migrateLooseKeys as Rr}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as Pr}from"../core/profiles/relocate.js";import{getNode as Er,listNodesForSupervision as Te,listParkedResidentsWithWakeMail as Ir,setFrozen as De}from"../core/canvas/index.js";import{createApiServer as xr,closeServer as Ce}from"./api/server.js";import{Router as Tr}from"./api/router.js";import{healthRoutes as Dr}from"./api/handlers/health.js";import{createDocumentListener as Cr}from"./api/document-listener.js";import{documentSecret as Lr}from"./api/document-secret.js";import{createAppListener as $r}from"./api/app-listener.js";import{registerMachineEnvironment as Ar}from"../core/environments.js";import{registerBlaxelRunnerFromEnv as Nr}from"../core/secrets/blaxel-register.js";import{markCloudRole as Mr,resolvePerson as Gr,retireLocalPerson as Or,setPersonId as Fr}from"../core/canvas/person.js";import{backfillNodeApps as Le,runPermissionsBoot as Br}from"../core/permissions/boot.js";import{startRunNotices as Hr}from"./run-notices.js";import{startGrantEffects as jr}from"./reconcilers/grant-effects.js";import{syncPackages as zr}from"../core/packages/directory-sync.js";import{createGrantSync as Ur,GRANT_SYNC_INTERVAL_S as $e}from"../core/grants/sync.js";import{sweepSandboxes as qr}from"../core/grants/on-sync.js";import{rebuildCronExecutions as Wr}from"./cron/run-socket.js";import{hasSyncedGrants as Jr}from"../core/grants/mirror.js";import{readConfig as le}from"../core/config.js";import{reconcileUndeliveredTickets as Kr}from"./human/sweep.js";import{resetHandedOffDeliveries as Vr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as Yr}from"../core/canvas/daemon-actions.js";import{sweepReviews as Qr}from"./review/sweep.js";import{pidfilePath as de}from"./pidfile.js";import{rendererWarning as Xr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as Zr}from"./supervise-cadence.js";import{createResumeDetector as et}from"./resume-detector.js";import{setFatalFaultAnnouncer as rt}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as tt}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as nt}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as ot}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as it}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as at}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as st}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as ct}from"./reconcilers/action-lane.js";import{endLiveExecution as lt,migrateLegacyExecutionIdentities as dt}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as mt}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as pt}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as ft}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as ut}from"./reconcilers/storage-due-work.js";import{PurgeDueWorkReconciler as wt}from"./reconcilers/purge-due-work.js";import{assertLedgerEnv as ht}from"../core/ledger/rates.js";import{LedgerSandboxReconciler as vt}from"./reconcilers/ledger-sandbox.js";import{LedgerStorageReconciler as gt}from"./reconcilers/ledger-storage.js";import{LedgerR2Reconciler as St}from"./reconcilers/ledger-r2.js";import{ImportLaneReconciler as yt}from"./reconcilers/import-lane.js";import{BashDeadlineReconciler as _t}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as bt}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as kt}from"./reconcilers/structured-request-lane.js";import{RecipeImageReconciler as Rt,subscribeImageBuilt as Pt}from"./reconcilers/recipe-image.js";import{reconcileStructuredRequestSidecars as Et}from"./structured-requests/boot.js";import{captureLivenessSnapshot as Ae,capturePidCommand as It,captureTeardownSnapshot as Ne,isPidAlive as xt,isAnyPidAlive as Me,killProcessTreePids as Ge}from"../core/canvas/pid.js";import{bindFleet as Tt,boundFleet as Dt}from"../core/runtime/fleet.js";import{bindDaemonControl as Ct,clearDaemonControl as Lt}from"./control.js";import{execDaemon as $t,spawnDaemon as At}from"./manage.js";import{reconcileAndMigrateBoot as Nt}from"../core/canvas/boot.js";import{resolveInstallId as Mt}from"../core/canvas/install-id.js";import{setInstallId as Gt}from"../core/runtime/nodes.js";import{claimDaemonOwnership as Ot,daemonOwnershipApplies as Ft,daemonOwnershipClaimed as Bt,releaseDaemonOwnership as Oe}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as Ht,closeAllDbs as me,openDb as Fe,withCanvasWrite as pe}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as jt}from"../core/canvas/import-sqlite.js";import{ensurePostgres as zt,ensurePostgresHealthy as Ut,stopPostgres as qt,withPostgresStartLock as Wt}from"./postgres/index.js";import{brokerSignalBus as H}from"./broker-signals/bus.js";import{brokerSignalStreams as Jt}from"./api/handlers/broker-signals.js";import{setRequestsStartRunObservers as Kt}from"./api/handlers/broker-ops.js";import{reconcilePackages as Vt}from"../core/graph/package-reconcile.js";import{migrateAppInstalls as Yt}from"../core/scoped-state/migrate-app-installs.js";import{publish as Qt,readBuiltinSnapshot as Xt}from"../core/graph/package-publish.js";import{startRepoSync as Zt}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as en,reapStaleSpares as rn}from"../core/runtime/warm-pool.js";import{DaemonFleet as tn}from"./fleet.js";import{copyNodeFilesToRows as nn}from"../core/runtime/first-boot-rows.js";import{BOOT_MIGRATIONS as on}from"./boot-migrations.js";import{emitEvent as d}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as an}from"../core/events/source.js";import{activateCanvasImport as sn,MigrationBlockedError as cn,runForcedCanvasImport as ln}from"../migrations/activation.js";import{adoptInstallPerson as dn,convertLegacyScopedStore as mn,personStoresCopied as pn}from"../core/scoped-state/migrate.js";import{readLegacyUserSnapshot as fn}from"../core/scoped-state/legacy-profiles.js";import{heldPersons as Be,installPreludeSnapshot as un}from"../core/scoped-state/person-store.js";import{loadPersonSettings as He}from"../core/scoped-state/person-rows.js";import{userScopeRoot as je}from"../core/scope.js";import{brokerCommandDeclaresCanvasHome as fe}from"../core/runtime/broker-ownership.js";import{brokerCommandDeclaresCanvasHome as qi}from"../core/runtime/broker-ownership.js";function ue(e){return/ --epoch (\S+) /.exec(e)?.[1]??null}a(ue,"brokerCommandEpoch");const wn=5e3,ze=150;async function hn(e){const l=Ae();if(l===null)throw d({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const u=vr(),f=[];for(const[n,m]of l.processes){const y=m.command;if(m.zombie||!y.includes("broker-cli.js")||!fe(y,u)||ue(y)===e)continue;const k=y.trim().split(/\s+/).at(-1)??"";k===""||k.endsWith("broker-cli.js")||f.push({pid:n,nodeId:k})}let v=0;const s=[];for(const n of f){const m=It(n.pid);if(m===null||!m.includes("broker-cli.js")||!fe(m,u)||ue(m)===e||m.trim().split(/\s+/).at(-1)!==n.nodeId)continue;const y=Ne(n.pid,null);if(!y.reused){try{process.platform==="linux"&&P(T)?await A({operation:"stop",pid:n.pid}):process.kill(n.pid,"SIGTERM")}catch{continue}v++,s.push({pid:n.pid,snap:y})}}const w=Date.now()+wn;for(;Date.now()<w&&s.some(n=>Me(n.snap.tree.length>0?n.snap.tree:[n.pid]));)await new Promise(n=>setTimeout(n,ze));let g=0;for(const n of s){const m=n.snap.tree.length>0?n.snap.tree:[n.pid];Me(m)&&(process.platform==="linux"&&P(T)?await A({operation:"stop",pid:n.pid}):Ge(m,"SIGKILL",n.snap.identities),g++)}g>0&&await new Promise(n=>setTimeout(n,ze));const p=Ae();if(p===null)throw d({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const c=[];for(const[n,m]of p.processes){const y=m.command;m.zombie||!y.includes("broker-cli.js")||fe(y,u)&&ue(y)!==e&&c.push(n)}if(c.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${c.join(", ")}`);d({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:f.length,sigtermed:v,sigkilled:g}})}a(hn,"reapPriorEpochBrokers");const vn=5e3;async function gn(e){const l=Array.from(e.entries());if(l.length===0)return;for(const[s]of l){const w=(await Er(s))?.status;(w==="active"||w==="idle")&&await De(s,new Date().toISOString())}const u=l.map(([,s])=>({pid:s.pid,snap:Ne(s.pid,null),exited:s.handle.exited}));for(const s of u)if(!s.snap.reused)try{process.platform==="linux"&&P(T)?await A({operation:"stop",pid:s.pid}):process.kill(s.pid,"SIGTERM")}catch{}const f=new Promise(s=>{setTimeout(s,vn).unref()});await Promise.allSettled(u.map(s=>Promise.race([s.exited.then(()=>{}),f])));const v=[];for(const s of u)xt(s.pid)&&(process.platform==="linux"&&P(T)?await A({operation:"stop",pid:s.pid}):Ge(s.snap.tree.length>0?s.snap.tree:[s.pid],"SIGKILL",s.snap.identities),v.push(s));await Promise.all(v.map(s=>s.exited));for(const[s]of l)await lt(s,{reason:"daemon-teardown"});d({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:u.length,sigkilled:v.length}})}a(gn,"teardownFleet");function Ue(){be(mr(de()),{recursive:!0}),cr(de(),String(process.pid),"utf8")}a(Ue,"writePidfile");function qe(e){return typeof e=="object"&&e!==null&&e.code==="ENOENT"}a(qe,"isMissingPathError");function We(e){const l=de();let u;try{u=lr(l,"utf8")}catch(f){if(qe(f))return;throw f}if(u.trim()!==String(e))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${e})`);try{dr(l)}catch(f){if(qe(f))return;throw f}}a(We,"removeOwnedPidfileMirror");const Sn={acceptsDetachedWork:a(()=>!0,"acceptsDetachedWork"),registerDetached:a(e=>{e.catch(()=>{})},"registerDetached")},Y=new tt;rt(e=>Y.handleFatalFault(e,!1));const yn=new nt,_n=new ot,bn=new it,kn=new at,Rn=new st,Pn=new ct,En=new pt,In=new ft,xn=new ut,Tn=new wt,Dn=new vt,Cn=new gt,Ln=new St,$n=new yt,An=new _t,Nn=new bt,Mn=new kt,Gn=new Rt;async function On(e=Date.now(),l=Sn){const u=Dt(),f=await Y.runFleetPreamble(u);let v,s;try{const g=await Te();v=g.filter(c=>c.status==="active"||c.status==="idle"),s=g.filter(c=>c.status==="done"||c.status==="canceled"||c.status==="dead");const p=new Set(s.map(c=>c.node_id));s.push(...(await Ir()).filter(c=>!p.has(c.node_id)))}catch(g){d({level:"error",event:"canvas.node_list.failed",error:g});return}await Y.run(e,{rows:v,fleet:u,lifecycle:l}),await yn.run(e,{rows:v});const w=await _n.run(e,{rows:[...v,...s],fleet:u});await bn.run(e),await Mn.run(e),await Gn.run(e,{lifecycle:l}),await kn.run(e,{lifecycle:l}),await Rn.run(e,{lifecycle:l}),await Pn.run(e,{lifecycle:l}),await En.run(e,{lifecycle:l}),await In.run(e,{lifecycle:l,fleet:u}),xn.run(e,{lifecycle:l}),Tn.run(e,{lifecycle:l}),Dn.run(e,{lifecycle:l}),Cn.run(e,{lifecycle:l}),Ln.run(e,{lifecycle:l}),$n.run(e,{lifecycle:l}),await hr(),Nn.run(e,{fleet:u,lifecycle:l}),await An.run(e,{rows:v}),Y.warnIfSaturated({fleet:u,lifecycle:l,thresholds:f,frozen:w})}a(On,"superviseTick");function Q(e){return e===void 0||e==="reconcile"}a(Q,"roleRunsLanes");function N(e){return e===void 0||e==="api"}a(N,"roleServesRequests");const Fn=66e4;function Bn(e){const l=new Tr().registerAll(Dr(()=>null).filter(n=>n.pattern==="/healthz")),u=a((n,m)=>{const y=(n.url??"/").split("?")[0];if((n.method??"GET").toUpperCase()==="GET"&&y==="/healthz"){l.handle(n,m,null,{class:"person"});return}m.writeHead(404,{"content-type":"application/json; charset=utf-8"}),m.end(JSON.stringify({error:{code:"not_found",message:"this process serves only /healthz",origin:"runtime"}}))},"handler"),f=Ie();P(f)&&ke(f);const v=V(u),s=new Promise((n,m)=>{v.once("error",m),v.listen(f,()=>{v.off("error",m),Re(f,384),n()})});let w,g=null;const p=e.tcp??Pe()??le("user").api.tcp;if(p!==void 0&&p!==""){const n=/^(.*):(\d+)$/.exec(p.trim());if(n===null||Number(n[2])>65535)throw new Error(`invalid reconcile probe TCP address: ${p}`);const m=V(u);m.on("error",y=>d({event:"api.server.failed",level:"error",error:y})),m.listen(Number(n[2]),n[1]===""?"0.0.0.0":n[1],()=>{g=`${n[1]===""?"0.0.0.0":n[1]}:${n[2]}`}),w=m}let c=!1;return{ready:s,tcpAddress:a(()=>g,"tcpAddress"),async close(n={}){if(!c){c=!0,await Promise.all([Ce(v,new Set,n.graceMs),w===void 0?Promise.resolve():Ce(w,new Set,n.graceMs)]);try{P(f)&&ke(f)}catch{}}}}}a(Bn,"createProbeServer");const Hn="daemon is draining for shutdown and admits no new work";async function jn(e){be(xe(),{recursive:!0,mode:493}),process.platform==="linux"&&Re(xe(),493);const l=Ie();if(P(l))throw new Error(`layout refusal cannot bind occupied socket: ${l}`);const u=V((p,c)=>{if(p.method==="GET"&&p.url==="/healthz"){c.writeHead(200,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}c.writeHead(503,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((p,c)=>{u.once("error",c),u.listen(l,()=>{u.off("error",c),p()})});let f;const v=e.tcp??Pe();if(v){const p=/^(.*):(\d+)$/.exec(v);if(!p||Number(p[2])>65535)throw new Error(`invalid refusal TCP address: ${v}`);const c=V(u.listeners("request")[0]);await new Promise((n,m)=>{c.once("error",m),c.listen(Number(p[2]),p[1],()=>{c.off("error",m),n()})}),f=c}Ue();let s;const w=a(()=>(s??=(async()=>{u.closeAllConnections(),f?.closeAllConnections(),await Promise.all([u,f].filter(p=>p!==void 0).map(p=>new Promise((c,n)=>p.close(m=>m?n(m):c())))),We(process.pid),process.off("SIGINT",g),process.off("SIGTERM",g)})(),s),"cleanup"),g=a(()=>{w().then(()=>process.exit(0),p=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(p)}
|
|
2
|
-
`),process.exit(1)})},"onSignal");return process.on("SIGINT",g),process.on("SIGTERM",g),w}a(
|
|
3
|
-
`)},"logPostgres"),u=!!process.env.CRTR_TEST_PG_URL,f=
|
|
1
|
+
var or=Object.defineProperty;var a=(e,l)=>or(e,"name",{value:l,configurable:!0});import{applyBootSettings as ir}from"./boot-settings.js";import{startPeerOutbox as ar}from"./peer/outbox.js";import{reportMachineStatus as sr}from"../core/peer/directory.js";import{createPeerRelay as cr}from"./peer/relay-service.js";import{writeFileSync as lr,readFileSync as dr,rmSync as mr,mkdirSync as ke,existsSync as R,unlinkSync as Re,chmodSync as Pe}from"node:fs";import{createServer as V}from"node:http";import{dirname as pr}from"node:path";import{envCrtrdTcp as Ee,envDocumentPort as fr}from"../shared/env.js";import{openCanvasConversationStore as ur,serveCanvasConversationStore as wr}from"../core/canvas/sessions.js";import{installConversationStore as hr}from"../core/conversation-store/index.js";import{pollRunObservers as vr}from"./runs/observer.js";import{randomBytes as Ie}from"node:crypto";import{apiSocketPath as xe,crtrHome as gr}from"../core/canvas/paths.js";import{activeAppUsers as Sr}from"../core/spaces/users.js";import{ensureAppSpace as yr,ensureUserFiles as _r}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as T}from"../launcher/spec.js";import{launcherRequest as A}from"../launcher/client.js";import{installLauncherEventSink as br}from"./launcher-events.js";import{initializeEmptyLayout as kr,layoutNeedsMigration as Rr,socketDir as Te}from"../core/layout.js";import{migrateLooseKeys as Pr}from"../core/layout-migrate/loose-keys.js";import{relocateProfileFolders as Er}from"../core/profiles/relocate.js";import{getNode as Ir,listNodesForSupervision as De,listParkedResidentsWithWakeMail as xr,setFrozen as Ce}from"../core/canvas/index.js";import{createApiServer as Tr,closeServer as Le}from"./api/server.js";import{Router as Dr}from"./api/router.js";import{healthRoutes as Cr}from"./api/handlers/health.js";import{createDocumentListener as Lr}from"./api/document-listener.js";import{documentSecret as $r}from"./api/document-secret.js";import{createAppListener as Ar}from"./api/app-listener.js";import{registerMachineEnvironment as Nr}from"../core/environments.js";import{registerBlaxelRunnerFromEnv as Mr}from"../core/secrets/blaxel-register.js";import{markCloudRole as Gr,resolvePerson as Or,retireLocalPerson as Fr,setPersonId as Br}from"../core/canvas/person.js";import{backfillNodeApps as $e,runPermissionsBoot as Hr}from"../core/permissions/boot.js";import{startRunNotices as jr}from"./run-notices.js";import{startGrantEffects as zr}from"./reconcilers/grant-effects.js";import{syncPackages as Ur}from"../core/packages/directory-sync.js";import{createGrantSync as qr,GRANT_SYNC_INTERVAL_S as Ae}from"../core/grants/sync.js";import{sweepSandboxes as Wr}from"../core/grants/on-sync.js";import{rebuildCronExecutions as Jr}from"./cron/run-socket.js";import{hasSyncedGrants as Kr}from"../core/grants/mirror.js";import{readConfig as le}from"../core/config.js";import{reconcileUndeliveredTickets as Vr}from"./human/sweep.js";import{resetHandedOffDeliveries as Yr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as Qr}from"../core/canvas/daemon-actions.js";import{sweepReviews as Xr}from"./review/sweep.js";import{pidfilePath as de}from"./pidfile.js";import{rendererWarning as Zr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as et}from"./supervise-cadence.js";import{createResumeDetector as rt}from"./resume-detector.js";import{setFatalFaultAnnouncer as tt}from"../core/canvas/recovery.js";import{BrokerSupervisionReconciler as nt}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as ot}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as it}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as at}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as st}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as ct}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as lt}from"./reconcilers/action-lane.js";import{endLiveExecution as dt,migrateLegacyExecutionIdentities as mt}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as pt}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as ft}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as ut}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as wt}from"./reconcilers/storage-due-work.js";import{PurgeDueWorkReconciler as ht}from"./reconcilers/purge-due-work.js";import{assertLedgerEnv as vt}from"../core/ledger/rates.js";import{LedgerSandboxReconciler as gt}from"./reconcilers/ledger-sandbox.js";import{LedgerStorageReconciler as St}from"./reconcilers/ledger-storage.js";import{LedgerR2Reconciler as yt}from"./reconcilers/ledger-r2.js";import{ImportLaneReconciler as _t}from"./reconcilers/import-lane.js";import{BashDeadlineReconciler as bt}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as kt}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as Rt}from"./reconcilers/structured-request-lane.js";import{RecipeImageReconciler as Pt,subscribeImageBuilt as Et}from"./reconcilers/recipe-image.js";import{reconcileStructuredRequestSidecars as It}from"./structured-requests/boot.js";import{captureLivenessSnapshot as Ne,capturePidCommand as xt,captureTeardownSnapshot as Me,isPidAlive as Tt,isAnyPidAlive as Ge,killProcessTreePids as Oe}from"../core/canvas/pid.js";import{bindFleet as Dt,boundFleet as Ct}from"../core/runtime/fleet.js";import{bindDaemonControl as Lt,clearDaemonControl as $t}from"./control.js";import{execDaemon as At,spawnDaemon as Nt}from"./manage.js";import{reconcileAndMigrateBoot as Mt}from"../core/canvas/boot.js";import{resolveInstallId as Gt}from"../core/canvas/install-id.js";import{setInstallId as Ot}from"../core/runtime/nodes.js";import{claimDaemonOwnership as Ft,daemonOwnershipApplies as Bt,daemonOwnershipClaimed as Ht,releaseDaemonOwnership as Fe}from"../core/canvas/daemon-owner.js";import{canvasDatabaseUrl as jt,closeAllDbs as me,openDb as Be,withCanvasWrite as pe}from"../core/canvas/db.js";import{importSqliteCanvasIfPresent as zt}from"../core/canvas/import-sqlite.js";import{ensurePostgres as Ut,ensurePostgresHealthy as qt,stopPostgres as Wt,withPostgresStartLock as Jt}from"./postgres/index.js";import{brokerSignalBus as H}from"./broker-signals/bus.js";import{brokerSignalStreams as Kt}from"./api/handlers/broker-signals.js";import{setRequestsStartRunObservers as Vt}from"./api/handlers/broker-ops.js";import{reconcilePackages as Yt}from"../core/graph/package-reconcile.js";import{migrateAppInstalls as Qt}from"../core/scoped-state/migrate-app-installs.js";import{publish as Xt,readBuiltinSnapshot as Zt}from"../core/graph/package-publish.js";import{startRepoSync as en}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as rn,reapStaleSpares as tn}from"../core/runtime/warm-pool.js";import{DaemonFleet as nn}from"./fleet.js";import{copyNodeFilesToRows as on}from"../core/runtime/first-boot-rows.js";import{BOOT_MIGRATIONS as an}from"./boot-migrations.js";import{emitEvent as m}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as sn}from"../core/events/source.js";import{activateCanvasImport as cn,MigrationBlockedError as ln,runForcedCanvasImport as dn}from"../migrations/activation.js";import{adoptInstallPerson as mn,convertLegacyScopedStore as pn,personStoresCopied as fn}from"../core/scoped-state/migrate.js";import{readLegacyUserSnapshot as un}from"../core/scoped-state/legacy-profiles.js";import{heldPersons as He,installPreludeSnapshot as wn}from"../core/scoped-state/person-store.js";import{loadPersonSettings as je}from"../core/scoped-state/person-rows.js";import{userScopeRoot as ze}from"../core/scope.js";import{brokerCommandDeclaresCanvasHome as fe}from"../core/runtime/broker-ownership.js";import{brokerCommandDeclaresCanvasHome as Wi}from"../core/runtime/broker-ownership.js";function ue(e){return/ --epoch (\S+) /.exec(e)?.[1]??null}a(ue,"brokerCommandEpoch");const hn=5e3,Ue=150;async function vn(e){const l=Ne();if(l===null)throw m({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const u=gr(),f=[];for(const[n,d]of l.processes){const y=d.command;if(d.zombie||!y.includes("broker-cli.js")||!fe(y,u)||ue(y)===e)continue;const P=y.trim().split(/\s+/).at(-1)??"";P===""||P.endsWith("broker-cli.js")||f.push({pid:n,nodeId:P})}let v=0;const s=[];for(const n of f){const d=xt(n.pid);if(d===null||!d.includes("broker-cli.js")||!fe(d,u)||ue(d)===e||d.trim().split(/\s+/).at(-1)!==n.nodeId)continue;const y=Me(n.pid,null);if(!y.reused){try{process.platform==="linux"&&R(T)?await A({operation:"stop",pid:n.pid}):process.kill(n.pid,"SIGTERM")}catch{continue}v++,s.push({pid:n.pid,snap:y})}}const w=Date.now()+hn;for(;Date.now()<w&&s.some(n=>Ge(n.snap.tree.length>0?n.snap.tree:[n.pid]));)await new Promise(n=>setTimeout(n,Ue));let g=0;for(const n of s){const d=n.snap.tree.length>0?n.snap.tree:[n.pid];Ge(d)&&(process.platform==="linux"&&R(T)?await A({operation:"stop",pid:n.pid}):Oe(d,"SIGKILL",n.snap.identities),g++)}g>0&&await new Promise(n=>setTimeout(n,Ue));const p=Ne();if(p===null)throw m({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const c=[];for(const[n,d]of p.processes){const y=d.command;d.zombie||!y.includes("broker-cli.js")||fe(y,u)&&ue(y)!==e&&c.push(n)}if(c.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${c.join(", ")}`);m({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:f.length,sigtermed:v,sigkilled:g}})}a(vn,"reapPriorEpochBrokers");const gn=5e3;async function Sn(e){const l=Array.from(e.entries());if(l.length===0)return;for(const[s]of l){const w=(await Ir(s))?.status;(w==="active"||w==="idle")&&await Ce(s,new Date().toISOString())}const u=l.map(([,s])=>({pid:s.pid,snap:Me(s.pid,null),exited:s.handle.exited}));for(const s of u)if(!s.snap.reused)try{process.platform==="linux"&&R(T)?await A({operation:"stop",pid:s.pid}):process.kill(s.pid,"SIGTERM")}catch{}const f=new Promise(s=>{setTimeout(s,gn).unref()});await Promise.allSettled(u.map(s=>Promise.race([s.exited.then(()=>{}),f])));const v=[];for(const s of u)Tt(s.pid)&&(process.platform==="linux"&&R(T)?await A({operation:"stop",pid:s.pid}):Oe(s.snap.tree.length>0?s.snap.tree:[s.pid],"SIGKILL",s.snap.identities),v.push(s));await Promise.all(v.map(s=>s.exited));for(const[s]of l)await dt(s,{reason:"daemon-teardown"});m({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:u.length,sigkilled:v.length}})}a(Sn,"teardownFleet");function qe(){ke(pr(de()),{recursive:!0}),lr(de(),String(process.pid),"utf8")}a(qe,"writePidfile");function We(e){return typeof e=="object"&&e!==null&&e.code==="ENOENT"}a(We,"isMissingPathError");function Je(e){const l=de();let u;try{u=dr(l,"utf8")}catch(f){if(We(f))return;throw f}if(u.trim()!==String(e))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${e})`);try{mr(l)}catch(f){if(We(f))return;throw f}}a(Je,"removeOwnedPidfileMirror");const yn={acceptsDetachedWork:a(()=>!0,"acceptsDetachedWork"),registerDetached:a(e=>{e.catch(()=>{})},"registerDetached")},Y=new nt;tt(e=>Y.handleFatalFault(e,!1));const _n=new ot,bn=new it,kn=new at,Rn=new st,Pn=new ct,En=new lt,In=new ft,xn=new ut,Tn=new wt,Dn=new ht,Cn=new gt,Ln=new St,$n=new yt,An=new _t,Nn=new bt,Mn=new kt,Gn=new Rt,On=new Pt;async function Fn(e=Date.now(),l=yn){const u=Ct(),f=await Y.runFleetPreamble(u);let v,s;try{const g=await De();v=g.filter(c=>c.status==="active"||c.status==="idle"),s=g.filter(c=>c.status==="done"||c.status==="canceled"||c.status==="dead");const p=new Set(s.map(c=>c.node_id));s.push(...(await xr()).filter(c=>!p.has(c.node_id)))}catch(g){m({level:"error",event:"canvas.node_list.failed",error:g});return}await Y.run(e,{rows:v,fleet:u,lifecycle:l}),await _n.run(e,{rows:v});const w=await bn.run(e,{rows:[...v,...s],fleet:u});await kn.run(e),await Gn.run(e),await On.run(e,{lifecycle:l}),await Rn.run(e,{lifecycle:l}),await Pn.run(e,{lifecycle:l}),await En.run(e,{lifecycle:l}),await In.run(e,{lifecycle:l}),await xn.run(e,{lifecycle:l,fleet:u}),Tn.run(e,{lifecycle:l}),Dn.run(e,{lifecycle:l}),Cn.run(e,{lifecycle:l}),Ln.run(e,{lifecycle:l}),$n.run(e,{lifecycle:l}),An.run(e,{lifecycle:l}),await vr(),Mn.run(e,{fleet:u,lifecycle:l}),await Nn.run(e,{rows:v}),Y.warnIfSaturated({fleet:u,lifecycle:l,thresholds:f,frozen:w})}a(Fn,"superviseTick");function Q(e){return e===void 0||e==="reconcile"}a(Q,"roleRunsLanes");function N(e){return e===void 0||e==="api"}a(N,"roleServesRequests");const Bn=66e4;function Hn(e){const l=new Dr().registerAll(Cr(()=>null).filter(n=>n.pattern==="/healthz")),u=a((n,d)=>{const y=(n.url??"/").split("?")[0];if((n.method??"GET").toUpperCase()==="GET"&&y==="/healthz"){l.handle(n,d,null,{class:"person"});return}d.writeHead(404,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({error:{code:"not_found",message:"this process serves only /healthz",origin:"runtime"}}))},"handler"),f=xe();R(f)&&Re(f);const v=V(u),s=new Promise((n,d)=>{v.once("error",d),v.listen(f,()=>{v.off("error",d),Pe(f,384),n()})});let w,g=null;const p=e.tcp??Ee()??le("user").api.tcp;if(p!==void 0&&p!==""){const n=/^(.*):(\d+)$/.exec(p.trim());if(n===null||Number(n[2])>65535)throw new Error(`invalid reconcile probe TCP address: ${p}`);const d=V(u);d.on("error",y=>m({event:"api.server.failed",level:"error",error:y})),d.listen(Number(n[2]),n[1]===""?"0.0.0.0":n[1],()=>{g=`${n[1]===""?"0.0.0.0":n[1]}:${n[2]}`}),w=d}let c=!1;return{ready:s,tcpAddress:a(()=>g,"tcpAddress"),async close(n={}){if(!c){c=!0,await Promise.all([Le(v,new Set,n.graceMs),w===void 0?Promise.resolve():Le(w,new Set,n.graceMs)]);try{R(f)&&Re(f)}catch{}}}}}a(Hn,"createProbeServer");const jn="daemon is draining for shutdown and admits no new work";async function zn(e){ke(Te(),{recursive:!0,mode:493}),process.platform==="linux"&&Pe(Te(),493);const l=xe();if(R(l))throw new Error(`layout refusal cannot bind occupied socket: ${l}`);const u=V((p,c)=>{if(p.method==="GET"&&p.url==="/healthz"){c.writeHead(200,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}c.writeHead(503,{"content-type":"application/json; charset=utf-8"}),c.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((p,c)=>{u.once("error",c),u.listen(l,()=>{u.off("error",c),p()})});let f;const v=e.tcp??Ee();if(v){const p=/^(.*):(\d+)$/.exec(v);if(!p||Number(p[2])>65535)throw new Error(`invalid refusal TCP address: ${v}`);const c=V(u.listeners("request")[0]);await new Promise((n,d)=>{c.once("error",d),c.listen(Number(p[2]),p[1],()=>{c.off("error",d),n()})}),f=c}qe();let s;const w=a(()=>(s??=(async()=>{u.closeAllConnections(),f?.closeAllConnections(),await Promise.all([u,f].filter(p=>p!==void 0).map(p=>new Promise((c,n)=>p.close(d=>d?n(d):c())))),Je(process.pid),process.off("SIGINT",g),process.off("SIGTERM",g)})(),s),"cleanup"),g=a(()=>{w().then(()=>process.exit(0),p=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(p)}
|
|
2
|
+
`),process.exit(1)})},"onSignal");return process.on("SIGINT",g),process.on("SIGTERM",g),w}a(zn,"serveLayoutRefusal");async function Un(e={}){if(e.role==="machine")return(await import("./machine-daemon.js")).runMachineDaemon();if(Rr())return zn(e);kr();const l=a(t=>{process.stderr.write(`[crtrd] ${t}
|
|
3
|
+
`)},"logPostgres"),u=!!process.env.CRTR_TEST_PG_URL,f=Bt(e.role);Vt(Q(e.role));const v=a(async()=>f?await Ft(Ie(16).toString("hex")):{claimed:!0,claim:{pid:process.pid,pid_identity:"",token:Ie(16).toString("hex")}},"claimOwnership"),{database:s,ownership:w}=u?{database:{url:await jt()},ownership:await v()}:await Jt(async()=>({database:await Ut({log:l,startLockHeld:!0}),ownership:await v()}));if(!w.claimed){if(w.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${w.incumbentPid??"?"})
|
|
4
4
|
`);else{const t=[w.incumbentPid!==void 0?`pid ${w.incumbentPid}`:void 0,w.verdict!==void 0?`verdict ${w.verdict}`:void 0].filter(h=>h!==void 0),i=t.length>0?` (${t.join(", ")})`:"";process.stderr.write(`[crtrd] claim lost: ${w.reason}${i}
|
|
5
|
-
`),w.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(w.reason==="own_identity_indeterminate"||w.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const g=w.claim;let p=null,c=null,n=null,
|
|
6
|
-
`);for(const r of await
|
|
7
|
-
`);await
|
|
8
|
-
`)}else{const r=await pe(async o=>(await
|
|
5
|
+
`),w.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(w.reason==="own_identity_indeterminate"||w.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const g=w.claim;let p=null,c=null,n=null,d=null,y,P="";try{f&&await vn(g.token);for(const r of Er())process.stderr.write(`[crtrd] ${r}
|
|
6
|
+
`);for(const r of await Pr())process.stderr.write(`[crtrd] ${r}
|
|
7
|
+
`);await Be(),await zt(s.url,l);const t=e.role===void 0&&!await fn();let i=null;t&&(pn({kind:"user",root:ze()}),i=un(ze())),e.role===void 0&&wn(i??{profiles:[],defaults:{},settings:{},state:{},apps:{}}),await H.listen(s.url);const h=a(r=>{je(r).catch(o=>m({level:"error",event:"daemon.settings.reload_failed",error:o}))},"reloadPerson");if(y=await H.subscribe(r=>{He().includes(r)&&h(r)},{kinds:["settings"],onListenStart:a(()=>{for(const r of He())h(r)},"onListenStart")}),e.role!==void 0&&Gr(),e.role!==void 0){let r;try{r=Zt()}catch(b){m({level:"error",event:"daemon.packages.snapshot_failed",error:b})}const o=await pe(async b=>(await $e(b),r===void 0?void 0:await Xt(b,r,"release")));d=await pe(async b=>await Qt(b));for(const b of o?.invalid??[])process.stderr.write(`[crtrd] package file is not a document: ${b.path}: ${b.error}
|
|
8
|
+
`)}else{const r=await pe(async o=>(await $e(o),await Yt(o)));for(const o of r.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${o.message}
|
|
9
9
|
`);for(const o of r.invalid)process.stderr.write(`[crtrd] package file is not a document: ${o.path}: ${o.error}
|
|
10
10
|
`);for(const o of r.quarantined)process.stderr.write(`[crtrd] plugin "${o.name}" has a damaged ${o.file} (${o.reason}); ${o.movedTo!==void 0?`moved to ${o.movedTo}`:"left in place and skipped"}
|
|
11
11
|
`);for(const o of r.kinds.refused)process.stderr.write(`[crtrd] plugin "${o.plugin}" kind ${o.kind} not registered: ${o.message}
|
|
12
12
|
`);for(const o of r.kinds.invalid)process.stderr.write(`[crtrd] plugin "${o.plugin}" declares invalid object kinds or scopes; no kinds registered and no app token: ${o.error}
|
|
13
|
-
`)}if(
|
|
14
|
-
`)}}catch(t){await H.close();try{if(f){const{released:i}=await
|
|
13
|
+
`)}if(P=await Gt(),Ot(P),e.role===void 0&&(p=await Or(),p!==null)){if(Br(p.id),await mn(p.id),process.platform==="darwin"){const{readConfig:r,updateConfig:o}=await import("../core/config.js");if(r("user").paths?.user_files===void 0){const{homedir:b}=await import("node:os");await o("user",be=>{be.paths??={},be.paths.user_files??=b()})}}c=await Fr(),c.joinedFrom!==null&&await je(c.person),await ir(),n=await Hr()}if(process.platform==="linux"&&R(T)&&(br(),await A({operation:"users.sync",users:await Sr()})),f){await pt(g.token);const r=await on();Object.values(r).some(o=>o>0)&&process.stderr.write(`[crtrd] node files copied into rows: ${JSON.stringify(r)}
|
|
14
|
+
`)}}catch(t){await H.close();try{if(f){const{released:i}=await Fe(g);if(!i)throw new Error("daemon ownership claim was not released")}await me()}catch(i){throw new AggregateError([t,i],"daemon startup failed and cleanup also failed")}throw t}let D=!0,X,j=Promise.resolve(),Z=!1,z,ee,re,C;const U=new Set;let te=!1,we=!1,E,L,ne,q=!1,W,M,G,O,F,oe,ie,ae;const Ke=rt();let I,S={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},$="initial startup activation",Ve=null;function Ye(){return S.phase==="ready"?null:{phase:S.phase,message:S.message,blockerPaths:S.blockerPaths}}a(Ye,"startupBlock");function k(t){U.add(t),t.finally(()=>{U.delete(t)}).catch(()=>{})}a(k,"registerDetached");const he={acceptsDetachedWork:a(()=>D,"acceptsDetachedWork"),registerDetached:k};Q(e.role)&&await en(he,Be);async function Qe(){try{D=!1,Kt.close(),ee?.(),ee=void 0,re?.(),y?.(),re=void 0,ie?.(),ie=void 0,await H.close(),z!==void 0&&(clearInterval(z),z=void 0),G!==void 0&&(clearInterval(G),G=void 0),O!==void 0&&(clearInterval(O),O=void 0),ae?.(),ae=void 0;let t;do t=j,await t;while(t!==j);for(;U.size>0;)await Promise.allSettled([...U]);if(C!==void 0&&(f&&await Sn(C),await C.exitsSettled(),C.dispose()),ne!==void 0&&await ne.close(),L!==void 0&&!q&&(q=!0,await L.close()),oe?.(),oe=void 0,F!==void 0){const i=F;F=void 0,await i.stop()}if(M!==void 0){const i=M;M=void 0,await i.close()}if(W!==void 0){const i=W;W=void 0,await i.close()}if(te||(Je(process.pid),te=!0),te&&!we){if(f){const{released:i}=await Fe(g);if(!i)throw new Error("daemon ownership claim was not released")}we=!0,await me()}}finally{$t(),process.off("SIGINT",Se),process.off("SIGTERM",ye),process.off("exit",_e)}}a(Qe,"cleanupAttempt");const J=1e3;let ve=!1;function Xe(){return ve?{graceMs:J}:(ve=!0,_.fresh(()=>{m({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:J}})}),setTimeout(()=>{Ze()},J),{graceMs:J})}a(Xe,"requestHandover");async function Ze(){try{await x()}catch(t){const i=t instanceof Error?t.stack??t.message:String(t);process.stderr.write(`[crtrd] handover cleanup failed:
|
|
15
15
|
${i}
|
|
16
|
-
`),process.exit(1)}try{process.platform==="linux"&&
|
|
17
|
-
`),process.exit(1)}process.exit(0)}a(
|
|
16
|
+
`),process.exit(1)}try{process.platform==="linux"&&R(T)&&(await A({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await At();const t=await Nt({waitForJoinLock:!0});_.fresh(()=>{m({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:t.pid??t.existing_pid??null,spawned:t.started}})})}catch(t){_.fresh(()=>{m({level:"error",event:"daemon.handover.successor_failed",error:t})});const i=t instanceof Error?t.message:String(t);process.stderr.write(`[crtrd] handover successor failed to start: ${i}
|
|
17
|
+
`),process.exit(1)}process.exit(0)}a(Ze,"performHandover");function x(){if(E!==void 0)return E;const t=Qe();return E=t,t.then(()=>{E===t&&(E=void 0)},()=>{E===t&&(E=void 0)}),t}a(x,"requestCleanup");async function er(){S={phase:"initializing",message:jn,blockerPaths:[]},L!==void 0&&!q&&(q=!0,await L.close({graceMs:Bn}))}a(er,"drainApi");async function ge(t){try{t==="SIGTERM"&&e.role==="api"&&await er(),await x(),await Wt({claimed:a(async()=>{try{return f&&await Ht()}finally{await me()}},"claimed")}),process.exit(0)}catch(i){process.exitCode=1;const h=i instanceof Error?i.stack??`${i.name}: ${i.message}`:String(i);process.stderr.write(`[crtrd] ${t} cleanup failed:
|
|
18
18
|
${h}
|
|
19
|
-
`),process.exit(1)}}a(ge,"handleSignal");function Se(){ge("SIGINT")}a(Se,"onSigint");function ye(){ge("SIGTERM")}a(ye,"onSigterm");function _e(){x().catch(()=>{})}a(_e,"onExit");async function se(t){
|
|
19
|
+
`),process.exit(1)}}a(ge,"handleSignal");function Se(){ge("SIGINT")}a(Se,"onSigint");function ye(){ge("SIGTERM")}a(ye,"onSigterm");function _e(){x().catch(()=>{})}a(_e,"onExit");async function se(t){D&&(j=j.then(t).then(()=>{},i=>{_.fresh(()=>{m({level:"error",event:"daemon.lane_job.failed",error:i})})}))}a(se,"enqueue");async function K(){!D||Z||(Ke.check()&&I&&(m({level:"info",event:"daemon.resume.detected"}),k(I().catch(()=>{}))),Z=!0,await se(async()=>{if(Z=!1,!!D)try{u||await qt({log:l}),await Fn(Date.now(),he)}catch(t){_.fresh(()=>{m({level:"error",event:"daemon.tick.failed",error:t})})}}))}a(K,"enqueueTick");let B=null;async function rr(){if(B!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${B instanceof Error?B.message:String(B)}`};if(S.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(S.phase!=="prepared"&&S.phase!=="recovering")throw{status:409,code:"not_prepared",message:S.message};const t=S.phase==="prepared";try{return await ce(),S={phase:"ready",message:"",blockerPaths:[]},t?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(i){throw B=i,{status:500,code:"admission_failed",message:`daemon admission failed: ${i instanceof Error?i.message:String(i)}`}}}a(rr,"admitStartup");async function tr(t){if($!==null)throw{status:409,code:"migration_in_progress",message:`the canvas import is already running (${$})`};$="repair";try{const i=await dn({dryRun:t.dryRun});if(t.dryRun||i.pending||S.phase!=="blocked")return{result:i,resumed:!1};if(e.prepare)return S={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:i,resumed:!1};try{return await ce(),S={phase:"ready",message:"",blockerPaths:[]},{result:i,resumed:!0}}catch(h){const r=h instanceof Error?h.message:String(h);return setTimeout(()=>{x().catch(()=>{})},1e3),{result:i,resumed:!1,resumeError:r}}}finally{$=null}}a(tr,"repairMigrations");async function nr(){X=e.intervalMs??et;const t=g.token;C=new nn({epoch:t,enqueue:se}),Dt(C),S={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{m({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:X}})});const i=P;_.fresh(()=>{m({level:"info",event:"daemon.install_id.resolved",fields:{install_id:i}})});const h=await Nr(i);if(_.fresh(()=>{m({level:"info",event:"daemon.environment.registered",fields:{environment_id:h.environmentId,created:h.created}})}),_.fresh(()=>{let r;try{r=Zr()}catch{return}r!==null&&m({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:r.slice(0,2048)}})}),vt(),e.role!==void 0&&!process.env.CRTR_DIRECTORY_URL)throw new Error("CRTR_DIRECTORY_URL must be set on a cloud role (--role api|reconcile): it names the directory this deployment mints tokens against");if(e.role!==void 0&&Mr(),!Q(e.role)){S={phase:"ready",message:"",blockerPaths:[]};return}if(await _.fresh(async()=>{await Xr(),await Vr(),await Yr(Date.now()),await Qr(Date.now())}),await _.fresh(async()=>{const{reconcile:r,migrated:o}=await Mt(mt);r.changed&&m({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:r.cleared.length,same_boot_proven:r.sameBootProven}}),o.length>0&&m({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:o.length}})}),await _.fresh(async()=>{for(const r of await De())r.status!=="active"||r.pi_pid!=null||r.frozen_at!=null||r.respawn_not_before!=null||(await Ce(r.node_id,new Date().toISOString()),m({level:"warn",event:"daemon.startup.launch_debt_recorded",node_id:r.node_id}))}),await _.fresh(async()=>{await It();for(const r of an){const o=await r.apply();o.changed&&m({level:"info",event:"daemon.boot_migration.applied",fields:{migration:r.id,...o.summary}})}}),k(Jr()),I){const r=a(()=>{k(I().catch(()=>{})),k(sr().then(o=>F?.observe(o)).then(()=>{},o=>{m({level:"warn",event:"daemon.peer.status_failed",error:o})}))},"refresh");k(Wr().then(({completion:o})=>o).catch(o=>{m({level:"error",event:"daemon.grants.sweep_failed",error:o})})),r(),G=setInterval(r,Ae*1e3),G.unref(),ae=ar({joined:!0,register:k})}await se(async()=>{await tn(),await rn()}),z=setInterval(()=>{K().catch(r=>m({level:"error",event:"daemon.tick.failed",error:r}))},X),ee=await Et(()=>{K().catch(r=>m({level:"error",event:"daemon.tick.failed",error:r}))}),re=await H.subscribe(()=>{K().catch(r=>m({level:"error",event:"daemon.tick.failed",error:r}))},{kinds:["spend_limits"]}),await K(),e.role===void 0&&import("../commands/sys/import/move.js").then(({settleMoveAtDaemonStart:r})=>r(()=>!D,o=>m({level:"warn",event:"daemon.move.settle_failed",error:o}))).catch(r=>m({level:"warn",event:"daemon.move.settle_failed",error:r}))}a(nr,"completeStartup");function ce(){return Ve??=nr().catch(t=>{throw S={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},t})}a(ce,"resumeStartup");try{process.on("SIGINT",Se),process.on("SIGTERM",ye),process.on("exit",_e),sn(),p!==null&&m({level:"info",event:"daemon.person.resolved",fields:{person_id:p.id,source:p.source}}),d!==null&&!d.alreadyDone&&m({level:"info",event:"daemon.packages.install_migration_done",fields:{moved:d.moved,discarded:d.discarded,people:d.people}}),n!==null&&m({level:"info",event:"daemon.permissions.boot",fields:{...n}}),c!==null&&m({level:"info",event:"daemon.person.retired",fields:{person_id:c.person,joined_from:c.joinedFrom,retired:c.retired,columns:c.columns}}),N(e.role)?W=await wr():hr(ur());const t=g.token;Lt({epoch:t,requestHandover:Xe,registerDetached:k,startupBlock:Ye,admitStartup:rr,repairMigrations:tr}),qe(),L=N(e.role)?Tr({tcp:e.tcp}):Hn(e),await L.ready;const i=fr()??le("user").document_port;if(N(e.role)){const r=Lr(i,$r());try{await r.ready,ne=r}catch(o){m({level:"warn",event:"daemon.document_listener.bind_failed",fields:{port:i,error:o instanceof Error?o.message:String(o)}})}}const h=le("user").runtime;if(h?.issuer&&(I=qr(h.issuer,k),h.unsandboxed===!0&&(F=cr({syncGrants:I}))),e.role==="api"){const r=a(()=>k(Ur().catch(o=>{m({level:"error",event:"daemon.packages.sync_failed",error:o})})),"packageTick");r(),O=setInterval(r,Ae*1e3),O.unref()}if(N(e.role)&&(oe=await jr()),ie=await zr({serves:N(e.role),runsLanes:Q(e.role)}),h?.unsandboxed===!0&&h.listener!==void 0&&m({level:"warn",event:"daemon.app_listener.refused_unsandboxed",fields:{reason:"runtime.unsandboxed is set; runtime.listener is ignored"}}),N(e.role)&&h!==void 0&&h.listener!==void 0&&h.unsandboxed!==!0&&(M=await Ar(h,async()=>await Kr(h.issuer.replace(/\/$/,"")),I),await M.ready),e.role===void 0)try{await cn()}catch(r){if($=null,r instanceof ln)return S={phase:"blocked",message:r.message,blockerPaths:r.blockerPaths},x;throw r}process.platform==="linux"&&R(T)&&(_r(),await yr("app:terminal"));try{e.prepare?S={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await ce(),S={phase:"ready",message:"",blockerPaths:[]})}finally{$=null}return x}catch(t){try{await x()}catch(i){throw new AggregateError([t,i],"daemon startup failed and cleanup also failed")}throw t}}a(Un,"runDaemon");var zi=Un;export{Bn as API_DRAIN_GRACE_MS,Wi as brokerCommandDeclaresCanvasHome,ue as brokerCommandEpoch,zi as default,Q as roleRunsLanes,N as roleServesRequests,Un as runDaemon,Fn as superviseTick,Sn as teardownFleet};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var v=Object.defineProperty;var a=(t,e)=>v(t,"name",{value:e,configurable:!0});import{openDb as
|
|
1
|
+
var v=Object.defineProperty;var a=(t,e)=>v(t,"name",{value:e,configurable:!0});import{openDb as l}from"../../core/canvas/db.js";import{isCloudRole as _}from"../../core/canvas/person.js";import{endLiveExecution as E,liveExecutionForNode as y}from"../../core/canvas/executions.js";import{emitEvent as c}from"../../core/events/emit.js";import{afterRevocationStop as S}from"../../core/grants/on-sync.js";import{allGrants as u,grantIdsWhere as h,grantStanding as m,mirrorOnly as g}from"../../core/grants/mirror.js";import{removeApp as b,removePersonData as k}from"../../core/grants/remove.js";import{wakeAllGrantWaiters as x,wakeGrantWaiters as N}from"../../core/grants/watch.js";import{discardOrphanedTokens as p,discardTokensForGrant as O}from"../../core/providers/token-cache.js";import{runnerForExecution as T}from"../../core/runtime/runner/index.js";import{closeGrantStreams as L,heldGrantIds as R}from"../../core/runs/events.js";import{stopNodeSandboxes as D}from"../../core/spaces/stop.js";import{brokerSignalBus as G}from"../broker-signals/bus.js";import{withRowClaim as P}from"./row-claim.js";const A=6e4;function f(t,e,r){c({level:"error",event:`daemon.grants.${t}`,error:e,fields:r})}a(f,"failed");function C(t,e,r){try{const o=e.sub===null||e.grantee===null?p(r):O({sub:e.sub,grantee:e.grantee,active:e.standing==="active"},r);o.tokens>0&&c({level:"info",event:"daemon.grants.outbound_tokens_discarded",fields:{grant:t,tokens:o.tokens}})}catch(o){f("outbound_tokens_discard_failed",o,{grant:t})}}a(C,"discardTokens");async function w(){try{await(await import("../../core/providers/bundles.js")).syncProviderBundles()}catch(t){c({level:"error",event:"daemon.providers.bundle_sync_failed",error:t})}}a(w,"reconcileProviderBundles");async function W(t,e){if(N(t),e.standing==="inactive"){const r=L(t);r>0&&c({level:"info",event:"daemon.grants.streams_closed",fields:{grant:t,grantee:e.grantee,streams:r}})}await w()}a(W,"serveEffects");async function F(t,e){const r=await P("nodes",t,A,async o=>{const n=await y(t);if(n===null)return;await o.assertOwned(),c({level:"info",event:"daemon.grants.node_stop",node_id:t,fields:{grant:e,execution_id:n.executionId}});const i=a(async()=>{await o.assertOwned(),await E(t,{reason:"permission_revoked"}),await S(t)},"finish");if(n.runner==="process"){const d=await D(t,"permission_revoked",{holdRecovery:!0,after:i});if(!d.confirmed)throw d.error??new Error(`stop of ${t} was not confirmed`);return}const s=await T(n.runner).stop(n,{reason:"permission_revoked"});if(!s.confirmed)throw s.error??new Error(`stop of ${t} was not confirmed`);await i()})}a(F,"stopRevokedNode");async function M(t,e){if(!(e.grantee!==null&&g({grantee:e.grantee}))){if(e.standing==="inactive"){const r=await(await l()).prepare("SELECT DISTINCT n.node_id FROM nodes n JOIN executions e ON e.node_id = n.node_id AND e.state != 'exited' WHERE n.grant_id = ?").all(t);for(const{node_id:o}of r)try{await F(o,t)}catch(n){f("node_stop_failed",n,{grant:t,node_id:o})}}if(e.status==="removed"&&e.removal_done_at===null&&e.grantee!==null&&e.sub!==null)try{e.grantee!=="app:terminal"?await b(e.grantee,e.sub):_()?await k(e.grantee,e.sub):c({level:"info",event:"daemon.grants.removal_settled_without_cleanup",fields:{grant:t,grantee:e.grantee,sub:e.sub}}),await(await l()).prepare("UPDATE grants SET removal_done_at = ? WHERE id = ? AND removal_done_at IS NULL").run(new Date().toISOString(),t)}catch(r){f("removal_failed",r,{grant:t,grantee:e.grantee})}}}a(M,"laneEffects");async function U(t,e){const r=await m(e);C(e,r,await u()),await Promise.all([t.serves?W(e,r):void 0,t.runsLanes&&r.standing!=="lagging"?M(e,r):void 0])}a(U,"applyGrantEffects");async function $(t){const e=new Set;if(t.serves)for(const o of R())e.add(o);if(t.runsLanes){const o=await(await l()).prepare("SELECT DISTINCT n.grant_id FROM nodes n JOIN executions e ON e.node_id = n.node_id AND e.state != 'exited' WHERE n.grant_id IS NOT NULL").all();for(const{grant_id:n}of o)e.add(n)}const r=[];for(const o of e)(await m(o)).status===null&&r.push(o);return r}a($,"goneGrantIds");async function re(t){const e=new Map,r=a(n=>{const i=(e.get(n)??Promise.resolve()).then(()=>U(t,n)).catch(s=>f("effects_failed",s,{grant:n})).finally(()=>{e.get(n)===i&&e.delete(n)});return e.set(n,i),i},"run"),o=a(async()=>{const n=t.serves?w():void 0;try{t.serves&&x(),p(await u());const i=new Set(await h(`status <> 'active' OR expires_at <= '${new Date().toISOString()}'`));for(const s of await $(t))i.add(s);await Promise.all([...i].map(r))}catch(i){f("catch_up_failed",i,{})}await n},"catchUp");return await G.subscribe(n=>{r(n)},{kinds:["grant"],onListenStart:a(()=>{o()},"onListenStart")})}a(re,"startGrantEffects");export{U as applyGrantEffects,re as startGrantEffects};
|
package/dist/shared/env.d.ts
CHANGED
|
@@ -16,6 +16,10 @@ export declare function isSandboxedNode(): boolean;
|
|
|
16
16
|
* descriptions (`GET /v1/providers/tools` through that relay); the relay's node token stays the authority. It is not `isSandboxedNode()`,
|
|
17
17
|
* which gates owner-only commands and launch-snapshot reads on a Linux launcher mount. */
|
|
18
18
|
export declare function providerProjectionOverBrokerRelay(): boolean;
|
|
19
|
+
/** True when this CLI's plugin and marketplace installs are its app's install block on the control plane (`app-plugins.replace` over `/v1`), not
|
|
20
|
+
* directories in a scope: a Linux launcher sandbox (`isSandboxedNode()`) or a fleet node reaching `/v1` through its broker relay
|
|
21
|
+
* (`providerProjectionOverBrokerRelay()`, which has no launcher mount). The `pkg plugin` and `pkg market` leaves choose their path with it. */
|
|
22
|
+
export declare function installsLiveInAppBlock(): boolean;
|
|
19
23
|
/** The app that owns the current node, supplied in its broker launch identity. */
|
|
20
24
|
export declare function envGrantee(): string | undefined;
|
|
21
25
|
/** Set to '1' by `nodeEnv` for an app-started run's brokers, which admit inbox
|
package/dist/shared/env.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var p=Object.defineProperty;var e=(n,r)=>p(n,"name",{value:r,configurable:!0});import{lstatSync as T,readFileSync as c,readdirSync as f}from"node:fs";import{join as i}from"node:path";import{covers as _}from"@north-light/identity";function
|
|
2
|
-
`).map(o=>o.split(" ")[4])),t=f(n,{withFileTypes:!0}).filter(o=>o.isDirectory()&&/^[a-zA-Z0-9][a-zA-Z0-9-]{0,127}$/.test(o.name)&&r.has(i(n,o.name))&&d(i(n,o.name))!==void 0);return t.length===1?t[0].name:void 0}catch{return}}e(E,"sandboxCliNodeId");function
|
|
1
|
+
var p=Object.defineProperty;var e=(n,r)=>p(n,"name",{value:r,configurable:!0});import{lstatSync as T,readFileSync as c,readdirSync as f}from"node:fs";import{join as i}from"node:path";import{covers as _}from"@north-light/identity";function h(){return process.env.CRTR_NODE_ID}e(h,"envNodeId");function d(n){for(const r of["broker-launch.json","cron-launch.json"]){const t=i(n,r);if(T(t,{throwIfNoEntry:!1})?.isFile())return t}}e(d,"launchSnapshotFile");function E(){if(process.platform==="linux")try{const n="/run/crtr/nodes",r=new Set(c("/proc/self/mountinfo","utf8").split(`
|
|
2
|
+
`).map(o=>o.split(" ")[4])),t=f(n,{withFileTypes:!0}).filter(o=>o.isDirectory()&&/^[a-zA-Z0-9][a-zA-Z0-9-]{0,127}$/.test(o.name)&&r.has(i(n,o.name))&&d(i(n,o.name))!==void 0);return t.length===1?t[0].name:void 0}catch{return}}e(E,"sandboxCliNodeId");function v(){return E()!==void 0}e(v,"isSandboxedNode");function C(){return/^https?:\/\//.test(process.env.CRTR_API_URL??"")&&(process.env.CRTR_API_SOCKET??"")!==""}e(C,"providerProjectionOverBrokerRelay");function H(){return v()||C()}e(H,"installsLiveInAppBlock");function K(){return process.env.CRTR_GRANTEE}e(K,"envGrantee");const a="CRTR_INBOX_BETWEEN_TURNS";function k(){return process.env[a]==="1"}e(k,"envInboxBetweenTurns");const A="CRTR_SCOPES_FILE";function y(){const n=process.env[A];if(n!==void 0&&n!==""){const t=JSON.parse(c(n,"utf8"));if(!Array.isArray(t)||t.some(o=>typeof o!="string"))throw new Error(`invalid scopes file ${n}`);return t}const r=process.env.CRTR_SCOPES;return r===void 0?null:r===""?[]:r.split(",")}e(y,"envScopes");function G(n,r){return n==null?!0:S(n,r)}e(G,"scopeAllowed");function S(n,r){return n.includes(r)?(_(r,r),!0):n.some(t=>_(t,r))}e(S,"coveredByAny");function b(){return process.env.CRTR_EXECUTION_ID}e(b,"envExecutionId");function w(){return process.env.CRTR_PROFILE_ID}e(w,"envProfileId");function g(){return process.env.CRTR_NODE_CWD}e(g,"envNodeCwd");function V(){return process.env.CRTR_MODEL_INTENT}e(V,"envModelIntent");function X(){return process.env.CRTR_MODEL_EXACT==="1"}e(X,"envModelExact");function j(){return process.env.CRTR_HOME}e(j,"envHomeOverride");function Y(){return process.env.CRTR_RUNTIME_HOME}e(Y,"envRuntimeHomeOverride");function s(n,r){if(n===void 0)return r;const t=Number(n);return Number.isFinite(t)&&t>0?t:r}e(s,"parsePositiveMs");const M=5*6e4;function $(){return s(process.env.CRTR_STREAM_WATCHDOG_MS,M)}e($,"envStreamWatchdogMs");const N=800;function z(){return s(process.env.CRTR_WATCHER_TICK_MS,N)}e(z,"envWatcherTickMs");const x=6e4;function Z(){return s(process.env.CRTR_WATCHER_BACKSTOP_MS,x)}e(Z,"envWatcherBackstopMs");const D=1e3;function J(){return s(process.env.CRTR_WATCHER_DEBOUNCE_MS,D)}e(J,"envWatcherDebounceMs");const O=6e4;function q(){return s(process.env.CRTR_RECAP_IDLE_MS,O)}e(q,"envRecapIdleMs");function Q(){const n=process.env.CRTR_RECAP_MODEL;return n!==void 0&&n.trim()!==""?n.trim():void 0}e(Q,"envRecapModelOverride");const l=300*1e3;function ee(){return s(process.env.CRTR_BASH_VALVE_MS,l)}e(ee,"envBashValveMs");const I=60*1e3;function ne(){return s(process.env.CRTR_BASH_AUTO_BG_DEADLINE_MS,I)}e(ne,"envBashAutoBgDeadlineMs");function re(){const n=process.env.CRTR_PIDFILE;return n!==void 0&&n!==""?n:void 0}e(re,"envPidfileOverride");const m="@earendil-works/pi-coding-agent";function te(){return process.env.CRTR_BROKER_ENGINE??m}e(te,"envBrokerEngine");function oe(){return process.env.CRTR_PI_BINARY}e(oe,"envPiBinaryOverride");function se(){return process.env.CRTR_NO_DAEMON_AUTOSTART==="1"}e(se,"envNoDaemonAutostart");function ue(){const n=process.env.CRTR_DOCUMENT_PORT;if(n===void 0||!/^\d+$/.test(n))return;const r=Number(n);return r<=65535?r:void 0}e(ue,"envDocumentPort");function ie(){return process.env.CRTRD_TCP}e(ie,"envCrtrdTcp");function ce(){const n=process.env.CRTRD_TOKEN;return n!==void 0&&n!==""?n:void 0}e(ce,"envCrtrdToken");function _e(){return process.env.CRTR_NO_EXPORTS==="1"}e(_e,"envNoExports");function Re(){return process.env.CRTR_DEBUG==="1"}e(Re,"envDebug");function R(n){if(n===void 0||n==="")return;const r=Number(n);return Number.isSafeInteger(r)&&r>=1?r:void 0}e(R,"parsePositiveInteger");function pe(){return R(process.env.CRTR_MAX_LIVE_BROKERS)}e(pe,"envMaxLiveBrokers");function Te(){return R(process.env.CRTR_WARN_LIVE_BROKERS)}e(Te,"envWarnLiveBrokers");function u(n){if(n===void 0)return;const r=Number(n);return Number.isFinite(r)&&r>0?r:void 0}e(u,"parsePositiveMsNoDefault");function fe(){return u(process.env.CRTR_TEST_NODE_SOCKET_WINDOW_MS)}e(fe,"envTestNodeSocketWindowMs");function de(){return u(process.env.CRTR_TEST_UNATTENDED_PARK_MS)}e(de,"envTestUnattendedParkMs");function Ee(){return u(process.env.CRTR_UNATTENDED_PARK_MS)}e(Ee,"envUnattendedParkMs");function ve(){return u(process.env.CRTR_BASH_SILENCE_MS)??15*6e4}e(ve,"envBashSilenceMs");function B(){return u(process.env.CRTR_TEST_PARK_SUMMARY_GRACE_MS)}e(B,"envTestParkSummaryGraceMs");const P=5*6e4;function Ce(){return B()??P}e(Ce,"parkSummaryGraceMs");export{a as INBOX_BETWEEN_TURNS_ENV,A as SCOPES_FILE_ENV,S as coveredByAny,ne as envBashAutoBgDeadlineMs,ve as envBashSilenceMs,ee as envBashValveMs,te as envBrokerEngine,ie as envCrtrdTcp,ce as envCrtrdToken,Re as envDebug,ue as envDocumentPort,b as envExecutionId,K as envGrantee,j as envHomeOverride,k as envInboxBetweenTurns,pe as envMaxLiveBrokers,X as envModelExact,V as envModelIntent,se as envNoDaemonAutostart,_e as envNoExports,g as envNodeCwd,h as envNodeId,oe as envPiBinaryOverride,re as envPidfileOverride,w as envProfileId,q as envRecapIdleMs,Q as envRecapModelOverride,Y as envRuntimeHomeOverride,y as envScopes,$ as envStreamWatchdogMs,fe as envTestNodeSocketWindowMs,B as envTestParkSummaryGraceMs,de as envTestUnattendedParkMs,Ee as envUnattendedParkMs,Te as envWarnLiveBrokers,Z as envWatcherBackstopMs,J as envWatcherDebounceMs,z as envWatcherTickMs,H as installsLiveInAppBlock,v as isSandboxedNode,d as launchSnapshotFile,Ce as parkSummaryGraceMs,C as providerProjectionOverBrokerRelay,E as sandboxCliNodeId,G as scopeAllowed};
|