@north-light/cli 0.3.592 → 0.3.593

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.
@@ -1,5 +1,5 @@
1
1
  /** The contract level this build serves and requires. Written by `scripts/contract.mjs release`; never edit by hand. */
2
- export declare const CONTRACT_LEVEL = "1.44";
2
+ export declare const CONTRACT_LEVEL = "1.45";
3
3
  /** Request header naming the one contract level the caller needs, e.g. `1.3`. Optional: absent means no check. */
4
4
  export declare const REQUIRED_CONTRACT_HEADER = "Crouter-Required-Contract";
5
5
  /** Response header listing the contract levels the server serves, comma-separated, one per major, e.g. `1.3`. */
@@ -1 +1 @@
1
- var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.44",i="Crouter-Required-Contract",p="Crouter-Contract";function l(r){const t=/^(\d+)\.(\d+)$/.exec(r.trim());return t===null?null:{major:Number(t[1]),minor:Number(t[2])}}e(l,"parseContractLevel");function a(r){return r==null?[]:r.split(",").map(t=>l(t)).filter(t=>t!==null)}e(a,"parseServedContracts");function s(r,t){const n=l(r);return n===null?!1:(typeof t=="string"||t===null||t===void 0?a(t):t).some(u=>u.major===n.major&&u.minor>=n.minor)}e(s,"contractSatisfies");function C(r,t,n={}){if(r===void 0)return null;if(l(r)===null)return{status:400,code:"invalid_request",param:i,message:`${i} must be a contract level like 1.0.`};if(s(r,t))return null;const o=r.trim();return{status:426,code:"runtime_version_unsupported",message:t===null||a(t).length===0?`The runtime did not name its contract level; the caller requires contract ${o} or later. Roll the runtime forward.`:`The runtime serves contract ${t}; the caller requires contract ${o} or later within the same major. Roll the runtime forward, or pin an SDK whose crouterContract the runtime serves.`,details:{sdk_version:(n.legacyMinimum??"").trim(),runtime_version:n.runtimeVersion??null,required_contract:o,runtime_contract:t}}}e(C,"contractRefusal");export{p as CONTRACT_HEADER,f as CONTRACT_LEVEL,i as REQUIRED_CONTRACT_HEADER,C as contractRefusal,s as contractSatisfies,l as parseContractLevel,a as parseServedContracts};
1
+ var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.45",i="Crouter-Required-Contract",p="Crouter-Contract";function l(r){const t=/^(\d+)\.(\d+)$/.exec(r.trim());return t===null?null:{major:Number(t[1]),minor:Number(t[2])}}e(l,"parseContractLevel");function a(r){return r==null?[]:r.split(",").map(t=>l(t)).filter(t=>t!==null)}e(a,"parseServedContracts");function s(r,t){const n=l(r);return n===null?!1:(typeof t=="string"||t===null||t===void 0?a(t):t).some(u=>u.major===n.major&&u.minor>=n.minor)}e(s,"contractSatisfies");function C(r,t,n={}){if(r===void 0)return null;if(l(r)===null)return{status:400,code:"invalid_request",param:i,message:`${i} must be a contract level like 1.0.`};if(s(r,t))return null;const o=r.trim();return{status:426,code:"runtime_version_unsupported",message:t===null||a(t).length===0?`The runtime did not name its contract level; the caller requires contract ${o} or later. Roll the runtime forward.`:`The runtime serves contract ${t}; the caller requires contract ${o} or later within the same major. Roll the runtime forward, or pin an SDK whose crouterContract the runtime serves.`,details:{sdk_version:(n.legacyMinimum??"").trim(),runtime_version:n.runtimeVersion??null,required_contract:o,runtime_contract:t}}}e(C,"contractRefusal");export{p as CONTRACT_HEADER,f as CONTRACT_LEVEL,i as REQUIRED_CONTRACT_HEADER,C as contractRefusal,s as contractSatisfies,l as parseContractLevel,a as parseServedContracts};
@@ -61,6 +61,12 @@ export interface ImportRunDTO {
61
61
  verify: ImportVerifyDTO | null;
62
62
  failure: ImportFailureDTO | null;
63
63
  unresolved_plugins: string[];
64
+ /** Plugins whose marketplace source the directory would not publish: the install is imported, its documents become the person's own copies. `holder` names the source that holds a `name_taken` name. */
65
+ refused_plugins?: Array<{
66
+ plugin: string;
67
+ reason: 'name_taken' | 'not_public' | 'not_found';
68
+ holder?: string;
69
+ }>;
64
70
  created: string;
65
71
  loaded: string | null;
66
72
  finished: string | null;
package/dist/cli.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- var R=Object.defineProperty;var s=(r,o)=>R(r,"name",{value:o,configurable:!0});import"./suppress-experimental-warnings.js";import{mkdirSync as _,openSync as b,readFileSync as E,renameSync as I,rmSync as C,statSync as g,writeFileSync as A}from"node:fs";import{dirname as h,join as a}from"node:path";import{enableCompileCache as N}from"node:module";import{fileURLToPath as v}from"node:url";import{isDaemonControlInvocation as f,isJoinInvocation as O,isLayoutMigrationInvocation as k,isPkgInstallInvocation as x,isSignInInvocation as $,personOnlyInvocation as P,resolveDaemonControlRoot as T,resolveLayoutMigrationRoot as F,resolveRoot as M}from"./build-root.js";import{maybeBootRoot as D}from"./core/runtime/front-door.js";import{maybeAutoUpdate as L}from"./core/auto-update.js";import{ensureProjectScope as U}from"./core/bootstrap.js";import{provisionExports as j,pruneLegacyExports as K}from"./core/host-exports/export.js";import{revalidateBundlePlugins as B}from"./core/command-plugins/revalidate.js";import{renderNode as J,runCli as y,walk as V}from"./core/command.js";import{dispatchWithStaleRecovery as W}from"./core/manifest-recovery.js";import{installStdoutErrorPolicy as q}from"./core/stdout-errors.js";import{handle as G}from"./core/io.js";import{CrtrError as m,ownerOnly as z}from"./core/errors.js";import{ExitCode as H}from"./types.js";import{layoutNeedsMigration as Q,runtimeRoot as X}from"./core/layout.js";import{ApiError as w}from"./api/index.js";import{mark as i}from"./core/timing.js";import{launchSnapshotFile as Y,sandboxCliNodeId as Z}from"./shared/env.js";import{nodeRunDir as d}from"./core/canvas/paths.js";import{installBrokerConfigSnapshot as rr}from"./core/config.js";b(v(import.meta.url),"r"),q(),i("cli.entry",{cmd:process.argv[2]??"(bare)"});try{N()}catch{}i("cli.compile_cache");const or=a(h(v(import.meta.url)),".."),l=a(X(),"first-run.json");function c(r){return(r instanceof Error?r.message:String(r)).split(/\r?\n/,1)[0]??""}s(c,"errorMessage");function tr(r){const o=h(l);_(o,{recursive:!0});const e=`${l}.${process.pid}.${Date.now()}.tmp`;try{A(e,JSON.stringify({packageVersion:r,completedAt:new Date().toISOString()})+`
3
- `,"utf8"),I(e,l)}finally{C(e,{force:!0})}}s(tr,"writeFirstRunMarker");async function er(){if(process.env.CRTR_NO_AUTO_INIT==="1")return;try{if(g(l,{throwIfNoEntry:!1})!==void 0)return}catch(o){process.stderr.write(`crtr: first-run reconcile marker check failed: ${c(o)}
4
- `);return}try{if(g(a(or,".git"),{throwIfNoEntry:!1})!==void 0)return}catch(o){process.stderr.write(`crtr: first-run reconcile source check failed: ${c(o)}
5
- `);return}let r;try{const{reconcileFirstRun:o}=await import("./commands/sys/setup-core.js");r=await o((e,p)=>{process.stderr.write(`crtr: first-run reconcile ${e} failed: ${c(p)}
2
+ var S=Object.defineProperty;var s=(r,o)=>S(r,"name",{value:o,configurable:!0});import"./suppress-experimental-warnings.js";import{mkdirSync as _,openSync as b,readFileSync as E,renameSync as I,rmSync as C,statSync as g,writeFileSync as O}from"node:fs";import{dirname as h,join as a}from"node:path";import{enableCompileCache as k}from"node:module";import{fileURLToPath as v}from"node:url";import{isDaemonControlInvocation as f,isJoinInvocation as A,isLayoutMigrationInvocation as N,isPkgInstallInvocation as x,isSignInInvocation as $,personOnlyInvocation as P,resolveDaemonControlRoot as T,resolveLayoutMigrationRoot as F,resolveRoot as M}from"./build-root.js";import{maybeBootRoot as j}from"./core/runtime/front-door.js";import{maybeAutoUpdate as D}from"./core/auto-update.js";import{ensureProjectScope as L}from"./core/bootstrap.js";import{provisionExports as U,pruneLegacyExports as B}from"./core/host-exports/export.js";import{revalidateBundlePlugins as K}from"./core/command-plugins/revalidate.js";import{renderNode as J,runCli as y,walk as V}from"./core/command.js";import{dispatchWithStaleRecovery as W}from"./core/manifest-recovery.js";import{installStdoutErrorPolicy as q}from"./core/stdout-errors.js";import{handle as G}from"./core/io.js";import{CrtrError as m,ownerOnly as z}from"./core/errors.js";import{ExitCode as H}from"./types.js";import{layoutNeedsMigration as Q,runtimeRoot as X}from"./core/layout.js";import{ApiError as w}from"./api/index.js";import{mark as i}from"./core/timing.js";import{launchSnapshotFile as Y,providerProjectionOverBrokerRelay as Z,sandboxCliNodeId as rr}from"./shared/env.js";import{nodeRunDir as d}from"./core/canvas/paths.js";import{installBrokerConfigSnapshot as or}from"./core/config.js";b(v(import.meta.url),"r"),q(),i("cli.entry",{cmd:process.argv[2]??"(bare)"});try{k()}catch{}i("cli.compile_cache");const er=a(h(v(import.meta.url)),".."),l=a(X(),"first-run.json");function c(r){return(r instanceof Error?r.message:String(r)).split(/\r?\n/,1)[0]??""}s(c,"errorMessage");function tr(r){const o=h(l);_(o,{recursive:!0});const t=`${l}.${process.pid}.${Date.now()}.tmp`;try{O(t,JSON.stringify({packageVersion:r,completedAt:new Date().toISOString()})+`
3
+ `,"utf8"),I(t,l)}finally{C(t,{force:!0})}}s(tr,"writeFirstRunMarker");async function ir(){if(process.env.CRTR_NO_AUTO_INIT==="1")return;try{if(g(l,{throwIfNoEntry:!1})!==void 0)return}catch(o){process.stderr.write(`crtr: first-run reconcile marker check failed: ${c(o)}
4
+ `);return}try{if(g(a(er,".git"),{throwIfNoEntry:!1})!==void 0)return}catch(o){process.stderr.write(`crtr: first-run reconcile source check failed: ${c(o)}
5
+ `);return}let r;try{const{reconcileFirstRun:o}=await import("./commands/sys/setup-core.js");r=await o((t,p)=>{process.stderr.write(`crtr: first-run reconcile ${t} failed: ${c(p)}
6
6
  `)})}catch(o){process.stderr.write(`crtr: first-run reconcile failed: ${c(o)}
7
7
  `)}finally{if(r!==void 0)try{tr(r)}catch(o){process.stderr.write(`crtr: first-run reconcile marker failed: ${c(o)}
8
- `)}}}s(er,"maybeReconcileFirstRun");async function ir(){const r=k(process.argv),o=Z(),e=o!==void 0;if(o!==void 0){process.env.CRTR_API_SOCKET=a(d(o),"cli.sock");const t=P(process.argv);if(t!==null)throw z(`crtr ${t} is not available to an app's node`,"Run this command from the person's terminal.");const n=JSON.parse(E(Y(d(o))??a(d(o),"broker-launch.json"),"utf8"));if(!n.configSnapshot)throw new Error("node launch is missing its config snapshot");rr(n.configSnapshot)}if(!e&&Q()&&!f(process.argv)&&!r)throw new m("layout_migration_required","run crtr sys migrate while the runtime is offline",H.NETWORK,{next:"Run `crtr sys migrate` while the runtime is offline, then restart it."});if(r){const t=await F();if(process.argv.slice(4).some(n=>n==="-h"||n==="--help")){process.stdout.write(J(V(t,["sys","migrate"]).node)+`
9
- `);return}await y(t,process.argv);return}if((!e||x(process.argv))&&!f(process.argv)&&!O(process.argv)&&!$(process.argv)){try{await(await import("./commands/person-settings.js")).loadPersonSettingsViaApi()}catch(t){const n=process.argv.slice(2).some(u=>u==="-h"||u==="--help"),S=t instanceof w&&(t.code==="daemon_unavailable"||t.code==="northlight_unreachable")||t instanceof m&&(t.code==="not_signed_in"||t.code==="move_unfinished"||t.code==="northlight_unreachable");if(!(n&&S))throw t;(await import("./core/scoped-state/person-store.js")).installPreludeSnapshot({profiles:[],defaults:{},settings:{},state:{},apps:{}})}i("cli.settings_loaded")}if(e||(K(process.argv),await er()),await D(process.argv),i("cli.front_door_passed"),f(process.argv)){await y(await T(),process.argv);return}e||await B(process.argv),i("cli.plugins_revalidated");const p=await M(process.argv[2]);i("cli.root_resolved"),e||(j(p,process.argv),await U(process.argv),await L(process.argv)),i("cli.dispatch_start"),await W(p,process.argv),i("cli.dispatch_done")}s(ir,"main"),ir().catch(r=>{(r instanceof Error&&r.name==="AbortError"||r instanceof Error&&/operation was aborted/i.test(r.message))&&(process.stderr.write(`crtr: Interrupted
10
- `),process.exit(130)),(r instanceof w||r instanceof m)&&(G(r),process.exit(typeof process.exitCode=="number"?process.exitCode:1));const e=r instanceof Error?r.message:String(r);process.stderr.write(`crtr: ${e}
8
+ `)}}}s(ir,"maybeReconcileFirstRun");async function nr(){const r=N(process.argv),o=rr(),t=o!==void 0;if(o!==void 0){process.env.CRTR_API_SOCKET=a(d(o),"cli.sock");const e=P(process.argv);if(e!==null)throw z(`crtr ${e} is not available to an app's node`,"Run this command from the person's terminal.");const n=JSON.parse(E(Y(d(o))??a(d(o),"broker-launch.json"),"utf8"));if(!n.configSnapshot)throw new Error("node launch is missing its config snapshot");or(n.configSnapshot)}if(!t&&Q()&&!f(process.argv)&&!r)throw new m("layout_migration_required","run crtr sys migrate while the runtime is offline",H.NETWORK,{next:"Run `crtr sys migrate` while the runtime is offline, then restart it."});if(r){const e=await F();if(process.argv.slice(4).some(n=>n==="-h"||n==="--help")){process.stdout.write(J(V(e,["sys","migrate"]).node)+`
9
+ `);return}await y(e,process.argv);return}if((!t||x(process.argv))&&!f(process.argv)&&!A(process.argv)&&!$(process.argv)){try{await(await import("./commands/person-settings.js")).loadPersonSettingsViaApi()}catch(e){const n=process.argv.slice(2).some(u=>u==="-h"||u==="--help"),R=e instanceof w&&(e.code==="daemon_unavailable"||e.code==="northlight_unreachable")||e instanceof m&&(e.code==="not_signed_in"||e.code==="move_unfinished"||e.code==="northlight_unreachable");if(!(n&&R))throw e;(await import("./core/scoped-state/person-store.js")).installPreludeSnapshot({profiles:[],defaults:{},settings:{},state:{},apps:{}})}i("cli.settings_loaded")}if(t||(B(process.argv),Z()||await ir()),await j(process.argv),i("cli.front_door_passed"),f(process.argv)){await y(await T(),process.argv);return}t||await K(process.argv),i("cli.plugins_revalidated");const p=await M(process.argv[2]);i("cli.root_resolved"),t||(U(p,process.argv),await L(process.argv),await D(process.argv)),i("cli.dispatch_start"),await W(p,process.argv),i("cli.dispatch_done")}s(nr,"main"),nr().catch(r=>{(r instanceof Error&&r.name==="AbortError"||r instanceof Error&&/operation was aborted/i.test(r.message))&&(process.stderr.write(`crtr: Interrupted
10
+ `),process.exit(130)),(r instanceof w||r instanceof m)&&(G(r),process.exit(typeof process.exitCode=="number"?process.exitCode:1));const t=r instanceof Error?r.message:String(r);process.stderr.write(`crtr: ${t}
11
11
  `),process.exit(1)});
@@ -1 +1 @@
1
- var y=Object.defineProperty;var m=(r,o)=>y(r,"name",{value:o,configurable:!0});import{defineLeaf as k}from"../../core/command.js";import{notFound as x}from"../../core/errors.js";import{paginate as g}from"../../core/pagination.js";import{listInstalledPlugins as q,listInstalledMarketplaces as w,listAllMarketplaces as h,findMarketplaceByName as b}from"../../core/resolver.js";import{resolveScopeArg as j,projectScopeRoot as M}from"../../core/scope.js";import{isSandboxedNode as _}from"../../shared/env.js";import{listAppMarketplaces as v}from"./app-installs.js";const D=k({name:"list",description:"list registered marketplaces",whenToUse:"listing registered marketplace sources with their git URL, ref, scope, and clone path",help:{name:"pkg market list",summary:"list registered marketplaces",params:[{kind:"flag",name:"scope",type:"enum",choices:["user","project","all"],required:!1,constraint:"Default: all."},{kind:"flag",name:"limit",type:"int",required:!1,default:50,constraint:"Default 50, max 200."},{kind:"flag",name:"cursor",type:"string",required:!1,constraint:"Opaque token from next_cursor. Omit on first call."}],output:[{name:"items",type:"object[]",required:!0,constraint:"Each: {name, scope, url, ref, path}. Sorted by scope then name ascending."},{name:"next_cursor",type:"string | null",required:!0,constraint:"null means no more items."},{name:"total",type:"integer | null",required:!0,constraint:"Total count."}],outputKind:"object",effects:["None. Read-only."]},run:m(async r=>{if(_())return v();const o=r.scope,l=r.limit,u=l!==void 0?Math.min(Math.max(1,l),200):50,c=r.cursor;let n;if(o!==void 0){const e=j(o);e==="all"?n=h():e==="builtin"?n=[]:n=w(e)}else n=h();const p=[...n].sort((e,i)=>e.scope==="project"&&i.scope!=="project"?-1:e.scope!=="project"&&i.scope==="project"?1:e.name.localeCompare(i.name)),s=g(p,{limit:u,cursor:c!==void 0?c:void 0},{defaultLimit:50,maxLimit:200,keyOf:m(e=>`${e.scope}:${e.name}`,"keyOf"),total:"count"});return{items:s.items.map(e=>({name:e.name,scope:e.scope,url:e.url,ref:e.ref,path:e.root})),next_cursor:s.next_cursor,total:s.total}},"run")}),E=k({name:"browse",description:"list plugins available in a marketplace",whenToUse:"browsing one registered marketplace source to choose a plugin install ref; each item includes ref=marketplace/plugin for `pkg plugin install`",help:{name:"pkg market browse",summary:"list plugins available in a marketplace",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Marketplace name."},{kind:"flag",name:"limit",type:"int",required:!1,default:50,constraint:"Default 50, max 200."},{kind:"flag",name:"cursor",type:"string",required:!1,constraint:"Opaque token from next_cursor. Omit on first call."}],output:[{name:"marketplace",type:"string",required:!0,constraint:"Echo of the input marketplace name."},{name:"items",type:"object[]",required:!0,constraint:"Each: {name, ref, source, version?, description?, keywords?, installed, installed_scope?}. ref is marketplace/plugin. Sorted by name ascending."},{name:"next_cursor",type:"string | null",required:!0,constraint:"null means no more items."},{name:"total",type:"integer | null",required:!0,constraint:"Total plugins in the marketplace; null if unavailable."}],outputKind:"object",effects:["None. Read-only."]},run:m(async r=>{const o=r.name,l=r.limit,u=l!==void 0?Math.min(Math.max(1,l),200):50,c=r.cursor,n=b(o);if(!n)throw x(`marketplace not found: ${o}`);const p=["project","user"].filter(t=>t!=="project"||M()!==null),s=new Map;for(const t of p)for(const a of q(t))s.has(a.name)||s.set(a.name,a.scope);const e=[...n.manifest.plugins].sort((t,a)=>t.name.localeCompare(a.name)),i=g(e,{limit:u,cursor:c!==void 0?c:void 0},{defaultLimit:50,maxLimit:200,keyOf:m(t=>t.name,"keyOf"),total:"count"});return{marketplace:n.name,items:i.items.map(t=>{const a=s.get(t.name),d=a!==void 0,f={name:t.name,ref:`${n.name}/${t.name}`,source:t.source,version:t.version,description:t.description,keywords:t.keywords,installed:d};return d&&(f.installed_scope=a),f}),next_cursor:i.next_cursor,total:i.total}},"run")});export{E as marketBrowse,D as marketList};
1
+ var y=Object.defineProperty;var m=(r,o)=>y(r,"name",{value:o,configurable:!0});import{defineLeaf as k}from"../../core/command.js";import{notFound as x}from"../../core/errors.js";import{paginate as g}from"../../core/pagination.js";import{listInstalledPlugins as q,listInstalledMarketplaces as w,listAllMarketplaces as h,findMarketplaceByName as j}from"../../core/resolver.js";import{resolveScopeArg as b,projectScopeRoot as M}from"../../core/scope.js";import{installsLiveInAppBlock as _}from"../../shared/env.js";import{listAppMarketplaces as v}from"./app-installs.js";const T=k({name:"list",description:"list registered marketplaces",whenToUse:"listing registered marketplace sources with their git URL, ref, scope, and clone path",help:{name:"pkg market list",summary:"list registered marketplaces",params:[{kind:"flag",name:"scope",type:"enum",choices:["user","project","all"],required:!1,constraint:"Default: all."},{kind:"flag",name:"limit",type:"int",required:!1,default:50,constraint:"Default 50, max 200."},{kind:"flag",name:"cursor",type:"string",required:!1,constraint:"Opaque token from next_cursor. Omit on first call."}],output:[{name:"items",type:"object[]",required:!0,constraint:"Each: {name, scope, url, ref, path}. Sorted by scope then name ascending."},{name:"next_cursor",type:"string | null",required:!0,constraint:"null means no more items."},{name:"total",type:"integer | null",required:!0,constraint:"Total count."}],outputKind:"object",effects:["None. Read-only."]},run:m(async r=>{if(_())return v();const o=r.scope,l=r.limit,p=l!==void 0?Math.min(Math.max(1,l),200):50,c=r.cursor;let n;if(o!==void 0){const e=b(o);e==="all"?n=h():e==="builtin"?n=[]:n=w(e)}else n=h();const u=[...n].sort((e,i)=>e.scope==="project"&&i.scope!=="project"?-1:e.scope!=="project"&&i.scope==="project"?1:e.name.localeCompare(i.name)),s=g(u,{limit:p,cursor:c!==void 0?c:void 0},{defaultLimit:50,maxLimit:200,keyOf:m(e=>`${e.scope}:${e.name}`,"keyOf"),total:"count"});return{items:s.items.map(e=>({name:e.name,scope:e.scope,url:e.url,ref:e.ref,path:e.root})),next_cursor:s.next_cursor,total:s.total}},"run")}),B=k({name:"browse",description:"list plugins available in a marketplace",whenToUse:"browsing one registered marketplace source to choose a plugin install ref; each item includes ref=marketplace/plugin for `pkg plugin install`",help:{name:"pkg market browse",summary:"list plugins available in a marketplace",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Marketplace name."},{kind:"flag",name:"limit",type:"int",required:!1,default:50,constraint:"Default 50, max 200."},{kind:"flag",name:"cursor",type:"string",required:!1,constraint:"Opaque token from next_cursor. Omit on first call."}],output:[{name:"marketplace",type:"string",required:!0,constraint:"Echo of the input marketplace name."},{name:"items",type:"object[]",required:!0,constraint:"Each: {name, ref, source, version?, description?, keywords?, installed, installed_scope?}. ref is marketplace/plugin. Sorted by name ascending."},{name:"next_cursor",type:"string | null",required:!0,constraint:"null means no more items."},{name:"total",type:"integer | null",required:!0,constraint:"Total plugins in the marketplace; null if unavailable."}],outputKind:"object",effects:["None. Read-only."]},run:m(async r=>{const o=r.name,l=r.limit,p=l!==void 0?Math.min(Math.max(1,l),200):50,c=r.cursor,n=j(o);if(!n)throw x(`marketplace not found: ${o}`);const u=["project","user"].filter(t=>t!=="project"||M()!==null),s=new Map;for(const t of u)for(const a of q(t))s.has(a.name)||s.set(a.name,a.scope);const e=[...n.manifest.plugins].sort((t,a)=>t.name.localeCompare(a.name)),i=g(e,{limit:p,cursor:c!==void 0?c:void 0},{defaultLimit:50,maxLimit:200,keyOf:m(t=>t.name,"keyOf"),total:"count"});return{marketplace:n.name,items:i.items.map(t=>{const a=s.get(t.name),d=a!==void 0,f={name:t.name,ref:`${n.name}/${t.name}`,source:t.source,version:t.version,description:t.description,keywords:t.keywords,installed:d};return d&&(f.installed_scope=a),f}),next_cursor:i.next_cursor,total:i.total}},"run")});export{B as marketBrowse,T as marketList};
@@ -1 +1 @@
1
- var Q=Object.defineProperty;var m=(o,n)=>Q(o,"name",{value:n,configurable:!0});import{dirname as R,join as g,relative as W,resolve as S}from"node:path";import{renameSync as y}from"node:fs";import{defineLeaf as D}from"../../core/command.js";import{general as j,notFound as d}from"../../core/errors.js";import{listAllMarketplaces as I,listAllPlugins as X,findMarketplaceByName as w}from"../../core/resolver.js";import{resolveScopeArg as C,requireScopeRoot as M,marketplacesDir as Y,pluginsDir as Z}from"../../core/scope.js";import{isSandboxedNode as G}from"../../shared/env.js";import{addAppMarketplace as ee,removeAppMarketplace as te}from"./app-installs.js";import{updateConfig as q,updateState as L,ensureScopeInitialized as oe,readConfig as ne}from"../../core/config.js";import{pathExists as $,ensureDir as _,removePath as f,isSymlink as ae,nowIso as N,readSymlinkTarget as re}from"../../core/fs-utils.js";import{clone as K,deriveNameFromUrl as ie,currentSha as O,gitSync as E,isGitRepo as F}from"../../core/git.js";import{readMarketplaceManifest as U,readPluginManifest as z}from"../../core/manifest.js";import{assertPluginCandidateValid as se,isMarketplaceRelativeSource as ce}from"./plugin-manage.js";import{resolveInstallScope as pe}from"./shared.js";async function le(o){const{url:n,ref:i,scope:t}=o,s=M(t);await oe(t,s);const p=ie(n),c=Y(t);_(c);const l=g(c,p);$(l)&&f(l),K(n,l,{depth:1,ref:i!==void 0?i:void 0});const u=U(l);if(!u)throw f(l),d(`marketplace manifest not found at ${l}/.crouter-marketplace/marketplace.json \u2014 not a valid marketplace`);const e=u.name,a=g(c,e);e!==p&&($(a)&&f(a),y(l,a));const r=i!==void 0?i:"main";return await q(t,k=>{k.marketplaces[e]={url:n,ref:r,installed_at:N()}}),{name:e,scope:t,path:a}}m(le,"addMarketplace");const qe=D({name:"add",description:"add a marketplace by git URL",whenToUse:"registering a new marketplace source repo so its plugins appear in marketplace browse output and become installable by marketplace/plugin ref",help:{name:"pkg market add",summary:"add a marketplace by git URL",params:[{kind:"positional",name:"url",type:"string",required:!0,constraint:"Git URL of the marketplace repo."},{kind:"flag",name:"ref",type:"string",required:!1,constraint:"Git ref to track. Default: main."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: project if available, else user."}],output:[{name:"name",type:"string",required:!0,constraint:"Marketplace name as declared in marketplace.json."},{name:"scope",type:"string",required:!0,constraint:"Scope the marketplace was added into."},{name:"path",type:"string",required:!0,constraint:"Absolute path to the cloned marketplace directory."}],outputKind:"object",effects:["Clones the marketplace repo. Registers the marketplace in config.json."]},run:m(async o=>{const n=o.url,i=o.ref,t=o.scope;return G()?await ee(n,i):le({url:n,ref:i,scope:pe(t)})},"run")}),Ne=D({name:"remove",description:"remove a marketplace",whenToUse:"unregistering a marketplace source: deletes the source clone and any plugins installed from it in the same scope",help:{name:"pkg market remove",summary:"remove a marketplace and its directory",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Marketplace name to remove."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Narrows resolution."}],output:[],outputKind:"object",effects:["Deletes the marketplace directory. Removes the entry from config.json. Removes plugin files and config entries sourced from that marketplace in the same scope."]},run:m(async o=>{const n=o.name;if(G())return await te(n);const i=o.scope;let t,s;if(i!==void 0){const a=C(i);if(a!=="all"&&a!=="builtin"){const r=a,k=w(n,r);if(!k)throw d(`marketplace not found: ${n} (scope: ${r})`);t=r,s=k.root}else{const r=w(n);if(!r)throw d(`marketplace not found: ${n}`);t=r.scope,s=r.root}}else{const a=w(n);if(!a)throw d(`marketplace not found: ${n}`);t=a.scope,s=a.root}const p=t,c=M(p),l=Z(p),u=ne(p),e=[];for(const[a,r]of Object.entries(u.plugins))r.source_marketplace===n&&e.push(a);for(const a of e){const r=g(l,a);f(r)}return f(s),await q(p,a=>{delete a.marketplaces[n];for(const r of e)delete a.plugins[r]}),{}},"run")});function B(o,n,i,t){try{K(o,t,{depth:1,ref:n})}catch(s){throw f(t),s}return{active:i,candidate:t,previous:`${t}.previous`}}m(B,"stageGitRoot");function ue(o){const n=[],i=[];try{for(const t of o){if($(t.previous))throw j(`prior staged backup remains at ${t.previous}`);y(t.active,t.previous),n.push(t)}for(const t of o)y(t.candidate,t.active),i.push(t)}catch(t){for(const s of[...i].reverse())y(s.active,s.candidate);for(const s of[...n].reverse())y(s.previous,s.active);throw t}for(const t of o)f(t.previous)}m(ue,"activateStagedRoots");function H(o,n,i){return{plugin:{...o,root:n,manifest:i},scopeRoot:R(R(o.root))}}m(H,"candidatePlugin");async function de(o){const n=O(o.root)??"",i=X().filter(u=>u.scope===o.scope&&u.sourceMarketplace===o.name),t=[],s=[];let p,c;const l=[];try{if(F(o.root)){const e=g(R(o.root),`.${o.name}.${process.pid}.${Date.now()}.candidate`);c=B(o.url,o.ref,o.root,e),s.push(e);const a=U(e);if(a===null)throw d(`marketplace manifest not found in candidate checkout: ${e}/.crouter-marketplace/marketplace.json`);p=a,t.push(c)}else{const e=U(o.root);if(e===null)throw d(`marketplace manifest not found after refresh: ${o.root}/.crouter-marketplace/marketplace.json`);p=e}for(const e of i){if(ae(e.root)){if(c===void 0)continue;const b=p.plugins.find(J=>J.name===e.name);if(b===void 0||!ce(b.source))throw j(`marketplace candidate removes relative-source plugin "${e.name}" while it is installed`);const P=re(e.root);if(P===null)throw j(`installed marketplace plugin "${e.name}" has no readable symlink target`);const x=W(o.root,S(R(e.root),P)),h=S(c.candidate,b.source);if(h!==S(c.candidate,x))throw j(`marketplace candidate moves installed relative-source plugin "${e.name}" from ${x} to ${b.source}`);if(!$(h))throw d(`marketplace candidate source path does not exist for plugin "${e.name}": ${h}`);const T=z(h);if(T===null)throw d(`plugin manifest not found in marketplace candidate: ${h}/.crouter-plugin/plugin.json`);l.push(H(e,h,T));continue}if(!$(e.root)||!F(e.root))continue;const a=E(["config","--get","remote.origin.url"],e.root).stdout.trim(),r=E(["symbolic-ref","--short","HEAD"],e.root).stdout.trim();if(a===""||r==="")continue;const k=e.scope==="project"?M("project"):M("user"),v=g(k,"tmp",`${e.name}.${process.pid}.${Date.now()}.candidate`);_(g(k,"tmp"));const V=B(a,r,e.root,v);s.push(v);const A=z(v);if(A===null)throw d(`plugin manifest not found in update candidate: ${v}/.crouter-plugin/plugin.json`);l.push(H(e,v,A)),t.push(V)}for(const e of l)await se(e.plugin.manifest,e.plugin.root,e.plugin.scope,e.scopeRoot,`${e.plugin.root}/.crouter-plugin/plugin.json`,l);ue(t),await L(o.scope,e=>{e.marketplaces[o.name]===void 0&&(e.marketplaces[o.name]={}),e.marketplaces[o.name].last_updated=N()});for(const e of i){const a=p.plugins.find(r=>r.name===e.name);a?.version!==void 0&&await q(e.scope,r=>{r.plugins[e.name]!==void 0&&(r.plugins[e.name].version=a.version)}),await L(e.scope,r=>{r.plugins[e.name]===void 0&&(r.plugins[e.name]={}),r.plugins[e.name].last_updated=N()})}const u=O(o.root)??"";return{name:o.name,updated:n!==u,sha:u}}finally{for(const u of s)f(u)}}m(de,"updateMarketplaceTarget");const Ue=D({name:"update",description:"replace marketplace source with its latest revision",whenToUse:"refreshing one named marketplace source from git, or all registered marketplace sources when no name is given; also updates installed plugins sourced from refreshed marketplaces",help:{name:"pkg market update",summary:"refresh one or all registered marketplaces",params:[{kind:"flag",name:"name",type:"string",required:!1,constraint:"Marketplace name to update. Omit to update all registered marketplaces."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Narrows resolution."}],output:[{name:"updated",type:"object[]",required:!0,constraint:"One entry per marketplace processed: {name, updated, sha}."}],outputKind:"object",effects:["Stages and validates marketplace and sourced-plugin revisions before atomically activating them, replacing each checkout even at an unchanged revision \u2014 local modifications inside those directories are discarded. `updated` reports whether the revision moved, not whether the checkout was replaced. Updates installed plugin content/version for plugins sourced from refreshed marketplaces."]},run:m(async o=>{const n=o.name,i=o.scope,t=i===void 0?void 0:C(i),s=n===void 0?t===void 0||t==="all"?I():t==="builtin"?[]:I().filter(c=>c.scope===t):(()=>{const c=t===void 0||t==="all"||t==="builtin"?w(n):w(n,t);if(c===null)throw d(`marketplace not found: ${n}${t===void 0||t==="all"||t==="builtin"?"":` (scope: ${t})`}`);return[c]})(),p=[];for(const c of s)p.push(await de(c));return{updated:p}},"run")});export{le as addMarketplace,qe as marketAdd,Ne as marketRemove,Ue as marketUpdate};
1
+ var Q=Object.defineProperty;var f=(o,n)=>Q(o,"name",{value:n,configurable:!0});import{dirname as R,join as g,relative as W,resolve as S}from"node:path";import{renameSync as y}from"node:fs";import{defineLeaf as D}from"../../core/command.js";import{general as j,notFound as d}from"../../core/errors.js";import{listAllMarketplaces as x,listAllPlugins as X,findMarketplaceByName as w}from"../../core/resolver.js";import{resolveScopeArg as C,requireScopeRoot as M,marketplacesDir as Y,pluginsDir as Z}from"../../core/scope.js";import{installsLiveInAppBlock as L}from"../../shared/env.js";import{addAppMarketplace as ee,removeAppMarketplace as te}from"./app-installs.js";import{updateConfig as q,updateState as G,ensureScopeInitialized as oe,readConfig as ne}from"../../core/config.js";import{pathExists as $,ensureDir as _,removePath as k,isSymlink as ae,nowIso as A,readSymlinkTarget as re}from"../../core/fs-utils.js";import{clone as K,deriveNameFromUrl as ie,currentSha as O,gitSync as B,isGitRepo as E}from"../../core/git.js";import{readMarketplaceManifest as N,readPluginManifest as F}from"../../core/manifest.js";import{assertPluginCandidateValid as se,isMarketplaceRelativeSource as ce}from"./plugin-manage.js";import{resolveInstallScope as pe}from"./shared.js";async function le(o){const{url:n,ref:i,scope:t}=o,s=M(t);await oe(t,s);const p=ie(n),c=Y(t);_(c);const l=g(c,p);$(l)&&k(l),K(n,l,{depth:1,ref:i!==void 0?i:void 0});const u=N(l);if(!u)throw k(l),d(`marketplace manifest not found at ${l}/.crouter-marketplace/marketplace.json \u2014 not a valid marketplace`);const e=u.name,a=g(c,e);e!==p&&($(a)&&k(a),y(l,a));const r=i!==void 0?i:"main";try{await q(t,m=>{m.marketplaces[e]={url:n,ref:r,installed_at:A()}})}catch(m){throw k(a),m}return{name:e,scope:t,path:a}}f(le,"addMarketplace");const qe=D({name:"add",description:"add a marketplace by git URL",whenToUse:"registering a new marketplace source repo so its plugins appear in marketplace browse output and become installable by marketplace/plugin ref",help:{name:"pkg market add",summary:"add a marketplace by git URL",params:[{kind:"positional",name:"url",type:"string",required:!0,constraint:"Git URL of the marketplace repo."},{kind:"flag",name:"ref",type:"string",required:!1,constraint:"Git ref to track. Default: main."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: project if available, else user."}],output:[{name:"name",type:"string",required:!0,constraint:"Marketplace name as declared in marketplace.json."},{name:"scope",type:"string",required:!0,constraint:"Scope the marketplace was added into."},{name:"path",type:"string",required:!0,constraint:"Absolute path to the cloned marketplace directory."}],outputKind:"object",effects:["Clones the marketplace repo. Registers the marketplace in config.json."]},run:f(async o=>{const n=o.url,i=o.ref,t=o.scope;return L()?await ee(n,i):le({url:n,ref:i,scope:pe(t)})},"run")}),Ae=D({name:"remove",description:"remove a marketplace",whenToUse:"unregistering a marketplace source: deletes the source clone and any plugins installed from it in the same scope",help:{name:"pkg market remove",summary:"remove a marketplace and its directory",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Marketplace name to remove."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Narrows resolution."}],output:[],outputKind:"object",effects:["Deletes the marketplace directory. Removes the entry from config.json. Removes plugin files and config entries sourced from that marketplace in the same scope."]},run:f(async o=>{const n=o.name;if(L())return await te(n);const i=o.scope;let t,s;if(i!==void 0){const a=C(i);if(a!=="all"&&a!=="builtin"){const r=a,m=w(n,r);if(!m)throw d(`marketplace not found: ${n} (scope: ${r})`);t=r,s=m.root}else{const r=w(n);if(!r)throw d(`marketplace not found: ${n}`);t=r.scope,s=r.root}}else{const a=w(n);if(!a)throw d(`marketplace not found: ${n}`);t=a.scope,s=a.root}const p=t,c=M(p),l=Z(p),u=ne(p),e=[];for(const[a,r]of Object.entries(u.plugins))r.source_marketplace===n&&e.push(a);for(const a of e){const r=g(l,a);k(r)}return k(s),await q(p,a=>{delete a.marketplaces[n];for(const r of e)delete a.plugins[r]}),{}},"run")});function z(o,n,i,t){try{K(o,t,{depth:1,ref:n})}catch(s){throw k(t),s}return{active:i,candidate:t,previous:`${t}.previous`}}f(z,"stageGitRoot");function ue(o){const n=[],i=[];try{for(const t of o){if($(t.previous))throw j(`prior staged backup remains at ${t.previous}`);y(t.active,t.previous),n.push(t)}for(const t of o)y(t.candidate,t.active),i.push(t)}catch(t){for(const s of[...i].reverse())y(s.active,s.candidate);for(const s of[...n].reverse())y(s.previous,s.active);throw t}for(const t of o)k(t.previous)}f(ue,"activateStagedRoots");function H(o,n,i){return{plugin:{...o,root:n,manifest:i},scopeRoot:R(R(o.root))}}f(H,"candidatePlugin");async function de(o){const n=O(o.root)??"",i=X().filter(u=>u.scope===o.scope&&u.sourceMarketplace===o.name),t=[],s=[];let p,c;const l=[];try{if(E(o.root)){const e=g(R(o.root),`.${o.name}.${process.pid}.${Date.now()}.candidate`);c=z(o.url,o.ref,o.root,e),s.push(e);const a=N(e);if(a===null)throw d(`marketplace manifest not found in candidate checkout: ${e}/.crouter-marketplace/marketplace.json`);p=a,t.push(c)}else{const e=N(o.root);if(e===null)throw d(`marketplace manifest not found after refresh: ${o.root}/.crouter-marketplace/marketplace.json`);p=e}for(const e of i){if(ae(e.root)){if(c===void 0)continue;const b=p.plugins.find(J=>J.name===e.name);if(b===void 0||!ce(b.source))throw j(`marketplace candidate removes relative-source plugin "${e.name}" while it is installed`);const P=re(e.root);if(P===null)throw j(`installed marketplace plugin "${e.name}" has no readable symlink target`);const I=W(o.root,S(R(e.root),P)),h=S(c.candidate,b.source);if(h!==S(c.candidate,I))throw j(`marketplace candidate moves installed relative-source plugin "${e.name}" from ${I} to ${b.source}`);if(!$(h))throw d(`marketplace candidate source path does not exist for plugin "${e.name}": ${h}`);const T=F(h);if(T===null)throw d(`plugin manifest not found in marketplace candidate: ${h}/.crouter-plugin/plugin.json`);l.push(H(e,h,T));continue}if(!$(e.root)||!E(e.root))continue;const a=B(["config","--get","remote.origin.url"],e.root).stdout.trim(),r=B(["symbolic-ref","--short","HEAD"],e.root).stdout.trim();if(a===""||r==="")continue;const m=e.scope==="project"?M("project"):M("user"),v=g(m,"tmp",`${e.name}.${process.pid}.${Date.now()}.candidate`);_(g(m,"tmp"));const V=z(a,r,e.root,v);s.push(v);const U=F(v);if(U===null)throw d(`plugin manifest not found in update candidate: ${v}/.crouter-plugin/plugin.json`);l.push(H(e,v,U)),t.push(V)}for(const e of l)await se(e.plugin.manifest,e.plugin.root,e.plugin.scope,e.scopeRoot,`${e.plugin.root}/.crouter-plugin/plugin.json`,l);ue(t),await G(o.scope,e=>{e.marketplaces[o.name]===void 0&&(e.marketplaces[o.name]={}),e.marketplaces[o.name].last_updated=A()});for(const e of i){const a=p.plugins.find(r=>r.name===e.name);a?.version!==void 0&&await q(e.scope,r=>{r.plugins[e.name]!==void 0&&(r.plugins[e.name].version=a.version)}),await G(e.scope,r=>{r.plugins[e.name]===void 0&&(r.plugins[e.name]={}),r.plugins[e.name].last_updated=A()})}const u=O(o.root)??"";return{name:o.name,updated:n!==u,sha:u}}finally{for(const u of s)k(u)}}f(de,"updateMarketplaceTarget");const Ne=D({name:"update",description:"replace marketplace source with its latest revision",whenToUse:"refreshing one named marketplace source from git, or all registered marketplace sources when no name is given; also updates installed plugins sourced from refreshed marketplaces",help:{name:"pkg market update",summary:"refresh one or all registered marketplaces",params:[{kind:"flag",name:"name",type:"string",required:!1,constraint:"Marketplace name to update. Omit to update all registered marketplaces."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Narrows resolution."}],output:[{name:"updated",type:"object[]",required:!0,constraint:"One entry per marketplace processed: {name, updated, sha}."}],outputKind:"object",effects:["Stages and validates marketplace and sourced-plugin revisions before atomically activating them, replacing each checkout even at an unchanged revision \u2014 local modifications inside those directories are discarded. `updated` reports whether the revision moved, not whether the checkout was replaced. Updates installed plugin content/version for plugins sourced from refreshed marketplaces."]},run:f(async o=>{const n=o.name,i=o.scope,t=i===void 0?void 0:C(i),s=n===void 0?t===void 0||t==="all"?x():t==="builtin"?[]:x().filter(c=>c.scope===t):(()=>{const c=t===void 0||t==="all"||t==="builtin"?w(n):w(n,t);if(c===null)throw d(`marketplace not found: ${n}${t===void 0||t==="all"||t==="builtin"?"":` (scope: ${t})`}`);return[c]})(),p=[];for(const c of s)p.push(await de(c));return{updated:p}},"run")});export{le as addMarketplace,qe as marketAdd,Ae as marketRemove,Ne as marketUpdate};
@@ -1 +1 @@
1
- var b=Object.defineProperty;var u=(t,a)=>b(t,"name",{value:a,configurable:!0});import{defineLeaf as f}from"../../core/command.js";import{notFound as k}from"../../core/errors.js";import{isSandboxedNode as v}from"../../shared/env.js";import{listAppPlugins as x}from"./app-installs.js";import{paginate as j}from"../../core/pagination.js";import{listInstalledPlugins as w,findPluginByName as p}from"../../core/resolver.js";import{packageFiles as q}from"../../core/graph/package-files.js";import{validatePluginCommands as P,validateEffectiveCommandPlugins as S}from"../../core/command-plugins/discovery.js";import{compileHookRegistry as D,discoverHookRegistry as M}from"../../core/command-hooks/discovery.js";import{hasDeclaredHooks as R,hookReport as h}from"../../core/command-hooks/report.js";import{SUBTREE_NAMES as C,coreCommandPaths as E,coreHookCatalog as N,extensibleCoreBranches as _}from"../../build-root.js";import{pluginMemoryDir as B,resolveScopeArg as g,projectScopeRoot as y}from"../../core/scope.js";async function I(t){if(!R(t.manifest))return;const a=await N(),l=M(a),n=l.validations.find(i=>i.plugin.root===t.root);if(n===void 0){const i=D(a,[t]);return h(i.validations[0])}return h(n,l.issues.filter(i=>i.plugin===t.name&&i.scope===t.scope))}u(I,"hookInspectionReport");const J=f({name:"list",description:"paginated list of installed plugins",whenToUse:"enumerating installed plugins across user and project scope, optionally including disabled plugins",help:{name:"pkg plugin list",summary:"paginated list of installed plugins",params:[{kind:"flag",name:"scope",type:"enum",choices:["user","project","all"],required:!1,constraint:"Default: all."},{kind:"flag",name:"include-disabled",type:"bool",required:!1,constraint:"Include disabled plugins. Default: false."},{kind:"flag",name:"limit",type:"int",required:!1,default:50,constraint:"Default 50, max 200."},{kind:"flag",name:"cursor",type:"string",required:!1,constraint:"Opaque token from next_cursor. Omit on first call."}],output:[{name:"items",type:"object[]",required:!0,constraint:"Each: {name, scope, version?, enabled, transport, source_marketplace?, path, documents?}. documents, on a sandboxed node, says why an install has no documents. transport is exec or http for command plugins, null for docs-only plugins. Sorted by scope then name ascending."},{name:"next_cursor",type:"string | null",required:!0,constraint:"null means no more items."},{name:"total",type:"integer | null",required:!0,constraint:"Exact when cheap; null otherwise."}],outputKind:"object",effects:["None on a Mac. On a sandboxed node it asks the directory about each enabled install, which publishes what the directory serves."]},run:u(async t=>{const a=t.scope,l=t.includeDisabled??!1;if(v())return await x(l);const n=t.limit,i=n!==void 0?Math.min(Math.max(1,n),200):50,c=t.cursor;let d;if(a!==void 0){const e=g(a);d=e==="all"?["project","user"].filter(s=>s!=="project"||y()!==null):[e]}else d=["project","user"].filter(e=>e!=="project"||y()!==null);const o=d.flatMap(e=>w(e)).filter(e=>l||e.enabled).sort((e,s)=>e.scope==="project"&&s.scope!=="project"?-1:e.scope!=="project"&&s.scope==="project"?1:e.name.localeCompare(s.name)),m=j(o,{limit:i,cursor:c!==void 0?c:void 0},{defaultLimit:50,maxLimit:200,keyOf:u(e=>`${e.scope}:${e.name}`,"keyOf"),total:"count"});return{items:m.items.map(e=>({name:e.name,scope:e.scope,version:e.version,enabled:e.enabled,transport:e.manifest.transport?.kind??null,source_marketplace:e.sourceMarketplace,path:e.root})),next_cursor:m.next_cursor,total:m.total}},"run")}),Q=f({name:"show",description:"read plugin manifest and substrate inventory",whenToUse:"reading one installed plugin in detail: manifest, provided documents, scope, path, and enabled state",help:{name:"pkg plugin show",summary:"read plugin manifest and metadata by name",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Plugin name."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Narrows resolution."}],output:[{name:"name",type:"string",required:!0,constraint:"Plugin name."},{name:"scope",type:"string",required:!0,constraint:"Scope the plugin is installed in."},{name:"path",type:"string",required:!0,constraint:"Absolute path to the plugin directory."},{name:"enabled",type:"boolean",required:!0,constraint:"Whether the plugin is active."},{name:"manifest",type:"object",required:!0,constraint:"Full plugin.json contents."},{name:"docs",type:"object[]",required:!0,constraint:"Each: {name, path}. Documents provided by the plugin (named under `<pluginName>/`)."},{name:"commands",type:"object",required:!1,constraint:"Present only when the plugin declares a command manifest (manifest.commands). {manifestPath: string, mounts: string[] (accepted command paths), extensibleBranches: string[] (plugin mount names accepting repository fragments), issues: object[] ({code, path?, message, received, expected, next})}. Validated statically; command execution never occurs."},{name:"hooks",type:"object",required:!1,constraint:"Present for every hook declaration, including hook-only plugins. {manifestPath?, executablePath?, declarations: {target, phase, op, description, effects}[], lifecycle?: {event, phase, op, description, effects}[], issues: object[] ({code, path?, message, received, expected, next}), trust: string}. Static only: no hook executable runs."}],outputKind:"object",effects:["None. Read-only. Command and lifecycle-hook manifests are validated statically; plugin executables are never executed."]},run:u(async t=>{const a=t.name,l=t.scope;let n;if(l!==void 0){const o=g(l);o==="all"||o==="builtin"?n=p(a):n=p(a,o)}else n=p(a);if(n===null)throw k(`plugin not found: ${a}`);const i=q(B(n)).files.map(o=>({name:`${n.name}/${o.name}`,path:o.path}));let c;if(typeof n.manifest.commands=="string"&&n.manifest.commands.length>0){const o=new Set(C),[m,e]=await Promise.all([E(),_()]),s=S(o,void 0,void 0,m,e).find(r=>r.plugin.root===n.root)??P(n,o,m,e);c={manifestPath:s.manifestPath,mounts:[...s.contributions.map(r=>r.node.name),...s.coreMounts.map(r=>[...r.parent,r.node.name].join(" "))],extensibleBranches:s.contributions.filter(r=>r.node.extensible===!0).map(r=>r.node.name),issues:s.issues}}const d=await I(n);return{name:n.name,scope:n.scope,path:n.root,enabled:n.enabled,manifest:n.manifest,docs:i.map(o=>({name:o.name,path:o.path})),...c!==void 0?{commands:c}:{},...d!==void 0?{hooks:d}:{}}},"run")});export{J as pluginList,Q as pluginShow};
1
+ var b=Object.defineProperty;var u=(t,a)=>b(t,"name",{value:a,configurable:!0});import{defineLeaf as f}from"../../core/command.js";import{notFound as k}from"../../core/errors.js";import{installsLiveInAppBlock as v}from"../../shared/env.js";import{listAppPlugins as x}from"./app-installs.js";import{paginate as j}from"../../core/pagination.js";import{listInstalledPlugins as w,findPluginByName as p}from"../../core/resolver.js";import{packageFiles as q}from"../../core/graph/package-files.js";import{validatePluginCommands as P,validateEffectiveCommandPlugins as S}from"../../core/command-plugins/discovery.js";import{compileHookRegistry as D,discoverHookRegistry as M}from"../../core/command-hooks/discovery.js";import{hasDeclaredHooks as R,hookReport as h}from"../../core/command-hooks/report.js";import{SUBTREE_NAMES as B,coreCommandPaths as C,coreHookCatalog as E,extensibleCoreBranches as _}from"../../build-root.js";import{pluginMemoryDir as I,resolveScopeArg as g,projectScopeRoot as y}from"../../core/scope.js";async function N(t){if(!R(t.manifest))return;const a=await E(),l=M(a),n=l.validations.find(i=>i.plugin.root===t.root);if(n===void 0){const i=D(a,[t]);return h(i.validations[0])}return h(n,l.issues.filter(i=>i.plugin===t.name&&i.scope===t.scope))}u(N,"hookInspectionReport");const J=f({name:"list",description:"paginated list of installed plugins",whenToUse:"enumerating installed plugins across user and project scope, optionally including disabled plugins",help:{name:"pkg plugin list",summary:"paginated list of installed plugins",params:[{kind:"flag",name:"scope",type:"enum",choices:["user","project","all"],required:!1,constraint:"Default: all."},{kind:"flag",name:"include-disabled",type:"bool",required:!1,constraint:"Include disabled plugins. Default: false."},{kind:"flag",name:"limit",type:"int",required:!1,default:50,constraint:"Default 50, max 200."},{kind:"flag",name:"cursor",type:"string",required:!1,constraint:"Opaque token from next_cursor. Omit on first call."}],output:[{name:"items",type:"object[]",required:!0,constraint:"Each: {name, scope, version?, enabled, transport, source_marketplace?, path, documents?}. documents, on a sandboxed node, says why an install has no documents. transport is exec or http for command plugins, null for docs-only plugins. Sorted by scope then name ascending."},{name:"next_cursor",type:"string | null",required:!0,constraint:"null means no more items."},{name:"total",type:"integer | null",required:!0,constraint:"Exact when cheap; null otherwise."}],outputKind:"object",effects:["None on a Mac. On a sandboxed node it asks the directory about each enabled install, which publishes what the directory serves."]},run:u(async t=>{const a=t.scope,l=t.includeDisabled??!1;if(v())return await x(l);const n=t.limit,i=n!==void 0?Math.min(Math.max(1,n),200):50,c=t.cursor;let d;if(a!==void 0){const e=g(a);d=e==="all"?["project","user"].filter(s=>s!=="project"||y()!==null):[e]}else d=["project","user"].filter(e=>e!=="project"||y()!==null);const o=d.flatMap(e=>w(e)).filter(e=>l||e.enabled).sort((e,s)=>e.scope==="project"&&s.scope!=="project"?-1:e.scope!=="project"&&s.scope==="project"?1:e.name.localeCompare(s.name)),m=j(o,{limit:i,cursor:c!==void 0?c:void 0},{defaultLimit:50,maxLimit:200,keyOf:u(e=>`${e.scope}:${e.name}`,"keyOf"),total:"count"});return{items:m.items.map(e=>({name:e.name,scope:e.scope,version:e.version,enabled:e.enabled,transport:e.manifest.transport?.kind??null,source_marketplace:e.sourceMarketplace,path:e.root})),next_cursor:m.next_cursor,total:m.total}},"run")}),Q=f({name:"show",description:"read plugin manifest and substrate inventory",whenToUse:"reading one installed plugin in detail: manifest, provided documents, scope, path, and enabled state",help:{name:"pkg plugin show",summary:"read plugin manifest and metadata by name",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Plugin name."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Narrows resolution."}],output:[{name:"name",type:"string",required:!0,constraint:"Plugin name."},{name:"scope",type:"string",required:!0,constraint:"Scope the plugin is installed in."},{name:"path",type:"string",required:!0,constraint:"Absolute path to the plugin directory."},{name:"enabled",type:"boolean",required:!0,constraint:"Whether the plugin is active."},{name:"manifest",type:"object",required:!0,constraint:"Full plugin.json contents."},{name:"docs",type:"object[]",required:!0,constraint:"Each: {name, path}. Documents provided by the plugin (named under `<pluginName>/`)."},{name:"commands",type:"object",required:!1,constraint:"Present only when the plugin declares a command manifest (manifest.commands). {manifestPath: string, mounts: string[] (accepted command paths), extensibleBranches: string[] (plugin mount names accepting repository fragments), issues: object[] ({code, path?, message, received, expected, next})}. Validated statically; command execution never occurs."},{name:"hooks",type:"object",required:!1,constraint:"Present for every hook declaration, including hook-only plugins. {manifestPath?, executablePath?, declarations: {target, phase, op, description, effects}[], lifecycle?: {event, phase, op, description, effects}[], issues: object[] ({code, path?, message, received, expected, next}), trust: string}. Static only: no hook executable runs."}],outputKind:"object",effects:["None. Read-only. Command and lifecycle-hook manifests are validated statically; plugin executables are never executed."]},run:u(async t=>{const a=t.name,l=t.scope;let n;if(l!==void 0){const o=g(l);o==="all"||o==="builtin"?n=p(a):n=p(a,o)}else n=p(a);if(n===null)throw k(`plugin not found: ${a}`);const i=q(I(n)).files.map(o=>({name:`${n.name}/${o.name}`,path:o.path}));let c;if(typeof n.manifest.commands=="string"&&n.manifest.commands.length>0){const o=new Set(B),[m,e]=await Promise.all([C(),_()]),s=S(o,void 0,void 0,m,e).find(r=>r.plugin.root===n.root)??P(n,o,m,e);c={manifestPath:s.manifestPath,mounts:[...s.contributions.map(r=>r.node.name),...s.coreMounts.map(r=>[...r.parent,r.node.name].join(" "))],extensibleBranches:s.contributions.filter(r=>r.node.extensible===!0).map(r=>r.node.name),issues:s.issues}}const d=await N(n);return{name:n.name,scope:n.scope,path:n.root,enabled:n.enabled,manifest:n.manifest,docs:i.map(o=>({name:o.name,path:o.path})),...c!==void 0?{commands:c}:{},...d!==void 0?{hooks:d}:{}}},"run")});export{J as pluginList,Q as pluginShow};
@@ -1,4 +1,4 @@
1
- var Le=Object.defineProperty;var d=(e,n)=>Le(e,"name",{value:n,configurable:!0});import{envProfileId as ne}from"../../shared/env.js";import{invalidPluginEnvReasons as _e}from"../../core/plugin-env.js";import{collectPluginEnv as te}from"./plugin-env.js";import{dirname as ie,join as v,resolve as V,isAbsolute as Be,relative as _}from"node:path";import{renameSync as j}from"node:fs";import{defineLeaf as B}from"../../core/command.js";import{cliClient as He,rethrowAsCliError as De}from"../api-client.js";import{isSandboxedNode as H}from"../../shared/env.js";import{installAppPlugin as Ie,removeAppPlugin as Ne,setAppPluginEnabled as oe,updateAppPlugins as Fe}from"./app-installs.js";import{notFound as g,usage as b,general as f,network as Ue,CrtrError as Ve}from"../../core/errors.js";import{findMarketplaceByName as Oe,findPluginByName as A,listAllPlugins as ae}from"../../core/resolver.js";import{pluginsDir as C,ensureProjectScopeRoot as G,userScopeRoot as S,resolveScopeArg as re,projectScopeRoot as se,requireScopeRoot as M}from"../../core/scope.js";import{readState as Ke,updateConfig as D,updateState as O,ensureScopeInitialized as J,invalidPluginKindsReasons as We,invalidPluginBinReasons as ze,invalidPluginRequiresReasons as Ge,normalizeRequires as Je}from"../../core/config.js";import{withExclusiveDirectoryLockAsync as Ye}from"../../core/exclusive-lock.js";import{bundleSwapLockPath as le}from"../../core/plugin-swap-lock.js";import{invalidPageComponentsReasons as Qe}from"../../core/human/page-catalog.js";import{pathExists as $,ensureDir as L,removePath as E,nowIso as Y,linkOrCopy as ce,isSymlink as K,readSymlinkTarget as Xe,readText as Ze,realpathOrSelf as en,walkFiles as nn}from"../../core/fs-utils.js";import{clone as W,deriveNameFromUrl as tn,currentSha as T,gitSync as de,isGitRepo as Q}from"../../core/git.js";import{readMarketplaceManifest as ue,readPluginManifest as x}from"../../core/manifest.js";import{pluginExtensionCatalog as on,validateMemoryExtensionValues as an,validatePluginMemoryExtensions as rn}from"../../core/plugin-extensions.js";import{lintPackageFrontmatter as sn,packageFiles as ln}from"../../core/graph/package-files.js";import{parseFrontmatterGeneric as cn}from"../../core/frontmatter.js";import{validatePluginCommands as X,validateEffectiveCommandPlugins as dn}from"../../core/command-plugins/discovery.js";import{normalizeEndpoint as un,validateEndpoint as pn,validateHttpPluginTransport as fn}from"../../core/command-plugins/endpoint.js";import{composeAppProcessEnv as mn,overlayBrokerEnv as hn,resolvePathExecutable as gn}from"../../core/runtime/spawn-env.js";import{bundleVersion as vn,installBundleArchive as yn,isInstalledGeneration as kn,recordCheckedGeneration as wn,validatePluginBundle as bn}from"../../core/command-plugins/bundle.js";import{compileHookRegistry as pe,discoverCandidateHookRegistry as $n,discoverCandidateHookRegistryForCandidates as jn,discoverHookRegistry as Pn}from"../../core/command-hooks/discovery.js";import{hasDeclaredHooks as fe,hookReport as me}from"../../core/command-hooks/report.js";import{fetchHttpPluginBundle as he}from"../../core/command-plugins/transport/http-fetch.js";import{leafPathsBelowRoot as Rn,objectBlocksReasons as xn,streamingLeafPathsBelowRoot as Sn}from"../../core/plugin-objects-manifest.js";import{SUBTREE_NAMES as I,coreCommandPaths as N,coreHookCatalog as ge,extensibleCoreBranches as F}from"../../build-root.js";import{ExitCode as En}from"../../types.js";import{isGitUrl as ve,resolvePluginConfigTarget as Tn,setPluginEnabled as ye,resolveInstallScope as qn}from"./shared.js";async function Z(e,n){const t=A(e,n);if(t===null||typeof t.manifest.commands!="string"||t.manifest.commands.length===0)return;const i=new Set(I),[o,r]=await Promise.all([N(),F()]),a=dn(i,void 0,void 0,o,r).find(l=>l.plugin.root===t.root)??X(t,i,o,r);return{mounts:[...a.contributions.map(l=>l.node.name),...a.coreMounts.map(l=>[...l.parent,l.node.name].join(" "))],issues:a.issues}}d(Z,"commandReport");const An=/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;function ke(e,n,t){if(typeof e!="string"||!An.test(e))throw b("invalid --name: expected a lowercase kebab-case plugin handle");const i=pn(n);if(!i.isValid)throw b(`invalid --endpoint: ${i.errors.join("; ")}`);const o=un(n),r=fn({kind:"http",endpoint:o,...t!==void 0?{authEnv:t}:{}});if(r.transport===void 0)throw b(`invalid HTTP plugin transport: ${r.errors.join("; ")}`);return r.transport}d(ke,"validateHttpInstall");function z(e,n){const t=n.map(i=>`- ${i.code}${i.path!==void 0?` at ${i.path}`:""}: ${i.message} (received ${JSON.stringify(i.received)}, expected ${i.expected})`);return f(`${e}:
1
+ var Le=Object.defineProperty;var d=(e,n)=>Le(e,"name",{value:n,configurable:!0});import{envProfileId as ne}from"../../shared/env.js";import{invalidPluginEnvReasons as _e}from"../../core/plugin-env.js";import{collectPluginEnv as te}from"./plugin-env.js";import{dirname as ie,join as v,resolve as V,isAbsolute as Be,relative as _}from"node:path";import{renameSync as j}from"node:fs";import{defineLeaf as B}from"../../core/command.js";import{cliClient as He,rethrowAsCliError as Ie}from"../api-client.js";import{installsLiveInAppBlock as H}from"../../shared/env.js";import{installAppPlugin as De,removeAppPlugin as Ne,setAppPluginEnabled as oe,updateAppPlugins as Fe}from"./app-installs.js";import{notFound as g,usage as b,general as f,network as Ue,CrtrError as Ve}from"../../core/errors.js";import{findMarketplaceByName as Oe,findPluginByName as A,listAllPlugins as ae}from"../../core/resolver.js";import{pluginsDir as C,ensureProjectScopeRoot as G,userScopeRoot as S,resolveScopeArg as re,projectScopeRoot as se,requireScopeRoot as M}from"../../core/scope.js";import{readState as Ke,updateConfig as I,updateState as O,ensureScopeInitialized as J,invalidPluginKindsReasons as We,invalidPluginBinReasons as ze,invalidPluginRequiresReasons as Ge,normalizeRequires as Je}from"../../core/config.js";import{withExclusiveDirectoryLockAsync as Ye}from"../../core/exclusive-lock.js";import{bundleSwapLockPath as le}from"../../core/plugin-swap-lock.js";import{invalidPageComponentsReasons as Qe}from"../../core/human/page-catalog.js";import{pathExists as $,ensureDir as L,removePath as E,nowIso as Y,linkOrCopy as ce,isSymlink as K,readSymlinkTarget as Xe,readText as Ze,realpathOrSelf as en,walkFiles as nn}from"../../core/fs-utils.js";import{clone as W,deriveNameFromUrl as tn,currentSha as T,gitSync as de,isGitRepo as Q}from"../../core/git.js";import{readMarketplaceManifest as ue,readPluginManifest as x}from"../../core/manifest.js";import{pluginExtensionCatalog as on,validateMemoryExtensionValues as an,validatePluginMemoryExtensions as rn}from"../../core/plugin-extensions.js";import{lintPackageFrontmatter as sn,packageFiles as ln}from"../../core/graph/package-files.js";import{parseFrontmatterGeneric as cn}from"../../core/frontmatter.js";import{validatePluginCommands as X,validateEffectiveCommandPlugins as dn}from"../../core/command-plugins/discovery.js";import{normalizeEndpoint as un,validateEndpoint as pn,validateHttpPluginTransport as fn}from"../../core/command-plugins/endpoint.js";import{composeAppProcessEnv as mn,overlayBrokerEnv as hn,resolvePathExecutable as gn}from"../../core/runtime/spawn-env.js";import{bundleVersion as vn,installBundleArchive as yn,isInstalledGeneration as kn,recordCheckedGeneration as wn,validatePluginBundle as bn}from"../../core/command-plugins/bundle.js";import{compileHookRegistry as pe,discoverCandidateHookRegistry as $n,discoverCandidateHookRegistryForCandidates as jn,discoverHookRegistry as Pn}from"../../core/command-hooks/discovery.js";import{hasDeclaredHooks as fe,hookReport as me}from"../../core/command-hooks/report.js";import{fetchHttpPluginBundle as he}from"../../core/command-plugins/transport/http-fetch.js";import{leafPathsBelowRoot as Rn,objectBlocksReasons as xn,streamingLeafPathsBelowRoot as Sn}from"../../core/plugin-objects-manifest.js";import{SUBTREE_NAMES as D,coreCommandPaths as N,coreHookCatalog as ge,extensibleCoreBranches as F}from"../../build-root.js";import{ExitCode as En}from"../../types.js";import{isGitUrl as ve,resolvePluginConfigTarget as Tn,setPluginEnabled as ye,resolveInstallScope as qn}from"./shared.js";async function Z(e,n){const t=A(e,n);if(t===null||typeof t.manifest.commands!="string"||t.manifest.commands.length===0)return;const i=new Set(D),[o,r]=await Promise.all([N(),F()]),a=dn(i,void 0,void 0,o,r).find(l=>l.plugin.root===t.root)??X(t,i,o,r);return{mounts:[...a.contributions.map(l=>l.node.name),...a.coreMounts.map(l=>[...l.parent,l.node.name].join(" "))],issues:a.issues}}d(Z,"commandReport");const An=/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;function ke(e,n,t){if(typeof e!="string"||!An.test(e))throw b("invalid --name: expected a lowercase kebab-case plugin handle");const i=pn(n);if(!i.isValid)throw b(`invalid --endpoint: ${i.errors.join("; ")}`);const o=un(n),r=fn({kind:"http",endpoint:o,...t!==void 0?{authEnv:t}:{}});if(r.transport===void 0)throw b(`invalid HTTP plugin transport: ${r.errors.join("; ")}`);return r.transport}d(ke,"validateHttpInstall");function z(e,n){const t=n.map(i=>`- ${i.code}${i.path!==void 0?` at ${i.path}`:""}: ${i.message} (received ${JSON.stringify(i.received)}, expected ${i.expected})`);return f(`${e}:
2
2
  ${t.join(`
3
3
  `)}`,{issues:n,...n.length>0?{next:n[0].next}:{}})}d(z,"invalidBundleError");function Cn(e,n){if(e.kinds===void 0)return;const t=We(e.kinds);if(t.length>0)throw f(`plugin manifest at ${n} declares an invalid kinds block:
4
4
  ${t.map(i=>`- ${i}`).join(`
@@ -8,12 +8,12 @@ ${t.map(i=>`- ${i}`).join(`
8
8
  ${t.map(i=>`- ${i}`).join(`
9
9
  `)}`,{issues:t})}d(Ln,"assertManifestRequiresValid");function we(e){if(e.requires===void 0)return[];const n={argv:[],env:{CRTR_NODE_CWD:process.cwd(),CRTR_PROFILE_ID:ne()??""}},t=hn(n,mn(process.cwd(),ne()??null,"app:terminal",{},{}));return Object.entries(Je(e.requires)).filter(([i])=>gn(i,t,process.cwd())===null).map(([i,o])=>`Warning: plugin "${e.name}" requires "${i}" on PATH \u2014 ${o}`)}d(we,"missingRequirementWarnings");function _n(e,n){if(e.page_components===void 0)return;const t=Qe(e.page_components);if(t.length>0)throw f(`plugin manifest at ${n} declares an invalid page_components block:
10
10
  ${t.map(i=>`- ${i}`).join(`
11
- `)}`,{issues:t})}d(_n,"assertManifestPageComponentsValid");async function ee(e,n){const t=A(e,n);if(t===null||!fe(t.manifest))return;const i=await ge(),o=Pn(i),r=o.validations.find(a=>a.plugin.root===t.root);if(r===void 0){const a=pe(i,[t]);return me(a.validations[0])}return me(r,o.issues.filter(a=>a.plugin===t.name&&a.scope===t.scope))}d(ee,"hookLifecycleReport");async function Bn(e,n,t,i,o,r){if(!fe(e))return;const a={name:e.name,scope:t,root:n,manifest:e,enabled:!0},l=r??[{plugin:a,scopeRoot:i}],c=await ge(),s=r===void 0?$n(c,a,i):jn(c,l),y=s.validations.find(p=>p.plugin.root===n)===void 0?pe(c,[a]).issues:s.issues.filter(p=>p.plugin===a.name&&p.scope===a.scope);if(y.length>0)throw z(`plugin hook behavior at ${o} is invalid`,y)}d(Bn,"assertManifestHooksValid");function Hn(e,n){const t=_(e,n);return t===""?e:t}d(Hn,"storeRelative");function Dn(e,n,t,i,o){const r=v(n,"memory");if(!$(r))return;const{files:a,invalid:l}=ln(r);if(l.length>0){const c=l.map(s=>`- ${Hn(r,s.path)}: ${s.error}`);throw f(`plugin "${e.name}" memory corpus at ${i} has documents that cannot be named:
11
+ `)}`,{issues:t})}d(_n,"assertManifestPageComponentsValid");async function ee(e,n){const t=A(e,n);if(t===null||!fe(t.manifest))return;const i=await ge(),o=Pn(i),r=o.validations.find(a=>a.plugin.root===t.root);if(r===void 0){const a=pe(i,[t]);return me(a.validations[0])}return me(r,o.issues.filter(a=>a.plugin===t.name&&a.scope===t.scope))}d(ee,"hookLifecycleReport");async function Bn(e,n,t,i,o,r){if(!fe(e))return;const a={name:e.name,scope:t,root:n,manifest:e,enabled:!0},l=r??[{plugin:a,scopeRoot:i}],c=await ge(),s=r===void 0?$n(c,a,i):jn(c,l),y=s.validations.find(p=>p.plugin.root===n)===void 0?pe(c,[a]).issues:s.issues.filter(p=>p.plugin===a.name&&p.scope===a.scope);if(y.length>0)throw z(`plugin hook behavior at ${o} is invalid`,y)}d(Bn,"assertManifestHooksValid");function Hn(e,n){const t=_(e,n);return t===""?e:t}d(Hn,"storeRelative");function In(e,n,t,i,o){const r=v(n,"memory");if(!$(r))return;const{files:a,invalid:l}=ln(r);if(l.length>0){const c=l.map(s=>`- ${Hn(r,s.path)}: ${s.error}`);throw f(`plugin "${e.name}" memory corpus at ${i} has documents that cannot be named:
12
12
  ${c.join(`
13
- `)}`,{issues:l,next:`Correct the package's memory documents, then retry \u2014 "${e.name}" was not activated.`})}return{docs:a.length,...o==="linked"?{linkedSource:en(r)}:{}}}d(Dn,"migratePluginMemory");async function P(e,n,t,i,o,r,a="package"){try{rn(e)}catch(m){const h=m instanceof Error?m.message:String(m);throw f(`plugin manifest at ${o} declares invalid memory_extensions: ${h}`)}if(Cn(e,o),_n(e,o),Mn(e,o),Ln(e,o),e.env!==void 0){const m=_e(e.env);if(m.length)throw f(`plugin manifest at ${o} declares an invalid env block:
13
+ `)}`,{issues:l,next:`Correct the package's memory documents, then retry \u2014 "${e.name}" was not activated.`})}return{docs:a.length,...o==="linked"?{linkedSource:en(r)}:{}}}d(In,"migratePluginMemory");async function P(e,n,t,i,o,r,a="package"){try{rn(e)}catch(m){const h=m instanceof Error?m.message:String(m);throw f(`plugin manifest at ${o} declares invalid memory_extensions: ${h}`)}if(Cn(e,o),_n(e,o),Mn(e,o),Ln(e,o),e.env!==void 0){const m=_e(e.env);if(m.length)throw f(`plugin manifest at ${o} declares an invalid env block:
14
14
  ${m.map(h=>`- ${h}`).join(`
15
- `)}`,{issues:m})}const[l,c]=await Promise.all([N(),F()]),s=X({name:e.name,scope:t,root:n,manifest:e,enabled:!0},new Set(I),l,c);if(s.issues.length>0)throw z(`plugin command behavior at ${o} is invalid`,s.issues);const u=s.manifest?.roots??[],y=xn(e,{name:e.name,leafPaths:Rn(e.name,e,u),streamingLeafPaths:Sn(e.name,e,u)});if(y.length>0)throw f(`plugin manifest at ${o} declares an invalid object_kinds or scopes block:
15
+ `)}`,{issues:m})}const[l,c]=await Promise.all([N(),F()]),s=X({name:e.name,scope:t,root:n,manifest:e,enabled:!0},new Set(D),l,c);if(s.issues.length>0)throw z(`plugin command behavior at ${o} is invalid`,s.issues);const u=s.manifest?.roots??[],y=xn(e,{name:e.name,leafPaths:Rn(e.name,e,u),streamingLeafPaths:Sn(e.name,e,u)});if(y.length>0)throw f(`plugin manifest at ${o} declares an invalid object_kinds or scopes block:
16
16
  ${y.map(m=>`- ${m}`).join(`
17
17
  `)}`,{issues:y});await Bn(e,n,t,i,o,r);const p=v(n,"memory"),k=on(t,i,{candidate:e});for(const m of nn(p,h=>h.endsWith(".md"),h=>h.startsWith("."))){let h;try{h=cn(Ze(m)).data}catch(R){throw f(`plugin memory document ${_(n,m)} has invalid YAML frontmatter: ${R instanceof Error?R.message.split(`
18
- `)[0]:String(R)}`)}const w=sn(h);if(w!==null)throw f(`plugin memory document ${_(n,m)} is invalid: ${w}`);for(const R of an(h?.extensions,k))throw f(`plugin memory document ${_(n,m)} is invalid: ${R.path}: ${R.message}`)}return Dn(e,n,t,o,a)}d(P,"assertPluginCandidateValid");function In(e){return{kind:"http",endpoint:new URL(e.endpoint).origin,...e.authEnv!==void 0?{authEnv:e.authEnv}:{}}}d(In,"invocationTransport");async function Nn(e,n,t,i,o){const r=await P(o,t,n,i,`${t}/.crouter-plugin/plugin.json`),a={name:e,scope:n,root:t,manifest:o,enabled:!0},[l,c]=await Promise.all([N(),F()]),s=X(a,new Set(I),l,c);if(s.issues.length>0)throw z(`bundle staged package for "${e}" is invalid`,s.issues);return r}d(Nn,"validateStagedBundle");function be(e){return e.sourceMarketplace!==void 0||Q(e.root)||K(e.root)}d(be,"isSourceInstalled");function $e(e){throw f(`HTTP plugin "${e}" uses the removed commands-only endpoint format`,{next:`Run \`crtr pkg plugin remove ${e}\`, then reinstall it with \`crtr pkg plugin install --name ${e} --endpoint <archive-url>\`.`})}d($e,"rejectLegacyHttpPlugin");function Fn(e,n,t){if(!$(t))return;const i=A(e,n);if(i?.manifest.bundle!==void 0)return i;throw i?.manifest.transport?.kind==="http"&&i!==void 0&&!be(i)&&$e(e),f(`plugin "${e}" already exists at ${t}; run \`crtr pkg plugin remove ${e}\` before installing the archive plugin.`)}d(Fn,"existingBundleRoot");function je(e,n,t,i){const o=ke(e,n.endpoint,n.authEnv),r={endpoint:o.endpoint,...o.authEnv!==void 0?{authEnv:o.authEnv}:{}},a=v(C(t),e);return Fn(e,t,a),{bundle:r,transport:In(r),scopeRootPath:i,root:a}}d(je,"resolveBundleTarget");function Pe(e,n){return{name:e,endpoint:n.endpoint,...n.authEnv!==void 0?{authEnv:n.authEnv}:{}}}d(Pe,"fetchTarget");const Un=6e4,Vn=1e4;async function Re(e,n,t,i){const o=n==="project"?G():S();return L(v(o,"tmp")),Ye(le(o,e),()=>i(o),{timeoutMs:t,timeoutError:d(()=>f(`timed out waiting for another crtr process to finish replacing plugin "${e}"`,{next:`Re-run once that process has finished, or check for a stalled crtr holding ${le(o,e)}.`}),"timeoutError")})}d(Re,"withBundleSwapLock");async function xe(e,n,t,i){return Re(e,t,Un,async o=>{const r=je(e,n,t,o),a=await he(Pe(e,r.bundle));if(a.status!==200){const l=`HTTP plugin bundle fetch failed for "${e}": ${a.message}`;throw a.status==="auth_env_missing"?b(l):a.status==="cli_unreachable"?Ue(l):f(l)}return Se(e,t,r,a.raw,a.etag,i)})}d(xe,"replaceBundlePlugin");async function Mt(e,n,t,i){return Re(e,t,Vn,async o=>{try{const r=je(e,n,t,o),a=i.conditional?Ke(t).plugins[e]?.etag:void 0,l={...a!==void 0?{ifNoneMatch:a}:{},...i.timeoutMs!==void 0?{timeoutMs:i.timeoutMs}:{}},c=await he(Pe(e,r.bundle),l);if(c.status===304)return;if(c.status!==200)throw f(`HTTP plugin bundle revalidation failed for "${e}": ${c.message}`);const s=vn(c.raw);if(kn(e,t,r.root,s)){await wn(t,e,c.etag,{swapped:!1});return}return await Se(e,t,r,c.raw,c.etag,i,s)}finally{await On(t,e)}})}d(Mt,"revalidateBundlePlugin");async function On(e,n){await O(e,t=>{t.plugins[n]===void 0&&(t.plugins[n]={}),t.plugins[n].checked_at=Y()})}d(On,"recordPluginCheck");async function Se(e,n,t,i,o,r,a){const{bundle:l,transport:c,scopeRootPath:s,root:u}=t,y=await Kn(e,i,{scope:n,scopeRootPath:s,root:u,bundle:l,transport:c,etag:o,enable:r.enable,...a!==void 0?{version:a}:{}}),{validated:p,version:k,staged:m}=y,h=await Z(e,n);if(h===void 0)throw f(`bundle plugin "${e}" has no staged command report`);const w=await ee(e,n);return{name:e,scope:n,path:u,transport:"http",version:k,docs:p.memory.length,kinds:Object.keys(p.kinds??{}).length,pageComponents:(p.page_components??[]).length,objectKinds:Object.keys(p.object_kinds??{}).length,scopes:(p.scopes??[]).length,commands:h,...w!==void 0?{hooks:w}:{},...m!==void 0?{memory:m}:{}}}d(Se,"applyBundleArchive");async function Lt(e){const[n,t]=await Promise.all([N(),F()]);return bn(e,{reservedCoreNames:new Set(I),coreCommandPaths:n,extensibleCoreBranches:t})}d(Lt,"validatePluginArchive");async function Kn(e,n,t){const[i,o]=await Promise.all([N(),F()]);return yn(e,n,{...t,validation:{reservedCoreNames:new Set(I),coreCommandPaths:i,extensibleCoreBranches:o},validateStaged:d((r,a)=>Nn(e,t.scope,r,t.scopeRootPath,a),"validateStaged"),invalid:d((r,a)=>z(r,a),"invalid")})}d(Kn,"installPluginArchive");async function Wn(e,n,t,i){const o=ke(e,n,t);return xe(e,{endpoint:o.endpoint,...o.authEnv!==void 0?{authEnv:o.authEnv}:{}},i,{enable:!0})}d(Wn,"installHttpPlugin");function zn(e){if(e.startsWith("./")||e.startsWith("../")||e.startsWith("/"))return{kind:"local",value:e};if(ve(e))return{kind:"git",value:e};const n=e.split("/");if(n.length===2&&n[0]!==""&&n[1]!=="")return{kind:"marketplace",value:{marketplace:n[0],plugin:n[1]}};throw b(`invalid plugin install ref: ${e}
19
- Accepted forms: marketplace/plugin, git URL (https://, ssh://, git@, file://, or *.git), or local path starting ./, ../, or /. Bare plugin names are not install refs.`)}d(zn,"classifyInstallRef");function Ee(e){return e.startsWith("./")||e.startsWith("../")||!Be(e)&&!ve(e)}d(Ee,"isMarketplaceRelativeSource");async function Te(e,n,t){const i=n.get(e);if(i!==void 0)return i;const o=Oe(e);if(o===null){if(t.strict)throw g(`marketplace not found: ${e}`);return null}const r=ue(o.root);if(r===null)throw g(`marketplace manifest not found: ${o.root}/.crouter-marketplace/marketplace.json`);if(!Q(o.root)){const s={marketplace:o,manifest:r,pull:{updated:!1,sha:T(o.root)??""}};return n.set(e,s),s}const a=T(o.root),l=v(ie(o.root),`.${e}.${process.pid}.${Date.now()}.candidate`),c=`${l}.previous`;try{W(o.url,l,{ref:o.ref,depth:1});const s=ue(l);if(s===null)throw g(`marketplace manifest not found in candidate checkout: ${l}/.crouter-marketplace/marketplace.json`);const u=ae().filter(p=>p.sourceMarketplace===e&&K(p.root));for(const p of u){const k=s.plugins.find(Me=>Me.name===p.name);if(k===void 0||!Ee(k.source))throw f(`marketplace candidate removes relative-source plugin "${p.name}" while it is installed`);const m=Xe(p.root);if(m===null)throw f(`installed marketplace plugin "${p.name}" has no readable symlink target`);const h=_(o.root,V(ie(p.root),m)),w=V(l,k.source);if(w!==V(l,h))throw f(`marketplace candidate moves installed relative-source plugin "${p.name}" from ${h} to ${k.source}`);if(!$(w))throw g(`marketplace candidate source path does not exist for plugin "${p.name}": ${w}`);const R=x(w);if(R===null)throw g(`plugin manifest not found in marketplace candidate: ${w}/.crouter-plugin/plugin.json`);const Ce=p.scope==="project"?M("project"):S();await P(R,w,p.scope,Ce,`${w}/.crouter-plugin/plugin.json`)}j(o.root,c);try{j(l,o.root)}catch(p){throw j(c,o.root),p}E(c);for(const p of u){const k=s.plugins.find(m=>m.name===p.name);await qe(p.scope,p.name,k.version??p.manifest.version)}await O(o.scope,p=>{p.marketplaces[e]===void 0&&(p.marketplaces[e]={}),p.marketplaces[e].last_updated=Y()});const y={marketplace:o,manifest:s,pull:{updated:a!==T(o.root),sha:T(o.root)??""}};return n.set(e,y),y}finally{E(l),$(c)&&!$(o.root)&&j(c,o.root)}}d(Te,"refreshMarketplace");async function qe(e,n,t){await D(e,i=>{const o=i.plugins[n];o!==void 0?t!==void 0&&(o.version=t):i.plugins[n]={enabled:!0,version:t}}),await O(e,i=>{i.plugins[n]===void 0&&(i.plugins[n]={}),i.plugins[n].last_updated=Y()})}d(qe,"updatePluginVersion");async function Gn(e,n,t){const i=t==="project"?G():S();await J(t,i);const o=C(t),r=tn(e),a=v(i,"tmp",`${r}.${process.pid}.${Date.now()}`);L(v(i,"tmp"));try{W(e,a,{ref:n,depth:1});const l=x(a);if(l===null)throw f(`cloned repo does not contain a valid .crouter-plugin/plugin.json: ${e}`);const c=await P(l,a,t,i,`${e}/.crouter-plugin/plugin.json`),s=v(o,l.name);if($(s))throw f(`plugin "${l.name}" is already installed at ${s}`);return L(o),j(a,s),await D(t,u=>{u.plugins[l.name]={enabled:!0,version:l.version}}),{name:l.name,scope:t,path:s,...c!==void 0?{memory:c}:{}}}finally{E(a)}}d(Gn,"installFromGit");async function Jn(e,n){const t=V(e),i=x(t);if(i===null)throw g(`plugin manifest not found at ${t}/.crouter-plugin/plugin.json`);const o=n==="project"?G():S(),r=await P(i,t,n,o,`${t}/.crouter-plugin/plugin.json`,void 0,"linked");await J(n,o);const a=v(C(n),i.name);if($(a))throw f(`plugin "${i.name}" is already installed at ${a}`);return ce(t,a),await D(n,l=>{l.plugins[i.name]={enabled:!0,version:i.version}}),{name:i.name,scope:n,path:a,...r!==void 0?{memory:r}:{}}}d(Jn,"installFromLocal");async function Yn(e,n){const t=await Te(e.marketplace,new Map,{strict:!0});if(t===null)throw g(`marketplace not found: ${e.marketplace}`);const i=t.manifest.plugins.find(s=>s.name===e.plugin);if(i===void 0)throw g(`plugin "${e.plugin}" not found in marketplace "${e.marketplace}"`);const o=M(n);await J(n,o);const r=v(C(n),e.plugin);if($(r))throw f(`plugin "${e.plugin}" is already installed at ${r}`);let a,l;if(Ee(i.source)){const s=v(t.marketplace.root,i.source);if(!$(s))throw g(`plugin source path does not exist: ${s}`);const u=x(s);if(u===null)throw g(`plugin manifest not found at ${s}/.crouter-plugin/plugin.json`);l=await P(u,s,n,o,`${s}/.crouter-plugin/plugin.json`,void 0,"linked"),ce(s,r),a=u}else{const s=v(o,"tmp",`${e.plugin}.${process.pid}.${Date.now()}`);L(v(o,"tmp"));try{W(i.source,s,{depth:1});const u=x(s);if(u===null)throw g(`plugin manifest not found at ${s}/.crouter-plugin/plugin.json`);l=await P(u,s,n,o,`${s}/.crouter-plugin/plugin.json`),L(C(n)),j(s,r),a=u}finally{E(s)}}const c=i.version??a.version;return await D(n,s=>{const u={enabled:!0,source_marketplace:e.marketplace};c!==void 0&&(u.version=c),s.plugins[e.plugin]=u}),{name:e.plugin,scope:n,path:r,...l!==void 0?{memory:l}:{}}}d(Yn,"installFromMarketplace");async function Qn(e){if(K(e.root)){const l=x(e.root);if(l===null)throw g(`plugin manifest not found at ${e.root}/.crouter-plugin/plugin.json`);const c=e.scope==="project"?M("project"):S(),s=await P(l,e.root,e.scope,c,`${e.root}/.crouter-plugin/plugin.json`,void 0,"linked");return{updated:!1,sha:T(e.root)??"",manifest:l,...s!==void 0?{memory:s}:{}}}if(!Q(e.root)){const l=x(e.root);if(l===null)throw g(`plugin manifest not found at ${e.root}/.crouter-plugin/plugin.json`);const c=e.scope==="project"?M("project"):S(),s=await P(l,e.root,e.scope,c,`${e.root}/.crouter-plugin/plugin.json`);return{updated:!1,sha:"",manifest:l,...s!==void 0?{memory:s}:{}}}const n=de(["config","--get","remote.origin.url"],e.root).stdout.trim();if(n==="")throw f(`plugin "${e.name}" has no origin remote to stage an update from`);const t=de(["symbolic-ref","--short","HEAD"],e.root).stdout.trim();if(t==="")throw f(`plugin "${e.name}" is detached at its installed revision and has no tracked branch to update`);const i=e.scope==="project"?M("project"):S(),o=v(i,"tmp",`${e.name}.${process.pid}.${Date.now()}.candidate`),r=`${o}.previous`,a=T(e.root);L(v(i,"tmp"));try{W(n,o,{ref:t,depth:1});const l=x(o);if(l===null)throw g(`plugin manifest not found in update candidate: ${o}/.crouter-plugin/plugin.json`);const c=await P(l,o,e.scope,i,`${o}/.crouter-plugin/plugin.json`);j(e.root,r);try{j(o,e.root)}catch(u){throw j(r,e.root),u}E(r);const s=T(e.root)??"";return{updated:a!==s,sha:s,manifest:l,...c!==void 0?{memory:c}:{}}}finally{E(o),$(r)&&!$(e.root)&&j(r,e.root)}}d(Qn,"updateGitPluginCandidate");async function Ae(e,n,t){if(e.manifest.provider!==void 0)return{name:e.name,transport:"http",updated:!1,warnings:[`"${e.name}" is a capability provider bundle; the runtime refreshes it on grant sync`]};const i=be(e);if(!i&&e.manifest.bundle!==void 0){const u=await xe(e.name,e.manifest.bundle,e.scope,{enable:e.enabled});return{name:e.name,transport:"http",updated:!0,version:u.version,docs:u.docs,kinds:u.kinds,pageComponents:u.pageComponents,objectKinds:u.objectKinds,scopes:u.scopes,commands:u.commands,...u.hooks!==void 0?{hooks:u.hooks}:{},...u.memory!==void 0?{memory:u.memory}:{}}}e.manifest.transport?.kind==="http"&&!i&&$e(e.name);let o,r=!1;if(e.sourceMarketplace!==void 0){const u=await Te(e.sourceMarketplace,n,t);u!==null&&(o=u.manifest.plugins.find(p=>p.name===e.name)?.version,r=u.pull.updated)}const a=await Qn(e);await qe(e.scope,e.name,o??a.manifest.version);const l=await Z(e.name,e.scope),c=await ee(e.name,e.scope),s=[...we(a.manifest),...await te(a.manifest)];return{name:e.name,...e.manifest.transport!==void 0?{transport:e.manifest.transport.kind==="machine"?"http":e.manifest.transport.kind}:{},updated:r||a.updated,sha:a.sha,...l!==void 0?{commands:l}:{},...c!==void 0?{hooks:c}:{},...a.memory!==void 0?{memory:a.memory}:{},...s.length>0?{warnings:s}:{}}}d(Ae,"updateOnePlugin");async function q(e){const n=await He().documents.reconcilePackages().catch(i=>De(i)),t=n.conflicts.find(i=>i.plugin===e);if(t)throw new Ve("conflict",`plugin "${e}" is installed but its documents are not on the canvas: ${t.message}`,En.GENERAL,{plugin:e,next:"Uninstall the other package providing that plugin name, then run `crtr pkg plugin update --name "+e+"`."});return n}d(q,"reconcilePluginDocuments");function Xn(e,n){const t=[...(e.kinds?.refused??[]).filter(o=>o.plugin===n).map(o=>`object kind ${o.kind} not registered: ${o.message}`),...(e.kinds?.invalid??[]).filter(o=>o.plugin===n).map(o=>`object kinds and scopes not applied; the plugin holds no app token until this is fixed: ${o.error}`)],i=e.tokens?.granted.find(o=>o.plugin===n);return{warnings:t,...i!==void 0?{appScopes:i.scopes}:{}}}d(Xn,"pluginObjectReport");function U(e,n,t){const{warnings:i,appScopes:o}=Xn(n,t),a=[...e.warnings??[],...i];return{...e,...a.length>0?{warnings:a}:{},...o!==void 0?{appScopes:o}:{}}}d(U,"withObjectReport");const _t=B({name:"install",description:"install from a ref or HTTP endpoint",whenToUse:"installing one plugin from a marketplace ref, git URL, local path, or an HTTP directory-archive endpoint",help:{name:"pkg plugin install",summary:"install a plugin from a ref or HTTP endpoint",params:[{kind:"positional",name:"install-ref",type:"string",required:!1,constraint:"Plugin install ref. Provide exactly one of INSTALL-REF or --endpoint. Accepted ref forms: marketplace/plugin; git URL starting https://, ssh://, git@, file://, or ending .git; local path starting ./, ../, or /."},{kind:"flag",name:"endpoint",type:"string",required:!1,constraint:"HTTP plugin directory-archive endpoint. Provide exactly one of --endpoint or INSTALL-REF. Must be an absolute https: URL; http: is permitted only for localhost, any *.localhost host, 127.0.0.1, [::1], ::1, or host.docker.internal. Credentials and fragments are rejected."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Required with --endpoint and rejected without it. Lowercase kebab-case plugin handle."},{kind:"flag",name:"auth-env",type:"string",required:!1,constraint:"Optional environment variable holding the HTTP bearer token. Accepted only with --endpoint; its value is never stored."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: project if available, else user."},{kind:"flag",name:"ref",type:"string",required:!1,constraint:"Git ref (branch/tag) for git URL installs. Rejected with --endpoint; ignored for marketplace and local path refs."}],output:[{name:"name",type:"string",required:!0,constraint:"Plugin name as installed."},{name:"scope",type:"string",required:!0,constraint:"Scope the plugin was installed into."},{name:"path",type:"string",required:!0,constraint:"Absolute path to the installed plugin directory."},{name:"transport",type:"string",required:!1,constraint:"exec or http when the plugin declares commands. Always http for an --endpoint install."},{name:"version",type:"string",required:!1,constraint:"First 12 hex characters of the fetched archive SHA-256 for an --endpoint install."},{name:"docs",type:"integer",required:!1,constraint:"Memory document count written from an --endpoint archive."},{name:"kinds",type:"integer",required:!1,constraint:"Kind-registry entry count declared by an --endpoint archive\u2019s bundle.json."},{name:"pageComponents",type:"integer",required:!1,constraint:"Page-component registration count declared by an --endpoint archive\u2019s bundle.json."},{name:"commands",type:"object",required:!1,constraint:"Present only when the plugin declares a command manifest. {mounts: string[] (accepted command paths now live for the next invocation), issues: object[] (typed validation issues that rejected a contribution \u2014 {code, path?, message, received, expected, next})}. Validated statically; commands are never executed."},{name:"hooks",type:"object",required:!1,constraint:"Present only when the plugin declares hooks. {manifestPath?, executablePath?, declarations: {target, phase, op, description, effects}[], lifecycle?: {event, phase, op, description, effects}[], issues: object[], trust: string}. Command targets, lifecycle events, and replacement collisions are validated statically; hook executables are never run."},{name:"objectKinds",type:"integer",required:!1,constraint:"Canvas object kind count declared by an --endpoint archive\u2019s bundle.json `object_kinds` block."},{name:"scopes",type:"integer",required:!1,constraint:"Canvas scope count declared by an --endpoint archive\u2019s bundle.json `scopes` block."},{name:"appScopes",type:"string[]",required:!1,constraint:"Present when the plugin holds an app token after install: the canvas scopes that token carries (the manifest `scopes`; empty for a plugin that declares kinds or serves over HTTP but asks for none)."},{name:"warnings",type:"string[]",required:!1,constraint:"Advisory missing PATH executable requirements declared by the installed plugin, kinds another owner already holds (`object kind <kind> not registered`), and an invalid `object_kinds`/`scopes` block that leaves the plugin without an app token. The install still succeeds and leaves the plugin enabled."},{name:"memory",type:"object",required:!1,constraint:"Present only when the plugin ships documents (its `memory/` folder). {docs: integer (documents checked), linkedSource?: string (the caller-owned directory, present only when the install keeps that directory as a live link)}."}],outputKind:"object",effects:["A ref install clones, links, or copies the plugin into the scope plugins directory and registers it with enabled=true. Marketplace installs refresh the source marketplace before resolving the plugin entry.","An --endpoint install fetches and validates one authenticated uncompressed tar archive before writing. It replaces the complete plugin directory with synthesized provenance and invocation metadata, commands.json, memory docs, and, when declared, hooks.json plus the hooks/ tree; it also copies any kind-registry entries the bundle.json declares (they join the launch registry below this scope\u2019s own config.json kinds), and any page components it declares (they join the active page-component catalog alongside the user config entries); a fetch or validation failure preserves the prior package.","If the plugin declares a command manifest, its commands go live on the next crtr invocation. Exec commands run trusted local code only when explicitly invoked; HTTP commands call their declared endpoint only when explicitly invoked.","Plugin command hooks go live on the next crtr invocation. They run implicitly with caller authority, may receive normalized inputs and after results, may block or replace declared targets, and may transmit received data. Lifecycle hooks run unprompted at every declared event before node launch and may transmit node identity and resolved paths. Hook executables are never run during install or update validation.","A plugin manifest `requires` declaration is checked against the PATH a node bash receives after install. Missing executables emit a warning with the declared install hint but never fail the install, disable the plugin, or remove its bare-binary contributions.","Before activation the candidate\u2019s `memory/` documents are checked for identity findings and refused if it cannot mount; a local path or marketplace-relative source stays a live link, named as `memory.linkedSource`. A refused package installs nothing."]},run:d(async e=>{const n=e.installRef,t=e.endpoint,i=e.name,o=e.authEnv,r=e.scope,a=e.ref;if(H()){if(n===void 0||t!==void 0||i!==void 0||o!==void 0||a!==void 0||r!==void 0)throw b("a sandboxed node installs only INSTALL-REF as marketplace/plugin");return await Ie(n)}const l=qn(r);if(n===void 0==(t===void 0))throw b("provide exactly one of INSTALL-REF or --endpoint");if(t!==void 0){if(i===void 0)throw b("--name is required with --endpoint");if(a!==void 0)throw b("--ref is only valid with a git INSTALL-REF, not --endpoint");const h=await Wn(i,t,o,l);return U(h,await q(i),i)}if(i!==void 0)throw b("--name is only valid with --endpoint");if(o!==void 0)throw b("--auth-env is only valid with --endpoint");const c=zn(n),s=c.kind==="marketplace"?await Yn(c.value,l):c.kind==="local"?await Jn(c.value,l):await Gn(c.value,a,l),u=A(s.name,s.scope),y=await Z(s.name,s.scope),p=await ee(s.name,s.scope),k=u===null?[]:[...we(u.manifest),...await te(u.manifest)],m=await q(s.name);return U({...s,...u?.manifest.transport!==void 0?{transport:u.manifest.transport.kind}:{},...y!==void 0?{commands:y}:{},...p!==void 0?{hooks:p}:{},...k.length>0?{warnings:k}:{}},m,s.name)},"run")}),Bt=B({name:"remove",description:"remove plugin and directory",whenToUse:"uninstalling a plugin: deletes its directory and removes it from config",help:{name:"pkg plugin remove",summary:"remove a plugin and its directory from the given scope",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Plugin name to remove."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: searches every scope."}],output:[{name:"scopes",type:"string[]",required:!0,constraint:"Scopes the plugin was removed from."}],outputKind:"object",effects:["Deletes the plugin directory. Removes the plugin entry from config.json."]},run:d(async e=>{const n=e.name,t=e.scope;if(n==="crtr")throw b('cannot remove builtin plugin "crtr" \u2014 it ships with the binary');if(H())return await Ne(n);let i;if(t!==void 0){const r=re(t);if(r==="builtin")throw b("cannot remove plugins from builtin scope");i=r==="all"?["project","user"].filter(a=>a!=="project"||se()!==null):[r]}else i=["project","user"].filter(r=>r!=="project"||se()!==null);const o=[];for(const r of i){const a=C(r);if(a===null)continue;const l=v(a,n);$(l)&&(E(l),await D(r,c=>{delete c.plugins[n]}),await O(r,c=>{delete c.plugins[n]}),o.push(r))}if(o.length===0)throw g(`plugin not found: ${n}`);return await q(),{scopes:o}},"run")}),Ht=B({name:"enable",description:"enable a plugin",whenToUse:"reactivating a plugin that was previously disabled so its memory docs resolve again",help:{name:"pkg plugin enable",summary:"enable a plugin in the given scope",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Plugin name to enable."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: scope where the plugin is installed."}],output:[{name:"scope",type:"string",required:!0,constraint:"Scope the change was applied in."},{name:"memory",type:"object",required:!1,constraint:"Present only when the plugin ships documents (its `memory/` folder). {docs: integer (documents checked), linkedSource?: string (the caller-owned directory, present only for a symlinked install)}."},{name:"appScopes",type:"string[]",required:!1,constraint:"Present when the plugin holds an app token: the canvas scopes it carries."},{name:"warnings",type:"string[]",required:!1,constraint:"Kinds another owner holds, or an invalid `object_kinds`/`scopes` block. Enabling still succeeds."}],outputKind:"object",effects:["Validates the installed package before any config change: its `memory/` documents are checked for identity findings and refused if it cannot mount. A refusal leaves the plugin disabled.","Sets plugin enabled=true in config.json."]},run:d(async e=>{const n=e.name;if(H())return await oe(n,!0);const t=Tn(n,e.scope);if(t===null)throw g(`plugin not found: ${n}`);const i=x(t.root);if(i===null)throw g(`plugin manifest not found at ${t.root}/.crouter-plugin/plugin.json`);const o=t.scope==="project"?M("project"):S(),r=await P(i,t.root,t.scope,o,`${t.root}/.crouter-plugin/plugin.json`,void 0,K(t.root)?"linked":"package"),a=await ye(e,!0),l=await q(n);return U({scope:a.scope,...r!==void 0?{memory:r}:{}},l,n)},"run")}),Dt=B({name:"disable",description:"disable without removing",whenToUse:"hiding a plugin from resolution without deleting it, so you can re-enable it later",help:{name:"pkg plugin disable",summary:"disable a plugin (keeps files, hides from resolution)",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Plugin name to disable."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: scope where the plugin is installed."}],output:[{name:"scope",type:"string",required:!0,constraint:"Scope the change was applied in."}],outputKind:"object",effects:["Sets plugin enabled=false in config.json."]},run:d(async e=>{if(H())return await oe(e.name,!1);const n=await ye(e,!1);return await q(),{scope:n.scope}},"run")}),It=B({name:"update",description:"update installed plugin content",whenToUse:"refreshing one named plugin or all installed plugins: archive plugins replace their complete package, while source-installed plugins retain their source update behavior",help:{name:"pkg plugin update",summary:"refresh one or all installed plugins",params:[{kind:"flag",name:"name",type:"string",required:!1,constraint:"Plugin name to update. Omit to update all installed plugins."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Narrows resolution."}],output:[{name:"updated",type:"object[]",required:!0,constraint:"One entry per plugin processed: {name, transport?, updated, sha?, version?, docs?, kinds?, pageComponents?, commands?, hooks?, memory?, warnings?, error?}. memory is present for a plugin shipping documents and reports the same memory check install performs: {docs, linkedSource?}. Archive plugins report transport=http and replace their complete directory after an authenticated refetch; version is the archive content identifier, docs is the written memory-document count, kinds is the declared kind-registry entry count, pageComponents is the declared page-component registration count, objectKinds is the declared canvas object kind count, and scopes is the declared canvas scope count. appScopes lists the scopes the plugin\u2019s app token carries after the update. sha is present for git updates. A bulk update reports and skips a failed archive plugin. commands is present for validated command plugins: {mounts: string[] (accepted command paths), issues: object[]}. hooks is present for plugins declaring hooks: {manifestPath?, executablePath?, declarations: {target, phase, op, description, effects}[], lifecycle?: {event, phase, op, description, effects}[], issues: object[], trust: string}; it is static and never executes hooks. warnings lists advisory PATH requirements that remain absent after update, kinds another owner holds, and an invalid object_kinds/scopes block."}],outputKind:"object",effects:["Archive plugins unconditionally re-fetch, validate, and replace their complete package; a named failure exits nonzero and preserves the prior package. Bulk update reports a failed archive plugin and continues. Source-installed plugins retain their source update, version, and last_updated behavior.","A capability provider bundle (installed by the runtime from the directory) is skipped with a warning: the runtime refreshes it on grant sync.","Each candidate passes the same pre-activation memory check install performs."]},run:d(async e=>{const n=e.name;if(H())return await Fe(n);const t=e.scope,i=t===void 0?void 0:re(t),o=new Map;if(n!==void 0){const c=i===void 0||i==="all"||i==="builtin"?A(n):A(n,i);if(c===null)throw g(`plugin not found: ${n}${i===void 0||i==="all"||i==="builtin"?"":` (scope: ${i})`}`);const s=await Ae(c,o,{strict:!0}),u=await q(n);return{updated:[U(s,u,s.name)]}}const r=ae().filter(c=>c.scope!=="builtin"&&(i===void 0||i==="all"||c.scope===i)),a=[];for(const c of r)try{a.push(await Ae(c,o,{strict:!1}))}catch(s){a.push({name:c.name,...c.manifest.transport!==void 0?{transport:c.manifest.transport.kind}:{},updated:!1,error:s instanceof Error?s.message:String(s)})}const l=await q();return{updated:a.map(c=>c.error===void 0?U(c,l,c.name):c)}},"run")});export{P as assertPluginCandidateValid,Yn as installFromMarketplace,Kn as installPluginArchive,Ee as isMarketplaceRelativeSource,Dt as pluginDisable,Ht as pluginEnable,_t as pluginInstall,Bt as pluginRemove,It as pluginUpdate,Mt as revalidateBundlePlugin,Lt as validatePluginArchive};
18
+ `)[0]:String(R)}`)}const w=sn(h);if(w!==null)throw f(`plugin memory document ${_(n,m)} is invalid: ${w}`);for(const R of an(h?.extensions,k))throw f(`plugin memory document ${_(n,m)} is invalid: ${R.path}: ${R.message}`)}return In(e,n,t,o,a)}d(P,"assertPluginCandidateValid");function Dn(e){return{kind:"http",endpoint:new URL(e.endpoint).origin,...e.authEnv!==void 0?{authEnv:e.authEnv}:{}}}d(Dn,"invocationTransport");async function Nn(e,n,t,i,o){const r=await P(o,t,n,i,`${t}/.crouter-plugin/plugin.json`),a={name:e,scope:n,root:t,manifest:o,enabled:!0},[l,c]=await Promise.all([N(),F()]),s=X(a,new Set(D),l,c);if(s.issues.length>0)throw z(`bundle staged package for "${e}" is invalid`,s.issues);return r}d(Nn,"validateStagedBundle");function be(e){return e.sourceMarketplace!==void 0||Q(e.root)||K(e.root)}d(be,"isSourceInstalled");function $e(e){throw f(`HTTP plugin "${e}" uses the removed commands-only endpoint format`,{next:`Run \`crtr pkg plugin remove ${e}\`, then reinstall it with \`crtr pkg plugin install --name ${e} --endpoint <archive-url>\`.`})}d($e,"rejectLegacyHttpPlugin");function Fn(e,n,t){if(!$(t))return;const i=A(e,n);if(i?.manifest.bundle!==void 0)return i;throw i?.manifest.transport?.kind==="http"&&i!==void 0&&!be(i)&&$e(e),f(`plugin "${e}" already exists at ${t}; run \`crtr pkg plugin remove ${e}\` before installing the archive plugin.`)}d(Fn,"existingBundleRoot");function je(e,n,t,i){const o=ke(e,n.endpoint,n.authEnv),r={endpoint:o.endpoint,...o.authEnv!==void 0?{authEnv:o.authEnv}:{}},a=v(C(t),e);return Fn(e,t,a),{bundle:r,transport:Dn(r),scopeRootPath:i,root:a}}d(je,"resolveBundleTarget");function Pe(e,n){return{name:e,endpoint:n.endpoint,...n.authEnv!==void 0?{authEnv:n.authEnv}:{}}}d(Pe,"fetchTarget");const Un=6e4,Vn=1e4;async function Re(e,n,t,i){const o=n==="project"?G():S();return L(v(o,"tmp")),Ye(le(o,e),()=>i(o),{timeoutMs:t,timeoutError:d(()=>f(`timed out waiting for another crtr process to finish replacing plugin "${e}"`,{next:`Re-run once that process has finished, or check for a stalled crtr holding ${le(o,e)}.`}),"timeoutError")})}d(Re,"withBundleSwapLock");async function xe(e,n,t,i){return Re(e,t,Un,async o=>{const r=je(e,n,t,o),a=await he(Pe(e,r.bundle));if(a.status!==200){const l=`HTTP plugin bundle fetch failed for "${e}": ${a.message}`;throw a.status==="auth_env_missing"?b(l):a.status==="cli_unreachable"?Ue(l):f(l)}return Se(e,t,r,a.raw,a.etag,i)})}d(xe,"replaceBundlePlugin");async function Mt(e,n,t,i){return Re(e,t,Vn,async o=>{try{const r=je(e,n,t,o),a=i.conditional?Ke(t).plugins[e]?.etag:void 0,l={...a!==void 0?{ifNoneMatch:a}:{},...i.timeoutMs!==void 0?{timeoutMs:i.timeoutMs}:{}},c=await he(Pe(e,r.bundle),l);if(c.status===304)return;if(c.status!==200)throw f(`HTTP plugin bundle revalidation failed for "${e}": ${c.message}`);const s=vn(c.raw);if(kn(e,t,r.root,s)){await wn(t,e,c.etag,{swapped:!1});return}return await Se(e,t,r,c.raw,c.etag,i,s)}finally{await On(t,e)}})}d(Mt,"revalidateBundlePlugin");async function On(e,n){await O(e,t=>{t.plugins[n]===void 0&&(t.plugins[n]={}),t.plugins[n].checked_at=Y()})}d(On,"recordPluginCheck");async function Se(e,n,t,i,o,r,a){const{bundle:l,transport:c,scopeRootPath:s,root:u}=t,y=await Kn(e,i,{scope:n,scopeRootPath:s,root:u,bundle:l,transport:c,etag:o,enable:r.enable,...a!==void 0?{version:a}:{}}),{validated:p,version:k,staged:m}=y,h=await Z(e,n);if(h===void 0)throw f(`bundle plugin "${e}" has no staged command report`);const w=await ee(e,n);return{name:e,scope:n,path:u,transport:"http",version:k,docs:p.memory.length,kinds:Object.keys(p.kinds??{}).length,pageComponents:(p.page_components??[]).length,objectKinds:Object.keys(p.object_kinds??{}).length,scopes:(p.scopes??[]).length,commands:h,...w!==void 0?{hooks:w}:{},...m!==void 0?{memory:m}:{}}}d(Se,"applyBundleArchive");async function Lt(e){const[n,t]=await Promise.all([N(),F()]);return bn(e,{reservedCoreNames:new Set(D),coreCommandPaths:n,extensibleCoreBranches:t})}d(Lt,"validatePluginArchive");async function Kn(e,n,t){const[i,o]=await Promise.all([N(),F()]);return yn(e,n,{...t,validation:{reservedCoreNames:new Set(D),coreCommandPaths:i,extensibleCoreBranches:o},validateStaged:d((r,a)=>Nn(e,t.scope,r,t.scopeRootPath,a),"validateStaged"),invalid:d((r,a)=>z(r,a),"invalid")})}d(Kn,"installPluginArchive");async function Wn(e,n,t,i){const o=ke(e,n,t);return xe(e,{endpoint:o.endpoint,...o.authEnv!==void 0?{authEnv:o.authEnv}:{}},i,{enable:!0})}d(Wn,"installHttpPlugin");function zn(e){if(e.startsWith("./")||e.startsWith("../")||e.startsWith("/"))return{kind:"local",value:e};if(ve(e))return{kind:"git",value:e};const n=e.split("/");if(n.length===2&&n[0]!==""&&n[1]!=="")return{kind:"marketplace",value:{marketplace:n[0],plugin:n[1]}};throw b(`invalid plugin install ref: ${e}
19
+ Accepted forms: marketplace/plugin, git URL (https://, ssh://, git@, file://, or *.git), or local path starting ./, ../, or /. Bare plugin names are not install refs.`)}d(zn,"classifyInstallRef");function Ee(e){return e.startsWith("./")||e.startsWith("../")||!Be(e)&&!ve(e)}d(Ee,"isMarketplaceRelativeSource");async function Te(e,n,t){const i=n.get(e);if(i!==void 0)return i;const o=Oe(e);if(o===null){if(t.strict)throw g(`marketplace not found: ${e}`);return null}const r=ue(o.root);if(r===null)throw g(`marketplace manifest not found: ${o.root}/.crouter-marketplace/marketplace.json`);if(!Q(o.root)){const s={marketplace:o,manifest:r,pull:{updated:!1,sha:T(o.root)??""}};return n.set(e,s),s}const a=T(o.root),l=v(ie(o.root),`.${e}.${process.pid}.${Date.now()}.candidate`),c=`${l}.previous`;try{W(o.url,l,{ref:o.ref,depth:1});const s=ue(l);if(s===null)throw g(`marketplace manifest not found in candidate checkout: ${l}/.crouter-marketplace/marketplace.json`);const u=ae().filter(p=>p.sourceMarketplace===e&&K(p.root));for(const p of u){const k=s.plugins.find(Me=>Me.name===p.name);if(k===void 0||!Ee(k.source))throw f(`marketplace candidate removes relative-source plugin "${p.name}" while it is installed`);const m=Xe(p.root);if(m===null)throw f(`installed marketplace plugin "${p.name}" has no readable symlink target`);const h=_(o.root,V(ie(p.root),m)),w=V(l,k.source);if(w!==V(l,h))throw f(`marketplace candidate moves installed relative-source plugin "${p.name}" from ${h} to ${k.source}`);if(!$(w))throw g(`marketplace candidate source path does not exist for plugin "${p.name}": ${w}`);const R=x(w);if(R===null)throw g(`plugin manifest not found in marketplace candidate: ${w}/.crouter-plugin/plugin.json`);const Ce=p.scope==="project"?M("project"):S();await P(R,w,p.scope,Ce,`${w}/.crouter-plugin/plugin.json`)}j(o.root,c);try{j(l,o.root)}catch(p){throw j(c,o.root),p}E(c);for(const p of u){const k=s.plugins.find(m=>m.name===p.name);await qe(p.scope,p.name,k.version??p.manifest.version)}await O(o.scope,p=>{p.marketplaces[e]===void 0&&(p.marketplaces[e]={}),p.marketplaces[e].last_updated=Y()});const y={marketplace:o,manifest:s,pull:{updated:a!==T(o.root),sha:T(o.root)??""}};return n.set(e,y),y}finally{E(l),$(c)&&!$(o.root)&&j(c,o.root)}}d(Te,"refreshMarketplace");async function qe(e,n,t){await I(e,i=>{const o=i.plugins[n];o!==void 0?t!==void 0&&(o.version=t):i.plugins[n]={enabled:!0,version:t}}),await O(e,i=>{i.plugins[n]===void 0&&(i.plugins[n]={}),i.plugins[n].last_updated=Y()})}d(qe,"updatePluginVersion");async function Gn(e,n,t){const i=t==="project"?G():S();await J(t,i);const o=C(t),r=tn(e),a=v(i,"tmp",`${r}.${process.pid}.${Date.now()}`);L(v(i,"tmp"));try{W(e,a,{ref:n,depth:1});const l=x(a);if(l===null)throw f(`cloned repo does not contain a valid .crouter-plugin/plugin.json: ${e}`);const c=await P(l,a,t,i,`${e}/.crouter-plugin/plugin.json`),s=v(o,l.name);if($(s))throw f(`plugin "${l.name}" is already installed at ${s}`);return L(o),j(a,s),await I(t,u=>{u.plugins[l.name]={enabled:!0,version:l.version}}),{name:l.name,scope:t,path:s,...c!==void 0?{memory:c}:{}}}finally{E(a)}}d(Gn,"installFromGit");async function Jn(e,n){const t=V(e),i=x(t);if(i===null)throw g(`plugin manifest not found at ${t}/.crouter-plugin/plugin.json`);const o=n==="project"?G():S(),r=await P(i,t,n,o,`${t}/.crouter-plugin/plugin.json`,void 0,"linked");await J(n,o);const a=v(C(n),i.name);if($(a))throw f(`plugin "${i.name}" is already installed at ${a}`);return ce(t,a),await I(n,l=>{l.plugins[i.name]={enabled:!0,version:i.version}}),{name:i.name,scope:n,path:a,...r!==void 0?{memory:r}:{}}}d(Jn,"installFromLocal");async function Yn(e,n){const t=await Te(e.marketplace,new Map,{strict:!0});if(t===null)throw g(`marketplace not found: ${e.marketplace}`);const i=t.manifest.plugins.find(s=>s.name===e.plugin);if(i===void 0)throw g(`plugin "${e.plugin}" not found in marketplace "${e.marketplace}"`);const o=M(n);await J(n,o);const r=v(C(n),e.plugin);if($(r))throw f(`plugin "${e.plugin}" is already installed at ${r}`);let a,l;if(Ee(i.source)){const s=v(t.marketplace.root,i.source);if(!$(s))throw g(`plugin source path does not exist: ${s}`);const u=x(s);if(u===null)throw g(`plugin manifest not found at ${s}/.crouter-plugin/plugin.json`);l=await P(u,s,n,o,`${s}/.crouter-plugin/plugin.json`,void 0,"linked"),ce(s,r),a=u}else{const s=v(o,"tmp",`${e.plugin}.${process.pid}.${Date.now()}`);L(v(o,"tmp"));try{W(i.source,s,{depth:1});const u=x(s);if(u===null)throw g(`plugin manifest not found at ${s}/.crouter-plugin/plugin.json`);l=await P(u,s,n,o,`${s}/.crouter-plugin/plugin.json`),L(C(n)),j(s,r),a=u}finally{E(s)}}const c=i.version??a.version;return await I(n,s=>{const u={enabled:!0,source_marketplace:e.marketplace};c!==void 0&&(u.version=c),s.plugins[e.plugin]=u}),{name:e.plugin,scope:n,path:r,...l!==void 0?{memory:l}:{}}}d(Yn,"installFromMarketplace");async function Qn(e){if(K(e.root)){const l=x(e.root);if(l===null)throw g(`plugin manifest not found at ${e.root}/.crouter-plugin/plugin.json`);const c=e.scope==="project"?M("project"):S(),s=await P(l,e.root,e.scope,c,`${e.root}/.crouter-plugin/plugin.json`,void 0,"linked");return{updated:!1,sha:T(e.root)??"",manifest:l,...s!==void 0?{memory:s}:{}}}if(!Q(e.root)){const l=x(e.root);if(l===null)throw g(`plugin manifest not found at ${e.root}/.crouter-plugin/plugin.json`);const c=e.scope==="project"?M("project"):S(),s=await P(l,e.root,e.scope,c,`${e.root}/.crouter-plugin/plugin.json`);return{updated:!1,sha:"",manifest:l,...s!==void 0?{memory:s}:{}}}const n=de(["config","--get","remote.origin.url"],e.root).stdout.trim();if(n==="")throw f(`plugin "${e.name}" has no origin remote to stage an update from`);const t=de(["symbolic-ref","--short","HEAD"],e.root).stdout.trim();if(t==="")throw f(`plugin "${e.name}" is detached at its installed revision and has no tracked branch to update`);const i=e.scope==="project"?M("project"):S(),o=v(i,"tmp",`${e.name}.${process.pid}.${Date.now()}.candidate`),r=`${o}.previous`,a=T(e.root);L(v(i,"tmp"));try{W(n,o,{ref:t,depth:1});const l=x(o);if(l===null)throw g(`plugin manifest not found in update candidate: ${o}/.crouter-plugin/plugin.json`);const c=await P(l,o,e.scope,i,`${o}/.crouter-plugin/plugin.json`);j(e.root,r);try{j(o,e.root)}catch(u){throw j(r,e.root),u}E(r);const s=T(e.root)??"";return{updated:a!==s,sha:s,manifest:l,...c!==void 0?{memory:c}:{}}}finally{E(o),$(r)&&!$(e.root)&&j(r,e.root)}}d(Qn,"updateGitPluginCandidate");async function Ae(e,n,t){if(e.manifest.provider!==void 0)return{name:e.name,transport:"http",updated:!1,warnings:[`"${e.name}" is a capability provider bundle; the runtime refreshes it on grant sync`]};const i=be(e);if(!i&&e.manifest.bundle!==void 0){const u=await xe(e.name,e.manifest.bundle,e.scope,{enable:e.enabled});return{name:e.name,transport:"http",updated:!0,version:u.version,docs:u.docs,kinds:u.kinds,pageComponents:u.pageComponents,objectKinds:u.objectKinds,scopes:u.scopes,commands:u.commands,...u.hooks!==void 0?{hooks:u.hooks}:{},...u.memory!==void 0?{memory:u.memory}:{}}}e.manifest.transport?.kind==="http"&&!i&&$e(e.name);let o,r=!1;if(e.sourceMarketplace!==void 0){const u=await Te(e.sourceMarketplace,n,t);u!==null&&(o=u.manifest.plugins.find(p=>p.name===e.name)?.version,r=u.pull.updated)}const a=await Qn(e);await qe(e.scope,e.name,o??a.manifest.version);const l=await Z(e.name,e.scope),c=await ee(e.name,e.scope),s=[...we(a.manifest),...await te(a.manifest)];return{name:e.name,...e.manifest.transport!==void 0?{transport:e.manifest.transport.kind==="machine"?"http":e.manifest.transport.kind}:{},updated:r||a.updated,sha:a.sha,...l!==void 0?{commands:l}:{},...c!==void 0?{hooks:c}:{},...a.memory!==void 0?{memory:a.memory}:{},...s.length>0?{warnings:s}:{}}}d(Ae,"updateOnePlugin");async function q(e){const n=await He().documents.reconcilePackages().catch(i=>Ie(i)),t=n.conflicts.find(i=>i.plugin===e);if(t)throw new Ve("conflict",`plugin "${e}" is installed but its documents are not on the canvas: ${t.message}`,En.GENERAL,{plugin:e,next:"Uninstall the other package providing that plugin name, then run `crtr pkg plugin update --name "+e+"`."});return n}d(q,"reconcilePluginDocuments");function Xn(e,n){const t=[...(e.kinds?.refused??[]).filter(o=>o.plugin===n).map(o=>`object kind ${o.kind} not registered: ${o.message}`),...(e.kinds?.invalid??[]).filter(o=>o.plugin===n).map(o=>`object kinds and scopes not applied; the plugin holds no app token until this is fixed: ${o.error}`)],i=e.tokens?.granted.find(o=>o.plugin===n);return{warnings:t,...i!==void 0?{appScopes:i.scopes}:{}}}d(Xn,"pluginObjectReport");function U(e,n,t){const{warnings:i,appScopes:o}=Xn(n,t),a=[...e.warnings??[],...i];return{...e,...a.length>0?{warnings:a}:{},...o!==void 0?{appScopes:o}:{}}}d(U,"withObjectReport");const _t=B({name:"install",description:"install from a ref or HTTP endpoint",whenToUse:"installing one plugin from a marketplace ref, git URL, local path, or an HTTP directory-archive endpoint",help:{name:"pkg plugin install",summary:"install a plugin from a ref or HTTP endpoint",params:[{kind:"positional",name:"install-ref",type:"string",required:!1,constraint:"Plugin install ref. Provide exactly one of INSTALL-REF or --endpoint. Accepted ref forms: marketplace/plugin; git URL starting https://, ssh://, git@, file://, or ending .git; local path starting ./, ../, or /."},{kind:"flag",name:"endpoint",type:"string",required:!1,constraint:"HTTP plugin directory-archive endpoint. Provide exactly one of --endpoint or INSTALL-REF. Must be an absolute https: URL; http: is permitted only for localhost, any *.localhost host, 127.0.0.1, [::1], ::1, or host.docker.internal. Credentials and fragments are rejected."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Required with --endpoint and rejected without it. Lowercase kebab-case plugin handle."},{kind:"flag",name:"auth-env",type:"string",required:!1,constraint:"Optional environment variable holding the HTTP bearer token. Accepted only with --endpoint; its value is never stored."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: project if available, else user."},{kind:"flag",name:"ref",type:"string",required:!1,constraint:"Git ref (branch/tag) for git URL installs. Rejected with --endpoint; ignored for marketplace and local path refs."}],output:[{name:"name",type:"string",required:!0,constraint:"Plugin name as installed."},{name:"scope",type:"string",required:!0,constraint:"Scope the plugin was installed into."},{name:"path",type:"string",required:!0,constraint:"Absolute path to the installed plugin directory."},{name:"transport",type:"string",required:!1,constraint:"exec or http when the plugin declares commands. Always http for an --endpoint install."},{name:"version",type:"string",required:!1,constraint:"First 12 hex characters of the fetched archive SHA-256 for an --endpoint install."},{name:"docs",type:"integer",required:!1,constraint:"Memory document count written from an --endpoint archive."},{name:"kinds",type:"integer",required:!1,constraint:"Kind-registry entry count declared by an --endpoint archive\u2019s bundle.json."},{name:"pageComponents",type:"integer",required:!1,constraint:"Page-component registration count declared by an --endpoint archive\u2019s bundle.json."},{name:"commands",type:"object",required:!1,constraint:"Present only when the plugin declares a command manifest. {mounts: string[] (accepted command paths now live for the next invocation), issues: object[] (typed validation issues that rejected a contribution \u2014 {code, path?, message, received, expected, next})}. Validated statically; commands are never executed."},{name:"hooks",type:"object",required:!1,constraint:"Present only when the plugin declares hooks. {manifestPath?, executablePath?, declarations: {target, phase, op, description, effects}[], lifecycle?: {event, phase, op, description, effects}[], issues: object[], trust: string}. Command targets, lifecycle events, and replacement collisions are validated statically; hook executables are never run."},{name:"objectKinds",type:"integer",required:!1,constraint:"Canvas object kind count declared by an --endpoint archive\u2019s bundle.json `object_kinds` block."},{name:"scopes",type:"integer",required:!1,constraint:"Canvas scope count declared by an --endpoint archive\u2019s bundle.json `scopes` block."},{name:"appScopes",type:"string[]",required:!1,constraint:"Present when the plugin holds an app token after install: the canvas scopes that token carries (the manifest `scopes`; empty for a plugin that declares kinds or serves over HTTP but asks for none)."},{name:"warnings",type:"string[]",required:!1,constraint:"Advisory missing PATH executable requirements declared by the installed plugin, kinds another owner already holds (`object kind <kind> not registered`), and an invalid `object_kinds`/`scopes` block that leaves the plugin without an app token. The install still succeeds and leaves the plugin enabled."},{name:"memory",type:"object",required:!1,constraint:"Present only when the plugin ships documents (its `memory/` folder). {docs: integer (documents checked), linkedSource?: string (the caller-owned directory, present only when the install keeps that directory as a live link)}."}],outputKind:"object",effects:["A ref install clones, links, or copies the plugin into the scope plugins directory and registers it with enabled=true. Marketplace installs refresh the source marketplace before resolving the plugin entry.","An --endpoint install fetches and validates one authenticated uncompressed tar archive before writing. It replaces the complete plugin directory with synthesized provenance and invocation metadata, commands.json, memory docs, and, when declared, hooks.json plus the hooks/ tree; it also copies any kind-registry entries the bundle.json declares (they join the launch registry below this scope\u2019s own config.json kinds), and any page components it declares (they join the active page-component catalog alongside the user config entries); a fetch or validation failure preserves the prior package.","If the plugin declares a command manifest, its commands go live on the next crtr invocation. Exec commands run trusted local code only when explicitly invoked; HTTP commands call their declared endpoint only when explicitly invoked.","Plugin command hooks go live on the next crtr invocation. They run implicitly with caller authority, may receive normalized inputs and after results, may block or replace declared targets, and may transmit received data. Lifecycle hooks run unprompted at every declared event before node launch and may transmit node identity and resolved paths. Hook executables are never run during install or update validation.","A plugin manifest `requires` declaration is checked against the PATH a node bash receives after install. Missing executables emit a warning with the declared install hint but never fail the install, disable the plugin, or remove its bare-binary contributions.","Before activation the candidate\u2019s `memory/` documents are checked for identity findings and refused if it cannot mount; a local path or marketplace-relative source stays a live link, named as `memory.linkedSource`. A refused package installs nothing."]},run:d(async e=>{const n=e.installRef,t=e.endpoint,i=e.name,o=e.authEnv,r=e.scope,a=e.ref;if(H()){if(n===void 0||t!==void 0||i!==void 0||o!==void 0||a!==void 0||r!==void 0)throw b("a sandboxed node installs only INSTALL-REF as marketplace/plugin");return await De(n)}const l=qn(r);if(n===void 0==(t===void 0))throw b("provide exactly one of INSTALL-REF or --endpoint");if(t!==void 0){if(i===void 0)throw b("--name is required with --endpoint");if(a!==void 0)throw b("--ref is only valid with a git INSTALL-REF, not --endpoint");const h=await Wn(i,t,o,l);return U(h,await q(i),i)}if(i!==void 0)throw b("--name is only valid with --endpoint");if(o!==void 0)throw b("--auth-env is only valid with --endpoint");const c=zn(n),s=c.kind==="marketplace"?await Yn(c.value,l):c.kind==="local"?await Jn(c.value,l):await Gn(c.value,a,l),u=A(s.name,s.scope),y=await Z(s.name,s.scope),p=await ee(s.name,s.scope),k=u===null?[]:[...we(u.manifest),...await te(u.manifest)],m=await q(s.name);return U({...s,...u?.manifest.transport!==void 0?{transport:u.manifest.transport.kind}:{},...y!==void 0?{commands:y}:{},...p!==void 0?{hooks:p}:{},...k.length>0?{warnings:k}:{}},m,s.name)},"run")}),Bt=B({name:"remove",description:"remove plugin and directory",whenToUse:"uninstalling a plugin: deletes its directory and removes it from config",help:{name:"pkg plugin remove",summary:"remove a plugin and its directory from the given scope",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Plugin name to remove."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: searches every scope."}],output:[{name:"scopes",type:"string[]",required:!0,constraint:"Scopes the plugin was removed from."}],outputKind:"object",effects:["Deletes the plugin directory. Removes the plugin entry from config.json."]},run:d(async e=>{const n=e.name,t=e.scope;if(n==="crtr")throw b('cannot remove builtin plugin "crtr" \u2014 it ships with the binary');if(H())return await Ne(n);let i;if(t!==void 0){const r=re(t);if(r==="builtin")throw b("cannot remove plugins from builtin scope");i=r==="all"?["project","user"].filter(a=>a!=="project"||se()!==null):[r]}else i=["project","user"].filter(r=>r!=="project"||se()!==null);const o=[];for(const r of i){const a=C(r);if(a===null)continue;const l=v(a,n);$(l)&&(E(l),await I(r,c=>{delete c.plugins[n]}),await O(r,c=>{delete c.plugins[n]}),o.push(r))}if(o.length===0)throw g(`plugin not found: ${n}`);return await q(),{scopes:o}},"run")}),Ht=B({name:"enable",description:"enable a plugin",whenToUse:"reactivating a plugin that was previously disabled so its memory docs resolve again",help:{name:"pkg plugin enable",summary:"enable a plugin in the given scope",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Plugin name to enable."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: scope where the plugin is installed."}],output:[{name:"scope",type:"string",required:!0,constraint:"Scope the change was applied in."},{name:"memory",type:"object",required:!1,constraint:"Present only when the plugin ships documents (its `memory/` folder). {docs: integer (documents checked), linkedSource?: string (the caller-owned directory, present only for a symlinked install)}."},{name:"appScopes",type:"string[]",required:!1,constraint:"Present when the plugin holds an app token: the canvas scopes it carries."},{name:"warnings",type:"string[]",required:!1,constraint:"Kinds another owner holds, or an invalid `object_kinds`/`scopes` block. Enabling still succeeds."}],outputKind:"object",effects:["Validates the installed package before any config change: its `memory/` documents are checked for identity findings and refused if it cannot mount. A refusal leaves the plugin disabled.","Sets plugin enabled=true in config.json."]},run:d(async e=>{const n=e.name;if(H())return await oe(n,!0);const t=Tn(n,e.scope);if(t===null)throw g(`plugin not found: ${n}`);const i=x(t.root);if(i===null)throw g(`plugin manifest not found at ${t.root}/.crouter-plugin/plugin.json`);const o=t.scope==="project"?M("project"):S(),r=await P(i,t.root,t.scope,o,`${t.root}/.crouter-plugin/plugin.json`,void 0,K(t.root)?"linked":"package"),a=await ye(e,!0),l=await q(n);return U({scope:a.scope,...r!==void 0?{memory:r}:{}},l,n)},"run")}),It=B({name:"disable",description:"disable without removing",whenToUse:"hiding a plugin from resolution without deleting it, so you can re-enable it later",help:{name:"pkg plugin disable",summary:"disable a plugin (keeps files, hides from resolution)",params:[{kind:"positional",name:"name",type:"string",required:!0,constraint:"Plugin name to disable."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Default: scope where the plugin is installed."}],output:[{name:"scope",type:"string",required:!0,constraint:"Scope the change was applied in."}],outputKind:"object",effects:["Sets plugin enabled=false in config.json."]},run:d(async e=>{if(H())return await oe(e.name,!1);const n=await ye(e,!1);return await q(),{scope:n.scope}},"run")}),Dt=B({name:"update",description:"update installed plugin content",whenToUse:"refreshing one named plugin or all installed plugins: archive plugins replace their complete package, while source-installed plugins retain their source update behavior",help:{name:"pkg plugin update",summary:"refresh one or all installed plugins",params:[{kind:"flag",name:"name",type:"string",required:!1,constraint:"Plugin name to update. Omit to update all installed plugins."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Narrows resolution."}],output:[{name:"updated",type:"object[]",required:!0,constraint:"One entry per plugin processed: {name, transport?, updated, sha?, version?, docs?, kinds?, pageComponents?, commands?, hooks?, memory?, warnings?, error?}. memory is present for a plugin shipping documents and reports the same memory check install performs: {docs, linkedSource?}. Archive plugins report transport=http and replace their complete directory after an authenticated refetch; version is the archive content identifier, docs is the written memory-document count, kinds is the declared kind-registry entry count, pageComponents is the declared page-component registration count, objectKinds is the declared canvas object kind count, and scopes is the declared canvas scope count. appScopes lists the scopes the plugin\u2019s app token carries after the update. sha is present for git updates. A bulk update reports and skips a failed archive plugin. commands is present for validated command plugins: {mounts: string[] (accepted command paths), issues: object[]}. hooks is present for plugins declaring hooks: {manifestPath?, executablePath?, declarations: {target, phase, op, description, effects}[], lifecycle?: {event, phase, op, description, effects}[], issues: object[], trust: string}; it is static and never executes hooks. warnings lists advisory PATH requirements that remain absent after update, kinds another owner holds, and an invalid object_kinds/scopes block."}],outputKind:"object",effects:["Archive plugins unconditionally re-fetch, validate, and replace their complete package; a named failure exits nonzero and preserves the prior package. Bulk update reports a failed archive plugin and continues. Source-installed plugins retain their source update, version, and last_updated behavior.","A capability provider bundle (installed by the runtime from the directory) is skipped with a warning: the runtime refreshes it on grant sync.","Each candidate passes the same pre-activation memory check install performs."]},run:d(async e=>{const n=e.name;if(H())return await Fe(n);const t=e.scope,i=t===void 0?void 0:re(t),o=new Map;if(n!==void 0){const c=i===void 0||i==="all"||i==="builtin"?A(n):A(n,i);if(c===null)throw g(`plugin not found: ${n}${i===void 0||i==="all"||i==="builtin"?"":` (scope: ${i})`}`);const s=await Ae(c,o,{strict:!0}),u=await q(n);return{updated:[U(s,u,s.name)]}}const r=ae().filter(c=>c.scope!=="builtin"&&(i===void 0||i==="all"||c.scope===i)),a=[];for(const c of r)try{a.push(await Ae(c,o,{strict:!1}))}catch(s){a.push({name:c.name,...c.manifest.transport!==void 0?{transport:c.manifest.transport.kind}:{},updated:!1,error:s instanceof Error?s.message:String(s)})}const l=await q();return{updated:a.map(c=>c.error===void 0?U(c,l,c.name):c)}},"run")});export{P as assertPluginCandidateValid,Yn as installFromMarketplace,Kn as installPluginArchive,Ee as isMarketplaceRelativeSource,It as pluginDisable,Ht as pluginEnable,_t as pluginInstall,Bt as pluginRemove,Dt as pluginUpdate,Mt as revalidateBundlePlugin,Lt as validatePluginArchive};
@@ -1,10 +1,10 @@
1
- var y=Object.defineProperty;var d=(n,o)=>y(n,"name",{value:o,configurable:!0});import{createProfileBlaxelEnvironment as v}from"../../environments.js";import{KEY_MAPPED_TABLES as U,mapPath as S}from"../bundle/layout.js";import{ImportLaneError as O}from"./stage.js";import{q as l}from"./catalog.js";const $=d((n,o)=>`${l(n)}.${l(o)}`,"sq");async function F(n,o,c){if(c.length===0)return 0;const e=d(s=>$(o,s),"S"),t=`(SELECT node_id FROM ${e("nodes")} WHERE status IN ('active', 'idle'))`;let a=0;const E=d(async(s,r,i,u=!1)=>{const _=await n.prepareUncached(`SELECT ${l(r)}::text AS value FROM ${e(s)} WHERE ${i} AND ${l(r)} IS NOT NULL`).all();for(const{value:R}of _){const T=u?H(R,c):S(c,R);T!==R&&(await n.prepareUncached(`UPDATE ${e(s)} SET ${l(r)} = ? WHERE ${l(r)}::text = ? AND ${i}`).run(T,R),a+=1)}},"apply");return await E("nodes","cwd",`node_id IN ${t}`),await E("worktrees","path",`node_id IN ${t}`),await E("worktrees","repo_root",`node_id IN ${t}`),await E("crons","cwd",`anchor_node IN ${t}`),await E("provider_approvals","prepared_files","TRUE",!0),await E("upload_uses","path",`run_id IN ${t}`),a}d(F,"rewritePaths");async function g(n,o){const c=$(o,"human_requests"),e=d(a=>`CASE WHEN ${a} IS NULL OR ${a} NOT LIKE '/%' THEN ${a}
1
+ var y=Object.defineProperty;var d=(n,o)=>y(n,"name",{value:o,configurable:!0});import{createProfileBlaxelEnvironment as v}from"../../environments.js";import{KEY_MAPPED_TABLES as U,mapPath as S}from"../bundle/layout.js";import{ImportLaneError as O}from"./stage.js";import{q as l}from"./catalog.js";const $=d((n,o)=>`${l(n)}.${l(o)}`,"sq");async function g(n,o,c){if(c.length===0)return 0;const e=d(s=>$(o,s),"S"),t=`(SELECT node_id FROM ${e("nodes")} WHERE status IN ('active', 'idle'))`;let a=0;const E=d(async(s,r,i,u=!1)=>{const _=await n.prepareUncached(`SELECT ${l(r)}::text AS value FROM ${e(s)} WHERE ${i} AND ${l(r)} IS NOT NULL`).all();for(const{value:R}of _){const h=u?A(R,c):S(c,R);h!==R&&(await n.prepareUncached(`UPDATE ${e(s)} SET ${l(r)} = ? WHERE ${l(r)}::text = ? AND ${i}`).run(h,R),a+=1)}},"apply");return await E("nodes","cwd",`node_id IN ${t}`),await E("worktrees","path",`node_id IN ${t}`),await E("worktrees","repo_root",`node_id IN ${t}`),await E("crons","cwd",`anchor_node IN ${t}`),await E("provider_approvals","prepared_files","TRUE",!0),await E("upload_uses","path",`run_id IN ${t}`),a}d(g,"rewritePaths");async function j(n,o){const c=$(o,"human_requests"),e=d(a=>`CASE WHEN ${a} IS NULL OR ${a} NOT LIKE '/%' THEN ${a}
2
2
  WHEN rtrim(${a}, '/') = rtrim(request_dir, '/') THEN 'requests/' || request_id
3
3
  WHEN ${a} LIKE rtrim(request_dir, '/') || '/%' THEN 'requests/' || request_id || '/' || substr(${a}, length(rtrim(request_dir, '/')) + 2)
4
4
  ELSE 'requests/' || request_id || '/source/' || regexp_replace(${a}, '^.*/', '') END`,"moved"),t=await n.prepareUncached(`UPDATE ${c} SET file = ${e("file")}, presentation_dir = ${e("presentation_dir")},
5
- request_dir = CASE WHEN request_dir LIKE '/%' THEN 'requests/' || request_id ELSE request_dir END`).run();return Number(t.changes)}d(g,"rewriteRequestPaths");function H(n,o){const c=d(e=>typeof e=="string"?S(o,e):Array.isArray(e)?e.map(c):e!==null&&typeof e=="object"?Object.fromEntries(Object.entries(e).map(([t,a])=>[t,c(a)])):e,"walk");try{return JSON.stringify(c(JSON.parse(n)))}catch{return S(o,n)}}d(H,"rewriteJsonPaths");async function j(n,o,c,e,t){const a=d(r=>$(o,r),"S"),E=await n.prepareUncached(`SELECT id, name FROM ${a("profiles")} ORDER BY id`).all();let s=0;for(const r of E)await n.prepareUncached(`SELECT 1 AS found FROM ${$(c,"profiles")} WHERE person = ? AND name = ? AND id <> ?`).get(t,r.name,r.id)!==void 0&&(await n.prepareUncached(`UPDATE ${a("profiles")} SET name = ? WHERE id = ?`).run(`${r.name}-${e}`,r.id),s+=1);return s}d(j,"renameClashingProfiles");async function q(n,o,c,e,t){const a=d(r=>$(o,r),"S");let E=0;const s=await n.prepareUncached(`SELECT id, grantee FROM ${a("profiles")} WHERE environment_id IS NULL ORDER BY id`).all();for(const r of s){let i=e.kind==="machine"?e.environmentObject??null:null;if(e.kind==="hosted"&&(i=(await n.prepareUncached(`SELECT environment_id FROM ${$(c,"profiles")} WHERE id = ?`).get(r.id))?.environment_id??await v(n,{profileId:r.id,grantee:r.grantee??"app:terminal",person:t})),i===null)throw new O("fill","bundle_invalid",`profile ${r.id} has no environment and the source names none`);await n.prepareUncached(`UPDATE ${a("profiles")} SET environment_id = ? WHERE id = ?`).run(i,r.id),E+=1}return E}d(q,"fillEnvironments");async function x(n,o,c,e){const t=d(s=>$(o,s),"S"),a={},E=d(async(s,r,...i)=>{const u=Number((await n.prepareUncached(r).run(...i)).changes);u>0&&(a[s]=(a[s]??0)+u)},"del");await E("limits",`DELETE FROM ${t("limits")} WHERE source <> 'person'`),e.kind==="hosted"&&(await n.prepareUncached(`UPDATE ${t("profiles")} SET environment_id = NULL WHERE environment_id IN (SELECT object FROM ${t("environments")})`).run(),await n.prepareUncached(`UPDATE ${t("objects")} SET home_machine = 'import-hosted-source' WHERE id IN (SELECT object FROM ${t("environments")})`).run());for(const[s,r]of Object.entries(U))await E(s,`DELETE FROM ${t(s)} WHERE ${l(r)}::text IN (SELECT source_key FROM import_key_map WHERE source_install_id = ? AND table_name = ?)`,e.installId,s);for(const s of c.tables.values())s.columns.includes("claimed_by")&&s.columns.includes("lease_expires_at")&&await n.prepareUncached(`UPDATE ${t(s.name)} SET claimed_by = NULL, lease_expires_at = NULL WHERE claimed_by IS NOT NULL OR lease_expires_at IS NOT NULL`).run();return a}d(x,"pruneStaged");async function b(n,o,c){const e=d(E=>$(o,E),"S"),t=`(SELECT d.object FROM ${e("documents")} d WHERE EXISTS (SELECT 1 FROM ${$(c,"documents")} t WHERE t.object = d.object))`;await n.exec(`CREATE TABLE ${e("_arr_revisions")} AS SELECT r.* FROM ${e("revisions")} r WHERE r.document IN ${t};
5
+ request_dir = CASE WHEN request_dir LIKE '/%' THEN 'requests/' || request_id ELSE request_dir END`).run();return Number(t.changes)}d(j,"rewriteRequestPaths");function A(n,o){const c=d(e=>typeof e=="string"?S(o,e):Array.isArray(e)?e.map(c):e!==null&&typeof e=="object"?Object.fromEntries(Object.entries(e).map(([t,a])=>[t,c(a)])):e,"walk");try{return JSON.stringify(c(JSON.parse(n)))}catch{return S(o,n)}}d(A,"rewriteJsonPaths");async function q(n,o,c,e,t){const a=d(r=>$(o,r),"S"),E=await n.prepareUncached(`SELECT id, name FROM ${a("profiles")} ORDER BY id`).all();let s=0;for(const r of E)await n.prepareUncached(`SELECT 1 AS found FROM ${$(c,"profiles")} WHERE person = ? AND name = ? AND id <> ?`).get(t,r.name,r.id)!==void 0&&(await n.prepareUncached(`UPDATE ${a("profiles")} SET name = ? WHERE id = ?`).run(`${r.name}-${e}`,r.id),s+=1);return s}d(q,"renameClashingProfiles");async function x(n,o,c,e,t){const a=d(r=>$(o,r),"S");let E=0;const s=await n.prepareUncached(`SELECT id, grantee FROM ${a("profiles")} WHERE environment_id IS NULL ORDER BY id`).all();for(const r of s){let i=e.kind==="machine"?e.environmentObject??null:null;if(e.kind==="hosted"&&(i=(await n.prepareUncached(`SELECT environment_id FROM ${$(c,"profiles")} WHERE id = ?`).get(r.id))?.environment_id??await v(n,{profileId:r.id,grantee:r.grantee??"app:terminal",person:t})),i===null)throw new O("fill","bundle_invalid",`profile ${r.id} has no environment and the source names none`);await n.prepareUncached(`UPDATE ${a("profiles")} SET environment_id = ? WHERE id = ?`).run(i,r.id),E+=1}return E}d(x,"fillEnvironments");async function k(n,o,c,e){const t=d(s=>$(o,s),"S"),a={},E=d(async(s,r,...i)=>{const u=Number((await n.prepareUncached(r).run(...i)).changes);u>0&&(a[s]=(a[s]??0)+u)},"del");await E("limits",`DELETE FROM ${t("limits")} WHERE source <> 'person'`),e.kind==="hosted"&&(await n.prepareUncached(`UPDATE ${t("profiles")} SET environment_id = NULL WHERE environment_id IN (SELECT object FROM ${t("environments")})`).run(),await n.prepareUncached(`UPDATE ${t("objects")} SET home_machine = 'import-hosted-source' WHERE id IN (SELECT object FROM ${t("environments")})`).run());for(const[s,r]of Object.entries(U))await E(s,`DELETE FROM ${t(s)} WHERE ${l(r)}::text IN (SELECT source_key FROM import_key_map WHERE source_install_id = ? AND table_name = ?)`,e.installId,s);for(const s of c.tables.values())s.columns.includes("claimed_by")&&s.columns.includes("lease_expires_at")&&await n.prepareUncached(`UPDATE ${t(s.name)} SET claimed_by = NULL, lease_expires_at = NULL WHERE claimed_by IS NOT NULL OR lease_expires_at IS NOT NULL`).run();return a}d(k,"pruneStaged");async function b(n,o,c){const e=d(E=>$(o,E),"S"),t=`(SELECT d.object FROM ${e("documents")} d WHERE EXISTS (SELECT 1 FROM ${$(c,"documents")} t WHERE t.object = d.object))`;await n.exec(`CREATE TABLE ${e("_arr_revisions")} AS SELECT r.* FROM ${e("revisions")} r WHERE r.document IN ${t};
6
6
  CREATE TABLE ${e("_arr_parents")} AS SELECT p.* FROM ${e("revision_parents")} p WHERE p.child IN (SELECT rev_id FROM ${e("revisions")} WHERE document IN ${t})`);const a=await n.prepareUncached(`SELECT r.document, r.rev_id, r.body_hash, r.rationale, r.created_at,
7
7
  COALESCE((SELECT array_agg(p.parent) FROM ${e("revision_parents")} p WHERE p.child = r.rev_id), ARRAY[]::text[]) AS parents
8
- FROM ${e("revisions")} r WHERE r.document IN ${t} ORDER BY r.document, r.number`).all();return await n.prepareUncached(`DELETE FROM ${e("revision_parents")} WHERE child IN (SELECT rev_id FROM ${e("revisions")} WHERE document IN ${t})`).run(),await n.prepareUncached(`DELETE FROM ${e("revisions")} WHERE document IN ${t}`).run(),await n.prepareUncached(`DELETE FROM ${e("objects")} WHERE id IN ${t}`).run(),await n.prepareUncached(`DELETE FROM ${e("documents")} WHERE object IN ${t}`).run(),a.map(E=>({...E}))}d(b,"takeRepoSyncedDocuments");async function k(n,o,c,e,t,a,E){const s=d(i=>[...E.get(i)??[],...i==="profiles"?["environment_id"]:[],"claimed_by","lease_expires_at"],"ignored"),r=[];for(const i of t){const u=e.tables.get(i);if(u===void 0||a.has(i)||u.primaryKey.length===0)continue;const _=u.primaryKey,R=_.some(p=>u.identity.includes(p)),T=_.map(p=>`s.${l(p)} = t.${l(p)}`).join(" AND "),w=R?"TRUE":_.map(p=>`s.${l(p)} IS DISTINCT FROM t.${l(p)}`).join(" OR "),I=new Set([...u.identity,...E.get(i)??[]]);for(const p of e.uniqueKeys.filter(f=>f.table===i&&!f.columns.every(m=>I.has(m)))){const f=p.predicate===null?"":` WHERE ${p.predicate}`,m=p.columns.map(h=>`s.${l(h)} = t.${l(h)}`).join(" AND "),L=await n.prepareUncached(`SELECT ${p.columns.map(h=>`s.${l(h)}::text`).join(" || ',' || ")} AS value
9
- FROM (SELECT * FROM ${$(o,i)}${f}) s JOIN (SELECT * FROM ${$(c,i)}${f}) t ON ${m} AND (${w}) LIMIT 10`).all();L.length>0&&r.push({table:i,key:p.name,values:L.map(h=>h.value)})}const N=u.columns.filter(p=>!u.identity.includes(p)&&!s(i).includes(p));if(N.length>0&&!R){const p=N.map(m=>`s.${l(m)} IS DISTINCT FROM t.${l(m)}`).join(" OR "),f=await n.prepareUncached(`SELECT ${_.map(m=>`s.${l(m)}::text`).join(" || ',' || ")} AS value
10
- FROM ${$(o,i)} s JOIN ${$(c,i)} t ON ${T} WHERE ${p} LIMIT 10`).all();f.length>0&&r.push({table:i,key:"primary key payload",values:f.map(m=>m.value)})}}if(r.length>0){const i=r[0];throw new O("pre-check","unique_conflict",`${i.table} ${i.key} collides with the target: ${i.values.join(", ")}`,{collisions:r})}}d(k,"precheck");export{q as fillEnvironments,k as precheck,x as pruneStaged,j as renameClashingProfiles,F as rewritePaths,g as rewriteRequestPaths,b as takeRepoSyncedDocuments};
8
+ FROM ${e("revisions")} r WHERE r.document IN ${t} ORDER BY r.document, r.number`).all();return await n.prepareUncached(`DELETE FROM ${e("revision_parents")} WHERE child IN (SELECT rev_id FROM ${e("revisions")} WHERE document IN ${t})`).run(),await n.prepareUncached(`DELETE FROM ${e("revisions")} WHERE document IN ${t}`).run(),await n.prepareUncached(`DELETE FROM ${e("objects")} WHERE id IN ${t}`).run(),await n.prepareUncached(`DELETE FROM ${e("documents")} WHERE object IN ${t}`).run(),a.map(E=>({...E}))}d(b,"takeRepoSyncedDocuments");const H={edges:["created_at"],relations:["created_at"]};async function P(n,o,c,e,t,a,E){const s=d(i=>[...E.get(i)??[],...i==="profiles"?["environment_id"]:[],...H[i]??[],"claimed_by","lease_expires_at"],"ignored"),r=[];for(const i of t){const u=e.tables.get(i);if(u===void 0||a.has(i)||u.primaryKey.length===0)continue;const _=u.primaryKey,R=_.some(p=>u.identity.includes(p)),h=_.map(p=>`s.${l(p)} = t.${l(p)}`).join(" AND "),w=R?"TRUE":_.map(p=>`s.${l(p)} IS DISTINCT FROM t.${l(p)}`).join(" OR "),I=new Set([...u.identity,...E.get(i)??[]]);for(const p of e.uniqueKeys.filter(f=>f.table===i&&!f.columns.every(m=>I.has(m)))){const f=p.predicate===null?"":` WHERE ${p.predicate}`,m=p.columns.map(T=>`s.${l(T)} = t.${l(T)}`).join(" AND "),L=await n.prepareUncached(`SELECT ${p.columns.map(T=>`s.${l(T)}::text`).join(" || ',' || ")} AS value
9
+ FROM (SELECT * FROM ${$(o,i)}${f}) s JOIN (SELECT * FROM ${$(c,i)}${f}) t ON ${m} AND (${w}) LIMIT 10`).all();L.length>0&&r.push({table:i,key:p.name,values:L.map(T=>T.value)})}const N=u.columns.filter(p=>!u.identity.includes(p)&&!s(i).includes(p));if(N.length>0&&!R){const p=N.map(m=>`s.${l(m)} IS DISTINCT FROM t.${l(m)}`).join(" OR "),f=await n.prepareUncached(`SELECT ${_.map(m=>`s.${l(m)}::text`).join(" || ',' || ")} AS value
10
+ FROM ${$(o,i)} s JOIN ${$(c,i)} t ON ${h} WHERE ${p} LIMIT 10`).all();f.length>0&&r.push({table:i,key:"primary key payload",values:f.map(m=>m.value)})}}if(r.length>0){const i=r[0];throw new O("pre-check","unique_conflict",`${i.table} ${i.key} collides with the target: ${i.values.join(", ")}`,{collisions:r})}}d(P,"precheck");export{x as fillEnvironments,P as precheck,k as pruneStaged,q as renameClashingProfiles,g as rewritePaths,j as rewriteRequestPaths,b as takeRepoSyncedDocuments};
@@ -0,0 +1,10 @@
1
+ import type { ImportRunDTO } from '../../../api/dto/imports.js';
2
+ import type { ImportPluginsStoreDTO } from '../../../api/dto/imports.js';
3
+ export type RefusedPlugin = NonNullable<ImportRunDTO['refused_plugins']>[number];
4
+ export interface PublishOutcome {
5
+ /** Plugins the directory refused: still imported as installs, their documents fall to I23. */
6
+ refused: RefusedPlugin[];
7
+ /** Plugins the directory could not answer for (unreachable): the run must not go on, or I23 would shadow the shared copy. */
8
+ unavailable: string[];
9
+ }
10
+ export declare function publishImportedPlugins(plugins: ImportPluginsStoreDTO | null): Promise<PublishOutcome>;
@@ -0,0 +1 @@
1
+ var i=Object.defineProperty;var t=(o,n)=>i(o,"name",{value:n,configurable:!0});import{reportPublished as f,syncInstall as c}from"../../packages/directory-sync.js";import{normalizeSourceUrl as d}from"../../packages/source-url.js";import{hasFleetSource as p}from"./stores.js";async function $(o){const n={refused:[],unavailable:[]};if(o===null)return n;const s=new Map(o.marketplaces.filter(e=>e.url!=null).map(e=>[`${e.owner}\0${e.name}`,d(e.url)])),l=new Map;for(const e of o.plugins){if(!e.enabled||!p(e))continue;const a=s.get(`${e.owner}\0${e.source_marketplace}`);a!==void 0&&l.set(`${e.name}\0${a}`,{name:e.name,url:a})}for(const{name:e,url:a}of l.values()){const{out:r,published:u}=await c(e,a);u!==void 0&&f(e,u),!r.ok&&(r.reason==="unavailable"?n.unavailable.push(e):n.refused.push({plugin:e,reason:r.reason,...r.reason==="name_taken"&&r.holder!==void 0?{holder:r.holder}:{}}))}return n}t($,"publishImportedPlugins");export{$ as publishImportedPlugins};
@@ -1,3 +1,3 @@
1
- var J=Object.defineProperty;var u=(e,s)=>J(e,"name",{value:s,configurable:!0});import{zstdCompressSync as q}from"node:zlib";import{blobStore as G}from"../../blobs/index.js";import{openDb as E,withCanvasWrite as $}from"../../canvas/db.js";import{insertImportedRevisions as z}from"../../graph/documents.js";import{getById as Q}from"../../graph/objects.js";import{bundleKeys as y,decodeRows as V,rowKeys as Y,SEQ_TABLES as X}from"../bundle/layout.js";import{readCatalog as Z}from"./catalog.js";import{copyRows as x,decodeKeys as I,encodeKeys as A}from"./copy.js";import{pruneStaged as ee,precheck as te,renameClashingProfiles as ae,rewritePaths as ne,rewriteRequestPaths as ie,takeRepoSyncedDocuments as se}from"./prepare.js";import{boundSub as re,checkSourcePerson as oe,checkStagedBlobKeys as de,checkStagedPersons as ce,checkStagedReferences as pe,checkStagedRequestKeys as ue}from"./bind.js";import{deleteRunBlobs as fe,moveRunBlobs as me}from"./blobs.js";import{dropStubRows as le,findFleetGrant as _e,grantMissing as we,rewriteIds as ge,rewritePerson as be}from"./rewrite.js";import{dropStaging as M,ImportLaneError as g,stage as he,stagingSchema as Ee,targetSchema as ye}from"./stage.js";import{verifyRun as Se}from"./verify.js";import{sandboxPathMap as ve,writeNodeFiles as Re}from"./path-map.js";import{allStoreKeys as Oe,writeStores as Te}from"./stores.js";import{COPIED_TABLES as ke}from"../bundle/layout.js";import{ClaimLostError as Pe}from"../../../daemon/reconcilers/row-claim.js";const je=10*6e4,S=u(()=>new Date().toISOString(),"now"),N=u(async(e,s)=>{await(await E()).prepare("UPDATE import_runs SET step = ? WHERE run_id = ?").run(s,e)},"setStep");async function De(e,s){const n=await e.get(y.source(s));if(n===null)throw new g("stage","bundle_invalid","source.json is missing");return JSON.parse(n.toString("utf8"))}u(De,"readSource");async function Ie(e,s,n,r,i,d){const a=await e.get(y.rows(n.run_id,"grants")),f=a===null?[]:V(a),m=[];for(const t of f){const c=re(t.sub,r,n.person,"grants.sub");await _e(s,i,d,c,t.grantee)===void 0&&m.push(t.grantee)}if(m.length===0)return!0;if(n.loaded_at===void 0||n.loaded_at===null)throw new g("stage","import_failed","a loading run has no loaded_at");if(Date.now()-Date.parse(n.loaded_at)>=je)throw we(m);return!1}u(Ie,"grantsReady");async function tt(e,s,n={}){const r=n.blobs??await G(),i=await E(),d=Ee(e.run_id),a=u(async t=>{await s.assertOwned(),await N(e.run_id,t),await n.atStep?.(t)},"at");let f=e.state==="committed";const m=[];try{const t=await De(r,e.run_id);if(e.source_kind==="machine"&&t.installId!==e.source_install_id)throw new g("stage","bundle_invalid",`source.json names install ${t.installId}, the run is for ${e.source_install_id}`);t.installId=e.source_install_id;const c=await ye(i),b=[...ke],w=await Z(i,c,[...b,"grants"]);if(!f){const l={},O=[],T=n.fleetRuntime??process.env.CRTR_RUNTIME??null;if(await oe(i,c,t.person,e.person),!await Ie(r,i,e,t.person,c,T)){await s.assertOwned(),await N(e.run_id,"waiting_for_grants");return}await a("stage");const W=await he(i,e.run_id,t.counts,o=>Y(e.run_id,o,t),o=>r.get(o));for(const[o,p]of Object.entries(W))l[o]={staged:p,inserted:0,mapped:0,dropped:0};const _={db:i,schema:d,target:c,catalog:w,sourcePerson:t.person,targetPerson:e.person,fleetRuntime:T,shippedBodies:t.shippedBodies??{}};await a("bind-staged");for(const[o,p]of Object.entries(await ce(_)))v(l,o,p);await de(_),await a("rewrite-person"),await be(_),await a("prune");const B=await ee(i,d,w,t),k=t.kind==="hosted"?ve(t.pathPrefixes??[]):[];await a("rewrite-paths"),await ne(i,d,k),await ie(i,d),await ue(_),await ae(i,d,c,t.installId,e.person),await a("rewrite-ids");const P=await ge(_);await a("drop");const L=await se(i,d,c),H=await le(_);for(const[o,p]of Object.entries({...B}))v(l,o,p);for(const[o,p]of Object.entries(H))v(l,o,p);await pe(_),await a("pre-check");const K=new Map([["messages",["seq"]],...X.map(o=>[o,["seq"]])]);await te(i,d,c,w,b,new Set,K),await a("blobs"),await me(i,d,r,e.run_id,m),await a("copy"),await $(async o=>{await s.assertOwned();const p=await x({db:o,runId:e.run_id,schema:d,target:c,catalog:w,source:t,person:e.person,idMap:P,pathMap:k}),j=await Ne(o,e.run_id,L);j>0&&(p.inserted.revisions=(p.inserted.revisions??0)+j),await Re(o,d,e.run_id,t);const D=await Te({db:o,blobs:r,runId:e.run_id,person:e.person,objectMap:P.object});for(const[U,F]of Object.entries(D.inserted))p.inserted[U]=F.length;return O.push(...D.unresolvedPlugins),await o.prepare("UPDATE import_runs SET counts_json = ?, loaded_at = COALESCE(loaded_at, ?) WHERE run_id = ?").run(JSON.stringify({...Me(l,p),unresolved_plugins:O}),S(),e.run_id),p}),f=!0,await n.atStep?.("committed")}await a("verify");const h=await Se(i,e.run_id,d,c,w,t.installId);if(await i.prepare("UPDATE import_runs SET verify_json = ? WHERE run_id = ?").run(JSON.stringify(h),e.run_id),!h.ok)throw new g("verify","verify_failed","the committed rows differ from the staged rows or an invariant failed",{differences:h.differences,invariants:h.invariants});await a("manifest");const R=y.manifest(e.run_id);await r.put(R,await Ce(e.run_id)),await M(i,e.run_id);for(const l of Oe(e.run_id))await r.delete(l);await fe(r,e.run_id),await i.prepare("UPDATE import_runs SET state = 'done', step = 'done', manifest_key = ?, finished_at = ? WHERE run_id = ?").run(R,S(),e.run_id)}catch(t){if(t instanceof Pe)throw t;const c=Ae(t);if(await M(i,e.run_id).catch(()=>{}),!f)for(const b of m)await r.delete(b).catch(()=>{});await i.prepare("UPDATE import_runs SET state = 'failed', failure_json = ?, finished_at = ? WHERE run_id = ?").run(JSON.stringify(c),S(),e.run_id)}}u(tt,"processRun");function Ae(e){if(e instanceof g)return{step:e.step,code:e.code,message:e.message,...e.detail===void 0?{}:{detail:e.detail}};const s=e.code;return{step:"unknown",code:typeof s=="string"?s:"import_failed",message:e instanceof Error?e.message:String(e)}}u(Ae,"failureOf");function v(e,s,n){(e[s]??={staged:0,inserted:0,mapped:0,dropped:0}).dropped+=n}u(v,"addDropped");function Me(e,s){for(const[n,r]of Object.entries(s.inserted))(e[n]??={staged:0,inserted:0,mapped:0,dropped:0}).inserted=r;for(const[n,r]of Object.entries(s.mapped))(e[n]??={staged:0,inserted:0,mapped:0,dropped:0}).mapped=r;return e}u(Me,"withInserted");async function Ne(e,s,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(a=>a.rev_id)))).map(a=>a.rev_id)),i=n.filter(a=>!r.has(a.rev_id)),d=new Map;for(const a of n)d.set(a.document,[...d.get(a.document)??[],a]);for(const[a,f]of d){const m=await Q(e,a);m!==null&&await z(e,m,f.map(t=>({rev_id:t.rev_id,parents:t.parents,body_hash:t.body_hash,rationale:t.rationale,created_at:t.created_at})))}return await C(e,s,"revisions",i.map(a=>[a.rev_id])),await C(e,s,"revision_parents",i.flatMap(a=>a.parents.map(f=>[a.rev_id,f]))),i.length}u(Ne,"arrive");async function C(e,s,n,r){if(r.length===0)return;const i=await e.prepare("SELECT inserted_keys FROM import_manifest WHERE run_id = ? AND table_name = ?").get(s,n);i===void 0?await e.prepare("INSERT INTO import_manifest (run_id, table_name, inserted_keys) VALUES (?, ?, ?)").run(s,n,A(r)):await e.prepare("UPDATE import_manifest SET inserted_keys = ? WHERE run_id = ? AND table_name = ?").run(A([...I(i.inserted_keys),...r]),s,n)}u(C,"appendManifest");async function Ce(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 q(Buffer.from(n.map(r=>JSON.stringify({table:r.table_name,keys:I(r.inserted_keys)})).join(`
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
- `))}u(Ce,"manifestFile");export{je as GRANT_WAIT_MS,tt as processRun};
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 p=(i,r)=>U(i,"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 V,isReservedEnvName as F}from"../../env-name.js";import{encodeKeys as M}from"./copy.js";import{compareInstallOwners as W}from"../../scoped-state/migrate-app-installs.js";const x=["auth","credentials","env","settings","plugins"],h=p((i,r)=>`imports/${i}/stores/${r}.sealed`,"storeKey"),$=p((i,r)=>`import_store|${i}|${r}`,"aadOf");async function q(i,r,d,s){if(s===null||typeof s!="object"||Array.isArray(s))throw new K(`the ${d} store must be a JSON object`);const{sealed:a,keyId:m}=k(JSON.stringify(s),y(),$(r,d));await i.put(h(r,d),Buffer.concat([Buffer.from(`${m}
2
- `),a]))}p(q,"putStore");class K extends Error{static{p(this,"StoreBodyError")}}async function I(i,r,d){const s=await i.get(h(r,d));if(s===null)return null;const a=s.indexOf(10);return JSON.parse(C(s.subarray(a+1),s.subarray(0,a).toString("utf8"),y(),$(r,d)))}p(I,"readStore");const z=p(i=>x.map(r=>h(i,r)),"allStoreKeys");async function Q(i){const{db:r,blobs:d,runId:s,person:a}=i,m=p(e=>i.objectMap.get(e)??e,"mapId"),w=p(e=>e.startsWith("profile:")?`profile:${m(e.slice(8))}`:e,"mapOwner"),E={},T=[],N=p((e,n)=>{(E[e]??=[]).push(n)},"record"),f=p(async(e,n,o,l)=>{await r.prepareUncached(`${n} ON CONFLICT DO NOTHING RETURNING 1 AS ok`).get(...o)!==void 0&&N(e,l)},"insert"),b=y(),A=new Date().toISOString(),u=await I(d,s,"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 o of e.cooldowns)await D(r,a,e.provider,e.id,o.family,o.until,o.at),N("model_account_cooldowns",[a,e.provider,e.id,o.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 I(d,s,"auth");if(L!==null)for(const[e,n]of Object.entries(L.apiKeys)){const{sealed:o,keyId:l}=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,o,l,A],[a,e])}const R=await I(d,s,"env");if(R!==null)for(const[e,n]of Object.entries(R.profiles)){const o=m(e);for(const[l,t]of Object.entries(n)){if(!V(l).isValid||F(l)||typeof t!="string"||t.includes("\0"))continue;const{sealed:c,keyId:_}=k(t,b,`env_vars|${a}|profile|${o}|${l}`);await f("env_vars","INSERT INTO env_vars (person, owner_kind, owner_id, name, value_sealed, key_id, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)",[a,"profile",o,l,c,_,A],[a,"profile",o,l])}}const S=await I(d,s,"settings");if(S!==null){for(const[e,n]of Object.entries(S.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(S.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(S.profileProjects)){const o=m(e);if(await r.prepare("SELECT 1 FROM profile_projects WHERE profile_id = ?").get(o)!==void 0)continue;let l=0;for(const t of n)await f("profile_projects","INSERT INTO profile_projects (profile_id, path, position) VALUES (?, ?, ?)",[o,t,l++],[o,t])}for(const[e,n]of Object.entries(S.defaults))await f("profile_defaults","INSERT INTO profile_defaults (person, directory, profile_id) VALUES (?, ?, ?)",[a,e,m(n)],[a,e])}const O=await I(d,s,"plugins");if(O!==null){const e=p(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([...O.marketplaces,...O.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 o=p(t=>[...t].sort((c,_)=>W(w(c.owner),n.get(c.owner)??"",w(_.owner),n.get(_.owner)??"")),"byProfileFirst"),l=new Map;for(const t of o(O.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(v=>[v.name,v.url]));let g=t.name;for(let v=2;_.has(g)&&_.get(g)!==(t.url??null);v+=1)g=`${t.name}-${v}`;l.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 o(O.plugins)){if(t.source_path!=null&&(t.source_marketplace??null)===null){T.push(`${t.owner}:${t.name}`);continue}const c=await e(t.owner),_=t.source_marketplace==null?null:l.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(s,e,M(n));return Object.keys(E).length>0&&await r.prepare("SELECT pg_notify('crtr_signal', ?)").get(`settings:${a}`),{inserted:E,unresolvedPlugins:T}}p(Q,"writeStores");export{x as STORE_NAMES,K as StoreBodyError,z as allStoreKeys,q as putStore,h as storeKey,Q as writeStores};
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 O}from"node:os";import{dirname as T,join as m}from"node:path";import{withCanvasWrite as w,openDb as x}from"../canvas/db.js";import{emitEvent as d}from"../events/emit.js";import{outboundFetch as j}from"../events/outbound.js";import{directoryIssuer as g}from"../directory/issuer.js";import{allGrants as D,providerIdsFromGrants as M}from"../grants/mirror.js";import{mintDirectoryToken as P}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 P(o),r=await j("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 d({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(O(),"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(T(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
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 ne(e,n){const o=await v(n,e);return"pkg"in o?{out:o,published:await _(o.pkg)}:{out:o}}i(ne,"syncInstall");async function p(e){const n=await _(e);for(const o of n.invalid)d({level:"error",event:"daemon.packages.invalid_file",fields:{app:e.name,path:o.path,error:o.error}});for(const o of n.kinds.refused)d({level:"warn",event:"daemon.packages.kind_refused",fields:{app:e.name,kind:o.kind,message:o.message}})}i(p,"publishAndReport");const b=new Set;function E(e,n,o){if(o.ok)return;const t=`${e}
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"&&d({level:"warn",event:"daemon.packages.resolve_refused",fields:{name:e,source:n??"",reason:o.reason}}))}i(E,"refused");async function oe(){if(g()===null)return;const e=await x(),n=await D(),o=M(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 p(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 p(u.pkg):E(a,null,u)}continue}const c=await y(a,s.digest);"pkg"in c&&await p(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(oe,"syncPackages");export{y as getPackage,_ as publishDirectoryPackage,v as resolvePackage,ne as syncInstall,oe as syncPackages};
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};
@@ -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 w}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 f}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!==f())throw new o(409,"release_mismatch",`the source runs ${u}, the control plane accepts ${f()}`);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 w(),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 w()).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,...s}=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:s,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??[],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 w());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};
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};
@@ -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(jn,"serveLayoutRefusal");async function zn(e={}){if(e.role==="machine")return(await import("./machine-daemon.js")).runMachineDaemon();if(kr())return jn(e);br();const l=a(t=>{process.stderr.write(`[crtrd] ${t}
3
- `)},"logPostgres"),u=!!process.env.CRTR_TEST_PG_URL,f=Ft(e.role);Kt(Q(e.role));const v=a(async()=>f?await Ot(Ee(16).toString("hex")):{claimed:!0,claim:{pid:process.pid,pid_identity:"",token:Ee(16).toString("hex")}},"claimOwnership"),{database:s,ownership:w}=u?{database:{url:await Ht()},ownership:await v()}:await Wt(async()=>({database:await zt({log:l,startLockHeld:!0}),ownership:await v()}));if(!w.claimed){if(w.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${w.incumbentPid??"?"})
1
+ var 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,m,y="";try{f&&await hn(g.token);for(const r of Pr())process.stderr.write(`[crtrd] ${r}
6
- `);for(const r of await Rr())process.stderr.write(`[crtrd] ${r}
7
- `);await Fe(),await jt(s.url,l);const t=e.role===void 0&&!await pn();let i=null;t&&(mn({kind:"user",root:je()}),i=fn(je())),e.role===void 0&&un(i??{profiles:[],defaults:{},settings:{},state:{},apps:{}}),await H.listen(s.url);const h=a(r=>{He(r).catch(o=>d({level:"error",event:"daemon.settings.reload_failed",error:o}))},"reloadPerson");if(m=await H.subscribe(r=>{Be().includes(r)&&h(r)},{kinds:["settings"],onListenStart:a(()=>{for(const r of Be())h(r)},"onListenStart")}),e.role!==void 0&&Mr(),e.role!==void 0){let r;try{r=Xt()}catch(b){d({level:"error",event:"daemon.packages.snapshot_failed",error:b})}const o=await pe(async b=>(await Le(b),r===void 0?void 0:await Qt(b,r,"release"))),$=await pe(async b=>await Yt(b));$.alreadyDone||d({level:"info",event:"daemon.packages.install_migration_done",fields:{moved:$.moved,discarded:$.discarded,people:$.people}});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 Le(o),await Vt(o)));for(const o of r.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${o.message}
5
+ `),w.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(w.reason==="own_identity_indeterminate"||w.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const g=w.claim;let p=null,c=null,n=null,d=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(y=await Mt(),Gt(y),e.role===void 0&&(p=await Gr(),p!==null)){if(Fr(p.id),await dn(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:$}=await import("node:os");await o("user",b=>{b.paths??={},b.paths.user_files??=$()})}}c=await Or(),c.joinedFrom!==null&&await He(c.person),await or(),n=await Br()}if(process.platform==="linux"&&P(T)&&(_r(),await A({operation:"users.sync",users:await gr()})),f){await mt(g.token);const r=await nn();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 Oe(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 k=!0,X,j=Promise.resolve(),Z=!1,z,ee,re,D;const U=new Set;let te=!1,we=!1,E,C,ne,q=!1,W,M,G,O,F,oe,ie,ae;const Je=et();let I,S={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},L="initial startup activation",Ke=null;function Ve(){return S.phase==="ready"?null:{phase:S.phase,message:S.message,blockerPaths:S.blockerPaths}}a(Ve,"startupBlock");function R(t){U.add(t),t.finally(()=>{U.delete(t)}).catch(()=>{})}a(R,"registerDetached");const he={acceptsDetachedWork:a(()=>k,"acceptsDetachedWork"),registerDetached:R};Q(e.role)&&await Zt(he,Fe);async function Ye(){try{k=!1,Jt.close(),ee?.(),ee=void 0,re?.(),m?.(),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(D!==void 0&&(f&&await gn(D),await D.exitsSettled(),D.dispose()),ne!==void 0&&await ne.close(),C!==void 0&&!q&&(q=!0,await C.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||(We(process.pid),te=!0),te&&!we){if(f){const{released:i}=await Oe(g);if(!i)throw new Error("daemon ownership claim was not released")}we=!0,await me()}}finally{Lt(),process.off("SIGINT",Se),process.off("SIGTERM",ye),process.off("exit",_e)}}a(Ye,"cleanupAttempt");const J=1e3;let ve=!1;function Qe(){return ve?{graceMs:J}:(ve=!0,_.fresh(()=>{d({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:J}})}),setTimeout(()=>{Xe()},J),{graceMs:J})}a(Qe,"requestHandover");async function Xe(){try{await x()}catch(t){const i=t instanceof Error?t.stack??t.message:String(t);process.stderr.write(`[crtrd] handover cleanup failed:
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"&&P(T)&&(await A({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await $t();const t=await At({waitForJoinLock:!0});_.fresh(()=>{d({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(()=>{d({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(Xe,"performHandover");function x(){if(E!==void 0)return E;const t=Ye();return E=t,t.then(()=>{E===t&&(E=void 0)},()=>{E===t&&(E=void 0)}),t}a(x,"requestCleanup");async function Ze(){S={phase:"initializing",message:Hn,blockerPaths:[]},C!==void 0&&!q&&(q=!0,await C.close({graceMs:Fn}))}a(Ze,"drainApi");async function ge(t){try{t==="SIGTERM"&&e.role==="api"&&await Ze(),await x(),await qt({claimed:a(async()=>{try{return f&&await Bt()}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:
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){k&&(j=j.then(t).then(()=>{},i=>{_.fresh(()=>{d({level:"error",event:"daemon.lane_job.failed",error:i})})}))}a(se,"enqueue");async function K(){!k||Z||(Je.check()&&I&&(d({level:"info",event:"daemon.resume.detected"}),R(I().catch(()=>{}))),Z=!0,await se(async()=>{if(Z=!1,!!k)try{u||await Ut({log:l}),await On(Date.now(),he)}catch(t){_.fresh(()=>{d({level:"error",event:"daemon.tick.failed",error:t})})}}))}a(K,"enqueueTick");let B=null;async function er(){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(er,"admitStartup");async function rr(t){if(L!==null)throw{status:409,code:"migration_in_progress",message:`the canvas import is already running (${L})`};L="repair";try{const i=await ln({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{L=null}}a(rr,"repairMigrations");async function tr(){X=e.intervalMs??Zr;const t=g.token;D=new tn({epoch:t,enqueue:se}),Tt(D),S={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{d({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:X}})});const i=y;_.fresh(()=>{d({level:"info",event:"daemon.install_id.resolved",fields:{install_id:i}})});const h=await Ar(i);if(_.fresh(()=>{d({level:"info",event:"daemon.environment.registered",fields:{environment_id:h.environmentId,created:h.created}})}),_.fresh(()=>{let r;try{r=Xr()}catch{return}r!==null&&d({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:r.slice(0,2048)}})}),ht(),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&&Nr(),!Q(e.role)){S={phase:"ready",message:"",blockerPaths:[]};return}if(await _.fresh(async()=>{await Qr(),await Kr(),await Vr(Date.now()),await Yr(Date.now())}),await _.fresh(async()=>{const{reconcile:r,migrated:o}=await Nt(dt);r.changed&&d({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:r.cleared.length,same_boot_proven:r.sameBootProven}}),o.length>0&&d({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:o.length}})}),await _.fresh(async()=>{for(const r of await Te())r.status!=="active"||r.pi_pid!=null||r.frozen_at!=null||r.respawn_not_before!=null||(await De(r.node_id,new Date().toISOString()),d({level:"warn",event:"daemon.startup.launch_debt_recorded",node_id:r.node_id}))}),await _.fresh(async()=>{await Et();for(const r of on){const o=await r.apply();o.changed&&d({level:"info",event:"daemon.boot_migration.applied",fields:{migration:r.id,...o.summary}})}}),R(Wr()),I){const r=a(()=>{R(I().catch(()=>{})),R(ar().then(o=>F?.observe(o)).then(()=>{},o=>{d({level:"warn",event:"daemon.peer.status_failed",error:o})}))},"refresh");R(qr().then(({completion:o})=>o).catch(o=>{d({level:"error",event:"daemon.grants.sweep_failed",error:o})})),r(),G=setInterval(r,$e*1e3),G.unref(),ae=ir({joined:!0,register:R})}await se(async()=>{await rn(),await en()}),z=setInterval(()=>{K().catch(r=>d({level:"error",event:"daemon.tick.failed",error:r}))},X),ee=await Pt(()=>{K().catch(r=>d({level:"error",event:"daemon.tick.failed",error:r}))}),re=await H.subscribe(()=>{K().catch(r=>d({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(()=>!k,o=>d({level:"warn",event:"daemon.move.settle_failed",error:o}))).catch(r=>d({level:"warn",event:"daemon.move.settle_failed",error:r}))}a(tr,"completeStartup");function ce(){return Ke??=tr().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),an(),p!==null&&d({level:"info",event:"daemon.person.resolved",fields:{person_id:p.id,source:p.source}}),n!==null&&d({level:"info",event:"daemon.permissions.boot",fields:{...n}}),c!==null&&d({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 ur():wr(fr());const t=g.token;Ct({epoch:t,requestHandover:Qe,registerDetached:R,startupBlock:Ve,admitStartup:er,repairMigrations:rr}),Ue(),C=N(e.role)?xr({tcp:e.tcp}):Bn(e),await C.ready;const i=pr()??le("user").document_port;if(N(e.role)){const r=Cr(i,Lr());try{await r.ready,ne=r}catch(o){d({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=Ur(h.issuer,R),h.unsandboxed===!0&&(F=sr({syncGrants:I}))),e.role==="api"){const r=a(()=>R(zr().catch(o=>{d({level:"error",event:"daemon.packages.sync_failed",error:o})})),"packageTick");r(),O=setInterval(r,$e*1e3),O.unref()}if(N(e.role)&&(oe=await Hr()),ie=await jr({serves:N(e.role),runsLanes:Q(e.role)}),h?.unsandboxed===!0&&h.listener!==void 0&&d({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 $r(h,async()=>await Jr(h.issuer.replace(/\/$/,"")),I),await M.ready),e.role===void 0)try{await sn()}catch(r){if(L=null,r instanceof cn)return S={phase:"blocked",message:r.message,blockerPaths:r.blockerPaths},x;throw r}process.platform==="linux"&&P(T)&&(yr(),await Sr("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{L=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(zn,"runDaemon");var ji=zn;export{Fn as API_DRAIN_GRACE_MS,qi as brokerCommandDeclaresCanvasHome,ue as brokerCommandEpoch,ji as default,Q as roleRunsLanes,N as roleServesRequests,zn as runDaemon,On as superviseTick,gn as teardownFleet};
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};
@@ -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
@@ -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 W(){return process.env.CRTR_NODE_ID}e(W,"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 F(){return E()!==void 0}e(F,"isSandboxedNode");function h(){return/^https?:\/\//.test(process.env.CRTR_API_URL??"")&&(process.env.CRTR_API_SOCKET??"")!==""}e(h,"providerProjectionOverBrokerRelay");function H(){return process.env.CRTR_GRANTEE}e(H,"envGrantee");const v="CRTR_INBOX_BETWEEN_TURNS";function K(){return process.env[v]==="1"}e(K,"envInboxBetweenTurns");const C="CRTR_SCOPES_FILE";function k(){const n=process.env[C];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(k,"envScopes");function y(n,r){return n==null?!0:a(n,r)}e(y,"scopeAllowed");function a(n,r){return n.includes(r)?(_(r,r),!0):n.some(t=>_(t,r))}e(a,"coveredByAny");function G(){return process.env.CRTR_EXECUTION_ID}e(G,"envExecutionId");function b(){return process.env.CRTR_PROFILE_ID}e(b,"envProfileId");function w(){return process.env.CRTR_NODE_CWD}e(w,"envNodeCwd");function g(){return process.env.CRTR_MODEL_INTENT}e(g,"envModelIntent");function V(){return process.env.CRTR_MODEL_EXACT==="1"}e(V,"envModelExact");function X(){return process.env.CRTR_HOME}e(X,"envHomeOverride");function j(){return process.env.CRTR_RUNTIME_HOME}e(j,"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 A=5*6e4;function Y(){return s(process.env.CRTR_STREAM_WATCHDOG_MS,A)}e(Y,"envStreamWatchdogMs");const S=800;function $(){return s(process.env.CRTR_WATCHER_TICK_MS,S)}e($,"envWatcherTickMs");const M=6e4;function z(){return s(process.env.CRTR_WATCHER_BACKSTOP_MS,M)}e(z,"envWatcherBackstopMs");const N=1e3;function Z(){return s(process.env.CRTR_WATCHER_DEBOUNCE_MS,N)}e(Z,"envWatcherDebounceMs");const x=6e4;function J(){return s(process.env.CRTR_RECAP_IDLE_MS,x)}e(J,"envRecapIdleMs");function q(){const n=process.env.CRTR_RECAP_MODEL;return n!==void 0&&n.trim()!==""?n.trim():void 0}e(q,"envRecapModelOverride");const D=300*1e3;function Q(){return s(process.env.CRTR_BASH_VALVE_MS,D)}e(Q,"envBashValveMs");const O=60*1e3;function ee(){return s(process.env.CRTR_BASH_AUTO_BG_DEADLINE_MS,O)}e(ee,"envBashAutoBgDeadlineMs");function ne(){const n=process.env.CRTR_PIDFILE;return n!==void 0&&n!==""?n:void 0}e(ne,"envPidfileOverride");const I="@earendil-works/pi-coding-agent";function re(){return process.env.CRTR_BROKER_ENGINE??I}e(re,"envBrokerEngine");function te(){return process.env.CRTR_PI_BINARY}e(te,"envPiBinaryOverride");function oe(){return process.env.CRTR_NO_DAEMON_AUTOSTART==="1"}e(oe,"envNoDaemonAutostart");function se(){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(se,"envDocumentPort");function ue(){return process.env.CRTRD_TCP}e(ue,"envCrtrdTcp");function ie(){const n=process.env.CRTRD_TOKEN;return n!==void 0&&n!==""?n:void 0}e(ie,"envCrtrdToken");function ce(){return process.env.CRTR_NO_EXPORTS==="1"}e(ce,"envNoExports");function _e(){return process.env.CRTR_DEBUG==="1"}e(_e,"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 Re(){return R(process.env.CRTR_MAX_LIVE_BROKERS)}e(Re,"envMaxLiveBrokers");function pe(){return R(process.env.CRTR_WARN_LIVE_BROKERS)}e(pe,"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 Te(){return u(process.env.CRTR_TEST_NODE_SOCKET_WINDOW_MS)}e(Te,"envTestNodeSocketWindowMs");function fe(){return u(process.env.CRTR_TEST_UNATTENDED_PARK_MS)}e(fe,"envTestUnattendedParkMs");function de(){return u(process.env.CRTR_UNATTENDED_PARK_MS)}e(de,"envUnattendedParkMs");function Ee(){return u(process.env.CRTR_BASH_SILENCE_MS)??15*6e4}e(Ee,"envBashSilenceMs");function l(){return u(process.env.CRTR_TEST_PARK_SUMMARY_GRACE_MS)}e(l,"envTestParkSummaryGraceMs");const m=5*6e4;function ve(){return l()??m}e(ve,"parkSummaryGraceMs");export{v as INBOX_BETWEEN_TURNS_ENV,C as SCOPES_FILE_ENV,a as coveredByAny,ee as envBashAutoBgDeadlineMs,Ee as envBashSilenceMs,Q as envBashValveMs,re as envBrokerEngine,ue as envCrtrdTcp,ie as envCrtrdToken,_e as envDebug,se as envDocumentPort,G as envExecutionId,H as envGrantee,X as envHomeOverride,K as envInboxBetweenTurns,Re as envMaxLiveBrokers,V as envModelExact,g as envModelIntent,oe as envNoDaemonAutostart,ce as envNoExports,w as envNodeCwd,W as envNodeId,te as envPiBinaryOverride,ne as envPidfileOverride,b as envProfileId,J as envRecapIdleMs,q as envRecapModelOverride,j as envRuntimeHomeOverride,k as envScopes,Y as envStreamWatchdogMs,Te as envTestNodeSocketWindowMs,l as envTestParkSummaryGraceMs,fe as envTestUnattendedParkMs,de as envUnattendedParkMs,pe as envWarnLiveBrokers,z as envWatcherBackstopMs,Z as envWatcherDebounceMs,$ as envWatcherTickMs,F as isSandboxedNode,d as launchSnapshotFile,ve as parkSummaryGraceMs,h as providerProjectionOverBrokerRelay,E as sandboxCliNodeId,y as scopeAllowed};
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.592",
3
+ "version": "0.3.593",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -145,7 +145,7 @@
145
145
  },
146
146
  "license": "UNLICENSED",
147
147
  "dependencies": {
148
- "@north-light/identity": "0.3.592",
148
+ "@north-light/identity": "0.3.593",
149
149
  "@earendil-works/chord": "1.1.0",
150
150
  "@earendil-works/pi-agent-core": "1.1.0",
151
151
  "@earendil-works/pi-ai": "1.1.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/identity",
3
- "version": "0.3.592",
3
+ "version": "0.3.593",
4
4
  "description": "Northlight access-token verification and scope rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/runtime.lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.592",
3
+ "version": "0.3.593",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/cli",
9
- "version": "0.3.592",
9
+ "version": "0.3.593",
10
10
  "hasInstallScript": true,
11
11
  "license": "UNLICENSED",
12
12
  "workspaces": [
@@ -28,7 +28,7 @@
28
28
  "@milkdown/preset-gfm": "^7.22.3",
29
29
  "@milkdown/prose": "^7.22.3",
30
30
  "@milkdown/utils": "^7.22.3",
31
- "@north-light/identity": "0.3.592",
31
+ "@north-light/identity": "0.3.593",
32
32
  "cron-parser": "^5.6.0",
33
33
  "esbuild": "^0.27.7",
34
34
  "jose": "^6.2.12",
@@ -11956,17 +11956,17 @@
11956
11956
  },
11957
11957
  "packages/crouter-api": {
11958
11958
  "name": "@north-light/api",
11959
- "version": "0.3.592",
11959
+ "version": "0.3.593",
11960
11960
  "license": "UNLICENSED"
11961
11961
  },
11962
11962
  "packages/crouter-env-docker": {
11963
11963
  "name": "@north-light/env-docker",
11964
- "version": "0.3.592",
11964
+ "version": "0.3.593",
11965
11965
  "license": "UNLICENSED"
11966
11966
  },
11967
11967
  "packages/crouter-identity": {
11968
11968
  "name": "@north-light/identity",
11969
- "version": "0.3.592",
11969
+ "version": "0.3.593",
11970
11970
  "license": "UNLICENSED",
11971
11971
  "dependencies": {
11972
11972
  "jose": "^6.2.1"
@@ -11974,21 +11974,21 @@
11974
11974
  },
11975
11975
  "packages/crouter-plugin": {
11976
11976
  "name": "@north-light/plugin",
11977
- "version": "0.3.592",
11977
+ "version": "0.3.593",
11978
11978
  "license": "UNLICENSED",
11979
11979
  "dependencies": {
11980
- "@north-light/api": "0.3.592",
11981
- "@north-light/identity": "0.3.592"
11980
+ "@north-light/api": "0.3.593",
11981
+ "@north-light/identity": "0.3.593"
11982
11982
  }
11983
11983
  },
11984
11984
  "packages/crouter-sdk": {
11985
11985
  "name": "@north-light/sdk",
11986
- "version": "0.3.592",
11986
+ "version": "0.3.593",
11987
11987
  "license": "UNLICENSED",
11988
11988
  "dependencies": {
11989
- "@north-light/api": "0.3.592",
11990
- "@north-light/identity": "0.3.592",
11991
- "@north-light/plugin": "0.3.592",
11989
+ "@north-light/api": "0.3.593",
11990
+ "@north-light/identity": "0.3.593",
11991
+ "@north-light/plugin": "0.3.593",
11992
11992
  "jose": "^6.2.1"
11993
11993
  },
11994
11994
  "bin": {