@north-light/cli 0.3.519 → 0.3.521
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/result.d.ts +20 -8
- package/dist/api/command-manifest/result.js +1 -1
- package/dist/api/command-manifest/schema.js +1 -1
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/health.d.ts +3 -0
- package/dist/api/plugin-manifest-schema.d.ts +7 -3
- package/dist/builtin-memory/crouter-plugin/deploying.md +3 -1
- package/dist/builtin-memory/crouter-plugin/output.md +1 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +6 -0
- package/dist/clients/attach/viewer.js +533 -533
- package/dist/commands/pkg/browse/model.d.ts +1 -1
- package/dist/commands/pkg/plugin-manage.js +1 -1
- package/dist/commands/sys/daemon.js +1 -1
- package/dist/core/canvas/migrations.js +28 -22
- package/dist/core/command-hooks/transport/exec-invoke.js +1 -1
- package/dist/core/command-plugins/transport/exec-invoke.js +1 -1
- package/dist/core/command-plugins/transport/http-plan.js +1 -1
- package/dist/core/command.js +9 -9
- package/dist/core/graph/documents.d.ts +6 -5
- package/dist/core/graph/documents.js +7 -7
- package/dist/core/graph/edges.d.ts +16 -0
- package/dist/core/graph/edges.js +1 -1
- package/dist/core/graph/repo-sync/exchange.js +2 -2
- package/dist/core/help.d.ts +4 -2
- package/dist/core/help.js +7 -7
- package/dist/core/plugin-app.js +1 -1
- package/dist/core/plugin-objects-manifest.d.ts +5 -3
- package/dist/core/plugin-objects-manifest.js +1 -1
- package/dist/core/providers/call.d.ts +2 -1
- package/dist/core/providers/call.js +1 -1
- package/dist/core/spaces/cache-env.d.ts +1 -1
- package/dist/core/spaces/cache-env.js +1 -1
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/docs.js +1 -1
- package/dist/daemon/api/handlers/health.js +3 -3
- package/docs/ci-runner.md +4 -5
- package/docs/plugin/deploying.md +3 -1
- package/docs/plugin/output.md +1 -1
- package/docs/sdk/release-notes.md +6 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export interface DeclaredOutputShape {
|
|
2
2
|
type: string;
|
|
3
3
|
constraint: string;
|
|
4
|
-
/** Named members of an `object` value. */
|
|
4
|
+
/** Named members of an `object` value. Declaring them closes the object. */
|
|
5
5
|
children?: DeclaredOutputField[];
|
|
6
|
-
/**
|
|
6
|
+
/** Shape of each contained value of an `array` or `map` value. */
|
|
7
7
|
items?: DeclaredOutputShape;
|
|
8
|
+
/** Alternatives of a `union` value. */
|
|
9
|
+
variants?: DeclaredOutputShape[];
|
|
8
10
|
/** Allowed values of an `enum` value. */
|
|
9
11
|
values?: string[];
|
|
10
12
|
}
|
|
@@ -12,14 +14,24 @@ export interface DeclaredOutputField extends DeclaredOutputShape {
|
|
|
12
14
|
name: string;
|
|
13
15
|
required: boolean;
|
|
14
16
|
}
|
|
17
|
+
/** The first place a value departs from its shape: `path` is the full path from
|
|
18
|
+
* the root the caller passed (`data.messages[3].from`, `--to[1]`). */
|
|
19
|
+
export interface ValueMismatch {
|
|
20
|
+
path: string;
|
|
21
|
+
expected: string;
|
|
22
|
+
receivedType: string;
|
|
23
|
+
}
|
|
15
24
|
/** Validates a result object against declared output field descriptors.
|
|
16
|
-
* Returns null if valid; otherwise returns a { field, receivedType } error
|
|
25
|
+
* Returns null if valid; otherwise returns a { field, receivedType } error
|
|
26
|
+
* where `field` is the full path of the first mismatch. */
|
|
17
27
|
export declare function validateDeclaredResult(output: DeclaredOutputField[], result: Record<string, unknown>): {
|
|
18
28
|
field: string;
|
|
19
29
|
receivedType?: string;
|
|
30
|
+
expected?: string;
|
|
20
31
|
} | null;
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
/** Checks one JSON value against one shape, recursively: object members, array
|
|
33
|
+
* items, map values, union variants, enum values and nullability. An object that
|
|
34
|
+
* declares `children` is closed — an undeclared member is a mismatch at any
|
|
35
|
+
* depth. Returns null, or the first mismatch with its full path from `path`.
|
|
36
|
+
* A declared type that is not recognized is accepted. */
|
|
37
|
+
export declare function validateValue(shape: DeclaredOutputShape, value: unknown, path: string): ValueMismatch | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var g=Object.defineProperty;var c=(u,e)=>g(u,"name",{value:e,configurable:!0});import{isRecord as d}from"../../shared/predicates.js";function j(u,e){for(const n of u){const l=e[n.name];if(!(Object.prototype.hasOwnProperty.call(e,n.name)&&l!==void 0)){if(n.required)return{field:n.name};continue}const t=s(n,l,n.name);if(t!==null)return{field:t.path,receivedType:t.receivedType,expected:t.expected}}return null}c(j,"validateDeclaredResult");function s(u,e,n){const l=u.type.split("|").map(t=>t.trim()).filter(Boolean),r=l.filter(t=>t.toLowerCase()!=="null");if(e===null&&r.length<l.length||r.length===1&&!p(r[0])||!l.every(t=>p(t)||/^[a-z][a-z0-9_-]*$/.test(t)))return null;if(r.length===1)return b(r[0],u,e,n);for(const t of r)if(b(t,u,e,n)===null)return null;return m(n,u.type,e)}c(s,"validateValue");function m(u,e,n){const l=n===null?"null":Array.isArray(n)?"array":typeof n;return{path:u,expected:e,receivedType:l}}c(m,"mismatch");function p(u){const e=u.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==="map"||e==="union"||e==="unknown"||e==="enum"||e==="file"}c(p,"knownType");function b(u,e,n,l){const r=u.toLowerCase(),t=c(o=>m(l,o,n),"fail");if(r==="null")return n===null?null:t("null");if(r==="unknown")return null;if(r==="enum")return e.values===void 0||typeof n=="string"&&e.values.includes(n)?null:t(`one of ${(e.values??[]).join(", ")}`);if(r==="file")return d(n)||typeof n=="string"?null:t("file");if(r==="string"||r==="path"||r==="markdown")return typeof n=="string"?null:t(r);if(r==="int"||r==="integer")return typeof n=="number"&&Number.isInteger(n)?null:t("int");if(r==="number"||r==="float")return typeof n=="number"?null:t("number");if(r==="bool"||r==="boolean")return typeof n=="boolean"?null:t("bool");if(r==="array"){if(!Array.isArray(n))return t("array");if(e.items===void 0)return null;for(let o=0;o<n.length;o++){const i=s(e.items,n[o],`${l}[${o}]`);if(i!==null)return i}return null}if(r.endsWith("[]"))return Array.isArray(n)?null:t("array");if(r==="map"){if(!d(n))return t("map");if(e.items===void 0)return null;for(const[o,i]of Object.entries(n)){const f=s(e.items,i,`${l}[${JSON.stringify(o)}]`);if(f!==null)return f}return null}if(r==="object"){if(!d(n))return t("object");if(e.children===void 0)return null;for(const i of e.children){const f=n[i.name];if(!(Object.prototype.hasOwnProperty.call(n,i.name)&&f!==void 0)){if(i.required)return{path:`${l}.${i.name}`,expected:`${i.type} (required)`,receivedType:"undefined"};continue}const y=s(i,f,`${l}.${i.name}`);if(y!==null)return y}const o=new Set(e.children.map(i=>i.name));for(const i of Object.keys(n))if(!o.has(i))return m(`${l}.${i}`,"no such member (the object is closed)",n[i]);return null}if(r==="union"){const o=[];for(const f of e.variants??[]){const a=s(f,n,l);if(a===null)return null;o.push(a)}const i=o[0];return i!==void 0&&i.path!==l&&o.every(f=>f.path===i.path)?{path:i.path,expected:[...new Set(o.map(f=>f.expected))].join(" | "),receivedType:i.receivedType}:t((e.variants??[]).map(f=>f.type).join(" | "))}return typeof n=="string"&&n===u?null:t(u)}c(b,"atomMismatch");export{j as validateDeclaredResult,s as validateValue};
|
|
@@ -1 +1 @@
|
|
|
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{le as mentionsFile,D as outputBaseType,me as validateCommandNode};
|
|
1
|
+
var Z=Object.defineProperty;var E=(e,t)=>Z(e,"name",{value:t,configurable:!0});import{isRecord as x}from"../../shared/predicates.js";const w=new Set(["hidden","normal","common","important"]),J=new Set(["string","int","bool","path","enum","file","json"]),W=/^[A-Za-z0-9_-]{1,64}$/,N=/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/,C=new Set(["kind","name","description","whenToUse","tier","rootEntry","extensible","summary","model","children"]),X=new Set([...C,"passthrough"]),Q=new Set(["bin","installHint"]),D=new Set(["kind","name","description","whenToUse","tier","summary","params","output","effects"]),L=new Set([...D,"outputKind"]),ee=new Set([...D,"rest","group"]),ne=new Set(["concept","description","whenToUse"]),te=new Set(["text","base64"]),H={positional:new Set(["kind","name","type","required","constraint","repeatable","encoding","defaultFromEnv"]),flag:new Set(["kind","name","type","choices","required","constraint","default","repeatable","encoding","defaultFromEnv","shape"]),stdin:new Set(["kind","name","required","constraint"]),"context-file":new Set(["kind","name","required","constraint","shape"])},ae=new Set(["name","type","required","constraint","children","items","variants","values"]),U=new Set(["type","constraint","children","items","variants","values"]),oe=/^[A-Z][A-Z0-9_]*$/;function m(e){return e===null?"null":Array.isArray(e)?"array":typeof e}E(m,"typeName");function T(e,t,a,n){const o=Object.keys(e).filter(h=>!t.has(h));return o.length>0?(n("command_node_invalid","unknown keys",o.join(", "),`only: ${[...t].join(", ")}`,"Remove the unknown keys.",a),!1):!0}E(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,ne,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}}E(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 h=0;for(let p=0;p<e.length;p++){const r=e[p],i=`${t}[${p}]`;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 H))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,H[u],i,a))return null;const c=r.name;if(typeof c!="string"||!N.test(c))return a("command_node_invalid","param name must be a kebab-case token",String(c),"a kebab-case token","Rename the param.",`${i}.name`),null;if(o.has(c))return a("command_node_invalid","duplicate param name",c,"unique param names","Rename the duplicate param.",`${i}.name`),null;if(o.add(c),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(h++,h>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 d=!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;d=r.repeatable}if(d&&r.type==="file")return a("command_node_invalid","repeatable is not valid on a file positional",`${c}: file`,"one file per file param","Remove repeatable, or declare one file param per file.",`${i}.repeatable`),null;const s=M(r,i,a);if(s===null)return null;const f=G(r,i,a);if(f===null)return null;if(d&&f!==void 0)return a("command_node_invalid","repeatable positional must not declare defaultFromEnv",c,"no defaultFromEnv on a repeatable positional (values accumulate into an array)","Remove defaultFromEnv, or drop repeatable.",`${i}.defaultFromEnv`),null;n.push({kind:"positional",name:c,...r.type!==void 0?{type:r.type}:{},required:r.required,constraint:r.constraint,...d?{repeatable:!0}:{},...s!==void 0?{encoding:s}:{},...f!==void 0?{defaultFromEnv:f}:{}})}else if(u==="stdin")n.push({kind:"stdin",name:c,required:r.required,constraint:r.constraint});else if(u==="context-file")n.push({kind:"context-file",name:c,required:r.required,constraint:r.constraint,...typeof r.shape=="string"?{shape:r.shape}:{}});else if(u==="flag"){const d=r.type;if(typeof d!="string"||!J.has(d))return a("command_node_invalid","flag type must be string|int|bool|path|enum|file|json",String(d),"string | int | bool | path | enum | file | json","Fix the flag type.",`${i}.type`),null;let s;if(d==="enum"){const _=r.choices;if(!Array.isArray(_)||_.length===0||!_.every(R=>typeof R=="string"))return a("command_node_invalid","enum flag requires a non-empty string choices array",m(_),"a non-empty array of strings","Declare choices for the enum flag.",`${i}.choices`),null;s=_}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 f;if(d==="json"){const _=r.shape;if(!x(_))return a("command_node_invalid","json flag requires a shape object",m(_),"a { type, constraint, children?, items?, variants?, values? } object","Declare the shape of the JSON value the flag takes.",`${i}.shape`),null;if(!T(_,U,`${i}.shape`,a))return null;const R=q(_,`${i}.shape`,!1,a);if(R===null)return null;f=R}else if(r.shape!==void 0)return a("command_node_invalid","shape is only valid on a json flag","shape","no shape unless type is json","Remove shape.",`${i}.shape`),null;let b=!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;b=r.repeatable}if(d==="json"&&(r.default!==void 0||r.defaultFromEnv!==void 0))return a("command_node_invalid","a json flag takes no default",c,"no default or defaultFromEnv on a json flag","Remove the default.",`${i}.${r.default!==void 0?"default":"defaultFromEnv"}`),null;if(b&&(d==="bool"||d==="path"||d==="file"||d==="json"))return a("command_node_invalid",`repeatable is not valid on a ${d} flag`,`${c}: ${d}`,"repeatable only on string | int | enum flags","Remove repeatable or change the flag type.",`${i}.repeatable`),null;if(b&&r.default!==void 0)return a("command_node_invalid","repeatable flag must not declare a default",c,"no default on a repeatable flag (values accumulate into an array)","Remove the default.",`${i}.default`),null;const S=M(r,i,a);if(S===null)return null;const v=G(r,i,a);if(v===null)return null;if(b&&v!==void 0)return a("command_node_invalid","repeatable flag must not declare defaultFromEnv",c,"no defaultFromEnv on a repeatable flag (values accumulate into an array)","Remove defaultFromEnv, or drop repeatable.",`${i}.defaultFromEnv`),null;n.push({kind:"flag",name:c,type:d,...f!==void 0?{shape:f}:{},...s!==void 0?{choices:s}:{},...S!==void 0?{encoding:S}:{},required:r.required,constraint:r.constraint,...r.default!==void 0?{default:r.default}:{},...b?{repeatable:!0}:{},...v!==void 0?{defaultFromEnv:v}:{}})}}return n}E(ie,"validateParams");function M(e,t,a){const n=e.encoding;if(n!==void 0)return typeof n!="string"||!te.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}E(M,"validateFileEncoding");function G(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}E(G,"validateDefaultFromEnv");function Y(e){return e.split("|").map(t=>t.trim().toLowerCase()).filter(t=>t.length>0&&t!=="null").join(" | ")}E(Y,"outputBaseType");function le(e){return e.split("|").some(t=>/^file(\[\])*$/i.test(t.trim()))}E(le,"mentionsFile");function q(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,h=Y(o);if(le(o)&&h!=="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(h==="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 p=[["children",["object"]],["items",["array","map"]],["variants",["union"]],["values",["enum"]]];for(const[d,s]of p)if(e[d]!==void 0&&!s.includes(h)){const f=s.map(b=>`"${b}"`).join(" or ");return n("command_node_invalid",`output ${d} is only valid on a ${s.join(" or ")} field`,`type ${o}`,`type ${f} (optionally "| null")`,`Remove ${d}, or change the type to ${s[0]}.`,`${t}.${d}`),null}if(h==="union"&&e.variants===void 0)return n("command_node_invalid","a union output requires variants",`type ${o}`,"variants: two or more shapes","Declare the alternatives the union may take.",`${t}.variants`),null;if(h==="map"&&e.items===void 0)return n("command_node_invalid","a map output requires items",`type ${o}`,"items: the shape of every value",`Declare the shape of the map's values, or use type "object".`,`${t}.items`),null;let r;if(e.children!==void 0){const d=z(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?, variants?, values? } object","Fix the items shape.",s),null;if(!T(d,U,s,n))return null;const f=q(d,s,!1,n);if(f===null)return null;i=f}let u;if(e.variants!==void 0){const d=e.variants;if(!Array.isArray(d)||d.length<2)return n("command_node_invalid","union variants must be an array of two or more shapes",Array.isArray(d)?`${d.length} variant${d.length===1?"":"s"}`:m(d),"an array of two or more { type, constraint, ... } shapes","Declare at least two variants, or use the variant's own type.",`${t}.variants`),null;u=[];for(let s=0;s<d.length;s++){const f=`${t}.variants[${s}]`,b=d[s];if(!x(b))return n("command_node_invalid","union variant must be an object",m(b),"a { type, constraint, ... } object","Fix the variant shape.",f),null;if(!T(b,U,f,n))return null;const S=q(b,f,!1,n);if(S===null)return null;u.push(S)}}let c;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;c=d}return{type:o,constraint:e.constraint,...r!==void 0?{children:r}:{},...i!==void 0?{items:i}:{},...u!==void 0?{variants:u}:{},...c!==void 0?{values:c}:{}}}E(q,"validateOutputShape");function de(e,t,a){return z(e,t,!0,a)}E(de,"validateOutput");function z(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=[],h=new Set;let p;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,ae,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(h.has(i.name))return n("command_node_invalid",a?"duplicate output field name":"duplicate output child name",i.name,a?"unique output field names within one leaf":"unique member names within one object",a?"Rename the duplicate field.":"Rename the duplicate member.",`${u}.name`),null;if(h.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 c=q(i,u,a,n);if(c===null)return null;if(Y(c.type)==="file"){if(p!==void 0)return n("command_node_invalid","a leaf declares at most one file output",`${p}, ${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;p=i.name}o.push({name:i.name,...c,required:i.required})}return o}E(z,"validateOutputFields");function B(e,t,a){const n=e.name;return typeof n!="string"||!N.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&&!w.has(String(e.tier))?(a("command_node_invalid","node tier must be hidden|normal|common|important",String(e.tier),"hidden | normal | common | important","Fix or omit tier.",`${t}.tier`),!1):!0}E(B,"checkCommon");function me(e,t,a,n,o,h={}){const p=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.",p),null;const r=e.kind;return r==="branch"?se(e,p,a,n,o,h):r==="leaf"?a&&h.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.",`${p}.kind`),null):ce(e,p,n,o):(o("command_node_invalid",'node kind must be "branch" or "leaf"',String(r),"branch | leaf","Set kind to branch or leaf.",`${p}.kind`),null)}E(me,"validateCommandNode");function se(e,t,a,n,o,h){const p=h.allowPassthrough!==!1;if(!T(e,n==="exec"&&p?X:C,t,o)||!B(e,t,o))return null;const i=e.rootEntry!==void 0,u=h.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 c=e.extensible!==void 0;if(c&&(!a||h.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 d;if(i){const v=re(e.rootEntry,`${t}.rootEntry`,o);if(v===null)return null;d=v}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 s;if(n==="exec"&&e.passthrough!==void 0){const v=ue(e.passthrough,`${t}.passthrough`,o);if(v===null)return null;s=v}if(c&&s!==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 f=e.children;if(s!==void 0){if(!Array.isArray(f)||f.length!==0)return o("command_node_invalid","a passthrough branch must declare children: []",Array.isArray(f)?`${f.length} children`:m(f),"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(f))return o("command_node_invalid","branch children must be an array",m(f),"an array of branch/leaf nodes (may be empty for populated by mounts)","Fix children.",`${t}.children`),null;const b=[],S=new Set;for(let v=0;v<f.length;v++){const _=me(f[v],[...t.split("."),`children[${v}]`],!1,n,o,h);if(_===null)return null;if(S.has(_.name))return o("command_node_invalid","duplicate child name",_.name,"unique child names within a branch","Rename the duplicate child.",`${t}.children[${v}].name`),null;S.add(_.name),b.push(_)}return{kind:"branch",name:e.name,description:e.description,whenToUse:e.whenToUse,...e.tier!==void 0?{tier:e.tier}:{},...d!==void 0?{rootEntry:d}:{},...c?{extensible:!0}:{},summary:e.summary,...e.model!==void 0?{model:e.model}:{},...s!==void 0?{passthrough:s}:{},children:b}}E(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,Q,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}}E(ue,"validatePassthrough");function ce(e,t,a,n){if(!T(e,a==="exec"?L:ee,t,n)||!B(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 h=de(e.output,`${t}.output`,n);if(h===null)return null;const p=e.effects;if(!Array.isArray(p)||p.length===0||!p.every(c=>typeof c=="string"))return n("command_node_invalid","leaf effects must be a non-empty string array",m(p),'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:h,effects:p};if(a==="exec")return{...r,outputKind:"object"};const i=e.group;if(i!==void 0&&(typeof i!="string"||!W.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=ye(e.rest,t,o,n);return u===null?null:{...r,rest:u,...i!==void 0?{group:i}:{}}}E(ce,"validateLeaf");const fe=new Set(["GET","POST","PUT","PATCH","DELETE"]),pe=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/,he=new Set(["path","query","body","header"]);function ye(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 h=new Set(["method","path","streaming","body","bodyRoot","params"]),p=Object.keys(e).filter(l=>!h.has(l));if(p.length>0)return n("command_rest_invalid","unknown rest keys",p.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,c=/\{([a-z][a-z0-9]*(?:-[a-z0-9]+)*)\}/g;let d;for(;(d=c.exec(i))!==null;)u.add(d[1]);let s=!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;s=e.streaming}const f=e.params;if(!x(f))return n("command_rest_invalid","params must be an object",m(f),"a mapping of param names to placement specs","Provide params.",`${o}.params`),null;const b=new Map;for(const l of a)b.set(l.name,l);const S={},v=[],_=new Set;for(const[l,$]of Object.entries(f)){if(!b.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(_.add(l),!x($))return n("command_rest_invalid","param mapping must be an object",m($),"{ in, as? }","Fix the param mapping.",`${o}.params.${l}`),null;const k=new Set(["in","as"]),j=Object.keys($).filter(F=>!k.has(F));if(j.length>0)return n("command_rest_invalid","unknown param mapping keys",j.join(", "),"only: in, as","Remove the unknown keys.",`${o}.params.${l}`),null;const y=$.in;if(typeof y!="string"||!he.has(y))return n("command_rest_invalid","param placement must be path|query|body|header",String(y),"path | query | body | header","Set in to a valid placement.",`${o}.params.${l}.in`),null;const g=$.as;if(g!==void 0&&(typeof g!="string"||g.length===0))return n("command_rest_invalid","as must be a non-empty string",String(g),"a non-empty string","Fix the as value.",`${o}.params.${l}.as`),null;if(y==="header"){if(typeof g!="string"||g.length===0)return n("command_rest_invalid","header placement requires a non-empty as",String(g),"a header name","Provide an as value for the header name.",`${o}.params.${l}.as`),null;if(!pe.test(g))return n("command_rest_invalid",'header "as" must be a legal HTTP header field name',g,"an HTTP token (letters, digits, and !#$%&'*+-.^_`|~)","Fix the header name.",`${o}.params.${l}.as`),null;if(g.toLowerCase()==="crtr-request-id")return n("command_rest_invalid","Crtr-Request-Id is assigned by crtr for each leaf call",g,"a header name other than Crtr-Request-Id","Remove this header mapping.",`${o}.params.${l}.as`),null}else if(y==="path"){if(g!==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=b.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 O=b.get(l);if((O.kind==="flag"||O.kind==="positional")&&O.repeatable===!0&&y!=="body")return n("command_rest_invalid","repeatable param must use body placement",`${l}: in "${y}"`,'in: "body" (array values ship as a JSON array)',`Map "${l}" to body placement.`,`${o}.params.${l}.in`),null;if(y==="body"){const I=b.get(l).kind==="stdin",V=g||l;v.push({name:l,isStdin:I,as:V})}S[l]={in:y,...g!==void 0?{as:g}:{}}}for(const l of a)if(!_.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 R=new Set;for(const[l,$]of Object.entries(S))$.in==="path"&&R.add(l);if(R.size!==u.size||![...u].every(l=>R.has(l)))return n("command_rest_invalid","mismatch between path placeholders and path params",`placeholders: ${[...u].join(", ")} | params: ${[...R].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"&&v.length>0)return n("command_rest_invalid","GET method cannot have body params",v.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 P;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 $={};for(const[y,g]of Object.entries(l)){if(typeof g!="string"&&typeof g!="number"&&typeof g!="boolean")return n("command_rest_invalid",`body.${y} must be a string, number, or boolean`,m(g),"string | number | boolean",`Fix body.${y}.`,`${o}.body.${y}`),null;$[y]=g}const k=new Set(v.map(y=>y.as)),j=Object.keys($).filter(y=>k.has(y));if(j.length>0)return n("command_rest_invalid","body constant key collides with a param's body key",j.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;P=$}let K;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;K=l}for(const[l,$]of Object.entries(S)){const k=b.get(l);if((k.kind==="context-file"||(k.kind==="positional"||k.kind==="flag")&&k.type==="path")&&$.in!=="body"){const y=k.kind==="context-file"?"context-file":`path ${k.kind}`;return n("command_rest_invalid",`file param (${y}) must be placed in body`,`${l}: in "${$.in}"`,'in: "body"',"Move the file param to body.",`${o}.params.${l}.in`),null}}const A=new Map;for(const l of v)A.has(l.as)||A.set(l.as,[]),A.get(l.as).push({name:l.name,isStdin:l.isStdin});for(const[l,$]of A.entries())if($.length>1){const k=$.filter(y=>y.isStdin).length,j=$.filter(y=>!y.isStdin).length;if(!(k===1&&j===1))return n("command_rest_invalid","invalid body as grouping",$.map(y=>y.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,...s!==!1?{streaming:s}:{},...P!==void 0?{body:P}:{},...K!==void 0?{bodyRoot:K}:{},params:S}}E(ye,"validateRestMapping");export{le as mentionsFile,Y as outputBaseType,me as validateCommandNode};
|
package/dist/api/contract.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The contract level this build serves and requires. Written by `scripts/contract.mjs release`; never edit by hand. */
|
|
2
|
-
export declare const CONTRACT_LEVEL = "1.
|
|
2
|
+
export declare const CONTRACT_LEVEL = "1.25";
|
|
3
3
|
/** Request header naming the one contract level the caller needs, e.g. `1.3`. Optional: absent means no check. */
|
|
4
4
|
export declare const REQUIRED_CONTRACT_HEADER = "Crouter-Required-Contract";
|
|
5
5
|
/** Response header listing the contract levels the server serves, comma-separated, one per major, e.g. `1.3`. */
|
package/dist/api/contract.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.
|
|
1
|
+
var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.25",i="Crouter-Required-Contract",p="Crouter-Contract";function l(r){const t=/^(\d+)\.(\d+)$/.exec(r.trim());return t===null?null:{major:Number(t[1]),minor:Number(t[2])}}e(l,"parseContractLevel");function a(r){return r==null?[]:r.split(",").map(t=>l(t)).filter(t=>t!==null)}e(a,"parseServedContracts");function s(r,t){const n=l(r);return n===null?!1:(typeof t=="string"||t===null||t===void 0?a(t):t).some(u=>u.major===n.major&&u.minor>=n.minor)}e(s,"contractSatisfies");function C(r,t,n={}){if(r===void 0)return null;if(l(r)===null)return{status:400,code:"invalid_request",param:i,message:`${i} must be a contract level like 1.0.`};if(s(r,t))return null;const o=r.trim();return{status:426,code:"runtime_version_unsupported",message:t===null||a(t).length===0?`The runtime did not name its contract level; the caller requires contract ${o} or later. Roll the runtime forward.`:`The runtime serves contract ${t}; the caller requires contract ${o} or later within the same major. Roll the runtime forward, or pin an SDK whose crouterContract the runtime serves.`,details:{sdk_version:(n.legacyMinimum??"").trim(),runtime_version:n.runtimeVersion??null,required_contract:o,runtime_contract:t}}}e(C,"contractRefusal");export{p as CONTRACT_HEADER,f as CONTRACT_LEVEL,i as REQUIRED_CONTRACT_HEADER,C as contractRefusal,s as contractSatisfies,l as parseContractLevel,a as parseServedContracts};
|
package/dist/api/dto/health.d.ts
CHANGED
|
@@ -37,6 +37,9 @@ export interface StatusDTO {
|
|
|
37
37
|
tcp: string | null;
|
|
38
38
|
};
|
|
39
39
|
runtime_version: string;
|
|
40
|
+
/** Id of the runtime generation this daemon process was launched from; null when it runs from a
|
|
41
|
+
* development checkout. Absent on a daemon that predates the field, so absence means unknown. */
|
|
42
|
+
generation_id?: string | null;
|
|
40
43
|
api_version: string;
|
|
41
44
|
/** Always present; only `ready` admits normal daemon work. */
|
|
42
45
|
startup_phase: StartupPhaseDTO;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** How prominently a node surfaces in ancestor `-h` listings. Default 'normal'. */
|
|
2
|
-
export type ManifestTier = 'normal' | 'common' | 'important';
|
|
2
|
+
export type ManifestTier = 'hidden' | 'normal' | 'common' | 'important';
|
|
3
3
|
/** Root-entry prose for a top-level branch — what every agent reads before it
|
|
4
4
|
* has engaged the command at all. Required on a plugin's top-level branch. */
|
|
5
5
|
export interface ManifestRootEntry {
|
|
@@ -16,8 +16,10 @@ export interface ManifestFieldShape {
|
|
|
16
16
|
constraint: string;
|
|
17
17
|
/** Named members; only when the base type is `object`. */
|
|
18
18
|
children?: ManifestField[];
|
|
19
|
-
/**
|
|
19
|
+
/** Value shape; only when the base type is `array` or `map`. */
|
|
20
20
|
items?: ManifestFieldShape;
|
|
21
|
+
/** Alternatives; required when the base type is `union`. */
|
|
22
|
+
variants?: ManifestFieldShape[];
|
|
21
23
|
/** The allowed values; required, and only valid, when the base type is `enum`. */
|
|
22
24
|
values?: string[];
|
|
23
25
|
}
|
|
@@ -53,7 +55,9 @@ export interface ManifestFlagParam {
|
|
|
53
55
|
/** 'bool' flags take no value — presence is true. 'file' names a file
|
|
54
56
|
* argument a capability-provider call carries; locally it is sent as the
|
|
55
57
|
* string typed, never read from disk (that is `path` with `encoding`). */
|
|
56
|
-
type: 'string' | 'int' | 'bool' | 'path' | 'enum' | 'file';
|
|
58
|
+
type: 'string' | 'int' | 'bool' | 'path' | 'enum' | 'file' | 'json';
|
|
59
|
+
/** Required only for a json flag. */
|
|
60
|
+
shape?: ManifestFieldShape;
|
|
57
61
|
/** Required, and only valid, when type is 'enum'. */
|
|
58
62
|
choices?: string[];
|
|
59
63
|
required: boolean;
|
|
@@ -63,7 +63,9 @@ A definition with `version: N`, or a list of definitions with one per supported
|
|
|
63
63
|
- removes a parameter, changes its type, makes it required, or adds a required one; removes an enum choice from a flag; or changes the environment variable it defaults from; or moves an existing positional;
|
|
64
64
|
- gains paging where it returned everything: a limit flag whose constraint states a default page size ("default N"), or a cursor input together with a `next_cursor`-style output. The answer keeps its shape, so callers that do not page silently get a partial list. A changed stated page size is breaking too. A new `--cursor`, `--limit` with no stated default, or `next_cursor` output alone is additive, and so is a declared `default`, which crtr applies while parsing and does not send;
|
|
65
65
|
- changes how the request is built: method, route (apart from the `/v<N>` segment), body root, constant body fields, a parameter's placement or wire name, or streaming;
|
|
66
|
-
- removes an output field, changes its type, makes it optional,
|
|
66
|
+
- removes an output field, changes its type, makes it optional, or adds a `file` output;
|
|
67
|
+
- widens an output so a client typed against the previous archive could read a value outside its type: adds an enum value or a union variant, adds `| null`, turns `int` into `number`, replaces a declared type with `unknown`, or opens a closed object by dropping its `children`;
|
|
68
|
+
- narrows a `json` flag's shape so a value the previous archive accepted is refused: removes an enum value or a union variant, adds a required member, or closes an open object by adding `children`.
|
|
67
69
|
|
|
68
70
|
A new optional flag, a new leaf, a new optional output field and changed prose are additive.
|
|
69
71
|
|
|
@@ -27,7 +27,7 @@ Each leaf declares an `output` object. Field object keys are returned verbatim i
|
|
|
27
27
|
|
|
28
28
|
Fields are required by default. Pass `{ required: false }` for a field that can be absent. `field.nullable(field.string('...'))` declares a present field that can be `string | null`; it does not make the field optional.
|
|
29
29
|
|
|
30
|
-
The handler result type is inferred from `output`, including structured members and elements. TypeScript reports a missing required field or a wrong value type at the `defineLeaf` call. `definePlugin` throws `PluginDefinitionError` for a malformed structured field, a file field that is not top-level, or a second file output. The Fetch handler checks the returned object's top-level fields again before sending it. It rejects missing fields, known type mismatches, and keys that were not declared, returning a non-2xx `handler_output_invalid` error instead of a JSON success response. It does not
|
|
30
|
+
The handler result type is inferred from `output`, including structured members and elements. TypeScript reports a missing required field or a wrong value type at the `defineLeaf` call. `definePlugin` throws `PluginDefinitionError` for a malformed structured field, a file field that is not top-level, or a second file output. The Fetch handler checks the returned object's top-level fields again before sending it. It rejects missing fields, known type mismatches, and keys that were not declared, returning a non-2xx `handler_output_invalid` error instead of a JSON success response. It also checks nested values against the declared shape: an object with declared `children` is closed, so a member it does not declare, a missing required member, an array element or map value of the wrong shape, or an enum value outside `values` is rejected. An `object` declared without `children` accepts any members.
|
|
31
31
|
|
|
32
32
|
## Structured fields
|
|
33
33
|
|
|
@@ -9,6 +9,12 @@ when-and-why-to-read: When you need Release notes, this knowledge should be read
|
|
|
9
9
|
|
|
10
10
|
Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
|
|
11
11
|
|
|
12
|
+
## 0.3.521
|
|
13
|
+
|
|
14
|
+
`GET /v1/status` names the runtime generation the daemon is serving. Additive: no request or response an existing app sends or reads changes.
|
|
15
|
+
|
|
16
|
+
- `StatusDTO.generation_id` (optional) is the id of the runtime generation the daemon process was launched from, or `null` when it runs from a development checkout. A runtime that predates the field omits it; treat absence as unknown.
|
|
17
|
+
|
|
12
18
|
## 0.3.515
|
|
13
19
|
|
|
14
20
|
`questions.cancel` and notices. Additive: no request or response an existing app sends or reads changes.
|