@north-light/crouter 0.3.356 → 0.3.358
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/command-manifest/manifest.d.ts +3 -0
- package/dist/api/command-manifest/manifest.js +1 -1
- package/dist/api/command-manifest/result.d.ts +11 -3
- package/dist/api/command-manifest/result.js +1 -1
- package/dist/api/command-manifest/schema.js +1 -1
- package/dist/api/plugin-manifest-schema.d.ts +31 -8
- package/dist/commands/pkg/browse/command-view.js +1 -1
- package/dist/core/command-plugins/bundle.d.ts +31 -0
- package/dist/core/command-plugins/bundle.js +1 -1
- package/dist/core/help.d.ts +6 -4
- package/dist/core/help.js +7 -7
- package/dist/core/providers/errors.d.ts +97 -0
- package/dist/core/providers/errors.js +1 -0
- package/dist/daemon/api/app-listener.js +12 -12
- package/dist/daemon/api/handlers/providers.d.ts +69 -0
- package/dist/daemon/api/handlers/providers.js +1 -0
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/server.js +1 -1
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +11 -11
|
@@ -6,6 +6,9 @@ export interface ValidatedCoreMount {
|
|
|
6
6
|
}
|
|
7
7
|
export interface ValidatedCommandManifest {
|
|
8
8
|
schemaVersion: 1;
|
|
9
|
+
/** The plugin's declared integer major (HTTP transport only); absent means
|
|
10
|
+
* undeclared, which a locally installed plugin treats as 1. */
|
|
11
|
+
version?: number;
|
|
9
12
|
baseUrl?: string;
|
|
10
13
|
timeouts?: ManifestTimeouts;
|
|
11
14
|
/** Top-level plugin branches or repository-fragment nodes, ready to mount. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var U=Object.defineProperty;var h=(e,n)=>U(e,"name",{value:n,configurable:!0});import{validateCommandNode as $}from"./schema.js";import{isRecord as _}from"../../shared/predicates.js";const k=/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/,L=new Set(["GET","POST","PUT","PATCH","DELETE"]);function f(e){return e===null?"null":Array.isArray(e)?"array":typeof e}h(f,"typeName");function K(e,n){const a=[],o=h((t,v,A,j,x,g)=>{a.push({code:t,message:v,received:A,expected:j,next:x,...g?{path:g}:{}})},"issue");if(!_(e))return o("command_manifest_invalid","manifest must be an object",f(e),n.extensionFragment===!0?"{ schemaVersion, transport, mounts }":n.transport==="http"?"{ schemaVersion, version?, baseUrl?, timeouts?, mounts, helpAddenda? }":"{ schemaVersion, mounts, helpAddenda? }","Provide a valid JSON manifest object."),{issues:a};const s=Object.keys(e),r=n.extensionFragment===!0?new Set(["schemaVersion","transport","mounts"]):n.transport==="http"?new Set(["schemaVersion","version","baseUrl","timeouts","mounts","helpAddenda"]):new Set(["schemaVersion","mounts","helpAddenda"]),l=s.filter(t=>!r.has(t));if(l.length>0)return o("command_manifest_invalid","unknown top-level keys",l.join(", "),n.extensionFragment===!0?"only: schemaVersion, transport, mounts":n.transport==="http"?"only: schemaVersion, version, baseUrl, timeouts, mounts, helpAddenda":"only: schemaVersion, mounts, helpAddenda","Remove the unknown keys."),{issues:a};if(e.schemaVersion!==1)return o("command_schema_version","schemaVersion must be exactly 1",String(e.schemaVersion),"1","Update the manifest schema version to 1.","schemaVersion"),{issues:a};let d;if(n.transport==="http"&&n.extensionFragment!==!0&&e.version!==void 0){const t=e.version;if(typeof t!="number"||!Number.isInteger(t)||t<1)return o("command_manifest_invalid","version must be an integer of at least 1",f(t)==="number"?String(t):f(t),"the plugin major version as an integer >= 1, or omitted","Set version to the plugin's integer major.","version"),{issues:a};d=t}let m;if(n.transport==="http"&&e.baseUrl!==void 0){if(typeof e.baseUrl!="string")return o("command_manifest_invalid","baseUrl must be a string",f(e.baseUrl),"an absolute HTTP(S) URL or omitted","Provide a valid baseUrl or remove it.","baseUrl"),{issues:a};try{const t=new URL(e.baseUrl);if(!["http:","https:"].includes(t.protocol))return o("command_manifest_invalid","baseUrl must be http: or https:",t.protocol,"http: or https:","Use an HTTP(S) URL.","baseUrl"),{issues:a};m=e.baseUrl}catch{return o("command_manifest_invalid","baseUrl must be a valid absolute URL",e.baseUrl,"a valid URL","Fix the baseUrl.","baseUrl"),{issues:a}}}let i;if(n.transport==="http"&&e.timeouts!==void 0){const t=M(e.timeouts,o);if(t===null)return{issues:a};i=t}let p;if(e.helpAddenda!==void 0){const t=F(e.helpAddenda,n.coreCommandPaths,o);if(t===null)return{issues:a};Object.keys(t).length>0&&(p=t)}const c=e.mounts;if(!Array.isArray(c))return o("command_manifest_invalid","mounts must be an array",f(c),"a non-empty array of { parent, node } contributions","Provide at least one mount.","mounts"),{issues:a};if(c.length===0)return o("command_manifest_invalid","mounts must be a non-empty array","empty array","at least one contribution","Add at least one mount.","mounts"),{issues:a};const b=[];for(let t=0;t<c.length;t++){const v=R(c[t],t,n.transport,o,n.extensionFragment===!0?{topLevelRootEntry:"forbidden",allowPassthrough:!1,allowExtensible:!1}:void 0);if(v===null)return{issues:a};b.push(v)}const u=b.filter(t=>t.parent.length>0&&n.extensibleCoreBranches?.has(t.parent.join(" "))===!0);for(const t of u){const v=t.parent.join(" ");if(n.coreCommandPaths?.has(`${v} ${t.node.name}`)===!0)return o("command_collision",`contributed command "${t.node.name}" conflicts with a child the extensible core branch already owns`,t.node.name,`a name not already owned by the ${v} core branch`,"Rename the contributed command or remove this mount.",`${t.parent.join(".")}.${t.node.name}`),{issues:a}}const y=S(b.filter(t=>!u.includes(t)),n.reservedCoreNames,n.extensibleCoreBranches,o);return y===null?{issues:a}:a.length===0?{manifest:{schemaVersion:1,...d!==void 0?{version:d}:{},...m!==void 0?{baseUrl:m}:{},...i!==void 0?{timeouts:i}:{},...p!==void 0?{helpAddenda:p}:{},roots:y,coreMounts:u},issues:[]}:{issues:a}}h(K,"validateCommandManifest");function M(e,n){if(!_(e))return n("command_manifest_invalid","timeouts must be an object",f(e),"{ connectMs?, requestMs?, streamIdleMs? }","Fix timeouts.","timeouts"),null;const a=new Set(["connectMs","requestMs","streamIdleMs"]),o=Object.keys(e).filter(r=>!a.has(r));if(o.length>0)return n("command_manifest_invalid","unknown timeouts keys",o.join(", "),"only: connectMs, requestMs, streamIdleMs","Remove the unknown keys.","timeouts"),null;const s={};for(const r of["connectMs","requestMs","streamIdleMs"])if(e[r]!==void 0){if(typeof e[r]!="number"||!Number.isInteger(e[r])||e[r]<=0)return n("command_manifest_invalid",`${r} must be a positive integer`,String(e[r]),"a positive integer",`Fix ${r}.`,`timeouts.${r}`),null;s[r]=e[r]}return s}h(M,"validateTimeouts");function F(e,n,a){if(!_(e))return a("command_manifest_invalid","helpAddenda must be an object",f(e),"a map of core command path \u2192 addendum text","Fix helpAddenda.","helpAddenda"),null;const o={};for(const[s,r]of Object.entries(e)){if(s.split(" ").some(l=>!k.test(l)))return a("command_help_addendum_invalid","helpAddenda key must be a space-separated core command path",s,'kebab tokens separated by single spaces (e.g. "cron" or "cron add")',"Fix the helpAddenda key.",`helpAddenda.${s}`),null;if(typeof r!="string"||r.length===0)return a("command_manifest_invalid","helpAddenda value must be a non-empty string",typeof r=="string"?"empty string":f(r),"non-empty addendum text","Fix the helpAddenda value.",`helpAddenda.${s}`),null;if(n!==void 0&&!n.has(s))return a("command_help_addendum_invalid","helpAddenda key names no crtr core command path",s,'an existing core command path (e.g. "cron", "cron add")',"Fix the key or remove the addendum.",`helpAddenda.${s}`),null;o[s]=r}return o}h(F,"validateHelpAddenda");function R(e,n,a,o,s){const r=`mounts[${n}]`;if(!_(e))return o("command_manifest_invalid","mount must be an object",f(e),"{ parent, node }","Fix the mount.",r),null;const l=new Set(["parent","node"]),d=Object.keys(e).filter(u=>!l.has(u));if(d.length>0)return o("command_manifest_invalid","unknown mount keys",d.join(", "),"only: parent, node","Remove the unknown keys.",r),null;const m=e.parent;if(!Array.isArray(m))return o("command_manifest_invalid","mount parent must be an array",f(m),"an array of kebab tokens, or [] for a top-level mount","Fix parent.",`${r}.parent`),null;for(let u=0;u<m.length;u++){const y=m[u];if(typeof y!="string"||!k.test(y))return o("command_manifest_invalid","parent token must be a kebab token",String(y),"a kebab-case identifier","Fix the parent path.",`${r}.parent[${u}]`),null}const i=m,p=e.node,c=i.length===0,b=$(p,[`${r}.node`],c,a,o,s);return b===null?null:{parent:i,node:b}}h(R,"validateMount");function S(e,n,a,o){const s=e.filter(i=>i.parent.length===0),r=e.filter(i=>i.parent.length>0);for(const i of s)if(n.has(i.node.name))return o("command_collision","node name conflicts with a crtr core command",i.node.name,`a name not in core (not one of: ${[...n].join(", ")})`,"Rename the node or remove this mount.",`${i.node.name}`),null;const l=s.map(i=>i.node.kind==="branch"?{...i.node,children:[...i.node.children]}:{...i.node});for(const i of r)if(n.has(i.parent[0]))return o("command_parent_invalid","parent path starts with a core branch that is not extensible",i.parent[0],`an extensible core branch (one of: ${[...a??[]].join(", ")})`,`Mount below one of the extensible core branches: ${[...a??[]].join(", ")}.`,i.parent.join(".")),null;for(const i of r)if(!V(l,i,o))return null;const d=new Set,m=h((i,p)=>{const c=p.join(".");return d.has(c)?(o("command_node_invalid","duplicate node path",c,"unique paths within the manifest","Remove the duplicate node.",c),!1):(d.add(c),i.kind==="branch"?i.children.every(b=>m(b,[...p,b.name])):!0)},"indexPaths");for(const i of l)if(!m(i,[i.name]))return null;return l}h(S,"materializeForest");function V(e,n,a){const[o,...s]=n.parent,r=e.find(d=>d.name===o);if(!r)return a("command_parent_invalid","parent path starts with unknown branch",o,"a branch this manifest contributes","Fix the parent path or add the parent branch.",n.parent.join(".")),!1;if(r.kind==="leaf")return a("command_parent_invalid","parent path resolves to a leaf",o,"a branch","Change the parent to point to a branch.",n.parent.join(".")),!1;let l=r;for(const d of s){const m=l.children.find(i=>i.name===d);if(!m)return a("command_parent_invalid","parent path contains unknown branch",d,"a branch this manifest contributes","Fix the parent path or add the intermediate branch.",n.parent.join(".")),!1;if(m.kind==="leaf")return a("command_parent_invalid","parent path resolves to a leaf",d,"a branch","Change the parent to point to a branch.",n.parent.join(".")),!1;l=m}return l.passthrough!==void 0?(a("command_parent_invalid","parent path resolves to a passthrough branch",n.parent.join("."),"a branch without passthrough","Remove the nested mount or remove passthrough from its parent branch.",n.parent.join(".")),!1):l.children.some(d=>d.name===n.node.name)?(a("command_node_invalid","duplicate child name",n.node.name,"unique child names within a branch","Rename the node or remove the duplicate.",`${n.parent.join(".")}.${n.node.name}`),!1):(l.children.push(n.node),!0)}h(V,"resolveAndAttachMount");export{K as validateCommandManifest};
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
export interface
|
|
2
|
-
name: string;
|
|
1
|
+
export interface DeclaredOutputShape {
|
|
3
2
|
type: string;
|
|
4
|
-
required: boolean;
|
|
5
3
|
constraint: string;
|
|
4
|
+
/** Named members of an `object` value. */
|
|
5
|
+
children?: DeclaredOutputField[];
|
|
6
|
+
/** Element shape of an `array` value. */
|
|
7
|
+
items?: DeclaredOutputShape;
|
|
8
|
+
/** Allowed values of an `enum` value. */
|
|
9
|
+
values?: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface DeclaredOutputField extends DeclaredOutputShape {
|
|
12
|
+
name: string;
|
|
13
|
+
required: boolean;
|
|
6
14
|
}
|
|
7
15
|
/** Validates a result object against declared output field descriptors.
|
|
8
16
|
* Returns null if valid; otherwise returns a { field, receivedType } error. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var c=Object.defineProperty;var i=(
|
|
1
|
+
var c=Object.defineProperty;var i=(n,e)=>c(n,"name",{value:e,configurable:!0});import{isRecord as u}from"../../shared/predicates.js";function g(n,e){return f(n,e,!0)}i(g,"validateDeclaredResult");function h(n,e){return f(n,e,!0)}i(h,"validateCoreDeclaredResult");function f(n,e,r){for(const t of n){const o=e[t.name],a=Object.prototype.hasOwnProperty.call(e,t.name)&&o!==void 0&&(r||o!==null);if(t.required&&!a)return{field:t.name};const s=r?p(t,o):y(t.type,o);if(a&&!s)return{field:t.name,receivedType:o===null?"null":typeof o}}return null}i(f,"validateFields");function y(n,e){const r=n.toLowerCase();return r==="string"||r==="path"?typeof e=="string":r==="int"||r==="integer"?typeof e=="number"&&Number.isInteger(e):r==="number"||r==="float"?typeof e=="number":r==="bool"||r==="boolean"?typeof e=="boolean":r==="array"||r.endsWith("[]")?Array.isArray(e):r==="object"?u(e):!0}i(y,"typeMatches");function p(n,e){const r=n.type.split("|").map(o=>o.trim()).filter(Boolean);return r.length===1&&!l(r[0])||!r.every(o=>l(o)||/^[a-z][a-z0-9_-]*$/.test(o))?!0:r.some(o=>m(o,e,n))}i(p,"coreTypeMatches");function l(n){const e=n.toLowerCase();return e==="null"||e==="string"||e==="path"||e==="markdown"||e==="int"||e==="integer"||e==="number"||e==="float"||e==="bool"||e==="boolean"||e==="array"||e.endsWith("[]")||e==="object"||e==="enum"||e==="file"}i(l,"knownType");function m(n,e,r){const t=n.toLowerCase();return t==="null"?e===null:t==="enum"?r.values===void 0||typeof e=="string"&&r.values.includes(e):t==="file"?u(e)||typeof e=="string":t==="string"||t==="path"||t==="markdown"?typeof e=="string":t==="int"||t==="integer"?typeof e=="number"&&Number.isInteger(e):t==="number"||t==="float"?typeof e=="number":t==="bool"||t==="boolean"?typeof e=="boolean":t==="array"||t.endsWith("[]")?Array.isArray(e):t==="object"?u(e):typeof e=="string"&&e===n}i(m,"atomMatches");export{h as validateCoreDeclaredResult,g as validateDeclaredResult};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var z=Object.defineProperty;var v=(e,o)=>z(e,"name",{value:o,configurable:!0});import{isRecord as x}from"../../shared/predicates.js";const B=new Set(["normal","common","important"]),I=new Set(["string","int","bool","path","enum"]),U=/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/,N=new Set(["kind","name","description","whenToUse","tier","rootEntry","extensible","summary","model","children"]),V=new Set([...N,"passthrough"]),L=new Set(["bin","installHint"]),O=new Set(["kind","name","description","whenToUse","tier","summary","params","output","effects"]),Z=new Set([...O,"outputKind"]),W=new Set([...O,"rest"]),X=new Set(["concept","description","whenToUse"]),J=new Set(["text","base64"]),C={positional:new Set(["kind","name","type","required","constraint","repeatable","encoding","defaultFromEnv"]),flag:new Set(["kind","name","type","choices","required","constraint","default","repeatable","encoding","defaultFromEnv"]),stdin:new Set(["kind","name","required","constraint"]),"context-file":new Set(["kind","name","required","constraint","shape"])},Q=new Set(["name","type","required","constraint"]),w=/^[A-Z][A-Z0-9_]*$/;function m(e){return e===null?"null":Array.isArray(e)?"array":typeof e}v(m,"typeName");function R(e,o,t,n){const a=Object.keys(e).filter(l=>!o.has(l));return a.length>0?(n("command_node_invalid","unknown keys",a.join(", "),`only: ${[...o].join(", ")}`,"Remove the unknown keys.",t),!1):!0}v(R,"checkKeys");function ee(e,o,t){if(!x(e))return t("command_node_invalid","rootEntry must be an object",m(e),"a { concept, description, whenToUse } object","Fix rootEntry.",o),null;if(!R(e,X,o,t))return null;for(const n of["concept","description","whenToUse"])if(typeof e[n]!="string"||e[n].length===0)return t("command_node_invalid",`rootEntry.${n} must be a non-empty string`,m(e[n]),"a non-empty string",`Add rootEntry.${n}.`,`${o}.${n}`),null;return{concept:e.concept,description:e.description,whenToUse:e.whenToUse}}v(ee,"validateRootEntry");function ne(e,o,t){if(e===void 0)return[];if(!Array.isArray(e))return t("command_node_invalid","params must be an array",m(e),"an array of params (or omitted)","Fix params.",o),null;const n=[],a=new Set;let l=0;for(let s=0;s<e.length;s++){const r=e[s],d=`${o}[${s}]`;if(!x(r))return t("command_node_invalid","param must be an object",m(r),"a param object","Fix the param.",d),null;const g=r.kind;if(typeof g!="string"||!(g in C))return t("command_node_invalid","param kind must be positional|flag|stdin|context-file",String(g),"positional | flag | stdin | context-file","Fix the param kind.",`${d}.kind`),null;if(!R(r,C[g],d,t))return null;const y=r.name;if(typeof y!="string"||!U.test(y))return t("command_node_invalid","param name must be a kebab-case token",String(y),"a kebab-case token","Rename the param.",`${d}.name`),null;if(a.has(y))return t("command_node_invalid","duplicate param name",y,"unique param names","Rename the duplicate param.",`${d}.name`),null;if(a.add(y),typeof r.required!="boolean")return t("command_node_invalid","param required must be a boolean",m(r.required),"true | false","Set required.",`${d}.required`),null;if(typeof r.constraint!="string")return t("command_node_invalid","param constraint must be a string",m(r.constraint),"an inline constraint string","Add a constraint.",`${d}.constraint`),null;if(g==="positional"){if(l++,l>1)return t("command_node_invalid","a leaf may declare at most one positional","multiple positionals","at most one positional param","Convert extra positionals to flags.",d),null;if(r.type!==void 0&&r.type!=="string"&&r.type!=="path")return t("command_node_invalid","positional type must be string|path",String(r.type),"string | path | omitted","Fix the positional type.",`${d}.type`),null;let h=!1;if(r.repeatable!==void 0){if(typeof r.repeatable!="boolean")return t("command_node_invalid","repeatable must be a boolean",m(r.repeatable),"true | false | omitted","Fix repeatable.",`${d}.repeatable`),null;h=r.repeatable}const _=H(r,d,t);if(_===null)return null;const c=M(r,d,t);if(c===null)return null;if(h&&c!==void 0)return t("command_node_invalid","repeatable positional must not declare defaultFromEnv",y,"no defaultFromEnv on a repeatable positional (values accumulate into an array)","Remove defaultFromEnv, or drop repeatable.",`${d}.defaultFromEnv`),null;n.push({kind:"positional",name:y,...r.type!==void 0?{type:r.type}:{},required:r.required,constraint:r.constraint,...h?{repeatable:!0}:{},..._!==void 0?{encoding:_}:{},...c!==void 0?{defaultFromEnv:c}:{}})}else if(g==="stdin")n.push({kind:"stdin",name:y,required:r.required,constraint:r.constraint});else if(g==="context-file")n.push({kind:"context-file",name:y,required:r.required,constraint:r.constraint,...typeof r.shape=="string"?{shape:r.shape}:{}});else if(g==="flag"){const h=r.type;if(typeof h!="string"||!I.has(h))return t("command_node_invalid","flag type must be string|int|bool|path|enum",String(h),"string | int | bool | path | enum","Fix the flag type.",`${d}.type`),null;let _;if(h==="enum"){const f=r.choices;if(!Array.isArray(f)||f.length===0||!f.every(k=>typeof k=="string"))return t("command_node_invalid","enum flag requires a non-empty string choices array",m(f),"a non-empty array of strings","Declare choices for the enum flag.",`${d}.choices`),null;_=f}else if(r.choices!==void 0)return t("command_node_invalid","choices is only valid on an enum flag","choices","no choices unless type is enum","Remove choices.",`${d}.choices`),null;let c=!1;if(r.repeatable!==void 0){if(typeof r.repeatable!="boolean")return t("command_node_invalid","repeatable must be a boolean",m(r.repeatable),"true | false | omitted","Fix repeatable.",`${d}.repeatable`),null;c=r.repeatable}if(c&&(h==="bool"||h==="path"))return t("command_node_invalid",`repeatable is not valid on a ${h} flag`,`${y}: ${h}`,"repeatable only on string | int | enum flags","Remove repeatable or change the flag type.",`${d}.repeatable`),null;if(c&&r.default!==void 0)return t("command_node_invalid","repeatable flag must not declare a default",y,"no default on a repeatable flag (values accumulate into an array)","Remove the default.",`${d}.default`),null;const $=H(r,d,t);if($===null)return null;const E=M(r,d,t);if(E===null)return null;if(c&&E!==void 0)return t("command_node_invalid","repeatable flag must not declare defaultFromEnv",y,"no defaultFromEnv on a repeatable flag (values accumulate into an array)","Remove defaultFromEnv, or drop repeatable.",`${d}.defaultFromEnv`),null;n.push({kind:"flag",name:y,type:h,..._!==void 0?{choices:_}:{},...$!==void 0?{encoding:$}:{},required:r.required,constraint:r.constraint,...r.default!==void 0?{default:r.default}:{},...c?{repeatable:!0}:{},...E!==void 0?{defaultFromEnv:E}:{}})}}return n}v(ne,"validateParams");function H(e,o,t){const n=e.encoding;if(n!==void 0)return typeof n!="string"||!J.has(n)?(t("command_node_invalid","param encoding must be text|base64",String(n),"text | base64 | omitted","Fix the encoding.",`${o}.encoding`),null):e.type!=="path"?(t("command_node_invalid",'encoding is only valid on a type:"path" param',`type ${String(e.type)}`,'type: "path"','Set the param type to "path", or remove encoding.',`${o}.encoding`),null):n}v(H,"validateFileEncoding");function M(e,o,t){const n=e.defaultFromEnv;if(n===void 0)return;if(typeof n!="string"||!w.test(n))return t("command_node_invalid","defaultFromEnv must name an environment variable in UPPER_SNAKE_CASE",String(n),'e.g. "CRTR_NODE_ID"',"Fix the environment variable name.",`${o}.defaultFromEnv`),null;const a=e.type===void 0&&e.kind==="positional"?"string":e.type;return a!=="string"&&a!=="path"?(t("command_node_invalid","defaultFromEnv is only valid on a string|path param",`type ${String(e.type)}`,'type: "string" | "path"',"Change the param type, or remove defaultFromEnv.",`${o}.defaultFromEnv`),null):e.default!==void 0?(t("command_node_invalid","a param must not declare both default and defaultFromEnv",`${String(e.name)}: default + defaultFromEnv`,"exactly one default source","Remove one \u2014 defaultFromEnv counts as supplied, a static default does not.",`${o}.defaultFromEnv`),null):n}v(M,"validateDefaultFromEnv");function te(e,o,t){if(!Array.isArray(e))return t("command_node_invalid","output must be an array",m(e),"an array of output fields","Declare the leaf output fields.",o),null;const n=[];for(let a=0;a<e.length;a++){const l=e[a],s=`${o}[${a}]`;if(!x(l))return t("command_node_invalid","output field must be an object",m(l),"a { name, type, required, constraint } object","Fix the output field.",s),null;if(!R(l,Q,s,t))return null;if(typeof l.name!="string"||l.name.length===0)return t("command_node_invalid","output field name must be a non-empty string",m(l.name),"a field name","Add a name.",`${s}.name`),null;if(typeof l.type!="string"||l.type.length===0)return t("command_node_invalid","output field type must be a non-empty string",m(l.type),"a type name","Add a type.",`${s}.type`),null;if(typeof l.required!="boolean")return t("command_node_invalid","output field required must be a boolean",m(l.required),"true | false","Set required.",`${s}.required`),null;if(typeof l.constraint!="string")return t("command_node_invalid","output field constraint must be a string",m(l.constraint),"a constraint string","Add a constraint.",`${s}.constraint`),null;n.push({name:l.name,type:l.type,required:l.required,constraint:l.constraint})}return n}v(te,"validateOutput");function G(e,o,t){const n=e.name;return typeof n!="string"||!U.test(n)?(t("command_node_invalid","node name must be a lowercase kebab-case token",String(n),'a kebab-case token like "app" or "list-all"',"Rename the node.",`${o}.name`),!1):typeof e.description!="string"||e.description.length===0?(t("command_node_invalid","node description must be a non-empty string",m(e.description),"a short parent-listing description","Add a description.",`${o}.description`),!1):typeof e.whenToUse!="string"||e.whenToUse.length===0?(t("command_node_invalid","node whenToUse must be a non-empty string",m(e.whenToUse),"a parent selection rubric","Add a whenToUse rubric.",`${o}.whenToUse`),!1):e.tier!==void 0&&!B.has(String(e.tier))?(t("command_node_invalid","node tier must be normal|common|important",String(e.tier),"normal | common | important","Fix or omit tier (external plugins cannot declare hidden commands).",`${o}.tier`),!1):!0}v(G,"checkCommon");function ae(e,o,t,n,a,l={}){const s=o.join(".");if(!x(e))return a("command_node_invalid","node must be an object",m(e),"a branch or leaf node object","Fix the node.",s),null;const r=e.kind;return r==="branch"?oe(e,s,t,n,a,l):r==="leaf"?t&&l.topLevelRootEntry!=="forbidden"?(a("command_node_invalid","top-level node must be a branch","leaf","a branch node with a rootEntry","Wrap the command in a top-level branch (noun) with a rootEntry.",`${s}.kind`),null):ie(e,s,n,a):(a("command_node_invalid",'node kind must be "branch" or "leaf"',String(r),"branch | leaf","Set kind to branch or leaf.",`${s}.kind`),null)}v(ae,"validateCommandNode");function oe(e,o,t,n,a,l){const s=l.allowPassthrough!==!1;if(!R(e,n==="exec"&&s?V:N,o,a)||!G(e,o,a))return null;const d=e.rootEntry!==void 0,g=l.topLevelRootEntry??"required";if(t&&g==="required"&&!d)return a("command_node_invalid","top-level branch requires a rootEntry","(missing)","a rootEntry { concept, description, whenToUse }","Add a rootEntry to the top-level branch.",`${o}.rootEntry`),null;if((!t||g==="forbidden")&&d)return a("command_node_invalid",t?"repository fragment root must not declare a rootEntry":"nested branch must not declare a rootEntry","rootEntry",t?"no rootEntry (the owning plugin branch already has one)":"no rootEntry (only top-level branches carry one)",t?"Remove rootEntry from the repository fragment root.":"Remove rootEntry from the nested branch.",`${o}.rootEntry`),null;const y=e.extensible!==void 0;if(y&&(!t||l.allowExtensible===!1||e.extensible!==!0))return a("command_node_invalid",t?"extensible must be exactly true on a plugin top-level branch":"extensible is valid only on a top-level branch mount",String(e.extensible),t?"extensible: true":"no extensible marker on nested branches",t?"Set extensible to true or remove it.":"Move the marker to the plugin top-level branch or remove it.",`${o}.extensible`),null;let h;if(d){const f=ee(e.rootEntry,`${o}.rootEntry`,a);if(f===null)return null;h=f}if(typeof e.summary!="string"||e.summary.length===0)return a("command_node_invalid","branch summary must be a non-empty string",m(e.summary),"a one-line summary string","Add a summary.",`${o}.summary`),null;if(e.model!==void 0&&typeof e.model!="string")return a("command_node_invalid","branch model must be a string",m(e.model),"a string or omitted","Fix or remove model.",`${o}.model`),null;let _;if(n==="exec"&&e.passthrough!==void 0){const f=re(e.passthrough,`${o}.passthrough`,a);if(f===null)return null;_=f}if(y&&_!==void 0)return a("command_node_invalid","an extensible branch cannot declare passthrough","extensible and passthrough","one branch mode: extensible or passthrough","Remove passthrough or remove extensible.",o),null;const c=e.children;if(_!==void 0){if(!Array.isArray(c)||c.length!==0)return a("command_node_invalid","a passthrough branch must declare children: []",Array.isArray(c)?`${c.length} children`:m(c),"an empty children array","Set children to [] \u2014 a passthrough branch forwards all argv and can own no subcommands.",`${o}.children`),null}else if(!Array.isArray(c))return a("command_node_invalid","branch children must be an array",m(c),"an array of branch/leaf nodes (may be empty for populated by mounts)","Fix children.",`${o}.children`),null;const $=[],E=new Set;for(let f=0;f<c.length;f++){const k=ae(c[f],[...o.split("."),`children[${f}]`],!1,n,a,l);if(k===null)return null;if(E.has(k.name))return a("command_node_invalid","duplicate child name",k.name,"unique child names within a branch","Rename the duplicate child.",`${o}.children[${f}].name`),null;E.add(k.name),$.push(k)}return{kind:"branch",name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...h!==void 0?{rootEntry:h}:{},...y?{extensible:!0}:{},summary:e.summary,...e.model!==void 0?{model:e.model}:{},..._!==void 0?{passthrough:_}:{},children:$}}v(oe,"validateBranch");function re(e,o,t){if(!x(e))return t("command_node_invalid","passthrough must be an object",m(e),"an object { bin, installHint }","Fix or remove passthrough.",o),null;if(!R(e,L,o,t))return null;for(const n of["bin","installHint"])if(typeof e[n]!="string"||e[n].length===0)return t("command_node_invalid",`passthrough ${n} must be a non-empty string`,m(e[n]),n==="bin"?"the external binary name":"a one-line install hint",`Set passthrough.${n}.`,`${o}.${n}`),null;return{bin:e.bin,installHint:e.installHint}}v(re,"validatePassthrough");function ie(e,o,t,n){if(!R(e,t==="exec"?Z:W,o,n)||!G(e,o,n))return null;if(typeof e.summary!="string"||e.summary.length===0)return n("command_node_invalid","leaf summary must be a non-empty string",m(e.summary),"a one-line summary string","Add a summary.",`${o}.summary`),null;if(t==="exec"&&e.outputKind!=="object")return n("command_node_invalid",'leaf outputKind must be "object" in v1',String(e.outputKind),'exactly "object"','Set outputKind to "object".',`${o}.outputKind`),null;if(t==="http"&&e.rest===void 0)return n("command_node_invalid","HTTP leaf requires a rest mapping","(missing)","a rest object with method, path, and params","Add a rest mapping.",`${o}.rest`),null;const a=ne(e.params,`${o}.params`,n);if(a===null)return null;const l=te(e.output,`${o}.output`,n);if(l===null)return null;const s=e.effects;if(!Array.isArray(s)||s.length===0||!s.every(g=>typeof g=="string"))return n("command_node_invalid","leaf effects must be a non-empty string array",m(s),'a non-empty array of strings (["None. Read-only."] for read-only leaves)',"Declare every persistent effect.",`${o}.effects`),null;const r={kind:"leaf",name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},summary:e.summary,params:a,output:l,effects:s};if(t==="exec")return{...r,outputKind:"object"};const d=se(e.rest,o,a,n);return d===null?null:{...r,rest:d}}v(ie,"validateLeaf");const le=new Set(["GET","POST","PUT","PATCH","DELETE"]),de=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/,me=new Set(["path","query","body","header"]);function se(e,o,t,n){const a=`${o}.rest`;if(!x(e))return n("command_rest_invalid","rest must be an object",m(e),"{ method, path, streaming?, params }","Provide a valid rest mapping.",a),null;const l=new Set(["method","path","streaming","body","bodyRoot","params"]),s=Object.keys(e).filter(i=>!l.has(i));if(s.length>0)return n("command_rest_invalid","unknown rest keys",s.join(", "),"only: method, path, streaming, body, bodyRoot, params","Remove the unknown keys.",a),null;const r=e.method;if(typeof r!="string"||!le.has(r))return n("command_rest_invalid","method must be GET|POST|PUT|PATCH|DELETE",String(r),"one of the HTTP methods","Set method to a valid HTTP verb.",`${a}.method`),null;const d=e.path;if(typeof d!="string"||!d.startsWith("/"))return n("command_rest_invalid","path must be an absolute path starting with /",String(d),'an absolute path like "/v1/devices/{id}"',"Fix the path.",`${a}.path`),null;const g=new Set,y=/\{([a-z][a-z0-9]*(?:-[a-z0-9]+)*)\}/g;let h;for(;(h=y.exec(d))!==null;)g.add(h[1]);let _=!1;if(e.streaming!==void 0){if(typeof e.streaming!="boolean")return n("command_rest_invalid","streaming must be a boolean",m(e.streaming),"true | false | omitted","Fix streaming.",`${a}.streaming`),null;_=e.streaming}const c=e.params;if(!x(c))return n("command_rest_invalid","params must be an object",m(c),"a mapping of param names to placement specs","Provide params.",`${a}.params`),null;const $=new Map;for(const i of t)$.set(i.name,i);const E={},f=[],k=new Set;for(const[i,b]of Object.entries(c)){if(!$.has(i))return n("command_rest_invalid","rest params key has no matching declared param",i,"a name from the leaf params array","Remove this rest params entry or declare the param.",`${a}.params.${i}`),null;if(k.add(i),!x(b))return n("command_rest_invalid","param mapping must be an object",m(b),"{ in, as? }","Fix the param mapping.",`${a}.params.${i}`),null;const S=new Set(["in","as"]),T=Object.keys(b).filter(F=>!S.has(F));if(T.length>0)return n("command_rest_invalid","unknown param mapping keys",T.join(", "),"only: in, as","Remove the unknown keys.",`${a}.params.${i}`),null;const u=b.in;if(typeof u!="string"||!me.has(u))return n("command_rest_invalid","param placement must be path|query|body|header",String(u),"path | query | body | header","Set in to a valid placement.",`${a}.params.${i}.in`),null;const p=b.as;if(p!==void 0&&(typeof p!="string"||p.length===0))return n("command_rest_invalid","as must be a non-empty string",String(p),"a non-empty string","Fix the as value.",`${a}.params.${i}.as`),null;if(u==="header"){if(typeof p!="string"||p.length===0)return n("command_rest_invalid","header placement requires a non-empty as",String(p),"a header name","Provide an as value for the header name.",`${a}.params.${i}.as`),null;if(!de.test(p))return n("command_rest_invalid",'header "as" must be a legal HTTP header field name',p,"an HTTP token (letters, digits, and !#$%&'*+-.^_`|~)","Fix the header name.",`${a}.params.${i}.as`),null;if(p.toLowerCase()==="crtr-request-id")return n("command_rest_invalid","Crtr-Request-Id is assigned by crtr for each leaf call",p,"a header name other than Crtr-Request-Id","Remove this header mapping.",`${a}.params.${i}.as`),null}else if(u==="path"){if(p!==void 0)return n("command_rest_invalid","path placement must not have as","as","no as (placeholder name governs)","Remove as.",`${a}.params.${i}.as`),null;const F=$.get(i);if(F.required!==!0)return n("command_rest_invalid",'in:"path" param must be declared required',`${i}: required ${String(F.required)}`,"required: true",`Set "required": true on the "${i}" param.`,`${a}.params.${i}`),null}const K=$.get(i);if((K.kind==="flag"||K.kind==="positional")&&K.repeatable===!0&&u!=="body")return n("command_rest_invalid","repeatable param must use body placement",`${i}: in "${u}"`,'in: "body" (array values ship as a JSON array)',`Map "${i}" to body placement.`,`${a}.params.${i}.in`),null;if(u==="body"){const D=$.get(i).kind==="stdin",Y=p||i;f.push({name:i,isStdin:D,as:Y})}E[i]={in:u,...p!==void 0?{as:p}:{}}}for(const i of t)if(!k.has(i.name))return n("command_rest_invalid","declared param not in rest mapping",i.name,"an entry in rest.params","Add a rest mapping for this param.",`${a}.params`),null;const A=new Set;for(const[i,b]of Object.entries(E))b.in==="path"&&A.add(i);if(A.size!==g.size||![...g].every(i=>A.has(i)))return n("command_rest_invalid","mismatch between path placeholders and path params",`placeholders: ${[...g].join(", ")} | params: ${[...A].join(", ")}`,'exact bijection between {placeholders} and in:"path" params',"Ensure every placeholder has a matching path param, and vice versa.",`${a}.path and ${a}.params`),null;if(r==="GET"&&f.length>0)return n("command_rest_invalid","GET method cannot have body params",f.map(i=>i.name).join(", "),"no body placement for GET","Move body params to query, or change the method.",`${a}.method or ${a}.params`),null;let j;if(e.body!==void 0){if(r==="GET")return n("command_rest_invalid","GET method cannot have body constants","body","no body for GET","Remove body, or change the method.",`${a}.body`),null;const i=e.body;if(!x(i))return n("command_rest_invalid","body must be an object",m(i),"a map of constant literal body fields (string | number | boolean)","Fix body.",`${a}.body`),null;const b={};for(const[u,p]of Object.entries(i)){if(typeof p!="string"&&typeof p!="number"&&typeof p!="boolean")return n("command_rest_invalid",`body.${u} must be a string, number, or boolean`,m(p),"string | number | boolean",`Fix body.${u}.`,`${a}.body.${u}`),null;b[u]=p}const S=new Set(f.map(u=>u.as)),T=Object.keys(b).filter(u=>S.has(u));if(T.length>0)return n("command_rest_invalid","body constant key collides with a param's body key",T.join(", "),"body constant keys distinct from every param's body key (its as or name)","Rename the colliding body constant or param.",`${a}.body`),null;j=b}let P;if(e.bodyRoot!==void 0){if(r==="GET")return n("command_rest_invalid","GET method cannot have bodyRoot","bodyRoot","no bodyRoot for GET","Remove bodyRoot, or change the method.",`${a}.bodyRoot`),null;const i=e.bodyRoot;if(typeof i!="string"||i.length===0)return n("command_rest_invalid","bodyRoot must be a non-empty string",m(i),"a non-empty string key","Fix bodyRoot.",`${a}.bodyRoot`),null;P=i}for(const[i,b]of Object.entries(E)){const S=$.get(i);if((S.kind==="context-file"||(S.kind==="positional"||S.kind==="flag")&&S.type==="path")&&b.in!=="body"){const u=S.kind==="context-file"?"context-file":`path ${S.kind}`;return n("command_rest_invalid",`file param (${u}) must be placed in body`,`${i}: in "${b.in}"`,'in: "body"',"Move the file param to body.",`${a}.params.${i}.in`),null}}const q=new Map;for(const i of f)q.has(i.as)||q.set(i.as,[]),q.get(i.as).push({name:i.name,isStdin:i.isStdin});for(const[i,b]of q.entries())if(b.length>1){const S=b.filter(u=>u.isStdin).length,T=b.filter(u=>!u.isStdin).length;if(!(S===1&&T===1))return n("command_rest_invalid","invalid body as grouping",b.map(u=>u.name).join(", "),"exactly one stdin param and one non-stdin param may share a body as (for coalesced sources)","Fix the body grouping.",`${a}.params (as: "${i}")`),null}return{method:r,path:d,..._!==!1?{streaming:_}:{},...j!==void 0?{body:j}:{},...P!==void 0?{bodyRoot:P}:{},params:E}}v(se,"validateRestMapping");export{ae as validateCommandNode};
|
|
1
|
+
var V=Object.defineProperty;var _=(e,t)=>V(e,"name",{value:t,configurable:!0});import{isRecord as x}from"../../shared/predicates.js";const Z=new Set(["normal","common","important"]),W=new Set(["string","int","bool","path","enum","file"]),X=/^[A-Za-z0-9_-]{1,64}$/,O=/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/,U=new Set(["kind","name","description","whenToUse","tier","rootEntry","extensible","summary","model","children"]),J=new Set([...U,"passthrough"]),L=new Set(["bin","installHint"]),N=new Set(["kind","name","description","whenToUse","tier","summary","params","output","effects"]),Q=new Set([...N,"outputKind"]),w=new Set([...N,"rest","group"]),ee=new Set(["concept","description","whenToUse"]),ne=new Set(["text","base64"]),C={positional:new Set(["kind","name","type","required","constraint","repeatable","encoding","defaultFromEnv"]),flag:new Set(["kind","name","type","choices","required","constraint","default","repeatable","encoding","defaultFromEnv"]),stdin:new Set(["kind","name","required","constraint"]),"context-file":new Set(["kind","name","required","constraint","shape"])},te=new Set(["name","type","required","constraint","children","items","values"]),ae=new Set(["type","constraint","children","items","values"]),oe=/^[A-Z][A-Z0-9_]*$/;function m(e){return e===null?"null":Array.isArray(e)?"array":typeof e}_(m,"typeName");function T(e,t,a,n){const o=Object.keys(e).filter(p=>!t.has(p));return o.length>0?(n("command_node_invalid","unknown keys",o.join(", "),`only: ${[...t].join(", ")}`,"Remove the unknown keys.",a),!1):!0}_(T,"checkKeys");function re(e,t,a){if(!x(e))return a("command_node_invalid","rootEntry must be an object",m(e),"a { concept, description, whenToUse } object","Fix rootEntry.",t),null;if(!T(e,ee,t,a))return null;for(const n of["concept","description","whenToUse"])if(typeof e[n]!="string"||e[n].length===0)return a("command_node_invalid",`rootEntry.${n} must be a non-empty string`,m(e[n]),"a non-empty string",`Add rootEntry.${n}.`,`${t}.${n}`),null;return{concept:e.concept,description:e.description,whenToUse:e.whenToUse}}_(re,"validateRootEntry");function ie(e,t,a){if(e===void 0)return[];if(!Array.isArray(e))return a("command_node_invalid","params must be an array",m(e),"an array of params (or omitted)","Fix params.",t),null;const n=[],o=new Set;let p=0;for(let c=0;c<e.length;c++){const r=e[c],i=`${t}[${c}]`;if(!x(r))return a("command_node_invalid","param must be an object",m(r),"a param object","Fix the param.",i),null;const u=r.kind;if(typeof u!="string"||!(u in C))return a("command_node_invalid","param kind must be positional|flag|stdin|context-file",String(u),"positional | flag | stdin | context-file","Fix the param kind.",`${i}.kind`),null;if(!T(r,C[u],i,a))return null;const d=r.name;if(typeof d!="string"||!O.test(d))return a("command_node_invalid","param name must be a kebab-case token",String(d),"a kebab-case token","Rename the param.",`${i}.name`),null;if(o.has(d))return a("command_node_invalid","duplicate param name",d,"unique param names","Rename the duplicate param.",`${i}.name`),null;if(o.add(d),typeof r.required!="boolean")return a("command_node_invalid","param required must be a boolean",m(r.required),"true | false","Set required.",`${i}.required`),null;if(typeof r.constraint!="string")return a("command_node_invalid","param constraint must be a string",m(r.constraint),"an inline constraint string","Add a constraint.",`${i}.constraint`),null;if(u==="positional"){if(p++,p>1)return a("command_node_invalid","a leaf may declare at most one positional","multiple positionals","at most one positional param","Convert extra positionals to flags.",i),null;if(r.type!==void 0&&r.type!=="string"&&r.type!=="path"&&r.type!=="file")return a("command_node_invalid","positional type must be string|path|file",String(r.type),"string | path | file | omitted","Fix the positional type.",`${i}.type`),null;let s=!1;if(r.repeatable!==void 0){if(typeof r.repeatable!="boolean")return a("command_node_invalid","repeatable must be a boolean",m(r.repeatable),"true | false | omitted","Fix repeatable.",`${i}.repeatable`),null;s=r.repeatable}if(s&&r.type==="file")return a("command_node_invalid","repeatable is not valid on a file positional",`${d}: file`,"one file per file param","Remove repeatable, or declare one file param per file.",`${i}.repeatable`),null;const g=H(r,i,a);if(g===null)return null;const y=M(r,i,a);if(y===null)return null;if(s&&y!==void 0)return a("command_node_invalid","repeatable positional must not declare defaultFromEnv",d,"no defaultFromEnv on a repeatable positional (values accumulate into an array)","Remove defaultFromEnv, or drop repeatable.",`${i}.defaultFromEnv`),null;n.push({kind:"positional",name:d,...r.type!==void 0?{type:r.type}:{},required:r.required,constraint:r.constraint,...s?{repeatable:!0}:{},...g!==void 0?{encoding:g}:{},...y!==void 0?{defaultFromEnv:y}:{}})}else if(u==="stdin")n.push({kind:"stdin",name:d,required:r.required,constraint:r.constraint});else if(u==="context-file")n.push({kind:"context-file",name:d,required:r.required,constraint:r.constraint,...typeof r.shape=="string"?{shape:r.shape}:{}});else if(u==="flag"){const s=r.type;if(typeof s!="string"||!W.has(s))return a("command_node_invalid","flag type must be string|int|bool|path|enum|file",String(s),"string | int | bool | path | enum | file","Fix the flag type.",`${i}.type`),null;let g;if(s==="enum"){const h=r.choices;if(!Array.isArray(h)||h.length===0||!h.every(k=>typeof k=="string"))return a("command_node_invalid","enum flag requires a non-empty string choices array",m(h),"a non-empty array of strings","Declare choices for the enum flag.",`${i}.choices`),null;g=h}else if(r.choices!==void 0)return a("command_node_invalid","choices is only valid on an enum flag","choices","no choices unless type is enum","Remove choices.",`${i}.choices`),null;let y=!1;if(r.repeatable!==void 0){if(typeof r.repeatable!="boolean")return a("command_node_invalid","repeatable must be a boolean",m(r.repeatable),"true | false | omitted","Fix repeatable.",`${i}.repeatable`),null;y=r.repeatable}if(y&&(s==="bool"||s==="path"||s==="file"))return a("command_node_invalid",`repeatable is not valid on a ${s} flag`,`${d}: ${s}`,"repeatable only on string | int | enum flags","Remove repeatable or change the flag type.",`${i}.repeatable`),null;if(y&&r.default!==void 0)return a("command_node_invalid","repeatable flag must not declare a default",d,"no default on a repeatable flag (values accumulate into an array)","Remove the default.",`${i}.default`),null;const $=H(r,i,a);if($===null)return null;const E=M(r,i,a);if(E===null)return null;if(y&&E!==void 0)return a("command_node_invalid","repeatable flag must not declare defaultFromEnv",d,"no defaultFromEnv on a repeatable flag (values accumulate into an array)","Remove defaultFromEnv, or drop repeatable.",`${i}.defaultFromEnv`),null;n.push({kind:"flag",name:d,type:s,...g!==void 0?{choices:g}:{},...$!==void 0?{encoding:$}:{},required:r.required,constraint:r.constraint,...r.default!==void 0?{default:r.default}:{},...y?{repeatable:!0}:{},...E!==void 0?{defaultFromEnv:E}:{}})}}return n}_(ie,"validateParams");function H(e,t,a){const n=e.encoding;if(n!==void 0)return typeof n!="string"||!ne.has(n)?(a("command_node_invalid","param encoding must be text|base64",String(n),"text | base64 | omitted","Fix the encoding.",`${t}.encoding`),null):e.type!=="path"?(a("command_node_invalid",'encoding is only valid on a type:"path" param',`type ${String(e.type)}`,'type: "path"','Set the param type to "path", or remove encoding.',`${t}.encoding`),null):n}_(H,"validateFileEncoding");function M(e,t,a){const n=e.defaultFromEnv;if(n===void 0)return;if(typeof n!="string"||!oe.test(n))return a("command_node_invalid","defaultFromEnv must name an environment variable in UPPER_SNAKE_CASE",String(n),'e.g. "CRTR_NODE_ID"',"Fix the environment variable name.",`${t}.defaultFromEnv`),null;const o=e.type===void 0&&e.kind==="positional"?"string":e.type;return o!=="string"&&o!=="path"?(a("command_node_invalid","defaultFromEnv is only valid on a string|path param",`type ${String(e.type)}`,'type: "string" | "path"',"Change the param type, or remove defaultFromEnv.",`${t}.defaultFromEnv`),null):e.default!==void 0?(a("command_node_invalid","a param must not declare both default and defaultFromEnv",`${String(e.name)}: default + defaultFromEnv`,"exactly one default source","Remove one \u2014 defaultFromEnv counts as supplied, a static default does not.",`${t}.defaultFromEnv`),null):n}_(M,"validateDefaultFromEnv");function D(e){return e.split("|").map(t=>t.trim().toLowerCase()).filter(t=>t.length>0&&t!=="null").join(" | ")}_(D,"outputBaseType");function le(e){return e.split("|").some(t=>/^file(\[\])*$/i.test(t.trim()))}_(le,"mentionsFile");function G(e,t,a,n){if(typeof e.type!="string"||e.type.length===0)return n("command_node_invalid","output field type must be a non-empty string",m(e.type),"a type name","Add a type.",`${t}.type`),null;if(typeof e.constraint!="string")return n("command_node_invalid","output field constraint must be a string",m(e.constraint),"a constraint string","Add a constraint.",`${t}.constraint`),null;const o=e.type,p=D(o);if(le(o)&&p!=="file")return n("command_node_invalid","a file output is one file, optionally nullable",o,'type "file" or "file | null"','Return one file per leaf; declare it as "file" or "file | null".',`${t}.type`),null;if(p==="file"&&!a)return n("command_node_invalid","a file output must be a top-level output field",o,`type "file" only on a leaf's own output fields, never inside children or items`,"Move the file field to the top level of the leaf output.",`${t}.type`),null;const c=[["children","object"],["items","array"],["values","enum"]];for(const[d,s]of c)if(e[d]!==void 0&&p!==s)return n("command_node_invalid",`output ${d} is only valid on an ${s} field`,`type ${o}`,`type "${s}" (optionally "${s} | null")`,`Remove ${d}, or change the type to ${s}.`,`${t}.${d}`),null;let r;if(e.children!==void 0){const d=Y(e.children,`${t}.children`,!1,n);if(d===null)return null;r=d}let i;if(e.items!==void 0){const d=e.items,s=`${t}.items`;if(!x(d))return n("command_node_invalid","output items must be an object",m(d),"a { type, constraint, children?, items?, values? } object","Fix the items shape.",s),null;if(!T(d,ae,s,n))return null;const g=G(d,s,!1,n);if(g===null)return null;i=g}let u;if(e.values!==void 0){const d=e.values;if(!Array.isArray(d)||d.length===0||!d.every(s=>typeof s=="string"&&s.length>0))return n("command_node_invalid","enum output values must be a non-empty array of strings",m(d),"a non-empty array of non-empty strings","Declare the values the field may take.",`${t}.values`),null;if(new Set(d).size!==d.length)return n("command_node_invalid","enum output values must be unique",d.join(", "),"distinct values","Remove the duplicate value.",`${t}.values`),null;u=d}return{type:o,constraint:e.constraint,...r!==void 0?{children:r}:{},...i!==void 0?{items:i}:{},...u!==void 0?{values:u}:{}}}_(G,"validateOutputShape");function de(e,t,a){return Y(e,t,!0,a)}_(de,"validateOutput");function Y(e,t,a,n){if(!Array.isArray(e))return n("command_node_invalid",a?"output must be an array":"output children must be an array",m(e),"an array of output fields",a?"Declare the leaf output fields.":"Declare the object's member fields.",t),null;const o=[],p=new Set;let c;for(let r=0;r<e.length;r++){const i=e[r],u=`${t}[${r}]`;if(!x(i))return n("command_node_invalid","output field must be an object",m(i),"a { name, type, required, constraint } object","Fix the output field.",u),null;if(!T(i,te,u,n))return null;if(typeof i.name!="string"||i.name.length===0)return n("command_node_invalid","output field name must be a non-empty string",m(i.name),"a field name","Add a name.",`${u}.name`),null;if(!a&&p.has(i.name))return n("command_node_invalid","duplicate output child name",i.name,"unique member names within one object","Rename the duplicate member.",`${u}.name`),null;if(p.add(i.name),typeof i.required!="boolean")return n("command_node_invalid","output field required must be a boolean",m(i.required),"true | false","Set required.",`${u}.required`),null;const d=G(i,u,a,n);if(d===null)return null;if(D(d.type)==="file"){if(c!==void 0)return n("command_node_invalid","a leaf declares at most one file output",`${c}, ${i.name}`,'one top-level output field of type "file"',"Return the extra files through a separate leaf, or drop the extra file field.",`${u}.type`),null;c=i.name}o.push({name:i.name,...d,required:i.required})}return o}_(Y,"validateOutputFields");function z(e,t,a){const n=e.name;return typeof n!="string"||!O.test(n)?(a("command_node_invalid","node name must be a lowercase kebab-case token",String(n),'a kebab-case token like "app" or "list-all"',"Rename the node.",`${t}.name`),!1):typeof e.description!="string"||e.description.length===0?(a("command_node_invalid","node description must be a non-empty string",m(e.description),"a short parent-listing description","Add a description.",`${t}.description`),!1):typeof e.whenToUse!="string"||e.whenToUse.length===0?(a("command_node_invalid","node whenToUse must be a non-empty string",m(e.whenToUse),"a parent selection rubric","Add a whenToUse rubric.",`${t}.whenToUse`),!1):e.tier!==void 0&&!Z.has(String(e.tier))?(a("command_node_invalid","node tier must be normal|common|important",String(e.tier),"normal | common | important","Fix or omit tier (external plugins cannot declare hidden commands).",`${t}.tier`),!1):!0}_(z,"checkCommon");function me(e,t,a,n,o,p={}){const c=t.join(".");if(!x(e))return o("command_node_invalid","node must be an object",m(e),"a branch or leaf node object","Fix the node.",c),null;const r=e.kind;return r==="branch"?se(e,c,a,n,o,p):r==="leaf"?a&&p.topLevelRootEntry!=="forbidden"?(o("command_node_invalid","top-level node must be a branch","leaf","a branch node with a rootEntry","Wrap the command in a top-level branch (noun) with a rootEntry.",`${c}.kind`),null):ce(e,c,n,o):(o("command_node_invalid",'node kind must be "branch" or "leaf"',String(r),"branch | leaf","Set kind to branch or leaf.",`${c}.kind`),null)}_(me,"validateCommandNode");function se(e,t,a,n,o,p){const c=p.allowPassthrough!==!1;if(!T(e,n==="exec"&&c?J:U,t,o)||!z(e,t,o))return null;const i=e.rootEntry!==void 0,u=p.topLevelRootEntry??"required";if(a&&u==="required"&&!i)return o("command_node_invalid","top-level branch requires a rootEntry","(missing)","a rootEntry { concept, description, whenToUse }","Add a rootEntry to the top-level branch.",`${t}.rootEntry`),null;if((!a||u==="forbidden")&&i)return o("command_node_invalid",a?"repository fragment root must not declare a rootEntry":"nested branch must not declare a rootEntry","rootEntry",a?"no rootEntry (the owning plugin branch already has one)":"no rootEntry (only top-level branches carry one)",a?"Remove rootEntry from the repository fragment root.":"Remove rootEntry from the nested branch.",`${t}.rootEntry`),null;const d=e.extensible!==void 0;if(d&&(!a||p.allowExtensible===!1||e.extensible!==!0))return o("command_node_invalid",a?"extensible must be exactly true on a plugin top-level branch":"extensible is valid only on a top-level branch mount",String(e.extensible),a?"extensible: true":"no extensible marker on nested branches",a?"Set extensible to true or remove it.":"Move the marker to the plugin top-level branch or remove it.",`${t}.extensible`),null;let s;if(i){const h=re(e.rootEntry,`${t}.rootEntry`,o);if(h===null)return null;s=h}if(typeof e.summary!="string"||e.summary.length===0)return o("command_node_invalid","branch summary must be a non-empty string",m(e.summary),"a one-line summary string","Add a summary.",`${t}.summary`),null;if(e.model!==void 0&&typeof e.model!="string")return o("command_node_invalid","branch model must be a string",m(e.model),"a string or omitted","Fix or remove model.",`${t}.model`),null;let g;if(n==="exec"&&e.passthrough!==void 0){const h=ue(e.passthrough,`${t}.passthrough`,o);if(h===null)return null;g=h}if(d&&g!==void 0)return o("command_node_invalid","an extensible branch cannot declare passthrough","extensible and passthrough","one branch mode: extensible or passthrough","Remove passthrough or remove extensible.",t),null;const y=e.children;if(g!==void 0){if(!Array.isArray(y)||y.length!==0)return o("command_node_invalid","a passthrough branch must declare children: []",Array.isArray(y)?`${y.length} children`:m(y),"an empty children array","Set children to [] \u2014 a passthrough branch forwards all argv and can own no subcommands.",`${t}.children`),null}else if(!Array.isArray(y))return o("command_node_invalid","branch children must be an array",m(y),"an array of branch/leaf nodes (may be empty for populated by mounts)","Fix children.",`${t}.children`),null;const $=[],E=new Set;for(let h=0;h<y.length;h++){const k=me(y[h],[...t.split("."),`children[${h}]`],!1,n,o,p);if(k===null)return null;if(E.has(k.name))return o("command_node_invalid","duplicate child name",k.name,"unique child names within a branch","Rename the duplicate child.",`${t}.children[${h}].name`),null;E.add(k.name),$.push(k)}return{kind:"branch",name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...s!==void 0?{rootEntry:s}:{},...d?{extensible:!0}:{},summary:e.summary,...e.model!==void 0?{model:e.model}:{},...g!==void 0?{passthrough:g}:{},children:$}}_(se,"validateBranch");function ue(e,t,a){if(!x(e))return a("command_node_invalid","passthrough must be an object",m(e),"an object { bin, installHint }","Fix or remove passthrough.",t),null;if(!T(e,L,t,a))return null;for(const n of["bin","installHint"])if(typeof e[n]!="string"||e[n].length===0)return a("command_node_invalid",`passthrough ${n} must be a non-empty string`,m(e[n]),n==="bin"?"the external binary name":"a one-line install hint",`Set passthrough.${n}.`,`${t}.${n}`),null;return{bin:e.bin,installHint:e.installHint}}_(ue,"validatePassthrough");function ce(e,t,a,n){if(!T(e,a==="exec"?Q:w,t,n)||!z(e,t,n))return null;if(typeof e.summary!="string"||e.summary.length===0)return n("command_node_invalid","leaf summary must be a non-empty string",m(e.summary),"a one-line summary string","Add a summary.",`${t}.summary`),null;if(a==="exec"&&e.outputKind!=="object")return n("command_node_invalid",'leaf outputKind must be "object" in v1',String(e.outputKind),'exactly "object"','Set outputKind to "object".',`${t}.outputKind`),null;if(a==="http"&&e.rest===void 0)return n("command_node_invalid","HTTP leaf requires a rest mapping","(missing)","a rest object with method, path, and params","Add a rest mapping.",`${t}.rest`),null;const o=ie(e.params,`${t}.params`,n);if(o===null)return null;const p=de(e.output,`${t}.output`,n);if(p===null)return null;const c=e.effects;if(!Array.isArray(c)||c.length===0||!c.every(d=>typeof d=="string"))return n("command_node_invalid","leaf effects must be a non-empty string array",m(c),'a non-empty array of strings (["None. Read-only."] for read-only leaves)',"Declare every persistent effect.",`${t}.effects`),null;const r={kind:"leaf",name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},summary:e.summary,params:o,output:p,effects:c};if(a==="exec")return{...r,outputKind:"object"};const i=e.group;if(i!==void 0&&(typeof i!="string"||!X.test(i)))return n("command_node_invalid","leaf group must match [A-Za-z0-9_-]{1,64}",typeof i=="string"?i:m(i),'a group id of 1-64 letters, digits, "_" or "-" (no ":" or whitespace)',`Rename the group on leaf ${t}.`,`${t}.group`),null;const u=he(e.rest,t,o,n);return u===null?null:{...r,rest:u,...i!==void 0?{group:i}:{}}}_(ce,"validateLeaf");const fe=new Set(["GET","POST","PUT","PATCH","DELETE"]),pe=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/,ye=new Set(["path","query","body","header"]);function he(e,t,a,n){const o=`${t}.rest`;if(!x(e))return n("command_rest_invalid","rest must be an object",m(e),"{ method, path, streaming?, params }","Provide a valid rest mapping.",o),null;const p=new Set(["method","path","streaming","body","bodyRoot","params"]),c=Object.keys(e).filter(l=>!p.has(l));if(c.length>0)return n("command_rest_invalid","unknown rest keys",c.join(", "),"only: method, path, streaming, body, bodyRoot, params","Remove the unknown keys.",o),null;const r=e.method;if(typeof r!="string"||!fe.has(r))return n("command_rest_invalid","method must be GET|POST|PUT|PATCH|DELETE",String(r),"one of the HTTP methods","Set method to a valid HTTP verb.",`${o}.method`),null;const i=e.path;if(typeof i!="string"||!i.startsWith("/"))return n("command_rest_invalid","path must be an absolute path starting with /",String(i),'an absolute path like "/v1/devices/{id}"',"Fix the path.",`${o}.path`),null;const u=new Set,d=/\{([a-z][a-z0-9]*(?:-[a-z0-9]+)*)\}/g;let s;for(;(s=d.exec(i))!==null;)u.add(s[1]);let g=!1;if(e.streaming!==void 0){if(typeof e.streaming!="boolean")return n("command_rest_invalid","streaming must be a boolean",m(e.streaming),"true | false | omitted","Fix streaming.",`${o}.streaming`),null;g=e.streaming}const y=e.params;if(!x(y))return n("command_rest_invalid","params must be an object",m(y),"a mapping of param names to placement specs","Provide params.",`${o}.params`),null;const $=new Map;for(const l of a)$.set(l.name,l);const E={},h=[],k=new Set;for(const[l,v]of Object.entries(y)){if(!$.has(l))return n("command_rest_invalid","rest params key has no matching declared param",l,"a name from the leaf params array","Remove this rest params entry or declare the param.",`${o}.params.${l}`),null;if(k.add(l),!x(v))return n("command_rest_invalid","param mapping must be an object",m(v),"{ in, as? }","Fix the param mapping.",`${o}.params.${l}`),null;const S=new Set(["in","as"]),R=Object.keys(v).filter(F=>!S.has(F));if(R.length>0)return n("command_rest_invalid","unknown param mapping keys",R.join(", "),"only: in, as","Remove the unknown keys.",`${o}.params.${l}`),null;const f=v.in;if(typeof f!="string"||!ye.has(f))return n("command_rest_invalid","param placement must be path|query|body|header",String(f),"path | query | body | header","Set in to a valid placement.",`${o}.params.${l}.in`),null;const b=v.as;if(b!==void 0&&(typeof b!="string"||b.length===0))return n("command_rest_invalid","as must be a non-empty string",String(b),"a non-empty string","Fix the as value.",`${o}.params.${l}.as`),null;if(f==="header"){if(typeof b!="string"||b.length===0)return n("command_rest_invalid","header placement requires a non-empty as",String(b),"a header name","Provide an as value for the header name.",`${o}.params.${l}.as`),null;if(!pe.test(b))return n("command_rest_invalid",'header "as" must be a legal HTTP header field name',b,"an HTTP token (letters, digits, and !#$%&'*+-.^_`|~)","Fix the header name.",`${o}.params.${l}.as`),null;if(b.toLowerCase()==="crtr-request-id")return n("command_rest_invalid","Crtr-Request-Id is assigned by crtr for each leaf call",b,"a header name other than Crtr-Request-Id","Remove this header mapping.",`${o}.params.${l}.as`),null}else if(f==="path"){if(b!==void 0)return n("command_rest_invalid","path placement must not have as","as","no as (placeholder name governs)","Remove as.",`${o}.params.${l}.as`),null;const F=$.get(l);if(F.required!==!0)return n("command_rest_invalid",'in:"path" param must be declared required',`${l}: required ${String(F.required)}`,"required: true",`Set "required": true on the "${l}" param.`,`${o}.params.${l}`),null}const K=$.get(l);if((K.kind==="flag"||K.kind==="positional")&&K.repeatable===!0&&f!=="body")return n("command_rest_invalid","repeatable param must use body placement",`${l}: in "${f}"`,'in: "body" (array values ship as a JSON array)',`Map "${l}" to body placement.`,`${o}.params.${l}.in`),null;if(f==="body"){const B=$.get(l).kind==="stdin",I=b||l;h.push({name:l,isStdin:B,as:I})}E[l]={in:f,...b!==void 0?{as:b}:{}}}for(const l of a)if(!k.has(l.name))return n("command_rest_invalid","declared param not in rest mapping",l.name,"an entry in rest.params","Add a rest mapping for this param.",`${o}.params`),null;const A=new Set;for(const[l,v]of Object.entries(E))v.in==="path"&&A.add(l);if(A.size!==u.size||![...u].every(l=>A.has(l)))return n("command_rest_invalid","mismatch between path placeholders and path params",`placeholders: ${[...u].join(", ")} | params: ${[...A].join(", ")}`,'exact bijection between {placeholders} and in:"path" params',"Ensure every placeholder has a matching path param, and vice versa.",`${o}.path and ${o}.params`),null;if(r==="GET"&&h.length>0)return n("command_rest_invalid","GET method cannot have body params",h.map(l=>l.name).join(", "),"no body placement for GET","Move body params to query, or change the method.",`${o}.method or ${o}.params`),null;let j;if(e.body!==void 0){if(r==="GET")return n("command_rest_invalid","GET method cannot have body constants","body","no body for GET","Remove body, or change the method.",`${o}.body`),null;const l=e.body;if(!x(l))return n("command_rest_invalid","body must be an object",m(l),"a map of constant literal body fields (string | number | boolean)","Fix body.",`${o}.body`),null;const v={};for(const[f,b]of Object.entries(l)){if(typeof b!="string"&&typeof b!="number"&&typeof b!="boolean")return n("command_rest_invalid",`body.${f} must be a string, number, or boolean`,m(b),"string | number | boolean",`Fix body.${f}.`,`${o}.body.${f}`),null;v[f]=b}const S=new Set(h.map(f=>f.as)),R=Object.keys(v).filter(f=>S.has(f));if(R.length>0)return n("command_rest_invalid","body constant key collides with a param's body key",R.join(", "),"body constant keys distinct from every param's body key (its as or name)","Rename the colliding body constant or param.",`${o}.body`),null;j=v}let P;if(e.bodyRoot!==void 0){if(r==="GET")return n("command_rest_invalid","GET method cannot have bodyRoot","bodyRoot","no bodyRoot for GET","Remove bodyRoot, or change the method.",`${o}.bodyRoot`),null;const l=e.bodyRoot;if(typeof l!="string"||l.length===0)return n("command_rest_invalid","bodyRoot must be a non-empty string",m(l),"a non-empty string key","Fix bodyRoot.",`${o}.bodyRoot`),null;P=l}for(const[l,v]of Object.entries(E)){const S=$.get(l);if((S.kind==="context-file"||(S.kind==="positional"||S.kind==="flag")&&S.type==="path")&&v.in!=="body"){const f=S.kind==="context-file"?"context-file":`path ${S.kind}`;return n("command_rest_invalid",`file param (${f}) must be placed in body`,`${l}: in "${v.in}"`,'in: "body"',"Move the file param to body.",`${o}.params.${l}.in`),null}}const q=new Map;for(const l of h)q.has(l.as)||q.set(l.as,[]),q.get(l.as).push({name:l.name,isStdin:l.isStdin});for(const[l,v]of q.entries())if(v.length>1){const S=v.filter(f=>f.isStdin).length,R=v.filter(f=>!f.isStdin).length;if(!(S===1&&R===1))return n("command_rest_invalid","invalid body as grouping",v.map(f=>f.name).join(", "),"exactly one stdin param and one non-stdin param may share a body as (for coalesced sources)","Fix the body grouping.",`${o}.params (as: "${l}")`),null}return{method:r,path:i,...g!==!1?{streaming:g}:{},...j!==void 0?{body:j}:{},...P!==void 0?{bodyRoot:P}:{},params:E}}_(he,"validateRestMapping");export{me as validateCommandNode};
|
|
@@ -7,13 +7,25 @@ export interface ManifestRootEntry {
|
|
|
7
7
|
description: string;
|
|
8
8
|
whenToUse: string;
|
|
9
9
|
}
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
/** The shape of one output value, without a name: the element of an `array`.
|
|
11
|
+
* `type` is a free type name; a trailing ` | null` marks it nullable and the
|
|
12
|
+
* remainder is its base type, which decides the structural keys it may carry. */
|
|
13
|
+
export interface ManifestFieldShape {
|
|
13
14
|
type: string;
|
|
14
|
-
required: boolean;
|
|
15
15
|
/** Inline semantic constraint — bounds, enum, token caps. */
|
|
16
16
|
constraint: string;
|
|
17
|
+
/** Named members; only when the base type is `object`. */
|
|
18
|
+
children?: ManifestField[];
|
|
19
|
+
/** Element shape; only when the base type is `array`. */
|
|
20
|
+
items?: ManifestFieldShape;
|
|
21
|
+
/** The allowed values; required, and only valid, when the base type is `enum`. */
|
|
22
|
+
values?: string[];
|
|
23
|
+
}
|
|
24
|
+
/** One declared output field of a leaf's result. Base type `file` marks the
|
|
25
|
+
* leaf's one file output: valid only as a top-level field, at most one per leaf. */
|
|
26
|
+
export interface ManifestField extends ManifestFieldShape {
|
|
27
|
+
name: string;
|
|
28
|
+
required: boolean;
|
|
17
29
|
}
|
|
18
30
|
/** How a local file named by a `path` param is encoded into the request: 'text'
|
|
19
31
|
* as UTF-8, 'base64' as the base64 of its raw bytes. The path string itself
|
|
@@ -22,8 +34,9 @@ export type ManifestFileEncoding = 'text' | 'base64';
|
|
|
22
34
|
export interface ManifestPositionalParam {
|
|
23
35
|
kind: 'positional';
|
|
24
36
|
name: string;
|
|
25
|
-
/** Display hint only; always parsed as string.
|
|
26
|
-
|
|
37
|
+
/** Display hint only; always parsed as string. `file` names a file argument
|
|
38
|
+
* a capability-provider call carries; locally it is sent as the string typed. */
|
|
39
|
+
type?: 'string' | 'path' | 'file';
|
|
27
40
|
required: boolean;
|
|
28
41
|
constraint: string;
|
|
29
42
|
/** Collect every remaining positional token into an array, in argv order.
|
|
@@ -37,8 +50,10 @@ export interface ManifestPositionalParam {
|
|
|
37
50
|
export interface ManifestFlagParam {
|
|
38
51
|
kind: 'flag';
|
|
39
52
|
name: string;
|
|
40
|
-
/** 'bool' flags take no value — presence is true.
|
|
41
|
-
|
|
53
|
+
/** 'bool' flags take no value — presence is true. 'file' names a file
|
|
54
|
+
* argument a capability-provider call carries; locally it is sent as the
|
|
55
|
+
* string typed, never read from disk (that is `path` with `encoding`). */
|
|
56
|
+
type: 'string' | 'int' | 'bool' | 'path' | 'enum' | 'file';
|
|
42
57
|
/** Required, and only valid, when type is 'enum'. */
|
|
43
58
|
choices?: string[];
|
|
44
59
|
required: boolean;
|
|
@@ -131,6 +146,11 @@ export interface ManifestExecLeaf extends ManifestLeafBase {
|
|
|
131
146
|
/** HTTP-transport leaf; `outputKind` derives from `rest.streaming`. */
|
|
132
147
|
export interface ManifestHttpLeaf extends ManifestLeafBase {
|
|
133
148
|
rest: RestMapping;
|
|
149
|
+
/** The capability-provider tool group this leaf belongs to, matching
|
|
150
|
+
* `[A-Za-z0-9_-]{1,64}`; the scope that admits it is `<provider>:<group>`.
|
|
151
|
+
* Required on every leaf of a bundle carrying a `provider` block; ignored by
|
|
152
|
+
* local dispatch. */
|
|
153
|
+
group?: string;
|
|
134
154
|
}
|
|
135
155
|
export type ManifestLeaf = ManifestExecLeaf | ManifestHttpLeaf;
|
|
136
156
|
export interface ManifestBranch<L extends ManifestLeafBase = ManifestLeaf> {
|
|
@@ -172,6 +192,9 @@ export interface ManifestMount<L extends ManifestLeafBase = ManifestLeaf> {
|
|
|
172
192
|
*/
|
|
173
193
|
export interface HttpPluginCommandManifest {
|
|
174
194
|
schemaVersion: 1;
|
|
195
|
+
/** The plugin's integer major version (≥ 1). Omitted means 1 for a plugin
|
|
196
|
+
* installed locally; a capability-provider bundle declares it. */
|
|
197
|
+
version?: number;
|
|
175
198
|
/** Overrides the registration endpoint as the base for every leaf's REST path. */
|
|
176
199
|
baseUrl?: string;
|
|
177
200
|
timeouts?: ManifestTimeouts;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var N=Object.defineProperty;var u=(e,
|
|
1
|
+
var N=Object.defineProperty;var u=(e,s)=>N(e,"name",{value:s,configurable:!0});import{visibleWidth as g,wrapTextWithAnsi as b}from"@earendil-works/pi-tui";import{theme as f,wrapText as x}from"../../../core/tui/panel.js";import{commandLabel as h}from"./model.js";function k(e,s,t){const n=[];return n.push(...a(h(e),t).map(r=>f.fg("accent",f.bold(r)))),n.push(...a(`${e.kind.toUpperCase()} COMMAND \xB7 ${(e.tier??"normal").toUpperCase()} TIER`,t).map(r=>f.fg("dim",r))),n.push(""),n.push(...p(e.description,t)),c(n,"WHEN TO USE",t),n.push(...p(e.whenToUse,t)),c(n,"SUMMARY",t),n.push(...p(e.summary,t)),e.kind==="leaf"?v(n,e,s,t):S(n,e,t),n}u(k,"renderCommandView");function v(e,s,t,n){if(c(e,"PARAMS",n),s.params.length===0)e.push(...p("No input parameters.",n));else for(const o of s.params)e.push(...M(o,n));if(c(e,"OUTPUT",n),s.output.length===0)e.push(...p("No output fields declared.",n));else for(const o of s.output)e.push(...m(o,n));if(c(e,"EFFECTS",n),s.effects.length===0)e.push(...p("No effects declared.",n));else for(const o of s.effects)e.push(...i(" \u2022 ",o,n));if(c(e,"INVOKED VIA",n),t.transport===null){e.push(...a("This plugin declares no command transport.",n).map(o=>f.fg("warning",o)));return}const r=t.transportTarget??"No target declared.";e.push(...i(` ${t.transport} `,r,n)),t.transport==="http"&&s.rest!==void 0&&e.push(...i(" REST ",`${s.rest.method} ${s.rest.path} \xB7 ${s.rest.streaming?"streaming response":"single response"}`,n))}u(v,"renderLeaf");function S(e,s,t){if(s.rootEntry!==void 0&&(c(e,"ROOT ENTRY",t),e.push(...i(" Concept ",s.rootEntry.concept,t)),e.push(...i(" Description ",s.rootEntry.description,t)),e.push(...i(" When to use ",s.rootEntry.whenToUse,t))),s.passthrough!==void 0&&(c(e,"PASSES THROUGH",t),e.push(...i(" Binary ",s.passthrough.bin,t)),e.push(...i(" Install ",s.passthrough.installHint,t))),c(e,"CHILDREN",t),s.children.length===0){e.push(...p(s.passthrough===void 0?"No subcommands.":"No subcommands \u2014 every argument is forwarded to the binary.",t));return}for(const n of s.children)e.push(...i(` ${h(n)} `,n.description,t))}u(S,"renderBranch");function M(e,s){const t=D(e),n=i(` ${t} `,O(e),s),r=C;return e.kind==="positional"?(e.repeatable===!0&&n.push(...i(r,"Repeatable; supply multiple values in argv order.",s)),e.encoding!==void 0&&n.push(...i(r,d(e.encoding),s)),e.defaultFromEnv!==void 0&&n.push(...i(r,E(e.defaultFromEnv),s))):e.kind==="flag"?(e.choices!==void 0&&n.push(...i(r,`Choices: ${e.choices.join(", ")}.`,s)),e.default!==void 0&&n.push(...i(r,`Default: ${R(e.default)}.`,s)),e.repeatable===!0&&n.push(...i(r,"Repeatable; supply the flag multiple times to accumulate values.",s)),e.encoding!==void 0&&n.push(...i(r,d(e.encoding),s)),e.defaultFromEnv!==void 0&&n.push(...i(r,E(e.defaultFromEnv),s)),e.hidden===!0&&n.push(...i(r,"Hidden from the command's ordinary -h output.",s,"warning"))):e.kind==="context-file"&&(n.push(...i(r,`Mapped input: ${e.name}.`,s)),e.shape!==void 0&&n.push(...i(r,`Expected JSON: ${e.shape}`,s))),n.push(...i(r,`Constraint: ${e.constraint}`,s)),n}u(M,"renderParam");function m(e,s,t=" "){const n=`${t} `,r=[...i(`${t}${e.name} `,`${e.type} \xB7 ${e.required?"required":"optional"}`,s),...i(n,`Constraint: ${e.constraint}`,s)];return r.push(...$(e,s,n)),r}u(m,"renderField");function $(e,s,t){const n=[];e.values!==void 0&&n.push(...i(t,`Values: ${e.values.join(", ")}.`,s));for(const r of e.children??[])n.push(...m(r,s,t));if(e.items!==void 0){const r=e.items,o=r.constraint.length>0?` \xB7 ${r.constraint}`:"";n.push(...i(t,`Each element: ${r.type}${o}`,s)),n.push(...$(r,s,`${t} `))}return n}u($,"renderShapeBody");const C=" ";function D(e){switch(e.kind){case"positional":return`${e.name}${e.repeatable===!0?"\u2026":""}`;case"flag":return`--${e.name}`;case"stdin":return e.name;case"context-file":return"--context-file PATH"}}u(D,"paramLabel");function O(e){const s=e.required?"required":"optional";switch(e.kind){case"positional":return`positional \xB7 ${e.type??"string"} \xB7 ${s}`;case"flag":return`flag \xB7 ${e.type} \xB7 ${s}`;case"stdin":return`stdin \xB7 raw text \xB7 ${s}`;case"context-file":return`context file \xB7 JSON file \xB7 ${s}`}}u(O,"paramMeta");function d(e){return e==="base64"?"Encoding: reads the local file's raw bytes and sends base64; the path itself is never sent.":"Encoding: reads the local file as UTF-8 text and sends its content; the path itself is never sent."}u(d,"encodingDetail");function R(e){return typeof e=="string"?JSON.stringify(e):String(e)}u(R,"formatValue");function E(e){return`When $${e} is set and non-empty, it supplies the value and counts as supplied.`}u(E,"envDefaultDetail");function c(e,s,t){e.push(""),e.push(...a(s,t).map(n=>f.fg("dim",n)))}u(c,"section");function p(e,s){return a(e,Math.max(1,s-2)).map(n=>` ${f.fg("dim",n)}`)}u(p,"paragraph");function i(e,s,t,n="dim"){const r=g(e);if(r>=t)return[...a(e.trimEnd(),t).map(l=>f.fg("accent",l)),...a(s,Math.max(1,t-2)).map(l=>` ${f.fg(n,l)}`)];const o=Math.max(1,t-r),T=" ".repeat(r);return a(s,o).map((l,y)=>y===0?`${f.fg("accent",e)}${f.fg(n,l)}`:`${T}${f.fg(n,l)}`)}u(i,"hanging");function a(e,s){const t=e.trim(),n=Math.max(1,s);return t.split(/\s+/).some(r=>g(r)>n)?b(t,n):x(t,n)}u(a,"wrapped");export{k as renderCommandView};
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import { type ValidatedCommandManifest } from '../../api/command-manifest/manifest.js';
|
|
2
2
|
import type { KindConfig, MemoryExtensionDeclarations, PageComponentRegistration } from '../../types.js';
|
|
3
|
+
/** Who may call a capability provider's tool group (contract 3). */
|
|
4
|
+
export type ProviderGroupWho = 'own' | 'user' | 'allowlist' | 'any';
|
|
5
|
+
/** One tool group a capability provider declares in `bundle.json`. */
|
|
6
|
+
export interface ProviderGroupDeclaration {
|
|
7
|
+
group: string;
|
|
8
|
+
consent_line: string;
|
|
9
|
+
/** Price per unit, or null/absent when the group is unpriced. */
|
|
10
|
+
rate?: number | null;
|
|
11
|
+
who: ProviderGroupWho;
|
|
12
|
+
/** The app principals the group admits: `[]` unless `who` is `allowlist`
|
|
13
|
+
* (where it may also be empty). */
|
|
14
|
+
allowlist: string[];
|
|
15
|
+
}
|
|
16
|
+
/** The `provider` block of a capability-provider bundle emitted by the directory. */
|
|
17
|
+
export interface ProviderBlock {
|
|
18
|
+
id: string;
|
|
19
|
+
major: number;
|
|
20
|
+
groups: ProviderGroupDeclaration[];
|
|
21
|
+
}
|
|
3
22
|
export interface BundleIssue {
|
|
4
23
|
code: string;
|
|
5
24
|
path?: string;
|
|
@@ -10,6 +29,18 @@ export interface BundleIssue {
|
|
|
10
29
|
}
|
|
11
30
|
export interface ValidatedBundle {
|
|
12
31
|
bundleVersion: 1;
|
|
32
|
+
/** The plugin's integer major: `bundle.json` `version`, else `commands.json`
|
|
33
|
+
* `version`; absent when neither declares one (treated as 1 locally). */
|
|
34
|
+
version?: number;
|
|
35
|
+
/** The transport `bundle.json` declares, when it declares one (a bundle the
|
|
36
|
+
* directory emits for a capability provider always does). */
|
|
37
|
+
transport?: {
|
|
38
|
+
kind: 'http';
|
|
39
|
+
endpoint: string;
|
|
40
|
+
authEnv?: string;
|
|
41
|
+
};
|
|
42
|
+
/** The capability-provider block: present only on a provider bundle. */
|
|
43
|
+
provider?: ProviderBlock;
|
|
13
44
|
/** Kind-registry contributions declared in `bundle.json` (optional). The
|
|
14
45
|
* installer copies this validated block into the synthesized plugin
|
|
15
46
|
* manifest, where `readMergedLaunchConfig` layers it — see
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var p=Object.defineProperty;var m=(e,s)=>p(e,"name",{value:s,configurable:!0});import{Parser as b}from"tar";import{validateCommandManifest as y}from"../../api/command-manifest/manifest.js";import{invalidPluginKindsReasons as g}from"../config.js";import{invalidPageComponentsReasons as k,normalizePageComponents as v}from"../human/page-catalog.js";import{validateMemoryExtensionDeclarations as j}from"../plugin-extensions.js";function _(e,s,t,n,r,i){return{code:e,message:s,received:t,expected:n,next:r,...i!==void 0?{path:i}:{}}}m(_,"issue");function d(e,s,t,n,r){return _("plugin_bundle_invalid",e,s,t,n,r)}m(d,"bundleInvalid");function x(e){if(e[0]===31&&e[1]===139)return d("bundle archive must be uncompressed tar data","gzip-compressed archive","an uncompressed tar archive","Serve the archive without compression.");if(e.length===0||e.length%512!==0)return d("bundle archive must be a non-empty whole number of 512-byte tar blocks",`${e.length} bytes`,"a non-empty tar stream with a 512-byte block length","Serve one complete uncompressed tar archive.");if(e.length<1024||!e.subarray(-1024).every(s=>s===0))return d("bundle archive is missing the required two zero-block terminator","archive tail is not 1024 zero bytes","a complete tar stream ending in two 512-byte zero blocks","Serve the complete uncompressed tar archive.")}m(x,"archiveEnvelopeIssue");function S(e,s){if(e.length===0||e.includes("\0")||e.includes("\\")||e.startsWith("/")||/^[A-Za-z]:($|\/)/.test(e))return d("bundle member path must be a safe relative POSIX path",e,"a relative path without absolute, drive-letter, backslash, or NUL forms","Use a safe relative POSIX member path.",e);if(s==="file"&&e.endsWith("/"))return d("only directory members may have a trailing slash",e,"a regular-file path without a trailing slash","Emit this member as a regular file path.",e);if((s==="directory"&&e.endsWith("/")?e.slice(0,-1):e).split("/").some(n=>n===""||n==="."||n===".."))return d("bundle member path contains an unsafe segment",e,"non-empty segments other than . or ..","Use a safe relative POSIX member path.",e)}m(S,"validateMemberName");function w(e,s){if(s==="file")return e==="bundle.json"||e==="commands.json"||e==="hooks.json"||e.startsWith("memory/")&&e.endsWith(".md")||e.startsWith("hooks/")?void 0:d("bundle contains an unsupported file member",e,"bundle.json, commands.json, hooks.json, memory/**/*.md, or hooks/**","Remove unsupported members from the archive.",e);if(!(e==="memory"||e.startsWith("memory/")||e==="hooks"||e.startsWith("hooks/")))return d("bundle contains an unsupported directory member",e,"memory or hooks, or a directory below either subtree","Remove unsupported directory members from the archive.",e)}m(w,"validateMemberShape");function $(e){return e.type==="directory"&&e.path.endsWith("/")?e.path.slice(0,-1):e.path}m($,"materializedPath");function E(e){const s=new Map;for(const t of e){const n=$(t),r=s.get(n);if(r!==void 0)return d(r===t.type?"bundle contains a duplicate member":"bundle claims one path as both a file and a directory",`${n} (${r} and ${t.type})`,"each materialized path exactly once, with one type","Emit each archive path once, as a single member type.",n);s.set(n,t.type)}for(const[t,n]of s)if(n==="file"){for(const r of s.keys())if(r.startsWith(`${t}/`))return d("bundle nests a member beneath a regular-file path",`${r} beneath file ${t}`,"members nested only beneath directory paths","Emit the parent path as a directory or remove the nested member.",r)}}m(E,"validateMemberTree");async function N(e){const s=[],t=new Set;let n;return await new Promise((r,i)=>{const c=new b({strict:!0,maxMetaEntrySize:Number.MAX_SAFE_INTEGER,gzip:!1,brotli:!1,zstd:!1,onReadEntry:m(a=>{const u=a.path,f=a.type==="File"||a.type==="OldFile"?"file":a.type==="Directory"?"directory":void 0;if(f===void 0||a.unsupported||a.ignore||a.linkpath!==void 0){n??=d("bundle member must be a regular file or directory without a link target",`${u} (${a.type})`,"a regular file or directory member without link metadata","Remove links and unsupported member types from the archive.",u),a.resume();return}n??=S(u,f),n??=w(u,f),t.has(u)&&(n??=d("bundle contains a duplicate member",u,"each member path exactly once","Emit each archive member only once.",u)),t.add(u);const l=[];a.on("data",h=>l.push(h)),a.on("end",()=>{n===void 0&&s.push({path:u,type:f,bytes:new Uint8Array(Buffer.concat(l))})})},"onReadEntry")});c.on("ignoredEntry",a=>{const u=a.path;n??=d("bundle contains an ignored or unsupported archive member",`${u} (${a.type})`,"only supported regular file or directory members","Remove ignored or unsupported members from the archive.",u),a.resume()}),c.once("error",i),c.once("end",r);try{c.end(Buffer.from(e))}catch(a){i(a)}}).catch(r=>{n??=d("bundle archive could not be parsed",r instanceof Error?r.message:String(r),"a valid complete uncompressed tar archive","Fix the archive format and serve it again.")}),n===void 0?{members:s}:{issue:n}}m(N,"parseArchive");function O(e){let s;try{s=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issue:d("bundle.json is not valid JSON","invalid JSON",'{"bundleVersion":1}',"Write valid bundle metadata.","bundle.json")}}if(typeof s!="object"||s===null||Array.isArray(s))return{issue:d("bundle.json must be a JSON object",JSON.stringify(s),"an object with bundleVersion set to 1 and optionally kinds",'Set bundle.json to {"bundleVersion":1}.',"bundle.json")};const t=s,n=Object.keys(t).filter(i=>i!=="bundleVersion"&&i!=="kinds"&&i!=="page_components"&&i!=="memory_extensions");if(t.bundleVersion!==1||n.length>0)return{issue:d("bundle.json must contain bundleVersion 1 and at most kinds, page_components, and memory_extensions blocks",JSON.stringify(s),"an object with bundleVersion set to 1 and optional kinds, page_components, and memory_extensions",'Set bundle.json to {"bundleVersion":1} plus optional contribution blocks.',"bundle.json")};if(t.kinds!==void 0){const i=g(t.kinds);if(i.length>0)return{issue:d("bundle.json kinds block is invalid",i.join("; "),"each entry a full KindConfig (whenToUse required) or a sparse patch of launch fields","Fix the kinds entries in bundle.json.","bundle.json")}}if(t.page_components!==void 0){const i=k(t.page_components);if(i.length>0)return{issue:d("bundle.json page_components block is invalid",i.join("; "),"an array of page component registrations, each a kind string or {kind, description?, useWhen?, doc?, display?}","Fix the page_components entries in bundle.json.","bundle.json")}}let r;if(t.memory_extensions!==void 0)try{r=j(t.memory_extensions)}catch(i){return{issue:d("bundle.json memory_extensions block is invalid",i instanceof Error?i.message:String(i),"a valid memory_extensions declaration object","Fix the memory_extensions declarations in bundle.json.","bundle.json")}}return{...t.kinds!==void 0?{kinds:t.kinds}:{},...t.page_components!==void 0?{page_components:v(t.page_components)}:{},...r!==void 0?{memory_extensions:r}:{}}}m(O,"parseBundleMetadata");function W(e,s,t,n=new Set){let r;try{r=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issues:[d("commands.json is not valid JSON","invalid JSON","a valid command-manifest JSON object","Regenerate commands.json.","commands.json")]}}const i=y(r,{transport:"http",reservedCoreNames:s,extensibleCoreBranches:n,...t!==void 0?{coreCommandPaths:t}:{}});return{...i.manifest!==void 0?{commands:i.manifest}:{},issues:i.issues}}m(W,"parseCommands");async function R(e,s){const t=x(e);if(t!==void 0)return{issues:[t]};const n=await N(e);if(n.issue!==void 0||n.members===void 0)return{issues:[n.issue]};const r=E(n.members);if(r!==void 0)return{issues:[r]};const i=n.members.find(o=>o.path==="bundle.json"&&o.type==="file"),c=n.members.find(o=>o.path==="commands.json"&&o.type==="file");if(i===void 0||c===void 0)return{issues:[d("bundle must contain exactly one bundle.json and one commands.json regular file",`bundle.json=${i!==void 0}, commands.json=${c!==void 0}`,"one regular bundle.json and one regular commands.json member","Add the required metadata members to the archive.")]};const a=n.members.find(o=>o.path==="hooks.json"&&o.type==="file"),u=n.members.filter(o=>o.type==="file"&&o.path.startsWith("hooks/"));if((a!==void 0||n.members.some(o=>o.path==="hooks"||o.path.startsWith("hooks/")))&&(a===void 0||!u.some(o=>o.path==="hooks/dispatch")))return{issues:[d("bundle hook payload must contain both hooks.json and hooks/dispatch regular files",`hooks.json=${a!==void 0}, hooks/dispatch=${u.some(o=>o.path==="hooks/dispatch")}`,"both hooks.json and hooks/dispatch regular files","Add both required hook members or remove the hook payload.")]};const l=O(i.bytes);if(l.issue!==void 0)return{issues:[l.issue]};const h=W(c.bytes,s.reservedCoreNames,s.coreCommandPaths,s.extensibleCoreBranches);return h.commands===void 0?{issues:h.issues}:{bundle:{bundleVersion:1,...l.kinds!==void 0?{kinds:l.kinds}:{},...l.page_components!==void 0?{page_components:l.page_components}:{},...l.memory_extensions!==void 0?{memory_extensions:l.memory_extensions}:{},commands:h.commands,commandsBytes:c.bytes,directories:n.members.filter(o=>o.type==="directory").map(o=>({path:o.path})),memory:n.members.filter(o=>o.type==="file"&&o.path.startsWith("memory/")).map(o=>({path:o.path,bytes:o.bytes})),...a!==void 0?{hooks:{manifest:a.bytes,files:u.map(o=>({path:o.path,bytes:o.bytes}))}}:{}},issues:[]}}m(R,"validatePluginBundle");export{R as validatePluginBundle};
|
|
1
|
+
var j=Object.defineProperty;var p=(e,o)=>j(e,"name",{value:o,configurable:!0});import{Parser as k}from"tar";import{validateCommandManifest as w}from"../../api/command-manifest/manifest.js";import{invalidPluginKindsReasons as _}from"../config.js";import{invalidPageComponentsReasons as S,normalizePageComponents as $}from"../human/page-catalog.js";import{validateMemoryExtensionDeclarations as O}from"../plugin-extensions.js";import{validateHttpPluginTransport as E}from"./endpoint.js";const g=/^[A-Za-z0-9_-]{1,64}$/,x=new Set(["id","major","groups"]),N=new Set(["group","consent_line","rate","who","allowlist"]),A=new Set(["own","user","allowlist","any"]),J=new Set(["bundleVersion","kinds","page_components","memory_extensions","version","transport","provider"]);function R(e,o,n,r,t,u){return{code:e,message:o,received:n,expected:r,next:t,...u!==void 0?{path:u}:{}}}p(R,"issue");function a(e,o,n,r,t){return R("plugin_bundle_invalid",e,o,n,r,t)}p(a,"bundleInvalid");function P(e){if(e[0]===31&&e[1]===139)return a("bundle archive must be uncompressed tar data","gzip-compressed archive","an uncompressed tar archive","Serve the archive without compression.");if(e.length===0||e.length%512!==0)return a("bundle archive must be a non-empty whole number of 512-byte tar blocks",`${e.length} bytes`,"a non-empty tar stream with a 512-byte block length","Serve one complete uncompressed tar archive.");if(e.length<1024||!e.subarray(-1024).every(o=>o===0))return a("bundle archive is missing the required two zero-block terminator","archive tail is not 1024 zero bytes","a complete tar stream ending in two 512-byte zero blocks","Serve the complete uncompressed tar archive.")}p(P,"archiveEnvelopeIssue");function I(e,o){if(e.length===0||e.includes("\0")||e.includes("\\")||e.startsWith("/")||/^[A-Za-z]:($|\/)/.test(e))return a("bundle member path must be a safe relative POSIX path",e,"a relative path without absolute, drive-letter, backslash, or NUL forms","Use a safe relative POSIX member path.",e);if(o==="file"&&e.endsWith("/"))return a("only directory members may have a trailing slash",e,"a regular-file path without a trailing slash","Emit this member as a regular file path.",e);if((o==="directory"&&e.endsWith("/")?e.slice(0,-1):e).split("/").some(r=>r===""||r==="."||r===".."))return a("bundle member path contains an unsafe segment",e,"non-empty segments other than . or ..","Use a safe relative POSIX member path.",e)}p(I,"validateMemberName");function W(e,o){if(o==="file")return e==="bundle.json"||e==="commands.json"||e==="hooks.json"||e.startsWith("memory/")&&e.endsWith(".md")||e.startsWith("hooks/")?void 0:a("bundle contains an unsupported file member",e,"bundle.json, commands.json, hooks.json, memory/**/*.md, or hooks/**","Remove unsupported members from the archive.",e);if(!(e==="memory"||e.startsWith("memory/")||e==="hooks"||e.startsWith("hooks/")))return a("bundle contains an unsupported directory member",e,"memory or hooks, or a directory below either subtree","Remove unsupported directory members from the archive.",e)}p(W,"validateMemberShape");function V(e){return e.type==="directory"&&e.path.endsWith("/")?e.path.slice(0,-1):e.path}p(V,"materializedPath");function z(e){const o=new Map;for(const n of e){const r=V(n),t=o.get(r);if(t!==void 0)return a(t===n.type?"bundle contains a duplicate member":"bundle claims one path as both a file and a directory",`${r} (${t} and ${n.type})`,"each materialized path exactly once, with one type","Emit each archive path once, as a single member type.",r);o.set(r,n.type)}for(const[n,r]of o)if(r==="file"){for(const t of o.keys())if(t.startsWith(`${n}/`))return a("bundle nests a member beneath a regular-file path",`${t} beneath file ${n}`,"members nested only beneath directory paths","Emit the parent path as a directory or remove the nested member.",t)}}p(z,"validateMemberTree");async function F(e){const o=[],n=new Set;let r;return await new Promise((t,u)=>{const m=new k({strict:!0,maxMetaEntrySize:Number.MAX_SAFE_INTEGER,gzip:!1,brotli:!1,zstd:!1,onReadEntry:p(s=>{const l=s.path,c=s.type==="File"||s.type==="OldFile"?"file":s.type==="Directory"?"directory":void 0;if(c===void 0||s.unsupported||s.ignore||s.linkpath!==void 0){r??=a("bundle member must be a regular file or directory without a link target",`${l} (${s.type})`,"a regular file or directory member without link metadata","Remove links and unsupported member types from the archive.",l),s.resume();return}r??=I(l,c),r??=W(l,c),n.has(l)&&(r??=a("bundle contains a duplicate member",l,"each member path exactly once","Emit each archive member only once.",l)),n.add(l);const i=[];s.on("data",b=>i.push(b)),s.on("end",()=>{r===void 0&&o.push({path:l,type:c,bytes:new Uint8Array(Buffer.concat(i))})})},"onReadEntry")});m.on("ignoredEntry",s=>{const l=s.path;r??=a("bundle contains an ignored or unsupported archive member",`${l} (${s.type})`,"only supported regular file or directory members","Remove ignored or unsupported members from the archive.",l),s.resume()}),m.once("error",u),m.once("end",t);try{m.end(Buffer.from(e))}catch(s){u(s)}}).catch(t=>{r??=a("bundle archive could not be parsed",t instanceof Error?t.message:String(t),"a valid complete uncompressed tar archive","Fix the archive format and serve it again.")}),r===void 0?{members:o}:{issue:r}}p(F,"parseArchive");function y(e){return typeof e=="number"&&Number.isInteger(e)&&e>=1}p(y,"isPositiveInt");function M(e){const o=p((s,l,c,i)=>({issue:a(s,l,c,"Fix the provider block in bundle.json.",`bundle.json.${i}`)}),"bad");if(typeof e!="object"||e===null||Array.isArray(e))return o("bundle.json provider must be an object",JSON.stringify(e),"{ id, major, groups }","provider");const n=e,r=Object.keys(n).filter(s=>!x.has(s));if(r.length>0)return o("bundle.json provider has unknown keys",r.join(", "),"only: id, major, groups","provider");if(typeof n.id!="string"||!g.test(n.id))return o("provider id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(n.id),'a provider id of 1-64 letters, digits, "_" or "-"',"provider.id");if(!y(n.major))return o("provider major must be an integer of at least 1",JSON.stringify(n.major),"the served major as an integer >= 1","provider.major");const t=n.groups;if(!Array.isArray(t)||t.length===0)return o("provider groups must be a non-empty array",JSON.stringify(t),"at least one { group, consent_line, rate?, who, allowlist }","provider.groups");const u=[],m=new Set;for(let s=0;s<t.length;s++){const l=`provider.groups[${s}]`,c=t[s];if(typeof c!="object"||c===null||Array.isArray(c))return o("provider group must be an object",JSON.stringify(c),"{ group, consent_line, rate?, who, allowlist }",l);const i=c,b=Object.keys(i).filter(v=>!N.has(v));if(b.length>0)return o("provider group has unknown keys",b.join(", "),"only: group, consent_line, rate, who, allowlist",l);if(typeof i.group!="string"||!g.test(i.group))return o("provider group id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(i.group),'a group id of 1-64 letters, digits, "_" or "-"',`${l}.group`);if(m.has(i.group))return o("provider group ids must be unique",i.group,"each group declared once",`${l}.group`);if(m.add(i.group),typeof i.consent_line!="string"||i.consent_line.trim().length===0)return o("provider group consent_line must be a non-empty string",JSON.stringify(i.consent_line),"the consent line shown for this group",`${l}.consent_line`);const f=i.rate;if(f!=null&&(typeof f!="number"||!Number.isFinite(f)||f<0))return o("provider group rate must be a non-negative number or null",JSON.stringify(f),"a finite number >= 0, null, or omitted",`${l}.rate`);if(typeof i.who!="string"||!A.has(i.who))return o("provider group who must be own|user|allowlist|any",JSON.stringify(i.who),"own | user | allowlist | any",`${l}.who`);const h=i.allowlist;if(!Array.isArray(h)||!h.every(v=>typeof v=="string"&&v.length>0))return o("provider group allowlist must be an array of non-empty strings",JSON.stringify(h),"an array of app principals ([] unless who is allowlist)",`${l}.allowlist`);if(h.length>0&&i.who!=="allowlist")return o("provider group allowlist must be empty unless who is allowlist",`who ${i.who}`,'who: "allowlist", or allowlist: []',`${l}.allowlist`);u.push({group:i.group,consent_line:i.consent_line,...f!==void 0?{rate:f}:{},who:i.who,allowlist:h})}return{provider:{id:n.id,major:n.major,groups:u}}}p(M,"parseProviderBlock");function B(e){const o=[],n=p((r,t)=>{const u=[...t,r.name];if(r.kind==="leaf")o.push({path:u.join(" "),leaf:r});else for(const m of r.children)n(m,u)},"walk");for(const r of e.roots)n(r,[]);for(const r of e.coreMounts)n(r.node,r.parent);return o}p(B,"collectLeaves");function D(e,o){const n=e.provider;if(n===void 0)return;if(e.transport===void 0)return a("a provider bundle must declare its transport","(missing)",'transport { kind: "http", endpoint } in bundle.json',"Add the provider endpoint as bundle.json transport.","bundle.json.transport");if(e.transport.authEnv!==void 0)return a("a provider bundle must not declare authEnv",e.transport.authEnv,"no authEnv: the runtime presents its own outbound token","Remove transport.authEnv from bundle.json.","bundle.json.transport.authEnv");const r=new Set(n.groups.map(t=>t.group));for(const{path:t,leaf:u}of B(o)){const m="group"in u?u.group:void 0;if(m===void 0)return a(`provider leaf "${t}" has no group`,t,"a group on every leaf of a provider bundle",`Add a group to leaf "${t}".`,`commands.json:${t}.group`);if(!r.has(m))return a(`provider leaf "${t}" names undeclared group "${m}"`,m,`one of: ${[...r].join(", ")}`,`Declare group "${m}" in the provider block, or move leaf "${t}" to a declared group.`,`commands.json:${t}.group`);if("rest"in u&&u.rest.streaming===!0)return a(`provider leaf "${t}" streams`,"rest.streaming: true","no streaming leaves in a provider bundle",`Make leaf "${t}" return one response.`,`commands.json:${t}.rest.streaming`)}}p(D,"providerBundleIssue");function q(e){let o;try{o=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issue:a("bundle.json is not valid JSON","invalid JSON",'{"bundleVersion":1}',"Write valid bundle metadata.","bundle.json")}}if(typeof o!="object"||o===null||Array.isArray(o))return{issue:a("bundle.json must be a JSON object",JSON.stringify(o),"an object with bundleVersion set to 1 and optionally kinds",'Set bundle.json to {"bundleVersion":1}.',"bundle.json")};const n=o,r=Object.keys(n).filter(s=>!J.has(s));if(n.bundleVersion!==1||r.length>0)return{issue:a("bundle.json must contain bundleVersion 1 and at most version, transport, provider, kinds, page_components, and memory_extensions",JSON.stringify(o),"an object with bundleVersion set to 1 and optional version, transport, provider, kinds, page_components, and memory_extensions",'Set bundle.json to {"bundleVersion":1} plus optional contribution blocks.',"bundle.json")};if(n.kinds!==void 0){const s=_(n.kinds);if(s.length>0)return{issue:a("bundle.json kinds block is invalid",s.join("; "),"each entry a full KindConfig (whenToUse required) or a sparse patch of launch fields","Fix the kinds entries in bundle.json.","bundle.json")}}if(n.page_components!==void 0){const s=S(n.page_components);if(s.length>0)return{issue:a("bundle.json page_components block is invalid",s.join("; "),"an array of page component registrations, each a kind string or {kind, description?, useWhen?, doc?, display?}","Fix the page_components entries in bundle.json.","bundle.json")}}let t;if(n.memory_extensions!==void 0)try{t=O(n.memory_extensions)}catch(s){return{issue:a("bundle.json memory_extensions block is invalid",s instanceof Error?s.message:String(s),"a valid memory_extensions declaration object","Fix the memory_extensions declarations in bundle.json.","bundle.json")}}if(n.version!==void 0&&!y(n.version))return{issue:a("bundle.json version must be an integer of at least 1",JSON.stringify(n.version),"the plugin major as an integer >= 1, or omitted","Fix version in bundle.json.","bundle.json.version")};let u;if(n.transport!==void 0){const s=E(n.transport);if(s.transport===void 0)return{issue:a("bundle.json transport is invalid",s.errors.join("; "),'{ kind: "http", endpoint, authEnv? }',"Fix transport in bundle.json.","bundle.json.transport")};u=s.transport}let m;if(n.provider!==void 0){const s=M(n.provider);if(s.issue!==void 0)return{issue:s.issue};if(m=s.provider,n.version!==void 0&&n.version!==m.major)return{issue:a("bundle.json provider major must equal version",`version ${String(n.version)}, major ${m.major}`,"one major","Set provider.major and version to the same integer.","bundle.json.provider.major")}}return{...n.version!==void 0?{version:n.version}:{},...u!==void 0?{transport:u}:{},...m!==void 0?{provider:m}:{},...n.kinds!==void 0?{kinds:n.kinds}:{},...n.page_components!==void 0?{page_components:$(n.page_components)}:{},...t!==void 0?{memory_extensions:t}:{}}}p(q,"parseBundleMetadata");function C(e,o,n,r=new Set){let t;try{t=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issues:[a("commands.json is not valid JSON","invalid JSON","a valid command-manifest JSON object","Regenerate commands.json.","commands.json")]}}const u=w(t,{transport:"http",reservedCoreNames:o,extensibleCoreBranches:r,...n!==void 0?{coreCommandPaths:n}:{}});return{...u.manifest!==void 0?{commands:u.manifest}:{},issues:u.issues}}p(C,"parseCommands");async function Y(e,o){const n=P(e);if(n!==void 0)return{issues:[n]};const r=await F(e);if(r.issue!==void 0||r.members===void 0)return{issues:[r.issue]};const t=z(r.members);if(t!==void 0)return{issues:[t]};const u=r.members.find(d=>d.path==="bundle.json"&&d.type==="file"),m=r.members.find(d=>d.path==="commands.json"&&d.type==="file");if(u===void 0||m===void 0)return{issues:[a("bundle must contain exactly one bundle.json and one commands.json regular file",`bundle.json=${u!==void 0}, commands.json=${m!==void 0}`,"one regular bundle.json and one regular commands.json member","Add the required metadata members to the archive.")]};const s=r.members.find(d=>d.path==="hooks.json"&&d.type==="file"),l=r.members.filter(d=>d.type==="file"&&d.path.startsWith("hooks/"));if((s!==void 0||r.members.some(d=>d.path==="hooks"||d.path.startsWith("hooks/")))&&(s===void 0||!l.some(d=>d.path==="hooks/dispatch")))return{issues:[a("bundle hook payload must contain both hooks.json and hooks/dispatch regular files",`hooks.json=${s!==void 0}, hooks/dispatch=${l.some(d=>d.path==="hooks/dispatch")}`,"both hooks.json and hooks/dispatch regular files","Add both required hook members or remove the hook payload.")]};const i=q(u.bytes);if(i.issue!==void 0)return{issues:[i.issue]};const b=C(m.bytes,o.reservedCoreNames,o.coreCommandPaths,o.extensibleCoreBranches);if(b.commands===void 0)return{issues:b.issues};const f=b.commands.version;if(i.version!==void 0&&f!==void 0&&i.version!==f)return{issues:[a("bundle.json version and commands.json version differ",`bundle.json ${i.version}, commands.json ${f}`,"one plugin major","Emit the same version in bundle.json and commands.json.","bundle.json.version")]};const h=i.version??f;if(i.provider!==void 0&&h===void 0)return{issues:[a("a provider bundle requires a plugin version","no version in bundle.json or commands.json",`version ${i.provider.major}, the provider major`,"Emit the provider major as the plugin version.","bundle.json.version")]};if(i.provider!==void 0&&h!==i.provider.major)return{issues:[a("bundle provider major must equal the plugin version",`version ${h}, major ${i.provider.major}`,"one major","Emit the provider major as the plugin version.","bundle.json.provider.major")]};const v=D(i,b.commands);return v!==void 0?{issues:[v]}:{bundle:{bundleVersion:1,...h!==void 0?{version:h}:{},...i.transport!==void 0?{transport:i.transport}:{},...i.provider!==void 0?{provider:i.provider}:{},...i.kinds!==void 0?{kinds:i.kinds}:{},...i.page_components!==void 0?{page_components:i.page_components}:{},...i.memory_extensions!==void 0?{memory_extensions:i.memory_extensions}:{},commands:b.commands,commandsBytes:m.bytes,directories:r.members.filter(d=>d.type==="directory").map(d=>({path:d.path})),memory:r.members.filter(d=>d.type==="file"&&d.path.startsWith("memory/")).map(d=>({path:d.path,bytes:d.bytes})),...s!==void 0?{hooks:{manifest:s.bytes,files:l.map(d=>({path:d.path,bytes:d.bytes}))}}:{}},issues:[]}}p(Y,"validatePluginBundle");export{Y as validatePluginBundle};
|
package/dist/core/help.d.ts
CHANGED
|
@@ -4,8 +4,9 @@ export type Field = DeclaredOutputField;
|
|
|
4
4
|
export interface PositionalParam {
|
|
5
5
|
kind: 'positional';
|
|
6
6
|
name: string;
|
|
7
|
-
/** Display hint only; always parsed as string.
|
|
8
|
-
|
|
7
|
+
/** Display hint only; always parsed as string. `file` names a
|
|
8
|
+
* capability-provider file argument, sent locally as the string typed. */
|
|
9
|
+
type?: 'string' | 'path' | 'file';
|
|
9
10
|
required: boolean;
|
|
10
11
|
constraint: string;
|
|
11
12
|
/** When true, every positional token is collected into an array in argv order. */
|
|
@@ -40,8 +41,9 @@ export interface FocusedFlagHelp {
|
|
|
40
41
|
export interface FlagParam {
|
|
41
42
|
kind: 'flag';
|
|
42
43
|
name: string;
|
|
43
|
-
/** 'bool' flags take no value — presence = true.
|
|
44
|
-
|
|
44
|
+
/** 'bool' flags take no value — presence = true. 'file' names a
|
|
45
|
+
* capability-provider file argument, sent locally as the string typed. */
|
|
46
|
+
type: 'string' | 'int' | 'bool' | 'path' | 'enum' | 'file';
|
|
45
47
|
/** Required only when type is 'enum'. */
|
|
46
48
|
choices?: string[];
|
|
47
49
|
required: boolean;
|
package/dist/core/help.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
var
|
|
1
|
+
var w=Object.defineProperty;var u=(e,n)=>w(e,"name",{value:n,configurable:!0});async function T(e,n,t){const s=u(async o=>{try{const r=await o();return()=>r}catch{return()=>null}},"read"),a=t===void 0?void 0:n.flags?.[t],i=new Map(a===void 0?[]:[[t,await s(a)]]);return{...e,...t===void 0&&n.leaf!==void 0?{dynamicState:await s(n.leaf)}:{},params:e.params?.map(o=>{const r=o.kind==="flag"?i.get(o.name):void 0;return o.kind==="flag"&&o.focusedHelp!==void 0&&r!==void 0?{...o,focusedHelp:{...o.focusedHelp,dynamicState:r}}:o})}}u(T,"withAsyncHelpState");function j(e,n,t){const s=Object.entries(n).map(([a,i])=>` ${a}="${i}"`).join("");return`<${e}${s}>
|
|
2
2
|
${t}
|
|
3
|
-
</${e}>`}
|
|
3
|
+
</${e}>`}u(j,"stateBlock");function d(e){if(e===void 0)return null;try{const n=e();return n!==null&&n!==""?n:null}catch{return null}}u(d,"evalDynamic");function l(e){let n=0;for(const t of e)t.length>n&&(n=t.length);return n}u(l,"maxLen");function p(e,n){return e+" ".repeat(Math.max(0,n-e.length))}u(p,"pad");const x=`I/O contract: flags and positional args on input; stdout is agent-ready markdown/XML you
|
|
4
4
|
act on directly \u2014 read it as a continuation of your prompt, don't parse it as data.
|
|
5
|
-
Exit 0 on success, non-zero on failure. Schemas appear at leaf -h; a leaf may route an uncommon flag to focused --flag -h guidance.`,
|
|
6
|
-
`)}
|
|
7
|
-
`)}
|
|
8
|
-
`)}if(e.guide!==void 0&&(t.push(""),t.push(e.guide)),t.push(""),s.length>0){t.push("Input");const
|
|
9
|
-
`)}
|
|
5
|
+
Exit 0 on success, non-zero on failure. Schemas appear at leaf -h; a leaf may route an uncommon flag to focused --flag -h guidance.`,S="Before running a crtr command that changes state \u2014 creates, mutates, or deletes anything \u2014 whose exact contract (args, flags, effects) you haven't verified this session, run `-h` on it and read the schema first \u2014 a reliable read beats a guess that wastes a turn or triggers an unintended effect. When a leaf routes a flag to focused help, read that focused help before using the flag; the compact row is discovery, not its invocation contract. Same when the user names a capability you don't fully recognize: `-h` it before acting.";function v(e){const n=e.subcommands??[],t=e.otherSubcommandCount??0;if(n.length===0&&t===0)return[];const s=[];if(n.length>0){const a=l(n.map(i=>i.path));for(const i of n)s.push(i.desc!==void 0&&i.desc!==""?` ${p(i.path,a)} ${i.desc}`:` ${i.path}`)}if(t>0){const a=n.length>0?"other subcommand":"subcommand";s.push(` [+${t} ${a}${t===1?"":"s"} \u2014 \`crtr ${e.name} -h\`]`)}return s}u(v,"rootSubcommandLines");function I(e){const n=[];n.push(`${e.tagline}`),n.push("");for(const t of e.commands){n.push(`<command name="${t.name}">`),n.push(t.concept),n.push(`use when ${t.useWhen}`);for(const a of v(t))n.push(a);const s=d(t.dynamicState);s!==null&&n.push(s),n.push("</command>"),n.push("")}if(e.globals.length>0){n.push("Globals");const t=l(e.globals.map(s=>s.name));for(const s of e.globals)n.push(` ${p(s.name,t)} ${s.desc}`);n.push("")}return n.push(x),n.push(""),n.push(S),n.join(`
|
|
6
|
+
`)}u(I,"renderRoot");function f(e){return e.replace(/"/g,"'")}u(f,"attr");function L(e){const n=[];n.push(`<command name="${e.name}" description="${f(e.summary)}">`);const t=d(e.dynamicState);t!==null&&n.push(t),e.model!==void 0&&n.push(e.model);for(const s of e.listing??[]){if(s.tier==="hidden")continue;const a=s.plugin===void 0?"":` plugin="${f(s.plugin)}"`,i=s.subCount!==void 0&&s.subCount>0?` subcommands="${s.subCount}"`:"";n.length>1&&n.push(""),n.push(`<subcommand name="${s.name}" description="${f(s.description)}" whenToUse="${f(s.whenToUse)}"${a}${i}/>`)}return e.extensionIssue!==void 0&&n.push(`<extension-issue fragment="${f(e.extensionIssue.fragment)}">This repository's contributed commands were rejected: ${e.extensionIssue.message}. Run \`crtr sys doctor\` for the full report.</extension-issue>`),n.push("</command>"),n.join(`
|
|
7
|
+
`)}u(L,"renderBranch");function k(e){if(e.kind==="positional")return`${e.name.toUpperCase()}${e.repeatable===!0?"...":""}`;if(e.kind==="stdin")return"stdin";if(e.kind==="context-file")return"--context-file PATH";const n=e;return n.type==="bool"?`--${n.name}`:`--${n.name} ${n.name.toUpperCase().replace(/-/g,"_")}`}u(k,"paramLabel");function $(e){return e===void 0?"":e==="base64"?" Local file path: the file is read and its raw bytes are base64-encoded into the request; the path itself is never sent.":" Local file path: the file is read as UTF-8 text and its content is sent as the value; the path itself is never sent."}u($,"fileEncodingNote");function g(e){return e===void 0?"":` Defaults to $${e} when that is set and non-empty in the environment, and counts as supplied.`}u(g,"envDefaultNote");function b(e){return e==="file"?" A file: pass its path.":""}u(b,"fileParamNote");function N(e,n){return e.focusedHelp===void 0?"":` Before using this flag, run \`crtr ${n} --${e.name} -h\` and read its focused guidance.`}u(N,"focusedFlagHelpNote");function O(e,n){const t=e.required?"required":"optional";if(e.kind==="positional"){const c=e.repeatable===!0?" Repeatable \u2014 pass multiple values to accumulate in argv order.":"";return`positional, ${t}.${b(e.type)}${$(e.encoding)}${g(e.defaultFromEnv)} ${e.constraint}${c}`}if(e.kind==="stdin")return`${t}. ${e.constraint}`;if(e.kind==="context-file"){const c=e.shape!==void 0?` Shape: ${e.shape}`:"";return`${t}. Path to a JSON file.${c} ${e.constraint}`.trim()}const s=e,a=N(s,n);if(s.type==="bool")return`${t} boolean. Presence means true. ${s.constraint}${a}`.trim();const i=s.default!==void 0?` Default: ${String(s.default)}.`:"",o=s.type==="enum"&&s.choices!==void 0?` One of: ${s.choices.join(", ")}.`:"",r=s.repeatable===!0?" Repeatable \u2014 pass multiple times to accumulate.":"";return`${s.type}, ${t}.${o}${i}${b(s.type)}${$(s.encoding)}${g(s.defaultFromEnv)} ${s.constraint}${r}${a}`.trim()}u(O,"paramDesc");function h(e){if(e.items===void 0)return e.type;const n=/\|\s*null\s*$/i.test(e.type)?", or null":"";return`array of ${h(e.items)}${n}`}u(h,"outputTypePhrase");function y(e){return e.values!==void 0?` One of: ${e.values.join(", ")}.`:""}u(y,"outputValuesNote");function m(e,n,t){const s=l(e.map(a=>a.name));for(const a of e){const i=a.constraint.length>0?` ${a.constraint}`:"";t.push(`${n}${p(a.name,s)} ${h(a)}.${y(a)}${i}`),C(a,`${n} `,t)}}u(m,"renderOutputFields");function C(e,n,t){e.children!==void 0&&e.children.length>0&&m(e.children,n,t);let s=e.items;for(;s!==void 0;){const a=y(s);if((s.constraint.length>0||a.length>0)&&t.push(`${n}each element: ${h(s)}.${a}${s.constraint.length>0?` ${s.constraint}`:""}`),s.children!==void 0&&s.children.length>0){m(s.children,n,t);return}s=s.items}}u(C,"renderOutputShapeBody");function A(e,n){const t=[`${e.name}: ${e.summary}.`],s=(e.params??[]).filter(o=>!(o.kind==="flag"&&o.hidden===!0)),a=n===void 0?void 0:s.find(o=>o.kind==="flag"&&o.name===n&&o.focusedHelp!==void 0);if(a?.focusedHelp!==void 0){const o=a.focusedHelp;if(t.push(""),t.push(`<parameter name="${a.name}">`),t.push("When to use"),t.push(` ${o.whenToUse}`),t.push(""),t.push("Value"),t.push(` ${o.value}`),(o.effects??[]).length>0){t.push(""),t.push("Effects");for(const c of o.effects??[])t.push(` ${c}`)}const r=d(o.dynamicState);return r!==null&&(t.push(""),t.push(r)),t.push("</parameter>"),t.join(`
|
|
8
|
+
`)}if(e.guide!==void 0&&(t.push(""),t.push(e.guide)),t.push(""),s.length>0){t.push("Input");const o=s.map(k),r=l(o);for(let c=0;c<s.length;c++)t.push(` ${p(o[c],r)} ${O(s[c],e.name)}`)}else t.push(e.inputNote!==void 0?e.inputNote:"No input parameters.");t.push(""),t.push("Output (fields carried in the rendered result)"),m(e.output," ",t),t.push(""),t.push("Effects");for(const o of e.effects)t.push(` ${o}`);const i=d(e.dynamicState);return i!==null&&(t.push(""),t.push(i)),t.join(`
|
|
9
|
+
`)}u(A,"renderLeafArgv");export{L as renderBranch,A as renderLeafArgv,I as renderRoot,j as stateBlock,T as withAsyncHelpState};
|