@north-light/cli 0.3.591 → 0.3.593
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/clients/attach/viewer.js +429 -429
- 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/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/daemon/api/handlers/imports.js +1 -1
- package/dist/daemon/crtrd.js +12 -12
- package/dist/daemon/manage.d.ts +10 -3
- package/dist/daemon/manage.js +3 -3
- package/dist/daemon/reconcilers/node-lifecycle/tick.d.ts +5 -1
- package/dist/daemon/reconcilers/node-lifecycle/tick.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
|
@@ -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};
|
|
@@ -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};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var G=Object.defineProperty;var c=(e,i)=>G(e,"name",{value:i,configurable:!0});import{zstdCompressSync as z}from"node:zlib";import{blobStore as Q}from"../../blobs/index.js";import{openDb as E,withCanvasWrite as V}from"../../canvas/db.js";import{insertImportedRevisions as Y}from"../../graph/documents.js";import{getById as X}from"../../graph/objects.js";import{bundleKeys as S,decodeRows as Z,rowKeys as x,SEQ_TABLES as ee}from"../bundle/layout.js";import{readCatalog as te}from"./catalog.js";import{copyRows as ae,decodeKeys as N,encodeKeys as C}from"./copy.js";import{pruneStaged as ne,precheck as ie,renameClashingProfiles as se,rewritePaths as re,rewriteRequestPaths as oe,takeRepoSyncedDocuments as de}from"./prepare.js";import{boundSub as pe,checkSourcePerson as ue,checkStagedBlobKeys as ce,checkStagedPersons as fe,checkStagedReferences as le,checkStagedRequestKeys as me}from"./bind.js";import{deleteRunBlobs as _e,moveRunBlobs as we}from"./blobs.js";import{dropStubRows as ge,findFleetGrant as be,grantMissing as he,rewriteIds as ye,rewritePerson as Ee}from"./rewrite.js";import{dropStaging as W,ImportLaneError as w,stage as Se,stagingSchema as ve,targetSchema as Re}from"./stage.js";import{verifyRun as Oe}from"./verify.js";import{sandboxPathMap as Te,writeNodeFiles as ke}from"./path-map.js";import{allStoreKeys as Pe,readPluginsStore as je,writeStores as De}from"./stores.js";import{publishImportedPlugins as Ie}from"./publish-plugins.js";import{COPIED_TABLES as Ae}from"../bundle/layout.js";import{ClaimLostError as Me}from"../../../daemon/reconcilers/row-claim.js";const B=10*6e4,v=c(()=>new Date().toISOString(),"now"),R=c(async(e,i)=>{await(await E()).prepare("UPDATE import_runs SET step = ? WHERE run_id = ?").run(i,e)},"setStep");async function Ne(e,i){const n=await e.get(S.source(i));if(n===null)throw new w("stage","bundle_invalid","source.json is missing");return JSON.parse(n.toString("utf8"))}c(Ne,"readSource");async function Ce(e,i,n,r,s,d){const t=await e.get(S.rows(n.run_id,"grants")),f=t===null?[]:Z(t),l=[];for(const a of f){const p=pe(a.sub,r,n.person,"grants.sub");await be(i,s,d,p,a.grantee)===void 0&&l.push(a.grantee)}if(l.length===0)return!0;if(n.loaded_at===void 0||n.loaded_at===null)throw new w("stage","import_failed","a loading run has no loaded_at");if(Date.now()-Date.parse(n.loaded_at)>=B)throw he(l);return!1}c(Ce,"grantsReady");async function rt(e,i,n={}){const r=n.blobs??await Q(),s=await E(),d=ve(e.run_id),t=c(async a=>{await i.assertOwned(),await R(e.run_id,a),await n.atStep?.(a)},"at");let f=e.state==="committed";const l=[];try{const a=await Ne(r,e.run_id);if(e.source_kind==="machine"&&a.installId!==e.source_install_id)throw new w("stage","bundle_invalid",`source.json names install ${a.installId}, the run is for ${e.source_install_id}`);a.installId=e.source_install_id;const p=await Re(s),b=[...Ae],g=await te(s,p,[...b,"grants"]);if(!f){const m={},k=[],P=[],j=n.fleetRuntime??process.env.CRTR_RUNTIME??null;if(await ue(s,p,a.person,e.person),!await Ce(r,s,e,a.person,p,j)){await i.assertOwned(),await R(e.run_id,"waiting_for_grants");return}await t("publish-plugins");const y=await Ie(await je(r,e.run_id));if(y.unavailable.length>0){if(e.loaded_at===void 0||e.loaded_at===null)throw new w("publish-plugins","import_failed","a loading run has no loaded_at");if(Date.now()-Date.parse(e.loaded_at)>=B)throw new w("publish-plugins","directory_unavailable",`the directory did not answer for: ${y.unavailable.slice(0,10).join(", ")}; rerun the import once it is reachable`,{plugins:y.unavailable});await i.assertOwned(),await R(e.run_id,"waiting_for_directory");return}P.push(...y.refused),await t("stage");const H=await Se(s,e.run_id,a.counts,o=>x(e.run_id,o,a),o=>r.get(o));for(const[o,u]of Object.entries(H))m[o]={staged:u,inserted:0,mapped:0,dropped:0};const _={db:s,schema:d,target:p,catalog:g,sourcePerson:a.person,targetPerson:e.person,fleetRuntime:j,shippedBodies:a.shippedBodies??{}};await t("bind-staged");for(const[o,u]of Object.entries(await fe(_)))O(m,o,u);await ce(_),await t("rewrite-person"),await Ee(_),await t("prune");const K=await ne(s,d,g,a),D=a.kind==="hosted"?Te(a.pathPrefixes??[]):[];await t("rewrite-paths"),await re(s,d,D),await oe(s,d),await me(_),await se(s,d,p,a.installId,e.person),await t("rewrite-ids");const I=await ye(_);await t("drop");const U=await de(s,d,p),F=await ge(_);for(const[o,u]of Object.entries({...K}))O(m,o,u);for(const[o,u]of Object.entries(F))O(m,o,u);await le(_),await t("pre-check");const J=new Map([["messages",["seq"]],...ee.map(o=>[o,["seq"]])]);await ie(s,d,p,g,b,new Set,J),await t("blobs"),await we(s,d,r,e.run_id,l),await t("copy"),await V(async o=>{await i.assertOwned();const u=await ae({db:o,runId:e.run_id,schema:d,target:p,catalog:g,source:a,person:e.person,idMap:I,pathMap:D}),A=await Le(o,e.run_id,U);A>0&&(u.inserted.revisions=(u.inserted.revisions??0)+A),await ke(o,d,e.run_id,a);const M=await De({db:o,blobs:r,runId:e.run_id,person:e.person,objectMap:I.object});for(const[q,$]of Object.entries(M.inserted))u.inserted[q]=$.length;return k.push(...M.unresolvedPlugins),await o.prepare("UPDATE import_runs SET counts_json = ?, loaded_at = COALESCE(loaded_at, ?) WHERE run_id = ?").run(JSON.stringify({...Be(m,u),unresolved_plugins:k,refused_plugins:P}),v(),e.run_id),u}),f=!0,await n.atStep?.("committed")}await t("verify");const h=await Oe(s,e.run_id,d,p,g,a.installId);if(await s.prepare("UPDATE import_runs SET verify_json = ? WHERE run_id = ?").run(JSON.stringify(h),e.run_id),!h.ok)throw new w("verify","verify_failed","the committed rows differ from the staged rows or an invariant failed",{differences:h.differences,invariants:h.invariants});await t("manifest");const T=S.manifest(e.run_id);await r.put(T,await He(e.run_id)),await W(s,e.run_id);for(const m of Pe(e.run_id))await r.delete(m);await _e(r,e.run_id),await s.prepare("UPDATE import_runs SET state = 'done', step = 'done', manifest_key = ?, finished_at = ? WHERE run_id = ?").run(T,v(),e.run_id)}catch(a){if(a instanceof Me)throw a;const p=We(a);if(await W(s,e.run_id).catch(()=>{}),!f)for(const b of l)await r.delete(b).catch(()=>{});await s.prepare("UPDATE import_runs SET state = 'failed', failure_json = ?, finished_at = ? WHERE run_id = ?").run(JSON.stringify(p),v(),e.run_id)}}c(rt,"processRun");function We(e){if(e instanceof w)return{step:e.step,code:e.code,message:e.message,...e.detail===void 0?{}:{detail:e.detail}};const i=e.code;return{step:"unknown",code:typeof i=="string"?i:"import_failed",message:e instanceof Error?e.message:String(e)}}c(We,"failureOf");function O(e,i,n){(e[i]??={staged:0,inserted:0,mapped:0,dropped:0}).dropped+=n}c(O,"addDropped");function Be(e,i){for(const[n,r]of Object.entries(i.inserted))(e[n]??={staged:0,inserted:0,mapped:0,dropped:0}).inserted=r;for(const[n,r]of Object.entries(i.mapped))(e[n]??={staged:0,inserted:0,mapped:0,dropped:0}).mapped=r;return e}c(Be,"withInserted");async function Le(e,i,n){const r=new Set((await e.prepareUncached("SELECT rev_id FROM revisions WHERE rev_id IN (SELECT jsonb_array_elements_text(?::jsonb))").all(JSON.stringify(n.map(t=>t.rev_id)))).map(t=>t.rev_id)),s=n.filter(t=>!r.has(t.rev_id)),d=new Map;for(const t of n)d.set(t.document,[...d.get(t.document)??[],t]);for(const[t,f]of d){const l=await X(e,t);l!==null&&await Y(e,l,f.map(a=>({rev_id:a.rev_id,parents:a.parents,body_hash:a.body_hash,rationale:a.rationale,created_at:a.created_at})))}return await L(e,i,"revisions",s.map(t=>[t.rev_id])),await L(e,i,"revision_parents",s.flatMap(t=>t.parents.map(f=>[t.rev_id,f]))),s.length}c(Le,"arrive");async function L(e,i,n,r){if(r.length===0)return;const s=await e.prepare("SELECT inserted_keys FROM import_manifest WHERE run_id = ? AND table_name = ?").get(i,n);s===void 0?await e.prepare("INSERT INTO import_manifest (run_id, table_name, inserted_keys) VALUES (?, ?, ?)").run(i,n,C(r)):await e.prepare("UPDATE import_manifest SET inserted_keys = ? WHERE run_id = ? AND table_name = ?").run(C([...N(s.inserted_keys),...r]),i,n)}c(L,"appendManifest");async function He(e){const n=await(await E()).prepare("SELECT table_name, inserted_keys FROM import_manifest WHERE run_id = ? ORDER BY table_name").all(e);return z(Buffer.from(n.map(r=>JSON.stringify({table:r.table_name,keys:N(r.inserted_keys)})).join(`
|
|
2
2
|
`)+`
|
|
3
|
-
`))}
|
|
3
|
+
`))}c(He,"manifestFile");export{B as GRANT_WAIT_MS,rt as processRun};
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import type { BlobStore } from '../../blobs/index.js';
|
|
2
2
|
import type { CanvasDb } from '../../canvas/db.js';
|
|
3
|
-
import type { ImportStoreNameDTO } from '../../../api/dto/imports.js';
|
|
3
|
+
import type { ImportPluginsStoreDTO, ImportStoreNameDTO } from '../../../api/dto/imports.js';
|
|
4
4
|
export declare const STORE_NAMES: readonly ImportStoreNameDTO[];
|
|
5
5
|
export declare const storeKey: (run: string, name: ImportStoreNameDTO) => string;
|
|
6
6
|
/** Seal one uploaded body under the target key and store it. The plaintext lives only in this call. */
|
|
7
7
|
export declare function putStore(blobs: BlobStore, run: string, name: ImportStoreNameDTO, body: unknown): Promise<void>;
|
|
8
8
|
export declare class StoreBodyError extends Error {
|
|
9
9
|
}
|
|
10
|
+
/** The uploaded plugins store, or null when the run has none. */
|
|
11
|
+
export declare const readPluginsStore: (blobs: BlobStore, run: string) => Promise<ImportPluginsStoreDTO | null>;
|
|
12
|
+
/** Whether the fleet can reinstall a plugin row: it names a marketplace. A row with a path only, or with neither (a Mac git-URL or HTTP-endpoint install, a provider
|
|
13
|
+
* bundle row), has no source the fleet can fetch; it is reported, not installed. Provider bundles reach the fleet through grants. */
|
|
14
|
+
export declare const hasFleetSource: (p: {
|
|
15
|
+
source_marketplace?: string | null;
|
|
16
|
+
}) => boolean;
|
|
10
17
|
/** Every store's blob key under a run, for deletion once the run is done. */
|
|
11
18
|
export declare const allStoreKeys: (run: string) => string[];
|
|
12
19
|
export interface StoreInput {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var U=Object.defineProperty;var
|
|
2
|
-
`),a]))}
|
|
1
|
+
var U=Object.defineProperty;var l=(o,r)=>U(o,"name",{value:r,configurable:!0});import{loadSecretsKey as y,open as C,seal as k}from"../../secrets/index.js";import{insertAccount as j,upsertModelCooldown as D}from"../../subscription-state.js";import{validateEnvVarName as F,isReservedEnvName as V}from"../../env-name.js";import{encodeKeys as x}from"./copy.js";import{compareInstallOwners as M}from"../../scoped-state/migrate-app-installs.js";const W=["auth","credentials","env","settings","plugins"],h=l((o,r)=>`imports/${o}/stores/${r}.sealed`,"storeKey"),$=l((o,r)=>`import_store|${o}|${r}`,"aadOf");async function z(o,r,d,i){if(i===null||typeof i!="object"||Array.isArray(i))throw new K(`the ${d} store must be a JSON object`);const{sealed:a,keyId:m}=k(JSON.stringify(i),y(),$(r,d));await o.put(h(r,d),Buffer.concat([Buffer.from(`${m}
|
|
2
|
+
`),a]))}l(z,"putStore");class K extends Error{static{l(this,"StoreBodyError")}}async function S(o,r,d){const i=await o.get(h(r,d));if(i===null)return null;const a=i.indexOf(10);return JSON.parse(C(i.subarray(a+1),i.subarray(0,a).toString("utf8"),y(),$(r,d)))}l(S,"readStore");const Q=l((o,r)=>S(o,r,"plugins"),"readPluginsStore"),P=l(o=>o.source_marketplace!=null,"hasFleetSource"),Y=l(o=>W.map(r=>h(o,r)),"allStoreKeys");async function Z(o){const{db:r,blobs:d,runId:i,person:a}=o,m=l(e=>o.objectMap.get(e)??e,"mapId"),w=l(e=>e.startsWith("profile:")?`profile:${m(e.slice(8))}`:e,"mapOwner"),E={},T=[],N=l((e,n)=>{(E[e]??=[]).push(n)},"record"),f=l(async(e,n,s,p)=>{await r.prepareUncached(`${n} ON CONFLICT DO NOTHING RETURNING 1 AS ok`).get(...s)!==void 0&&N(e,p)},"insert"),b=y(),A=new Date().toISOString(),u=await S(d,i,"credentials");if(u!==null){for(const e of u.accounts)if(await r.prepare("SELECT 1 FROM model_accounts WHERE person = ? AND provider_id = ? AND (id = ? OR label_key = ?)").get(a,e.provider,e.id,e.labelKey)===void 0){await j(r,a,e.provider,{providerId:e.provider,id:e.id,label:e.label,labelKey:e.labelKey,position:e.position,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}},{refresh:e.refresh,access:e.access,expires:e.expires,...e.accountId?{accountId:e.accountId}:{},...e.clientId?{clientId:e.clientId}:{}}),N("model_accounts",[a,e.provider,e.id]);for(const s of e.cooldowns)await D(r,a,e.provider,e.id,s.family,s.until,s.at),N("model_account_cooldowns",[a,e.provider,e.id,s.family])}u.rotation!==void 0&&await f("model_settings","INSERT INTO model_settings (person, default_fallback_strength, revert_when_available) VALUES (?, ?, ?)",[a,u.rotation.defaultFallbackStrength,u.rotation.revertWhenAvailable===null?null:u.rotation.revertWhenAvailable?1:0],[a]),u.openaiDeviceId!==void 0&&/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(u.openaiDeviceId)&&await r.prepare("INSERT INTO model_settings (person, openai_device_id) VALUES (?, ?) ON CONFLICT (person) DO UPDATE SET openai_device_id = COALESCE(model_settings.openai_device_id, EXCLUDED.openai_device_id)").run(a,u.openaiDeviceId.toLowerCase())}const L=await S(d,i,"auth");if(L!==null)for(const[e,n]of Object.entries(L.apiKeys)){const{sealed:s,keyId:p}=k(JSON.stringify(n),b,`model_api_keys|${a}|${e}`);await f("model_api_keys","INSERT INTO model_api_keys (person, provider_id, credential_sealed, key_id, updated_at) VALUES (?, ?, ?, ?, ?)",[a,e,s,p,A],[a,e])}const R=await S(d,i,"env");if(R!==null)for(const[e,n]of Object.entries(R.profiles)){const s=m(e);for(const[p,t]of Object.entries(n)){if(!F(p).isValid||V(p)||typeof t!="string"||t.includes("\0"))continue;const{sealed:c,keyId:_}=k(t,b,`env_vars|${a}|profile|${s}|${p}`);await f("env_vars","INSERT INTO env_vars (person, owner_kind, owner_id, name, value_sealed, key_id, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)",[a,"profile",s,p,c,_,A],[a,"profile",s,p])}}const O=await S(d,i,"settings");if(O!==null){for(const[e,n]of Object.entries(O.settings))await f("scope_settings","INSERT INTO scope_settings (person, owner_key, raw_json) VALUES (?, ?, ?)",[a,w(e),JSON.stringify(n)],[a,w(e)]);for(const[e,n]of Object.entries(O.state))await f("scope_bookkeeping","INSERT INTO scope_bookkeeping (person, owner_key, last_self_check, active_canvas) VALUES (?, ?, ?, ?)",[a,w(e),n.last_self_check??null,n.activeCanvas??null],[a,w(e)]);for(const[e,n]of Object.entries(O.profileProjects)){const s=m(e);if(await r.prepare("SELECT 1 FROM profile_projects WHERE profile_id = ?").get(s)!==void 0)continue;let p=0;for(const t of n)await f("profile_projects","INSERT INTO profile_projects (profile_id, path, position) VALUES (?, ?, ?)",[s,t,p++],[s,t])}for(const[e,n]of Object.entries(O.defaults))await f("profile_defaults","INSERT INTO profile_defaults (person, directory, profile_id) VALUES (?, ?, ?)",[a,e,m(n)],[a,e])}const v=await S(d,i,"plugins");if(v!==null){const e=l(async t=>{if(t.startsWith("app:"))return t;if(t==="user")return"app:terminal";if(!t.startsWith("profile:"))throw new Error(`unsupported install owner in the bundle: ${t}`);return(await r.prepare("SELECT grantee FROM profiles WHERE id = ?").get(m(t.slice(8))))?.grantee??"app:terminal"},"appOf"),n=new Map;for(const t of new Set([...v.marketplaces,...v.plugins].map(c=>c.owner)))t.startsWith("profile:")&&n.set(t,(await r.prepare("SELECT last_used_at FROM profiles WHERE id = ?").get(m(t.slice(8))))?.last_used_at??"");const s=l(t=>[...t].sort((c,_)=>M(w(c.owner),n.get(c.owner)??"",w(_.owner),n.get(_.owner)??"")),"byProfileFirst"),p=new Map;for(const t of s(v.marketplaces)){const c=await e(t.owner),_=new Map((await r.prepare("SELECT name, url FROM scope_marketplaces WHERE person = ? AND owner_key = ?").all(a,c)).map(I=>[I.name,I.url]));let g=t.name;for(let I=2;_.has(g)&&_.get(g)!==(t.url??null);I+=1)g=`${t.name}-${I}`;p.set(`${t.owner}\0${t.name}`,g),await f("scope_marketplaces","INSERT INTO scope_marketplaces (person, owner_key, name, last_updated, url, ref, installed_at) VALUES (?, ?, ?, ?, ?, ?, ?)",[a,c,g,t.last_updated??null,t.url??null,t.ref??null,t.installed_at??null],[a,c,g])}for(const t of s(v.plugins)){if(!P(t)){T.push(`${t.owner}:${t.name}`);continue}const c=await e(t.owner),_=t.source_marketplace==null?null:p.get(`${t.owner}\0${t.source_marketplace}`)??t.source_marketplace;await f("scope_plugins","INSERT INTO scope_plugins (person, owner_key, name, last_updated, etag, checked_at, enabled, source_marketplace, version, source_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",[a,c,t.name,t.last_updated??null,t.etag??null,t.checked_at??null,t.enabled?1:0,_,t.version??null,t.source_path??null],[a,c,t.name])}(E.scope_plugins??[]).length+(E.scope_marketplaces??[]).length>0&&await f("person_settings","INSERT INTO person_settings (person, plugins_revision) VALUES (?, 1)",[a],[a])}for(const[e,n]of Object.entries(E))await r.prepare("INSERT INTO import_manifest (run_id, table_name, inserted_keys) VALUES (?, ?, ?) ON CONFLICT (run_id, table_name) DO UPDATE SET inserted_keys = EXCLUDED.inserted_keys").run(i,e,x(n));return Object.keys(E).length>0&&await r.prepare("SELECT pg_notify('crtr_signal', ?)").get(`settings:${a}`),{inserted:E,unresolvedPlugins:T}}l(Z,"writeStores");export{W as STORE_NAMES,K as StoreBodyError,Y as allStoreKeys,P as hasFleetSource,z as putStore,Q as readPluginsStore,h as storeKey,Z as writeStores};
|
|
@@ -37,5 +37,7 @@ export declare function syncInstall(name: string, url: string): Promise<{
|
|
|
37
37
|
out: FetchOutcome;
|
|
38
38
|
published?: PublishReport;
|
|
39
39
|
}>;
|
|
40
|
+
/** Callers with no one to answer (the timer, the import lane): files the publisher could not read and kinds it refused are events. */
|
|
41
|
+
export declare function reportPublished(name: string, r: PublishReport): void;
|
|
40
42
|
/** One package-sync pass. Called after each grant sync on the api role. */
|
|
41
43
|
export declare function syncPackages(): Promise<void>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var S=Object.defineProperty;var i=(e,n)=>S(e,"name",{value:n,configurable:!0});import{mkdtempSync as A,mkdirSync as I,rmSync as N,writeFileSync as $}from"node:fs";import{tmpdir as
|
|
1
|
+
var S=Object.defineProperty;var i=(e,n)=>S(e,"name",{value:n,configurable:!0});import{mkdtempSync as A,mkdirSync as I,rmSync as N,writeFileSync as $}from"node:fs";import{tmpdir as x}from"node:os";import{dirname as O,join as m}from"node:path";import{withCanvasWrite as w,openDb as T}from"../canvas/db.js";import{emitEvent as p}from"../events/emit.js";import{outboundFetch as P}from"../events/outbound.js";import{directoryIssuer as g}from"../directory/issuer.js";import{allGrants as j,providerIdsFromGrants as D}from"../grants/mirror.js";import{mintDirectoryToken as M}from"../grants/sync.js";import{publish as R,readPackageSnapshot as F,withdraw as W}from"../graph/package-publish.js";import{normalizeSourceUrl as k}from"./source-url.js";const L=25e3,C=15e3;async function h(e,n){const o=g();if(o===null)return{ok:!1,reason:"unavailable"};try{const t=await M(o),r=await P("package_sync",`${o}${e}`,{method:n.method,headers:{Authorization:`Bearer ${t.access_token}`,...n.body===void 0?{}:{"content-type":"application/json"},...n.etag===void 0?{}:{"If-None-Match":`"${n.etag}"`}},...n.body===void 0?{}:{body:JSON.stringify(n.body)},signal:AbortSignal.timeout(n.wait)});if(r.status===304)return{ok:!0,notModified:!0};if(r.status===404)return{ok:!1,reason:"not_found"};if(r.status===409){const l=await r.json().catch(()=>({}));return(l.error?.code??l.code)==="name_taken"?{ok:!1,reason:"name_taken",holder:l.error?.details?.holder_source??l.error?.holder_source??l.holder_source}:{ok:!1,reason:"not_public"}}return r.ok?{ok:!0,pkg:await r.json()}:{ok:!1,reason:"unavailable"}}catch(t){return p({level:"warn",event:"daemon.packages.fetch_failed",fields:{path:e},error:t}),{ok:!1,reason:"unavailable"}}}i(h,"call");const y=i((e,n)=>h(`/v1/packages/${encodeURIComponent(e)}`,{method:"GET",etag:n,wait:C}),"getPackage"),v=i((e,n)=>h("/v1/packages/resolve",{method:"POST",body:{source_url:e,name:n},wait:L}),"resolvePackage");async function _(e){const n=A(m(x(),"crtr-pkg-"));try{for(const t of e.files){const r=m(n,t.path);if(!r.startsWith(`${n}/`))throw new Error(`package file escapes its folder: ${t.path}`);I(O(r),{recursive:!0}),$(r,t.text)}const o={...F(n,{app:e.name,origin:e.origin,version:e.version,revision:e.revision,source:e.source===null?null:k(e.source),launchKinds:e.kinds,objectKinds:e.object_kinds}),digest:e.digest};return await w(async t=>await R(t,o,"package-sync"))}finally{N(n,{recursive:!0,force:!0})}}i(_,"publishDirectoryPackage");async function G(e){return await e.prepare(`SELECT p.name, m.url FROM scope_plugins p LEFT JOIN scope_marketplaces m
|
|
2
2
|
ON m.person = p.person AND m.owner_key = p.owner_key AND m.name = p.source_marketplace
|
|
3
3
|
WHERE p.owner_key LIKE 'app:%' AND p.enabled = 1`).all()}i(G,"wantedPlugins");async function J(e){return(await e.prepare(`SELECT i.external_id, p.origin, p.source, p.digest, p.withdrawn_at FROM package_publications p
|
|
4
|
-
JOIN identities i ON i.object = p.app AND i.scheme = 'app' WHERE p.origin <> 'builtin'`).all()).map(o=>({name:o.external_id.replace(/^app:/,""),origin:o.origin,source:o.source,digest:o.digest,withdrawn:o.withdrawn_at!==null}))}i(J,"publications");async function
|
|
4
|
+
JOIN identities i ON i.object = p.app AND i.scheme = 'app' WHERE p.origin <> 'builtin'`).all()).map(o=>({name:o.external_id.replace(/^app:/,""),origin:o.origin,source:o.source,digest:o.digest,withdrawn:o.withdrawn_at!==null}))}i(J,"publications");async function oe(e,n){const o=await v(n,e);return"pkg"in o?{out:o,published:await _(o.pkg)}:{out:o}}i(oe,"syncInstall");function K(e,n){for(const o of n.invalid)p({level:"error",event:"daemon.packages.invalid_file",fields:{app:e,path:o.path,error:o.error}});for(const o of n.kinds.refused)p({level:"warn",event:"daemon.packages.kind_refused",fields:{app:e,kind:o.kind,message:o.message}})}i(K,"reportPublished");async function d(e){K(e.name,await _(e))}i(d,"publishAndReport");const b=new Set;function E(e,n,o){if(o.ok)return;const t=`${e}
|
|
5
5
|
${n}
|
|
6
|
-
${o.reason}`;b.has(t)||(b.add(t),o.reason!=="unavailable"&&
|
|
6
|
+
${o.reason}`;b.has(t)||(b.add(t),o.reason!=="unavailable"&&p({level:"warn",event:"daemon.packages.resolve_refused",fields:{name:e,source:n??"",reason:o.reason}}))}i(E,"refused");async function ae(){if(g()===null)return;const e=await T(),n=await j(),o=D(n),t=await G(e),r=new Map((await J(e)).map(a=>[a.name,a])),l=new Set([...o,...t.map(a=>a.name)]);for(const a of t){const s=r.get(a.name),c=a.url===null?null:k(a.url);if(c!==null&&(s===void 0||s.withdrawn||s.source!==c)){const u=await v(c,a.name);"pkg"in u?await d(u.pkg):E(a.name,c,u)}}for(const a of l){const s=r.get(a);if(s===void 0||s.withdrawn){if(o.includes(a)){const u=await y(a);"pkg"in u?await d(u.pkg):E(a,null,u)}continue}const c=await y(a,s.digest);"pkg"in c&&await d(c.pkg)}const f=n.some(a=>a.status==="active");for(const[a,s]of r)s.withdrawn||l.has(a)||s.origin==="provider"&&!f||await w(async c=>{await W(c,a)})}i(ae,"syncPackages");export{y as getPackage,_ as publishDirectoryPackage,K as reportPublished,v as resolvePackage,oe as syncInstall,ae as syncPackages};
|