@north-light/cli 0.3.483 → 0.3.485
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/client.d.ts +6 -0
- package/dist/api/client.js +3 -3
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/custom-objects.d.ts +5 -1
- package/dist/api/dto/docs.d.ts +26 -0
- package/dist/api/dto/objects.d.ts +21 -1
- package/dist/api/dto/plugins.d.ts +22 -0
- package/dist/api/dto/plugins.js +0 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -1
- package/dist/api/routes.d.ts +2 -0
- package/dist/api/routes.js +1 -1
- package/dist/builtin-memory/crouter-plugin/deploying.md +107 -0
- package/dist/builtin-memory/crouter-sdk/release-notes.md +69 -0
- package/dist/clients/attach/viewer.js +579 -578
- package/dist/commands/pkg/plugin-manage.d.ts +4 -0
- package/dist/commands/pkg/plugin-manage.js +18 -16
- package/dist/core/canvas/migrations.js +27 -18
- package/dist/core/command-plugins/bundle.d.ts +4 -1
- package/dist/core/command-plugins/bundle.js +1 -1
- package/dist/core/command-plugins/caller-header.d.ts +10 -0
- package/dist/core/command-plugins/caller-header.js +1 -0
- package/dist/core/command-plugins/compose.js +1 -1
- package/dist/core/command-plugins/endpoint.d.ts +4 -0
- package/dist/core/command-plugins/endpoint.js +1 -1
- package/dist/core/command-plugins/transport/exec-invoke-async.d.ts +11 -0
- package/dist/core/command-plugins/transport/exec-invoke-async.js +1 -0
- package/dist/core/command-plugins/transport/exec-invoke.d.ts +14 -0
- package/dist/core/command-plugins/transport/exec-invoke.js +1 -1
- package/dist/core/command-plugins/transport/http-invoke.d.ts +9 -2
- package/dist/core/command-plugins/transport/http-invoke.js +3 -3
- package/dist/core/command-plugins/transport/http-plan.js +1 -1
- package/dist/core/command-plugins/transport/sign.d.ts +55 -0
- package/dist/core/command-plugins/transport/sign.js +3 -0
- package/dist/core/graph/access.d.ts +19 -0
- package/dist/core/graph/access.js +3 -3
- package/dist/core/graph/custom.d.ts +29 -9
- package/dist/core/graph/custom.js +9 -8
- package/dist/core/graph/events.js +8 -8
- package/dist/core/graph/package-kinds.d.ts +25 -0
- package/dist/core/graph/package-kinds.js +1 -0
- package/dist/core/graph/package-reconcile.d.ts +20 -0
- package/dist/core/graph/package-reconcile.js +1 -0
- package/dist/core/graph/reader.d.ts +5 -0
- package/dist/core/graph/reader.js +3 -3
- package/dist/core/graph/receivers.d.ts +2 -1
- package/dist/core/graph/types.d.ts +2 -0
- package/dist/core/graph/watches.d.ts +4 -0
- package/dist/core/graph/watches.js +9 -8
- package/dist/core/peer/directory.d.ts +2 -1
- package/dist/core/peer/directory.js +1 -1
- package/dist/core/peer/ingest.d.ts +6 -1
- package/dist/core/peer/ingest.js +1 -1
- package/dist/core/peer/names.d.ts +2 -0
- package/dist/core/peer/names.js +1 -1
- package/dist/core/peer/outbox-store.d.ts +2 -0
- package/dist/core/peer/receiver.js +1 -1
- package/dist/core/peer/stubs.d.ts +26 -1
- package/dist/core/peer/stubs.js +1 -1
- package/dist/core/plugin-app.d.ts +31 -0
- package/dist/core/plugin-app.js +1 -0
- package/dist/core/plugin-objects-manifest.d.ts +35 -0
- package/dist/core/plugin-objects-manifest.js +1 -0
- package/dist/core/providers/bundles.js +2 -2
- package/dist/core/providers/call.d.ts +3 -0
- package/dist/core/providers/call.js +1 -1
- package/dist/core/secrets.d.ts +11 -0
- package/dist/core/secrets.js +1 -1
- package/dist/daemon/api/app-listener.js +13 -13
- package/dist/daemon/api/handlers/custom-objects.js +3 -3
- package/dist/daemon/api/handlers/objects.js +3 -2
- package/dist/daemon/api/handlers/package-docs.js +1 -1
- package/dist/daemon/api/handlers/plugin-app-token.d.ts +6 -0
- package/dist/daemon/api/handlers/plugin-app-token.js +1 -0
- package/dist/daemon/api/handlers/providers.d.ts +7 -0
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/server.js +1 -1
- package/dist/daemon/crtrd.js +7 -5
- package/dist/daemon/peer/forward.d.ts +4 -1
- package/dist/daemon/peer/forward.js +1 -1
- package/dist/daemon/plugin-objects/read-leaf.d.ts +20 -0
- package/dist/daemon/plugin-objects/read-leaf.js +2 -0
- package/dist/types.d.ts +16 -0
- package/docs/ci-runner.md +4 -1
- package/docs/plugin/deploying.md +107 -0
- package/docs/sdk/release-notes.md +69 -0
- package/package.json +2 -2
- package/packages/crouter-identity/dist/index.d.ts +1 -1
- package/packages/crouter-identity/dist/index.js +6 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
|
@@ -31,6 +31,10 @@ interface BundleReplaceResult extends Record<string, unknown> {
|
|
|
31
31
|
kinds: number;
|
|
32
32
|
/** Count of page-component registrations the bundle declared (0 when none). */
|
|
33
33
|
pageComponents: number;
|
|
34
|
+
/** Count of canvas object kinds the bundle declared (0 when none). */
|
|
35
|
+
objectKinds: number;
|
|
36
|
+
/** Count of canvas scopes the bundle declared (0 when none). */
|
|
37
|
+
scopes: number;
|
|
34
38
|
commands: CommandReport;
|
|
35
39
|
hooks?: HookReport;
|
|
36
40
|
memory?: PluginMemoryReport;
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
var
|
|
2
|
-
${
|
|
3
|
-
`)}`,{issues:
|
|
4
|
-
${
|
|
5
|
-
`)}`,{issues:
|
|
6
|
-
${
|
|
7
|
-
`)}`,{issues:
|
|
8
|
-
${
|
|
9
|
-
`)}`,{issues:
|
|
10
|
-
${
|
|
11
|
-
`)}`,{issues:
|
|
1
|
+
var Ce=Object.defineProperty;var d=(e,n)=>Ce(e,"name",{value:n,configurable:!0});import{envProfileId as Ae}from"../../shared/env.js";import{invalidPluginEnvReasons as Me}from"../../core/plugin-env.js";import{collectPluginEnv as ee}from"./plugin-env.js";import{dirname as ne,join as v,resolve as F,isAbsolute as _e,relative as L}from"node:path";import{renameSync as j}from"node:fs";import{defineLeaf as B}from"../../core/command.js";import{cliClient as Le,rethrowAsCliError as Be}from"../api-client.js";import{notFound as g,usage as b,general as f,network as He,CrtrError as De}from"../../core/errors.js";import{findMarketplaceByName as Ie,findPluginByName as C,listAllPlugins as te}from"../../core/resolver.js";import{pluginsDir as A,ensureProjectScopeRoot as z,userScopeRoot as S,resolveScopeArg as ie,projectScopeRoot as oe,requireScopeRoot as M}from"../../core/scope.js";import{readState as Ne,updateConfig as H,updateState as V,ensureScopeInitialized as G,invalidPluginKindsReasons as Ue,invalidPluginBinReasons as Fe,invalidPluginRequiresReasons as Ve,normalizeRequires as Oe}from"../../core/config.js";import{withExclusiveDirectoryLockAsync as Ke}from"../../core/exclusive-lock.js";import{bundleSwapLockPath as ae}from"../../core/plugin-swap-lock.js";import{invalidPageComponentsReasons as We}from"../../core/human/page-catalog.js";import{pathExists as $,ensureDir as _,removePath as E,nowIso as J,linkOrCopy as re,isSymlink as O,readSymlinkTarget as ze,readText as Ge,realpathOrSelf as Je,walkFiles as Ye}from"../../core/fs-utils.js";import{clone as K,deriveNameFromUrl as Qe,currentSha as T,gitSync as se,isGitRepo as Y}from"../../core/git.js";import{readMarketplaceManifest as le,readPluginManifest as x}from"../../core/manifest.js";import{pluginExtensionCatalog as Xe,validateMemoryExtensionValues as Ze,validatePluginMemoryExtensions as en}from"../../core/plugin-extensions.js";import{lintPackageFrontmatter as nn,packageFiles as tn}from"../../core/graph/package-files.js";import{parseFrontmatterGeneric as on}from"../../core/frontmatter.js";import{validatePluginCommands as Q,validateEffectiveCommandPlugins as an}from"../../core/command-plugins/discovery.js";import{normalizeEndpoint as rn,validateEndpoint as sn,validateHttpPluginTransport as ln}from"../../core/command-plugins/endpoint.js";import{buildBrokerEnv as cn,resolvePathExecutable as dn}from"../../core/runtime/spawn-env.js";import{bundleVersion as un,installBundleArchive as pn,isInstalledGeneration as fn,recordCheckedGeneration as mn,validatePluginBundle as hn}from"../../core/command-plugins/bundle.js";import{compileHookRegistry as ce,discoverCandidateHookRegistry as gn,discoverCandidateHookRegistryForCandidates as vn,discoverHookRegistry as yn}from"../../core/command-hooks/discovery.js";import{hasDeclaredHooks as de,hookReport as ue}from"../../core/command-hooks/report.js";import{fetchHttpPluginBundle as pe}from"../../core/command-plugins/transport/http-fetch.js";import{leafPathsBelowRoot as kn,objectBlocksReasons as wn,streamingLeafPathsBelowRoot as bn}from"../../core/plugin-objects-manifest.js";import{SUBTREE_NAMES as D,coreCommandPaths as I,coreHookCatalog as fe,extensibleCoreBranches as N}from"../../build-root.js";import{ExitCode as $n}from"../../types.js";import{isGitUrl as me,resolvePluginConfigTarget as jn,setPluginEnabled as he,resolveInstallScope as Pn}from"./shared.js";async function X(e,n){const t=C(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([I(),N()]),a=an(i,void 0,void 0,o,r).find(l=>l.plugin.root===t.root)??Q(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(X,"commandReport");const Rn=/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;function ge(e,n,t){if(typeof e!="string"||!Rn.test(e))throw b("invalid --name: expected a lowercase kebab-case plugin handle");const i=sn(n);if(!i.isValid)throw b(`invalid --endpoint: ${i.errors.join("; ")}`);const o=rn(n),r=ln({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(ge,"validateHttpInstall");function W(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
|
+
${t.join(`
|
|
3
|
+
`)}`,{issues:n,...n.length>0?{next:n[0].next}:{}})}d(W,"invalidBundleError");function xn(e,n){if(e.kinds===void 0)return;const t=Ue(e.kinds);if(t.length>0)throw f(`plugin manifest at ${n} declares an invalid kinds block:
|
|
4
|
+
${t.map(i=>`- ${i}`).join(`
|
|
5
|
+
`)}`,{issues:t})}d(xn,"assertManifestKindsValid");function Sn(e,n){if(e.bin===void 0)return;const t=Fe(e.bin);if(t.length>0)throw f(`plugin manifest at ${n} declares an invalid bin block:
|
|
6
|
+
${t.map(i=>`- ${i}`).join(`
|
|
7
|
+
`)}`,{issues:t})}d(Sn,"assertManifestBinValid");function En(e,n){if(e.requires===void 0)return;const t=Ve(e.requires);if(t.length>0)throw f(`plugin manifest at ${n} declares an invalid requires block:
|
|
8
|
+
${t.map(i=>`- ${i}`).join(`
|
|
9
|
+
`)}`,{issues:t})}d(En,"assertManifestRequiresValid");function ve(e){if(e.requires===void 0)return[];const n=cn({argv:[],env:{CRTR_NODE_CWD:process.cwd(),CRTR_PROFILE_ID:Ae()??""}});return Object.entries(Oe(e.requires)).filter(([t])=>dn(t,n,process.cwd())===null).map(([t,i])=>`Warning: plugin "${e.name}" requires "${t}" on PATH \u2014 ${i}`)}d(ve,"missingRequirementWarnings");function Tn(e,n){if(e.page_components===void 0)return;const t=We(e.page_components);if(t.length>0)throw f(`plugin manifest at ${n} declares an invalid page_components block:
|
|
10
|
+
${t.map(i=>`- ${i}`).join(`
|
|
11
|
+
`)}`,{issues:t})}d(Tn,"assertManifestPageComponentsValid");async function Z(e,n){const t=C(e,n);if(t===null||!de(t.manifest))return;const i=await fe(),o=yn(i),r=o.validations.find(a=>a.plugin.root===t.root);if(r===void 0){const a=ce(i,[t]);return ue(a.validations[0])}return ue(r,o.issues.filter(a=>a.plugin===t.name&&a.scope===t.scope))}d(Z,"hookLifecycleReport");async function qn(e,n,t,i,o,r){if(!de(e))return;const a={name:e.name,scope:t,root:n,manifest:e,enabled:!0},l=r??[{plugin:a,scopeRoot:i}],c=await fe(),s=r===void 0?gn(c,a,i):vn(c,l),y=s.validations.find(p=>p.plugin.root===n)===void 0?ce(c,[a]).issues:s.issues.filter(p=>p.plugin===a.name&&p.scope===a.scope);if(y.length>0)throw W(`plugin hook behavior at ${o} is invalid`,y)}d(qn,"assertManifestHooksValid");function Cn(e,n){const t=L(e,n);return t===""?e:t}d(Cn,"storeRelative");function An(e,n,t,i,o){const r=v(n,"memory");if(!$(r))return;const{files:a,invalid:l}=tn(r);if(l.length>0){const c=l.map(s=>`- ${Cn(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:
|
|
14
|
-
${
|
|
15
|
-
`)}`,{issues:
|
|
16
|
-
|
|
17
|
-
Accepted forms: marketplace/plugin, git URL (https://, ssh://, git@, file://, or *.git), or local path starting ./, ../, or /. Bare plugin names are not install refs.`)}u(_n,"classifyInstallRef");function je(e){return e.startsWith("./")||e.startsWith("../")||!Me(e)&&!me(e)}u(je,"isMarketplaceRelativeSource");async function xe(e,t,n){const i=t.get(e);if(i!==void 0)return i;const o=De(e);if(o===null){if(n.strict)throw h(`marketplace not found: ${e}`);return null}const s=se(o.root);if(s===null)throw h(`marketplace manifest not found: ${o.root}/.crouter-marketplace/marketplace.json`);if(!K(o.root)){const l={marketplace:o,manifest:s,pull:{updated:!1,sha:S(o.root)??""}};return t.set(e,l),l}const a=S(o.root),r=g(ee(o.root),`.${e}.${process.pid}.${Date.now()}.candidate`),c=`${r}.previous`;try{V(o.url,r,{ref:o.ref,depth:1});const l=se(r);if(l===null)throw h(`marketplace manifest not found in candidate checkout: ${r}/.crouter-marketplace/marketplace.json`);const p=ne().filter(d=>d.sourceMarketplace===e&&F(d.root));for(const d of p){const f=l.plugins.find(Te=>Te.name===d.name);if(f===void 0||!je(f.source))throw m(`marketplace candidate removes relative-source plugin "${d.name}" while it is installed`);const y=ze(d.root);if(y===null)throw m(`installed marketplace plugin "${d.name}" has no readable symlink target`);const k=L(o.root,N(ee(d.root),y)),$=N(r,f.source);if($!==N(r,k))throw m(`marketplace candidate moves installed relative-source plugin "${d.name}" from ${k} to ${f.source}`);if(!b($))throw h(`marketplace candidate source path does not exist for plugin "${d.name}": ${$}`);const X=x($);if(X===null)throw h(`plugin manifest not found in marketplace candidate: ${$}/.crouter-plugin/plugin.json`);const Se=d.scope==="project"?C("project"):R();await j(X,$,d.scope,Se,`${$}/.crouter-plugin/plugin.json`)}P(o.root,c);try{P(r,o.root)}catch(d){throw P(c,o.root),d}E(c);for(const d of p){const f=l.plugins.find(y=>y.name===d.name);Re(d.scope,d.name,f.version??d.manifest.version)}U(o.scope,d=>{d.marketplaces[e]===void 0&&(d.marketplaces[e]={}),d.marketplaces[e].last_updated=G()});const v={marketplace:o,manifest:l,pull:{updated:a!==S(o.root),sha:S(o.root)??""}};return t.set(e,v),v}finally{E(r),b(c)&&!b(o.root)&&P(c,o.root)}}u(xe,"refreshMarketplace");function Re(e,t,n){B(e,i=>{const o=i.plugins[t];o!==void 0?n!==void 0&&(o.version=n):i.plugins[t]={enabled:!0,version:n}}),U(e,i=>{i.plugins[t]===void 0&&(i.plugins[t]={}),i.plugins[t].last_updated=G()})}u(Re,"updatePluginVersion");async function Dn(e,t,n){const i=n==="project"?O():R();z(n,i);const o=A(n),s=Ye(e),a=g(i,"tmp",`${s}.${process.pid}.${Date.now()}`);M(g(i,"tmp"));try{V(e,a,{ref:t,depth:1});const r=x(a);if(r===null)throw m(`cloned repo does not contain a valid .crouter-plugin/plugin.json: ${e}`);const c=await j(r,a,n,i,`${e}/.crouter-plugin/plugin.json`),l=g(o,r.name);if(b(l))throw m(`plugin "${r.name}" is already installed at ${l}`);return M(o),P(a,l),B(n,p=>{p.plugins[r.name]={enabled:!0,version:r.version}}),{name:r.name,scope:n,path:l,...c!==void 0?{memory:c}:{}}}finally{E(a)}}u(Dn,"installFromGit");async function In(e,t){const n=N(e),i=x(n);if(i===null)throw h(`plugin manifest not found at ${n}/.crouter-plugin/plugin.json`);const o=t==="project"?O():R(),s=await j(i,n,t,o,`${n}/.crouter-plugin/plugin.json`,void 0,"linked");z(t,o);const a=g(A(t),i.name);if(b(a))throw m(`plugin "${i.name}" is already installed at ${a}`);return ae(n,a),B(t,r=>{r.plugins[i.name]={enabled:!0,version:i.version}}),{name:i.name,scope:t,path:a,...s!==void 0?{memory:s}:{}}}u(In,"installFromLocal");async function Nn(e,t){const n=await xe(e.marketplace,new Map,{strict:!0});if(n===null)throw h(`marketplace not found: ${e.marketplace}`);const i=n.manifest.plugins.find(l=>l.name===e.plugin);if(i===void 0)throw h(`plugin "${e.plugin}" not found in marketplace "${e.marketplace}"`);const o=C(t);z(t,o);const s=g(A(t),e.plugin);if(b(s))throw m(`plugin "${e.plugin}" is already installed at ${s}`);let a,r;if(je(i.source)){const l=g(n.marketplace.root,i.source);if(!b(l))throw h(`plugin source path does not exist: ${l}`);const p=x(l);if(p===null)throw h(`plugin manifest not found at ${l}/.crouter-plugin/plugin.json`);r=await j(p,l,t,o,`${l}/.crouter-plugin/plugin.json`,void 0,"linked"),ae(l,s),a=p}else{const l=g(o,"tmp",`${e.plugin}.${process.pid}.${Date.now()}`);M(g(o,"tmp"));try{V(i.source,l,{depth:1});const p=x(l);if(p===null)throw h(`plugin manifest not found at ${l}/.crouter-plugin/plugin.json`);r=await j(p,l,t,o,`${l}/.crouter-plugin/plugin.json`),M(A(t)),P(l,s),a=p}finally{E(l)}}const c=i.version??a.version;return B(t,l=>{const p={enabled:!0,source_marketplace:e.marketplace};c!==void 0&&(p.version=c),l.plugins[e.plugin]=p}),{name:e.plugin,scope:t,path:s,...r!==void 0?{memory:r}:{}}}u(Nn,"installFromMarketplace");async function Un(e){if(F(e.root)){const r=x(e.root);if(r===null)throw h(`plugin manifest not found at ${e.root}/.crouter-plugin/plugin.json`);const c=e.scope==="project"?C("project"):R(),l=await j(r,e.root,e.scope,c,`${e.root}/.crouter-plugin/plugin.json`,void 0,"linked");return{updated:!1,sha:S(e.root)??"",manifest:r,...l!==void 0?{memory:l}:{}}}if(!K(e.root)){const r=x(e.root);if(r===null)throw h(`plugin manifest not found at ${e.root}/.crouter-plugin/plugin.json`);const c=e.scope==="project"?C("project"):R(),l=await j(r,e.root,e.scope,c,`${e.root}/.crouter-plugin/plugin.json`);return{updated:!1,sha:"",manifest:r,...l!==void 0?{memory:l}:{}}}const t=re(["config","--get","remote.origin.url"],e.root).stdout.trim();if(t==="")throw m(`plugin "${e.name}" has no origin remote to stage an update from`);const n=re(["symbolic-ref","--short","HEAD"],e.root).stdout.trim();if(n==="")throw m(`plugin "${e.name}" is detached at its installed revision and has no tracked branch to update`);const i=e.scope==="project"?C("project"):R(),o=g(i,"tmp",`${e.name}.${process.pid}.${Date.now()}.candidate`),s=`${o}.previous`,a=S(e.root);M(g(i,"tmp"));try{V(t,o,{ref:n,depth:1});const r=x(o);if(r===null)throw h(`plugin manifest not found in update candidate: ${o}/.crouter-plugin/plugin.json`);const c=await j(r,o,e.scope,i,`${o}/.crouter-plugin/plugin.json`);P(e.root,s);try{P(o,e.root)}catch(p){throw P(s,e.root),p}E(s);const l=S(e.root)??"";return{updated:a!==l,sha:l,manifest:r,...c!==void 0?{memory:c}:{}}}finally{E(o),b(s)&&!b(e.root)&&P(s,e.root)}}u(Un,"updateGitPluginCandidate");async function Ee(e,t,n){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=ve(e);if(!i&&e.manifest.bundle!==void 0){const p=await $e(e.name,e.manifest.bundle,e.scope,{enable:e.enabled});return{name:e.name,transport:"http",updated:!0,version:p.version,docs:p.docs,kinds:p.kinds,pageComponents:p.pageComponents,commands:p.commands,...p.hooks!==void 0?{hooks:p.hooks}:{},...p.memory!==void 0?{memory:p.memory}:{}}}e.manifest.transport?.kind==="http"&&!i&&ye(e.name);let o,s=!1;if(e.sourceMarketplace!==void 0){const p=await xe(e.sourceMarketplace,t,n);p!==null&&(o=p.manifest.plugins.find(d=>d.name===e.name)?.version,s=p.pull.updated)}const a=await Un(e);Re(e.scope,e.name,o??a.manifest.version);const r=await Y(e.name,e.scope),c=await Q(e.name,e.scope),l=[...ge(a.manifest),...await Z(a.manifest)];return{name:e.name,...e.manifest.transport!==void 0?{transport:e.manifest.transport.kind==="machine"?"http":e.manifest.transport.kind}:{},updated:s||a.updated,sha:a.sha,...r!==void 0?{commands:r}:{},...c!==void 0?{hooks:c}:{},...a.memory!==void 0?{memory:a.memory}:{},...l.length>0?{warnings:l}:{}}}u(Ee,"updateOnePlugin");async function T(e){const n=(await Le().documents.reconcilePackages().catch(i=>He(i))).conflicts.find(i=>i.plugin===e);if(n)throw new _e("conflict",`plugin "${e}" is installed but its documents are not on the canvas: ${n.message}`,yn.GENERAL,{plugin:e,next:"Uninstall the other package providing that plugin name, then run `crtr pkg plugin update --name "+e+"`."})}u(T,"reconcilePluginDocuments");const bt=H({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:"warnings",type:"string[]",required:!1,constraint:"Advisory missing PATH executable requirements declared by the installed plugin. 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:u(async e=>{const t=e.installRef,n=e.endpoint,i=e.name,o=e.authEnv,s=e.scope,a=e.ref,r=wn(s);if(t===void 0==(n===void 0))throw w("provide exactly one of INSTALL-REF or --endpoint");if(n!==void 0){if(i===void 0)throw w("--name is required with --endpoint");if(a!==void 0)throw w("--ref is only valid with a git INSTALL-REF, not --endpoint");const y=await Bn(i,n,o,r);return await T(i),y}if(i!==void 0)throw w("--name is only valid with --endpoint");if(o!==void 0)throw w("--auth-env is only valid with --endpoint");const c=_n(t),l=c.kind==="marketplace"?await Nn(c.value,r):c.kind==="local"?await In(c.value,r):await Dn(c.value,a,r),p=q(l.name,l.scope),v=await Y(l.name,l.scope),d=await Q(l.name,l.scope),f=p===null?[]:[...ge(p.manifest),...await Z(p.manifest)];return await T(l.name),{...l,...p?.manifest.transport!==void 0?{transport:p.manifest.transport.kind}:{},...v!==void 0?{commands:v}:{},...d!==void 0?{hooks:d}:{},...f.length>0?{warnings:f}:{}}},"run")}),$t=H({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:u(async e=>{const t=e.name,n=e.scope;if(t==="crtr")throw w('cannot remove builtin plugin "crtr" \u2014 it ships with the binary');let i;if(n!==void 0){const s=te(n);if(s==="builtin")throw w("cannot remove plugins from builtin scope");i=s==="all"?["project","user"].filter(a=>a!=="project"||ie()!==null):[s]}else i=["project","user"].filter(s=>s!=="project"||ie()!==null);const o=[];for(const s of i){const a=A(s);if(a===null)continue;const r=g(a,t);b(r)&&(E(r),B(s,c=>{delete c.plugins[t]}),U(s,c=>{delete c.plugins[t]}),o.push(s))}if(o.length===0)throw h(`plugin not found: ${t}`);return await T(),{scopes:o}},"run")}),Pt=H({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)}."}],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:u(async e=>{const t=e.name,n=kn(t,e.scope);if(n===null)throw h(`plugin not found: ${t}`);const i=x(n.root);if(i===null)throw h(`plugin manifest not found at ${n.root}/.crouter-plugin/plugin.json`);const o=n.scope==="project"?C("project"):R(),s=await j(i,n.root,n.scope,o,`${n.root}/.crouter-plugin/plugin.json`,void 0,F(n.root)?"linked":"package"),a=await fe(e,!0);return await T(t),{scope:a.scope,...s!==void 0?{memory:s}:{}}},"run")}),jt=H({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:u(async e=>{const t=await fe(e,!1);return await T(),{scope:t.scope}},"run")}),xt=H({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, and pageComponents is the declared page-component registration count. 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."}],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:u(async e=>{const t=e.name,n=e.scope,i=n===void 0?void 0:te(n),o=new Map;if(t!==void 0){const r=i===void 0||i==="all"||i==="builtin"?q(t):q(t,i);if(r===null)throw h(`plugin not found: ${t}${i===void 0||i==="all"||i==="builtin"?"":` (scope: ${i})`}`);const c=await Ee(r,o,{strict:!0});return await T(t),{updated:[c]}}const s=ne().filter(r=>r.scope!=="builtin"&&(i===void 0||i==="all"||r.scope===i)),a=[];for(const r of s)try{a.push(await Ee(r,o,{strict:!1}))}catch(c){a.push({name:r.name,...r.manifest.transport!==void 0?{transport:r.manifest.transport.kind}:{},updated:!1,error:c instanceof Error?c.message:String(c)})}return await T(),{updated:a}},"run")});export{j as assertPluginCandidateValid,Nn as installFromMarketplace,Hn as installPluginArchive,je as isMarketplaceRelativeSource,jt as pluginDisable,Pt as pluginEnable,bt as pluginInstall,$t as pluginRemove,xt as pluginUpdate,kt as revalidateBundlePlugin,wt as validatePluginArchive};
|
|
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:Je(r)}:{}}}d(An,"migratePluginMemory");async function P(e,n,t,i,o,r,a="package"){try{en(e)}catch(m){const h=m instanceof Error?m.message:String(m);throw f(`plugin manifest at ${o} declares invalid memory_extensions: ${h}`)}if(xn(e,o),Tn(e,o),Sn(e,o),En(e,o),e.env!==void 0){const m=Me(e.env);if(m.length)throw f(`plugin manifest at ${o} declares an invalid env block:
|
|
14
|
+
${m.map(h=>`- ${h}`).join(`
|
|
15
|
+
`)}`,{issues:m})}const[l,c]=await Promise.all([I(),N()]),s=Q({name:e.name,scope:t,root:n,manifest:e,enabled:!0},new Set(D),l,c);if(s.issues.length>0)throw W(`plugin command behavior at ${o} is invalid`,s.issues);const u=s.manifest?.roots??[],y=wn(e,{name:e.name,leafPaths:kn(e.name,u),streamingLeafPaths:bn(e.name,u)});if(y.length>0)throw f(`plugin manifest at ${o} declares an invalid object_kinds or scopes block:
|
|
16
|
+
${y.map(m=>`- ${m}`).join(`
|
|
17
|
+
`)}`,{issues:y});await qn(e,n,t,i,o,r);const p=v(n,"memory"),k=Xe(t,i,{candidate:e});for(const m of Ye(p,h=>h.endsWith(".md"),h=>h.startsWith("."))){let h;try{h=on(Ge(m)).data}catch(R){throw f(`plugin memory document ${L(n,m)} has invalid YAML frontmatter: ${R instanceof Error?R.message.split(`
|
|
18
|
+
`)[0]:String(R)}`)}const w=nn(h);if(w!==null)throw f(`plugin memory document ${L(n,m)} is invalid: ${w}`);for(const R of Ze(h?.extensions,k))throw f(`plugin memory document ${L(n,m)} is invalid: ${R.path}: ${R.message}`)}return An(e,n,t,o,a)}d(P,"assertPluginCandidateValid");function Mn(e){return{kind:"http",endpoint:new URL(e.endpoint).origin,...e.authEnv!==void 0?{authEnv:e.authEnv}:{}}}d(Mn,"invocationTransport");async function _n(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([I(),N()]),s=Q(a,new Set(D),l,c);if(s.issues.length>0)throw W(`bundle staged package for "${e}" is invalid`,s.issues);return r}d(_n,"validateStagedBundle");function ye(e){return e.sourceMarketplace!==void 0||Y(e.root)||O(e.root)}d(ye,"isSourceInstalled");function ke(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(ke,"rejectLegacyHttpPlugin");function Ln(e,n,t){if(!$(t))return;const i=C(e,n);if(i?.manifest.bundle!==void 0)return i;throw i?.manifest.transport?.kind==="http"&&i!==void 0&&!ye(i)&&ke(e),f(`plugin "${e}" already exists at ${t}; run \`crtr pkg plugin remove ${e}\` before installing the archive plugin.`)}d(Ln,"existingBundleRoot");function we(e,n,t,i){const o=ge(e,n.endpoint,n.authEnv),r={endpoint:o.endpoint,...o.authEnv!==void 0?{authEnv:o.authEnv}:{}},a=v(A(t),e);return Ln(e,t,a),{bundle:r,transport:Mn(r),scopeRootPath:i,root:a}}d(we,"resolveBundleTarget");function be(e,n){return{name:e,endpoint:n.endpoint,...n.authEnv!==void 0?{authEnv:n.authEnv}:{}}}d(be,"fetchTarget");const Bn=6e4,Hn=1e4;async function $e(e,n,t,i){const o=n==="project"?z():S();return _(v(o,"tmp")),Ke(ae(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 ${ae(o,e)}.`}),"timeoutError")})}d($e,"withBundleSwapLock");async function je(e,n,t,i){return $e(e,t,Bn,async o=>{const r=we(e,n,t,o),a=await pe(be(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"?He(l):f(l)}return Pe(e,t,r,a.raw,a.etag,i)})}d(je,"replaceBundlePlugin");async function Rt(e,n,t,i){return $e(e,t,Hn,async o=>{try{const r=we(e,n,t,o),a=i.conditional?Ne(t).plugins[e]?.etag:void 0,l={...a!==void 0?{ifNoneMatch:a}:{},...i.timeoutMs!==void 0?{timeoutMs:i.timeoutMs}:{}},c=await pe(be(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=un(c.raw);if(fn(e,t,r.root,s)){mn(t,e,c.etag,{swapped:!1});return}return await Pe(e,t,r,c.raw,c.etag,i,s)}finally{Dn(t,e)}})}d(Rt,"revalidateBundlePlugin");function Dn(e,n){V(e,t=>{t.plugins[n]===void 0&&(t.plugins[n]={}),t.plugins[n].checked_at=J()})}d(Dn,"recordPluginCheck");async function Pe(e,n,t,i,o,r,a){const{bundle:l,transport:c,scopeRootPath:s,root:u}=t,y=await In(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 X(e,n);if(h===void 0)throw f(`bundle plugin "${e}" has no staged command report`);const w=await Z(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(Pe,"applyBundleArchive");async function xt(e){const[n,t]=await Promise.all([I(),N()]);return hn(e,{reservedCoreNames:new Set(D),coreCommandPaths:n,extensibleCoreBranches:t})}d(xt,"validatePluginArchive");async function In(e,n,t){const[i,o]=await Promise.all([I(),N()]);return pn(e,n,{...t,validation:{reservedCoreNames:new Set(D),coreCommandPaths:i,extensibleCoreBranches:o},validateStaged:d((r,a)=>_n(e,t.scope,r,t.scopeRootPath,a),"validateStaged"),invalid:d((r,a)=>W(r,a),"invalid")})}d(In,"installPluginArchive");async function Nn(e,n,t,i){const o=ge(e,n,t);return je(e,{endpoint:o.endpoint,...o.authEnv!==void 0?{authEnv:o.authEnv}:{}},i,{enable:!0})}d(Nn,"installHttpPlugin");function Un(e){if(e.startsWith("./")||e.startsWith("../")||e.startsWith("/"))return{kind:"local",value:e};if(me(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(Un,"classifyInstallRef");function Re(e){return e.startsWith("./")||e.startsWith("../")||!_e(e)&&!me(e)}d(Re,"isMarketplaceRelativeSource");async function xe(e,n,t){const i=n.get(e);if(i!==void 0)return i;const o=Ie(e);if(o===null){if(t.strict)throw g(`marketplace not found: ${e}`);return null}const r=le(o.root);if(r===null)throw g(`marketplace manifest not found: ${o.root}/.crouter-marketplace/marketplace.json`);if(!Y(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(ne(o.root),`.${e}.${process.pid}.${Date.now()}.candidate`),c=`${l}.previous`;try{K(o.url,l,{ref:o.ref,depth:1});const s=le(l);if(s===null)throw g(`marketplace manifest not found in candidate checkout: ${l}/.crouter-marketplace/marketplace.json`);const u=te().filter(p=>p.sourceMarketplace===e&&O(p.root));for(const p of u){const k=s.plugins.find(qe=>qe.name===p.name);if(k===void 0||!Re(k.source))throw f(`marketplace candidate removes relative-source plugin "${p.name}" while it is installed`);const m=ze(p.root);if(m===null)throw f(`installed marketplace plugin "${p.name}" has no readable symlink target`);const h=L(o.root,F(ne(p.root),m)),w=F(l,k.source);if(w!==F(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 Te=p.scope==="project"?M("project"):S();await P(R,w,p.scope,Te,`${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);Se(p.scope,p.name,k.version??p.manifest.version)}V(o.scope,p=>{p.marketplaces[e]===void 0&&(p.marketplaces[e]={}),p.marketplaces[e].last_updated=J()});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(xe,"refreshMarketplace");function Se(e,n,t){H(e,i=>{const o=i.plugins[n];o!==void 0?t!==void 0&&(o.version=t):i.plugins[n]={enabled:!0,version:t}}),V(e,i=>{i.plugins[n]===void 0&&(i.plugins[n]={}),i.plugins[n].last_updated=J()})}d(Se,"updatePluginVersion");async function Fn(e,n,t){const i=t==="project"?z():S();G(t,i);const o=A(t),r=Qe(e),a=v(i,"tmp",`${r}.${process.pid}.${Date.now()}`);_(v(i,"tmp"));try{K(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 _(o),j(a,s),H(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(Fn,"installFromGit");async function Vn(e,n){const t=F(e),i=x(t);if(i===null)throw g(`plugin manifest not found at ${t}/.crouter-plugin/plugin.json`);const o=n==="project"?z():S(),r=await P(i,t,n,o,`${t}/.crouter-plugin/plugin.json`,void 0,"linked");G(n,o);const a=v(A(n),i.name);if($(a))throw f(`plugin "${i.name}" is already installed at ${a}`);return re(t,a),H(n,l=>{l.plugins[i.name]={enabled:!0,version:i.version}}),{name:i.name,scope:n,path:a,...r!==void 0?{memory:r}:{}}}d(Vn,"installFromLocal");async function On(e,n){const t=await xe(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);G(n,o);const r=v(A(n),e.plugin);if($(r))throw f(`plugin "${e.plugin}" is already installed at ${r}`);let a,l;if(Re(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"),re(s,r),a=u}else{const s=v(o,"tmp",`${e.plugin}.${process.pid}.${Date.now()}`);_(v(o,"tmp"));try{K(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`),_(A(n)),j(s,r),a=u}finally{E(s)}}const c=i.version??a.version;return H(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(On,"installFromMarketplace");async function Kn(e){if(O(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(!Y(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=se(["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=se(["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);_(v(i,"tmp"));try{K(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(Kn,"updateGitPluginCandidate");async function Ee(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=ye(e);if(!i&&e.manifest.bundle!==void 0){const u=await je(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&&ke(e.name);let o,r=!1;if(e.sourceMarketplace!==void 0){const u=await xe(e.sourceMarketplace,n,t);u!==null&&(o=u.manifest.plugins.find(p=>p.name===e.name)?.version,r=u.pull.updated)}const a=await Kn(e);Se(e.scope,e.name,o??a.manifest.version);const l=await X(e.name,e.scope),c=await Z(e.name,e.scope),s=[...ve(a.manifest),...await ee(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(Ee,"updateOnePlugin");async function q(e){const n=await Le().documents.reconcilePackages().catch(i=>Be(i)),t=n.conflicts.find(i=>i.plugin===e);if(t)throw new De("conflict",`plugin "${e}" is installed but its documents are not on the canvas: ${t.message}`,$n.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 Wn(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(Wn,"pluginObjectReport");function U(e,n,t){const{warnings:i,appScopes:o}=Wn(n,t),a=[...e.warnings??[],...i];return{...e,...a.length>0?{warnings:a}:{},...o!==void 0?{appScopes:o}:{}}}d(U,"withObjectReport");const St=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,l=Pn(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 Nn(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=Un(n),s=c.kind==="marketplace"?await On(c.value,l):c.kind==="local"?await Vn(c.value,l):await Fn(c.value,a,l),u=C(s.name,s.scope),y=await X(s.name,s.scope),p=await Z(s.name,s.scope),k=u===null?[]:[...ve(u.manifest),...await ee(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")}),Et=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');let i;if(t!==void 0){const r=ie(t);if(r==="builtin")throw b("cannot remove plugins from builtin scope");i=r==="all"?["project","user"].filter(a=>a!=="project"||oe()!==null):[r]}else i=["project","user"].filter(r=>r!=="project"||oe()!==null);const o=[];for(const r of i){const a=A(r);if(a===null)continue;const l=v(a,n);$(l)&&(E(l),H(r,c=>{delete c.plugins[n]}),V(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")}),Tt=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,t=jn(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,O(t.root)?"linked":"package"),a=await he(e,!0),l=await q(n);return U({scope:a.scope,...r!==void 0?{memory:r}:{}},l,n)},"run")}),qt=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=>{const n=await he(e,!1);return await q(),{scope:n.scope}},"run")}),Ct=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,t=e.scope,i=t===void 0?void 0:ie(t),o=new Map;if(n!==void 0){const c=i===void 0||i==="all"||i==="builtin"?C(n):C(n,i);if(c===null)throw g(`plugin not found: ${n}${i===void 0||i==="all"||i==="builtin"?"":` (scope: ${i})`}`);const s=await Ee(c,o,{strict:!0}),u=await q(n);return{updated:[U(s,u,s.name)]}}const r=te().filter(c=>c.scope!=="builtin"&&(i===void 0||i==="all"||c.scope===i)),a=[];for(const c of r)try{a.push(await Ee(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,On as installFromMarketplace,In as installPluginArchive,Re as isMarketplaceRelativeSource,qt as pluginDisable,Tt as pluginEnable,St as pluginInstall,Et as pluginRemove,Ct as pluginUpdate,Rt as revalidateBundlePlugin,xt as validatePluginArchive};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var Te=Object.defineProperty;var o=(e,t)=>Te(e,"name",{value:t,configurable:!0});import{randomBytes as P}from"node:crypto";import{parseScope as ae}from"@north-light/identity";import{importNodeSqlAuthority as de,recreateNodeRowsView as _e}from"./node-sql-migration.js";import{migrateRecoveryWitnesses as ce}from"./recovery.js";import{migrateMessagingState as Ne}from"./deliveries.js";import{migrateHumanRequestsState as Le}from"../human/requests.js";import{existsSync as U,readFileSync as M,readdirSync as k,realpathSync as le,statSync as ue}from"node:fs";import{basename as pe,join as j,resolve as Y}from"node:path";import{appSpace as Re}from"../layout.js";import{emitEvent as Ae}from"../events/emit.js";import{isSafeNodeId as Oe,nodesRoot as h,nodeMetaPath as x}from"./paths.js";import{convertLegacyEdges as
|
|
1
|
+
var Te=Object.defineProperty;var o=(e,t)=>Te(e,"name",{value:t,configurable:!0});import{randomBytes as P}from"node:crypto";import{parseScope as ae}from"@north-light/identity";import{importNodeSqlAuthority as de,recreateNodeRowsView as _e}from"./node-sql-migration.js";import{migrateRecoveryWitnesses as ce}from"./recovery.js";import{migrateMessagingState as Ne}from"./deliveries.js";import{migrateHumanRequestsState as Le}from"../human/requests.js";import{existsSync as U,readFileSync as M,readdirSync as k,realpathSync as le,statSync as ue}from"node:fs";import{basename as pe,join as j,resolve as Y}from"node:path";import{appSpace as Re}from"../layout.js";import{emitEvent as Ae}from"../events/emit.js";import{isSafeNodeId as Oe,nodesRoot as h,nodeMetaPath as x}from"./paths.js";import{convertLegacyEdges as Ie}from"../../migrations/004-canvas-documents/legacy-edges.js";import{customExtensionPaths as me}from"./extensions.js";import{readMetaObject as K,updateJsonFileDurably as q,withMetaLock as fe,writeMetaObjectLocked as Xe}from"./meta-file.js";function Ue(e){e.exec(`
|
|
2
2
|
CREATE TABLE IF NOT EXISTS nodes (
|
|
3
3
|
node_id TEXT PRIMARY KEY,
|
|
4
4
|
name TEXT NOT NULL,
|
|
@@ -23,7 +23,7 @@ CREATE TABLE IF NOT EXISTS edges (
|
|
|
23
23
|
CREATE INDEX IF NOT EXISTS idx_edges_to ON edges(type, to_id);
|
|
24
24
|
CREATE INDEX IF NOT EXISTS idx_edges_from ON edges(type, from_id);
|
|
25
25
|
CREATE INDEX IF NOT EXISTS idx_nodes_status ON nodes(status);
|
|
26
|
-
`)}o(Ue,"baselineSchema");function b(e,t){return new Set(e.prepare(`PRAGMA table_info(${t})`).all().map(n=>n.name))}o(b,"tableColumns");function $(e,t,n,E){t.has(n)||(e.exec(E),t.add(n))}o($,"addColumnIfMissing");function
|
|
26
|
+
`)}o(Ue,"baselineSchema");function b(e,t){return new Set(e.prepare(`PRAGMA table_info(${t})`).all().map(n=>n.name))}o(b,"tableColumns");function $(e,t,n,E){t.has(n)||(e.exec(E),t.add(n))}o($,"addColumnIfMissing");function I(e){return b(e,"nodes")}o(I,"nodeColumns");const N=$;function Ce(e){const t=I(e);N(e,t,"intent","ALTER TABLE nodes ADD COLUMN intent TEXT;"),N(e,t,"pi_pid","ALTER TABLE nodes ADD COLUMN pi_pid INTEGER;"),N(e,t,"window","ALTER TABLE nodes ADD COLUMN window TEXT;"),N(e,t,"tmux_session","ALTER TABLE nodes ADD COLUMN tmux_session TEXT;")}o(Ce,"addRuntimeColumns");function Se(e){const t=h();if(!U(t))return;const n=e.prepare('UPDATE nodes SET intent = ?, pi_pid = ?, "window" = ?, tmux_session = ? WHERE node_id = ?');for(const E of k(t)){const T=x(E);if(!U(T))continue;let i;try{i=JSON.parse(M(T,"utf8"))}catch{continue}n.run(i.intent??null,i.pi_pid??null,i.window??null,i.tmux_session??null,E)}}o(Se,"backfillRuntime");function De(e){B(e,3,()=>{if(!(e.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'edges'").get()!==void 0)){e.exec(`
|
|
27
27
|
CREATE TABLE edges (
|
|
28
28
|
type TEXT NOT NULL,
|
|
29
29
|
from_id TEXT NOT NULL,
|
|
@@ -57,7 +57,7 @@ DROP TABLE edges;
|
|
|
57
57
|
ALTER TABLE edges_new RENAME TO edges;
|
|
58
58
|
CREATE INDEX IF NOT EXISTS idx_edges_to ON edges(type, to_id);
|
|
59
59
|
CREATE INDEX IF NOT EXISTS idx_edges_from ON edges(type, from_id);
|
|
60
|
-
`);const E=e.prepare("SELECT COUNT(*) AS n FROM edges").get().n;if(E!==n)throw new Error(`edges FK rebuild lost rows: expected ${n} non-orphan edge(s), got ${E}`);const T=e.prepare("PRAGMA foreign_key_check").all();if(T.length>0)throw new Error(`edges FK rebuild left ${T.length} foreign-key violation(s)`)})}o(De,"edgesForeignKeyCascade");function ge(e){const t=
|
|
60
|
+
`);const E=e.prepare("SELECT COUNT(*) AS n FROM edges").get().n;if(E!==n)throw new Error(`edges FK rebuild lost rows: expected ${n} non-orphan edge(s), got ${E}`);const T=e.prepare("PRAGMA foreign_key_check").all();if(T.length>0)throw new Error(`edges FK rebuild left ${T.length} foreign-key violation(s)`)})}o(De,"edgesForeignKeyCascade");function ge(e){const t=I(e);N(e,t,"pane","ALTER TABLE nodes ADD COLUMN pane TEXT;")}o(ge,"addPaneColumn");function we(e){e.exec(`
|
|
61
61
|
CREATE TABLE IF NOT EXISTS focuses (
|
|
62
62
|
focus_id TEXT PRIMARY KEY, -- stable internal id for the viewport
|
|
63
63
|
pane TEXT, -- the durable %pane_id realizing the focus
|
|
@@ -131,12 +131,12 @@ SELECT
|
|
|
131
131
|
FROM wakeups;
|
|
132
132
|
|
|
133
133
|
DROP TABLE wakeups;
|
|
134
|
-
`)}o(ve,"migrateWakeupsToTriggers");function he(e){const t=
|
|
134
|
+
`)}o(ve,"migrateWakeupsToTriggers");function he(e){const t=I(e);N(e,t,"host_kind","ALTER TABLE nodes ADD COLUMN host_kind TEXT;")}o(he,"addHostKindColumn");function xe(e){e.exec(`
|
|
135
135
|
CREATE TABLE IF NOT EXISTS canvas_meta (
|
|
136
136
|
key TEXT PRIMARY KEY,
|
|
137
137
|
value TEXT NOT NULL
|
|
138
138
|
);
|
|
139
|
-
`)}o(xe,"addCanvasMetaTable");function Fe(e){const t=
|
|
139
|
+
`)}o(xe,"addCanvasMetaTable");function Fe(e){const t=I(e);N(e,t,"profile_id","ALTER TABLE nodes ADD COLUMN profile_id TEXT;")}o(Fe,"addProfileIdColumn");function Me(e){const t=I(e);N(e,t,"pi_pid_identity","ALTER TABLE nodes ADD COLUMN pi_pid_identity TEXT;")}o(Me,"addPidIdentityColumn");function ke(e){const t=I(e);N(e,t,"waiting_for","ALTER TABLE nodes ADD COLUMN waiting_for TEXT;")}o(ke,"addMessageWaitColumn");function J(e){const t=I(e);if(t.size===0)return;N(e,t,"final_report","ALTER TABLE nodes ADD COLUMN final_report TEXT;"),N(e,t,"finalized_at","ALTER TABLE nodes ADD COLUMN finalized_at TEXT;");const n=e.prepare("SELECT node_id FROM nodes WHERE status = 'done' AND final_report IS NULL").all(),E=e.prepare("UPDATE nodes SET final_report = ?, finalized_at = ? WHERE node_id = ? AND final_report IS NULL");for(const{node_id:T}of n){const i=j(h(),T,"reports");if(!U(i))continue;let a=null;for(const c of k(i))if(c.endsWith(".md"))try{const L=M(j(i,c),"utf8"),l=/^---\nnode: [^\n]+\nkind: final\nts: ([^\n]+)\n---\n/.exec(L);if(l===null||Number.isNaN(Date.parse(l[1])))continue;const r={name:c,ts:l[1]};(a===null||r.ts>a.ts||r.ts===a.ts&&r.name>a.name)&&(a=r)}catch{}a!==null&&E.run(a.name,a.ts,T)}}o(J,"addCanonicalFinalColumns");function je(e){const t=h();if(U(t))for(const n of k(t)){const E=x(n);U(E)&&q(E,T=>{const i=T.launch;if(typeof i!="object"||i===null||Array.isArray(i))return!1;const a=i.extensions;if(!Array.isArray(a)||!a.every(L=>typeof L=="string"))return!1;const c=me(a);return c.length===a.length?!1:(i.extensions=c,!0)})}}o(je,"normalizePersistedLaunchExtensions");function He(e){const t=I(e);N(e,t,"waiting_for_generation","ALTER TABLE nodes ADD COLUMN waiting_for_generation INTEGER NOT NULL DEFAULT 0;")}o(He,"addMessageWaitGenerationColumn");function Ge(e){e.exec(`
|
|
140
140
|
CREATE TABLE IF NOT EXISTS consult_outbox (
|
|
141
141
|
node_id TEXT PRIMARY KEY, -- legacy work node id
|
|
142
142
|
dir TEXT NOT NULL, -- legacy ticket directory
|
|
@@ -228,7 +228,7 @@ DROP INDEX IF EXISTS idx_triggers_owner;
|
|
|
228
228
|
DROP INDEX IF EXISTS idx_triggers_eval;
|
|
229
229
|
DROP INDEX IF EXISTS idx_triggers_deadline;
|
|
230
230
|
DROP TABLE IF EXISTS triggers;
|
|
231
|
-
`)}o(Qe,"migrateTriggersToCrons");function ze(e,t,n,E,T){const i=X(n,"owner_id",E),a=e.prepare("SELECT cwd, profile_id FROM nodes WHERE node_id = ?").get(i),c=(a===void 0?void 0:w(a,"cwd"))??T,L=a===void 0?null:w(a,"profile_id")??null,l=w(n,"node_id")??null,r=X(n,"action",E),_=X(n,"action_mode",E),d=X(n,"trigger_type",E),u=Ye(n.payload),p=w(n,"created")??new Date().toISOString(),O=_==="deadline_message"?"urgent":H(u,"tier")??"normal",R=H(u,"label");let s,A,
|
|
231
|
+
`)}o(Qe,"migrateTriggersToCrons");function ze(e,t,n,E,T){const i=X(n,"owner_id",E),a=e.prepare("SELECT cwd, profile_id FROM nodes WHERE node_id = ?").get(i),c=(a===void 0?void 0:w(a,"cwd"))??T,L=a===void 0?null:w(a,"profile_id")??null,l=w(n,"node_id")??null,r=X(n,"action",E),_=X(n,"action_mode",E),d=X(n,"trigger_type",E),u=Ye(n.payload),p=w(n,"created")??new Date().toISOString(),O=_==="deadline_message"?"urgent":H(u,"tier")??"normal",R=H(u,"label");let s,A,m=!1;if(r==="msg"&&(_==="inbox_message"||_==="deadline_message")){if(l===null)throw new Error("has no message target");const f=H(u,"body");f!==void 0?s=z(l,O,f,E):d==="when"?(m=!0,s=Ke(l,O)):s=z(l,O,R??"Scheduled message (no body was recorded when this schedule was armed).",E),A=R??(_==="deadline_message"?"deadline":"scheduled message")}else if(r==="msg"&&_==="fresh_revive"){if(l===null)throw new Error("has no revive target");s=`crtr node lifecycle revive ${g(l)} --fresh`,A=R??"scheduled fresh revive"}else if(r==="new"&&_==="node_birth")s=qe(u,E),A=H(u,"name")??`scheduled ${X(u,"kind",E)} node`;else throw new Error(`has unsupported action ${r}/${_}`);let D,y,G=null;if(d==="when"){D=X(n,"next_eval_at",E),y=X(n,"eval_every",E),G=w(n,"timeout_at")??null;const f=X(n,"when_script",E);s=`${m?`if ! CRTR_GATE_STDOUT="$(
|
|
232
232
|
${f}
|
|
233
233
|
)"; then`:`if ! (
|
|
234
234
|
${f}
|
|
@@ -378,7 +378,7 @@ CREATE TABLE human_action_deliveries (
|
|
|
378
378
|
updated_at INTEGER NOT NULL
|
|
379
379
|
);
|
|
380
380
|
CREATE INDEX idx_human_action_deliveries_due ON human_action_deliveries(state, next_attempt_at);
|
|
381
|
-
`)}o(Tt,"addHumanActionDeliveries");function at(e){const t=
|
|
381
|
+
`)}o(Tt,"addHumanActionDeliveries");function at(e){const t=I(e);N(e,t,"pi_session_file","ALTER TABLE nodes ADD COLUMN pi_session_file TEXT;");for(const n of k(h(),{withFileTypes:!0}))if(n.isDirectory())try{const E=JSON.parse(M(x(n.name),"utf8"));typeof E.pi_session_file=="string"&&e.prepare("UPDATE nodes SET pi_session_file = ? WHERE node_id = ?").run(E.pi_session_file,n.name)}catch{}e.exec("CREATE INDEX IF NOT EXISTS idx_nodes_pi_session_file ON nodes(pi_session_file);")}o(at,"addNodeSessionFileIndex");function Z(e,t){const n=o(c=>{const L=Y(c);return U(L)?le(L):L},"canonical"),E=e.prepare("UPDATE nodes SET pi_session_file = ? WHERE node_id = ?");e.exec("DROP INDEX IF EXISTS idx_nodes_pi_session_file;");const T=e.prepare("SELECT node_id FROM nodes ORDER BY created ASC, node_id ASC").all();for(const{node_id:c}of T){const L=x(c);if(K(L)===null){E.run(null,c);continue}const l=fe(L,()=>{const r=K(L);if(r===null)throw new Error(`node ${c} session metadata changed during ownership migration`);if(typeof r.pi_session_file!="string")return null;const _=r.pi_session_file,d=n(_);return d!==_&&(r.pi_session_file=d,Xe(L,r)),d});E.run(l,c)}const i=e.prepare(`
|
|
382
382
|
SELECT pi_session_file
|
|
383
383
|
FROM nodes
|
|
384
384
|
WHERE pi_session_file IS NOT NULL
|
|
@@ -389,7 +389,7 @@ SELECT node_id
|
|
|
389
389
|
FROM nodes
|
|
390
390
|
WHERE pi_session_file = ?
|
|
391
391
|
ORDER BY created ASC, node_id ASC
|
|
392
|
-
`).all(c);for(const{node_id:l}of L.slice(1)){if(!q(x(l),_=>{const d=_.pi_session_file;if(typeof d!="string"||n(d)!==c)throw new Error(`node ${l} session metadata changed during ownership migration`);return _.pi_session_id=null,_.pi_session_file=null,!0}))throw new Error(`node ${l} session metadata changed during ownership migration`);a.run(l)}}e.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_nodes_pi_session_file ON nodes(pi_session_file);")}o(Z,"reconcileNodeSessionFileOwners");function dt(e){Z(e,!0)}o(dt,"makeNodeSessionFileUnique");function _t(e){Z(e,!1)}o(_t,"repairNodeSessionFileUniqueIndex");function ct(e){const t=
|
|
392
|
+
`).all(c);for(const{node_id:l}of L.slice(1)){if(!q(x(l),_=>{const d=_.pi_session_file;if(typeof d!="string"||n(d)!==c)throw new Error(`node ${l} session metadata changed during ownership migration`);return _.pi_session_id=null,_.pi_session_file=null,!0}))throw new Error(`node ${l} session metadata changed during ownership migration`);a.run(l)}}e.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_nodes_pi_session_file ON nodes(pi_session_file);")}o(Z,"reconcileNodeSessionFileOwners");function dt(e){Z(e,!0)}o(dt,"makeNodeSessionFileUnique");function _t(e){Z(e,!1)}o(_t,"repairNodeSessionFileUniqueIndex");function ct(e){const t=I(e);N(e,t,"frozen_at","ALTER TABLE nodes ADD COLUMN frozen_at TEXT;"),N(e,t,"respawn_failures","ALTER TABLE nodes ADD COLUMN respawn_failures INTEGER NOT NULL DEFAULT 0;"),N(e,t,"respawn_not_before","ALTER TABLE nodes ADD COLUMN respawn_not_before TEXT;"),N(e,t,"launched_at","ALTER TABLE nodes ADD COLUMN launched_at TEXT;"),N(e,t,"last_settled_at","ALTER TABLE nodes ADD COLUMN last_settled_at TEXT;"),N(e,t,"terminal_reason","ALTER TABLE nodes ADD COLUMN terminal_reason TEXT;"),e.exec(`
|
|
393
393
|
UPDATE nodes SET terminal_reason = CASE
|
|
394
394
|
WHEN status = 'done' AND final_report IS NOT NULL THEN 'finalized'
|
|
395
395
|
WHEN status = 'done' AND intent = 'parked' THEN 'parked'
|
|
@@ -398,7 +398,7 @@ UPDATE nodes SET terminal_reason = CASE
|
|
|
398
398
|
WHEN status = 'dead' THEN 'crashed'
|
|
399
399
|
END
|
|
400
400
|
WHERE status IN ('done','canceled','dead') AND terminal_reason IS NULL;
|
|
401
|
-
`)}o(ct,"addNodeLifecycleColumns");function Nt(e){const t=
|
|
401
|
+
`)}o(ct,"addNodeLifecycleColumns");function Nt(e){const t=I(e);N(e,t,"description","ALTER TABLE nodes ADD COLUMN description TEXT;"),N(e,t,"cycles","ALTER TABLE nodes ADD COLUMN cycles INTEGER;");const n=h();if(!U(n))return;const E=e.prepare("UPDATE nodes SET description = ?, cycles = ? WHERE node_id = ?");for(const T of k(n)){const i=x(T);if(!U(i))continue;let a;try{a=JSON.parse(M(i,"utf8"))}catch{continue}const c=typeof a.description=="string"?a.description:null,L=typeof a.cycles=="number"?a.cycles:null;E.run(c,L,T)}}o(Nt,"addNodeLabelColumns");function Lt(e){const t=I(e);N(e,t,"outcome_kind","ALTER TABLE nodes ADD COLUMN outcome_kind TEXT;"),N(e,t,"outcome_reason","ALTER TABLE nodes ADD COLUMN outcome_reason TEXT;"),N(e,t,"outcome_at","ALTER TABLE nodes ADD COLUMN outcome_at TEXT;"),N(e,t,"outcome_revision","ALTER TABLE nodes ADD COLUMN outcome_revision INTEGER NOT NULL DEFAULT 0;"),N(e,t,"outcome_detail_json","ALTER TABLE nodes ADD COLUMN outcome_detail_json TEXT;"),N(e,t,"deadline_at","ALTER TABLE nodes ADD COLUMN deadline_at TEXT;"),e.exec(`
|
|
402
402
|
CREATE TABLE IF NOT EXISTS node_outcome_deliveries (
|
|
403
403
|
node_id TEXT PRIMARY KEY,
|
|
404
404
|
state TEXT NOT NULL,
|
|
@@ -431,7 +431,7 @@ UPDATE nodes SET outcome_kind='failure',
|
|
|
431
431
|
outcome_reason=COALESCE(terminal_reason,'crashed'),
|
|
432
432
|
outcome_at=COALESCE(finalized_at, created), outcome_revision=1
|
|
433
433
|
WHERE final_report IS NULL AND status IN ('done','dead','canceled') AND outcome_at IS NULL;
|
|
434
|
-
`)}o(Lt,"addNodeOutcomeAndDeadline");function lt(e){const t=
|
|
434
|
+
`)}o(Lt,"addNodeOutcomeAndDeadline");function lt(e){const t=I(e);N(e,t,"telemetry_tokens_in","ALTER TABLE nodes ADD COLUMN telemetry_tokens_in INTEGER;"),N(e,t,"telemetry_context_tokens","ALTER TABLE nodes ADD COLUMN telemetry_context_tokens INTEGER;"),N(e,t,"telemetry_last_activity","ALTER TABLE nodes ADD COLUMN telemetry_last_activity TEXT;"),N(e,t,"telemetry_updated_at","ALTER TABLE nodes ADD COLUMN telemetry_updated_at TEXT;");const n=e.prepare(`UPDATE nodes
|
|
435
435
|
SET telemetry_tokens_in = ?, telemetry_context_tokens = ?,
|
|
436
436
|
telemetry_last_activity = ?, telemetry_updated_at = ?
|
|
437
437
|
WHERE node_id = ?`);for(const{node_id:E}of e.prepare("SELECT node_id FROM nodes").all()){const T=j(h(),E,"job","telemetry.json");if(U(T))try{const i=JSON.parse(M(T,"utf8")),a=typeof i.tokens_in=="number"&&Number.isFinite(i.tokens_in)?i.tokens_in:null,c=typeof i.context_tokens=="number"&&Number.isFinite(i.context_tokens)?i.context_tokens:null,L=typeof i.last_activity=="string"?i.last_activity:null,l=ue(T).mtime.toISOString();n.run(a,c,L,l,E)}catch{}}}o(lt,"addNodeTelemetryColumns");function ut(e){e.exec(`
|
|
@@ -441,7 +441,7 @@ CREATE INDEX IF NOT EXISTS deliveries_external_due_order
|
|
|
441
441
|
DROP INDEX IF EXISTS deliveries_external_due;
|
|
442
442
|
CREATE INDEX IF NOT EXISTS idx_nodes_parent_created ON nodes(parent, created);
|
|
443
443
|
CREATE INDEX IF NOT EXISTS idx_edges_type_created ON edges(type, created);
|
|
444
|
-
`)}o(ut,"addRecurringLaneIndexes");function pt(e){_e(e)}o(pt,"projectLiveNodeState");function Rt(e){N(e,
|
|
444
|
+
`)}o(ut,"addRecurringLaneIndexes");function pt(e){_e(e)}o(pt,"projectLiveNodeState");function Rt(e){N(e,I(e),"scopes_json","ALTER TABLE nodes ADD COLUMN scopes_json TEXT;")}o(Rt,"addNodeScopes");function At(e){const t=I(e);N(e,t,"grantee","ALTER TABLE nodes ADD COLUMN grantee TEXT NOT NULL DEFAULT 'user:local';"),N(e,t,"sub","ALTER TABLE nodes ADD COLUMN sub TEXT;"),N(e,t,"grant_id","ALTER TABLE nodes ADD COLUMN grant_id TEXT;"),N(e,t,"client_id","ALTER TABLE nodes ADD COLUMN client_id TEXT;"),N(e,t,"metadata_json","ALTER TABLE nodes ADD COLUMN metadata_json TEXT;"),N(e,t,"run_id","ALTER TABLE nodes ADD COLUMN run_id TEXT;"),N(e,t,"event_cursor","ALTER TABLE nodes ADD COLUMN event_cursor INTEGER;"),N(e,t,"usage_json","ALTER TABLE nodes ADD COLUMN usage_json TEXT;"),N(e,t,"deleting_at","ALTER TABLE nodes ADD COLUMN deleting_at TEXT;"),e.exec(`
|
|
445
445
|
ALTER TABLE sessions ADD COLUMN event_offset INTEGER;
|
|
446
446
|
CREATE INDEX idx_nodes_run_id ON nodes(run_id);
|
|
447
447
|
-- Child nodes may be pruned while their root run and event history remain.
|
|
@@ -469,7 +469,7 @@ CREATE INDEX IF NOT EXISTS idx_edges_type_created ON edges(type, created);
|
|
|
469
469
|
`)}o(At,"addRunFoundation");function Ot(e){e.exec("ALTER TABLE nodes ADD COLUMN last_activity_at TEXT;"),e.exec(`UPDATE nodes SET last_activity_at = (
|
|
470
470
|
SELECT e.ts FROM run_events e WHERE e.run_id = nodes.node_id
|
|
471
471
|
ORDER BY e.sequence_number DESC LIMIT 1
|
|
472
|
-
) WHERE run_id = node_id`)}o(Ot,"addRunLastActivity");function
|
|
472
|
+
) WHERE run_id = node_id`)}o(Ot,"addRunLastActivity");function It(e){e.exec(`
|
|
473
473
|
CREATE TABLE structured_requests (
|
|
474
474
|
request_id TEXT PRIMARY KEY,
|
|
475
475
|
node_id TEXT REFERENCES nodes(node_id) ON DELETE CASCADE,
|
|
@@ -494,7 +494,7 @@ CREATE TABLE structured_requests (
|
|
|
494
494
|
CREATE UNIQUE INDEX idx_structured_requests_one_pending_node ON structured_requests(node_id) WHERE state = 'pending';
|
|
495
495
|
CREATE INDEX idx_structured_requests_expiry ON structured_requests(state, deadline_at);
|
|
496
496
|
CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done_at) WHERE effects_done_at IS NULL;
|
|
497
|
-
`)}o(
|
|
497
|
+
`)}o(It,"addStructuredRequests");function mt(e){e.exec(`
|
|
498
498
|
CREATE TABLE grants (
|
|
499
499
|
id TEXT PRIMARY KEY, runtime TEXT NOT NULL, sub TEXT NOT NULL, grantee TEXT NOT NULL,
|
|
500
500
|
rev INTEGER NOT NULL, scopes_json TEXT NOT NULL, resolved_scopes_json TEXT NOT NULL,
|
|
@@ -503,7 +503,7 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
|
|
|
503
503
|
);
|
|
504
504
|
CREATE UNIQUE INDEX idx_grants_grantee_sub ON grants(grantee, sub);
|
|
505
505
|
CREATE TABLE runtime_state (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
506
|
-
`)}o(
|
|
506
|
+
`)}o(mt,"addGrantMirror");function ft(e){e.exec("UPDATE nodes SET grantee = 'app:terminal' WHERE grantee = 'user:local';")}o(ft,"migrateTerminalGrantees");function Xt(e){const t={llm:"crtr:llm",act:"crtr:act",schedule:"crtr:schedule","memory:read":"crtr:memory:read:user","memory:write":"crtr:memory:rw:user"},n=e.prepare("SELECT node_id, scopes_json FROM nodes WHERE scopes_json IS NOT NULL").all(),E=e.prepare("UPDATE nodes SET scopes_json = ? WHERE node_id = ?");for(const T of n){const i=JSON.parse(T.scopes_json);if(!Array.isArray(i))throw new Error(`invalid launched scopes for node ${T.node_id}`);const a=[];for(const c of i){if(typeof c!="string")continue;const L=t[c]??c;try{ae(L),a.push(L)}catch(l){if(!(l instanceof TypeError))throw l}}E.run(JSON.stringify([...new Set(a)]),T.node_id)}}o(Xt,"migrateLaunchedScopes");function Ut(e){e.exec(`CREATE TABLE node_tokens (
|
|
507
507
|
broker_process_id TEXT PRIMARY KEY REFERENCES executions(execution_id) ON DELETE CASCADE,
|
|
508
508
|
node_id TEXT NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
|
|
509
509
|
token_hash TEXT NOT NULL UNIQUE, grantee TEXT NOT NULL, uid INTEGER NOT NULL,
|
|
@@ -881,7 +881,7 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
|
|
|
881
881
|
VALUES (?, ?, 1, NULL, ?) RETURNING rowid`),l=e.prepare(`INSERT INTO watch_subscriptions
|
|
882
882
|
(watch, pattern, filter_json, config_json, started_by, ends_on, created_at)
|
|
883
883
|
VALUES (?, 'outcome', NULL, '{"1":"after-turn"}', 'spawn', NULL, ?)`),r=e.prepare("UPDATE deliveries SET destination_id = ?, idempotency_key = NULL WHERE delivery_id = ?"),_=e.prepare("SELECT body_json FROM messages WHERE message_id = ?"),d=e.prepare(`UPDATE messages SET body_json = ?, event = 'outcome', object_id = ?, level = 1
|
|
884
|
-
WHERE message_id = ?`);for(const s of i){if(s.owner===null)throw new Error(`v67 outcome node ${s.node_id} has no owner`);const A=JSON.parse(s.adapter_json);if(A.adapter!=="subprocess"||!Array.isArray(A.argv)||typeof A.cwd!="string"||typeof A.config_origin!="string")throw new Error(`v67 invalid outcome adapter on ${s.delivery_id}`);const
|
|
884
|
+
WHERE message_id = ?`);for(const s of i){if(s.owner===null)throw new Error(`v67 outcome node ${s.node_id} has no owner`);const A=JSON.parse(s.adapter_json);if(A.adapter!=="subprocess"||!Array.isArray(A.argv)||typeof A.cwd!="string"||typeof A.config_origin!="string")throw new Error(`v67 invalid outcome adapter on ${s.delivery_id}`);const m=P(16).toString("hex"),D=new Date(s.created_at).toISOString(),y=a.get(m,s.owner,s.privacy,`outcome/${s.node_id}`,D);c.run(y.rowid,JSON.stringify({argv:A.argv,cwd:A.cwd,config_origin:A.config_origin,name:s.destination_id}),JSON.stringify(A.payload??null));const G=L.get(y.rowid,e.prepare("SELECT rowid FROM objects WHERE id = ?").get(s.node_id).rowid,D);if(l.run(G.rowid,D),s.message_id===null){if(s.state!=="armed")throw new Error(`v67 delivery ${s.delivery_id} has no message but is ${s.state}`);continue}const f=_.get(s.message_id);if(!f||f.body_json===null)throw new Error(`v67 delivery ${s.delivery_id} has no frozen outcome document`);if(e.prepare("SELECT COUNT(*) AS n FROM deliveries WHERE message_id = ?").get(s.message_id).n!==1)throw new Error(`v67 outcome message ${s.message_id} is shared`);const v=JSON.parse(f.body_json);if(v===null||Array.isArray(v)||typeof v!="object")throw new Error(`v67 invalid outcome body on ${s.delivery_id}`);delete v.action,delete v.delivery,d.run(JSON.stringify(v),s.node_id,s.message_id),r.run(m,s.delivery_id)}const u=e.prepare("SELECT COUNT(*) AS n FROM deliveries WHERE state = 'armed'").get();if(u.n!==i.filter(s=>s.state==="armed").length)throw new Error(`v67 found ${u.n} armed deliveries but only ${i.length} mapped outcome rows`);const p=e.prepare("SELECT COUNT(*) AS n FROM deliveries").get().n-u.n,O=e.prepare("SELECT name, sql FROM sqlite_master WHERE type = 'view' AND sql LIKE '%deliveries%'").all();for(const s of O)e.exec(`DROP VIEW "${s.name.replaceAll('"','""')}"`);e.exec(`
|
|
885
885
|
CREATE TABLE deliveries_new (
|
|
886
886
|
delivery_id TEXT PRIMARY KEY,
|
|
887
887
|
message_id TEXT NOT NULL REFERENCES messages(message_id),
|
|
@@ -924,7 +924,7 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
|
|
|
924
924
|
state TEXT NOT NULL DEFAULT 'reserved' CHECK (state IN ('reserved', 'warm_claimed', 'complete'))
|
|
925
925
|
);
|
|
926
926
|
CREATE INDEX IF NOT EXISTS idx_create_requests_expires ON create_requests(expires_at);
|
|
927
|
-
`)}o(Ht,"addCreateRequests");function Gt(e){for(const[r,_]of[["sub","TEXT"],["grant_id","TEXT"],["scopes_json","TEXT"],["origin_run_id","TEXT"],["run_execution_id","TEXT"]])S(e,"crons",r)||e.exec(`ALTER TABLE crons ADD COLUMN ${r} ${_}`);const t=[],n=o(r=>e.prepare("SELECT id, sub, resolved_scopes_json FROM grants WHERE grantee = ? ORDER BY CASE status WHEN 'active' THEN 0 ELSE 1 END, rev DESC LIMIT 1").get(r),"grantRow"),E=o(r=>e.prepare("SELECT grantee, sub, resolved_scopes_json FROM grants WHERE id = ?").get(r),"grantById"),T=o(r=>{try{const _=JSON.parse(r);return Array.isArray(_)?_.flatMap(d=>d&&d.mode==="always"&&typeof d.scope=="string"?[d.scope]:[]):[]}catch{return[]}},"snapshot"),i=o((r,_)=>{if(_===null)return null;const d=e.prepare("SELECT grantee, sub, grant_id, scopes_json, run_id FROM node_rows WHERE node_id = ?").get(_);if(d===void 0||d.grantee!==r)return null;const u=r==="app:terminal";let p=null;if(d.scopes_json!==null){let A;try{A=JSON.parse(d.scopes_json)}catch{return{pause:"bad_scopes"}}if(!Array.isArray(A)||!A.every(
|
|
927
|
+
`)}o(Ht,"addCreateRequests");function Gt(e){for(const[r,_]of[["sub","TEXT"],["grant_id","TEXT"],["scopes_json","TEXT"],["origin_run_id","TEXT"],["run_execution_id","TEXT"]])S(e,"crons",r)||e.exec(`ALTER TABLE crons ADD COLUMN ${r} ${_}`);const t=[],n=o(r=>e.prepare("SELECT id, sub, resolved_scopes_json FROM grants WHERE grantee = ? ORDER BY CASE status WHEN 'active' THEN 0 ELSE 1 END, rev DESC LIMIT 1").get(r),"grantRow"),E=o(r=>e.prepare("SELECT grantee, sub, resolved_scopes_json FROM grants WHERE id = ?").get(r),"grantById"),T=o(r=>{try{const _=JSON.parse(r);return Array.isArray(_)?_.flatMap(d=>d&&d.mode==="always"&&typeof d.scope=="string"?[d.scope]:[]):[]}catch{return[]}},"snapshot"),i=o((r,_)=>{if(_===null)return null;const d=e.prepare("SELECT grantee, sub, grant_id, scopes_json, run_id FROM node_rows WHERE node_id = ?").get(_);if(d===void 0||d.grantee!==r)return null;const u=r==="app:terminal";let p=null;if(d.scopes_json!==null){let A;try{A=JSON.parse(d.scopes_json)}catch{return{pause:"bad_scopes"}}if(!Array.isArray(A)||!A.every(m=>typeof m=="string"))return{pause:"bad_scopes"};p=A}const O=u?n(r):void 0;let R=d.grant_id??O?.id??null;R===null&&!u&&d.sub!==null&&(R=e.prepare("SELECT id FROM grants WHERE grantee = ? AND sub = ? ORDER BY CASE status WHEN 'active' THEN 0 ELSE 1 END, rev DESC LIMIT 1").get(r,d.sub)?.id??null);const s=R===null?void 0:E(R);return R!==null&&(s===void 0||s.grantee!==r||d.sub!==null&&s.sub!==d.sub)?{pause:"no_grant"}:R===null&&!u?{pause:"no_grant"}:(p===null&&!u&&(p=T(s.resolved_scopes_json)),{principal:{sub:d.sub??s?.sub??O?.sub??"user:local",grant_id:R,scopes:p,origin_run_id:d.run_id}})},"fromCreator"),a=o(r=>{const _=n(r);return r==="app:terminal"?{sub:_?.sub??"user:local",grant_id:_?.id??null,scopes:null,origin_run_id:null}:_===void 0?null:{sub:_.sub,grant_id:_.id,scopes:T(_.resolved_scopes_json),origin_run_id:null}},"fromGrantee"),c=e.prepare("SELECT cron_id, grantee, created_by, isolated, cwd FROM crons WHERE sub IS NULL").all(),L=e.prepare("UPDATE crons SET sub = ?, grant_id = ?, scopes_json = ?, origin_run_id = ? WHERE cron_id = ?"),l=new Map;for(const r of c){const _=i(r.grantee,r.created_by),d=_===null?a(r.grantee):"principal"in _?_.principal:null;if(d===null){const u=_!==null&&"pause"in _?_.pause:"no_grant";L.run("user:local",null,null,null,r.cron_id),e.prepare("UPDATE crons SET state = 'paused' WHERE cron_id = ?").run(r.cron_id),t.push({event:`cron.migrated.${u}`,fields:{cron_id:r.cron_id,grantee:r.grantee}})}else l.set(r.cron_id,d),L.run(d.sub,d.grant_id,d.scopes===null?null:JSON.stringify(d.scopes),d.origin_run_id,r.cron_id);if(r.isolated!==0){const u=j(Re(r.grantee).root,"crons",r.cron_id);e.prepare("UPDATE crons SET cwd = ? WHERE cron_id = ?").run(u,r.cron_id),t.push({event:"cron.migrated.isolated_cwd",fields:{cron_id:r.cron_id,old_cwd:r.cwd,cwd:u}})}}if(C(e,"daemon_actions")){const r=e.prepare("SELECT action_id, adapter_json FROM daemon_actions WHERE state IN ('pending', 'handed_off') AND json_extract(adapter_json, '$.adapter') = 'cron_spawn' AND json_extract(adapter_json, '$.principal') IS NULL").all();for(const _ of r){const d=JSON.parse(_.adapter_json),u=typeof d.cronId=="string"?d.cronId:null,p=u===null?void 0:e.prepare("SELECT grantee, sub, grant_id, scopes_json, origin_run_id, isolated, created_by FROM crons WHERE cron_id = ?").get(u);let O=null;if(p!==void 0&&!(p.grant_id===null&&p.grantee!=="app:terminal"))O={cron_id:u,grantee:p.grantee,sub:p.sub,grant_id:p.grant_id,scopes:p.scopes_json===null?null:JSON.parse(p.scopes_json),origin_run_id:p.origin_run_id,isolated:p.isolated!==0,creator:p.created_by};else if(p===void 0){const R=typeof d.parent=="string"?d.parent:null,s=R===null?void 0:e.prepare("SELECT grantee FROM node_rows WHERE node_id = ?").get(R),A=s===void 0?null:i(s.grantee,R),m=A===null||!("principal"in A)?null:A.principal;s!==void 0&&m!==null&&(O={cron_id:u,grantee:s.grantee,sub:m.sub,grant_id:m.grant_id,scopes:m.scopes,origin_run_id:m.origin_run_id,isolated:d.isolated===!0,creator:R})}if(O!==null)e.prepare("UPDATE daemon_actions SET adapter_json = ? WHERE action_id = ?").run(JSON.stringify({...d,principal:O}),_.action_id);else{const R=Date.now();e.prepare("UPDATE daemon_actions SET state = 'permanent_failed', failed_at = ?, updated_at = ?, claimed_at = NULL, claim_owner = NULL, next_attempt_at = NULL, last_failure_json = ? WHERE action_id = ?").run(R,R,JSON.stringify({kind:"grant_refused",message:"the cron has no derivable principal to authorize this birth"}),_.action_id),t.push({event:"cron.birth.refused",fields:{cron_id:u,action_id:_.action_id,reason:"grant_missing"}})}}}for(const{event:r,fields:_}of t)Ae({level:"warn",event:r,fields:_})}o(Gt,"addCronPrincipal");const F=[Ue,Ce,Se,De,ge,we,ye,he,ve,xe,Fe,Me,ke,J,je,J,He,Ge,be,Be,We,Qe,Ve,Ze,et,tt,nt,ot,Et,it,st,rt,Tt,at,ct,dt,_t,Nt,Lt,e=>{de(e),ce(e),Ne(e),Le(e)},ut,pt,lt,Rt,At,Ot,mt,Ut,ft,Xt,ee,te,ne,oe,Ct,St,Ee,Dt,Ie,gt,wt,yt,vt,kt,Mt,Ft,jt,xt,e=>e.exec(`
|
|
928
928
|
CREATE TABLE audit_rows (
|
|
929
929
|
id INTEGER PRIMARY KEY, request_id TEXT NOT NULL, time TEXT NOT NULL, grantee TEXT, sub TEXT,
|
|
930
930
|
client_id TEXT, class TEXT, operation TEXT NOT NULL, scope_checked TEXT,
|
|
@@ -986,7 +986,7 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
|
|
|
986
986
|
-- that registered a custom object fixes the run that object reads from.
|
|
987
987
|
CREATE TABLE deleted_isolated_nodes (node_id TEXT PRIMARY KEY, run_id TEXT NOT NULL);
|
|
988
988
|
ALTER TABLE custom_objects ADD COLUMN registered_by TEXT;
|
|
989
|
-
`),
|
|
989
|
+
`),It,Ht,e=>e.exec("ALTER TABLE grants ADD COLUMN dependencies_json TEXT NOT NULL DEFAULT '{}'"),e=>e.exec("ALTER TABLE grants ADD COLUMN run_notice_url TEXT"),e=>e.exec(`
|
|
990
990
|
CREATE TABLE usage (
|
|
991
991
|
grantee TEXT NOT NULL, month TEXT NOT NULL, payer TEXT NOT NULL, source TEXT NOT NULL, served_model TEXT NOT NULL,
|
|
992
992
|
input_tokens INTEGER NOT NULL DEFAULT 0, output_tokens INTEGER NOT NULL DEFAULT 0, cached_tokens INTEGER NOT NULL DEFAULT 0,
|
|
@@ -1031,4 +1031,13 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
|
|
|
1031
1031
|
UPDATE nodes SET run_id = NULL
|
|
1032
1032
|
WHERE run_id IS NOT NULL AND run_id != node_id AND isolated = 0
|
|
1033
1033
|
AND NOT EXISTS (SELECT 1 FROM nodes r WHERE r.node_id = nodes.run_id);
|
|
1034
|
-
`)
|
|
1034
|
+
`),e=>e.exec(`
|
|
1035
|
+
ALTER TABLE object_kinds ADD COLUMN read_json TEXT;
|
|
1036
|
+
ALTER TABLE object_kinds ADD COLUMN source TEXT NOT NULL DEFAULT 'register' CHECK (source IN ('register','manifest','peer'));
|
|
1037
|
+
ALTER TABLE object_kinds ADD COLUMN declared INTEGER NOT NULL DEFAULT 1;
|
|
1038
|
+
`),e=>e.exec(`
|
|
1039
|
+
CREATE TABLE peer_ingested_destinations (
|
|
1040
|
+
key TEXT PRIMARY KEY,
|
|
1041
|
+
ingested_at INTEGER NOT NULL
|
|
1042
|
+
);
|
|
1043
|
+
`),e=>e.exec("ALTER TABLE watches ADD COLUMN link_app INTEGER REFERENCES objects(rowid) ON DELETE CASCADE")];function B(e,t,n){const E=e.prepare("PRAGMA foreign_keys").get().foreign_keys;e.exec("PRAGMA foreign_keys = OFF;");try{e.exec("BEGIN IMMEDIATE");try{W(e)===t&&(n(),e.exec(`PRAGMA user_version = ${t+1}`)),e.exec("COMMIT")}catch(T){try{e.exec("ROLLBACK")}catch{}throw T}}finally{e.exec(`PRAGMA foreign_keys = ${E};`)}}o(B,"selfTransactionalMigration");const bt=new Set([3,61,63,66]);function W(e){return e.prepare("PRAGMA user_version").get().user_version}o(W,"currentUserVersion");function se(e,t){F[t](e),e.exec(`PRAGMA user_version = ${t+1}`)}o(se,"applyMigration");function En(e){const t=W(e);if(t>F.length)throw new Error(`unsupported canvas schema version ${t} (latest supported ${F.length})`);if(t===F.length)return;const n=e.prepare("PRAGMA busy_timeout").get().timeout;e.exec("PRAGMA busy_timeout = 60000;");try{Bt(e)}finally{e.exec(`PRAGMA busy_timeout = ${n};`)}}o(En,"migrate");function Bt(e){for(;;){const t=W(e);if(t>=F.length)return;if(bt.has(t)){if(e.isTransaction)throw new Error(`canvas migration v${t+1} requires autocommit`);F[t](e);continue}if(e.isTransaction){se(e,t);continue}e.exec("BEGIN IMMEDIATE");try{W(e)===t&&se(e,t),e.exec("COMMIT")}catch(n){try{e.exec("ROLLBACK")}catch{}throw n}}}o(Bt,"runPendingMigrations");export{F as MIGRATIONS,En as migrate};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ValidatedCommandManifest } from '../../api/command-manifest/manifest.js';
|
|
2
|
-
import type { KindConfig, MemoryExtensionDeclarations, PageComponentRegistration, PluginBundle, PluginManifest, Scope } from '../../types.js';
|
|
2
|
+
import type { KindConfig, MemoryExtensionDeclarations, ObjectKindDeclaration, PageComponentRegistration, PluginBundle, PluginManifest, Scope } from '../../types.js';
|
|
3
3
|
import { type BundleTransport } from './endpoint.js';
|
|
4
4
|
/** Who may call a capability provider's tool group (contract 3). */
|
|
5
5
|
export type ProviderGroupWho = 'own' | 'user' | 'allowlist' | 'any';
|
|
@@ -51,6 +51,9 @@ export interface ValidatedBundle {
|
|
|
51
51
|
/** Plugin-owned memory metadata declarations copied into the synthesized
|
|
52
52
|
* installed manifest alongside the archive's memory documents. */
|
|
53
53
|
memory_extensions?: MemoryExtensionDeclarations;
|
|
54
|
+
/** Canvas object kinds and scopes declared in `bundle.json` (optional), copied into the synthesized manifest. */
|
|
55
|
+
object_kinds?: Record<string, ObjectKindDeclaration>;
|
|
56
|
+
scopes?: string[];
|
|
54
57
|
commands: ValidatedCommandManifest;
|
|
55
58
|
commandsBytes: Uint8Array;
|
|
56
59
|
directories: ReadonlyArray<{
|