@north-light/cli 0.3.592 → 0.3.594
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/imports.d.ts +6 -0
- package/dist/cli.js +7 -7
- package/dist/commands/pkg/market-inspect.js +1 -1
- package/dist/commands/pkg/market-manage.js +1 -1
- package/dist/commands/pkg/plugin-inspect.js +1 -1
- package/dist/commands/pkg/plugin-manage.js +6 -6
- package/dist/core/canvas/canvas.js +1 -1
- package/dist/core/grants/remove.d.ts +4 -0
- package/dist/core/grants/remove.js +1 -1
- package/dist/core/import/load/prepare.js +5 -5
- package/dist/core/import/load/publish-plugins.d.ts +10 -0
- package/dist/core/import/load/publish-plugins.js +1 -0
- package/dist/core/import/load/run.js +2 -2
- package/dist/core/import/load/stores.d.ts +8 -1
- package/dist/core/import/load/stores.js +2 -2
- package/dist/core/packages/directory-sync.d.ts +2 -0
- package/dist/core/packages/directory-sync.js +3 -3
- package/dist/core/peer/home.d.ts +1 -1
- package/dist/core/peer/home.js +2 -2
- package/dist/core/runtime/runner/blaxel/runner.js +3 -3
- package/dist/daemon/api/handlers/imports.js +1 -1
- package/dist/daemon/crtrd.js +12 -12
- package/dist/daemon/reconcilers/grant-effects.js +1 -1
- package/dist/shared/env.d.ts +4 -0
- package/dist/shared/env.js +2 -2
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
- package/scripts/sandbox-bootstrap.sh +10 -4
- package/scripts/sandbox-start.mjs +54 -17
package/dist/api/contract.d.ts
CHANGED
|
@@ -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.
|
|
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`. */
|
package/dist/api/contract.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.
|
|
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
|
|
3
|
-
`,"utf8"),I(
|
|
4
|
-
`);return}try{if(g(a(
|
|
5
|
-
`);return}let r;try{const{reconcileFirstRun:o}=await import("./commands/sys/setup-core.js");r=await o((
|
|
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(
|
|
9
|
-
`);return}await y(
|
|
10
|
-
`),process.exit(130)),(r instanceof w||r instanceof m)&&(G(r),process.exit(typeof process.exitCode=="number"?process.exitCode:1));const
|
|
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
|
|
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
|
|
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{
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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};
|
|
@@ -64,4 +64,4 @@ var oe=Object.defineProperty;var o=(e,n)=>oe(e,"name",{value:n,configurable:!0})
|
|
|
64
64
|
JOIN objects d ON d.id = w.object AND d.type = 'document' AND d.deleted_at IS NULL
|
|
65
65
|
JOIN objects o ON o.id = w.watcher AND o.deleted_at IS NULL
|
|
66
66
|
LEFT JOIN nodes n ON n.node_id = w.watcher
|
|
67
|
-
WHERE w.watcher != d.owner AND d.owner IS NOT NULL AND (n.node_id IS NULL OR n.status IN ('active', 'idle'))`).all();return new Set(e.map(n=>n.owner))}o(Xe,"ownersOfWatchedDocuments");async function te(e){const t=(await s()).prepare("SELECT node_id FROM nodes WHERE run_id = ? AND node_id != ?"),a=new Set(e.map(i=>i.node_id));return await m(e,async i=>(await t.all(i.node_id,i.node_id)).every(r=>a.has(r.node_id)))}o(te,"retainRunRootsWithMembers");async function ae(e,n){if(e.length===0)return;const t=await O(e,async i=>await I(i.node_id)),a=await U(e.map(i=>i.node_id));for(const i of e)await p(async r=>await j(r,i.node_id));if(await x(a),n!==void 0){for(const i of t)await F(i,async()=>{await Z(i),ne(i)});n(t.flatMap(ee));return}for(const i of t)await F(i,async()=>await W(i))}o(ae,"deletePrunedNodes");async function F(e,n){try{n()}catch(t){Ee({level:"error",event:"canvas.node_files.remove_failed",error:t,fields:{node_id:e.nodeId}})}}o(F,"eachNodeBestEffort");function ie(e){let n=0;for(const t of[e.created,e.launched_at,e.last_settled_at,e.finalized_at,e.outcome_at,e.last_activity_at]){if(t==null)continue;const a=Date.parse(t);Number.isFinite(a)&&a>n&&(n=a)}try{const t=ue(S(Ue(e.node_id),"telemetry.json"),{throwIfNoEntry:!1});t!==void 0&&t.mtimeMs>n&&(n=t.mtimeMs)}catch{}return n}o(ie,"lastActivityMs");async function ht(e){const n=e.dryRun??!1,t=await Ye(),a=await m(t,async u=>await X(u)&&!await e.held(u.node_id)),i=t.length-a.length,r=Math.max(0,e.maxNodes-i),d=a.map(u=>({row:u,recency:ie(u)})).sort((u,_)=>_.recency-u.recency).slice(r).map(({row:u})=>({node_id:u.node_id,status:u.status,created:u.created})),l=await te(d);return n||l.length===0?{pruned:l,dryRun:n}:(await ae(l),{pruned:l,dryRun:n})}o(ht,"pruneToLimit");export{K as PARKED_RESIDENT_SQL,Ke as RESUMABLE_NODE_SQL,Fn as abandonCreateRequest,gn as assertRunNotDeleting,at as browseNodes,bn as canonicalSessionFile,wt as childrenOf,ft as claimWarmSpare,Vn as clearFinalReportIfMatches,et as clearFrozen,Wn as completeCreateRequest,An as createNode,Rt as deleteNode,St as deleteNodes,Pn as dueNodeDeadlines,kn as findCreateRequest,Cn as findNodeBySessionFile,Qn as getFinalReport,h as getNode,vn as getNodes,Hn as getRow,Mn as getRowByPane,ot as graphNodes,X as isCollectible,pt as isWarmSpare,ie as lastActivityMs,Ye as listNodes,tt as listNodesByProfile,st as listNodesForSupervision,ut as listParkedResidentsWithWakeMail,mt as listResumableRoots,rt as listTerminalPlacementResidue,_t as listWarmSpares,I as nodeFiles,Ve as nodesForSupervisionSql,xn as pruneExpiredCreateRequests,Ot as pruneNodes,ht as pruneToLimit,jn as recordCreateRequestNode,Gn as recordLaunch,nt as recordRespawnAttempt,Xn as recordSettledTurn,lt as registerWarmSpare,W as removeNodeFiles,Un as reserveCreateRequest,In as reserveCreateRequestNode,dt as resumableNodeCwds,yt as rosterNodes,Zn as setFrozen,Jn as setIntent,$n as setNodeDeadline,Kn as setPresence,zn as setStatus,qn as shiftProfileNodeDeadlines,Qe as terminalPlacementResidueSql,Yn as transitionStatusIntent,ct as unregisterWarmSpare,Bn as updateNode,it as updateNodeTelemetry,Et as warmSpareCount,Nt as withFreshTerminalGuard};
|
|
67
|
+
WHERE w.watcher != d.owner AND d.owner IS NOT NULL AND (n.node_id IS NULL OR n.status IN ('active', 'idle'))`).all();return new Set(e.map(n=>n.owner))}o(Xe,"ownersOfWatchedDocuments");async function te(e){const t=(await s()).prepare("SELECT node_id FROM nodes WHERE run_id = ? AND node_id != ?"),a=new Set(e.map(i=>i.node_id));return await m(e,async i=>(await t.all(i.node_id,i.node_id)).every(r=>a.has(r.node_id)))}o(te,"retainRunRootsWithMembers");async function ae(e,n){if(e.length===0)return;const t=await O(e,async i=>await I(i.node_id)),a=await U(e.map(i=>i.node_id));for(const i of e)await p(async r=>await j(r,i.node_id));if(await x(a),n!==void 0){for(const i of t)await F(i,async()=>{await Z(i),ne(i)});n(t.flatMap(ee));return}for(const i of t)await F(i,async()=>await W(i))}o(ae,"deletePrunedNodes");async function F(e,n){try{await n()}catch(t){Ee({level:"error",event:"canvas.node_files.remove_failed",error:t,fields:{node_id:e.nodeId}})}}o(F,"eachNodeBestEffort");function ie(e){let n=0;for(const t of[e.created,e.launched_at,e.last_settled_at,e.finalized_at,e.outcome_at,e.last_activity_at]){if(t==null)continue;const a=Date.parse(t);Number.isFinite(a)&&a>n&&(n=a)}try{const t=ue(S(Ue(e.node_id),"telemetry.json"),{throwIfNoEntry:!1});t!==void 0&&t.mtimeMs>n&&(n=t.mtimeMs)}catch{}return n}o(ie,"lastActivityMs");async function ht(e){const n=e.dryRun??!1,t=await Ye(),a=await m(t,async u=>await X(u)&&!await e.held(u.node_id)),i=t.length-a.length,r=Math.max(0,e.maxNodes-i),d=a.map(u=>({row:u,recency:ie(u)})).sort((u,_)=>_.recency-u.recency).slice(r).map(({row:u})=>({node_id:u.node_id,status:u.status,created:u.created})),l=await te(d);return n||l.length===0?{pruned:l,dryRun:n}:(await ae(l),{pruned:l,dryRun:n})}o(ht,"pruneToLimit");export{K as PARKED_RESIDENT_SQL,Ke as RESUMABLE_NODE_SQL,Fn as abandonCreateRequest,gn as assertRunNotDeleting,at as browseNodes,bn as canonicalSessionFile,wt as childrenOf,ft as claimWarmSpare,Vn as clearFinalReportIfMatches,et as clearFrozen,Wn as completeCreateRequest,An as createNode,Rt as deleteNode,St as deleteNodes,Pn as dueNodeDeadlines,kn as findCreateRequest,Cn as findNodeBySessionFile,Qn as getFinalReport,h as getNode,vn as getNodes,Hn as getRow,Mn as getRowByPane,ot as graphNodes,X as isCollectible,pt as isWarmSpare,ie as lastActivityMs,Ye as listNodes,tt as listNodesByProfile,st as listNodesForSupervision,ut as listParkedResidentsWithWakeMail,mt as listResumableRoots,rt as listTerminalPlacementResidue,_t as listWarmSpares,I as nodeFiles,Ve as nodesForSupervisionSql,xn as pruneExpiredCreateRequests,Ot as pruneNodes,ht as pruneToLimit,jn as recordCreateRequestNode,Gn as recordLaunch,nt as recordRespawnAttempt,Xn as recordSettledTurn,lt as registerWarmSpare,W as removeNodeFiles,Un as reserveCreateRequest,In as reserveCreateRequestNode,dt as resumableNodeCwds,yt as rosterNodes,Zn as setFrozen,Jn as setIntent,$n as setNodeDeadline,Kn as setPresence,zn as setStatus,qn as shiftProfileNodeDeadlines,Qe as terminalPlacementResidueSql,Yn as transitionStatusIntent,ct as unregisterWarmSpare,Bn as updateNode,it as updateNodeTelemetry,Et as warmSpareCount,Nt as withFreshTerminalGuard};
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
/** Runtime spec §15 "On removal" (design flow 10). Audit and usage rows stay. */
|
|
2
2
|
export declare function removeApp(app: string, sub?: string): Promise<void>;
|
|
3
|
+
/** What a fleet does when a person's grant of `app` is removed and the app itself stays: that person's nodes, crons, questions,
|
|
4
|
+
* sandboxes, mail, upload uses and node files go. Never the app's shared parts (shares, uploads, space, env, OS user), even for
|
|
5
|
+
* the last person. This is the one removal that accepts `app:terminal`, whose whole-app removal `removeApp` refuses. */
|
|
6
|
+
export declare function removePersonData(app: string, sub: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var h=Object.defineProperty;var l=(r,o)=>h(r,"name",{value:o,configurable:!0});import{rmSync as O}from"node:fs";import{join as D}from"node:path";import{deleteNodes as T,nodeFiles as C,removeNodeFiles as F}from"../canvas/canvas.js";import{hasUnreconciledWorktree as W}from"../canvas/worktrees.js";import{withCanvasWrite as p,openDb as m}from"../canvas/db.js";import{cancelCron as L}from"../canvas/crons.js";import{emitEvent as w}from"../events/emit.js";import{appSpace as N,appsRoot as u,secretsDir as H}from"../layout.js";import{notifyRunDeleted as y}from"../runs/events.js";import{cancelOpenQuestions as M}from"../runs/questions.js";import{removeTreeBeneath as $}from"../spaces/open-beneath.js";import{markGranteeUploadsDeletePending as U,releaseRunUploads as A,turnSharesOffFor as q}from"../storage/tables.js";import{stopNodeSandboxes as I}from"../spaces/stop.js";import{activeAppUser as P,markAppUserRemoved as k}from"../spaces/users.js";import{launcherRequest as b}from"../../launcher/client.js";import{LaunchRefused as j}from"../../launcher/spec.js";import{localGrants as B}from"./mirror.js";import{launcherPresent as g,listSandboxes as x,stopSandboxPid as G}from"./sandboxes.js";import{filterAsync as R}from"../async-array.js";async function Ee(r,o){if(r==="app:terminal")throw new Error("app:terminal cannot be removed");const s=o===void 0||!(await B()).some(d=>d.grantee===r&&d.sub!==o&&d.status!=="removed");await _(r,o,s)}l(Ee,"removeApp");async function pe(r,o){await _(r,o,!1,!0)}l(pe,"removePersonData");async function _(r,o,s,d=!1){const f=N(r),t=s?await P(r):null;w({level:"info",event:"daemon.grants.removal_started",fields:{grantee:r,...o===void 0?{}:{sub:o},uid:t?.uid??null}});const i=o===void 0?{sql:"grantee = ?",args:[r]}:{sql:"grantee = ? AND sub = ?",args:[r,o]};for(const{node_id:e}of await(await m()).prepare(`SELECT node_id FROM node_rows WHERE ${i.sql} ORDER BY created DESC`).all(...i.args))await M({node_id:e},"the app was removed");const a=await p(async e=>(await e.prepare(`UPDATE nodes SET deleting_at = COALESCE(deleting_at, ?) WHERE node_id IN (SELECT node_id FROM node_rows WHERE ${i.sql})`).run(new Date().toISOString(),...i.args),(await e.prepare(`SELECT node_id FROM node_rows WHERE ${i.sql} ORDER BY created DESC`).all(...i.args)).map(n=>n.node_id)));for(const{cron_id:e}of await(await m()).prepare(`SELECT cron_id FROM crons WHERE ${i.sql}`).all(...i.args))await L(e);const c=(await Promise.all(a.map(e=>I(e,"app_removed",{deleting:!0})))).filter(e=>!e.confirmed);if(c.length)throw new Error(`sandbox teardown could not be confirmed for ${c.length} node(s) of ${r}`);if(t&&g())for(const e of await x())e.uid===t.uid&&await G(e.pid);const v=await R(a,async e=>(await(await m()).prepare("SELECT run_id FROM nodes WHERE node_id = ?").get(e))?.run_id===e);if(await p(async e=>{if(s&&await q(r,e),await A(a,e),s&&await U(r,e),!a.length)return;const n=a.map(()=>"?").join(","),S=await e.prepareUncached(`SELECT message_id FROM messages WHERE sender_node_id IN (${n}) UNION SELECT message_id FROM deliveries WHERE destination_id IN (${n})`).all(...a,...a);await e.prepareUncached(`DELETE FROM deliveries WHERE destination_id IN (${n})`).run(...a);for(const{message_id:E}of S)await e.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)").run(E,E)}),d){const e=await R(a,W);if(e.length)throw new Error(`refusing to delete nodes with unreconciled managed worktrees: ${e.join(", ")}`);for(const n of a)await F(await C(n))}await T(a);for(const e of v)y(e);if(s&&(O(D(H(),"env",f.root.slice(u().length+1)),{recursive:!0,force:!0}),$(u(),f.root)),t&&g())try{await b({operation:"user.delete",uid:t.uid})}catch(e){if(!(e instanceof j&&e.refusal==="invalid_uid"))throw e}t&&await k(t.uid),w({level:"info",event:"daemon.grants.removal_completed",fields:{grantee:r,...o===void 0?{}:{sub:o},uid:t?.uid??null,nodes:a.length}})}l(_,"cascade");export{Ee as removeApp,pe as removePersonData};
|
|
@@ -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
|
|
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(
|
|
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
|
|
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(
|
|
10
|
-
FROM ${$(o,i)} s JOIN ${$(c,i)} t ON ${
|
|
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};
|